/*
 * ChaCha20-Poly1305 Implementation for SSH-2
 *
 * Protocol spec:
 *  http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.chacha20poly1305?rev=1.2&content-type=text/x-cvsweb-markup
 *
 * ChaCha20 spec:
 *  http://cr.yp.to/chacha/chacha-20080128.pdf
 *
 * Salsa20 spec:
 *  http://cr.yp.to/snuffle/spec.pdf
 *
 * Poly1305-AES spec:
 *  http://cr.yp.to/mac/poly1305-20050329.pdf
 *
 * The nonce for the Poly1305 is the second part of the key output
 * from the first round of ChaCha20. This removes the AES requirement.
 * This is undocumented!
 *
 * This has an intricate link between the cipher and the MAC. The
 * keying of both is done in by the cipher and setting of the IV is
 * done by the MAC. One cannot operate without the other. The
 * configuration of the ssh2_cipher structure ensures that the MAC is
 * set (and others ignored) if this cipher is chosen.
 *
 * This cipher also encrypts the length using a different
 * instantiation of the cipher using a different key and IV made from
 * the sequence number which is passed in addition when calling
 * encrypt/decrypt on it.
 */

#include "ssh.h"
#include "sshbn.h"

#ifndef INLINE
#define INLINE
#endif

/* ChaCha20 implementation, only supporting 256-bit keys */

/* State for each ChaCha20 instance */
struct chacha20 {
    /* Current context, usually with the count incremented
     * 0-3 are the static constant
     * 4-11 are the key
     * 12-13 are the counter
     * 14-15 are the IV */
    uint32 state[16];
    /* The output of the state above ready to xor */
    unsigned char current[64];
    /* The index of the above currently used to allow a true streaming cipher */
    int currentIndex;
};

static INLINE void chacha20_round(struct chacha20 *ctx)
{
    int i;
    uint32 copy[16];

    /* Take a copy */
    memcpy(copy, ctx->state, sizeof(copy));

    /* A circular rotation for a 32bit number */
#define rotl(x, shift) x = ((x << shift) | (x >> (32 - shift)))

    /* What to do for each quarter round operation */
#define qrop(a, b, c, d)                        \
    copy[a] += copy[b];                         \
    copy[c] ^= copy[a];                         \
    rotl(copy[c], d)

    /* A quarter round */
#define quarter(a, b, c, d)                     \
    qrop(a, b, d, 16);                          \
    qrop(c, d, b, 12);                          \
    qrop(a, b, d, 8);                           \
    qrop(c, d, b, 7)

    /* Do 20 rounds, in pairs because every other is different */
    for (i = 0; i < 20; i += 2) {
        /* A round */
        quarter(0, 4, 8, 12);
        quarter(1, 5, 9, 13);
        quarter(2, 6, 10, 14);
        quarter(3, 7, 11, 15);
        /* Another slightly different round */
        quarter(0, 5, 10, 15);
        quarter(1, 6, 11, 12);
        quarter(2, 7, 8, 13);
        quarter(3, 4, 9, 14);
    }

    /* Dump the macros, don't need them littering */
#undef rotl
#undef qrop
#undef quarter

    /* Add the initial state */
    for (i = 0; i < 16; ++i) {
        copy[i] += ctx->state[i];
    }

    /* Update the content of the xor buffer */
    for (i = 0; i < 16; ++i) {
        ctx->current[i * 4 + 0] = copy[i] >> 0;
        ctx->current[i * 4 + 1] = copy[i] >> 8;
        ctx->current[i * 4 + 2] = copy[i] >> 16;
        ctx->current[i * 4 + 3] = copy[i] >> 24;
    }
    /* State full, reset pointer to beginning */
    ctx->currentIndex = 0;
    smemclr(copy, sizeof(copy));

    /* Increment round counter */
    ++ctx->state[12];
    /* Check for overflow, not done in one line so the 32 bits are chopped by the type */
    if (!(uint32)(ctx->state[12])) {
        ++ctx->state[13];
    }
}

/* Initialise context with 256bit key */
static void chacha20_key(struct chacha20 *ctx, const unsigned char *key)
{
    static const char constant[16] = "expand 32-byte k";

    /* Add the fixed string to the start of the state */
    ctx->state[0] = GET_32BIT_LSB_FIRST(constant + 0);
    ctx->state[1] = GET_32BIT_LSB_FIRST(constant + 4);
    ctx->state[2] = GET_32BIT_LSB_FIRST(constant + 8);
    ctx->state[3] = GET_32BIT_LSB_FIRST(constant + 12);

    /* Add the key */
    ctx->state[4]  = GET_32BIT_LSB_FIRST(key + 0);
    ctx->state[5]  = GET_32BIT_LSB_FIRST(key + 4);
    ctx->state[6]  = GET_32BIT_LSB_FIRST(key + 8);
    ctx->state[7]  = GET_32BIT_LSB_FIRST(key + 12);
    ctx->state[8]  = GET_32BIT_LSB_FIRST(key + 16);
    ctx->state[9]  = GET_32BIT_LSB_FIRST(key + 20);
    ctx->state[10] = GET_32BIT_LSB_FIRST(key + 24);
    ctx->state[11] = GET_32BIT_LSB_FIRST(key + 28);

    /* New key, dump context */
    ctx->currentIndex = 64;
}

static void chacha20_iv(struct chacha20 *ctx, const unsigned char *iv)
{
    ctx->state[12] = 0;
    ctx->state[13] = 0;
    ctx->state[14] = GET_32BIT_MSB_FIRST(iv);
    ctx->state[15] = GET_32BIT_MSB_FIRST(iv + 4);

    /* New IV, dump context */
    ctx->currentIndex = 64;
}

