diff --git a/common/tlv-parser.c b/common/tlv-parser.c
new file mode 100644
index 000000000..b52cc7a21
--- /dev/null
+++ b/common/tlv-parser.c
@@ -0,0 +1,827 @@
+/* tlv-parser.c - Parse BER encoded objects
+ * Copyright (C) 2023, 2024 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"
+
+
+#define TLV_MAX_DEPTH 25
+
+
+
+/* An object to control the ASN.1 parsing. */
+struct tlv_parser_s
+{
+ /* The original buffer with the entire pkcs#12 object and its length. */
+ unsigned char *origbuffer;
+ size_t origbufsize;
+
+ /* The original offset for debugging. */
+ size_t origoff;
+
+ /* Here we keep a copy of the former TLV. This is returned by
+ * tlv_parser_release. */
+ tlv_parser_t lasttlv;
+
+ /* The current buffer we are working on and its length. */
+ unsigned char *buffer;
+ size_t bufsize;
+
+ size_t crammed; /* 0 or actual length of crammed octet strings. */
+ int in_ndef; /* Flag indicating that we are in a NDEF. */
+ int pending; /* The last tlv_next has not yet been processed. */
+
+ struct tag_info ti; /* The current tag. */
+ gpg_error_t lasterr; /* Last error from tlv function. */
+ const char *lastfunc;/* Name of last called function. */
+ int verbosity; /* Arg from tlv_parser_new. */
+
+ unsigned int stacklen; /* Used size of the stack. */
+ struct {
+ unsigned char *buffer; /* Saved value of BUFFER. */
+ size_t bufsize; /* Saved value of BUFSIZE. */
+ size_t length; /* Length of the container (ti.length). */
+ size_t crammed; /* Saved CRAMMED value. */
+ int in_ndef; /* Saved IN_NDEF flag (ti.ndef). */
+ } stack[TLV_MAX_DEPTH];
+};
+
+
+static size_t cram_octet_string (tlv_parser_t tlv, int testmode);
+
+
+
+void
+_tlv_parser_dump_tag (const char *text, int lno, tlv_parser_t tlv)
+{
+ struct tag_info *ti;
+
+ if (!tlv || tlv->verbosity < 2)
+ return;
+
+ ti = &tlv->ti;
+
+ log_debug ("%s:%d: %zu@%04zu class=%d tag=%lu %c len=%zu%s nhdr=%zu\n",
+ text, lno, tlv->origoff, tlv_parser_offset (tlv) - ti->nhdr,
+ ti->class, ti->tag, ti->is_constructed?'c':'p',
+ ti->length,ti->ndef?" ndef":"", ti->nhdr);
+}
+
+
+void
+_tlv_parser_dump_state (const char *text, const char *text2,
+ int lno, tlv_parser_t tlv)
+{
+ if (!tlv || tlv->verbosity < 2)
+ return;
+
+ log_debug ("p12_parse:%s%s%s:%d: %zu@%04zu lvl=%u %s\n",
+ text,
+ text2? "/":"", text2? text2:"",
+ lno, tlv->origoff, tlv_parser_offset (tlv),
+ tlv->stacklen,
+ tlv->in_ndef? " in-ndef":"");
+}
+
+
+static void
+dump_to_file (const void *s, size_t n, const char *name)
+{
+#if 0
+ FILE *fp;
+ char fname[100];
+ static int fcount;
+
+ snprintf (fname, sizeof fname, "tmp-%03d-%s", ++fcount, name);
+ log_debug ("dumping %zu bytes to '%s'\n", n, fname);
+ fp = fopen (fname, "wb");
+ if (!fp || fwrite (s, n, 1, fp) != 1)
+ exit (2);
+ fclose (fp);
+#else
+ (void)s;
+ (void)n;
+ (void)name;
+#endif
+}
+
+
+/* Parse the buffer at the address BUFFER which is of SIZE and return
+ * the tag and the length part from the TLV triplet. Update BUFFER
+ * and SIZE on success. Checks that the encoded length does not
+ * exhaust the length of the provided buffer. */
+static int
+parse_tag (unsigned char const **buffer, size_t *size, struct tag_info *ti)
+{
+ gpg_error_t err;
+ int tag;
+
+ err = parse_ber_header (buffer, size,
+ &ti->class, &tag,
+ &ti->is_constructed, &ti->ndef,
+ &ti->length, &ti->nhdr);
+ if (err)
+ return err;
+ if (tag < 0)
+ return gpg_error (GPG_ERR_EOVERFLOW);
+ ti->tag = tag;
+
+ if (ti->length > *size)
+ {
+ /* data larger than buffer. */
+ log_debug ("%s: ti->length=%zu for a buffer of size=%zu\n",
+ __func__, ti->length, *size);
+ return gpg_error (GPG_ERR_BUFFER_TOO_SHORT);
+ }
+
+ return 0;
+}
+
+/* Public version of parse_tag. */
+gpg_error_t
+tlv_parse_tag (unsigned char const **buffer, size_t *size, struct tag_info *ti)
+{
+ return parse_tag (buffer, size, ti);
+}
+
+
+/* Create a new TLV object. */
+tlv_parser_t
+_tlv_parser_new (const unsigned char *buffer, size_t bufsize,
+ int verbosity, tlv_parser_t lasttlv, int lno)
+{
+ tlv_parser_t tlv;
+
+ if (verbosity > 1)
+ log_debug ("%s:%d: %zu@%zu (%p,%zu)\n", __func__, lno,
+ lasttlv?lasttlv->origoff:0, tlv_parser_offset (lasttlv),
+ buffer, bufsize);
+
+ tlv = xtrycalloc (1, sizeof *tlv);
+ if (tlv)
+ {
+ char *mybuf = xtrymalloc ( bufsize + 1);
+ if (!mybuf)
+ {
+ xfree (tlv);
+ return NULL;
+ }
+ memcpy (mybuf, buffer, bufsize);
+ mybuf[bufsize] = 0;
+ tlv->origbuffer = mybuf;
+ tlv->origbufsize = bufsize;
+ tlv->origoff = tlv_parser_offset (lasttlv);
+ tlv->buffer = mybuf;
+ tlv->bufsize = bufsize;
+ tlv->crammed = 0;
+ tlv->verbosity = verbosity;
+ tlv->lasttlv = lasttlv;
+ dump_to_file (mybuf, bufsize, "context");
+ }
+ return tlv;
+}
+
+
+/* Free the TLV object and returns the last TLV object stored in this
+ * TLV. */
+tlv_parser_t
+_tlv_parser_release (tlv_parser_t tlv, int lno)
+{
+ tlv_parser_t result;
+
+ if (!tlv)
+ return NULL;
+ result = tlv->lasttlv;
+ if (tlv->verbosity > 1)
+ {
+ if (result)
+ log_debug ("%s:%d: done; returning last TLV %zu@%zu (%p,%zu)\n",
+ __func__, lno, result->origoff,
+ tlv_parser_offset (result), result->buffer, result->bufsize);
+ else
+ log_debug ("%s:%d: done\n", __func__, lno);
+ }
+ xfree (tlv->origbuffer);
+ xfree (tlv);
+ return result;
+}
+
+
+/* Helper for tlv_expect_sequence and tlv_expect_context_tag. */
+static gpg_error_t
+_tlv_push (tlv_parser_t tlv)
+{
+
+ /* Right now our pointer is at the value of the current container.
+ * We push that info onto the stack. */
+ if (tlv->stacklen >= TLV_MAX_DEPTH)
+ return (tlv->lasterr = gpg_error (GPG_ERR_TOO_MANY));
+ tlv->stack[tlv->stacklen].buffer = tlv->buffer;
+ tlv->stack[tlv->stacklen].bufsize = tlv->bufsize;
+ tlv->stack[tlv->stacklen].in_ndef = tlv->in_ndef;
+ tlv->stack[tlv->stacklen].length = tlv->ti.length;
+ tlv->stack[tlv->stacklen].crammed = tlv->crammed;
+ tlv->stacklen++;
+
+ tlv->in_ndef = tlv->ti.ndef;
+
+ /* We set the size of the buffer to the TLV length if it is known or
+ * else to the size of the remaining entire buffer. */
+ if (tlv->in_ndef)
+ {
+ if ((tlv->buffer - tlv->origbuffer) > tlv->origbufsize)
+ return (tlv->lasterr = gpg_error (GPG_ERR_BUG));
+ tlv->bufsize = tlv->origbufsize - (tlv->buffer - tlv->origbuffer);
+ }
+ else
+ tlv->bufsize = tlv->ti.length;
+
+ _tlv_parser_dump_state (__func__, NULL, 0, tlv);
+ return 0;
+}
+
+
+/* Helper for tlv_next. */
+static gpg_error_t
+_tlv_pop (tlv_parser_t tlv)
+{
+ size_t length;
+
+ /* We reached the end of a container, either due to the size limit
+ * or due to an end tag. Now we pop the last container so that we
+ * are positioned at the value of the last container. */
+ if (!tlv->stacklen)
+ return gpg_error (GPG_ERR_EOF);
+
+ tlv->stacklen--;
+ tlv->in_ndef = tlv->stack[tlv->stacklen].in_ndef;
+ length = tlv->ti.length = tlv->stack[tlv->stacklen].length;
+ tlv->crammed = tlv->stack[tlv->stacklen].crammed;
+ if (tlv->in_ndef)
+ {
+ /* We keep buffer but adjust bufsize to the end of the origbuffer. */
+ if ((tlv->buffer - tlv->origbuffer) > tlv->origbufsize)
+ return (tlv->lasterr = gpg_error (GPG_ERR_BUG));
+ tlv->bufsize = tlv->origbufsize - (tlv->buffer - tlv->origbuffer);
+ }
+ else
+ {
+ tlv->buffer = tlv->stack[tlv->stacklen].buffer;
+ tlv->bufsize = tlv->stack[tlv->stacklen].bufsize;
+ if (length > tlv->bufsize)
+ {
+ if (tlv->verbosity > 1)
+ log_debug ("%s: container larger than buffer (%zu/%zu)\n",
+ __func__, length, tlv->bufsize);
+ return gpg_error (GPG_ERR_INV_BER);
+ }
+ tlv->buffer += length;
+ tlv->bufsize -= length;
+
+ }
+ _tlv_parser_dump_state (__func__, NULL, 0, tlv);
+ return 0;
+}
+
+
+/* Parse the next tag and value. Also detect the end of a
+ * container. The caller should use the tlv_next macro. */
+gpg_error_t
+_tlv_parser_next (tlv_parser_t tlv, unsigned int flag, int lno)
+{
+ gpg_error_t err;
+ const unsigned char *buffer;
+ size_t save_bufsize;
+ const unsigned char *save_buffer;
+ int i;
+
+ tlv->lasterr = 0;
+ tlv->lastfunc = __func__;
+
+ if (tlv->pending)
+ {
+ tlv->pending = 0;
+ if (tlv->verbosity > 1)
+ log_debug ("%s:%d: skipped\n", __func__, lno);
+ return 0;
+ }
+
+ if (tlv->verbosity > 1)
+ log_debug ("%s:%d: called (%p,%zu)\n", __func__, lno,
+ tlv->buffer, tlv->bufsize);
+ /* If we are at the end of an ndef container pop the stack. */
+ if (!tlv->in_ndef && !tlv->bufsize)
+ {
+ if (tlv->verbosity > 1)
+ for (i=0; i < tlv->stacklen; i++)
+ log_debug ("%s: stack[%d] (%p,@%zu,%zu) len=%zu (%zu) %s\n",
+ __func__, i,
+ tlv->stack[i].buffer,
+ tlv->stack[i].buffer - tlv->origbuffer,
+ tlv->stack[i].bufsize,
+ tlv->stack[i].length,
+ tlv->stack[i].crammed,
+ tlv->stack[i].in_ndef? " ndef":"");
+ do
+ err = _tlv_pop (tlv);
+ while (!err && !tlv->in_ndef && !tlv->bufsize);
+
+ if (err)
+ return (tlv->lasterr = err);
+ if (tlv->verbosity > 1)
+ log_debug ("%s: container(s) closed due to size (lvl=%d)\n",
+ __func__, tlv->stacklen);
+ }
+
+ again:
+ /* Get the next tag. */
+ save_buffer = buffer = tlv->buffer;
+ save_bufsize = tlv->bufsize;
+ err = parse_tag (&buffer, &tlv->bufsize, &tlv->ti);
+ tlv->buffer = (unsigned char *)buffer;
+ if (err)
+ {
+ if (tlv->verbosity > 1)
+ {
+ log_debug ("%s: reading tag returned err=%d\n", __func__, err);
+ log_printhex (save_buffer, save_bufsize > 40? 40: save_bufsize,
+ "%s: data was\n", __func__);
+ dump_to_file (tlv->origbuffer, save_buffer - tlv->origbuffer,
+ "parseerr");
+ }
+ return err;
+ }
+
+ if ( ( (tlv->ti.class == CLASS_UNIVERSAL && tlv->ti.tag == TAG_OCTET_STRING)
+ || ((flag & TLV_PARSER_FLAG_T5793)
+ && tlv->ti.class == CLASS_CONTEXT && tlv->ti.tag == 0))
+ && tlv->ti.is_constructed && cram_octet_string (tlv, 1))
+ {
+ if (tlv->verbosity > 1)
+ log_debug ("%s: cramming %s\n", __func__,
+ tlv->ti.tag? "constructed octet strings":"for Mozilla bug");
+ if (!cram_octet_string (tlv, 0))
+ return (tlv->lasterr = gpg_error (GPG_ERR_BAD_BER));
+ }
+
+ /* If there is an end tag in an ndef container pop the stack. Also
+ * pop other containers which are fully consumed. */
+ if (tlv->in_ndef && (tlv->ti.class == CLASS_UNIVERSAL
+ && !tlv->ti.tag && !tlv->ti.is_constructed))
+ {
+ do
+ err = _tlv_pop (tlv);
+ while (!err && !tlv->in_ndef && !tlv->bufsize);
+ if (err)
+ return (tlv->lasterr = err);
+ if (tlv->verbosity > 1)
+ log_debug ("%s: container(s) closed due to end tag (lvl=%d)\n",
+ __func__, tlv->stacklen);
+ goto again;
+ }
+
+ _tlv_parser_dump_tag (__func__, lno, tlv);
+ return 0;
+}
+
+
+/* Return the current neting level of the TLV object. */
+unsigned int
+tlv_parser_level (tlv_parser_t tlv)
+{
+ return tlv? tlv->stacklen : 0;
+}
+
+/* Returns the current offset of the parser. */
+size_t
+tlv_parser_offset (tlv_parser_t tlv)
+{
+ return tlv? (size_t)(tlv->buffer - tlv->origbuffer) : 0;
+}
+
+
+/* Return a string with the last function used. If TLV is NULL an
+ * empty string is returned. */
+const char *
+tlv_parser_lastfunc (tlv_parser_t tlv)
+{
+ return tlv? tlv->lastfunc:"";
+}
+
+
+const char *
+tlv_parser_lasterrstr (tlv_parser_t tlv)
+{
+ return tlv? gpg_strerror (tlv->lasterr) : "tlv parser not yet initialized";
+}
+
+
+/* Set a flag to indicate that the last tlv_next has not yet been
+ * consumed. */
+void
+tlv_parser_set_pending (tlv_parser_t tlv)
+{
+ tlv->pending = 1;
+}
+
+
+/* Return the length of the last read tag. If with_header is 1 the
+ * lengtb of the header is added to the returned length. */
+size_t
+tlv_parser_tag_length (tlv_parser_t tlv, int with_header)
+{
+ if (with_header)
+ return tlv->ti.length + tlv->ti.nhdr;
+ else
+ return tlv->ti.length;
+}
+
+
+/* Skip over the value of the current tag. Does not yet work for ndef
+ * containers. */
+void
+tlv_parser_skip (tlv_parser_t tlv)
+{
+ tlv->lastfunc = __func__;
+ log_assert (tlv->bufsize >= tlv->ti.length);
+ tlv->buffer += tlv->ti.length;
+ tlv->bufsize -= tlv->ti.length;
+}
+
+
+/* Expect that the current tag is a sequence and setup the context for
+ * processing. */
+gpg_error_t
+tlv_expect_sequence (tlv_parser_t tlv)
+{
+ tlv->lastfunc = __func__;
+ if (!(tlv->ti.class == CLASS_UNIVERSAL && tlv->ti.tag == TAG_SEQUENCE
+ && tlv->ti.is_constructed))
+ return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ));
+ return _tlv_push (tlv);
+}
+
+
+/* Expect that the current tag is a context tag and setup the context
+ * for processing. The tag of the context is returned at R_TAG. */
+gpg_error_t
+tlv_expect_context_tag (tlv_parser_t tlv, int *r_tag)
+{
+ tlv->lastfunc = __func__;
+ if (!(tlv->ti.class == CLASS_CONTEXT && tlv->ti.is_constructed))
+ return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ));
+ *r_tag = tlv->ti.tag;
+ return _tlv_push (tlv);
+}
+
+
+/* Expect that the current tag is a SET and setup the context for
+ * processing. */
+gpg_error_t
+tlv_expect_set (tlv_parser_t tlv)
+{
+ tlv->lastfunc = __func__;
+ if (!(tlv->ti.class == CLASS_UNIVERSAL && tlv->ti.tag == TAG_SET
+ && tlv->ti.is_constructed))
+ return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ));
+ return _tlv_push (tlv);
+}
+
+
+/* Expect an object of CLASS with TAG and store its value at
+ * (R_DATA,R_DATALEN). Then skip over its value to the next tag.
+ * Note that the stored value is not allocated but points into
+ * TLV. */
+gpg_error_t
+tlv_expect_object (tlv_parser_t tlv, int class, int tag,
+ unsigned char const **r_data, size_t *r_datalen)
+{
+ const unsigned char *p;
+ size_t n;
+ int needpush = 0;
+
+ tlv->lastfunc = __func__;
+ /* Note that the parser has already crammed the octet strings for a
+ * [0] to workaround the Mozilla bug. */
+ if (!(tlv->ti.class == class && tlv->ti.tag == tag))
+ return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ));
+ p = tlv->buffer;
+ n = tlv->ti.length;
+ if (!n && tlv->ti.ndef)
+ {
+ n = tlv->bufsize;
+ needpush = 1;
+ }
+ else if (!tlv->ti.length)
+ return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT));
+
+ if (tlv->verbosity > 1)
+ log_debug ("%s: %zu@%zu %c len=%zu (%zu) bufsize=%zu of %zu\n",
+ __func__,
+ tlv->origoff, tlv_parser_offset (tlv),
+ tlv->ti.is_constructed? 'c':'p',
+ n, tlv->crammed,
+ tlv->bufsize, tlv->origbufsize);
+
+ if (r_data)
+ *r_data = p;
+ if (r_datalen)
+ *r_datalen = tlv->crammed? tlv->crammed : n;
+
+ if (needpush)
+ return _tlv_push (tlv);
+
+ if (!(tlv->bufsize >= n))
+ return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT));
+ tlv->buffer += n;
+ tlv->bufsize -= n;
+ tlv->crammed = 0;
+ return 0;
+}
+
+
+/* Expect that the current tag is an object string and store its value
+ * at (R_DATA,R_DATALEN). Then skip over its value to the next tag.
+ * Note that the stored value are not allocated but point into TLV. */
+gpg_error_t
+tlv_expect_octet_string (tlv_parser_t tlv,
+ unsigned char const **r_data, size_t *r_datalen)
+{
+ size_t n;
+
+ tlv->lastfunc = __func__;
+ /* The parser has already crammed constructed octet strings. */
+ if (!(tlv->ti.class == CLASS_UNIVERSAL && tlv->ti.tag == TAG_OCTET_STRING))
+ return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ));
+ if (!(n=tlv->ti.length) || tlv->ti.ndef )
+ return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT));
+
+ if (tlv->verbosity > 1)
+ log_debug ("%s: %zu@%zu %c len=%zu (%zu) bufsize=%zu of %zu\n",
+ __func__,
+ tlv->origoff, tlv_parser_offset (tlv),
+ tlv->ti.is_constructed? 'c':'p',
+ n, tlv->crammed,
+ tlv->bufsize, tlv->origbufsize);
+
+ if (r_data)
+ *r_data = tlv->buffer;
+ if (r_datalen)
+ *r_datalen = tlv->crammed? tlv->crammed : tlv->ti.length;
+
+ if (!(tlv->bufsize >= tlv->ti.length))
+ return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT));
+ tlv->buffer += tlv->ti.length;
+ tlv->bufsize -= tlv->ti.length;
+ tlv->crammed = 0;
+ return 0;
+}
+
+
+/* Expect that the current tag is an integer and return its value at
+ * R_VALUE. Then skip over its value to the next tag. */
+gpg_error_t
+tlv_expect_integer (tlv_parser_t tlv, int *r_value)
+{
+ const unsigned char *p;
+ size_t n;
+ int value;
+
+ tlv->lastfunc = __func__;
+ if (!(tlv->ti.class == CLASS_UNIVERSAL && tlv->ti.tag == TAG_INTEGER
+ && !tlv->ti.is_constructed))
+ return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ));
+ p = tlv->buffer;
+ if (!(n=tlv->ti.length))
+ return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT));
+
+ /* We currently support only positive values. */
+ if ((*p & 0x80))
+ return (tlv->lasterr = gpg_error (GPG_ERR_ERANGE));
+
+ for (value = 0; n; n--)
+ {
+ value <<= 8;
+ value |= (*p++) & 0xff;
+ if (value < 0)
+ return (tlv->lasterr = gpg_error (GPG_ERR_EOVERFLOW));
+ }
+ *r_value = value;
+ if (!(tlv->bufsize >= tlv->ti.length))
+ return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT));
+ tlv->buffer += tlv->ti.length;
+ tlv->bufsize -= tlv->ti.length;
+ return 0;
+}
+
+
+/* Variant of tlv_expect_integer which returns an MPI. If IGNORE_ZERO
+ * is set a value of 0 is ignored and R_VALUE not changed and the
+ * function returns GPG_ERR_FALSE. No check for negative encoded
+ * integers is done because the old code here worked the same and we
+ * can't foreclose invalid encoded PKCS#12 stuff - after all it is
+ * PKCS#12 see https://www.cs.auckland.ac.nz/~pgut001/pubs/pfx.html */
+#ifdef GCRYPT_VERSION
+gpg_error_t
+tlv_expect_mpinteger (tlv_parser_t tlv, int ignore_zero,
+ gcry_mpi_t *r_value)
+{
+ const unsigned char *p;
+ size_t n;
+
+ tlv->lastfunc = __func__;
+ if (!(tlv->ti.class == CLASS_UNIVERSAL && tlv->ti.tag == TAG_INTEGER
+ && !tlv->ti.is_constructed))
+ return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ));
+ p = tlv->buffer;
+ if (!(n=tlv->ti.length))
+ return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT));
+
+ if (!(tlv->bufsize >= tlv->ti.length))
+ return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT));
+ tlv->buffer += tlv->ti.length;
+ tlv->bufsize -= tlv->ti.length;
+ if (ignore_zero && n == 1 && !*p)
+ return gpg_error (GPG_ERR_FALSE);
+
+ return gcry_mpi_scan (r_value, GCRYMPI_FMT_USG, p, n, NULL);
+}
+#endif /*GCRYPT_VERSION*/
+
+
+/* Expect that the current tag is an object id and store its value at
+ * (R_OID,R_OIDLEN). Then skip over its value to the next tag. Note
+ * that the stored value is not allocated but points into TLV. */
+gpg_error_t
+tlv_expect_object_id (tlv_parser_t tlv,
+ unsigned char const **r_oid, size_t *r_oidlen)
+{
+ const unsigned char *p;
+ size_t n;
+
+ tlv->lastfunc = __func__;
+ if (!(tlv->ti.class == CLASS_UNIVERSAL && tlv->ti.tag == TAG_OBJECT_ID
+ && !tlv->ti.is_constructed))
+ return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ));
+ p = tlv->buffer;
+ if (!(n=tlv->ti.length))
+ return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT));
+
+ *r_oid = p;
+ *r_oidlen = tlv->ti.length;
+ if (!(tlv->bufsize >= tlv->ti.length))
+ return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT));
+ tlv->buffer += tlv->ti.length;
+ tlv->bufsize -= tlv->ti.length;
+ return 0;
+}
+
+
+/* Expect a NULL tag. */
+gpg_error_t
+tlv_expect_null (tlv_parser_t tlv)
+{
+ tlv->lastfunc = __func__;
+ if (!(tlv->ti.class == CLASS_UNIVERSAL && tlv->ti.tag == TAG_NULL
+ && !tlv->ti.is_constructed && !tlv->ti.length))
+ return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ));
+ return 0;
+}
+
+
+/* Given a BER encoded constructed octet string like the example below
+ * from Mozilla Firefox 1.0.4 (which actually exports certs as single
+ * byte chunks of octet strings) in the buffer described by TLV.
+ * Although the example uses an ndef for the length of the constructed
+ * octet string, a fixed length is also allowed.
+ *
+ * 24 NDEF: OCTET STRING
+ * 04 1: OCTET STRING -- TLV->buffer points to here
+ * : 30
+ * 04 1: OCTET STRING
+ * : 80
+ * [...]
+ * 04 2: OCTET STRING
+ * : 00 00
+ * : } -- This denotes a Null tag and are the last
+ * -- two bytes in INPUT.
+ *
+ * Turn it into a primitive octet string of this form:
+ *
+ * 24 2: OCTET STRING
+ * : 30 80
+ *
+ * and fill it up with FE to the original length. Unless TESTMODE is
+ * true the TLV object including the the member and the data is
+ * adjusted accordingly; however the intiial tag is not changed (in
+ * the example the "24 NDEF") because this is not needed anymore.
+ *
+ * On error 0 is returned and in this case the buffer might have
+ * already been modified and thus the caller should better stop
+ * parsing - unless TESTMODE was used. */
+static size_t
+cram_octet_string (tlv_parser_t tlv, int testmode)
+{
+ gpg_error_t err;
+ size_t totallen; /* Length of the non-crammed octet strings. */
+ size_t crammedlen; /* Length of the crammed octet strings. */
+ const unsigned char *s, *save_s;
+ unsigned char *d;
+ size_t n, save_n;
+ struct tag_info ti;
+
+ if (tlv->ti.class == CLASS_UNIVERSAL && tlv->ti.tag == TAG_OCTET_STRING)
+ ; /* Okay. */
+ else if (tlv->ti.class == CLASS_CONTEXT && tlv->ti.tag == 0)
+ ; /* Workaround for Mozilla bug; see T5793 */
+ else
+ return 0; /* Oops - we should not have been called. */
+ if (!tlv->ti.is_constructed)
+ return 0; /* Oops - Not a constructed octet string. */
+ if (!tlv->ti.ndef && tlv->ti.length < 4)
+ return 0; /* Fixed length but too short. */
+
+ /* Let S point to the first octet string chunk. */
+ s = tlv->buffer;
+ n = tlv->ti.ndef? tlv->bufsize : tlv->ti.length;
+
+ d = (unsigned char *)s;
+ totallen = crammedlen = 0;
+ while (n)
+ {
+ save_s = s;
+ save_n = n;
+ if ((err=parse_tag (&s, &n, &ti)))
+ {
+ if (tlv->verbosity > 1)
+ {
+ log_debug ("%s: parse_tag(n=%zu) failed : %s\n",
+ __func__, save_n, gpg_strerror (err));
+ log_printhex (save_s, save_n > 40? 40:save_n, "%s: data was",
+ __func__);
+ }
+ return 0;
+ }
+ if (tlv->verbosity > 1)
+ log_debug ("%s:%s ti.ndef=%d ti.tag=%lu ti.length=%zu (n %zu->%zu)\n",
+ __func__, testmode?"test:":"",
+ ti.ndef, ti.tag, ti.length, save_n, n);
+ if (ti.class == CLASS_UNIVERSAL && ti.tag == TAG_OCTET_STRING
+ && !ti.ndef && !ti.is_constructed)
+ {
+ if (!testmode)
+ memmove (d, s, ti.length);
+ d += ti.length; /* Update destination */
+ totallen += ti.length + ti.nhdr;
+ crammedlen += ti.length;
+ s += ti.length; /* Skip to next tag. */
+ n -= ti.length;
+ }
+ else if (ti.class == CLASS_UNIVERSAL && !ti.tag && !ti.is_constructed)
+ {
+ totallen += ti.nhdr;
+ break; /* EOC - Ready */
+ }
+ else
+ return 0; /* Invalid data. */
+ }
+ if (!testmode)
+ {
+ memset (d, '\xfe', totallen - crammedlen);
+ tlv->ti.length = totallen;
+ tlv->ti.is_constructed = 0;
+ tlv->ti.ndef = 0;
+ tlv->crammed = crammedlen;
+ if (tlv->verbosity > 1)
+ {
+ log_debug ("%s: crammed length is %zu\n", __func__, crammedlen);
+ log_debug ("%s: total length is %zu\n", __func__, totallen);
+ }
+ dump_to_file (tlv->buffer, totallen, "crammed");
+ }
+ return totallen;
+}