@@ -201,10 +201,38 @@ namespace libp2p::protocol {
201201 consumeObservedAddresses (msg.observedaddr (), peer_id, stream);
202202 }
203203
204+ // Check for signedPeerRecord first - if present and valid, use its addresses
205+ // Otherwise fall back to listenAddrs
204206 std::vector<std::string> addresses;
205- for (const auto &addr : msg.listenaddrs ()) {
206- addresses.push_back (addr);
207+ bool has_valid_signed_record = false ;
208+
209+ if (msg.has_signedpeerrecord ()) {
210+ auto signed_record_addresses =
211+ consumeSignedPeerRecord (msg.signedpeerrecord (), peer_id, stream);
212+ if (!signed_record_addresses.empty ()) {
213+ // Valid signed peer record found - use its addresses
214+ has_valid_signed_record = true ;
215+ for (const auto &addr : signed_record_addresses) {
216+ addresses.push_back (fromMultiaddrToString (addr));
217+ }
218+ } else {
219+ // signedPeerRecord was present but validation failed
220+ log_->warn (" peer {} sent invalid signedPeerRecord, rejecting addresses" ,
221+ peer_id.toBase58 ());
222+ // Don't accept addresses from listenAddrs either if signedPeerRecord
223+ // validation failed - this prevents address injection attacks
224+ signal_identify_received_ (peer_id);
225+ return ;
226+ }
227+ }
228+
229+ // If no signedPeerRecord or it was missing, use listenAddrs
230+ if (!has_valid_signed_record) {
231+ for (const auto &addr : msg.listenaddrs ()) {
232+ addresses.push_back (addr);
233+ }
207234 }
235+
208236 consumeListenAddresses (addresses, peer_id);
209237
210238 signal_identify_received_ (peer_id);
@@ -397,4 +425,45 @@ namespace libp2p::protocol {
397425 upsert_res.error ());
398426 }
399427 }
428+
429+ std::vector<multi::Multiaddress>
430+ IdentifyMessageProcessor::consumeSignedPeerRecord (
431+ const std::string &signed_peer_record_bytes,
432+ const peer::PeerId &peer_id,
433+ const StreamSPtr &stream) {
434+ // Security fix: Validate signedPeerRecord to prevent address injection
435+ // attacks. According to libp2p spec, a signed peer record envelope must:
436+ // 1. Have a valid envelope signature
437+ // 2. Contain a public key that derives a PeerId equal to remotePeerId
438+ // 3. Contain a PeerRecord with peerId matching the derived PeerId
439+ // Only if all checks pass should we accept the certified addresses.
440+
441+ if (signed_peer_record_bytes.empty ()) {
442+ log_->warn (" peer {} sent empty signedPeerRecord" , peer_id.toBase58 ());
443+ return {};
444+ }
445+
446+ auto stream_peer_id_res = stream->remotePeerId ();
447+ if (!stream_peer_id_res) {
448+ log_->warn (" cannot validate signedPeerRecord: no remote peer ID in stream" );
449+ return {};
450+ }
451+ auto remote_peer_id = stream_peer_id_res.value ();
452+
453+ // TODO: Implement full peer record envelope parsing and validation
454+ // according to libp2p peer record specification.
455+ // For now, we reject all signedPeerRecords to prevent the vulnerability
456+ // where malicious peers could inject third-party signed records.
457+ // This is a security fix to prevent address poisoning attacks.
458+
459+ log_->warn (
460+ " peer {} sent signedPeerRecord, but full validation is not yet "
461+ " implemented. Rejecting to prevent address injection attacks. "
462+ " Full peer record envelope parsing needs to be implemented." ,
463+ peer_id.toBase58 ());
464+
465+ // Return empty vector to indicate validation failed
466+ // The caller will reject addresses if validation fails
467+ return {};
468+ }
400469} // namespace libp2p::protocol
0 commit comments