xref: /linux/crypto/polyval-generic.c (revision 25489a4f556414445d342951615178368ee45cde)
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 
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 
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 
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 
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 
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 
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 
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 
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 
189 static int __init polyval_mod_init(void)
190 {
191 	return crypto_register_shash(&polyval_alg);
192 }
193 
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