#include <crypto/gf128hash.h>
#include <linux/export.h>
#include <linux/module.h>
#include <linux/string.h>
#include <linux/unaligned.h>
#ifdef CONFIG_ARCH_SUPPORTS_INT128
static void clmul64(u64 a, u64 b, u64 *out_lo, u64 *out_hi)
{
u64 a0 = a & 0x1111111111111110;
u64 a1 = a & 0x2222222222222220;
u64 a2 = a & 0x4444444444444440;
u64 a3 = a & 0x8888888888888880;
u64 b0 = b & 0x1111111111111111;
u64 b1 = b & 0x2222222222222222;
u64 b2 = b & 0x4444444444444444;
u64 b3 = b & 0x8888888888888888;
u128 c0 = (a0 * (u128)b0) ^ (a1 * (u128)b3) ^
(a2 * (u128)b2) ^ (a3 * (u128)b1);
u128 c1 = (a0 * (u128)b1) ^ (a1 * (u128)b0) ^
(a2 * (u128)b3) ^ (a3 * (u128)b2);
u128 c2 = (a0 * (u128)b2) ^ (a1 * (u128)b1) ^
(a2 * (u128)b0) ^ (a3 * (u128)b3);
u128 c3 = (a0 * (u128)b3) ^ (a1 * (u128)b2) ^
(a2 * (u128)b1) ^ (a3 * (u128)b0);
u64 e0 = -(a & 1) & b;
u64 e1 = -((a >> 1) & 1) & b;
u64 e2 = -((a >> 2) & 1) & b;
u64 e3 = -((a >> 3) & 1) & b;
u64 extra_lo = e0 ^ (e1 << 1) ^ (e2 << 2) ^ (e3 << 3);
u64 extra_hi = (e1 >> 63) ^ (e2 >> 62) ^ (e3 >> 61);
*out_lo = (((u64)c0) & 0x1111111111111111) ^
(((u64)c1) & 0x2222222222222222) ^
(((u64)c2) & 0x4444444444444444) ^
(((u64)c3) & 0x8888888888888888) ^ extra_lo;
*out_hi = (((u64)(c0 >> 64)) & 0x1111111111111111) ^
(((u64)(c1 >> 64)) & 0x2222222222222222) ^
(((u64)(c2 >> 64)) & 0x4444444444444444) ^
(((u64)(c3 >> 64)) & 0x8888888888888888) ^ extra_hi;
}
#else
static u64 clmul32(u32 a, u32 b)
{
u32 a0 = a & 0x11111111;
u32 a1 = a & 0x22222222;
u32 a2 = a & 0x44444444;
u32 a3 = a & 0x88888888;
u32 b0 = b & 0x11111111;
u32 b1 = b & 0x22222222;
u32 b2 = b & 0x44444444;
u32 b3 = b & 0x88888888;
u64 c0 = (a0 * (u64)b0) ^ (a1 * (u64)b3) ^
(a2 * (u64)b2) ^ (a3 * (u64)b1);
u64 c1 = (a0 * (u64)b1) ^ (a1 * (u64)b0) ^
(a2 * (u64)b3) ^ (a3 * (u64)b2);
u64 c2 = (a0 * (u64)b2) ^ (a1 * (u64)b1) ^
(a2 * (u64)b0) ^ (a3 * (u64)b3);
u64 c3 = (a0 * (u64)b3) ^ (a1 * (u64)b2) ^
(a2 * (u64)b1) ^ (a3 * (u64)b0);
return (c0 & 0x1111111111111111) ^
(c1 & 0x2222222222222222) ^
(c2 & 0x4444444444444444) ^
(c3 & 0x8888888888888888);
}
static void clmul64(u64 a, u64 b, u64 *out_lo, u64 *out_hi)
{
u32 a_lo = (u32)a;
u32 a_hi = a >> 32;
u32 b_lo = (u32)b;
u32 b_hi = b >> 32;
u64 lo = clmul32(a_lo, b_lo);
u64 hi = clmul32(a_hi, b_hi);
u64 mi = clmul32(a_lo ^ a_hi, b_lo ^ b_hi) ^ lo ^ hi;
*out_lo = lo ^ (mi << 32);
*out_hi = hi ^ (mi >> 32);
}
#endif
static void __maybe_unused
polyval_mul_generic(struct polyval_elem *a, const struct polyval_elem *b)
{
u64 c0, c1, c2, c3, mi0, mi1;
clmul64(le64_to_cpu(a->lo), le64_to_cpu(b->lo), &c0, &c1);
clmul64(le64_to_cpu(a->hi), le64_to_cpu(b->hi), &c2, &c3);
clmul64(le64_to_cpu(a->lo ^ a->hi), le64_to_cpu(b->lo ^ b->hi),
&mi0, &mi1);
mi0 ^= c0 ^ c2;
mi1 ^= c1 ^ c3;
c1 ^= mi0;
c2 ^= mi1;
c1 ^= (c0 << 63) ^ (c0 << 62) ^ (c0 << 57);
c2 ^= c0 ^ (c0 >> 1) ^ (c0 >> 2) ^ (c0 >> 7);
c2 ^= (c1 << 63) ^ (c1 << 62) ^ (c1 << 57);
c3 ^= c1 ^ (c1 >> 1) ^ (c1 >> 2) ^ (c1 >> 7);
a->lo = cpu_to_le64(c2);
a->hi = cpu_to_le64(c3);
}
static void __maybe_unused ghash_blocks_generic(struct polyval_elem *acc,
const struct polyval_elem *key,
const u8 *data, size_t nblocks)
{
do {
acc->lo ^=
cpu_to_le64(get_unaligned_be64((__be64 *)(data + 8)));
acc->hi ^= cpu_to_le64(get_unaligned_be64((__be64 *)data));
polyval_mul_generic(acc, key);
data += GHASH_BLOCK_SIZE;
} while (--nblocks);
}
static void __maybe_unused
polyval_blocks_generic(struct polyval_elem *acc, const struct polyval_elem *key,
const u8 *data, size_t nblocks)
{
do {
acc->lo ^= get_unaligned((__le64 *)data);
acc->hi ^= get_unaligned((__le64 *)(data + 8));
polyval_mul_generic(acc, key);
data += POLYVAL_BLOCK_SIZE;
} while (--nblocks);
}
static void __maybe_unused ghash_key_to_polyval(const u8 in[GHASH_BLOCK_SIZE],
struct polyval_elem *out)
{
u64 hi = get_unaligned_be64(&in[0]);
u64 lo = get_unaligned_be64(&in[8]);
u64 mask = (s64)hi >> 63;
hi = (hi << 1) ^ (lo >> 63) ^ (mask & ((u64)0xc2 << 56));
lo = (lo << 1) ^ (mask & 1);
out->lo = cpu_to_le64(lo);
out->hi = cpu_to_le64(hi);
}
static void polyval_acc_to_ghash(const struct polyval_elem *in,
u8 out[GHASH_BLOCK_SIZE])
{
put_unaligned_be64(le64_to_cpu(in->hi), &out[0]);
put_unaligned_be64(le64_to_cpu(in->lo), &out[8]);
}
static void __maybe_unused ghash_acc_to_polyval(const u8 in[GHASH_BLOCK_SIZE],
struct polyval_elem *out)
{
out->lo = cpu_to_le64(get_unaligned_be64(&in[8]));
out->hi = cpu_to_le64(get_unaligned_be64(&in[0]));
}
#ifdef CONFIG_CRYPTO_LIB_GF128HASH_ARCH
#include "gf128hash.h"
#endif
void ghash_preparekey(struct ghash_key *key, const u8 raw_key[GHASH_BLOCK_SIZE])
{
#ifdef ghash_preparekey_arch
ghash_preparekey_arch(key, raw_key);
#else
ghash_key_to_polyval(raw_key, &key->h);
#endif
}
EXPORT_SYMBOL_GPL(ghash_preparekey);
static void ghash_mul(struct ghash_ctx *ctx)
{
#ifdef ghash_mul_arch
ghash_mul_arch(&ctx->acc, ctx->key);
#elif defined(ghash_blocks_arch)
static const u8 zeroes[GHASH_BLOCK_SIZE];
ghash_blocks_arch(&ctx->acc, ctx->key, zeroes, 1);
#else
polyval_mul_generic(&ctx->acc, &ctx->key->h);
#endif
}
static void ghash_blocks(struct ghash_ctx *ctx, const u8 *data, size_t nblocks)
{
#ifdef ghash_blocks_arch
ghash_blocks_arch(&ctx->acc, ctx->key, data, nblocks);
#else
ghash_blocks_generic(&ctx->acc, &ctx->key->h, data, nblocks);
#endif
}
void ghash_update(struct ghash_ctx *ctx, const u8 *data, size_t len)
{
if (unlikely(ctx->partial)) {
size_t n = min(len, GHASH_BLOCK_SIZE - ctx->partial);
len -= n;
while (n--)
ctx->acc.bytes[GHASH_BLOCK_SIZE - 1 - ctx->partial++] ^=
*data++;
if (ctx->partial < GHASH_BLOCK_SIZE)
return;
ghash_mul(ctx);
}
if (len >= GHASH_BLOCK_SIZE) {
size_t nblocks = len / GHASH_BLOCK_SIZE;
ghash_blocks(ctx, data, nblocks);
data += len & ~(GHASH_BLOCK_SIZE - 1);
len &= GHASH_BLOCK_SIZE - 1;
}
for (size_t i = 0; i < len; i++)
ctx->acc.bytes[GHASH_BLOCK_SIZE - 1 - i] ^= data[i];
ctx->partial = len;
}
EXPORT_SYMBOL_GPL(ghash_update);
void ghash_final(struct ghash_ctx *ctx, u8 out[GHASH_BLOCK_SIZE])
{
if (unlikely(ctx->partial))
ghash_mul(ctx);
polyval_acc_to_ghash(&ctx->acc, out);
memzero_explicit(ctx, sizeof(*ctx));
}
EXPORT_SYMBOL_GPL(ghash_final);
void polyval_preparekey(struct polyval_key *key,
const u8 raw_key[POLYVAL_BLOCK_SIZE])
{
#ifdef polyval_preparekey_arch
polyval_preparekey_arch(key, raw_key);
#else
memcpy(key->h.bytes, raw_key, POLYVAL_BLOCK_SIZE);
#endif
}
EXPORT_SYMBOL_GPL(polyval_preparekey);
static void polyval_mul(struct polyval_ctx *ctx)
{
#ifdef polyval_mul_arch
polyval_mul_arch(&ctx->acc, ctx->key);
#elif defined(polyval_blocks_arch)
static const u8 zeroes[POLYVAL_BLOCK_SIZE];
polyval_blocks_arch(&ctx->acc, ctx->key, zeroes, 1);
#else
polyval_mul_generic(&ctx->acc, &ctx->key->h);
#endif
}
static void polyval_blocks(struct polyval_ctx *ctx,
const u8 *data, size_t nblocks)
{
#ifdef polyval_blocks_arch
polyval_blocks_arch(&ctx->acc, ctx->key, data, nblocks);
#else
polyval_blocks_generic(&ctx->acc, &ctx->key->h, data, nblocks);
#endif
}
void polyval_update(struct polyval_ctx *ctx, const u8 *data, size_t len)
{
if (unlikely(ctx->partial)) {
size_t n = min(len, POLYVAL_BLOCK_SIZE - ctx->partial);
len -= n;
while (n--)
ctx->acc.bytes[ctx->partial++] ^= *data++;
if (ctx->partial < POLYVAL_BLOCK_SIZE)
return;
polyval_mul(ctx);
}
if (len >= POLYVAL_BLOCK_SIZE) {
size_t nblocks = len / POLYVAL_BLOCK_SIZE;
polyval_blocks(ctx, data, nblocks);
data += len & ~(POLYVAL_BLOCK_SIZE - 1);
len &= POLYVAL_BLOCK_SIZE - 1;
}
for (size_t i = 0; i < len; i++)
ctx->acc.bytes[i] ^= data[i];
ctx->partial = len;
}
EXPORT_SYMBOL_GPL(polyval_update);
void polyval_final(struct polyval_ctx *ctx, u8 out[POLYVAL_BLOCK_SIZE])
{
if (unlikely(ctx->partial))
polyval_mul(ctx);
memcpy(out, &ctx->acc, POLYVAL_BLOCK_SIZE);
memzero_explicit(ctx, sizeof(*ctx));
}
EXPORT_SYMBOL_GPL(polyval_final);
#ifdef gf128hash_mod_init_arch
static int __init gf128hash_mod_init(void)
{
gf128hash_mod_init_arch();
return 0;
}
subsys_initcall(gf128hash_mod_init);
static void __exit gf128hash_mod_exit(void)
{
}
module_exit(gf128hash_mod_exit);
#endif
MODULE_DESCRIPTION("GF(2^128) polynomial hashing: GHASH and POLYVAL");
MODULE_LICENSE("GPL");