From ee43158b64ca898ec5dd544efc00c0dc5029df39 Mon Sep 17 00:00:00 2001 From: Rhys Weatherley Date: Mon, 30 Nov 2015 18:23:21 +1000 Subject: [PATCH] Speck block cipher --- doc/crypto.dox | 5 +- doc/mainpage.dox | 2 +- libraries/Crypto/Speck.cpp | 184 ++++++++++++++++++ libraries/Crypto/Speck.h | 49 +++++ .../Crypto/examples/TestSpeck/TestSpeck.ino | 166 ++++++++++++++++ 5 files changed, 404 insertions(+), 2 deletions(-) create mode 100644 libraries/Crypto/Speck.cpp create mode 100644 libraries/Crypto/Speck.h create mode 100644 libraries/Crypto/examples/TestSpeck/TestSpeck.ino 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.87us14.88us43.74us132 ChaCha (12 rounds)10.38us10.38us43.74us132 ChaCha (8 rounds)8.13us8.14us43.74us132 +Speck (128-bit key)N.NNusN.NNusN.NNus275 +Speck (192-bit key)N.NNusN.NNusN.NNus275 +Speck (256-bit key)N.NNusN.NNusN.NNus275 AEAD AlgorithmEncryption (per byte)Decryption (per byte)Key SetupState Size (bytes) ChaChaPoly41.23us41.23us902.55us255 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() +{ +}