Skip to content

Commit 94db966

Browse files
committed
net: use generic network key for addrcache
The generic key can also be used in other places where behavior between different network identities should be uncorrelated to avoid fingerprinting. This also changes RANDOMIZER_ID - since it is not being persisted to disk, there are no compatibility issues.
1 parent 1444ed8 commit 94db966

File tree

7 files changed

+61
-26
lines changed

7 files changed

+61
-26
lines changed

src/net.cpp

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ const std::string NET_MESSAGE_TYPE_OTHER = "*other*";
108108

109109
static const uint64_t RANDOMIZER_ID_NETGROUP = 0x6c0edd8036ef4036ULL; // SHA256("netgroup")[0:8]
110110
static const uint64_t RANDOMIZER_ID_LOCALHOSTNONCE = 0xd93e69e2bbfa5735ULL; // SHA256("localhostnonce")[0:8]
111-
static const uint64_t RANDOMIZER_ID_ADDRCACHE = 0x1cf2e4ddd306dda9ULL; // SHA256("addrcache")[0:8]
111+
static const uint64_t RANDOMIZER_ID_NETWORKKEY = 0x0e8a2b136c592a7dULL; // SHA256("networkkey")[0:8]
112112
//
113113
// Global state variables
114114
//
@@ -530,6 +530,13 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
530530
if (!addr_bind.IsValid()) {
531531
addr_bind = GetBindAddress(*sock);
532532
}
533+
uint64_t network_id = GetDeterministicRandomizer(RANDOMIZER_ID_NETWORKKEY)
534+
.Write(target_addr.GetNetClass())
535+
.Write(addr_bind.GetAddrBytes())
536+
// For outbound connections, the port of the bound address is randomly
537+
// assigned by the OS and would therefore not be useful for seeding.
538+
.Write(0)
539+
.Finalize();
533540
CNode* pnode = new CNode(id,
534541
std::move(sock),
535542
target_addr,
@@ -539,6 +546,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
539546
pszDest ? pszDest : "",
540547
conn_type,
541548
/*inbound_onion=*/false,
549+
network_id,
542550
CNodeOptions{
543551
.permission_flags = permission_flags,
544552
.i2p_sam_session = std::move(i2p_transient_session),
@@ -1832,6 +1840,11 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
18321840
ServiceFlags local_services = GetLocalServices();
18331841
const bool use_v2transport(local_services & NODE_P2P_V2);
18341842

1843+
uint64_t network_id = GetDeterministicRandomizer(RANDOMIZER_ID_NETWORKKEY)
1844+
.Write(inbound_onion ? NET_ONION : addr.GetNetClass())
1845+
.Write(addr_bind.GetAddrBytes())
1846+
.Write(addr_bind.GetPort()) // inbound connections use bind port
1847+
.Finalize();
18351848
CNode* pnode = new CNode(id,
18361849
std::move(sock),
18371850
CAddress{addr, NODE_NONE},
@@ -1841,6 +1854,7 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
18411854
/*addrNameIn=*/"",
18421855
ConnectionType::INBOUND,
18431856
inbound_onion,
1857+
network_id,
18441858
CNodeOptions{
18451859
.permission_flags = permission_flags,
18461860
.prefer_evict = discouraged,
@@ -3519,15 +3533,9 @@ std::vector<CAddress> CConnman::GetAddressesUnsafe(size_t max_addresses, size_t
35193533
std::vector<CAddress> CConnman::GetAddresses(CNode& requestor, size_t max_addresses, size_t max_pct)
35203534
{
35213535
auto local_socket_bytes = requestor.addrBind.GetAddrBytes();
3522-
uint64_t cache_id = GetDeterministicRandomizer(RANDOMIZER_ID_ADDRCACHE)
3523-
.Write(requestor.ConnectedThroughNetwork())
3524-
.Write(local_socket_bytes)
3525-
// For outbound connections, the port of the bound address is randomly
3526-
// assigned by the OS and would therefore not be useful for seeding.
3527-
.Write(requestor.IsInboundConn() ? requestor.addrBind.GetPort() : 0)
3528-
.Finalize();
3536+
uint64_t network_id = requestor.m_network_key;
35293537
const auto current_time = GetTime<std::chrono::microseconds>();
3530-
auto r = m_addr_response_caches.emplace(cache_id, CachedAddrResponse{});
3538+
auto r = m_addr_response_caches.emplace(network_id, CachedAddrResponse{});
35313539
CachedAddrResponse& cache_entry = r.first->second;
35323540
if (cache_entry.m_cache_entry_expiration < current_time) { // If emplace() added new one it has expiration 0.
35333541
cache_entry.m_addrs_response_cache = GetAddressesUnsafe(max_addresses, max_pct, /*network=*/std::nullopt);
@@ -3804,6 +3812,7 @@ CNode::CNode(NodeId idIn,
38043812
const std::string& addrNameIn,
38053813
ConnectionType conn_type_in,
38063814
bool inbound_onion,
3815+
uint64_t network_key,
38073816
CNodeOptions&& node_opts)
38083817
: m_transport{MakeTransport(idIn, node_opts.use_v2transport, conn_type_in == ConnectionType::INBOUND)},
38093818
m_permission_flags{node_opts.permission_flags},
@@ -3816,6 +3825,7 @@ CNode::CNode(NodeId idIn,
38163825
m_inbound_onion{inbound_onion},
38173826
m_prefer_evict{node_opts.prefer_evict},
38183827
nKeyedNetGroup{nKeyedNetGroupIn},
3828+
m_network_key{network_key},
38193829
m_conn_type{conn_type_in},
38203830
id{idIn},
38213831
nLocalHostNonce{nLocalHostNonceIn},

src/net.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -738,6 +738,10 @@ class CNode
738738
std::atomic_bool fPauseRecv{false};
739739
std::atomic_bool fPauseSend{false};
740740

741+
/** Network key used to prevent fingerprinting our node across networks.
742+
* Influenced by the network and the bind address (+ bind port for inbounds) */
743+
const uint64_t m_network_key;
744+
741745
const ConnectionType m_conn_type;
742746

743747
/** Move all messages from the received queue to the processing queue. */
@@ -889,6 +893,7 @@ class CNode
889893
const std::string& addrNameIn,
890894
ConnectionType conn_type_in,
891895
bool inbound_onion,
896+
uint64_t network_key,
892897
CNodeOptions&& node_opts = {});
893898
CNode(const CNode&) = delete;
894899
CNode& operator=(const CNode&) = delete;

src/test/denialofservice_tests.cpp

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
6262
CAddress(),
6363
/*addrNameIn=*/"",
6464
ConnectionType::OUTBOUND_FULL_RELAY,
65-
/*inbound_onion=*/false};
65+
/*inbound_onion=*/false,
66+
/*network_key=*/0};
6667

6768
connman.Handshake(
6869
/*node=*/dummyNode1,
@@ -128,7 +129,8 @@ void AddRandomOutboundPeer(NodeId& id, std::vector<CNode*>& vNodes, PeerManager&
128129
CAddress(),
129130
/*addrNameIn=*/"",
130131
connType,
131-
/*inbound_onion=*/false});
132+
/*inbound_onion=*/false,
133+
/*network_key=*/0});
132134
CNode &node = *vNodes.back();
133135
node.SetCommonVersion(PROTOCOL_VERSION);
134136

@@ -327,7 +329,8 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
327329
CAddress(),
328330
/*addrNameIn=*/"",
329331
ConnectionType::INBOUND,
330-
/*inbound_onion=*/false};
332+
/*inbound_onion=*/false,
333+
/*network_key=*/1};
331334
nodes[0]->SetCommonVersion(PROTOCOL_VERSION);
332335
peerLogic->InitializeNode(*nodes[0], NODE_NETWORK);
333336
nodes[0]->fSuccessfullyConnected = true;
@@ -347,7 +350,8 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
347350
CAddress(),
348351
/*addrNameIn=*/"",
349352
ConnectionType::INBOUND,
350-
/*inbound_onion=*/false};
353+
/*inbound_onion=*/false,
354+
/*network_key=*/1};
351355
nodes[1]->SetCommonVersion(PROTOCOL_VERSION);
352356
peerLogic->InitializeNode(*nodes[1], NODE_NETWORK);
353357
nodes[1]->fSuccessfullyConnected = true;
@@ -377,7 +381,8 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
377381
CAddress(),
378382
/*addrNameIn=*/"",
379383
ConnectionType::OUTBOUND_FULL_RELAY,
380-
/*inbound_onion=*/false};
384+
/*inbound_onion=*/false,
385+
/*network_key=*/2};
381386
nodes[2]->SetCommonVersion(PROTOCOL_VERSION);
382387
peerLogic->InitializeNode(*nodes[2], NODE_NETWORK);
383388
nodes[2]->fSuccessfullyConnected = true;
@@ -419,7 +424,8 @@ BOOST_AUTO_TEST_CASE(DoS_bantime)
419424
CAddress(),
420425
/*addrNameIn=*/"",
421426
ConnectionType::INBOUND,
422-
/*inbound_onion=*/false};
427+
/*inbound_onion=*/false,
428+
/*network_key=*/1};
423429
dummyNode.SetCommonVersion(PROTOCOL_VERSION);
424430
peerLogic->InitializeNode(dummyNode, NODE_NETWORK);
425431
dummyNode.fSuccessfullyConnected = true;

