diff --git a/libi2pd/ChaCha20.cpp b/libi2pd/ChaCha20.cpp index 0ab98fb2..dc6eb18f 100644 --- a/libi2pd/ChaCha20.cpp +++ b/libi2pd/ChaCha20.cpp @@ -50,44 +50,26 @@ void quarterround(uint32_t *x, int a, int b, int c, int d) x[c] += x[d]; x[b] = rotl32(x[b] ^ x[c], 7); } - struct State_t - { - State_t() {}; - State_t(State_t &&) = delete; - - State_t & operator += (const State_t & other) - { - for(int i = 0; i < 16; i++) - data[i] += other.data[i]; - return *this; - } - void Copy(const State_t & other) - { - memcpy(data, other.data, sizeof(uint32_t) * 16); - } - uint32_t data[16]; - }; +struct Block_t +{ + Block_t() {}; + Block_t(Block_t &&) = delete; - struct Block_t - { - Block_t() {}; - Block_t(Block_t &&) = delete; + uint8_t data[blocksize]; - uint8_t data[blocksize]; + void operator << (const Chacha20State & st) + { + int i; + for (i = 0; i < 16; i++) + u32t8le(st.data[i], data + (i << 2)); + } +}; - void operator << (const State_t & st) - { - int i; - for (i = 0; i < 16; i++) - u32t8le(st.data[i], data + (i << 2)); - } - }; - -void block(const State_t &input, Block_t & block, int rounds) +void block(const Chacha20State &input, Block_t & block, int rounds) { int i; - State_t x; + Chacha20State x; x.Copy(input); for (i = rounds; i > 0; i -= 2) @@ -107,44 +89,41 @@ void block(const State_t &input, Block_t & block, int rounds) } } // namespace chacha + 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) + { + 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]; + } + } + } - - -void chacha20(uint8_t * buf, size_t sz, const uint8_t * nonce, const uint8_t * key, uint32_t counter) -{ - chacha::State_t state; - chacha::Block_t block; - size_t i, j; - - state.data[0] = 0x61707865; - state.data[1] = 0x3320646e; - state.data[2] = 0x79622d32; - state.data[3] = 0x6b206574; - - for (i = 0; i < 8; i++) - state.data[4 + i] = chacha::u8t32le(key + i * 4); - - - state.data[12] = counter; - - for (i = 0; i < 3; i++) - state.data[13 + i] = chacha::u8t32le(nonce + i * 4); - - - for (i = 0; i < sz; i += chacha::blocksize) - { - chacha::block(state, block, chacha::rounds); - state.data[12]++; - for (j = i; j < i + chacha::blocksize; j++) - { - if (j >= sz) break; - buf[j] ^= block.data[j - i]; - } - } - -} - + 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); + } } } #endif diff --git a/libi2pd/ChaCha20.h b/libi2pd/ChaCha20.h index 5a8ce145..da817847 100644 --- a/libi2pd/ChaCha20.h +++ b/libi2pd/ChaCha20.h @@ -9,6 +9,8 @@ #define LIBI2PD_CHACHA20_H #include #include +#include +#include #include "Crypto.h" #if LEGACY_OPENSSL @@ -19,10 +21,32 @@ namespace crypto const std::size_t CHACHA20_KEY_BYTES = 32; const std::size_t CHACHA20_NOUNCE_BYTES = 12; + struct Chacha20State + { + Chacha20State () {}; + Chacha20State (Chacha20State &&) = delete; + + Chacha20State & operator += (const Chacha20State & other) + { + for(int i = 0; i < 16; i++) + data[i] += other.data[i]; + return *this; + } + + void Copy(const Chacha20State & other) + { + memcpy(data, other.data, sizeof(uint32_t) * 16); + } + uint32_t data[16]; + }; + + 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); + /** 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); -} +} } #endif diff --git a/libi2pd/Crypto.cpp b/libi2pd/Crypto.cpp index dcd89064..85145804 100644 --- a/libi2pd/Crypto.cpp +++ b/libi2pd/Crypto.cpp @@ -1172,6 +1172,55 @@ namespace crypto return ret; } + void AEADChaCha20Poly1305Encrypt (std::vector >& bufs, const uint8_t * key, const uint8_t * nonce, uint8_t * mac) + { + if (bufs.empty ()) return; +#if LEGACY_OPENSSL + // generate one time poly key + uint64_t polyKey[8]; + memset(polyKey, 0, sizeof(polyKey)); + chacha20 ((uint8_t *)polyKey, 64, nonce, key, 0); + Poly1305 polyHash (polyKey); + // encrypt buffers + Chacha20State state; + Chacha20Init (state, nonce, key, 1); + size_t size = 0; + for (auto& it: bufs) + { + Chacha20Encrypt (state, (uint8_t *)it.first, it.second); + polyHash.Update ((uint8_t *)it.first, it.second); // after encryption + size += it.second; + } + // padding + uint8_t padding[16]; + memset (padding, 0, 16); + auto rem = size & 0x0F; // %16 + if (rem) + { + // padding2 + rem = 16 - rem; + polyHash.Update (padding, rem); + } + // adLen and msgLen + // adLen is always zero + htole64buf (padding + 8, size); + polyHash.Update (padding, 16); + // MAC + polyHash.Finish ((uint64_t *)mac); +#else + int outlen = 0; + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new (); + EVP_EncryptInit_ex(ctx, EVP_chacha20_poly1305(), 0, 0, 0); + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, 12, 0); + EVP_EncryptInit_ex(ctx, NULL, NULL, key, nonce); + for (auto& it: bufs) + EVP_EncryptUpdate(ctx, (uint8_t *)it.first, &outlen, (uint8_t *)it.first, it.second); + EVP_EncryptFinal_ex(ctx, NULL, &outlen); + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, 16, mac); + EVP_CIPHER_CTX_free (ctx); +#endif + } + // init and terminate /* std::vector > m_OpenSSLMutexes; diff --git a/libi2pd/Crypto.h b/libi2pd/Crypto.h index 475f7fa3..79d16e50 100644 --- a/libi2pd/Crypto.h +++ b/libi2pd/Crypto.h @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -282,6 +283,8 @@ namespace crypto // AEAD/ChaCha20/Poly1305 bool AEADChaCha20Poly1305 (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen, const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len, bool encrypt); // msgLen is len without tag + void AEADChaCha20Poly1305Encrypt (std::vector >& bufs, const uint8_t * key, const uint8_t * nonce, uint8_t * mac); // encrypt multiple buffers with zero ad + // init and terminate void InitCrypto (bool precomputation); void TerminateCrypto (); diff --git a/tests/test-aeadchacha20poly1305.cpp b/tests/test-aeadchacha20poly1305.cpp index dcd4b4d6..38717a62 100644 --- a/tests/test-aeadchacha20poly1305.cpp +++ b/tests/test-aeadchacha20poly1305.cpp @@ -51,4 +51,10 @@ int main () uint8_t buf1[114]; assert (i2p::crypto::AEADChaCha20Poly1305 (buf, 114, ad, 12, key, nonce, buf1, 114, false)); assert (memcmp (buf1, text, 114) == 0); + // test encryption of multiple buffers + memcpy (buf, text, 114); + std::vector > bufs{ std::make_pair (buf, 114) }; + 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); }