diff options
author | Werner Koch <[email protected]> | 2023-06-28 15:33:24 +0000 |
---|---|---|
committer | Werner Koch <[email protected]> | 2023-06-28 15:34:19 +0000 |
commit | 101433dfb42b333e48427baf9dd58ac4787c9786 (patch) | |
tree | 34bddebb7e77d36f3b200b3c6ce2a1b603769441 /sm/minip12.c | |
parent | sm: Remove duplicated code. (diff) | |
download | gnupg-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.c | 1943 |
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; |