Added general-purpose API for AEAD algorithms, and GCM implementation.
[BearSSL] / src / aead / gcm.c
diff --git a/src/aead/gcm.c b/src/aead/gcm.c
new file mode 100644 (file)
index 0000000..9cf0f38
--- /dev/null
@@ -0,0 +1,296 @@
+/*
+ * Copyright (c) 2017 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/*
+ * Implementation Notes
+ * ====================
+ *
+ * Since CTR and GHASH implementations can handle only full blocks, a
+ * 16-byte buffer (buf[]) is maintained in the context:
+ *
+ *  - When processing AAD, buf[] contains the 0-15 unprocessed bytes.
+ *
+ *  - When doing CTR encryption / decryption, buf[] contains the AES output
+ *    for the last partial block, to be used with the next few bytes of
+ *    data, as well as the already encrypted bytes. For instance, if the
+ *    processed data length so far is 21 bytes, then buf[0..4] contains
+ *    the five last encrypted bytes, and buf[5..15] contains the next 11
+ *    AES output bytes to be XORed with the next 11 bytes of input.
+ *
+ *    The recorded AES output bytes are used to complete the block when
+ *    the corresponding bytes are obtained. Note that buf[] always
+ *    contains the _encrypted_ bytes, whether we apply encryption or
+ *    decryption: these bytes are used as input to GHASH when the block
+ *    is complete.
+ *
+ * In both cases, the low bits of the data length counters (count_aad,
+ * count_ctr) are used to work out the current situation.
+ */
+
+/* see bearssl_aead.h */
+void
+br_gcm_init(br_gcm_context *ctx, const br_block_ctr_class **bctx, br_ghash gh)
+{
+       unsigned char iv[12];
+
+       ctx->bctx = bctx;
+       ctx->gh = gh;
+
+       /*
+        * The GHASH key h[] is the raw encryption of the all-zero
+        * block. Since we only have a CTR implementation, we use it
+        * with an all-zero IV and a zero counter, to CTR-encrypt an
+        * all-zero block.
+        */
+       memset(ctx->h, 0, sizeof ctx->h);
+       memset(iv, 0, sizeof iv);
+       (*bctx)->run(bctx, iv, 0, ctx->h, sizeof ctx->h);
+}
+
+/* see bearssl_aead.h */
+void
+br_gcm_reset(br_gcm_context *ctx, const void *iv, size_t len)
+{
+       /*
+        * If the provided nonce is 12 bytes, then this is the initial
+        * IV for CTR mode; it will be used with a counter that starts
+        * at 2 (value 1 is for encrypting the GHASH output into the tag).
+        *
+        * If the provided nonce has any other length, then it is hashed
+        * (with GHASH) into a 16-byte value that will be the IV for CTR
+        * (both 12-byte IV and 32-bit counter).
+        */
+       if (len == 12) {
+               memcpy(ctx->j0_1, iv, 12);
+               ctx->j0_2 = 1;
+       } else {
+               unsigned char ty[16], tmp[16];
+
+               memset(ty, 0, sizeof ty);
+               ctx->gh(ty, ctx->h, iv, len);
+               memset(tmp, 0, 8);
+               br_enc64be(tmp + 8, (uint64_t)len << 3);
+               ctx->gh(ty, ctx->h, tmp, 16);
+               memcpy(ctx->j0_1, ty, 12);
+               ctx->j0_2 = br_dec32be(ty + 12);
+       }
+       ctx->jc = ctx->j0_2 + 1;
+       memset(ctx->y, 0, sizeof ctx->y);
+       ctx->count_aad = 0;
+       ctx->count_ctr = 0;
+}
+
+/* see bearssl_aead.h */
+void
+br_gcm_aad_inject(br_gcm_context *ctx, const void *data, size_t len)
+{
+       size_t ptr, dlen;
+
+       ptr = (size_t)ctx->count_aad & (size_t)15;
+       if (ptr != 0) {
+               /*
+                * If there is a partial block, then we first try to
+                * complete it.
+                */
+               size_t clen;
+
+               clen = 16 - ptr;
+               if (len < clen) {
+                       memcpy(ctx->buf + ptr, data, len);
+                       ctx->count_aad += (uint64_t)len;
+                       return;
+               }
+               memcpy(ctx->buf + ptr, data, clen);
+               ctx->gh(ctx->y, ctx->h, ctx->buf, 16);
+               data = (const unsigned char *)data + clen;
+               len -= clen;
+               ctx->count_aad += (uint64_t)clen;
+       }
+
+       /*
+        * Now AAD is aligned on a 16-byte block (with regards to GHASH).
+        * We process all complete blocks, and save the last partial
+        * block.
+        */
+       dlen = len & ~(size_t)15;
+       ctx->gh(ctx->y, ctx->h, data, dlen);
+       memcpy(ctx->buf, (const unsigned char *)data + dlen, len - dlen);
+       ctx->count_aad += (uint64_t)len;
+}
+
+/* see bearssl_aead.h */
+void
+br_gcm_flip(br_gcm_context *ctx)
+{
+       /*
+        * We complete the GHASH computation if there is a partial block.
+        * The GHASH implementation automatically applies padding with
+        * zeros.
+        */
+       size_t ptr;
+
+       ptr = (size_t)ctx->count_aad & (size_t)15;
+       if (ptr != 0) {
+               ctx->gh(ctx->y, ctx->h, ctx->buf, ptr);
+       }
+}
+
+/* see bearssl_aead.h */
+void
+br_gcm_run(br_gcm_context *ctx, int encrypt, void *data, size_t len)
+{
+       unsigned char *buf;
+       size_t ptr, dlen;
+
+       buf = data;
+       ptr = (size_t)ctx->count_ctr & (size_t)15;
+       if (ptr != 0) {
+               /*
+                * If we have a partial block, then we try to complete it.
+                */
+               size_t u, clen;
+
+               clen = 16 - ptr;
+               if (len < clen) {
+                       clen = len;
+               }
+               for (u = 0; u < clen; u ++) {
+                       unsigned x, y;
+
+                       x = buf[u];
+                       y = x ^ ctx->buf[ptr + u];
+                       ctx->buf[ptr + u] = encrypt ? y : x;
+                       buf[u] = y;
+               }
+               ctx->count_ctr += (uint64_t)clen;
+               buf += clen;
+               len -= clen;
+               if (ptr + clen < 16) {
+                       return;
+               }
+               ctx->gh(ctx->y, ctx->h, ctx->buf, 16);
+       }
+
+       /*
+        * Process full blocks.
+        */
+       dlen = len & ~(size_t)15;
+       if (!encrypt) {
+               ctx->gh(ctx->y, ctx->h, buf, dlen);
+       }
+       ctx->jc = (*ctx->bctx)->run(ctx->bctx, ctx->j0_1, ctx->jc, buf, dlen);
+       if (encrypt) {
+               ctx->gh(ctx->y, ctx->h, buf, dlen);
+       }
+       buf += dlen;
+       len -= dlen;
+       ctx->count_ctr += (uint64_t)dlen;
+
+       if (len > 0) {
+               /*
+                * There is a partial block.
+                */
+               size_t u;
+
+               memset(ctx->buf, 0, sizeof ctx->buf);
+               ctx->jc = (*ctx->bctx)->run(ctx->bctx, ctx->j0_1,
+                       ctx->jc, ctx->buf, 16);
+               for (u = 0; u < len; u ++) {
+                       unsigned x, y;
+
+                       x = buf[u];
+                       y = x ^ ctx->buf[u];
+                       ctx->buf[u] = encrypt ? y : x;
+                       buf[u] = y;
+               }
+               ctx->count_ctr += (uint64_t)len;
+       }
+}
+
+/* see bearssl_aead.h */
+void
+br_gcm_get_tag(br_gcm_context *ctx, void *tag)
+{
+       size_t ptr;
+       unsigned char tmp[16];
+
+       ptr = (size_t)ctx->count_ctr & (size_t)15;
+       if (ptr > 0) {
+               /*
+                * There is a partial block: encrypted/decrypted data has
+                * been produced, but the encrypted bytes must still be
+                * processed by GHASH.
+                */
+               ctx->gh(ctx->y, ctx->h, ctx->buf, ptr);
+       }
+
+       /*
+        * Final block for GHASH: the AAD and plaintext lengths (in bits).
+        */
+       br_enc64be(tmp, ctx->count_aad << 3);
+       br_enc64be(tmp + 8, ctx->count_ctr << 3);
+       ctx->gh(ctx->y, ctx->h, tmp, 16);
+
+       /*
+        * Tag is the GHASH output XORed with the encryption of the
+        * nonce with the initial counter value.
+        */
+       memcpy(tag, ctx->y, 16);
+       (*ctx->bctx)->run(ctx->bctx, ctx->j0_1, ctx->j0_2, tag, 16);
+}
+
+/* see bearssl_aead.h */
+uint32_t
+br_gcm_check_tag(br_gcm_context *ctx, const void *tag)
+{
+       unsigned char tmp[16];
+       size_t u;
+       int x;
+
+       br_gcm_get_tag(ctx, tmp);
+       x = 0;
+       for (u = 0; u < sizeof tmp; u ++) {
+               x |= tmp[u] ^ ((const unsigned char *)tag)[u];
+       }
+       return EQ0(x);
+}
+
+/* see bearssl_aead.h */
+const br_aead_class br_gcm_vtable = {
+       16,
+       (void (*)(const br_aead_class **, const void *, size_t))
+               &br_gcm_reset,
+       (void (*)(const br_aead_class **, const void *, size_t))
+               &br_gcm_aad_inject,
+       (void (*)(const br_aead_class **))
+               &br_gcm_flip,
+       (void (*)(const br_aead_class **, int, void *, size_t))
+               &br_gcm_run,
+       (void (*)(const br_aead_class **, void *))
+               &br_gcm_get_tag,
+       (uint32_t (*)(const br_aead_class **, const void *))
+               &br_gcm_check_tag
+};