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

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