/*
 *   AES-128 and AES-CMAC (RFC 4493).
 *
 *   Portions Copyright RISC OS Developments 2026, credited to the RISC OS One Project.
 *   Original implementation placed in the public domain.
 *   Optimised forward rounds Copyright (c) RISC OS Developments 2026.
 *   Those modifications are licensed under CDDL-1.0.
 */

#include <string.h>

#include "aes.h"

static const unsigned char sbox[256] =
{
    0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76,
    0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0,
    0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15,
    0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75,
    0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84,
    0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf,
    0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8,
    0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2,
    0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73,
    0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb,
    0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79,
    0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08,
    0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a,
    0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e,
    0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf,
    0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16
};

/* Round constants, one per key schedule round */
static const unsigned char rcon[AES_ROUNDS] =
{
    0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x1b,0x36
};

void AESSetKey(aes_key_t *k, const unsigned char key[AES_KEYLEN])
{
    unsigned char *w;
    unsigned char t[4];
    int i, j;

    w = (unsigned char *) k->round_key;
    memcpy(w, key, AES_KEYLEN);

    for(i = 1; i <= AES_ROUNDS; i++)
    {
        unsigned char *prev = w + (i - 1) * AES_BLOCKLEN;
        unsigned char *cur  = w + i * AES_BLOCKLEN;

        /* RotWord then SubWord of the previous last column, plus Rcon */
        t[0] = (unsigned char) (sbox[prev[13]] ^ rcon[i - 1]);
        t[1] = sbox[prev[14]];
        t[2] = sbox[prev[15]];
        t[3] = sbox[prev[12]];

        for(j = 0; j < 4; j++)
            cur[j] = (unsigned char) (prev[j] ^ t[j]);
        for(j = 4; j < AES_BLOCKLEN; j++)
            cur[j] = (unsigned char) (prev[j] ^ cur[j - 4]);
    }
    /* Keep the expanded key in the word form used by every AES round. */
    for(i = 0; i < (AES_ROUNDS + 1) * AES_BLOCKLEN / 4; i++)
        k->round_key[i] = ((unsigned int)w[4 * i] << 24) |
                          ((unsigned int)w[4 * i + 1] << 16) |
                          ((unsigned int)w[4 * i + 2] << 8) |
                          (unsigned int)w[4 * i + 3];
    memset(t, 0, sizeof(t));
}

/* Copyright (c) RISC OS Developments 2026. All rights reserved.
 * SPDX-License-Identifier: CDDL-1.0
 * Forward AES T table for the SMB3 software cipher.
 */
