diff options
-rw-r--r-- | common/Makefile.am | 2 | ||||
-rw-r--r-- | common/asn1.c | 551 | ||||
-rw-r--r-- | common/asn1.h | 65 | ||||
-rw-r--r-- | common/oid.h | 12 | ||||
-rw-r--r-- | common/tests/Makefile.am | 2 | ||||
-rw-r--r-- | common/tests/test-asn1.c | 113 | ||||
-rw-r--r-- | common/tests/test-x509.c | 185 | ||||
-rw-r--r-- | common/x509.c | 152 | ||||
-rw-r--r-- | common/x509.h | 56 | ||||
-rw-r--r-- | trust/mozilla.c | 31 | ||||
-rw-r--r-- | trust/parser.c | 546 | ||||
-rw-r--r-- | trust/parser.h | 14 | ||||
-rw-r--r-- | trust/tests/test-data.h | 28 | ||||
-rw-r--r-- | trust/tests/test-parser.c | 103 |
14 files changed, 1193 insertions, 667 deletions
diff --git a/common/Makefile.am b/common/Makefile.am index bed4630..145627c 100644 --- a/common/Makefile.am +++ b/common/Makefile.am @@ -39,12 +39,14 @@ noinst_LTLIBRARIES += \ $(NULL) libp11_data_la_SOURCES = \ + asn1.c asn1.h \ base64.c base64.h \ checksum.c checksum.h \ oid.c oid.h \ openssl.asn openssl.asn.h \ pem.c pem.h \ pkix.asn pkix.asn.h \ + x509.c x509.h \ $(NULL) asn: diff --git a/common/asn1.c b/common/asn1.c new file mode 100644 index 0000000..3df56d9 --- /dev/null +++ b/common/asn1.c @@ -0,0 +1,551 @@ +/* + * Copyright (C) 2012 Red Hat Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the + * above copyright notice, this list of conditions and + * the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * * The names of contributors to this software may not be + * used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * Author: Stef Walter <stefw@redhat.com> + */ + +#include "config.h" + +#include "asn1.h" +#define P11_DEBUG_FLAG P11_DEBUG_TRUST +#include "debug.h" +#include "oid.h" + +#include "openssl.asn.h" +#include "pkix.asn.h" + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +static void +free_asn1_def (void *data) +{ + node_asn *def = data; + asn1_delete_structure (&def); +} + +struct { + const ASN1_ARRAY_TYPE* tab; + const char *prefix; + int prefix_len; +} asn1_tabs[] = { + { pkix_asn1_tab, "PKIX1.", 6 }, + { openssl_asn1_tab, "OPENSSL.", 8 }, + { NULL, }, +}; + +p11_dict * +p11_asn1_defs_load (void) +{ + char message[ASN1_MAX_ERROR_DESCRIPTION_SIZE] = { 0, }; + node_asn *def; + p11_dict *defs; + int ret; + int i; + + defs = p11_dict_new (p11_dict_str_hash, p11_dict_str_equal, NULL, free_asn1_def); + + for (i = 0; asn1_tabs[i].tab != NULL; i++) { + + def = NULL; + ret = asn1_array2tree (asn1_tabs[i].tab, &def, message); + if (ret != ASN1_SUCCESS) { + p11_debug_precond ("failed to load %s* definitions: %s: %s\n", + asn1_tabs[i].prefix, asn1_strerror (ret), message); + return NULL; + } + + if (!p11_dict_set (defs, (void *)asn1_tabs[i].prefix, def)) + return_val_if_reached (NULL); + } + + return defs; +} + +static node_asn * +lookup_def (p11_dict *asn1_defs, + const char *struct_name) +{ + int i; + + for (i = 0; asn1_tabs[i].tab != NULL; i++) { + if (strncmp (struct_name, asn1_tabs[i].prefix, asn1_tabs[i].prefix_len) == 0) + return p11_dict_get (asn1_defs, asn1_tabs[i].prefix); + } + + p11_debug_precond ("unknown prefix for element: %s\n", struct_name); + return NULL; +} + +node_asn * +p11_asn1_create (p11_dict *asn1_defs, + const char *struct_name) +{ + node_asn *def; + node_asn *asn; + int ret; + + return_val_if_fail (asn1_defs != NULL, NULL); + + def = lookup_def (asn1_defs, struct_name); + return_val_if_fail (def != NULL, NULL); + + ret = asn1_create_element (def, struct_name, &asn); + if (ret != ASN1_SUCCESS) { + p11_debug_precond ("failed to create element %s: %s\n", + struct_name, asn1_strerror (ret)); + return NULL; + } + + return asn; +} + +node_asn * +p11_asn1_decode (p11_dict *asn1_defs, + const char *struct_name, + const unsigned char *der, + size_t der_len, + char *message) +{ + char msg[ASN1_MAX_ERROR_DESCRIPTION_SIZE]; + node_asn *asn = NULL; + int ret; + + return_val_if_fail (asn1_defs != NULL, NULL); + + if (message == NULL) + message = msg; + + asn = p11_asn1_create (asn1_defs, struct_name); + return_val_if_fail (asn != NULL, NULL); + + /* asn1_der_decoding destroys the element if fails */ + ret = asn1_der_decoding (&asn, der, der_len, message); + + if (ret != ASN1_SUCCESS) { + p11_debug ("couldn't parse %s: %s: %s", + struct_name, asn1_strerror (ret), message); + return NULL; + } + + return asn; +} + +unsigned char * +p11_asn1_encode (node_asn *asn, + size_t *der_len) +{ + char message[ASN1_MAX_ERROR_DESCRIPTION_SIZE]; + struct asn1_data_node data = { NULL, }; + unsigned char *der; + int len; + int ret; + + return_val_if_fail (der_len != NULL, NULL); + + len = 0; + ret = asn1_der_coding (asn, "", NULL, &len, message); + return_val_if_fail (ret != ASN1_SUCCESS, NULL); + + if (ret == ASN1_MEM_ERROR) { + der = malloc (len); + return_val_if_fail (der != NULL, NULL); + + ret = asn1_der_coding (asn, "", der, &len, message); + } + + if (ret != ASN1_SUCCESS) { + asn1_read_node_value (asn, &data); + p11_debug_precond ("failed to encode %s: %s\n", + data.name ? data.name : "(unknown)", message); + return NULL; + } + + if (der_len) + *der_len = len; + return der; +} + +static int +atoin (const char *p, + int digits) +{ + int ret = 0, base = 1; + while(--digits >= 0) { + if (p[digits] < '0' || p[digits] > '9') + return -1; + ret += (p[digits] - '0') * base; + base *= 10; + } + return ret; +} + +static int +two_to_four_digit_year (int year) +{ + time_t now; + struct tm tm; + int century, current; + + return_val_if_fail (year >= 0 && year <= 99, -1); + + /* Get the current year */ + now = time (NULL); + return_val_if_fail (now >= 0, -1); + if (!gmtime_r (&now, &tm)) + return_val_if_reached (-1); + + current = (tm.tm_year % 100); + century = (tm.tm_year + 1900) - current; + + /* + * Check if it's within 40 years before the + * current date. + */ + if (current < 40) { + if (year < current) + return century + year; + if (year > 100 - (40 - current)) + return (century - 100) + year; + } else { + if (year < current && year > (current - 40)) + return century + year; + } + + /* + * If it's after then adjust for overflows to + * the next century. + */ + if (year < current) + return century + 100 + year; + else + return century + year; +} + +static int +parse_utc_time (const char *time, + size_t n_time, + struct tm *when, + int *offset) +{ + const char *p, *e; + int year; + + assert (when != NULL); + assert (time != NULL); + assert (offset != NULL); + + /* YYMMDDhhmmss.ffff Z | +0000 */ + if (n_time < 6 || n_time >= 28) + return 0; + + /* Reset everything to default legal values */ + memset (when, 0, sizeof (*when)); + *offset = 0; + when->tm_mday = 1; + + /* Select the digits part of it */ + p = time; + for (e = p; *e >= '0' && *e <= '9'; ++e); + + if (p + 2 <= e) { + year = atoin (p, 2); + p += 2; + + /* + * 40 years in the past is our century. 60 years + * in the future is the next century. + */ + when->tm_year = two_to_four_digit_year (year) - 1900; + } + if (p + 2 <= e) { + when->tm_mon = atoin (p, 2) - 1; + p += 2; + } + if (p + 2 <= e) { + when->tm_mday = atoin (p, 2); + p += 2; + } + if (p + 2 <= e) { + when->tm_hour = atoin (p, 2); + p += 2; + } + if (p + 2 <= e) { + when->tm_min = atoin (p, 2); + p += 2; + } + if (p + 2 <= e) { + when->tm_sec = atoin (p, 2); + p += 2; + } + + if (when->tm_year < 0 || when->tm_year > 9999 || + when->tm_mon < 0 || when->tm_mon > 11 || + when->tm_mday < 1 || when->tm_mday > 31 || + when->tm_hour < 0 || when->tm_hour > 23 || + when->tm_min < 0 || when->tm_min > 59 || + when->tm_sec < 0 || when->tm_sec > 59) + return 0; + + /* Make sure all that got parsed */ + if (p != e) + return 0; + + /* Now the remaining optional stuff */ + e = time + n_time; + + /* See if there's a fraction, and discard it if so */ + if (p < e && *p == '.' && p + 5 <= e) + p += 5; + + /* See if it's UTC */ + if (p < e && *p == 'Z') { + p += 1; + + /* See if it has a timezone */ + } else if ((*p == '-' || *p == '+') && p + 3 <= e) { + int off, neg; + + neg = *p == '-'; + ++p; + + off = atoin (p, 2) * 3600; + if (off < 0 || off > 86400) + return 0; + p += 2; + + if (p + 2 <= e) { + off += atoin (p, 2) * 60; + p += 2; + } + + /* Use TZ offset */ + if (neg) + *offset = 0 - off; + else + *offset = off; + } + + /* Make sure everything got parsed */ + if (p != e) + return 0; + + return 1; +} + +static int +parse_general_time (const char *time, + size_t n_time, + struct tm *when, + int *offset) +{ + const char *p, *e; + + assert (time != NULL); + assert (when != NULL); + assert (offset != NULL); + + /* YYYYMMDDhhmmss.ffff Z | +0000 */ + if (n_time < 8 || n_time >= 30) + return 0; + + /* Reset everything to default legal values */ + memset (when, 0, sizeof (*when)); + *offset = 0; + when->tm_mday = 1; + + /* Select the digits part of it */ + p = time; + for (e = p; *e >= '0' && *e <= '9'; ++e); + + if (p + 4 <= e) { + when->tm_year = atoin (p, 4) - 1900; + p += 4; + } + if (p + 2 <= e) { + when->tm_mon = atoin (p, 2) - 1; + p += 2; + } + if (p + 2 <= e) { + when->tm_mday = atoin (p, 2); + p += 2; + } + if (p + 2 <= e) { + when->tm_hour = atoin (p, 2); + p += 2; + } + if (p + 2 <= e) { + when->tm_min = atoin (p, 2); + p += 2; + } + if (p + 2 <= e) { + when->tm_sec = atoin (p, 2); + p += 2; + } + + if (when->tm_year < 0 || when->tm_year > 9999 || + when->tm_mon < 0 || when->tm_mon > 11 || + when->tm_mday < 1 || when->tm_mday > 31 || + when->tm_hour < 0 || when->tm_hour > 23 || + when->tm_min < 0 || when->tm_min > 59 || + when->tm_sec < 0 || when->tm_sec > 59) + return 0; + + /* Make sure all that got parsed */ + if (p != e) + return 0; + + /* Now the remaining optional stuff */ + e = time + n_time; + + /* See if there's a fraction, and discard it if so */ + if (p < e && *p == '.' && p + 5 <= e) + p += 5; + + /* See if it's UTC */ + if (p < e && *p == 'Z') { + p += 1; + + /* See if it has a timezone */ + } else if ((*p == '-' || *p == '+') && p + 3 <= e) { + int off, neg; + + neg = *p == '-'; + ++p; + + off = atoin (p, 2) * 3600; + if (off < 0 || off > 86400) + return 0; + p += 2; + + if (p + 2 <= e) { + off += atoin (p, 2) * 60; + p += 2; + } + + /* Use TZ offset */ + if (neg) + *offset = 0 - off; + else + *offset = off; + } + + /* Make sure everything got parsed */ + if (p != e) + return 0; + + return 1; +} + +static time_t +when_and_offset_to_time_t (struct tm *when, + int tz_offset) +{ + time_t timet; + + /* In order to work with 32 bit time_t. */ + if (sizeof (time_t) <= 4 && when->tm_year >= 2038) { + timet = (time_t)2145914603; /* 2037-12-31 23:23:23 */ + + /* Convert to seconds since epoch */ + } else { + timet = timegm (when); + return_val_if_fail (timet >= 0, -1); + timet += tz_offset; + } + + if (!gmtime_r (&timet, when)) + return_val_if_reached (-1); + + return timet; +} + +time_t +p11_asn1_parse_utc (const char *time_str, + struct tm *when) +{ + struct tm dummy; + int tz_offset; + int ret; + + if (!when) + when = &dummy; + + ret = parse_utc_time (time_str, strlen (time_str), + when, &tz_offset); + if (!ret) + return -1; + + return when_and_offset_to_time_t (when, tz_offset); +} + +time_t +p11_asn1_parse_general (const char *time_str, + struct tm *when) +{ + struct tm dummy; + int tz_offset; + int ret; + + if (!when) + when = &dummy; + + ret = parse_general_time (time_str, strlen (time_str), + when, &tz_offset); + if (!ret) + return -1; + + return when_and_offset_to_time_t (when, tz_offset); +} + +ssize_t +p11_asn1_tlv_length (const unsigned char *data, + size_t length) +{ + unsigned char cls; + int counter = 0; + int cb, len; + unsigned long tag; + + if (asn1_get_tag_der (data, length, &cls, &cb, &tag) == ASN1_SUCCESS) { + counter += cb; + len = asn1_get_length_der (data + cb, length - cb, &cb); + counter += cb; + if (len >= 0) { + len += counter; + if (length >= len) + return len; + } + } + + return -1; +} diff --git a/common/asn1.h b/common/asn1.h new file mode 100644 index 0000000..76a84ed --- /dev/null +++ b/common/asn1.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2012 Red Hat Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the + * above copyright notice, this list of conditions and + * the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * * The names of contributors to this software may not be + * used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * Author: Stef Walter <stefw@redhat.com> + */ + +#include <libtasn1.h> + +#include "dict.h" + +#ifndef P11_ASN1_H_ +#define P11_ASN1_H_ + +p11_dict * p11_asn1_defs_load (void); + +node_asn * p11_asn1_decode (p11_dict *asn1_defs, + const char *struct_name, + const unsigned char *der, + size_t der_len, + char *message); + +node_asn * p11_asn1_create (p11_dict *asn1_defs, + const char *struct_name); + +unsigned char * p11_asn1_encode (node_asn *asn, + size_t *der_len); + +time_t p11_asn1_parse_utc (const char *time_str, + struct tm *when); + +time_t p11_asn1_parse_general (const char *time_str, + struct tm *when); + +ssize_t p11_asn1_tlv_length (const unsigned char *data, + size_t length); + +#endif /* P11_ASN1_H_ */ diff --git a/common/oid.h b/common/oid.h index 181539a..a06054a 100644 --- a/common/oid.h +++ b/common/oid.h @@ -129,6 +129,7 @@ static const unsigned char P11_OID_OPENSSL_REJECT[] = */ static const unsigned char P11_OID_SERVER_AUTH[] = { 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01 }; +static const char P11_OID_SERVER_AUTH_STR[] = "1.3.6.1.5.5.7.3.1"; /* * 1.3.6.1.5.5.7.3.2: Client Auth @@ -137,6 +138,7 @@ static const unsigned char P11_OID_SERVER_AUTH[] = */ static const unsigned char P11_OID_CLIENT_AUTH[] = { 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02 }; +static const char P11_OID_CLIENT_AUTH_STR[] = "1.3.6.1.5.5.7.3.2"; /* * 1.3.6.1.5.5.7.3.3: Code Signing @@ -145,6 +147,7 @@ static const unsigned char P11_OID_CLIENT_AUTH[] = */ static const unsigned char P11_OID_CODE_SIGNING[] = { 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x03 }; +static const char P11_OID_CODE_SIGNING_STR[] = "1.3.6.1.5.5.7.3.3"; /* * 1.3.6.1.5.5.7.3.4: Email Protection @@ -153,6 +156,7 @@ static const unsigned char P11_OID_CODE_SIGNING[] = */ static const unsigned char P11_OID_EMAIL_PROTECTION[] = { 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x04 }; +static const char P11_OID_EMAIL_PROTECTION_STR[] = "1.3.6.1.5.5.7.3.4"; /* * 1.3.6.1.5.5.7.3.5: IPSec End System @@ -161,6 +165,7 @@ static const unsigned char P11_OID_EMAIL_PROTECTION[] = */ static const unsigned char P11_OID_IPSEC_END_SYSTEM[] = { 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x05 }; +static const char P11_OID_IPSEC_END_SYSTEM_STR[] = "1.3.6.1.5.5.7.3.5"; /* * 1.3.6.1.5.5.7.3.6: IPSec Tunnel @@ -169,6 +174,7 @@ static const unsigned char P11_OID_IPSEC_END_SYSTEM[] = */ static const unsigned char P11_OID_IPSEC_TUNNEL[] = { 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x06 }; +static const char P11_OID_IPSEC_TUNNEL_STR[] = "1.3.6.1.5.5.7.3.6"; /* * 1.3.6.1.5.5.7.3.7: IPSec User @@ -177,6 +183,7 @@ static const unsigned char P11_OID_IPSEC_TUNNEL[] = */ static const unsigned char P11_OID_IPSEC_USER[] = { 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x07 }; +static const char P11_OID_IPSEC_USER_STR[] = "1.3.6.1.5.5.7.3.7"; /* * 1.3.6.1.5.5.7.3.8: Time Stamping @@ -185,7 +192,7 @@ static const unsigned char P11_OID_IPSEC_USER[] = */ static const unsigned char P11_OID_TIME_STAMPING[] = { 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x08 }; - +static const char P11_OID_TIME_STAMPING_STR[] = "1.3.6.1.5.5.7.3.8"; /* * 1.3.6.1.4.1.3319.6.10.16: Reserved key purpose * @@ -202,7 +209,6 @@ static const unsigned char P11_OID_TIME_STAMPING[] = */ static const unsigned char P11_OID_RESERVED_PURPOSE[] = { 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x99, 0x77, 0x06, 0x0a, 0x10 }; -static const char P11_OID_RESERVED_PURPOSE_STR[] = - "1.3.6.1.4.1.3319.6.10.16"; +static const char P11_OID_RESERVED_PURPOSE_STR[] = "1.3.6.1.4.1.3319.6.10.16"; #endif diff --git a/common/tests/Makefile.am b/common/tests/Makefile.am index 94c6556..6d8d76e 100644 --- a/common/tests/Makefile.am +++ b/common/tests/Makefile.am @@ -34,9 +34,11 @@ LDADD += \ $(NULL) CHECK_PROGS += \ + test-asn1 \ test-checksum \ test-pem \ test-oid \ + test-x509 \ $(NULL) noinst_PROGRAMS += \ diff --git a/common/tests/test-asn1.c b/common/tests/test-asn1.c new file mode 100644 index 0000000..34e9129 --- /dev/null +++ b/common/tests/test-asn1.c @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2012 Red Hat Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the + * above copyright notice, this list of conditions and + * the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * * The names of contributors to this software may not be + * used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * Author: Stef Walter <stefw@gnome.org> + */ + +#include "config.h" +#include "CuTest.h" + +#include "asn1.h" +#include "debug.h" +#include "oid.h" +#include "x509.h" + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +struct { + p11_dict *asn1_defs; +} test; + +static void +setup (CuTest *cu) +{ + test.asn1_defs = p11_asn1_defs_load (); + CuAssertPtrNotNull (cu, test.asn1_defs); +} + +static void +teardown (CuTest *cu) +{ + p11_dict_free (test.asn1_defs); + memset (&test, 0, sizeof (test)); +} + +static void +test_tlv_length (CuTest *cu) +{ + struct { + const char *der; + size_t der_len; + int expected; + } tlv_lengths[] = { + { "\x01\x01\x00", 3, 3 }, + { "\x01\x01\x00\x01\x02", 5, 3 }, + { "\x01\x05\x00", 3, -1 }, + { NULL } + }; + + int length; + int i; + + setup (cu); + + for (i = 0; tlv_lengths[i].der != NULL; i++) { + length = p11_asn1_tlv_length ((const unsigned char *)tlv_lengths[i].der, tlv_lengths[i].der_len); + CuAssertIntEquals (cu, tlv_lengths[i].expected, length); + } + + teardown (cu); +} + +int +main (void) +{ + CuString *output = CuStringNew (); + CuSuite* suite = CuSuiteNew (); + int ret; + + setenv ("P11_KIT_STRICT", "1", 1); + p11_debug_init (); + + SUITE_ADD_TEST (suite, test_tlv_length); + + CuSuiteRun (suite); + CuSuiteSummary (suite, output); + CuSuiteDetails (suite, output); + printf ("%s\n", output->buffer); + ret = suite->failCount; + CuSuiteDelete (suite); + CuStringDelete (output); + + return ret; +} diff --git a/common/tests/test-x509.c b/common/tests/test-x509.c new file mode 100644 index 0000000..17e25bf --- /dev/null +++ b/common/tests/test-x509.c @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2012 Red Hat Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the + * above copyright notice, this list of conditions and + * the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * * The names of contributors to this software may not be + * used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * Author: Stef Walter <stefw@gnome.org> + */ + +#include "config.h" +#include "CuTest.h" + +#include "asn1.h" +#include "debug.h" +#include "oid.h" +#include "x509.h" + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +struct { + p11_dict *asn1_defs; +} test; + +static void +setup (CuTest *cu) +{ + test.asn1_defs = p11_asn1_defs_load (); + CuAssertPtrNotNull (cu, test.asn1_defs); +} + +static void +teardown (CuTest *cu) +{ + p11_dict_free (test.asn1_defs); + memset (&test, 0, sizeof (test)); +} + +static const char test_ku_ds_and_np[] = { + 0x03, 0x03, 0x07, 0xc0, 0x00, +}; + +static const char test_ku_none[] = { + 0x03, 0x03, 0x07, 0x00, 0x00, +}; + +static const char test_ku_cert_crl_sign[] = { + 0x03, 0x03, 0x07, 0x06, 0x00, +}; + +static const char test_eku_server_and_client[] = { + 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, 0x06, 0x08, 0x2b, 0x06, + 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, +}; + +static const char test_eku_none[] = { + 0x30, 0x00, +}; + +static const char test_eku_client_email_and_timestamp[] = { + 0x30, 0x1e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x06, 0x08, 0x2b, 0x06, + 0x01, 0x05, 0x05, 0x07, 0x03, 0x04, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x08, +}; + +struct { + const char *eku; + size_t length; + const char *expected[16]; +} extended_key_usage_fixtures[] = { + { test_eku_server_and_client, sizeof (test_eku_server_and_client), + { P11_OID_CLIENT_AUTH_STR, P11_OID_SERVER_AUTH_STR, NULL }, }, + { test_eku_none, sizeof (test_eku_none), + { NULL, }, }, + { test_eku_client_email_and_timestamp, sizeof (test_eku_client_email_and_timestamp), + { P11_OID_CLIENT_AUTH_STR, P11_OID_EMAIL_PROTECTION_STR, P11_OID_TIME_STAMPING_STR }, }, + { NULL }, +}; + +static void +test_parse_extended_key_usage (CuTest *cu) +{ + p11_dict *ekus; + int i, j; + + setup (cu); + + for (i = 0; extended_key_usage_fixtures[i].eku != NULL; i++) { + ekus = p11_x509_parse_extended_key_usage (test.asn1_defs, + (const unsigned char *)extended_key_usage_fixtures[i].eku, + extended_key_usage_fixtures[i].length); + CuAssertPtrNotNull (cu, ekus); + + for (j = 0; extended_key_usage_fixtures[i].expected[j] != NULL; j++) + CuAssertTrue (cu, p11_dict_get (ekus, extended_key_usage_fixtures[i].expected[j]) != NULL); + CuAssertIntEquals (cu, j, p11_dict_size (ekus)); + + p11_dict_free (ekus); + } + + teardown (cu); +} + +struct { + const char *ku; + size_t length; + unsigned int expected; +} key_usage_fixtures[] = { + { test_ku_ds_and_np, sizeof (test_ku_ds_and_np), P11_KU_DIGITAL_SIGNATURE | P11_KU_NON_REPUDIATION }, + { test_ku_none, sizeof (test_ku_none), 0 }, + { test_ku_cert_crl_sign, sizeof (test_ku_cert_crl_sign), P11_KU_KEY_CERT_SIGN | P11_KU_CRL_SIGN }, + { NULL }, +}; + +static void +test_parse_key_usage (CuTest *cu) +{ + unsigned int ku; + int i; + bool ret; + + setup (cu); + + for (i = 0; key_usage_fixtures[i].ku != NULL; i++) { + ku = 0; + + ret = p11_x509_parse_key_usage (test.asn1_defs, + (const unsigned char *)key_usage_fixtures[i].ku, + key_usage_fixtures[i].length, &ku); + CuAssertIntEquals (cu, true, ret); + + CuAssertIntEquals (cu, key_usage_fixtures[i].expected, ku); + } + + teardown (cu); +} + +int +main (void) +{ + CuString *output = CuStringNew (); + CuSuite* suite = CuSuiteNew (); + int ret; + + setenv ("P11_KIT_STRICT", "1", 1); + p11_debug_init (); + + SUITE_ADD_TEST (suite, test_parse_extended_key_usage); + SUITE_ADD_TEST (suite, test_parse_key_usage); + + CuSuiteRun (suite); + CuSuiteSummary (suite, output); + CuSuiteDetails (suite, output); + printf ("%s\n", output->buffer); + ret = suite->failCount; + CuSuiteDelete (suite); + CuStringDelete (output); + + return ret; +} diff --git a/common/x509.c b/common/x509.c new file mode 100644 index 0000000..8eb513d --- /dev/null +++ b/common/x509.c @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2012 Red Hat Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the + * above copyright notice, this list of conditions and + * the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * * The names of contributors to this software may not be + * used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * Author: Stef Walter <stefw@redhat.com> + */ + +#include "config.h" + +#include "asn1.h" +#define P11_DEBUG_FLAG P11_DEBUG_TRUST +#include "debug.h" +#include "oid.h" +#include "x509.h" + +#include <stdlib.h> +#include <string.h> + +bool +p11_x509_parse_basic_constraints (p11_dict *asn1_defs, + const unsigned char *ext_der, + size_t ext_len, + bool *is_ca) +{ + char buffer[8]; + node_asn *ext; + int ret; + int len; + + return_val_if_fail (is_ca != NULL, false); + + ext = p11_asn1_decode (asn1_defs, "PKIX1.BasicConstraints", ext_der, ext_len, NULL); + if (ext == NULL) + return false; + + len = sizeof (buffer); + ret = asn1_read_value (ext, "cA", buffer, &len); + return_val_if_fail (ret == ASN1_SUCCESS, false); + + *is_ca = (strcmp (buffer, "TRUE") == 0); + asn1_delete_structure (&ext); + + return true; +} + +bool +p11_x509_parse_key_usage (p11_dict *asn1_defs, + const unsigned char *ext_der, + size_t ext_len, + unsigned int *ku) +{ + char message[ASN1_MAX_ERROR_DESCRIPTION_SIZE] = { 0, }; + unsigned char buf[2]; + node_asn *ext; + int len; + int ret; + + ext = p11_asn1_decode (asn1_defs, "PKIX1.KeyUsage", ext_der, ext_len, message); + if (ext == NULL) + return false; + + len = sizeof (buf); + ret = asn1_read_value (ext, "", buf, &len); + return_val_if_fail (ret == ASN1_SUCCESS, false); + + /* A bit string, so combine into one set of flags */ + *ku = buf[0] | (buf[1] << 8); + + asn1_delete_structure (&ext); + + return true; +} + +p11_dict * +p11_x509_parse_extended_key_usage (p11_dict *asn1_defs, + const unsigned char *ext_der, + size_t ext_len) +{ + node_asn *asn; + char field[128]; + p11_dict *ekus; + char *eku; + int ret; + int len; + int i; + + asn = p11_asn1_decode (asn1_defs, "PKIX1.ExtKeyUsageSyntax", ext_der, ext_len, NULL); + if (asn == NULL) + return NULL; + + ekus = p11_dict_new (p11_dict_str_hash, p11_dict_str_equal, free, NULL); + + for (i = 1; ; i++) { + if (snprintf (field, sizeof (field), "?%u", i) < 0) + return_val_if_reached (NULL); + + len = 0; + ret = asn1_read_value (asn, field, NULL, &len); + if (ret == ASN1_ELEMENT_NOT_FOUND) + break; + + return_val_if_fail (ret == ASN1_MEM_ERROR, NULL); + + eku = malloc (len + 1); + return_val_if_fail (eku != NULL, NULL); + + ret = asn1_read_value (asn, field, eku, &len); + return_val_if_fail (ret == ASN1_SUCCESS, NULL); + + eku[len] = 0; + + /* If it's our reserved OID, then skip */ + if (strcmp (eku, P11_OID_RESERVED_PURPOSE_STR) == 0) { + free (eku); + continue; + } + + if (!p11_dict_set (ekus, eku, eku)) + return_val_if_reached (NULL); + } + + asn1_delete_structure (&asn); + + return ekus; +} diff --git a/common/x509.h b/common/x509.h new file mode 100644 index 0000000..b7c9a5e --- /dev/null +++ b/common/x509.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2012 Red Hat Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the + * above copyright notice, this list of conditions and + * the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * * The names of contributors to this software may not be + * used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * Author: Stef Walter <stefw@redhat.com> + */ + +#include <libtasn1.h> + +#include "dict.h" + +#ifndef P11_X509_H_ +#define P11_X509_H_ + +bool p11_x509_parse_basic_constraints (p11_dict *asn1_defs, + const unsigned char *ext_der, + size_t ext_len, + bool *is_ca); + +bool p11_x509_parse_key_usage (p11_dict *asn1_defs, + const unsigned char *data, + size_t length, + unsigned int *ku); + +p11_dict * p11_x509_parse_extended_key_usage (p11_dict *asn1_defs, + const unsigned char *ext_der, + size_t ext_len); + +#endif /* P11_X509_H_ */ diff --git a/trust/mozilla.c b/trust/mozilla.c index fd5e287..39b0b25 100644 --- a/trust/mozilla.c +++ b/trust/mozilla.c @@ -34,6 +34,7 @@ #include "config.h" +#include "asn1.h" #include "attrs.h" #include "checksum.h" #define P11_DEBUG_FLAG P11_DEBUG_TRUST @@ -43,6 +44,7 @@ #include "mozilla.h" #include "oid.h" #include "parser.h" +#include "x509.h" #include "pkcs11.h" #include "pkcs11x.h" @@ -57,6 +59,7 @@ update_ku (p11_parser *parser, CK_TRUST present) { unsigned char *data = NULL; + p11_dict *asn1_defs; unsigned int ku = 0; size_t length; CK_TRUST defawlt; @@ -92,7 +95,8 @@ update_ku (p11_parser *parser, */ defawlt = CKT_NETSCAPE_TRUST_UNKNOWN; - if (p11_parse_key_usage (parser, data, length, &ku) != P11_PARSE_SUCCESS) + asn1_defs = p11_parser_get_asn1_defs (parser); + if (!p11_x509_parse_key_usage (asn1_defs, data, length, &ku)) p11_message ("invalid key usage certificate extension"); free (data); } @@ -122,21 +126,22 @@ update_eku (p11_parser *parser, unsigned char *data = NULL; p11_dict *ekus = NULL; p11_dict *reject = NULL; + p11_dict *asn1_defs; size_t length; CK_ULONG i; struct { CK_ATTRIBUTE_TYPE type; - const unsigned char *eku; + const char *eku; } eku_attribute_map[] = { - { CKA_TRUST_SERVER_AUTH, P11_OID_SERVER_AUTH }, - { CKA_TRUST_CLIENT_AUTH, P11_OID_CLIENT_AUTH }, - { CKA_TRUST_CODE_SIGNING, P11_OID_CODE_SIGNING }, - { CKA_TRUST_EMAIL_PROTECTION, P11_OID_EMAIL_PROTECTION }, - { CKA_TRUST_IPSEC_END_SYSTEM, P11_OID_IPSEC_END_SYSTEM }, - { CKA_TRUST_IPSEC_TUNNEL, P11_OID_IPSEC_TUNNEL }, - { CKA_TRUST_IPSEC_USER, P11_OID_IPSEC_USER }, - { CKA_TRUST_TIME_STAMPING, P11_OID_TIME_STAMPING }, + { CKA_TRUST_SERVER_AUTH, P11_OID_SERVER_AUTH_STR }, + { CKA_TRUST_CLIENT_AUTH, P11_OID_CLIENT_AUTH_STR }, + { CKA_TRUST_CODE_SIGNING, P11_OID_CODE_SIGNING_STR }, + { CKA_TRUST_EMAIL_PROTECTION, P11_OID_EMAIL_PROTECTION_STR }, + { CKA_TRUST_IPSEC_END_SYSTEM, P11_OID_IPSEC_END_SYSTEM_STR }, + { CKA_TRUST_IPSEC_TUNNEL, P11_OID_IPSEC_TUNNEL_STR }, + { CKA_TRUST_IPSEC_USER, P11_OID_IPSEC_USER_STR }, + { CKA_TRUST_TIME_STAMPING, P11_OID_TIME_STAMPING_STR }, { CKA_INVALID }, }; @@ -161,7 +166,8 @@ update_eku (p11_parser *parser, */ defawlt = CKT_NETSCAPE_TRUST_UNKNOWN; - ekus = p11_parse_extended_key_usage (parser, data, length); + asn1_defs = p11_parser_get_asn1_defs (parser); + ekus = p11_x509_parse_extended_key_usage (asn1_defs, data, length); if (ekus == NULL) p11_message ("invalid extended key usage certificate extension"); free (data); @@ -169,7 +175,8 @@ update_eku (p11_parser *parser, data = p11_parsing_get_extension (parser, parsing, P11_OID_OPENSSL_REJECT, &length); if (data) { - reject = p11_parse_extended_key_usage (parser, data, length); + asn1_defs = p11_parser_get_asn1_defs (parser); + reject = p11_x509_parse_extended_key_usage (asn1_defs, data, length); if (reject == NULL) p11_message ("invalid reject key usage certificate extension"); free (data); diff --git a/trust/parser.c b/trust/parser.c index 28464fd..d33babb 100644 --- a/trust/parser.c +++ b/trust/parser.c @@ -35,6 +35,7 @@ #include "config.h" #include "array.h" +#include "asn1.h" #include "attrs.h" #include "checksum.h" #define P11_DEBUG_FLAG P11_DEBUG_TRUST @@ -47,6 +48,7 @@ #include "parser.h" #include "pem.h" #include "pkcs11x.h" +#include "x509.h" #include <libtasn1.h> @@ -61,12 +63,10 @@ #include <string.h> #include <unistd.h> -#include "openssl.asn.h" -#include "pkix.asn.h" - struct _p11_parser { - node_asn *pkix_definitions; - node_asn *openssl_definitions; + p11_dict *asn1_defs; + + /* Set during a parse */ p11_parser_sink sink; void *sink_data; const char *probable_label; @@ -83,53 +83,6 @@ typedef int (* parser_func) (p11_parser *parser, const unsigned char *data, size_t length); -static node_asn * -decode_asn1 (p11_parser *parser, - const char *struct_name, - const unsigned char *data, - size_t length, - char *message) -{ - char msg[ASN1_MAX_ERROR_DESCRIPTION_SIZE]; - node_asn *definitions; - node_asn *el = NULL; - int ret; - - if (message == NULL) - message = msg; - - if (strncmp (struct_name, "PKIX1.", 6) == 0) { - definitions = parser->pkix_definitions; - - } else if (strncmp (struct_name, "OPENSSL.", 8) == 0) { - definitions = parser->openssl_definitions; - - } else { - p11_debug_precond ("unknown prefix for element: %s", struct_name); - return NULL; - } - - ret = asn1_create_element (definitions, struct_name, &el); - if (ret != ASN1_SUCCESS) { - p11_debug_precond ("failed to create element %s at %s: %d", - struct_name, __func__, ret); - return NULL; - } - - return_val_if_fail (ret == ASN1_SUCCESS, NULL); - - /* asn1_der_decoding destroys the element if fails */ - ret = asn1_der_decoding (&el, data, length, message); - - if (ret != ASN1_SUCCESS) { - p11_debug ("couldn't parse %s: %s: %s", - struct_name, asn1_strerror (ret), message); - return NULL; - } - - return el; -} - static void begin_parsing (p11_parser *parser, node_asn *cert_asn, @@ -238,277 +191,6 @@ calc_check_value (const unsigned char *data, memcpy (check_value, checksum, 3); } -static int -atoin (const char *p, - int digits) -{ - int ret = 0, base = 1; - while(--digits >= 0) { - if (p[digits] < '0' || p[digits] > '9') - return -1; - ret += (p[digits] - '0') * base; - base *= 10; - } - return ret; -} - -static int -two_to_four_digit_year (int year) -{ - time_t now; - struct tm tm; - int century, current; - - return_val_if_fail (year >= 0 && year <= 99, -1); - - /* Get the current year */ - now = time (NULL); - return_val_if_fail (now >= 0, -1); - if (!gmtime_r (&now, &tm)) - return_val_if_reached (-1); - - current = (tm.tm_year % 100); - century = (tm.tm_year + 1900) - current; - - /* - * Check if it's within 40 years before the - * current date. - */ - if (current < 40) { - if (year < current) - return century + year; - if (year > 100 - (40 - current)) - return (century - 100) + year; - } else { - if (year < current && year > (current - 40)) - return century + year; - } - - /* - * If it's after then adjust for overflows to - * the next century. - */ - if (year < current) - return century + 100 + year; - else - return century + year; -} - -static bool -parse_utc_time (const char *time, - size_t n_time, - struct tm *when, - int *offset) -{ - const char *p, *e; - int year; - - assert (when != NULL); - assert (time != NULL); - assert (offset != NULL); - - /* YYMMDDhhmmss.ffff Z | +0000 */ - if (n_time < 6 || n_time >= 28) - return false; - - /* Reset everything to default legal values */ - memset (when, 0, sizeof (*when)); - *offset = 0; - when->tm_mday = 1; - - /* Select the digits part of it */ - p = time; - for (e = p; *e >= '0' && *e <= '9'; ++e); - - if (p + 2 <= e) { - year = atoin (p, 2); - p += 2; - - /* - * 40 years in the past is our century. 60 years - * in the future is the next century. - */ - when->tm_year = two_to_four_digit_year (year) - 1900; - } - if (p + 2 <= e) { - when->tm_mon = atoin (p, 2) - 1; - p += 2; - } - if (p + 2 <= e) { - when->tm_mday = atoin (p, 2); - p += 2; - } - if (p + 2 <= e) { - when->tm_hour = atoin (p, 2); - p += 2; - } - if (p + 2 <= e) { - when->tm_min = atoin (p, 2); - p += 2; - } - if (p + 2 <= e) { - when->tm_sec = atoin (p, 2); - p += 2; - } - - if (when->tm_year < 0 || when->tm_year > 9999 || - when->tm_mon < 0 || when->tm_mon > 11 || - when->tm_mday < 1 || when->tm_mday > 31 || - when->tm_hour < 0 || when->tm_hour > 23 || - when->tm_min < 0 || when->tm_min > 59 || - when->tm_sec < 0 || when->tm_sec > 59) - return false; - - /* Make sure all that got parsed */ - if (p != e) - return false; - - /* Now the remaining optional stuff */ - e = time + n_time; - - /* See if there's a fraction, and discard it if so */ - if (p < e && *p == '.' && p + 5 <= e) - p += 5; - - /* See if it's UTC */ - if (p < e && *p == 'Z') { - p += 1; - - /* See if it has a timezone */ - } else if ((*p == '-' || *p == '+') && p + 3 <= e) { - int off, neg; - - neg = *p == '-'; - ++p; - - off = atoin (p, 2) * 3600; - if (off < 0 || off > 86400) - return false; - p += 2; - - if (p + 2 <= e) { - off += atoin (p, 2) * 60; - p += 2; - } - - /* Use TZ offset */ - if (neg) - *offset = 0 - off; - else - *offset = off; - } - - /* Make sure everything got parsed */ - if (p != e) - return false; - - return true; -} - -static bool -parse_general_time (const char *time, - size_t n_time, - struct tm *when, - int *offset) -{ - const char *p, *e; - - assert (time != NULL); - assert (when != NULL); - assert (offset != NULL); - - /* YYYYMMDDhhmmss.ffff Z | +0000 */ - if (n_time < 8 || n_time >= 30) - return false; - - /* Reset everything to default legal values */ - memset (when, 0, sizeof (*when)); - *offset = 0; - when->tm_mday = 1; - - /* Select the digits part of it */ - p = time; - for (e = p; *e >= '0' && *e <= '9'; ++e); - - if (p + 4 <= e) { - when->tm_year = atoin (p, 4) - 1900; - p += 4; - } - if (p + 2 <= e) { - when->tm_mon = atoin (p, 2) - 1; - p += 2; - } - if (p + 2 <= e) { - when->tm_mday = atoin (p, 2); - p += 2; - } - if (p + 2 <= e) { - when->tm_hour = atoin (p, 2); - p += 2; - } - if (p + 2 <= e) { - when->tm_min = atoin (p, 2); - p += 2; - } - if (p + 2 <= e) { - when->tm_sec = atoin (p, 2); - p += 2; - } - - if (when->tm_year < 0 || when->tm_year > 9999 || - when->tm_mon < 0 || when->tm_mon > 11 || - when->tm_mday < 1 || when->tm_mday > 31 || - when->tm_hour < 0 || when->tm_hour > 23 || - when->tm_min < 0 || when->tm_min > 59 || - when->tm_sec < 0 || when->tm_sec > 59) - return false; - - /* Make sure all that got parsed */ - if (p != e) - return false; - - /* Now the remaining optional stuff */ - e = time + n_time; - - /* See if there's a fraction, and discard it if so */ - if (p < e && *p == '.' && p + 5 <= e) - p += 5; - - /* See if it's UTC */ - if (p < e && *p == 'Z') { - p += 1; - - /* See if it has a timezone */ - } else if ((*p == '-' || *p == '+') && p + 3 <= e) { - int off, neg; - - neg = *p == '-'; - ++p; - - off = atoin (p, 2) * 3600; - if (off < 0 || off > 86400) - return false; - p += 2; - - if (p + 2 <= e) { - off += atoin (p, 2) * 60; - p += 2; - } - - /* Use TZ offset */ - if (neg) - *offset = 0 - off; - else - *offset = off; - } - - /* Make sure everything got parsed */ - if (p != e) - return false; - - return true; -} - static bool calc_date (node_asn *cert, const char *field, @@ -516,7 +198,6 @@ calc_date (node_asn *cert, { node_asn *choice; struct tm when; - int tz_offset; char buf[64]; time_t timet; char *sub; @@ -536,15 +217,15 @@ calc_date (node_asn *cert, len = sizeof (buf) - 1; ret = asn1_read_value (cert, sub, buf, &len); return_val_if_fail (ret == ASN1_SUCCESS, false); - if (!parse_general_time (buf, len, &when, &tz_offset)) - return_val_if_reached (false); + timet = p11_asn1_parse_general (buf, &when); + return_val_if_fail (timet >= 0, false); } else if (strcmp (buf, "utcTime") == 0) { len = sizeof (buf) - 1; ret = asn1_read_value (cert, sub, buf, &len); return_val_if_fail (ret == ASN1_SUCCESS, false); - if (!parse_utc_time (buf, len - 1, &when, &tz_offset)) - return_val_if_reached (false); + timet = p11_asn1_parse_utc (buf, &when); + return_val_if_fail (timet >= 0, false); } else { return_val_if_reached (false); @@ -552,20 +233,6 @@ calc_date (node_asn *cert, free (sub); - /* In order to work with 32 bit time_t. */ - if (sizeof (time_t) <= 4 && when.tm_year >= 2038) { - timet = (time_t)2145914603; /* 2037-12-31 23:23:23 */ - - /* Convert to seconds since epoch */ - } else { - timet = timegm (&when); - return_val_if_fail (timet >= 0, false); - timet += tz_offset; - } - - if (!gmtime_r (&timet, &when)) - return_val_if_reached (false); - assert (sizeof (date->year) == 4); snprintf ((char *)buf, 5, "%04d", 1900 + when.tm_year); memcpy (date->year, buf, 4); @@ -798,110 +465,6 @@ p11_parsing_get_certificate (p11_parser *parser, return match_parsing_object (parser, match); } -int -p11_parse_basic_constraints (p11_parser *parser, - const unsigned char *data, - size_t length, - int *is_ca) -{ - char buffer[8]; - node_asn *ext; - int ret; - int len; - - return_val_if_fail (is_ca != NULL, P11_PARSE_FAILURE); - - ext = decode_asn1 (parser, "PKIX1.BasicConstraints", data, length, NULL); - return_val_if_fail (ext != NULL, P11_PARSE_UNRECOGNIZED); - - len = sizeof (buffer); - ret = asn1_read_value (ext, "cA", buffer, &len); - return_val_if_fail (ret == ASN1_SUCCESS, P11_PARSE_FAILURE); - - *is_ca = (strcmp (buffer, "TRUE") == 0); - asn1_delete_structure (&ext); - - return P11_PARSE_SUCCESS; -} - -int -p11_parse_key_usage (p11_parser *parser, - const unsigned char *data, - size_t length, - unsigned int *ku) -{ - char message[ASN1_MAX_ERROR_DESCRIPTION_SIZE] = { 0, }; - unsigned char buf[2]; - node_asn *ext; - int len; - int ret; - - ext = decode_asn1 (parser, "PKIX1.KeyUsage", data, length, message); - if (ext == NULL) - return P11_PARSE_UNRECOGNIZED; - - len = sizeof (buf); - ret = asn1_read_value (ext, "", buf, &len); - return_val_if_fail (ret == ASN1_SUCCESS, P11_PARSE_FAILURE); - - /* A bit string, so combine into one set of flags */ - *ku = buf[0] | (buf[1] << 8); - - asn1_delete_structure (&ext); - - return P11_PARSE_SUCCESS; -} - -p11_dict * -p11_parse_extended_key_usage (p11_parser *parser, - const unsigned char *eku_der, - size_t eku_len) -{ - node_asn *ext; - char field[128]; - unsigned char *eku; - p11_dict *ekus; - int start; - int end; - int ret; - int i; - - ext = decode_asn1 (parser, "PKIX1.ExtKeyUsageSyntax", eku_der, eku_len, NULL); - if (ext == NULL) - return NULL; - - ekus = p11_dict_new (p11_oid_hash, p11_oid_equal, free, NULL); - - for (i = 1; ; i++) { - if (snprintf (field, sizeof (field), "?%u", i) < 0) - return_val_if_reached (NULL); - - ret = asn1_der_decoding_startEnd (ext, eku_der, eku_len, field, &start, &end); - if (ret == ASN1_ELEMENT_NOT_FOUND) - break; - - return_val_if_fail (ret == ASN1_SUCCESS, NULL); - - /* Make sure it's a simple OID with certain assumptions */ - if (!p11_oid_simple (eku_der + start, (end - start) + 1)) - continue; - - /* If it's our reserved OID, then skip */ - if (p11_oid_equal (eku_der + start, P11_OID_RESERVED_PURPOSE)) - continue; - - eku = memdup (eku_der + start, (end - start) + 1); - return_val_if_fail (eku != NULL, NULL); - - if (!p11_dict_set (ekus, eku, eku)) - return_val_if_reached (NULL); - } - - asn1_delete_structure (&ext); - - return ekus; -} - static int parse_der_x509_certificate (p11_parser *parser, const unsigned char *data, @@ -911,7 +474,7 @@ parse_der_x509_certificate (p11_parser *parser, CK_ATTRIBUTE *attrs; node_asn *cert; - cert = decode_asn1 (parser, "PKIX1.Certificate", data, length, NULL); + cert = p11_asn1_decode (parser->asn1_defs, "PKIX1.Certificate", data, length, NULL); if (cert == NULL) return P11_PARSE_UNRECOGNIZED; @@ -928,29 +491,6 @@ parse_der_x509_certificate (p11_parser *parser, return P11_PARSE_SUCCESS; } -static ssize_t -calc_der_length (const unsigned char *data, - size_t length) -{ - unsigned char cls; - int counter = 0; - int cb, len; - unsigned long tag; - - if (asn1_get_tag_der (data, length, &cls, &cb, &tag) == ASN1_SUCCESS) { - counter += cb; - len = asn1_get_length_der (data + cb, length - cb, &cb); - counter += cb; - if (len >= 0) { - len += counter; - if (length >= len) - return len; - } - } - - return -1; -} - static int build_der_extension (p11_parser *parser, CK_ATTRIBUTE *cert, @@ -994,21 +534,13 @@ build_stapled_extension (p11_parser *parser, CK_BBOOL critical, node_asn *ext) { - char message[ASN1_MAX_ERROR_DESCRIPTION_SIZE]; - char *der; - int len; + unsigned char *der; + size_t len; int ret; - len = 0; - ret = asn1_der_coding (ext, "", NULL, &len, message); - return_val_if_fail (ret == ASN1_MEM_ERROR, P11_PARSE_FAILURE); - - der = malloc (len); + der = p11_asn1_encode (ext, &len); return_val_if_fail (der != NULL, P11_PARSE_FAILURE); - ret = asn1_der_coding (ext, "", der, &len, message); - return_val_if_fail (ret == ASN1_SUCCESS, P11_PARSE_FAILURE); - ret = build_der_extension (parser, cert, oid, critical, (unsigned char *)der, len); free (der); @@ -1065,8 +597,8 @@ build_eku_extension (p11_parser *parser, void *value; int ret; - ret = asn1_create_element (parser->pkix_definitions, "PKIX1.ExtKeyUsageSyntax", &dest); - return_val_if_fail (ret == ASN1_SUCCESS, P11_PARSE_FAILURE); + dest = p11_asn1_create (parser->asn1_defs, "PKIX1.ExtKeyUsageSyntax"); + return_val_if_fail (dest != NULL, P11_PARSE_FAILURE); p11_dict_iterate (oid_strs, &iter); while (p11_dict_next (&iter, NULL, &value)) { @@ -1116,8 +648,8 @@ build_bc_extension (p11_parser *parser, node_asn *ext; int ret; - ret = asn1_create_element (parser->pkix_definitions, "PKIX1.BasicConstraints", &ext); - return_val_if_fail (ret == ASN1_SUCCESS, P11_PARSE_FAILURE); + ext = p11_asn1_create (parser->asn1_defs, "PKIX1.BasicConstraints"); + return_val_if_fail (ext != NULL, P11_PARSE_FAILURE); /* FALSE is the default, so clear if not CA */ ret = asn1_write_value (ext, "cA", is_ca ? "TRUE" : NULL, is_ca ? -1 : 0); @@ -1133,7 +665,7 @@ build_bc_extension (p11_parser *parser, return ret; } -static int +static bool is_v1_x509_authority (CK_ATTRIBUTE *cert, node_asn *node) { @@ -1153,14 +685,14 @@ is_v1_x509_authority (CK_ATTRIBUTE *cert, len = 1; } - return_val_if_fail (ret == ASN1_SUCCESS, 0); + return_val_if_fail (ret == ASN1_SUCCESS, false); /* * In X.509 version v1 is the integer zero. Two's complement * integer, but zero is easy to read. */ if (len != 1 || buffer[0] != 0) - return 0; + return false; /* Must be self-signed, ie: same subject and issuer */ subject = p11_attrs_find (cert, CKA_SUBJECT); @@ -1174,7 +706,7 @@ update_category (p11_parser *parser, CK_ATTRIBUTE *cert) { CK_ATTRIBUTE *category; - int is_ca = 0; + bool is_ca = 0; unsigned char *data; size_t length; int ret; @@ -1182,7 +714,7 @@ update_category (p11_parser *parser, /* See if we have a basic constraints extension */ data = p11_parsing_get_extension (parser, parser->parsing, P11_OID_BASIC_CONSTRAINTS, &length); if (data) { - if (!p11_parse_basic_constraints (parser, data, length, &is_ca)) + if (!p11_x509_parse_basic_constraints (parser->asn1_defs, data, length, &is_ca)) p11_message ("invalid basic constraints certificate extension"); free (data); @@ -1242,7 +774,7 @@ update_trust_and_distrust (p11_parser *parser, /* See if we have a basic constraints extension */ data = p11_parsing_get_extension (parser, parser->parsing, P11_OID_EXTENDED_KEY_USAGE, &length); if (data) { - ekus = p11_parse_extended_key_usage (parser, data, length); + ekus = p11_x509_parse_extended_key_usage (parser->asn1_defs, data, length); if (ekus == NULL) p11_message ("invalid extendend key usage certificate extension"); else if (p11_dict_size (ekus) == 0) { @@ -1393,15 +925,15 @@ parse_openssl_trusted_certificate (p11_parser *parser, * the X.509 certificate. */ - cert_len = calc_der_length (data, length); + cert_len = p11_asn1_tlv_length (data, length); if (cert_len <= 0) return P11_PARSE_UNRECOGNIZED; - cert = decode_asn1 (parser, "PKIX1.Certificate", data, cert_len, NULL); + cert = p11_asn1_decode (parser->asn1_defs, "PKIX1.Certificate", data, cert_len, NULL); if (cert == NULL) return P11_PARSE_UNRECOGNIZED; - aux = decode_asn1 (parser, "OPENSSL.CertAux", data + cert_len, length - cert_len, NULL); + aux = p11_asn1_decode (parser->asn1_defs, "OPENSSL.CertAux", data + cert_len, length - cert_len, NULL); if (aux == NULL) { asn1_delete_structure (&cert); return P11_PARSE_UNRECOGNIZED; @@ -1493,23 +1025,10 @@ static parser_func all_parsers[] = { p11_parser * p11_parser_new (void) { - char message[ASN1_MAX_ERROR_DESCRIPTION_SIZE] = { 0, }; p11_parser parser = { 0, }; - int ret; - ret = asn1_array2tree (pkix_asn1_tab, &parser.pkix_definitions, message); - if (ret != ASN1_SUCCESS) { - p11_debug_precond ("failed to load pkix_asn1_tab in %s: %d %s", - __func__, ret, message); - return NULL; - } - - ret = asn1_array2tree (openssl_asn1_tab, &parser.openssl_definitions, message); - if (ret != ASN1_SUCCESS) { - p11_debug_precond ("failed to load openssl_asn1_tab in %s: %d %s", - __func__, ret, message); - return NULL; - } + parser.asn1_defs = p11_asn1_defs_load (); + return_val_if_fail (parser.asn1_defs != NULL, NULL); return memdup (&parser, sizeof (parser)); } @@ -1520,7 +1039,7 @@ p11_parser_free (p11_parser *parser) if (!parser) return; - asn1_delete_structure (&parser->pkix_definitions); + p11_dict_free (parser->asn1_defs); free (parser); } @@ -1602,3 +1121,10 @@ p11_parse_file (p11_parser *parser, return ret; } + +p11_dict * +p11_parser_get_asn1_defs (p11_parser *parser) +{ + return_val_if_fail (parser != NULL, NULL); + return parser->asn1_defs; +} diff --git a/trust/parser.h b/trust/parser.h index da19bce..201c2c3 100644 --- a/trust/parser.h +++ b/trust/parser.h @@ -76,19 +76,7 @@ int p11_parse_file (p11_parser *parser, p11_parser_sink sink, void *sink_data); -int p11_parse_basic_constraints (p11_parser *parser, - const unsigned char *data, - size_t length, - int *is_ca); - -int p11_parse_key_usage (p11_parser *parser, - const unsigned char *data, - size_t length, - unsigned int *ku); - -p11_dict * p11_parse_extended_key_usage (p11_parser *parser, - const unsigned char *data, - size_t length); +p11_dict * p11_parser_get_asn1_defs (p11_parser *parser); /* Functions used for retrieving parsing information */ diff --git a/trust/tests/test-data.h b/trust/tests/test-data.h index 300e342..b07830d 100644 --- a/trust/tests/test-data.h +++ b/trust/tests/test-data.h @@ -32,6 +32,8 @@ * Author: Stef Walter <stefw@gnome.org> */ +#include "pkcs11.h" + #include <sys/types.h> #ifndef TEST_DATA_H_ @@ -228,30 +230,4 @@ static const char test_cacert3_ca_serial[] = { 0x02, 0x03, 0x0a, 0x41, 0x8a, }; -static const char test_ku_ds_and_np[] = { - 0x03, 0x03, 0x07, 0xc0, 0x00, -}; - -static const char test_ku_none[] = { - 0x03, 0x03, 0x07, 0x00, 0x00, -}; - -static const char test_ku_cert_crl_sign[] = { - 0x03, 0x03, 0x07, 0x06, 0x00, -}; - -static const char test_eku_server_and_client[] = { - 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, 0x06, 0x08, 0x2b, 0x06, - 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, -}; - -static const char test_eku_none[] = { - 0x30, 0x00, -}; - -static const char test_eku_client_email_and_timestamp[] = { - 0x30, 0x1e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x06, 0x08, 0x2b, 0x06, - 0x01, 0x05, 0x05, 0x07, 0x03, 0x04, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x08, -}; - #endif /* TEST_DATA_H_ */ diff --git a/trust/tests/test-parser.c b/trust/tests/test-parser.c index 5bb690a..80f0f07 100644 --- a/trust/tests/test-parser.c +++ b/trust/tests/test-parser.c @@ -518,105 +518,6 @@ test_parse_unrecognized (CuTest *cu) teardown (cu); } -struct { - const char *eku; - size_t length; - const unsigned char *expected[16]; -} extended_key_usage_fixtures[] = { - { test_eku_server_and_client, sizeof (test_eku_server_and_client), - { P11_OID_CLIENT_AUTH, P11_OID_SERVER_AUTH, NULL }, }, - { test_eku_none, sizeof (test_eku_none), - { NULL, }, }, - { test_eku_client_email_and_timestamp, sizeof (test_eku_client_email_and_timestamp), - { P11_OID_CLIENT_AUTH, P11_OID_EMAIL_PROTECTION, P11_OID_TIME_STAMPING }, }, - { NULL }, -}; - -static void -test_parse_extended_key_usage (CuTest *cu) -{ - p11_dict *ekus; - int i, j; - - setup (cu); - - for (i = 0; extended_key_usage_fixtures[i].eku != NULL; i++) { - ekus = p11_parse_extended_key_usage (test.parser, - (const unsigned char *)extended_key_usage_fixtures[i].eku, - extended_key_usage_fixtures[i].length); - CuAssertPtrNotNull (cu, ekus); - - for (j = 0; extended_key_usage_fixtures[i].expected[j] != NULL; j++) - CuAssertTrue (cu, p11_dict_get (ekus, extended_key_usage_fixtures[i].expected[j]) != NULL); - CuAssertIntEquals (cu, j, p11_dict_size (ekus)); - - p11_dict_free (ekus); - } - - teardown (cu); -} - -static void -test_bad_extended_key_usage (CuTest *cu) -{ - p11_dict *ekus; - - setup (cu); - - ekus = p11_parse_extended_key_usage (test.parser, (const unsigned char *)"blah", 4); - CuAssertPtrEquals (cu, NULL, ekus); - - teardown (cu); -} - -struct { - const char *ku; - size_t length; - unsigned int expected; -} key_usage_fixtures[] = { - { test_ku_ds_and_np, sizeof (test_ku_ds_and_np), P11_KU_DIGITAL_SIGNATURE | P11_KU_NON_REPUDIATION }, - { test_ku_none, sizeof (test_ku_none), 0 }, - { test_ku_cert_crl_sign, sizeof (test_ku_cert_crl_sign), P11_KU_KEY_CERT_SIGN | P11_KU_CRL_SIGN }, - { NULL }, -}; - -static void -test_parse_key_usage (CuTest *cu) -{ - unsigned int ku; - int i; - int ret; - - setup (cu); - - for (i = 0; key_usage_fixtures[i].ku != NULL; i++) { - ku = 0; - - ret = p11_parse_key_usage (test.parser, - (const unsigned char *)key_usage_fixtures[i].ku, - key_usage_fixtures[i].length, &ku); - CuAssertIntEquals (cu, P11_PARSE_SUCCESS, ret); - - CuAssertIntEquals (cu, key_usage_fixtures[i].expected, ku); - } - - teardown (cu); -} - -static void -test_bad_key_usage (CuTest *cu) -{ - unsigned int ku; - int ret; - - setup (cu); - - ret = p11_parse_key_usage (test.parser, (const unsigned char *)"blah", 4, &ku); - CuAssertIntEquals (cu, P11_PARSE_UNRECOGNIZED, ret); - - teardown (cu); -} - int main (void) { @@ -628,10 +529,6 @@ main (void) p11_debug_init (); p11_message_quiet (); - SUITE_ADD_TEST (suite, test_bad_extended_key_usage); - SUITE_ADD_TEST (suite, test_parse_extended_key_usage); - SUITE_ADD_TEST (suite, test_bad_key_usage); - SUITE_ADD_TEST (suite, test_parse_key_usage); SUITE_ADD_TEST (suite, test_parse_der_certificate); SUITE_ADD_TEST (suite, test_parse_pem_certificate); SUITE_ADD_TEST (suite, test_parse_openssl_trusted); |