2 * Copyright (C) 2009 Martin Willi
3 * 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
17 #include "pem_builder.h"
26 #include <sys/types.h>
32 #include <utils/lexparser.h>
33 #include <asn1/asn1.h>
34 #include <crypto/hashers/hasher.h>
35 #include <crypto/crypters/crypter.h>
36 #include <credentials/certificates/x509.h>
38 #define PKCS5_SALT_LEN 8 /* bytes */
41 * check the presence of a pattern in a character string, skip if found
43 static bool present(char* pattern
, chunk_t
* ch
)
45 u_int len
= strlen(pattern
);
47 if (ch
->len
>= len
&& strneq(ch
->ptr
, pattern
, len
))
49 *ch
= chunk_skip(*ch
, len
);
56 * find a boundary of the form -----tag name-----
58 static bool find_boundary(char* tag
, chunk_t
*line
)
60 chunk_t name
= chunk_empty
;
62 if (!present("-----", line
) ||
63 !present(tag
, line
) ||
68 *line
= chunk_skip(*line
, 1);
74 if (present("-----", line
))
76 DBG2(DBG_LIB
, " -----%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 status_t
pem_decrypt(chunk_t
*blob
, encryption_algorithm_t alg
,
88 size_t key_size
, 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 /* build key from passphrase and IV */
99 hasher
= lib
->crypto
->create_hasher(lib
->crypto
, HASH_MD5
);
102 DBG1(DBG_LIB
, " MD5 hash algorithm not available");
103 return NOT_SUPPORTED
;
105 hash
.len
= hasher
->get_hash_size(hasher
);
106 hash
.ptr
= alloca(hash
.len
);
107 hasher
->get_hash(hasher
, passphrase
, NULL
);
108 hasher
->get_hash(hasher
, salt
, hash
.ptr
);
109 memcpy(key
.ptr
, hash
.ptr
, hash
.len
);
111 if (key
.len
> hash
.len
)
113 hasher
->get_hash(hasher
, hash
, NULL
);
114 hasher
->get_hash(hasher
, passphrase
, NULL
);
115 hasher
->get_hash(hasher
, salt
, hash
.ptr
);
116 memcpy(key
.ptr
+ hash
.len
, hash
.ptr
, key
.len
- hash
.len
);
118 hasher
->destroy(hasher
);
121 crypter
= lib
->crypto
->create_crypter(lib
->crypto
, alg
, key_size
);
124 DBG1(DBG_LIB
, " %N encryption algorithm not available",
125 encryption_algorithm_names
, alg
);
126 return NOT_SUPPORTED
;
128 crypter
->set_key(crypter
, key
);
130 if (iv
.len
!= crypter
->get_iv_size(crypter
) ||
131 blob
->len
% crypter
->get_block_size(crypter
))
133 crypter
->destroy(crypter
);
134 DBG1(DBG_LIB
, " data size is not multiple of block size");
137 crypter
->decrypt(crypter
, *blob
, iv
, &decrypted
);
138 crypter
->destroy(crypter
);
139 memcpy(blob
->ptr
, decrypted
.ptr
, blob
->len
);
140 chunk_free(&decrypted
);
142 /* determine amount of padding */
143 last_padding_pos
= blob
->ptr
+ blob
->len
- 1;
144 padding
= *last_padding_pos
;
145 if (padding
> blob
->len
)
147 first_padding_pos
= blob
->ptr
;
151 first_padding_pos
= last_padding_pos
- padding
;
153 /* check the padding pattern */
154 while (--last_padding_pos
> first_padding_pos
)
156 if (*last_padding_pos
!= padding
)
158 DBG1(DBG_LIB
, " invalid passphrase");
163 blob
->len
-= padding
;
168 * Converts a PEM encoded file into its binary form (RFC 1421, RFC 934)
170 static status_t
pem_to_bin(chunk_t
*blob
, bool *pgp
)
181 encryption_algorithm_t alg
= ENCR_UNDEFINED
;
183 bool encrypted
= FALSE
;
184 state_t state
= PEM_PRE
;
187 chunk_t line
= chunk_empty
;
188 chunk_t iv
= chunk_empty
;
189 u_char iv_buf
[HASH_SIZE_MD5
];
190 status_t status
= NOT_FOUND
;
191 enumerator_t
*enumerator
;
192 shared_key_t
*shared
;
198 while (fetchline(&src
, &line
))
200 if (state
== PEM_PRE
)
202 if (find_boundary("BEGIN", &line
))
210 if (find_boundary("END", &line
))
215 if (state
== PEM_MSG
)
218 if (memchr(line
.ptr
, ':', line
.len
) == NULL
)
223 if (state
== PEM_HEADER
)
226 chunk_t name
= chunk_empty
;
227 chunk_t value
= chunk_empty
;
229 /* an empty line separates HEADER and BODY */
236 /* we are looking for a parameter: value pair */
237 DBG2(DBG_LIB
, " %.*s", (int)line
.len
, line
.ptr
);
238 ugh
= extract_parameter_value(&name
, &value
, &line
);
243 if (match("Proc-Type", &name
) && *value
.ptr
== '4')
247 else if (match("DEK-Info", &name
))
251 if (!extract_token(&dek
, ',', &value
))
255 if (match("DES-EDE3-CBC", &dek
))
260 else if (match("AES-128-CBC", &dek
))
265 else if (match("AES-192-CBC", &dek
))
270 else if (match("AES-256-CBC", &dek
))
277 DBG1(DBG_LIB
, " encryption algorithm '%.*s'"
278 " not supported", dek
.len
, dek
.ptr
);
279 return NOT_SUPPORTED
;
281 eat_whitespace(&value
);
282 iv
= chunk_from_hex(value
, iv
.ptr
);
285 else /* state is PEM_BODY */
289 /* remove any trailing whitespace */
290 if (!extract_token(&data
,' ', &line
))
295 /* check for PGP armor checksum */
296 if (*data
.ptr
== '=')
301 DBG2(DBG_LIB
, " armor checksum: %.*s", (int)data
.len
,
306 if (blob
->len
- dst
.len
< data
.len
/ 4 * 3)
310 data
= chunk_from_base64(data
, dst
.ptr
);
317 /* set length to size of binary blob */
320 if (state
!= PEM_POST
)
322 DBG1(DBG_LIB
, " file coded in unknown format, discarded");
330 enumerator
= lib
->credmgr
->create_shared_enumerator(lib
->credmgr
,
331 SHARED_PRIVATE_KEY_PASS
, NULL
, NULL
);
332 while (enumerator
->enumerate(enumerator
, &shared
, NULL
, NULL
))
334 chunk_t passphrase
, chunk
;
336 passphrase
= shared
->get_key(shared
);
337 chunk
= chunk_clone(*blob
);
338 status
= pem_decrypt(&chunk
, alg
, key_size
, iv
, passphrase
);
339 if (status
== SUCCESS
)
341 memcpy(blob
->ptr
, chunk
.ptr
, chunk
.len
);
342 blob
->len
= chunk
.len
;
345 if (status
!= INVALID_ARG
)
346 { /* try again only if passphrase invalid */
350 enumerator
->destroy(enumerator
);
355 * load the credential from a blob
357 static void *load_from_blob(chunk_t blob
, credential_type_t type
, int subtype
,
363 blob
= chunk_clone(blob
);
366 if (pem_to_bin(&blob
, &pgp
) != SUCCESS
)
371 if (pgp
&& type
== CRED_PRIVATE_KEY
)
373 /* PGP encoded keys are parsed with a KEY_ANY key type, as it
374 * can contain any type of key. However, ipsec.secrets uses
375 * RSA for PGP keys, which is actually wrong. */
379 /* if CERT_ANY is given, ASN1 encoded blob is handled as X509 */
380 if (type
== CRED_CERTIFICATE
&& subtype
== CERT_ANY
)
382 subtype
= pgp ? CERT_GPG
: CERT_X509
;
384 cred
= lib
->creds
->create(lib
->creds
, type
, subtype
,
385 pgp ? BUILD_BLOB_PGP
: BUILD_BLOB_ASN1_DER
, blob
,
386 flags ? BUILD_X509_FLAG
: BUILD_END
,
393 * load the credential from a file
395 static void *load_from_file(char *file
, credential_type_t type
, int subtype
,
403 fd
= open(file
, O_RDONLY
);
406 DBG1(DBG_LIB
, " opening '%s' failed: %s", file
, strerror(errno
));
410 if (fstat(fd
, &sb
) == -1)
412 DBG1(DBG_LIB
, " getting file size of '%s' failed: %s", file
,
418 addr
= mmap(NULL
, sb
.st_size
, PROT_READ
, MAP_PRIVATE
, fd
, 0);
419 if (addr
== MAP_FAILED
)
421 DBG1(DBG_LIB
, " mapping '%s' failed: %s", file
, strerror(errno
));
426 cred
= load_from_blob(chunk_create(addr
, sb
.st_size
), type
, subtype
, flags
);
428 munmap(addr
, sb
.st_size
);
434 * load the credential from a file descriptor
436 static void *load_from_fd(int fd
, credential_type_t type
, int subtype
,
441 ssize_t len
, total
= 0;
445 len
= read(fd
, pos
, buf
+ sizeof(buf
) - pos
);
448 DBG1(DBG_LIB
, "reading from file descriptor failed: %s",
457 if (total
== sizeof(buf
))
459 DBG1(DBG_LIB
, "buffer too small to read from file descriptor");
463 return load_from_blob(chunk_create(buf
, total
), type
, subtype
, flags
);
467 * Load all kind of PEM encoded credentials.
469 static void *pem_load(credential_type_t type
, int subtype
, va_list args
)
473 chunk_t pem
= chunk_empty
;
478 switch (va_arg(args
, builder_part_t
))
480 case BUILD_FROM_FILE
:
481 file
= va_arg(args
, char*);
484 fd
= va_arg(args
, int);
487 pem
= va_arg(args
, chunk_t
);
489 case BUILD_X509_FLAG
:
490 flags
= va_arg(args
, int);
502 return load_from_blob(pem
, type
, subtype
, flags
);
506 return load_from_file(file
, type
, subtype
, flags
);
510 return load_from_fd(fd
, type
, subtype
, flags
);
516 * Private key PEM loader.
518 private_key_t
*pem_private_key_load(key_type_t type
, va_list args
)
520 return pem_load(CRED_PRIVATE_KEY
, type
, args
);
524 * Public key PEM loader.
526 public_key_t
*pem_public_key_load(key_type_t type
, va_list args
)
528 return pem_load(CRED_PUBLIC_KEY
, type
, args
);
532 * Certificate PEM loader.
534 certificate_t
*pem_certificate_load(certificate_type_t type
, va_list args
)
536 return pem_load(CRED_CERTIFICATE
, type
, args
);