- removed, since we use pluto asn1 stuff
[strongswan.git] / Source / lib / asn1-pluto / pem.c
1 /*
2 * Copyright (C) 2005 Jan Hutter, Martin Willi
3 * Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <unistd.h>
19 #include <errno.h>
20 #include <string.h>
21 #include <stddef.h>
22 #include <sys/types.h>
23
24 #include "pem.h"
25 #include "ttodata.h"
26
27 #include <crypto/hashers/hasher.h>
28 #include <crypto/crypters/crypter.h>
29
30
31 /*
32 * check the presence of a pattern in a character string
33 */
34 static bool present(const char* pattern, chunk_t* ch)
35 {
36 u_int pattern_len = strlen(pattern);
37
38 if (ch->len >= pattern_len && strncmp(ch->ptr, pattern, pattern_len) == 0)
39 {
40 ch->ptr += pattern_len;
41 ch->len -= pattern_len;
42 return TRUE;
43 }
44 return FALSE;
45 }
46
47 /*
48 * compare string with chunk
49 */
50 static bool match(const char *pattern, const chunk_t *ch)
51 {
52 return ch->len == strlen(pattern) && strncmp(pattern, ch->ptr, ch->len) == 0;
53 }
54
55 /*
56 * find a boundary of the form -----tag name-----
57 */
58 static bool find_boundary(const char* tag, chunk_t *line)
59 {
60 chunk_t name = CHUNK_INITIALIZER;
61
62 if (!present("-----", line))
63 return FALSE;
64 if (!present(tag, line))
65 return FALSE;
66 if (*line->ptr != ' ')
67 return FALSE;
68 line->ptr++; line->len--;
69
70 /* extract name */
71 name.ptr = line->ptr;
72 while (line->len > 0)
73 {
74 if (present("-----", line))
75 {
76 return TRUE;
77 }
78 line->ptr++; line->len--; name.len++;
79 }
80 return FALSE;
81 }
82
83 /*
84 * eat whitespace
85 */
86 static void eat_whitespace(chunk_t *src)
87 {
88 while (src->len > 0 && (*src->ptr == ' ' || *src->ptr == '\t'))
89 {
90 src->ptr++; src->len--;
91 }
92 }
93
94 /*
95 * extracts a token ending with a given termination symbol
96 */
97 static bool extract_token(chunk_t *token, char termination, chunk_t *src)
98 {
99 u_char *eot = memchr(src->ptr, termination, src->len);
100
101 /* initialize empty token */
102 *token = CHUNK_INITIALIZER;
103
104 if (eot == NULL) /* termination symbol not found */
105 {
106 return FALSE;
107 }
108
109 /* extract token */
110 token->ptr = src->ptr;
111 token->len = (u_int)(eot - src->ptr);
112
113 /* advance src pointer after termination symbol */
114 src->ptr = eot + 1;
115 src->len -= (token->len + 1);
116
117 return TRUE;
118 }
119
120 /*
121 * extracts a name: value pair from the PEM header
122 */
123 static bool extract_parameter(chunk_t *name, chunk_t *value, chunk_t *line)
124 {
125 /* extract name */
126 if (!extract_token(name,':', line))
127 {
128 return FALSE;
129 }
130
131 eat_whitespace(line);
132
133 /* extract value */
134 *value = *line;
135 return TRUE;
136 }
137
138 /*
139 * fetches a new line terminated by \n or \r\n
140 */
141 static bool fetchline(chunk_t *src, chunk_t *line)
142 {
143 if (src->len == 0) /* end of src reached */
144 return FALSE;
145
146 if (extract_token(line, '\n', src))
147 {
148 if (line->len > 0 && *(line->ptr + line->len -1) == '\r')
149 line->len--; /* remove optional \r */
150 }
151 else /*last line ends without newline */
152 {
153 *line = *src;
154 src->ptr += src->len;
155 src->len = 0;
156 }
157 return TRUE;
158 }
159
160 /*
161 * decrypts a DES-EDE-CBC encrypted data block
162 */
163 static status_t pem_decrypt(chunk_t *blob, chunk_t *iv, char *passphrase)
164 {
165 hasher_t *hasher;
166 crypter_t *crypter;
167 chunk_t hash;
168 chunk_t decrypted;
169 chunk_t pass = {passphrase, strlen(passphrase)};
170 chunk_t key = {alloca(24), 24};
171 u_int8_t padding, *last_padding_pos, *first_padding_pos;
172
173 /* build key from passphrase and IV */
174 hasher = hasher_create(HASH_MD5);
175 hash.len = hasher->get_block_size(hasher);
176 hash.ptr = alloca(hash.len);
177 hasher->get_hash(hasher, pass, NULL);
178 hasher->get_hash(hasher, *iv, hash.ptr);
179
180 memcpy(key.ptr, hash.ptr, hash.len);
181
182 hasher->get_hash(hasher, hash, NULL);
183 hasher->get_hash(hasher, pass, NULL);
184 hasher->get_hash(hasher, *iv, hash.ptr);
185
186 memcpy(key.ptr + hash.len, hash.ptr, key.len - hash.len);
187
188 hasher->destroy(hasher);
189
190 /* decrypt blob */
191 crypter = crypter_create(ENCR_3DES, 0);
192 crypter->set_key(crypter, key);
193 crypter->decrypt(crypter, *blob, *iv, &decrypted);
194 memcpy(blob->ptr, decrypted.ptr, blob->len);
195 chunk_free(&decrypted);
196
197 /* determine amount of padding */
198 last_padding_pos = blob->ptr + blob->len - 1;
199 padding = *last_padding_pos;
200 first_padding_pos = (padding > blob->len) ? blob->ptr : last_padding_pos - padding;
201
202 /* check the padding pattern */
203 while (--last_padding_pos > first_padding_pos)
204 {
205 if (*last_padding_pos != padding)
206 return FALSE;
207 }
208 /* remove padding */
209 blob->len -= padding;
210 return TRUE;
211 }
212
213 /* Converts a PEM encoded file into its binary form
214 *
215 * RFC 1421 Privacy Enhancement for Electronic Mail, February 1993
216 * RFC 934 Message Encapsulation, January 1985
217 */
218 status_t pemtobin(chunk_t *blob, char *pass)
219 {
220 typedef enum {
221 PEM_PRE = 0,
222 PEM_MSG = 1,
223 PEM_HEADER = 2,
224 PEM_BODY = 3,
225 PEM_POST = 4,
226 PEM_ABORT = 5
227 } state_t;
228
229 bool encrypted = FALSE;
230
231 state_t state = PEM_PRE;
232
233 chunk_t src = *blob;
234 chunk_t dst = *blob;
235 chunk_t line = CHUNK_INITIALIZER;
236 chunk_t iv = CHUNK_INITIALIZER;
237
238 u_char iv_buf[16]; /* MD5 digest size */
239
240 /* zero size of converted blob */
241 dst.len = 0;
242
243 /* zero size of IV */
244 iv.ptr = iv_buf;
245 iv.len = 0;
246
247 while (fetchline(&src, &line))
248 {
249 if (state == PEM_PRE)
250 {
251 if (find_boundary("BEGIN", &line))
252 {
253 state = PEM_MSG;
254 }
255 continue;
256 }
257 else
258 {
259 if (find_boundary("END", &line))
260 {
261 state = PEM_POST;
262 break;
263 }
264 if (state == PEM_MSG)
265 {
266 state = (memchr(line.ptr, ':', line.len) == NULL) ? PEM_BODY : PEM_HEADER;
267 }
268 if (state == PEM_HEADER)
269 {
270 chunk_t name = CHUNK_INITIALIZER;
271 chunk_t value = CHUNK_INITIALIZER;
272
273 /* an empty line separates HEADER and BODY */
274 if (line.len == 0)
275 {
276 state = PEM_BODY;
277 continue;
278 }
279
280 /* we are looking for a name: value pair */
281 if (!extract_parameter(&name, &value, &line))
282 continue;
283
284 if (match("Proc-Type", &name) && *value.ptr == '4')
285 encrypted = TRUE;
286 else if (match("DEK-Info", &name))
287 {
288 const char *ugh = NULL;
289 size_t len = 0;
290 chunk_t dek;
291
292 if (!extract_token(&dek, ',', &value))
293 dek = value;
294
295 /* we support DES-EDE3-CBC encrypted files, only */
296 if (!match("DES-EDE3-CBC", &dek))
297 return NOT_SUPPORTED;
298
299 eat_whitespace(&value);
300 ugh = ttodata(value.ptr, value.len, 16, iv.ptr, 16, &len);
301 if (ugh)
302 return PARSE_ERROR;
303
304 iv.len = len;
305 }
306 }
307 else /* state is PEM_BODY */
308 {
309 const char *ugh = NULL;
310 size_t len = 0;
311 chunk_t data;
312
313 /* remove any trailing whitespace */
314 if (!extract_token(&data ,' ', &line))
315 {
316 data = line;
317 }
318
319 ugh = ttodata(data.ptr, data.len, 64, dst.ptr, blob->len - dst.len, &len);
320 if (ugh)
321 {
322 state = PEM_ABORT;
323 break;
324 }
325 else
326 {
327 dst.ptr += len;
328 dst.len += len;
329 }
330 }
331 }
332 }
333 /* set length to size of binary blob */
334 blob->len = dst.len;
335
336 if (state != PEM_POST)
337 return PARSE_ERROR;
338
339 if (encrypted)
340 return pem_decrypt(blob, &iv, pass);
341 else
342 return SUCCESS;
343 }