static void chacha20_encrypt(struct chacha20 *ctx, unsigned char *blk, int len)
{
    while (len) {
        /* If we don't have any state left, then cycle to the next */
        if (ctx->currentIndex >= 64) {
            chacha20_round(ctx);
        }

        /* Do the xor while there's some state left and some plaintext left */
        while (ctx->currentIndex < 64 && len) {
            *blk++ ^= ctx->current[ctx->currentIndex++];
            --len;
        }
    }
}

/* Decrypt is encrypt... It's xor against a PRNG... */
static INLINE void chacha20_decrypt(struct chacha20 *ctx,
                                    unsigned char *blk, int len)
{
    chacha20_encrypt(ctx, blk, len);
}

/* Poly1305 implementation (no AES, nonce is not encrypted) */

#define NWORDS ((130 + BIGNUM_INT_BITS-1) / BIGNUM_INT_BITS)
typedef struct bigval {
    BignumInt w[NWORDS];
} bigval;

static void bigval_clear(bigval *r)
{
    int i;
    for (i = 0; i < NWORDS; i++)
        r->w[i] = 0;
}

static void bigval_import_le(bigval *r, const void *vdata, int len)
{
    const unsigned char *data = (const unsigned char *)vdata;
    int i;
    bigval_clear(r);
    for (i = 0; i < len; i++)
        r->w[i / BIGNUM_INT_BYTES] |=
            (BignumInt)data[i] << (8 * (i % BIGNUM_INT_BYTES));
}

static void bigval_export_le(const bigval *r, void *vdata, int len)
{
    unsigned char *data = (unsigned char *)vdata;
    int i;
    for (i = 0; i < len; i++)
        data[i] = r->w[i / BIGNUM_INT_BYTES] >> (8 * (i % BIGNUM_INT_BYTES));
}

/*
 * Addition of bigvals, not mod p.
 */
static void bigval_add(bigval *r, const bigval *a, const bigval *b)
{
#if BIGNUM_INT_BITS == 64
    /* ./contrib/make1305.py add 64 */
    BignumDblInt acclo;
    acclo = 0;
    acclo += a->w[0];
    acclo += b->w[0];
    r->w[0] = acclo;
    acclo >>= 64;
    acclo += a->w[1];
    acclo += b->w[1];
    r->w[1] = acclo;
    acclo >>= 64;
    acclo += a->w[2];
    acclo += b->w[2];
    r->w[2] = acclo;
    acclo >>= 64;
#elif BIGNUM_INT_BITS == 32
    /* ./contrib/make1305.py add 32 */
    BignumDblInt acclo;
    acclo = 0;
    acclo += a->w[0];
    acclo += b->w[0];
    r->w[0] = acclo;
    acclo >>= 32;
    acclo += a->w[1];
    acclo += b->w[1];
    r->w[1] = acclo;
    acclo >>= 32;
    acclo += a->w[2];
    acclo += b->w[2];
    r->w[2] = acclo;
    acclo >>= 32;
    acclo += a->w[3];
    acclo += b->w[3];
    r->w[3] = acclo;
    acclo >>= 32;
    acclo += a->w[4];
    acclo += b->w[4];
    r->w[4] = acclo;
    acclo >>= 32;
#elif BIGNUM_INT_BITS == 16
    /* ./contrib/make1305.py add 16 */
    BignumDblInt acclo;
    acclo = 0;
    acclo += a->w[0];
    acclo += b->w[0];
    r->w[0] = acclo;
    acclo >>= 16;
    acclo += a->w[1];
    acclo += b->w[1];
    r->w[1] = acclo;
    acclo >>= 16;
    acclo += a->w[2];
    acclo += b->w[2];
    r->w[2] = acclo;
    acclo >>= 16;
    acclo += a->w[3];
    acclo += b->w[3];
    r->w[3] = acclo;
    acclo >>= 16;
    acclo += a->w[4];
    acclo += b->w[4];
    r->w[4] = acclo;
    acclo >>= 16;
    acclo += a->w[5];
    acclo += b->w[5];
    r->w[5] = acclo;
    acclo >>= 16;
    acclo += a->w[6];
    acclo += b->w[6];
    r->w[6] = acclo;
    acclo >>= 16;
    acclo += a->w[7];
    acclo += b->w[7];
    r->w[7] = acclo;
    acclo >>= 16;
    acclo += a->w[8];
    acclo += b->w[8];
    r->w[8] = acclo;
    acclo >>= 16;
#else
#error Run contrib/make1305.py again with a different bit count
#endif
}

/*
 * Multiplication of bigvals mod p. Uses r as temporary storage, so
 * don't pass r aliasing a or b.
 */
