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