diff options
Diffstat (limited to 'crypto/testmgr.c')
-rw-r--r-- | crypto/testmgr.c | 613 |
1 files changed, 185 insertions, 428 deletions
diff --git a/crypto/testmgr.c b/crypto/testmgr.c index a275c7c2c371..6a870e21b0cf 100644 --- a/crypto/testmgr.c +++ b/crypto/testmgr.c @@ -1208,443 +1208,222 @@ static int test_hash(struct crypto_ahash *tfm, return 0; } -static int __test_aead(struct crypto_aead *tfm, int enc, - const struct aead_testvec *template, unsigned int tcount, - const bool diff_dst, const int align_offset) +static int test_aead_vec_cfg(const char *driver, int enc, + const struct aead_testvec *vec, + unsigned int vec_num, + const struct testvec_config *cfg, + struct aead_request *req, + struct cipher_test_sglists *tsgls) { - const char *algo = crypto_tfm_alg_driver_name(crypto_aead_tfm(tfm)); - unsigned int i, j, k, n, temp; - int ret = -ENOMEM; - char *q; - char *key; - struct aead_request *req; - struct scatterlist *sg; - struct scatterlist *sgout; - const char *e, *d; - struct crypto_wait wait; - unsigned int authsize, iv_len; - char *iv; - char *xbuf[XBUFSIZE]; - char *xoutbuf[XBUFSIZE]; - char *axbuf[XBUFSIZE]; - - iv = kzalloc(MAX_IVLEN, GFP_KERNEL); - if (!iv) - return ret; - key = kmalloc(MAX_KEYLEN, GFP_KERNEL); - if (!key) - goto out_noxbuf; - if (testmgr_alloc_buf(xbuf)) - goto out_noxbuf; - if (testmgr_alloc_buf(axbuf)) - goto out_noaxbuf; - if (diff_dst && testmgr_alloc_buf(xoutbuf)) - goto out_nooutbuf; - - /* avoid "the frame size is larger than 1024 bytes" compiler warning */ - sg = kmalloc(array3_size(sizeof(*sg), 8, (diff_dst ? 4 : 2)), - GFP_KERNEL); - if (!sg) - goto out_nosg; - sgout = &sg[16]; - - if (diff_dst) - d = "-ddst"; - else - d = ""; + struct crypto_aead *tfm = crypto_aead_reqtfm(req); + const unsigned int alignmask = crypto_aead_alignmask(tfm); + const unsigned int ivsize = crypto_aead_ivsize(tfm); + const unsigned int authsize = vec->clen - vec->plen; + const u32 req_flags = CRYPTO_TFM_REQ_MAY_BACKLOG | cfg->req_flags; + const char *op = enc ? "encryption" : "decryption"; + DECLARE_CRYPTO_WAIT(wait); + u8 _iv[3 * (MAX_ALGAPI_ALIGNMASK + 1) + MAX_IVLEN]; + u8 *iv = PTR_ALIGN(&_iv[0], 2 * (MAX_ALGAPI_ALIGNMASK + 1)) + + cfg->iv_offset + + (cfg->iv_offset_relative_to_alignmask ? alignmask : 0); + struct kvec input[2]; + int err; - if (enc == ENCRYPT) - e = "encryption"; + /* Set the key */ + if (vec->wk) + crypto_aead_set_flags(tfm, CRYPTO_TFM_REQ_FORBID_WEAK_KEYS); else - e = "decryption"; - - crypto_init_wait(&wait); - - req = aead_request_alloc(tfm, GFP_KERNEL); - if (!req) { - pr_err("alg: aead%s: Failed to allocate request for %s\n", - d, algo); - goto out; + crypto_aead_clear_flags(tfm, CRYPTO_TFM_REQ_FORBID_WEAK_KEYS); + err = crypto_aead_setkey(tfm, vec->key, vec->klen); + if (err) { + if (vec->fail) /* expectedly failed to set key? */ + return 0; + pr_err("alg: aead: %s setkey failed with err %d on test vector %u; flags=%#x\n", + driver, err, vec_num, crypto_aead_get_flags(tfm)); + return err; } - - aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, - crypto_req_done, &wait); - - iv_len = crypto_aead_ivsize(tfm); - - for (i = 0, j = 0; i < tcount; i++) { - const char *input, *expected_output; - unsigned int inlen, outlen; - char *inbuf, *outbuf, *assocbuf; - - if (template[i].np) - continue; - if (enc) { - if (template[i].novrfy) - continue; - input = template[i].ptext; - inlen = template[i].plen; - expected_output = template[i].ctext; - outlen = template[i].clen; - } else { - input = template[i].ctext; - inlen = template[i].clen; - expected_output = template[i].ptext; - outlen = template[i].plen; - } - - j++; - - /* some templates have no input data but they will - * touch input - */ - inbuf = xbuf[0] + align_offset; - assocbuf = axbuf[0]; - - ret = -EINVAL; - if (WARN_ON(align_offset + template[i].clen > PAGE_SIZE || - template[i].alen > PAGE_SIZE)) - goto out; - - memcpy(inbuf, input, inlen); - memcpy(assocbuf, template[i].assoc, template[i].alen); - if (template[i].iv) - memcpy(iv, template[i].iv, iv_len); - else - memset(iv, 0, iv_len); - - crypto_aead_clear_flags(tfm, ~0); - if (template[i].wk) - crypto_aead_set_flags(tfm, - CRYPTO_TFM_REQ_FORBID_WEAK_KEYS); - - if (template[i].klen > MAX_KEYLEN) { - pr_err("alg: aead%s: setkey failed on test %d for %s: key size %d > %d\n", - d, j, algo, template[i].klen, - MAX_KEYLEN); - ret = -EINVAL; - goto out; - } - memcpy(key, template[i].key, template[i].klen); - - ret = crypto_aead_setkey(tfm, key, template[i].klen); - if (template[i].fail == !ret) { - pr_err("alg: aead%s: setkey failed on test %d for %s: flags=%x\n", - d, j, algo, crypto_aead_get_flags(tfm)); - goto out; - } else if (ret) - continue; - - authsize = template[i].clen - template[i].plen; - ret = crypto_aead_setauthsize(tfm, authsize); - if (ret) { - pr_err("alg: aead%s: Failed to set authsize to %u on test %d for %s\n", - d, authsize, j, algo); - goto out; - } - - k = !!template[i].alen; - sg_init_table(sg, k + 1); - sg_set_buf(&sg[0], assocbuf, template[i].alen); - sg_set_buf(&sg[k], inbuf, template[i].clen); - outbuf = inbuf; - - if (diff_dst) { - sg_init_table(sgout, k + 1); - sg_set_buf(&sgout[0], assocbuf, template[i].alen); - - outbuf = xoutbuf[0] + align_offset; - sg_set_buf(&sgout[k], outbuf, template[i].clen); - } - - aead_request_set_crypt(req, sg, (diff_dst) ? sgout : sg, inlen, - iv); - - aead_request_set_ad(req, template[i].alen); - - ret = crypto_wait_req(enc ? crypto_aead_encrypt(req) - : crypto_aead_decrypt(req), &wait); - - switch (ret) { - case 0: - if (template[i].novrfy) { - /* verification was supposed to fail */ - pr_err("alg: aead%s: %s failed on test %d for %s: ret was 0, expected -EBADMSG\n", - d, e, j, algo); - /* so really, we got a bad message */ - ret = -EBADMSG; - goto out; - } - break; - case -EBADMSG: - if (template[i].novrfy) - /* verification failure was expected */ - continue; - /* fall through */ - default: - pr_err("alg: aead%s: %s failed on test %d for %s: ret=%d\n", - d, e, j, algo, -ret); - goto out; - } - - if (memcmp(outbuf, expected_output, outlen)) { - pr_err("alg: aead%s: Test %d failed on %s for %s\n", - d, j, e, algo); - hexdump(outbuf, outlen); - ret = -EINVAL; - goto out; - } + if (vec->fail) { + pr_err("alg: aead: %s setkey unexpectedly succeeded on test vector %u\n", + driver, vec_num); + return -EINVAL; } - for (i = 0, j = 0; i < tcount; i++) { - const char *input, *expected_output; - unsigned int inlen, outlen; - - /* alignment tests are only done with continuous buffers */ - if (align_offset != 0) - break; - - if (!template[i].np) - continue; - - if (enc) { - if (template[i].novrfy) - continue; - input = template[i].ptext; - inlen = template[i].plen; - expected_output = template[i].ctext; - outlen = template[i].clen; - } else { - input = template[i].ctext; - inlen = template[i].clen; - expected_output = template[i].ptext; - outlen = template[i].plen; - } - - j++; - - if (template[i].iv) - memcpy(iv, template[i].iv, iv_len); - else - memset(iv, 0, MAX_IVLEN); - - crypto_aead_clear_flags(tfm, ~0); - if (template[i].wk) - crypto_aead_set_flags(tfm, - CRYPTO_TFM_REQ_FORBID_WEAK_KEYS); - if (template[i].klen > MAX_KEYLEN) { - pr_err("alg: aead%s: setkey failed on test %d for %s: key size %d > %d\n", - d, j, algo, template[i].klen, MAX_KEYLEN); - ret = -EINVAL; - goto out; - } - memcpy(key, template[i].key, template[i].klen); - - ret = crypto_aead_setkey(tfm, key, template[i].klen); - if (template[i].fail == !ret) { - pr_err("alg: aead%s: setkey failed on chunk test %d for %s: flags=%x\n", - d, j, algo, crypto_aead_get_flags(tfm)); - goto out; - } else if (ret) - continue; - - authsize = template[i].clen - template[i].plen; - - ret = -EINVAL; - sg_init_table(sg, template[i].anp + template[i].np); - if (diff_dst) - sg_init_table(sgout, template[i].anp + template[i].np); - - ret = -EINVAL; - for (k = 0, temp = 0; k < template[i].anp; k++) { - if (WARN_ON(offset_in_page(IDX[k]) + - template[i].atap[k] > PAGE_SIZE)) - goto out; - sg_set_buf(&sg[k], - memcpy(axbuf[IDX[k] >> PAGE_SHIFT] + - offset_in_page(IDX[k]), - template[i].assoc + temp, - template[i].atap[k]), - template[i].atap[k]); - if (diff_dst) - sg_set_buf(&sgout[k], - axbuf[IDX[k] >> PAGE_SHIFT] + - offset_in_page(IDX[k]), - template[i].atap[k]); - temp += template[i].atap[k]; - } - - for (k = 0, temp = 0; k < template[i].np; k++) { - n = template[i].tap[k]; - if (k == template[i].np - 1 && !enc) - n += authsize; - - if (WARN_ON(offset_in_page(IDX[k]) + n > PAGE_SIZE)) - goto out; - - q = xbuf[IDX[k] >> PAGE_SHIFT] + offset_in_page(IDX[k]); - memcpy(q, input + temp, n); - sg_set_buf(&sg[template[i].anp + k], q, n); - - if (diff_dst) { - q = xoutbuf[IDX[k] >> PAGE_SHIFT] + - offset_in_page(IDX[k]); - - memset(q, 0, n); - - sg_set_buf(&sgout[template[i].anp + k], q, n); - } - - if (k == template[i].np - 1 && enc) - n += authsize; - if (offset_in_page(q) + n < PAGE_SIZE) - q[n] = 0; - - temp += n; - } + /* Set the authentication tag size */ + err = crypto_aead_setauthsize(tfm, authsize); + if (err) { + pr_err("alg: aead: %s setauthsize failed with err %d on test vector %u\n", + driver, err, vec_num); + return err; + } - ret = crypto_aead_setauthsize(tfm, authsize); - if (ret) { - pr_err("alg: aead%s: Failed to set authsize to %u on chunk test %d for %s\n", - d, authsize, j, algo); - goto out; - } + /* 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); - if (enc) { - if (WARN_ON(sg[template[i].anp + k - 1].offset + - sg[template[i].anp + k - 1].length + - authsize > PAGE_SIZE)) { - ret = -EINVAL; - goto out; - } + /* Build the src/dst scatterlists */ + input[0].iov_base = (void *)vec->assoc; + input[0].iov_len = vec->alen; + input[1].iov_base = enc ? (void *)vec->ptext : (void *)vec->ctext; + input[1].iov_len = enc ? vec->plen : vec->clen; + err = build_cipher_test_sglists(tsgls, cfg, alignmask, + vec->alen + (enc ? vec->plen : + vec->clen), + vec->alen + (enc ? vec->clen : + vec->plen), + input, 2); + if (err) { + pr_err("alg: aead: %s %s: error preparing scatterlists for test vector %u, cfg=\"%s\"\n", + driver, op, vec_num, cfg->name); + return err; + } - if (diff_dst) - sgout[template[i].anp + k - 1].length += - authsize; - sg[template[i].anp + k - 1].length += authsize; - } + /* Do the actual encryption or decryption */ + testmgr_poison(req->__ctx, crypto_aead_reqsize(tfm)); + aead_request_set_callback(req, req_flags, crypto_req_done, &wait); + aead_request_set_crypt(req, tsgls->src.sgl_ptr, tsgls->dst.sgl_ptr, + enc ? vec->plen : vec->clen, iv); + aead_request_set_ad(req, vec->alen); + err = crypto_wait_req(enc ? crypto_aead_encrypt(req) : + crypto_aead_decrypt(req), &wait); - aead_request_set_crypt(req, sg, (diff_dst) ? sgout : sg, - inlen, iv); + aead_request_set_tfm(req, tfm); /* TODO: get rid of this */ - aead_request_set_ad(req, template[i].alen); + if (err) { + if (err == -EBADMSG && vec->novrfy) + return 0; + pr_err("alg: aead: %s %s failed with err %d on test vector %u, cfg=\"%s\"\n", + driver, op, err, vec_num, cfg->name); + return err; + } + if (vec->novrfy) { + pr_err("alg: aead: %s %s unexpectedly succeeded on test vector %u, cfg=\"%s\"\n", + driver, op, vec_num, cfg->name); + return -EINVAL; + } - ret = crypto_wait_req(enc ? crypto_aead_encrypt(req) - : crypto_aead_decrypt(req), &wait); + /* Check for the correct output (ciphertext or plaintext) */ + err = verify_correct_output(&tsgls->dst, enc ? vec->ctext : vec->ptext, + enc ? vec->clen : vec->plen, + vec->alen, enc || !cfg->inplace); + if (err == -EOVERFLOW) { + pr_err("alg: aead: %s %s overran dst buffer on test vector %u, cfg=\"%s\"\n", + driver, op, vec_num, cfg->name); + return err; + } + if (err) { + pr_err("alg: aead: %s %s test failed (wrong result) on test vector %u, cfg=\"%s\"\n", + driver, op, vec_num, cfg->name); + return err; + } - switch (ret) { - case 0: - if (template[i].novrfy) { - /* verification was supposed to fail */ - pr_err("alg: aead%s: %s failed on chunk test %d for %s: ret was 0, expected -EBADMSG\n", - d, e, j, algo); - /* so really, we got a bad message */ - ret = -EBADMSG; - goto out; - } - break; - case -EBADMSG: - if (template[i].novrfy) - /* verification failure was expected */ - continue; - /* fall through */ - default: - pr_err("alg: aead%s: %s failed on chunk test %d for %s: ret=%d\n", - d, e, j, algo, -ret); - goto out; - } + return 0; +} - ret = -EINVAL; - for (k = 0, temp = 0; k < template[i].np; k++) { - if (diff_dst) - q = xoutbuf[IDX[k] >> PAGE_SHIFT] + - offset_in_page(IDX[k]); - else - q = xbuf[IDX[k] >> PAGE_SHIFT] + - offset_in_page(IDX[k]); +static int test_aead_vec(const char *driver, int enc, + const struct aead_testvec *vec, unsigned int vec_num, + struct aead_request *req, + struct cipher_test_sglists *tsgls) +{ + unsigned int i; + int err; - n = template[i].tap[k]; - if (k == template[i].np - 1 && enc) - n += authsize; + if (enc && vec->novrfy) + return 0; - if (memcmp(q, expected_output + temp, n)) { - pr_err("alg: aead%s: Chunk test %d failed on %s at page %u for %s\n", - d, j, e, k, algo); - hexdump(q, n); - goto out; - } + for (i = 0; i < ARRAY_SIZE(default_cipher_testvec_configs); i++) { + err = test_aead_vec_cfg(driver, enc, vec, vec_num, + &default_cipher_testvec_configs[i], + req, tsgls); + if (err) + return err; + } - q += n; - if (k == template[i].np - 1 && !enc) { - if (!diff_dst && memcmp(q, input + temp + n, - authsize)) - n = authsize; - else - n = 0; - } else { - for (n = 0; offset_in_page(q + n) && q[n]; n++) - ; - } - if (n) { - pr_err("alg: aead%s: Result buffer corruption in chunk test %d on %s at page %u for %s: %u bytes:\n", - d, j, e, k, algo, n); - hexdump(q, n); - goto out; - } +#ifdef CONFIG_CRYPTO_MANAGER_EXTRA_TESTS + if (!noextratests) { + struct testvec_config cfg; + char cfgname[TESTVEC_CONFIG_NAMELEN]; - temp += template[i].tap[k]; + for (i = 0; i < fuzz_iterations; i++) { + generate_random_testvec_config(&cfg, cfgname, + sizeof(cfgname)); + err = test_aead_vec_cfg(driver, enc, vec, vec_num, + &cfg, req, tsgls); + if (err) + return err; } } +#endif + return 0; +} - ret = 0; +static int test_aead(const char *driver, int enc, + const struct aead_test_suite *suite, + struct aead_request *req, + struct cipher_test_sglists *tsgls) +{ + unsigned int i; + int err; -out: - aead_request_free(req); - kfree(sg); -out_nosg: - if (diff_dst) - testmgr_free_buf(xoutbuf); -out_nooutbuf: - testmgr_free_buf(axbuf); -out_noaxbuf: - testmgr_free_buf(xbuf); -out_noxbuf: - kfree(key); - kfree(iv); - return ret; + for (i = 0; i < suite->count; i++) { + err = test_aead_vec(driver, enc, &suite->vecs[i], i, req, + tsgls); + if (err) + return err; + } + return 0; } -static int test_aead(struct crypto_aead *tfm, int enc, - const struct aead_testvec *template, unsigned int tcount) +static int alg_test_aead(const struct alg_test_desc *desc, const char *driver, + u32 type, u32 mask) { - unsigned int alignmask; - int ret; + const struct aead_test_suite *suite = &desc->suite.aead; + struct crypto_aead *tfm; + struct aead_request *req = NULL; + struct cipher_test_sglists *tsgls = NULL; + int err; - /* test 'dst == src' case */ - ret = __test_aead(tfm, enc, template, tcount, false, 0); - if (ret) - return ret; + if (suite->count <= 0) { + pr_err("alg: aead: empty test suite for %s\n", driver); + return -EINVAL; + } - /* test 'dst != src' case */ - ret = __test_aead(tfm, enc, template, tcount, true, 0); - if (ret) - return ret; + tfm = crypto_alloc_aead(driver, type, mask); + if (IS_ERR(tfm)) { + pr_err("alg: aead: failed to allocate transform for %s: %ld\n", + driver, PTR_ERR(tfm)); + return PTR_ERR(tfm); + } - /* test unaligned buffers, check with one byte offset */ - ret = __test_aead(tfm, enc, template, tcount, true, 1); - if (ret) - return ret; + req = aead_request_alloc(tfm, GFP_KERNEL); + if (!req) { + pr_err("alg: aead: failed to allocate request for %s\n", + driver); + err = -ENOMEM; + goto out; + } - alignmask = crypto_tfm_alg_alignmask(&tfm->base); - if (alignmask) { - /* Check if alignment mask for tfm is correctly set. */ - ret = __test_aead(tfm, enc, template, tcount, true, - alignmask + 1); - if (ret) - return ret; + tsgls = alloc_cipher_test_sglists(); + if (!tsgls) { + pr_err("alg: aead: failed to allocate test buffers for %s\n", + driver); + err = -ENOMEM; + goto out; } - return 0; + err = test_aead(driver, ENCRYPT, suite, req, tsgls); + if (err) + goto out; + + err = test_aead(driver, DECRYPT, suite, req, tsgls); +out: + free_cipher_test_sglists(tsgls); + aead_request_free(req); + crypto_free_aead(tfm); + return err; } static int test_cipher(struct crypto_cipher *tfm, int enc, @@ -2274,28 +2053,6 @@ out: return err; } -static int alg_test_aead(const struct alg_test_desc *desc, const char *driver, - u32 type, u32 mask) -{ - const struct aead_test_suite *suite = &desc->suite.aead; - struct crypto_aead *tfm; - int err; - - tfm = crypto_alloc_aead(driver, type, mask); - if (IS_ERR(tfm)) { - printk(KERN_ERR "alg: aead: Failed to load transform for %s: " - "%ld\n", driver, PTR_ERR(tfm)); - return PTR_ERR(tfm); - } - - err = test_aead(tfm, ENCRYPT, suite->vecs, suite->count); - if (!err) - err = test_aead(tfm, DECRYPT, suite->vecs, suite->count); - - crypto_free_aead(tfm); - return err; -} - static int alg_test_cipher(const struct alg_test_desc *desc, const char *driver, u32 type, u32 mask) { |