From 455549f835fab8754ea1a4c327c96e18f8237378 Mon Sep 17 00:00:00 2001 From: Rhys Weatherley Date: Fri, 27 Apr 2018 11:01:29 +1000 Subject: [PATCH] ASCON-128 finalist from the CAESAR competition --- README.md | 3 +- doc/crypto.dox | 4 +- gen/genascon.c | 520 +++++++++++++ host/Crypto/Makefile | 2 + .../CryptoLW/examples/TestAscon/TestAscon.ino | 403 ++++++++++ libraries/CryptoLW/src/Ascon128.cpp | 356 +++++++++ libraries/CryptoLW/src/Ascon128.h | 63 ++ libraries/CryptoLW/src/Ascon128AVR.cpp | 718 ++++++++++++++++++ 8 files changed, 2067 insertions(+), 2 deletions(-) create mode 100644 gen/genascon.c create mode 100644 libraries/CryptoLW/examples/TestAscon/TestAscon.ino create mode 100644 libraries/CryptoLW/src/Ascon128.cpp create mode 100644 libraries/CryptoLW/src/Ascon128.h create mode 100644 libraries/CryptoLW/src/Ascon128AVR.cpp 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.89us25.88us690.63us362 EAX<SpeckTiny> (256-bit key)78.20us78.20us1269.19us122 Acorn12820.39us20.06us4817.82us60 +Ascon12842.71us43.07us738.68us60 Hash AlgorithmHashing (per byte)Finalization State Size (bytes) SHA25643.85us2841.04us 107 @@ -253,6 +254,7 @@ All figures are for the Arduino Due running at 84 MHz: EAX<Speck> (256-bit key)2.80us2.80us81.63us384 EAX<SpeckTiny> (256-bit key)6.69us6.69us110.91us144 Acorn1280.75us0.75us175.70us64 +Ascon1283.52us3.50us51.67us72 Hash AlgorithmHashing (per byte)Finalization State Size (bytes) SHA2561.15us76.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__