1214501Srpaulo/*
2214501Srpaulo * PKCS #8 (Private-key information syntax)
3214501Srpaulo * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
4214501Srpaulo *
5252726Srpaulo * This software may be distributed under the terms of the BSD license.
6252726Srpaulo * See README for more details.
7214501Srpaulo */
8214501Srpaulo
9214501Srpaulo#include "includes.h"
10214501Srpaulo
11214501Srpaulo#include "common.h"
12214501Srpaulo#include "asn1.h"
13214501Srpaulo#include "bignum.h"
14214501Srpaulo#include "rsa.h"
15214501Srpaulo#include "pkcs5.h"
16214501Srpaulo#include "pkcs8.h"
17214501Srpaulo
18214501Srpaulo
19214501Srpaulostruct crypto_private_key * pkcs8_key_import(const u8 *buf, size_t len)
20214501Srpaulo{
21214501Srpaulo	struct asn1_hdr hdr;
22214501Srpaulo	const u8 *pos, *end;
23214501Srpaulo	struct bignum *zero;
24214501Srpaulo	struct asn1_oid oid;
25214501Srpaulo	char obuf[80];
26214501Srpaulo
27214501Srpaulo	/* PKCS #8, Chapter 6 */
28214501Srpaulo
29214501Srpaulo	/* PrivateKeyInfo ::= SEQUENCE */
30214501Srpaulo	if (asn1_get_next(buf, len, &hdr) < 0 ||
31214501Srpaulo	    hdr.class != ASN1_CLASS_UNIVERSAL ||
32214501Srpaulo	    hdr.tag != ASN1_TAG_SEQUENCE) {
33214501Srpaulo		wpa_printf(MSG_DEBUG, "PKCS #8: Does not start with PKCS #8 "
34214501Srpaulo			   "header (SEQUENCE); assume PKCS #8 not used");
35214501Srpaulo		return NULL;
36214501Srpaulo	}
37214501Srpaulo	pos = hdr.payload;
38214501Srpaulo	end = pos + hdr.length;
39214501Srpaulo
40214501Srpaulo	/* version Version (Version ::= INTEGER) */
41214501Srpaulo	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
42214501Srpaulo	    hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) {
43214501Srpaulo		wpa_printf(MSG_DEBUG, "PKCS #8: Expected INTEGER - found "
44214501Srpaulo			   "class %d tag 0x%x; assume PKCS #8 not used",
45214501Srpaulo			   hdr.class, hdr.tag);
46214501Srpaulo		return NULL;
47214501Srpaulo	}
48214501Srpaulo
49214501Srpaulo	zero = bignum_init();
50214501Srpaulo	if (zero == NULL)
51214501Srpaulo		return NULL;
52214501Srpaulo
53214501Srpaulo	if (bignum_set_unsigned_bin(zero, hdr.payload, hdr.length) < 0) {
54214501Srpaulo		wpa_printf(MSG_DEBUG, "PKCS #8: Failed to parse INTEGER");
55214501Srpaulo		bignum_deinit(zero);
56214501Srpaulo		return NULL;
57214501Srpaulo	}
58214501Srpaulo	pos = hdr.payload + hdr.length;
59214501Srpaulo
60214501Srpaulo	if (bignum_cmp_d(zero, 0) != 0) {
61214501Srpaulo		wpa_printf(MSG_DEBUG, "PKCS #8: Expected zero INTEGER in the "
62214501Srpaulo			   "beginning of private key; not found; assume "
63214501Srpaulo			   "PKCS #8 not used");
64214501Srpaulo		bignum_deinit(zero);
65214501Srpaulo		return NULL;
66214501Srpaulo	}
67214501Srpaulo	bignum_deinit(zero);
68214501Srpaulo
69214501Srpaulo	/* privateKeyAlgorithm PrivateKeyAlgorithmIdentifier
70214501Srpaulo	 * (PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier) */
71214501Srpaulo	if (asn1_get_next(pos, len, &hdr) < 0 ||
72214501Srpaulo	    hdr.class != ASN1_CLASS_UNIVERSAL ||
73214501Srpaulo	    hdr.tag != ASN1_TAG_SEQUENCE) {
74214501Srpaulo		wpa_printf(MSG_DEBUG, "PKCS #8: Expected SEQUENCE "
75214501Srpaulo			   "(AlgorithmIdentifier) - found class %d tag 0x%x; "
76214501Srpaulo			   "assume PKCS #8 not used",
77214501Srpaulo			   hdr.class, hdr.tag);
78214501Srpaulo		return NULL;
79214501Srpaulo	}
80214501Srpaulo
81214501Srpaulo	if (asn1_get_oid(hdr.payload, hdr.length, &oid, &pos)) {
82214501Srpaulo		wpa_printf(MSG_DEBUG, "PKCS #8: Failed to parse OID "
83214501Srpaulo			   "(algorithm); assume PKCS #8 not used");
84214501Srpaulo		return NULL;
85214501Srpaulo	}
86214501Srpaulo
87214501Srpaulo	asn1_oid_to_str(&oid, obuf, sizeof(obuf));
88214501Srpaulo	wpa_printf(MSG_DEBUG, "PKCS #8: algorithm=%s", obuf);
89214501Srpaulo
90214501Srpaulo	if (oid.len != 7 ||
91214501Srpaulo	    oid.oid[0] != 1 /* iso */ ||
92214501Srpaulo	    oid.oid[1] != 2 /* member-body */ ||
93214501Srpaulo	    oid.oid[2] != 840 /* us */ ||
94214501Srpaulo	    oid.oid[3] != 113549 /* rsadsi */ ||
95214501Srpaulo	    oid.oid[4] != 1 /* pkcs */ ||
96214501Srpaulo	    oid.oid[5] != 1 /* pkcs-1 */ ||
97214501Srpaulo	    oid.oid[6] != 1 /* rsaEncryption */) {
98214501Srpaulo		wpa_printf(MSG_DEBUG, "PKCS #8: Unsupported private key "
99214501Srpaulo			   "algorithm %s", obuf);
100214501Srpaulo		return NULL;
101214501Srpaulo	}
102214501Srpaulo
103214501Srpaulo	pos = hdr.payload + hdr.length;
104214501Srpaulo
105214501Srpaulo	/* privateKey PrivateKey (PrivateKey ::= OCTET STRING) */
106214501Srpaulo	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
107214501Srpaulo	    hdr.class != ASN1_CLASS_UNIVERSAL ||
108214501Srpaulo	    hdr.tag != ASN1_TAG_OCTETSTRING) {
109214501Srpaulo		wpa_printf(MSG_DEBUG, "PKCS #8: Expected OCTETSTRING "
110214501Srpaulo			   "(privateKey) - found class %d tag 0x%x",
111214501Srpaulo			   hdr.class, hdr.tag);
112214501Srpaulo		return NULL;
113214501Srpaulo	}
114214501Srpaulo	wpa_printf(MSG_DEBUG, "PKCS #8: Try to parse RSAPrivateKey");
115214501Srpaulo
116214501Srpaulo	return (struct crypto_private_key *)
117214501Srpaulo		crypto_rsa_import_private_key(hdr.payload, hdr.length);
118214501Srpaulo}
119214501Srpaulo
120214501Srpaulo
121214501Srpaulostruct crypto_private_key *
122214501Srpaulopkcs8_enc_key_import(const u8 *buf, size_t len, const char *passwd)
123214501Srpaulo{
124214501Srpaulo	struct asn1_hdr hdr;
125214501Srpaulo	const u8 *pos, *end, *enc_alg;
126214501Srpaulo	size_t enc_alg_len;
127214501Srpaulo	u8 *data;
128214501Srpaulo	size_t data_len;
129214501Srpaulo
130214501Srpaulo	if (passwd == NULL)
131214501Srpaulo		return NULL;
132214501Srpaulo
133214501Srpaulo	/*
134214501Srpaulo	 * PKCS #8, Chapter 7
135214501Srpaulo	 * EncryptedPrivateKeyInfo ::= SEQUENCE {
136214501Srpaulo	 *   encryptionAlgorithm EncryptionAlgorithmIdentifier,
137214501Srpaulo	 *   encryptedData EncryptedData }
138214501Srpaulo	 * EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
139214501Srpaulo	 * EncryptedData ::= OCTET STRING
140214501Srpaulo	 */
141214501Srpaulo
142214501Srpaulo	if (asn1_get_next(buf, len, &hdr) < 0 ||
143214501Srpaulo	    hdr.class != ASN1_CLASS_UNIVERSAL ||
144214501Srpaulo	    hdr.tag != ASN1_TAG_SEQUENCE) {
145214501Srpaulo		wpa_printf(MSG_DEBUG, "PKCS #8: Does not start with PKCS #8 "
146214501Srpaulo			   "header (SEQUENCE); assume encrypted PKCS #8 not "
147214501Srpaulo			   "used");
148214501Srpaulo		return NULL;
149214501Srpaulo	}
150214501Srpaulo	pos = hdr.payload;
151214501Srpaulo	end = pos + hdr.length;
152214501Srpaulo
153214501Srpaulo	/* encryptionAlgorithm EncryptionAlgorithmIdentifier */
154214501Srpaulo	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
155214501Srpaulo	    hdr.class != ASN1_CLASS_UNIVERSAL ||
156214501Srpaulo	    hdr.tag != ASN1_TAG_SEQUENCE) {
157214501Srpaulo		wpa_printf(MSG_DEBUG, "PKCS #8: Expected SEQUENCE "
158214501Srpaulo			   "(AlgorithmIdentifier) - found class %d tag 0x%x; "
159214501Srpaulo			   "assume encrypted PKCS #8 not used",
160214501Srpaulo			   hdr.class, hdr.tag);
161214501Srpaulo		return NULL;
162214501Srpaulo	}
163214501Srpaulo	enc_alg = hdr.payload;
164214501Srpaulo	enc_alg_len = hdr.length;
165214501Srpaulo	pos = hdr.payload + hdr.length;
166214501Srpaulo
167214501Srpaulo	/* encryptedData EncryptedData */
168214501Srpaulo	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
169214501Srpaulo	    hdr.class != ASN1_CLASS_UNIVERSAL ||
170214501Srpaulo	    hdr.tag != ASN1_TAG_OCTETSTRING) {
171214501Srpaulo		wpa_printf(MSG_DEBUG, "PKCS #8: Expected OCTETSTRING "
172214501Srpaulo			   "(encryptedData) - found class %d tag 0x%x",
173214501Srpaulo			   hdr.class, hdr.tag);
174214501Srpaulo		return NULL;
175214501Srpaulo	}
176214501Srpaulo
177214501Srpaulo	data = pkcs5_decrypt(enc_alg, enc_alg_len, hdr.payload, hdr.length,
178214501Srpaulo			     passwd, &data_len);
179214501Srpaulo	if (data) {
180214501Srpaulo		struct crypto_private_key *key;
181214501Srpaulo		key = pkcs8_key_import(data, data_len);
182214501Srpaulo		os_free(data);
183214501Srpaulo		return key;
184214501Srpaulo	}
185214501Srpaulo
186214501Srpaulo	return NULL;
187214501Srpaulo}
188