Skip to content

Commit 8f7b9eb

Browse files
committed
Merge bitcoin/bitcoin#28433: Follow-up to BIP324 connection support
6470438 doc: fix typos and mistakes in BIP324 code comments (Pieter Wuille) 9bde93d net: do not use send buffer to store/cache garbage (Pieter Wuille) b6934fd net: merge V2Transport constructors, move key gen (Pieter Wuille) Pull request description: This addresses a few remaining comments on #28196: * Deduplicate the `V2Transport` constructors (bitcoin/bitcoin#28196 (comment)) * Do not use the send buffer to store garbage (bitcoin/bitcoin#28196 (comment)) * Fix typo (bitcoin/bitcoin#28196 (comment)) In addition, also fix an incorrect description in `V2Transport::SendState` (it claimed garbage was sent in the `READY` state, but it's in the `AWAITING_KEY` state). ACKs for top commit: naumenkogs: ACK 6470438 theStack: Code-review ACK 6470438 Tree-SHA512: 4bf6d2fe73c8054502d0b60e9de1722f8b3dd269c2dd6bf67197c3fb6eabcf047b6360cdab3c1fd5504215c2ac4ac2890a022780efc30ff583776242c8112451
2 parents c5a63ea + 6470438 commit 8f7b9eb

File tree

6 files changed

+72
-47
lines changed

6 files changed

+72
-47
lines changed

src/bip324.cpp

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,6 @@
2222
#include <iterator>
2323
#include <string>
2424

25-
BIP324Cipher::BIP324Cipher() noexcept
26-
{
27-
m_key.MakeNewKey(true);
28-
uint256 entropy = GetRandHash();
29-
m_our_pubkey = m_key.EllSwiftCreate(MakeByteSpan(entropy));
30-
}
31-
3225
BIP324Cipher::BIP324Cipher(const CKey& key, Span<const std::byte> ent32) noexcept :
3326
m_key(key)
3427
{

src/bip324.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ class BIP324Cipher
4141
std::array<std::byte, GARBAGE_TERMINATOR_LEN> m_recv_garbage_terminator;
4242

4343
public:
44-
/** Initialize a BIP324 cipher with securely generated random keys. */
45-
BIP324Cipher() noexcept;
44+
/** No default constructor; keys must be provided to create a BIP324Cipher. */
45+
BIP324Cipher() = delete;
4646

4747
/** Initialize a BIP324 cipher with specified key and encoding entropy (testing only). */
4848
BIP324Cipher(const CKey& key, Span<const std::byte> ent32) noexcept;

src/net.cpp

Lines changed: 46 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -979,36 +979,56 @@ class V2MessageMap
979979

980980
const V2MessageMap V2_MESSAGE_MAP;
981981

982-
} // namespace
982+
CKey GenerateRandomKey() noexcept
983+
{
984+
CKey key;
985+
key.MakeNewKey(/*fCompressed=*/true);
986+
return key;
987+
}
983988

984-
V2Transport::V2Transport(NodeId nodeid, bool initiating, int type_in, int version_in) noexcept :
985-
m_cipher{}, m_initiating{initiating}, m_nodeid{nodeid},
986-
m_v1_fallback{nodeid, type_in, version_in}, m_recv_type{type_in}, m_recv_version{version_in},
987-
m_recv_state{initiating ? RecvState::KEY : RecvState::KEY_MAYBE_V1},
988-
m_send_state{initiating ? SendState::AWAITING_KEY : SendState::MAYBE_V1}
989+
std::vector<uint8_t> GenerateRandomGarbage() noexcept
989990
{
990-
// Construct garbage (including its length) using a FastRandomContext.
991+
std::vector<uint8_t> ret;
991992
FastRandomContext rng;
992-
size_t garbage_len = rng.randrange(MAX_GARBAGE_LEN + 1);
993-
// Initialize the send buffer with ellswift pubkey + garbage.
994-
m_send_buffer.resize(EllSwiftPubKey::size() + garbage_len);
993+
ret.resize(rng.randrange(V2Transport::MAX_GARBAGE_LEN + 1));
994+
rng.fillrand(MakeWritableByteSpan(ret));
995+
return ret;
996+
}
997+
998+
} // namespace
999+
1000+
void V2Transport::StartSendingHandshake() noexcept
1001+
{
1002+
AssertLockHeld(m_send_mutex);
1003+
Assume(m_send_state == SendState::AWAITING_KEY);
1004+
Assume(m_send_buffer.empty());
1005+
// Initialize the send buffer with ellswift pubkey + provided garbage.
1006+
m_send_buffer.resize(EllSwiftPubKey::size() + m_send_garbage.size());
9951007
std::copy(std::begin(m_cipher.GetOurPubKey()), std::end(m_cipher.GetOurPubKey()), MakeWritableByteSpan(m_send_buffer).begin());
996-
rng.fillrand(MakeWritableByteSpan(m_send_buffer).subspan(EllSwiftPubKey::size()));
1008+
std::copy(m_send_garbage.begin(), m_send_garbage.end(), m_send_buffer.begin() + EllSwiftPubKey::size());
1009+
// We cannot wipe m_send_garbage as it will still be used to construct the garbage
1010+
// authentication packet.
9971011
}
9981012

