diff --git a/src/cms.c b/src/cms.c index dcc3f58..881443f 100644 --- a/src/cms.c +++ b/src/cms.c @@ -1,3645 +1,3756 @@ /* cms.c - cryptographic message syntax main functions * Copyright (C) 2001, 2003, 2004, 2008, 2012, 2020 g10 Code GmbH * * This file is part of KSBA. * * KSBA is free software; you can redistribute it and/or modify * it under the terms of either * * - the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at * your option) any later version. * * or * * - the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at * your option) any later version. * * or both in parallel, as here. * * KSBA is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public * License for more details. * * You should have received a copies of the GNU General Public License * and the GNU Lesser General Public License along with this program; * if not, see . */ /* References: * RFC-5652 := Cryptographic Message Syntax (CMS) (aka STD0070) * SPHINX := CMS profile developed by the German BSI. * (see also https://lwn.net/2001/1011/a/german-smime.php3) * PKCS#7 := Original specification of CMS */ #include #include #include #include #include #include #include "util.h" #include "cms.h" #include "convert.h" #include "keyinfo.h" #include "der-encoder.h" #include "ber-help.h" #include "sexp-parse.h" #include "cert.h" #include "der-builder.h" static gpg_error_t ct_parse_data (ksba_cms_t cms); static gpg_error_t ct_parse_signed_data (ksba_cms_t cms); static gpg_error_t ct_parse_enveloped_data (ksba_cms_t cms); static gpg_error_t ct_parse_digested_data (ksba_cms_t cms); static gpg_error_t ct_parse_encrypted_data (ksba_cms_t cms); static gpg_error_t ct_build_data (ksba_cms_t cms); static gpg_error_t ct_build_signed_data (ksba_cms_t cms); static gpg_error_t ct_build_enveloped_data (ksba_cms_t cms); static gpg_error_t ct_build_digested_data (ksba_cms_t cms); static gpg_error_t ct_build_encrypted_data (ksba_cms_t cms); static struct { const char *oid; ksba_content_type_t ct; gpg_error_t (*parse_handler)(ksba_cms_t); gpg_error_t (*build_handler)(ksba_cms_t); } content_handlers[] = { { "1.2.840.113549.1.7.1", KSBA_CT_DATA, ct_parse_data , ct_build_data }, { "1.2.840.113549.1.7.2", KSBA_CT_SIGNED_DATA, ct_parse_signed_data , ct_build_signed_data }, { "1.2.840.113549.1.7.3", KSBA_CT_ENVELOPED_DATA, ct_parse_enveloped_data, ct_build_enveloped_data }, { "1.2.840.113549.1.7.5", KSBA_CT_DIGESTED_DATA, ct_parse_digested_data , ct_build_digested_data }, { "1.2.840.113549.1.7.6", KSBA_CT_ENCRYPTED_DATA, ct_parse_encrypted_data, ct_build_encrypted_data }, { "1.2.840.113549.1.9.16.1.2", KSBA_CT_AUTH_DATA }, { "1.3.6.1.4.1.311.2.1.4", KSBA_CT_SPC_IND_DATA_CTX, ct_parse_data , ct_build_data }, { NULL } }; static const char oidstr_contentType[] = "1.2.840.113549.1.9.3"; /*static char oid_contentType[9] = "\x2A\x86\x48\x86\xF7\x0D\x01\x09\x03";*/ static const char oidstr_messageDigest[] = "1.2.840.113549.1.9.4"; static const char oid_messageDigest[9] ="\x2A\x86\x48\x86\xF7\x0D\x01\x09\x04"; static const char oidstr_signingTime[] = "1.2.840.113549.1.9.5"; static const char oid_signingTime[9] = "\x2A\x86\x48\x86\xF7\x0D\x01\x09\x05"; static const char oidstr_smimeCapabilities[] = "1.2.840.113549.1.9.15"; #if 0 /* Set to 1 to use this debug helper. */ static void log_sexp (const char *text, ksba_const_sexp_t p) { int level = 0; gpgrt_log_debug ("%s: ", text); if (!p) gpgrt_log_printf ("[none]"); else { for (;;) { if (*p == '(') { gpgrt_log_printf ("%c", *p); p++; level++; } else if (*p == ')') { gpgrt_log_printf ("%c", *p); p++; if (--level <= 0 ) return; } else if (!digitp (p)) { gpgrt_log_printf ("[invalid s-exp]"); return; } else { char *endp; const unsigned char *s; unsigned long len, n; len = strtoul (p, &endp, 10); p = endp; if (*p != ':') { gpgrt_log_printf ("[invalid s-exp]"); return; } p++; for (s=p,n=0; n < len; n++, s++) if ( !((*s >= 'a' && *s <= 'z') || (*s >= 'A' && *s <= 'Z') || (*s >= '0' && *s <= '9') || *s == '-' || *s == '.')) break; if (n < len) { gpgrt_log_printf ("#"); for (n=0; n < len; n++, p++) gpgrt_log_printf ("%02X", *p); gpgrt_log_printf ("#"); } else { for (n=0; n < len; n++, p++) gpgrt_log_printf ("%c", *p); } } } } gpgrt_log_printf ("\n"); } #endif /* debug helper */ /* Helper for read_and_hash_cont(). */ static gpg_error_t read_hash_block (ksba_cms_t cms, unsigned long nleft) { gpg_error_t err; char buffer[4096]; size_t n, nread; while (nleft) { n = nleft < sizeof (buffer)? nleft : sizeof (buffer); err = ksba_reader_read (cms->reader, buffer, n, &nread); if (err) return err; nleft -= nread; if (cms->hash_fnc) cms->hash_fnc (cms->hash_fnc_arg, buffer, nread); if (cms->writer) err = ksba_writer_write (cms->writer, buffer, nread); if (err) return err; } return 0; } /* Copy all the bytes from the reader to the writer and hash them if a a hash function has been set. The writer may be NULL to just do the hashing */ static gpg_error_t read_and_hash_cont (ksba_cms_t cms) { gpg_error_t err = 0; unsigned long nleft; struct tag_info ti; if (cms->inner_cont_ndef) { for (;;) { err = _ksba_ber_read_tl (cms->reader, &ti); if (err) return err; if (ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_OCTET_STRING && !ti.is_constructed) { /* next chunk */ nleft = ti.length; err = read_hash_block (cms, nleft); if (err) return err; } else if (ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_OCTET_STRING && ti.is_constructed) { /* next chunk is constructed */ for (;;) { err = _ksba_ber_read_tl (cms->reader, &ti); if (err) return err; if (ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_OCTET_STRING && !ti.is_constructed) { nleft = ti.length; err = read_hash_block (cms, nleft); if (err) return err; } else if (ti.class == CLASS_UNIVERSAL && !ti.tag && !ti.is_constructed) break; /* ready with this chunk */ else return gpg_error (GPG_ERR_ENCODING_PROBLEM); } } else if (ti.class == CLASS_UNIVERSAL && !ti.tag && !ti.is_constructed) return 0; /* ready */ else return gpg_error (GPG_ERR_ENCODING_PROBLEM); } } else { /* This is basically the same as above but we allow for arbitrary types. Not sure whether it is really needed but right in the beginning of gnupg 1.9 we had at least one message with didn't used octet strings. Not ethat we don't do proper NLEFT checking but well why should we validate these things? Well, it might be nice to have such a feature but then we should write a more general mechanism to do that. */ nleft = cms->inner_cont_len; /* First read the octet string but allow all types here */ err = _ksba_ber_read_tl (cms->reader, &ti); if (err) return err; if (nleft < ti.nhdr) return gpg_error (GPG_ERR_ENCODING_PROBLEM); nleft -= ti.nhdr; if (ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_OCTET_STRING && ti.is_constructed) { /* Next chunk is constructed */ for (;;) { err = _ksba_ber_read_tl (cms->reader, &ti); if (err) return err; if (ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_OCTET_STRING && !ti.is_constructed) { nleft = ti.length; err = read_hash_block (cms, nleft); if (err) return err; } else if (ti.class == CLASS_UNIVERSAL && !ti.tag && !ti.is_constructed) break; /* Ready with this chunk */ else return gpg_error (GPG_ERR_ENCODING_PROBLEM); } } else if (ti.class == CLASS_UNIVERSAL && !ti.tag && !ti.is_constructed) return 0; /* ready */ else { err = read_hash_block (cms, nleft); if (err) return err; } } return 0; } /* Copy all the encrypted bytes from the reader to the writer. Handles indefinite length encoding */ static gpg_error_t read_encrypted_cont (ksba_cms_t cms) { gpg_error_t err = 0; unsigned long nleft; char buffer[4096]; size_t n, nread; if (cms->inner_cont_ndef) { struct tag_info ti; /* fixme: this ist mostly a duplicate of the code in read_and_hash_cont(). */ for (;;) { err = _ksba_ber_read_tl (cms->reader, &ti); if (err) return err; if (ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_OCTET_STRING && !ti.is_constructed) { /* next chunk */ nleft = ti.length; while (nleft) { n = nleft < sizeof (buffer)? nleft : sizeof (buffer); err = ksba_reader_read (cms->reader, buffer, n, &nread); if (err) return err; nleft -= nread; err = ksba_writer_write (cms->writer, buffer, nread); if (err) return err; } } else if (ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_OCTET_STRING && ti.is_constructed) { /* next chunk is constructed */ for (;;) { err = _ksba_ber_read_tl (cms->reader, &ti); if (err) return err; if (ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_OCTET_STRING && !ti.is_constructed) { nleft = ti.length; while (nleft) { n = nleft < sizeof (buffer)? nleft : sizeof (buffer); err = ksba_reader_read (cms->reader, buffer, n, &nread); if (err) return err; nleft -= nread; if (cms->writer) err = ksba_writer_write (cms->writer, buffer, nread); if (err) return err; } } else if (ti.class == CLASS_UNIVERSAL && !ti.tag && !ti.is_constructed) break; /* ready with this chunk */ else return gpg_error (GPG_ERR_ENCODING_PROBLEM); } } else if (ti.class == CLASS_UNIVERSAL && !ti.tag && !ti.is_constructed) return 0; /* ready */ else return gpg_error (GPG_ERR_ENCODING_PROBLEM); } } else { nleft = cms->inner_cont_len; while (nleft) { n = nleft < sizeof (buffer)? nleft : sizeof (buffer); err = ksba_reader_read (cms->reader, buffer, n, &nread); if (err) return err; nleft -= nread; err = ksba_writer_write (cms->writer, buffer, nread); if (err) return err; } } return 0; } /* copy data from reader to writer. Assume that it is an octet string and insert undefinite length headers where needed */ static gpg_error_t write_encrypted_cont (ksba_cms_t cms) { gpg_error_t err = 0; char buffer[4096]; size_t nread; /* we do it the simple way: the parts are made up from the chunks we got from the read function. Fixme: We should write the tag here, and write a definite length header if everything fits into our local buffer. Actually pretty simple to do, but I am too lazy right now. */ while (!(err = ksba_reader_read (cms->reader, buffer, sizeof buffer, &nread)) ) { err = _ksba_ber_write_tl (cms->writer, TYPE_OCTET_STRING, CLASS_UNIVERSAL, 0, nread); if (!err) err = ksba_writer_write (cms->writer, buffer, nread); } if (gpg_err_code (err) == GPG_ERR_EOF) /* write the end tag */ err = _ksba_ber_write_tl (cms->writer, 0, 0, 0, 0); return err; } /* Figure out whether the data read from READER is a CMS object and return its content type. This function does only peek at the READER and tries to identify the type with best effort. Because of the ubiquity of the stupid and insecure pkcs#12 format, the function will also identify those files and return KSBA_CT_PKCS12; there is and will be no other pkcs#12 support in this library. */ ksba_content_type_t ksba_cms_identify (ksba_reader_t reader) { struct tag_info ti; unsigned char buffer[24]; const unsigned char*p; size_t n, count; char *oid; int i; int maybe_p12 = 0; if (!reader) return KSBA_CT_NONE; /* oops */ /* This is a common example of a CMS object - it is obvious that we only need to read a few bytes to get to the OID: 30 82 0B 59 06 09 2A 86 48 86 F7 0D 01 07 02 A0 82 0B 4A 30 82 0B 46 02 ----------- ++++++++++++++++++++++++++++++++ SEQUENCE OID (signedData) (2 byte len) For a pkcs12 message we have this: 30 82 08 59 02 01 03 30 82 08 1F 06 09 2A 86 48 86 F7 0D 01 07 01 A0 82 ----------- ++++++++ ----------- ++++++++++++++++++++++++++++++++ SEQUENCE INTEGER SEQUENCE OID (data) This we need to read at least 22 bytes, we add 2 bytes to cope with length headers store with 4 bytes. */ for (count = sizeof buffer; count; count -= n) { if (ksba_reader_read (reader, buffer+sizeof (buffer)-count, count, &n)) return KSBA_CT_NONE; /* too short */ } n = sizeof buffer; if (ksba_reader_unread (reader, buffer, n)) return KSBA_CT_NONE; /* oops */ p = buffer; if (_ksba_ber_parse_tl (&p, &n, &ti)) return KSBA_CT_NONE; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed) ) return KSBA_CT_NONE; if (_ksba_ber_parse_tl (&p, &n, &ti)) return KSBA_CT_NONE; if ( ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_INTEGER && !ti.is_constructed && ti.length == 1 && n && *p == 3) { maybe_p12 = 1; p++; n--; if (_ksba_ber_parse_tl (&p, &n, &ti)) return KSBA_CT_NONE; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed) ) return KSBA_CT_NONE; if (_ksba_ber_parse_tl (&p, &n, &ti)) return KSBA_CT_NONE; } if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_OBJECT_ID && !ti.is_constructed && ti.length) || ti.length > n) return KSBA_CT_NONE; oid = ksba_oid_to_str (p, ti.length); if (!oid) return KSBA_CT_NONE; /* out of core */ for (i=0; content_handlers[i].oid; i++) { if (!strcmp (content_handlers[i].oid, oid)) break; } ksba_free(oid); if (!content_handlers[i].oid) return KSBA_CT_NONE; /* unknown */ if (maybe_p12 && (content_handlers[i].ct == KSBA_CT_DATA || content_handlers[i].ct == KSBA_CT_SIGNED_DATA)) return KSBA_CT_PKCS12; return content_handlers[i].ct; } /** * ksba_cms_new: * * Create a new and empty CMS object * * Return value: A CMS object or an error code. **/ gpg_error_t ksba_cms_new (ksba_cms_t *r_cms) { *r_cms = xtrycalloc (1, sizeof **r_cms); if (!*r_cms) return gpg_error_from_errno (errno); return 0; } /* Release a list of value trees. */ static void release_value_tree (struct value_tree_s *tree) { while (tree) { struct value_tree_s *tmp = tree->next; _ksba_asn_release_nodes (tree->root); xfree (tree->image); xfree (tree); tree = tmp; } } /** * ksba_cms_release: * @cms: A CMS object * * Release a CMS object. **/ void ksba_cms_release (ksba_cms_t cms) { if (!cms) return; xfree (cms->content.oid); while (cms->digest_algos) { struct oidlist_s *ol = cms->digest_algos->next; xfree (cms->digest_algos->oid); xfree (cms->digest_algos); cms->digest_algos = ol; } while (cms->cert_list) { struct certlist_s *cl = cms->cert_list->next; ksba_cert_release (cms->cert_list->cert); xfree (cms->cert_list->enc_val.algo); xfree (cms->cert_list->enc_val.value); xfree (cms->cert_list->enc_val.ecdh.e); xfree (cms->cert_list->enc_val.ecdh.wrap_algo); xfree (cms->cert_list->enc_val.ecdh.encr_algo); xfree (cms->cert_list); cms->cert_list = cl; } while (cms->cert_info_list) { struct certlist_s *cl = cms->cert_info_list->next; ksba_cert_release (cms->cert_info_list->cert); xfree (cms->cert_info_list->enc_val.algo); xfree (cms->cert_info_list->enc_val.value); xfree (cms->cert_info_list); cms->cert_info_list = cl; } xfree (cms->inner_cont_oid); xfree (cms->encr_algo_oid); xfree (cms->encr_iv); xfree (cms->data.digest); while (cms->signer_info) { struct signer_info_s *tmp = cms->signer_info->next; _ksba_asn_release_nodes (cms->signer_info->root); xfree (cms->signer_info->image); xfree (cms->signer_info->cache.digest_algo); xfree (cms->signer_info); cms->signer_info = tmp; } release_value_tree (cms->recp_info); while (cms->sig_val) { struct sig_val_s *tmp = cms->sig_val->next; xfree (cms->sig_val->algo); xfree (cms->sig_val->value); + xfree (cms->sig_val->ecc.r); xfree (cms->sig_val); cms->sig_val = tmp; } while (cms->capability_list) { struct oidparmlist_s *tmp = cms->capability_list->next; xfree (cms->capability_list->oid); xfree (cms->capability_list); cms->capability_list = tmp; } xfree (cms); } gpg_error_t ksba_cms_set_reader_writer (ksba_cms_t cms, ksba_reader_t r, ksba_writer_t w) { if (!cms || !(r || w)) return gpg_error (GPG_ERR_INV_VALUE); if ((r && cms->reader) || (w && cms->writer) ) return gpg_error (GPG_ERR_CONFLICT); /* already set */ cms->reader = r; cms->writer = w; return 0; } gpg_error_t ksba_cms_parse (ksba_cms_t cms, ksba_stop_reason_t *r_stopreason) { gpg_error_t err; int i; if (!cms || !r_stopreason) return gpg_error (GPG_ERR_INV_VALUE); *r_stopreason = KSBA_SR_RUNNING; if (!cms->stop_reason) { /* Initial state: start parsing */ err = _ksba_cms_parse_content_info (cms); if (err) return err; for (i=0; content_handlers[i].oid; i++) { if (!strcmp (content_handlers[i].oid, cms->content.oid)) break; } if (!content_handlers[i].oid) return gpg_error (GPG_ERR_UNKNOWN_CMS_OBJ); if (!content_handlers[i].parse_handler) return gpg_error (GPG_ERR_UNSUPPORTED_CMS_OBJ); cms->content.ct = content_handlers[i].ct; cms->content.handler = content_handlers[i].parse_handler; cms->stop_reason = KSBA_SR_GOT_CONTENT; } else if (cms->content.handler) { err = cms->content.handler (cms); if (err) return err; } else return gpg_error (GPG_ERR_UNSUPPORTED_CMS_OBJ); *r_stopreason = cms->stop_reason; return 0; } gpg_error_t ksba_cms_build (ksba_cms_t cms, ksba_stop_reason_t *r_stopreason) { gpg_error_t err; if (!cms || !r_stopreason) return gpg_error (GPG_ERR_INV_VALUE); *r_stopreason = KSBA_SR_RUNNING; if (!cms->stop_reason) { /* Initial state: check that the content handler is known */ if (!cms->writer) return gpg_error (GPG_ERR_MISSING_ACTION); if (!cms->content.handler) return gpg_error (GPG_ERR_MISSING_ACTION); if (!cms->inner_cont_oid) return gpg_error (GPG_ERR_MISSING_ACTION); cms->stop_reason = KSBA_SR_GOT_CONTENT; } else if (cms->content.handler) { err = cms->content.handler (cms); if (err) return err; } else return gpg_error (GPG_ERR_UNSUPPORTED_CMS_OBJ); *r_stopreason = cms->stop_reason; return 0; } /* Return the content type. A WHAT of 0 returns the real content type whereas a 1 returns the inner content type. */ ksba_content_type_t ksba_cms_get_content_type (ksba_cms_t cms, int what) { int i; if (!cms) return 0; if (!what) return cms->content.ct; if (what == 1 && cms->inner_cont_oid) { for (i=0; content_handlers[i].oid; i++) { if (!strcmp (content_handlers[i].oid, cms->inner_cont_oid)) return content_handlers[i].ct; } } return 0; } /* Return the object ID of the current cms. This is a constant string valid as long as the context is valid and no new parse is started. */ const char * ksba_cms_get_content_oid (ksba_cms_t cms, int what) { if (!cms) return NULL; if (!what) return cms->content.oid; if (what == 1) return cms->inner_cont_oid; if (what == 2) return cms->encr_algo_oid; return NULL; } /* Copy the initialization vector into iv and its len into ivlen. The caller should provide a suitable large buffer */ gpg_error_t ksba_cms_get_content_enc_iv (ksba_cms_t cms, void *iv, size_t maxivlen, size_t *ivlen) { if (!cms || !iv || !ivlen) return gpg_error (GPG_ERR_INV_VALUE); if (!cms->encr_ivlen) return gpg_error (GPG_ERR_NO_DATA); if (cms->encr_ivlen > maxivlen) return gpg_error (GPG_ERR_BUFFER_TOO_SHORT); memcpy (iv, cms->encr_iv, cms->encr_ivlen); *ivlen = cms->encr_ivlen; return 0; } /** * ksba_cert_get_digest_algo_list: * @cert: Initialized certificate object * @idx: enumerator * * Figure out the the digest algorithm used for the signature and * return its OID. Note that the algos returned are just hints on * what to hash. * * Return value: NULL for no more algorithms or a string valid as long * as the the cms object is valid. **/ const char * ksba_cms_get_digest_algo_list (ksba_cms_t cms, int idx) { struct oidlist_s *ol; if (!cms) return NULL; for (ol=cms->digest_algos; ol && idx; ol = ol->next, idx-- ) ; if (!ol) return NULL; return ol->oid; } /** * ksba_cms_get_issuer_serial: * @cms: CMS object * @idx: index number * @r_issuer: returns the issuer * @r_serial: returns the serial number * * This functions returns the issuer and serial number either from the * sid or the rid elements of a CMS object. * * Return value: 0 on success or an error code. An error code of -1 * is returned to indicate that there is no issuer with that idx, * GPG_ERR_No_Data is returned to indicate that there is no issuer at * all. **/ gpg_error_t ksba_cms_get_issuer_serial (ksba_cms_t cms, int idx, char **r_issuer, ksba_sexp_t *r_serial) { gpg_error_t err; const char *issuer_path, *serial_path; AsnNode root; const unsigned char *image; AsnNode n; if (!cms) return gpg_error (GPG_ERR_INV_VALUE); if (idx < 0) return gpg_error (GPG_ERR_INV_INDEX); if (cms->signer_info) { struct signer_info_s *si; for (si=cms->signer_info; si && idx; si = si->next, idx-- ) ; if (!si) return -1; root = si->root; image = si->image; } else if (cms->recp_info) { struct value_tree_s *tmp; for (tmp=cms->recp_info; tmp && idx; tmp=tmp->next, idx-- ) ; if (!tmp) return -1; root = tmp->root; image = tmp->image; } else return gpg_error (GPG_ERR_NO_DATA); if (cms->signer_info) { issuer_path = "SignerInfo.sid.issuerAndSerialNumber.issuer"; serial_path = "SignerInfo.sid.issuerAndSerialNumber.serialNumber"; } else if (cms->recp_info) { /* Find the choice to use. */ n = _ksba_asn_find_node (root, "RecipientInfo.+"); if (!n || !n->name) return gpg_error (GPG_ERR_NO_VALUE); if (!strcmp (n->name, "ktri")) { issuer_path = "ktri.rid.issuerAndSerialNumber.issuer"; serial_path = "ktri.rid.issuerAndSerialNumber.serialNumber"; } else if (!strcmp (n->name, "kari")) { issuer_path = ("kari..recipientEncryptedKeys" "..rid.issuerAndSerialNumber.issuer"); serial_path = ("kari..recipientEncryptedKeys" "..rid.issuerAndSerialNumber.serialNumber"); } else if (!strcmp (n->name, "kekri")) return gpg_error (GPG_ERR_UNSUPPORTED_CMS_OBJ); else return gpg_error (GPG_ERR_INV_CMS_OBJ); root = n; } if (r_issuer) { n = _ksba_asn_find_node (root, issuer_path); if (!n || !n->down) return gpg_error (GPG_ERR_NO_VALUE); n = n->down; /* dereference the choice node */ if (n->off == -1) { /* fputs ("get_issuer problem at node:\n", stderr); */ /* _ksba_asn_node_dump_all (n, stderr); */ return gpg_error (GPG_ERR_GENERAL); } err = _ksba_dn_to_str (image, n, r_issuer); if (err) return err; } if (r_serial) { char numbuf[22]; int numbuflen; unsigned char *p; /* fixme: we do not release the r_issuer stuff on error */ n = _ksba_asn_find_node (root, serial_path); if (!n) return gpg_error (GPG_ERR_NO_VALUE); if (n->off == -1) { /* fputs ("get_serial problem at node:\n", stderr); */ /* _ksba_asn_node_dump_all (n, stderr); */ return gpg_error (GPG_ERR_GENERAL); } sprintf (numbuf,"(%u:", (unsigned int)n->len); numbuflen = strlen (numbuf); p = xtrymalloc (numbuflen + n->len + 2); if (!p) return gpg_error (GPG_ERR_ENOMEM); strcpy (p, numbuf); memcpy (p+numbuflen, image + n->off + n->nhdr, n->len); p[numbuflen + n->len] = ')'; p[numbuflen + n->len + 1] = 0; *r_serial = p; } return 0; } /** * ksba_cms_get_digest_algo: * @cms: CMS object * @idx: index of signer * * Figure out the the digest algorithm used by the signer @idx return * its OID. This is the algorithm acually used to calculate the * signature. * * Return value: NULL for no such signer or a constn string valid as * long as the CMS object lives. **/ const char * ksba_cms_get_digest_algo (ksba_cms_t cms, int idx) { AsnNode n; char *algo; struct signer_info_s *si; if (!cms) return NULL; if (!cms->signer_info) return NULL; if (idx < 0) return NULL; for (si=cms->signer_info; si && idx; si = si->next, idx-- ) ; if (!si) return NULL; if (si->cache.digest_algo) return si->cache.digest_algo; n = _ksba_asn_find_node (si->root, "SignerInfo.digestAlgorithm.algorithm"); algo = _ksba_oid_node_to_str (si->image, n); if (algo) { si->cache.digest_algo = algo; } return algo; } /** * ksba_cms_get_cert: * @cms: CMS object * @idx: enumerator * * Get the certificate out of a CMS. The caller should use this in a * loop to get all certificates. The returned certificate is a * shallow copy of the original one; the caller must still use * ksba_cert_release() to free it. * * Return value: A Certificate object or NULL for end of list or error **/ ksba_cert_t ksba_cms_get_cert (ksba_cms_t cms, int idx) { struct certlist_s *cl; if (!cms || idx < 0) return NULL; for (cl=cms->cert_list; cl && idx; cl = cl->next, idx--) ; if (!cl) return NULL; ksba_cert_ref (cl->cert); return cl->cert; } /* Return the extension attribute messageDigest */ gpg_error_t ksba_cms_get_message_digest (ksba_cms_t cms, int idx, char **r_digest, size_t *r_digest_len) { AsnNode nsiginfo, n; struct signer_info_s *si; if (!cms || !r_digest || !r_digest_len) return gpg_error (GPG_ERR_INV_VALUE); if (!cms->signer_info) return gpg_error (GPG_ERR_NO_DATA); if (idx < 0) return gpg_error (GPG_ERR_INV_INDEX); for (si=cms->signer_info; si && idx; si = si->next, idx-- ) ; if (!si) return -1; *r_digest = NULL; *r_digest_len = 0; nsiginfo = _ksba_asn_find_node (si->root, "SignerInfo.signedAttrs"); if (!nsiginfo) return gpg_error (GPG_ERR_BUG); n = _ksba_asn_find_type_value (si->image, nsiginfo, 0, oid_messageDigest, DIM(oid_messageDigest)); if (!n) return 0; /* this is okay, because the element is optional */ /* check that there is only one */ if (_ksba_asn_find_type_value (si->image, nsiginfo, 1, oid_messageDigest, DIM(oid_messageDigest))) return gpg_error (GPG_ERR_DUP_VALUE); /* the value is is a SET OF OCTECT STRING but the set must have excactly one OCTECT STRING. (rfc2630 11.2) */ if ( !(n->type == TYPE_SET_OF && n->down && n->down->type == TYPE_OCTET_STRING && !n->down->right)) return gpg_error (GPG_ERR_INV_CMS_OBJ); n = n->down; if (n->off == -1) return gpg_error (GPG_ERR_BUG); *r_digest_len = n->len; *r_digest = xtrymalloc (n->len); if (!*r_digest) return gpg_error (GPG_ERR_ENOMEM); memcpy (*r_digest, si->image + n->off + n->nhdr, n->len); return 0; } /* Return the extension attribute signing time, which may be empty for no signing time available. */ gpg_error_t ksba_cms_get_signing_time (ksba_cms_t cms, int idx, ksba_isotime_t r_sigtime) { AsnNode nsiginfo, n; struct signer_info_s *si; if (!cms) return gpg_error (GPG_ERR_INV_VALUE); *r_sigtime = 0; if (!cms->signer_info) return gpg_error (GPG_ERR_NO_DATA); if (idx < 0) return gpg_error (GPG_ERR_INV_INDEX); for (si=cms->signer_info; si && idx; si = si->next, idx-- ) ; if (!si) return -1; *r_sigtime = 0; nsiginfo = _ksba_asn_find_node (si->root, "SignerInfo.signedAttrs"); if (!nsiginfo) return 0; /* This is okay because signedAttribs are optional. */ n = _ksba_asn_find_type_value (si->image, nsiginfo, 0, oid_signingTime, DIM(oid_signingTime)); if (!n) return 0; /* This is okay because signing time is optional. */ /* check that there is only one */ if (_ksba_asn_find_type_value (si->image, nsiginfo, 1, oid_signingTime, DIM(oid_signingTime))) return gpg_error (GPG_ERR_DUP_VALUE); /* the value is is a SET OF CHOICE but the set must have excactly one CHOICE of generalized or utctime. (rfc2630 11.3) */ if ( !(n->type == TYPE_SET_OF && n->down && (n->down->type == TYPE_GENERALIZED_TIME || n->down->type == TYPE_UTC_TIME) && !n->down->right)) return gpg_error (GPG_ERR_INV_CMS_OBJ); n = n->down; if (n->off == -1) return gpg_error (GPG_ERR_BUG); return _ksba_asntime_to_iso (si->image + n->off + n->nhdr, n->len, n->type == TYPE_UTC_TIME, r_sigtime); } /* Return a list of OIDs stored as signed attributes for the signature number IDX. All the values (OIDs) for the the requested OID REQOID are returned delimited by a linefeed. Caller must free that list. -1 is returned when IDX is larger than the number of signatures, GPG_ERR_No_Data is returned when there is no such attribute for the given signer. */ gpg_error_t ksba_cms_get_sigattr_oids (ksba_cms_t cms, int idx, const char *reqoid, char **r_value) { gpg_error_t err; AsnNode nsiginfo, n; struct signer_info_s *si; unsigned char *reqoidbuf; size_t reqoidlen; char *retstr = NULL; int i; if (!cms || !r_value) return gpg_error (GPG_ERR_INV_VALUE); if (!cms->signer_info) return gpg_error (GPG_ERR_NO_DATA); if (idx < 0) return gpg_error (GPG_ERR_INV_INDEX); *r_value = NULL; for (si=cms->signer_info; si && idx; si = si->next, idx-- ) ; if (!si) return -1; /* no more signers */ nsiginfo = _ksba_asn_find_node (si->root, "SignerInfo.signedAttrs"); if (!nsiginfo) return -1; /* this is okay, because signedAttribs are optional */ err = ksba_oid_from_str (reqoid, &reqoidbuf, &reqoidlen); if(err) return err; for (i=0; (n = _ksba_asn_find_type_value (si->image, nsiginfo, i, reqoidbuf, reqoidlen)); i++) { char *line, *p; /* the value is is a SET OF OBJECT ID but the set must have excactly one OBJECT ID. (rfc2630 11.1) */ if ( !(n->type == TYPE_SET_OF && n->down && n->down->type == TYPE_OBJECT_ID && !n->down->right)) { xfree (reqoidbuf); xfree (retstr); return gpg_error (GPG_ERR_INV_CMS_OBJ); } n = n->down; if (n->off == -1) { xfree (reqoidbuf); xfree (retstr); return gpg_error (GPG_ERR_BUG); } p = _ksba_oid_node_to_str (si->image, n); if (!p) { xfree (reqoidbuf); xfree (retstr); return gpg_error (GPG_ERR_INV_CMS_OBJ); } if (!retstr) line = retstr = xtrymalloc (strlen (p) + 2); else { char *tmp = xtryrealloc (retstr, strlen (retstr) + 1 + strlen (p) + 2); if (!tmp) line = NULL; else { retstr = tmp; line = stpcpy (retstr + strlen (retstr), "\n"); } } if (!line) { xfree (reqoidbuf); xfree (retstr); xfree (p); return gpg_error (GPG_ERR_ENOMEM); } strcpy (line, p); xfree (p); } xfree (reqoidbuf); if (!n && !i) return -1; /* no such attribute */ *r_value = retstr; return 0; } /** * ksba_cms_get_sig_val: * @cms: CMS object * @idx: index of signer * * Return the actual signature of signer @idx in a format suitable to * be used as input to Libgcrypt's verification function. The caller * must free the returned string. * * Return value: NULL or a string with a S-Exp. **/ ksba_sexp_t ksba_cms_get_sig_val (ksba_cms_t cms, int idx) { AsnNode n, n2; gpg_error_t err; ksba_sexp_t string; struct signer_info_s *si; if (!cms) return NULL; if (!cms->signer_info) return NULL; if (idx < 0) return NULL; for (si=cms->signer_info; si && idx; si = si->next, idx-- ) ; if (!si) return NULL; n = _ksba_asn_find_node (si->root, "SignerInfo.signatureAlgorithm"); if (!n) return NULL; if (n->off == -1) { /* fputs ("ksba_cms_get_sig_val problem at node:\n", stderr); */ /* _ksba_asn_node_dump_all (n, stderr); */ return NULL; } n2 = n->right; /* point to the actual value */ err = _ksba_sigval_to_sexp (si->image + n->off, n->nhdr + n->len + ((!n2||n2->off == -1)? 0:(n2->nhdr+n2->len)), &string); if (err) return NULL; return string; } /* Helper to dump a S-expression. */ #if 0 static void dbg_print_sexp (ksba_const_sexp_t p) { int level = 0; if (!p) fputs ("[none]", stdout); else { for (;;) { if (*p == '(') { putchar (*p); p++; level++; } else if (*p == ')') { putchar (*p); p++; if (--level <= 0 ) { putchar ('\n'); return; } } else if (!digitp (p)) { fputs ("[invalid s-exp]\n", stdout); return; } else { const unsigned char *s; char *endp; unsigned long len, n; len = strtoul (p, &endp, 10); p = endp; if (*p != ':') { fputs ("[invalid s-exp]\n", stdout); return; } p++; for (s=p,n=0; n < len; n++, s++) if ( !((*s >= 'a' && *s <= 'z') || (*s >= 'A' && *s <= 'Z') || (*s >= '0' && *s <= '9') || *s == '-' || *s == '.')) break; if (n < len) { putchar('#'); for (n=0; n < len; n++, p++) printf ("%02X", *p); putchar('#'); } else { for (n=0; n < len; n++, p++) putchar (*p); } } } } putchar ('\n'); } #endif /* 0 */ /** * ksba_cms_get_enc_val: * @cms: CMS object * @idx: index of recipient info * * Return the encrypted value (the session key) of recipient @idx in a * format suitable to be used as input to Libgcrypt's decryption * function. The caller must free the returned string. * * Return value: NULL or a string with a S-Exp. **/ ksba_sexp_t ksba_cms_get_enc_val (ksba_cms_t cms, int idx) { AsnNode root, n, n2; gpg_error_t err; ksba_sexp_t string; struct value_tree_s *vt; char *keyencralgo = NULL; /* Key encryption algo. */ char *parm = NULL; /* Helper to get the parms of kencralgo. */ size_t parmlen; char *keywrapalgo = NULL; /* Key wrap algo. */ struct tag_info ti; const unsigned char *der; size_t derlen; if (!cms) return NULL; if (!cms->recp_info) return NULL; if (idx < 0) return NULL; for (vt=cms->recp_info; vt && idx; vt=vt->next, idx--) ; if (!vt) return NULL; /* No value at this IDX */ /* Find the choice to use. */ root = _ksba_asn_find_node (vt->root, "RecipientInfo.+"); if (!root || !root->name) return NULL; if (!strcmp (root->name, "ktri")) { n = _ksba_asn_find_node (root, "ktri.keyEncryptionAlgorithm"); if (!n || n->off == -1) return NULL; n2 = n->right; /* point to the actual value */ err = _ksba_encval_to_sexp (vt->image + n->off, n->nhdr + n->len + ((!n2||n2->off == -1)? 0:(n2->nhdr+n2->len)), &string); } else if (!strcmp (root->name, "kari")) { /* _ksba_asn_node_dump_all (root, stderr); */ /* Get the encrypted key. Result is in (DER,DERLEN) */ n = _ksba_asn_find_node (root, ("kari..recipientEncryptedKeys" "..encryptedKey")); if (!n || n->off == -1) { err = gpg_error (GPG_ERR_INV_KEYINFO); goto leave; } der = vt->image + n->off; derlen = n->nhdr + n->len; err = parse_octet_string (&der, &derlen, &ti); if (err) goto leave; derlen = ti.length; /* gpgrt_log_printhex (der, derlen, "%s: encryptedKey", __func__); */ /* Get the KEK algos. */ n = _ksba_asn_find_node (root, "kari..keyEncryptionAlgorithm"); if (!n || n->off == -1) { err = gpg_error (GPG_ERR_INV_KEYINFO); goto leave; } err = _ksba_parse_algorithm_identifier2 (vt->image + n->off, n->nhdr + n->len, NULL, &keyencralgo, &parm, &parmlen); if (err) goto leave; if (!parm) { err = gpg_error (GPG_ERR_INV_KEYINFO); goto leave; } err = _ksba_parse_algorithm_identifier (parm, parmlen,NULL, &keywrapalgo); if (err) goto leave; /* gpgrt_log_debug ("%s: keyencralgo='%s'\n", __func__, keyencralgo); */ /* gpgrt_log_debug ("%s: keywrapalgo='%s'\n", __func__, keywrapalgo); */ /* Get the ephemeral public key. */ n = _ksba_asn_find_node (root, "kari..originator..originatorKey"); if (!n || n->off == -1) { err = gpg_error (GPG_ERR_INV_KEYINFO); goto leave; } err = _ksba_encval_kari_to_sexp (vt->image + n->off, n->nhdr + n->len, keyencralgo, keywrapalgo, der, derlen, &string); if (err) goto leave; /* gpgrt_log_debug ("%s: encryptedKey:\n", __func__); */ /* dbg_print_sexp (string); */ } else if (!strcmp (n->name, "kekri")) return NULL; /*GPG_ERR_UNSUPPORTED_CMS_OBJ*/ else return NULL; /*GPG_ERR_INV_CMS_OBJ*/ leave: xfree (keyencralgo); xfree (keywrapalgo); xfree (parm); if (err) { /* gpgrt_log_debug ("%s: error: %s\n", __func__, gpg_strerror (err)); */ return NULL; } return string; } /* Provide a hash function so that we are able to hash the data */ void ksba_cms_set_hash_function (ksba_cms_t cms, void (*hash_fnc)(void *, const void *, size_t), void *hash_fnc_arg) { if (cms) { cms->hash_fnc = hash_fnc; cms->hash_fnc_arg = hash_fnc_arg; } } /* hash the signed attributes of the given signer */ gpg_error_t ksba_cms_hash_signed_attrs (ksba_cms_t cms, int idx) { AsnNode n; struct signer_info_s *si; if (!cms) return gpg_error (GPG_ERR_INV_VALUE); if (!cms->hash_fnc) return gpg_error (GPG_ERR_MISSING_ACTION); if (idx < 0) return -1; for (si=cms->signer_info; si && idx; si = si->next, idx-- ) ; if (!si) return -1; n = _ksba_asn_find_node (si->root, "SignerInfo.signedAttrs"); if (!n || n->off == -1) return gpg_error (GPG_ERR_NO_VALUE); /* We don't hash the implicit tag [0] but a SET tag */ cms->hash_fnc (cms->hash_fnc_arg, "\x31", 1); cms->hash_fnc (cms->hash_fnc_arg, si->image + n->off + 1, n->nhdr + n->len - 1); return 0; } /* Code to create CMS structures */ /** * ksba_cms_set_content_type: * @cms: A CMS object * @what: 0 for content type, 1 for inner content type * @type: Type constant * * Set the content type used for build operations. This should be the * first operation before starting to create a CMS message. * * Return value: 0 on success or an error code **/ gpg_error_t ksba_cms_set_content_type (ksba_cms_t cms, int what, ksba_content_type_t type) { int i; char *oid; if (!cms || what < 0 || what > 1 ) return gpg_error (GPG_ERR_INV_VALUE); for (i=0; content_handlers[i].oid; i++) { if (content_handlers[i].ct == type) break; } if (!content_handlers[i].oid) return gpg_error (GPG_ERR_UNKNOWN_CMS_OBJ); if (!content_handlers[i].build_handler) return gpg_error (GPG_ERR_UNSUPPORTED_CMS_OBJ); oid = xtrystrdup (content_handlers[i].oid); if (!oid) return gpg_error (GPG_ERR_ENOMEM); if (!what) { cms->content.oid = oid; cms->content.ct = content_handlers[i].ct; cms->content.handler = content_handlers[i].build_handler; } else { cms->inner_cont_oid = oid; } return 0; } /** * ksba_cms_add_digest_algo: * @cms: A CMS object * @oid: A stringified object OID describing the hash algorithm * * Set the algorithm to be used for creating the hash. Note, that we * currently can't do a per-signer hash. * * Return value: 0 on success or an error code **/ gpg_error_t ksba_cms_add_digest_algo (ksba_cms_t cms, const char *oid) { struct oidlist_s *ol; if (!cms || !oid) return gpg_error (GPG_ERR_INV_VALUE); ol = xtrymalloc (sizeof *ol); if (!ol) return gpg_error (GPG_ERR_ENOMEM); ol->oid = xtrystrdup (oid); if (!ol->oid) { xfree (ol); return gpg_error (GPG_ERR_ENOMEM); } ol->next = cms->digest_algos; cms->digest_algos = ol; return 0; } /** * ksba_cms_add_signer: * @cms: A CMS object * @cert: A certificate used to describe the signer. * * This functions starts assembly of a new signed data content or adds * another signer to the list of signers. * * Return value: 0 on success or an error code. **/ gpg_error_t ksba_cms_add_signer (ksba_cms_t cms, ksba_cert_t cert) { struct certlist_s *cl, *cl2; if (!cms) return gpg_error (GPG_ERR_INV_VALUE); cl = xtrycalloc (1,sizeof *cl); if (!cl) return gpg_error (GPG_ERR_ENOMEM); ksba_cert_ref (cert); cl->cert = cert; if (!cms->cert_list) cms->cert_list = cl; else { for (cl2=cms->cert_list; cl2->next; cl2 = cl2->next) ; cl2->next = cl; } return 0; } /** * ksba_cms_add_cert: * @cms: A CMS object * @cert: A certificate to be send along with the signed data. * * This functions adds a certificate to the list of certificates send * along with the signed data. Using this is optional but it is very * common to include at least the certificate of the signer it self. * * Return value: 0 on success or an error code. **/ gpg_error_t ksba_cms_add_cert (ksba_cms_t cms, ksba_cert_t cert) { struct certlist_s *cl; if (!cms || !cert) return gpg_error (GPG_ERR_INV_VALUE); /* first check whether this is a duplicate. */ for (cl = cms->cert_info_list; cl; cl = cl->next) { if (!_ksba_cert_cmp (cert, cl->cert)) return 0; /* duplicate */ } /* Okay, add it. */ cl = xtrycalloc (1,sizeof *cl); if (!cl) return gpg_error (GPG_ERR_ENOMEM); ksba_cert_ref (cert); cl->cert = cert; cl->next = cms->cert_info_list; cms->cert_info_list = cl; return 0; } /* Add an S/MIME capability as an extended attribute to the message. This function is to be called for each capability in turn. The first capability added will receive the highest priority. CMS is the context, OID the object identifier of the capability and if DER is not NULL it is used as the DER-encoded parameters of the capability; the length of that DER object is given in DERLEN. DERLEN should be 0 if DER is NULL. The function returns 0 on success or an error code. */ gpg_error_t ksba_cms_add_smime_capability (ksba_cms_t cms, const char *oid, const unsigned char *der, size_t derlen) { gpg_error_t err; struct oidparmlist_s *opl, *opl2; if (!cms || !oid) return gpg_error (GPG_ERR_INV_VALUE); if (!der) derlen = 0; opl = xtrymalloc (sizeof *opl + derlen - 1); if (!opl) return gpg_error_from_errno (errno); opl->next = NULL; opl->oid = xtrystrdup (oid); if (!opl->oid) { err = gpg_error_from_errno (errno); xfree (opl); return err; } opl->parmlen = derlen; if (der) memcpy (opl->parm, der, derlen); /* Append it to maintain the desired order. */ if (!cms->capability_list) cms->capability_list = opl; else { for (opl2=cms->capability_list; opl2->next; opl2 = opl2->next) ; opl2->next = opl; } return 0; } /** * ksba_cms_set_message_digest: * @cms: A CMS object * @idx: The index of the signer * @digest: a message digest * @digest_len: the length of the message digest * * Set a message digest into the signedAttributes of the signer with * the index IDX. The index of a signer is determined by the sequence * of ksba_cms_add_signer() calls; the first signer has the index 0. * This function is to be used when the hash value of the data has * been calculated and before the create function requests the sign * operation. * * Return value: 0 on success or an error code **/ gpg_error_t ksba_cms_set_message_digest (ksba_cms_t cms, int idx, const unsigned char *digest, size_t digest_len) { struct certlist_s *cl; if (!cms || !digest) return gpg_error (GPG_ERR_INV_VALUE); if (!digest_len || digest_len > DIM(cl->msg_digest)) return gpg_error (GPG_ERR_INV_VALUE); if (idx < 0) return gpg_error (GPG_ERR_INV_INDEX); for (cl=cms->cert_list; cl && idx; cl = cl->next, idx--) ; if (!cl) return gpg_error (GPG_ERR_INV_INDEX); /* no certificate to store it */ cl->msg_digest_len = digest_len; memcpy (cl->msg_digest, digest, digest_len); return 0; } /** * ksba_cms_set_signing_time: * @cms: A CMS object * @idx: The index of the signer * @sigtime: a time or an empty value to use the current time * * Set a signing time into the signedAttributes of the signer with * the index IDX. The index of a signer is determined by the sequence * of ksba_cms_add_signer() calls; the first signer has the index 0. * * Return value: 0 on success or an error code **/ gpg_error_t ksba_cms_set_signing_time (ksba_cms_t cms, int idx, const ksba_isotime_t sigtime) { struct certlist_s *cl; if (!cms) return gpg_error (GPG_ERR_INV_VALUE); if (idx < 0) return gpg_error (GPG_ERR_INV_INDEX); for (cl=cms->cert_list; cl && idx; cl = cl->next, idx--) ; if (!cl) return gpg_error (GPG_ERR_INV_INDEX); /* no certificate to store it */ /* Fixme: We might want to check the validity of the passed time string. */ if (!*sigtime) _ksba_current_time (cl->signing_time); else _ksba_copy_time (cl->signing_time, sigtime); return 0; } -/* - r_sig = (sig-val - ( - ( ) - ... - ( ) - )) - The sexp must be in canonical form. - Note the must be given as a stringified OID or the special - string "rsa". - - Note that IDX is only used for consistency checks. +/* Set the signature value as a canonical encoded s-expression. + * + * r_sig = (sig-val + * ( + * ( ) + * ... + * ( ) + * )) + * + * must be given as a stringified OID or the special string + * "rsa". For ECC must either be "ecdsa" or the OID matching the used + * hash algorithm; the expected parameters are "r" and "s". + * + * Note that IDX is only used for consistency checks. */ gpg_error_t ksba_cms_set_sig_val (ksba_cms_t cms, int idx, ksba_const_sexp_t sigval) { - const unsigned char *s; - unsigned long n; + gpg_error_t err; + unsigned long n, namelen; struct sig_val_s *sv, **sv_tail; + const unsigned char *s, *endp, *name; + int ecc; /* True for ECC algos. */ int i; if (!cms) return gpg_error (GPG_ERR_INV_VALUE); if (idx < 0) return gpg_error (GPG_ERR_INV_INDEX); /* only one signer for now */ + /* log_sexp ("sigval:", sigval); */ s = sigval; if (*s != '(') return gpg_error (GPG_ERR_INV_SEXP); s++; for (i=0, sv_tail=&cms->sig_val; *sv_tail; sv_tail=&(*sv_tail)->next, i++) ; if (i != idx) return gpg_error (GPG_ERR_INV_INDEX); if (!(n = snext (&s))) return gpg_error (GPG_ERR_INV_SEXP); if (!smatch (&s, 7, "sig-val")) return gpg_error (GPG_ERR_UNKNOWN_SEXP); if (*s != '(') return gpg_error (digitp (s)? GPG_ERR_UNKNOWN_SEXP : GPG_ERR_INV_SEXP); s++; /* Break out the algorithm ID. */ if (!(n = snext (&s))) return gpg_error (GPG_ERR_INV_SEXP); sv = xtrycalloc (1, sizeof *sv); if (!sv) return gpg_error (GPG_ERR_ENOMEM); + if (n==3 && s[0] == 'r' && s[1] == 's' && s[2] == 'a') - { /* kludge to allow "rsa" to be passed as algorithm name */ - sv->algo = xtrystrdup ("1.2.840.113549.1.1.1"); + { + sv->algo = xtrystrdup ("1.2.840.113549.1.1.1"); /* rsa */ + if (!sv->algo) + { + xfree (sv); + return gpg_error (GPG_ERR_ENOMEM); + } + } + else if (n==5 && !memcmp (s, "ecdsa", 5)) + { + /* Use a placeholder for later fixup. */ + sv->algo = xtrystrdup ("ecdsa"); if (!sv->algo) { xfree (sv); return gpg_error (GPG_ERR_ENOMEM); } } else { sv->algo = xtrymalloc (n+1); if (!sv->algo) { xfree (sv); return gpg_error (GPG_ERR_ENOMEM); } memcpy (sv->algo, s, n); sv->algo[n] = 0; } s += n; - /* And now the values - FIXME: For now we only support one */ - /* fixme: start loop */ - if (*s != '(') - { - xfree (sv->algo); - xfree (sv); - return gpg_error (digitp (s)? GPG_ERR_UNKNOWN_SEXP : GPG_ERR_INV_SEXP); - } - s++; + ecc = (!strcmp (sv->algo, "ecdsa") /* placeholder */ + || !strcmp (sv->algo, "1.2.840.10045.4.3.2") /* ecdsa-with-SHA256 */ + || !strcmp (sv->algo, "1.2.840.10045.4.3.3") /* ecdsa-with-SHA384 */ + || !strcmp (sv->algo, "1.2.840.10045.4.3.4") /* ecdsa-with-SHA512 */ + ); - if (!(n = snext (&s))) - { - xfree (sv->algo); - xfree (sv); - return gpg_error (GPG_ERR_INV_SEXP); - } - s += n; /* ignore the name of the parameter */ + xfree (sv->value); sv->value = NULL; + xfree (sv->ecc.r); sv->ecc.r = NULL; - if (!digitp(s)) + while (*s == '(') { - xfree (sv->algo); - xfree (sv); - /* May also be an invalid S-EXP. */ - return gpg_error (GPG_ERR_UNKNOWN_SEXP); - } + s++; + n = strtoul (s, (char**)&endp, 10); + s = endp; + if (!n || *s != ':') + { + err = gpg_error (GPG_ERR_INV_SEXP); + goto leave; + } + s++; + name = s; + namelen = n; + s += n; - if (!(n = snext (&s))) - { - xfree (sv->algo); - xfree (sv); - return gpg_error (GPG_ERR_INV_SEXP); + if (!digitp(s)) + { + err = gpg_error (GPG_ERR_UNKNOWN_SEXP); /* or invalid sexp */ + goto leave; + } + n = strtoul (s, (char**)&endp, 10); + s = endp; + if (!n || *s != ':') + { + err = gpg_error (GPG_ERR_INV_SEXP); + goto leave; + } + s++; + + if (namelen == 1 && *name == 's') + { + /* Store the "main" parameter into value. */ + xfree (sv->value); + sv->value = xtrymalloc (n); + if (!sv->value) + { + err = gpg_error_from_syserror (); + goto leave; + } + memcpy (sv->value, s, n); + sv->valuelen = n; + } + else if (ecc && namelen == 1 && *name == 'r') + { + xfree (sv->ecc.r); + sv->ecc.r = xtrymalloc (n); + if (!sv->ecc.r) + { + err = gpg_error_from_syserror (); + goto leave; + } + memcpy (sv->ecc.r, s, n); + sv->ecc.rlen = n; + } + /* (We ignore all other parameter of the (key value) form.) */ + + s += n; + if ( *s != ')') + { + err = gpg_error (GPG_ERR_UNKNOWN_SEXP); /* or invalid sexp */ + goto leave; + } + s++; } - sv->value = xtrymalloc (n); - if (!sv->value) + /* Expect two closing parenthesis. */ + if (*s != ')') { - xfree (sv->algo); - xfree (sv); - return gpg_error (GPG_ERR_ENOMEM); + err = gpg_error (digitp (s)? GPG_ERR_UNKNOWN_SEXP : GPG_ERR_INV_SEXP); + goto leave; } - memcpy (sv->value, s, n); - sv->valuelen = n; - s += n; + s++; if ( *s != ')') { - xfree (sv->value); - xfree (sv->algo); - xfree (sv); - return gpg_error (GPG_ERR_UNKNOWN_SEXP); /* but may also be an invalid one */ + err = gpg_error (GPG_ERR_INV_SEXP); + goto leave; } - s++; - /* fixme: end loop over parameters */ - /* we need 2 closing parenthesis */ - if ( *s != ')' || s[1] != ')') + /* Check that we have all required data. */ + if (!sv->value) { - xfree (sv->value); - xfree (sv->algo); - xfree (sv); - return gpg_error (GPG_ERR_INV_SEXP); + err = gpg_error (GPG_ERR_INV_SEXP); + goto leave; + } + if (ecc && (!sv->ecc.r || !sv->ecc.rlen)) + { + err = gpg_error (GPG_ERR_INV_SEXP); + goto leave; } *sv_tail = sv; - return 0; + return 0; /* Success. */ + + leave: /* Note: This is an error-only label. */ + xfree (sv->value); + xfree (sv->algo); + xfree (sv->ecc.r); + xfree (sv); + return err; } /* Set the content encryption algorithm to OID and optionally set the initialization vector to IV */ gpg_error_t ksba_cms_set_content_enc_algo (ksba_cms_t cms, const char *oid, const void *iv, size_t ivlen) { if (!cms || !oid) return gpg_error (GPG_ERR_INV_VALUE); xfree (cms->encr_iv); cms->encr_iv = NULL; cms->encr_ivlen = 0; cms->encr_algo_oid = xtrystrdup (oid); if (!cms->encr_algo_oid) return gpg_error (GPG_ERR_ENOMEM); if (iv) { cms->encr_iv = xtrymalloc (ivlen); if (!cms->encr_iv) return gpg_error (GPG_ERR_ENOMEM); memcpy (cms->encr_iv, iv, ivlen); cms->encr_ivlen = ivlen; } return 0; } /* * encval is expected to be a canonical encoded S-Exp of this form: * (enc-val * ( * ( ) * ... * ( ) * (encr-algo ) * (wrap-algo ) * )) * * Note the must be given as a stringified OID or the special * string "rsa". For RSA there is just one parameter named "a"; * encr-algo and wrap-algo are also not used. For ECC must be * "ecdh", the parameter "s" gives the encrypted key, "e" specified * the ephemeral public key, and wrap-algo algo and encr-algo are the * stringified OIDs for the ECDH algorithm parameters. */ gpg_error_t ksba_cms_set_enc_val (ksba_cms_t cms, int idx, ksba_const_sexp_t encval) { /*FIXME: This shares most code with ...set_sig_val */ struct certlist_s *cl; const char *s, *endp, *name; unsigned long n, namelen; int ecdh = 0; /* We expect ECC parameters. */ if (!cms) return gpg_error (GPG_ERR_INV_VALUE); if (idx < 0) return gpg_error (GPG_ERR_INV_INDEX); for (cl=cms->cert_list; cl && idx; cl = cl->next, idx--) ; if (!cl) return gpg_error (GPG_ERR_INV_INDEX); /* No cert to store the value. */ /* log_sexp ("encval", encval); */ s = encval; if (*s != '(') return gpg_error (GPG_ERR_INV_SEXP); s++; n = strtoul (s, (char**)&endp, 10); s = endp; if (!n || *s!=':') return gpg_error (GPG_ERR_INV_SEXP); /* we don't allow empty lengths */ s++; if (n != 7 || memcmp (s, "enc-val", 7)) return gpg_error (GPG_ERR_UNKNOWN_SEXP); s += 7; if (*s != '(') return gpg_error (digitp (s)? GPG_ERR_UNKNOWN_SEXP : GPG_ERR_INV_SEXP); s++; /* break out the algorithm ID */ n = strtoul (s, (char**)&endp, 10); s = endp; if (!n || *s != ':') return gpg_error (GPG_ERR_INV_SEXP); /* we don't allow empty lengths */ s++; xfree (cl->enc_val.algo); if (n==3 && !memcmp (s, "rsa", 3)) { /* kludge to allow "rsa" to be passed as algorithm name */ cl->enc_val.algo = xtrystrdup ("1.2.840.113549.1.1.1"); if (!cl->enc_val.algo) return gpg_error (GPG_ERR_ENOMEM); } else if (n==4 && !memcmp (s, "ecdh", 4)) { cl->enc_val.algo = xtrystrdup ("1.2.840.10045.2.1"); /* ecPublicKey */ if (!cl->enc_val.algo) return gpg_error (GPG_ERR_ENOMEM); } else { cl->enc_val.algo = xtrymalloc (n+1); if (!cl->enc_val.algo) return gpg_error (GPG_ERR_ENOMEM); memcpy (cl->enc_val.algo, s, n); cl->enc_val.algo[n] = 0; } s += n; ecdh = !strcmp (cl->enc_val.algo, "1.2.840.10045.2.1"); xfree (cl->enc_val.value); cl->enc_val.value = NULL; xfree (cl->enc_val.ecdh.e); cl->enc_val.ecdh.e = NULL; xfree (cl->enc_val.ecdh.encr_algo); cl->enc_val.ecdh.encr_algo = NULL; xfree (cl->enc_val.ecdh.wrap_algo); cl->enc_val.ecdh.wrap_algo = NULL; while (*s == '(') { s++; n = strtoul (s, (char**)&endp, 10); s = endp; if (!n || *s != ':') return gpg_error (GPG_ERR_INV_SEXP); s++; name = s; namelen = n; s += n; if (!digitp(s)) return gpg_error (GPG_ERR_UNKNOWN_SEXP); /* or invalid sexp */ n = strtoul (s, (char**)&endp, 10); s = endp; if (!n || *s != ':') return gpg_error (GPG_ERR_INV_SEXP); s++; if (namelen == 1 && ((!ecdh && *name == 'a') || (ecdh && *name == 's'))) { /* Store the "main" parameter into value. */ xfree (cl->enc_val.value); cl->enc_val.value = xtrymalloc (n); if (!cl->enc_val.value) return gpg_error (GPG_ERR_ENOMEM); memcpy (cl->enc_val.value, s, n); cl->enc_val.valuelen = n; } else if (!ecdh) ; /* Ignore all other parameters for RSA. */ else if (namelen == 1 && *name == 'e') { xfree (cl->enc_val.ecdh.e); cl->enc_val.ecdh.e = xtrymalloc (n); if (!cl->enc_val.ecdh.e) return gpg_error (GPG_ERR_ENOMEM); memcpy (cl->enc_val.ecdh.e, s, n); cl->enc_val.ecdh.elen = n; } else if (namelen == 9 && !memcmp (name, "encr-algo", 9)) { xfree (cl->enc_val.ecdh.encr_algo); cl->enc_val.ecdh.encr_algo = xtrymalloc (n+1); if (!cl->enc_val.ecdh.encr_algo) return gpg_error (GPG_ERR_ENOMEM); memcpy (cl->enc_val.ecdh.encr_algo, s, n); cl->enc_val.ecdh.encr_algo[n] = 0; } else if (namelen == 9 && !memcmp (name, "wrap-algo", 9)) { xfree (cl->enc_val.ecdh.wrap_algo); cl->enc_val.ecdh.wrap_algo = xtrymalloc (n+1); if (!cl->enc_val.ecdh.wrap_algo) return gpg_error (GPG_ERR_ENOMEM); memcpy (cl->enc_val.ecdh.wrap_algo, s, n); cl->enc_val.ecdh.wrap_algo[n] = 0; } /* (We ignore all other parameter of the (key value) form.) */ s += n; if ( *s != ')') return gpg_error (GPG_ERR_UNKNOWN_SEXP); /* or invalid sexp */ s++; } /* Expect two closing parenthesis. */ if (*s != ')') return gpg_error (digitp (s)? GPG_ERR_UNKNOWN_SEXP : GPG_ERR_INV_SEXP); s++; if ( *s != ')') return gpg_error (GPG_ERR_INV_SEXP); /* Check that we have all required data. */ if (!cl->enc_val.value) return gpg_error (GPG_ERR_INV_SEXP); if (ecdh && (!cl->enc_val.ecdh.e || !cl->enc_val.ecdh.elen || !cl->enc_val.ecdh.encr_algo || !cl->enc_val.ecdh.wrap_algo)) return gpg_error (GPG_ERR_INV_SEXP); return 0; } /** * ksba_cms_add_recipient: * @cms: A CMS object * @cert: A certificate used to describe the recipient. * * This functions starts assembly of a new enveloped data content or adds * another recipient to the list of recipients. * * Note: after successful completion of this function ownership of * @cert is transferred to @cms. * * Return value: 0 on success or an error code. **/ gpg_error_t ksba_cms_add_recipient (ksba_cms_t cms, ksba_cert_t cert) { /* for now we use the same structure */ return ksba_cms_add_signer (cms, cert); } /* Content handler for parsing messages */ static gpg_error_t ct_parse_data (ksba_cms_t cms) { (void)cms; return gpg_error (GPG_ERR_NOT_IMPLEMENTED); } static gpg_error_t ct_parse_signed_data (ksba_cms_t cms) { enum { sSTART, sGOT_HASH, sIN_DATA, sERROR } state = sERROR; ksba_stop_reason_t stop_reason = cms->stop_reason; gpg_error_t err = 0; cms->stop_reason = KSBA_SR_RUNNING; /* Calculate state from last reason and do some checks */ if (stop_reason == KSBA_SR_GOT_CONTENT) { state = sSTART; } else if (stop_reason == KSBA_SR_NEED_HASH) { state = sGOT_HASH; } else if (stop_reason == KSBA_SR_BEGIN_DATA) { if (!cms->hash_fnc) err = gpg_error (GPG_ERR_MISSING_ACTION); else state = sIN_DATA; } else if (stop_reason == KSBA_SR_END_DATA) { state = sGOT_HASH; } else if (stop_reason == KSBA_SR_RUNNING) err = gpg_error (GPG_ERR_INV_STATE); else if (stop_reason) err = gpg_error (GPG_ERR_BUG); if (err) return err; /* Do the action */ if (state == sSTART) err = _ksba_cms_parse_signed_data_part_1 (cms); else if (state == sGOT_HASH) err = _ksba_cms_parse_signed_data_part_2 (cms); else if (state == sIN_DATA) err = read_and_hash_cont (cms); else err = gpg_error (GPG_ERR_INV_STATE); if (err) return err; /* Calculate new stop reason */ if (state == sSTART) { if (cms->detached_data && !cms->data.digest) { /* We use this stop reason to inform the caller about a detached signatures. Actually there is no need for him to hash the data now, he can do this also later. */ stop_reason = KSBA_SR_NEED_HASH; } else { /* The user must now provide a hash function so that we can hash the data in the next round */ stop_reason = KSBA_SR_BEGIN_DATA; } } else if (state == sIN_DATA) stop_reason = KSBA_SR_END_DATA; else if (state ==sGOT_HASH) stop_reason = KSBA_SR_READY; cms->stop_reason = stop_reason; return 0; } static gpg_error_t ct_parse_enveloped_data (ksba_cms_t cms) { enum { sSTART, sREST, sINDATA, sERROR } state = sERROR; ksba_stop_reason_t stop_reason = cms->stop_reason; gpg_error_t err = 0; cms->stop_reason = KSBA_SR_RUNNING; /* Calculate state from last reason and do some checks */ if (stop_reason == KSBA_SR_GOT_CONTENT) { state = sSTART; } else if (stop_reason == KSBA_SR_DETACHED_DATA) { state = sREST; } else if (stop_reason == KSBA_SR_BEGIN_DATA) { state = sINDATA; } else if (stop_reason == KSBA_SR_END_DATA) { state = sREST; } else if (stop_reason == KSBA_SR_RUNNING) err = gpg_error (GPG_ERR_INV_STATE); else if (stop_reason) err = gpg_error (GPG_ERR_BUG); if (err) return err; /* Do the action */ if (state == sSTART) err = _ksba_cms_parse_enveloped_data_part_1 (cms); else if (state == sREST) err = _ksba_cms_parse_enveloped_data_part_2 (cms); else if (state == sINDATA) err = read_encrypted_cont (cms); else err = gpg_error (GPG_ERR_INV_STATE); if (err) return err; /* Calculate new stop reason */ if (state == sSTART) { stop_reason = cms->detached_data? KSBA_SR_DETACHED_DATA : KSBA_SR_BEGIN_DATA; } else if (state == sINDATA) stop_reason = KSBA_SR_END_DATA; else if (state ==sREST) stop_reason = KSBA_SR_READY; cms->stop_reason = stop_reason; return 0; } static gpg_error_t ct_parse_digested_data (ksba_cms_t cms) { (void)cms; return gpg_error (GPG_ERR_NOT_IMPLEMENTED); } static gpg_error_t ct_parse_encrypted_data (ksba_cms_t cms) { (void)cms; return gpg_error (GPG_ERR_NOT_IMPLEMENTED); } /* Content handlers for building messages */ static gpg_error_t ct_build_data (ksba_cms_t cms) { (void)cms; return gpg_error (GPG_ERR_NOT_IMPLEMENTED); } /* Write everything up to the encapsulated data content type. */ static gpg_error_t build_signed_data_header (ksba_cms_t cms) { gpg_error_t err; unsigned char *buf; const char *s; size_t len; int i; /* Write the outer contentInfo. */ err = _ksba_ber_write_tl (cms->writer, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, 0); if (err) return err; err = ksba_oid_from_str (cms->content.oid, &buf, &len); if (err) return err; err = _ksba_ber_write_tl (cms->writer, TYPE_OBJECT_ID, CLASS_UNIVERSAL, 0, len); if (!err) err = ksba_writer_write (cms->writer, buf, len); xfree (buf); if (err) return err; err = _ksba_ber_write_tl (cms->writer, 0, CLASS_CONTEXT, 1, 0); if (err) return err; /* The SEQUENCE */ err = _ksba_ber_write_tl (cms->writer, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, 0); if (err) return err; /* figure out the CMSVersion to be used */ if (0 /* fixme: have_attribute_certificates || encapsulated_content != data || any_signer_info_is_version_3*/ ) s = "\x03"; else s = "\x01"; err = _ksba_ber_write_tl (cms->writer, TYPE_INTEGER, CLASS_UNIVERSAL, 0, 1); if (err) return err; err = ksba_writer_write (cms->writer, s, 1); if (err) return err; /* SET OF DigestAlgorithmIdentifier */ { unsigned char *value; size_t valuelen; ksba_writer_t tmpwrt; err = ksba_writer_new (&tmpwrt); if (err) return err; err = ksba_writer_set_mem (tmpwrt, 512); if (err) { ksba_writer_release (tmpwrt); return err; } for (i=0; (s = ksba_cms_get_digest_algo_list (cms, i)); i++) { int j; const char *s2; /* (make sure not to write duplicates) */ for (j=0; j < i && (s2=ksba_cms_get_digest_algo_list (cms, j)); j++) { if (!strcmp (s, s2)) break; } if (j == i) { err = _ksba_der_write_algorithm_identifier (tmpwrt, s, NULL, 0); if (err) { ksba_writer_release (tmpwrt); return err; } } } value = ksba_writer_snatch_mem (tmpwrt, &valuelen); ksba_writer_release (tmpwrt); if (!value) { err = gpg_error (GPG_ERR_ENOMEM); return err; } err = _ksba_ber_write_tl (cms->writer, TYPE_SET, CLASS_UNIVERSAL, 1, valuelen); if (!err) err = ksba_writer_write (cms->writer, value, valuelen); xfree (value); if (err) return err; } /* Write the (inner) encapsulatedContentInfo */ /* if we have a detached signature we don't need to use undefinite length here - but it doesn't matter either */ err = _ksba_ber_write_tl (cms->writer, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, 0); if (err) return err; err = ksba_oid_from_str (cms->inner_cont_oid, &buf, &len); if (err) return err; err = _ksba_ber_write_tl (cms->writer, TYPE_OBJECT_ID, CLASS_UNIVERSAL, 0, len); if (!err) err = ksba_writer_write (cms->writer, buf, len); xfree (buf); if (err) return err; if ( !cms->detached_data) { /* write the tag */ err = _ksba_ber_write_tl (cms->writer, 0, CLASS_CONTEXT, 1, 0); if (err) return err; } return err; } /* Set the issuer/serial from the cert to the node. mode 0: sid mode 1: rid */ static gpg_error_t set_issuer_serial (AsnNode info, ksba_cert_t cert, int mode) { gpg_error_t err; AsnNode dst, src; if (!info || !cert) return gpg_error (GPG_ERR_INV_VALUE); src = _ksba_asn_find_node (cert->root, "Certificate.tbsCertificate.serialNumber"); dst = _ksba_asn_find_node (info, mode? "rid.issuerAndSerialNumber.serialNumber": "sid.issuerAndSerialNumber.serialNumber"); err = _ksba_der_copy_tree (dst, src, cert->image); if (err) return err; src = _ksba_asn_find_node (cert->root, "Certificate.tbsCertificate.issuer"); dst = _ksba_asn_find_node (info, mode? "rid.issuerAndSerialNumber.issuer": "sid.issuerAndSerialNumber.issuer"); err = _ksba_der_copy_tree (dst, src, cert->image); if (err) return err; return 0; } /* Store the sequence of capabilities at NODE */ static gpg_error_t store_smime_capability_sequence (AsnNode node, struct oidparmlist_s *capabilities) { gpg_error_t err; struct oidparmlist_s *cap, *cap2; unsigned char *value; size_t valuelen; ksba_writer_t tmpwrt; err = ksba_writer_new (&tmpwrt); if (err) return err; err = ksba_writer_set_mem (tmpwrt, 512); if (err) { ksba_writer_release (tmpwrt); return err; } for (cap=capabilities; cap; cap = cap->next) { /* (avoid writing duplicates) */ for (cap2=capabilities; cap2 != cap; cap2 = cap2->next) { if (!strcmp (cap->oid, cap2->oid) && cap->parmlen && cap->parmlen == cap2->parmlen && !memcmp (cap->parm, cap2->parm, cap->parmlen)) break; /* Duplicate found. */ } if (cap2 == cap) { /* RFC3851 requires that a missing parameter must not be encoded as NULL. This is in contrast to all other usages of the algorithm identifier where ist is allowed and in some profiles (e.g. tmttv2) even explicitly suggested to use NULL. */ err = _ksba_der_write_algorithm_identifier (tmpwrt, cap->oid, cap->parmlen?cap->parm:(const void*)"", cap->parmlen); if (err) { ksba_writer_release (tmpwrt); return err; } } } value = ksba_writer_snatch_mem (tmpwrt, &valuelen); if (!value) err = gpg_error (GPG_ERR_ENOMEM); if (!err) err = _ksba_der_store_sequence (node, value, valuelen); xfree (value); ksba_writer_release (tmpwrt); return err; } /* An object used to construct the signed attributes. */ struct attrarray_s { AsnNode root; unsigned char *image; size_t imagelen; }; /* Thank you ASN.1 committee for allowing us to employ a sort to make that DER encoding even more complicate. */ static int compare_attrarray (const void *a_v, const void *b_v) { const struct attrarray_s *a = a_v; const struct attrarray_s *b = b_v; const unsigned char *ap, *bp; size_t an, bn; ap = a->image; an = a->imagelen; bp = b->image; bn = b->imagelen; for (; an && bn; an--, bn--, ap++, bp++ ) if (*ap != *bp) return *ap - *bp; return (an == bn)? 0 : (an > bn)? 1 : -1; } /* Write the END of data NULL tag and everything we can write before the user can calculate the signature */ static gpg_error_t build_signed_data_attributes (ksba_cms_t cms) { gpg_error_t err; int signer; ksba_asn_tree_t cms_tree = NULL; struct certlist_s *certlist; struct oidlist_s *digestlist; struct signer_info_s *si, **si_tail; AsnNode root = NULL; struct attrarray_s attrarray[4]; int attridx = 0; int i; memset (attrarray, 0, sizeof (attrarray)); /* Write the End tag */ err = _ksba_ber_write_tl (cms->writer, 0, 0, 0, 0); if (err) return err; if (cms->signer_info) return gpg_error (GPG_ERR_CONFLICT); /* This list must be empty at this point. */ /* Write optional certificates */ if (cms->cert_info_list) { unsigned long totallen = 0; const unsigned char *der; size_t n; for (certlist = cms->cert_info_list; certlist; certlist = certlist->next) { if (!ksba_cert_get_image (certlist->cert, &n)) return gpg_error (GPG_ERR_GENERAL); /* User passed an unitialized cert */ totallen += n; } err = _ksba_ber_write_tl (cms->writer, 0, CLASS_CONTEXT, 1, totallen); if (err) return err; for (certlist = cms->cert_info_list; certlist; certlist = certlist->next) { if (!(der=ksba_cert_get_image (certlist->cert, &n))) return gpg_error (GPG_ERR_BUG); err = ksba_writer_write (cms->writer, der, n); if (err ) return err; } } /* If we ever support it, here is the right place to do it: Write the optional CRLs */ /* Now we have to prepare the signer info. For now we will just build the signedAttributes, so that the user can do the signature calculation */ err = ksba_asn_create_tree ("cms", &cms_tree); if (err) return err; certlist = cms->cert_list; if (!certlist) { err = gpg_error (GPG_ERR_MISSING_VALUE); /* oops */ goto leave; } digestlist = cms->digest_algos; if (!digestlist) { err = gpg_error (GPG_ERR_MISSING_VALUE); /* oops */ goto leave; } si_tail = &cms->signer_info; for (signer=0; certlist; signer++, certlist = certlist->next, digestlist = digestlist->next) { AsnNode attr; AsnNode n; unsigned char *image; size_t imagelen; for (i = 0; i < attridx; i++) { _ksba_asn_release_nodes (attrarray[i].root); xfree (attrarray[i].image); } attridx = 0; memset (attrarray, 0, sizeof (attrarray)); if (!digestlist) { err = gpg_error (GPG_ERR_MISSING_VALUE); /* oops */ goto leave; } if (!certlist->cert || !digestlist->oid) { err = gpg_error (GPG_ERR_BUG); goto leave; } /* Include the pretty important message digest. */ attr = _ksba_asn_expand_tree (cms_tree->parse_tree, "CryptographicMessageSyntax.Attribute"); if (!attr) { err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); goto leave; } n = _ksba_asn_find_node (attr, "Attribute.attrType"); if (!n) { err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); goto leave; } err = _ksba_der_store_oid (n, oidstr_messageDigest); if (err) goto leave; n = _ksba_asn_find_node (attr, "Attribute.attrValues"); if (!n || !n->down) return gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); n = n->down; /* fixme: ugly hack */ assert (certlist && certlist->msg_digest_len); err = _ksba_der_store_octet_string (n, certlist->msg_digest, certlist->msg_digest_len); if (err) goto leave; err = _ksba_der_encode_tree (attr, &image, &imagelen); if (err) goto leave; attrarray[attridx].root = attr; attrarray[attridx].image = image; attrarray[attridx].imagelen = imagelen; attridx++; /* Include the content-type attribute. */ attr = _ksba_asn_expand_tree (cms_tree->parse_tree, "CryptographicMessageSyntax.Attribute"); if (!attr) { err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); goto leave; } n = _ksba_asn_find_node (attr, "Attribute.attrType"); if (!n) { err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); goto leave; } err = _ksba_der_store_oid (n, oidstr_contentType); if (err) goto leave; n = _ksba_asn_find_node (attr, "Attribute.attrValues"); if (!n || !n->down) { err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); goto leave; } n = n->down; /* fixme: ugly hack */ err = _ksba_der_store_oid (n, cms->inner_cont_oid); if (err) goto leave; err = _ksba_der_encode_tree (attr, &image, &imagelen); if (err) goto leave; attrarray[attridx].root = attr; attrarray[attridx].image = image; attrarray[attridx].imagelen = imagelen; attridx++; /* Include the signing time */ if (*certlist->signing_time) { attr = _ksba_asn_expand_tree (cms_tree->parse_tree, "CryptographicMessageSyntax.Attribute"); if (!attr) { err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); goto leave; } n = _ksba_asn_find_node (attr, "Attribute.attrType"); if (!n) { err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); goto leave; } err = _ksba_der_store_oid (n, oidstr_signingTime); if (err) goto leave; n = _ksba_asn_find_node (attr, "Attribute.attrValues"); if (!n || !n->down) { err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); goto leave; } n = n->down; /* fixme: ugly hack */ err = _ksba_der_store_time (n, certlist->signing_time); if (err) goto leave; err = _ksba_der_encode_tree (attr, &image, &imagelen); if (err) goto leave; /* We will use the attributes again - so save them */ attrarray[attridx].root = attr; attrarray[attridx].image = image; attrarray[attridx].imagelen = imagelen; attridx++; } /* Include the S/MIME capabilities with the first signer. */ if (cms->capability_list && !signer) { attr = _ksba_asn_expand_tree (cms_tree->parse_tree, "CryptographicMessageSyntax.Attribute"); if (!attr) { err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); goto leave; } n = _ksba_asn_find_node (attr, "Attribute.attrType"); if (!n) { err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); goto leave; } err = _ksba_der_store_oid (n, oidstr_smimeCapabilities); if (err) goto leave; n = _ksba_asn_find_node (attr, "Attribute.attrValues"); if (!n || !n->down) { err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); goto leave; } n = n->down; /* fixme: ugly hack */ err = store_smime_capability_sequence (n, cms->capability_list); if (err) goto leave; err = _ksba_der_encode_tree (attr, &image, &imagelen); if (err) goto leave; attrarray[attridx].root = attr; attrarray[attridx].image = image; attrarray[attridx].imagelen = imagelen; attridx++; } /* Arggh. That silly ASN.1 DER encoding rules: We need to sort the SET values. */ qsort (attrarray, attridx, sizeof (struct attrarray_s), compare_attrarray); /* Now copy them to an SignerInfo tree. This tree is not complete but suitable for ksba_cms_hash_signed_attributes() */ root = _ksba_asn_expand_tree (cms_tree->parse_tree, "CryptographicMessageSyntax.SignerInfo"); n = _ksba_asn_find_node (root, "SignerInfo.signedAttrs"); if (!n || !n->down) { err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); goto leave; } /* This is another ugly hack to move to the element we want */ for (n = n->down->down; n && n->type != TYPE_SEQUENCE; n = n->right) ; if (!n) { err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); goto leave; } assert (attridx <= DIM (attrarray)); for (i=0; i < attridx; i++) { if (i) { if ( !(n=_ksba_asn_insert_copy (n))) { err = gpg_error (GPG_ERR_ENOMEM); goto leave; } } err = _ksba_der_copy_tree (n, attrarray[i].root, attrarray[i].image); if (err) goto leave; _ksba_asn_release_nodes (attrarray[i].root); free (attrarray[i].image); attrarray[i].root = NULL; attrarray[i].image = NULL; } err = _ksba_der_encode_tree (root, &image, NULL); if (err) goto leave; si = xtrycalloc (1, sizeof *si); if (!si) return gpg_error (GPG_ERR_ENOMEM); si->root = root; root = NULL; si->image = image; /* Hmmm, we don't set the length of the image. */ *si_tail = si; si_tail = &si->next; } leave: _ksba_asn_release_nodes (root); ksba_asn_tree_release (cms_tree); for (i = 0; i < attridx; i++) { _ksba_asn_release_nodes (attrarray[i].root); xfree (attrarray[i].image); } return err; } /* The user has calculated the signatures and we can therefore write everything left over to do. */ static gpg_error_t build_signed_data_rest (ksba_cms_t cms) { gpg_error_t err; int signer; ksba_asn_tree_t cms_tree = NULL; struct certlist_s *certlist; struct oidlist_s *digestlist; struct signer_info_s *si; struct sig_val_s *sv; ksba_writer_t tmpwrt = NULL; AsnNode root = NULL; + ksba_der_t dbld = NULL; /* Now we can really write the signer info */ err = ksba_asn_create_tree ("cms", &cms_tree); if (err) return err; certlist = cms->cert_list; if (!certlist) { err = gpg_error (GPG_ERR_MISSING_VALUE); /* oops */ return err; } /* To construct the set we use a temporary writer object. */ err = ksba_writer_new (&tmpwrt); if (err) goto leave; err = ksba_writer_set_mem (tmpwrt, 2048); if (err) goto leave; digestlist = cms->digest_algos; si = cms->signer_info; sv = cms->sig_val; for (signer=0; certlist; signer++, certlist = certlist->next, digestlist = digestlist->next, si = si->next, sv = sv->next) { AsnNode n, n2; unsigned char *image; size_t imagelen; + const char *oid; if (!digestlist || !si || !sv) { err = gpg_error (GPG_ERR_MISSING_VALUE); /* oops */ goto leave; } if (!certlist->cert || !digestlist->oid) { err = gpg_error (GPG_ERR_BUG); goto leave; } root = _ksba_asn_expand_tree (cms_tree->parse_tree, "CryptographicMessageSyntax.SignerInfo"); /* We store a version of 1 because we use the issuerAndSerialNumber */ n = _ksba_asn_find_node (root, "SignerInfo.version"); if (!n) { err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); goto leave; } err = _ksba_der_store_integer (n, "\x00\x00\x00\x01\x01"); if (err) goto leave; /* Store the sid */ n = _ksba_asn_find_node (root, "SignerInfo.sid"); if (!n) { err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); goto leave; } err = set_issuer_serial (n, certlist->cert, 0); if (err) goto leave; /* store the digestAlgorithm */ n = _ksba_asn_find_node (root, "SignerInfo.digestAlgorithm.algorithm"); if (!n) { err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); goto leave; } err = _ksba_der_store_oid (n, digestlist->oid); if (err) goto leave; n = _ksba_asn_find_node (root, "SignerInfo.digestAlgorithm.parameters"); if (!n) { err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); goto leave; } err = _ksba_der_store_null (n); if (err) goto leave; /* and the signed attributes */ n = _ksba_asn_find_node (root, "SignerInfo.signedAttrs"); if (!n || !n->down) { err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); goto leave; } assert (si->root); assert (si->image); n2 = _ksba_asn_find_node (si->root, "SignerInfo.signedAttrs"); if (!n2 || !n2->down) { err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); goto leave; } err = _ksba_der_copy_tree (n, n2, si->image); if (err) goto leave; image = NULL; /* store the signatureAlgorithm */ n = _ksba_asn_find_node (root, "SignerInfo.signatureAlgorithm.algorithm"); if (!n) { err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); goto leave; } if (!sv->algo) { err = gpg_error (GPG_ERR_MISSING_VALUE); goto leave; } - err = _ksba_der_store_oid (n, sv->algo); + + if (!strcmp (sv->algo, "ecdsa")) + { + /* Look at the digest algorithm and replace accordingly. */ + if (!strcmp (digestlist->oid, "2.16.840.1.101.3.4.2.1")) + oid = "1.2.840.10045.4.3.2"; /* ecdsa-with-SHA256 */ + else if (!strcmp (digestlist->oid, "2.16.840.1.101.3.4.2.2")) + oid = "1.2.840.10045.4.3.3"; /* ecdsa-with-SHA384 */ + else if (!strcmp (digestlist->oid, "2.16.840.1.101.3.4.2.3")) + oid = "1.2.840.10045.4.3.4"; /* ecdsa-with-SHA512 */ + else + { + err = gpg_error (GPG_ERR_DIGEST_ALGO); + goto leave; + } + } + else + oid = sv->algo; + + err = _ksba_der_store_oid (n, oid); if (err) goto leave; n = _ksba_asn_find_node (root, "SignerInfo.signatureAlgorithm.parameters"); if (!n) { err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); goto leave; } err = _ksba_der_store_null (n); if (err) goto leave; /* store the signature */ if (!sv->value) { err = gpg_error (GPG_ERR_MISSING_VALUE); goto leave; } n = _ksba_asn_find_node (root, "SignerInfo.signature"); if (!n) { err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); goto leave; } - err = _ksba_der_store_octet_string (n, sv->value, sv->valuelen); - if (err) - goto leave; + + if (sv->ecc.r) /* ECDSA */ + { + unsigned char *tmpder; + size_t tmpderlen; + + _ksba_der_release (dbld); + dbld = _ksba_der_builder_new (0); + if (!dbld) + { + err = gpg_error_from_syserror (); + goto leave; + } + _ksba_der_add_tag (dbld, 0, TYPE_SEQUENCE); + _ksba_der_add_int (dbld, sv->ecc.r, sv->ecc.rlen, 1); + _ksba_der_add_int (dbld, sv->value, sv->valuelen, 1); + _ksba_der_add_end (dbld); + + err = _ksba_der_builder_get (dbld, &tmpder, &tmpderlen); + if (err) + goto leave; + err = _ksba_der_store_octet_string (n, tmpder, tmpderlen); + xfree (tmpder); + if (err) + goto leave; + } + else /* RSA */ + { + err = _ksba_der_store_octet_string (n, sv->value, sv->valuelen); + if (err) + goto leave; + } /* Make the DER encoding and write it out. */ err = _ksba_der_encode_tree (root, &image, &imagelen); if (err) goto leave; err = ksba_writer_write (tmpwrt, image, imagelen); xfree (image); if (err) goto leave; } /* Write out the SET filled with all signer infos */ { unsigned char *value; size_t valuelen; value = ksba_writer_snatch_mem (tmpwrt, &valuelen); if (!value) { err = gpg_error (GPG_ERR_ENOMEM); goto leave; } err = _ksba_ber_write_tl (cms->writer, TYPE_SET, CLASS_UNIVERSAL, 1, valuelen); if (!err) err = ksba_writer_write (cms->writer, value, valuelen); xfree (value); if (err) goto leave; } /* Write 3 end tags */ err = _ksba_ber_write_tl (cms->writer, 0, 0, 0, 0); if (!err) err = _ksba_ber_write_tl (cms->writer, 0, 0, 0, 0); if (!err) err = _ksba_ber_write_tl (cms->writer, 0, 0, 0, 0); leave: ksba_asn_tree_release (cms_tree); _ksba_asn_release_nodes (root); ksba_writer_release (tmpwrt); - + _ksba_der_release (dbld); return err; } static gpg_error_t ct_build_signed_data (ksba_cms_t cms) { enum { sSTART, sDATAREADY, sGOTSIG, sERROR } state = sERROR; ksba_stop_reason_t stop_reason; gpg_error_t err = 0; stop_reason = cms->stop_reason; cms->stop_reason = KSBA_SR_RUNNING; /* Calculate state from last reason and do some checks */ if (stop_reason == KSBA_SR_GOT_CONTENT) { state = sSTART; } else if (stop_reason == KSBA_SR_BEGIN_DATA) { /* fixme: check that the message digest has been set */ state = sDATAREADY; } else if (stop_reason == KSBA_SR_END_DATA) state = sDATAREADY; else if (stop_reason == KSBA_SR_NEED_SIG) { if (!cms->sig_val) err = gpg_error (GPG_ERR_MISSING_ACTION); /* No ksba_cms_set_sig_val () called */ state = sGOTSIG; } else if (stop_reason == KSBA_SR_RUNNING) err = gpg_error (GPG_ERR_INV_STATE); else if (stop_reason) err = gpg_error (GPG_ERR_BUG); if (err) return err; /* Do the action */ if (state == sSTART) { /* figure out whether a detached signature is requested */ if (cms->cert_list && cms->cert_list->msg_digest_len) cms->detached_data = 1; else cms->detached_data = 0; /* and start encoding */ err = build_signed_data_header (cms); } else if (state == sDATAREADY) { if (!cms->detached_data) err = _ksba_ber_write_tl (cms->writer, 0, 0, 0, 0); if (!err) err = build_signed_data_attributes (cms); } else if (state == sGOTSIG) err = build_signed_data_rest (cms); else err = gpg_error (GPG_ERR_INV_STATE); if (err) return err; /* Calculate new stop reason */ if (state == sSTART) { /* user should write the data and calculate the hash or do nothing in case of END_DATA */ stop_reason = cms->detached_data? KSBA_SR_END_DATA : KSBA_SR_BEGIN_DATA; } else if (state == sDATAREADY) stop_reason = KSBA_SR_NEED_SIG; else if (state == sGOTSIG) stop_reason = KSBA_SR_READY; cms->stop_reason = stop_reason; return 0; } /* write everything up to the encryptedContentInfo including the tag */ static gpg_error_t build_enveloped_data_header (ksba_cms_t cms) { gpg_error_t err; int recpno; struct certlist_s *certlist; unsigned char *buf; const char *s; size_t len; ksba_der_t dbld = NULL; int any_ecdh = 0; /* See whether we have any ECDH recipients. */ for (certlist = cms->cert_list; certlist; certlist = certlist->next) if (certlist->enc_val.ecdh.e) { any_ecdh = 1; break; } /* Write the outer contentInfo */ /* fixme: code is shared with signed_data_header */ err = _ksba_ber_write_tl (cms->writer, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, 0); if (err) return err; err = ksba_oid_from_str (cms->content.oid, &buf, &len); if (err) return err; err = _ksba_ber_write_tl (cms->writer, TYPE_OBJECT_ID, CLASS_UNIVERSAL, 0, len); if (!err) err = ksba_writer_write (cms->writer, buf, len); xfree (buf); if (err) return err; err = _ksba_ber_write_tl (cms->writer, 0, CLASS_CONTEXT, 1, 0); if (err) return err; /* The SEQUENCE */ err = _ksba_ber_write_tl (cms->writer, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, 0); if (err) return err; /* figure out the CMSVersion to be used (from rfc2630): version is the syntax version number. If originatorInfo is present, then version shall be 2. If any of the RecipientInfo structures included have a version other than 0, then the version shall be 2. If unprotectedAttrs is present, then version shall be 2. If originatorInfo is absent, all of the RecipientInfo structures are version 0, and unprotectedAttrs is absent, then version shall be 0. For SPHINX the version number must be 0. */ s = any_ecdh? "\x02" :"\x00"; err = _ksba_ber_write_tl (cms->writer, TYPE_INTEGER, CLASS_UNIVERSAL, 0, 1); if (err) return err; err = ksba_writer_write (cms->writer, s, 1); if (err) return err; /* Note: originatorInfo is not yet implemented and must not be used for SPHINX */ certlist = cms->cert_list; if (!certlist) { err = gpg_error (GPG_ERR_MISSING_VALUE); /* oops */ goto leave; } dbld = _ksba_der_builder_new (0); if (!dbld) { err = gpg_error_from_syserror (); goto leave; } _ksba_der_add_tag (dbld, 0, TYPE_SET); for (recpno=0; certlist; recpno++, certlist = certlist->next) { const unsigned char *der; size_t derlen; if (!certlist->cert) { err = gpg_error (GPG_ERR_BUG); goto leave; } if (!certlist->enc_val.ecdh.e) /* RSA (ktri) */ { _ksba_der_add_tag (dbld, 0, TYPE_SEQUENCE); /* We store a version of 0 because we are only allowed to * use the issuerAndSerialNumber for SPHINX */ _ksba_der_add_ptr (dbld, 0, TYPE_INTEGER, "", 1); /* rid.issuerAndSerialNumber */ _ksba_der_add_tag (dbld, 0, TYPE_SEQUENCE); /* rid.issuerAndSerialNumber.issuer */ err = _ksba_cert_get_issuer_dn_ptr (certlist->cert, &der, &derlen); if (err) goto leave; _ksba_der_add_der (dbld, der, derlen); /* rid.issuerAndSerialNumber.serialNumber */ err = _ksba_cert_get_serial_ptr (certlist->cert, &der, &derlen); if (err) goto leave; _ksba_der_add_der (dbld, der, derlen); _ksba_der_add_end (dbld); /* Store the keyEncryptionAlgorithm */ _ksba_der_add_tag (dbld, 0, TYPE_SEQUENCE); if (!certlist->enc_val.algo || !certlist->enc_val.value) { err = gpg_error (GPG_ERR_MISSING_VALUE); goto leave; } _ksba_der_add_oid (dbld, certlist->enc_val.algo); /* Now store NULL for the optional parameters. From Peter * Gutmann's X.509 style guide: * * Another pitfall to be aware of is that algorithms which * have no parameters have this specified as a NULL value * rather than omitting the parameters field entirely. The * reason for this is that when the 1988 syntax for * AlgorithmIdentifier was translated into the 1997 syntax, * the OPTIONAL associated with the AlgorithmIdentifier * parameters got lost. Later it was recovered via a defect * report, but by then everyone thought that algorithm * parameters were mandatory. Because of this the algorithm * parameters should be specified as NULL, regardless of what * you read elsewhere. * * The trouble is that things *never* get better, they just * stay the same, only more so * -- Terry Pratchett, "Eric" * * Although this is about signing, we always do it. Versions of * Libksba before 1.0.6 had a bug writing out the NULL tag here, * thus in reality we used to be correct according to the * standards despite we didn't intended so. */ _ksba_der_add_ptr (dbld, 0, TYPE_NULL, NULL, 0); _ksba_der_add_end (dbld); /* Store the encryptedKey */ if (!certlist->enc_val.value) { err = gpg_error (GPG_ERR_MISSING_VALUE); goto leave; } _ksba_der_add_ptr (dbld, 0, TYPE_OCTET_STRING, certlist->enc_val.value, certlist->enc_val.valuelen); } else /* ECDH */ { _ksba_der_add_tag (dbld, CLASS_CONTEXT, 1); /* kari */ _ksba_der_add_ptr (dbld, 0, TYPE_INTEGER, "\x03", 1); _ksba_der_add_tag (dbld, CLASS_CONTEXT, 0); /* originator */ _ksba_der_add_tag (dbld, CLASS_CONTEXT, 1); /* originatorKey */ _ksba_der_add_tag (dbld, 0, TYPE_SEQUENCE); /* algorithm */ _ksba_der_add_oid (dbld, certlist->enc_val.algo); _ksba_der_add_end (dbld); _ksba_der_add_bts (dbld, certlist->enc_val.ecdh.e, certlist->enc_val.ecdh.elen, 0); _ksba_der_add_end (dbld); /* end originatorKey */ _ksba_der_add_end (dbld); /* end originator */ _ksba_der_add_tag (dbld, 0, TYPE_SEQUENCE); /* keyEncrAlgo */ _ksba_der_add_oid (dbld, certlist->enc_val.ecdh.encr_algo); _ksba_der_add_tag (dbld, 0, TYPE_SEQUENCE); _ksba_der_add_oid (dbld, certlist->enc_val.ecdh.wrap_algo); _ksba_der_add_end (dbld); _ksba_der_add_end (dbld); /* end keyEncrAlgo */ _ksba_der_add_tag (dbld, 0, TYPE_SEQUENCE); /* recpEncrKeys */ _ksba_der_add_tag (dbld, 0, TYPE_SEQUENCE); /* recpEncrKey */ /* rid.issuerAndSerialNumber */ _ksba_der_add_tag (dbld, 0, TYPE_SEQUENCE); err = _ksba_cert_get_issuer_dn_ptr (certlist->cert, &der, &derlen); if (err) goto leave; _ksba_der_add_der (dbld, der, derlen); err = _ksba_cert_get_serial_ptr (certlist->cert, &der, &derlen); if (err) goto leave; _ksba_der_add_der (dbld, der, derlen); _ksba_der_add_end (dbld); /* encryptedKey */ if (!certlist->enc_val.value) { err = gpg_error (GPG_ERR_MISSING_VALUE); goto leave; } _ksba_der_add_ptr (dbld, 0, TYPE_OCTET_STRING, certlist->enc_val.value, certlist->enc_val.valuelen); _ksba_der_add_end (dbld); /* end recpEncrKey */ _ksba_der_add_end (dbld); /* end recpEncrKeys */ } _ksba_der_add_end (dbld); /* End SEQUENCE (ktri or kari) */ } _ksba_der_add_end (dbld); /* End SET */ /* Write out the SET filled with all recipient infos */ { unsigned char *image; size_t imagelen; err = _ksba_der_builder_get (dbld, &image, &imagelen); if (err) goto leave; err = ksba_writer_write (cms->writer, image, imagelen); xfree (image); if (err) goto leave; } /* Write the (inner) encryptedContentInfo */ err = _ksba_ber_write_tl (cms->writer, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, 0); if (err) return err; err = ksba_oid_from_str (cms->inner_cont_oid, &buf, &len); if (err) return err; err = _ksba_ber_write_tl (cms->writer, TYPE_OBJECT_ID, CLASS_UNIVERSAL, 0, len); if (!err) err = ksba_writer_write (cms->writer, buf, len); xfree (buf); if (err) return err; /* and the encryptionAlgorithm */ err = _ksba_der_write_algorithm_identifier (cms->writer, cms->encr_algo_oid, cms->encr_iv, cms->encr_ivlen); if (err) return err; /* write the tag for the encrypted data, it is an implicit octect string in constructed form and indefinite length */ err = _ksba_ber_write_tl (cms->writer, 0, CLASS_CONTEXT, 1, 0); if (err) return err; /* Now the encrypted data should be written */ leave: _ksba_der_release (dbld); return err; } static gpg_error_t ct_build_enveloped_data (ksba_cms_t cms) { enum { sSTART, sINDATA, sREST, sERROR } state = sERROR; ksba_stop_reason_t stop_reason; gpg_error_t err = 0; stop_reason = cms->stop_reason; cms->stop_reason = KSBA_SR_RUNNING; /* Calculate state from last reason and do some checks */ if (stop_reason == KSBA_SR_GOT_CONTENT) state = sSTART; else if (stop_reason == KSBA_SR_BEGIN_DATA) state = sINDATA; else if (stop_reason == KSBA_SR_END_DATA) state = sREST; else if (stop_reason == KSBA_SR_RUNNING) err = gpg_error (GPG_ERR_INV_STATE); else if (stop_reason) err = gpg_error (GPG_ERR_BUG); if (err) return err; /* Do the action */ if (state == sSTART) err = build_enveloped_data_header (cms); else if (state == sINDATA) err = write_encrypted_cont (cms); else if (state == sREST) { /* SPHINX does not allow for unprotectedAttributes */ /* Write 5 end tags */ err = _ksba_ber_write_tl (cms->writer, 0, 0, 0, 0); if (!err) err = _ksba_ber_write_tl (cms->writer, 0, 0, 0, 0); if (!err) err = _ksba_ber_write_tl (cms->writer, 0, 0, 0, 0); if (!err) err = _ksba_ber_write_tl (cms->writer, 0, 0, 0, 0); } else err = gpg_error (GPG_ERR_INV_STATE); if (err) return err; /* Calculate new stop reason */ if (state == sSTART) { /* user should now write the encrypted data */ stop_reason = KSBA_SR_BEGIN_DATA; } else if (state == sINDATA) { /* tell the user that we wrote everything */ stop_reason = KSBA_SR_END_DATA; } else if (state == sREST) { stop_reason = KSBA_SR_READY; } cms->stop_reason = stop_reason; return 0; } static gpg_error_t ct_build_digested_data (ksba_cms_t cms) { (void)cms; return gpg_error (GPG_ERR_NOT_IMPLEMENTED); } static gpg_error_t ct_build_encrypted_data (ksba_cms_t cms) { (void)cms; return gpg_error (GPG_ERR_NOT_IMPLEMENTED); } diff --git a/src/cms.h b/src/cms.h index 5432473..f1d7149 100644 --- a/src/cms.h +++ b/src/cms.h @@ -1,173 +1,178 @@ /* cms.h - Internal definitions for the CMS functions * Copyright (C) 2001, 2012 g10 Code GmbH * * This file is part of KSBA. * * KSBA is free software; you can redistribute it and/or modify * it under the terms of either * * - the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at * your option) any later version. * * or * * - the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at * your option) any later version. * * or both in parallel, as here. * * KSBA is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public * License for more details. * * You should have received a copies of the GNU General Public License * and the GNU Lesser General Public License along with this program; * if not, see . */ #ifndef CMS_H #define CMS_H 1 #include "ksba.h" #ifndef HAVE_TYPEDEFD_ASNNODE typedef struct asn_node_struct *AsnNode; /* FIXME: should not go here */ #define HAVE_TYPEDEFD_ASNNODE #endif /* This structure is used to store the results of a BER parser run. */ struct value_tree_s { struct value_tree_s *next; AsnNode root; /* root of the tree with the values */ unsigned char *image; size_t imagelen; }; struct enc_val_s { char *algo; unsigned char *value; /* RSA's "a" or ECDH's "s". (malloced) */ size_t valuelen; struct { unsigned char *e; /* Malloced buffer. */ size_t elen; /* Length of E. */ char *encr_algo; /* Malloced OID string. */ char *wrap_algo; /* Malloced OID string. */ } ecdh; }; struct oidlist_s { struct oidlist_s *next; char *oid; }; /* A structure to store an OID and a parameter. */ struct oidparmlist_s { struct oidparmlist_s *next; char *oid; size_t parmlen; unsigned char parm[1]; }; struct certlist_s { struct certlist_s *next; ksba_cert_t cert; int msg_digest_len; /* used length of .. */ char msg_digest[64]; /* enough space to store a SHA-512 hash */ ksba_isotime_t signing_time; struct { AsnNode root; unsigned char *image; } sa; struct enc_val_s enc_val; /* used for creating enveloped data */ }; struct signer_info_s { struct signer_info_s *next; AsnNode root; /* root of the tree with the values */ unsigned char *image; size_t imagelen; struct { char *digest_algo; } cache; }; struct sig_val_s { struct sig_val_s *next; char *algo; - unsigned char *value; - size_t valuelen; + unsigned char *value; /* Malloced buffer for parameter "s". */ + size_t valuelen; /* Used length of VALUE. */ + struct { + unsigned char *r; /* Malloced buffer for parameter "r". */ + size_t rlen; /* Length of R. */ + } ecc; }; + struct ksba_cms_s { gpg_error_t last_error; ksba_reader_t reader; ksba_writer_t writer; void (*hash_fnc)(void *, const void *, size_t); void *hash_fnc_arg; ksba_stop_reason_t stop_reason; struct { char *oid; unsigned long length; int ndef; ksba_content_type_t ct; gpg_error_t (*handler)(ksba_cms_t); } content; struct { unsigned char *digest; int digest_len; } data; int cms_version; struct oidlist_s *digest_algos; struct certlist_s *cert_list; char *inner_cont_oid; /* Encapsulated or Encrypted ContentInfo.contentType as string */ unsigned long inner_cont_len; int inner_cont_ndef; int detached_data; /* no actual data */ char *encr_algo_oid; char *encr_iv; size_t encr_ivlen; struct certlist_s *cert_info_list; /* A list with certificates intended to be send with a signed message */ struct oidparmlist_s *capability_list; /* A list of S/MIME capabilities. */ struct signer_info_s *signer_info; struct value_tree_s *recp_info; struct sig_val_s *sig_val; struct enc_val_s *enc_val; }; /*-- cms.c --*/ /*-- cms-parser.c --*/ gpg_error_t _ksba_cms_parse_content_info (ksba_cms_t cms); gpg_error_t _ksba_cms_parse_signed_data_part_1 (ksba_cms_t cms); gpg_error_t _ksba_cms_parse_signed_data_part_2 (ksba_cms_t cms); gpg_error_t _ksba_cms_parse_enveloped_data_part_1 (ksba_cms_t cms); gpg_error_t _ksba_cms_parse_enveloped_data_part_2 (ksba_cms_t cms); #endif /*CMS_H*/ diff --git a/src/der-builder.c b/src/der-builder.c index 24ecd5c..c6c0e39 100644 --- a/src/der-builder.c +++ b/src/der-builder.c @@ -1,594 +1,625 @@ /* der-builder.c - Straightforward DER object builder * Copyright (C) 2020 g10 Code GmbH * * This file is part of KSBA. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This file is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ /* This is a new way in KSBA to build DER objects without the need and * overhead of using an ASN.1 module. It further avoids a lot of error * checking because the error checking is delayed to the last call. * * For an example on how to use it see cms.c */ #include #include #include #include #include #include "util.h" #include "asn1-constants.h" #include "convert.h" #include "ber-help.h" #include "der-builder.h" struct item_s { short int class; short int tag; unsigned int hdrlen:4; /* Computed size of tag+length field. */ unsigned int is_constructed:1; /* This is a constructed element. */ unsigned int verbatim:1; /* Copy the value verbatim. */ unsigned int is_stop:1; /* This is a STOP item. */ const void *value; size_t valuelen; char *buffer; /* Malloced space or NULL. */ }; /* Our DER context object; it may eventually be extended to also * feature a parser. */ struct ksba_der_s { gpg_error_t error; /* Last error. */ size_t nallocateditems; /* Number of allocated items. */ size_t nitems; /* Number of used items. */ struct item_s *items; /* Array of items. */ int laststop; /* Used as return value of compute_length. */ unsigned int finished:1;/* The object has been constructed. */ }; /* Release a DER object. */ void _ksba_der_release (ksba_der_t d) { int idx; if (!d) return; for (idx=0; idx < d->nitems; idx++) xfree (d->items[idx].buffer); xfree (d->items); xfree (d); } /* Allocate a new DER builder instance. Returns NULL on error. * NITEMS can be used to tell the number of DER items needed so to * reduce the number of automatic reallocations. */ ksba_der_t _ksba_der_builder_new (unsigned int nitems) { ksba_der_t d; d = xtrycalloc (1, sizeof *d); if (!d) return NULL; if (nitems) { d->nallocateditems = nitems; d->items = xtrycalloc (d->nallocateditems, sizeof *d->items); if (!d->items) { xfree (d); return NULL; } } return d; } /* Reset a DER build context so that a new sequence can be build. */ void _ksba_der_builder_reset (ksba_der_t d) { int idx; if (!d) return; /* Oops. */ for (idx=0; idx < d->nitems; idx++) { if (d->items[idx].buffer) { xfree (d->items[idx].buffer); d->items[idx].buffer = NULL; } d->items[idx].hdrlen = 0; d->items[idx].is_constructed = 0; d->items[idx].verbatim = 0; d->items[idx].is_stop = 0; d->items[idx].value = NULL; } d->nitems = 0; d->finished = 0; d->error = 0; } /* Make sure the array of items is large enough for one new item. * Records any error in D and returns true in that case. True is also * returned if D is in finished state. */ static int ensure_space (ksba_der_t d) { struct item_s *newitems; if (!d || d->error || d->finished) return 1; if (d->nitems == d->nallocateditems) { d->nallocateditems += 32; newitems = _ksba_reallocarray (d->items, d->nitems, d->nallocateditems, sizeof *newitems); if (!newitems) d->error = gpg_error_from_syserror (); else d->items = newitems; } return !!d->error; } /* Add a new primitive element to the builder instance D. The element * is described by CLASS, TAG, VALUE, and VALUELEN. CLASS and TAG * must describe a primitive element and (VALUE,VALUELEN) specify its * value. The value is a pointer and its object must not be changed * as long as the instance D exists. For a TYPE_NULL tag no value is * expected. Errors are not returned but recorded for later * retrieval. */ void _ksba_der_add_ptr (ksba_der_t d, int class, int tag, void *value, size_t valuelen) { if (ensure_space (d)) return; d->items[d->nitems].class = class; d->items[d->nitems].tag = tag; d->items[d->nitems].value = value; d->items[d->nitems].valuelen = valuelen; d->nitems++; } /* This is a low level function which assumes that D has been * validated, VALUE is not NULL and enough space for a new item is * available. It takes ownership of VALUE. VERBATIM is usually * passed as false */ static void add_val_core (ksba_der_t d, int class, int tag, void *value, size_t valuelen, int verbatim) { d->items[d->nitems].buffer = value; d->items[d->nitems].class = class; d->items[d->nitems].tag = tag; d->items[d->nitems].value = value; d->items[d->nitems].valuelen = valuelen; d->items[d->nitems].verbatim = !!verbatim; d->nitems++; } /* This is the same as ksba_der_add_ptr but it takes a copy of the * value and thus the caller does not need to care about keeping the * value. */ void _ksba_der_add_val (ksba_der_t d, int class, int tag, const void *value, size_t valuelen) { void *p; if (ensure_space (d)) return; if (!value || !valuelen) { d->error = gpg_error (GPG_ERR_INV_VALUE); return; } p = xtrymalloc (valuelen); if (!p) { d->error = gpg_error_from_syserror (); return; } memcpy (p, value, valuelen); add_val_core (d, class, tag, p, valuelen, 0); } /* Add an OBJECT ID element to D. The OID is given in decimal dotted * format as OIDSTR. */ void _ksba_der_add_oid (ksba_der_t d, const char *oidstr) { gpg_error_t err; unsigned char *buf; size_t len; if (ensure_space (d)) return; err = ksba_oid_from_str (oidstr, &buf, &len); if (err) d->error = err; else add_val_core (d, 0, TYPE_OBJECT_ID, buf, len, 0); } /* Add a BIT STRING to D. Using a separate function allows to easily * pass the number of unused bits. */ void _ksba_der_add_bts (ksba_der_t d, const void *value, size_t valuelen, unsigned int unusedbits) { unsigned char *p; if (ensure_space (d)) return; if (!value || !valuelen || unusedbits > 7) { d->error = gpg_error (GPG_ERR_INV_VALUE); return; } p = xtrymalloc (1+valuelen); if (!p) { d->error = gpg_error_from_syserror (); return; } p[0] = unusedbits; memcpy (p+1, value, valuelen); add_val_core (d, 0, TYPE_BIT_STRING, p, 1+valuelen, 0); } +/* Add (VALUE, VALUELEN) as an INTEGER to D. If FORCE_POSITIVE iset + * set a 0 or positive number is stored regardless of what is in + * (VALUE, VALUELEN). */ +void +_ksba_der_add_int (ksba_der_t d, const void *value, size_t valuelen, + int force_positive) +{ + unsigned char *p; + int need_extra; + + if (ensure_space (d)) + return; + if (!value || !valuelen) + need_extra = 1; /* Assume the integer value 0 was meant. */ + else + need_extra = (force_positive && (*(const unsigned char*)value & 0x80)); + + p = xtrymalloc (need_extra+valuelen); + if (!p) + { + d->error = gpg_error_from_syserror (); + return; + } + if (need_extra) + p[0] = 0; + if (valuelen) + memcpy (p+need_extra, value, valuelen); + add_val_core (d, 0, TYPE_INTEGER, p, need_extra+valuelen, 0); +} + + /* This function allows to add a pre-constructed DER object to the * builder. It should be a valid DER object but its values is not * further checked and copied verbatim to the final DER object * constructed for the handle D. */ void _ksba_der_add_der (ksba_der_t d, const void *der, size_t derlen) { void *p; if (ensure_space (d)) return; if (!der || !derlen) { d->error = gpg_error (GPG_ERR_INV_VALUE); return; } p = xtrymalloc (derlen); if (!p) { d->error = gpg_error_from_syserror (); return; } memcpy (p, der, derlen); add_val_core (d, 0, 0, p, derlen, 1); } /* Add a new constructed object to the builder instance D. The object * is described by CLASS and TAG which must describe a constructed * object. The elements of the constructed objects are added with * more call using the add functions. To close a constructed element * a call to tlv_builer_add_end is required. Errors are not returned * but recorded for later retrieval. */ void _ksba_der_add_tag (ksba_der_t d, int class, int tag) { if (ensure_space (d)) return; d->items[d->nitems].class = class; d->items[d->nitems].tag = tag; d->items[d->nitems].is_constructed = 1; d->nitems++; } /* A call to this function closes a constructed element. This must be * called even for an empty constructed element. */ void _ksba_der_add_end (ksba_der_t d) { if (ensure_space (d)) return; d->items[d->nitems].is_stop = 1; d->nitems++; } /* Return the length of the TL header of a to be constructed TLV. * LENGTH gives the length of the value, if it is 0 indefinite length * is assumed. LENGTH is ignored for the NULL tag. TAG must be less * than 0x1f. On error 0 is returned. Note that this function is * similar to _ksba_ber_count_tl but we want our own copy here. Note * that the returned length is always less than 16 and can thus be * storred in a few bits. */ static unsigned int count_tl (int class, int tag, size_t length) { unsigned int hdrlen = 0; int i; if (tag < 0x1f) hdrlen++; else return 0; if (!tag && !class) hdrlen++; /* end tag */ else if (tag == TYPE_NULL && !class) hdrlen++; /* NULL tag */ else if (!length) hdrlen++; /* indefinite length */ else if (length < 128) hdrlen++; else { i = (length <= 0xff ? 1: length <= 0xffff ? 2: length <= 0xffffff ? 3: 4); hdrlen++; if (i > 3) hdrlen++; if (i > 2) hdrlen++; if (i > 1) hdrlen++; hdrlen++; } return hdrlen; } /* Write TAG of CLASS to BUFFER. CONSTRUCTED is a flag telling * whether the value is constructed. LENGTH gives the length of the * value, if it is 0 undefinite length is assumed. LENGTH is ignored * for the NULL tag. TAG must be less that 0x1f. The caller must * make sure that the written TL field does not overflow the * buffer. */ static void write_tl (unsigned char *buffer, int class, int tag, int constructed, size_t length) { int i; if (tag < 0x1f) { *buffer = (class << 6) | tag; if (constructed) *buffer |= 0x20; buffer++; } else { assert (!"oops"); } if (!tag && !class) *buffer++ = 0; /* end tag */ else if (tag == TYPE_NULL && !class) *buffer++ = 0; /* NULL tag */ else if (!length) *buffer++ = 0x80; /* indefinite length */ else if (length < 128) *buffer++ = length; else { /* If we know the sizeof a size_t we could support larger * objects - however this is pretty ridiculous */ i = (length <= 0xff ? 1: length <= 0xffff ? 2: length <= 0xffffff ? 3: 4); *buffer++ = (0x80 | i); if (i > 3) *buffer++ = length >> 24; if (i > 2) *buffer++ = length >> 16; if (i > 1) *buffer++ = length >> 8; *buffer++ = length; } } /* Compute and set the length of all constructed elements in the item * array of D starting at IDX up to the corresponding stop item. On * error d->error is set. */ static size_t compute_lengths (ksba_der_t d, int idx) { size_t total = 0; if (d->error) return 0; for (; idx < d->nitems; idx++) { if (d->items[idx].is_stop) { d->laststop = idx; break; } if (d->items[idx].verbatim) { total += d->items[idx].valuelen; continue; } if (d->items[idx].is_constructed) { d->items[idx].valuelen = compute_lengths (d, idx+1); if (d->error) return 0; /* Note: The last processed IDX is stored at d->LASTSTOP. */ } d->items[idx].hdrlen = count_tl (d->items[idx].class, d->items[idx].tag, d->items[idx].valuelen); if (!d->items[idx].hdrlen) { if (d->error) d->error = gpg_error (GPG_ERR_ENCODING_PROBLEM); return 0; /* Error. */ } total += d->items[idx].hdrlen + d->items[idx].valuelen; if (d->items[idx].is_constructed) idx = d->laststop; } return total; } /* Return the constructed DER object at D. On success the object is * stored at R_OBJ and its length at R_OBJLEN. The caller needs to * release that memory. On error NULL is stored at R_OBJ and an error * code is returned. Further the number of successful calls prior to * the error are stored at R_OBJLEN. Note than an error may stem from * any of the previous call made to this object or from constructing * the DER object. If this function is called with NULL for R_OBJ * only the current error state is returned and no further processing * is done. This can be used to figure which of the add calls induced * the error. */ gpg_error_t _ksba_der_builder_get (ksba_der_t d, unsigned char **r_obj, size_t *r_objlen) { gpg_error_t err; int idx; unsigned char *buffer = NULL; unsigned char *p; size_t bufsize, buflen; *r_obj = NULL; *r_objlen = 0; if (!d) return gpg_error (GPG_ERR_INV_ARG); if (d->error) { err = d->error; if (r_objlen) *r_objlen = d->nitems; goto leave; } if (!r_obj) return 0; if (!d->finished) { if (!d->nitems || !d->items[d->nitems-1].is_stop) { err = gpg_error (GPG_ERR_NO_OBJ); goto leave; } compute_lengths (d, 0); err = d->error; if (err) goto leave; d->finished = 1; } /* If the first element is a primitive element we rightly assume no * other elements follow. It is the user's duty to build a valid * ASN.1 object. */ bufsize = d->items[0].hdrlen + d->items[0].valuelen; /* for (idx=0; idx < d->nitems; idx++) */ /* gpgrt_log_debug ("DERB[%2d]: c=%d t=%2d %s p=%p h=%u l=%zu\n", */ /* idx, */ /* d->items[idx].class, */ /* d->items[idx].tag, */ /* d->items[idx].verbatim? "verbatim": */ /* d->items[idx].is_stop? "stop": */ /* d->items[idx].is_constructed? "cons":"prim", */ /* d->items[idx].value, */ /* d->items[idx].hdrlen, */ /* d->items[idx].valuelen); */ buffer = xtrymalloc (bufsize); if (!buffer) { err = gpg_error_from_syserror (); goto leave; } buflen = 0; p = buffer; for (idx=0; idx < d->nitems; idx++) { if (d->items[idx].is_stop) continue; if (!d->items[idx].verbatim) { if (buflen + d->items[idx].hdrlen > bufsize) { err = gpg_error (GPG_ERR_BUG); goto leave; } write_tl (p, d->items[idx].class, d->items[idx].tag, d->items[idx].is_constructed, d->items[idx].valuelen); p += d->items[idx].hdrlen; buflen += d->items[idx].hdrlen; } if (d->items[idx].value) { if (buflen + d->items[idx].valuelen > bufsize) { err = gpg_error (GPG_ERR_BUG); goto leave; } memcpy (p, d->items[idx].value, d->items[idx].valuelen); p += d->items[idx].valuelen; buflen += d->items[idx].valuelen; } } assert (buflen == bufsize); *r_obj = buffer; *r_objlen = buflen; buffer = NULL; leave: xfree (buffer); return err; } diff --git a/src/der-builder.h b/src/der-builder.h index de4d62d..8e24506 100644 --- a/src/der-builder.h +++ b/src/der-builder.h @@ -1,52 +1,54 @@ /* der-builder.h - Straightforward DER object builder * Copyright (C) 2020 g10 Code GmbH * * This file is part of KSBA. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This file is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef DER_BUILDER_H #define DER_BUILDER_H 1 struct ksba_der_s; typedef struct ksba_der_s *ksba_der_t; /* A generic release function. If we add a DER parser we will use the * same object and then it does not make sense to have several release * functions. */ void _ksba_der_release (ksba_der_t hd); /* Create a new builder context. */ ksba_der_t _ksba_der_builder_new (unsigned int nitems); /* Reset a builder context. */ void _ksba_der_builder_reset (ksba_der_t d); void _ksba_der_add_ptr (ksba_der_t d, int class, int tag, void *value, size_t valuelen); void _ksba_der_add_val (ksba_der_t d, int class, int tag, const void *value, size_t valuelen); void _ksba_der_add_oid (ksba_der_t d, const char *oidstr); void _ksba_der_add_bts (ksba_der_t d, const void *value, size_t valuelen, unsigned int unusedbits); +void _ksba_der_add_int (ksba_der_t d, const void *value, size_t valuelen, + int force_positive); void _ksba_der_add_der (ksba_der_t d, const void *der, size_t derlen); void _ksba_der_add_tag (ksba_der_t d, int class, int tag); void _ksba_der_add_end (ksba_der_t d); gpg_error_t _ksba_der_builder_get (ksba_der_t d, unsigned char **r_obj, size_t *r_objlen); #endif /*DER_BUILDER_H*/ diff --git a/tests/samples/README b/tests/samples/README index dc37b5d..886b0a2 100644 --- a/tests/samples/README +++ b/tests/samples/README @@ -1,63 +1,73 @@ Certificates downloaded from http://www.magmacom.com/~mbartel/iso/\ certificates/samples/sample_certificates.html on 2003-11-20: authority.crt A root certificate betsy.crt An "everyday" certificate. bull.crt Same but includes a BMPString. Note, that these certs use MD2. Certificates downloaded from http://www.openvalidation.org/download/downloadrootcertsCA1.htm and on 2003-11-20 and prefixed with "ov-": ov-root-ca-cert.crt Root certificate ov-ocsp-server.crt The certificate of the OCSP responder ov-user.crt User certificate ov-userrev.crt A user certificate revoked by OCSP ov-server.crt A server (SSL) certificate ov-serverrev.crt A server certificate revoked by OCSP ov-user.p12 Private keys for the above certificates, ov-userrev.p12 passphrase is "start". ov-server.p12 ov-serverrev.p12 ov-test-crl.crl The current CRL The responder adress is http://ocsp.openvalidation.org Certificates downloaded from http://www.openvalidation.org/en/test/ca2.html on 2006-08-30 and prefixed with "ov2-": ov2-root-ca-cert.crt Root certificate ov2-ocsp-server.crt The certificate of the OCSP responder ov2-user.crt User certificate ov2-userrev.crt A user certificate revoked by OCSP Certificates downloaded on 2007-04-05 from http://dev.experimentalstuff.com:8082/CIC_sample-certs_2006-06-22.zip and converted to binary format. These are signed with ECDSA-P256-SHA384, ECDSA-P256-SHA512, and ECDSA-P384-SHA512. secp256r1-sha384_cert.crt secp256r1-sha512_cert.crt secp384r1-sha512_cert.crt From http://dev.experimentalstuff.com:8082/certs/secp256r1ca.cert.pem openssl-secp256r1ca.cert.crt -ECDH samples from the Mozilla bug tracker: +ECDH sample enveloped data from the Mozilla bug tracker: ecdh-sample1.p7m ecdh-sample1.p7m.asn Commented dump. -RSA sample created with gpgsm +RSA sample enveloped data created with gpgsm rsa-sample1.p7m rsa-sample1.p7m.asn Commented dump + +RSA sample signature created with gpgsm + + rsa-sample1.p7s + rsa-sample1.p7s.asn Commented dump + +ECDSA sample signature created with Governikus Signer + + ecdsa-sample1.p7s signed data is hitchhiker.txt + ecdsa-sample1.p7s.asn Commented dump diff --git a/tests/samples/ecdsa-sample1.p7s b/tests/samples/ecdsa-sample1.p7s new file mode 100644 index 0000000..28375b1 Binary files /dev/null and b/tests/samples/ecdsa-sample1.p7s differ diff --git a/tests/samples/ecdsa-sample1.p7s.asn b/tests/samples/ecdsa-sample1.p7s.asn new file mode 100644 index 0000000..82d42bb --- /dev/null +++ b/tests/samples/ecdsa-sample1.p7s.asn @@ -0,0 +1,284 @@ + 0 NDEF: SEQUENCE { -- ContentInfo + 2 9: OBJECT IDENTIFIER signedData (1 2 840 113549 1 7 2) + 13 NDEF: [0] { -- content + 15 NDEF: SEQUENCE { + 17 1: INTEGER 1 -- version + 20 15: SET { -- digestAlgorithms + 22 13: SEQUENCE { -- digestalgorithmIdentifier + 24 9: OBJECT IDENTIFIER sha-256 (2 16 840 1 101 3 4 2 1) + 35 0: NULL + : } + : } + 37 NDEF: SEQUENCE { -- encapContentInfo + 39 9: OBJECT IDENTIFIER data (1 2 840 113549 1 7 1) + : } -- (no data thus this is a detached signature) + 52 NDEF: [0] { -- certificates + 54 711: SEQUENCE { + 58 431: SEQUENCE { + 62 3: [0] { + 64 1: INTEGER 2 + : } + 67 4: INTEGER 649228179 + 73 13: SEQUENCE { + 75 9: OBJECT IDENTIFIER + : sha256WithRSAEncryption (1 2 840 113549 1 1 11) + 86 0: NULL + : } + 88 119: SEQUENCE { + 90 11: SET { + 92 9: SEQUENCE { + 94 3: OBJECT IDENTIFIER countryName (2 5 4 6) + 99 2: PrintableString 'DE' + : } + : } + 103 28: SET { + 105 26: SEQUENCE { + 107 3: OBJECT IDENTIFIER organizationName (2 5 4 10) + 112 19: UTF8String 'Deutsche Telekom AG' + : } + : } + 133 18: SET { + 135 16: SEQUENCE { + 137 3: OBJECT IDENTIFIER organizationalUnitName (2 5 4 11) + 142 9: UTF8String 'T-TeleSec' + : } + : } + 153 20: SET { + 155 18: SEQUENCE { + 157 3: OBJECT IDENTIFIER commonName (2 5 4 3) + 162 11: UTF8String 'TKS CA 1:PN' + : } + : } + 175 10: SET { + 177 8: SEQUENCE { + 179 3: OBJECT IDENTIFIER serialNumber (2 5 4 5) + 184 1: PrintableString '1' + : } + : } + 187 20: SET { + 189 18: SEQUENCE { + 191 3: OBJECT IDENTIFIER pseudonym (2 5 4 65) + 196 11: UTF8String 'TKS CA 1:PN' + : } + : } + : } + 209 30: SEQUENCE { + 211 13: UTCTime 05/02/2013 12:35:12 GMT + 226 13: UTCTime 05/02/2023 12:35:12 GMT + : } + 241 75: SEQUENCE { + 243 11: SET { + 245 9: SEQUENCE { + 247 3: OBJECT IDENTIFIER countryName (2 5 4 6) + 252 2: PrintableString 'DE' + : } + : } + 256 23: SET { + 258 21: SEQUENCE { + 260 3: OBJECT IDENTIFIER commonName (2 5 4 3) + 265 14: UTF8String 'TKS 13 A 04562' + : } + : } + 281 10: SET { + 283 8: SEQUENCE { + 285 3: OBJECT IDENTIFIER serialNumber (2 5 4 5) + 290 1: PrintableString '1' + : } + : } + 293 23: SET { + 295 21: SEQUENCE { + 297 3: OBJECT IDENTIFIER pseudonym (2 5 4 65) + 302 14: UTF8String 'TKS 13 A 04562' + : } + : } + : } + 318 89: SEQUENCE { + 320 19: SEQUENCE { + 322 7: OBJECT IDENTIFIER ecPublicKey (1 2 840 10045 2 1) + 331 8: OBJECT IDENTIFIER prime256v1 (1 2 840 10045 3 1 7) + : } + 341 66: BIT STRING + : 04 A4 1C 91 05 47 42 1F FB F9 08 BF 2E 4A 53 B6 + : FB 9E D7 25 09 04 D7 71 9A 89 E0 5A 32 E6 DD 93 + : 19 9F E2 99 34 4F 7C 43 EB DC 71 23 47 7B BA 34 + : BE 39 A7 CF 0F F5 82 E5 D1 FC 3F 10 4B 8E 6D 4F + : 4F + : } + 409 82: [3] { + 411 80: SEQUENCE { + 413 31: SEQUENCE { + 415 3: OBJECT IDENTIFIER + : authorityKeyIdentifier (2 5 29 35) + 420 24: OCTET STRING, encapsulates { + 422 22: SEQUENCE { + 424 20: [0] + : CB 03 DF 18 AD 26 0E C8 99 56 83 C6 CD 1D EA 61 + : 02 19 60 39 + : } + : } + : } + 446 29: SEQUENCE { + 448 3: OBJECT IDENTIFIER subjectKeyIdentifier (2 5 29 14) + 453 22: OCTET STRING, encapsulates { + 455 20: OCTET STRING + : D2 A5 17 30 59 88 96 56 72 8A 1E B1 D6 02 18 69 + : 06 00 5A BA + : } + : } + 477 14: SEQUENCE { + 479 3: OBJECT IDENTIFIER keyUsage (2 5 29 15) + 484 1: BOOLEAN TRUE + 487 4: OCTET STRING, encapsulates { + 489 2: BIT STRING 7 unused bits + : '1'B (bit 0) + : } + : } + : } + : } + : } + 493 13: SEQUENCE { + 495 9: OBJECT IDENTIFIER + : sha256WithRSAEncryption (1 2 840 113549 1 1 11) + 506 0: NULL + : } + 508 257: BIT STRING + : 04 3C 56 07 7F B9 EB 93 4E FE CB 23 56 EC 73 B3 + : 10 9A A4 80 91 45 56 77 48 B0 AC BA 25 E4 52 96 + : 62 55 41 30 70 4C 0C 81 77 F3 E9 52 4E C9 DE FB + : 11 70 82 37 1E A1 CF F3 48 8C 26 F3 59 41 25 F5 + : 39 55 56 80 42 D1 9E 63 90 41 BC C4 B2 4D 1C 66 + : 1B CF 88 95 96 CB A0 83 B8 D3 D4 C9 0E E2 E1 89 + : A6 BE E3 2B FB 75 6F 36 43 BF 4E 8D FE 9C B2 79 + : ED 61 8F 63 74 6F C9 B4 AC C2 16 F6 A9 CD 00 0E + : A7 21 7E 84 21 6A 69 FD 88 79 51 9E A4 63 8A 5B + : 00 CE 9F 8E B1 F5 E1 97 1F 53 7B A0 2A 5E 6C D7 + : 62 9F 4B 11 45 43 44 1D 4B FF 8F 87 7D BB B6 38 + : E0 EE 0A 2B 55 72 43 58 66 61 9A 70 8A 34 18 E7 + : C1 68 05 7F C1 03 A3 11 1D ED A3 AD 6C F8 34 B6 + : 63 6A EB 53 C8 21 13 9C 6E C4 68 B4 2D F5 AA 59 + : 6D 42 C7 0D 24 0B 7F 28 8F 76 80 02 BE 8B 52 5F + : 2E B4 79 17 8B D9 9E E2 28 58 72 E5 F1 23 7E CF + : } + : } -- end certificates + 771 447: SET { -- signerInfos + 775 443: SEQUENCE { -- SignerInfo + 779 1: INTEGER 1 -- version + 782 127: SEQUENCE { --sid.signerIdentifier + 784 119: SEQUENCE { + 786 11: SET { + 788 9: SEQUENCE { + 790 3: OBJECT IDENTIFIER countryName (2 5 4 6) + 795 2: PrintableString 'DE' + : } + : } + 799 28: SET { + 801 26: SEQUENCE { + 803 3: OBJECT IDENTIFIER organizationName (2 5 4 10) + 808 19: UTF8String 'Deutsche Telekom AG' + : } + : } + 829 18: SET { + 831 16: SEQUENCE { + 833 3: OBJECT IDENTIFIER organizationalUnitName (2 5 4 11) + 838 9: UTF8String 'T-TeleSec' + : } + : } + 849 20: SET { + 851 18: SEQUENCE { + 853 3: OBJECT IDENTIFIER commonName (2 5 4 3) + 858 11: UTF8String 'TKS CA 1:PN' + : } + : } + 871 10: SET { + 873 8: SEQUENCE { + 875 3: OBJECT IDENTIFIER serialNumber (2 5 4 5) + 880 1: PrintableString '1' + : } + : } + 883 20: SET { + 885 18: SEQUENCE { + 887 3: OBJECT IDENTIFIER pseudonym (2 5 4 65) + 892 11: UTF8String 'TKS CA 1:PN' + : } + : } + : } + 905 4: INTEGER 649228179 + : } -- end signerIdentifier + 911 13: SEQUENCE { -- digestAlgorithm + 913 9: OBJECT IDENTIFIER sha-256 (2 16 840 1 101 3 4 2 1) + 924 0: NULL + : } + 926 206: [0] { -- signedAttrs + 929 24: SEQUENCE { + 931 9: OBJECT IDENTIFIER contentType (1 2 840 113549 1 9 3) + 942 11: SET { + 944 9: OBJECT IDENTIFIER data (1 2 840 113549 1 7 1) + : } + : } + 955 28: SEQUENCE { + 957 9: OBJECT IDENTIFIER signingTime (1 2 840 113549 1 9 5) + 968 15: SET { + 970 13: UTCTime 08/05/2020 08:30:09 GMT + : } + : } + 985 42: SEQUENCE { + 987 9: OBJECT IDENTIFIER '1 2 840 113549 1 9 52' + 998 29: SET { +1000 27: SEQUENCE { +1002 13: SEQUENCE { +1004 9: OBJECT IDENTIFIER + : sha-256 (2 16 840 1 101 3 4 2 1) +1015 0: NULL + : } +1017 10: [1] { +1019 8: OBJECT IDENTIFIER + : ecdsaWithSHA256 (1 2 840 10045 4 3 2) + : } + : } + : } + : } +1029 47: SEQUENCE { +1031 9: OBJECT IDENTIFIER messageDigest (1 2 840 113549 1 9 4) +1042 34: SET { +1044 32: OCTET STRING + : A5 F1 A3 86 07 5E 07 9F 44 D3 9F 89 FF C6 4A E6 + : A0 A3 7F DC F3 38 95 CC 7C A0 E8 17 DA E3 55 D6 + : } + : } +1078 55: SEQUENCE { +1080 11: OBJECT IDENTIFIER + : signingCertificateV2 (1 2 840 113549 1 9 16 2 47) +1093 40: SET { +1095 38: SEQUENCE { +1097 36: SEQUENCE { +1099 34: SEQUENCE { +1101 32: OCTET STRING + : DA AF A8 84 FE 82 0A BA 58 4C 4D 56 F8 31 46 4A + : E1 6B 56 66 40 9B F1 5D F1 2B 6C 90 8F 76 AD 23 + : } + : } + : } + : } + : } + : } -- end signedAttrs +1135 10: SEQUENCE { -- signatureAlgorithm +1137 8: OBJECT IDENTIFIER ecdsaWithSHA256 (1 2 840 10045 4 3 2) + : } +1147 71: OCTET STRING, encapsulates { -- signature +1149 69: SEQUENCE { +1151 33: INTEGER + : 00 A0 DE 18 D7 2A 72 FF E7 E4 47 27 80 67 F3 85 + : 0C 90 72 44 18 C3 30 18 7C 5A A1 E8 7F 1B C9 48 + : 3D +1186 32: INTEGER + : 36 B1 15 F6 DA BA 02 16 48 64 91 17 32 D5 43 84 + : 35 80 0D E3 EF 00 10 FD BF 4D 26 8A 54 CD 72 BD + : } + : } +1220 0: [1] -- unsignedAttrs + : Error: Object has zero length. + : } + : } + : } + : } + : } diff --git a/tests/samples/rsa-sample1.p7s b/tests/samples/rsa-sample1.p7s new file mode 100644 index 0000000..c02b37f Binary files /dev/null and b/tests/samples/rsa-sample1.p7s differ diff --git a/tests/samples/rsa-sample1.p7s.asn b/tests/samples/rsa-sample1.p7s.asn new file mode 100644 index 0000000..351e98d --- /dev/null +++ b/tests/samples/rsa-sample1.p7s.asn @@ -0,0 +1,323 @@ + 0 NDEF: SEQUENCE { -- ContentInfo + 2 9: OBJECT IDENTIFIER signedData (1 2 840 113549 1 7 2) + 13 NDEF: [0] { -- content + 15 NDEF: SEQUENCE { + 17 1: INTEGER 1 -- version + 20 15: SET { -- digestAlgorithms + 22 13: SEQUENCE { -- digestAlgorithmIdentifier + 24 9: OBJECT IDENTIFIER sha-256 (2 16 840 1 101 3 4 2 1) + 35 0: NULL + : } + : } + 37 NDEF: SEQUENCE { -- encapContentInfo + 39 9: OBJECT IDENTIFIER data (1 2 840 113549 1 7 1) + 50 NDEF: [0] { + 52 NDEF: OCTET STRING { + 54 683: OCTET STRING + : 'Far back in the mists of ancient time, in the gr' + : 'eat and glorious days of the.former Galactic Emp' + : 'ire, life was wild, rich and largely tax free...' + : 'Mighty starships plied their way between exotic ' + : 'suns, seeking adventure and.reward among the fur' + : 'thest reaches of Galactic space. In those days,' + : ' spirits.were brave, the stakes were high, men w' + : 'ere real men, women were real women.and small fu' + : 'rry creatures from Alpha Centauri were real smal' + : 'l furry creatures.from Alpha Centauri. And all ' + : 'dared to brave unknown terrors, to do mighty.dee' + : 'ds, to boldly split infinitives that no man had ' + : 'split before -- and thus.was the Empire forged..' + : '..-- Douglas Adams, "The Hitchhiker's Guide to t' + : 'he Galaxy".' + : } + : } + : } -- end encapContentInfo + 747 1284: [0] { -- certificates + 751 1280: SEQUENCE { -- certificate + 755 744: SEQUENCE { + 759 3: [0] { + 761 1: INTEGER 2 + : } + 764 2: INTEGER 6659 + 768 13: SEQUENCE { + 770 9: OBJECT IDENTIFIER + : sha256WithRSAEncryption (1 2 840 113549 1 1 11) + 781 0: NULL + : } + 783 120: SEQUENCE { + 785 11: SET { + 787 9: SEQUENCE { + 789 3: OBJECT IDENTIFIER countryName (2 5 4 6) + 794 2: PrintableString 'DE' + : } + : } + 798 22: SET { + 800 20: SEQUENCE { + 802 3: OBJECT IDENTIFIER organizationName (2 5 4 10) + 807 13: PrintableString 'g10 Code GmbH' + : } + : } + 822 16: SET { + 824 14: SEQUENCE { + 826 3: OBJECT IDENTIFIER organizationalUnitName (2 5 4 11) + 831 7: PrintableString 'Testlab' + : } + : } + 840 30: SET { + 842 28: SEQUENCE { + 844 3: OBJECT IDENTIFIER commonName (2 5 4 3) + 849 21: PrintableString 'g10 Code TEST CA 2019' + : } + : } + 872 31: SET { + 874 29: SEQUENCE { + 876 9: OBJECT IDENTIFIER + : emailAddress (1 2 840 113549 1 9 1) + 887 16: IA5String 'info@g10code.com' + : } + : } + : } + 905 32: SEQUENCE { + 907 13: UTCTime 16/05/2019 07:28:06 GMT + 922 15: GeneralizedTime 05/04/2063 17:00:00 GMT + : } + 939 79: SEQUENCE { + 941 11: SET { + 943 9: SEQUENCE { + 945 3: OBJECT IDENTIFIER countryName (2 5 4 6) + 950 2: PrintableString 'DE' + : } + : } + 954 22: SET { + 956 20: SEQUENCE { + 958 3: OBJECT IDENTIFIER organizationName (2 5 4 10) + 963 13: PrintableString 'g10 Code GmbH' + : } + : } + 978 16: SET { + 980 14: SEQUENCE { + 982 3: OBJECT IDENTIFIER organizationalUnitName (2 5 4 11) + 987 7: PrintableString 'Testlab' + : } + : } + 996 22: SET { + 998 20: SEQUENCE { +1000 3: OBJECT IDENTIFIER commonName (2 5 4 3) +1005 13: PrintableString 'Edward.Tester' + : } + : } + : } +1020 418: SEQUENCE { +1024 13: SEQUENCE { +1026 9: OBJECT IDENTIFIER + : rsaEncryption (1 2 840 113549 1 1 1) +1037 0: NULL + : } +1039 399: BIT STRING, encapsulates { +1044 394: SEQUENCE { +1048 385: INTEGER + : 00 D3 20 E1 13 35 21 EF A5 19 A5 EB 2A 3E 67 A0 + : 60 D1 9B B6 46 C2 3B 81 5E 95 08 95 3F 8C 87 C5 + : 19 F0 CF EC 9E 2A 95 BC E0 24 53 77 1D A6 85 99 + : DC A9 34 D8 31 35 3F F9 3D 14 D5 5F C8 93 B5 A7 + : CC F5 56 B9 72 20 78 19 08 BC A0 20 53 5F 02 3F + : B7 E9 5E 0E BA D1 D4 4B 31 17 67 C3 92 29 A0 AE + : 8F 1B FC 1A 16 90 46 AB 86 21 BE 2C 09 88 6D 11 + : 37 A2 AA DD F7 E1 68 E3 1C 10 EE 36 44 73 28 7D + : 3F 91 B9 EE 09 A2 7A E9 EC DA E3 0E 2C 66 1D 8B + : 99 D0 C7 EA 3C F4 58 41 4E 13 F1 CA A7 56 BD 84 + : EF EF 1A 8E 84 D3 D3 E3 45 BF 3B B5 99 C7 30 26 + : 56 09 7B 7D 3F 45 BC E8 7E CA F5 27 CC 59 BF 3A + : 32 E0 67 A0 5E 88 1C 55 EB 6B EF 6A 9A C5 E2 C5 + : 42 82 18 0D F2 0D FD 39 C2 BD B1 FA 51 A4 32 1C + : BA AC A1 E7 58 B3 AA EA 7F 52 F3 A0 B1 36 21 12 + : 55 94 D1 37 32 F1 37 F2 6F 94 37 F5 1D 01 FD F2 + : 79 D3 F1 C5 5B 7F 84 D5 CC 66 20 2E 8A 3C 97 B6 + : 60 1C F3 92 44 7C 99 27 1C 91 E2 D7 35 68 55 FF + : 06 BA F9 8B 26 ED 8E 01 22 6B 05 E3 DD 27 C6 35 + : 67 F1 8D CD 77 AA A1 92 B2 55 94 A4 8A 95 FA 4E + : 16 52 41 92 4F FD D5 EA EF 4B 78 1D 69 D7 E8 D6 + : B2 C4 F1 DD A8 33 83 9B A3 D0 29 60 91 75 7C 75 + : 67 7A 02 0F 70 5A D4 44 13 1B 14 C5 3E F8 D7 4A + : C9 28 BC D1 06 19 A3 B9 CB 42 58 D5 4A 13 E5 A2 + : D5 +1437 3: INTEGER 65537 + : } + : } + : } +1442 59: [3] { +1444 57: SEQUENCE { +1446 39: SEQUENCE { +1448 3: OBJECT IDENTIFIER subjectAltName (2 5 29 17) +1453 32: OCTET STRING, encapsulates { +1455 30: SEQUENCE { +1457 28: [1] 'edward.tester@demo.gnupg.com' + : } + : } + : } +1487 14: SEQUENCE { +1489 3: OBJECT IDENTIFIER keyUsage (2 5 29 15) +1494 1: BOOLEAN TRUE +1497 4: OCTET STRING, encapsulates { +1499 2: BIT STRING 6 unused bits + : '11'B + : } + : } + : } + : } + : } +1503 13: SEQUENCE { +1505 9: OBJECT IDENTIFIER + : sha256WithRSAEncryption (1 2 840 113549 1 1 11) +1516 0: NULL + : } +1518 513: BIT STRING + : 26 BD C2 0B 32 79 8C 43 7C 8C 1F 08 7B EC A8 C8 + : EE AD 8C B1 CF 72 27 29 F7 97 DD 24 40 56 61 56 + : 39 A2 43 DE A5 92 F8 7D D5 B4 36 C3 50 34 E0 C6 + : 68 A0 BD F2 28 94 DC D3 49 39 D5 2B 3C 7B E4 0F + : BD 6D 07 13 72 C1 F5 83 97 BE A9 87 BE 06 76 35 + : 63 DC 34 09 63 D0 E1 86 8F 1C 99 65 59 1B 73 8F + : 8C AA 50 2A BE 4D 4D 27 9C F5 42 68 BC 11 87 5F + : 14 9E 85 1C 6A 47 FC 87 89 E4 7B 34 3F BA C5 12 + : 38 71 6A 6A 3F D1 D0 A0 84 4B AA 73 8F 6C 05 3A + : 99 C4 26 8A A6 44 23 1E 0B 8F 57 FC B7 30 15 A6 + : 24 63 5E 3A 44 60 5F 94 20 1B 97 4E 3F 21 93 29 + : 05 ED B2 09 D8 46 AA 59 43 24 73 2A 76 C6 CC 54 + : 16 5D 27 CF 43 FE E8 7D B5 36 F4 65 BD 98 9F 5B + : 42 4D 38 4C 63 79 83 9A AD F7 24 81 17 09 4A 39 + : 84 1B E3 F4 12 3C C4 44 EE 18 56 DD BD D2 FF 1F + : 52 7C F6 84 5F 33 34 D5 54 BF 60 C1 69 8A EA BE + : 52 AA CD 3E D8 32 D2 6A 2C D1 61 F1 0C 34 BC A5 + : B1 99 79 49 48 3E 07 79 5C B0 DA F9 CD EF 75 83 + : DD 2E 08 54 8D 76 4D BF 5A A6 AE 81 97 17 7E 36 + : 93 9D DB 5F 7F 87 FA DC 48 6F 11 C8 E7 AE 13 CE + : BA FD 83 31 36 63 FA DF 55 F7 33 C3 BF 67 99 AF + : 02 4D 64 CC 3D D9 32 A4 B1 7C 65 C1 F4 FF 8C AD + : 97 5B F2 2F C7 D4 E4 F8 41 2B 7C 1E 0C 7D A2 15 + : 55 51 16 35 EB 87 92 D1 8C 55 C0 AD 18 23 50 8B + : C7 13 B9 DD 1A 55 85 7F 9C BE A1 9D BF 5A 17 ED + : 0C 94 05 F5 4A D1 62 56 2B 9A A9 7F E1 ED E8 95 + : B5 06 93 B5 FD D1 21 3D 00 B6 91 D1 07 6D 36 03 + : 90 5B 6F B4 ED E5 FF EE 74 74 D3 C6 27 6D 1F 28 + : 10 76 BE B3 2A D7 00 FD 47 44 C8 A4 7D A5 1B 9A + : 1D 49 69 80 64 1A AC 7A 06 67 C4 E0 9D 60 2C 27 + : 8B FD 53 BB 61 0D DF 18 1A 62 72 41 D1 FB 2D B8 + : C9 1F E3 17 CD 48 34 14 B6 61 59 49 46 A7 94 21 + : } -- end certificate + : } -- end certificates +2035 703: SET { -- signerInfos +2039 699: SEQUENCE { -- SignerInfo +2043 1: INTEGER 1 -- version +2046 126: SEQUENCE { -- sid.signerIdentifier +2048 120: SEQUENCE { +2050 11: SET { +2052 9: SEQUENCE { +2054 3: OBJECT IDENTIFIER countryName (2 5 4 6) +2059 2: PrintableString 'DE' + : } + : } +2063 22: SET { +2065 20: SEQUENCE { +2067 3: OBJECT IDENTIFIER organizationName (2 5 4 10) +2072 13: PrintableString 'g10 Code GmbH' + : } + : } +2087 16: SET { +2089 14: SEQUENCE { +2091 3: OBJECT IDENTIFIER organizationalUnitName (2 5 4 11) +2096 7: PrintableString 'Testlab' + : } + : } +2105 30: SET { +2107 28: SEQUENCE { +2109 3: OBJECT IDENTIFIER commonName (2 5 4 3) +2114 21: PrintableString 'g10 Code TEST CA 2019' + : } + : } +2137 31: SET { +2139 29: SEQUENCE { +2141 9: OBJECT IDENTIFIER + : emailAddress (1 2 840 113549 1 9 1) +2152 16: IA5String 'info@g10code.com' + : } + : } + : } +2170 2: INTEGER 6659 -- serial + : } -- end signerIdentifier +2174 13: SEQUENCE { -- digestAlgorithm +2176 9: OBJECT IDENTIFIER sha-256 (2 16 840 1 101 3 4 2 1) +2187 0: NULL + : } +2189 147: [0] { -- signedAttrs +2192 24: SEQUENCE { +2194 9: OBJECT IDENTIFIER contentType (1 2 840 113549 1 9 3) +2205 11: SET { +2207 9: OBJECT IDENTIFIER data (1 2 840 113549 1 7 1) + : } + : } +2218 28: SEQUENCE { +2220 9: OBJECT IDENTIFIER signingTime (1 2 840 113549 1 9 5) +2231 15: SET { +2233 13: UTCTime 11/05/2020 12:09:13 GMT + : } + : } +2248 40: SEQUENCE { +2250 9: OBJECT IDENTIFIER + : sMIMECapabilities (1 2 840 113549 1 9 15) +2261 27: SET { +2263 25: SEQUENCE { +2265 11: SEQUENCE { +2267 9: OBJECT IDENTIFIER + : aes128-CBC (2 16 840 1 101 3 4 1 2) + : } +2278 10: SEQUENCE { +2280 8: OBJECT IDENTIFIER + : des-EDE3-CBC (1 2 840 113549 3 7) + : } + : } + : } + : } +2290 47: SEQUENCE { +2292 9: OBJECT IDENTIFIER messageDigest (1 2 840 113549 1 9 4) +2303 34: SET { +2305 32: OCTET STRING + : DB 0B 0E 52 8C 33 35 70 0B F0 57 B8 2B 8E D7 F3 + : 45 E5 4E A9 EC 7D A0 67 E0 5C AD 33 A6 03 4F 6C + : } + : } + : } -- end signedAttrs +2339 13: SEQUENCE { -- signatureAlgorithm +2341 9: OBJECT IDENTIFIER rsaEncryption (1 2 840 113549 1 1 1) +2352 0: NULL + : } +2354 384: OCTET STRING -- signature + : 63 10 7A 48 55 AB 40 2B 6B 7C 84 91 DC 64 FB D7 + : 8D BE DE DB 03 3C 6B 81 87 2E 67 E9 53 74 36 E3 + : 25 08 97 21 CB 9B 6B 1B 0C 93 94 F7 38 CC 4E CB + : 3C EA 07 5C E6 CC 89 0D D9 C8 0E 4D 11 8C 09 10 + : BA 4A EE 61 7C 66 5C FD 9E BF 89 68 DD 61 43 6C + : 68 3C D0 44 1C 5B EC 09 60 29 EF 4A 0D 86 40 1B + : 9F CA F8 2F 29 2D 00 B4 C3 23 2A 7E 2A 29 C5 AE + : 12 DF F0 93 BC 8D 44 28 F5 1C 8E 37 2B 5B 82 1F + : 18 02 F1 68 41 EB F9 99 92 9D 21 56 99 33 50 80 + : 73 DD 35 67 1F 75 CA 5C F4 07 BC B5 53 29 8D B9 + : 30 4B 8C 28 90 47 9B 92 74 98 FE 37 27 1B C5 C9 + : 65 AA 23 99 AB CA A5 31 5F D6 C5 7D 31 C2 6F AD + : 71 2E 8A 5F 72 5A 5D E3 4D 43 D9 5E DC 09 B5 30 + : 10 C1 C4 C9 BF BD 07 C6 D6 F0 09 2F 7A 6A 21 6D + : 2F 4D 6C CB 95 6F 69 E7 D2 94 32 6A 19 48 49 C7 + : 18 31 AB 4F D3 42 C2 91 BD 2C AD F2 14 10 4A 9B + : 69 DB C1 42 58 FB FD B1 EA 03 32 F0 79 37 48 35 + : E2 81 BC 08 A1 EE 6C 7B 14 16 A9 E2 1D CB D6 A4 + : A9 73 4F D9 E8 57 46 A1 33 77 DF 8F F9 44 44 F4 + : CA 9B 22 07 05 89 E4 7F 60 2F BA 15 ED DE 84 0D + : 3F 72 07 90 E9 0F 6A 3E 6B D1 C7 CD 9F DE D5 3F + : 93 17 7F CF 3D 34 E3 90 CC 07 3F 83 FF AF 2F 93 + : 69 3E 00 C3 D5 2B B6 BD 9D 22 D0 66 CB AD 13 36 + : DC BD 38 87 B0 68 4A E9 2B D0 80 BE 70 C4 56 16 + : } -- end SignerInfo + : } -- end signerInfos + : } -- end SignedData + : } -- end content + : } -- end ContentInfo