introduced new logging subsystem using bus:
[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 <asn1/asn1.h>
27 #include <asn1/ttodata.h>
28
29 #include <utils/lexparser.h>
30 #include <crypto/hashers/hasher.h>
31 #include <crypto/crypters/crypter.h>
32
33 #define PKCS5_SALT_LEN 8 /* bytes */
34
35 /**
36 * check the presence of a pattern in a character string
37 */
38 static bool present(const char* pattern, chunk_t* ch)
39 {
40 u_int pattern_len = strlen(pattern);
41
42 if (ch->len >= pattern_len && strncmp(ch->ptr, pattern, pattern_len) == 0)
43 {
44 ch->ptr += pattern_len;
45 ch->len -= pattern_len;
46 return TRUE;
47 }
48 return FALSE;
49 }
50
51 /**
52 * find a boundary of the form -----tag name-----
53 */
54 static bool find_boundary(const char* tag, chunk_t *line)
55 {
56 chunk_t name = CHUNK_INITIALIZER;
57
58 if (!present("-----", line))
59 return FALSE;
60 if (!present(tag, line))
61 return FALSE;
62 if (*line->ptr != ' ')
63 return FALSE;
64 line->ptr++; line->len--;
65
66 /* extract name */
67 name.ptr = line->ptr;
68 while (line->len > 0)
69 {
70 if (present("-----", line))
71 {
72 DBG2(" -----%s %.*s-----", tag, (int)name.len, name.ptr);
73 return TRUE;
74 }
75 line->ptr++; line->len--; name.len++;
76 }
77 return FALSE;
78 }
79
80 /*
81 * decrypts a DES-EDE-CBC encrypted data block
82 */
83 static err_t pem_decrypt(chunk_t *blob, encryption_algorithm_t alg, size_t key_size,
84 chunk_t *iv, chunk_t *passphrase)
85 {
86 hasher_t *hasher;
87 crypter_t *crypter;
88 chunk_t salt = { iv->ptr, PKCS5_SALT_LEN };
89 chunk_t hash;
90 chunk_t decrypted;
91 chunk_t key = {alloca(key_size), key_size};
92 u_int8_t padding, *last_padding_pos, *first_padding_pos;
93
94 if (passphrase == NULL || passphrase->len == 0)
95 return "missing passphrase";
96
97 /* build key from passphrase and IV */
98 hasher = hasher_create(HASH_MD5);
99 hash.len = hasher->get_hash_size(hasher);
100 hash.ptr = alloca(hash.len);
101 hasher->get_hash(hasher, *passphrase, NULL);
102 hasher->get_hash(hasher, salt, hash.ptr);
103 memcpy(key.ptr, hash.ptr, hash.len);
104
105 if (key.len > hash.len)
106 {
107 hasher->get_hash(hasher, hash, NULL);
108 hasher->get_hash(hasher, *passphrase, NULL);
109 hasher->get_hash(hasher, salt, hash.ptr);
110 memcpy(key.ptr + hash.len, hash.ptr, key.len - hash.len);
111 }
112 hasher->destroy(hasher);
113
114 /* decrypt blob */
115 crypter = crypter_create(alg, key_size);
116 crypter->set_key(crypter, key);
117 if (crypter->decrypt(crypter, *blob, *iv, &decrypted) != SUCCESS)
118 {
119 return "data size is not multiple of block size";
120 }
121 memcpy(blob->ptr, decrypted.ptr, blob->len);
122 chunk_free(&decrypted);
123
124 /* determine amount of padding */
125 last_padding_pos = blob->ptr + blob->len - 1;
126 padding = *last_padding_pos;
127 first_padding_pos = (padding > blob->len) ? blob->ptr : last_padding_pos - padding;
128
129 /* check the padding pattern */
130 while (--last_padding_pos > first_padding_pos)
131 {
132 if (*last_padding_pos != padding)
133 return "invalid passphrase";
134 }
135 /* remove padding */
136 blob->len -= padding;
137 return NULL;
138 }
139
140 /* Converts a PEM encoded file into its binary form
141 *
142 * RFC 1421 Privacy Enhancement for Electronic Mail, February 1993
143 * RFC 934 Message Encapsulation, January 1985
144 */
145 err_t pem_to_bin(chunk_t *blob, chunk_t *passphrase, bool *pgp)
146 {
147 typedef enum {
148 PEM_PRE = 0,
149 PEM_MSG = 1,
150 PEM_HEADER = 2,
151 PEM_BODY = 3,
152 PEM_POST = 4,
153 PEM_ABORT = 5
154 } state_t;
155
156 encryption_algorithm_t alg = ENCR_UNDEFINED;
157 size_t key_size = 0;
158
159 bool encrypted = FALSE;
160
161 state_t state = PEM_PRE;
162
163 chunk_t src = *blob;
164 chunk_t dst = *blob;
165 chunk_t line = CHUNK_INITIALIZER;
166 chunk_t iv = CHUNK_INITIALIZER;
167
168 u_char iv_buf[16]; /* MD5 digest size */
169
170 /* zero size of converted blob */
171 dst.len = 0;
172
173 /* zero size of IV */
174 iv.ptr = iv_buf;
175 iv.len = 0;
176
177 while (fetchline(&src, &line))
178 {
179 if (state == PEM_PRE)
180 {
181 if (find_boundary("BEGIN", &line))
182 {
183 state = PEM_MSG;
184 }
185 continue;
186 }
187 else
188 {
189 if (find_boundary("END", &line))
190 {
191 state = PEM_POST;
192 break;
193 }
194 if (state == PEM_MSG)
195 {
196 state = (memchr(line.ptr, ':', line.len) == NULL) ? PEM_BODY : PEM_HEADER;
197 }
198 if (state == PEM_HEADER)
199 {
200 err_t ugh = NULL;
201 chunk_t name = CHUNK_INITIALIZER;
202 chunk_t value = CHUNK_INITIALIZER;
203
204 /* an empty line separates HEADER and BODY */
205 if (line.len == 0)
206 {
207 state = PEM_BODY;
208 continue;
209 }
210
211 /* we are looking for a parameter: value pair */
212 DBG2(" %.*s", (int)line.len, line.ptr);
213 ugh = extract_parameter_value(&name, &value, &line);
214 if (ugh != NULL)
215 continue;
216
217 if (match("Proc-Type", &name) && *value.ptr == '4')
218 encrypted = TRUE;
219 else if (match("DEK-Info", &name))
220 {
221 size_t len = 0;
222 chunk_t dek;
223
224 if (!extract_token(&dek, ',', &value))
225 dek = value;
226
227 /* we support DES-EDE3-CBC encrypted files, only */
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 }