Skip to content

Commit

Permalink
crypto: testmgr - fuzz AEADs against their generic implementation
Browse files Browse the repository at this point in the history
When the extra crypto self-tests are enabled, test each AEAD algorithm
against its generic implementation when one is available.  This
involves: checking the algorithm properties for consistency, then
randomly generating test vectors using the generic implementation and
running them against the implementation under test.  Both good and bad
inputs are tested.

Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
  • Loading branch information
ebiggers authored and herbertx committed Apr 18, 2019
1 parent d435e10 commit 40153b1
Showing 1 changed file with 229 additions and 0 deletions.
229 changes: 229 additions & 0 deletions crypto/testmgr.c
Original file line number Diff line number Diff line change
Expand Up @@ -1767,6 +1767,226 @@ static int test_aead_vec(const char *driver, int enc,
return 0;
}

#ifdef CONFIG_CRYPTO_MANAGER_EXTRA_TESTS
/*
* Generate an AEAD test vector from the given implementation.
* Assumes the buffers in 'vec' were already allocated.
*/
static void generate_random_aead_testvec(struct aead_request *req,
struct aead_testvec *vec,
unsigned int maxkeysize,
unsigned int maxdatasize,
char *name, size_t max_namelen)
{
struct crypto_aead *tfm = crypto_aead_reqtfm(req);
const unsigned int ivsize = crypto_aead_ivsize(tfm);
unsigned int maxauthsize = crypto_aead_alg(tfm)->maxauthsize;
unsigned int authsize;
unsigned int total_len;
int i;
struct scatterlist src[2], dst;
u8 iv[MAX_IVLEN];
DECLARE_CRYPTO_WAIT(wait);

/* Key: length in [0, maxkeysize], but usually choose maxkeysize */
vec->klen = maxkeysize;
if (prandom_u32() % 4 == 0)
vec->klen = prandom_u32() % (maxkeysize + 1);
generate_random_bytes((u8 *)vec->key, vec->klen);
vec->setkey_error = crypto_aead_setkey(tfm, vec->key, vec->klen);

/* IV */
generate_random_bytes((u8 *)vec->iv, ivsize);

/* Tag length: in [0, maxauthsize], but usually choose maxauthsize */
authsize = maxauthsize;
if (prandom_u32() % 4 == 0)
authsize = prandom_u32() % (maxauthsize + 1);
if (WARN_ON(authsize > maxdatasize))
authsize = maxdatasize;
maxdatasize -= authsize;
vec->setauthsize_error = crypto_aead_setauthsize(tfm, authsize);

/* Plaintext and associated data */
total_len = generate_random_length(maxdatasize);
if (prandom_u32() % 4 == 0)
vec->alen = 0;
else
vec->alen = generate_random_length(total_len);
vec->plen = total_len - vec->alen;
generate_random_bytes((u8 *)vec->assoc, vec->alen);
generate_random_bytes((u8 *)vec->ptext, vec->plen);

vec->clen = vec->plen + authsize;

/*
* If the key or authentication tag size couldn't be set, no need to
* continue to encrypt.
*/
if (vec->setkey_error || vec->setauthsize_error)
goto done;

/* Ciphertext */
sg_init_table(src, 2);
i = 0;
if (vec->alen)
sg_set_buf(&src[i++], vec->assoc, vec->alen);
if (vec->plen)
sg_set_buf(&src[i++], vec->ptext, vec->plen);
sg_init_one(&dst, vec->ctext, vec->alen + vec->clen);
memcpy(iv, vec->iv, ivsize);
aead_request_set_callback(req, 0, crypto_req_done, &wait);
aead_request_set_crypt(req, src, &dst, vec->plen, iv);
aead_request_set_ad(req, vec->alen);
vec->crypt_error = crypto_wait_req(crypto_aead_encrypt(req), &wait);
if (vec->crypt_error == 0)
memmove((u8 *)vec->ctext, vec->ctext + vec->alen, vec->clen);
done:
snprintf(name, max_namelen,
"\"random: alen=%u plen=%u authsize=%u klen=%u\"",
vec->alen, vec->plen, authsize, vec->klen);
}

