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:
parent
bb9f0c2b96
commit
9ae79f469f
@ -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>
|
||||
|
@ -67,6 +67,8 @@ SOURCES = \
|
||||
|
||||
SKETCHES = \
|
||||
TestAES/TestAES.ino \
|
||||
TestAESTiny/TestAESTiny.ino \
|
||||
TestAESSmall/TestAESSmall.ino \
|
||||
TestBigNumberUtil/TestBigNumberUtil.ino \
|
||||
TestBLAKE2b/TestBLAKE2b.ino \
|
||||
TestBLAKE2s/TestBLAKE2s.ino \
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
|
170
libraries/Crypto/examples/TestAESSmall/TestAESSmall.ino
Normal file
170
libraries/Crypto/examples/TestAESSmall/TestAESSmall.ino
Normal 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()
|
||||
{
|
||||
}
|
150
libraries/Crypto/examples/TestAESTiny/TestAESTiny.ino
Normal file
150
libraries/Crypto/examples/TestAESTiny/TestAESTiny.ino
Normal 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()
|
||||
{
|
||||
}
|
@ -1,6 +1,10 @@
|
||||
AES128 KEYWORD1
|
||||
AES192 KEYWORD1
|
||||
AES256 KEYWORD1
|
||||
AESTiny128 KEYWORD1
|
||||
AESTiny256 KEYWORD1
|
||||
AESSmall128 KEYWORD1
|
||||
AESSmall256 KEYWORD1
|
||||
Speck KEYWORD1
|
||||
SpeckTiny KEYWORD1
|
||||
SpeckSmall KEYWORD1
|
||||
|
Loading…
x
Reference in New Issue
Block a user