src/test/fuzz/p2p_headers_presync.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ void HeadersSyncSetup::ResetAndInitialize()
7070

7171
for (auto conn_type : conn_types) {
7272
CAddress addr{};
73-
m_connections.push_back(new CNode(id++, nullptr, addr, 0, 0, addr, "", conn_type, false));
73+
m_connections.push_back(new CNode(id++, nullptr, addr, 0, 0, addr, "", conn_type, false, 0));
7474
CNode& p2p_node = *m_connections.back();
7575

7676
connman.Handshake(

src/test/fuzz/util/net.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,8 @@ auto ConsumeNode(FuzzedDataProvider& fuzzed_data_provider, const std::optional<N
239239
const std::string addr_name = fuzzed_data_provider.ConsumeRandomLengthString(64);
240240
const ConnectionType conn_type = fuzzed_data_provider.PickValueInArray(ALL_CONNECTION_TYPES);
241241
const bool inbound_onion{conn_type == ConnectionType::INBOUND ? fuzzed_data_provider.ConsumeBool() : false};
242+
const uint64_t network_id = fuzzed_data_provider.ConsumeIntegral<uint64_t>();
243+
242244
NetPermissionFlags permission_flags = ConsumeWeakEnum(fuzzed_data_provider, ALL_NET_PERMISSION_FLAGS);
243245
if constexpr (ReturnUniquePtr) {
244246
return std::make_unique<CNode>(node_id,
@@ -250,6 +252,7 @@ auto ConsumeNode(FuzzedDataProvider& fuzzed_data_provider, const std::optional<N
250252
addr_name,
251253
conn_type,
252254
inbound_onion,
255+
network_id,
253256
CNodeOptions{ .permission_flags = permission_flags });
254257
} else {
255258
return CNode{node_id,
@@ -261,6 +264,7 @@ auto ConsumeNode(FuzzedDataProvider& fuzzed_data_provider, const std::optional<N
261264
addr_name,
262265
conn_type,
263266
inbound_onion,
267+
network_id,
264268
CNodeOptions{ .permission_flags = permission_flags }};
265269
}
266270
}

src/test/net_peer_connection_tests.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@ void AddPeer(NodeId& id, std::vector<CNode*>& nodes, PeerManager& peerman, Connm
7272
CAddress{},
7373
/*addrNameIn=*/"",
7474
conn_type,
75-
/*inbound_onion=*/inbound_onion});
75+
/*inbound_onion=*/inbound_onion,
76+
/*network_key=*/0});
7677
CNode& node = *nodes.back();
7778
node.SetCommonVersion(PROTOCOL_VERSION);
7879

src/test/net_tests.cpp

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test)
6767
CAddress(),
6868
pszDest,
6969
ConnectionType::OUTBOUND_FULL_RELAY,
70-
/*inbound_onion=*/false);
70+
/*inbound_onion=*/false,
71+
/*network_key=*/0);
7172
BOOST_CHECK(pnode1->IsFullOutboundConn() == true);
7273
BOOST_CHECK(pnode1->IsManualConn() == false);
7374
BOOST_CHECK(pnode1->IsBlockOnlyConn() == false);
@@ -85,7 +86,8 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test)
8586
CAddress(),
8687
pszDest,
8788
ConnectionType::INBOUND,
88-
/*inbound_onion=*/false);
89+
/*inbound_onion=*/false,
90+
/*network_key=*/1);
8991
BOOST_CHECK(pnode2->IsFullOutboundConn() == false);
9092
BOOST_CHECK(pnode2->IsManualConn() == false);
9193
BOOST_CHECK(pnode2->IsBlockOnlyConn() == false);
@@ -103,7 +105,8 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test)
103105
CAddress(),
104106
pszDest,
105107
ConnectionType::OUTBOUND_FULL_RELAY,
106-
/*inbound_onion=*/false);
108+
/*inbound_onion=*/false,
109+
/*network_key=*/2);
107110
BOOST_CHECK(pnode3->IsFullOutboundConn() == true);
108111
BOOST_CHECK(pnode3->IsManualConn() == false);
109112
BOOST_CHECK(pnode3->IsBlockOnlyConn() == false);
@@ -121,7 +124,8 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test)
121124
CAddress(),
122125
pszDest,
123126
ConnectionType::INBOUND,
124-
/*inbound_onion=*/true);
127+
/*inbound_onion=*/true,
128+
/*network_key=*/3);
125129
BOOST_CHECK(pnode4->IsFullOutboundConn() == false);
126130
BOOST_CHECK(pnode4->IsManualConn() == false);
127131
BOOST_CHECK(pnode4->IsBlockOnlyConn() == false);
@@ -613,7 +617,8 @@ BOOST_AUTO_TEST_CASE(ipv4_peer_with_ipv6_addrMe_test)
613617
CAddress{},
614618
/*pszDest=*/std::string{},
615619
ConnectionType::OUTBOUND_FULL_RELAY,
616-
/*inbound_onion=*/false);
620+
/*inbound_onion=*/false,
621+
/*network_key=*/0);
617622
pnode->fSuccessfullyConnected.store(true);
618623

