OSDN Git Service

2013.10.24
[uclinux-h8/uClinux-dist.git] / freeswan / pluto / pem.c
1 /* Loading of PEM encoded files with optional encryption
2  * Copyright (C) 2001-2002 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  * RCSID $Id: pem.c,v 1.4 2002-07-08 05:17:42 danield Exp $
15  */
16
17 /* decrypt a PEM encoded data block using DES-EDE3-CBD
18  * see RFC 1423 PEM: Algorithms, Modes and Identifiers
19  */
20
21 #include <stdio.h>
22 #include <string.h>
23 #include <stddef.h>
24 #include <sys/types.h>
25
26 #include <freeswan.h>
27 #define HEADER_DES_LOCL_H   /* stupid trick to force prototype decl in <des.h> */
28 #include <des.h>
29
30 #include "constants.h"
31 #include "defs.h"
32 #include "log.h"
33 #include "md5.h"
34 #include "pem.h"
35
36 /*
37  * check the presence of a pattern in a character string
38  */
39 static bool
40 present(const char* pattern, chunk_t* ch)
41 {
42     u_int pattern_len = strlen(pattern);
43
44     if (ch->len >= pattern_len && strncmp(ch->ptr, pattern, pattern_len) == 0)
45     {
46         ch->ptr += pattern_len;
47         ch->len -= pattern_len;
48         return TRUE;
49     }
50     return FALSE;
51 }
52
53 /*
54  * compare string with chunk
55  */
56 static bool
57 match(const char *pattern, const chunk_t *ch)
58 {
59     return ch->len == strlen(pattern) &&
60            strncmp(pattern, ch->ptr, ch->len) == 0;
61 }
62
63 /*
64  * find a boundary of the form -----tag name-----
65  */
66 static bool
67 find_boundary(const char* tag, chunk_t *line)
68 {
69     chunk_t name = empty_chunk;
70
71     if (!present("-----", line))
72         return FALSE;
73     if (!present(tag, line))
74         return FALSE;
75     if (*line->ptr != ' ')
76         return FALSE;
77     line->ptr++;  line->len--;
78
79     /* extract name */
80     name.ptr = line->ptr;
81     while (line->len > 0)
82     {
83         if (present("-----", line))
84         {
85             DBG(DBG_PARSING,
86                 DBG_log("  -----%s %.*s-----",
87                         tag, (int)name.len, name.ptr);
88             )
89             return TRUE;
90         }
91         line->ptr++;  line->len--;  name.len++;
92     }
93     return FALSE;
94 }
95
96 /*
97  * eat whitespace
98  */
99 static void
100 eat_whitespace(chunk_t *src)
101 {
102     while (src->len > 0 && (*src->ptr == ' ' || *src->ptr == '\t'))
103     {
104         src->ptr++;  src->len--;
105     }
106 }
107
108 /*
109  * extracts a token ending with a given termination symbol
110  */
111 static bool
112 extract_token(chunk_t *token, char termination, chunk_t *src)
113 {
114     u_char *eot = memchr(src->ptr, termination, src->len);
115
116     /* initialize empty token */
117     *token = empty_chunk;
118
119     if (eot == NULL) /* termination symbol not found */
120         return FALSE;
121
122     /* extract token */
123     token->ptr = src->ptr;
124     token->len = (u_int)(eot - src->ptr);
125
126     /* advance src pointer after termination symbol */
127     src->ptr = eot + 1;
128     src->len -= (token->len + 1);
129
130    return TRUE;
131 }
132
133 /*
134  * extracts a name: value pair from the PEM header
135  */
136 static bool
137 extract_parameter(chunk_t *name, chunk_t *value, chunk_t *line)
138 {
139     DBG(DBG_PARSING,
140         DBG_log("  %.*s", (int)line->len, line->ptr);
141     )
142
143     /* extract name */
144     if (!extract_token(name,':', line))
145         return FALSE;
146
147     eat_whitespace(line);
148
149     /* extract value */
150     *value = *line;
151     return TRUE;
152 }
153
154 /*
155  *  fetches a new line terminated by \n or \r\n
156  */
157 static bool
158 fetchline(chunk_t *src, chunk_t *line)
159 {
160     if (src->len == 0) /* end of src reached */
161         return FALSE;
162
163     if (extract_token(line, '\n', src))
164     {
165         if (line->len > 0 && *(line->ptr + line->len -1) == '\r')
166             line->len--;  /* remove optional \r */
167     }
168     else /*last line ends without newline */
169     {
170         *line = *src;
171         src->ptr += src->len;
172         src->len = 0;
173     }
174     return TRUE;
175 }
176
177 /*
178  * decrypts a DES-EDE-CBC encrypted data block
179  */
180 static err_t
181 pem_decrypt_3des(chunk_t *blob, chunk_t *iv, const char *passphrase)
182 {
183     MD5_CTX context;
184     u_char digest[MD5_DIGEST_SIZE];
185     u_char key[24];
186     des_cblock *deskey = (des_cblock *)key;
187     des_key_schedule ks[3];
188     u_char padding, *last_padding_pos, *first_padding_pos;
189
190    if (iv->len != DES_CBC_BLOCK_SIZE)
191         return "size of DES-EDE3-CBC IV is not 8 bytes";
192
193     /* Convert passphrase to 3des key */
194     MD5Init(&context);
195     MD5Update(&context, passphrase, strlen(passphrase));
196     MD5Update(&context, iv->ptr, iv->len);
197     MD5Final(digest, &context);
198
199     memcpy(key, digest, MD5_DIGEST_SIZE);
200
201     MD5Init(&context);
202     MD5Update(&context, digest, MD5_DIGEST_SIZE);
203     MD5Update(&context, passphrase, strlen(passphrase));
204     MD5Update(&context, iv->ptr, iv->len);
205     MD5Final(digest, &context);
206
207     memcpy(key + MD5_DIGEST_SIZE, digest, 24 - MD5_DIGEST_SIZE);
208
209     (void) des_set_key(&deskey[0], ks[0]);
210     (void) des_set_key(&deskey[1], ks[1]);
211     (void) des_set_key(&deskey[2], ks[2]);
212
213     /* decrypt data block */
214     des_ede3_cbc_encrypt((des_cblock *)blob->ptr, (des_cblock *)blob->ptr,
215         blob->len, ks[0], ks[1], ks[2], (des_cblock *)iv->ptr, FALSE);
216
217     /* determine amount of padding */
218     last_padding_pos = blob->ptr + blob->len - 1;
219     padding = *last_padding_pos;
220     first_padding_pos = (padding > blob->len)?
221                          blob->ptr : last_padding_pos - padding;
222
223     /* check the padding pattern */
224     while (--last_padding_pos > first_padding_pos)
225     {
226         if (*last_padding_pos != padding)
227             return "wrong padding after decryption - please check your passphrase";
228     }
229
230     /* remove padding */
231     blob->len -= padding;
232     return NULL;
233 }
234
235  /*  Converts a PEM encoded file into its binary form
236  *
237  *  RFC 1421 Privacy Enhancement for Electronic Mail, February 1993
238  *  RFC 934 Message Encapsulation, January 1985
239  */
240 err_t
241 pemtobin(chunk_t *blob, const char* passphrase)
242 {
243     typedef enum {
244         PEM_PRE    = 0,
245         PEM_MSG    = 1,
246         PEM_HEADER = 2,
247         PEM_BODY   = 3,
248         PEM_POST   = 4,
249         PEM_ABORT  = 5
250     } state_t;
251
252     bool encrypted = FALSE;
253
254     state_t state  = PEM_PRE;
255
256     chunk_t src    = *blob;
257     chunk_t dst    = *blob;
258     chunk_t line   = empty_chunk;
259     chunk_t iv     = empty_chunk;
260
261     u_char iv_buf[MAX_DIGEST_LEN];
262
263     /* zero size of converted blob */
264     dst.len = 0;
265
266     /* zero size of IV */
267     iv.ptr = iv_buf;
268     iv.len = 0;
269
270     while (fetchline(&src, &line))
271     {
272         if (state == PEM_PRE)
273         {
274             if (find_boundary("BEGIN", &line))
275                 state = PEM_MSG;
276             continue;
277         }
278         else
279         {
280             if (find_boundary("END", &line))
281             {
282                 state = PEM_POST;
283                 break;
284             }
285             if (state == PEM_MSG)
286             {
287                 state = (memchr(line.ptr, ':', line.len) == NULL)?
288                             PEM_BODY : PEM_HEADER;
289             }
290             if (state == PEM_HEADER)
291             {
292                 chunk_t name  = empty_chunk;
293                 chunk_t value = empty_chunk;
294
295                 /* an empty line separates HEADER and BODY */
296                 if (line.len == 0)
297                 {
298                     state = PEM_BODY;
299                     continue;
300                 }
301
302                 /* we are looking for a name: value pair */
303                 if (!extract_parameter(&name, &value, &line))
304                     continue;
305
306                 if (match("Proc-Type", &name) && *value.ptr == '4')
307                         encrypted = TRUE;
308                 else if (match("DEK-Info", &name))
309                 {
310                     char *ugh = NULL;
311                     int len = 0;
312                     chunk_t dek;
313
314                     if (!extract_token(&dek, ',', &value))
315                         dek = value;
316
317                     /* we support DES-EDE3-CBC encrypted files, only */
318                     if (!match("DES-EDE3-CBC", &dek))
319                         return "we support DES-EDE3-CBC encrypted files, only";
320
321                     eat_whitespace(&value);
322                     ugh = ttodata(value.ptr, value.len, 16,
323                                   iv.ptr, MAX_DIGEST_LEN, &len);
324                     if (ugh)
325                         return "error in IV";
326
327                     iv.len = len;
328                 }
329             }
330             else /* state is PEM_BODY */
331             {
332                 char *ugh = NULL;
333                 int len = 0;
334                 chunk_t data;
335
336                 /* remove any trailing whitespace */
337                 if (!extract_token(&data ,' ', &line))
338                     data = line;
339
340                 ugh = ttodata(data.ptr, data.len, 64,
341                               dst.ptr, blob->len - dst.len, &len);
342                 if (ugh)
343                 {
344                     DBG(DBG_PARSING,
345                         DBG_log("  %s", ugh);
346                     )
347                     state = PEM_ABORT;
348                     break;
349                 }
350                 else
351                 {
352                     dst.ptr += len;
353                     dst.len += len;
354                 }
355             }
356         }
357     }
358     /* set length to size of binary blob */
359     blob->len = dst.len;
360
361     if (state != PEM_POST)
362 #if 0
363         return "file coded in unknown format, discarded";
364 #endif
365         return " ";
366         
367     if (encrypted)
368     {
369         DBG(DBG_CRYPT,
370             DBG_log("  decrypting file using 'DES-EDE3-CBC'");
371         )
372         if (strlen(passphrase) == 0)
373             return "no passphrase available";
374
375         return pem_decrypt_3des(blob, &iv, passphrase);
376     }
377     else
378         return NULL;
379 }