diff --git a/common/Makefile.am b/common/Makefile.am
index 094ebe6e2..31924317e 100644
--- a/common/Makefile.am
+++ b/common/Makefile.am
@@ -57,7 +57,7 @@ common_sources = \
gc-opt-flags.h \
keyserver.h \
sexp-parse.h \
- tlv.c tlv.h \
+ tlv.c tlv.h tlv-builder.c \
init.c init.h \
sexputil.c \
sysutils.c sysutils.h \
diff --git a/common/tlv-builder.c b/common/tlv-builder.c
new file mode 100644
index 000000000..3b644ca24
--- /dev/null
+++ b/common/tlv-builder.c
@@ -0,0 +1,387 @@
+/* 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 .
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include
+
+#include
+#include
+#include
+#include
+
+#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
+ * changed as long as the instance TB exists. For a TAG_NULL no vlaue
+ * 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. */
+
+ 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;
+}
diff --git a/common/tlv.c b/common/tlv.c
index 86e954a19..947464bf7 100644
--- a/common/tlv.c
+++ b/common/tlv.c
@@ -32,21 +32,13 @@
#include
#include
#include
-#include
-
-#if GNUPG_MAJOR_VERSION == 1
-#define GPG_ERR_EOF (-1)
-#define GPG_ERR_BAD_BER (1) /*G10ERR_GENERAL*/
-#define GPG_ERR_INV_SEXP (45) /*G10ERR_INV_ARG*/
-typedef int gpg_error_t;
-#define gpg_make_err(x,n) (n)
-#else
#include
-#endif
+
#include "util.h"
#include "tlv.h"
+
static const unsigned char *
do_find_tlv (const unsigned char *buffer, size_t length,
int tag, size_t *nbytes, int nestlevel)
@@ -157,107 +149,6 @@ find_tlv_unchecked (const unsigned char *buffer, size_t length,
}
-/* 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. */
-
- 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;
-}
-
-
/* ASN.1 BER parser: Parse BUFFER of length SIZE and return the tag
and the length part from the TLV triplet. Update BUFFER and SIZE
on success. */
diff --git a/common/tlv.h b/common/tlv.h
index 0c915e63f..e371ca57e 100644
--- a/common/tlv.h
+++ b/common/tlv.h
@@ -72,6 +72,11 @@ enum tlv_tag_type {
};
+struct tlv_builder_s;
+typedef struct tlv_builder_s *tlv_builder_t;
+
+/*-- tlv.c --*/
+
/* Locate a TLV encoded data object in BUFFER of LENGTH and return a
pointer to value as well as its length in NBYTES. Return NULL if
it was not found or if the object does not fit into the buffer. */
@@ -87,14 +92,6 @@ const unsigned char *find_tlv_unchecked (const unsigned char *buffer,
size_t length,
int tag, size_t *nbytes);
-/* Wite a TLV header to MEMBUF. */
-void put_tlv_to_membuf (membuf_t *membuf, int class, int tag,
- int constructed, size_t length);
-
-/* Count the length of a to be constructed TLV. */
-size_t get_tlv_length (int class, int tag, int constructed, size_t length);
-
-
/* ASN.1 BER parser: Parse BUFFER of length SIZE and return the tag
and the length part from the TLV triplet. Update BUFFER and SIZE
on success. */
@@ -120,5 +117,26 @@ gpg_error_t parse_sexp (unsigned char const **buf, size_t *buflen,
int *depth, unsigned char const **tok, size_t *toklen);
+/*-- tlv-builder.c --*/
+
+tlv_builder_t tlv_builder_new (int use_secure);
+void tlv_builder_add_ptr (tlv_builder_t tb, int class, int tag,
+ void *value, size_t valuelen);
+void tlv_builder_add_val (tlv_builder_t tb, int class, int tag,
+ const void *value, size_t valuelen);
+void tlv_builder_add_tag (tlv_builder_t tb, int class, int tag);
+void tlv_builder_add_end (tlv_builder_t tb);
+gpg_error_t tlv_builder_finalize (tlv_builder_t tb,
+ void **r_obj, size_t *r_objlen);
+
+/* Wite a TLV header to MEMBUF. */
+void put_tlv_to_membuf (membuf_t *membuf, int class, int tag,
+ int constructed, size_t length);
+
+/* Count the length of a to be constructed TLV. */
+size_t get_tlv_length (int class, int tag, int constructed, size_t length);
+
+
+
#endif /* SCD_TLV_H */