diff --git a/src/certreq.c b/src/certreq.c index e0869e9..9fba0c8 100644 --- a/src/certreq.c +++ b/src/certreq.c @@ -1,1129 +1,1192 @@ /* 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"; +#if 0 /* Set to 1 to use this debug helper. */ +static void +log_sexp (const char *text, ksba_const_sexp_t p) +{ + int level = 0; + + gpgrt_log_debug ("%s: ", text); + if (!p) + gpgrt_log_printf ("[none]"); + else + { + for (;;) + { + if (*p == '(') + { + gpgrt_log_printf ("%c", *p); + p++; + level++; + } + else if (*p == ')') + { + gpgrt_log_printf ("%c", *p); + p++; + if (--level <= 0 ) + return; + } + else if (!digitp (p)) + { + gpgrt_log_printf ("[invalid s-exp]"); + return; + } + else + { + char *endp; + const unsigned char *s; + unsigned long len, n; + + len = strtoul (p, &endp, 10); + p = endp; + if (*p != ':') + { + gpgrt_log_printf ("[invalid s-exp]"); + return; + } + p++; + for (s=p,n=0; n < len; n++, s++) + if ( !((*s >= 'a' && *s <= 'z') + || (*s >= 'A' && *s <= 'Z') + || (*s >= '0' && *s <= '9') + || *s == '-' || *s == '.')) + break; + if (n < len) + { + gpgrt_log_printf ("#"); + for (n=0; n < len; n++, p++) + gpgrt_log_printf ("%02X", *p); + gpgrt_log_printf ("#"); + } + else + { + for (n=0; n < len; n++, p++) + gpgrt_log_printf ("%c", *p); + } + } + } + } + gpgrt_log_printf ("\n"); +} +#endif /* debug helper */ /** * 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_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, 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; + cr->sig_val.is_ecc = 2; } s += n; + if (cr->sig_val.is_ecc == 2 + || !strcmp (cr->sig_val.algo, "1.3.101.112") /* Ed25519 */ + || !strcmp (cr->sig_val.algo, "1.3.101.113")) /* Ed448 */ + cr->sig_val.is_ecc = 2; + else if (!strcmp (cr->sig_val.algo, "1.2.840.10045.4.1") /* with-sha1 */ + || !strcmp (cr->sig_val.algo, "1.2.840.10045.4.3.1") /* with-sha224 */ + || !strcmp (cr->sig_val.algo, "1.2.840.10045.4.3.2") /* with-sha256 */ + || !strcmp (cr->sig_val.algo, "1.2.840.10045.4.3.3") /* with-sha384 */ + || !strcmp (cr->sig_val.algo, "1.2.840.10045.4.3.4"))/* with-sha512 */ + cr->sig_val.is_ecc = 1; + else + cr->sig_val.is_ecc = 0; + /* 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); + if (cr->sig_val.is_ecc != 2 && 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) + if (cr->sig_val.is_ecc != 2 && 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) + if (cr->sig_val.is_ecc == 2 || 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) + if (cr->sig_val.is_ecc == 2 || 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; + ksba_der_t dbld; + unsigned char *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; + dbld = _ksba_der_builder_new (0); + if (!dbld) + { + err = gpg_error_from_syserror (); + goto leave; + } + + /* Start outer sequence. */ + _ksba_der_add_tag (dbld, 0, TYPE_SEQUENCE); - /* store the cri */ + /* 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; + _ksba_der_add_der (dbld, cr->cri.der, cr->cri.derlen); - /* store the signatureAlgorithm */ + /* 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; + _ksba_der_add_tag (dbld, 0, TYPE_SEQUENCE); + _ksba_der_add_oid (dbld, cr->sig_val.algo); + if (!cr->sig_val.is_ecc) + _ksba_der_add_ptr (dbld, 0, TYPE_NULL, NULL, 0); + _ksba_der_add_end (dbld); - /* 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; + /* Write the signature */ + _ksba_der_add_bts (dbld, cr->sig_val.value, cr->sig_val.valuelen, 0); - /* 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); + /* End outer sequence. */ + _ksba_der_add_end (dbld); + + /* and finally write the result */ + err = _ksba_der_builder_get (dbld, &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) + if (!cr->writer) err = gpg_error (GPG_ERR_MISSING_ACTION); else err = ksba_writer_write (cr->writer, value, valuelen); leave: - ksba_writer_release (writer); + ksba_der_release (dbld); 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 */ +/* The main function to build a certificate request. It is used in a + * loop to 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/certreq.h b/src/certreq.h index 43388b6..2a9503c 100644 --- a/src/certreq.h +++ b/src/certreq.h @@ -1,124 +1,125 @@ /* certreq.h - Internal definitions for pkcs-10 objects * Copyright (C) 2002, 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 . */ #ifndef CERTREQ_H #define CERTREQ_H 1 #include "ksba.h" #ifndef HAVE_TYPEDEFD_ASNNODE typedef struct asn_node_struct *AsnNode; /* FIXME: should not go here */ #define HAVE_TYPEDEFD_ASNNODE #endif struct extn_list_s { struct extn_list_s *next; const char *oid; int critical; int derlen; unsigned char der[1]; }; /* Object to collect information for building a GeneralNames. */ struct general_names_s { struct general_names_s *next; int tag; /* The GeneralName CHOICE. Only certain values are supported. This is not strictly required because DATA below has already been prefixed with the DER encoded tag. */ size_t datalen; /* Length of the data. */ char data[1]; /* The actual data: encoded tag, llength and value. */ }; struct ksba_certreq_s { gpg_error_t last_error; ksba_writer_t writer; void (*hash_fnc)(void *, const void *, size_t); void *hash_fnc_arg; int any_build_done; struct { struct { char *der; /* Malloced serialno; if this is set we want to build a real X.509 certificate. */ size_t derlen; } serial; struct { char *der; size_t derlen; } issuer; ksba_isotime_t not_before; ksba_isotime_t not_after; struct { unsigned char *der; size_t derlen; } siginfo; } x509; struct { char *der; size_t derlen; } subject; struct { unsigned char *der; size_t derlen; } key; struct general_names_s *subject_alt_names; struct extn_list_s *extn_list; struct { unsigned char *der; size_t derlen; } cri; struct { char *algo; + int is_ecc; /* 1 = plain ecc, 2 = EdDSA */ unsigned char *value; size_t valuelen; } sig_val; }; #endif /*CERTREQ_H*/ diff --git a/src/keyinfo.c b/src/keyinfo.c index 48f396c..666726f 100644 --- a/src/keyinfo.c +++ b/src/keyinfo.c @@ -1,1596 +1,1633 @@ /* keyinfo.c - Parse and build a keyInfo structure * Copyright (C) 2001, 2002, 2007, 2008, 2012, 2020 g10 Code GmbH * * This file is part of KSBA. * * KSBA is free software; you can redistribute it and/or modify * it under the terms of either * * - the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at * your option) any later version. * * or * * - the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at * your option) any later version. * * or both in parallel, as here. * * KSBA is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public * License for more details. * * You should have received a copies of the GNU General Public License * and the GNU Lesser General Public License along with this program; * if not, see . */ /* 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" #include "der-builder.h" /* Constants used for the public key algorithms. */ typedef enum { + PKALGO_NONE, 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; + unsigned char pkalgo; /* If not 0 force the use of ALGO. */ } curve_names[] = { - /* For backward compatibility we keep the two original OIDs from - * OpenPGP and do not replace them by those from RFC-8410. It is - * anyway better to use the dotted decimal form. */ - { "1.3.6.1.4.1.11591.15.1", "Ed25519" }, - { "1.3.6.1.4.1.3029.1.5.1", "Curve25519" }, - { "1.3.101.110", "X25519" }, + { "1.3.101.112", "Ed25519", PKALGO_ED25519}, + { "1.3.101.110", "Curve25519", PKALGO_X25519}, + { "1.3.101.110", "X25519", PKALGO_X25519}, - { "1.3.101.113", "Ed448" }, - { "1.3.101.111", "X448" }, + { "1.3.101.113", "Ed448", PKALGO_ED448 }, + { "1.3.101.111", "X448", PKALGO_X448 }, { "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 a curve name or its * OID in dotted form return a string in dotted form of the name. The - * caller must free the result. On error NULL is returned. */ + * caller must free the result. On error NULL is returned. If a + * curve requires the use of a certain algorithm, that algorithm is + * stored at R_PKALGO. */ static char * -get_ecc_curve_oid (const unsigned char *buf, size_t buflen) +get_ecc_curve_oid (const unsigned char *buf, size_t buflen, pkalgo_t *r_pkalgo) { unsigned char *result; + int i, find_pkalgo; /* 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); + *r_pkalgo = curve_names[i].pkalgo; + find_pkalgo = 0; } + else + find_pkalgo = 1; result = xtrymalloc (buflen + 1); if (!result) return NULL; /* Ooops */ memcpy (result, buf, buflen); result[buflen] = 0; + + if (find_pkalgo) + { + /* We still need to check whether the OID requires a certain ALGO. */ + for (i=0; curve_names[i].oid; i++) + if (!strcmp (curve_names[i].oid, result)) + { + *r_pkalgo = curve_names[i].pkalgo; + break; + } + } + return result; } /* 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 return the table * entriy with the OID string. If WITH_SIG is true, the table of * signature algorithms is consulted first. */ static const char * oid_from_buffer (const unsigned char *buf, unsigned int buflen, 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; return sig_algo_table[i].oidstring; } } /* 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; return pk_algo_table[i].oidstring; } /* 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, int algoinfomode, unsigned char **r_der, size_t *r_derlen) { gpg_error_t err; const unsigned char *s; char *endp; unsigned long n; const char *algo_oid; char *curve_oid = NULL; - pkalgo_t pkalgo; + pkalgo_t pkalgo, force_pkalgo; int i; struct { const char *name; int namelen; const unsigned char *value; int valuelen; } parm[10]; int parmidx; const char *parmdesc, *algoparmdesc; ksba_der_t dbld = NULL; ksba_der_t dbld2 = NULL; unsigned char *tmpder; size_t tmpderlen; 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 (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); 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++; algo_oid = oid_from_buffer (s, n, &pkalgo, algoinfomode); if (!algo_oid) return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); - s += n; /* Collect all the values. */ + force_pkalgo = 0; for (parmidx = 0; *s != ')' ; parmidx++) { if (parmidx >= DIM(parm)) { err = gpg_error (GPG_ERR_GENERAL); goto leave; } if (*s != '(') { err = gpg_error (digitp(s)? GPG_ERR_UNKNOWN_SEXP:GPG_ERR_INV_SEXP); goto leave; } s++; n = strtoul (s, &endp, 10); s = endp; if (!n || *s != ':') { err = gpg_error (GPG_ERR_INV_SEXP); goto leave; } s++; parm[parmidx].name = s; parm[parmidx].namelen = n; s += n; if (!digitp(s)) { err = gpg_error (GPG_ERR_UNKNOWN_SEXP); /* ... or invalid S-Exp. */ goto leave; } 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 != ')') { err = gpg_error (GPG_ERR_UNKNOWN_SEXP); /* ... or invalid S-Exp. */ goto leave; } s++; if (parm[parmidx].namelen == 5 && !memcmp (parm[parmidx].name, "curve", 5) && !curve_oid) { curve_oid = get_ecc_curve_oid (parm[parmidx].value, - parm[parmidx].valuelen); + parm[parmidx].valuelen, &force_pkalgo); parmidx--; /* No need to store this parameter. */ } } s++; /* Allow for optional elements. */ if (*s == '(') { int depth = 1; err = sskip (&s, &depth); if (err) goto leave; } /* We need another closing parenthesis. */ if ( *s != ')' ) { err = gpg_error (GPG_ERR_INV_SEXP); goto leave; } + if (force_pkalgo) + pkalgo = force_pkalgo; + /* Describe the parameters in the order we want 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 = algoinfomode? "" : "ne"; break; case PKALGO_DSA: parmdesc = algoinfomode? "" : "y"; algoparmdesc = "pqg"; break; case PKALGO_ECC: + parmdesc = algoinfomode? "" : "q"; + break; case PKALGO_ED25519: + case PKALGO_X25519: case PKALGO_ED448: + case PKALGO_X448: parmdesc = algoinfomode? "" : "q"; + if (curve_oid) + algo_oid = curve_oid; break; default: err = gpg_error (GPG_ERR_UNKNOWN_ALGORITHM); goto leave; } /* Create a builder. */ dbld = _ksba_der_builder_new (0); if (!dbld) { err = gpg_error_from_syserror (); goto leave; } /* The outer sequence. */ if (!algoinfomode) _ksba_der_add_tag (dbld, 0, TYPE_SEQUENCE); /* The sequence. */ _ksba_der_add_tag (dbld, 0, TYPE_SEQUENCE); /* The object id. */ _ksba_der_add_oid (dbld, algo_oid); /* The parameter. */ if (algoparmdesc) { /* Write the sequence tag followed by the integers. */ _ksba_der_add_tag (dbld, 0, TYPE_SEQUENCE); for (s = algoparmdesc; *s; s++) for (i=0; i < parmidx; i++) if (parm[i].namelen == 1 && parm[i].name[0] == *s) { _ksba_der_add_int (dbld, parm[i].value, parm[i].valuelen, 1); break; /* inner loop */ } _ksba_der_add_end (dbld); } - else if (pkalgo == PKALGO_ECC) + else if (pkalgo == PKALGO_ECC && !algoinfomode) { /* We only support the namedCurve choice for ECC parameters. */ if (!curve_oid) { err = gpg_error (GPG_ERR_UNKNOWN_CURVE); goto leave; } _ksba_der_add_oid (dbld, curve_oid); } else if (pkalgo == PKALGO_RSA) { _ksba_der_add_ptr (dbld, 0, TYPE_NULL, NULL, 0); } _ksba_der_add_end (dbld); /* sequence. */ /* Add the bit string if we are not in algoinfomode. */ if (!algoinfomode) { if (*parmdesc == 'q' && !parmdesc[1]) { /* This is ECC - Q is directly written as a bit string. */ for (i=0; i < parmidx; i++) if (parm[i].namelen == 1 && parm[i].name[0] == 'q') { - _ksba_der_add_bts (dbld, parm[i].value, parm[i].valuelen, 0); + if ((parm[i].valuelen & 1) && parm[i].valuelen > 32 + && (parm[i].value[0] == 0x40 + || parm[i].value[0] == 0x41 + || parm[i].value[0] == 0x42)) + { + /* Odd length and prefixed with 0x40 - this is the + * rfc4880bis indicator octet for extended point + * formats - we may not emit that octet here. */ + _ksba_der_add_bts (dbld, parm[i].value+1, + parm[i].valuelen-1, 0); + } + else + _ksba_der_add_bts (dbld, parm[i].value, parm[i].valuelen, 0); break; } } else /* Non-ECC - embed the values. */ { dbld2 = _ksba_der_builder_new (10); if (!dbld2) { err = gpg_error_from_syserror (); goto leave; } /* Note that no sequence is used if only one integer is written. */ if (parmdesc[0] && parmdesc[1]) _ksba_der_add_tag (dbld2, 0, TYPE_SEQUENCE); for (s = parmdesc; *s; s++) for (i=0; i < parmidx; i++) if (parm[i].namelen == 1 && parm[i].name[0] == *s) { _ksba_der_add_int (dbld2, parm[i].value, parm[i].valuelen, 1); break; /* inner loop */ } if (parmdesc[0] && parmdesc[1]) _ksba_der_add_end (dbld2); err = _ksba_der_builder_get (dbld2, &tmpder, &tmpderlen); if (err) goto leave; _ksba_der_add_bts (dbld, tmpder, tmpderlen, 0); xfree (tmpder); } _ksba_der_add_end (dbld); /* Outer sequence. */ } /* Get the result. */ err = _ksba_der_builder_get (dbld, r_der, r_derlen); leave: _ksba_der_release (dbld2); _ksba_der_release (dbld); 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); }