Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 91308019 authored by Romain Izard's avatar Romain Izard Committed by Herbert Xu
Browse files

crypto: atmel-aes - properly set IV after {en,de}crypt



Certain cipher modes like CTS expect the IV (req->info) of
ablkcipher_request (or equivalently req->iv of skcipher_request) to
contain the last ciphertext block when the {en,de}crypt operation is done.

Fix this issue for the Atmel AES hardware engine. The tcrypt test
case for cts(cbc(aes)) is now correctly passed.

In the case of in-place decryption, copy the ciphertext in an
intermediate buffer before decryption.

Signed-off-by: default avatarRomain Izard <romain.izard.pro@gmail.com>
Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
parent 142a27f0
Loading
Loading
Loading
Loading
+37 −3
Original line number Diff line number Diff line
@@ -111,6 +111,7 @@ struct atmel_aes_base_ctx {
	int			keylen;
	u32			key[AES_KEYSIZE_256 / sizeof(u32)];
	u16			block_size;
	bool			is_aead;
};

struct atmel_aes_ctx {
@@ -157,6 +158,7 @@ struct atmel_aes_authenc_ctx {

struct atmel_aes_reqctx {
	unsigned long		mode;
	u32			lastc[AES_BLOCK_SIZE / sizeof(u32)];
};

#ifdef CONFIG_CRYPTO_DEV_ATMEL_AUTHENC
@@ -498,12 +500,34 @@ static void atmel_aes_authenc_complete(struct atmel_aes_dev *dd, int err);
static inline int atmel_aes_complete(struct atmel_aes_dev *dd, int err)
{
#ifdef CONFIG_CRYPTO_DEV_ATMEL_AUTHENC
	if (dd->ctx->is_aead)
		atmel_aes_authenc_complete(dd, err);
#endif

	clk_disable(dd->iclk);
	dd->flags &= ~AES_FLAGS_BUSY;

	if (!dd->ctx->is_aead) {
		struct ablkcipher_request *req =
			ablkcipher_request_cast(dd->areq);
		struct atmel_aes_reqctx *rctx = ablkcipher_request_ctx(req);
		struct crypto_ablkcipher *ablkcipher =
			crypto_ablkcipher_reqtfm(req);
		int ivsize = crypto_ablkcipher_ivsize(ablkcipher);

		if (rctx->mode & AES_FLAGS_ENCRYPT) {
			scatterwalk_map_and_copy(req->info, req->dst,
				req->nbytes - ivsize, ivsize, 0);
		} else {
			if (req->src == req->dst) {
				memcpy(req->info, rctx->lastc, ivsize);
			} else {
				scatterwalk_map_and_copy(req->info, req->src,
					req->nbytes - ivsize, ivsize, 0);
			}
		}
	}

	if (dd->is_async)
		dd->areq->complete(dd->areq, err);

@@ -1072,11 +1096,11 @@ static int atmel_aes_ctr_start(struct atmel_aes_dev *dd)

static int atmel_aes_crypt(struct ablkcipher_request *req, unsigned long mode)
{
	struct atmel_aes_base_ctx *ctx;
	struct crypto_ablkcipher *ablkcipher = crypto_ablkcipher_reqtfm(req);
	struct atmel_aes_base_ctx *ctx = crypto_ablkcipher_ctx(ablkcipher);
	struct atmel_aes_reqctx *rctx;
	struct atmel_aes_dev *dd;

	ctx = crypto_ablkcipher_ctx(crypto_ablkcipher_reqtfm(req));
	switch (mode & AES_FLAGS_OPMODE_MASK) {
	case AES_FLAGS_CFB8:
		ctx->block_size = CFB8_BLOCK_SIZE;
@@ -1098,6 +1122,7 @@ static int atmel_aes_crypt(struct ablkcipher_request *req, unsigned long mode)
		ctx->block_size = AES_BLOCK_SIZE;
		break;
	}
	ctx->is_aead = false;

	dd = atmel_aes_find_dev(ctx);
	if (!dd)
@@ -1106,6 +1131,13 @@ static int atmel_aes_crypt(struct ablkcipher_request *req, unsigned long mode)
	rctx = ablkcipher_request_ctx(req);
	rctx->mode = mode;

	if (!(mode & AES_FLAGS_ENCRYPT) && (req->src == req->dst)) {
		int ivsize = crypto_ablkcipher_ivsize(ablkcipher);

		scatterwalk_map_and_copy(rctx->lastc, req->src,
			(req->nbytes - ivsize), ivsize, 0);
	}

	return atmel_aes_handle_queue(dd, &req->base);
}

@@ -1740,6 +1772,7 @@ static int atmel_aes_gcm_crypt(struct aead_request *req,

	ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
	ctx->block_size = AES_BLOCK_SIZE;
	ctx->is_aead = true;

	dd = atmel_aes_find_dev(ctx);
	if (!dd)
@@ -2224,6 +2257,7 @@ static int atmel_aes_authenc_crypt(struct aead_request *req,

	rctx->base.mode = mode;
	ctx->block_size = AES_BLOCK_SIZE;
	ctx->is_aead = true;

	dd = atmel_aes_find_dev(ctx);
	if (!dd)