linux/arch/x86/crypto/ghash-clmulni-intel_glue.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Accelerated GHASH implementation with Intel PCLMULQDQ-NI
   4 * instructions. This file contains glue code.
   5 *
   6 * Copyright (c) 2009 Intel Corp.
   7 *   Author: Huang Ying <ying.huang@intel.com>
   8 */
   9
  10#include <linux/err.h>
  11#include <linux/module.h>
  12#include <linux/init.h>
  13#include <linux/kernel.h>
  14#include <linux/crypto.h>
  15#include <crypto/algapi.h>
  16#include <crypto/cryptd.h>
  17#include <crypto/gf128mul.h>
  18#include <crypto/internal/hash.h>
  19#include <crypto/internal/simd.h>
  20#include <asm/cpu_device_id.h>
  21#include <asm/simd.h>
  22
  23#define GHASH_BLOCK_SIZE        16
  24#define GHASH_DIGEST_SIZE       16
  25
  26void clmul_ghash_mul(char *dst, const u128 *shash);
  27
  28void clmul_ghash_update(char *dst, const char *src, unsigned int srclen,
  29                        const u128 *shash);
  30
  31struct ghash_async_ctx {
  32        struct cryptd_ahash *cryptd_tfm;
  33};
  34
  35struct ghash_ctx {
  36        u128 shash;
  37};
  38
  39struct ghash_desc_ctx {
  40        u8 buffer[GHASH_BLOCK_SIZE];
  41        u32 bytes;
  42};
  43
  44static int ghash_init(struct shash_desc *desc)
  45{
  46        struct ghash_desc_ctx *dctx = shash_desc_ctx(desc);
  47
  48        memset(dctx, 0, sizeof(*dctx));
  49
  50        return 0;
  51}
  52
  53static int ghash_setkey(struct crypto_shash *tfm,
  54                        const u8 *key, unsigned int keylen)
  55{
  56        struct ghash_ctx *ctx = crypto_shash_ctx(tfm);
  57        be128 *x = (be128 *)key;
  58        u64 a, b;
  59
  60        if (keylen != GHASH_BLOCK_SIZE)
  61                return -EINVAL;
  62
  63        /* perform multiplication by 'x' in GF(2^128) */
  64        a = be64_to_cpu(x->a);
  65        b = be64_to_cpu(x->b);
  66
  67        ctx->shash.a = (b << 1) | (a >> 63);
  68        ctx->shash.b = (a << 1) | (b >> 63);
  69
  70        if (a >> 63)
  71                ctx->shash.b ^= ((u64)0xc2) << 56;
  72
  73        return 0;
  74}
  75
  76static int ghash_update(struct shash_desc *desc,
  77                         const u8 *src, unsigned int srclen)
  78{
  79        struct ghash_desc_ctx *dctx = shash_desc_ctx(desc);
  80        struct ghash_ctx *ctx = crypto_shash_ctx(desc->tfm);
  81        u8 *dst = dctx->buffer;
  82
  83        kernel_fpu_begin();
  84        if (dctx->bytes) {
  85                int n = min(srclen, dctx->bytes);
  86                u8 *pos = dst + (GHASH_BLOCK_SIZE - dctx->bytes);
  87
  88                dctx->bytes -= n;
  89                srclen -= n;
  90
  91                while (n--)
  92                        *pos++ ^= *src++;
  93
  94                if (!dctx->bytes)
  95                        clmul_ghash_mul(dst, &ctx->shash);
  96        }
  97
  98        clmul_ghash_update(dst, src, srclen, &ctx->shash);
  99        kernel_fpu_end();
 100
 101        if (srclen & 0xf) {
 102                src += srclen - (srclen & 0xf);
 103                srclen &= 0xf;
 104                dctx->bytes = GHASH_BLOCK_SIZE - srclen;
 105                while (srclen--)
 106                        *dst++ ^= *src++;
 107        }
 108
 109        return 0;
 110}
 111
 112static void ghash_flush(struct ghash_ctx *ctx, struct ghash_desc_ctx *dctx)
 113{
 114        u8 *dst = dctx->buffer;
 115
 116        if (dctx->bytes) {
 117                u8 *tmp = dst + (GHASH_BLOCK_SIZE - dctx->bytes);
 118
 119                while (dctx->bytes--)
 120                        *tmp++ ^= 0;
 121
 122                kernel_fpu_begin();
 123                clmul_ghash_mul(dst, &ctx->shash);
 124                kernel_fpu_end();
 125        }
 126
 127        dctx->bytes = 0;
 128}
 129
 130static int ghash_final(struct shash_desc *desc, u8 *dst)
 131{
 132        struct ghash_desc_ctx *dctx = shash_desc_ctx(desc);
 133        struct ghash_ctx *ctx = crypto_shash_ctx(desc->tfm);
 134        u8 *buf = dctx->buffer;
 135
 136        ghash_flush(ctx, dctx);
 137        memcpy(dst, buf, GHASH_BLOCK_SIZE);
 138
 139        return 0;
 140}
 141
 142static struct shash_alg ghash_alg = {
 143        .digestsize     = GHASH_DIGEST_SIZE,
 144        .init           = ghash_init,
 145        .update         = ghash_update,
 146        .final          = ghash_final,
 147        .setkey         = ghash_setkey,
 148        .descsize       = sizeof(struct ghash_desc_ctx),
 149        .base           = {
 150                .cra_name               = "__ghash",
 151                .cra_driver_name        = "__ghash-pclmulqdqni",
 152                .cra_priority           = 0,
 153                .cra_flags              = CRYPTO_ALG_INTERNAL,
 154                .cra_blocksize          = GHASH_BLOCK_SIZE,
 155                .cra_ctxsize            = sizeof(struct ghash_ctx),
 156                .cra_module             = THIS_MODULE,
 157        },
 158};
 159
 160static int ghash_async_init(struct ahash_request *req)
 161{
 162        struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
 163        struct ghash_async_ctx *ctx = crypto_ahash_ctx(tfm);
 164        struct ahash_request *cryptd_req = ahash_request_ctx(req);
 165        struct cryptd_ahash *cryptd_tfm = ctx->cryptd_tfm;
 166        struct shash_desc *desc = cryptd_shash_desc(cryptd_req);
 167        struct crypto_shash *child = cryptd_ahash_child(cryptd_tfm);
 168
 169        desc->tfm = child;
 170        return crypto_shash_init(desc);
 171}
 172
 173static int ghash_async_update(struct ahash_request *req)
 174{
 175        struct ahash_request *cryptd_req = ahash_request_ctx(req);
 176        struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
 177        struct ghash_async_ctx *ctx = crypto_ahash_ctx(tfm);
 178        struct cryptd_ahash *cryptd_tfm = ctx->cryptd_tfm;
 179
 180        if (!crypto_simd_usable() ||
 181            (in_atomic() && cryptd_ahash_queued(cryptd_tfm))) {
 182                memcpy(cryptd_req, req, sizeof(*req));
 183                ahash_request_set_tfm(cryptd_req, &cryptd_tfm->base);
 184                return crypto_ahash_update(cryptd_req);
 185        } else {
 186                struct shash_desc *desc = cryptd_shash_desc(cryptd_req);
 187                return shash_ahash_update(req, desc);
 188        }
 189}
 190
 191static int ghash_async_final(struct ahash_request *req)
 192{
 193        struct ahash_request *cryptd_req = ahash_request_ctx(req);
 194        struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
 195        struct ghash_async_ctx *ctx = crypto_ahash_ctx(tfm);
 196        struct cryptd_ahash *cryptd_tfm = ctx->cryptd_tfm;
 197
 198        if (!crypto_simd_usable() ||
 199            (in_atomic() && cryptd_ahash_queued(cryptd_tfm))) {
 200                memcpy(cryptd_req, req, sizeof(*req));
 201                ahash_request_set_tfm(cryptd_req, &cryptd_tfm->base);
 202                return crypto_ahash_final(cryptd_req);
 203        } else {
 204                struct shash_desc *desc = cryptd_shash_desc(cryptd_req);
 205                return crypto_shash_final(desc, req->result);
 206        }
 207}
 208
 209static int ghash_async_import(struct ahash_request *req, const void *in)
 210{
 211        struct ahash_request *cryptd_req = ahash_request_ctx(req);
 212        struct shash_desc *desc = cryptd_shash_desc(cryptd_req);
 213        struct ghash_desc_ctx *dctx = shash_desc_ctx(desc);
 214
 215        ghash_async_init(req);
 216        memcpy(dctx, in, sizeof(*dctx));
 217        return 0;
 218
 219}
 220
 221static int ghash_async_export(struct ahash_request *req, void *out)
 222{
 223        struct ahash_request *cryptd_req = ahash_request_ctx(req);
 224        struct shash_desc *desc = cryptd_shash_desc(cryptd_req);
 225        struct ghash_desc_ctx *dctx = shash_desc_ctx(desc);
 226
 227        memcpy(out, dctx, sizeof(*dctx));
 228        return 0;
 229
 230}
 231
 232static int ghash_async_digest(struct ahash_request *req)
 233{
 234        struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
 235        struct ghash_async_ctx *ctx = crypto_ahash_ctx(tfm);
 236        struct ahash_request *cryptd_req = ahash_request_ctx(req);
 237        struct cryptd_ahash *cryptd_tfm = ctx->cryptd_tfm;
 238
 239        if (!crypto_simd_usable() ||
 240            (in_atomic() && cryptd_ahash_queued(cryptd_tfm))) {
 241                memcpy(cryptd_req, req, sizeof(*req));
 242                ahash_request_set_tfm(cryptd_req, &cryptd_tfm->base);
 243                return crypto_ahash_digest(cryptd_req);
 244        } else {
 245                struct shash_desc *desc = cryptd_shash_desc(cryptd_req);
 246                struct crypto_shash *child = cryptd_ahash_child(cryptd_tfm);
 247
 248                desc->tfm = child;
 249                return shash_ahash_digest(req, desc);
 250        }
 251}
 252
 253static int ghash_async_setkey(struct crypto_ahash *tfm, const u8 *key,
 254                              unsigned int keylen)
 255{
 256        struct ghash_async_ctx *ctx = crypto_ahash_ctx(tfm);
 257        struct crypto_ahash *child = &ctx->cryptd_tfm->base;
 258
 259        crypto_ahash_clear_flags(child, CRYPTO_TFM_REQ_MASK);
 260        crypto_ahash_set_flags(child, crypto_ahash_get_flags(tfm)
 261                               & CRYPTO_TFM_REQ_MASK);
 262        return crypto_ahash_setkey(child, key, keylen);
 263}
 264
 265static int ghash_async_init_tfm(struct crypto_tfm *tfm)
 266{
 267        struct cryptd_ahash *cryptd_tfm;
 268        struct ghash_async_ctx *ctx = crypto_tfm_ctx(tfm);
 269
 270        cryptd_tfm = cryptd_alloc_ahash("__ghash-pclmulqdqni",
 271                                        CRYPTO_ALG_INTERNAL,
 272                                        CRYPTO_ALG_INTERNAL);
 273        if (IS_ERR(cryptd_tfm))
 274                return PTR_ERR(cryptd_tfm);
 275        ctx->cryptd_tfm = cryptd_tfm;
 276        crypto_ahash_set_reqsize(__crypto_ahash_cast(tfm),
 277                                 sizeof(struct ahash_request) +
 278                                 crypto_ahash_reqsize(&cryptd_tfm->base));
 279
 280        return 0;
 281}
 282
 283static void ghash_async_exit_tfm(struct crypto_tfm *tfm)
 284{
 285        struct ghash_async_ctx *ctx = crypto_tfm_ctx(tfm);
 286
 287        cryptd_free_ahash(ctx->cryptd_tfm);
 288}
 289
 290static struct ahash_alg ghash_async_alg = {
 291        .init           = ghash_async_init,
 292        .update         = ghash_async_update,
 293        .final          = ghash_async_final,
 294        .setkey         = ghash_async_setkey,
 295        .digest         = ghash_async_digest,
 296        .export         = ghash_async_export,
 297        .import         = ghash_async_import,
 298        .halg = {
 299                .digestsize     = GHASH_DIGEST_SIZE,
 300                .statesize = sizeof(struct ghash_desc_ctx),
 301                .base = {
 302                        .cra_name               = "ghash",
 303                        .cra_driver_name        = "ghash-clmulni",
 304                        .cra_priority           = 400,
 305                        .cra_ctxsize            = sizeof(struct ghash_async_ctx),
 306                        .cra_flags              = CRYPTO_ALG_ASYNC,
 307                        .cra_blocksize          = GHASH_BLOCK_SIZE,
 308                        .cra_module             = THIS_MODULE,
 309                        .cra_init               = ghash_async_init_tfm,
 310                        .cra_exit               = ghash_async_exit_tfm,
 311                },
 312        },
 313};
 314
 315static const struct x86_cpu_id pcmul_cpu_id[] = {
 316        X86_MATCH_FEATURE(X86_FEATURE_PCLMULQDQ, NULL), /* Pickle-Mickle-Duck */
 317        {}
 318};
 319MODULE_DEVICE_TABLE(x86cpu, pcmul_cpu_id);
 320
 321static int __init ghash_pclmulqdqni_mod_init(void)
 322{
 323        int err;
 324
 325        if (!x86_match_cpu(pcmul_cpu_id))
 326                return -ENODEV;
 327
 328        err = crypto_register_shash(&ghash_alg);
 329        if (err)
 330                goto err_out;
 331        err = crypto_register_ahash(&ghash_async_alg);
 332        if (err)
 333                goto err_shash;
 334
 335        return 0;
 336
 337err_shash:
 338        crypto_unregister_shash(&ghash_alg);
 339err_out:
 340        return err;
 341}
 342
 343static void __exit ghash_pclmulqdqni_mod_exit(void)
 344{
 345        crypto_unregister_ahash(&ghash_async_alg);
 346        crypto_unregister_shash(&ghash_alg);
 347}
 348
 349module_init(ghash_pclmulqdqni_mod_init);
 350module_exit(ghash_pclmulqdqni_mod_exit);
 351
 352MODULE_LICENSE("GPL");
 353MODULE_DESCRIPTION("GHASH hash function, accelerated by PCLMULQDQ-NI");
 354MODULE_ALIAS_CRYPTO("ghash");
 355