forked from mirrors/linux
		
	crypto: aegis - Add generic AEGIS AEAD implementations
This patch adds the generic implementation of the AEGIS family of AEAD algorithms (AEGIS-128, AEGIS-128L, and AEGIS-256). The original authors of AEGIS are Hongjun Wu and Bart Preneel. At the time of writing, AEGIS is one of the finalists in CAESAR, an open competition intended to select a portfolio of alternatives to the problematic AES-GCM: https://competitions.cr.yp.to/caesar-submissions.html https://competitions.cr.yp.to/round3/aegisv11.pdf Signed-off-by: Ondrej Mosnacek <omosnacek@gmail.com> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
This commit is contained in:
		
							parent
							
								
									15f47ce575
								
							
						
					
					
						commit
						f606a88e58
					
				
					 6 changed files with 1572 additions and 0 deletions
				
			
		|  | @ -289,6 +289,27 @@ config CRYPTO_CHACHA20POLY1305 | |||
| 	  with the Poly1305 authenticator. It is defined in RFC7539 for use in | ||||
| 	  IETF protocols. | ||||
| 
 | ||||
| config CRYPTO_AEGIS128 | ||||
| 	tristate "AEGIS-128 AEAD algorithm" | ||||
| 	select CRYPTO_AEAD | ||||
| 	select CRYPTO_AES  # for AES S-box tables | ||||
| 	help | ||||
| 	 Support for the AEGIS-128 dedicated AEAD algorithm. | ||||
| 
 | ||||
| config CRYPTO_AEGIS128L | ||||
| 	tristate "AEGIS-128L AEAD algorithm" | ||||
| 	select CRYPTO_AEAD | ||||
| 	select CRYPTO_AES  # for AES S-box tables | ||||
| 	help | ||||
| 	 Support for the AEGIS-128L dedicated AEAD algorithm. | ||||
| 
 | ||||
| config CRYPTO_AEGIS256 | ||||
| 	tristate "AEGIS-256 AEAD algorithm" | ||||
| 	select CRYPTO_AEAD | ||||
| 	select CRYPTO_AES  # for AES S-box tables | ||||
| 	help | ||||
| 	 Support for the AEGIS-256 dedicated AEAD algorithm. | ||||
| 
 | ||||
| config CRYPTO_SEQIV | ||||
| 	tristate "Sequence Number IV Generator" | ||||
| 	select CRYPTO_AEAD | ||||
|  |  | |||
|  | @ -86,6 +86,9 @@ obj-$(CONFIG_CRYPTO_KEYWRAP) += keywrap.o | |||
| obj-$(CONFIG_CRYPTO_GCM) += gcm.o | ||||
| obj-$(CONFIG_CRYPTO_CCM) += ccm.o | ||||
| obj-$(CONFIG_CRYPTO_CHACHA20POLY1305) += chacha20poly1305.o | ||||
| obj-$(CONFIG_CRYPTO_AEGIS128) += aegis128.o | ||||
| obj-$(CONFIG_CRYPTO_AEGIS128L) += aegis128l.o | ||||
| obj-$(CONFIG_CRYPTO_AEGIS256) += aegis256.o | ||||
| obj-$(CONFIG_CRYPTO_PCRYPT) += pcrypt.o | ||||
| obj-$(CONFIG_CRYPTO_CRYPTD) += cryptd.o | ||||
| obj-$(CONFIG_CRYPTO_MCRYPTD) += mcryptd.o | ||||
|  |  | |||
							
								
								
									
										80
									
								
								crypto/aegis.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								crypto/aegis.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,80 @@ | |||
| /* SPDX-License-Identifier: GPL-2.0 */ | ||||
| /*
 | ||||
|  * AEGIS common definitions | ||||
|  * | ||||
|  * Copyright (c) 2018 Ondrej Mosnacek <omosnacek@gmail.com> | ||||
|  * Copyright (c) 2018 Red Hat, Inc. All rights reserved. | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify it | ||||
|  * under the terms of the GNU General Public License as published by the Free | ||||
|  * Software Foundation; either version 2 of the License, or (at your option) | ||||
|  * any later version. | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _CRYPTO_AEGIS_H | ||||
| #define _CRYPTO_AEGIS_H | ||||
| 
 | ||||
| #include <crypto/aes.h> | ||||
| #include <linux/types.h> | ||||
| 
 | ||||
| #define AEGIS_BLOCK_SIZE 16 | ||||
| 
 | ||||
| union aegis_block { | ||||
| 	__le64 words64[AEGIS_BLOCK_SIZE / sizeof(__le64)]; | ||||
| 	u32 words32[AEGIS_BLOCK_SIZE / sizeof(u32)]; | ||||
| 	u8 bytes[AEGIS_BLOCK_SIZE]; | ||||
| }; | ||||
| 
 | ||||
| #define AEGIS_BLOCK_ALIGN (__alignof__(union aegis_block)) | ||||
| #define AEGIS_ALIGNED(p) IS_ALIGNED((uintptr_t)p, AEGIS_BLOCK_ALIGN) | ||||
| 
 | ||||
| static const union aegis_block crypto_aegis_const[2] = { | ||||
| 	{ .words64 = { | ||||
| 		cpu_to_le64(U64_C(0x0d08050302010100)), | ||||
| 		cpu_to_le64(U64_C(0x6279e99059372215)), | ||||
| 	} }, | ||||
| 	{ .words64 = { | ||||
| 		cpu_to_le64(U64_C(0xf12fc26d55183ddb)), | ||||
| 		cpu_to_le64(U64_C(0xdd28b57342311120)), | ||||
| 	} }, | ||||
| }; | ||||
| 
 | ||||
| static void crypto_aegis_block_xor(union aegis_block *dst, | ||||
| 				   const union aegis_block *src) | ||||
| { | ||||
| 	dst->words64[0] ^= src->words64[0]; | ||||
| 	dst->words64[1] ^= src->words64[1]; | ||||
| } | ||||
| 
 | ||||
| static void crypto_aegis_block_and(union aegis_block *dst, | ||||
| 				   const union aegis_block *src) | ||||
| { | ||||
| 	dst->words64[0] &= src->words64[0]; | ||||
| 	dst->words64[1] &= src->words64[1]; | ||||
| } | ||||
| 
 | ||||
| static void crypto_aegis_aesenc(union aegis_block *dst, | ||||
| 				const union aegis_block *src, | ||||
| 				const union aegis_block *key) | ||||
| { | ||||
| 	u32 *d = dst->words32; | ||||
| 	const u8  *s  = src->bytes; | ||||
| 	const u32 *k  = key->words32; | ||||
| 	const u32 *t0 = crypto_ft_tab[0]; | ||||
| 	const u32 *t1 = crypto_ft_tab[1]; | ||||
| 	const u32 *t2 = crypto_ft_tab[2]; | ||||
| 	const u32 *t3 = crypto_ft_tab[3]; | ||||
| 	u32 d0, d1, d2, d3; | ||||
| 
 | ||||
| 	d0 = t0[s[ 0]] ^ t1[s[ 5]] ^ t2[s[10]] ^ t3[s[15]] ^ k[0]; | ||||
| 	d1 = t0[s[ 4]] ^ t1[s[ 9]] ^ t2[s[14]] ^ t3[s[ 3]] ^ k[1]; | ||||
| 	d2 = t0[s[ 8]] ^ t1[s[13]] ^ t2[s[ 2]] ^ t3[s[ 7]] ^ k[2]; | ||||
| 	d3 = t0[s[12]] ^ t1[s[ 1]] ^ t2[s[ 6]] ^ t3[s[11]] ^ k[3]; | ||||
| 
 | ||||
| 	d[0] = d0; | ||||
| 	d[1] = d1; | ||||
| 	d[2] = d2; | ||||
| 	d[3] = d3; | ||||
| } | ||||
| 
 | ||||
| #endif /* _CRYPTO_AEGIS_H */ | ||||
							
								
								
									
										463
									
								
								crypto/aegis128.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										463
									
								
								crypto/aegis128.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,463 @@ | |||
| /*
 | ||||
|  * The AEGIS-128 Authenticated-Encryption Algorithm | ||||
|  * | ||||
|  * Copyright (c) 2017-2018 Ondrej Mosnacek <omosnacek@gmail.com> | ||||
|  * Copyright (C) 2017-2018 Red Hat, Inc. All rights reserved. | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify it | ||||
|  * under the terms of the GNU General Public License as published by the Free | ||||
|  * Software Foundation; either version 2 of the License, or (at your option) | ||||
|  * any later version. | ||||
|  */ | ||||
| 
 | ||||
| #include <crypto/algapi.h> | ||||
| #include <crypto/internal/aead.h> | ||||
| #include <crypto/internal/skcipher.h> | ||||
| #include <crypto/scatterwalk.h> | ||||
| #include <linux/err.h> | ||||
| #include <linux/init.h> | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/scatterlist.h> | ||||
| 
 | ||||
| #include "aegis.h" | ||||
| 
 | ||||
| #define AEGIS128_NONCE_SIZE 16 | ||||
| #define AEGIS128_STATE_BLOCKS 5 | ||||
| #define AEGIS128_KEY_SIZE 16 | ||||
| #define AEGIS128_MIN_AUTH_SIZE 8 | ||||
| #define AEGIS128_MAX_AUTH_SIZE 16 | ||||
| 
 | ||||
| struct aegis_state { | ||||
| 	union aegis_block blocks[AEGIS128_STATE_BLOCKS]; | ||||
| }; | ||||
| 
 | ||||
| struct aegis_ctx { | ||||
| 	union aegis_block key; | ||||
| }; | ||||
| 
 | ||||
| struct aegis128_ops { | ||||
| 	int (*skcipher_walk_init)(struct skcipher_walk *walk, | ||||
| 				  struct aead_request *req, bool atomic); | ||||
| 
 | ||||
| 	void (*crypt_chunk)(struct aegis_state *state, u8 *dst, | ||||
| 			    const u8 *src, unsigned int size); | ||||
| }; | ||||
| 
 | ||||
