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

AES tiny and small memory versions

This commit is contained in:
Rhys Weatherley 2018-04-07 02:39:30 +10:00
parent bb9f0c2b96
commit 9ae79f469f
9 changed files with 1002 additions and 20 deletions

View File

@ -37,6 +37,18 @@
\li Post-quantum algorithms: NewHope
\li Random number generation: \link RNGClass RNG\endlink, TransistorNoiseSource, RingOscillatorNoiseSource
Reduced memory versions of some algorithms (encryption is slower, but the
RAM required for the key schedule is less):
\li AESTiny128, AESSmall128, AESTiny256, AESSmall256
\li SpeckTiny, SpeckSmall
The "tiny" versions only support encryption which makes them suitable for
the CTR, CFB, OFB, EAX, and GCM block cipher modes but not CBC. The "small"
versions use a little more memory but support both encryptionm and decryption.
\section crypto_optimizations Optimizations
All cryptographic algorithms have been optimized for 8-bit Arduino platforms
like the Uno. Memory usage is also reduced, particularly for SHA256
and SHA512 which save 192 and 512 bytes respectively over traditional
@ -76,9 +88,13 @@ Ardunino Mega 2560 running at 16 MHz are similar:
<table>
<tr><td>Encryption Algorithm</td><td align="right">Encryption (per byte)</td><td align="right">Decryption (per byte)</td><td align="right">Key Setup</td><td>State Size (bytes)</td></tr>
<tr><td>AES128 (ECB mode)</td><td align="right">33.28us</td><td align="right">63.18us</td><td align="right">160.00us</td><td align="right">181</td></tr>
<tr><td>AES192 (ECB mode)</td><td align="right">39.94us</td><td align="right">76.48us</td><td align="right">166.54us</td><td align="right">213</td></tr>
<tr><td>AES256 (ECB mode)</td><td align="right">46.61us</td><td align="right">89.78us</td><td align="right">227.97us</td><td align="right">245</td></tr>
<tr><td>AES128 (ECB mode)</td><td align="right">33.28us</td><td align="right">63.18us</td><td align="right">158.68us</td><td align="right">181</td></tr>
<tr><td>AES192 (ECB mode)</td><td align="right">39.94us</td><td align="right">76.48us</td><td align="right">165.34us</td><td align="right">213</td></tr>
<tr><td>AES256 (ECB mode)</td><td align="right">46.61us</td><td align="right">89.78us</td><td align="right">217.79us</td><td align="right">245</td></tr>
<tr><td>AESTiny128 (ECB mode)</td><td align="right">40.37us</td><td align="right"> </td><td align="right">10.16us</td><td align="right">18</td></tr>
<tr><td>AESTiny256 (ECB mode)</td><td align="right">56.84us</td><td align="right"> </td><td align="right">17.20us</td><td align="right">34</td></tr>
<tr><td>AESSmall128 (ECB mode)</td><td align="right">40.37us</td><td align="right">71.36us</td><td align="right">134.22us</td><td align="right">34</td></tr>
<tr><td>AESSmall256 (ECB mode)</td><td align="right">56.84us</td><td align="right">100.55us</td><td align="right">177.73us</td><td align="right">66</td></tr>
<tr><td>ChaCha (20 rounds)</td><td align="right">14.87us</td><td align="right">14.88us</td><td align="right">43.74us</td><td align="right">132</td></tr>
<tr><td>ChaCha (12 rounds)</td><td align="right">10.38us</td><td align="right">10.38us</td><td align="right">43.74us</td><td align="right">132</td></tr>
<tr><td>ChaCha (8 rounds)</td><td align="right">8.13us</td><td align="right">8.14us</td><td align="right">43.74us</td><td align="right">132</td></tr>
@ -159,9 +175,13 @@ All figures are for the Arduino Due running at 84 MHz:
<table>
<tr><td>Encryption Algorithm</td><td align="right">Encryption (per byte)</td><td align="right">Decryption (per byte)</td><td align="right">Key Setup</td><td>State Size (bytes)</td></tr>
<tr><td>AES128 (ECB mode)</td><td align="right">5.71us</td><td align="right">10.41us</td><td align="right">34.73us</td><td align="right">188</td></tr>
<tr><td>AES192 (ECB mode)</td><td align="right">6.87us</td><td align="right">12.57us</td><td align="right">36.51us</td><td align="right">220</td></tr>
<tr><td>AES256 (ECB mode)</td><td align="right">8.04us</td><td align="right">14.72</td><td align="right">49.96us</td><td align="right">252</td></tr>
<tr><td>AES128 (ECB mode)</td><td align="right">6.58us</td><td align="right">11.40us</td><td align="right">38.15us</td><td align="right">188</td></tr>
<tr><td>AES192 (ECB mode)</td><td align="right">7.94us</td><td align="right">13.83us</td><td align="right">39.79us</td><td align="right">220</td></tr>
<tr><td>AES256 (ECB mode)</td><td align="right">9.30us</td><td align="right">16.25us</td><td align="right">49.68us</td><td align="right">252</td></tr>
<tr><td>AESTiny128 (ECB mode)</td><td align="right">7.23us</td><td align="right"> </td><td align="right">1.25us</td><td align="right">20</td></tr>
<tr><td>AESTiny256 (ECB mode)</td><td align="right">10.62us</td><td align="right"> </td><td align="right">1.43us</td><td align="right">36</td></tr>
<tr><td>AESSmall128 (ECB mode)</td><td align="right">7.23us</td><td align="right">12.33us</td><td align="right">23.44us</td><td align="right">36</td></tr>
<tr><td>AESSmall256 (ECB mode)</td><td align="right">10.62us</td><td align="right">16.92us</td><td align="right">31.88us</td><td align="right">68</td></tr>
<tr><td>ChaCha (20 rounds)</td><td align="right">0.87us</td><td align="right">0.88us</td><td align="right">4.96us</td><td align="right">136</td></tr>
<tr><td>ChaCha (12 rounds)</td><td align="right">0.70us</td><td align="right">0.71us</td><td align="right">4.96us</td><td align="right">136</td></tr>
<tr><td>ChaCha (8 rounds)</td><td align="right">0.62us</td><td align="right">0.62us</td><td align="right">4.96us</td><td align="right">136</td></tr>

