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