Skip to content

Commit d18a8f6

Browse files
committed
Merge bitcoin/bitcoin#28525: net: Drop v2 garbage authentication packet
e3720bc net: Simplify v2 recv logic by decoupling AAD from state machine (Tim Ruffing) b0f5175 net: Drop v2 garbage authentication packet (Tim Ruffing) Pull request description: Note that this is a breaking change, see also bitcoin/bips#1498 The benefit is a simpler implementation: - The protocol state machine does not need separate states for garbage authentication and version phases. - The special case of "ignoring the ignore bit" is removed. - The freedom to choose the contents of the garbage authentication packet is removed. This simplifies testing. ACKs for top commit: naumenkogs: ACK e3720bc sipa: ACK e3720bc. Re-ran the v2 transport fuzzer overnight. ajtowns: ACK e3720bc - simpler and more flexible, nice achow101: ACK e3720bc Sjors: utACK e3720bc theStack: Code-review ACK e3720bc Tree-SHA512: 16600ed868c8a346828de075c4072e37cf86440751d08ab099fe8b58ff71d8b371a90397d6a4247096796db68794275e7e0403f218859567d04838e0411dadd6
2 parents 9d5150a + e3720bc commit d18a8f6

File tree

3 files changed

+69
-103
lines changed

3 files changed

+69
-103
lines changed

src/net.cpp

Lines changed: 24 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1002,8 +1002,7 @@ void V2Transport::StartSendingHandshake() noexcept
10021002
m_send_buffer.resize(EllSwiftPubKey::size() + m_send_garbage.size());
10031003
std::copy(std::begin(m_cipher.GetOurPubKey()), std::end(m_cipher.GetOurPubKey()), MakeWritableByteSpan(m_send_buffer).begin());
10041004
std::copy(m_send_garbage.begin(), m_send_garbage.end(), m_send_buffer.begin() + EllSwiftPubKey::size());
1005-
// We cannot wipe m_send_garbage as it will still be used to construct the garbage
1006-
// authentication packet.
1005+
// We cannot wipe m_send_garbage as it will still be used as AAD later in the handshake.
10071006
}
10081007

10091008
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 :
@@ -1037,9 +1036,6 @@ void V2Transport::SetReceiveState(RecvState recv_state) noexcept
10371036
Assume(recv_state == RecvState::GARB_GARBTERM);
10381037
break;
10391038
case RecvState::GARB_GARBTERM:
1040-
Assume(recv_state == RecvState::GARBAUTH);
1041-
break;
1042-
case RecvState::GARBAUTH:
10431039
Assume(recv_state == RecvState::VERSION);
10441040
break;
10451041
case RecvState::VERSION:
@@ -1171,24 +1167,15 @@ bool V2Transport::ProcessReceivedKeyBytes() noexcept
11711167
m_cipher.GetSendGarbageTerminator().end(),
11721168
MakeWritableByteSpan(m_send_buffer).last(BIP324Cipher::GARBAGE_TERMINATOR_LEN).begin());
11731169