static const unsigned int te0[256] =
{
    0xc66363a5u, 0xf87c7c84u, 0xee777799u, 0xf67b7b8du,
    0xfff2f20du, 0xd66b6bbdu, 0xde6f6fb1u, 0x91c5c554u,
    0x60303050u, 0x02010103u, 0xce6767a9u, 0x562b2b7du,
    0xe7fefe19u, 0xb5d7d762u, 0x4dababe6u, 0xec76769au,
    0x8fcaca45u, 0x1f82829du, 0x89c9c940u, 0xfa7d7d87u,
    0xeffafa15u, 0xb25959ebu, 0x8e4747c9u, 0xfbf0f00bu,
    0x41adadecu, 0xb3d4d467u, 0x5fa2a2fdu, 0x45afafeau,
    0x239c9cbfu, 0x53a4a4f7u, 0xe4727296u, 0x9bc0c05bu,
    0x75b7b7c2u, 0xe1fdfd1cu, 0x3d9393aeu, 0x4c26266au,
    0x6c36365au, 0x7e3f3f41u, 0xf5f7f702u, 0x83cccc4fu,
    0x6834345cu, 0x51a5a5f4u, 0xd1e5e534u, 0xf9f1f108u,
    0xe2717193u, 0xabd8d873u, 0x62313153u, 0x2a15153fu,
    0x0804040cu, 0x95c7c752u, 0x46232365u, 0x9dc3c35eu,
    0x30181828u, 0x379696a1u, 0x0a05050fu, 0x2f9a9ab5u,
    0x0e070709u, 0x24121236u, 0x1b80809bu, 0xdfe2e23du,
    0xcdebeb26u, 0x4e272769u, 0x7fb2b2cdu, 0xea75759fu,
    0x1209091bu, 0x1d83839eu, 0x582c2c74u, 0x341a1a2eu,
    0x361b1b2du, 0xdc6e6eb2u, 0xb45a5aeeu, 0x5ba0a0fbu,
    0xa45252f6u, 0x763b3b4du, 0xb7d6d661u, 0x7db3b3ceu,
    0x5229297bu, 0xdde3e33eu, 0x5e2f2f71u, 0x13848497u,
    0xa65353f5u, 0xb9d1d168u, 0x00000000u, 0xc1eded2cu,
    0x40202060u, 0xe3fcfc1fu, 0x79b1b1c8u, 0xb65b5bedu,
    0xd46a6abeu, 0x8dcbcb46u, 0x67bebed9u, 0x7239394bu,
    0x944a4adeu, 0x984c4cd4u, 0xb05858e8u, 0x85cfcf4au,
    0xbbd0d06bu, 0xc5efef2au, 0x4faaaae5u, 0xedfbfb16u,
    0x864343c5u, 0x9a4d4dd7u, 0x66333355u, 0x11858594u,
    0x8a4545cfu, 0xe9f9f910u, 0x04020206u, 0xfe7f7f81u,
    0xa05050f0u, 0x783c3c44u, 0x259f9fbau, 0x4ba8a8e3u,
    0xa25151f3u, 0x5da3a3feu, 0x804040c0u, 0x058f8f8au,
    0x3f9292adu, 0x219d9dbcu, 0x70383848u, 0xf1f5f504u,
    0x63bcbcdfu, 0x77b6b6c1u, 0xafdada75u, 0x42212163u,
    0x20101030u, 0xe5ffff1au, 0xfdf3f30eu, 0xbfd2d26du,
    0x81cdcd4cu, 0x180c0c14u, 0x26131335u, 0xc3ecec2fu,
    0xbe5f5fe1u, 0x359797a2u, 0x884444ccu, 0x2e171739u,
    0x93c4c457u, 0x55a7a7f2u, 0xfc7e7e82u, 0x7a3d3d47u,
    0xc86464acu, 0xba5d5de7u, 0x3219192bu, 0xe6737395u,
    0xc06060a0u, 0x19818198u, 0x9e4f4fd1u, 0xa3dcdc7fu,
    0x44222266u, 0x542a2a7eu, 0x3b9090abu, 0x0b888883u,
    0x8c4646cau, 0xc7eeee29u, 0x6bb8b8d3u, 0x2814143cu,
    0xa7dede79u, 0xbc5e5ee2u, 0x160b0b1du, 0xaddbdb76u,
    0xdbe0e03bu, 0x64323256u, 0x743a3a4eu, 0x140a0a1eu,
    0x924949dbu, 0x0c06060au, 0x4824246cu, 0xb85c5ce4u,
    0x9fc2c25du, 0xbdd3d36eu, 0x43acacefu, 0xc46262a6u,
    0x399191a8u, 0x319595a4u, 0xd3e4e437u, 0xf279798bu,
    0xd5e7e732u, 0x8bc8c843u, 0x6e373759u, 0xda6d6db7u,
    0x018d8d8cu, 0xb1d5d564u, 0x9c4e4ed2u, 0x49a9a9e0u,
    0xd86c6cb4u, 0xac5656fau, 0xf3f4f407u, 0xcfeaea25u,
    0xca6565afu, 0xf47a7a8eu, 0x47aeaee9u, 0x10080818u,
    0x6fbabad5u, 0xf0787888u, 0x4a25256fu, 0x5c2e2e72u,
    0x381c1c24u, 0x57a6a6f1u, 0x73b4b4c7u, 0x97c6c651u,
    0xcbe8e823u, 0xa1dddd7cu, 0xe874749cu, 0x3e1f1f21u,
    0x964b4bddu, 0x61bdbddcu, 0x0d8b8b86u, 0x0f8a8a85u,
    0xe0707090u, 0x7c3e3e42u, 0x71b5b5c4u, 0xcc6666aau,
    0x904848d8u, 0x06030305u, 0xf7f6f601u, 0x1c0e0e12u,
    0xc26161a3u, 0x6a35355fu, 0xae5757f9u, 0x69b9b9d0u,
    0x17868691u, 0x99c1c158u, 0x3a1d1d27u, 0x279e9eb9u,
    0xd9e1e138u, 0xebf8f813u, 0x2b9898b3u, 0x22111133u,
    0xd26969bbu, 0xa9d9d970u, 0x078e8e89u, 0x339494a7u,
    0x2d9b9bb6u, 0x3c1e1e22u, 0x15878792u, 0xc9e9e920u,
    0x87cece49u, 0xaa5555ffu, 0x50282878u, 0xa5dfdf7au,
    0x038c8c8fu, 0x59a1a1f8u, 0x09898980u, 0x1a0d0d17u,
    0x65bfbfdau, 0xd7e6e631u, 0x844242c6u, 0xd06868b8u,
    0x824141c3u, 0x299999b0u, 0x5a2d2d77u, 0x1e0f0f11u,
    0x7bb0b0cbu, 0xa85454fcu, 0x6dbbbbd6u, 0x2c16163au
};