static void bigval_mul_mod_p(bigval *r, const bigval *a, const bigval *b)
{
#if BIGNUM_INT_BITS == 64
    /* ./contrib/make1305.py mul 64 */
    BignumDblInt tmp;
    BignumDblInt acclo;
    BignumDblInt acchi;
    BignumDblInt acc2lo;
    acclo = 0;
    acchi = 0;
    tmp = (BignumDblInt)(a->w[0]) * (b->w[0]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 64;
    r->w[0] = acclo;
    acclo = acchi + (acclo >> 64);
    acchi = 0;
    tmp = (BignumDblInt)(a->w[0]) * (b->w[1]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 64;
    tmp = (BignumDblInt)(a->w[1]) * (b->w[0]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 64;
    r->w[1] = acclo;
    acclo = acchi + (acclo >> 64);
    acchi = 0;
    tmp = (BignumDblInt)(a->w[0]) * (b->w[2]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 64;
    tmp = (BignumDblInt)(a->w[1]) * (b->w[1]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 64;
    tmp = (BignumDblInt)(a->w[2]) * (b->w[0]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 64;
    r->w[2] = acclo & (((BignumInt)1 << 2)-1);
    acc2lo = 0;
    acc2lo += ((acclo >> 2) & (((BignumInt)1 << 62)-1)) * ((BignumDblInt)5 << 0);
    acclo = acchi + (acclo >> 64);
    acchi = 0;
    tmp = (BignumDblInt)(a->w[1]) * (b->w[2]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 64;
    tmp = (BignumDblInt)(a->w[2]) * (b->w[1]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 64;
    acc2lo += (acclo & (((BignumInt)1 << 2)-1)) * ((BignumDblInt)5 << 62);
    acc2lo += r->w[0];
    r->w[0] = acc2lo;
    acc2lo >>= 64;
    acc2lo += ((acclo >> 2) & (((BignumInt)1 << 62)-1)) * ((BignumDblInt)5 << 0);
    acclo = acchi + (acclo >> 64);
    acchi = 0;
    tmp = (BignumDblInt)(a->w[2]) * (b->w[2]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 64;
    acc2lo += (acclo & (((BignumInt)1 << 2)-1)) * ((BignumDblInt)5 << 62);
    acc2lo += r->w[1];
    r->w[1] = acc2lo;
    acc2lo >>= 64;
    acc2lo += ((acclo >> 2) & (((BignumInt)1 << 2)-1)) * ((BignumDblInt)5 << 0);
    acc2lo += r->w[2];
    r->w[2] = acc2lo;
    acc2lo = 0;
    acc2lo += ((acclo >> 4) & (((BignumInt)1 << 60)-1)) * ((BignumDblInt)25 << 0);
    acclo = acchi + (acclo >> 64);
    acchi = 0;
    acc2lo += (acclo & (((BignumInt)1 << 4)-1)) * ((BignumDblInt)25 << 60);
    acc2lo += r->w[0];
    r->w[0] = acc2lo;
    acc2lo >>= 64;
    acc2lo += ((acclo >> 4) & (((BignumInt)1 << 60)-1)) * ((BignumDblInt)25 << 0);
    acclo = acchi + (acclo >> 64);
    acchi = 0;
    acc2lo += r->w[1];
    r->w[1] = acc2lo;
    acc2lo >>= 64;
    acc2lo += r->w[2];
    r->w[2] = acc2lo;
    acc2lo >>= 64;
#elif BIGNUM_INT_BITS == 32
    /* ./contrib/make1305.py mul 32 */
    BignumDblInt tmp;
    BignumDblInt acclo;
    BignumDblInt acchi;
    BignumDblInt acc2lo;
    acclo = 0;
    acchi = 0;
    tmp = (BignumDblInt)(a->w[0]) * (b->w[0]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 32;
    r->w[0] = acclo;
    acclo = acchi + (acclo >> 32);
    acchi = 0;
    tmp = (BignumDblInt)(a->w[0]) * (b->w[1]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 32;
    tmp = (BignumDblInt)(a->w[1]) * (b->w[0]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 32;
    r->w[1] = acclo;
    acclo = acchi + (acclo >> 32);
    acchi = 0;
    tmp = (BignumDblInt)(a->w[0]) * (b->w[2]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 32;
    tmp = (BignumDblInt)(a->w[1]) * (b->w[1]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 32;
    tmp = (BignumDblInt)(a->w[2]) * (b->w[0]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 32;
    r->w[2] = acclo;
    acclo = acchi + (acclo >> 32);
    acchi = 0;
    tmp = (BignumDblInt)(a->w[0]) * (b->w[3]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 32;
    tmp = (BignumDblInt)(a->w[1]) * (b->w[2]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 32;
    tmp = (BignumDblInt)(a->w[2]) * (b->w[1]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 32;
    tmp = (BignumDblInt)(a->w[3]) * (b->w[0]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 32;
    r->w[3] = acclo;
    acclo = acchi + (acclo >> 32);
    acchi = 0;
    tmp = (BignumDblInt)(a->w[0]) * (b->w[4]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 32;
    tmp = (BignumDblInt)(a->w[1]) * (b->w[3]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 32;
    tmp = (BignumDblInt)(a->w[2]) * (b->w[2]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 32;
    tmp = (BignumDblInt)(a->w[3]) * (b->w[1]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 32;
    tmp = (BignumDblInt)(a->w[4]) * (b->w[0]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 32;
    r->w[4] = acclo & (((BignumInt)1 << 2)-1);
    acc2lo = 0;
    acc2lo += ((acclo >> 2) & (((BignumInt)1 << 30)-1)) * ((BignumDblInt)5 << 0);
    acclo = acchi + (acclo >> 32);
    acchi = 0;
    tmp = (BignumDblInt)(a->w[1]) * (b->w[4]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 32;
    tmp = (BignumDblInt)(a->w[2]) * (b->w[3]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 32;
    tmp = (BignumDblInt)(a->w[3]) * (b->w[2]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 32;
    tmp = (BignumDblInt)(a->w[4]) * (b->w[1]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 32;
    acc2lo += (acclo & (((BignumInt)1 << 2)-1)) * ((BignumDblInt)5 << 30);
    acc2lo += r->w[0];
    r->w[0] = acc2lo;
    acc2lo >>= 32;
    acc2lo += ((acclo >> 2) & (((BignumInt)1 << 30)-1)) * ((BignumDblInt)5 << 0);
    acclo = acchi + (acclo >> 32);
    acchi = 0;
    tmp = (BignumDblInt)(a->w[2]) * (b->w[4]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 32;
    tmp = (BignumDblInt)(a->w[3]) * (b->w[3]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 32;
    tmp = (BignumDblInt)(a->w[4]) * (b->w[2]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 32;
    acc2lo += (acclo & (((BignumInt)1 << 2)-1)) * ((BignumDblInt)5 << 30);
    acc2lo += r->w[1];
    r->w[1] = acc2lo;
    acc2lo >>= 32;
    acc2lo += ((acclo >> 2) & (((BignumInt)1 << 30)-1)) * ((BignumDblInt)5 << 0);
    acclo = acchi + (acclo >> 32);
    acchi = 0;
    tmp = (BignumDblInt)(a->w[3]) * (b->w[4]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 32;
    tmp = (BignumDblInt)(a->w[4]) * (b->w[3]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 32;
    acc2lo += (acclo & (((BignumInt)1 << 2)-1)) * ((BignumDblInt)5 << 30);
    acc2lo += r->w[2];
    r->w[2] = acc2lo;
    acc2lo >>= 32;
    acc2lo += ((acclo >> 2) & (((BignumInt)1 << 30)-1)) * ((BignumDblInt)5 << 0);
    acclo = acchi + (acclo >> 32);
    acchi = 0;
    tmp = (BignumDblInt)(a->w[4]) * (b->w[4]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 32;
    acc2lo += (acclo & (((BignumInt)1 << 2)-1)) * ((BignumDblInt)5 << 30);
    acc2lo += r->w[3];
    r->w[3] = acc2lo;
    acc2lo >>= 32;
    acc2lo += ((acclo >> 2) & (((BignumInt)1 << 2)-1)) * ((BignumDblInt)5 << 0);
    acc2lo += r->w[4];
    r->w[4] = acc2lo;
    acc2lo = 0;
    acc2lo += ((acclo >> 4) & (((BignumInt)1 << 28)-1)) * ((BignumDblInt)25 << 0);
    acclo = acchi + (acclo >> 32);
    acchi = 0;
    acc2lo += (acclo & (((BignumInt)1 << 4)-1)) * ((BignumDblInt)25 << 28);
    acc2lo += r->w[0];
    r->w[0] = acc2lo;
    acc2lo >>= 32;
    acc2lo += ((acclo >> 4) & (((BignumInt)1 << 28)-1)) * ((BignumDblInt)25 << 0);
    acclo = acchi + (acclo >> 32);
    acchi = 0;
    acc2lo += r->w[1];
    r->w[1] = acc2lo;
    acc2lo >>= 32;
    acc2lo += r->w[2];
    r->w[2] = acc2lo;
    acc2lo >>= 32;
    acc2lo += r->w[3];
    r->w[3] = acc2lo;
    acc2lo >>= 32;
    acc2lo += r->w[4];
    r->w[4] = acc2lo;
    acc2lo >>= 32;
#elif BIGNUM_INT_BITS == 16
    /* ./contrib/make1305.py mul 16 */
    BignumDblInt tmp;
    BignumDblInt acclo;
    BignumDblInt acchi;
    BignumDblInt acc2lo;
    acclo = 0;
    acchi = 0;
    tmp = (BignumDblInt)(a->w[0]) * (b->w[0]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    r->w[0] = acclo;
    acclo = acchi + (acclo >> 16);
    acchi = 0;
    tmp = (BignumDblInt)(a->w[0]) * (b->w[1]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[1]) * (b->w[0]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    r->w[1] = acclo;
    acclo = acchi + (acclo >> 16);
    acchi = 0;
    tmp = (BignumDblInt)(a->w[0]) * (b->w[2]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[1]) * (b->w[1]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[2]) * (b->w[0]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    r->w[2] = acclo;
    acclo = acchi + (acclo >> 16);
    acchi = 0;
    tmp = (BignumDblInt)(a->w[0]) * (b->w[3]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[1]) * (b->w[2]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[2]) * (b->w[1]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[3]) * (b->w[0]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    r->w[3] = acclo;
    acclo = acchi + (acclo >> 16);
    acchi = 0;
    tmp = (BignumDblInt)(a->w[0]) * (b->w[4]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[1]) * (b->w[3]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[2]) * (b->w[2]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[3]) * (b->w[1]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[4]) * (b->w[0]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    r->w[4] = acclo;
    acclo = acchi + (acclo >> 16);
    acchi = 0;
    tmp = (BignumDblInt)(a->w[0]) * (b->w[5]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[1]) * (b->w[4]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[2]) * (b->w[3]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[3]) * (b->w[2]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[4]) * (b->w[1]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[5]) * (b->w[0]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    r->w[5] = acclo;
    acclo = acchi + (acclo >> 16);
    acchi = 0;
    tmp = (BignumDblInt)(a->w[0]) * (b->w[6]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[1]) * (b->w[5]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[2]) * (b->w[4]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[3]) * (b->w[3]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[4]) * (b->w[2]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[5]) * (b->w[1]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[6]) * (b->w[0]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    r->w[6] = acclo;
    acclo = acchi + (acclo >> 16);
    acchi = 0;
    tmp = (BignumDblInt)(a->w[0]) * (b->w[7]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[1]) * (b->w[6]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[2]) * (b->w[5]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[3]) * (b->w[4]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[4]) * (b->w[3]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[5]) * (b->w[2]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[6]) * (b->w[1]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[7]) * (b->w[0]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    r->w[7] = acclo;
    acclo = acchi + (acclo >> 16);
    acchi = 0;
    tmp = (BignumDblInt)(a->w[0]) * (b->w[8]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[1]) * (b->w[7]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[2]) * (b->w[6]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[3]) * (b->w[5]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[4]) * (b->w[4]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[5]) * (b->w[3]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[6]) * (b->w[2]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[7]) * (b->w[1]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[8]) * (b->w[0]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    r->w[8] = acclo & (((BignumInt)1 << 2)-1);
    acc2lo = 0;
    acc2lo += ((acclo >> 2) & (((BignumInt)1 << 14)-1)) * ((BignumDblInt)5 << 0);
    acclo = acchi + (acclo >> 16);
    acchi = 0;
    tmp = (BignumDblInt)(a->w[1]) * (b->w[8]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[2]) * (b->w[7]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[3]) * (b->w[6]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[4]) * (b->w[5]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[5]) * (b->w[4]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[6]) * (b->w[3]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[7]) * (b->w[2]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[8]) * (b->w[1]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    acc2lo += (acclo & (((BignumInt)1 << 2)-1)) * ((BignumDblInt)5 << 14);
    acc2lo += r->w[0];
    r->w[0] = acc2lo;
    acc2lo >>= 16;
    acc2lo += ((acclo >> 2) & (((BignumInt)1 << 14)-1)) * ((BignumDblInt)5 << 0);
    acclo = acchi + (acclo >> 16);
    acchi = 0;
    tmp = (BignumDblInt)(a->w[2]) * (b->w[8]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[3]) * (b->w[7]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[4]) * (b->w[6]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[5]) * (b->w[5]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[6]) * (b->w[4]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[7]) * (b->w[3]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[8]) * (b->w[2]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    acc2lo += (acclo & (((BignumInt)1 << 2)-1)) * ((BignumDblInt)5 << 14);
    acc2lo += r->w[1];
    r->w[1] = acc2lo;
    acc2lo >>= 16;
    acc2lo += ((acclo >> 2) & (((BignumInt)1 << 14)-1)) * ((BignumDblInt)5 << 0);
    acclo = acchi + (acclo >> 16);
    acchi = 0;
    tmp = (BignumDblInt)(a->w[3]) * (b->w[8]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[4]) * (b->w[7]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[5]) * (b->w[6]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[6]) * (b->w[5]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[7]) * (b->w[4]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[8]) * (b->w[3]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    acc2lo += (acclo & (((BignumInt)1 << 2)-1)) * ((BignumDblInt)5 << 14);
    acc2lo += r->w[2];
    r->w[2] = acc2lo;
    acc2lo >>= 16;
    acc2lo += ((acclo >> 2) & (((BignumInt)1 << 14)-1)) * ((BignumDblInt)5 << 0);
    acclo = acchi + (acclo >> 16);
    acchi = 0;
    tmp = (BignumDblInt)(a->w[4]) * (b->w[8]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[5]) * (b->w[7]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[6]) * (b->w[6]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[7]) * (b->w[5]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[8]) * (b->w[4]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    acc2lo += (acclo & (((BignumInt)1 << 2)-1)) * ((BignumDblInt)5 << 14);
    acc2lo += r->w[3];
    r->w[3] = acc2lo;
    acc2lo >>= 16;
    acc2lo += ((acclo >> 2) & (((BignumInt)1 << 14)-1)) * ((BignumDblInt)5 << 0);
    acclo = acchi + (acclo >> 16);
    acchi = 0;
    tmp = (BignumDblInt)(a->w[5]) * (b->w[8]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[6]) * (b->w[7]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[7]) * (b->w[6]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[8]) * (b->w[5]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    acc2lo += (acclo & (((BignumInt)1 << 2)-1)) * ((BignumDblInt)5 << 14);
    acc2lo += r->w[4];
    r->w[4] = acc2lo;
    acc2lo >>= 16;
    acc2lo += ((acclo >> 2) & (((BignumInt)1 << 14)-1)) * ((BignumDblInt)5 << 0);
    acclo = acchi + (acclo >> 16);
    acchi = 0;
    tmp = (BignumDblInt)(a->w[6]) * (b->w[8]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[7]) * (b->w[7]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[8]) * (b->w[6]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    acc2lo += (acclo & (((BignumInt)1 << 2)-1)) * ((BignumDblInt)5 << 14);
    acc2lo += r->w[5];
    r->w[5] = acc2lo;
    acc2lo >>= 16;
    acc2lo += ((acclo >> 2) & (((BignumInt)1 << 14)-1)) * ((BignumDblInt)5 << 0);
    acclo = acchi + (acclo >> 16);
    acchi = 0;
    tmp = (BignumDblInt)(a->w[7]) * (b->w[8]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    tmp = (BignumDblInt)(a->w[8]) * (b->w[7]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    acc2lo += (acclo & (((BignumInt)1 << 2)-1)) * ((BignumDblInt)5 << 14);
    acc2lo += r->w[6];
    r->w[6] = acc2lo;
    acc2lo >>= 16;
    acc2lo += ((acclo >> 2) & (((BignumInt)1 << 14)-1)) * ((BignumDblInt)5 << 0);
    acclo = acchi + (acclo >> 16);
    acchi = 0;
    tmp = (BignumDblInt)(a->w[8]) * (b->w[8]);
    acclo += tmp & BIGNUM_INT_MASK;
    acchi += tmp >> 16;
    acc2lo += (acclo & (((BignumInt)1 << 2)-1)) * ((BignumDblInt)5 << 14);
    acc2lo += r->w[7];
    r->w[7] = acc2lo;
    acc2lo >>= 16;
    acc2lo += ((acclo >> 2) & (((BignumInt)1 << 2)-1)) * ((BignumDblInt)5 << 0);
    acc2lo += r->w[8];
    r->w[8] = acc2lo;
    acc2lo = 0;
    acc2lo += ((acclo >> 4) & (((BignumInt)1 << 12)-1)) * ((BignumDblInt)25 << 0);
    acclo = acchi + (acclo >> 16);
    acchi = 0;
    acc2lo += (acclo & (((BignumInt)1 << 4)-1)) * ((BignumDblInt)25 << 12);
    acc2lo += r->w[0];
    r->w[0] = acc2lo;
    acc2lo >>= 16;
    acc2lo += ((acclo >> 4) & (((BignumInt)1 << 12)-1)) * ((BignumDblInt)25 << 0);
    acclo = acchi + (acclo >> 16);
    acchi = 0;
    acc2lo += r->w[1];
    r->w[1] = acc2lo;
    acc2lo >>= 16;
    acc2lo += r->w[2];
    r->w[2] = acc2lo;
    acc2lo >>= 16;
    acc2lo += r->w[3];
    r->w[3] = acc2lo;
    acc2lo >>= 16;
    acc2lo += r->w[4];
    r->w[4] = acc2lo;
    acc2lo >>= 16;
    acc2lo += r->w[5];
    r->w[5] = acc2lo;
    acc2lo >>= 16;
    acc2lo += r->w[6];
    r->w[6] = acc2lo;
    acc2lo >>= 16;
    acc2lo += r->w[7];
    r->w[7] = acc2lo;
    acc2lo >>= 16;
    acc2lo += r->w[8];
    r->w[8] = acc2lo;
    acc2lo >>= 16;
#else
#error Run contrib/make1305.py again with a different bit count
#endif
}

static void bigval_final_reduce(bigval *n)
{
#if BIGNUM_INT_BITS == 64
    /* ./contrib/make1305.py final_reduce 64 */
    BignumDblInt acclo;
    acclo = 0;
    acclo += 5 * ((n->w[2] >> 2) + 1);
    acclo += n->w[0];
    acclo >>= 64;
    acclo += n->w[1];
    acclo >>= 64;
    acclo += n->w[2];
    acclo = 5 * (acclo >> 2);
    acclo += n->w[0];
    n->w[0] = acclo;
    acclo >>= 64;
    acclo += n->w[1];
    n->w[1] = acclo;
    acclo >>= 64;
    acclo += n->w[2];
    n->w[2] = acclo;
    acclo >>= 64;
    n->w[2] &= (1 << 2) - 1;
#elif BIGNUM_INT_BITS == 32
    /* ./contrib/make1305.py final_reduce 32 */
    BignumDblInt acclo;
    acclo = 0;
    acclo += 5 * ((n->w[4] >> 2) + 1);
    acclo += n->w[0];
    acclo >>= 32;
    acclo += n->w[1];
    acclo >>= 32;
    acclo += n->w[2];
    acclo >>= 32;
    acclo += n->w[3];
    acclo >>= 32;
    acclo += n->w[4];
    acclo = 5 * (acclo >> 2);
    acclo += n->w[0];
    n->w[0] = acclo;
    acclo >>= 32;
    acclo += n->w[1];
    n->w[1] = acclo;
    acclo >>= 32;
    acclo += n->w[2];
    n->w[2] = acclo;
    acclo >>= 32;
    acclo += n->w[3];
    n->w[3] = acclo;
    acclo >>= 32;
    acclo += n->w[4];
    n->w[4] = acclo;
    acclo >>= 32;
    n->w[4] &= (1 << 2) - 1;
#elif BIGNUM_INT_BITS == 16
    /* ./contrib/make1305.py final_reduce 16 */
    BignumDblInt acclo;
    acclo = 0;
    acclo += 5 * ((n->w[8] >> 2) + 1);
    acclo += n->w[0];
    acclo >>= 16;
    acclo += n->w[1];
    acclo >>= 16;
    acclo += n->w[2];
    acclo >>= 16;
    acclo += n->w[3];
    acclo >>= 16;
    acclo += n->w[4];
    acclo >>= 16;
    acclo += n->w[5];
    acclo >>= 16;
    acclo += n->w[6];
    acclo >>= 16;
    acclo += n->w[7];
    acclo >>= 16;
    acclo += n->w[8];
    acclo = 5 * (acclo >> 2);
    acclo += n->w[0];
    n->w[0] = acclo;
    acclo >>= 16;
    acclo += n->w[1];
    n->w[1] = acclo;
    acclo >>= 16;
    acclo += n->w[2];
    n->w[2] = acclo;
    acclo >>= 16;
    acclo += n->w[3];
    n->w[3] = acclo;
    acclo >>= 16;
    acclo += n->w[4];
    n->w[4] = acclo;
    acclo >>= 16;
    acclo += n->w[5];
    n->w[5] = acclo;
    acclo >>= 16;
    acclo += n->w[6];
    n->w[6] = acclo;
    acclo >>= 16;
    acclo += n->w[7];
    n->w[7] = acclo;
    acclo >>= 16;
    acclo += n->w[8];
    n->w[8] = acclo;
    acclo >>= 16;
    n->w[8] &= (1 << 2) - 1;
#else
#error Run contrib/make1305.py again with a different bit count
#endif
}

struct poly1305 {
    unsigned char nonce[16];
    bigval r;
    bigval h;

    /* Buffer in case we get less that a multiple of 16 bytes */
    unsigned char buffer[16];
    int bufferIndex;
};

static void poly1305_init(struct poly1305 *ctx)
{
    memset(ctx->nonce, 0, 16);
    ctx->bufferIndex = 0;
    bigval_clear(&ctx->h);
}

/* Takes a 256 bit key */
static void poly1305_key(struct poly1305 *ctx, const unsigned char *key)
{
    unsigned char key_copy[16];
    memcpy(key_copy, key, 16);

    /* Key the MAC itself
     * bytes 4, 8, 12 and 16 are required to have their top four bits clear */
    key_copy[3] &= 0x0f;
    key_copy[7] &= 0x0f;
    key_copy[11] &= 0x0f;
    key_copy[15] &= 0x0f;
    /* bytes 5, 9 and 13 are required to have their bottom two bits clear */
    key_copy[4] &= 0xfc;
    key_copy[8] &= 0xfc;
    key_copy[12] &= 0xfc;
    bigval_import_le(&ctx->r, key_copy, 16);
    smemclr(key_copy, sizeof(key_copy));

    /* Use second 128 bits are the nonce */
    memcpy(ctx->nonce, key+16, 16);
}

/* Feed up to 16 bytes (should only be less for the last chunk) */
static void poly1305_feed_chunk(struct poly1305 *ctx,
                                const unsigned char *chunk, int len)
{
    bigval c;
    bigval_import_le(&c, chunk, len);
    c.w[len / BIGNUM_INT_BYTES] |=
        (BignumInt)1 << (8 * (len % BIGNUM_INT_BYTES));
    bigval_add(&c, &c, &ctx->h);
    bigval_mul_mod_p(&ctx->h, &c, &ctx->r);
}

static void poly1305_feed(struct poly1305 *ctx,
                          const unsigned char *buf, int len)
{
    /* Check for stuff left in the buffer from last time */
    if (ctx->bufferIndex) {
        /* Try to fill up to 16 */
        while (ctx->bufferIndex < 16 && len) {
            ctx->buffer[ctx->bufferIndex++] = *buf++;
            --len;
        }
        if (ctx->bufferIndex == 16) {
            poly1305_feed_chunk(ctx, ctx->buffer, 16);
            ctx->bufferIndex = 0;
        }
    }

    /* Process 16 byte whole chunks */
    while (len >= 16) {
        poly1305_feed_chunk(ctx, buf, 16);
        len -= 16;
        buf += 16;
    }

    /* Cache stuff that's left over */
    if (len) {
        memcpy(ctx->buffer, buf, len);
        ctx->bufferIndex = len;
    }
}

/* Finalise and populate buffer with 16 byte with MAC */
static void poly1305_finalise(struct poly1305 *ctx, unsigned char *mac)
{
    bigval tmp;

    if (ctx->bufferIndex) {
        poly1305_feed_chunk(ctx, ctx->buffer, ctx->bufferIndex);
    }

    bigval_import_le(&tmp, ctx->nonce, 16);
    bigval_final_reduce(&ctx->h);
    bigval_add(&tmp, &tmp, &ctx->h);
    bigval_export_le(&tmp, mac, 16);
}

/* SSH-2 wrapper */

struct ccp_context {
    struct chacha20 a_cipher; /* Used for length */
    struct chacha20 b_cipher; /* Used for content */

    /* Cache of the first 4 bytes because they are the sequence number */
    /* Kept in 8 bytes with the top as zero to allow easy passing to setiv */
    int mac_initialised; /* Where we have got to in filling mac_iv */
    unsigned char mac_iv[8];

    struct poly1305 mac;
};

static void *poly_make_context(void *ctx)
{
    return ctx;
}

static void poly_free_context(void *ctx)
{
    /* Not allocated, just forwarded, no need to free */
}

static void poly_setkey(void *ctx, unsigned char *key)
{
    /* Uses the same context as ChaCha20, so ignore */
}

static void poly_start(void *handle)
{
    struct ccp_context *ctx = (struct ccp_context *)handle;

    ctx->mac_initialised = 0;
    memset(ctx->mac_iv, 0, 8);
    poly1305_init(&ctx->mac);
}

static void poly_bytes(void *handle, unsigned char const *blk, int len)
{
    struct ccp_context *ctx = (struct ccp_context *)handle;

    /* First 4 bytes are the IV */
    while (ctx->mac_initialised < 4 && len) {
        ctx->mac_iv[7 - ctx->mac_initialised] = *blk++;
        ++ctx->mac_initialised;
        --len;
    }

    /* Initialise the IV if needed */
    if (ctx->mac_initialised == 4) {
        chacha20_iv(&ctx->b_cipher, ctx->mac_iv);
        ++ctx->mac_initialised;  /* Don't do it again */

        /* Do first rotation */
        chacha20_round(&ctx->b_cipher);

        /* Set the poly key */
        poly1305_key(&ctx->mac, ctx->b_cipher.current);

        /* Set the first round as used */
        ctx->b_cipher.currentIndex = 64;
    }

    /* Update the MAC with anything left */
    if (len) {
        poly1305_feed(&ctx->mac, blk, len);
    }
}

static void poly_genresult(void *handle, unsigned char *blk)
{
    struct ccp_context *ctx = (struct ccp_context *)handle;
    poly1305_finalise(&ctx->mac, blk);
}

static int poly_verresult(void *handle, unsigned char const *blk)
{
    struct ccp_context *ctx = (struct ccp_context *)handle;
    int res;
    unsigned char mac[16];
    poly1305_finalise(&ctx->mac, mac);
    res = smemeq(blk, mac, 16);
    return res;
}

/* The generic poly operation used before generate and verify */
static void poly_op(void *handle, unsigned char *blk, int len, unsigned long seq)
{
    unsigned char iv[4];
    poly_start(handle);
    PUT_32BIT_MSB_FIRST(iv, seq);
    /* poly_bytes expects the first 4 bytes to be the IV */
    poly_bytes(handle, iv, 4);
    smemclr(iv, sizeof(iv));
    poly_bytes(handle, blk, len);
}

static void poly_generate(void *handle, unsigned char *blk, int len, unsigned long seq)
{
    poly_op(handle, blk, len, seq);
    poly_genresult(handle, blk+len);
}

static int poly_verify(void *handle, unsigned char *blk, int len, unsigned long seq)
{
    poly_op(handle, blk, len, seq);
    return poly_verresult(handle, blk+len);
}

static const struct ssh_mac ssh2_poly1305 = {
    poly_make_context, poly_free_context,
    poly_setkey,

    /* whole-packet operations */
    poly_generate, poly_verify,

    /* partial-packet operations */
    poly_start, poly_bytes, poly_genresult, poly_verresult,

    "", "", /* Not selectable individually, just part of ChaCha20-Poly1305 */
    16, "Poly1305"
};

static void *ccp_make_context(void)
{
    struct ccp_context *ctx = snew(struct ccp_context);
    if (ctx) {
        poly1305_init(&ctx->mac);
    }
    return ctx;
}

static void ccp_free_context(void *vctx)
{
    struct ccp_context *ctx = (struct ccp_context *)vctx;
    smemclr(&ctx->a_cipher, sizeof(ctx->a_cipher));
    smemclr(&ctx->b_cipher, sizeof(ctx->b_cipher));
    smemclr(&ctx->mac, sizeof(ctx->mac));
    sfree(ctx);
}

static void ccp_iv(void *vctx, unsigned char *iv)
{
    /* struct ccp_context *ctx = (struct ccp_context *)vctx; */
    /* IV is set based on the sequence number */
}

static void ccp_key(void *vctx, unsigned char *key)
{
    struct ccp_context *ctx = (struct ccp_context *)vctx;
    /* Initialise the a_cipher (for decrypting lengths) with the first 256 bits */
    chacha20_key(&ctx->a_cipher, key + 32);
    /* Initialise the b_cipher (for content and MAC) with the second 256 bits */
    chacha20_key(&ctx->b_cipher, key);
}

static void ccp_encrypt(void *vctx, unsigned char *blk, int len)
{
    struct ccp_context *ctx = (struct ccp_context *)vctx;
    chacha20_encrypt(&ctx->b_cipher, blk, len);
}

static void ccp_decrypt(void *vctx, unsigned char *blk, int len)
{
    struct ccp_context *ctx = (struct ccp_context *)vctx;
    chacha20_decrypt(&ctx->b_cipher, blk, len);
}

static void ccp_length_op(struct ccp_context *ctx, unsigned char *blk, int len,
                          unsigned long seq)
{
    unsigned char iv[8];
    /*
     * According to RFC 4253 (section 6.4), the packet sequence number wraps
     * at 2^32, so its 32 high-order bits will always be zero.
     */
    PUT_32BIT_LSB_FIRST(iv, 0);
    PUT_32BIT_LSB_FIRST(iv + 4, seq);
    chacha20_iv(&ctx->a_cipher, iv);
    chacha20_iv(&ctx->b_cipher, iv);
    /* Reset content block count to 1, as the first is the key for Poly1305 */
    ++ctx->b_cipher.state[12];
    smemclr(iv, sizeof(iv));
}

static void ccp_encrypt_length(void *vctx, unsigned char *blk, int len,
                               unsigned long seq)
{
    struct ccp_context *ctx = (struct ccp_context *)vctx;
    ccp_length_op(ctx, blk, len, seq);
    chacha20_encrypt(&ctx->a_cipher, blk, len);
}

static void ccp_decrypt_length(void *vctx, unsigned char *blk, int len,
                               unsigned long seq)
{
    struct ccp_context *ctx = (struct ccp_context *)vctx;
    ccp_length_op(ctx, blk, len, seq);
    chacha20_decrypt(&ctx->a_cipher, blk, len);
}

static const struct ssh2_cipher ssh2_chacha20_poly1305 = {

    ccp_make_context,
    ccp_free_context,
    ccp_iv,
    ccp_key,
    ccp_encrypt,
    ccp_decrypt,
    ccp_encrypt_length,
    ccp_decrypt_length,

    "chacha20-poly1305@openssh.com",
    1, 512, SSH_CIPHER_SEPARATE_LENGTH, "ChaCha20",

    &ssh2_poly1305
};

static const struct ssh2_cipher *const ccp_list[] = {
    &ssh2_chacha20_poly1305
};

const struct ssh2_ciphers ssh2_ccp = {
    sizeof(ccp_list) / sizeof(*ccp_list),
    ccp_list
};
