aboutsummaryrefslogtreecommitdiffstats
path: root/sm/minip12.c
diff options
context:
space:
mode:
authorWerner Koch <[email protected]>2023-06-28 15:33:24 +0000
committerWerner Koch <[email protected]>2023-06-28 15:34:19 +0000
commit101433dfb42b333e48427baf9dd58ac4787c9786 (patch)
tree34bddebb7e77d36f3b200b3c6ce2a1b603769441 /sm/minip12.c
parentsm: Remove duplicated code. (diff)
downloadgnupg-101433dfb42b333e48427baf9dd58ac4787c9786.tar.gz
gnupg-101433dfb42b333e48427baf9dd58ac4787c9786.zip
sm: Major rewrite of the PKCS#12 parser
* sm/minip12.c: Reworked most of the parser. (p12_set_verbosity): Add arg debug and change all callers. * sm/t-minip12.c: Major rewrite to run regression tests unattended. * sm/Makefile.am (module_maint_tests): Move t-Minit to ... (module_tests): here. * tests/cms/samplekeys/Description-p12: New. -- Note that cram_octet_string stuff has not yet been reworked. I need to locate the sample files first. GnuPG-bug-id: 6536
Diffstat (limited to 'sm/minip12.c')
-rw-r--r--sm/minip12.c1943
1 files changed, 1200 insertions, 743 deletions
diff --git a/sm/minip12.c b/sm/minip12.c
index 695c60ff1..69e23455a 100644
--- a/sm/minip12.c
+++ b/sm/minip12.c
@@ -1,7 +1,7 @@
/* minip12.c - A minimal pkcs-12 implementation.
* Copyright (C) 2002, 2003, 2004, 2006, 2011 Free Software Foundation, Inc.
* Copyright (C) 2014 Werner Koch
- * Copyright (C) 2022 g10 Code GmbH
+ * Copyright (C) 2022, 2023 g10 Code GmbH
*
* This file is part of GnuPG.
*
@@ -78,6 +78,8 @@ static unsigned char const oid_pkcs5PBES2[9] = {
0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x05, 0x0D };
static unsigned char const oid_aes128_CBC[9] = {
0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x01, 0x02 };
+static unsigned char const oid_aes256_CBC[9] = {
+ 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x01, 0x2A };
static unsigned char const oid_rsaEncryption[9] = {
0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 };
@@ -143,6 +145,35 @@ struct tag_info
int ndef; /* It is an indefinite length */
};
+
+#define TLV_MAX_DEPTH 20
+
+/* An object to control the ASN.1 parsing. */
+struct tlv_ctx_s
+{
+ /* The current buffer we are working on and its length. */
+ const unsigned char *buffer;
+ size_t bufsize;
+
+ size_t offset; /* The current offset into this buffer. */
+ 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. */
+
+ unsigned int pop_count;/* Number of pops by tlv_next. */
+ unsigned int stacklen; /* Used size of the stack. */
+ struct {
+ const unsigned char *buffer; /* Saved value of BUFFER. */
+ size_t bufsize; /* Saved value of BUFSIZE. */
+ size_t offset; /* Saved value of OFFSET. */
+ int in_ndef; /* Saved IN_NDEF flag. */
+ } stack[TLV_MAX_DEPTH];
+};
+
+
/* Parser communication object. */
struct p12_parse_ctx_s
{
@@ -168,23 +199,24 @@ static int opt_verbose;
void
-p12_set_verbosity (int verbose)
+p12_set_verbosity (int verbose, int debug)
{
- opt_verbose = verbose;
+ opt_verbose = !!verbose;
+ if (debug)
+ opt_verbose = 2;
}
-#if 0
static void
dump_tag_info (const char *text, struct tag_info *ti)
{
- log_debug ("p12_parse(%s): ti.class=%d tag=%lu len=%zu nhdr=%zu %s%s\n",
- text,
- ti->class, ti->tag, ti->length, ti->nhdr,
- ti->is_constructed?" cons":"",
- ti->ndef?" ndef":"");
+ if (opt_verbose > 1)
+ log_debug ("p12_parse(%s): ti.class=%d tag=%lu len=%zu nhdr=%zu %s%s\n",
+ text,
+ ti->class, ti->tag, ti->length, ti->nhdr,
+ ti->is_constructed?" cons":"",
+ ti->ndef?" ndef":"");
}
-#endif
/* Wrapper around tlv_builder_add_ptr to add an OID. When we
@@ -279,6 +311,7 @@ builder_add_mpi (tlv_builder_t tb, int class, int tag, gcry_mpi_t mpi,
}
+
/* 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
@@ -306,6 +339,382 @@ parse_tag (unsigned char const **buffer, size_t *size, struct tag_info *ti)
}
+/* Create a new TLV object. */
+static struct tlv_ctx_s *
+tlv_new (const unsigned char *buffer, size_t bufsize)
+{
+ struct tlv_ctx_s *tlv;
+ tlv = xtrycalloc (1, sizeof *tlv);
+ if (tlv)
+ {
+ tlv->buffer = buffer;
+ tlv->bufsize = bufsize;
+ }
+ return tlv;
+}
+
+
+static void
+tlv_release (struct tlv_ctx_s *tlv)
+{
+ xfree (tlv);
+}
+
+
+/* Helper for tlv_next and tlv_peek. */
+static gpg_error_t
+_tlv_peek (struct tlv_ctx_s *tlv, size_t *r_n)
+{
+ const unsigned char *p;
+
+ if (tlv->offset > tlv->bufsize)
+ return gpg_error (GPG_ERR_BUG);
+ p = tlv->buffer + tlv->offset;
+ *r_n = tlv->bufsize - tlv->offset;
+ return parse_tag (&p, r_n, &tlv->ti);
+}
+
+
+/* Helper for tlv_expect_sequence and tlv_expect_context_tag. */
+static gpg_error_t
+_tlv_push (struct tlv_ctx_s *tlv)
+{
+ 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].offset = tlv->offset;
+ tlv->stack[tlv->stacklen].in_ndef = tlv->in_ndef;
+ tlv->stacklen++;
+ tlv->buffer += tlv->offset;
+ tlv->bufsize = tlv->ti.length;
+ tlv->offset = 0;
+ tlv->in_ndef = tlv->ti.ndef;
+ return 0;
+}
+
+
+/* Helper for tlv_next. */
+static gpg_error_t
+_tlv_pop (struct tlv_ctx_s *tlv)
+{
+ size_t saveoff;
+
+ if (!tlv->stacklen)
+ return gpg_error (GPG_ERR_EOF);
+
+ saveoff = tlv->offset;
+
+ tlv->stacklen--;
+ tlv->buffer = tlv->stack[tlv->stacklen].buffer;
+ tlv->bufsize = tlv->stack[tlv->stacklen].bufsize;
+ tlv->offset = tlv->stack[tlv->stacklen].offset;
+ tlv->in_ndef = tlv->stack[tlv->stacklen].in_ndef;
+
+ /* Move offset of the container to the end of the container. */
+ tlv->offset += saveoff;
+ if (tlv->offset > tlv->bufsize)
+ return gpg_error (GPG_ERR_INV_BER);
+
+ tlv->pop_count++;
+ return 0;
+}
+
+
+/* Parse the next tag and value. Also detect the end of a container;
+ * tlv_popped() can be used to detect this. */
+static gpg_error_t
+tlv_next (struct tlv_ctx_s *tlv)
+{
+ gpg_error_t err;
+ size_t n;
+
+ tlv->pop_count = 0;
+ tlv->lasterr = 0;
+ tlv->lastfunc = __func__;
+ if (tlv->pending)
+ {
+ tlv->pending = 0;
+ return 0;
+ }
+
+ if (!tlv->in_ndef && tlv->offset == tlv->bufsize)
+ {
+ /* We are at the end of a container. Pop the stack. */
+ do
+ err = _tlv_pop (tlv);
+ while (!err && !tlv->in_ndef && tlv->offset == tlv->bufsize);
+ if (err)
+ return (tlv->lasterr = err);
+ }
+
+ err = _tlv_peek (tlv, &n);
+ if (err)
+ return err;
+ if (tlv->in_ndef && (tlv->ti.class == CLASS_UNIVERSAL
+ && !tlv->ti.tag && !tlv->ti.is_constructed))
+ {
+ /* End tag while in ndef container. Skip the tag, and pop. */
+ tlv->offset += n - (tlv->bufsize - tlv->offset);
+ err = _tlv_pop (tlv);
+ // FIXME see above and run peek again.
+ if (err)
+ return (tlv->lasterr = err);
+ }
+
+ /* Set offset to the value of the TLV. */
+ tlv->offset += tlv->bufsize - tlv->offset - n;
+ dump_tag_info ("tlv_next", &tlv->ti);
+ return 0;
+}
+
+
+/* Return the current neting level of the TLV object. */
+static unsigned int
+tlv_level (struct tlv_ctx_s *tlv)
+{
+ return tlv->stacklen;
+}
+
+
+/* If called right after tlv_next the number of container levels
+ * popped are returned. */
+static unsigned int
+tlv_popped (struct tlv_ctx_s *tlv)
+{
+ return tlv->pop_count;
+}
+
+
+/* Set a flag to indicate that the last tlv_next has not yet been
+ * consumed. */
+static void
+tlv_set_pending (struct tlv_ctx_s *tlv)
+{
+ tlv->pending = 1;
+}
+
+
+/* Skip over the value of the current tag. */
+static void
+tlv_skip (struct tlv_ctx_s *tlv)
+{
+ tlv->lastfunc = __func__;
+ tlv->offset += tlv->ti.length;
+}
+
+
+/* Expect that the current tag is a sequence and setup the context for
+ * processing. */
+static gpg_error_t
+tlv_expect_sequence (struct tlv_ctx_s *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);
+}
+
+/* Variant of tlv_expect_sequence to be used for the ouyter sequence
+ * of an object which might have padding after the ASN.1 data. */
+static gpg_error_t
+tlv_expect_top_sequence (struct tlv_ctx_s *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));
+ tlv->bufsize = tlv->ti.nhdr + tlv->ti.length;
+ 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. */
+static gpg_error_t
+tlv_expect_context_tag (struct tlv_ctx_s *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. */
+static gpg_error_t
+tlv_expect_set (struct tlv_ctx_s *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 are not allocated but point into
+ * TLV. */
+static gpg_error_t
+tlv_expect_object (struct tlv_ctx_s *tlv, int class, int tag,
+ unsigned char const **r_data, size_t *r_datalen)
+{
+ const unsigned char *p;
+
+ tlv->lastfunc = __func__;
+ if (!(tlv->ti.class == class && tlv->ti.tag == tag
+ && !tlv->ti.is_constructed))
+ return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ));
+ p = tlv->buffer + tlv->offset;
+ if (!tlv->ti.length)
+ return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT));
+
+ *r_data = p;
+ *r_datalen = tlv->ti.length;
+
+ tlv->offset += tlv->ti.length;
+ 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.
+ * If ENCAPSULATES is set the octet string is used as a new
+ * container. R_DATA and R_DATALEN are optional. */
+static gpg_error_t
+tlv_expect_octet_string (struct tlv_ctx_s *tlv, int encapsulates,
+ unsigned char const **r_data, size_t *r_datalen)
+{
+ const unsigned char *p;
+ size_t n;
+
+ tlv->lastfunc = __func__;
+ if (!(tlv->ti.class == CLASS_UNIVERSAL && tlv->ti.tag == TAG_OCTET_STRING
+ && !tlv->ti.is_constructed))
+ return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ));
+ p = tlv->buffer + tlv->offset;
+ if (!(n=tlv->ti.length))
+ return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT));
+
+ if (r_data)
+ *r_data = p;
+ if (r_datalen)
+ *r_datalen = tlv->ti.length;
+ if (encapsulates)
+ return _tlv_push (tlv);
+
+ tlv->offset += tlv->ti.length;
+ return 0;
+}
+
+
+/* Expect a NULL tag. */
+static gpg_error_t
+tlv_expect_null (struct tlv_ctx_s *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;
+}
+
+
+/* Expect that the current tag is an integer and return its value at
+ * R_VALUE. Then skip over its value to the next tag. */
+static gpg_error_t
+tlv_expect_integer (struct tlv_ctx_s *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 + tlv->offset;
+ 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;
+ tlv->offset += 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 doe 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 */
+static gpg_error_t
+tlv_expect_mpinteger (struct tlv_ctx_s *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 + tlv->offset;
+ if (!(n=tlv->ti.length))
+ return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT));
+
+ tlv->offset += 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);
+}
+
+
+/* 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. */
+static gpg_error_t
+tlv_expect_object_id (struct tlv_ctx_s *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 + tlv->offset;
+ if (!(n=tlv->ti.length))
+ return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT));
+
+ *r_oid = p;
+ *r_oidlen = tlv->ti.length;
+ tlv->offset += tlv->ti.length;
+ return 0;
+}
+
+
+
/* Given an ASN.1 chunk of a structure like:
24 NDEF: OCTET STRING -- This is not passed to us
@@ -561,7 +970,7 @@ crypt_block (unsigned char *buffer, size_t length, char *salt, size_t saltlen,
return;
}
- if (cipher_algo == GCRY_CIPHER_AES128
+ if ((cipher_algo == GCRY_CIPHER_AES128 || cipher_algo == GCRY_CIPHER_AES256)
? set_key_iv_pbes2 (chd, salt, saltlen, iter, iv, ivlen, pw, cipher_algo)
: set_key_iv (chd, salt, saltlen, iter, pw,
cipher_algo == GCRY_CIPHER_RFC2268_40? 5:24))
@@ -708,482 +1117,508 @@ bag_decrypted_data_p (const void *plaintext, size_t length)
static int
-parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx,
- const unsigned char *buffer, size_t length,
- int startoffset, size_t *r_consumed)
+parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv)
{
- struct tag_info ti;
- const unsigned char *p = buffer;
- const unsigned char *p_start = buffer;
- size_t n = length;
+ gpg_error_t err = 0;
const char *where;
+ const unsigned char *oid;
+ size_t oidlen;
+ const unsigned char *data;
+ size_t datalen;
+ int intval;
char salt[20];
size_t saltlen;
char iv[16];
unsigned int iter;
unsigned char *plain = NULL;
- unsigned char *cram_buffer = NULL;
- size_t consumed = 0; /* Number of bytes consumed from the original buffer. */
int is_3des = 0;
int is_pbes2 = 0;
+ int is_aes256 = 0;
int keyelem_count;
+ int renewed_tlv = 0;
+ int loopcount;
+ unsigned int startlevel;
- where = "start";
- if (parse_tag (&p, &n, &ti))
+ where = "bag.encryptedData";
+ if (opt_verbose)
+ log_info ("processing %s\n", where);
+
+ if (tlv_next (tlv))
goto bailout;
- if (ti.class != CLASS_CONTEXT || ti.tag)
+ if (tlv_expect_context_tag (tlv, &intval) || intval != 0 )
goto bailout;
- if (parse_tag (&p, &n, &ti))
+
+ if (tlv_next (tlv))
goto bailout;
- if (ti.tag != TAG_SEQUENCE)
+ if (tlv_expect_sequence (tlv))
goto bailout;
where = "bag.encryptedData.version";
- if (parse_tag (&p, &n, &ti))
+ if (tlv_next (tlv))
goto bailout;
- if (ti.tag != TAG_INTEGER || ti.length != 1 || *p != 0)
+ if ((err = tlv_expect_integer (tlv, &intval)))
goto bailout;
- p++; n--;
- if (parse_tag (&p, &n, &ti))
+ if (intval)
+ {
+ err = gpg_error (GPG_ERR_INV_VALUE);
+ goto bailout;
+ }
+
+ if (tlv_next (tlv))
goto bailout;
- if (ti.tag != TAG_SEQUENCE)
+ if (tlv_expect_sequence (tlv))
goto bailout;
where = "bag.encryptedData.data";
- if (parse_tag (&p, &n, &ti))
+ if (tlv_next (tlv))
goto bailout;
- if (ti.tag != TAG_OBJECT_ID || ti.length != DIM(oid_data)
- || memcmp (p, oid_data, DIM(oid_data)))
+ if (tlv_expect_object_id (tlv, &oid, &oidlen))
+ goto bailout;
+ if (oidlen != DIM(oid_data) || memcmp (oid, oid_data, DIM(oid_data)))
goto bailout;
- p += DIM(oid_data);
- n -= DIM(oid_data);
where = "bag.encryptedData.keyinfo";
- if (parse_tag (&p, &n, &ti))
+ if (tlv_next (tlv))
goto bailout;
- if (ti.class || ti.tag != TAG_SEQUENCE)
+ if (tlv_expect_sequence (tlv))
goto bailout;
- if (parse_tag (&p, &n, &ti))
+
+ if (tlv_next (tlv))
+ goto bailout;
+ if (tlv_expect_object_id (tlv, &oid, &oidlen))
goto bailout;
- if (!ti.class && ti.tag == TAG_OBJECT_ID
- && ti.length == DIM(oid_pbeWithSHAAnd40BitRC2_CBC)
- && !memcmp (p, oid_pbeWithSHAAnd40BitRC2_CBC,
+ if (oidlen == DIM(oid_pbeWithSHAAnd40BitRC2_CBC)
+ && !memcmp (oid, oid_pbeWithSHAAnd40BitRC2_CBC,
DIM(oid_pbeWithSHAAnd40BitRC2_CBC)))
+ ;
+ else if (oidlen == DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC)
+ && !memcmp (oid, oid_pbeWithSHAAnd3_KeyTripleDES_CBC,
+ DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC)))
+ is_3des = 1;
+ else if (oidlen == DIM(oid_pkcs5PBES2)
+ && !memcmp (oid, oid_pkcs5PBES2, oidlen))
+ is_pbes2 = 1;
+ else
{
- p += DIM(oid_pbeWithSHAAnd40BitRC2_CBC);
- n -= DIM(oid_pbeWithSHAAnd40BitRC2_CBC);
- }
- else if (!ti.class && ti.tag == TAG_OBJECT_ID
- && ti.length == DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC)
- && !memcmp (p, oid_pbeWithSHAAnd3_KeyTripleDES_CBC,
- DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC)))
- {
- p += DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC);
- n -= DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC);
- is_3des = 1;
- }
- else if (!ti.class && ti.tag == TAG_OBJECT_ID
- && ti.length == DIM(oid_pkcs5PBES2)
- && !memcmp (p, oid_pkcs5PBES2, ti.length))
- {
- p += ti.length;
- n -= ti.length;
- is_pbes2 = 1;
+ err = gpg_error (GPG_ERR_UNKNOWN_ALGORITHM);
+ goto bailout;
}
- else
- goto bailout;
+ /*FIXME: This code is duplicated in parse_shrouded_key_bag. */
if (is_pbes2)
{
where = "pkcs5PBES2-params";
- if (parse_tag (&p, &n, &ti))
+ if (tlv_next (tlv))
goto bailout;
- if (ti.class || ti.tag != TAG_SEQUENCE)
+ if (tlv_expect_sequence (tlv))
goto bailout;
- if (parse_tag (&p, &n, &ti))
+
+ if (tlv_next (tlv))
goto bailout;
- if (ti.class || ti.tag != TAG_SEQUENCE)
+ if (tlv_expect_sequence (tlv))
goto bailout;
- if (parse_tag (&p, &n, &ti))
+
+ if (tlv_next (tlv))
goto bailout;
- if (!(!ti.class && ti.tag == TAG_OBJECT_ID
- && ti.length == DIM(oid_pkcs5PBKDF2)
- && !memcmp (p, oid_pkcs5PBKDF2, ti.length)))
- goto bailout; /* Not PBKDF2. */
- p += ti.length;
- n -= ti.length;
- if (parse_tag (&p, &n, &ti))
+ if (tlv_expect_object_id (tlv, &oid, &oidlen))
+ goto bailout;
+ if (oidlen != DIM(oid_pkcs5PBKDF2)
+ || memcmp (oid, oid_pkcs5PBKDF2, oidlen))
+ {
+ err = gpg_error (GPG_ERR_INV_BER); /* Not PBKDF2. */
+ goto bailout;
+ }
+
+ if (tlv_next (tlv))
goto bailout;
- if (ti.class || ti.tag != TAG_SEQUENCE)
+ if (tlv_expect_sequence (tlv))
goto bailout;
- if (parse_tag (&p, &n, &ti))
+
+ if (tlv_next (tlv))
goto bailout;
- if (!(!ti.class && ti.tag == TAG_OCTET_STRING
- && ti.length >= 8 && ti.length < sizeof salt))
- goto bailout; /* No salt or unsupported length. */
- saltlen = ti.length;
- memcpy (salt, p, saltlen);
- p += saltlen;
- n -= saltlen;
+ if (tlv_expect_octet_string (tlv, 0, &data, &datalen))
+ goto bailout;
+ if (datalen < 8 || datalen > sizeof salt)
+ {
+ log_info ("bad length of salt (%zu)\n", datalen);
+ err = gpg_error (GPG_ERR_INV_LENGTH);
+ goto bailout;
+ }
+ saltlen = datalen;
+ memcpy (salt, data, saltlen);
- if (parse_tag (&p, &n, &ti))
+ if (tlv_next (tlv))
goto bailout;
- if (!(!ti.class && ti.tag == TAG_INTEGER && ti.length))
- goto bailout; /* No valid iteration count. */
- for (iter=0; ti.length; ti.length--)
+ if ((err = tlv_expect_integer (tlv, &intval)))
+ goto bailout;
+ if (!intval) /* Not a valid iteration count. */
{
- iter <<= 8;
- iter |= (*p++) & 0xff;
- n--;
+ err = gpg_error (GPG_ERR_INV_VALUE);
+ goto bailout;
}
+ iter = intval;
+
/* Note: We don't support the optional parameters but assume
that the algorithmIdentifier follows. */
- if (parse_tag (&p, &n, &ti))
+ if (tlv_next (tlv))
goto bailout;
- if (ti.class || ti.tag != TAG_SEQUENCE)
+ if (tlv_expect_sequence (tlv))
goto bailout;
- if (parse_tag (&p, &n, &ti))
+
+ if (tlv_next (tlv))
goto bailout;
- if (!(!ti.class && ti.tag == TAG_OBJECT_ID
- && ti.length == DIM(oid_aes128_CBC)
- && !memcmp (p, oid_aes128_CBC, ti.length)))
- goto bailout; /* Not AES-128. */
- p += ti.length;
- n -= ti.length;
- if (parse_tag (&p, &n, &ti))
+ if (tlv_expect_object_id (tlv, &oid, &oidlen))
goto bailout;
- if (!(!ti.class && ti.tag == TAG_OCTET_STRING && ti.length == sizeof iv))
- goto bailout; /* Bad IV. */
- memcpy (iv, p, sizeof iv);
- p += sizeof iv;
- n -= sizeof iv;
+
+ if (oidlen == DIM(oid_aes128_CBC)
+ && !memcmp (oid, oid_aes128_CBC, oidlen))
+ ;
+ else if (oidlen == DIM(oid_aes256_CBC)
+ && !memcmp (oid, oid_aes256_CBC, oidlen))
+ is_aes256 = 1;
+ else
+ {
+ gpgrt_log_printhex (oid, oidlen, "cipher algo:");
+ err = gpg_error (GPG_ERR_CIPHER_ALGO);
+ goto bailout;
+ }
+
+ if (tlv_next (tlv))
+ goto bailout;
+ if (tlv_expect_octet_string (tlv, 0, &data, &datalen))
+ goto bailout;
+ if (datalen != sizeof iv)
+ {
+ err = gpg_error (GPG_ERR_INV_LENGTH);
+ goto bailout; /* Bad IV. */
+ }
+ memcpy (iv, data, datalen);
}
else
{
where = "rc2or3des-params";
- if (parse_tag (&p, &n, &ti))
+ if (tlv_next (tlv))
goto bailout;
- if (ti.class || ti.tag != TAG_SEQUENCE)
+ if (tlv_expect_sequence (tlv))
goto bailout;
- if (parse_tag (&p, &n, &ti))
+
+ if (tlv_next (tlv))
goto bailout;
- if (ti.class || ti.tag != TAG_OCTET_STRING
- || ti.length < 8 || ti.length > 20 )
+ if (tlv_expect_octet_string (tlv, 0, &data, &datalen))
goto bailout;
- saltlen = ti.length;
- memcpy (salt, p, saltlen);
- p += saltlen;
- n -= saltlen;
- if (parse_tag (&p, &n, &ti))
+ if (datalen < 8 || datalen > 20)
+ {
+ log_info ("bad length of salt (%zu) for 3DES\n", datalen);
+ err = gpg_error (GPG_ERR_INV_LENGTH);
+ goto bailout;
+ }
+ saltlen = datalen;
+ memcpy (salt, data, saltlen);
+
+ if (tlv_next (tlv))
goto bailout;
- if (ti.class || ti.tag != TAG_INTEGER || !ti.length )
+ if ((err = tlv_expect_integer (tlv, &intval)))
goto bailout;
- for (iter=0; ti.length; ti.length--)
+ if (!intval)
{
- iter <<= 8;
- iter |= (*p++) & 0xff;
- n--;
+ err = gpg_error (GPG_ERR_INV_VALUE);
+ goto bailout;
}
+ iter = intval;
}
where = "rc2or3desoraes-ciphertext";
- if (parse_tag (&p, &n, &ti))
+ if (tlv_next (tlv))
goto bailout;
- consumed = p - p_start;
- if (ti.class == CLASS_CONTEXT && ti.tag == 0 && ti.is_constructed && ti.ndef)
- {
- /* Mozilla exported certs now come with single byte chunks of
- octet strings. (Mozilla Firefox 1.0.4). Arghh. */
- where = "cram-rc2or3des-ciphertext";
- cram_buffer = cram_octet_string ( p, &n, &consumed);
- if (!cram_buffer)
- goto bailout;
- p = p_start = cram_buffer;
- if (r_consumed)
- *r_consumed = consumed;
- r_consumed = NULL; /* Donot update that value on return. */
- ti.length = n;
- }
- else if (ti.class == CLASS_CONTEXT && ti.tag == 0 && ti.is_constructed)
- {
- where = "octets-rc2or3des-ciphertext";
- n = ti.length;
- cram_buffer = cram_octet_string ( p, &n, &consumed);
- if (!cram_buffer)
- goto bailout;
- p = p_start = cram_buffer;
- if (r_consumed)
- *r_consumed = consumed;
- r_consumed = NULL; /* Do not update that value on return. */
- ti.length = n;
- }
- else if (ti.class == CLASS_CONTEXT && ti.tag == 0 && ti.length )
- ;
- else
+ /* consumed = p - p_start; */
+ /* if (ti.class == CLASS_CONTEXT && ti.tag == 0 && ti.is_constructed && ti.ndef) */
+ /* { */
+ /* /\* Mozilla exported certs now come with single byte chunks of */
+ /* octet strings. (Mozilla Firefox 1.0.4). Arghh. *\/ */
+ /* where = "cram-rc2or3des-ciphertext"; */
+ /* cram_buffer = cram_octet_string ( p, &n, &consumed); */
+ /* if (!cram_buffer) */
+ /* goto bailout; */
+ /* p = p_start = cram_buffer; */
+ /* if (r_consumed) */
+ /* *r_consumed = consumed; */
+ /* r_consumed = NULL; /\* Donot update that value on return. *\/ */
+ /* ti.length = n; */
+ /* } */
+ /* else if (ti.class == CLASS_CONTEXT && ti.tag == 0 && ti.is_constructed) */
+ /* { */
+ /* where = "octets-rc2or3des-ciphertext"; */
+ /* n = ti.length; */
+ /* cram_buffer = cram_octet_string ( p, &n, &consumed); */
+ /* if (!cram_buffer) */
+ /* goto bailout; */
+ /* p = p_start = cram_buffer; */
+ /* if (r_consumed) */
+ /* *r_consumed = consumed; */
+ /* r_consumed = NULL; /\* Do not update that value on return. *\/ */
+ /* ti.length = n; */
+ /* } */
+ /* else if (ti.class == CLASS_CONTEXT && ti.tag == 0 && ti.length ) */
+ /* ; */
+ /* else */
+ /* goto bailout; */
+ if (tlv_expect_object (tlv, CLASS_CONTEXT, 0, &data, &datalen))
goto bailout;
if (opt_verbose)
- log_info ("%lu bytes of %s encrypted text\n",ti.length,
- is_pbes2?"AES128":is_3des?"3DES":"RC2");
+ log_info ("%zu bytes of %s encrypted text\n", datalen,
+ is_pbes2?(is_aes256?"AES256":"AES128"):is_3des?"3DES":"RC2");
- plain = gcry_malloc_secure (ti.length);
+ plain = gcry_malloc_secure (datalen);
if (!plain)
{
+ err = gpg_error_from_syserror ();
log_error ("error allocating decryption buffer\n");
goto bailout;
}
- decrypt_block (p, plain, ti.length, salt, saltlen, iter,
+ decrypt_block (data, plain, datalen, salt, saltlen, iter,
iv, is_pbes2?16:0, ctx->password,
- is_pbes2 ? GCRY_CIPHER_AES128 :
+ is_pbes2 ? (is_aes256?GCRY_CIPHER_AES256:GCRY_CIPHER_AES128) :
is_3des ? GCRY_CIPHER_3DES : GCRY_CIPHER_RFC2268_40,
bag_decrypted_data_p);
- n = ti.length;
- startoffset = 0;
- p_start = p = plain;
- where = "outer.outer.seq";
- if (parse_tag (&p, &n, &ti))
+ /* We do not need the TLV anymore and allocated a new one. */
+ where = "bag.encryptedData.decrypted-text";
+ tlv = tlv_new (plain, datalen);
+ if (!tlv)
{
- ctx->badpass = 1;
+ err = gpg_error_from_syserror ();
goto bailout;
}
- if (ti.class || ti.tag != TAG_SEQUENCE)
+ renewed_tlv = 1;
+
+ if (tlv_next (tlv))
{
ctx->badpass = 1;
goto bailout;
}
-
- if (parse_tag (&p, &n, &ti))
+ if (tlv_expect_top_sequence (tlv))
{
ctx->badpass = 1;
goto bailout;
}
/* Loop over all certificates inside the bag. */
- while (n)
+ loopcount = 0;
+ startlevel = tlv_level (tlv);
+ while (!(err = tlv_next (tlv)) && tlv_level (tlv) == startlevel)
{
int iscrlbag = 0;
int iskeybag = 0;
+ loopcount++;
where = "certbag.nextcert";
- if (ti.class || ti.tag != TAG_SEQUENCE)
+ if (tlv_expect_sequence (tlv))
goto bailout;
- where = "certbag.objectidentifier";
- if (parse_tag (&p, &n, &ti))
+ where = "certbag.oid";
+ if (tlv_next (tlv))
goto bailout;
- if (ti.class || ti.tag != TAG_OBJECT_ID)
+ if (tlv_expect_object_id (tlv, &oid, &oidlen))
goto bailout;
- if ( ti.length == DIM(oid_pkcs_12_CertBag)
- && !memcmp (p, oid_pkcs_12_CertBag, DIM(oid_pkcs_12_CertBag)))
- {
- p += DIM(oid_pkcs_12_CertBag);
- n -= DIM(oid_pkcs_12_CertBag);
- }
- else if ( ti.length == DIM(oid_pkcs_12_CrlBag)
- && !memcmp (p, oid_pkcs_12_CrlBag, DIM(oid_pkcs_12_CrlBag)))
- {
- p += DIM(oid_pkcs_12_CrlBag);
- n -= DIM(oid_pkcs_12_CrlBag);
- iscrlbag = 1;
- }
- else if ( ti.length == DIM(oid_pkcs_12_keyBag)
- && !memcmp (p, oid_pkcs_12_keyBag, DIM(oid_pkcs_12_keyBag)))
+ if (oidlen == DIM(oid_pkcs_12_CertBag)
+ && !memcmp (oid, oid_pkcs_12_CertBag, DIM(oid_pkcs_12_CertBag)))
+ ;
+ else if (oidlen == DIM(oid_pkcs_12_CrlBag)
+ && !memcmp (oid, oid_pkcs_12_CrlBag, DIM(oid_pkcs_12_CrlBag)))
+ iscrlbag = 1;
+ else if (oidlen == DIM(oid_pkcs_12_keyBag)
+ && !memcmp (oid, oid_pkcs_12_keyBag, DIM(oid_pkcs_12_keyBag)))
{
/* The TrustedMIME plugin for MS Outlook started to create
files with just one outer 3DES encrypted container and
inside the certificates as well as the key. */
- p += DIM(oid_pkcs_12_keyBag);
- n -= DIM(oid_pkcs_12_keyBag);
iskeybag = 1;
}
else
- goto bailout;
+ {
+ gpgrt_log_printhex (oid, oidlen, "cert bag type OID:");
+ err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+ goto bailout;
+ }
where = "certbag.before.certheader";
- if (parse_tag (&p, &n, &ti))
+ if (tlv_next (tlv))
goto bailout;
- if (ti.class != CLASS_CONTEXT || ti.tag)
+ if (tlv_expect_context_tag (tlv, &intval) || intval != 0 )
goto bailout;
+
if (iscrlbag)
{
log_info ("skipping unsupported crlBag\n");
- p += ti.length;
- n -= ti.length;
}
else if (iskeybag && ctx->privatekey)
{
log_info ("one keyBag already processed; skipping this one\n");
- p += ti.length;
- n -= ti.length;
}
else if (iskeybag)
{
- int len;
-
if (opt_verbose)
log_info ("processing simple keyBag\n");
- /* Fixme: This code is duplicated from parse_bag_data. */
- if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_SEQUENCE)
+ if (tlv_next (tlv))
goto bailout;
- if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_INTEGER
- || ti.length != 1 || *p)
+ if (tlv_expect_sequence (tlv))
goto bailout;
- p++; n--;
- if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_SEQUENCE)
+
+ if (tlv_next (tlv))
goto bailout;
- len = ti.length;
- if (parse_tag (&p, &n, &ti))
+ if ((err = tlv_expect_integer (tlv, &intval)))
goto bailout;
- if (len < ti.nhdr)
+ if (intval)
+ {
+ err = gpg_error (GPG_ERR_INV_VALUE);
+ goto bailout;
+ }
+
+ if (tlv_next (tlv))
goto bailout;
- len -= ti.nhdr;
- if (ti.class || ti.tag != TAG_OBJECT_ID
- || ti.length != DIM(oid_rsaEncryption)
- || memcmp (p, oid_rsaEncryption,
- DIM(oid_rsaEncryption)))
+ if (tlv_expect_sequence (tlv))
goto bailout;
- p += DIM (oid_rsaEncryption);
- n -= DIM (oid_rsaEncryption);
- if (len < ti.length)
+
+ if (tlv_next (tlv))
+ goto bailout;
+ if (tlv_expect_object_id (tlv, &oid, &oidlen))
+ goto bailout;
+ if (oidlen != DIM(oid_rsaEncryption)
+ || memcmp (oid, oid_rsaEncryption, oidlen))
+ {
+ err = gpg_error (GPG_ERR_PUBKEY_ALGO);
+ goto bailout;
+ }
+
+ /* We ignore the next octet string. */
+ if (tlv_next (tlv))
goto bailout;
- len -= ti.length;
- if (n < len)
+ if (tlv_expect_octet_string (tlv, 0, &data, &datalen))
goto bailout;
- p += len;
- n -= len;
- if ( parse_tag (&p, &n, &ti)
- || ti.class || ti.tag != TAG_OCTET_STRING)
+
+ if (tlv_next (tlv))
goto bailout;
- if ( parse_tag (&p, &n, &ti)
- || ti.class || ti.tag != TAG_SEQUENCE)
+ if (tlv_expect_sequence (tlv))
goto bailout;
- len = ti.length;
- log_assert (!ctx->privatekey);
+ if (ctx->privatekey)
+ {
+ err = gpg_error (GPG_ERR_DUP_VALUE);
+ log_error ("a private key has already been received\n");
+ goto bailout;
+ }
ctx->privatekey = gcry_calloc (10, sizeof *ctx->privatekey);
if (!ctx->privatekey)
{
+ err = gpg_error_from_syserror ();
log_error ("error allocating private key element array\n");
goto bailout;
}
- keyelem_count = 0;
where = "reading.keybag.key-parameters";
- for (keyelem_count = 0; len && keyelem_count < 9;)
+ keyelem_count = 0;
+ while (!(err = tlv_next (tlv)) && !tlv_popped (tlv))
{
- if ( parse_tag (&p, &n, &ti)
- || ti.class || ti.tag != TAG_INTEGER)
- goto bailout;
- if (len < ti.nhdr)
- goto bailout;
- len -= ti.nhdr;
- if (len < ti.length)
- goto bailout;
- len -= ti.length;
- if (!keyelem_count && ti.length == 1 && !*p)
- ; /* ignore the very first one if it is a 0 */
- else
+ if (keyelem_count >= 9)
{
- int rc;
-
- rc = gcry_mpi_scan (ctx->privatekey+keyelem_count,
- GCRYMPI_FMT_USG, p,
- ti.length, NULL);
- if (rc)
- {
- log_error ("error parsing key parameter: %s\n",
- gpg_strerror (rc));
- goto bailout;
- }
- keyelem_count++;
+ err = gpg_error (GPG_ERR_TOO_MANY);
+ goto bailout;
+ }
+
+ err = tlv_expect_mpinteger (tlv, !keyelem_count,
+ ctx->privatekey+keyelem_count);
+ if (!keyelem_count && gpg_err_code (err) == GPG_ERR_FALSE)
+ ; /* Ignore the first value iff it is zero. */
+ else if (err)
+ {
+ log_error ("error parsing RSA key parameter %d: %s\n",
+ keyelem_count, gpg_strerror (err));
+ goto bailout;
}
- p += ti.length;
- n -= ti.length;
+ log_debug ("RSA key parameter %d found\n", keyelem_count);
+ keyelem_count++;
}
- if (len)
+ if (err && gpg_err_code (err) != GPG_ERR_EOF)
goto bailout;
+ err = 0;
}
else
{
if (opt_verbose)
log_info ("processing certBag\n");
- if (parse_tag (&p, &n, &ti))
+
+ if (tlv_next (tlv))
goto bailout;
- if (ti.class || ti.tag != TAG_SEQUENCE)
+ if (tlv_expect_sequence (tlv))
goto bailout;
- if (parse_tag (&p, &n, &ti))
+
+ if (tlv_next (tlv))
goto bailout;
- if (ti.class || ti.tag != TAG_OBJECT_ID
- || ti.length != DIM(oid_x509Certificate_for_pkcs_12)
- || memcmp (p, oid_x509Certificate_for_pkcs_12,
- DIM(oid_x509Certificate_for_pkcs_12)))
+ if (tlv_expect_object_id (tlv, &oid, &oidlen))
goto bailout;
- p += DIM(oid_x509Certificate_for_pkcs_12);
- n -= DIM(oid_x509Certificate_for_pkcs_12);
+ if (oidlen != DIM(oid_x509Certificate_for_pkcs_12)
+ || memcmp (oid, oid_x509Certificate_for_pkcs_12,
+ DIM(oid_x509Certificate_for_pkcs_12)))
+ {
+ err = gpg_error (GPG_ERR_UNSUPPORTED_CERT);
+ goto bailout;
+ }
where = "certbag.before.octetstring";
- if (parse_tag (&p, &n, &ti))
+ if (tlv_next (tlv))
goto bailout;
- if (ti.class != CLASS_CONTEXT || ti.tag)
+ if (tlv_expect_context_tag (tlv, &intval))
goto bailout;
- if (parse_tag (&p, &n, &ti))
+ if (intval)
+ {
+ err = gpg_error (GPG_ERR_BAD_BER);
+ goto bailout;
+ }
+
+ if (tlv_next (tlv))
goto bailout;
- if (ti.class || ti.tag != TAG_OCTET_STRING || ti.ndef)
+ if (tlv_expect_octet_string (tlv, 0, &data, &datalen))
goto bailout;
/* Return the certificate. */
if (ctx->certcb)
- ctx->certcb (ctx->certcbarg, p, ti.length);
-
- p += ti.length;
- n -= ti.length;
+ ctx->certcb (ctx->certcbarg, data, datalen);
}
- /* Ugly hack to cope with the padding: Forget about the rest if
- that is less or equal to the cipher's block length. We can
- reasonable assume that all valid data will be longer than
- just one block. */
- if (n <= (is_pbes2? 16:8))
- n = 0;
-
/* Skip the optional SET with the pkcs12 cert attributes. */
- if (n)
- {
- where = "bag.attributes";
- if (parse_tag (&p, &n, &ti))
- goto bailout;
- if (!ti.class && ti.tag == TAG_SEQUENCE)
- ; /* No attributes. */
- else if (!ti.class && ti.tag == TAG_SET && !ti.ndef)
- { /* The optional SET. */
- p += ti.length;
- n -= ti.length;
- if (n <= (is_pbes2?16:8))
- n = 0;
- if (n && parse_tag (&p, &n, &ti))
- goto bailout;
- }
- else
- goto bailout;
+ where = "bag.attribute_set";
+ err = tlv_next (tlv);
+ if (gpg_err_code (err) == GPG_ERR_EOF)
+ break;
+ if (err)
+ goto bailout;
+ err = tlv_expect_set (tlv);
+ if (!err)
+ { /* This is the optional set of attributes. Skip it. */
+ tlv_skip (tlv);
+ if (opt_verbose)
+ log_info ("skipping bag.attribute_set\n");
}
+ else if (gpg_err_code (err) == GPG_ERR_INV_OBJ)
+ tlv_set_pending (tlv); /* The next tlv_next will be skipped. */
+ else
+ goto bailout;
}
+ if (err && gpg_err_code (err) != GPG_ERR_EOF)
+ {
+ if (!loopcount) /* The first while(tlv_next) failed. */
+ ctx->badpass = 1;
+ goto bailout;
+ }
+ err = 0;
- if (r_consumed)
- *r_consumed = consumed;
- gcry_free (plain);
- gcry_free (cram_buffer);
- return 0;
-
- bailout:
- if (r_consumed)
- *r_consumed = consumed;
+ leave:
+ if (renewed_tlv)
+ tlv_release (tlv);
gcry_free (plain);
- gcry_free (cram_buffer);
- log_error ("encryptedData error at \"%s\", offset %u\n",
- where, (unsigned int)((p - p_start)+startoffset));
if (ctx->badpass)
{
/* Note, that the following string might be used by other programs
@@ -1191,7 +1626,19 @@ parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx,
translated or changed. */
log_error ("possibly bad passphrase given\n");
}
- return -1;
+ return err;
+
+ bailout:
+ if (!err)
+ err = gpg_error (GPG_ERR_GENERAL);
+ log_error ("%s(%s): offset %u.%zu (%s): %s - %s\n",
+ __func__, where,
+ tlv? tlv->stacklen : 0,
+ tlv? tlv->offset : 0,
+ tlv? tlv->lastfunc : "",
+ tlv ? gpg_strerror (tlv->lasterr) : "init failed",
+ gpg_strerror (err));
+ goto leave;
}
@@ -1223,363 +1670,403 @@ bag_data_p (const void *plaintext, size_t length)
static gpg_error_t
-parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx,
- const unsigned char *buffer, size_t length,
- int startoffset,
- size_t *r_consumed)
+parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv)
{
gpg_error_t err = 0;
- struct tag_info ti;
- const unsigned char *p = buffer;
- const unsigned char *p_start = buffer;
- size_t n = length;
const char *where;
+ const unsigned char *oid;
+ size_t oidlen;
+ const unsigned char *data;
+ size_t datalen;
+ int intval;
char salt[20];
size_t saltlen;
char iv[16];
unsigned int iter;
- int len;
+ int renewed_tlv = 0; /* True if the TLV must be released. */
unsigned char *plain = NULL;
- unsigned char *cram_buffer = NULL;
- size_t consumed = 0; /* Number of bytes consumed from the original buffer. */
int is_pbes2 = 0;
- int keyelem_count = 0;
+ int is_aes256 = 0;
where = "shrouded_key_bag";
- if (parse_tag (&p, &n, &ti))
+ if (opt_verbose)
+ log_info ("processing %s\n", where);
+
+ if (tlv_next (tlv))
goto bailout;
- if (ti.class != CLASS_CONTEXT || ti.tag)
+ if (tlv_expect_context_tag (tlv, &intval) || intval != 0 )
goto bailout;
- if (parse_tag (&p, &n, &ti))
+
+ if (tlv_next (tlv))
goto bailout;
- if (ti.class || ti.tag != TAG_SEQUENCE)
+ if (tlv_expect_sequence (tlv))
goto bailout;
- if (parse_tag (&p, &n, &ti))
+
+ where = "shrouded_key_bag.cipherinfo";
+ if (tlv_next (tlv))
goto bailout;
- if (ti.class || ti.tag != TAG_SEQUENCE)
+ if (tlv_expect_sequence (tlv))
goto bailout;
- if (parse_tag (&p, &n, &ti))
+
+ if (tlv_next (tlv))
goto bailout;
- if (ti.class == 0 && ti.tag == TAG_OBJECT_ID
- && ti.length == DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC)
- && !memcmp (p, oid_pbeWithSHAAnd3_KeyTripleDES_CBC,
+ if (tlv_expect_object_id (tlv, &oid, &oidlen))
+ goto bailout;
+
+ if (oidlen == DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC)
+ && !memcmp (oid, oid_pbeWithSHAAnd3_KeyTripleDES_CBC,
DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC)))
+ ; /* Standard cipher. */
+ else if (oidlen == DIM(oid_pkcs5PBES2)
+ && !memcmp (oid, oid_pkcs5PBES2, DIM(oid_pkcs5PBES2)))
+ is_pbes2 = 1;
+ else
{
- p += DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC);
- n -= DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC);
- }
- else if (ti.class == 0 && ti.tag == TAG_OBJECT_ID
- && ti.length == DIM(oid_pkcs5PBES2)
- && !memcmp (p, oid_pkcs5PBES2, DIM(oid_pkcs5PBES2)))
- {
- p += DIM(oid_pkcs5PBES2);
- n -= DIM(oid_pkcs5PBES2);
- is_pbes2 = 1;
+ err = gpg_error (GPG_ERR_UNKNOWN_ALGORITHM);
+ goto bailout;
}
- else
- goto bailout;
if (is_pbes2)
{
where = "shrouded_key_bag.pkcs5PBES2-params";
- if (parse_tag (&p, &n, &ti))
+ if (tlv_next (tlv))
goto bailout;
- if (ti.class || ti.tag != TAG_SEQUENCE)
+ if (tlv_expect_sequence (tlv))
goto bailout;
- if (parse_tag (&p, &n, &ti))
+
+ if (tlv_next (tlv))
goto bailout;
- if (ti.class || ti.tag != TAG_SEQUENCE)
+ if (tlv_expect_sequence (tlv))
+ goto bailout;
+
+ if (tlv_next (tlv))
goto bailout;
- if (parse_tag (&p, &n, &ti))
+ if (tlv_expect_object_id (tlv, &oid, &oidlen))
goto bailout;
- if (!(!ti.class && ti.tag == TAG_OBJECT_ID
- && ti.length == DIM(oid_pkcs5PBKDF2)
- && !memcmp (p, oid_pkcs5PBKDF2, ti.length)))
+ if (!(oidlen == DIM(oid_pkcs5PBKDF2)
+ && !memcmp (oid, oid_pkcs5PBKDF2, oidlen)))
goto bailout; /* Not PBKDF2. */
- p += ti.length;
- n -= ti.length;
- if (parse_tag (&p, &n, &ti))
+
+ if (tlv_next (tlv))
+ goto bailout;
+ if (tlv_expect_sequence (tlv))
goto bailout;
- if (ti.class || ti.tag != TAG_SEQUENCE)
+
+ if (tlv_next (tlv))
goto bailout;
- if (parse_tag (&p, &n, &ti))
+ if (tlv_expect_octet_string (tlv, 0, &data, &datalen))
goto bailout;
- if (!(!ti.class && ti.tag == TAG_OCTET_STRING
- && ti.length >= 8 && ti.length < sizeof salt))
- goto bailout; /* No salt or unsupported length. */
- saltlen = ti.length;
- memcpy (salt, p, saltlen);
- p += saltlen;
- n -= saltlen;
+ if (datalen < 8 || datalen > sizeof salt)
+ {
+ log_info ("bad length of salt (%zu) for AES\n", datalen);
+ err = gpg_error (GPG_ERR_INV_LENGTH);
+ goto bailout;
+ }
+ saltlen = datalen;
+ memcpy (salt, data, saltlen);
- if (parse_tag (&p, &n, &ti))
+ if (tlv_next (tlv))
+ goto bailout;
+ if ((err = tlv_expect_integer (tlv, &intval)))
goto bailout;
- if (!(!ti.class && ti.tag == TAG_INTEGER && ti.length))
- goto bailout; /* No valid iteration count. */
- for (iter=0; ti.length; ti.length--)
+ if (!intval) /* Not a valid iteration count. */
{
- iter <<= 8;
- iter |= (*p++) & 0xff;
- n--;
+ err = gpg_error (GPG_ERR_INV_VALUE);
+ goto bailout;
}
+ iter = intval;
+
/* Note: We don't support the optional parameters but assume
that the algorithmIdentifier follows. */
- if (parse_tag (&p, &n, &ti))
+ if (tlv_next (tlv))
goto bailout;
- if (ti.class || ti.tag != TAG_SEQUENCE)
+ if (tlv_expect_sequence (tlv))
goto bailout;
- if (parse_tag (&p, &n, &ti))
+
+ if (tlv_next (tlv))
+ goto bailout;
+ if (tlv_expect_object_id (tlv, &oid, &oidlen))
+ goto bailout;
+ if (oidlen == DIM(oid_aes128_CBC)
+ && !memcmp (oid, oid_aes128_CBC, oidlen))
+ ;
+ else if (oidlen == DIM(oid_aes256_CBC)
+ && !memcmp (oid, oid_aes256_CBC, oidlen))
+ is_aes256 = 1;
+ else
+ {
+ gpgrt_log_printhex (oid, oidlen, "cipher is:");
+ err = gpg_error (GPG_ERR_CIPHER_ALGO);
+ goto bailout;
+ }
+
+ if (tlv_next (tlv))
goto bailout;
- if (!(!ti.class && ti.tag == TAG_OBJECT_ID
- && ti.length == DIM(oid_aes128_CBC)
- && !memcmp (p, oid_aes128_CBC, ti.length)))
- goto bailout; /* Not AES-128. */
- p += ti.length;
- n -= ti.length;
- if (parse_tag (&p, &n, &ti))
+ if (tlv_expect_octet_string (tlv, 0, &data, &datalen))
goto bailout;
- if (!(!ti.class && ti.tag == TAG_OCTET_STRING && ti.length == sizeof iv))
+ if (datalen != sizeof iv)
goto bailout; /* Bad IV. */
- memcpy (iv, p, sizeof iv);
- p += sizeof iv;
- n -= sizeof iv;
+ memcpy (iv, data, datalen);
}
else
{
where = "shrouded_key_bag.3des-params";
- if (parse_tag (&p, &n, &ti))
+ if (tlv_next (tlv))
goto bailout;
- if (ti.class || ti.tag != TAG_SEQUENCE)
+ if (tlv_expect_sequence (tlv))
goto bailout;
- if (parse_tag (&p, &n, &ti))
+
+ if (tlv_next (tlv))
goto bailout;
- if (ti.class || ti.tag != TAG_OCTET_STRING
- || ti.length < 8 || ti.length > 20)
+ if (tlv_expect_octet_string (tlv, 0, &data, &datalen))
goto bailout;
- saltlen = ti.length;
- memcpy (salt, p, saltlen);
- p += saltlen;
- n -= saltlen;
- if (parse_tag (&p, &n, &ti))
+ if (datalen < 8 || datalen > 20)
+ {
+ log_info ("bad length of salt (%zu) for 3DES\n", datalen);
+ err = gpg_error (GPG_ERR_INV_LENGTH);
+ goto bailout;
+ }
+ saltlen = datalen;
+ memcpy (salt, data, saltlen);
+
+ if (tlv_next (tlv))
goto bailout;
- if (ti.class || ti.tag != TAG_INTEGER || !ti.length )
+ if ((err = tlv_expect_integer (tlv, &intval)))
goto bailout;
- for (iter=0; ti.length; ti.length--)
+ if (!intval)
{
- iter <<= 8;
- iter |= (*p++) & 0xff;
- n--;
+ err = gpg_error (GPG_ERR_INV_VALUE);
+ goto bailout;
}
+ iter = intval;
}
where = "shrouded_key_bag.3desoraes-ciphertext";
- if (parse_tag (&p, &n, &ti))
+ if (tlv_next (tlv))
goto bailout;
- if (ti.class || ti.tag != TAG_OCTET_STRING || !ti.length )
+ if (tlv_expect_octet_string (tlv, 0, &data, &datalen))
goto bailout;
if (opt_verbose)
- log_info ("%lu bytes of %s encrypted text\n",
- ti.length, is_pbes2? "AES128":"3DES");
+ log_info ("%zu bytes of %s encrypted text\n",
+ datalen, is_pbes2? (is_aes256?"AES256":"AES128"):"3DES");
+
+ plain = gcry_malloc_secure (datalen);
- plain = gcry_malloc_secure (ti.length);
if (!plain)
{
+ err = gpg_error_from_syserror ();
log_error ("error allocating decryption buffer\n");
goto bailout;
}
- consumed += p - p_start + ti.length;
- decrypt_block (p, plain, ti.length, salt, saltlen, iter,
+ decrypt_block (data, plain, datalen, salt, saltlen, iter,
iv, is_pbes2? 16:0, ctx->password,
- is_pbes2? GCRY_CIPHER_AES128 : GCRY_CIPHER_3DES,
+ is_pbes2 ? (is_aes256?GCRY_CIPHER_AES256:GCRY_CIPHER_AES128)
+ : GCRY_CIPHER_3DES,
bag_data_p);
- n = ti.length;
- startoffset = 0;
- p_start = p = plain;
+ /* We do not need the TLV anymore and allocated a new one. */
where = "shrouded_key_bag.decrypted-text";
- if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_SEQUENCE)
- goto bailout;
- if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_INTEGER
- || ti.length != 1 || *p)
- goto bailout;
- p++; n--;
- if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_SEQUENCE)
+ tlv = tlv_new (plain, datalen);
+ if (!tlv)
+ {
+ err = gpg_error_from_syserror ();
+ goto bailout;
+ }
+ renewed_tlv = 1;
+
+ if (tlv_next (tlv))
+ {
+ ctx->badpass = 1;
+ goto bailout;
+ }
+ if (tlv_expect_top_sequence (tlv))
+ {
+ ctx->badpass = 1;
+ goto bailout;
+ }
+
+ if (tlv_next (tlv))
+ {
+ ctx->badpass = 1;
+ goto bailout;
+ }
+ if ((err = tlv_expect_integer (tlv, &intval)))
+ {
+ ctx->badpass = 1;
+ goto bailout;
+ }
+ if (intval)
+ {
+ ctx->badpass = 1;
+ err = gpg_error (GPG_ERR_INV_VALUE);
+ goto bailout;
+ }
+
+ if (tlv_next (tlv))
goto bailout;
- len = ti.length;
- if (parse_tag (&p, &n, &ti))
+ if (tlv_expect_sequence (tlv))
goto bailout;
- if (len < ti.nhdr)
+
+ if (tlv_next (tlv))
goto bailout;
- len -= ti.nhdr;
- if (ti.class || ti.tag != TAG_OBJECT_ID)
+ if (tlv_expect_object_id (tlv, &oid, &oidlen))
goto bailout;
- /* gpgrt_log_printhex (p, ti.length, "OID:"); */
- if (ti.length == DIM(oid_rsaEncryption)
- && !memcmp (p, oid_rsaEncryption, DIM(oid_rsaEncryption)))
+ if (oidlen == DIM(oid_rsaEncryption)
+ && !memcmp (oid, oid_rsaEncryption, oidlen))
{
- p += DIM (oid_rsaEncryption);
- n -= DIM (oid_rsaEncryption);
+ if (opt_verbose > 1)
+ log_debug ("RSA parameters\n");
+ if (tlv_next (tlv))
+ goto bailout;
+ if (tlv_expect_null (tlv))
+ tlv_set_pending (tlv); /* NULL tag missing - ignore this. */
}
- else if (ti.length == DIM(oid_pcPublicKey)
- && !memcmp (p, oid_pcPublicKey, DIM(oid_pcPublicKey)))
+ else if (oidlen == DIM(oid_pcPublicKey)
+ && !memcmp (oid, oid_pcPublicKey, oidlen))
{
/* See RFC-5915 for the format. */
- p += DIM (oid_pcPublicKey);
- n -= DIM (oid_pcPublicKey);
- if (len < ti.length)
+ if (tlv_next (tlv))
goto bailout;
- len -= ti.length;
- if (n < len)
- goto bailout;
- if (parse_tag (&p, &n, &ti))
- goto bailout;
- /* gpgrt_log_debug ("ti=%d/%lu len=%lu\n",ti.class,ti.tag,ti.length); */
- if (len < ti.nhdr)
- goto bailout;
- len -= ti.nhdr;
- if (ti.class || ti.tag != TAG_OBJECT_ID)
+ if (tlv_expect_object_id (tlv, &oid, &oidlen))
goto bailout;
ksba_free (ctx->curve);
- ctx->curve = ksba_oid_to_str (p, ti.length);
+ ctx->curve = ksba_oid_to_str (oid, oidlen);
if (!ctx->curve)
- goto bailout;
- /* log_debug ("OID of curve is: %s\n", curve); */
- p += ti.length;
- n -= ti.length;
+ {
+ err = gpg_error (GPG_ERR_INV_OID_STRING);
+ goto bailout;
+ }
+ if (opt_verbose > 1)
+ log_debug ("OID of curve is: %s\n", ctx->curve);
}
- else
- goto bailout;
- if (len < ti.length)
+ else /* Unknown key format */
+ {
+ gpgrt_log_printhex (oid, oidlen, "key format OID:");
+ err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+ goto bailout;
+ }
+
+ /* An octet string to encapsulate the key elements. */
+ if (tlv_next (tlv))
goto bailout;
- len -= ti.length;
- if (n < len)
+ if (tlv_expect_octet_string (tlv, 1, &data, &datalen))
goto bailout;
- p += len;
- n -= len;
- if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_OCTET_STRING)
+
+ if (tlv_next (tlv))
goto bailout;
- if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_SEQUENCE)
+ if (tlv_expect_sequence (tlv))
goto bailout;
- len = ti.length;
if (ctx->privatekey)
{
- log_error ("a key has already been received\n");
+ err = gpg_error (GPG_ERR_DUP_VALUE);
+ log_error ("a private key has already been received\n");
goto bailout;
}
ctx->privatekey = gcry_calloc (10, sizeof *ctx->privatekey);
if (!ctx->privatekey)
{
-
+ err = gpg_error_from_syserror ();
log_error ("error allocating privatekey element array\n");
goto bailout;
}
- keyelem_count = 0;
where = "shrouded_key_bag.reading.key-parameters";
if (ctx->curve) /* ECC case. */
{
- if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_INTEGER)
- goto bailout;
- if (len < ti.nhdr)
+ if (tlv_next (tlv))
goto bailout;
- len -= ti.nhdr;
- if (len < ti.length)
+ if ((err = tlv_expect_integer (tlv, &intval)))
goto bailout;
- len -= ti.length;
- if (ti.length != 1 && *p != 1)
+ if (intval != 1)
{
+ err = gpg_error (GPG_ERR_INV_VALUE);
log_error ("error parsing private ecPublicKey parameter: %s\n",
"bad version");
goto bailout;
}
- p += ti.length;
- n -= ti.length;
- if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_OCTET_STRING)
- goto bailout;
- if (len < ti.nhdr)
+
+ if (tlv_next (tlv))
goto bailout;
- len -= ti.nhdr;
- if (len < ti.length)
+ if (tlv_expect_octet_string (tlv, 0, &data, &datalen))
goto bailout;
- len -= ti.length;
- /* log_printhex (p, ti.length, "ecc q="); */
+ if (opt_verbose > 1)
+ log_printhex (data, datalen, "ecc q=");
err = gcry_mpi_scan (ctx->privatekey, GCRYMPI_FMT_USG,
- p, ti.length, NULL);
+ data, datalen, NULL);
if (err)
{
log_error ("error parsing key parameter: %s\n", gpg_strerror (err));
goto bailout;
}
- p += ti.length;
- n -= ti.length;
-
- len = 0; /* Skip the rest. */
}
else /* RSA case */
{
- for (keyelem_count=0; len && keyelem_count < 9;)
+ int keyelem_count = 0;
+ int firstparam = 1;
+
+ while (!(err = tlv_next (tlv)) && !tlv_popped (tlv))
{
- if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_INTEGER)
- goto bailout;
- if (len < ti.nhdr)
- goto bailout;
- len -= ti.nhdr;
- if (len < ti.length)
- goto bailout;
- len -= ti.length;
- if (!keyelem_count && ti.length == 1 && !*p)
- ; /* ignore the very first one if it is a 0 */
+ if (keyelem_count >= 9)
+ {
+ err = gpg_error (GPG_ERR_TOO_MANY);
+ goto bailout;
+ }
+
+ err = tlv_expect_mpinteger (tlv, firstparam,
+ ctx->privatekey+keyelem_count);
+ if (firstparam && gpg_err_code (err) == GPG_ERR_FALSE)
+ ; /* Ignore the first value iff it is zero. */
+ else if (err)
+ {
+ log_error ("error parsing RSA key parameter %d: %s\n",
+ keyelem_count, gpg_strerror (err));
+ goto bailout;
+ }
else
{
- err = gcry_mpi_scan (ctx->privatekey+keyelem_count,
- GCRYMPI_FMT_USG, p, ti.length, NULL);
- if (err)
- {
- log_error ("error parsing key parameter: %s\n",
- gpg_strerror (err));
- goto bailout;
- }
+ if (opt_verbose > 1)
+ log_debug ("RSA key parameter %d found\n", keyelem_count);
keyelem_count++;
}
- p += ti.length;
- n -= ti.length;
+ firstparam = 0;
}
+ if (err && gpg_err_code (err) != GPG_ERR_EOF)
+ goto bailout;
+ err = 0;
}
- if (len)
- goto bailout;
- goto leave;
+ leave:
+ gcry_free (plain);
+ if (renewed_tlv)
+ tlv_release (tlv);
+ return err;
bailout:
- gcry_free (plain);
- log_error ("data error at \"%s\", offset %zu\n",
- where, (size_t)((p - p_start) + startoffset));
if (!err)
err = gpg_error (GPG_ERR_GENERAL);
-
- leave:
- gcry_free (cram_buffer);
- if (r_consumed)
- *r_consumed = consumed;
- return err;
+ log_error ("%s(%s): offset %u.%zu (%s): %s - %s\n",
+ __func__, where,
+ tlv? tlv->stacklen : 0,
+ tlv? tlv->offset : 0,
+ tlv? tlv->lastfunc : "",
+ tlv ? gpg_strerror (tlv->lasterr) : "init failed",
+ gpg_strerror (err));
+ goto leave;
}
static gpg_error_t
-parse_cert_bag (struct p12_parse_ctx_s *ctx,
- const unsigned char *buffer, size_t length,
- int startoffset,
- size_t *r_consumed)
+parse_cert_bag (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv)
{
gpg_error_t err = 0;
- struct tag_info ti;
- const unsigned char *p = buffer;
- const unsigned char *p_start = buffer;
- size_t n = length;
const char *where;
- size_t consumed = 0; /* Number of bytes consumed from the original buffer. */
+ int intval;
+ const unsigned char *oid;
+ size_t oidlen;
+ const unsigned char *data;
+ size_t datalen;
if (opt_verbose)
log_info ("processing certBag\n");
@@ -1590,181 +2077,182 @@ parse_cert_bag (struct p12_parse_ctx_s *ctx,
* OBJECT IDENTIFIER pkcs-12-certBag
*/
where = "certbag.before.certheader";
- if (parse_tag (&p, &n, &ti))
+ if (tlv_next (tlv))
goto bailout;
- if (ti.class != CLASS_CONTEXT || ti.tag)
+ if (tlv_expect_context_tag (tlv, &intval))
goto bailout;
- if (parse_tag (&p, &n, &ti))
+ if (intval)
+ {
+ err = gpg_error (GPG_ERR_INV_VALUE);
+ goto bailout;
+ }
+
+ if (tlv_next (tlv))
goto bailout;
- if (ti.class || ti.tag != TAG_SEQUENCE)
+ if (tlv_expect_sequence (tlv))
goto bailout;
- if (parse_tag (&p, &n, &ti))
+
+ if (tlv_next (tlv))
+ goto bailout;
+ if (tlv_expect_object_id (tlv, &oid, &oidlen))
goto bailout;
- if (ti.class || ti.tag != TAG_OBJECT_ID
- || ti.length != DIM(oid_x509Certificate_for_pkcs_12)
- || memcmp (p, oid_x509Certificate_for_pkcs_12,
- DIM(oid_x509Certificate_for_pkcs_12)))
+ if (oidlen != DIM(oid_x509Certificate_for_pkcs_12)
+ || memcmp (oid, oid_x509Certificate_for_pkcs_12, oidlen))
goto bailout;
- p += DIM(oid_x509Certificate_for_pkcs_12);
- n -= DIM(oid_x509Certificate_for_pkcs_12);
+
/* Expect:
* [0]
* OCTET STRING encapsulates -- the certificates
*/
where = "certbag.before.octetstring";
- if (parse_tag (&p, &n, &ti))
+ if (tlv_next (tlv))
goto bailout;
- if (ti.class != CLASS_CONTEXT || ti.tag)
+ if (tlv_expect_context_tag (tlv, &intval) || intval != 0 )
goto bailout;
- if (parse_tag (&p, &n, &ti))
+
+ if (tlv_next (tlv))
goto bailout;
- if (ti.class || ti.tag != TAG_OCTET_STRING || ti.ndef)
+ if (tlv_expect_octet_string (tlv, 0, &data, &datalen))
goto bailout;
/* Return the certificate from the octet string. */
if (ctx->certcb)
- ctx->certcb (ctx->certcbarg, p, ti.length);
-
- p += ti.length;
- n -= ti.length;
+ ctx->certcb (ctx->certcbarg, data, datalen);
- if (!n)
- goto leave; /* ready. */
-
- /* Expect:
+ /* Expect optional:
* SET
* SEQUENCE -- we actually ignore this.
*/
where = "certbag.attribute_set";
- if (parse_tag (&p, &n, &ti))
+ if (tlv_next (tlv))
goto bailout;
- if (!ti.class && ti.tag == TAG_SET && !ti.ndef)
- { /* Comsume the optional SET. */
- p += ti.length;
- n -= ti.length;
- if (parse_tag (&p, &n, &ti))
- goto bailout;
+ err = tlv_expect_set (tlv);
+ if (!err)
+ { /* This is the optional set of attributes. Skip it. */
+ tlv_skip (tlv);
+ if (opt_verbose)
+ log_info ("skipping certbag.attribute_set\n");
}
-
- goto leave;
-
- bailout:
- log_error ( "data error at \"%s\", offset %u\n",
- where, (unsigned int)((p - p_start) + startoffset));
- err = gpg_error (GPG_ERR_GENERAL);
+ else if (gpg_err_code (err) == GPG_ERR_INV_OBJ)
+ tlv_set_pending (tlv); /* The next tlv_next will be skipped. */
+ else
+ goto bailout;
leave:
- if (r_consumed)
- *r_consumed = consumed;
return err;
+
+ bailout:
+ log_error ("%s(%s): offset %u.%zu (%s): %s - %s\n",
+ __func__, where,
+ tlv? tlv->stacklen : 0,
+ tlv? tlv->offset : 0,
+ tlv? tlv->lastfunc : "",
+ tlv ? gpg_strerror (tlv->lasterr) : "init failed",
+ gpg_strerror (err));
+ if (!err)
+ err = gpg_error (GPG_ERR_GENERAL);
+ goto leave;
}
static gpg_error_t
-parse_bag_data (struct p12_parse_ctx_s *ctx,
- const unsigned char *buffer, size_t length, int startoffset,
- size_t *r_consumed)
+parse_bag_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv)
{
gpg_error_t err = 0;
- struct tag_info ti;
- const unsigned char *p = buffer;
- const unsigned char *p_start = buffer;
- size_t n = length;
const char *where;
- unsigned char *cram_buffer = NULL;
- size_t consumed = 0; /* Number of bytes consumed from the original buffer. */
+ int intval;
+ const unsigned char *oid;
+ size_t oidlen;
+ unsigned int startlevel;
+
+ if (opt_verbose)
+ log_info ("processing bag data\n");
/* Expect:
* [0]
* OCTET STRING, encapsulates
*/
where = "data";
- if (parse_tag (&p, &n, &ti))
+ if (tlv_next (tlv))
goto bailout;
- if (ti.class != CLASS_CONTEXT || ti.tag)
+ if (tlv_expect_context_tag (tlv, &intval) || intval != 0 )
goto bailout;
- if (parse_tag (&p, &n, &ti))
+
+ if (tlv_next (tlv))
goto bailout;
- if (ti.class || ti.tag != TAG_OCTET_STRING)
+ if (tlv_expect_octet_string (tlv, 1, NULL, NULL))
goto bailout;
- consumed = p - p_start;
- if (ti.is_constructed && ti.ndef)
- {
- /* Mozilla exported certs now come with single byte chunks of
- octet strings. (Mozilla Firefox 1.0.4). Arghh. */
- where = "data.cram_os";
- cram_buffer = cram_octet_string ( p, &n, &consumed);
- if (!cram_buffer)
- goto bailout;
- p = p_start = cram_buffer;
- if (r_consumed)
- *r_consumed = consumed;
- r_consumed = NULL; /* Ugly hack to not update that value on return. */
- }
-
/* Expect:
* SEQUENCE
- * SEQUENCE
- */
- where = "data.2seqs";
- if (parse_tag (&p, &n, &ti))
- goto bailout;
- if (ti.class || ti.tag != TAG_SEQUENCE)
- goto bailout;
- if (parse_tag (&p, &n, &ti))
- goto bailout;
- if (ti.class || ti.tag != TAG_SEQUENCE)
- goto bailout;
-
- /* Expect:
- * OBJECT IDENTIFIER
*/
- where = "data.oid";
- if (parse_tag (&p, &n, &ti))
+ where = "data.outerseqs";
+ if (tlv_next (tlv))
goto bailout;
- if (ti.class || ti.tag != TAG_OBJECT_ID)
+ if (tlv_expect_sequence (tlv))
goto bailout;
- /* Now divert to the actual parser. */
- if (ti.length == DIM(oid_pkcs_12_pkcs_8ShroudedKeyBag)
- && !memcmp (p, oid_pkcs_12_pkcs_8ShroudedKeyBag,
- DIM(oid_pkcs_12_pkcs_8ShroudedKeyBag)))
+ startlevel = tlv_level (tlv);
+ while (!(err = tlv_next (tlv)) && tlv_level (tlv) == startlevel)
{
- p += DIM(oid_pkcs_12_pkcs_8ShroudedKeyBag);
- n -= DIM(oid_pkcs_12_pkcs_8ShroudedKeyBag);
-
- if (parse_shrouded_key_bag (ctx, p, n,
- startoffset + (p - p_start), r_consumed))
+ /* Expect:
+ * SEQUENCE
+ */
+ where = "data.innerseqs";
+ if (tlv_expect_sequence (tlv))
goto bailout;
- }
- else if ( ti.length == DIM(oid_pkcs_12_CertBag)
- && !memcmp (p, oid_pkcs_12_CertBag, DIM(oid_pkcs_12_CertBag)))
- {
- p += DIM(oid_pkcs_12_CertBag);
- n -= DIM(oid_pkcs_12_CertBag);
- if (parse_cert_bag (ctx, p, n,
- startoffset + (p - p_start), r_consumed))
+ /* Expect:
+ * OBJECT IDENTIFIER
+ */
+ where = "data.oid";
+ if (tlv_next (tlv))
goto bailout;
+ if (tlv_expect_object_id (tlv, &oid, &oidlen))
+ goto bailout;
+
+ /* Divert to the actual parser. */
+ if (oidlen == DIM(oid_pkcs_12_pkcs_8ShroudedKeyBag)
+ && !memcmp (oid, oid_pkcs_12_pkcs_8ShroudedKeyBag,
+ DIM(oid_pkcs_12_pkcs_8ShroudedKeyBag)))
+ {
+ if ((err = parse_shrouded_key_bag (ctx, tlv)))
+ goto bailout;
+ }
+ else if (oidlen == DIM(oid_pkcs_12_CertBag)
+ && !memcmp (oid, oid_pkcs_12_CertBag, DIM(oid_pkcs_12_CertBag)))
+ {
+ if ((err = parse_cert_bag (ctx, tlv)))
+ goto bailout;
+ }
+ else
+ {
+ tlv_skip (tlv);
+ log_info ("unknown inner data type - skipped\n");
+ }
}
- else
+ if (err && gpg_err_code (err) != GPG_ERR_EOF)
goto bailout;
-
- goto leave;
-
- bailout:
- log_error ( "data error at \"%s\", offset %u\n",
- where, (unsigned int)((p - p_start) + startoffset));
- err = gpg_error (GPG_ERR_GENERAL);
+ err = 0;
+ if (tlv_popped (tlv))
+ tlv_set_pending (tlv);
leave:
- gcry_free (cram_buffer);
- if (r_consumed) /* Store the number of consumed bytes unless already done. */
- *r_consumed = consumed;
return err;
+
+ bailout:
+ if (!err)
+ err = gpg_error (GPG_ERR_GENERAL);
+ log_error ("%s(%s): offset %u.%zu (%s): %s - %s\n",
+ __func__, where,
+ tlv? tlv->stacklen : 0,
+ tlv? tlv->offset : 0,
+ tlv? tlv->lastfunc : "",
+ tlv ? gpg_strerror (tlv->lasterr) : "init failed",
+ gpg_strerror (err));
+ goto leave;
}
@@ -1772,7 +2260,7 @@ parse_bag_data (struct p12_parse_ctx_s *ctx,
secret key parameters. This is a very limited implementation in
that it is only able to look for 3DES encoded encryptedData and
tries to extract the first private key object it finds. In case of
- an error NULL is returned. CERTCB and CERRTCBARG are used to pass
+ an error NULL is returned. CERTCB and CERTCBARG are used to pass
X.509 certificates back to the caller. If R_CURVE is not NULL and
an ECC key was found the OID of the curve is stored there. */
gcry_mpi_t *
@@ -1780,16 +2268,14 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw,
void (*certcb)(void*, const unsigned char*, size_t),
void *certcbarg, int *r_badpass, char **r_curve)
{
- struct tag_info ti;
- const unsigned char *p = buffer;
- const unsigned char *p_start = buffer;
- size_t n = length;
+ gpg_error_t err;
const char *where;
- int bagseqlength, len;
- int bagseqndef, lenndef;
- unsigned char *cram_buffer = NULL;
- size_t consumed;
+ struct tlv_ctx_s *tlv;
struct p12_parse_ctx_s ctx = { NULL };
+ const unsigned char *oid;
+ size_t oidlen;
+ int intval;
+ unsigned int startlevel;
*r_badpass = 0;
@@ -1797,146 +2283,111 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw,
ctx.certcbarg = certcbarg;
ctx.password = pw;
+ tlv = tlv_new (buffer, length);
+ if (!tlv)
+ {
+ err = gpg_error_from_syserror ();
+ goto bailout;
+ }
where = "pfx";
- if (parse_tag (&p, &n, &ti))
+ if (tlv_next (tlv))
goto bailout;
- if (ti.tag != TAG_SEQUENCE)
+ if (tlv_expect_sequence (tlv))
goto bailout;
where = "pfxVersion";
- if (parse_tag (&p, &n, &ti))
+ if (tlv_next (tlv))
goto bailout;
- if (ti.tag != TAG_INTEGER || ti.length != 1 || *p != 3)
+ if (tlv_expect_integer (tlv, &intval) || intval != 3)
goto bailout;
- p++; n--;
where = "authSave";
- if (parse_tag (&p, &n, &ti))
- goto bailout;
- if (ti.tag != TAG_SEQUENCE)
- goto bailout;
- if (parse_tag (&p, &n, &ti))
+ if (tlv_next (tlv))
goto bailout;
- if (ti.tag != TAG_OBJECT_ID || ti.length != DIM(oid_data)
- || memcmp (p, oid_data, DIM(oid_data)))
+ if (tlv_expect_sequence (tlv))
goto bailout;
- p += DIM(oid_data);
- n -= DIM(oid_data);
- if (parse_tag (&p, &n, &ti))
+ if (tlv_next (tlv))
goto bailout;
- if (ti.class != CLASS_CONTEXT || ti.tag)
+ if (tlv_expect_object_id (tlv, &oid, &oidlen))
goto bailout;
- if (parse_tag (&p, &n, &ti))
+ if (oidlen != DIM(oid_data) || memcmp (oid, oid_data, DIM(oid_data)))
goto bailout;
- if (ti.class != CLASS_UNIVERSAL || ti.tag != TAG_OCTET_STRING)
+
+ if (tlv_next (tlv))
+ goto bailout;
+ if (tlv_expect_context_tag (tlv, &intval) || intval != 0 )
goto bailout;
- if (ti.is_constructed && ti.ndef)
+ if (tlv_next (tlv))
+ goto bailout;
+ if (tlv->ti.is_constructed && tlv->ti.ndef)
{
- /* Mozilla exported certs now come with single byte chunks of
- octet strings. (Mozilla Firefox 1.0.4). Arghh. */
- where = "cram-bags";
- cram_buffer = cram_octet_string ( p, &n, NULL);
- if (!cram_buffer)
- goto bailout;
- p = p_start = cram_buffer;
+ log_debug ("FIXME Put this into our TLV machinery.\n");
+ /* /\* Mozilla exported certs now come with single byte chunks of */
+ /* octet strings. (Mozilla Firefox 1.0.4). Arghh. *\/ */
+ /* where = "cram-bags"; */
+ /* cram_buffer = cram_octet_string ( p, &n, NULL); */
+ /* if (!cram_buffer) */
+ /* goto bailout; */
+ /* p = p_start = cram_buffer; */
}
+ if (tlv_expect_octet_string (tlv, 1, NULL, NULL))
+ goto bailout;
+
where = "bags";
- if (parse_tag (&p, &n, &ti))
+ if (tlv_next (tlv))
goto bailout;
- if (ti.class != CLASS_UNIVERSAL || ti.tag != TAG_SEQUENCE)
+ if (tlv_expect_sequence (tlv))
goto bailout;
- bagseqndef = ti.ndef;
- bagseqlength = ti.length;
- while (bagseqlength || bagseqndef)
+
+ startlevel = tlv_level (tlv);
+ while (!(err = tlv_next (tlv)) && tlv_level (tlv) == startlevel)
{
- /* log_debug ("p12_parse: at offset %ld\n", (p - p_start)); */
where = "bag-sequence";
- if (parse_tag (&p, &n, &ti))
- goto bailout;
- if (bagseqndef && ti.class == CLASS_UNIVERSAL
- && !ti.tag && !ti.is_constructed)
- break; /* Ready */
- if (ti.class != CLASS_UNIVERSAL || ti.tag != TAG_SEQUENCE)
+ if (tlv_expect_sequence (tlv))
goto bailout;
- if (!bagseqndef)
- {
- if (bagseqlength < ti.nhdr)
- goto bailout;
- bagseqlength -= ti.nhdr;
- if (bagseqlength < ti.length)
- goto bailout;
- bagseqlength -= ti.length;
- }
- lenndef = ti.ndef;
- len = ti.length;
-
- if (parse_tag (&p, &n, &ti))
+ if (tlv_next (tlv))
+ goto bailout;
+ if (tlv_expect_object_id (tlv, &oid, &oidlen))
goto bailout;
- if (lenndef)
- len = ti.nhdr;
- else
- len -= ti.nhdr;
- if (ti.tag == TAG_OBJECT_ID && ti.length == DIM(oid_encryptedData)
- && !memcmp (p, oid_encryptedData, DIM(oid_encryptedData)))
+ if (oidlen == DIM(oid_encryptedData)
+ && !memcmp (oid, oid_encryptedData, DIM(oid_encryptedData)))
{
-
- p += DIM(oid_encryptedData);
- n -= DIM(oid_encryptedData);
- if (!lenndef)
- len -= DIM(oid_encryptedData);
where = "bag.encryptedData";
- consumed = 0;
- if (parse_bag_encrypted_data (&ctx, p, n, (p - p_start), &consumed))
- {
- *r_badpass = ctx.badpass;
- goto bailout;
- }
- if (lenndef)
- len += consumed;
+ if ((err=parse_bag_encrypted_data (&ctx, tlv)))
+ goto bailout;
}
- else if (ti.tag == TAG_OBJECT_ID && ti.length == DIM(oid_data)
- && !memcmp (p, oid_data, DIM(oid_data)))
+ else if (oidlen == DIM(oid_data)
+ && !memcmp (oid, oid_data, DIM(oid_data)))
{
- p += DIM(oid_data);
- n -= DIM(oid_data);
- if (!lenndef)
- len -= DIM(oid_data);
-
where = "bag.data";
- consumed = 0;
- if (parse_bag_data (&ctx, p, n, (p - p_start), &consumed))
+ if ((err=parse_bag_data (&ctx, tlv)))
goto bailout;
- if (lenndef)
- len += consumed;
}
- else
+ else if (oidlen == DIM(oid_pkcs_12_pkcs_8ShroudedKeyBag)
+ && !memcmp (oid, oid_pkcs_12_pkcs_8ShroudedKeyBag,
+ DIM(oid_pkcs_12_pkcs_8ShroudedKeyBag)))
{
- log_info ("unknown outer bag type - skipped\n");
- p += ti.length;
- n -= ti.length;
+ where = "bag.shroudedkeybag";
+ if ((err = parse_shrouded_key_bag (&ctx, tlv)))
+ goto bailout;
}
-
- if (len < 0 || len > n)
- goto bailout;
- p += len;
- n -= len;
- if (lenndef)
+ else
{
- /* Need to skip the Null Tag. */
- if (parse_tag (&p, &n, &ti))
- goto bailout;
- if (!(ti.class == CLASS_UNIVERSAL && !ti.tag && !ti.is_constructed))
- goto bailout;
+ tlv_skip (tlv);
+ log_info ("unknown outer bag type - skipped\n");
}
}
+ if (err && gpg_err_code (err) != GPG_ERR_EOF)
+ goto bailout;
+ err = 0;
- gcry_free (cram_buffer);
+ tlv_release (tlv);
if (r_curve)
*r_curve = ctx.curve;
else
@@ -1945,8 +2396,14 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw,
return ctx.privatekey;
bailout:
- log_error ("error at \"%s\", offset %u\n",
- where, (unsigned int)(p - p_start));
+ *r_badpass = ctx.badpass;
+ log_error ("%s(%s): offset %u.%zu (%s): %s - %s\n",
+ __func__, where,
+ tlv? tlv->stacklen : 0,
+ tlv? tlv->offset : 0,
+ tlv? tlv->lastfunc : "",
+ tlv ? gpg_strerror (tlv->lasterr) : "init failed",
+ gpg_strerror (err));
if (ctx.privatekey)
{
int i;
@@ -1956,7 +2413,7 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw,
gcry_free (ctx.privatekey);
ctx.privatekey = NULL;
}
- gcry_free (cram_buffer);
+ tlv_release (tlv);
gcry_free (ctx.curve);
if (r_curve)
*r_curve = NULL;