[RFC PATCH 2/2] arm64: add support for AES using ARMv8 Crypto Extensions

Jussi Kivilinna jussi.kivilinna at iki.fi
Sat Sep 14 04:08:13 EDT 2013


On 13.09.2013 18:08, Ard Biesheuvel wrote:
> This adds ARMv8 Crypto Extensions based implemenations of
> AES in CBC, CTR and XTS mode.
> 
> Signed-off-by: Ard Biesheuvel <ard.biesheuvel at linaro.org>
> ---
..snip..
> +static int xts_set_key(struct crypto_tfm *tfm, const u8 *in_key,
> +		       unsigned int key_len)
> +{
> +	struct crypto_aes_xts_ctx *ctx = crypto_tfm_ctx(tfm);
> +	u32 *flags = &tfm->crt_flags;
> +	int ret;
> +
> +	ret = crypto_aes_expand_key(&ctx->key1, in_key, key_len/2);
> +	if (!ret)
> +		ret = crypto_aes_expand_key(&ctx->key2, &in_key[key_len/2],
> +					    key_len/2);

Use checkpatch.

> +	if (!ret)
> +		return 0;
> +
> +	*flags |= CRYPTO_TFM_RES_BAD_KEY_LEN;
> +	return -EINVAL;
> +}
> +
> +static int cbc_encrypt(struct blkcipher_desc *desc, struct scatterlist *dst,
> +		       struct scatterlist *src, unsigned int nbytes)
> +{
> +	struct crypto_aes_ctx *ctx = crypto_blkcipher_ctx(desc->tfm);
> +	int err, first, rounds = 6 + ctx->key_length/4;
> +	struct blkcipher_walk walk;
> +	unsigned int blocks;
> +
> +	blkcipher_walk_init(&walk, dst, src, nbytes);
> +	err = blkcipher_walk_virt(desc, &walk);
> +
> +	kernel_neon_begin();

Is sleeping allowed within kernel_neon_begin/end block? If not, you need to
clear CRYPTO_TFM_REQ_MAY_SLEEP on desc->flags. Otherwise blkcipher_walk_done
might sleep.

> +	for (first = 1; (blocks = (walk.nbytes / AES_BLOCK_SIZE)); first = 0) {
> +		aesce_cbc_encrypt(walk.dst.virt.addr, walk.src.virt.addr,
> +				  (u8*)ctx->key_enc, rounds, blocks, walk.iv,
> +				  first);
> +
> +		err = blkcipher_walk_done(desc, &walk, blocks * AES_BLOCK_SIZE);
> +	}
> +	kernel_neon_end();
> +
> +	/* non-integral sizes are not supported in CBC */
> +	if (unlikely(walk.nbytes))
> +		err = -EINVAL;

I think blkcipher_walk_done already does this check by comparing against
alg.cra_blocksize.

> +
> +	return err;
> +}
..snip..
> +
> +static int ctr_encrypt(struct blkcipher_desc *desc, struct scatterlist *dst,
> +		       struct scatterlist *src, unsigned int nbytes)
> +{
> +	struct crypto_aes_ctx *ctx = crypto_blkcipher_ctx(desc->tfm);
> +	int err, first, rounds = 6 + ctx->key_length/4;
> +	struct blkcipher_walk walk;
> +	u8 ctr[AES_BLOCK_SIZE];
> +
> +	blkcipher_walk_init(&walk, dst, src, nbytes);
> +	err = blkcipher_walk_virt(desc, &walk);
> +
> +	memcpy(ctr, walk.iv, AES_BLOCK_SIZE);
> +
> +	kernel_neon_begin();
> +	for (first = 1; (nbytes = walk.nbytes); first = 0) {
> +		aesce_ctr_encrypt(walk.dst.virt.addr, walk.src.virt.addr,
> +				  (u8*)ctx->key_enc, rounds, nbytes, ctr, first);
> +
> +		err = blkcipher_walk_done(desc, &walk, 0);
> +
> +		/* non-integral block *must* be the last one */
> +		if (unlikely(walk.nbytes && (nbytes & (AES_BLOCK_SIZE-1)))) {
> +			err = -EINVAL;

Other CTR implementations do not have this.. not needed?

> +			break;
> +		}
> +	}
..snip..
> +static struct crypto_alg aesce_cbc_algs[] = { {
> +	.cra_name		= "__cbc-aes-aesce",
> +	.cra_driver_name	= "__driver-cbc-aes-aesce",
> +	.cra_priority		= 0,
> +	.cra_flags		= CRYPTO_ALG_TYPE_BLKCIPHER,
> +	.cra_blocksize		= AES_BLOCK_SIZE,
> +	.cra_ctxsize		= sizeof(struct crypto_aes_ctx),
> +	.cra_alignmask		= 0,
> +	.cra_type		= &crypto_blkcipher_type,
> +	.cra_module		= THIS_MODULE,
> +	.cra_u = {
> +		.blkcipher = {
> +			.min_keysize	= AES_MIN_KEY_SIZE,
> +			.max_keysize	= AES_MAX_KEY_SIZE,
> +			.ivsize		= AES_BLOCK_SIZE,
> +			.setkey		= crypto_aes_set_key,
> +			.encrypt	= cbc_encrypt,
> +			.decrypt	= cbc_decrypt,
> +		},
> +	},
> +}, {
> +	.cra_name		= "__ctr-aes-aesce",
> +	.cra_driver_name	= "__driver-ctr-aes-aesce",
> +	.cra_priority		= 0,
> +	.cra_flags		= CRYPTO_ALG_TYPE_BLKCIPHER,
> +	.cra_blocksize		= AES_BLOCK_SIZE,

CTR mode is stream cipher, cra_blocksize must be set to 1.

This should have been picked up by in-kernel run-time tests, check
CONFIG_CRYPTO_MANAGER_DISABLE_TESTS (and CONFIG_CRYPTO_TEST/tcrypt
module).

> +	.cra_ctxsize		= sizeof(struct crypto_aes_ctx),
> +	.cra_alignmask		= 0,
> +	.cra_type		= &crypto_blkcipher_type,
> +	.cra_module		= THIS_MODULE,
> +	.cra_u = {
> +		.blkcipher = {
> +			.min_keysize	= AES_MIN_KEY_SIZE,
> +			.max_keysize	= AES_MAX_KEY_SIZE,
> +			.ivsize		= AES_BLOCK_SIZE,
> +			.setkey		= crypto_aes_set_key,
> +			.encrypt	= ctr_encrypt,
> +			.decrypt	= ctr_encrypt,
> +		},
> +	},
> +}, {
> +	.cra_name		= "__xts-aes-aesce",
> +	.cra_driver_name	= "__driver-xts-aes-aesce",
> +	.cra_priority		= 0,
> +	.cra_flags		= CRYPTO_ALG_TYPE_BLKCIPHER,
> +	.cra_blocksize		= AES_BLOCK_SIZE,
> +	.cra_ctxsize		= sizeof(struct crypto_aes_xts_ctx),
> +	.cra_alignmask		= 0,
> +	.cra_type		= &crypto_blkcipher_type,
> +	.cra_module		= THIS_MODULE,
> +	.cra_u = {
> +		.blkcipher = {
> +			.min_keysize	= 2*AES_MIN_KEY_SIZE,
> +			.max_keysize	= 2*AES_MAX_KEY_SIZE,
> +			.ivsize		= AES_BLOCK_SIZE,
> +			.setkey		= xts_set_key,
> +			.encrypt	= xts_encrypt,
> +			.decrypt	= xts_decrypt,
> +		},
> +	},
> +}, {
> +	.cra_name		= "cbc(aes)",
> +	.cra_driver_name	= "cbc-aes-aesce",
> +	.cra_priority		= 200,
> +	.cra_flags		= CRYPTO_ALG_TYPE_ABLKCIPHER|CRYPTO_ALG_ASYNC,
> +	.cra_blocksize		= AES_BLOCK_SIZE,
> +	.cra_ctxsize		= sizeof(struct async_helper_ctx),
> +	.cra_alignmask		= 0,
> +	.cra_type		= &crypto_ablkcipher_type,
> +	.cra_module		= THIS_MODULE,
> +	.cra_init		= ablk_init,
> +	.cra_exit		= ablk_exit,
> +	.cra_u = {
> +		.ablkcipher = {
> +			.min_keysize	= AES_MIN_KEY_SIZE,
> +			.max_keysize	= AES_MAX_KEY_SIZE,
> +			.ivsize		= AES_BLOCK_SIZE,
> +			.setkey		= ablk_set_key,
> +			.encrypt	= ablk_encrypt,
> +			.decrypt	= ablk_decrypt,
> +		}
> +	}
> +}, {
> +	.cra_name		= "ctr(aes)",
> +	.cra_driver_name	= "ctr-aes-aesce",
> +	.cra_priority		= 200,
> +	.cra_flags		= CRYPTO_ALG_TYPE_ABLKCIPHER|CRYPTO_ALG_ASYNC,
> +	.cra_blocksize		= AES_BLOCK_SIZE,

cra_blocksize = 1

> +	.cra_ctxsize		= sizeof(struct async_helper_ctx),
> +	.cra_alignmask		= 0,
..snip..
> +ENTRY(aesce_xts_encrypt)
> +	tst		w7, #1				// first call?
> +	beq		.Lencmore
> +
> +	ld1		{v0.16b}, [x6]
> +	load_round_keys	w4, x2
> +	encrypt_block	v3.16b, v0.16b, w4		// first tweak
> +	load_round_keys	w4, x3
> +	ldr		q4, .Lxts_mul_x
> +	b		.Lencfirst
> +.Lencmore:
> +	NEXT_TWEAK	(v3, v4, v8)
> +.Lencfirst:
> +	subs		x5, x5, #16
> +.Lencloop:
> +	ld1		{v1.16b}, [x1], #16
> +	eor		v0.16b, v1.16b, v3.16b
> +	encrypt_block	v2.16b, v0.16b, w4
> +	eor		v2.16b, v2.16b, v3.16b
> +	st1		{v2.16b}, [x0], #16
> +	beq		.Lencout
> +
> +	NEXT_TWEAK	(v3, v4, v8)
> +	subs		x5, x5, #16
> +	bpl		.Lencloop
> +
> +	sub		x0, x0, #16
> +	add		x5, x5, #16
> +	mov		x2, x0
> +.Lencsteal:
> +	ldrb		w6, [x1], #1
> +	ldrb		w7, [x2, #-16]
> +	strb		w6, [x2, #-16]
> +	strb		w7, [x2], #1
> +	subs		x5, x5, #1
> +	bne		.Lencsteal

Cipher text stealing here is dead-code, since alg.cra_blocksize is set
to 16 bytes.

Currently other XTS implementations do not have CTS either so this is
not really needed anyway atm (crypto/xts.c: "sector sizes which are not
a multiple of 16 bytes are, however currently unsupported").

-Jussi



More information about the linux-arm mailing list