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.99us | 16.99us | 322.92us | 344 |
EAX<Speck> (256-bit key) | 2.80us | 2.80us | 81.63us | 384 |
EAX<SpeckTiny> (256-bit key) | 6.69us | 6.69us | 110.91us | 144 |
+Acorn128 | 0.75us | 0.75us | 175.70us | 64 |
|
Hash Algorithm | Hashing (per byte) | Finalization | | State Size (bytes) |
SHA256 | 1.15us | 76.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