corrected debug output of passphrase
[strongswan.git] / src / libstrongswan / asn1 / pem.c
1 /*
2 * Copyright (C) 2001-2008 Andreas Steffen
3 *
4 * Hochschule fuer Technik Rapperswil
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <unistd.h>
20 #include <errno.h>
21 #include <string.h>
22 #include <stddef.h>
23 #include <sys/types.h>
24
25 #include "pem.h"
26
27 #include <library.h>
28 #include <debug.h>
29 #include <asn1/asn1.h>
30
31 #include <utils/lexparser.h>
32 #include <crypto/hashers/hasher.h>
33 #include <crypto/crypters/crypter.h>
34
35 #define PKCS5_SALT_LEN 8 /* bytes */
36
37 /**
38 * check the presence of a pattern in a character string
39 */
40 static bool present(const char* pattern, chunk_t* ch)
41 {
42 u_int pattern_len = strlen(pattern);
43
44 if (ch->len >= pattern_len && strneq(ch->ptr, pattern, pattern_len))
45 {
46 ch->ptr += pattern_len;
47 ch->len -= pattern_len;
48 return TRUE;
49 }
50 return FALSE;
51 }
52
53 /**
54 * find a boundary of the form -----tag name-----
55 */
56 static bool find_boundary(const char* tag, chunk_t *line)
57 {
58 chunk_t name = chunk_empty;
59
60 if (!present("-----", line))
61 return FALSE;
62 if (!present(tag, line))
63 return FALSE;
64 if (*line->ptr != ' ')
65 return FALSE;
66 line->ptr++; line->len--;
67
68 /* extract name */
69 name.ptr = line->ptr;
70 while (line->len > 0)
71 {
72 if (present("-----", line))
73 {
74 DBG2(" -----%s %.*s-----", tag, (int)name.len, name.ptr);
75 return TRUE;
76 }
77 line->ptr++; line->len--; name.len++;
78 }
79 return FALSE;
80 }
81
82 /*
83 * decrypts a passphrase protected encrypted data block
84 */
85 static bool pem_decrypt(chunk_t *blob, encryption_algorithm_t alg, size_t key_size,
86 chunk_t *iv, chunk_t *passphrase)
87 {
88 hasher_t *hasher;
89 crypter_t *crypter;
90 chunk_t salt = { iv->ptr, PKCS5_SALT_LEN };
91 chunk_t hash;
92 chunk_t decrypted;
93 chunk_t key = {alloca(key_size), key_size};
94 u_int8_t padding, *last_padding_pos, *first_padding_pos;
95
96 if (passphrase == NULL || passphrase->len == 0)
97 {
98 DBG1(" missing passphrase");
99 return FALSE;
100 }
101
102 /* build key from passphrase and IV */
103 hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD5);
104 if (hasher == NULL)
105 {
106 DBG1(" MD5 hash algorithm not available");
107 return FALSE;
108 }
109 hash.len = hasher->get_hash_size(hasher);
110 hash.ptr = alloca(hash.len);
111 hasher->get_hash(hasher, *passphrase, NULL);
112 hasher->get_hash(hasher, salt, hash.ptr);
113 memcpy(key.ptr, hash.ptr, hash.len);
114
115 if (key.len > hash.len)
116 {
117 hasher->get_hash(hasher, hash, NULL);
118 hasher->get_hash(hasher, *passphrase, NULL);
119 hasher->get_hash(hasher, salt, hash.ptr);
120 memcpy(key.ptr + hash.len, hash.ptr, key.len - hash.len);
121 }
122 hasher->destroy(hasher);
123
124 /* decrypt blob */
125 crypter = lib->crypto->create_crypter(lib->crypto, alg, key_size);
126 if (crypter == NULL)
127 {
128 DBG1(" %N encryption algorithm not available",
129 encryption_algorithm_names, alg);
130 return FALSE;
131 }
132 crypter->set_key(crypter, key);
133
134 if (iv->len != crypter->get_block_size(crypter) ||
135 blob->len % iv->len)
136 {
137 crypter->destroy(crypter);
138 DBG1(" data size is not multiple of block size");
139 return FALSE;
140 }
141 crypter->decrypt(crypter, *blob, *iv, &decrypted);
142 crypter->destroy(crypter);
143 memcpy(blob->ptr, decrypted.ptr, blob->len);
144 chunk_free(&decrypted);
145
146 /* determine amount of padding */
147 last_padding_pos = blob->ptr + blob->len - 1;
148 padding = *last_padding_pos;
149 first_padding_pos = (padding > blob->len) ? blob->ptr : last_padding_pos - padding;
150
151 /* check the padding pattern */
152 while (--last_padding_pos > first_padding_pos)
153 {
154 if (*last_padding_pos != padding)
155 {
156 DBG1(" invalid passphrase");
157 return FALSE;
158 }
159 }
160 /* remove padding */
161 blob->len -= padding;
162 return TRUE;
163 }
164
165 /* Converts a PEM encoded file into its binary form
166 *
167 * RFC 1421 Privacy Enhancement for Electronic Mail, February 1993
168 * RFC 934 Message Encapsulation, January 1985
169 */
170 bool pem_to_bin(chunk_t *blob, chunk_t *passphrase, bool *pgp)
171 {
172 typedef enum {
173 PEM_PRE = 0,
174 PEM_MSG = 1,
175 PEM_HEADER = 2,
176 PEM_BODY = 3,
177 PEM_POST = 4,
178 PEM_ABORT = 5
179 } state_t;
180
181 encryption_algorithm_t alg = ENCR_UNDEFINED;
182 size_t key_size = 0;
183
184 bool encrypted = FALSE;
185
186 state_t state = PEM_PRE;
187
188 chunk_t src = *blob;
189 chunk_t dst = *blob;
190 chunk_t line = chunk_empty;
191 chunk_t iv = chunk_empty;
192
193 u_char iv_buf[16]; /* MD5 digest size */
194
195 /* zero size of converted blob */
196 dst.len = 0;
197
198 /* zero size of IV */
199 iv.ptr = iv_buf;
200 iv.len = 0;
201
202 while (fetchline(&src, &line))
203 {
204 if (state == PEM_PRE)
205 {
206 if (find_boundary("BEGIN", &line))
207 {
208 state = PEM_MSG;
209 }
210 continue;
211 }
212 else
213 {
214 if (find_boundary("END", &line))
215 {
216 state = PEM_POST;
217 break;
218 }
219 if (state == PEM_MSG)
220 {
221 state = (memchr(line.ptr, ':', line.len) == NULL) ? PEM_BODY : PEM_HEADER;
222 }
223 if (state == PEM_HEADER)
224 {
225 err_t ugh = NULL;
226 chunk_t name = chunk_empty;
227 chunk_t value = chunk_empty;
228
229 /* an empty line separates HEADER and BODY */
230 if (line.len == 0)
231 {
232 state = PEM_BODY;
233 continue;
234 }
235
236 /* we are looking for a parameter: value pair */
237 DBG2(" %.*s", (int)line.len, line.ptr);
238 ugh = extract_parameter_value(&name, &value, &line);
239 if (ugh != NULL)
240 continue;
241
242 if (match("Proc-Type", &name) && *value.ptr == '4')
243 encrypted = TRUE;
244 else if (match("DEK-Info", &name))
245 {
246 chunk_t dek;
247
248 if (!extract_token(&dek, ',', &value))
249 dek = value;
250
251 if (match("DES-EDE3-CBC", &dek))
252 {
253 alg = ENCR_3DES;
254 key_size = 24;
255 }
256 else if (match("AES-128-CBC", &dek))
257 {
258 alg = ENCR_AES_CBC;
259 key_size = 16;
260 }
261 else if (match("AES-192-CBC", &dek))
262 {
263 alg = ENCR_AES_CBC;
264 key_size = 24;
265 }
266 else if (match("AES-256-CBC", &dek))
267 {
268 alg = ENCR_AES_CBC;
269 key_size = 32;
270 }
271 else
272 {
273 DBG1(" encryption algorithm '%.s' not supported",
274 dek.len, dek.ptr);
275 return FALSE;
276 }
277 eat_whitespace(&value);
278 iv = chunk_from_hex(value, iv.ptr);
279 }
280 }
281 else /* state is PEM_BODY */
282 {
283 chunk_t data;
284
285 /* remove any trailing whitespace */
286 if (!extract_token(&data ,' ', &line))
287 {
288 data = line;
289 }
290
291 /* check for PGP armor checksum */
292 if (*data.ptr == '=')
293 {
294 *pgp = TRUE;
295 data.ptr++;
296 data.len--;
297 DBG2(" armor checksum: %.*s", (int)data.len, data.ptr);
298 continue;
299 }
300
301 if (blob->len - dst.len < data.len / 4 * 3)
302 {
303 state = PEM_ABORT;
304 }
305 data = chunk_from_base64(data, dst.ptr);
306
307 dst.ptr += data.len;
308 dst.len += data.len;
309 }
310 }
311 }
312 /* set length to size of binary blob */
313 blob->len = dst.len;
314
315 if (state != PEM_POST)
316 {
317 DBG1(" file coded in unknown format, discarded");
318 return FALSE;
319 }
320 if (!encrypted)
321 {
322 return TRUE;
323 }
324 return pem_decrypt(blob, alg, key_size, &iv, passphrase);
325
326 }
327
328 /* load a coded key or certificate file with autodetection
329 * of binary DER or base64 PEM ASN.1 formats and armored PGP format
330 */
331 bool pem_asn1_load_file(char *filename, chunk_t *passphrase,
332 chunk_t *blob, bool *pgp)
333 {
334 FILE *fd = fopen(filename, "r");
335
336 if (fd)
337 {
338 int bytes;
339 fseek(fd, 0, SEEK_END );
340 blob->len = ftell(fd);
341 rewind(fd);
342 blob->ptr = malloc(blob->len);
343 bytes = fread(blob->ptr, 1, blob->len, fd);
344 fclose(fd);
345 DBG2(" loading '%s' (%d bytes)", filename, bytes);
346
347 *pgp = FALSE;
348
349 /* try DER format */
350 if (is_asn1(*blob))
351 {
352 DBG2(" file coded in DER format");
353 return TRUE;
354 }
355
356 if (passphrase != NULL)
357 {
358 DBG4(" passphrase: %#B", passphrase);
359 }
360
361 /* try PEM format */
362 if (pem_to_bin(blob, passphrase, pgp))
363 {
364 if (*pgp)
365 {
366 DBG2(" file coded in armored PGP format");
367 return TRUE;
368 }
369 if (is_asn1(*blob))
370 {
371 DBG2(" file coded in PEM format");
372 return TRUE;
373 }
374 DBG1(" file coded in unknown format, discarded");
375 }
376
377 /* a conversion error has occured */
378 chunk_free(blob);
379 }
380 else
381 {
382 DBG1(" reading file '%s' failed", filename);
383 }
384 return FALSE;
385 }
386