/* * Common footer included by every implementation of the AES-GCM MAC. * * The difficult part of AES-GCM, which is done differently depending * on what hardware acceleration is available, is the actual * evaluation of a polynomial over GF(2^128) whose coefficients are * 128-bit chunks of data. But preparing those chunks in the first * place (out of the ciphertext, associated data, and an * administrative block containing the lengths of both) is done in the * same way no matter what technique is used for the evaluation, so * that's centralised into this file, along with as much of the other * functionality as possible. * * This footer file is #included by each implementation, but each one * will define its own struct type for the state, so that each alloc * function will test sizeof() a different structure, and similarly * for free when it zeroes out the state on cleanup. * * The functions in the source file may be defined as 'inline' so that * the functions in here can inline them. The 'coeff' function in * particular probably should be, because that's called once per * 16-byte block, so eliminating function call overheads is especially * useful there. * * This footer has the following expectations from the source file * that #includes it: * * - define AESGCM_FLAVOUR to be a fragment of a C identifier that * will be included in all the function names (both the ones * defined in the implementation source file and those in here). * For example purposes below I'll suppose that this is 'foo'. * * - define AESGCM_NAME to be a string literal that will be included * in the display name of the implementation. * * - define a typedef 'aesgcm_foo' to be the state structure for the * implementation, and inside that structure, expand the macro * AESGCM_COMMON_FIELDS defined in aesgcm.h * * - define the following functions: * * // Determine whether this implementation is available at run time * static bool aesgcm_foo_available(void); * * // Set up the 'key' of the polynomial part of the MAC, that is, * // the value at which the polynomial will be evaluated. 'var' is * // a 16-byte data block in the byte order it comes out of AES. * static void aesgcm_foo_setkey_impl(aesgcm_foo *ctx, * const unsigned char *var); * * // Set up at the start of evaluating an individual polynomial. * // 'mask' is the 16-byte data block that will be XORed into the * // output value of the polynomial, also in AES byte order. This * // function should store 'mask' in whatever form is most * // convenient, and initialise an accumulator to zero. * static void aesgcm_foo_setup(aesgcm_foo *ctx, * const unsigned char *mask); * * // Fold in a coefficient of the polynomial, by means of XORing * // it into the accumulator and then multiplying the accumulator * // by the variable passed to setkey_impl() above. * // * // 'coeff' points to the 16-byte block of data that the * // polynomial coefficient will be made out of. * // * // You probably want to mark this function 'inline'. * static void aesgcm_foo_coeff(aesgcm_foo *ctx, * const unsigned char *coeff); * * // Generate the output MAC, by XORing the accumulator's final * // value with the mask passed to setup() above. * // * // 'output' points to a 16-byte region of memory to write the * // result to. * static void aesgcm_foo_output(aesgcm_foo *ctx, * unsigned char *output); * * - if allocation of the state structure must be done in a * non-standard way (e.g. x86 needs this to force greater alignment * than standard malloc provides), then #define SPECIAL_ALLOC and * define this additional function: * * // Allocate a state structure, zero out its contents, and return it. * static aesgcm_foo *aesgcm_foo_alloc(void); * * - if freeing must also be done in an unusual way, #define * SPECIAL_FREE and define this function: * * // Zero out the state structure to avoid information leaks if the * // memory is reused, and then free it. * static void aesgcm_foo_free(aesgcm_foo *ctx); */ #ifndef AESGCM_FLAVOUR #error AESGCM_FLAVOUR must be defined by any module including this footer #endif #ifndef AESGCM_NAME #error AESGCM_NAME must be defined by any module including this footer #endif #define CONTEXT CAT(aesgcm_, AESGCM_FLAVOUR) #define PREFIX(name) CAT(CAT(aesgcm_, AESGCM_FLAVOUR), CAT(_, name)) #include "aes.h" // for aes_encrypt_ecb_block static const char *PREFIX(mac_text_name)(ssh2_mac *mac) { return "AES-GCM (" AESGCM_NAME ")"; } static void PREFIX(mac_next_message)(ssh2_mac *mac) { CONTEXT *ctx = container_of(mac, CONTEXT, mac); /* * Make the mask value for a single MAC instance, by encrypting * the all-zeroes word using the associated AES instance in its * ordinary GCM fashion. This consumes the first block of * keystream (with per-block counter equal to 1), leaving the * second block of keystream ready to be used on the first block * of plaintext. */ unsigned char buf[16]; memset(buf, 0, 16); ssh_cipher_encrypt(ctx->cipher, buf, 16); PREFIX(setup)(ctx, buf); /* give it to the implementation to store */ smemclr(buf, sizeof(buf)); } static void PREFIX(mac_setkey)(ssh2_mac *mac, ptrlen key) { CONTEXT *ctx = container_of(mac, CONTEXT, mac); /* * Make the value of the polynomial variable, by encrypting the * all-zeroes word using the associated AES instance in the * special ECB mode. This is done via the special AES-specific API * function encrypt_ecb_block, which doesn't touch the counter * state at all. */ unsigned char var[16]; memset(var, 0, 16); aes_encrypt_ecb_block(ctx->cipher, var); PREFIX(setkey_impl)(ctx, var); smemclr(var, sizeof(var)); PREFIX(mac_next_message)(mac); /* set up mask */ } static void PREFIX(mac_start)(ssh2_mac *mac) { CONTEXT *ctx = container_of(mac, CONTEXT, mac); ctx->skipgot = ctx->aadgot = ctx->ciphertextlen = ctx->partlen = 0; } /* * Handle receiving data via the BinarySink API and turning it into a * collection of 16-byte blocks to use as polynomial coefficients. * * This code is written in a fully general way, which is able to * handle an arbitrary number of bytes at the start of the data to * ignore completely (necessary for PuTTY integration), and an * arbitrary number to treat as associated data, and the rest will be * regarded as ciphertext. The stream can be interrupted at any byte * position and resumed later; a partial block will be stored as * necessary. * * At the time of writing this comment, in live use most of that * generality isn't required: the full data is passed to this function * in just one call. But there's no guarantee of that staying true in * future, so we do the full deal here just in case, and the test * vectors in cryptsuite.py will test it. (And they'll use * set_prefix_lengths to set up different configurations from the SSH * usage.) */ static void PREFIX(mac_BinarySink_write)( BinarySink *bs, const void *blkv, size_t len) { CONTEXT *ctx = BinarySink_DOWNCAST(bs, CONTEXT); const unsigned char *blk = (const unsigned char *)blkv; /* * Skip the prefix sequence number used as implicit extra data in * SSH MACs. This is not included in the associated data field for * GCM, because the IV incrementation policy provides its own * sequence numbering. */ if (ctx->skipgot < ctx->skiplen) { size_t n = ctx->skiplen - ctx->skipgot; if (n > len) n = len; blk += n; len -= n; ctx->skipgot += n; if (len == 0) return; } /* * Read additional authenticated data and fold it in to the MAC. */ while (ctx->aadgot < ctx->aadlen) { size_t n = ctx->aadlen - ctx->aadgot; if (n > len) n = len; if (ctx->partlen || n < 16) { /* * Fold data into the partial block. */ if (n > 16 - ctx->partlen) n = 16 - ctx->partlen; memcpy(ctx->partblk + ctx->partlen, blk, n); ctx->partlen += n; } else if (n >= 16) { /* * Consume a whole block of AAD. */ PREFIX(coeff)(ctx, blk); n = 16; } blk += n; len -= n; ctx->aadgot += n; if (ctx->partlen == 16) { PREFIX(coeff)(ctx, ctx->partblk); ctx->partlen = 0; } if (ctx->aadgot == ctx->aadlen && ctx->partlen) { memset(ctx->partblk + ctx->partlen, 0, 16 - ctx->partlen); PREFIX(coeff)(ctx, ctx->partblk); ctx->partlen = 0; } if (len == 0) return; } /* * Read the main ciphertext and fold it in to the MAC. */ while (len > 0) { size_t n = len; if (ctx->partlen || n < 16) { /* * Fold data into the partial block. */ if (n > 16 - ctx->partlen) n = 16 - ctx->partlen; memcpy(ctx->partblk + ctx->partlen, blk, n); ctx->partlen += n; } else if (n >= 16) { /* * Consume a whole block of ciphertext. */ PREFIX(coeff)(ctx, blk); n = 16; } blk += n; len -= n; ctx->ciphertextlen += n; if (ctx->partlen == 16) { PREFIX(coeff)(ctx, ctx->partblk); ctx->partlen = 0; } } } static void PREFIX(mac_genresult)(ssh2_mac *mac, unsigned char *output) { CONTEXT *ctx = container_of(mac, CONTEXT, mac); /* * Consume any partial block of ciphertext remaining. */ if (ctx->partlen) { memset(ctx->partblk + ctx->partlen, 0, 16 - ctx->partlen); PREFIX(coeff)(ctx, ctx->partblk); } /* * Consume the final block giving the lengths of the AAD and ciphertext. */ unsigned char blk[16]; memset(blk, 0, 16); PUT_64BIT_MSB_FIRST(blk, ctx->aadlen * 8); PUT_64BIT_MSB_FIRST(blk + 8, ctx->ciphertextlen * 8); PREFIX(coeff)(ctx, blk); /* * And call the implementation's output function. */ PREFIX(output)(ctx, output); smemclr(blk, sizeof(blk)); smemclr(ctx->partblk, 16); } static ssh2_mac *PREFIX(mac_new)(const ssh2_macalg *alg, ssh_cipher *cipher) { const struct aesgcm_extra *extra = alg->extra; if (!check_aesgcm_availability(extra)) return NULL; #ifdef SPECIAL_ALLOC CONTEXT *ctx = PREFIX(alloc)(); #else CONTEXT *ctx = snew(CONTEXT); memset(ctx, 0, sizeof(CONTEXT)); #endif ctx->mac.vt = alg; ctx->cipher = cipher; /* Default values for SSH-2, overridable by set_prefix_lengths for * testcrypt purposes */ ctx->skiplen = 4; ctx->aadlen = 4; BinarySink_INIT(ctx, PREFIX(mac_BinarySink_write)); BinarySink_DELEGATE_INIT(&ctx->mac, ctx); return &ctx->mac; } static void PREFIX(set_prefix_lengths)(ssh2_mac *mac, size_t skip, size_t aad) { CONTEXT *ctx = container_of(mac, CONTEXT, mac); ctx->skiplen = skip; ctx->aadlen = aad; } static void PREFIX(mac_free)(ssh2_mac *mac) { CONTEXT *ctx = container_of(mac, CONTEXT, mac); #ifdef SPECIAL_FREE PREFIX(free)(ctx); #else smemclr(ctx, sizeof(*ctx)); sfree(ctx); #endif } static struct aesgcm_extra_mutable PREFIX(extra_mut); static const struct aesgcm_extra PREFIX(extra) = { .check_available = PREFIX(available), .mut = &PREFIX(extra_mut), .set_prefix_lengths = PREFIX(set_prefix_lengths), }; const ssh2_macalg CAT(ssh2_aesgcm_mac_, AESGCM_FLAVOUR) = { .new = PREFIX(mac_new), .free = PREFIX(mac_free), .setkey = PREFIX(mac_setkey), .start = PREFIX(mac_start), .genresult = PREFIX(mac_genresult), .next_message = PREFIX(mac_next_message), .text_name = PREFIX(mac_text_name), .name = "", .etm_name = "", /* Not selectable independently */ .len = 16, .keylen = 0, .extra = &PREFIX(extra), };