999-
V2Transport::V2Transport(NodeId nodeid, bool initiating, int type_in, int version_in, const CKey& key, Span<const std::byte> ent32, Span<const uint8_t> garbage) noexcept :
1013+
V2Transport::V2Transport(NodeId nodeid, bool initiating, int type_in, int version_in, const CKey& key, Span<const std::byte> ent32, std::vector<uint8_t> garbage) noexcept :
10001014
m_cipher{key, ent32}, m_initiating{initiating}, m_nodeid{nodeid},
10011015
m_v1_fallback{nodeid, type_in, version_in}, m_recv_type{type_in}, m_recv_version{version_in},
10021016
m_recv_state{initiating ? RecvState::KEY : RecvState::KEY_MAYBE_V1},
1017+
m_send_garbage{std::move(garbage)},
10031018
m_send_state{initiating ? SendState::AWAITING_KEY : SendState::MAYBE_V1}
10041019
{
1005-
assert(garbage.size() <= MAX_GARBAGE_LEN);
1006-
// Initialize the send buffer with ellswift pubkey + provided garbage.
1007-
m_send_buffer.resize(EllSwiftPubKey::size() + garbage.size());
1008-
std::copy(std::begin(m_cipher.GetOurPubKey()), std::end(m_cipher.GetOurPubKey()), MakeWritableByteSpan(m_send_buffer).begin());
1009-
std::copy(garbage.begin(), garbage.end(), m_send_buffer.begin() + EllSwiftPubKey::size());
1020+
Assume(m_send_garbage.size() <= MAX_GARBAGE_LEN);
1021+
// Start sending immediately if we're the initiator of the connection.
1022+
if (initiating) {
1023+
LOCK(m_send_mutex);
1024+
StartSendingHandshake();
1025+
}
10101026
}
10111027

