diff --git a/doc/crypto.dox b/doc/crypto.dox index bfcee113..d33c82b3 100644 --- a/doc/crypto.dox +++ b/doc/crypto.dox @@ -31,7 +31,7 @@ \li Stream ciphers: ChaCha \li Authenticated encryption with associated data (AEAD): ChaChaPoly, EAX, GCM \li Hash algorithms: SHA256, SHA512, SHA3_256, SHA3_512, BLAKE2s, BLAKE2b (regular and HMAC modes) -\li Message authenticators: Poly1305, GHASH +\li Message authenticators: Poly1305, GHASH, OMAC \li Public key algorithms: Curve25519, Ed25519 \li Random number generation: \link RNGClass RNG\endlink, TransistorNoiseSource, RingOscillatorNoiseSource diff --git a/doc/mainpage.dox b/doc/mainpage.dox index a9e25cc6..892772a9 100644 --- a/doc/mainpage.dox +++ b/doc/mainpage.dox @@ -96,7 +96,7 @@ realtime clock and the LCD library to implement an alarm clock. \li Stream ciphers: ChaCha \li Authenticated encryption with associated data (AEAD): ChaChaPoly, EAX, GCM \li Hash algorithms: SHA256, SHA512, SHA3_256, SHA3_512, BLAKE2s, BLAKE2b (regular and HMAC modes) -\li Message authenticators: Poly1305, GHASH +\li Message authenticators: Poly1305, GHASH, OMAC \li Public key algorithms: Curve25519, Ed25519 \li Random number generation: \link RNGClass RNG\endlink, TransistorNoiseSource, RingOscillatorNoiseSource diff --git a/libraries/Crypto/EAX.cpp b/libraries/Crypto/EAX.cpp index dc14b2ae..d5b3cffe 100644 --- a/libraries/Crypto/EAX.cpp +++ b/libraries/Crypto/EAX.cpp @@ -21,7 +21,6 @@ */ #include "EAX.h" -#include "GF128.h" #include "Crypto.h" #include @@ -42,10 +41,8 @@ * This constructor must be followed by a call to setBlockCipher(). */ EAXCommon::EAXCommon() - : blockCipher(0) { state.encPosn = 0; - state.authPosn = 0; state.authMode = 0; } @@ -56,7 +53,7 @@ EAXCommon::~EAXCommon() size_t EAXCommon::keySize() const { - return blockCipher->keySize(); + return omac.blockCipher()->keySize(); } size_t EAXCommon::ivSize() const @@ -73,7 +70,7 @@ size_t EAXCommon::tagSize() const bool EAXCommon::setKey(const uint8_t *key, size_t len) { - return blockCipher->setKey(key, len); + return omac.blockCipher()->setKey(key, len); } bool EAXCommon::setIV(const uint8_t *iv, size_t len) @@ -83,16 +80,16 @@ bool EAXCommon::setIV(const uint8_t *iv, size_t len) return false; // Hash the IV to create the initial nonce for CTR mode. Also creates B. - omacInitFirst(state.counter); - omacUpdate(state.counter, iv, len); - omacFinal(state.counter); + omac.initFirst(state.counter); + omac.update(state.counter, iv, len); + omac.finalize(state.counter); // The tag is initially the nonce value. Will be XOR'ed with // the hash of the authenticated and encrypted data later. memcpy(state.tag, state.counter, 16); // Start the hashing context for the authenticated data. - omacInit(state.hash, 1); + omac.initNext(state.hash, 1); state.encPosn = 16; state.authMode = 1; @@ -105,21 +102,21 @@ void EAXCommon::encrypt(uint8_t *output, const uint8_t *input, size_t len) if (state.authMode) closeAuthData(); encryptCTR(output, input, len); - omacUpdate(state.hash, output, len); + omac.update(state.hash, output, len); } void EAXCommon::decrypt(uint8_t *output, const uint8_t *input, size_t len) { if (state.authMode) closeAuthData(); - omacUpdate(state.hash, input, len); + omac.update(state.hash, input, len); encryptCTR(output, input, len); } void EAXCommon::addAuthData(const void *data, size_t len) { if (state.authMode) - omacUpdate(state.hash, (const uint8_t *)data, len); + omac.update(state.hash, (const uint8_t *)data, len); } void EAXCommon::computeTag(void *tag, size_t len) @@ -146,95 +143,6 @@ void EAXCommon::clear() clean(state); } -/** - * \brief Initialises the first OMAC hashing context and creates the B value. - * - * \param omac The OMAC hashing context. - */ -void EAXCommon::omacInitFirst(uint8_t omac[16]) -{ - // Start the OMAC context for the nonce. We assume that the - // data that follows will be at least 1 byte in length so that - // we can encrypt the zeroes now to derive the B value. - memset(omac, 0, 16); - blockCipher->encryptBlock(omac, omac); - state.authPosn = 0; - - // Generate the B value from the encrypted block of zeroes. - // We will need this later when finalising the OMAC hashes. - memcpy(state.b, omac, 16); - GF128::dblEAX(state.b); -} - -/** - * \brief Initialises an OMAC hashing context. - * - * \param omac The OMAC hashing context. - * \param t The tag value indicating which OMAC calculation we are doing. - */ -void EAXCommon::omacInit(uint8_t omac[16], uint8_t t) -{ - memset(omac, 0, 15); - omac[15] = t; - state.authPosn = 16; -} - -/** - * \brief Updates an OMAC hashing context with more data. - * - * \param omac The OMAC hashing context. - * \param data Points to the data to be hashed. - * \parm len The number of bytes to be hashed. - */ -void EAXCommon::omacUpdate(uint8_t omac[16], const uint8_t *data, size_t len) -{ - while (len > 0) { - // Encrypt the current block if it is already full. - if (state.authPosn == 16) { - blockCipher->encryptBlock(omac, omac); - state.authPosn = 0; - } - - // XOR the incoming data with the current block. - uint8_t size = 16 - state.authPosn; - if (size > len) - size = (uint8_t)len; - for (uint8_t index = 0; index < size; ++index) - omac[(state.authPosn)++] ^= data[index]; - - // Move onto the next block. - len -= size; - data += size; - } -} - -/** - * \brief Finalises an OMAC hashing context. - * - * \param omac The OMAC hashing context on entry, the final OMAC value on exit. - */ -void EAXCommon::omacFinal(uint8_t omac[16]) -{ - // Apply padding if necessary. - if (state.authPosn != 16) { - // Need padding: XOR with P = 2 * B. - uint32_t p[4]; - memcpy(p, state.b, 16); - GF128::dblEAX(p); - omac[state.authPosn] ^= 0x80; - for (uint8_t index = 0; index < 16; ++index) - omac[index] ^= ((const uint8_t *)p)[index]; - clean(p); - } else { - // No padding necessary: XOR with B. - for (uint8_t index = 0; index < 16; ++index) - omac[index] ^= ((const uint8_t *)(state.b))[index]; - } - - // Encrypt the hash to get the final OMAC value. - blockCipher->encryptBlock(omac, omac); -} - /** * \brief Closes the authenticated data portion of the session and * starts encryption or decryption. @@ -242,13 +150,13 @@ void EAXCommon::omacFinal(uint8_t omac[16]) void EAXCommon::closeAuthData() { // Finalise the OMAC hash and XOR it with the final tag. - omacFinal(state.hash); + omac.finalize(state.hash); for (uint8_t index = 0; index < 16; ++index) state.tag[index] ^= state.hash[index]; state.authMode = 0; // Initialise the hashing context for the ciphertext data. - omacInit(state.hash, 2); + omac.initNext(state.hash, 2); } /** @@ -266,7 +174,7 @@ void EAXCommon::encryptCTR(uint8_t *output, const uint8_t *input, size_t len) // Do we need to start a new block? if (state.encPosn == 16) { // Encrypt the counter to create the next keystream block. - blockCipher->encryptBlock(state.stream, state.counter); + omac.blockCipher()->encryptBlock(state.stream, state.counter); state.encPosn = 0; // Increment the counter, taking care not to reveal @@ -304,7 +212,7 @@ void EAXCommon::closeTag() closeAuthData(); // Finalise the hash over the ciphertext and XOR with the final tag. - omacFinal(state.hash); + omac.finalize(state.hash); for (uint8_t index = 0; index < 16; ++index) state.tag[index] ^= state.hash[index]; } diff --git a/libraries/Crypto/EAX.h b/libraries/Crypto/EAX.h index 32e2a83c..1511a5d0 100644 --- a/libraries/Crypto/EAX.h +++ b/libraries/Crypto/EAX.h @@ -25,6 +25,7 @@ #include "AuthenticatedCipher.h" #include "BlockCipher.h" +#include "OMAC.h" class EAXCommon : public AuthenticatedCipher { @@ -50,25 +51,21 @@ public: protected: EAXCommon(); - void setBlockCipher(BlockCipher *cipher) { blockCipher = cipher; } + void setBlockCipher(BlockCipher *cipher) + { + omac.setBlockCipher(cipher); + } private: - BlockCipher *blockCipher; struct { uint8_t counter[16]; uint8_t stream[16]; uint8_t tag[16]; uint8_t hash[16]; - uint32_t b[4]; uint8_t encPosn; - uint8_t authPosn; uint8_t authMode; } state; - - void omacInitFirst(uint8_t omac[16]); - void omacInit(uint8_t omac[16], uint8_t t); - void omacUpdate(uint8_t omac[16], const uint8_t *data, size_t len); - void omacFinal(uint8_t omac[16]); + OMAC omac; void closeAuthData(); void encryptCTR(uint8_t *output, const uint8_t *input, size_t len); diff --git a/libraries/Crypto/OMAC.cpp b/libraries/Crypto/OMAC.cpp new file mode 100644 index 00000000..70cf0836 --- /dev/null +++ b/libraries/Crypto/OMAC.cpp @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2016 Southern Storm Software, Pty Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "OMAC.h" +#include "GF128.h" +#include "Crypto.h" +#include + +/** + * \class OMAC OMAC.h + * \brief Implementation of the OMAC message authenticator. + * + * OMAC is the message authentication part of EAX mode. It is provided + * as a separate class for the convenience of applications that need + * message authentication separate from encryption. + * + * References: https://en.wikipedia.org/wiki/EAX_mode, + * http://web.cs.ucdavis.edu/~rogaway/papers/eax.html + * + * \sa EAX + */ + +/** + * \brief Constructs a new OMAC object. + * + * This constructor must be followed by a call to setBlockCipher() + * to specify the block cipher to use. + */ +OMAC::OMAC() + : _blockCipher(0) + , posn(0) +{ +} + +/** + * \brief Destroys this OMAC object. + * + * \sa clear() + */ +OMAC::~OMAC() +{ + clean(b); +} + +/** + * \fn BlockCipher *OMAC::blockCipher() const + * \brief Gets the block cipher that is in use for this OMAC object. + * + * \sa setBlockCipher() + */ + +/** + * \fn void OMAC::setBlockCipher(BlockCipher *cipher) + * \brief Sets the block cipher to use for this OMAC object. + * + * \param cipher The block cipher to use to implement OMAC. + * This object must have a block size of 128 bits (16 bytes). + * + * \sa blockCipher() + */ + +/** + * \brief Initialises the first OMAC hashing context and creates the B value. + * + * \param omac The OMAC hashing context. + * + * This function must be called first before initNext(), update(), or + * finalize() to create the B value from the OMAC algorithm which is + * used to finalize later hashes. It is assumed that setBlockCipher() + * has already been called. + * + * The tag value for the context is implicitly set to zero, which means + * that the context can be used for ordinary hashing as long as the + * data that follows is non-zero in length. Alternatively, initNext() + * can be called to restart the context with a specific tag. + * + * This function must be called again whenever the block cipher or the + * key changes. + * + * \sa initNext(), update(), finalize() + */ +void OMAC::initFirst(uint8_t omac[16]) +{ + // Start the OMAC context. We assume that the data that follows + // will be at least 1 byte in length so that we can encrypt the + // zeroes now to derive the B value. + memset(omac, 0, 16); + _blockCipher->encryptBlock(omac, omac); + posn = 0; + + // Generate the B value from the encrypted block of zeroes. + // We will need this later when finalising the OMAC hashes. + memcpy(b, omac, 16); + GF128::dblEAX(b); +} + +/** + * \brief Initialises or restarts an OMAC hashing context. + * + * \param omac The OMAC hashing context. + * \param tag The tag value indicating which OMAC calculation we are doing. + * + * It is assumed that initFirst() was called previously to create the B + * value for the context. + * + * \sa initFirst(), update(), finalize() + */ +void OMAC::initNext(uint8_t omac[16], uint8_t tag) +{ + memset(omac, 0, 15); + omac[15] = tag; + posn = 16; +} + +/** + * \brief Updates an OMAC hashing context with more data. + * + * \param omac The OMAC hashing context. + * \param data Points to the data to be hashed. + * \param size The number of bytes to be hashed. + * + * \sa initFirst(), initNext(), finalize() + */ +void OMAC::update(uint8_t omac[16], const uint8_t *data, size_t size) +{ + while (size > 0) { + // Encrypt the current block if it is already full. + if (posn == 16) { + _blockCipher->encryptBlock(omac, omac); + posn = 0; + } + + // XOR the incoming data with the current block. + uint8_t len = 16 - posn; + if (len > size) + len = (uint8_t)size; + for (uint8_t index = 0; index < len; ++index) + omac[posn++] ^= data[index]; + + // Move onto the next block. + size -= len; + data += len; + } +} + +/** + * \brief Finalises an OMAC hashing context. + * + * \param omac The OMAC hashing context on entry, the final OMAC value on exit. + * + * \sa initFirst(), initNext(), update() + */ +void OMAC::finalize(uint8_t omac[16]) +{ + // Apply padding if necessary. + if (posn != 16) { + // Need padding: XOR with P = 2 * B. + uint32_t p[4]; + memcpy(p, b, 16); + GF128::dblEAX(p); + omac[posn] ^= 0x80; + for (uint8_t index = 0; index < 16; ++index) + omac[index] ^= ((const uint8_t *)p)[index]; + clean(p); + } else { + // No padding necessary: XOR with B. + for (uint8_t index = 0; index < 16; ++index) + omac[index] ^= ((const uint8_t *)b)[index]; + } + + // Encrypt the hash to get the final OMAC value. + _blockCipher->encryptBlock(omac, omac); +} + +/** + * \brief Clears all security-sensitive state from this object. + */ +void OMAC::clear() +{ + clean(b); +} diff --git a/libraries/Crypto/OMAC.h b/libraries/Crypto/OMAC.h new file mode 100644 index 00000000..37e0fa74 --- /dev/null +++ b/libraries/Crypto/OMAC.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2016 Southern Storm Software, Pty Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef CRYPTO_OMAC_H +#define CRYPTO_OMAC_H + +#include "BlockCipher.h" + +class OMAC +{ +public: + OMAC(); + ~OMAC(); + + BlockCipher *blockCipher() const { return _blockCipher; } + void setBlockCipher(BlockCipher *cipher) { _blockCipher = cipher; } + + void initFirst(uint8_t omac[16]); + void initNext(uint8_t omac[16], uint8_t tag); + void update(uint8_t omac[16], const uint8_t *data, size_t size); + void finalize(uint8_t omac[16]); + + void clear(); + +private: + BlockCipher *_blockCipher; + uint32_t b[4]; + uint8_t posn; +}; + +#endif