aboutsummaryrefslogtreecommitdiffstats
path: root/src/tls
diff options
context:
space:
mode:
authorJouni Malinen <j@w1.fi>2015-12-13 18:52:43 (GMT)
committerJouni Malinen <j@w1.fi>2015-12-14 13:49:01 (GMT)
commit6b7bb4292393535ca79955b7e7ca7d1eea8c2034 (patch)
treea2ff662376e634d9f78dd8c9e7fe375a6871df07 /src/tls
parent5ce2941bfea59b0e2df4b996305d642792150a34 (diff)
downloadhostap-6b7bb4292393535ca79955b7e7ca7d1eea8c2034.zip
hostap-6b7bb4292393535ca79955b7e7ca7d1eea8c2034.tar.gz
hostap-6b7bb4292393535ca79955b7e7ca7d1eea8c2034.tar.bz2
TLS: Add minimal support for PKCS #12
This allows the internal TLS implementation to parse a private key and a certificate from a PKCS #12 file protected with pbeWithSHAAnd3-KeyTripleDES-CBC. Signed-off-by: Jouni Malinen <j@w1.fi>
Diffstat (limited to 'src/tls')
-rw-r--r--src/tls/tlsv1_cred.c738
1 files changed, 737 insertions, 1 deletions
diff --git a/src/tls/tlsv1_cred.c b/src/tls/tlsv1_cred.c
index 067562b..92f97c7 100644
--- a/src/tls/tlsv1_cred.c
+++ b/src/tls/tlsv1_cred.c
@@ -1,6 +1,6 @@
/*
* TLSv1 credentials
- * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2015, Jouni Malinen <j@w1.fi>
*
* 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"
@@ -325,6 +328,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)
{
@@ -333,6 +1065,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;