| static void crypto_aegis128_update(struct aegis_state *state) | ||||
| { | ||||
| 	union aegis_block tmp; | ||||
| 	unsigned int i; | ||||
| 
 | ||||
| 	tmp = state->blocks[AEGIS128_STATE_BLOCKS - 1]; | ||||
| 	for (i = AEGIS128_STATE_BLOCKS - 1; i > 0; i--) | ||||
| 		crypto_aegis_aesenc(&state->blocks[i], &state->blocks[i - 1], | ||||
| 				    &state->blocks[i]); | ||||
| 	crypto_aegis_aesenc(&state->blocks[0], &tmp, &state->blocks[0]); | ||||
| } | ||||
| 
 | ||||
| static void crypto_aegis128_update_a(struct aegis_state *state, | ||||
| 				     const union aegis_block *msg) | ||||
| { | ||||
| 	crypto_aegis128_update(state); | ||||
| 	crypto_aegis_block_xor(&state->blocks[0], msg); | ||||
| } | ||||
| 
 | ||||
| static void crypto_aegis128_update_u(struct aegis_state *state, const void *msg) | ||||
| { | ||||
| 	crypto_aegis128_update(state); | ||||
| 	crypto_xor(state->blocks[0].bytes, msg, AEGIS_BLOCK_SIZE); | ||||
| } | ||||
| 
 | ||||
