root/lib/libcrypto/modes/gcm128.c
/* $OpenBSD: gcm128.c,v 1.55 2026/01/17 14:30:37 jsing Exp $ */
/* ====================================================================
 * Copyright (c) 2010 The OpenSSL Project.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. All advertising materials mentioning features or use of this
 *    software must display the following acknowledgment:
 *    "This product includes software developed by the OpenSSL Project
 *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
 *
 * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
 *    endorse or promote products derived from this software without
 *    prior written permission. For written permission, please contact
 *    openssl-core@openssl.org.
 *
 * 5. Products derived from this software may not be called "OpenSSL"
 *    nor may "OpenSSL" appear in their names without prior written
 *    permission of the OpenSSL Project.
 *
 * 6. Redistributions of any form whatsoever must retain the following
 *    acknowledgment:
 *    "This product includes software developed by the OpenSSL Project
 *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
 *
 * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 * ====================================================================
 */

#include <string.h>

#include <openssl/crypto.h>

#include "crypto_arch.h"
#include "crypto_internal.h"
#include "modes_local.h"

void
gcm_init_4bit(u128 Htable[16], uint64_t H[2])
{
        u128 V;
        uint64_t T;
        int i;

        Htable[0].hi = 0;
        Htable[0].lo = 0;
        V.hi = H[0];
        V.lo = H[1];

        for (Htable[8] = V, i = 4; i > 0; i >>= 1) {
                T = U64(0xe100000000000000) & (0 - (V.lo & 1));
                V.lo = (V.hi << 63) | (V.lo >> 1);
                V.hi = (V.hi >> 1 ) ^ T;
                Htable[i] = V;
        }

        for (i = 2; i < 16; i <<= 1) {
                u128 *Hi = Htable + i;
                int   j;
                for (V = *Hi, j = 1; j < i; j++) {
                        Hi[j].hi = V.hi ^ Htable[j].hi;
                        Hi[j].lo = V.lo ^ Htable[j].lo;
                }
        }
}

#if !defined(HAVE_GCM_GHASH_4BIT) && !defined(HAVE_GCM_GMULT_4BIT)
static const uint16_t rem_4bit[16] = {
        0x0000, 0x1c20, 0x3840, 0x2460, 0x7080, 0x6ca0, 0x48c0, 0x54e0,
        0xe100, 0xfd20, 0xd940, 0xc560, 0x9180, 0x8da0, 0xa9c0, 0xb5e0,
};
#endif

#ifdef HAVE_GCM_GMULT_4BIT
void gcm_gmult_4bit(uint64_t Xi[2], const u128 Htable[16]);

#else
static void
gcm_gmult_4bit(uint64_t Xi[2], const u128 Htable[16])
{
        u128 Z;
        int cnt = 15;
        size_t rem, nlo, nhi;

        nlo = ((const uint8_t *)Xi)[15];
        nhi = nlo >> 4;
        nlo &= 0xf;

        Z.hi = Htable[nlo].hi;
        Z.lo = Htable[nlo].lo;

        while (1) {
                rem = (size_t)Z.lo & 0xf;
                Z.lo = (Z.hi << 60)|(Z.lo >> 4);
                Z.hi = (Z.hi >> 4);
                Z.hi ^= (uint64_t)rem_4bit[rem] << 48;
                Z.hi ^= Htable[nhi].hi;
                Z.lo ^= Htable[nhi].lo;

                if (--cnt < 0)
                        break;

                nlo = ((const uint8_t *)Xi)[cnt];
                nhi = nlo >> 4;
                nlo &= 0xf;

                rem = (size_t)Z.lo & 0xf;
                Z.lo = (Z.hi << 60)|(Z.lo >> 4);
                Z.hi = (Z.hi >> 4);
                Z.hi ^= (uint64_t)rem_4bit[rem] << 48;
                Z.hi ^= Htable[nlo].hi;
                Z.lo ^= Htable[nlo].lo;
        }

        Xi[0] = htobe64(Z.hi);
        Xi[1] = htobe64(Z.lo);
}
#endif

#ifdef HAVE_GCM_GHASH_4BIT
void gcm_ghash_4bit(uint64_t Xi[2], const u128 Htable[16], const uint8_t *inp,
    size_t len);