1028+
V2Transport::V2Transport(NodeId nodeid, bool initiating, int type_in, int version_in) noexcept :
1029+
V2Transport{nodeid, initiating, type_in, version_in, GenerateRandomKey(),
1030+
MakeByteSpan(GetRandHash()), GenerateRandomGarbage()} { }
1031+
10121032
void V2Transport::SetReceiveState(RecvState recv_state) noexcept
10131033
{
10141034
AssertLockHeld(m_recv_mutex);
@@ -1087,9 +1107,10 @@ void V2Transport::ProcessReceivedMaybeV1Bytes() noexcept
10871107
if (!std::equal(m_recv_buffer.begin(), m_recv_buffer.end(), v1_prefix.begin())) {
10881108
// Mismatch with v1 prefix, so we can assume a v2 connection.
10891109
SetReceiveState(RecvState::KEY); // Convert to KEY state, leaving received bytes around.
1090-
// Transition the sender to AWAITING_KEY state (if not already).
1110+
// Transition the sender to AWAITING_KEY state and start sending.
10911111
LOCK(m_send_mutex);
10921112
SetSendState(SendState::AWAITING_KEY);
1113+
StartSendingHandshake();
10931114
} else if (m_recv_buffer.size() == v1_prefix.size()) {
10941115
// Full match with the v1 prefix, so fall back to v1 behavior.
10951116
LOCK(m_send_mutex);
@@ -1149,7 +1170,6 @@ bool V2Transport::ProcessReceivedKeyBytes() noexcept
11491170
SetSendState(SendState::READY);
11501171

11511172
// Append the garbage terminator to the send buffer.
1152-
size_t garbage_len = m_send_buffer.size() - EllSwiftPubKey::size();
11531173
m_send_buffer.resize(m_send_buffer.size() + BIP324Cipher::GARBAGE_TERMINATOR_LEN);
11541174
std::copy(m_cipher.GetSendGarbageTerminator().begin(),
11551175
m_cipher.GetSendGarbageTerminator().end(),
@@ -1160,9 +1180,12 @@ bool V2Transport::ProcessReceivedKeyBytes() noexcept
11601180
m_send_buffer.resize(m_send_buffer.size() + BIP324Cipher::EXPANSION);
11611181
m_cipher.Encrypt(
11621182
/*contents=*/{},
1163-
/*aad=*/MakeByteSpan(m_send_buffer).subspan(EllSwiftPubKey::size(), garbage_len),
1183+
/*aad=*/MakeByteSpan(m_send_garbage),
11641184
/*ignore=*/false,
11651185
/*output=*/MakeWritableByteSpan(m_send_buffer).last(BIP324Cipher::EXPANSION));
1186+
// We no longer need the garbage.
1187+
m_send_garbage.clear();
1188+
m_send_garbage.shrink_to_fit();
11661189

11671190
// Construct version packet in the send buffer.
11681191
m_send_buffer.resize(m_send_buffer.size() + BIP324Cipher::EXPANSION + VERSION_CONTENTS.size());
@@ -1532,9 +1555,7 @@ Transport::BytesToSend V2Transport::GetBytesToSend(bool have_next_message) const
15321555
LOCK(m_send_mutex);
15331556
if (m_send_state == SendState::V1) return m_v1_fallback.GetBytesToSend(have_next_message);
15341557

1535-
// We do not send anything in MAYBE_V1 state (as we don't know if the peer is v1 or v2),
1536-
// despite there being data in the send buffer in that state.
1537-
if (m_send_state == SendState::MAYBE_V1) return {{}, false, m_send_type};
1558+
if (m_send_state == SendState::MAYBE_V1) Assume(m_send_buffer.empty());
15381559
Assume(m_send_pos <= m_send_buffer.size());
15391560
return {
15401561
Span{m_send_buffer}.subspan(m_send_pos),
@@ -1553,10 +1574,8 @@ void V2Transport::MarkBytesSent(size_t bytes_sent) noexcept
15531574

15541575
m_send_pos += bytes_sent;
15551576
Assume(m_send_pos <= m_send_buffer.size());
1556-
// Only wipe the buffer when everything is sent in the READY state. In the AWAITING_KEY state
1557-
// we still need the garbage that's in the send buffer to construct the garbage authentication
1558-
// packet.
1559-
if (m_send_state == SendState::READY && m_send_pos == m_send_buffer.size()) {
1577+
// Wipe the buffer when everything is sent.
1578+
if (m_send_pos == m_send_buffer.size()) {
15601579
m_send_pos = 0;
15611580
m_send_buffer = {};
15621581
}

src/net.h

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -540,25 +540,25 @@ class V2Transport final : public Transport
540540
enum class SendState : uint8_t {
541541
/** (Responder only) Not sending until v1 or v2 is detected.
542542
*
543-
* This is the initial state for responders. The send buffer contains the public key to
544-
* send, but nothing is sent in this state yet. When the receiver determines whether this
543+
* This is the initial state for responders. The send buffer is empty.
544+
* When the receiver determines whether this
545545
* is a V1 or V2 connection, the sender state becomes AWAITING_KEY (for v2) or V1 (for v1).
546546
*/
547547
MAYBE_V1,
548548

549549
/** Waiting for the other side's public key.
550550
*
551-
* This is the initial state for initiators. The public key is sent out. When the receiver
552-
* receives the other side's public key and transitions to GARB_GARBTERM, the sender state
553-
* becomes READY. */
551+
* This is the initial state for initiators. The public key and garbage is sent out. When
552+
* the receiver receives the other side's public key and transitions to GARB_GARBTERM, the
553+
* sender state becomes READY. */
554554
AWAITING_KEY,
555555

556556
/** Normal sending state.
557557
*
558558
* In this state, the ciphers are initialized, so packets can be sent. When this state is
559-
* entered, the garbage, garbage terminator, garbage authentication packet, and version
560-
* packet are appended to the send buffer (in addition to the key which may still be
561-
* there). In this state a message can be provided if the send buffer is empty. */
559+
* entered, the garbage terminator, garbage authentication packet, and version
560+
* packet are appended to the send buffer (in addition to the key and garbage which may
561+
* still be there). In this state a message can be provided if the send buffer is empty. */
562562
READY,
563563

564564
/** This transport is using v1 fallback.
@@ -601,6 +601,8 @@ class V2Transport final : public Transport
601601
std::vector<uint8_t> m_send_buffer GUARDED_BY(m_send_mutex);
602602
/** How many bytes from the send buffer have been sent so far. */
603603
uint32_t m_send_pos GUARDED_BY(m_send_mutex) {0};
604+
/** The garbage sent, or to be sent (MAYBE_V1 and AWAITING_KEY state only). */
605+
std::vector<uint8_t> m_send_garbage GUARDED_BY(m_send_mutex);
604606
/** Type of the message being sent. */
605607
std::string m_send_type GUARDED_BY(m_send_mutex);
606608
/** Current sender state. */
@@ -614,6 +616,8 @@ class V2Transport final : public Transport
614616
static std::optional<std::string> GetMessageType(Span<const uint8_t>& contents) noexcept;
615617
/** Determine how many received bytes can be processed in one go (not allowed in V1 state). */
616618
size_t GetMaxBytesToProcess() noexcept EXCLUSIVE_LOCKS_REQUIRED(m_recv_mutex);
619+
/** Put our public key + garbage in the send buffer. */
620+
void StartSendingHandshake() noexcept EXCLUSIVE_LOCKS_REQUIRED(m_send_mutex);
617621
/** Process bytes in m_recv_buffer, while in KEY_MAYBE_V1 state. */
618622
void ProcessReceivedMaybeV1Bytes() noexcept EXCLUSIVE_LOCKS_REQUIRED(m_recv_mutex, !m_send_mutex);
619623
/** Process bytes in m_recv_buffer, while in KEY state. */
@@ -636,7 +640,7 @@ class V2Transport final : public Transport
636640
V2Transport(NodeId nodeid, bool initiating, int type_in, int version_in) noexcept;
637641

638642
/** Construct a V2 transport with specified keys and garbage (test use only). */
639-
V2Transport(NodeId nodeid, bool initiating, int type_in, int version_in, const CKey& key, Span<const std::byte> ent32, Span<const uint8_t> garbage) noexcept;
643+
V2Transport(NodeId nodeid, bool initiating, int type_in, int version_in, const CKey& key, Span<const std::byte> ent32, std::vector<uint8_t> garbage) noexcept;
640644

641645
// Receive side functions.
642646
bool ReceivedMessageComplete() const noexcept override EXCLUSIVE_LOCKS_REQUIRED(!m_recv_mutex);

src/test/fuzz/p2p_transport_serialization.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,7 @@ std::unique_ptr<Transport> MakeV2Transport(NodeId nodeid, bool initiator, RNG& r
366366
.Write(garb.data(), garb.size())
367367
.Finalize(UCharCast(ent.data()));
368368

369-
return std::make_unique<V2Transport>(nodeid, initiator, SER_NETWORK, INIT_PROTO_VERSION, key, ent, garb);
369+
return std::make_unique<V2Transport>(nodeid, initiator, SER_NETWORK, INIT_PROTO_VERSION, key, ent, std::move(garb));
370370
}
371371

372372
} // namespace

src/test/net_tests.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1008,12 +1008,20 @@ BOOST_AUTO_TEST_CASE(advertise_local_address)
10081008

10091009
namespace {
10101010

1011+
CKey GenerateRandomTestKey() noexcept
1012+
{
1013+
CKey key;
1014+
uint256 key_data = InsecureRand256();
1015+
key.Set(key_data.begin(), key_data.end(), true);
1016+
return key;
1017+
}
1018+
10111019
/** A class for scenario-based tests of V2Transport
10121020
*
10131021
* Each V2TransportTester encapsulates a V2Transport (the one being tested), and can be told to
10141022
* interact with it. To do so, it also encapsulates a BIP324Cipher to act as the other side. A
10151023
* second V2Transport is not used, as doing so would not permit scenarios that involve sending
1016-
* invalid data, or ones scenarios using BIP324 features that are not implemented on the sending
1024+
* invalid data, or ones using BIP324 features that are not implemented on the sending
10171025
* side (like decoy packets).
10181026
*/
10191027
class V2TransportTester
@@ -1031,6 +1039,7 @@ class V2TransportTester
10311039
/** Construct a tester object. test_initiator: whether the tested transport is initiator. */
10321040
V2TransportTester(bool test_initiator) :
10331041
m_transport(0, test_initiator, SER_NETWORK, INIT_PROTO_VERSION),
1042+
m_cipher{GenerateRandomTestKey(), MakeByteSpan(InsecureRand256())},
10341043
m_test_initiator(test_initiator) {}
10351044

10361045
/** Data type returned by Interact:

0 commit comments

Comments
 (0)