1174-
// Construct garbage authentication packet in the send buffer (using the garbage data which
1175-
// is still there).
1176-
m_send_buffer.resize(m_send_buffer.size() + BIP324Cipher::EXPANSION);
1177-
m_cipher.Encrypt(
1178-
/*contents=*/{},
1179-
/*aad=*/MakeByteSpan(m_send_garbage),
1180-
/*ignore=*/false,
1181-
/*output=*/MakeWritableByteSpan(m_send_buffer).last(BIP324Cipher::EXPANSION));
1182-
// We no longer need the garbage.
1183-
ClearShrink(m_send_garbage);
1184-
1185-
// Construct version packet in the send buffer.
1170+
// Construct version packet in the send buffer, with the sent garbage data as AAD.
11861171
m_send_buffer.resize(m_send_buffer.size() + BIP324Cipher::EXPANSION + VERSION_CONTENTS.size());
11871172
m_cipher.Encrypt(
11881173
/*contents=*/VERSION_CONTENTS,
1189-
/*aad=*/{},
1174+
/*aad=*/MakeByteSpan(m_send_garbage),
11901175
/*ignore=*/false,
11911176
/*output=*/MakeWritableByteSpan(m_send_buffer).last(BIP324Cipher::EXPANSION + VERSION_CONTENTS.size()));
1177+
// We no longer need the garbage.
1178+
ClearShrink(m_send_garbage);
11921179
} else {
11931180
// We still have to receive more key bytes.
11941181
}
@@ -1202,11 +1189,11 @@ bool V2Transport::ProcessReceivedGarbageBytes() noexcept
12021189
Assume(m_recv_buffer.size() <= MAX_GARBAGE_LEN + BIP324Cipher::GARBAGE_TERMINATOR_LEN);
12031190
if (m_recv_buffer.size() >= BIP324Cipher::GARBAGE_TERMINATOR_LEN) {
12041191
if (MakeByteSpan(m_recv_buffer).last(BIP324Cipher::GARBAGE_TERMINATOR_LEN) == m_cipher.GetReceiveGarbageTerminator()) {
1205-
// Garbage terminator received. Switch to receiving garbage authentication packet.
1206-
m_recv_garbage = std::move(m_recv_buffer);
1207-
m_recv_garbage.resize(m_recv_garbage.size() - BIP324Cipher::GARBAGE_TERMINATOR_LEN);
1192+
// Garbage terminator received. Store garbage to authenticate it as AAD later.
1193+
m_recv_aad = std::move(m_recv_buffer);
1194+
m_recv_aad.resize(m_recv_aad.size() - BIP324Cipher::GARBAGE_TERMINATOR_LEN);
12081195
m_recv_buffer.clear();
1209-
SetReceiveState(RecvState::GARBAUTH);
1196+
SetReceiveState(RecvState::VERSION);
12101197
} else if (m_recv_buffer.size() == MAX_GARBAGE_LEN + BIP324Cipher::GARBAGE_TERMINATOR_LEN) {
12111198
// We've reached the maximum length for garbage + garbage terminator, and the
12121199
// terminator still does not match. Abort.
@@ -1225,8 +1212,7 @@ bool V2Transport::ProcessReceivedGarbageBytes() noexcept
12251212
bool V2Transport::ProcessReceivedPacketBytes() noexcept
12261213
{
12271214
AssertLockHeld(m_recv_mutex);
1228-
Assume(m_recv_state == RecvState::GARBAUTH || m_recv_state == RecvState::VERSION ||
1229-
m_recv_state == RecvState::APP);
1215+
Assume(m_recv_state == RecvState::VERSION || m_recv_state == RecvState::APP);
12301216

12311217
// The maximum permitted contents length for a packet, consisting of:
12321218
// - 0x00 byte: indicating long message type encoding
@@ -1249,45 +1235,37 @@ bool V2Transport::ProcessReceivedPacketBytes() noexcept
12491235
// as GetMaxBytesToProcess only allows up to LENGTH_LEN into the buffer before that point.
12501236
m_recv_decode_buffer.resize(m_recv_len);
12511237
bool ignore{false};
1252-
Span<const std::byte> aad;
1253-
if (m_recv_state == RecvState::GARBAUTH) aad = MakeByteSpan(m_recv_garbage);
12541238
bool ret = m_cipher.Decrypt(
12551239
/*input=*/MakeByteSpan(m_recv_buffer).subspan(BIP324Cipher::LENGTH_LEN),
1256-
/*aad=*/aad,
1240+
/*aad=*/MakeByteSpan(m_recv_aad),
12571241
/*ignore=*/ignore,
12581242
/*contents=*/MakeWritableByteSpan(m_recv_decode_buffer));
12591243
if (!ret) {
12601244
LogPrint(BCLog::NET, "V2 transport error: packet decryption failure (%u bytes), peer=%d\n", m_recv_len, m_nodeid);
12611245
return false;
12621246
}
1247+
// We have decrypted a valid packet with the AAD we expected, so clear the expected AAD.
1248+
ClearShrink(m_recv_aad);
12631249
// Feed the last 4 bytes of the Poly1305 authentication tag (and its timing) into our RNG.
12641250
RandAddEvent(ReadLE32(m_recv_buffer.data() + m_recv_buffer.size() - 4));
12651251

1266-
// At this point we have a valid packet decrypted into m_recv_decode_buffer. Depending on
1267-
// the current state, decide what to do with it.
1268-
switch (m_recv_state) {
1269-
case RecvState::GARBAUTH:
1270-
// Ignore flag does not matter for garbage authentication. Any valid packet functions
1271-
// as authentication. Receive and process the version packet next.
1272-
SetReceiveState(RecvState::VERSION);
1273-
ClearShrink(m_recv_garbage);
1274-
break;
1275-
case RecvState::VERSION:
1276-
if (!ignore) {
1252+
// At this point we have a valid packet decrypted into m_recv_decode_buffer. If it's not a
1253+
// decoy, which we simply ignore, use the current state to decide what to do with it.
1254+
if (!ignore) {
1255+
switch (m_recv_state) {
1256+
case RecvState::VERSION:
12771257
// Version message received; transition to application phase. The contents is
12781258
// ignored, but can be used for future extensions.
12791259
SetReceiveState(RecvState::APP);
1280-
}
1281-
break;
1282-
case RecvState::APP:
1283-
if (!ignore) {
1260+
break;
1261+
case RecvState::APP:
12841262
// Application message decrypted correctly. It can be extracted using GetMessage().
12851263
SetReceiveState(RecvState::APP_READY);
1264+
break;
1265+
default:
1266+
// Any other state is invalid (this function should not have been called).
1267+
Assume(false);
12861268
}
1287-
break;
1288-
default:
1289-
// Any other state is invalid (this function should not have been called).
1290-
Assume(false);
12911269
}
12921270
// Wipe the receive buffer where the next packet will be received into.
12931271
ClearShrink(m_recv_buffer);
@@ -1323,7 +1301,6 @@ size_t V2Transport::GetMaxBytesToProcess() noexcept
13231301
case RecvState::GARB_GARBTERM:
13241302
// Process garbage bytes one by one (because terminator may appear anywhere).
13251303
return 1;
1326-
case RecvState::GARBAUTH:
13271304
case RecvState::VERSION:
13281305
case RecvState::APP:
13291306
// These three states all involve decoding a packet. Process the length descriptor first,
@@ -1377,7 +1354,6 @@ bool V2Transport::ReceivedBytes(Span<const uint8_t>& msg_bytes) noexcept
13771354
// bytes).
13781355
m_recv_buffer.reserve(MAX_GARBAGE_LEN + BIP324Cipher::GARBAGE_TERMINATOR_LEN);
13791356
break;
1380-
case RecvState::GARBAUTH:
13811357
case RecvState::VERSION:
13821358
case RecvState::APP: {
13831359
// During states where a packet is being received, as much as is expected but never
@@ -1421,7 +1397,6 @@ bool V2Transport::ReceivedBytes(Span<const uint8_t>& msg_bytes) noexcept
14211397
if (!ProcessReceivedGarbageBytes()) return false;
14221398
break;
14231399

1424-
case RecvState::GARBAUTH:
14251400
case RecvState::VERSION:
14261401
case RecvState::APP:
14271402
if (!ProcessReceivedPacketBytes()) return false;

src/net.h

Lines changed: 18 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -460,10 +460,10 @@ class V2Transport final : public Transport
460460
*
461461
* start(responder)
462462
* |
463-
* | start(initiator) /---------\
464-
* | | | |
465-
* v v v |
466-
* KEY_MAYBE_V1 -> KEY -> GARB_GARBTERM -> GARBAUTH -> VERSION -> APP -> APP_READY
463+
* | start(initiator) /---------\
464+
* | | | |
465+
* v v v |
466+
* KEY_MAYBE_V1 -> KEY -> GARB_GARBTERM -> VERSION -> APP -> APP_READY
467467
* |
468468
* \-------> V1
469469
*/
@@ -485,24 +485,19 @@ class V2Transport final : public Transport
485485
/** Garbage and garbage terminator.
486486
*
487487
* Whenever a byte is received, the last 16 bytes are compared with the expected garbage
488-
* terminator. When that happens, the state becomes GARBAUTH. If no matching terminator is
488+
* terminator. When that happens, the state becomes VERSION. If no matching terminator is
489489
* received in 4111 bytes (4095 for the maximum garbage length, and 16 bytes for the
490490
* terminator), the connection aborts. */
491491
GARB_GARBTERM,
492492

493-
/** Garbage authentication packet.
494-
*
495-
* A packet is received, and decrypted/verified with AAD set to the garbage received during
496-
* the GARB_GARBTERM state. If that succeeds, the state becomes VERSION. If it fails the
497-
* connection aborts. */
498-
GARBAUTH,
499-
500493
/** Version packet.
501494
*
502-
* A packet is received, and decrypted/verified. If that succeeds, the state becomes APP,
503-
* and the decrypted contents is interpreted as version negotiation (currently, that means
504-
* ignoring it, but it can be used for negotiating future extensions). If it fails, the
505-
* connection aborts. */
495+
* A packet is received, and decrypted/verified. If that fails, the connection aborts. The
496+
* first received packet in this state (whether it's a decoy or not) is expected to
497+
* authenticate the garbage received during the GARB_GARBTERM state as associated
498+
* authenticated data (AAD). The first non-decoy packet in this state is interpreted as
499+
* version negotiation (currently, that means ignoring the contents, but it can be used for
500+
* negotiating future extensions), and afterwards the state becomes APP. */
506501
VERSION,
507502

508503
/** Application packet.
@@ -556,9 +551,9 @@ class V2Transport final : public Transport
556551
/** Normal sending state.
557552
*
558553
* In this state, the ciphers are initialized, so packets can be sent. When this state is
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. */
554+
* entered, the garbage terminator and version packet are appended to the send buffer (in
555+
* addition to the key and garbage which may still be there). In this state a message can be
556+
* provided if the send buffer is empty. */
562557
READY,
563558

564559
/** This transport is using v1 fallback.
@@ -578,13 +573,13 @@ class V2Transport final : public Transport
578573

579574
/** Lock for receiver-side fields. */
580575
mutable Mutex m_recv_mutex ACQUIRED_BEFORE(m_send_mutex);
581-
/** In {GARBAUTH, VERSION, APP}, the decrypted packet length, if m_recv_buffer.size() >=
576+
/** In {VERSION, APP}, the decrypted packet length, if m_recv_buffer.size() >=
582577
* BIP324Cipher::LENGTH_LEN. Unspecified otherwise. */
583578
uint32_t m_recv_len GUARDED_BY(m_recv_mutex) {0};
584579
/** Receive buffer; meaning is determined by m_recv_state. */
585580
std::vector<uint8_t> m_recv_buffer GUARDED_BY(m_recv_mutex);
586-
/** During GARBAUTH, the garbage received during GARB_GARBTERM. */
587-
std::vector<uint8_t> m_recv_garbage GUARDED_BY(m_recv_mutex);
581+
/** AAD expected in next received packet (currently used only for garbage). */
582+
std::vector<uint8_t> m_recv_aad GUARDED_BY(m_recv_mutex);
588583
/** Buffer to put decrypted contents in, for converting to CNetMessage. */
589584
std::vector<uint8_t> m_recv_decode_buffer GUARDED_BY(m_recv_mutex);
590585
/** Deserialization type. */
@@ -624,7 +619,7 @@ class V2Transport final : public Transport
624619
bool ProcessReceivedKeyBytes() noexcept EXCLUSIVE_LOCKS_REQUIRED(m_recv_mutex, !m_send_mutex);
625620
/** Process bytes in m_recv_buffer, while in GARB_GARBTERM state. */
626621
bool ProcessReceivedGarbageBytes() noexcept EXCLUSIVE_LOCKS_REQUIRED(m_recv_mutex);
627-
/** Process bytes in m_recv_buffer, while in GARBAUTH/VERSION/APP state. */
622+
/** Process bytes in m_recv_buffer, while in VERSION/APP state. */
628623
bool ProcessReceivedPacketBytes() noexcept EXCLUSIVE_LOCKS_REQUIRED(m_recv_mutex);
629624

630625
public:

0 commit comments

Comments
 (0)