Commit d8ea98aa authored by Eric Biggers's avatar Eric Biggers Committed by Herbert Xu

crypto: testmgr - test the shash API

For hash algorithms implemented using the "shash" algorithm type, test
both the ahash and shash APIs, not just the ahash API.

Testing the ahash API already tests the shash API indirectly, which is
normally good enough.  However, there have been corner cases where there
have been shash bugs that don't get exposed through the ahash API.  So,
update testmgr to test the shash API too.

This would have detected the arm64 SHA-1 and SHA-2 bugs for which fixes
were just sent out (https://patchwork.kernel.org/patch/10964843/ and
https://patchwork.kernel.org/patch/10965089/):

    alg: shash: sha1-ce test failed (wrong result) on test vector 0, cfg="init+finup aligned buffer"
    alg: shash: sha224-ce test failed (wrong result) on test vector 0, cfg="init+finup aligned buffer"
    alg: shash: sha256-ce test failed (wrong result) on test vector 0, cfg="init+finup aligned buffer"

This also would have detected the bugs fixed by commit 307508d1
("crypto: crct10dif-generic - fix use via crypto_shash_digest()") and
commit dec3d0b1
("crypto: x86/crct10dif-pcl - fix use via crypto_shash_digest()").
Signed-off-by: default avatarEric Biggers <ebiggers@google.com>
Acked-by: default avatarArd Biesheuvel <ard.biesheuvel@linaro.org>
Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
parent 6bd934de
...@@ -1037,6 +1037,205 @@ static void crypto_reenable_simd_for_test(void) ...@@ -1037,6 +1037,205 @@ static void crypto_reenable_simd_for_test(void)
} }
#endif /* !CONFIG_CRYPTO_MANAGER_EXTRA_TESTS */ #endif /* !CONFIG_CRYPTO_MANAGER_EXTRA_TESTS */
static int build_hash_sglist(struct test_sglist *tsgl,
const struct hash_testvec *vec,
const struct testvec_config *cfg,
unsigned int alignmask,
const struct test_sg_division *divs[XBUFSIZE])
{
struct kvec kv;
struct iov_iter input;
kv.iov_base = (void *)vec->plaintext;
kv.iov_len = vec->psize;
iov_iter_kvec(&input, WRITE, &kv, 1, vec->psize);
return build_test_sglist(tsgl, cfg->src_divs, alignmask, vec->psize,
&input, divs);
}
static int check_hash_result(const char *type,
const u8 *result, unsigned int digestsize,
const struct hash_testvec *vec,
const char *vec_name,
const char *driver,
const struct testvec_config *cfg)
{
if (memcmp(result, vec->digest, digestsize) != 0) {
pr_err("alg: %s: %s test failed (wrong result) on test vector %s, cfg=\"%s\"\n",
type, driver, vec_name, cfg->name);
return -EINVAL;
}
if (!testmgr_is_poison(&result[digestsize], TESTMGR_POISON_LEN)) {
pr_err("alg: %s: %s overran result buffer on test vector %s, cfg=\"%s\"\n",
type, driver, vec_name, cfg->name);
return -EOVERFLOW;
}
return 0;
}
static inline int check_shash_op(const char *op, int err,
const char *driver, const char *vec_name,
const struct testvec_config *cfg)
{
if (err)
pr_err("alg: shash: %s %s() failed with err %d on test vector %s, cfg=\"%s\"\n",
driver, op, err, vec_name, cfg->name);
return err;
}
static inline const void *sg_data(struct scatterlist *sg)
{
return page_address(sg_page(sg)) + sg->offset;
}
/* Test one hash test vector in one configuration, using the shash API */
static int test_shash_vec_cfg(const char *driver,
const struct hash_testvec *vec,
const char *vec_name,
const struct testvec_config *cfg,
struct shash_desc *desc,
struct test_sglist *tsgl,
u8 *hashstate)
{
struct crypto_shash *tfm = desc->tfm;
const unsigned int alignmask = crypto_shash_alignmask(tfm);
const unsigned int digestsize = crypto_shash_digestsize(tfm);
const unsigned int statesize = crypto_shash_statesize(tfm);
const struct test_sg_division *divs[XBUFSIZE];
unsigned int i;
u8 result[HASH_MAX_DIGESTSIZE + TESTMGR_POISON_LEN];
int err;
/* Set the key, if specified */
if (vec->ksize) {
err = crypto_shash_setkey(tfm, vec->key, vec->ksize);
if (err) {
if (err == vec->setkey_error)
return 0;
pr_err("alg: shash: %s setkey failed on test vector %s; expected_error=%d, actual_error=%d, flags=%#x\n",
driver, vec_name, vec->setkey_error, err,
crypto_shash_get_flags(tfm));
return err;
}
if (vec->setkey_error) {
pr_err("alg: shash: %s setkey unexpectedly succeeded on test vector %s; expected_error=%d\n",
driver, vec_name, vec->setkey_error);
return -EINVAL;
}
}
/* Build the scatterlist for the source data */
err = build_hash_sglist(tsgl, vec, cfg, alignmask, divs);
if (err) {
pr_err("alg: shash: %s: error preparing scatterlist for test vector %s, cfg=\"%s\"\n",
driver, vec_name, cfg->name);
return err;
}
/* Do the actual hashing */
testmgr_poison(desc->__ctx, crypto_shash_descsize(tfm));
testmgr_poison(result, digestsize + TESTMGR_POISON_LEN);
if (cfg->finalization_type == FINALIZATION_TYPE_DIGEST ||
vec->digest_error) {
/* Just using digest() */
if (tsgl->nents != 1)
return 0;
if (cfg->nosimd)
crypto_disable_simd_for_test();
err = crypto_shash_digest(desc, sg_data(&tsgl->sgl[0]),
tsgl->sgl[0].length, result);
if (cfg->nosimd)
crypto_reenable_simd_for_test();
if (err) {
if (err == vec->digest_error)
return 0;
pr_err("alg: shash: %s digest() failed on test vector %s; expected_error=%d, actual_error=%d, cfg=\"%s\"\n",
driver, vec_name, vec->digest_error, err,
cfg->name);
return err;
}
if (vec->digest_error) {
pr_err("alg: shash: %s digest() unexpectedly succeeded on test vector %s; expected_error=%d, cfg=\"%s\"\n",
driver, vec_name, vec->digest_error, cfg->name);
return -EINVAL;
}
goto result_ready;
}
/* Using init(), zero or more update(), then final() or finup() */
if (cfg->nosimd)
crypto_disable_simd_for_test();
err = crypto_shash_init(desc);
if (cfg->nosimd)
crypto_reenable_simd_for_test();
err = check_shash_op("init", err, driver, vec_name, cfg);
if (err)
return err;
for (i = 0; i < tsgl->nents; i++) {
if (i + 1 == tsgl->nents &&
cfg->finalization_type == FINALIZATION_TYPE_FINUP) {
if (divs[i]->nosimd)
crypto_disable_simd_for_test();
err = crypto_shash_finup(desc, sg_data(&tsgl->sgl[i]),
tsgl->sgl[i].length, result);
if (divs[i]->nosimd)
crypto_reenable_simd_for_test();
err = check_shash_op("finup", err, driver, vec_name,
cfg);
if (err)
return err;
goto result_ready;
}
if (divs[i]->nosimd)
crypto_disable_simd_for_test();
err = crypto_shash_update(desc, sg_data(&tsgl->sgl[i]),
tsgl->sgl[i].length);
if (divs[i]->nosimd)
crypto_reenable_simd_for_test();
err = check_shash_op("update", err, driver, vec_name, cfg);
if (err)
return err;
if (divs[i]->flush_type == FLUSH_TYPE_REIMPORT) {
/* Test ->export() and ->import() */
testmgr_poison(hashstate + statesize,
TESTMGR_POISON_LEN);
err = crypto_shash_export(desc, hashstate);
err = check_shash_op("export", err, driver, vec_name,
cfg);
if (err)
return err;
if (!testmgr_is_poison(hashstate + statesize,
TESTMGR_POISON_LEN)) {
pr_err("alg: shash: %s export() overran state buffer on test vector %s, cfg=\"%s\"\n",
driver, vec_name, cfg->name);
return -EOVERFLOW;
}
testmgr_poison(desc->__ctx, crypto_shash_descsize(tfm));
err = crypto_shash_import(desc, hashstate);
err = check_shash_op("import", err, driver, vec_name,
cfg);
if (err)
return err;
}
}
if (cfg->nosimd)
crypto_disable_simd_for_test();
err = crypto_shash_final(desc, result);
if (cfg->nosimd)
crypto_reenable_simd_for_test();
err = check_shash_op("final", err, driver, vec_name, cfg);
if (err)
return err;
result_ready:
return check_hash_result("shash", result, digestsize, vec, vec_name,
driver, cfg);
}
static int do_ahash_op(int (*op)(struct ahash_request *req), static int do_ahash_op(int (*op)(struct ahash_request *req),
struct ahash_request *req, struct ahash_request *req,
struct crypto_wait *wait, bool nosimd) struct crypto_wait *wait, bool nosimd)
...@@ -1054,31 +1253,32 @@ static int do_ahash_op(int (*op)(struct ahash_request *req), ...@@ -1054,31 +1253,32 @@ static int do_ahash_op(int (*op)(struct ahash_request *req),
return crypto_wait_req(err, wait); return crypto_wait_req(err, wait);
} }
static int check_nonfinal_hash_op(const char *op, int err, static int check_nonfinal_ahash_op(const char *op, int err,
u8 *result, unsigned int digestsize, u8 *result, unsigned int digestsize,
const char *driver, const char *vec_name, const char *driver, const char *vec_name,
const struct testvec_config *cfg) const struct testvec_config *cfg)
{ {
if (err) { if (err) {
pr_err("alg: hash: %s %s() failed with err %d on test vector %s, cfg=\"%s\"\n", pr_err("alg: ahash: %s %s() failed with err %d on test vector %s, cfg=\"%s\"\n",
driver, op, err, vec_name, cfg->name); driver, op, err, vec_name, cfg->name);
return err; return err;
} }
if (!testmgr_is_poison(result, digestsize)) { if (!testmgr_is_poison(result, digestsize)) {
pr_err("alg: hash: %s %s() used result buffer on test vector %s, cfg=\"%s\"\n", pr_err("alg: ahash: %s %s() used result buffer on test vector %s, cfg=\"%s\"\n",
driver, op, vec_name, cfg->name); driver, op, vec_name, cfg->name);
return -EINVAL; return -EINVAL;
} }
return 0; return 0;
} }
static int test_hash_vec_cfg(const char *driver, /* Test one hash test vector in one configuration, using the ahash API */
const struct hash_testvec *vec, static int test_ahash_vec_cfg(const char *driver,
const char *vec_name, const struct hash_testvec *vec,
const struct testvec_config *cfg, const char *vec_name,
struct ahash_request *req, const struct testvec_config *cfg,
struct test_sglist *tsgl, struct ahash_request *req,
u8 *hashstate) struct test_sglist *tsgl,
u8 *hashstate)
{ {
struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
const unsigned int alignmask = crypto_ahash_alignmask(tfm); const unsigned int alignmask = crypto_ahash_alignmask(tfm);
...@@ -1087,8 +1287,6 @@ static int test_hash_vec_cfg(const char *driver, ...@@ -1087,8 +1287,6 @@ static int test_hash_vec_cfg(const char *driver,
const u32 req_flags = CRYPTO_TFM_REQ_MAY_BACKLOG | cfg->req_flags; const u32 req_flags = CRYPTO_TFM_REQ_MAY_BACKLOG | cfg->req_flags;
const struct test_sg_division *divs[XBUFSIZE]; const struct test_sg_division *divs[XBUFSIZE];
DECLARE_CRYPTO_WAIT(wait); DECLARE_CRYPTO_WAIT(wait);
struct kvec _input;
struct iov_iter input;
unsigned int i; unsigned int i;
struct scatterlist *pending_sgl; struct scatterlist *pending_sgl;
unsigned int pending_len; unsigned int pending_len;
...@@ -1101,26 +1299,22 @@ static int test_hash_vec_cfg(const char *driver, ...@@ -1101,26 +1299,22 @@ static int test_hash_vec_cfg(const char *driver,
if (err) { if (err) {
if (err == vec->setkey_error) if (err == vec->setkey_error)
return 0; return 0;
pr_err("alg: hash: %s setkey failed on test vector %s; expected_error=%d, actual_error=%d, flags=%#x\n", pr_err("alg: ahash: %s setkey failed on test vector %s; expected_error=%d, actual_error=%d, flags=%#x\n",
driver, vec_name, vec->setkey_error, err, driver, vec_name, vec->setkey_error, err,
crypto_ahash_get_flags(tfm)); crypto_ahash_get_flags(tfm));
return err; return err;
} }
if (vec->setkey_error) { if (vec->setkey_error) {
pr_err("alg: hash: %s setkey unexpectedly succeeded on test vector %s; expected_error=%d\n", pr_err("alg: ahash: %s setkey unexpectedly succeeded on test vector %s; expected_error=%d\n",
driver, vec_name, vec->setkey_error); driver, vec_name, vec->setkey_error);
return -EINVAL; return -EINVAL;
} }
} }
/* Build the scatterlist for the source data */ /* Build the scatterlist for the source data */
_input.iov_base = (void *)vec->plaintext; err = build_hash_sglist(tsgl, vec, cfg, alignmask, divs);
_input.iov_len = vec->psize;
iov_iter_kvec(&input, WRITE, &_input, 1, vec->psize);
err = build_test_sglist(tsgl, cfg->src_divs, alignmask, vec->psize,
&input, divs);
if (err) { if (err) {
pr_err("alg: hash: %s: error preparing scatterlist for test vector %s, cfg=\"%s\"\n", pr_err("alg: ahash: %s: error preparing scatterlist for test vector %s, cfg=\"%s\"\n",
driver, vec_name, cfg->name); driver, vec_name, cfg->name);
return err; return err;
} }
...@@ -1140,13 +1334,13 @@ static int test_hash_vec_cfg(const char *driver, ...@@ -1140,13 +1334,13 @@ static int test_hash_vec_cfg(const char *driver,
if (err) { if (err) {
if (err == vec->digest_error) if (err == vec->digest_error)
return 0; return 0;
pr_err("alg: hash: %s digest() failed on test vector %s; expected_error=%d, actual_error=%d, cfg=\"%s\"\n", pr_err("alg: ahash: %s digest() failed on test vector %s; expected_error=%d, actual_error=%d, cfg=\"%s\"\n",
driver, vec_name, vec->digest_error, err, driver, vec_name, vec->digest_error, err,
cfg->name); cfg->name);
return err; return err;
} }
if (vec->digest_error) { if (vec->digest_error) {
pr_err("alg: hash: %s digest() unexpectedly succeeded on test vector %s; expected_error=%d, cfg=\"%s\"\n", pr_err("alg: ahash: %s digest() unexpectedly succeeded on test vector %s; expected_error=%d, cfg=\"%s\"\n",
driver, vec_name, vec->digest_error, cfg->name); driver, vec_name, vec->digest_error, cfg->name);
return -EINVAL; return -EINVAL;
} }
...@@ -1158,8 +1352,8 @@ static int test_hash_vec_cfg(const char *driver, ...@@ -1158,8 +1352,8 @@ static int test_hash_vec_cfg(const char *driver,
ahash_request_set_callback(req, req_flags, crypto_req_done, &wait); ahash_request_set_callback(req, req_flags, crypto_req_done, &wait);
ahash_request_set_crypt(req, NULL, result, 0); ahash_request_set_crypt(req, NULL, result, 0);
err = do_ahash_op(crypto_ahash_init, req, &wait, cfg->nosimd); err = do_ahash_op(crypto_ahash_init, req, &wait, cfg->nosimd);
err = check_nonfinal_hash_op("init", err, result, digestsize, err = check_nonfinal_ahash_op("init", err, result, digestsize,
driver, vec_name, cfg); driver, vec_name, cfg);
if (err) if (err)
return err; return err;
...@@ -1175,9 +1369,9 @@ static int test_hash_vec_cfg(const char *driver, ...@@ -1175,9 +1369,9 @@ static int test_hash_vec_cfg(const char *driver,
pending_len); pending_len);
err = do_ahash_op(crypto_ahash_update, req, &wait, err = do_ahash_op(crypto_ahash_update, req, &wait,
divs[i]->nosimd); divs[i]->nosimd);
err = check_nonfinal_hash_op("update", err, err = check_nonfinal_ahash_op("update", err,
result, digestsize, result, digestsize,
driver, vec_name, cfg); driver, vec_name, cfg);
if (err) if (err)
return err; return err;
pending_sgl = NULL; pending_sgl = NULL;
...@@ -1188,23 +1382,23 @@ static int test_hash_vec_cfg(const char *driver, ...@@ -1188,23 +1382,23 @@ static int test_hash_vec_cfg(const char *driver,
testmgr_poison(hashstate + statesize, testmgr_poison(hashstate + statesize,
TESTMGR_POISON_LEN); TESTMGR_POISON_LEN);
err = crypto_ahash_export(req, hashstate); err = crypto_ahash_export(req, hashstate);
err = check_nonfinal_hash_op("export", err, err = check_nonfinal_ahash_op("export", err,
result, digestsize, result, digestsize,
driver, vec_name, cfg); driver, vec_name, cfg);
if (err) if (err)
return err; return err;
if (!testmgr_is_poison(hashstate + statesize, if (!testmgr_is_poison(hashstate + statesize,
TESTMGR_POISON_LEN)) { TESTMGR_POISON_LEN)) {
pr_err("alg: hash: %s export() overran state buffer on test vector %s, cfg=\"%s\"\n", pr_err("alg: ahash: %s export() overran state buffer on test vector %s, cfg=\"%s\"\n",
driver, vec_name, cfg->name); driver, vec_name, cfg->name);
return -EOVERFLOW; return -EOVERFLOW;
} }
testmgr_poison(req->__ctx, crypto_ahash_reqsize(tfm)); testmgr_poison(req->__ctx, crypto_ahash_reqsize(tfm));
err = crypto_ahash_import(req, hashstate); err = crypto_ahash_import(req, hashstate);
err = check_nonfinal_hash_op("import", err, err = check_nonfinal_ahash_op("import", err,
result, digestsize, result, digestsize,
driver, vec_name, cfg); driver, vec_name, cfg);
if (err) if (err)
return err; return err;
} }
...@@ -1218,13 +1412,13 @@ static int test_hash_vec_cfg(const char *driver, ...@@ -1218,13 +1412,13 @@ static int test_hash_vec_cfg(const char *driver,
if (cfg->finalization_type == FINALIZATION_TYPE_FINAL) { if (cfg->finalization_type == FINALIZATION_TYPE_FINAL) {
/* finish with update() and final() */ /* finish with update() and final() */
err = do_ahash_op(crypto_ahash_update, req, &wait, cfg->nosimd); err = do_ahash_op(crypto_ahash_update, req, &wait, cfg->nosimd);
err = check_nonfinal_hash_op("update", err, result, digestsize, err = check_nonfinal_ahash_op("update", err, result, digestsize,
driver, vec_name, cfg); driver, vec_name, cfg);
if (err) if (err)
return err; return err;
err = do_ahash_op(crypto_ahash_final, req, &wait, cfg->nosimd); err = do_ahash_op(crypto_ahash_final, req, &wait, cfg->nosimd);
if (err) { if (err) {
pr_err("alg: hash: %s final() failed with err %d on test vector %s, cfg=\"%s\"\n", pr_err("alg: ahash: %s final() failed with err %d on test vector %s, cfg=\"%s\"\n",
driver, err, vec_name, cfg->name); driver, err, vec_name, cfg->name);
return err; return err;
} }
...@@ -1232,31 +1426,49 @@ static int test_hash_vec_cfg(const char *driver, ...@@ -1232,31 +1426,49 @@ static int test_hash_vec_cfg(const char *driver,
/* finish with finup() */ /* finish with finup() */
err = do_ahash_op(crypto_ahash_finup, req, &wait, cfg->nosimd); err = do_ahash_op(crypto_ahash_finup, req, &wait, cfg->nosimd);
if (err) { if (err) {
pr_err("alg: hash: %s finup() failed with err %d on test vector %s, cfg=\"%s\"\n", pr_err("alg: ahash: %s finup() failed with err %d on test vector %s, cfg=\"%s\"\n",
driver, err, vec_name, cfg->name); driver, err, vec_name, cfg->name);
return err; return err;
} }
} }
result_ready: result_ready:
/* Check that the algorithm produced the correct digest */ return check_hash_result("ahash", result, digestsize, vec, vec_name,
if (memcmp(result, vec->digest, digestsize) != 0) { driver, cfg);
pr_err("alg: hash: %s test failed (wrong result) on test vector %s, cfg=\"%s\"\n", }
driver, vec_name, cfg->name);
return -EINVAL; static int test_hash_vec_cfg(const char *driver,
} const struct hash_testvec *vec,
if (!testmgr_is_poison(&result[digestsize], TESTMGR_POISON_LEN)) { const char *vec_name,
pr_err("alg: hash: %s overran result buffer on test vector %s, cfg=\"%s\"\n", const struct testvec_config *cfg,
driver, vec_name, cfg->name); struct ahash_request *req,
return -EOVERFLOW; struct shash_desc *desc,
struct test_sglist *tsgl,
u8 *hashstate)
{
int err;
/*
* For algorithms implemented as "shash", most bugs will be detected by
* both the shash and ahash tests. Test the shash API first so that the
* failures involve less indirection, so are easier to debug.
*/
if (desc) {
err = test_shash_vec_cfg(driver, vec, vec_name, cfg, desc, tsgl,
hashstate);
if (err)
return err;
} }
return 0; return test_ahash_vec_cfg(driver, vec, vec_name, cfg, req, tsgl,
hashstate);
} }
static int test_hash_vec(const char *driver, const struct hash_testvec *vec, static int test_hash_vec(const char *driver, const struct hash_testvec *vec,
unsigned int vec_num, struct ahash_request *req, unsigned int vec_num, struct ahash_request *req,
struct test_sglist *tsgl, u8 *hashstate) struct shash_desc *desc, struct test_sglist *tsgl,
u8 *hashstate)
{ {
char vec_name[16]; char vec_name[16];
unsigned int i; unsigned int i;
...@@ -1267,7 +1479,7 @@ static int test_hash_vec(const char *driver, const struct hash_testvec *vec, ...@@ -1267,7 +1479,7 @@ static int test_hash_vec(const char *driver, const struct hash_testvec *vec,
for (i = 0; i < ARRAY_SIZE(default_hash_testvec_configs); i++) { for (i = 0; i < ARRAY_SIZE(default_hash_testvec_configs); i++) {
err = test_hash_vec_cfg(driver, vec, vec_name, err = test_hash_vec_cfg(driver, vec, vec_name,
&default_hash_testvec_configs[i], &default_hash_testvec_configs[i],
req, tsgl, hashstate); req, desc, tsgl, hashstate);
if (err) if (err)
return err; return err;
} }
...@@ -1281,7 +1493,7 @@ static int test_hash_vec(const char *driver, const struct hash_testvec *vec, ...@@ -1281,7 +1493,7 @@ static int test_hash_vec(const char *driver, const struct hash_testvec *vec,
generate_random_testvec_config(&cfg, cfgname, generate_random_testvec_config(&cfg, cfgname,
sizeof(cfgname)); sizeof(cfgname));
err = test_hash_vec_cfg(driver, vec, vec_name, &cfg, err = test_hash_vec_cfg(driver, vec, vec_name, &cfg,
req, tsgl, hashstate); req, desc, tsgl, hashstate);
if (err) if (err)
return err; return err;
} }
...@@ -1343,6 +1555,7 @@ static int test_hash_vs_generic_impl(const char *driver, ...@@ -1343,6 +1555,7 @@ static int test_hash_vs_generic_impl(const char *driver,
const char *generic_driver, const char *generic_driver,
unsigned int maxkeysize, unsigned int maxkeysize,
struct ahash_request *req, struct ahash_request *req,
struct shash_desc *desc,
struct test_sglist *tsgl, struct test_sglist *tsgl,
u8 *hashstate) u8 *hashstate)
{ {
...@@ -1423,7 +1636,7 @@ static int test_hash_vs_generic_impl(const char *driver, ...@@ -1423,7 +1636,7 @@ static int test_hash_vs_generic_impl(const char *driver,
generate_random_testvec_config(&cfg, cfgname, sizeof(cfgname)); generate_random_testvec_config(&cfg, cfgname, sizeof(cfgname));
err = test_hash_vec_cfg(driver, &vec, vec_name, &cfg, err = test_hash_vec_cfg(driver, &vec, vec_name, &cfg,
req, tsgl, hashstate); req, desc, tsgl, hashstate);
if (err) if (err)
goto out; goto out;
cond_resched(); cond_resched();
...@@ -1441,6 +1654,7 @@ static int test_hash_vs_generic_impl(const char *driver, ...@@ -1441,6 +1654,7 @@ static int test_hash_vs_generic_impl(const char *driver,
const char *generic_driver, const char *generic_driver,
unsigned int maxkeysize, unsigned int maxkeysize,
struct ahash_request *req, struct ahash_request *req,
struct shash_desc *desc,
struct test_sglist *tsgl, struct test_sglist *tsgl,
u8 *hashstate) u8 *hashstate)
{ {
...@@ -1448,26 +1662,67 @@ static int test_hash_vs_generic_impl(const char *driver, ...@@ -1448,26 +1662,67 @@ static int test_hash_vs_generic_impl(const char *driver,
} }
#endif /* !CONFIG_CRYPTO_MANAGER_EXTRA_TESTS */ #endif /* !CONFIG_CRYPTO_MANAGER_EXTRA_TESTS */
static int alloc_shash(const char *driver, u32 type, u32 mask,
struct crypto_shash **tfm_ret,
struct shash_desc **desc_ret)
{
struct crypto_shash *tfm;
struct shash_desc *desc;
tfm = crypto_alloc_shash(driver, type, mask);
if (IS_ERR(tfm)) {
if (PTR_ERR(tfm) == -ENOENT) {
/*
* This algorithm is only available through the ahash
* API, not the shash API, so skip the shash tests.
*/
return 0;
}
pr_err("alg: hash: failed to allocate shash transform for %s: %ld\n",
driver, PTR_ERR(tfm));
return PTR_ERR(tfm);
}
desc = kmalloc(sizeof(*desc) + crypto_shash_descsize(tfm), GFP_KERNEL);
if (!desc) {
crypto_free_shash(tfm);
return -ENOMEM;
}
desc->tfm = tfm;
*tfm_ret = tfm;
*desc_ret = desc;
return 0;
}
static int __alg_test_hash(const struct hash_testvec *vecs, static int __alg_test_hash(const struct hash_testvec *vecs,
unsigned int num_vecs, const char *driver, unsigned int num_vecs, const char *driver,
u32 type, u32 mask, u32 type, u32 mask,
const char *generic_driver, unsigned int maxkeysize) const char *generic_driver, unsigned int maxkeysize)
{ {
struct crypto_ahash *tfm; struct crypto_ahash *atfm = NULL;
struct ahash_request *req = NULL; struct ahash_request *req = NULL;
struct crypto_shash *stfm = NULL;
struct shash_desc *desc = NULL;
struct test_sglist *tsgl = NULL; struct test_sglist *tsgl = NULL;
u8 *hashstate = NULL; u8 *hashstate = NULL;
unsigned int statesize;
unsigned int i; unsigned int i;
int err; int err;
tfm = crypto_alloc_ahash(driver, type, mask); /*
if (IS_ERR(tfm)) { * Always test the ahash API. This works regardless of whether the
* algorithm is implemented as ahash or shash.
*/
atfm = crypto_alloc_ahash(driver, type, mask);
if (IS_ERR(atfm)) {
pr_err("alg: hash: failed to allocate transform for %s: %ld\n", pr_err("alg: hash: failed to allocate transform for %s: %ld\n",
driver, PTR_ERR(tfm)); driver, PTR_ERR(atfm));
return PTR_ERR(tfm); return PTR_ERR(atfm);
} }
req = ahash_request_alloc(tfm, GFP_KERNEL); req = ahash_request_alloc(atfm, GFP_KERNEL);
if (!req) { if (!req) {
pr_err("alg: hash: failed to allocate request for %s\n", pr_err("alg: hash: failed to allocate request for %s\n",
driver); driver);
...@@ -1475,6 +1730,14 @@ static int __alg_test_hash(const struct hash_testvec *vecs, ...@@ -1475,6 +1730,14 @@ static int __alg_test_hash(const struct hash_testvec *vecs,
goto out; goto out;
} }
/*
* If available also test the shash API, to cover corner cases that may
* be missed by testing the ahash API only.
*/
err = alloc_shash(driver, type, mask, &stfm, &desc);
if (err)
goto out;
tsgl = kmalloc(sizeof(*tsgl), GFP_KERNEL); tsgl = kmalloc(sizeof(*tsgl), GFP_KERNEL);
if (!tsgl || init_test_sglist(tsgl) != 0) { if (!tsgl || init_test_sglist(tsgl) != 0) {
pr_err("alg: hash: failed to allocate test buffers for %s\n", pr_err("alg: hash: failed to allocate test buffers for %s\n",
...@@ -1485,8 +1748,10 @@ static int __alg_test_hash(const struct hash_testvec *vecs, ...@@ -1485,8 +1748,10 @@ static int __alg_test_hash(const struct hash_testvec *vecs,
goto out; goto out;
} }
hashstate = kmalloc(crypto_ahash_statesize(tfm) + TESTMGR_POISON_LEN, statesize = crypto_ahash_statesize(atfm);
GFP_KERNEL); if (stfm)
statesize = max(statesize, crypto_shash_statesize(stfm));
hashstate = kmalloc(statesize + TESTMGR_POISON_LEN, GFP_KERNEL);
if (!hashstate) { if (!hashstate) {
pr_err("alg: hash: failed to allocate hash state buffer for %s\n", pr_err("alg: hash: failed to allocate hash state buffer for %s\n",
driver); driver);
...@@ -1495,20 +1760,23 @@ static int __alg_test_hash(const struct hash_testvec *vecs, ...@@ -1495,20 +1760,23 @@ static int __alg_test_hash(const struct hash_testvec *vecs,
} }
for (i = 0; i < num_vecs; i++) { for (i = 0; i < num_vecs; i++) {
err = test_hash_vec(driver, &vecs[i], i, req, tsgl, hashstate); err = test_hash_vec(driver, &vecs[i], i, req, desc, tsgl,
hashstate);
if (err) if (err)
goto out; goto out;
} }
err = test_hash_vs_generic_impl(driver, generic_driver, maxkeysize, req, err = test_hash_vs_generic_impl(driver, generic_driver, maxkeysize, req,
tsgl, hashstate); desc, tsgl, hashstate);
out: out:
kfree(hashstate); kfree(hashstate);
if (tsgl) { if (tsgl) {
destroy_test_sglist(tsgl); destroy_test_sglist(tsgl);
kfree(tsgl); kfree(tsgl);
} }
kfree(desc);
crypto_free_shash(stfm);
ahash_request_free(req); ahash_request_free(req);
crypto_free_ahash(tfm); crypto_free_ahash(atfm);
return err; return err;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment