diff --git a/src/headers/tomcrypt_macros.h b/src/headers/tomcrypt_macros.h index 16e37054..ce74c659 100644 --- a/src/headers/tomcrypt_macros.h +++ b/src/headers/tomcrypt_macros.h @@ -6,6 +6,8 @@ #define LTC_TMPVAR_(n, l) LTC_TMPVAR__(n, l) #define LTC_TMPVAR(n) LTC_TMPVAR_(LTC_ ## n ## _, __LINE__) +#define LTC_BIT(n) (1u << (n)) + /* ---- HELPER MACROS ---- */ #ifdef ENDIAN_NEUTRAL diff --git a/src/headers/tomcrypt_pk.h b/src/headers/tomcrypt_pk.h index 5fee1a2b..9acfe970 100644 --- a/src/headers/tomcrypt_pk.h +++ b/src/headers/tomcrypt_pk.h @@ -1053,4 +1053,248 @@ int der_decode_generalizedtime(const unsigned char *in, unsigned long *inlen, int der_length_generalizedtime(const ltc_generalizedtime *gtime, unsigned long *outlen); +/* X.509 specific enums, structs and APIs */ + +typedef enum ltc_x509_details { + /* The Serial is an integer, but we provide it as a hex string. */ + LTC_X509_SERIAL = 0, + /* The most commonly used elements in an X.509 Name. + * These ones will be converted to a UTF-8 String and + * stored inside the `str` pointer of the `ltc_x509_string`. + */ + /* CommonName */ + LTC_X509_CN, + /* Country Code */ + LTC_X509_C, + /* Locality */ + LTC_X509_L, + /* State or Province */ + LTC_X509_ST, + /* Organisation */ + LTC_X509_O, + /* OrganisationalUnit */ + LTC_X509_OU, + /* EmailAddress */ + LTC_X509_EMAIL, + + /* GeneralName subtypes + * GeneralName ::= CHOICE { + * otherName [0] OtherName, + * rfc822Name [1] IA5String, + * dNSName [2] IA5String, + * x400Address [3] ORAddress, + * directoryName [4] Name, + * ediPartyName [5] EDIPartyName, + * uniformResourceIdentifier [6] IA5String, + * iPAddress [7] OCTET STRING, + * registeredID [8] OBJECT IDENTIFIER } + */ + LTC_X509_OTHER_NAME = 30, + LTC_X509_RFC822_NAME, + LTC_X509_DNS_NAME, + LTC_X509_X400_ADDRESS, + LTC_X509_DIRECTORY_NAME, + LTC_X509_EDI_PARTY_NAME, + LTC_X509_UNIFORM_RESOURCE_IDENTIFIER, + LTC_X509_IP_ADDRESS, + LTC_X509_REGISTERED_ID, + + /* Value was encoded as an OCTET STRING */ + LTC_X509_OCTET_STRING, + + /* The most commonly used X.509 Certificate Extension. */ + /* AuthorityKeyIdentifier */ + LTC_X509_CE_AUTHORITY_KEY_ID = 100, + /* SubjectKeyIdentifier */ + LTC_X509_CE_SUBJECT_KEY_ID, + /* KeyUsage */ + LTC_X509_CE_KEY_USAGE, + /* SubjectAltName */ + LTC_X509_CE_SUBJECT_ALT_NAME, + /* BasicConstraints */ + LTC_X509_CE_BASIC_CONSTRAINTS, + /* ExtendedKeyUsage */ + LTC_X509_CE_EXT_KEY_USAGE, + + /* The rest will not be decoded and has to be treated + * manually through the `asn1` pointer of the struct. + */ + LTC_X509_UNKNOWN = 0x7fff, +} ltc_x509_details; + +typedef struct ltc_x509_string { + ltc_x509_details type; + const char *str; + const ltc_asn1_list *asn1; +} ltc_x509_string; + +typedef struct ltc_x509_name { + const ltc_x509_string *names; + unsigned long names_num; + const ltc_asn1_list *asn1; +} ltc_x509_name; + +typedef struct ltc_x509_time { + union { + ltc_utctime *utc; + ltc_generalizedtime *generalized; + } u; + int utc; + const char *str; + const ltc_asn1_list *asn1; +} ltc_x509_time; + +typedef struct ltc_x509_signature_algorithm { + enum ltc_pka_id pka; + union { + const char *hash; + ltc_rsa_parameters rsa_params; + } u; + const ltc_asn1_list *asn1; +} ltc_x509_signature_algorithm; + +typedef struct ltc_x509_validity { + ltc_x509_time not_before, not_after; +} ltc_x509_validity; + +/* + * Certificate Extensions + */ + +/* KeyUsage ::= BIT STRING */ +typedef enum ltc_x509_ce_key_usage { + /* digitalSignature (0) */ + LTC_KU_DS = LTC_BIT(0), + /* contentCommitment (1) */ + LTC_KU_CC = LTC_BIT(1), + /* keyEncipherment (2) */ + LTC_KU_KE = LTC_BIT(2), + /* dataEncipherment (3) */ + LTC_KU_DE = LTC_BIT(3), + /* keyAgreement (4) */ + LTC_KU_KA = LTC_BIT(4), + /* keyCertSign (5) */ + LTC_KU_KCS = LTC_BIT(5), + /* cRLSign (6) */ + LTC_KU_CRLS = LTC_BIT(6), + /* encipherOnly (7) */ + LTC_KU_EO = LTC_BIT(7), + /* decipherOnly (8) */ + LTC_KU_DO = LTC_BIT(8), +} ltc_x509_ce_key_usage; + +/* ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId + * KeyPurposeId ::= OBJECT IDENTIFIER + */ +typedef enum ltc_x509_ce_ext_key_usage { + /* anyExtendedKeyUsage */ + LTC_EKU_ANY = LTC_BIT(0), + /* serverAuth */ + LTC_EKU_SA = LTC_BIT(1), + /* clientAuth */ + LTC_EKU_CA = LTC_BIT(2), + /* codeSigning */ + LTC_EKU_CS = LTC_BIT(3), + /* emailProtection */ + LTC_EKU_EP = LTC_BIT(4), + /* timeStamping */ + LTC_EKU_TS = LTC_BIT(5), + /* OCSPSigning */ + LTC_EKU_OS = LTC_BIT(6), +} ltc_x509_ce_ext_key_usage; + +typedef struct ltc_x509_extension { + ltc_x509_details type; + const ltc_asn1_list *oid; + int critical; + union { + /* .type = LTC_X509_AUTHORITY_KEY_ID */ + struct { + ltc_x509_string key_identifier; + ltc_x509_string authority_cert_issuer; + ltc_x509_string authority_cert_serial_number; + } authority_key_id; + /* .type = LTC_X509_SUBJECT_KEY_ID */ + ltc_x509_string subject_key_identifier; + /* .type = LTC_X509_KEY_USAGE + * Bitmask of `enum ltc_x509_ce_key_usage` + */ + ulong32 key_usage; + /* .type = LTC_X509_SUBJECT_ALT_NAME */ + ltc_x509_name subject_alt_name; + /* .type = LTC_X509_BASIC_CONSTRAINTS */ + struct { + int ca; + /* pathLenConstraint is marked as OPTIONAL: + * -1 -> value missing + * 0..INT_MAX -> valid values + */ + int path_len; + } basic_constraints; + /* .type = LTC_X509_EXT_KEY_USAGE + * Bitmask of `enum ltc_x509_ce_ext_key_usage` + */ + ulong32 ext_key_usage; + } u; + const ltc_asn1_list *asn1; +} ltc_x509_extension; + +typedef struct ltc_x509_extensions { + const ltc_x509_extension *extensions; + unsigned long extensions_num; + const ltc_asn1_list *asn1; + + const ltc_x509_extension *authority_key_id; + const ltc_x509_extension *subject_key_identifier; + const ltc_x509_extension *key_usage; + const ltc_x509_extension *subject_alt_name; + const ltc_x509_extension *basic_constraints; + const ltc_x509_extension *ext_key_usage; + /* let's pre-reserve this for future extensions */ + const ltc_x509_extension *more[7]; +} ltc_x509_extensions; + +typedef struct ltc_x509_tbs_certificate { + unsigned long version; + ltc_x509_string serial_number; + ltc_x509_signature_algorithm signature_algorithm; + ltc_x509_name issuer; + ltc_x509_validity validity; + ltc_x509_name subject; + ltc_pka_key subject_public_key_info; + const ltc_asn1_list *issuer_uid; + const ltc_asn1_list *subject_uid; + ltc_x509_extensions extensions; + const ltc_asn1_list *asn1; +} ltc_x509_tbs_certificate; + +typedef struct ltc_x509_signature { + const unsigned char *signature; + /* The signature length is given in bits, but it is stored as bytes. + * I.e. a signature of 123 bits will be stored in 128bits resp. 16bytes. + */ + unsigned long signature_len; + const ltc_asn1_list *asn1; +} ltc_x509_signature; + +typedef struct ltc_x509_certificate { + ltc_x509_tbs_certificate tbs_certificate; + ltc_x509_signature_algorithm signature_algorithm; + ltc_x509_signature signature; + const ltc_asn1_list *asn1; +} ltc_x509_certificate; + +int x509_import(const unsigned char *asn1_cert, unsigned long asn1_len, const ltc_x509_certificate **out); +#ifdef LTC_PEM +int x509_import_pem(const char *pem, unsigned long *pem_len, const ltc_x509_certificate **out); +#ifndef LTC_NO_FILE +int x509_import_pem_filehandle(FILE *f, const ltc_x509_certificate **out); +#endif /* LTC_NO_FILE */ +#endif /* LTC_PEM */ +int x509_cert_is_signed_by(const ltc_x509_certificate *cert, const ltc_pka_key *key, int *stat); +int x509_cmp_name(const ltc_x509_name *a, const ltc_x509_name *b); +int x509_name_detail_get(const ltc_x509_name *name, ltc_x509_details type, const ltc_x509_string **str); +const char *x509_name_detail_desc(ltc_x509_details type); +void x509_free(const ltc_x509_certificate **cert); + #endif diff --git a/src/headers/tomcrypt_private.h b/src/headers/tomcrypt_private.h index 1658cca7..b80d38c1 100644 --- a/src/headers/tomcrypt_private.h +++ b/src/headers/tomcrypt_private.h @@ -63,6 +63,33 @@ enum ltc_oid_id { LTC_OID_RSA_OAEP, LTC_OID_RSA_MGF1, LTC_OID_RSA_PSS, + LTC_OID_RSA_WITH_MD5, + LTC_OID_RSA_WITH_SHA1, + LTC_OID_RSA_WITH_SHA224, + LTC_OID_RSA_WITH_SHA256, + LTC_OID_RSA_WITH_SHA384, + LTC_OID_RSA_WITH_SHA512, + LTC_OID_RSA_WITH_SHA512_224, + LTC_OID_RSA_WITH_SHA512_512, + LTC_OID_ECDSA_WITH_SHA1, + LTC_OID_ECDSA_WITH_SHA224, + LTC_OID_ECDSA_WITH_SHA256, + LTC_OID_ECDSA_WITH_SHA384, + LTC_OID_ECDSA_WITH_SHA512, + LTC_OID_DSA_WITH_SHA1, + LTC_OID_DSA_WITH_SHA224, + LTC_OID_DSA_WITH_SHA256, + LTC_OID_DSA_WITH_SHA384, + LTC_OID_DSA_WITH_SHA512, + LTC_OID_ECDSA_WITH_SHA3_224, + LTC_OID_ECDSA_WITH_SHA3_256, + LTC_OID_ECDSA_WITH_SHA3_384, + LTC_OID_ECDSA_WITH_SHA3_512, + LTC_OID_RSA_WITH_SHA3_224, + LTC_OID_RSA_WITH_SHA3_256, + LTC_OID_RSA_WITH_SHA3_384, + LTC_OID_RSA_WITH_SHA3_512, + LTC_OID_NUM }; @@ -444,13 +471,14 @@ int rand_bn_upto(void *N, void *limit, prng_state *prng, int wprng); int pk_get_oid(enum ltc_oid_id id, const char **st); int pk_get_pka_id(enum ltc_oid_id id, enum ltc_pka_id *pka); +int pk_get_sig_alg(enum ltc_oid_id id, ltc_x509_signature_algorithm *sig_alg); int pk_get_oid_id(enum ltc_pka_id pka, enum ltc_oid_id *oid); -#ifdef LTC_DER -int pk_get_oid_from_asn1(const ltc_asn1_list *oid, enum ltc_oid_id *id); -#endif int pk_oid_str_to_num(const char *OID, unsigned long *oid, unsigned long *oidlen); int pk_oid_num_to_str(const unsigned long *oid, unsigned long oidlen, char *OID, unsigned long *outlen); - +#ifdef LTC_DER +int pk_get_oid_from_asn1(const ltc_asn1_list *oid, enum ltc_oid_id *id); +int pk_oid_cmp_with_asn1(const char *o1, const ltc_asn1_list *o2); +#endif int pk_oid_cmp_with_ulong(const char *o1, const unsigned long *o2, unsigned long o2size); /* ---- RSA Routines ---- */ @@ -715,6 +743,7 @@ int ec25519_crypto_ctx( unsigned char *out, unsigned long *outlen, #define LTC_ASN1_IS_TYPE(e, t) (((e) != NULL) && ((e)->type == (t))) /* DER handling */ +int der_decode_sequence_flexi_limited(const unsigned char *in, unsigned long *inlen, long max_depth, ltc_asn1_list **out); int der_decode_custom_type_ex(const unsigned char *in, unsigned long inlen, ltc_asn1_list *root, ltc_asn1_list *list, unsigned long outlen, unsigned int flags); @@ -733,6 +762,12 @@ int der_length_sequence_ex(const ltc_asn1_list *list, unsigned long inlen, int der_length_object_identifier_full(const unsigned long *words, unsigned long nwords, unsigned long *outlen, unsigned long *datalen); +int der_decode_ia5_string_data(const unsigned char *in, unsigned long inlen, + char *out, unsigned long *outlen); +int der_decode_object_identifier_data(const unsigned char *in, unsigned long inlen, + unsigned long *words, unsigned long *outlen); + + int der_ia5_char_encode(int c); int der_ia5_value_decode(int v); @@ -804,9 +839,11 @@ int x509_decode_subject_public_key_info(const unsigned char *in, unsigned long i ltc_asn1_type parameters_type, ltc_asn1_list* parameters, unsigned long *parameters_len); int x509_get_pka(const ltc_asn1_list *pub, enum ltc_pka_id *pka); -int x509_import_spki(const unsigned char *asn1_cert, unsigned long asn1_len, ltc_pka_key *k, ltc_asn1_list **root); - -int pk_oid_cmp_with_asn1(const char *o1, const ltc_asn1_list *o2); +int x509_get_sig_alg(const ltc_asn1_list *pub, ltc_x509_signature_algorithm *sig_alg); +int x509_import_spki(const unsigned char *buf, unsigned long len, ltc_pka_key *k, ltc_asn1_list **root); +int x509_get_extensions(const ltc_asn1_list *seq, ltc_x509_extensions *extensions); +void x509_free_extensions(const ltc_x509_extensions *extensions); +int x509_get_serial(const ltc_asn1_list *asn1, ltc_x509_string *serial); #endif /* LTC_DER */ diff --git a/src/pk/asn1/der/ia5/der_decode_ia5_string.c b/src/pk/asn1/der/ia5/der_decode_ia5_string.c index 8fd80cf1..cedb7d28 100644 --- a/src/pk/asn1/der/ia5/der_decode_ia5_string.c +++ b/src/pk/asn1/der/ia5/der_decode_ia5_string.c @@ -9,6 +9,30 @@ #ifdef LTC_DER +int der_decode_ia5_string_data(const unsigned char *in, unsigned long inlen, + char *out, unsigned long *outlen) +{ + unsigned long x, y; + int t; + + if (inlen > *outlen) { + *outlen = inlen; + return CRYPT_BUFFER_OVERFLOW; + } + + /* read the data */ + for (x = y = 0; y < inlen; y++) { + t = der_ia5_value_decode(in[x++]); + if (t == -1) { + return CRYPT_INVALID_ARG; + } + out[y] = t; + } + + *outlen = y; + + return CRYPT_OK; +} /** Store a IA5 STRING @@ -22,7 +46,7 @@ int der_decode_ia5_string(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen) { unsigned long x, y, len; - int t, err; + int err; LTC_ARGCHK(in != NULL); LTC_ARGCHK(out != NULL); @@ -47,27 +71,11 @@ int der_decode_ia5_string(const unsigned char *in, unsigned long inlen, x += y; /* is it too long? */ - if (len > *outlen) { - *outlen = len; - return CRYPT_BUFFER_OVERFLOW; - } - if (len > (inlen - x)) { return CRYPT_INVALID_PACKET; } - /* read the data */ - for (y = 0; y < len; y++) { - t = der_ia5_value_decode(in[x++]); - if (t == -1) { - return CRYPT_INVALID_ARG; - } - out[y] = t; - } - - *outlen = y; - - return CRYPT_OK; + return der_decode_ia5_string_data(in + x, len, (char*)out, outlen); } #endif diff --git a/src/pk/asn1/der/object_identifier/der_decode_object_identifier.c b/src/pk/asn1/der/object_identifier/der_decode_object_identifier.c index 72512477..bd66fe33 100644 --- a/src/pk/asn1/der/object_identifier/der_decode_object_identifier.c +++ b/src/pk/asn1/der/object_identifier/der_decode_object_identifier.c @@ -8,55 +8,15 @@ */ #ifdef LTC_DER -/** - Decode OID data and store the array of integers in words - @param in The OID DER encoded data - @param inlen The length of the OID data - @param words [out] The destination of the OID words - @param outlen [in/out] The number of OID words - @return CRYPT_OK if successful -*/ -int der_decode_object_identifier(const unsigned char *in, unsigned long inlen, - unsigned long *words, unsigned long *outlen) +int der_decode_object_identifier_data(const unsigned char *in, unsigned long inlen, + unsigned long *words, unsigned long *outlen) { unsigned long x, y, t, len; int err; - LTC_ARGCHK(in != NULL); - LTC_ARGCHK(words != NULL); - LTC_ARGCHK(outlen != NULL); - - /* header is at least 3 bytes */ - if (inlen < 3) { - return CRYPT_INVALID_PACKET; - } - - /* must be room for at least two words */ - if (*outlen < 2) { - *outlen = 2; - return CRYPT_BUFFER_OVERFLOW; - } - - /* decode the packet header */ - x = 0; - if ((in[x++] & 0x1F) != 0x06) { - return CRYPT_INVALID_PACKET; - } - - /* get the length of the data */ - y = inlen - x; - if ((err = der_decode_asn1_length(in + x, &y, &len)) != CRYPT_OK) { - return err; - } - x += y; - - if ((len == 0) || (len > (inlen - x))) { - return CRYPT_INVALID_PACKET; - } - /* decode words */ - y = 0; - t = 0; + x = y = t = 0; + len = inlen; while (len--) { t = (t << 7) | (in[x] & 0x7F); if (!(in[x++] & 0x80)) { @@ -91,4 +51,53 @@ int der_decode_object_identifier(const unsigned char *in, unsigned long inle return err; } +/** + Decode OID data and store the array of integers in words + @param in The OID DER encoded data + @param inlen The length of the OID data + @param words [out] The destination of the OID words + @param outlen [in/out] The number of OID words + @return CRYPT_OK if successful +*/ +int der_decode_object_identifier(const unsigned char *in, unsigned long inlen, + unsigned long *words, unsigned long *outlen) +{ + unsigned long x, y, len; + int err; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(words != NULL); + LTC_ARGCHK(outlen != NULL); + + /* header is at least 3 bytes */ + if (inlen < 3) { + return CRYPT_INVALID_PACKET; + } + + /* must be room for at least two words */ + if (*outlen < 2) { + *outlen = 2; + return CRYPT_BUFFER_OVERFLOW; + } + + /* decode the packet header */ + x = 0; + if ((in[x++] & 0x1F) != 0x06) { + return CRYPT_INVALID_PACKET; + } + + /* get the length of the data */ + y = inlen - x; + if ((err = der_decode_asn1_length(in + x, &y, &len)) != CRYPT_OK) { + return err; + } + x += y; + + if ((len == 0) || (len > (inlen - x))) { + return CRYPT_INVALID_PACKET; + } + + return der_decode_object_identifier_data(in + x, len, words, outlen); +} + #endif diff --git a/src/pk/asn1/der/sequence/der_decode_sequence_flexi.c b/src/pk/asn1/der/sequence/der_decode_sequence_flexi.c index ada1bd67..88c43f06 100644 --- a/src/pk/asn1/der/sequence/der_decode_sequence_flexi.c +++ b/src/pk/asn1/der/sequence/der_decode_sequence_flexi.c @@ -52,7 +52,7 @@ void s_print_err(const char *errstr, ltc_asn1_list *l, int err, unsigned long id @param depth The depth/level of decoding recursion we've already reached @return CRYPT_OK on success. */ -static int s_der_decode_sequence_flexi(const unsigned char *in, unsigned long *inlen, ltc_asn1_list **out, unsigned long depth) +static int s_der_decode_sequence_flexi(const unsigned char *in, unsigned long *inlen, ltc_asn1_list **out, unsigned long depth, long max_depth) { ltc_asn1_list *l; int err; @@ -433,6 +433,9 @@ static int s_der_decode_sequence_flexi(const unsigned char *in, unsigned long *i if (depth > LTC_DER_MAX_RECURSION) { err = CRYPT_PK_ASN1_ERROR; goto error; + } else if (max_depth > 0 && (long)depth > max_depth) { + totlen = *inlen; + goto default_out; } if ((l->data = XMALLOC(len)) == NULL) { @@ -453,7 +456,7 @@ static int s_der_decode_sequence_flexi(const unsigned char *in, unsigned long *i len_len = len; /* Sequence elements go as child */ - if ((err = s_der_decode_sequence_flexi(in, &len, &(l->child), depth+1)) != CRYPT_OK) { + if ((err = s_der_decode_sequence_flexi(in, &len, &(l->child), depth+1, max_depth)) != CRYPT_OK) { goto error; } if (len_len != len) { @@ -489,6 +492,7 @@ static int s_der_decode_sequence_flexi(const unsigned char *in, unsigned long *i break; default: +default_out: /* invalid byte ... this is a soft error */ /* remove link */ if (l->prev) { @@ -540,7 +544,19 @@ error: */ int der_decode_sequence_flexi(const unsigned char *in, unsigned long *inlen, ltc_asn1_list **out) { - return s_der_decode_sequence_flexi(in, inlen, out, 0); + return s_der_decode_sequence_flexi(in, inlen, out, 0, -1); +} + +/** + ASN.1 DER Flexi(ble) decoder will decode arbitrary DER packets and create a linked list of the decoded elements. + @param in The input buffer + @param inlen [in/out] The length of the input buffer and on output the amount of decoded data + @param out [out] A pointer to the linked list + @return CRYPT_OK on success. +*/ +int der_decode_sequence_flexi_limited(const unsigned char *in, unsigned long *inlen, long max_depth, ltc_asn1_list **out) +{ + return s_der_decode_sequence_flexi(in, inlen, out, 0, max_depth); } #endif diff --git a/src/pk/asn1/oid/pk_get.c b/src/pk/asn1/oid/pk_get.c index 81a1b2f3..159f3c1c 100644 --- a/src/pk/asn1/oid/pk_get.c +++ b/src/pk/asn1/oid/pk_get.c @@ -23,6 +23,32 @@ static const oid_table_entry pka_oids[] = { { LTC_OID_RSA_OAEP, LTC_PKA_RSA, NULL, "1.2.840.113549.1.1.7" }, { LTC_OID_RSA_MGF1, LTC_PKA_RSA, NULL, "1.2.840.113549.1.1.8" }, { LTC_OID_RSA_PSS, LTC_PKA_RSA_PSS, NULL, "1.2.840.113549.1.1.10" }, + { LTC_OID_RSA_WITH_MD5, LTC_PKA_RSA, "md5", "1.2.840.113549.1.1.4" }, + { LTC_OID_RSA_WITH_SHA1, LTC_PKA_RSA, "sha1", "1.2.840.113549.1.1.5" }, + { LTC_OID_RSA_WITH_SHA224, LTC_PKA_RSA, "sha224", "1.2.840.113549.1.1.14" }, + { LTC_OID_RSA_WITH_SHA256, LTC_PKA_RSA, "sha256", "1.2.840.113549.1.1.11" }, + { LTC_OID_RSA_WITH_SHA384, LTC_PKA_RSA, "sha384", "1.2.840.113549.1.1.12" }, + { LTC_OID_RSA_WITH_SHA512, LTC_PKA_RSA, "sha512", "1.2.840.113549.1.1.13" }, + { LTC_OID_RSA_WITH_SHA512_224, LTC_PKA_RSA, "sha512-224", "1.2.840.113549.1.1.15" }, + { LTC_OID_RSA_WITH_SHA512_512, LTC_PKA_RSA, "sha512-256", "1.2.840.113549.1.1.16" }, + { LTC_OID_ECDSA_WITH_SHA1, LTC_PKA_EC, "sha1", "1.2.840.10045.4.1" }, + { LTC_OID_ECDSA_WITH_SHA224, LTC_PKA_EC, "sha224", "1.2.840.10045.4.3.1" }, + { LTC_OID_ECDSA_WITH_SHA256, LTC_PKA_EC, "sha256", "1.2.840.10045.4.3.2" }, + { LTC_OID_ECDSA_WITH_SHA384, LTC_PKA_EC, "sha384", "1.2.840.10045.4.3.3" }, + { LTC_OID_ECDSA_WITH_SHA512, LTC_PKA_EC, "sha512", "1.2.840.10045.4.3.4" }, + { LTC_OID_DSA_WITH_SHA1, LTC_PKA_DSA, "sha1", "1.2.840.10040.4.3" }, + { LTC_OID_DSA_WITH_SHA224, LTC_PKA_DSA, "sha224", "2.16.840.1.101.3.4.3.1" }, + { LTC_OID_DSA_WITH_SHA256, LTC_PKA_DSA, "sha256", "2.16.840.1.101.3.4.3.2" }, + { LTC_OID_DSA_WITH_SHA384, LTC_PKA_DSA, "sha384", "2.16.840.1.101.3.4.3.3" }, + { LTC_OID_DSA_WITH_SHA512, LTC_PKA_DSA, "sha512", "2.16.840.1.101.3.4.3.4" }, + { LTC_OID_ECDSA_WITH_SHA3_224, LTC_PKA_EC, "sha3-224", "2.16.840.1.101.3.4.3.9" }, + { LTC_OID_ECDSA_WITH_SHA3_256, LTC_PKA_EC, "sha3-256", "2.16.840.1.101.3.4.3.10" }, + { LTC_OID_ECDSA_WITH_SHA3_384, LTC_PKA_EC, "sha3-384", "2.16.840.1.101.3.4.3.11" }, + { LTC_OID_ECDSA_WITH_SHA3_512, LTC_PKA_EC, "sha3-512", "2.16.840.1.101.3.4.3.12" }, + { LTC_OID_RSA_WITH_SHA3_224, LTC_PKA_RSA, "sha3-512", "2.16.840.1.101.3.4.3.13" }, + { LTC_OID_RSA_WITH_SHA3_256, LTC_PKA_RSA, "sha3-512", "2.16.840.1.101.3.4.3.14" }, + { LTC_OID_RSA_WITH_SHA3_384, LTC_PKA_RSA, "sha3-512", "2.16.840.1.101.3.4.3.15" }, + { LTC_OID_RSA_WITH_SHA3_512, LTC_PKA_RSA, "sha3-512", "2.16.840.1.101.3.4.3.16" }, }; static LTC_INLINE const oid_table_entry* s_get_entry(enum ltc_oid_id id) @@ -76,6 +102,16 @@ int pk_get_pka_id(enum ltc_oid_id id, enum ltc_pka_id *pka) return s_get_values(id, pka, NULL); } +/* + Returns the Signature Algorithm requested, PKA ID + Hash algorithm. + @return CRYPT_OK if valid +*/ +int pk_get_sig_alg(enum ltc_oid_id id, ltc_x509_signature_algorithm *sig_alg) +{ + LTC_ARGCHK(sig_alg != NULL); + return s_get_values(id, &sig_alg->pka, &sig_alg->u.hash); +} + /* Returns the OID ID requested. @return CRYPT_OK if valid diff --git a/src/pk/asn1/x509/x509_extensions.c b/src/pk/asn1/x509/x509_extensions.c new file mode 100644 index 00000000..64df74a8 --- /dev/null +++ b/src/pk/asn1/x509/x509_extensions.c @@ -0,0 +1,656 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ +#include "tomcrypt_private.h" + +/** + @file x509_extensions.c + Extensions part of an X.509 cert, Steffen Jaeckel +*/ + +#ifdef LTC_DER + +#define OID_DETAIL_ELEMENT_VA(d, n, ...) { .detail = d, .node = # n, __VA_ARGS__ } + +#define st_oid_detail st_x509_extension_oid_detail + +typedef struct st_oid_detail { + ltc_x509_details detail; + const char *node; + union { + struct { + ltc_asn1_type type; + der_flexi_handler handler; + } ce; + struct { + ulong32 bit; + } eku; + } u; + ltc_x509_string *str; +} st_oid_detail; + +static LTC_INLINE int s_get_element_(const st_oid_detail* details, unsigned long num, const char *arc, unsigned long arclen, const ltc_asn1_list *oid, const st_oid_detail **result) +{ + char oid_str[LTC_OID_MAX_STRLEN] = { 0 }; + unsigned long i, oid_str_len = sizeof(oid_str); + const char *node; + int err; + *result = NULL; + if (oid->type != LTC_ASN1_OBJECT_IDENTIFIER) { + return CRYPT_INVALID_PACKET; + } + if ((err = pk_oid_num_to_str(oid->data, oid->size, oid_str, &oid_str_len)) != CRYPT_OK) { + return err; + } + if (XMEMCMP(oid_str, arc, arclen)) { + return CRYPT_OK; + } + node = oid_str + arclen; + if (*node != '.') { + return CRYPT_INVALID_PACKET; + } + node++; + for (i = 0; i < num; ++i) { + if (XSTRCMP(node, details[i].node) == 0) { + *result = &details[i]; + break; + } + } + return CRYPT_OK; +} +#define s_get_element(arr, arc, oid, res) s_get_element_(arr, LTC_ARRAY_SIZE(arr), arc, sizeof(arc) - 1, oid, res) + + +#ifndef S_FREE +#define S_FREE +#define s_free(p) s_free_((void*) p) +static LTC_INLINE void s_free_(void* p) +{ + if (p == NULL) { + return; + } + XFREE(p); +} +#endif + +#ifndef S_FREE_X509_STRING_ARRAY +#define S_FREE_X509_STRING_ARRAY +#define s_free_x509_string_array(s, n) s_free_x509_string_array_((ltc_x509_string*)s, n) +static LTC_INLINE void s_free_x509_string_array_(ltc_x509_string *strings, unsigned long num) +{ + unsigned long n; + for (n = num; n --> 0;) { + s_free(strings[n].str); + } + s_free(strings); +} +#endif + +#ifndef S_IS_CONTEXT_SPECIFIC +#define S_IS_CONTEXT_SPECIFIC +static LTC_INLINE int s_is_context_specific(const ltc_asn1_list *seq) +{ + if (seq->type != LTC_ASN1_CUSTOM_TYPE) + return 0; + if (seq->klass != LTC_ASN1_CL_CONTEXT_SPECIFIC) + return 0; + return 1; +} +#endif + +#ifndef S_IS_CONTEXT_SPECIFIC_PRIMITIVE +#define S_IS_CONTEXT_SPECIFIC_PRIMITIVE +static LTC_INLINE int s_is_context_specific_primitive(const ltc_asn1_list *seq) +{ + if (!s_is_context_specific(seq)) + return 0; + if (seq->pc != LTC_ASN1_PC_PRIMITIVE) + return 0; + return 1; +} +#endif + +static LTC_INLINE int s_looks_like_general_name(const ltc_asn1_list *name) +{ + if (!s_is_context_specific(name) + || (name->pc == LTC_ASN1_PC_PRIMITIVE && name->tag > 8)) + return CRYPT_PK_ASN1_ERROR; + return CRYPT_OK; +} + +typedef unsigned short int ushort16; +#if defined(ENDIAN_LITTLE) +#define LTC_NTOHS(y) ( ((ushort16)((y)[0] & 255)<<8) | ((ushort16)((y)[1] & 255)) ) +#else +#define LTC_NTOHS(y) (*((ushort16*)(y))) +#endif + +/* RFC 5280, Ch. 4.2.1.6. Subject Alternative Name + * [...] + * GeneralName ::= CHOICE { + * otherName [0] OtherName, + * rfc822Name [1] IA5String, + * dNSName [2] IA5String, + * x400Address [3] ORAddress, + * directoryName [4] Name, + * ediPartyName [5] EDIPartyName, + * uniformResourceIdentifier [6] IA5String, + * iPAddress [7] OCTET STRING, + * registeredID [8] OBJECT IDENTIFIER } + * + * OtherName ::= SEQUENCE { + * type-id OBJECT IDENTIFIER, + * value [0] EXPLICIT ANY DEFINED BY type-id } + * + * EDIPartyName ::= SEQUENCE { + * nameAssigner [0] DirectoryString OPTIONAL, + * partyName [1] DirectoryString } + */ +static int s_get_general_name(const ltc_asn1_list *seq, ltc_x509_string *name) +{ + int err = CRYPT_OK; + char *str = NULL; + unsigned long len; + + switch (seq->tag) { + case 1: + case 2: + case 6: + len = seq->size + 1; + str = XMALLOC(len); + if (str == NULL) { + err = CRYPT_MEM; + break; + } + if ((err = der_decode_ia5_string_data(seq->data, seq->size, str, &len)) != CRYPT_OK) { + break; + } + str[len] = '\0'; + break; + case 7: + { + int nbytes; + unsigned char *ip = seq->data; + if (seq->size == 4) { + str = XMALLOC(4 * 4); + if (str == NULL) { + err = CRYPT_MEM; + break; + } + nbytes = snprintf(str, 16, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]); + if (nbytes < 0) { + err = CRYPT_ERROR; + } + } else if (seq->size == 16) { + str = XMALLOC(8 * 5); + if (str == NULL) { + err = CRYPT_MEM; + break; + } + nbytes = snprintf(str, 40, "%x:%x:%x:%x:%x:%x:%x:%x", LTC_NTOHS(&ip[0]), LTC_NTOHS(&ip[2]), + LTC_NTOHS(&ip[4]), LTC_NTOHS(&ip[6]), + LTC_NTOHS(&ip[8]), LTC_NTOHS(&ip[10]), + LTC_NTOHS(&ip[12]), LTC_NTOHS(&ip[14])); + if (nbytes < 0) { + err = CRYPT_ERROR; + } + } else { + err = CRYPT_PK_ASN1_ERROR; + } + } + break; + case 8: + { + unsigned long oid[LTC_DER_OID_DEFAULT_NODES], oid_len = LTC_DER_OID_DEFAULT_NODES; + if ((err = der_decode_object_identifier_data(seq->data, seq->size, oid, &oid_len)) != CRYPT_OK) { + break; + } + if ((err = pk_oid_num_to_str(oid, oid_len, NULL, &len)) != CRYPT_BUFFER_OVERFLOW) { + break; + } + str = XMALLOC(len); + if (str == NULL) { + err = CRYPT_MEM; + break; + } + err = pk_oid_num_to_str(oid, oid_len, str, &len); + } + break; + case 0: + case 3: + case 4: + case 5: + break; + default: + err = CRYPT_PK_ASN1_ERROR; + break; + } + if (err != CRYPT_OK) { + if (str != NULL) { + XFREE(str); + str = NULL; + } + return err; + } + name->type = LTC_X509_OTHER_NAME + seq->tag; + name->asn1 = seq; + name->str = str; + + return err; +} + +static int s_octet_string_to_hex_string(const ltc_asn1_list *asn1, ltc_x509_string *hex) +{ + unsigned long len; + char *str; + int err; + len = asn1->size * 2 + 1; + str = XMALLOC(len); + if (str == NULL) { + return CRYPT_MEM; + } + if ((err = base16_encode(asn1->data, asn1->size, str, &len, 1)) != CRYPT_OK) { + XFREE(str); + return err; + } + hex->asn1 = asn1; + hex->str = str; + hex->type = LTC_X509_OCTET_STRING; + return CRYPT_OK; +} + +/* RFC 5280, Ch. 4.2.1.1. Authority Key Identifier + * AuthorityKeyIdentifier ::= SEQUENCE { + * keyIdentifier [0] KeyIdentifier OPTIONAL, + * authorityCertIssuer [1] GeneralNames OPTIONAL, + * authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL } + * + * KeyIdentifier ::= OCTET STRING + */ +static int s_get_aki(const ltc_asn1_list *seq, ltc_x509_extension *san) +{ + int err; + ltc_asn1_list *element = seq->child; + while(element && s_is_context_specific_primitive(element)) { + switch (element->tag) { + case 0: + if ((err = s_octet_string_to_hex_string(element, &san->u.authority_key_id.key_identifier)) != CRYPT_OK) { + return err; + } + break; + case 1: + if ((err = s_looks_like_general_name(element->child)) != CRYPT_OK) { + return err; + } + if ((err = s_get_general_name(element->child, &san->u.authority_key_id.authority_cert_issuer)) != CRYPT_OK) { + return err; + } + break; + case 2: + if ((err = x509_get_serial(element->child, &san->u.authority_key_id.authority_cert_serial_number)) != CRYPT_OK) { + return err; + } + break; + default: + return CRYPT_PK_ASN1_ERROR; + } + element = element->next; + } + return CRYPT_OK; +} + +/* RFC 5280, Ch. 4.2.1.2. Subject Key Identifier + * SubjectKeyIdentifier ::= KeyIdentifier + * + * KeyIdentifier ::= OCTET STRING + */ +static int s_get_ski(const ltc_asn1_list *seq, ltc_x509_extension *san) +{ + void *buf; + unsigned long len, outlen; + int err; + if (seq->type != LTC_ASN1_OCTET_STRING) { + return CRYPT_PK_ASN1_ERROR; + } + /* `size` still contains the ASN.1 header and length, so we're safe length-wise */ + len = seq->size * 2; + buf = XMALLOC(len); + if (buf == NULL) { + return CRYPT_MEM; + } + outlen = len; + if ((err = der_decode_octet_string(seq->data, seq->size, buf, &outlen)) != CRYPT_OK) { + XFREE(buf); + return err; + } + if ((err = base16_encode(buf, outlen, buf, &len, 1)) != CRYPT_OK) { + XFREE(buf); + return err; + } + san->u.subject_key_identifier.asn1 = seq; + san->u.subject_key_identifier.str = buf; + san->u.subject_key_identifier.type = LTC_X509_OCTET_STRING; + return err; +} + +/* RFC 5280, Ch. 4.2.1.3. Key Usage + * KeyUsage ::= BIT STRING { + * digitalSignature (0), + * [...] + * decipherOnly (8) } + */ +static int s_get_ku(const ltc_asn1_list *bitstr, ltc_x509_extension *eku) +{ + unsigned char ku[9]; + unsigned long n, kulen = sizeof(ku); + int err; + eku->u.key_usage = 0; + if ((err = der_decode_bit_string(bitstr->data, bitstr->size, ku, &kulen)) != CRYPT_OK) { + return err; + } + for (n = 0; n < kulen; ++n) { + eku->u.key_usage |= ku[n] ? (1 << n) : 0; + } + return err; +} + +/* RFC 5280, Ch. 4.2.1.9. Basic Constraints + * BasicConstraints ::= SEQUENCE { + * cA BOOLEAN DEFAULT FALSE, + * pathLenConstraint INTEGER (0..MAX) OPTIONAL } + */ +static int s_get_bc(const ltc_asn1_list *seq, ltc_x509_extension *bc) +{ + ltc_asn1_list *element = seq->child; + bc->u.basic_constraints.ca = 0; + bc->u.basic_constraints.path_len = -1; + if (element == NULL || element->type == LTC_ASN1_EOL) + return CRYPT_OK; + if (element->type == LTC_ASN1_BOOLEAN) { + bc->u.basic_constraints.ca = *(int*)element->data ? 1 : 0; + element = element->next; + } + if (element == NULL) + return CRYPT_OK; + if (element->type == LTC_ASN1_INTEGER) { + if (ltc_mp_count_bits(element->data) > (int)((sizeof(bc->u.basic_constraints.path_len) * CHAR_BIT) - 1)) + return CRYPT_OVERFLOW; + bc->u.basic_constraints.path_len = (int)ltc_mp_get_int(element->data); + } + return CRYPT_OK; +} + +/* RFC 5280, Ch. 4.2.1.12. Extended Key Usage + * id-ce-extKeyUsage OBJECT IDENTIFIER ::= { id-ce 37 } + * ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId + * KeyPurposeId ::= OBJECT IDENTIFIER + */ + +#define X509_EKU_ELEMENT(detail, node, type) OID_DETAIL_ELEMENT_VA(detail, node, .u.eku.bit = type) + +/* anyExtendedKeyUsage is allocated under the id-ce-extKeyUsage arc + * + * anyExtendedKeyUsage OBJECT IDENTIFIER ::= { id-ce-extKeyUsage 0 } + */ +static const char x509_eku_arc[] = "2.5.29.37"; +static const st_oid_detail eku_any_map[] = { + X509_EKU_ELEMENT(LTC_X509_CE_EXT_KEY_USAGE, 0, LTC_EKU_ANY), +}; + +/* all other key purpose OID's are allocated under the id-pkix arc + * + * id-kp OBJECT IDENTIFIER ::= { id-pkix 3 } + */ +static const char x509_kp_arc[] = "1.3.6.1.5.5.7.3"; +static const st_oid_detail eku_elements_map[] = { + X509_EKU_ELEMENT(LTC_X509_CE_EXT_KEY_USAGE, 1, LTC_EKU_SA), + X509_EKU_ELEMENT(LTC_X509_CE_EXT_KEY_USAGE, 2, LTC_EKU_CA), + X509_EKU_ELEMENT(LTC_X509_CE_EXT_KEY_USAGE, 3, LTC_EKU_CS), + X509_EKU_ELEMENT(LTC_X509_CE_EXT_KEY_USAGE, 4, LTC_EKU_EP), + X509_EKU_ELEMENT(LTC_X509_CE_EXT_KEY_USAGE, 8, LTC_EKU_TS), + X509_EKU_ELEMENT(LTC_X509_CE_EXT_KEY_USAGE, 9, LTC_EKU_OS), +}; + +static int s_get_eku(const ltc_asn1_list *seq, ltc_x509_extension *eku) +{ + int err = CRYPT_INVALID_PACKET; + ltc_asn1_list *oid = seq->child; + eku->u.ext_key_usage = 0; + while (oid) { + const st_oid_detail *ce = NULL; + err = s_get_element(eku_any_map, x509_eku_arc, oid, &ce); + if (err == CRYPT_OK && ce) { + eku->u.ext_key_usage |= ce->u.eku.bit; + oid = oid->next; + continue; + } else if (err != CRYPT_OK) { + break; + } + err = s_get_element(eku_elements_map, x509_kp_arc, oid, &ce); + if (err == CRYPT_OK && ce) { + eku->u.ext_key_usage |= ce->u.eku.bit; + } else if (err != CRYPT_OK) { + break; + } + oid = oid->next; + } + if (eku->u.ext_key_usage == 0) { + err = CRYPT_INVALID_PACKET; + } + return err; +} + +/* RFC 5280, Ch. 4.2.1.6. Subject Alternative Name + * SubjectAltName ::= GeneralNames + * + * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName + */ +static int s_get_san(const ltc_asn1_list *seq, ltc_x509_extension *san) +{ + int err = CRYPT_PK_ASN1_ERROR; + ltc_x509_string *names; + unsigned long num = 0, cur = 0; + ltc_asn1_list *name = seq->child; + + while (name) { + if ((err = s_looks_like_general_name(name)) != CRYPT_OK) { + return err; + } + num++; + name = name->next; + } + + names = XCALLOC(num, sizeof(*names)); + if (names == NULL) { + return CRYPT_MEM; + } + name = seq->child; + + while (name && cur < num) { + if ((err = s_get_general_name(name, &names[cur])) != CRYPT_OK) { + break; + } + name = name->next; + cur++; + } + if (err == CRYPT_OK) { + san->u.subject_alt_name.asn1 = seq; + san->u.subject_alt_name.names = names; + san->u.subject_alt_name.names_num = num; + } else { + s_free_x509_string_array(names, num); + } + return err; +} + +#define X509_CE_ELEMENT(detail, oid, type_, hndl) OID_DETAIL_ELEMENT_VA(detail, oid, .u.ce.type = type_, .u.ce.handler = (der_flexi_handler)hndl) + +/* The certificate extension OID's arc is defined as follows + * + * id-ce OBJECT IDENTIFIER ::= { joint-iso-ccitt(2) ds(5) 29 } + */ +static const char x509_ce_arc[] = "2.5.29"; +static const st_oid_detail ce_elements_map[] = { + X509_CE_ELEMENT(LTC_X509_CE_AUTHORITY_KEY_ID, 35, LTC_ASN1_SEQUENCE, s_get_aki), + X509_CE_ELEMENT(LTC_X509_CE_SUBJECT_KEY_ID, 14, LTC_ASN1_OCTET_STRING, s_get_ski), + X509_CE_ELEMENT(LTC_X509_CE_KEY_USAGE, 15, LTC_ASN1_BIT_STRING, s_get_ku), + X509_CE_ELEMENT(LTC_X509_CE_SUBJECT_ALT_NAME, 17, LTC_ASN1_SEQUENCE, s_get_san), + X509_CE_ELEMENT(LTC_X509_CE_BASIC_CONSTRAINTS, 19, LTC_ASN1_SEQUENCE, s_get_bc), + X509_CE_ELEMENT(LTC_X509_CE_EXT_KEY_USAGE, 37, LTC_ASN1_SEQUENCE, s_get_eku), +}; + +typedef struct st_ce_value { + const st_oid_detail* ce; + ltc_asn1_list *crit; + ltc_x509_extension value; +} st_ce_value; + +static int s_get_ce_element(const ltc_asn1_list *oid, st_ce_value *ce) +{ + int err = s_get_element(ce_elements_map, x509_ce_arc, oid, &ce->ce); + ce->value.oid = (err == CRYPT_OK) ? oid : NULL; + return err; +} + +static int s_get_ce_value(const ltc_asn1_list *os, st_ce_value *ce) +{ + int err = CRYPT_OK; + ce->value.asn1 = os; + if (ce->ce == NULL) { + ce->value.type = LTC_X509_UNKNOWN; + return CRYPT_OK; + } + if (ce->ce->u.ce.type == LTC_ASN1_SEQUENCE) { + ltc_asn1_list *value; + unsigned long len = os->size; + if ((err = der_decode_sequence_flexi_limited(os->data, &len, 2, &value)) != CRYPT_OK) { + return err; + } + if (value->type != LTC_ASN1_SEQUENCE) { + err = CRYPT_INVALID_PACKET; + goto err_out; + } else { + err = ce->ce->u.ce.handler(value, &ce->value); + } + der_free_sequence_flexi(value); + } else { + err = ce->ce->u.ce.handler(os, &ce->value); + } + if (err == CRYPT_OK) { + ce->value.type = ce->ce->detail; + } +err_out: + return err; +} + +static LTC_INLINE void s_free_extension(const ltc_x509_extension *ext) +{ + switch (ext->type) { + case LTC_X509_CE_AUTHORITY_KEY_ID: + s_free(ext->u.authority_key_id.key_identifier.str); + s_free(ext->u.authority_key_id.authority_cert_issuer.str); + s_free(ext->u.authority_key_id.authority_cert_serial_number.str); + break; + case LTC_X509_CE_SUBJECT_KEY_ID: + s_free(ext->u.subject_key_identifier.str); + break; + case LTC_X509_CE_SUBJECT_ALT_NAME: + s_free_x509_string_array(ext->u.subject_alt_name.names, ext->u.subject_alt_name.names_num); + break; + case LTC_X509_CE_BASIC_CONSTRAINTS: + case LTC_X509_CE_EXT_KEY_USAGE: + case LTC_X509_CE_KEY_USAGE: + default: + break; + } +} + +static LTC_INLINE void s_free_extensions(const ltc_x509_extension *extensions, unsigned long num) +{ + unsigned long n; + for (n = num; n --> 0;) { + s_free_extension(&extensions[n]); + } + s_free(extensions); +} + +void x509_free_extensions(const ltc_x509_extensions *extensions) +{ + s_free_extensions(extensions->extensions, extensions->extensions_num); +} + +int x509_get_extensions(const ltc_asn1_list *seq, ltc_x509_extensions *extensions) +{ + ltc_x509_extension *extensions_; + unsigned long extensions_num = 0; + ltc_asn1_list *cur; + unsigned long cur_num = 0; + int err; + if (seq->type != LTC_ASN1_SEQUENCE) + return CRYPT_INVALID_PACKET; + cur = seq->child; + while (cur) { + extensions_num++; + cur = cur->next; + } + extensions_ = XCALLOC(extensions_num, sizeof(*extensions->extensions)); + if (extensions_ == NULL) { + return CRYPT_MEM; + } + cur = seq->child; + while (cur) { + st_ce_value val = {0}; + der_flexi_check flexi_should[4]; + if (cur_num > extensions_num) { + err = CRYPT_ERROR; + goto error_out; + } + LTC_SET_DER_FLEXI_HANDLER(flexi_should, 0, LTC_ASN1_OBJECT_IDENTIFIER, s_get_ce_element, &val); + LTC_SET_DER_FLEXI_CHECK_OPT(flexi_should, 1, LTC_ASN1_BOOLEAN, &val.crit); + LTC_SET_DER_FLEXI_HANDLER(flexi_should, 2, LTC_ASN1_OCTET_STRING, s_get_ce_value, &val); + LTC_SET_DER_FLEXI_CHECK(flexi_should, 3, LTC_ASN1_EOL, NULL); + if ((err = der_flexi_sequence_cmp(cur, flexi_should)) != CRYPT_OK) { + goto error_out; + } + extensions_[cur_num] = val.value; + if (val.crit) { + extensions_[cur_num].critical = *(int*)val.crit->data; + } + cur_num++; + cur = cur->next; + } + for (cur_num = 0; cur_num < extensions_num; ++cur_num) { + switch (extensions_[cur_num].type) { + case LTC_X509_CE_AUTHORITY_KEY_ID: + extensions->authority_key_id = &extensions_[cur_num]; + break; + case LTC_X509_CE_SUBJECT_KEY_ID: + extensions->subject_key_identifier = &extensions_[cur_num]; + break; + case LTC_X509_CE_KEY_USAGE: + extensions->key_usage = &extensions_[cur_num]; + break; + case LTC_X509_CE_SUBJECT_ALT_NAME: + extensions->subject_alt_name = &extensions_[cur_num]; + break; + case LTC_X509_CE_BASIC_CONSTRAINTS: + extensions->basic_constraints = &extensions_[cur_num]; + break; + case LTC_X509_CE_EXT_KEY_USAGE: + extensions->ext_key_usage = &extensions_[cur_num]; + break; + default: + break; + } + } + extensions->asn1 = seq; + extensions->extensions = extensions_; + extensions->extensions_num = cur_num; + return CRYPT_OK; +error_out: + s_free_extensions(extensions_, extensions_num); + return err; +} + +#undef st_oid_detail + +#endif diff --git a/src/pk/asn1/x509/x509_get_pka.c b/src/pk/asn1/x509/x509_get.c similarity index 65% rename from src/pk/asn1/x509/x509_get_pka.c rename to src/pk/asn1/x509/x509_get.c index 0236c761..131615a6 100644 --- a/src/pk/asn1/x509/x509_get_pka.c +++ b/src/pk/asn1/x509/x509_get.c @@ -4,16 +4,15 @@ /** @file x509_get_pka.c - Extract the PKA from an X.509 cert, Steffen Jaeckel + Extract details from an X.509 cert, Steffen Jaeckel */ #ifdef LTC_DER -int x509_get_pka(const ltc_asn1_list *pub, enum ltc_pka_id *pka) +static LTC_INLINE int s_x509_get_oid(const ltc_asn1_list *pub, enum ltc_oid_id *oid_id) { - der_flexi_check flexi_should[4]; + der_flexi_check flexi_should[3]; ltc_asn1_list *seqid, *id = NULL; - enum ltc_oid_id oid_id; int err; unsigned long n = 0; LTC_SET_DER_FLEXI_CHECK(flexi_should, n++, LTC_ASN1_SEQUENCE, &seqid); @@ -29,10 +28,27 @@ int x509_get_pka(const ltc_asn1_list *pub, enum ltc_pka_id *pka) if (err != CRYPT_OK && err != CRYPT_INPUT_TOO_LONG) { return err; } - if ((err = pk_get_oid_from_asn1(id, &oid_id)) != CRYPT_OK) { + return pk_get_oid_from_asn1(id, oid_id); +} + +int x509_get_pka(const ltc_asn1_list *pub, enum ltc_pka_id *pka) +{ + int err; + enum ltc_oid_id oid_id; + if ((err = s_x509_get_oid(pub, &oid_id)) != CRYPT_OK) { return err; } return pk_get_pka_id(oid_id, pka); } +int x509_get_sig_alg(const ltc_asn1_list *seq, ltc_x509_signature_algorithm *sig_alg) +{ + int err; + enum ltc_oid_id oid_id; + if ((err = s_x509_get_oid(seq, &oid_id)) != CRYPT_OK) { + return err; + } + return pk_get_sig_alg(oid_id, sig_alg); +} + #endif /* LTC_DER */ diff --git a/src/pk/asn1/x509/x509_import.c b/src/pk/asn1/x509/x509_import.c new file mode 100644 index 00000000..93f9bcf1 --- /dev/null +++ b/src/pk/asn1/x509/x509_import.c @@ -0,0 +1,683 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ +#include "tomcrypt_private.h" +#include + +/** + @file x509_import.c + Import an X.509 cert, Steffen Jaeckel +*/ + +#ifdef LTC_DER + +#define LTC_ASN1_BMP_STRING 30 + +#define ASN1_STRING LTC_BIT(LTC_ASN1_TELETEX_STRING) \ + | LTC_BIT(LTC_ASN1_PRINTABLE_STRING) \ + | LTC_BIT(LTC_ASN1_UTF8_STRING) \ + | LTC_BIT(LTC_ASN1_CUSTOM_TYPE) +#define ASN1_IA5_STRING LTC_BIT(LTC_ASN1_IA5_STRING) + +#define OID_DETAIL_ELEMENT(detail, oid, types, ub) { detail, oid, types, ub, NULL } +#define OID_DETAIL_ELEMENT_5(detail, arc, oid, types, ub) { detail, arc "." # oid, types, ub, NULL } + +#define st_oid_detail st_x509_import_oid_detail + +typedef struct st_oid_detail { + ltc_x509_details detail; + const char *oid; + ulong32 types; + unsigned long upper_bound; + ltc_x509_string *str; +} st_oid_detail; + +#define X509_NAME_ARC "2.5.4" +#define X509_NAME_ELEMENT(detail, oid, ub) OID_DETAIL_ELEMENT_5(detail, X509_NAME_ARC, oid, ASN1_STRING, ub) + +static const st_oid_detail name_elements_map[] = { + X509_NAME_ELEMENT(LTC_X509_CN, 3, 64), + X509_NAME_ELEMENT(LTC_X509_C, 6, 2), + X509_NAME_ELEMENT(LTC_X509_L, 7, 128), + X509_NAME_ELEMENT(LTC_X509_ST, 8, 128), + X509_NAME_ELEMENT(LTC_X509_O, 10, 64), + X509_NAME_ELEMENT(LTC_X509_OU, 11, 64), + OID_DETAIL_ELEMENT(LTC_X509_EMAIL, "1.2.840.113549.1.9.1", ASN1_IA5_STRING, 255), +}; + +static LTC_INLINE const st_oid_detail* s_get_name_element(const char *oid) +{ + unsigned long i; + for (i = 0; i < LTC_ARRAY_SIZE(name_elements_map); ++i) { + if (XSTRCMP(oid, name_elements_map[i].oid) == 0) { + return &name_elements_map[i]; + } + } + return NULL; +} + +static LTC_INLINE int s_get_as_string(const ltc_asn1_list *value, char **str) +{ + char *work = NULL; + size_t str_len; + switch (value->type) { + case LTC_ASN1_UTF8_STRING: + str_len = wcstombs(NULL, value->data, 0); + if (str_len == (size_t)-1) { + /* In case `wcstombs()` can't decode the string, we + * don't error out, but leave it up to the user. + */ + break; + } + str_len++; + work = XMALLOC(str_len); + if (work == NULL) + return CRYPT_MEM; + str_len = wcstombs(work, value->data, str_len); + if (str_len == (size_t)-1) { + XFREE(work); + return CRYPT_ERROR; + } + break; + case LTC_ASN1_IA5_STRING: + case LTC_ASN1_PRINTABLE_STRING: + case LTC_ASN1_TELETEX_STRING: + work = XMALLOC(value->size + 1); + if (work == NULL) + return CRYPT_MEM; + XMEMCPY(work, value->data, value->size); + work[value->size] = '\0'; + break; + case LTC_ASN1_CUSTOM_TYPE: + if (value->tag == LTC_ASN1_BMP_STRING) { + /* We don't decode BMP Strings, but the user + * is free to do so themselves. + */ + break; + } + /* FALLTHROUGH */ + default: + return CRYPT_INVALID_ARG; + } + *str = work; + return CRYPT_OK; +} + +static LTC_INLINE int s_get_name_element_by_oid(const ltc_asn1_list *oid, st_oid_detail *name_element) +{ + char oid_str[LTC_OID_MAX_STRLEN] = { 0 }, *str = NULL; + unsigned long oid_str_len = sizeof(oid_str); + ltc_asn1_list *value; + const st_oid_detail *detail; + static const st_oid_detail unknown_detail = { .detail = LTC_X509_UNKNOWN }; + int err; + if (oid == NULL + || oid->type != LTC_ASN1_OBJECT_IDENTIFIER + || oid->next == NULL) { + return CRYPT_PK_ASN1_ERROR; + } + value = oid->next; + if ((err = pk_oid_num_to_str(oid->data, oid->size, oid_str, &oid_str_len)) != CRYPT_OK) { + return err; + } + detail = s_get_name_element(oid_str); + if (detail) { + if (!(LTC_BIT(value->type) & detail->types)) + return CRYPT_PK_ASN1_ERROR; + if (value->size > detail->upper_bound) + return CRYPT_PK_ASN1_ERROR; + if ((err = s_get_as_string(value, &str)) != CRYPT_OK) { + return err; + } + } else { + detail = &unknown_detail; + } + if (name_element) { + ltc_x509_string *orig_str = name_element->str; + orig_str->type = detail->detail; + orig_str->asn1 = value; + orig_str->str = str; + *name_element = *detail; + name_element->str = orig_str; + } + return CRYPT_OK; +} + +static LTC_INLINE int s_x509_get_name_process(const ltc_asn1_list *set, st_oid_detail *name_elements, unsigned long name_elements_num) +{ + unsigned long num = 0; + for (; set != NULL; set = set->next) { + ltc_asn1_list *inner = set->child; + int err; + if (num > name_elements_num) { + return CRYPT_BUFFER_OVERFLOW; + } + if (set->type != LTC_ASN1_SET + || inner == NULL + || inner->type != LTC_ASN1_SEQUENCE) { + return CRYPT_PK_ASN1_ERROR; + } + if ((err = s_get_name_element_by_oid(inner->child, &name_elements[num])) != CRYPT_OK) { + return err; + } + num++; + } + return CRYPT_OK; +} + +#ifndef S_FREE +#define S_FREE +#define s_free(p) s_free_((void*) p) +static LTC_INLINE void s_free_(void* p) +{ + if (p == NULL) { + return; + } + XFREE((void*)p); +} +#endif + +#ifndef S_FREE_X509_STRING_ARRAY +#define S_FREE_X509_STRING_ARRAY +#define s_free_x509_string_array(s, n) s_free_x509_string_array_((ltc_x509_string*)s, n) +static LTC_INLINE void s_free_x509_string_array_(ltc_x509_string *strings, unsigned long num) +{ + unsigned long n; + for (n = num; n --> 0;) { + s_free(strings[n].str); + } + s_free(strings); +} +#endif + +#define SETBIT(v, n) (v=((unsigned char)(v) | (1U << (unsigned char)(n)))) +#define CLRBIT(v, n) (v=((unsigned char)(v) & ~(1U << (unsigned char)(n)))) + +static LTC_INLINE int s_bit_string_to_raw_bit_string(const ltc_asn1_list *bs, unsigned char **out, unsigned long *outlen) +{ + unsigned char *r = bs->data, *w = XCALLOC((bs->size + 7) / 8, 1); + unsigned long y; + + if (w == NULL) { + return CRYPT_MEM; + } + + /* decode/store the bits */ + for (y = 0; y < bs->size; y++) { + if (r[y]) { + SETBIT(w[y/8], 7-(y%8)); + } else { + CLRBIT(w[y/8], 7-(y%8)); + } + } + + *out = w; + *outlen = bs->size; + return CRYPT_OK; +} + +static LTC_INLINE int s_x509_check_version(unsigned long version) +{ + /* RFC5280 Ch. 4.1 + * Version ::= INTEGER { v1(0), v2(1), v3(2) } + * We only accept x.509v3 */ + if (version != 2) { + return CRYPT_PK_ASN1_ERROR; + } + return CRYPT_OK; +} + +static int s_x509_get_version(const ltc_asn1_list *seq, unsigned long *version) +{ + int err; + ltc_asn1_list work[2]; + LTC_SET_ASN1_CUSTOM_CONSTRUCTED(work, 0, LTC_ASN1_CL_CONTEXT_SPECIFIC, 0, work + 1); + LTC_SET_ASN1(work, 1, LTC_ASN1_SHORT_INTEGER, version, 1UL); + if ((err = der_decode_custom_type(seq->data, seq->size, work)) != CRYPT_OK) { + return err; + } + return s_x509_check_version(*version); +} + +int x509_get_serial(const ltc_asn1_list *asn1, ltc_x509_string *serial) +{ + void *tmp; + unsigned long len; + int err; + /* We're printing the serial number as hex string. + * The length of the hex string is double the binary size + 3. + * 3 comes from: eventually sign + reserve byte if value is 0 + terminating NUL byte. + */ + len = ltc_mp_unsigned_bin_size(asn1->data) * 2 + 3; + tmp = XMALLOC(len); + if (tmp == NULL) + return CRYPT_MEM; + if ((err = ltc_mp_tohex(asn1->data, tmp)) != CRYPT_OK) { + XFREE(tmp); + return err; + } + serial->type = LTC_X509_SERIAL; + serial->asn1 = asn1; + serial->str = tmp; + return CRYPT_OK; +} + +static int s_x509_get_sig_alg(const ltc_asn1_list *seq, ltc_x509_signature_algorithm *sig_alg) +{ + int err; + ltc_asn1_list fake_sig_parent = {0}; + + fake_sig_parent.type = LTC_ASN1_SEQUENCE; + fake_sig_parent.child = (ltc_asn1_list*)seq; + + if ((err = x509_get_sig_alg(&fake_sig_parent, sig_alg)) != CRYPT_OK) { + return err; + } + sig_alg->asn1 = seq; + return err; +} + +/* Decode a Name according to RFC 5280 + * + * Name ::= CHOICE { -- only one possibility for now -- + * rdnSequence RDNSequence } + * + * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName + * + * RelativeDistinguishedName ::= + * SET SIZE (1..MAX) OF AttributeTypeAndValue + * + * AttributeTypeAndValue ::= SEQUENCE { + * type AttributeType, + * value AttributeValue } + * + * AttributeType ::= OBJECT IDENTIFIER + * + * AttributeValue ::= ANY -- DEFINED BY AttributeType + */ +static int s_x509_get_name(const ltc_asn1_list *seq, ltc_x509_name *name) +{ + ltc_asn1_list *set = seq->child; + st_oid_detail *name_elements; + ltc_x509_string *names; + unsigned long n, names_num = 0; + int err; + for (; set != NULL && set->type != LTC_ASN1_EOL; set = set->next) { + names_num++; + } + if (names_num == 0) { + name->asn1 = seq; + name->names = NULL; + name->names_num = 0; + return CRYPT_OK; + } + name_elements = XCALLOC(names_num, sizeof(*name_elements)); + if (name_elements == NULL) { + return CRYPT_MEM; + } + names = XCALLOC(names_num, sizeof(*names)); + if (names == NULL) { + err = CRYPT_MEM; + goto err_out; + } + for (n = 0; n < names_num; ++n) { + name_elements[n].str = &names[n]; + } + if ((err = s_x509_get_name_process(seq->child, name_elements, names_num)) != CRYPT_OK) { + goto err_out; + } + name->asn1 = seq; + name->names = names; + name->names_num = names_num; +err_out: + if (err != CRYPT_OK) { + s_free_x509_string_array(names, names_num); + } + XFREE(name_elements); + return err; +} + +/* Decode the Validity according to RFC 5280 + * + * Validity ::= SEQUENCE { + * notBefore Time, + * notAfter Time } + * + * Time ::= CHOICE { + * utcTime UTCTime, + * generalTime GeneralizedTime } + */ + +static int s_x509_get_validity(const ltc_asn1_list *seq, ltc_x509_validity *validity) +{ + int n, err, ret; + ltc_asn1_list *source = seq->child; + ltc_x509_time *value = &validity->not_before; + validity->not_before.str = NULL; + validity->not_after.str = NULL; + for (n = 0; n < 2; ++n) { + if (source->type == LTC_ASN1_GENERALIZEDTIME) { + ltc_generalizedtime *gt = source->data; + value->utc = 0; + value->u.generalized = gt; + /* RFC5280 Ch. 4.1.2.5.2 + * GeneralizedTime values MUST be expressed in Greenwich Mean Time [...] + * GeneralizedTime values MUST NOT include fractional seconds. */ + if (gt->off_hh || gt->off_mm || gt->fs) { + err = CRYPT_PK_ASN1_ERROR; + goto err_out; + } else { + void *str = XMALLOC(24); + if (str == NULL) { + err = CRYPT_MEM; + goto err_out; + } + ret = snprintf(str, 24, "%04d-%02d-%02d %02d:%02d:%02dZ", + gt->YYYY, gt->MM, gt->DD, gt->hh, gt->mm, gt->ss); + if (ret < 0 || ret >= 24) { + err = CRYPT_ERROR; + XFREE(str); + goto err_out; + } + value->str = str; + value->asn1 = source; + } + } else if (source->type == LTC_ASN1_UTCTIME) { + ltc_utctime* ut = source->data; + value->utc = 1; + value->u.utc = ut; + /* RFC5280 Ch. 4.1.2.5.1 + * UTCTime values MUST be expressed in Greenwich Mean Time */ + if (ut->off_hh || ut->off_mm) { + err = CRYPT_PK_ASN1_ERROR; + goto err_out; + } else { + const char *YY; + void *str = XMALLOC(24); + if (str == NULL) { + err = CRYPT_MEM; + goto err_out; + } + /* Let's hope this software is not used after 2049... + * RFC5280 Ch. 4.1.2.5.1 + * Where YY is greater than or equal to 50, the year SHALL be + * interpreted as 19YY; and + * + * Where YY is less than 50, the year SHALL be interpreted as 20YY. + */ + YY = ut->YY >= 50 ? "19" : "20"; + ret = snprintf(str, 24, "%s%02d-%02d-%02d %02d:%02d:%02dZ", + YY, ut->YY, ut->MM, ut->DD, ut->hh, ut->mm, ut->ss); + if (ret < 0 || ret >= 24) { + err = CRYPT_ERROR; + XFREE(str); + goto err_out; + } + value->str = str; + value->asn1 = source; + } + } else { + err = CRYPT_PK_ASN1_ERROR; + goto err_out; + } + source = source->next; + value = &validity->not_after; + } + return CRYPT_OK; +err_out: + if (validity->not_after.str) + s_free(validity->not_after.str); + if (validity->not_before.str) + s_free(validity->not_before.str); + return err; +} + +static int s_x509_check_spki(const ltc_asn1_list *seq, const ltc_pka_key *spki) +{ + LTC_UNUSED_PARAM(seq); + if (spki->id > LTC_PKA_UNDEF && spki->id < LTC_PKA_NUM) { + return CRYPT_OK; + } + return CRYPT_PK_INVALID_TYPE; +} + +#ifndef S_IS_CONTEXT_SPECIFIC +#define S_IS_CONTEXT_SPECIFIC +static LTC_INLINE int s_is_context_specific(const ltc_asn1_list *seq) +{ + if (seq->type != LTC_ASN1_CUSTOM_TYPE) + return 0; + if (seq->klass != LTC_ASN1_CL_CONTEXT_SPECIFIC) + return 0; + return 1; +} +#endif + +#ifndef S_IS_CONTEXT_SPECIFIC_PRIMITIVE +#define S_IS_CONTEXT_SPECIFIC_PRIMITIVE +static LTC_INLINE int s_is_context_specific_primitive(const ltc_asn1_list *seq) +{ + if (!s_is_context_specific(seq)) + return 0; + if (seq->pc != LTC_ASN1_PC_PRIMITIVE) + return 0; + return 1; +} +#endif + +static LTC_INLINE int s_is_context_specific_constructed(const ltc_asn1_list *seq) +{ + if (!s_is_context_specific(seq)) + return 0; + if (seq->pc != LTC_ASN1_PC_CONSTRUCTED) + return 0; + return 1; +} + +static int s_x509_get_optionals(const ltc_asn1_list *seq, ltc_x509_tbs_certificate *tbs_cert) +{ + switch (seq->tag) { + /* Context specific tags [1] and [2] are of type UniqueIdentifier + * + * RFC5280 Ch. 4.1.2.8. Unique Identifiers + * [...] Applications conforming to + * this profile SHOULD be capable of parsing certificates that include + * unique identifiers, but there are no processing requirements + * associated with the unique identifiers. + * + * Good luck decoding them yourself :) + */ + case 1: + if (!s_is_context_specific_primitive(seq)) + return CRYPT_PK_ASN1_ERROR; + tbs_cert->issuer_uid = seq; + return CRYPT_OK; + case 2: + if (!s_is_context_specific_primitive(seq)) + return CRYPT_PK_ASN1_ERROR; + tbs_cert->subject_uid = seq; + return CRYPT_OK; + case 3: + if (!s_is_context_specific_constructed(seq)) + return CRYPT_PK_ASN1_ERROR; + return x509_get_extensions(seq->child, &tbs_cert->extensions); + default: + return CRYPT_PK_ASN1_ERROR; + } + return CRYPT_PK_ASN1_ERROR; +} + +/* Decode a TBSCertificate according to RFC 5280 + * + * TBSCertificate ::= SEQUENCE { + * version [0] EXPLICIT Version DEFAULT v1, + * serialNumber CertificateSerialNumber, + * signature AlgorithmIdentifier, + * issuer Name, + * validity Validity, + * subject Name, + * subjectPublicKeyInfo SubjectPublicKeyInfo, + * issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, + * -- If present, version MUST be v2 or v3 + * subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, + * -- If present, version MUST be v2 or v3 + * extensions [3] EXPLICIT Extensions OPTIONAL + * -- If present, version MUST be v3 + * } + */ +static int s_x509_import_tbs_certificate(const ltc_asn1_list *asn1, ltc_x509_tbs_certificate *tbs_cert) +{ + int err; + der_flexi_check flexi_should[11]; + LTC_SET_DER_FLEXI_HANDLER_OPT(flexi_should, 0, LTC_ASN1_CUSTOM_TYPE, s_x509_get_version, &tbs_cert->version); + LTC_SET_DER_FLEXI_HANDLER(flexi_should, 1, LTC_ASN1_INTEGER, x509_get_serial, &tbs_cert->serial_number); + LTC_SET_DER_FLEXI_HANDLER(flexi_should, 2, LTC_ASN1_SEQUENCE, s_x509_get_sig_alg, &tbs_cert->signature_algorithm); + LTC_SET_DER_FLEXI_HANDLER(flexi_should, 3, LTC_ASN1_SEQUENCE, s_x509_get_name, &tbs_cert->issuer); + LTC_SET_DER_FLEXI_HANDLER(flexi_should, 4, LTC_ASN1_SEQUENCE, s_x509_get_validity, &tbs_cert->validity); + LTC_SET_DER_FLEXI_HANDLER(flexi_should, 5, LTC_ASN1_SEQUENCE, s_x509_get_name, &tbs_cert->subject); + LTC_SET_DER_FLEXI_HANDLER(flexi_should, 6, LTC_ASN1_SEQUENCE, s_x509_check_spki, &tbs_cert->subject_public_key_info); + LTC_SET_DER_FLEXI_HANDLER_OPT(flexi_should, 7, LTC_ASN1_CUSTOM_TYPE, s_x509_get_optionals, tbs_cert); + LTC_SET_DER_FLEXI_HANDLER_OPT(flexi_should, 8, LTC_ASN1_CUSTOM_TYPE, s_x509_get_optionals, tbs_cert); + LTC_SET_DER_FLEXI_HANDLER_OPT(flexi_should, 9, LTC_ASN1_CUSTOM_TYPE, s_x509_get_optionals, tbs_cert); + LTC_SET_DER_FLEXI_CHECK(flexi_should, 10, LTC_ASN1_EOL, NULL); + if ((err = der_flexi_sequence_cmp(asn1, flexi_should)) == CRYPT_OK) { + tbs_cert->asn1 = asn1; + err = s_x509_check_version(tbs_cert->version); + } + return err; +} + +/** + Import an X.509 certificate from ASN.1 DER-encoded data. + + @param asn1_cert Pointer to the ASN.1 DER-encoded data. + @param asn1_len Length of the ASN.1 DER-encoded data. + @param out The resulting X.509 certificate. + @return CRYPT_OK if successful. +*/ +int x509_import(const unsigned char *asn1_cert, unsigned long asn1_len, const ltc_x509_certificate **out) +{ + ltc_x509_certificate *cert; + ltc_asn1_list *root, *tbs_cert, *sig_alg, *sig; + int err; + cert = XCALLOC(1, sizeof(*cert)); + if (cert == NULL) { + return CRYPT_MEM; + } + if ((err = x509_import_spki(asn1_cert, asn1_len, &cert->tbs_certificate.subject_public_key_info, &root)) != CRYPT_OK) { + goto err_out; + } + + tbs_cert = root->child; + sig_alg = tbs_cert->next; + sig = sig_alg->next; + cert->signature.asn1 = sig; + cert->asn1 = root; + + if ((err = s_x509_import_tbs_certificate(tbs_cert, &cert->tbs_certificate)) != CRYPT_OK) { + goto err_out; + } + + if ((err = s_x509_get_sig_alg(sig_alg, &cert->signature_algorithm)) != CRYPT_OK) { + goto err_out; + } + if ((err = s_bit_string_to_raw_bit_string(sig, (unsigned char**)&cert->signature.signature, &cert->signature.signature_len))) { + goto err_out; + } + *out = cert; + return err; +err_out: + x509_free((const ltc_x509_certificate **)&cert); + return err; +} + +#ifdef LTC_PEM +extern const struct pem_header_id pem_std_headers[]; + +static int s_x509_import_pem(struct get_char *g, unsigned long *pem_len, const ltc_x509_certificate **out) +{ + int err; + struct pem_headers hdr = { .id = &pem_std_headers[0] }; + unsigned long len = 0; + unsigned char *asn1_cert = NULL; + if ((err = pem_read((void**)&asn1_cert, &len, &hdr, g)) != CRYPT_OK) { + return err; + } + + err = x509_import(asn1_cert, len, out); + + *pem_len = g->total_read; + + XFREE(asn1_cert); + + return err; +} + +/** + Import an X.509 certificate from PEM-encoded data. + + @param pem Pointer to the PEM-encoded data. + @param pem_len [in] Length of the PEM-encoded data. + [out] The length of the data used. + @param out The resulting X.509 certificate. + @return CRYPT_OK if successful. +*/ +int x509_import_pem(const char *pem, unsigned long *pem_len, const ltc_x509_certificate **out) +{ + struct get_char g = pem_get_char_init(pem, *pem_len); + return s_x509_import_pem(&g, pem_len, out); +} + +#ifndef LTC_NO_FILE +/** + Import an X.509 certificate from PEM-encoded data from a FILE handle. + + @param f The FILE handle. + @param out The resulting X.509 certificate. + @return CRYPT_OK if successful. +*/ +int x509_import_pem_filehandle(FILE *f, const ltc_x509_certificate **out) +{ + unsigned long pem_len; + struct get_char g = pem_get_char_init_filehandle(f); + return s_x509_import_pem(&g, &pem_len, out); +} +#endif /* LTC_NO_FILE */ +#endif /* LTC_PEM */ + +static void s_free_x509_name(const ltc_x509_name *name) +{ + s_free_x509_string_array(name->names, name->names_num); +} + +static void s_free_x509_tbs_cert(const ltc_x509_tbs_certificate *tbs_cert) +{ + x509_free_extensions(&tbs_cert->extensions); + s_free_x509_name(&tbs_cert->subject); + s_free(tbs_cert->validity.not_after.str); + s_free(tbs_cert->validity.not_before.str); + s_free_x509_name(&tbs_cert->issuer); + s_free(tbs_cert->serial_number.str); + pka_key_free((void*)&tbs_cert->subject_public_key_info); +} + +/** + Free an X.509 certificate from memory + + @param cert The X.509 certificate to free. +*/ +void x509_free(const ltc_x509_certificate **cert) +{ + const ltc_x509_certificate *c; + LTC_ARGCHKVD(cert != NULL); + c = *cert; + LTC_ARGCHKVD(c != NULL); + s_free(c->signature.signature); + s_free_x509_tbs_cert(&c->tbs_certificate); + der_free_sequence_flexi((void*)c->asn1); + s_free(c); + *cert = NULL; +} + +#undef st_oid_detail + +#endif /* LTC_DER */ diff --git a/src/pk/asn1/x509/x509_utils.c b/src/pk/asn1/x509/x509_utils.c new file mode 100644 index 00000000..0babca76 --- /dev/null +++ b/src/pk/asn1/x509/x509_utils.c @@ -0,0 +1,194 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ +#include "tomcrypt_private.h" + +/** + @file x509_utils.c + More X.509 APIs, Steffen Jaeckel +*/ + +typedef struct pka_sig_args { + int hash_idx; + const ltc_rsa_parameters *rsa_params; +} pka_sig_args; + +static LTC_INLINE int s_pka_verify(const unsigned char *msg, unsigned long msglen, + const unsigned char *sig, unsigned long siglen, + pka_sig_args *sig_args, + int *stat, + const ltc_pka_key *key) +{ + ltc_rsa_op_parameters rsa_params = { 0 }; + ltc_ecc_sig_opts ecc_opts = { + .type = LTC_ECCSIG_ANSIX962 + }; + switch (key->id) { +#ifdef LTC_MRSA + case LTC_PKA_RSA: + rsa_params.params.hash_alg = hash_descriptor[sig_args->hash_idx].name; + rsa_params.padding = LTC_PKCS_1_V1_5; + /* RSA Keys usually use PKCS#1 v1.5 padding */ + return rsa_verify_hash_v2(sig, siglen, msg, msglen, &rsa_params, stat, &key->u.rsa); + case LTC_PKA_RSA_PSS: + { + if (sig_args->rsa_params == NULL) + return CRYPT_PK_INVALID_TYPE; + rsa_params.params = *sig_args->rsa_params; + rsa_params.padding = LTC_PKCS_1_PSS; + return rsa_verify_hash_v2(sig, siglen, msg, msglen, &rsa_params, stat, &key->u.rsa); + } +#endif +#ifdef LTC_MDSA + case LTC_PKA_DSA: + return dsa_verify_hash(sig, siglen, msg, msglen, stat, &key->u.dsa); +#endif +#ifdef LTC_MECC + case LTC_PKA_EC: + return ecc_verify_hash_v2(sig, siglen, msg, msglen, &ecc_opts, stat, &key->u.ecc); +#endif +#ifdef LTC_CURVE25519 + case LTC_PKA_ED25519: + return ed25519_verify(msg, msglen, sig, siglen, stat, &key->u.ed25519); +#endif + default: + return CRYPT_PK_INVALID_TYPE; + } +} + +/* RFC5280 Ch. 4.1.1.2. signatureAlgorithm + * [...] + * This field MUST contain the same algorithm identifier as the + * signature field in the sequence tbsCertificate (Section 4.1.2.3). + */ +static LTC_INLINE int s_signature_algorithms_equal(const ltc_x509_signature_algorithm *a, const ltc_x509_signature_algorithm *b) +{ + if (a->pka != b->pka) + return 0; + if (a->pka == LTC_PKA_RSA_PSS) { + if (!rsa_params_equal(&a->u.rsa_params, &b->u.rsa_params)) + return 0; + } + return 1; +} + +/** + Check whether an X.509 certificate is signed by a specific PKA key. + + @param cert The X.509 certificate to validate. + @param key The key to validate the certificate with. + @param stat [out] The result of the signature comparison: 1==valid, 0==invalid. + @return CRYPT_OK if successful +*/ +int x509_cert_is_signed_by(const ltc_x509_certificate *cert, const ltc_pka_key *key, int *stat) +{ + unsigned char buf[MAXBLOCKSIZE], *msg; + unsigned long msglen = sizeof(buf); + const char *hashalg; + pka_sig_args sig_args = {0}; + int err; + + LTC_ARGCHK(cert != NULL); + LTC_ARGCHK(key != NULL); + LTC_ARGCHK(stat != NULL); + + *stat = 0; + /* Check that signatureAlgorithms match AND the key must be appropriate. */ + if (!s_signature_algorithms_equal(&cert->signature_algorithm, &cert->tbs_certificate.signature_algorithm) + || (cert->signature_algorithm.pka != key->id)) { + return CRYPT_PK_TYPE_MISMATCH; + } + sig_args.hash_idx = -1; + if (key->id == LTC_PKA_ED25519) { + msg = cert->tbs_certificate.asn1->data; + msglen = cert->tbs_certificate.asn1->size; + } else { + if (key->id == LTC_PKA_RSA_PSS) { + if (cert->signature_algorithm.u.rsa_params.pss_oaep) { + sig_args.rsa_params = &cert->signature_algorithm.u.rsa_params; + if (key->u.rsa.params.pss_oaep && !rsa_params_equal(&key->u.rsa.params, sig_args.rsa_params)) { + return CRYPT_PK_TYPE_MISMATCH; + } + } else if (key->u.rsa.params.pss_oaep) { + sig_args.rsa_params = &key->u.rsa.params; + } else { + return CRYPT_PK_TYPE_MISMATCH; + } + hashalg = sig_args.rsa_params->hash_alg; + } else { + hashalg = cert->signature_algorithm.u.hash; + } + if ((sig_args.hash_idx = find_hash(hashalg)) == -1) { + return CRYPT_INVALID_HASH; + } + if ((err = hash_memory(sig_args.hash_idx, cert->tbs_certificate.asn1->data, cert->tbs_certificate.asn1->size, buf, &msglen)) != CRYPT_OK) { + return err; + } + msg = buf; + } + if ((err = s_pka_verify(msg, msglen, cert->signature.signature, cert->signature.signature_len/8, &sig_args, stat, key)) != CRYPT_OK) { + return err; + } + return err; +} + +/** + Compare two X.509 NAME instances. + + @param a A X.509 NAME. + @param b A X.509 NAME. + @return 1 if equal, 0 if unequal. +*/ +int x509_cmp_name(const ltc_x509_name *a, const ltc_x509_name *b) +{ + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(b != NULL); + + if (a == b) + return 1; + if (a->asn1->size != b->asn1->size) + return 0; + return XMEMCMP(a->asn1->data, b->asn1->data, a->asn1->size) == 0 ? 1 : 0; +} + +/** + Retrieve a specific component from an X.509 NAME. + + @param name A X.509 NAME. + @param type The type of the component to retrieve. + @param str The result. + @return CRYPT_OK if successful. +*/ +int x509_name_detail_get(const ltc_x509_name *name, ltc_x509_details type, const ltc_x509_string **str) +{ + unsigned long n; + + LTC_ARGCHK(name != NULL); + LTC_ARGCHK(str != NULL); + + for (n = 0; n < name->names_num; ++n) { + if (name->names[n].type == type) { + *str = &name->names[n]; + return CRYPT_OK; + } + } + *str = NULL; + return CRYPT_INVALID_ARG; +} + +const char *x509_name_detail_desc(ltc_x509_details type) +{ + const char *standard_attributes[] = { + "Serial", + "CommonName", + "CountryCode", + "Locality", + "State or Province", + "Organisation", + "OrganisationalUnit", + "EmailAddress", + }; + if (type <= LTC_X509_EMAIL) { + return standard_attributes[type]; + } + return "Unknown"; +}