2 * Copyright (C) 2001-2008 Andreas Steffen
4 * Hochschule fuer Technik Rapperswil
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
25 #include <sys/types.h>
31 #include <asn1/asn1.h>
33 #include <utils/lexparser.h>
34 #include <crypto/hashers/hasher.h>
35 #include <crypto/crypters/crypter.h>
37 #define PKCS5_SALT_LEN 8 /* bytes */
40 * check the presence of a pattern in a character string
42 static bool present(const char* pattern
, chunk_t
* ch
)
44 u_int pattern_len
= strlen(pattern
);
46 if (ch
->len
>= pattern_len
&& strneq(ch
->ptr
, pattern
, pattern_len
))
48 ch
->ptr
+= pattern_len
;
49 ch
->len
-= pattern_len
;
56 * find a boundary of the form -----tag name-----
58 static bool find_boundary(const char* tag
, chunk_t
*line
)
60 chunk_t name
= chunk_empty
;
62 if (!present("-----", line
))
64 if (!present(tag
, line
))
66 if (*line
->ptr
!= ' ')
68 line
->ptr
++; line
->len
--;
74 if (present("-----", line
))
76 DBG2(" -----%s %.*s-----", tag
, (int)name
.len
, name
.ptr
);
79 line
->ptr
++; line
->len
--; name
.len
++;
85 * decrypts a passphrase protected encrypted data block
87 static err_t
pem_decrypt(chunk_t
*blob
, encryption_algorithm_t alg
, size_t key_size
,
88 chunk_t
*iv
, chunk_t
*passphrase
)
92 chunk_t salt
= { iv
->ptr
, PKCS5_SALT_LEN
};
95 chunk_t key
= {alloca(key_size
), key_size
};
96 u_int8_t padding
, *last_padding_pos
, *first_padding_pos
;
98 if (passphrase
== NULL
|| passphrase
->len
== 0)
100 return "missing passphrase";
103 /* build key from passphrase and IV */
104 hasher
= lib
->crypto
->create_hasher(lib
->crypto
, HASH_MD5
);
107 return "MD5 hasher not supported";
109 hash
.len
= hasher
->get_hash_size(hasher
);
110 hash
.ptr
= alloca(hash
.len
);
111 hasher
->get_hash(hasher
, *passphrase
, NULL
);
112 hasher
->get_hash(hasher
, salt
, hash
.ptr
);
113 memcpy(key
.ptr
, hash
.ptr
, hash
.len
);
115 if (key
.len
> hash
.len
)
117 hasher
->get_hash(hasher
, hash
, NULL
);
118 hasher
->get_hash(hasher
, *passphrase
, NULL
);
119 hasher
->get_hash(hasher
, salt
, hash
.ptr
);
120 memcpy(key
.ptr
+ hash
.len
, hash
.ptr
, key
.len
- hash
.len
);
122 hasher
->destroy(hasher
);
125 crypter
= lib
->crypto
->create_crypter(lib
->crypto
, alg
, key_size
);
126 crypter
->set_key(crypter
, key
);
128 if (iv
->len
!= crypter
->get_block_size(crypter
) ||
131 crypter
->destroy(crypter
);
132 return "data size is not multiple of block size";
134 crypter
->decrypt(crypter
, *blob
, *iv
, &decrypted
);
135 crypter
->destroy(crypter
);
136 memcpy(blob
->ptr
, decrypted
.ptr
, blob
->len
);
137 chunk_free(&decrypted
);
139 /* determine amount of padding */
140 last_padding_pos
= blob
->ptr
+ blob
->len
- 1;
141 padding
= *last_padding_pos
;
142 first_padding_pos
= (padding
> blob
->len
) ? blob
->ptr
: last_padding_pos
- padding
;
144 /* check the padding pattern */
145 while (--last_padding_pos
> first_padding_pos
)
147 if (*last_padding_pos
!= padding
)
148 return "invalid passphrase";
151 blob
->len
-= padding
;
155 /* Converts a PEM encoded file into its binary form
157 * RFC 1421 Privacy Enhancement for Electronic Mail, February 1993
158 * RFC 934 Message Encapsulation, January 1985
160 err_t
pem_to_bin(chunk_t
*blob
, chunk_t
*passphrase
, bool *pgp
)
171 encryption_algorithm_t alg
= ENCR_UNDEFINED
;
174 bool encrypted
= FALSE
;
176 state_t state
= PEM_PRE
;
180 chunk_t line
= chunk_empty
;
181 chunk_t iv
= chunk_empty
;
183 u_char iv_buf
[16]; /* MD5 digest size */
185 /* zero size of converted blob */
188 /* zero size of IV */
192 while (fetchline(&src
, &line
))
194 if (state
== PEM_PRE
)
196 if (find_boundary("BEGIN", &line
))
204 if (find_boundary("END", &line
))
209 if (state
== PEM_MSG
)
211 state
= (memchr(line
.ptr
, ':', line
.len
) == NULL
) ? PEM_BODY
: PEM_HEADER
;
213 if (state
== PEM_HEADER
)
216 chunk_t name
= chunk_empty
;
217 chunk_t value
= chunk_empty
;
219 /* an empty line separates HEADER and BODY */
226 /* we are looking for a parameter: value pair */
227 DBG2(" %.*s", (int)line
.len
, line
.ptr
);
228 ugh
= extract_parameter_value(&name
, &value
, &line
);
232 if (match("Proc-Type", &name
) && *value
.ptr
== '4')
234 else if (match("DEK-Info", &name
))
238 if (!extract_token(&dek
, ',', &value
))
241 if (match("DES-EDE3-CBC", &dek
))
246 else if (match("AES-128-CBC", &dek
))
251 else if (match("AES-192-CBC", &dek
))
256 else if (match("AES-256-CBC", &dek
))
263 return "encryption algorithm not supported";
265 eat_whitespace(&value
);
266 iv
= chunk_from_hex(value
, iv
.ptr
);
269 else /* state is PEM_BODY */
273 /* remove any trailing whitespace */
274 if (!extract_token(&data
,' ', &line
))
279 /* check for PGP armor checksum */
280 if (*data
.ptr
== '=')
285 DBG2(" Armor checksum: %.*s", (int)data
.len
, data
.ptr
);
289 if (blob
->len
- dst
.len
< data
.len
/ 4 * 3)
293 data
= chunk_from_base64(data
, dst
.ptr
);
300 /* set length to size of binary blob */
303 if (state
!= PEM_POST
)
304 return "file coded in unknown format, discarded";
310 return pem_decrypt(blob
, alg
, key_size
, &iv
, passphrase
);
314 /* load a coded key or certificate file with autodetection
315 * of binary DER or base64 PEM ASN.1 formats and armored PGP format
317 bool pem_asn1_load_file(char *filename
, chunk_t
*passphrase
,
318 chunk_t
*blob
, bool *pgp
)
322 FILE *fd
= fopen(filename
, "r");
327 fseek(fd
, 0, SEEK_END
);
328 blob
->len
= ftell(fd
);
330 blob
->ptr
= malloc(blob
->len
);
331 bytes
= fread(blob
->ptr
, 1, blob
->len
, fd
);
333 DBG2(" loading '%s' (%d bytes)", filename
, bytes
);
340 DBG2(" file coded in DER format");
344 if (passphrase
!= NULL
)
345 DBG4(" passphrase:", passphrase
->ptr
, passphrase
->len
);
348 ugh
= pem_to_bin(blob
, passphrase
, pgp
);
354 DBG2(" file coded in armored PGP format");
359 DBG2(" file coded in PEM format");
362 ugh
= "file coded in unknown format, discarded";
365 /* a conversion error has occured */
371 DBG1(" reading file '%s' failed", filename
);