From 91bffb9d1f1048d7d52115ad2dbd6703dc439084 Mon Sep 17 00:00:00 2001 From: Rhys Weatherley Date: Sat, 21 Apr 2018 17:50:42 +1000 Subject: [PATCH] ACORN-128 AEAD cipher --- doc/Doxyfile | 1 + doc/crypto.dox | 3 +- .../CryptoLW/examples/TestAcorn/TestAcorn.ino | 415 +++++++++++ libraries/CryptoLW/keywords.txt | 1 + libraries/CryptoLW/library.properties | 10 + libraries/CryptoLW/src/Acorn128.cpp | 668 ++++++++++++++++++ libraries/CryptoLW/src/Acorn128.h | 87 +++ libraries/CryptoLW/src/CryptoLW.h | 29 + 8 files changed, 1213 insertions(+), 1 deletion(-) create mode 100644 libraries/CryptoLW/examples/TestAcorn/TestAcorn.ino create mode 100644 libraries/CryptoLW/keywords.txt create mode 100644 libraries/CryptoLW/library.properties create mode 100644 libraries/CryptoLW/src/Acorn128.cpp create mode 100644 libraries/CryptoLW/src/Acorn128.h create mode 100644 libraries/CryptoLW/src/CryptoLW.h diff --git a/doc/Doxyfile b/doc/Doxyfile index 62232722..6d0ea40f 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -656,6 +656,7 @@ WARN_LOGFILE = # with spaces. INPUT = ../libraries/Crypto \ + ../libraries/CryptoLW/src \ ../libraries/NewHope \ ../libraries/RingOscillatorNoiseSource \ ../libraries/TransistorNoiseSource \ diff --git a/doc/crypto.dox b/doc/crypto.dox index 867615b5..df01f72b 100644 --- a/doc/crypto.dox +++ b/doc/crypto.dox @@ -29,7 +29,7 @@ \li Block ciphers: AES128, AES192, AES256, Speck \li Block cipher modes: CTR, CFB, CBC, OFB, EAX, GCM, XTS \li Stream ciphers: ChaCha -\li Authenticated encryption with associated data (AEAD): ChaChaPoly, EAX, GCM +\li Authenticated encryption with associated data (AEAD): ChaChaPoly, EAX, GCM, Acorn128 \li Hash algorithms: SHA256, SHA512, SHA3_256, SHA3_512, BLAKE2s, BLAKE2b (regular and HMAC modes) \li Extendable output functions (XOF's): SHAKE128, SHAKE256 \li Message authenticators: Poly1305, GHASH, OMAC @@ -206,6 +206,7 @@ All figures are for the Arduino Due running at 84 MHz: EAX<AES256>16.99us16.99us322.92us344 EAX<Speck> (256-bit key)2.80us2.80us81.63us384 EAX<SpeckTiny> (256-bit key)6.69us6.69us110.91us144 +Acorn1280.75us0.75us175.70us64 Hash AlgorithmHashing (per byte)Finalization State Size (bytes) SHA2561.15us76.60us 120 diff --git a/libraries/CryptoLW/examples/TestAcorn/TestAcorn.ino b/libraries/CryptoLW/examples/TestAcorn/TestAcorn.ino new file mode 100644 index 00000000..7c6d4c39 --- /dev/null +++ b/libraries/CryptoLW/examples/TestAcorn/TestAcorn.ino @@ -0,0 +1,415 @@ +/* + * 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 Acorn128 implementation to verify +correct behaviour. +*/ + +#include +#include +#include +#include "utility/ProgMemUtil.h" + +#define MAX_PLAINTEXT_LEN 73 +#define MAX_AUTHDATA_LEN 39 + +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 Acorn128 from the specification. +static TestVector const testVectorAcorn128_1 PROGMEM = { + .name = "Acorn128 #1", + .key = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + .plaintext = {0}, + .ciphertext = {0}, + .authdata = {0}, + .iv = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + .tag = {0x83, 0x5e, 0x53, 0x17, 0x89, 0x6e, 0x86, 0xb2, + 0x44, 0x71, 0x43, 0xc7, 0x4f, 0x6f, 0xfc, 0x1e}, + .authsize = 0, + .datasize = 0 +}; +static TestVector const testVectorAcorn128_2 PROGMEM = { + .name = "Acorn128 #2", + .key = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + .plaintext = {0x01}, + .ciphertext = {0x2b}, + .authdata = {0}, + .iv = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + .tag = {0x4b, 0x60, 0x64, 0x0e, 0x26, 0xf0, 0xa9, 0x9d, + 0xd0, 0x1f, 0x93, 0xbf, 0x63, 0x49, 0x97, 0xcb}, + .authsize = 0, + .datasize = 1 +}; +static TestVector const testVectorAcorn128_3 PROGMEM = { + .name = "Acorn128 #3", + .key = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + .plaintext = {0}, + .ciphertext = {0}, + .authdata = {0x01}, + .iv = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + .tag = {0x98, 0x2e, 0xf7, 0xd1, 0xbb, 0xa7, 0xf8, 0x9a, + 0x15, 0x75, 0x29, 0x7a, 0x09, 0x5c, 0xd7, 0xf2}, + .authsize = 1, + .datasize = 0 +}; +static TestVector const testVectorAcorn128_4 PROGMEM = { + .name = "Acorn128 #4", + .key = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, + .plaintext = {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, + .ciphertext = {0x86, 0x80, 0x1f, 0xa8, 0x9e, 0x33, 0xd9, 0x92, + 0x35, 0xdd, 0x4d, 0x1a, 0x72, 0xce, 0x00, 0x1a}, + .authdata = {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, + .iv = {0x00, 0x03, 0x06, 0x09, 0x0c, 0x0f, 0x12, 0x15, + 0x18, 0x1b, 0x1e, 0x21, 0x24, 0x27, 0x2a, 0x2d}, + .tag = {0xd9, 0xc6, 0x6b, 0x4a, 0xdb, 0x3c, 0xde, 0x07, + 0x3e, 0x63, 0x50, 0xcc, 0x7e, 0x23, 0x7e, 0x01}, + .authsize = 16, + .datasize = 16 +}; +static TestVector const testVectorAcorn128_5 PROGMEM = { + .name = "Acorn128 #5", + .key = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, + .plaintext = {0x00, 0x07, 0x0e, 0x15, 0x1c, 0x23, 0x2a, 0x31, + 0x38, 0x3f, 0x46, 0x4d, 0x54, 0x5b, 0x62, 0x69, + 0x70, 0x77, 0x7e, 0x85, 0x8c, 0x93, 0x9a, 0xa1, + 0xa8, 0xaf, 0xb6, 0xbd, 0xc4, 0xcb, 0xd2, 0xd9, + 0xe0, 0xe7, 0xee, 0xf5, 0xfc, 0x03, 0x0a, 0x11, + 0x18, 0x1f, 0x26, 0x2d, 0x34, 0x3b, 0x42, 0x49, + 0x50, 0x57, 0x5e, 0x65, 0x6c, 0x73, 0x7a, 0x81, + 0x88, 0x8f, 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb9, + 0xc0, 0xc7, 0xce, 0xd5, 0xdc, 0xe3, 0xea, 0xf1, + 0xf8}, + .ciphertext = {0xe7, 0xef, 0x31, 0x63, 0x78, 0x44, 0x46, 0x44, + 0x70, 0x5c, 0x43, 0x81, 0xc8, 0x88, 0x83, 0x3b, + 0x6d, 0x62, 0xa7, 0x49, 0x00, 0x5a, 0xb8, 0xfa, + 0x14, 0x6a, 0x85, 0x90, 0x4d, 0x5e, 0x5a, 0xb7, + 0x7c, 0x57, 0x58, 0x21, 0x58, 0x39, 0x5d, 0x8f, + 0xe6, 0xb6, 0x66, 0xe6, 0xc8, 0x51, 0x77, 0x64, + 0x8a, 0xeb, 0x77, 0x84, 0xcf, 0x2e, 0xea, 0xed, + 0x3c, 0x22, 0xe7, 0xe9, 0x6b, 0xf5, 0x90, 0x09, + 0xcd, 0x7a, 0xd2, 0x1b, 0xa5, 0xdf, 0x1a, 0x0f, + 0xc0}, + .authdata = {0x00, 0x05, 0x0a, 0x0f, 0x14, 0x19, 0x1e, 0x23, + 0x28, 0x2d, 0x32, 0x37, 0x3c, 0x41, 0x46, 0x4b, + 0x50, 0x55, 0x5a, 0x5f, 0x64, 0x69, 0x6e, 0x73, + 0x78, 0x7d, 0x82, 0x87, 0x8c, 0x91, 0x96, 0x9b, + 0xa0, 0xa5, 0xaa, 0xaf, 0xb4, 0xb9, 0xbe}, + .iv = {0x00, 0x03, 0x06, 0x09, 0x0c, 0x0f, 0x12, 0x15, + 0x18, 0x1b, 0x1e, 0x21, 0x24, 0x27, 0x2a, 0x2d}, + .tag = {0x51, 0xb4, 0xbd, 0x86, 0xc6, 0x8c, 0xcf, 0x06, + 0x82, 0xf5, 0x69, 0x5d, 0x26, 0x67, 0xd5, 0x35}, + .authsize = 39, + .datasize = 73 +}; + +TestVector testVector; + +Acorn128 acorn; + +byte buffer[MAX_PLAINTEXT_LEN]; + +bool testCipher_N(Acorn128 *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(Acorn128 *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(Acorn128 *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(Acorn128 *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(Acorn128 *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(Acorn128 *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(Acorn128 *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(Acorn128 *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(Acorn128)); + Serial.println(); + + Serial.println("Test Vectors:"); + testCipher(&acorn, &testVectorAcorn128_1); + testCipher(&acorn, &testVectorAcorn128_2); + testCipher(&acorn, &testVectorAcorn128_3); + testCipher(&acorn, &testVectorAcorn128_4); + testCipher(&acorn, &testVectorAcorn128_5); + + Serial.println(); + + Serial.println("Performance Tests:"); + perfCipher(&acorn, &testVectorAcorn128_4); +} + +void loop() +{ +} diff --git a/libraries/CryptoLW/keywords.txt b/libraries/CryptoLW/keywords.txt new file mode 100644 index 00000000..79ccb087 --- /dev/null +++ b/libraries/CryptoLW/keywords.txt @@ -0,0 +1 @@ +Acorn128 KEYWORD1 diff --git a/libraries/CryptoLW/library.properties b/libraries/CryptoLW/library.properties new file mode 100644 index 00000000..6467225d --- /dev/null +++ b/libraries/CryptoLW/library.properties @@ -0,0 +1,10 @@ +name=CryptoLW +version=1.0.0 +author=Rhys Weatherley +maintainer=Rhys Weatherley +sentence=Light-weight algorithms for the Arduino Cryptography Library +paragraph=This library provides implementations of various "light-weight" cryptography algorithms, designed for resource-constrained environments. +category=Communication +url=https://github.com/rweather/arduinolibs +architectures=* +includes=CryptoLW.h diff --git a/libraries/CryptoLW/src/Acorn128.cpp b/libraries/CryptoLW/src/Acorn128.cpp new file mode 100644 index 00000000..4b4bb59f --- /dev/null +++ b/libraries/CryptoLW/src/Acorn128.cpp @@ -0,0 +1,668 @@ +/* + * 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 "Acorn128.h" +#include "Crypto.h" +#include "utility/EndianUtil.h" +#include + +/** + * \class Acorn128 Acorn128.h + * \brief ACORN-128 authenticated cipher. + * + * Acorn128 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/acornv3.pdf, + * http://competitions.cr.yp.to/caesar-submissions.html + * + * \sa AuthenticatedCipher + */ + +/** + * \brief Constructs a new Acorn128 authenticated cipher. + */ +Acorn128::Acorn128() +{ + state.authDone = 0; +} + +/** + * \brief Destroys this Acorn128 authenticated cipher. + */ +Acorn128::~Acorn128() +{ + clean(state); +} + +/** + * \brief Gets the size of the Acorn128 key in bytes. + * + * \return Always returns 16, indicating a 128-bit key. + */ +size_t Acorn128::keySize() const +{ + return 16; +} + +/** + * \brief Gets the size of the Acorn128 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 Acorn128::ivSize() const +{ + return 16; +} + +/** + * \brief Gets the size of the Acorn128 authentication tag in bytes. + * + * \return Always returns 16, indicating a 128-bit authentication tag. + */ +size_t Acorn128::tagSize() const +{ + return 16; +} + +// Acorn128 constants for ca and cb. +#define CA_0 ((uint32_t)0x00000000) +#define CA_1 ((uint32_t)0xFFFFFFFF) +#define CB_0 ((uint32_t)0x00000000) +#define CB_1 ((uint32_t)0xFFFFFFFF) +#define CA_0_BYTE ((uint8_t)0x00) +#define CA_1_BYTE ((uint8_t)0xFF) +#define CB_0_BYTE ((uint8_t)0x00) +#define CB_1_BYTE ((uint8_t)0xFF) + +// maj() and ch() functions for mixing the state. +#define maj(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) +#define ch(x, y, z) (((x) & (y)) ^ ((~(x)) & (z))) + +/** + * \brief Encrypts an 8-bit byte using Acorn128. + * + * \param state The state for the Acorn128 cipher. + * \param plaintext The plaintext byte. + * \param ca The ca constant. + * \param cb The cb constant. + * + * \return The ciphertext byte. + */ +static uint8_t acornEncrypt8 + (Acorn128State *state, uint8_t plaintext, uint8_t ca, uint8_t cb) +{ + // Extract out various sub-parts of the state as 8-bit bytes. + #define s_extract_8(name, shift) \ + ((uint8_t)(state->name##_l >> (shift))) + uint8_t s244 = s_extract_8(s6, 14); + uint8_t s235 = s_extract_8(s6, 5); + uint8_t s196 = s_extract_8(s5, 3); + uint8_t s160 = s_extract_8(s4, 6); + uint8_t s111 = s_extract_8(s3, 4); + uint8_t s66 = s_extract_8(s2, 5); + uint8_t s23 = s_extract_8(s1, 23); + uint8_t s12 = s_extract_8(s1, 12); + + // Update the LFSR's. + uint8_t s7_l = state->s7 ^ s235 ^ state->s6_l; + state->s6_l ^= s196 ^ ((uint8_t)(state->s5_l)); + state->s5_l ^= s160 ^ ((uint8_t)(state->s4_l)); + state->s4_l ^= s111 ^ ((uint8_t)(state->s3_l)); + state->s3_l ^= s66 ^ ((uint8_t)(state->s2_l)); + state->s2_l ^= s23 ^ ((uint8_t)(state->s1_l)); + + // Generate the next 8 keystream bits. + // k = S[12] ^ S[154] ^ maj(S[235], S[61], S[193]) + // ^ ch(S[230], S[111], S[66]) + uint8_t ks = s12 ^ state->s4_l ^ + maj(s235, state->s2_l, state->s5_l) ^ + ch(state->s6_l, s111, s66); + + // Generate the next 8 non-linear feedback bits. + // f = S[0] ^ ~S[107] ^ maj(S[244], S[23], S[160]) + // ^ (ca & S[196]) ^ (cb & ks) + // f ^= plaintext + uint8_t f = state->s1_l ^ (~state->s3_l) ^ + maj(s244, s23, s160) ^ (ca & s196) ^ (cb & ks); + f ^= plaintext; + + // Shift the state downwards by 8 bits. + #define s_shift_8(name1, name2, shift) \ + (state->name1##_l = (state->name1##_l >> 8) | \ + (((uint32_t)(state->name1##_h)) << 24), \ + state->name1##_h = (state->name1##_h >> 8) | \ + ((state->name2##_l & 0xFF) << ((shift) - 40))) + #define s_shift_8_mixed(name1, name2, shift) \ + (state->name1##_l = (state->name1##_l >> 8) | \ + (((uint32_t)(state->name1##_h)) << 24) | \ + (state->name2##_l << ((shift) - 8)), \ + state->name1##_h = ((state->name2##_l & 0xFF) >> (40 - (shift)))) + s7_l ^= (f << 4); + state->s7 = f >> 4; + s_shift_8(s1, s2, 61); + s_shift_8(s2, s3, 46); + s_shift_8(s3, s4, 47); + s_shift_8_mixed(s4, s5, 39); + s_shift_8_mixed(s5, s6, 37); + state->s6_l = (state->s6_l >> 8) | (state->s6_h << 24); + state->s6_h = (state->s6_h >> 8) | (((uint32_t)s7_l) << 19); + + // Return the ciphertext byte to the caller. + return plaintext ^ ks; +} + +/** + * \brief Encrypts a 32-bit word using Acorn128. + * + * \param state The state for the Acorn128 cipher. + * \param plaintext The plaintext word. + * \param ca The ca constant. + * \param cb The cb constant. + * + * \return The ciphertext word. + */ +static uint32_t acornEncrypt32 + (Acorn128State *state, uint32_t plaintext, uint32_t ca, uint32_t cb) +{ + // Extract out various sub-parts of the state as 32-bit words. + #define s_extract_32(name, shift) \ + ((state->name##_l >> (shift)) | \ + (((uint32_t)(state->name##_h)) << (32 - (shift)))) + uint32_t s244 = s_extract_32(s6, 14); + uint32_t s235 = s_extract_32(s6, 5); + uint32_t s196 = s_extract_32(s5, 3); + uint32_t s160 = s_extract_32(s4, 6); + uint32_t s111 = s_extract_32(s3, 4); + uint32_t s66 = s_extract_32(s2, 5); + uint32_t s23 = s_extract_32(s1, 23); + uint32_t s12 = s_extract_32(s1, 12); + + // Update the LFSR's. + uint32_t s7_l = state->s7 ^ s235 ^ state->s6_l; + state->s6_l ^= s196 ^ state->s5_l; + state->s5_l ^= s160 ^ state->s4_l; + state->s4_l ^= s111 ^ state->s3_l; + state->s3_l ^= s66 ^ state->s2_l; + state->s2_l ^= s23 ^ state->s1_l; + + // Generate the next 32 keystream bits. + // k = S[12] ^ S[154] ^ maj(S[235], S[61], S[193]) + // ^ ch(S[230], S[111], S[66]) + uint32_t ks = s12 ^ state->s4_l ^ + maj(s235, state->s2_l, state->s5_l) ^ + ch(state->s6_l, s111, s66); + + // Generate the next 32 non-linear feedback bits. + // f = S[0] ^ ~S[107] ^ maj(S[244], S[23], S[160]) + // ^ (ca & S[196]) ^ (cb & ks) + // f ^= plaintext + uint32_t f = state->s1_l ^ (~state->s3_l) ^ + maj(s244, s23, s160) ^ (ca & s196) ^ (cb & ks); + f ^= plaintext; + + // Shift the state downwards by 32 bits. + #define s_shift_32(name1, name2, shift) \ + (state->name1##_l = state->name1##_h | (state->name2##_l << (shift)), \ + state->name1##_h = (state->name2##_l >> (32 - (shift)))) + s7_l ^= (f << 4); + state->s7 = (uint8_t)(f >> 28); + s_shift_32(s1, s2, 29); + s_shift_32(s2, s3, 14); + s_shift_32(s3, s4, 15); + s_shift_32(s4, s5, 7); + s_shift_32(s5, s6, 5); + state->s6_l = state->s6_h | (s7_l << 27); + state->s6_h = s7_l >> 5; + + // Return the ciphertext word to the caller. + return plaintext ^ ks; +} + +/** + * \brief Encrypts a 32-bit word using Acorn128. + * + * \param state The state for the Acorn128 cipher. + * \param plaintext The plaintext word. + * + * \return The ciphertext word. + * + * This version assumes that ca = 1 and cb = 0. + */ +static inline uint32_t acornEncrypt32Fast + (Acorn128State *state, uint32_t plaintext) +{ + // Extract out various sub-parts of the state as 32-bit words. + #define s_extract_32(name, shift) \ + ((state->name##_l >> (shift)) | \ + (((uint32_t)(state->name##_h)) << (32 - (shift)))) + uint32_t s244 = s_extract_32(s6, 14); + uint32_t s235 = s_extract_32(s6, 5); + uint32_t s196 = s_extract_32(s5, 3); + uint32_t s160 = s_extract_32(s4, 6); + uint32_t s111 = s_extract_32(s3, 4); + uint32_t s66 = s_extract_32(s2, 5); + uint32_t s23 = s_extract_32(s1, 23); + uint32_t s12 = s_extract_32(s1, 12); + + // Update the LFSR's. + uint32_t s7_l = state->s7 ^ s235 ^ state->s6_l; + state->s6_l ^= s196 ^ state->s5_l; + state->s5_l ^= s160 ^ state->s4_l; + state->s4_l ^= s111 ^ state->s3_l; + state->s3_l ^= s66 ^ state->s2_l; + state->s2_l ^= s23 ^ state->s1_l; + + // Generate the next 32 keystream bits. + // k = S[12] ^ S[154] ^ maj(S[235], S[61], S[193]) + // ^ ch(S[230], S[111], S[66]) + uint32_t ks = s12 ^ state->s4_l ^ + maj(s235, state->s2_l, state->s5_l) ^ + ch(state->s6_l, s111, s66); + + // Generate the next 32 non-linear feedback bits. + // f = S[0] ^ ~S[107] ^ maj(S[244], S[23], S[160]) + // ^ (ca & S[196]) ^ (cb & ks) + // f ^= plaintext + // Note: ca will always be 1 and cb will always be 0. + uint32_t f = state->s1_l ^ (~state->s3_l) ^ maj(s244, s23, s160) ^ s196; + f ^= plaintext; + + // Shift the state downwards by 32 bits. + #define s_shift_32(name1, name2, shift) \ + (state->name1##_l = state->name1##_h | (state->name2##_l << (shift)), \ + state->name1##_h = (state->name2##_l >> (32 - (shift)))) + s7_l ^= (f << 4); + state->s7 = (uint8_t)(f >> 28); + s_shift_32(s1, s2, 29); + s_shift_32(s2, s3, 14); + s_shift_32(s3, s4, 15); + s_shift_32(s4, s5, 7); + s_shift_32(s5, s6, 5); + state->s6_l = state->s6_h | (s7_l << 27); + state->s6_h = s7_l >> 5; + + // Return the ciphertext word to the caller. + return plaintext ^ ks; +} + +/** + * \brief Decrypts an 8-bit byte using Acorn128. + * + * \param state The state for the Acorn128 cipher. + * \param ciphertext The ciphertext byte. + * + * \return The plaintext byte. + */ +static inline uint8_t acornDecrypt8(Acorn128State *state, uint8_t ciphertext) +{ + // Extract out various sub-parts of the state as 8-bit bytes. + #define s_extract_8(name, shift) \ + ((uint8_t)(state->name##_l >> (shift))) + uint8_t s244 = s_extract_8(s6, 14); + uint8_t s235 = s_extract_8(s6, 5); + uint8_t s196 = s_extract_8(s5, 3); + uint8_t s160 = s_extract_8(s4, 6); + uint8_t s111 = s_extract_8(s3, 4); + uint8_t s66 = s_extract_8(s2, 5); + uint8_t s23 = s_extract_8(s1, 23); + uint8_t s12 = s_extract_8(s1, 12); + + // Update the LFSR's. + uint8_t s7_l = state->s7 ^ s235 ^ state->s6_l; + state->s6_l ^= s196 ^ ((uint8_t)(state->s5_l)); + state->s5_l ^= s160 ^ ((uint8_t)(state->s4_l)); + state->s4_l ^= s111 ^ ((uint8_t)(state->s3_l)); + state->s3_l ^= s66 ^ ((uint8_t)(state->s2_l)); + state->s2_l ^= s23 ^ ((uint8_t)(state->s1_l)); + + // Generate the next 8 keystream bits and decrypt the ciphertext. + // k = S[12] ^ S[154] ^ maj(S[235], S[61], S[193]) + // ^ ch(S[230], S[111], S[66]) + uint8_t ks = s12 ^ state->s4_l ^ + maj(s235, state->s2_l, state->s5_l) ^ + ch(state->s6_l, s111, s66); + uint8_t plaintext = ciphertext ^ ks; + + // Generate the next 8 non-linear feedback bits. + // f = S[0] ^ ~S[107] ^ maj(S[244], S[23], S[160]) + // ^ (ca & S[196]) ^ (cb & ks) + // f ^= plaintext + // Note: ca will always be 1 and cb will always be 0. + uint8_t f = state->s1_l ^ (~state->s3_l) ^ maj(s244, s23, s160) ^ s196; + f ^= plaintext; + + // Shift the state downwards by 8 bits. + #define s_shift_8(name1, name2, shift) \ + (state->name1##_l = (state->name1##_l >> 8) | \ + (((uint32_t)(state->name1##_h)) << 24), \ + state->name1##_h = (state->name1##_h >> 8) | \ + ((state->name2##_l & 0xFF) << ((shift) - 40))) + #define s_shift_8_mixed(name1, name2, shift) \ + (state->name1##_l = (state->name1##_l >> 8) | \ + (((uint32_t)(state->name1##_h)) << 24) | \ + (state->name2##_l << ((shift) - 8)), \ + state->name1##_h = ((state->name2##_l & 0xFF) >> (40 - (shift)))) + s7_l ^= (f << 4); + state->s7 = f >> 4; + s_shift_8(s1, s2, 61); + s_shift_8(s2, s3, 46); + s_shift_8(s3, s4, 47); + s_shift_8_mixed(s4, s5, 39); + s_shift_8_mixed(s5, s6, 37); + state->s6_l = (state->s6_l >> 8) | (state->s6_h << 24); + state->s6_h = (state->s6_h >> 8) | (((uint32_t)s7_l) << 19); + + // Return the plaintext byte to the caller. + return plaintext; +} + +/** + * \brief Decrypts a 32-bit word using Acorn128. + * + * \param state The state for the Acorn128 cipher. + * \param ciphertext The ciphertext word. + * + * \return The plaintext word. + */ +static inline uint32_t acornDecrypt32(Acorn128State *state, uint32_t ciphertext) +{ + // Extract out various sub-parts of the state as 32-bit words. + #define s_extract_32(name, shift) \ + ((state->name##_l >> (shift)) | \ + (((uint32_t)(state->name##_h)) << (32 - (shift)))) + uint32_t s244 = s_extract_32(s6, 14); + uint32_t s235 = s_extract_32(s6, 5); + uint32_t s196 = s_extract_32(s5, 3); + uint32_t s160 = s_extract_32(s4, 6); + uint32_t s111 = s_extract_32(s3, 4); + uint32_t s66 = s_extract_32(s2, 5); + uint32_t s23 = s_extract_32(s1, 23); + uint32_t s12 = s_extract_32(s1, 12); + + // Update the LFSR's. + uint32_t s7_l = state->s7 ^ s235 ^ state->s6_l; + state->s6_l ^= s196 ^ state->s5_l; + state->s5_l ^= s160 ^ state->s4_l; + state->s4_l ^= s111 ^ state->s3_l; + state->s3_l ^= s66 ^ state->s2_l; + state->s2_l ^= s23 ^ state->s1_l; + + // Generate the next 32 keystream bits and decrypt the ciphertext. + // k = S[12] ^ S[154] ^ maj(S[235], S[61], S[193]) + // ^ ch(S[230], S[111], S[66]) + uint32_t ks = s12 ^ state->s4_l ^ + maj(s235, state->s2_l, state->s5_l) ^ + ch(state->s6_l, s111, s66); + uint32_t plaintext = ciphertext ^ ks; + + // Generate the next 32 non-linear feedback bits. + // f = S[0] ^ ~S[107] ^ maj(S[244], S[23], S[160]) + // ^ (ca & S[196]) ^ (cb & ks) + // f ^= plaintext + // Note: ca will always be 1 and cb will always be 0. + uint32_t f = state->s1_l ^ (~state->s3_l) ^ maj(s244, s23, s160) ^ s196; + f ^= plaintext; + + // Shift the state downwards by 32 bits. + #define s_shift_32(name1, name2, shift) \ + (state->name1##_l = state->name1##_h | (state->name2##_l << (shift)), \ + state->name1##_h = (state->name2##_l >> (32 - (shift)))) + s7_l ^= (f << 4); + state->s7 = (uint8_t)(f >> 28); + s_shift_32(s1, s2, 29); + s_shift_32(s2, s3, 14); + s_shift_32(s3, s4, 15); + s_shift_32(s4, s5, 7); + s_shift_32(s5, s6, 5); + state->s6_l = state->s6_h | (s7_l << 27); + state->s6_h = s7_l >> 5; + + // Return the plaintext word to the caller. + return plaintext; +} + +/** + * \brief Adds 256 bits of padding to the Acorn128 state. + * + * \param state The state for the Acorn128 cipher. + * \param cb The cb constant for the padding block. + */ +static void acornPad(Acorn128State *state, uint32_t cb) +{ + acornEncrypt32(state, 1, CA_1, cb); + acornEncrypt32(state, 0, CA_1, cb); + acornEncrypt32(state, 0, CA_1, cb); + acornEncrypt32(state, 0, CA_1, cb); + acornEncrypt32(state, 0, CA_0, cb); + acornEncrypt32(state, 0, CA_0, cb); + acornEncrypt32(state, 0, CA_0, cb); + acornEncrypt32(state, 0, CA_0, cb); +} + +bool Acorn128::setKey(const uint8_t *key, size_t len) +{ + // We cannot initialize the key block until we also have the IV. + // So we simply validate the length and save the key away for later. + if (len == 16) { + memcpy(state.k, key, 16); +#if !defined(CRYPTO_LITTLE_ENDIAN) + state.k[0] = le32toh(state.k[0]); + state.k[1] = le32toh(state.k[1]); + state.k[2] = le32toh(state.k[2]); + state.k[3] = le32toh(state.k[3]); +#endif + return true; + } else { + return false; + } +} + +bool Acorn128::setIV(const uint8_t *iv, size_t len) +{ + if (len != 16) + return false; + + // Unpack the iv into four 32-bit words. + uint32_t ivwords[4]; + memcpy(ivwords, iv, 16); +#if !defined(CRYPTO_LITTLE_ENDIAN) + ivwords[0] = le32toh(ivwords[0]); + ivwords[1] = le32toh(ivwords[1]); + ivwords[2] = le32toh(ivwords[2]); + ivwords[3] = le32toh(ivwords[3]); +#endif + + // Initialize the state to zero. + state.s1_l = 0; + state.s1_h = 0; + state.s2_l = 0; + state.s2_h = 0; + state.s3_h = 0; + state.s3_l = 0; + state.s4_l = 0; + state.s4_h = 0; + state.s5_h = 0; + state.s5_l = 0; + state.s6_l = 0; + state.s6_h = 0; + state.s7 = 0; + state.authDone = 0; + + // Run the cipher for 1792 steps, 32 at a time, + // which mixes the key and IV into the cipher state. + acornEncrypt32(&state, state.k[0], CA_1, CB_1); + acornEncrypt32(&state, state.k[1], CA_1, CB_1); + acornEncrypt32(&state, state.k[2], CA_1, CB_1); + acornEncrypt32(&state, state.k[3], CA_1, CB_1); + acornEncrypt32(&state, ivwords[0], CA_1, CB_1); + acornEncrypt32(&state, ivwords[1], CA_1, CB_1); + acornEncrypt32(&state, ivwords[2], CA_1, CB_1); + acornEncrypt32(&state, ivwords[3], CA_1, CB_1); + acornEncrypt32(&state, state.k[0] ^ 0x00000001, CA_1, CB_1); + acornEncrypt32(&state, state.k[1], CA_1, CB_1); + acornEncrypt32(&state, state.k[2], CA_1, CB_1); + acornEncrypt32(&state, state.k[3], CA_1, CB_1); + for (uint8_t i = 0; i < 11; ++i) { + acornEncrypt32(&state, state.k[0], CA_1, CB_1); + acornEncrypt32(&state, state.k[1], CA_1, CB_1); + acornEncrypt32(&state, state.k[2], CA_1, CB_1); + acornEncrypt32(&state, state.k[3], CA_1, CB_1); + } + + // Clean up and exit. + clean(ivwords); + return true; +} + +void Acorn128::encrypt(uint8_t *output, const uint8_t *input, size_t len) +{ + uint32_t temp; + if (!state.authDone) { + acornPad(&state, CB_1); + state.authDone = 1; + } + while (len >= 4) { + uint32_t temp = ((uint32_t)input[0]) | + (((uint32_t)input[1]) << 8) | + (((uint32_t)input[2]) << 16) | + (((uint32_t)input[3]) << 24); + temp = acornEncrypt32Fast(&state, temp); + output[0] = (uint8_t)temp; + output[1] = (uint8_t)(temp >> 8); + output[2] = (uint8_t)(temp >> 16); + output[3] = (uint8_t)(temp >> 24); + input += 4; + output += 4; + len -= 4; + } + while (len > 0) { + *output++ = acornEncrypt8(&state, *input++, CA_1_BYTE, CB_0_BYTE); + --len; + } +} + +void Acorn128::decrypt(uint8_t *output, const uint8_t *input, size_t len) +{ + uint32_t temp; + if (!state.authDone) { + acornPad(&state, CB_1); + state.authDone = 1; + } + while (len >= 4) { + uint32_t temp = ((uint32_t)input[0]) | + (((uint32_t)input[1]) << 8) | + (((uint32_t)input[2]) << 16) | + (((uint32_t)input[3]) << 24); + temp = acornDecrypt32(&state, temp); + output[0] = (uint8_t)temp; + output[1] = (uint8_t)(temp >> 8); + output[2] = (uint8_t)(temp >> 16); + output[3] = (uint8_t)(temp >> 24); + input += 4; + output += 4; + len -= 4; + } + while (len > 0) { + *output++ = acornDecrypt8(&state, *input++); + --len; + } +} + +void Acorn128::addAuthData(const void *data, size_t len) +{ + // Cannot add any more auth data if we've started to encrypt or decrypt. + if (state.authDone) + return; + + // Encrypt the auth data with ca = 1, cb = 1. + const uint8_t *input = (const uint8_t *)data; + while (len >= 4) { + uint32_t temp = ((uint32_t)input[0]) | + (((uint32_t)input[1]) << 8) | + (((uint32_t)input[2]) << 16) | + (((uint32_t)input[3]) << 24); + acornEncrypt32(&state, temp, CA_1, CB_1); + input += 4; + len -= 4; + } + while (len > 0) { + acornEncrypt8(&state, *input++, CA_1_BYTE, CB_1_BYTE); + --len; + } +} + +void Acorn128::computeTag(void *tag, size_t len) +{ + // Finalize the data and apply padding. + if (!state.authDone) + acornPad(&state, CB_1); + acornPad(&state, CB_0); + + // Encrypt 768 zero bits and extract the last 128 for the tag. + uint32_t temp[4]; + for (uint8_t i = 0; i < 20; ++i) + acornEncrypt32(&state, 0, CA_1, CB_1); + temp[0] = acornEncrypt32(&state, 0, CA_1, CB_1); + temp[1] = acornEncrypt32(&state, 0, CA_1, CB_1); + temp[2] = acornEncrypt32(&state, 0, CA_1, CB_1); + temp[3] = acornEncrypt32(&state, 0, CA_1, CB_1); +#if !defined(CRYPTO_LITTLE_ENDIAN) + temp[0] = htole32(temp[0]); + temp[1] = htole32(temp[1]); + temp[2] = htole32(temp[2]); + temp[3] = htole32(temp[3]); +#endif + + // Truncate to the requested length and return the value. + if (len > 16) + len = 16; + memcpy(tag, temp, len); + clean(temp); +} + +bool Acorn128::checkTag(const void *tag, size_t len) +{ + // Can never match if the expected tag length is too long. + if (len > 16) + return false; + + // Compute the authentication tag and check it. + uint8_t temp[16]; + computeTag(temp, len); + bool equal = secure_compare(temp, tag, len); + clean(temp); + return equal; +} + +/** + * \brief Clears all security-sensitive state from this cipher object. + */ +void Acorn128::clear() +{ + clean(state); +} diff --git a/libraries/CryptoLW/src/Acorn128.h b/libraries/CryptoLW/src/Acorn128.h new file mode 100644 index 00000000..1bca355e --- /dev/null +++ b/libraries/CryptoLW/src/Acorn128.h @@ -0,0 +1,87 @@ +/* + * 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_ACORN128_H +#define CRYPTO_ACORN128_H + +#include "AuthenticatedCipher.h" + +/** @cond acorn128_state */ + +// The ACORN-128 state consists of 293 bits split across six +// Linear Feedback Shift Registers (LFSR's) and 4 bits spare. +// In this implementation, each LFSR is represented by a +// 48-bit or 64-bit register split into 32/16-bit words. +// The optimized reference implementation from the algorithm's +// authors uses 7 uint64_t registers, for a total state size +// of 448 bits. This version uses 328 bits for same data and +// should be efficient on 8-bit and 32-bit microcontrollers. +typedef struct +{ + uint32_t k[4]; // Cached copy of the key for multiple requests. + uint32_t s1_l; // LFSR1, 61 bits, 0..60, low word + uint32_t s1_h; // LFSR1, high word + uint32_t s2_l; // LFSR2, 46 bits, 61..106, low word + uint16_t s2_h; // LFSR2, high word + uint16_t s3_h; // LFSR3, 47 bits, 107..153, high word + uint32_t s3_l; // LFSR3, low word + uint32_t s4_l; // LFSR4, 39 bits, 154..192, low word + uint16_t s4_h; // LFSR4, high word + uint16_t s5_h; // LFSR5, 37 bits, 193..229, high word + uint32_t s5_l; // LFSR5, low word + uint32_t s6_l; // LFSR6, 59 bits, 230..288, low word + uint32_t s6_h; // LFSR6, high word + uint8_t s7; // Top most 4 bits, 289..292 + uint8_t authDone; // Non-zero once authentication is done. + +} Acorn128State; + +/** @endcond */ + +class Acorn128 : public AuthenticatedCipher +{ +public: + Acorn128(); + virtual ~Acorn128(); + + 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: + Acorn128State state; +}; + +#endif diff --git a/libraries/CryptoLW/src/CryptoLW.h b/libraries/CryptoLW/src/CryptoLW.h new file mode 100644 index 00000000..b0815a5d --- /dev/null +++ b/libraries/CryptoLW/src/CryptoLW.h @@ -0,0 +1,29 @@ +/* + * 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_LW_H +#define CRYPTO_LW_H + +// This header exists to make the Arudino IDE add the library to the +// include and link paths when the sketch includes . + +#endif