diff --git a/src/cert.c b/src/cert.c index 40adb91..198030a 100644 --- a/src/cert.c +++ b/src/cert.c @@ -1,2197 +1,2197 @@ /* cert.c - main function for the certificate handling * Copyright (C) 2001, 2002, 2003, 2004, 2005, 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 . */ #include #include #include #include #include #include #include "util.h" #include "ber-decoder.h" #include "ber-help.h" #include "convert.h" #include "keyinfo.h" #include "sexp-parse.h" #include "cert.h" static const char oidstr_subjectKeyIdentifier[] = "2.5.29.14"; static const char oidstr_keyUsage[] = "2.5.29.15"; static const char oidstr_subjectAltName[] = "2.5.29.17"; static const char oidstr_issuerAltName[] = "2.5.29.18"; static const char oidstr_basicConstraints[] = "2.5.29.19"; static const char oidstr_crlDistributionPoints[] = "2.5.29.31"; static const char oidstr_certificatePolicies[] = "2.5.29.32"; static const char oidstr_authorityKeyIdentifier[] = "2.5.29.35"; static const char oidstr_extKeyUsage[] = "2.5.29.37"; static const char oidstr_authorityInfoAccess[] = "1.3.6.1.5.5.7.1.1"; static const char oidstr_subjectInfoAccess[] = "1.3.6.1.5.5.7.1.11"; /** * ksba_cert_new: * * Create a new and empty certificate object * * Return value: 0 on success or error code. For a successful * operation, ACERT is set to the new certifixate obbject, otherwise * it is set to NULL. **/ gpg_error_t ksba_cert_new (ksba_cert_t *acert) { *acert = xtrycalloc (1, sizeof **acert); if (!*acert) return gpg_error_from_errno (errno); (*acert)->ref_count++; return 0; } void ksba_cert_ref (ksba_cert_t cert) { if (!cert) fprintf (stderr, "BUG: ksba_cert_ref for NULL\n"); else ++cert->ref_count; } /** * ksba_cert_release: * @cert: A certificate object * * Release a certificate object. **/ void ksba_cert_release (ksba_cert_t cert) { int i; if (!cert) return; if (cert->ref_count < 1) { fprintf (stderr, "BUG: trying to release an already released cert\n"); return; } if (--cert->ref_count) return; if (cert->udata) { struct cert_user_data *ud = cert->udata; cert->udata = NULL; do { struct cert_user_data *ud2 = ud->next; if (ud->data && ud->data != ud->databuf) xfree (ud->data); xfree (ud); ud = ud2; } while (ud); } xfree (cert->cache.digest_algo); if (cert->cache.extns_valid) { for (i=0; i < cert->cache.n_extns; i++) xfree (cert->cache.extns[i].oid); xfree (cert->cache.extns); } _ksba_asn_release_nodes (cert->root); ksba_asn_tree_release (cert->asn_tree); xfree (cert->image); xfree (cert); } /* Store arbitrary data along with a certificate. The DATA of length DATALEN will be stored under the string KEY. If some data is already stored under this key it will be replaced by the new data. Using NULL for DATA will effectivly delete the data. On error (i.e. out or memory) an already existing data object stored under KEY may get deleted. This function is not thread safe because we don't employ any locking. */ gpg_error_t ksba_cert_set_user_data (ksba_cert_t cert, const char *key, const void *data, size_t datalen) { struct cert_user_data *ud; if (!cert || !key || !*key) return gpg_error (GPG_ERR_INV_VALUE); for (ud=cert->udata; ud; ud = ud->next) if (!strcmp (ud->key, key)) break; if (ud) /* Update the data stored under this key or reuse this item. */ { if (ud->data && ud->data != ud->databuf) xfree (ud->data); ud->data = NULL; if (data && datalen <= sizeof ud->databuf) { memcpy (ud->databuf, data, datalen); ud->data = ud->databuf; ud->datalen = datalen; } else if (data) { ud->data = xtrymalloc (datalen); if (!ud->data) return gpg_error_from_errno (errno); memcpy (ud->data, data, datalen); ud->datalen = datalen; } } else if (data) /* Insert as a new item. */ { ud = xtrycalloc (1, sizeof *ud + strlen (key)); if (!ud) return gpg_error_from_errno (errno); strcpy (ud->key, key); if (datalen <= sizeof ud->databuf) { memcpy (ud->databuf, data, datalen); ud->data = ud->databuf; ud->datalen = datalen; } else { ud->data = xtrymalloc (datalen); if (!ud->data) { xfree (ud); return gpg_error_from_errno (errno); } memcpy (ud->data, data, datalen); ud->datalen = datalen; } ud->next = cert->udata; cert->udata = ud; } return 0; } /* Return user data for certificate CERT stored under the string KEY. The caller needs to provide a suitable large BUFFER and pass the usable length of the buffer in BUFFERLEN. If DATALEN is not NULL, the length of the data stored at BUFFER will be stored there. If BUFFER is NULL, BUFFERLEN will be ignored and the required length of the buffer will be returned at DATALEN. On success 0 is returned. If no data is stored under KEY GPG_ERR_NOT_FOUND is returned. If the provided buffer is too short, GPG_ERR_BUFFER_TOO_SHORT will be returned (note, that this is not the case if BUFFER is NULL). */ gpg_error_t ksba_cert_get_user_data (ksba_cert_t cert, const char *key, void *buffer, size_t bufferlen, size_t *datalen) { struct cert_user_data *ud; if (!cert || !key || !*key) return gpg_error (GPG_ERR_INV_VALUE); for (ud=cert->udata; ud; ud = ud->next) if (!strcmp (ud->key, key)) break; if (!ud || !ud->data) return gpg_error (GPG_ERR_NOT_FOUND); if (datalen) *datalen = ud->datalen; if (buffer) { if (ud->datalen > bufferlen) return gpg_error (GPG_ERR_BUFFER_TOO_SHORT); memcpy (buffer, ud->data, ud->datalen); } return 0; } /** * ksba_cert_read_der: * @cert: An unitialized certificate object * @reader: A KSBA Reader object * * Read the next certificate from the reader and store it in the * certificate object for future access. The certificate is parsed * and rejected if it has any syntactical or semantical error * (i.e. does not match the ASN.1 description). * * Return value: 0 on success or an error value **/ gpg_error_t ksba_cert_read_der (ksba_cert_t cert, ksba_reader_t reader) { gpg_error_t err = 0; BerDecoder decoder = NULL; if (!cert || !reader) return gpg_error (GPG_ERR_INV_VALUE); if (cert->initialized) return gpg_error (GPG_ERR_CONFLICT); /* Fixme: should remove the old one */ _ksba_asn_release_nodes (cert->root); ksba_asn_tree_release (cert->asn_tree); cert->root = NULL; cert->asn_tree = NULL; err = ksba_asn_create_tree ("tmttv2", &cert->asn_tree); if (err) goto leave; decoder = _ksba_ber_decoder_new (); if (!decoder) { err = gpg_error (GPG_ERR_ENOMEM); goto leave; } err = _ksba_ber_decoder_set_reader (decoder, reader); if (err) goto leave; err = _ksba_ber_decoder_set_module (decoder, cert->asn_tree); if (err) goto leave; err = _ksba_ber_decoder_decode (decoder, "TMTTv2.Certificate", 0, &cert->root, &cert->image, &cert->imagelen); if (!err) cert->initialized = 1; leave: _ksba_ber_decoder_release (decoder); return err; } gpg_error_t ksba_cert_init_from_mem (ksba_cert_t cert, const void *buffer, size_t length) { gpg_error_t err; ksba_reader_t reader; err = ksba_reader_new (&reader); if (err) return err; err = ksba_reader_set_mem (reader, buffer, length); if (err) { ksba_reader_release (reader); return err; } err = ksba_cert_read_der (cert, reader); ksba_reader_release (reader); return err; } const unsigned char * ksba_cert_get_image (ksba_cert_t cert, size_t *r_length ) { AsnNode n; if (!cert) return NULL; if (!cert->initialized) return NULL; n = _ksba_asn_find_node (cert->root, "Certificate"); if (!n) return NULL; if (n->off == -1) { /* fputs ("ksba_cert_get_image problem at node:\n", stderr); */ /* _ksba_asn_node_dump_all (n, stderr); */ return NULL; } /* Due to minor problems in our parser we might hit the assertion below. Thus we better return a error, proper. */ if ( !(n->nhdr + n->len + n->off <= cert->imagelen) ) { fprintf (stderr,"\nOops, ksba_cert_get_image failed: " "imagelen=%lu hdr=%d len=%d off=%d\n", (unsigned long)cert->imagelen, n->nhdr, (int)n->len, n->off); return NULL; } /*assert (n->nhdr + n->len + n->off <= cert->imagelen);*/ if (r_length) *r_length = n->nhdr + n->len; return cert->image + n->off; } /* Check whether certificates A and B are identical and return o in this case. */ int _ksba_cert_cmp (ksba_cert_t a, ksba_cert_t b) { const unsigned char *img_a, *img_b; size_t len_a, len_b; img_a = ksba_cert_get_image (a, &len_a); if (!img_a) return 1; img_b = ksba_cert_get_image (b, &len_b); if (!img_b) return 1; return !(len_a == len_b && !memcmp (img_a, img_b, len_a)); } gpg_error_t ksba_cert_hash (ksba_cert_t cert, int what, void (*hasher)(void *, const void *, size_t length), void *hasher_arg) { AsnNode n; if (!cert /*|| !hasher*/) return gpg_error (GPG_ERR_INV_VALUE); if (!cert->initialized) return gpg_error (GPG_ERR_NO_DATA); n = _ksba_asn_find_node (cert->root, what == 1? "Certificate.tbsCertificate" : "Certificate"); if (!n) return gpg_error (GPG_ERR_NO_VALUE); /* oops - should be there */ if (n->off == -1) { /* fputs ("ksba_cert_hash problem at node:\n", stderr); */ /* _ksba_asn_node_dump_all (n, stderr); */ return gpg_error (GPG_ERR_NO_VALUE); } hasher (hasher_arg, cert->image + n->off, n->nhdr + n->len); return 0; } /** * ksba_cert_get_digest_algo: * @cert: Initialized certificate object * * Figure out the digest algorithm used for the signature and return * its OID. Note that in the case of rsaPSS the returned value is the * OID of rsaPSS (1.2.840.113549.1.1.10) and not the hash algorithm to * use. The hash algorithm needs to be extracted from the S-expression * returned by ksba_cert_get_sig_val. * * This function is intended as a helper for the ksba_cert_hash(). * * Return value: NULL for error otherwise a constant string with the OID. * This string is valid as long the certificate object is valid. **/ const char * ksba_cert_get_digest_algo (ksba_cert_t cert) { gpg_error_t err; AsnNode n; char *algo; size_t nread; if (!cert) return NULL; /* Ooops (can't set cert->last_error :-(). */ if (!cert->initialized) { cert->last_error = gpg_error (GPG_ERR_NO_DATA); return NULL; } if (cert->cache.digest_algo) return cert->cache.digest_algo; /* n = _ksba_asn_find_node (cert->root, */ /* "Certificate.signatureAlgorithm.algorithm"); */ /* algo = _ksba_oid_node_to_str (cert->image, n); */ /* if (!algo) */ /* cert->last_error = gpg_error (GPG_ERR_UNKNOWN_ALGORITHM); */ /* else */ /* cert->cache.digest_algo = algo; */ n = _ksba_asn_find_node (cert->root, "Certificate.signatureAlgorithm"); if (!n || n->off == -1) { algo = NULL; err = gpg_error (GPG_ERR_UNKNOWN_ALGORITHM); } else err = _ksba_parse_algorithm_identifier (cert->image + n->off, n->nhdr + n->len, &nread, &algo); if (err) cert->last_error = err; else cert->cache.digest_algo = algo; return algo; } /** * ksba_cert_get_serial: * @cert: certificate object * * This function returnes the serial number of the certificate. The * serial number is an integer returned as an canonical encoded * S-expression with just one element. * * Return value: An allocated S-Exp or NULL for no value. **/ ksba_sexp_t ksba_cert_get_serial (ksba_cert_t cert) { AsnNode n; char *p; char numbuf[22]; int numbuflen; if (!cert || !cert->initialized) return NULL; n = _ksba_asn_find_node (cert->root, "Certificate.tbsCertificate.serialNumber"); if (!n) return NULL; /* oops - should be there */ if (n->off == -1) { /* fputs ("get_serial problem at node:\n", stderr); */ /* _ksba_asn_node_dump_all (n, stderr); */ return NULL; } sprintf (numbuf,"(%u:", (unsigned int)n->len); numbuflen = strlen (numbuf); p = xtrymalloc (numbuflen + n->len + 2); if (!p) return NULL; strcpy (p, numbuf); memcpy (p+numbuflen, cert->image + n->off + n->nhdr, n->len); p[numbuflen + n->len] = ')'; p[numbuflen + n->len + 1] = 0; return p; } /* Return a pointer to the DER encoding of the serial number in CERT in PTR and the length of that field in LENGTH. */ gpg_error_t _ksba_cert_get_serial_ptr (ksba_cert_t cert, unsigned char const **ptr, size_t *length) { asn_node_t n; if (!cert || !cert->initialized || !ptr || !length) return gpg_error (GPG_ERR_INV_VALUE); n = _ksba_asn_find_node (cert->root, "Certificate.tbsCertificate.serialNumber"); if (!n || n->off == -1) return gpg_error (GPG_ERR_NO_VALUE); *ptr = cert->image + n->off; *length = n->nhdr + n->len; return 0; } /* Return a pointer to the DER encoding of the issuer's DN in CERT in PTR and the length of that object in LENGTH. */ gpg_error_t _ksba_cert_get_issuer_dn_ptr (ksba_cert_t cert, unsigned char const **ptr, size_t *length) { asn_node_t n; if (!cert || !cert->initialized || !ptr || !length) return gpg_error (GPG_ERR_INV_VALUE); n = _ksba_asn_find_node (cert->root, "Certificate.tbsCertificate.issuer"); if (!n || !n->down) return gpg_error (GPG_ERR_NO_VALUE); /* oops - should be there */ n = n->down; /* dereference the choice node */ if (n->off == -1) return gpg_error (GPG_ERR_NO_VALUE); *ptr = cert->image + n->off; *length = n->nhdr + n->len; return 0; } /* Return a pointer to the DER encoding of the subject's DN in CERT in PTR and the length of that object in LENGTH. */ gpg_error_t _ksba_cert_get_subject_dn_ptr (ksba_cert_t cert, unsigned char const **ptr, size_t *length) { asn_node_t n; if (!cert || !cert->initialized || !ptr || !length) return gpg_error (GPG_ERR_INV_VALUE); n = _ksba_asn_find_node (cert->root, "Certificate.tbsCertificate.subject"); if (!n || !n->down) return gpg_error (GPG_ERR_NO_VALUE); /* oops - should be there */ n = n->down; /* dereference the choice node */ if (n->off == -1) return gpg_error (GPG_ERR_NO_VALUE); *ptr = cert->image + n->off; *length = n->nhdr + n->len; return 0; } /* Worker function for get_isssuer and get_subject. */ static gpg_error_t get_name (ksba_cert_t cert, int idx, int use_subject, char **result) { gpg_error_t err; char *p; int i; const char *oid; struct tag_info ti; const unsigned char *der; size_t off, derlen, seqlen; if (!cert || !cert->initialized || !result) return gpg_error (GPG_ERR_INV_VALUE); if (idx < 0) return gpg_error (GPG_ERR_INV_INDEX); *result = NULL; if (!idx) { /* Get the required DN */ AsnNode n; n = _ksba_asn_find_node (cert->root, (use_subject? "Certificate.tbsCertificate.subject": "Certificate.tbsCertificate.issuer") ); if (!n || !n->down) return gpg_error (GPG_ERR_NO_VALUE); /* oops - should be there */ n = n->down; /* dereference the choice node */ if (n->off == -1) return gpg_error (GPG_ERR_NO_VALUE); err = _ksba_dn_to_str (cert->image, n, &p); if (err) return err; *result = p; return 0; } /* get {issuer,subject}AltName */ for (i=0; !(err=ksba_cert_get_extension (cert, i, &oid, NULL, &off, &derlen)); i++) { if (!strcmp (oid, (use_subject? oidstr_subjectAltName:oidstr_issuerAltName))) break; } if (err) return err; /* no alt name or error*/ der = cert->image + off; /* FIXME: We should use _ksba_name_new_from_der and ksba_name_enum here */ err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CERT_OBJ); if (ti.ndef) return gpg_error (GPG_ERR_NOT_DER_ENCODED); seqlen = ti.length; if (seqlen > derlen) return gpg_error (GPG_ERR_BAD_BER); if (!seqlen) return gpg_error (GPG_ERR_INV_CERT_OBJ); /* empty sequence is not allowed */ while (seqlen) { err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) return err; if (ti.class != CLASS_CONTEXT) return gpg_error (GPG_ERR_INV_CERT_OBJ); /* we expected a tag */ if (ti.ndef) return gpg_error (GPG_ERR_NOT_DER_ENCODED); if (seqlen < ti.nhdr) return gpg_error (GPG_ERR_BAD_BER); seqlen -= ti.nhdr; if (seqlen < ti.length) return gpg_error (GPG_ERR_BAD_BER); seqlen -= ti.length; if (derlen < ti.length) return gpg_error (GPG_ERR_BAD_BER); if (!(ti.tag == 1 || ti.tag == 2 || ti.tag == 6)) ; /* Not a supported tag: Do not change IDX. */ else if (--idx) ; /* not yet at the desired index */ else if (ti.tag == 1) { /* rfc822Name - this is an implicit IA5_STRING */ p = xtrymalloc (ti.length+3); if (!p) return gpg_error (GPG_ERR_ENOMEM); *p = '<'; memcpy (p+1, der, ti.length); p[ti.length+1] = '>'; p[ti.length+2] = 0; *result = p; return 0; } else if (ti.tag == 2 || ti.tag == 6) { /* dNSName or URI - this are implicit IA5_STRINGs */ char numbuf[20], *numbufp; size_t numbuflen; numbufp = smklen (numbuf, DIM(numbuf), ti.length, &numbuflen); p = xtrymalloc (11 + numbuflen + ti.length + 3); if (!p) return gpg_error (GPG_ERR_ENOMEM); *result = p; p = stpcpy (p, ti.tag == 2? "(8:dns-name" : "(3:uri"); p = stpcpy (p, numbufp); memcpy (p, der, ti.length); p += ti.length; *p++ = ')'; *p = 0; return 0; } /* advance pointer */ der += ti.length; derlen -= ti.length; } return gpg_error (GPG_ERR_EOF); } /** * ksba_cert_get_issuer: * @cert: certificate object * * With @idx == 0 this function returns the Distinguished Name (DN) of * the certificate issuer which in most cases is a CA. The format of * the returned string is in accordance with RFC-2253. NULL is * returned if the DN is not available which is an error and should * have been catched by the certificate reading function. * * With @idx > 0 the function may be used to enumerate alternate * issuer names. The function returns NULL if there are no more * alternate names. The function does only return alternate names * which are recognized by libksba and ignores others. The format of * the returned name is either a RFC-2253 formated one which can be * detected by checking whether the first character is letter or * digit. rfc-2822 conform email addresses are returned enclosed in * angle brackets, the opening angle bracket should be used to * indicate this. Other formats are returned as an S-Expression in * canonical format, so a opening parenthesis may be used to detect * this encoding, the name may include binary null characters, so * strlen may return a length shorther than actually used, the real * length is implictly given by the structure of the S-Exp, an extra * null is appended for safety reasons. * * The caller must free the returned string using ksba_free() or the * function he has registered as a replacement. * * Return value: An allocated string or NULL for error. **/ char * ksba_cert_get_issuer (ksba_cert_t cert, int idx) { gpg_error_t err; char *name; err = get_name (cert, idx, 0, &name); if (err) { cert->last_error = err; return NULL; } return name; } /* See ..get_issuer */ char * ksba_cert_get_subject (ksba_cert_t cert, int idx) { gpg_error_t err; char *name; err = get_name (cert, idx, 1, &name); if (err) { cert->last_error = err; return NULL; } return name; } /** * ksba_cert_get_valididy: * @cert: certificate object * @what: 0 for notBefore, 1 for notAfter * @timebuf: Returns the time. * * Return the validity object from the certificate. If no value is * available 0 is returned because we can safely assume that this is * not a valid date. * * Return value: The time value an 0 or an error code. **/ gpg_error_t ksba_cert_get_validity (ksba_cert_t cert, int what, ksba_isotime_t timebuf) { AsnNode n, n2; if (!cert || what < 0 || what > 1) return gpg_error (GPG_ERR_INV_VALUE); *timebuf = 0; if (!cert->initialized) return gpg_error (GPG_ERR_NO_DATA); n = _ksba_asn_find_node (cert->root, what == 0? "Certificate.tbsCertificate.validity.notBefore" : "Certificate.tbsCertificate.validity.notAfter"); if (!n) return 0; /* no value available */ /* Fixme: We should remove the choice node and don't use this ugly hack */ for (n2=n->down; n2; n2 = n2->right) { if ((n2->type == TYPE_UTC_TIME || n2->type == TYPE_GENERALIZED_TIME) && n2->off != -1) break; } n = n2; if (!n) return 0; /* no value available */ return_val_if_fail (n->off != -1, gpg_error (GPG_ERR_BUG)); return _ksba_asntime_to_iso (cert->image + n->off + n->nhdr, n->len, n->type == TYPE_UTC_TIME, timebuf); } ksba_sexp_t ksba_cert_get_public_key (ksba_cert_t cert) { AsnNode n; gpg_error_t err; ksba_sexp_t string; if (!cert) return NULL; if (!cert->initialized) return NULL; n = _ksba_asn_find_node (cert->root, "Certificate" ".tbsCertificate.subjectPublicKeyInfo"); if (!n) { cert->last_error = gpg_error (GPG_ERR_NO_VALUE); return NULL; } err = _ksba_keyinfo_to_sexp (cert->image + n->off, n->nhdr + n->len, &string); if (err) { cert->last_error = err; return NULL; } return string; } /* Return a pointer to the DER encoding of the actual public key (i.e. the bit string) in PTR and the length of that object in LENGTH. */ gpg_error_t _ksba_cert_get_public_key_ptr (ksba_cert_t cert, unsigned char const **ptr, size_t *length) { asn_node_t n; if (!cert || !cert->initialized || !ptr || !length) return gpg_error (GPG_ERR_INV_VALUE); n = _ksba_asn_find_node (cert->root, "Certificate.tbsCertificate.subjectPublicKeyInfo"); if (!n || !n->down || !n->down->right) return gpg_error (GPG_ERR_NO_VALUE); /* oops - should be there */ n = n->down->right; if (n->off == -1) return gpg_error (GPG_ERR_NO_VALUE); *ptr = cert->image + n->off + n->nhdr; *length = n->len; /* Somehow we end up at the preceding NULL value, and not at a sequence, we hack it way here. */ if (*length && !**ptr) { (*length)--; (*ptr)++; } return 0; } ksba_sexp_t ksba_cert_get_sig_val (ksba_cert_t cert) { AsnNode n, n2; gpg_error_t err; ksba_sexp_t string; if (!cert) return NULL; if (!cert->initialized) return NULL; n = _ksba_asn_find_node (cert->root, "Certificate.signatureAlgorithm"); if (!n) { cert->last_error = gpg_error (GPG_ERR_NO_VALUE); return NULL; } if (n->off == -1) { /* fputs ("ksba_cert_get_sig_val problem at node:\n", stderr); */ /* _ksba_asn_node_dump_all (n, stderr); */ cert->last_error = gpg_error (GPG_ERR_NO_VALUE); return NULL; } n2 = n->right; err = _ksba_sigval_to_sexp (cert->image + n->off, n->nhdr + n->len + ((!n2||n2->off == -1)? 0:(n2->nhdr+n2->len)), &string); if (err) { cert->last_error = err; return NULL; } return string; } /* Read all extensions into the cache */ static gpg_error_t read_extensions (ksba_cert_t cert) { AsnNode start, n; int count; assert (!cert->cache.extns_valid); assert (!cert->cache.extns); start = _ksba_asn_find_node (cert->root, "Certificate.tbsCertificate.extensions.."); for (count=0, n=start; n; n = n->right) count++; if (!count) { cert->cache.n_extns = 0; cert->cache.extns_valid = 1; return 0; /* no extensions at all */ } cert->cache.extns = xtrycalloc (count, sizeof *cert->cache.extns); if (!cert->cache.extns) return gpg_error (GPG_ERR_ENOMEM); cert->cache.n_extns = count; { for (count=0; start; start = start->right, count++) { n = start->down; if (!n || n->type != TYPE_OBJECT_ID) goto no_value; cert->cache.extns[count].oid = _ksba_oid_node_to_str (cert->image, n); if (!cert->cache.extns[count].oid) goto no_value; n = n->right; if (n && n->type == TYPE_BOOLEAN) { if (n->off != -1 && n->len && cert->image[n->off + n->nhdr]) cert->cache.extns[count].crit = 1; n = n->right; } if (!n || n->type != TYPE_OCTET_STRING || n->off == -1) goto no_value; cert->cache.extns[count].off = n->off + n->nhdr; cert->cache.extns[count].len = n->len; } assert (count == cert->cache.n_extns); cert->cache.extns_valid = 1; return 0; no_value: for (count=0; count < cert->cache.n_extns; count++) xfree (cert->cache.extns[count].oid); xfree (cert->cache.extns); cert->cache.extns = NULL; return gpg_error (GPG_ERR_NO_VALUE); } } /* Return information about the IDX nth extension */ gpg_error_t ksba_cert_get_extension (ksba_cert_t cert, int idx, char const **r_oid, int *r_crit, size_t *r_deroff, size_t *r_derlen) { gpg_error_t err; if (!cert) return gpg_error (GPG_ERR_INV_VALUE); if (!cert->initialized) return gpg_error (GPG_ERR_NO_DATA); if (!cert->cache.extns_valid) { err = read_extensions (cert); if (err) return err; assert (cert->cache.extns_valid); } if (idx == cert->cache.n_extns) return gpg_error (GPG_ERR_EOF); /* No more extensions. */ if (idx < 0 || idx >= cert->cache.n_extns) return gpg_error (GPG_ERR_INV_INDEX); if (r_oid) *r_oid = cert->cache.extns[idx].oid; if (r_crit) *r_crit = cert->cache.extns[idx].crit; if (r_deroff) *r_deroff = cert->cache.extns[idx].off; if (r_derlen) *r_derlen = cert->cache.extns[idx].len; return 0; } /* Return information on the basicConstraint (2.5.19.19) of CERT. R_CA receives true if this is a CA and only in that case R_PATHLEN is set to the maximim certification path length or -1 if there is nosuch limitation */ gpg_error_t ksba_cert_is_ca (ksba_cert_t cert, int *r_ca, int *r_pathlen) { gpg_error_t err; const char *oid; int idx, crit; size_t off, derlen, seqlen; const unsigned char *der; struct tag_info ti; unsigned long value; /* set default values */ if (r_ca) *r_ca = 0; if (r_pathlen) *r_pathlen = -1; for (idx=0; !(err=ksba_cert_get_extension (cert, idx, &oid, &crit, &off, &derlen)); idx++) { if (!strcmp (oid, oidstr_basicConstraints)) break; } if (gpg_err_code (err) == GPG_ERR_EOF) return 0; /* no such constraint */ if (err) return err; /* check that there is only one */ for (idx++; !(err=ksba_cert_get_extension (cert, idx, &oid, NULL, NULL, NULL)); idx++) { if (!strcmp (oid, oidstr_basicConstraints)) return gpg_error (GPG_ERR_DUP_VALUE); } der = cert->image + off; err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CERT_OBJ); if (ti.ndef) return gpg_error (GPG_ERR_NOT_DER_ENCODED); seqlen = ti.length; if (seqlen > derlen) return gpg_error (GPG_ERR_BAD_BER); if (!seqlen) return 0; /* an empty sequence is allowed because both elements are optional */ err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) return err; if (seqlen < ti.nhdr) return gpg_error (GPG_ERR_BAD_BER); seqlen -= ti.nhdr; if (seqlen < ti.length) return gpg_error (GPG_ERR_BAD_BER); seqlen -= ti.length; if (ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_BOOLEAN) { if (ti.length != 1) return gpg_error (GPG_ERR_ENCODING_PROBLEM); if (r_ca) *r_ca = !!*der; der++; derlen--; if (!seqlen) return 0; /* ready (no pathlength) */ err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) return err; if (seqlen < ti.nhdr) return gpg_error (GPG_ERR_BAD_BER); seqlen -= ti.nhdr; if (seqlen < ti.length) return gpg_error (GPG_ERR_BAD_BER); seqlen -= ti.length; } if (!(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_INTEGER)) return gpg_error (GPG_ERR_INV_CERT_OBJ); for (value=0; ti.length; ti.length--) { value <<= 8; value |= (*der++) & 0xff; derlen--; } if (r_pathlen) *r_pathlen = value; /* if the extension is marked as critical and any stuff is still left we better return an error */ if (crit && seqlen) return gpg_error (GPG_ERR_INV_CERT_OBJ); return 0; } /* Get the key usage flags. The function returns Ksba_No_Data if no key usage is specified. */ gpg_error_t ksba_cert_get_key_usage (ksba_cert_t cert, unsigned int *r_flags) { gpg_error_t err; const char *oid; int idx, crit; size_t off, derlen; const unsigned char *der; struct tag_info ti; unsigned int bits, mask; int i, unused, full; if (!r_flags) return gpg_error (GPG_ERR_INV_VALUE); *r_flags = 0; for (idx=0; !(err=ksba_cert_get_extension (cert, idx, &oid, &crit, &off, &derlen)); idx++) { if (!strcmp (oid, oidstr_keyUsage)) break; } if (gpg_err_code (err) == GPG_ERR_EOF || gpg_err_code (err) == GPG_ERR_NO_VALUE) return gpg_error (GPG_ERR_NO_DATA); /* no key usage */ if (err) return err; /* check that there is only one */ for (idx++; !(err=ksba_cert_get_extension (cert, idx, &oid, NULL, NULL, NULL)); idx++) { if (!strcmp (oid, oidstr_keyUsage)) return gpg_error (GPG_ERR_DUP_VALUE); } der = cert->image + off; err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_BIT_STRING && !ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CERT_OBJ); if (ti.ndef) return gpg_error (GPG_ERR_NOT_DER_ENCODED); if (!ti.length || ti.length > derlen) return gpg_error (GPG_ERR_ENCODING_PROBLEM); /* number of unused bits missing */ unused = *der++; derlen--; ti.length--; if ((!ti.length && unused) || unused/8 > ti.length) return gpg_error (GPG_ERR_ENCODING_PROBLEM); full = ti.length - (unused+7)/8; unused %= 8; mask = 0; for (i=1; unused; i <<= 1, unused--) mask |= i; /* the first octet */ if (!ti.length) return 0; /* no bits set */ bits = *der++; derlen--; ti.length--; if (full) full--; else { bits &= ~mask; mask = 0; } if (bits & 0x80) *r_flags |= KSBA_KEYUSAGE_DIGITAL_SIGNATURE; if (bits & 0x40) *r_flags |= KSBA_KEYUSAGE_NON_REPUDIATION; if (bits & 0x20) *r_flags |= KSBA_KEYUSAGE_KEY_ENCIPHERMENT; if (bits & 0x10) *r_flags |= KSBA_KEYUSAGE_DATA_ENCIPHERMENT; if (bits & 0x08) *r_flags |= KSBA_KEYUSAGE_KEY_AGREEMENT; if (bits & 0x04) *r_flags |= KSBA_KEYUSAGE_KEY_CERT_SIGN; if (bits & 0x02) *r_flags |= KSBA_KEYUSAGE_CRL_SIGN; if (bits & 0x01) *r_flags |= KSBA_KEYUSAGE_ENCIPHER_ONLY; /* the second octet */ if (!ti.length) return 0; /* no bits set */ bits = *der++; derlen--; ti.length--; if (full) full--; else { bits &= mask; mask = ~0; } if (bits & 0x80) *r_flags |= KSBA_KEYUSAGE_DECIPHER_ONLY; return 0; } /* Note, that this helper is also used for ext_key_usage. */ static gpg_error_t append_cert_policy (char **policies, const char *oid, int crit) { char *p; if (!*policies) { *policies = xtrymalloc (strlen (oid) + 4); if (!*policies) return gpg_error (GPG_ERR_ENOMEM); p = *policies; } else { char *tmp = xtryrealloc (*policies, strlen(*policies) + 1 + strlen (oid) + 4); if (!tmp) return gpg_error (GPG_ERR_ENOMEM); *policies = tmp; p = stpcpy (tmp+strlen (tmp), "\n");; } strcpy (stpcpy (p, oid), crit? ":C:": ":N:"); return 0; } /* Return a string with the certificatePolicies delimited by linefeeds. The return values may be extended to carry more information er line, so the caller should only use the first white-space delimited token per line. The function returns GPG_ERR_NO_DATA when this extension is not used. Caller must free the returned value. */ gpg_error_t ksba_cert_get_cert_policies (ksba_cert_t cert, char **r_policies) { gpg_error_t err; const char *oid; int idx, crit; size_t off, derlen, seqlen; const unsigned char *der; struct tag_info ti; if (!cert || !r_policies) return gpg_error (GPG_ERR_INV_VALUE); *r_policies = NULL; for (idx=0; !(err=ksba_cert_get_extension (cert, idx, &oid, &crit, &off, &derlen)); idx++) { if (!strcmp (oid, oidstr_certificatePolicies)) { char *suboid; der = cert->image + off; err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) goto leave; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed) ) { err = gpg_error (GPG_ERR_INV_CERT_OBJ); goto leave; } if (ti.ndef) { err = gpg_error (GPG_ERR_NOT_DER_ENCODED); goto leave; } seqlen = ti.length; if (seqlen > derlen) { err = gpg_error (GPG_ERR_BAD_BER); goto leave; } while (seqlen) { size_t seqseqlen; err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) goto leave; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed) ) { err = gpg_error (GPG_ERR_INV_CERT_OBJ); goto leave; } if (ti.ndef) { err = gpg_error (GPG_ERR_NOT_DER_ENCODED); goto leave; } if (ti.length > derlen) { err = gpg_error (GPG_ERR_BAD_BER); goto leave; } if (!ti.length) { /* We do not accept an empty inner SEQ */ err = gpg_error (GPG_ERR_INV_CERT_OBJ); goto leave; } if (ti.nhdr+ti.length > seqlen) { err = gpg_error (GPG_ERR_BAD_BER); goto leave; } seqlen -= ti.nhdr + ti.length; seqseqlen = ti.length; err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) goto leave; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_OBJECT_ID)) { err = gpg_error (GPG_ERR_INV_CERT_OBJ); goto leave; } if (ti.length > derlen) { err = gpg_error (GPG_ERR_BAD_BER); goto leave; } if (ti.nhdr+ti.length > seqseqlen) { err = gpg_error (GPG_ERR_BAD_BER); goto leave; } seqseqlen -= ti.nhdr; suboid = ksba_oid_to_str (der, ti.length); if (!suboid) { err = gpg_error (GPG_ERR_ENOMEM); goto leave; } der += ti.length; derlen -= ti.length; seqseqlen -= ti.length; err = append_cert_policy (r_policies, suboid, crit); xfree (suboid); if (err) goto leave; /* skip the rest of the seq which is more or less optional */ der += seqseqlen; derlen -= seqseqlen; } } } if (gpg_err_code (err) == GPG_ERR_EOF) err = 0; if (!*r_policies || gpg_err_code (err) == GPG_ERR_NO_VALUE) err = gpg_error (GPG_ERR_NO_DATA); leave: if (err) { xfree (*r_policies); *r_policies = NULL; } return err; } /* Return a string with the extendedKeyUsageOIDs delimited by linefeeds. The return values may be extended to carry more information per line, so the caller should only use the first white-space delimited token per line. The function returns GPG_ERR_NO_DATA when this extension is not used. Caller must free the returned value. */ gpg_error_t ksba_cert_get_ext_key_usages (ksba_cert_t cert, char **result) { gpg_error_t err; const char *oid; int idx, crit; size_t off, derlen; const unsigned char *der; struct tag_info ti; if (!cert || !result) return gpg_error (GPG_ERR_INV_VALUE); *result = NULL; for (idx=0; !(err=ksba_cert_get_extension (cert, idx, &oid, &crit, &off, &derlen)); idx++) { if (!strcmp (oid, oidstr_extKeyUsage)) { char *suboid; der = cert->image + off; err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) goto leave; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed) ) { err = gpg_error (GPG_ERR_INV_CERT_OBJ); goto leave; } if (ti.ndef) { err = gpg_error (GPG_ERR_NOT_DER_ENCODED); goto leave; } if (ti.length > derlen) { err = gpg_error (GPG_ERR_BAD_BER); goto leave; } while (derlen) { err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) goto leave; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_OBJECT_ID)) { err = gpg_error (GPG_ERR_INV_CERT_OBJ); goto leave; } if (ti.ndef) { err = gpg_error (GPG_ERR_NOT_DER_ENCODED); goto leave; } if (ti.length > derlen) { err = gpg_error (GPG_ERR_BAD_BER); goto leave; } suboid = ksba_oid_to_str (der, ti.length); if (!suboid) { err = gpg_error (GPG_ERR_ENOMEM); goto leave; } der += ti.length; derlen -= ti.length; err = append_cert_policy (result, suboid, crit); xfree (suboid); if (err) goto leave; } } } if (gpg_err_code (err) == GPG_ERR_EOF) err = 0; if (!*result || gpg_err_code (err) == GPG_ERR_NO_VALUE) err = gpg_error (GPG_ERR_NO_DATA); leave: if (err) { xfree (*result); *result = NULL; } return err; } /* Helper function for ksba_cert_get_crl_dist_point */ static gpg_error_t parse_distribution_point (const unsigned char *der, size_t derlen, ksba_name_t *distpoint, ksba_name_t *issuer, ksba_crl_reason_t *reason) { gpg_error_t err; struct tag_info ti; err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) return err; if (ti.class != CLASS_CONTEXT) return gpg_error (GPG_ERR_INV_CERT_OBJ); /* we expected a tag */ if (ti.ndef) return gpg_error (GPG_ERR_NOT_DER_ENCODED); if (derlen < ti.length) return gpg_error (GPG_ERR_BAD_BER); if (ti.tag == 0 && derlen) { /* distributionPointName */ err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) return err; if (ti.class != CLASS_CONTEXT) return gpg_error (GPG_ERR_INV_CERT_OBJ); /* we expected a tag */ if (ti.ndef) return gpg_error (GPG_ERR_NOT_DER_ENCODED); if (derlen < ti.nhdr) return gpg_error (GPG_ERR_BAD_BER); if (derlen < ti.length) return gpg_error (GPG_ERR_BAD_BER); if (ti.tag == 0) { if (distpoint) { err = _ksba_name_new_from_der (distpoint, der, ti.length); if (err) return err; } } else { /* We don't support nameRelativeToCRLIssuer yet*/ } der += ti.length; derlen -= ti.length; if (!derlen) return 0; /* read the next optional element */ err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) return err; if (ti.class != CLASS_CONTEXT) return gpg_error (GPG_ERR_INV_CERT_OBJ); /* we expected a tag */ if (ti.ndef) return gpg_error (GPG_ERR_NOT_DER_ENCODED); if (derlen < ti.length) return gpg_error (GPG_ERR_BAD_BER); } if (ti.tag == 1 && derlen) { /* reasonFlags */ unsigned int bits, mask; int i, unused, full; if (!ti.length || ti.length > derlen) return gpg_error (GPG_ERR_ENCODING_PROBLEM); unused = *der++; derlen--; ti.length--; if ((!ti.length && unused) || unused/8 > ti.length) return gpg_error (GPG_ERR_ENCODING_PROBLEM); full = ti.length - (unused+7)/8; unused %= 8; mask = 0; for (i=1; unused; i <<= 1, unused--) mask |= i; /* we are only required to look at the first octect */ if (ti.length && reason) { bits = *der; if (full) full--; else { bits &= ~mask; mask = 0; } if (bits & 0x80) *reason |= KSBA_CRLREASON_UNSPECIFIED; if (bits & 0x40) *reason |= KSBA_CRLREASON_KEY_COMPROMISE; if (bits & 0x20) *reason |= KSBA_CRLREASON_CA_COMPROMISE; if (bits & 0x10) *reason |= KSBA_CRLREASON_AFFILIATION_CHANGED; if (bits & 0x08) *reason |= KSBA_CRLREASON_SUPERSEDED; if (bits & 0x04) *reason |= KSBA_CRLREASON_CESSATION_OF_OPERATION; if (bits & 0x02) *reason |= KSBA_CRLREASON_CERTIFICATE_HOLD; } der += ti.length; derlen -= ti.length; if (!derlen) return 0; /* read the next optional element */ err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) return err; if (ti.class != CLASS_CONTEXT) return gpg_error (GPG_ERR_INV_CERT_OBJ); /* we expected a tag */ if (ti.ndef) return gpg_error (GPG_ERR_NOT_DER_ENCODED); if (derlen < ti.length) return gpg_error (GPG_ERR_BAD_BER); } if (ti.tag == 2 && derlen) { /* crlIssuer */ if (issuer) { err = _ksba_name_new_from_der (issuer, der, ti.length); if (err) return err; } der += ti.length; derlen -= ti.length; } if (derlen) return gpg_error (GPG_ERR_INV_CERT_OBJ); return 0; } /* Return the CRLDistPoints given in the cert extension. IDX should be iterated started from 0 until the function returns -1. R_DISTPOINT returns a ksba_name_t object with the distribution point name(s) the return value may be NULL to indicate that this name is not available. R_ISSUER returns the CRL issuer; if the returned value is NULL the caller should assume that the CRL issuer is the same as the certificate issuer. R_REASON returns the reason for the CRL. This is a bit encoded value with no bit set if this has not been specified in the cert. The caller may pass NULL to any of the pointer arguments if he is not interested in this value. The return values for R_DISTPOINT and R_ISSUER must be released by the caller using ksba_name_release(). */ gpg_error_t ksba_cert_get_crl_dist_point (ksba_cert_t cert, int idx, ksba_name_t *r_distpoint, ksba_name_t *r_issuer, ksba_crl_reason_t *r_reason) { gpg_error_t err; const char *oid; size_t off, derlen; int myidx, crit; if (r_distpoint) *r_distpoint = NULL; if (r_issuer) *r_issuer = NULL; if (r_reason) *r_reason = 0; for (myidx=0; !(err=ksba_cert_get_extension (cert, myidx, &oid, &crit, &off, &derlen)); myidx++) { if (!strcmp (oid, oidstr_crlDistributionPoints)) { const unsigned char *der; struct tag_info ti; size_t seqlen; der = cert->image + off; err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CERT_OBJ); if (ti.ndef) return gpg_error (GPG_ERR_NOT_DER_ENCODED); seqlen = ti.length; if (seqlen > derlen) return gpg_error (GPG_ERR_BAD_BER); /* Note: an empty sequence is actually not allowed but we better don't care */ while (seqlen) { err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CERT_OBJ); if (derlen < ti.length) return gpg_error (GPG_ERR_BAD_BER); if (seqlen < ti.nhdr) return gpg_error (GPG_ERR_BAD_BER); seqlen -= ti.nhdr; if (seqlen < ti.length) return gpg_error (GPG_ERR_BAD_BER); if (idx) { /* skip because we are not yet at the desired index */ der += ti.length; derlen -= ti.length; seqlen -= ti.length; idx--; continue; } if (!ti.length) return 0; err = parse_distribution_point (der, ti.length, r_distpoint, r_issuer, r_reason); if (err && r_distpoint) { ksba_name_release (*r_distpoint); *r_distpoint = NULL; } if (err && r_issuer) { ksba_name_release (*r_issuer); *r_issuer = NULL; } if (err && r_reason) *r_reason = 0; return err; } } } return err; } /* Return the authorityKeyIdentifier in R_NAME and R_SERIAL or/and in R_KEYID. GPG_ERR_NO_DATA is returned if no authorityKeyIdentifier or only one using the keyIdentifier method is available and R_KEYID is NULL. */ gpg_error_t ksba_cert_get_auth_key_id (ksba_cert_t cert, ksba_sexp_t *r_keyid, ksba_name_t *r_name, ksba_sexp_t *r_serial) { gpg_error_t err; const char *oid; size_t off, derlen; const unsigned char *der; const unsigned char *keyid_der = NULL; size_t keyid_derlen = 0; int idx, crit; struct tag_info ti; char numbuf[30]; size_t numbuflen; if (r_keyid) *r_keyid = NULL; if (!r_name || !r_serial) return gpg_error (GPG_ERR_INV_VALUE); *r_name = NULL; *r_serial = NULL; for (idx=0; !(err=ksba_cert_get_extension (cert, idx, &oid, &crit, &off, &derlen)); idx++) { if (!strcmp (oid, oidstr_authorityKeyIdentifier)) break; } if (gpg_err_code (err) == GPG_ERR_EOF || gpg_err_code (err) == GPG_ERR_NO_VALUE) return gpg_error (GPG_ERR_NO_DATA); /* not available */ if (err) return err; /* check that there is only one */ for (idx++; !(err=ksba_cert_get_extension (cert, idx, &oid, NULL, NULL, NULL)); idx++) { if (!strcmp (oid, oidstr_authorityKeyIdentifier)) return gpg_error (GPG_ERR_DUP_VALUE); } der = cert->image + off; err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CERT_OBJ); if (ti.ndef) return gpg_error (GPG_ERR_NOT_DER_ENCODED); if (ti.length > derlen) return gpg_error (GPG_ERR_BAD_BER); err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) return err; if (ti.class != CLASS_CONTEXT) return gpg_error (GPG_ERR_INV_CERT_OBJ); /* we expected a tag */ if (ti.ndef) return gpg_error (GPG_ERR_NOT_DER_ENCODED); if (derlen < ti.length) return gpg_error (GPG_ERR_BAD_BER); if (ti.tag == 0) { /* keyIdentifier: Save it away and skip over it. */ keyid_der = der; keyid_derlen = ti.length; der += ti.length; derlen -= ti.length; /* If the keyid has been requested but no other data follows, we directly jump to the end. */ if (r_keyid && !derlen) goto build_keyid; if (!derlen) return gpg_error (GPG_ERR_NO_DATA); /* not available */ err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) return err; if (ti.class != CLASS_CONTEXT) return gpg_error (GPG_ERR_INV_CERT_OBJ); /* we expected a tag */ if (ti.ndef) return gpg_error (GPG_ERR_NOT_DER_ENCODED); if (derlen < ti.length) return gpg_error (GPG_ERR_BAD_BER); } if (ti.tag != 1 || !derlen) return gpg_error (GPG_ERR_INV_CERT_OBJ); err = _ksba_name_new_from_der (r_name, der, ti.length); if (err) return err; der += ti.length; derlen -= ti.length; /* fixme: we should release r_name before returning on error */ err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) return err; if (ti.class != CLASS_CONTEXT) return gpg_error (GPG_ERR_INV_CERT_OBJ); /* we expected a tag */ if (ti.ndef) return gpg_error (GPG_ERR_NOT_DER_ENCODED); if (derlen < ti.length) return gpg_error (GPG_ERR_BAD_BER); if (ti.tag != 2 || !derlen) return gpg_error (GPG_ERR_INV_CERT_OBJ); sprintf (numbuf,"(%u:", (unsigned int)ti.length); numbuflen = strlen (numbuf); *r_serial = xtrymalloc (numbuflen + ti.length + 2); if (!*r_serial) return gpg_error (GPG_ERR_ENOMEM); strcpy (*r_serial, numbuf); memcpy (*r_serial+numbuflen, der, ti.length); (*r_serial)[numbuflen + ti.length] = ')'; (*r_serial)[numbuflen + ti.length + 1] = 0; build_keyid: if (r_keyid && keyid_der && keyid_derlen) { sprintf (numbuf,"(%u:", (unsigned int)keyid_derlen); numbuflen = strlen (numbuf); *r_keyid = xtrymalloc (numbuflen + keyid_derlen + 2); if (!*r_keyid) return gpg_error (GPG_ERR_ENOMEM); strcpy (*r_keyid, numbuf); memcpy (*r_keyid+numbuflen, keyid_der, keyid_derlen); (*r_keyid)[numbuflen + keyid_derlen] = ')'; (*r_keyid)[numbuflen + keyid_derlen + 1] = 0; } return 0; } /* Return a simple octet string extension at the object identifier OID from certificate CERT. The data is return as a simple S-expression and stored at R_DATA. Returns 0 on success or an error code. common error codes are: GPG_ERR_NO_DATA if no such extension is available, GPG_ERR_DUP_VALUE if more than one is available. If R_CRIT is not NULL, the critical extension flag will be stored at that address. */ static gpg_error_t get_simple_octet_string_ext (ksba_cert_t cert, const char *oid, int *r_crit, ksba_sexp_t *r_data) { gpg_error_t err; const char *tmpoid; size_t off, derlen; const unsigned char *der; int idx, crit; struct tag_info ti; char numbuf[30]; size_t numbuflen; if (!r_data) return gpg_error (GPG_ERR_INV_VALUE); *r_data = NULL; for (idx=0; !(err=ksba_cert_get_extension (cert, idx, &tmpoid, &crit, &off, &derlen)); idx++) { if (!strcmp (tmpoid, oid)) break; } if (err) { if (gpg_err_code (err) == GPG_ERR_EOF || gpg_err_code (err) == GPG_ERR_NO_VALUE) return gpg_error (GPG_ERR_NO_DATA); return err; } /* Check that there is only one */ for (idx++; !(err=ksba_cert_get_extension (cert, idx, &tmpoid, NULL, NULL, NULL)); idx++) { if (!strcmp (tmpoid, oid)) return gpg_error (GPG_ERR_DUP_VALUE); } der = cert->image + off; err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_OCTET_STRING && !ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CERT_OBJ); if (ti.ndef) return gpg_error (GPG_ERR_NOT_DER_ENCODED); if (ti.length > derlen) return gpg_error (GPG_ERR_BAD_BER); if (ti.length != derlen) return gpg_error (GPG_ERR_INV_CERT_OBJ); /* Garbage follows. */ sprintf (numbuf,"(%u:", (unsigned int)ti.length); numbuflen = strlen (numbuf); *r_data = xtrymalloc (numbuflen + ti.length + 2); if (!*r_data) return gpg_error (GPG_ERR_ENOMEM); strcpy (*r_data, numbuf); memcpy (*r_data+numbuflen, der, ti.length); (*r_data)[numbuflen + ti.length] = ')'; (*r_data)[numbuflen + ti.length + 1] = 0; if (r_crit) *r_crit = crit; return 0; } /* Return the subjectKeyIdentifier extension as a simple allocated S-expression at the address of R_KEYID. 0 is returned on success, GPG_ERR_NO_DATA if no such extension is available or any other - error code. If R_CRIT is not passed as NULL, the criticla flag of + error code. If R_CRIT is not passed as NULL, the critical flag of this is extension is stored there. */ gpg_error_t ksba_cert_get_subj_key_id (ksba_cert_t cert, int *r_crit, ksba_sexp_t *r_keyid) { return get_simple_octet_string_ext (cert, oidstr_subjectKeyIdentifier, r_crit, r_keyid); } /* MODE 0 := authorityInfoAccess 1 := subjectInfoAccess Caller must release METHOD and LOCATION if the function returned with success; on error both variables will point to NULL. */ static gpg_error_t get_info_access (ksba_cert_t cert, int idx, int mode, char **method, ksba_name_t *location) { gpg_error_t err; const char *oid; size_t off, derlen; int myidx, crit; *method = NULL; *location = NULL; if (!cert || !cert->initialized) return gpg_error (GPG_ERR_INV_VALUE); if (idx < 0) return gpg_error (GPG_ERR_INV_INDEX); for (myidx=0; !(err=ksba_cert_get_extension (cert, myidx, &oid, &crit, &off, &derlen)); myidx++) { if (!strcmp (oid,(mode == 0)? oidstr_authorityInfoAccess : oidstr_subjectInfoAccess) ) { const unsigned char *der; struct tag_info ti; size_t seqlen; der = cert->image + off; /* What we are going to parse is: * * AuthorityInfoAccessSyntax ::= * SEQUENCE SIZE (1..MAX) OF AccessDescription * * AccessDescription ::= SEQUENCE { * accessMethod OBJECT IDENTIFIER, * accessLocation GeneralName } */ err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CERT_OBJ); if (ti.ndef) return gpg_error (GPG_ERR_NOT_DER_ENCODED); seqlen = ti.length; if (seqlen > derlen) return gpg_error (GPG_ERR_BAD_BER); /* Note: an empty sequence is actually not allowed but we better don't care. */ while (seqlen) { err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CERT_OBJ); if (derlen < ti.length) return gpg_error (GPG_ERR_BAD_BER); if (seqlen < ti.nhdr) return gpg_error (GPG_ERR_BAD_BER); seqlen -= ti.nhdr; if (seqlen < ti.length) return gpg_error (GPG_ERR_BAD_BER); if (idx) { /* Skip because we are not yet at the desired index. */ der += ti.length; derlen -= ti.length; seqlen -= ti.length; idx--; continue; } /* We only need the next object, thus we can (and actually need to) limit the DERLEN to the length of the current sequence. */ derlen = ti.length; if (!derlen) return gpg_error (GPG_ERR_INV_CERT_OBJ); err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_OBJECT_ID && !ti.is_constructed)) return gpg_error (GPG_ERR_INV_CERT_OBJ); if (ti.ndef) return gpg_error (GPG_ERR_NOT_DER_ENCODED); if (derlen < ti.length) return gpg_error (GPG_ERR_BAD_BER); *method = ksba_oid_to_str (der, ti.length); if (!*method) return gpg_error (GPG_ERR_ENOMEM); der += ti.length; derlen -= ti.length; err = _ksba_name_new_from_der (location, der, derlen); if (err) { ksba_free (*method); *method = NULL; return err; } return 0; } } } return err; } /* Return the authorityInfoAccess attributes. IDX should be iterated starting from 0 until the function returns GPG_ERR_EOF. R_METHOD returns an allocated string with the OID of one item and R_LOCATION return the GeneralName for that OID. The return values for R_METHOD and R_LOCATION must be released by the caller unless the function returned an error; the function will however make sure that R_METHOD and R_LOCATION will point to NULL if the function returns an error. See RFC 5280, section 4.2.2.1 */ gpg_error_t ksba_cert_get_authority_info_access (ksba_cert_t cert, int idx, char **r_method, ksba_name_t *r_location) { if (!r_method || !r_location) return gpg_error (GPG_ERR_INV_VALUE); return get_info_access (cert, idx, 0, r_method, r_location); } /* Return the subjectInfoAccess attributes. IDX should be iterated starting from 0 until the function returns GPG_ERR_EOF. R_METHOD returns an allocated string with the OID of one item and R_LOCATION return the GeneralName for that OID. The return values for R_METHOD and R_LOCATION must be released by the caller unless the function returned an error; the function will however make sure that R_METHOD and R_LOCATION will point to NULL if the function returns an error. See RFC 5280, section 4.2.2.2 */ gpg_error_t ksba_cert_get_subject_info_access (ksba_cert_t cert, int idx, char **r_method, ksba_name_t *r_location) { if (!r_method || !r_location) return gpg_error (GPG_ERR_INV_VALUE); return get_info_access (cert, idx, 1, r_method, r_location); } diff --git a/src/certreq.c b/src/certreq.c index 91bddf2..e0869e9 100644 --- a/src/certreq.c +++ b/src/certreq.c @@ -1,1129 +1,1129 @@ /* certreq.c - create pkcs-10 messages * Copyright (C) 2002, 2011, 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 . */ #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 "certreq.h" static const char oidstr_subjectAltName[] = "2.5.29.17"; static const char oidstr_extensionReq[] = "1.2.840.113549.1.9.14"; /** * ksba_cms_new: * * Create a new and empty CMS object * * Return value: A CMS object or an error code. **/ gpg_error_t ksba_certreq_new (ksba_certreq_t *r_cr) { *r_cr = xtrycalloc (1, sizeof **r_cr); if (!*r_cr) return gpg_error_from_errno (errno); return 0; } /** * ksba_certreq_release: * @cms: A Certreq object * * Release a Certreq object. **/ void ksba_certreq_release (ksba_certreq_t cr) { if (!cr) return; xfree (cr->x509.serial.der); xfree (cr->x509.issuer.der); xfree (cr->x509.siginfo.der); xfree (cr->subject.der); xfree (cr->key.der); xfree (cr->cri.der); xfree (cr->sig_val.algo); xfree (cr->sig_val.value); while (cr->subject_alt_names) { struct general_names_s *tmp = cr->subject_alt_names->next; xfree (cr->subject_alt_names); cr->subject_alt_names = tmp; } while (cr->extn_list) { struct extn_list_s *e = cr->extn_list->next; xfree (cr->extn_list); cr->extn_list = e; } xfree (cr); } gpg_error_t ksba_certreq_set_writer (ksba_certreq_t cr, ksba_writer_t w) { if (!cr || !w) return gpg_error (GPG_ERR_INV_VALUE); cr->writer = w; return 0; } /* Provide a hash function so that we are able to hash the data */ void ksba_certreq_set_hash_function (ksba_certreq_t cr, void (*hash_fnc)(void *, const void *, size_t), void *hash_fnc_arg) { if (cr) { cr->hash_fnc = hash_fnc; cr->hash_fnc_arg = hash_fnc_arg; } } /* Store the serial number. If this function is used, a real X.509 certificate will be built instead of a pkcs#10 certificate signing request. SN must be a simple canonical encoded s-expression with the serial number as its only item. Note that this function allows to set a negative serial number, which is not forbidden but probably not a good idea. */ gpg_error_t ksba_certreq_set_serial (ksba_certreq_t cr, ksba_const_sexp_t sn) { const char *p = (const char *)sn; unsigned long n; char *endp; if (!cr || !sn || !p || *p != '(') return gpg_error (GPG_ERR_INV_VALUE); p++; n = strtoul (p, &endp, 10); p = endp; if (*p++ != ':' || !n) return gpg_error (GPG_ERR_INV_VALUE); /* Remove invalid leading zero bytes. */ for (; n > 1 && !*p && !(p[1] & 0x80); n--, p++) ; if (cr->x509.serial.der) return gpg_error (GPG_ERR_CONFLICT); /* Already set */ cr->x509.serial.der = xtrymalloc (n); if (!cr->x509.serial.der) return gpg_error_from_syserror (); memcpy (cr->x509.serial.der, p, n); cr->x509.serial.derlen = n; return 0; } /* Store the issuer's name. NAME must be a valid RFC-2253 encoded DN name. Only used for building an X.509 certificate. */ gpg_error_t ksba_certreq_set_issuer (ksba_certreq_t cr, const char *name) { if (!cr || !name) return gpg_error (GPG_ERR_INV_VALUE); if (cr->x509.issuer.der) return gpg_error (GPG_ERR_CONFLICT); /* Already set */ return _ksba_dn_from_str (name, &cr->x509.issuer.der, &cr->x509.issuer.derlen); } /* Store validity information. The time is in TIMEBUF. A value of 0 for WHAT stores the notBefore time, a value of 1 stores the notAfter time. Only used for building an X.509 certificate. */ gpg_error_t ksba_certreq_set_validity (ksba_certreq_t cr, int what, const ksba_isotime_t timebuf) { if (!cr || what < 0 || what > 1 || !timebuf || _ksba_assert_time_format (timebuf)) return gpg_error (GPG_ERR_INV_VALUE); _ksba_copy_time (what?cr->x509.not_after:cr->x509.not_before, timebuf); return 0; } /* Store the signing key info. This is used to extract the signing algorithm; the signing itself needs to be done by the caller as response to a stop code. The expression SIGINFO is similar to a sig-val one, however most parameters are not required. The expected structure of this canonical encoded s-expression is: (sig-val ( ( ) ... ( ))) */ gpg_error_t ksba_certreq_set_siginfo (ksba_certreq_t cr, ksba_const_sexp_t siginfo) { if (!cr || !siginfo) return gpg_error (GPG_ERR_INV_VALUE); xfree (cr->x509.siginfo.der); cr->x509.siginfo.der = NULL; - return _ksba_algoinfo_from_sexp (siginfo, &cr->x509.siginfo.der, - &cr->x509.siginfo.derlen); + return _ksba_keyinfo_from_sexp (siginfo, 1, &cr->x509.siginfo.der, + &cr->x509.siginfo.derlen); } /* Store the subject's name. Does perform some syntactic checks on the name. The first added subject is the real one, all subsequent calls add subjectAltNames. NAME must be a valid RFC-2253 encoded DN name for the first one or an email address enclosed in angle brackets for all further calls. */ gpg_error_t ksba_certreq_add_subject (ksba_certreq_t cr, const char *name) { unsigned long namelen; size_t n, n1; struct general_names_s *gn; unsigned char *der; int tag; const char *endp; if (!cr || !name) return gpg_error (GPG_ERR_INV_VALUE); if (!cr->subject.der) return _ksba_dn_from_str (name, &cr->subject.der, &cr->subject.derlen); /* This is assumed to be an subjectAltName. */ /* Note that the way we pass the name should match what ksba_cert_get_subject() returns. In particular we expect that it is a real string and thus a canonical S-expression is additionally terminated by a 0. */ namelen = strlen (name); if (*name == '<' && name[namelen-1] == '>' && namelen >= 4 && strchr (name, '@')) { name++; namelen -= 2; tag = 1; /* rfc822Name */ } else if (!strncmp (name, "(8:dns-name", 11)) { tag = 2; /* dNSName */ namelen = strtoul (name+11, (char**)&endp, 10); name = endp; if (!namelen || *name != ':') return gpg_error (GPG_ERR_INV_SEXP); name++; } else if (!strncmp (name, "(3:uri", 6)) { tag = 6; /* uRI */ namelen = strtoul (name+6, (char**)&endp, 10); name = endp; if (!namelen || *name != ':') return gpg_error (GPG_ERR_INV_SEXP); name++; } else return gpg_error (GPG_ERR_INV_VALUE); n1 = _ksba_ber_count_tl (tag, CLASS_CONTEXT, 0, namelen); n1 += namelen; gn = xtrymalloc (sizeof *gn + n1 - 1); if (!gn) return gpg_error_from_errno (errno); gn->tag = tag; gn->datalen = n1; der = (unsigned char *)gn->data; n = _ksba_ber_encode_tl (der, tag, CLASS_CONTEXT, 0, namelen); if (!n) return gpg_error (GPG_ERR_BUG); der += n; memcpy (der, name, namelen); assert (der + namelen - (unsigned char*)gn->data == n1); gn->next = cr->subject_alt_names; cr->subject_alt_names = gn; return 0; } /* Add the GeneralNames object GNAMES to the list of extensions in CR. Use OID as object identifier for the extensions. */ static gpg_error_t add_general_names_to_extn (ksba_certreq_t cr, struct general_names_s *gnames, const char *oid) { struct general_names_s *g; size_t n, n1, n2; struct extn_list_s *e; unsigned char *der; /* Calculate the required size. */ n1 = 0; for (g=gnames; g; g = g->next) n1 += g->datalen; n2 = _ksba_ber_count_tl (TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, n1); n2 += n1; /* Allocate memory and encode all. */ e = xtrymalloc (sizeof *e + n2 - 1); if (!e) return gpg_error_from_errno (errno); e->oid = oid; e->critical = 0; e->derlen = n2; der = e->der; n = _ksba_ber_encode_tl (der, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, n1); if (!n) return gpg_error (GPG_ERR_BUG); /* (no need to cleanup after a bug) */ der += n; for (g=gnames; g; g = g->next) { memcpy (der, g->data, g->datalen); der += g->datalen; } assert (der - e->der == n2); e->next = cr->extn_list; cr->extn_list = e; return 0; } /* Store the subject's publickey. */ gpg_error_t ksba_certreq_set_public_key (ksba_certreq_t cr, ksba_const_sexp_t key) { if (!cr) return gpg_error (GPG_ERR_INV_VALUE); xfree (cr->key.der); cr->key.der = NULL; - return _ksba_keyinfo_from_sexp (key, &cr->key.der, &cr->key.derlen); + return _ksba_keyinfo_from_sexp (key, 0, &cr->key.der, &cr->key.derlen); } /* Generic function to add an extension to a certificate request. The extension must be provided readily encoded in the buffer DER of length DERLEN bytes; the OID is to be given in OID and IS_CRIT should be set to true if that extension shall be marked critical. */ gpg_error_t ksba_certreq_add_extension (ksba_certreq_t cr, const char *oid, int is_crit, const void *der, size_t derlen) { size_t oidlen; struct extn_list_s *e; if (!cr || !oid|| !*oid || !der || !derlen) return gpg_error (GPG_ERR_INV_VALUE); oidlen = strlen (oid); e = xtrymalloc (sizeof *e + derlen + oidlen); if (!e) return gpg_error_from_errno (errno); e->critical = is_crit; e->derlen = derlen; memcpy (e->der, der, derlen); strcpy (e->der+derlen, oid); e->oid = e->der + derlen; e->next = cr->extn_list; cr->extn_list = e; return 0; } /* * r_sig = (sig-val * ( * ( ) * ... * ( ) * )) * The sexp must be in canonical form. * Fixme: The code is mostly duplicated from cms.c * Note, that must be given as a stringified OID or the special * string "rsa" which is translated to sha1WithRSAEncryption */ gpg_error_t ksba_certreq_set_sig_val (ksba_certreq_t cr, ksba_const_sexp_t sigval) { const unsigned char *s, *saved; char *buf = NULL; unsigned long n, len; int pass, nparam; int is_EdDSA = 0; if (!cr) return gpg_error (GPG_ERR_INV_VALUE); s = sigval; if (*s != '(') return gpg_error (GPG_ERR_INV_SEXP); s++; 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); xfree (cr->sig_val.algo); if (n==3 && s[0] == 'r' && s[1] == 's' && s[2] == 'a') { /* kludge to allow "rsa" to be passed as algorithm name */ cr->sig_val.algo = xtrystrdup ("1.2.840.113549.1.1.5"); if (!cr->sig_val.algo) return gpg_error (GPG_ERR_ENOMEM); } else { cr->sig_val.algo = xtrymalloc (n+1); if (!cr->sig_val.algo) return gpg_error (GPG_ERR_ENOMEM); memcpy (cr->sig_val.algo, s, n); cr->sig_val.algo[n] = 0; if (!memcmp (s, "eddsa", 5)) is_EdDSA = 1; } s += n; /* And now the values. * * If there is only one value, the signature is simply * that value. Otherwise, the signature is a DER-encoded * SEQUENCE of INTEGERs representing the different values. * * We need three passes over the values: * - first pass is to get the number of values (nparam); * - second pass is to compute the total length (len); * - third pass is to build the final signature. */ for (pass = 1, nparam = len = 0, saved = s; pass < 4; pass++) { s = saved; if (pass == 3) { size_t needed = len; if (!is_EdDSA && nparam > 1) needed += _ksba_ber_count_tl (TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, len); xfree (cr->sig_val.value); cr->sig_val.value = xtrymalloc (needed); if (!cr->sig_val.value) return gpg_error (GPG_ERR_ENOMEM); cr->sig_val.valuelen = needed; buf = cr->sig_val.value; if (!is_EdDSA && nparam > 1) buf += _ksba_ber_encode_tl (buf, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, len); } while (*s != ')') { if (*s != '(') return gpg_error (digitp (s)? GPG_ERR_UNKNOWN_SEXP : GPG_ERR_INV_SEXP); s++; if (!(n = snext (&s))) return gpg_error (GPG_ERR_INV_SEXP); s += n; /* Ignore the name of the parameter. */ if (!digitp (s)) return gpg_error (GPG_ERR_UNKNOWN_SEXP); if (!(n = snext (&s))) return gpg_error (GPG_ERR_INV_SEXP); if (pass == 1) nparam++; else if (pass == 2) { if (is_EdDSA || nparam == 1) len += n; else len += _ksba_ber_count_tl (TYPE_INTEGER, CLASS_UNIVERSAL, 0, *s >= 0x80? n + 1 : n) + (*s >= 0x80? n + 1 : n); } else if (pass == 3) { if (is_EdDSA || nparam == 1) { memcpy (buf, s, n); buf += n; } else { if (*s >= 0x80) { /* Add leading zero byte. */ buf += _ksba_ber_encode_tl (buf, TYPE_INTEGER, CLASS_UNIVERSAL, 0, n + 1); *buf++ = 0; } else buf += _ksba_ber_encode_tl (buf, TYPE_INTEGER, CLASS_UNIVERSAL, 0, n); memcpy (buf, s, n); buf += n; } } s += n; if (*s != ')') return gpg_error (GPG_ERR_UNKNOWN_SEXP); s++; } } /* we need 2 closing parenthesis */ if ( *s != ')' || s[1] != ')') return gpg_error (GPG_ERR_INV_SEXP); return 0; } /* Build the extension block and return it in R_DER and R_DERLEN. IF CERTMODE is true build X.509 certificate extension instead. */ static gpg_error_t build_extensions (ksba_certreq_t cr, int certmode, void **r_der, size_t *r_derlen) { gpg_error_t err; ksba_writer_t writer, w=NULL; struct extn_list_s *e; unsigned char *value = NULL; size_t valuelen; unsigned char *p; size_t n; *r_der = NULL; *r_derlen = 0; err = ksba_writer_new (&writer); if (err) goto leave; err = ksba_writer_set_mem (writer, 2048); if (err) goto leave; err = ksba_writer_new (&w); if (err) goto leave; for (e=cr->extn_list; e; e = e->next) { err = ksba_writer_set_mem (w, e->derlen + 100); if (err) goto leave; err = ksba_oid_from_str (e->oid, &p, &n); if(err) goto leave; err = _ksba_ber_write_tl (w, TYPE_OBJECT_ID, CLASS_UNIVERSAL, 0, n); if (!err) err = ksba_writer_write (w, p, n); xfree (p); if (e->critical) { err = _ksba_ber_write_tl (w, TYPE_BOOLEAN, CLASS_UNIVERSAL, 0, 1); if (!err) err = ksba_writer_write (w, "\xff", 1); if(err) goto leave; } err = _ksba_ber_write_tl (w, TYPE_OCTET_STRING, CLASS_UNIVERSAL, 0, e->derlen); if (!err) err = ksba_writer_write (w, e->der, e->derlen); if(err) goto leave; p = ksba_writer_snatch_mem (w, &n); if (!p) { err = gpg_error (GPG_ERR_ENOMEM); goto leave; } err = _ksba_ber_write_tl (writer, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, n); if (!err) err = ksba_writer_write (writer, p, n); xfree (p); p = NULL; if (err) goto leave; } /* Embed all the sequences into another sequence */ value = ksba_writer_snatch_mem (writer, &valuelen); if (!value) { err = gpg_error (GPG_ERR_ENOMEM); goto leave; } err = ksba_writer_set_mem (writer, valuelen+10); if (err) goto leave; err = _ksba_ber_write_tl (writer, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, valuelen); if (!err) err = ksba_writer_write (writer, value, valuelen); if (err) goto leave; xfree (value); value = ksba_writer_snatch_mem (writer, &valuelen); if (!value) { err = gpg_error (GPG_ERR_ENOMEM); goto leave; } if (!certmode) { /* Now create the extension request sequence content */ err = ksba_writer_set_mem (writer, valuelen+100); if (err) goto leave; err = ksba_oid_from_str (oidstr_extensionReq, &p, &n); if(err) goto leave; err = _ksba_ber_write_tl (writer, TYPE_OBJECT_ID, CLASS_UNIVERSAL, 0, n); if (!err) err = ksba_writer_write (writer, p, n); xfree (p); p = NULL; if (err) return err; err = _ksba_ber_write_tl (writer, TYPE_SET, CLASS_UNIVERSAL, 1, valuelen); if (!err) err = ksba_writer_write (writer, value, valuelen); /* Put this all into a SEQUENCE */ xfree (value); value = ksba_writer_snatch_mem (writer, &valuelen); if (!value) { err = gpg_error (GPG_ERR_ENOMEM); goto leave; } err = ksba_writer_set_mem (writer, valuelen+10); if (err) goto leave; err = _ksba_ber_write_tl (writer, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, valuelen); if (!err) err = ksba_writer_write (writer, value, valuelen); if (err) goto leave; xfree (value); value = ksba_writer_snatch_mem (writer, &valuelen); if (!value) { err = gpg_error (GPG_ERR_ENOMEM); goto leave; } } *r_der = value; *r_derlen = valuelen; value = NULL; leave: ksba_writer_release (writer); ksba_writer_release (w); xfree (value); return err; } /* Build a value tree from the already stored values. */ static gpg_error_t build_cri (ksba_certreq_t cr) { gpg_error_t err; ksba_writer_t writer; void *value = NULL; size_t valuelen; int certmode; /* If a serial number has been set, we don't create a CSR but a proper certificate. */ certmode = !!cr->x509.serial.der; err = ksba_writer_new (&writer); if (err) goto leave; err = ksba_writer_set_mem (writer, 2048); if (err) goto leave; if (!cr->key.der) { err = gpg_error (GPG_ERR_MISSING_VALUE); goto leave; } /* We write all stuff out to a temporary writer object, then use this object to create the cri and store the cri image */ if (certmode) { /* Store the version structure; version is 3 (encoded as 2): [0] { INTEGER 2 } */ err = ksba_writer_write (writer, "\xa0\x03\x02\x01\x02", 5); } else { /* Store version v1 (which is a 0). */ err = _ksba_ber_write_tl (writer, TYPE_INTEGER, CLASS_UNIVERSAL, 0, 1); if (!err) err = ksba_writer_write (writer, "", 1); } if (err) goto leave; /* For a certificate we need to store the s/n, the signature algorithm identifier, the issuer DN and the validity. */ if (certmode) { /* Store the serial number. */ err = _ksba_ber_write_tl (writer, TYPE_INTEGER, CLASS_UNIVERSAL, 0, cr->x509.serial.derlen); if (!err) err = ksba_writer_write (writer, cr->x509.serial.der, cr->x509.serial.derlen); if (err) goto leave; /* Store the signature algorithm identifier. */ if (!cr->x509.siginfo.der) err = gpg_error (GPG_ERR_MISSING_VALUE); else err = ksba_writer_write (writer, cr->x509.siginfo.der, cr->x509.siginfo.derlen); if (err) goto leave; /* Store the issuer DN. If no issuer DN has been set we use the subject DN. */ if (cr->x509.issuer.der) err = ksba_writer_write (writer, cr->x509.issuer.der, cr->x509.issuer.derlen); else if (cr->subject.der) err = ksba_writer_write (writer, cr->subject.der, cr->subject.derlen); else err = gpg_error (GPG_ERR_MISSING_VALUE); if (err) goto leave; /* Store the Validity. */ { unsigned char templ[36]; unsigned char *tp; tp = templ; *tp++ = 0x30; *tp++ = 0x22; *tp++ = TYPE_GENERALIZED_TIME; *tp++ = 15; if (cr->x509.not_before[0]) { if (_ksba_cmp_time (cr->x509.not_before, "20500101T000000") >= 0) { memcpy (tp, cr->x509.not_before, 8); tp += 8; memcpy (tp, cr->x509.not_before+9, 6); tp += 6; } else { tp[-2] = TYPE_UTC_TIME; tp[-1] = 13; memcpy (tp, cr->x509.not_before+2, 6); tp += 6; memcpy (tp, cr->x509.not_before+9, 6); tp += 6; } } else { tp[-2] = TYPE_UTC_TIME; tp[-1] = 13; memcpy (tp, "110101000000", 12); tp += 12; } *tp++ = 'Z'; *tp++ = TYPE_GENERALIZED_TIME; *tp++ = 15; if (cr->x509.not_after[0]) { if (_ksba_cmp_time (cr->x509.not_after, "20500101T000000") >= 0) { memcpy (tp, cr->x509.not_after, 8); tp += 8; memcpy (tp, cr->x509.not_after+9, 6); tp += 6; } else { tp[-2] = TYPE_UTC_TIME; tp[-1] = 13; memcpy (tp, cr->x509.not_after+2, 6); tp += 6; memcpy (tp, cr->x509.not_after+9, 6); tp += 6; } } else { memcpy (tp,"20630405170000", 14); tp += 14; } *tp++ = 'Z'; assert (tp - templ <= 36); templ[1] = tp - templ - 2; /* Fixup the sequence length. */ err = ksba_writer_write (writer, templ, tp - templ); if (err) goto leave; } } /* store the subject */ if (!cr->subject.der) { err = gpg_error (GPG_ERR_MISSING_VALUE); goto leave; } err = ksba_writer_write (writer, cr->subject.der, cr->subject.derlen); if (err) goto leave; /* store the public key info */ err = ksba_writer_write (writer, cr->key.der, cr->key.derlen); if (err) goto leave; /* Copy generalNames objects to the extension list. */ if (cr->subject_alt_names) { err = add_general_names_to_extn (cr, cr->subject_alt_names, oidstr_subjectAltName); if (err) goto leave; while (cr->subject_alt_names) { struct general_names_s *tmp = cr->subject_alt_names->next; xfree (cr->subject_alt_names); cr->subject_alt_names = tmp; } cr->subject_alt_names = NULL; } /* Write the extensions. Note that the implicit SET OF is REQUIRED */ xfree (value); value = NULL; valuelen = 0; if (cr->extn_list) { err = build_extensions (cr, certmode, &value, &valuelen); if (err) goto leave; err = _ksba_ber_write_tl (writer, certmode? 3:0, CLASS_CONTEXT, 1, valuelen); if (!err) err = ksba_writer_write (writer, value, valuelen); if (err) goto leave; } else { /* We can't write an object of length zero using our ber_write function. So we must open encode it. */ err = ksba_writer_write (writer, certmode? "\xa3\x02\x30":"\xa0\x02\x30", 4); if (err) goto leave; } /* pack it into the sequence */ xfree (value); value = ksba_writer_snatch_mem (writer, &valuelen); if (!value) { err = gpg_error (GPG_ERR_ENOMEM); goto leave; } /* reinitialize the buffer to create the outer sequence */ err = ksba_writer_set_mem (writer, valuelen+10); if (err) goto leave; /* write outer sequence */ err = _ksba_ber_write_tl (writer, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, valuelen); if (!err) err = ksba_writer_write (writer, value, valuelen); if (err) goto leave; /* and store the final result */ cr->cri.der = ksba_writer_snatch_mem (writer, &cr->cri.derlen); if (!cr->cri.der) err = gpg_error (GPG_ERR_ENOMEM); leave: ksba_writer_release (writer); xfree (value); return err; } static gpg_error_t hash_cri (ksba_certreq_t cr) { if (!cr->hash_fnc) return gpg_error (GPG_ERR_MISSING_ACTION); if (!cr->cri.der) return gpg_error (GPG_ERR_INV_STATE); cr->hash_fnc (cr->hash_fnc_arg, cr->cri.der, cr->cri.derlen); return 0; } /* The user has calculated the signatures and we can now write the signature */ static gpg_error_t sign_and_write (ksba_certreq_t cr) { gpg_error_t err; ksba_writer_t writer; void *value = NULL; size_t valuelen; err = ksba_writer_new (&writer); if (err) goto leave; err = ksba_writer_set_mem (writer, 2048); if (err) goto leave; /* store the cri */ if (!cr->cri.der) { err = gpg_error (GPG_ERR_MISSING_VALUE); goto leave; } err = ksba_writer_write (writer, cr->cri.der, cr->cri.derlen); if (err) goto leave; /* store the signatureAlgorithm */ if (!cr->sig_val.algo) return gpg_error (GPG_ERR_MISSING_VALUE); err = _ksba_der_write_algorithm_identifier (writer, cr->sig_val.algo, NULL, 0); if (err) goto leave; /* write the signature */ err = _ksba_ber_write_tl (writer, TYPE_BIT_STRING, CLASS_UNIVERSAL, 0, 1 + cr->sig_val.valuelen); if (!err) err = ksba_writer_write (writer, "", 1); if (!err) err = ksba_writer_write (writer, cr->sig_val.value, cr->sig_val.valuelen); if (err) goto leave; /* pack it into the outer sequence */ value = ksba_writer_snatch_mem (writer, &valuelen); if (!value) { err = gpg_error (GPG_ERR_ENOMEM); goto leave; } err = ksba_writer_set_mem (writer, valuelen+10); if (err) goto leave; /* write outer sequence */ err = _ksba_ber_write_tl (writer, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, valuelen); if (!err) err = ksba_writer_write (writer, value, valuelen); if (err) goto leave; /* and finally write the result */ xfree (value); value = ksba_writer_snatch_mem (writer, &valuelen); if (!value) err = gpg_error (GPG_ERR_ENOMEM); else if (!cr->writer) err = gpg_error (GPG_ERR_MISSING_ACTION); else err = ksba_writer_write (cr->writer, value, valuelen); leave: ksba_writer_release (writer); xfree (value); return err; } /* The main function to build a certificate request. It used used in a loop so allow for interaction between the function and the caller */ gpg_error_t ksba_certreq_build (ksba_certreq_t cr, ksba_stop_reason_t *r_stopreason) { enum { sSTART, sHASHING, sGOTSIG, sERROR } state = sERROR; gpg_error_t err = 0; ksba_stop_reason_t stop_reason; if (!cr || !r_stopreason) return gpg_error (GPG_ERR_INV_VALUE); if (!cr->any_build_done) { /* first time initialization of the stop reason */ *r_stopreason = 0; cr->any_build_done = 1; } /* Calculate state from last reason */ stop_reason = *r_stopreason; *r_stopreason = KSBA_SR_RUNNING; switch (stop_reason) { case 0: state = sSTART; break; case KSBA_SR_NEED_HASH: state = sHASHING; break; case KSBA_SR_NEED_SIG: if (!cr->sig_val.algo) err = gpg_error (GPG_ERR_MISSING_ACTION); else state = sGOTSIG; break; case KSBA_SR_RUNNING: err = gpg_error (GPG_ERR_INV_STATE); break; default: err = gpg_error (GPG_ERR_BUG); break; } if (err) return err; /* Do the action */ switch (state) { case sSTART: err = build_cri (cr); break; case sHASHING: err = hash_cri (cr); break; case sGOTSIG: err = sign_and_write (cr); break; default: err = gpg_error (GPG_ERR_INV_STATE); break; } if (err) return err; /* Calculate new stop reason */ switch (state) { case sSTART: stop_reason = KSBA_SR_NEED_HASH; /* caller should set the hash function*/ break; case sHASHING: stop_reason = KSBA_SR_NEED_SIG; break; case sGOTSIG: stop_reason = KSBA_SR_READY; break; default: break; } *r_stopreason = stop_reason; return 0; } diff --git a/src/keyinfo.c b/src/keyinfo.c index 3dc0f34..e826f6b 100644 --- a/src/keyinfo.c +++ b/src/keyinfo.c @@ -1,2033 +1,1764 @@ /* keyinfo.c - Parse and build a keyInfo structure * Copyright (C) 2001, 2002, 2007, 2008, 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 . */ /* Instead of using the ASN parser - which is easily possible - we use a simple handcoded one to speed up the operation and to make it more robust. */ #include #include #include #include #include #include "util.h" #include "asn1-func.h" #include "keyinfo.h" #include "shared.h" #include "convert.h" #include "ber-help.h" #include "sexp-parse.h" #include "stringbuf.h" /* Constants used for the public key algorithms. */ typedef enum { PKALGO_RSA, PKALGO_DSA, PKALGO_ECC, PKALGO_X25519, PKALGO_X448, PKALGO_ED25519, PKALGO_ED448 } pkalgo_t; struct algo_table_s { const char *oidstring; const unsigned char *oid; /* NULL indicattes end of table */ int oidlen; int supported; /* Values > 1 are also used to indicate hacks. */ pkalgo_t pkalgo; const char *algo_string; const char *elem_string; /* parameter name or '-' */ const char *ctrl_string; /* expected tag values (value > 127 are raw data)*/ const char *parmelem_string; /* parameter name or '-'. */ const char *parmctrl_string; /* expected tag values. */ const char *digest_string; /* The digest algo if included in the OID. */ }; /* Special values for the supported field. */ #define SUPPORTED_RSAPSS 2 static const struct algo_table_s pk_algo_table[] = { { /* iso.member-body.us.rsadsi.pkcs.pkcs-1.1 */ "1.2.840.113549.1.1.1", /* rsaEncryption (RSAES-PKCA1-v1.5) */ "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01", 9, 1, PKALGO_RSA, "rsa", "-ne", "\x30\x02\x02" }, { /* iso.member-body.us.rsadsi.pkcs.pkcs-1.7 */ "1.2.840.113549.1.1.7", /* RSAES-OAEP */ "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x07", 9, 0, PKALGO_RSA, "rsa", "-ne", "\x30\x02\x02"}, { /* iso.member-body.us.rsadsi.pkcs.pkcs-1.10 */ "1.2.840.113549.1.1.10", /* rsaPSS */ "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x0a", 9, SUPPORTED_RSAPSS, PKALGO_RSA, "rsa", "-ne", "\x30\x02\x02"}, { /* */ "2.5.8.1.1", /* rsa (ambiguous due to missing padding rules)*/ "\x55\x08\x01\x01", 4, 1, PKALGO_RSA, "ambiguous-rsa", "-ne", "\x30\x02\x02" }, { /* iso.member-body.us.x9-57.x9cm.1 */ "1.2.840.10040.4.1", /* dsa */ "\x2a\x86\x48\xce\x38\x04\x01", 7, 1, PKALGO_DSA, "dsa", "y", "\x02", "-pqg", "\x30\x02\x02\x02" }, { /* iso.member-body.us.ansi-x9-62.2.1 */ "1.2.840.10045.2.1", /* ecPublicKey */ "\x2a\x86\x48\xce\x3d\x02\x01", 7, 1, PKALGO_ECC, "ecc", "q", "\x80" }, { /* iso.identified-organization.thawte.110 */ "1.3.101.110", /* X25519 */ "\x2b\x65\x6e", 3, 1, PKALGO_X25519, "ecc", "q", "\x80" }, { /* iso.identified-organization.thawte.111 */ "1.3.101.111", /* X448 */ "\x2b\x65\x6f", 3, 1, PKALGO_X448, "ecc", "q", "\x80" }, { /* iso.identified-organization.thawte.112 */ "1.3.101.112", /* Ed25519 */ "\x2b\x65\x70", 3, 1, PKALGO_ED25519, "ecc", "q", "\x80" }, { /* iso.identified-organization.thawte.113 */ "1.3.101.113", /* Ed448 */ "\x2b\x65\x71", 3, 1, PKALGO_ED448, "ecc", "q", "\x80" }, {NULL} }; static const struct algo_table_s sig_algo_table[] = { { /* iso.member-body.us.rsadsi.pkcs.pkcs-1.5 */ "1.2.840.113549.1.1.5", /* sha1WithRSAEncryption */ "\x2A\x86\x48\x86\xF7\x0D\x01\x01\x05", 9, 1, PKALGO_RSA, "rsa", "s", "\x82", NULL, NULL, "sha1" }, { /* iso.member-body.us.rsadsi.pkcs.pkcs-1.4 */ "1.2.840.113549.1.1.4", /* md5WithRSAEncryption */ "\x2A\x86\x48\x86\xF7\x0D\x01\x01\x04", 9, 1, PKALGO_RSA, "rsa", "s", "\x82", NULL, NULL, "md5" }, { /* iso.member-body.us.rsadsi.pkcs.pkcs-1.2 */ "1.2.840.113549.1.1.2", /* md2WithRSAEncryption */ "\x2A\x86\x48\x86\xF7\x0D\x01\x01\x02", 9, 0, PKALGO_RSA, "rsa", "s", "\x82", NULL, NULL, "md2" }, { /* iso.member-body.us.x9-57.x9cm.1 */ "1.2.840.10040.4.3", /* dsa */ "\x2a\x86\x48\xce\x38\x04\x01", 7, 1, PKALGO_DSA, "dsa", "-rs", "\x30\x02\x02" }, { /* iso.member-body.us.x9-57.x9cm.3 */ "1.2.840.10040.4.3", /* dsaWithSha1 */ "\x2a\x86\x48\xce\x38\x04\x03", 7, 1, PKALGO_DSA, "dsa", "-rs", "\x30\x02\x02", NULL, NULL, "sha1" }, { /* Teletrust signature algorithm. */ "1.3.36.8.5.1.2.2", /* dsaWithRIPEMD160 */ "\x2b\x24\x08\x05\x01\x02\x02", 7, 1, PKALGO_DSA, "dsa", "-rs", "\x30\x02\x02", NULL, NULL, "rmd160" }, { /* NIST Algorithm */ "2.16.840.1.101.3.4.3.1", /* dsaWithSha224 */ "\x06\x09\x60\x86\x48\x01\x65\x03\x04\x03\x01", 11, 1, PKALGO_DSA, "dsa", "-rs", "\x30\x02\x02", NULL, NULL, "sha224" }, { /* NIST Algorithm (the draft also used .1 but we better use .2) */ "2.16.840.1.101.3.4.3.2", /* dsaWithSha256 */ "\x06\x09\x60\x86\x48\x01\x65\x03\x04\x03\x01", 11, 1, PKALGO_DSA, "dsa", "-rs", "\x30\x02\x02", NULL, NULL, "sha256" }, { /* iso.member-body.us.ansi-x9-62.signatures.ecdsa-with-sha1 */ "1.2.840.10045.4.1", /* ecdsa */ "\x2a\x86\x48\xce\x3d\x04\x01", 7, 1, PKALGO_ECC, "ecdsa", "-rs", "\x30\x02\x02", NULL, NULL, "sha1" }, { /* iso.member-body.us.ansi-x9-62.signatures.ecdsa-with-specified */ "1.2.840.10045.4.3", "\x2a\x86\x48\xce\x3d\x04\x03", 7, 1, PKALGO_ECC, "ecdsa", "-rs", "\x30\x02\x02", NULL, NULL, NULL }, /* The digest algorithm is given by the parameter. */ { /* iso.member-body.us.ansi-x9-62.signatures.ecdsa-with-sha224 */ "1.2.840.10045.4.3.1", "\x2a\x86\x48\xce\x3d\x04\x03\x01", 8, 1, PKALGO_ECC, "ecdsa", "-rs", "\x30\x02\x02", NULL, NULL, "sha224" }, { /* iso.member-body.us.ansi-x9-62.signatures.ecdsa-with-sha256 */ "1.2.840.10045.4.3.2", "\x2a\x86\x48\xce\x3d\x04\x03\x02", 8, 1, PKALGO_ECC, "ecdsa", "-rs", "\x30\x02\x02", NULL, NULL, "sha256" }, { /* iso.member-body.us.ansi-x9-62.signatures.ecdsa-with-sha384 */ "1.2.840.10045.4.3.3", "\x2a\x86\x48\xce\x3d\x04\x03\x03", 8, 1, PKALGO_ECC, "ecdsa", "-rs", "\x30\x02\x02", NULL, NULL, "sha384" }, { /* iso.member-body.us.ansi-x9-62.signatures.ecdsa-with-sha512 */ "1.2.840.10045.4.3.4", "\x2a\x86\x48\xce\x3d\x04\x03\x04", 8, 1, PKALGO_ECC, "ecdsa", "-rs", "\x30\x02\x02", NULL, NULL, "sha512" }, { /* iso.member-body.us.rsadsi.pkcs.pkcs-1.1 */ "1.2.840.113549.1.1.1", /* rsaEncryption used without hash algo*/ "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01", 9, 1, PKALGO_RSA, "rsa", "s", "\x82" }, { /* from NIST's OIW - actually belongs in a pure hash table */ "1.3.14.3.2.26", /* sha1 */ "\x2B\x0E\x03\x02\x1A", 5, 0, PKALGO_RSA, "sha-1", "", "", NULL, NULL, "sha1" }, { /* As used by telesec cards */ "1.3.36.3.3.1.2", /* rsaSignatureWithripemd160 */ "\x2b\x24\x03\x03\x01\x02", 6, 1, PKALGO_RSA, "rsa", "s", "\x82", NULL, NULL, "rmd160" }, { /* from NIST's OIW - used by TU Darmstadt */ "1.3.14.3.2.29", /* sha-1WithRSAEncryption */ "\x2B\x0E\x03\x02\x1D", 5, 1, PKALGO_RSA, "rsa", "s", "\x82", NULL, NULL, "sha1" }, { /* from PKCS#1 */ "1.2.840.113549.1.1.11", /* sha256WithRSAEncryption */ "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x0b", 9, 1, PKALGO_RSA, "rsa", "s", "\x82", NULL, NULL, "sha256" }, { /* from PKCS#1 */ "1.2.840.113549.1.1.12", /* sha384WithRSAEncryption */ "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x0c", 9, 1, PKALGO_RSA, "rsa", "s", "\x82", NULL, NULL, "sha384" }, { /* from PKCS#1 */ "1.2.840.113549.1.1.13", /* sha512WithRSAEncryption */ "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x0d", 9, 1, PKALGO_RSA, "rsa", "s", "\x82", NULL, NULL, "sha512" }, { /* iso.member-body.us.rsadsi.pkcs.pkcs-1.10 */ "1.2.840.113549.1.1.10", /* rsaPSS */ "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x0a", 9, SUPPORTED_RSAPSS, PKALGO_RSA, "rsa", "s", "\x82", NULL, NULL, NULL}, { /* TeleTrust signature scheme with RSA signature and DSI according to ISO/IEC 9796-2 with random number and RIPEMD-160. I am not sure for what this is good; thus disabled. */ "1.3.36.3.4.3.2.2", /* sigS_ISO9796-2rndWithrsa_ripemd160 */ "\x2B\x24\x03\x04\x03\x02\x02", 7, 0, PKALGO_RSA, "rsa", "s", "\x82", NULL, NULL, "rmd160" }, { /* iso.identified-organization.thawte.112 */ "1.3.101.112", /* Ed25519 */ "\x2b\x65\x70", 3, 1, PKALGO_ED25519, "eddsa", "", "", NULL, NULL, NULL }, { /* iso.identified-organization.thawte.113 */ "1.3.101.113", /* Ed448 */ "\x2b\x65\x71", 3, 1, PKALGO_ED448, "eddsa", "", "", NULL, NULL, NULL }, {NULL} }; static const struct algo_table_s enc_algo_table[] = { {/* iso.member-body.us.rsadsi.pkcs.pkcs-1.1 */ "1.2.840.113549.1.1.1", /* rsaEncryption (RSAES-PKCA1-v1.5) */ "\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01", 9, 1, PKALGO_RSA, "rsa", "a", "\x82" }, {/* iso.member-body.us.ansi-x9-62.2.1 */ "1.2.840.10045.2.1", /* ecPublicKey */ "\x2a\x86\x48\xce\x3d\x02\x01", 7, 1, PKALGO_ECC, "ecdh", "e", "\x80" }, {NULL} }; /* This tables maps names of ECC curves names to OIDs. A similar table is used by Libgcrypt. */ static const struct { const char *oid; const char *name; } curve_names[] = { { "1.3.6.1.4.1.3029.1.5.1", "Curve25519" }, { "1.3.6.1.4.1.11591.15.1", "Ed25519" }, { "1.2.840.10045.3.1.1", "NIST P-192" }, { "1.2.840.10045.3.1.1", "nistp192" }, { "1.2.840.10045.3.1.1", "prime192v1" }, { "1.2.840.10045.3.1.1", "secp192r1" }, { "1.3.132.0.33", "NIST P-224" }, { "1.3.132.0.33", "nistp224" }, { "1.3.132.0.33", "secp224r1" }, { "1.2.840.10045.3.1.7", "NIST P-256" }, { "1.2.840.10045.3.1.7", "nistp256" }, { "1.2.840.10045.3.1.7", "prime256v1" }, { "1.2.840.10045.3.1.7", "secp256r1" }, { "1.3.132.0.34", "NIST P-384" }, { "1.3.132.0.34", "nistp384" }, { "1.3.132.0.34", "secp384r1" }, { "1.3.132.0.35", "NIST P-521" }, { "1.3.132.0.35", "nistp521" }, { "1.3.132.0.35", "secp521r1" }, { "1.3.36.3.3.2.8.1.1.1" , "brainpoolP160r1" }, { "1.3.36.3.3.2.8.1.1.3" , "brainpoolP192r1" }, { "1.3.36.3.3.2.8.1.1.5" , "brainpoolP224r1" }, { "1.3.36.3.3.2.8.1.1.7" , "brainpoolP256r1" }, { "1.3.36.3.3.2.8.1.1.9" , "brainpoolP320r1" }, { "1.3.36.3.3.2.8.1.1.11", "brainpoolP384r1" }, { "1.3.36.3.3.2.8.1.1.13", "brainpoolP512r1" }, { "1.2.643.2.2.35.1", "GOST2001-CryptoPro-A" }, { "1.2.643.2.2.35.2", "GOST2001-CryptoPro-B" }, { "1.2.643.2.2.35.3", "GOST2001-CryptoPro-C" }, { "1.2.643.7.1.2.1.2.1", "GOST2012-tc26-A" }, { "1.2.643.7.1.2.1.2.2", "GOST2012-tc26-B" }, { "1.3.132.0.10", "secp256k1" }, { NULL, NULL} }; #define TLV_LENGTH(prefix) do { \ if (!prefix ## len) \ return gpg_error (GPG_ERR_INV_KEYINFO); \ c = *(prefix)++; prefix ## len--; \ if (c == 0x80) \ return gpg_error (GPG_ERR_NOT_DER_ENCODED); \ if (c == 0xff) \ return gpg_error (GPG_ERR_BAD_BER); \ \ if ( !(c & 0x80) ) \ len = c; \ else \ { \ int count = c & 0x7f; \ \ for (len=0; count; count--) \ { \ len <<= 8; \ if (!prefix ## len) \ return gpg_error (GPG_ERR_BAD_BER);\ c = *(prefix)++; prefix ## len--; \ len |= c & 0xff; \ } \ } \ if (len > prefix ## len) \ return gpg_error (GPG_ERR_INV_KEYINFO); \ } while (0) /* Given a string BUF of length BUFLEN with either the name of an ECC curve or its OID in dotted form return the DER encoding of the OID. The caller must free the result. On error NULL is returned. */ static unsigned char * get_ecc_curve_oid (const unsigned char *buf, size_t buflen, size_t *r_oidlen) { unsigned char *der_oid; /* Skip an optional "oid." prefix. */ if (buflen > 4 && buf[3] == '.' && digitp (buf+4) && ((buf[0] == 'o' && buf[1] == 'i' && buf[2] == 'd') ||(buf[0] == 'O' && buf[1] == 'I' && buf[2] == 'D'))) { buf += 4; buflen -= 4; } /* If it does not look like an OID - map it through the table. */ if (buflen && !digitp (buf)) { int i; for (i=0; curve_names[i].oid; i++) if (buflen == strlen (curve_names[i].name) && !memcmp (buf, curve_names[i].name, buflen)) break; if (!curve_names[i].oid) return NULL; /* Not found. */ buf = curve_names[i].oid; buflen = strlen (curve_names[i].oid); } if (_ksba_oid_from_buf (buf, buflen, &der_oid, r_oidlen)) return NULL; return der_oid; } /* Return the OFF and the LEN of algorithm within DER. Do some checks and return the number of bytes read in r_nread, adding this to der does point into the BIT STRING. mode 0: just get the algorithm identifier. FIXME: should be able to handle BER Encoding. mode 1: as described. */ static gpg_error_t get_algorithm (int mode, const unsigned char *der, size_t derlen, size_t *r_nread, size_t *r_pos, size_t *r_len, int *r_bitstr, size_t *r_parm_pos, size_t *r_parm_len, int *r_parm_type) { int c; const unsigned char *start = der; const unsigned char *startseq; unsigned long seqlen, len; *r_bitstr = 0; if (r_parm_pos) *r_parm_pos = 0; if (r_parm_len) *r_parm_len = 0; if (r_parm_type) *r_parm_type = 0; /* get the inner sequence */ if (!derlen) return gpg_error (GPG_ERR_INV_KEYINFO); c = *der++; derlen--; if ( c != 0x30 ) return gpg_error (GPG_ERR_UNEXPECTED_TAG); /* not a SEQUENCE */ TLV_LENGTH(der); seqlen = len; startseq = der; /* get the object identifier */ if (!derlen) return gpg_error (GPG_ERR_INV_KEYINFO); c = *der++; derlen--; if ( c != 0x06 ) return gpg_error (GPG_ERR_UNEXPECTED_TAG); /* not an OBJECT IDENTIFIER */ TLV_LENGTH(der); /* der does now point to an oid of length LEN */ *r_pos = der - start; *r_len = len; der += len; derlen -= len; seqlen -= der - startseq;; /* Parse the parameter. */ if (seqlen) { const unsigned char *startparm = der; if (!derlen) return gpg_error (GPG_ERR_INV_KEYINFO); c = *der++; derlen--; if ( c == 0x05 ) { /* gpgrt_log_debug ("%s: parameter: NULL \n", __func__); */ if (!derlen) return gpg_error (GPG_ERR_INV_KEYINFO); c = *der++; derlen--; if (c) return gpg_error (GPG_ERR_BAD_BER); /* NULL must have a length of 0 */ seqlen -= 2; } else if (r_parm_pos && r_parm_len && c == 0x04) { /* This is an octet string parameter and we need it. */ if (r_parm_type) *r_parm_type = TYPE_OCTET_STRING; TLV_LENGTH(der); *r_parm_pos = der - start; *r_parm_len = len; seqlen -= der - startparm; der += len; derlen -= len; seqlen -= len; } else if (r_parm_pos && r_parm_len && c == 0x06) { /* This is an object identifier. */ if (r_parm_type) *r_parm_type = TYPE_OBJECT_ID; TLV_LENGTH(der); *r_parm_pos = der - start; *r_parm_len = len; seqlen -= der - startparm; der += len; derlen -= len; seqlen -= len; } else if (r_parm_pos && r_parm_len && c == 0x30) { /* This is a sequence. */ if (r_parm_type) *r_parm_type = TYPE_SEQUENCE; TLV_LENGTH(der); *r_parm_pos = startparm - start; *r_parm_len = len + (der - startparm); seqlen -= der - startparm; der += len; derlen -= len; seqlen -= len; } else { /* printf ("parameter: with tag %02x - ignored\n", c); */ TLV_LENGTH(der); seqlen -= der - startparm; /* skip the value */ der += len; derlen -= len; seqlen -= len; } } if (seqlen) return gpg_error (GPG_ERR_INV_KEYINFO); if (mode) { /* move forward to the BIT_STR */ if (!derlen) return gpg_error (GPG_ERR_INV_KEYINFO); c = *der++; derlen--; if (c == 0x03) *r_bitstr = 1; /* BIT STRING */ else if (c == 0x04) ; /* OCTECT STRING */ else return gpg_error (GPG_ERR_UNEXPECTED_TAG); /* not a BIT STRING */ TLV_LENGTH(der); } *r_nread = der - start; return 0; } gpg_error_t _ksba_parse_algorithm_identifier (const unsigned char *der, size_t derlen, size_t *r_nread, char **r_oid) { return _ksba_parse_algorithm_identifier2 (der, derlen, r_nread, r_oid, NULL, NULL); } /* Note that R_NREAD, R_PARM, and R_PARMLEN are optional. */ gpg_error_t _ksba_parse_algorithm_identifier2 (const unsigned char *der, size_t derlen, size_t *r_nread, char **r_oid, char **r_parm, size_t *r_parmlen) { gpg_error_t err; int is_bitstr; size_t nread, off, len, off2, len2; int parm_type; /* fixme: get_algorithm might return the error invalid keyinfo - this should be invalid algorithm identifier */ *r_oid = NULL; if (r_nread) *r_nread = 0; off2 = len2 = 0; err = get_algorithm (0, der, derlen, &nread, &off, &len, &is_bitstr, &off2, &len2, &parm_type); if (err) return err; if (r_nread) *r_nread = nread; *r_oid = ksba_oid_to_str (der+off, len); if (!*r_oid) return gpg_error (GPG_ERR_ENOMEM); /* Special hack for ecdsaWithSpecified. We replace the returned OID by the one in the parameter. */ if (off2 && len2 && parm_type == TYPE_SEQUENCE && !strcmp (*r_oid, "1.2.840.10045.4.3")) { xfree (*r_oid); *r_oid = NULL; err = get_algorithm (0, der+off2, len2, &nread, &off, &len, &is_bitstr, NULL, NULL, NULL); if (err) { if (r_nread) *r_nread = 0; return err; } *r_oid = ksba_oid_to_str (der+off2+off, len); if (!*r_oid) { if (r_nread) *r_nread = 0; return gpg_error (GPG_ERR_ENOMEM); } off2 = len2 = 0; /* So that R_PARM is set to NULL. */ } if (r_parm && r_parmlen) { if (off2 && len2) { *r_parm = xtrymalloc (len2); if (!*r_parm) { xfree (*r_oid); *r_oid = NULL; return gpg_error (GPG_ERR_ENOMEM); } memcpy (*r_parm, der+off2, len2); *r_parmlen = len2; } else { *r_parm = NULL; *r_parmlen = 0; } } return 0; } /* Assume that der is a buffer of length DERLEN with a DER encoded ASN.1 structure like this: keyInfo ::= SEQUENCE { SEQUENCE { algorithm OBJECT IDENTIFIER, parameters ANY DEFINED BY algorithm OPTIONAL } publicKey BIT STRING } The function parses this structure and create a SEXP suitable to be used as a public key in Libgcrypt. The S-Exp will be returned in a string which the caller must free. We don't pass an ASN.1 node here but a plain memory block. */ gpg_error_t _ksba_keyinfo_to_sexp (const unsigned char *der, size_t derlen, ksba_sexp_t *r_string) { gpg_error_t err; int c; size_t nread, off, len, parm_off, parm_len; int parm_type; char *parm_oid = NULL; int algoidx; int is_bitstr; const unsigned char *parmder = NULL; size_t parmderlen = 0; const unsigned char *ctrl; const char *elem; struct stringbuf sb; *r_string = NULL; /* check the outer sequence */ if (!derlen) return gpg_error (GPG_ERR_INV_KEYINFO); c = *der++; derlen--; if ( c != 0x30 ) return gpg_error (GPG_ERR_UNEXPECTED_TAG); /* not a SEQUENCE */ TLV_LENGTH(der); /* and now the inner part */ err = get_algorithm (1, der, derlen, &nread, &off, &len, &is_bitstr, &parm_off, &parm_len, &parm_type); if (err) return err; /* look into our table of supported algorithms */ for (algoidx=0; pk_algo_table[algoidx].oid; algoidx++) { if ( len == pk_algo_table[algoidx].oidlen && !memcmp (der+off, pk_algo_table[algoidx].oid, len)) break; } if (!pk_algo_table[algoidx].oid) return gpg_error (GPG_ERR_UNKNOWN_ALGORITHM); if (!pk_algo_table[algoidx].supported) return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); if (parm_off && parm_len && parm_type == TYPE_OBJECT_ID) parm_oid = ksba_oid_to_str (der+parm_off, parm_len); else if (parm_off && parm_len) { parmder = der + parm_off; parmderlen = parm_len; } der += nread; derlen -= nread; if (is_bitstr) { /* Funny: X.509 defines the signature value as a bit string but CMS as an octet string - for ease of implementation we always allow both */ if (!derlen) { xfree (parm_oid); return gpg_error (GPG_ERR_INV_KEYINFO); } c = *der++; derlen--; if (c) fprintf (stderr, "warning: number of unused bits is not zero\n"); } /* fixme: we should calculate the initial length form the size of the sequence, so that we don't need a realloc later */ init_stringbuf (&sb, 100); put_stringbuf (&sb, "(10:public-key("); /* fixme: we can also use the oidstring here and prefix it with "oid." - this way we can pass more information into Libgcrypt or whatever library is used */ put_stringbuf_sexp (&sb, pk_algo_table[algoidx].algo_string); /* Insert the curve name for ECC. */ if (pk_algo_table[algoidx].pkalgo == PKALGO_ECC && parm_oid) { put_stringbuf (&sb, "("); put_stringbuf_sexp (&sb, "curve"); put_stringbuf_sexp (&sb, parm_oid); put_stringbuf (&sb, ")"); } else if (pk_algo_table[algoidx].pkalgo == PKALGO_ED25519 || pk_algo_table[algoidx].pkalgo == PKALGO_ED448 || pk_algo_table[algoidx].pkalgo == PKALGO_X25519 || pk_algo_table[algoidx].pkalgo == PKALGO_X448) { put_stringbuf (&sb, "("); put_stringbuf_sexp (&sb, "curve"); put_stringbuf_sexp (&sb, pk_algo_table[algoidx].oidstring); put_stringbuf (&sb, ")"); } /* If parameters are given and we have a description for them, parse them. */ if (parmder && parmderlen && pk_algo_table[algoidx].parmelem_string && pk_algo_table[algoidx].parmctrl_string) { elem = pk_algo_table[algoidx].parmelem_string; ctrl = pk_algo_table[algoidx].parmctrl_string; for (; *elem; ctrl++, elem++) { int is_int; if ( (*ctrl & 0x80) && !elem[1] ) { /* Hack to allow reading a raw value. */ is_int = 1; len = parmderlen; } else { if (!parmderlen) { xfree (parm_oid); return gpg_error (GPG_ERR_INV_KEYINFO); } c = *parmder++; parmderlen--; if ( c != *ctrl ) { xfree (parm_oid); return gpg_error (GPG_ERR_UNEXPECTED_TAG); } is_int = c == 0x02; TLV_LENGTH (parmder); } if (is_int && *elem != '-') /* Take this integer. */ { char tmp[2]; put_stringbuf (&sb, "("); tmp[0] = *elem; tmp[1] = 0; put_stringbuf_sexp (&sb, tmp); put_stringbuf_mem_sexp (&sb, parmder, len); parmder += len; parmderlen -= len; put_stringbuf (&sb, ")"); } } } /* FIXME: We don't release the stringbuf in case of error better let the macro jump to a label */ elem = pk_algo_table[algoidx].elem_string; ctrl = pk_algo_table[algoidx].ctrl_string; for (; *elem; ctrl++, elem++) { int is_int; if ( (*ctrl & 0x80) && !elem[1] ) { /* Hack to allow reading a raw value. */ is_int = 1; len = derlen; } else { if (!derlen) { xfree (parm_oid); return gpg_error (GPG_ERR_INV_KEYINFO); } c = *der++; derlen--; if ( c != *ctrl ) { xfree (parm_oid); return gpg_error (GPG_ERR_UNEXPECTED_TAG); } is_int = c == 0x02; TLV_LENGTH (der); } if (is_int && *elem != '-') /* Take this integer. */ { char tmp[2]; put_stringbuf (&sb, "("); tmp[0] = *elem; tmp[1] = 0; put_stringbuf_sexp (&sb, tmp); put_stringbuf_mem_sexp (&sb, der, len); der += len; derlen -= len; put_stringbuf (&sb, ")"); } } put_stringbuf (&sb, "))"); xfree (parm_oid); *r_string = get_stringbuf (&sb); if (!*r_string) return gpg_error (GPG_ERR_ENOMEM); return 0; } /* Match the algorithm string given in BUF which is of length BUFLEN with the known algorithms from our table and returns the table entries for the DER encoded OID. If WITH_SIG is true, the table of signature algorithms is consulted first. */ static const unsigned char * oid_from_buffer (const unsigned char *buf, int buflen, int *oidlen, pkalgo_t *r_pkalgo, int with_sig) { int i; /* Ignore an optional "oid." prefix. */ if (buflen > 4 && buf[3] == '.' && digitp (buf+4) && ((buf[0] == 'o' && buf[1] == 'i' && buf[2] == 'd') ||(buf[0] == 'O' && buf[1] == 'I' && buf[2] == 'D'))) { buf += 4; buflen -= 4; } if (with_sig) { /* Scan the signature table first. */ for (i=0; sig_algo_table[i].oid; i++) { if (!sig_algo_table[i].supported) continue; if (buflen == strlen (sig_algo_table[i].oidstring) && !memcmp (buf, sig_algo_table[i].oidstring, buflen)) break; if (buflen == strlen (sig_algo_table[i].algo_string) && !memcmp (buf, sig_algo_table[i].algo_string, buflen)) break; } if (sig_algo_table[i].oid) { *r_pkalgo = sig_algo_table[i].pkalgo; *oidlen = sig_algo_table[i].oidlen; return sig_algo_table[i].oid; } } /* Scan the standard table. */ for (i=0; pk_algo_table[i].oid; i++) { if (!pk_algo_table[i].supported) continue; if (buflen == strlen (pk_algo_table[i].oidstring) && !memcmp (buf, pk_algo_table[i].oidstring, buflen)) break; if (buflen == strlen (pk_algo_table[i].algo_string) && !memcmp (buf, pk_algo_table[i].algo_string, buflen)) break; } if (!pk_algo_table[i].oid) return NULL; *r_pkalgo = pk_algo_table[i].pkalgo; *oidlen = pk_algo_table[i].oidlen; return pk_algo_table[i].oid; } -/* Take a public-key S-Exp and convert it into a DER encoded - publicKeyInfo */ +/* If ALGOINFOMODE is false: Take the "public-key" s-expression SEXP + * and convert it into a DER encoded publicKeyInfo. + * + * If ALGOINFOMODE is true: Take the "sig-val" s-expression SEXP and + * convert it into a DER encoded algorithmInfo. */ gpg_error_t -_ksba_keyinfo_from_sexp (ksba_const_sexp_t sexp, +_ksba_keyinfo_from_sexp (ksba_const_sexp_t sexp, int algoinfomode, unsigned char **r_der, size_t *r_derlen) { gpg_error_t err; const unsigned char *s; char *endp; unsigned long n, n1; const unsigned char *oid; int oidlen; unsigned char *curve_oid = NULL; size_t curve_oidlen; pkalgo_t pkalgo; int i; struct { const char *name; int namelen; const unsigned char *value; int valuelen; } parm[10]; int parmidx; int idxtbl[10]; int idxtbllen; const char *parmdesc, *algoparmdesc; ksba_writer_t writer = NULL; void *algoparmseq_value = NULL; size_t algoparmseq_len; void *bitstr_value = NULL; size_t bitstr_len; if (!sexp) return gpg_error (GPG_ERR_INV_VALUE); s = sexp; if (*s != '(') return gpg_error (GPG_ERR_INV_SEXP); s++; n = strtoul (s, &endp, 10); s = endp; if (!n || *s != ':') - return gpg_error (GPG_ERR_INV_SEXP); /* we don't allow empty lengths */ + return gpg_error (GPG_ERR_INV_SEXP); /* We don't allow empty lengths. */ s++; - if (n != 10 || memcmp (s, "public-key", 10)) + + if (algoinfomode && n == 7 && !memcmp (s, "sig-val", 7)) + s += 7; + else if (n == 10 || !memcmp (s, "public-key", 10)) + s += 10; + else return gpg_error (GPG_ERR_UNKNOWN_SEXP); - s += 10; + if (*s != '(') return gpg_error (digitp (s)? GPG_ERR_UNKNOWN_SEXP : GPG_ERR_INV_SEXP); s++; /* Break out the algorithm ID */ n = strtoul (s, &endp, 10); s = endp; if (!n || *s != ':') - return gpg_error (GPG_ERR_INV_SEXP); /* we don't allow empty lengths */ + return gpg_error (GPG_ERR_INV_SEXP); /* We don't allow empty lengths. */ s++; - oid = oid_from_buffer (s, n, &oidlen, &pkalgo, 0); + + oid = oid_from_buffer (s, n, &oidlen, &pkalgo, algoinfomode); if (!oid) return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); s += n; /* Collect all the values */ for (parmidx = 0; *s != ')' ; parmidx++) { if (parmidx >= DIM(parm)) return gpg_error (GPG_ERR_GENERAL); if (*s != '(') return gpg_error (digitp(s)? GPG_ERR_UNKNOWN_SEXP:GPG_ERR_INV_SEXP); s++; n = strtoul (s, &endp, 10); s = endp; if (!n || *s != ':') return gpg_error (GPG_ERR_INV_SEXP); s++; parm[parmidx].name = s; parm[parmidx].namelen = n; s += n; if (!digitp(s)) return gpg_error (GPG_ERR_UNKNOWN_SEXP); /* ... or invalid S-Exp. */ n = strtoul (s, &endp, 10); s = endp; if (!n || *s != ':') return gpg_error (GPG_ERR_INV_SEXP); s++; parm[parmidx].value = s; parm[parmidx].valuelen = n; s += n; if ( *s != ')') return gpg_error (GPG_ERR_UNKNOWN_SEXP); /* ... or invalid S-Exp. */ s++; } s++; /* Allow for optional elements. */ if (*s == '(') { int depth = 1; err = sskip (&s, &depth); if (err) return err; } /* We need another closing parenthesis. */ if ( *s != ')' ) return gpg_error (GPG_ERR_INV_SEXP); /* Describe the parameters in the order we want them and construct IDXTBL to access them. For DSA wie also set algoparmdesc so that we can later build the parameters for the algorithmIdentifier. */ algoparmdesc = NULL; switch (pkalgo) { - case PKALGO_RSA: parmdesc = "ne"; break; - case PKALGO_DSA: parmdesc = "y" ; algoparmdesc = "pqg"; break; + case PKALGO_RSA: + parmdesc = algoinfomode? "" : "ne"; + break; + case PKALGO_DSA: + parmdesc = algoinfomode? "" : "y"; + algoparmdesc = "pqg"; + break; case PKALGO_ECC: - parmdesc = "Cq"; - for (i = 0; i < parmidx; i++) + parmdesc = algoinfomode? "C" : "Cq"; + for (i = 0; !algoinfomode && i < parmidx; i++) if (parm[i].namelen == 5 && !memcmp (parm[i].name,"curve",5)) { /* FIXME: Access to pk_algo_table with constant is ugly. */ if (parm[i].valuelen == 7 && !memcmp (parm[i].value, "Ed25519", 7)) { pkalgo = PKALGO_ED25519; parmdesc = "q"; oid = pk_algo_table[7].oid; oidlen = pk_algo_table[7].oidlen; break; } else if (parm[i].valuelen == 5 && !memcmp (parm[i].value, "Ed448", 5)) { pkalgo = PKALGO_ED448; parmdesc = "q"; oid = pk_algo_table[8].oid; oidlen = pk_algo_table[8].oidlen; break; } } break; default: return gpg_error (GPG_ERR_UNKNOWN_ALGORITHM); } idxtbllen = 0; for (s = parmdesc; *s; s++) { for (i=0; i < parmidx; i++) { assert (idxtbllen < DIM (idxtbl)); switch (*s) { case 'C': /* Magic value for "curve". */ if (parm[i].namelen == 5 && !memcmp (parm[i].name, "curve", 5)) { idxtbl[idxtbllen++] = i; i = parmidx; /* Break inner loop. */ } break; default: if (parm[i].namelen == 1 && parm[i].name[0] == *s) { idxtbl[idxtbllen++] = i; i = parmidx; /* Break inner loop. */ } break; } } } if (idxtbllen != strlen (parmdesc)) return gpg_error (GPG_ERR_UNKNOWN_SEXP); if (pkalgo == PKALGO_ECC) { curve_oid = get_ecc_curve_oid (parm[idxtbl[0]].value, parm[idxtbl[0]].valuelen, &curve_oidlen); if (!curve_oid) return gpg_error (GPG_ERR_UNKNOWN_SEXP); } /* Create write object. */ err = ksba_writer_new (&writer); if (err) goto leave; err = ksba_writer_set_mem (writer, 1024); if (err) goto leave; /* We create the keyinfo in 2 steps: 1. We build the inner one and encapsulate it in a bit string. 2. We create the outer sequence include the algorithm identifier and the bit string from step 1. */ - if (pkalgo == PKALGO_ECC) + if (algoinfomode) + ; + else if (pkalgo == PKALGO_ECC) { /* Write the bit string header and the number of unused bits. */ err = _ksba_ber_write_tl (writer, TYPE_BIT_STRING, CLASS_UNIVERSAL, 0, parm[idxtbl[1]].valuelen + 1); if (!err) err = ksba_writer_write (writer, "", 1); /* And the actual raw value. */ if (!err) err = ksba_writer_write (writer, parm[idxtbl[1]].value, parm[idxtbl[1]].valuelen); if (err) goto leave; } else if (pkalgo == PKALGO_ED25519 || pkalgo == PKALGO_ED448) { /* Write the bit string header and the number of unused bits. */ err = _ksba_ber_write_tl (writer, TYPE_BIT_STRING, CLASS_UNIVERSAL, 0, parm[idxtbl[0]].valuelen + 1); if (!err) err = ksba_writer_write (writer, "", 1); /* And the actual raw value. */ if (!err) err = ksba_writer_write (writer, parm[idxtbl[0]].value, parm[idxtbl[0]].valuelen); if (err) goto leave; } else /* RSA and DSA */ { /* Calculate the size of the sequence value and the size of the bit string value. Note that in case there is only one integer to write, no sequence is used. */ for (n=0, i=0; i < idxtbllen; i++ ) { n += _ksba_ber_count_tl (TYPE_INTEGER, CLASS_UNIVERSAL, 0, parm[idxtbl[i]].valuelen); n += parm[idxtbl[i]].valuelen; } n1 = 1; /* # of unused bits. */ if (idxtbllen > 1) n1 += _ksba_ber_count_tl (TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, n); n1 += n; /* Write the bit string header and the number of unused bits. */ err = _ksba_ber_write_tl (writer, TYPE_BIT_STRING, CLASS_UNIVERSAL, 0, n1); if (!err) err = ksba_writer_write (writer, "", 1); if (err) goto leave; /* Write the sequence tag and the integers. */ if (idxtbllen > 1) err = _ksba_ber_write_tl (writer, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1,n); if (err) goto leave; for (i=0; i < idxtbllen; i++) { /* fixme: we should make sure that the integer conforms to the ASN.1 encoding rules. */ err = _ksba_ber_write_tl (writer, TYPE_INTEGER, CLASS_UNIVERSAL, 0, parm[idxtbl[i]].valuelen); if (!err) err = ksba_writer_write (writer, parm[idxtbl[i]].value, parm[idxtbl[i]].valuelen); if (err) goto leave; } } - /* Get the encoded bit string. */ - bitstr_value = ksba_writer_snatch_mem (writer, &bitstr_len); - if (!bitstr_value) + if (!algoinfomode) { - err = gpg_error (GPG_ERR_ENOMEM); - goto leave; + /* Get the encoded bit string. */ + bitstr_value = ksba_writer_snatch_mem (writer, &bitstr_len); + if (!bitstr_value) + { + err = gpg_error (GPG_ERR_ENOMEM); + goto leave; + } } + /* Create the sequence of the algorithm identifier. */ + /* If the algorithmIdentifier requires a sequence with parameters, build them now. We can reuse the IDXTBL for that. */ if (algoparmdesc) { idxtbllen = 0; for (s = algoparmdesc; *s; s++) { for (i=0; i < parmidx; i++) { assert (idxtbllen < DIM (idxtbl)); if (parm[i].namelen == 1 && parm[i].name[0] == *s) { idxtbl[idxtbllen++] = i; break; } } } if (idxtbllen != strlen (algoparmdesc)) return gpg_error (GPG_ERR_UNKNOWN_SEXP); err = ksba_writer_set_mem (writer, 1024); if (err) goto leave; /* Calculate the size of the sequence. */ for (n=0, i=0; i < idxtbllen; i++ ) { n += _ksba_ber_count_tl (TYPE_INTEGER, CLASS_UNIVERSAL, 0, parm[idxtbl[i]].valuelen); n += parm[idxtbl[i]].valuelen; } /* n += _ksba_ber_count_tl (TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, n); */ /* Write the sequence tag followed by the integers. */ err = _ksba_ber_write_tl (writer, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, n); if (err) goto leave; for (i=0; i < idxtbllen; i++) { err = _ksba_ber_write_tl (writer, TYPE_INTEGER, CLASS_UNIVERSAL, 0, parm[idxtbl[i]].valuelen); if (!err) err = ksba_writer_write (writer, parm[idxtbl[i]].value, parm[idxtbl[i]].valuelen); if (err) goto leave; } /* Get the encoded sequence. */ algoparmseq_value = ksba_writer_snatch_mem (writer, &algoparmseq_len); if (!algoparmseq_value) { err = gpg_error (GPG_ERR_ENOMEM); goto leave; } } else algoparmseq_len = 0; - /* Reinitialize the buffer to create the outer sequence. */ + /* Reinitialize the buffer to create the (outer) sequence. */ err = ksba_writer_set_mem (writer, 1024); if (err) goto leave; - /* Calulate lengths. */ + /* Calculate lengths. */ n = _ksba_ber_count_tl (TYPE_OBJECT_ID, CLASS_UNIVERSAL, 0, oidlen); n += oidlen; if (algoparmseq_len) { n += algoparmseq_len; } else if (pkalgo == PKALGO_ECC) { n += _ksba_ber_count_tl (TYPE_OBJECT_ID, CLASS_UNIVERSAL, 0, curve_oidlen); n += curve_oidlen; } else if (pkalgo == PKALGO_RSA) { n += _ksba_ber_count_tl (TYPE_NULL, CLASS_UNIVERSAL, 0, 0); } - n1 = n; - n1 += _ksba_ber_count_tl (TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, n); - n1 += bitstr_len; + if (!algoinfomode) + { + n1 = n; + n1 += _ksba_ber_count_tl (TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, n); + n1 += bitstr_len; - /* The outer sequence. */ - err = _ksba_ber_write_tl (writer, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, n1); - if (err) - goto leave; + /* The outer sequence. */ + err = _ksba_ber_write_tl (writer, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, n1); + if (err) + goto leave; + } /* The sequence. */ err = _ksba_ber_write_tl (writer, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, n); if (err) goto leave; /* The object id. */ err = _ksba_ber_write_tl (writer, TYPE_OBJECT_ID,CLASS_UNIVERSAL, 0, oidlen); if (!err) err = ksba_writer_write (writer, oid, oidlen); if (err) goto leave; /* The parameter. */ if (algoparmseq_len) { err = ksba_writer_write (writer, algoparmseq_value, algoparmseq_len); } else if (pkalgo == PKALGO_ECC) { + /* We only support the namedCuve choice for ECC parameters. */ err = _ksba_ber_write_tl (writer, TYPE_OBJECT_ID, CLASS_UNIVERSAL, 0, curve_oidlen); if (!err) err = ksba_writer_write (writer, curve_oid, curve_oidlen); } else if (pkalgo == PKALGO_RSA) { err = _ksba_ber_write_tl (writer, TYPE_NULL, CLASS_UNIVERSAL, 0, 0); } if (err) goto leave; - /* Append the pre-constructed bit string. */ - err = ksba_writer_write (writer, bitstr_value, bitstr_len); - if (err) - goto leave; - - /* Get the result. */ - *r_der = ksba_writer_snatch_mem (writer, r_derlen); - if (!*r_der) - err = gpg_error (GPG_ERR_ENOMEM); - - leave: - ksba_writer_release (writer); - xfree (bitstr_value); - xfree (curve_oid); - return err; -} - - -/* Take a sig-val s-expression and convert it into a DER encoded - algorithmInfo. Unfortunately this function clones a lot of code - from _ksba_keyinfo_from_sexp. */ -gpg_error_t -_ksba_algoinfo_from_sexp (ksba_const_sexp_t sexp, - unsigned char **r_der, size_t *r_derlen) -{ - gpg_error_t err; - const unsigned char *s; - char *endp; - unsigned long n; - const unsigned char *oid; - int oidlen; - unsigned char *curve_oid = NULL; - size_t curve_oidlen; - pkalgo_t pkalgo; - int i; - struct { - const char *name; - int namelen; - const unsigned char *value; - int valuelen; - } parm[10]; - int parmidx; - int idxtbl[10]; - int idxtbllen; - const char *parmdesc, *algoparmdesc; - ksba_writer_t writer = NULL; - void *algoparmseq_value = NULL; - size_t algoparmseq_len; - - if (!sexp) - return gpg_error (GPG_ERR_INV_VALUE); - - s = sexp; - if (*s != '(') - return gpg_error (GPG_ERR_INV_SEXP); - s++; - - n = strtoul (s, &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, "sig-val", 7)) - s += 7; - else if (n == 10 && !memcmp (s, "public-key", 10)) - s += 10; - else - 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 */ - n = strtoul (s, &endp, 10); - s = endp; - if (!n || *s != ':') - return gpg_error (GPG_ERR_INV_SEXP); /* We don't allow empty lengths. */ - s++; - oid = oid_from_buffer (s, n, &oidlen, &pkalgo, 1); - if (!oid) - return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); - s += n; - - /* Collect all the values */ - for (parmidx = 0; *s != ')' ; parmidx++) - { - if (parmidx >= DIM(parm)) - return gpg_error (GPG_ERR_GENERAL); - if (*s != '(') - return gpg_error (digitp(s)? GPG_ERR_UNKNOWN_SEXP:GPG_ERR_INV_SEXP); - s++; - n = strtoul (s, &endp, 10); - s = endp; - if (!n || *s != ':') - return gpg_error (GPG_ERR_INV_SEXP); - s++; - parm[parmidx].name = s; - parm[parmidx].namelen = n; - s += n; - if (!digitp(s)) - return gpg_error (GPG_ERR_UNKNOWN_SEXP); /* ... or invalid S-Exp. */ - - n = strtoul (s, &endp, 10); - s = endp; - if (!n || *s != ':') - return gpg_error (GPG_ERR_INV_SEXP); - s++; - parm[parmidx].value = s; - parm[parmidx].valuelen = n; - s += n; - if ( *s != ')') - return gpg_error (GPG_ERR_UNKNOWN_SEXP); /* ... or invalid S-Exp. */ - s++; - } - s++; - /* Allow for optional elements. */ - if (*s == '(') + if (!algoinfomode) { - int depth = 1; - err = sskip (&s, &depth); - if (err) - return err; - } - /* We need another closing parenthesis. */ - if ( *s != ')' ) - return gpg_error (GPG_ERR_INV_SEXP); - - /* Describe the parameters in the order we want them and construct - IDXTBL to access them. For DSA wie also set algoparmdesc so - that we can later build the parameters for the - algorithmIdentifier. */ - algoparmdesc = NULL; - switch (pkalgo) - { - case PKALGO_RSA: parmdesc = ""; break; - case PKALGO_DSA: parmdesc = "" ; algoparmdesc = "pqg"; break; - case PKALGO_ECC: parmdesc = "C"; break; - default: return gpg_error (GPG_ERR_UNKNOWN_ALGORITHM); - } - - idxtbllen = 0; - for (s = parmdesc; *s; s++) - { - for (i=0; i < parmidx; i++) - { - assert (idxtbllen < DIM (idxtbl)); - switch (*s) - { - case 'C': /* Magic value for "curve". */ - if (parm[i].namelen == 5 && !memcmp (parm[i].name, "curve", 5)) - { - idxtbl[idxtbllen++] = i; - i = parmidx; /* Break inner loop. */ - } - break; - default: - if (parm[i].namelen == 1 && parm[i].name[0] == *s) - { - idxtbl[idxtbllen++] = i; - i = parmidx; /* Break inner loop. */ - } - break; - } - } - } - if (idxtbllen != strlen (parmdesc)) - return gpg_error (GPG_ERR_UNKNOWN_SEXP); - - if (pkalgo == PKALGO_ECC) - { - curve_oid = get_ecc_curve_oid (parm[idxtbl[0]].value, - parm[idxtbl[0]].valuelen, - &curve_oidlen); - if (!curve_oid) - return gpg_error (GPG_ERR_UNKNOWN_SEXP); - } - - - /* Create write object. */ - err = ksba_writer_new (&writer); - if (err) - goto leave; - err = ksba_writer_set_mem (writer, 1024); - if (err) - goto leave; - - /* Create the sequence of the algorithm identifier. */ - - /* If the algorithmIdentifier requires a sequence with parameters, - build them now. We can reuse the IDXTBL for that. */ - if (algoparmdesc) - { - idxtbllen = 0; - for (s = algoparmdesc; *s; s++) - { - for (i=0; i < parmidx; i++) - { - assert (idxtbllen < DIM (idxtbl)); - if (parm[i].namelen == 1 && parm[i].name[0] == *s) - { - idxtbl[idxtbllen++] = i; - break; - } - } - } - if (idxtbllen != strlen (algoparmdesc)) - return gpg_error (GPG_ERR_UNKNOWN_SEXP); - - err = ksba_writer_set_mem (writer, 1024); + /* Append the pre-constructed bit string. */ + err = ksba_writer_write (writer, bitstr_value, bitstr_len); if (err) goto leave; - - /* Calculate the size of the sequence. */ - for (n=0, i=0; i < idxtbllen; i++ ) - { - n += _ksba_ber_count_tl (TYPE_INTEGER, CLASS_UNIVERSAL, 0, - parm[idxtbl[i]].valuelen); - n += parm[idxtbl[i]].valuelen; - } - - /* Write the sequence tag followed by the integers. */ - err = _ksba_ber_write_tl (writer, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, n); - if (err) - goto leave; - for (i=0; i < idxtbllen; i++) - { - err = _ksba_ber_write_tl (writer, TYPE_INTEGER, CLASS_UNIVERSAL, 0, - parm[idxtbl[i]].valuelen); - if (!err) - err = ksba_writer_write (writer, parm[idxtbl[i]].value, - parm[idxtbl[i]].valuelen); - if (err) - goto leave; - } - - /* Get the encoded sequence. */ - algoparmseq_value = ksba_writer_snatch_mem (writer, &algoparmseq_len); - if (!algoparmseq_value) - { - err = gpg_error (GPG_ERR_ENOMEM); - goto leave; - } } - else - algoparmseq_len = 0; - - /* Reinitialize the buffer to create the sequence. */ - err = ksba_writer_set_mem (writer, 1024); - if (err) - goto leave; - - /* Calulate lengths. */ - n = _ksba_ber_count_tl (TYPE_OBJECT_ID, CLASS_UNIVERSAL, 0, oidlen); - n += oidlen; - if (algoparmseq_len) - { - n += algoparmseq_len; - } - else if (pkalgo == PKALGO_ECC) - { - n += _ksba_ber_count_tl (TYPE_OBJECT_ID, CLASS_UNIVERSAL, - 0, curve_oidlen); - n += curve_oidlen; - } - else if (pkalgo == PKALGO_RSA) - { - n += _ksba_ber_count_tl (TYPE_NULL, CLASS_UNIVERSAL, 0, 0); - } - - /* Write the sequence. */ - err = _ksba_ber_write_tl (writer, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, n); - if (err) - goto leave; - - /* Write the object id. */ - err = _ksba_ber_write_tl (writer, TYPE_OBJECT_ID, CLASS_UNIVERSAL, 0, oidlen); - if (!err) - err = ksba_writer_write (writer, oid, oidlen); - if (err) - goto leave; - - /* Write the parameters. */ - if (algoparmseq_len) - { - err = ksba_writer_write (writer, algoparmseq_value, algoparmseq_len); - } - else if (pkalgo == PKALGO_ECC) - { - /* We only support the namedCuve choice for ECC parameters. */ - err = _ksba_ber_write_tl (writer, TYPE_OBJECT_ID, CLASS_UNIVERSAL, - 0, curve_oidlen); - if (!err) - err = ksba_writer_write (writer, curve_oid, curve_oidlen); - } - else if (pkalgo == PKALGO_RSA) - { - err = _ksba_ber_write_tl (writer, TYPE_NULL, CLASS_UNIVERSAL, 0, 0); - } - if (err) - goto leave; /* Get the result. */ *r_der = ksba_writer_snatch_mem (writer, r_derlen); if (!*r_der) err = gpg_error (GPG_ERR_ENOMEM); leave: ksba_writer_release (writer); + xfree (bitstr_value); xfree (curve_oid); return err; } - /* Helper function to parse the parameters used for rsaPSS. * Given this sample DER object in (DER,DERLEN): * * SEQUENCE { * [0] { * SEQUENCE { * OBJECT IDENTIFIER sha-512 (2 16 840 1 101 3 4 2 3) * } * } * [1] { * SEQUENCE { * OBJECT IDENTIFIER pkcs1-MGF (1 2 840 113549 1 1 8) * SEQUENCE { * OBJECT IDENTIFIER sha-512 (2 16 840 1 101 3 4 2 3) * } * } * } * [2] { * INTEGER 64 * } * } * * The function returns the first OID at R_PSSHASH and the salt length * at R_SALTLEN. If the salt length is missing its default value is * returned. In case object does not resemble a the expected rsaPSS * parameters GPG_ERR_INV_OBJ is returned; other errors are returned * for an syntatically invalid object. On error NULL is stored at * R_PSSHASH. */ gpg_error_t _ksba_keyinfo_get_pss_info (const unsigned char *der, size_t derlen, char **r_psshash, unsigned int *r_saltlen) { gpg_error_t err; struct tag_info ti; char *psshash = NULL; char *tmpoid = NULL; unsigned int saltlen; *r_psshash = NULL; *r_saltlen = 0; err = parse_sequence (&der, &derlen, &ti); if (err) goto leave; /* Get the hash algo. */ err = parse_context_tag (&der, &derlen, &ti, 0); if (err) goto unknown_parms; err = parse_sequence (&der, &derlen, &ti); if (err) goto unknown_parms; err = parse_object_id_into_str (&der, &derlen, &psshash); if (err) goto unknown_parms; err = parse_optional_null (&der, &derlen, NULL); if (err) goto unknown_parms; /* Check the MGF OID and that its hash algo matches. */ err = parse_context_tag (&der, &derlen, &ti, 1); if (err) goto unknown_parms; err = parse_sequence (&der, &derlen, &ti); if (err) goto leave; err = parse_object_id_into_str (&der, &derlen, &tmpoid); if (err) goto unknown_parms; if (strcmp (tmpoid, "1.2.840.113549.1.1.8")) /* MGF1 */ goto unknown_parms; err = parse_sequence (&der, &derlen, &ti); if (err) goto leave; xfree (tmpoid); err = parse_object_id_into_str (&der, &derlen, &tmpoid); if (err) goto unknown_parms; if (strcmp (tmpoid, psshash)) goto unknown_parms; err = parse_optional_null (&der, &derlen, NULL); if (err) goto unknown_parms; /* Get the optional saltLength. */ err = parse_context_tag (&der, &derlen, &ti, 2); if (gpg_err_code (err) == GPG_ERR_INV_OBJ || gpg_err_code (err) == GPG_ERR_FALSE) saltlen = 20; /* Optional element - use default value */ else if (err) goto unknown_parms; else { err = parse_integer (&der, &derlen, &ti); if (err) goto leave; for (saltlen=0; ti.length; ti.length--) { saltlen <<= 8; saltlen |= (*der++) & 0xff; derlen--; } } /* All fine. */ *r_psshash = psshash; psshash = NULL; *r_saltlen = saltlen; err = 0; goto leave; unknown_parms: err = gpg_error (GPG_ERR_INV_OBJ); leave: xfree (psshash); xfree (tmpoid); return err; } /* Mode 0: work as described under _ksba_sigval_to_sexp * mode 1: work as described under _ksba_encval_to_sexp * mode 2: same as mode 1 but for ECDH; in this mode * KEYENCRYALO, KEYWRAPALGO, ENCRKEY, ENCRYKLEYLEN * are also required. */ static gpg_error_t cryptval_to_sexp (int mode, const unsigned char *der, size_t derlen, const char *keyencralgo, const char *keywrapalgo, const void *encrkey, size_t encrkeylen, ksba_sexp_t *r_string) { gpg_error_t err; const struct algo_table_s *algo_table; int c; size_t nread, off, len; int algoidx; int is_bitstr; const unsigned char *ctrl; const char *elem; struct stringbuf sb; size_t parm_off, parm_len; int parm_type; char *pss_hash = NULL; unsigned int salt_length = 0; /* FIXME: The entire function is very similar to keyinfo_to_sexp */ *r_string = NULL; if (!mode) algo_table = sig_algo_table; else algo_table = enc_algo_table; err = get_algorithm (1, der, derlen, &nread, &off, &len, &is_bitstr, &parm_off, &parm_len, &parm_type); if (err) return err; /* look into our table of supported algorithms */ for (algoidx=0; algo_table[algoidx].oid; algoidx++) { if ( len == algo_table[algoidx].oidlen && !memcmp (der+off, algo_table[algoidx].oid, len)) break; } if (!algo_table[algoidx].oid) return gpg_error (GPG_ERR_UNKNOWN_ALGORITHM); if (!algo_table[algoidx].supported) return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); if (parm_type == TYPE_SEQUENCE && algo_table[algoidx].supported == SUPPORTED_RSAPSS) { /* This is rsaPSS and we collect the parameters. We simplify * this by assuming that pkcs1-MGF is used with an identical * hash algorithm. All other kinds of parameters are ignored. */ err = _ksba_keyinfo_get_pss_info (der + parm_off, parm_len, &pss_hash, &salt_length); if (gpg_err_code (err) == GPG_ERR_INV_OBJ) err = 0; if (err) return err; } der += nread; derlen -= nread; if (is_bitstr) { /* Funny: X.509 defines the signature value as a bit string but CMS as an octet string - for ease of implementation we always allow both */ if (!derlen) return gpg_error (GPG_ERR_INV_KEYINFO); c = *der++; derlen--; if (c) fprintf (stderr, "warning: number of unused bits is not zero\n"); } /* fixme: we should calculate the initial length form the size of the sequence, so that we don't neen a realloc later */ init_stringbuf (&sb, 100); put_stringbuf (&sb, mode? "(7:enc-val(":"(7:sig-val("); put_stringbuf_sexp (&sb, algo_table[algoidx].algo_string); /* FIXME: We don't release the stringbuf in case of error better let the macro jump to a label */ if (!mode && (algo_table[algoidx].pkalgo == PKALGO_ED25519 ||algo_table[algoidx].pkalgo == PKALGO_ED448)) { /* EdDSA is special: R and S are simply concatenated; see rfc8410. */ put_stringbuf (&sb, "(1:r"); put_stringbuf_mem_sexp (&sb, der, derlen/2); put_stringbuf (&sb, ")"); der += derlen/2; derlen /= 2; put_stringbuf (&sb, "(1:s"); put_stringbuf_mem_sexp (&sb, der, derlen); put_stringbuf (&sb, ")"); } else { elem = algo_table[algoidx].elem_string; ctrl = algo_table[algoidx].ctrl_string; for (; *elem; ctrl++, elem++) { int is_int; if ( (*ctrl & 0x80) && !elem[1] ) { /* Hack to allow a raw value */ is_int = 1; len = derlen; } else { if (!derlen) return gpg_error (GPG_ERR_INV_KEYINFO); c = *der++; derlen--; if ( c != *ctrl ) return gpg_error (GPG_ERR_UNEXPECTED_TAG); is_int = c == 0x02; TLV_LENGTH (der); } if (is_int && *elem != '-') { /* take this integer */ char tmp[2]; put_stringbuf (&sb, "("); tmp[0] = *elem; tmp[1] = 0; put_stringbuf_sexp (&sb, tmp); put_stringbuf_mem_sexp (&sb, der, len); der += len; derlen -= len; put_stringbuf (&sb, ")"); } } } if (mode == 2) /* ECDH */ { put_stringbuf (&sb, "(1:s"); put_stringbuf_mem_sexp (&sb, encrkey, encrkeylen); put_stringbuf (&sb, ")"); } put_stringbuf (&sb, ")"); if (!mode && algo_table[algoidx].digest_string) { /* Insert the hash algorithm if included in the OID. */ put_stringbuf (&sb, "(4:hash"); put_stringbuf_sexp (&sb, algo_table[algoidx].digest_string); put_stringbuf (&sb, ")"); } if (!mode && pss_hash) { put_stringbuf (&sb, "(5:flags3:pss)"); put_stringbuf (&sb, "(9:hash-algo"); put_stringbuf_sexp (&sb, pss_hash); put_stringbuf (&sb, ")"); put_stringbuf (&sb, "(11:salt-length"); put_stringbuf_uint (&sb, salt_length); put_stringbuf (&sb, ")"); } if (mode == 2) /* ECDH */ { put_stringbuf (&sb, "(9:encr-algo"); put_stringbuf_sexp (&sb, keyencralgo); put_stringbuf (&sb, ")(9:wrap-algo"); put_stringbuf_sexp (&sb, keywrapalgo); put_stringbuf (&sb, ")"); } put_stringbuf (&sb, ")"); *r_string = get_stringbuf (&sb); if (!*r_string) return gpg_error (GPG_ERR_ENOMEM); xfree (pss_hash); return 0; } /* Assume that DER is a buffer of length DERLEN with a DER encoded Asn.1 structure like this: SEQUENCE { algorithm OBJECT IDENTIFIER, parameters ANY DEFINED BY algorithm OPTIONAL } signature BIT STRING We only allow parameters == NULL. The function parses this structure and creates a S-Exp suitable to be used as signature value in Libgcrypt: (sig-val ( ( ) ... ( )) (hash algo)) The S-Exp will be returned in a string which the caller must free. We don't pass an ASN.1 node here but a plain memory block. */ gpg_error_t _ksba_sigval_to_sexp (const unsigned char *der, size_t derlen, ksba_sexp_t *r_string) { return cryptval_to_sexp (0, der, derlen, NULL, NULL, NULL, 0, r_string); } /* Assume that der is a buffer of length DERLEN with a DER encoded * ASN.1 structure like this: * * SEQUENCE { * algorithm OBJECT IDENTIFIER, * parameters ANY DEFINED BY algorithm OPTIONAL * } * encryptedKey OCTET STRING * * The function parses this structure and creates a S-expression * suitable to be used as encrypted value in Libgcrypt's public key * functions: * * (enc-val * ( * ( ) * ... * ( ) * )) * * The S-expression will be returned in a string which the caller must * free. Note that the input buffer may not a proper ASN.1 object but * a plain memory block; this is becuase the SEQUENCE is followed by * an OCTET STRING or BIT STRING. */ gpg_error_t _ksba_encval_to_sexp (const unsigned char *der, size_t derlen, ksba_sexp_t *r_string) { return cryptval_to_sexp (1, der, derlen, NULL, NULL, NULL, 0, r_string); } /* Assume that der is a buffer of length DERLEN with a DER encoded * ASN.1 structure like this: * * [1] { * SEQUENCE { * algorithm OBJECT IDENTIFIER, * parameters ANY DEFINED BY algorithm OPTIONAL * } * encryptedKey BIT STRING * } * * The function parses this structure and creates an S-expression * conveying all parameters required for ECDH: * * (enc-val * (ecdh * (e ) * (s ) * (ukm ) * (encr-algo ) * (wrap-algo ))) * * E is the ephemeral public key and S is the encrypted key. The user * keying material (ukm) is optional. The S-expression will be * returned in a string which the caller must free. */ gpg_error_t _ksba_encval_kari_to_sexp (const unsigned char *der, size_t derlen, const char *keyencralgo, const char *keywrapalgo, const void *enckey, size_t enckeylen, ksba_sexp_t *r_string) { gpg_error_t err; struct tag_info ti; size_t save_derlen = derlen; err = parse_context_tag (&der, &derlen, &ti, 1); if (err) return err; if (save_derlen < ti.nhdr) return gpg_error (GPG_ERR_INV_BER); derlen = save_derlen - ti.nhdr; return cryptval_to_sexp (2, der, derlen, keyencralgo, keywrapalgo, enckey, enckeylen, r_string); } diff --git a/src/keyinfo.h b/src/keyinfo.h index 0fe17df..66f5805 100644 --- a/src/keyinfo.h +++ b/src/keyinfo.h @@ -1,79 +1,79 @@ /* keyinfo.h - Parse and build a keyInfo structure * 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 KEYINFO_H #define KEYINFO_H #include "asn1-func.h" gpg_error_t _ksba_parse_algorithm_identifier (const unsigned char *der, size_t derlen, size_t *r_nread, char **r_oid); gpg_error_t _ksba_parse_algorithm_identifier2 (const unsigned char *der, size_t derlen, size_t *r_nread, char **r_oid, char **r_parm, size_t *r_parmlen); gpg_error_t _ksba_keyinfo_to_sexp (const unsigned char *der, size_t derlen, ksba_sexp_t *r_string) _KSBA_VISIBILITY_DEFAULT; -gpg_error_t _ksba_keyinfo_from_sexp (ksba_const_sexp_t sexp, +gpg_error_t _ksba_keyinfo_from_sexp (ksba_const_sexp_t sexp, int algoinfomode, unsigned char **r_der, size_t *r_derlen) _KSBA_VISIBILITY_DEFAULT; gpg_error_t _ksba_algoinfo_from_sexp (ksba_const_sexp_t sexp, unsigned char **r_der, size_t *r_derlen); gpg_error_t _ksba_keyinfo_get_pss_info (const unsigned char *der, size_t derlen, char **r_psshash, unsigned int *r_saltlen); gpg_error_t _ksba_sigval_to_sexp (const unsigned char *der, size_t derlen, ksba_sexp_t *r_string); gpg_error_t _ksba_encval_to_sexp (const unsigned char *der, size_t derlen, ksba_sexp_t *r_string); gpg_error_t _ksba_encval_kari_to_sexp (const unsigned char *der, size_t derlen, const char *keyencralgo, const char *keywrapalgo, const void *enckey, size_t enckeylen, ksba_sexp_t *r_string); int _ksba_node_with_oid_to_digest_algo (const unsigned char *image, AsnNode node); #endif /*KEYINFO_H*/ diff --git a/tests/cert-basic.c b/tests/cert-basic.c index 4d460bc..4d190ee 100644 --- a/tests/cert-basic.c +++ b/tests/cert-basic.c @@ -1,619 +1,619 @@ /* cert-basic.c - basic test for the certificate management. * Copyright (C) 2001, 2002, 2004, 2005 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 the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * 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 copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include "../src/ksba.h" #define _KSBA_VISIBILITY_DEFAULT /* */ #include "../src/keyinfo.h" #include "oidtranstbl.h" #include "t-common.h" #ifdef __MINGW32CE__ #define getenv(a) (NULL) #endif #define digitp(p) (*(p) >= '0' && *(p) <= '9') #define fail_if_err(a) do { if(a) { \ fprintf (stderr, "%s:%d: KSBA error: %s\n", \ __FILE__, __LINE__, gpg_strerror(a)); \ exit (1); } \ } while(0) #define fail_if_err2(f, a) do { if(a) {\ fprintf (stderr, "%s:%d: KSBA error on file `%s': %s\n", \ __FILE__, __LINE__, (f), gpg_strerror(a)); \ exit (1); } \ } while(0) #define xfree(a) ksba_free (a) static int verbose; static int errorcount = 0; static void print_names (int indent, ksba_name_t name) { int idx; const char *s; int indent_all; if ((indent_all = (indent < 0))) indent = - indent; if (!name) { fputs ("none\n", stdout); return; } for (idx=0; (s = ksba_name_enum (name, idx)); idx++) { char *p = ksba_name_get_uri (name, idx); printf ("%*s%s\n", idx||indent_all?indent:0, "", p?p:s); xfree (p); } } /* Return the description for OID; if no description is available NULL is returned. */ static const char * get_oid_desc (const char *oid) { int i; if (oid) for (i=0; oidtranstbl[i].oid; i++) if (!strcmp (oidtranstbl[i].oid, oid)) return oidtranstbl[i].desc; return NULL; } static void print_oid_and_desc (const char *oid, int with_lf) { const char *s = get_oid_desc (oid); printf ("%s%s%s%s", oid, s?" (":"", s?s:"", s?")":""); if (with_lf) putchar ('\n'); } static void print_oid_list (int indent, char *list) { char *lf; int indent_all, c; size_t n; if ((indent_all = (indent < 0))) indent = - indent; while (*list) { printf ("%*s", indent_all?indent:0, ""); indent_all = 1; if (!(lf = strchr (list, '\n'))) lf = list + strlen (list); n = strspn (list, "0123456789."); c = list[n]; list[n] = 0; print_oid_and_desc (list, 0); list[n] = c; c = *lf; *lf = 0; printf (" %s\n", list+n); *lf = c; list = *lf? (lf+1):lf; } } static void list_extensions (ksba_cert_t cert) { gpg_error_t err; const char *oid; int idx, crit, is_ca, pathlen; size_t off, len; unsigned int usage, reason; char *string, *p; ksba_name_t name1, name2; ksba_sexp_t serial; ksba_sexp_t keyid; for (idx=0; !(err=ksba_cert_get_extension (cert, idx, &oid, &crit, &off, &len));idx++) { const char *s = get_oid_desc (oid); printf ("Extn: %s%s%s%s at %d with length %d %s\n", oid, s?" (":"", s?s:"", s?")":"", (int)off, (int)len, crit? "(critical)":""); } if (err && gpg_err_code (err) != GPG_ERR_EOF ) { fprintf (stderr, "%s:%d: enumerating extensions failed: %s\n", __FILE__, __LINE__, gpg_strerror (err)); errorcount++; } /* subjectKeyIdentifier */ err = ksba_cert_get_subj_key_id (cert, NULL, &keyid); if (!err || gpg_err_code (err) == GPG_ERR_NO_DATA) { fputs ("SubjectKeyIdentifier: ", stdout); if (gpg_err_code (err) == GPG_ERR_NO_DATA) fputs ("none", stdout); else { print_sexp (keyid); ksba_free (keyid); } putchar ('\n'); } /* authorityKeyIdentifier */ err = ksba_cert_get_auth_key_id (cert, &keyid, &name1, &serial); if (!err || gpg_err_code (err) == GPG_ERR_NO_DATA) { fputs ("AuthorityKeyIdentifier: ", stdout); if (gpg_err_code (err) == GPG_ERR_NO_DATA) fputs ("none\n", stdout); else { if (name1) { print_names (24, name1); ksba_name_release (name1); fputs (" serial: ", stdout); print_sexp (serial); ksba_free (serial); } putchar ('\n'); if (keyid) { fputs (" keyIdentifier: ", stdout); print_sexp (keyid); ksba_free (keyid); putchar ('\n'); } } } else { fprintf (stderr, "%s:%d: ksba_cert_get_auth_key_id: %s\n", __FILE__, __LINE__, gpg_strerror (err)); errorcount++; } err = ksba_cert_is_ca (cert, &is_ca, &pathlen); if (err) { fprintf (stderr, "%s:%d: ksba_cert_is_ca failed: %s\n", __FILE__, __LINE__, gpg_strerror (err)); errorcount++; } else if (is_ca) printf ("This is a CA certificate with a path length of %d\n", pathlen); err = ksba_cert_get_key_usage (cert, &usage); if (gpg_err_code (err) == GPG_ERR_NO_DATA) printf ("KeyUsage: Not specified\n"); else if (err) { fprintf (stderr, "%s:%d: ksba_cert_get_key_usage failed: %s\n", __FILE__, __LINE__, gpg_strerror (err)); errorcount++; } else { fputs ("KeyUsage:", stdout); if ( (usage & KSBA_KEYUSAGE_DIGITAL_SIGNATURE)) fputs (" digitalSignature", stdout); if ( (usage & KSBA_KEYUSAGE_NON_REPUDIATION)) fputs (" nonRepudiation", stdout); if ( (usage & KSBA_KEYUSAGE_KEY_ENCIPHERMENT)) fputs (" keyEncipherment", stdout); if ( (usage & KSBA_KEYUSAGE_DATA_ENCIPHERMENT)) fputs (" dataEncripherment", stdout); if ( (usage & KSBA_KEYUSAGE_KEY_AGREEMENT)) fputs (" keyAgreement", stdout); if ( (usage & KSBA_KEYUSAGE_KEY_CERT_SIGN)) fputs (" certSign", stdout); if ( (usage & KSBA_KEYUSAGE_CRL_SIGN)) fputs (" crlSign", stdout); if ( (usage & KSBA_KEYUSAGE_ENCIPHER_ONLY)) fputs (" encipherOnly", stdout); if ( (usage & KSBA_KEYUSAGE_DECIPHER_ONLY)) fputs (" decipherOnly", stdout); putchar ('\n'); } err = ksba_cert_get_ext_key_usages (cert, &string); if (gpg_err_code (err) == GPG_ERR_NO_DATA) printf ("ExtKeyUsages: none\n"); else if (err) { fprintf (stderr, "%s:%d: ksba_cert_ext_key_usages failed: %s\n", __FILE__, __LINE__, gpg_strerror (err)); errorcount++; } else { fputs ("ExtKeyUsages: ", stdout); print_oid_list (14, string); xfree (string); } err = ksba_cert_get_cert_policies (cert, &string); if (gpg_err_code (err) == GPG_ERR_NO_DATA) printf ("CertificatePolicies: none\n"); else if (err) { fprintf (stderr, "%s:%d: ksba_cert_get_cert_policies failed: %s\n", __FILE__, __LINE__, gpg_strerror (err)); errorcount++; } else { /* for display purposes we replace the linefeeds by commas */ for (p=string; *p; p++) { if (*p == '\n') *p = ','; } fputs ("CertificatePolicies: ", stdout); print_oid_list (21, string); xfree (string); } /* CRL distribution point */ for (idx=0; !(err=ksba_cert_get_crl_dist_point (cert, idx, &name1, &name2, &reason));idx++) { fputs ("CRLDistPoint: ", stdout); print_names (14, name1); fputs (" reasons:", stdout); if ( !reason ) fputs (" none", stdout); if ( (reason & KSBA_CRLREASON_UNSPECIFIED)) fputs (" unused", stdout); if ( (reason & KSBA_CRLREASON_KEY_COMPROMISE)) fputs (" keyCompromise", stdout); if ( (reason & KSBA_CRLREASON_CA_COMPROMISE)) fputs (" caCompromise", stdout); if ( (reason & KSBA_CRLREASON_AFFILIATION_CHANGED)) fputs (" affiliationChanged", stdout); if ( (reason & KSBA_CRLREASON_SUPERSEDED)) fputs (" superseded", stdout); if ( (reason & KSBA_CRLREASON_CESSATION_OF_OPERATION)) fputs (" cessationOfOperation", stdout); if ( (reason & KSBA_CRLREASON_CERTIFICATE_HOLD)) fputs (" certificateHold", stdout); putchar ('\n'); fputs (" issuer: ", stdout); print_names (14, name2); ksba_name_release (name1); ksba_name_release (name2); } if (err && gpg_err_code (err) != GPG_ERR_EOF) { fprintf (stderr, "%s:%d: ksba_cert_get_crl_dist_point failed: %s\n", __FILE__, __LINE__, gpg_strerror (err)); errorcount++; } /* authorityInfoAccess. */ for (idx=0; !(err=ksba_cert_get_authority_info_access (cert, idx, &string, &name1)) ; idx++) { fputs ("authorityInfoAccess: ", stdout); print_oid_and_desc (string, 1); print_names (-21, name1); ksba_name_release (name1); ksba_free (string); } if (err && gpg_err_code (err) != GPG_ERR_EOF) { fprintf (stderr, "%s:%d: " "ksba_cert_get_authority_info_access failed: %s\n", __FILE__, __LINE__, gpg_strerror (err)); errorcount++; } /* subjectInfoAccess. */ for (idx=0; !(err=ksba_cert_get_subject_info_access (cert, idx, &string, &name1)) ; idx++) { fputs ("subjectInfoAccess: ", stdout); print_oid_and_desc (string, 1); print_names (-19, name1); ksba_name_release (name1); ksba_free (string); } if (err && gpg_err_code (err) != GPG_ERR_EOF) { fprintf (stderr, "%s:%d: " "ksba_cert_get_subject_info_access failed: %s\n", __FILE__, __LINE__, gpg_strerror (err)); errorcount++; } } static void one_file (const char *fname) { gpg_error_t err; FILE *fp; ksba_reader_t r; ksba_cert_t cert; char *dn; ksba_isotime_t t; int idx; ksba_sexp_t sexp; const char *oid, *s; fp = fopen (fname, "rb"); if (!fp) { fprintf (stderr, "%s:%d: can't open `%s': %s\n", __FILE__, __LINE__, fname, strerror (errno)); exit (1); } err = ksba_reader_new (&r); if (err) fail_if_err (err); err = ksba_reader_set_file (r, fp); fail_if_err (err); err = ksba_cert_new (&cert); if (err) fail_if_err (err); err = ksba_cert_read_der (cert, r); fail_if_err2 (fname, err); printf ("Certificate in `%s':\n", fname); sexp = ksba_cert_get_serial (cert); fputs (" serial....: ", stdout); print_sexp (sexp); ksba_free (sexp); putchar ('\n'); for (idx=0;(dn = ksba_cert_get_issuer (cert, idx));idx++) { fputs (idx?" aka: ":" issuer....: ", stdout); print_dn (dn); ksba_free (dn); putchar ('\n'); } for (idx=0;(dn = ksba_cert_get_subject (cert, idx));idx++) { fputs (idx?" aka: ":" subject...: ", stdout); print_dn (dn); ksba_free (dn); putchar ('\n'); } ksba_cert_get_validity (cert, 0, t); fputs (" notBefore.: ", stdout); print_time (t); putchar ('\n'); ksba_cert_get_validity (cert, 1, t); fputs (" notAfter..: ", stdout); print_time (t); putchar ('\n'); oid = ksba_cert_get_digest_algo (cert); s = get_oid_desc (oid); printf (" hash algo.: %s%s%s%s\n", oid?oid:"(null)", s?" (":"",s?s:"",s?")":""); /* Under Windows the _ksba_keyinfo_from_sexp are not exported. */ #ifndef __WIN32 /* check that the sexp to keyinfo conversion works */ { ksba_sexp_t public; public = ksba_cert_get_public_key (cert); if (!public) { fprintf (stderr, "%s:%d: public key not found\n", __FILE__, __LINE__); errorcount++; } else { unsigned char *der; size_t derlen; if (verbose) { fputs (" pubkey....: ", stdout); print_sexp (public); putchar ('\n'); } - err = _ksba_keyinfo_from_sexp (public, &der, &derlen); + err = _ksba_keyinfo_from_sexp (public, 0, &der, &derlen); if (err) { fprintf (stderr, "%s:%d: converting public key failed: %s\n", __FILE__, __LINE__, gpg_strerror (err)); errorcount++; } else { ksba_sexp_t tmp; if (verbose) { fputs (" pubkey-DER: ", stdout); print_hex (der, derlen); putchar ('\n'); } err = _ksba_keyinfo_to_sexp (der, derlen, &tmp); if (err) { fprintf (stderr, "%s:%d: re-converting public key failed: %s\n", __FILE__, __LINE__, gpg_strerror (err)); errorcount++; } else { unsigned char *der2; size_t derlen2; - err = _ksba_keyinfo_from_sexp (tmp, &der2, &derlen2); + err = _ksba_keyinfo_from_sexp (tmp, 0, &der2, &derlen2); if (err) { fprintf (stderr, "%s:%d: re-re-converting " "public key failed: %s\n", __FILE__, __LINE__, gpg_strerror (err)); errorcount++; } else if (derlen != derlen2 || memcmp (der, der2, derlen)) { fprintf (stderr, "%s:%d: mismatch after " "re-re-converting public key\n", __FILE__, __LINE__); errorcount++; xfree (der2); } else { /* Don't leak memory if everything is ok. */ xfree (der2); } xfree (tmp); } xfree (der); } ksba_free (public); } } #endif if (verbose) { sexp = ksba_cert_get_sig_val (cert); fputs (" sigval....: ", stdout); print_sexp (sexp); ksba_free (sexp); putchar ('\n'); } list_extensions (cert); ksba_cert_release (cert); err = ksba_cert_new (&cert); if (err) fail_if_err (err); err = ksba_cert_read_der (cert, r); if (err && gpg_err_code (err) != GPG_ERR_EOF) { fprintf (stderr, "%s:%d: expected EOF but got: %s\n", __FILE__, __LINE__, gpg_strerror (err)); errorcount++; } putchar ('\n'); ksba_cert_release (cert); ksba_reader_release (r); fclose (fp); } int main (int argc, char **argv) { const char *srcdir = getenv ("srcdir"); if (!srcdir) srcdir = "."; if (argc) { argc--; argv++; } if (argc && !strcmp (*argv, "--verbose")) { verbose = 1; argc--; argv++; } if (argc) { for (; argc; argc--, argv++) one_file (*argv); } else { const char *files[] = { "cert_dfn_pca01.der", "cert_dfn_pca15.der", "cert_g10code_test1.der", NULL }; int idx; for (idx=0; files[idx]; idx++) { char *fname; fname = xmalloc (strlen (srcdir) + 1 + strlen (files[idx]) + 1); strcpy (fname, srcdir); strcat (fname, "/"); strcat (fname, files[idx]); one_file (fname); ksba_free (fname); } } return !!errorcount; }