Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ BITCOIN_CORE_H = \
base58.h \
batchedlogger.h \
bech32.h \
bip324.h \
blockencodings.h \
bloom.h \
cachemap.h \
Expand Down Expand Up @@ -408,6 +409,7 @@ libbitcoin_server_a_SOURCES = \
addrman.cpp \
banman.cpp \
batchedlogger.cpp \
bip324.cpp \
blockencodings.cpp \
blockfilter.cpp \
chain.cpp \
Expand Down Expand Up @@ -577,10 +579,10 @@ crypto_libbitcoin_crypto_base_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) $(PIC_FLA
crypto_libbitcoin_crypto_base_a_SOURCES = \
crypto/aes.cpp \
crypto/aes.h \
crypto/chacha_poly_aead.h \
crypto/chacha_poly_aead.cpp \
crypto/chacha20.h \
crypto/chacha20.cpp \
crypto/chacha20poly1305.h \
crypto/chacha20poly1305.cpp \
crypto/common.h \
crypto/hkdf_sha256_32.cpp \
crypto/hkdf_sha256_32.h \
Expand Down
1 change: 0 additions & 1 deletion src/Makefile.bench.include
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ bench_bench_dash_SOURCES = \
bench/examples.cpp \
bench/rollingbloom.cpp \
bench/chacha20.cpp \
bench/chacha_poly_aead.cpp \
bench/crypto_hash.cpp \
bench/ccoins_caching.cpp \
bench/gcs_filter.cpp \
Expand Down
3 changes: 2 additions & 1 deletion src/Makefile.test.include
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ BITCOIN_TESTS =\
test/base64_tests.cpp \
test/bech32_tests.cpp \
test/bip32_tests.cpp \
test/bip324_tests.cpp \
test/block_reward_reallocation_tests.cpp \
test/blockchain_tests.cpp \
test/blockencodings_tests.cpp \
Expand Down Expand Up @@ -242,6 +243,7 @@ test_fuzz_fuzz_SOURCES = \
test/fuzz/banman.cpp \
test/fuzz/base_encode_decode.cpp \
test/fuzz/bech32.cpp \
test/fuzz/bip324.cpp \
test/fuzz/block.cpp \
test/fuzz/block_header.cpp \
test/fuzz/blockfilter.cpp \
Expand All @@ -255,7 +257,6 @@ test_fuzz_fuzz_SOURCES = \
test/fuzz/crypto_aes256.cpp \
test/fuzz/crypto_aes256cbc.cpp \
test/fuzz/crypto_chacha20.cpp \
test/fuzz/crypto_chacha20_poly1305_aead.cpp \
test/fuzz/crypto_common.cpp \
test/fuzz/crypto_diff_fuzz_chacha20.cpp \
test/fuzz/crypto_hkdf_hmac_sha256_l32.cpp \
Expand Down
44 changes: 37 additions & 7 deletions src/bench/chacha20.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include <bench/bench.h>
#include <crypto/chacha20.h>
#include <crypto/chacha20poly1305.h>

/* Number of bytes to process per iteration */
static const uint64_t BUFFER_SIZE_TINY = 64;
Expand All @@ -13,14 +14,25 @@ static const uint64_t BUFFER_SIZE_LARGE = 1024*1024;

static void CHACHA20(benchmark::Bench& bench, size_t buffersize)
{
std::vector<uint8_t> key(32,0);
ChaCha20 ctx(key.data());
ctx.SetIV(0);
ctx.Seek64(0);
std::vector<uint8_t> in(buffersize,0);
std::vector<uint8_t> out(buffersize,0);
std::vector<std::byte> key(32, {});
ChaCha20 ctx(key);
ctx.Seek({0, 0}, 0);
std::vector<std::byte> in(buffersize, {});
std::vector<std::byte> out(buffersize, {});
bench.batch(in.size()).unit("byte").run([&] {
ctx.Crypt(in.data(), out.data(), in.size());
ctx.Crypt(in, out);
});
}

static void FSCHACHA20POLY1305(benchmark::Bench& bench, size_t buffersize)
{
std::vector<std::byte> key(32);
FSChaCha20Poly1305 ctx(key, 224);
std::vector<std::byte> in(buffersize);
std::vector<std::byte> aad;
std::vector<std::byte> out(buffersize + FSChaCha20Poly1305::EXPANSION);
bench.batch(in.size()).unit("byte").run([&] {
ctx.Encrypt(in, aad, out);
});
}

Expand All @@ -39,6 +51,24 @@ static void CHACHA20_1MB(benchmark::Bench& bench)
CHACHA20(bench, BUFFER_SIZE_LARGE);
}