#else
static void
gcm_ghash_4bit(uint64_t Xi[2], const u128 Htable[16],
    const uint8_t *inp, size_t len)
{
        u128 Z;
        int cnt;
        size_t rem, nlo, nhi;

        do {
                cnt = 15;
                nlo = ((const uint8_t *)Xi)[15];
                nlo ^= inp[15];
                nhi = nlo >> 4;
                nlo &= 0xf;

                Z.hi = Htable[nlo].hi;
                Z.lo = Htable[nlo].lo;

                while (1) {
                        rem = (size_t)Z.lo & 0xf;
                        Z.lo = (Z.hi << 60)|(Z.lo >> 4);
                        Z.hi = (Z.hi >> 4);
                        Z.hi ^= (uint64_t)rem_4bit[rem] << 48;
                        Z.hi ^= Htable[nhi].hi;
                        Z.lo ^= Htable[nhi].lo;

                        if (--cnt < 0)
                                break;

                        nlo = ((const uint8_t *)Xi)[cnt];
                        nlo ^= inp[cnt];
                        nhi = nlo >> 4;
                        nlo &= 0xf;

                        rem = (size_t)Z.lo & 0xf;
                        Z.lo = (Z.hi << 60)|(Z.lo >> 4);
                        Z.hi = (Z.hi >> 4);
                        Z.hi ^= (uint64_t)rem_4bit[rem] << 48;
                        Z.hi ^= Htable[nlo].hi;
                        Z.lo ^= Htable[nlo].lo;
                }

                Xi[0] = htobe64(Z.hi);
                Xi[1] = htobe64(Z.lo);
        } while (inp += 16, len -= 16);
}
#endif

static inline void
gcm_mul(GCM128_CONTEXT *ctx, uint64_t u[2])
{
        ctx->gmult(u, ctx->Htable);
}

static inline void
gcm_ghash(GCM128_CONTEXT *ctx, const uint8_t *in, size_t len)
{
        ctx->ghash(ctx->Xi.u, ctx->Htable, in, len);
}

#ifdef HAVE_GCM128_INIT
void gcm128_init(GCM128_CONTEXT *ctx);

#else
static void
gcm128_init(GCM128_CONTEXT *ctx)
{
        gcm_init_4bit(ctx->Htable, ctx->H.u);
        ctx->gmult = gcm_gmult_4bit;
        ctx->ghash = gcm_ghash_4bit;
}
#endif

void
CRYPTO_gcm128_init(GCM128_CONTEXT *ctx, void *key, block128_f block)
{
        memset(ctx, 0, sizeof(*ctx));
        ctx->block = block;
        ctx->key = key;

        (*block)(ctx->H.c, ctx->H.c, key);

        /* H is stored in host byte order */
        ctx->H.u[0] = be64toh(ctx->H.u[0]);
        ctx->H.u[1] = be64toh(ctx->H.u[1]);

        gcm128_init(ctx);
}
LCRYPTO_ALIAS(CRYPTO_gcm128_init);

GCM128_CONTEXT *
CRYPTO_gcm128_new(void *key, block128_f block)
{
        GCM128_CONTEXT *ctx;

        if ((ctx = calloc(1, sizeof(*ctx))) == NULL)
                return NULL;

        CRYPTO_gcm128_init(ctx, key, block);

        return ctx;
}
LCRYPTO_ALIAS(CRYPTO_gcm128_new);

void
CRYPTO_gcm128_release(GCM128_CONTEXT *ctx)
{
        freezero(ctx, sizeof(*ctx));
}
LCRYPTO_ALIAS(CRYPTO_gcm128_release);

void
CRYPTO_gcm128_setiv(GCM128_CONTEXT *ctx, const unsigned char *iv, size_t len)
{
        unsigned int ctr;

        ctx->Yi.u[0] = 0;
        ctx->Yi.u[1] = 0;
        ctx->Xi.u[0] = 0;
        ctx->Xi.u[1] = 0;
        ctx->len.u[0] = 0;      /* AAD length */
        ctx->len.u[1] = 0;      /* message length */
        ctx->ares = 0;
        ctx->mres = 0;

        if (len == 12) {
                memcpy(ctx->Yi.c, iv, 12);
                ctx->Yi.c[15] = 1;
                ctr = 1;
        } else {
                size_t i;
                uint64_t len0 = len;

                while (len >= 16) {
                        for (i = 0; i < 16; i++)
                                ctx->Yi.c[i] ^= iv[i];
                        gcm_mul(ctx, ctx->Yi.u);
                        iv += 16;
                        len -= 16;
                }
                if (len > 0) {
                        for (i = 0; i < len; i++)
                                ctx->Yi.c[i] ^= iv[i];
                        gcm_mul(ctx, ctx->Yi.u);
                }
                len0 <<= 3;
                ctx->Yi.u[1] ^= htobe64(len0);

                gcm_mul(ctx, ctx->Yi.u);

                ctr = be32toh(ctx->Yi.d[3]);
        }

        (*ctx->block)(ctx->Yi.c, ctx->EK0.c, ctx->key);
        ctx->Yi.d[3] = htobe32(++ctr);
}
LCRYPTO_ALIAS(CRYPTO_gcm128_setiv);

