mirror of
https://github.com/torvalds/linux.git
synced 2024-12-29 14:21:47 +00:00
5a8dedfa32
Omit the endian swabbing when folding the lengths of the assoc and
crypt input buffers into the state to finalize the tag. This is not
necessary given that the memory representation of the state is in
machine native endianness already.
This fixes an error reported by tcrypt running on a big endian system:
alg: aead: Test 2 failed on encryption for morus640-generic
00000000: a8 30 ef fb e6 26 eb 23 b0 87 dd 98 57 f3 e1 4b
00000010: 21
alg: aead: Test 2 failed on encryption for morus1280-generic
00000000: 88 19 1b fb 1c 29 49 0e ee 82 2f cb 97 a6 a5 ee
00000010: 5f
Fixes: 396be41f16
("crypto: morus - Add generic MORUS AEAD implementations")
Cc: <stable@vger.kernel.org> # v4.18+
Reviewed-by: Ondrej Mosnacek <omosnace@redhat.com>
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
537 lines
14 KiB
C
537 lines
14 KiB
C
/*
|
|
* The MORUS-640 Authenticated-Encryption Algorithm
|
|
*
|
|
* Copyright (c) 2016-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 <asm/unaligned.h>
|
|
#include <crypto/algapi.h>
|
|
#include <crypto/internal/aead.h>
|
|
#include <crypto/internal/skcipher.h>
|
|
#include <crypto/morus_common.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>
|
|
|
|
#define MORUS640_WORD_SIZE 4
|
|
#define MORUS640_BLOCK_SIZE (MORUS_BLOCK_WORDS * MORUS640_WORD_SIZE)
|
|
#define MORUS640_BLOCK_ALIGN (__alignof__(__le32))
|
|
#define MORUS640_ALIGNED(p) IS_ALIGNED((uintptr_t)p, MORUS640_BLOCK_ALIGN)
|
|
|
|
struct morus640_block {
|
|
u32 words[MORUS_BLOCK_WORDS];
|
|
};
|
|
|
|
union morus640_block_in {
|
|
__le32 words[MORUS_BLOCK_WORDS];
|
|
u8 bytes[MORUS640_BLOCK_SIZE];
|
|
};
|
|
|
|
struct morus640_state {
|
|
struct morus640_block s[MORUS_STATE_BLOCKS];
|
|
};
|
|
|
|
struct morus640_ctx {
|
|
struct morus640_block key;
|
|
};
|
|
|
|
struct morus640_ops {
|
|
int (*skcipher_walk_init)(struct skcipher_walk *walk,
|
|
struct aead_request *req, bool atomic);
|
|
|
|
void (*crypt_chunk)(struct morus640_state *state,
|
|
u8 *dst, const u8 *src, unsigned int size);
|
|
};
|
|
|
|
static const struct morus640_block crypto_morus640_const[2] = {
|
|
{ .words = {
|
|
U32_C(0x02010100),
|
|
U32_C(0x0d080503),
|
|
U32_C(0x59372215),
|
|
U32_C(0x6279e990),
|
|
} },
|
|
{ .words = {
|
|
U32_C(0x55183ddb),
|
|
U32_C(0xf12fc26d),
|
|
U32_C(0x42311120),
|
|
U32_C(0xdd28b573),
|
|
} },
|
|
};
|
|
|
|
static void crypto_morus640_round(struct morus640_block *b0,
|
|
struct morus640_block *b1,
|
|
struct morus640_block *b2,
|
|
struct morus640_block *b3,
|
|
struct morus640_block *b4,
|
|
const struct morus640_block *m,
|
|
unsigned int b, unsigned int w)
|
|
{
|
|
unsigned int i;
|
|
struct morus640_block tmp;
|
|
|
|
for (i = 0; i < MORUS_BLOCK_WORDS; i++) {
|
|
b0->words[i] ^= b1->words[i] & b2->words[i];
|
|
b0->words[i] ^= b3->words[i];
|
|
b0->words[i] ^= m->words[i];
|
|
b0->words[i] = rol32(b0->words[i], b);
|
|
}
|
|
|
|
tmp = *b3;
|
|
for (i = 0; i < MORUS_BLOCK_WORDS; i++)
|
|
b3->words[(i + w) % MORUS_BLOCK_WORDS] = tmp.words[i];
|
|
}
|
|
|
|
static void crypto_morus640_update(struct morus640_state *state,
|
|
const struct morus640_block *m)
|
|
{
|
|
static const struct morus640_block z = {};
|
|
|
|
struct morus640_block *s = state->s;
|
|
|
|
crypto_morus640_round(&s[0], &s[1], &s[2], &s[3], &s[4], &z, 5, 1);
|
|
crypto_morus640_round(&s[1], &s[2], &s[3], &s[4], &s[0], m, 31, 2);
|
|
crypto_morus640_round(&s[2], &s[3], &s[4], &s[0], &s[1], m, 7, 3);
|
|
crypto_morus640_round(&s[3], &s[4], &s[0], &s[1], &s[2], m, 22, 2);
|
|
crypto_morus640_round(&s[4], &s[0], &s[1], &s[2], &s[3], m, 13, 1);
|
|
}
|
|
|
|
static void crypto_morus640_load_a(struct morus640_block *dst, const u8 *src)
|
|
{
|
|
unsigned int i;
|
|
for (i = 0; i < MORUS_BLOCK_WORDS; i++) {
|
|
dst->words[i] = le32_to_cpu(*(const __le32 *)src);
|
|
src += MORUS640_WORD_SIZE;
|
|
}
|
|
}
|
|
|
|
static void crypto_morus640_load_u(struct morus640_block *dst, const u8 *src)
|
|
{
|
|
unsigned int i;
|
|
for (i = 0; i < MORUS_BLOCK_WORDS; i++) {
|
|
dst->words[i] = get_unaligned_le32(src);
|
|
src += MORUS640_WORD_SIZE;
|
|
}
|
|
}
|
|
|
|
static void crypto_morus640_load(struct morus640_block *dst, const u8 *src)
|
|
{
|
|
if (MORUS640_ALIGNED(src))
|
|
crypto_morus640_load_a(dst, src);
|
|
else
|
|
crypto_morus640_load_u(dst, src);
|
|
}
|
|
|
|
static void crypto_morus640_store_a(u8 *dst, const struct morus640_block *src)
|
|
{
|
|
unsigned int i;
|
|
for (i = 0; i < MORUS_BLOCK_WORDS; i++) {
|
|
*(__le32 *)dst = cpu_to_le32(src->words[i]);
|
|
dst += MORUS640_WORD_SIZE;
|
|
}
|
|
}
|
|
|
|
static void crypto_morus640_store_u(u8 *dst, const struct morus640_block *src)
|
|
{
|
|
unsigned int i;
|
|
for (i = 0; i < MORUS_BLOCK_WORDS; i++) {
|
|
put_unaligned_le32(src->words[i], dst);
|
|
dst += MORUS640_WORD_SIZE;
|
|
}
|
|
}
|
|
|
|
static void crypto_morus640_store(u8 *dst, const struct morus640_block *src)
|
|
{
|
|
if (MORUS640_ALIGNED(dst))
|
|
crypto_morus640_store_a(dst, src);
|
|
else
|
|
crypto_morus640_store_u(dst, src);
|
|
}
|
|
|
|
static void crypto_morus640_ad(struct morus640_state *state, const u8 *src,
|
|
unsigned int size)
|
|
{
|
|
struct morus640_block m;
|
|
|
|
if (MORUS640_ALIGNED(src)) {
|
|
while (size >= MORUS640_BLOCK_SIZE) {
|
|
crypto_morus640_load_a(&m, src);
|
|
crypto_morus640_update(state, &m);
|
|
|
|
size -= MORUS640_BLOCK_SIZE;
|
|
src += MORUS640_BLOCK_SIZE;
|
|
}
|
|
} else {
|
|
while (size >= MORUS640_BLOCK_SIZE) {
|
|
crypto_morus640_load_u(&m, src);
|
|
crypto_morus640_update(state, &m);
|
|
|
|
size -= MORUS640_BLOCK_SIZE;
|
|
src += MORUS640_BLOCK_SIZE;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void crypto_morus640_core(const struct morus640_state *state,
|
|
struct morus640_block *blk)
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < MORUS_BLOCK_WORDS; i++)
|
|
blk->words[(i + 3) % MORUS_BLOCK_WORDS] ^= state->s[1].words[i];
|
|
|
|
for (i = 0; i < MORUS_BLOCK_WORDS; i++) {
|
|
blk->words[i] ^= state->s[0].words[i];
|
|
blk->words[i] ^= state->s[2].words[i] & state->s[3].words[i];
|
|
}
|
|
}
|
|
|
|
static void crypto_morus640_encrypt_chunk(struct morus640_state *state, u8 *dst,
|
|
const u8 *src, unsigned int size)
|
|
{
|
|
struct morus640_block c, m;
|
|
|
|
if (MORUS640_ALIGNED(src) && MORUS640_ALIGNED(dst)) {
|
|
while (size >= MORUS640_BLOCK_SIZE) {
|
|
crypto_morus640_load_a(&m, src);
|
|
c = m;
|
|
crypto_morus640_core(state, &c);
|
|
crypto_morus640_store_a(dst, &c);
|
|
crypto_morus640_update(state, &m);
|
|
|
|
src += MORUS640_BLOCK_SIZE;
|
|
dst += MORUS640_BLOCK_SIZE;
|
|
size -= MORUS640_BLOCK_SIZE;
|
|
}
|
|
} else {
|
|
while (size >= MORUS640_BLOCK_SIZE) {
|
|
crypto_morus640_load_u(&m, src);
|
|
c = m;
|
|
crypto_morus640_core(state, &c);
|
|
crypto_morus640_store_u(dst, &c);
|
|
crypto_morus640_update(state, &m);
|
|
|
|
src += MORUS640_BLOCK_SIZE;
|
|
dst += MORUS640_BLOCK_SIZE;
|
|
size -= MORUS640_BLOCK_SIZE;
|
|
}
|
|
}
|
|
|
|
if (size > 0) {
|
|
union morus640_block_in tail;
|
|
|
|
memcpy(tail.bytes, src, size);
|
|
memset(tail.bytes + size, 0, MORUS640_BLOCK_SIZE - size);
|
|
|
|
crypto_morus640_load_a(&m, tail.bytes);
|
|
c = m;
|
|
crypto_morus640_core(state, &c);
|
|
crypto_morus640_store_a(tail.bytes, &c);
|
|
crypto_morus640_update(state, &m);
|
|
|
|
memcpy(dst, tail.bytes, size);
|
|
}
|
|
}
|
|
|
|
static void crypto_morus640_decrypt_chunk(struct morus640_state *state, u8 *dst,
|
|
const u8 *src, unsigned int size)
|
|
{
|
|
struct morus640_block m;
|
|
|
|
if (MORUS640_ALIGNED(src) && MORUS640_ALIGNED(dst)) {
|
|
while (size >= MORUS640_BLOCK_SIZE) {
|
|
crypto_morus640_load_a(&m, src);
|
|
crypto_morus640_core(state, &m);
|
|
crypto_morus640_store_a(dst, &m);
|
|
crypto_morus640_update(state, &m);
|
|
|
|
src += MORUS640_BLOCK_SIZE;
|
|
dst += MORUS640_BLOCK_SIZE;
|
|
size -= MORUS640_BLOCK_SIZE;
|
|
}
|
|
} else {
|
|
while (size >= MORUS640_BLOCK_SIZE) {
|
|
crypto_morus640_load_u(&m, src);
|
|
crypto_morus640_core(state, &m);
|
|
crypto_morus640_store_u(dst, &m);
|
|
crypto_morus640_update(state, &m);
|
|
|
|
src += MORUS640_BLOCK_SIZE;
|
|
dst += MORUS640_BLOCK_SIZE;
|
|
size -= MORUS640_BLOCK_SIZE;
|
|
}
|
|
}
|
|
|
|
if (size > 0) {
|
|
union morus640_block_in tail;
|
|
|
|
memcpy(tail.bytes, src, size);
|
|
memset(tail.bytes + size, 0, MORUS640_BLOCK_SIZE - size);
|
|
|
|
crypto_morus640_load_a(&m, tail.bytes);
|
|
crypto_morus640_core(state, &m);
|
|
crypto_morus640_store_a(tail.bytes, &m);
|
|
memset(tail.bytes + size, 0, MORUS640_BLOCK_SIZE - size);
|
|
crypto_morus640_load_a(&m, tail.bytes);
|
|
crypto_morus640_update(state, &m);
|
|
|
|
memcpy(dst, tail.bytes, size);
|
|
}
|
|
}
|
|
|
|
static void crypto_morus640_init(struct morus640_state *state,
|
|
const struct morus640_block *key,
|
|
const u8 *iv)
|
|
{
|
|
static const struct morus640_block z = {};
|
|
|
|
unsigned int i;
|
|
|
|
crypto_morus640_load(&state->s[0], iv);
|
|
state->s[1] = *key;
|
|
for (i = 0; i < MORUS_BLOCK_WORDS; i++)
|
|
state->s[2].words[i] = U32_C(0xFFFFFFFF);
|
|
state->s[3] = crypto_morus640_const[0];
|
|
state->s[4] = crypto_morus640_const[1];
|
|
|
|
for (i = 0; i < 16; i++)
|
|
crypto_morus640_update(state, &z);
|
|
|
|
for (i = 0; i < MORUS_BLOCK_WORDS; i++)
|
|
state->s[1].words[i] ^= key->words[i];
|
|
}
|
|
|
|
static void crypto_morus640_process_ad(struct morus640_state *state,
|
|
struct scatterlist *sg_src,
|
|
unsigned int assoclen)
|
|
{
|
|
struct scatter_walk walk;
|
|
struct morus640_block m;
|
|
union morus640_block_in 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 >= MORUS640_BLOCK_SIZE) {
|
|
if (pos > 0) {
|
|
unsigned int fill = MORUS640_BLOCK_SIZE - pos;
|
|
memcpy(buf.bytes + pos, src, fill);
|
|
|
|
crypto_morus640_load_a(&m, buf.bytes);
|
|
crypto_morus640_update(state, &m);
|
|
|
|
pos = 0;
|
|
left -= fill;
|
|
src += fill;
|
|
}
|
|
|
|
crypto_morus640_ad(state, src, left);
|
|
src += left & ~(MORUS640_BLOCK_SIZE - 1);
|
|
left &= MORUS640_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, MORUS640_BLOCK_SIZE - pos);
|
|
|
|
crypto_morus640_load_a(&m, buf.bytes);
|
|
crypto_morus640_update(state, &m);
|
|
}
|
|
}
|
|
|
|
static void crypto_morus640_process_crypt(struct morus640_state *state,
|
|
struct aead_request *req,
|
|
const struct morus640_ops *ops)
|
|
{
|
|
struct skcipher_walk walk;
|
|
u8 *dst;
|
|
const u8 *src;
|
|
|
|
ops->skcipher_walk_init(&walk, req, false);
|
|
|
|
while (walk.nbytes) {
|
|
src = walk.src.virt.addr;
|
|
dst = walk.dst.virt.addr;
|
|
|
|
ops->crypt_chunk(state, dst, src, walk.nbytes);
|
|
|
|
skcipher_walk_done(&walk, 0);
|
|
}
|
|
}
|
|
|
|
static void crypto_morus640_final(struct morus640_state *state,
|
|
struct morus640_block *tag_xor,
|
|
u64 assoclen, u64 cryptlen)
|
|
{
|
|
struct morus640_block tmp;
|
|
unsigned int i;
|
|
|
|
tmp.words[0] = lower_32_bits(assoclen * 8);
|
|
tmp.words[1] = upper_32_bits(assoclen * 8);
|
|
tmp.words[2] = lower_32_bits(cryptlen * 8);
|
|
tmp.words[3] = upper_32_bits(cryptlen * 8);
|
|
|
|
for (i = 0; i < MORUS_BLOCK_WORDS; i++)
|
|
state->s[4].words[i] ^= state->s[0].words[i];
|
|
|
|
for (i = 0; i < 10; i++)
|
|
crypto_morus640_update(state, &tmp);
|
|
|
|
crypto_morus640_core(state, tag_xor);
|
|
}
|
|
|
|
static int crypto_morus640_setkey(struct crypto_aead *aead, const u8 *key,
|
|
unsigned int keylen)
|
|
{
|
|
struct morus640_ctx *ctx = crypto_aead_ctx(aead);
|
|
|
|
if (keylen != MORUS640_BLOCK_SIZE) {
|
|
crypto_aead_set_flags(aead, CRYPTO_TFM_RES_BAD_KEY_LEN);
|
|
return -EINVAL;
|
|
}
|
|
|
|
crypto_morus640_load(&ctx->key, key);
|
|
return 0;
|
|
}
|
|
|
|
static int crypto_morus640_setauthsize(struct crypto_aead *tfm,
|
|
unsigned int authsize)
|
|
{
|
|
return (authsize <= MORUS_MAX_AUTH_SIZE) ? 0 : -EINVAL;
|
|
}
|
|
|
|
static void crypto_morus640_crypt(struct aead_request *req,
|
|
struct morus640_block *tag_xor,
|
|
unsigned int cryptlen,
|
|
const struct morus640_ops *ops)
|
|
{
|
|
struct crypto_aead *tfm = crypto_aead_reqtfm(req);
|
|
struct morus640_ctx *ctx = crypto_aead_ctx(tfm);
|
|
struct morus640_state state;
|
|
|
|
crypto_morus640_init(&state, &ctx->key, req->iv);
|
|
crypto_morus640_process_ad(&state, req->src, req->assoclen);
|
|
crypto_morus640_process_crypt(&state, req, ops);
|
|
crypto_morus640_final(&state, tag_xor, req->assoclen, cryptlen);
|
|
}
|
|
|
|
static int crypto_morus640_encrypt(struct aead_request *req)
|
|
{
|
|
static const struct morus640_ops ops = {
|
|
.skcipher_walk_init = skcipher_walk_aead_encrypt,
|
|
.crypt_chunk = crypto_morus640_encrypt_chunk,
|
|
};
|
|
|
|
struct crypto_aead *tfm = crypto_aead_reqtfm(req);
|
|
struct morus640_block tag = {};
|
|
union morus640_block_in tag_out;
|
|
unsigned int authsize = crypto_aead_authsize(tfm);
|
|
unsigned int cryptlen = req->cryptlen;
|
|
|
|
crypto_morus640_crypt(req, &tag, cryptlen, &ops);
|
|
crypto_morus640_store(tag_out.bytes, &tag);
|
|
|
|
scatterwalk_map_and_copy(tag_out.bytes, req->dst,
|
|
req->assoclen + cryptlen, authsize, 1);
|
|
return 0;
|
|
}
|
|
|
|
static int crypto_morus640_decrypt(struct aead_request *req)
|
|
{
|
|
static const struct morus640_ops ops = {
|
|
.skcipher_walk_init = skcipher_walk_aead_decrypt,
|
|
.crypt_chunk = crypto_morus640_decrypt_chunk,
|
|
};
|
|
static const u8 zeros[MORUS640_BLOCK_SIZE] = {};
|
|
|
|
struct crypto_aead *tfm = crypto_aead_reqtfm(req);
|
|
union morus640_block_in tag_in;
|
|
struct morus640_block tag;
|
|
unsigned int authsize = crypto_aead_authsize(tfm);
|
|
unsigned int cryptlen = req->cryptlen - authsize;
|
|
|
|
scatterwalk_map_and_copy(tag_in.bytes, req->src,
|
|
req->assoclen + cryptlen, authsize, 0);
|
|
|
|
crypto_morus640_load(&tag, tag_in.bytes);
|
|
crypto_morus640_crypt(req, &tag, cryptlen, &ops);
|
|
crypto_morus640_store(tag_in.bytes, &tag);
|
|
|
|
return crypto_memneq(tag_in.bytes, zeros, authsize) ? -EBADMSG : 0;
|
|
}
|
|
|
|
static int crypto_morus640_init_tfm(struct crypto_aead *tfm)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static void crypto_morus640_exit_tfm(struct crypto_aead *tfm)
|
|
{
|
|
}
|
|
|
|
static struct aead_alg crypto_morus640_alg = {
|
|
.setkey = crypto_morus640_setkey,
|
|
.setauthsize = crypto_morus640_setauthsize,
|
|
.encrypt = crypto_morus640_encrypt,
|
|
.decrypt = crypto_morus640_decrypt,
|
|
.init = crypto_morus640_init_tfm,
|
|
.exit = crypto_morus640_exit_tfm,
|
|
|
|
.ivsize = MORUS_NONCE_SIZE,
|
|
.maxauthsize = MORUS_MAX_AUTH_SIZE,
|
|
.chunksize = MORUS640_BLOCK_SIZE,
|
|
|
|
.base = {
|
|
.cra_blocksize = 1,
|
|
.cra_ctxsize = sizeof(struct morus640_ctx),
|
|
.cra_alignmask = 0,
|
|
|
|
.cra_priority = 100,
|
|
|
|
.cra_name = "morus640",
|
|
.cra_driver_name = "morus640-generic",
|
|
|
|
.cra_module = THIS_MODULE,
|
|
}
|
|
};
|
|
|
|
static int __init crypto_morus640_module_init(void)
|
|
{
|
|
return crypto_register_aead(&crypto_morus640_alg);
|
|
}
|
|
|
|
static void __exit crypto_morus640_module_exit(void)
|
|
{
|
|
crypto_unregister_aead(&crypto_morus640_alg);
|
|
}
|
|
|
|
module_init(crypto_morus640_module_init);
|
|
module_exit(crypto_morus640_module_exit);
|
|
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_AUTHOR("Ondrej Mosnacek <omosnacek@gmail.com>");
|
|
MODULE_DESCRIPTION("MORUS-640 AEAD algorithm");
|
|
MODULE_ALIAS_CRYPTO("morus640");
|
|
MODULE_ALIAS_CRYPTO("morus640-generic");
|