summaryrefslogtreecommitdiff
path: root/crypto
diff options
context:
space:
mode:
authorCristian Stoica <cristian.stoica@freescale.com>2014-03-17 07:36:58 (GMT)
committerJose Rivera <German.Rivera@freescale.com>2014-03-28 13:40:46 (GMT)
commita64f733483ca7205f93d3e7c7eb5a60186e81bad (patch)
tree6a733f33c7929046661c16b6ad0160f0c81f4d82 /crypto
parentfd67b46ff7ec36d2cd45d03e3a05592c394d6c25 (diff)
downloadlinux-fsl-qoriq-a64f733483ca7205f93d3e7c7eb5a60186e81bad.tar.xz
crypto: tls - fix encryption for buffers larger than 4Kb
This fixes a defect in TLS driver that prevents correct encryption of buffers larger than a single page. The scatterlists were incorrectly chained and data above 4Kb was not encrypted. Change-Id: I9bf558055312f14e5acb3ba99f8d14d3f89aad07 Signed-off-by: Cristian Stoica <cristian.stoica@freescale.com> Reviewed-on: http://git.am.freescale.net:8181/10082 Tested-by: Review Code-CDREVIEW <CDREVIEW@freescale.com> Reviewed-by: Horia Ioan Geanta Neag <horia.geanta@freescale.com> Reviewed-by: Jose Rivera <German.Rivera@freescale.com> (cherry picked from commit 02cc641eaeccc7dbb422f20e28b2022f48074268) Reviewed-on: http://git.am.freescale.net:8181/10381 Reviewed-by: Mircea Pop <mircea.pop@freescale.com> Reviewed-by: Alexandru Porosanu <alexandru.porosanu@freescale.com>
Diffstat (limited to 'crypto')
-rw-r--r--crypto/tls.c66
1 files changed, 47 insertions, 19 deletions
diff --git a/crypto/tls.c b/crypto/tls.c
index 99a88b9..38cd1ba 100644
--- a/crypto/tls.c
+++ b/crypto/tls.c
@@ -146,7 +146,7 @@ static int crypto_tls_genicv(u8 *hash, struct scatterlist *src,
if (srclen) {
sg_init_table(icv, 2);
sg_set_page(icv, sg_page(assoc), assoc->length, assoc->offset);
- scatterwalk_crypto_chain(icv, src, 0, 2);
+ scatterwalk_sg_chain(icv, 2, src);
} else {
icv = assoc;
}
@@ -210,7 +210,7 @@ static int crypto_tls_encrypt(struct aead_request *req)
unsigned int cryptlen, phashlen;
struct scatterlist *cipher = treq_ctx->cipher;
- struct scatterlist *icv = treq_ctx->icv;
+ struct scatterlist *sg, *src_last = NULL;
int err;
/*
* The hash and the cipher are applied at different times and their
@@ -231,37 +231,65 @@ static int crypto_tls_encrypt(struct aead_request *req)
crypto_ahash_alignmask(ctx->auth) + 1);
/*
- * STEP 1: create ICV and add necessary padding
+ * STEP 1: create ICV together with necessary padding
*/
err = crypto_tls_gen_padicv(hash, &phashlen, req);
if (err)
return err;
- /* we can reuse treq_cfr->icv because crypto_tls_gen_padicv finished */
- sg_init_one(icv, hash, phashlen);
-
/*
- * STEP 2: encrypt the frame and return the result
- * Chain the payload with the hash. If the payload is empty, use
- * directly the hash scatterlist
+ * STEP 2: Hash and padding are combined with the payload
+ * depending on the form it arrives. Scatter tables must have at least
+ * one page of data before chaining with another table and can't have
+ * an empty data page. The following code addresses these requirements.
+ *
+ * For same-destination, hash is copied directly after the
+ * payload since the buffers must have enough space for encryption.
+ * For different destination there are several casess to check.
+ * If the payload is empty, only the hash is encrypted, otherwise the
+ * payload scatterlist is merged with the hash. A special merging case
+ * is when the payload has only one page of data. In that case the
+ * payload page is moved to another scatterlist and prepared there for
+ * encryption.
*/
- if (req->cryptlen) {
- sg_init_table(cipher, 2);
- sg_set_page(cipher, sg_page(req->src), req->cryptlen,
- req->src->offset);
- scatterwalk_crypto_chain(cipher, icv, 0, 2);
+
+ if (req->src == req->dst) {
+ scatterwalk_map_and_copy(hash, req->src, req->cryptlen,
+ phashlen, 1);
} else {
- cipher = icv;
+ if (req->cryptlen) {
+ sg_init_table(cipher, 2);
+ sg_set_buf(cipher + 1, hash, phashlen);
+ if (sg_is_last(req->src)) {
+ sg_set_page(cipher, sg_page(req->src),
+ req->src->length, req->src->offset);
+ req->src = cipher;
+ } else {
+ for (sg = req->src; sg; sg = sg_next(sg))
+ src_last = sg;
+ sg_set_page(cipher, sg_page(src_last),
+ src_last->length, src_last->offset);
+ scatterwalk_sg_chain(src_last, 1, cipher);
+ }
+ } else {
+ sg_init_one(req->src, hash, phashlen);
+ }
}
- /* prepare the cipher request */
+
+ /*
+ * STEP 3: encrypt the frame and return the result
+ */
cryptlen = req->cryptlen + phashlen;
ablkcipher_request_set_tfm(abreq, ctx->enc);
- ablkcipher_request_set_crypt(abreq, cipher, req->dst, cryptlen,
+ ablkcipher_request_set_crypt(abreq, req->src, req->dst, cryptlen,
req->iv);
- /* mark the completion of the whole encryption request */
+ /* set the callback for encryption request termination */
ablkcipher_request_set_callback(abreq, aead_request_flags(req),
req->base.complete, req->base.data);
- /* Apply the cipher transform. The result will be in req->dst */
+ /*
+ * Apply the cipher transform. The result will be in req->dst when the
+ * asynchronuous call terminates
+ */
err = crypto_ablkcipher_encrypt(abreq);
return err;