diff --git a/doc/crypto.dox b/doc/crypto.dox
index bd71e448..b053ab84 100644
--- a/doc/crypto.dox
+++ b/doc/crypto.dox
@@ -26,7 +26,7 @@
\section crypto_algorithms Supported Algorithms
-\li Block ciphers: AES128, AES192, AES256
+\li Block ciphers: AES128, AES192, AES256, Speck
\li Block cipher modes: CTR, CFB, CBC, OFB, GCM
\li Stream ciphers: ChaCha
\li Authenticated encryption with associated data (AEAD): ChaChaPoly, GCM
@@ -71,6 +71,9 @@ Ardunino Mega 2560 running at 16 MHz are similar:
ChaCha (20 rounds) | 14.87us | 14.88us | 43.74us | 132 |
ChaCha (12 rounds) | 10.38us | 10.38us | 43.74us | 132 |
ChaCha (8 rounds) | 8.13us | 8.14us | 43.74us | 132 |
+Speck (128-bit key) | N.NNus | N.NNus | N.NNus | 275 |
+Speck (192-bit key) | N.NNus | N.NNus | N.NNus | 275 |
+Speck (256-bit key) | N.NNus | N.NNus | N.NNus | 275 |
|
AEAD Algorithm | Encryption (per byte) | Decryption (per byte) | Key Setup | State Size (bytes) |
ChaChaPoly | 41.23us | 41.23us | 902.55us | 255 |
diff --git a/doc/mainpage.dox b/doc/mainpage.dox
index 8b2ba5f7..cfc150c3 100644
--- a/doc/mainpage.dox
+++ b/doc/mainpage.dox
@@ -91,7 +91,7 @@ realtime clock and the LCD library to implement an alarm clock.
\section main_Crypto Cryptographic Library
-\li Block ciphers: AES128, AES192, AES256
+\li Block ciphers: AES128, AES192, AES256, Speck
\li Block cipher modes: CTR, CFB, CBC, OFB, GCM
\li Stream ciphers: ChaCha
\li Authenticated encryption with associated data (AEAD): ChaChaPoly, GCM
diff --git a/libraries/Crypto/Speck.cpp b/libraries/Crypto/Speck.cpp
new file mode 100644
index 00000000..7bc4b9a5
--- /dev/null
+++ b/libraries/Crypto/Speck.cpp
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2015 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 "Speck.h"
+#include "Crypto.h"
+#include "utility/RotateUtil.h"
+#include "utility/EndianUtil.h"
+#include
+
+/**
+ * \class Speck Speck.h
+ * \brief Speck block cipher with a 128-bit block size.
+ *
+ * Speck is a family of lightweight block ciphers designed by the
+ * National Security Agency (NSA). The ciphers are highly optimized
+ * for software implementation on microcontrollers.
+ *
+ * This class implements the Speck family that uses 128-bit block sizes
+ * with 128-bit, 192-bit, or 256-bit key sizes. Other Speck families support
+ * smaller block sizes of 32, 48, 64, or 96 bits but such block sizes are
+ * really too small for use in modern cryptosystems.
+ *
+ * \note Current crytoanalysis (up until 2015) has not revealed any obvious
+ * weaknesses in the full-round version of Speck. But if you are wary of
+ * ciphers designed by the NSA, then use ChaCha or AES instead.
+ *
+ * References: https://en.wikipedia.org/wiki/Speck_%28cipher%29,
+ * http://eprint.iacr.org/2013/404
+ */
+
+/**
+ * \brief Constructs a Speck block cipher with no initial key.
+ *
+ * This constructor must be followed by a call to setKey() before the
+ * block cipher can be used for encryption or decryption.
+ */
+Speck::Speck()
+ : rounds(32)
+{
+}
+
+Speck::~Speck()
+{
+ clean(k);
+}
+
+size_t Speck::blockSize() const
+{
+ return 16;
+}
+
+size_t Speck::keySize() const
+{
+ // Also supports 128-bit and 192-bit, but we only report 256-bit.
+ return 32;
+}
+
+// Pack/unpack big-endian 64-bit quantities.
+#if defined(__AVR__)
+#define pack64(data, value) \
+ do { \
+ const uint8_t *src = (const uint8_t *)&(value); \
+ (data)[0] = src[7]; \
+ (data)[1] = src[6]; \
+ (data)[2] = src[5]; \
+ (data)[3] = src[4]; \
+ (data)[4] = src[3]; \
+ (data)[5] = src[2]; \
+ (data)[6] = src[1]; \
+ (data)[7] = src[0]; \
+ } while (0)
+#define unpack64(value, data) \
+ do { \
+ uint8_t *dest = (uint8_t *)&(value); \
+ dest[0] = (data)[7]; \
+ dest[1] = (data)[6]; \
+ dest[2] = (data)[5]; \
+ dest[3] = (data)[4]; \
+ dest[4] = (data)[3]; \
+ dest[5] = (data)[2]; \
+ dest[6] = (data)[1]; \
+ dest[7] = (data)[0]; \
+ } while (0)
+#else
+#define pack64(data, value) \
+ do { \
+ uint64_t v = htobe64((value)); \
+ memcpy((data), &v, sizeof(uint64_t)); \
+ } while (0)
+#define unpack64(value, data) \
+ do { \
+ memcpy(&(value), (data), sizeof(uint64_t)); \
+ (value) = be64toh((value)); \
+ } while (0)
+#endif
+
+bool Speck::setKey(const uint8_t *key, size_t len)
+{
+ uint64_t l[4];
+ uint8_t m;
+ if (len == 32) {
+ m = 4;
+ unpack64(l[2], key);
+ unpack64(l[1], key + 8);
+ unpack64(l[0], key + 16);
+ unpack64(k[0], key + 24);
+ } else if (len == 24) {
+ m = 3;
+ unpack64(l[1], key);
+ unpack64(l[0], key + 8);
+ unpack64(k[0], key + 16);
+ } else if (len == 16) {
+ m = 2;
+ unpack64(l[0], key);
+ unpack64(k[0], key + 8);
+ } else {
+ return false;
+ }
+ rounds = 30 + m;
+ uint8_t li_in = 0;
+ uint8_t li_out = m - 1;
+ for (uint8_t i = 0; i < (rounds - 1); ++i) {
+ l[li_out] = (k[i] + rightRotate8_64(l[li_in])) ^ i;
+ k[i + 1] = leftRotate3_64(k[i]) ^ l[li_out];
+ if ((++li_in) >= m)
+ li_in = 0;
+ if ((++li_out) >= m)
+ li_out = 0;
+ }
+ clean(l);
+ return true;
+}
+
+void Speck::encryptBlock(uint8_t *output, const uint8_t *input)
+{
+ uint64_t x, y;
+ const uint64_t *s = k;
+ unpack64(x, input);
+ unpack64(y, input + 8);
+ for (uint8_t round = rounds; round > 0; --round, ++s) {
+ x = (rightRotate8_64(x) + y) ^ s[0];
+ y = leftRotate3_64(y) ^ x;
+ }
+ pack64(output, x);
+ pack64(output + 8, y);
+}
+
+void Speck::decryptBlock(uint8_t *output, const uint8_t *input)
+{
+ uint64_t x, y;
+ const uint64_t *s = k + rounds - 1;
+ unpack64(x, input);
+ unpack64(y, input + 8);
+ for (uint8_t round = rounds; round > 0; --round, --s) {
+ y = rightRotate3_64(x ^ y);
+ x = leftRotate8_64((x ^ s[0]) - y);
+ }
+ pack64(output, x);
+ pack64(output + 8, y);
+}
+
+void Speck::clear()
+{
+ clean(k);
+}
diff --git a/libraries/Crypto/Speck.h b/libraries/Crypto/Speck.h
new file mode 100644
index 00000000..915432ed
--- /dev/null
+++ b/libraries/Crypto/Speck.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2015 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_SPECK_H
+#define CRYPTO_SPECK_H
+
+#include "BlockCipher.h"
+
+class Speck : public BlockCipher
+{
+public:
+ Speck();
+ virtual ~Speck();
+
+ size_t blockSize() const;
+ size_t keySize() const;
+
+ bool setKey(const uint8_t *key, size_t len);
+
+ void encryptBlock(uint8_t *output, const uint8_t *input);
+ void decryptBlock(uint8_t *output, const uint8_t *input);
+
+ void clear();
+
+private:
+ uint64_t k[34];
+ uint8_t rounds;
+};
+
+#endif
diff --git a/libraries/Crypto/examples/TestSpeck/TestSpeck.ino b/libraries/Crypto/examples/TestSpeck/TestSpeck.ino
new file mode 100644
index 00000000..00eb5c09
--- /dev/null
+++ b/libraries/Crypto/examples/TestSpeck/TestSpeck.ino
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2015 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 Speck implementation to verify correct behaviour.
+*/
+
+#include
+#include
+#include
+
+struct TestVector
+{
+ const char *name;
+ byte key[32];
+ byte plaintext[16];
+ byte ciphertext[16];
+};
+
+// Define the test vectors from http://eprint.iacr.org/2013/404
+static TestVector const testVectorSpeck128 = {
+ .name = "Speck-128-ECB",
+ .key = {0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
+ 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00},
+ .plaintext = {0x6c, 0x61, 0x76, 0x69, 0x75, 0x71, 0x65, 0x20,
+ 0x74, 0x69, 0x20, 0x65, 0x64, 0x61, 0x6d, 0x20},
+ .ciphertext = {0xa6, 0x5d, 0x98, 0x51, 0x79, 0x78, 0x32, 0x65,
+ 0x78, 0x60, 0xfe, 0xdf, 0x5c, 0x57, 0x0d, 0x18}
+};
+static TestVector const testVectorSpeck192 = {
+ .name = "Speck-192-ECB",
+ .key = {0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10,
+ 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
+ 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00},
+ .plaintext = {0x72, 0x61, 0x48, 0x20, 0x66, 0x65, 0x69, 0x68,
+ 0x43, 0x20, 0x6f, 0x74, 0x20, 0x74, 0x6e, 0x65},
+ .ciphertext = {0x1b, 0xe4, 0xcf, 0x3a, 0x13, 0x13, 0x55, 0x66,
+ 0xf9, 0xbc, 0x18, 0x5d, 0xe0, 0x3c, 0x18, 0x86}
+};
+static TestVector const testVectorSpeck256 = {
+ .name = "Speck-256-ECB",
+ .key = {0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18,
+ 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10,
+ 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
+ 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00},
+ .plaintext = {0x65, 0x73, 0x6f, 0x68, 0x74, 0x20, 0x6e, 0x49,
+ 0x20, 0x2e, 0x72, 0x65, 0x6e, 0x6f, 0x6f, 0x70},
+ .ciphertext = {0x41, 0x09, 0x01, 0x04, 0x05, 0xc0, 0xf5, 0x3e,
+ 0x4e, 0xee, 0xb4, 0x8d, 0x9c, 0x18, 0x8f, 0x43}
+};
+
+Speck speck;
+
+byte buffer[16];
+
+void testCipher(BlockCipher *cipher, const struct TestVector *test, size_t keySize)
+{
+ Serial.print(test->name);
+ Serial.print(" Encryption ... ");
+ cipher->setKey(test->key, keySize);
+ cipher->encryptBlock(buffer, test->plaintext);
+ if (memcmp(buffer, test->ciphertext, 16) == 0)
+ Serial.println("Passed");
+ else
+ Serial.println("Failed");
+
+ Serial.print(test->name);
+ Serial.print(" Decryption ... ");
+ cipher->decryptBlock(buffer, test->ciphertext);
+ if (memcmp(buffer, test->plaintext, 16) == 0)
+ Serial.println("Passed");
+ else
+ Serial.println("Failed");
+}
+
+void perfCipher(BlockCipher *cipher, const struct TestVector *test, size_t keySize)
+{
+ unsigned long start;
+ unsigned long elapsed;
+ int count;
+
+ Serial.print(test->name);
+ Serial.print(" Set Key ... ");
+ start = micros();
+ for (count = 0; count < 10000; ++count) {
+ cipher->setKey(test->key, keySize);
+ }
+ elapsed = micros() - start;
+ Serial.print(elapsed / 10000.0);
+ Serial.print("us per operation, ");
+ Serial.print((10000.0 * 1000000.0) / elapsed);
+ Serial.println(" per second");
+
+ Serial.print(test->name);
+ Serial.print(" Encrypt ... ");
+ start = micros();
+ for (count = 0; count < 5000; ++count) {
+ cipher->encryptBlock(buffer, buffer);
+ }
+ elapsed = micros() - start;
+ Serial.print(elapsed / (5000.0 * 16.0));
+ Serial.print("us per byte, ");
+ Serial.print((16.0 * 5000.0 * 1000000.0) / elapsed);
+ Serial.println(" bytes per second");
+
+ Serial.print(test->name);
+ Serial.print(" Decrypt ... ");
+ start = micros();
+ for (count = 0; count < 5000; ++count) {
+ cipher->decryptBlock(buffer, buffer);
+ }
+ elapsed = micros() - start;
+ Serial.print(elapsed / (5000.0 * 16.0));
+ Serial.print("us per byte, ");
+ Serial.print((16.0 * 5000.0 * 1000000.0) / elapsed);
+ Serial.println(" bytes per second");
+
+ Serial.println();
+}
+
+void setup()
+{
+ Serial.begin(9600);
+
+ Serial.println();
+
+ Serial.println("State Sizes:");
+ Serial.print("Speck ... ");
+ Serial.println(sizeof(Speck));
+ Serial.println();
+
+ Serial.println("Test Vectors:");
+ testCipher(&speck, &testVectorSpeck128, 16);
+ testCipher(&speck, &testVectorSpeck192, 24);
+ testCipher(&speck, &testVectorSpeck256, 32);
+
+ Serial.println();
+
+ Serial.println("Performance Tests:");
+ perfCipher(&speck, &testVectorSpeck128, 16);
+ perfCipher(&speck, &testVectorSpeck192, 24);
+ perfCipher(&speck, &testVectorSpeck256, 32);
+}
+
+void loop()
+{
+}