Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mRemoteNG/PuTTYNG.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/aesgcm-footer.h')
-rw-r--r--crypto/aesgcm-footer.h368
1 files changed, 368 insertions, 0 deletions
diff --git a/crypto/aesgcm-footer.h b/crypto/aesgcm-footer.h
new file mode 100644
index 00000000..981905da
--- /dev/null
+++ b/crypto/aesgcm-footer.h
@@ -0,0 +1,368 @@
+/*
+ * 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 posible.
+ *
+ * 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),
+};