rfc(0015): Pluggable Transport Skins for DPI resistance
- RFC-0015: Complete specification for censorship-resistant L0 - Transport Skins: RAW, MIMIC_HTTPS, MIMIC_DNS, MIMIC_VIDEO, STEGO - Polymorphic Noise Generator (PNG): Per-session traffic shaping - Anti-fingerprinting: TLS JA3 parroting, ECH, DoH tunneling - Gherkin BDD: 50+ scenarios for TDD implementation Targets: GFW, RKN, Iran/Turkmenistan edge cases Kenya compliant: Embedded-only, no cloud calls Features: - transport_skins.feature: Skin selection, probing, defense - polymorphic_noise.feature: PNG determinism, distributions Next: Sprint 5 implementation (MIMIC_HTTPS + PNG core)
This commit is contained in:
parent
65f9af6b5d
commit
03c6389063
|
|
@ -0,0 +1,393 @@
|
||||||
|
# RFC-0015: Pluggable Transport Skins (PTS)
|
||||||
|
|
||||||
|
**Status:** Draft
|
||||||
|
**Author:** Frankie (Silicon Architect)
|
||||||
|
**Date:** 2026-02-03
|
||||||
|
**Target:** Janus SDK L0 Transport Layer
|
||||||
|
**Classification:** CRYPTOGRAPHIC / CENSORSHIP-RESISTANT
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
Transport Skins provide **pluggable censorship resistance** for Libertaria's L0 Transport layer. Each "skin" wraps the standard LWF (Lightweight Wire Format) frame to mimic benign traffic patterns, defeating state-level Deep Packet Inspection (DPI) as deployed by China's GFW, Russia's RKN, Iran's Filternet, and similar adversaries.
|
||||||
|
|
||||||
|
**Core Innovation:** Per-session **Polymorphic Noise Generator (PNG)** ensures no two sessions ever exhibit identical traffic patterns.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Threat Model
|
||||||
|
|
||||||
|
### Adversary Capabilities (GFW-Class)
|
||||||
|
| Technique | Capability | Our Counter |
|
||||||
|
|-----------|------------|-------------|
|
||||||
|
| Magic Byte Detection | Signature matching at line rate | Skins remove/replace magic bytes |
|
||||||
|
| TLS Fingerprinting (JA3/JA4) | Statistical TLS handshake analysis | utls-style parroting (Chrome/Firefox mimicry) |
|
||||||
|
| SNI Inspection | Cleartext server name identification | ECH (Encrypted Client Hello) + Domain Fronting |
|
||||||
|
| Packet Size Analysis | Fixed MTU detection | Probabilistic size distributions |
|
||||||
|
| Timing Correlation | Inter-packet timing patterns | Exponential/Gamma jitter |
|
||||||
|
| Flow Correlation | Long-term traffic statistics | Epoch rotation (100-1000 packets) |
|
||||||
|
| Active Probing | Sending test traffic to suspected relays | Honeytrap responses + IP blacklisting |
|
||||||
|
| DNS Manipulation | Poisoning, blocking, inspection | DoH (DNS-over-HTTPS) tunneling |
|
||||||
|
|
||||||
|
### Non-Goals
|
||||||
|
- **Traffic confirmation attacks** (end-to-end correlation): Out of scope; use L2 Membrane mixing
|
||||||
|
- **Physical layer interception**: Out of scope; requires steganographic hardware
|
||||||
|
- **Compromised endpoints**: Out of scope; requires TEE/SEV-SNP attestation
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────────────────┐
|
||||||
|
│ RFC-0015: TRANSPORT SKINS │
|
||||||
|
│ "Submarine Camouflage" │
|
||||||
|
├─────────────────────────────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ L3+ Application │
|
||||||
|
│ │ │
|
||||||
|
│ ▼ │
|
||||||
|
│ ┌─────────────────────────────────────────────────────────────────┐ │
|
||||||
|
│ │ LWF FRAME │ │
|
||||||
|
│ │ • 1350 bytes (configurable) │ │
|
||||||
|
│ │ • XChaCha20-Poly1305 encrypted │ │
|
||||||
|
│ │ • Magic bytes: LWF\0 (internal only) │ │
|
||||||
|
│ └─────────────────────────────────────────────────────────────────┘ │
|
||||||
|
│ │ │
|
||||||
|
│ ▼ │
|
||||||
|
│ ┌─────────────────────────────────────────────────────────────────┐ │
|
||||||
|
│ │ POLYMORPHIC NOISE GENERATOR (PNG) │ │
|
||||||
|
│ │ • ECDH-derived per-session seed │ │
|
||||||
|
│ │ • Epoch-based profile rotation (100-1000 packets) │ │
|
||||||
|
│ │ • Deterministic both ends (same seed = same noise) │ │
|
||||||
|
│ └─────────────────────────────────────────────────────────────────┘ │
|
||||||
|
│ │ │
|
||||||
|
│ ▼ │
|
||||||
|
│ ┌─────────────────────────────────────────────────────────────────┐ │
|
||||||
|
│ │ SKIN SELECTOR │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ ┌─────────┐ ┌─────────────┐ ┌───────────┐ ┌───────────┐ │ │
|
||||||
|
│ │ │ RAW │ │MIMIC_HTTPS │ │MIMIC_DNS │ │MIMIC_VIDEO│ │ │
|
||||||
|
│ │ │ UDP │ │WebSocket/TLS│ │DoH Tunnel │ │HLS chunks │ │ │
|
||||||
|
│ │ └────┬────┘ └──────┬──────┘ └─────┬─────┘ └─────┬─────┘ │ │
|
||||||
|
│ │ │ │ │ │ │ │
|
||||||
|
│ │ └──────────────┴───────────────┴──────────────┘ │ │
|
||||||
|
│ │ │ │ │
|
||||||
|
│ │ Auto-selection via probing │ │
|
||||||
|
│ └─────────────────────────────────────────────────────────────────┘ │
|
||||||
|
│ │ │
|
||||||
|
│ ▼ │
|
||||||
|
│ NETWORK (ISP/GFW/RKN sees only the skin's traffic pattern) │
|
||||||
|
│ │
|
||||||
|
└─────────────────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Polymorphic Noise Generator (PNG)
|
||||||
|
|
||||||
|
### Design Principles
|
||||||
|
1. **Per-session uniqueness:** ECDH handshake secret seeds ChaCha20 RNG
|
||||||
|
2. **Deterministic:** Both peers derive identical noise from shared secret
|
||||||
|
3. **Epoch rotation:** Profile changes every N packets (prevents long-term analysis)
|
||||||
|
4. **Distribution matching:** Sample sizes/timing from real-world captures
|
||||||
|
|
||||||
|
### Noise Parameters (Per Epoch)
|
||||||
|
```zig
|
||||||
|
pub const EpochProfile = struct {
|
||||||
|
// Packet size distribution
|
||||||
|
size_distribution: enum { Normal, Pareto, Bimodal, LogNormal },
|
||||||
|
size_mean: u16, // e.g., 1440 bytes
|
||||||
|
size_stddev: u16, // e.g., 200 bytes
|
||||||
|
|
||||||
|
// Timing distribution
|
||||||
|
timing_distribution: enum { Exponential, Gamma, Pareto },
|
||||||
|
timing_lambda: f64, // For exponential: mean inter-packet time
|
||||||
|
|
||||||
|
// Dummy packet injection
|
||||||
|
dummy_probability: f64, // 0.0-0.15 (0-15% fake packets)
|
||||||
|
dummy_distribution: enum { Uniform, Bursty },
|
||||||
|
|
||||||
|
// Epoch boundaries
|
||||||
|
epoch_packet_count: u32, // 100-1000 packets before rotation
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### Seed Derivation
|
||||||
|
```
|
||||||
|
Session Secret (ECDH) → HKDF-SHA256 → 256-bit PNG Seed
|
||||||
|
↓
|
||||||
|
┌───────────────────────┐
|
||||||
|
│ ChaCha20 RNG State │
|
||||||
|
└───────────────────────┘
|
||||||
|
↓
|
||||||
|
┌───────────────────────┐
|
||||||
|
│ Epoch Profile Chain │
|
||||||
|
│ (deterministic) │
|
||||||
|
└───────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Transport Skins
|
||||||
|
|
||||||
|
### Skin 0: RAW (Unrestricted Networks)
|
||||||
|
**Use case:** Friendly jurisdictions, LAN, high-performance paths
|
||||||
|
|
||||||
|
| Property | Value |
|
||||||
|
|----------|-------|
|
||||||
|
| Protocol | UDP direct |
|
||||||
|
| Port | 7844 (default) |
|
||||||
|
| Overhead | 0% |
|
||||||
|
| Latency | Minimal |
|
||||||
|
| Kenya Viable | ✅ Yes |
|
||||||
|
|
||||||
|
**Wire format:**
|
||||||
|
```
|
||||||
|
[LWF Frame: 1350 bytes]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Skin 1: MIMIC_HTTPS (Standard Censorship Bypass)
|
||||||
|
**Use case:** GFW, RKN, corporate firewalls (90% coverage)
|
||||||
|
|
||||||
|
| Property | Value |
|
||||||
|
|----------|-------|
|
||||||
|
| Protocol | WebSocket over TLS 1.3 |
|
||||||
|
| Port | 443 |
|
||||||
|
| SNI | Domain fronting capable (ECH preferred) |
|
||||||
|
| Overhead | ~5% (TLS + WS framing) |
|
||||||
|
| Latency | +50-100ms |
|
||||||
|
| Kenya Viable | ✅ Yes |
|
||||||
|
|
||||||
|
**TLS Fingerprinting Defense:**
|
||||||
|
- utls-style parroting (exact Chrome/Firefox JA3 signatures)
|
||||||
|
- HTTP/2 settings matching browser defaults
|
||||||
|
- ALPN: `h2, http/1.1`
|
||||||
|
|
||||||
|
**Wire format:**
|
||||||
|
```
|
||||||
|
TLS 1.3 Record Layer {
|
||||||
|
Content Type: Application Data (23)
|
||||||
|
TLS Ciphertext: {
|
||||||
|
WebSocket Frame {
|
||||||
|
FIN: 1
|
||||||
|
Opcode: Binary (0x02)
|
||||||
|
Masked: 0 (server→client) / 1 (client→server)
|
||||||
|
Payload: [PNG Noise] + [LWF Frame]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**WebSocket Handshake (Cover):**
|
||||||
|
```
|
||||||
|
GET /api/v3/stream HTTP/1.1
|
||||||
|
Host: cdn.cloudflare.com
|
||||||
|
Upgrade: websocket
|
||||||
|
Connection: Upgrade
|
||||||
|
Sec-WebSocket-Key: <base64(random)>
|
||||||
|
Sec-WebSocket-Version: 13
|
||||||
|
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)...
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Skin 2: MIMIC_DNS (Deep Censorship Bypass)
|
||||||
|
**Use case:** UDP blocked, HTTPS throttled, Iran/Turkmenistan edge cases
|
||||||
|
|
||||||
|
| Property | Value |
|
||||||
|
|----------|-------|
|
||||||
|
| Protocol | DNS-over-HTTPS (DoH) |
|
||||||
|
| Endpoint | 1.1.1.1, 8.8.8.8, 9.9.9.9 |
|
||||||
|
| Overhead | ~300% (Base64url encoding) |
|
||||||
|
| Latency | +200-500ms |
|
||||||
|
| Kenya Viable | ⚠️ Marginal (bandwidth-heavy) |
|
||||||
|
|
||||||
|
**DNS Tunnel Defenses:**
|
||||||
|
- **DoH not raw DNS:** Blends with real DoH traffic
|
||||||
|
- **Query distribution:** Match real DoH query timing (not regular intervals)
|
||||||
|
- **Label entropy:** Use dictionary words for subdomain labels (not base32)
|
||||||
|
|
||||||
|
**Wire format:**
|
||||||
|
```
|
||||||
|
POST /dns-query HTTP/2
|
||||||
|
Host: cloudflare-dns.com
|
||||||
|
Content-Type: application/dns-message
|
||||||
|
Accept: application/dns-message
|
||||||
|
|
||||||
|
Body: DNS Message {
|
||||||
|
Question: <LWF fragment encoded as DNS query name>
|
||||||
|
QTYPE: TXT (or HTTPS for larger payloads)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Skin 3: MIMIC_VIDEO (High-Bandwidth Bypass)
|
||||||
|
**Use case:** Video-streaming-whitelisted networks, QoS prioritization
|
||||||
|
|
||||||
|
| Property | Value |
|
||||||
|
|----------|-------|
|
||||||
|
| Protocol | HTTPS with HLS (HTTP Live Streaming) chunk framing |
|
||||||
|
| Mimics | Netflix, YouTube, Twitch |
|
||||||
|
| Overhead | ~10% (HLS `.ts` container) |
|
||||||
|
| Latency | +100-200ms |
|
||||||
|
| Kenya Viable | ✅ Yes |
|
||||||
|
|
||||||
|
**Wire format:**
|
||||||
|
```
|
||||||
|
HTTP/2 200 OK
|
||||||
|
Content-Type: video/mp2t
|
||||||
|
X-LWF-Sequence: <epoch_packet_num>
|
||||||
|
|
||||||
|
Body: [HLS MPEG-TS Container] {
|
||||||
|
Adaptation Field: [PNG padding]
|
||||||
|
Payload: [LWF Frame]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Skin 4: STEGO_IMAGE (Nuclear Option)
|
||||||
|
**Use case:** Total lockdown, emergency fallback only
|
||||||
|
|
||||||
|
| Property | Value |
|
||||||
|
|----------|-------|
|
||||||
|
| Protocol | HTTPS POST to image hosting (Imgur, etc.) |
|
||||||
|
| Stego Method | Generative steganography (StyleGAN encoding) |
|
||||||
|
| Bandwidth | ~1 byte per image (extremely slow) |
|
||||||
|
| Latency | Seconds to minutes |
|
||||||
|
| Kenya Viable | ❌ Emergency only |
|
||||||
|
|
||||||
|
**Note:** Traditional LSB steganography is broken against ML detection. Use generative encoding only.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Automatic Skin Selection
|
||||||
|
|
||||||
|
### Probe Sequence
|
||||||
|
```zig
|
||||||
|
pub const SkinProbe = struct {
|
||||||
|
/// Attempt skin selection with timeouts
|
||||||
|
pub async fn auto_select(relay: RelayEndpoint) !TransportSkin {
|
||||||
|
// 1. RAW UDP (fastest, 100ms timeout)
|
||||||
|
if (try probe_raw(relay, 100ms)) {
|
||||||
|
return .raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. HTTPS WebSocket (500ms timeout)
|
||||||
|
if (try probe_https(relay, 500ms)) {
|
||||||
|
return .mimic_https(relay);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. DNS Tunnel (1s timeout)
|
||||||
|
if (try probe_dns(relay, 1s)) {
|
||||||
|
return .mimic_dns(relay);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Nuclear option (no probe, async only)
|
||||||
|
return .stego_async(relay);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### Multi-Path Agility (MPTCP-Style)
|
||||||
|
```zig
|
||||||
|
pub const MultiSkinSession = struct {
|
||||||
|
primary: TransportSkin, // 90% bandwidth (HTTPS)
|
||||||
|
secondary: TransportSkin, // 10% bandwidth (DNS keepalive)
|
||||||
|
|
||||||
|
/// If primary throttled, signal via secondary
|
||||||
|
pub fn adapt_to_throttling(self: *Self) void {
|
||||||
|
if (self.primary.detect_throttling()) {
|
||||||
|
self.secondary.signal_endpoint_switch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Active Probing Defenses
|
||||||
|
|
||||||
|
### Honeytrap Responses
|
||||||
|
When probed without valid session state:
|
||||||
|
1. **HTTPS Skin:** Respond as legitimate web server (nginx default page)
|
||||||
|
2. **DNS Skin:** Return NXDOMAIN or valid A record (not relay IP)
|
||||||
|
3. **Rate limit:** Exponential backoff on failed handshakes
|
||||||
|
|
||||||
|
### Reputation Tokens
|
||||||
|
Prevent rapid relay scanning:
|
||||||
|
```
|
||||||
|
Client → Relay: ClientHello + PoW (Argon2, 100ms)
|
||||||
|
Relay → Client: ServerHello (only if PoW valid)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Implementation Phases
|
||||||
|
|
||||||
|
### Phase 1: Foundation (Sprint 5)
|
||||||
|
- [ ] PNG core (ChaCha20 RNG, epoch rotation)
|
||||||
|
- [ ] RAW skin (baseline)
|
||||||
|
- [ ] MIMIC_HTTPS skin (WebSocket + TLS)
|
||||||
|
- [ ] utls fingerprint parroting
|
||||||
|
- [ ] Automatic probe selection
|
||||||
|
|
||||||
|
### Phase 2: Deep Bypass (Sprint 6)
|
||||||
|
- [ ] MIMIC_DNS skin (DoH tunnel)
|
||||||
|
- [ ] ECH support (Encrypted Client Hello)
|
||||||
|
- [ ] Active probing defenses
|
||||||
|
- [ ] Multi-path agility
|
||||||
|
|
||||||
|
### Phase 3: Advanced (Sprint 7)
|
||||||
|
- [ ] MIMIC_VIDEO skin (HLS framing)
|
||||||
|
- [ ] Distribution matching from real captures
|
||||||
|
- [ ] Steganography (generative only)
|
||||||
|
- [ ] Formal security audit
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Kenya Compliance Check
|
||||||
|
|
||||||
|
| Skin | RAM | Binary Size | Cloud Calls | Viable? |
|
||||||
|
|------|-----|-------------|-------------|---------|
|
||||||
|
| RAW | <1MB | +0KB | None | ✅ |
|
||||||
|
| MIMIC_HTTPS | <2MB | +50KB (TLS) | None (embedded TLS) | ✅ |
|
||||||
|
| MIMIC_DNS | <1MB | +10KB | DoH to public resolver | ✅ |
|
||||||
|
| MIMIC_VIDEO | <2MB | +20KB (HLS) | None | ✅ |
|
||||||
|
| STEGO | >100MB | +500MB (ML models) | Image host upload | ❌ |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Security Considerations
|
||||||
|
|
||||||
|
### TLS Fingerprinting (Critical)
|
||||||
|
**Risk:** Rustls default JA3 signature is trivially blockable.
|
||||||
|
**Mitigation:** Mandatory utls parroting; exact Chrome/Firefox match.
|
||||||
|
|
||||||
|
### DNS Tunnel Detectability (High)
|
||||||
|
**Risk:** Base32 subdomains have high entropy (4.8 vs 2.5 bits/char).
|
||||||
|
**Mitigation:** Use DoH to major providers; dictionary-word labels.
|
||||||
|
|
||||||
|
### Flow Correlation (Medium)
|
||||||
|
**Risk:** Long-term traffic statistics identify protocol.
|
||||||
|
**Mitigation:** PNG epoch rotation; per-session uniqueness.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
1. **utls:** [github.com/refraction-networking/utls](https://github.com/refraction-networking/utls) — TLS fingerprint parroting
|
||||||
|
2. **Snowflake:** [Tor Project](https://snowflake.torproject.org/) — WebRTC pluggable transport
|
||||||
|
3. **Conjure:** [refraction.network](https://refraction.network/) — Refraction networking
|
||||||
|
4. **ECH:** RFC 9446 — Encrypted Client Hello
|
||||||
|
5. **DoH:** RFC 8484 — DNS over HTTPS
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*"The submarine wears chameleon skin. The hull remains the same."*
|
||||||
|
⚡️
|
||||||
|
|
@ -0,0 +1,251 @@
|
||||||
|
Feature: RFC-0015 Polymorphic Noise Generator (PNG)
|
||||||
|
As a Libertaria protocol developer
|
||||||
|
I want cryptographically secure per-session traffic shaping
|
||||||
|
So that state-level DPI cannot fingerprint or correlate sessions
|
||||||
|
|
||||||
|
Background:
|
||||||
|
Given the PNG is initialized with ChaCha20 RNG
|
||||||
|
And the entropy source is the ECDH shared secret
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Seed Derivation and Determinism
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
Scenario: PNG seed derives from ECDH shared secret
|
||||||
|
Given Alice and Bob perform X25519 ECDH
|
||||||
|
And the shared secret is 32 bytes
|
||||||
|
When Alice derives PNG seed via HKDF-SHA256
|
||||||
|
And Bob derives PNG seed via HKDF-SHA256
|
||||||
|
Then both seeds should be 256 bits
|
||||||
|
And the seeds should be identical
|
||||||
|
And the derivation should use "Libertaria-PNG-v1" as context
|
||||||
|
|
||||||
|
Scenario: Different sessions produce different seeds
|
||||||
|
Given Alice and Bob perform ECDH for Session A
|
||||||
|
And Alice and Bob perform ECDH for Session B
|
||||||
|
When PNG seeds are derived for both sessions
|
||||||
|
Then the seeds should be different
|
||||||
|
And the Hamming distance should be ~128 bits
|
||||||
|
|
||||||
|
Scenario: PNG seed has sufficient entropy
|
||||||
|
Given 1000 independent ECDH handshakes
|
||||||
|
When PNG seeds are derived for all sessions
|
||||||
|
Then no seed collisions should occur
|
||||||
|
And the distribution should pass Chi-square randomness test
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Epoch Profile Generation
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
Scenario: Epoch profile contains all noise parameters
|
||||||
|
Given a PNG with valid seed
|
||||||
|
When the first epoch profile is generated
|
||||||
|
Then it should contain size_distribution variant
|
||||||
|
And size_mean and size_stddev parameters
|
||||||
|
And timing_distribution variant
|
||||||
|
And timing_lambda parameter
|
||||||
|
And dummy_probability between 0.0 and 0.15
|
||||||
|
And epoch_packet_count between 100 and 1000
|
||||||
|
|
||||||
|
Scenario: Sequential epochs are deterministic
|
||||||
|
Given a PNG with seed S
|
||||||
|
When epoch 0 profile is generated
|
||||||
|
And epoch 1 profile is generated
|
||||||
|
And a second PNG with same seed S
|
||||||
|
When epoch 0 and 1 profiles are generated again
|
||||||
|
Then all corresponding epochs should match exactly
|
||||||
|
|
||||||
|
Scenario: Different seeds produce uncorrelated epochs
|
||||||
|
Given PNG A with seed S1
|
||||||
|
And PNG B with seed S2
|
||||||
|
When 10 epochs are generated for both
|
||||||
|
Then size_mean of corresponding epochs should not correlate
|
||||||
|
And timing_lambda values should not correlate
|
||||||
|
And Kolmogorov-Smirnov test should show different distributions
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Packet Size Noise
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
Scenario Outline: Packet size distributions match theoretical models
|
||||||
|
Given the epoch profile specifies <distribution> distribution
|
||||||
|
And size_mean = <mean> bytes
|
||||||
|
And size_stddev = <stddev> bytes
|
||||||
|
When 10000 packet sizes are sampled
|
||||||
|
Then the empirical distribution should match theoretical <distribution>
|
||||||
|
And the Chi-square test p-value should be > 0.05
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
| distribution | mean | stddev |
|
||||||
|
| Normal | 1440 | 200 |
|
||||||
|
| Pareto | 1440 | 400 |
|
||||||
|
| Bimodal | 1200 | 300 |
|
||||||
|
| LogNormal | 1500 | 250 |
|
||||||
|
|
||||||
|
Scenario: Packet sizes stay within valid bounds
|
||||||
|
Given any epoch profile
|
||||||
|
When packet sizes are sampled
|
||||||
|
Then all sizes should be >= 64 bytes
|
||||||
|
And all sizes should be <= 1500 bytes (Ethernet MTU)
|
||||||
|
And sizes should never exceed interface MTU
|
||||||
|
|
||||||
|
Scenario: Bimodal distribution matches video streaming
|
||||||
|
Given video streaming capture data
|
||||||
|
And epoch specifies Bimodal distribution
|
||||||
|
When PNG samples packet sizes
|
||||||
|
Then the two modes should be at ~600 bytes and ~1440 bytes
|
||||||
|
And the ratio should be approximately 1:3
|
||||||
|
And the distribution should match YouTube 1080p captures
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Timing Noise (Inter-packet Jitter)
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
Scenario Outline: Timing distributions match theoretical models
|
||||||
|
Given the epoch profile specifies <distribution> timing
|
||||||
|
And timing_lambda = <lambda>
|
||||||
|
When 10000 inter-packet delays are sampled
|
||||||
|
Then the empirical distribution should match theoretical <distribution>
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
| distribution | lambda |
|
||||||
|
| Exponential | 0.01 |
|
||||||
|
| Gamma | 0.005 |
|
||||||
|
| Pareto | 0.001 |
|
||||||
|
|
||||||
|
Scenario: Timing jitter prevents clock skew attacks
|
||||||
|
Given an adversary measures inter-packet timing
|
||||||
|
When the PNG applies jitter with Exponential distribution
|
||||||
|
Then the coefficient of variation should be high (>0.5)
|
||||||
|
And timing side-channel attacks should fail
|
||||||
|
|
||||||
|
Scenario: Maximum latency bound enforcement
|
||||||
|
Given real-time voice application requirements
|
||||||
|
And maximum acceptable latency of 500ms
|
||||||
|
When timing noise is applied
|
||||||
|
Then no single packet should be delayed >500ms
|
||||||
|
And 99th percentile latency should be <300ms
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Dummy Packet Injection
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
Scenario: Dummy injection rate follows probability
|
||||||
|
Given dummy_probability = 0.10 (10%)
|
||||||
|
When 10000 transmission opportunities occur
|
||||||
|
Then approximately 1000 dummy packets should be injected
|
||||||
|
And the binomial 95% confidence interval should contain the count
|
||||||
|
|
||||||
|
Scenario: Dummy packets are indistinguishable from real
|
||||||
|
Given a mix of real and dummy packets
|
||||||
|
When examined by adversary
|
||||||
|
Then packet sizes should have same distribution
|
||||||
|
And timing should follow same patterns
|
||||||
|
And entropy analysis should not distinguish them
|
||||||
|
|
||||||
|
Scenario: Bursty dummy injection pattern
|
||||||
|
Given dummy_distribution = Bursty
|
||||||
|
And dummy_probability = 0.15
|
||||||
|
When dummies are injected
|
||||||
|
Then they should arrive in clusters (bursts)
|
||||||
|
And inter-burst gaps should follow exponential distribution
|
||||||
|
And intra-burst timing should be rapid
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Epoch Rotation
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
Scenario: Epoch rotates after packet count threshold
|
||||||
|
Given epoch_packet_count = 500
|
||||||
|
When 499 packets are transmitted
|
||||||
|
Then the profile should remain unchanged
|
||||||
|
When the 500th packet is transmitted
|
||||||
|
Then epoch rotation should trigger
|
||||||
|
And a new epoch profile should be generated
|
||||||
|
|
||||||
|
Scenario: Epoch rotation preserves session state
|
||||||
|
Given an active encrypted session
|
||||||
|
And epoch rotation triggers
|
||||||
|
When the new epoch begins
|
||||||
|
Then encryption keys should remain valid
|
||||||
|
And sequence numbers should continue monotonically
|
||||||
|
And no rekeying should be required
|
||||||
|
|
||||||
|
Scenario: Maximum epoch duration prevents indefinite exposure
|
||||||
|
Given epoch_packet_count = 1000
|
||||||
|
And a low-bandwidth application sends 1 packet/minute
|
||||||
|
When 60 minutes elapse with only 60 packets
|
||||||
|
Then the epoch should rotate anyway (time-based fallback)
|
||||||
|
And the maximum epoch duration should be 10 minutes
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Integration with Transport Skins
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
Scenario: PNG noise applied before skin wrapping
|
||||||
|
Given MIMIC_HTTPS skin is active
|
||||||
|
And an LWF frame of 1350 bytes
|
||||||
|
When PNG adds padding noise
|
||||||
|
Then the total size should follow epoch's distribution
|
||||||
|
And the padding should be added before TLS encryption
|
||||||
|
And the WebSocket frame should contain padded payload
|
||||||
|
|
||||||
|
Scenario: PNG noise subtraction by receiving peer
|
||||||
|
Given PNG adds 50 bytes of padding to a packet
|
||||||
|
When the packet arrives at destination
|
||||||
|
And the peer uses same PNG seed
|
||||||
|
Then the padding should be identifiable
|
||||||
|
And the original 1350-byte LWF frame should be recoverable
|
||||||
|
|
||||||
|
Scenario: Different skins use same PNG instance
|
||||||
|
Given a session starts with RAW skin
|
||||||
|
And PNG is seeded
|
||||||
|
When skin switches to MIMIC_HTTPS
|
||||||
|
Then the PNG should continue same epoch sequence
|
||||||
|
And noise patterns should remain consistent
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Statistical Security Tests
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
Scenario: NIST SP 800-22 randomness tests
|
||||||
|
Given 1MB of PNG output (ChaCha20 keystream)
|
||||||
|
When subjected to NIST statistical test suite
|
||||||
|
Then all 15 tests should pass
|
||||||
|
Including Frequency, Runs, FFT, Template matching
|
||||||
|
|
||||||
|
Scenario: Dieharder randomness tests
|
||||||
|
Given 10MB of PNG output
|
||||||
|
When subjected to Dieharder test suite
|
||||||
|
Then no tests should report "WEAK" or "FAILED"
|
||||||
|
|
||||||
|
Scenario: Avalanche effect on seed changes
|
||||||
|
Given PNG seed S1 produces output stream O1
|
||||||
|
When one bit is flipped in seed (S2 = S1 XOR 0x01)
|
||||||
|
And output stream O2 is generated
|
||||||
|
Then O1 and O2 should differ in ~50% of bits
|
||||||
|
And the correlation coefficient should be ~0
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Performance and Resource Usage
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
Scenario: PNG generation is fast enough for line rate
|
||||||
|
Given 1 Gbps network interface
|
||||||
|
And 1500 byte packets
|
||||||
|
When PNG generates noise for each packet
|
||||||
|
Then generation time should be <1μs per packet
|
||||||
|
And CPU usage should be <5% of one core
|
||||||
|
|
||||||
|
Scenario: PNG memory footprint is minimal
|
||||||
|
Given the PNG is initialized
|
||||||
|
When measuring memory usage
|
||||||
|
Then ChaCha20 state should use ≤136 bytes
|
||||||
|
And epoch profile should use ≤64 bytes
|
||||||
|
And total PNG overhead should be <1KB per session
|
||||||
|
|
||||||
|
Scenario: PNG works on constrained devices
|
||||||
|
Given a device with 10MB RAM (Kenya compliance)
|
||||||
|
When 1000 concurrent sessions are active
|
||||||
|
Then total PNG memory should be <10MB
|
||||||
|
And each session PNG overhead should be <10KB
|
||||||
|
|
@ -0,0 +1,214 @@
|
||||||
|
Feature: RFC-0015 Pluggable Transport Skins
|
||||||
|
As a Libertaria node operator in a censored region
|
||||||
|
I want to automatically select camouflaged transport protocols
|
||||||
|
So that my traffic evades detection by state-level DPI (GFW, RKN, etc.)
|
||||||
|
|
||||||
|
Background:
|
||||||
|
Given the L0 transport layer is initialized
|
||||||
|
And the node has a valid relay endpoint configuration
|
||||||
|
And the Polymorphic Noise Generator (PNG) is seeded with ECDH secret
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Skin Selection and Probing
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
Scenario: Automatic skin selection succeeds with RAW UDP
|
||||||
|
Given the network allows outbound UDP to port 7844
|
||||||
|
When the skin probe sequence starts
|
||||||
|
And the RAW UDP probe completes within 100ms
|
||||||
|
Then the transport skin should be "RAW"
|
||||||
|
And the LWF frames should be sent unmodified over UDP
|
||||||
|
|
||||||
|
Scenario: Automatic skin selection falls back to HTTPS
|
||||||
|
Given the network blocks UDP port 7844
|
||||||
|
And HTTPS traffic to port 443 is allowed
|
||||||
|
When the RAW UDP probe times out after 100ms
|
||||||
|
And the HTTPS WebSocket probe completes within 500ms
|
||||||
|
Then the transport skin should be "MIMIC_HTTPS"
|
||||||
|
And the LWF frames should be wrapped in WebSocket frames over TLS 1.3
|
||||||
|
|
||||||
|
Scenario: Automatic skin selection falls back to DNS tunnel
|
||||||
|
Given the network blocks all UDP except DNS
|
||||||
|
And blocks HTTPS to non-whitelisted domains
|
||||||
|
When the RAW UDP probe times out
|
||||||
|
And the HTTPS probe times out after 500ms
|
||||||
|
And the DNS DoH probe completes within 1s
|
||||||
|
Then the transport skin should be "MIMIC_DNS"
|
||||||
|
And the LWF frames should be encoded as DNS queries over HTTPS
|
||||||
|
|
||||||
|
Scenario: Automatic skin selection reaches nuclear option
|
||||||
|
Given the network implements deep packet inspection on all protocols
|
||||||
|
And all previous probes fail
|
||||||
|
When the probe sequence reaches the steganography fallback
|
||||||
|
Then the transport skin should be "STEGO_IMAGE"
|
||||||
|
And the user should be warned of extreme latency
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Polymorphic Noise Generator (PNG)
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
Scenario: PNG generates per-session unique noise
|
||||||
|
Given two independent sessions to the same relay
|
||||||
|
And both sessions complete ECDH handshake
|
||||||
|
When Session A derives PNG seed from shared secret
|
||||||
|
And Session B derives PNG seed from its shared secret
|
||||||
|
Then the PNG seeds should be different
|
||||||
|
And the epoch profiles should be different
|
||||||
|
And the packet size distributions should not correlate
|
||||||
|
|
||||||
|
Scenario: PNG generates deterministic noise for session peers
|
||||||
|
Given a single session between Alice and Bob
|
||||||
|
And they complete ECDH handshake
|
||||||
|
When Alice derives PNG seed from shared secret
|
||||||
|
And Bob derives PNG seed from same shared secret
|
||||||
|
Then the PNG seeds should be identical
|
||||||
|
And Alice's noise can be subtracted by Bob
|
||||||
|
|
||||||
|
Scenario: PNG epoch rotation prevents long-term analysis
|
||||||
|
Given a session using MIMIC_HTTPS skin
|
||||||
|
And the epoch length is set to 500 packets
|
||||||
|
When 499 packets have been transmitted
|
||||||
|
Then the packet size distribution should follow Profile A
|
||||||
|
When the 500th packet is transmitted
|
||||||
|
Then the epoch should rotate
|
||||||
|
And the packet size distribution should follow Profile B
|
||||||
|
And Profile B should be different from Profile A
|
||||||
|
|
||||||
|
Scenario: PNG matches real-world distributions
|
||||||
|
Given MIMIC_HTTPS skin with Netflix emulation
|
||||||
|
When the PNG samples packet sizes
|
||||||
|
Then the distribution should be Pareto with mean 1440 bytes
|
||||||
|
And the distribution should match Netflix video chunk captures
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# MIMIC_HTTPS Skin (WebSocket over TLS)
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
Scenario: HTTPS skin mimics Chrome TLS fingerprint
|
||||||
|
Given the transport skin is "MIMIC_HTTPS"
|
||||||
|
When the TLS handshake initiates
|
||||||
|
Then the ClientHello should match Chrome 120 JA3 signature
|
||||||
|
And the cipher suites should match Chrome defaults
|
||||||
|
And the extensions order should match Chrome
|
||||||
|
And the ALPN should be "h2,http/1.1"
|
||||||
|
|
||||||
|
Scenario: HTTPS skin WebSocket handshake looks legitimate
|
||||||
|
Given the transport skin is "MIMIC_HTTPS"
|
||||||
|
When the WebSocket upgrade request is sent
|
||||||
|
Then the HTTP headers should include legitimate User-Agent
|
||||||
|
And the request path should look like a real API endpoint
|
||||||
|
And the Origin header should be set appropriately
|
||||||
|
|
||||||
|
Scenario: HTTPS skin hides LWF magic bytes
|
||||||
|
Given an LWF frame with magic bytes "LWF\0"
|
||||||
|
When wrapped in MIMIC_HTTPS skin
|
||||||
|
Then the wire format should be TLS ciphertext
|
||||||
|
And the magic bytes should not appear in cleartext
|
||||||
|
And DPI signature matching should fail
|
||||||
|
|
||||||
|
Scenario: HTTPS skin with domain fronting
|
||||||
|
Given the relay supports domain fronting
|
||||||
|
And the cover domain is "cdn.cloudflare.com"
|
||||||
|
And the real endpoint is "relay.libertaria.network"
|
||||||
|
When the TLS handshake initiates
|
||||||
|
Then the SNI should be "cdn.cloudflare.com"
|
||||||
|
And the HTTP Host header should be "relay.libertaria.network"
|
||||||
|
|
||||||
|
Scenario: HTTPS skin with ECH (Encrypted Client Hello)
|
||||||
|
Given the relay supports ECH
|
||||||
|
And the client has ECH config for the relay
|
||||||
|
When the TLS handshake initiates
|
||||||
|
Then the ClientHelloInner should contain real SNI
|
||||||
|
And the ClientHelloOuter should have encrypted SNI
|
||||||
|
And passive DPI should not see the real destination
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# MIMIC_DNS Skin (DoH Tunnel)
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
Scenario: DNS skin uses DoH not raw DNS
|
||||||
|
Given the transport skin is "MIMIC_DNS"
|
||||||
|
When a DNS query is sent
|
||||||
|
Then it should be an HTTPS POST to 1.1.1.1
|
||||||
|
And the Content-Type should be "application/dns-message"
|
||||||
|
And not use raw port 53 UDP
|
||||||
|
|
||||||
|
Scenario: DNS skin avoids high-entropy labels
|
||||||
|
Given the transport skin is "MIMIC_DNS"
|
||||||
|
When encoding LWF data as DNS queries
|
||||||
|
Then subdomain labels should use dictionary words
|
||||||
|
And the Shannon entropy should be < 3.5 bits/char
|
||||||
|
And not use Base32/Base64 encoding
|
||||||
|
|
||||||
|
Scenario: DNS skin matches real DoH timing
|
||||||
|
Given the transport skin is "MIMIC_DNS"
|
||||||
|
When sending queries
|
||||||
|
Then the inter-query timing should follow Gamma distribution
|
||||||
|
And not be perfectly regular
|
||||||
|
And should match Cloudflare DoH query patterns
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Anti-Fingerprinting and Active Defense
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
Scenario: Active probe receives honeytrap response
|
||||||
|
Given an adversary sends probe traffic to relay
|
||||||
|
And the probe has no valid session cookie
|
||||||
|
When the relay receives the probe
|
||||||
|
Then it should respond as nginx default server
|
||||||
|
And return HTTP 200 with generic index.html
|
||||||
|
And not reveal itself as Libertaria relay
|
||||||
|
|
||||||
|
Scenario: Rate limiting on failed handshakes
|
||||||
|
Given an adversary attempts rapid handshake scanning
|
||||||
|
When more than 10 failed handshakes occur from same IP in 1 minute
|
||||||
|
Then subsequent connections should be rate limited
|
||||||
|
And exponential backoff should apply
|
||||||
|
|
||||||
|
Scenario: PoW prevents relay enumeration
|
||||||
|
Given the relay requires proof-of-work
|
||||||
|
When a client connects without valid PoW
|
||||||
|
Then the connection should be rejected
|
||||||
|
When a client connects with valid Argon2 PoW (100ms compute)
|
||||||
|
Then the connection should proceed to handshake
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Multi-Path Agility
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
Scenario: Primary skin throttling triggers fallback
|
||||||
|
Given primary skin is MIMIC_HTTPS at 90% bandwidth
|
||||||
|
And secondary skin is MIMIC_DNS at 10% bandwidth
|
||||||
|
When GFW detects and throttles HTTPS traffic
|
||||||
|
Then the secondary channel should signal endpoint switch
|
||||||
|
And the primary should migrate to new relay IP
|
||||||
|
|
||||||
|
Scenario: Seamless skin switching without rekeying
|
||||||
|
Given an active session with MIMIC_HTTPS
|
||||||
|
When the skin switches to MIMIC_DNS due to blocking
|
||||||
|
Then the LWF encryption keys should remain valid
|
||||||
|
And no re-handshake should be required
|
||||||
|
And in-flight packets should not be lost
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Error Handling and Edge Cases
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
Scenario: All probes fail raises alert
|
||||||
|
Given all network paths are blocked
|
||||||
|
When the skin probe sequence completes
|
||||||
|
And no viable skin is found
|
||||||
|
Then the user should receive "Network severely restricted" alert
|
||||||
|
And manual configuration option should be offered
|
||||||
|
|
||||||
|
Scenario: Skin mid-session failure recovery
|
||||||
|
Given a session is active with MIMIC_HTTPS
|
||||||
|
When the TLS connection drops unexpectedly
|
||||||
|
Then automatic reconnection should attempt same skin first
|
||||||
|
And fallback to next skin after 3 retries
|
||||||
|
|
||||||
|
Scenario: Invalid skin configuration is rejected
|
||||||
|
Given the configuration specifies unknown skin "MIMIC_UNKNOWN"
|
||||||
|
When the transport initializes
|
||||||
|
Then initialization should fail with "Invalid skin"
|
||||||
|
And fallback to automatic selection should occur
|
||||||
Loading…
Reference in New Issue