diff --git a/src/certreq.c b/src/certreq.c index 46fca44..5c730a7 100644 --- a/src/certreq.c +++ b/src/certreq.c @@ -1,1080 +1,1132 @@ /* 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); } /* 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); } /* 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 char *s, *endp; - unsigned long n; + const unsigned char *s, *saved; + char *buf = NULL; + unsigned long n, len; + int pass, nparam; if (!cr) return gpg_error (GPG_ERR_INV_VALUE); s = sigval; if (*s != '(') return gpg_error (GPG_ERR_INV_SEXP); s++; - n = strtoul (s, (char**)&endp, 10); - s = endp; - if (!n || *s!=':') - return gpg_error (GPG_ERR_INV_SEXP); /* we don't allow empty lengths */ - s++; - if (n != 7 || memcmp (s, "sig-val", 7)) + if (!(n = snext (&s))) + return gpg_error (GPG_ERR_INV_SEXP); + if (!smatch (&s, 7, "sig-val")) return gpg_error (GPG_ERR_UNKNOWN_SEXP); - s += 7; if (*s != '(') return gpg_error (digitp (s)? GPG_ERR_UNKNOWN_SEXP : GPG_ERR_INV_SEXP); s++; /* break out the algorithm ID */ - n = strtoul (s, (char**)&endp, 10); - s = endp; - if (!n || *s != ':') - return gpg_error (GPG_ERR_INV_SEXP); /* we don't allow empty lengths */ - s++; + 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; } s += n; - /* And now the values - FIXME: For now we only support one */ - /* fixme: start loop */ - if (*s != '(') - return gpg_error (digitp (s)? GPG_ERR_UNKNOWN_SEXP : GPG_ERR_INV_SEXP); - s++; - n = strtoul (s, (char**)&endp, 10); - s = endp; - if (!n || *s != ':') - return gpg_error (GPG_ERR_INV_SEXP); - s++; - s += n; /* ignore the name of the parameter */ + /* 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 (!digitp(s)) - return gpg_error (GPG_ERR_UNKNOWN_SEXP); /* but may also be an invalid one */ - n = strtoul (s, (char**)&endp, 10); - s = endp; - if (!n || *s != ':') - return gpg_error (GPG_ERR_INV_SEXP); - s++; - if (n > 1 && !*s) - { /* We might have a leading zero due to the way we encode - MPIs - this zero should not go into the BIT STRING. */ - s++; - n--; + if (pass == 3) + { + size_t needed = len; + if (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 (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 (nparam > 1) + len += _ksba_ber_count_tl (TYPE_INTEGER, CLASS_UNIVERSAL, 0, + *s >= 0x80? n + 1 : n) + + (*s >= 0x80? n + 1 : n); + else + len += (n > 1 && !*s)? n - 1 : n; + } + else if (pass == 3) + { + if (nparam > 1) + { + 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; + } + else + { + if (n > 1 && !*s) + { /* Remove leading zero byte, which must not be + included in the bitstring. */ + s++; + n--; + } + memcpy (buf, s, n); + buf += n; + } + } + + s += n; + if (*s != ')') + return gpg_error (GPG_ERR_UNKNOWN_SEXP); + s++; + } } - xfree (cr->sig_val.value); - cr->sig_val.value = xtrymalloc (n); - if (!cr->sig_val.value) - return gpg_error (GPG_ERR_ENOMEM); - memcpy (cr->sig_val.value, s, n); - cr->sig_val.valuelen = n; - s += n; - if ( *s != ')') - return gpg_error (GPG_ERR_UNKNOWN_SEXP); /* but may also be an invalid one */ - s++; - /* fixme: end loop over parameters */ /* 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; }