diff --git a/src/Makefile.am b/src/Makefile.am index 0f1fe72..137bbed 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,141 +1,141 @@ # Makefile.am - for the KSBA ASN.1 and X.509 library # Copyright (C) 2001, 2002, 2004, 2005, 2007 g10 Code GmbH # # This file is part of KSBA. # # KSBA is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # KSBA is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . ## Process this file with automake to produce Makefile.in pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = ksba.pc asn1_modules = tmttv2.asn cms.asn buildtool_src = asn1-gentables.c gen-help.c gen-help.h EXTRA_DIST = ksba.h.in ksba-config.in $(asn1_modules) ksba.m4 \ libksba.vers libksba.def \ asn1-parse.c asn1-tables.c $(buildtool_src) ksba.pc.in BUILT_SOURCES = asn1-parse.c asn1-tables.c bin_SCRIPTS = ksba-config nodist_include_HEADERS = ksba.h lib_LTLIBRARIES = libksba.la noinst_PROGRAMS = ber-dump m4datadir = $(datadir)/aclocal m4data_DATA = ksba.m4 CLEANFILES = asn1-gentables DISTCLEANFILES = asn1-tables.c AM_CPPFLAGS = -I$(top_builddir)/gl -I$(top_srcdir)/gl AM_CFLAGS = $(GPG_ERROR_CFLAGS) $(COVERAGE_CFLAGS) if HAVE_LD_VERSION_SCRIPT libksba_version_script_cmd = -Wl,--version-script=$(srcdir)/libksba.vers else libksba_version_script_cmd = endif if HAVE_W32_SYSTEM RCCOMPILE = $(RC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(libksba_la_CPPFLAGS) $(AM_CPPFLAGS) $(CPPFLAGS) LTRCCOMPILE = $(LIBTOOL) --mode=compile --tag=RC $(RCCOMPILE) SUFFIXES = .rc .lo .rc.lo: $(LTRCCOMPILE) -i "$<" -o "$@" ksba_res = versioninfo.lo no_undefined = -no-undefined export_symbols = -export-symbols $(srcdir)/libksba.def extra_ltoptions = -XCClinker -static-libgcc install-def-file: $(INSTALL) $(srcdir)/libksba.def $(DESTDIR)$(libdir)/libksba.def uninstall-def-file: -rm $(DESTDIR)$(libdir)/libksba.def ksba_deps = $(ksba_res) libksba.def else !HAVE_W32_SYSTEM ksba_res = no_undefined = export_symbols = extra_ltoptions = ksba_deps = install-def-file: uninstall-def-file: endif !HAVE_W32_SYSTEM libksba_la_LDFLAGS = $(no_undefined) $(export_symbols) $(extra_ltoptions) \ $(libksba_version_script_cmd) -version-info \ @LIBKSBA_LT_CURRENT@:@LIBKSBA_LT_REVISION@:@LIBKSBA_LT_AGE@ \ $(COVERAGE_LDFLAGS) libksba_la_INCLUDES = -I$(top_srcdir)/lib libksba_la_DEPENDENCIES = $(srcdir)/libksba.vers $(ksba_deps) libksba_la_LIBADD = $(ksba_res) @LTLIBOBJS@ @GPG_ERROR_LIBS@ libksba_la_SOURCES = \ ksba.h \ visibility.c visibility.h \ reader.c reader.h \ writer.c writer.h \ asn1-parse.y \ asn1-func.c asn1-func2.c asn1-func.h \ ber-help.c ber-help.h \ ber-decoder.c ber-decoder.h \ der-encoder.c der-encoder.h \ cert.c cert.h \ cms.c cms.h cms-parser.c \ crl.c crl.h \ certreq.c certreq.h \ ocsp.c ocsp.h \ keyinfo.c keyinfo.h \ - oid.c name.c dn.c time.c convert.h \ + oid.c name.c dn.c time.c convert.h stringbuf.h \ version.c util.c util.h shared.h \ sexp-parse.h \ asn1-tables.c ber_dump_SOURCES = ber-dump.c \ ber-decoder.c ber-help.c reader.c writer.c asn1-parse.c \ asn1-func.c oid.c time.c util.c ber_dump_LDADD = $(GPG_ERROR_LIBS) ../gl/libgnu.la ber_dump_CFLAGS = $(AM_CFLAGS) asn1-parse.c : asn1-func.h gen-help.h asn1-gentables: asn1-gentables.c asn1-parse.c asn1-func.c gen-help.c gen-help.h $(CC_FOR_BUILD) $(CFLAGS_FOR_BUILD) $(LDFLAGS_FOR_BUILD) \ $(CPPFLAGS_FOR_BUILD) -I$(srcdir) -DBUILD_GENTOOLS -o $@ \ $(srcdir)/asn1-gentables.c \ `test -f 'asn1-parse.c' || echo '$(srcdir)/'`asn1-parse.c \ $(srcdir)/asn1-func.c \ $(srcdir)/gen-help.c asn1-tables.c : $(asn1_modules) asn1-gentables @set -e; list=""; \ for file in $(asn1_modules); do list="$$list $(srcdir)/$$file";done;\ ./asn1-gentables $$list > asn1-tables.c install-data-local: install-def-file uninstall-local: uninstall-def-file diff --git a/src/dn.c b/src/dn.c index 958850b..6510772 100644 --- a/src/dn.c +++ b/src/dn.c @@ -1,1335 +1,1204 @@ /* dn.c - Distinguished Name helper functions * Copyright (C) 2001, 2006, 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 . */ /* Reference is RFC-2253 */ #include #include #include #include #include #include "util.h" #include "asn1-func.h" #include "ber-help.h" #include "ber-decoder.h" +#include "stringbuf.h" + static const struct { const char *name; int source; /* 0 = unknown 1 = rfc2253 2 = David Chadwick, July 2003 3 = Peter Gutmann */ const char *description; size_t oidlen; const unsigned char *oid; /* DER encoded OID. */ const char *oidstr; /* OID as dotted string. */ } oid_name_tbl[] = { {"CN", 1, "CommonName", 3, "\x55\x04\x03", "2.5.4.3" }, {"SN", 2, "Surname", 3, "\x55\x04\x04", "2.5.4.4" }, {"SERIALNUMBER", 2, "SerialNumber",3, "\x55\x04\x05", "2.5.4.5" }, {"C", 1, "CountryName", 3, "\x55\x04\x06", "2.5.4.6" }, {"L" , 1, "LocalityName", 3, "\x55\x04\x07", "2.5.4.7" }, {"ST", 1, "StateOrProvince", 3, "\x55\x04\x08", "2.5.4.8" }, {"STREET", 1, "StreetAddress", 3, "\x55\x04\x09", "2.5.4.9" }, {"O", 1, "OrganizationName", 3, "\x55\x04\x0a", "2.5.4.10" }, {"OU", 1, "OrganizationalUnit", 3, "\x55\x04\x0b", "2.5.4.11" }, {"T", 2, "Title", 3, "\x55\x04\x0c", "2.5.4.12" }, {"D", 3, "Description", 3, "\x55\x04\x0d", "2.5.4.13" }, {"BC", 3, "BusinessCategory", 3, "\x55\x04\x0f", "2.5.4.15" }, {"ADDR", 2, "PostalAddress", 3, "\x55\x04\x11", "2.5.4.16" }, {"POSTALCODE" , 0, "PostalCode", 3, "\x55\x04\x11", "2.5.4.17" }, {"GN", 2, "GivenName", 3, "\x55\x04\x2a", "2.5.4.42" }, {"PSEUDO", 2, "Pseudonym", 3, "\x55\x04\x41", "2.5.4.65" }, {"DC", 1, "domainComponent", 10, "\x09\x92\x26\x89\x93\xF2\x2C\x64\x01\x19", "0.9.2342.19200300.100.1.25" }, {"UID", 1, "userid", 10, "\x09\x92\x26\x89\x93\xF2\x2C\x64\x01\x01", "0.9.2342.19200300.100.1.1 " }, {"EMAIL", 3, "emailAddress", 9, "\x2A\x86\x48\x86\xF7\x0D\x01\x09\x01", "1.2.840.113549.1.9.1" }, { NULL } }; #define N 0x00 #define P 0x01 static unsigned char charclasses[128] = { N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, P, N, N, N, N, N, N, P, P, P, N, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, N, N, P, N, P, N, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, N, N, N, N, N, N, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, N, N, N, N, N }; #undef N #undef P -struct stringbuf { - size_t len; - size_t size; - char *buf; - int out_of_core; -}; - - - -static void -init_stringbuf (struct stringbuf *sb, int initiallen) -{ - sb->len = 0; - sb->size = initiallen; - sb->out_of_core = 0; - /* allocate one more, so that get_stringbuf can append a nul */ - sb->buf = xtrymalloc (initiallen+1); - if (!sb->buf) - sb->out_of_core = 1; -} - -static void -deinit_stringbuf (struct stringbuf *sb) -{ - xfree (sb->buf); - sb->buf = NULL; - sb->out_of_core = 1; /* make sure the caller does an init before reuse */ -} - - -static void -put_stringbuf (struct stringbuf *sb, const char *text) -{ - size_t n = strlen (text); - - if (sb->out_of_core) - return; - - if (sb->len + n >= sb->size) - { - char *p; - - sb->size += n + 100; - p = xtryrealloc (sb->buf, sb->size); - if ( !p) - { - sb->out_of_core = 1; - return; - } - sb->buf = p; - } - memcpy (sb->buf+sb->len, text, n); - sb->len += n; -} - -static void -put_stringbuf_mem (struct stringbuf *sb, const char *text, size_t n) -{ - if (sb->out_of_core) - return; - - if (sb->len + n >= sb->size) - { - char *p; - - sb->size += n + 100; - p = xtryrealloc (sb->buf, sb->size); - if ( !p) - { - sb->out_of_core = 1; - return; - } - sb->buf = p; - } - memcpy (sb->buf+sb->len, text, n); - sb->len += n; -} - -static void -put_stringbuf_mem_skip (struct stringbuf *sb, const char *text, size_t n, - int skip) -{ - char *p; - - if (!skip) - { - put_stringbuf_mem (sb, text, n); - return; - } - if (sb->out_of_core) - return; - - if (sb->len + n >= sb->size) - { - /* Note: we allocate too much here, but we don't care. */ - sb->size += n + 100; - p = xtryrealloc (sb->buf, sb->size); - if ( !p) - { - sb->out_of_core = 1; - return; - } - sb->buf = p; - } - p = sb->buf+sb->len; - while (n > skip) - { - text += skip; - n -= skip; - *p++ = *text++; - n--; - sb->len++; - } -} - -static char * -get_stringbuf (struct stringbuf *sb) -{ - char *p; - - if (sb->out_of_core) - { - xfree (sb->buf); sb->buf = NULL; - return NULL; - } - - sb->buf[sb->len] = 0; - p = sb->buf; - sb->buf = NULL; - sb->out_of_core = 1; /* make sure the caller does an init before reuse */ - return p; -} - /* This function is used for 1 byte encodings to insert any required quoting. It does not do the quoting for a space or hash mark at the beginning of a string or a space as the last character of a string. It will do steps of SKIP+1 characters, assuming that these SKIP characters are null octets. */ static void append_quoted (struct stringbuf *sb, const unsigned char *value, size_t length, int skip) { unsigned char tmp[4]; const unsigned char *s = value; size_t n = 0; for (;;) { for (value = s; n+skip < length; n++, s++) { s += skip; n += skip; if (*s < ' ' || *s > 126 || strchr (",+\"\\<>;", *s) ) break; } if (s != value) put_stringbuf_mem_skip (sb, value, s-value, skip); if (n+skip >= length) return; /* ready */ s += skip; n += skip; if ( *s < ' ' || *s > 126 ) { snprintf (tmp, sizeof tmp, "\\%02X", *s); put_stringbuf_mem (sb, tmp, 3); } else { tmp[0] = '\\'; tmp[1] = *s; put_stringbuf_mem (sb, tmp, 2); } n++; s++; } } /* Append VALUE of LENGTH and TYPE to SB. Do the required quoting. */ static void append_utf8_value (const unsigned char *value, size_t length, struct stringbuf *sb) { unsigned char tmp[6]; const unsigned char *s; size_t n; int i, nmore; if (length && (*value == ' ' || *value == '#')) { tmp[0] = '\\'; tmp[1] = *value; put_stringbuf_mem (sb, tmp, 2); value++; length--; } if (length && value[length-1] == ' ') { tmp[0] = '\\'; tmp[1] = ' '; put_stringbuf_mem (sb, tmp, 2); length--; } for (s=value, n=0;;) { for (value = s; n < length && !(*s & 0x80); n++, s++) ; if (s != value) append_quoted (sb, value, s-value, 0); if (n==length) return; /* ready */ if (!(*s & 0x80)) nmore = 0; /* Not expected here: high bit not set. */ else if ( (*s & 0xe0) == 0xc0 ) /* 110x xxxx */ nmore = 1; else if ( (*s & 0xf0) == 0xe0 ) /* 1110 xxxx */ nmore = 2; else if ( (*s & 0xf8) == 0xf0 ) /* 1111 0xxx */ nmore = 3; else if ( (*s & 0xfc) == 0xf8 ) /* 1111 10xx */ nmore = 4; else if ( (*s & 0xfe) == 0xfc ) /* 1111 110x */ nmore = 5; else /* Invalid encoding */ nmore = 0; if (!nmore) { /* Encoding error: We quote the bad byte. */ snprintf (tmp, sizeof tmp, "\\%02X", *s); put_stringbuf_mem (sb, tmp, 3); s++; n++; } else { tmp[0] = *s++; n++; for (i=1; n < length && i <= nmore; i++) { if ( (*s & 0xc0) != 0x80) break; /* Invalid encoding - let the next cycle detect this. */ tmp[i] = *s++; n++; } put_stringbuf_mem (sb, tmp, i); } } } /* Append VALUE of LENGTH and TYPE to SB. Do character set conversion and quoting */ static void append_latin1_value (const unsigned char *value, size_t length, struct stringbuf *sb) { unsigned char tmp[2]; const unsigned char *s; size_t n; if (length && (*value == ' ' || *value == '#')) { tmp[0] = '\\'; tmp[1] = *value; put_stringbuf_mem (sb, tmp, 2); value++; length--; } if (length && value[length-1] == ' ') { tmp[0] = '\\'; tmp[1] = ' '; put_stringbuf_mem (sb, tmp, 2); length--; } for (s=value, n=0;;) { for (value = s; n < length && !(*s & 0x80); n++, s++) ; if (s != value) append_quoted (sb, value, s-value, 0); if (n==length) return; /* ready */ assert ((*s & 0x80)); tmp[0] = 0xc0 | ((*s >> 6) & 3); tmp[1] = 0x80 | ( *s & 0x3f ); put_stringbuf_mem (sb, tmp, 2); n++; s++; } } /* Append VALUE of LENGTH and TYPE to SB. Do UCS-4 to utf conversion and and quoting */ static void append_ucs4_value (const unsigned char *value, size_t length, struct stringbuf *sb) { unsigned char tmp[7]; const unsigned char *s; size_t n; unsigned int c; int i; if (length>3 && !value[0] && !value[1] && !value[2] && (value[3] == ' ' || value[3] == '#')) { tmp[0] = '\\'; tmp[1] = *value; put_stringbuf_mem (sb, tmp, 2); value += 4; length -= 4; } if (length>3 && !value[0] && !value[1] && !value[2] && value[3] == ' ') { tmp[0] = '\\'; tmp[1] = ' '; put_stringbuf_mem (sb, tmp, 2); length -= 4; } for (s=value, n=0;;) { for (value = s; n+3 < length && !s[0] && !s[1] && !s[2] && !(s[3] & 0x80); n += 4, s += 4) ; if (s != value) append_quoted (sb, value, s-value, 3); if (n>=length) return; /* ready */ if (n < 4) { /* This is an invalid encoding - better stop after adding one impossible characater */ put_stringbuf_mem (sb, "\xff", 1); return; } c = *s++ << 24; c |= *s++ << 16; c |= *s++ << 8; c |= *s++; n += 4; i=0; if (c < (1<<11)) { tmp[i++] = 0xc0 | ( c >> 6); tmp[i++] = 0x80 | ( c & 0x3f); } else if (c < (1<<16)) { tmp[i++] = 0xe0 | ( c >> 12); tmp[i++] = 0x80 | ((c >> 6) & 0x3f); tmp[i++] = 0x80 | ( c & 0x3f); } else if (c < (1<<21)) { tmp[i++] = 0xf0 | ( c >> 18); tmp[i++] = 0x80 | ((c >> 12) & 0x3f); tmp[i++] = 0x80 | ((c >> 6) & 0x3f); tmp[i++] = 0x80 | ( c & 0x3f); } else if (c < (1<<26)) { tmp[i++] = 0xf8 | ( c >> 24); tmp[i++] = 0x80 | ((c >> 18) & 0x3f); tmp[i++] = 0x80 | ((c >> 12) & 0x3f); tmp[i++] = 0x80 | ((c >> 6) & 0x3f); tmp[i++] = 0x80 | ( c & 0x3f); } else { tmp[i++] = 0xfc | ( c >> 30); tmp[i++] = 0x80 | ((c >> 24) & 0x3f); tmp[i++] = 0x80 | ((c >> 18) & 0x3f); tmp[i++] = 0x80 | ((c >> 12) & 0x3f); tmp[i++] = 0x80 | ((c >> 6) & 0x3f); tmp[i++] = 0x80 | ( c & 0x3f); } put_stringbuf_mem (sb, tmp, i); } } /* Append VALUE of LENGTH and TYPE to SB. Do UCS-2 to utf conversion and and quoting */ static void append_ucs2_value (const unsigned char *value, size_t length, struct stringbuf *sb) { unsigned char tmp[3]; const unsigned char *s; size_t n; unsigned int c; int i; if (length>1 && !value[0] && (value[1] == ' ' || value[1] == '#')) { tmp[0] = '\\'; tmp[1] = *value; put_stringbuf_mem (sb, tmp, 2); value += 2; length -= 2; } if (length>1 && !value[0] && value[1] == ' ') { tmp[0] = '\\'; tmp[1] = ' '; put_stringbuf_mem (sb, tmp, 2); length -=2; } for (s=value, n=0;;) { for (value = s; n+1 < length && !s[0] && !(s[1] & 0x80); n += 2, s += 2) ; if (s != value) append_quoted (sb, value, s-value, 1); if (n>=length) return; /* ready */ if (n < 2) { /* This is an invalid encoding - better stop after adding one impossible characater */ put_stringbuf_mem (sb, "\xff", 1); return; } c = *s++ << 8; c |= *s++; n += 2; i=0; if (c < (1<<11)) { tmp[i++] = 0xc0 | ( c >> 6); tmp[i++] = 0x80 | ( c & 0x3f); } else { tmp[i++] = 0xe0 | ( c >> 12); tmp[i++] = 0x80 | ((c >> 6) & 0x3f); tmp[i++] = 0x80 | ( c & 0x3f); } put_stringbuf_mem (sb, tmp, i); } } /* Append attribute and value. ROOT is the sequence */ static gpg_error_t append_atv (const unsigned char *image, AsnNode root, struct stringbuf *sb) { AsnNode node = root->down; const char *name; int use_hex = 0; int i; if (!node || node->type != TYPE_OBJECT_ID) return gpg_error (GPG_ERR_UNEXPECTED_TAG); if (node->off == -1) return gpg_error (GPG_ERR_NO_VALUE); /* Hmmm, this might lead to misunderstandings */ name = NULL; for (i=0; oid_name_tbl[i].name; i++) { if (oid_name_tbl[i].source == 1 && node->len == oid_name_tbl[i].oidlen && !memcmp (image+node->off+node->nhdr, oid_name_tbl[i].oid, node->len)) { name = oid_name_tbl[i].name; break; } } if (name) put_stringbuf (sb, name); else { /* No name for the OID in the table; at least not DER encoded. Now convert the OID to a string, try to find it in the table again and use the string as last resort. */ char *p; p = ksba_oid_to_str (image+node->off+node->nhdr, node->len); if (!p) return gpg_error (GPG_ERR_ENOMEM); for (i=0; *p && oid_name_tbl[i].name; i++) { if (oid_name_tbl[i].source == 1 && !strcmp (p, oid_name_tbl[i].oidstr)) { name = oid_name_tbl[i].name; break; } } if (name) put_stringbuf (sb, name); else { put_stringbuf (sb, p); use_hex = 1; } xfree (p); } put_stringbuf (sb, "="); node = node->right; if (!node || node->off == -1) return gpg_error (GPG_ERR_NO_VALUE); switch (use_hex? 0 : node->type) { case TYPE_UTF8_STRING: append_utf8_value (image+node->off+node->nhdr, node->len, sb); break; case TYPE_PRINTABLE_STRING: case TYPE_IA5_STRING: /* we assume that wrong encodings are latin-1 */ case TYPE_TELETEX_STRING: /* Not correct, but mostly used as latin-1 */ append_latin1_value (image+node->off+node->nhdr, node->len, sb); break; case TYPE_UNIVERSAL_STRING: append_ucs4_value (image+node->off+node->nhdr, node->len, sb); break; case TYPE_BMP_STRING: append_ucs2_value (image+node->off+node->nhdr, node->len, sb); break; case 0: /* forced usage of hex */ default: put_stringbuf (sb, "#"); for (i=0; i < node->len; i++) { char tmp[3]; snprintf (tmp, sizeof tmp, "%02X", image[node->off+node->nhdr+i]); put_stringbuf (sb, tmp); } break; } return 0; } static gpg_error_t dn_to_str (const unsigned char *image, AsnNode root, struct stringbuf *sb) { gpg_error_t err; AsnNode nset; if (!root ) return 0; /* empty DN */ nset = root->down; if (!nset) return 0; /* consider this as empty */ if (nset->type != TYPE_SET_OF) return gpg_error (GPG_ERR_UNEXPECTED_TAG); /* output in reverse order */ while (nset->right) nset = nset->right; for (;;) { AsnNode nseq; if (nset->type != TYPE_SET_OF) return gpg_error (GPG_ERR_UNEXPECTED_TAG); for (nseq = nset->down; nseq; nseq = nseq->right) { if (nseq->type != TYPE_SEQUENCE) return gpg_error (GPG_ERR_UNEXPECTED_TAG); if (nseq != nset->down) put_stringbuf (sb, "+"); err = append_atv (image, nseq, sb); if (err) return err; } if (nset == root->down) break; put_stringbuf (sb, ","); nset = nset->left; } return 0; } gpg_error_t _ksba_dn_to_str (const unsigned char *image, AsnNode node, char **r_string) { gpg_error_t err; struct stringbuf sb; *r_string = NULL; if (!node || node->type != TYPE_SEQUENCE_OF) return gpg_error (GPG_ERR_INV_VALUE); init_stringbuf (&sb, 100); err = dn_to_str (image, node, &sb); if (!err) { *r_string = get_stringbuf (&sb); if (!*r_string) err = gpg_error (GPG_ERR_ENOMEM); } deinit_stringbuf (&sb); return err; } /* 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; } gpg_error_t _ksba_derdn_to_str (const unsigned char *der, size_t derlen, char **r_string) { gpg_error_t err; AsnNode root; unsigned char *image; size_t imagelen; ksba_reader_t reader; err = ksba_reader_new (&reader); if (err) return err; err = ksba_reader_set_mem (reader, der, derlen); if (err) { ksba_reader_release (reader); return err; } err = create_and_run_decoder (reader, "TMTTv2.CertificateList.tbsCertList.issuer", &root, &image, &imagelen); ksba_reader_release (reader); if (!err) { err = _ksba_dn_to_str (image, root->down, r_string); _ksba_asn_release_nodes (root); xfree (image); } return err; } /* Convert a string back to DN */ /* Count the number of bytes in a quoted string, return a pointer to the character after the string or NULL in case of an paring error. The number of bytes needed to store the string verbatim will be return as RESULT. With V2COMAP true, the string is assumed to be in v2 quoting (but w/o the leading quote character) */ static const char * count_quoted_string (const char *string, size_t *result, int v2compat, int *stringtype) { const unsigned char *s; int nbytes = 0; int highbit = 0; int nonprint = 0; int atsign = 0; *stringtype = 0; for (s=string; *s; s++) { if (*s == '\\') { /* pair */ s++; if (*s == ',' || *s == '=' || *s == '+' || *s == '<' || *s == '>' || *s == '#' || *s == ';' || *s == '\\' || *s == '\"' || *s == ' ') { if (!charclasses[*s]) nonprint = 1; nbytes++; } else if (hexdigitp (s) && hexdigitp (s+1)) { int c = xtoi_2 (s); if ((c & 0x80)) highbit = 1; else if (c == '@') atsign = 1; else if (!charclasses[c]) nonprint = 1; s++; nbytes++; } else return NULL; /* invalid escape sequence */ } else if (*s == '\"') { if (v2compat) break; return NULL; /* invalid encoding */ } else if (!v2compat && (*s == ',' || *s == '=' || *s == '+' || *s == '<' || *s == '>' || *s == '#' || *s == ';') ) { break; } else { nbytes++; if ((*s & 0x80)) highbit = 1; else if (*s == '@') atsign = 1; else if (!charclasses[*s]) nonprint = 1; } } /* Fixme: Should be remove spaces or white spces from the end unless they are not escaped or we are in v2compat mode? See TODO */ if (highbit || nonprint) *stringtype = TYPE_UTF8_STRING; else if (atsign) *stringtype = TYPE_IA5_STRING; else *stringtype = TYPE_PRINTABLE_STRING; *result = nbytes; return s; } /* Write out the data to W and do the required escaping. Note that NBYTES is the number of bytes actually to be written, i.e. it is the result from count_quoted_string */ static gpg_error_t write_escaped (ksba_writer_t w, const unsigned char *buffer, size_t nbytes) { const unsigned char *s; gpg_error_t err; for (s=buffer; nbytes; s++) { if (*s == '\\') { s++; if (hexdigitp (s) && hexdigitp (s+1)) { unsigned char buf = xtoi_2 (s); err = ksba_writer_write (w, &buf, 1); if (err) return err; s++; nbytes--; } else { err = ksba_writer_write (w, s, 1); if (err) return err; nbytes--; } } else { err = ksba_writer_write (w, s, 1); if (err) return err; nbytes--; } } return 0; } /* Parse one RDN, and write it to WRITER. Returns a pointer to the next RDN part where the comma has already been skipped or NULL in case of an error. When NULL is passed as WRITER, the function does not allocate any memory but just parses the string and returns the ENDP. If ROFF or RLEN are not NULL, they will receive informaion useful for error reporting. */ static gpg_error_t parse_rdn (const unsigned char *string, const char **endp, ksba_writer_t writer, size_t *roff, size_t *rlen) { const unsigned char *orig_string = string; const unsigned char *s, *s1; size_t n, n1; int i; unsigned char *p; unsigned char *oidbuf = NULL; unsigned char *valuebuf = NULL; const unsigned char *oid = NULL; size_t oidlen; const unsigned char *value = NULL; int valuelen; int valuetype; int need_escaping = 0; gpg_error_t err = 0; size_t dummy_roff, dummy_rlen; if (!roff) roff = &dummy_roff; if (!rlen) rlen = &dummy_rlen; *roff = *rlen = 0; if (!string) return gpg_error (GPG_ERR_INV_VALUE); while (*string == ' ') string++; *roff = string - orig_string; if (!*string) return gpg_error (GPG_ERR_SYNTAX); /* empty elements are not allowed */ s = string; if ( ((*s == 'o' && s[1] == 'i' && s[2] == 'd') ||(*s == 'O' && s[1] == 'I' && s[2] == 'D')) && s[3] == '.' && digitp (s+4)) s += 3; /* skip a prefixed oid */ /* parse attributeType */ string = s; *roff = string - orig_string; if (digitp (s)) { /* oid */ for (s++; digitp (s) || (*s == '.' && s[1] != '.'); s++) ; n = s - string; while (*s == ' ') s++; if (*s != '=') return gpg_error (GPG_ERR_SYNTAX); if (writer) { p = xtrymalloc (n+1); if (!p) return gpg_error (GPG_ERR_ENOMEM); memcpy (p, string, n); p[n] = 0; err = ksba_oid_from_str (p, &oidbuf, &oidlen); xfree (p); if (err) return err; oid = oidbuf; } } else if ((*s >= 'A' && *s <= 'Z') || (*s >= 'a' && *s <= 'z') ) { /* name */ for (s++; ((*s >= 'A' && *s <= 'Z') || (*s >= 'a' && *s <= 'z') || digitp (s) || *s == '-'); s++) ; n = s - string; while (*s == ' ') s++; if (*s != '=') return gpg_error (GPG_ERR_SYNTAX); for (i=0; oid_name_tbl[i].name; i++) { if ( n == strlen (oid_name_tbl[i].name) && !ascii_memcasecmp (string, oid_name_tbl[i].name, n)) break; } if (!oid_name_tbl[i].name) { *roff = string - orig_string; *rlen = n; return gpg_error (GPG_ERR_UNKNOWN_NAME); } oid = oid_name_tbl[i].oid; oidlen = oid_name_tbl[i].oidlen; } else return gpg_error (GPG_ERR_INV_NAME); s++; while (*s == ' ') s++; string = s; *roff = string - orig_string; /* parse attributeValue */ if (!*s) { err = gpg_error (GPG_ERR_SYNTAX); /* missing value */ goto leave; } if (*s == '#') { /* hexstring */ int need_utf8 = 0; int need_ia5 = 0; string = ++s; for (; hexdigitp (s); s++) s++; n = s - string; if (!n || (n & 1)) { *rlen = n; err = gpg_error (GPG_ERR_SYNTAX); /* no hex digits or odd number */ goto leave; } while (*s == ' ') s++; n /= 2; valuelen = n; if (writer) { valuebuf = xtrymalloc (valuelen); if (!valuebuf) { err = gpg_error (GPG_ERR_ENOMEM); goto leave; } for (p=valuebuf, s1=string; n; p++, s1 += 2, n--) { *p = xtoi_2 (s1); if (*p == '@') need_ia5 = 1; else if ((*p & 0x80) || !charclasses[*p]) need_utf8 = 1; } value = valuebuf; } else { for (s1=string; n; s1 += 2, n--) { unsigned int c; c = xtoi_2 (s1); if (c == '@') need_ia5 = 1; else if ((c & 0x80) || !charclasses[c]) need_utf8 = 1; } } valuetype = need_utf8? TYPE_UTF8_STRING : need_ia5 ? TYPE_IA5_STRING : TYPE_PRINTABLE_STRING; } else if (*s == '\"') { /* old style quotation */ string = s+1; s = count_quoted_string (string, &n, 1, &valuetype); if (!s || *s != '\"') { *rlen = s - orig_string; err = gpg_error (GPG_ERR_SYNTAX); /* error or quote not closed */ goto leave; } s++; while (*s == ' ') s++; value = string; valuelen = n; need_escaping = 1; } else { /* regular v3 quoted string */ s = count_quoted_string (string, &n, 0, &valuetype); if (!s) { err = gpg_error (GPG_ERR_SYNTAX); /* error */ goto leave; } while (*s == ' ') s++; value = string; valuelen = n; need_escaping = 1; } if (!valuelen) { err = gpg_error (GPG_ERR_SYNTAX); /* empty elements are not allowed */ goto leave; } if ( *s && *s != ',' && *s != ';' && *s != '+') { *roff = s - orig_string; err = gpg_error (GPG_ERR_SYNTAX); /* invalid delimiter */ goto leave; } if (*s == '+') /* fixme: implement this */ { *roff = s - orig_string; *rlen = 1; err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); goto leave; } *endp = *s? (s+1):s; if (writer) { /* write out the data */ /* need to calculate the length in advance */ n1 = _ksba_ber_count_tl (TYPE_OBJECT_ID, CLASS_UNIVERSAL, 0, oidlen); n1 += oidlen; n1 += _ksba_ber_count_tl (valuetype, CLASS_UNIVERSAL, 0, valuelen); n1 += valuelen; /* The SET tag */ n = _ksba_ber_count_tl (TYPE_SET, CLASS_UNIVERSAL, 1, n); n += n1; err = _ksba_ber_write_tl (writer, TYPE_SET, CLASS_UNIVERSAL, 1, n); /* The sequence tag */ n = n1; err = _ksba_ber_write_tl (writer, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, n); /* 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 value. Note that we don't need any conversion to the target characters set because the input is expected to be utf8 and the target type is either utf8, IA5 or printable string where the last two are subsets of utf8 */ err = _ksba_ber_write_tl (writer, valuetype, CLASS_UNIVERSAL, 0, valuelen); if (!err) err = need_escaping? write_escaped (writer, value, valuelen) : ksba_writer_write (writer, value, valuelen); } leave: xfree (oidbuf); xfree (valuebuf); return err; } gpg_error_t _ksba_dn_from_str (const char *string, char **rbuf, size_t *rlength) { gpg_error_t err; ksba_writer_t writer; const char *s, *endp; void *buf = NULL; size_t buflen; char const **part_array = NULL; int part_array_size, nparts; *rbuf = NULL; *rlength = 0; /* We are going to build the object using a writer object. */ err = ksba_writer_new (&writer); if (!err) err = ksba_writer_set_mem (writer, 1024); if (err) return err; /* We must assign it in reverse order so we do it in 2 passes. */ part_array_size = 0; for (nparts=0, s=string; s && *s;) { err = parse_rdn (s, &endp, NULL, NULL, NULL); if (err) goto leave; if (nparts >= part_array_size) { char const **tmp; part_array_size += 2; tmp = part_array_size? xtryrealloc (part_array, part_array_size * sizeof *tmp) : xtrymalloc (part_array_size * sizeof *tmp); if (!tmp) { err = gpg_error (GPG_ERR_ENOMEM); goto leave; } part_array = tmp; } part_array[nparts++] = s; s = endp; } if (!nparts) { err = gpg_error (GPG_ERR_SYNTAX); goto leave; } while (--nparts >= 0) { err = parse_rdn (part_array[nparts], &endp, writer, NULL, NULL); if (err) goto leave; } /* Now get the memory. */ buf = ksba_writer_snatch_mem (writer, &buflen); if (!buf) { err = gpg_error (GPG_ERR_ENOMEM); goto leave; } /* Reinitialize the buffer to create the outer sequence. */ err = ksba_writer_set_mem (writer, buflen + 10); if (err) goto leave; /* write the outer sequence */ err = _ksba_ber_write_tl (writer, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, buflen); if (err) goto leave; /* write the collected sets */ err = ksba_writer_write (writer, buf, buflen); if (err) goto leave; /* and get the result */ *rbuf = ksba_writer_snatch_mem (writer, rlength); if (!*rbuf) { err = gpg_error (GPG_ERR_ENOMEM); goto leave; } leave: xfree (part_array); ksba_writer_release (writer); xfree (buf); return err; } gpg_error_t ksba_dn_der2str (const void *der, size_t derlen, char **rstring) { return _ksba_derdn_to_str (der, derlen, rstring); } gpg_error_t ksba_dn_str2der (const char *string, unsigned char **rder, size_t *rderlen) { return _ksba_dn_from_str (string, (char**)rder, rderlen); } /* Assuming that STRING contains an rfc2253 encoded string, test whether this string may be passed as a valid DN to libksba. On success the functions returns 0. On error the function returns an error code and stores the offset within STRING of the erroneous part at RERROFF. RERRLEN will then receive the length of the erroneous part. This function is most useful to test whether a symbolic name (like SN) is supported. SEQ should be passed as 0 for now. RERROFF and RERRLEN may be passed as NULL if the caller is not interested at this value. */ gpg_error_t ksba_dn_teststr (const char *string, int seq, size_t *rerroff, size_t *rerrlen) { size_t dummy_erroff, dummy_errlen; gpg_error_t err; int nparts; const char *s, *endp; size_t off, len; if (!rerroff) rerroff = &dummy_erroff; if (!rerrlen) rerrlen = &dummy_errlen; *rerrlen = *rerroff = 0; for (nparts=0, s=string; s && *s; nparts++) { err = parse_rdn (s, &endp, NULL, &off, &len); if (err && !seq--) { *rerroff = s - string + off; *rerrlen = len? len : strlen (s); return err; } s = endp; } if (!nparts) return gpg_error (GPG_ERR_SYNTAX); return 0; } diff --git a/src/keyinfo.c b/src/keyinfo.c index 816126e..e08b7db 100644 --- a/src/keyinfo.c +++ b/src/keyinfo.c @@ -1,1971 +1,1877 @@ /* 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} }; - - -struct stringbuf { - size_t len; - size_t size; - char *buf; - int out_of_core; -}; - - #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; } -static void -init_stringbuf (struct stringbuf *sb, int initiallen) -{ - sb->len = 0; - sb->size = initiallen; - sb->out_of_core = 0; - /* allocate one more, so that get_stringbuf can append a nul */ - sb->buf = xtrymalloc (initiallen+1); - if (!sb->buf) - sb->out_of_core = 1; -} - -static void -put_stringbuf_mem (struct stringbuf *sb, const char *text, size_t n) -{ - if (sb->out_of_core) - return; - - if (sb->len + n >= sb->size) - { - char *p; - - sb->size += n + 100; - p = xtryrealloc (sb->buf, sb->size); - if ( !p) - { - sb->out_of_core = 1; - return; - } - sb->buf = p; - } - memcpy (sb->buf+sb->len, text, n); - sb->len += n; -} - -static void -put_stringbuf (struct stringbuf *sb, const char *text) -{ - put_stringbuf_mem (sb, text,strlen (text)); -} - -static void -put_stringbuf_mem_sexp (struct stringbuf *sb, const char *text, size_t length) -{ - char buf[20]; - sprintf (buf,"%u:", (unsigned int)length); - put_stringbuf (sb, buf); - put_stringbuf_mem (sb, text, length); -} - -static void -put_stringbuf_sexp (struct stringbuf *sb, const char *text) -{ - put_stringbuf_mem_sexp (sb, text, strlen (text)); -} - -static void -put_stringbuf_uint (struct stringbuf *sb, unsigned int value) -{ - char buf[35]; - snprintf (buf, sizeof buf, "%u", (unsigned int)value); - put_stringbuf_sexp (sb, buf); -} - - -static char * -get_stringbuf (struct stringbuf *sb) -{ - char *p; - - if (sb->out_of_core) - { - xfree (sb->buf); sb->buf = NULL; - return NULL; - } - - sb->buf[sb->len] = 0; - p = sb->buf; - sb->buf = NULL; - sb->out_of_core = 1; /* make sure the caller does an init before reuse */ - return p; -} - - /* Assume that der is a buffer of length DERLEN with a DER encoded - Asn.1 structure like this: + 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; } /* 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; struct tag_info ti; /* 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 parmeters. We simplify * this by assuming that pkcs1-MGF is used with an identical * hash algorithm. All other kinds of parameters are ignored. */ const unsigned char *parmder = der + parm_off; size_t parmderlen = parm_len; const unsigned char *parmder_end; char *tmpoid = NULL; err = parse_sequence (&parmder, &parmderlen, &ti); if (err) return err; parmder_end = parmder + ti.length; /* Get the hash algo. */ err = parse_context_tag (&parmder, &parmderlen, &ti, 0); if (err) goto unknown_parms; err = parse_sequence (&parmder, &parmderlen, &ti); if (err) goto unknown_parms; err = parse_object_id_into_str (&parmder, &parmderlen, &pss_hash); if (err) goto unknown_parms; /* Check the MGF OID and that its hash algo matches. */ err = parse_context_tag (&parmder, &parmderlen, &ti, 1); if (err) goto unknown_parms; err = parse_sequence (&parmder, &parmderlen, &ti); if (err) return err; err = parse_object_id_into_str (&parmder, &parmderlen, &tmpoid); if (err) goto unknown_parms; if (strcmp (tmpoid, "1.2.840.113549.1.1.8")) goto unknown_parms; err = parse_sequence (&parmder, &parmderlen, &ti); if (err) return err; xfree (tmpoid); err = parse_object_id_into_str (&parmder, &parmderlen, &tmpoid); if (err) goto unknown_parms; if (strcmp (tmpoid, pss_hash)) goto unknown_parms; /* Get the optional saltLength. */ err = parse_context_tag (&parmder, &parmderlen, &ti, 2); if (gpg_err_code (err) == GPG_ERR_INV_OBJ || gpg_err_code (err) == GPG_ERR_FALSE) { parmder -= ti.nhdr; /* backoff */ parmderlen += ti.nhdr; salt_length = 10; /* Optional element - use default value */ } else if (err) goto unknown_parms; else { err = parse_integer (&parmder, &parmderlen, &ti); if (err) return err; for (salt_length=0; ti.length; ti.length--) { salt_length <<= 8; salt_length |= (*parmder++) & 0xff; parmderlen--; } } if (parmder_end > parmder) goto unknown_parms; /* The sequence is larger than announced. */ goto parms_ready; unknown_parms: xfree (pss_hash); pss_hash = NULL; err = 0; parms_ready: xfree (tmpoid); } 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, "(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); } diff --git a/src/stringbuf.h b/src/stringbuf.h new file mode 100644 index 0000000..197f153 --- /dev/null +++ b/src/stringbuf.h @@ -0,0 +1,183 @@ +/* stringbuf.h - Inline functions for building strings. + * Copyright (C) 2001, 2002, 2007, 2008, 2012, 2020 g10 Code GmbH + * + * This file is part of KSBA. + * + * KSBA is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * KSBA is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * You should have received a copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see . + */ + +#ifndef STRINGBUF_H +#define STRINGBUF_H 1 + +#include "util.h" +#include "errno.h" + +struct stringbuf +{ + size_t len; + size_t size; + char *buf; + gpg_error_t out_of_core; +}; + + +static inline void +init_stringbuf (struct stringbuf *sb, int initiallen) +{ + sb->len = 0; + sb->size = initiallen; + sb->out_of_core = 0; + /* allocate one more, so that get_stringbuf can append a nul */ + sb->buf = xtrymalloc (initiallen+1); + if (!sb->buf) + sb->out_of_core = errno? errno : ENOMEM; +} + + +static inline void +deinit_stringbuf (struct stringbuf *sb) +{ + xfree (sb->buf); + sb->buf = NULL; + sb->out_of_core = ENOMEM; /* make sure the caller does an init before reuse */ +} + + +static inline void +put_stringbuf_mem (struct stringbuf *sb, const char *text, size_t n) +{ + if (sb->out_of_core) + return; + + if (sb->len + n >= sb->size) + { + char *p; + + sb->size += n + 100; + p = xtryrealloc (sb->buf, sb->size + 1); + if (!p) + { + sb->out_of_core = errno? errno : ENOMEM; + return; + } + sb->buf = p; + } + memcpy (sb->buf+sb->len, text, n); + sb->len += n; +} + + +static inline void +put_stringbuf_mem_skip (struct stringbuf *sb, const char *text, size_t n, + int skip) +{ + char *p; + + if (!skip) + { + put_stringbuf_mem (sb, text, n); + return; + } + if (sb->out_of_core) + return; + + if (sb->len + n >= sb->size) + { + /* Note: we allocate too much here, but we don't care. */ + sb->size += n + 100; + p = xtryrealloc (sb->buf, sb->size + 1); + if ( !p) + { + sb->out_of_core = errno? errno : ENOMEM; + return; + } + sb->buf = p; + } + p = sb->buf+sb->len; + while (n > skip) + { + text += skip; + n -= skip; + *p++ = *text++; + n--; + sb->len++; + } +} + + +static inline void +put_stringbuf (struct stringbuf *sb, const char *text) +{ + put_stringbuf_mem (sb, text,strlen (text)); +} + + +static inline void +put_stringbuf_mem_sexp (struct stringbuf *sb, const char *text, size_t length) +{ + char buf[20]; + sprintf (buf,"%u:", (unsigned int)length); + put_stringbuf (sb, buf); + put_stringbuf_mem (sb, text, length); +} + + +static inline void +put_stringbuf_sexp (struct stringbuf *sb, const char *text) +{ + put_stringbuf_mem_sexp (sb, text, strlen (text)); +} + + +static inline void +put_stringbuf_uint (struct stringbuf *sb, unsigned int value) +{ + char buf[35]; + snprintf (buf, sizeof buf, "%u", (unsigned int)value); + put_stringbuf_sexp (sb, buf); +} + + +static inline char * +get_stringbuf (struct stringbuf *sb) +{ + char *p; + + if (sb->out_of_core) + { + xfree (sb->buf); sb->buf = NULL; + gpg_err_set_errno (sb->out_of_core); + return NULL; + } + + sb->buf[sb->len] = 0; + p = sb->buf; + sb->buf = NULL; + sb->out_of_core = ENOMEM; /* make sure the caller does an init before reuse */ + return p; +} + + +#endif /*STRINGBUF_H*/