/* Forward AES rounds using a compact 1 KiB lookup table. */
static unsigned int aes_word(const unsigned char *p)
{
    return ((unsigned int)p[0] << 24) | ((unsigned int)p[1] << 16) |
           ((unsigned int)p[2] << 8) | (unsigned int)p[3];
}

static unsigned int aes_ror(unsigned int word, int bits)
{
    return (word >> bits) | (word << (32 - bits));
}

static void aes_putword(unsigned char *p, unsigned int word)
{
    p[0] = (unsigned char)(word >> 24);
    p[1] = (unsigned char)(word >> 16);
    p[2] = (unsigned char)(word >> 8);
    p[3] = (unsigned char)word;
}

void AESEncryptBlock(const aes_key_t *k,
                     const unsigned char in[AES_BLOCKLEN],
                     unsigned char out[AES_BLOCKLEN])
{
    const unsigned int *rk;
    unsigned int s0, s1, s2, s3, t0, t1, t2, t3;
    int round;

    rk = k->round_key;
    s0 = aes_word(in) ^ rk[0];
    s1 = aes_word(in + 4) ^ rk[1];
    s2 = aes_word(in + 8) ^ rk[2];
    s3 = aes_word(in + 12) ^ rk[3];
    for(round = 1; round < AES_ROUNDS; round++)
    {
        rk = k->round_key + round * (AES_BLOCKLEN / 4);
        t0 = te0[s0 >> 24] ^ aes_ror(te0[(s1 >> 16) & 255], 8) ^
             aes_ror(te0[(s2 >> 8) & 255], 16) ^ aes_ror(te0[s3 & 255], 24) ^
             rk[0];
        t1 = te0[s1 >> 24] ^ aes_ror(te0[(s2 >> 16) & 255], 8) ^
             aes_ror(te0[(s3 >> 8) & 255], 16) ^ aes_ror(te0[s0 & 255], 24) ^
             rk[1];
        t2 = te0[s2 >> 24] ^ aes_ror(te0[(s3 >> 16) & 255], 8) ^
             aes_ror(te0[(s0 >> 8) & 255], 16) ^ aes_ror(te0[s1 & 255], 24) ^
             rk[2];
        t3 = te0[s3 >> 24] ^ aes_ror(te0[(s0 >> 16) & 255], 8) ^
             aes_ror(te0[(s1 >> 8) & 255], 16) ^ aes_ror(te0[s2 & 255], 24) ^
             rk[3];
        s0 = t0; s1 = t1; s2 = t2; s3 = t3;
    }
    rk = k->round_key + AES_ROUNDS * (AES_BLOCKLEN / 4);
    t0 = ((unsigned int)sbox[s0 >> 24] << 24) |
         ((unsigned int)sbox[(s1 >> 16) & 255] << 16) |
         ((unsigned int)sbox[(s2 >> 8) & 255] << 8) | sbox[s3 & 255];
    t1 = ((unsigned int)sbox[s1 >> 24] << 24) |
         ((unsigned int)sbox[(s2 >> 16) & 255] << 16) |
         ((unsigned int)sbox[(s3 >> 8) & 255] << 8) | sbox[s0 & 255];
    t2 = ((unsigned int)sbox[s2 >> 24] << 24) |
         ((unsigned int)sbox[(s3 >> 16) & 255] << 16) |
         ((unsigned int)sbox[(s0 >> 8) & 255] << 8) | sbox[s1 & 255];
    t3 = ((unsigned int)sbox[s3 >> 24] << 24) |
         ((unsigned int)sbox[(s0 >> 16) & 255] << 16) |
         ((unsigned int)sbox[(s1 >> 8) & 255] << 8) | sbox[s2 & 255];
    aes_putword(out, t0 ^ rk[0]);
    aes_putword(out + 4, t1 ^ rk[1]);
    aes_putword(out + 8, t2 ^ rk[2]);
    aes_putword(out + 12, t3 ^ rk[3]);
}