| static void crypto_aegis128_init(struct aegis_state *state, | ||||
| 				 const union aegis_block *key, | ||||
| 				 const u8 *iv) | ||||
| { | ||||
| 	union aegis_block key_iv; | ||||
| 	unsigned int i; | ||||
| 
 | ||||
| 	key_iv = *key; | ||||
| 	crypto_xor(key_iv.bytes, iv, AEGIS_BLOCK_SIZE); | ||||
| 
 | ||||
| 	state->blocks[0] = key_iv; | ||||
| 	state->blocks[1] = crypto_aegis_const[1]; | ||||
| 	state->blocks[2] = crypto_aegis_const[0]; | ||||
| 	state->blocks[3] = *key; | ||||
| 	state->blocks[4] = *key; | ||||
| 
 | ||||
| 	crypto_aegis_block_xor(&state->blocks[3], &crypto_aegis_const[0]); | ||||
| 	crypto_aegis_block_xor(&state->blocks[4], &crypto_aegis_const[1]); | ||||
| 
 | ||||
| 	for (i = 0; i < 5; i++) { | ||||
| 		crypto_aegis128_update_a(state, key); | ||||
| 		crypto_aegis128_update_a(state, &key_iv); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void crypto_aegis128_ad(struct aegis_state *state, | ||||
| 			       const u8 *src, unsigned int size) | ||||
| { | ||||
| 	if (AEGIS_ALIGNED(src)) { | ||||
| 		const union aegis_block *src_blk = | ||||
| 				(const union aegis_block *)src; | ||||
| 
 | ||||
| 		while (size >= AEGIS_BLOCK_SIZE) { | ||||
| 			crypto_aegis128_update_a(state, src_blk); | ||||
| 
 | ||||
| 			size -= AEGIS_BLOCK_SIZE; | ||||
| 			src_blk++; | ||||
| 		} | ||||
| 	} else { | ||||
| 		while (size >= AEGIS_BLOCK_SIZE) { | ||||
| 			crypto_aegis128_update_u(state, src); | ||||
| 
 | ||||
| 			size -= AEGIS_BLOCK_SIZE; | ||||
| 			src += AEGIS_BLOCK_SIZE; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void crypto_aegis128_encrypt_chunk(struct aegis_state *state, u8 *dst, | ||||
| 					  const u8 *src, unsigned int size) | ||||
| { | ||||
| 	union aegis_block tmp; | ||||
| 
 | ||||
| 	if (AEGIS_ALIGNED(src) && AEGIS_ALIGNED(dst)) { | ||||
| 		while (size >= AEGIS_BLOCK_SIZE) { | ||||
| 			union aegis_block *dst_blk = | ||||
| 					(union aegis_block *)dst; | ||||
| 			const union aegis_block *src_blk = | ||||
| 					(const union aegis_block *)src; | ||||
| 
 | ||||
| 			tmp = state->blocks[2]; | ||||
| 			crypto_aegis_block_and(&tmp, &state->blocks[3]); | ||||
| 			crypto_aegis_block_xor(&tmp, &state->blocks[4]); | ||||
| 			crypto_aegis_block_xor(&tmp, &state->blocks[1]); | ||||
| 			crypto_aegis_block_xor(&tmp, src_blk); | ||||
| 
 | ||||
| 			crypto_aegis128_update_a(state, src_blk); | ||||
| 
 | ||||
| 			*dst_blk = tmp; | ||||
| 
 | ||||
| 			size -= AEGIS_BLOCK_SIZE; | ||||
| 			src += AEGIS_BLOCK_SIZE; | ||||
| 			dst += AEGIS_BLOCK_SIZE; | ||||
| 		} | ||||
| 	} else { | ||||
| 		while (size >= AEGIS_BLOCK_SIZE) { | ||||
| 			tmp = state->blocks[2]; | ||||
| 			crypto_aegis_block_and(&tmp, &state->blocks[3]); | ||||
| 			crypto_aegis_block_xor(&tmp, &state->blocks[4]); | ||||
| 			crypto_aegis_block_xor(&tmp, &state->blocks[1]); | ||||
| 			crypto_xor(tmp.bytes, src, AEGIS_BLOCK_SIZE); | ||||
| 
 | ||||
| 			crypto_aegis128_update_u(state, src); | ||||
| 
 | ||||
| 			memcpy(dst, tmp.bytes, AEGIS_BLOCK_SIZE); | ||||
| 
 | ||||
| 			size -= AEGIS_BLOCK_SIZE; | ||||
| 			src += AEGIS_BLOCK_SIZE; | ||||
| 			dst += AEGIS_BLOCK_SIZE; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (size > 0) { | ||||
| 		union aegis_block msg = {}; | ||||
| 		memcpy(msg.bytes, src, size); | ||||
| 
 | ||||
| 		tmp = state->blocks[2]; | ||||
| 		crypto_aegis_block_and(&tmp, &state->blocks[3]); | ||||
| 		crypto_aegis_block_xor(&tmp, &state->blocks[4]); | ||||
| 		crypto_aegis_block_xor(&tmp, &state->blocks[1]); | ||||
| 
 | ||||
| 		crypto_aegis128_update_a(state, &msg); | ||||
| 
 | ||||
| 		crypto_aegis_block_xor(&msg, &tmp); | ||||
| 
 | ||||
| 		memcpy(dst, msg.bytes, size); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void crypto_aegis128_decrypt_chunk(struct aegis_state *state, u8 *dst, | ||||
| 					  const u8 *src, unsigned int size) | ||||
| { | ||||
| 	union aegis_block tmp; | ||||
| 
 | ||||
| 	if (AEGIS_ALIGNED(src) && AEGIS_ALIGNED(dst)) { | ||||
| 		while (size >= AEGIS_BLOCK_SIZE) { | ||||
| 			union aegis_block *dst_blk = | ||||
| 					(union aegis_block *)dst; | ||||
| 			const union aegis_block *src_blk = | ||||
| 					(const union aegis_block *)src; | ||||
| 
 | ||||
| 			tmp = state->blocks[2]; | ||||
| 			crypto_aegis_block_and(&tmp, &state->blocks[3]); | ||||
| 			crypto_aegis_block_xor(&tmp, &state->blocks[4]); | ||||
| 			crypto_aegis_block_xor(&tmp, &state->blocks[1]); | ||||
| 			crypto_aegis_block_xor(&tmp, src_blk); | ||||
| 
 | ||||
| 			crypto_aegis128_update_a(state, &tmp); | ||||
| 
 | ||||
| 			*dst_blk = tmp; | ||||
| 
 | ||||
| 			size -= AEGIS_BLOCK_SIZE; | ||||
| 			src += AEGIS_BLOCK_SIZE; | ||||
| 			dst += AEGIS_BLOCK_SIZE; | ||||
| 		} | ||||
| 	} else { | ||||
| 		while (size >= AEGIS_BLOCK_SIZE) { | ||||
| 			tmp = state->blocks[2]; | ||||
| 			crypto_aegis_block_and(&tmp, &state->blocks[3]); | ||||
| 			crypto_aegis_block_xor(&tmp, &state->blocks[4]); | ||||
| 			crypto_aegis_block_xor(&tmp, &state->blocks[1]); | ||||
| 			crypto_xor(tmp.bytes, src, AEGIS_BLOCK_SIZE); | ||||
| 
 | ||||
| 			crypto_aegis128_update_a(state, &tmp); | ||||
| 
 | ||||
| 			memcpy(dst, tmp.bytes, AEGIS_BLOCK_SIZE); | ||||
| 
 | ||||
| 			size -= AEGIS_BLOCK_SIZE; | ||||
| 			src += AEGIS_BLOCK_SIZE; | ||||
| 			dst += AEGIS_BLOCK_SIZE; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (size > 0) { | ||||
| 		union aegis_block msg = {}; | ||||
| 		memcpy(msg.bytes, src, size); | ||||
| 
 | ||||
| 		tmp = state->blocks[2]; | ||||
| 		crypto_aegis_block_and(&tmp, &state->blocks[3]); | ||||
| 		crypto_aegis_block_xor(&tmp, &state->blocks[4]); | ||||
| 		crypto_aegis_block_xor(&tmp, &state->blocks[1]); | ||||
| 		crypto_aegis_block_xor(&msg, &tmp); | ||||
| 
 | ||||
| 		memset(msg.bytes + size, 0, AEGIS_BLOCK_SIZE - size); | ||||
| 
 | ||||
| 		crypto_aegis128_update_a(state, &msg); | ||||
| 
 | ||||
| 		memcpy(dst, msg.bytes, size); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void crypto_aegis128_process_ad(struct aegis_state *state, | ||||
| 				       struct scatterlist *sg_src, | ||||
| 				       unsigned int assoclen) | ||||
| { | ||||
| 	struct scatter_walk walk; | ||||
| 	union aegis_block buf; | ||||
| 	unsigned int pos = 0; | ||||
| 
 | ||||
| 	scatterwalk_start(&walk, sg_src); | ||||
| 	while (assoclen != 0) { | ||||
| 		unsigned int size = scatterwalk_clamp(&walk, assoclen); | ||||
| 		unsigned int left = size; | ||||
| 		void *mapped = scatterwalk_map(&walk); | ||||
| 		const u8 *src = (const u8 *)mapped; | ||||
| 
 | ||||
| 		if (pos + size >= AEGIS_BLOCK_SIZE) { | ||||
| 			if (pos > 0) { | ||||
| 				unsigned int fill = AEGIS_BLOCK_SIZE - pos; | ||||
| 				memcpy(buf.bytes + pos, src, fill); | ||||
| 				crypto_aegis128_update_a(state, &buf); | ||||
| 				pos = 0; | ||||
| 				left -= fill; | ||||
| 				src += fill; | ||||
| 			} | ||||
| 
 | ||||
| 			crypto_aegis128_ad(state, src, left); | ||||
| 			src += left & ~(AEGIS_BLOCK_SIZE - 1); | ||||
| 			left &= AEGIS_BLOCK_SIZE - 1; | ||||
| 		} | ||||
| 
 | ||||
| 		memcpy(buf.bytes + pos, src, left); | ||||
| 
 | ||||
| 		pos += left; | ||||
| 		assoclen -= size; | ||||
| 		scatterwalk_unmap(mapped); | ||||
| 		scatterwalk_advance(&walk, size); | ||||
| 		scatterwalk_done(&walk, 0, assoclen); | ||||
| 	} | ||||
| 
 | ||||
| 	if (pos > 0) { | ||||
| 		memset(buf.bytes + pos, 0, AEGIS_BLOCK_SIZE - pos); | ||||
| 		crypto_aegis128_update_a(state, &buf); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void crypto_aegis128_process_crypt(struct aegis_state *state, | ||||
| 					  struct aead_request *req, | ||||
| 					  const struct aegis128_ops *ops) | ||||
| { | ||||
| 	struct skcipher_walk walk; | ||||
| 	u8 *src, *dst; | ||||
| 	unsigned int chunksize; | ||||
| 
 | ||||
| 	ops->skcipher_walk_init(&walk, req, false); | ||||
| 
 | ||||
| 	while (walk.nbytes) { | ||||
| 		src = walk.src.virt.addr; | ||||
| 		dst = walk.dst.virt.addr; | ||||
| 		chunksize = walk.nbytes; | ||||
| 
 | ||||
| 		ops->crypt_chunk(state, dst, src, chunksize); | ||||
| 
 | ||||
| 		skcipher_walk_done(&walk, 0); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void crypto_aegis128_final(struct aegis_state *state, | ||||
| 				  union aegis_block *tag_xor, | ||||
| 				  u64 assoclen, u64 cryptlen) | ||||
| { | ||||
| 	u64 assocbits = assoclen * 8; | ||||
| 	u64 cryptbits = cryptlen * 8; | ||||
| 
 | ||||
| 	union aegis_block tmp; | ||||
| 	unsigned int i; | ||||
| 
 | ||||
| 	tmp.words64[0] = cpu_to_le64(assocbits); | ||||
| 	tmp.words64[1] = cpu_to_le64(cryptbits); | ||||
| 
 | ||||
| 	crypto_aegis_block_xor(&tmp, &state->blocks[3]); | ||||
| 
 | ||||
| 	for (i = 0; i < 7; i++) | ||||
| 		crypto_aegis128_update_a(state, &tmp); | ||||
| 
 | ||||
| 	for (i = 0; i < AEGIS128_STATE_BLOCKS; i++) | ||||
| 		crypto_aegis_block_xor(tag_xor, &state->blocks[i]); | ||||
| } | ||||
| 
 | ||||
| static int crypto_aegis128_setkey(struct crypto_aead *aead, const u8 *key, | ||||
| 				  unsigned int keylen) | ||||
| { | ||||
| 	struct aegis_ctx *ctx = crypto_aead_ctx(aead); | ||||
| 
 | ||||
| 	if (keylen != AEGIS128_KEY_SIZE) { | ||||
| 		crypto_aead_set_flags(aead, CRYPTO_TFM_RES_BAD_KEY_LEN); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	memcpy(ctx->key.bytes, key, AEGIS128_KEY_SIZE); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int crypto_aegis128_setauthsize(struct crypto_aead *tfm, | ||||
| 				       unsigned int authsize) | ||||
| { | ||||
| 	if (authsize > AEGIS128_MAX_AUTH_SIZE) | ||||
| 		return -EINVAL; | ||||
| 	if (authsize < AEGIS128_MIN_AUTH_SIZE) | ||||
| 		return -EINVAL; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void crypto_aegis128_crypt(struct aead_request *req, | ||||
| 				  union aegis_block *tag_xor, | ||||
| 				  unsigned int cryptlen, | ||||
| 				  const struct aegis128_ops *ops) | ||||
| { | ||||
| 	struct crypto_aead *tfm = crypto_aead_reqtfm(req); | ||||
| 	struct aegis_ctx *ctx = crypto_aead_ctx(tfm); | ||||
| 	struct aegis_state state; | ||||
| 
 | ||||
| 	crypto_aegis128_init(&state, &ctx->key, req->iv); | ||||
| 	crypto_aegis128_process_ad(&state, req->src, req->assoclen); | ||||
| 	crypto_aegis128_process_crypt(&state, req, ops); | ||||
| 	crypto_aegis128_final(&state, tag_xor, req->assoclen, cryptlen); | ||||
| } | ||||
| 
 | ||||
| static int crypto_aegis128_encrypt(struct aead_request *req) | ||||
| { | ||||
| 	static const struct aegis128_ops ops = { | ||||
| 		.skcipher_walk_init = skcipher_walk_aead_encrypt, | ||||
| 		.crypt_chunk = crypto_aegis128_encrypt_chunk, | ||||
| 	}; | ||||
| 
 | ||||
| 	struct crypto_aead *tfm = crypto_aead_reqtfm(req); | ||||
| 	union aegis_block tag = {}; | ||||
| 	unsigned int authsize = crypto_aead_authsize(tfm); | ||||
| 	unsigned int cryptlen = req->cryptlen; | ||||
| 
 | ||||
| 	crypto_aegis128_crypt(req, &tag, cryptlen, &ops); | ||||
| 
 | ||||
| 	scatterwalk_map_and_copy(tag.bytes, req->dst, req->assoclen + cryptlen, | ||||
| 				 authsize, 1); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int crypto_aegis128_decrypt(struct aead_request *req) | ||||
| { | ||||
| 	static const struct aegis128_ops ops = { | ||||
| 		.skcipher_walk_init = skcipher_walk_aead_decrypt, | ||||
| 		.crypt_chunk = crypto_aegis128_decrypt_chunk, | ||||
| 	}; | ||||
| 	static const u8 zeros[AEGIS128_MAX_AUTH_SIZE] = {}; | ||||
| 
 | ||||
| 	struct crypto_aead *tfm = crypto_aead_reqtfm(req); | ||||
| 	union aegis_block tag; | ||||
| 	unsigned int authsize = crypto_aead_authsize(tfm); | ||||
| 	unsigned int cryptlen = req->cryptlen - authsize; | ||||
| 
 | ||||
| 	scatterwalk_map_and_copy(tag.bytes, req->src, req->assoclen + cryptlen, | ||||
| 				 authsize, 0); | ||||
| 
 | ||||
| 	crypto_aegis128_crypt(req, &tag, cryptlen, &ops); | ||||
| 
 | ||||
| 	return crypto_memneq(tag.bytes, zeros, authsize) ? -EBADMSG : 0; | ||||
| } | ||||
| 
 | ||||
| static int crypto_aegis128_init_tfm(struct crypto_aead *tfm) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void crypto_aegis128_exit_tfm(struct crypto_aead *tfm) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| static struct aead_alg crypto_aegis128_alg = { | ||||
| 	.setkey = crypto_aegis128_setkey, | ||||
| 	.setauthsize = crypto_aegis128_setauthsize, | ||||
| 	.encrypt = crypto_aegis128_encrypt, | ||||
| 	.decrypt = crypto_aegis128_decrypt, | ||||
| 	.init = crypto_aegis128_init_tfm, | ||||
| 	.exit = crypto_aegis128_exit_tfm, | ||||
| 
 | ||||
| 	.ivsize = AEGIS128_NONCE_SIZE, | ||||
| 	.maxauthsize = AEGIS128_MAX_AUTH_SIZE, | ||||
| 	.chunksize = AEGIS_BLOCK_SIZE, | ||||
| 
 | ||||
| 	.base = { | ||||
| 		.cra_flags = CRYPTO_ALG_TYPE_AEAD, | ||||
| 		.cra_blocksize = 1, | ||||
| 		.cra_ctxsize = sizeof(struct aegis_ctx), | ||||
| 		.cra_alignmask = 0, | ||||
| 
 | ||||
| 		.cra_priority = 100, | ||||
| 
 | ||||
| 		.cra_name = "aegis128", | ||||
| 		.cra_driver_name = "aegis128-generic", | ||||
| 
 | ||||
| 		.cra_module = THIS_MODULE, | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| static int __init crypto_aegis128_module_init(void) | ||||
| { | ||||
| 	return crypto_register_aead(&crypto_aegis128_alg); | ||||
| } | ||||
| 
 | ||||
| static void __exit crypto_aegis128_module_exit(void) | ||||
| { | ||||
| 	crypto_unregister_aead(&crypto_aegis128_alg); | ||||
| } | ||||
| 
 | ||||
| module_init(crypto_aegis128_module_init); | ||||
| module_exit(crypto_aegis128_module_exit); | ||||
| 
 | ||||
| MODULE_LICENSE("GPL"); | ||||
| MODULE_AUTHOR("Ondrej Mosnacek <omosnacek@gmail.com>"); | ||||
| MODULE_DESCRIPTION("AEGIS-128 AEAD algorithm"); | ||||
| MODULE_ALIAS_CRYPTO("aegis128"); | ||||
| MODULE_ALIAS_CRYPTO("aegis128-generic"); | ||||
							
								
								
									
										527
									
								
								crypto/aegis128l.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										527
									
								
								crypto/aegis128l.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,527 @@ | |||
| /*
 | ||||
|  * The AEGIS-128L Authenticated-Encryption Algorithm | ||||
|  * | ||||
|  * Copyright (c) 2017-2018 Ondrej Mosnacek <omosnacek@gmail.com> | ||||
|  * Copyright (C) 2017-2018 Red Hat, Inc. All rights reserved. | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify it | ||||
|  * under the terms of the GNU General Public License as published by the Free | ||||
|  * Software Foundation; either version 2 of the License, or (at your option) | ||||
|  * any later version. | ||||
|  */ | ||||
| 
 | ||||
| #include <crypto/algapi.h> | ||||
| #include <crypto/internal/aead.h> | ||||
| #include <crypto/internal/skcipher.h> | ||||
| #include <crypto/scatterwalk.h> | ||||
| #include <linux/err.h> | ||||
| #include <linux/init.h> | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/scatterlist.h> | ||||
| 
 | ||||
| #include "aegis.h" | ||||
| 
 | ||||
| #define AEGIS128L_CHUNK_BLOCKS 2 | ||||
| #define AEGIS128L_CHUNK_SIZE (AEGIS128L_CHUNK_BLOCKS * AEGIS_BLOCK_SIZE) | ||||
| #define AEGIS128L_NONCE_SIZE 16 | ||||
| #define AEGIS128L_STATE_BLOCKS 8 | ||||
| #define AEGIS128L_KEY_SIZE 16 | ||||
| #define AEGIS128L_MIN_AUTH_SIZE 8 | ||||
| #define AEGIS128L_MAX_AUTH_SIZE 16 | ||||
| 
 | ||||
| union aegis_chunk { | ||||
| 	union aegis_block blocks[AEGIS128L_CHUNK_BLOCKS]; | ||||
| 	u8 bytes[AEGIS128L_CHUNK_SIZE]; | ||||
| }; | ||||
| 
 | ||||
| struct aegis_state { | ||||
| 	union aegis_block blocks[AEGIS128L_STATE_BLOCKS]; | ||||
| }; | ||||
| 
 | ||||
| struct aegis_ctx { | ||||
| 	union aegis_block key; | ||||
| }; | ||||
| 
 | ||||
| struct aegis128l_ops { | ||||
| 	int (*skcipher_walk_init)(struct skcipher_walk *walk, | ||||
| 				  struct aead_request *req, bool atomic); | ||||
| 
 | ||||
| 	void (*crypt_chunk)(struct aegis_state *state, u8 *dst, | ||||
| 			    const u8 *src, unsigned int size); | ||||
| }; | ||||
| 
 | ||||
| static void crypto_aegis128l_update(struct aegis_state *state) | ||||
| { | ||||
| 	union aegis_block tmp; | ||||
| 	unsigned int i; | ||||
| 
 | ||||
| 	tmp = state->blocks[AEGIS128L_STATE_BLOCKS - 1]; | ||||
| 	for (i = AEGIS128L_STATE_BLOCKS - 1; i > 0; i--) | ||||
| 		crypto_aegis_aesenc(&state->blocks[i], &state->blocks[i - 1], | ||||
| 				    &state->blocks[i]); | ||||
| 	crypto_aegis_aesenc(&state->blocks[0], &tmp, &state->blocks[0]); | ||||
| } | ||||
| 
 | ||||
| static void crypto_aegis128l_update_a(struct aegis_state *state, | ||||
| 				      const union aegis_chunk *msg) | ||||
| { | ||||
| 	crypto_aegis128l_update(state); | ||||
| 	crypto_aegis_block_xor(&state->blocks[0], &msg->blocks[0]); | ||||
| 	crypto_aegis_block_xor(&state->blocks[4], &msg->blocks[1]); | ||||
| } | ||||
| 
 | ||||
| static void crypto_aegis128l_update_u(struct aegis_state *state, | ||||
| 				      const void *msg) | ||||
| { | ||||
| 	crypto_aegis128l_update(state); | ||||
| 	crypto_xor(state->blocks[0].bytes, msg + 0 * AEGIS_BLOCK_SIZE, | ||||
| 			AEGIS_BLOCK_SIZE); | ||||
| 	crypto_xor(state->blocks[4].bytes, msg + 1 * AEGIS_BLOCK_SIZE, | ||||
| 			AEGIS_BLOCK_SIZE); | ||||
| } | ||||
| 
 | ||||
| static void crypto_aegis128l_init(struct aegis_state *state, | ||||
| 				  const union aegis_block *key, | ||||
| 				  const u8 *iv) | ||||
| { | ||||
| 	union aegis_block key_iv; | ||||
| 	union aegis_chunk chunk; | ||||
| 	unsigned int i; | ||||
| 
 | ||||
| 	memcpy(chunk.blocks[0].bytes, iv, AEGIS_BLOCK_SIZE); | ||||
| 	chunk.blocks[1] = *key; | ||||
| 
 | ||||
| 	key_iv = *key; | ||||
| 	crypto_aegis_block_xor(&key_iv, &chunk.blocks[0]); | ||||
| 
 | ||||
| 	state->blocks[0] = key_iv; | ||||
| 	state->blocks[1] = crypto_aegis_const[1]; | ||||
| 	state->blocks[2] = crypto_aegis_const[0]; | ||||
| 	state->blocks[3] = crypto_aegis_const[1]; | ||||
| 	state->blocks[4] = key_iv; | ||||
| 	state->blocks[5] = *key; | ||||
| 	state->blocks[6] = *key; | ||||
| 	state->blocks[7] = *key; | ||||
| 
 | ||||
| 	crypto_aegis_block_xor(&state->blocks[5], &crypto_aegis_const[0]); | ||||
| 	crypto_aegis_block_xor(&state->blocks[6], &crypto_aegis_const[1]); | ||||
| 	crypto_aegis_block_xor(&state->blocks[7], &crypto_aegis_const[0]); | ||||
| 
 | ||||
| 	for (i = 0; i < 10; i++) { | ||||
| 		crypto_aegis128l_update_a(state, &chunk); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void crypto_aegis128l_ad(struct aegis_state *state, | ||||
| 				const u8 *src, unsigned int size) | ||||
| { | ||||
| 	if (AEGIS_ALIGNED(src)) { | ||||
| 		const union aegis_chunk *src_chunk = | ||||
| 				(const union aegis_chunk *)src; | ||||
| 
 | ||||
| 		while (size >= AEGIS128L_CHUNK_SIZE) { | ||||
|                     crypto_aegis128l_update_a(state, src_chunk); | ||||
| 
 | ||||
| 			size -= AEGIS128L_CHUNK_SIZE; | ||||
| 			src_chunk += 1; | ||||
| 		} | ||||
| 	} else { | ||||
| 		while (size >= AEGIS128L_CHUNK_SIZE) { | ||||
| 			crypto_aegis128l_update_u(state, src); | ||||
| 
 | ||||
| 			size -= AEGIS128L_CHUNK_SIZE; | ||||
| 			src += AEGIS128L_CHUNK_SIZE; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void crypto_aegis128l_encrypt_chunk(struct aegis_state *state, u8 *dst, | ||||
| 					   const u8 *src, unsigned int size) | ||||
| { | ||||
| 	union aegis_chunk tmp; | ||||
| 	union aegis_block *tmp0 = &tmp.blocks[0]; | ||||
| 	union aegis_block *tmp1 = &tmp.blocks[1]; | ||||
| 
 | ||||
| 	if (AEGIS_ALIGNED(src) && AEGIS_ALIGNED(dst)) { | ||||
| 		while (size >= AEGIS128L_CHUNK_SIZE) { | ||||
| 			union aegis_chunk *dst_blk = | ||||
| 					(union aegis_chunk *)dst; | ||||
| 			const union aegis_chunk *src_blk = | ||||
| 					(const union aegis_chunk *)src; | ||||
| 
 | ||||
| 			*tmp0 = state->blocks[2]; | ||||
| 			crypto_aegis_block_and(tmp0, &state->blocks[3]); | ||||
| 			crypto_aegis_block_xor(tmp0, &state->blocks[6]); | ||||
| 			crypto_aegis_block_xor(tmp0, &state->blocks[1]); | ||||
| 			crypto_aegis_block_xor(tmp0, &src_blk->blocks[0]); | ||||
| 
 | ||||
| 			*tmp1 = state->blocks[6]; | ||||
| 			crypto_aegis_block_and(tmp1, &state->blocks[7]); | ||||
| 			crypto_aegis_block_xor(tmp1, &state->blocks[5]); | ||||
| 			crypto_aegis_block_xor(tmp1, &state->blocks[2]); | ||||
| 			crypto_aegis_block_xor(tmp1, &src_blk->blocks[1]); | ||||
| 
 | ||||
| 			crypto_aegis128l_update_a(state, src_blk); | ||||
| 
 | ||||
| 			*dst_blk = tmp; | ||||
| 
 | ||||
| 			size -= AEGIS128L_CHUNK_SIZE; | ||||
| 			src += AEGIS128L_CHUNK_SIZE; | ||||
| 			dst += AEGIS128L_CHUNK_SIZE; | ||||
| 		} | ||||
| 	} else { | ||||
| 		while (size >= AEGIS128L_CHUNK_SIZE) { | ||||
| 			*tmp0 = state->blocks[2]; | ||||
| 			crypto_aegis_block_and(tmp0, &state->blocks[3]); | ||||
| 			crypto_aegis_block_xor(tmp0, &state->blocks[6]); | ||||
| 			crypto_aegis_block_xor(tmp0, &state->blocks[1]); | ||||
| 			crypto_xor(tmp0->bytes, src + 0 * AEGIS_BLOCK_SIZE, | ||||
| 				   AEGIS_BLOCK_SIZE); | ||||
| 
 | ||||
| 			*tmp1 = state->blocks[6]; | ||||
| 			crypto_aegis_block_and(tmp1, &state->blocks[7]); | ||||
| 			crypto_aegis_block_xor(tmp1, &state->blocks[5]); | ||||
| 			crypto_aegis_block_xor(tmp1, &state->blocks[2]); | ||||
| 			crypto_xor(tmp1->bytes, src + 1 * AEGIS_BLOCK_SIZE, | ||||
| 				   AEGIS_BLOCK_SIZE); | ||||
| 
 | ||||
| 			crypto_aegis128l_update_u(state, src); | ||||
| 
 | ||||
| 			memcpy(dst, tmp.bytes, AEGIS128L_CHUNK_SIZE); | ||||
| 
 | ||||
| 			size -= AEGIS128L_CHUNK_SIZE; | ||||
| 			src += AEGIS128L_CHUNK_SIZE; | ||||
| 			dst += AEGIS128L_CHUNK_SIZE; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (size > 0) { | ||||
| 		union aegis_chunk msg = {}; | ||||
| 		memcpy(msg.bytes, src, size); | ||||
| 
 | ||||
| 		*tmp0 = state->blocks[2]; | ||||
| 		crypto_aegis_block_and(tmp0, &state->blocks[3]); | ||||
| 		crypto_aegis_block_xor(tmp0, &state->blocks[6]); | ||||
| 		crypto_aegis_block_xor(tmp0, &state->blocks[1]); | ||||
| 
 | ||||
| 		*tmp1 = state->blocks[6]; | ||||
| 		crypto_aegis_block_and(tmp1, &state->blocks[7]); | ||||
| 		crypto_aegis_block_xor(tmp1, &state->blocks[5]); | ||||
| 		crypto_aegis_block_xor(tmp1, &state->blocks[2]); | ||||
| 
 | ||||
| 		crypto_aegis128l_update_a(state, &msg); | ||||
| 
 | ||||
| 		crypto_aegis_block_xor(&msg.blocks[0], tmp0); | ||||
| 		crypto_aegis_block_xor(&msg.blocks[1], tmp1); | ||||
| 
 | ||||
| 		memcpy(dst, msg.bytes, size); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void crypto_aegis128l_decrypt_chunk(struct aegis_state *state, u8 *dst, | ||||
| 					   const u8 *src, unsigned int size) | ||||
| { | ||||
| 	union aegis_chunk tmp; | ||||
| 	union aegis_block *tmp0 = &tmp.blocks[0]; | ||||
| 	union aegis_block *tmp1 = &tmp.blocks[1]; | ||||
| 
 | ||||
| 	if (AEGIS_ALIGNED(src) && AEGIS_ALIGNED(dst)) { | ||||
| 		while (size >= AEGIS128L_CHUNK_SIZE) { | ||||
| 			union aegis_chunk *dst_blk = | ||||
| 					(union aegis_chunk *)dst; | ||||
| 			const union aegis_chunk *src_blk = | ||||
| 					(const union aegis_chunk *)src; | ||||
| 
 | ||||
| 			*tmp0 = state->blocks[2]; | ||||
| 			crypto_aegis_block_and(tmp0, &state->blocks[3]); | ||||
| 			crypto_aegis_block_xor(tmp0, &state->blocks[6]); | ||||
| 			crypto_aegis_block_xor(tmp0, &state->blocks[1]); | ||||
| 			crypto_aegis_block_xor(tmp0, &src_blk->blocks[0]); | ||||
| 
 | ||||
| 			*tmp1 = state->blocks[6]; | ||||
| 			crypto_aegis_block_and(tmp1, &state->blocks[7]); | ||||
| 			crypto_aegis_block_xor(tmp1, &state->blocks[5]); | ||||
| 			crypto_aegis_block_xor(tmp1, &state->blocks[2]); | ||||
| 			crypto_aegis_block_xor(tmp1, &src_blk->blocks[1]); | ||||
| 
 | ||||
| 			crypto_aegis128l_update_a(state, &tmp); | ||||
| 
 | ||||
| 			*dst_blk = tmp; | ||||
| 
 | ||||
| 			size -= AEGIS128L_CHUNK_SIZE; | ||||
| 			src += AEGIS128L_CHUNK_SIZE; | ||||
| 			dst += AEGIS128L_CHUNK_SIZE; | ||||
| 		} | ||||
| 	} else { | ||||
| 		while (size >= AEGIS128L_CHUNK_SIZE) { | ||||
| 			*tmp0 = state->blocks[2]; | ||||
| 			crypto_aegis_block_and(tmp0, &state->blocks[3]); | ||||
| 			crypto_aegis_block_xor(tmp0, &state->blocks[6]); | ||||
| 			crypto_aegis_block_xor(tmp0, &state->blocks[1]); | ||||
| 			crypto_xor(tmp0->bytes, src + 0 * AEGIS_BLOCK_SIZE, | ||||
| 				   AEGIS_BLOCK_SIZE); | ||||
| 
 | ||||
| 			*tmp1 = state->blocks[6]; | ||||
| 			crypto_aegis_block_and(tmp1, &state->blocks[7]); | ||||
| 			crypto_aegis_block_xor(tmp1, &state->blocks[5]); | ||||
| 			crypto_aegis_block_xor(tmp1, &state->blocks[2]); | ||||
| 			crypto_xor(tmp1->bytes, src + 1 * AEGIS_BLOCK_SIZE, | ||||
| 				   AEGIS_BLOCK_SIZE); | ||||
| 
 | ||||
| 			crypto_aegis128l_update_a(state, &tmp); | ||||
| 
 | ||||
| 			memcpy(dst, tmp.bytes, AEGIS128L_CHUNK_SIZE); | ||||
| 
 | ||||
| 			size -= AEGIS128L_CHUNK_SIZE; | ||||
| 			src += AEGIS128L_CHUNK_SIZE; | ||||
| 			dst += AEGIS128L_CHUNK_SIZE; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (size > 0) { | ||||
| 		union aegis_chunk msg = {}; | ||||
| 		memcpy(msg.bytes, src, size); | ||||
| 
 | ||||
| 		*tmp0 = state->blocks[2]; | ||||
| 		crypto_aegis_block_and(tmp0, &state->blocks[3]); | ||||
| 		crypto_aegis_block_xor(tmp0, &state->blocks[6]); | ||||
| 		crypto_aegis_block_xor(tmp0, &state->blocks[1]); | ||||
| 		crypto_aegis_block_xor(&msg.blocks[0], tmp0); | ||||
| 
 | ||||
| 		*tmp1 = state->blocks[6]; | ||||
| 		crypto_aegis_block_and(tmp1, &state->blocks[7]); | ||||
| 		crypto_aegis_block_xor(tmp1, &state->blocks[5]); | ||||
| 		crypto_aegis_block_xor(tmp1, &state->blocks[2]); | ||||
| 		crypto_aegis_block_xor(&msg.blocks[1], tmp1); | ||||
| 
 | ||||
| 		memset(msg.bytes + size, 0, AEGIS128L_CHUNK_SIZE - size); | ||||
| 
 | ||||
| 		crypto_aegis128l_update_a(state, &msg); | ||||
| 
 | ||||
| 		memcpy(dst, msg.bytes, size); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void crypto_aegis128l_process_ad(struct aegis_state *state, | ||||
| 					struct scatterlist *sg_src, | ||||
| 					unsigned int assoclen) | ||||
| { | ||||
| 	struct scatter_walk walk; | ||||
| 	union aegis_chunk buf; | ||||
| 	unsigned int pos = 0; | ||||
| 
 | ||||
| 	scatterwalk_start(&walk, sg_src); | ||||
| 	while (assoclen != 0) { | ||||
| 		unsigned int size = scatterwalk_clamp(&walk, assoclen); | ||||
| 		unsigned int left = size; | ||||
| 		void *mapped = scatterwalk_map(&walk); | ||||
| 		const u8 *src = (const u8 *)mapped; | ||||
| 
 | ||||
| 		if (pos + size >= AEGIS128L_CHUNK_SIZE) { | ||||
| 			if (pos > 0) { | ||||
| 				unsigned int fill = AEGIS128L_CHUNK_SIZE - pos; | ||||
| 				memcpy(buf.bytes + pos, src, fill); | ||||
| 				crypto_aegis128l_update_a(state, &buf); | ||||
| 				pos = 0; | ||||
| 				left -= fill; | ||||
| 				src += fill; | ||||
| 			} | ||||
| 
 | ||||
| 			crypto_aegis128l_ad(state, src, left); | ||||
| 			src += left & ~(AEGIS128L_CHUNK_SIZE - 1); | ||||
| 			left &= AEGIS128L_CHUNK_SIZE - 1; | ||||
| 		} | ||||
| 
 | ||||
| 		memcpy(buf.bytes + pos, src, left); | ||||
| 
 | ||||
| 		pos += left; | ||||
| 		assoclen -= size; | ||||
| 		scatterwalk_unmap(mapped); | ||||
| 		scatterwalk_advance(&walk, size); | ||||
| 		scatterwalk_done(&walk, 0, assoclen); | ||||
| 	} | ||||
| 
 | ||||
| 	if (pos > 0) { | ||||
| 		memset(buf.bytes + pos, 0, AEGIS128L_CHUNK_SIZE - pos); | ||||
| 		crypto_aegis128l_update_a(state, &buf); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void crypto_aegis128l_process_crypt(struct aegis_state *state, | ||||
| 					   struct aead_request *req, | ||||
| 					   const struct aegis128l_ops *ops) | ||||
| { | ||||
| 	struct skcipher_walk walk; | ||||
| 	u8 *src, *dst; | ||||
| 	unsigned int chunksize; | ||||
| 
 | ||||
| 	ops->skcipher_walk_init(&walk, req, false); | ||||
| 
 | ||||
| 	while (walk.nbytes) { | ||||
| 		src = walk.src.virt.addr; | ||||
| 		dst = walk.dst.virt.addr; | ||||
| 		chunksize = walk.nbytes; | ||||
| 
 | ||||
| 		ops->crypt_chunk(state, dst, src, chunksize); | ||||
| 
 | ||||
| 		skcipher_walk_done(&walk, 0); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void crypto_aegis128l_final(struct aegis_state *state, | ||||
| 				   union aegis_block *tag_xor, | ||||
| 				   u64 assoclen, u64 cryptlen) | ||||
| { | ||||
| 	u64 assocbits = assoclen * 8; | ||||
| 	u64 cryptbits = cryptlen * 8; | ||||
| 
 | ||||
| 	union aegis_chunk tmp; | ||||
| 	unsigned int i; | ||||
| 
 | ||||
| 	tmp.blocks[0].words64[0] = cpu_to_le64(assocbits); | ||||
| 	tmp.blocks[0].words64[1] = cpu_to_le64(cryptbits); | ||||
| 
 | ||||
| 	crypto_aegis_block_xor(&tmp.blocks[0], &state->blocks[2]); | ||||
| 
 | ||||
| 	tmp.blocks[1] = tmp.blocks[0]; | ||||
| 	for (i = 0; i < 7; i++) | ||||
| 		crypto_aegis128l_update_a(state, &tmp); | ||||
| 
 | ||||
| 	for (i = 0; i < 7; i++) | ||||
| 		crypto_aegis_block_xor(tag_xor, &state->blocks[i]); | ||||
| } | ||||
| 
 | ||||
| static int crypto_aegis128l_setkey(struct crypto_aead *aead, const u8 *key, | ||||
| 				   unsigned int keylen) | ||||
| { | ||||
| 	struct aegis_ctx *ctx = crypto_aead_ctx(aead); | ||||
| 
 | ||||
| 	if (keylen != AEGIS128L_KEY_SIZE) { | ||||
| 		crypto_aead_set_flags(aead, CRYPTO_TFM_RES_BAD_KEY_LEN); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	memcpy(ctx->key.bytes, key, AEGIS128L_KEY_SIZE); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int crypto_aegis128l_setauthsize(struct crypto_aead *tfm, | ||||
| 					unsigned int authsize) | ||||
| { | ||||
| 	if (authsize > AEGIS128L_MAX_AUTH_SIZE) | ||||
| 		return -EINVAL; | ||||
| 	if (authsize < AEGIS128L_MIN_AUTH_SIZE) | ||||
| 		return -EINVAL; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void crypto_aegis128l_crypt(struct aead_request *req, | ||||
| 				   union aegis_block *tag_xor, | ||||
| 				   unsigned int cryptlen, | ||||
| 				   const struct aegis128l_ops *ops) | ||||
| { | ||||
| 	struct crypto_aead *tfm = crypto_aead_reqtfm(req); | ||||
| 	struct aegis_ctx *ctx = crypto_aead_ctx(tfm); | ||||
| 	struct aegis_state state; | ||||
| 
 | ||||
| 	crypto_aegis128l_init(&state, &ctx->key, req->iv); | ||||
| 	crypto_aegis128l_process_ad(&state, req->src, req->assoclen); | ||||
| 	crypto_aegis128l_process_crypt(&state, req, ops); | ||||
| 	crypto_aegis128l_final(&state, tag_xor, req->assoclen, cryptlen); | ||||
| } | ||||
| 
 | ||||
| static int crypto_aegis128l_encrypt(struct aead_request *req) | ||||
| { | ||||
| 	static const struct aegis128l_ops ops = { | ||||
| 		.skcipher_walk_init = skcipher_walk_aead_encrypt, | ||||
| 		.crypt_chunk = crypto_aegis128l_encrypt_chunk, | ||||
| 	}; | ||||
| 
 | ||||
| 	struct crypto_aead *tfm = crypto_aead_reqtfm(req); | ||||
| 	union aegis_block tag = {}; | ||||
| 	unsigned int authsize = crypto_aead_authsize(tfm); | ||||
| 	unsigned int cryptlen = req->cryptlen; | ||||
| 
 | ||||
| 	crypto_aegis128l_crypt(req, &tag, cryptlen, &ops); | ||||
| 
 | ||||
| 	scatterwalk_map_and_copy(tag.bytes, req->dst, req->assoclen + cryptlen, | ||||
| 				 authsize, 1); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int crypto_aegis128l_decrypt(struct aead_request *req) | ||||
| { | ||||
| 	static const struct aegis128l_ops ops = { | ||||
| 		.skcipher_walk_init = skcipher_walk_aead_decrypt, | ||||
| 		.crypt_chunk = crypto_aegis128l_decrypt_chunk, | ||||
| 	}; | ||||
| 	static const u8 zeros[AEGIS128L_MAX_AUTH_SIZE] = {}; | ||||
| 
 | ||||
| 	struct crypto_aead *tfm = crypto_aead_reqtfm(req); | ||||
| 	union aegis_block tag; | ||||
| 	unsigned int authsize = crypto_aead_authsize(tfm); | ||||
| 	unsigned int cryptlen = req->cryptlen - authsize; | ||||
| 
 | ||||
| 	scatterwalk_map_and_copy(tag.bytes, req->src, req->assoclen + cryptlen, | ||||
| 				 authsize, 0); | ||||
| 
 | ||||
| 	crypto_aegis128l_crypt(req, &tag, cryptlen, &ops); | ||||
| 
 | ||||
| 	return crypto_memneq(tag.bytes, zeros, authsize) ? -EBADMSG : 0; | ||||
| } | ||||
| 
 | ||||
| static int crypto_aegis128l_init_tfm(struct crypto_aead *tfm) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void crypto_aegis128l_exit_tfm(struct crypto_aead *tfm) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| static struct aead_alg crypto_aegis128l_alg = { | ||||
| 	.setkey = crypto_aegis128l_setkey, | ||||
| 	.setauthsize = crypto_aegis128l_setauthsize, | ||||
| 	.encrypt = crypto_aegis128l_encrypt, | ||||
| 	.decrypt = crypto_aegis128l_decrypt, | ||||
| 	.init = crypto_aegis128l_init_tfm, | ||||
| 	.exit = crypto_aegis128l_exit_tfm, | ||||
| 
 | ||||
| 	.ivsize = AEGIS128L_NONCE_SIZE, | ||||
| 	.maxauthsize = AEGIS128L_MAX_AUTH_SIZE, | ||||
| 	.chunksize = AEGIS128L_CHUNK_SIZE, | ||||
| 
 | ||||
| 	.base = { | ||||
| 		.cra_flags = CRYPTO_ALG_TYPE_AEAD, | ||||
| 		.cra_blocksize = 1, | ||||
| 		.cra_ctxsize = sizeof(struct aegis_ctx), | ||||
| 		.cra_alignmask = 0, | ||||
| 
 | ||||
| 		.cra_priority = 100, | ||||
| 
 | ||||
| 		.cra_name = "aegis128l", | ||||
| 		.cra_driver_name = "aegis128l-generic", | ||||
| 
 | ||||
| 		.cra_module = THIS_MODULE, | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| static int __init crypto_aegis128l_module_init(void) | ||||
| { | ||||
| 	return crypto_register_aead(&crypto_aegis128l_alg); | ||||
| } | ||||
| 
 | ||||
| static void __exit crypto_aegis128l_module_exit(void) | ||||
| { | ||||
| 	crypto_unregister_aead(&crypto_aegis128l_alg); | ||||
| } | ||||
| 
 | ||||
| module_init(crypto_aegis128l_module_init); | ||||
| module_exit(crypto_aegis128l_module_exit); | ||||
| 
 | ||||
| MODULE_LICENSE("GPL"); | ||||
| MODULE_AUTHOR("Ondrej Mosnacek <omosnacek@gmail.com>"); | ||||
| MODULE_DESCRIPTION("AEGIS-128L AEAD algorithm"); | ||||
| MODULE_ALIAS_CRYPTO("aegis128l"); | ||||
| MODULE_ALIAS_CRYPTO("aegis128l-generic"); | ||||
							
								
								
									
										478
									
								
								crypto/aegis256.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										478
									
								
								crypto/aegis256.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,478 @@ | |||
| /*
 | ||||
|  * The AEGIS-256 Authenticated-Encryption Algorithm | ||||
|  * | ||||
|  * Copyright (c) 2017-2018 Ondrej Mosnacek <omosnacek@gmail.com> | ||||
|  * Copyright (C) 2017-2018 Red Hat, Inc. All rights reserved. | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify it | ||||
|  * under the terms of the GNU General Public License as published by the Free | ||||
|  * Software Foundation; either version 2 of the License, or (at your option) | ||||
|  * any later version. | ||||
|  */ | ||||
| 
 | ||||
| #include <crypto/algapi.h> | ||||
| #include <crypto/internal/aead.h> | ||||
| #include <crypto/internal/skcipher.h> | ||||
| #include <crypto/scatterwalk.h> | ||||
| #include <linux/err.h> | ||||
| #include <linux/init.h> | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/scatterlist.h> | ||||
| 
 | ||||
| #include "aegis.h" | ||||
| 
 | ||||
| #define AEGIS256_NONCE_SIZE 32 | ||||
| #define AEGIS256_STATE_BLOCKS 6 | ||||
| #define AEGIS256_KEY_SIZE 32 | ||||
| #define AEGIS256_MIN_AUTH_SIZE 8 | ||||
| #define AEGIS256_MAX_AUTH_SIZE 16 | ||||
| 
 | ||||
| struct aegis_state { | ||||
| 	union aegis_block blocks[AEGIS256_STATE_BLOCKS]; | ||||
| }; | ||||
| 
 | ||||
| struct aegis_ctx { | ||||
| 	union aegis_block key[AEGIS256_KEY_SIZE / AEGIS_BLOCK_SIZE]; | ||||
| }; | ||||
| 
 | ||||
| struct aegis256_ops { | ||||
| 	int (*skcipher_walk_init)(struct skcipher_walk *walk, | ||||
| 				  struct aead_request *req, bool atomic); | ||||
| 
 | ||||
| 	void (*crypt_chunk)(struct aegis_state *state, u8 *dst, | ||||
| 			    const u8 *src, unsigned int size); | ||||
| }; | ||||
| 
 | ||||
| static void crypto_aegis256_update(struct aegis_state *state) | ||||
| { | ||||
| 	union aegis_block tmp; | ||||
| 	unsigned int i; | ||||
| 
 | ||||
| 	tmp = state->blocks[AEGIS256_STATE_BLOCKS - 1]; | ||||
| 	for (i = AEGIS256_STATE_BLOCKS - 1; i > 0; i--) | ||||
| 		crypto_aegis_aesenc(&state->blocks[i], &state->blocks[i - 1], | ||||
| 				    &state->blocks[i]); | ||||
| 	crypto_aegis_aesenc(&state->blocks[0], &tmp, &state->blocks[0]); | ||||
| } | ||||
| 
 | ||||
| static void crypto_aegis256_update_a(struct aegis_state *state, | ||||
| 				     const union aegis_block *msg) | ||||
| { | ||||
| 	crypto_aegis256_update(state); | ||||
| 	crypto_aegis_block_xor(&state->blocks[0], msg); | ||||
| } | ||||
| 
 | ||||
| static void crypto_aegis256_update_u(struct aegis_state *state, const void *msg) | ||||
| { | ||||
| 	crypto_aegis256_update(state); | ||||
| 	crypto_xor(state->blocks[0].bytes, msg, AEGIS_BLOCK_SIZE); | ||||
| } | ||||
| 
 | ||||
| static void crypto_aegis256_init(struct aegis_state *state, | ||||
| 				 const union aegis_block *key, | ||||
| 				 const u8 *iv) | ||||
| { | ||||
| 	union aegis_block key_iv[2]; | ||||
| 	unsigned int i; | ||||
| 
 | ||||
| 	key_iv[0] = key[0]; | ||||
| 	key_iv[1] = key[1]; | ||||
| 	crypto_xor(key_iv[0].bytes, iv + 0 * AEGIS_BLOCK_SIZE, | ||||
| 			AEGIS_BLOCK_SIZE); | ||||
| 	crypto_xor(key_iv[1].bytes, iv + 1 * AEGIS_BLOCK_SIZE, | ||||
| 			AEGIS_BLOCK_SIZE); | ||||
| 
 | ||||
| 	state->blocks[0] = key_iv[0]; | ||||
| 	state->blocks[1] = key_iv[1]; | ||||
| 	state->blocks[2] = crypto_aegis_const[1]; | ||||
| 	state->blocks[3] = crypto_aegis_const[0]; | ||||
| 	state->blocks[4] = key[0]; | ||||
| 	state->blocks[5] = key[1]; | ||||
| 
 | ||||
| 	crypto_aegis_block_xor(&state->blocks[4], &crypto_aegis_const[0]); | ||||
| 	crypto_aegis_block_xor(&state->blocks[5], &crypto_aegis_const[1]); | ||||
| 
 | ||||
| 	for (i = 0; i < 4; i++) { | ||||
| 		crypto_aegis256_update_a(state, &key[0]); | ||||
| 		crypto_aegis256_update_a(state, &key[1]); | ||||
| 		crypto_aegis256_update_a(state, &key_iv[0]); | ||||
| 		crypto_aegis256_update_a(state, &key_iv[1]); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void crypto_aegis256_ad(struct aegis_state *state, | ||||
| 			       const u8 *src, unsigned int size) | ||||
| { | ||||
| 	if (AEGIS_ALIGNED(src)) { | ||||
| 		const union aegis_block *src_blk = | ||||
| 				(const union aegis_block *)src; | ||||
| 
 | ||||
| 		while (size >= AEGIS_BLOCK_SIZE) { | ||||
| 			crypto_aegis256_update_a(state, src_blk); | ||||
| 
 | ||||
| 			size -= AEGIS_BLOCK_SIZE; | ||||
| 			src_blk++; | ||||
| 		} | ||||
| 	} else { | ||||
| 		while (size >= AEGIS_BLOCK_SIZE) { | ||||
| 			crypto_aegis256_update_u(state, src); | ||||
| 
 | ||||
| 			size -= AEGIS_BLOCK_SIZE; | ||||
| 			src += AEGIS_BLOCK_SIZE; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void crypto_aegis256_encrypt_chunk(struct aegis_state *state, u8 *dst, | ||||
| 					  const u8 *src, unsigned int size) | ||||
| { | ||||
| 	union aegis_block tmp; | ||||
| 
 | ||||
| 	if (AEGIS_ALIGNED(src) && AEGIS_ALIGNED(dst)) { | ||||
| 		while (size >= AEGIS_BLOCK_SIZE) { | ||||
| 			union aegis_block *dst_blk = | ||||
| 					(union aegis_block *)dst; | ||||
| 			const union aegis_block *src_blk = | ||||
| 					(const union aegis_block *)src; | ||||
| 
 | ||||
| 			tmp = state->blocks[2]; | ||||
| 			crypto_aegis_block_and(&tmp, &state->blocks[3]); | ||||
| 			crypto_aegis_block_xor(&tmp, &state->blocks[5]); | ||||
| 			crypto_aegis_block_xor(&tmp, &state->blocks[4]); | ||||
| 			crypto_aegis_block_xor(&tmp, &state->blocks[1]); | ||||
| 			crypto_aegis_block_xor(&tmp, src_blk); | ||||
| 
 | ||||
| 			crypto_aegis256_update_a(state, src_blk); | ||||
| 
 | ||||
| 			*dst_blk = tmp; | ||||
| 
 | ||||
| 			size -= AEGIS_BLOCK_SIZE; | ||||
| 			src += AEGIS_BLOCK_SIZE; | ||||
| 			dst += AEGIS_BLOCK_SIZE; | ||||
| 		} | ||||
| 	} else { | ||||
| 		while (size >= AEGIS_BLOCK_SIZE) { | ||||
| 			tmp = state->blocks[2]; | ||||
| 			crypto_aegis_block_and(&tmp, &state->blocks[3]); | ||||
| 			crypto_aegis_block_xor(&tmp, &state->blocks[5]); | ||||
| 			crypto_aegis_block_xor(&tmp, &state->blocks[4]); | ||||
| 			crypto_aegis_block_xor(&tmp, &state->blocks[1]); | ||||
| 			crypto_xor(tmp.bytes, src, AEGIS_BLOCK_SIZE); | ||||
| 
 | ||||
| 			crypto_aegis256_update_u(state, src); | ||||
| 
 | ||||
| 			memcpy(dst, tmp.bytes, AEGIS_BLOCK_SIZE); | ||||
| 
 | ||||
| 			size -= AEGIS_BLOCK_SIZE; | ||||
| 			src += AEGIS_BLOCK_SIZE; | ||||
| 			dst += AEGIS_BLOCK_SIZE; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (size > 0) { | ||||
| 		union aegis_block msg = {}; | ||||
| 		memcpy(msg.bytes, src, size); | ||||
| 
 | ||||
| 		tmp = state->blocks[2]; | ||||
| 		crypto_aegis_block_and(&tmp, &state->blocks[3]); | ||||
| 		crypto_aegis_block_xor(&tmp, &state->blocks[5]); | ||||
| 		crypto_aegis_block_xor(&tmp, &state->blocks[4]); | ||||
| 		crypto_aegis_block_xor(&tmp, &state->blocks[1]); | ||||
| 
 | ||||
| 		crypto_aegis256_update_a(state, &msg); | ||||
| 
 | ||||
| 		crypto_aegis_block_xor(&msg, &tmp); | ||||
| 
 | ||||
| 		memcpy(dst, msg.bytes, size); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void crypto_aegis256_decrypt_chunk(struct aegis_state *state, u8 *dst, | ||||
| 					  const u8 *src, unsigned int size) | ||||
| { | ||||
| 	union aegis_block tmp; | ||||
| 
 | ||||
| 	if (AEGIS_ALIGNED(src) && AEGIS_ALIGNED(dst)) { | ||||
| 		while (size >= AEGIS_BLOCK_SIZE) { | ||||
| 			union aegis_block *dst_blk = | ||||
| 					(union aegis_block *)dst; | ||||
| 			const union aegis_block *src_blk = | ||||
| 					(const union aegis_block *)src; | ||||
| 
 | ||||
| 			tmp = state->blocks[2]; | ||||
| 			crypto_aegis_block_and(&tmp, &state->blocks[3]); | ||||
| 			crypto_aegis_block_xor(&tmp, &state->blocks[5]); | ||||
| 			crypto_aegis_block_xor(&tmp, &state->blocks[4]); | ||||
| 			crypto_aegis_block_xor(&tmp, &state->blocks[1]); | ||||
| 			crypto_aegis_block_xor(&tmp, src_blk); | ||||
| 
 | ||||
| 			crypto_aegis256_update_a(state, &tmp); | ||||
| 
 | ||||
| 			*dst_blk = tmp; | ||||
| 
 | ||||
| 			size -= AEGIS_BLOCK_SIZE; | ||||
| 			src += AEGIS_BLOCK_SIZE; | ||||
| 			dst += AEGIS_BLOCK_SIZE; | ||||
| 		} | ||||
| 	} else { | ||||
| 		while (size >= AEGIS_BLOCK_SIZE) { | ||||
| 			tmp = state->blocks[2]; | ||||
| 			crypto_aegis_block_and(&tmp, &state->blocks[3]); | ||||
| 			crypto_aegis_block_xor(&tmp, &state->blocks[5]); | ||||
| 			crypto_aegis_block_xor(&tmp, &state->blocks[4]); | ||||
| 			crypto_aegis_block_xor(&tmp, &state->blocks[1]); | ||||
| 			crypto_xor(tmp.bytes, src, AEGIS_BLOCK_SIZE); | ||||
| 
 | ||||
| 			crypto_aegis256_update_a(state, &tmp); | ||||
| 
 | ||||
| 			memcpy(dst, tmp.bytes, AEGIS_BLOCK_SIZE); | ||||
| 
 | ||||
| 			size -= AEGIS_BLOCK_SIZE; | ||||
| 			src += AEGIS_BLOCK_SIZE; | ||||
| 			dst += AEGIS_BLOCK_SIZE; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (size > 0) { | ||||
| 		union aegis_block msg = {}; | ||||
| 		memcpy(msg.bytes, src, size); | ||||
| 
 | ||||
| 		tmp = state->blocks[2]; | ||||
| 		crypto_aegis_block_and(&tmp, &state->blocks[3]); | ||||
| 		crypto_aegis_block_xor(&tmp, &state->blocks[5]); | ||||
| 		crypto_aegis_block_xor(&tmp, &state->blocks[4]); | ||||
| 		crypto_aegis_block_xor(&tmp, &state->blocks[1]); | ||||
| 		crypto_aegis_block_xor(&msg, &tmp); | ||||
| 
 | ||||
| 		memset(msg.bytes + size, 0, AEGIS_BLOCK_SIZE - size); | ||||
| 
 | ||||
| 		crypto_aegis256_update_a(state, &msg); | ||||
| 
 | ||||
| 		memcpy(dst, msg.bytes, size); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void crypto_aegis256_process_ad(struct aegis_state *state, | ||||
| 				       struct scatterlist *sg_src, | ||||
| 				       unsigned int assoclen) | ||||
| { | ||||
| 	struct scatter_walk walk; | ||||
| 	union aegis_block buf; | ||||
| 	unsigned int pos = 0; | ||||
| 
 | ||||
| 	scatterwalk_start(&walk, sg_src); | ||||
| 	while (assoclen != 0) { | ||||
| 		unsigned int size = scatterwalk_clamp(&walk, assoclen); | ||||
| 		unsigned int left = size; | ||||
| 		void *mapped = scatterwalk_map(&walk); | ||||
| 		const u8 *src = (const u8 *)mapped; | ||||
| 
 | ||||
| 		if (pos + size >= AEGIS_BLOCK_SIZE) { | ||||
| 			if (pos > 0) { | ||||
| 				unsigned int fill = AEGIS_BLOCK_SIZE - pos; | ||||
| 				memcpy(buf.bytes + pos, src, fill); | ||||
| 				crypto_aegis256_update_a(state, &buf); | ||||
| 				pos = 0; | ||||
| 				left -= fill; | ||||
| 				src += fill; | ||||
| 			} | ||||
| 
 | ||||
| 			crypto_aegis256_ad(state, src, left); | ||||
| 			src += left & ~(AEGIS_BLOCK_SIZE - 1); | ||||
| 			left &= AEGIS_BLOCK_SIZE - 1; | ||||
| 		} | ||||
| 
 | ||||
| 		memcpy(buf.bytes + pos, src, left); | ||||
| 
 | ||||
| 		pos += left; | ||||
| 		assoclen -= size; | ||||
| 		scatterwalk_unmap(mapped); | ||||
| 		scatterwalk_advance(&walk, size); | ||||
| 		scatterwalk_done(&walk, 0, assoclen); | ||||
| 	} | ||||
| 
 | ||||
| 	if (pos > 0) { | ||||
| 		memset(buf.bytes + pos, 0, AEGIS_BLOCK_SIZE - pos); | ||||
| 		crypto_aegis256_update_a(state, &buf); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void crypto_aegis256_process_crypt(struct aegis_state *state, | ||||
| 					  struct aead_request *req, | ||||
| 					  const struct aegis256_ops *ops) | ||||
| { | ||||
| 	struct skcipher_walk walk; | ||||
| 	u8 *src, *dst; | ||||
| 	unsigned int chunksize; | ||||
| 
 | ||||
| 	ops->skcipher_walk_init(&walk, req, false); | ||||
| 
 | ||||
| 	while (walk.nbytes) { | ||||
| 		src = walk.src.virt.addr; | ||||
| 		dst = walk.dst.virt.addr; | ||||
| 		chunksize = walk.nbytes; | ||||
| 
 | ||||
| 		ops->crypt_chunk(state, dst, src, chunksize); | ||||
| 
 | ||||
| 		skcipher_walk_done(&walk, 0); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void crypto_aegis256_final(struct aegis_state *state, | ||||
| 				  union aegis_block *tag_xor, | ||||
| 				  u64 assoclen, u64 cryptlen) | ||||
| { | ||||
| 	u64 assocbits = assoclen * 8; | ||||
| 	u64 cryptbits = cryptlen * 8; | ||||
| 
 | ||||
| 	union aegis_block tmp; | ||||
| 	unsigned int i; | ||||
| 
 | ||||
| 	tmp.words64[0] = cpu_to_le64(assocbits); | ||||
| 	tmp.words64[1] = cpu_to_le64(cryptbits); | ||||
| 
 | ||||
| 	crypto_aegis_block_xor(&tmp, &state->blocks[3]); | ||||
| 
 | ||||
| 	for (i = 0; i < 7; i++) | ||||
| 		crypto_aegis256_update_a(state, &tmp); | ||||
| 
 | ||||
| 	for (i = 0; i < AEGIS256_STATE_BLOCKS; i++) | ||||
| 		crypto_aegis_block_xor(tag_xor, &state->blocks[i]); | ||||
| } | ||||
| 
 | ||||
| static int crypto_aegis256_setkey(struct crypto_aead *aead, const u8 *key, | ||||
| 				  unsigned int keylen) | ||||
| { | ||||
| 	struct aegis_ctx *ctx = crypto_aead_ctx(aead); | ||||
| 
 | ||||
| 	if (keylen != AEGIS256_KEY_SIZE) { | ||||
| 		crypto_aead_set_flags(aead, CRYPTO_TFM_RES_BAD_KEY_LEN); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	memcpy(ctx->key[0].bytes, key, AEGIS_BLOCK_SIZE); | ||||
| 	memcpy(ctx->key[1].bytes, key + AEGIS_BLOCK_SIZE, | ||||
| 			AEGIS_BLOCK_SIZE); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int crypto_aegis256_setauthsize(struct crypto_aead *tfm, | ||||
| 				       unsigned int authsize) | ||||
| { | ||||
| 	if (authsize > AEGIS256_MAX_AUTH_SIZE) | ||||
| 		return -EINVAL; | ||||
| 	if (authsize < AEGIS256_MIN_AUTH_SIZE) | ||||
| 		return -EINVAL; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void crypto_aegis256_crypt(struct aead_request *req, | ||||
| 				  union aegis_block *tag_xor, | ||||
| 				  unsigned int cryptlen, | ||||
| 				  const struct aegis256_ops *ops) | ||||
| { | ||||
| 	struct crypto_aead *tfm = crypto_aead_reqtfm(req); | ||||
| 	struct aegis_ctx *ctx = crypto_aead_ctx(tfm); | ||||
| 	struct aegis_state state; | ||||
| 
 | ||||
| 	crypto_aegis256_init(&state, ctx->key, req->iv); | ||||
| 	crypto_aegis256_process_ad(&state, req->src, req->assoclen); | ||||
| 	crypto_aegis256_process_crypt(&state, req, ops); | ||||
| 	crypto_aegis256_final(&state, tag_xor, req->assoclen, cryptlen); | ||||
| } | ||||
| 
 | ||||
| static int crypto_aegis256_encrypt(struct aead_request *req) | ||||
| { | ||||
| 	static const struct aegis256_ops ops = { | ||||
| 		.skcipher_walk_init = skcipher_walk_aead_encrypt, | ||||
| 		.crypt_chunk = crypto_aegis256_encrypt_chunk, | ||||
| 	}; | ||||
| 
 | ||||
| 	struct crypto_aead *tfm = crypto_aead_reqtfm(req); | ||||
| 	union aegis_block tag = {}; | ||||
| 	unsigned int authsize = crypto_aead_authsize(tfm); | ||||
| 	unsigned int cryptlen = req->cryptlen; | ||||
| 
 | ||||
| 	crypto_aegis256_crypt(req, &tag, cryptlen, &ops); | ||||
| 
 | ||||
| 	scatterwalk_map_and_copy(tag.bytes, req->dst, req->assoclen + cryptlen, | ||||
| 				 authsize, 1); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int crypto_aegis256_decrypt(struct aead_request *req) | ||||
| { | ||||
| 	static const struct aegis256_ops ops = { | ||||
| 		.skcipher_walk_init = skcipher_walk_aead_decrypt, | ||||
| 		.crypt_chunk = crypto_aegis256_decrypt_chunk, | ||||
| 	}; | ||||
| 	static const u8 zeros[AEGIS256_MAX_AUTH_SIZE] = {}; | ||||
| 
 | ||||
| 	struct crypto_aead *tfm = crypto_aead_reqtfm(req); | ||||
| 	union aegis_block tag; | ||||
| 	unsigned int authsize = crypto_aead_authsize(tfm); | ||||
| 	unsigned int cryptlen = req->cryptlen - authsize; | ||||
| 
 | ||||
| 	scatterwalk_map_and_copy(tag.bytes, req->src, req->assoclen + cryptlen, | ||||
| 				 authsize, 0); | ||||
| 
 | ||||
| 	crypto_aegis256_crypt(req, &tag, cryptlen, &ops); | ||||
| 
 | ||||
| 	return crypto_memneq(tag.bytes, zeros, authsize) ? -EBADMSG : 0; | ||||
| } | ||||
| 
 | ||||
| static int crypto_aegis256_init_tfm(struct crypto_aead *tfm) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void crypto_aegis256_exit_tfm(struct crypto_aead *tfm) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| static struct aead_alg crypto_aegis256_alg = { | ||||
| 	.setkey = crypto_aegis256_setkey, | ||||
| 	.setauthsize = crypto_aegis256_setauthsize, | ||||
| 	.encrypt = crypto_aegis256_encrypt, | ||||
| 	.decrypt = crypto_aegis256_decrypt, | ||||
| 	.init = crypto_aegis256_init_tfm, | ||||
| 	.exit = crypto_aegis256_exit_tfm, | ||||
| 
 | ||||
| 	.ivsize = AEGIS256_NONCE_SIZE, | ||||
| 	.maxauthsize = AEGIS256_MAX_AUTH_SIZE, | ||||
| 	.chunksize = AEGIS_BLOCK_SIZE, | ||||
| 
 | ||||
| 	.base = { | ||||
| 		.cra_flags = CRYPTO_ALG_TYPE_AEAD, | ||||
| 		.cra_blocksize = 1, | ||||
| 		.cra_ctxsize = sizeof(struct aegis_ctx), | ||||
| 		.cra_alignmask = 0, | ||||
| 
 | ||||
| 		.cra_priority = 100, | ||||
| 
 | ||||
| 		.cra_name = "aegis256", | ||||
| 		.cra_driver_name = "aegis256-generic", | ||||
| 
 | ||||
| 		.cra_module = THIS_MODULE, | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| static int __init crypto_aegis256_module_init(void) | ||||
| { | ||||
| 	return crypto_register_aead(&crypto_aegis256_alg); | ||||
| } | ||||
| 
 | ||||
| static void __exit crypto_aegis256_module_exit(void) | ||||
| { | ||||
| 	crypto_unregister_aead(&crypto_aegis256_alg); | ||||
| } | ||||
| 
 | ||||
| module_init(crypto_aegis256_module_init); | ||||
| module_exit(crypto_aegis256_module_exit); | ||||
| 
 | ||||
| MODULE_LICENSE("GPL"); | ||||
| MODULE_AUTHOR("Ondrej Mosnacek <omosnacek@gmail.com>"); | ||||
| MODULE_DESCRIPTION("AEGIS-256 AEAD algorithm"); | ||||
| MODULE_ALIAS_CRYPTO("aegis256"); | ||||
| MODULE_ALIAS_CRYPTO("aegis256-generic"); | ||||
		Loading…
	
		Reference in a new issue
	
	 Ondrej Mosnacek
						Ondrej Mosnacek