/*
* Test the AEAD algorithm represented by @req against the corresponding generic
* implementation, if one is available.
*/
static int test_aead_vs_generic_impl(const char *driver,
const struct alg_test_desc *test_desc,
struct aead_request *req,
struct cipher_test_sglists *tsgls)
{
struct crypto_aead *tfm = crypto_aead_reqtfm(req);
const unsigned int ivsize = crypto_aead_ivsize(tfm);
const unsigned int maxauthsize = crypto_aead_alg(tfm)->maxauthsize;
const unsigned int blocksize = crypto_aead_blocksize(tfm);
const unsigned int maxdatasize = (2 * PAGE_SIZE) - TESTMGR_POISON_LEN;
const char *algname = crypto_aead_alg(tfm)->base.cra_name;
const char *generic_driver = test_desc->generic_driver;
char _generic_driver[CRYPTO_MAX_ALG_NAME];
struct crypto_aead *generic_tfm = NULL;
struct aead_request *generic_req = NULL;
unsigned int maxkeysize;
unsigned int i;
struct aead_testvec vec = { 0 };
char vec_name[64];
struct testvec_config cfg;
char cfgname[TESTVEC_CONFIG_NAMELEN];
int err;

if (noextratests)
return 0;

if (!generic_driver) { /* Use default naming convention? */
err = build_generic_driver_name(algname, _generic_driver);
if (err)
return err;
generic_driver = _generic_driver;
}

if (strcmp(generic_driver, driver) == 0) /* Already the generic impl? */
return 0;

generic_tfm = crypto_alloc_aead(generic_driver, 0, 0);
if (IS_ERR(generic_tfm)) {
err = PTR_ERR(generic_tfm);
if (err == -ENOENT) {
pr_warn("alg: aead: skipping comparison tests for %s because %s is unavailable\n",
driver, generic_driver);
return 0;
}
pr_err("alg: aead: error allocating %s (generic impl of %s): %d\n",
generic_driver, algname, err);
return err;
}

generic_req = aead_request_alloc(generic_tfm, GFP_KERNEL);
if (!generic_req) {
err = -ENOMEM;
goto out;
}

/* Check the algorithm properties for consistency. */

if (maxauthsize != crypto_aead_alg(generic_tfm)->maxauthsize) {
pr_err("alg: aead: maxauthsize for %s (%u) doesn't match generic impl (%u)\n",
driver, maxauthsize,
crypto_aead_alg(generic_tfm)->maxauthsize);
err = -EINVAL;
goto out;
}

if (ivsize != crypto_aead_ivsize(generic_tfm)) {
pr_err("alg: aead: ivsize for %s (%u) doesn't match generic impl (%u)\n",
driver, ivsize, crypto_aead_ivsize(generic_tfm));
err = -EINVAL;
goto out;
}

if (blocksize != crypto_aead_blocksize(generic_tfm)) {
pr_err("alg: aead: blocksize for %s (%u) doesn't match generic impl (%u)\n",
driver, blocksize, crypto_aead_blocksize(generic_tfm));
err = -EINVAL;
goto out;
}

/*
* Now generate test vectors using the generic implementation, and test
* the other implementation against them.
*/

maxkeysize = 0;
for (i = 0; i < test_desc->suite.aead.count; i++)
maxkeysize = max_t(unsigned int, maxkeysize,
test_desc->suite.aead.vecs[i].klen);

vec.key = kmalloc(maxkeysize, GFP_KERNEL);
vec.iv = kmalloc(ivsize, GFP_KERNEL);
vec.assoc = kmalloc(maxdatasize, GFP_KERNEL);
vec.ptext = kmalloc(maxdatasize, GFP_KERNEL);
vec.ctext = kmalloc(maxdatasize, GFP_KERNEL);
if (!vec.key || !vec.iv || !vec.assoc || !vec.ptext || !vec.ctext) {
err = -ENOMEM;
goto out;
}

for (i = 0; i < fuzz_iterations * 8; i++) {
generate_random_aead_testvec(generic_req, &vec,
maxkeysize, maxdatasize,
vec_name, sizeof(vec_name));
generate_random_testvec_config(&cfg, cfgname, sizeof(cfgname));

err = test_aead_vec_cfg(driver, ENCRYPT, &vec, vec_name, &cfg,
req, tsgls);
if (err)
goto out;
err = test_aead_vec_cfg(driver, DECRYPT, &vec, vec_name, &cfg,
req, tsgls);
if (err)
goto out;
cond_resched();
}
err = 0;
out:
kfree(vec.key);
kfree(vec.iv);
kfree(vec.assoc);
kfree(vec.ptext);
kfree(vec.ctext);
crypto_free_aead(generic_tfm);
aead_request_free(generic_req);
return err;
}
#else /* !CONFIG_CRYPTO_MANAGER_EXTRA_TESTS */
static int test_aead_vs_generic_impl(const char *driver,
const struct alg_test_desc *test_desc,
struct aead_request *req,
struct cipher_test_sglists *tsgls)
{
return 0;
}
#endif /* !CONFIG_CRYPTO_MANAGER_EXTRA_TESTS */

static int test_aead(const char *driver, int enc,
const struct aead_test_suite *suite,
struct aead_request *req,
Expand Down Expand Up @@ -1826,6 +2046,10 @@ static int alg_test_aead(const struct alg_test_desc *desc, const char *driver,
goto out;

err = test_aead(driver, DECRYPT, suite, req, tsgls);
if (err)
goto out;

err = test_aead_vs_generic_impl(driver, desc, req, tsgls);
out:
free_cipher_test_sglists(tsgls);
aead_request_free(req);
Expand Down Expand Up @@ -3599,6 +3823,7 @@ static const struct alg_test_desc alg_test_descs[] = {
}
}, {
.alg = "ccm(aes)",
.generic_driver = "ccm_base(ctr(aes-generic),cbcmac(aes-generic))",
.test = alg_test_aead,
.fips_allowed = 1,
.suite = {
Expand Down Expand Up @@ -4017,6 +4242,7 @@ static const struct alg_test_desc alg_test_descs[] = {
}
}, {
.alg = "gcm(aes)",
.generic_driver = "gcm_base(ctr(aes-generic),ghash-generic)",
.test = alg_test_aead,
.fips_allowed = 1,
.suite = {
Expand Down Expand Up @@ -4288,20 +4514,23 @@ static const struct alg_test_desc alg_test_descs[] = {
}
}, {
.alg = "rfc4106(gcm(aes))",
.generic_driver = "rfc4106(gcm_base(ctr(aes-generic),ghash-generic))",
.test = alg_test_aead,
.fips_allowed = 1,
.suite = {
.aead = __VECS(aes_gcm_rfc4106_tv_template)
}
}, {
.alg = "rfc4309(ccm(aes))",
.generic_driver = "rfc4309(ccm_base(ctr(aes-generic),cbcmac(aes-generic)))",
.test = alg_test_aead,
.fips_allowed = 1,
.suite = {
.aead = __VECS(aes_ccm_rfc4309_tv_template)
}
}, {
.alg = "rfc4543(gcm(aes))",
.generic_driver = "rfc4543(gcm_base(ctr(aes-generic),ghash-generic))",
.test = alg_test_aead,
.suite = {
.aead = __VECS(aes_gcm_rfc4543_tv_template)
Expand Down

0 comments on commit 40153b1

Please sign in to comment.