pkcs5.c revision 214501
1139823Simp/*
254263Sshin * PKCS #5 (Password-based Encryption)
354263Sshin * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
454263Sshin *
554263Sshin * This program is free software; you can redistribute it and/or modify
654263Sshin * it under the terms of the GNU General Public License version 2 as
754263Sshin * published by the Free Software Foundation.
854263Sshin *
954263Sshin * Alternatively, this software may be distributed under the terms of BSD
1054263Sshin * license.
1154263Sshin *
1254263Sshin * See README and COPYING for more details.
1354263Sshin */
1454263Sshin
1554263Sshin#include "includes.h"
1654263Sshin
1754263Sshin#include "common.h"
1854263Sshin#include "crypto/crypto.h"
1954263Sshin#include "crypto/md5.h"
2054263Sshin#include "asn1.h"
2154263Sshin#include "pkcs5.h"
2254263Sshin
2354263Sshin
2454263Sshinstruct pkcs5_params {
2554263Sshin	enum pkcs5_alg {
2654263Sshin		PKCS5_ALG_UNKNOWN,
2754263Sshin		PKCS5_ALG_MD5_DES_CBC
28273087Sae	} alg;
29273087Sae	u8 salt[8];
3054263Sshin	size_t salt_len;
3154263Sshin	unsigned int iter_count;
32172467Ssilby};
33172467Ssilby
34172467Ssilby
3562587Sitojunenum pkcs5_alg pkcs5_get_alg(struct asn1_oid *oid)
3654263Sshin{
3754263Sshin	if (oid->len == 7 &&
3854263Sshin	    oid->oid[0] == 1 /* iso */ &&
39273087Sae	    oid->oid[1] == 2 /* member-body */ &&
40273087Sae	    oid->oid[2] == 840 /* us */ &&
4154263Sshin	    oid->oid[3] == 113549 /* rsadsi */ &&
4254263Sshin	    oid->oid[4] == 1 /* pkcs */ &&
4354263Sshin	    oid->oid[5] == 5 /* pkcs-5 */ &&
4454263Sshin	    oid->oid[6] == 3 /* pbeWithMD5AndDES-CBC */)
4554263Sshin		return PKCS5_ALG_MD5_DES_CBC;
4654263Sshin
4754263Sshin	return PKCS5_ALG_UNKNOWN;
48105293Sume}
4962587Sitojun
5062587Sitojun
5154263Sshinstatic int pkcs5_get_params(const u8 *enc_alg, size_t enc_alg_len,
52257176Sglebius			    struct pkcs5_params *params)
5354263Sshin{
54196019Srwatson	struct asn1_hdr hdr;
5554263Sshin	const u8 *enc_alg_end, *pos, *end;
5654263Sshin	struct asn1_oid oid;
5754263Sshin	char obuf[80];
5854263Sshin
5954263Sshin	/* AlgorithmIdentifier */
6062587Sitojun
6162587Sitojun	enc_alg_end = enc_alg + enc_alg_len;
6255009Sshin
6362587Sitojun	os_memset(params, 0, sizeof(*params));
6454263Sshin
6562587Sitojun	if (asn1_get_oid(enc_alg, enc_alg_end - enc_alg, &oid, &pos)) {
6654263Sshin		wpa_printf(MSG_DEBUG, "PKCS #5: Failed to parse OID "
6754263Sshin			   "(algorithm)");
68273087Sae		return -1;
6954263Sshin	}
70105293Sume
71105293Sume	asn1_oid_to_str(&oid, obuf, sizeof(obuf));
72276148Sae	wpa_printf(MSG_DEBUG, "PKCS #5: encryption algorithm %s", obuf);
73105293Sume	params->alg = pkcs5_get_alg(&oid);
74105293Sume	if (params->alg == PKCS5_ALG_UNKNOWN) {
75276148Sae		wpa_printf(MSG_INFO, "PKCS #5: unsupported encryption "
76152242Sru			   "algorithm %s", obuf);
77152242Sru		return -1;
78152242Sru	}
79152242Sru
80152242Sru	/*
81270008Skevlo	 * PKCS#5, Section 8
82152242Sru	 * PBEParameter ::= SEQUENCE {
83152242Sru	 *   salt OCTET STRING SIZE(8),
84105293Sume	 *   iterationCount INTEGER }
85105293Sume	 */
86276148Sae
87276148Sae	if (asn1_get_next(pos, enc_alg_end - pos, &hdr) < 0 ||
88207369Sbz	    hdr.class != ASN1_CLASS_UNIVERSAL ||
89274225Sglebius	    hdr.tag != ASN1_TAG_SEQUENCE) {
90195699Srwatson		wpa_printf(MSG_DEBUG, "PKCS #5: Expected SEQUENCE "
9154263Sshin			   "(PBEParameter) - found class %d tag 0x%x",
9254263Sshin			   hdr.class, hdr.tag);
93273087Sae		return -1;
9454263Sshin	}
95273087Sae	pos = hdr.payload;
96147256Sbrooks	end = hdr.payload + hdr.length;
97273087Sae
98273087Sae	/* salt OCTET STRING SIZE(8) */
9954263Sshin	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
10054263Sshin	    hdr.class != ASN1_CLASS_UNIVERSAL ||
101189494Smarius	    hdr.tag != ASN1_TAG_OCTETSTRING ||
102189494Smarius	    hdr.length != 8) {
103273087Sae		wpa_printf(MSG_DEBUG, "PKCS #5: Expected OCTETSTRING SIZE(8) "
104189494Smarius			   "(salt) - found class %d tag 0x%x size %d",
105189494Smarius			   hdr.class, hdr.tag, hdr.length);
106243882Sglebius		return -1;
107273087Sae	}
108273087Sae	pos = hdr.payload + hdr.length;
109189494Smarius	os_memcpy(params->salt, hdr.payload, hdr.length);
110273087Sae	params->salt_len = hdr.length;
111189494Smarius	wpa_hexdump(MSG_DEBUG, "PKCS #5: salt",
112189494Smarius		    params->salt, params->salt_len);
113189494Smarius
114189494Smarius	/* iterationCount INTEGER */
115189494Smarius	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
116189494Smarius	    hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) {
117189494Smarius		wpa_printf(MSG_DEBUG, "PKCS #5: Expected INTEGER - found "
118273087Sae			   "class %d tag 0x%x", hdr.class, hdr.tag);
119273087Sae		return -1;
120273087Sae	}
121273087Sae	if (hdr.length == 1)
122273087Sae		params->iter_count = *hdr.payload;
123273087Sae	else if (hdr.length == 2)
12454263Sshin		params->iter_count = WPA_GET_BE16(hdr.payload);
125273087Sae	else if (hdr.length == 4)
126273087Sae		params->iter_count = WPA_GET_BE32(hdr.payload);
12754263Sshin	else {
128273087Sae		wpa_hexdump(MSG_DEBUG, "PKCS #5: Unsupported INTEGER value "
129273087Sae			    " (iterationCount)",
130273087Sae			    hdr.payload, hdr.length);
131273087Sae		return -1;
132273087Sae	}
13362587Sitojun	wpa_printf(MSG_DEBUG, "PKCS #5: iterationCount=0x%x",
134273087Sae		   params->iter_count);
13554263Sshin	if (params->iter_count == 0 || params->iter_count > 0xffff) {
13654263Sshin		wpa_printf(MSG_INFO, "PKCS #5: Unsupported "
137276148Sae			   "iterationCount=0x%x", params->iter_count);
138269699Skevlo		return -1;
13954263Sshin	}
140273087Sae
141147503Sbz	return 0;
142273087Sae}
14354263Sshin
144273087Sae
14554263Sshinstatic struct crypto_cipher * pkcs5_crypto_init(struct pkcs5_params *params,
146273087Sae						const char *passwd)
147147503Sbz{
148147503Sbz	unsigned int i;
149196039Srwatson	u8 hash[MD5_MAC_LEN];
150269699Skevlo	const u8 *addr[2];
151147503Sbz	size_t len[2];
152147503Sbz
153273087Sae	if (params->alg != PKCS5_ALG_MD5_DES_CBC)
154273087Sae		return NULL;
155273087Sae
156273087Sae	addr[0] = (const u8 *) passwd;
157273087Sae	len[0] = os_strlen(passwd);
158273087Sae	addr[1] = params->salt;
15954263Sshin	len[1] = params->salt_len;
160196039Srwatson	if (md5_vector(2, addr, len, hash) < 0)
16154263Sshin		return NULL;
162269699Skevlo	addr[0] = hash;
16354263Sshin	len[0] = MD5_MAC_LEN;
16462587Sitojun	for (i = 1; i < params->iter_count; i++) {
16562587Sitojun		if (md5_vector(1, addr, len, hash) < 0)
166105293Sume			return NULL;
16762587Sitojun	}
168105293Sume	/* TODO: DES key parity bits(?) */
169169454Srwatson	wpa_hexdump_key(MSG_DEBUG, "PKCS #5: DES key", hash, 8);
17062587Sitojun	wpa_hexdump_key(MSG_DEBUG, "PKCS #5: DES IV", hash + 8, 8);
171282965Sae
17262587Sitojun	return crypto_cipher_init(CRYPTO_CIPHER_ALG_DES, hash + 8, hash, 8);
173273087Sae}
17462587Sitojun
17562587Sitojun
176282965Saeu8 * pkcs5_decrypt(const u8 *enc_alg, size_t enc_alg_len,
177273087Sae		   const u8 *enc_data, size_t enc_data_len,
178282965Sae		   const char *passwd, size_t *data_len)
179282965Sae{
180282965Sae	struct crypto_cipher *ctx;
181282965Sae	u8 *eb, pad;
182282965Sae	struct pkcs5_params params;
183282965Sae	unsigned int i;
18462587Sitojun
18562587Sitojun	if (pkcs5_get_params(enc_alg, enc_alg_len, &params) < 0) {
186105293Sume		wpa_printf(MSG_DEBUG, "PKCS #5: Unsupported parameters");
187273087Sae		return NULL;
188105293Sume	}
189273087Sae
190273087Sae	ctx = pkcs5_crypto_init(&params, passwd);
191273087Sae	if (ctx == NULL) {
192273087Sae		wpa_printf(MSG_DEBUG, "PKCS #5: Failed to initialize crypto");
19362587Sitojun		return NULL;
194194951Srwatson	}
19562587Sitojun
196147256Sbrooks	/* PKCS #5, Section 7 - Decryption process */
19762587Sitojun	if (enc_data_len < 16 || enc_data_len % 8) {
19862587Sitojun		wpa_printf(MSG_INFO, "PKCS #5: invalid length of ciphertext "
19962587Sitojun			   "%d", (int) enc_data_len);
20062587Sitojun		crypto_cipher_deinit(ctx);
20162587Sitojun		return NULL;
20262587Sitojun	}
203105293Sume
204178888Sjulian	eb = os_malloc(enc_data_len);
205178888Sjulian	if (eb == NULL) {
206178888Sjulian		crypto_cipher_deinit(ctx);
207105293Sume		return NULL;
20878064Sume	}
209172307Scsjp
210273087Sae	if (crypto_cipher_decrypt(ctx, enc_data, eb, enc_data_len) < 0) {
21162587Sitojun		wpa_printf(MSG_DEBUG, "PKCS #5: Failed to decrypt EB");
212172307Scsjp		crypto_cipher_deinit(ctx);
21362587Sitojun		os_free(eb);
214282965Sae		return NULL;
21562587Sitojun	}
216105293Sume	crypto_cipher_deinit(ctx);
217105293Sume
218105293Sume	pad = eb[enc_data_len - 1];
219105293Sume	if (pad > 8) {
220105293Sume		wpa_printf(MSG_INFO, "PKCS #5: Invalid PS octet 0x%x", pad);
221105293Sume		os_free(eb);
222273087Sae		return NULL;
223105293Sume	}
224105293Sume	for (i = enc_data_len - pad; i < enc_data_len; i++) {
225105293Sume		if (eb[i] != pad) {
226105293Sume			wpa_hexdump(MSG_INFO, "PKCS #5: Invalid PS",
227105293Sume				    eb + enc_data_len - pad, pad);
228105293Sume			os_free(eb);
229105293Sume			return NULL;
230273087Sae		}
231105293Sume	}
232105293Sume
233105293Sume	wpa_hexdump_key(MSG_MSGDUMP, "PKCS #5: message M (encrypted key)",
234273087Sae			eb, enc_data_len - pad);
235105293Sume
236105293Sume	*data_len = enc_data_len - pad;
237105293Sume	return eb;
238169454Srwatson}
239105293Sume