root/arch/x86/crypto/ghash-clmulni-intel_glue.c
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Accelerated GHASH implementation with Intel PCLMULQDQ-NI
 * instructions. This file contains glue code.
 *
 * Copyright (c) 2009 Intel Corp.
 *   Author: Huang Ying <ying.huang@intel.com>
 */

#include <asm/cpu_device_id.h>
#include <asm/simd.h>
#include <crypto/b128ops.h>
#include <crypto/ghash.h>
#include <crypto/internal/hash.h>
#include <crypto/utils.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/string.h>
#include <linux/unaligned.h>

asmlinkage void clmul_ghash_mul(char *dst, const le128 *shash);

asmlinkage int clmul_ghash_update(char *dst, const char *src,
                                  unsigned int srclen, const le128 *shash);

struct x86_ghash_ctx {
        le128 shash;
};

static int ghash_init(struct shash_desc *desc)
{
        struct ghash_desc_ctx *dctx = shash_desc_ctx(desc);

        memset(dctx, 0, sizeof(*dctx));

        return 0;
}

static int ghash_setkey(struct crypto_shash *tfm,
                        const u8 *key, unsigned int keylen)
{
        struct x86_ghash_ctx *ctx = crypto_shash_ctx(tfm);
        u64 a, b;

        if (keylen != GHASH_BLOCK_SIZE)
                return -EINVAL;

        /*
         * GHASH maps bits to polynomial coefficients backwards, which makes it
         * hard to implement.  But it can be shown that the GHASH multiplication
         *
         *      D * K (mod x^128 + x^7 + x^2 + x + 1)
         *
         * (where D is a data block and K is the key) is equivalent to:
         *
         *      bitreflect(D) * bitreflect(K) * x^(-127)
         *              (mod x^128 + x^127 + x^126 + x^121 + 1)
         *
         * So, the code below precomputes:
         *
         *      bitreflect(K) * x^(-127) (mod x^128 + x^127 + x^126 + x^121 + 1)
         *
         * ... but in Montgomery form (so that Montgomery multiplication can be
         * used), i.e. with an extra x^128 factor, which means actually:
         *
         *      bitreflect(K) * x (mod x^128 + x^127 + x^126 + x^121 + 1)
         *
         * The within-a-byte part of bitreflect() cancels out GHASH's built-in
         * reflection, and thus bitreflect() is actually a byteswap.
         */
        a = get_unaligned_be64(key);
        b = get_unaligned_be64(key + 8);
        ctx->shash.a = cpu_to_le64((a << 1) | (b >> 63));
        ctx->shash.b = cpu_to_le64((b << 1) | (a >> 63));
        if (a >> 63)
                ctx->shash.a ^= cpu_to_le64((u64)0xc2 << 56);
        return 0;
}

static int ghash_update(struct shash_desc *desc,
                         const u8 *src, unsigned int srclen)
{
        struct x86_ghash_ctx *ctx = crypto_shash_ctx(desc->tfm);
        struct ghash_desc_ctx *dctx = shash_desc_ctx(desc);
        u8 *dst = dctx->buffer;
        int remain;

        kernel_fpu_begin();
        remain = clmul_ghash_update(dst, src, srclen, &ctx->shash);
        kernel_fpu_end();
        return remain;
}

static void ghash_flush(struct x86_ghash_ctx *ctx, struct ghash_desc_ctx *dctx,
                        const u8 *src, unsigned int len)
{
        u8 *dst = dctx->buffer;

        kernel_fpu_begin();
        if (len) {
                crypto_xor(dst, src, len);
                clmul_ghash_mul(dst, &ctx->shash);
        }
        kernel_fpu_end();
}

static int ghash_finup(struct shash_desc *desc, const u8 *src,
                       unsigned int len, u8 *dst)
{
        struct x86_ghash_ctx *ctx = crypto_shash_ctx(desc->tfm);
        struct ghash_desc_ctx *dctx = shash_desc_ctx(desc);
        u8 *buf = dctx->buffer;

        ghash_flush(ctx, dctx, src, len);
        memcpy(dst, buf, GHASH_BLOCK_SIZE);

        return 0;
}

static struct shash_alg ghash_alg = {
        .digestsize     = GHASH_DIGEST_SIZE,
        .init           = ghash_init,
        .update         = ghash_update,
        .finup          = ghash_finup,
        .setkey         = ghash_setkey,
        .descsize       = sizeof(struct ghash_desc_ctx),
        .base           = {
                .cra_name               = "ghash",
                .cra_driver_name        = "ghash-pclmulqdqni",
                .cra_priority           = 400,
                .cra_flags              = CRYPTO_AHASH_ALG_BLOCK_ONLY,
                .cra_blocksize          = GHASH_BLOCK_SIZE,
                .cra_ctxsize            = sizeof(struct x86_ghash_ctx),
                .cra_module             = THIS_MODULE,
        },
};

static const struct x86_cpu_id pcmul_cpu_id[] = {
        X86_MATCH_FEATURE(X86_FEATURE_PCLMULQDQ, NULL), /* Pickle-Mickle-Duck */
        {}
};
MODULE_DEVICE_TABLE(x86cpu, pcmul_cpu_id);

static int __init ghash_pclmulqdqni_mod_init(void)
{
        if (!x86_match_cpu(pcmul_cpu_id))
                return -ENODEV;

        return crypto_register_shash(&ghash_alg);
}

static void __exit ghash_pclmulqdqni_mod_exit(void)
{
        crypto_unregister_shash(&ghash_alg);
}

module_init(ghash_pclmulqdqni_mod_init);
module_exit(ghash_pclmulqdqni_mod_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("GHASH hash function, accelerated by PCLMULQDQ-NI");
MODULE_ALIAS_CRYPTO("ghash");