diff --git a/README.md b/README.md
index 7f4b19f2..cbfd75c7 100644
--- a/README.md
+++ b/README.md
@@ -24,7 +24,8 @@ Recent significant changes to the library
Apr 2018:
-* Acorn128 authenticated cipher (finalist in the CAESAR AEAD competition).
+* Acorn128 and Ascon128 authenticated ciphers (finalists in the CAESAR AEAD
+ competition in the light-weight category).
* Split the library into Crypto (core), CryptoLW (light-weight), and
CryptoLegacy (deprecated algorithms).
* Tiny and small versions of AES for reducing memory requirements.
diff --git a/doc/crypto.dox b/doc/crypto.dox
index fa8fb5db..a8286a71 100644
--- a/doc/crypto.dox
+++ b/doc/crypto.dox
@@ -59,7 +59,7 @@ The algorithms in the "libraries/CryptoLW" directory are new algorithms
that have been designed for "light-weight" environments where memory and
CPU resources are constrained:
-\li Authenticated encryption with associated data (AEAD): Acorn128
+\li Authenticated encryption with associated data (AEAD): Acorn128, Ascon128
\li Block ciphers: Speck, SpeckSmall, SpeckTiny
These algorithms are fairly new, but they are ideal for Arduino devices.
@@ -165,6 +165,7 @@ Ardunino Mega 2560 running at 16 MHz are similar:
EAX<Speck> (256-bit key) | 25.89us | 25.88us | 690.63us | 362 |
EAX<SpeckTiny> (256-bit key) | 78.20us | 78.20us | 1269.19us | 122 |
Acorn128 | 20.39us | 20.06us | 4817.82us | 60 |
+Ascon128 | 42.71us | 43.07us | 738.68us | 60 |
|
Hash Algorithm | Hashing (per byte) | Finalization | | State Size (bytes) |
SHA256 | 43.85us | 2841.04us | | 107 |
@@ -253,6 +254,7 @@ All figures are for the Arduino Due running at 84 MHz:
EAX<Speck> (256-bit key) | 2.80us | 2.80us | 81.63us | 384 |
EAX<SpeckTiny> (256-bit key) | 6.69us | 6.69us | 110.91us | 144 |
Acorn128 | 0.75us | 0.75us | 175.70us | 64 |
+Ascon128 | 3.52us | 3.50us | 51.67us | 72 |
|
Hash Algorithm | Hashing (per byte) | Finalization | | State Size (bytes) |
SHA256 | 1.15us | 76.60us | | 120 |
diff --git a/gen/genascon.c b/gen/genascon.c
new file mode 100644
index 00000000..c9d741eb
--- /dev/null
+++ b/gen/genascon.c
@@ -0,0 +1,520 @@
+/*
+ * Copyright (C) 2018 Southern Storm Software, Pty Ltd.
+ *
+ * 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.
+ */
+
+// Special-purpose compiler that generates the AVR version of Ascon128.
+
+#include
+#include
+#include
+
+static int indent = 4;
+
+static const int temp_reg = 0; // Register number for the AVR "__tmp_reg__".
+
+// Registers that can be used for temporary values, in the best
+// order to allocate them. High registers are listed first.
+static int regs[] = {
+ 16, 17, 18, 19, 20, 21, 22, 23, 7, 8, 9, 10, 11, 12, 13, 14, 15
+};
+#define num_regs (sizeof(regs) / sizeof(regs[0]))
+
+// Which registers are currently in use?
+static int reg_in_use[num_regs] = {0};
+
+// Which registers did we use while generating the code?
+static int reg_used[num_regs] = {0};
+
+// Information about a set of registers storing a 64-bit quantity.
+typedef struct
+{
+ int first; // First register in the set for 8-bit rotates.
+ int reg[8]; // Register numbers.
+
+} Reg64;
+
+// Indent the code and print a string.
+void indent_printf(const char *format, ...)
+{
+ va_list va;
+ int posn;
+ va_start(va, format);
+ for (posn = 0; posn < indent; ++posn)
+ putc(' ', stdout);
+ vfprintf(stdout, format, va);
+ va_end(va);
+}
+
+// Print an assembler instruction within quotes.
+void insn_printf(const char *format, ...)
+{
+ va_list va;
+ int posn;
+ va_start(va, format);
+ for (posn = 0; posn < indent; ++posn)
+ putc(' ', stdout);
+ putc('"', stdout);
+ vfprintf(stdout, format, va);
+ putc('\\', stdout);
+ putc('n', stdout);
+ putc('"', stdout);
+ putc('\n', stdout);
+ va_end(va);
+}
+
+// Allocate an unused register, starting with high registers.
+void alloc_high_reg(int *reg)
+{
+ unsigned index;
+ if (*reg != -1) {
+ fprintf(stderr, "Temporary register wasn't previously released\n");
+ exit(1);
+ }
+ for (index = 0; index < num_regs; ++index) {
+ if (!reg_in_use[index]) {
+ reg_in_use[index] = 1;
+ reg_used[index] = 1;
+ *reg = regs[index];
+ if (*reg < 16) {
+ fprintf(stderr, "Ran out of temporary high registers\n");
+ exit(1);
+ }
+ return;
+ }
+ }
+ fprintf(stderr, "Ran out of temporary registers\n");
+ exit(1);
+}
+
+// Allocate an unused register, starting with low registers
+// because we know we won't need the value in a high reg later.
+static void alloc_low_reg(int *reg)
+{
+ unsigned index;
+ if (*reg != -1) {
+ fprintf(stderr, "Temporary register wasn't previously released\n");
+ exit(1);
+ }
+ for (index = num_regs; index > 0; --index) {
+ if (!reg_in_use[index - 1]) {
+ reg_in_use[index - 1] = 1;
+ reg_used[index - 1] = 1;
+ *reg = regs[index - 1];
+ return;
+ }
+ }
+ fprintf(stderr, "Ran out of temporary registers\n");
+ exit(1);
+}
+
+// Release a register back to the allocation pool.
+static void release_reg(int *reg)
+{
+ unsigned index;
+ for (index = 0; index < num_regs; ++index) {
+ if (regs[index] == *reg && reg_in_use[index]) {
+ reg_in_use[index] = 0;
+ *reg = -1;
+ return;
+ }
+ }
+ fprintf(stderr, "Released a register that was not in use\n");
+ exit(1);
+}
+
+// Check that we have a high register when we need one.
+void check_high_reg(int *reg)
+{
+ if ((*reg) < 16) {
+ fprintf(stderr, "r%d is not a high register\n", *reg);
+ exit(1);
+ }
+}
+
+// Check that all temporary registers have been released.
+static void check_regs(void)
+{
+ unsigned index;
+ for (index = 0; index < num_regs; ++index) {
+ if (reg_in_use[index]) {
+ fprintf(stderr, "Register r%d has not been released\n",
+ regs[index]);
+ exit(1);
+ }
+ }
+}
+
+// Allocate a 64-bit register set.
+static void alloc_reg64(Reg64 *reg)
+{
+ reg->first = 0;
+ reg->reg[0] = -1;
+ reg->reg[1] = -1;
+ reg->reg[2] = -1;
+ reg->reg[3] = -1;
+ reg->reg[4] = -1;
+ reg->reg[5] = -1;
+ reg->reg[6] = -1;
+ reg->reg[7] = -1;
+ alloc_low_reg(&(reg->reg[0]));
+ alloc_low_reg(&(reg->reg[1]));
+ alloc_low_reg(&(reg->reg[2]));
+ alloc_low_reg(&(reg->reg[3]));
+ alloc_low_reg(&(reg->reg[4]));
+ alloc_low_reg(&(reg->reg[5]));
+ alloc_low_reg(&(reg->reg[6]));
+ alloc_low_reg(&(reg->reg[7]));
+}
+
+// Release a 64-bit register set.
+static void release_reg64(Reg64 *reg)
+{
+ release_reg(&(reg->reg[0]));
+ release_reg(&(reg->reg[1]));
+ release_reg(&(reg->reg[2]));
+ release_reg(&(reg->reg[3]));
+ release_reg(&(reg->reg[4]));
+ release_reg(&(reg->reg[5]));
+ release_reg(&(reg->reg[6]));
+ release_reg(&(reg->reg[7]));
+}
+
+#define REGn(r, n) ((r)->reg[((n) + (r)->first) % 8])
+
+// Rotate a 64-bit register left by 1 bit.
+static void rotate_reg64_left_1(Reg64 *reg)
+{
+ insn_printf("lsl r%d", REGn(reg, 0));
+ insn_printf("rol r%d", REGn(reg, 1));
+ insn_printf("rol r%d", REGn(reg, 2));
+ insn_printf("rol r%d", REGn(reg, 3));
+ insn_printf("rol r%d", REGn(reg, 4));
+ insn_printf("rol r%d", REGn(reg, 5));
+ insn_printf("rol r%d", REGn(reg, 6));
+ insn_printf("rol r%d", REGn(reg, 7));
+ insn_printf("adc r%d,__zero_reg__", REGn(reg, 0));
+}
+
+// Rotate a 64-bit register left by 8 bits.
+static void rotate_reg64_left_8(Reg64 *reg)
+{
+ reg->first = (reg->first + 7) % 8;
+}
+
+// Rotate a 64-bit register right by 1 bit.
+static void rotate_reg64_right_1(Reg64 *reg)
+{
+ insn_printf("bst r%d,0", REGn(reg, 0));
+ insn_printf("ror r%d", REGn(reg, 7));
+ insn_printf("ror r%d", REGn(reg, 6));
+ insn_printf("ror r%d", REGn(reg, 5));
+ insn_printf("ror r%d", REGn(reg, 4));
+ insn_printf("ror r%d", REGn(reg, 3));
+ insn_printf("ror r%d", REGn(reg, 2));
+ insn_printf("ror r%d", REGn(reg, 1));
+ insn_printf("ror r%d", REGn(reg, 0));
+ insn_printf("bld r%d,7", REGn(reg, 7));
+}
+
+// Rotate a 64-bit register right by 8 bits.
+static void rotate_reg64_right_8(Reg64 *reg)
+{
+ reg->first = (reg->first + 1) % 8;
+}
+
+// Rotate a 64-bit register right by a number of bits.
+static void rotate_reg64_right(Reg64 *reg, int bits)
+{
+ int bytes;
+ if ((bits % 8) < 4) {
+ // Rotate in the right direction.
+ bytes = bits / 8;
+ while (bytes > 0) {
+ rotate_reg64_right_8(reg);
+ --bytes;
+ }
+ bits %= 8;
+ while (bits > 0) {
+ rotate_reg64_right_1(reg);
+ --bits;
+ }
+ } else {
+ // Quicker to rotate in the left direction.
+ bits = 64 - bits;
+ bytes = bits / 8;
+ while (bytes > 0) {
+ rotate_reg64_left_8(reg);
+ --bytes;
+ }
+ bits %= 8;
+ while (bits > 0) {
+ rotate_reg64_left_1(reg);
+ --bits;
+ }
+ }
+}
+
+// Load a 64-bit register from a Z pointer offset.
+static void load_reg64(Reg64 *reg, int offset)
+{
+ int index;
+ for (index = 0; index < 8; ++index, ++offset) {
+ if (offset != 0)
+ insn_printf("ldd r%d,Z+%d", REGn(reg, index), offset);
+ else
+ insn_printf("ld r%d,Z", REGn(reg, index));
+ }
+}
+
+// Store a 64-bit register to a Z pointer offset.
+static void store_reg64(Reg64 *reg, int offset)
+{
+ int index;
+ for (index = 0; index < 8; ++index, ++offset) {
+ if (offset != 0)
+ insn_printf("std Z+%d,r%d", offset, REGn(reg, index));
+ else
+ insn_printf("st Z,r%d", REGn(reg, index));
+ }
+}
+
+// Copy one 64-bit register into another.
+static void copy_reg64(Reg64 *dst, Reg64 *src)
+{
+ int index;
+ for (index = 0; index < 8; ++index)
+ insn_printf("mov r%d,r%d", REGn(dst, index), REGn(src, index));
+}
+
+// XOR two 64-bit registers.
+static void xor_reg64(Reg64 *dst, Reg64 *src)
+{
+ int index;
+ for (index = 0; index < 8; ++index)
+ insn_printf("eor r%d,r%d", REGn(dst, index), REGn(src, index));
+}
+
+// Print the names of the temporary registers that we used.
+static void temp_regs(void)
+{
+ unsigned index;
+ int first = 1;
+ indent_printf(": ");
+ for (index = 0; index < num_regs; ++index) {
+ if (reg_used[index]) {
+ if (first) {
+ first = 0;
+ printf("\"r%d\"", regs[index]);
+ } else {
+ printf(", \"r%d\"", regs[index]);
+ }
+ }
+ }
+ printf(", \"memory\"\n");
+}
+
+// Handle one byte within the substitution layer.
+static void substitute(int offset, int reg)
+{
+ int x0 = -1;
+ int x1 = -1;
+ int x2 = reg;
+ int x3 = -1;
+ int x4 = -1;
+ int t0 = -1;
+ int t1 = -1;
+ int t2 = -1;
+ int t3 = -1;
+ int t4 = -1;
+
+ // Allocate five temporary registers for the five words.
+ // When processing the zero byte, we already have x2 loaded.
+ alloc_low_reg(&x0);
+ alloc_low_reg(&x1);
+ if (reg == -1)
+ alloc_low_reg(&x2);
+ alloc_low_reg(&x3);
+ alloc_low_reg(&x4);
+
+ // Need five more registers for intermediate results.
+ alloc_low_reg(&t0);
+ alloc_low_reg(&t1);
+ alloc_low_reg(&t2);
+ alloc_low_reg(&t3);
+ alloc_low_reg(&t4);
+
+ // Read the five bytes from the state.
+ if (offset != 0)
+ insn_printf("ldd r%d,Z+%d", x0, offset);
+ else
+ insn_printf("ld r%d,Z", x0);
+ insn_printf("ldd r%d,Z+%d", x1, 8 + offset);
+ if (reg == -1)
+ insn_printf("ldd r%d,Z+%d", x2, 16 + offset);
+ insn_printf("ldd r%d,Z+%d", x3, 24 + offset);
+ insn_printf("ldd r%d,Z+%d", x4, 32 + offset);
+
+ // Mix the bytes together.
+ // x0 ^= x4; x4 ^= x3; x2 ^= x1;
+ // t0 = ~x0; t1 = ~x1; t2 = ~x2; t3 = ~x3; t4 = ~x4;
+ // t0 &= x1; t1 &= x2; t2 &= x3; t3 &= x4; t4 &= x0;
+ // x0 ^= t1; x1 ^= t2; x2 ^= t3; x3 ^= t4; x4 ^= t0;
+ // x1 ^= x0; x0 ^= x4; x3 ^= x2; x2 = ~x2;
+ insn_printf("eor r%d,r%d", x0, x4);
+ insn_printf("eor r%d,r%d", x4, x3);
+ insn_printf("eor r%d,r%d", x2, x1);
+ insn_printf("mov r%d,r%d", t0, x0);
+ insn_printf("com r%d", t0);
+ insn_printf("and r%d,r%d", t0, x1);
+ insn_printf("mov r%d,r%d", t1, x1);
+ insn_printf("com r%d", t1);
+ insn_printf("and r%d,r%d", t1, x2);
+ insn_printf("mov r%d,r%d", t2, x2);
+ insn_printf("com r%d", t2);
+ insn_printf("and r%d,r%d", t2, x3);
+ insn_printf("mov r%d,r%d", t3, x3);
+ insn_printf("com r%d", t3);
+ insn_printf("and r%d,r%d", t3, x4);
+ insn_printf("mov r%d,r%d", t4, x4);
+ insn_printf("com r%d", t4);
+ insn_printf("and r%d,r%d", t4, x0);
+ insn_printf("eor r%d,r%d", x0, t1);
+ insn_printf("eor r%d,r%d", x1, t2);
+ insn_printf("eor r%d,r%d", x2, t3);
+ insn_printf("eor r%d,r%d", x3, t4);
+ insn_printf("eor r%d,r%d", x4, t0);
+ insn_printf("eor r%d,r%d", x1, x0);
+ insn_printf("eor r%d,r%d", x0, x4);
+ insn_printf("eor r%d,r%d", x3, x2);
+ insn_printf("com r%d", x2);
+
+ // Write the five bytes back to the state.
+ if (offset != 0)
+ insn_printf("std Z+%d,r%d", offset, x0);
+ else
+ insn_printf("st Z,r%d", x0);
+ insn_printf("std Z+%d,r%d", 8 + offset, x1);
+ insn_printf("std Z+%d,r%d", 16 + offset, x2);
+ insn_printf("std Z+%d,r%d", 24 + offset, x3);
+ insn_printf("std Z+%d,r%d", 32 + offset, x4);
+
+ // Release the temporary registers.
+ release_reg(&x0);
+ release_reg(&x1);
+ if (reg == -1)
+ release_reg(&x2);
+ release_reg(&x3);
+ release_reg(&x4);
+ release_reg(&t0);
+ release_reg(&t1);
+ release_reg(&t2);
+ release_reg(&t3);
+ release_reg(&t4);
+}
+
+static void diffuse(int n, int shift1, int shift2)
+{
+ Reg64 x, t;
+ int offset = n * 8;
+
+ // Allocate the temporary registers that we need.
+ alloc_reg64(&x);
+ alloc_reg64(&t);
+
+ // Read the 64-bit word into the x registers.
+ load_reg64(&x, offset);
+
+ // Rotate step 1: t = x >>> shift1, t ^= x
+ copy_reg64(&t, &x);
+ rotate_reg64_right(&t, shift1);
+ xor_reg64(&t, &x);
+
+ // Rotate step 2: x = x >>> shift2, x ^= t
+ rotate_reg64_right(&x, shift2);
+ xor_reg64(&x, &t);
+
+ // Store the 64-bit result back to the state.
+ store_reg64(&x, offset);
+
+ // Release the temporary registers.
+ release_reg64(&x);
+ release_reg64(&t);
+}
+
+static void permute(void)
+{
+ int reg = -1;
+
+ // Print the function header.
+ printf("void Ascon128::permute(uint8_t first)\n");
+ printf("{\n");
+ indent_printf("// AVR version generated by the genascon tool.\n");
+ indent_printf("__asm__ __volatile__ (\n");
+ indent += 4;
+
+ // Output the top of the loop.
+ insn_printf("1:");
+
+ // XOR the low byte of x2 with the round constant.
+ alloc_low_reg(®);
+ insn_printf("ldd r%d,Z+16", reg);
+ insn_printf("eor r%d,%%1", reg);
+
+ // Substitution layer. Mix the words one byte at a time.
+ // For the first byte we already have x2 loaded into "reg".
+ substitute(0, reg);
+ release_reg(®);
+ substitute(1, -1);
+ substitute(2, -1);
+ substitute(3, -1);
+ substitute(4, -1);
+ substitute(5, -1);
+ substitute(6, -1);
+ substitute(7, -1);
+
+ // Linear diffusion layer.
+ diffuse(0, 19, 28);
+ diffuse(1, 61, 39);
+ diffuse(2, 1, 6);
+ diffuse(3, 10, 17);
+ diffuse(4, 7, 41);
+
+ // Output the bottom of the loop.
+ insn_printf("subi %%1,0x0F");
+ insn_printf("cpi %%1,0x3C");
+ insn_printf("breq 2f");
+ insn_printf("rjmp 1b");
+ insn_printf("2:");
+
+ // Declare the registers that we need.
+ indent_printf(":: \"z\"(state.S), \"d\"((uint8_t)(0xF0 - (first << 4) + first))\n");
+ temp_regs();
+ indent -= 4;
+ indent_printf(");\n");
+ printf("}\n\n");
+ check_regs();
+}
+
+int main(int argc, char *argv[])
+{
+ permute();
+ return 0;
+}
diff --git a/host/Crypto/Makefile b/host/Crypto/Makefile
index e854c64b..661131c0 100644
--- a/host/Crypto/Makefile
+++ b/host/Crypto/Makefile
@@ -37,6 +37,7 @@ SOURCES = \
AES192.cpp \
AES256.cpp \
AESCommon.cpp \
+ Ascon128.cpp \
AuthenticatedCipher.cpp \
BigNumberUtil.cpp \
BLAKE2b.cpp \
@@ -79,6 +80,7 @@ SKETCHES = \
TestAES/TestAES.ino \
TestAESTiny/TestAESTiny.ino \
TestAESSmall/TestAESSmall.ino \
+ TestAscon/TestAscon.ino \
TestBigNumberUtil/TestBigNumberUtil.ino \
TestBLAKE2b/TestBLAKE2b.ino \
TestBLAKE2s/TestBLAKE2s.ino \
diff --git a/libraries/CryptoLW/examples/TestAscon/TestAscon.ino b/libraries/CryptoLW/examples/TestAscon/TestAscon.ino
new file mode 100644
index 00000000..533cfb02
--- /dev/null
+++ b/libraries/CryptoLW/examples/TestAscon/TestAscon.ino
@@ -0,0 +1,403 @@
+/*
+ * Copyright (C) 2018 Southern Storm Software, Pty Ltd.
+ *
+ * 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.
+ */
+
+/*
+This example runs tests on the Ascon128 implementation to verify
+correct behaviour.
+*/
+
+#include
+#include
+#include
+#include "utility/ProgMemUtil.h"
+
+#define MAX_PLAINTEXT_LEN 43
+#define MAX_AUTHDATA_LEN 17
+
+struct TestVector
+{
+ const char *name;
+ uint8_t key[16];
+ uint8_t plaintext[MAX_PLAINTEXT_LEN];
+ uint8_t ciphertext[MAX_PLAINTEXT_LEN];
+ uint8_t authdata[MAX_AUTHDATA_LEN];
+ uint8_t iv[16];
+ uint8_t tag[16];
+ size_t authsize;
+ size_t datasize;
+};
+
+// Test vectors for Ascon128, generated with the reference Python version:
+// https://github.com/meichlseder/pyascon
+static TestVector const testVectorAscon128_1 PROGMEM = {
+ .name = "Ascon128 #1",
+ .key = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ .plaintext = {0x61, 0x73, 0x63, 0x6f, 0x6e},
+ .ciphertext = {0x86, 0x88, 0x62, 0x14, 0x0e},
+ .authdata = {0x41, 0x53, 0x43, 0x4f, 0x4e},
+ .iv = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ .tag = {0xad, 0x65, 0xf5, 0x94, 0x22, 0x58, 0xda, 0xd5,
+ 0x3c, 0xaa, 0x7a, 0x56, 0xf3, 0xa2, 0x92, 0xd8},
+ .authsize = 5,
+ .datasize = 5
+};
+static TestVector const testVectorAscon128_2 PROGMEM = {
+ .name = "Ascon128 #2",
+ .key = {0x0d, 0x49, 0x29, 0x92, 0x65, 0x8b, 0xd8, 0xa3,
+ 0xe4, 0x7b, 0xf9, 0x10, 0xd4, 0xc5, 0x87, 0xad},
+ .plaintext = {0x61},
+ .ciphertext = {0xc5},
+ .authdata = {0},
+ .iv = {0x5a, 0xcb, 0x17, 0x2a, 0x1a, 0x93, 0x3d, 0xb1,
+ 0x8a, 0x6a, 0x40, 0xac, 0x6e, 0x4c, 0x68, 0xd0},
+ .tag = {0x2e, 0x0b, 0xf2, 0xb1, 0xfc, 0xd8, 0x64, 0x69,
+ 0x01, 0x1c, 0x4f, 0x8b, 0x78, 0x4a, 0x65, 0x0d},
+ .authsize = 0,
+ .datasize = 1
+};
+static TestVector const testVectorAscon128_3 PROGMEM = {
+ .name = "Ascon128 #3",
+ .key = {0x91, 0xb3, 0x9d, 0x22, 0xf3, 0xb7, 0x7f, 0x51,
+ 0x33, 0x0a, 0xa3, 0xa4, 0xea, 0x38, 0xea, 0xa2},
+ .plaintext = {0},
+ .ciphertext = {0},
+ .authdata = {0x64},
+ .iv = {0x2e, 0xec, 0x64, 0x25, 0xb3, 0xec, 0xf0, 0x63,
+ 0xb4, 0x3e, 0x29, 0xc7, 0x68, 0x29, 0x3c, 0x49},
+ .tag = {0xfd, 0x24, 0x0e, 0x3c, 0x3d, 0xc4, 0x11, 0x0d,
+ 0xe1, 0x54, 0x4c, 0xd5, 0x24, 0x18, 0xd9, 0x4c},
+ .authsize = 1,
+ .datasize = 0
+};
+static TestVector const testVectorAscon128_4 PROGMEM = {
+ .name = "Ascon128 #4",
+ .key = {0x72, 0xfd, 0x18, 0xde, 0xbd, 0xee, 0x86, 0x13,
+ 0x4f, 0x7c, 0x44, 0x29, 0x84, 0x37, 0x56, 0x06},
+ .plaintext = {0x70, 0x6c, 0x61, 0x69, 0x6e, 0x74, 0x78, 0x74},
+ .ciphertext = {0x91, 0xd0, 0xc3, 0x88, 0xea, 0xc0, 0xe6, 0xd9},
+ .authdata = {0x61, 0x73, 0x73, 0x64, 0x61, 0x74, 0x31, 0x32},
+ .iv = {0x91, 0x5f, 0xf8, 0xff, 0xca, 0xd8, 0xae, 0x1d,
+ 0xf4, 0x45, 0xeb, 0x03, 0xe2, 0x18, 0xfd, 0x25},
+ .tag = {0x16, 0x69, 0x74, 0xbf, 0xbd, 0x43, 0xd7, 0xa8,
+ 0xfe, 0x43, 0xf0, 0xce, 0xe2, 0xdd, 0xb9, 0xf8},
+ .authsize = 8,
+ .datasize = 8
+};
+static TestVector const testVectorAscon128_5 PROGMEM = {
+ .name = "Ascon128 #5",
+ .key = {0x8a, 0xa5, 0xed, 0xc5, 0x88, 0x49, 0x75, 0xc8,
+ 0xd1, 0xa1, 0xb8, 0x44, 0xd0, 0x15, 0x50, 0x5a},
+ .plaintext = {0x54, 0x68, 0x65, 0x20, 0x72, 0x61, 0x69, 0x6e,
+ 0x20, 0x69, 0x6e, 0x20, 0x73, 0x70, 0x61, 0x69,
+ 0x6e, 0x20, 0x66, 0x61, 0x6c, 0x6c, 0x73, 0x20,
+ 0x6d, 0x61, 0x69, 0x6e, 0x6c, 0x79, 0x20, 0x6f,
+ 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x6c,
+ 0x61, 0x69, 0x6e},
+ .ciphertext = {0x4a, 0xb4, 0xe2, 0x87, 0x90, 0x07, 0x4b, 0x78,
+ 0x88, 0x70, 0x71, 0xc0, 0x62, 0xd6, 0xab, 0x6b,
+ 0x32, 0xd4, 0xb1, 0xec, 0xc7, 0xd8, 0x44, 0x93,
+ 0x36, 0x9a, 0x38, 0x81, 0xd6, 0x65, 0x2f, 0x85,
+ 0xaa, 0xf9, 0x70, 0x90, 0x61, 0x97, 0x3e, 0x1f,
+ 0x60, 0x12, 0x66},
+ .authdata = {0x48, 0x6f, 0x77, 0x20, 0x6e, 0x6f, 0x77, 0x20,
+ 0x62, 0x72, 0x6f, 0x77, 0x6e, 0x20, 0x63, 0x6f,
+ 0x77},
+ .iv = {0xbc, 0x52, 0x27, 0xa5, 0x72, 0x58, 0xfe, 0x00,
+ 0xcb, 0x7b, 0x0f, 0x31, 0xa4, 0xb6, 0xff, 0xda},
+ .tag = {0x92, 0xfe, 0x72, 0xf8, 0x69, 0xc9, 0x95, 0x41,
+ 0x1f, 0xc4, 0x57, 0xde, 0xa6, 0xf2, 0xf9, 0x2d},
+ .authsize = 17,
+ .datasize = 43
+};
+
+TestVector testVector;
+
+Ascon128 acorn;
+
+byte buffer[128];
+
+bool testCipher_N(Ascon128 *cipher, const struct TestVector *test, size_t inc)
+{
+ size_t posn, len;
+ uint8_t tag[16];
+
+ if (!inc)
+ inc = 1;
+
+ cipher->clear();
+ if (!cipher->setKey(test->key, 16)) {
+ Serial.print("setKey ");
+ return false;
+ }
+ if (!cipher->setIV(test->iv, 16)) {
+ Serial.print("setIV ");
+ return false;
+ }
+
+ memset(buffer, 0xBA, sizeof(buffer));
+
+ for (posn = 0; posn < test->authsize; posn += inc) {
+ len = test->authsize - posn;
+ if (len > inc)
+ len = inc;
+ cipher->addAuthData(test->authdata + posn, len);
+ }
+
+ for (posn = 0; posn < test->datasize; posn += inc) {
+ len = test->datasize - posn;
+ if (len > inc)
+ len = inc;
+ cipher->encrypt(buffer + posn, test->plaintext + posn, len);
+ }
+
+ if (memcmp(buffer, test->ciphertext, test->datasize) != 0) {
+ Serial.print(buffer[0], HEX);
+ Serial.print("->");
+ Serial.print(test->ciphertext[0], HEX);
+ return false;
+ }
+
+ cipher->computeTag(tag, sizeof(tag));
+ if (memcmp(tag, test->tag, sizeof(tag)) != 0) {
+ Serial.print("computed wrong tag ... ");
+ return false;
+ }
+
+ cipher->setKey(test->key, 16);
+ cipher->setIV(test->iv, 16);
+
+ for (posn = 0; posn < test->authsize; posn += inc) {
+ len = test->authsize - posn;
+ if (len > inc)
+ len = inc;
+ cipher->addAuthData(test->authdata + posn, len);
+ }
+
+ for (posn = 0; posn < test->datasize; posn += inc) {
+ len = test->datasize - posn;
+ if (len > inc)
+ len = inc;
+ cipher->decrypt(buffer + posn, test->ciphertext + posn, len);
+ }
+
+ if (memcmp(buffer, test->plaintext, test->datasize) != 0)
+ return false;
+
+ if (!cipher->checkTag(tag, sizeof(tag))) {
+ Serial.print("tag did not check ... ");
+ return false;
+ }
+
+ return true;
+}
+
+void testCipher(Ascon128 *cipher, const struct TestVector *test)
+{
+ bool ok;
+
+ memcpy_P(&testVector, test, sizeof(TestVector));
+ test = &testVector;
+
+ Serial.print(test->name);
+ Serial.print(" ... ");
+
+ ok = testCipher_N(cipher, test, test->datasize);
+ ok &= testCipher_N(cipher, test, 1);
+ ok &= testCipher_N(cipher, test, 2);
+ ok &= testCipher_N(cipher, test, 5);
+ ok &= testCipher_N(cipher, test, 8);
+ ok &= testCipher_N(cipher, test, 13);
+ ok &= testCipher_N(cipher, test, 16);
+
+ if (ok)
+ Serial.println("Passed");
+ else
+ Serial.println("Failed");
+}
+
+void perfCipherSetKey(Ascon128 *cipher, const struct TestVector *test)
+{
+ unsigned long start;
+ unsigned long elapsed;
+ int count;
+
+ memcpy_P(&testVector, test, sizeof(TestVector));
+ test = &testVector;
+
+ Serial.print(test->name);
+ Serial.print(" SetKey ... ");
+
+ start = micros();
+ for (count = 0; count < 1000; ++count) {
+ cipher->setKey(test->key, 16);
+ cipher->setIV(test->iv, 16);
+ }
+ elapsed = micros() - start;
+
+ Serial.print(elapsed / 1000.0);
+ Serial.print("us per operation, ");
+ Serial.print((1000.0 * 1000000.0) / elapsed);
+ Serial.println(" per second");
+}
+
+void perfCipherEncrypt(Ascon128 *cipher, const struct TestVector *test)
+{
+ unsigned long start;
+ unsigned long elapsed;
+ int count;
+
+ memcpy_P(&testVector, test, sizeof(TestVector));
+ test = &testVector;
+
+ Serial.print(test->name);
+ Serial.print(" Encrypt ... ");
+
+ cipher->setKey(test->key, 16);
+ cipher->setIV(test->iv, 16);
+ start = micros();
+ for (count = 0; count < 500; ++count) {
+ cipher->encrypt(buffer, buffer, 128);
+ }
+ elapsed = micros() - start;
+
+ Serial.print(elapsed / (128.0 * 500.0));
+ Serial.print("us per byte, ");
+ Serial.print((128.0 * 500.0 * 1000000.0) / elapsed);
+ Serial.println(" bytes per second");
+}
+
+void perfCipherDecrypt(Ascon128 *cipher, const struct TestVector *test)
+{
+ unsigned long start;
+ unsigned long elapsed;
+ int count;
+
+ memcpy_P(&testVector, test, sizeof(TestVector));
+ test = &testVector;
+
+ Serial.print(test->name);
+ Serial.print(" Decrypt ... ");
+
+ cipher->setKey(test->key, 16);
+ cipher->setIV(test->iv, 16);
+ start = micros();
+ for (count = 0; count < 500; ++count) {
+ cipher->decrypt(buffer, buffer, 128);
+ }
+ elapsed = micros() - start;
+
+ Serial.print(elapsed / (128.0 * 500.0));
+ Serial.print("us per byte, ");
+ Serial.print((128.0 * 500.0 * 1000000.0) / elapsed);
+ Serial.println(" bytes per second");
+}
+
+void perfCipherAddAuthData(Ascon128 *cipher, const struct TestVector *test)
+{
+ unsigned long start;
+ unsigned long elapsed;
+ int count;
+
+ memcpy_P(&testVector, test, sizeof(TestVector));
+ test = &testVector;
+
+ Serial.print(test->name);
+ Serial.print(" AddAuthData ... ");
+
+ cipher->setKey(test->key, 16);
+ cipher->setIV(test->iv, 16);
+ start = micros();
+ memset(buffer, 0xBA, 128);
+ for (count = 0; count < 500; ++count) {
+ cipher->addAuthData(buffer, 128);
+ }
+ elapsed = micros() - start;
+
+ Serial.print(elapsed / (128.0 * 500.0));
+ Serial.print("us per byte, ");
+ Serial.print((128.0 * 500.0 * 1000000.0) / elapsed);
+ Serial.println(" bytes per second");
+}
+
+void perfCipherComputeTag(Ascon128 *cipher, const struct TestVector *test)
+{
+ unsigned long start;
+ unsigned long elapsed;
+ int count;
+
+ memcpy_P(&testVector, test, sizeof(TestVector));
+ test = &testVector;
+
+ Serial.print(test->name);
+ Serial.print(" ComputeTag ... ");
+
+ cipher->setKey(test->key, 16);
+ cipher->setIV(test->iv, 16);
+ start = micros();
+ for (count = 0; count < 1000; ++count) {
+ cipher->computeTag(buffer, 16);
+ }
+ elapsed = micros() - start;
+
+ Serial.print(elapsed / 1000.0);
+ Serial.print("us per operation, ");
+ Serial.print((1000.0 * 1000000.0) / elapsed);
+ Serial.println(" per second");
+}
+
+void perfCipher(Ascon128 *cipher, const struct TestVector *test)
+{
+ perfCipherSetKey(cipher, test);
+ perfCipherEncrypt(cipher, test);
+ perfCipherDecrypt(cipher, test);
+ perfCipherAddAuthData(cipher, test);
+ perfCipherComputeTag(cipher, test);
+}
+
+void setup()
+{
+ Serial.begin(9600);
+
+ Serial.println();
+
+ Serial.print("State Size ... ");
+ Serial.println(sizeof(Ascon128));
+ Serial.println();
+
+ Serial.println("Test Vectors:");
+ testCipher(&acorn, &testVectorAscon128_1);
+ testCipher(&acorn, &testVectorAscon128_2);
+ testCipher(&acorn, &testVectorAscon128_3);
+ testCipher(&acorn, &testVectorAscon128_4);
+ testCipher(&acorn, &testVectorAscon128_5);
+
+ Serial.println();
+
+ Serial.println("Performance Tests:");
+ perfCipher(&acorn, &testVectorAscon128_4);
+}
+
+void loop()
+{
+}
diff --git a/libraries/CryptoLW/src/Ascon128.cpp b/libraries/CryptoLW/src/Ascon128.cpp
new file mode 100644
index 00000000..9ea09150
--- /dev/null
+++ b/libraries/CryptoLW/src/Ascon128.cpp
@@ -0,0 +1,356 @@
+/*
+ * Copyright (C) 2018 Southern Storm Software, Pty Ltd.
+ *
+ * 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 "Ascon128.h"
+#include "Crypto.h"
+#include "utility/EndianUtil.h"
+#include "utility/RotateUtil.h"
+#include "utility/ProgMemUtil.h"
+#include
+
+/**
+ * \class Ascon128 Ascon128.h
+ * \brief ASCON-128 authenticated cipher.
+ *
+ * Ascon128 is an authenticated cipher designed for memory-limited
+ * environments with a 128-bit key, a 128-bit initialization vector,
+ * and a 128-bit authentication tag. It was one of the finalists
+ * in the CAESAR AEAD competition.
+ *
+ * References: http://competitions.cr.yp.to/round3/asconv12.pdf,
+ * http://ascon.iaik.tugraz.at/
+ *
+ * \sa AuthenticatedCipher
+ */
+
+/**
+ * \brief Constructs a new Ascon128 authenticated cipher.
+ */
+Ascon128::Ascon128()
+#if defined(CRYPTO_LITTLE_ENDIAN)
+ : posn(7)
+ , authMode(1)
+#else
+ : posn(0)
+ , authMode(1)
+#endif
+{
+}
+
+/**
+ * \brief Destroys this Ascon128 authenticated cipher.
+ */
+Ascon128::~Ascon128()
+{
+ clean(state);
+}
+
+/**
+ * \brief Gets the size of the Ascon128 key in bytes.
+ *
+ * \return Always returns 16, indicating a 128-bit key.
+ */
+size_t Ascon128::keySize() const
+{
+ return 16;
+}
+
+/**
+ * \brief Gets the size of the Ascon128 initialization vector in bytes.
+ *
+ * \return Always returns 16, indicating a 128-bit IV.
+ *
+ * Authentication tags may be truncated to 8 bytes, but the algorithm authors
+ * recommend using a full 16-byte tag.
+ */
+size_t Ascon128::ivSize() const
+{
+ return 16;
+}
+
+/**
+ * \brief Gets the size of the Ascon128 authentication tag in bytes.
+ *
+ * \return Always returns 16, indicating a 128-bit authentication tag.
+ */
+size_t Ascon128::tagSize() const
+{
+ return 16;
+}
+
+bool Ascon128::setKey(const uint8_t *key, size_t len)
+{
+ if (len != 16)
+ return false;
+ memcpy(state.K, key, 16);
+#if defined(CRYPTO_LITTLE_ENDIAN)
+ state.K[0] = be64toh(state.K[0]);
+ state.K[1] = be64toh(state.K[1]);
+#endif
+ return true;
+}
+
+bool Ascon128::setIV(const uint8_t *iv, size_t len)
+{
+ // Validate the length of the IV.
+ if (len != 16)
+ return false;
+
+ // Set up the initial state.
+ state.S[0] = 0x80400C0600000000ULL;
+ state.S[1] = state.K[0];
+ state.S[2] = state.K[1];
+ memcpy(state.S + 3, iv, 16);
+#if defined(CRYPTO_LITTLE_ENDIAN)
+ state.S[3] = be64toh(state.S[3]);
+ state.S[4] = be64toh(state.S[4]);
+ posn = 7;
+ authMode = 1;
+#else
+ posn = 0;
+ authMode = 1;
+#endif
+
+ // Permute the state with 12 rounds starting at round 0.
+ permute(0);
+
+ // XOR the end of the state with the original key.
+ state.S[3] ^= state.K[0];
+ state.S[4] ^= state.K[1];
+ return true;
+}
+
+void Ascon128::encrypt(uint8_t *output, const uint8_t *input, size_t len)
+{
+ if (authMode)
+ endAuth();
+ const uint8_t *in = (const uint8_t *)input;
+ uint8_t *out = (uint8_t *)output;
+ while (len > 0) {
+ // Encrypt the next byte using the first 64-bit word in the state.
+ ((uint8_t *)(state.S))[posn] ^= *in++;
+ *out++ = ((const uint8_t *)(state.S))[posn];
+ --len;
+
+ // Permute the state for b = 6 rounds at the end of each block.
+#if defined(CRYPTO_LITTLE_ENDIAN)
+ if (posn > 0) {
+ --posn;
+ } else {
+ permute(6);
+ posn = 7;
+ }
+#else
+ if ((++posn) == 8) {
+ permute(6);
+ posn = 0;
+ }
+#endif
+ }
+}
+
+void Ascon128::decrypt(uint8_t *output, const uint8_t *input, size_t len)
+{
+ if (authMode)
+ endAuth();
+ const uint8_t *in = (const uint8_t *)input;
+ uint8_t *out = (uint8_t *)output;
+ while (len > 0) {
+ // Decrypt the next byte using the first 64-bit word in the state.
+ *out++ = ((const uint8_t *)(state.S))[posn] ^ *in;
+ ((uint8_t *)(state.S))[posn] = *in++;
+ --len;
+
+ // Permute the state for b = 6 rounds at the end of each block.
+#if defined(CRYPTO_LITTLE_ENDIAN)
+ if (posn > 0) {
+ --posn;
+ } else {
+ permute(6);
+ posn = 7;
+ }
+#else
+ if ((++posn) == 8) {
+ permute(6);
+ posn = 0;
+ }
+#endif
+ }
+}
+
+void Ascon128::addAuthData(const void *data, size_t len)
+{
+ if (!authMode)
+ return;
+ const uint8_t *in = (const uint8_t *)data;
+ while (len > 0) {
+ // Incorporate the next byte of auth data into the internal state.
+ ((uint8_t *)(state.S))[posn] ^= *in++;
+ --len;
+
+ // Permute the state for b = 6 rounds at the end of each block.
+#if defined(CRYPTO_LITTLE_ENDIAN)
+ if (posn > 0) {
+ --posn;
+ } else {
+ permute(6);
+ posn = 7;
+ }
+#else
+ if ((++posn) == 8) {
+ permute(6);
+ posn = 0;
+ }
+#endif
+ }
+ authMode = 2; // We have some auth data now.
+}
+
+void Ascon128::computeTag(void *tag, size_t len)
+{
+ // End authentication mode if there was no plaintext/ciphertext.
+ if (authMode)
+ endAuth();
+
+ // Pad the last block, add the original key, and permute the state.
+ ((uint8_t *)(state.S))[posn] ^= 0x80;
+ state.S[1] ^= state.K[0];
+ state.S[2] ^= state.K[1];
+ permute(0);
+
+ // Compute the tag and convert it into big-endian in the return buffer.
+ uint64_t T[2];
+ T[0] = htobe64(state.S[3] ^ state.K[0]);
+ T[1] = htobe64(state.S[4] ^ state.K[1]);
+ if (len > 16)
+ len = 16;
+ memcpy(tag, T, len);
+ clean(T);
+}
+
+bool Ascon128::checkTag(const void *tag, size_t len)
+{
+ // The tag can never match if it is larger than the maximum allowed size.
+ if (len > 16)
+ return false;
+
+ // End authentication mode if there was no plaintext/ciphertext.
+ if (authMode)
+ endAuth();
+
+ // Pad the last block, add the original key, and permute the state.
+ ((uint8_t *)(state.S))[posn] ^= 0x80;
+ state.S[1] ^= state.K[0];
+ state.S[2] ^= state.K[1];
+ permute(0);
+
+ // Compute the tag and convert it into big-endian.
+ uint64_t T[2];
+ T[0] = htobe64(state.S[3] ^ state.K[0]);
+ T[1] = htobe64(state.S[4] ^ state.K[1]);
+ if (len > 16)
+ len = 16;
+ bool ok = secure_compare(T, tag, len);
+ clean(T);
+ return ok;
+}
+
+/**
+ * \brief Clears all security-sensitive state from this cipher object.
+ */
+void Ascon128::clear()
+{
+ clean(state);
+#if defined(CRYPTO_LITTLE_ENDIAN)
+ posn = 7;
+ authMode = 1;
+#else
+ posn = 0;
+ authMode = 1;
+#endif
+}
+
+#if !defined(__AVR__) || defined(CRYPTO_DOC)
+
+/**
+ * \brief Permutes the Ascon128 state.
+ *
+ * \param first The first round start permuting at, between 0 and 11.
+ */
+void Ascon128::permute(uint8_t first)
+{
+ uint64_t t0, t1, t2, t3, t4;
+ #define x0 state.S[0]
+ #define x1 state.S[1]
+ #define x2 state.S[2]
+ #define x3 state.S[3]
+ #define x4 state.S[4]
+ while (first < 12) {
+ // Add the round constant to the state.
+ x2 ^= ((0x0F - first) << 4) | first;
+
+ // Substitution layer - apply the s-box using bit-slicing
+ // according to the algorithm recommended in the specification.
+ x0 ^= x4; x4 ^= x3; x2 ^= x1;
+ t0 = ~x0; t1 = ~x1; t2 = ~x2; t3 = ~x3; t4 = ~x4;
+ t0 &= x1; t1 &= x2; t2 &= x3; t3 &= x4; t4 &= x0;
+ x0 ^= t1; x1 ^= t2; x2 ^= t3; x3 ^= t4; x4 ^= t0;
+ x1 ^= x0; x0 ^= x4; x3 ^= x2; x2 = ~x2;
+
+ // Linear diffusion layer.
+ x0 ^= rightRotate19_64(x0) ^ rightRotate28_64(x0);
+ x1 ^= rightRotate61_64(x1) ^ rightRotate39_64(x1);
+ x2 ^= rightRotate1_64(x2) ^ rightRotate6_64(x2);
+ x3 ^= rightRotate10_64(x3) ^ rightRotate17_64(x3);
+ x4 ^= rightRotate7_64(x4) ^ rightRotate41_64(x4);
+
+ // Move onto the next round.
+ ++first;
+ }
+ #undef x0
+ #undef x1
+ #undef x2
+ #undef x3
+ #undef x4
+}
+
+#endif // !__AVR__
+
+/**
+ * \brief Ends authenticating the associated data and moves onto encryption.
+ */
+void Ascon128::endAuth()
+{
+ if (authMode == 2) {
+ // We had some auth data, so we need to pad and permute the last block.
+ // There is no need to do this if there were zero bytes of auth data.
+ ((uint8_t *)(state.S))[posn] ^= 0x80;
+ permute(6);
+ }
+ state.S[4] ^= 1; // Domain separation between auth data and payload data.
+ authMode = 0;
+#if defined(CRYPTO_LITTLE_ENDIAN)
+ posn = 7;
+#else
+ posn = 0;
+#endif
+}
diff --git a/libraries/CryptoLW/src/Ascon128.h b/libraries/CryptoLW/src/Ascon128.h
new file mode 100644
index 00000000..466dcd55
--- /dev/null
+++ b/libraries/CryptoLW/src/Ascon128.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2018 Southern Storm Software, Pty Ltd.
+ *
+ * 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.
+ */
+
+#ifndef CRYPTO_ASCON128_H
+#define CRYPTO_ASCON128_H
+
+#include "AuthenticatedCipher.h"
+
+class Ascon128 : public AuthenticatedCipher
+{
+public:
+ Ascon128();
+ virtual ~Ascon128();
+
+ size_t keySize() const;
+ size_t ivSize() const;
+ size_t tagSize() const;
+
+ bool setKey(const uint8_t *key, size_t len);
+ bool setIV(const uint8_t *iv, size_t len);
+
+ void encrypt(uint8_t *output, const uint8_t *input, size_t len);
+ void decrypt(uint8_t *output, const uint8_t *input, size_t len);
+
+ void addAuthData(const void *data, size_t len);
+
+ void computeTag(void *tag, size_t len);
+ bool checkTag(const void *tag, size_t len);
+
+ void clear();
+
+private:
+ struct {
+ uint64_t K[2];
+ uint64_t S[5];
+ } state;
+ uint8_t posn;
+ uint8_t authMode;
+
+ void permute(uint8_t first);
+ void endAuth();
+};
+
+#endif
diff --git a/libraries/CryptoLW/src/Ascon128AVR.cpp b/libraries/CryptoLW/src/Ascon128AVR.cpp
new file mode 100644
index 00000000..2f3cb1fb
--- /dev/null
+++ b/libraries/CryptoLW/src/Ascon128AVR.cpp
@@ -0,0 +1,718 @@
+/*
+ * Copyright (C) 2018 Southern Storm Software, Pty Ltd.
+ *
+ * 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 "Ascon128.h"
+
+#if defined(__AVR__)
+
+void Ascon128::permute(uint8_t first)
+{
+ // AVR version generated by the genascon tool.
+ __asm__ __volatile__ (
+ "1:\n"
+ "ldd r15,Z+16\n"
+ "eor r15,%1\n"
+ "ld r14,Z\n"
+ "ldd r13,Z+8\n"
+ "ldd r12,Z+24\n"
+ "ldd r11,Z+32\n"
+ "eor r14,r11\n"
+ "eor r11,r12\n"
+ "eor r15,r13\n"
+ "mov r10,r14\n"
+ "com r10\n"
+ "and r10,r13\n"
+ "mov r9,r13\n"
+ "com r9\n"
+ "and r9,r15\n"
+ "mov r8,r15\n"
+ "com r8\n"
+ "and r8,r12\n"
+ "mov r7,r12\n"
+ "com r7\n"
+ "and r7,r11\n"
+ "mov r23,r11\n"
+ "com r23\n"
+ "and r23,r14\n"
+ "eor r14,r9\n"
+ "eor r13,r8\n"
+ "eor r15,r7\n"
+ "eor r12,r23\n"
+ "eor r11,r10\n"
+ "eor r13,r14\n"
+ "eor r14,r11\n"
+ "eor r12,r15\n"
+ "com r15\n"
+ "st Z,r14\n"
+ "std Z+8,r13\n"
+ "std Z+16,r15\n"
+ "std Z+24,r12\n"
+ "std Z+32,r11\n"
+ "ldd r15,Z+1\n"
+ "ldd r14,Z+9\n"
+ "ldd r13,Z+17\n"
+ "ldd r12,Z+25\n"
+ "ldd r11,Z+33\n"
+ "eor r15,r11\n"
+ "eor r11,r12\n"
+ "eor r13,r14\n"
+ "mov r10,r15\n"
+ "com r10\n"
+ "and r10,r14\n"
+ "mov r9,r14\n"
+ "com r9\n"
+ "and r9,r13\n"
+ "mov r8,r13\n"
+ "com r8\n"
+ "and r8,r12\n"
+ "mov r7,r12\n"
+ "com r7\n"
+ "and r7,r11\n"
+ "mov r23,r11\n"
+ "com r23\n"
+ "and r23,r15\n"
+ "eor r15,r9\n"
+ "eor r14,r8\n"
+ "eor r13,r7\n"
+ "eor r12,r23\n"
+ "eor r11,r10\n"
+ "eor r14,r15\n"
+ "eor r15,r11\n"
+ "eor r12,r13\n"
+ "com r13\n"
+ "std Z+1,r15\n"
+ "std Z+9,r14\n"
+ "std Z+17,r13\n"
+ "std Z+25,r12\n"
+ "std Z+33,r11\n"
+ "ldd r15,Z+2\n"
+ "ldd r14,Z+10\n"
+ "ldd r13,Z+18\n"
+ "ldd r12,Z+26\n"
+ "ldd r11,Z+34\n"
+ "eor r15,r11\n"
+ "eor r11,r12\n"
+ "eor r13,r14\n"
+ "mov r10,r15\n"
+ "com r10\n"
+ "and r10,r14\n"
+ "mov r9,r14\n"
+ "com r9\n"
+ "and r9,r13\n"
+ "mov r8,r13\n"
+ "com r8\n"
+ "and r8,r12\n"
+ "mov r7,r12\n"
+ "com r7\n"
+ "and r7,r11\n"
+ "mov r23,r11\n"
+ "com r23\n"
+ "and r23,r15\n"
+ "eor r15,r9\n"
+ "eor r14,r8\n"
+ "eor r13,r7\n"
+ "eor r12,r23\n"
+ "eor r11,r10\n"
+ "eor r14,r15\n"
+ "eor r15,r11\n"
+ "eor r12,r13\n"
+ "com r13\n"
+ "std Z+2,r15\n"
+ "std Z+10,r14\n"
+ "std Z+18,r13\n"
+ "std Z+26,r12\n"
+ "std Z+34,r11\n"
+ "ldd r15,Z+3\n"
+ "ldd r14,Z+11\n"
+ "ldd r13,Z+19\n"
+ "ldd r12,Z+27\n"
+ "ldd r11,Z+35\n"
+ "eor r15,r11\n"
+ "eor r11,r12\n"
+ "eor r13,r14\n"
+ "mov r10,r15\n"
+ "com r10\n"
+ "and r10,r14\n"
+ "mov r9,r14\n"
+ "com r9\n"
+ "and r9,r13\n"
+ "mov r8,r13\n"
+ "com r8\n"
+ "and r8,r12\n"
+ "mov r7,r12\n"
+ "com r7\n"
+ "and r7,r11\n"
+ "mov r23,r11\n"
+ "com r23\n"
+ "and r23,r15\n"
+ "eor r15,r9\n"
+ "eor r14,r8\n"
+ "eor r13,r7\n"
+ "eor r12,r23\n"
+ "eor r11,r10\n"
+ "eor r14,r15\n"
+ "eor r15,r11\n"
+ "eor r12,r13\n"
+ "com r13\n"
+ "std Z+3,r15\n"
+ "std Z+11,r14\n"
+ "std Z+19,r13\n"
+ "std Z+27,r12\n"
+ "std Z+35,r11\n"
+ "ldd r15,Z+4\n"
+ "ldd r14,Z+12\n"
+ "ldd r13,Z+20\n"
+ "ldd r12,Z+28\n"
+ "ldd r11,Z+36\n"
+ "eor r15,r11\n"
+ "eor r11,r12\n"
+ "eor r13,r14\n"
+ "mov r10,r15\n"
+ "com r10\n"
+ "and r10,r14\n"
+ "mov r9,r14\n"
+ "com r9\n"
+ "and r9,r13\n"
+ "mov r8,r13\n"
+ "com r8\n"
+ "and r8,r12\n"
+ "mov r7,r12\n"
+ "com r7\n"
+ "and r7,r11\n"
+ "mov r23,r11\n"
+ "com r23\n"
+ "and r23,r15\n"
+ "eor r15,r9\n"
+ "eor r14,r8\n"
+ "eor r13,r7\n"
+ "eor r12,r23\n"
+ "eor r11,r10\n"
+ "eor r14,r15\n"
+ "eor r15,r11\n"
+ "eor r12,r13\n"
+ "com r13\n"
+ "std Z+4,r15\n"
+ "std Z+12,r14\n"
+ "std Z+20,r13\n"
+ "std Z+28,r12\n"
+ "std Z+36,r11\n"
+ "ldd r15,Z+5\n"
+ "ldd r14,Z+13\n"
+ "ldd r13,Z+21\n"
+ "ldd r12,Z+29\n"
+ "ldd r11,Z+37\n"
+ "eor r15,r11\n"
+ "eor r11,r12\n"
+ "eor r13,r14\n"
+ "mov r10,r15\n"
+ "com r10\n"
+ "and r10,r14\n"
+ "mov r9,r14\n"
+ "com r9\n"
+ "and r9,r13\n"
+ "mov r8,r13\n"
+ "com r8\n"
+ "and r8,r12\n"
+ "mov r7,r12\n"
+ "com r7\n"
+ "and r7,r11\n"
+ "mov r23,r11\n"
+ "com r23\n"
+ "and r23,r15\n"
+ "eor r15,r9\n"
+ "eor r14,r8\n"
+ "eor r13,r7\n"
+ "eor r12,r23\n"
+ "eor r11,r10\n"
+ "eor r14,r15\n"
+ "eor r15,r11\n"
+ "eor r12,r13\n"
+ "com r13\n"
+ "std Z+5,r15\n"
+ "std Z+13,r14\n"
+ "std Z+21,r13\n"
+ "std Z+29,r12\n"
+ "std Z+37,r11\n"
+ "ldd r15,Z+6\n"
+ "ldd r14,Z+14\n"
+ "ldd r13,Z+22\n"
+ "ldd r12,Z+30\n"
+ "ldd r11,Z+38\n"
+ "eor r15,r11\n"
+ "eor r11,r12\n"
+ "eor r13,r14\n"
+ "mov r10,r15\n"
+ "com r10\n"
+ "and r10,r14\n"
+ "mov r9,r14\n"
+ "com r9\n"
+ "and r9,r13\n"
+ "mov r8,r13\n"
+ "com r8\n"
+ "and r8,r12\n"
+ "mov r7,r12\n"
+ "com r7\n"
+ "and r7,r11\n"
+ "mov r23,r11\n"
+ "com r23\n"
+ "and r23,r15\n"
+ "eor r15,r9\n"
+ "eor r14,r8\n"
+ "eor r13,r7\n"
+ "eor r12,r23\n"
+ "eor r11,r10\n"
+ "eor r14,r15\n"
+ "eor r15,r11\n"
+ "eor r12,r13\n"
+ "com r13\n"
+ "std Z+6,r15\n"
+ "std Z+14,r14\n"
+ "std Z+22,r13\n"
+ "std Z+30,r12\n"
+ "std Z+38,r11\n"
+ "ldd r15,Z+7\n"
+ "ldd r14,Z+15\n"
+ "ldd r13,Z+23\n"
+ "ldd r12,Z+31\n"
+ "ldd r11,Z+39\n"
+ "eor r15,r11\n"
+ "eor r11,r12\n"
+ "eor r13,r14\n"
+ "mov r10,r15\n"
+ "com r10\n"
+ "and r10,r14\n"
+ "mov r9,r14\n"
+ "com r9\n"
+ "and r9,r13\n"
+ "mov r8,r13\n"
+ "com r8\n"
+ "and r8,r12\n"
+ "mov r7,r12\n"
+ "com r7\n"
+ "and r7,r11\n"
+ "mov r23,r11\n"
+ "com r23\n"
+ "and r23,r15\n"
+ "eor r15,r9\n"
+ "eor r14,r8\n"
+ "eor r13,r7\n"
+ "eor r12,r23\n"
+ "eor r11,r10\n"
+ "eor r14,r15\n"
+ "eor r15,r11\n"
+ "eor r12,r13\n"
+ "com r13\n"
+ "std Z+7,r15\n"
+ "std Z+15,r14\n"
+ "std Z+23,r13\n"
+ "std Z+31,r12\n"
+ "std Z+39,r11\n"
+ "ld r15,Z\n"
+ "ldd r14,Z+1\n"
+ "ldd r13,Z+2\n"
+ "ldd r12,Z+3\n"
+ "ldd r11,Z+4\n"
+ "ldd r10,Z+5\n"
+ "ldd r9,Z+6\n"
+ "ldd r8,Z+7\n"
+ "mov r7,r15\n"
+ "mov r23,r14\n"
+ "mov r22,r13\n"
+ "mov r21,r12\n"
+ "mov r20,r11\n"
+ "mov r19,r10\n"
+ "mov r18,r9\n"
+ "mov r17,r8\n"
+ "bst r22,0\n"
+ "ror r23\n"
+ "ror r7\n"
+ "ror r17\n"
+ "ror r18\n"
+ "ror r19\n"
+ "ror r20\n"
+ "ror r21\n"
+ "ror r22\n"
+ "bld r23,7\n"
+ "bst r22,0\n"
+ "ror r23\n"
+ "ror r7\n"
+ "ror r17\n"
+ "ror r18\n"
+ "ror r19\n"
+ "ror r20\n"
+ "ror r21\n"
+ "ror r22\n"
+ "bld r23,7\n"
+ "bst r22,0\n"
+ "ror r23\n"
+ "ror r7\n"
+ "ror r17\n"
+ "ror r18\n"
+ "ror r19\n"
+ "ror r20\n"
+ "ror r21\n"
+ "ror r22\n"
+ "bld r23,7\n"
+ "eor r22,r15\n"
+ "eor r21,r14\n"
+ "eor r20,r13\n"
+ "eor r19,r12\n"
+ "eor r18,r11\n"
+ "eor r17,r10\n"
+ "eor r7,r9\n"
+ "eor r23,r8\n"
+ "lsl r11\n"
+ "rol r10\n"
+ "rol r9\n"
+ "rol r8\n"
+ "rol r15\n"
+ "rol r14\n"
+ "rol r13\n"
+ "rol r12\n"
+ "adc r11,__zero_reg__\n"
+ "lsl r11\n"
+ "rol r10\n"
+ "rol r9\n"
+ "rol r8\n"
+ "rol r15\n"
+ "rol r14\n"
+ "rol r13\n"
+ "rol r12\n"
+ "adc r11,__zero_reg__\n"
+ "lsl r11\n"
+ "rol r10\n"
+ "rol r9\n"
+ "rol r8\n"
+ "rol r15\n"
+ "rol r14\n"
+ "rol r13\n"
+ "rol r12\n"
+ "adc r11,__zero_reg__\n"
+ "lsl r11\n"
+ "rol r10\n"
+ "rol r9\n"
+ "rol r8\n"
+ "rol r15\n"
+ "rol r14\n"
+ "rol r13\n"
+ "rol r12\n"
+ "adc r11,__zero_reg__\n"
+ "eor r11,r22\n"
+ "eor r10,r21\n"
+ "eor r9,r20\n"
+ "eor r8,r19\n"
+ "eor r15,r18\n"
+ "eor r14,r17\n"
+ "eor r13,r7\n"
+ "eor r12,r23\n"
+ "st Z,r11\n"
+ "std Z+1,r10\n"
+ "std Z+2,r9\n"
+ "std Z+3,r8\n"
+ "std Z+4,r15\n"
+ "std Z+5,r14\n"
+ "std Z+6,r13\n"
+ "std Z+7,r12\n"
+ "ldd r15,Z+8\n"
+ "ldd r14,Z+9\n"
+ "ldd r13,Z+10\n"
+ "ldd r12,Z+11\n"
+ "ldd r11,Z+12\n"
+ "ldd r10,Z+13\n"
+ "ldd r9,Z+14\n"
+ "ldd r8,Z+15\n"
+ "mov r7,r15\n"
+ "mov r23,r14\n"
+ "mov r22,r13\n"
+ "mov r21,r12\n"
+ "mov r20,r11\n"
+ "mov r19,r10\n"
+ "mov r18,r9\n"
+ "mov r17,r8\n"
+ "lsl r7\n"
+ "rol r23\n"
+ "rol r22\n"
+ "rol r21\n"
+ "rol r20\n"
+ "rol r19\n"
+ "rol r18\n"
+ "rol r17\n"
+ "adc r7,__zero_reg__\n"
+ "lsl r7\n"
+ "rol r23\n"
+ "rol r22\n"
+ "rol r21\n"
+ "rol r20\n"
+ "rol r19\n"
+ "rol r18\n"
+ "rol r17\n"
+ "adc r7,__zero_reg__\n"
+ "lsl r7\n"
+ "rol r23\n"
+ "rol r22\n"
+ "rol r21\n"
+ "rol r20\n"
+ "rol r19\n"
+ "rol r18\n"
+ "rol r17\n"
+ "adc r7,__zero_reg__\n"
+ "eor r7,r15\n"
+ "eor r23,r14\n"
+ "eor r22,r13\n"
+ "eor r21,r12\n"
+ "eor r20,r11\n"
+ "eor r19,r10\n"
+ "eor r18,r9\n"
+ "eor r17,r8\n"
+ "lsl r10\n"
+ "rol r9\n"
+ "rol r8\n"
+ "rol r15\n"
+ "rol r14\n"
+ "rol r13\n"
+ "rol r12\n"
+ "rol r11\n"
+ "adc r10,__zero_reg__\n"
+ "eor r10,r7\n"
+ "eor r9,r23\n"
+ "eor r8,r22\n"
+ "eor r15,r21\n"
+ "eor r14,r20\n"
+ "eor r13,r19\n"
+ "eor r12,r18\n"
+ "eor r11,r17\n"
+ "std Z+8,r10\n"
+ "std Z+9,r9\n"
+ "std Z+10,r8\n"
+ "std Z+11,r15\n"
+ "std Z+12,r14\n"
+ "std Z+13,r13\n"
+ "std Z+14,r12\n"
+ "std Z+15,r11\n"
+ "ldd r15,Z+16\n"
+ "ldd r14,Z+17\n"
+ "ldd r13,Z+18\n"
+ "ldd r12,Z+19\n"
+ "ldd r11,Z+20\n"
+ "ldd r10,Z+21\n"
+ "ldd r9,Z+22\n"
+ "ldd r8,Z+23\n"
+ "mov r7,r15\n"
+ "mov r23,r14\n"
+ "mov r22,r13\n"
+ "mov r21,r12\n"
+ "mov r20,r11\n"
+ "mov r19,r10\n"
+ "mov r18,r9\n"
+ "mov r17,r8\n"
+ "bst r7,0\n"
+ "ror r17\n"
+ "ror r18\n"
+ "ror r19\n"
+ "ror r20\n"
+ "ror r21\n"
+ "ror r22\n"
+ "ror r23\n"
+ "ror r7\n"
+ "bld r17,7\n"
+ "eor r7,r15\n"
+ "eor r23,r14\n"
+ "eor r22,r13\n"
+ "eor r21,r12\n"
+ "eor r20,r11\n"
+ "eor r19,r10\n"
+ "eor r18,r9\n"
+ "eor r17,r8\n"
+ "lsl r14\n"
+ "rol r13\n"
+ "rol r12\n"
+ "rol r11\n"
+ "rol r10\n"
+ "rol r9\n"
+ "rol r8\n"
+ "rol r15\n"
+ "adc r14,__zero_reg__\n"
+ "lsl r14\n"
+ "rol r13\n"
+ "rol r12\n"
+ "rol r11\n"
+ "rol r10\n"
+ "rol r9\n"
+ "rol r8\n"
+ "rol r15\n"
+ "adc r14,__zero_reg__\n"
+ "eor r14,r7\n"
+ "eor r13,r23\n"
+ "eor r12,r22\n"
+ "eor r11,r21\n"
+ "eor r10,r20\n"
+ "eor r9,r19\n"
+ "eor r8,r18\n"
+ "eor r15,r17\n"
+ "std Z+16,r14\n"
+ "std Z+17,r13\n"
+ "std Z+18,r12\n"
+ "std Z+19,r11\n"
+ "std Z+20,r10\n"
+ "std Z+21,r9\n"
+ "std Z+22,r8\n"
+ "std Z+23,r15\n"
+ "ldd r15,Z+24\n"
+ "ldd r14,Z+25\n"
+ "ldd r13,Z+26\n"
+ "ldd r12,Z+27\n"
+ "ldd r11,Z+28\n"
+ "ldd r10,Z+29\n"
+ "ldd r9,Z+30\n"
+ "ldd r8,Z+31\n"
+ "mov r7,r15\n"
+ "mov r23,r14\n"
+ "mov r22,r13\n"
+ "mov r21,r12\n"
+ "mov r20,r11\n"
+ "mov r19,r10\n"
+ "mov r18,r9\n"
+ "mov r17,r8\n"
+ "bst r23,0\n"
+ "ror r7\n"
+ "ror r17\n"
+ "ror r18\n"
+ "ror r19\n"
+ "ror r20\n"
+ "ror r21\n"
+ "ror r22\n"
+ "ror r23\n"
+ "bld r7,7\n"
+ "bst r23,0\n"
+ "ror r7\n"
+ "ror r17\n"
+ "ror r18\n"
+ "ror r19\n"
+ "ror r20\n"
+ "ror r21\n"
+ "ror r22\n"
+ "ror r23\n"
+ "bld r7,7\n"
+ "eor r23,r15\n"
+ "eor r22,r14\n"
+ "eor r21,r13\n"
+ "eor r20,r12\n"
+ "eor r19,r11\n"
+ "eor r18,r10\n"
+ "eor r17,r9\n"
+ "eor r7,r8\n"
+ "bst r13,0\n"
+ "ror r14\n"
+ "ror r15\n"
+ "ror r8\n"
+ "ror r9\n"
+ "ror r10\n"
+ "ror r11\n"
+ "ror r12\n"
+ "ror r13\n"
+ "bld r14,7\n"
+ "eor r13,r23\n"
+ "eor r12,r22\n"
+ "eor r11,r21\n"
+ "eor r10,r20\n"
+ "eor r9,r19\n"
+ "eor r8,r18\n"
+ "eor r15,r17\n"
+ "eor r14,r7\n"
+ "std Z+24,r13\n"
+ "std Z+25,r12\n"
+ "std Z+26,r11\n"
+ "std Z+27,r10\n"
+ "std Z+28,r9\n"
+ "std Z+29,r8\n"
+ "std Z+30,r15\n"
+ "std Z+31,r14\n"
+ "ldd r15,Z+32\n"
+ "ldd r14,Z+33\n"
+ "ldd r13,Z+34\n"
+ "ldd r12,Z+35\n"
+ "ldd r11,Z+36\n"
+ "ldd r10,Z+37\n"
+ "ldd r9,Z+38\n"
+ "ldd r8,Z+39\n"
+ "mov r7,r15\n"
+ "mov r23,r14\n"
+ "mov r22,r13\n"
+ "mov r21,r12\n"
+ "mov r20,r11\n"
+ "mov r19,r10\n"
+ "mov r18,r9\n"
+ "mov r17,r8\n"
+ "lsl r23\n"
+ "rol r22\n"
+ "rol r21\n"
+ "rol r20\n"
+ "rol r19\n"
+ "rol r18\n"
+ "rol r17\n"
+ "rol r7\n"
+ "adc r23,__zero_reg__\n"
+ "eor r23,r15\n"
+ "eor r22,r14\n"
+ "eor r21,r13\n"
+ "eor r20,r12\n"
+ "eor r19,r11\n"
+ "eor r18,r10\n"
+ "eor r17,r9\n"
+ "eor r7,r8\n"
+ "bst r10,0\n"
+ "ror r11\n"
+ "ror r12\n"
+ "ror r13\n"
+ "ror r14\n"
+ "ror r15\n"
+ "ror r8\n"
+ "ror r9\n"
+ "ror r10\n"
+ "bld r11,7\n"
+ "eor r10,r23\n"
+ "eor r9,r22\n"
+ "eor r8,r21\n"
+ "eor r15,r20\n"
+ "eor r14,r19\n"
+ "eor r13,r18\n"
+ "eor r12,r17\n"
+ "eor r11,r7\n"
+ "std Z+32,r10\n"
+ "std Z+33,r9\n"
+ "std Z+34,r8\n"
+ "std Z+35,r15\n"
+ "std Z+36,r14\n"
+ "std Z+37,r13\n"
+ "std Z+38,r12\n"
+ "std Z+39,r11\n"
+ "subi %1,0x0F\n"
+ "cpi %1,0x3C\n"
+ "breq 2f\n"
+ "rjmp 1b\n"
+ "2:\n"
+ :: "z"(state.S), "d"((uint8_t)(0xF0 - (first << 4) + first))
+ : "r17", "r18", "r19", "r20", "r21", "r22", "r23", "r7", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", "memory"
+ );
+}
+
+#endif // __AVR__