]> git.ipfire.org Git - thirdparty/hostap.git/blame - src/tls/tlsv1_cred.c
Add support for PKCS #5 encrypted PKCS #8 keys with internal crypto
[thirdparty/hostap.git] / src / tls / tlsv1_cred.c
CommitLineData
6fc6879b
JM
1/*
2 * TLSv1 credentials
3 * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 *
9 * Alternatively, this software may be distributed under the terms of BSD
10 * license.
11 *
12 * See README and COPYING for more details.
13 */
14
15#include "includes.h"
16
17#include "common.h"
18#include "base64.h"
19#include "crypto.h"
20#include "x509v3.h"
21#include "tlsv1_cred.h"
22
23
24struct tlsv1_credentials * tlsv1_cred_alloc(void)
25{
26 struct tlsv1_credentials *cred;
27 cred = os_zalloc(sizeof(*cred));
28 return cred;
29}
30
31
32void tlsv1_cred_free(struct tlsv1_credentials *cred)
33{
34 if (cred == NULL)
35 return;
36
37 x509_certificate_chain_free(cred->trusted_certs);
38 x509_certificate_chain_free(cred->cert);
39 crypto_private_key_free(cred->key);
40 os_free(cred->dh_p);
41 os_free(cred->dh_g);
42 os_free(cred);
43}
44
45
46static int tlsv1_add_cert_der(struct x509_certificate **chain,
47 const u8 *buf, size_t len)
48{
49 struct x509_certificate *cert;
50 char name[128];
51
52 cert = x509_certificate_parse(buf, len);
53 if (cert == NULL) {
54 wpa_printf(MSG_INFO, "TLSv1: %s - failed to parse certificate",
55 __func__);
56 return -1;
57 }
58
59 cert->next = *chain;
60 *chain = cert;
61
62 x509_name_string(&cert->subject, name, sizeof(name));
63 wpa_printf(MSG_DEBUG, "TLSv1: Added certificate: %s", name);
64
65 return 0;
66}
67
68
69static const char *pem_cert_begin = "-----BEGIN CERTIFICATE-----";
70static const char *pem_cert_end = "-----END CERTIFICATE-----";
1b8409a0
JM
71static const char *pem_key_begin = "-----BEGIN RSA PRIVATE KEY-----";
72static const char *pem_key_end = "-----END RSA PRIVATE KEY-----";
8ef74414
JM
73static const char *pem_key2_begin = "-----BEGIN PRIVATE KEY-----";
74static const char *pem_key2_end = "-----END PRIVATE KEY-----";
3f4ed97a
JM
75static const char *pem_key_enc_begin = "-----BEGIN ENCRYPTED PRIVATE KEY-----";
76static const char *pem_key_enc_end = "-----END ENCRYPTED PRIVATE KEY-----";
6fc6879b
JM
77
78
79static const u8 * search_tag(const char *tag, const u8 *buf, size_t len)
80{
81 size_t i, plen;
82
83 plen = os_strlen(tag);
84 if (len < plen)
85 return NULL;
86
87 for (i = 0; i < len - plen; i++) {
88 if (os_memcmp(buf + i, tag, plen) == 0)
89 return buf + i;
90 }
91
92 return NULL;
93}
94
95
96static int tlsv1_add_cert(struct x509_certificate **chain,
97 const u8 *buf, size_t len)
98{
99 const u8 *pos, *end;
100 unsigned char *der;
101 size_t der_len;
102
103 pos = search_tag(pem_cert_begin, buf, len);
104 if (!pos) {
105 wpa_printf(MSG_DEBUG, "TLSv1: No PEM certificate tag found - "
106 "assume DER format");
107 return tlsv1_add_cert_der(chain, buf, len);
108 }
109
110 wpa_printf(MSG_DEBUG, "TLSv1: Converting PEM format certificate into "
111 "DER format");
112
113 while (pos) {
114 pos += os_strlen(pem_cert_begin);
115 end = search_tag(pem_cert_end, pos, buf + len - pos);
116 if (end == NULL) {
117 wpa_printf(MSG_INFO, "TLSv1: Could not find PEM "
118 "certificate end tag (%s)", pem_cert_end);
119 return -1;
120 }
121
122 der = base64_decode(pos, end - pos, &der_len);
123 if (der == NULL) {
124 wpa_printf(MSG_INFO, "TLSv1: Could not decode PEM "
125 "certificate");
126 return -1;
127 }
128
129 if (tlsv1_add_cert_der(chain, der, der_len) < 0) {
130 wpa_printf(MSG_INFO, "TLSv1: Failed to parse PEM "
131 "certificate after DER conversion");
132 os_free(der);
133 return -1;
134 }
135
136 os_free(der);
137
138 end += os_strlen(pem_cert_end);
139 pos = search_tag(pem_cert_begin, end, buf + len - end);
140 }
141
142 return 0;
143}
144
145
146static int tlsv1_set_cert_chain(struct x509_certificate **chain,
147 const char *cert, const u8 *cert_blob,
148 size_t cert_blob_len)
149{
150 if (cert_blob)
151 return tlsv1_add_cert(chain, cert_blob, cert_blob_len);
152
153 if (cert) {
154 u8 *buf;
155 size_t len;
156 int ret;
157
158 buf = (u8 *) os_readfile(cert, &len);
159 if (buf == NULL) {
160 wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'",
161 cert);
162 return -1;
163 }
164
165 ret = tlsv1_add_cert(chain, buf, len);
166 os_free(buf);
167 return ret;
168 }
169
170 return 0;
171}
172
173
174/**
175 * tlsv1_set_ca_cert - Set trusted CA certificate(s)
176 * @cred: TLSv1 credentials from tlsv1_cred_alloc()
177 * @cert: File or reference name for X.509 certificate in PEM or DER format
178 * @cert_blob: cert as inlined data or %NULL if not used
179 * @cert_blob_len: ca_cert_blob length
180 * @path: Path to CA certificates (not yet supported)
181 * Returns: 0 on success, -1 on failure
182 */
183int tlsv1_set_ca_cert(struct tlsv1_credentials *cred, const char *cert,
184 const u8 *cert_blob, size_t cert_blob_len,
185 const char *path)
186{
187 if (tlsv1_set_cert_chain(&cred->trusted_certs, cert,
188 cert_blob, cert_blob_len) < 0)
189 return -1;
190
191 if (path) {
192 /* TODO: add support for reading number of certificate files */
193 wpa_printf(MSG_INFO, "TLSv1: Use of CA certificate directory "
194 "not yet supported");
195 return -1;
196 }
197
198 return 0;
199}
200
201
202/**
203 * tlsv1_set_cert - Set certificate
204 * @cred: TLSv1 credentials from tlsv1_cred_alloc()
205 * @cert: File or reference name for X.509 certificate in PEM or DER format
206 * @cert_blob: cert as inlined data or %NULL if not used
207 * @cert_blob_len: cert_blob length
208 * Returns: 0 on success, -1 on failure
209 */
210int tlsv1_set_cert(struct tlsv1_credentials *cred, const char *cert,
211 const u8 *cert_blob, size_t cert_blob_len)
212{
213 return tlsv1_set_cert_chain(&cred->cert, cert,
214 cert_blob, cert_blob_len);
215}
216
217
1b8409a0
JM
218static int tlsv1_set_key_pem(struct tlsv1_credentials *cred,
219 const u8 *key, size_t len)
220{
221 const u8 *pos, *end;
222 unsigned char *der;
223 size_t der_len;
224
225 pos = search_tag(pem_key_begin, key, len);
8ef74414
JM
226 if (!pos) {
227 pos = search_tag(pem_key2_begin, key, len);
228 if (!pos)
229 return -1;
230 pos += os_strlen(pem_key2_begin);
231 end = search_tag(pem_key2_end, pos, key + len - pos);
232 if (!end)
233 return -1;
234 } else {
235 pos += os_strlen(pem_key_begin);
236 end = search_tag(pem_key_end, pos, key + len - pos);
237 if (!end)
238 return -1;
239 }
1b8409a0
JM
240
241 der = base64_decode(pos, end - pos, &der_len);
242 if (!der)
243 return -1;
3f4ed97a
JM
244 cred->key = crypto_private_key_import(der, der_len, NULL);
245 os_free(der);
246 return cred->key ? 0 : -1;
247}
248
249
250static int tlsv1_set_key_enc_pem(struct tlsv1_credentials *cred,
251 const u8 *key, size_t len, const char *passwd)
252{
253 const u8 *pos, *end;
254 unsigned char *der;
255 size_t der_len;
256
257 if (passwd == NULL)
258 return -1;
259 pos = search_tag(pem_key_enc_begin, key, len);
260 if (!pos)
261 return -1;
262 pos += os_strlen(pem_key_enc_begin);
263 end = search_tag(pem_key_enc_end, pos, key + len - pos);
264 if (!end)
265 return -1;
266
267 der = base64_decode(pos, end - pos, &der_len);
268 if (!der)
269 return -1;
270 cred->key = crypto_private_key_import(der, der_len, passwd);
1b8409a0
JM
271 os_free(der);
272 return cred->key ? 0 : -1;
273}
274
275
6fc6879b 276static int tlsv1_set_key(struct tlsv1_credentials *cred,
3f4ed97a 277 const u8 *key, size_t len, const char *passwd)
6fc6879b 278{
3f4ed97a 279 cred->key = crypto_private_key_import(key, len, passwd);
1b8409a0
JM
280 if (cred->key == NULL)
281 tlsv1_set_key_pem(cred, key, len);
3f4ed97a
JM
282 if (cred->key == NULL)
283 tlsv1_set_key_enc_pem(cred, key, len, passwd);
6fc6879b
JM
284 if (cred->key == NULL) {
285 wpa_printf(MSG_INFO, "TLSv1: Failed to parse private key");
286 return -1;
287 }
288 return 0;
289}
290
291
292/**
293 * tlsv1_set_private_key - Set private key
294 * @cred: TLSv1 credentials from tlsv1_cred_alloc()
295 * @private_key: File or reference name for the key in PEM or DER format
296 * @private_key_passwd: Passphrase for decrypted private key, %NULL if no
297 * passphrase is used.
298 * @private_key_blob: private_key as inlined data or %NULL if not used
299 * @private_key_blob_len: private_key_blob length
300 * Returns: 0 on success, -1 on failure
301 */
302int tlsv1_set_private_key(struct tlsv1_credentials *cred,
303 const char *private_key,
304 const char *private_key_passwd,
305 const u8 *private_key_blob,
306 size_t private_key_blob_len)
307{
308 crypto_private_key_free(cred->key);
309 cred->key = NULL;
310
311 if (private_key_blob)
312 return tlsv1_set_key(cred, private_key_blob,
3f4ed97a
JM
313 private_key_blob_len,
314 private_key_passwd);
6fc6879b
JM
315
316 if (private_key) {
317 u8 *buf;
318 size_t len;
319 int ret;
320
321 buf = (u8 *) os_readfile(private_key, &len);
322 if (buf == NULL) {
323 wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'",
324 private_key);
325 return -1;
326 }
327
3f4ed97a 328 ret = tlsv1_set_key(cred, buf, len, private_key_passwd);
6fc6879b
JM
329 os_free(buf);
330 return ret;
331 }
332
333 return 0;
334}
335
336
337static int tlsv1_set_dhparams_der(struct tlsv1_credentials *cred,
338 const u8 *dh, size_t len)
339{
340 struct asn1_hdr hdr;
341 const u8 *pos, *end;
342
343 pos = dh;
344 end = dh + len;
345
346 /*
347 * DHParameter ::= SEQUENCE {
348 * prime INTEGER, -- p
349 * base INTEGER, -- g
350 * privateValueLength INTEGER OPTIONAL }
351 */
352
353 /* DHParamer ::= SEQUENCE */
354 if (asn1_get_next(pos, len, &hdr) < 0 ||
355 hdr.class != ASN1_CLASS_UNIVERSAL ||
356 hdr.tag != ASN1_TAG_SEQUENCE) {
357 wpa_printf(MSG_DEBUG, "DH: DH parameters did not start with a "
358 "valid SEQUENCE - found class %d tag 0x%x",
359 hdr.class, hdr.tag);
360 return -1;
361 }
362 pos = hdr.payload;
363
364 /* prime INTEGER */
365 if (asn1_get_next(pos, end - pos, &hdr) < 0)
366 return -1;
367
368 if (hdr.class != ASN1_CLASS_UNIVERSAL ||
369 hdr.tag != ASN1_TAG_INTEGER) {
370 wpa_printf(MSG_DEBUG, "DH: No INTEGER tag found for p; "
371 "class=%d tag=0x%x", hdr.class, hdr.tag);
372 return -1;
373 }
374
375 wpa_hexdump(MSG_MSGDUMP, "DH: prime (p)", hdr.payload, hdr.length);
376 if (hdr.length == 0)
377 return -1;
378 os_free(cred->dh_p);
379 cred->dh_p = os_malloc(hdr.length);
380 if (cred->dh_p == NULL)
381 return -1;
382 os_memcpy(cred->dh_p, hdr.payload, hdr.length);
383 cred->dh_p_len = hdr.length;
384 pos = hdr.payload + hdr.length;
385
386 /* base INTEGER */
387 if (asn1_get_next(pos, end - pos, &hdr) < 0)
388 return -1;
389
390 if (hdr.class != ASN1_CLASS_UNIVERSAL ||
391 hdr.tag != ASN1_TAG_INTEGER) {
392 wpa_printf(MSG_DEBUG, "DH: No INTEGER tag found for g; "
393 "class=%d tag=0x%x", hdr.class, hdr.tag);
394 return -1;
395 }
396
397 wpa_hexdump(MSG_MSGDUMP, "DH: base (g)", hdr.payload, hdr.length);
398 if (hdr.length == 0)
399 return -1;
400 os_free(cred->dh_g);
401 cred->dh_g = os_malloc(hdr.length);
402 if (cred->dh_g == NULL)
403 return -1;
404 os_memcpy(cred->dh_g, hdr.payload, hdr.length);
405 cred->dh_g_len = hdr.length;
406
407 return 0;
408}
409
410
411static const char *pem_dhparams_begin = "-----BEGIN DH PARAMETERS-----";
412static const char *pem_dhparams_end = "-----END DH PARAMETERS-----";
413
414
415static int tlsv1_set_dhparams_blob(struct tlsv1_credentials *cred,
416 const u8 *buf, size_t len)
417{
418 const u8 *pos, *end;
419 unsigned char *der;
420 size_t der_len;
421
422 pos = search_tag(pem_dhparams_begin, buf, len);
423 if (!pos) {
424 wpa_printf(MSG_DEBUG, "TLSv1: No PEM dhparams tag found - "
425 "assume DER format");
426 return tlsv1_set_dhparams_der(cred, buf, len);
427 }
428
429 wpa_printf(MSG_DEBUG, "TLSv1: Converting PEM format dhparams into DER "
430 "format");
431
432 pos += os_strlen(pem_dhparams_begin);
433 end = search_tag(pem_dhparams_end, pos, buf + len - pos);
434 if (end == NULL) {
435 wpa_printf(MSG_INFO, "TLSv1: Could not find PEM dhparams end "
436 "tag (%s)", pem_dhparams_end);
437 return -1;
438 }
439
440 der = base64_decode(pos, end - pos, &der_len);
441 if (der == NULL) {
442 wpa_printf(MSG_INFO, "TLSv1: Could not decode PEM dhparams");
443 return -1;
444 }
445
446 if (tlsv1_set_dhparams_der(cred, der, der_len) < 0) {
447 wpa_printf(MSG_INFO, "TLSv1: Failed to parse PEM dhparams "
448 "DER conversion");
449 os_free(der);
450 return -1;
451 }
452
453 os_free(der);
454
455 return 0;
456}
457
458
459/**
460 * tlsv1_set_dhparams - Set Diffie-Hellman parameters
461 * @cred: TLSv1 credentials from tlsv1_cred_alloc()
462 * @dh_file: File or reference name for the DH params in PEM or DER format
463 * @dh_blob: DH params as inlined data or %NULL if not used
464 * @dh_blob_len: dh_blob length
465 * Returns: 0 on success, -1 on failure
466 */
467int tlsv1_set_dhparams(struct tlsv1_credentials *cred, const char *dh_file,
468 const u8 *dh_blob, size_t dh_blob_len)
469{
470 if (dh_blob)
471 return tlsv1_set_dhparams_blob(cred, dh_blob, dh_blob_len);
472
473 if (dh_file) {
474 u8 *buf;
475 size_t len;
476 int ret;
477
478 buf = (u8 *) os_readfile(dh_file, &len);
479 if (buf == NULL) {
480 wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'",
481 dh_file);
482 return -1;
483 }
484
485 ret = tlsv1_set_dhparams_blob(cred, buf, len);
486 os_free(buf);
487 return ret;
488 }
489
490 return 0;
491}