From 5a930994be93825b90ea71e1ae1123424f5c82cb Mon Sep 17 00:00:00 2001 From: Rhys Weatherley Date: Sat, 3 Jan 2015 13:24:07 +1000 Subject: [PATCH] CFB, CBC, and OFB block cipher modes --- libraries/Crypto/CBC.cpp | 171 +++++++++++++ libraries/Crypto/CBC.h | 66 +++++ libraries/Crypto/CFB.cpp | 195 +++++++++++++++ libraries/Crypto/CFB.h | 65 +++++ libraries/Crypto/CTR.cpp | 2 +- libraries/Crypto/Hash.cpp | 2 +- libraries/Crypto/OFB.cpp | 160 ++++++++++++ libraries/Crypto/OFB.h | 65 +++++ libraries/Crypto/examples/TestCBC/TestCBC.ino | 205 +++++++++++++++ libraries/Crypto/examples/TestCFB/TestCFB.ino | 235 ++++++++++++++++++ libraries/Crypto/examples/TestOFB/TestOFB.ino | 235 ++++++++++++++++++ 11 files changed, 1399 insertions(+), 2 deletions(-) create mode 100644 libraries/Crypto/CBC.cpp create mode 100644 libraries/Crypto/CBC.h create mode 100644 libraries/Crypto/CFB.cpp create mode 100644 libraries/Crypto/CFB.h create mode 100644 libraries/Crypto/OFB.cpp create mode 100644 libraries/Crypto/OFB.h create mode 100644 libraries/Crypto/examples/TestCBC/TestCBC.ino create mode 100644 libraries/Crypto/examples/TestCFB/TestCFB.ino create mode 100644 libraries/Crypto/examples/TestOFB/TestOFB.ino diff --git a/libraries/Crypto/CBC.cpp b/libraries/Crypto/CBC.cpp new file mode 100644 index 00000000..b7442da9 --- /dev/null +++ b/libraries/Crypto/CBC.cpp @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2015 Southern Storm Software, Pty Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "CBC.h" +#include "Crypto.h" +#include + +/** + * \class CBCCommon CBC.h + * \brief Concrete base class to assist with implementing CBC for + * 128-bit block ciphers. + * + * Reference: http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation + * + * \sa CBC + */ + +/** + * \brief Constructs a new cipher in CBC mode. + * + * This constructor should be followed by a call to setBlockCipher(). + */ +CBCCommon::CBCCommon() + : blockCipher(0) + , posn(16) +{ +} + +/** + * \brief Destroys this cipher object after clearing sensitive information. + */ +CBCCommon::~CBCCommon() +{ + clean(iv); + clean(temp); +} + +size_t CBCCommon::keySize() const +{ + return blockCipher->keySize(); +} + +size_t CBCCommon::ivSize() const +{ + return 16; +} + +bool CBCCommon::setKey(const uint8_t *key, size_t len) +{ + // Verify the cipher's block size, just in case. + if (blockCipher->blockSize() != 16) + return false; + + // Set the key on the underlying block cipher. + return blockCipher->setKey(key, len); +} + +bool CBCCommon::setIV(const uint8_t *iv, size_t len) +{ + if (len != 16) + return false; + memcpy(this->iv, iv, 16); + posn = 16; + return true; +} + +void CBCCommon::encrypt(uint8_t *output, const uint8_t *input, size_t len) +{ + uint8_t posn; + while (len >= 16) { + for (posn = 0; posn < 16; ++posn) + iv[posn] ^= *input++; + blockCipher->encryptBlock(iv, iv); + for (posn = 0; posn < 16; ++posn) + *output++ = iv[posn]; + len -= 16; + } +} + +void CBCCommon::decrypt(uint8_t *output, const uint8_t *input, size_t len) +{ + uint8_t posn; + while (len >= 16) { + blockCipher->decryptBlock(temp, input); + for (posn = 0; posn < 16; ++posn) { + uint8_t in = *input++; + *output++ = temp[posn] ^ iv[posn]; + iv[posn] = in; + } + len -= 16; + } +} + +void CBCCommon::clear() +{ + blockCipher->clear(); + clean(iv); + clean(temp); + posn = 16; +} + +/** + * \fn void CBCCommon::setBlockCipher(BlockCipher *cipher) + * \brief Sets the block cipher to use for this CBC object. + * + * \param cipher The block cipher to use to implement CBC mode, + * which must have a block size of 16 bytes (128 bits). + */ + +/** + * \class CBC CBC.h + * \brief Implementation of the Cipher Block Chaining (CBC) mode for + * 128-bit block ciphers. + * + * The template parameter T must be a concrete subclass of BlockCipher + * indicating the specific block cipher to use. T must have a block size + * of 16 bytes (128 bits). + * + * For example, the following creates a CBC object using AES192 as the + * underlying cipher: + * + * \code + * CBC cbc; + * cbc.setKey(key, 24); + * cbc.setIV(iv, 16); + * cbc.encrypt(output, input, len); + * \endcode + * + * Decryption is similar: + * + * \code + * CBC cbc; + * cbc.setKey(key, 24); + * cbc.setIV(iv, 16); + * cbc.decrypt(output, input, len); + * \endcode + * + * The size of the ciphertext will always be the same as the size of + * the plaintext. Also, the length of the plaintext/ciphertext must be a + * multiple of 16. Extra bytes are ignored and not encrypted. The caller + * is responsible for padding the underlying data to a multiple of 16 + * using an appropriate padding scheme for the application. + * + * Reference: http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation + * + * \sa CTR, CFB, OFB + */ + +/** + * \fn CBC::CBC() + * \brief Constructs a new CBC object for the block cipher T. + */ diff --git a/libraries/Crypto/CBC.h b/libraries/Crypto/CBC.h new file mode 100644 index 00000000..7554b896 --- /dev/null +++ b/libraries/Crypto/CBC.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2015 Southern Storm Software, Pty Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef CRYPTO_CBC_h +#define CRYPTO_CBC_h + +#include "Cipher.h" +#include "BlockCipher.h" + +class CBCCommon : public Cipher +{ +public: + virtual ~CBCCommon(); + + size_t keySize() const; + size_t ivSize() const; + + bool setKey(const uint8_t *key, size_t len); + bool setIV(const uint8_t *iv, size_t len); + + void encrypt(uint8_t *output, const uint8_t *input, size_t len); + void decrypt(uint8_t *output, const uint8_t *input, size_t len); + + void clear(); + +protected: + CBCCommon(); + void setBlockCipher(BlockCipher *cipher) { blockCipher = cipher; } + +private: + BlockCipher *blockCipher; + uint8_t iv[16]; + uint8_t temp[16]; + uint8_t posn; +}; + +template +class CBC : public CBCCommon +{ +public: + CBC() { setBlockCipher(&cipher); } + +private: + T cipher; +}; + +#endif diff --git a/libraries/Crypto/CFB.cpp b/libraries/Crypto/CFB.cpp new file mode 100644 index 00000000..beb4f817 --- /dev/null +++ b/libraries/Crypto/CFB.cpp @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2015 Southern Storm Software, Pty Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "CFB.h" +#include "Crypto.h" +#include + +/** + * \class CFBCommon CFB.h + * \brief Concrete base class to assist with implementing CFB for + * 128-bit block ciphers. + * + * Reference: http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation + * + * \sa CFB + */ + +/** + * \brief Constructs a new cipher in CFB mode. + * + * This constructor should be followed by a call to setBlockCipher(). + */ +CFBCommon::CFBCommon() + : blockCipher(0) + , posn(16) +{ +} + +/** + * \brief Destroys this cipher object after clearing sensitive information. + */ +CFBCommon::~CFBCommon() +{ + clean(iv); +} + +size_t CFBCommon::keySize() const +{ + return blockCipher->keySize(); +} + +size_t CFBCommon::ivSize() const +{ + return 16; +} + +bool CFBCommon::setKey(const uint8_t *key, size_t len) +{ + // Verify the cipher's block size, just in case. + if (blockCipher->blockSize() != 16) + return false; + + // Set the key on the underlying block cipher. + return blockCipher->setKey(key, len); +} + +bool CFBCommon::setIV(const uint8_t *iv, size_t len) +{ + if (len != 16) + return false; + memcpy(this->iv, iv, 16); + posn = 16; + return true; +} + +void CFBCommon::encrypt(uint8_t *output, const uint8_t *input, size_t len) +{ + uint8_t size; + while (len > 0) { + // If we have exhausted the current keystream block, then encrypt + // the IV/ciphertext to get another keystream block. + if (posn >= 16) { + blockCipher->encryptBlock(iv, iv); + posn = 0; + } + + // XOR the plaintext with the encrypted IV to get the new ciphertext. + // We keep building up the ciphertext byte by byte in the IV buffer + // until we have a full block's worth, and then the IV is encrypted + // again by the code above. + size = 16 - posn; + if (size > len) + size = len; + len -= size; + while (size > 0) { + iv[posn] ^= *input++; + *output++ = iv[posn++]; + --size; + } + } +} + +void CFBCommon::decrypt(uint8_t *output, const uint8_t *input, size_t len) +{ + uint8_t size; + while (len > 0) { + // If we have exhausted the current keystream block, then encrypt + // the IV/ciphertext to get another keystream block. + if (posn >= 16) { + blockCipher->encryptBlock(iv, iv); + posn = 0; + } + + // XOR the ciphertext with the encrypted IV to get the new plaintext. + // We keep building up the ciphertext byte by byte in the IV buffer + // until we have a full block's worth, and then the IV is encrypted + // again by the code above. + size = 16 - posn; + if (size > len) + size = len; + len -= size; + while (size > 0) { + uint8_t in = *input++; + *output++ = iv[posn] ^ in; + iv[posn++] = in; + --size; + } + } +} + +void CFBCommon::clear() +{ + blockCipher->clear(); + clean(iv); + posn = 16; +} + +/** + * \fn void CFBCommon::setBlockCipher(BlockCipher *cipher) + * \brief Sets the block cipher to use for this CFB object. + * + * \param cipher The block cipher to use to implement CFB mode, + * which must have a block size of 16 bytes (128 bits). + */ + +/** + * \class CFB CFB.h + * \brief Implementation of the Cipher Feedback (CFB) mode for + * 128-bit block ciphers. + * + * The template parameter T must be a concrete subclass of BlockCipher + * indicating the specific block cipher to use. T must have a block size + * of 16 bytes (128 bits). The size of the CFB shift register is the same + * as the block size. + * + * For example, the following creates a CFB object using AES192 as the + * underlying cipher: + * + * \code + * CFB cfb; + * cfb.setKey(key, 24); + * cfb.setIV(iv, 16); + * cfb.encrypt(output, input, len); + * \endcode + * + * Decryption is similar: + * + * \code + * CFB cfb; + * cfb.setKey(key, 24); + * cfb.setIV(iv, 16); + * cfb.decrypt(output, input, len); + * \endcode + * + * The size of the ciphertext will always be the same as the size of + * the plaintext. + * + * Reference: http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation + * + * \sa CTR, OFB, CBC + */ + +/** + * \fn CFB::CFB() + * \brief Constructs a new CFB object for the block cipher T. + */ diff --git a/libraries/Crypto/CFB.h b/libraries/Crypto/CFB.h new file mode 100644 index 00000000..23efa0b6 --- /dev/null +++ b/libraries/Crypto/CFB.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2015 Southern Storm Software, Pty Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef CRYPTO_CFB_h +#define CRYPTO_CFB_h + +#include "Cipher.h" +#include "BlockCipher.h" + +class CFBCommon : public Cipher +{ +public: + virtual ~CFBCommon(); + + size_t keySize() const; + size_t ivSize() const; + + bool setKey(const uint8_t *key, size_t len); + bool setIV(const uint8_t *iv, size_t len); + + void encrypt(uint8_t *output, const uint8_t *input, size_t len); + void decrypt(uint8_t *output, const uint8_t *input, size_t len); + + void clear(); + +protected: + CFBCommon(); + void setBlockCipher(BlockCipher *cipher) { blockCipher = cipher; } + +private: + BlockCipher *blockCipher; + uint8_t iv[16]; + uint8_t posn; +}; + +template +class CFB : public CFBCommon +{ +public: + CFB() { setBlockCipher(&cipher); } + +private: + T cipher; +}; + +#endif diff --git a/libraries/Crypto/CTR.cpp b/libraries/Crypto/CTR.cpp index fe4bf81f..72f4e9e7 100644 --- a/libraries/Crypto/CTR.cpp +++ b/libraries/Crypto/CTR.cpp @@ -211,7 +211,7 @@ void CTRCommon::clear() * * Reference: http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation * - * \sa CFB, Cipher + * \sa CFB, OFB, CBC */ /** diff --git a/libraries/Crypto/Hash.cpp b/libraries/Crypto/Hash.cpp index dc81ac7a..e6801107 100644 --- a/libraries/Crypto/Hash.cpp +++ b/libraries/Crypto/Hash.cpp @@ -23,7 +23,7 @@ #include "Hash.h" /** - * \class Hash Hash.h + * \class Hash Hash.h * \brief Abstract base class for cryptographic hash algorithms. * * \sa SHA1, SHA256 diff --git a/libraries/Crypto/OFB.cpp b/libraries/Crypto/OFB.cpp new file mode 100644 index 00000000..22e3c988 --- /dev/null +++ b/libraries/Crypto/OFB.cpp @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2015 Southern Storm Software, Pty Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "OFB.h" +#include "Crypto.h" +#include + +/** + * \class OFBCommon OFB.h + * \brief Concrete base class to assist with implementing OFB for + * 128-bit block ciphers. + * + * Reference: http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation + * + * \sa OFB + */ + +/** + * \brief Constructs a new cipher in OFB mode. + * + * This constructor should be followed by a call to setBlockCipher(). + */ +OFBCommon::OFBCommon() + : blockCipher(0) + , posn(16) +{ +} + +/** + * \brief Destroys this cipher object after clearing sensitive information. + */ +OFBCommon::~OFBCommon() +{ + clean(iv); +} + +size_t OFBCommon::keySize() const +{ + return blockCipher->keySize(); +} + +size_t OFBCommon::ivSize() const +{ + return 16; +} + +bool OFBCommon::setKey(const uint8_t *key, size_t len) +{ + // Verify the cipher's block size, just in case. + if (blockCipher->blockSize() != 16) + return false; + + // Set the key on the underlying block cipher. + return blockCipher->setKey(key, len); +} + +bool OFBCommon::setIV(const uint8_t *iv, size_t len) +{ + if (len != 16) + return false; + memcpy(this->iv, iv, 16); + posn = 16; + return true; +} + +void OFBCommon::encrypt(uint8_t *output, const uint8_t *input, size_t len) +{ + uint8_t size; + while (len > 0) { + // If we have exhausted the current keystream block, then encrypt + // the IV/ciphertext to get another keystream block. + if (posn >= 16) { + blockCipher->encryptBlock(iv, iv); + posn = 0; + } + + // XOR the plaintext with the encrypted IV to get the new ciphertext. + size = 16 - posn; + if (size > len) + size = len; + len -= size; + while (size > 0) { + *output++ = *input++ ^ iv[posn++]; + --size; + } + } +} + +void OFBCommon::decrypt(uint8_t *output, const uint8_t *input, size_t len) +{ + encrypt(output, input, len); +} + +void OFBCommon::clear() +{ + blockCipher->clear(); + clean(iv); + posn = 16; +} + +/** + * \fn void OFBCommon::setBlockCipher(BlockCipher *cipher) + * \brief Sets the block cipher to use for this OFB object. + * + * \param cipher The block cipher to use to implement OFB mode, + * which must have a block size of 16 bytes (128 bits). + */ + +/** + * \class OFB OFB.h + * \brief Implementation of the Output Feedback (OFB) mode for + * 128-bit block ciphers. + * + * The template parameter T must be a concrete subclass of BlockCipher + * indicating the specific block cipher to use. T must have a block size + * of 16 bytes (128 bits). + * + * For example, the following creates a OFB object using AES192 as the + * underlying cipher: + * + * \code + * OFB ofb; + * ofb.setKey(key, 24); + * ofb.setIV(iv, 16); + * ofb.encrypt(output, input, len); + * \endcode + * + * Decryption is identical to encryption for OFB mode. + * + * The size of the ciphertext will always be the same as the size of + * the plaintext. + * + * Reference: http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation + * + * \sa CTR, CFB, CBC + */ + +/** + * \fn OFB::OFB() + * \brief Constructs a new OFB object for the block cipher T. + */ diff --git a/libraries/Crypto/OFB.h b/libraries/Crypto/OFB.h new file mode 100644 index 00000000..762d1ce4 --- /dev/null +++ b/libraries/Crypto/OFB.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2015 Southern Storm Software, Pty Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef CRYPTO_OFB_h +#define CRYPTO_OFB_h + +#include "Cipher.h" +#include "BlockCipher.h" + +class OFBCommon : public Cipher +{ +public: + virtual ~OFBCommon(); + + size_t keySize() const; + size_t ivSize() const; + + bool setKey(const uint8_t *key, size_t len); + bool setIV(const uint8_t *iv, size_t len); + + void encrypt(uint8_t *output, const uint8_t *input, size_t len); + void decrypt(uint8_t *output, const uint8_t *input, size_t len); + + void clear(); + +protected: + OFBCommon(); + void setBlockCipher(BlockCipher *cipher) { blockCipher = cipher; } + +private: + BlockCipher *blockCipher; + uint8_t iv[16]; + uint8_t posn; +}; + +template +class OFB : public OFBCommon +{ +public: + OFB() { setBlockCipher(&cipher); } + +private: + T cipher; +}; + +#endif diff --git a/libraries/Crypto/examples/TestCBC/TestCBC.ino b/libraries/Crypto/examples/TestCBC/TestCBC.ino new file mode 100644 index 00000000..3225735b --- /dev/null +++ b/libraries/Crypto/examples/TestCBC/TestCBC.ino @@ -0,0 +1,205 @@ +/* + * 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 CBC implementation to verify correct behaviour. +*/ + +#include +#include +#include +#include + +#define MAX_PLAINTEXT_SIZE 64 +#define MAX_CIPHERTEXT_SIZE 64 + +struct TestVector +{ + const char *name; + byte key[16]; + byte plaintext[MAX_PLAINTEXT_SIZE]; + byte ciphertext[MAX_CIPHERTEXT_SIZE]; + byte iv[16]; + size_t size; +}; + +// Test vectors for AES in CBC mode from section F.2 of: +// http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf +static TestVector const testVectorAES128CBC1 = { + .name = "AES-128-CBC", + .key = {0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, + 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c}, + .plaintext = {0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, + 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, + 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, + 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, + 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, + 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, + 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, + 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10}, + .ciphertext = {0x76, 0x49, 0xab, 0xac, 0x81, 0x19, 0xb2, 0x46, + 0xce, 0xe9, 0x8e, 0x9b, 0x12, 0xe9, 0x19, 0x7d, + 0x50, 0x86, 0xcb, 0x9b, 0x50, 0x72, 0x19, 0xee, + 0x95, 0xdb, 0x11, 0x3a, 0x91, 0x76, 0x78, 0xb2, + 0x73, 0xbe, 0xd6, 0xb8, 0xe3, 0xc1, 0x74, 0x3b, + 0x71, 0x16, 0xe6, 0x9e, 0x22, 0x22, 0x95, 0x16, + 0x3f, 0xf1, 0xca, 0xa1, 0x68, 0x1f, 0xac, 0x09, + 0x12, 0x0e, 0xca, 0x30, 0x75, 0x86, 0xe1, 0xa7}, + .iv = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, + .size = 64 +}; + +CBC cbcaes128; + +byte buffer[128]; + +bool testCipher_N(Cipher *cipher, const struct TestVector *test, size_t inc) +{ + byte output[MAX_CIPHERTEXT_SIZE]; + size_t posn, len; + + cipher->clear(); + if (!cipher->setKey(test->key, cipher->keySize())) { + Serial.print("setKey "); + return false; + } + if (!cipher->setIV(test->iv, cipher->ivSize())) { + Serial.print("setIV "); + return false; + } + + memset(output, 0xBA, sizeof(output)); + + for (posn = 0; posn < test->size; posn += inc) { + len = test->size - posn; + if (len > inc) + len = inc; + cipher->encrypt(output + posn, test->plaintext + posn, len); + } + + if (memcmp(output, test->ciphertext, test->size) != 0) { + Serial.print(output[0], HEX); + Serial.print("->"); + Serial.print(test->ciphertext[0], HEX); + return false; + } + + cipher->setKey(test->key, cipher->keySize()); + cipher->setIV(test->iv, cipher->ivSize()); + + for (posn = 0; posn < test->size; posn += inc) { + len = test->size - posn; + if (len > inc) + len = inc; + cipher->decrypt(output + posn, test->ciphertext + posn, len); + } + + if (memcmp(output, test->plaintext, test->size) != 0) + return false; + + return true; +} + +void testCipher(Cipher *cipher, const struct TestVector *test) +{ + bool ok; + + Serial.print(test->name); + Serial.print(" ... "); + + // The CBC class only accepts input that is a multiple of 16 in size. + ok = testCipher_N(cipher, test, test->size); + ok &= testCipher_N(cipher, test, 16); + ok &= testCipher_N(cipher, test, 32); + + if (ok) + Serial.println("Passed"); + else + Serial.println("Failed"); +} + +void perfCipherEncrypt(const char *name, Cipher *cipher, const struct TestVector *test) +{ + unsigned long start; + unsigned long elapsed; + int count; + + Serial.print(name); + Serial.print(" ... "); + + cipher->setKey(test->key, cipher->keySize()); + cipher->setIV(test->iv, cipher->ivSize()); + start = micros(); + for (count = 0; count < 500; ++count) { + cipher->encrypt(buffer, buffer, sizeof(buffer)); + } + elapsed = micros() - start; + + Serial.print(elapsed / (sizeof(buffer) * 500.0)); + Serial.print("us per byte, "); + Serial.print((sizeof(buffer) * 500.0 * 1000000.0) / elapsed); + Serial.println(" bytes per second"); +} + +void perfCipherDecrypt(const char *name, Cipher *cipher, const struct TestVector *test) +{ + unsigned long start; + unsigned long elapsed; + int count; + + Serial.print(name); + Serial.print(" ... "); + + cipher->setKey(test->key, cipher->keySize()); + cipher->setIV(test->iv, cipher->ivSize()); + start = micros(); + for (count = 0; count < 500; ++count) { + cipher->decrypt(buffer, buffer, sizeof(buffer)); + } + elapsed = micros() - start; + + Serial.print(elapsed / (sizeof(buffer) * 500.0)); + Serial.print("us per byte, "); + Serial.print((sizeof(buffer) * 500.0 * 1000000.0) / elapsed); + Serial.println(" bytes per second"); +} + +void setup() +{ + Serial.begin(9600); + + Serial.println(); + + Serial.println("Test Vectors:"); + testCipher(&cbcaes128, &testVectorAES128CBC1); + + Serial.println(); + + Serial.println("Performance Tests:"); + perfCipherEncrypt("AES-128-CBC Encrypt", &cbcaes128, &testVectorAES128CBC1); + perfCipherDecrypt("AES-128-CBC Decrypt", &cbcaes128, &testVectorAES128CBC1); +} + +void loop() +{ +} diff --git a/libraries/Crypto/examples/TestCFB/TestCFB.ino b/libraries/Crypto/examples/TestCFB/TestCFB.ino new file mode 100644 index 00000000..c1390a97 --- /dev/null +++ b/libraries/Crypto/examples/TestCFB/TestCFB.ino @@ -0,0 +1,235 @@ +/* + * 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 CFB implementation to verify correct behaviour. +*/ + +#include +#include +#include +#include + +#define MAX_PLAINTEXT_SIZE 64 +#define MAX_CIPHERTEXT_SIZE 64 + +struct TestVector +{ + const char *name; + byte key[16]; + byte plaintext[MAX_PLAINTEXT_SIZE]; + byte ciphertext[MAX_CIPHERTEXT_SIZE]; + byte iv[16]; + size_t size; +}; + +// Test vectors for AES in CFB mode from section F.3 of: +// http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf +static TestVector const testVectorAES128CFB1 = { + .name = "AES-128-CFB #1", + .key = {0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, + 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c}, + .plaintext = {0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, + 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, + 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, + 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, + 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, + 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, + 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, + 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10}, + .ciphertext = {0x3b, 0x3f, 0xd9, 0x2e, 0xb7, 0x2d, 0xad, 0x20, + 0x33, 0x34, 0x49, 0xf8, 0xe8, 0x3c, 0xfb, 0x4a, + 0xc8, 0xa6, 0x45, 0x37, 0xa0, 0xb3, 0xa9, 0x3f, + 0xcd, 0xe3, 0xcd, 0xad, 0x9f, 0x1c, 0xe5, 0x8b, + 0x26, 0x75, 0x1f, 0x67, 0xa3, 0xcb, 0xb1, 0x40, + 0xb1, 0x80, 0x8c, 0xf1, 0x87, 0xa4, 0xf4, 0xdf, + 0xc0, 0x4b, 0x05, 0x35, 0x7c, 0x5d, 0x1c, 0x0e, + 0xea, 0xc4, 0xc6, 0x6f, 0x9f, 0xf7, 0xf2, 0xe6}, + .iv = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, + .size = 64 +}; +static TestVector const testVectorAES128CFB2 = { + // Same as above, but with a short last block to check that + // CFB mode works correctly on non block-aligned plaintext. + .name = "AES-128-CFB #2", + .key = {0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, + 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c}, + .plaintext = {0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, + 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, + 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, + 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, + 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, + 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, + 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, + 0xad, 0x2b, 0x41, 0x7b, 0xe6}, + .ciphertext = {0x3b, 0x3f, 0xd9, 0x2e, 0xb7, 0x2d, 0xad, 0x20, + 0x33, 0x34, 0x49, 0xf8, 0xe8, 0x3c, 0xfb, 0x4a, + 0xc8, 0xa6, 0x45, 0x37, 0xa0, 0xb3, 0xa9, 0x3f, + 0xcd, 0xe3, 0xcd, 0xad, 0x9f, 0x1c, 0xe5, 0x8b, + 0x26, 0x75, 0x1f, 0x67, 0xa3, 0xcb, 0xb1, 0x40, + 0xb1, 0x80, 0x8c, 0xf1, 0x87, 0xa4, 0xf4, 0xdf, + 0xc0, 0x4b, 0x05, 0x35, 0x7c, 0x5d, 0x1c, 0x0e, + 0xea, 0xc4, 0xc6, 0x6f, 0x9f}, + .iv = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, + .size = 61 +}; + +CFB cfbaes128; + +byte buffer[128]; + +bool testCipher_N(Cipher *cipher, const struct TestVector *test, size_t inc) +{ + byte output[MAX_CIPHERTEXT_SIZE]; + size_t posn, len; + + cipher->clear(); + if (!cipher->setKey(test->key, cipher->keySize())) { + Serial.print("setKey "); + return false; + } + if (!cipher->setIV(test->iv, cipher->ivSize())) { + Serial.print("setIV "); + return false; + } + + memset(output, 0xBA, sizeof(output)); + + for (posn = 0; posn < test->size; posn += inc) { + len = test->size - posn; + if (len > inc) + len = inc; + cipher->encrypt(output + posn, test->plaintext + posn, len); + } + + if (memcmp(output, test->ciphertext, test->size) != 0) { + Serial.print(output[0], HEX); + Serial.print("->"); + Serial.print(test->ciphertext[0], HEX); + return false; + } + + cipher->setKey(test->key, cipher->keySize()); + cipher->setIV(test->iv, cipher->ivSize()); + + for (posn = 0; posn < test->size; posn += inc) { + len = test->size - posn; + if (len > inc) + len = inc; + cipher->decrypt(output + posn, test->ciphertext + posn, len); + } + + if (memcmp(output, test->plaintext, test->size) != 0) + return false; + + return true; +} + +void testCipher(Cipher *cipher, const struct TestVector *test) +{ + bool ok; + + Serial.print(test->name); + Serial.print(" ... "); + + ok = testCipher_N(cipher, test, test->size); + ok &= testCipher_N(cipher, test, 1); + ok &= testCipher_N(cipher, test, 2); + ok &= testCipher_N(cipher, test, 5); + ok &= testCipher_N(cipher, test, 8); + ok &= testCipher_N(cipher, test, 13); + ok &= testCipher_N(cipher, test, 16); + + if (ok) + Serial.println("Passed"); + else + Serial.println("Failed"); +} + +void perfCipherEncrypt(const char *name, Cipher *cipher, const struct TestVector *test) +{ + unsigned long start; + unsigned long elapsed; + int count; + + Serial.print(name); + Serial.print(" ... "); + + cipher->setKey(test->key, cipher->keySize()); + cipher->setIV(test->iv, cipher->ivSize()); + start = micros(); + for (count = 0; count < 500; ++count) { + cipher->encrypt(buffer, buffer, sizeof(buffer)); + } + elapsed = micros() - start; + + Serial.print(elapsed / (sizeof(buffer) * 500.0)); + Serial.print("us per byte, "); + Serial.print((sizeof(buffer) * 500.0 * 1000000.0) / elapsed); + Serial.println(" bytes per second"); +} + +void perfCipherDecrypt(const char *name, Cipher *cipher, const struct TestVector *test) +{ + unsigned long start; + unsigned long elapsed; + int count; + + Serial.print(name); + Serial.print(" ... "); + + cipher->setKey(test->key, cipher->keySize()); + cipher->setIV(test->iv, cipher->ivSize()); + start = micros(); + for (count = 0; count < 500; ++count) { + cipher->decrypt(buffer, buffer, sizeof(buffer)); + } + elapsed = micros() - start; + + Serial.print(elapsed / (sizeof(buffer) * 500.0)); + Serial.print("us per byte, "); + Serial.print((sizeof(buffer) * 500.0 * 1000000.0) / elapsed); + Serial.println(" bytes per second"); +} + +void setup() +{ + Serial.begin(9600); + + Serial.println(); + + Serial.println("Test Vectors:"); + testCipher(&cfbaes128, &testVectorAES128CFB1); + testCipher(&cfbaes128, &testVectorAES128CFB2); + + Serial.println(); + + Serial.println("Performance Tests:"); + perfCipherEncrypt("AES-128-CFB Encrypt", &cfbaes128, &testVectorAES128CFB1); + perfCipherDecrypt("AES-128-CFB Decrypt", &cfbaes128, &testVectorAES128CFB1); +} + +void loop() +{ +} diff --git a/libraries/Crypto/examples/TestOFB/TestOFB.ino b/libraries/Crypto/examples/TestOFB/TestOFB.ino new file mode 100644 index 00000000..2918c9e7 --- /dev/null +++ b/libraries/Crypto/examples/TestOFB/TestOFB.ino @@ -0,0 +1,235 @@ +/* + * 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 OFB implementation to verify correct behaviour. +*/ + +#include +#include +#include +#include + +#define MAX_PLAINTEXT_SIZE 64 +#define MAX_CIPHERTEXT_SIZE 64 + +struct TestVector +{ + const char *name; + byte key[16]; + byte plaintext[MAX_PLAINTEXT_SIZE]; + byte ciphertext[MAX_CIPHERTEXT_SIZE]; + byte iv[16]; + size_t size; +}; + +// Test vectors for AES in OFB mode from section F.4 of: +// http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf +static TestVector const testVectorAES128OFB1 = { + .name = "AES-128-OFB", + .key = {0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, + 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c}, + .plaintext = {0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, + 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, + 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, + 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, + 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, + 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, + 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, + 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10}, + .ciphertext = {0x3b, 0x3f, 0xd9, 0x2e, 0xb7, 0x2d, 0xad, 0x20, + 0x33, 0x34, 0x49, 0xf8, 0xe8, 0x3c, 0xfb, 0x4a, + 0x77, 0x89, 0x50, 0x8d, 0x16, 0x91, 0x8f, 0x03, + 0xf5, 0x3c, 0x52, 0xda, 0xc5, 0x4e, 0xd8, 0x25, + 0x97, 0x40, 0x05, 0x1e, 0x9c, 0x5f, 0xec, 0xf6, + 0x43, 0x44, 0xf7, 0xa8, 0x22, 0x60, 0xed, 0xcc, + 0x30, 0x4c, 0x65, 0x28, 0xf6, 0x59, 0xc7, 0x78, + 0x66, 0xa5, 0x10, 0xd9, 0xc1, 0xd6, 0xae, 0x5e}, + .iv = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, + .size = 64 +}; +static TestVector const testVectorAES128OFB2 = { + // Same as above, but with a short last block to check that + // OFB mode works correctly on non block-aligned plaintext. + .name = "AES-128-OFB", + .key = {0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, + 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c}, + .plaintext = {0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, + 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, + 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, + 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, + 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, + 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, + 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, + 0xad, 0x2b, 0x41, 0x7b, 0xe6}, + .ciphertext = {0x3b, 0x3f, 0xd9, 0x2e, 0xb7, 0x2d, 0xad, 0x20, + 0x33, 0x34, 0x49, 0xf8, 0xe8, 0x3c, 0xfb, 0x4a, + 0x77, 0x89, 0x50, 0x8d, 0x16, 0x91, 0x8f, 0x03, + 0xf5, 0x3c, 0x52, 0xda, 0xc5, 0x4e, 0xd8, 0x25, + 0x97, 0x40, 0x05, 0x1e, 0x9c, 0x5f, 0xec, 0xf6, + 0x43, 0x44, 0xf7, 0xa8, 0x22, 0x60, 0xed, 0xcc, + 0x30, 0x4c, 0x65, 0x28, 0xf6, 0x59, 0xc7, 0x78, + 0x66, 0xa5, 0x10, 0xd9, 0xc1}, + .iv = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, + .size = 61 +}; + +OFB ofbaes128; + +byte buffer[128]; + +bool testCipher_N(Cipher *cipher, const struct TestVector *test, size_t inc) +{ + byte output[MAX_CIPHERTEXT_SIZE]; + size_t posn, len; + + cipher->clear(); + if (!cipher->setKey(test->key, cipher->keySize())) { + Serial.print("setKey "); + return false; + } + if (!cipher->setIV(test->iv, cipher->ivSize())) { + Serial.print("setIV "); + return false; + } + + memset(output, 0xBA, sizeof(output)); + + for (posn = 0; posn < test->size; posn += inc) { + len = test->size - posn; + if (len > inc) + len = inc; + cipher->encrypt(output + posn, test->plaintext + posn, len); + } + + if (memcmp(output, test->ciphertext, test->size) != 0) { + Serial.print(output[0], HEX); + Serial.print("->"); + Serial.print(test->ciphertext[0], HEX); + return false; + } + + cipher->setKey(test->key, cipher->keySize()); + cipher->setIV(test->iv, cipher->ivSize()); + + for (posn = 0; posn < test->size; posn += inc) { + len = test->size - posn; + if (len > inc) + len = inc; + cipher->decrypt(output + posn, test->ciphertext + posn, len); + } + + if (memcmp(output, test->plaintext, test->size) != 0) + return false; + + return true; +} + +void testCipher(Cipher *cipher, const struct TestVector *test) +{ + bool ok; + + Serial.print(test->name); + Serial.print(" ... "); + + ok = testCipher_N(cipher, test, test->size); + ok &= testCipher_N(cipher, test, 1); + ok &= testCipher_N(cipher, test, 2); + ok &= testCipher_N(cipher, test, 5); + ok &= testCipher_N(cipher, test, 8); + ok &= testCipher_N(cipher, test, 13); + ok &= testCipher_N(cipher, test, 16); + + if (ok) + Serial.println("Passed"); + else + Serial.println("Failed"); +} + +void perfCipherEncrypt(const char *name, Cipher *cipher, const struct TestVector *test) +{ + unsigned long start; + unsigned long elapsed; + int count; + + Serial.print(name); + Serial.print(" ... "); + + cipher->setKey(test->key, cipher->keySize()); + cipher->setIV(test->iv, cipher->ivSize()); + start = micros(); + for (count = 0; count < 500; ++count) { + cipher->encrypt(buffer, buffer, sizeof(buffer)); + } + elapsed = micros() - start; + + Serial.print(elapsed / (sizeof(buffer) * 500.0)); + Serial.print("us per byte, "); + Serial.print((sizeof(buffer) * 500.0 * 1000000.0) / elapsed); + Serial.println(" bytes per second"); +} + +void perfCipherDecrypt(const char *name, Cipher *cipher, const struct TestVector *test) +{ + unsigned long start; + unsigned long elapsed; + int count; + + Serial.print(name); + Serial.print(" ... "); + + cipher->setKey(test->key, cipher->keySize()); + cipher->setIV(test->iv, cipher->ivSize()); + start = micros(); + for (count = 0; count < 500; ++count) { + cipher->decrypt(buffer, buffer, sizeof(buffer)); + } + elapsed = micros() - start; + + Serial.print(elapsed / (sizeof(buffer) * 500.0)); + Serial.print("us per byte, "); + Serial.print((sizeof(buffer) * 500.0 * 1000000.0) / elapsed); + Serial.println(" bytes per second"); +} + +void setup() +{ + Serial.begin(9600); + + Serial.println(); + + Serial.println("Test Vectors:"); + testCipher(&ofbaes128, &testVectorAES128OFB1); + testCipher(&ofbaes128, &testVectorAES128OFB2); + + Serial.println(); + + Serial.println("Performance Tests:"); + perfCipherEncrypt("AES-128-OFB Encrypt", &ofbaes128, &testVectorAES128OFB1); + perfCipherDecrypt("AES-128-OFB Decrypt", &ofbaes128, &testVectorAES128OFB1); +} + +void loop() +{ +}