/* * Copyright (C) 2003-2012 Free Software Foundation, Inc. * * Author: Nikos Mavrogiannopoulos * * This file is part of GnuTLS. * * The GnuTLS is free software; you can redistribute it and/or * modify it under the terms of 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. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * */ #include #include #include #include #include #include #include #include #include #include "x509_int.h" #include struct oid2string { const char *oid; const char *ldap_desc; int choice; /* of type DirectoryString */ int printable; const char *asn_desc; /* description in the pkix file */ }; /* This list contains all the OIDs that may be * contained in a rdnSequence and are printable. */ static const struct oid2string _oid2str[] = { /* PKIX */ {"1.3.6.1.5.5.7.9.1", "dateOfBirth", 0, 1, "PKIX1.GeneralizedTime"}, {"1.3.6.1.5.5.7.9.2", "placeOfBirth", 0, 1, "PKIX1.DirectoryString"}, {"1.3.6.1.5.5.7.9.3", "gender", 0, 1, "PKIX1.PrintableString"}, {"1.3.6.1.5.5.7.9.4", "countryOfCitizenship", 0, 1, "PKIX1.PrintableString"}, {"1.3.6.1.5.5.7.9.5", "countryOfResidence", 0, 1, "PKIX1.PrintableString"}, {"2.5.4.6", "C", 0, 1, "PKIX1.PrintableString"}, {"2.5.4.9", "STREET", 1, 1, "PKIX1.DirectoryString"}, {"2.5.4.12", "T", 1, 1, "PKIX1.DirectoryString"}, {"2.5.4.10", "O", 1, 1, "PKIX1.DirectoryString"}, {"2.5.4.11", "OU", 1, 1, "PKIX1.DirectoryString"}, {"2.5.4.3", "CN", 1, 1, "PKIX1.DirectoryString"}, {"2.5.4.7", "L", 1, 1, "PKIX1.DirectoryString"}, {"2.5.4.8", "ST", 1, 1, "PKIX1.DirectoryString"}, {"2.5.4.5", "serialNumber", 0, 1, "PKIX1.PrintableString"}, {"2.5.4.20", "telephoneNumber", 0, 1, "PKIX1.PrintableString"}, {"2.5.4.4", "surName", 1, 1, "PKIX1.DirectoryString"}, {"2.5.4.43", "initials", 1, 1, "PKIX1.DirectoryString"}, {"2.5.4.44", "generationQualifier", 1, 1, "PKIX1.DirectoryString"}, {"2.5.4.42", "givenName", 1, 1, "PKIX1.DirectoryString"}, {"2.5.4.65", "pseudonym", 1, 1, "PKIX1.DirectoryString"}, {"2.5.4.46", "dnQualifier", 0, 1, "PKIX1.PrintableString"}, {"2.5.4.17", "postalCode", 1, 1, "PKIX1.DirectoryString"}, {"2.5.4.41", "Name", 1, 1, "PKIX1.DirectoryString"}, {"2.5.4.15", "businessCategory", 1, 1, "PKIX1.DirectoryString"}, {"0.9.2342.19200300.100.1.25", "DC", 0, 1, "PKIX1.IA5String"}, {"0.9.2342.19200300.100.1.1", "UID", 1, 1, "PKIX1.DirectoryString"}, /* Extended validation */ {"1.3.6.1.4.1.311.60.2.1.1", "jurisdictionOfIncorporationLocalityName", 1, 1, "PKIX1.DirectoryString"}, {"1.3.6.1.4.1.311.60.2.1.2", "jurisdictionOfIncorporationStateOrProvinceName", 1, 1, "PKIX1.DirectoryString"}, {"1.3.6.1.4.1.311.60.2.1.3", "jurisdictionOfIncorporationCountryName", 0, 1, "PKIX1.PrintableString"}, /* PKCS #9 */ {"1.2.840.113549.1.9.1", "EMAIL", 0, 1, "PKIX1.IA5String"}, {"1.2.840.113549.1.9.7", NULL, 1, 1, "PKIX1.pkcs-9-challengePassword"}, /* friendly name */ {"1.2.840.113549.1.9.20", NULL, 0, 1, "PKIX1.BMPString"}, /* local key id */ {"1.2.840.113549.1.9.21", NULL, 0, 1, "PKIX1.pkcs-9-localKeyId"}, /* rfc3920 section 5.1.1 */ {"1.3.6.1.5.5.7.8.5", "XmppAddr", 0, 1, "PKIX1.UTF8String"}, {NULL, NULL, 0, 0, ""} }; /* Returns 1 if the data defined by the OID are printable. */ int _gnutls_x509_oid_data_printable (const char *oid) { unsigned int i = 0; do { if (strcmp (_oid2str[i].oid, oid) == 0) return _oid2str[i].printable; i++; } while (_oid2str[i].oid != NULL); return 0; } /** * gnutls_x509_dn_oid_known: * @oid: holds an Object Identifier in a null terminated string * * This function will inform about known DN OIDs. This is useful since * functions like gnutls_x509_crt_set_dn_by_oid() use the information * on known OIDs to properly encode their input. Object Identifiers * that are not known are not encoded by these functions, and their * input is stored directly into the ASN.1 structure. In that case of * unknown OIDs, you have the responsibility of DER encoding your * data. * * Returns: 1 on known OIDs and 0 otherwise. **/ int gnutls_x509_dn_oid_known (const char *oid) { unsigned int i = 0; do { if (strcmp (_oid2str[i].oid, oid) == 0) return 1; i++; } while (_oid2str[i].oid != NULL); return 0; } /* Returns 1 if the data defined by the OID are of a choice * type. */ int _gnutls_x509_oid_data_choice (const char *oid) { unsigned int i = 0; do { if (strcmp (_oid2str[i].oid, oid) == 0) return _oid2str[i].choice; i++; } while (_oid2str[i].oid != NULL); return 0; } /** * gnutls_x509_dn_oid_name: * @oid: holds an Object Identifier in a null terminated string * @flags: 0 or %GNUTLS_X509_DN_OID_* * * This function will return the name of a known DN OID. If * %GNUTLS_X509_DN_OID_RETURN_OID is specified this function * will return the given OID if no descriptive name has been * found. * * Returns: A null terminated string or NULL otherwise. * * Since: 3.0 **/ const char* gnutls_x509_dn_oid_name (const char *oid, unsigned int flags) { unsigned int i = 0; do { if (strcmp (_oid2str[i].oid, oid) == 0) return _oid2str[i].ldap_desc; i++; } while (_oid2str[i].oid != NULL); if (flags & GNUTLS_X509_DN_OID_RETURN_OID) return oid; else return NULL; } const char * _gnutls_x509_oid2asn_string (const char *oid) { unsigned int i = 0; do { if (strcmp (_oid2str[i].oid, oid) == 0) return _oid2str[i].asn_desc; i++; } while (_oid2str[i].oid != NULL); return NULL; } /* This function will convert an attribute value, specified by the OID, * to a string. The result will be a null terminated string. * * res may be null. This will just return the res_size, needed to * hold the string. */ int _gnutls_x509_oid_data2string (const char *oid, void *value, int value_size, char *res, size_t * res_size) { char str[MAX_STRING_LEN], tmpname[128]; const char *ANAME = NULL; int CHOICE = -1, len = -1, result; ASN1_TYPE tmpasn = ASN1_TYPE_EMPTY; char asn1_err[ASN1_MAX_ERROR_DESCRIPTION_SIZE] = ""; if (value == NULL || value_size <= 0 || res_size == NULL) { gnutls_assert (); return GNUTLS_E_INVALID_REQUEST; } if (_gnutls_x509_oid_data_printable (oid) == 0) { gnutls_assert (); return GNUTLS_E_INTERNAL_ERROR; } ANAME = _gnutls_x509_oid2asn_string (oid); CHOICE = _gnutls_x509_oid_data_choice (oid); if (ANAME == NULL) { gnutls_assert (); return GNUTLS_E_INTERNAL_ERROR; } if ((result = asn1_create_element (_gnutls_get_pkix (), ANAME, &tmpasn)) != ASN1_SUCCESS) { gnutls_assert (); return _gnutls_asn2err (result); } if ((result = asn1_der_decoding (&tmpasn, value, value_size, asn1_err)) != ASN1_SUCCESS) { gnutls_assert (); _gnutls_debug_log ("asn1_der_decoding: %s:%s\n", str, asn1_err); asn1_delete_structure (&tmpasn); return _gnutls_asn2err (result); } /* If this is a choice then we read the choice. Otherwise it * is the value; */ len = sizeof (str) - 1; if ((result = asn1_read_value (tmpasn, "", str, &len)) != ASN1_SUCCESS) { /* CHOICE */ gnutls_assert (); asn1_delete_structure (&tmpasn); return _gnutls_asn2err (result); } if (CHOICE == 0) { str[len] = 0; /* Refuse to deal with strings containing NULs. */ if (strlen (str) != (size_t)len) return GNUTLS_E_ASN1_DER_ERROR; if (res) _gnutls_str_cpy (res, *res_size, str); *res_size = (size_t)len; asn1_delete_structure (&tmpasn); } else { /* CHOICE */ int non_printable = 0, teletex = 0; str[len] = 0; /* Note that we do not support strings other than * UTF-8 (thus ASCII as well). */ if (strcmp (str, "printableString") != 0 && strcmp (str, "ia5String") != 0 && strcmp (str, "utf8String") != 0) { non_printable = 1; } if (strcmp (str, "teletexString") == 0) teletex = 1; _gnutls_str_cpy (tmpname, sizeof (tmpname), str); len = sizeof (str) - 1; if ((result = asn1_read_value (tmpasn, tmpname, str, &len)) != ASN1_SUCCESS) { asn1_delete_structure (&tmpasn); return _gnutls_asn2err (result); } asn1_delete_structure (&tmpasn); if (teletex != 0) { int ascii = 0, i; /* HACK: if the teletex string contains only ascii * characters then treat it as printable. */ for (i = 0; i < len; i++) if (!isascii (str[i])) ascii = 1; if (ascii == 0) non_printable = 0; } if (non_printable == 0) { str[len] = 0; /* Refuse to deal with strings containing NULs. */ if (strlen (str) != (size_t)len) return GNUTLS_E_ASN1_DER_ERROR; if (res) _gnutls_str_cpy (res, *res_size, str); *res_size = (size_t)len; } else { result = _gnutls_x509_data2hex (str, (size_t)len, res, res_size); if (result < 0) { gnutls_assert (); return result; } } } return 0; } /* Converts a data string to an LDAP rfc2253 hex string * something like '#01020304' */ int _gnutls_x509_data2hex (const void * data, size_t data_size, void * _out, size_t * sizeof_out) { char *res; char escaped[MAX_STRING_LEN]; unsigned int size; char* out = _out; if (2 * data_size + 1 > MAX_STRING_LEN) { gnutls_assert (); return GNUTLS_E_INTERNAL_ERROR; } res = _gnutls_bin2hex (data, data_size, escaped, sizeof (escaped), NULL); if (!res) { gnutls_assert (); return GNUTLS_E_INTERNAL_ERROR; } size = strlen (res) + 1; if (size + 1 > *sizeof_out) { *sizeof_out = size; return GNUTLS_E_SHORT_MEMORY_BUFFER; } *sizeof_out = size; /* -1 for the null +1 for the '#' */ if (out) { out[0] = '#'; out[1] = 0; _gnutls_str_cat (out, *sizeof_out, res); } return 0; } /* TIME functions * Convertions between generalized or UTC time to time_t * */ /* This is an emulations of the struct tm. * Since we do not use libc's functions, we don't need to * depend on the libc structure. */ typedef struct fake_tm { int tm_mon; int tm_year; /* FULL year - ie 1971 */ int tm_mday; int tm_hour; int tm_min; int tm_sec; } fake_tm; /* The mktime_utc function is due to Russ Allbery (rra@stanford.edu), * who placed it under public domain: */ /* The number of days in each month. */ static const int MONTHDAYS[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; /* Whether a given year is a leap year. */ #define ISLEAP(year) \ (((year) % 4) == 0 && (((year) % 100) != 0 || ((year) % 400) == 0)) /* ** Given a struct tm representing a calendar time in UTC, convert it to ** seconds since epoch. Returns (time_t) -1 if the time is not ** convertable. Note that this function does not canonicalize the provided ** struct tm, nor does it allow out of range values or years before 1970. */ static time_t mktime_utc (const struct fake_tm *tm) { time_t result = 0; int i; /* We do allow some ill-formed dates, but we don't do anything special * with them and our callers really shouldn't pass them to us. Do * explicitly disallow the ones that would cause invalid array accesses * or other algorithm problems. */ if (tm->tm_mon < 0 || tm->tm_mon > 11 || tm->tm_year < 1970) return (time_t) - 1; /* Convert to a time_t. */ for (i = 1970; i < tm->tm_year; i++) result += 365 + ISLEAP (i); for (i = 0; i < tm->tm_mon; i++) result += MONTHDAYS[i]; if (tm->tm_mon > 1 && ISLEAP (tm->tm_year)) result++; result = 24 * (result + tm->tm_mday - 1) + tm->tm_hour; result = 60 * result + tm->tm_min; result = 60 * result + tm->tm_sec; return result; } /* this one will parse dates of the form: * month|day|hour|minute|sec* (2 chars each) * and year is given. Returns a time_t date. */ static time_t _gnutls_x509_time2gtime (const char *ttime, int year) { char xx[4]; struct fake_tm etime; time_t ret; if (strlen (ttime) < 8) { gnutls_assert (); return (time_t) - 1; } etime.tm_year = year; /* In order to work with 32 bit * time_t. */ if (sizeof (time_t) <= 4 && etime.tm_year >= 2038) return (time_t) 2145914603; /* 2037-12-31 23:23:23 */ if (etime.tm_year < 1970) return (time_t) 0; xx[2] = 0; /* get the month */ memcpy (xx, ttime, 2); /* month */ etime.tm_mon = atoi (xx) - 1; ttime += 2; /* get the day */ memcpy (xx, ttime, 2); /* day */ etime.tm_mday = atoi (xx); ttime += 2; /* get the hour */ memcpy (xx, ttime, 2); /* hour */ etime.tm_hour = atoi (xx); ttime += 2; /* get the minutes */ memcpy (xx, ttime, 2); /* minutes */ etime.tm_min = atoi (xx); ttime += 2; if (strlen (ttime) >= 2) { memcpy (xx, ttime, 2); etime.tm_sec = atoi (xx); ttime += 2; } else etime.tm_sec = 0; ret = mktime_utc (&etime); return ret; } /* returns a time_t value that contains the given time. * The given time is expressed as: * YEAR(2)|MONTH(2)|DAY(2)|HOUR(2)|MIN(2)|SEC(2)* * * (seconds are optional) */ static time_t _gnutls_x509_utcTime2gtime (const char *ttime) { char xx[3]; int year; if (strlen (ttime) < 10) { gnutls_assert (); return (time_t) - 1; } xx[2] = 0; /* get the year */ memcpy (xx, ttime, 2); /* year */ year = atoi (xx); ttime += 2; if (year > 49) year += 1900; else year += 2000; return _gnutls_x509_time2gtime (ttime, year); } /* returns a time value that contains the given time. * The given time is expressed as: * YEAR(2)|MONTH(2)|DAY(2)|HOUR(2)|MIN(2)|SEC(2) */ static int _gnutls_x509_gtime2utcTime (time_t gtime, char *str_time, size_t str_time_size) { size_t ret; struct tm _tm; if (!gmtime_r (>ime, &_tm)) { gnutls_assert (); return GNUTLS_E_INTERNAL_ERROR; } ret = strftime (str_time, str_time_size, "%y%m%d%H%M%SZ", &_tm); if (!ret) { gnutls_assert (); return GNUTLS_E_SHORT_MEMORY_BUFFER; } return 0; } /* returns a time_t value that contains the given time. * The given time is expressed as: * YEAR(4)|MONTH(2)|DAY(2)|HOUR(2)|MIN(2)|SEC(2)* */ time_t _gnutls_x509_generalTime2gtime (const char *ttime) { char xx[5]; int year; if (strlen (ttime) < 12) { gnutls_assert (); return (time_t) - 1; } if (strchr (ttime, 'Z') == 0) { gnutls_assert (); /* sorry we don't support it yet */ return (time_t) - 1; } xx[4] = 0; /* get the year */ memcpy (xx, ttime, 4); /* year */ year = atoi (xx); ttime += 4; return _gnutls_x509_time2gtime (ttime, year); } /* Extracts the time in time_t from the ASN1_TYPE given. When should * be something like "tbsCertList.thisUpdate". */ #define MAX_TIME 64 time_t _gnutls_x509_get_time (ASN1_TYPE c2, const char *when) { char ttime[MAX_TIME]; char name[128]; time_t c_time = (time_t) - 1; int len, result; _gnutls_str_cpy (name, sizeof (name), when); len = sizeof (ttime) - 1; if ((result = asn1_read_value (c2, name, ttime, &len)) < 0) { gnutls_assert (); return (time_t) (-1); } /* CHOICE */ if (strcmp (ttime, "generalTime") == 0) { _gnutls_str_cat (name, sizeof (name), ".generalTime"); len = sizeof (ttime) - 1; result = asn1_read_value (c2, name, ttime, &len); if (result == ASN1_SUCCESS) c_time = _gnutls_x509_generalTime2gtime (ttime); } else { /* UTCTIME */ _gnutls_str_cat (name, sizeof (name), ".utcTime"); len = sizeof (ttime) - 1; result = asn1_read_value (c2, name, ttime, &len); if (result == ASN1_SUCCESS) c_time = _gnutls_x509_utcTime2gtime (ttime); } /* We cannot handle dates after 2031 in 32 bit machines. * a time_t of 64bits has to be used. */ if (result != ASN1_SUCCESS) { gnutls_assert (); return (time_t) (-1); } return c_time; } /* Sets the time in time_t in the ASN1_TYPE given. Where should * be something like "tbsCertList.thisUpdate". */ int _gnutls_x509_set_time (ASN1_TYPE c2, const char *where, time_t tim) { char str_time[MAX_TIME]; char name[128]; int result, len; _gnutls_str_cpy (name, sizeof (name), where); if ((result = asn1_write_value (c2, name, "utcTime", 1)) < 0) { gnutls_assert (); return _gnutls_asn2err (result); } result = _gnutls_x509_gtime2utcTime (tim, str_time, sizeof (str_time)); if (result < 0) { gnutls_assert (); return result; } _gnutls_str_cat (name, sizeof (name), ".utcTime"); len = strlen (str_time); result = asn1_write_value (c2, name, str_time, len); if (result != ASN1_SUCCESS) { gnutls_assert (); return _gnutls_asn2err (result); } return 0; } gnutls_x509_subject_alt_name_t _gnutls_x509_san_find_type (char *str_type) { if (strcmp (str_type, "dNSName") == 0) return GNUTLS_SAN_DNSNAME; if (strcmp (str_type, "rfc822Name") == 0) return GNUTLS_SAN_RFC822NAME; if (strcmp (str_type, "uniformResourceIdentifier") == 0) return GNUTLS_SAN_URI; if (strcmp (str_type, "iPAddress") == 0) return GNUTLS_SAN_IPADDRESS; if (strcmp (str_type, "otherName") == 0) return GNUTLS_SAN_OTHERNAME; if (strcmp (str_type, "directoryName") == 0) return GNUTLS_SAN_DN; return (gnutls_x509_subject_alt_name_t) - 1; } /* A generic export function. Will export the given ASN.1 encoded data * to PEM or DER raw data. */ int _gnutls_x509_export_int_named (ASN1_TYPE asn1_data, const char *name, gnutls_x509_crt_fmt_t format, const char *pem_header, unsigned char *output_data, size_t * output_data_size) { int result, len; if (format == GNUTLS_X509_FMT_DER) { if (output_data == NULL) *output_data_size = 0; len = *output_data_size; if ((result = asn1_der_coding (asn1_data, name, output_data, &len, NULL)) != ASN1_SUCCESS) { *output_data_size = (size_t)len; if (result == ASN1_MEM_ERROR) { return GNUTLS_E_SHORT_MEMORY_BUFFER; } gnutls_assert (); return _gnutls_asn2err (result); } *output_data_size = (size_t)len; } else { /* PEM */ uint8_t *out; gnutls_datum_t tmp; result = _gnutls_x509_der_encode (asn1_data, name, &tmp, 0); if (result < 0) { gnutls_assert (); return result; } result = _gnutls_fbase64_encode (pem_header, tmp.data, tmp.size, &out); _gnutls_free_datum (&tmp); if (result < 0) { gnutls_assert (); return result; } if (result == 0) { /* oooops */ gnutls_assert (); return GNUTLS_E_INTERNAL_ERROR; } if ((size_t) result > *output_data_size) { gnutls_assert (); gnutls_free (out); *output_data_size = (size_t)result; return GNUTLS_E_SHORT_MEMORY_BUFFER; } *output_data_size = (size_t)result; if (output_data) { memcpy (output_data, out, (size_t)result); /* do not include the null character into output size. */ *output_data_size = (size_t)result - 1; } gnutls_free (out); } return 0; } int _gnutls_x509_export_int (ASN1_TYPE asn1_data, gnutls_x509_crt_fmt_t format, const char *pem_header, unsigned char *output_data, size_t * output_data_size) { return _gnutls_x509_export_int_named (asn1_data, "", format, pem_header, output_data, output_data_size); } /* Decodes an octet string. Leave string_type null for a normal * octet string. Otherwise put something like BMPString, PrintableString * etc. */ int _gnutls_x509_decode_octet_string (const char *string_type, const uint8_t * der, size_t der_size, uint8_t * output, size_t * output_size) { ASN1_TYPE c2 = ASN1_TYPE_EMPTY; int result, tmp_output_size; char strname[64]; if (string_type == NULL) _gnutls_str_cpy (strname, sizeof (strname), "PKIX1.pkcs-7-Data"); else { _gnutls_str_cpy (strname, sizeof (strname), "PKIX1."); _gnutls_str_cat (strname, sizeof (strname), string_type); } if ((result = asn1_create_element (_gnutls_get_pkix (), strname, &c2)) != ASN1_SUCCESS) { gnutls_assert (); result = _gnutls_asn2err (result); goto cleanup; } result = asn1_der_decoding (&c2, der, der_size, NULL); if (result != ASN1_SUCCESS) { gnutls_assert (); result = _gnutls_asn2err (result); goto cleanup; } tmp_output_size = *output_size; result = asn1_read_value (c2, "", output, &tmp_output_size); *output_size = (size_t)tmp_output_size; if (result != ASN1_SUCCESS) { gnutls_assert (); result = _gnutls_asn2err (result); goto cleanup; } result = 0; cleanup: if (c2) asn1_delete_structure (&c2); return result; } /* Reads a value from an ASN1 tree, and puts the output * in an allocated variable in the given datum. * flags == 0 do nothing with the DER output * flags == 1 parse the DER output as OCTET STRING * flags == 2 the value is a BIT STRING */ int _gnutls_x509_read_value (ASN1_TYPE c, const char *root, gnutls_datum_t * ret, int flags) { int len = 0, result; size_t slen; uint8_t *tmp = NULL; result = asn1_read_value (c, root, NULL, &len); if (result != ASN1_MEM_ERROR) { gnutls_assert (); result = _gnutls_asn2err (result); return result; } if (flags == 2) len /= 8; tmp = gnutls_malloc ((size_t)len); if (tmp == NULL) { gnutls_assert (); result = GNUTLS_E_MEMORY_ERROR; goto cleanup; } result = asn1_read_value (c, root, tmp, &len); if (result != ASN1_SUCCESS) { gnutls_assert (); result = _gnutls_asn2err (result); goto cleanup; } if (flags == 2) len /= 8; /* Extract the OCTET STRING. */ if (flags == 1) { slen = (size_t)len; result = _gnutls_x509_decode_octet_string (NULL, tmp, slen, tmp, &slen); if (result < 0) { gnutls_assert (); goto cleanup; } len = slen; } ret->data = tmp; ret->size = (unsigned)len; return 0; cleanup: gnutls_free (tmp); return result; } /* DER Encodes the src ASN1_TYPE and stores it to * the given datum. If str is non null then the data are encoded as * an OCTET STRING. */ int _gnutls_x509_der_encode (ASN1_TYPE src, const char *src_name, gnutls_datum_t * res, int str) { int size, result; int asize; uint8_t *data = NULL; ASN1_TYPE c2 = ASN1_TYPE_EMPTY; size = 0; result = asn1_der_coding (src, src_name, NULL, &size, NULL); if (result != ASN1_MEM_ERROR) { gnutls_assert (); result = _gnutls_asn2err (result); goto cleanup; } /* allocate data for the der */ if (str) size += 16; /* for later to include the octet tags */ asize = size; data = gnutls_malloc ((size_t)size); if (data == NULL) { gnutls_assert (); result = GNUTLS_E_MEMORY_ERROR; goto cleanup; } result = asn1_der_coding (src, src_name, data, &size, NULL); if (result != ASN1_SUCCESS) { gnutls_assert (); result = _gnutls_asn2err (result); goto cleanup; } if (str) { if ((result = asn1_create_element (_gnutls_get_pkix (), "PKIX1.pkcs-7-Data", &c2)) != ASN1_SUCCESS) { gnutls_assert (); result = _gnutls_asn2err (result); goto cleanup; } result = asn1_write_value (c2, "", data, size); if (result != ASN1_SUCCESS) { gnutls_assert (); result = _gnutls_asn2err (result); goto cleanup; } result = asn1_der_coding (c2, "", data, &asize, NULL); if (result != ASN1_SUCCESS) { gnutls_assert (); result = _gnutls_asn2err (result); goto cleanup; } size = asize; asn1_delete_structure (&c2); } res->data = data; res->size = (unsigned)size; return 0; cleanup: gnutls_free (data); asn1_delete_structure (&c2); return result; } /* DER Encodes the src ASN1_TYPE and stores it to * dest in dest_name. Useful to encode something and store it * as OCTET. If str is non null then the data are encoded as * an OCTET STRING. */ int _gnutls_x509_der_encode_and_copy (ASN1_TYPE src, const char *src_name, ASN1_TYPE dest, const char *dest_name, int str) { int result; gnutls_datum_t encoded; result = _gnutls_x509_der_encode (src, src_name, &encoded, str); if (result < 0) { gnutls_assert (); return result; } /* Write the data. */ result = asn1_write_value (dest, dest_name, encoded.data, (int)encoded.size); _gnutls_free_datum (&encoded); if (result != ASN1_SUCCESS) { gnutls_assert (); return _gnutls_asn2err (result); } return 0; } /* Writes the value of the datum in the given ASN1_TYPE. If str is non * (0) it encodes it as OCTET STRING. */ int _gnutls_x509_write_value (ASN1_TYPE c, const char *root, const gnutls_datum_t * data, int str) { int result; ASN1_TYPE c2 = ASN1_TYPE_EMPTY; gnutls_datum_t val = { NULL, 0 }; if (str) { /* Convert it to OCTET STRING */ if ((result = asn1_create_element (_gnutls_get_pkix (), "PKIX1.pkcs-7-Data", &c2)) != ASN1_SUCCESS) { gnutls_assert (); result = _gnutls_asn2err (result); goto cleanup; } result = asn1_write_value (c2, "", data->data, data->size); if (result != ASN1_SUCCESS) { gnutls_assert (); result = _gnutls_asn2err (result); goto cleanup; } result = _gnutls_x509_der_encode (c2, "", &val, 0); if (result < 0) { gnutls_assert (); goto cleanup; } } else { val.data = data->data; val.size = data->size; } /* Write the data. */ result = asn1_write_value (c, root, val.data, val.size); if (result != ASN1_SUCCESS) { gnutls_assert (); result = _gnutls_asn2err (result); goto cleanup; } result = 0; cleanup: asn1_delete_structure (&c2); if (val.data != data->data) _gnutls_free_datum (&val); return result; } void _asnstr_append_name (char *name, size_t name_size, const char *part1, const char *part2) { if (part1[0] != 0) { _gnutls_str_cpy (name, name_size, part1); _gnutls_str_cat (name, name_size, part2); } else _gnutls_str_cpy (name, name_size, part2 + 1 /* remove initial dot */ ); } /* Encodes and copies the private key parameters into a * subjectPublicKeyInfo structure. * */ int _gnutls_x509_encode_and_copy_PKI_params (ASN1_TYPE dst, const char *dst_name, gnutls_pk_algorithm_t pk_algorithm, gnutls_pk_params_st * params) { const char *pk; gnutls_datum_t der = { NULL, 0 }; int result; char name[128]; pk = _gnutls_x509_pk_to_oid (pk_algorithm); if (pk == NULL) { gnutls_assert (); return GNUTLS_E_UNKNOWN_PK_ALGORITHM; } /* write the OID */ _asnstr_append_name (name, sizeof (name), dst_name, ".algorithm.algorithm"); result = asn1_write_value (dst, name, pk, 1); if (result != ASN1_SUCCESS) { gnutls_assert (); return _gnutls_asn2err (result); } result = _gnutls_x509_write_pubkey_params (pk_algorithm, params, &der); if (result < 0) { gnutls_assert (); return result; } _asnstr_append_name (name, sizeof (name), dst_name, ".algorithm.parameters"); result = asn1_write_value (dst, name, der.data, der.size); _gnutls_free_datum (&der); if (result != ASN1_SUCCESS) { gnutls_assert (); return _gnutls_asn2err (result); } result = _gnutls_x509_write_pubkey (pk_algorithm, params, &der); if (result < 0) { gnutls_assert (); return result; } /* Write the DER parameters. (in bits) */ _asnstr_append_name (name, sizeof (name), dst_name, ".subjectPublicKey"); result = asn1_write_value (dst, name, der.data, der.size * 8); _gnutls_free_datum (&der); if (result != ASN1_SUCCESS) { gnutls_assert (); return _gnutls_asn2err (result); } return 0; } /* Encodes and public key parameters into a * subjectPublicKeyInfo structure and stores it in der. */ int _gnutls_x509_encode_PKI_params (gnutls_datum_t *der, gnutls_pk_algorithm_t pk_algorithm, gnutls_pk_params_st * params) { int ret; ASN1_TYPE tmp; ret = asn1_create_element (_gnutls_get_pkix (), "PKIX1.Certificate", &tmp); if (ret != ASN1_SUCCESS) { gnutls_assert (); return _gnutls_asn2err (ret); } ret = _gnutls_x509_encode_and_copy_PKI_params (tmp, "tbsCertificate.subjectPublicKeyInfo", pk_algorithm, params); if (ret != ASN1_SUCCESS) { gnutls_assert (); ret = _gnutls_asn2err (ret); goto cleanup; } ret = _gnutls_x509_der_encode(tmp, "tbsCertificate.subjectPublicKeyInfo", der, 0); cleanup: asn1_delete_structure (&tmp); return ret; } /* Reads and returns the PK algorithm of the given certificate-like * ASN.1 structure. src_name should be something like "tbsCertificate.subjectPublicKeyInfo". */ int _gnutls_x509_get_pk_algorithm (ASN1_TYPE src, const char *src_name, unsigned int *bits) { int result; int algo; char oid[64]; int len; gnutls_pk_params_st params; char name[128]; gnutls_pk_params_init(¶ms); _asnstr_append_name (name, sizeof (name), src_name, ".algorithm.algorithm"); len = sizeof (oid); result = asn1_read_value (src, name, oid, &len); if (result != ASN1_SUCCESS) { gnutls_assert (); return _gnutls_asn2err (result); } algo = _gnutls_x509_oid2pk_algorithm (oid); if (algo == GNUTLS_PK_UNKNOWN) { _gnutls_debug_log ("%s: unknown public key algorithm: %s\n", __func__, oid); } if (bits == NULL) { return algo; } /* Now read the parameters' bits */ result = _gnutls_get_asn_mpis(src, src_name, ¶ms); if (result < 0) return gnutls_assert_val(result); bits[0] = pubkey_to_bits(algo, ¶ms); gnutls_pk_params_release(¶ms); return algo; } /* Reads the DER signed data from the certificate and allocates space and * returns them into signed_data. */ int _gnutls_x509_get_signed_data (ASN1_TYPE src, const char *src_name, gnutls_datum_t * signed_data) { gnutls_datum_t der; int start, end, result; result = _gnutls_x509_der_encode (src, "", &der, 0); if (result < 0) { gnutls_assert (); return result; } /* Get the signed data */ result = asn1_der_decoding_startEnd (src, der.data, der.size, src_name, &start, &end); if (result != ASN1_SUCCESS) { result = _gnutls_asn2err (result); gnutls_assert (); goto cleanup; } result = _gnutls_set_datum (signed_data, &der.data[start], end - start + 1); if (result < 0) { gnutls_assert (); goto cleanup; } result = 0; cleanup: _gnutls_free_datum (&der); return result; } /*- * gnutls_x509_get_signature_algorithm: * @src: should contain an ASN1_TYPE structure * @src_name: the description of the signature field * * This function will return a value of the #gnutls_sign_algorithm_t * enumeration that is the signature algorithm that has been used to * sign this certificate. * * Returns: a #gnutls_sign_algorithm_t value, or a negative error code on * error. -*/ int _gnutls_x509_get_signature_algorithm (ASN1_TYPE src, const char *src_name) { int result; gnutls_datum_t sa; /* Read the signature algorithm. Note that parameters are not * read. They will be read from the issuer's certificate if needed. */ result = _gnutls_x509_read_value (src, src_name, &sa, 0); if (result < 0) { gnutls_assert (); return result; } result = _gnutls_x509_oid2sign_algorithm ( (char*)sa.data); _gnutls_free_datum (&sa); return result; } /* Reads the DER signature from the certificate and allocates space and * returns them into signed_data. */ int _gnutls_x509_get_signature (ASN1_TYPE src, const char *src_name, gnutls_datum_t * signature) { int result, len; unsigned int bits; signature->data = NULL; signature->size = 0; /* Read the signature */ len = 0; result = asn1_read_value (src, src_name, NULL, &len); if (result != ASN1_MEM_ERROR) { result = _gnutls_asn2err (result); gnutls_assert (); goto cleanup; } bits = len; if (bits % 8 != 0) { gnutls_assert (); result = GNUTLS_E_CERTIFICATE_ERROR; goto cleanup; } len = bits / 8; signature->data = gnutls_malloc (len); if (signature->data == NULL) { gnutls_assert (); result = GNUTLS_E_MEMORY_ERROR; return result; } /* read the bit string of the signature */ bits = len; result = asn1_read_value (src, src_name, signature->data, (int*)&bits); if (result != ASN1_SUCCESS) { result = _gnutls_asn2err (result); gnutls_assert (); goto cleanup; } signature->size = len; return 0; cleanup: return result; }