619624
// the peer claims to be reaching us via IPv6
@@ -667,7 +672,8 @@ BOOST_AUTO_TEST_CASE(get_local_addr_for_peer_port)
667672
/*addrBindIn=*/CService{},
668673
/*addrNameIn=*/std::string{},
669674
/*conn_type_in=*/ConnectionType::OUTBOUND_FULL_RELAY,
670-
/*inbound_onion=*/false};
675+
/*inbound_onion=*/false,
676+
/*network_key=*/0};
671677
peer_out.fSuccessfullyConnected = true;
672678
peer_out.SetAddrLocal(peer_us);
673679

@@ -688,7 +694,8 @@ BOOST_AUTO_TEST_CASE(get_local_addr_for_peer_port)
688694
/*addrBindIn=*/CService{},
689695
/*addrNameIn=*/std::string{},
690696
/*conn_type_in=*/ConnectionType::INBOUND,
691-
/*inbound_onion=*/false};
697+
/*inbound_onion=*/false,
698+
/*network_key=*/1};
692699
peer_in.fSuccessfullyConnected = true;
693700
peer_in.SetAddrLocal(peer_us);
694701

@@ -825,7 +832,8 @@ BOOST_AUTO_TEST_CASE(initial_advertise_from_version_message)
825832
/*addrBindIn=*/CService{},
826833
/*addrNameIn=*/std::string{},
827834
/*conn_type_in=*/ConnectionType::OUTBOUND_FULL_RELAY,
828-
/*inbound_onion=*/false};
835+
/*inbound_onion=*/false,
836+
/*network_key=*/2};
829837

830838
const uint64_t services{NODE_NETWORK | NODE_WITNESS};
831839
const int64_t time{0};
@@ -900,7 +908,8 @@ BOOST_AUTO_TEST_CASE(advertise_local_address)
900908
CAddress{},
901909
/*pszDest=*/std::string{},
902910
ConnectionType::OUTBOUND_FULL_RELAY,
903-
/*inbound_onion=*/false);
911+
/*inbound_onion=*/false,
912+
/*network_key=*/0);
904913
};
905914
g_reachable_nets.Add(NET_CJDNS);
906915

0 commit comments

Comments
 (0)