diff --git a/TestVectors/chacha20poly1305.txt b/TestVectors/chacha20poly1305.txt index c480b442..f020ca89 100644 --- a/TestVectors/chacha20poly1305.txt +++ b/TestVectors/chacha20poly1305.txt @@ -56,3 +56,30 @@ Ciphertext: 64 a0 86 15 75 86 1a f4 60 f0 62 c7 9b e6 43 bd \ a6 ad 5c b4 02 2b 02 70 9b MAC: ee ad 9d 67 89 0c bb 22 39 23 36 fe a1 85 1f 38 Test: Encrypt + +AlgorithmType: AuthenticatedSymmetricCipher +Name: XChaCha20/Poly1305 +Source: draft-arciszewski-xchacha (https://tools.ietf.org/html/draft-arciszewski-xchacha), Appendix A.1 example +Key: 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f \ + 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f +IV: 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f \ + 50 51 52 53 54 55 56 57 +Header: 50 51 52 53 c0 c1 c2 c3 c4 c5 c6 c7 +Plaintext: 4c 61 64 69 65 73 20 61 6e 64 20 47 65 6e 74 6c \ + 65 6d 65 6e 20 6f 66 20 74 68 65 20 63 6c 61 73 \ + 73 20 6f 66 20 27 39 39 3a 20 49 66 20 49 20 63 \ + 6f 75 6c 64 20 6f 66 66 65 72 20 79 6f 75 20 6f \ + 6e 6c 79 20 6f 6e 65 20 74 69 70 20 66 6f 72 20 \ + 74 68 65 20 66 75 74 75 72 65 2c 20 73 75 6e 73 \ + 63 72 65 65 6e 20 77 6f 75 6c 64 20 62 65 20 69 \ + 74 2e +Ciphertext: bd 6d 17 9d 3e 83 d4 3b 95 76 57 94 93 c0 e9 39 \ + 57 2a 17 00 25 2b fa cc be d2 90 2c 21 39 6c bb \ + 73 1c 7f 1b 0b 4a a6 44 0b f3 a8 2f 4e da 7e 39 \ + ae 64 c6 70 8c 54 c2 16 cb 96 b7 2e 12 13 b4 52 \ + 2f 8c 9b a4 0d b5 d9 45 b1 1b 69 b9 82 c1 bb 9e \ + 3f 3f ac 2b c3 69 48 8f 76 b2 38 35 65 d3 ff f9 \ + 21 f9 66 4c 97 63 7d a9 76 88 12 f6 15 c6 8b 13 \ + b5 2e +MAC: c0 87 59 24 c1 c7 98 79 47 de af d8 78 0a cf 49 +Test: Encrypt diff --git a/chacha.cpp b/chacha.cpp index 437aeff7..e8e26fa9 100644 --- a/chacha.cpp +++ b/chacha.cpp @@ -558,7 +558,7 @@ void XChaCha20_Policy::SeekToIteration(lword iterationCount) { // Should we throw here??? XChaCha does not have a block // counter, so I'm not sure how to seek on it. - CRYPTOPP_ASSERT(0); + CRYPTOPP_ASSERT(0); CRYPTOPP_UNUSED(iterationCount); } unsigned int XChaCha20_Policy::GetAlignment() const 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 diff --git a/chachapoly.h b/chachapoly.h index cb6d8fce..630b3c49 100644 --- a/chachapoly.h +++ b/chachapoly.h @@ -21,6 +21,8 @@ NAMESPACE_BEGIN(CryptoPP) +////////////////////////////// IETF ChaChaTLS ////////////////////////////// + /// \brief ChaCha20Poly1305 cipher base implementation /// \details Base implementation of the AuthenticatedSymmetricCipher interface /// \since Crypto++ 8.1 @@ -166,6 +168,153 @@ struct ChaCha20Poly1305 : public AuthenticatedSymmetricCipherDocumentation typedef ChaCha20Poly1305_Final Decryption; }; +////////////////////////////// IETF XChaCha20 draft ////////////////////////////// + +/// \brief XChaCha20Poly1305 cipher base implementation +/// \details Base implementation of the AuthenticatedSymmetricCipher interface +/// \since Crypto++ 8.1 +class XChaCha20Poly1305_Base : public AuthenticatedSymmetricCipherBase +{ +public: + virtual ~XChaCha20Poly1305_Base() {} + + virtual const MessageAuthenticationCode & GetMAC() const = 0; + virtual MessageAuthenticationCode & AccessMAC() = 0; + +public: + // AuthenticatedSymmetricCipher + std::string AlgorithmName() const + {return std::string("XChaCha20/Poly1305");} + std::string AlgorithmProvider() const + {return GetSymmetricCipher().AlgorithmProvider();} + size_t MinKeyLength() const + {return 32;} + size_t MaxKeyLength() const + {return 32;} + size_t DefaultKeyLength() const + {return 32;} + size_t GetValidKeyLength(size_t n) const + {CRYPTOPP_UNUSED(n); return 32;} + bool IsValidKeyLength(size_t n) const + {return n==32;} + unsigned int OptimalDataAlignment() const + {return GetSymmetricCipher().OptimalDataAlignment();} + IV_Requirement IVRequirement() const + {return UNIQUE_IV;} + unsigned int IVSize() const + {return 24;} + unsigned int MinIVLength() const + {return 24;} + unsigned int MaxIVLength() const + {return 24;} + unsigned int DigestSize() const + {return 16;} + lword MaxHeaderLength() const + {return LWORD_MAX;} // 2^64-1 bytes + lword MaxMessageLength() const + {return W64LIT(274877906880);} // 2^38-1 blocks + lword MaxFooterLength() const + {return 0;} + + /// \brief Encrypts and calculates a MAC in one call + /// \param ciphertext the encryption buffer + /// \param mac the mac buffer + /// \param macSize the size of the MAC buffer, in bytes + /// \param iv the iv buffer + /// \param ivLength the size of the IV buffer, in bytes + /// \param aad the AAD buffer + /// \param aadLength the size of the AAD buffer, in bytes + /// \param message the message buffer + /// \param messageLength the size of the messagetext buffer, in bytes + /// \details EncryptAndAuthenticate() encrypts and generates the MAC in one call. The function + /// truncates the MAC if macSize < TagSize(). + virtual void 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); + + /// \brief Decrypts and verifies a MAC in one call + /// \param message the decryption buffer + /// \param mac the mac buffer + /// \param macSize the size of the MAC buffer, in bytes + /// \param iv the iv buffer + /// \param ivLength the size of the IV buffer, in bytes + /// \param aad the AAD buffer + /// \param aadLength the size of the AAD buffer, in bytes + /// \param ciphertext the cipher buffer + /// \param ciphertextLength the size of the ciphertext buffer, in bytes + /// \return true if the MAC is valid and the decoding succeeded, false otherwise + /// \details DecryptAndVerify() decrypts and verifies the MAC in one call. + /// message is a decryption buffer and should be at least as large as the ciphertext buffer. + /// \details The function returns true iff MAC is valid. DecryptAndVerify() assumes the MAC + /// is truncated if macLength < TagSize(). + virtual bool DecryptAndVerify(byte *message, const byte *mac, size_t macSize, const byte *iv, int ivLength, const byte *aad, size_t aadLength, const byte *ciphertext, size_t ciphertextLength); + +protected: + // AuthenticatedSymmetricCipherBase + bool AuthenticationIsOnPlaintext() const {return false;} + unsigned int AuthenticationBlockSize() const {return 1;} + void SetKeyWithoutResync(const byte *userKey, size_t keylength, const NameValuePairs ¶ms); + void Resync(const byte *iv, size_t len); + size_t AuthenticateBlocks(const byte *data, size_t len); + void AuthenticateLastHeaderBlock(); + void AuthenticateLastConfidentialBlock(); + void AuthenticateLastFooterBlock(byte *mac, size_t macSize); + +protected: + // See comments in chachapoly.cpp + void RekeyCipherAndMac(const byte *userKey, size_t userKeyLength, const NameValuePairs ¶ms); + + SecByteBlock m_userKey; +}; + +/// \brief XChaCha20Poly1305 cipher final implementation +/// \tparam T_IsEncryption flag indicating cipher direction +/// \details XChaCha20Poly1305 is an authenticated encryption scheme that combines +/// XChaCha20 and Poly1305-TLS. The scheme is defined in RFC 8439, section 2.8, +/// AEAD_CHACHA20_POLY1305 construction, and uses the IETF versions of ChaCha +/// and Poly1305. +/// \sa RFC 8439, ChaCha20 and Poly1305 +/// for IETF Protocols. +/// \since Crypto++ 8.1 +template +class XChaCha20Poly1305_Final : public XChaCha20Poly1305_Base +{ +public: + static std::string StaticAlgorithmName() + {return std::string("XChaCha20/Poly1305");} + +protected: + const SymmetricCipher & GetSymmetricCipher() + {return const_cast(this)->AccessSymmetricCipher();} + SymmetricCipher & AccessSymmetricCipher() + {return m_cipher;} + bool IsForwardTransformation() const + {return T_IsEncryption;} + + const MessageAuthenticationCode & GetMAC() const + {return const_cast(this)->AccessMAC();} + MessageAuthenticationCode & AccessMAC() + {return m_mac;} + +private: + XChaCha20::Encryption m_cipher; + Poly1305TLS m_mac; +}; + +/// \brief XChaCha20/Poly1305-TLS AEAD scheme +/// \details XChaCha20Poly1305 is an authenticated encryption scheme that combines +/// XChaCha20 and Poly1305-TLS. The scheme is defined in RFC 8439, section 2.8, +/// AEAD_XCHACHA20_POLY1305 construction, and uses the IETF versions of ChaCha +/// and Poly1305. +/// \sa RFC 8439, ChaCha20 and Poly1305 +/// for IETF Protocols. +/// \since Crypto++ 8.1 +struct XChaCha20Poly1305 : public AuthenticatedSymmetricCipherDocumentation +{ + /// \brief XChaCha20Poly1305 encryption + typedef XChaCha20Poly1305_Final Encryption; + /// \brief XChaCha20Poly1305 decryption + typedef XChaCha20Poly1305_Final Decryption; +}; + NAMESPACE_END #endif // CRYPTOPP_CHACHA_POLY1305_H diff --git a/regtest3.cpp b/regtest3.cpp index aace8d7f..f954c09a 100644 --- a/regtest3.cpp +++ b/regtest3.cpp @@ -72,6 +72,7 @@ void RegisterFactories4() RegisterAuthenticatedSymmetricCipherDefaultFactories >(); RegisterAuthenticatedSymmetricCipherDefaultFactories >(); RegisterAuthenticatedSymmetricCipherDefaultFactories(); + RegisterAuthenticatedSymmetricCipherDefaultFactories(); RegisterSymmetricCipherDefaultFactories >(); // For test vectors RegisterSymmetricCipherDefaultFactories >();