1
0
mirror of https://github.com/taigrr/arduinolibs synced 2025-01-18 04:33:12 -08:00

Move Speck from Crypto to CryptoLW

This commit is contained in:
Rhys Weatherley
2018-04-26 07:10:07 +10:00
parent 60ac9c4d6b
commit a03d95e7b4
13 changed files with 7 additions and 4 deletions

View File

@@ -0,0 +1,210 @@
/*
* 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 <Crypto.h>
#include <Speck.h>
#include <SpeckSmall.h>
#include <SpeckTiny.h>
#include <string.h>
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;
SpeckSmall speckSmall;
SpeckTiny speckTiny;
byte buffer[16];
void testCipher(BlockCipher *cipher, const struct TestVector *test, size_t keySize, bool decryption = true)
{
crypto_feed_watchdog();
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");
if (!decryption)
return;
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, bool decryption = true)
{
unsigned long start;
unsigned long elapsed;
int count;
crypto_feed_watchdog();
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");
if (!decryption) {
Serial.println();
return;
}
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.print("SpeckSmall ... ");
Serial.println(sizeof(SpeckSmall));
Serial.print("SpeckTiny ... ");
Serial.println(sizeof(SpeckTiny));
Serial.println();
Serial.println("Speck Test Vectors:");
testCipher(&speck, &testVectorSpeck128, 16);
testCipher(&speck, &testVectorSpeck192, 24);
testCipher(&speck, &testVectorSpeck256, 32);
Serial.println();
Serial.println("SpeckSmall Test Vectors:");
testCipher(&speckSmall, &testVectorSpeck128, 16);
testCipher(&speckSmall, &testVectorSpeck192, 24);
testCipher(&speckSmall, &testVectorSpeck256, 32);
Serial.println();
Serial.println("SpeckTiny Test Vectors:");
testCipher(&speckTiny, &testVectorSpeck128, 16, false);
testCipher(&speckTiny, &testVectorSpeck192, 24, false);
testCipher(&speckTiny, &testVectorSpeck256, 32, false);
Serial.println();
Serial.println("Speck Performance Tests:");
perfCipher(&speck, &testVectorSpeck128, 16);
perfCipher(&speck, &testVectorSpeck192, 24);
perfCipher(&speck, &testVectorSpeck256, 32);
Serial.println("SpeckSmall Performance Tests:");
perfCipher(&speckSmall, &testVectorSpeck128, 16);
perfCipher(&speckSmall, &testVectorSpeck192, 24);
perfCipher(&speckSmall, &testVectorSpeck256, 32);
Serial.println("SpeckTiny Performance Tests:");
perfCipher(&speckTiny, &testVectorSpeck128, 16, false);
perfCipher(&speckTiny, &testVectorSpeck192, 24, false);
perfCipher(&speckTiny, &testVectorSpeck256, 32, false);
}
void loop()
{
}

View File

@@ -1 +1,4 @@
Acorn128 KEYWORD1
Speck KEYWORD1
SpeckSmall KEYWORD1
SpeckTiny KEYWORD1

View File

@@ -1,7 +1,7 @@
{
"name": "CryptoLW",
"version": "0.1.6",
"keywords": "Acorn128",
"keywords": "Acorn128,Speck,SpeckSmall,SpeckTiny",
"description": "Light-weight ciphers for the Arduino Cryptography Library",
"authors":
{

View File

@@ -0,0 +1,553 @@
/*
* 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 <string.h>
/**
* \class Speck Speck.h <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
* 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.
*
* The SpeckTiny and SpeckSmall classes provide alternative implementations
* that have reduced RAM and flash size requirements at the cost of some
* features and performance.
*
* References: https://en.wikipedia.org/wiki/Speck_%28cipher%29,
* http://eprint.iacr.org/2013/404
*
* \sa SpeckTiny, SpeckSmall
*/
// The "avr-gcc" compiler doesn't do a very good job of compiling
// code involving 64-bit values. So we have to use inline assembly.
// It also helps to break the state up into 32-bit quantities
// because "asm" supports register names like %A0, %B0, %C0, %D0
// for the bytes in a 32-bit quantity, but it does not support
// %E0, %F0, %G0, %H0 for the high bytes of a 64-bit quantity.
#if defined(__AVR__)
#define USE_AVR_INLINE_ASM 1
#endif
/**
* \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 byte-aligned big-endian 64-bit quantities.
#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)
bool Speck::setKey(const uint8_t *key, size_t len)
{
#if USE_AVR_INLINE_ASM
// Automatically generated by the genspeck tool.
uint64_t l[4];
uint8_t m, mb;
if (len == 32) {
m = 4;
mb = 3 * 8;
} else if (len == 24) {
m = 3;
mb = 2 * 8;
} else if (len == 16) {
m = 2;
mb = 8;
} else {
return false;
}
rounds = 30 + m;
uint8_t r = rounds - 1;
__asm__ __volatile__ (
"ld __tmp_reg__,-X\n"
"st Z+,__tmp_reg__\n"
"ld __tmp_reg__,-X\n"
"st Z+,__tmp_reg__\n"
"ld __tmp_reg__,-X\n"
"st Z+,__tmp_reg__\n"
"ld __tmp_reg__,-X\n"
"st Z+,__tmp_reg__\n"
"ld __tmp_reg__,-X\n"
"st Z+,__tmp_reg__\n"
"ld __tmp_reg__,-X\n"
"st Z+,__tmp_reg__\n"
"ld __tmp_reg__,-X\n"
"st Z+,__tmp_reg__\n"
"ld __tmp_reg__,-X\n"
"st Z+,__tmp_reg__\n"
"sbiw r30,8\n"
"movw r10,r30\n"
"movw r30,%A2\n"
"ldd r8,%3\n"
"1:\n"
"ld __tmp_reg__,-X\n"
"st Z+,__tmp_reg__\n"
"dec r8\n"
"brne 1b\n"
"movw r26,%A2\n"
"movw r30,r10\n"
"clr %A2\n"
"ldd %B2,%3\n"
"clr r25\n"
"ld r16,Z+\n"
"ld r17,Z+\n"
"ld r18,Z+\n"
"ld r19,Z+\n"
"ld r20,Z+\n"
"ld r21,Z+\n"
"ld r22,Z+\n"
"ld r23,Z+\n"
"2:\n"
"add r26,%A2\n"
"adc r27,__zero_reg__\n"
"ld r15,X+\n"
"ld r8,X+\n"
"ld r9,X+\n"
"ld r10,X+\n"
"ld r11,X+\n"
"ld r12,X+\n"
"ld r13,X+\n"
"ld r14,X+\n"
"sub r26,%A2\n"
"sbc r27,__zero_reg__\n"
"sbiw r26,8\n"
"add r8,r16\n"
"adc r9,r17\n"
"adc r10,r18\n"
"adc r11,r19\n"
"adc r12,r20\n"
"adc r13,r21\n"
"adc r14,r22\n"
"adc r15,r23\n"
"eor r8,r25\n"
"add r26,%B2\n"
"adc r27,__zero_reg__\n"
"st X+,r8\n"
"st X+,r9\n"
"st X+,r10\n"
"st X+,r11\n"
"st X+,r12\n"
"st X+,r13\n"
"st X+,r14\n"
"st X+,r15\n"
"sub r26,%B2\n"
"sbc r27,__zero_reg__\n"
"sbiw r26,8\n"
"lsl r16\n"
"rol r17\n"
"rol r18\n"
"rol r19\n"
"rol r20\n"
"rol r21\n"
"rol r22\n"
"rol r23\n"
"adc r16, __zero_reg__\n"
"lsl r16\n"
"rol r17\n"
"rol r18\n"
"rol r19\n"
"rol r20\n"
"rol r21\n"
"rol r22\n"
"rol r23\n"
"adc r16, __zero_reg__\n"
"lsl r16\n"
"rol r17\n"
"rol r18\n"
"rol r19\n"
"rol r20\n"
"rol r21\n"
"rol r22\n"
"rol r23\n"
"adc r16, __zero_reg__\n"
"eor r16,r8\n"
"eor r17,r9\n"
"eor r18,r10\n"
"eor r19,r11\n"
"eor r20,r12\n"
"eor r21,r13\n"
"eor r22,r14\n"
"eor r23,r15\n"
"st Z+,r16\n"
"st Z+,r17\n"
"st Z+,r18\n"
"st Z+,r19\n"
"st Z+,r20\n"
"st Z+,r21\n"
"st Z+,r22\n"
"st Z+,r23\n"
"ldi r24,8\n"
"add %A2,r24\n"
"add %B2,r24\n"
"ldi r24,0x1F\n"
"and %A2,r24\n"
"and %B2,r24\n"
"ldd r8,%4\n"
"inc r25\n"
"cp r25,r8\n"
"breq 3f\n"
"rjmp 2b\n"
"3:\n"
"ldi r24,32\n"
"4:\n"
"st X+,__zero_reg__\n"
"dec r24\n"
"brne 4b\n"
: : "z"(k), "x"(key + len), "r"(l), "Q"(mb), "Q"(r)
: "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
"r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", "memory"
, "r24", "r25"
);
return true;
#else
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;
#endif
}
void Speck::encryptBlock(uint8_t *output, const uint8_t *input)
{
#if USE_AVR_INLINE_ASM
// Automatically generated by the genspeck tool.
__asm__ __volatile__ (
"ld r15,X+\n"
"ld r14,X+\n"
"ld r13,X+\n"
"ld r12,X+\n"
"ld r11,X+\n"
"ld r10,X+\n"
"ld r9,X+\n"
"ld r8,X+\n"
"ld r23,X+\n"
"ld r22,X+\n"
"ld r21,X+\n"
"ld r20,X+\n"
"ld r19,X+\n"
"ld r18,X+\n"
"ld r17,X+\n"
"ld r16,X\n"
"1:\n"
"add r9,r16\n"
"adc r10,r17\n"
"adc r11,r18\n"
"adc r12,r19\n"
"adc r13,r20\n"
"adc r14,r21\n"
"adc r15,r22\n"
"adc r8,r23\n"
"ld __tmp_reg__,Z+\n"
"eor __tmp_reg__,r9\n"
"ld r9,Z+\n"
"eor r9,r10\n"
"ld r10,Z+\n"
"eor r10,r11\n"
"ld r11,Z+\n"
"eor r11,r12\n"
"ld r12,Z+\n"
"eor r12,r13\n"
"ld r13,Z+\n"
"eor r13,r14\n"
"ld r14,Z+\n"
"eor r14,r15\n"
"ld r15,Z+\n"
"eor r15,r8\n"
"mov r8,__tmp_reg__\n"
"lsl r16\n"
"rol r17\n"
"rol r18\n"
"rol r19\n"
"rol r20\n"
"rol r21\n"
"rol r22\n"
"rol r23\n"
"adc r16, __zero_reg__\n"
"lsl r16\n"
"rol r17\n"
"rol r18\n"
"rol r19\n"
"rol r20\n"
"rol r21\n"
"rol r22\n"
"rol r23\n"
"adc r16, __zero_reg__\n"
"lsl r16\n"
"rol r17\n"
"rol r18\n"
"rol r19\n"
"rol r20\n"
"rol r21\n"
"rol r22\n"
"rol r23\n"
"adc r16, __zero_reg__\n"
"eor r16,r8\n"
"eor r17,r9\n"
"eor r18,r10\n"
"eor r19,r11\n"
"eor r20,r12\n"
"eor r21,r13\n"
"eor r22,r14\n"
"eor r23,r15\n"
"dec %2\n"
"breq 2f\n"
"rjmp 1b\n"
"2:\n"
"ldd r26,%A3\n"
"ldd r27,%B3\n"
"st X+,r15\n"
"st X+,r14\n"
"st X+,r13\n"
"st X+,r12\n"
"st X+,r11\n"
"st X+,r10\n"
"st X+,r9\n"
"st X+,r8\n"
"st X+,r23\n"
"st X+,r22\n"
"st X+,r21\n"
"st X+,r20\n"
"st X+,r19\n"
"st X+,r18\n"
"st X+,r17\n"
"st X,r16\n"
: : "x"(input), "z"(k), "r"(rounds), "Q"(output)
: "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
"r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", "memory"
);
#else
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);
#endif
}
void Speck::decryptBlock(uint8_t *output, const uint8_t *input)
{
#if USE_AVR_INLINE_ASM
// Automatically generated by the genspeck tool.
__asm__ __volatile__ (
"ld r15,X+\n"
"ld r14,X+\n"
"ld r13,X+\n"
"ld r12,X+\n"
"ld r11,X+\n"
"ld r10,X+\n"
"ld r9,X+\n"
"ld r8,X+\n"
"ld r23,X+\n"
"ld r22,X+\n"
"ld r21,X+\n"
"ld r20,X+\n"
"ld r19,X+\n"
"ld r18,X+\n"
"ld r17,X+\n"
"ld r16,X\n"
"1:\n"
"eor r16,r8\n"
"eor r17,r9\n"
"eor r18,r10\n"
"eor r19,r11\n"
"eor r20,r12\n"
"eor r21,r13\n"
"eor r22,r14\n"
"eor r23,r15\n"
"bst r16,0\n"
"ror r23\n"
"ror r22\n"
"ror r21\n"
"ror r20\n"
"ror r19\n"
"ror r18\n"
"ror r17\n"
"ror r16\n"
"bld r23,7\n"
"bst r16,0\n"
"ror r23\n"
"ror r22\n"
"ror r21\n"
"ror r20\n"
"ror r19\n"
"ror r18\n"
"ror r17\n"
"ror r16\n"
"bld r23,7\n"
"bst r16,0\n"
"ror r23\n"
"ror r22\n"
"ror r21\n"
"ror r20\n"
"ror r19\n"
"ror r18\n"
"ror r17\n"
"ror r16\n"
"bld r23,7\n"
"ld __tmp_reg__,-Z\n"
"eor __tmp_reg__,r15\n"
"ld r15,-Z\n"
"eor r15,r14\n"
"ld r14,-Z\n"
"eor r14,r13\n"
"ld r13,-Z\n"
"eor r13,r12\n"
"ld r12,-Z\n"
"eor r12,r11\n"
"ld r11,-Z\n"
"eor r11,r10\n"
"ld r10,-Z\n"
"eor r10,r9\n"
"ld r9,-Z\n"
"eor r9,r8\n"
"mov r8,__tmp_reg__\n"
"sub r9,r16\n"
"sbc r10,r17\n"
"sbc r11,r18\n"
"sbc r12,r19\n"
"sbc r13,r20\n"
"sbc r14,r21\n"
"sbc r15,r22\n"
"sbc r8,r23\n"
"dec %2\n"
"breq 2f\n"
"rjmp 1b\n"
"2:\n"
"ldd r26,%A3\n"
"ldd r27,%B3\n"
"st X+,r15\n"
"st X+,r14\n"
"st X+,r13\n"
"st X+,r12\n"
"st X+,r11\n"
"st X+,r10\n"
"st X+,r9\n"
"st X+,r8\n"
"st X+,r23\n"
"st X+,r22\n"
"st X+,r21\n"
"st X+,r20\n"
"st X+,r19\n"
"st X+,r18\n"
"st X+,r17\n"
"st X,r16\n"
: : "x"(input), "z"(k + rounds), "r"(rounds), "Q"(output)
: "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
"r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", "memory"
);
#else
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);
#endif
}
void Speck::clear()
{
clean(k);
}

View File

@@ -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

View File

@@ -0,0 +1,586 @@
/*
* Copyright (C) 2016 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 "SpeckSmall.h"
#include "Crypto.h"
#include "utility/RotateUtil.h"
#include "utility/EndianUtil.h"
#include <string.h>
/**
* \class SpeckSmall SpeckSmall.h <SpeckSmall.h>
* \brief Speck block cipher with a 128-bit block size (small-memory version).
*
* This class differs from the Speck class in that the RAM requirements are
* vastly reduced. The key schedule is expanded round by round instead of
* being generated and stored by setKey(). The performance of encryption
* and decryption is slightly less because of this.
*
* This class is useful when RAM is at a premium and reduced encryption
* performance is not a hindrance to the application. Even though the
* performance is reduced, this class is still faster than AES with
* equivalent key sizes.
*
* The companion SpeckTiny class uses even less RAM but only supports the
* encryptBlock() operation. Block cipher modes like CTR, EAX, and GCM
* do not need the decryptBlock() operation, so SpeckTiny may be a better
* option than SpeckSmall for many applications.
*
* See the documentation for the Speck class for more information on the
* Speck family of block ciphers.
*
* References: https://en.wikipedia.org/wiki/Speck_%28cipher%29,
* http://eprint.iacr.org/2013/404
*
* \sa Speck, SpeckTiny
*/
// The "avr-gcc" compiler doesn't do a very good job of compiling
// code involving 64-bit values. So we have to use inline assembly.
// It also helps to break the state up into 32-bit quantities
// because "asm" supports register names like %A0, %B0, %C0, %D0
// for the bytes in a 32-bit quantity, but it does not support
// %E0, %F0, %G0, %H0 for the high bytes of a 64-bit quantity.
#if defined(__AVR__)
#define USE_AVR_INLINE_ASM 1
#endif
// Pack/unpack byte-aligned big-endian 64-bit quantities.
#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)
/**
* \brief Constructs a small-memory 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.
*/
SpeckSmall::SpeckSmall()
{
}
SpeckSmall::~SpeckSmall()
{
clean(l);
}
bool SpeckSmall::setKey(const uint8_t *key, size_t len)
{
// Try setting the key for the forward encryption direction.
if (!SpeckTiny::setKey(key, len))
return false;
#if USE_AVR_INLINE_ASM
// Expand the key schedule to get the l and s values at the end
// of the schedule, which will allow us to reverse it later.
uint8_t mb = (rounds - 31) * 8;
__asm__ __volatile__ (
"ld r16,Z+\n" // s = k[0]
"ld r17,Z+\n"
"ld r18,Z+\n"
"ld r19,Z+\n"
"ld r20,Z+\n"
"ld r21,Z+\n"
"ld r22,Z+\n"
"ld r23,Z+\n"
"mov r24,%3\n" // memcpy(l, k + 1, mb)
"3:\n"
"ld __tmp_reg__,Z+\n"
"st X+,__tmp_reg__\n"
"dec r24\n"
"brne 3b\n"
"sub %A1,%3\n" // return X to its initial value
"sbc %B1,__zero_reg__\n"
"1:\n"
// l[li_out] = (s + rightRotate8_64(l[li_in])) ^ i;
"add %A1,%2\n" // X = &(l[li_in])
"adc %B1,__zero_reg__\n"
"ld r15,X+\n" // x = rightRotate8_64(l[li_in])
"ld r8,X+\n"
"ld r9,X+\n"
"ld r10,X+\n"
"ld r11,X+\n"
"ld r12,X+\n"
"ld r13,X+\n"
"ld r14,X+\n"
"add r8,r16\n" // x += s
"adc r9,r17\n"
"adc r10,r18\n"
"adc r11,r19\n"
"adc r12,r20\n"
"adc r13,r21\n"
"adc r14,r22\n"
"adc r15,r23\n"
"eor r8,%4\n" // x ^= i
// X = X - li_in + li_out
"ldi r24,8\n" // li_in = li_in + 1
"add %2,r24\n"
"sub %A1,%2\n" // return X to its initial value
"sbc %B1,__zero_reg__\n"
"ldi r25,0x1f\n"
"and %2,r25\n" // li_in = li_in % 4
"add %A1,%3\n" // X = &(l[li_out])
"adc %B1,__zero_reg__\n"
"st X+,r8\n" // l[li_out] = x
"st X+,r9\n"
"st X+,r10\n"
"st X+,r11\n"
"st X+,r12\n"
"st X+,r13\n"
"st X+,r14\n"
"st X+,r15\n"
"add %3,r24\n" // li_out = li_out + 1
"sub %A1,%3\n" // return X to its initial value
"sbc %B1,__zero_reg__\n"
"and %3,r25\n" // li_out = li_out % 4
// s = leftRotate3_64(s) ^ l[li_out];
"lsl r16\n" // s = leftRotate1_64(s)
"rol r17\n"
"rol r18\n"
"rol r19\n"
"rol r20\n"
"rol r21\n"
"rol r22\n"
"rol r23\n"
"adc r16,__zero_reg__\n"
"lsl r16\n" // s = leftRotate1_64(s)
"rol r17\n"
"rol r18\n"
"rol r19\n"
"rol r20\n"
"rol r21\n"
"rol r22\n"
"rol r23\n"
"adc r16,__zero_reg__\n"
"lsl r16\n" // s = leftRotate1_64(s)
"rol r17\n"
"rol r18\n"
"rol r19\n"
"rol r20\n"
"rol r21\n"
"rol r22\n"
"rol r23\n"
"adc r16,__zero_reg__\n"
"eor r16,r8\n" // s ^= x
"eor r17,r9\n"
"eor r18,r10\n"
"eor r19,r11\n"
"eor r20,r12\n"
"eor r21,r13\n"
"eor r22,r14\n"
"eor r23,r15\n"
// Loop
"inc %4\n" // ++i
"dec %5\n" // --rounds
"breq 2f\n"
"rjmp 1b\n"
"2:\n"
"add %A1,%3\n" // X = &(l[li_out])
"adc %B1,__zero_reg__\n"
"st X+,r16\n" // l[li_out] = s
"st X+,r17\n"
"st X+,r18\n"
"st X+,r19\n"
"st X+,r20\n"
"st X+,r21\n"
"st X+,r22\n"
"st X+,r23\n"
: : "z"(k), "x"(l),
"r"((uint8_t)0), // initial value of li_in
"r"((uint8_t)mb), // initial value of li_out
"r"(0), // initial value of i
"r"(rounds - 1)
: "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
"r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
"r24", "r25"
);
return true;
#else
// Expand the key schedule to get the l and s values at the end
// of the schedule, which will allow us to reverse it later.
uint8_t m = rounds - 30;
uint8_t li_in = 0;
uint8_t li_out = m - 1;
uint64_t s = k[0];
memcpy(l, k + 1, (m - 1) * sizeof(uint64_t));
for (uint8_t i = 0; i < (rounds - 1); ++i) {
l[li_out] = (s + rightRotate8_64(l[li_in])) ^ i;
s = leftRotate3_64(s) ^ l[li_out];
li_in = (li_in + 1) & 0x03;
li_out = (li_out + 1) & 0x03;
}
// Save the final s value in the l array so that we can recover it later.
l[li_out] = s;
return true;
#endif
}
void SpeckSmall::decryptBlock(uint8_t *output, const uint8_t *input)
{
#if USE_AVR_INLINE_ASM
// Automatically generated by the genspeck tool.
uint64_t l[5];
uint8_t r = rounds;
uint8_t li_in = ((r + 3) & 0x03) * 8;
uint8_t li_out = ((((r - 31) & 0x03) * 8) + li_in) & 0x1F;
__asm__ __volatile__ (
"ldd r25,%4\n"
"ldi r24,32\n"
"1:\n"
"ld __tmp_reg__,X+\n"
"st Z+,__tmp_reg__\n"
"dec r24\n"
"brne 1b\n"
"movw r26,r30\n"
"sbiw r30,32\n"
"add r30,r25\n"
"adc r31,__zero_reg__\n"
"ld __tmp_reg__,Z\n"
"st X+,__tmp_reg__\n"
"ldd __tmp_reg__,Z+1\n"
"st X+,__tmp_reg__\n"
"ldd __tmp_reg__,Z+2\n"
"st X+,__tmp_reg__\n"
"ldd __tmp_reg__,Z+3\n"
"st X+,__tmp_reg__\n"
"ldd __tmp_reg__,Z+4\n"
"st X+,__tmp_reg__\n"
"ldd __tmp_reg__,Z+5\n"
"st X+,__tmp_reg__\n"
"ldd __tmp_reg__,Z+6\n"
"st X+,__tmp_reg__\n"
"ldd __tmp_reg__,Z+7\n"
"st X+,__tmp_reg__\n"
"sub r30,r25\n"
"sbc r31,__zero_reg__\n"
"movw r26,%A2\n"
"ld r15,X+\n"
"ld r14,X+\n"
"ld r13,X+\n"
"ld r12,X+\n"
"ld r11,X+\n"
"ld r10,X+\n"
"ld r9,X+\n"
"ld r8,X+\n"
"ld r23,X+\n"
"ld r22,X+\n"
"ld r21,X+\n"
"ld r20,X+\n"
"ld r19,X+\n"
"ld r18,X+\n"
"ld r17,X+\n"
"ld r16,X\n"
"ldd %A2,%6\n"
"mov %B2,r25\n"
"ldd r25,%5\n"
"dec r25\n"
"movw r26,r30\n"
"adiw r26,40\n"
"2:\n"
"eor r16,r8\n"
"eor r17,r9\n"
"eor r18,r10\n"
"eor r19,r11\n"
"eor r20,r12\n"
"eor r21,r13\n"
"eor r22,r14\n"
"eor r23,r15\n"
"bst r16,0\n"
"ror r23\n"
"ror r22\n"
"ror r21\n"
"ror r20\n"
"ror r19\n"
"ror r18\n"
"ror r17\n"
"ror r16\n"
"bld r23,7\n"
"bst r16,0\n"
"ror r23\n"
"ror r22\n"
"ror r21\n"
"ror r20\n"
"ror r19\n"
"ror r18\n"
"ror r17\n"
"ror r16\n"
"bld r23,7\n"
"bst r16,0\n"
"ror r23\n"
"ror r22\n"
"ror r21\n"
"ror r20\n"
"ror r19\n"
"ror r18\n"
"ror r17\n"
"ror r16\n"
"bld r23,7\n"
"ld __tmp_reg__,-X\n"
"eor __tmp_reg__,r15\n"
"ld r15,-X\n"
"eor r15,r14\n"
"ld r14,-X\n"
"eor r14,r13\n"
"ld r13,-X\n"
"eor r13,r12\n"
"ld r12,-X\n"
"eor r12,r11\n"
"ld r11,-X\n"
"eor r11,r10\n"
"ld r10,-X\n"
"eor r10,r9\n"
"ld r9,-X\n"
"eor r9,r8\n"
"mov r8,__tmp_reg__\n"
"sub r9,r16\n"
"sbc r10,r17\n"
"sbc r11,r18\n"
"sbc r12,r19\n"
"sbc r13,r20\n"
"sbc r14,r21\n"
"sbc r15,r22\n"
"sbc r8,r23\n"
"or r25,r25\n"
"brne 3f\n"
"rjmp 4f\n"
"3:\n"
"dec r25\n"
"push r8\n"
"push r9\n"
"push r10\n"
"push r11\n"
"push r12\n"
"push r13\n"
"push r14\n"
"push r15\n"
"push r16\n"
"push r17\n"
"push r18\n"
"push r19\n"
"push r20\n"
"push r21\n"
"push r22\n"
"push r23\n"
"ldi r24,24\n"
"add %A2,r24\n"
"add %B2,r24\n"
"ldi r24,0x1F\n"
"and %A2,r24\n"
"and %B2,r24\n"
"ld r16,X+\n"
"ld r17,X+\n"
"ld r18,X+\n"
"ld r19,X+\n"
"ld r20,X+\n"
"ld r21,X+\n"
"ld r22,X+\n"
"ld r23,X+\n"
"add r30,%B2\n"
"adc r31,__zero_reg__\n"
"ld r8,Z\n"
"ldd r9,Z+1\n"
"ldd r10,Z+2\n"
"ldd r11,Z+3\n"
"ldd r12,Z+4\n"
"ldd r13,Z+5\n"
"ldd r14,Z+6\n"
"ldd r15,Z+7\n"
"sub r30,%B2\n"
"sbc r31,__zero_reg__\n"
"eor r16,r8\n"
"eor r17,r9\n"
"eor r18,r10\n"
"eor r19,r11\n"
"eor r20,r12\n"
"eor r21,r13\n"
"eor r22,r14\n"
"eor r23,r15\n"
"bst r16,0\n"
"ror r23\n"
"ror r22\n"
"ror r21\n"
"ror r20\n"
"ror r19\n"
"ror r18\n"
"ror r17\n"
"ror r16\n"
"bld r23,7\n"
"bst r16,0\n"
"ror r23\n"
"ror r22\n"
"ror r21\n"
"ror r20\n"
"ror r19\n"
"ror r18\n"
"ror r17\n"
"ror r16\n"
"bld r23,7\n"
"bst r16,0\n"
"ror r23\n"
"ror r22\n"
"ror r21\n"
"ror r20\n"
"ror r19\n"
"ror r18\n"
"ror r17\n"
"ror r16\n"
"bld r23,7\n"
"st -X,r23\n"
"st -X,r22\n"
"st -X,r21\n"
"st -X,r20\n"
"st -X,r19\n"
"st -X,r18\n"
"st -X,r17\n"
"st -X,r16\n"
"adiw r26,8\n"
"eor r8,r25\n"
"sub r8,r16\n"
"sbc r9,r17\n"
"sbc r10,r18\n"
"sbc r11,r19\n"
"sbc r12,r20\n"
"sbc r13,r21\n"
"sbc r14,r22\n"
"sbc r15,r23\n"
"add r30,%A2\n"
"adc r31,__zero_reg__\n"
"st Z,r15\n"
"std Z+1,r8\n"
"std Z+2,r9\n"
"std Z+3,r10\n"
"std Z+4,r11\n"
"std Z+5,r12\n"
"std Z+6,r13\n"
"std Z+7,r14\n"
"sub r30,%A2\n"
"sbc r31,__zero_reg__\n"
"pop r23\n"
"pop r22\n"
"pop r21\n"
"pop r20\n"
"pop r19\n"
"pop r18\n"
"pop r17\n"
"pop r16\n"
"pop r15\n"
"pop r14\n"
"pop r13\n"
"pop r12\n"
"pop r11\n"
"pop r10\n"
"pop r9\n"
"pop r8\n"
"rjmp 2b\n"
"4:\n"
"ldd r26,%A3\n"
"ldd r27,%B3\n"
"st X+,r15\n"
"st X+,r14\n"
"st X+,r13\n"
"st X+,r12\n"
"st X+,r11\n"
"st X+,r10\n"
"st X+,r9\n"
"st X+,r8\n"
"st X+,r23\n"
"st X+,r22\n"
"st X+,r21\n"
"st X+,r20\n"
"st X+,r19\n"
"st X+,r18\n"
"st X+,r17\n"
"st X,r16\n"
: : "x"(this->l), "z"(l), "r"(input), "Q"(output), "Q"(li_out), "Q"(r), "Q"(li_in)
: "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
"r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", "memory"
, "r24", "r25"
);
#else
uint64_t l[4];
uint64_t x, y, s;
uint8_t round;
uint8_t li_in = (rounds + 3) & 0x03;
uint8_t li_out = ((rounds - 31) + li_in) & 0x03;
// Prepare the key schedule, starting at the end.
for (round = li_in; round != li_out; round = (round + 1) & 0x03)
l[round] = this->l[round];
s = this->l[li_out];
// Unpack the input and convert from big-endian.
unpack64(x, input);
unpack64(y, input + 8);
// Perform all decryption rounds except the last while
// expanding the decryption schedule on the fly.
for (uint8_t round = rounds - 1; round > 0; --round) {
// Decrypt using the current round key.
y = rightRotate3_64(x ^ y);
x = leftRotate8_64((x ^ s) - y);
// Generate the round key for the previous round.
li_in = (li_in + 3) & 0x03;
li_out = (li_out + 3) & 0x03;
s = rightRotate3_64(s ^ l[li_out]);
l[li_in] = leftRotate8_64((l[li_out] ^ (round - 1)) - s);
}
// Perform the final decryption round.
y = rightRotate3_64(x ^ y);
x = leftRotate8_64((x ^ s) - y);
// Pack the output and convert to big-endian.
pack64(output, x);
pack64(output + 8, y);
#endif
}
void SpeckSmall::clear()
{
SpeckTiny::clear();
clean(l);
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (C) 2016 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_SMALL_H
#define CRYPTO_SPECK_SMALL_H
#include "SpeckTiny.h"
class SpeckSmall : public SpeckTiny
{
public:
SpeckSmall();
virtual ~SpeckSmall();
bool setKey(const uint8_t *key, size_t len);
void decryptBlock(uint8_t *output, const uint8_t *input);
void clear();
private:
uint64_t l[4];
};
#endif

View File

@@ -0,0 +1,456 @@
/*
* Copyright (C) 2016 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 "SpeckTiny.h"
#include "Crypto.h"
#include "utility/RotateUtil.h"
#include "utility/EndianUtil.h"
#include <string.h>
/**
* \class SpeckTiny SpeckTiny.h <SpeckTiny.h>
* \brief Speck block cipher with a 128-bit block size (tiny-memory version).
*
* This class differs from the Speck class in the following ways:
*
* \li RAM requirements are vastly reduced. The key (up to 256 bits) is
* stored directly and then expanded to the full key schedule round by round.
* The setKey() method is very fast because of this.
* \li Performance of encryptBlock() is slower than for Speck due to
* expanding the key on the fly rather than ahead of time.
* \li The decryptBlock() function is not supported, which means that CBC
* mode cannot be used but the CTR, CFB, OFB, EAX, and GCM modes can be used.
*
* This class is useful when RAM is at a premium, CBC mode is not required,
* and reduced encryption performance is not a hindrance to the application.
* Even though the performance of encryptBlock() is reduced, this class is
* still faster than AES with equivalent key sizes.
*
* The companion SpeckSmall class supports decryptBlock() at the cost of
* some additional memory and slower setKey() times.
*
* See the documentation for the Speck class for more information on the
* Speck family of block ciphers.
*
* References: https://en.wikipedia.org/wiki/Speck_%28cipher%29,
* http://eprint.iacr.org/2013/404
*
* \sa Speck, SpeckSmall
*/
// The "avr-gcc" compiler doesn't do a very good job of compiling
// code involving 64-bit values. So we have to use inline assembly.
// It also helps to break the state up into 32-bit quantities
// because "asm" supports register names like %A0, %B0, %C0, %D0
// for the bytes in a 32-bit quantity, but it does not support
// %E0, %F0, %G0, %H0 for the high bytes of a 64-bit quantity.
#if defined(__AVR__)
#define USE_AVR_INLINE_ASM 1
#endif
/**
* \brief Constructs a tiny-memory 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.
*/
SpeckTiny::SpeckTiny()
: rounds(32)
{
}
SpeckTiny::~SpeckTiny()
{
clean(k);
}
size_t SpeckTiny::blockSize() const
{
return 16;
}
size_t SpeckTiny::keySize() const
{
// Also supports 128-bit and 192-bit, but we only report 256-bit.
return 32;
}
// Pack/unpack byte-aligned big-endian 64-bit quantities.
#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)
bool SpeckTiny::setKey(const uint8_t *key, size_t len)
{
#if USE_AVR_INLINE_ASM
// Determine the number of rounds to use and validate the key length.
if (len == 32) {
rounds = 34;
} else if (len == 24) {
rounds = 33;
} else if (len == 16) {
rounds = 32;
} else {
return false;
}
// Copy the bytes of the key into the "k" array in reverse order to
// convert big endian into little-endian.
__asm__ __volatile__ (
"1:\n"
"ld __tmp_reg__,-Z\n"
"st X+,__tmp_reg__\n"
"dec %2\n"
"brne 1b\n"
: : "x"(k), "z"(key + len), "r"(len)
);
#else
if (len == 32) {
rounds = 34;
unpack64(k[3], key);
unpack64(k[2], key + 8);
unpack64(k[1], key + 16);
unpack64(k[0], key + 24);
} else if (len == 24) {
rounds = 33;
unpack64(k[2], key);
unpack64(k[1], key + 8);
unpack64(k[0], key + 16);
} else if (len == 16) {
rounds = 32;
unpack64(k[1], key);
unpack64(k[0], key + 8);
} else {
return false;
}
#endif
return true;
}
void SpeckTiny::encryptBlock(uint8_t *output, const uint8_t *input)
{
#if USE_AVR_INLINE_ASM
// Automatically generated by the genspeck tool.
uint64_t l[5];
uint8_t r = rounds;
uint8_t mb = (r - 31) * 8;
__asm__ __volatile__ (
"movw r8,r30\n"
"ldd r16,%4\n"
"ldi r24,8\n"
"add r16,r24\n"
"1:\n"
"ld __tmp_reg__,X+\n"
"st Z+,__tmp_reg__\n"
"dec r16\n"
"brne 1b\n"
"movw r30,r8\n"
"movw r26,%A2\n"
"ld r15,X+\n"
"ld r14,X+\n"
"ld r13,X+\n"
"ld r12,X+\n"
"ld r11,X+\n"
"ld r10,X+\n"
"ld r9,X+\n"
"ld r8,X+\n"
"ld r23,X+\n"
"ld r22,X+\n"
"ld r21,X+\n"
"ld r20,X+\n"
"ld r19,X+\n"
"ld r18,X+\n"
"ld r17,X+\n"
"ld r16,X\n"
"clr %A2\n"
"ldd %B2,%4\n"
"clr r25\n"
"2:\n"
"add r9,r16\n"
"adc r10,r17\n"
"adc r11,r18\n"
"adc r12,r19\n"
"adc r13,r20\n"
"adc r14,r21\n"
"adc r15,r22\n"
"adc r8,r23\n"
"ld __tmp_reg__,Z+\n"
"eor __tmp_reg__,r9\n"
"ld r9,Z+\n"
"eor r9,r10\n"
"ld r10,Z+\n"
"eor r10,r11\n"
"ld r11,Z+\n"
"eor r11,r12\n"
"ld r12,Z+\n"
"eor r12,r13\n"
"ld r13,Z+\n"
"eor r13,r14\n"
"ld r14,Z+\n"
"eor r14,r15\n"
"ld r15,Z+\n"
"eor r15,r8\n"
"mov r8,__tmp_reg__\n"
"lsl r16\n"
"rol r17\n"
"rol r18\n"
"rol r19\n"
"rol r20\n"
"rol r21\n"
"rol r22\n"
"rol r23\n"
"adc r16, __zero_reg__\n"
"lsl r16\n"
"rol r17\n"
"rol r18\n"
"rol r19\n"
"rol r20\n"
"rol r21\n"
"rol r22\n"
"rol r23\n"
"adc r16, __zero_reg__\n"
"lsl r16\n"
"rol r17\n"
"rol r18\n"
"rol r19\n"
"rol r20\n"
"rol r21\n"
"rol r22\n"
"rol r23\n"
"adc r16, __zero_reg__\n"
"eor r16,r8\n"
"eor r17,r9\n"
"eor r18,r10\n"
"eor r19,r11\n"
"eor r20,r12\n"
"eor r21,r13\n"
"eor r22,r14\n"
"eor r23,r15\n"
"mov __tmp_reg__,r25\n"
"inc __tmp_reg__\n"
"ldd r24,%5\n"
"cp __tmp_reg__,r24\n"
"brne 3f\n"
"rjmp 4f\n"
"3:\n"
"push r8\n"
"push r9\n"
"push r10\n"
"push r11\n"
"push r12\n"
"push r13\n"
"push r14\n"
"push r15\n"
"push r16\n"
"push r17\n"
"push r18\n"
"push r19\n"
"push r20\n"
"push r21\n"
"push r22\n"
"push r23\n"
"sbiw r30,8\n"
"ld r16,Z\n"
"ldd r17,Z+1\n"
"ldd r18,Z+2\n"
"ldd r19,Z+3\n"
"ldd r20,Z+4\n"
"ldd r21,Z+5\n"
"ldd r22,Z+6\n"
"ldd r23,Z+7\n"
"add r30,%A2\n"
"adc r31,__zero_reg__\n"
"ldd r15,Z+8\n"
"ldd r8,Z+9\n"
"ldd r9,Z+10\n"
"ldd r10,Z+11\n"
"ldd r11,Z+12\n"
"ldd r12,Z+13\n"
"ldd r13,Z+14\n"
"ldd r14,Z+15\n"
"add r8,r16\n"
"adc r9,r17\n"
"adc r10,r18\n"
"adc r11,r19\n"
"adc r12,r20\n"
"adc r13,r21\n"
"adc r14,r22\n"
"adc r15,r23\n"
"eor r8,r25\n"
"sub r30,%A2\n"
"sbc r31,__zero_reg__\n"
"add r30,%B2\n"
"adc r31,__zero_reg__\n"
"std Z+8,r8\n"
"std Z+9,r9\n"
"std Z+10,r10\n"
"std Z+11,r11\n"
"std Z+12,r12\n"
"std Z+13,r13\n"
"std Z+14,r14\n"
"std Z+15,r15\n"
"sub r30,%B2\n"
"sbc r31,__zero_reg__\n"
"lsl r16\n"
"rol r17\n"
"rol r18\n"
"rol r19\n"
"rol r20\n"
"rol r21\n"
"rol r22\n"
"rol r23\n"
"adc r16, __zero_reg__\n"
"lsl r16\n"
"rol r17\n"
"rol r18\n"
"rol r19\n"
"rol r20\n"
"rol r21\n"
"rol r22\n"
"rol r23\n"
"adc r16, __zero_reg__\n"
"lsl r16\n"
"rol r17\n"
"rol r18\n"
"rol r19\n"
"rol r20\n"
"rol r21\n"
"rol r22\n"
"rol r23\n"
"adc r16, __zero_reg__\n"
"eor r16,r8\n"
"eor r17,r9\n"
"eor r18,r10\n"
"eor r19,r11\n"
"eor r20,r12\n"
"eor r21,r13\n"
"eor r22,r14\n"
"eor r23,r15\n"
"st Z,r16\n"
"std Z+1,r17\n"
"std Z+2,r18\n"
"std Z+3,r19\n"
"std Z+4,r20\n"
"std Z+5,r21\n"
"std Z+6,r22\n"
"std Z+7,r23\n"
"ldi r24,8\n"
"add %A2,r24\n"
"add %B2,r24\n"
"ldi r24,0x1F\n"
"and %A2,r24\n"
"and %B2,r24\n"
"pop r23\n"
"pop r22\n"
"pop r21\n"
"pop r20\n"
"pop r19\n"
"pop r18\n"
"pop r17\n"
"pop r16\n"
"pop r15\n"
"pop r14\n"
"pop r13\n"
"pop r12\n"
"pop r11\n"
"pop r10\n"
"pop r9\n"
"pop r8\n"
"inc r25\n"
"rjmp 2b\n"
"4:\n"
"ldd r26,%A3\n"
"ldd r27,%B3\n"
"st X+,r15\n"
"st X+,r14\n"
"st X+,r13\n"
"st X+,r12\n"
"st X+,r11\n"
"st X+,r10\n"
"st X+,r9\n"
"st X+,r8\n"
"st X+,r23\n"
"st X+,r22\n"
"st X+,r21\n"
"st X+,r20\n"
"st X+,r19\n"
"st X+,r18\n"
"st X+,r17\n"
"st X,r16\n"
: : "x"(k), "z"(l), "r"(input), "Q"(output), "Q"(mb), "Q"(r)
: "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
"r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", "memory"
, "r24", "r25"
);
#else
uint64_t l[4];
uint64_t x, y, s;
uint8_t round;
uint8_t li_in = 0;
uint8_t li_out = rounds - 31;
uint8_t i = 0;
// Copy the input block into the work registers.
unpack64(x, input);
unpack64(y, input + 8);
// Prepare the key schedule.
memcpy(l, k + 1, li_out * sizeof(uint64_t));
s = k[0];
// Perform all encryption rounds except the last.
for (round = rounds - 1; round > 0; --round, ++i) {
// Perform the round with the current key schedule word.
x = (rightRotate8_64(x) + y) ^ s;
y = leftRotate3_64(y) ^ x;
// Calculate the next key schedule word.
l[li_out] = (s + rightRotate8_64(l[li_in])) ^ i;
s = leftRotate3_64(s) ^ l[li_out];
li_in = (li_in + 1) & 0x03;
li_out = (li_out + 1) & 0x03;
}
// Perform the final round and copy to the output.
x = (rightRotate8_64(x) + y) ^ s;
y = leftRotate3_64(y) ^ x;
pack64(output, x);
pack64(output + 8, y);
#endif
}
void SpeckTiny::decryptBlock(uint8_t *output, const uint8_t *input)
{
// Decryption is not supported by SpeckTiny. Use SpeckSmall instead.
}
void SpeckTiny::clear()
{
clean(k);
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright (C) 2016 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_TINY_H
#define CRYPTO_SPECK_TINY_H
#include "BlockCipher.h"
class SpeckSmall;
class SpeckTiny : public BlockCipher
{
public:
SpeckTiny();
virtual ~SpeckTiny();
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[4];
uint8_t rounds;
friend class SpeckSmall;
};
#endif