/* One left shift of a 128 bit value, with the CMAC reduction applied when a
   bit falls off the top */
static void cmac_shift(const unsigned char in[AES_BLOCKLEN],
                       unsigned char out[AES_BLOCKLEN])
{
    int i, carry;

    carry = in[0] >> 7;
    for(i = 0; i < AES_BLOCKLEN - 1; i++)
        out[i] = (unsigned char) ((in[i] << 1) | (in[i + 1] >> 7));
    out[AES_BLOCKLEN - 1] = (unsigned char) (in[AES_BLOCKLEN - 1] << 1);
    if(carry)
        out[AES_BLOCKLEN - 1] ^= 0x87;
}

void aes_cmac_init(aes_cmac_context_t *ctx,
                   const unsigned char key[AES_KEYLEN])
{
    unsigned char zero[AES_BLOCKLEN];
    unsigned char l[AES_BLOCKLEN];

    AESSetKey(&ctx->key, key);
    memset(zero, 0, sizeof(zero));
    AESEncryptBlock(&ctx->key, zero, l);
    cmac_shift(l, ctx->k1);
    cmac_shift(ctx->k1, ctx->k2);
    memset(l, 0, sizeof(l));
}

void aes_cmac_compute(const aes_cmac_context_t *ctx,
                      const void *data, size_t len,
                      unsigned char mac[AES_BLOCKLEN])
{
    unsigned char x[AES_BLOCKLEN], block[AES_BLOCKLEN];
    const unsigned char *p;
    size_t whole;
    int i, last_is_whole;

    /* How many whole blocks come before the last one.  A message that is a
       non-zero multiple of the block size ends on a whole block; anything
       else, including an empty message, ends on a padded one. */
    last_is_whole = (len > 0) && ((len % AES_BLOCKLEN) == 0);
    if(last_is_whole)
        whole = (len / AES_BLOCKLEN) - 1;
    else
        whole = len / AES_BLOCKLEN;

    memset(x, 0, sizeof(x));
    p = (const unsigned char *) data;
    while(whole > 0)
    {
        for(i = 0; i < AES_BLOCKLEN; i++)
            block[i] = (unsigned char) (x[i] ^ p[i]);
        AESEncryptBlock(&ctx->key, block, x);
        p += AES_BLOCKLEN;
        len -= AES_BLOCKLEN;
        whole--;
    }

    /* The last block, XORed with K1 whole or K2 padded */
    memset(block, 0, sizeof(block));
    if(last_is_whole)
    {
        for(i = 0; i < AES_BLOCKLEN; i++)
            block[i] = (unsigned char) (p[i] ^ ctx->k1[i]);
    }
    else
    {
        for(i = 0; i < (int) len; i++)
            block[i] = p[i];
        block[len] = 0x80;
        for(i = 0; i < AES_BLOCKLEN; i++)
            block[i] = (unsigned char) (block[i] ^ ctx->k2[i]);
    }
    for(i = 0; i < AES_BLOCKLEN; i++)
        block[i] = (unsigned char) (block[i] ^ x[i]);
    AESEncryptBlock(&ctx->key, block, mac);

    memset(x, 0, sizeof(x));  memset(block, 0, sizeof(block));
}