static void FSCHACHA20POLY1305_64BYTES(benchmark::Bench& bench)
{
FSCHACHA20POLY1305(bench, BUFFER_SIZE_TINY);
}

static void FSCHACHA20POLY1305_256BYTES(benchmark::Bench& bench)
{
FSCHACHA20POLY1305(bench, BUFFER_SIZE_SMALL);
}

static void FSCHACHA20POLY1305_1MB(benchmark::Bench& bench)
{
FSCHACHA20POLY1305(bench, BUFFER_SIZE_LARGE);
}

BENCHMARK(CHACHA20_64BYTES);
BENCHMARK(CHACHA20_256BYTES);
BENCHMARK(CHACHA20_1MB);
BENCHMARK(FSCHACHA20POLY1305_64BYTES);
BENCHMARK(FSCHACHA20POLY1305_256BYTES);
BENCHMARK(FSCHACHA20POLY1305_1MB);
126 changes: 0 additions & 126 deletions src/bench/chacha_poly_aead.cpp

This file was deleted.

10 changes: 6 additions & 4 deletions src/bench/poly1305.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,20 @@
#include <bench/bench.h>
#include <crypto/poly1305.h>

#include <span.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 void POLY1305(benchmark::Bench& bench, size_t buffersize)
{
std::vector<unsigned char> tag(POLY1305_TAGLEN, 0);
std::vector<unsigned char> key(POLY1305_KEYLEN, 0);
std::vector<unsigned char> in(buffersize, 0);
std::vector<std::byte> tag(Poly1305::TAGLEN, {});
std::vector<std::byte> key(Poly1305::KEYLEN, {});
std::vector<std::byte> in(buffersize, {});
bench.batch(in.size()).unit("byte").run([&] {
poly1305_auth(tag.data(), in.data(), in.size(), key.data());
Poly1305{key}.Update(in).Finalize(tag);
});
}

Expand Down
116 changes: 116 additions & 0 deletions src/bip324.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// Copyright (c) 2023 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 <bip324.h>

#include <chainparams.h>
#include <crypto/chacha20.h>
#include <crypto/chacha20poly1305.h>
#include <crypto/hkdf_sha256_32.h>
#include <key.h>
#include <pubkey.h>
#include <random.h>
#include <span.h>
#include <support/cleanse.h>
#include <uint256.h>

#include <algorithm>
#include <assert.h>
#include <cstdint>
#include <cstddef>
#include <iterator>
#include <string>

BIP324Cipher::BIP324Cipher() noexcept
{
m_key.MakeNewKey(true);
uint256 entropy = GetRandHash();
m_our_pubkey = m_key.EllSwiftCreate(MakeByteSpan(entropy));
}

BIP324Cipher::BIP324Cipher(const CKey& key, Span<const std::byte> ent32) noexcept :
m_key(key)
{
m_our_pubkey = m_key.EllSwiftCreate(ent32);
}

BIP324Cipher::BIP324Cipher(const CKey& key, const EllSwiftPubKey& pubkey) noexcept :
m_key(key), m_our_pubkey(pubkey) {}

