@@ -115,6 +115,7 @@ static const uint64_t RANDOMIZER_ID_LOCALHOSTNONCE = 0xd93e69e2bbfa5735ULL; // S
115
115
static const uint64_t RANDOMIZER_ID_ADDRCACHE = 0x1cf2e4ddd306dda9ULL ; // SHA256("addrcache")[0:8]
116
116
117
117
static constexpr uint8_t V2_MAX_MSG_TYPE_LEN = 12 ; // maximum length for V2 (BIP324) string message types
118
+ static constexpr size_t V2_MAX_GARBAGE_BYTES = 4095 ; // maximum length for V2 (BIP324) shapable handshake
118
119
//
119
120
// Global state variables
120
121
//
@@ -700,10 +701,16 @@ void CNode::InitV2P2P(const Span<const std::byte> their_ellswift, const Span<con
700
701
if (initiating) {
701
702
m_deserializer = std::make_unique<V2TransportDeserializer>(GetId (), v2_keys.responder_L , v2_keys.responder_P , v2_keys.rekey_salt );
702
703
m_serializer = std::make_unique<V2TransportSerializer>(v2_keys.initiator_L , v2_keys.initiator_P , v2_keys.rekey_salt );
704
+ v2_sent_garbage_terminator = v2_keys.initiator_garbage_terminator ;
705
+ v2_recv_garbage_terminator = v2_keys.responder_garbage_terminator ;
703
706
} else {
704
707
m_deserializer = std::make_unique<V2TransportDeserializer>(GetId (), v2_keys.initiator_L , v2_keys.initiator_P , v2_keys.rekey_salt );
705
708
m_serializer = std::make_unique<V2TransportSerializer>(v2_keys.responder_L , v2_keys.responder_P , v2_keys.rekey_salt );
709
+ v2_sent_garbage_terminator = v2_keys.responder_garbage_terminator ;
710
+ v2_recv_garbage_terminator = v2_keys.initiator_garbage_terminator ;
706
711
}
712
+ // Both peers must keep around a copy of the garbage terminator for the BIP324 shapable handshake
713
+ v2_keys_derived = true ;
707
714
}
708
715
709
716
void CNode::EnsureInitV2Key (bool initiating)
@@ -737,13 +744,24 @@ bool CNode::ReceiveMsgBytes(Span<const uint8_t> msg_bytes, bool& complete)
737
744
// decompose a transport agnostic CNetMessage from the deserializer
738
745
bool reject_message{false };
739
746
bool disconnect{false };
740
- CNetMessage msg = m_deserializer->GetMessage (time , reject_message, disconnect, {});
747
+
748
+ std::vector<std::byte> aad;
749
+ if (PreferV2Conn () && !m_authenticated_v2_garbage) {
750
+ std::copy (v2_garbage_bytes_recd.begin (), v2_garbage_bytes_recd.end (), std::back_inserter (aad));
751
+ }
752
+ CNetMessage msg = m_deserializer->GetMessage (time , reject_message, disconnect, aad);
741
753
742
754
if (disconnect) {
743
755
// v2 p2p incorrect MAC tag. Disconnect from peer.
744
756
return false ;
745
757
}
746
758
759
+ if (!aad.empty ()) {
760
+ // first message to authenticate garbage
761
+ m_authenticated_v2_garbage = true ;
762
+ memory_cleanse (v2_garbage_bytes_recd.data (), v2_garbage_bytes_recd.size ());
763
+ }
764
+
747
765
// inbound clients do not know whether the peer is trying to talk v1 or v2.
748
766
// if the first message is not VERSION, we reinterpret the bytes as v2 ellswift
749
767
if (!m_prefer_p2p_v2 && IsInboundConn () && nVersion == 0 && msg.m_type != NetMsgType::VERSION) {
@@ -785,7 +803,17 @@ int V1TransportDeserializer::readHeader(Span<const uint8_t> msg_bytes)
785
803
memcpy (&hdrbuf[nHdrPos], msg_bytes.data (), nCopy);
786
804
nHdrPos += nCopy;
787
805
788
- // if header incomplete, exit
806
+ if (validated_magic_len < CMessageHeader::MESSAGE_START_SIZE) {
807
+ auto available = std::min ((size_t )nHdrPos, CMessageHeader::MESSAGE_START_SIZE);
808
+ if (memcmp (hdrbuf.data (), m_chain_params.MessageStart (), available) != 0 ) {
809
+ LogPrint (BCLog::NET, " Header error: Wrong MessageStart %s received, peer=%d\n " , HexStr (hdrbuf), m_node_id);
810
+ return -1 ;
811
+ } else {
812
+ validated_magic_len = available;
813
+ }
814
+ }
815
+
816
+ // don't have complete header yet, exit
789
817
if (nHdrPos < CMessageHeader::HEADER_SIZE)
790
818
return nCopy;
791
819
@@ -798,12 +826,6 @@ int V1TransportDeserializer::readHeader(Span<const uint8_t> msg_bytes)
798
826
return -1 ;
799
827
}
800
828
801
- // Check start string, network magic
802
- if (memcmp (hdr.pchMessageStart , m_chain_params.MessageStart (), CMessageHeader::MESSAGE_START_SIZE) != 0 ) {
803
- LogPrint (BCLog::NET, " Header error: Wrong MessageStart %s received, peer=%d\n " , HexStr (hdr.pchMessageStart ), m_node_id);
804
- return -1 ;
805
- }
806
-
807
829
// reject messages larger than MAX_SIZE or MAX_PROTOCOL_MESSAGE_LENGTH
808
830
if (hdr.nMessageSize > MAX_SIZE || hdr.nMessageSize > MAX_PROTOCOL_MESSAGE_LENGTH) {
809
831
LogPrint (BCLog::NET, " Header error: Size too large (%s, %u bytes), peer=%d\n " , SanitizeString (hdr.GetCommand ()), hdr.nMessageSize , m_node_id);
@@ -979,9 +1001,14 @@ CNetMessage V2TransportDeserializer::GetMessage(const std::chrono::microseconds
979
1001
vRecv.resize (m_contents_size);
980
1002
reject_message = reject_message || (BIP324HeaderFlags (BIP324_IGNORE & flags) != BIP324_NONE);
981
1003
982
- // The first message we receive is the BIP324 transport version placeholder message.
983
- // Discard it for v2.0 clients.
984
- if (!m_processed_version_placeholder) {
1004
+ if (!aad.empty ()) {
1005
+ // This only happens for the first encrypted message received by an inbound client
1006
+ // which is meant to authenticate the garbage bytes for the BIP324 shapable handshake
1007
+ // That message is not to be passed to the p2p application layer.
1008
+ reject_message = true ;
1009
+ } else if (!m_processed_version_placeholder) {
1010
+ // BIP324 transport version placeholder message.
1011
+ // Discard it for v2.0 clients.
985
1012
reject_message = true ;
986
1013
m_processed_version_placeholder = true ;
987
1014
}
@@ -1568,43 +1595,111 @@ void CConnman::SocketHandlerConnected(const std::vector<CNode*>& nodes,
1568
1595
}
1569
1596
nBytes = pnode->m_sock ->Recv (pchBuf, sizeof (pchBuf), MSG_DONTWAIT);
1570
1597
}
1598
+ uint8_t * ptr = pchBuf;
1571
1599
if (nBytes > 0 ) {
1572
1600
bool notify = false ;
1573
1601
size_t num_bytes = (size_t )nBytes;
1574
- if (!pnode->ReceiveMsgBytes ({pchBuf, num_bytes}, notify)) {
1602
+
1603
+ // for a v2 outbound peer:
1604
+ // prior to InitV2P2P(), pnode->m_deserializer is not init and ReceiveMsgBytes() will return false
1605
+ // for a v2 inbound peer:
1606
+ // prior to InitV2P2P(), pnode->m_deserializer is init to V1TransportDeserializer, ReceiveMsgBytes will fail due to a malformed header according to the v1 protocol
1607
+ // for all v2 peers:
1608
+ // after InitV2P2P(), keys are derived, but m_deserializer would simply disconnect on MAC failure until
1609
+ // the key exchage phase ends with the garbage terminator
1610
+ // after garbage terminator, ReceiveMsgBytes() should work on valid v2 encrypted packets
1611
+ if ((pnode->v2_keys_derived && !pnode->v2_garbage_terminated ) ||
1612
+ !pnode->ReceiveMsgBytes ({ptr, num_bytes}, notify)) {
1575
1613
// when we cannot understand the received bytes, disconnect if:
1576
1614
// 1. we don't support BIP324 v2, or
1577
1615
// 2. v2 key exchange is complete, we should understand the bytes, or
1578
1616
// 3. we've previously received a v1 (or v2) VERSION message from the peer
1579
- // 4. the received bytes are not exactly the size of an ellswift encoding
1580
1617
if (!gArgs .GetBoolArg (" -v2transport" , DEFAULT_V2_TRANSPORT) ||
1581
1618
pnode->v2_key_exchange_complete ||
1582
- pnode->nVersion != 0 ||
1583
- num_bytes < ELLSWIFT_ENCODED_SIZE) {
1619
+ pnode->nVersion != 0 ) {
1584
1620
pnode->CloseSocketDisconnect ();
1585
1621
} else {
1586
1622
pnode->EnsureInitV2Key (!pnode->IsInboundConn ());
1587
1623
1588
- pnode->InitV2P2P ({AsBytePtr (pchBuf), ELLSWIFT_ENCODED_SIZE}, MakeByteSpan (pnode->ellswift_pubkey ), !pnode->IsInboundConn ());
1589
- if (pnode->IsInboundConn ()) {
1590
- PushV2EllSwiftPubkey (pnode);
1624
+ if (!pnode->v2_keys_derived ) {
1625
+ // If we're the inbound peer, upon receiving any ellswift bytes we send our ellswift key
1626
+ if (pnode->IsInboundConn () && pnode->peer_ellswift_buf .empty ()) {
1627
+ PushV2EllSwiftPubkey (pnode);
1628
+ // We now know the peer prefers a BIP324 v2 connection
1629
+ pnode->m_prefer_p2p_v2 = true ;
1630
+ }
1631
+
1632
+ // Keys are not derived because we don't have the peer ellswift yet, keep buffering.
1633
+ auto old_ellswift_sz = pnode->peer_ellswift_buf .size ();
1634
+ auto more_ellswift_bytes = std::min (ELLSWIFT_ENCODED_SIZE - old_ellswift_sz, num_bytes);
1635
+ pnode->peer_ellswift_buf .resize (old_ellswift_sz + more_ellswift_bytes);
1636
+ memcpy (pnode->peer_ellswift_buf .data () + old_ellswift_sz, ptr, more_ellswift_bytes);
1637
+
1638
+ ptr += more_ellswift_bytes;
1639
+ num_bytes -= more_ellswift_bytes;
1640
+
1641
+ if (pnode->peer_ellswift_buf .size () < ELLSWIFT_ENCODED_SIZE)
1642
+ continue ;
1643
+
1644
+ // At this point we have the entire peer ellswift, we can derive keys
1645
+ // and instantiate the v2 encryption session
1646
+ pnode->InitV2P2P (pnode->peer_ellswift_buf , MakeByteSpan (pnode->ellswift_pubkey ), !pnode->IsInboundConn ());
1647
+
1648
+ // After keys are exchanged, both peers send the garbage terminator
1649
+ // followed by the v2 encrypted message that authenticates the garbage
1650
+ // which is currently empty as the mechanism is unused in bitcoin core.
1651
+ PushV2GarbageTerminator (pnode);
1652
+ CSerializedNetMsg garbage_auth_msg;
1653
+ PushMessage (pnode, std::move (garbage_auth_msg));
1654
+ // Send empty message again for transport version placeholder
1655
+ CSerializedNetMsg transport_version_msg;
1656
+ PushMessage (pnode, std::move (transport_version_msg));
1591
1657
}
1592
1658
1593
- // Send empty message for transport version placeholder
1594
- CSerializedNetMsg msg;
1595
- PushMessage (pnode, std::move (msg));
1596
-
1597
- if (!pnode->IsInboundConn ()) {
1598
- // Outbound peer has completed ECDH and can start the P2P protocol
1599
- m_msgproc->InitP2P (*pnode, nLocalServices);
1659
+ if (pnode->v2_keys_derived && !pnode->v2_garbage_terminated && num_bytes > 0 ) {
1660
+ // Keep buffering bytes until the garbage terminator
1661
+ auto old_size = pnode->v2_garbage_bytes_recd .size ();
1662
+ auto new_size = old_size + num_bytes;
1663
+
1664
+ // Might be better to just allocate to V2_MAX_GARBAGE_BYTES at start once the mechanism
1665
+ // is actually used
1666
+ pnode->v2_garbage_bytes_recd .resize (std::min (new_size, V2_MAX_GARBAGE_BYTES));
1667
+ memcpy (pnode->v2_garbage_bytes_recd .data () + old_size, ptr, (new_size - old_size));
1668
+ auto it = std::search (pnode->v2_garbage_bytes_recd .begin (), pnode->v2_garbage_bytes_recd .end (),
1669
+ pnode->v2_recv_garbage_terminator .begin (), pnode->v2_recv_garbage_terminator .end ());
1670
+
1671
+ if (it != pnode->v2_garbage_bytes_recd .end ()) {
1672
+ // Found the terminator...
1673
+ auto garbage_size = it - pnode->v2_garbage_bytes_recd .begin ();
1674
+ pnode->v2_garbage_bytes_recd .erase (it, pnode->v2_garbage_bytes_recd .end ());
1675
+ if (garbage_size <= long {V2_MAX_GARBAGE_BYTES}) {
1676
+ // ...in less than the maximum allowed bytes
1677
+ auto fwd = garbage_size + pnode->v2_recv_garbage_terminator .size () - old_size;
1678
+ ptr += fwd;
1679
+ num_bytes -= fwd;
1680
+ pnode->v2_garbage_terminated = true ;
1681
+ } else {
1682
+ // ..but more than the maximum allowed bytes were used
1683
+ pnode->CloseSocketDisconnect ();
1684
+ }
1685
+ memory_cleanse (pnode->v2_recv_garbage_terminator .data (), pnode->v2_recv_garbage_terminator .size ());
1686
+ } else if (pnode->v2_garbage_bytes_recd .size () >= V2_MAX_GARBAGE_BYTES) {
1687
+ pnode->CloseSocketDisconnect ();
1688
+ memory_cleanse (pnode->v2_recv_garbage_terminator .data (), pnode->v2_recv_garbage_terminator .size ());
1689
+ }
1600
1690
}
1601
1691
1602
- pnode->v2_key_exchange_complete = true ;
1603
- if (num_bytes > ELLSWIFT_ENCODED_SIZE &&
1604
- !pnode->ReceiveMsgBytes (
1605
- {pchBuf + ELLSWIFT_ENCODED_SIZE, (size_t )(nBytes - ELLSWIFT_ENCODED_SIZE)},
1606
- notify)) {
1607
- pnode->CloseSocketDisconnect ();
1692
+ // after a successful ECDH and shapable handshake garbage termination, process the remaining bytes.
1693
+ if (pnode->v2_garbage_terminated ) {
1694
+ if (num_bytes > 0 && !pnode->ReceiveMsgBytes ({ptr, num_bytes}, notify)) {
1695
+ pnode->CloseSocketDisconnect ();
1696
+ } else {
1697
+ pnode->v2_key_exchange_complete = true ;
1698
+ if (!pnode->IsInboundConn ()) {
1699
+ // Outbound peer has completed key exchange and can start the P2P protocol
1700
+ m_msgproc->InitP2P (*pnode, nLocalServices);
1701
+ }
1702
+ }
1608
1703
}
1609
1704
}
1610
1705
}
@@ -3173,6 +3268,21 @@ void CConnman::PushV2EllSwiftPubkey(CNode* pnode)
3173
3268
if (nBytesSent) RecordBytesSent (nBytesSent);
3174
3269
}
3175
3270
3271
+ void CConnman::PushV2GarbageTerminator (CNode* pnode)
3272
+ {
3273
+ std::vector<unsigned char > terminator_uchars;
3274
+ terminator_uchars.resize (pnode->v2_sent_garbage_terminator .size ());
3275
+ memcpy (terminator_uchars.data (), pnode->v2_sent_garbage_terminator .data (), terminator_uchars.size ());
3276
+ {
3277
+ LOCK (pnode->cs_vSend );
3278
+ pnode->nSendSize += pnode->v2_sent_garbage_terminator .size ();
3279
+
3280
+ // We do not have to send immediately because this is followed shortly by the
3281
+ // transport version message
3282
+ pnode->vSendMsg .push_back (terminator_uchars);
3283
+ }
3284
+ }
3285
+
3176
3286
bool CConnman::ForNode (NodeId id, std::function<bool (CNode* pnode)> func)
3177
3287
{
3178
3288
CNode* found = nullptr ;
0 commit comments