From 26c83877ef30c81054746721f23f8df91ef44337 Mon Sep 17 00:00:00 2001 From: Jeffrey Walton Date: Wed, 6 Feb 2019 01:03:28 -0500 Subject: [PATCH] Add IETF XChaCha (GH #727, PR #794) --- Filelist.txt | 1 + TestVectors/all.txt | 1 + TestVectors/xchacha.txt | 75 +++++++++++++++++++++ bench2.cpp | 2 +- chacha.cpp | 141 ++++++++++++++++++++++++++++++++++++---- chacha.h | 85 +++++++++++++++++++++--- regtest2.cpp | 2 + 7 files changed, 286 insertions(+), 21 deletions(-) create mode 100644 TestVectors/xchacha.txt diff --git a/Filelist.txt b/Filelist.txt index f6e62292..366cecd4 100644 --- a/Filelist.txt +++ b/Filelist.txt @@ -549,6 +549,7 @@ TestVectors/ttmac.txt TestVectors/vmac.txt TestVectors/wake.txt TestVectors/whrlpool.txt +TestVectors/xchacha.txt TestPrograms/test_32bit.cxx TestPrograms/test_64bit.cxx TestPrograms/test_arm_acle.cxx diff --git a/TestVectors/all.txt b/TestVectors/all.txt index 224ff898..dbfcb6cf 100644 --- a/TestVectors/all.txt +++ b/TestVectors/all.txt @@ -9,6 +9,7 @@ Test: TestVectors/ccm.txt Test: TestVectors/chacha_tls.txt Test: TestVectors/chacha20poly1305.txt Test: TestVectors/chacha.txt +Test: TestVectors/xchacha.txt Test: TestVectors/cham.txt Test: TestVectors/cmac.txt Test: TestVectors/dlies.txt diff --git a/TestVectors/xchacha.txt b/TestVectors/xchacha.txt new file mode 100644 index 00000000..bcd216b6 --- /dev/null +++ b/TestVectors/xchacha.txt @@ -0,0 +1,75 @@ +AlgorithmType: SymmetricCipher +Name: XChaCha20 +Source: https://tools.ietf.org/html/draft-arciszewski-xchacha +# +Comment: A.2. Example and Test Vector for XChaCha20 +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 58 +Plaintext: r304 00 +Ciphertext: 29 62 4b 4b 1b 14 0a ce 53 74 0e 40 5b 21 68 54 \ + 0f d7 d6 30 c1 f5 36 fe cd 72 2f c3 cd db a7 f4 \ + cc a9 8c f9 e4 7e 5e 64 d1 15 45 0f 9b 12 5b 54 \ + 44 9f f7 61 41 ca 62 0a 1f 9c fc ab 2a 1a 8a 25 \ + 5e 76 6a 52 66 b8 78 84 61 20 ea 64 ad 99 aa 47 \ + 94 71 e6 3b ef cb d3 7c d1 c2 2a 22 1f e4 62 21 \ + 5c f3 2c 74 89 5b f5 05 86 3c cd dd 48 f6 29 16 \ + dc 65 21 f1 ec 50 a5 ae 08 90 3a a2 59 d9 bf 60 \ + 7c d8 02 6f ba 54 86 04 f1 b6 07 2d 91 bc 91 24 \ + 3a 5b 84 5f 7f d1 71 b0 2e dc 5a 0a 84 cf 28 dd \ + 24 11 46 bc 37 6e 3f 48 df 5e 7f ee 1d 11 04 8c \ + 19 0a 3d 3d eb 0f eb 64 b4 2d 9c 6f de ee 29 0f \ + a0 e6 ae 2c 26 c0 24 9e a8 c1 81 f7 e2 ff d1 00 \ + cb e5 fd 3c 4f 82 71 d6 2b 15 33 0c b8 fd cf 00 \ + b3 df 50 7c a8 c9 24 f7 01 7b 7e 71 2d 15 a2 eb \ + 5c 50 48 44 51 e5 4e 1b 4b 99 5b d8 fd d9 45 97 \ + bb 94 d7 af 0b 2c 04 df 10 ba 08 90 89 9e d9 29 \ + 3a 0f 55 b8 ba fa 99 92 64 03 5f 1d 4f be 7f e0 \ + aa fa 10 9a 62 37 20 27 e5 0e 10 cd fe cc a1 27 +Test: Encrypt +# +Comment: A.2. Example and Test Vector for XChaCha20 +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 58 +Plaintext: 54 68 65 20 64 68 6f 6c 65 20 28 70 72 6f 6e 6f \ + 75 6e 63 65 64 20 22 64 6f 6c 65 22 29 20 69 73 \ + 20 61 6c 73 6f 20 6b 6e 6f 77 6e 20 61 73 20 74 \ + 68 65 20 41 73 69 61 74 69 63 20 77 69 6c 64 20 \ + 64 6f 67 2c 20 72 65 64 20 64 6f 67 2c 20 61 6e \ + 64 20 77 68 69 73 74 6c 69 6e 67 20 64 6f 67 2e \ + 20 49 74 20 69 73 20 61 62 6f 75 74 20 74 68 65 \ + 20 73 69 7a 65 20 6f 66 20 61 20 47 65 72 6d 61 \ + 6e 20 73 68 65 70 68 65 72 64 20 62 75 74 20 6c \ + 6f 6f 6b 73 20 6d 6f 72 65 20 6c 69 6b 65 20 61 \ + 20 6c 6f 6e 67 2d 6c 65 67 67 65 64 20 66 6f 78 \ + 2e 20 54 68 69 73 20 68 69 67 68 6c 79 20 65 6c \ + 75 73 69 76 65 20 61 6e 64 20 73 6b 69 6c 6c 65 \ + 64 20 6a 75 6d 70 65 72 20 69 73 20 63 6c 61 73 \ + 73 69 66 69 65 64 20 77 69 74 68 20 77 6f 6c 76 \ + 65 73 2c 20 63 6f 79 6f 74 65 73 2c 20 6a 61 63 \ + 6b 61 6c 73 2c 20 61 6e 64 20 66 6f 78 65 73 20 \ + 69 6e 20 74 68 65 20 74 61 78 6f 6e 6f 6d 69 63 \ + 20 66 61 6d 69 6c 79 20 43 61 6e 69 64 61 65 2e +Ciphertext: 7d 0a 2e 6b 7f 7c 65 a2 36 54 26 30 29 4e 06 3b \ + 7a b9 b5 55 a5 d5 14 9a a2 1e 4a e1 e4 fb ce 87 \ + ec c8 e0 8a 8b 5e 35 0a be 62 2b 2f fa 61 7b 20 \ + 2c fa d7 20 32 a3 03 7e 76 ff dc dc 43 76 ee 05 \ + 3a 19 0d 7e 46 ca 1d e0 41 44 85 03 81 b9 cb 29 \ + f0 51 91 53 86 b8 a7 10 b8 ac 4d 02 7b 8b 05 0f \ + 7c ba 58 54 e0 28 d5 64 e4 53 b8 a9 68 82 41 73 \ + fc 16 48 8b 89 70 ca c8 28 f1 1a e5 3c ab d2 01 \ + 12 f8 71 07 df 24 ee 61 83 d2 27 4f e4 c8 b1 48 \ + 55 34 ef 2c 5f bc 1e c2 4b fc 36 63 ef aa 08 bc \ + 04 7d 29 d2 50 43 53 2d b8 39 1a 8a 3d 77 6b f4 \ + 37 2a 69 55 82 7c cb 0c dd 4a f4 03 a7 ce 4c 63 \ + d5 95 c7 5a 43 e0 45 f0 cc e1 f2 9c 8b 93 bd 65 \ + af c5 97 49 22 f2 14 a4 0b 7c 40 2c db 91 ae 73 \ + c0 b6 36 15 cd ad 04 80 68 0f 16 51 5a 7a ce 9d \ + 39 23 64 64 32 8a 37 74 3f fc 28 f4 dd b3 24 f4 \ + d0 f5 bb dc 27 0c 65 b1 74 9a 6e ff f1 fb aa 09 \ + 53 61 75 cc d2 9f b9 e6 05 7b 30 73 20 d3 16 83 \ + 8a 9c 71 f7 0b 5b 59 07 a6 6f 7e a4 9a ad c4 09 +Test: Encrypt diff --git a/bench2.cpp b/bench2.cpp index 89a04e42..0be2195a 100644 --- a/bench2.cpp +++ b/bench2.cpp @@ -142,7 +142,7 @@ void Benchmark2(double t, double hertz) BenchMarkByName("ChaCha", 0, "ChaCha20"); BenchMarkByName("ChaCha", 0, "ChaCha12", MakeParameters(Name::Rounds(), 12)); BenchMarkByName("ChaCha", 0, "ChaCha8", MakeParameters(Name::Rounds(), 8)); - BenchMarkByName("ChaChaTLS", 0, "ChaChaTLS"); + BenchMarkByName("ChaChaTLS"); BenchMarkByName("Sosemanuk"); BenchMarkByName("Rabbit"); BenchMarkByName("RabbitWithIV"); diff --git a/chacha.cpp b/chacha.cpp index fcea81ec..437aeff7 100644 --- a/chacha.cpp +++ b/chacha.cpp @@ -39,6 +39,7 @@ void ChaCha_TestInstantiations() { ChaCha::Encryption x; ChaChaTLS::Encryption y; + XChaCha20::Encryption z; } #endif @@ -218,9 +219,35 @@ void ChaCha_OperateKeystream(KeystreamOperation operation, // We may re-enter a SIMD keystream operation from here. } while (iterationCount--); +} - #undef CHACHA_QUARTER_ROUND - #undef CHACHA_OUTPUT +// XChaCha key derivation +void HChaCha_OperateKeystream(const word32 state[16], word32 output[8]) +{ + word32 x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; + + x0 = state[0]; x1 = state[1]; x2 = state[2]; x3 = state[3]; + x4 = state[4]; x5 = state[5]; x6 = state[6]; x7 = state[7]; + x8 = state[8]; x9 = state[9]; x10 = state[10]; x11 = state[11]; + x12 = state[12]; x13 = state[13]; x14 = state[14]; x15 = state[15]; + + for (int i = 20; i > 0; i -= 2) + { + CHACHA_QUARTER_ROUND(x0, x4, x8, x12); + CHACHA_QUARTER_ROUND(x1, x5, x9, x13); + CHACHA_QUARTER_ROUND(x2, x6, x10, x14); + CHACHA_QUARTER_ROUND(x3, x7, x11, x15); + + CHACHA_QUARTER_ROUND(x0, x5, x10, x15); + CHACHA_QUARTER_ROUND(x1, x6, x11, x12); + CHACHA_QUARTER_ROUND(x2, x7, x8, x13); + CHACHA_QUARTER_ROUND(x3, x4, x9, x14); + } + + output[0] = x0; output[1] = x1; + output[2] = x2; output[3] = x3; + output[4] = x12; output[5] = x13; + output[6] = x14; output[7] = x15; } std::string ChaCha_AlgorithmProvider() @@ -398,19 +425,14 @@ void ChaChaTLS_Policy::CipherSetKey(const NameValuePairs ¶ms, const byte *ke // the function, so we have to use the heavier-weight SetKey to change it. word64 block; if (params.GetValue("InitialBlock", block)) - m_state[16] = static_cast(block); + m_state[CTR] = static_cast(block); else - m_state[16] = 0; - - // State words are defined in RFC 8439, Section 2.3. - m_state[0] = 0x61707865; - m_state[1] = 0x3320646e; - m_state[2] = 0x79622d32; - m_state[3] = 0x6b206574; + m_state[CTR] = 0; // State words are defined in RFC 8439, Section 2.3. Key is 32-bytes. GetBlock get(key); - get(m_state[4])(m_state[5])(m_state[6])(m_state[7])(m_state[8])(m_state[9])(m_state[10])(m_state[11]); + get(m_state[KEY+0])(m_state[KEY+1])(m_state[KEY+2])(m_state[KEY+3]) + (m_state[KEY+4])(m_state[KEY+5])(m_state[KEY+6])(m_state[KEY+7]); } void ChaChaTLS_Policy::CipherResynchronize(byte *keystreamBuffer, const byte *IV, size_t length) @@ -418,9 +440,16 @@ void ChaChaTLS_Policy::CipherResynchronize(byte *keystreamBuffer, const byte *IV CRYPTOPP_UNUSED(keystreamBuffer), CRYPTOPP_UNUSED(length); CRYPTOPP_ASSERT(length==12); + // State words are defined in RFC 8439, Section 2.3. + m_state[0] = 0x61707865; m_state[1] = 0x3320646e; + m_state[2] = 0x79622d32; m_state[3] = 0x6b206574; + + // Copy saved key into state + std::memcpy(m_state+4, m_state+KEY, 8*sizeof(word32)); + // State words are defined in RFC 8439, Section 2.3 GetBlock get(IV); - m_state[12] = m_state[16]; + m_state[12] = m_state[CTR]; get(m_state[13])(m_state[14])(m_state[15]); } @@ -461,4 +490,92 @@ void ChaChaTLS_Policy::OperateKeystream(KeystreamOperation operation, CRYPTOPP_ASSERT(discard==0); } +////////////////////////////// IETF XChaCha20 ////////////////////////////// + +std::string XChaCha20_Policy::AlgorithmName() const +{ + return std::string("XChaCha20"); +} + +std::string XChaCha20_Policy::AlgorithmProvider() const +{ + return ChaCha_AlgorithmProvider(); +} + +void XChaCha20_Policy::CipherSetKey(const NameValuePairs ¶ms, const byte *key, size_t length) +{ + CRYPTOPP_ASSERT(key); CRYPTOPP_ASSERT(length == 32); + + // XChaCha20 is always 20 rounds. Fetch Rounds() to avoid a spurious failure. + int rounds = params.GetIntValueWithDefault(Name::Rounds(), ROUNDS); + if (rounds != 20) + throw InvalidRounds(XChaCha20::StaticAlgorithmName(), rounds); + + word64 block; + if (params.GetValue("InitialBlock", block)) + m_state[CTR] = static_cast(block); + else + m_state[CTR] = 1; + + // Stash key away for use in CipherResynchronize + GetBlock get(key); + get(m_state[KEY+0])(m_state[KEY+1])(m_state[KEY+2])(m_state[KEY+3]) + (m_state[KEY+4])(m_state[KEY+5])(m_state[KEY+6])(m_state[KEY+7]); +} + +void XChaCha20_Policy::CipherResynchronize(byte *keystreamBuffer, const byte *iv, size_t length) +{ + CRYPTOPP_UNUSED(keystreamBuffer), CRYPTOPP_UNUSED(length); + CRYPTOPP_ASSERT(length==24); + + // HChaCha derivation + m_state[0] = 0x61707865; m_state[1] = 0x3320646e; + m_state[2] = 0x79622d32; m_state[3] = 0x6b206574; + + // Copy saved key into state + std::memcpy(m_state+4, m_state+KEY, 8*sizeof(word32)); + + GetBlock get(iv); + get(m_state[12])(m_state[13])(m_state[14])(m_state[15]); + + // Operate the keystream without adding state back in. + // This function also gathers the key words into a + // contiguous 8-word block. + HChaCha_OperateKeystream(m_state, m_state+4); + + // XChaCha state + m_state[0] = 0x61707865; m_state[1] = 0x3320646e; + m_state[2] = 0x79622d32; m_state[3] = 0x6b206574; + + // Setup new IV + m_state[12] = m_state[CTR]; + m_state[13] = 0; + m_state[14] = GetWord(false, LITTLE_ENDIAN_ORDER, iv+16); + m_state[15] = GetWord(false, LITTLE_ENDIAN_ORDER, iv+20); +} + +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); +} + +unsigned int XChaCha20_Policy::GetAlignment() const +{ + return ChaCha_GetAlignment(); +} + +unsigned int XChaCha20_Policy::GetOptimalBlockSize() const +{ + return ChaCha_GetOptimalBlockSize(); +} + +void XChaCha20_Policy::OperateKeystream(KeystreamOperation operation, + byte *output, const byte *input, size_t iterationCount) +{ + ChaCha_OperateKeystream(operation, m_state, m_state[12], m_state[13], + ROUNDS, output, input, iterationCount); +} + NAMESPACE_END diff --git a/chacha.h b/chacha.h index 13ae74c5..76aeb01b 100644 --- a/chacha.h +++ b/chacha.h @@ -5,10 +5,10 @@ // The library added Bernstein's ChaCha classses at Crypto++ 5.6.4. The IETF // uses a slightly different implementation than Bernstein, and the IETF -// classes were added at Crypto++ 8.1. We wanted to maintain ABI compatibility -// at the 8.1 release so the original ChaCha classes were not disturbed. -// Instead new classes were added for IETF ChaCha. The back-end implementation -// shares code as expected, however. +// ChaCha and XChaCha classes were added at Crypto++ 8.1. We wanted to maintain +// ABI compatibility at the 8.1 release so the original ChaCha classes were not +// disturbed. Instead new classes were added for IETF ChaCha. The back-end +// implementation shares code as expected, however. /// \file chacha.h /// \brief Classes for ChaCha8, ChaCha12 and ChaCha20 stream ciphers @@ -20,8 +20,11 @@ /// implementation for cipher suites /// TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, /// TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, -/// and TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256. -/// \since ChaCha since Crypto++ 5.6.4, ChaChaTLS since Crypto++ 8.1 +/// and TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256. Finally, +/// the library provides XChaCha: +/// eXtended-nonce ChaCha and AEAD_XChaCha20_Poly1305 (rev. 03). +/// \since ChaCha since Crypto++ 5.6.4, ChaChaTLS and XChaCha20 since Crypto++ 8.1 #ifndef CRYPTOPP_CHACHA_H #define CRYPTOPP_CHACHA_H @@ -81,7 +84,9 @@ protected: /// \since Crypto++ 5.6.4 struct ChaCha : public ChaCha_Info, public SymmetricCipherDocumentation { + /// \brief ChaCha Encryption typedef SymmetricCipherFinal >, ChaCha_Info > Encryption; + /// \brief ChaCha Decryption typedef Encryption Decryption; }; @@ -123,8 +128,10 @@ protected: std::string AlgorithmName() const; std::string AlgorithmProvider() const; - FixedSizeAlignedSecBlock m_state; + FixedSizeAlignedSecBlock m_state; CRYPTOPP_CONSTANT(ROUNDS = ChaChaTLS_Info::ROUNDS) + CRYPTOPP_CONSTANT(KEY = 16) // Index into m_state + CRYPTOPP_CONSTANT(CTR = 24) // Index into m_state }; /// \brief ChaCha-TLS stream cipher @@ -136,14 +143,76 @@ protected: /// TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, and /// TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256. /// \sa RFC 8439, ChaCha20 and -/// Poly1305 for IETF Protocols, How +/// Poly1305 for IETF Protocols, How /// to handle block counter wrap in IETF's ChaCha algorithm? and /// Issue /// 790, ChaChaTLS results when counter block wraps. /// \since Crypto++ 8.1 struct ChaChaTLS : public ChaChaTLS_Info, public SymmetricCipherDocumentation { + /// \brief ChaCha-TLS Encryption typedef SymmetricCipherFinal >, ChaChaTLS_Info > Encryption; + /// \brief ChaCha-TLS Decryption + typedef Encryption Decryption; +}; + +////////////////////////////// IETF XChaCha20 draft ////////////////////////////// + +/// \brief XChaCha stream cipher information +/// \since Crypto++ 8.1 +struct XChaCha20_Info : public FixedKeyLength<32, SimpleKeyingInterface::UNIQUE_IV, 24>, FixedRounds<20> +{ + /// \brief The algorithm name + /// \returns the algorithm name + /// \details StaticAlgorithmName returns the algorithm's name as a static + /// member function. + /// \details This is the IETF's XChaCha from draft-arciszewski-xchacha. + static const char* StaticAlgorithmName() { + return "XChaCha20"; + } +}; + +/// \brief XChaCha stream cipher implementation +/// \since Crypto++ 8.1 +class CRYPTOPP_NO_VTABLE XChaCha20_Policy : public AdditiveCipherConcretePolicy +{ +public: + virtual ~XChaCha20_Policy() {} + XChaCha20_Policy() {} + +protected: + void CipherSetKey(const NameValuePairs ¶ms, const byte *key, size_t length); + void OperateKeystream(KeystreamOperation operation, byte *output, const byte *input, size_t iterationCount); + void CipherResynchronize(byte *keystreamBuffer, const byte *IV, size_t length); + bool CipherIsRandomAccess() const {return false;} + void SeekToIteration(lword iterationCount); + unsigned int GetAlignment() const; + unsigned int GetOptimalBlockSize() const; + + std::string AlgorithmName() const; + std::string AlgorithmProvider() const; + + FixedSizeAlignedSecBlock m_state; + CRYPTOPP_CONSTANT(ROUNDS = XChaCha20_Info::ROUNDS) + CRYPTOPP_CONSTANT(KEY = 16) // Index into m_state + CRYPTOPP_CONSTANT(CTR = 24) // Index into m_state +}; + +/// \brief XChaCha stream cipher +/// \details This is the IETF's XChaCha from draft-arciszewski-xchacha. +/// \sa XChaCha: +/// eXtended-nonce ChaCha and AEAD_XChaCha20_Poly1305 (rev. 03), How +/// to handle block counter wrap in IETF's ChaCha algorithm? and +/// Issue +/// 790, ChaCha20 results when counter block wraps. +/// \since Crypto++ 8.1 +struct XChaCha20 : public XChaCha20_Info, public SymmetricCipherDocumentation +{ + /// \brief XChaCha Encryption + typedef SymmetricCipherFinal >, XChaCha20_Info > Encryption; + /// \brief XChaCha Decryption typedef Encryption Decryption; }; diff --git a/regtest2.cpp b/regtest2.cpp index 84a3f1d0..e14036fa 100644 --- a/regtest2.cpp +++ b/regtest2.cpp @@ -23,6 +23,7 @@ #include "sha3.h" #include "blake2.h" #include "ripemd.h" +#include "chacha.h" #include "poly1305.h" #include "siphash.h" #include "panama.h" @@ -95,6 +96,7 @@ void RegisterFactories3() RegisterSymmetricCipherDefaultFactories(); RegisterSymmetricCipherDefaultFactories(); RegisterSymmetricCipherDefaultFactories(); + RegisterSymmetricCipherDefaultFactories(); RegisterSymmetricCipherDefaultFactories(); RegisterSymmetricCipherDefaultFactories(); RegisterSymmetricCipherDefaultFactories();