From: Ard Biesheuvel <ard.biesheuvel@linaro.org>
To: linux-crypto@vger.kernel.org
Cc: herbert@gondor.apana.org.au, ebiggers@kernel.org,
gmazyland@gmail.com, Ard Biesheuvel <ard.biesheuvel@linaro.org>,
Pascal van Leeuwen <pvanleeuwen@verimatrix.com>,
Ondrej Mosnacek <omosnace@redhat.com>
Subject: [PATCH v2] crypto: xts - add support for ciphertext stealing
Date: Fri, 9 Aug 2019 20:14:57 +0300 [thread overview]
Message-ID: <20190809171457.12400-1-ard.biesheuvel@linaro.org> (raw)
Add support for the missing ciphertext stealing part of the XTS-AES
specification, which permits inputs of any size >= the block size.
Cc: Pascal van Leeuwen <pvanleeuwen@verimatrix.com>
Cc: Ondrej Mosnacek <omosnace@redhat.com>
Tested-by: Milan Broz <gmazyland@gmail.com>
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
---
v2: fix scatterlist issue in async handling
remove stale comment
crypto/xts.c | 152 +++++++++++++++++---
1 file changed, 132 insertions(+), 20 deletions(-)
diff --git a/crypto/xts.c b/crypto/xts.c
index 11211003db7e..78c19e4dde7d 100644
--- a/crypto/xts.c
+++ b/crypto/xts.c
@@ -1,8 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/* XTS: as defined in IEEE1619/D16
* http://grouper.ieee.org/groups/1619/email/pdf00086.pdf
- * (sector sizes which are not a multiple of 16 bytes are,
- * however currently unsupported)
*
* Copyright (c) 2007 Rik Snel <rsnel@cube.dyndns.org>
*
@@ -34,6 +32,8 @@ struct xts_instance_ctx {
struct rctx {
le128 t;
+ struct scatterlist *tail;
+ struct scatterlist sg[2];
struct skcipher_request subreq;
};
@@ -84,10 +84,11 @@ static int setkey(struct crypto_skcipher *parent, const u8 *key,
* mutliple calls to the 'ecb(..)' instance, which usually would be slower than
* just doing the gf128mul_x_ble() calls again.
*/
-static int xor_tweak(struct skcipher_request *req, bool second_pass)
+static int xor_tweak(struct skcipher_request *req, bool second_pass, bool enc)
{
struct rctx *rctx = skcipher_request_ctx(req);
struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
+ const bool cts = (req->cryptlen % XTS_BLOCK_SIZE);
const int bs = XTS_BLOCK_SIZE;
struct skcipher_walk w;
le128 t = rctx->t;
@@ -109,6 +110,20 @@ static int xor_tweak(struct skcipher_request *req, bool second_pass)
wdst = w.dst.virt.addr;
do {
+ if (unlikely(cts) &&
+ w.total - w.nbytes + avail < 2 * XTS_BLOCK_SIZE) {
+ if (!enc) {
+ if (second_pass)
+ rctx->t = t;
+ gf128mul_x_ble(&t, &t);
+ }
+ le128_xor(wdst, &t, wsrc);
+ if (enc && second_pass)
+ gf128mul_x_ble(&rctx->t, &t);
+ skcipher_walk_done(&w, avail - bs);
+ return 0;
+ }
+
le128_xor(wdst++, &t, wsrc++);
gf128mul_x_ble(&t, &t);
} while ((avail -= bs) >= bs);
@@ -119,17 +134,71 @@ static int xor_tweak(struct skcipher_request *req, bool second_pass)
return err;
}
-static int xor_tweak_pre(struct skcipher_request *req)
+static int xor_tweak_pre(struct skcipher_request *req, bool enc)
{
- return xor_tweak(req, false);
+ return xor_tweak(req, false, enc);
}
-static int xor_tweak_post(struct skcipher_request *req)
+static int xor_tweak_post(struct skcipher_request *req, bool enc)
{
- return xor_tweak(req, true);
+ return xor_tweak(req, true, enc);
}
-static void crypt_done(struct crypto_async_request *areq, int err)
+static void cts_done(struct crypto_async_request *areq, int err)
+{
+ struct skcipher_request *req = areq->data;
+ le128 b;
+
+ if (!err) {
+ struct rctx *rctx = skcipher_request_ctx(req);
+
+ scatterwalk_map_and_copy(&b, rctx->tail, 0, XTS_BLOCK_SIZE, 0);
+ le128_xor(&b, &rctx->t, &b);
+ scatterwalk_map_and_copy(&b, rctx->tail, 0, XTS_BLOCK_SIZE, 1);
+ }
+
+ skcipher_request_complete(req, err);
+}
+
+static int cts_final(struct skcipher_request *req,
+ int (*crypt)(struct skcipher_request *req))
+{
+ struct priv *ctx = crypto_skcipher_ctx(crypto_skcipher_reqtfm(req));
+ int offset = req->cryptlen & ~(XTS_BLOCK_SIZE - 1);
+ struct rctx *rctx = skcipher_request_ctx(req);
+ struct skcipher_request *subreq = &rctx->subreq;
+ int tail = req->cryptlen % XTS_BLOCK_SIZE;
+ le128 b[2];
+ int err;
+
+ rctx->tail = scatterwalk_ffwd(rctx->sg, req->dst,
+ offset - XTS_BLOCK_SIZE);
+
+ scatterwalk_map_and_copy(b, rctx->tail, 0, XTS_BLOCK_SIZE, 0);
+ memcpy(b + 1, b, tail);
+ scatterwalk_map_and_copy(b, req->src, offset, tail, 0);
+
+ le128_xor(b, &rctx->t, b);
+
+ scatterwalk_map_and_copy(b, rctx->tail, 0, XTS_BLOCK_SIZE + tail, 1);
+
+ skcipher_request_set_tfm(subreq, ctx->child);
+ skcipher_request_set_callback(subreq, req->base.flags, cts_done, req);
+ skcipher_request_set_crypt(subreq, rctx->tail, rctx->tail,
+ XTS_BLOCK_SIZE, NULL);
+
+ err = crypt(subreq);
+ if (err)
+ return err;
+
+ scatterwalk_map_and_copy(b, rctx->tail, 0, XTS_BLOCK_SIZE, 0);
+ le128_xor(b, &rctx->t, b);
+ scatterwalk_map_and_copy(b, rctx->tail, 0, XTS_BLOCK_SIZE, 1);
+
+ return 0;
+}
+
+static void encrypt_done(struct crypto_async_request *areq, int err)
{
struct skcipher_request *req = areq->data;
@@ -137,47 +206,90 @@ static void crypt_done(struct crypto_async_request *areq, int err)
struct rctx *rctx = skcipher_request_ctx(req);
rctx->subreq.base.flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP;
- err = xor_tweak_post(req);
+ err = xor_tweak_post(req, true);
+
+ if (!err && unlikely(req->cryptlen % XTS_BLOCK_SIZE)) {
+ err = cts_final(req, crypto_skcipher_encrypt);
+ if (err == -EINPROGRESS)
+ return;
+ }
}
skcipher_request_complete(req, err);
}
-static void init_crypt(struct skcipher_request *req)
+static void decrypt_done(struct crypto_async_request *areq, int err)
+{
+ struct skcipher_request *req = areq->data;
+
+ if (!err) {
+ struct rctx *rctx = skcipher_request_ctx(req);
+
+ rctx->subreq.base.flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP;
+ err = xor_tweak_post(req, false);
+
+ if (!err && unlikely(req->cryptlen % XTS_BLOCK_SIZE)) {
+ err = cts_final(req, crypto_skcipher_decrypt);
+ if (err == -EINPROGRESS)
+ return;
+ }
+ }
+
+ skcipher_request_complete(req, err);
+}
+
+static int init_crypt(struct skcipher_request *req, crypto_completion_t compl)
{
struct priv *ctx = crypto_skcipher_ctx(crypto_skcipher_reqtfm(req));
struct rctx *rctx = skcipher_request_ctx(req);
struct skcipher_request *subreq = &rctx->subreq;
+ if (req->cryptlen < XTS_BLOCK_SIZE)
+ return -EINVAL;
+
skcipher_request_set_tfm(subreq, ctx->child);
- skcipher_request_set_callback(subreq, req->base.flags, crypt_done, req);
+ skcipher_request_set_callback(subreq, req->base.flags, compl, req);
skcipher_request_set_crypt(subreq, req->dst, req->dst,
- req->cryptlen, NULL);
+ req->cryptlen & ~(XTS_BLOCK_SIZE - 1), NULL);
/* calculate first value of T */
crypto_cipher_encrypt_one(ctx->tweak, (u8 *)&rctx->t, req->iv);
+
+ return 0;
}
static int encrypt(struct skcipher_request *req)
{
struct rctx *rctx = skcipher_request_ctx(req);
struct skcipher_request *subreq = &rctx->subreq;
+ int err;
- init_crypt(req);
- return xor_tweak_pre(req) ?:
- crypto_skcipher_encrypt(subreq) ?:
- xor_tweak_post(req);
+ err = init_crypt(req, encrypt_done) ?:
+ xor_tweak_pre(req, true) ?:
+ crypto_skcipher_encrypt(subreq) ?:
+ xor_tweak_post(req, true);
+
+ if (err || likely((req->cryptlen % XTS_BLOCK_SIZE) == 0))
+ return err;
+
+ return cts_final(req, crypto_skcipher_encrypt);
}
static int decrypt(struct skcipher_request *req)
{
struct rctx *rctx = skcipher_request_ctx(req);
struct skcipher_request *subreq = &rctx->subreq;
+ int err;
+
+ err = init_crypt(req, decrypt_done) ?:
+ xor_tweak_pre(req, false) ?:
+ crypto_skcipher_decrypt(subreq) ?:
+ xor_tweak_post(req, false);
+
+ if (err || likely((req->cryptlen % XTS_BLOCK_SIZE) == 0))
+ return err;
- init_crypt(req);
- return xor_tweak_pre(req) ?:
- crypto_skcipher_decrypt(subreq) ?:
- xor_tweak_post(req);
+ return cts_final(req, crypto_skcipher_decrypt);
}
static int init_tfm(struct crypto_skcipher *tfm)
--
2.17.1
next reply other threads:[~2019-08-09 17:15 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-08-09 17:14 Ard Biesheuvel [this message]
2019-08-15 12:08 ` [PATCH v2] crypto: xts - add support for ciphertext stealing Herbert Xu
2019-08-16 1:02 ` Eric Biggers
2019-08-16 5:21 ` Ard Biesheuvel
2019-08-19 6:33 ` Herbert Xu
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20190809171457.12400-1-ard.biesheuvel@linaro.org \
--to=ard.biesheuvel@linaro.org \
--cc=ebiggers@kernel.org \
--cc=gmazyland@gmail.com \
--cc=herbert@gondor.apana.org.au \
--cc=linux-crypto@vger.kernel.org \
--cc=omosnace@redhat.com \
--cc=pvanleeuwen@verimatrix.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).