29
29
#include < scheduler.h>
30
30
#include < util/sock.h>
31
31
#include < util/strencodings.h>
32
+ #include < util/string.h>
32
33
#include < util/syscall_sandbox.h>
33
34
#include < util/system.h>
34
35
#include < util/thread.h>
@@ -65,6 +66,8 @@ static_assert (MAX_BLOCK_RELAY_ONLY_ANCHORS <= static_cast<size_t>(MAX_BLOCK_REL
65
66
/* * Anchor IP address database file name */
66
67
const char * const ANCHORS_DATABASE_FILENAME = " anchors.dat" ;
67
68
69
+ static constexpr uint64_t V2_MAX_CONTENTS_LENGTH = 0x01000000 - 1 ; // 2^24 - 1
70
+
68
71
// How often to dump addresses to peers.dat
69
72
static constexpr std::chrono::minutes DUMP_PEERS_INTERVAL{15 };
70
73
@@ -110,6 +113,8 @@ const std::string NET_MESSAGE_TYPE_OTHER = "*other*";
110
113
static const uint64_t RANDOMIZER_ID_NETGROUP = 0x6c0edd8036ef4036ULL ; // SHA256("netgroup")[0:8]
111
114
static const uint64_t RANDOMIZER_ID_LOCALHOSTNONCE = 0xd93e69e2bbfa5735ULL ; // SHA256("localhostnonce")[0:8]
112
115
static const uint64_t RANDOMIZER_ID_ADDRCACHE = 0x1cf2e4ddd306dda9ULL ; // SHA256("addrcache")[0:8]
116
+
117
+ static constexpr uint8_t V2_LONG_MSG_TYPE_LEN = 12 ; // V2 (BIP324) message type long ids
113
118
//
114
119
// Global state variables
115
120
//
@@ -685,7 +690,14 @@ bool CNode::ReceiveMsgBytes(Span<const uint8_t> msg_bytes, bool& complete)
685
690
if (m_deserializer->Complete ()) {
686
691
// decompose a transport agnostic CNetMessage from the deserializer
687
692
bool reject_message{false };
688
- CNetMessage msg = m_deserializer->GetMessage (time , reject_message);
693
+ bool disconnect{false };
694
+ CNetMessage msg = m_deserializer->GetMessage (time , reject_message, disconnect);
695
+
696
+ if (disconnect) {
697
+ // v2 p2p incorrect MAC tag. Disconnect from peer.
698
+ return false ;
699
+ }
700
+
689
701
if (reject_message) {
690
702
// Message deserialization failed. Drop the message but don't disconnect the peer.
691
703
// store the size of the corrupt message
@@ -777,10 +789,12 @@ const uint256& V1TransportDeserializer::GetMessageHash() const
777
789
return data_hash;
778
790
}
779
791
780
- CNetMessage V1TransportDeserializer::GetMessage (const std::chrono::microseconds time, bool & reject_message)
792
+ CNetMessage V1TransportDeserializer::GetMessage (const std::chrono::microseconds time, bool & reject_message, bool & disconnect )
781
793
{
782
794
// Initialize out parameter
783
795
reject_message = false ;
796
+ disconnect = false ;
797
+
784
798
// decompose a single CNetMessage from the TransportDeserializer
785
799
CNetMessage msg (std::move (vRecv));
786
800
@@ -802,6 +816,7 @@ CNetMessage V1TransportDeserializer::GetMessage(const std::chrono::microseconds
802
816
HexStr (Span{hash}.first (CMessageHeader::CHECKSUM_SIZE)),
803
817
HexStr (hdr.pchChecksum ),
804
818
m_node_id);
819
+ // TODO: Should we disconnect the v1 peer in this case?
805
820
reject_message = true ;
806
821
} else if (!hdr.IsCommandValid ()) {
807
822
LogPrint (BCLog::NET, " Header error: Invalid message type (%s, %u bytes), peer=%d\n " ,
@@ -814,7 +829,7 @@ CNetMessage V1TransportDeserializer::GetMessage(const std::chrono::microseconds
814
829
return msg;
815
830
}
816
831
817
- void V1TransportSerializer::prepareForTransport (CSerializedNetMsg& msg, std::vector<unsigned char >& header) const
832
+ bool V1TransportSerializer::prepareForTransport (CSerializedNetMsg& msg, std::vector<unsigned char >& header) const
818
833
{
819
834
// create dbl-sha256 checksum
820
835
uint256 hash = Hash (msg.data );
@@ -826,6 +841,179 @@ void V1TransportSerializer::prepareForTransport(CSerializedNetMsg& msg, std::vec
826
841
// serialize header
827
842
header.reserve (CMessageHeader::HEADER_SIZE);
828
843
CVectorWriter{SER_NETWORK, INIT_PROTO_VERSION, header, 0 , hdr};
844
+ return true ;
845
+ }
846
+
847
+ int V2TransportDeserializer::readHeader (Span<const uint8_t > pkt_bytes)
848
+ {
849
+ // copy data to temporary parsing buffer
850
+ const size_t remaining = BIP324_LENGTH_FIELD_LEN - m_hdr_pos;
851
+ const size_t copy_bytes = std::min<unsigned int >(remaining, pkt_bytes.size ());
852
+
853
+ memcpy (&vRecv[m_hdr_pos], pkt_bytes.data (), copy_bytes);
854
+ m_hdr_pos += copy_bytes;
855
+
856
+ // if we don't have the encrypted length yet, exit
857
+ if (m_hdr_pos < BIP324_LENGTH_FIELD_LEN) {
858
+ return copy_bytes;
859
+ }
860
+
861
+ // we have the 3 bytes encrypted packet length at this point
862
+ std::array<std::byte, BIP324_LENGTH_FIELD_LEN> encrypted_pkt_len;
863
+ memcpy (encrypted_pkt_len.data (), vRecv.data (), BIP324_LENGTH_FIELD_LEN);
864
+
865
+ // the encrypted packet data = bip324 header + contents (message type + message payload)
866
+ m_contents_size = m_cipher_suite->DecryptLength (encrypted_pkt_len);
867
+
868
+ // m_contents_size is the size of the p2p message
869
+ if (m_contents_size > V2_MAX_CONTENTS_LENGTH) {
870
+ return -1 ;
871
+ }
872
+
873
+ // switch state to reading message data
874
+ m_in_data = true ;
875
+
876
+ return copy_bytes;
877
+ }
878
+
879
+ int V2TransportDeserializer::readData (Span<const uint8_t > pkt_bytes)
880
+ {
881
+ // Read the BIP324 encrypted packet data.
882
+ const size_t remaining = BIP324_HEADER_LEN + m_contents_size + RFC8439_EXPANSION - m_data_pos;
883
+ const size_t copy_bytes = std::min<unsigned int >(remaining, pkt_bytes.size ());
884
+
885
+ // extend buffer, respect previous copied encrypted length
886
+ if (vRecv.size () < BIP324_LENGTH_FIELD_LEN + m_data_pos + copy_bytes) {
887
+ // Allocate up to 256 KiB ahead, but never more than the total message size.
888
+ vRecv.resize (BIP324_LENGTH_FIELD_LEN + std::min (BIP324_HEADER_LEN + m_contents_size, m_data_pos + copy_bytes + 256 * 1024 ) + RFC8439_EXPANSION, std::byte{0x00 });
889
+ }
890
+
891
+ memcpy (&vRecv[BIP324_LENGTH_FIELD_LEN + m_data_pos], pkt_bytes.data (), copy_bytes);
892
+ m_data_pos += copy_bytes;
893
+
894
+ return copy_bytes;
895
+ }
896
+
897
+ CNetMessage V2TransportDeserializer::GetMessage (const std::chrono::microseconds time, bool & reject_message, bool & disconnect)
898
+ {
899
+ const size_t min_contents_size = 1 ; // BIP324 1-byte message type id is the minimum contents
900
+
901
+ // Initialize out parameters
902
+ reject_message = (vRecv.size () < V2_MIN_PACKET_LENGTH + min_contents_size);
903
+ disconnect = false ;
904
+
905
+ // In v2, vRecv contains:
906
+ // 3 bytes of encrypted packet length
907
+ // 1-byte encrypted bip324 header
908
+ // variable length encrypted contents(message type and message payload) and
909
+ // mac tag
910
+ assert (Complete ());
911
+
912
+ std::string msg_type;
913
+
914
+ BIP324HeaderFlags flags;
915
+ size_t msg_type_size = 1 ; // at least one byte needed for message type
916
+ if (m_cipher_suite->Crypt ({},
917
+ Span{reinterpret_cast <const std::byte*>(vRecv.data () + BIP324_LENGTH_FIELD_LEN), BIP324_HEADER_LEN + m_contents_size + RFC8439_EXPANSION},
918
+ Span{reinterpret_cast <std::byte*>(vRecv.data ()), m_contents_size}, flags, false )) {
919
+ // MAC check was successful
920
+ vRecv.resize (m_contents_size);
921
+ reject_message = reject_message || (BIP324HeaderFlags (BIP324_IGNORE & flags) != BIP324_NONE);
922
+
923
+ if (!reject_message) {
924
+ uint8_t msg_type_id = NO_BIP324_SHORT_ID;
925
+ try {
926
+ vRecv >> msg_type_id;
927
+ } catch (const std::ios_base::failure&) {
928
+ LogPrint (BCLog::NET, " Failed to read message type, peer=%d\n " , m_node_id);
929
+ reject_message = true ;
930
+ }
931
+
932
+ if (msg_type_id == NO_BIP324_SHORT_ID) {
933
+ if (vRecv.size () < V2_LONG_MSG_TYPE_LEN) {
934
+ LogPrint (BCLog::NET, " Invalid v2 message type long id, peer=%d\n " , m_node_id);
935
+ reject_message = true ;
936
+ }
937
+ msg_type.resize (V2_LONG_MSG_TYPE_LEN);
938
+ vRecv.read (MakeWritableByteSpan (msg_type));
939
+ auto trim_pos = std::find (msg_type.begin (), msg_type.end (), 0 );
940
+ if (trim_pos != msg_type.end ()) {
941
+ msg_type.resize (trim_pos - msg_type.begin ());
942
+ }
943
+ msg_type_size += V2_LONG_MSG_TYPE_LEN;
944
+ } else {
945
+ auto mtype = GetMessageTypeFromShortID (msg_type_id);
946
+ if (mtype.has_value ()) {
947
+ msg_type = mtype.value ();
948
+ } else {
949
+ // unknown-short-id results in a valid but unknown message (will be skipped)
950
+ msg_type = " unknown-" + ToString (msg_type_id);
951
+ reject_message = true ;
952
+ }
953
+ }
954
+ }
955
+ } else {
956
+ // Invalid mac tag
957
+ LogPrint (BCLog::NET, " Invalid v2 mac tag, peer=%d\n " , m_node_id);
958
+ disconnect = true ;
959
+ reject_message = true ;
960
+ }
961
+
962
+ // we'll always return a CNetMessage (even if decryption fails)
963
+ // decompose a single CNetMessage from the TransportDeserializer
964
+ CNetMessage msg (std::move (vRecv));
965
+ msg.m_type = msg_type;
966
+ msg.m_time = time ;
967
+
968
+ if (!reject_message) {
969
+ msg.m_message_size = m_contents_size - msg_type_size;
970
+ msg.m_raw_message_size = V2_MIN_PACKET_LENGTH + m_contents_size; // raw wire size
971
+ }
972
+
973
+ Reset ();
974
+ return msg;
975
+ }
976
+
977
+ bool V2TransportSerializer::prepareForTransport (CSerializedNetMsg& msg, std::vector<unsigned char >& header) const
978
+ {
979
+ size_t serialized_msg_type_size = 1 ; // short-IDs are 1 byte
980
+ uint8_t short_msg_type = GetShortIDFromMessageType (msg.m_type );
981
+ if (short_msg_type == NO_BIP324_SHORT_ID) {
982
+ // message type without an assigned short-ID
983
+ assert (msg.m_type .size () <= V2_LONG_MSG_TYPE_LEN);
984
+ // encode as NO_BIP324_SHORT_ID || 12-byte long id
985
+ serialized_msg_type_size += V2_LONG_MSG_TYPE_LEN;
986
+ }
987
+
988
+ std::vector<unsigned char > msg_type_bytes (serialized_msg_type_size, 0 );
989
+
990
+ // append the short-ID or the varstr of the msg type
991
+ msg_type_bytes[0 ] = short_msg_type;
992
+ if (short_msg_type == NO_BIP324_SHORT_ID) {
993
+ // append ASCII command string
994
+ memcpy (msg_type_bytes.data () + 1 , msg.m_type .data (), msg.m_type .size ());
995
+ }
996
+
997
+ // insert message type directly into the CSerializedNetMsg data buffer (insert at begin)
998
+ // TODO: if we refactor the BIP324CipherSuite::Crypt() function to allow separate buffers for
999
+ // the message type and payload we could avoid a insert and thus a potential reallocation
1000
+ msg.data .insert (msg.data .begin (), msg_type_bytes.begin (), msg_type_bytes.end ());
1001
+
1002
+ auto contents_size = msg.data .size ();
1003
+ auto encrypted_pkt_size = V2_MIN_PACKET_LENGTH + contents_size;
1004
+ // resize the message buffer to make space for the MAC tag
1005
+ msg.data .resize (encrypted_pkt_size, 0 );
1006
+
1007
+ BIP324HeaderFlags flags{BIP324_NONE};
1008
+ // encrypt the payload, this should always succeed (controlled buffers, don't check the MAC during encrypting)
1009
+ auto success = m_cipher_suite->Crypt ({},
1010
+ Span{reinterpret_cast <const std::byte*>(msg.data .data ()), contents_size},
1011
+ Span{reinterpret_cast <std::byte*>(msg.data .data ()), encrypted_pkt_size},
1012
+ flags, true );
1013
+ if (!success) {
1014
+ LogPrint (BCLog::NET, " error in v2 p2p encryption for message type: %s\n " , msg.m_type );
1015
+ }
1016
+ return success;
829
1017
}
830
1018
831
1019
size_t CConnman::SocketSendData (CNode& node) const
@@ -2832,7 +3020,10 @@ void CConnman::PushMessage(CNode* pnode, CSerializedNetMsg&& msg)
2832
3020
2833
3021
// make sure we use the appropriate network transport format
2834
3022
std::vector<unsigned char > serializedHeader;
2835
- pnode->m_serializer ->prepareForTransport (msg, serializedHeader);
3023
+ if (!pnode->m_serializer ->prepareForTransport (msg, serializedHeader)) {
3024
+ return ;
3025
+ }
3026
+
2836
3027
size_t nTotalSize = nMessageSize + serializedHeader.size ();
2837
3028
2838
3029
size_t nBytesSent = 0 ;
0 commit comments