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
57 struct polyval_tfm_ctx {
58 struct gf128mul_4k *gf128;
59 };
60
61 struct polyval_desc_ctx {
62 union {
63 u8 buffer[POLYVAL_BLOCK_SIZE];
64 be128 buffer128;
65 };
66 };
67
copy_and_reverse(u8 dst[POLYVAL_BLOCK_SIZE],const u8 src[POLYVAL_BLOCK_SIZE])68 static 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
polyval_setkey(struct crypto_shash * tfm,const u8 * key,unsigned int keylen)78 static 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
polyval_init(struct shash_desc * desc)102 static 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
polyval_update(struct shash_desc * desc,const u8 * src,unsigned int srclen)111 static 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
polyval_finup(struct shash_desc * desc,const u8 * src,unsigned int len,u8 * dst)129 static 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
polyval_export(struct shash_desc * desc,void * out)144 static 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
polyval_import(struct shash_desc * desc,const void * in)152 static 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
polyval_exit_tfm(struct crypto_shash * tfm)160 static 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
167 static 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
polyval_mod_init(void)189 static int __init polyval_mod_init(void)
190 {
191 return crypto_register_shash(&polyval_alg);
192 }
193
polyval_mod_exit(void)194 static void __exit polyval_mod_exit(void)
195 {
196 crypto_unregister_shash(&polyval_alg);
197 }
198
199 module_init(polyval_mod_init);
200 module_exit(polyval_mod_exit);
201
202 MODULE_LICENSE("GPL");
203 MODULE_DESCRIPTION("POLYVAL hash function");
204 MODULE_ALIAS_CRYPTO("polyval");
205 MODULE_ALIAS_CRYPTO("polyval-generic");
206