summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWerner Koch <wk@gnupg.org>2023-06-28 17:33:24 +0200
committerWerner Koch <wk@gnupg.org>2023-06-28 17:34:19 +0200
commit101433dfb42b333e48427baf9dd58ac4787c9786 (patch)
tree34bddebb7e77d36f3b200b3c6ce2a1b603769441
parentsm: Remove duplicated code. (diff)
downloadgnupg2-101433dfb42b333e48427baf9dd58ac4787c9786.tar.xz
gnupg2-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
-rw-r--r--sm/Makefile.am4
-rw-r--r--sm/gpgsm.c2
-rw-r--r--sm/minip12.c1943
-rw-r--r--sm/minip12.h2
-rw-r--r--sm/t-minip12.c700
-rw-r--r--tests/cms/samplekeys/Description-p1220
-rw-r--r--tests/cms/samplekeys/README8
7 files changed, 1880 insertions, 799 deletions
diff --git a/sm/Makefile.am b/sm/Makefile.am
index 03de7026a..ee728e851 100644
--- a/sm/Makefile.am
+++ b/sm/Makefile.am
@@ -77,8 +77,8 @@ gpgsm_LDFLAGS =
gpgsm_DEPENDENCIES = $(resource_objs)
-module_tests =
-module_maint_tests = t-minip12
+module_tests = t-minip12
+module_maint_tests =
t_common_src =
t_common_ldadd = $(libcommon) $(LIBGCRYPT_LIBS) $(KSBA_LIBS) \
diff --git a/sm/gpgsm.c b/sm/gpgsm.c
index ad7652c7d..ce977413d 100644
--- a/sm/gpgsm.c
+++ b/sm/gpgsm.c
@@ -804,7 +804,7 @@ set_debug (void)
/* minip12.c may be used outside of GnuPG, thus we don't have the
* opt structure over there. */
- p12_set_verbosity (opt.verbose);
+ p12_set_verbosity (opt.verbose, opt.debug);
}
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;
diff --git a/sm/minip12.h b/sm/minip12.h
index 84c5f5f79..654cab0e6 100644
--- a/sm/minip12.h
+++ b/sm/minip12.h
@@ -23,7 +23,7 @@
#include <gcrypt.h>
-void p12_set_verbosity (int verbose);
+void p12_set_verbosity (int verbose, int debug);
gcry_mpi_t *p12_parse (const unsigned char *buffer, size_t length,
const char *pw,
diff --git a/sm/t-minip12.c b/sm/t-minip12.c
index 97bbcb9dc..80ba4c69e 100644
--- a/sm/t-minip12.c
+++ b/sm/t-minip12.c
@@ -1,5 +1,5 @@
/* t-minip12.c - Test driver for minip12.c
- * Copyright (C) 2020 g10 Code GmbH
+ * Copyright (C) 2020, 2023 g10 Code GmbH
*
* This file is part of GnuPG.
*
@@ -15,6 +15,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <https://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: GPL-3.0-or-later
*/
#include <config.h>
@@ -22,6 +23,8 @@
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
+#include <stdarg.h>
+#include <ctype.h>
#include "../common/util.h"
#include "minip12.h"
@@ -31,7 +34,336 @@
static int verbose;
static int debug;
+static int any_error;
+static void die (const char *format, ...) GPGRT_ATTR_NR_PRINTF(1,2);
+static void err (const char *format, ...) GPGRT_ATTR_PRINTF(1,2);
+static void inf (const char *format, ...) GPGRT_ATTR_PRINTF(1,2);
+/* static void dbg (const char *format, ...) GPGRT_ATTR_PRINTF(1,2); */
+static void printresult (const char *format, ...) GPGRT_ATTR_PRINTF(1,2);
+static char *my_xstrconcat (const char *s1, ...) GPGRT_ATTR_SENTINEL(0);
+
+#define xstrconcat my_xstrconcat
+#define trim_spaces(a) my_trim_spaces ((a))
+#define my_isascii(c) (!((c) & 0x80))
+
+
+
+
+
+/* Print diagnostic message and exit with failure. */
+static void
+die (const char *format, ...)
+{
+ va_list arg_ptr;
+
+ fflush (stdout);
+ fprintf (stderr, "%s: ", PGM);
+
+ va_start (arg_ptr, format);
+ vfprintf (stderr, format, arg_ptr);
+ va_end (arg_ptr);
+ if (!*format || format[strlen(format)-1] != '\n')
+ putc ('\n', stderr);
+
+ exit (1);
+}
+
+
+/* Print diagnostic message. */
+static void
+err (const char *format, ...)
+{
+ va_list arg_ptr;
+
+ any_error = 1;
+
+ fflush (stdout);
+ fprintf (stderr, "%s: ", PGM);
+
+ va_start (arg_ptr, format);
+ vfprintf (stderr, format, arg_ptr);
+ va_end (arg_ptr);
+ if (!*format || format[strlen(format)-1] != '\n')
+ putc ('\n', stderr);
+}
+
+
+/* Print an info message. */
+static void
+inf (const char *format, ...)
+{
+ va_list arg_ptr;
+
+ if (verbose)
+ {
+ fprintf (stderr, "%s: ", PGM);
+
+ va_start (arg_ptr, format);
+ vfprintf (stderr, format, arg_ptr);
+ va_end (arg_ptr);
+ if (!*format || format[strlen(format)-1] != '\n')
+ putc ('\n', stderr);
+ }
+}
+
+
+/* Print a debug message. */
+/* static void */
+/* dbg (const char *format, ...) */
+/* { */
+/* va_list arg_ptr; */
+
+/* if (debug) */
+/* { */
+/* fprintf (stderr, "%s: DBG: ", PGM); */
+
+/* va_start (arg_ptr, format); */
+/* vfprintf (stderr, format, arg_ptr); */
+/* va_end (arg_ptr); */
+/* if (!*format || format[strlen(format)-1] != '\n') */
+/* putc ('\n', stderr); */
+/* } */
+/* } */
+
+
+/* Print a result line to stdout. */
+static void
+printresult (const char *format, ...)
+{
+ va_list arg_ptr;
+
+ fflush (stdout);
+#ifdef HAVE_FLOCKFILE
+ flockfile (stdout);
+#endif
+ va_start (arg_ptr, format);
+ vfprintf (stdout, format, arg_ptr);
+ if (*format && format[strlen(format)-1] != '\n')
+ putc ('\n', stdout);
+ va_end (arg_ptr);
+ fflush (stdout);
+#ifdef HAVE_FLOCKFILE
+ funlockfile (stdout);
+#endif
+}
+
+
+/* Helper for xstrconcat and strconcat. */
+static char *
+do_strconcat (int xmode, const char *s1, va_list arg_ptr)
+{
+ const char *argv[48];
+ size_t argc;
+ size_t needed;
+ char *buffer, *p;
+
+ argc = 0;
+ argv[argc++] = s1;
+ needed = strlen (s1);
+ while (((argv[argc] = va_arg (arg_ptr, const char *))))
+ {
+ needed += strlen (argv[argc]);
+ if (argc >= DIM (argv)-1)
+ die ("too may args for strconcat\n");
+ argc++;
+ }
+ needed++;
+ buffer = xmode? xmalloc (needed) : malloc (needed);
+ for (p = buffer, argc=0; argv[argc]; argc++)
+ p = stpcpy (p, argv[argc]);
+
+ return buffer;
+}
+
+
+/* Concatenate the string S1 with all the following strings up to a
+ NULL. Returns a malloced buffer with the new string or dies on error. */
+static char *
+my_xstrconcat (const char *s1, ...)
+{
+ va_list arg_ptr;
+ char *result;
+
+ if (!s1)
+ result = xstrdup ("");
+ else
+ {
+ va_start (arg_ptr, s1);
+ result = do_strconcat (1, s1, arg_ptr);
+ va_end (arg_ptr);
+ }
+ return result;
+}
+
+
+static char *
+my_trim_spaces (char *str )
+{
+ char *string, *p, *mark;
+
+ string = str;
+ for (p=string; *p && isspace (*(unsigned char *)p) ; p++)
+ ;
+ for (mark=NULL; (*string = *p); string++, p++ )
+ if (isspace (*(unsigned char *)p))
+ {
+ if (!mark)
+ mark = string;
+ }
+ else
+ mark = NULL;
+ if (mark)
+ *mark = '\0';
+
+ return str ;
+}
+
+
+/* Prepend FNAME with the srcdir environment variable's value and
+ * return an allocated filename. */
+static char *
+prepend_srcdir (const char *fname)
+{
+ static const char *srcdir;
+
+ if (!srcdir && !(srcdir = getenv ("srcdir")))
+ return xstrdup (fname);
+ else
+ return xstrconcat (srcdir, "/", fname, NULL);
+}
+
+
+/* (BUFFER,BUFLEN) and return a malloced hexstring. */
+static char *
+hash_buffer (const void *buffer, size_t buflen)
+{
+ unsigned char hash[20];
+ char *result;
+ int i;
+
+ gcry_md_hash_buffer (GCRY_MD_SHA1, hash, buffer, buflen);
+ result = xmalloc (41);
+ for (i=0; i < 20; i++)
+ snprintf (result + 2*i, 3, "%02x", hash[i]);
+ return result;
+}
+
+
+/* Read next line but skip over empty and comment lines. Caller must
+ xfree the result. */
+static char *
+read_textline (FILE *fp, int *lineno)
+{
+ char line[4096];
+ char *p;
+
+ do
+ {
+ if (!fgets (line, sizeof line, fp))
+ {
+ if (feof (fp))
+ return NULL;
+ die ("error reading input line: %s\n", strerror (errno));
+ }
+ ++*lineno;
+ p = strchr (line, '\n');
+ if (!p)
+ die ("input line %d not terminated or too long\n", *lineno);
+ *p = 0;
+ for (p--;p > line && my_isascii (*p) && isspace (*p); p--)
+ *p = 0;
+ }
+ while (!*line || *line == '#');
+ return xstrdup (line);
+}
+
+
+/* Copy the data after the tag to BUFFER. BUFFER will be allocated as
+ needed. */
+static void
+copy_data (char **buffer, const char *line, int lineno)
+{
+ const char *s;
+
+ xfree (*buffer);
+ *buffer = NULL;
+
+ s = strchr (line, ':');
+ if (!s)
+ {
+ err ("syntax error at input line %d", lineno);
+ return;
+ }
+ for (s++; my_isascii (*s) && isspace (*s); s++)
+ ;
+ *buffer = xstrdup (s);
+}
+
+
+static void
+hexdowncase (char *string)
+{
+ char *p;
+
+ if (string)
+ for (p=string; *p; p++)
+ if (my_isascii (*p))
+ *p = tolower (*p);
+}
+
+
+/* Return the value of the variable VARNAME from ~/.gnupg-autogen.rc
+ * or NULL if it does not exists or is empty. */
+static char *
+value_from_gnupg_autogen_rc (const char *varname)
+{
+ const char *home;
+ char *fname;
+ FILE *fp;
+ char *line = NULL;
+ char *p;
+ int lineno = 0;
+
+ if (!(home = getenv ("HOME")))
+ home = "";
+ fname = xstrconcat (home, "/.gnupg-autogen.rc", NULL);
+ fp = fopen (fname, "r");
+ if (!fp)
+ goto leave;
+
+ while ((line = read_textline (fp, &lineno)))
+ {
+ p = strchr (line, '=');
+ if (p)
+ {
+ *p++ = 0;
+ trim_spaces (line);
+ if (!strcmp (line, varname))
+ {
+ trim_spaces (p);
+ if (*p)
+ {
+ memmove (line, p, strlen (p)+1);
+ if (*line == '~' && line[1] == '/')
+ {
+ p = xstrconcat (home, line+1, NULL);
+ xfree (line);
+ line = p;
+ }
+ break; /* found. */
+ }
+ }
+ }
+ xfree (line);
+ }
+
+ leave:
+ if (fp)
+ fclose (fp);
+ xfree (fname);
+ return line;
+}
static void
@@ -45,13 +377,90 @@ cert_cb (void *opaque, const unsigned char *cert, size_t certlen)
}
+/* Parse one PKCS#12 file. Returns zero on success. */
+static int
+one_file (const char *name, const char *pass)
+{
+ FILE *fp;
+ struct stat st;
+ unsigned char *buf;
+ size_t buflen;
+ gcry_mpi_t *result;
+ int badpass;
+ char *curve = NULL;
-int
-main (int argc, char **argv)
+ fp = fopen (name, "rb");
+ if (!fp)
+ {
+ fprintf (stderr, PGM": can't open '%s': %s\n", name, strerror (errno));
+ return 1;
+ }
+
+ if (fstat (fileno(fp), &st))
+ {
+ fprintf (stderr, PGM": can't stat '%s': %s\n", name, strerror (errno));
+ return 1;
+ }
+
+ buflen = st.st_size;
+ buf = xmalloc (buflen+1);
+ if (fread (buf, buflen, 1, fp) != 1)
+ {
+ fprintf (stderr, "error reading '%s': %s\n", name, strerror (errno));
+ return 1;
+ }
+ fclose (fp);
+
+ result = p12_parse (buf, buflen, pass, cert_cb, NULL, &badpass, &curve);
+ if (result)
+ {
+ int i, rc;
+ unsigned char *tmpbuf;
+
+ if (curve)
+ log_info ("curve: %s\n", curve);
+ for (i=0; result[i]; i++)
+ {
+ rc = gcry_mpi_aprint (GCRYMPI_FMT_HEX, &tmpbuf, NULL, result[i]);
+ if (rc)
+ log_error ("%d: [error printing number: %s]\n",
+ i, gpg_strerror (rc));
+ else
+ {
+ log_info ("%d: %s\n", i, tmpbuf);
+ gcry_free (tmpbuf);
+ }
+ }
+ }
+ if (badpass)
+ log_error ("Bad password given?\n");
+
+ xfree (buf);
+ return 0;
+}
+
+
+static void
+cert_collect_cb (void *opaque, const unsigned char *cert, size_t certlen)
+{
+ char **certstr = opaque;
+ char *hash;
+
+ hash = hash_buffer (cert, certlen);
+ if (*certstr)
+ {
+ *certstr = xstrconcat (*certstr, ",", hash, NULL);
+ xfree (hash);
+ }
+ else
+ *certstr = hash;
+}
+
+
+static int
+run_one_test (const char *name, const char *desc, const char *pass,
+ const char *certexpected, const char *keyexpected)
{
- int last_argc = -1;
- char const *name = NULL;
- char const *pass = NULL;
FILE *fp;
struct stat st;
unsigned char *buf;
@@ -59,6 +468,219 @@ main (int argc, char **argv)
gcry_mpi_t *result;
int badpass;
char *curve = NULL;
+ char *resulthash = NULL;
+ char *p;
+ char *certstr = NULL;
+ int ret;
+
+ inf ("testing '%s' (%s)", name , desc? desc:"");
+ fp = fopen (name, "rb");
+ if (!fp)
+ {
+ err ("can't open '%s': %s\n", name, strerror (errno));
+ printresult ("FAIL: %s - test file not found\n", name);
+ return 1;
+ }
+
+ if (fstat (fileno (fp), &st))
+ {
+ err ("can't stat '%s': %s\n", name, strerror (errno));
+ printresult ("FAIL: %s - error stating test file\n", name);
+ fclose (fp);
+ return 1;
+ }
+
+ buflen = st.st_size;
+ buf = xmalloc (buflen+1);
+ if (fread (buf, buflen, 1, fp) != 1)
+ {
+ err ("error reading '%s': %s\n", name, strerror (errno));
+ printresult ("FAIL: %s - error reading test file\n", name);
+ fclose (fp);
+ xfree (buf);
+ return 1;
+ }
+ fclose (fp);
+
+ result = p12_parse (buf, buflen, pass? pass:"", cert_collect_cb, &certstr,
+ &badpass, &curve);
+ if (result)
+ {
+ int i, rc;
+ char *tmpstring;
+ unsigned char *tmpbuf;
+ char numbuf[20];
+
+ if (curve)
+ {
+ if (verbose > 1)
+ inf ("curve: %s\n", curve);
+ tmpstring = xstrconcat ("curve:", curve, "\n", NULL);
+ }
+ else
+ tmpstring = xstrdup ("\n");
+ for (i=0; result[i]; i++)
+ {
+ rc = gcry_mpi_aprint (GCRYMPI_FMT_HEX, &tmpbuf, NULL, result[i]);
+ if (rc)
+ die ("result %d: [error printing number: %s]\n",
+ i, gpg_strerror (rc));
+ else
+ {
+ if (verbose > 1)
+ inf ("result %d: %s\n", i, tmpbuf);
+ snprintf (numbuf, sizeof numbuf, "%d:", i);
+ p = xstrconcat (tmpstring, numbuf, tmpbuf, "\n", NULL);
+ xfree (tmpstring);
+ tmpstring = p;
+ gcry_free (tmpbuf);
+ }
+ }
+
+ resulthash = hash_buffer (tmpstring, strlen (tmpstring));
+ xfree (tmpstring);
+ }
+
+ if (verbose > 1)
+ {
+ inf ("cert(exp)=%s", certexpected);
+ inf ("cert(got)=%s", certstr? certstr:"[null]");
+ inf ("key(exp)=%s", keyexpected);
+ inf ("key(got)=%s", resulthash? resulthash:"[null]");
+ }
+
+ ret = 1;
+ if (!result)
+ printresult ("FAIL: %s - error from parser\n", name);
+ else if (certexpected && !certstr)
+ printresult ("FAIL: %s - expected certs but got none\n", name);
+ else if (!certexpected && certstr)
+ printresult ("FAIL: %s - no certs expected but got one\n", name);
+ else if (certexpected && certstr && strcmp (certexpected, certstr))
+ printresult ("FAIL: %s - certs not as expected\n", name);
+ else if (keyexpected && !resulthash)
+ printresult ("FAIL: %s - expected key but got none\n", name);
+ else if (!keyexpected && resulthash)
+ printresult ("FAIL: %s - key not expected but got one\n", name);
+ else if (keyexpected && resulthash && strcmp (keyexpected, resulthash))
+ printresult ("FAIL: %s - keys not as expected\n", name);
+ else
+ {
+ printresult ("PASS: %s\n", name);
+ ret = 0;
+ }
+
+ if (result)
+ {
+ int i;
+ for (i=0; result[i]; i++)
+ gcry_mpi_release (result[i]);
+ gcry_free (result);
+ }
+ xfree (certstr);
+ xfree (resulthash);
+ xfree (curve);
+ xfree (buf);
+ return ret;
+}
+
+
+/* Run a regression test using the Info take from DESCFNAME. */
+static int
+run_tests_from_file (const char *descfname)
+{
+ FILE *fp;
+ char *descdir;
+ int lineno, ntests;
+ char *line;
+ char *name = NULL;
+ char *desc = NULL;
+ char *pass = NULL;
+ char *cert = NULL;
+ char *key = NULL;
+ int ret = 0;
+ char *p;
+
+ inf ("Running tests from '%s'", descfname);
+ descdir = xstrdup (descfname);
+ p = strrchr (descdir, '/');
+ if (p)
+ *p = 0;
+ else
+ {
+ xfree (descdir);
+ descdir = xstrdup (".");
+ }
+
+ fp = fopen (descfname, "r");
+ if (!fp)
+ die ("error opening '%s': %s\n", descfname, strerror (errno));
+
+ lineno = ntests = 0;
+ while ((line = read_textline (fp, &lineno)))
+ {
+ if (!strncmp (line, "Name:", 5))
+ {
+ if (name)
+ ret |= run_one_test (name, desc, pass, cert, key);
+ xfree (cert); cert = NULL;
+ xfree (desc); desc = NULL;
+ xfree (pass); pass = NULL;
+ xfree (key); key = NULL;
+ copy_data (&name, line, lineno);
+ if (name)
+ {
+ p = xstrconcat (descdir, "/", name, NULL);
+ xfree (name);
+ name = p;
+ }
+ }
+ else if (!strncmp (line, "Desc:", 5))
+ copy_data (&desc, line, lineno);
+ else if (!strncmp (line, "Pass:", 5))
+ copy_data (&pass, line, lineno);
+ else if (!strncmp (line, "Cert:", 5))
+ {
+ p = NULL;
+ copy_data (&p, line, lineno);
+ hexdowncase (p);
+ if (p && cert)
+ cert = xstrconcat (cert, ",", p, NULL);
+ else
+ cert = p;
+ }
+ else if (!strncmp (line, "Key:", 4))
+ {
+ copy_data (&key, line, lineno);
+ hexdowncase (key);
+ }
+ else
+ inf ("%s:%d: unknown tag ignored", descfname, lineno);
+
+ xfree (line);
+ }
+ if (name)
+ ret |= run_one_test (name, desc, pass, cert, key);
+ xfree (name);
+ xfree (desc);
+ xfree (pass);
+ xfree (cert);
+ xfree (key);
+
+ fclose (fp);
+ xfree (descdir);
+ return ret;
+}
+
+
+
+int
+main (int argc, char **argv)
+{
+ int last_argc = -1;
+ char const *name = NULL;
+ char const *pass = NULL;
+ int ret;
if (argc)
{ argc--; argv++; }
@@ -73,8 +695,10 @@ main (int argc, char **argv)
else if (!strcmp (*argv, "--help"))
{
fputs ("usage: " PGM " <pkcs12file> [<passphrase>]\n"
+ "Without <pkcs12file> a regression test is run\n"
"Options:\n"
" --verbose print timings etc.\n"
+ " given twice shows more\n"
" --debug flyswatter\n"
, stdout);
exit (0);
@@ -97,7 +721,12 @@ main (int argc, char **argv)
}
}
- if (argc == 1)
+ if (!argc)
+ {
+ name = NULL;
+ pass = NULL;
+ }
+ else if (argc == 1)
{
name = argv[0];
pass = "";
@@ -109,57 +738,38 @@ main (int argc, char **argv)
}
else
{
- fprintf (stderr, "usage: " PGM " <file> [<passphrase>]\n");
+ fprintf (stderr, "usage: " PGM " [<file> [<passphrase>]]\n");
exit (1);
}
gcry_control (GCRYCTL_DISABLE_SECMEM, NULL);
gcry_control (GCRYCTL_INITIALIZATION_FINISHED, NULL);
-
- fp = fopen (name, "rb");
- if (!fp)
+ if (name)
{
- fprintf (stderr, PGM": can't open '%s': %s\n", name, strerror (errno));
- return 1;
+ p12_set_verbosity (verbose, debug);
+ ret = one_file (name, pass);
}
-
- if (fstat (fileno(fp), &st))
+ else
{
- fprintf (stderr, PGM": can't stat '%s': %s\n", name, strerror (errno));
- return 1;
- }
+ char *descfname, *p;
- buflen = st.st_size;
- buf = gcry_malloc (buflen+1);
- if (!buf || fread (buf, buflen, 1, fp) != 1)
- {
- fprintf (stderr, "error reading '%s': %s\n", name, strerror (errno));
- return 1;
- }
- fclose (fp);
+ if (verbose > 1)
+ p12_set_verbosity (verbose > 1? (verbose - 1):0, debug);
+ descfname = prepend_srcdir ("../tests/cms/samplekeys/Description-p12");
+ ret = run_tests_from_file (descfname);
+ xfree (descfname);
- result = p12_parse (buf, buflen, pass, cert_cb, NULL, &badpass, &curve);
- if (result)
- {
- int i, rc;
- unsigned char *tmpbuf;
-
- if (curve)
- log_info ("curve: %s\n", curve);
- for (i=0; result[i]; i++)
+ /* Check whether we have non-public regression test cases. */
+ p = value_from_gnupg_autogen_rc ("GNUPG_EXTRA_TESTS_DIR");
+ if (p)
{
- rc = gcry_mpi_aprint (GCRYMPI_FMT_HEX, &tmpbuf, NULL, result[i]);
- if (rc)
- log_error ("%d: [error printing number: %s]\n",
- i, gpg_strerror (rc));
- else
- {
- log_info ("%d: %s\n", i, tmpbuf);
- gcry_free (tmpbuf);
- }
+ descfname = xstrconcat (p, "/pkcs12/Description", NULL);
+ xfree (p);
+ ret |= run_tests_from_file (descfname);
+ xfree (descfname);
}
}
- return 0;
+ return ret;
}
diff --git a/tests/cms/samplekeys/Description-p12 b/tests/cms/samplekeys/Description-p12
new file mode 100644
index 000000000..bd1e35c91
--- /dev/null
+++ b/tests/cms/samplekeys/Description-p12
@@ -0,0 +1,20 @@
+# Description-p12 - Machine readable description of our P12 test vectors
+
+Name: ov-user.p12
+Desc: Private test key from www.openvalidation.org
+Pass: start
+Cert: 4753a910e0c8b4caa8663ca0e4273a884eb5397d
+Key: 93be89edd11214ab74280d988a665b6beef876c5
+
+Name: ov-server.p12
+Desc: Private test key from www.openvalidation.org
+Pass: start
+Cert: 1997fadf6cc1af03e4845c4cba38fb2397315143
+Key: 63b1d7233e75c3a462cb4b8ea3ad285e8ecba91c
+
+Name: opensc-test.p12
+Desc: PKCS#12 key and certificates taken from OpenSC (RC2+3DES,PKCS#8)
+Pass: password
+Cert: 115abfc3ae554092a57ade74177fedf9459af5d2
+Cert: a0d6d318952c313ff8c33cd3f629647ff1de76b3
+Key: 5a36c61706367ecdb52e8779e3a32bbac1069fa1
diff --git a/tests/cms/samplekeys/README b/tests/cms/samplekeys/README
index 65255cb61..14bbf2bdc 100644
--- a/tests/cms/samplekeys/README
+++ b/tests/cms/samplekeys/README
@@ -1,10 +1,5 @@
This is a collection of keys we use with the regression tests.
-
-opensc-tests.p12 PKCS#12 key and certificates taken from OpenSC.
- Passphrase is "password"
-
-ov-user.p12 Private tests keys from www.openvalidation.org.
-ov-server.p12 Passphrase for both is "start"
+For the *.p12 files see Description-p12
ossl-rentec-user.pem An OpenSSL generated user certificate using a
bunch of attributes and DC RDNs.
@@ -21,4 +16,3 @@ steed-self-signing-nonthority.pem
The STEED Self-Signing Nonthority.
68A638998DFABAC510EA645CE34F9686B2EDF7EA.key
The private Key of The STEED Self-Signing Nonthority.
-