From 4076e3ee91212b100bb601037565dc00abd41e0b Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Mon, 8 Apr 2013 18:13:03 +0200 Subject: [PATCH 01/29] Extract PKCS#5 handling from pkcs8 plugin to separate helper class --- src/libstrongswan/Android.mk | 2 +- src/libstrongswan/Makefile.am | 4 +- src/libstrongswan/crypto/pkcs5.c | 630 ++++++++++++++++++ src/libstrongswan/crypto/pkcs5.h | 61 ++ .../plugins/pkcs8/pkcs8_builder.c | 471 +------------ 5 files changed, 710 insertions(+), 458 deletions(-) create mode 100644 src/libstrongswan/crypto/pkcs5.c create mode 100644 src/libstrongswan/crypto/pkcs5.h diff --git a/src/libstrongswan/Android.mk b/src/libstrongswan/Android.mk index a46b0d9a1..289697a46 100644 --- a/src/libstrongswan/Android.mk +++ b/src/libstrongswan/Android.mk @@ -8,7 +8,7 @@ asn1/asn1.c asn1/asn1_parser.c asn1/oid.c bio/bio_reader.c bio/bio_writer.c \ collections/blocking_queue.c collections/enumerator.c collections/hashtable.c \ collections/linked_list.c crypto/crypters/crypter.c crypto/hashers/hasher.c \ crypto/proposal/proposal_keywords.c crypto/proposal/proposal_keywords_static.c \ -crypto/prfs/prf.c crypto/prfs/mac_prf.c \ +crypto/prfs/prf.c crypto/prfs/mac_prf.c crypto/pkcs5.c \ crypto/rngs/rng.c crypto/prf_plus.c crypto/signers/signer.c \ crypto/signers/mac_signer.c crypto/crypto_factory.c crypto/crypto_tester.c \ crypto/diffie_hellman.c crypto/aead.c crypto/transform.c \ diff --git a/src/libstrongswan/Makefile.am b/src/libstrongswan/Makefile.am index b5a4b9bab..5968a3b3f 100644 --- a/src/libstrongswan/Makefile.am +++ b/src/libstrongswan/Makefile.am @@ -6,7 +6,7 @@ asn1/asn1.c asn1/asn1_parser.c asn1/oid.c bio/bio_reader.c bio/bio_writer.c \ collections/blocking_queue.c collections/enumerator.c collections/hashtable.c \ collections/linked_list.c crypto/crypters/crypter.c crypto/hashers/hasher.c \ crypto/proposal/proposal_keywords.c crypto/proposal/proposal_keywords_static.c \ -crypto/prfs/prf.c crypto/prfs/mac_prf.c \ +crypto/prfs/prf.c crypto/prfs/mac_prf.c crypto/pkcs5.c \ crypto/rngs/rng.c crypto/prf_plus.c crypto/signers/signer.c \ crypto/signers/mac_signer.c crypto/crypto_factory.c crypto/crypto_tester.c \ crypto/diffie_hellman.c crypto/aead.c crypto/transform.c \ @@ -45,7 +45,7 @@ crypto/proposal/proposal_keywords.h crypto/proposal/proposal_keywords_static.h \ crypto/prfs/prf.h crypto/prfs/mac_prf.h crypto/rngs/rng.h crypto/nonce_gen.h \ crypto/prf_plus.h crypto/signers/signer.h crypto/signers/mac_signer.h \ crypto/crypto_factory.h crypto/crypto_tester.h crypto/diffie_hellman.h \ -crypto/aead.h crypto/transform.h \ +crypto/aead.h crypto/transform.h crypto/pkcs5.h \ credentials/credential_factory.h credentials/builder.h \ credentials/cred_encoding.h credentials/keys/private_key.h \ credentials/keys/public_key.h credentials/keys/shared_key.h \ diff --git a/src/libstrongswan/crypto/pkcs5.c b/src/libstrongswan/crypto/pkcs5.c new file mode 100644 index 000000000..76a67fc4a --- /dev/null +++ b/src/libstrongswan/crypto/pkcs5.c @@ -0,0 +1,630 @@ +/* + * Copyright (C) 2012-2013 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pkcs5.h" + +#include +#include +#include +#include + +typedef struct private_pkcs5_t private_pkcs5_t; + +/** + * Private data of a pkcs5_t object + */ +struct private_pkcs5_t { + + /** + * Implements pkcs5_t. + */ + pkcs5_t public; + + /** + * Salt used during encryption + */ + chunk_t salt; + + /** + * Iterations for key derivation + */ + u_int64_t iterations; + + /** + * Encryption algorithm + */ + encryption_algorithm_t encr; + + /** + * Encryption key length + */ + size_t keylen; + + /** + * Crypter + */ + crypter_t *crypter; + + + /** + * The encryption scheme + */ + enum { + PKCS5_SCHEME_PBES1, + PKCS5_SCHEME_PBES2, + } scheme; + + /** + * Data used for individual schemes + */ + union { + struct { + /** + * Hash algorithm + */ + hash_algorithm_t hash; + + /** + * Hasher + */ + hasher_t *hasher; + + } pbes1; + struct { + /** + * PRF algorithm + */ + pseudo_random_function_t prf_alg; + + /** + * PRF + */ + prf_t * prf; + + /** + * IV + */ + chunk_t iv; + + } pbes2; + } data; +}; + +/** + * Verify padding of decrypted blob. + * Length of blob is adjusted accordingly. + */ +static bool verify_padding(chunk_t *blob) +{ + u_int8_t padding, count; + + padding = count = blob->ptr[blob->len - 1]; + + if (padding > 8) + { + return FALSE; + } + for (; blob->len && count; --blob->len, --count) + { + if (blob->ptr[blob->len - 1] != padding) + { + return FALSE; + } + } + return TRUE; +} + +/** + * Prototype for key derivation functions. + */ +typedef bool (*kdf_t)(private_pkcs5_t *this, chunk_t password, chunk_t key); + +/** + * Try to decrypt the given data with the given password using the given + * key derivation function. keymat is where the kdf function writes the key + * to, key and iv point to the actual keys and initialization vectors resp. + */ +static bool decrypt_generic(private_pkcs5_t *this, chunk_t password, + chunk_t data, chunk_t *decrypted, kdf_t kdf, + chunk_t keymat, chunk_t key, chunk_t iv) +{ + if (!kdf(this, password, keymat)) + { + return FALSE; + } + if (!this->crypter->set_key(this->crypter, key) || + !this->crypter->decrypt(this->crypter, data, iv, decrypted)) + { + memwipe(keymat.ptr, keymat.len); + return FALSE; + } + memwipe(keymat.ptr, keymat.len); + if (verify_padding(decrypted)) + { + return TRUE; + } + chunk_free(decrypted); + return FALSE; +} + +/** + * Function F of PBKDF2 + */ +static bool pbkdf2_f(chunk_t block, prf_t *prf, chunk_t seed, + u_int64_t iterations) +{ + chunk_t u; + u_int64_t i; + + u = chunk_alloca(prf->get_block_size(prf)); + if (!prf->get_bytes(prf, seed, u.ptr)) + { + return FALSE; + } + memcpy(block.ptr, u.ptr, block.len); + + for (i = 1; i < iterations; i++) + { + if (!prf->get_bytes(prf, u, u.ptr)) + { + return FALSE; + } + memxor(block.ptr, u.ptr, block.len); + } + return TRUE; +} + +/** + * PBKDF2 key derivation function for PBES2, key must be allocated + */ +static bool pbkdf2(private_pkcs5_t *this, chunk_t password, chunk_t key) +{ + prf_t *prf; + chunk_t keymat, block, seed; + size_t blocks; + u_int32_t i = 0; + + prf = this->data.pbes2.prf; + + if (!prf->set_key(prf, password)) + { + return FALSE; + } + + block.len = prf->get_block_size(prf); + blocks = (key.len - 1) / block.len + 1; + keymat = chunk_alloca(blocks * block.len); + + seed = chunk_cata("cc", this->salt, chunk_from_thing(i)); + + for (; i < blocks; i++) + { + htoun32(seed.ptr + this->salt.len, i + 1); + block.ptr = keymat.ptr + (i * block.len); + if (!pbkdf2_f(block, prf, seed, this->iterations)) + { + return FALSE; + } + } + memcpy(key.ptr, keymat.ptr, key.len); + return TRUE; +} + +/** + * PBKDF1 key derivation function for PBES1, key must be allocated + */ +static bool pbkdf1(private_pkcs5_t *this, chunk_t password, chunk_t key) +{ + hasher_t *hasher; + chunk_t hash; + u_int64_t i; + + hasher = this->data.pbes1.hasher; + + hash = chunk_alloca(hasher->get_hash_size(hasher)); + if (!hasher->get_hash(hasher, password, NULL) || + !hasher->get_hash(hasher, this->salt, hash.ptr)) + { + return FALSE; + } + + for (i = 1; i < this->iterations; i++) + { + if (!hasher->get_hash(hasher, hash, hash.ptr)) + { + return FALSE; + } + } + memcpy(key.ptr, hash.ptr, key.len); + return TRUE; +} + +static bool ensure_crypto_primitives(private_pkcs5_t *this, chunk_t data) +{ + if (!this->crypter) + { + this->crypter = lib->crypto->create_crypter(lib->crypto, this->encr, + this->keylen); + if (!this->crypter) + { + DBG1(DBG_ASN, " %N encryption algorithm not available", + encryption_algorithm_names, this->encr); + return FALSE; + } + } + if (data.len % this->crypter->get_block_size(this->crypter)) + { + DBG1(DBG_ASN, " data size is not a multiple of block size"); + return FALSE; + } + switch (this->scheme) + { + case PKCS5_SCHEME_PBES1: + { + if (!this->data.pbes1.hasher) + { + hasher_t *hasher; + + hasher = lib->crypto->create_hasher(lib->crypto, + this->data.pbes1.hash); + if (!hasher) + { + DBG1(DBG_ASN, " %N hash algorithm not available", + hash_algorithm_names, this->data.pbes1.hash); + return FALSE; + } + if (hasher->get_hash_size(hasher) < this->keylen) + { + hasher->destroy(hasher); + return FALSE; + } + this->data.pbes1.hasher = hasher; + } + } + case PKCS5_SCHEME_PBES2: + { + if (!this->data.pbes2.prf) + { + prf_t *prf; + + prf = lib->crypto->create_prf(lib->crypto, + this->data.pbes2.prf_alg); + if (!prf) + { + DBG1(DBG_ASN, " %N prf algorithm not available", + pseudo_random_function_names, + this->data.pbes2.prf_alg); + return FALSE; + } + this->data.pbes2.prf = prf; + } + } + } + return TRUE; +} + +METHOD(pkcs5_t, decrypt, bool, + private_pkcs5_t *this, chunk_t password, chunk_t data, chunk_t *decrypted) +{ + chunk_t keymat, key, iv; + kdf_t kdf; + + if (!ensure_crypto_primitives(this, data) || !decrypted) + { + return FALSE; + } + switch (this->scheme) + { + case PKCS5_SCHEME_PBES1: + kdf = pbkdf1; + keymat = chunk_alloca(this->keylen * 2); + key = chunk_create(keymat.ptr, this->keylen); + iv = chunk_create(keymat.ptr + this->keylen, this->keylen); + break; + case PKCS5_SCHEME_PBES2: + kdf = pbkdf2; + keymat = chunk_alloca(this->keylen); + key = keymat; + iv = this->data.pbes2.iv; + break; + default: + return FALSE; + } + return decrypt_generic(this, password, data, decrypted, kdf, + keymat, key, iv); +} + +/** + * Converts an ASN.1 INTEGER object to an u_int64_t. If the INTEGER is longer + * than 8 bytes only the 8 LSBs are returned. + * + * @param blob body of an ASN.1 coded integer object + * @return converted integer + */ +static u_int64_t parse_asn1_integer_uint64(chunk_t blob) +{ + u_int64_t val = 0; + int i; + + for (i = 0; i < blob.len; i++) + { /* if it is longer than 8 bytes, we just use the 8 LSBs */ + val <<= 8; + val |= (u_int64_t)blob.ptr[i]; + } + return val; +} + +/** + * ASN.1 definition of a PBEParameter structure + */ +static const asn1Object_t pbeParameterObjects[] = { + { 0, "PBEParameter", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ + { 1, "salt", ASN1_OCTET_STRING, ASN1_BODY }, /* 1 */ + { 1, "iterationCount", ASN1_INTEGER, ASN1_BODY }, /* 2 */ + { 0, "exit", ASN1_EOC, ASN1_EXIT } +}; +#define PBEPARAM_SALT 1 +#define PBEPARAM_ITERATION_COUNT 2 + +/** + * Parse a PBEParameter structure + */ +static bool parse_pbes1_params(private_pkcs5_t *this, chunk_t blob, int level0) +{ + asn1_parser_t *parser; + chunk_t object; + int objectID; + bool success; + + parser = asn1_parser_create(pbeParameterObjects, blob); + parser->set_top_level(parser, level0); + + while (parser->iterate(parser, &objectID, &object)) + { + switch (objectID) + { + case PBEPARAM_SALT: + { + this->salt = chunk_clone(object); + break; + } + case PBEPARAM_ITERATION_COUNT: + { + this->iterations = parse_asn1_integer_uint64(object); + break; + } + } + } + success = parser->success(parser); + parser->destroy(parser); + return success; +} + +/** + * ASN.1 definition of a PBKDF2-params structure + * The salt is actually a CHOICE and could be an AlgorithmIdentifier from + * PBKDF2-SaltSources (but as per RFC 2898 that's for future versions). + */ +static const asn1Object_t pbkdf2ParamsObjects[] = { + { 0, "PBKDF2-params", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ + { 1, "salt", ASN1_OCTET_STRING, ASN1_BODY }, /* 1 */ + { 1, "iterationCount",ASN1_INTEGER, ASN1_BODY }, /* 2 */ + { 1, "keyLength", ASN1_INTEGER, ASN1_OPT|ASN1_BODY }, /* 3 */ + { 1, "end opt", ASN1_EOC, ASN1_END }, /* 4 */ + { 1, "prf", ASN1_EOC, ASN1_DEF|ASN1_RAW }, /* 5 */ + { 0, "exit", ASN1_EOC, ASN1_EXIT } +}; +#define PBKDF2_SALT 1 +#define PBKDF2_ITERATION_COUNT 2 +#define PBKDF2_KEYLENGTH 3 +#define PBKDF2_PRF 5 + +/** + * Parse a PBKDF2-params structure + */ +static bool parse_pbkdf2_params(private_pkcs5_t *this, chunk_t blob, int level0) +{ + asn1_parser_t *parser; + chunk_t object; + int objectID; + bool success; + + parser = asn1_parser_create(pbkdf2ParamsObjects, blob); + parser->set_top_level(parser, level0); + + /* keylen is optional */ + this->keylen = 0; + + while (parser->iterate(parser, &objectID, &object)) + { + switch (objectID) + { + case PBKDF2_SALT: + { + this->salt = chunk_clone(object); + break; + } + case PBKDF2_ITERATION_COUNT: + { + this->iterations = parse_asn1_integer_uint64(object); + break; + } + case PBKDF2_KEYLENGTH: + { + this->keylen = (size_t)parse_asn1_integer_uint64(object); + break; + } + case PBKDF2_PRF: + { /* defaults to id-hmacWithSHA1, no other is currently defined */ + this->data.pbes2.prf_alg = PRF_HMAC_SHA1; + break; + } + } + } + success = parser->success(parser); + parser->destroy(parser); + return success; +} + +/** + * ASN.1 definition of a PBES2-params structure + */ +static const asn1Object_t pbes2ParamsObjects[] = { + { 0, "PBES2-params", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ + { 1, "keyDerivationFunc", ASN1_EOC, ASN1_RAW }, /* 1 */ + { 1, "encryptionScheme", ASN1_EOC, ASN1_RAW }, /* 2 */ + { 0, "exit", ASN1_EOC, ASN1_EXIT } +}; +#define PBES2PARAMS_KEY_DERIVATION_FUNC 1 +#define PBES2PARAMS_ENCRYPTION_SCHEME 2 + +/** + * Parse a PBES2-params structure + */ +static bool parse_pbes2_params(private_pkcs5_t *this, chunk_t blob, int level0) +{ + asn1_parser_t *parser; + chunk_t object, params; + int objectID; + bool success = FALSE; + + parser = asn1_parser_create(pbes2ParamsObjects, blob); + parser->set_top_level(parser, level0); + + while (parser->iterate(parser, &objectID, &object)) + { + switch (objectID) + { + case PBES2PARAMS_KEY_DERIVATION_FUNC: + { + int oid = asn1_parse_algorithmIdentifier(object, + parser->get_level(parser) + 1, ¶ms); + if (oid != OID_PBKDF2) + { /* unsupported key derivation function */ + goto end; + } + if (!parse_pbkdf2_params(this, params, + parser->get_level(parser) + 1)) + { + goto end; + } + break; + } + case PBES2PARAMS_ENCRYPTION_SCHEME: + { + int oid = asn1_parse_algorithmIdentifier(object, + parser->get_level(parser) + 1, ¶ms); + if (oid != OID_3DES_EDE_CBC) + { /* unsupported encryption scheme */ + goto end; + } + if (this->keylen <= 0) + { /* default key length for DES-EDE3-CBC-Pad */ + this->keylen = 24; + } + if (!asn1_parse_simple_object(¶ms, ASN1_OCTET_STRING, + parser->get_level(parser) + 1, "IV")) + { + goto end; + } + this->encr = ENCR_3DES; + this->data.pbes2.iv = chunk_clone(params); + break; + } + } + } + success = parser->success(parser); +end: + parser->destroy(parser); + return success; +} + +METHOD(pkcs5_t, destroy, void, + private_pkcs5_t *this) +{ + DESTROY_IF(this->crypter); + chunk_free(&this->salt); + switch (this->scheme) + { + case PKCS5_SCHEME_PBES1: + DESTROY_IF(this->data.pbes1.hasher); + break; + case PKCS5_SCHEME_PBES2: + DESTROY_IF(this->data.pbes2.prf); + chunk_free(&this->data.pbes2.iv); + break; + } + free(this); +} + +/* + * Described in header + */ +pkcs5_t *pkcs5_from_algorithmIdentifier(chunk_t blob, int level0) +{ + private_pkcs5_t *this; + chunk_t params; + int oid; + + INIT(this, + .public = { + .decrypt = _decrypt, + .destroy = _destroy, + }, + .scheme = PKCS5_SCHEME_PBES1, + .keylen = 8, + ); + + oid = asn1_parse_algorithmIdentifier(blob, level0, ¶ms); + + switch (oid) + { + case OID_PBE_MD5_DES_CBC: + this->encr = ENCR_DES; + this->data.pbes1.hash = HASH_MD5; + break; + case OID_PBE_SHA1_DES_CBC: + this->encr = ENCR_DES; + this->data.pbes1.hash = HASH_SHA1; + break; + case OID_PBES2: + this->scheme = PKCS5_SCHEME_PBES2; + break; + default: + /* encryption scheme not supported */ + goto failure; + } + + switch (this->scheme) + { + case PKCS5_SCHEME_PBES1: + if (!parse_pbes1_params(this, params, level0)) + { + goto failure; + } + break; + case PKCS5_SCHEME_PBES2: + if (!parse_pbes2_params(this, params, level0)) + { + goto failure; + } + break; + } + return &this->public; + +failure: + destroy(this); + return NULL; +} diff --git a/src/libstrongswan/crypto/pkcs5.h b/src/libstrongswan/crypto/pkcs5.h new file mode 100644 index 000000000..b16d3736e --- /dev/null +++ b/src/libstrongswan/crypto/pkcs5.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2013 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup pkcs5 pkcs5 + * @{ @ingroup crypto + */ + +#ifndef PKCS5_H_ +#define PKCS5_H_ + +typedef struct pkcs5_t pkcs5_t; + +#include + +/** + * PKCS#5 helper class + */ +struct pkcs5_t { + + /** + * Decrypt the given data using the given password and the scheme derived + * from the initial AlgorithmIdentifier object. + * + * @param password password used for decryption + * @param data data to decrypt + * @param decrypted decrypted data gets allocated + * @return TRUE on success, FALSE otherwise + */ + bool (*decrypt)(pkcs5_t *this, chunk_t password, chunk_t data, + chunk_t *decrypted) __attribute__((warn_unused_result)); + + /** + * Destroy the object and any associated cryptographic primitive. + */ + void (*destroy)(pkcs5_t *this); +}; + +/** + * Create a PKCS#5 helper object from an ASN.1 encoded AlgorithmIdentifier + * object. + * + * @param blob ASN.1 encoded AlgorithmIdentifier + * @param level0 ASN.1 parser level + * @return pkcs5_t object, NULL on failure + */ +pkcs5_t *pkcs5_from_algorithmIdentifier(chunk_t blob, int level0); + +#endif /** PKCS5_H_ @}*/ diff --git a/src/libstrongswan/plugins/pkcs8/pkcs8_builder.c b/src/libstrongswan/plugins/pkcs8/pkcs8_builder.c index 26a3620d7..e93a8361c 100644 --- a/src/libstrongswan/plugins/pkcs8/pkcs8_builder.c +++ b/src/libstrongswan/plugins/pkcs8/pkcs8_builder.c @@ -19,6 +19,7 @@ #include #include #include +#include #include /** @@ -100,450 +101,39 @@ end: return key; } -/** - * Verify padding of decrypted blob. - * Length of blob is adjusted accordingly. - */ -static bool verify_padding(chunk_t *blob) -{ - u_int8_t padding, count; - - padding = count = blob->ptr[blob->len - 1]; - if (padding > 8) - { - return FALSE; - } - for (; blob->len && count; --blob->len, --count) - { - if (blob->ptr[blob->len - 1] != padding) - { - return FALSE; - } - } - return TRUE; -} - -/** - * Prototype for key derivation functions. - */ -typedef bool (*kdf_t)(void *generator, chunk_t password, chunk_t salt, - u_int64_t iterations, chunk_t key); - /** * Try to decrypt the given blob with multiple passwords using the given - * key derivation function. keymat is where the kdf function writes the key - * to, key and iv point to the actual keys and initialization vectors resp. + * pkcs5 object. */ -static private_key_t *decrypt_private_key(chunk_t blob, - encryption_algorithm_t encr, size_t key_len, kdf_t kdf, - void *generator, chunk_t salt, u_int64_t iterations, - chunk_t keymat, chunk_t key, chunk_t iv) +static private_key_t *decrypt_private_key(pkcs5_t *pkcs5, chunk_t blob) { enumerator_t *enumerator; shared_key_t *shared; - crypter_t *crypter; private_key_t *private_key = NULL; - crypter = lib->crypto->create_crypter(lib->crypto, encr, key_len); - if (!crypter) - { - DBG1(DBG_ASN, " %N encryption algorithm not available", - encryption_algorithm_names, encr); - return NULL; - } - if (blob.len % crypter->get_block_size(crypter)) - { - DBG1(DBG_ASN, " data size is not a multiple of block size"); - crypter->destroy(crypter); - return NULL; - } - enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr, SHARED_PRIVATE_KEY_PASS, NULL, NULL); while (enumerator->enumerate(enumerator, &shared, NULL, NULL)) { chunk_t decrypted; - if (!kdf(generator, shared->get_key(shared), salt, iterations, keymat)) + if (!pkcs5->decrypt(pkcs5, shared->get_key(shared), blob, &decrypted)) { continue; } - if (!crypter->set_key(crypter, key) || - !crypter->decrypt(crypter, blob, iv, &decrypted)) + private_key = parse_private_key(decrypted); + if (private_key) { - continue; - } - if (verify_padding(&decrypted)) - { - private_key = parse_private_key(decrypted); - if (private_key) - { - chunk_clear(&decrypted); - break; - } + chunk_clear(&decrypted); + break; } chunk_free(&decrypted); } enumerator->destroy(enumerator); - crypter->destroy(crypter); return private_key; } -/** - * Function F of PBKDF2 - */ -static bool pbkdf2_f(chunk_t block, prf_t *prf, chunk_t seed, - u_int64_t iterations) -{ - chunk_t u; - u_int64_t i; - - u = chunk_alloca(prf->get_block_size(prf)); - if (!prf->get_bytes(prf, seed, u.ptr)) - { - return FALSE; - } - memcpy(block.ptr, u.ptr, block.len); - - for (i = 1; i < iterations; i++) - { - if (!prf->get_bytes(prf, u, u.ptr)) - { - return FALSE; - } - memxor(block.ptr, u.ptr, block.len); - } - return TRUE; -} - -/** - * PBKDF2 key derivation function - */ -static bool pbkdf2(prf_t *prf, chunk_t password, chunk_t salt, - u_int64_t iterations, chunk_t key) -{ - chunk_t keymat, block, seed; - size_t blocks; - u_int32_t i = 0, *ni; - - if (!prf->set_key(prf, password)) - { - return FALSE; - } - - block.len = prf->get_block_size(prf); - blocks = (key.len - 1) / block.len + 1; - keymat = chunk_alloca(blocks * block.len); - - seed = chunk_cata("cc", salt, chunk_from_thing(i)); - ni = (u_int32_t*)(seed.ptr + salt.len); - - for (; i < blocks; i++) - { - *ni = htonl(i + 1); - block.ptr = keymat.ptr + (i * block.len); - if (!pbkdf2_f(block, prf, seed, iterations)) - { - return FALSE; - } - } - - memcpy(key.ptr, keymat.ptr, key.len); - - return TRUE; -} - -/** - * Decrypt an encrypted PKCS#8 encoded private key according to PBES2 - */ -static private_key_t *decrypt_private_key_pbes2(chunk_t blob, - encryption_algorithm_t encr, size_t key_len, - chunk_t iv, pseudo_random_function_t prf_func, - chunk_t salt, u_int64_t iterations) -{ - private_key_t *private_key; - prf_t *prf; - chunk_t key; - - prf = lib->crypto->create_prf(lib->crypto, prf_func); - if (!prf) - { - DBG1(DBG_ASN, " %N prf algorithm not available", - pseudo_random_function_names, prf_func); - return NULL; - } - - key = chunk_alloca(key_len); - - private_key = decrypt_private_key(blob, encr, key_len, (kdf_t)pbkdf2, prf, - salt, iterations, key, key, iv); - - prf->destroy(prf); - return private_key; -} - -/** - * PBKDF1 key derivation function - */ -static bool pbkdf1(hasher_t *hasher, chunk_t password, chunk_t salt, - u_int64_t iterations, chunk_t key) -{ - chunk_t hash; - u_int64_t i; - - hash = chunk_alloca(hasher->get_hash_size(hasher)); - if (!hasher->get_hash(hasher, password, NULL) || - !hasher->get_hash(hasher, salt, hash.ptr)) - { - return FALSE; - } - - for (i = 1; i < iterations; i++) - { - if (!hasher->get_hash(hasher, hash, hash.ptr)) - { - return FALSE; - } - } - - memcpy(key.ptr, hash.ptr, key.len); - - return TRUE; -} - -/** - * Decrypt an encrypted PKCS#8 encoded private key according to PBES1 - */ -static private_key_t *decrypt_private_key_pbes1(chunk_t blob, - encryption_algorithm_t encr, size_t key_len, - hash_algorithm_t hash, chunk_t salt, - u_int64_t iterations) -{ - private_key_t *private_key = NULL; - hasher_t *hasher = NULL; - chunk_t keymat, key, iv; - - hasher = lib->crypto->create_hasher(lib->crypto, hash); - if (!hasher) - { - DBG1(DBG_ASN, " %N hash algorithm not available", - hash_algorithm_names, hash); - goto end; - } - if (hasher->get_hash_size(hasher) < key_len) - { - goto end; - } - - keymat = chunk_alloca(key_len * 2); - key.len = key_len; - key.ptr = keymat.ptr; - iv.len = key_len; - iv.ptr = keymat.ptr + key_len; - - private_key = decrypt_private_key(blob, encr, key_len, (kdf_t)pbkdf1, - hasher, salt, iterations, keymat, - key, iv); - -end: - DESTROY_IF(hasher); - return private_key; -} - -/** - * Parse an ASN1_INTEGER to a u_int64_t. - */ -static u_int64_t parse_asn1_integer_uint64(chunk_t blob) -{ - u_int64_t val = 0; - int i; - - for (i = 0; i < blob.len; i++) - { /* if it is longer than 8 bytes, we just use the 8 LSBs */ - val <<= 8; - val |= (u_int64_t)blob.ptr[i]; - } - return val; -} - -/** - * ASN.1 definition of a PBKDF2-params structure - * The salt is actually a CHOICE and could be an AlgorithmIdentifier from - * PBKDF2-SaltSources (but as per RFC 2898 that's for future versions). - */ -static const asn1Object_t pbkdf2ParamsObjects[] = { - { 0, "PBKDF2-params", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ - { 1, "salt", ASN1_OCTET_STRING, ASN1_BODY }, /* 1 */ - { 1, "iterationCount",ASN1_INTEGER, ASN1_BODY }, /* 2 */ - { 1, "keyLength", ASN1_INTEGER, ASN1_OPT|ASN1_BODY }, /* 3 */ - { 1, "end opt", ASN1_EOC, ASN1_END }, /* 4 */ - { 1, "prf", ASN1_EOC, ASN1_DEF|ASN1_RAW }, /* 5 */ - { 0, "exit", ASN1_EOC, ASN1_EXIT } -}; -#define PBKDF2_SALT 1 -#define PBKDF2_ITERATION_COUNT 2 -#define PBKDF2_KEY_LENGTH 3 -#define PBKDF2_PRF 5 - -/** - * Parse a PBKDF2-params structure - */ -static void parse_pbkdf2_params(chunk_t blob, chunk_t *salt, - u_int64_t *iterations, size_t *key_len, - pseudo_random_function_t *prf) -{ - asn1_parser_t *parser; - chunk_t object; - int objectID; - - parser = asn1_parser_create(pbkdf2ParamsObjects, blob); - - *key_len = 0; /* key_len is optional */ - - while (parser->iterate(parser, &objectID, &object)) - { - switch (objectID) - { - case PBKDF2_SALT: - { - *salt = object; - break; - } - case PBKDF2_ITERATION_COUNT: - { - *iterations = parse_asn1_integer_uint64(object); - break; - } - case PBKDF2_KEY_LENGTH: - { - *key_len = (size_t)parse_asn1_integer_uint64(object); - break; - } - case PBKDF2_PRF: - { /* defaults to id-hmacWithSHA1 */ - *prf = PRF_HMAC_SHA1; - break; - } - } - } - - parser->destroy(parser); -} - -/** - * ASN.1 definition of a PBES2-params structure - */ -static const asn1Object_t pbes2ParamsObjects[] = { - { 0, "PBES2-params", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ - { 1, "keyDerivationFunc", ASN1_EOC, ASN1_RAW }, /* 1 */ - { 1, "encryptionScheme", ASN1_EOC, ASN1_RAW }, /* 2 */ - { 0, "exit", ASN1_EOC, ASN1_EXIT } -}; -#define PBES2PARAMS_KEY_DERIVATION_FUNC 1 -#define PBES2PARAMS_ENCRYPTION_SCHEME 2 - -/** - * Parse a PBES2-params structure - */ -static void parse_pbes2_params(chunk_t blob, chunk_t *salt, - u_int64_t *iterations, size_t *key_len, - pseudo_random_function_t *prf, - encryption_algorithm_t *encr, chunk_t *iv) -{ - asn1_parser_t *parser; - chunk_t object, params; - int objectID; - - parser = asn1_parser_create(pbes2ParamsObjects, blob); - - while (parser->iterate(parser, &objectID, &object)) - { - switch (objectID) - { - case PBES2PARAMS_KEY_DERIVATION_FUNC: - { - int oid = asn1_parse_algorithmIdentifier(object, - parser->get_level(parser) + 1, ¶ms); - if (oid != OID_PBKDF2) - { /* unsupported key derivation function */ - goto end; - } - parse_pbkdf2_params(params, salt, iterations, key_len, prf); - break; - } - case PBES2PARAMS_ENCRYPTION_SCHEME: - { - int oid = asn1_parse_algorithmIdentifier(object, - parser->get_level(parser) + 1, ¶ms); - if (oid != OID_3DES_EDE_CBC) - { /* unsupported encryption scheme */ - goto end; - } - if (*key_len <= 0) - { /* default key len for DES-EDE3-CBC-Pad */ - *key_len = 24; - } - if (!asn1_parse_simple_object(¶ms, ASN1_OCTET_STRING, - parser->get_level(parser) + 1, "IV")) - { - goto end; - } - *encr = ENCR_3DES; - *iv = params; - break; - } - } - } - -end: - parser->destroy(parser); -} - -/** - * ASN.1 definition of a PBEParameter structure - */ -static const asn1Object_t pbeParameterObjects[] = { - { 0, "PBEParameter", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ - { 1, "salt", ASN1_OCTET_STRING, ASN1_BODY }, /* 1 */ - { 1, "iterationCount", ASN1_INTEGER, ASN1_BODY }, /* 2 */ - { 0, "exit", ASN1_EOC, ASN1_EXIT } -}; -#define PBEPARAM_SALT 1 -#define PBEPARAM_ITERATION_COUNT 2 - -/** - * Parse a PBEParameter structure - */ -static void parse_pbe_parameters(chunk_t blob, chunk_t *salt, - u_int64_t *iterations) -{ - asn1_parser_t *parser; - chunk_t object; - int objectID; - - parser = asn1_parser_create(pbeParameterObjects, blob); - - while (parser->iterate(parser, &objectID, &object)) - { - switch (objectID) - { - case PBEPARAM_SALT: - { - *salt = object; - break; - } - case PBEPARAM_ITERATION_COUNT: - { - *iterations = parse_asn1_integer_uint64(object); - break; - } - } - } - - parser->destroy(parser); -} - /** * ASN.1 definition of an encryptedPrivateKeyInfo structure */ @@ -563,14 +153,10 @@ static const asn1Object_t encryptedPKIObjects[] = { static private_key_t *parse_encrypted_private_key(chunk_t blob) { asn1_parser_t *parser; - chunk_t object, params, salt = chunk_empty, iv = chunk_empty; - u_int64_t iterations = 0; + chunk_t object; int objectID; - encryption_algorithm_t encr = ENCR_UNDEFINED; - hash_algorithm_t hash = HASH_UNKNOWN; - pseudo_random_function_t prf = PRF_UNDEFINED; private_key_t *key = NULL; - size_t key_len = 8; + pkcs5_t *pkcs5 = NULL; parser = asn1_parser_create(encryptedPKIObjects, blob); @@ -580,49 +166,24 @@ static private_key_t *parse_encrypted_private_key(chunk_t blob) { case EPKINFO_ENCRYPTION_ALGORITHM: { - int oid = asn1_parse_algorithmIdentifier(object, - parser->get_level(parser) + 1, ¶ms); - - switch (oid) + pkcs5 = pkcs5_from_algorithmIdentifier(object, + parser->get_level(parser) + 1); + if (!pkcs5) { - case OID_PBE_MD5_DES_CBC: - encr = ENCR_DES; - hash = HASH_MD5; - parse_pbe_parameters(params, &salt, &iterations); - break; - case OID_PBE_SHA1_DES_CBC: - encr = ENCR_DES; - hash = HASH_SHA1; - parse_pbe_parameters(params, &salt, &iterations); - break; - case OID_PBES2: - parse_pbes2_params(params, &salt, &iterations, - &key_len, &prf, &encr, &iv); - break; - default: - /* encryption scheme not supported */ - goto end; + goto end; } break; } case EPKINFO_ENCRYPTED_DATA: { - if (prf != PRF_UNDEFINED) - { - key = decrypt_private_key_pbes2(object, encr, key_len, iv, - prf, salt, iterations); - } - else - { - key = decrypt_private_key_pbes1(object, encr, key_len, hash, - salt, iterations); - } + key = decrypt_private_key(pkcs5, object); break; } } } end: + DESTROY_IF(pkcs5); parser->destroy(parser); return key; } From c734c2d875bbea7673dbbc796a88f2f21e5265b1 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Mon, 8 Apr 2013 18:31:34 +0200 Subject: [PATCH 02/29] Extract function to convert ASN.1 INTEGER object to u_int64_t --- src/libstrongswan/asn1/asn1.c | 16 ++++++++++++++++ src/libstrongswan/asn1/asn1.h | 9 +++++++++ src/libstrongswan/crypto/pkcs5.c | 26 +++----------------------- 3 files changed, 28 insertions(+), 23 deletions(-) diff --git a/src/libstrongswan/asn1/asn1.c b/src/libstrongswan/asn1/asn1.c index f438cb20e..68f37f471 100644 --- a/src/libstrongswan/asn1/asn1.c +++ b/src/libstrongswan/asn1/asn1.c @@ -549,6 +549,22 @@ bool asn1_parse_simple_object(chunk_t *object, asn1_t type, u_int level, const c return TRUE; } +/* + * Described in header + */ +u_int64_t asn1_parse_integer_uint64(chunk_t blob) +{ + u_int64_t val = 0; + int i; + + for (i = 0; i < blob.len; i++) + { /* if it is longer than 8 bytes, we just use the 8 LSBs */ + val <<= 8; + val |= (u_int64_t)blob.ptr[i]; + } + return val; +} + /** * ASN.1 definition of an algorithmIdentifier */ diff --git a/src/libstrongswan/asn1/asn1.h b/src/libstrongswan/asn1/asn1.h index 15ffff62e..a1d625380 100644 --- a/src/libstrongswan/asn1/asn1.h +++ b/src/libstrongswan/asn1/asn1.h @@ -170,6 +170,15 @@ int asn1_parse_algorithmIdentifier(chunk_t blob, int level0, chunk_t *params); bool asn1_parse_simple_object(chunk_t *object, asn1_t type, u_int level0, const char* name); +/** + * Converts an ASN.1 INTEGER object to an u_int64_t. If the INTEGER is longer + * than 8 bytes only the 8 LSBs are returned. + * + * @param blob body of an ASN.1 coded integer object + * @return converted integer + */ +u_int64_t asn1_parse_integer_uint64(chunk_t blob); + /** * Print the value of an ASN.1 simple object * diff --git a/src/libstrongswan/crypto/pkcs5.c b/src/libstrongswan/crypto/pkcs5.c index 76a67fc4a..5c5d78c28 100644 --- a/src/libstrongswan/crypto/pkcs5.c +++ b/src/libstrongswan/crypto/pkcs5.c @@ -346,26 +346,6 @@ METHOD(pkcs5_t, decrypt, bool, keymat, key, iv); } -/** - * Converts an ASN.1 INTEGER object to an u_int64_t. If the INTEGER is longer - * than 8 bytes only the 8 LSBs are returned. - * - * @param blob body of an ASN.1 coded integer object - * @return converted integer - */ -static u_int64_t parse_asn1_integer_uint64(chunk_t blob) -{ - u_int64_t val = 0; - int i; - - for (i = 0; i < blob.len; i++) - { /* if it is longer than 8 bytes, we just use the 8 LSBs */ - val <<= 8; - val |= (u_int64_t)blob.ptr[i]; - } - return val; -} - /** * ASN.1 definition of a PBEParameter structure */ @@ -402,7 +382,7 @@ static bool parse_pbes1_params(private_pkcs5_t *this, chunk_t blob, int level0) } case PBEPARAM_ITERATION_COUNT: { - this->iterations = parse_asn1_integer_uint64(object); + this->iterations = asn1_parse_integer_uint64(object); break; } } @@ -458,12 +438,12 @@ static bool parse_pbkdf2_params(private_pkcs5_t *this, chunk_t blob, int level0) } case PBKDF2_ITERATION_COUNT: { - this->iterations = parse_asn1_integer_uint64(object); + this->iterations = asn1_parse_integer_uint64(object); break; } case PBKDF2_KEYLENGTH: { - this->keylen = (size_t)parse_asn1_integer_uint64(object); + this->keylen = (size_t)asn1_parse_integer_uint64(object); break; } case PBKDF2_PRF: From 9d4fc8677fe8bc10e023be5c0a887062191ae602 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 10 Apr 2013 19:24:09 +0200 Subject: [PATCH 03/29] Add implementation of the RC2 block cipher (RFC 2268) --- configure.in | 4 + src/libstrongswan/Makefile.am | 7 + src/libstrongswan/crypto/crypters/crypter.c | 7 +- src/libstrongswan/crypto/crypters/crypter.h | 13 +- src/libstrongswan/plugins/rc2/Makefile.am | 15 + src/libstrongswan/plugins/rc2/rc2_crypter.c | 349 ++++++++++++++++++++ src/libstrongswan/plugins/rc2/rc2_crypter.h | 50 +++ src/libstrongswan/plugins/rc2/rc2_plugin.c | 76 +++++ src/libstrongswan/plugins/rc2/rc2_plugin.h | 42 +++ 9 files changed, 559 insertions(+), 4 deletions(-) create mode 100644 src/libstrongswan/plugins/rc2/Makefile.am create mode 100644 src/libstrongswan/plugins/rc2/rc2_crypter.c create mode 100644 src/libstrongswan/plugins/rc2/rc2_crypter.h create mode 100644 src/libstrongswan/plugins/rc2/rc2_plugin.c create mode 100644 src/libstrongswan/plugins/rc2/rc2_plugin.h diff --git a/configure.in b/configure.in index dd16633a7..c51146a12 100644 --- a/configure.in +++ b/configure.in @@ -112,6 +112,7 @@ ARG_ENABL_SET([ldap], [enable LDAP fetching plugin to fetch files via ARG_DISBL_SET([aes], [disable AES software implementation plugin.]) ARG_DISBL_SET([des], [disable DES/3DES software implementation plugin.]) ARG_ENABL_SET([blowfish], [enable Blowfish software implementation plugin.]) +ARG_DISBL_SET([rc2], [disable RC2 software implementation plugin.]) ARG_ENABL_SET([md4], [enable MD4 software implementation plugin.]) ARG_DISBL_SET([md5], [disable MD5 software implementation plugin.]) ARG_DISBL_SET([sha1], [disable SHA1 software implementation plugin.]) @@ -952,6 +953,7 @@ ADD_PLUGIN([pkcs11], [s charon pki nm cmd]) ADD_PLUGIN([aes], [s charon openac scepclient pki scripts nm cmd]) ADD_PLUGIN([des], [s charon openac scepclient pki scripts nm cmd]) ADD_PLUGIN([blowfish], [s charon openac scepclient pki scripts nm cmd]) +ADD_PLUGIN([rc2], [s charon openac scepclient pki scripts nm cmd]) ADD_PLUGIN([sha1], [s charon openac scepclient pki scripts medsrv attest nm cmd]) ADD_PLUGIN([sha2], [s charon openac scepclient pki scripts medsrv attest nm cmd]) ADD_PLUGIN([md4], [s charon openac manager scepclient pki nm cmd]) @@ -1081,6 +1083,7 @@ AM_CONDITIONAL(USE_LDAP, test x$ldap = xtrue) AM_CONDITIONAL(USE_AES, test x$aes = xtrue) AM_CONDITIONAL(USE_DES, test x$des = xtrue) AM_CONDITIONAL(USE_BLOWFISH, test x$blowfish = xtrue) +AM_CONDITIONAL(USE_RC2, test x$rc2 = xtrue) AM_CONDITIONAL(USE_MD4, test x$md4 = xtrue) AM_CONDITIONAL(USE_MD5, test x$md5 = xtrue) AM_CONDITIONAL(USE_SHA1, test x$sha1 = xtrue) @@ -1274,6 +1277,7 @@ AC_CONFIG_FILES([ src/libstrongswan/plugins/cmac/Makefile src/libstrongswan/plugins/des/Makefile src/libstrongswan/plugins/blowfish/Makefile + src/libstrongswan/plugins/rc2/Makefile src/libstrongswan/plugins/md4/Makefile src/libstrongswan/plugins/md5/Makefile src/libstrongswan/plugins/sha1/Makefile diff --git a/src/libstrongswan/Makefile.am b/src/libstrongswan/Makefile.am index 5968a3b3f..ae4fc75b6 100644 --- a/src/libstrongswan/Makefile.am +++ b/src/libstrongswan/Makefile.am @@ -175,6 +175,13 @@ if MONOLITHIC endif endif +if USE_RC2 + SUBDIRS += plugins/rc2 +if MONOLITHIC + libstrongswan_la_LIBADD += plugins/rc2/libstrongswan-rc2.la +endif +endif + if USE_MD4 SUBDIRS += plugins/md4 if MONOLITHIC diff --git a/src/libstrongswan/crypto/crypters/crypter.c b/src/libstrongswan/crypto/crypters/crypter.c index 0730c707c..8123adde5 100644 --- a/src/libstrongswan/crypto/crypters/crypter.c +++ b/src/libstrongswan/crypto/crypters/crypter.c @@ -46,12 +46,13 @@ ENUM_NEXT(encryption_algorithm_names, ENCR_CAMELLIA_CBC, ENCR_CAMELLIA_CCM_ICV16 "CAMELLIA_CCM_8", "CAMELLIA_CCM_12", "CAMELLIA_CCM_16"); -ENUM_NEXT(encryption_algorithm_names, ENCR_UNDEFINED, ENCR_TWOFISH_CBC, ENCR_CAMELLIA_CCM_ICV16, +ENUM_NEXT(encryption_algorithm_names, ENCR_UNDEFINED, ENCR_RC2_CBC, ENCR_CAMELLIA_CCM_ICV16, "UNDEFINED", "DES_ECB", "SERPENT_CBC", - "TWOFISH_CBC"); -ENUM_END(encryption_algorithm_names, ENCR_TWOFISH_CBC); + "TWOFISH_CBC", + "RC2_CBC"); +ENUM_END(encryption_algorithm_names, ENCR_RC2_CBC); /* * Described in header. diff --git a/src/libstrongswan/crypto/crypters/crypter.h b/src/libstrongswan/crypto/crypters/crypter.h index fe854f53d..849aea500 100644 --- a/src/libstrongswan/crypto/crypters/crypter.h +++ b/src/libstrongswan/crypto/crypters/crypter.h @@ -60,7 +60,9 @@ enum encryption_algorithm_t { ENCR_UNDEFINED = 1024, ENCR_DES_ECB = 1025, ENCR_SERPENT_CBC = 1026, - ENCR_TWOFISH_CBC = 1027 + ENCR_TWOFISH_CBC = 1027, + /* see macros below to handle RC2 (effective) key length */ + ENCR_RC2_CBC = 1028, }; #define DES_BLOCK_SIZE 8 @@ -70,6 +72,15 @@ enum encryption_algorithm_t { #define SERPENT_BLOCK_SIZE 16 #define TWOFISH_BLOCK_SIZE 16 +/** + * For RC2, if the effective key size in bits is not key_size * 8, it should + * be encoded with the macro below. It can be decoded with the other two macros. + * After decoding the value should be validated. + */ +#define RC2_KEY_SIZE(kl, eff) ((kl) | ((eff) << 8)) +#define RC2_EFFECTIVE_KEY_LEN(ks) ((ks) >> 8) +#define RC2_KEY_LEN(ks) ((ks) & 0xff) + /** * enum name for encryption_algorithm_t. */ diff --git a/src/libstrongswan/plugins/rc2/Makefile.am b/src/libstrongswan/plugins/rc2/Makefile.am new file mode 100644 index 000000000..156574de3 --- /dev/null +++ b/src/libstrongswan/plugins/rc2/Makefile.am @@ -0,0 +1,15 @@ + +INCLUDES = -I$(top_srcdir)/src/libstrongswan + +AM_CFLAGS = -rdynamic + +if MONOLITHIC +noinst_LTLIBRARIES = libstrongswan-rc2.la +else +plugin_LTLIBRARIES = libstrongswan-rc2.la +endif + +libstrongswan_rc2_la_SOURCES = \ + rc2_plugin.h rc2_plugin.c rc2_crypter.c rc2_crypter.h + +libstrongswan_rc2_la_LDFLAGS = -module -avoid-version diff --git a/src/libstrongswan/plugins/rc2/rc2_crypter.c b/src/libstrongswan/plugins/rc2/rc2_crypter.c new file mode 100644 index 000000000..256acf817 --- /dev/null +++ b/src/libstrongswan/plugins/rc2/rc2_crypter.c @@ -0,0 +1,349 @@ +/* + * Copyright (C) 2013 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "rc2_crypter.h" + +typedef struct private_rc2_crypter_t private_rc2_crypter_t; + +#define RC2_BLOCK_SIZE 8 + +#define ROL16(x, k) ({ u_int16_t _x = (x); (_x << (k)) | (_x >> (16 - (k))); }) +#define ROR16(x, k) ({ u_int16_t _x = (x); (_x >> (k)) | (_x << (16 - (k))); }) + +#define GET16(x) ({ u_char *_x = (x); (u_int16_t)_x[0] | ((u_int16_t)_x[1] << 8); }) +#define PUT16(x, v) ({ u_char *_x = (x); u_int16_t _v = (v); _x[0] = _v, _x[1] = _v >> 8; }) + +/** + * Private data of rc2_crypter_t + */ +struct private_rc2_crypter_t { + + /** + * Public interface + */ + rc2_crypter_t public; + + /** + * The expanded key in 16-bit words + */ + u_int16_t K[64]; + + /** + * Key size in bytes + */ + size_t T; + + /** + * Effective key size in bits + */ + size_t T1; +}; + +/** + * PITABLE + */ +static const u_char PITABLE[256] = +{ + 0xd9, 0x78, 0xf9, 0xc4, 0x19, 0xdd, 0xb5, 0xed, + 0x28, 0xe9, 0xfd, 0x79, 0x4a, 0xa0, 0xd8, 0x9d, + 0xc6, 0x7e, 0x37, 0x83, 0x2b, 0x76, 0x53, 0x8e, + 0x62, 0x4c, 0x64, 0x88, 0x44, 0x8b, 0xfb, 0xa2, + 0x17, 0x9a, 0x59, 0xf5, 0x87, 0xb3, 0x4f, 0x13, + 0x61, 0x45, 0x6d, 0x8d, 0x09, 0x81, 0x7d, 0x32, + 0xbd, 0x8f, 0x40, 0xeb, 0x86, 0xb7, 0x7b, 0x0b, + 0xf0, 0x95, 0x21, 0x22, 0x5c, 0x6b, 0x4e, 0x82, + 0x54, 0xd6, 0x65, 0x93, 0xce, 0x60, 0xb2, 0x1c, + 0x73, 0x56, 0xc0, 0x14, 0xa7, 0x8c, 0xf1, 0xdc, + 0x12, 0x75, 0xca, 0x1f, 0x3b, 0xbe, 0xe4, 0xd1, + 0x42, 0x3d, 0xd4, 0x30, 0xa3, 0x3c, 0xb6, 0x26, + 0x6f, 0xbf, 0x0e, 0xda, 0x46, 0x69, 0x07, 0x57, + 0x27, 0xf2, 0x1d, 0x9b, 0xbc, 0x94, 0x43, 0x03, + 0xf8, 0x11, 0xc7, 0xf6, 0x90, 0xef, 0x3e, 0xe7, + 0x06, 0xc3, 0xd5, 0x2f, 0xc8, 0x66, 0x1e, 0xd7, + 0x08, 0xe8, 0xea, 0xde, 0x80, 0x52, 0xee, 0xf7, + 0x84, 0xaa, 0x72, 0xac, 0x35, 0x4d, 0x6a, 0x2a, + 0x96, 0x1a, 0xd2, 0x71, 0x5a, 0x15, 0x49, 0x74, + 0x4b, 0x9f, 0xd0, 0x5e, 0x04, 0x18, 0xa4, 0xec, + 0xc2, 0xe0, 0x41, 0x6e, 0x0f, 0x51, 0xcb, 0xcc, + 0x24, 0x91, 0xaf, 0x50, 0xa1, 0xf4, 0x70, 0x39, + 0x99, 0x7c, 0x3a, 0x85, 0x23, 0xb8, 0xb4, 0x7a, + 0xfc, 0x02, 0x36, 0x5b, 0x25, 0x55, 0x97, 0x31, + 0x2d, 0x5d, 0xfa, 0x98, 0xe3, 0x8a, 0x92, 0xae, + 0x05, 0xdf, 0x29, 0x10, 0x67, 0x6c, 0xba, 0xc9, + 0xd3, 0x00, 0xe6, 0xcf, 0xe1, 0x9e, 0xa8, 0x2c, + 0x63, 0x16, 0x01, 0x3f, 0x58, 0xe2, 0x89, 0xa9, + 0x0d, 0x38, 0x34, 0x1b, 0xab, 0x33, 0xff, 0xb0, + 0xbb, 0x48, 0x0c, 0x5f, 0xb9, 0xb1, 0xcd, 0x2e, + 0xc5, 0xf3, 0xdb, 0x47, 0xe5, 0xa5, 0x9c, 0x77, + 0x0a, 0xa6, 0x20, 0x68, 0xfe, 0x7f, 0xc1, 0xad, +}; + +/** + * Encrypt a single block of data + */ +static void encrypt_block(private_rc2_crypter_t *this, u_char R[]) +{ + register u_int16_t R0, R1, R2, R3, *Kj; + int rounds = 3, mix = 5; + + R0 = GET16(R); + R1 = GET16(R + 2); + R2 = GET16(R + 4); + R3 = GET16(R + 6); + Kj = &this->K[0]; + + /* 5 mix, mash, 6 mix, mash, 5 mix */ + while (TRUE) + { + /* mix */ + R0 = ROL16(R0 + *(Kj++) + (R3 & R2) + (~R3 & R1), 1); + R1 = ROL16(R1 + *(Kj++) + (R0 & R3) + (~R0 & R2), 2); + R2 = ROL16(R2 + *(Kj++) + (R1 & R0) + (~R1 & R3), 3); + R3 = ROL16(R3 + *(Kj++) + (R2 & R1) + (~R2 & R0), 5); + + if (--mix == 0) + { + if (--rounds == 0) + { + break; + } + mix = (rounds == 2) ? 6 : 5; + /* mash */ + R0 += this->K[R3 & 63]; + R1 += this->K[R0 & 63]; + R2 += this->K[R1 & 63]; + R3 += this->K[R2 & 63]; + } + } + + PUT16(R, R0); + PUT16(R + 2, R1); + PUT16(R + 4, R2); + PUT16(R + 6, R3); +} + +/** + * Decrypt a single block of data. + */ +static void decrypt_block(private_rc2_crypter_t *this, u_char R[]) +{ + register u_int16_t R0, R1, R2, R3, *Kj; + int rounds = 3, mix = 5; + + R0 = GET16(R); + R1 = GET16(R + 2); + R2 = GET16(R + 4); + R3 = GET16(R + 6); + Kj = &this->K[63]; + + /* 5 r-mix, r-mash, 6 r-mix, r-mash, 5 r-mix */ + while (TRUE) + { + /* r-mix */ + R3 = ROR16(R3, 5); + R3 = R3 - *(Kj--) - (R2 & R1) - (~R2 & R0); + R2 = ROR16(R2, 3); + R2 = R2 - *(Kj--) - (R1 & R0) - (~R1 & R3); + R1 = ROR16(R1, 2); + R1 = R1 - *(Kj--) - (R0 & R3) - (~R0 & R2); + R0 = ROR16(R0, 1); + R0 = R0 - *(Kj--) - (R3 & R2) - (~R3 & R1); + + if (--mix == 0) + { + if (--rounds == 0) + { + break; + } + mix = (rounds == 2) ? 6 : 5; + /* r-mash */ + R3 -= this->K[R2 & 63]; + R2 -= this->K[R1 & 63]; + R1 -= this->K[R0 & 63]; + R0 -= this->K[R3 & 63]; + } + } + + PUT16(R, R0); + PUT16(R + 2, R1); + PUT16(R + 4, R2); + PUT16(R + 6, R3); +} + +METHOD(crypter_t, decrypt, bool, + private_rc2_crypter_t *this, chunk_t data, chunk_t iv, chunk_t *decrypted) +{ + u_int8_t *in, *out, *prev; + + if (data.len % RC2_BLOCK_SIZE || iv.len != RC2_BLOCK_SIZE) + { + return FALSE; + } + + in = data.ptr + data.len - RC2_BLOCK_SIZE; + out = data.ptr; + if (decrypted) + { + *decrypted = chunk_alloc(data.len); + out = decrypted->ptr; + } + out += data.len - RC2_BLOCK_SIZE; + + prev = in; + for (; in >= data.ptr; in -= RC2_BLOCK_SIZE, out -= RC2_BLOCK_SIZE) + { + if (decrypted) + { + memcpy(out, in, RC2_BLOCK_SIZE); + } + decrypt_block(this, out); + prev -= RC2_BLOCK_SIZE; + if (prev < data.ptr) + { + prev = iv.ptr; + } + memxor(out, prev, RC2_BLOCK_SIZE); + } + return TRUE; +} + +METHOD(crypter_t, encrypt, bool, + private_rc2_crypter_t *this, chunk_t data, chunk_t iv, chunk_t *encrypted) +{ + u_int8_t *in, *out, *end, *prev; + + if (data.len % RC2_BLOCK_SIZE || iv.len != RC2_BLOCK_SIZE) + { + return FALSE; + } + + in = data.ptr; + end = data.ptr + data.len; + out = data.ptr; + if (encrypted) + { + *encrypted = chunk_alloc(data.len); + out = encrypted->ptr; + } + + prev = iv.ptr; + for (; in < end; in += RC2_BLOCK_SIZE, out += RC2_BLOCK_SIZE) + { + if (encrypted) + { + memcpy(out, in, RC2_BLOCK_SIZE); + } + memxor(out, prev, RC2_BLOCK_SIZE); + encrypt_block(this, out); + prev = out; + } + return TRUE; +} + +METHOD(crypter_t, get_block_size, size_t, + private_rc2_crypter_t *this) +{ + return RC2_BLOCK_SIZE; +} + +METHOD(crypter_t, get_iv_size, size_t, + private_rc2_crypter_t *this) +{ + return RC2_BLOCK_SIZE; +} + +METHOD(crypter_t, get_key_size, size_t, + private_rc2_crypter_t *this) +{ + return this->T; +} + +METHOD(crypter_t, set_key, bool, + private_rc2_crypter_t *this, chunk_t key) +{ + u_int8_t L[128], T8, TM, idx; + int i; + + if (key.len != this->T) + { + return FALSE; + } + for (i = 0; i < key.len; i++) + { + L[i] = key.ptr[i]; + } + for (; i < 128; i++) + { + idx = L[i-1] + L[i-key.len]; + L[i] = PITABLE[idx]; + } + T8 = (this->T1 + 7) / 8; + TM = ~(0xff << (8 - (8*T8 - this->T1))); + L[128-T8] = PITABLE[L[128-T8] & TM]; + for (i = 127-T8; i >= 0; i--) + { + idx = L[i+1] ^ L[i+T8]; + L[i] = PITABLE[idx]; + } + for (i = 0; i < 64; i++) + { + this->K[i] = GET16(&L[i << 1]); + } + memwipe(L, sizeof(L)); + return TRUE; +} + +METHOD(crypter_t, destroy, void, + private_rc2_crypter_t *this) +{ + memwipe(this->K, sizeof(this->K)); + free(this); +} + +/* + * Described in header + */ +rc2_crypter_t *rc2_crypter_create(encryption_algorithm_t algo, size_t key_size) +{ + private_rc2_crypter_t *this; + size_t effective; + + if (algo != ENCR_RC2_CBC) + { + return NULL; + } + key_size = max(1, key_size); + effective = RC2_EFFECTIVE_KEY_LEN(key_size); + key_size = min(128, RC2_KEY_LEN(key_size)); + effective = max(1, min(1024, effective ?: key_size * 8)); + + INIT(this, + .public = { + .crypter = { + .encrypt = _encrypt, + .decrypt = _decrypt, + .get_block_size = _get_block_size, + .get_iv_size = _get_iv_size, + .get_key_size = _get_key_size, + .set_key = _set_key, + .destroy = _destroy, + }, + }, + .T = key_size, + .T1 = effective, + ); + + return &this->public; +} diff --git a/src/libstrongswan/plugins/rc2/rc2_crypter.h b/src/libstrongswan/plugins/rc2/rc2_crypter.h new file mode 100644 index 000000000..d478762a6 --- /dev/null +++ b/src/libstrongswan/plugins/rc2/rc2_crypter.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2013 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup rc2_crypter rc2_crypter + * @{ @ingroup rc2_p + */ + +#ifndef RC2_CRYPTER_H_ +#define RC2_CRYPTER_H_ + +typedef struct rc2_crypter_t rc2_crypter_t; + +#include + +/** + * Class implementing the RC2 block cipher as defined in RFC 2268. + */ +struct rc2_crypter_t { + + /** + * Implements crypter_t interface. + */ + crypter_t crypter; +}; + +/** + * Constructor to create rc2_crypter_t objects. + * + * @param algo algorithm to implement (ENCR_RC2_CBC) + * @param key_size use the RC2_KEY_SIZE macro if the effective key size + * in bits is different than the actual length of the key + * @return rc2_crypter_t object, NULL if not supported + */ +rc2_crypter_t *rc2_crypter_create(encryption_algorithm_t algo, + size_t key_size); + +#endif /** RC2_CRYPTER_H_ @}*/ diff --git a/src/libstrongswan/plugins/rc2/rc2_plugin.c b/src/libstrongswan/plugins/rc2/rc2_plugin.c new file mode 100644 index 000000000..6c6fa76d6 --- /dev/null +++ b/src/libstrongswan/plugins/rc2/rc2_plugin.c @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2013 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "rc2_plugin.h" + +#include +#include "rc2_crypter.h" + +typedef struct private_rc2_plugin_t private_rc2_plugin_t; + +/** + * Private data of rc2_plugin + */ +struct private_rc2_plugin_t { + + /** + * Public interface + */ + rc2_plugin_t public; +}; + +METHOD(plugin_t, get_name, char*, + private_rc2_plugin_t *this) +{ + return "rc2"; +} + +METHOD(plugin_t, get_features, int, + private_rc2_plugin_t *this, plugin_feature_t *features[]) +{ + static plugin_feature_t f[] = { + PLUGIN_REGISTER(CRYPTER, rc2_crypter_create), + PLUGIN_PROVIDE(CRYPTER, ENCR_RC2_CBC, 0), + }; + *features = f; + return countof(f); +} + +METHOD(plugin_t, destroy, void, + private_rc2_plugin_t *this) +{ + free(this); +} + +/* + * Described in header + */ +plugin_t *rc2_plugin_create() +{ + private_rc2_plugin_t *this; + + INIT(this, + .public = { + .plugin = { + .get_name = _get_name, + .get_features = _get_features, + .destroy = _destroy, + }, + }, + ); + + return &this->public.plugin; +} + diff --git a/src/libstrongswan/plugins/rc2/rc2_plugin.h b/src/libstrongswan/plugins/rc2/rc2_plugin.h new file mode 100644 index 000000000..cbbac51af --- /dev/null +++ b/src/libstrongswan/plugins/rc2/rc2_plugin.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2013 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup rc2_p rc2 + * @ingroup plugins + * + * @defgroup rc2_plugin rc2_plugin + * @{ @ingroup rc2_p + */ + +#ifndef RC2_PLUGIN_H_ +#define RC2_PLUGIN_H_ + +#include + +typedef struct rc2_plugin_t rc2_plugin_t; + +/** + * Plugin implementing RC2 (RFC 2268). + */ +struct rc2_plugin_t { + + /** + * Implements plugin interface + */ + plugin_t plugin; +}; + +#endif /** RC2_PLUGIN_H_ @}*/ From 162c06f2f55a0e88623589969af28b1539c3a012 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 10 Apr 2013 19:25:26 +0200 Subject: [PATCH 04/29] Fix cleanup in crypto_tester if a crypter fails --- src/libstrongswan/crypto/crypto_tester.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libstrongswan/crypto/crypto_tester.c b/src/libstrongswan/crypto/crypto_tester.c index 12db0961b..5a0dccced 100644 --- a/src/libstrongswan/crypto/crypto_tester.c +++ b/src/libstrongswan/crypto/crypto_tester.c @@ -265,7 +265,10 @@ METHOD(crypto_tester_t, test_crypter, bool, failure: crypter->destroy(crypter); chunk_free(&cipher); - chunk_free(&plain); + if (plain.ptr != vector->plain) + { + chunk_free(&plain); + } if (failed) { DBG1(DBG_LIB, "disabled %N[%s]: %s test vector failed", From cb38e2f30ad771e8985b6c45203a56fea00608ce Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 10 Apr 2013 19:26:05 +0200 Subject: [PATCH 05/29] Add test vectors for RC2 --- .../plugins/test_vectors/Makefile.am | 1 + .../plugins/test_vectors/test_vectors.h | 8 ++ .../plugins/test_vectors/test_vectors/rc2.c | 109 ++++++++++++++++++ 3 files changed, 118 insertions(+) create mode 100644 src/libstrongswan/plugins/test_vectors/test_vectors/rc2.c diff --git a/src/libstrongswan/plugins/test_vectors/Makefile.am b/src/libstrongswan/plugins/test_vectors/Makefile.am index 5280300a8..20716d3a0 100644 --- a/src/libstrongswan/plugins/test_vectors/Makefile.am +++ b/src/libstrongswan/plugins/test_vectors/Makefile.am @@ -26,6 +26,7 @@ libstrongswan_test_vectors_la_SOURCES = \ test_vectors/des.c \ test_vectors/idea.c \ test_vectors/null.c \ + test_vectors/rc2.c \ test_vectors/rc5.c \ test_vectors/serpent_cbc.c \ test_vectors/twofish_cbc.c \ diff --git a/src/libstrongswan/plugins/test_vectors/test_vectors.h b/src/libstrongswan/plugins/test_vectors/test_vectors.h index a00469779..788baae57 100644 --- a/src/libstrongswan/plugins/test_vectors/test_vectors.h +++ b/src/libstrongswan/plugins/test_vectors/test_vectors.h @@ -55,6 +55,14 @@ TEST_VECTOR_CRYPTER(des3_cbc2) TEST_VECTOR_CRYPTER(idea1) TEST_VECTOR_CRYPTER(idea2) TEST_VECTOR_CRYPTER(null1) +TEST_VECTOR_CRYPTER(rc2_1) +TEST_VECTOR_CRYPTER(rc2_2) +TEST_VECTOR_CRYPTER(rc2_3) +TEST_VECTOR_CRYPTER(rc2_4) +TEST_VECTOR_CRYPTER(rc2_5) +TEST_VECTOR_CRYPTER(rc2_6) +TEST_VECTOR_CRYPTER(rc2_7) +TEST_VECTOR_CRYPTER(rc2_8) TEST_VECTOR_CRYPTER(rc5_1) TEST_VECTOR_CRYPTER(rc5_2) TEST_VECTOR_CRYPTER(serpent_cbc1) diff --git a/src/libstrongswan/plugins/test_vectors/test_vectors/rc2.c b/src/libstrongswan/plugins/test_vectors/test_vectors/rc2.c new file mode 100644 index 000000000..b03d12038 --- /dev/null +++ b/src/libstrongswan/plugins/test_vectors/test_vectors/rc2.c @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2013 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the Licenseor (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be usefulbut + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include + +/** + * Test vectors from RFC 2268 + */ + +/** + * RC2 key length 8 bytes, effective key length 63 bits + */ +crypter_test_vector_t rc2_1 = { + .alg = ENCR_RC2_CBC, .key_size = RC2_KEY_SIZE(8, 63), .len = 8, + .key = "\x00\x00\x00\x00\x00\x00\x00\x00", + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00", + .plain = "\x00\x00\x00\x00\x00\x00\x00\x00", + .cipher = "\xeb\xb7\x73\xf9\x93\x27\x8e\xff", +}; + +/** + * RC2 key length 8 bytes, effective key length 64 bits + */ +crypter_test_vector_t rc2_2 = { + .alg = ENCR_RC2_CBC, .key_size = RC2_KEY_SIZE(8, 64), .len = 8, + .key = "\xff\xff\xff\xff\xff\xff\xff\xff", + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00", + .plain = "\xff\xff\xff\xff\xff\xff\xff\xff", + .cipher = "\x27\x8b\x27\xe4\x2e\x2f\x0d\x49", +}; + +/** + * RC2 key length 8 bytes, effective key length 64 bits + */ +crypter_test_vector_t rc2_3 = { + .alg = ENCR_RC2_CBC, .key_size = RC2_KEY_SIZE(8, 64), .len = 8, + .key = "\x30\x00\x00\x00\x00\x00\x00\x00", + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00", + .plain = "\x10\x00\x00\x00\x00\x00\x00\x01", + .cipher = "\x30\x64\x9e\xdf\x9b\xe7\xd2\xc2", +}; + +/** + * RC2 key length 1 byte, effective key length 64 bits + */ +crypter_test_vector_t rc2_4 = { + .alg = ENCR_RC2_CBC, .key_size = RC2_KEY_SIZE(1, 64), .len = 8, + .key = "\x88", + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00", + .plain = "\x00\x00\x00\x00\x00\x00\x00\x00", + .cipher = "\x61\xa8\xa2\x44\xad\xac\xcc\xf0", +}; + +/** + * RC2 key length 7 bytes, effective key length 64 bits + */ +crypter_test_vector_t rc2_5 = { + .alg = ENCR_RC2_CBC, .key_size = RC2_KEY_SIZE(7, 64), .len = 8, + .key = "\x88\xbc\xa9\x0e\x90\x87\x5a", + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00", + .plain = "\x00\x00\x00\x00\x00\x00\x00\x00", + .cipher = "\x6c\xcf\x43\x08\x97\x4c\x26\x7f", +}; + +/** + * RC2 key length 16 bytes, effective key length 64 bits + */ +crypter_test_vector_t rc2_6 = { + .alg = ENCR_RC2_CBC, .key_size = RC2_KEY_SIZE(16, 64), .len = 8, + .key = "\x88\xbc\xa9\x0e\x90\x87\x5a\x7f\x0f\x79\xc3\x84\x62\x7b\xaf\xb2", + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00", + .plain = "\x00\x00\x00\x00\x00\x00\x00\x00", + .cipher = "\x1a\x80\x7d\x27\x2b\xbe\x5d\xb1", +}; + +/** + * RC2 key length 16 bytes, effective key length 128 bits + */ +crypter_test_vector_t rc2_7 = { + .alg = ENCR_RC2_CBC, .key_size = RC2_KEY_SIZE(16, 128), .len = 8, + .key = "\x88\xbc\xa9\x0e\x90\x87\x5a\x7f\x0f\x79\xc3\x84\x62\x7b\xaf\xb2", + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00", + .plain = "\x00\x00\x00\x00\x00\x00\x00\x00", + .cipher = "\x22\x69\x55\x2a\xb0\xf8\x5c\xa6", +}; + +/** + * RC2 key length 33 bytes, effective key length 129 bits + */ +crypter_test_vector_t rc2_8 = { + .alg = ENCR_RC2_CBC, .key_size = RC2_KEY_SIZE(33, 129), .len = 8, + .key = "\x88\xbc\xa9\x0e\x90\x87\x5a\x7f\x0f\x79\xc3\x84\x62\x7b\xaf\xb2" + "\x16\xf8\x0a\x6f\x85\x92\x05\x84\xc4\x2f\xce\xb0\xbe\x25\x5d\xaf\x1e", + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00", + .plain = "\x00\x00\x00\x00\x00\x00\x00\x00", + .cipher = "\x5b\x78\xd3\xa4\x3d\xff\xf1\xf1", +}; From 594d847f791d33408ad266d8231228543447113a Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 11 Apr 2013 13:27:02 +0200 Subject: [PATCH 06/29] PKCS#5 wrapper can decrypt PKCS#12-like schemes --- src/libstrongswan/asn1/oid.txt | 10 +- src/libstrongswan/crypto/pkcs5.c | 174 ++++++++++++++++++++++++++++++- 2 files changed, 180 insertions(+), 4 deletions(-) diff --git a/src/libstrongswan/asn1/oid.txt b/src/libstrongswan/asn1/oid.txt index 49ef1cdf2..8cbd07e4b 100644 --- a/src/libstrongswan/asn1/oid.txt +++ b/src/libstrongswan/asn1/oid.txt @@ -121,6 +121,14 @@ 0x08 "unstructuredAddress" OID_UNSTRUCTURED_ADDRESS 0x0E "extensionRequest" OID_EXTENSION_REQUEST 0x0F "S/MIME Capabilities" + 0x0c "PKCS-12" + 0x01 "pbeIds" + 0x01 "pbeWithSHAAnd128BitRC4" OID_PBE_SHA1_RC4_128 + 0x02 "pbeWithSHAAnd40BitRC4" OID_PBE_SHA1_RC4_40 + 0x03 "pbeWithSHAAnd3-KeyTripleDES-CBC" OID_PBE_SHA1_3DES_CBC + 0x04 "pbeWithSHAAnd2-KeyTripleDES-CBC" OID_PBE_SHA1_3DES_2KEY_CBC + 0x05 "pbeWithSHAAnd128BitRC2-CBC" OID_PBE_SHA1_RC2_CBC_128 + 0x06 "pbeWithSHAAnd40BitRC2-CBC" OID_PBE_SHA1_RC2_CBC_40 0x02 "digestAlgorithm" 0x02 "md2" OID_MD2 0x05 "md5" OID_MD5 @@ -240,7 +248,7 @@ 0x05 "caRepository" 0x08 "ipsec" 0x02 "certificate" - 0x02 "iKEIntermediate" OID_IKE_INTERMEDIATE + 0x02 "iKEIntermediate" OID_IKE_INTERMEDIATE 0x0E "oiw" 0x03 "secsig" 0x02 "algorithms" diff --git a/src/libstrongswan/crypto/pkcs5.c b/src/libstrongswan/crypto/pkcs5.c index 5c5d78c28..b0fae5278 100644 --- a/src/libstrongswan/crypto/pkcs5.c +++ b/src/libstrongswan/crypto/pkcs5.c @@ -64,6 +64,7 @@ struct private_pkcs5_t { enum { PKCS5_SCHEME_PBES1, PKCS5_SCHEME_PBES2, + PKCS5_SCHEME_PKCS12, } scheme; /** @@ -159,6 +160,159 @@ static bool decrypt_generic(private_pkcs5_t *this, chunk_t password, return FALSE; } +/** + * v * ceiling(len/v) + */ +#define PKCS12_LEN(len, v) (((len) + v-1) & ~(v-1)) + +/** + * Copy src to dst as many times as possible + */ +static inline void pkcs12_copy_chunk(chunk_t dst, chunk_t src) +{ + size_t i; + + for (i = 0; i < dst.len; i++) + { + dst.ptr[i] = src.ptr[i % src.len]; + } +} + +/** + * Treat two chunks as integers in network order and add them together. + * The result is stored in the first chunk, if the second chunk is longer or the + * result overflows this is ignored. + */ +static void pkcs12_add_chunks(chunk_t a, chunk_t b) +{ + u_int16_t sum; + u_int8_t rem = 0; + ssize_t i, j; + + for (i = a.len - 1, j = b.len -1; i >= 0 && j >= 0; i--, j--) + { + sum = a.ptr[i] + b.ptr[j] + rem; + a.ptr[i] = (u_char)sum; + rem = sum >> 8; + } + for (; i >= 0 && rem; i--) + { + sum = a.ptr[i] + rem; + a.ptr[i] = (u_char)sum; + rem = sum >> 8; + } +} + +/** + * Do the actual key derivation with the given password and id + * id is 1 for encryption keys, 2 for IVs, 3 for MAC keys. + */ +static bool pkcs12_derive(private_pkcs5_t *this, chunk_t unicode, + char id, chunk_t result) +{ + chunk_t out = result, D, S, P = chunk_empty, I, Ai, B, Ij; + hasher_t *hasher; + size_t Slen, v, u; + u_int64_t i; + + switch (this->data.pbes1.hash) + { + case HASH_MD2: + case HASH_MD5: + case HASH_SHA1: + case HASH_SHA224: + case HASH_SHA256: + v = 64; + break; + case HASH_SHA384: + case HASH_SHA512: + v = 128; + break; + default: + return FALSE; + } + hasher = this->data.pbes1.hasher; + u = hasher->get_hash_size(hasher); + + D = chunk_alloca(v); + memset(D.ptr, id, D.len); + + Slen = PKCS12_LEN(this->salt.len, v); + I = chunk_alloca(Slen + PKCS12_LEN(unicode.len, v)); + S = chunk_create(I.ptr, Slen); + P = chunk_create(I.ptr + Slen, I.len - Slen); + pkcs12_copy_chunk(S, this->salt); + pkcs12_copy_chunk(P, unicode); + + Ai = chunk_alloca(u); + B = chunk_alloca(v); + + while (TRUE) + { + if (!hasher->get_hash(hasher, D, NULL) || + !hasher->get_hash(hasher, I, Ai.ptr)) + { + return FALSE; + } + for (i = 1; i < this->iterations; i++) + { + if (!hasher->get_hash(hasher, Ai, Ai.ptr)) + { + return FALSE; + } + } + memcpy(out.ptr, Ai.ptr, min(out.len, Ai.len)); + out = chunk_skip(out, Ai.len); + if (!out.len) + { + break; + } + pkcs12_copy_chunk(B, Ai); + /* B = B+1 */ + pkcs12_add_chunks(B, chunk_from_chars(0x01)); + Ij = chunk_create(I.ptr, v); + while (Ij.len) + { /* Ij = Ij + B + 1 */ + pkcs12_add_chunks(Ij, B); + Ij = chunk_skip(Ij, v); + } + } + return TRUE; +} + +/** + * KDF defined in PKCS#12 + */ +static bool pkcs12_kdf(private_pkcs5_t *this, chunk_t password, chunk_t keymat) +{ + chunk_t unicode = chunk_empty, key, iv; + int i; + + if (password.len) + { /* convert the password to UTF-16BE (without BOM) with 0 terminator */ + unicode = chunk_alloca(password.len * 2 + 2); + for (i = 0; i < password.len; i++) + { + unicode.ptr[i * 2] = 0; + unicode.ptr[i * 2 + 1] = password.ptr[i]; + } + unicode.ptr[i * 2] = 0; + unicode.ptr[i * 2 + 1] = 0; + } + + key = chunk_create(keymat.ptr, this->keylen); + iv = chunk_create(keymat.ptr + this->keylen, keymat.len - this->keylen); + + if (!pkcs12_derive(this, unicode, 1, key) || + !pkcs12_derive(this, unicode, 2, iv)) + { + memwipe(unicode.ptr, unicode.len); + return FALSE; + } + memwipe(unicode.ptr, unicode.len); + return TRUE; +} + /** * Function F of PBKDF2 */ @@ -272,6 +426,7 @@ static bool ensure_crypto_primitives(private_pkcs5_t *this, chunk_t data) switch (this->scheme) { case PKCS5_SCHEME_PBES1: + case PKCS5_SCHEME_PKCS12: { if (!this->data.pbes1.hasher) { @@ -325,13 +480,18 @@ METHOD(pkcs5_t, decrypt, bool, { return FALSE; } + kdf = pbkdf1; switch (this->scheme) { + case PKCS5_SCHEME_PKCS12: + kdf = pkcs12_kdf; + /* fall-through */ case PKCS5_SCHEME_PBES1: - kdf = pbkdf1; - keymat = chunk_alloca(this->keylen * 2); + keymat = chunk_alloca(this->keylen + + this->crypter->get_iv_size(this->crypter)); key = chunk_create(keymat.ptr, this->keylen); - iv = chunk_create(keymat.ptr + this->keylen, this->keylen); + iv = chunk_create(keymat.ptr + this->keylen, + keymat.len - this->keylen); break; case PKCS5_SCHEME_PBES2: kdf = pbkdf2; @@ -539,6 +699,7 @@ METHOD(pkcs5_t, destroy, void, switch (this->scheme) { case PKCS5_SCHEME_PBES1: + case PKCS5_SCHEME_PKCS12: DESTROY_IF(this->data.pbes1.hasher); break; case PKCS5_SCHEME_PBES2: @@ -579,6 +740,12 @@ pkcs5_t *pkcs5_from_algorithmIdentifier(chunk_t blob, int level0) this->encr = ENCR_DES; this->data.pbes1.hash = HASH_SHA1; break; + case OID_PBE_SHA1_RC2_CBC_40: + this->scheme = PKCS5_SCHEME_PKCS12; + this->keylen = 5; + this->encr = ENCR_RC2_CBC; + this->data.pbes1.hash = HASH_SHA1; + break; case OID_PBES2: this->scheme = PKCS5_SCHEME_PBES2; break; @@ -590,6 +757,7 @@ pkcs5_t *pkcs5_from_algorithmIdentifier(chunk_t blob, int level0) switch (this->scheme) { case PKCS5_SCHEME_PBES1: + case PKCS5_SCHEME_PKCS12: if (!parse_pbes1_params(this, params, level0)) { goto failure; From d41e54c68dee58c5675cea2782f9a16103f0afc3 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 11 Apr 2013 15:02:28 +0200 Subject: [PATCH 07/29] Move PKCS#12 key derivation to a separate file --- src/libstrongswan/Android.mk | 2 +- src/libstrongswan/Makefile.am | 3 +- .../credentials/containers/pkcs12.c | 173 ++++++++++++++++++ .../credentials/containers/pkcs12.h | 51 ++++++ src/libstrongswan/crypto/pkcs5.c | 156 ++-------------- 5 files changed, 238 insertions(+), 147 deletions(-) create mode 100644 src/libstrongswan/credentials/containers/pkcs12.c create mode 100644 src/libstrongswan/credentials/containers/pkcs12.h diff --git a/src/libstrongswan/Android.mk b/src/libstrongswan/Android.mk index 289697a46..75b501fdd 100644 --- a/src/libstrongswan/Android.mk +++ b/src/libstrongswan/Android.mk @@ -17,7 +17,7 @@ credentials/cred_encoding.c credentials/keys/private_key.c \ credentials/keys/public_key.c credentials/keys/shared_key.c \ credentials/certificates/certificate.c credentials/certificates/crl.c \ credentials/certificates/ocsp_response.c \ -credentials/containers/container.c \ +credentials/containers/container.c credentials/containers/pkcs12.c \ credentials/ietf_attributes/ietf_attributes.c credentials/credential_manager.c \ credentials/sets/auth_cfg_wrapper.c credentials/sets/ocsp_response_wrapper.c \ credentials/sets/cert_cache.c credentials/sets/mem_cred.c \ diff --git a/src/libstrongswan/Makefile.am b/src/libstrongswan/Makefile.am index ae4fc75b6..db1f8cf33 100644 --- a/src/libstrongswan/Makefile.am +++ b/src/libstrongswan/Makefile.am @@ -15,7 +15,7 @@ credentials/cred_encoding.c credentials/keys/private_key.c \ credentials/keys/public_key.c credentials/keys/shared_key.c \ credentials/certificates/certificate.c credentials/certificates/crl.c \ credentials/certificates/ocsp_response.c \ -credentials/containers/container.c \ +credentials/containers/container.c credentials/containers/pkcs12.c \ credentials/ietf_attributes/ietf_attributes.c credentials/credential_manager.c \ credentials/sets/auth_cfg_wrapper.c credentials/sets/ocsp_response_wrapper.c \ credentials/sets/cert_cache.c credentials/sets/mem_cred.c \ @@ -55,6 +55,7 @@ credentials/certificates/pkcs10.h credentials/certificates/ocsp_request.h \ credentials/certificates/ocsp_response.h \ credentials/certificates/pgp_certificate.h \ credentials/containers/container.h credentials/containers/pkcs7.h \ +credentials/containers/pkcs12.h \ credentials/ietf_attributes/ietf_attributes.h \ credentials/credential_manager.h credentials/sets/auth_cfg_wrapper.h \ credentials/sets/ocsp_response_wrapper.h credentials/sets/cert_cache.h \ diff --git a/src/libstrongswan/credentials/containers/pkcs12.c b/src/libstrongswan/credentials/containers/pkcs12.c new file mode 100644 index 000000000..7b812d27d --- /dev/null +++ b/src/libstrongswan/credentials/containers/pkcs12.c @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2013 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pkcs12.h" + +#include + +/** + * v * ceiling(len/v) + */ +#define PKCS12_LEN(len, v) (((len) + v-1) & ~(v-1)) + +/** + * Copy src to dst as many times as possible + */ +static inline void copy_chunk(chunk_t dst, chunk_t src) +{ + size_t i; + + for (i = 0; i < dst.len; i++) + { + dst.ptr[i] = src.ptr[i % src.len]; + } +} + +/** + * Treat two chunks as integers in network order and add them together. + * The result is stored in the first chunk, if the second chunk is longer or the + * result overflows this is ignored. + */ +static void add_chunks(chunk_t a, chunk_t b) +{ + u_int16_t sum; + u_int8_t rem = 0; + ssize_t i, j; + + for (i = a.len - 1, j = b.len -1; i >= 0 && j >= 0; i--, j--) + { + sum = a.ptr[i] + b.ptr[j] + rem; + a.ptr[i] = (u_char)sum; + rem = sum >> 8; + } + for (; i >= 0 && rem; i--) + { + sum = a.ptr[i] + rem; + a.ptr[i] = (u_char)sum; + rem = sum >> 8; + } +} + +/** + * Do the actual key derivation with the given hasher, password and id. + */ +static bool derive_key(hash_algorithm_t hash, chunk_t unicode, chunk_t salt, + u_int64_t iterations, char id, chunk_t result) +{ + chunk_t out = result, D, S, P = chunk_empty, I, Ai, B, Ij; + hasher_t *hasher; + size_t Slen, v, u; + u_int64_t i; + bool success = FALSE; + + hasher = lib->crypto->create_hasher(lib->crypto, hash); + if (!hasher) + { + DBG1(DBG_ASN, " %N hash algorithm not available", + hash_algorithm_names, hash); + return FALSE; + } + switch (hash) + { + case HASH_MD2: + case HASH_MD5: + case HASH_SHA1: + case HASH_SHA224: + case HASH_SHA256: + v = 64; + break; + case HASH_SHA384: + case HASH_SHA512: + v = 128; + break; + default: + goto end; + } + u = hasher->get_hash_size(hasher); + + D = chunk_alloca(v); + memset(D.ptr, id, D.len); + + Slen = PKCS12_LEN(salt.len, v); + I = chunk_alloca(Slen + PKCS12_LEN(unicode.len, v)); + S = chunk_create(I.ptr, Slen); + P = chunk_create(I.ptr + Slen, I.len - Slen); + copy_chunk(S, salt); + copy_chunk(P, unicode); + + Ai = chunk_alloca(u); + B = chunk_alloca(v); + + while (TRUE) + { + if (!hasher->get_hash(hasher, D, NULL) || + !hasher->get_hash(hasher, I, Ai.ptr)) + { + goto end; + } + for (i = 1; i < iterations; i++) + { + if (!hasher->get_hash(hasher, Ai, Ai.ptr)) + { + goto end; + } + } + memcpy(out.ptr, Ai.ptr, min(out.len, Ai.len)); + out = chunk_skip(out, Ai.len); + if (!out.len) + { + break; + } + copy_chunk(B, Ai); + /* B = B+1 */ + add_chunks(B, chunk_from_chars(0x01)); + Ij = chunk_create(I.ptr, v); + for (i = 0; i < I.len; i += v, Ij.ptr += v) + { /* Ij = Ij + B + 1 */ + add_chunks(Ij, B); + } + } + success = TRUE; +end: + hasher->destroy(hasher); + return success; +} + +/* + * Described in header + */ +bool pkcs12_derive_key(hash_algorithm_t hash, chunk_t password, chunk_t salt, + u_int64_t iterations, pkcs12_key_type_t type, chunk_t key) +{ + chunk_t unicode = chunk_empty; + bool success; + int i; + + if (password.len) + { /* convert the password to UTF-16BE (without BOM) with 0 terminator */ + unicode = chunk_alloca(password.len * 2 + 2); + for (i = 0; i < password.len; i++) + { + unicode.ptr[i * 2] = 0; + unicode.ptr[i * 2 + 1] = password.ptr[i]; + } + unicode.ptr[i * 2] = 0; + unicode.ptr[i * 2 + 1] = 0; + } + + success = derive_key(hash, unicode, salt, iterations, type, key); + memwipe(unicode.ptr, unicode.len); + return success; +} diff --git a/src/libstrongswan/credentials/containers/pkcs12.h b/src/libstrongswan/credentials/containers/pkcs12.h new file mode 100644 index 000000000..a6c9746ef --- /dev/null +++ b/src/libstrongswan/credentials/containers/pkcs12.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2013 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup pkcs12 pkcs12 + * @{ @ingroup containers + */ + +#ifndef PKCS12_H_ +#define PKCS12_H_ + +#include + +typedef enum pkcs12_key_type_t pkcs12_key_type_t; + +/** + * The types of password based keys used by PKCS#12. + */ +enum pkcs12_key_type_t { + PKCS12_KEY_ENCRYPTION = 1, + PKCS12_KEY_IV = 2, + PKCS12_KEY_MAC = 3, +}; + +/** + * Derive the keys used in PKCS#12 for password integrity/privacy mode. + * + * @param hash hash algorithm to use for key derivation + * @param password password (ASCII) + * @param salt salt value + * @param iterations number of iterations + * @param type type of key to derive + * @param key the returned key, must be allocated of desired length + * @return TRUE on success + */ +bool pkcs12_derive_key(hash_algorithm_t hash, chunk_t password, chunk_t salt, + u_int64_t iterations, pkcs12_key_type_t type, chunk_t key); + +#endif /** PKCS12_H_ @}*/ diff --git a/src/libstrongswan/crypto/pkcs5.c b/src/libstrongswan/crypto/pkcs5.c index b0fae5278..7a7339915 100644 --- a/src/libstrongswan/crypto/pkcs5.c +++ b/src/libstrongswan/crypto/pkcs5.c @@ -19,6 +19,7 @@ #include #include #include +#include typedef struct private_pkcs5_t private_pkcs5_t; @@ -161,156 +162,19 @@ static bool decrypt_generic(private_pkcs5_t *this, chunk_t password, } /** - * v * ceiling(len/v) - */ -#define PKCS12_LEN(len, v) (((len) + v-1) & ~(v-1)) - -/** - * Copy src to dst as many times as possible - */ -static inline void pkcs12_copy_chunk(chunk_t dst, chunk_t src) -{ - size_t i; - - for (i = 0; i < dst.len; i++) - { - dst.ptr[i] = src.ptr[i % src.len]; - } -} - -/** - * Treat two chunks as integers in network order and add them together. - * The result is stored in the first chunk, if the second chunk is longer or the - * result overflows this is ignored. - */ -static void pkcs12_add_chunks(chunk_t a, chunk_t b) -{ - u_int16_t sum; - u_int8_t rem = 0; - ssize_t i, j; - - for (i = a.len - 1, j = b.len -1; i >= 0 && j >= 0; i--, j--) - { - sum = a.ptr[i] + b.ptr[j] + rem; - a.ptr[i] = (u_char)sum; - rem = sum >> 8; - } - for (; i >= 0 && rem; i--) - { - sum = a.ptr[i] + rem; - a.ptr[i] = (u_char)sum; - rem = sum >> 8; - } -} - -/** - * Do the actual key derivation with the given password and id - * id is 1 for encryption keys, 2 for IVs, 3 for MAC keys. - */ -static bool pkcs12_derive(private_pkcs5_t *this, chunk_t unicode, - char id, chunk_t result) -{ - chunk_t out = result, D, S, P = chunk_empty, I, Ai, B, Ij; - hasher_t *hasher; - size_t Slen, v, u; - u_int64_t i; - - switch (this->data.pbes1.hash) - { - case HASH_MD2: - case HASH_MD5: - case HASH_SHA1: - case HASH_SHA224: - case HASH_SHA256: - v = 64; - break; - case HASH_SHA384: - case HASH_SHA512: - v = 128; - break; - default: - return FALSE; - } - hasher = this->data.pbes1.hasher; - u = hasher->get_hash_size(hasher); - - D = chunk_alloca(v); - memset(D.ptr, id, D.len); - - Slen = PKCS12_LEN(this->salt.len, v); - I = chunk_alloca(Slen + PKCS12_LEN(unicode.len, v)); - S = chunk_create(I.ptr, Slen); - P = chunk_create(I.ptr + Slen, I.len - Slen); - pkcs12_copy_chunk(S, this->salt); - pkcs12_copy_chunk(P, unicode); - - Ai = chunk_alloca(u); - B = chunk_alloca(v); - - while (TRUE) - { - if (!hasher->get_hash(hasher, D, NULL) || - !hasher->get_hash(hasher, I, Ai.ptr)) - { - return FALSE; - } - for (i = 1; i < this->iterations; i++) - { - if (!hasher->get_hash(hasher, Ai, Ai.ptr)) - { - return FALSE; - } - } - memcpy(out.ptr, Ai.ptr, min(out.len, Ai.len)); - out = chunk_skip(out, Ai.len); - if (!out.len) - { - break; - } - pkcs12_copy_chunk(B, Ai); - /* B = B+1 */ - pkcs12_add_chunks(B, chunk_from_chars(0x01)); - Ij = chunk_create(I.ptr, v); - while (Ij.len) - { /* Ij = Ij + B + 1 */ - pkcs12_add_chunks(Ij, B); - Ij = chunk_skip(Ij, v); - } - } - return TRUE; -} - -/** - * KDF defined in PKCS#12 + * KDF as used by PKCS#12 */ static bool pkcs12_kdf(private_pkcs5_t *this, chunk_t password, chunk_t keymat) { - chunk_t unicode = chunk_empty, key, iv; - int i; - - if (password.len) - { /* convert the password to UTF-16BE (without BOM) with 0 terminator */ - unicode = chunk_alloca(password.len * 2 + 2); - for (i = 0; i < password.len; i++) - { - unicode.ptr[i * 2] = 0; - unicode.ptr[i * 2 + 1] = password.ptr[i]; - } - unicode.ptr[i * 2] = 0; - unicode.ptr[i * 2 + 1] = 0; - } + chunk_t key, iv; key = chunk_create(keymat.ptr, this->keylen); iv = chunk_create(keymat.ptr + this->keylen, keymat.len - this->keylen); - if (!pkcs12_derive(this, unicode, 1, key) || - !pkcs12_derive(this, unicode, 2, iv)) - { - memwipe(unicode.ptr, unicode.len); - return FALSE; - } - memwipe(unicode.ptr, unicode.len); - return TRUE; + return pkcs12_derive_key(this->data.pbes1.hash, password, this->salt, + this->iterations, PKCS12_KEY_ENCRYPTION, key) && + pkcs12_derive_key(this->data.pbes1.hash, password, this->salt, + this->iterations, PKCS12_KEY_IV, iv); } /** @@ -426,7 +290,6 @@ static bool ensure_crypto_primitives(private_pkcs5_t *this, chunk_t data) switch (this->scheme) { case PKCS5_SCHEME_PBES1: - case PKCS5_SCHEME_PKCS12: { if (!this->data.pbes1.hasher) { @@ -466,6 +329,8 @@ static bool ensure_crypto_primitives(private_pkcs5_t *this, chunk_t data) this->data.pbes2.prf = prf; } } + case PKCS5_SCHEME_PKCS12: + break; } return TRUE; } @@ -699,13 +564,14 @@ METHOD(pkcs5_t, destroy, void, switch (this->scheme) { case PKCS5_SCHEME_PBES1: - case PKCS5_SCHEME_PKCS12: DESTROY_IF(this->data.pbes1.hasher); break; case PKCS5_SCHEME_PBES2: DESTROY_IF(this->data.pbes2.prf); chunk_free(&this->data.pbes2.iv); break; + case PKCS5_SCHEME_PKCS12: + break; } free(this); } From 8e48e0009aa9cb64348f910fbda5f66d0a4d901e Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 11 Apr 2013 16:19:16 +0200 Subject: [PATCH 08/29] Add support for PKCS#7/CMS encrypted-data --- .../credentials/containers/container.c | 3 +- .../credentials/containers/container.h | 13 +- src/libstrongswan/plugins/pkcs7/Makefile.am | 1 + .../plugins/pkcs7/pkcs7_encrypted_data.c | 216 ++++++++++++++++++ .../plugins/pkcs7/pkcs7_encrypted_data.h | 36 +++ .../plugins/pkcs7/pkcs7_generic.c | 3 + 6 files changed, 267 insertions(+), 5 deletions(-) create mode 100644 src/libstrongswan/plugins/pkcs7/pkcs7_encrypted_data.c create mode 100644 src/libstrongswan/plugins/pkcs7/pkcs7_encrypted_data.h diff --git a/src/libstrongswan/credentials/containers/container.c b/src/libstrongswan/credentials/containers/container.c index d1e67b21b..9f01f559d 100644 --- a/src/libstrongswan/credentials/containers/container.c +++ b/src/libstrongswan/credentials/containers/container.c @@ -15,9 +15,10 @@ #include "container.h" -ENUM(container_type_names, CONTAINER_PKCS7, CONTAINER_PKCS7_ENVELOPED_DATA, +ENUM(container_type_names, CONTAINER_PKCS7, CONTAINER_PKCS7_ENCRYPTED_DATA, "PKCS7", "PKCS7_DATA", "PKCS7_SIGNED_DATA", "PKCS7_ENVELOPED_DATA", + "PKCS7_ENCRYPTED_DATA", ); diff --git a/src/libstrongswan/credentials/containers/container.h b/src/libstrongswan/credentials/containers/container.h index fc5c09041..ff1d1e1a2 100644 --- a/src/libstrongswan/credentials/containers/container.h +++ b/src/libstrongswan/credentials/containers/container.h @@ -1,4 +1,7 @@ /* + * Copyright (C) 2013 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * * Copyright (C) 2012 Martin Willi * Copyright (C) 2012 revosec AG * @@ -31,14 +34,16 @@ typedef enum container_type_t container_type_t; * Type of the container. */ enum container_type_t { - /** Any kind of PKCS7/CMS container */ + /** Any kind of PKCS#7/CMS container */ CONTAINER_PKCS7, - /** PKCS7/CMS plain "data" */ + /** PKCS#7/CMS plain "data" */ CONTAINER_PKCS7_DATA, - /** PKCS7/CMS "signed-data" */ + /** PKCS#7/CMS "signed-data" */ CONTAINER_PKCS7_SIGNED_DATA, - /** PKCS7/CMS "enveloped-data" */ + /** PKCS#7/CMS "enveloped-data" */ CONTAINER_PKCS7_ENVELOPED_DATA, + /** PKCS#7/CMS "encrypted-data" */ + CONTAINER_PKCS7_ENCRYPTED_DATA, }; /** diff --git a/src/libstrongswan/plugins/pkcs7/Makefile.am b/src/libstrongswan/plugins/pkcs7/Makefile.am index 6310daece..000bde23e 100644 --- a/src/libstrongswan/plugins/pkcs7/Makefile.am +++ b/src/libstrongswan/plugins/pkcs7/Makefile.am @@ -12,6 +12,7 @@ endif libstrongswan_pkcs7_la_SOURCES = \ pkcs7_generic.h pkcs7_generic.c \ pkcs7_signed_data.h pkcs7_signed_data.c \ + pkcs7_encrypted_data.h pkcs7_encrypted_data.c \ pkcs7_enveloped_data.h pkcs7_enveloped_data.c \ pkcs7_data.h pkcs7_data.c \ pkcs7_attributes.h pkcs7_attributes.c \ diff --git a/src/libstrongswan/plugins/pkcs7/pkcs7_encrypted_data.c b/src/libstrongswan/plugins/pkcs7/pkcs7_encrypted_data.c new file mode 100644 index 000000000..2c414c391 --- /dev/null +++ b/src/libstrongswan/plugins/pkcs7/pkcs7_encrypted_data.c @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2013 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pkcs7_encrypted_data.h" + +#include +#include +#include +#include +#include + +typedef struct private_pkcs7_encrypted_data_t private_pkcs7_encrypted_data_t; + +/** + * Private data of a PKCS#7 signed-data container. + */ +struct private_pkcs7_encrypted_data_t { + + /** + * Implements pkcs7_t. + */ + pkcs7_t public; + + /** + * Decrypted content + */ + chunk_t content; + + /** + * Encrypted and encoded PKCS#7 encrypted-data + */ + chunk_t encoding; +}; + +/** + * Decrypt encrypted-data with available passwords + */ +static bool decrypt(pkcs5_t *pkcs5, chunk_t data, chunk_t *decrypted) +{ + enumerator_t *enumerator; + shared_key_t *shared; + bool success = FALSE; + + enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr, + SHARED_PRIVATE_KEY_PASS, NULL, NULL); + while (enumerator->enumerate(enumerator, &shared, NULL, NULL)) + { + if (pkcs5->decrypt(pkcs5, shared->get_key(shared), data, decrypted)) + { + success = TRUE; + break; + } + } + enumerator->destroy(enumerator); + return success; +} + +/** + * ASN.1 definition of the PKCS#7 encrypted-data type + */ +static const asn1Object_t encryptedDataObjects[] = { + { 0, "encryptedData", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ + { 1, "version", ASN1_INTEGER, ASN1_BODY }, /* 1 */ + { 1, "encryptedContentInfo", ASN1_SEQUENCE, ASN1_OBJ }, /* 2 */ + { 2, "contentType", ASN1_OID, ASN1_BODY }, /* 3 */ + { 2, "contentEncryptionAlgorithm", ASN1_EOC, ASN1_RAW }, /* 4 */ + { 2, "encryptedContent", ASN1_CONTEXT_S_0, ASN1_BODY }, /* 5 */ + { 0, "exit", ASN1_EOC, ASN1_EXIT } +}; +#define PKCS7_VERSION 1 +#define PKCS7_CONTENT_TYPE 3 +#define PKCS7_CONTENT_ENC_ALGORITHM 4 +#define PKCS7_ENCRYPTED_CONTENT 5 + +/** + * Parse and decrypt encrypted-data + */ +static bool parse(private_pkcs7_encrypted_data_t *this, chunk_t content) +{ + asn1_parser_t *parser; + chunk_t object; + int objectID, version; + bool success = FALSE; + chunk_t encrypted = chunk_empty; + pkcs5_t *pkcs5 = NULL; + + parser = asn1_parser_create(encryptedDataObjects, content); + + while (parser->iterate(parser, &objectID, &object)) + { + int level = parser->get_level(parser); + + switch (objectID) + { + case PKCS7_VERSION: + version = object.len ? (int)*object.ptr : 0; + DBG2(DBG_LIB, " v%d", version); + if (version != 0) + { + DBG1(DBG_LIB, "encryptedData version is not 0"); + goto end; + } + break; + case PKCS7_CONTENT_TYPE: + if (asn1_known_oid(object) != OID_PKCS7_DATA) + { + DBG1(DBG_LIB, "encrypted content not of type pkcs7 data"); + goto end; + } + break; + case PKCS7_CONTENT_ENC_ALGORITHM: + pkcs5 = pkcs5_from_algorithmIdentifier(object, level + 1); + if (!pkcs5) + { + DBG1(DBG_LIB, "failed to detect PKCS#5 scheme"); + goto end; + } + break; + case PKCS7_ENCRYPTED_CONTENT: + encrypted = object; + break; + } + } + success = parser->success(parser); + +end: + parser->destroy(parser); + success = success && decrypt(pkcs5, encrypted, &this->content); + DESTROY_IF(pkcs5); + return success; +} + +METHOD(container_t, get_type, container_type_t, + private_pkcs7_encrypted_data_t *this) +{ + return CONTAINER_PKCS7_ENCRYPTED_DATA; +} + +METHOD(container_t, get_data, bool, + private_pkcs7_encrypted_data_t *this, chunk_t *data) +{ + if (this->content.len) + { + *data = chunk_clone(this->content); + return TRUE; + } + return FALSE; +} + +METHOD(container_t, get_encoding, bool, + private_pkcs7_encrypted_data_t *this, chunk_t *data) +{ + *data = chunk_clone(this->encoding); + return TRUE; +} + +METHOD(container_t, destroy, void, + private_pkcs7_encrypted_data_t *this) +{ + free(this->content.ptr); + free(this->encoding.ptr); + free(this); +} + +/** + * Generic constructor + */ +static private_pkcs7_encrypted_data_t* create_empty() +{ + private_pkcs7_encrypted_data_t *this; + + INIT(this, + .public = { + .container = { + .get_type = _get_type, + .create_signature_enumerator = (void*)enumerator_create_empty, + .get_data = _get_data, + .get_encoding = _get_encoding, + .destroy = _destroy, + }, + .create_cert_enumerator = (void*)enumerator_create_empty, + .get_attribute = (void*)return_false, + }, + ); + + return this; +} + +/** + * See header. + */ +pkcs7_t *pkcs7_encrypted_data_load(chunk_t encoding, chunk_t content) +{ + private_pkcs7_encrypted_data_t *this = create_empty(); + + this->encoding = chunk_clone(encoding); + if (!parse(this, content)) + { + destroy(this); + return NULL; + } + + return &this->public; +} diff --git a/src/libstrongswan/plugins/pkcs7/pkcs7_encrypted_data.h b/src/libstrongswan/plugins/pkcs7/pkcs7_encrypted_data.h new file mode 100644 index 000000000..b685557fc --- /dev/null +++ b/src/libstrongswan/plugins/pkcs7/pkcs7_encrypted_data.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2013 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup pkcs7_encrypted_data pkcs7_encrypted_data + * @{ @ingroup pkcs7p + */ + +#ifndef PKCS7_ENCRYPTED_DATA_H_ +#define PKCS7_ENCRYPTED_DATA_H_ + +#include +#include + +/** + * Parse a PKCS#7 encrypted-data container. + * + * @param encoding full contentInfo encoding + * @param content DER encoded content from contentInfo + * @return CONTAINER_PKCS7_ENCRYPTED_DATA container, NULL on failure + */ +pkcs7_t *pkcs7_encrypted_data_load(chunk_t encoding, chunk_t content); + +#endif /** PKCS7_ENCRYPTED_DATA_H_ @}*/ diff --git a/src/libstrongswan/plugins/pkcs7/pkcs7_generic.c b/src/libstrongswan/plugins/pkcs7/pkcs7_generic.c index 35d8d11a7..24d7cd848 100644 --- a/src/libstrongswan/plugins/pkcs7/pkcs7_generic.c +++ b/src/libstrongswan/plugins/pkcs7/pkcs7_generic.c @@ -20,6 +20,7 @@ #include "pkcs7_generic.h" #include "pkcs7_data.h" #include "pkcs7_signed_data.h" +#include "pkcs7_encrypted_data.h" #include "pkcs7_enveloped_data.h" #include @@ -85,6 +86,8 @@ end: return pkcs7_signed_data_load(blob, content); case OID_PKCS7_ENVELOPED_DATA: return pkcs7_enveloped_data_load(blob, content); + case OID_PKCS7_ENCRYPTED_DATA: + return pkcs7_encrypted_data_load(blob, content); default: DBG1(DBG_ASN, "pkcs7 content type %d not supported", type); return NULL; From 0d0929fa0c0977dc362c84d05cc5ef68edc37f58 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 11 Apr 2013 17:54:45 +0200 Subject: [PATCH 09/29] Register PKCS#8 builder for KEY_ANY --- src/libstrongswan/plugins/pkcs8/pkcs8_plugin.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libstrongswan/plugins/pkcs8/pkcs8_plugin.c b/src/libstrongswan/plugins/pkcs8/pkcs8_plugin.c index f78c83054..129fbb045 100644 --- a/src/libstrongswan/plugins/pkcs8/pkcs8_plugin.c +++ b/src/libstrongswan/plugins/pkcs8/pkcs8_plugin.c @@ -43,6 +43,7 @@ METHOD(plugin_t, get_features, int, { static plugin_feature_t f[] = { PLUGIN_REGISTER(PRIVKEY, pkcs8_private_key_load, FALSE), + PLUGIN_PROVIDE(PRIVKEY, KEY_ANY), PLUGIN_PROVIDE(PRIVKEY, KEY_RSA), PLUGIN_PROVIDE(PRIVKEY, KEY_ECDSA), }; From 047fca1169f210a2a9c308d7ea1f28401d6e29f8 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 11 Apr 2013 19:39:32 +0200 Subject: [PATCH 10/29] Support the PKCS#5/PKCS#12 encryption scheme used by OpenSSL for private keys --- src/libstrongswan/crypto/pkcs5.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libstrongswan/crypto/pkcs5.c b/src/libstrongswan/crypto/pkcs5.c index 7a7339915..ed2c41a0d 100644 --- a/src/libstrongswan/crypto/pkcs5.c +++ b/src/libstrongswan/crypto/pkcs5.c @@ -606,6 +606,12 @@ pkcs5_t *pkcs5_from_algorithmIdentifier(chunk_t blob, int level0) this->encr = ENCR_DES; this->data.pbes1.hash = HASH_SHA1; break; + case OID_PBE_SHA1_3DES_CBC: + this->scheme = PKCS5_SCHEME_PKCS12; + this->keylen = 24; + this->encr = ENCR_3DES; + this->data.pbes1.hash = HASH_SHA1; + break; case OID_PBE_SHA1_RC2_CBC_40: this->scheme = PKCS5_SCHEME_PKCS12; this->keylen = 5; From 199fdcadaed254c95ebd5ac0fb70493147584cc6 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 11 Apr 2013 19:41:48 +0200 Subject: [PATCH 11/29] Function added to convert a hash algorithm to an HMAC integrity algorithm --- src/libstrongswan/crypto/hashers/hasher.c | 66 +++++++++++++++++++++++ src/libstrongswan/crypto/hashers/hasher.h | 11 ++++ 2 files changed, 77 insertions(+) diff --git a/src/libstrongswan/crypto/hashers/hasher.c b/src/libstrongswan/crypto/hashers/hasher.c index dc73d5223..4ed48ba36 100644 --- a/src/libstrongswan/crypto/hashers/hasher.c +++ b/src/libstrongswan/crypto/hashers/hasher.c @@ -177,6 +177,72 @@ hash_algorithm_t hasher_algorithm_from_integrity(integrity_algorithm_t alg, return HASH_UNKNOWN; } +/* + * Described in header. + */ +integrity_algorithm_t hasher_algorithm_to_integrity(hash_algorithm_t alg, + size_t length) +{ + switch (alg) + { + case HASH_MD5: + switch (length) + { + case 12: + return AUTH_HMAC_MD5_96; + case 16: + return AUTH_HMAC_MD5_128; + } + break; + case HASH_SHA1: + case HASH_PREFERRED: + switch (length) + { + case 12: + return AUTH_HMAC_SHA1_96; + case 16: + return AUTH_HMAC_SHA1_128; + case 20: + return AUTH_HMAC_SHA1_160; + } + break; + case HASH_SHA256: + switch (length) + { + case 12: + return AUTH_HMAC_SHA2_256_96; + case 16: + return AUTH_HMAC_SHA2_256_128; + case 32: + return AUTH_HMAC_SHA2_256_256; + } + break; + case HASH_SHA384: + switch (length) + { + case 24: + return AUTH_HMAC_SHA2_384_192; + case 48: + return AUTH_HMAC_SHA2_384_384; + + } + break; + case HASH_SHA512: + switch (length) + { + case 32: + return AUTH_HMAC_SHA2_512_256; + } + break; + case HASH_MD2: + case HASH_MD4: + case HASH_SHA224: + case HASH_UNKNOWN: + break; + } + return AUTH_UNDEFINED; +} + /* * Described in header. */ diff --git a/src/libstrongswan/crypto/hashers/hasher.h b/src/libstrongswan/crypto/hashers/hasher.h index 759f6a23c..4e46fca10 100644 --- a/src/libstrongswan/crypto/hashers/hasher.h +++ b/src/libstrongswan/crypto/hashers/hasher.h @@ -153,6 +153,17 @@ hash_algorithm_t hasher_algorithm_from_prf(pseudo_random_function_t alg); hash_algorithm_t hasher_algorithm_from_integrity(integrity_algorithm_t alg, size_t *length); +/** + * Conversion of hash algorithm to integrity algorithm (if based on a hash). + * + * @param alg hash algorithm + * @param length length of the signature + * @return integrity algorithm, AUTH_UNDEFINED if none is known + * based on the given hash function + */ +integrity_algorithm_t hasher_algorithm_to_integrity(hash_algorithm_t alg, + size_t length); + /** * Conversion of hash algorithm into ASN.1 OID. * From feef637368c6c0d36c7c8829db5af0b0bd8fcc38 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 12 Apr 2013 11:59:01 +0200 Subject: [PATCH 12/29] Add pkcs12 plugin which adds support for decoding PKCS#12 containers --- configure.in | 6 +- src/libstrongswan/Makefile.am | 7 + src/libstrongswan/asn1/oid.txt | 11 + .../credentials/containers/container.c | 3 +- .../credentials/containers/container.h | 2 + .../credentials/containers/pkcs12.h | 27 + src/libstrongswan/plugins/pkcs12/Makefile.am | 16 + .../plugins/pkcs12/pkcs12_decode.c | 581 ++++++++++++++++++ .../plugins/pkcs12/pkcs12_decode.h | 38 ++ .../plugins/pkcs12/pkcs12_plugin.c | 77 +++ .../plugins/pkcs12/pkcs12_plugin.h | 42 ++ 11 files changed, 808 insertions(+), 2 deletions(-) create mode 100644 src/libstrongswan/plugins/pkcs12/Makefile.am create mode 100644 src/libstrongswan/plugins/pkcs12/pkcs12_decode.c create mode 100644 src/libstrongswan/plugins/pkcs12/pkcs12_decode.h create mode 100644 src/libstrongswan/plugins/pkcs12/pkcs12_plugin.c create mode 100644 src/libstrongswan/plugins/pkcs12/pkcs12_plugin.h diff --git a/configure.in b/configure.in index c51146a12..c858a294c 100644 --- a/configure.in +++ b/configure.in @@ -129,6 +129,7 @@ ARG_DISBL_SET([pubkey], [disable RAW public key support plugin.]) ARG_DISBL_SET([pkcs1], [disable PKCS1 key decoding plugin.]) ARG_DISBL_SET([pkcs7], [disable PKCS7 container support plugin.]) ARG_DISBL_SET([pkcs8], [disable PKCS8 private key decoding plugin.]) +ARG_DISBL_SET([pkcs12], [disable PKCS12 container support plugin.]) ARG_DISBL_SET([pgp], [disable PGP key decoding plugin.]) ARG_DISBL_SET([dnskey], [disable DNS RR key decoding plugin.]) ARG_DISBL_SET([sshkey], [disable SSH key decoding plugin.]) @@ -966,8 +967,9 @@ ADD_PLUGIN([revocation], [s charon nm cmd]) ADD_PLUGIN([constraints], [s charon nm cmd]) ADD_PLUGIN([pubkey], [s charon cmd]) ADD_PLUGIN([pkcs1], [s charon openac scepclient pki scripts manager medsrv attest nm cmd]) -ADD_PLUGIN([pkcs7], [s scepclient pki]) +ADD_PLUGIN([pkcs7], [s scepclient pki scripts]) ADD_PLUGIN([pkcs8], [s charon openac scepclient pki scripts manager medsrv attest nm cmd]) +ADD_PLUGIN([pkcs12], [s charon scepclient pki scripts]) ADD_PLUGIN([pgp], [s charon]) ADD_PLUGIN([dnskey], [s charon]) ADD_PLUGIN([sshkey], [s charon nm cmd]) @@ -1100,6 +1102,7 @@ AM_CONDITIONAL(USE_PUBKEY, test x$pubkey = xtrue) AM_CONDITIONAL(USE_PKCS1, test x$pkcs1 = xtrue) AM_CONDITIONAL(USE_PKCS7, test x$pkcs7 = xtrue) AM_CONDITIONAL(USE_PKCS8, test x$pkcs8 = xtrue) +AM_CONDITIONAL(USE_PKCS12, test x$pkcs12 = xtrue) AM_CONDITIONAL(USE_PGP, test x$pgp = xtrue) AM_CONDITIONAL(USE_DNSKEY, test x$dnskey = xtrue) AM_CONDITIONAL(USE_SSHKEY, test x$sshkey = xtrue) @@ -1296,6 +1299,7 @@ AC_CONFIG_FILES([ src/libstrongswan/plugins/pkcs1/Makefile src/libstrongswan/plugins/pkcs7/Makefile src/libstrongswan/plugins/pkcs8/Makefile + src/libstrongswan/plugins/pkcs12/Makefile src/libstrongswan/plugins/pgp/Makefile src/libstrongswan/plugins/dnskey/Makefile src/libstrongswan/plugins/sshkey/Makefile diff --git a/src/libstrongswan/Makefile.am b/src/libstrongswan/Makefile.am index db1f8cf33..2465fb09d 100644 --- a/src/libstrongswan/Makefile.am +++ b/src/libstrongswan/Makefile.am @@ -309,6 +309,13 @@ if MONOLITHIC endif endif +if USE_PKCS12 + SUBDIRS += plugins/pkcs12 +if MONOLITHIC + libstrongswan_la_LIBADD += plugins/pkcs12/libstrongswan-pkcs12.la +endif +endif + if USE_PGP SUBDIRS += plugins/pgp if MONOLITHIC diff --git a/src/libstrongswan/asn1/oid.txt b/src/libstrongswan/asn1/oid.txt index 8cbd07e4b..6030aa111 100644 --- a/src/libstrongswan/asn1/oid.txt +++ b/src/libstrongswan/asn1/oid.txt @@ -121,6 +121,9 @@ 0x08 "unstructuredAddress" OID_UNSTRUCTURED_ADDRESS 0x0E "extensionRequest" OID_EXTENSION_REQUEST 0x0F "S/MIME Capabilities" + 0x16 "certTypes" + 0x01 "X.509" OID_X509_CERTIFICATE + 0x02 "SDSI" 0x0c "PKCS-12" 0x01 "pbeIds" 0x01 "pbeWithSHAAnd128BitRC4" OID_PBE_SHA1_RC4_128 @@ -129,6 +132,14 @@ 0x04 "pbeWithSHAAnd2-KeyTripleDES-CBC" OID_PBE_SHA1_3DES_2KEY_CBC 0x05 "pbeWithSHAAnd128BitRC2-CBC" OID_PBE_SHA1_RC2_CBC_128 0x06 "pbeWithSHAAnd40BitRC2-CBC" OID_PBE_SHA1_RC2_CBC_40 + 0x0a "PKCS-12v1" + 0x01 "bagIds" + 0x01 "keyBag" OID_P12_KEY_BAG + 0x02 "pkcs8ShroudedKeyBag" OID_P12_PKCS8_KEY_BAG + 0x03 "certBag" OID_P12_CERT_BAG + 0x04 "crlBag" OID_P12_CRL_BAG + 0x05 "secretBag" + 0x06 "safeContentsBag" 0x02 "digestAlgorithm" 0x02 "md2" OID_MD2 0x05 "md5" OID_MD5 diff --git a/src/libstrongswan/credentials/containers/container.c b/src/libstrongswan/credentials/containers/container.c index 9f01f559d..7456d43db 100644 --- a/src/libstrongswan/credentials/containers/container.c +++ b/src/libstrongswan/credentials/containers/container.c @@ -15,10 +15,11 @@ #include "container.h" -ENUM(container_type_names, CONTAINER_PKCS7, CONTAINER_PKCS7_ENCRYPTED_DATA, +ENUM(container_type_names, CONTAINER_PKCS7, CONTAINER_PKCS12, "PKCS7", "PKCS7_DATA", "PKCS7_SIGNED_DATA", "PKCS7_ENVELOPED_DATA", "PKCS7_ENCRYPTED_DATA", + "PKCS12", ); diff --git a/src/libstrongswan/credentials/containers/container.h b/src/libstrongswan/credentials/containers/container.h index ff1d1e1a2..ee329881d 100644 --- a/src/libstrongswan/credentials/containers/container.h +++ b/src/libstrongswan/credentials/containers/container.h @@ -44,6 +44,8 @@ enum container_type_t { CONTAINER_PKCS7_ENVELOPED_DATA, /** PKCS#7/CMS "encrypted-data" */ CONTAINER_PKCS7_ENCRYPTED_DATA, + /** A PKCS#12 container */ + CONTAINER_PKCS12, }; /** diff --git a/src/libstrongswan/credentials/containers/pkcs12.h b/src/libstrongswan/credentials/containers/pkcs12.h index a6c9746ef..f22ef045a 100644 --- a/src/libstrongswan/credentials/containers/pkcs12.h +++ b/src/libstrongswan/credentials/containers/pkcs12.h @@ -21,9 +21,11 @@ #ifndef PKCS12_H_ #define PKCS12_H_ +#include #include typedef enum pkcs12_key_type_t pkcs12_key_type_t; +typedef struct pkcs12_t pkcs12_t; /** * The types of password based keys used by PKCS#12. @@ -34,6 +36,31 @@ enum pkcs12_key_type_t { PKCS12_KEY_MAC = 3, }; +/** + * PKCS#12/PFX container type. + */ +struct pkcs12_t { + + /** + * Implements container_t. + */ + container_t container; + + /** + * Create an enumerator over extracted certificates. + * + * @return enumerator over certificate_t + */ + enumerator_t* (*create_cert_enumerator)(pkcs12_t *this); + + /** + * Create an enumerator over extracted private keys. + * + * @return enumerator over private_key_t + */ + enumerator_t* (*create_key_enumerator)(pkcs12_t *this); +}; + /** * Derive the keys used in PKCS#12 for password integrity/privacy mode. * diff --git a/src/libstrongswan/plugins/pkcs12/Makefile.am b/src/libstrongswan/plugins/pkcs12/Makefile.am new file mode 100644 index 000000000..1f703bd6c --- /dev/null +++ b/src/libstrongswan/plugins/pkcs12/Makefile.am @@ -0,0 +1,16 @@ + +INCLUDES = -I$(top_srcdir)/src/libstrongswan + +AM_CFLAGS = -rdynamic + +if MONOLITHIC +noinst_LTLIBRARIES = libstrongswan-pkcs12.la +else +plugin_LTLIBRARIES = libstrongswan-pkcs12.la +endif + +libstrongswan_pkcs12_la_SOURCES = \ + pkcs12_plugin.h pkcs12_plugin.c \ + pkcs12_decode.h pkcs12_decode.c + +libstrongswan_pkcs12_la_LDFLAGS = -module -avoid-version diff --git a/src/libstrongswan/plugins/pkcs12/pkcs12_decode.c b/src/libstrongswan/plugins/pkcs12/pkcs12_decode.c new file mode 100644 index 000000000..379f24796 --- /dev/null +++ b/src/libstrongswan/plugins/pkcs12/pkcs12_decode.c @@ -0,0 +1,581 @@ +/* + * Copyright (C) 2013 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pkcs12_decode.h" + +#include +#include +#include +#include +#include + +typedef struct private_pkcs12_t private_pkcs12_t; + +/** + * Private data of a pkcs12_t object + */ +struct private_pkcs12_t { + + /** + * Public interface + */ + pkcs12_t public; + + /** + * Contained credentials + */ + mem_cred_t *creds; +}; + +METHOD(container_t, get_type, container_type_t, + private_pkcs12_t *this) +{ + return CONTAINER_PKCS12; +} + +METHOD(container_t, get_data, bool, + private_pkcs12_t *this, chunk_t *data) +{ + /* we could return the content of the outer-most PKCS#7 container (authSafe) + * don't really see the point though */ + return FALSE; +} + +METHOD(container_t, get_encoding, bool, + private_pkcs12_t *this, chunk_t *encoding) +{ + /* similar to get_data() we don't have any use for it at the moment */ + return FALSE; +} + +METHOD(pkcs12_t, create_cert_enumerator, enumerator_t*, + private_pkcs12_t *this) +{ + return this->creds->set.create_cert_enumerator(&this->creds->set, CERT_ANY, + KEY_ANY, NULL, FALSE); +} + +METHOD(pkcs12_t, create_key_enumerator, enumerator_t*, + private_pkcs12_t *this) +{ + return this->creds->set.create_private_enumerator(&this->creds->set, + KEY_ANY, NULL); +} + +METHOD(container_t, destroy, void, + private_pkcs12_t *this) +{ + this->creds->destroy(this->creds); + free(this); +} + +static private_pkcs12_t *pkcs12_create() +{ + private_pkcs12_t *this; + + INIT(this, + .public = { + .container = { + .get_type = _get_type, + .create_signature_enumerator = (void*)enumerator_create_empty, + .get_data = _get_data, + .get_encoding = _get_encoding, + .destroy = _destroy, + }, + .create_cert_enumerator = _create_cert_enumerator, + .create_key_enumerator = _create_key_enumerator, + }, + .creds = mem_cred_create(), + ); + return this; +} + +/** + * ASN.1 definition of an CertBag structure + */ +static const asn1Object_t certBagObjects[] = { + { 0, "CertBag", ASN1_SEQUENCE, ASN1_BODY }, /* 0 */ + { 1, "certId", ASN1_OID, ASN1_BODY }, /* 1 */ + { 1, "certValue", ASN1_CONTEXT_C_0, ASN1_BODY }, /* 2 */ + { 0, "exit", ASN1_EOC, ASN1_EXIT } +}; +#define CERT_BAG_ID 1 +#define CERT_BAG_VALUE 2 + +/** + * Parse a CertBag structure and extract certificate + */ +static bool add_certificate(private_pkcs12_t *this, int level0, chunk_t blob) +{ + asn1_parser_t *parser; + chunk_t object; + int objectID; + int oid = OID_UNKNOWN; + bool success = FALSE; + + parser = asn1_parser_create(certBagObjects, blob); + parser->set_top_level(parser, level0); + + while (parser->iterate(parser, &objectID, &object)) + { + switch (objectID) + { + case CERT_BAG_ID: + oid = asn1_known_oid(object); + break; + case CERT_BAG_VALUE: + { + if (oid == OID_X509_CERTIFICATE && + asn1_parse_simple_object(&object, ASN1_OCTET_STRING, + parser->get_level(parser)+1, "x509Certificate")) + { + certificate_t *cert; + + DBG2(DBG_ASN, "-- > parsing certificate from PKCS#12"); + cert = lib->creds->create(lib->creds, + CRED_CERTIFICATE, CERT_X509, + BUILD_BLOB_ASN1_DER, object, + BUILD_END); + if (cert) + { + this->creds->add_cert(this->creds, FALSE, cert); + DBG2(DBG_ASN, "-- < --"); + } + else + { + DBG2(DBG_ASN, "-- < failed parsing certificate from " + "PKCS#12"); + } + } + break; + } + } + } + success = parser->success(parser); + parser->destroy(parser); + return success; +} + +/** + * ASN.1 definition of an AuthenticatedSafe structure + */ +static const asn1Object_t safeContentsObjects[] = { + { 0, "SafeContents", ASN1_SEQUENCE, ASN1_LOOP }, /* 0 */ + { 1, "SafeBag", ASN1_SEQUENCE, ASN1_BODY }, /* 1 */ + { 2, "bagId", ASN1_OID, ASN1_BODY }, /* 2 */ + { 2, "bagValue", ASN1_CONTEXT_C_0, ASN1_BODY }, /* 3 */ + { 2, "bagAttr", ASN1_SET, ASN1_OPT|ASN1_RAW }, /* 4 */ + { 2, "end opt", ASN1_EOC, ASN1_END }, /* 5 */ + { 0, "end loop", ASN1_EOC, ASN1_END }, /* 6 */ + { 0, "exit", ASN1_EOC, ASN1_EXIT } +}; +#define SAFE_BAG_ID 2 +#define SAFE_BAG_VALUE 3 + +/** + * Parse a SafeContents structure and extract credentials + */ +static bool parse_safe_contents(private_pkcs12_t *this, int level0, + chunk_t blob) +{ + asn1_parser_t *parser; + chunk_t object; + int objectID; + int oid = OID_UNKNOWN; + bool success = FALSE; + + parser = asn1_parser_create(safeContentsObjects, blob); + parser->set_top_level(parser, level0); + + while (parser->iterate(parser, &objectID, &object)) + { + switch (objectID) + { + case SAFE_BAG_ID: + oid = asn1_known_oid(object); + break; + case SAFE_BAG_VALUE: + { + switch (oid) + { + case OID_P12_CERT_BAG: + { + add_certificate(this, parser->get_level(parser)+1, + object); + break; + } + case OID_P12_KEY_BAG: + case OID_P12_PKCS8_KEY_BAG: + { + private_key_t *key; + + DBG2(DBG_ASN, "-- > parsing private key from PKCS#12"); + key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, + KEY_ANY, BUILD_BLOB_ASN1_DER, object, + BUILD_END); + if (key) + { + this->creds->add_key(this->creds, key); + DBG2(DBG_ASN, "-- < --"); + } + else + { + DBG2(DBG_ASN, "-- < failed parsing private key " + "from PKCS#12"); + } + } + default: + break; + } + break; + } + } + } + success = parser->success(parser); + parser->destroy(parser); + return success; +} + +/** + * ASN.1 definition of an AuthenticatedSafe structure + */ +static const asn1Object_t authenticatedSafeObjects[] = { + { 0, "AuthenticatedSafe", ASN1_SEQUENCE, ASN1_LOOP }, /* 0 */ + { 1, "ContentInfo", ASN1_SEQUENCE, ASN1_OBJ }, /* 1 */ + { 0, "end loop", ASN1_EOC, ASN1_END }, /* 2 */ + { 0, "exit", ASN1_EOC, ASN1_EXIT } +}; +#define AUTHENTICATED_SAFE_DATA 1 + +/** + * Parse an AuthenticatedSafe structure + */ +static bool parse_authenticated_safe(private_pkcs12_t *this, chunk_t blob) +{ + asn1_parser_t *parser; + chunk_t object; + int objectID; + bool success = FALSE; + + parser = asn1_parser_create(authenticatedSafeObjects, blob); + + while (parser->iterate(parser, &objectID, &object)) + { + switch (objectID) + { + case AUTHENTICATED_SAFE_DATA: + { + container_t *container; + chunk_t data; + + container = lib->creds->create(lib->creds, CRED_CONTAINER, + CONTAINER_PKCS7, BUILD_BLOB_ASN1_DER, + object, BUILD_END); + if (!container) + { + goto end; + } + switch (container->get_type(container)) + { + case CONTAINER_PKCS7_DATA: + case CONTAINER_PKCS7_ENCRYPTED_DATA: + case CONTAINER_PKCS7_ENVELOPED_DATA: + if (container->get_data(container, &data)) + { + break; + } + /* fall-through */ + default: + container->destroy(container); + goto end; + } + container->destroy(container); + + if (!parse_safe_contents(this, parser->get_level(parser)+1, + data)) + { + chunk_free(&data); + goto end; + } + chunk_free(&data); + break; + } + } + } + success = parser->success(parser); +end: + parser->destroy(parser); + return success; +} + +/** + * Verify the given MAC with available passwords. + */ +static bool verify_mac(hash_algorithm_t hash, chunk_t salt, + u_int64_t iterations, chunk_t data, chunk_t mac) +{ + integrity_algorithm_t integ; + enumerator_t *enumerator; + shared_key_t *shared; + signer_t *signer; + chunk_t key, calculated; + bool success = FALSE; + + integ = hasher_algorithm_to_integrity(hash, mac.len); + signer = lib->crypto->create_signer(lib->crypto, integ); + if (!signer) + { + return FALSE; + } + key = chunk_alloca(signer->get_key_size(signer)); + calculated = chunk_alloca(signer->get_block_size(signer)); + + enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr, + SHARED_PRIVATE_KEY_PASS, NULL, NULL); + while (enumerator->enumerate(enumerator, &shared, NULL, NULL)) + { + if (!pkcs12_derive_key(hash, shared->get_key(shared), salt, iterations, + PKCS12_KEY_MAC, key)) + { + break; + } + if (!signer->set_key(signer, key) || + !signer->get_signature(signer, data, calculated.ptr)) + { + break; + } + if (chunk_equals(mac, calculated)) + { + success = TRUE; + break; + } + } + enumerator->destroy(enumerator); + signer->destroy(signer); + return success; +} + +/** + * ASN.1 definition of digestInfo + */ +static const asn1Object_t digestInfoObjects[] = { + { 0, "digestInfo", ASN1_SEQUENCE, ASN1_OBJ }, /* 0 */ + { 1, "digestAlgorithm", ASN1_EOC, ASN1_RAW }, /* 1 */ + { 1, "digest", ASN1_OCTET_STRING, ASN1_BODY }, /* 2 */ + { 0, "exit", ASN1_EOC, ASN1_EXIT } +}; +#define DIGEST_INFO_ALGORITHM 1 +#define DIGEST_INFO_DIGEST 2 + +/** + * Parse a digestInfo structure + */ +static bool parse_digest_info(chunk_t blob, int level0, hash_algorithm_t *hash, + chunk_t *digest) +{ + asn1_parser_t *parser; + chunk_t object; + int objectID; + bool success; + + parser = asn1_parser_create(digestInfoObjects, blob); + parser->set_top_level(parser, level0); + + while (parser->iterate(parser, &objectID, &object)) + { + switch (objectID) + + { + case DIGEST_INFO_ALGORITHM: + { + int oid = asn1_parse_algorithmIdentifier(object, + parser->get_level(parser)+1, NULL); + + *hash = hasher_algorithm_from_oid(oid); + break; + } + case DIGEST_INFO_DIGEST: + { + *digest = object; + break; + } + default: + break; + } + } + success = parser->success(parser); + parser->destroy(parser); + return success; +} + +/** + * ASN.1 definition of a PFX structure + */ +static const asn1Object_t PFXObjects[] = { + { 0, "PFX", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ + { 1, "version", ASN1_INTEGER, ASN1_BODY }, /* 1 */ + { 1, "authSafe", ASN1_SEQUENCE, ASN1_OBJ }, /* 2 */ + { 1, "macData", ASN1_SEQUENCE, ASN1_OPT|ASN1_BODY }, /* 3 */ + { 2, "mac", ASN1_SEQUENCE, ASN1_RAW }, /* 4 */ + { 2, "macSalt", ASN1_OCTET_STRING, ASN1_BODY }, /* 5 */ + { 2, "iterations", ASN1_INTEGER, ASN1_DEF|ASN1_BODY }, /* 6 */ + { 1, "end opt", ASN1_EOC, ASN1_END }, /* 7 */ + { 0, "exit", ASN1_EOC, ASN1_EXIT } +}; +#define PFX_AUTH_SAFE 2 +#define PFX_MAC 4 +#define PFX_SALT 5 +#define PFX_ITERATIONS 6 + +/** + * Parse an ASN.1 encoded PFX structure + */ +static bool parse_PFX(private_pkcs12_t *this, chunk_t blob) +{ + asn1_parser_t *parser; + int objectID; + chunk_t object, auth_safe, digest = chunk_empty, salt = chunk_empty, + data = chunk_empty; + hash_algorithm_t hash = HASH_UNKNOWN; + container_t *container = NULL; + u_int64_t iterations = 0; + bool success = FALSE; + + parser = asn1_parser_create(PFXObjects, blob); + + while (parser->iterate(parser, &objectID, &object)) + { + switch (objectID) + { + case PFX_AUTH_SAFE: + { + auth_safe = object; + break; + } + case PFX_MAC: + { + if (!parse_digest_info(object, parser->get_level(parser)+1, + &hash, &digest)) + { + goto end_parse; + } + break; + } + case PFX_SALT: + { + salt = object; + break; + } + case PFX_ITERATIONS: + { + iterations = object.len ? asn1_parse_integer_uint64(object) : 1; + break; + } + } + } + success = parser->success(parser); + +end_parse: + parser->destroy(parser); + if (!success) + { + return FALSE; + } + + success = FALSE; + DBG2(DBG_ASN, "-- > --"); + container = lib->creds->create(lib->creds, CRED_CONTAINER, CONTAINER_PKCS7, + BUILD_BLOB_ASN1_DER, auth_safe, BUILD_END); + if (container && container->get_data(container, &data)) + { + if (hash != HASH_UNKNOWN) + { + if (container->get_type(container) != CONTAINER_PKCS7_DATA) + { + goto end; + } + if (!verify_mac(hash, salt, iterations, data, digest)) + { + DBG1(DBG_ASN, " MAC verification of PKCS#12 container failed"); + goto end; + } + } + else + { + enumerator_t *enumerator; + auth_cfg_t *auth; + + if (container->get_type(container) != CONTAINER_PKCS7_SIGNED_DATA) + { + goto end; + } + enumerator = container->create_signature_enumerator(container); + if (!enumerator->enumerate(enumerator, &auth)) + { + DBG1(DBG_ASN, " signature verification of PKCS#12 container " + "failed"); + enumerator->destroy(enumerator); + goto end; + } + enumerator->destroy(enumerator); + } + success = parse_authenticated_safe(this, data); + } +end: + DBG2(DBG_ASN, "-- < --"); + DESTROY_IF(container); + chunk_free(&data); + return success; +} + +/** + * See header. + */ +pkcs12_t *pkcs12_decode(container_type_t type, va_list args) +{ + private_pkcs12_t *this; + chunk_t blob = chunk_empty; + + while (TRUE) + { + switch (va_arg(args, builder_part_t)) + { + case BUILD_BLOB_ASN1_DER: + blob = va_arg(args, chunk_t); + continue; + case BUILD_END: + break; + default: + return NULL; + } + break; + } + if (blob.len) + { + if (blob.len >= 2 && + blob.ptr[0] == ASN1_SEQUENCE && blob.ptr[1] == 0x80) + { /* looks like infinite length BER encoding, but we can't handle it. + */ + return NULL; + } + this = pkcs12_create(); + if (parse_PFX(this, blob)) + { + return &this->public; + } + destroy(this); + } + return NULL; +} diff --git a/src/libstrongswan/plugins/pkcs12/pkcs12_decode.h b/src/libstrongswan/plugins/pkcs12/pkcs12_decode.h new file mode 100644 index 000000000..e2998968f --- /dev/null +++ b/src/libstrongswan/plugins/pkcs12/pkcs12_decode.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2013 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup pkcs12_decode pkcs12_decode + * @{ @ingroup pkcs12 + */ + +#ifndef PKCS12_DECODE_H_ +#define PKCS12_DECODE_H_ + +#include +#include + +/** + * Load a PKCS#12 container. + * + * The argument list must contain a single BUILD_BLOB_ASN1_DER argument. + * + * @param type type of the container, CONTAINER_PKCS12 + * @param args builder_part_t argument list + * @return container, NULL on failure + */ +pkcs12_t *pkcs12_decode(container_type_t type, va_list args); + +#endif /** PKCS12_DECODE_H_ @}*/ diff --git a/src/libstrongswan/plugins/pkcs12/pkcs12_plugin.c b/src/libstrongswan/plugins/pkcs12/pkcs12_plugin.c new file mode 100644 index 000000000..ae0fb9093 --- /dev/null +++ b/src/libstrongswan/plugins/pkcs12/pkcs12_plugin.c @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2012 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pkcs12_plugin.h" + +#include + +#include "pkcs12_decode.h" + +typedef struct private_pkcs12_plugin_t private_pkcs12_plugin_t; + +/** + * private data of pkcs12_plugin + */ +struct private_pkcs12_plugin_t { + + /** + * public functions + */ + pkcs12_plugin_t public; +}; + +METHOD(plugin_t, get_name, char*, + private_pkcs12_plugin_t *this) +{ + return "pkcs12"; +} + +METHOD(plugin_t, get_features, int, + private_pkcs12_plugin_t *this, plugin_feature_t *features[]) +{ + static plugin_feature_t f[] = { + PLUGIN_REGISTER(CONTAINER_DECODE, pkcs12_decode, FALSE), + PLUGIN_PROVIDE(CONTAINER_DECODE, CONTAINER_PKCS12), + }; + *features = f; + return countof(f); +} + +METHOD(plugin_t, destroy, void, + private_pkcs12_plugin_t *this) +{ + free(this); +} + +/* + * see header file + */ +plugin_t *pkcs12_plugin_create() +{ + private_pkcs12_plugin_t *this; + + INIT(this, + .public = { + .plugin = { + .get_name = _get_name, + .get_features = _get_features, + .destroy = _destroy, + }, + }, + ); + + return &this->public.plugin; +} + diff --git a/src/libstrongswan/plugins/pkcs12/pkcs12_plugin.h b/src/libstrongswan/plugins/pkcs12/pkcs12_plugin.h new file mode 100644 index 000000000..3bd7f2df3 --- /dev/null +++ b/src/libstrongswan/plugins/pkcs12/pkcs12_plugin.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2013 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup pkcs12 pkcs12 + * @ingroup plugins + * + * @defgroup pkcs12_plugin pkcs12_plugin + * @{ @ingroup pkcs12 + */ + +#ifndef PKCS12_PLUGIN_H_ +#define PKCS12_PLUGIN_H_ + +#include + +typedef struct pkcs12_plugin_t pkcs12_plugin_t; + +/** + * Plugin providing PKCS#12 decoding functions + */ +struct pkcs12_plugin_t { + + /** + * Implements plugin interface. + */ + plugin_t plugin; +}; + +#endif /** PKCS12_PLUGIN_H_ @}*/ From d8be7d38bf9a0b4cf62e7713aa5b354144938620 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 12 Apr 2013 12:10:22 +0200 Subject: [PATCH 13/29] Also support 128-bit RC2 --- src/libstrongswan/crypto/pkcs5.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libstrongswan/crypto/pkcs5.c b/src/libstrongswan/crypto/pkcs5.c index ed2c41a0d..679d270d2 100644 --- a/src/libstrongswan/crypto/pkcs5.c +++ b/src/libstrongswan/crypto/pkcs5.c @@ -613,8 +613,9 @@ pkcs5_t *pkcs5_from_algorithmIdentifier(chunk_t blob, int level0) this->data.pbes1.hash = HASH_SHA1; break; case OID_PBE_SHA1_RC2_CBC_40: + case OID_PBE_SHA1_RC2_CBC_128: this->scheme = PKCS5_SCHEME_PKCS12; - this->keylen = 5; + this->keylen = (oid == OID_PBE_SHA1_RC2_CBC_40) ? 5 : 16; this->encr = ENCR_RC2_CBC; this->data.pbes1.hash = HASH_SHA1; break; From 1f2a34d6d8f497e37e856567857ba44bea431b8b Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 12 Apr 2013 12:48:04 +0200 Subject: [PATCH 14/29] Add support for untruncated HMAC-SHA-512 --- src/libstrongswan/crypto/hashers/hasher.c | 6 ++++++ src/libstrongswan/crypto/signers/signer.h | 4 +++- src/libstrongswan/plugins/af_alg/af_alg_signer.c | 1 + src/libstrongswan/plugins/hmac/hmac_plugin.c | 2 ++ src/libstrongswan/plugins/openssl/openssl_plugin.c | 1 + 5 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/libstrongswan/crypto/hashers/hasher.c b/src/libstrongswan/crypto/hashers/hasher.c index 4ed48ba36..679bb324e 100644 --- a/src/libstrongswan/crypto/hashers/hasher.c +++ b/src/libstrongswan/crypto/hashers/hasher.c @@ -141,6 +141,9 @@ hash_algorithm_t hasher_algorithm_from_integrity(integrity_algorithm_t alg, case AUTH_HMAC_SHA2_384_384: *length = 48; break; + case AUTH_HMAC_SHA2_512_512: + *length = 64; + break; default: break; } @@ -163,6 +166,7 @@ hash_algorithm_t hasher_algorithm_from_integrity(integrity_algorithm_t alg, case AUTH_HMAC_SHA2_384_384: return HASH_SHA384; case AUTH_HMAC_SHA2_512_256: + case AUTH_HMAC_SHA2_512_512: return HASH_SHA512; case AUTH_AES_CMAC_96: case AUTH_AES_128_GMAC: @@ -232,6 +236,8 @@ integrity_algorithm_t hasher_algorithm_to_integrity(hash_algorithm_t alg, { case 32: return AUTH_HMAC_SHA2_512_256; + case 64: + return AUTH_HMAC_SHA2_512_512; } break; case HASH_MD2: diff --git a/src/libstrongswan/crypto/signers/signer.h b/src/libstrongswan/crypto/signers/signer.h index 9b6bd479a..e0cf7eb5a 100644 --- a/src/libstrongswan/crypto/signers/signer.h +++ b/src/libstrongswan/crypto/signers/signer.h @@ -70,8 +70,10 @@ enum integrity_algorithm_t { AUTH_HMAC_SHA2_256_256 = 1027, /** SHA384 full length truncation variant, as used in TLS */ AUTH_HMAC_SHA2_384_384 = 1028, + /** SHA512 full length truncation variant */ + AUTH_HMAC_SHA2_512_512 = 1029, /** draft-kanno-ipsecme-camellia-xcbc, not yet assigned by IANA */ - AUTH_CAMELLIA_XCBC_96 = 1029, + AUTH_CAMELLIA_XCBC_96 = 1030, }; /** diff --git a/src/libstrongswan/plugins/af_alg/af_alg_signer.c b/src/libstrongswan/plugins/af_alg/af_alg_signer.c index d995b1351..6ee380633 100644 --- a/src/libstrongswan/plugins/af_alg/af_alg_signer.c +++ b/src/libstrongswan/plugins/af_alg/af_alg_signer.c @@ -64,6 +64,7 @@ static struct { {AUTH_HMAC_SHA2_384_192, "hmac(sha384)", 24, 48, }, {AUTH_HMAC_SHA2_384_384, "hmac(sha384)", 48, 48, }, {AUTH_HMAC_SHA2_512_256, "hmac(sha512)", 32, 64, }, + {AUTH_HMAC_SHA2_512_512, "hmac(sha512)", 64, 64, }, {AUTH_AES_XCBC_96, "xcbc(aes)", 12, 16, }, {AUTH_CAMELLIA_XCBC_96, "xcbc(camellia)", 12, 16, }, }; diff --git a/src/libstrongswan/plugins/hmac/hmac_plugin.c b/src/libstrongswan/plugins/hmac/hmac_plugin.c index f9c0c484b..43d5a0364 100644 --- a/src/libstrongswan/plugins/hmac/hmac_plugin.c +++ b/src/libstrongswan/plugins/hmac/hmac_plugin.c @@ -73,6 +73,8 @@ METHOD(plugin_t, get_features, int, PLUGIN_DEPENDS(HASHER, HASH_SHA384), PLUGIN_PROVIDE(SIGNER, AUTH_HMAC_SHA2_512_256), PLUGIN_DEPENDS(HASHER, HASH_SHA512), + PLUGIN_PROVIDE(SIGNER, AUTH_HMAC_SHA2_512_512), + PLUGIN_DEPENDS(HASHER, HASH_SHA512), }; *features = f; return countof(f); diff --git a/src/libstrongswan/plugins/openssl/openssl_plugin.c b/src/libstrongswan/plugins/openssl/openssl_plugin.c index fb7a6d587..97d57471d 100644 --- a/src/libstrongswan/plugins/openssl/openssl_plugin.c +++ b/src/libstrongswan/plugins/openssl/openssl_plugin.c @@ -307,6 +307,7 @@ METHOD(plugin_t, get_features, int, PLUGIN_PROVIDE(SIGNER, AUTH_HMAC_SHA2_384_192), PLUGIN_PROVIDE(SIGNER, AUTH_HMAC_SHA2_384_384), PLUGIN_PROVIDE(SIGNER, AUTH_HMAC_SHA2_512_256), + PLUGIN_PROVIDE(SIGNER, AUTH_HMAC_SHA2_512_512), #endif #endif /* OPENSSL_NO_HMAC */ #if OPENSSL_VERSION_NUMBER >= 0x1000100fL From 89d350f46a539c7dba1b4cb3337685b75032fec9 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 12 Apr 2013 18:28:17 +0200 Subject: [PATCH 15/29] charon-cmd: Request password for private keys --- src/charon-cmd/cmd/cmd_creds.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/charon-cmd/cmd/cmd_creds.c b/src/charon-cmd/cmd/cmd_creds.c index 98337db55..9a37edff5 100644 --- a/src/charon-cmd/cmd/cmd_creds.c +++ b/src/charon-cmd/cmd/cmd_creds.c @@ -84,6 +84,9 @@ static shared_key_t* callback_shared(private_cmd_creds_t *this, case SHARED_IKE: label = "Preshared Key: "; break; + case SHARED_PRIVATE_KEY_PASS: + label = "Password: "; + break; default: return NULL; } From f77d6e16d2f50645ab7099eb1ac72b2059da994f Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 12 Apr 2013 19:32:01 +0200 Subject: [PATCH 16/29] charon-cmd: match_me/match_other are optional in callback credentials --- src/charon-cmd/cmd/cmd_creds.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/charon-cmd/cmd/cmd_creds.c b/src/charon-cmd/cmd/cmd_creds.c index 9a37edff5..4626c6dbe 100644 --- a/src/charon-cmd/cmd/cmd_creds.c +++ b/src/charon-cmd/cmd/cmd_creds.c @@ -96,7 +96,14 @@ static shared_key_t* callback_shared(private_cmd_creds_t *this, return NULL; } this->prompted = TRUE; - *match_me = *match_other = ID_MATCH_PERFECT; + if (match_me) + { + *match_me = ID_MATCH_PERFECT; + } + if (match_other) + { + *match_other = ID_MATCH_PERFECT; + } return shared_key_create(type, chunk_clone(chunk_from_str(pwd))); } From abc04e6b3f50e036450b8444100e005a5ff5c754 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 12 Apr 2013 18:41:26 +0200 Subject: [PATCH 17/29] Remove pluto specific certificate types --- src/libstrongswan/credentials/certificates/certificate.c | 5 +---- src/libstrongswan/credentials/certificates/certificate.h | 4 ---- src/libstrongswan/plugins/pem/pem_plugin.c | 6 ------ 3 files changed, 1 insertion(+), 14 deletions(-) diff --git a/src/libstrongswan/credentials/certificates/certificate.c b/src/libstrongswan/credentials/certificates/certificate.c index bc4209ca7..b281c1669 100644 --- a/src/libstrongswan/credentials/certificates/certificate.c +++ b/src/libstrongswan/credentials/certificates/certificate.c @@ -18,7 +18,7 @@ #include #include -ENUM(certificate_type_names, CERT_ANY, CERT_PLUTO_CRL, +ENUM(certificate_type_names, CERT_ANY, CERT_GPG, "ANY", "X509", "X509_CRL", @@ -28,9 +28,6 @@ ENUM(certificate_type_names, CERT_ANY, CERT_PLUTO_CRL, "TRUSTED_PUBKEY", "PKCS10_REQUEST", "PGP", - "PLUTO_CERT", - "PLUTO_AC", - "PLUTO_CRL", ); ENUM(cert_validation_names, VALIDATION_GOOD, VALIDATION_REVOKED, diff --git a/src/libstrongswan/credentials/certificates/certificate.h b/src/libstrongswan/credentials/certificates/certificate.h index b7a88ffbd..d59126bd5 100644 --- a/src/libstrongswan/credentials/certificates/certificate.h +++ b/src/libstrongswan/credentials/certificates/certificate.h @@ -52,10 +52,6 @@ enum certificate_type_t { CERT_PKCS10_REQUEST, /** PGP certificate */ CERT_GPG, - /** Pluto cert_t (not a certificate_t), either x509 or PGP */ - CERT_PLUTO_CERT, - /** Pluto x509crl_t (not a certificate_t), certificate revocation list */ - CERT_PLUTO_CRL, }; /** diff --git a/src/libstrongswan/plugins/pem/pem_plugin.c b/src/libstrongswan/plugins/pem/pem_plugin.c index d74ab9ee3..9b0795f69 100644 --- a/src/libstrongswan/plugins/pem/pem_plugin.c +++ b/src/libstrongswan/plugins/pem/pem_plugin.c @@ -104,12 +104,6 @@ METHOD(plugin_t, get_features, int, PLUGIN_REGISTER(CERT_DECODE, pem_certificate_load, FALSE), PLUGIN_PROVIDE(CERT_DECODE, CERT_GPG), PLUGIN_DEPENDS(CERT_DECODE, CERT_GPG), - - /* pluto specific certificate formats */ - PLUGIN_REGISTER(CERT_DECODE, pem_certificate_load, FALSE), - PLUGIN_PROVIDE(CERT_DECODE, CERT_PLUTO_CERT), - PLUGIN_REGISTER(CERT_DECODE, pem_certificate_load, FALSE), - PLUGIN_PROVIDE(CERT_DECODE, CERT_PLUTO_CRL), }; *features = f; return countof(f); From 3bd498284ef27e7dfd41db06447918d544c6ea70 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 12 Apr 2013 19:00:15 +0200 Subject: [PATCH 18/29] PEM plugin loads PKCS#12 containers from (DER-encoded) files It is not actually able to handle PEM encoded PKCS#12 files produced by OpenSSL. --- src/libstrongswan/plugins/pem/pem_builder.c | 8 ++++++++ src/libstrongswan/plugins/pem/pem_builder.h | 11 +++++++++++ src/libstrongswan/plugins/pem/pem_plugin.c | 5 +++++ 3 files changed, 24 insertions(+) diff --git a/src/libstrongswan/plugins/pem/pem_builder.c b/src/libstrongswan/plugins/pem/pem_builder.c index 08e81b3c5..e9d55f3b8 100644 --- a/src/libstrongswan/plugins/pem/pem_builder.c +++ b/src/libstrongswan/plugins/pem/pem_builder.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2013 Tobias Brunner * Copyright (C) 2009 Martin Willi * Copyright (C) 2001-2008 Andreas Steffen * Hochschule fuer Technik Rapperswil @@ -564,3 +565,10 @@ certificate_t *pem_certificate_load(certificate_type_t type, va_list args) return pem_load(CRED_CERTIFICATE, type, args); } +/** + * Container PEM loader. + */ +container_t *pem_container_load(container_type_t type, va_list args) +{ + return pem_load(CRED_CONTAINER, type, args); +} diff --git a/src/libstrongswan/plugins/pem/pem_builder.h b/src/libstrongswan/plugins/pem/pem_builder.h index 87f5a2c69..b1bfc6d4d 100644 --- a/src/libstrongswan/plugins/pem/pem_builder.h +++ b/src/libstrongswan/plugins/pem/pem_builder.h @@ -1,4 +1,5 @@ /* + * Copyright (C) 2013 Tobias Brunner * Copyright (C) 2009 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -25,6 +26,7 @@ #include #include #include +#include /** * Load PEM encoded private keys. @@ -53,5 +55,14 @@ public_key_t *pem_public_key_load(key_type_t type, va_list args); */ certificate_t *pem_certificate_load(certificate_type_t type, va_list args); +/** + * Build PEM encoded containers. + * + * @param type type of the container + * @param args builder_part_t argument list + * @return container, NULL if failed + */ +container_t *pem_container_load(container_type_t type, va_list args); + #endif /** PEM_BUILDER_H_ @}*/ diff --git a/src/libstrongswan/plugins/pem/pem_plugin.c b/src/libstrongswan/plugins/pem/pem_plugin.c index 9b0795f69..e7edd7b89 100644 --- a/src/libstrongswan/plugins/pem/pem_plugin.c +++ b/src/libstrongswan/plugins/pem/pem_plugin.c @@ -104,6 +104,11 @@ METHOD(plugin_t, get_features, int, PLUGIN_REGISTER(CERT_DECODE, pem_certificate_load, FALSE), PLUGIN_PROVIDE(CERT_DECODE, CERT_GPG), PLUGIN_DEPENDS(CERT_DECODE, CERT_GPG), + + /* container PEM decoding */ + PLUGIN_REGISTER(CONTAINER_DECODE, pem_container_load, FALSE), + PLUGIN_PROVIDE(CONTAINER_DECODE, CONTAINER_PKCS12), + PLUGIN_DEPENDS(CONTAINER_DECODE, CONTAINER_PKCS12), }; *features = f; return countof(f); From 02116fdc2dd312a86d48e9dcf409d6cdfcf6822b Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 12 Apr 2013 19:30:03 +0200 Subject: [PATCH 19/29] charon-cmd: Add support for PKCS#12 files --- configure.in | 4 +-- src/charon-cmd/cmd/cmd_connection.c | 1 + src/charon-cmd/cmd/cmd_creds.c | 44 ++++++++++++++++++++++++++++- src/charon-cmd/cmd/cmd_options.c | 4 +++ src/charon-cmd/cmd/cmd_options.h | 4 +++ 5 files changed, 54 insertions(+), 3 deletions(-) diff --git a/configure.in b/configure.in index c858a294c..87466f2db 100644 --- a/configure.in +++ b/configure.in @@ -967,9 +967,9 @@ ADD_PLUGIN([revocation], [s charon nm cmd]) ADD_PLUGIN([constraints], [s charon nm cmd]) ADD_PLUGIN([pubkey], [s charon cmd]) ADD_PLUGIN([pkcs1], [s charon openac scepclient pki scripts manager medsrv attest nm cmd]) -ADD_PLUGIN([pkcs7], [s scepclient pki scripts]) +ADD_PLUGIN([pkcs7], [s scepclient pki scripts cmd]) ADD_PLUGIN([pkcs8], [s charon openac scepclient pki scripts manager medsrv attest nm cmd]) -ADD_PLUGIN([pkcs12], [s charon scepclient pki scripts]) +ADD_PLUGIN([pkcs12], [s charon scepclient pki scripts cmd]) ADD_PLUGIN([pgp], [s charon]) ADD_PLUGIN([dnskey], [s charon]) ADD_PLUGIN([sshkey], [s charon nm cmd]) diff --git a/src/charon-cmd/cmd/cmd_connection.c b/src/charon-cmd/cmd/cmd_connection.c index 9c25df907..e48f54887 100644 --- a/src/charon-cmd/cmd/cmd_connection.c +++ b/src/charon-cmd/cmd/cmd_connection.c @@ -391,6 +391,7 @@ METHOD(cmd_connection_t, handle, bool, break; case CMD_OPT_RSA: case CMD_OPT_AGENT: + case CMD_OPT_PKCS12: this->key_seen = TRUE; break; case CMD_OPT_LOCAL_TS: diff --git a/src/charon-cmd/cmd/cmd_creds.c b/src/charon-cmd/cmd/cmd_creds.c index 4626c6dbe..526ff7c9c 100644 --- a/src/charon-cmd/cmd/cmd_creds.c +++ b/src/charon-cmd/cmd/cmd_creds.c @@ -22,6 +22,7 @@ #include #include +#include #include typedef struct private_cmd_creds_t private_cmd_creds_t; @@ -70,6 +71,7 @@ static shared_key_t* callback_shared(private_cmd_creds_t *this, identification_t *me, identification_t *other, id_match_t *match_me, id_match_t *match_other) { + shared_key_t *shared; char *label, *pwd; if (this->prompted) @@ -104,7 +106,10 @@ static shared_key_t* callback_shared(private_cmd_creds_t *this, { *match_other = ID_MATCH_PERFECT; } - return shared_key_create(type, chunk_clone(chunk_from_str(pwd))); + shared = shared_key_create(type, chunk_clone(chunk_from_str(pwd))); + /* cache password in case it is required more than once */ + this->creds->add_shared(this->creds, shared, NULL); + return shared->get_ref(shared); } /** @@ -182,6 +187,40 @@ static void load_agent(private_cmd_creds_t *this) this->creds->add_key(this->creds, privkey); } +/** + * Load a PKCS#12 file from path + */ +static void load_pkcs12(private_cmd_creds_t *this, char *path) +{ + enumerator_t *enumerator; + certificate_t *cert; + private_key_t *key; + container_t *container; + pkcs12_t *pkcs12; + + container = lib->creds->create(lib->creds, CRED_CONTAINER, CONTAINER_PKCS12, + BUILD_FROM_FILE, path, BUILD_END); + if (!container) + { + DBG1(DBG_CFG, "loading PKCS#12 file '%s' failed", path); + exit(1); + } + pkcs12 = (pkcs12_t*)container; + enumerator = pkcs12->create_cert_enumerator(pkcs12); + while (enumerator->enumerate(enumerator, &cert)) + { + this->creds->add_cert(this->creds, TRUE, cert->get_ref(cert)); + } + enumerator->destroy(enumerator); + enumerator = pkcs12->create_key_enumerator(pkcs12); + while (enumerator->enumerate(enumerator, &key)) + { + this->creds->add_key(this->creds, key->get_ref(key)); + } + enumerator->destroy(enumerator); + container->destroy(container); +} + METHOD(cmd_creds_t, handle, bool, private_cmd_creds_t *this, cmd_option_type_t opt, char *arg) { @@ -193,6 +232,9 @@ METHOD(cmd_creds_t, handle, bool, case CMD_OPT_RSA: load_key(this, KEY_RSA, arg); break; + case CMD_OPT_PKCS12: + load_pkcs12(this, arg); + break; case CMD_OPT_IDENTITY: this->identity = arg; break; diff --git a/src/charon-cmd/cmd/cmd_options.c b/src/charon-cmd/cmd/cmd_options.c index 06d0996cc..e7dbff7e0 100644 --- a/src/charon-cmd/cmd/cmd_options.c +++ b/src/charon-cmd/cmd/cmd_options.c @@ -38,6 +38,10 @@ cmd_option_t cmd_options[CMD_OPT_COUNT] = { "trusted certificate, for authentication or trust chain validation", {}}, { CMD_OPT_RSA, "rsa", required_argument, "path", "RSA private key to use for authentication", {}}, + { CMD_OPT_PKCS12, "p12", required_argument, "path", + "PKCS#12 file with private key and certificates to use for ", { + "authentication and trust chain validation" + }}, { CMD_OPT_AGENT, "agent", optional_argument, "socket", "use SSH agent for authentication. If socket is not specified", { "it is read from the SSH_AUTH_SOCK environment variable", diff --git a/src/charon-cmd/cmd/cmd_options.h b/src/charon-cmd/cmd/cmd_options.h index a14896f83..7a6080f3a 100644 --- a/src/charon-cmd/cmd/cmd_options.h +++ b/src/charon-cmd/cmd/cmd_options.h @@ -1,4 +1,7 @@ /* + * Copyright (C) 2013 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * * Copyright (C) 2013 Martin Willi * Copyright (C) 2013 revosec AG * @@ -35,6 +38,7 @@ enum cmd_option_type_t { CMD_OPT_REMOTE_IDENTITY, CMD_OPT_CERT, CMD_OPT_RSA, + CMD_OPT_PKCS12, CMD_OPT_AGENT, CMD_OPT_LOCAL_TS, CMD_OPT_REMOTE_TS, From 651d5ab8e72f2dd2ce40544b59d974007afdc880 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 17 Apr 2013 11:35:18 +0200 Subject: [PATCH 20/29] openssl: Properly cleanup OpenSSL library --- src/libstrongswan/plugins/openssl/openssl_plugin.c | 9 +++++++-- src/libstrongswan/utils/leak_detective.c | 7 ------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/libstrongswan/plugins/openssl/openssl_plugin.c b/src/libstrongswan/plugins/openssl/openssl_plugin.c index 97d57471d..2371008fd 100644 --- a/src/libstrongswan/plugins/openssl/openssl_plugin.c +++ b/src/libstrongswan/plugins/openssl/openssl_plugin.c @@ -14,6 +14,7 @@ * for more details. */ +#include #include #include #include @@ -445,11 +446,15 @@ METHOD(plugin_t, get_features, int, METHOD(plugin_t, destroy, void, private_openssl_plugin_t *this) { + CONF_modules_free(); + OBJ_cleanup(); + EVP_cleanup(); #ifndef OPENSSL_NO_ENGINE ENGINE_cleanup(); #endif /* OPENSSL_NO_ENGINE */ - EVP_cleanup(); - CONF_modules_free(); + CRYPTO_cleanup_all_ex_data(); + ERR_remove_thread_state(NULL); + ERR_free_strings(); threading_cleanup(); diff --git a/src/libstrongswan/utils/leak_detective.c b/src/libstrongswan/utils/leak_detective.c index 26e3e43fc..4f3c9f78b 100644 --- a/src/libstrongswan/utils/leak_detective.c +++ b/src/libstrongswan/utils/leak_detective.c @@ -475,13 +475,6 @@ char *whitelist[] = { "Curl_client_write", /* ClearSilver */ "nerr_init", - /* OpenSSL */ - "RSA_new_method", - "DH_new_method", - "ENGINE_load_builtin_engines", - "OPENSSL_config", - "ecdsa_check", - "ERR_put_error", /* libgcrypt */ "gcry_control", "gcry_check_version", From 780900ab0e747a4dbdf70bfa2a40f1cd19581744 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 17 Apr 2013 11:43:06 +0200 Subject: [PATCH 21/29] openssl: Add PKCS#12 parsing via OpenSSL --- src/libstrongswan/plugins/openssl/Makefile.am | 1 + .../plugins/openssl/openssl_pkcs12.c | 266 ++++++++++++++++++ .../plugins/openssl/openssl_pkcs12.h | 37 +++ .../plugins/openssl/openssl_plugin.c | 3 + 4 files changed, 307 insertions(+) create mode 100644 src/libstrongswan/plugins/openssl/openssl_pkcs12.c create mode 100644 src/libstrongswan/plugins/openssl/openssl_pkcs12.h diff --git a/src/libstrongswan/plugins/openssl/Makefile.am b/src/libstrongswan/plugins/openssl/Makefile.am index 0ca27983f..7ae5ea43e 100644 --- a/src/libstrongswan/plugins/openssl/Makefile.am +++ b/src/libstrongswan/plugins/openssl/Makefile.am @@ -24,6 +24,7 @@ libstrongswan_openssl_la_SOURCES = \ openssl_x509.c openssl_x509.h \ openssl_crl.c openssl_crl.h \ openssl_pkcs7.c openssl_pkcs7.h \ + openssl_pkcs12.c openssl_pkcs12.h \ openssl_rng.c openssl_rng.h \ openssl_hmac.c openssl_hmac.h \ openssl_gcm.c openssl_gcm.h diff --git a/src/libstrongswan/plugins/openssl/openssl_pkcs12.c b/src/libstrongswan/plugins/openssl/openssl_pkcs12.c new file mode 100644 index 000000000..d16b2cc05 --- /dev/null +++ b/src/libstrongswan/plugins/openssl/openssl_pkcs12.c @@ -0,0 +1,266 @@ +/* + * Copyright (C) 2013 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#define _GNU_SOURCE /* for asprintf() */ +#include +#include + +#include "openssl_pkcs12.h" +#include "openssl_util.h" + +#include +#include + +typedef struct private_pkcs12_t private_pkcs12_t; + +/** + * Private data of a pkcs12_t object. + */ +struct private_pkcs12_t { + + /** + * Public pkcs12_t interface. + */ + pkcs12_t public; + + /** + * OpenSSL PKCS#12 structure + */ + PKCS12 *p12; + + /** + * Credentials contained in container + */ + mem_cred_t *creds; +}; + +/** + * Decode certificate and add it to our credential set + */ +static bool add_cert(private_pkcs12_t *this, X509 *x509) +{ + certificate_t *cert = NULL; + chunk_t encoding; + + if (!x509) + { /* no certificate is ok */ + return TRUE; + } + encoding = openssl_i2chunk(X509, x509); + if (encoding.ptr) + { + cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, + BUILD_BLOB_ASN1_DER, encoding, + BUILD_END); + if (cert) + { + this->creds->add_cert(this->creds, FALSE, cert); + } + } + chunk_free(&encoding); + X509_free(x509); + return cert != NULL; +} + +/** + * Add CA certificates to our credential set + */ +static bool add_cas(private_pkcs12_t *this, STACK_OF(X509) *cas) +{ + bool success = TRUE; + int i; + + if (!cas) + { /* no CAs is ok */ + return TRUE; + } + for (i = 0; i < sk_X509_num(cas); i++) + { + if (!add_cert(this, sk_X509_value(cas, i))) + { /* continue to free all X509 objects */ + success = FALSE; + } + } + sk_X509_free(cas); + return success; +} + +/** + * Decode private key and add it to our credential set + */ +static bool add_key(private_pkcs12_t *this, EVP_PKEY *private) +{ + private_key_t *key = NULL; + chunk_t encoding; + key_type_t type; + + if (!private) + { /* no private key is ok */ + return TRUE; + } + switch (EVP_PKEY_type(private->type)) + { + case EVP_PKEY_RSA: + type = KEY_RSA; + break; + case EVP_PKEY_EC: + type = KEY_ECDSA; + break; + default: + EVP_PKEY_free(private); + return FALSE; + } + encoding = openssl_i2chunk(PrivateKey, private); + if (encoding.ptr) + { + key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, type, + BUILD_BLOB_ASN1_DER, encoding, + BUILD_END); + if (key) + { + this->creds->add_key(this->creds, key); + } + } + chunk_clear(&encoding); + EVP_PKEY_free(private); + return key != NULL; +} + +/** + * Decrypt PKCS#12 file and unpack credentials + */ +static bool decrypt_and_unpack(private_pkcs12_t *this) +{ + enumerator_t *enumerator; + shared_key_t *shared; + STACK_OF(X509) *cas = NULL; + EVP_PKEY *private; + X509 *cert; + chunk_t key; + char *password; + bool success = FALSE; + + enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr, + SHARED_PRIVATE_KEY_PASS, NULL, NULL); + while (enumerator->enumerate(enumerator, &shared, NULL, NULL)) + { + key = shared->get_key(shared); + if (!key.ptr || asprintf(&password, "%.*s", (int)key.len, key.ptr) < 0) + { + password = NULL; + } + if (PKCS12_parse(this->p12, password, &private, &cert, &cas)) + { + success = add_key(this, private); + success &= add_cert(this, cert); + success &= add_cas(this, cas); + free(password); + break; + } + free(password); + } + enumerator->destroy(enumerator); + return success; +} + +METHOD(container_t, get_type, container_type_t, + private_pkcs12_t *this) +{ + return CONTAINER_PKCS12; +} + +METHOD(pkcs12_t, create_cert_enumerator, enumerator_t*, + private_pkcs12_t *this) +{ + return this->creds->set.create_cert_enumerator(&this->creds->set, CERT_ANY, + KEY_ANY, NULL, FALSE); +} + +METHOD(pkcs12_t, create_key_enumerator, enumerator_t*, + private_pkcs12_t *this) +{ + return this->creds->set.create_private_enumerator(&this->creds->set, + KEY_ANY, NULL); +} + +METHOD(container_t, destroy, void, + private_pkcs12_t *this) +{ + if (this->p12) + { + PKCS12_free(this->p12); + } + this->creds->destroy(this->creds); + free(this); +} + +/** + * Parse a PKCS#12 container + */ +static pkcs12_t *parse(chunk_t blob) +{ + private_pkcs12_t *this; + BIO *bio; + + INIT(this, + .public = { + .container = { + .get_type = _get_type, + .create_signature_enumerator = (void*)enumerator_create_empty, + .get_data = (void*)return_false, + .get_encoding = (void*)return_false, + .destroy = _destroy, + }, + .create_cert_enumerator = _create_cert_enumerator, + .create_key_enumerator = _create_key_enumerator, + }, + .creds = mem_cred_create(), + ); + + bio = BIO_new_mem_buf(blob.ptr, blob.len); + this->p12 = d2i_PKCS12_bio(bio, NULL); + BIO_free(bio); + + if (!this->p12 || !decrypt_and_unpack(this)) + { + destroy(this); + return NULL; + } + return &this->public; +} + +/* + * Defined in header + */ +pkcs12_t *openssl_pkcs12_load(container_type_t type, va_list args) +{ + chunk_t blob = chunk_empty; + + while (TRUE) + { + switch (va_arg(args, builder_part_t)) + { + case BUILD_BLOB_ASN1_DER: + blob = va_arg(args, chunk_t); + continue; + case BUILD_END: + break; + default: + return NULL; + } + break; + } + return blob.len ? parse(blob) : NULL; +} diff --git a/src/libstrongswan/plugins/openssl/openssl_pkcs12.h b/src/libstrongswan/plugins/openssl/openssl_pkcs12.h new file mode 100644 index 000000000..5c3e5933d --- /dev/null +++ b/src/libstrongswan/plugins/openssl/openssl_pkcs12.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2013 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup openssl_pkcs12 openssl_pkcs12 + * @{ @ingroup openssl_p + */ + +#ifndef OPENSSL_PKCS12_H_ +#define OPENSSL_PKCS12_H_ + +#include + +/** + * Load a PKCS#12 container. + * + * The argument list must contain a single BUILD_BLOB_ASN1_DER argument. + * + * @param type type of the container, CONTAINER_PKCS12 + * @param args builder_part_t argument list + * @return container, NULL on failure + */ +pkcs12_t *openssl_pkcs12_load(container_type_t type, va_list args); + +#endif /** OPENSSL_PKCS12_H_ @}*/ diff --git a/src/libstrongswan/plugins/openssl/openssl_plugin.c b/src/libstrongswan/plugins/openssl/openssl_plugin.c index 2371008fd..174f94e05 100644 --- a/src/libstrongswan/plugins/openssl/openssl_plugin.c +++ b/src/libstrongswan/plugins/openssl/openssl_plugin.c @@ -42,6 +42,7 @@ #include "openssl_x509.h" #include "openssl_crl.h" #include "openssl_pkcs7.h" +#include "openssl_pkcs12.h" #include "openssl_rng.h" #include "openssl_hmac.h" #include "openssl_gcm.h" @@ -394,6 +395,8 @@ METHOD(plugin_t, get_features, int, PLUGIN_PROVIDE(CONTAINER_DECODE, CONTAINER_PKCS7), #endif /* OPENSSL_NO_CMS */ #endif /* OPENSSL_VERSION_NUMBER */ + PLUGIN_REGISTER(CONTAINER_DECODE, openssl_pkcs12_load, TRUE), + PLUGIN_PROVIDE(CONTAINER_DECODE, CONTAINER_PKCS12), #ifndef OPENSSL_NO_ECDH /* EC DH groups */ PLUGIN_REGISTER(DH, openssl_ec_diffie_hellman_create), From 3ee2af97bfd77927fe2a765821030a8419f2f072 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 17 Apr 2013 13:00:51 +0200 Subject: [PATCH 22/29] openssl: Don't use deprecated CRYPTO_set_id_callback() with OpenSSL >= 1.0.0 --- .../plugins/openssl/openssl_plugin.c | 46 ++++++++++++------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/src/libstrongswan/plugins/openssl/openssl_plugin.c b/src/libstrongswan/plugins/openssl/openssl_plugin.c index 174f94e05..7c880402b 100644 --- a/src/libstrongswan/plugins/openssl/openssl_plugin.c +++ b/src/libstrongswan/plugins/openssl/openssl_plugin.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Tobias Brunner + * Copyright (C) 2008-2013 Tobias Brunner * Copyright (C) 2008 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -142,6 +142,13 @@ static unsigned long id_function(void) return 1 + (unsigned long)thread_current_id(); } +#if OPENSSL_VERSION_NUMBER >= 0x1000000fL +static void threadid_function(CRYPTO_THREADID *threadid) +{ + CRYPTO_THREADID_set_numeric(threadid, id_function()); +} +#endif /* OPENSSL_VERSION_NUMBER */ + /** * initialize OpenSSL for multi-threaded use */ @@ -149,7 +156,12 @@ static void threading_init() { int i, num_locks; +#if OPENSSL_VERSION_NUMBER >= 0x1000000fL + CRYPTO_THREADID_set_callback(threadid_function); +#else CRYPTO_set_id_callback(id_function); +#endif + CRYPTO_set_locking_callback(locking_function); CRYPTO_set_dynlock_create_callback(create_function); @@ -164,6 +176,22 @@ static void threading_init() } } +/** + * cleanup OpenSSL threading locks + */ +static void threading_cleanup() +{ + int i, num_locks; + + num_locks = CRYPTO_num_locks(); + for (i = 0; i < num_locks; i++) + { + mutex[i]->destroy(mutex[i]); + } + free(mutex); + mutex = NULL; +} + /** * Seed the OpenSSL RNG, if required */ @@ -193,22 +221,6 @@ static bool seed_rng() return TRUE; } -/** - * cleanup OpenSSL threading locks - */ -static void threading_cleanup() -{ - int i, num_locks; - - num_locks = CRYPTO_num_locks(); - for (i = 0; i < num_locks; i++) - { - mutex[i]->destroy(mutex[i]); - } - free(mutex); - mutex = NULL; -} - METHOD(plugin_t, get_name, char*, private_openssl_plugin_t *this) { From 904390e88742ff1f3ff0fa2c533078c7b5ba9b18 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 17 Apr 2013 13:16:20 +0200 Subject: [PATCH 23/29] openssl: Cleanup thread specific error buffer --- .../plugins/openssl/openssl_plugin.c | 43 ++++++++++++++++--- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/src/libstrongswan/plugins/openssl/openssl_plugin.c b/src/libstrongswan/plugins/openssl/openssl_plugin.c index 7c880402b..5d2074144 100644 --- a/src/libstrongswan/plugins/openssl/openssl_plugin.c +++ b/src/libstrongswan/plugins/openssl/openssl_plugin.c @@ -29,6 +29,7 @@ #include #include #include +#include #include "openssl_util.h" #include "openssl_crypter.h" #include "openssl_hasher.h" @@ -132,17 +133,47 @@ static void destroy_function(struct CRYPTO_dynlock_value *lock, free(lock); } +/** + * Thread-local value used to cleanup thread-specific error buffers + */ +static thread_value_t *cleanup; + +/** + * Called when a thread is destroyed. Avoid recursion by setting the thread id + * explicitly. + */ +static void cleanup_thread(void *arg) +{ +#if OPENSSL_VERSION_NUMBER >= 0x1000000fL + CRYPTO_THREADID tid; + + CRYPTO_THREADID_set_numeric(&tid, (u_long)(uintptr_t)arg); + ERR_remove_thread_state(&tid); +#else + ERR_remove_state((u_long)(uintptr_t)arg); +#endif +} + /** * Thread-ID callback function */ -static unsigned long id_function(void) +static u_long id_function(void) { + u_long id; + /* ensure the thread ID is never zero, otherwise OpenSSL might try to * acquire locks recursively */ - return 1 + (unsigned long)thread_current_id(); + id = 1 + (u_long)thread_current_id(); + + /* cleanup a thread's state later if OpenSSL interacted with it */ + cleanup->set(cleanup, (void*)(uintptr_t)id); + return id; } #if OPENSSL_VERSION_NUMBER >= 0x1000000fL +/** + * Callback for thread ID + */ static void threadid_function(CRYPTO_THREADID *threadid) { CRYPTO_THREADID_set_numeric(threadid, id_function()); @@ -156,6 +187,8 @@ static void threading_init() { int i, num_locks; + cleanup = thread_value_create(cleanup_thread); + #if OPENSSL_VERSION_NUMBER >= 0x1000000fL CRYPTO_THREADID_set_callback(threadid_function); #else @@ -190,6 +223,8 @@ static void threading_cleanup() } free(mutex); mutex = NULL; + + cleanup->destroy(cleanup); } /** @@ -468,10 +503,8 @@ METHOD(plugin_t, destroy, void, ENGINE_cleanup(); #endif /* OPENSSL_NO_ENGINE */ CRYPTO_cleanup_all_ex_data(); - ERR_remove_thread_state(NULL); - ERR_free_strings(); - threading_cleanup(); + ERR_free_strings(); free(this); } From 7971278c92ffa930ca808435e176810702b95568 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 17 Apr 2013 13:49:13 +0200 Subject: [PATCH 24/29] stroke: Load credentials from PKCS#12 files (P12 token) --- man/ipsec.secrets.5.in | 21 +++- src/libcharon/plugins/stroke/stroke_cred.c | 107 ++++++++++++++++++--- 2 files changed, 109 insertions(+), 19 deletions(-) diff --git a/man/ipsec.secrets.5.in b/man/ipsec.secrets.5.in index 319d4856b..ee20c9670 100644 --- a/man/ipsec.secrets.5.in +++ b/man/ipsec.secrets.5.in @@ -91,6 +91,9 @@ defines an RSA private key .B ECDSA defines an ECDSA private key .TP +.B P12 +defines a PKCS#12 container +.TP .B EAP defines EAP credentials .TP @@ -133,16 +136,26 @@ Similarly, a character sequence beginning with .B 0s is interpreted as Base64 encoded binary data. .TP -.B [ ] : RSA [ | %prompt ] +.B : RSA [ | %prompt ] .TQ -.B [ ] : ECDSA [ | %prompt ] +.B : ECDSA [ | %prompt ] For the private key file both absolute paths or paths relative to \fI/etc/ipsec.d/private\fP are accepted. If the private key file is encrypted, the \fIpassphrase\fP must be defined. Instead of a passphrase .B %prompt -can be used which then causes the daemons to ask the user for the password +can be used which then causes the daemon to ask the user for the password whenever it is required to decrypt the key. .TP +.B : P12 [ | %prompt ] +For the PKCS#12 file both absolute paths or paths relative to +\fI/etc/ipsec.d/private\fP are accepted. If the container is +encrypted, the \fIpassphrase\fP must be defined. Instead of a passphrase +.B %prompt +can be used which then causes the daemon to ask the user for the password +whenever it is required to decrypt the container. Private keys, client and CA +certificates are extracted from the container. To use such a client certificate +in a connection set leftid to one of the subjects of the certificate. +.TP .B : EAP The format of \fIsecret\fP is the same as that of \fBPSK\fP secrets. .br @@ -165,7 +178,7 @@ key. The slot number defines the slot on the token, the module name refers to the module name defined in strongswan.conf(5). Instead of specifying the pin code statically, .B %prompt -can be specified, which causes the daemons to ask the user for the pin code. +can be specified, which causes the daemon to ask the user for the pin code. .LP .SH FILES diff --git a/src/libcharon/plugins/stroke/stroke_cred.c b/src/libcharon/plugins/stroke/stroke_cred.c index f24082ee3..703410016 100644 --- a/src/libcharon/plugins/stroke/stroke_cred.c +++ b/src/libcharon/plugins/stroke/stroke_cred.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2012 Tobias Brunner + * Copyright (C) 2008-2013 Tobias Brunner * Copyright (C) 2008 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -72,7 +73,7 @@ struct private_stroke_cred_t { /** * ignore missing CA basic constraint (i.e. treat all certificates in - * ipsec.conf ca sections and ipsec.d/cacert as CA certificates) + * ipsec.conf ca sections and ipsec.d/cacerts as CA certificates) */ bool force_ca_cert; @@ -225,7 +226,7 @@ METHOD(stroke_cred_t, load_ca, certificate_t*, cert->destroy(cert); return NULL; } - DBG1(DBG_CFG, " loaded ca certificate \"%Y\" from '%s", + DBG1(DBG_CFG, " loaded ca certificate \"%Y\" from '%s'", cert->get_subject(cert), filename); return this->creds->add_cert_ref(this->creds, TRUE, cert); } @@ -821,15 +822,14 @@ static bool load_pin(mem_cred_t *secrets, chunk_t line, int line_nr, } /** - * Load a private key + * Load a private key or PKCS#12 container from a file */ -static bool load_private(mem_cred_t *secrets, chunk_t line, int line_nr, - FILE *prompt, key_type_t key_type) +static bool load_from_file(chunk_t line, int line_nr, FILE *prompt, + char *path, int type, int subtype, + void **result) { - char path[PATH_MAX]; chunk_t filename; chunk_t secret = chunk_empty; - private_key_t *key; err_t ugh = extract_value(&filename, &line); @@ -846,12 +846,12 @@ static bool load_private(mem_cred_t *secrets, chunk_t line, int line_nr, if (*filename.ptr == '/') { /* absolute path name */ - snprintf(path, sizeof(path), "%.*s", (int)filename.len, filename.ptr); + snprintf(path, PATH_MAX, "%.*s", (int)filename.len, filename.ptr); } else { /* relative path name */ - snprintf(path, sizeof(path), "%s/%.*s", PRIVATE_KEY_DIR, + snprintf(path, PATH_MAX, "%s/%.*s", PRIVATE_KEY_DIR, (int)filename.len, filename.ptr); } @@ -877,6 +877,7 @@ static bool load_private(mem_cred_t *secrets, chunk_t line, int line_nr, free(secret.ptr); if (!prompt) { + *result = NULL; return TRUE; } /* use callback credential set to prompt for the passphrase */ @@ -886,8 +887,8 @@ static bool load_private(mem_cred_t *secrets, chunk_t line, int line_nr, cb = callback_cred_create_shared((void*)passphrase_cb, &pp_data); lib->credmgr->add_local_set(lib->credmgr, &cb->set, FALSE); - key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, key_type, - BUILD_FROM_FILE, path, BUILD_END); + *result = lib->creds->create(lib->creds, type, subtype, + BUILD_FROM_FILE, path, BUILD_END); lib->credmgr->remove_local_set(lib->credmgr, &cb->set); cb->destroy(cb); @@ -903,12 +904,29 @@ static bool load_private(mem_cred_t *secrets, chunk_t line, int line_nr, mem->add_shared(mem, shared, NULL); lib->credmgr->add_local_set(lib->credmgr, &mem->set, FALSE); - key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, key_type, - BUILD_FROM_FILE, path, BUILD_END); + *result = lib->creds->create(lib->creds, type, subtype, + BUILD_FROM_FILE, path, BUILD_END); lib->credmgr->remove_local_set(lib->credmgr, &mem->set); mem->destroy(mem); } + return TRUE; +} + +/** + * Load a private key + */ +static bool load_private(mem_cred_t *secrets, chunk_t line, int line_nr, + FILE *prompt, key_type_t key_type) +{ + char path[PATH_MAX]; + private_key_t *key; + + if (!load_from_file(line, line_nr, prompt, path, CRED_PRIVATE_KEY, + key_type, (void**)&key)) + { + return FALSE; + } if (key) { DBG1(DBG_CFG, " loaded %N private key from '%s'", @@ -922,6 +940,58 @@ static bool load_private(mem_cred_t *secrets, chunk_t line, int line_nr, return TRUE; } +/** + * Load a PKCS#12 container + */ +static bool load_pkcs12(mem_cred_t *secrets, chunk_t line, int line_nr, + FILE *prompt) +{ + enumerator_t *enumerator; + char path[PATH_MAX]; + certificate_t *cert; + private_key_t *key; + pkcs12_t *pkcs12; + + if (!load_from_file(line, line_nr, prompt, path, CRED_CONTAINER, + CONTAINER_PKCS12, (void**)&pkcs12)) + { + return FALSE; + } + if (!pkcs12) + { + DBG1(DBG_CFG, " loading credentials from '%s' failed", path); + return TRUE; + } + enumerator = pkcs12->create_cert_enumerator(pkcs12); + while (enumerator->enumerate(enumerator, &cert)) + { + x509_t *x509 = (x509_t*)cert; + + if (x509->get_flags(x509) & X509_CA) + { + DBG1(DBG_CFG, " loaded ca certificate \"%Y\" from '%s'", + cert->get_subject(cert), path); + } + else + { + DBG1(DBG_CFG, " loaded certificate \"%Y\" from '%s'", + cert->get_subject(cert), path); + } + secrets->add_cert(secrets, TRUE, cert->get_ref(cert)); + } + enumerator->destroy(enumerator); + enumerator = pkcs12->create_key_enumerator(pkcs12); + while (enumerator->enumerate(enumerator, &key)) + { + DBG1(DBG_CFG, " loaded %N private key from '%s'", + key_type_names, key->get_type(key), path); + secrets->add_key(secrets, key->get_ref(key)); + } + enumerator->destroy(enumerator); + pkcs12->container.destroy(&pkcs12->container); + return TRUE; +} + /** * Load a shared key */ @@ -1140,6 +1210,13 @@ static void load_secrets(private_stroke_cred_t *this, mem_cred_t *secrets, break; } } + else if (match("P12", &token)) + { + if (!load_pkcs12(secrets, line, line_nr, prompt)) + { + break; + } + } else if (match("PIN", &token)) { if (!load_pin(secrets, line, line_nr, prompt)) @@ -1160,7 +1237,7 @@ static void load_secrets(private_stroke_cred_t *this, mem_cred_t *secrets, else { DBG1(DBG_CFG, "line %d: token must be either " - "RSA, ECDSA, PSK, EAP, XAUTH or PIN", line_nr); + "RSA, ECDSA, P12, PIN, PSK, EAP, XAUTH or NTLM", line_nr); break; } } From e240b03e68bff8c834e271238037e149d5e1379d Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 17 Apr 2013 15:51:11 +0200 Subject: [PATCH 25/29] stroke: Fix prompt and error messages in passphrase callback --- src/libcharon/plugins/stroke/stroke_cred.c | 24 ++++++++++++---------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/libcharon/plugins/stroke/stroke_cred.c b/src/libcharon/plugins/stroke/stroke_cred.c index 703410016..84d2262db 100644 --- a/src/libcharon/plugins/stroke/stroke_cred.c +++ b/src/libcharon/plugins/stroke/stroke_cred.c @@ -602,6 +602,8 @@ static err_t extract_secret(chunk_t *secret, chunk_t *line) typedef struct { /** socket we use for prompting */ FILE *prompt; + /** type of secret to unlock */ + int type; /** private key file */ char *path; /** number of tries */ @@ -609,12 +611,12 @@ typedef struct { } passphrase_cb_data_t; /** - * Callback function to receive Passphrases + * Callback function to receive passphrases */ static shared_key_t* passphrase_cb(passphrase_cb_data_t *data, - shared_key_type_t type, - identification_t *me, identification_t *other, - id_match_t *match_me, id_match_t *match_other) + shared_key_type_t type, identification_t *me, + identification_t *other, id_match_t *match_me, + id_match_t *match_other) { chunk_t secret; char buf[256]; @@ -628,13 +630,15 @@ static shared_key_t* passphrase_cb(passphrase_cb_data_t *data, { if (data->try > 5) { - fprintf(data->prompt, "PIN invalid, giving up.\n"); + fprintf(data->prompt, "Passphrase invalid, giving up.\n"); return NULL; } - fprintf(data->prompt, "PIN invalid!\n"); + fprintf(data->prompt, "Passphrase invalid!\n"); } data->try++; - fprintf(data->prompt, "Private key '%s' is encrypted.\n", data->path); + fprintf(data->prompt, "%s '%s' is encrypted.\n", + data->type == CRED_PRIVATE_KEY ? "Private key" : "PKCS#12 file", + data->path); fprintf(data->prompt, "Passphrase:\n"); if (fgets(buf, sizeof(buf), data->prompt)) { @@ -867,9 +871,10 @@ static bool load_from_file(chunk_t line, int line_nr, FILE *prompt, } if (secret.len == 7 && strneq(secret.ptr, "%prompt", 7)) { - callback_cred_t *cb = NULL; + callback_cred_t *cb; passphrase_cb_data_t pp_data = { .prompt = prompt, + .type = type, .path = path, .try = 1, }; @@ -881,9 +886,6 @@ static bool load_from_file(chunk_t line, int line_nr, FILE *prompt, return TRUE; } /* use callback credential set to prompt for the passphrase */ - pp_data.prompt = prompt; - pp_data.path = path; - pp_data.try = 1; cb = callback_cred_create_shared((void*)passphrase_cb, &pp_data); lib->credmgr->add_local_set(lib->credmgr, &cb->set, FALSE); From 4a64c3e9a0db4edcebff7f529caaf8bc0008fa38 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 17 Apr 2013 15:54:23 +0200 Subject: [PATCH 26/29] stroke: Cache passwords so the user is not prompted multiple times for the same password To verify/decrypt a PKCS#12 container a password might be needed multiple times. If it was entered correctly we don't want to bother the user again with another password prompt. The passwords for MAC creation and encryption could be different so the user might be prompted multiple times after all. --- src/libcharon/plugins/stroke/stroke_cred.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/libcharon/plugins/stroke/stroke_cred.c b/src/libcharon/plugins/stroke/stroke_cred.c index 84d2262db..5f8911f5e 100644 --- a/src/libcharon/plugins/stroke/stroke_cred.c +++ b/src/libcharon/plugins/stroke/stroke_cred.c @@ -600,6 +600,8 @@ static err_t extract_secret(chunk_t *secret, chunk_t *line) * Data for passphrase callback */ typedef struct { + /** cached passphrases */ + mem_cred_t *cache; /** socket we use for prompting */ FILE *prompt; /** type of secret to unlock */ @@ -618,6 +620,7 @@ static shared_key_t* passphrase_cb(passphrase_cb_data_t *data, identification_t *other, id_match_t *match_me, id_match_t *match_other) { + shared_key_t *shared; chunk_t secret; char buf[256]; @@ -654,7 +657,10 @@ static shared_key_t* passphrase_cb(passphrase_cb_data_t *data, { *match_other = ID_MATCH_NONE; } - return shared_key_create(SHARED_PRIVATE_KEY_PASS, chunk_clone(secret)); + shared = shared_key_create(SHARED_PRIVATE_KEY_PASS, + chunk_clone(secret)); + data->cache->add_shared(data->cache, shared->get_ref(shared), NULL); + return shared; } } return NULL; @@ -885,6 +891,10 @@ static bool load_from_file(chunk_t line, int line_nr, FILE *prompt, *result = NULL; return TRUE; } + /* add cache first so if valid passphrases are needed multiple times + * the callback is not called anymore */ + pp_data.cache = mem_cred_create(); + lib->credmgr->add_local_set(lib->credmgr, &pp_data.cache->set, FALSE); /* use callback credential set to prompt for the passphrase */ cb = callback_cred_create_shared((void*)passphrase_cb, &pp_data); lib->credmgr->add_local_set(lib->credmgr, &cb->set, FALSE); @@ -894,6 +904,8 @@ static bool load_from_file(chunk_t line, int line_nr, FILE *prompt, lib->credmgr->remove_local_set(lib->credmgr, &cb->set); cb->destroy(cb); + lib->credmgr->remove_local_set(lib->credmgr, &pp_data.cache->set); + pp_data.cache->destroy(pp_data.cache); } else { From 1c080407b2ef0a6d0cb2146ec8b0ec5760435ece Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 17 Apr 2013 16:03:05 +0200 Subject: [PATCH 27/29] stroke: Fail silently if another builder calls PW callback after giving up Also reduced the number of tries to 3. --- src/libcharon/plugins/stroke/stroke_cred.c | 23 +++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/libcharon/plugins/stroke/stroke_cred.c b/src/libcharon/plugins/stroke/stroke_cred.c index 5f8911f5e..22e7ddfbe 100644 --- a/src/libcharon/plugins/stroke/stroke_cred.c +++ b/src/libcharon/plugins/stroke/stroke_cred.c @@ -620,6 +620,7 @@ static shared_key_t* passphrase_cb(passphrase_cb_data_t *data, identification_t *other, id_match_t *match_me, id_match_t *match_other) { + static const int max_tries = 3; shared_key_t *shared; chunk_t secret; char buf[256]; @@ -629,16 +630,20 @@ static shared_key_t* passphrase_cb(passphrase_cb_data_t *data, return NULL; } + data->try++; + if (data->try > max_tries + 1) + { /* another builder might call this after we gave up, fail silently */ + return NULL; + } + if (data->try > max_tries) + { + fprintf(data->prompt, "Passphrase invalid, giving up.\n"); + return NULL; + } if (data->try > 1) { - if (data->try > 5) - { - fprintf(data->prompt, "Passphrase invalid, giving up.\n"); - return NULL; - } fprintf(data->prompt, "Passphrase invalid!\n"); } - data->try++; fprintf(data->prompt, "%s '%s' is encrypted.\n", data->type == CRED_PRIVATE_KEY ? "Private key" : "PKCS#12 file", data->path); @@ -700,12 +705,12 @@ static shared_key_t* pin_cb(pin_cb_data_t *data, shared_key_type_t type, return NULL; } + data->try++; if (data->try > 1) { fprintf(data->prompt, "PIN invalid, aborting.\n"); return NULL; } - data->try++; fprintf(data->prompt, "Login to '%s' required\n", data->card); fprintf(data->prompt, "PIN:\n"); if (fgets(buf, sizeof(buf), data->prompt)) @@ -794,7 +799,7 @@ static bool load_pin(mem_cred_t *secrets, chunk_t line, int line_nr, pin_data.prompt = prompt; pin_data.card = smartcard; pin_data.keyid = chunk; - pin_data.try = 1; + pin_data.try = 0; cb = callback_cred_create_shared((void*)pin_cb, &pin_data); lib->credmgr->add_local_set(lib->credmgr, &cb->set, FALSE); } @@ -882,7 +887,7 @@ static bool load_from_file(chunk_t line, int line_nr, FILE *prompt, .prompt = prompt, .type = type, .path = path, - .try = 1, + .try = 0, }; free(secret.ptr); From b7aa6b789e3bf326b06ef0d4137cad22dc17b7c9 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 17 Apr 2013 17:13:28 +0200 Subject: [PATCH 28/29] Load pkcs7 plugin in charon (and while we are at it in nm) --- configure.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.in b/configure.in index 87466f2db..8df0c2a0b 100644 --- a/configure.in +++ b/configure.in @@ -967,7 +967,7 @@ ADD_PLUGIN([revocation], [s charon nm cmd]) ADD_PLUGIN([constraints], [s charon nm cmd]) ADD_PLUGIN([pubkey], [s charon cmd]) ADD_PLUGIN([pkcs1], [s charon openac scepclient pki scripts manager medsrv attest nm cmd]) -ADD_PLUGIN([pkcs7], [s scepclient pki scripts cmd]) +ADD_PLUGIN([pkcs7], [s charon scepclient pki scripts nm cmd]) ADD_PLUGIN([pkcs8], [s charon openac scepclient pki scripts manager medsrv attest nm cmd]) ADD_PLUGIN([pkcs12], [s charon scepclient pki scripts cmd]) ADD_PLUGIN([pgp], [s charon]) From 6040eff9006940680a5668bbff5343b7b53cf9e5 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 17 Apr 2013 17:32:37 +0200 Subject: [PATCH 29/29] stroke: Add second password if provided --- src/libcharon/plugins/stroke/stroke_cred.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/libcharon/plugins/stroke/stroke_cred.c b/src/libcharon/plugins/stroke/stroke_cred.c index 22e7ddfbe..6b37ac787 100644 --- a/src/libcharon/plugins/stroke/stroke_cred.c +++ b/src/libcharon/plugins/stroke/stroke_cred.c @@ -921,6 +921,19 @@ static bool load_from_file(chunk_t line, int line_nr, FILE *prompt, shared = shared_key_create(SHARED_PRIVATE_KEY_PASS, secret); mem = mem_cred_create(); mem->add_shared(mem, shared, NULL); + if (eat_whitespace(&line)) + { /* if there is a second passphrase add that too, could be needed for + * PKCS#12 files using different passwords for MAC and encryption */ + ugh = extract_secret(&secret, &line); + if (ugh != NULL) + { + DBG1(DBG_CFG, "line %d: malformed passphrase: %s", line_nr, ugh); + mem->destroy(mem); + return FALSE; + } + shared = shared_key_create(SHARED_PRIVATE_KEY_PASS, secret); + mem->add_shared(mem, shared, NULL); + } lib->credmgr->add_local_set(lib->credmgr, &mem->set, FALSE); *result = lib->creds->create(lib->creds, type, subtype,