void aes_cmac(const unsigned char key[AES_KEYLEN],
              const void *data, size_t len,
              unsigned char mac[AES_BLOCKLEN])
{
    aes_cmac_context_t ctx;

    aes_cmac_init(&ctx, key);
    aes_cmac_compute(&ctx, data, len, mac);
    memset(&ctx, 0, sizeof(ctx));
}


/*
    AES-CCM, which is what SMB 3.0 and 3.0.2 encrypt with.

    Counter mode for the data and CBC-MAC for the tag, both built out of
    the forward cipher alone - so nothing here needs the inverse cipher,
    and the block encryption above is the whole of the arithmetic.

    The nonce is shorter than a block, and what is left over counts: with
    an 11 byte nonce, as SMB uses, four bytes remain to hold a length or a
    counter, which is where the q below comes from.

    Lengths are held in size_t and encoded big endian into those q bytes;
    the caller's data must therefore fit in them, which for q = 4 is no
    real limit for a protocol whose messages are bounded anyway.
*/

/* The counter block for keystream position i.  Position 0 makes the block
   that masks the tag; 1 upwards mask the data. */
static void ccm_ctr_block(unsigned char a[AES_BLOCKLEN],
                          const unsigned char *nonce, size_t nonce_len,
                          unsigned int counter)
{
    size_t q, i;

    q = (size_t) AES_BLOCKLEN - 1 - nonce_len;
    memset(a, 0, AES_BLOCKLEN);
    a[0] = (unsigned char) (q - 1);
    memcpy(a + 1, nonce, nonce_len);
    for(i = 0; i < q; i++)
        a[AES_BLOCKLEN - 1 - i] = (unsigned char) ((counter >> (8 * i)) & 0xFF);
}

/* Feed bytes through the CBC-MAC, zero padded up to a whole block */
static void ccm_absorb(const aes_key_t *k, unsigned char x[AES_BLOCKLEN],
                       const unsigned char *p, size_t len)
{
    unsigned char block[AES_BLOCKLEN];
    size_t take;
    int i;

    while(len > 0)
    {
        take = (len < (size_t) AES_BLOCKLEN) ? len : (size_t) AES_BLOCKLEN;
        memset(block, 0, sizeof(block));
        memcpy(block, p, take);
        for(i = 0; i < AES_BLOCKLEN; i++)
            block[i] = (unsigned char) (block[i] ^ x[i]);
        AESEncryptBlock(k, block, x);
        p += take;
        len -= take;
    }
    memset(block, 0, sizeof(block));
}

/*
    The CBC-MAC over the whole of what is being protected: a first block
    describing the message, then the additional data, then the payload.

    The additional data is prefixed with its own length and the two are
    padded together as one run, not separately - padding after the length
    would give a different tag, and the server would reject every message.
*/
static void ccm_mac(const aes_key_t *k,
                    const unsigned char *nonce, size_t nonce_len,
                    const unsigned char *aad, size_t aad_len,
                    const unsigned char *data, size_t len,
                    unsigned char x[AES_BLOCKLEN])
{
    unsigned char block[AES_BLOCKLEN];
    size_t q, i, take;

    q = (size_t) AES_BLOCKLEN - 1 - nonce_len;

    memset(block, 0, sizeof(block));
    block[0] = (unsigned char) ((aad_len ? 0x40 : 0x00)      /* data follows */
                                | (((AES_BLOCKLEN - 2) / 2) << 3)  /* 16 byte tag */
                                | (q - 1));
    memcpy(block + 1, nonce, nonce_len);
    for(i = 0; i < q; i++)
        block[AES_BLOCKLEN - 1 - i] = (unsigned char) ((len >> (8 * i)) & 0xFF);

    memset(x, 0, AES_BLOCKLEN);
    ccm_absorb(k, x, block, AES_BLOCKLEN);

    if(aad_len > 0)
    {
        memset(block, 0, sizeof(block));
        block[0] = (unsigned char) ((aad_len >> 8) & 0xFF);
        block[1] = (unsigned char) (aad_len & 0xFF);
        take = (aad_len < (size_t) AES_BLOCKLEN - 2)
                   ? aad_len : (size_t) AES_BLOCKLEN - 2;
        memcpy(block + 2, aad, take);
        ccm_absorb(k, x, block, AES_BLOCKLEN);
        if(aad_len > take)
            ccm_absorb(k, x, aad + take, aad_len - take);
    }

    ccm_absorb(k, x, data, len);
    memset(block, 0, sizeof(block));
}

