From ef6db64e9fa342c8627458ce020ed935c96922de Mon Sep 17 00:00:00 2001 From: orignal Date: Fri, 30 Nov 2018 16:21:11 -0500 Subject: [PATCH] correct chacha20 for multiple messages --- libi2pd/ChaCha20.cpp | 96 +++++++++++++++-------------- libi2pd/ChaCha20.h | 29 +++++++-- libi2pd/Crypto.cpp | 6 +- tests/test-aeadchacha20poly1305.cpp | 2 +- 4 files changed, 78 insertions(+), 55 deletions(-) diff --git a/libi2pd/ChaCha20.cpp b/libi2pd/ChaCha20.cpp index dc6eb18f..0ee7cf75 100644 --- a/libi2pd/ChaCha20.cpp +++ b/libi2pd/ChaCha20.cpp @@ -15,9 +15,6 @@ namespace crypto { namespace chacha { -constexpr int rounds = 20; -constexpr std::size_t blocksize = 64; - void u32t8le(uint32_t v, uint8_t * p) { p[0] = v & 0xff; @@ -51,22 +48,14 @@ void quarterround(uint32_t *x, int a, int b, int c, int d) } -struct Block_t +void Chacha20Block::operator << (const Chacha20State & st) { - Block_t() {}; - Block_t(Block_t &&) = delete; - - uint8_t data[blocksize]; - - void operator << (const Chacha20State & st) - { - int i; - for (i = 0; i < 16; i++) - u32t8le(st.data[i], data + (i << 2)); - } -}; + int i; + for (i = 0; i < 16; i++) + u32t8le(st.data[i], data + (i << 2)); +} -void block(const Chacha20State &input, Block_t & block, int rounds) +void block (Chacha20State &input, int rounds) { int i; Chacha20State x; @@ -84,45 +73,60 @@ void block(const Chacha20State &input, Block_t & block, int rounds) quarterround(x.data, 3, 4, 9, 14); } x += input; - block << x; + input.block << x; } -} // namespace chacha - void Chacha20Init (Chacha20State& state, const uint8_t * nonce, const uint8_t * key, uint32_t counter) +void Chacha20Init (Chacha20State& state, const uint8_t * nonce, const uint8_t * key, uint32_t counter) +{ + state.data[0] = 0x61707865; + state.data[1] = 0x3320646e; + state.data[2] = 0x79622d32; + state.data[3] = 0x6b206574; + for (size_t i = 0; i < 8; i++) + state.data[4 + i] = chacha::u8t32le(key + i * 4); + + state.data[12] = counter; + for (size_t i = 0; i < 3; i++) + state.data[13 + i] = chacha::u8t32le(nonce + i * 4); +} + +void Chacha20Encrypt (Chacha20State& state, uint8_t * buf, size_t sz) +{ + if (state.offset > 0) { - state.data[0] = 0x61707865; - state.data[1] = 0x3320646e; - state.data[2] = 0x79622d32; - state.data[3] = 0x6b206574; - for (size_t i = 0; i < 8; i++) - state.data[4 + i] = chacha::u8t32le(key + i * 4); - - state.data[12] = counter; - for (size_t i = 0; i < 3; i++) - state.data[13 + i] = chacha::u8t32le(nonce + i * 4); + // previous block if any + auto s = chacha::blocksize - state.offset; + if (sz < s) s = sz; + for (size_t i = 0; i < s; i++) + buf[i] ^= state.block.data[state.offset + i]; + buf += s; + sz -= s; + state.offset = 0; } - - void Chacha20Encrypt (Chacha20State& state, uint8_t * buf, size_t sz) + for (size_t i = 0; i < sz; i += chacha::blocksize) { - chacha::Block_t block; - for (size_t i = 0; i < sz; i += chacha::blocksize) - { - chacha::block(state, block, chacha::rounds); - state.data[12]++; - for (size_t j = i; j < i + chacha::blocksize; j++) - { - if (j >= sz) break; - buf[j] ^= block.data[j - i]; - } - } + chacha::block(state, chacha::rounds); + state.data[12]++; + for (size_t j = i; j < i + chacha::blocksize; j++) + { + if (j >= sz) + { + state.offset = j & 0x3F; // % 64 + break; + } + buf[j] ^= state.block.data[j - i]; + } } +} +} // namespace chacha + void chacha20(uint8_t * buf, size_t sz, const uint8_t * nonce, const uint8_t * key, uint32_t counter) { - Chacha20State state; - Chacha20Init (state, nonce, key, counter); - Chacha20Encrypt (state, buf, sz); + chacha::Chacha20State state; + chacha::Chacha20Init (state, nonce, key, counter); + chacha::Chacha20Encrypt (state, buf, sz); } } } diff --git a/libi2pd/ChaCha20.h b/libi2pd/ChaCha20.h index da817847..0a2f3be0 100644 --- a/libi2pd/ChaCha20.h +++ b/libi2pd/ChaCha20.h @@ -18,12 +18,28 @@ namespace i2p { namespace crypto { - const std::size_t CHACHA20_KEY_BYTES = 32; - const std::size_t CHACHA20_NOUNCE_BYTES = 12; + const std::size_t CHACHA20_KEY_BYTES = 32; + const std::size_t CHACHA20_NOUNCE_BYTES = 12; + +namespace chacha +{ + constexpr std::size_t blocksize = 64; + constexpr int rounds = 20; + + struct Chacha20State; + struct Chacha20Block + { + Chacha20Block () {}; + Chacha20Block (Chacha20Block &&) = delete; + + uint8_t data[blocksize]; + + void operator << (const Chacha20State & st); + }; struct Chacha20State { - Chacha20State () {}; + Chacha20State (): offset (0) {}; Chacha20State (Chacha20State &&) = delete; Chacha20State & operator += (const Chacha20State & other) @@ -38,10 +54,13 @@ namespace crypto memcpy(data, other.data, sizeof(uint32_t) * 16); } uint32_t data[16]; - }; + Chacha20Block block; + size_t offset; + }; void Chacha20Init (Chacha20State& state, const uint8_t * nonce, const uint8_t * key, uint32_t counter); - void Chacha20Encrypt (Chacha20State& state, uint8_t * buf, size_t sz); + void Chacha20Encrypt (Chacha20State& state, uint8_t * buf, size_t sz); +} /** encrypt buf in place with chacha20 */ void chacha20(uint8_t * buf, size_t sz, const uint8_t * nonce, const uint8_t * key, uint32_t counter=1); diff --git a/libi2pd/Crypto.cpp b/libi2pd/Crypto.cpp index 85145804..659fddcc 100644 --- a/libi2pd/Crypto.cpp +++ b/libi2pd/Crypto.cpp @@ -1182,12 +1182,12 @@ namespace crypto chacha20 ((uint8_t *)polyKey, 64, nonce, key, 0); Poly1305 polyHash (polyKey); // encrypt buffers - Chacha20State state; - Chacha20Init (state, nonce, key, 1); + chacha::Chacha20State state; + chacha::Chacha20Init (state, nonce, key, 1); size_t size = 0; for (auto& it: bufs) { - Chacha20Encrypt (state, (uint8_t *)it.first, it.second); + chacha::Chacha20Encrypt (state, (uint8_t *)it.first, it.second); polyHash.Update ((uint8_t *)it.first, it.second); // after encryption size += it.second; } diff --git a/tests/test-aeadchacha20poly1305.cpp b/tests/test-aeadchacha20poly1305.cpp index 38717a62..d10ab2fc 100644 --- a/tests/test-aeadchacha20poly1305.cpp +++ b/tests/test-aeadchacha20poly1305.cpp @@ -53,7 +53,7 @@ int main () assert (memcmp (buf1, text, 114) == 0); // test encryption of multiple buffers memcpy (buf, text, 114); - std::vector > bufs{ std::make_pair (buf, 114) }; + std::vector > bufs{ std::make_pair (buf, 50), std::make_pair (buf + 50, 50), std::make_pair (buf + 100, 14) }; i2p::crypto::AEADChaCha20Poly1305Encrypt (bufs, key, nonce, buf + 114); i2p::crypto::AEADChaCha20Poly1305 (buf, 114, nullptr, 0, key, nonce, buf1, 114, false); assert (memcmp (buf1, text, 114) == 0);