forked from bitcoin/bitcoin
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge bitcoin#15649: Add ChaCha20Poly1305@Bitcoin AEAD
bb326ad Add ChaCha20Poly1305@Bitcoin AEAD benchmark (Jonas Schnelli) 99aea04 Add ChaCha20Poly1305@Bitcoin tests (Jonas Schnelli) af5d1b5 Add ChaCha20Poly1305@Bitcoin AEAD implementation (Jonas Schnelli) Pull request description: This adds a new AEAD (authenticated encryption with additional data) construct optimised for small messages (like used in Bitcoins p2p network). Includes: bitcoin#15519, bitcoin#15512 (please review those first). The construct is specified here. https://gist.github.com/jonasschnelli/c530ea8421b8d0e80c51486325587c52#ChaCha20Poly1305Bitcoin_Cipher_Suite This aims for being used in v2 peer-to-peer messages. ACKs for top commit: laanwj: code review ACK bb326ad Tree-SHA512: 15bcb86c510fce7abb7a73536ff2ae89893b24646bf108c6cf18f064d672dbbbea8b1dd0868849fdac0c6854e498f1345d01dab56d1c92031afd728302234686
- Loading branch information
Showing
6 changed files
with
525 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
// Copyright (c) 2019 The Bitcoin Core developers | ||
// Distributed under the MIT software license, see the accompanying | ||
// file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||
|
||
#include <iostream> | ||
|
||
#include <bench/bench.h> | ||
#include <crypto/chacha_poly_aead.h> | ||
#include <crypto/poly1305.h> // for the POLY1305_TAGLEN constant | ||
#include <hash.h> | ||
|
||
#include <limits> | ||
#include <assert.h> | ||
|
||
/* Number of bytes to process per iteration */ | ||
static constexpr uint64_t BUFFER_SIZE_TINY = 64; | ||
static constexpr uint64_t BUFFER_SIZE_SMALL = 256; | ||
static constexpr uint64_t BUFFER_SIZE_LARGE = 1024 * 1024; | ||
|
||
static const unsigned char k1[32] = {0}; | ||
static const unsigned char k2[32] = {0}; | ||
|
||
static ChaCha20Poly1305AEAD aead(k1, 32, k2, 32); | ||
|
||
static void CHACHA20_POLY1305_AEAD(benchmark::State& state, size_t buffersize, bool include_decryption) | ||
{ | ||
std::vector<unsigned char> in(buffersize + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0); | ||
std::vector<unsigned char> out(buffersize + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0); | ||
uint64_t seqnr_payload = 0; | ||
uint64_t seqnr_aad = 0; | ||
int aad_pos = 0; | ||
uint32_t len = 0; | ||
while (state.KeepRunning()) { | ||
// encrypt or decrypt the buffer with a static key | ||
assert(aead.Crypt(seqnr_payload, seqnr_aad, aad_pos, out.data(), out.size(), in.data(), buffersize, true)); | ||
|
||
if (include_decryption) { | ||
// if we decrypt, include the GetLength | ||
assert(aead.GetLength(&len, seqnr_aad, aad_pos, in.data())); | ||
assert(aead.Crypt(seqnr_payload, seqnr_aad, aad_pos, out.data(), out.size(), in.data(), buffersize, true)); | ||
} | ||
|
||
// increase main sequence number | ||
seqnr_payload++; | ||
// increase aad position (position in AAD keystream) | ||
aad_pos += CHACHA20_POLY1305_AEAD_AAD_LEN; | ||
if (aad_pos + CHACHA20_POLY1305_AEAD_AAD_LEN > CHACHA20_ROUND_OUTPUT) { | ||
aad_pos = 0; | ||
seqnr_aad++; | ||
} | ||
if (seqnr_payload + 1 == std::numeric_limits<uint64_t>::max()) { | ||
// reuse of nonce+key is okay while benchmarking. | ||
seqnr_payload = 0; | ||
seqnr_aad = 0; | ||
aad_pos = 0; | ||
} | ||
} | ||
} | ||
|
||
static void CHACHA20_POLY1305_AEAD_64BYTES_ONLY_ENCRYPT(benchmark::State& state) | ||
{ | ||
CHACHA20_POLY1305_AEAD(state, BUFFER_SIZE_TINY, false); | ||
} | ||
|
||
static void CHACHA20_POLY1305_AEAD_256BYTES_ONLY_ENCRYPT(benchmark::State& state) | ||
{ | ||
CHACHA20_POLY1305_AEAD(state, BUFFER_SIZE_SMALL, false); | ||
} | ||
|
||
static void CHACHA20_POLY1305_AEAD_1MB_ONLY_ENCRYPT(benchmark::State& state) | ||
{ | ||
CHACHA20_POLY1305_AEAD(state, BUFFER_SIZE_LARGE, false); | ||
} | ||
|
||
static void CHACHA20_POLY1305_AEAD_64BYTES_ENCRYPT_DECRYPT(benchmark::State& state) | ||
{ | ||
CHACHA20_POLY1305_AEAD(state, BUFFER_SIZE_TINY, true); | ||
} | ||
|
||
static void CHACHA20_POLY1305_AEAD_256BYTES_ENCRYPT_DECRYPT(benchmark::State& state) | ||
{ | ||
CHACHA20_POLY1305_AEAD(state, BUFFER_SIZE_SMALL, true); | ||
} | ||
|
||
static void CHACHA20_POLY1305_AEAD_1MB_ENCRYPT_DECRYPT(benchmark::State& state) | ||
{ | ||
CHACHA20_POLY1305_AEAD(state, BUFFER_SIZE_LARGE, true); | ||
} | ||
|
||
// Add Hash() (dbl-sha256) bench for comparison | ||
|
||
static void HASH(benchmark::State& state, size_t buffersize) | ||
{ | ||
uint8_t hash[CHash256::OUTPUT_SIZE]; | ||
std::vector<uint8_t> in(buffersize,0); | ||
while (state.KeepRunning()) | ||
CHash256().Write(in.data(), in.size()).Finalize(hash); | ||
} | ||
|
||
static void HASH_64BYTES(benchmark::State& state) | ||
{ | ||
HASH(state, BUFFER_SIZE_TINY); | ||
} | ||
|
||
static void HASH_256BYTES(benchmark::State& state) | ||
{ | ||
HASH(state, BUFFER_SIZE_SMALL); | ||
} | ||
|
||
static void HASH_1MB(benchmark::State& state) | ||
{ | ||
HASH(state, BUFFER_SIZE_LARGE); | ||
} | ||
|
||
BENCHMARK(CHACHA20_POLY1305_AEAD_64BYTES_ONLY_ENCRYPT, 500000); | ||
BENCHMARK(CHACHA20_POLY1305_AEAD_256BYTES_ONLY_ENCRYPT, 250000); | ||
BENCHMARK(CHACHA20_POLY1305_AEAD_1MB_ONLY_ENCRYPT, 340); | ||
BENCHMARK(CHACHA20_POLY1305_AEAD_64BYTES_ENCRYPT_DECRYPT, 500000); | ||
BENCHMARK(CHACHA20_POLY1305_AEAD_256BYTES_ENCRYPT_DECRYPT, 250000); | ||
BENCHMARK(CHACHA20_POLY1305_AEAD_1MB_ENCRYPT_DECRYPT, 340); | ||
BENCHMARK(HASH_64BYTES, 500000); | ||
BENCHMARK(HASH_256BYTES, 250000); | ||
BENCHMARK(HASH_1MB, 340); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
// Copyright (c) 2019 The Bitcoin Core developers | ||
// Distributed under the MIT software license, see the accompanying | ||
// file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||
|
||
#include <crypto/chacha_poly_aead.h> | ||
|
||
#include <crypto/common.h> | ||
#include <crypto/poly1305.h> | ||
#include <support/cleanse.h> | ||
|
||
#include <assert.h> | ||
#include <string.h> | ||
|
||
#include <cstdio> | ||
#include <limits> | ||
|
||
#ifndef HAVE_TIMINGSAFE_BCMP | ||
|
||
int timingsafe_bcmp(const unsigned char* b1, const unsigned char* b2, size_t n) | ||
{ | ||
const unsigned char *p1 = b1, *p2 = b2; | ||
int ret = 0; | ||
|
||
for (; n > 0; n--) | ||
ret |= *p1++ ^ *p2++; | ||
return (ret != 0); | ||
} | ||
|
||
#endif // TIMINGSAFE_BCMP | ||
|
||
ChaCha20Poly1305AEAD::ChaCha20Poly1305AEAD(const unsigned char* K_1, size_t K_1_len, const unsigned char* K_2, size_t K_2_len) | ||
{ | ||
assert(K_1_len == CHACHA20_POLY1305_AEAD_KEY_LEN); | ||
assert(K_2_len == CHACHA20_POLY1305_AEAD_KEY_LEN); | ||
m_chacha_main.SetKey(K_1, CHACHA20_POLY1305_AEAD_KEY_LEN); | ||
m_chacha_header.SetKey(K_2, CHACHA20_POLY1305_AEAD_KEY_LEN); | ||
|
||
// set the cached sequence number to uint64 max which hints for an unset cache. | ||
// we can't hit uint64 max since the rekey rule (which resets the sequence number) is 1GB | ||
m_cached_aad_seqnr = std::numeric_limits<uint64_t>::max(); | ||
} | ||
|
||
bool ChaCha20Poly1305AEAD::Crypt(uint64_t seqnr_payload, uint64_t seqnr_aad, int aad_pos, unsigned char* dest, size_t dest_len /* length of the output buffer for sanity checks */, const unsigned char* src, size_t src_len, bool is_encrypt) | ||
{ | ||
// check buffer boundaries | ||
if ( | ||
// if we encrypt, make sure the source contains at least the expected AAD and the destination has at least space for the source + MAC | ||
(is_encrypt && (src_len < CHACHA20_POLY1305_AEAD_AAD_LEN || dest_len < src_len + POLY1305_TAGLEN)) || | ||
// if we decrypt, make sure the source contains at least the expected AAD+MAC and the destination has at least space for the source - MAC | ||
(!is_encrypt && (src_len < CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN || dest_len < src_len - POLY1305_TAGLEN))) { | ||
return false; | ||
} | ||
|
||
unsigned char expected_tag[POLY1305_TAGLEN], poly_key[POLY1305_KEYLEN]; | ||
memset(poly_key, 0, sizeof(poly_key)); | ||
m_chacha_main.SetIV(seqnr_payload); | ||
|
||
// block counter 0 for the poly1305 key | ||
// use lower 32bytes for the poly1305 key | ||
// (throws away 32 unused bytes (upper 32) from this ChaCha20 round) | ||
m_chacha_main.Seek(0); | ||
m_chacha_main.Crypt(poly_key, poly_key, sizeof(poly_key)); | ||
|
||
// if decrypting, verify the tag prior to decryption | ||
if (!is_encrypt) { | ||
const unsigned char* tag = src + src_len - POLY1305_TAGLEN; | ||
poly1305_auth(expected_tag, src, src_len - POLY1305_TAGLEN, poly_key); | ||
|
||
// constant time compare the calculated MAC with the provided MAC | ||
if (timingsafe_bcmp(expected_tag, tag, POLY1305_TAGLEN) != 0) { | ||
memory_cleanse(expected_tag, sizeof(expected_tag)); | ||
memory_cleanse(poly_key, sizeof(poly_key)); | ||
return false; | ||
} | ||
memory_cleanse(expected_tag, sizeof(expected_tag)); | ||
// MAC has been successfully verified, make sure we don't covert it in decryption | ||
src_len -= POLY1305_TAGLEN; | ||
} | ||
|
||
// calculate and cache the next 64byte keystream block if requested sequence number is not yet the cache | ||
if (m_cached_aad_seqnr != seqnr_aad) { | ||
m_cached_aad_seqnr = seqnr_aad; | ||
m_chacha_header.SetIV(seqnr_aad); | ||
m_chacha_header.Seek(0); | ||
m_chacha_header.Keystream(m_aad_keystream_buffer, CHACHA20_ROUND_OUTPUT); | ||
} | ||
// crypt the AAD (3 bytes message length) with given position in AAD cipher instance keystream | ||
dest[0] = src[0] ^ m_aad_keystream_buffer[aad_pos]; | ||
dest[1] = src[1] ^ m_aad_keystream_buffer[aad_pos + 1]; | ||
dest[2] = src[2] ^ m_aad_keystream_buffer[aad_pos + 2]; | ||
|
||
// Set the playload ChaCha instance block counter to 1 and crypt the payload | ||
m_chacha_main.Seek(1); | ||
m_chacha_main.Crypt(src + CHACHA20_POLY1305_AEAD_AAD_LEN, dest + CHACHA20_POLY1305_AEAD_AAD_LEN, src_len - CHACHA20_POLY1305_AEAD_AAD_LEN); | ||
|
||
// If encrypting, calculate and append tag | ||
if (is_encrypt) { | ||
// the poly1305 tag expands over the AAD (3 bytes length) & encrypted payload | ||
poly1305_auth(dest + src_len, dest, src_len, poly_key); | ||
} | ||
|
||
// cleanse no longer required MAC and polykey | ||
memory_cleanse(poly_key, sizeof(poly_key)); | ||
return true; | ||
} | ||
|
||
bool ChaCha20Poly1305AEAD::GetLength(uint32_t* len24_out, uint64_t seqnr_aad, int aad_pos, const uint8_t* ciphertext) | ||
{ | ||
// enforce valid aad position to avoid accessing outside of the 64byte keystream cache | ||
// (there is space for 21 times 3 bytes) | ||
assert(aad_pos >= 0 && aad_pos < CHACHA20_ROUND_OUTPUT - CHACHA20_POLY1305_AEAD_AAD_LEN); | ||
if (m_cached_aad_seqnr != seqnr_aad) { | ||
// we need to calculate the 64 keystream bytes since we reached a new aad sequence number | ||
m_cached_aad_seqnr = seqnr_aad; | ||
m_chacha_header.SetIV(seqnr_aad); // use LE for the nonce | ||
m_chacha_header.Seek(0); // block counter 0 | ||
m_chacha_header.Keystream(m_aad_keystream_buffer, CHACHA20_ROUND_OUTPUT); // write keystream to the cache | ||
} | ||
|
||
// decrypt the ciphertext length by XORing the right position of the 64byte keystream cache with the ciphertext | ||
*len24_out = (ciphertext[0] ^ m_aad_keystream_buffer[aad_pos + 0]) | | ||
(ciphertext[1] ^ m_aad_keystream_buffer[aad_pos + 1]) << 8 | | ||
(ciphertext[2] ^ m_aad_keystream_buffer[aad_pos + 2]) << 16; | ||
|
||
return true; | ||
} |
Oops, something went wrong.