diff --git a/src/libstrongswan/crypto/crypto_factory.c b/src/libstrongswan/crypto/crypto_factory.c index 3fce7b72b..c4c045175 100644 --- a/src/libstrongswan/crypto/crypto_factory.c +++ b/src/libstrongswan/crypto/crypto_factory.c @@ -368,6 +368,20 @@ METHOD(crypto_factory_t, create_kdf, kdf_t*, { if (entry->algo == algo) { + if (this->test_on_create) + { + kdf_test_args_t test_args = {}; + + va_start(test_args.args, algo); + if (!this->tester->test_kdf(this->tester, algo, + entry->create_kdf, &test_args, NULL, + default_plugin_name)) + { + va_end(test_args.args); + continue; + } + va_end(test_args.args); + } va_start(args, algo); kdf = entry->create_kdf(algo, args); va_end(args); @@ -787,8 +801,17 @@ METHOD(crypto_factory_t, add_kdf, bool, private_crypto_factory_t *this, key_derivation_function_t algo, const char *plugin_name, kdf_constructor_t create) { - add_entry(this, this->kdfs, algo, plugin_name, 0, create); - return TRUE; + u_int speed = 0; + + if (!this->test_on_add || + this->tester->test_kdf(this->tester, algo, create, NULL, + this->bench ? &speed : NULL, plugin_name)) + { + add_entry(this, this->kdfs, algo, plugin_name, 0, create); + return TRUE; + } + this->test_failures++; + return FALSE; } METHOD(crypto_factory_t, remove_kdf, void, @@ -1255,6 +1278,8 @@ METHOD(crypto_factory_t, add_test_vector, void, return this->tester->add_prf_vector(this->tester, vector); case EXTENDED_OUTPUT_FUNCTION: return this->tester->add_xof_vector(this->tester, vector); + case KEY_DERIVATION_FUNCTION: + return this->tester->add_kdf_vector(this->tester, vector); case DETERMINISTIC_RANDOM_BIT_GENERATOR: return this->tester->add_drbg_vector(this->tester, vector); case RANDOM_NUMBER_GENERATOR: @@ -1318,6 +1343,10 @@ METHOD(enumerator_t, verify_enumerate, bool, *valid = this->tester->test_xof(this->tester, entry->algo, entry->create_xof, NULL, entry->plugin_name); break; + case KEY_DERIVATION_FUNCTION: + *valid = this->tester->test_kdf(this->tester, entry->algo, + entry->create_kdf, NULL, NULL, entry->plugin_name); + break; case DETERMINISTIC_RANDOM_BIT_GENERATOR: *valid = this->tester->test_drbg(this->tester, entry->algo, entry->create_drbg, NULL, entry->plugin_name); @@ -1373,6 +1402,9 @@ METHOD(crypto_factory_t, create_verify_enumerator, enumerator_t*, case EXTENDED_OUTPUT_FUNCTION: inner = this->xofs->create_enumerator(this->xofs); break; + case KEY_DERIVATION_FUNCTION: + inner = this->kdfs->create_enumerator(this->kdfs); + break; case DETERMINISTIC_RANDOM_BIT_GENERATOR: inner = this->drbgs->create_enumerator(this->drbgs); break; diff --git a/src/libstrongswan/crypto/crypto_tester.c b/src/libstrongswan/crypto/crypto_tester.c index 04ed2288c..5fd7d2ba9 100644 --- a/src/libstrongswan/crypto/crypto_tester.c +++ b/src/libstrongswan/crypto/crypto_tester.c @@ -69,6 +69,11 @@ struct private_crypto_tester_t { */ linked_list_t *xof; + /** + * List of KDF test vectors + */ + linked_list_t *kdf; + /** * List of DRBG test vectors */ @@ -1186,6 +1191,185 @@ failure: return !failed; } + + +/** + * Create a KDF using the given arguments + */ +static kdf_t *create_kdf_args(kdf_constructor_t create, + key_derivation_function_t alg, ...) +{ + va_list args; + kdf_t *kdf; + + va_start(args, alg); + kdf = create(alg, args); + va_end(args); + return kdf; +} + +/** + * Create a KDF using arguments from the given test vector + */ +static kdf_t *create_kdf_vector(kdf_constructor_t create, + key_derivation_function_t alg, + kdf_test_vector_t *vector) +{ + switch (alg) + { + case KDF_PRF_PLUS: + return create_kdf_args(create, alg, vector->arg.prf); + case KDF_UNDEFINED: + break; + } + return NULL; +} + +/** + * Check if the given test vector applies to the passed arguments + */ +static bool kdf_vector_applies(key_derivation_function_t alg, + kdf_test_args_t *args, kdf_test_vector_t *vector) +{ + bool applies = FALSE; + + switch (alg) + { + case KDF_PRF_PLUS: + { + pseudo_random_function_t prf; + VA_ARGS_VGET(args->args, prf); + applies = (prf == vector->arg.prf); + break; + } + case KDF_UNDEFINED: + break; + } + return applies; +} + +METHOD(crypto_tester_t, test_kdf, bool, + private_crypto_tester_t *this, key_derivation_function_t alg, + kdf_constructor_t create, kdf_test_args_t *args, u_int *speed, + const char *plugin_name) +{ + enumerator_t *enumerator; + kdf_test_vector_t *vector; + va_list copy; + bool failed = FALSE; + u_int tested = 0, construction_failed = 0; + + enumerator = this->kdf->create_enumerator(this->kdf); + while (enumerator->enumerate(enumerator, &vector)) + { + kdf_t *kdf; + chunk_t out = chunk_empty; + + if (vector->alg != alg || + (args && !kdf_vector_applies(alg, args, vector))) + { + continue; + } + + tested++; + failed = TRUE; + if (args) + { + va_copy(copy, args->args); + kdf = create(alg, copy); + va_end(copy); + } + else + { + kdf = create_kdf_vector(create, alg, vector); + } + if (!kdf) + { + if (args) + { + DBG1(DBG_LIB, "disabled %N[%s]: creating instance failed", + key_derivation_function_names, alg, plugin_name); + break; + } + /* while there could be a problem, the constructor might just not + * be able to create an instance for this test vector, we check + * for that at the end */ + construction_failed++; + failed = FALSE; + continue; + } + + if (vector->key.len && + !kdf->set_param(kdf, KDF_PARAM_KEY, vector->key)) + { + goto failure; + } + if (vector->salt.len && + !kdf->set_param(kdf, KDF_PARAM_SALT, vector->salt)) + { + goto failure; + } + /* allocated bytes */ + if (!kdf->allocate_bytes(kdf, vector->out.len, &out)) + { + goto failure; + } + if (!chunk_equals(out, vector->out)) + { + goto failure; + } + /* bytes to existing buffer */ + memset(out.ptr, 0, out.len); + if (!kdf->get_bytes(kdf, out.len, out.ptr)) + { + goto failure; + } + if (!chunk_equals(out, vector->out)) + { + goto failure; + } + + failed = FALSE; +failure: + kdf->destroy(kdf); + chunk_free(&out); + if (failed) + { + DBG1(DBG_LIB, "disabled %N[%s]: %s test vector failed", + key_derivation_function_names, alg, plugin_name, + get_name(vector)); + break; + } + } + enumerator->destroy(enumerator); + if (!tested) + { + DBG1(DBG_LIB, "%s %N[%s]: no test vectors found", + this->required ? "disabled" : "enabled ", + key_derivation_function_names, alg, plugin_name); + return !this->required; + } + tested -= construction_failed; + if (!tested) + { + DBG1(DBG_LIB, "%s %N[%s]: unable to apply any available test vectors", + this->required ? "disabled" : "enabled ", + key_derivation_function_names, alg, plugin_name); + return !this->required; + } + if (!failed) + { + if (speed) + { + DBG2(DBG_LIB, "benchmarking for %N is currently not supported", + key_derivation_function_names, alg); + } + DBG1(DBG_LIB, "enabled %N[%s]: passed %u test vectors", + key_derivation_function_names, alg, plugin_name, tested); + } + return !failed; +} + /** * Benchmark a DRBG */ @@ -1622,6 +1806,12 @@ METHOD(crypto_tester_t, add_xof_vector, void, this->xof->insert_last(this->xof, vector); } +METHOD(crypto_tester_t, add_kdf_vector, void, + private_crypto_tester_t *this, kdf_test_vector_t *vector) +{ + this->kdf->insert_last(this->kdf, vector); +} + METHOD(crypto_tester_t, add_drbg_vector, void, private_crypto_tester_t *this, drbg_test_vector_t *vector) { @@ -1649,6 +1839,7 @@ METHOD(crypto_tester_t, destroy, void, this->hasher->destroy(this->hasher); this->prf->destroy(this->prf); this->xof->destroy(this->xof); + this->kdf->destroy(this->kdf); this->drbg->destroy(this->drbg); this->rng->destroy(this->rng); this->dh->destroy(this->dh); @@ -1670,6 +1861,7 @@ crypto_tester_t *crypto_tester_create() .test_hasher = _test_hasher, .test_prf = _test_prf, .test_xof = _test_xof, + .test_kdf = _test_kdf, .test_drbg = _test_drbg, .test_rng = _test_rng, .test_dh = _test_dh, @@ -1679,6 +1871,7 @@ crypto_tester_t *crypto_tester_create() .add_hasher_vector = _add_hasher_vector, .add_prf_vector = _add_prf_vector, .add_xof_vector = _add_xof_vector, + .add_kdf_vector = _add_kdf_vector, .add_drbg_vector = _add_drbg_vector, .add_rng_vector = _add_rng_vector, .add_dh_vector = _add_dh_vector, @@ -1690,6 +1883,7 @@ crypto_tester_t *crypto_tester_create() .hasher = linked_list_create(), .prf = linked_list_create(), .xof = linked_list_create(), + .kdf = linked_list_create(), .drbg = linked_list_create(), .rng = linked_list_create(), .dh = linked_list_create(), diff --git a/src/libstrongswan/crypto/crypto_tester.h b/src/libstrongswan/crypto/crypto_tester.h index 04de79158..4e4e62303 100644 --- a/src/libstrongswan/crypto/crypto_tester.h +++ b/src/libstrongswan/crypto/crypto_tester.h @@ -32,6 +32,8 @@ 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 xof_test_vector_t xof_test_vector_t; +typedef struct kdf_test_vector_t kdf_test_vector_t; +typedef struct kdf_test_args_t kdf_test_args_t; typedef struct drbg_test_vector_t drbg_test_vector_t; typedef struct rng_test_vector_t rng_test_vector_t; typedef struct dh_test_vector_t dh_test_vector_t; @@ -130,6 +132,26 @@ struct xof_test_vector_t { u_char *out; }; +struct kdf_test_vector_t { + /** kdf algorithm this test vector tests */ + key_derivation_function_t alg; + /** argument passed to constructor, type depends on alg */ + union { + pseudo_random_function_t prf; + } arg; + /** optional key */ + chunk_t key; + /** optional salt */ + chunk_t salt; + /** expected output */ + chunk_t out; +}; + +struct kdf_test_args_t { + /** the arguments used to construct the KDF */ + va_list args; +}; + struct drbg_test_vector_t { /** drbg type this test vector tests */ drbg_type_t type; @@ -256,6 +278,22 @@ struct crypto_tester_t { bool (*test_xof)(crypto_tester_t *this, ext_out_function_t alg, xof_constructor_t create, u_int *speed, const char *plugin_name); + /** + * Test a KDF algorithm. + * + * If constructor arguments are passed, only matching test vectors are + * tried. Otherwise, all are tried and implementations are allowed to fail + * construction with unsupported arguments. + * + * @param alg algorithm to test + * @param create constructor function for the XOF + * @param args optional arguments to pass to constructor + * @param speed speed test result, NULL to omit + * @return TRUE if test passed + */ + bool (*test_kdf)(crypto_tester_t *this, key_derivation_function_t alg, + kdf_constructor_t create, kdf_test_args_t *args, + u_int *speed, const char *plugin_name); /** * Test a DRBG type. * @@ -332,6 +370,13 @@ struct crypto_tester_t { */ void (*add_xof_vector)(crypto_tester_t *this, xof_test_vector_t *vector); + /** + * Add a test vector to test a KDF. + * + * @param vector pointer to test vector + */ + void (*add_kdf_vector)(crypto_tester_t *this, kdf_test_vector_t *vector); + /** * Add a test vector to test a DRBG. * diff --git a/src/libstrongswan/tests/suites/test_vectors.c b/src/libstrongswan/tests/suites/test_vectors.c index 0cc862fe6..78428e065 100644 --- a/src/libstrongswan/tests/suites/test_vectors.c +++ b/src/libstrongswan/tests/suites/test_vectors.c @@ -29,6 +29,7 @@ static transform_type_t tfs[] = { HASH_ALGORITHM, PSEUDO_RANDOM_FUNCTION, EXTENDED_OUTPUT_FUNCTION, + KEY_DERIVATION_FUNCTION, DETERMINISTIC_RANDOM_BIT_GENERATOR, RANDOM_NUMBER_GENERATOR, DIFFIE_HELLMAN_GROUP,