diff --git a/src/libstrongswan/Makefile.am b/src/libstrongswan/Makefile.am index ca5f5e5e2..681434e4c 100644 --- a/src/libstrongswan/Makefile.am +++ b/src/libstrongswan/Makefile.am @@ -28,6 +28,7 @@ crypto/rngs/rng.c crypto/rngs/rng.h \ crypto/prf_plus.h crypto/prf_plus.c \ crypto/signers/signer.c crypto/signers/signer.h \ crypto/crypto_factory.c crypto/crypto_factory.h \ +crypto/crypto_tester.c crypto/crypto_tester.h \ crypto/diffie_hellman.c crypto/diffie_hellman.h \ crypto/transform.c crypto/transform.h \ credentials/credential_factory.c credentials/credential_factory.h \ diff --git a/src/libstrongswan/crypto/crypto_tester.c b/src/libstrongswan/crypto/crypto_tester.c new file mode 100644 index 000000000..4b8173402 --- /dev/null +++ b/src/libstrongswan/crypto/crypto_tester.c @@ -0,0 +1,628 @@ +/* + * Copyright (C) 2009 Martin Willi + * 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 "crypto_tester.h" + +#include +#include + +typedef struct private_crypto_tester_t private_crypto_tester_t; + +/** + * Private data of an crypto_tester_t object. + */ +struct private_crypto_tester_t { + + /** + * Public crypto_tester_t interface. + */ + crypto_tester_t public; + + /** + * List of crypter test vectors + */ + linked_list_t *crypter; + + /** + * List of signer test vectors + */ + linked_list_t *signer; + + /** + * List of hasher test vectors + */ + linked_list_t *hasher; + + /** + * List of PRF test vectors + */ + linked_list_t *prf; + + /** + * List of RNG test vectors + */ + linked_list_t *rng; + + /** + * Is a test vector required to pass a test? + */ + bool required; + + /** + * should we run RNG_TRUE tests? Enough entropy? + */ + bool rng_true; +}; + +/** + * Implementation of crypto_tester_t.test_crypter + */ +static bool test_crypter(private_crypto_tester_t *this, + encryption_algorithm_t alg, size_t key_size, crypter_constructor_t create) +{ + enumerator_t *enumerator; + crypter_test_vector_t *vector; + bool failed = FALSE; + u_int tested = 0; + + enumerator = this->crypter->create_enumerator(this->crypter); + while (enumerator->enumerate(enumerator, &vector)) + { + crypter_t *crypter; + chunk_t key, plain, cipher, iv; + + if (vector->alg != alg) + { + continue; + } + if (key_size && key_size != vector->key_size) + { /* test only vectors with a specific key size, if key size given */ + continue; + } + crypter = create(alg, vector->key_size); + if (!crypter) + { /* key size not supported... */ + continue; + } + + failed = FALSE; + tested++; + + key = chunk_create(vector->key, crypter->get_key_size(crypter)); + crypter->set_key(crypter, key); + iv = chunk_create(vector->iv, crypter->get_block_size(crypter)); + + /* allocated encryption */ + plain = chunk_create(vector->plain, vector->len); + crypter->encrypt(crypter, plain, iv, &cipher); + if (!memeq(vector->cipher, cipher.ptr, cipher.len)) + { + failed = TRUE; + } + /* inline decryption */ + crypter->decrypt(crypter, cipher, iv, NULL); + if (!memeq(vector->plain, cipher.ptr, cipher.len)) + { + failed = TRUE; + } + free(cipher.ptr); + /* allocated decryption */ + cipher = chunk_create(vector->cipher, vector->len); + crypter->decrypt(crypter, cipher, iv, &plain); + if (!memeq(vector->plain, plain.ptr, plain.len)) + { + failed = TRUE; + } + /* inline encryption */ + crypter->encrypt(crypter, plain, iv, NULL); + if (!memeq(vector->cipher, plain.ptr, plain.len)) + { + failed = TRUE; + } + free(plain.ptr); + + crypter->destroy(crypter); + if (failed) + { + DBG1("test vector %d failed, %N disabled", + tested, encryption_algorithm_names, alg); + break; + } + DBG2("%N test vector %d successful", + encryption_algorithm_names, alg, tested); + } + enumerator->destroy(enumerator); + if (!tested) + { + DBG1("no test vectors found for %N%s", + encryption_algorithm_names, alg, + this->required ? ", disabled" : ""); + return !this->required; + } + if (!failed) + { + DBG2("successfully tested %d test vectors for %N", + tested, encryption_algorithm_names, alg); + } + return !failed; +} + +/** + * Implementation of crypto_tester_t.test_signer + */ +static bool test_signer(private_crypto_tester_t *this, + integrity_algorithm_t alg, signer_constructor_t create) +{ + enumerator_t *enumerator; + signer_test_vector_t *vector; + bool failed = FALSE; + u_int tested = 0; + + enumerator = this->signer->create_enumerator(this->signer); + while (enumerator->enumerate(enumerator, &vector)) + { + signer_t *signer; + chunk_t key, data, mac; + + if (vector->alg != alg) + { + continue; + } + + tested++; + signer = create(alg); + if (!signer) + { + DBG1("creating instance failed, %N disabled", + integrity_algorithm_names, alg); + failed = TRUE; + break; + } + + failed = FALSE; + + key = chunk_create(vector->key, signer->get_key_size(signer)); + signer->set_key(signer, key); + + /* allocated signature */ + data = chunk_create(vector->data, vector->len); + signer->allocate_signature(signer, data, &mac); + if (mac.len != signer->get_block_size(signer)) + { + failed = TRUE; + } + if (!memeq(vector->mac, mac.ptr, mac.len)) + { + failed = TRUE; + } + /* signature to existing buffer */ + memset(mac.ptr, 0, mac.len); + signer->get_signature(signer, data, mac.ptr); + if (!memeq(vector->mac, mac.ptr, mac.len)) + { + failed = TRUE; + } + /* signature verification, good case */ + if (!signer->verify_signature(signer, data, mac)) + { + failed = TRUE; + } + /* signature verification, bad case */ + *(mac.ptr + mac.len - 1) += 1; + if (signer->verify_signature(signer, data, mac)) + { + failed = TRUE; + } + /* signature to existing buffer, using append mode */ + if (data.len > 2) + { + memset(mac.ptr, 0, mac.len); + signer->allocate_signature(signer, chunk_create(data.ptr, 1), NULL); + signer->get_signature(signer, chunk_create(data.ptr + 1, 1), NULL); + signer->get_signature(signer, chunk_skip(data, 2), mac.ptr); + if (!memeq(vector->mac, mac.ptr, mac.len)) + { + failed = TRUE; + } + } + free(mac.ptr); + + signer->destroy(signer); + if (failed) + { + DBG1("test vector %d failed, %N disabled", + tested, integrity_algorithm_names, alg); + break; + } + DBG2(" %N test vector %d successful", + integrity_algorithm_names, alg, tested); + } + enumerator->destroy(enumerator); + if (!tested) + { + DBG1("no test vectors found for %N%s", + integrity_algorithm_names, alg, + this->required ? ", disabled" : ""); + return !this->required; + } + if (!failed) + { + DBG2("successfully tested %d test vectors for %N", + tested, integrity_algorithm_names, alg); + } + return !failed; +} + +/** + * Implementation of hasher_t.test_hasher + */ +static bool test_hasher(private_crypto_tester_t *this, hash_algorithm_t alg, + hasher_constructor_t create) +{ + enumerator_t *enumerator; + hasher_test_vector_t *vector; + bool failed = FALSE; + u_int tested = 0; + + enumerator = this->hasher->create_enumerator(this->hasher); + while (enumerator->enumerate(enumerator, &vector)) + { + hasher_t *hasher; + chunk_t data, hash; + + if (vector->alg != alg) + { + continue; + } + + tested++; + hasher = create(alg); + if (!hasher) + { + DBG1("creating instance failed, %N disabled", + hash_algorithm_names, alg); + failed = TRUE; + break; + } + + failed = FALSE; + + /* allocated hash */ + data = chunk_create(vector->data, vector->len); + hasher->allocate_hash(hasher, data, &hash); + if (hash.len != hasher->get_hash_size(hasher)) + { + failed = TRUE; + } + if (!memeq(vector->hash, hash.ptr, hash.len)) + { + failed = TRUE; + } + /* hash to existing buffer */ + memset(hash.ptr, 0, hash.len); + hasher->get_hash(hasher, data, hash.ptr); + if (!memeq(vector->hash, hash.ptr, hash.len)) + { + failed = TRUE; + } + /* hasher to existing buffer, using append mode */ + if (data.len > 2) + { + memset(hash.ptr, 0, hash.len); + hasher->allocate_hash(hasher, chunk_create(data.ptr, 1), NULL); + hasher->get_hash(hasher, chunk_create(data.ptr + 1, 1), NULL); + hasher->get_hash(hasher, chunk_skip(data, 2), hash.ptr); + if (!memeq(vector->hash, hash.ptr, hash.len)) + { + failed = TRUE; + } + } + free(hash.ptr); + + hasher->destroy(hasher); + if (failed) + { + DBG1("test vector %d failed, %N disabled", + tested, hash_algorithm_names, alg); + break; + } + DBG2("%N test vector %d successful", + hash_algorithm_names, alg, tested); + } + enumerator->destroy(enumerator); + if (!tested) + { + DBG1("no test vectors found for %N%s", + hash_algorithm_names, alg, + this->required ? ", disabled" : ""); + return !this->required; + } + if (!failed) + { + DBG2("successfully tested %d test vectors for %N", + tested, hash_algorithm_names, alg); + } + return !failed; +} + +/** + * Implementation of crypto_tester_t.test_prf + */ +static bool test_prf(private_crypto_tester_t *this, + pseudo_random_function_t alg, prf_constructor_t create) +{ + enumerator_t *enumerator; + prf_test_vector_t *vector; + bool failed = FALSE; + u_int tested = 0; + + enumerator = this->prf->create_enumerator(this->prf); + while (enumerator->enumerate(enumerator, &vector)) + { + prf_t *prf; + chunk_t key, seed, out; + + if (vector->alg != alg) + { + continue; + } + + tested++; + prf = create(alg); + if (!prf) + { + DBG1("creating instance failed, %N disabled", + pseudo_random_function_names, alg); + failed = TRUE; + break; + } + + failed = FALSE; + + key = chunk_create(vector->key, vector->key_size); + prf->set_key(prf, key); + + /* allocated bytes */ + seed = chunk_create(vector->seed, vector->len); + prf->allocate_bytes(prf, seed, &out); + if (out.len != prf->get_block_size(prf)) + { + failed = TRUE; + } + if (!memeq(vector->out, out.ptr, out.len)) + { + failed = TRUE; + } + /* bytes to existing buffer */ + memset(out.ptr, 0, out.len); + prf->get_bytes(prf, seed, out.ptr); + if (!memeq(vector->out, out.ptr, out.len)) + { + failed = TRUE; + } + /* bytes to existing buffer, using append mode */ + if (seed.len > 2) + { + memset(out.ptr, 0, out.len); + prf->allocate_bytes(prf, chunk_create(seed.ptr, 1), NULL); + prf->get_bytes(prf, chunk_create(seed.ptr + 1, 1), NULL); + prf->get_bytes(prf, chunk_skip(seed, 2), out.ptr); + if (!memeq(vector->out, out.ptr, out.len)) + { + failed = TRUE; + } + } + free(out.ptr); + + prf->destroy(prf); + if (failed) + { + DBG1("test vector %d failed, %N disabled", + tested, pseudo_random_function_names, alg); + break; + } + DBG2("%N test vector %d successful", + pseudo_random_function_names, alg, tested); + } + enumerator->destroy(enumerator); + if (!tested) + { + DBG1("no test vectors found for %N%s", + pseudo_random_function_names, alg, + this->required ? ", disabled" : ""); + return !this->required; + } + if (!failed) + { + DBG2("successfully tested %d testvectors for %N", + tested, pseudo_random_function_names, alg); + } + return !failed; +} + +/** + * Implementation of crypto_tester_t.test_rng + */ +static bool test_rng(private_crypto_tester_t *this, rng_quality_t quality, + rng_constructor_t create) +{ + enumerator_t *enumerator; + rng_test_vector_t *vector; + bool failed = FALSE; + u_int tested = 0; + + if (!this->rng_true && quality == RNG_TRUE) + { + DBG1("skipping %N test, disabled by config", rng_quality_names, quality); + return TRUE; + } + + enumerator = this->rng->create_enumerator(this->rng); + while (enumerator->enumerate(enumerator, &vector)) + { + rng_t *rng; + chunk_t data; + + if (vector->quality != quality) + { + continue; + } + + tested++; + rng = create(quality); + if (!rng) + { + DBG1("creating instance failed, %N disabled", + rng_quality_names, quality); + failed = TRUE; + break; + } + + failed = FALSE; + + /* allocated bytes */ + rng->allocate_bytes(rng, vector->len, &data); + if (data.len != vector->len) + { + failed = TRUE; + } + if (!vector->test(vector->user, data)) + { + failed = TRUE; + } + /* bytes to existing buffer */ + memset(data.ptr, 0, data.len); + rng->get_bytes(rng, vector->len, data.ptr); + if (!vector->test(vector->user, data)) + { + failed = TRUE; + } + free(data.ptr); + + rng->destroy(rng); + if (failed) + { + DBG1("test vector %d failed, %N disabled", + tested, rng_quality_names, quality); + break; + } + DBG2("%N test vector %d successful", rng_quality_names, quality, tested); + } + enumerator->destroy(enumerator); + if (!tested) + { + DBG1("no test vectors found for %N%s", + rng_quality_names, quality, this->required ? ", disabled" : ""); + return !this->required; + } + if (!failed) + { + DBG2("successfully tested %d testvectors for %N", + tested, rng_quality_names, quality); + } + return !failed; +} + +/** + * Implementation of crypter_tester_t.add_crypter_vector + */ +static void add_crypter_vector(private_crypto_tester_t *this, + crypter_test_vector_t *vector) +{ + this->crypter->insert_last(this->crypter, vector); +} + +/** + * Implementation of crypter_tester_t.add_signer_vector + */ +static void add_signer_vector(private_crypto_tester_t *this, + signer_test_vector_t *vector) +{ + this->signer->insert_last(this->signer, vector); +} + +/** + * Implementation of crypter_tester_t.add_hasher_vector + */ +static void add_hasher_vector(private_crypto_tester_t *this, + hasher_test_vector_t *vector) +{ + this->hasher->insert_last(this->hasher, vector); +} + +/** + * Implementation of crypter_tester_t.add_prf_vector + */ +static void add_prf_vector(private_crypto_tester_t *this, + prf_test_vector_t *vector) +{ + this->prf->insert_last(this->prf, vector); +} + +/** + * Implementation of crypter_tester_t.add_rng_vector + */ +static void add_rng_vector(private_crypto_tester_t *this, + rng_test_vector_t *vector) +{ + this->rng->insert_last(this->rng, vector); +} + +/** + * Implementation of crypto_tester_t.destroy. + */ +static void destroy(private_crypto_tester_t *this) +{ + this->crypter->destroy(this->crypter); + this->signer->destroy(this->signer); + this->hasher->destroy(this->hasher); + this->prf->destroy(this->prf); + this->rng->destroy(this->rng); + free(this); +} + +/** + * See header + */ +crypto_tester_t *crypto_tester_create() +{ + private_crypto_tester_t *this = malloc_thing(private_crypto_tester_t); + + this->public.test_crypter = (bool(*)(crypto_tester_t*, encryption_algorithm_t alg,size_t key_size, crypter_constructor_t create))test_crypter; + this->public.test_signer = (bool(*)(crypto_tester_t*, integrity_algorithm_t alg, signer_constructor_t create))test_signer; + this->public.test_hasher = (bool(*)(crypto_tester_t*, hash_algorithm_t alg, hasher_constructor_t create))test_hasher; + this->public.test_prf = (bool(*)(crypto_tester_t*, pseudo_random_function_t alg, prf_constructor_t create))test_prf; + this->public.test_rng = (bool(*)(crypto_tester_t*, rng_quality_t quality, rng_constructor_t create))test_rng; + this->public.add_crypter_vector = (void(*)(crypto_tester_t*, crypter_test_vector_t *vector))add_crypter_vector; + this->public.add_signer_vector = (void(*)(crypto_tester_t*, signer_test_vector_t *vector))add_signer_vector; + this->public.add_hasher_vector = (void(*)(crypto_tester_t*, hasher_test_vector_t *vector))add_hasher_vector; + this->public.add_prf_vector = (void(*)(crypto_tester_t*, prf_test_vector_t *vector))add_prf_vector; + this->public.add_rng_vector = (void(*)(crypto_tester_t*, rng_test_vector_t *vector))add_rng_vector; + this->public.destroy = (void(*)(crypto_tester_t*))destroy; + + this->crypter = linked_list_create(); + this->signer = linked_list_create(); + this->hasher = linked_list_create(); + this->prf = linked_list_create(); + this->rng = linked_list_create(); + + this->required = lib->settings->get_bool(lib->settings, + "libstrongswan.crypto.test.required", FALSE); + this->rng_true = lib->settings->get_bool(lib->settings, + "libstrongswan.crypto.test.rng_true", FALSE); + + return &this->public; +} + diff --git a/src/libstrongswan/crypto/crypto_tester.h b/src/libstrongswan/crypto/crypto_tester.h new file mode 100644 index 000000000..5a9726126 --- /dev/null +++ b/src/libstrongswan/crypto/crypto_tester.h @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2009 Martin Willi + * 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 crypto_tester crypto_tester + * @{ @ingroup crypto + */ + +#ifndef CRYPTO_TESTER_H_ +#define CRYPTO_TESTER_H_ + +typedef struct crypto_tester_t crypto_tester_t; + +#include + +typedef struct crypter_test_vector_t crypter_test_vector_t; +typedef struct signer_test_vector_t signer_test_vector_t; +typedef struct hasher_test_vector_t hasher_test_vector_t; +typedef struct prf_test_vector_t prf_test_vector_t; +typedef struct rng_test_vector_t rng_test_vector_t; + +struct crypter_test_vector_t { + /** encryption algorithm this vector tests */ + encryption_algorithm_t alg; + /** key length to use, in bytes */ + size_t key_size; + /** encryption key of test vector */ + u_char *key; + /** initialization vector, using crypters blocksize bytes */ + u_char *iv; + /** length of plain and cipher text */ + size_t len; + /** plain text */ + u_char *plain; + /** cipher text */ + u_char *cipher; +}; + +struct signer_test_vector_t { + /** signer algorithm this test vector tests */ + pseudo_random_function_t alg; + /** key to use, with a length the algorithm expects */ + u_char *key; + /** size of the input data */ + size_t len; + /** input data */ + u_char *data; + /** expected output, with ouput size of the tested algorithm */ + u_char *mac; +}; + +struct hasher_test_vector_t { + /** hash algorithm this test vector tests */ + hash_algorithm_t alg; + /** length of the input data */ + size_t len; + /** input data */ + u_char *data; + /** expected hash, with hash size of the tested algorithm */ + u_char *hash; +}; + +struct prf_test_vector_t { + /** prf algorithm this test vector tests */ + pseudo_random_function_t alg; + /** key length to use, in bytes */ + size_t key_size; + /** key to use */ + u_char *key; + /** size of the seed data */ + size_t len; + /** seed data */ + u_char *seed; + /** expected output, with block size of the tested algorithm */ + u_char *out; +}; + +/** + * Test vector for a RNG. + * + * Contains a callback function to analyze the output of a RNG, + */ +struct rng_test_vector_t { + /** quality of random data this test vector tests */ + rng_quality_t quality; + /** callback function to test RNG output, returns TRUE if data ok */ + bool (*test)(void *user, chunk_t data); + /** number of bytes the function requests */ + size_t len; + /** user data passed back to the test() function on invocation */ + void *user; +}; + +/** + * Cryptographic primitive testing framework. + */ +struct crypto_tester_t { + + /** + * Test a crypter algorithm, optionally using a specified key size. + * + * @param alg algorithm to test + * @param key_size key size to test, 0 for all + * @param create constructor function for the crypter + * @return TRUE if test passed + */ + bool (*test_crypter)(crypto_tester_t *this, encryption_algorithm_t alg, + size_t key_size, crypter_constructor_t create); + /** + * Test a signer algorithm. + * + * @param alg algorithm to test + * @param create constructor function for the signer + * @return TRUE if test passed + */ + bool (*test_signer)(crypto_tester_t *this, integrity_algorithm_t alg, + signer_constructor_t create); + /** + * Test a hasher algorithm. + * + * @param alg algorithm to test + * @param create constructor function for the hasher + * @return TRUE if test passed + */ + bool (*test_hasher)(crypto_tester_t *this, hash_algorithm_t alg, + hasher_constructor_t create); + /** + * Test a PRF algorithm. + * + * @param alg algorithm to test + * @param create constructor function for the PRF + * @return TRUE if test passed + */ + bool (*test_prf)(crypto_tester_t *this, pseudo_random_function_t alg, + prf_constructor_t create); + /** + * Test a RNG implementation. + * + * @param alg algorithm to test + * @param create constructor function for the RNG + * @return TRUE if test passed + */ + bool (*test_rng)(crypto_tester_t *this, rng_quality_t quality, + rng_constructor_t create); + /** + * Add a test vector to test a crypter. + * + * @param vector pointer to test vector + */ + void (*add_crypter_vector)(crypto_tester_t *this, + crypter_test_vector_t *vector); + /** + * Add a test vector to test a signer. + * + * @param vector pointer to test vector + */ + void (*add_signer_vector)(crypto_tester_t *this, + signer_test_vector_t *vector); + /** + * Add a test vector to test a hasher. + * + * @param vector pointer to test vector + */ + void (*add_hasher_vector)(crypto_tester_t *this, + hasher_test_vector_t *vector); + /** + * Add a test vector to test a PRF. + * + * @param vector pointer to test vector + */ + void (*add_prf_vector)(crypto_tester_t *this, prf_test_vector_t *vector); + + /** + * Add a test vector to test a RNG. + * + * @param vector pointer to test vector + */ + void (*add_rng_vector)(crypto_tester_t *this, rng_test_vector_t *vector); + + /** + * Destroy a crypto_tester_t. + */ + void (*destroy)(crypto_tester_t *this); +}; + +/** + * Create a crypto_tester instance. + */ +crypto_tester_t *crypto_tester_create(); + +#endif /* CRYPTO_TESTER_ @}*/