View File

@ -67,6 +67,8 @@ SOURCES = \
SKETCHES = \
TestAES/TestAES.ino \
TestAESTiny/TestAESTiny.ino \
TestAESSmall/TestAESSmall.ino \
TestBigNumberUtil/TestBigNumberUtil.ino \
TestBLAKE2b/TestBLAKE2b.ino \
TestBLAKE2s/TestBLAKE2s.ino \

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015 Southern Storm Software, Pty Ltd.
* Copyright (C) 2015,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"),
@ -25,6 +25,11 @@
#include "BlockCipher.h"
class AESTiny128;
class AESTiny256;
class AESSmall128;
class AESSmall256;
class AESCommon : public BlockCipher
{
public:
@ -40,13 +45,22 @@ public:
protected:
AESCommon();
/** @cond */
/** @cond aes_internal */
uint8_t rounds;
uint8_t *schedule;
void keyScheduleCore(uint8_t *output, const uint8_t *input, uint8_t iteration);
void applySbox(uint8_t *output, const uint8_t *input);
static void subBytesAndShiftRows(uint8_t *output, const uint8_t *input);
static void inverseShiftRowsAndSubBytes(uint8_t *output, const uint8_t *input);
static void mixColumn(uint8_t *output, uint8_t *input);
static void inverseMixColumn(uint8_t *output, const uint8_t *input);
static void keyScheduleCore(uint8_t *output, const uint8_t *input, uint8_t iteration);
static void applySbox(uint8_t *output, const uint8_t *input);
/** @endcond */
friend class AESTiny128;
friend class AESTiny256;
friend class AESSmall128;
friend class AESSmall256;
};
class AES128 : public AESCommon
@ -91,4 +105,76 @@ private:
uint8_t sched[240];
};
class AESTiny256 : public BlockCipher
{
public:
AESTiny256();
virtual ~AESTiny256();
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:
uint8_t schedule[32];
};
class AESSmall256 : public AESTiny256
{
public:
AESSmall256();
virtual ~AESSmall256();
bool setKey(const uint8_t *key, size_t len);
void decryptBlock(uint8_t *output, const uint8_t *input);
void clear();
private:
uint8_t reverse[32];
};
class AESTiny128 : public BlockCipher
{
public:
AESTiny128();
virtual ~AESTiny128();
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:
uint8_t schedule[16];
};
class AESSmall128 : public AESTiny128
{
public:
AESSmall128();
virtual ~AESSmall128();
bool setKey(const uint8_t *key, size_t len);
void decryptBlock(uint8_t *output, const uint8_t *input);
void clear();
private:
uint8_t reverse[16];
};
#endif

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015 Southern Storm Software, Pty Ltd.
* Copyright (C) 2015,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"),
@ -28,7 +28,7 @@
* \class AES128 AES.h <AES.h>
* \brief AES block cipher with 128-bit keys.
*
* \sa AES192, AES256
* \sa AES192, AES256, AESTiny128, AESSmall128
*/
/**
@ -96,3 +96,257 @@ bool AES128::setKey(const uint8_t *key, size_t len)
return true;
}
/**
* \class AESTiny128 AES.h <AES.h>
* \brief AES block cipher with 128-bit keys and tiny memory usage.
*
* This class differs from the AES128 class in the following ways:
*
* \li RAM requirements are vastly reduced. The key 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 AES128 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.
*
* The companion AESSmall128 class supports decryptBlock() at the cost of
* some additional memory and slower setKey() times.
*
* \sa AESSmall128, AES128
*/
/** @cond */
// Helper macros.
#define KCORE(n) \
do { \
AESCommon::keyScheduleCore(temp, schedule + 12, (n)); \
schedule[0] ^= temp[0]; \
schedule[1] ^= temp[1]; \
schedule[2] ^= temp[2]; \
schedule[3] ^= temp[3]; \
} while (0)
#define KXOR(a, b) \
do { \
schedule[(a) * 4] ^= schedule[(b) * 4]; \
schedule[(a) * 4 + 1] ^= schedule[(b) * 4 + 1]; \
schedule[(a) * 4 + 2] ^= schedule[(b) * 4 + 2]; \
schedule[(a) * 4 + 3] ^= schedule[(b) * 4 + 3]; \
} while (0)
/** @endcond */
/**
* \brief Constructs an AES 128-bit 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.
*/
AESTiny128::AESTiny128()
{
}
AESTiny128::~AESTiny128()
{
clean(schedule);
}
/**
* \brief Size of an AES block in bytes.
* \return Always returns 16.
*/
size_t AESTiny128::blockSize() const
{
return 16;
}
/**
* \brief Size of a 128-bit AES key in bytes.
* \return Always returns 16.
*/
size_t AESTiny128::keySize() const
{
return 16;
}
bool AESTiny128::setKey(const uint8_t *key, size_t len)
{
if (len == 16) {
// Make a copy of the key - it will be expanded in encryptBlock().
memcpy(schedule, key, 16);
return true;
}
return false;
}
void AESTiny128::encryptBlock(uint8_t *output, const uint8_t *input)
{
uint8_t schedule[16];
uint8_t posn;
uint8_t round;
uint8_t state1[16];
uint8_t state2[16];
uint8_t temp[4];
// Start with the key in the schedule buffer.
memcpy(schedule, this->schedule, 16);
// Copy the input into the state and XOR with the key schedule.
for (posn = 0; posn < 16; ++posn)
state1[posn] = input[posn] ^ schedule[posn];
// Perform the first 9 rounds of the cipher.
for (round = 1; round <= 9; ++round) {
// Expand the next 16 bytes of the key schedule.
KCORE(round);
KXOR(1, 0);
KXOR(2, 1);
KXOR(3, 2);
// Encrypt using the key schedule.
AESCommon::subBytesAndShiftRows(state2, state1);
AESCommon::mixColumn(state1, state2);
AESCommon::mixColumn(state1 + 4, state2 + 4);
AESCommon::mixColumn(state1 + 8, state2 + 8);
AESCommon::mixColumn(state1 + 12, state2 + 12);
for (posn = 0; posn < 16; ++posn)
state1[posn] ^= schedule[posn];
}
// Expand the final 16 bytes of the key schedule.
KCORE(10);
KXOR(1, 0);
KXOR(2, 1);
KXOR(3, 2);
// Perform the final round.
AESCommon::subBytesAndShiftRows(state2, state1);
for (posn = 0; posn < 16; ++posn)
output[posn] = state2[posn] ^ schedule[posn];
}
void AESTiny128::decryptBlock(uint8_t *output, const uint8_t *input)
{
// Decryption is not supported by AESTiny128.
}
void AESTiny128::clear()
{
clean(schedule);
}
/**
* \class AESSmall128 AES.h <AES.h>
* \brief AES block cipher with 128-bit keys and reduced memory usage.
*
* This class differs from the AES128 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.
*
* The companion AESTiny128 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 AESTiny128 may be a better
* option than AESSmall128 for many applications.
*
* \sa AESTiny128, AES128
*/
/**
* \brief Constructs an AES 128-bit 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.
*/
AESSmall128::AESSmall128()
{
}
AESSmall128::~AESSmall128()
{
clean(reverse);
}
bool AESSmall128::setKey(const uint8_t *key, size_t len)
{
uint8_t *schedule;
uint8_t round;
uint8_t temp[4];
// Set the encryption key first.
if (!AESTiny128::setKey(key, len))
return false;
// Expand the key schedule up to the last round which gives
// us the round keys to use for the final two rounds. We can
// then work backwards from there in decryptBlock().
schedule = reverse;
memcpy(schedule, key, 16);
for (round = 1; round <= 10; ++round) {
KCORE(round);
KXOR(1, 0);
KXOR(2, 1);
KXOR(3, 2);
}
// Key is ready to go.
return true;
}
void AESSmall128::decryptBlock(uint8_t *output, const uint8_t *input)
{
uint8_t schedule[16];
uint8_t round;
uint8_t posn;
uint8_t state1[16];
uint8_t state2[16];
uint8_t temp[4];
// Start with the end of the decryption schedule.
memcpy(schedule, reverse, 16);
// Copy the input into the state and reverse the final round.
for (posn = 0; posn < 16; ++posn)
state1[posn] = input[posn] ^ schedule[posn];
AESCommon::inverseShiftRowsAndSubBytes(state2, state1);
KXOR(3, 2);
KXOR(2, 1);
KXOR(1, 0);
KCORE(10);
// Perform the next 9 rounds of the decryption process.
for (round = 9; round >= 1; --round) {
// Decrypt using the key schedule.
for (posn = 0; posn < 16; ++posn)
state2[posn] ^= schedule[posn];
AESCommon::inverseMixColumn(state1, state2);
AESCommon::inverseMixColumn(state1 + 4, state2 + 4);
AESCommon::inverseMixColumn(state1 + 8, state2 + 8);
AESCommon::inverseMixColumn(state1 + 12, state2 + 12);
AESCommon::inverseShiftRowsAndSubBytes(state2, state1);
// Expand the next 16 bytes of the key schedule in reverse.
KXOR(3, 2);
KXOR(2, 1);
KXOR(1, 0);
KCORE(round);
}
// Reverse the initial round and create the output words.
for (posn = 0; posn < 16; ++posn)
output[posn] = state2[posn] ^ schedule[posn];
}
void AESSmall128::clear()
{
clean(reverse);
AESTiny128::clear();
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015 Southern Storm Software, Pty Ltd.
* Copyright (C) 2015,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"),
@ -28,7 +28,7 @@
* \class AES256 AES.h <AES.h>
* \brief AES block cipher with 256-bit keys.
*
* \sa AES128, AES192
* \sa AES128, AES192, AESTiny256, AESSmall256
*/
/**
@ -103,3 +103,295 @@ bool AES256::setKey(const uint8_t *key, size_t len)
return true;
}
/**
* \class AESTiny256 AES.h <AES.h>
* \brief AES block cipher with 256-bit keys and tiny memory usage.
*
* This class differs from the AES256 class in the following ways:
*
* \li RAM requirements are vastly reduced. The key 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 AES256 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.
*
* The companion AESSmall256 class supports decryptBlock() at the cost of
* some additional memory and slower setKey() times.
*
* \sa AESSmall256, AES256
*/
/** @cond */
// Helper macros.
#define LEFT 0
#define RIGHT 16
#define ENCRYPT(phase) \
do { \
AESCommon::subBytesAndShiftRows(state2, state1); \
AESCommon::mixColumn(state1, state2); \
AESCommon::mixColumn(state1 + 4, state2 + 4); \
AESCommon::mixColumn(state1 + 8, state2 + 8); \
AESCommon::mixColumn(state1 + 12, state2 + 12); \
for (posn = 0; posn < 16; ++posn) \
state1[posn] ^= schedule[posn + (phase)]; \
} while (0)
#define DECRYPT(phase) \
do { \
for (posn = 0; posn < 16; ++posn) \
state2[posn] ^= schedule[posn + (phase)]; \
AESCommon::inverseMixColumn(state1, state2); \
AESCommon::inverseMixColumn(state1 + 4, state2 + 4); \
AESCommon::inverseMixColumn(state1 + 8, state2 + 8); \
AESCommon::inverseMixColumn(state1 + 12, state2 + 12); \
AESCommon::inverseShiftRowsAndSubBytes(state2, state1); \
} while (0)
#define KCORE(n) \
do { \
AESCommon::keyScheduleCore(temp, schedule + 28, (n)); \
schedule[0] ^= temp[0]; \
schedule[1] ^= temp[1]; \
schedule[2] ^= temp[2]; \
schedule[3] ^= temp[3]; \
} while (0)
#define KXOR(a, b) \
do { \
schedule[(a) * 4] ^= schedule[(b) * 4]; \
schedule[(a) * 4 + 1] ^= schedule[(b) * 4 + 1]; \
schedule[(a) * 4 + 2] ^= schedule[(b) * 4 + 2]; \
schedule[(a) * 4 + 3] ^= schedule[(b) * 4 + 3]; \
} while (0)
#define KSBOX() \
do { \
AESCommon::applySbox(temp, schedule + 12); \
schedule[16] ^= temp[0]; \
schedule[17] ^= temp[1]; \
schedule[18] ^= temp[2]; \
schedule[19] ^= temp[3]; \
} while (0)
/** @endcond */
/**
* \brief Constructs an AES 256-bit 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.
*/
AESTiny256::AESTiny256()
{
}
AESTiny256::~AESTiny256()
{
clean(schedule);
}
/**
* \brief Size of an AES block in bytes.
* \return Always returns 16.
*/
size_t AESTiny256::blockSize() const
{
return 16;
}
/**
* \brief Size of a 256-bit AES key in bytes.
* \return Always returns 32.
*/
size_t AESTiny256::keySize() const
{
return 32;
}
bool AESTiny256::setKey(const uint8_t *key, size_t len)
{
if (len == 32) {
// Make a copy of the key - it will be expanded in encryptBlock().
memcpy(schedule, key, 32);
return true;
}
return false;
}
void AESTiny256::encryptBlock(uint8_t *output, const uint8_t *input)
{
uint8_t schedule[32];
uint8_t posn;
uint8_t round;
uint8_t state1[16];
uint8_t state2[16];
uint8_t temp[4];
// Start with the key in the schedule buffer.
memcpy(schedule, this->schedule, 32);
// Copy the input into the state and perform the first round.
for (posn = 0; posn < 16; ++posn)
state1[posn] = input[posn] ^ schedule[posn];
ENCRYPT(RIGHT);
// Perform the next 12 rounds of the cipher two at a time.
for (round = 1; round <= 6; ++round) {
// Expand the next 32 bytes of the key schedule.
KCORE(round);
KXOR(1, 0);
KXOR(2, 1);
KXOR(3, 2);
KSBOX();
KXOR(5, 4);
KXOR(6, 5);
KXOR(7, 6);
// Encrypt using the left and right halves of the key schedule.
ENCRYPT(LEFT);
ENCRYPT(RIGHT);
}
// Expand the final 16 bytes of the key schedule.
KCORE(7);
KXOR(1, 0);
KXOR(2, 1);
KXOR(3, 2);
// Perform the final round.
AESCommon::subBytesAndShiftRows(state2, state1);
for (posn = 0; posn < 16; ++posn)
output[posn] = state2[posn] ^ schedule[posn];
}
void AESTiny256::decryptBlock(uint8_t *output, const uint8_t *input)
{
// Decryption is not supported by AESTiny256.
}
void AESTiny256::clear()
{
clean(schedule);
}
/**
* \class AESSmall256 AES.h <AES.h>
* \brief AES block cipher with 256-bit keys and reduced memory usage.
*
* This class differs from the AES256 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.
*
* The companion AESTiny256 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 AESTiny256 may be a better
* option than AESSmall256 for many applications.
*
* \sa AESTiny256, AES256
*/
/**
* \brief Constructs an AES 256-bit 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.
*/
AESSmall256::AESSmall256()
{
}
AESSmall256::~AESSmall256()
{
clean(reverse);
}
bool AESSmall256::setKey(const uint8_t *key, size_t len)
{
uint8_t *schedule;
uint8_t round;
uint8_t temp[4];
// Set the encryption key first.
if (!AESTiny256::setKey(key, len))
return false;
// Expand the key schedule up to the last round which gives
// us the round keys to use for the final two rounds. We can
// then work backwards from there in decryptBlock().
schedule = reverse;
memcpy(schedule, key, 32);
for (round = 1; round <= 6; ++round) {
KCORE(round);
KXOR(1, 0);
KXOR(2, 1);
KXOR(3, 2);
KSBOX();
KXOR(5, 4);
KXOR(6, 5);
KXOR(7, 6);
}
KCORE(7);
KXOR(1, 0);
KXOR(2, 1);
KXOR(3, 2);
// Key is ready to go.
return true;
}
void AESSmall256::decryptBlock(uint8_t *output, const uint8_t *input)
{
uint8_t schedule[32];
uint8_t round;
uint8_t posn;
uint8_t state1[16];
uint8_t state2[16];
uint8_t temp[4];
// Start with the end of the decryption schedule.
memcpy(schedule, reverse, 32);
// Copy the input into the state and reverse the final round.
for (posn = 0; posn < 16; ++posn)
state1[posn] = input[posn] ^ schedule[posn];
AESCommon::inverseShiftRowsAndSubBytes(state2, state1);
KXOR(3, 2);
KXOR(2, 1);
KXOR(1, 0);
KCORE(7);
// Perform the next 12 rounds of the decryption process two at a time.
for (round = 6; round >= 1; --round) {
// Decrypt using the right and left halves of the key schedule.
DECRYPT(RIGHT);
DECRYPT(LEFT);
// Expand the next 32 bytes of the key schedule in reverse.
KXOR(7, 6);
KXOR(6, 5);
KXOR(5, 4);
KSBOX();
KXOR(3, 2);
KXOR(2, 1);
KXOR(1, 0);
KCORE(round);
}
// Reverse the initial round and create the output words.
DECRYPT(RIGHT);
for (posn = 0; posn < 16; ++posn)
output[posn] = state2[posn] ^ schedule[posn];
}
void AESSmall256::clear()
{
clean(reverse);
AESTiny256::clear();
}

View File

@ -43,7 +43,7 @@
* \sa ChaCha, AES128, AES192, AES256
*/
/** @cond */
/** @cond sbox */
// AES S-box (http://en.wikipedia.org/wiki/Rijndael_S-box)
static uint8_t const sbox[256] PROGMEM = {
@ -179,7 +179,9 @@ static uint8_t const K[8] = {
#define OUT(col, row) output[(col) * 4 + (row)]
#define IN(col, row) input[(col) * 4 + (row)]
static void subBytesAndShiftRows(uint8_t *output, const uint8_t *input)
/** @cond aes_funcs */
void AESCommon::subBytesAndShiftRows(uint8_t *output, const uint8_t *input)
{
OUT(0, 0) = pgm_read_byte(sbox + IN(0, 0));
OUT(0, 1) = pgm_read_byte(sbox + IN(1, 1));
@ -199,7 +201,7 @@ static void subBytesAndShiftRows(uint8_t *output, const uint8_t *input)
OUT(3, 3) = pgm_read_byte(sbox + IN(2, 3));
}
static void inverseShiftRowsAndSubBytes(uint8_t *output, const uint8_t *input)
void AESCommon::inverseShiftRowsAndSubBytes(uint8_t *output, const uint8_t *input)
{
OUT(0, 0) = pgm_read_byte(sbox_inverse + IN(0, 0));
OUT(0, 1) = pgm_read_byte(sbox_inverse + IN(3, 1));
@ -219,7 +221,7 @@ static void inverseShiftRowsAndSubBytes(uint8_t *output, const uint8_t *input)
OUT(3, 3) = pgm_read_byte(sbox_inverse + IN(0, 3));
}
static void mixColumn(uint8_t *output, uint8_t *input)
void AESCommon::mixColumn(uint8_t *output, uint8_t *input)
{
uint16_t t; // Needed by the gmul2 macro.
uint8_t a = input[0];
@ -236,7 +238,7 @@ static void mixColumn(uint8_t *output, uint8_t *input)
output[3] = a2 ^ a ^ b ^ c ^ d2;
}
static void inverseMixColumn(uint8_t *output, const uint8_t *input)
void AESCommon::inverseMixColumn(uint8_t *output, const uint8_t *input)
{
uint16_t t; // Needed by the gmul2, gmul4, and gmul8 macros.
uint8_t a = input[0];
@ -261,6 +263,8 @@ static void inverseMixColumn(uint8_t *output, const uint8_t *input)
output[3] = a8 ^ a2 ^ a ^ b8 ^ b4 ^ b ^ c8 ^ c ^ d8 ^ d4 ^ d2;
}
/** @endcond */
void AESCommon::encryptBlock(uint8_t *output, const uint8_t *input)
{
const uint8_t *roundKey = schedule;
@ -328,7 +332,7 @@ void AESCommon::clear()
clean(schedule, (rounds + 1) * 16);
}
/** @cond */
/** @cond aes_keycore */
void AESCommon::keyScheduleCore(uint8_t *output, const uint8_t *input, uint8_t iteration)
{

View File

@ -0,0 +1,170 @@
/*
* 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 small AES implementation to verify behaviour.
*/
#include <Crypto.h>
#include <AES.h>
#include <string.h>
struct TestVector
{
const char *name;
byte key[32];
byte plaintext[16];
byte ciphertext[16];
};
// Define the ECB test vectors from the FIPS specification.
static TestVector const testVectorAES128 = {
.name = "AES-128-ECB",
.key = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F},
.plaintext = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF},
.ciphertext = {0x69, 0xC4, 0xE0, 0xD8, 0x6A, 0x7B, 0x04, 0x30,
0xD8, 0xCD, 0xB7, 0x80, 0x70, 0xB4, 0xC5, 0x5A}
};
static TestVector const testVectorAES192 = {
.name = "AES-192-ECB",
.key = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17},
.plaintext = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF},
.ciphertext = {0xDD, 0xA9, 0x7C, 0xA4, 0x86, 0x4C, 0xDF, 0xE0,
0x6E, 0xAF, 0x70, 0xA0, 0xEC, 0x0D, 0x71, 0x91}
};
static TestVector const testVectorAES256 = {
.name = "AES-256-ECB",
.key = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F},
.plaintext = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF},
.ciphertext = {0x8E, 0xA2, 0xB7, 0xCA, 0x51, 0x67, 0x45, 0xBF,
0xEA, 0xFC, 0x49, 0x90, 0x4B, 0x49, 0x60, 0x89}
};
AESSmall128 aes128;
AESSmall256 aes256;
byte buffer[16];
void testCipher(BlockCipher *cipher, const struct TestVector *test)
{
crypto_feed_watchdog();
Serial.print(test->name);
Serial.print(" Encryption ... ");
cipher->setKey(test->key, cipher->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)
{
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, cipher->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("AESSmall128 ... ");
Serial.println(sizeof(AESSmall128));
Serial.print("AESSmall256 ... ");
Serial.println(sizeof(AESSmall256));
Serial.println();
Serial.println("Test Vectors:");
testCipher(&aes128, &testVectorAES128);
testCipher(&aes256, &testVectorAES256);
Serial.println();
Serial.println("Performance Tests:");
perfCipher(&aes128, &testVectorAES128);
perfCipher(&aes256, &testVectorAES256);
}
void loop()
{
}

View File

@ -0,0 +1,150 @@
/*
* 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 tiny AES implementation to verify behaviour.
*/
#include <Crypto.h>
#include <AES.h>
#include <string.h>
struct TestVector
{
const char *name;
byte key[32];
byte plaintext[16];
byte ciphertext[16];
};
// Define the ECB test vectors from the FIPS specification.
static TestVector const testVectorAES128 = {
.name = "AES-128-ECB",
.key = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F},
.plaintext = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF},
.ciphertext = {0x69, 0xC4, 0xE0, 0xD8, 0x6A, 0x7B, 0x04, 0x30,
0xD8, 0xCD, 0xB7, 0x80, 0x70, 0xB4, 0xC5, 0x5A}
};
static TestVector const testVectorAES192 = {
.name = "AES-192-ECB",
.key = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17},
.plaintext = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF},
.ciphertext = {0xDD, 0xA9, 0x7C, 0xA4, 0x86, 0x4C, 0xDF, 0xE0,
0x6E, 0xAF, 0x70, 0xA0, 0xEC, 0x0D, 0x71, 0x91}
};
static TestVector const testVectorAES256 = {
.name = "AES-256-ECB",
.key = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F},
.plaintext = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF},
.ciphertext = {0x8E, 0xA2, 0xB7, 0xCA, 0x51, 0x67, 0x45, 0xBF,
0xEA, 0xFC, 0x49, 0x90, 0x4B, 0x49, 0x60, 0x89}
};
AESTiny128 aes128;
AESTiny256 aes256;
byte buffer[16];
void testCipher(BlockCipher *cipher, const struct TestVector *test)
{
crypto_feed_watchdog();
Serial.print(test->name);
Serial.print(" Encryption ... ");
cipher->setKey(test->key, cipher->keySize());
cipher->encryptBlock(buffer, test->plaintext);
if (memcmp(buffer, test->ciphertext, 16) == 0)
Serial.println("Passed");
else
Serial.println("Failed");
}
void perfCipher(BlockCipher *cipher, const struct TestVector *test)
{
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, cipher->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.println();
}
void setup()
{
Serial.begin(9600);
Serial.println();
Serial.println("State Sizes:");
Serial.print("AESTiny128 ... ");
Serial.println(sizeof(AESTiny128));
Serial.print("AESTiny256 ... ");
Serial.println(sizeof(AESTiny256));
Serial.println();
Serial.println("Test Vectors:");
testCipher(&aes128, &testVectorAES128);
testCipher(&aes256, &testVectorAES256);
Serial.println();
Serial.println("Performance Tests:");
perfCipher(&aes128, &testVectorAES128);
perfCipher(&aes256, &testVectorAES256);
}
void loop()
{
}

View File

@ -1,6 +1,10 @@
AES128 KEYWORD1
AES192 KEYWORD1
AES256 KEYWORD1
AESTiny128 KEYWORD1
AESTiny256 KEYWORD1
AESSmall128 KEYWORD1
AESSmall256 KEYWORD1
Speck KEYWORD1
SpeckTiny KEYWORD1
SpeckSmall KEYWORD1