2020-04-24 13:14:05 +02:00
|
|
|
/* tlv-builder.c - Build DER encoded objects
|
|
|
|
* Copyright (C) 2020 g10 Code GmbH
|
|
|
|
*
|
|
|
|
* This file is part of GnuPG.
|
|
|
|
*
|
|
|
|
* This file is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU Lesser General Public License as
|
|
|
|
* published by the Free Software Foundation; either version 2.1 of
|
|
|
|
* the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This file is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
|
|
* along with this program; if not, see <https://www.gnu.org/licenses/>.
|
|
|
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <gpg-error.h>
|
|
|
|
|
|
|
|
#include "util.h"
|
|
|
|
#include "tlv.h"
|
|
|
|
|
|
|
|
|
|
|
|
struct item_s
|
|
|
|
{
|
|
|
|
int class;
|
|
|
|
int tag;
|
|
|
|
unsigned int is_constructed:1; /* This is a constructed element. */
|
|
|
|
unsigned int is_stop:1; /* This is a STOP item. */
|
|
|
|
const void *value;
|
|
|
|
size_t valuelen;
|
|
|
|
char *buffer; /* Malloced space or NULL. */
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
struct tlv_builder_s
|
|
|
|
{
|
|
|
|
gpg_error_t error; /* Last error. */
|
|
|
|
int use_secure; /* Use secure memory for the result. */
|
|
|
|
size_t nallocateditems; /* Number of allocated items. */
|
|
|
|
size_t nitems; /* Number of used items. */
|
|
|
|
struct item_s *items; /* Array of items. */
|
|
|
|
int laststop; /* Used as return value of compute_length. */
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/* Allocate a new TLV Builder instance. Returns NULL on error. If
|
|
|
|
* SECURE is set the final object is stored in secure memory. */
|
|
|
|
tlv_builder_t
|
|
|
|
tlv_builder_new (int secure)
|
|
|
|
{
|
|
|
|
tlv_builder_t tb;
|
|
|
|
|
|
|
|
tb = xtrycalloc (1, sizeof *tb);
|
|
|
|
if (tb && secure)
|
|
|
|
tb->use_secure = 1;
|
|
|
|
return tb;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Make sure the array of items is large enough for one new item.
|
|
|
|
* Records any error in TB and returns true in that case. */
|
|
|
|
static int
|
|
|
|
ensure_space (tlv_builder_t tb)
|
|
|
|
{
|
|
|
|
struct item_s *newitems;
|
|
|
|
|
|
|
|
if (!tb || tb->error)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
if (tb->nitems == tb->nallocateditems)
|
|
|
|
{
|
|
|
|
tb->nallocateditems += 32;
|
|
|
|
newitems = gpgrt_reallocarray (tb->items, tb->nitems,
|
|
|
|
tb->nallocateditems, sizeof *newitems);
|
|
|
|
if (!newitems)
|
|
|
|
tb->error = gpg_error_from_syserror ();
|
|
|
|
else
|
|
|
|
tb->items = newitems;
|
|
|
|
}
|
|
|
|
return !!tb->error;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Add a new primitive element to the builder instance TB. The
|
|
|
|
* element is described by CLASS, TAG, VALUE, and VALUEEN. CLASS and
|
|
|
|
* TAG must describe a primitive element and (VALUE,VALUELEN) specify
|
|
|
|
* its value. The value is a pointer and its object must not be
|
2024-05-12 18:09:23 -04:00
|
|
|
* changed as long as the instance TB exists. For a TAG_NULL no value
|
2020-04-24 13:14:05 +02:00
|
|
|
* is expected. Errors are not returned but recorded for later
|
|
|
|
* retrieval. */
|
|
|
|
void
|
|
|
|
tlv_builder_add_ptr (tlv_builder_t tb, int class, int tag,
|
|
|
|
void *value, size_t valuelen)
|
|
|
|
{
|
|
|
|
if (ensure_space (tb))
|
|
|
|
return;
|
|
|
|
tb->items[tb->nitems].class = class;
|
|
|
|
tb->items[tb->nitems].tag = tag;
|
|
|
|
tb->items[tb->nitems].value = value;
|
|
|
|
tb->items[tb->nitems].valuelen = valuelen;
|
|
|
|
tb->nitems++;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* This is the same as tlv_builder_add_ptr but it takes a copy of the
|
|
|
|
* value and thus the caller does not need to care about it. */
|
|
|
|
void
|
|
|
|
tlv_builder_add_val (tlv_builder_t tb, int class, int tag,
|
|
|
|
const void *value, size_t valuelen)
|
|
|
|
{
|
|
|
|
void *p;
|
|
|
|
|
|
|
|
if (ensure_space (tb))
|
|
|
|
return;
|
|
|
|
if (!value || !valuelen)
|
|
|
|
{
|
|
|
|
tb->error = gpg_error (GPG_ERR_INV_VALUE);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
p = tb->use_secure? xtrymalloc_secure (valuelen) : xtrymalloc (valuelen);
|
|
|
|
if (!p)
|
|
|
|
{
|
|
|
|
tb->error = gpg_error_from_syserror ();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
memcpy (p, value, valuelen);
|
|
|
|
tb->items[tb->nitems].buffer = p;
|
|
|
|
tb->items[tb->nitems].class = class;
|
|
|
|
tb->items[tb->nitems].tag = tag;
|
|
|
|
tb->items[tb->nitems].value = p;
|
|
|
|
tb->items[tb->nitems].valuelen = valuelen;
|
|
|
|
tb->nitems++;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Add a new constructed object to the builder instance TB. The
|
|
|
|
* object is described by CLASS and TAG which must describe a
|
|
|
|
* constructed object. The elements of the constructed objects are
|
|
|
|
* added with more call to the add functions. To close a constructed
|
|
|
|
* element a call to tlv_builer_add_end is required. Errors are not
|
|
|
|
* returned but recorded for later retrieval. */
|
|
|
|
void
|
|
|
|
tlv_builder_add_tag (tlv_builder_t tb, int class, int tag)
|
|
|
|
{
|
|
|
|
if (ensure_space (tb))
|
|
|
|
return;
|
|
|
|
tb->items[tb->nitems].class = class;
|
|
|
|
tb->items[tb->nitems].tag = tag;
|
|
|
|
tb->items[tb->nitems].is_constructed = 1;
|
|
|
|
tb->nitems++;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* A call to this function closes a constructed element. This must be
|
|
|
|
* called even for an empty constructed element. */
|
|
|
|
void
|
|
|
|
tlv_builder_add_end (tlv_builder_t tb)
|
|
|
|
{
|
|
|
|
if (ensure_space (tb))
|
|
|
|
return;
|
|
|
|
tb->items[tb->nitems].is_stop = 1;
|
|
|
|
tb->nitems++;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Compute and set the length of all constructed elements in the item
|
|
|
|
* array of TB starting at IDX up to the corresponding stop item. On
|
|
|
|
* error tb->error is set. */
|
|
|
|
static size_t
|
|
|
|
compute_lengths (tlv_builder_t tb, int idx)
|
|
|
|
{
|
|
|
|
size_t total = 0;
|
|
|
|
|
|
|
|
if (tb->error)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
for (; idx < tb->nitems; idx++)
|
|
|
|
{
|
|
|
|
if (tb->items[idx].is_stop)
|
|
|
|
{
|
|
|
|
tb->laststop = idx;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (tb->items[idx].is_constructed)
|
|
|
|
{
|
|
|
|
tb->items[idx].valuelen = compute_lengths (tb, idx+1);
|
|
|
|
if (tb->error)
|
|
|
|
return 0;
|
|
|
|
/* Note: The last processed IDX is stored at tb->LASTSTOP. */
|
|
|
|
}
|
|
|
|
total += get_tlv_length (tb->items[idx].class, tb->items[idx].tag,
|
|
|
|
tb->items[idx].is_constructed,
|
|
|
|
tb->items[idx].valuelen);
|
|
|
|
if (tb->items[idx].is_constructed)
|
|
|
|
idx = tb->laststop;
|
|
|
|
}
|
|
|
|
return total;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Return the constructed DER encoding and release this instance. On
|
|
|
|
* success the object is stored at R_OBJ and its length at R_OBJLEN.
|
|
|
|
* The caller needs to release that memory. On error NULL is stored
|
|
|
|
* at R_OBJ and an error code is returned. Note than an error may
|
|
|
|
* stem from any of the previous call made to this object or from
|
|
|
|
* constructing the the DER object. */
|
|
|
|
gpg_error_t
|
|
|
|
tlv_builder_finalize (tlv_builder_t tb, void **r_obj, size_t *r_objlen)
|
|
|
|
{
|
|
|
|
gpg_error_t err;
|
|
|
|
membuf_t mb;
|
|
|
|
int mb_initialized = 0;
|
|
|
|
int idx;
|
|
|
|
|
|
|
|
*r_obj = NULL;
|
|
|
|
*r_objlen = 0;
|
|
|
|
|
|
|
|
if (!tb)
|
|
|
|
return gpg_error (GPG_ERR_INTERNAL);
|
|
|
|
if (tb->error)
|
|
|
|
{
|
|
|
|
err = tb->error;
|
|
|
|
goto leave;
|
|
|
|
}
|
|
|
|
if (!tb->nitems || !tb->items[tb->nitems-1].is_stop)
|
|
|
|
{
|
|
|
|
err = gpg_error (GPG_ERR_NO_OBJ);
|
|
|
|
goto leave;
|
|
|
|
}
|
|
|
|
|
|
|
|
compute_lengths (tb, 0);
|
|
|
|
err = tb->error;
|
|
|
|
if (err)
|
|
|
|
goto leave;
|
|
|
|
|
|
|
|
/* for (idx=0; idx < tb->nitems; idx++) */
|
|
|
|
/* log_debug ("TLVB[%2d]: c=%d t=%2d %s p=%p l=%zu\n", */
|
|
|
|
/* idx, */
|
|
|
|
/* tb->items[idx].class, */
|
|
|
|
/* tb->items[idx].tag, */
|
|
|
|
/* tb->items[idx].is_stop? "stop": */
|
|
|
|
/* tb->items[idx].is_constructed? "cons":"prim", */
|
|
|
|
/* tb->items[idx].value, */
|
|
|
|
/* tb->items[idx].valuelen); */
|
|
|
|
|
|
|
|
if (tb->use_secure)
|
|
|
|
init_membuf_secure (&mb, 512);
|
|
|
|
else
|
|
|
|
init_membuf (&mb, 512);
|
|
|
|
mb_initialized = 1;
|
|
|
|
|
|
|
|
for (idx=0; idx < tb->nitems; idx++)
|
|
|
|
{
|
|
|
|
if (tb->items[idx].is_stop)
|
|
|
|
continue;
|
|
|
|
put_tlv_to_membuf (&mb, tb->items[idx].class, tb->items[idx].tag,
|
|
|
|
tb->items[idx].is_constructed,
|
|
|
|
tb->items[idx].valuelen);
|
|
|
|
if (tb->items[idx].value)
|
|
|
|
put_membuf (&mb, tb->items[idx].value, tb->items[idx].valuelen);
|
|
|
|
}
|
|
|
|
|
|
|
|
*r_obj = get_membuf (&mb, r_objlen);
|
|
|
|
if (!*r_obj)
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
mb_initialized = 0;
|
|
|
|
|
|
|
|
leave:
|
|
|
|
if (mb_initialized)
|
|
|
|
xfree (get_membuf (&mb, NULL));
|
|
|
|
for (idx=0; idx < tb->nitems; idx++)
|
|
|
|
xfree (tb->items[idx].buffer);
|
|
|
|
xfree (tb->items);
|
|
|
|
xfree (tb);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Write TAG of CLASS to MEMBUF. CONSTRUCTED is a flag telling
|
|
|
|
* whether the value is constructed. LENGTH gives the length of the
|
|
|
|
* value, if it is 0 undefinite length is assumed. LENGTH is ignored
|
|
|
|
* for the NULL tag. TAG must be less that 0x1f. */
|
|
|
|
void
|
|
|
|
put_tlv_to_membuf (membuf_t *membuf, int class, int tag,
|
|
|
|
int constructed, size_t length)
|
|
|
|
{
|
|
|
|
unsigned char buf[20];
|
|
|
|
int buflen = 0;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (tag < 0x1f)
|
|
|
|
{
|
|
|
|
*buf = (class << 6) | tag;
|
|
|
|
if (constructed)
|
|
|
|
*buf |= 0x20;
|
|
|
|
buflen++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
BUG ();
|
|
|
|
|
|
|
|
if (!tag && !class)
|
|
|
|
buf[buflen++] = 0; /* end tag */
|
|
|
|
else if (tag == TAG_NULL && !class)
|
|
|
|
buf[buflen++] = 0; /* NULL tag */
|
|
|
|
else if (!length)
|
|
|
|
buf[buflen++] = 0x80; /* indefinite length */
|
|
|
|
else if (length < 128)
|
|
|
|
buf[buflen++] = length;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* If we know the sizeof a size_t we could support larger
|
|
|
|
* objects - however this is pretty ridiculous */
|
|
|
|
i = (length <= 0xff ? 1:
|
|
|
|
length <= 0xffff ? 2:
|
|
|
|
length <= 0xffffff ? 3: 4);
|
|
|
|
|
|
|
|
buf[buflen++] = (0x80 | i);
|
|
|
|
if (i > 3)
|
|
|
|
buf[buflen++] = length >> 24;
|
|
|
|
if (i > 2)
|
|
|
|
buf[buflen++] = length >> 16;
|
|
|
|
if (i > 1)
|
|
|
|
buf[buflen++] = length >> 8;
|
|
|
|
buf[buflen++] = length;
|
|
|
|
}
|
|
|
|
|
|
|
|
put_membuf (membuf, buf, buflen);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Return the length of the to be constructed TLV. CONSTRUCTED is a
|
|
|
|
* flag telling whether the value is constructed. LENGTH gives the
|
|
|
|
* length of the value, if it is 0 undefinite length is assumed.
|
|
|
|
* LENGTH is ignored for the NULL tag. TAG must be less that 0x1f. */
|
|
|
|
size_t
|
|
|
|
get_tlv_length (int class, int tag, int constructed, size_t length)
|
|
|
|
{
|
|
|
|
size_t buflen = 0;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
(void)constructed; /* Not used, but passed for uniformity of such calls. */
|
|
|
|
|
2021-05-20 10:13:51 +02:00
|
|
|
/* coverity[identical_branches] */
|
2020-04-24 13:14:05 +02:00
|
|
|
if (tag < 0x1f)
|
|
|
|
{
|
|
|
|
buflen++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
buflen++; /* assume one and let the actual write function bail out */
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!tag && !class)
|
|
|
|
buflen++; /* end tag */
|
|
|
|
else if (tag == TAG_NULL && !class)
|
|
|
|
buflen++; /* NULL tag */
|
|
|
|
else if (!length)
|
|
|
|
buflen++; /* indefinite length */
|
|
|
|
else if (length < 128)
|
|
|
|
buflen++;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
i = (length <= 0xff ? 1:
|
|
|
|
length <= 0xffff ? 2:
|
|
|
|
length <= 0xffffff ? 3: 4);
|
|
|
|
|
|
|
|
buflen++;
|
|
|
|
if (i > 3)
|
|
|
|
buflen++;
|
|
|
|
if (i > 2)
|
|
|
|
buflen++;
|
|
|
|
if (i > 1)
|
|
|
|
buflen++;
|
|
|
|
buflen++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return buflen + length;
|
|
|
|
}
|