X-Git-Url: http://www.project-moonshot.org/gitweb/?p=mech_eap.git;a=blobdiff_plain;f=libeap%2Fsrc%2Ftls%2Ftlsv1_cred.c;fp=libeap%2Fsrc%2Ftls%2Ftlsv1_cred.c;h=52c1ae0143da3cb82a173beefaa673e34c4b4bf8;hp=1ea6827b898ec16f95514eb1e89226ae1bd88e78;hb=d1dd9aae6741e74f20bfc35e1db598652680279d;hpb=bd3bd69af16ab99706ba70ed11a3e291e968e5c6 diff --git a/libeap/src/tls/tlsv1_cred.c b/libeap/src/tls/tlsv1_cred.c index 1ea6827..52c1ae0 100644 --- a/libeap/src/tls/tlsv1_cred.c +++ b/libeap/src/tls/tlsv1_cred.c @@ -1,6 +1,6 @@ /* * TLSv1 credentials - * Copyright (c) 2006-2009, Jouni Malinen + * Copyright (c) 2006-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -11,6 +11,9 @@ #include "common.h" #include "base64.h" #include "crypto/crypto.h" +#include "crypto/sha1.h" +#include "pkcs5.h" +#include "pkcs8.h" #include "x509v3.h" #include "tlsv1_cred.h" @@ -33,6 +36,8 @@ void tlsv1_cred_free(struct tlsv1_credentials *cred) crypto_private_key_free(cred->key); os_free(cred->dh_p); os_free(cred->dh_g); + os_free(cred->ocsp_stapling_response); + os_free(cred->ocsp_stapling_response_multi); os_free(cred); } @@ -190,6 +195,43 @@ int tlsv1_set_ca_cert(struct tlsv1_credentials *cred, const char *cert, const u8 *cert_blob, size_t cert_blob_len, const char *path) { + if (cert && os_strncmp(cert, "hash://", 7) == 0) { + const char *pos = cert + 7; + if (os_strncmp(pos, "server/sha256/", 14) != 0) { + wpa_printf(MSG_DEBUG, + "TLSv1: Unsupported ca_cert hash value '%s'", + cert); + return -1; + } + pos += 14; + if (os_strlen(pos) != 32 * 2) { + wpa_printf(MSG_DEBUG, + "TLSv1: Unexpected SHA256 hash length in ca_cert '%s'", + cert); + return -1; + } + if (hexstr2bin(pos, cred->srv_cert_hash, 32) < 0) { + wpa_printf(MSG_DEBUG, + "TLSv1: Invalid SHA256 hash value in ca_cert '%s'", + cert); + return -1; + } + cred->server_cert_only = 1; + cred->ca_cert_verify = 0; + wpa_printf(MSG_DEBUG, + "TLSv1: Checking only server certificate match"); + return 0; + } + + if (cert && os_strncmp(cert, "probe://", 8) == 0) { + cred->cert_probe = 1; + cred->ca_cert_verify = 0; + wpa_printf(MSG_DEBUG, "TLSv1: Only probe server certificate"); + return 0; + } + + cred->ca_cert_verify = cert || cert_blob || path; + if (tlsv1_set_cert_chain(&cred->trusted_certs, cert, cert_blob, cert_blob_len) < 0) return -1; @@ -288,6 +330,735 @@ static struct crypto_private_key * tlsv1_set_key_enc_pem(const u8 *key, } +#ifdef PKCS12_FUNCS + +static int oid_is_rsadsi(struct asn1_oid *oid) +{ + return oid->len >= 4 && + oid->oid[0] == 1 /* iso */ && + oid->oid[1] == 2 /* member-body */ && + oid->oid[2] == 840 /* us */ && + oid->oid[3] == 113549 /* rsadsi */; +} + + +static int pkcs12_is_bagtype_oid(struct asn1_oid *oid, unsigned long type) +{ + return oid->len == 9 && + oid_is_rsadsi(oid) && + oid->oid[4] == 1 /* pkcs */ && + oid->oid[5] == 12 /* pkcs-12 */ && + oid->oid[6] == 10 && + oid->oid[7] == 1 /* bagtypes */ && + oid->oid[8] == type; +} + + +static int is_oid_pkcs7(struct asn1_oid *oid) +{ + return oid->len == 7 && + oid->oid[0] == 1 /* iso */ && + oid->oid[1] == 2 /* member-body */ && + oid->oid[2] == 840 /* us */ && + oid->oid[3] == 113549 /* rsadsi */ && + oid->oid[4] == 1 /* pkcs */ && + oid->oid[5] == 7 /* pkcs-7 */; +} + + +static int is_oid_pkcs7_data(struct asn1_oid *oid) +{ + return is_oid_pkcs7(oid) && oid->oid[6] == 1 /* data */; +} + + +static int is_oid_pkcs7_enc_data(struct asn1_oid *oid) +{ + return is_oid_pkcs7(oid) && oid->oid[6] == 6 /* encryptedData */; +} + + +static int is_oid_pkcs9(struct asn1_oid *oid) +{ + return oid->len >= 6 && + oid->oid[0] == 1 /* iso */ && + oid->oid[1] == 2 /* member-body */ && + oid->oid[2] == 840 /* us */ && + oid->oid[3] == 113549 /* rsadsi */ && + oid->oid[4] == 1 /* pkcs */ && + oid->oid[5] == 9 /* pkcs-9 */; +} + + +static int is_oid_pkcs9_friendly_name(struct asn1_oid *oid) +{ + return oid->len == 7 && is_oid_pkcs9(oid) && + oid->oid[6] == 20; +} + + +static int is_oid_pkcs9_local_key_id(struct asn1_oid *oid) +{ + return oid->len == 7 && is_oid_pkcs9(oid) && + oid->oid[6] == 21; +} + + +static int is_oid_pkcs9_x509_cert(struct asn1_oid *oid) +{ + return oid->len == 8 && is_oid_pkcs9(oid) && + oid->oid[6] == 22 /* certTypes */ && + oid->oid[7] == 1 /* x509Certificate */; +} + + +static int pkcs12_keybag(struct tlsv1_credentials *cred, + const u8 *buf, size_t len) +{ + /* TODO */ + return 0; +} + + +static int pkcs12_pkcs8_keybag(struct tlsv1_credentials *cred, + const u8 *buf, size_t len, + const char *passwd) +{ + struct crypto_private_key *key; + + /* PKCS8ShroudedKeyBag ::= EncryptedPrivateKeyInfo */ + key = pkcs8_enc_key_import(buf, len, passwd); + if (!key) + return -1; + + wpa_printf(MSG_DEBUG, + "PKCS #12: Successfully decrypted PKCS8ShroudedKeyBag"); + crypto_private_key_free(cred->key); + cred->key = key; + + return 0; +} + + +static int pkcs12_certbag(struct tlsv1_credentials *cred, + const u8 *buf, size_t len) +{ + struct asn1_hdr hdr; + struct asn1_oid oid; + char obuf[80]; + const u8 *pos, *end; + + /* + * CertBag ::= SEQUENCE { + * certId BAG-TYPE.&id ({CertTypes}), + * certValue [0] EXPLICIT BAG-TYPE.&Type ({CertTypes}{@certId}) + * } + */ + + if (asn1_get_next(buf, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected SEQUENCE (CertBag) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + + pos = hdr.payload; + end = hdr.payload + hdr.length; + + if (asn1_get_oid(pos, end - pos, &oid, &pos)) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Failed to parse OID (certId)"); + return -1; + } + + asn1_oid_to_str(&oid, obuf, sizeof(obuf)); + wpa_printf(MSG_DEBUG, "PKCS #12: certId %s", obuf); + + if (!is_oid_pkcs9_x509_cert(&oid)) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Ignored unsupported certificate type (certId %s)", + obuf); + } + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || + hdr.tag != 0) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected [0] EXPLICIT (certValue) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + + if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_OCTETSTRING) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected OCTET STRING (x509Certificate) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + + wpa_hexdump(MSG_DEBUG, "PKCS #12: x509Certificate", + hdr.payload, hdr.length); + if (cred->cert) { + struct x509_certificate *cert; + + wpa_printf(MSG_DEBUG, "PKCS #12: Ignore extra certificate"); + cert = x509_certificate_parse(hdr.payload, hdr.length); + if (!cert) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Failed to parse x509Certificate"); + return 0; + } + x509_certificate_chain_free(cert); + + return 0; + } + return tlsv1_set_cert(cred, NULL, hdr.payload, hdr.length); +} + + +static int pkcs12_parse_attr_friendly_name(const u8 *pos, const u8 *end) +{ + struct asn1_hdr hdr; + + /* + * RFC 2985, 5.5.1: + * friendlyName ATTRIBUTE ::= { + * WITH SYNTAX BMPString (SIZE(1..pkcs-9-ub-friendlyName)) + * EQUALITY MATCHING RULE caseIgnoreMatch + * SINGLE VALUE TRUE + * ID pkcs-9-at-friendlyName + * } + */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_BMPSTRING) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected BMPSTRING (friendlyName) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return 0; + } + wpa_hexdump_ascii(MSG_DEBUG, "PKCS #12: friendlyName", + hdr.payload, hdr.length); + return 0; +} + + +static int pkcs12_parse_attr_local_key_id(const u8 *pos, const u8 *end) +{ + struct asn1_hdr hdr; + + /* + * RFC 2985, 5.5.2: + * localKeyId ATTRIBUTE ::= { + * WITH SYNTAX OCTET STRING + * EQUALITY MATCHING RULE octetStringMatch + * SINGLE VALUE TRUE + * ID pkcs-9-at-localKeyId + * } + */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_OCTETSTRING) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected OCTET STRING (localKeyID) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + wpa_hexdump_key(MSG_DEBUG, "PKCS #12: localKeyID", + hdr.payload, hdr.length); + return 0; +} + + +static int pkcs12_parse_attr(const u8 *pos, size_t len) +{ + const u8 *end = pos + len; + struct asn1_hdr hdr; + struct asn1_oid a_oid; + char obuf[80]; + + /* + * PKCS12Attribute ::= SEQUENCE { + * attrId ATTRIBUTE.&id ({PKCS12AttrSet}), + * attrValues SET OF ATTRIBUTE.&Type ({PKCS12AttrSet}{@attrId}) + * } + */ + + if (asn1_get_oid(pos, end - pos, &a_oid, &pos)) { + wpa_printf(MSG_DEBUG, "PKCS #12: Failed to parse OID (attrId)"); + return -1; + } + + asn1_oid_to_str(&a_oid, obuf, sizeof(obuf)); + wpa_printf(MSG_DEBUG, "PKCS #12: attrId %s", obuf); + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SET) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected SET (attrValues) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + wpa_hexdump_key(MSG_MSGDUMP, "PKCS #12: attrValues", + hdr.payload, hdr.length); + pos = hdr.payload; + end = hdr.payload + hdr.length; + + if (is_oid_pkcs9_friendly_name(&a_oid)) + return pkcs12_parse_attr_friendly_name(pos, end); + if (is_oid_pkcs9_local_key_id(&a_oid)) + return pkcs12_parse_attr_local_key_id(pos, end); + + wpa_printf(MSG_DEBUG, "PKCS #12: Ignore unknown attribute"); + return 0; +} + + +static int pkcs12_safebag(struct tlsv1_credentials *cred, + const u8 *buf, size_t len, const char *passwd) +{ + struct asn1_hdr hdr; + struct asn1_oid oid; + char obuf[80]; + const u8 *pos = buf, *end = buf + len; + const u8 *value; + size_t value_len; + + wpa_hexdump_key(MSG_MSGDUMP, "PKCS #12: SafeBag", buf, len); + + /* BAG-TYPE ::= TYPE-IDENTIFIER */ + if (asn1_get_oid(pos, end - pos, &oid, &pos)) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Failed to parse OID (BAG-TYPE)"); + return -1; + } + + asn1_oid_to_str(&oid, obuf, sizeof(obuf)); + wpa_printf(MSG_DEBUG, "PKCS #12: BAG-TYPE %s", obuf); + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || + hdr.tag != 0) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected [0] EXPLICIT (bagValue) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return 0; + } + value = hdr.payload; + value_len = hdr.length; + wpa_hexdump_key(MSG_MSGDUMP, "PKCS #12: bagValue", value, value_len); + pos = hdr.payload + hdr.length; + + if (pos < end) { + /* bagAttributes SET OF PKCS12Attribute OPTIONAL */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SET) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected SET (bagAttributes) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + wpa_hexdump_key(MSG_MSGDUMP, "PKCS #12: bagAttributes", + hdr.payload, hdr.length); + + pos = hdr.payload; + end = hdr.payload + hdr.length; + while (pos < end) { + /* PKCS12Attribute ::= SEQUENCE */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected SEQUENCE (PKCS12Attribute) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + if (pkcs12_parse_attr(hdr.payload, hdr.length) < 0) + return -1; + pos = hdr.payload + hdr.length; + } + } + + if (pkcs12_is_bagtype_oid(&oid, 1)) + return pkcs12_keybag(cred, value, value_len); + if (pkcs12_is_bagtype_oid(&oid, 2)) + return pkcs12_pkcs8_keybag(cred, value, value_len, passwd); + if (pkcs12_is_bagtype_oid(&oid, 3)) + return pkcs12_certbag(cred, value, value_len); + + wpa_printf(MSG_DEBUG, "PKCS #12: Ignore unsupported BAG-TYPE"); + return 0; +} + + +static int pkcs12_safecontents(struct tlsv1_credentials *cred, + const u8 *buf, size_t len, + const char *passwd) +{ + struct asn1_hdr hdr; + const u8 *pos, *end; + + /* SafeContents ::= SEQUENCE OF SafeBag */ + if (asn1_get_next(buf, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected SEQUENCE (SafeContents) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + pos = hdr.payload; + end = hdr.payload + hdr.length; + + /* + * SafeBag ::= SEQUENCE { + * bagId BAG-TYPE.&id ({PKCS12BagSet}) + * bagValue [0] EXPLICIT BAG-TYPE.&Type({PKCS12BagSet}{@bagId}), + * bagAttributes SET OF PKCS12Attribute OPTIONAL + * } + */ + + while (pos < end) { + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected SEQUENCE (SafeBag) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + if (pkcs12_safebag(cred, hdr.payload, hdr.length, passwd) < 0) + return -1; + pos = hdr.payload + hdr.length; + } + + return 0; +} + + +static int pkcs12_parse_content_data(struct tlsv1_credentials *cred, + const u8 *pos, const u8 *end, + const char *passwd) +{ + struct asn1_hdr hdr; + + /* Data ::= OCTET STRING */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_OCTETSTRING) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected OCTET STRING (Data) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + + wpa_hexdump(MSG_MSGDUMP, "PKCS #12: Data", hdr.payload, hdr.length); + + return pkcs12_safecontents(cred, hdr.payload, hdr.length, passwd); +} + + +static int pkcs12_parse_content_enc_data(struct tlsv1_credentials *cred, + const u8 *pos, const u8 *end, + const char *passwd) +{ + struct asn1_hdr hdr; + struct asn1_oid oid; + char buf[80]; + const u8 *enc_alg; + u8 *data; + size_t enc_alg_len, data_len; + int res = -1; + + /* + * EncryptedData ::= SEQUENCE { + * version Version, + * encryptedContentInfo EncryptedContentInfo } + */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected SEQUENCE (EncryptedData) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return 0; + } + pos = hdr.payload; + + /* Version ::= INTEGER */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) { + wpa_printf(MSG_DEBUG, + "PKCS #12: No INTEGER tag found for version; class=%d tag=0x%x", + hdr.class, hdr.tag); + return -1; + } + if (hdr.length != 1 || hdr.payload[0] != 0) { + wpa_printf(MSG_DEBUG, "PKCS #12: Unrecognized PKCS #7 version"); + return -1; + } + pos = hdr.payload + hdr.length; + + wpa_hexdump(MSG_MSGDUMP, "PKCS #12: EncryptedContentInfo", + pos, end - pos); + + /* + * EncryptedContentInfo ::= SEQUENCE { + * contentType ContentType, + * contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier, + * encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL } + */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected SEQUENCE (EncryptedContentInfo) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + + pos = hdr.payload; + end = pos + hdr.length; + + /* ContentType ::= OBJECT IDENTIFIER */ + if (asn1_get_oid(pos, end - pos, &oid, &pos)) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Could not find OBJECT IDENTIFIER (contentType)"); + return -1; + } + asn1_oid_to_str(&oid, buf, sizeof(buf)); + wpa_printf(MSG_DEBUG, "PKCS #12: EncryptedContentInfo::contentType %s", + buf); + + if (!is_oid_pkcs7_data(&oid)) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Unsupported EncryptedContentInfo::contentType %s", + buf); + return 0; + } + + /* ContentEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "PKCS #12: Expected SEQUENCE (ContentEncryptionAlgorithmIdentifier) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + enc_alg = hdr.payload; + enc_alg_len = hdr.length; + pos = hdr.payload + hdr.length; + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || + hdr.tag != 0) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected [0] IMPLICIT (encryptedContent) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + + /* EncryptedContent ::= OCTET STRING */ + data = pkcs5_decrypt(enc_alg, enc_alg_len, hdr.payload, hdr.length, + passwd, &data_len); + if (data) { + wpa_hexdump_key(MSG_MSGDUMP, + "PKCS #12: Decrypted encryptedContent", + data, data_len); + res = pkcs12_safecontents(cred, data, data_len, passwd); + os_free(data); + } + + return res; +} + + +static int pkcs12_parse_content(struct tlsv1_credentials *cred, + const u8 *buf, size_t len, + const char *passwd) +{ + const u8 *pos = buf; + const u8 *end = buf + len; + struct asn1_oid oid; + char txt[80]; + struct asn1_hdr hdr; + + wpa_hexdump(MSG_MSGDUMP, "PKCS #12: ContentInfo", buf, len); + + if (asn1_get_oid(pos, end - pos, &oid, &pos)) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Could not find OBJECT IDENTIFIER (contentType)"); + return 0; + } + + asn1_oid_to_str(&oid, txt, sizeof(txt)); + wpa_printf(MSG_DEBUG, "PKCS #12: contentType %s", txt); + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || + hdr.tag != 0) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected [0] EXPLICIT (content) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return 0; + } + pos = hdr.payload; + + if (is_oid_pkcs7_data(&oid)) + return pkcs12_parse_content_data(cred, pos, end, passwd); + if (is_oid_pkcs7_enc_data(&oid)) + return pkcs12_parse_content_enc_data(cred, pos, end, passwd); + + wpa_printf(MSG_DEBUG, "PKCS #12: Ignored unsupported contentType %s", + txt); + + return 0; +} + + +static int pkcs12_parse(struct tlsv1_credentials *cred, + const u8 *key, size_t len, const char *passwd) +{ + struct asn1_hdr hdr; + const u8 *pos, *end; + struct asn1_oid oid; + char buf[80]; + + /* + * PFX ::= SEQUENCE { + * version INTEGER {v3(3)}(v3,...), + * authSafe ContentInfo, + * macData MacData OPTIONAL + * } + */ + + if (asn1_get_next(key, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected SEQUENCE (PFX) - found class %d tag 0x%x; assume PKCS #12 not used", + hdr.class, hdr.tag); + return -1; + } + + pos = hdr.payload; + end = pos + hdr.length; + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) { + wpa_printf(MSG_DEBUG, + "PKCS #12: No INTEGER tag found for version; class=%d tag=0x%x", + hdr.class, hdr.tag); + return -1; + } + if (hdr.length != 1 || hdr.payload[0] != 3) { + wpa_printf(MSG_DEBUG, "PKCS #12: Unrecognized version"); + return -1; + } + pos = hdr.payload + hdr.length; + + /* + * ContentInfo ::= SEQUENCE { + * contentType ContentType, + * content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL } + */ + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected SEQUENCE (authSafe) - found class %d tag 0x%x; assume PKCS #12 not used", + hdr.class, hdr.tag); + return -1; + } + + pos = hdr.payload; + end = pos + hdr.length; + + /* ContentType ::= OBJECT IDENTIFIER */ + if (asn1_get_oid(pos, end - pos, &oid, &pos)) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Could not find OBJECT IDENTIFIER (contentType); assume PKCS #12 not used"); + return -1; + } + asn1_oid_to_str(&oid, buf, sizeof(buf)); + wpa_printf(MSG_DEBUG, "PKCS #12: contentType %s", buf); + if (!is_oid_pkcs7_data(&oid)) { + wpa_printf(MSG_DEBUG, "PKCS #12: Unsupported contentType %s", + buf); + return -1; + } + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || + hdr.tag != 0) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected [0] EXPLICIT (content) - found class %d tag 0x%x; assume PKCS #12 not used", + hdr.class, hdr.tag); + return -1; + } + + pos = hdr.payload; + + /* Data ::= OCTET STRING */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_OCTETSTRING) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected OCTET STRING (Data) - found class %d tag 0x%x; assume PKCS #12 not used", + hdr.class, hdr.tag); + return -1; + } + + /* + * AuthenticatedSafe ::= SEQUENCE OF ContentInfo + * -- Data if unencrypted + * -- EncryptedData if password-encrypted + * -- EnvelopedData if public key-encrypted + */ + wpa_hexdump(MSG_MSGDUMP, "PKCS #12: Data content", + hdr.payload, hdr.length); + + if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected SEQUENCE within Data content - found class %d tag 0x%x; assume PKCS #12 not used", + hdr.class, hdr.tag); + return -1; + } + + pos = hdr.payload; + end = pos + hdr.length; + + while (end > pos) { + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected SEQUENCE (ContentInfo) - found class %d tag 0x%x; assume PKCS #12 not used", + hdr.class, hdr.tag); + return -1; + } + if (pkcs12_parse_content(cred, hdr.payload, hdr.length, + passwd) < 0) + return -1; + + pos = hdr.payload + hdr.length; + } + + return 0; +} + +#endif /* PKCS12_FUNCS */ + + static int tlsv1_set_key(struct tlsv1_credentials *cred, const u8 *key, size_t len, const char *passwd) { @@ -296,6 +1067,10 @@ static int tlsv1_set_key(struct tlsv1_credentials *cred, cred->key = tlsv1_set_key_pem(key, len); if (cred->key == NULL) cred->key = tlsv1_set_key_enc_pem(key, len, passwd); +#ifdef PKCS12_FUNCS + if (!cred->key) + pkcs12_parse(cred, key, len, passwd); +#endif /* PKCS12_FUNCS */ if (cred->key == NULL) { wpa_printf(MSG_INFO, "TLSv1: Failed to parse private key"); return -1;