/* * Set if trying to decrypt an inauthentic ciphertext with this * algorithm might result in EINVAL rather than EBADMSG, due to other * validation the algorithm does on the inputs such as length checks.
*/ unsignedint einval_allowed : 1;
/* * Set if this algorithm requires that the IV be located at the end of * the AAD buffer, in addition to being given in the normal way. The * behavior when the two IV copies differ is implementation-defined.
*/ unsignedint aad_iv : 1;
};
struct alg_test_desc { constchar *alg; constchar *generic_driver; int (*test)(conststruct alg_test_desc *desc, constchar *driver,
u32 type, u32 mask); int fips_allowed; /* set if alg is allowed in fips mode */
/* Is the memory region still fully poisoned? */ staticinlinebool testmgr_is_poison(constvoid *addr, size_t len)
{ return memchr_inv(addr, TESTMGR_POISON_BYTE, len) == NULL;
}
/* flush type for hash algorithms */ enum flush_type { /* merge with update of previous buffer(s) */
FLUSH_TYPE_NONE = 0,
/* update with previous buffer(s) before doing this one */
FLUSH_TYPE_FLUSH,
/* likewise, but also export and re-import the intermediate state */
FLUSH_TYPE_REIMPORT,
};
/* finalization function for hash algorithms */ enum finalization_type {
FINALIZATION_TYPE_FINAL, /* use final() */
FINALIZATION_TYPE_FINUP, /* use finup() */
FINALIZATION_TYPE_DIGEST, /* use digest() */
};
/* * Whether the crypto operation will occur in-place, and if so whether the * source and destination scatterlist pointers will coincide (req->src == * req->dst), or whether they'll merely point to two separate scatterlists * (req->src != req->dst) that reference the same underlying memory. * * This is only relevant for algorithm types that support in-place operation.
*/ enum inplace_mode {
OUT_OF_PLACE,
INPLACE_ONE_SGLIST,
INPLACE_TWO_SGLISTS,
};
#define TEST_SG_TOTAL 10000
/** * struct test_sg_division - description of a scatterlist entry * * This struct describes one entry of a scatterlist being constructed to check a * crypto test vector. * * @proportion_of_total: length of this chunk relative to the total length, * given as a proportion out of TEST_SG_TOTAL so that it * scales to fit any test vector * @offset: byte offset into a 2-page buffer at which this chunk will start * @offset_relative_to_alignmask: if true, add the algorithm's alignmask to the * @offset * @flush_type: for hashes, whether an update() should be done now vs. * continuing to accumulate data * @nosimd: if doing the pending update(), do it with SIMD disabled?
*/ struct test_sg_division { unsignedint proportion_of_total; unsignedint offset; bool offset_relative_to_alignmask; enum flush_type flush_type; bool nosimd;
};
/** * struct testvec_config - configuration for testing a crypto test vector * * This struct describes the data layout and other parameters with which each * crypto test vector can be tested. * * @name: name of this config, logged for debugging purposes if a test fails * @inplace_mode: whether and how to operate on the data in-place, if applicable * @req_flags: extra request_flags, e.g. CRYPTO_TFM_REQ_MAY_SLEEP * @src_divs: description of how to arrange the source scatterlist * @dst_divs: description of how to arrange the dst scatterlist, if applicable * for the algorithm type. Defaults to @src_divs if unset. * @iv_offset: misalignment of the IV in the range [0..MAX_ALGAPI_ALIGNMASK+1], * where 0 is aligned to a 2*(MAX_ALGAPI_ALIGNMASK+1) byte boundary * @iv_offset_relative_to_alignmask: if true, add the algorithm's alignmask to * the @iv_offset * @key_offset: misalignment of the key, where 0 is default alignment * @key_offset_relative_to_alignmask: if true, add the algorithm's alignmask to * the @key_offset * @finalization_type: what finalization function to use for hashes * @nosimd: execute with SIMD disabled? Requires !CRYPTO_TFM_REQ_MAY_SLEEP. * This applies to the parts of the operation that aren't controlled * individually by @nosimd_setkey or @src_divs[].nosimd. * @nosimd_setkey: set the key (if applicable) with SIMD disabled? Requires * !CRYPTO_TFM_REQ_MAY_SLEEP.
*/ struct testvec_config { constchar *name; enum inplace_mode inplace_mode;
u32 req_flags; struct test_sg_division src_divs[XBUFSIZE]; struct test_sg_division dst_divs[XBUFSIZE]; unsignedint iv_offset; unsignedint key_offset; bool iv_offset_relative_to_alignmask; bool key_offset_relative_to_alignmask; enum finalization_type finalization_type; bool nosimd; bool nosimd_setkey;
};
#define TESTVEC_CONFIG_NAMELEN 192
/* * The following are the lists of testvec_configs to test for each algorithm * type when the "fast" crypto self-tests are enabled. They aim to provide good * test coverage, while keeping the test time much shorter than the "full" tests * so that the "fast" tests can be enabled in a wider range of circumstances.
*/
staticbool valid_sg_divisions(conststruct test_sg_division *divs, unsignedint count, int *flags_ret)
{ unsignedint total = 0; unsignedint i;
for (i = 0; i < count && total != TEST_SG_TOTAL; i++) { if (divs[i].proportion_of_total <= 0 ||
divs[i].proportion_of_total > TEST_SG_TOTAL - total) returnfalse;
total += divs[i].proportion_of_total; if (divs[i].flush_type != FLUSH_TYPE_NONE)
*flags_ret |= SGDIVS_HAVE_FLUSHES; if (divs[i].nosimd)
*flags_ret |= SGDIVS_HAVE_NOSIMD;
} return total == TEST_SG_TOTAL &&
memchr_inv(&divs[i], 0, (count - i) * sizeof(divs[0])) == NULL;
}
/* * Check whether the given testvec_config is valid. This isn't strictly needed * since every testvec_config should be valid, but check anyway so that people * don't unknowingly add broken configs that don't do what they wanted.
*/ staticbool valid_testvec_config(conststruct testvec_config *cfg)
{ int flags = 0;
if (cfg->name == NULL) returnfalse;
if (!valid_sg_divisions(cfg->src_divs, ARRAY_SIZE(cfg->src_divs),
&flags)) returnfalse;
if (cfg->dst_divs[0].proportion_of_total) { if (!valid_sg_divisions(cfg->dst_divs,
ARRAY_SIZE(cfg->dst_divs), &flags)) returnfalse;
} else { if (memchr_inv(cfg->dst_divs, 0, sizeof(cfg->dst_divs))) returnfalse; /* defaults to dst_divs=src_divs */
}
staticint init_test_sglist(struct test_sglist *tsgl)
{ return __testmgr_alloc_buf(tsgl->bufs, 1 /* two pages per buffer */);
}
staticvoid destroy_test_sglist(struct test_sglist *tsgl)
{ return __testmgr_free_buf(tsgl->bufs, 1 /* two pages per buffer */);
}
/** * build_test_sglist() - build a scatterlist for a crypto test * * @tsgl: the scatterlist to build. @tsgl->bufs[] contains an array of 2-page * buffers which the scatterlist @tsgl->sgl[] will be made to point into. * @divs: the layout specification on which the scatterlist will be based * @alignmask: the algorithm's alignmask * @total_len: the total length of the scatterlist to build in bytes * @data: if non-NULL, the buffers will be filled with this data until it ends. * Otherwise the buffers will be poisoned. In both cases, some bytes * past the end of each buffer will be poisoned to help detect overruns. * @out_divs: if non-NULL, the test_sg_division to which each scatterlist entry * corresponds will be returned here. This will match @divs except * that divisions resolving to a length of 0 are omitted as they are * not included in the scatterlist. * * Return: 0 or a -errno value
*/ staticint build_test_sglist(struct test_sglist *tsgl, conststruct test_sg_division *divs, constunsignedint alignmask, constunsignedint total_len, struct iov_iter *data, conststruct test_sg_division *out_divs[XBUFSIZE])
{ struct { conststruct test_sg_division *div;
size_t length;
} partitions[XBUFSIZE]; constunsignedint ndivs = count_test_sg_divisions(divs); unsignedint len_remaining = total_len; unsignedint i;
BUILD_BUG_ON(ARRAY_SIZE(partitions) != ARRAY_SIZE(tsgl->sgl)); if (WARN_ON(ndivs > ARRAY_SIZE(partitions))) return -EINVAL;
/* Calculate the (div, length) pairs */
tsgl->nents = 0; for (i = 0; i < ndivs; i++) { unsignedint len_this_sg =
min(len_remaining,
(total_len * divs[i].proportion_of_total +
TEST_SG_TOTAL / 2) / TEST_SG_TOTAL);
/* Set up the sgl entries and fill the data or poison */
sg_init_table(tsgl->sgl, tsgl->nents); for (i = 0; i < tsgl->nents; i++) { unsignedint offset = partitions[i].div->offset; void *addr;
if (partitions[i].div->offset_relative_to_alignmask)
offset += alignmask;
/* * Verify that a scatterlist crypto operation produced the correct output. * * @tsgl: scatterlist containing the actual output * @expected_output: buffer containing the expected output * @len_to_check: length of @expected_output in bytes * @unchecked_prefix_len: number of ignored bytes in @tsgl prior to real result * @check_poison: verify that the poison bytes after each chunk are intact? * * Return: 0 if correct, -EINVAL if incorrect, -EOVERFLOW if buffer overrun.
*/ staticint verify_correct_output(conststruct test_sglist *tsgl, constchar *expected_output, unsignedint len_to_check, unsignedint unchecked_prefix_len, bool check_poison)
{ unsignedint i;
for (i = 0; i < tsgl->nents; i++) { struct scatterlist *sg = &tsgl->sgl_ptr[i]; unsignedint len = sg->length; unsignedint offset = sg->offset; constchar *actual_output;
if (unchecked_prefix_len) { if (unchecked_prefix_len >= len) {
unchecked_prefix_len -= len; continue;
}
offset += unchecked_prefix_len;
len -= unchecked_prefix_len;
unchecked_prefix_len = 0;
}
len = min(len, len_to_check);
actual_output = page_address(sg_page(sg)) + offset; if (memcmp(expected_output, actual_output, len) != 0) return -EINVAL; if (check_poison &&
!testmgr_is_poison(actual_output + len, TESTMGR_POISON_LEN)) return -EOVERFLOW;
len_to_check -= len;
expected_output += len;
} if (WARN_ON(len_to_check != 0)) return -EINVAL; return 0;
}
for (i = 0; i < tsgl->nents; i++) { if (tsgl->sgl[i].page_link != tsgl->sgl_saved[i].page_link) returntrue; if (tsgl->sgl[i].offset != tsgl->sgl_saved[i].offset) returntrue; if (tsgl->sgl[i].length != tsgl->sgl_saved[i].length) returntrue;
} returnfalse;
}
/* * In-place crypto operations can use the same scatterlist for both the * source and destination (req->src == req->dst), or can use separate * scatterlists (req->src != req->dst) which point to the same * underlying memory. Make sure to test both cases.
*/ if (cfg->inplace_mode == INPLACE_ONE_SGLIST) {
tsgls->dst.sgl_ptr = tsgls->src.sgl;
tsgls->dst.nents = tsgls->src.nents; return 0;
} if (cfg->inplace_mode == INPLACE_TWO_SGLISTS) { /* * For now we keep it simple and only test the case where the * two scatterlists have identical entries, rather than * different entries that split up the same memory differently.
*/
memcpy(tsgls->dst.sgl, tsgls->src.sgl,
tsgls->src.nents * sizeof(tsgls->src.sgl[0]));
memcpy(tsgls->dst.sgl_saved, tsgls->src.sgl,
tsgls->src.nents * sizeof(tsgls->src.sgl[0]));
tsgls->dst.sgl_ptr = tsgls->dst.sgl;
tsgls->dst.nents = tsgls->src.nents; return 0;
} /* Out of place */ return build_test_sglist(&tsgls->dst,
cfg->dst_divs[0].proportion_of_total ?
cfg->dst_divs : cfg->src_divs,
alignmask, dst_total_len, NULL, NULL);
}
/* * Support for testing passing a misaligned key to setkey(): * * If cfg->key_offset is set, copy the key into a new buffer at that offset, * optionally adding alignmask. Else, just use the key directly.
*/ staticint prepare_keybuf(const u8 *key, unsignedint ksize, conststruct testvec_config *cfg, unsignedint alignmask, const u8 **keybuf_ret, const u8 **keyptr_ret)
{ unsignedint key_offset = cfg->key_offset;
u8 *keybuf = NULL, *keyptr = (u8 *)key;
/* * Like setkey_f(tfm, key, ksize), but sometimes misalign the key. * In addition, run the setkey function in no-SIMD context if requested.
*/ #define do_setkey(setkey_f, tfm, key, ksize, cfg, alignmask) \
({ \ const u8 *keybuf, *keyptr; \ int err; \
\
err = prepare_keybuf((key), (ksize), (cfg), (alignmask), \
&keybuf, &keyptr); \ if (err == 0) { \ if ((cfg)->nosimd_setkey) \
crypto_disable_simd_for_test(); \
err = setkey_f((tfm), keyptr, (ksize)); \ if ((cfg)->nosimd_setkey) \
crypto_reenable_simd_for_test(); \
kfree(keybuf); \
} \
err; \
})
/* * The fuzz tests use prandom instead of the normal Linux RNG since they don't * need cryptographically secure random numbers. This greatly improves the * performance of these tests, especially if they are run before the Linux RNG * has been initialized or if they are run on a lockdep-enabled kernel.
*/
staticinline u32 prandom_u32_below(struct rnd_state *rng, u32 ceil)
{ /* * This is slightly biased for non-power-of-2 values of 'ceil', but this * isn't important here.
*/ return prandom_u32_state(rng) % ceil;
}
/* Generate a random length in range [0, max_len], but prefer smaller values */ staticunsignedint generate_random_length(struct rnd_state *rng, unsignedint max_len)
{ unsignedint len = prandom_u32_below(rng, max_len + 1);
switch (prandom_u32_below(rng, 4)) { case 0:
len %= 64; break; case 1:
len %= 256; break; case 2:
len %= 1024; break; default: break;
} if (len && prandom_u32_below(rng, 4) == 0)
len = rounddown_pow_of_two(len); return len;
}
/* Flip a random bit in the given nonempty data buffer */ staticvoid flip_random_bit(struct rnd_state *rng, u8 *buf, size_t size)
{
size_t bitpos;
/* Flip a random byte in the given nonempty data buffer */ staticvoid flip_random_byte(struct rnd_state *rng, u8 *buf, size_t size)
{
buf[prandom_u32_below(rng, size)] ^= 0xff;
}
/* Sometimes make some random changes to the given nonempty data buffer */ staticvoid mutate_buffer(struct rnd_state *rng, u8 *buf, size_t size)
{
size_t num_flips;
size_t i;
/* Sometimes flip some bits */ if (prandom_u32_below(rng, 4) == 0) {
num_flips = min_t(size_t, 1 << prandom_u32_below(rng, 8),
size * 8); for (i = 0; i < num_flips; i++)
flip_random_bit(rng, buf, size);
}
/* Sometimes flip some bytes */ if (prandom_u32_below(rng, 4) == 0) {
num_flips = min_t(size_t, 1 << prandom_u32_below(rng, 8), size); for (i = 0; i < num_flips; i++)
flip_random_byte(rng, buf, size);
}
}
/* Randomly generate 'count' bytes, but sometimes make them "interesting" */ staticvoid generate_random_bytes(struct rnd_state *rng, u8 *buf, size_t count)
{
u8 b;
u8 increment;
size_t i;
if (count == 0) return;
switch (prandom_u32_below(rng, 8)) { /* Choose a generation strategy */ case 0: case 1: /* All the same byte, plus optional mutations */ switch (prandom_u32_below(rng, 4)) { case 0:
b = 0x00; break; case 1:
b = 0xff; break; default:
b = prandom_u8(rng); break;
}
memset(buf, b, count);
mutate_buffer(rng, buf, count); break; case 2: /* Ascending or descending bytes, plus optional mutations */
increment = prandom_u8(rng);
b = prandom_u8(rng); for (i = 0; i < count; i++, b += increment)
buf[i] = b;
mutate_buffer(rng, buf, count); break; default: /* Fully random bytes */
prandom_bytes_state(rng, buf, count);
}
}
/* * Given an algorithm name, build the name of the generic implementation of that * algorithm, assuming the usual naming convention. Specifically, this appends * "-generic" to every part of the name that is not a template name. Examples: * * aes => aes-generic * cbc(aes) => cbc(aes-generic) * cts(cbc(aes)) => cts(cbc(aes-generic)) * rfc7539(chacha20,poly1305) => rfc7539(chacha20-generic,poly1305-generic) * * Return: 0 on success, or -ENAMETOOLONG if the generic name would be too long
*/ staticint build_generic_driver_name(constchar *algname, char driver_name[CRYPTO_MAX_ALG_NAME])
{ constchar *in = algname; char *out = driver_name;
size_t len = strlen(algname);
if (len >= CRYPTO_MAX_ALG_NAME) goto too_long; do { constchar *in_saved = in;
while (*in && *in != '(' && *in != ')' && *in != ',')
*out++ = *in++; if (*in != '(' && in > in_saved) {
len += 8; if (len >= CRYPTO_MAX_ALG_NAME) goto too_long;
memcpy(out, "-generic", 8);
out += 8;
}
} while ((*out++ = *in++) != '\0'); return 0;
too_long:
pr_err("alg: generic driver name for \"%s\" would be too long\n",
algname); return -ENAMETOOLONG;
}
/* * 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(vec, vec_name, cfg, desc, tsgl,
hashstate); if (err) return err;
}
for (i = 0; i < fuzz_iterations; i++) {
generate_random_testvec_config(&rng, &cfg, cfgname, sizeof(cfgname));
err = test_hash_vec_cfg(vec, vec_name, &cfg,
req, desc, tsgl, hashstate); if (err) return err;
cond_resched();
}
} return 0;
}
/* * Generate a hash test vector from the given implementation. * Assumes the buffers in 'vec' were already allocated.
*/ staticvoid generate_random_hash_testvec(struct rnd_state *rng, struct ahash_request *req, struct hash_testvec *vec, unsignedint maxkeysize, unsignedint maxdatasize, char *name, size_t max_namelen)
{ /* Data */
vec->psize = generate_random_length(rng, maxdatasize);
generate_random_bytes(rng, (u8 *)vec->plaintext, vec->psize);
/* * Key: length in range [1, maxkeysize], but usually choose maxkeysize. * If algorithm is unkeyed, then maxkeysize == 0 and set ksize = 0.
*/
vec->setkey_error = 0;
vec->ksize = 0; if (maxkeysize) {
vec->ksize = maxkeysize; if (prandom_u32_below(rng, 4) == 0)
vec->ksize = prandom_u32_inclusive(rng, 1, maxkeysize);
generate_random_bytes(rng, (u8 *)vec->key, vec->ksize);
vec->setkey_error = crypto_ahash_setkey(
crypto_ahash_reqtfm(req), vec->key, vec->ksize); /* If the key couldn't be set, no need to continue to digest. */ if (vec->setkey_error) goto done;
}
tfm = crypto_alloc_shash(driver, type, mask); if (IS_ERR(tfm)) { if (PTR_ERR(tfm) == -ENOENT || PTR_ERR(tfm) == -EEXIST) { /* * 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);
}
/* * 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)) { if (PTR_ERR(atfm) == -ENOENT) return 0;
pr_err("alg: hash: failed to allocate transform for %s: %ld\n",
driver, PTR_ERR(atfm)); return PTR_ERR(atfm);
}
driver = crypto_ahash_driver_name(atfm);
req = ahash_request_alloc(atfm, GFP_KERNEL); if (!req) {
pr_err("alg: hash: failed to allocate request for %s\n",
driver);
err = -ENOMEM; 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); if (!tsgl || init_test_sglist(tsgl) != 0) {
pr_err("alg: hash: failed to allocate test buffers for %s\n",
driver);
kfree(tsgl);
tsgl = NULL;
err = -ENOMEM; goto out;
}
statesize = crypto_ahash_statesize(atfm); if (stfm)
statesize = max(statesize, crypto_shash_statesize(stfm));
hashstate = kmalloc(statesize + TESTMGR_POISON_LEN, GFP_KERNEL); if (!hashstate) {
pr_err("alg: hash: failed to allocate hash state buffer for %s\n",
driver);
err = -ENOMEM; goto out;
}
for (i = 0; i < num_vecs; i++) { if (fips_enabled && vecs[i].fips_skip) continue;
/* * For OPTIONAL_KEY algorithms, we have to do all the unkeyed tests * first, before setting a key on the tfm. To make this easier, we * require that the unkeyed test vectors (if any) are listed first.
*/
for (nr_unkeyed = 0; nr_unkeyed < tcount; nr_unkeyed++) { if (template[nr_unkeyed].ksize) break;
} for (nr_keyed = 0; nr_unkeyed + nr_keyed < tcount; nr_keyed++) { if (!template[nr_unkeyed + nr_keyed].ksize) {
pr_err("alg: hash: test vectors for %s out of order, " "unkeyed ones must come first\n", desc->alg); return -EINVAL;
}
maxkeysize = max_t(unsignedint, maxkeysize, template[nr_unkeyed + nr_keyed].ksize);
}
/* Set the key */ if (vec->wk)
crypto_aead_set_flags(tfm, CRYPTO_TFM_REQ_FORBID_WEAK_KEYS); else
crypto_aead_clear_flags(tfm, CRYPTO_TFM_REQ_FORBID_WEAK_KEYS);
err = do_setkey(crypto_aead_setkey, tfm, vec->key, vec->klen,
cfg, alignmask); if (err && err != vec->setkey_error) {
pr_err("alg: aead: %s setkey failed on test vector %s; expected_error=%d, actual_error=%d, flags=%#x\n",
driver, vec_name, vec->setkey_error, err,
crypto_aead_get_flags(tfm)); return err;
} if (!err && vec->setkey_error) {
pr_err("alg: aead: %s setkey unexpectedly succeeded on test vector %s; expected_error=%d\n",
driver, vec_name, vec->setkey_error); return -EINVAL;
}
/* Set the authentication tag size */
err = crypto_aead_setauthsize(tfm, authsize); if (err && err != vec->setauthsize_error) {
pr_err("alg: aead: %s setauthsize failed on test vector %s; expected_error=%d, actual_error=%d\n",
driver, vec_name, vec->setauthsize_error, err); return err;
} if (!err && vec->setauthsize_error) {
pr_err("alg: aead: %s setauthsize unexpectedly succeeded on test vector %s; expected_error=%d\n",
driver, vec_name, vec->setauthsize_error); return -EINVAL;
}
if (vec->setkey_error || vec->setauthsize_error) return 0;
/* The IV must be copied to a buffer, as the algorithm may modify it */ if (WARN_ON(ivsize > MAX_IVLEN)) return -EINVAL; if (vec->iv)
memcpy(iv, vec->iv, ivsize); else
memset(iv, 0, ivsize);
/* * Make at least one random change to a (ciphertext, AAD) pair. "Ciphertext" * here means the full ciphertext including the authentication tag. The * authentication tag (and hence also the ciphertext) is assumed to be nonempty.
*/ staticvoid mutate_aead_message(struct rnd_state *rng, struct aead_testvec *vec, bool aad_iv, unsignedint ivsize)
{ constunsignedint aad_tail_size = aad_iv ? ivsize : 0; constunsignedint authsize = vec->clen - vec->plen;
if (prandom_bool(rng) && vec->alen > aad_tail_size) { /* Mutate the AAD */
flip_random_bit(rng, (u8 *)vec->assoc,
vec->alen - aad_tail_size); if (prandom_bool(rng)) return;
} if (prandom_bool(rng)) { /* Mutate auth tag (assuming it's at the end of ciphertext) */
flip_random_bit(rng, (u8 *)vec->ctext + vec->plen, authsize);
} else { /* Mutate any part of the ciphertext */
flip_random_bit(rng, (u8 *)vec->ctext, vec->clen);
}
}
/* * Minimum authentication tag size in bytes at which we assume that we can * reliably generate inauthentic messages, i.e. not generate an authentic * message by chance.
*/ #define MIN_COLLISION_FREE_AUTHSIZE 8
if (inauthentic && prandom_bool(rng)) { /* Generate a random ciphertext. */
generate_random_bytes(rng, (u8 *)vec->ctext, vec->clen);
} else { int i = 0; struct scatterlist src[2], dst;
u8 iv[MAX_IVLEN];
DECLARE_CRYPTO_WAIT(wait);
/* Generate a random plaintext and encrypt it. */
sg_init_table(src, 2); if (vec->alen)
sg_set_buf(&src[i++], vec->assoc, vec->alen); if (vec->plen) {
generate_random_bytes(rng, (u8 *)vec->ptext, 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 encryption failed, we're done. */ if (vec->crypt_error != 0) return;
memmove((u8 *)vec->ctext, vec->ctext + vec->alen, vec->clen); if (!inauthentic) return; /* * Mutate the authentic (ciphertext, AAD) pair to get an * inauthentic one.
*/
mutate_aead_message(rng, vec, suite->aad_iv, ivsize);
}
vec->novrfy = 1; if (suite->einval_allowed)
vec->crypt_error = -EINVAL;
}
/* * Generate an AEAD test vector 'vec' using the implementation specified by * 'req'. The buffers in 'vec' must already be allocated. * * If 'prefer_inauthentic' is true, then this function will generate inauthentic * test vectors (i.e. vectors with 'vec->novrfy=1') more often.
*/ staticvoid generate_random_aead_testvec(struct rnd_state *rng, struct aead_request *req, struct aead_testvec *vec, conststruct aead_test_suite *suite, unsignedint maxkeysize, unsignedint maxdatasize, char *name, size_t max_namelen, bool prefer_inauthentic)
{ struct crypto_aead *tfm = crypto_aead_reqtfm(req); constunsignedint ivsize = crypto_aead_ivsize(tfm); constunsignedint maxauthsize = crypto_aead_maxauthsize(tfm); unsignedint authsize; unsignedint total_len;
/* Key: length in [0, maxkeysize], but usually choose maxkeysize */
vec->klen = maxkeysize; if (prandom_u32_below(rng, 4) == 0)
vec->klen = prandom_u32_below(rng, maxkeysize + 1);
generate_random_bytes(rng, (u8 *)vec->key, vec->klen);
vec->setkey_error = crypto_aead_setkey(tfm, vec->key, vec->klen);
/* IV */
generate_random_bytes(rng, (u8 *)vec->iv, ivsize);
/* Tag length: in [0, maxauthsize], but usually choose maxauthsize */
authsize = maxauthsize; if (prandom_u32_below(rng, 4) == 0)
authsize = prandom_u32_below(rng, maxauthsize + 1); if (prefer_inauthentic && authsize < MIN_COLLISION_FREE_AUTHSIZE)
authsize = MIN_COLLISION_FREE_AUTHSIZE; if (WARN_ON(authsize > maxdatasize))
authsize = maxdatasize;
maxdatasize -= authsize;
vec->setauthsize_error = crypto_aead_setauthsize(tfm, authsize);
/* * Generate the AAD, plaintext, and ciphertext. Not applicable if the * key or the authentication tag size couldn't be set.
*/
vec->novrfy = 0;
vec->crypt_error = 0; if (vec->setkey_error == 0 && vec->setauthsize_error == 0)
generate_aead_message(rng, req, suite, vec, prefer_inauthentic);
snprintf(name, max_namelen, "\"random: alen=%u plen=%u authsize=%u klen=%u novrfy=%d\"",
vec->alen, vec->plen, authsize, vec->klen, vec->novrfy);
}
staticvoid try_to_generate_inauthentic_testvec(struct aead_slow_tests_ctx *ctx)
{ int i;
for (i = 0; i < 10; i++) {
generate_random_aead_testvec(&ctx->rng, ctx->req, &ctx->vec,
&ctx->test_desc->suite.aead,
ctx->maxkeysize, ctx->maxdatasize,
ctx->vec_name, sizeof(ctx->vec_name), true); if (ctx->vec.novrfy) return;
}
}
/* * Generate inauthentic test vectors (i.e. ciphertext, AAD pairs that aren't the * result of an encryption with the key) and verify that decryption fails.
*/ staticint test_aead_inauthentic_inputs(struct aead_slow_tests_ctx *ctx)
{ unsignedint i; int err;
for (i = 0; i < fuzz_iterations * 8; i++) { /* * Since this part of the tests isn't comparing the * implementation to another, there's no point in testing any * test vectors other than inauthentic ones (vec.novrfy=1) here. * * If we're having trouble generating such a test vector, e.g. * if the algorithm keeps rejecting the generated keys, don't * retry forever; just continue on.
*/
try_to_generate_inauthentic_testvec(ctx); if (ctx->vec.novrfy) {
generate_random_testvec_config(&ctx->rng, &ctx->cfg,
ctx->cfgname, sizeof(ctx->cfgname));
err = test_aead_vec_cfg(DECRYPT, &ctx->vec,
ctx->vec_name, &ctx->cfg,
ctx->req, ctx->tsgls); if (err) return err;
}
cond_resched();
} return 0;
}
/* * Test the AEAD algorithm against the corresponding generic implementation, if * one is available.
*/ staticint test_aead_vs_generic_impl(struct aead_slow_tests_ctx *ctx)
{ struct crypto_aead *tfm = ctx->tfm; constchar *algname = crypto_aead_alg(tfm)->base.cra_name; constchar *driver = crypto_aead_driver_name(tfm); constchar *generic_driver = ctx->test_desc->generic_driver; char _generic_driver[CRYPTO_MAX_ALG_NAME]; struct crypto_aead *generic_tfm = NULL; struct aead_request *generic_req = NULL; unsignedint i; int err;
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;
}
ret = -EINVAL; if (WARN_ON(template[i].len > PAGE_SIZE)) goto out;
data = xbuf[0];
memcpy(data, input, template[i].len);
crypto_cipher_clear_flags(tfm, ~0); if (template[i].wk)
crypto_cipher_set_flags(tfm, CRYPTO_TFM_REQ_FORBID_WEAK_KEYS);
ret = crypto_cipher_setkey(tfm, template[i].key, template[i].klen); if (ret) { if (ret == template[i].setkey_error) continue;
pr_err("alg: cipher: %s setkey failed on test vector %u; expected_error=%d, actual_error=%d, flags=%#x\n",
algo, j, template[i].setkey_error, ret,
crypto_cipher_get_flags(tfm)); goto out;
} if (template[i].setkey_error) {
pr_err("alg: cipher: %s setkey unexpectedly succeeded on test vector %u; expected_error=%d\n",
algo, j, template[i].setkey_error);
ret = -EINVAL; goto out;
}
for (k = 0; k < template[i].len;
k += crypto_cipher_blocksize(tfm)) { if (enc)
crypto_cipher_encrypt_one(tfm, data + k,
data + k); else
crypto_cipher_decrypt_one(tfm, data + k,
data + k);
}
q = data; if (memcmp(q, result, template[i].len)) {
printk(KERN_ERR "alg: cipher: Test %d failed " "on %s for %s\n", j, e, algo);
hexdump(q, template[i].len);
ret = -EINVAL; goto out;
}
}
/* Set the key */ if (vec->wk)
crypto_skcipher_set_flags(tfm, CRYPTO_TFM_REQ_FORBID_WEAK_KEYS); else
crypto_skcipher_clear_flags(tfm,
CRYPTO_TFM_REQ_FORBID_WEAK_KEYS);
err = do_setkey(crypto_skcipher_setkey, tfm, vec->key, vec->klen,
cfg, alignmask); if (err) { if (err == vec->setkey_error) return 0;
pr_err("alg: skcipher: %s setkey failed on test vector %s; expected_error=%d, actual_error=%d, flags=%#x\n",
driver, vec_name, vec->setkey_error, err,
crypto_skcipher_get_flags(tfm)); return err;
} if (vec->setkey_error) {
pr_err("alg: skcipher: %s setkey unexpectedly succeeded on test vector %s; expected_error=%d\n",
driver, vec_name, vec->setkey_error); return -EINVAL;
}
/* The IV must be copied to a buffer, as the algorithm may modify it */ if (ivsize) { if (WARN_ON(ivsize > MAX_IVLEN)) return -EINVAL; if (vec->iv)
memcpy(iv, vec->iv, ivsize); else
memset(iv, 0, ivsize);
} else {
iv = NULL;
}
/* Do the actual encryption or decryption */
testmgr_poison(req->__ctx, crypto_skcipher_reqsize(tfm));
skcipher_request_set_callback(req, req_flags, crypto_req_done, &wait);
skcipher_request_set_crypt(req, tsgls->src.sgl_ptr, tsgls->dst.sgl_ptr,
vec->len, iv); if (cfg->nosimd)
crypto_disable_simd_for_test();
err = enc ? crypto_skcipher_encrypt(req) : crypto_skcipher_decrypt(req); if (cfg->nosimd)
crypto_reenable_simd_for_test();
err = crypto_wait_req(err, &wait);
/* Check that the algorithm didn't overwrite things it shouldn't have */ if (req->cryptlen != vec->len ||
req->iv != iv ||
req->src != tsgls->src.sgl_ptr ||
req->dst != tsgls->dst.sgl_ptr ||
crypto_skcipher_reqtfm(req) != tfm ||
req->base.complete != crypto_req_done ||
req->base.flags != req_flags ||
req->base.data != &wait) {
pr_err("alg: skcipher: %s %s corrupted request struct on test vector %s, cfg=\"%s\"\n",
driver, op, vec_name, cfg->name); if (req->cryptlen != vec->len)
pr_err("alg: skcipher: changed 'req->cryptlen'\n"); if (req->iv != iv)
pr_err("alg: skcipher: changed 'req->iv'\n"); if (req->src != tsgls->src.sgl_ptr)
pr_err("alg: skcipher: changed 'req->src'\n"); if (req->dst != tsgls->dst.sgl_ptr)
pr_err("alg: skcipher: changed 'req->dst'\n"); if (crypto_skcipher_reqtfm(req) != tfm)
pr_err("alg: skcipher: changed 'req->base.tfm'\n"); if (req->base.complete != crypto_req_done)
pr_err("alg: skcipher: changed 'req->base.complete'\n"); if (req->base.flags != req_flags)
pr_err("alg: skcipher: changed 'req->base.flags'\n"); if (req->base.data != &wait)
pr_err("alg: skcipher: changed 'req->base.data'\n"); return -EINVAL;
} if (is_test_sglist_corrupted(&tsgls->src)) {
pr_err("alg: skcipher: %s %s corrupted src sgl on test vector %s, cfg=\"%s\"\n",
driver, op, vec_name, cfg->name); return -EINVAL;
} if (tsgls->dst.sgl_ptr != tsgls->src.sgl &&
is_test_sglist_corrupted(&tsgls->dst)) {
pr_err("alg: skcipher: %s %s corrupted dst sgl on test vector %s, cfg=\"%s\"\n",
driver, op, vec_name, cfg->name); return -EINVAL;
}
/* Check for success or failure */ if (err) { if (err == vec->crypt_error) return 0;
pr_err("alg: skcipher: %s %s failed on test vector %s; expected_error=%d, actual_error=%d, cfg=\"%s\"\n",
driver, op, vec_name, vec->crypt_error, err, cfg->name); return err;
} if (vec->crypt_error) {
pr_err("alg: skcipher: %s %s unexpectedly succeeded on test vector %s; expected_error=%d, cfg=\"%s\"\n",
driver, op, vec_name, vec->crypt_error, cfg->name); return -EINVAL;
}
/* Check for the correct output (ciphertext or plaintext) */
err = verify_correct_output(&tsgls->dst, enc ? vec->ctext : vec->ptext,
vec->len, 0, true); if (err == -EOVERFLOW) {
pr_err("alg: skcipher: %s %s overran dst buffer on test vector %s, cfg=\"%s\"\n",
driver, op, vec_name, cfg->name); return err;
} if (err) {
pr_err("alg: skcipher: %s %s test failed (wrong result) on test vector %s, cfg=\"%s\"\n",
driver, op, vec_name, cfg->name); return err;
}
/* If applicable, check that the algorithm generated the correct IV */ if (vec->iv_out && memcmp(iv, vec->iv_out, ivsize) != 0) {
pr_err("alg: skcipher: %s %s test failed (wrong output IV) on test vector %s, cfg=\"%s\"\n",
driver, op, vec_name, cfg->name);
hexdump(iv, ivsize); return -EINVAL;
}
/* If the key couldn't be set, no need to continue to encrypt. */ if (vec->setkey_error) goto done;
/* Ciphertext */
sg_init_one(&src, vec->ptext, vec->len);
sg_init_one(&dst, vec->ctext, vec->len);
memcpy(iv, vec->iv, ivsize);
skcipher_request_set_callback(req, 0, crypto_req_done, &wait);
skcipher_request_set_crypt(req, &src, &dst, vec->len, iv);
vec->crypt_error = crypto_wait_req(crypto_skcipher_encrypt(req), &wait); if (vec->crypt_error != 0) { /* * The only acceptable error here is for an invalid length, so * skcipher decryption should fail with the same error too. * We'll test for this. But to keep the API usage well-defined, * explicitly initialize the ciphertext buffer too.
*/
memset((u8 *)vec->ctext, 0, vec->len);
}
done:
snprintf(name, max_namelen, "\"random: len=%u klen=%u\"",
vec->len, vec->klen);
}
ret = crypto_wait_req(crypto_acomp_compress(req), &wait); if (ret) {
pr_err("alg: acomp: compression failed on test %d for %s: ret=%d\n",
i + 1, algo, -ret);
kfree(input_vec);
acomp_request_free(req); goto out;
}
ret = crypto_wait_req(crypto_acomp_decompress(req), &wait); if (ret) {
pr_err("alg: acomp: compression failed on test %d for %s: ret=%d\n",
i + 1, algo, -ret);
kfree(input_vec);
acomp_request_free(req); goto out;
}
if (req->dlen != ctemplate[i].inlen) {
pr_err("alg: acomp: Compression test %d failed for %s: output len = %d\n",
i + 1, algo, req->dlen);
ret = -EINVAL;
kfree(input_vec);
acomp_request_free(req); goto out;
}
if (memcmp(input_vec, decomp_out, req->dlen)) {
pr_err("alg: acomp: Compression test %d failed for %s\n",
i + 1, algo);
hexdump(output, req->dlen);
ret = -EINVAL;
kfree(input_vec);
acomp_request_free(req); goto out;
}
kfree(input_vec);
acomp_request_free(req);
}
for (i = 0; i < dtcount; i++) { unsignedint dlen = COMP_BUF_SIZE; int ilen = dtemplate[i].inlen; void *input_vec;
input_vec = kmemdup(dtemplate[i].input, ilen, GFP_KERNEL); if (!input_vec) {
ret = -ENOMEM; goto out;
}
ret = crypto_wait_req(crypto_acomp_decompress(req), &wait); if (ret) {
pr_err("alg: acomp: decompression failed on test %d for %s: ret=%d\n",
i + 1, algo, -ret);
kfree(input_vec);
acomp_request_free(req); goto out;
}
if (req->dlen != dtemplate[i].outlen) {
pr_err("alg: acomp: Decompression test %d failed for %s: output len = %d\n",
i + 1, algo, req->dlen);
ret = -EINVAL;
kfree(input_vec);
acomp_request_free(req); goto out;
}
if (memcmp(output, dtemplate[i].output, req->dlen)) {
pr_err("alg: acomp: Decompression test %d failed for %s\n",
i + 1, algo);
hexdump(output, req->dlen);
ret = -EINVAL;
kfree(input_vec);
acomp_request_free(req); goto out;
}
/* Use appropriate parameter as base */
kpp_request_set_input(req, NULL, 0);
sg_init_one(&dst, output_buf, out_len_max);
kpp_request_set_output(req, &dst, out_len_max);
kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
crypto_req_done, &wait);
/* Compute party A's public key */
err = crypto_wait_req(crypto_kpp_generate_public_key(req), &wait); if (err) {
pr_err("alg: %s: Party A: generate public key test failed. err %d\n",
alg, err); goto free_output;
}
if (vec->genkey) { /* Save party A's public key */
a_public = kmemdup(sg_virt(req->dst), out_len_max, GFP_KERNEL); if (!a_public) {
err = -ENOMEM; goto free_output;
}
} else { /* Verify calculated public key */ if (memcmp(vec->expected_a_public, sg_virt(req->dst),
vec->expected_a_public_size)) {
pr_err("alg: %s: Party A: generate public key test failed. Invalid output\n",
alg);
err = -EINVAL; goto free_output;
}
}
/* Calculate shared secret key by using counter part (b) public key. */
input_buf = kmemdup(vec->b_public, vec->b_public_size, GFP_KERNEL); if (!input_buf) {
err = -ENOMEM; goto free_output;
}
if (vec->genkey) { /* Save the shared secret obtained by party A */
a_ss = kmemdup(sg_virt(req->dst), vec->expected_ss_size, GFP_KERNEL); if (!a_ss) {
err = -ENOMEM; goto free_all;
}
/* * Calculate party B's shared secret by using party A's * public key.
*/
err = crypto_kpp_set_secret(tfm, vec->b_secret,
vec->b_secret_size); if (err < 0) goto free_all;
/* * verify shared secret from which the user will derive * secret key by executing whatever hash it has chosen
*/ if (memcmp(shared_secret, sg_virt(req->dst),
vec->expected_ss_size)) {
pr_err("alg: %s: compute shared secret test failed. Invalid output\n",
alg);
err = -EINVAL;
}
for (i = 0; i < tcount; i++) {
ret = do_test_kpp(tfm, vecs++, alg); if (ret) {
pr_err("alg: %s: test failed on vector %d, err=%d\n",
alg, i + 1, ret); return ret;
}
} return 0;
}
req = akcipher_request_alloc(tfm, GFP_KERNEL); if (!req) goto free_xbuf;
crypto_init_wait(&wait);
if (vecs->public_key_vec)
err = crypto_akcipher_set_pub_key(tfm, vecs->key,
vecs->key_len); else
err = crypto_akcipher_set_priv_key(tfm, vecs->key,
vecs->key_len); if (err) goto free_req;
/* First run encrypt test which does not require a private key */
err = -ENOMEM;
out_len_max = crypto_akcipher_maxsize(tfm);
outbuf_enc = kzalloc(out_len_max, GFP_KERNEL); if (!outbuf_enc) goto free_req;
err = crypto_wait_req(crypto_akcipher_encrypt(req), &wait); if (err) {
pr_err("alg: akcipher: encrypt test failed. err %d\n", err); goto free_all;
} if (c) { if (req->dst_len != c_size) {
pr_err("alg: akcipher: encrypt test failed. Invalid output len\n");
err = -EINVAL; goto free_all;
} /* verify that encrypted message is equal to expected */ if (memcmp(c, outbuf_enc, c_size) != 0) {
pr_err("alg: akcipher: encrypt test failed. Invalid output\n");
hexdump(outbuf_enc, c_size);
err = -EINVAL; goto free_all;
}
}
/* * Don't invoke decrypt test which requires a private key * for vectors with only a public key.
*/ if (vecs->public_key_vec) {
err = 0; goto free_all;
}
outbuf_dec = kzalloc(out_len_max, GFP_KERNEL); if (!outbuf_dec) {
err = -ENOMEM; goto free_all;
}
if (!c) {
c = outbuf_enc;
c_size = req->dst_len;
}
for (i = 0; i < tcount; i++) {
ret = test_sig_one(tfm, vecs++); if (ret) {
pr_err("alg: sig: test %d failed for %s: err %d\n",
i + 1, algo, ret); return ret;
}
} return 0;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.