int
CRYPTO_gcm128_aad(GCM128_CONTEXT *ctx, const unsigned char *aad, size_t len)
{
        unsigned int n;
        uint64_t alen;
        size_t i;

        if (ctx->len.u[1] != 0)
                return -2;

        alen = ctx->len.u[0] + len;
        if (alen > (U64(1) << 61) || (sizeof(len) == 8 && alen < len))
                return -1;
        ctx->len.u[0] = alen;

        if ((n = ctx->ares) > 0) {
                while (n > 0 && len > 0) {
                        ctx->Xi.c[n] ^= *(aad++);
                        n = (n + 1) % 16;
                        len--;
                }
                if (n > 0) {
                        ctx->ares = n;
                        return 0;
                }
                gcm_mul(ctx, ctx->Xi.u);
        }

        if ((i = (len & (size_t)-16)) > 0) {
                gcm_ghash(ctx, aad, i);
                aad += i;
                len -= i;
        }
        if (len > 0) {
                n = (unsigned int)len;
                for (i = 0; i < len; i++)
                        ctx->Xi.c[i] ^= aad[i];
        }
        ctx->ares = n;

        return 0;
}
LCRYPTO_ALIAS(CRYPTO_gcm128_aad);

int
CRYPTO_gcm128_encrypt(GCM128_CONTEXT *ctx, const unsigned char *in,
    unsigned char *out, size_t len)
{
        unsigned int n, ctr;
        uint64_t mlen;
        size_t i;

        mlen = ctx->len.u[1] + len;
        if (mlen > ((U64(1) << 36) - 32) || (sizeof(len) == 8 && mlen < len))
                return -1;
        ctx->len.u[1] = mlen;

        if (ctx->ares > 0) {
                /* First call to encrypt finalizes GHASH(AAD) */
                gcm_mul(ctx, ctx->Xi.u);
                ctx->ares = 0;
        }

        ctr = be32toh(ctx->Yi.d[3]);

        n = ctx->mres;

        for (i = 0; i < len; i++) {
                if (n == 0) {
                        ctx->block(ctx->Yi.c, ctx->EKi.c, ctx->key);
                        ctx->Yi.d[3] = htobe32(++ctr);
                }
                ctx->Xi.c[n] ^= out[i] = in[i] ^ ctx->EKi.c[n];
                n = (n + 1) % 16;
                if (n == 0)
                        gcm_mul(ctx, ctx->Xi.u);
        }

        ctx->mres = n;

        return 0;
}
LCRYPTO_ALIAS(CRYPTO_gcm128_encrypt);

int
CRYPTO_gcm128_decrypt(GCM128_CONTEXT *ctx, const unsigned char *in,
    unsigned char *out, size_t len)
{
        unsigned int n, ctr;
        uint64_t mlen;
        uint8_t c;
        size_t i;

        mlen = ctx->len.u[1] + len;
        if (mlen > ((U64(1) << 36) - 32) || (sizeof(len) == 8 && mlen < len))
                return -1;
        ctx->len.u[1] = mlen;

        if (ctx->ares) {
                /* First call to decrypt finalizes GHASH(AAD) */
                gcm_mul(ctx, ctx->Xi.u);
                ctx->ares = 0;
        }

        ctr = be32toh(ctx->Yi.d[3]);

        n = ctx->mres;

        for (i = 0; i < len; i++) {
                if (n == 0) {
                        ctx->block(ctx->Yi.c, ctx->EKi.c, ctx->key);
                        ctx->Yi.d[3] = htobe32(++ctr);
                }
                c = in[i];
                out[i] = c ^ ctx->EKi.c[n];
                ctx->Xi.c[n] ^= c;
                n = (n + 1) % 16;
                if (n == 0)
                        gcm_mul(ctx, ctx->Xi.u);
        }

        ctx->mres = n;

        return 0;
}
LCRYPTO_ALIAS(CRYPTO_gcm128_decrypt);

