fix: peer verification successful

This commit is contained in:
Akash Mondal
2025-06-29 05:37:57 +00:00
committed by lla-dane
parent e2fee14bc5
commit 8263052f88
6 changed files with 831 additions and 56 deletions

View File

@ -1,20 +1,39 @@
#!/usr/bin/env python3
"""
Fixed QUIC handshake test to debug connection issues.
"""
import logging
import os
from pathlib import Path
import secrets
import sys
from tempfile import NamedTemporaryFile
from time import time
from aioquic._buffer import Buffer
from aioquic.quic.configuration import QuicConfiguration
from aioquic.quic.connection import QuicConnection
from aioquic.quic.logger import QuicFileLogger
from aioquic.quic.packet import pull_quic_header
import trio
from libp2p.crypto.ed25519 import create_new_key_pair
from libp2p.transport.quic.security import LIBP2P_TLS_EXTENSION_OID
from libp2p.peer.id import ID
from libp2p.transport.quic.security import (
LIBP2P_TLS_EXTENSION_OID,
create_quic_security_transport,
)
from libp2p.transport.quic.transport import QUICTransport, QUICTransportConfig
from libp2p.transport.quic.utils import create_quic_multiaddr
logging.basicConfig(
format="%(asctime)s %(levelname)s %(name)s %(message)s", level=logging.DEBUG
)
# Adjust this path to your project structure
project_root = Path(__file__).parent.parent.parent
sys.path.insert(0, str(project_root))
@ -256,10 +275,162 @@ async def test_server_startup():
return False
async def test_full_handshake_and_certificate_exchange():
"""
Test a full handshake to ensure it completes and peer certificates are exchanged.
This version is corrected to use the actual APIs available in the codebase.
"""
print("\n=== TESTING FULL HANDSHAKE AND CERTIFICATE EXCHANGE (CORRECTED) ===")
# 1. Generate KeyPairs and create libp2p security configs for client and server.
# The `create_quic_security_transport` function from `test_quic.py` is the
# correct helper to use, and it requires a `KeyPair` argument.
client_key_pair = create_new_key_pair()
server_key_pair = create_new_key_pair()
# This is the correct way to get the security configuration objects.
client_security_config = create_quic_security_transport(
client_key_pair.private_key, ID.from_pubkey(client_key_pair.public_key)
)
server_security_config = create_quic_security_transport(
server_key_pair.private_key, ID.from_pubkey(server_key_pair.public_key)
)
print("✅ libp2p security configs created.")
# 2. Create aioquic configurations and manually apply security settings,
# mimicking what the `QUICTransport` class does internally.
client_secrets_log_file = NamedTemporaryFile(
mode="w", delete=False, suffix="-client.log"
)
client_aioquic_config = QuicConfiguration(
is_client=True,
alpn_protocols=["libp2p"],
secrets_log_file=client_secrets_log_file,
)
client_aioquic_config.certificate = client_security_config.tls_config.certificate
client_aioquic_config.private_key = client_security_config.tls_config.private_key
client_aioquic_config.verify_mode = (
client_security_config.create_client_config().verify_mode
)
client_aioquic_config.quic_logger = QuicFileLogger(
"/home/akmo/GitHub/py-libp2p/examples/echo/logs"
)
server_secrets_log_file = NamedTemporaryFile(
mode="w", delete=False, suffix="-server.log"
)
server_aioquic_config = QuicConfiguration(
is_client=False,
alpn_protocols=["libp2p"],
secrets_log_file=server_secrets_log_file,
)
server_aioquic_config.certificate = server_security_config.tls_config.certificate
server_aioquic_config.private_key = server_security_config.tls_config.private_key
server_aioquic_config.verify_mode = (
server_security_config.create_server_config().verify_mode
)
server_aioquic_config.quic_logger = QuicFileLogger(
"/home/akmo/GitHub/py-libp2p/examples/echo/logs"
)
print("✅ aioquic configurations created and configured.")
print(f"🔑 Client secrets will be logged to: {client_secrets_log_file.name}")
print(f"🔑 Server secrets will be logged to: {server_secrets_log_file.name}")
# 3. Instantiate client, initiate its `connect` call, and get the ODCID for the server.
client_address = ("127.0.0.1", 1234)
server_address = ("127.0.0.1", 4321)
client_aioquic_config.connection_id_length = 8
client_conn = QuicConnection(configuration=client_aioquic_config)
client_conn.connect(server_address, now=time())
print("✅ aioquic connections instantiated correctly.")
print("🔧 Client CIDs")
print(f"Local Init CID: ", client_conn._local_initial_source_connection_id.hex())
print(
f"Remote Init CID: ",
(client_conn._remote_initial_source_connection_id or b"").hex(),
)
print(
f"Original Destination CID: ",
client_conn.original_destination_connection_id.hex(),
)
print(f"Host CID: {client_conn._host_cids[0].cid.hex()}")
# 4. Instantiate the server with the ODCID from the client.
server_aioquic_config.connection_id_length = 8
server_conn = QuicConnection(
configuration=server_aioquic_config,
original_destination_connection_id=client_conn.original_destination_connection_id,
)
print("✅ aioquic connections instantiated correctly.")
# 5. Manually drive the handshake process by exchanging datagrams.
max_duration_s = 5
start_time = time()
while time() - start_time < max_duration_s:
for datagram, _ in client_conn.datagrams_to_send(now=time()):
header = pull_quic_header(Buffer(data=datagram))
print("Client packet source connection id", header.source_cid.hex())
print("Client packet destination connection id", header.destination_cid.hex())
print("--SERVER INJESTING CLIENT PACKET---")
server_conn.receive_datagram(datagram, client_address, now=time())
print(
f"Server remote initial source id: {(server_conn._remote_initial_source_connection_id or b'').hex()}"
)
for datagram, _ in server_conn.datagrams_to_send(now=time()):
header = pull_quic_header(Buffer(data=datagram))
print("Server packet source connection id", header.source_cid.hex())
print("Server packet destination connection id", header.destination_cid.hex())
print("--CLIENT INJESTING SERVER PACKET---")
client_conn.receive_datagram(datagram, server_address, now=time())
# Check for completion
if client_conn._handshake_complete and server_conn._handshake_complete:
break
await trio.sleep(0.01)
# 6. Assertions to verify the outcome.
assert client_conn._handshake_complete, "❌ Client handshake did not complete."
assert server_conn._handshake_complete, "❌ Server handshake did not complete."
print("✅ Handshake completed for both peers.")
# The key assertion: check if the peer certificate was received.
client_peer_cert = getattr(client_conn.tls, "_peer_certificate", None)
server_peer_cert = getattr(server_conn.tls, "_peer_certificate", None)
client_secrets_log_file.close()
server_secrets_log_file.close()
os.unlink(client_secrets_log_file.name)
os.unlink(server_secrets_log_file.name)
assert client_peer_cert is not None, (
"❌ Client FAILED to receive server certificate."
)
print("✅ Client successfully received server certificate.")
assert server_peer_cert is not None, (
"❌ Server FAILED to receive client certificate."
)
print("✅ Server successfully received client certificate.")
print("🎉 Test Passed: Full handshake and certificate exchange successful.")
async def main():
"""Run all tests with better error handling."""
print("Starting QUIC diagnostic tests...")
handshake_ok = await test_full_handshake_and_certificate_exchange()
if not handshake_ok:
print("\n❌ CRITICAL: Handshake failed!")
print("Apply the handshake fix and try again.")
return
# Test 1: Certificate generation
cert_ok = await test_certificate_generation()
if not cert_ok: