+gcm128
authorbg <bg@nerilex.org>
Thu, 22 Jan 2015 01:03:49 +0000 (02:03 +0100)
committerbg <bg@nerilex.org>
Thu, 22 Jan 2015 01:03:49 +0000 (02:03 +0100)
gcm/gcm128.c [new file with mode: 0644]
gcm/gcm128.h [new file with mode: 0644]
mkfiles/gcm.mk [new file with mode: 0644]
test_src/main-gcm-test.c [new file with mode: 0644]

diff --git a/gcm/gcm128.c b/gcm/gcm128.c
new file mode 100644 (file)
index 0000000..1f71046
--- /dev/null
@@ -0,0 +1,331 @@
+/* gcm128.c */
+/*
+    This file is part of the AVR-Crypto-Lib.
+    Copyright (C) 2006-2015 Daniel Otte (daniel.otte@rub.de)
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+#include <memxor.h>
+#include <blockcipher_descriptor.h>
+#include <bcal-basic.h>
+#include <gcm128.h>
+
+#include <stdio.h>
+#include <cli.h>
+#include <uart.h>
+
+#define DUMP_LEN_LINE(x, len, line) do { \
+        printf("\n\n (DBG <" __FILE__ " %s " #line ">)" #x ":", __func__); \
+        cli_hexdump_block((x), (len), 4, 16); \
+        uart0_flush(); } while (0)
+
+#define DUMP_dummy(x, len, v) DUMP_LEN_LINE(x, len, v)
+#define DUMP_LEN(x, len) DUMP_dummy(x, len, __LINE__)
+
+
+#define DUMP(x) DUMP_LEN(x, sizeof(x))
+
+
+#define BLOCK_BYTES GCM128_BLOCK_BYTES
+#define BLOCK_BITS GCM128_BLOCK_BITS
+
+#define COUNT_BYTES GCM128_COUNT_BYTES
+#define COUNT_BITS GCM128_COUNT_BITS
+
+#define POLY_BYTE 0xE1
+
+
+static uint8_t shift_block_right(void *a)
+{
+    uint8_t c1 = 0, c2;
+    uint8_t i = BLOCK_BYTES;
+    uint8_t *p = a;
+    do {
+        c2 = *p & 1;
+        *p = (*p >> 1) | c1;
+        p++;
+        c1 = c2 ? 0x80 : 0;
+    } while (--i);
+    return c2;
+}
+
+static void gmul128(
+        void *dest,
+        const void *a,
+        const void *b)
+{
+    uint8_t c, v[BLOCK_BYTES], t, dummy[BLOCK_BYTES + 1], *(lut[2]);
+    const uint8_t *x = b;
+    uint8_t i, j;
+    memset(dest, 0, BLOCK_BYTES);
+    memset(dummy, 0, BLOCK_BYTES);
+    dummy[BLOCK_BYTES] = POLY_BYTE;
+    memcpy(v, a, BLOCK_BYTES);
+    lut[0] = dummy;
+    lut[1] = v;
+    i = BLOCK_BYTES;
+    do {
+        j = 8;
+        t = *x++;
+        do {
+            memxor(dest, lut[t >> 7], BLOCK_BYTES);
+            t <<= 1;
+            c = shift_block_right(v);
+            v[0] ^= dummy[15 + c];
+        } while (--j);
+    } while (--i);
+}
+
+static void ghash128_init(ghash128_ctx_t *ctx)
+{
+    memset(ctx->tag, 0, 16);
+}
+
+static void ghash128_block(
+        ghash128_ctx_t *ctx,
+        const void *block)
+{
+    uint8_t tmp[BLOCK_BYTES];
+    memcpy(tmp, ctx->tag, BLOCK_BYTES);
+    memxor(tmp, block, BLOCK_BYTES);
+    gmul128(ctx->tag, tmp, ctx->key);
+}
+
+static void inc32(void *a)
+{
+    uint8_t c, *p = a;
+    c = (p[3]++ == 0);
+    c &= ((p[2] += c) == 0);
+    c &= ((p[1] += c) == 0);
+    p[0] +=c;
+}
+
+int8_t gcm128_init(
+        gcm128_ctx_t *ctx,
+        const bcdesc_t *cipher,
+        const void *key,
+        uint16_t key_length_b,
+        const void *iv,
+        uint16_t iv_length_b)
+{
+    uint8_t r;
+    uint8_t tmp[BLOCK_BYTES];
+    if (bcal_cipher_getBlocksize_b(cipher) != BLOCK_BITS) {
+        return -1;
+    }
+    if ((r = bcal_cipher_init(cipher, key, key_length_b, &ctx->cipher_ctx))) {
+        printf_P(PSTR("Error: return code: %"PRId8" key length: %"PRId16" <%s %s %d>\n"), r, key_length_b, __FILE__, __func__, __LINE__);
+        uart0_flush();
+        return -2;
+    }
+    memset(ctx->ghash_ctx.key, 0, BLOCK_BYTES);
+    bcal_cipher_enc(ctx->ghash_ctx.key, &ctx->cipher_ctx);
+    ghash128_init(&ctx->ghash_ctx);
+    if (iv_length_b == BLOCK_BITS - COUNT_BITS) {
+        memcpy(ctx->ctr, iv, (BLOCK_BITS - COUNT_BITS) / 8);
+        memset(&ctx->ctr[BLOCK_BYTES - COUNT_BYTES], 0, COUNT_BYTES - 1);
+        ctx->ctr[BLOCK_BYTES - 1] = 1;
+    } else {
+        uint16_t ctr = iv_length_b / BLOCK_BITS;
+        while (ctr--)
+        {
+            ghash128_block(&ctx->ghash_ctx, iv);
+            iv = &((uint8_t*)iv)[BLOCK_BYTES];
+        }
+        memset(tmp, 0, BLOCK_BYTES);
+        memcpy(tmp, iv, (iv_length_b % BLOCK_BITS + 7) / 8);
+        if (iv_length_b & 7) {
+            tmp[(iv_length_b % BLOCK_BITS) / 8] &= 0xff << (8 - (iv_length_b & 7));
+        }
+        ghash128_block(&ctx->ghash_ctx, tmp);
+        memset(tmp, 0, BLOCK_BYTES);
+        tmp[BLOCK_BYTES - 2] = iv_length_b >> 8;
+        tmp[BLOCK_BYTES - 1] = iv_length_b & 0xff;
+        ghash128_block(&ctx->ghash_ctx, tmp);
+        memcpy(ctx->ctr, ctx->ghash_ctx.tag, BLOCK_BYTES);
+        ghash128_init(&ctx->ghash_ctx);
+    }
+    memcpy(ctx->j0, &ctx->ctr[BLOCK_BYTES - COUNT_BYTES], COUNT_BYTES);
+    ctx->length_a = 0;
+    ctx->length_c = 0;
+    return 0;
+}
+
+void gcm128_add_ad_block(
+        gcm128_ctx_t *ctx,
+        const void *block )
+{
+    ghash128_block(&ctx->ghash_ctx, block);
+    ctx->length_a += BLOCK_BITS;
+}
+
+void gcm128_add_ad_final_block(
+        gcm128_ctx_t *ctx,
+        const void *block,
+        uint16_t length_b )
+{
+    uint8_t tmp[BLOCK_BYTES];
+    while (length_b >= BLOCK_BITS)
+    {
+        gcm128_add_ad_block(ctx, block);
+        length_b -= BLOCK_BITS;
+        block = &((uint8_t*)block)[BLOCK_BYTES];
+    }
+    if (length_b > 0) {
+        memset(tmp, 0, BLOCK_BYTES);
+        memcpy(tmp, block, (length_b + 7) / 8);
+        if (length_b & 7) {
+            tmp[length_b / 8] &= 0xff << (8 - (length_b & 7));
+        }
+        ghash128_block(&ctx->ghash_ctx, tmp);
+        ctx->length_a += length_b;
+    }
+}
+
+void gcm128_encrypt_block(
+        gcm128_ctx_t *ctx,
+        void *dest,
+        const void *src)
+{
+    uint8_t tmp[BLOCK_BYTES];
+    inc32(&ctx->ctr[BLOCK_BYTES - COUNT_BYTES]);
+    memcpy(tmp, ctx->ctr, BLOCK_BYTES);
+    bcal_cipher_enc(tmp, &ctx->cipher_ctx);
+    memxor(tmp, src, BLOCK_BYTES);
+    ghash128_block(&ctx->ghash_ctx, tmp);
+    ctx->length_c += BLOCK_BITS;
+    if (dest) {
+        memcpy(dest, tmp, BLOCK_BYTES);
+    }
+}
+
+void gcm128_encrypt_final_block(
+        gcm128_ctx_t *ctx,
+        void *dest,
+        const void *src,
+        uint16_t length_b
+        )
+{
+    uint8_t tmp[BLOCK_BYTES];
+    while (length_b >= BLOCK_BITS) {
+        gcm128_encrypt_block(ctx, dest, src);
+        length_b -= BLOCK_BITS;
+        if (dest) {
+            dest = &((uint8_t*)dest)[BLOCK_BYTES];
+        }
+        src = &((uint8_t*)src)[BLOCK_BYTES];
+    }
+    if (length_b > 0) {
+        inc32(&ctx->ctr[BLOCK_BYTES - COUNT_BYTES]);
+        memcpy(tmp, ctx->ctr, BLOCK_BYTES);
+        bcal_cipher_enc(tmp, &ctx->cipher_ctx);
+        memxor(tmp, src, BLOCK_BYTES);
+        memset(&tmp[(length_b + 7) / 8], 0, BLOCK_BYTES - (length_b + 7) / 8);
+        if (length_b & 7) {
+            tmp[length_b / 8] &= 0xff << (8 - (length_b & 7));
+        }
+        ghash128_block(&ctx->ghash_ctx, tmp);
+        ctx->length_c += length_b;
+        if (dest) {
+            memcpy(dest, tmp, (length_b + 7) / 8);
+        }
+    }
+}
+
+void gcm128_decrypt_block(
+        gcm128_ctx_t *ctx,
+        void *dest,
+        const void *src)
+{
+    uint8_t tmp[BLOCK_BYTES];
+    ghash128_block(&ctx->ghash_ctx, src);
+    inc32(&ctx->ctr[11]);
+    memcpy(tmp, ctx->ctr, BLOCK_BYTES);
+    bcal_cipher_enc(tmp, &ctx->cipher_ctx);
+    memxor(tmp, src, BLOCK_BYTES);
+    ctx->length_c += BLOCK_BITS;
+    if (dest) {
+        memcpy(dest, tmp, BLOCK_BYTES);
+    }
+}
+
+void gcm128_decrypt_final_block(
+        gcm128_ctx_t *ctx,
+        void *dest,
+        const void *src,
+        uint16_t length_b)
+{
+    uint8_t tmp[BLOCK_BYTES];
+    while (length_b > BLOCK_BITS) {
+        gcm128_decrypt_block(ctx, dest, src);
+        length_b -= BLOCK_BITS;
+        if (dest) {
+            dest = &((uint8_t*)dest)[BLOCK_BYTES];
+        }
+        src = &((uint8_t*)src)[BLOCK_BYTES];
+    }
+    if (length_b > 0) {
+        memcpy(tmp, src, (length_b + 7) / 8);
+        memset(&tmp[(length_b + 7) / 8], 0, BLOCK_BYTES - (length_b + 7) / 8);
+        if (length_b & 7) {
+            tmp[length_b / 8] &= 0xff << (8 - (length_b & 7));
+        }
+        ghash128_block(&ctx->ghash_ctx, tmp);
+        inc32(&ctx->ctr[11]);
+        memcpy(tmp, ctx->ctr, BLOCK_BYTES);
+        bcal_cipher_enc(tmp, &ctx->cipher_ctx);
+        memxor(tmp, src, BLOCK_BYTES);
+        memset(&tmp[(length_b + 7) / 8], 0, BLOCK_BYTES - (length_b + 7) / 8);
+        if (length_b & 7) {
+            tmp[length_b / 8] &= 0xff << (8 - (length_b & 7));
+        }
+        ctx->length_c += length_b;
+        if (dest) {
+            memcpy(dest, tmp, (length_b + 7) / 8);
+        }
+    }
+}
+
+void gcm128_finalize(gcm128_ctx_t *ctx, void *tag, uint16_t tag_length_b)
+{
+    uint8_t tmp[BLOCK_BYTES];
+    memset(tmp, 0, BLOCK_BYTES);
+    tmp[4] = ctx->length_a >> 24;
+    tmp[5] = ctx->length_a >> 16;
+    tmp[6] = ctx->length_a >>  8;
+    tmp[7] = ctx->length_a;
+    tmp[12] = ctx->length_c >> 24;
+    tmp[13] = ctx->length_c >> 16;
+    tmp[14] = ctx->length_c >>  8;
+    tmp[15] = ctx->length_c;
+    ghash128_block(&ctx->ghash_ctx, tmp);
+    memcpy(tmp, ctx->ctr, BLOCK_BYTES - COUNT_BYTES);
+    memcpy(&tmp[BLOCK_BYTES - COUNT_BYTES], ctx->j0, COUNT_BYTES);
+    bcal_cipher_enc(tmp, &ctx->cipher_ctx);
+    bcal_cipher_free(&ctx->cipher_ctx);
+    memxor(tmp, ctx->ghash_ctx.tag, BLOCK_BYTES);
+    if (tag_length_b > BLOCK_BITS) {
+        tag_length_b = BLOCK_BITS;
+    }
+    if (tag_length_b & 7) {
+        tmp[tag_length_b / 8] &= 0xff << (8 - (tag_length_b & 7));
+    }
+    if (tag) {
+        memcpy(tag, tmp, (tag_length_b + 7) / 8);
+    }
+}
diff --git a/gcm/gcm128.h b/gcm/gcm128.h
new file mode 100644 (file)
index 0000000..755b75a
--- /dev/null
@@ -0,0 +1,116 @@
+/* gcm128.h */
+/*
+    This file is part of the AVR-Crypto-Lib.
+    Copyright (C) 2015 Daniel Otte (daniel.otte@rub.de)
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef GCM_GCM128_H_
+#define GCM_GCM128_H_
+
+
+/* gcm128.c */
+/*
+    This file is part of the AVR-Crypto-Lib.
+    Copyright (C) 2006-2015 Daniel Otte (daniel.otte@rub.de)
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+#include <memxor.h>
+#include <blockcipher_descriptor.h>
+#include <gcm128.h>
+
+#define GCM128_BLOCK_BYTES 16
+#define GCM128_BLOCK_BITS (GCM128_BLOCK_BYTES * 8)
+
+#define GCM128_COUNT_BYTES 4
+#define GCM128_COUNT_BITS (GCM128_COUNT_BYTES * 8)
+
+
+
+typedef struct {
+    uint8_t tag[GCM128_BLOCK_BYTES];
+    uint8_t key[GCM128_BLOCK_BYTES];
+} ghash128_ctx_t;
+
+typedef struct {
+    ghash128_ctx_t ghash_ctx;
+    bcgen_ctx_t cipher_ctx;
+    uint8_t ctr[GCM128_BLOCK_BYTES];
+    uint8_t j0[GCM128_COUNT_BYTES];
+    uint32_t length_a;
+    uint32_t length_c;
+} gcm128_ctx_t;
+
+int8_t gcm128_init(
+        gcm128_ctx_t *ctx,
+        const bcdesc_t *cipher,
+        const void *key,
+        uint16_t key_length_b,
+        const void *iv,
+        uint16_t iv_length_b);
+
+void gcm128_add_ad_block(
+        gcm128_ctx_t *ctx,
+        const void *block );
+
+void gcm128_add_ad_final_block(
+        gcm128_ctx_t *ctx,
+        const void *block,
+        uint16_t length_b );
+
+void gcm128_encrypt_block(
+        gcm128_ctx_t *ctx,
+        void *dest,
+        const void *src);
+
+void gcm128_encrypt_final_block(
+        gcm128_ctx_t *ctx,
+        void *dest,
+        const void *src,
+        uint16_t length_b);
+
+void gcm128_decrypt_block(
+        gcm128_ctx_t *ctx,
+        void *dest,
+        const void *src);
+
+void gcm128_decrypt_final_block(
+        gcm128_ctx_t *ctx,
+        void *dest,
+        const void *src,
+        uint16_t length_b);
+
+void gcm128_finalize(
+        gcm128_ctx_t *ctx,
+        void *tag,
+        uint16_t tag_length_b);
+
+#endif /* GCM_GCM128_H_ */
diff --git a/mkfiles/gcm.mk b/mkfiles/gcm.mk
new file mode 100644 (file)
index 0000000..2421a38
--- /dev/null
@@ -0,0 +1,18 @@
+# Makefile for AES
+ALGO_NAME := GCM
+
+# comment out the following line for removement of AES from the build process
+BLOCK_CIPHERS += $(ALGO_NAME)
+
+$(ALGO_NAME)_DIR      := gcm/
+$(ALGO_NAME)_INCDIR   := memxor/ aes/ gf256mul/ bcal/
+$(ALGO_NAME)_OBJ      := gcm128.o \
+                         aes_enc-asm.o aes_dec-asm.o aes_sbox-asm.o aes_invsbox-asm.o  \
+                         aes_keyschedule-asm.o 
+$(ALGO_NAME)_TESTBIN  := main-gcm-test.o $(CLI_STD) $(BCAL_STD)  \
+                         bcal_aes128.o bcal_aes192.o bcal_aes256.o  \
+                         dump-asm.o dump-decl.o        \
+                         memxor.o
+$(ALGO_NAME)_NESSIE_TEST      := test nessie
+$(ALGO_NAME)_PERFORMANCE_TEST := performance
+
diff --git a/test_src/main-gcm-test.c b/test_src/main-gcm-test.c
new file mode 100644 (file)
index 0000000..9834c2e
--- /dev/null
@@ -0,0 +1,301 @@
+/* main-gcm-test.c */
+/*
+    This file is part of the AVR-Crypto-Lib.
+    Copyright (C) 2006-2014 Daniel Otte (daniel.otte@rub.de)
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+/*
+ * GCM test-suit
+ *
+*/
+
+
+#include "main-test-common.h"
+
+#include <gcm128.h>
+#include <bcal_aes128.h>
+#include <bcal_aes192.h>
+#include <bcal_aes256.h>
+#include "performance_test.h"
+
+char *algo_name = "GCM-AES128";
+
+/*****************************************************************************
+ *  additional validation-functions                                          *
+ *****************************************************************************/
+
+#define DUMP_LEN(x, len) do { printf("%s", "\n\n" #x ":"); \
+                    cli_hexdump_block((x), (len), 4, 16); } while (0)
+
+#define DUMP(x) DUMP_LEN(x, sizeof(x))
+
+#define elementsof(t) (sizeof(t) / sizeof(t[0]))
+
+/*****************************************************************************
+ *                                                                           *
+ *****************************************************************************/
+
+const uint8_t zero_block[] PROGMEM = {
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+const uint8_t key_b[] PROGMEM = {
+        0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c,
+        0x6d, 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08,
+        0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c,
+        0x6d, 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08
+};
+
+
+const uint8_t ad_block[] PROGMEM = {
+        0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef,
+        0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef,
+        0xab, 0xad, 0xda, 0xd2
+};
+
+const uint8_t pt_block[] PROGMEM = {
+        0xd9, 0x31, 0x32, 0x25, 0xf8, 0x84, 0x06, 0xe5,
+        0xa5, 0x59, 0x09, 0xc5, 0xaf, 0xf5, 0x26, 0x9a,
+        0x86, 0xa7, 0xa9, 0x53, 0x15, 0x34, 0xf7, 0xda,
+        0x2e, 0x4c, 0x30, 0x3d, 0x8a, 0x31, 0x8a, 0x72,
+        0x1c, 0x3c, 0x0c, 0x95, 0x95, 0x68, 0x09, 0x53,
+        0x2f, 0xcf, 0x0e, 0x24, 0x49, 0xa6, 0xb5, 0x25,
+        0xb1, 0x6a, 0xed, 0xf5, 0xaa, 0x0d, 0xe6, 0x57,
+        0xba, 0x63, 0x7b, 0x39, 0x1a, 0xaf, 0xd2, 0x55
+};
+
+/*****************************************************************************/
+
+const uint8_t iv_block[] PROGMEM = {
+        0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, 0xdb, 0xad,
+        0xde, 0xca, 0xf8, 0x88
+};
+
+/*****************************************************************************/
+
+const uint8_t iv_6[] PROGMEM = {
+        0x93, 0x13, 0x22, 0x5d, 0xf8, 0x84, 0x06, 0xe5,
+        0x55, 0x90, 0x9c, 0x5a, 0xff, 0x52, 0x69, 0xaa,
+        0x6a, 0x7a, 0x95, 0x38, 0x53, 0x4f, 0x7d, 0xa1,
+        0xe4, 0xc3, 0x03, 0xd2, 0xa3, 0x18, 0xa7, 0x28,
+        0xc3, 0xc0, 0xc9, 0x51, 0x56, 0x80, 0x95, 0x39,
+        0xfc, 0xf0, 0xe2, 0x42, 0x9a, 0x6b, 0x52, 0x54,
+        0x16, 0xae, 0xdb, 0xf5, 0xa0, 0xde, 0x6a, 0x57,
+        0xa6, 0x37, 0xb3, 0x9b
+};
+
+typedef struct {
+    const void *key;
+    uint16_t key_length_b;
+    const void *iv;
+    uint16_t iv_length_b;
+    const void *ad;
+    uint16_t ad_length_b;
+    const void *pt;
+    uint16_t pt_length_b;
+} test_t;
+
+const test_t test_set[] PROGMEM = {
+        { zero_block, 128, zero_block, 96, NULL, 0, NULL, 0 },
+        { zero_block, 128, zero_block, 96, NULL, 0, zero_block, 128 },
+        { key_b, 128, iv_block, 96, NULL, 0, pt_block, 512 },
+        { key_b, 128, iv_block, 96, ad_block, 160, pt_block, 480 },
+        { key_b, 128, iv_block, 64, ad_block, 160, pt_block, 480 },
+        { key_b, 128, iv_6, sizeof(iv_6) * 8, ad_block, 160, pt_block, 480 },
+
+        { zero_block, 192, zero_block, 96, NULL, 0, NULL, 0 },
+        { zero_block, 192, zero_block, 96, NULL, 0, zero_block, 128 },
+        { key_b, 192, iv_block, 96, NULL, 0, pt_block, 512 },
+        { key_b, 192, iv_block, 96, ad_block, 160, pt_block, 480 },
+        { key_b, 192, iv_block, 64, ad_block, 160, pt_block, 480 },
+        { key_b, 192, iv_6, sizeof(iv_6) * 8, ad_block, 160, pt_block, 480 },
+
+        { zero_block, 256, zero_block, 96, NULL, 0, NULL, 0 },
+        { zero_block, 256, zero_block, 96, NULL, 0, zero_block, 128 },
+        { key_b, 256, iv_block, 96, NULL, 0, pt_block, 512 },
+        { key_b, 256, iv_block, 96, ad_block, 160, pt_block, 480 },
+        { key_b, 256, iv_block, 64, ad_block, 160, pt_block, 480 },
+        { key_b, 256, iv_6, sizeof(iv_6) * 8, ad_block, 160, pt_block, 480 },
+};
+
+/*****************************************************************************
+ *                                                                           *
+ *****************************************************************************/
+
+
+int8_t gcm128_simple(
+        const void *key,
+        const void *iv,
+        uint16_t iv_length_b,
+        const void * ad,
+        uint16_t ad_length_b,
+        void *dest,
+        const void *src,
+        uint16_t src_length_b,
+        void *tag,
+        uint8_t tag_length_b)
+{
+    gcm128_ctx_t ctx;
+    DUMP_LEN(key, 16);
+    DUMP_LEN(iv, (iv_length_b + 7) / 8);
+    DUMP_LEN(ad, (ad_length_b + 7) / 8);
+    DUMP_LEN(src, (src_length_b + 7) / 8);
+    if (gcm128_init(&ctx, &aes128_desc, key, 128, iv, iv_length_b)) {
+        return -1;
+    }
+    gcm128_add_ad_final_block(&ctx, ad, ad_length_b);
+    gcm128_encrypt_final_block(&ctx, dest, src, src_length_b);
+    gcm128_finalize(&ctx, tag, tag_length_b);
+    if (dest) {
+        DUMP_LEN(dest, (src_length_b + 7) / 8);
+    }
+    DUMP_LEN(tag, (tag_length_b + 7) / 8);
+    return 0;
+}
+
+int8_t gcm128_simple_progmem(
+        const void *key_p,
+        uint16_t key_length_b,
+        const void *iv_p,
+        uint16_t iv_length_b,
+        const void * ad_p,
+        uint16_t ad_length_b,
+        const void *src_p,
+        uint16_t src_length_b,
+        void *tag,
+        uint8_t tag_length_b)
+{
+    int8_t r;
+    gcm128_ctx_t ctx;
+    const bcdesc_t *cipher;
+    switch (key_length_b) {
+    case 128: cipher = &aes128_desc; break;
+    case 192: cipher = &aes192_desc; break;
+    case 256: cipher = &aes256_desc; break;
+    default: return -1;
+    }
+    {
+        uint8_t key[key_length_b / 8];
+        uint8_t iv[(iv_length_b + 7) / 8];
+        memcpy_P(key, key_p, key_length_b / 8);
+        memcpy_P(iv, iv_p, (iv_length_b + 7) / 8);
+        if ((r = gcm128_init(&ctx, cipher, key, key_length_b, iv, iv_length_b))) {
+            printf_P(PSTR("DBG: (Oooops) Error: %"PRId8"\n"), r);
+            uart0_flush();
+            return -1;
+        }
+
+    }
+    uint8_t tmp[GCM128_BLOCK_BYTES];
+    while (ad_length_b >= GCM128_BLOCK_BITS) {
+        memcpy_P(tmp, ad_p, GCM128_BLOCK_BYTES);
+        ad_p = &((uint8_t*)ad_p)[GCM128_BLOCK_BYTES];
+        ad_length_b -= GCM128_BLOCK_BITS;
+        gcm128_add_ad_block(&ctx, tmp);
+    }
+    memcpy_P(tmp, ad_p, (ad_length_b + 7) / 8);
+    gcm128_add_ad_final_block(&ctx, tmp, ad_length_b);
+
+    while (src_length_b >= GCM128_BLOCK_BITS) {
+        memcpy_P(tmp, src_p, GCM128_BLOCK_BYTES);
+        src_p = &((uint8_t*)src_p)[GCM128_BLOCK_BYTES];
+        src_length_b -= GCM128_BLOCK_BITS;
+        gcm128_encrypt_block(&ctx, tmp, tmp);
+//        DUMP(tmp);
+    }
+    memcpy_P(tmp, src_p, (src_length_b + 7) / 8);
+    gcm128_encrypt_final_block(&ctx, tmp, tmp, src_length_b);
+    if (src_length_b > 0) {
+//        DUMP_LEN(tmp, (src_length_b + 7) / 8);
+    }
+
+    gcm128_finalize(&ctx, tag, tag_length_b);
+    return 0;
+}
+
+void testrun_gcm128(void)
+{
+    uint8_t key[16];
+    uint8_t iv[12];
+    uint8_t tag[16];
+    uint8_t plain[16];
+    uint8_t cipher[16];
+    memset(key, 0, sizeof(key));
+    memset(iv, 0, sizeof(iv));
+    memset(plain, 0, sizeof(plain));
+    gcm128_simple(key, iv, 96, NULL, 0, NULL, NULL, 0, tag, 128);
+    gcm128_simple(key, iv, 96, NULL, 0, cipher, plain, 128, tag, 128);
+}
+
+
+
+void testrun_gcm128_progmem(void)
+{
+    test_t t;
+    uint8_t tag[16];
+    uint8_t i;
+    for (i = 0; i < elementsof(test_set); ++i) {
+        printf_P(PSTR("== Test %"PRId8" ==\n"), i + 1);
+        uart0_flush();
+        memcpy_P(&t, &test_set[i], sizeof(t));
+        gcm128_simple_progmem(
+                t.key, t.key_length_b,
+                t.iv, t.iv_length_b,
+                t.ad, t.ad_length_b,
+                t.pt, t.pt_length_b,
+                tag, 128);
+        DUMP(tag);
+        puts("\n\n");
+        uart0_flush();
+    }
+}
+
+/*****************************************************************************
+ *  main                                                                     *
+ *****************************************************************************/
+
+const char nessie_str[]      PROGMEM = "nessie";
+const char test_str[]        PROGMEM = "test";
+const char ftest_str[]       PROGMEM = "ftest";
+const char gtest_str[]       PROGMEM = "gtest";
+const char performance_str[] PROGMEM = "performance";
+const char echo_str[]        PROGMEM = "echo";
+
+const cmdlist_entry_t cmdlist[] PROGMEM = {
+//    { nessie_str,      NULL, NULL },
+    { test_str,        NULL, testrun_gcm128_progmem},
+//    { ftest_str,       NULL, testrun_f32},
+//    { gtest_str,       NULL, testrun_g32},
+//    { performance_str, NULL, testrun_performance_arcfour},
+    { echo_str,    (void*)1, (void_fpt)echo_ctrl},
+    { NULL,            NULL, NULL}
+};
+
+int main(void) {
+    main_setup();
+
+    for(;;){
+        welcome_msg(algo_name);
+        cmd_interface(cmdlist);
+    }
+
+}
+
+