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
21 #include <sys/types.h>
27 #include <utils/lexparser.h>
28 #include <utils/logger_manager.h>
29 #include <crypto/hashers/hasher.h>
30 #include <crypto/crypters/crypter.h>
32 static logger_t
*logger
= NULL
;
35 * initializes the PEM logger
37 static void pem_init_logger(void)
40 logger
= logger_manager
->get_logger(logger_manager
, ASN1
);
44 * check the presence of a pattern in a character string
46 static bool present(const char* pattern
, chunk_t
* ch
)
48 u_int pattern_len
= strlen(pattern
);
50 if (ch
->len
>= pattern_len
&& strncmp(ch
->ptr
, pattern
, pattern_len
) == 0)
52 ch
->ptr
+= pattern_len
;
53 ch
->len
-= pattern_len
;
60 * find a boundary of the form -----tag name-----
62 static bool find_boundary(const char* tag
, chunk_t
*line
)
64 chunk_t name
= CHUNK_INITIALIZER
;
66 if (!present("-----", line
))
68 if (!present(tag
, line
))
70 if (*line
->ptr
!= ' ')
72 line
->ptr
++; line
->len
--;
78 if (present("-----", line
))
80 logger
->log(logger
, CONTROL
|LEVEL2
,
81 " -----%s %.*s-----", tag
, (int)name
.len
, name
.ptr
);
84 line
->ptr
++; line
->len
--; name
.len
++;
90 * decrypts a DES-EDE-CBC encrypted data block
92 static err_t
pem_decrypt(chunk_t
*blob
, chunk_t
*iv
, char *passphrase
)
98 chunk_t pass
= {(char*)passphrase
, strlen(passphrase
)};
99 chunk_t key
= {alloca(24), 24};
100 u_int8_t padding
, *last_padding_pos
, *first_padding_pos
;
102 /* build key from passphrase and IV */
103 hasher
= hasher_create(HASH_MD5
);
104 hash
.len
= hasher
->get_hash_size(hasher
);
105 hash
.ptr
= alloca(hash
.len
);
106 hasher
->get_hash(hasher
, pass
, NULL
);
107 hasher
->get_hash(hasher
, *iv
, hash
.ptr
);
109 memcpy(key
.ptr
, hash
.ptr
, hash
.len
);
111 hasher
->get_hash(hasher
, hash
, NULL
);
112 hasher
->get_hash(hasher
, pass
, NULL
);
113 hasher
->get_hash(hasher
, *iv
, hash
.ptr
);
115 memcpy(key
.ptr
+ hash
.len
, hash
.ptr
, key
.len
- hash
.len
);
117 hasher
->destroy(hasher
);
120 crypter
= crypter_create(ENCR_3DES
, 0);
121 crypter
->set_key(crypter
, key
);
122 crypter
->decrypt(crypter
, *blob
, *iv
, &decrypted
);
123 memcpy(blob
->ptr
, decrypted
.ptr
, blob
->len
);
124 chunk_free(&decrypted
);
126 /* determine amount of padding */
127 last_padding_pos
= blob
->ptr
+ blob
->len
- 1;
128 padding
= *last_padding_pos
;
129 first_padding_pos
= (padding
> blob
->len
) ? blob
->ptr
: last_padding_pos
- padding
;
131 /* check the padding pattern */
132 while (--last_padding_pos
> first_padding_pos
)
134 if (*last_padding_pos
!= padding
)
135 return "invalid passphrase";
138 blob
->len
-= padding
;
142 /* Converts a PEM encoded file into its binary form
144 * RFC 1421 Privacy Enhancement for Electronic Mail, February 1993
145 * RFC 934 Message Encapsulation, January 1985
147 err_t
pem_to_bin(chunk_t
*blob
, char *passphrase
, bool *pgp
)
158 bool encrypted
= FALSE
;
160 state_t state
= PEM_PRE
;
164 chunk_t line
= CHUNK_INITIALIZER
;
165 chunk_t iv
= CHUNK_INITIALIZER
;
167 u_char iv_buf
[16]; /* MD5 digest size */
169 /* zero size of converted blob */
172 /* zero size of IV */
178 while (fetchline(&src
, &line
))
180 if (state
== PEM_PRE
)
182 if (find_boundary("BEGIN", &line
))
190 if (find_boundary("END", &line
))
195 if (state
== PEM_MSG
)
197 state
= (memchr(line
.ptr
, ':', line
.len
) == NULL
) ? PEM_BODY
: PEM_HEADER
;
199 if (state
== PEM_HEADER
)
201 chunk_t name
= CHUNK_INITIALIZER
;
202 chunk_t value
= CHUNK_INITIALIZER
;
204 /* an empty line separates HEADER and BODY */
211 /* we are looking for a parameter: value pair */
212 logger
->log(logger
, CONTROL
|LEVEL2
, " %.*s", (int)line
.len
, line
.ptr
);
213 if (!extract_parameter_value(&name
, &value
, &line
))
216 if (match("Proc-Type", &name
) && *value
.ptr
== '4')
218 else if (match("DEK-Info", &name
))
220 const char *ugh
= NULL
;
224 if (!extract_token(&dek
, ',', &value
))
227 /* we support DES-EDE3-CBC encrypted files, only */
228 if (!match("DES-EDE3-CBC", &dek
))
229 return "encryption algorithm not supported";
231 eat_whitespace(&value
);
232 ugh
= ttodata(value
.ptr
, value
.len
, 16, iv
.ptr
, 16, &len
);
234 return "error in IV";
239 else /* state is PEM_BODY */
241 const char *ugh
= NULL
;
245 /* remove any trailing whitespace */
246 if (!extract_token(&data
,' ', &line
))
251 /* check for PGP armor checksum */
252 if (*data
.ptr
== '=')
257 logger
->log(logger
, CONTROL
|LEVEL2
, " Armor checksum: %.*s",
258 (int)data
.len
, data
.ptr
);
262 ugh
= ttodata(data
.ptr
, data
.len
, 64, dst
.ptr
, blob
->len
- dst
.len
, &len
);
276 /* set length to size of binary blob */
279 if (state
!= PEM_POST
)
280 return "file coded in unknown format, discarded";
282 return (encrypted
)?
pem_decrypt(blob
, &iv
, passphrase
) : NULL
;
285 /* load a coded key or certificate file with autodetection
286 * of binary DER or base64 PEM ASN.1 formats and armored PGP format
288 bool pem_asn1_load_file(const char *filename
, char *passphrase
,
289 const char *type
, chunk_t
*blob
, bool *pgp
)
293 FILE *fd
= fopen(filename
, "r");
300 fseek(fd
, 0, SEEK_END
);
301 blob
->len
= ftell(fd
);
303 blob
->ptr
= malloc(blob
->len
);
304 bytes
= fread(blob
->ptr
, 1, blob
->len
, fd
);
306 logger
->log(logger
, CONTROL
, " loaded %s file '%s' (%d bytes)", type
, filename
, bytes
);
313 logger
->log(logger
, CONTROL
|LEVEL1
, " file coded in DER format");
318 ugh
= pem_to_bin(blob
, passphrase
, pgp
);
324 logger
->log(logger
, CONTROL
|LEVEL1
, " file coded in armored PGP format");
329 logger
->log(logger
, CONTROL
|LEVEL1
, " file coded in PEM format");
332 ugh
= "file coded in unknown format, discarded";
335 /* a conversion error has occured */
336 logger
->log(logger
, ERROR
, " %s", ugh
);
341 logger
->log(logger
, ERROR
, " could not open %s file '%s'", type
, filename
);