/* Counter mode over the payload, which is its own inverse */
static void ccm_crypt(const aes_key_t *k,
                      const unsigned char *nonce, size_t nonce_len,
                      unsigned char *data, size_t len)
{
    unsigned char a[AES_BLOCKLEN], s[AES_BLOCKLEN];
    unsigned int counter;
    size_t take, i;

    counter = 1;
    while(len > 0)
    {
        ccm_ctr_block(a, nonce, nonce_len, counter);
        AESEncryptBlock(k, a, s);
        take = (len < (size_t) AES_BLOCKLEN) ? len : (size_t) AES_BLOCKLEN;
        for(i = 0; i < take; i++)
            data[i] = (unsigned char) (data[i] ^ s[i]);
        data += take;
        len -= take;
        counter++;
    }
    memset(a, 0, sizeof(a));
    memset(s, 0, sizeof(s));
}

void aes_ccm_encrypt(const unsigned char key[AES_KEYLEN],
                     const unsigned char *nonce, size_t nonce_len,
                     const unsigned char *aad, size_t aad_len,
                     unsigned char *data, size_t len,
                     unsigned char tag[AES_BLOCKLEN])
{
    aes_key_t k;
    unsigned char x[AES_BLOCKLEN], a0[AES_BLOCKLEN], s0[AES_BLOCKLEN];
    int i;

    AESSetKey(&k, key);
    /* The tag is taken over the plain text, so it is computed first */
    ccm_mac(&k, nonce, nonce_len, aad, aad_len, data, len, x);
    ccm_ctr_block(a0, nonce, nonce_len, 0);
    AESEncryptBlock(&k, a0, s0);
    for(i = 0; i < AES_BLOCKLEN; i++)
        tag[i] = (unsigned char) (x[i] ^ s0[i]);
    ccm_crypt(&k, nonce, nonce_len, data, len);

    memset(&k, 0, sizeof(k));
    memset(x, 0, sizeof(x));
    memset(a0, 0, sizeof(a0));
    memset(s0, 0, sizeof(s0));
}

/*
    Undo that, and say whether the tag was right.

    Returns 0 and leaves the buffer decrypted but untrustworthy if it was
    not: the caller must throw the message away rather than read it.  The
    comparison is over the whole tag whatever it finds, so that how much
    of it matched cannot be timed.
*/
int aes_ccm_decrypt(const unsigned char key[AES_KEYLEN],
                    const unsigned char *nonce, size_t nonce_len,
                    const unsigned char *aad, size_t aad_len,
                    unsigned char *data, size_t len,
                    const unsigned char tag[AES_BLOCKLEN])
{
    aes_key_t k;
    unsigned char x[AES_BLOCKLEN], a0[AES_BLOCKLEN], s0[AES_BLOCKLEN];
    int i, diff;

    AESSetKey(&k, key);
    ccm_crypt(&k, nonce, nonce_len, data, len);
    ccm_mac(&k, nonce, nonce_len, aad, aad_len, data, len, x);
    ccm_ctr_block(a0, nonce, nonce_len, 0);
    AESEncryptBlock(&k, a0, s0);

    diff = 0;
    for(i = 0; i < AES_BLOCKLEN; i++)
        diff |= (int) (unsigned char) (tag[i] ^ (x[i] ^ s0[i]));

    memset(&k, 0, sizeof(k));
    memset(x, 0, sizeof(x));
    memset(a0, 0, sizeof(a0));
    memset(s0, 0, sizeof(s0));
    return diff == 0;
}
