diff --git a/src/ber-help.c b/src/ber-help.c index 42960f1..1b72bf0 100644 --- a/src/ber-help.c +++ b/src/ber-help.c @@ -1,676 +1,712 @@ /* ber-help.c - BER herlper functions * Copyright (C) 2001, 2012 g10 Code GmbH * * This file is part of KSBA. * * KSBA is free software; you can redistribute it and/or modify * it under the terms of either * * - the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at * your option) any later version. * * or * * - the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at * your option) any later version. * * or both in parallel, as here. * * KSBA is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public * License for more details. * * You should have received a copies of the GNU General Public License * and the GNU Lesser General Public License along with this program; * if not, see . */ #include #include #include #include #include #include "util.h" #include "asn1-func.h" /* need some constants */ #include "convert.h" #include "ber-help.h" /* Fixme: The parser functions should check that primitive types don't have the constructed bit set (which is not allowed). This saves us some work when using these parsers */ static int read_byte (ksba_reader_t reader) { unsigned char buf; size_t nread; int rc; do rc = ksba_reader_read (reader, &buf, 1, &nread); while (!rc && !nread); return rc? -1: buf; } static int premature_eof (struct tag_info *ti) { /* Note: We do an strcmp on this string at other places. */ ti->err_string = "premature EOF"; return gpg_error (GPG_ERR_BAD_BER); } static gpg_error_t eof_or_error (ksba_reader_t reader, struct tag_info *ti, int premature) { gpg_error_t err; err = ksba_reader_error (reader); if (err) { ti->err_string = "read error"; return err; } if (premature) return premature_eof (ti); return gpg_error (GPG_ERR_EOF); } /* Read the tag and the length part from the TLV triplet. */ gpg_error_t _ksba_ber_read_tl (ksba_reader_t reader, struct tag_info *ti) { int c; unsigned long tag; ti->length = 0; ti->ndef = 0; ti->nhdr = 0; ti->err_string = NULL; ti->non_der = 0; /* Get the tag */ c = read_byte (reader); if (c==-1) return eof_or_error (reader, ti, 0); ti->buf[ti->nhdr++] = c; ti->class = (c & 0xc0) >> 6; ti->is_constructed = !!(c & 0x20); tag = c & 0x1f; if (tag == 0x1f) { tag = 0; do { /* We silently ignore an overflow in the tag value. It is not worth checking for it. */ tag <<= 7; c = read_byte (reader); if (c == -1) return eof_or_error (reader, ti, 1); if (ti->nhdr >= DIM (ti->buf)) { ti->err_string = "tag+length header too large"; return gpg_error (GPG_ERR_BAD_BER); } ti->buf[ti->nhdr++] = c; tag |= c & 0x7f; } while (c & 0x80); } ti->tag = tag; /* Get the length */ c = read_byte (reader); if (c == -1) return eof_or_error (reader, ti, 1); if (ti->nhdr >= DIM (ti->buf)) { ti->err_string = "tag+length header too large"; return gpg_error (GPG_ERR_BAD_BER); } ti->buf[ti->nhdr++] = c; if ( !(c & 0x80) ) ti->length = c; else if (c == 0x80) { ti->ndef = 1; ti->non_der = 1; } else if (c == 0xff) { ti->err_string = "forbidden length value"; return gpg_error (GPG_ERR_BAD_BER); } else { unsigned long len = 0; int count = c & 0x7f; if (count > sizeof (len) || count > sizeof (size_t)) return gpg_error (GPG_ERR_BAD_BER); for (; count; count--) { len <<= 8; c = read_byte (reader); if (c == -1) return eof_or_error (reader, ti, 1); if (ti->nhdr >= DIM (ti->buf)) { ti->err_string = "tag+length header too large"; return gpg_error (GPG_ERR_BAD_BER); } ti->buf[ti->nhdr++] = c; len |= c & 0xff; } ti->length = len; } /* Without this kludge some example certs can't be parsed */ if (ti->class == CLASS_UNIVERSAL && !ti->tag) ti->length = 0; return 0; } /* Parse the buffer at the address BUFFER which of SIZE and return the * tag and the length part from the TLV triplet. Update BUFFER and * SIZE on success. Note that this function will never return * GPG_ERR_INV_OBJ so that this error code can be used by the parse_foo * functions below to return an error for unexpected tags and the * caller is able to backoff in that case. */ gpg_error_t _ksba_ber_parse_tl (unsigned char const **buffer, size_t *size, struct tag_info *ti) { int c; unsigned long tag; const unsigned char *buf = *buffer; size_t length = *size; ti->length = 0; ti->ndef = 0; ti->nhdr = 0; ti->err_string = NULL; ti->non_der = 0; /* Get the tag */ if (!length) return premature_eof (ti); c = *buf++; length--; ti->buf[ti->nhdr++] = c; ti->class = (c & 0xc0) >> 6; ti->is_constructed = !!(c & 0x20); tag = c & 0x1f; if (tag == 0x1f) { tag = 0; do { /* We silently ignore an overflow in the tag value. It is not worth checking for it. */ tag <<= 7; if (!length) return premature_eof (ti); c = *buf++; length--; if (ti->nhdr >= DIM (ti->buf)) { ti->err_string = "tag+length header too large"; return gpg_error (GPG_ERR_BAD_BER); } ti->buf[ti->nhdr++] = c; tag |= c & 0x7f; } while (c & 0x80); } ti->tag = tag; /* Get the length */ if (!length) return premature_eof (ti); c = *buf++; length--; if (ti->nhdr >= DIM (ti->buf)) { ti->err_string = "tag+length header too large"; return gpg_error (GPG_ERR_BAD_BER); } ti->buf[ti->nhdr++] = c; if ( !(c & 0x80) ) ti->length = c; else if (c == 0x80) { ti->ndef = 1; ti->non_der = 1; } else if (c == 0xff) { ti->err_string = "forbidden length value"; return gpg_error (GPG_ERR_BAD_BER); } else { unsigned long len = 0; int count = c & 0x7f; if (count > sizeof (len) || count > sizeof (size_t)) return gpg_error (GPG_ERR_BAD_BER); for (; count; count--) { len <<= 8; if (!length) return premature_eof (ti); c = *buf++; length--; if (ti->nhdr >= DIM (ti->buf)) { ti->err_string = "tag+length header too large"; return gpg_error (GPG_ERR_BAD_BER); } ti->buf[ti->nhdr++] = c; len |= c & 0xff; } /* Sanity check for the length: This is done so that we can take * the value for malloc plus some additional bytes without * risking an overflow. */ if (len > (1 << 30)) return gpg_error (GPG_ERR_BAD_BER); ti->length = len; } /* Without this kludge some example certs can't be parsed */ if (ti->class == CLASS_UNIVERSAL && !ti->tag) ti->length = 0; *buffer = buf; *size = length; return 0; } /* Write TAG of CLASS to WRITER. constructed is a flag telling whether the value is a constructed one. length gives the length of the value, if it is 0 undefinite length is assumed. length is ignored for the NULL tag. */ gpg_error_t _ksba_ber_write_tl (ksba_writer_t writer, unsigned long tag, enum tag_class class, int constructed, unsigned long length) { unsigned char buf[50]; int buflen = 0; if (tag < 0x1f) { *buf = (class << 6) | tag; if (constructed) *buf |= 0x20; buflen++; } else { return gpg_error (GPG_ERR_NOT_IMPLEMENTED); } if (!tag && !class) buf[buflen++] = 0; /* end tag */ else if (tag == TYPE_NULL && !class) buf[buflen++] = 0; /* NULL tag */ else if (!length) buf[buflen++] = 0x80; /* indefinite length */ else if (length < 128) buf[buflen++] = length; else { int i; /* fixme: if we know the sizeof an ulong we could support larger objects - however this is pretty ridiculous */ i = (length <= 0xff ? 1: length <= 0xffff ? 2: length <= 0xffffff ? 3: 4); buf[buflen++] = (0x80 | i); if (i > 3) buf[buflen++] = length >> 24; if (i > 2) buf[buflen++] = length >> 16; if (i > 1) buf[buflen++] = length >> 8; buf[buflen++] = length; } return ksba_writer_write (writer, buf, buflen); } /* Encode TAG of CLASS in BUFFER. CONSTRUCTED is a flag telling whether the value is a constructed one. LENGTH gives the length of the value, if it is 0 undefinite length is assumed. LENGTH is ignored for the NULL tag. It is assumed that the provide buffer is large enough for storing the result - this is usually achieved by using _ksba_ber_count_tl() in advance. Returns 0 in case of an error or the length of the encoding.*/ size_t _ksba_ber_encode_tl (unsigned char *buffer, unsigned long tag, enum tag_class class, int constructed, unsigned long length) { unsigned char *buf = buffer; if (tag < 0x1f) { *buf = (class << 6) | tag; if (constructed) *buf |= 0x20; buf++; } else { return 0; /*Not implemented*/ } if (!tag && !class) *buf++ = 0; /* end tag */ else if (tag == TYPE_NULL && !class) *buf++ = 0; /* NULL tag */ else if (!length) *buf++ = 0x80; /* indefinite length */ else if (length < 128) *buf++ = length; else { int i; /* fixme: if we know the sizeof an ulong we could support larger objetcs - however this is pretty ridiculous */ i = (length <= 0xff ? 1: length <= 0xffff ? 2: length <= 0xffffff ? 3: 4); *buf++ = (0x80 | i); if (i > 3) *buf++ = length >> 24; if (i > 2) *buf++ = length >> 16; if (i > 1) *buf++ = length >> 8; *buf++ = length; } return buf - buffer; } /* Calculate the length of the TL needed to encode a TAG of CLASS. CONSTRUCTED is a flag telling whether the value is a constructed one. LENGTH gives the length of the value; if it is 0 an indefinite length is assumed. LENGTH is ignored for the NULL tag. */ size_t _ksba_ber_count_tl (unsigned long tag, enum tag_class class, int constructed, unsigned long length) { int buflen = 0; (void)constructed; /* Not used, but passed for uniformity of such calls. */ if (tag < 0x1f) { buflen++; } else { buflen++; /* assume one and let the actual write function bail out */ } if (!tag && !class) buflen++; /* end tag */ else if (tag == TYPE_NULL && !class) buflen++; /* NULL tag */ else if (!length) buflen++; /* indefinite length */ else if (length < 128) buflen++; else { int i; /* fixme: if we know the sizeof an ulong we could support larger objetcs - however this is pretty ridiculous */ i = (length <= 0xff ? 1: length <= 0xffff ? 2: length <= 0xffffff ? 3: 4); buflen++; if (i > 3) buflen++; if (i > 2) buflen++; if (i > 1) buflen++; buflen++; } return buflen; } gpg_error_t _ksba_parse_sequence (unsigned char const **buf, size_t *len, struct tag_info *ti) { gpg_error_t err; err = _ksba_ber_parse_tl (buf, len, ti); if (err) ; else if (!(ti->class == CLASS_UNIVERSAL && ti->tag == TYPE_SEQUENCE && ti->is_constructed) ) err = gpg_error (GPG_ERR_INV_OBJ); else if (ti->length > *len) err = gpg_error (GPG_ERR_BAD_BER); return err; } /* Note that this function returns GPG_ERR_FALSE if the TLV is valid * but the tag does not match. The caller may thus check for this * error code and compare against other tag values. */ gpg_error_t _ksba_parse_context_tag (unsigned char const **buf, size_t *len, struct tag_info *ti, int tag) { gpg_error_t err; err = _ksba_ber_parse_tl (buf, len, ti); if (err) ; else if (!(ti->class == CLASS_CONTEXT && ti->is_constructed)) err = gpg_error (GPG_ERR_INV_OBJ); else if (ti->length > *len) err = gpg_error (GPG_ERR_BAD_BER); else if (ti->tag != tag) err = gpg_error (GPG_ERR_FALSE); return err; } gpg_error_t _ksba_parse_enumerated (unsigned char const **buf, size_t *len, struct tag_info *ti, size_t maxlen) { gpg_error_t err; err = _ksba_ber_parse_tl (buf, len, ti); if (err) ; else if (!(ti->class == CLASS_UNIVERSAL && ti->tag == TYPE_ENUMERATED && !ti->is_constructed) ) err = gpg_error (GPG_ERR_INV_OBJ); else if (!ti->length) err = gpg_error (GPG_ERR_TOO_SHORT); else if (maxlen && ti->length > maxlen) err = gpg_error (GPG_ERR_TOO_LARGE); else if (ti->length > *len) err = gpg_error (GPG_ERR_BAD_BER); return err; } gpg_error_t _ksba_parse_integer (unsigned char const **buf, size_t *len, struct tag_info *ti) { gpg_error_t err; err = _ksba_ber_parse_tl (buf, len, ti); if (err) ; else if (!(ti->class == CLASS_UNIVERSAL && ti->tag == TYPE_INTEGER && !ti->is_constructed) ) err = gpg_error (GPG_ERR_INV_OBJ); else if (!ti->length) err = gpg_error (GPG_ERR_TOO_SHORT); else if (ti->length > *len) err = gpg_error (GPG_ERR_BAD_BER); return err; } gpg_error_t _ksba_parse_octet_string (unsigned char const **buf, size_t *len, struct tag_info *ti) { gpg_error_t err; err= _ksba_ber_parse_tl (buf, len, ti); if (err) ; else if (!(ti->class == CLASS_UNIVERSAL && ti->tag == TYPE_OCTET_STRING && !ti->is_constructed) ) err = gpg_error (GPG_ERR_INV_OBJ); else if (!ti->length) err = gpg_error (GPG_ERR_TOO_SHORT); else if (ti->length > *len) err = gpg_error (GPG_ERR_BAD_BER); return err; } /* Note that R_BOOL will only be set if a value has been given. Thus the caller should set it to the default value prior to calling this function. Obviously no call to parse_skip is required after calling this function. */ gpg_error_t _ksba_parse_optional_boolean (unsigned char const **buf, size_t *len, int *r_bool) { gpg_error_t err; struct tag_info ti; err = _ksba_ber_parse_tl (buf, len, &ti); if (err) ; else if (!ti.length) err = gpg_error (GPG_ERR_TOO_SHORT); else if (ti.length > *len) err = gpg_error (GPG_ERR_BAD_BER); else if (ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_BOOLEAN && !ti.is_constructed) { if (ti.length != 1) err = gpg_error (GPG_ERR_BAD_BER); *r_bool = !!**buf; parse_skip (buf, len, &ti); } else { /* Undo the read. */ *buf -= ti.nhdr; *len += ti.nhdr; } return err; } +/* Parse an optional Null tag. Ir R_SEEN is not NULL it is set to + * true if a NULL tag was encountered. */ +gpg_error_t +_ksba_parse_optional_null (unsigned char const **buf, size_t *len, + int *r_seen) +{ + gpg_error_t err; + struct tag_info ti; + + if (r_seen) + *r_seen = 0; + err = _ksba_ber_parse_tl (buf, len, &ti); + if (err) + ; + else if (ti.length > *len) + err = gpg_error (GPG_ERR_BAD_BER); + else if (ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_NULL + && !ti.is_constructed) + { + if (ti.length) + err = gpg_error (GPG_ERR_BAD_BER); + if (r_seen) + *r_seen = 1; + parse_skip (buf, len, &ti); + } + else + { /* Undo the read. */ + *buf -= ti.nhdr; + *len += ti.nhdr; + } + + return err; +} + + + gpg_error_t _ksba_parse_object_id_into_str (unsigned char const **buf, size_t *len, char **oid) { struct tag_info ti; gpg_error_t err; *oid = NULL; err = _ksba_ber_parse_tl (buf, len, &ti); if (err) ; else if (!(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_OBJECT_ID && !ti.is_constructed) ) err = gpg_error (GPG_ERR_INV_OBJ); else if (!ti.length) err = gpg_error (GPG_ERR_TOO_SHORT); else if (ti.length > *len) err = gpg_error (GPG_ERR_BAD_BER); else if (!(*oid = ksba_oid_to_str (*buf, ti.length))) err = gpg_error_from_syserror (); else { *buf += ti.length; *len -= ti.length; } return err; } gpg_error_t _ksba_parse_asntime_into_isotime (unsigned char const **buf, size_t *len, ksba_isotime_t isotime) { struct tag_info ti; gpg_error_t err; err = _ksba_ber_parse_tl (buf, len, &ti); if (err) ; else if ( !(ti.class == CLASS_UNIVERSAL && (ti.tag == TYPE_UTC_TIME || ti.tag == TYPE_GENERALIZED_TIME) && !ti.is_constructed) ) err = gpg_error (GPG_ERR_INV_OBJ); else if (ti.length > *len) err = gpg_error (GPG_ERR_INV_BER); else if (!(err = _ksba_asntime_to_iso (*buf, ti.length, ti.tag == TYPE_UTC_TIME, isotime))) parse_skip (buf, len, &ti); return err; } diff --git a/src/ber-help.h b/src/ber-help.h index 20d39e8..8223a90 100644 --- a/src/ber-help.h +++ b/src/ber-help.h @@ -1,122 +1,127 @@ /* ber-help.h - Basic Encoding Rules helpers * Copyright (C) 2001, 2012 g10 Code GmbH * * This file is part of KSBA. * * KSBA is free software; you can redistribute it and/or modify * it under the terms of either * * - the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at * your option) any later version. * * or * * - the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at * your option) any later version. * * or both in parallel, as here. * * KSBA is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public * License for more details. * * You should have received a copies of the GNU General Public License * and the GNU Lesser General Public License along with this program; * if not, see . */ #ifndef BER_HELP_H #define BER_HELP_H 1 struct tag_info { enum tag_class class; int is_constructed; unsigned long tag; unsigned long length; /* length part of the TLV */ int ndef; /* It is an indefinite length */ size_t nhdr; /* number of bytes in the TL */ unsigned char buf[10]; /* buffer for the TL */ const char *err_string; int non_der; }; gpg_error_t _ksba_ber_read_tl (ksba_reader_t reader, struct tag_info *ti); gpg_error_t _ksba_ber_parse_tl (unsigned char const **buffer, size_t *size, struct tag_info *ti); gpg_error_t _ksba_ber_write_tl (ksba_writer_t writer, unsigned long tag, enum tag_class class, int constructed, unsigned long length); size_t _ksba_ber_encode_tl (unsigned char *buffer, unsigned long tag, enum tag_class class, int constructed, unsigned long length); size_t _ksba_ber_count_tl (unsigned long tag, enum tag_class class, int constructed, unsigned long length); static inline void parse_skip (unsigned char const **buf, size_t *len, struct tag_info *ti) { if (ti->length) { assert (ti->length <= *len); *len -= ti->length; *buf += ti->length; } } gpg_error_t _ksba_parse_sequence (unsigned char const **buf, size_t *len, struct tag_info *ti); #define parse_sequence(buf,len,ti) \ _ksba_parse_sequence ((buf),(len),(ti)) gpg_error_t _ksba_parse_context_tag (unsigned char const **buf, size_t *len, struct tag_info *ti, int tag); #define parse_context_tag(buf,len,ti,tag) \ _ksba_parse_context_tag ((buf),(len),(ti),(tag)) gpg_error_t _ksba_parse_enumerated (unsigned char const **buf, size_t *len, struct tag_info *ti, size_t maxlen); #define parse_enumerated(buf,len,ti,maxlen) \ _ksba_parse_enumerated ((buf),(len),(ti),(maxlen)) gpg_error_t _ksba_parse_integer (unsigned char const **buf, size_t *len, struct tag_info *ti); #define parse_integer(buf,len,ti) \ _ksba_parse_integer ((buf),(len),(ti)) gpg_error_t _ksba_parse_octet_string (unsigned char const **buf, size_t *len, struct tag_info *ti); #define parse_octet_string(buf,len,ti) \ _ksba_parse_octet_string ((buf),(len),(ti)) gpg_error_t _ksba_parse_optional_boolean (unsigned char const **buf, size_t *len, int *r_bool); #define parse_optional_boolean(buf,len,r_bool) \ _ksba_parse_optional_boolean ((buf),(len),(r_bool)) +gpg_error_t _ksba_parse_optional_null (unsigned char const **buf, size_t *len, + int *r_seen); +#define parse_optional_null(buf,len,r_seen) \ + _ksba_parse_optional_null ((buf),(len),(r_seen)) + gpg_error_t _ksba_parse_object_id_into_str (unsigned char const **buf, size_t *len, char **oid); #define parse_object_id_into_str(buf,len,r_oid) \ _ksba_parse_object_id_into_str ((buf),(len),(r_oid)) gpg_error_t _ksba_parse_asntime_into_isotime (unsigned char const **buf, size_t *len, ksba_isotime_t isotime); #define parse_asntime_into_isotime(buf,len,isotime) \ _ksba_parse_asntime_into_isotime ((buf),(len),(isotime)) #endif /*BER_HELP_H*/ diff --git a/src/crl.c b/src/crl.c index 60f955a..9f71c85 100644 --- a/src/crl.c +++ b/src/crl.c @@ -1,1459 +1,1459 @@ /* crl.c - CRL parser * Copyright (C) 2002, 2004, 2005, 2012 g10 Code GmbH * * This file is part of KSBA. * * KSBA is free software; you can redistribute it and/or modify * it under the terms of either * * - the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at * your option) any later version. * * or * * - the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at * your option) any later version. * * or both in parallel, as here. * * KSBA is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public * License for more details. * * You should have received a copies of the GNU General Public License * and the GNU Lesser General Public License along with this program; * if not, see . */ #include #include #include #include #include #include #include "util.h" #include "convert.h" #include "keyinfo.h" #include "der-encoder.h" #include "ber-help.h" #include "ber-decoder.h" #include "crl.h" #include "stringbuf.h" static const char oidstr_crlNumber[] = "2.5.29.20"; static const char oidstr_crlReason[] = "2.5.29.21"; #if 0 static const char oidstr_issuingDistributionPoint[] = "2.5.29.28"; #endif static const char oidstr_certificateIssuer[] = "2.5.29.29"; static const char oidstr_authorityKeyIdentifier[] = "2.5.29.35"; /* We better buffer the hashing. */ static inline void do_hash (ksba_crl_t crl, const void *buffer, size_t length) { while (length) { size_t n = length; if (crl->hashbuf.used + n > sizeof crl->hashbuf.buffer) n = sizeof crl->hashbuf.buffer - crl->hashbuf.used; memcpy (crl->hashbuf.buffer+crl->hashbuf.used, buffer, n); crl->hashbuf.used += n; if (crl->hashbuf.used == sizeof crl->hashbuf.buffer) { if (crl->hash_fnc) crl->hash_fnc (crl->hash_fnc_arg, crl->hashbuf.buffer, crl->hashbuf.used); crl->hashbuf.used = 0; } buffer = (const char *)buffer + n; length -= n; } } #define HASH(a,b) do_hash (crl, (a), (b)) /** * ksba_crl_new: * * Create a new and empty CRL object * * Return value: A CRL object or an error code. **/ gpg_error_t ksba_crl_new (ksba_crl_t *r_crl) { *r_crl = xtrycalloc (1, sizeof **r_crl); if (!*r_crl) return gpg_error_from_errno (errno); return 0; } /** * ksba_crl_release: * @crl: A CRL object * * Release a CRL object. **/ void ksba_crl_release (ksba_crl_t crl) { if (!crl) return; xfree (crl->algo.oid); xfree (crl->algo.parm); _ksba_asn_release_nodes (crl->issuer.root); xfree (crl->issuer.image); xfree (crl->item.serial); xfree (crl->sigval); while (crl->extension_list) { crl_extn_t tmp = crl->extension_list->next; xfree (crl->extension_list->oid); xfree (crl->extension_list); crl->extension_list = tmp; } xfree (crl); } gpg_error_t ksba_crl_set_reader (ksba_crl_t crl, ksba_reader_t r) { if (!crl || !r) return gpg_error (GPG_ERR_INV_VALUE); crl->reader = r; return 0; } /* Provide a hash function so that we are able to hash the data */ void ksba_crl_set_hash_function (ksba_crl_t crl, void (*hash_fnc)(void *, const void *, size_t), void *hash_fnc_arg) { if (crl) { crl->hash_fnc = hash_fnc; crl->hash_fnc_arg = hash_fnc_arg; } } /* access functions */ /** * ksba_crl_get_digest_algo: * @cms: CMS object * * Figure out the the digest algorithm used for the signature and return * its OID. * * Return value: NULL if the signature algorithm is not yet available * or there is a mismatched between "tbsCertList.signature" and * "signatureAlgorithm"; on success the OID is returned which is valid * as long as the CRL object is valid. **/ const char * ksba_crl_get_digest_algo (ksba_crl_t crl) { if (!crl) return NULL; /* fixme: implement the described check */ return crl->algo.oid; } /** * ksba_crl_get_issuer: * @cms: CMS object * @r_issuer: returns the issuer * * This functions returns the issuer of the CRL. The caller must * release the returned object. * * Return value: 0 on success or an error code **/ gpg_error_t ksba_crl_get_issuer (ksba_crl_t crl, char **r_issuer) { gpg_error_t err; AsnNode n; const unsigned char *image; if (!crl || !r_issuer) return gpg_error (GPG_ERR_INV_VALUE); if (!crl->issuer.root) return gpg_error (GPG_ERR_NO_DATA); n = crl->issuer.root; image = crl->issuer.image; if (!n || !n->down) return gpg_error (GPG_ERR_NO_VALUE); n = n->down; /* dereference the choice node */ if (n->off == -1) { /* fputs ("get_issuer problem at node:\n", stderr); */ /* _ksba_asn_node_dump_all (n, stderr); */ return gpg_error (GPG_ERR_GENERAL); } err = _ksba_dn_to_str (image, n, r_issuer); return err; } /* Return the CRL extension in OID, CRITICAL, DER and DERLEN. The caller should iterate IDX from 0 upwards until GPG_ERR_EOF is returned. Note, that the returned values are valid as long as the context is valid and no new parsing has been started. */ gpg_error_t ksba_crl_get_extension (ksba_crl_t crl, int idx, char const **oid, int *critical, unsigned char const **der, size_t *derlen) { crl_extn_t e; if (!crl) return gpg_error (GPG_ERR_INV_VALUE); if (idx < 0) return gpg_error (GPG_ERR_INV_INDEX); for (e=crl->extension_list; e && idx; e = e->next, idx-- ) ; if (!e) return gpg_error (GPG_ERR_EOF); if (oid) *oid = e->oid; if (critical) *critical = e->critical; if (der) *der = e->der; if (derlen) *derlen = e->derlen; return 0; } /* Return the authorityKeyIdentifier in r_name and r_serial or in r_keyID. GPG_ERR_NO_DATA is returned if no authorityKeyIdentifier or only one using the keyIdentifier method is available and R_KEYID is NULL. FIXME: This function shares a lot of code with the one in cert.c */ gpg_error_t ksba_crl_get_auth_key_id (ksba_crl_t crl, ksba_sexp_t *r_keyid, ksba_name_t *r_name, ksba_sexp_t *r_serial) { gpg_error_t err; size_t derlen; const unsigned char *der; const unsigned char *keyid_der = NULL; size_t keyid_derlen = 0; struct tag_info ti; char numbuf[30]; size_t numbuflen; crl_extn_t e; if (r_keyid) *r_keyid = NULL; if (!crl || !r_name || !r_serial) return gpg_error (GPG_ERR_INV_VALUE); *r_name = NULL; *r_serial = NULL; for (e=crl->extension_list; e; e = e->next) if (!strcmp (e->oid, oidstr_authorityKeyIdentifier)) break; if (!e) return gpg_error (GPG_ERR_NO_DATA); /* not available */ /* Check that there is only one */ { crl_extn_t e2; for (e2 = e->next; e2; e2 = e2->next) if (!strcmp (e2->oid, oidstr_authorityKeyIdentifier)) return gpg_error (GPG_ERR_DUP_VALUE); } der = e->der; derlen = e->derlen; err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CRL_OBJ); if (ti.ndef) return gpg_error (GPG_ERR_NOT_DER_ENCODED); if (ti.length > derlen) return gpg_error (GPG_ERR_BAD_BER); err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) return err; if (ti.class != CLASS_CONTEXT) return gpg_error (GPG_ERR_INV_CRL_OBJ); /* We expected a tag. */ if (ti.ndef) return gpg_error (GPG_ERR_NOT_DER_ENCODED); if (derlen < ti.length) return gpg_error (GPG_ERR_BAD_BER); if (ti.tag == 0) { /* keyIdentifier: Just save it away for later use. */ keyid_der = der; keyid_derlen = ti.length; der += ti.length; derlen -= ti.length; /* If the keyid has been requested but no other data follows, we directly jump to the end. */ if (r_keyid && !derlen) goto build_keyid; if (!derlen) return gpg_error (GPG_ERR_NO_DATA); /* not available */ err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) return err; if (ti.class != CLASS_CONTEXT) return gpg_error (GPG_ERR_INV_CRL_OBJ); /* we expected a tag */ if (ti.ndef) return gpg_error (GPG_ERR_NOT_DER_ENCODED); if (derlen < ti.length) return gpg_error (GPG_ERR_BAD_BER); } if (ti.tag != 1 || !derlen) return gpg_error (GPG_ERR_INV_CRL_OBJ); err = _ksba_name_new_from_der (r_name, der, ti.length); if (err) return err; der += ti.length; derlen -= ti.length; /* Fixme: we should release r_name before returning on error */ err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) return err; if (ti.class != CLASS_CONTEXT) return gpg_error (GPG_ERR_INV_CRL_OBJ); /* we expected a tag */ if (ti.ndef) return gpg_error (GPG_ERR_NOT_DER_ENCODED); if (derlen < ti.length) return gpg_error (GPG_ERR_BAD_BER); if (ti.tag != 2 || !derlen) return gpg_error (GPG_ERR_INV_CRL_OBJ); sprintf (numbuf,"(%u:", (unsigned int)ti.length); numbuflen = strlen (numbuf); *r_serial = xtrymalloc (numbuflen + ti.length + 2); if (!*r_serial) return gpg_error_from_errno (errno); strcpy (*r_serial, numbuf); memcpy (*r_serial+numbuflen, der, ti.length); (*r_serial)[numbuflen + ti.length] = ')'; (*r_serial)[numbuflen + ti.length + 1] = 0; build_keyid: if (r_keyid && keyid_der && keyid_derlen) { sprintf (numbuf,"(%u:", (unsigned int)keyid_derlen); numbuflen = strlen (numbuf); *r_keyid = xtrymalloc (numbuflen + keyid_derlen + 2); if (!*r_keyid) return gpg_error (GPG_ERR_ENOMEM); strcpy (*r_keyid, numbuf); memcpy (*r_keyid+numbuflen, keyid_der, keyid_derlen); (*r_keyid)[numbuflen + keyid_derlen] = ')'; (*r_keyid)[numbuflen + keyid_derlen + 1] = 0; } return 0; } /* Return the optional crlNumber in NUMBER or GPG_ERR_NO_DATA if it is not available. Caller must release NUMBER if the fuction retruned with success. */ gpg_error_t ksba_crl_get_crl_number (ksba_crl_t crl, ksba_sexp_t *number) { gpg_error_t err; size_t derlen; const unsigned char *der; struct tag_info ti; char numbuf[30]; size_t numbuflen; crl_extn_t e; if (!crl || !number) return gpg_error (GPG_ERR_INV_VALUE); *number = NULL; for (e=crl->extension_list; e; e = e->next) if (!strcmp (e->oid, oidstr_crlNumber)) break; if (!e) return gpg_error (GPG_ERR_NO_DATA); /* not available */ /* Check that there is only one. */ { crl_extn_t e2; for (e2 = e->next; e2; e2 = e2->next) if (!strcmp (e2->oid, oidstr_crlNumber)) return gpg_error (GPG_ERR_DUP_VALUE); } der = e->der; derlen = e->derlen; err = parse_integer (&der, &derlen, &ti); if (err) return err; sprintf (numbuf,"(%u:", (unsigned int)ti.length); numbuflen = strlen (numbuf); *number = xtrymalloc (numbuflen + ti.length + 2); if (!*number) return gpg_error_from_errno (errno); strcpy (*number, numbuf); memcpy (*number+numbuflen, der, ti.length); (*number)[numbuflen + ti.length] = ')'; (*number)[numbuflen + ti.length + 1] = 0; return 0; } /** * ksba_crl_get_update_times: * @crl: CRL object * @this: Returns the thisUpdate value * @next: Returns the nextUpdate value. * * THIS and NEXT may be given as NULL if the value is not required. * Return value: 0 on success or an error code **/ gpg_error_t ksba_crl_get_update_times (ksba_crl_t crl, ksba_isotime_t this, ksba_isotime_t next) { if (this) *this = 0; if (next) *next = 0; if (!crl) return gpg_error (GPG_ERR_INV_VALUE); if (!*crl->this_update) return gpg_error (GPG_ERR_INV_TIME); if (this) _ksba_copy_time (this, crl->this_update); if (next) _ksba_copy_time (next, crl->next_update); return 0; } /** * ksba_crl_get_item: * @crl: CRL object * @r_serial: Returns a S-exp with the serial number; caller must free. * @r_revocation_date: Returns the recocation date * @r_reason: Return the reason for revocation * * Return the serial number, revocation time and reason of the current * item. Any of these arguments may be passed as %NULL if the value * is not of interest. This function should be used after the parse * function came back with %KSBA_SR_GOT_ITEM. For efficiency reasons * the function should be called only once, the implementation may * return an error for the second call. * * Return value: 0 in success or an error code. **/ gpg_error_t ksba_crl_get_item (ksba_crl_t crl, ksba_sexp_t *r_serial, ksba_isotime_t r_revocation_date, ksba_crl_reason_t *r_reason) { if (r_revocation_date) *r_revocation_date = 0; if (!crl) return gpg_error (GPG_ERR_INV_VALUE); if (r_serial) { if (!crl->item.serial) return gpg_error (GPG_ERR_NO_DATA); *r_serial = crl->item.serial; crl->item.serial = NULL; } if (r_revocation_date) _ksba_copy_time (r_revocation_date, crl->item.revocation_date); if (r_reason) *r_reason = crl->item.reason; return 0; } /** * ksba_crl_get_sig_val: * @crl: CRL object * * Return the actual signature in a format suitable to be used as * input to Libgcrypt's verification function. The caller must free * the returned string. For a rsaPSS signed CRLs this function may * also be called right after rsaPSS has been detected using * ksba_crl_get_digest_algo and before the the signature value can be * retrieved. In this case an S-expression of the form * * (sig-val (hash-algo OID)(salt-length N)) * * is returned. The caller should extract the actual to be used hash * algorithm from that S-expression. Note that after the actual * signature as been seen, a similar S-expression is returned but in * this case also with the (rsa(s XXX)) list. * * Return value: NULL or a string with an S-Exp. **/ ksba_sexp_t ksba_crl_get_sig_val (ksba_crl_t crl) { ksba_sexp_t p; if (!crl) return NULL; if (!crl->sigval && crl->algo.oid && !strcmp (crl->algo.oid, "1.2.840.113549.1.1.10") && crl->algo.parm && crl->algo.parmlen) { char *pss_hash; unsigned int salt_length; struct stringbuf sb; if (_ksba_keyinfo_get_pss_info (crl->algo.parm, crl->algo.parmlen, &pss_hash, &salt_length)) return NULL; init_stringbuf (&sb, 100); - put_stringbuf (&sb,"(7:sig-val(9:hash-algo"); + put_stringbuf (&sb,"(7:sig-val(5:flags3:pss)(9:hash-algo"); put_stringbuf_sexp (&sb, pss_hash); put_stringbuf (&sb, ")(11:salt-length"); put_stringbuf_uint (&sb, salt_length); put_stringbuf (&sb, "))"); return get_stringbuf (&sb); } if (!crl->sigval) return NULL; p = crl->sigval; crl->sigval = NULL; return p; } /* Parser functions */ /* read one byte */ static int read_byte (ksba_reader_t reader) { unsigned char buf; size_t nread; int rc; do rc = ksba_reader_read (reader, &buf, 1, &nread); while (!rc && !nread); return rc? -1: buf; } /* read COUNT bytes into buffer. Return 0 on success */ static int read_buffer (ksba_reader_t reader, char *buffer, size_t count) { size_t nread; while (count) { if (ksba_reader_read (reader, buffer, count, &nread)) return -1; buffer += nread; count -= nread; } return 0; } /* Create a new decoder and run it for the given element */ /* Fixme: this code is duplicated from cms-parser.c */ static gpg_error_t create_and_run_decoder (ksba_reader_t reader, const char *elem_name, AsnNode *r_root, unsigned char **r_image, size_t *r_imagelen) { gpg_error_t err; ksba_asn_tree_t crl_tree; BerDecoder decoder; err = ksba_asn_create_tree ("tmttv2", &crl_tree); if (err) return err; decoder = _ksba_ber_decoder_new (); if (!decoder) { ksba_asn_tree_release (crl_tree); return gpg_error (GPG_ERR_ENOMEM); } err = _ksba_ber_decoder_set_reader (decoder, reader); if (err) { ksba_asn_tree_release (crl_tree); _ksba_ber_decoder_release (decoder); return err; } err = _ksba_ber_decoder_set_module (decoder, crl_tree); if (err) { ksba_asn_tree_release (crl_tree); _ksba_ber_decoder_release (decoder); return err; } err = _ksba_ber_decoder_decode (decoder, elem_name, 0, r_root, r_image, r_imagelen); _ksba_ber_decoder_release (decoder); ksba_asn_tree_release (crl_tree); return err; } /* Parse the extension in the buffer DER or length DERLEN and return the result in OID, CRITICAL, OFF and LEN. */ static gpg_error_t parse_one_extension (const unsigned char *der, size_t derlen, char **oid, int *critical, size_t *off, size_t *len) { gpg_error_t err; struct tag_info ti; const unsigned char *start = der; *oid = NULL; *critical = 0; *off = *len = 0; /* Extension ::= SEQUENCE { extnID OBJECT IDENTIFIER, critical BOOLEAN DEFAULT FALSE, extnValue OCTET STRING } */ err = parse_sequence (&der, &derlen, &ti); if (err) goto failure; err = parse_object_id_into_str (&der, &derlen, oid); if (err) goto failure; err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) goto failure; if (ti.length > derlen) return gpg_error (GPG_ERR_BAD_BER); if (ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_BOOLEAN && !ti.is_constructed) { if (ti.length != 1) goto bad_ber; *critical = !!*der; parse_skip (&der, &derlen, &ti); } else { /* Undo that read. */ der -= ti.nhdr; derlen += ti.nhdr; } err = parse_octet_string (&der, &derlen, &ti); if (err) goto failure; *off = der - start; *len = ti.length; return 0; bad_ber: err = gpg_error (GPG_ERR_BAD_BER); failure: xfree (*oid); *oid = NULL; return err; } /* Store an extension into the context. */ static gpg_error_t store_one_extension (ksba_crl_t crl, const unsigned char *der, size_t derlen) { gpg_error_t err; char *oid; int critical; size_t off, len; crl_extn_t e; err = parse_one_extension (der, derlen, &oid, &critical, &off, &len); if (err) return err; e = xtrymalloc (sizeof *e + len - 1); if (!e) { err = gpg_error_from_errno (errno); xfree (oid); return err; } e->oid = oid; e->critical = critical; e->derlen = len; memcpy (e->der, der + off, len); e->next = crl->extension_list; crl->extension_list = e; return 0; } /* Parse the fixed block at the beginning. We use a custom parser here because our BER decoder is not yet able to stop at certain points */ static gpg_error_t parse_to_next_update (ksba_crl_t crl) { gpg_error_t err; struct tag_info ti; unsigned long outer_len, tbs_len; int outer_ndef, tbs_ndef; int c; unsigned char tmpbuf[500]; /* for OID or algorithmIdentifier */ size_t nread; /* read the outer sequence */ err = _ksba_ber_read_tl (crl->reader, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CRL_OBJ); outer_len = ti.length; outer_ndef = ti.ndef; if (!outer_ndef && outer_len < 10) return gpg_error (GPG_ERR_TOO_SHORT); /* read the tbs sequence */ err = _ksba_ber_read_tl (crl->reader, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CRL_OBJ); HASH (ti.buf, ti.nhdr); if (!outer_ndef) { if (outer_len < ti.nhdr) return gpg_error (GPG_ERR_BAD_BER); /* Triplet header larger than outer sequence */ outer_len -= ti.nhdr; if (!ti.ndef && outer_len < ti.length) return gpg_error (GPG_ERR_BAD_BER); /* Triplet larger than outer sequence */ outer_len -= ti.length; } tbs_len = ti.length; tbs_ndef = ti.ndef; if (!tbs_ndef && tbs_len < 10) return gpg_error (GPG_ERR_TOO_SHORT); /* read the optional version integer */ crl->crl_version = -1; err = _ksba_ber_read_tl (crl->reader, &ti); if (err) return err; if ( ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_INTEGER) { if ( ti.is_constructed || !ti.length ) return gpg_error (GPG_ERR_INV_CRL_OBJ); HASH (ti.buf, ti.nhdr); if (!tbs_ndef) { if (tbs_len < ti.nhdr) return gpg_error (GPG_ERR_BAD_BER); tbs_len -= ti.nhdr; if (tbs_len < ti.length) return gpg_error (GPG_ERR_BAD_BER); tbs_len -= ti.length; } /* fixme: we should also check the outer data length here and in the follwing code. It might however be easier to to thsi at the end of this sequence */ if (ti.length != 1) return gpg_error (GPG_ERR_UNSUPPORTED_CRL_VERSION); if ( (c=read_byte (crl->reader)) == -1) { err = ksba_reader_error (crl->reader); return err? err : gpg_error (GPG_ERR_GENERAL); } if ( !(c == 0 || c == 1) ) return gpg_error (GPG_ERR_UNSUPPORTED_CRL_VERSION); { unsigned char tmp = c; HASH (&tmp, 1); } crl->crl_version = c; err = _ksba_ber_read_tl (crl->reader, &ti); if (err) return err; } /* read the algorithm identifier */ if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CRL_OBJ); if (!tbs_ndef) { if (tbs_len < ti.nhdr) return gpg_error (GPG_ERR_BAD_BER); tbs_len -= ti.nhdr; if (!ti.ndef && tbs_len < ti.length) return gpg_error (GPG_ERR_BAD_BER); tbs_len -= ti.length; } if (ti.nhdr + ti.length >= DIM(tmpbuf)) return gpg_error (GPG_ERR_TOO_LARGE); memcpy (tmpbuf, ti.buf, ti.nhdr); err = read_buffer (crl->reader, tmpbuf+ti.nhdr, ti.length); if (err) return err; HASH (tmpbuf, ti.nhdr+ti.length); xfree (crl->algo.oid); crl->algo.oid = NULL; xfree (crl->algo.parm); crl->algo.parm = NULL; err = _ksba_parse_algorithm_identifier2 (tmpbuf, ti.nhdr+ti.length, &nread, &crl->algo.oid, &crl->algo.parm, &crl->algo.parmlen); if (err) return err; assert (nread <= ti.nhdr + ti.length); if (nread < ti.nhdr + ti.length) return gpg_error (GPG_ERR_TOO_SHORT); /* read the name */ { unsigned long n = ksba_reader_tell (crl->reader); err = create_and_run_decoder (crl->reader, "TMTTv2.CertificateList.tbsCertList.issuer", &crl->issuer.root, &crl->issuer.image, &crl->issuer.imagelen); if (err) return err; /* imagelen might be larger than the valid data (due to read ahead). So we need to get the count from the reader */ n = ksba_reader_tell (crl->reader) - n; if (n > crl->issuer.imagelen) return gpg_error (GPG_ERR_BUG); HASH (crl->issuer.image, n); if (!tbs_ndef) { if (tbs_len < n) return gpg_error (GPG_ERR_BAD_BER); tbs_len -= n; } } /* read the thisUpdate time */ err = _ksba_ber_read_tl (crl->reader, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && (ti.tag == TYPE_UTC_TIME || ti.tag == TYPE_GENERALIZED_TIME) && !ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CRL_OBJ); if (!tbs_ndef) { if (tbs_len < ti.nhdr) return gpg_error (GPG_ERR_BAD_BER); tbs_len -= ti.nhdr; if (!ti.ndef && tbs_len < ti.length) return gpg_error (GPG_ERR_BAD_BER); tbs_len -= ti.length; } if (ti.nhdr + ti.length >= DIM(tmpbuf)) return gpg_error (GPG_ERR_TOO_LARGE); memcpy (tmpbuf, ti.buf, ti.nhdr); err = read_buffer (crl->reader, tmpbuf+ti.nhdr, ti.length); if (err) return err; HASH (tmpbuf, ti.nhdr+ti.length); _ksba_asntime_to_iso (tmpbuf+ti.nhdr, ti.length, ti.tag == TYPE_UTC_TIME, crl->this_update); /* Read the optional nextUpdate time. */ err = _ksba_ber_read_tl (crl->reader, &ti); if (err) return err; if ( ti.class == CLASS_UNIVERSAL && (ti.tag == TYPE_UTC_TIME || ti.tag == TYPE_GENERALIZED_TIME) && !ti.is_constructed ) { if (!tbs_ndef) { if (tbs_len < ti.nhdr) return gpg_error (GPG_ERR_BAD_BER); tbs_len -= ti.nhdr; if (!ti.ndef && tbs_len < ti.length) return gpg_error (GPG_ERR_BAD_BER); tbs_len -= ti.length; } if (ti.nhdr + ti.length >= DIM(tmpbuf)) return gpg_error (GPG_ERR_TOO_LARGE); memcpy (tmpbuf, ti.buf, ti.nhdr); err = read_buffer (crl->reader, tmpbuf+ti.nhdr, ti.length); if (err) return err; HASH (tmpbuf, ti.nhdr+ti.length); _ksba_asntime_to_iso (tmpbuf+ti.nhdr, ti.length, ti.tag == TYPE_UTC_TIME, crl->next_update); err = _ksba_ber_read_tl (crl->reader, &ti); if (err) return err; } /* Read the first sequence tag of the optional SEQ of SEQ. */ if (tbs_ndef || tbs_len) { if (ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed ) { /* yes, there is one */ HASH (ti.buf, ti.nhdr); if (!tbs_ndef) { if (tbs_len < ti.nhdr) return gpg_error (GPG_ERR_BAD_BER); tbs_len -= ti.nhdr; if (!ti.ndef && tbs_len < ti.length) return gpg_error (GPG_ERR_BAD_BER); tbs_len -= ti.length; } crl->state.have_seqseq = 1; crl->state.seqseq_ndef = ti.ndef; crl->state.seqseq_len = ti.length; /* and read the next */ err = _ksba_ber_read_tl (crl->reader, &ti); if (err) return err; } } /* We need to save some stuff for the next round. */ crl->state.ti = ti; crl->state.outer_ndef = outer_ndef; crl->state.outer_len = outer_len; crl->state.tbs_ndef = tbs_ndef; crl->state.tbs_len = tbs_len; return 0; } /* Store an entry extension into the current item. */ static gpg_error_t store_one_entry_extension (ksba_crl_t crl, const unsigned char *der, size_t derlen) { gpg_error_t err; char *oid; int critical; size_t off, len; err = parse_one_extension (der, derlen, &oid, &critical, &off, &len); if (err) return err; if (!strcmp (oid, oidstr_crlReason)) { struct tag_info ti; const unsigned char *buf = der+off; size_t mylen = len; err = parse_enumerated (&buf, &mylen, &ti, 1); if (err) return err; /* Note that we OR the values so that in case this extension is repeated we can track all reason codes. */ switch (*buf) { case 0: crl->item.reason |= KSBA_CRLREASON_UNSPECIFIED; break; case 1: crl->item.reason |= KSBA_CRLREASON_KEY_COMPROMISE; break; case 2: crl->item.reason |= KSBA_CRLREASON_CA_COMPROMISE; break; case 3: crl->item.reason |= KSBA_CRLREASON_AFFILIATION_CHANGED; break; case 4: crl->item.reason |= KSBA_CRLREASON_SUPERSEDED; break; case 5: crl->item.reason |= KSBA_CRLREASON_CESSATION_OF_OPERATION; break; case 6: crl->item.reason |= KSBA_CRLREASON_CERTIFICATE_HOLD; break; case 8: crl->item.reason |= KSBA_CRLREASON_REMOVE_FROM_CRL; break; case 9: crl->item.reason |= KSBA_CRLREASON_PRIVILEGE_WITHDRAWN; break; case 10: crl->item.reason |= KSBA_CRLREASON_AA_COMPROMISE; break; default: crl->item.reason |= KSBA_CRLREASON_OTHER; break; } } if (!strcmp (oid, oidstr_certificateIssuer)) { /* FIXME: We need to implement this. */ } else if (critical) err = gpg_error (GPG_ERR_UNKNOWN_CRIT_EXTN); xfree (oid); return err; } /* Parse the revokedCertificates SEQUENCE of SEQUENCE using a custom parser for efficiency and return after each entry */ static gpg_error_t parse_crl_entry (ksba_crl_t crl, int *got_entry) { gpg_error_t err; struct tag_info ti = crl->state.ti; unsigned long seqseq_len= crl->state.seqseq_len; int seqseq_ndef = crl->state.seqseq_ndef; unsigned long len; int ndef; unsigned char tmpbuf[4096]; /* for time, serial number and extensions */ char numbuf[22]; int numbuflen; /* Check the length to see whether we are at the end of the seq but do this only when we know that we have this optional seq of seq. */ if (!crl->state.have_seqseq) return 0; /* ready (no entries at all) */ if (!seqseq_ndef && !seqseq_len) return 0; /* ready */ /* if this is not a SEQUENCE the CRL is invalid */ if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CRL_OBJ); HASH (ti.buf, ti.nhdr); if (!seqseq_ndef) { if (seqseq_len < ti.nhdr) return gpg_error (GPG_ERR_BAD_BER); seqseq_len -= ti.nhdr; if (!ti.ndef && seqseq_len < ti.length) return gpg_error (GPG_ERR_BAD_BER); seqseq_len -= ti.length; } ndef = ti.ndef; len = ti.length; /* get the serial number */ err = _ksba_ber_read_tl (crl->reader, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_INTEGER && !ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CRL_OBJ); if (!ndef) { if (len < ti.nhdr) return gpg_error (GPG_ERR_BAD_BER); len -= ti.nhdr; if (!ti.ndef && len < ti.length) return gpg_error (GPG_ERR_BAD_BER); len -= ti.length; } if (ti.nhdr + ti.length >= DIM(tmpbuf)) return gpg_error (GPG_ERR_TOO_LARGE); memcpy (tmpbuf, ti.buf, ti.nhdr); err = read_buffer (crl->reader, tmpbuf+ti.nhdr, ti.length); if (err) return err; HASH (tmpbuf, ti.nhdr+ti.length); xfree (crl->item.serial); sprintf (numbuf,"(%u:", (unsigned int)ti.length); numbuflen = strlen (numbuf); crl->item.serial = xtrymalloc (numbuflen + ti.length + 2); if (!crl->item.serial) return gpg_error (GPG_ERR_ENOMEM); strcpy (crl->item.serial, numbuf); memcpy (crl->item.serial+numbuflen, tmpbuf+ti.nhdr, ti.length); crl->item.serial[numbuflen + ti.length] = ')'; crl->item.serial[numbuflen + ti.length + 1] = 0; crl->item.reason = 0; /* get the revocation time */ err = _ksba_ber_read_tl (crl->reader, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && (ti.tag == TYPE_UTC_TIME || ti.tag == TYPE_GENERALIZED_TIME) && !ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CRL_OBJ); if (!ndef) { if (len < ti.nhdr) return gpg_error (GPG_ERR_BAD_BER); len -= ti.nhdr; if (!ti.ndef && len < ti.length) return gpg_error (GPG_ERR_BAD_BER); len -= ti.length; } if (ti.nhdr + ti.length >= DIM(tmpbuf)) return gpg_error (GPG_ERR_TOO_LARGE); memcpy (tmpbuf, ti.buf, ti.nhdr); err = read_buffer (crl->reader, tmpbuf+ti.nhdr, ti.length); if (err) return err; HASH (tmpbuf, ti.nhdr+ti.length); _ksba_asntime_to_iso (tmpbuf+ti.nhdr, ti.length, ti.tag == TYPE_UTC_TIME, crl->item.revocation_date); /* if there is still space we must parse the optional entryExtensions */ if (ndef) return gpg_error (GPG_ERR_UNSUPPORTED_ENCODING); else if (len) { /* read the outer sequence */ err = _ksba_ber_read_tl (crl->reader, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CRL_OBJ); if (ti.ndef) return gpg_error (GPG_ERR_UNSUPPORTED_ENCODING); HASH (ti.buf, ti.nhdr); if (len < ti.nhdr) return gpg_error (GPG_ERR_BAD_BER); len -= ti.nhdr; if (len < ti.length) return gpg_error (GPG_ERR_BAD_BER); /* now loop over the extensions */ while (len) { err = _ksba_ber_read_tl (crl->reader, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CRL_OBJ); if (ti.ndef) return gpg_error (GPG_ERR_UNSUPPORTED_ENCODING); if (len < ti.nhdr) return gpg_error (GPG_ERR_BAD_BER); len -= ti.nhdr; if (len < ti.length) return gpg_error (GPG_ERR_BAD_BER); len -= ti.length; if (ti.nhdr + ti.length >= DIM(tmpbuf)) return gpg_error (GPG_ERR_TOO_LARGE); memcpy (tmpbuf, ti.buf, ti.nhdr); err = read_buffer (crl->reader, tmpbuf+ti.nhdr, ti.length); if (err) return err; HASH (tmpbuf, ti.nhdr+ti.length); err = store_one_entry_extension (crl, tmpbuf, ti.nhdr+ti.length); if (err) return err; } } /* read ahead */ err = _ksba_ber_read_tl (crl->reader, &ti); if (err) return err; *got_entry = 1; /* Fixme: the seqseq length is not correct if any element was ndef'd */ crl->state.ti = ti; crl->state.seqseq_ndef = seqseq_ndef; crl->state.seqseq_len = seqseq_len; return 0; } /* This function is used when a [0] tag was encountered to read the crlExtensions */ static gpg_error_t parse_crl_extensions (ksba_crl_t crl) { gpg_error_t err; struct tag_info ti = crl->state.ti; unsigned long ext_len, len; unsigned char tmpbuf[4096]; /* for extensions */ /* if we do not have a tag [0] we are done with this */ if (!(ti.class == CLASS_CONTEXT && ti.tag == 0 && ti.is_constructed)) return 0; if (ti.ndef) return gpg_error (GPG_ERR_UNSUPPORTED_ENCODING); HASH (ti.buf, ti.nhdr); ext_len = ti.length; /* read the outer sequence */ err = _ksba_ber_read_tl (crl->reader, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CRL_OBJ); if (ti.ndef) return gpg_error (GPG_ERR_UNSUPPORTED_ENCODING); HASH (ti.buf, ti.nhdr); if (ext_len < ti.nhdr) return gpg_error (GPG_ERR_BAD_BER); ext_len -= ti.nhdr; if (ext_len < ti.length) return gpg_error (GPG_ERR_BAD_BER); len = ti.length; /* now loop over the extensions */ while (len) { err = _ksba_ber_read_tl (crl->reader, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CRL_OBJ); if (ti.ndef) return gpg_error (GPG_ERR_UNSUPPORTED_ENCODING); if (len < ti.nhdr) return gpg_error (GPG_ERR_BAD_BER); len -= ti.nhdr; if (len < ti.length) return gpg_error (GPG_ERR_BAD_BER); len -= ti.length; if (ti.nhdr + ti.length >= DIM(tmpbuf)) return gpg_error (GPG_ERR_TOO_LARGE); /* fixme use a larger buffer if the extension does not fit into tmpbuf */ memcpy (tmpbuf, ti.buf, ti.nhdr); err = read_buffer (crl->reader, tmpbuf+ti.nhdr, ti.length); if (err) return err; HASH (tmpbuf, ti.nhdr+ti.length); err = store_one_extension (crl, tmpbuf, ti.nhdr+ti.length); if (err) return err; } /* read ahead */ err = _ksba_ber_read_tl (crl->reader, &ti); if (err) return err; crl->state.ti = ti; return 0; } /* Parse the signatureAlgorithm and the signature */ static gpg_error_t parse_signature (ksba_crl_t crl) { gpg_error_t err; struct tag_info ti = crl->state.ti; unsigned char tmpbuf[2048]; /* for the sig algo and bitstr */ size_t n, n2; /* We do read the stuff into a temporary buffer so that we can apply our parsing function for this structure */ /* read the algorithmIdentifier sequence */ if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CRL_OBJ); if (ti.ndef) return gpg_error (GPG_ERR_UNSUPPORTED_ENCODING); n = ti.nhdr + ti.length; if (n >= DIM(tmpbuf)) return gpg_error (GPG_ERR_TOO_LARGE); memcpy (tmpbuf, ti.buf, ti.nhdr); err = read_buffer (crl->reader, tmpbuf+ti.nhdr, ti.length); if (err) return err; /* and append the bit string */ err = _ksba_ber_read_tl (crl->reader, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_BIT_STRING && !ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CRL_OBJ); n2 = ti.nhdr + ti.length; if (n + n2 >= DIM(tmpbuf)) return gpg_error (GPG_ERR_TOO_LARGE); memcpy (tmpbuf+n, ti.buf, ti.nhdr); err = read_buffer (crl->reader, tmpbuf+n+ti.nhdr, ti.length); if (err) return err; /* now parse it */ xfree (crl->sigval); crl->sigval = NULL; return _ksba_sigval_to_sexp (tmpbuf, n + n2, &crl->sigval); } /* The actual parser which should be used with a new CRL object and run in a loop until the the KSBA_SR_READY is encountered */ gpg_error_t ksba_crl_parse (ksba_crl_t crl, ksba_stop_reason_t *r_stopreason) { enum { sSTART, sCRLENTRY, sCRLEXT, sERROR } state = sERROR; ksba_stop_reason_t stop_reason; gpg_error_t err = 0; int got_entry = 0; if (!crl || !r_stopreason) return gpg_error (GPG_ERR_INV_VALUE); if (!crl->any_parse_done) { /* first time initialization of the stop reason */ *r_stopreason = 0; crl->any_parse_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_BEGIN_ITEMS: case KSBA_SR_GOT_ITEM: state = sCRLENTRY; break; case KSBA_SR_END_ITEMS: state = sCRLEXT; 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 = parse_to_next_update (crl); break; case sCRLENTRY: err = parse_crl_entry (crl, &got_entry); break; case sCRLEXT: err = parse_crl_extensions (crl); if (!err) { if (crl->hash_fnc && crl->hashbuf.used) crl->hash_fnc (crl->hash_fnc_arg, crl->hashbuf.buffer, crl->hashbuf.used); crl->hashbuf.used = 0; err = parse_signature (crl); } 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_BEGIN_ITEMS; break; case sCRLENTRY: stop_reason = got_entry? KSBA_SR_GOT_ITEM : KSBA_SR_END_ITEMS; break; case sCRLEXT: stop_reason = KSBA_SR_READY; break; default: break; } *r_stopreason = stop_reason; return 0; } diff --git a/src/keyinfo.c b/src/keyinfo.c index fa84274..11859cd 100644 --- a/src/keyinfo.c +++ b/src/keyinfo.c @@ -1,1919 +1,1926 @@ /* keyinfo.c - Parse and build a keyInfo structure * Copyright (C) 2001, 2002, 2007, 2008, 2012 g10 Code GmbH * * This file is part of KSBA. * * KSBA is free software; you can redistribute it and/or modify * it under the terms of either * * - the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at * your option) any later version. * * or * * - the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at * your option) any later version. * * or both in parallel, as here. * * KSBA is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public * License for more details. * * You should have received a copies of the GNU General Public License * and the GNU Lesser General Public License along with this program; * if not, see . */ /* Instead of using the ASN parser - which is easily possible - we use a simple handcoded one to speed up the operation and to make it more robust. */ #include #include #include #include #include #include "util.h" #include "asn1-func.h" #include "keyinfo.h" #include "shared.h" #include "convert.h" #include "ber-help.h" #include "sexp-parse.h" #include "stringbuf.h" /* Constants used for the public key algorithms. */ typedef enum { PKALGO_RSA, PKALGO_DSA, PKALGO_ECC, PKALGO_X25519, PKALGO_X448, PKALGO_ED25519, PKALGO_ED448 } pkalgo_t; struct algo_table_s { const char *oidstring; const unsigned char *oid; /* NULL indicattes end of table */ int oidlen; int supported; /* Values > 1 are also used to indicate hacks. */ pkalgo_t pkalgo; const char *algo_string; const char *elem_string; /* parameter name or '-' */ const char *ctrl_string; /* expected tag values (value > 127 are raw data)*/ const char *parmelem_string; /* parameter name or '-'. */ const char *parmctrl_string; /* expected tag values. */ const char *digest_string; /* The digest algo if included in the OID. */ }; /* Special values for the supported field. */ #define SUPPORTED_RSAPSS 2 static const struct algo_table_s pk_algo_table[] = { { /* iso.member-body.us.rsadsi.pkcs.pkcs-1.1 */ "1.2.840.113549.1.1.1", /* rsaEncryption (RSAES-PKCA1-v1.5) */ "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01", 9, 1, PKALGO_RSA, "rsa", "-ne", "\x30\x02\x02" }, { /* iso.member-body.us.rsadsi.pkcs.pkcs-1.7 */ "1.2.840.113549.1.1.7", /* RSAES-OAEP */ "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x07", 9, 0, PKALGO_RSA, "rsa", "-ne", "\x30\x02\x02"}, { /* iso.member-body.us.rsadsi.pkcs.pkcs-1.10 */ "1.2.840.113549.1.1.10", /* rsaPSS */ "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x0a", 9, SUPPORTED_RSAPSS, PKALGO_RSA, "rsa", "-ne", "\x30\x02\x02"}, { /* */ "2.5.8.1.1", /* rsa (ambiguous due to missing padding rules)*/ "\x55\x08\x01\x01", 4, 1, PKALGO_RSA, "ambiguous-rsa", "-ne", "\x30\x02\x02" }, { /* iso.member-body.us.x9-57.x9cm.1 */ "1.2.840.10040.4.1", /* dsa */ "\x2a\x86\x48\xce\x38\x04\x01", 7, 1, PKALGO_DSA, "dsa", "y", "\x02", "-pqg", "\x30\x02\x02\x02" }, { /* iso.member-body.us.ansi-x9-62.2.1 */ "1.2.840.10045.2.1", /* ecPublicKey */ "\x2a\x86\x48\xce\x3d\x02\x01", 7, 1, PKALGO_ECC, "ecc", "q", "\x80" }, { /* iso.identified-organization.thawte.110 */ "1.3.101.110", /* X25519 */ "\x2b\x65\x6e", 3, 1, PKALGO_X25519, "ecc", "q", "\x80" }, { /* iso.identified-organization.thawte.111 */ "1.3.101.111", /* X448 */ "\x2b\x65\x6f", 3, 1, PKALGO_X448, "ecc", "q", "\x80" }, { /* iso.identified-organization.thawte.112 */ "1.3.101.112", /* Ed25519 */ "\x2b\x65\x70", 3, 1, PKALGO_ED25519, "ecc", "q", "\x80" }, { /* iso.identified-organization.thawte.113 */ "1.3.101.113", /* Ed448 */ "\x2b\x65\x71", 3, 1, PKALGO_ED448, "ecc", "q", "\x80" }, {NULL} }; static const struct algo_table_s sig_algo_table[] = { { /* iso.member-body.us.rsadsi.pkcs.pkcs-1.5 */ "1.2.840.113549.1.1.5", /* sha1WithRSAEncryption */ "\x2A\x86\x48\x86\xF7\x0D\x01\x01\x05", 9, 1, PKALGO_RSA, "rsa", "s", "\x82", NULL, NULL, "sha1" }, { /* iso.member-body.us.rsadsi.pkcs.pkcs-1.4 */ "1.2.840.113549.1.1.4", /* md5WithRSAEncryption */ "\x2A\x86\x48\x86\xF7\x0D\x01\x01\x04", 9, 1, PKALGO_RSA, "rsa", "s", "\x82", NULL, NULL, "md5" }, { /* iso.member-body.us.rsadsi.pkcs.pkcs-1.2 */ "1.2.840.113549.1.1.2", /* md2WithRSAEncryption */ "\x2A\x86\x48\x86\xF7\x0D\x01\x01\x02", 9, 0, PKALGO_RSA, "rsa", "s", "\x82", NULL, NULL, "md2" }, { /* iso.member-body.us.x9-57.x9cm.1 */ "1.2.840.10040.4.3", /* dsa */ "\x2a\x86\x48\xce\x38\x04\x01", 7, 1, PKALGO_DSA, "dsa", "-rs", "\x30\x02\x02" }, { /* iso.member-body.us.x9-57.x9cm.3 */ "1.2.840.10040.4.3", /* dsaWithSha1 */ "\x2a\x86\x48\xce\x38\x04\x03", 7, 1, PKALGO_DSA, "dsa", "-rs", "\x30\x02\x02", NULL, NULL, "sha1" }, { /* Teletrust signature algorithm. */ "1.3.36.8.5.1.2.2", /* dsaWithRIPEMD160 */ "\x2b\x24\x08\x05\x01\x02\x02", 7, 1, PKALGO_DSA, "dsa", "-rs", "\x30\x02\x02", NULL, NULL, "rmd160" }, { /* NIST Algorithm */ "2.16.840.1.101.3.4.3.1", /* dsaWithSha224 */ "\x06\x09\x60\x86\x48\x01\x65\x03\x04\x03\x01", 11, 1, PKALGO_DSA, "dsa", "-rs", "\x30\x02\x02", NULL, NULL, "sha224" }, { /* NIST Algorithm (the draft also used .1 but we better use .2) */ "2.16.840.1.101.3.4.3.2", /* dsaWithSha256 */ "\x06\x09\x60\x86\x48\x01\x65\x03\x04\x03\x01", 11, 1, PKALGO_DSA, "dsa", "-rs", "\x30\x02\x02", NULL, NULL, "sha256" }, { /* iso.member-body.us.ansi-x9-62.signatures.ecdsa-with-sha1 */ "1.2.840.10045.4.1", /* ecdsa */ "\x2a\x86\x48\xce\x3d\x04\x01", 7, 1, PKALGO_ECC, "ecdsa", "-rs", "\x30\x02\x02", NULL, NULL, "sha1" }, { /* iso.member-body.us.ansi-x9-62.signatures.ecdsa-with-specified */ "1.2.840.10045.4.3", "\x2a\x86\x48\xce\x3d\x04\x03", 7, 1, PKALGO_ECC, "ecdsa", "-rs", "\x30\x02\x02", NULL, NULL, NULL }, /* The digest algorithm is given by the parameter. */ { /* iso.member-body.us.ansi-x9-62.signatures.ecdsa-with-sha224 */ "1.2.840.10045.4.3.1", "\x2a\x86\x48\xce\x3d\x04\x03\x01", 8, 1, PKALGO_ECC, "ecdsa", "-rs", "\x30\x02\x02", NULL, NULL, "sha224" }, { /* iso.member-body.us.ansi-x9-62.signatures.ecdsa-with-sha256 */ "1.2.840.10045.4.3.2", "\x2a\x86\x48\xce\x3d\x04\x03\x02", 8, 1, PKALGO_ECC, "ecdsa", "-rs", "\x30\x02\x02", NULL, NULL, "sha256" }, { /* iso.member-body.us.ansi-x9-62.signatures.ecdsa-with-sha384 */ "1.2.840.10045.4.3.3", "\x2a\x86\x48\xce\x3d\x04\x03\x03", 8, 1, PKALGO_ECC, "ecdsa", "-rs", "\x30\x02\x02", NULL, NULL, "sha384" }, { /* iso.member-body.us.ansi-x9-62.signatures.ecdsa-with-sha512 */ "1.2.840.10045.4.3.4", "\x2a\x86\x48\xce\x3d\x04\x03\x04", 8, 1, PKALGO_ECC, "ecdsa", "-rs", "\x30\x02\x02", NULL, NULL, "sha512" }, { /* iso.member-body.us.rsadsi.pkcs.pkcs-1.1 */ "1.2.840.113549.1.1.1", /* rsaEncryption used without hash algo*/ "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01", 9, 1, PKALGO_RSA, "rsa", "s", "\x82" }, { /* from NIST's OIW - actually belongs in a pure hash table */ "1.3.14.3.2.26", /* sha1 */ "\x2B\x0E\x03\x02\x1A", 5, 0, PKALGO_RSA, "sha-1", "", "", NULL, NULL, "sha1" }, { /* As used by telesec cards */ "1.3.36.3.3.1.2", /* rsaSignatureWithripemd160 */ "\x2b\x24\x03\x03\x01\x02", 6, 1, PKALGO_RSA, "rsa", "s", "\x82", NULL, NULL, "rmd160" }, { /* from NIST's OIW - used by TU Darmstadt */ "1.3.14.3.2.29", /* sha-1WithRSAEncryption */ "\x2B\x0E\x03\x02\x1D", 5, 1, PKALGO_RSA, "rsa", "s", "\x82", NULL, NULL, "sha1" }, { /* from PKCS#1 */ "1.2.840.113549.1.1.11", /* sha256WithRSAEncryption */ "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x0b", 9, 1, PKALGO_RSA, "rsa", "s", "\x82", NULL, NULL, "sha256" }, { /* from PKCS#1 */ "1.2.840.113549.1.1.12", /* sha384WithRSAEncryption */ "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x0c", 9, 1, PKALGO_RSA, "rsa", "s", "\x82", NULL, NULL, "sha384" }, { /* from PKCS#1 */ "1.2.840.113549.1.1.13", /* sha512WithRSAEncryption */ "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x0d", 9, 1, PKALGO_RSA, "rsa", "s", "\x82", NULL, NULL, "sha512" }, { /* iso.member-body.us.rsadsi.pkcs.pkcs-1.10 */ "1.2.840.113549.1.1.10", /* rsaPSS */ "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x0a", 9, SUPPORTED_RSAPSS, PKALGO_RSA, "rsa", "s", "\x82", NULL, NULL, NULL}, { /* TeleTrust signature scheme with RSA signature and DSI according to ISO/IEC 9796-2 with random number and RIPEMD-160. I am not sure for what this is good; thus disabled. */ "1.3.36.3.4.3.2.2", /* sigS_ISO9796-2rndWithrsa_ripemd160 */ "\x2B\x24\x03\x04\x03\x02\x02", 7, 0, PKALGO_RSA, "rsa", "s", "\x82", NULL, NULL, "rmd160" }, { /* iso.identified-organization.thawte.112 */ "1.3.101.112", /* Ed25519 */ "\x2b\x65\x70", 3, 1, PKALGO_ED25519, "eddsa", "-rs", "\x80", NULL, NULL, NULL }, { /* iso.identified-organization.thawte.113 */ "1.3.101.113", /* Ed448 */ "\x2b\x65\x71", 3, 1, PKALGO_ED448, "eddsa", "-rs", "\x80", 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" }, {NULL} }; /* This tables maps names of ECC curves names to OIDs. A similar table is used by Libgcrypt. */ static const struct { const char *oid; const char *name; } curve_names[] = { { "1.3.6.1.4.1.3029.1.5.1", "Curve25519" }, { "1.3.6.1.4.1.11591.15.1", "Ed25519" }, { "1.2.840.10045.3.1.1", "NIST P-192" }, { "1.2.840.10045.3.1.1", "nistp192" }, { "1.2.840.10045.3.1.1", "prime192v1" }, { "1.2.840.10045.3.1.1", "secp192r1" }, { "1.3.132.0.33", "NIST P-224" }, { "1.3.132.0.33", "nistp224" }, { "1.3.132.0.33", "secp224r1" }, { "1.2.840.10045.3.1.7", "NIST P-256" }, { "1.2.840.10045.3.1.7", "nistp256" }, { "1.2.840.10045.3.1.7", "prime256v1" }, { "1.2.840.10045.3.1.7", "secp256r1" }, { "1.3.132.0.34", "NIST P-384" }, { "1.3.132.0.34", "nistp384" }, { "1.3.132.0.34", "secp384r1" }, { "1.3.132.0.35", "NIST P-521" }, { "1.3.132.0.35", "nistp521" }, { "1.3.132.0.35", "secp521r1" }, { "1.3.36.3.3.2.8.1.1.1" , "brainpoolP160r1" }, { "1.3.36.3.3.2.8.1.1.3" , "brainpoolP192r1" }, { "1.3.36.3.3.2.8.1.1.5" , "brainpoolP224r1" }, { "1.3.36.3.3.2.8.1.1.7" , "brainpoolP256r1" }, { "1.3.36.3.3.2.8.1.1.9" , "brainpoolP320r1" }, { "1.3.36.3.3.2.8.1.1.11", "brainpoolP384r1" }, { "1.3.36.3.3.2.8.1.1.13", "brainpoolP512r1" }, { "1.2.643.2.2.35.1", "GOST2001-CryptoPro-A" }, { "1.2.643.2.2.35.2", "GOST2001-CryptoPro-B" }, { "1.2.643.2.2.35.3", "GOST2001-CryptoPro-C" }, { "1.2.643.7.1.2.1.2.1", "GOST2012-tc26-A" }, { "1.2.643.7.1.2.1.2.2", "GOST2012-tc26-B" }, { "1.3.132.0.10", "secp256k1" }, { NULL, NULL} }; #define TLV_LENGTH(prefix) do { \ if (!prefix ## len) \ return gpg_error (GPG_ERR_INV_KEYINFO); \ c = *(prefix)++; prefix ## len--; \ if (c == 0x80) \ return gpg_error (GPG_ERR_NOT_DER_ENCODED); \ if (c == 0xff) \ return gpg_error (GPG_ERR_BAD_BER); \ \ if ( !(c & 0x80) ) \ len = c; \ else \ { \ int count = c & 0x7f; \ \ for (len=0; count; count--) \ { \ len <<= 8; \ if (!prefix ## len) \ return gpg_error (GPG_ERR_BAD_BER);\ c = *(prefix)++; prefix ## len--; \ len |= c & 0xff; \ } \ } \ if (len > prefix ## len) \ return gpg_error (GPG_ERR_INV_KEYINFO); \ } while (0) /* Given a string BUF of length BUFLEN with either the name of an ECC curve or its OID in dotted form return the DER encoding of the OID. The caller must free the result. On error NULL is returned. */ static unsigned char * get_ecc_curve_oid (const unsigned char *buf, size_t buflen, size_t *r_oidlen) { unsigned char *der_oid; /* Skip an optional "oid." prefix. */ if (buflen > 4 && buf[3] == '.' && digitp (buf+4) && ((buf[0] == 'o' && buf[1] == 'i' && buf[2] == 'd') ||(buf[0] == 'O' && buf[1] == 'I' && buf[2] == 'D'))) { buf += 4; buflen -= 4; } /* If it does not look like an OID - map it through the table. */ if (buflen && !digitp (buf)) { int i; for (i=0; curve_names[i].oid; i++) if (buflen == strlen (curve_names[i].name) && !memcmp (buf, curve_names[i].name, buflen)) break; if (!curve_names[i].oid) return NULL; /* Not found. */ buf = curve_names[i].oid; buflen = strlen (curve_names[i].oid); } if (_ksba_oid_from_buf (buf, buflen, &der_oid, r_oidlen)) return NULL; return der_oid; } /* Return the OFF and the LEN of algorithm within DER. Do some checks and return the number of bytes read in r_nread, adding this to der does point into the BIT STRING. mode 0: just get the algorithm identifier. FIXME: should be able to handle BER Encoding. mode 1: as described. */ static gpg_error_t get_algorithm (int mode, const unsigned char *der, size_t derlen, size_t *r_nread, size_t *r_pos, size_t *r_len, int *r_bitstr, size_t *r_parm_pos, size_t *r_parm_len, int *r_parm_type) { int c; const unsigned char *start = der; const unsigned char *startseq; unsigned long seqlen, len; *r_bitstr = 0; if (r_parm_pos) *r_parm_pos = 0; if (r_parm_len) *r_parm_len = 0; if (r_parm_type) *r_parm_type = 0; /* get the inner sequence */ if (!derlen) return gpg_error (GPG_ERR_INV_KEYINFO); c = *der++; derlen--; if ( c != 0x30 ) return gpg_error (GPG_ERR_UNEXPECTED_TAG); /* not a SEQUENCE */ TLV_LENGTH(der); seqlen = len; startseq = der; /* get the object identifier */ if (!derlen) return gpg_error (GPG_ERR_INV_KEYINFO); c = *der++; derlen--; if ( c != 0x06 ) return gpg_error (GPG_ERR_UNEXPECTED_TAG); /* not an OBJECT IDENTIFIER */ TLV_LENGTH(der); /* der does now point to an oid of length LEN */ *r_pos = der - start; *r_len = len; der += len; derlen -= len; seqlen -= der - startseq;; /* Parse the parameter. */ if (seqlen) { const unsigned char *startparm = der; if (!derlen) return gpg_error (GPG_ERR_INV_KEYINFO); c = *der++; derlen--; if ( c == 0x05 ) { /* gpgrt_log_debug ("%s: parameter: NULL \n", __func__); */ if (!derlen) return gpg_error (GPG_ERR_INV_KEYINFO); c = *der++; derlen--; if (c) return gpg_error (GPG_ERR_BAD_BER); /* NULL must have a length of 0 */ seqlen -= 2; } else if (r_parm_pos && r_parm_len && c == 0x04) { /* This is an octet string parameter and we need it. */ if (r_parm_type) *r_parm_type = TYPE_OCTET_STRING; TLV_LENGTH(der); *r_parm_pos = der - start; *r_parm_len = len; seqlen -= der - startparm; der += len; derlen -= len; seqlen -= len; } else if (r_parm_pos && r_parm_len && c == 0x06) { /* This is an object identifier. */ if (r_parm_type) *r_parm_type = TYPE_OBJECT_ID; TLV_LENGTH(der); *r_parm_pos = der - start; *r_parm_len = len; seqlen -= der - startparm; der += len; derlen -= len; seqlen -= len; } else if (r_parm_pos && r_parm_len && c == 0x30) { /* This is a sequence. */ if (r_parm_type) *r_parm_type = TYPE_SEQUENCE; TLV_LENGTH(der); *r_parm_pos = startparm - start; *r_parm_len = len + (der - startparm); seqlen -= der - startparm; der += len; derlen -= len; seqlen -= len; } else { /* printf ("parameter: with tag %02x - ignored\n", c); */ TLV_LENGTH(der); seqlen -= der - startparm; /* skip the value */ der += len; derlen -= len; seqlen -= len; } } if (seqlen) return gpg_error (GPG_ERR_INV_KEYINFO); if (mode) { /* move forward to the BIT_STR */ if (!derlen) return gpg_error (GPG_ERR_INV_KEYINFO); c = *der++; derlen--; if (c == 0x03) *r_bitstr = 1; /* BIT STRING */ else if (c == 0x04) ; /* OCTECT STRING */ else return gpg_error (GPG_ERR_UNEXPECTED_TAG); /* not a BIT STRING */ TLV_LENGTH(der); } *r_nread = der - start; return 0; } gpg_error_t _ksba_parse_algorithm_identifier (const unsigned char *der, size_t derlen, size_t *r_nread, char **r_oid) { return _ksba_parse_algorithm_identifier2 (der, derlen, r_nread, r_oid, NULL, NULL); } 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; *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; *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) { *r_nread = 0; return err; } *r_oid = ksba_oid_to_str (der+off2+off, len); if (!*r_oid) { *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, ")"); } /* If parameters are given and we have a description for them, parse them. */ if (parmder && parmderlen && pk_algo_table[algoidx].parmelem_string && pk_algo_table[algoidx].parmctrl_string) { elem = pk_algo_table[algoidx].parmelem_string; ctrl = pk_algo_table[algoidx].parmctrl_string; for (; *elem; ctrl++, elem++) { int is_int; if ( (*ctrl & 0x80) && !elem[1] ) { /* Hack to allow reading a raw value. */ is_int = 1; len = parmderlen; } else { if (!parmderlen) { xfree (parm_oid); return gpg_error (GPG_ERR_INV_KEYINFO); } c = *parmder++; parmderlen--; if ( c != *ctrl ) { xfree (parm_oid); return gpg_error (GPG_ERR_UNEXPECTED_TAG); } is_int = c == 0x02; TLV_LENGTH (parmder); } if (is_int && *elem != '-') /* Take this integer. */ { char tmp[2]; put_stringbuf (&sb, "("); tmp[0] = *elem; tmp[1] = 0; put_stringbuf_sexp (&sb, tmp); put_stringbuf_mem_sexp (&sb, parmder, len); parmder += len; parmderlen -= len; put_stringbuf (&sb, ")"); } } } /* FIXME: We don't release the stringbuf in case of error better let the macro jump to a label */ elem = pk_algo_table[algoidx].elem_string; ctrl = pk_algo_table[algoidx].ctrl_string; for (; *elem; ctrl++, elem++) { int is_int; if ( (*ctrl & 0x80) && !elem[1] ) { /* Hack to allow reading a raw value. */ is_int = 1; len = derlen; } else { if (!derlen) { xfree (parm_oid); return gpg_error (GPG_ERR_INV_KEYINFO); } c = *der++; derlen--; if ( c != *ctrl ) { xfree (parm_oid); return gpg_error (GPG_ERR_UNEXPECTED_TAG); } is_int = c == 0x02; TLV_LENGTH (der); } if (is_int && *elem != '-') /* Take this integer. */ { char tmp[2]; put_stringbuf (&sb, "("); tmp[0] = *elem; tmp[1] = 0; put_stringbuf_sexp (&sb, tmp); put_stringbuf_mem_sexp (&sb, der, len); der += len; derlen -= len; put_stringbuf (&sb, ")"); } } put_stringbuf (&sb, "))"); xfree (parm_oid); *r_string = get_stringbuf (&sb); if (!*r_string) return gpg_error (GPG_ERR_ENOMEM); return 0; } /* Match the algorithm string given in BUF which is of length BUFLEN with the known algorithms from our table and returns the table entries for the DER encoded OID. If WITH_SIG is true, the table of signature algorithms is consulted first. */ static const unsigned char * oid_from_buffer (const unsigned char *buf, int buflen, int *oidlen, pkalgo_t *r_pkalgo, int with_sig) { int i; /* Ignore an optional "oid." prefix. */ if (buflen > 4 && buf[3] == '.' && digitp (buf+4) && ((buf[0] == 'o' && buf[1] == 'i' && buf[2] == 'd') ||(buf[0] == 'O' && buf[1] == 'I' && buf[2] == 'D'))) { buf += 4; buflen -= 4; } if (with_sig) { /* Scan the signature table first. */ for (i=0; sig_algo_table[i].oid; i++) { if (!sig_algo_table[i].supported) continue; if (buflen == strlen (sig_algo_table[i].oidstring) && !memcmp (buf, sig_algo_table[i].oidstring, buflen)) break; if (buflen == strlen (sig_algo_table[i].algo_string) && !memcmp (buf, sig_algo_table[i].algo_string, buflen)) break; } if (sig_algo_table[i].oid) { *r_pkalgo = sig_algo_table[i].pkalgo; *oidlen = sig_algo_table[i].oidlen; return sig_algo_table[i].oid; } } /* Scan the standard table. */ for (i=0; pk_algo_table[i].oid; i++) { if (!pk_algo_table[i].supported) continue; if (buflen == strlen (pk_algo_table[i].oidstring) && !memcmp (buf, pk_algo_table[i].oidstring, buflen)) break; if (buflen == strlen (pk_algo_table[i].algo_string) && !memcmp (buf, pk_algo_table[i].algo_string, buflen)) break; } if (!pk_algo_table[i].oid) return NULL; *r_pkalgo = pk_algo_table[i].pkalgo; *oidlen = pk_algo_table[i].oidlen; return pk_algo_table[i].oid; } /* Take a public-key S-Exp and convert it into a DER encoded publicKeyInfo */ gpg_error_t _ksba_keyinfo_from_sexp (ksba_const_sexp_t sexp, unsigned char **r_der, size_t *r_derlen) { gpg_error_t err; const unsigned char *s; char *endp; unsigned long n, n1; const unsigned char *oid; int oidlen; unsigned char *curve_oid = NULL; size_t curve_oidlen; pkalgo_t pkalgo; int i; struct { const char *name; int namelen; const unsigned char *value; int valuelen; } parm[10]; int parmidx; int idxtbl[10]; int idxtbllen; const char *parmdesc, *algoparmdesc; ksba_writer_t writer = NULL; void *algoparmseq_value = NULL; size_t algoparmseq_len; void *bitstr_value = NULL; size_t bitstr_len; if (!sexp) return gpg_error (GPG_ERR_INV_VALUE); s = sexp; if (*s != '(') return gpg_error (GPG_ERR_INV_SEXP); s++; n = strtoul (s, &endp, 10); s = endp; if (!n || *s != ':') return gpg_error (GPG_ERR_INV_SEXP); /* we don't allow empty lengths */ s++; if (n != 10 || memcmp (s, "public-key", 10)) return gpg_error (GPG_ERR_UNKNOWN_SEXP); s += 10; if (*s != '(') return gpg_error (digitp (s)? GPG_ERR_UNKNOWN_SEXP : GPG_ERR_INV_SEXP); s++; /* Break out the algorithm ID */ n = strtoul (s, &endp, 10); s = endp; if (!n || *s != ':') return gpg_error (GPG_ERR_INV_SEXP); /* we don't allow empty lengths */ s++; oid = oid_from_buffer (s, n, &oidlen, &pkalgo, 0); if (!oid) return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); s += n; /* Collect all the values */ for (parmidx = 0; *s != ')' ; parmidx++) { if (parmidx >= DIM(parm)) return gpg_error (GPG_ERR_GENERAL); if (*s != '(') return gpg_error (digitp(s)? GPG_ERR_UNKNOWN_SEXP:GPG_ERR_INV_SEXP); s++; n = strtoul (s, &endp, 10); s = endp; if (!n || *s != ':') return gpg_error (GPG_ERR_INV_SEXP); s++; parm[parmidx].name = s; parm[parmidx].namelen = n; s += n; if (!digitp(s)) return gpg_error (GPG_ERR_UNKNOWN_SEXP); /* ... or invalid S-Exp. */ n = strtoul (s, &endp, 10); s = endp; if (!n || *s != ':') return gpg_error (GPG_ERR_INV_SEXP); s++; parm[parmidx].value = s; parm[parmidx].valuelen = n; s += n; if ( *s != ')') return gpg_error (GPG_ERR_UNKNOWN_SEXP); /* ... or invalid S-Exp. */ s++; } s++; /* Allow for optional elements. */ if (*s == '(') { int depth = 1; err = sskip (&s, &depth); if (err) return err; } /* We need another closing parenthesis. */ if ( *s != ')' ) return gpg_error (GPG_ERR_INV_SEXP); /* Describe the parameters in the order we want them and construct IDXTBL to access them. For DSA wie also set algoparmdesc so that we can later build the parameters for the algorithmIdentifier. */ algoparmdesc = NULL; switch (pkalgo) { case PKALGO_RSA: parmdesc = "ne"; break; case PKALGO_DSA: parmdesc = "y" ; algoparmdesc = "pqg"; break; case PKALGO_ECC: parmdesc = "Cq"; for (i = 0; i < parmidx; i++) if (parm[i].namelen == 5 && !memcmp (parm[i].name,"curve",5)) { /* FIXME: Access to pk_algo_table with constant is ugly. */ if (parm[i].valuelen == 7 && !memcmp (parm[i].value, "Ed25519", 7)) { pkalgo = PKALGO_ED25519; parmdesc = "q"; oid = pk_algo_table[7].oid; oidlen = pk_algo_table[7].oidlen; break; } else if (parm[i].valuelen == 5 && !memcmp (parm[i].value, "Ed448", 5)) { pkalgo = PKALGO_ED448; parmdesc = "q"; oid = pk_algo_table[8].oid; oidlen = pk_algo_table[8].oidlen; break; } } break; default: return gpg_error (GPG_ERR_UNKNOWN_ALGORITHM); } idxtbllen = 0; for (s = parmdesc; *s; s++) { for (i=0; i < parmidx; i++) { assert (idxtbllen < DIM (idxtbl)); switch (*s) { case 'C': /* Magic value for "curve". */ if (parm[i].namelen == 5 && !memcmp (parm[i].name, "curve", 5)) { idxtbl[idxtbllen++] = i; i = parmidx; /* Break inner loop. */ } break; default: if (parm[i].namelen == 1 && parm[i].name[0] == *s) { idxtbl[idxtbllen++] = i; i = parmidx; /* Break inner loop. */ } break; } } } if (idxtbllen != strlen (parmdesc)) return gpg_error (GPG_ERR_UNKNOWN_SEXP); if (pkalgo == PKALGO_ECC) { curve_oid = get_ecc_curve_oid (parm[idxtbl[0]].value, parm[idxtbl[0]].valuelen, &curve_oidlen); if (!curve_oid) return gpg_error (GPG_ERR_UNKNOWN_SEXP); } /* Create write object. */ err = ksba_writer_new (&writer); if (err) goto leave; err = ksba_writer_set_mem (writer, 1024); if (err) goto leave; /* We create the keyinfo in 2 steps: 1. We build the inner one and encapsulate it in a bit string. 2. We create the outer sequence include the algorithm identifier and the bit string from step 1. */ if (pkalgo == PKALGO_ECC) { /* Write the bit string header and the number of unused bits. */ err = _ksba_ber_write_tl (writer, TYPE_BIT_STRING, CLASS_UNIVERSAL, 0, parm[idxtbl[1]].valuelen + 1); if (!err) err = ksba_writer_write (writer, "", 1); /* And the actual raw value. */ if (!err) err = ksba_writer_write (writer, parm[idxtbl[1]].value, parm[idxtbl[1]].valuelen); if (err) goto leave; } else if (pkalgo == PKALGO_ED25519 || pkalgo == PKALGO_ED448) { /* Write the bit string header and the number of unused bits. */ err = _ksba_ber_write_tl (writer, TYPE_BIT_STRING, CLASS_UNIVERSAL, 0, parm[idxtbl[0]].valuelen + 1); if (!err) err = ksba_writer_write (writer, "", 1); /* And the actual raw value. */ if (!err) err = ksba_writer_write (writer, parm[idxtbl[0]].value, parm[idxtbl[0]].valuelen); if (err) goto leave; } else /* RSA and DSA */ { /* Calculate the size of the sequence value and the size of the bit string value. Note that in case there is only one integer to write, no sequence is used. */ for (n=0, i=0; i < idxtbllen; i++ ) { n += _ksba_ber_count_tl (TYPE_INTEGER, CLASS_UNIVERSAL, 0, parm[idxtbl[i]].valuelen); n += parm[idxtbl[i]].valuelen; } n1 = 1; /* # of unused bits. */ if (idxtbllen > 1) n1 += _ksba_ber_count_tl (TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, n); n1 += n; /* Write the bit string header and the number of unused bits. */ err = _ksba_ber_write_tl (writer, TYPE_BIT_STRING, CLASS_UNIVERSAL, 0, n1); if (!err) err = ksba_writer_write (writer, "", 1); if (err) goto leave; /* Write the sequence tag and the integers. */ if (idxtbllen > 1) err = _ksba_ber_write_tl (writer, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1,n); if (err) goto leave; for (i=0; i < idxtbllen; i++) { /* fixme: we should make sure that the integer conforms to the ASN.1 encoding rules. */ err = _ksba_ber_write_tl (writer, TYPE_INTEGER, CLASS_UNIVERSAL, 0, parm[idxtbl[i]].valuelen); if (!err) err = ksba_writer_write (writer, parm[idxtbl[i]].value, parm[idxtbl[i]].valuelen); if (err) goto leave; } } /* Get the encoded bit string. */ bitstr_value = ksba_writer_snatch_mem (writer, &bitstr_len); if (!bitstr_value) { err = gpg_error (GPG_ERR_ENOMEM); goto leave; } /* If the algorithmIdentifier requires a sequence with parameters, build them now. We can reuse the IDXTBL for that. */ if (algoparmdesc) { idxtbllen = 0; for (s = algoparmdesc; *s; s++) { for (i=0; i < parmidx; i++) { assert (idxtbllen < DIM (idxtbl)); if (parm[i].namelen == 1 && parm[i].name[0] == *s) { idxtbl[idxtbllen++] = i; break; } } } if (idxtbllen != strlen (algoparmdesc)) return gpg_error (GPG_ERR_UNKNOWN_SEXP); err = ksba_writer_set_mem (writer, 1024); if (err) goto leave; /* Calculate the size of the sequence. */ for (n=0, i=0; i < idxtbllen; i++ ) { n += _ksba_ber_count_tl (TYPE_INTEGER, CLASS_UNIVERSAL, 0, parm[idxtbl[i]].valuelen); n += parm[idxtbl[i]].valuelen; } /* n += _ksba_ber_count_tl (TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, n); */ /* Write the sequence tag followed by the integers. */ err = _ksba_ber_write_tl (writer, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, n); if (err) goto leave; for (i=0; i < idxtbllen; i++) { err = _ksba_ber_write_tl (writer, TYPE_INTEGER, CLASS_UNIVERSAL, 0, parm[idxtbl[i]].valuelen); if (!err) err = ksba_writer_write (writer, parm[idxtbl[i]].value, parm[idxtbl[i]].valuelen); if (err) goto leave; } /* Get the encoded sequence. */ algoparmseq_value = ksba_writer_snatch_mem (writer, &algoparmseq_len); if (!algoparmseq_value) { err = gpg_error (GPG_ERR_ENOMEM); goto leave; } } else algoparmseq_len = 0; /* Reinitialize the buffer to create the outer sequence. */ err = ksba_writer_set_mem (writer, 1024); if (err) goto leave; /* Calulate lengths. */ n = _ksba_ber_count_tl (TYPE_OBJECT_ID, CLASS_UNIVERSAL, 0, oidlen); n += oidlen; if (algoparmseq_len) { n += algoparmseq_len; } else if (pkalgo == PKALGO_ECC) { n += _ksba_ber_count_tl (TYPE_OBJECT_ID, CLASS_UNIVERSAL, 0, curve_oidlen); n += curve_oidlen; } else if (pkalgo == PKALGO_RSA) { n += _ksba_ber_count_tl (TYPE_NULL, CLASS_UNIVERSAL, 0, 0); } n1 = n; n1 += _ksba_ber_count_tl (TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, n); n1 += bitstr_len; /* The outer sequence. */ err = _ksba_ber_write_tl (writer, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, n1); if (err) goto leave; /* The sequence. */ err = _ksba_ber_write_tl (writer, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, n); if (err) goto leave; /* The object id. */ err = _ksba_ber_write_tl (writer, TYPE_OBJECT_ID,CLASS_UNIVERSAL, 0, oidlen); if (!err) err = ksba_writer_write (writer, oid, oidlen); if (err) goto leave; /* The parameter. */ if (algoparmseq_len) { err = ksba_writer_write (writer, algoparmseq_value, algoparmseq_len); } else if (pkalgo == PKALGO_ECC) { err = _ksba_ber_write_tl (writer, TYPE_OBJECT_ID, CLASS_UNIVERSAL, 0, curve_oidlen); if (!err) err = ksba_writer_write (writer, curve_oid, curve_oidlen); } else if (pkalgo == PKALGO_RSA) { err = _ksba_ber_write_tl (writer, TYPE_NULL, CLASS_UNIVERSAL, 0, 0); } if (err) goto leave; /* Append the pre-constructed bit string. */ err = ksba_writer_write (writer, bitstr_value, bitstr_len); if (err) goto leave; /* Get the result. */ *r_der = ksba_writer_snatch_mem (writer, r_derlen); if (!*r_der) err = gpg_error (GPG_ERR_ENOMEM); leave: ksba_writer_release (writer); xfree (bitstr_value); xfree (curve_oid); return err; } /* Take a sig-val s-expression and convert it into a DER encoded algorithmInfo. Unfortunately this function clones a lot of code from _ksba_keyinfo_from_sexp. */ gpg_error_t _ksba_algoinfo_from_sexp (ksba_const_sexp_t sexp, unsigned char **r_der, size_t *r_derlen) { gpg_error_t err; const unsigned char *s; char *endp; unsigned long n; const unsigned char *oid; int oidlen; unsigned char *curve_oid = NULL; size_t curve_oidlen; pkalgo_t pkalgo; int i; struct { const char *name; int namelen; const unsigned char *value; int valuelen; } parm[10]; int parmidx; int idxtbl[10]; int idxtbllen; const char *parmdesc, *algoparmdesc; ksba_writer_t writer = NULL; void *algoparmseq_value = NULL; size_t algoparmseq_len; if (!sexp) return gpg_error (GPG_ERR_INV_VALUE); s = sexp; if (*s != '(') return gpg_error (GPG_ERR_INV_SEXP); s++; n = strtoul (s, &endp, 10); s = endp; if (!n || *s != ':') return gpg_error (GPG_ERR_INV_SEXP); /* We don't allow empty lengths. */ s++; if (n == 7 && !memcmp (s, "sig-val", 7)) s += 7; else if (n == 10 && !memcmp (s, "public-key", 10)) s += 10; else return gpg_error (GPG_ERR_UNKNOWN_SEXP); if (*s != '(') return gpg_error (digitp (s)? GPG_ERR_UNKNOWN_SEXP : GPG_ERR_INV_SEXP); s++; /* Break out the algorithm ID */ n = strtoul (s, &endp, 10); s = endp; if (!n || *s != ':') return gpg_error (GPG_ERR_INV_SEXP); /* We don't allow empty lengths. */ s++; oid = oid_from_buffer (s, n, &oidlen, &pkalgo, 1); if (!oid) return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); s += n; /* Collect all the values */ for (parmidx = 0; *s != ')' ; parmidx++) { if (parmidx >= DIM(parm)) return gpg_error (GPG_ERR_GENERAL); if (*s != '(') return gpg_error (digitp(s)? GPG_ERR_UNKNOWN_SEXP:GPG_ERR_INV_SEXP); s++; n = strtoul (s, &endp, 10); s = endp; if (!n || *s != ':') return gpg_error (GPG_ERR_INV_SEXP); s++; parm[parmidx].name = s; parm[parmidx].namelen = n; s += n; if (!digitp(s)) return gpg_error (GPG_ERR_UNKNOWN_SEXP); /* ... or invalid S-Exp. */ n = strtoul (s, &endp, 10); s = endp; if (!n || *s != ':') return gpg_error (GPG_ERR_INV_SEXP); s++; parm[parmidx].value = s; parm[parmidx].valuelen = n; s += n; if ( *s != ')') return gpg_error (GPG_ERR_UNKNOWN_SEXP); /* ... or invalid S-Exp. */ s++; } s++; /* Allow for optional elements. */ if (*s == '(') { int depth = 1; err = sskip (&s, &depth); if (err) return err; } /* We need another closing parenthesis. */ if ( *s != ')' ) return gpg_error (GPG_ERR_INV_SEXP); /* Describe the parameters in the order we want them and construct IDXTBL to access them. For DSA wie also set algoparmdesc so that we can later build the parameters for the algorithmIdentifier. */ algoparmdesc = NULL; switch (pkalgo) { case PKALGO_RSA: parmdesc = ""; break; case PKALGO_DSA: parmdesc = "" ; algoparmdesc = "pqg"; break; case PKALGO_ECC: parmdesc = "C"; break; default: return gpg_error (GPG_ERR_UNKNOWN_ALGORITHM); } idxtbllen = 0; for (s = parmdesc; *s; s++) { for (i=0; i < parmidx; i++) { assert (idxtbllen < DIM (idxtbl)); switch (*s) { case 'C': /* Magic value for "curve". */ if (parm[i].namelen == 5 && !memcmp (parm[i].name, "curve", 5)) { idxtbl[idxtbllen++] = i; i = parmidx; /* Break inner loop. */ } break; default: if (parm[i].namelen == 1 && parm[i].name[0] == *s) { idxtbl[idxtbllen++] = i; i = parmidx; /* Break inner loop. */ } break; } } } if (idxtbllen != strlen (parmdesc)) return gpg_error (GPG_ERR_UNKNOWN_SEXP); if (pkalgo == PKALGO_ECC) { curve_oid = get_ecc_curve_oid (parm[idxtbl[0]].value, parm[idxtbl[0]].valuelen, &curve_oidlen); if (!curve_oid) return gpg_error (GPG_ERR_UNKNOWN_SEXP); } /* Create write object. */ err = ksba_writer_new (&writer); if (err) goto leave; err = ksba_writer_set_mem (writer, 1024); if (err) goto leave; /* Create the sequence of the algorithm identifier. */ /* If the algorithmIdentifier requires a sequence with parameters, build them now. We can reuse the IDXTBL for that. */ if (algoparmdesc) { idxtbllen = 0; for (s = algoparmdesc; *s; s++) { for (i=0; i < parmidx; i++) { assert (idxtbllen < DIM (idxtbl)); if (parm[i].namelen == 1 && parm[i].name[0] == *s) { idxtbl[idxtbllen++] = i; break; } } } if (idxtbllen != strlen (algoparmdesc)) return gpg_error (GPG_ERR_UNKNOWN_SEXP); err = ksba_writer_set_mem (writer, 1024); if (err) goto leave; /* Calculate the size of the sequence. */ for (n=0, i=0; i < idxtbllen; i++ ) { n += _ksba_ber_count_tl (TYPE_INTEGER, CLASS_UNIVERSAL, 0, parm[idxtbl[i]].valuelen); n += parm[idxtbl[i]].valuelen; } /* Write the sequence tag followed by the integers. */ err = _ksba_ber_write_tl (writer, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, n); if (err) goto leave; for (i=0; i < idxtbllen; i++) { err = _ksba_ber_write_tl (writer, TYPE_INTEGER, CLASS_UNIVERSAL, 0, parm[idxtbl[i]].valuelen); if (!err) err = ksba_writer_write (writer, parm[idxtbl[i]].value, parm[idxtbl[i]].valuelen); if (err) goto leave; } /* Get the encoded sequence. */ algoparmseq_value = ksba_writer_snatch_mem (writer, &algoparmseq_len); if (!algoparmseq_value) { err = gpg_error (GPG_ERR_ENOMEM); goto leave; } } else algoparmseq_len = 0; /* Reinitialize the buffer to create the sequence. */ err = ksba_writer_set_mem (writer, 1024); if (err) goto leave; /* Calulate lengths. */ n = _ksba_ber_count_tl (TYPE_OBJECT_ID, CLASS_UNIVERSAL, 0, oidlen); n += oidlen; if (algoparmseq_len) { n += algoparmseq_len; } else if (pkalgo == PKALGO_ECC) { n += _ksba_ber_count_tl (TYPE_OBJECT_ID, CLASS_UNIVERSAL, 0, curve_oidlen); n += curve_oidlen; } else if (pkalgo == PKALGO_RSA) { n += _ksba_ber_count_tl (TYPE_NULL, CLASS_UNIVERSAL, 0, 0); } /* Write the sequence. */ err = _ksba_ber_write_tl (writer, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, n); if (err) goto leave; /* Write the object id. */ err = _ksba_ber_write_tl (writer, TYPE_OBJECT_ID, CLASS_UNIVERSAL, 0, oidlen); if (!err) err = ksba_writer_write (writer, oid, oidlen); if (err) goto leave; /* Write the parameters. */ if (algoparmseq_len) { err = ksba_writer_write (writer, algoparmseq_value, algoparmseq_len); } else if (pkalgo == PKALGO_ECC) { /* We only support the namedCuve choice for ECC parameters. */ err = _ksba_ber_write_tl (writer, TYPE_OBJECT_ID, CLASS_UNIVERSAL, 0, curve_oidlen); if (!err) err = ksba_writer_write (writer, curve_oid, curve_oidlen); } else if (pkalgo == PKALGO_RSA) { err = _ksba_ber_write_tl (writer, TYPE_NULL, CLASS_UNIVERSAL, 0, 0); } if (err) goto leave; /* Get the result. */ *r_der = ksba_writer_snatch_mem (writer, r_derlen); if (!*r_der) err = gpg_error (GPG_ERR_ENOMEM); leave: ksba_writer_release (writer); xfree (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 */ static gpg_error_t cryptval_to_sexp (int mode, const unsigned char *der, size_t derlen, 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 */ 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, ")"); } } 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, ")"); } 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, 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 We only allow parameters == NULL. The function parses this structure and creates a S-Exp suitable to be used as encrypted value in Libgcrypt's public key functions: (enc-val ( ( ) ... ( ) )) 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_encval_to_sexp (const unsigned char *der, size_t derlen, ksba_sexp_t *r_string) { return cryptval_to_sexp (1, der, derlen, r_string); }