|
| 1 | +# VLESS Reality Protocol Implementation |
| 2 | + |
| 3 | +This document describes the implementation of the VLESS Reality protocol in rustls. |
| 4 | + |
| 5 | +## Overview |
| 6 | + |
| 7 | +Reality is a protocol extension that provides enhanced privacy by encrypting the TLS session ID using a shared secret derived from X25519 ECDH with the server's public key. This implementation allows rustls clients to establish TLS connections with Reality-enabled servers. |
| 8 | + |
| 9 | +## Protocol Specification |
| 10 | + |
| 11 | +The Reality protocol follows these steps: |
| 12 | + |
| 13 | +1. **Key Generation**: Client generates an ephemeral X25519 keypair (`client_secret`, `client_public`) |
| 14 | + |
| 15 | +2. **ECDH**: Client performs Elliptic Curve Diffie-Hellman with server's public key: |
| 16 | + ``` |
| 17 | + shared_secret = client_secret.diffie_hellman(server_public_key) |
| 18 | + ``` |
| 19 | + |
| 20 | +3. **Key Derivation**: Client derives authentication key using HKDF-SHA256: |
| 21 | + ``` |
| 22 | + auth_key = HKDF-SHA256( |
| 23 | + ikm=shared_secret, |
| 24 | + salt=hello_random[:20], |
| 25 | + info="REALITY" |
| 26 | + ) |
| 27 | + ``` |
| 28 | + |
| 29 | +4. **Plaintext Construction**: Client constructs 16-byte plaintext: |
| 30 | + ``` |
| 31 | + [0..3] = client_version (3 bytes) + reserved (1 byte) |
| 32 | + [4..8] = Unix timestamp (big-endian u32) |
| 33 | + [8..16] = short_id (zero-padded to 8 bytes) |
| 34 | + ``` |
| 35 | + |
| 36 | +5. **Encryption**: Client encrypts plaintext using AES-128-GCM: |
| 37 | + ``` |
| 38 | + session_id = AES-128-GCM( |
| 39 | + key=auth_key, |
| 40 | + nonce=hello_random[20..32], |
| 41 | + aad=full_ClientHello_bytes, |
| 42 | + plaintext=plaintext |
| 43 | + ) |
| 44 | + ``` |
| 45 | + The result is 32 bytes: ciphertext (16 bytes) + authentication tag (16 bytes) |
| 46 | + |
| 47 | +6. **Key Share Injection**: Client's public key (`client_public`) is injected into the ClientHello `key_share` extension as an X25519 key exchange |
| 48 | + |
| 49 | +## API Usage |
| 50 | + |
| 51 | +### Basic Example |
| 52 | + |
| 53 | +```rust |
| 54 | +use std::sync::Arc; |
| 55 | +use watfaq_rustls::client::RealityConfig; |
| 56 | +use watfaq_rustls::{ClientConfig, RootCertStore}; |
| 57 | + |
| 58 | +// Server's X25519 public key (obtained securely out-of-band) |
| 59 | +let server_pubkey = [0u8; 32]; // Replace with actual key |
| 60 | + |
| 61 | +// Client identifier |
| 62 | +let short_id = vec![0x12, 0x34, 0x56, 0x78]; |
| 63 | + |
| 64 | +// Create Reality configuration |
| 65 | +let reality = RealityConfig::new(server_pubkey, short_id)?; |
| 66 | + |
| 67 | +// Build client configuration with Reality |
| 68 | +let config = ClientConfig::builder() |
| 69 | + .with_root_certificates(root_store) |
| 70 | + .with_reality(reality) |
| 71 | + .with_no_client_auth(); |
| 72 | + |
| 73 | +// Use normally |
| 74 | +let conn = ClientConnection::new(Arc::new(config), server_name)?; |
| 75 | +``` |
| 76 | + |
| 77 | +### Advanced Configuration |
| 78 | + |
| 79 | +```rust |
| 80 | +// Customize client version |
| 81 | +let reality = RealityConfig::new(server_pubkey, short_id)? |
| 82 | + .with_client_version([1, 2, 3]); |
| 83 | +``` |
| 84 | + |
| 85 | +### Running the Example |
| 86 | + |
| 87 | +The `reality-client` example demonstrates a complete Reality connection: |
| 88 | + |
| 89 | +```bash |
| 90 | +cd examples |
| 91 | +cargo run --bin reality-client -- \ |
| 92 | + example.com:443 \ |
| 93 | + 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef \ |
| 94 | + 12345678 |
| 95 | +``` |
| 96 | + |
| 97 | +Arguments: |
| 98 | +- `example.com:443` - Server address and port |
| 99 | +- Second argument - Server's X25519 public key (64 hex characters = 32 bytes) |
| 100 | +- Third argument - Short ID (up to 16 hex characters = 8 bytes) |
| 101 | + |
| 102 | +## Architecture |
| 103 | + |
| 104 | +### Module Structure |
| 105 | + |
| 106 | +``` |
| 107 | +rustls/src/client/ |
| 108 | +├── reality.rs # Core Reality implementation |
| 109 | +├── hs.rs # Handshake integration |
| 110 | +├── client_conn.rs # ClientConfig with reality_config field |
| 111 | +└── builder.rs # Builder pattern (with_reality method) |
| 112 | +``` |
| 113 | + |
| 114 | +### Key Components |
| 115 | + |
| 116 | +#### `RealityConfig` (Public API) |
| 117 | + |
| 118 | +```rust |
| 119 | +pub struct RealityConfig { |
| 120 | + server_public_key: [u8; 32], // Server's X25519 public key |
| 121 | + short_id: Vec<u8>, // Client identifier (max 8 bytes) |
| 122 | + client_version: [u8; 3], // Protocol version |
| 123 | +} |
| 124 | +``` |
| 125 | + |
| 126 | +#### `RealitySessionState` (Internal) |
| 127 | + |
| 128 | +```rust |
| 129 | +pub(crate) struct RealitySessionState { |
| 130 | + config: Arc<RealityConfig>, |
| 131 | + client_public: [u8; 32], // Client's ephemeral public key |
| 132 | + shared_secret: [u8; 32], // ECDH shared secret |
| 133 | +} |
| 134 | +``` |
| 135 | + |
| 136 | +### Handshake Integration |
| 137 | + |
| 138 | +The Reality protocol is integrated into the TLS handshake at key points: |
| 139 | + |
| 140 | +1. **Initialization** (`hs.rs:start_handshake`): |
| 141 | + - Creates `RealitySessionState` if Reality is configured |
| 142 | + - Performs X25519 ECDH to derive shared secret |
| 143 | + |
| 144 | +2. **Key Share Injection** (`hs.rs:emit_client_hello_for_retry`): |
| 145 | + - Replaces normal key_share with Reality X25519 public key |
| 146 | + - Ensures ClientHello contains the correct key exchange |
| 147 | + |
| 148 | +3. **Session ID Computation** (`hs.rs:emit_client_hello_for_retry`): |
| 149 | + - After ClientHello is constructed with zero session_id |
| 150 | + - Encodes ClientHello to get full bytes |
| 151 | + - Computes encrypted session_id using Reality protocol |
| 152 | + - Updates ClientHello with computed session_id |
| 153 | + |
| 154 | +## Cryptographic Implementation |
| 155 | + |
| 156 | +### Crypto Provider Support |
| 157 | + |
| 158 | +Reality implementation supports both `ring` and `aws-lc-rs` crypto providers through conditional compilation: |
| 159 | + |
| 160 | +```rust |
| 161 | +#[cfg(feature = "ring")] |
| 162 | +fn perform_x25519_ecdh(...) { /* ring implementation */ } |
| 163 | + |
| 164 | +#[cfg(feature = "aws_lc_rs")] |
| 165 | +fn perform_x25519_ecdh(...) { /* aws-lc-rs implementation */ } |
| 166 | +``` |
| 167 | + |
| 168 | +### Cryptographic Operations |
| 169 | + |
| 170 | +1. **X25519 ECDH**: Uses provider's X25519 implementation |
| 171 | +2. **HKDF-SHA256**: Obtained from TLS13_AES_128_GCM_SHA256 cipher suite |
| 172 | +3. **AES-128-GCM**: Uses provider's AEAD implementation |
| 173 | +4. **Randomness**: Uses provider's secure random generator |
| 174 | + |
| 175 | +## Security Considerations |
| 176 | + |
| 177 | +### Requirements |
| 178 | + |
| 179 | +- **Server Public Key Security**: The server's X25519 public key must be obtained through a secure, authenticated channel. An attacker with the ability to substitute the server public key can break the Reality protocol's privacy guarantees. |
| 180 | + |
| 181 | +- **Short ID Confidentiality**: The `short_id` serves as a client identifier. Keep it confidential to prevent tracking. |
| 182 | + |
| 183 | +- **Crypto Provider**: Reality requires X25519 and AES-128-GCM support. Use either `ring` or `aws-lc-rs` features. |
| 184 | + |
| 185 | +- **TLS Version**: Reality is compatible with both TLS 1.2 and TLS 1.3, but works best with TLS 1.3. |
| 186 | + |
| 187 | +### Threat Model |
| 188 | + |
| 189 | +Reality protects against: |
| 190 | +- **Passive Observation**: Session IDs are encrypted, preventing observers from correlating connections |
| 191 | +- **Active Probing**: Without the correct server private key, attackers cannot decrypt or forge valid session IDs |
| 192 | + |
| 193 | +Reality does NOT protect against: |
| 194 | +- **Compromised Server**: If the server's private key is compromised, past session IDs can be decrypted |
| 195 | +- **Traffic Analysis**: Connection timing and size metadata may still leak information |
| 196 | + |
| 197 | +## Testing |
| 198 | + |
| 199 | +### Unit Tests |
| 200 | + |
| 201 | +```bash |
| 202 | +cargo test --package watfaq-rustls --lib reality |
| 203 | +``` |
| 204 | + |
| 205 | +Tests include: |
| 206 | +- Configuration validation |
| 207 | +- X25519 ECDH correctness |
| 208 | +- AES-128-GCM encryption |
| 209 | +- Session ID plaintext structure |
| 210 | +- Full session ID computation pipeline |
| 211 | + |
| 212 | +### Integration Tests |
| 213 | + |
| 214 | +Integration tests verify: |
| 215 | +- RealitySessionState creation |
| 216 | +- Key share entry generation |
| 217 | +- Complete session_id computation with both crypto providers |
| 218 | +- Builder pattern integration |
| 219 | + |
| 220 | +## Implementation Notes |
| 221 | + |
| 222 | +### Design Decisions |
| 223 | + |
| 224 | +1. **Inline Computation**: Session ID is computed inline during handshake emission rather than using a callback, providing access to `Random` value and full `ClientHello` bytes. |
| 225 | + |
| 226 | +2. **State Management**: Reality state is passed through the handshake state machine to maintain access to cryptographic materials. |
| 227 | + |
| 228 | +3. **Provider Abstraction**: Uses rustls's existing crypto provider system for all cryptographic operations. |
| 229 | + |
| 230 | +4. **No Protocol Breaking Changes**: Reality is implemented as an optional extension without modifying core TLS protocol handling. |
| 231 | + |
| 232 | +### Limitations |
| 233 | + |
| 234 | +- **One-Time Use**: The ephemeral X25519 keypair is generated per connection and not reused. |
| 235 | + |
| 236 | +- **No HelloRetryRequest**: Reality state is not carried through HelloRetryRequest flows (set to `None` on retry). |
| 237 | + |
| 238 | +- **Fixed Crypto**: Currently requires HKDF-SHA256 (from TLS13_AES_128_GCM_SHA256) and AES-128-GCM. |
| 239 | + |
| 240 | +## References |
| 241 | + |
| 242 | +- [VLESS Protocol Specification](https://github.com/XTLS/REALITY) |
| 243 | +- [RFC 7748: Elliptic Curves for Security (X25519)](https://datatracker.ietf.org/doc/html/rfc7748) |
| 244 | +- [RFC 5869: HKDF-SHA256](https://datatracker.ietf.org/doc/html/rfc5869) |
| 245 | +- [RFC 5116: AES-GCM](https://datatracker.ietf.org/doc/html/rfc5116) |
| 246 | + |
| 247 | +## Changelog |
| 248 | + |
| 249 | +### Version 0.23.21 |
| 250 | + |
| 251 | +- Initial implementation of VLESS Reality protocol |
| 252 | +- Support for both `ring` and `aws-lc-rs` crypto providers |
| 253 | +- Public API: `RealityConfig`, `RealityConfigError` |
| 254 | +- Builder method: `ConfigBuilder::with_reality()` |
| 255 | +- Example: `reality-client` binary |
| 256 | +- Comprehensive unit and integration tests |
| 257 | + |
| 258 | +## License |
| 259 | + |
| 260 | +This implementation follows the same license as rustls: Apache-2.0 OR ISC OR MIT. |
0 commit comments