diff options
Diffstat (limited to 'sys/crypto/openssl/ossl_aes_gcm.c')
-rw-r--r-- | sys/crypto/openssl/ossl_aes_gcm.c | 615 |
1 files changed, 615 insertions, 0 deletions
diff --git a/sys/crypto/openssl/ossl_aes_gcm.c b/sys/crypto/openssl/ossl_aes_gcm.c new file mode 100644 index 000000000000..e1cdc710c3aa --- /dev/null +++ b/sys/crypto/openssl/ossl_aes_gcm.c @@ -0,0 +1,615 @@ +/* + * Copyright 2010-2022 The OpenSSL Project Authors. All Rights Reserved. + * Copyright (c) 2021, Intel Corporation. All Rights Reserved. + * Copyright (c) 2023, Raptor Engineering, LLC. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* + * This file contains an AES-GCM wrapper implementation from OpenSSL, using + * AES-NI (x86) or POWER8 Crypto Extensions (ppc). It was ported from + * cipher_aes_gcm_hw_aesni.inc and it makes use of a generic C implementation + * for partial blocks, ported from gcm128.c with OPENSSL_SMALL_FOOTPRINT defined. + */ + +#include <sys/endian.h> +#include <sys/systm.h> + +#include <crypto/openssl/ossl.h> +#include <crypto/openssl/ossl_aes_gcm.h> +#include <crypto/openssl/ossl_cipher.h> + +#include <opencrypto/cryptodev.h> + +_Static_assert( + sizeof(struct ossl_gcm_context) <= sizeof(struct ossl_cipher_context), + "ossl_gcm_context too large"); + +#if defined(__amd64__) || defined(__i386__) +#define AES_set_encrypt_key aesni_set_encrypt_key +#define AES_gcm_encrypt aesni_gcm_encrypt +#define AES_gcm_decrypt aesni_gcm_decrypt +#define AES_encrypt aesni_encrypt +#define AES_ctr32_encrypt_blocks aesni_ctr32_encrypt_blocks +#define GCM_init gcm_init_avx +#define GCM_gmult gcm_gmult_avx +#define GCM_ghash gcm_ghash_avx + +void AES_set_encrypt_key(const void *key, int bits, void *ctx); +size_t AES_gcm_encrypt(const unsigned char *in, unsigned char *out, size_t len, + const void *key, unsigned char ivec[16], uint64_t *Xi); +size_t AES_gcm_decrypt(const unsigned char *in, unsigned char *out, size_t len, + const void *key, unsigned char ivec[16], uint64_t *Xi); +void AES_encrypt(const unsigned char *in, unsigned char *out, void *ks); +void AES_ctr32_encrypt_blocks(const unsigned char *in, unsigned char *out, + size_t blocks, void *ks, const unsigned char *iv); + +void GCM_init(__uint128_t Htable[16], uint64_t Xi[2]); +void GCM_gmult(uint64_t Xi[2], const __uint128_t Htable[16]); +void GCM_ghash(uint64_t Xi[2], const __uint128_t Htable[16], const void *in, + size_t len); + +#elif defined(__powerpc64__) +#define AES_set_encrypt_key aes_p8_set_encrypt_key +#define AES_gcm_encrypt(i,o,l,k,v,x) ppc_aes_gcm_crypt(i,o,l,k,v,x,1) +#define AES_gcm_decrypt(i,o,l,k,v,x) ppc_aes_gcm_crypt(i,o,l,k,v,x,0) +#define AES_encrypt aes_p8_encrypt +#define AES_ctr32_encrypt_blocks aes_p8_ctr32_encrypt_blocks +#define GCM_init gcm_init_p8 +#define GCM_gmult gcm_gmult_p8 +#define GCM_ghash gcm_ghash_p8 + +size_t ppc_aes_gcm_encrypt(const unsigned char *in, unsigned char *out, size_t len, + const void *key, unsigned char ivec[16], uint64_t *Xi); +size_t ppc_aes_gcm_decrypt(const unsigned char *in, unsigned char *out, size_t len, + const void *key, unsigned char ivec[16], uint64_t *Xi); + +void AES_set_encrypt_key(const void *key, int bits, void *ctx); +void AES_encrypt(const unsigned char *in, unsigned char *out, void *ks); +void AES_ctr32_encrypt_blocks(const unsigned char *in, unsigned char *out, + size_t blocks, void *ks, const unsigned char *iv); + +void GCM_init(__uint128_t Htable[16], uint64_t Xi[2]); +void GCM_gmult(uint64_t Xi[2], const __uint128_t Htable[16]); +void GCM_ghash(uint64_t Xi[2], const __uint128_t Htable[16], const void *in, + size_t len); + +static size_t +ppc_aes_gcm_crypt(const unsigned char *in, unsigned char *out, + size_t len, const void *key, unsigned char ivec_[16], uint64_t *Xi, + int encrypt) +{ + union { + uint32_t d[4]; + uint8_t c[16]; + } *ivec = (void *)ivec_; + int s = 0; + int ndone = 0; + int ctr_reset = 0; + uint32_t ivec_val; + uint64_t blocks_unused; + uint64_t nb = len / 16; + uint64_t next_ctr = 0; + unsigned char ctr_saved[12]; + + memcpy(ctr_saved, ivec, 12); + + while (nb) { + ivec_val = ivec->d[3]; +#if BYTE_ORDER == LITTLE_ENDIAN + ivec_val = bswap32(ivec_val); +#endif + + blocks_unused = (uint64_t)0xffffffffU + 1 - (uint64_t)ivec_val; + if (nb > blocks_unused) { + len = blocks_unused * 16; + nb -= blocks_unused; + next_ctr = blocks_unused; + ctr_reset = 1; + } else { + len = nb * 16; + next_ctr = nb; + nb = 0; + } + + s = encrypt ? ppc_aes_gcm_encrypt(in, out, len, key, ivec->c, Xi) : + ppc_aes_gcm_decrypt(in, out, len, key, ivec->c, Xi); + + /* add counter to ivec */ +#if BYTE_ORDER == LITTLE_ENDIAN + ivec->d[3] = bswap32(ivec_val + next_ctr); +#else + ivec->d[3] += next_ctr; +#endif + if (ctr_reset) { + ctr_reset = 0; + in += len; + out += len; + } + memcpy(ivec, ctr_saved, 12); + ndone += s; + } + + return ndone; +} + +#else +#error "Unsupported architecture!" +#endif + +static void +gcm_init(struct ossl_gcm_context *ctx, const void *key, size_t keylen) +{ + KASSERT(keylen == 128 || keylen == 192 || keylen == 256, + ("%s: invalid key length %zu", __func__, keylen)); + + memset(&ctx->gcm, 0, sizeof(ctx->gcm)); + memset(&ctx->aes_ks, 0, sizeof(ctx->aes_ks)); + AES_set_encrypt_key(key, keylen, &ctx->aes_ks); + ctx->ops->init(ctx, key, keylen); +} + +static void +gcm_tag_op(struct ossl_gcm_context *ctx, unsigned char *tag, size_t len) +{ + (void)ctx->ops->finish(ctx, NULL, 0); + memcpy(tag, ctx->gcm.Xi.c, len); +} + +static void +gcm_init_op(struct ossl_gcm_context *ctx, const void *key, size_t keylen) +{ + AES_encrypt(ctx->gcm.H.c, ctx->gcm.H.c, &ctx->aes_ks); + +#if BYTE_ORDER == LITTLE_ENDIAN + ctx->gcm.H.u[0] = bswap64(ctx->gcm.H.u[0]); + ctx->gcm.H.u[1] = bswap64(ctx->gcm.H.u[1]); +#endif + + GCM_init(ctx->gcm.Htable, ctx->gcm.H.u); +} + +static void +gcm_setiv_op(struct ossl_gcm_context *ctx, const unsigned char *iv, + size_t len) +{ + uint32_t ctr; + + KASSERT(len == AES_GCM_IV_LEN, + ("%s: invalid IV length %zu", __func__, len)); + + ctx->gcm.len.u[0] = 0; + ctx->gcm.len.u[1] = 0; + ctx->gcm.ares = ctx->gcm.mres = 0; + + memcpy(ctx->gcm.Yi.c, iv, len); + ctx->gcm.Yi.c[12] = 0; + ctx->gcm.Yi.c[13] = 0; + ctx->gcm.Yi.c[14] = 0; + ctx->gcm.Yi.c[15] = 1; + ctr = 1; + + ctx->gcm.Xi.u[0] = 0; + ctx->gcm.Xi.u[1] = 0; + + AES_encrypt(ctx->gcm.Yi.c, ctx->gcm.EK0.c, &ctx->aes_ks); + ctr++; + +#if BYTE_ORDER == LITTLE_ENDIAN + ctx->gcm.Yi.d[3] = bswap32(ctr); +#else + ctx->gcm.Yi.d[3] = ctr; +#endif +} + +static int +gcm_aad_op(struct ossl_gcm_context *ctx, const unsigned char *aad, + size_t len) +{ + size_t i; + unsigned int n; + uint64_t alen = ctx->gcm.len.u[0]; + + if (ctx->gcm.len.u[1]) + return -2; + + alen += len; + if (alen > (1ull << 61) || (sizeof(len) == 8 && alen < len)) + return -1; + ctx->gcm.len.u[0] = alen; + + n = ctx->gcm.ares; + if (n) { + while (n && len) { + ctx->gcm.Xi.c[n] ^= *(aad++); + --len; + n = (n + 1) % 16; + } + if (n == 0) + GCM_gmult(ctx->gcm.Xi.u, ctx->gcm.Htable); + else { + ctx->gcm.ares = n; + return 0; + } + } + if ((i = (len & (size_t)-AES_BLOCK_LEN))) { + GCM_ghash(ctx->gcm.Xi.u, ctx->gcm.Htable, aad, i); + aad += i; + len -= i; + } + if (len) { + n = (unsigned int)len; + for (i = 0; i < len; ++i) + ctx->gcm.Xi.c[i] ^= aad[i]; + } + + ctx->gcm.ares = n; + return 0; +} + +static int +gcm_encrypt(struct ossl_gcm_context *ctx, const unsigned char *in, + unsigned char *out, size_t len) +{ + unsigned int n, ctr, mres; + size_t i; + uint64_t mlen = ctx->gcm.len.u[1]; + + mlen += len; + if (mlen > ((1ull << 36) - 32) || (sizeof(len) == 8 && mlen < len)) + return -1; + ctx->gcm.len.u[1] = mlen; + + mres = ctx->gcm.mres; + + if (ctx->gcm.ares) { + /* First call to encrypt finalizes GHASH(AAD) */ + GCM_gmult(ctx->gcm.Xi.u, ctx->gcm.Htable); + ctx->gcm.ares = 0; + } + +#if BYTE_ORDER == LITTLE_ENDIAN + ctr = bswap32(ctx->gcm.Yi.d[3]); +#else + ctr = ctx->gcm.Yi.d[3]; +#endif + + n = mres % 16; + for (i = 0; i < len; ++i) { + if (n == 0) { + AES_encrypt(ctx->gcm.Yi.c, ctx->gcm.EKi.c, + &ctx->aes_ks); + ++ctr; +#if BYTE_ORDER == LITTLE_ENDIAN + ctx->gcm.Yi.d[3] = bswap32(ctr); +#else + ctx->gcm.Yi.d[3] = ctr; +#endif + } + ctx->gcm.Xi.c[n] ^= out[i] = in[i] ^ ctx->gcm.EKi.c[n]; + mres = n = (n + 1) % 16; + if (n == 0) + GCM_gmult(ctx->gcm.Xi.u, ctx->gcm.Htable); + } + + ctx->gcm.mres = mres; + return 0; +} + +static int +gcm_encrypt_ctr32(struct ossl_gcm_context *ctx, const unsigned char *in, + unsigned char *out, size_t len) +{ + unsigned int n, ctr, mres; + size_t i; + uint64_t mlen = ctx->gcm.len.u[1]; + + mlen += len; + if (mlen > ((1ull << 36) - 32) || (sizeof(len) == 8 && mlen < len)) + return -1; + ctx->gcm.len.u[1] = mlen; + + mres = ctx->gcm.mres; + + if (ctx->gcm.ares) { + /* First call to encrypt finalizes GHASH(AAD) */ + GCM_gmult(ctx->gcm.Xi.u, ctx->gcm.Htable); + ctx->gcm.ares = 0; + } + +#if BYTE_ORDER == LITTLE_ENDIAN + ctr = bswap32(ctx->gcm.Yi.d[3]); +#else + ctr = ctx->gcm.Yi.d[3]; +#endif + + n = mres % 16; + if (n) { + while (n && len) { + ctx->gcm.Xi.c[n] ^= *(out++) = *(in++) ^ ctx->gcm.EKi.c[n]; + --len; + n = (n + 1) % 16; + } + if (n == 0) { + GCM_gmult(ctx->gcm.Xi.u, ctx->gcm.Htable); + mres = 0; + } else { + ctx->gcm.mres = n; + return 0; + } + } + if ((i = (len & (size_t)-16))) { + size_t j = i / 16; + + AES_ctr32_encrypt_blocks(in, out, j, &ctx->aes_ks, ctx->gcm.Yi.c); + ctr += (unsigned int)j; +#if BYTE_ORDER == LITTLE_ENDIAN + ctx->gcm.Yi.d[3] = bswap32(ctr); +#else + ctx->gcm.Yi.d[3] = ctr; +#endif + in += i; + len -= i; + while (j--) { + for (i = 0; i < 16; ++i) + ctx->gcm.Xi.c[i] ^= out[i]; + GCM_gmult(ctx->gcm.Xi.u, ctx->gcm.Htable); + out += 16; + } + } + if (len) { + AES_encrypt(ctx->gcm.Yi.c, ctx->gcm.EKi.c, &ctx->aes_ks); + ++ctr; +#if BYTE_ORDER == LITTLE_ENDIAN + ctx->gcm.Yi.d[3] = bswap32(ctr); +#else + ctx->gcm.Yi.d[3] = ctr; +#endif + while (len--) { + ctx->gcm.Xi.c[mres++] ^= out[n] = in[n] ^ ctx->gcm.EKi.c[n]; + ++n; + } + } + + ctx->gcm.mres = mres; + return 0; +} + +static int +gcm_encrypt_op(struct ossl_gcm_context *ctx, const unsigned char *in, + unsigned char *out, size_t len) +{ + size_t bulk = 0, res; + int error; + + res = MIN(len, (AES_BLOCK_LEN - ctx->gcm.mres) % AES_BLOCK_LEN); + if ((error = gcm_encrypt(ctx, in, out, res)) != 0) + return error; + + bulk = AES_gcm_encrypt(in + res, out + res, len - res, + &ctx->aes_ks, ctx->gcm.Yi.c, ctx->gcm.Xi.u); + ctx->gcm.len.u[1] += bulk; + bulk += res; + + if ((error = gcm_encrypt_ctr32(ctx, in + bulk, out + bulk, + len - bulk)) != 0) + return error; + + return 0; +} + +static int +gcm_decrypt(struct ossl_gcm_context *ctx, const unsigned char *in, + unsigned char *out, size_t len) +{ + unsigned int n, ctr, mres; + size_t i; + uint64_t mlen = ctx->gcm.len.u[1]; + + mlen += len; + if (mlen > ((1ull << 36) - 32) || (sizeof(len) == 8 && mlen < len)) + return -1; + ctx->gcm.len.u[1] = mlen; + + mres = ctx->gcm.mres; + + if (ctx->gcm.ares) { + /* First call to encrypt finalizes GHASH(AAD) */ + GCM_gmult(ctx->gcm.Xi.u, ctx->gcm.Htable); + ctx->gcm.ares = 0; + } + +#if BYTE_ORDER == LITTLE_ENDIAN + ctr = bswap32(ctx->gcm.Yi.d[3]); +#else + ctr = ctx->gcm.Yi.d[3]; +#endif + + n = mres % 16; + for (i = 0; i < len; ++i) { + uint8_t c; + if (n == 0) { + AES_encrypt(ctx->gcm.Yi.c, ctx->gcm.EKi.c, + &ctx->aes_ks); + ++ctr; +#if BYTE_ORDER == LITTLE_ENDIAN + ctx->gcm.Yi.d[3] = bswap32(ctr); +#else + ctx->gcm.Yi.d[3] = ctr; +#endif + } + c = in[i]; + out[i] = c ^ ctx->gcm.EKi.c[n]; + ctx->gcm.Xi.c[n] ^= c; + mres = n = (n + 1) % 16; + if (n == 0) + GCM_gmult(ctx->gcm.Xi.u, ctx->gcm.Htable); + } + + ctx->gcm.mres = mres; + return 0; +} + +static int +gcm_decrypt_ctr32(struct ossl_gcm_context *ctx, const unsigned char *in, + unsigned char *out, size_t len) +{ + unsigned int n, ctr, mres; + size_t i; + uint64_t mlen = ctx->gcm.len.u[1]; + + mlen += len; + if (mlen > ((1ull << 36) - 32) || (sizeof(len) == 8 && mlen < len)) + return -1; + ctx->gcm.len.u[1] = mlen; + + mres = ctx->gcm.mres; + + if (ctx->gcm.ares) { + /* First call to decrypt finalizes GHASH(AAD) */ + GCM_gmult(ctx->gcm.Xi.u, ctx->gcm.Htable); + ctx->gcm.ares = 0; + } + +#if BYTE_ORDER == LITTLE_ENDIAN + ctr = bswap32(ctx->gcm.Yi.d[3]); +#else + ctr = ctx->gcm.Yi.d[3]; +#endif + + n = mres % 16; + if (n) { + while (n && len) { + uint8_t c = *(in++); + *(out++) = c ^ ctx->gcm.EKi.c[n]; + ctx->gcm.Xi.c[n] ^= c; + --len; + n = (n + 1) % 16; + } + if (n == 0) { + GCM_gmult(ctx->gcm.Xi.u, ctx->gcm.Htable); + mres = 0; + } else { + ctx->gcm.mres = n; + return 0; + } + } + if ((i = (len & (size_t)-16))) { + size_t j = i / 16; + + while (j--) { + size_t k; + for (k = 0; k < 16; ++k) + ctx->gcm.Xi.c[k] ^= in[k]; + GCM_gmult(ctx->gcm.Xi.u, ctx->gcm.Htable); + in += 16; + } + j = i / 16; + in -= i; + AES_ctr32_encrypt_blocks(in, out, j, &ctx->aes_ks, ctx->gcm.Yi.c); + ctr += (unsigned int)j; +#if BYTE_ORDER == LITTLE_ENDIAN + ctx->gcm.Yi.d[3] = bswap32(ctr); +#else + ctx->gcm.Yi.d[3] = ctr; +#endif + out += i; + in += i; + len -= i; + } + if (len) { + AES_encrypt(ctx->gcm.Yi.c, ctx->gcm.EKi.c, &ctx->aes_ks); + ++ctr; +#if BYTE_ORDER == LITTLE_ENDIAN + ctx->gcm.Yi.d[3] = bswap32(ctr); +#else + ctx->gcm.Yi.d[3] = ctr; +#endif + while (len--) { + uint8_t c = in[n]; + ctx->gcm.Xi.c[mres++] ^= c; + out[n] = c ^ ctx->gcm.EKi.c[n]; + ++n; + } + } + + ctx->gcm.mres = mres; + return 0; +} + +static int +gcm_decrypt_op(struct ossl_gcm_context *ctx, const unsigned char *in, + unsigned char *out, size_t len) +{ + size_t bulk = 0, res; + int error; + + res = MIN(len, (AES_BLOCK_LEN - ctx->gcm.mres) % AES_BLOCK_LEN); + if ((error = gcm_decrypt(ctx, in, out, res)) != 0) + return error; + + bulk = AES_gcm_decrypt(in + res, out + res, len - res, &ctx->aes_ks, + ctx->gcm.Yi.c, ctx->gcm.Xi.u); + ctx->gcm.len.u[1] += bulk; + bulk += res; + + if ((error = gcm_decrypt_ctr32(ctx, in + bulk, out + bulk, len - bulk)) != 0) + return error; + + return 0; +} + +static int +gcm_finish_op(struct ossl_gcm_context *ctx, const unsigned char *tag, + size_t len) +{ + uint64_t alen = ctx->gcm.len.u[0] << 3; + uint64_t clen = ctx->gcm.len.u[1] << 3; + + if (ctx->gcm.mres || ctx->gcm.ares) + GCM_gmult(ctx->gcm.Xi.u, ctx->gcm.Htable); + +#if BYTE_ORDER == LITTLE_ENDIAN + alen = bswap64(alen); + clen = bswap64(clen); +#endif + + ctx->gcm.Xi.u[0] ^= alen; + ctx->gcm.Xi.u[1] ^= clen; + GCM_gmult(ctx->gcm.Xi.u, ctx->gcm.Htable); + + ctx->gcm.Xi.u[0] ^= ctx->gcm.EK0.u[0]; + ctx->gcm.Xi.u[1] ^= ctx->gcm.EK0.u[1]; + + if (tag != NULL) + return timingsafe_bcmp(ctx->gcm.Xi.c, tag, len); + return 0; +} + +static const struct ossl_aes_gcm_ops gcm_ops = { + .init = gcm_init_op, + .setiv = gcm_setiv_op, + .aad = gcm_aad_op, + .encrypt = gcm_encrypt_op, + .decrypt = gcm_decrypt_op, + .finish = gcm_finish_op, + .tag = gcm_tag_op, +}; + +int ossl_aes_gcm_setkey(const unsigned char *key, int klen, void *_ctx); + +int +ossl_aes_gcm_setkey(const unsigned char *key, int klen, + void *_ctx) +{ + struct ossl_gcm_context *ctx; + + ctx = _ctx; + ctx->ops = &gcm_ops; + gcm_init(ctx, key, klen); + return (0); +} |