"""Finite loopback-only HTTP/3 WebTransport receiver for native security proof.""" import asyncio, datetime, hashlib, ipaddress, json, os, pathlib, sys from aioquic.asyncio import QuicConnectionProtocol, serve from aioquic.h3.connection import H3Connection, H3_ALPN from aioquic.h3.events import HeadersReceived, DatagramReceived, WebTransportStreamDataReceived from aioquic.quic.configuration import QuicConfiguration from aioquic.quic.events import ProtocolNegotiated from cryptography import x509 from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import ec from cryptography.x509.oid import NameOID facts = {"packets": 0, "connections": 0, "datagrams": 0, "streams": 0, "bytes": 0, "workerBytes": 0, "marker": False} marker = b"SYNTHETIC_WEBTRANSPORT_DLP_MARKER" class Receiver(QuicConnectionProtocol): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.http = None self.worker_sessions = set() def datagram_received(self, data, addr): facts["packets"] += 1 super().datagram_received(data, addr) def quic_event_received(self, event): if isinstance(event, ProtocolNegotiated): self.http = H3Connection(self._quic, enable_webtransport=True) if not self.http: return for item in self.http.handle_event(event): if isinstance(item, HeadersReceived): headers = dict(item.headers) if headers.get(b":method") == b"CONNECT" and headers.get(b":protocol") == b"webtransport": facts["connections"] += 1 if headers.get(b":path", b"").endswith(b"/worker"): self.worker_sessions.add(item.stream_id) self.http.send_headers(item.stream_id, [(b":status", b"200"), (b"sec-webtransport-http3-draft", b"draft02")]) self.transmit() elif isinstance(item, (DatagramReceived, WebTransportStreamDataReceived)): facts["datagrams" if isinstance(item, DatagramReceived) else "streams"] += 1 facts["bytes"] += len(item.data) session_id = item.stream_id if isinstance(item, DatagramReceived) else item.session_id if session_id in self.worker_sessions: facts["workerBytes"] += len(item.data) facts["marker"] |= marker in item.data async def main(): root = pathlib.Path(sys.argv[1]).resolve() root.mkdir(parents=True, exist_ok=True) key = ec.generate_private_key(ec.SECP256R1()) now = datetime.datetime.now(datetime.timezone.utc) name = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, "127.0.0.1")]) cert = x509.CertificateBuilder().subject_name(name).issuer_name(name).public_key(key.public_key()).serial_number(x509.random_serial_number()).not_valid_before(now-datetime.timedelta(minutes=1)).not_valid_after(now+datetime.timedelta(days=7)).add_extension(x509.SubjectAlternativeName([x509.IPAddress(ipaddress.ip_address("127.0.0.1"))]), critical=False).sign(key, hashes.SHA256()) cert_file, key_file = root / "certificate.pem", root / "key.pem" cert_file.write_bytes(cert.public_bytes(serialization.Encoding.PEM)) key_file.write_bytes(key.private_bytes(serialization.Encoding.PEM, serialization.PrivateFormat.PKCS8, serialization.NoEncryption())) config = QuicConfiguration(is_client=False, alpn_protocols=H3_ALPN, max_datagram_frame_size=65536) config.load_cert_chain(str(cert_file), str(key_file)) server = await serve("127.0.0.1", 0, configuration=config, create_protocol=Receiver) async def stats(reader, writer): await reader.read(8192) body = json.dumps(facts).encode() writer.write(b"HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: " + str(len(body)).encode() + b"\r\nConnection: close\r\n\r\n" + body) await writer.drain() writer.close() control = await asyncio.start_server(stats, "127.0.0.1", 0) print(json.dumps({"quicPort": server._transport.get_extra_info("sockname")[1], "statsPort": control.sockets[0].getsockname()[1], "hash": hashlib.sha256(cert.public_bytes(serialization.Encoding.DER)).hexdigest(), "aioquic": __import__("aioquic").__version__}), flush=True) await asyncio.sleep(180) server.close() control.close() if __name__ == "__main__": asyncio.run(main())