diff options
Diffstat (limited to 'src/libstrongswan/crypto')
-rw-r--r-- | src/libstrongswan/crypto/crypters/crypter.c | 7 | ||||
-rw-r--r-- | src/libstrongswan/crypto/crypto_tester.c | 27 | ||||
-rw-r--r-- | src/libstrongswan/crypto/diffie_hellman.c | 87 | ||||
-rw-r--r-- | src/libstrongswan/crypto/diffie_hellman.h | 30 | ||||
-rw-r--r-- | src/libstrongswan/crypto/hashers/hash_algorithm_set.c | 113 | ||||
-rw-r--r-- | src/libstrongswan/crypto/hashers/hash_algorithm_set.h | 76 | ||||
-rw-r--r-- | src/libstrongswan/crypto/hashers/hasher.c | 102 | ||||
-rw-r--r-- | src/libstrongswan/crypto/hashers/hasher.h | 40 | ||||
-rw-r--r-- | src/libstrongswan/crypto/mgf1/mgf1.c | 180 | ||||
-rw-r--r-- | src/libstrongswan/crypto/mgf1/mgf1.h | 77 | ||||
-rw-r--r-- | src/libstrongswan/crypto/mgf1/mgf1_bitspender.c | 208 | ||||
-rw-r--r-- | src/libstrongswan/crypto/mgf1/mgf1_bitspender.h | 67 | ||||
-rw-r--r-- | src/libstrongswan/crypto/pkcs5.c | 32 |
13 files changed, 994 insertions, 52 deletions
diff --git a/src/libstrongswan/crypto/crypters/crypter.c b/src/libstrongswan/crypto/crypters/crypter.c index 8123adde5..1e73baa4e 100644 --- a/src/libstrongswan/crypto/crypters/crypter.c +++ b/src/libstrongswan/crypto/crypters/crypter.c @@ -96,6 +96,10 @@ encryption_algorithm_t encryption_algorithm_from_oid(int oid, size_t *key_size) alg = ENCR_CAMELLIA_CBC; alg_key_size = 256; break; + case OID_BLOWFISH_CBC: + alg = ENCR_BLOWFISH; + alg_key_size = 0; + break; default: alg = ENCR_UNDEFINED; alg_key_size = 0; @@ -154,6 +158,9 @@ int encryption_algorithm_to_oid(encryption_algorithm_t alg, size_t key_size) oid = OID_UNKNOWN; } break; + case ENCR_BLOWFISH: + oid = OID_BLOWFISH_CBC; + break; default: oid = OID_UNKNOWN; } diff --git a/src/libstrongswan/crypto/crypto_tester.c b/src/libstrongswan/crypto/crypto_tester.c index d09844bfa..15ed17381 100644 --- a/src/libstrongswan/crypto/crypto_tester.c +++ b/src/libstrongswan/crypto/crypto_tester.c @@ -580,13 +580,22 @@ METHOD(crypto_tester_t, test_signer, bool, break; } + data = chunk_create(vector->data, vector->len); key = chunk_create(vector->key, signer->get_key_size(signer)); if (!signer->set_key(signer, key)) { goto failure; } + /* do partial append mode and check if key gets set correctly */ + if (!signer->get_signature(signer, data, NULL)) + { + goto failure; + } + if (!signer->set_key(signer, key)) + { + goto failure; + } /* allocated signature */ - data = chunk_create(vector->data, vector->len); if (!signer->allocate_signature(signer, data, &mac)) { goto failure; @@ -905,13 +914,25 @@ METHOD(crypto_tester_t, test_prf, bool, break; } + seed = chunk_create(vector->seed, vector->len); key = chunk_create(vector->key, vector->key_size); if (!prf->set_key(prf, key)) { goto failure; } + if (alg != PRF_FIPS_SHA1_160) + { + /* do partial append mode and check if key gets set correctly */ + if (!prf->get_bytes(prf, seed, NULL)) + { + goto failure; + } + if (!prf->set_key(prf, key)) + { + goto failure; + } + } /* allocated bytes */ - seed = chunk_create(vector->seed, vector->len); if (!prf->allocate_bytes(prf, seed, &out)) { goto failure; @@ -942,7 +963,7 @@ METHOD(crypto_tester_t, test_prf, bool, goto failure; } /* bytes to existing buffer, using append mode */ - if (seed.len > 2) + if (alg != PRF_FIPS_SHA1_160 && seed.len > 2) { memset(out.ptr, 0, out.len); if (vector->stateful) diff --git a/src/libstrongswan/crypto/diffie_hellman.c b/src/libstrongswan/crypto/diffie_hellman.c index 87c9b21f8..0d4cd9109 100644 --- a/src/libstrongswan/crypto/diffie_hellman.c +++ b/src/libstrongswan/crypto/diffie_hellman.c @@ -42,15 +42,16 @@ ENUM_NEXT(diffie_hellman_group_names, MODP_1024_160, ECP_512_BP, ECP_521_BIT, "ECP_256_BP", "ECP_384_BP", "ECP_512_BP"); -ENUM_NEXT(diffie_hellman_group_names, MODP_NULL, MODP_CUSTOM, ECP_512_BP, - "MODP_NULL", - "MODP_CUSTOM"); -ENUM_NEXT(diffie_hellman_group_names, NTRU_112_BIT, NTRU_256_BIT, MODP_CUSTOM, +ENUM_NEXT(diffie_hellman_group_names, MODP_NULL, MODP_NULL, ECP_512_BP, + "MODP_NULL"); +ENUM_NEXT(diffie_hellman_group_names, NTRU_112_BIT, NTRU_256_BIT, MODP_NULL, "NTRU_112", "NTRU_128", "NTRU_192", "NTRU_256"); -ENUM_END(diffie_hellman_group_names, NTRU_256_BIT); +ENUM_NEXT(diffie_hellman_group_names, MODP_CUSTOM, MODP_CUSTOM, NTRU_256_BIT, + "MODP_CUSTOM"); +ENUM_END(diffie_hellman_group_names, MODP_CUSTOM); /** @@ -439,7 +440,7 @@ void diffie_hellman_init() { int i; - if (lib->settings->get_int(lib->settings, + if (lib->settings->get_bool(lib->settings, "%s.dh_exponent_ansi_x9_42", TRUE, lib->ns)) { for (i = 0; i < countof(dh_params); i++) @@ -463,7 +464,7 @@ diffie_hellman_params_t *diffie_hellman_get_params(diffie_hellman_group_t group) if (!dh_params[i].public.exp_len) { if (!dh_params[i].public.subgroup.len && - lib->settings->get_int(lib->settings, + lib->settings->get_bool(lib->settings, "%s.dh_exponent_ansi_x9_42", TRUE, lib->ns)) { dh_params[i].public.exp_len = dh_params[i].public.prime.len; @@ -500,3 +501,75 @@ bool diffie_hellman_group_is_ec(diffie_hellman_group_t group) return FALSE; } } + +/** + * See header. + */ +bool diffie_hellman_verify_value(diffie_hellman_group_t group, chunk_t value) +{ + diffie_hellman_params_t *params; + bool valid = FALSE; + + switch (group) + { + case MODP_768_BIT: + case MODP_1024_BIT: + case MODP_1536_BIT: + case MODP_2048_BIT: + case MODP_3072_BIT: + case MODP_4096_BIT: + case MODP_6144_BIT: + case MODP_8192_BIT: + case MODP_1024_160: + case MODP_2048_224: + case MODP_2048_256: + params = diffie_hellman_get_params(group); + if (params) + { + valid = value.len == params->prime.len; + } + break; + case ECP_192_BIT: + valid = value.len == 48; + break; + case ECP_224_BIT: + case ECP_224_BP: + valid = value.len == 56; + break; + case ECP_256_BIT: + case ECP_256_BP: + valid = value.len == 64; + break; + case ECP_384_BIT: + case ECP_384_BP: + valid = value.len == 96; + break; + case ECP_512_BP: + valid = value.len == 128; + break; + case ECP_521_BIT: + valid = value.len == 132; + break; + case NTRU_112_BIT: + case NTRU_128_BIT: + case NTRU_192_BIT: + case NTRU_256_BIT: + /* verification currently not supported, do in plugin */ + valid = FALSE; + break; + case MODP_NULL: + case MODP_CUSTOM: + valid = TRUE; + break; + case MODP_NONE: + /* fail */ + break; + /* compile-warn unhandled groups, fail verification */ + } + if (!valid) + { + DBG1(DBG_ENC, "invalid DH public value size (%zu bytes) for %N", + value.len, diffie_hellman_group_names, group); + } + return valid; +} diff --git a/src/libstrongswan/crypto/diffie_hellman.h b/src/libstrongswan/crypto/diffie_hellman.h index 105db22f1..4704cd0da 100644 --- a/src/libstrongswan/crypto/diffie_hellman.h +++ b/src/libstrongswan/crypto/diffie_hellman.h @@ -63,12 +63,14 @@ enum diffie_hellman_group_t { /** insecure NULL diffie hellman group for testing, in PRIVATE USE */ MODP_NULL = 1024, /** MODP group with custom generator/prime */ - MODP_CUSTOM = 1025, /** Parameters defined by IEEE 1363.1, in PRIVATE USE */ NTRU_112_BIT = 1030, NTRU_128_BIT = 1031, NTRU_192_BIT = 1032, - NTRU_256_BIT = 1033 + NTRU_256_BIT = 1033, + /** internally used DH group with additional parameters g and p, outside + * of PRIVATE USE (i.e. IKEv2 DH group range) so it can't be negotiated */ + MODP_CUSTOM = 65536, }; /** @@ -87,9 +89,10 @@ struct diffie_hellman_t { * Space for returned secret is allocated and must be freed by the caller. * * @param secret shared secret will be written into this chunk - * @return SUCCESS, FAILED if not both DH values are set + * @return TRUE if shared secret computed successfully */ - status_t (*get_shared_secret) (diffie_hellman_t *this, chunk_t *secret); + bool (*get_shared_secret)(diffie_hellman_t *this, chunk_t *secret) + __attribute__((warn_unused_result)); /** * Sets the public value of partner. @@ -97,8 +100,10 @@ struct diffie_hellman_t { * Chunk gets cloned and can be destroyed afterwards. * * @param value public value of partner + * @return TRUE if other public value verified and set */ - void (*set_other_public_value) (diffie_hellman_t *this, chunk_t value); + bool (*set_other_public_value)(diffie_hellman_t *this, chunk_t value) + __attribute__((warn_unused_result)); /** * Gets the own public value to transmit. @@ -106,8 +111,10 @@ struct diffie_hellman_t { * Space for returned chunk is allocated and must be freed by the caller. * * @param value public value of caller is stored at this location + * @return TRUE if public value retrieved */ - void (*get_my_public_value) (diffie_hellman_t *this, chunk_t *value); + bool (*get_my_public_value) (diffie_hellman_t *this, chunk_t *value) + __attribute__((warn_unused_result)); /** * Get the DH group used. @@ -168,8 +175,17 @@ diffie_hellman_params_t *diffie_hellman_get_params(diffie_hellman_group_t group) * Check if a given DH group is an ECDH group * * @param group group to check - * @return TUE if group is an ECP group + * @return TRUE if group is an ECP group */ bool diffie_hellman_group_is_ec(diffie_hellman_group_t group); +/** + * Check if a diffie hellman public value is valid for given group. + * + * @param group group the value is used in + * @param value public DH value to check + * @return TRUE if value looks valid for group + */ +bool diffie_hellman_verify_value(diffie_hellman_group_t group, chunk_t value); + #endif /** DIFFIE_HELLMAN_H_ @}*/ diff --git a/src/libstrongswan/crypto/hashers/hash_algorithm_set.c b/src/libstrongswan/crypto/hashers/hash_algorithm_set.c new file mode 100644 index 000000000..93b67cb13 --- /dev/null +++ b/src/libstrongswan/crypto/hashers/hash_algorithm_set.c @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2015 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 <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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 "hash_algorithm_set.h" + +#include <collections/array.h> + +typedef struct private_hash_algorithm_set_t private_hash_algorithm_set_t; + +struct private_hash_algorithm_set_t { + + /** + * Public interface + */ + hash_algorithm_set_t public; + + /** + * Algorithms contained in the set + */ + array_t *algorithms; +}; + +/** + * Sort hash algorithms + */ +static int hash_sort(const void *a, const void *b, void *user) +{ + const hash_algorithm_t *ha = a, *hb = b; + return *ha - *hb; +} + +/** + * Find a hash algorithm + */ +static int hash_find(const void *a, const void *b) +{ + return hash_sort(a, b, NULL); +} + +METHOD(hash_algorithm_set_t, contains, bool, + private_hash_algorithm_set_t *this, hash_algorithm_t hash) +{ + return array_bsearch(this->algorithms, &hash, hash_find, NULL) != -1; +} + +METHOD(hash_algorithm_set_t, add, void, + private_hash_algorithm_set_t *this, hash_algorithm_t hash) +{ + if (!contains(this, hash)) + { + array_insert(this->algorithms, ARRAY_TAIL, &hash); + array_sort(this->algorithms, hash_sort, NULL); + } +} + +METHOD(hash_algorithm_set_t, count, int, + private_hash_algorithm_set_t *this) +{ + return array_count(this->algorithms); +} + +static bool hash_filter(void *data, void **in, hash_algorithm_t *out) +{ + *out = **(hash_algorithm_t**)in; + return TRUE; +} + +METHOD(hash_algorithm_set_t, create_enumerator, enumerator_t*, + private_hash_algorithm_set_t *this) +{ + return enumerator_create_filter(array_create_enumerator(this->algorithms), + (void*)hash_filter, NULL, NULL); +} + +METHOD(hash_algorithm_set_t, destroy, void, + private_hash_algorithm_set_t *this) +{ + array_destroy(this->algorithms); + free(this); +} + +/** + * Described in header + */ +hash_algorithm_set_t *hash_algorithm_set_create() +{ + private_hash_algorithm_set_t *this; + + INIT(this, + .public = { + .add = _add, + .contains = _contains, + .count = _count, + .create_enumerator = _create_enumerator, + .destroy = _destroy, + }, + .algorithms = array_create(sizeof(hash_algorithm_t), 0), + ); + + return &this->public; +}
\ No newline at end of file diff --git a/src/libstrongswan/crypto/hashers/hash_algorithm_set.h b/src/libstrongswan/crypto/hashers/hash_algorithm_set.h new file mode 100644 index 000000000..00e90cc2e --- /dev/null +++ b/src/libstrongswan/crypto/hashers/hash_algorithm_set.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2015 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 <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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 hash_algorithm_set hash_algorithm_set + * @{ @ingroup crypto + */ + +#ifndef HASH_ALGORITHM_SET_H_ +#define HASH_ALGORITHM_SET_H_ + +typedef struct hash_algorithm_set_t hash_algorithm_set_t; + +#include <library.h> +#include <crypto/hashers/hasher.h> + +/** + * A set of hash algorithms + */ +struct hash_algorithm_set_t { + + /** + * Add the given algorithm to the set. + * + * @param alg hash algorithm + */ + void (*add)(hash_algorithm_set_t *this, hash_algorithm_t alg); + + /** + * Check if the given algorithm is contained in the set. + * + * @param alg hash algorithm + * @return TRUE if contained in set + */ + bool (*contains)(hash_algorithm_set_t *this, hash_algorithm_t alg); + + /** + * Number of hash algorithms contained in the set. + * + * @return number of algorithms + */ + int (*count)(hash_algorithm_set_t *this); + + /** + * Enumerate the algorithms contained in the set. + * + * @return enumerator over hash_algorithm_t (sorted by identifier) + */ + enumerator_t *(*create_enumerator)(hash_algorithm_set_t *this); + + /** + * Destroy a hash_algorithm_set_t instance + */ + void (*destroy)(hash_algorithm_set_t *this); +}; + +/** + * Create a set of hash algorithms. + * + * @return hash_algorithm_set_t instance + */ +hash_algorithm_set_t *hash_algorithm_set_create(); + +#endif /** HASH_ALGORITHM_SET_H_ @}*/ diff --git a/src/libstrongswan/crypto/hashers/hasher.c b/src/libstrongswan/crypto/hashers/hasher.c index 13cbb5a59..38eebea9c 100644 --- a/src/libstrongswan/crypto/hashers/hasher.c +++ b/src/libstrongswan/crypto/hashers/hasher.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Tobias Brunner + * Copyright (C) 2012-2015 Tobias Brunner * Copyright (C) 2005-2006 Martin Willi * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil @@ -19,29 +19,31 @@ #include <asn1/oid.h> -ENUM(hash_algorithm_names, HASH_UNKNOWN, HASH_SHA512, +ENUM_BEGIN(hash_algorithm_names, HASH_SHA1, HASH_SHA512, + "HASH_SHA1", + "HASH_SHA256", + "HASH_SHA384", + "HASH_SHA512"); +ENUM_NEXT(hash_algorithm_names, HASH_UNKNOWN, HASH_SHA224, HASH_SHA512, "HASH_UNKNOWN", "HASH_MD2", "HASH_MD4", "HASH_MD5", - "HASH_SHA1", - "HASH_SHA224", - "HASH_SHA256", - "HASH_SHA384", - "HASH_SHA512" -); + "HASH_SHA224"); +ENUM_END(hash_algorithm_names, HASH_SHA224); -ENUM(hash_algorithm_short_names, HASH_UNKNOWN, HASH_SHA512, +ENUM_BEGIN(hash_algorithm_short_names, HASH_SHA1, HASH_SHA512, + "sha1", + "sha256", + "sha384", + "sha512"); +ENUM_NEXT(hash_algorithm_short_names, HASH_UNKNOWN, HASH_SHA224, HASH_SHA512, "unknown", "md2", "md4", "md5", - "sha1", - "sha224", - "sha256", - "sha384", - "sha512" -); + "sha224"); +ENUM_END(hash_algorithm_short_names, HASH_SHA224); /* * Described in header. @@ -249,6 +251,28 @@ integrity_algorithm_t hasher_algorithm_to_integrity(hash_algorithm_t alg, /* * Described in header. */ +bool hasher_algorithm_for_ikev2(hash_algorithm_t alg) +{ + switch (alg) + { + case HASH_SHA1: + case HASH_SHA256: + case HASH_SHA384: + case HASH_SHA512: + return TRUE; + case HASH_UNKNOWN: + case HASH_MD2: + case HASH_MD4: + case HASH_MD5: + case HASH_SHA224: + break; + } + return FALSE; +} + +/* + * Described in header. + */ int hasher_algorithm_to_oid(hash_algorithm_t alg) { int oid; @@ -323,8 +347,56 @@ int hasher_signature_algorithm_to_oid(hash_algorithm_t alg, key_type_t key) default: return OID_UNKNOWN; } + case KEY_BLISS: + switch (alg) + { + case HASH_SHA256: + return OID_BLISS_WITH_SHA256; + case HASH_SHA384: + return OID_BLISS_WITH_SHA384; + case HASH_SHA512: + return OID_BLISS_WITH_SHA512; + default: + return OID_UNKNOWN; + } default: return OID_UNKNOWN; } } +/* + * Defined in header. + */ +hash_algorithm_t hasher_from_signature_scheme(signature_scheme_t scheme) +{ + switch (scheme) + { + case SIGN_UNKNOWN: + case SIGN_RSA_EMSA_PKCS1_NULL: + case SIGN_ECDSA_WITH_NULL: + break; + case SIGN_RSA_EMSA_PKCS1_MD5: + return HASH_MD5; + case SIGN_RSA_EMSA_PKCS1_SHA1: + case SIGN_ECDSA_WITH_SHA1_DER: + return HASH_SHA1; + case SIGN_RSA_EMSA_PKCS1_SHA224: + return HASH_SHA224; + case SIGN_RSA_EMSA_PKCS1_SHA256: + case SIGN_ECDSA_WITH_SHA256_DER: + case SIGN_ECDSA_256: + case SIGN_BLISS_WITH_SHA256: + return HASH_SHA256; + case SIGN_RSA_EMSA_PKCS1_SHA384: + case SIGN_ECDSA_WITH_SHA384_DER: + case SIGN_ECDSA_384: + case SIGN_BLISS_WITH_SHA384: + return HASH_SHA384; + case SIGN_RSA_EMSA_PKCS1_SHA512: + case SIGN_ECDSA_WITH_SHA512_DER: + case SIGN_ECDSA_521: + case SIGN_BLISS_WITH_SHA512: + return HASH_SHA512; + } + return HASH_UNKNOWN; +} diff --git a/src/libstrongswan/crypto/hashers/hasher.h b/src/libstrongswan/crypto/hashers/hasher.h index 37ef0b6ab..772586308 100644 --- a/src/libstrongswan/crypto/hashers/hasher.h +++ b/src/libstrongswan/crypto/hashers/hasher.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Tobias Brunner + * Copyright (C) 2012-2015 Tobias Brunner * Copyright (C) 2005-2006 Martin Willi * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil @@ -32,19 +32,19 @@ typedef struct hasher_t hasher_t; #include <credentials/keys/public_key.h> /** - * Algorithms to use for hashing. + * Hash algorithms as defined for IKEv2 by RFC 7427 */ enum hash_algorithm_t { - /** not specified hash function */ - HASH_UNKNOWN = 0, - HASH_MD2 = 1, - HASH_MD4 = 2, - HASH_MD5 = 3, - HASH_SHA1 = 4, - HASH_SHA224 = 5, - HASH_SHA256 = 6, - HASH_SHA384 = 7, - HASH_SHA512 = 8 + HASH_SHA1 = 1, + HASH_SHA256 = 2, + HASH_SHA384 = 3, + HASH_SHA512 = 4, + /* use private use range for algorithms not defined/permitted by RFC 7427 */ + HASH_UNKNOWN = 1024, + HASH_MD2 = 1025, + HASH_MD4 = 1026, + HASH_MD5 = 1027, + HASH_SHA224 = 1028, }; #define HASH_SIZE_MD2 16 @@ -163,6 +163,14 @@ integrity_algorithm_t hasher_algorithm_to_integrity(hash_algorithm_t alg, size_t length); /** + * Check if the given algorithm may be used for IKEv2 signature authentication. + * + * @param alg hash algorithm + * @return TRUE if algorithm may be used, FALSE otherwise + */ +bool hasher_algorithm_for_ikev2(hash_algorithm_t alg); + +/** * Conversion of hash algorithm into ASN.1 OID. * * @param alg hash algorithm @@ -179,4 +187,12 @@ int hasher_algorithm_to_oid(hash_algorithm_t alg); */ int hasher_signature_algorithm_to_oid(hash_algorithm_t alg, key_type_t key); +/** + * Determine the hash algorithm associated with a given signature scheme. + * + * @param scheme signature scheme + * @return hash algorithm (could be HASH_UNKNOWN) + */ +hash_algorithm_t hasher_from_signature_scheme(signature_scheme_t scheme); + #endif /** HASHER_H_ @}*/ diff --git a/src/libstrongswan/crypto/mgf1/mgf1.c b/src/libstrongswan/crypto/mgf1/mgf1.c new file mode 100644 index 000000000..4bbcd6e99 --- /dev/null +++ b/src/libstrongswan/crypto/mgf1/mgf1.c @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2013-2014 Andreas Steffen + * HSR 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 <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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 "mgf1.h" + +#include "crypto/hashers/hasher.h" +#include "utils/debug.h" +#include "utils/test.h" + +typedef struct private_mgf1_t private_mgf1_t; + +/** + * Private data of an mgf1_t object. + */ +struct private_mgf1_t { + + /** + * Public mgf1_t interface. + */ + mgf1_t public; + + /** + * Hasher the MGF1 Mask Generation Function is based on + */ + hasher_t *hasher; + + /** + * Counter + */ + u_int32_t counter; + + /** + * Set if counter has reached 2^32 + */ + bool overflow; + + /** + * Current state to be hashed + */ + chunk_t state; + + /** + * Position of the 4 octet counter string + */ + u_char *ctr_str; + +}; + +METHOD(mgf1_t, get_hash_size, size_t, + private_mgf1_t *this) +{ + return this->hasher->get_hash_size(this->hasher); +} + +METHOD(mgf1_t, get_mask, bool, + private_mgf1_t *this, size_t mask_len, u_char *mask) +{ + u_char buf[HASH_SIZE_SHA512]; + size_t hash_len; + + hash_len = this->hasher->get_hash_size(this->hasher); + + while (mask_len > 0) + { + /* detect overflow, set counter string and increment counter */ + if (this->overflow) + { + return FALSE; + } + htoun32(this->ctr_str, this->counter++); + if (this->counter == 0) + { + this->overflow = TRUE; + } + + /* get the next or final mask block from the hash function */ + if (!this->hasher->get_hash(this->hasher, this->state, + (mask_len < hash_len) ? buf : mask)) + { + return FALSE; + } + if (mask_len < hash_len) + { + memcpy(mask, buf, mask_len); + return TRUE; + } + mask_len -= hash_len; + mask += hash_len; + } + return TRUE; +} + +METHOD(mgf1_t, allocate_mask, bool, + private_mgf1_t *this, size_t mask_len, chunk_t *mask) +{ + if (mask_len == 0) + { + *mask = chunk_empty; + return TRUE; + } + *mask = chunk_alloc(mask_len); + + return get_mask(this, mask_len, mask->ptr); +} + +METHOD(mgf1_t, destroy, void, + private_mgf1_t *this) +{ + this->hasher->destroy(this->hasher); + chunk_clear(&this->state); + free(this); +} + +/* + * Described in header. + */ +mgf1_t *mgf1_create(hash_algorithm_t alg, chunk_t seed, + bool hash_seed) +{ + private_mgf1_t *this; + hasher_t *hasher; + size_t state_len; + + if (seed.len == 0) + { + DBG1(DBG_LIB, "empty seed for MGF1"); + return NULL; + } + + hasher = lib->crypto->create_hasher(lib->crypto, alg); + if (!hasher) + { + DBG1(DBG_LIB, "failed to create %N hasher for MGF1", + hash_algorithm_names, alg); + return NULL; + } + state_len = (hash_seed ? hasher->get_hash_size(hasher) : seed.len) + 4; + + INIT(this, + .public = { + .get_hash_size = _get_hash_size, + .allocate_mask = _allocate_mask, + .get_mask = _get_mask, + .destroy = _destroy, + }, + .hasher = hasher, + .state = chunk_alloc(state_len), + ); + + /* determine position of the 4 octet counter string */ + this->ctr_str = this->state.ptr + state_len - 4; + + if (hash_seed) + { + if (!hasher->get_hash(hasher, seed, this->state.ptr)) + { + DBG1(DBG_LIB, "failed to hash seed for MGF1"); + destroy(this); + return NULL; + } + } + else + { + memcpy(this->state.ptr, seed.ptr, seed.len); + } + + return &this->public; +} diff --git a/src/libstrongswan/crypto/mgf1/mgf1.h b/src/libstrongswan/crypto/mgf1/mgf1.h new file mode 100644 index 000000000..592d31596 --- /dev/null +++ b/src/libstrongswan/crypto/mgf1/mgf1.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2013-2014 Andreas Steffen + * HSR 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 <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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 mgf1 mgf1 + * @{ @ingroup crypto + */ + +#ifndef MGF1_H_ +#define MGF1_H_ + +typedef struct mgf1_t mgf1_t; + +#include <library.h> + +/** + * Implements the PKCS#1 MGF1 Mask Generation Function based on a hash function + * defined in section 10.2.1 of RFC 2437 + */ +struct mgf1_t { + + /** + * Get the hash size of the underlying hash function + * + * @return hash size in bytes + */ + size_t (*get_hash_size)(mgf1_t *this); + + /** + * Generate a mask pattern and copy it to an output buffer + * If the maximum number of requests has been reached, reseeding occurs + * + * @param mask_len number of mask bytes to generate + * @param mask output buffer of minimum size mask_len + * @return TRUE if successful + */ + bool (*get_mask)(mgf1_t *this, size_t mask_len, u_char *mask); + + /** + * Generate a mask pattern and return it in an allocated chunk + * + * @param mask_len number of mask bytes to generate + * @param mask chunk containing generated mask + * @return TRUE if successful + */ + bool (*allocate_mask)(mgf1_t *this, size_t mask_len, chunk_t *mask); + + /** + * Destroy the MGF1 object + */ + void (*destroy)(mgf1_t *this); +}; + +/** + * Create an MGF1 object + * + * @param alg hash algorithm to be used by MGF1 + * @param seed seed used by MGF1 to generate mask from + * @param hash_seed hash seed before using it as a seed for MGF1 + */ +mgf1_t *mgf1_create(hash_algorithm_t alg, chunk_t seed, + bool hash_seed); + +#endif /** MGF1_H_ @}*/ + diff --git a/src/libstrongswan/crypto/mgf1/mgf1_bitspender.c b/src/libstrongswan/crypto/mgf1/mgf1_bitspender.c new file mode 100644 index 000000000..ef0a2bd01 --- /dev/null +++ b/src/libstrongswan/crypto/mgf1/mgf1_bitspender.c @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2014 Andreas Steffen + * HSR 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 <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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 "mgf1_bitspender.h" + +#include <crypto/mgf1/mgf1.h> + +typedef struct private_mgf1_bitspender_t private_mgf1_bitspender_t; + +/** + * Private data structure for mgf1_bitspender_t object + */ +struct private_mgf1_bitspender_t { + /** + * Public interface. + */ + mgf1_bitspender_t public; + + /** + * MGF1 bit mask generator + */ + mgf1_t *mgf1; + + /** + * Octet storage (accommodates up to 64 octets) + */ + uint8_t octets[HASH_SIZE_SHA512]; + + /** + * Length of the returned hash value in octets + */ + int hash_len; + + /** + * Number of generated octets + */ + int octets_count; + + /** + * Number of available octets + */ + int octets_left; + + /** + * Bit storage (accommodates up to 32 bits) + */ + uint32_t bits; + + /** + * Number of available bits + */ + int bits_left; + + /** + * Byte storage (accommodates up to 4 bytes) + */ + uint8_t bytes[4]; + + /** + * Number of available bytes + */ + int bytes_left; + +}; + +METHOD(mgf1_bitspender_t, get_bits, bool, + private_mgf1_bitspender_t *this, int bits_needed, uint32_t *bits) +{ + int bits_now; + + *bits = 0x00000000; + + if (bits_needed == 0) + { + /* trivial */ + return TRUE; + } + if (bits_needed > 32) + { + /* too many bits requested */ + return FALSE; + } + + while (bits_needed) + { + if (this->bits_left == 0) + { + if (this->octets_left == 0) + { + /* get another block from MGF1 */ + if (!this->mgf1->get_mask(this->mgf1, this->hash_len, + this->octets)) + { + /* no block available */ + return FALSE; + } + this->octets_left = this->hash_len; + this->octets_count += this->hash_len; + } + this->bits = untoh32(this->octets + this->hash_len - + this->octets_left); + this->bits_left = 32; + this->octets_left -= 4; + } + if (bits_needed > this->bits_left) + { + bits_now = this->bits_left; + this->bits_left = 0; + bits_needed -= bits_now; + } + else + { + bits_now = bits_needed; + this->bits_left -= bits_needed; + bits_needed = 0; + } + if (bits_now == 32) + { + *bits = this->bits; + } + else + { + *bits <<= bits_now; + *bits |= this->bits >> this->bits_left; + if (this->bits_left) + { + this->bits &= 0xffffffff >> (32 - this->bits_left); + } + } + } + return TRUE; +} + +METHOD(mgf1_bitspender_t, get_byte, bool, + private_mgf1_bitspender_t *this, uint8_t *byte) +{ + if (this->bytes_left == 0) + { + if (this->octets_left == 0) + { + /* get another block from MGF1 */ + if (!this->mgf1->get_mask(this->mgf1, this->hash_len, this->octets)) + { + /* no block available */ + return FALSE; + } + this->octets_left = this->hash_len; + this->octets_count += this->hash_len; + } + memcpy(this->bytes, this->octets + this->hash_len - this->octets_left, 4); + this->bytes_left = 4; + this->octets_left -= 4; + } + *byte = this->bytes[4 - this->bytes_left--]; + + return TRUE; +} + +METHOD(mgf1_bitspender_t, destroy, void, + private_mgf1_bitspender_t *this) +{ + DBG2(DBG_LIB, "mgf1 generated %u octets", this->octets_count); + memwipe(this->octets, sizeof(this->octets)); + this->mgf1->destroy(this->mgf1); + free(this); +} + +/** + * See header. + */ +mgf1_bitspender_t *mgf1_bitspender_create(hash_algorithm_t alg, chunk_t seed, + bool hash_seed) +{ + private_mgf1_bitspender_t *this; + mgf1_t *mgf1; + + mgf1 = mgf1_create(alg, seed, hash_seed); + if (!mgf1) + { + return NULL; + } + DBG2(DBG_LIB, "mgf1 based on %N is seeded with %u octets", + hash_algorithm_short_names, alg, seed.len); + + INIT(this, + .public = { + .get_bits = _get_bits, + .get_byte = _get_byte, + .destroy = _destroy, + }, + .mgf1 = mgf1, + .hash_len = mgf1->get_hash_size(mgf1), + ); + + return &this->public; +} diff --git a/src/libstrongswan/crypto/mgf1/mgf1_bitspender.h b/src/libstrongswan/crypto/mgf1/mgf1_bitspender.h new file mode 100644 index 000000000..f7df8e834 --- /dev/null +++ b/src/libstrongswan/crypto/mgf1/mgf1_bitspender.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2014 Andreas Steffen + * HSR 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 <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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 mgf1_bitspender mgf1_bitspender + * @{ @ingroup mgf1 + */ + +#ifndef MGF1_BITSPENDER_H_ +#define MGF1_BITSPENDER_H_ + +#include <library.h> +#include <crypto/hashers/hasher.h> + +typedef struct mgf1_bitspender_t mgf1_bitspender_t; + +/** + * Generates a given number of pseudo-random bits at a time using MGF1 + */ +struct mgf1_bitspender_t { + + /** + * Get pseudo-random bits + * + * @param bits_needed Number of needed bits (1..32) + * @param bits Pseudo-random bits + * @result FALSE if internal MGF1 error occurred + */ + bool (*get_bits)(mgf1_bitspender_t *this, int bits_needed, uint32_t *bits); + + /** + * Get a pseudo-random byte + * + * @param byte Pseudo-random byte + * @result FALSE if internal MGF1 error occurred + */ + bool (*get_byte)(mgf1_bitspender_t *this, uint8_t *byte); + + /** + * Destroy mgf1_bitspender_t object + */ + void (*destroy)(mgf1_bitspender_t *this); +}; + +/** + * Create a mgf1_bitspender_t object + * + * @param alg Hash algorithm to be used with MGF1 + * @param seed Seed used to initialize MGF1 + * @param hash_seed Hash seed before using it as a seed for MFG1 + */ +mgf1_bitspender_t *mgf1_bitspender_create(hash_algorithm_t alg, chunk_t seed, + bool hash_seed); + +#endif /** MGF1_BITSPENDER_H_ @}*/ diff --git a/src/libstrongswan/crypto/pkcs5.c b/src/libstrongswan/crypto/pkcs5.c index 3b4df0e8a..478926f2f 100644 --- a/src/libstrongswan/crypto/pkcs5.c +++ b/src/libstrongswan/crypto/pkcs5.c @@ -108,13 +108,13 @@ struct private_pkcs5_t { * Verify padding of decrypted blob. * Length of blob is adjusted accordingly. */ -static bool verify_padding(chunk_t *blob) +static bool verify_padding(crypter_t *crypter, chunk_t *blob) { u_int8_t padding, count; padding = count = blob->ptr[blob->len - 1]; - if (padding > 8) + if (padding > crypter->get_block_size(crypter)) { return FALSE; } @@ -153,7 +153,7 @@ static bool decrypt_generic(private_pkcs5_t *this, chunk_t password, return FALSE; } memwipe(keymat.ptr, keymat.len); - if (verify_padding(decrypted)) + if (verify_padding(this->crypter, decrypted)) { return TRUE; } @@ -504,6 +504,7 @@ static bool parse_pbes2_params(private_pkcs5_t *this, chunk_t blob, int level0) { asn1_parser_t *parser; chunk_t object, params; + size_t keylen; int objectID; bool success = FALSE; @@ -533,20 +534,35 @@ static bool parse_pbes2_params(private_pkcs5_t *this, chunk_t blob, int level0) { int oid = asn1_parse_algorithmIdentifier(object, parser->get_level(parser) + 1, ¶ms); - if (oid != OID_3DES_EDE_CBC) + this->encr = encryption_algorithm_from_oid(oid, &keylen); + if (this->encr == ENCR_UNDEFINED) { /* unsupported encryption scheme */ goto end; } - if (this->keylen <= 0) - { /* default key length for DES-EDE3-CBC-Pad */ - this->keylen = 24; + /* prefer encoded key length */ + this->keylen = this->keylen ?: keylen / 8; + if (!this->keylen) + { /* set default key length for known algorithms */ + switch (this->encr) + { + case ENCR_DES: + this->keylen = 8; + break; + case ENCR_3DES: + this->keylen = 24; + break; + case ENCR_BLOWFISH: + this->keylen = 16; + break; + default: + goto end; + } } 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; } |