void BIP324Cipher::Initialize(const EllSwiftPubKey& their_pubkey, bool initiator, bool self_decrypt) noexcept
{
// Determine salt (fixed string + network magic bytes)
const auto& message_header = Params().MessageStart();
std::string salt = std::string{"bitcoin_v2_shared_secret"} + std::string(std::begin(message_header), std::end(message_header));

// Perform ECDH to derive shared secret.
ECDHSecret ecdh_secret = m_key.ComputeBIP324ECDHSecret(their_pubkey, m_our_pubkey, initiator);

// Derive encryption keys from shared secret, and initialize stream ciphers and AEADs.
bool side = (initiator != self_decrypt);
CHKDF_HMAC_SHA256_L32 hkdf(UCharCast(ecdh_secret.data()), ecdh_secret.size(), salt);
std::array<std::byte, 32> hkdf_32_okm;
hkdf.Expand32("initiator_L", UCharCast(hkdf_32_okm.data()));
(side ? m_send_l_cipher : m_recv_l_cipher).emplace(hkdf_32_okm, REKEY_INTERVAL);
hkdf.Expand32("initiator_P", UCharCast(hkdf_32_okm.data()));
(side ? m_send_p_cipher : m_recv_p_cipher).emplace(hkdf_32_okm, REKEY_INTERVAL);
hkdf.Expand32("responder_L", UCharCast(hkdf_32_okm.data()));
(side ? m_recv_l_cipher : m_send_l_cipher).emplace(hkdf_32_okm, REKEY_INTERVAL);
hkdf.Expand32("responder_P", UCharCast(hkdf_32_okm.data()));
(side ? m_recv_p_cipher : m_send_p_cipher).emplace(hkdf_32_okm, REKEY_INTERVAL);

// Derive garbage terminators from shared secret.
hkdf.Expand32("garbage_terminators", UCharCast(hkdf_32_okm.data()));
std::copy(std::begin(hkdf_32_okm), std::begin(hkdf_32_okm) + GARBAGE_TERMINATOR_LEN,
(initiator ? m_send_garbage_terminator : m_recv_garbage_terminator).begin());
std::copy(std::end(hkdf_32_okm) - GARBAGE_TERMINATOR_LEN, std::end(hkdf_32_okm),
(initiator ? m_recv_garbage_terminator : m_send_garbage_terminator).begin());

// Derive session id from shared secret.
hkdf.Expand32("session_id", UCharCast(m_session_id.data()));

// Wipe all variables that contain information which could be used to re-derive encryption keys.
memory_cleanse(ecdh_secret.data(), ecdh_secret.size());
memory_cleanse(hkdf_32_okm.data(), sizeof(hkdf_32_okm));
memory_cleanse(&hkdf, sizeof(hkdf));
m_key = CKey();
}

void BIP324Cipher::Encrypt(Span<const std::byte> contents, Span<const std::byte> aad, bool ignore, Span<std::byte> output) noexcept
{
assert(output.size() == contents.size() + EXPANSION);

// Encrypt length.
std::byte len[LENGTH_LEN];
len[0] = std::byte{(uint8_t)(contents.size() & 0xFF)};
len[1] = std::byte{(uint8_t)((contents.size() >> 8) & 0xFF)};
len[2] = std::byte{(uint8_t)((contents.size() >> 16) & 0xFF)};
m_send_l_cipher->Crypt(len, output.first(LENGTH_LEN));

// Encrypt plaintext.
std::byte header[HEADER_LEN] = {ignore ? IGNORE_BIT : std::byte{0}};
m_send_p_cipher->Encrypt(header, contents, aad, output.subspan(LENGTH_LEN));
}

uint32_t BIP324Cipher::DecryptLength(Span<const std::byte> input) noexcept
{
assert(input.size() == LENGTH_LEN);

std::byte buf[LENGTH_LEN];
// Decrypt length
m_recv_l_cipher->Crypt(input, buf);
// Convert to number.
return uint32_t(buf[0]) + (uint32_t(buf[1]) << 8) + (uint32_t(buf[2]) << 16);
}

bool BIP324Cipher::Decrypt(Span<const std::byte> input, Span<const std::byte> aad, bool& ignore, Span<std::byte> contents) noexcept
{
assert(input.size() + LENGTH_LEN == contents.size() + EXPANSION);

std::byte header[HEADER_LEN];
if (!m_recv_p_cipher->Decrypt(input, aad, header, contents)) return false;

ignore = (header[0] & IGNORE_BIT) == IGNORE_BIT;
return true;
}
Loading