Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * POLYVAL: hash function for HCTR2.
4 *
5 * Copyright (c) 2007 Nokia Siemens Networks - Mikko Herranen <mh1@iki.fi>
6 * Copyright (c) 2009 Intel Corp.
7 * Author: Huang Ying <ying.huang@intel.com>
8 * Copyright 2021 Google LLC
9 */
10
11/*
12 * Code based on crypto/ghash-generic.c
13 *
14 * POLYVAL is a keyed hash function similar to GHASH. POLYVAL uses a different
15 * modulus for finite field multiplication which makes hardware accelerated
16 * implementations on little-endian machines faster. POLYVAL is used in the
17 * kernel to implement HCTR2, but was originally specified for AES-GCM-SIV
18 * (RFC 8452).
19 *
20 * For more information see:
21 * Length-preserving encryption with HCTR2:
22 * https://eprint.iacr.org/2021/1441.pdf
23 * AES-GCM-SIV: Nonce Misuse-Resistant Authenticated Encryption:
24 * https://datatracker.ietf.org/doc/html/rfc8452
25 *
26 * Like GHASH, POLYVAL is not a cryptographic hash function and should
27 * not be used outside of crypto modes explicitly designed to use POLYVAL.
28 *
29 * This implementation uses a convenient trick involving the GHASH and POLYVAL
30 * fields. This trick allows multiplication in the POLYVAL field to be
31 * implemented by using multiplication in the GHASH field as a subroutine. An
32 * element of the POLYVAL field can be converted to an element of the GHASH
33 * field by computing x*REVERSE(a), where REVERSE reverses the byte-ordering of
34 * a. Similarly, an element of the GHASH field can be converted back to the
35 * POLYVAL field by computing REVERSE(x^{-1}*a). For more information, see:
36 * https://datatracker.ietf.org/doc/html/rfc8452#appendix-A
37 *
38 * By using this trick, we do not need to implement the POLYVAL field for the
39 * generic implementation.
40 *
41 * Warning: this generic implementation is not intended to be used in practice
42 * and is not constant time. For practical use, a hardware accelerated
43 * implementation of POLYVAL should be used instead.
44 *
45 */
46
47#include <crypto/gf128mul.h>
48#include <crypto/internal/hash.h>
49#include <crypto/polyval.h>
50#include <crypto/utils.h>
51#include <linux/errno.h>
52#include <linux/kernel.h>
53#include <linux/module.h>
54#include <linux/string.h>
55#include <linux/unaligned.h>
56
57struct polyval_tfm_ctx {
58 struct gf128mul_4k *gf128;
59};
60
61struct polyval_desc_ctx {
62 union {
63 u8 buffer[POLYVAL_BLOCK_SIZE];
64 be128 buffer128;
65 };
66};
67
68static void copy_and_reverse(u8 dst[POLYVAL_BLOCK_SIZE],
69 const u8 src[POLYVAL_BLOCK_SIZE])
70{
71 u64 a = get_unaligned((const u64 *)&src[0]);
72 u64 b = get_unaligned((const u64 *)&src[8]);
73
74 put_unaligned(swab64(a), (u64 *)&dst[8]);
75 put_unaligned(swab64(b), (u64 *)&dst[0]);
76}
77
78static int polyval_setkey(struct crypto_shash *tfm,
79 const u8 *key, unsigned int keylen)
80{
81 struct polyval_tfm_ctx *ctx = crypto_shash_ctx(tfm);
82 be128 k;
83
84 if (keylen != POLYVAL_BLOCK_SIZE)
85 return -EINVAL;
86
87 gf128mul_free_4k(ctx->gf128);
88
89 BUILD_BUG_ON(sizeof(k) != POLYVAL_BLOCK_SIZE);
90 copy_and_reverse((u8 *)&k, key);
91 gf128mul_x_lle(&k, &k);
92
93 ctx->gf128 = gf128mul_init_4k_lle(&k);
94 memzero_explicit(&k, POLYVAL_BLOCK_SIZE);
95
96 if (!ctx->gf128)
97 return -ENOMEM;
98
99 return 0;
100}
101
102static int polyval_init(struct shash_desc *desc)
103{
104 struct polyval_desc_ctx *dctx = shash_desc_ctx(desc);
105
106 memset(dctx, 0, sizeof(*dctx));
107
108 return 0;
109}
110
111static int polyval_update(struct shash_desc *desc,
112 const u8 *src, unsigned int srclen)
113{
114 struct polyval_desc_ctx *dctx = shash_desc_ctx(desc);
115 const struct polyval_tfm_ctx *ctx = crypto_shash_ctx(desc->tfm);
116 u8 tmp[POLYVAL_BLOCK_SIZE];
117
118 do {
119 copy_and_reverse(tmp, src);
120 crypto_xor(dctx->buffer, tmp, POLYVAL_BLOCK_SIZE);
121 gf128mul_4k_lle(&dctx->buffer128, ctx->gf128);
122 src += POLYVAL_BLOCK_SIZE;
123 srclen -= POLYVAL_BLOCK_SIZE;
124 } while (srclen >= POLYVAL_BLOCK_SIZE);
125
126 return srclen;
127}
128
129static int polyval_finup(struct shash_desc *desc, const u8 *src,
130 unsigned int len, u8 *dst)
131{
132 struct polyval_desc_ctx *dctx = shash_desc_ctx(desc);
133
134 if (len) {
135 u8 tmp[POLYVAL_BLOCK_SIZE] = {};
136
137 memcpy(tmp, src, len);
138 polyval_update(desc, tmp, POLYVAL_BLOCK_SIZE);
139 }
140 copy_and_reverse(dst, dctx->buffer);
141 return 0;
142}
143
144static int polyval_export(struct shash_desc *desc, void *out)
145{
146 struct polyval_desc_ctx *dctx = shash_desc_ctx(desc);
147
148 copy_and_reverse(out, dctx->buffer);
149 return 0;
150}
151
152static int polyval_import(struct shash_desc *desc, const void *in)
153{
154 struct polyval_desc_ctx *dctx = shash_desc_ctx(desc);
155
156 copy_and_reverse(dctx->buffer, in);
157 return 0;
158}
159
160static void polyval_exit_tfm(struct crypto_shash *tfm)
161{
162 struct polyval_tfm_ctx *ctx = crypto_shash_ctx(tfm);
163
164 gf128mul_free_4k(ctx->gf128);
165}
166
167static struct shash_alg polyval_alg = {
168 .digestsize = POLYVAL_DIGEST_SIZE,
169 .init = polyval_init,
170 .update = polyval_update,
171 .finup = polyval_finup,
172 .setkey = polyval_setkey,
173 .export = polyval_export,
174 .import = polyval_import,
175 .exit_tfm = polyval_exit_tfm,
176 .statesize = sizeof(struct polyval_desc_ctx),
177 .descsize = sizeof(struct polyval_desc_ctx),
178 .base = {
179 .cra_name = "polyval",
180 .cra_driver_name = "polyval-generic",
181 .cra_priority = 100,
182 .cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY,
183 .cra_blocksize = POLYVAL_BLOCK_SIZE,
184 .cra_ctxsize = sizeof(struct polyval_tfm_ctx),
185 .cra_module = THIS_MODULE,
186 },
187};
188
189static int __init polyval_mod_init(void)
190{
191 return crypto_register_shash(&polyval_alg);
192}
193
194static void __exit polyval_mod_exit(void)
195{
196 crypto_unregister_shash(&polyval_alg);
197}
198
199module_init(polyval_mod_init);
200module_exit(polyval_mod_exit);
201
202MODULE_LICENSE("GPL");
203MODULE_DESCRIPTION("POLYVAL hash function");
204MODULE_ALIAS_CRYPTO("polyval");
205MODULE_ALIAS_CRYPTO("polyval-generic");