From 76b47204dfc40b0e285ce64a978daccaf3db3b2f Mon Sep 17 00:00:00 2001 From: Jeffrey Walton Date: Wed, 6 Feb 2019 04:14:39 -0500 Subject: Add IETF XChaCha20Poly1305 (GH #727, PR #795) --- chachapoly.cpp | 101 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) (limited to 'chachapoly.cpp') diff --git a/chachapoly.cpp b/chachapoly.cpp index 0da0a785..8816935d 100644 --- a/chachapoly.cpp +++ b/chachapoly.cpp @@ -8,6 +8,8 @@ NAMESPACE_BEGIN(CryptoPP) +////////////////////////////// IETF ChaChaTLS ////////////////////////////// + // RekeyCipherAndMac is heavier-weight than we like. The Authenc framework was // predicated on BlockCiphers, where the key and key schedule could be // calculated independent of the IV being used. However, the ChaCha and @@ -46,6 +48,7 @@ void ChaCha20Poly1305_Base::SetKeyWithoutResync(const byte *userKey, size_t user // an IV, which results in an exception. In this case the user will need // to call Resynchronize to key ChaCha and Poly1305. // RekeyCipherAndMac(userKey, userKeyLength, params); + CRYPTOPP_UNUSED(params); } void ChaCha20Poly1305_Base::Resync(const byte *iv, size_t len) @@ -103,4 +106,102 @@ bool ChaCha20Poly1305_Base::DecryptAndVerify(byte *message, const byte *mac, siz return TruncatedVerify(mac, macLength); } +////////////////////////////// IETF XChaCha20 draft ////////////////////////////// + +// RekeyCipherAndMac is heavier-weight than we like. The Authenc framework was +// predicated on BlockCiphers, where the key and key schedule could be +// calculated independent of the IV being used. However, the ChaCha and +// ChaCha20Poly1305 construction combines key setup and IV. That is, both are +// needed to key or rekey the cipher. Even a simple Resync() requires us to +// regenerate the initial state for both ChaCha20 and Poly1305. +void XChaCha20Poly1305_Base::RekeyCipherAndMac(const byte *userKey, size_t keylength, const NameValuePairs ¶ms) +{ + // Derive MAC key + AlgorithmParameters block0 = MakeParameters("InitialBlock", (word64)0, true); + AccessSymmetricCipher().SetKey(userKey, keylength, CombinedNameValuePairs(params, block0)); + + // Only the first 256-bits are used to key the MAC + SecByteBlock derived(NULLPTR, 32); + AccessSymmetricCipher().ProcessString(derived, derived.size()); + + // Key the Poly1305 MAC + AccessMAC().SetKey(derived, derived.size(), params); + + // Key the ChaCha20 cipher + AlgorithmParameters block1 = MakeParameters("InitialBlock", (word64)1, true); + AccessSymmetricCipher().SetKey(userKey, keylength, CombinedNameValuePairs(params, block1)); +} + +void XChaCha20Poly1305_Base::SetKeyWithoutResync(const byte *userKey, size_t userKeyLength, const NameValuePairs ¶ms) +{ + CRYPTOPP_ASSERT(userKey && userKeyLength == 32); + m_userKey.Assign(userKey, userKeyLength); + + // XChaCha20/Poly1305 initial state depends on both the key and IV. The + // IV may or may not be present during the call to SetKeyWithoutResync. + // If the IV is present, the framework will call SetKeyWithoutResync + // followed by Resynchronize which calls Resync. In this case we defer + // calculating the initial state until the call to Resynchronize. + // If the IV is not present, it avoids calling ChaCha's SetKey without + // an IV, which results in an exception. In this case the user will need + // to call Resynchronize to key ChaCha and Poly1305. + // RekeyCipherAndMac(userKey, userKeyLength, params); + CRYPTOPP_UNUSED(params); +} + +void XChaCha20Poly1305_Base::Resync(const byte *iv, size_t len) +{ + CRYPTOPP_ASSERT(iv && len == 24); + RekeyCipherAndMac(m_userKey, m_userKey.SizeInBytes(), + MakeParameters(Name::IV(), ConstByteArrayParameter(iv,len))); +} + +size_t XChaCha20Poly1305_Base::AuthenticateBlocks(const byte *data, size_t len) +{ + AccessMAC().Update(data, len); + return 0; +} + +void XChaCha20Poly1305_Base::AuthenticateLastHeaderBlock() +{ + // Pad to a multiple of 16 or 0 + const byte zero[16] = {0}; + size_t pad = (16 - (m_totalHeaderLength % 16)) % 16; + AccessMAC().Update(zero, pad); +} + +void XChaCha20Poly1305_Base::AuthenticateLastConfidentialBlock() +{ + // Pad to a multiple of 16 or 0 + const byte zero[16] = {0}; + size_t pad = (16 - (m_totalMessageLength % 16)) % 16; + AccessMAC().Update(zero, pad); +} + +void XChaCha20Poly1305_Base::AuthenticateLastFooterBlock(byte *mac, size_t macSize) +{ + CRYPTOPP_ALIGN_DATA(8) byte length[2*sizeof(word64)]; + PutWord(true, LITTLE_ENDIAN_ORDER, length+0, m_totalHeaderLength); + PutWord(true, LITTLE_ENDIAN_ORDER, length+8, m_totalMessageLength); + AccessMAC().Update(length, sizeof(length)); + AccessMAC().TruncatedFinal(mac, macSize); + m_state = State_KeySet; +} + +void XChaCha20Poly1305_Base::EncryptAndAuthenticate(byte *ciphertext, byte *mac, size_t macSize, const byte *iv, int ivLength, const byte *aad, size_t aadLength, const byte *message, size_t messageLength) +{ + Resynchronize(iv, ivLength); + Update(aad, aadLength); + ProcessString(ciphertext, message, messageLength); + TruncatedFinal(mac, macSize); +} + +bool XChaCha20Poly1305_Base::DecryptAndVerify(byte *message, const byte *mac, size_t macLength, const byte *iv, int ivLength, const byte *aad, size_t aadLength, const byte *ciphertext, size_t ciphertextLength) +{ + Resynchronize(iv, ivLength); + Update(aad, aadLength); + ProcessString(message, ciphertext, ciphertextLength); + return TruncatedVerify(mac, macLength); +} + NAMESPACE_END -- cgit v1.2.1