xref: /freebsd/sys/crypto/openssl/arm/ossl_aes_gcm.c (revision e655cc70dfcda5cfedb5a1d9bef1e87d55519f64)
1629a7237SMark Johnston /*
2629a7237SMark Johnston  * Copyright 2010-2022 The OpenSSL Project Authors. All Rights Reserved.
3629a7237SMark Johnston  *
4629a7237SMark Johnston  * Licensed under the Apache License 2.0 (the "License").  You may not use
5629a7237SMark Johnston  * this file except in compliance with the License.  You can obtain a copy
6629a7237SMark Johnston  * in the file LICENSE in the source distribution or at
7629a7237SMark Johnston  * https://www.openssl.org/source/license.html
8629a7237SMark Johnston  */
9629a7237SMark Johnston 
10629a7237SMark Johnston #include <sys/types.h>
11629a7237SMark Johnston #include <sys/endian.h>
12629a7237SMark Johnston #include <sys/systm.h>
13629a7237SMark Johnston 
14629a7237SMark Johnston #include <crypto/openssl/ossl.h>
15629a7237SMark Johnston #include <crypto/openssl/ossl_arm.h>
16629a7237SMark Johnston #include <crypto/openssl/ossl_aes_gcm.h>
17629a7237SMark Johnston #include <crypto/openssl/ossl_cipher.h>
18*e655cc70SMark Johnston #include <crypto/openssl/arm_arch.h>
19629a7237SMark Johnston 
20629a7237SMark Johnston #include <opencrypto/cryptodev.h>
21629a7237SMark Johnston 
22629a7237SMark Johnston _Static_assert(
23629a7237SMark Johnston     sizeof(struct ossl_gcm_context) <= sizeof(struct ossl_cipher_context),
24629a7237SMark Johnston     "ossl_gcm_context too large");
25629a7237SMark Johnston 
26629a7237SMark Johnston void AES_encrypt(const void *in, void *out, const void *ks);
27629a7237SMark Johnston void AES_set_encrypt_key(const void *key, int keylen, void *ks);
28629a7237SMark Johnston 
29629a7237SMark Johnston void gcm_init_neon(__uint128_t Htable[16], const uint64_t Xi[2]);
30629a7237SMark Johnston void gcm_gmult_neon(uint64_t Xi[2], const __uint128_t Htable[16]);
31629a7237SMark Johnston void gcm_ghash_neon(uint64_t Xi[2], const __uint128_t Htable[16],
32629a7237SMark Johnston     const void *in, size_t len);
33629a7237SMark Johnston 
34629a7237SMark Johnston void ossl_bsaes_ctr32_encrypt_blocks(const unsigned char *in,
35629a7237SMark Johnston     unsigned char *out, size_t blocks, void *ks, const unsigned char *iv);
36629a7237SMark Johnston 
37629a7237SMark Johnston static void
gcm_init(struct ossl_gcm_context * ctx,const void * key,size_t keylen)38629a7237SMark Johnston gcm_init(struct ossl_gcm_context *ctx, const void *key, size_t keylen)
39629a7237SMark Johnston {
40629a7237SMark Johnston 	memset(&ctx->gcm, 0, sizeof(ctx->gcm));
41629a7237SMark Johnston 	memset(&ctx->aes_ks, 0, sizeof(ctx->aes_ks));
42629a7237SMark Johnston 
43629a7237SMark Johnston 	AES_set_encrypt_key(key, keylen, &ctx->aes_ks);
44629a7237SMark Johnston 	AES_encrypt(ctx->gcm.H.c, ctx->gcm.H.c, &ctx->aes_ks);
45629a7237SMark Johnston 
46629a7237SMark Johnston #if BYTE_ORDER == LITTLE_ENDIAN
47629a7237SMark Johnston 	ctx->gcm.H.u[0] = bswap64(ctx->gcm.H.u[0]);
48629a7237SMark Johnston 	ctx->gcm.H.u[1] = bswap64(ctx->gcm.H.u[1]);
49629a7237SMark Johnston #endif
50629a7237SMark Johnston 
51629a7237SMark Johnston 	gcm_init_neon(ctx->gcm.Htable, ctx->gcm.H.u);
52629a7237SMark Johnston }
53629a7237SMark Johnston 
54629a7237SMark Johnston static void
gcm_setiv(struct ossl_gcm_context * ctx,const unsigned char * iv,size_t len)55629a7237SMark Johnston gcm_setiv(struct ossl_gcm_context *ctx, const unsigned char *iv, size_t len)
56629a7237SMark Johnston {
57629a7237SMark Johnston 	uint32_t ctr;
58629a7237SMark Johnston 
59629a7237SMark Johnston 	KASSERT(len == AES_GCM_IV_LEN,
60629a7237SMark Johnston 	    ("%s: invalid IV length %zu", __func__, len));
61629a7237SMark Johnston 
62629a7237SMark Johnston 	ctx->gcm.len.u[0] = 0;
63629a7237SMark Johnston 	ctx->gcm.len.u[1] = 0;
64629a7237SMark Johnston 	ctx->gcm.ares = ctx->gcm.mres = 0;
65629a7237SMark Johnston 
66629a7237SMark Johnston 	memcpy(ctx->gcm.Yi.c, iv, len);
67629a7237SMark Johnston 	ctx->gcm.Yi.c[12] = 0;
68629a7237SMark Johnston 	ctx->gcm.Yi.c[13] = 0;
69629a7237SMark Johnston 	ctx->gcm.Yi.c[14] = 0;
70629a7237SMark Johnston 	ctx->gcm.Yi.c[15] = 1;
71629a7237SMark Johnston 	ctr = 1;
72629a7237SMark Johnston 
73629a7237SMark Johnston 	ctx->gcm.Xi.u[0] = 0;
74629a7237SMark Johnston 	ctx->gcm.Xi.u[1] = 0;
75629a7237SMark Johnston 
76629a7237SMark Johnston 	AES_encrypt(ctx->gcm.Yi.c, ctx->gcm.EK0.c, &ctx->aes_ks);
77629a7237SMark Johnston 	ctr++;
78629a7237SMark Johnston 
79629a7237SMark Johnston #if BYTE_ORDER == LITTLE_ENDIAN
80629a7237SMark Johnston 	ctx->gcm.Yi.d[3] = bswap32(ctr);
81629a7237SMark Johnston #else
82629a7237SMark Johnston 	ctx->gcm.Yi.d[3] = ctr;
83629a7237SMark Johnston #endif
84629a7237SMark Johnston }
85629a7237SMark Johnston 
86629a7237SMark Johnston static int
gcm_finish(struct ossl_gcm_context * ctx,const unsigned char * tag,size_t len)87629a7237SMark Johnston gcm_finish(struct ossl_gcm_context *ctx, const unsigned char *tag, size_t len)
88629a7237SMark Johnston {
89629a7237SMark Johnston 	uint64_t alen = ctx->gcm.len.u[0] << 3;
90629a7237SMark Johnston 	uint64_t clen = ctx->gcm.len.u[1] << 3;
91629a7237SMark Johnston 
92629a7237SMark Johnston 	if (ctx->gcm.mres || ctx->gcm.ares)
93629a7237SMark Johnston 		gcm_gmult_neon(ctx->gcm.Xi.u, ctx->gcm.Htable);
94629a7237SMark Johnston 
95629a7237SMark Johnston #if BYTE_ORDER == LITTLE_ENDIAN
96629a7237SMark Johnston 	alen = bswap64(alen);
97629a7237SMark Johnston 	clen = bswap64(clen);
98629a7237SMark Johnston #endif
99629a7237SMark Johnston 
100629a7237SMark Johnston 	ctx->gcm.Xi.u[0] ^= alen;
101629a7237SMark Johnston 	ctx->gcm.Xi.u[1] ^= clen;
102629a7237SMark Johnston 	gcm_gmult_neon(ctx->gcm.Xi.u, ctx->gcm.Htable);
103629a7237SMark Johnston 
104629a7237SMark Johnston 	ctx->gcm.Xi.u[0] ^= ctx->gcm.EK0.u[0];
105629a7237SMark Johnston 	ctx->gcm.Xi.u[1] ^= ctx->gcm.EK0.u[1];
106629a7237SMark Johnston 
107629a7237SMark Johnston 	if (tag != NULL)
108629a7237SMark Johnston 		return timingsafe_bcmp(ctx->gcm.Xi.c, tag, len);
109629a7237SMark Johnston 	return 0;
110629a7237SMark Johnston }
111629a7237SMark Johnston 
112629a7237SMark Johnston static int
gcm_aad(struct ossl_gcm_context * ctx,const unsigned char * aad,size_t len)113629a7237SMark Johnston gcm_aad(struct ossl_gcm_context *ctx, const unsigned char *aad, size_t len)
114629a7237SMark Johnston {
115629a7237SMark Johnston 	size_t i;
116629a7237SMark Johnston 	unsigned int n;
117629a7237SMark Johnston 	uint64_t alen = ctx->gcm.len.u[0];
118629a7237SMark Johnston 
119629a7237SMark Johnston 	if (ctx->gcm.len.u[1])
120629a7237SMark Johnston 		return -2;
121629a7237SMark Johnston 
122629a7237SMark Johnston 	alen += len;
123629a7237SMark Johnston 	if (alen > ((uint64_t)1 << 61) || (sizeof(len) == 8 && alen < len))
124629a7237SMark Johnston 		return -1;
125629a7237SMark Johnston 	ctx->gcm.len.u[0] = alen;
126629a7237SMark Johnston 
127629a7237SMark Johnston 	n = ctx->gcm.ares;
128629a7237SMark Johnston 	if (n) {
129629a7237SMark Johnston 		while (n && len) {
130629a7237SMark Johnston 			ctx->gcm.Xi.c[n] ^= *(aad++);
131629a7237SMark Johnston 			--len;
132629a7237SMark Johnston 			n = (n + 1) % 16;
133629a7237SMark Johnston 		}
134629a7237SMark Johnston 		if (n == 0)
135629a7237SMark Johnston 			gcm_gmult_neon(ctx->gcm.Xi.u, ctx->gcm.Htable);
136629a7237SMark Johnston 		else {
137629a7237SMark Johnston 			ctx->gcm.ares = n;
138629a7237SMark Johnston 			return 0;
139629a7237SMark Johnston 		}
140629a7237SMark Johnston 	}
141629a7237SMark Johnston 	if ((i = (len & (size_t)-AES_BLOCK_LEN))) {
142629a7237SMark Johnston 		gcm_ghash_neon(ctx->gcm.Xi.u, ctx->gcm.Htable, aad, i);
143629a7237SMark Johnston 		aad += i;
144629a7237SMark Johnston 		len -= i;
145629a7237SMark Johnston 	}
146629a7237SMark Johnston 	if (len) {
147629a7237SMark Johnston 		n = (unsigned int)len;
148629a7237SMark Johnston 		for (i = 0; i < len; ++i)
149629a7237SMark Johnston 			ctx->gcm.Xi.c[i] ^= aad[i];
150629a7237SMark Johnston 	}
151629a7237SMark Johnston 
152629a7237SMark Johnston 	ctx->gcm.ares = n;
153629a7237SMark Johnston 	return 0;
154629a7237SMark Johnston }
155629a7237SMark Johnston 
156629a7237SMark Johnston static int
gcm_encrypt(struct ossl_gcm_context * ctx,const unsigned char * in,unsigned char * out,size_t len)157629a7237SMark Johnston gcm_encrypt(struct ossl_gcm_context *ctx, const unsigned char *in,
158629a7237SMark Johnston     unsigned char *out, size_t len)
159629a7237SMark Johnston {
160629a7237SMark Johnston 	struct bsaes_key bsks;
161629a7237SMark Johnston 	unsigned int n, ctr, mres;
162629a7237SMark Johnston 	size_t i;
163629a7237SMark Johnston 	uint64_t mlen = ctx->gcm.len.u[1];
164629a7237SMark Johnston 
165629a7237SMark Johnston 	mlen += len;
166629a7237SMark Johnston 	if (mlen > (((uint64_t)1 << 36) - 32) ||
167629a7237SMark Johnston 	    (sizeof(len) == 8 && mlen < len))
168629a7237SMark Johnston 		return -1;
169629a7237SMark Johnston 	ctx->gcm.len.u[1] = mlen;
170629a7237SMark Johnston 
171629a7237SMark Johnston 	mres = ctx->gcm.mres;
172629a7237SMark Johnston 
173629a7237SMark Johnston 	if (ctx->gcm.ares) {
174629a7237SMark Johnston 		/* First call to encrypt finalizes GHASH(AAD) */
175629a7237SMark Johnston 		gcm_gmult_neon(ctx->gcm.Xi.u, ctx->gcm.Htable);
176629a7237SMark Johnston 		ctx->gcm.ares = 0;
177629a7237SMark Johnston 	}
178629a7237SMark Johnston 
179629a7237SMark Johnston #if BYTE_ORDER == LITTLE_ENDIAN
180629a7237SMark Johnston 	ctr = bswap32(ctx->gcm.Yi.d[3]);
181629a7237SMark Johnston #else
182629a7237SMark Johnston 	ctr = ctx->gcm.Yi.d[3];
183629a7237SMark Johnston #endif
184629a7237SMark Johnston 
185629a7237SMark Johnston 	n = mres % 16;
186629a7237SMark Johnston 	if (n) {
187629a7237SMark Johnston 		while (n && len) {
188629a7237SMark Johnston 			ctx->gcm.Xi.c[n] ^= *(out++) = *(in++) ^ ctx->gcm.EKi.c[n];
189629a7237SMark Johnston 			--len;
190629a7237SMark Johnston 			n = (n + 1) % 16;
191629a7237SMark Johnston 		}
192629a7237SMark Johnston 		if (n == 0) {
193629a7237SMark Johnston 			gcm_gmult_neon(ctx->gcm.Xi.u, ctx->gcm.Htable);
194629a7237SMark Johnston 			mres = 0;
195629a7237SMark Johnston 		} else {
196629a7237SMark Johnston 			ctx->gcm.mres = n;
197629a7237SMark Johnston 			return 0;
198629a7237SMark Johnston 		}
199629a7237SMark Johnston 	}
200629a7237SMark Johnston 	if ((i = (len & (size_t)-16))) {
201629a7237SMark Johnston 		size_t j = i / 16;
202629a7237SMark Johnston 
203629a7237SMark Johnston 		memcpy(&bsks.ks, &ctx->aes_ks, sizeof(bsks.ks));
204629a7237SMark Johnston 		bsks.converted = 0;
205629a7237SMark Johnston 		ossl_bsaes_ctr32_encrypt_blocks(in, out, j, &bsks,
206629a7237SMark Johnston 		    ctx->gcm.Yi.c);
207629a7237SMark Johnston 		ctr += (unsigned int)j;
208629a7237SMark Johnston #if BYTE_ORDER == LITTLE_ENDIAN
209629a7237SMark Johnston 		ctx->gcm.Yi.d[3] = bswap32(ctr);
210629a7237SMark Johnston #else
211629a7237SMark Johnston 		ctx->gcm.Yi.d[3] = ctr;
212629a7237SMark Johnston #endif
213629a7237SMark Johnston 		in += i;
214629a7237SMark Johnston 		len -= i;
215629a7237SMark Johnston 		while (j--) {
216629a7237SMark Johnston 			for (i = 0; i < 16; ++i)
217629a7237SMark Johnston 				ctx->gcm.Xi.c[i] ^= out[i];
218629a7237SMark Johnston 			gcm_gmult_neon(ctx->gcm.Xi.u, ctx->gcm.Htable);
219629a7237SMark Johnston 			out += 16;
220629a7237SMark Johnston 		}
221629a7237SMark Johnston 	}
222629a7237SMark Johnston 	if (len) {
223629a7237SMark Johnston 		AES_encrypt(ctx->gcm.Yi.c, ctx->gcm.EKi.c, &ctx->aes_ks);
224629a7237SMark Johnston 		++ctr;
225629a7237SMark Johnston #if BYTE_ORDER == LITTLE_ENDIAN
226629a7237SMark Johnston 		ctx->gcm.Yi.d[3] = bswap32(ctr);
227629a7237SMark Johnston #else
228629a7237SMark Johnston 		ctx->gcm.Yi.d[3] = ctr;
229629a7237SMark Johnston #endif
230629a7237SMark Johnston 		while (len--) {
231629a7237SMark Johnston 			ctx->gcm.Xi.c[mres++] ^= out[n] = in[n] ^ ctx->gcm.EKi.c[n];
232629a7237SMark Johnston 			++n;
233629a7237SMark Johnston 		}
234629a7237SMark Johnston 	}
235629a7237SMark Johnston 
236629a7237SMark Johnston 	ctx->gcm.mres = mres;
237629a7237SMark Johnston 	return 0;
238629a7237SMark Johnston }
239629a7237SMark Johnston 
240629a7237SMark Johnston static int
gcm_decrypt(struct ossl_gcm_context * ctx,const unsigned char * in,unsigned char * out,size_t len)241629a7237SMark Johnston gcm_decrypt(struct ossl_gcm_context *ctx, const unsigned char *in,
242629a7237SMark Johnston     unsigned char *out, size_t len)
243629a7237SMark Johnston {
244629a7237SMark Johnston 	struct bsaes_key bsks;
245629a7237SMark Johnston 	unsigned int n, ctr, mres;
246629a7237SMark Johnston 	size_t i;
247629a7237SMark Johnston 	uint64_t mlen = ctx->gcm.len.u[1];
248629a7237SMark Johnston 
249629a7237SMark Johnston 	mlen += len;
250629a7237SMark Johnston 	if (mlen > ((1ull << 36) - 32) || (sizeof(len) == 8 && mlen < len))
251629a7237SMark Johnston 		return -1;
252629a7237SMark Johnston 	ctx->gcm.len.u[1] = mlen;
253629a7237SMark Johnston 
254629a7237SMark Johnston 	mres = ctx->gcm.mres;
255629a7237SMark Johnston 
256629a7237SMark Johnston 	if (ctx->gcm.ares) {
257629a7237SMark Johnston 		/* First call to decrypt finalizes GHASH(AAD) */
258629a7237SMark Johnston 		gcm_gmult_neon(ctx->gcm.Xi.u, ctx->gcm.Htable);
259629a7237SMark Johnston 		ctx->gcm.ares = 0;
260629a7237SMark Johnston 	}
261629a7237SMark Johnston 
262629a7237SMark Johnston #if BYTE_ORDER == LITTLE_ENDIAN
263629a7237SMark Johnston 	ctr = bswap32(ctx->gcm.Yi.d[3]);
264629a7237SMark Johnston #else
265629a7237SMark Johnston 	ctr = ctx->gcm.Yi.d[3];
266629a7237SMark Johnston #endif
267629a7237SMark Johnston 
268629a7237SMark Johnston 	n = mres % 16;
269629a7237SMark Johnston 	if (n) {
270629a7237SMark Johnston 		while (n && len) {
271629a7237SMark Johnston 			uint8_t c = *(in++);
272629a7237SMark Johnston 			*(out++) = c ^ ctx->gcm.EKi.c[n];
273629a7237SMark Johnston 			ctx->gcm.Xi.c[n] ^= c;
274629a7237SMark Johnston 			--len;
275629a7237SMark Johnston 			n = (n + 1) % 16;
276629a7237SMark Johnston 		}
277629a7237SMark Johnston 		if (n == 0) {
278629a7237SMark Johnston 			gcm_gmult_neon(ctx->gcm.Xi.u, ctx->gcm.Htable);
279629a7237SMark Johnston 			mres = 0;
280629a7237SMark Johnston 		} else {
281629a7237SMark Johnston 			ctx->gcm.mres = n;
282629a7237SMark Johnston 			return 0;
283629a7237SMark Johnston 		}
284629a7237SMark Johnston 	}
285629a7237SMark Johnston 	if ((i = (len & (size_t)-16))) {
286629a7237SMark Johnston 		size_t j = i / 16;
287629a7237SMark Johnston 
288629a7237SMark Johnston 		while (j--) {
289629a7237SMark Johnston 			size_t k;
290629a7237SMark Johnston 			for (k = 0; k < 16; ++k)
291629a7237SMark Johnston 				ctx->gcm.Xi.c[k] ^= in[k];
292629a7237SMark Johnston 			gcm_gmult_neon(ctx->gcm.Xi.u, ctx->gcm.Htable);
293629a7237SMark Johnston 			in += 16;
294629a7237SMark Johnston 		}
295629a7237SMark Johnston 		j = i / 16;
296629a7237SMark Johnston 		in -= i;
297629a7237SMark Johnston 		memcpy(&bsks.ks, &ctx->aes_ks, sizeof(bsks.ks));
298629a7237SMark Johnston 		bsks.converted = 0;
299629a7237SMark Johnston 		ossl_bsaes_ctr32_encrypt_blocks(in, out, j, &bsks,
300629a7237SMark Johnston 		    ctx->gcm.Yi.c);
301629a7237SMark Johnston 		ctr += (unsigned int)j;
302629a7237SMark Johnston #if BYTE_ORDER == LITTLE_ENDIAN
303629a7237SMark Johnston 		ctx->gcm.Yi.d[3] = bswap32(ctr);
304629a7237SMark Johnston #else
305629a7237SMark Johnston 		ctx->gcm.Yi.d[3] = ctr;
306629a7237SMark Johnston #endif
307629a7237SMark Johnston 		out += i;
308629a7237SMark Johnston 		in += i;
309629a7237SMark Johnston 		len -= i;
310629a7237SMark Johnston 	}
311629a7237SMark Johnston 	if (len) {
312629a7237SMark Johnston 		AES_encrypt(ctx->gcm.Yi.c, ctx->gcm.EKi.c, &ctx->aes_ks);
313629a7237SMark Johnston 		++ctr;
314629a7237SMark Johnston #if BYTE_ORDER == LITTLE_ENDIAN
315629a7237SMark Johnston 		ctx->gcm.Yi.d[3] = bswap32(ctr);
316629a7237SMark Johnston #else
317629a7237SMark Johnston 		ctx->gcm.Yi.d[3] = ctr;
318629a7237SMark Johnston #endif
319629a7237SMark Johnston 		while (len--) {
320629a7237SMark Johnston 			uint8_t c = in[n];
321629a7237SMark Johnston 			ctx->gcm.Xi.c[mres++] ^= c;
322629a7237SMark Johnston 			out[n] = c ^ ctx->gcm.EKi.c[n];
323629a7237SMark Johnston 			++n;
324629a7237SMark Johnston 		}
325629a7237SMark Johnston 	}
326629a7237SMark Johnston 
327629a7237SMark Johnston 	ctx->gcm.mres = mres;
328629a7237SMark Johnston 	return 0;
329629a7237SMark Johnston }
330629a7237SMark Johnston 
331629a7237SMark Johnston static void
gcm_tag(struct ossl_gcm_context * ctx,unsigned char * tag,size_t len)332629a7237SMark Johnston gcm_tag(struct ossl_gcm_context *ctx, unsigned char *tag, size_t len)
333629a7237SMark Johnston {
334629a7237SMark Johnston 	gcm_finish(ctx, NULL, 0);
335629a7237SMark Johnston 	memcpy(tag, ctx->gcm.Xi.c, len);
336629a7237SMark Johnston }
337629a7237SMark Johnston 
338629a7237SMark Johnston static const struct ossl_aes_gcm_ops gcm_ops_neon = {
339629a7237SMark Johnston 	.init = gcm_init,
340629a7237SMark Johnston 	.setiv = gcm_setiv,
341629a7237SMark Johnston 	.aad = gcm_aad,
342629a7237SMark Johnston 	.encrypt = gcm_encrypt,
343629a7237SMark Johnston 	.decrypt = gcm_decrypt,
344629a7237SMark Johnston 	.finish = gcm_finish,
345629a7237SMark Johnston 	.tag = gcm_tag,
346629a7237SMark Johnston };
347629a7237SMark Johnston 
348629a7237SMark Johnston int ossl_aes_gcm_setkey(const unsigned char *key, int klen, void *_ctx);
349629a7237SMark Johnston 
350629a7237SMark Johnston int
ossl_aes_gcm_setkey(const unsigned char * key,int klen,void * _ctx)351629a7237SMark Johnston ossl_aes_gcm_setkey(const unsigned char *key, int klen, void *_ctx)
352629a7237SMark Johnston {
353629a7237SMark Johnston 	struct ossl_gcm_context *ctx;
354629a7237SMark Johnston 
355629a7237SMark Johnston 	ctx = _ctx;
356629a7237SMark Johnston 	ctx->ops = &gcm_ops_neon;
357629a7237SMark Johnston 	gcm_init(ctx, key, klen);
358629a7237SMark Johnston 	return (0);
359629a7237SMark Johnston }
360