]>
git.ipfire.org Git - thirdparty/strongswan.git/blob - programs/pluto/pem.c
1 /* Loading of PEM encoded files with optional encryption
2 * Copyright (C) 2001-2004 Andreas Steffen, Zuercher Hochschule Winterthur
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>.
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
14 * RCSID $Id: pem.c,v 1.4 2005/08/17 16:31:24 as Exp $
17 /* decrypt a PEM encoded data block using DES-EDE3-CBC
18 * see RFC 1423 PEM: Algorithms, Modes and Identifiers
27 #include <sys/types.h>
30 #define HEADER_DES_LOCL_H /* stupid trick to force prototype decl in <des.h> */
31 #include <crypto/des.h>
33 #include "constants.h"
41 * check the presence of a pattern in a character string
44 present(const char* pattern
, chunk_t
* ch
)
46 u_int pattern_len
= strlen(pattern
);
48 if (ch
->len
>= pattern_len
&& strncmp(ch
->ptr
, pattern
, pattern_len
) == 0)
50 ch
->ptr
+= pattern_len
;
51 ch
->len
-= pattern_len
;
58 * compare string with chunk
61 match(const char *pattern
, const chunk_t
*ch
)
63 return ch
->len
== strlen(pattern
) &&
64 strncmp(pattern
, ch
->ptr
, ch
->len
) == 0;
68 * find a boundary of the form -----tag name-----
71 find_boundary(const char* tag
, chunk_t
*line
)
73 chunk_t name
= empty_chunk
;
75 if (!present("-----", line
))
77 if (!present(tag
, line
))
79 if (*line
->ptr
!= ' ')
81 line
->ptr
++; line
->len
--;
87 if (present("-----", line
))
90 DBG_log(" -----%s %.*s-----",
91 tag
, (int)name
.len
, name
.ptr
);
95 line
->ptr
++; line
->len
--; name
.len
++;
104 eat_whitespace(chunk_t
*src
)
106 while (src
->len
> 0 && (*src
->ptr
== ' ' || *src
->ptr
== '\t'))
108 src
->ptr
++; src
->len
--;
113 * extracts a token ending with a given termination symbol
116 extract_token(chunk_t
*token
, char termination
, chunk_t
*src
)
118 u_char
*eot
= memchr(src
->ptr
, termination
, src
->len
);
120 /* initialize empty token */
121 *token
= empty_chunk
;
123 if (eot
== NULL
) /* termination symbol not found */
127 token
->ptr
= src
->ptr
;
128 token
->len
= (u_int
)(eot
- src
->ptr
);
130 /* advance src pointer after termination symbol */
132 src
->len
-= (token
->len
+ 1);
138 * extracts a name: value pair from the PEM header
141 extract_parameter(chunk_t
*name
, chunk_t
*value
, chunk_t
*line
)
144 DBG_log(" %.*s", (int)line
->len
, line
->ptr
);
148 if (!extract_token(name
,':', line
))
151 eat_whitespace(line
);
159 * fetches a new line terminated by \n or \r\n
162 fetchline(chunk_t
*src
, chunk_t
*line
)
164 if (src
->len
== 0) /* end of src reached */
167 if (extract_token(line
, '\n', src
))
169 if (line
->len
> 0 && *(line
->ptr
+ line
->len
-1) == '\r')
170 line
->len
--; /* remove optional \r */
172 else /*last line ends without newline */
175 src
->ptr
+= src
->len
;
182 * decrypts a DES-EDE-CBC encrypted data block
185 pem_decrypt_3des(chunk_t
*blob
, chunk_t
*iv
, const char *passphrase
)
188 u_char digest
[MD5_DIGEST_SIZE
];
189 u_char des_iv
[DES_CBC_BLOCK_SIZE
];
191 des_cblock
*deskey
= (des_cblock
*)key
;
192 des_key_schedule ks
[3];
193 u_char padding
, *last_padding_pos
, *first_padding_pos
;
195 /* Convert passphrase to 3des key */
197 MD5Update(&context
, passphrase
, strlen(passphrase
));
198 MD5Update(&context
, iv
->ptr
, iv
->len
);
199 MD5Final(digest
, &context
);
201 memcpy(key
, digest
, MD5_DIGEST_SIZE
);
204 MD5Update(&context
, digest
, MD5_DIGEST_SIZE
);
205 MD5Update(&context
, passphrase
, strlen(passphrase
));
206 MD5Update(&context
, iv
->ptr
, iv
->len
);
207 MD5Final(digest
, &context
);
209 memcpy(key
+ MD5_DIGEST_SIZE
, digest
, 24 - MD5_DIGEST_SIZE
);
211 (void) des_set_key(&deskey
[0], ks
[0]);
212 (void) des_set_key(&deskey
[1], ks
[1]);
213 (void) des_set_key(&deskey
[2], ks
[2]);
215 /* decrypt data block */
216 memcpy(des_iv
, iv
->ptr
, DES_CBC_BLOCK_SIZE
);
217 des_ede3_cbc_encrypt((des_cblock
*)blob
->ptr
, (des_cblock
*)blob
->ptr
,
218 blob
->len
, ks
[0], ks
[1], ks
[2], (des_cblock
*)des_iv
, FALSE
);
220 /* determine amount of padding */
221 last_padding_pos
= blob
->ptr
+ blob
->len
- 1;
222 padding
= *last_padding_pos
;
223 first_padding_pos
= (padding
> blob
->len
)?
224 blob
->ptr
: last_padding_pos
- padding
;
226 /* check the padding pattern */
227 while (--last_padding_pos
> first_padding_pos
)
229 if (*last_padding_pos
!= padding
)
234 blob
->len
-= padding
;
239 * optionally prompts for a passphrase before decryption
240 * currently we support DES-EDE3-CBC, only
243 pem_decrypt(chunk_t
*blob
, chunk_t
*iv
, prompt_pass_t
*pass
, const char* label
)
246 DBG_log(" decrypting file using 'DES-EDE3-CBC'");
248 if (iv
->len
!= DES_CBC_BLOCK_SIZE
)
249 return "size of DES-EDE3-CBC IV is not 8 bytes";
252 return "no passphrase available";
254 /* do we prompt for the passphrase? */
255 if (pass
->prompt
&& pass
->fd
!= NULL_FD
)
259 err_t ugh
= "invalid passphrase, too many trials";
261 whack_log(RC_ENTERSECRET
, "need passphrase for '%s'", label
);
263 for (i
= 0; i
< MAX_PROMPT_PASS_TRIALS
; i
++)
268 whack_log(RC_ENTERSECRET
, "invalid passphrase, please try again");
270 n
= read(pass
->fd
, pass
->secret
, PROMPT_PASS_LEN
);
274 err_t ugh
= "read(whackfd) failed";
276 whack_log(RC_LOG_SERIOUS
,ugh
);
280 pass
->secret
[n
-1] = '\0';
282 if (strlen(pass
->secret
) == 0)
284 err_t ugh
= "no passphrase entered, aborted";
286 whack_log(RC_LOG_SERIOUS
, ugh
);
290 clonetochunk(blob_copy
, blob
->ptr
, blob
->len
, "blob copy");
292 if (pem_decrypt_3des(blob
, iv
, pass
->secret
))
294 whack_log(RC_SUCCESS
, "valid passphrase");
295 pfree(blob_copy
.ptr
);
299 /* blob is useless after wrong decryption, restore the original */
303 whack_log(RC_LOG_SERIOUS
, ugh
);
308 if (pem_decrypt_3des(blob
, iv
, pass
->secret
))
311 return "invalid passphrase";
315 /* Converts a PEM encoded file into its binary form
317 * RFC 1421 Privacy Enhancement for Electronic Mail, February 1993
318 * RFC 934 Message Encapsulation, January 1985
321 pemtobin(chunk_t
*blob
, prompt_pass_t
*pass
, const char* label
, bool *pgp
)
332 bool encrypted
= FALSE
;
334 state_t state
= PEM_PRE
;
338 chunk_t line
= empty_chunk
;
339 chunk_t iv
= empty_chunk
;
341 u_char iv_buf
[MAX_DIGEST_LEN
];
343 /* zero size of converted blob */
346 /* zero size of IV */
350 while (fetchline(&src
, &line
))
352 if (state
== PEM_PRE
)
354 if (find_boundary("BEGIN", &line
))
363 if (find_boundary("END", &line
))
368 if (state
== PEM_MSG
)
370 state
= (memchr(line
.ptr
, ':', line
.len
) == NULL
)?
371 PEM_BODY
: PEM_HEADER
;
373 if (state
== PEM_HEADER
)
375 chunk_t name
= empty_chunk
;
376 chunk_t value
= empty_chunk
;
378 /* an empty line separates HEADER and BODY */
385 /* we are looking for a name: value pair */
386 if (!extract_parameter(&name
, &value
, &line
))
389 if (match("Proc-Type", &name
) && *value
.ptr
== '4')
391 else if (match("DEK-Info", &name
))
393 const char *ugh
= NULL
;
397 if (!extract_token(&dek
, ',', &value
))
400 /* we support DES-EDE3-CBC encrypted files, only */
401 if (!match("DES-EDE3-CBC", &dek
))
402 return "we support DES-EDE3-CBC encrypted files, only";
404 eat_whitespace(&value
);
405 ugh
= ttodata(value
.ptr
, value
.len
, 16,
406 iv
.ptr
, MAX_DIGEST_LEN
, &len
);
408 return "error in IV";
413 else /* state is PEM_BODY */
415 const char *ugh
= NULL
;
419 /* remove any trailing whitespace */
420 if (!extract_token(&data
,' ', &line
))
423 /* check for PGP armor checksum */
424 if (*data
.ptr
== '=')
430 DBG_log(" Armor checksum: %.*s", (int)data
.len
, data
.ptr
);
435 ugh
= ttodata(data
.ptr
, data
.len
, 64,
436 dst
.ptr
, blob
->len
- dst
.len
, &len
);
453 /* set length to size of binary blob */
456 if (state
!= PEM_POST
)
457 return "file coded in unknown format, discarded";
460 return pem_decrypt(blob
, &iv
, pass
, label
);