Skip to content

Commit fff49eb

Browse files
author
chechunchi
committed
implement vless reality
1 parent 4cae3aa commit fff49eb

12 files changed

Lines changed: 1389 additions & 3 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

REALITY.md

Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
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.

examples/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ publish = false
88

99
[dependencies]
1010
async-std = { workspace = true, optional = true }
11+
base64 = { workspace = true }
1112
clap = { workspace = true }
1213
env_logger = { workspace = true }
1314
hickory-resolver = { workspace = true }

0 commit comments

Comments
 (0)