pqtransport is the protocol layer of the TurkanaNation post-quantum stack.
It speaks UDP, TLS 1.3 hybrid key exchange, DNS / DoH / DoT, mDNS, QUIC
frames, and HTTP/1.1–3. Cryptography is exclusively
package:pqforge.
Infrastructure is exclusively
package:swissarmyknife.
There is no dart:ffi and no platform TLS (SecureSocket) on the PQ path.
import 'package:pqtransport/pqtransport.dart';
final crypto = PqTransportCrypto();
final identity = PqTlsServerIdentity.generate(crypto);
final (a, b) = MemoryByteSocket.pair();
final client = PqTlsSocket.client(a, crypto: crypto);
final server = PqTlsSocket.server(b, crypto: crypto, identity: identity);
await Future.wait([server.handshake(), client.handshake()]);
final key = client.exporter('app', Uint8List(0), 32);
Signal#
| Gate | Value |
|---|---|
| Version | 0.1.0 |
| SDK | >=3.12.0 <4.0.0 |
| Tests | 153 passed |
| Line coverage | 90.7% of lib/ |
| Hybrid groups | 3 RFC 10024 codecs + live KEX |
| Live KEX | X25519, P-256, P-384 |
| FFI | none |
| Platform TLS on PQ path | none |
Hybrid groups (RFC 10024)#
Concatenation order is group-dependent. X25519MLKEM768 does not
follow RFC 9954 naming order. ML-KEM is first on the wire. NIST-curve
groups put ECDHE first. Concat uses concatenateSharedSecrets;
PqForgeCombiner.combine() (always classical then PQ) is not on this path.
| Group | Codepoint | Client | Server | SS | Order | Live |
|---|---|---|---|---|---|---|
| X25519MLKEM768 | 0x11EC | 1216 | 1120 | 64 | ML-KEM then X25519 | Yes |
| SecP256r1MLKEM768 | 0x11EB | 1249 | 1153 | 64 | ECDHE then ML-KEM | Yes (balanced) |
| SecP384r1MLKEM1024 | 0x11ED | 1665 | 1665 | 80 | ECDHE then ML-KEM | Yes (maximum) |
P-256 / P-384 ECDH is live via pqforge 0.4.4. Profile/group mismatches
fail closed (requireGroup) rather than silently dropping to classical.
See Hybrid Groups and Claim Boundary.
The shape#
| Layer | What it gives you |
|---|---|
| Core |
HybridGroup
,
requireLength
,
Transcript
,
PqTransportCrypto
, every protocol size in
lengths.dart
|
| UDP | AES-256-GCM datagrams, replay before AEAD, Throttler, encrypted session (all three groups) |
| TLS 1.3 |
swissarmyknife
StateMachine
, RFC 8446 hellos, IANA
0x1302
/
0x1303
, ML-DSA-65 CertificateVerify, exporter
|
| DNS | A/AAAA/CNAME/MX/TXT/SRV/CAA/HTTPS/SVCB/OPT/PTR/NS, CircuitBreaker, TTL Cache |
| mDNS | Probe / announce / browse, optional ML-DSA-65 TXT |
| QUIC / HTTP | 1-RTT packet protect, CRYPTO/STREAM frames, HTTP/1.1 over PqTlsSocket |
Family#
application (HTTP, DNS, mDNS, QUIC frames)
│
pqtransport — this package. Protocol. No primitives.
│
pqforge — ML-KEM, ML-DSA, X25519, P-256/P-384 ECDH, AES-256-GCM, ChaCha, HKDF
│
pqcrypto — FIPS 203/204/205 primitives + KAT evidence
swissarmyknife supplies Result, StateMachine, Cache,
CircuitBreaker, and Throttler. Read Sister packages.
Do not overclaim#
This layer may not invent a stronger claim than pqcrypto / pqforge.
- Not a FIPS 140 module. No CMVP listing.
- Best-effort side-channel posture in Dart. Best-effort zeroization.
- RFC 10024-aligned hybrid encoding with unit-tested concatenation — not "interoperable with OpenSSL."
- Live KEX in 0.1.0 is all three RFC 10024 groups.
-
TLS default suite is IANA
TLS_AES_256_GCM_SHA384(0x1302, SHA-384).0x1303(ChaCha + SHA-256) is offered and completes on dart2js. Private-use0xFF00is retired.
Full wording: Claim Boundary.
Start#
dependencies:
pqtransport: ^0.1.0
pqforge: ^0.4.5
swissarmyknife: ^0.1.0
Then read Getting Started, the API Guide, and
keep the Cookbook nearby. Canonical markdown lives in
doc/INDEX.md.