int
CRYPTO_gcm128_encrypt_ctr32(GCM128_CONTEXT *ctx, const unsigned char *in,
    unsigned char *out, size_t len, ctr128_f stream)
{
        unsigned int n, ctr;
        uint64_t mlen;
        size_t i, j;

        mlen = ctx->len.u[1] + len;
        if (mlen > ((U64(1) << 36) - 32) || (sizeof(len) == 8 && mlen < len))
                return -1;
        ctx->len.u[1] = mlen;

        if (ctx->ares > 0) {
                /* First call to encrypt finalizes GHASH(AAD) */
                gcm_mul(ctx, ctx->Xi.u);
                ctx->ares = 0;
        }

        ctr = be32toh(ctx->Yi.d[3]);

        if ((n = ctx->mres) > 0) {
                while (n > 0 && len > 0) {
                        ctx->Xi.c[n] ^= *(out++) = *(in++) ^ ctx->EKi.c[n];
                        n = (n + 1) % 16;
                        len--;
                }
                if (n > 0) {
                        ctx->mres = n;
                        return 0;
                }
                gcm_mul(ctx, ctx->Xi.u);
        }
        if ((i = (len & (size_t)-16)) > 0) {
                j = i / 16;
                stream(in, out, j, ctx->key, ctx->Yi.c);
                ctr += (unsigned int)j;
                ctx->Yi.d[3] = htobe32(ctr);
                gcm_ghash(ctx, out, i);
                in += i;
                out += i;
                len -= i;
        }
        if (len > 0) {
                ctx->block(ctx->Yi.c, ctx->EKi.c, ctx->key);
                ctx->Yi.d[3] = htobe32(++ctr);
                while (len-- > 0) {
                        ctx->Xi.c[n] ^= out[n] = in[n] ^ ctx->EKi.c[n];
                        n++;
                }
        }

        ctx->mres = n;

        return 0;
}
LCRYPTO_ALIAS(CRYPTO_gcm128_encrypt_ctr32);

int
CRYPTO_gcm128_decrypt_ctr32(GCM128_CONTEXT *ctx, const unsigned char *in,
    unsigned char *out, size_t len, ctr128_f stream)
{
        unsigned int n, ctr;
        uint64_t mlen;
        size_t i, j;
        uint8_t c;

        mlen = ctx->len.u[1] + len;
        if (mlen > ((U64(1) << 36) - 32) || (sizeof(len) == 8 && mlen < len))
                return -1;
        ctx->len.u[1] = mlen;

        if (ctx->ares > 0) {
                /* First call to decrypt finalizes GHASH(AAD) */
                gcm_mul(ctx, ctx->Xi.u);
                ctx->ares = 0;
        }

        ctr = be32toh(ctx->Yi.d[3]);

        if ((n = ctx->mres) > 0) {
                while (n > 0 && len > 0) {
                        c = *(in++);
                        *(out++) = c ^ ctx->EKi.c[n];
                        ctx->Xi.c[n] ^= c;
                        n = (n + 1) % 16;
                        len--;
                }
                if (n > 0) {
                        ctx->mres = n;
                        return 0;
                }
                gcm_mul(ctx, ctx->Xi.u);
        }
        if ((i = (len & (size_t)-16)) > 0) {
                j = i / 16;
                gcm_ghash(ctx, in, i);
                stream(in, out, j, ctx->key, ctx->Yi.c);
                ctr += (unsigned int)j;
                ctx->Yi.d[3] = htobe32(ctr);
                in += i;
                out += i;
                len -= i;
        }
        if (len > 0) {
                ctx->block(ctx->Yi.c, ctx->EKi.c, ctx->key);
                ctx->Yi.d[3] = htobe32(++ctr);
                while (len-- > 0) {
                        c = in[n];
                        ctx->Xi.c[n] ^= c;
                        out[n] = c ^ ctx->EKi.c[n];
                        n++;
                }
        }

        ctx->mres = n;

        return 0;
}
LCRYPTO_ALIAS(CRYPTO_gcm128_decrypt_ctr32);

int
CRYPTO_gcm128_finish(GCM128_CONTEXT *ctx, const unsigned char *tag,
    size_t len)
{
        uint64_t alen, clen;

        alen = ctx->len.u[0] << 3;
        clen = ctx->len.u[1] << 3;

        if (ctx->ares > 0 || ctx->mres > 0)
                gcm_mul(ctx, ctx->Xi.u);

        ctx->Xi.u[0] ^= htobe64(alen);
        ctx->Xi.u[1] ^= htobe64(clen);
        gcm_mul(ctx, ctx->Xi.u);

        ctx->Xi.u[0] ^= ctx->EK0.u[0];
        ctx->Xi.u[1] ^= ctx->EK0.u[1];

        if (tag == NULL || len > sizeof(ctx->Xi))
                return -1;

        return timingsafe_memcmp(ctx->Xi.c, tag, len);
}
LCRYPTO_ALIAS(CRYPTO_gcm128_finish);

void
CRYPTO_gcm128_tag(GCM128_CONTEXT *ctx, unsigned char *tag, size_t len)
{
        CRYPTO_gcm128_finish(ctx, NULL, 0);

        if (len > sizeof(ctx->Xi.c))
                len = sizeof(ctx->Xi.c);

        memcpy(tag, ctx->Xi.c, len);
}
LCRYPTO_ALIAS(CRYPTO_gcm128_tag);