Skip to content

Commit e731794

Browse files
authored
Add alpn identifier. (#3)
* Add alpn identifier. * Support multiple protocols.
1 parent fa600b4 commit e731794

File tree

3 files changed

+181
-27
lines changed

3 files changed

+181
-27
lines changed

README.md

+16-4
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,20 @@ fast authenticated encryption a chacha8poly1305 cipher is used.
3232

3333
## Session
3434

35-
The initial packet contains the ephemeral client public key, the encrypted client static public key
36-
and the encrypted client transport parameters. After the initial packet 0-rtt packets can be sent
37-
using the `initiator-0rtt-key` without having to wait for a response from the server.
35+
The session transcript is initialized with the protocol identifier. The client ephemeral and server
36+
static public keys, and the es dh are absorbed into the session transcript. From this session transcript
37+
a session identifier is extracted which is used to initialize a keyed session transcript as per the
38+
xoodyak paper section 3.3 authenticated encryption with a common secret. The encrypted client static
39+
public key, the ss dh and the psk which defaults to [0; 32] if none was provided are added to the
40+
session transcript. Then the encrypted ALPN string used to identify the application protocol and the
41+
encrypted client transport parameters as defined by the quic spec are added to the transcript. Common
42+
ALPN strings are `h3` for web or `libp2p` for libp2p applications. Finally an authentication tag for
43+
the crypto frame and 0-rtt keys are extracted.
44+
45+
The initial packet's crypto frame contains the protocol identifier, the client ephemeral public
46+
key, the encrypted client static public key, the encrypted ALPN identifier, the encrypted client
47+
transport parameters and an authentication tag. After the initial packet 0-rtt packets can be sent
48+
using the extracted 0-rtt key without having to wait for a response from the server.
3849

3950
```
4051
Initial:
@@ -48,6 +59,7 @@ p | Absorb(e)
4859
c | Encrypt(s)
4960
| Absorb(ss)
5061
| Absorb(psk)
62+
c | Encrypt(alpn)
5163
c | Encrypt(client_transport_parameters)
5264
t | Squeeze(16)
5365
| initiator-0rtt-key = SqueezeKey(32)
@@ -70,7 +82,7 @@ t | Squeeze(16)
7082
```
7183

7284
During the transport session the 1-rtt keys might need to be rotated. This happens when approaching
73-
`u64::MAX` sent packets or if requested by the other party. See the quic spec for details.
85+
`u64::MAX` sent packets or if forced by the connection. See the quic spec for details.
7486

7587
```
7688
Key rotation:

src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ mod session;
55

66
pub use crate::aead::ChaCha8PacketKey;
77
pub use crate::keylog::{KeyLog, KeyLogFile};
8-
pub use crate::session::{NoiseConfig, NoiseSession};
8+
pub use crate::session::{NoiseConfig, NoiseClientConfig, NoiseServerConfig, NoiseSession};
99
pub use ed25519_dalek::{Keypair, PublicKey, SecretKey};
1010

1111
// https://github.com/quicwg/base-drafts/wiki/QUIC-Versions

src/session.rs

+164-22
Original file line numberDiff line numberDiff line change
@@ -13,34 +13,73 @@ use std::sync::Arc;
1313
use subtle::ConstantTimeEq;
1414
use xoodoo::Xoodyak;
1515

16-
/// Noise configuration struct.
17-
#[derive(Default)]
18-
pub struct NoiseConfig {
19-
/// Keypair to use. If none is provided one will be generated.
20-
pub keypair: Option<Keypair>,
16+
pub struct NoiseClientConfig {
17+
/// Keypair to use.
18+
pub keypair: Keypair,
19+
/// Optional private shared key usable as a password for private networks.
20+
pub psk: Option<[u8; 32]>,
21+
/// Enables keylogging for debugging purposes to the path provided by `SSLKEYLOGFILE`.
22+
pub keylogger: Option<Arc<dyn KeyLog>>,
2123
/// The remote public key. This needs to be set.
22-
pub remote_public_key: Option<PublicKey>,
24+
pub remote_public_key: PublicKey,
25+
/// ALPN string to use.
26+
pub alpn: Vec<u8>,
27+
}
28+
29+
impl From<NoiseClientConfig> for NoiseConfig {
30+
fn from(config: NoiseClientConfig) -> Self {
31+
Self {
32+
keypair: Some(config.keypair),
33+
psk: config.psk,
34+
keylogger: config.keylogger,
35+
remote_public_key: Some(config.remote_public_key),
36+
alpn: Some(config.alpn),
37+
supported_protocols: None,
38+
}
39+
}
40+
}
41+
42+
pub struct NoiseServerConfig {
43+
/// Keypair to use.
44+
pub keypair: Keypair,
2345
/// Optional private shared key usable as a password for private networks.
2446
pub psk: Option<[u8; 32]>,
2547
/// Enables keylogging for debugging purposes to the path provided by `SSLKEYLOGFILE`.
2648
pub keylogger: Option<Arc<dyn KeyLog>>,
49+
/// Supported ALPN identifiers.
50+
pub supported_protocols: Vec<Vec<u8>>,
2751
}
2852

29-
impl Clone for NoiseConfig {
30-
fn clone(&self) -> Self {
31-
let keypair = self
32-
.keypair
33-
.as_ref()
34-
.map(|keypair| Keypair::from_bytes(&keypair.to_bytes()).unwrap());
53+
impl From<NoiseServerConfig> for NoiseConfig {
54+
fn from(config: NoiseServerConfig) -> Self {
3555
Self {
36-
keypair,
37-
remote_public_key: self.remote_public_key,
38-
psk: self.psk,
39-
keylogger: self.keylogger.clone(),
56+
keypair: Some(config.keypair),
57+
psk: config.psk,
58+
keylogger: config.keylogger,
59+
remote_public_key: None,
60+
alpn: None,
61+
supported_protocols: Some(config.supported_protocols),
4062
}
4163
}
4264
}
4365

66+
/// Noise configuration struct.
67+
#[derive(Default)]
68+
pub struct NoiseConfig {
69+
/// Keypair to use.
70+
keypair: Option<Keypair>,
71+
/// Optional private shared key usable as a password for private networks.
72+
psk: Option<[u8; 32]>,
73+
/// Enables keylogging for debugging purposes to the path provided by `SSLKEYLOGFILE`.
74+
keylogger: Option<Arc<dyn KeyLog>>,
75+
/// The remote public key. This needs to be set.
76+
remote_public_key: Option<PublicKey>,
77+
/// ALPN string to use.
78+
alpn: Option<Vec<u8>>,
79+
/// Supported ALPN identifiers.
80+
supported_protocols: Option<Vec<Vec<u8>>>,
81+
}
82+
4483
impl ClientConfig<NoiseSession> for NoiseConfig {
4584
fn new() -> Self {
4685
Default::default()
@@ -81,6 +120,8 @@ impl NoiseConfig {
81120
e,
82121
s,
83122
psk: self.psk.unwrap_or_default(),
123+
alpn: self.alpn.clone(),
124+
supported_protocols: self.supported_protocols.clone(),
84125
transport_parameters: *params,
85126
remote_transport_parameters: None,
86127
remote_e: None,
@@ -91,13 +132,32 @@ impl NoiseConfig {
91132
}
92133
}
93134

135+
impl Clone for NoiseConfig {
136+
fn clone(&self) -> Self {
137+
let keypair = self
138+
.keypair
139+
.as_ref()
140+
.map(|keypair| Keypair::from_bytes(&keypair.to_bytes()).unwrap());
141+
Self {
142+
keypair,
143+
psk: self.psk,
144+
keylogger: self.keylogger.clone(),
145+
remote_public_key: self.remote_public_key,
146+
alpn: self.alpn.clone(),
147+
supported_protocols: self.supported_protocols.clone(),
148+
}
149+
}
150+
}
151+
94152
pub struct NoiseSession {
95153
xoodyak: Xoodyak,
96154
state: State,
97155
side: Side,
98156
e: Keypair,
99157
s: Keypair,
100158
psk: [u8; 32],
159+
alpn: Option<Vec<u8>>,
160+
supported_protocols: Option<Vec<Vec<u8>>>,
101161
transport_parameters: TransportParameters,
102162
remote_transport_parameters: Option<TransportParameters>,
103163
remote_e: Option<PublicKey>,
@@ -133,7 +193,7 @@ fn connection_refused(reason: &str) -> TransportError {
133193
}
134194

135195
impl Session for NoiseSession {
136-
type HandshakeData = ();
196+
type HandshakeData = Vec<u8>;
137197
type Identity = PublicKey;
138198
type ClientConfig = NoiseConfig;
139199
type ServerConfig = Arc<NoiseConfig>;
@@ -183,35 +243,84 @@ impl Session for NoiseSession {
183243
tracing::trace!("read_handshake {:?} {:?}", self.state, self.side);
184244
match (self.state, self.side) {
185245
(State::Initial, Side::Server) => {
246+
// protocol identifier
247+
if handshake.is_empty() {
248+
return Err(connection_refused("invalid crypto frame"));
249+
}
186250
let (len, rest) = handshake.split_at(1);
187-
let (protocol_id, rest) = rest.split_at(len[0] as usize);
251+
let len = len[0] as usize;
252+
if rest.len() < len {
253+
return Err(connection_refused("invalid crypto frame"));
254+
}
255+
let (protocol_id, rest) = rest.split_at(len);
188256
if protocol_id != b"Noise_IKpsk1_Edx25519_ChaCha8Poly" {
189257
return Err(connection_refused("unsupported protocol id"));
190258
}
191-
let (e, rest) = rest.split_at(32);
192259
self.xoodyak.absorb(protocol_id);
260+
// e
261+
if rest.len() < 32 {
262+
return Err(connection_refused("invalid crypto frame"));
263+
}
264+
let (e, rest) = rest.split_at(32);
193265
self.xoodyak.absorb(e);
194-
self.xoodyak.absorb(self.s.public.as_bytes());
195266
let e = PublicKey::from_bytes(e)
196267
.map_err(|_| connection_refused("invalid ephemeral public key"))?;
197268
self.remote_e = Some(e);
269+
// s
270+
self.xoodyak.absorb(self.s.public.as_bytes());
271+
// es
198272
let es = self.s.diffie_hellman(&e);
199273
self.xoodyak.absorb(&es);
274+
// initialize keyed session transcript
200275
let mut key = [0; 32];
201276
self.xoodyak.squeeze(&mut key);
202277
self.xoodyak = Xoodyak::keyed(&key, None, None, None);
278+
// s
279+
if rest.len() < 32 {
280+
return Err(connection_refused("invalid crypto frame"));
281+
}
203282
let (remote_s, rest) = rest.split_at(32);
204283
let mut s = [0; 32];
205284
self.xoodyak.decrypt(&remote_s, &mut s);
206285
let s = PublicKey::from_bytes(&s)
207286
.map_err(|_| connection_refused("invalid static public key"))?;
208287
self.remote_s = Some(s);
288+
// ss
209289
let ss = self.s.diffie_hellman(&s);
210290
self.xoodyak.absorb(&ss);
291+
// psk
211292
self.xoodyak.absorb(&self.psk);
293+
// alpn
294+
if rest.is_empty() {
295+
return Err(connection_refused("invalid crypto frame"));
296+
}
297+
let (len, rest) = rest.split_at(1);
298+
let len = len[0] as usize;
299+
if rest.len() < len {
300+
return Err(connection_refused("invalid crypto frame"));
301+
}
302+
let (alpn, rest) = rest.split_at(len);
303+
let mut alpn = alpn.to_vec();
304+
self.xoodyak.decrypt_in_place(&mut alpn);
305+
let is_supported = self
306+
.supported_protocols
307+
.as_ref()
308+
.expect("invalid config")
309+
.into_iter()
310+
.find(|proto| proto.as_slice() == alpn)
311+
.is_some();
312+
if !is_supported {
313+
return Err(connection_refused("unsupported alpn"));
314+
}
315+
self.alpn = Some(alpn);
316+
// transport parameters
317+
if rest.len() < 16 {
318+
return Err(connection_refused("invalid crypto frame"));
319+
}
212320
let (params, auth) = rest.split_at(rest.len() - 16);
213321
let mut transport_parameters = vec![0; params.len()];
214322
self.xoodyak.decrypt(&params, &mut transport_parameters);
323+
// check tag
215324
let mut tag = [0; 16];
216325
self.xoodyak.squeeze(&mut tag);
217326
if !bool::from(tag.ct_eq(&auth)) {
@@ -225,19 +334,30 @@ impl Session for NoiseSession {
225334
Ok(true)
226335
}
227336
(State::Handshake, Side::Client) => {
337+
// e
338+
if handshake.len() < 32 {
339+
return Err(connection_refused("invalid crypto frame"));
340+
}
228341
let (remote_e, rest) = handshake.split_at(32);
229342
let mut e = [0; 32];
230343
self.xoodyak.decrypt(&remote_e, &mut e);
231344
let e = PublicKey::from_bytes(&e)
232345
.map_err(|_| connection_refused("invalid ephemeral public key"))?;
233346
self.remote_e = Some(e);
347+
// ee
234348
let ee = self.e.diffie_hellman(&e);
235349
self.xoodyak.absorb(&ee);
350+
// se
236351
let se = self.s.diffie_hellman(&e);
237352
self.xoodyak.absorb(&se);
353+
// transport parameters
354+
if rest.len() < 16 {
355+
return Err(connection_refused("invalid crypto frame"));
356+
}
238357
let (params, auth) = rest.split_at(rest.len() - 16);
239358
let mut transport_parameters = vec![0; params.len()];
240359
self.xoodyak.decrypt(&params, &mut transport_parameters);
360+
// check tag
241361
let mut tag = [0; 16];
242362
self.xoodyak.squeeze(&mut tag);
243363
if !bool::from(tag.ct_eq(&auth)) {
@@ -262,34 +382,50 @@ impl Session for NoiseSession {
262382
tracing::trace!("write_handshake {:?} {:?}", self.state, self.side);
263383
match (self.state, self.side) {
264384
(State::Initial, Side::Client) => {
385+
// protocol identifier
265386
let protocol_id = b"Noise_IKpsk1_Edx25519_ChaCha8Poly";
266387
self.xoodyak.absorb(protocol_id);
267388
handshake.extend_from_slice(&[protocol_id.len() as u8]);
268389
handshake.extend_from_slice(protocol_id);
390+
// e
269391
self.xoodyak.absorb(self.e.public.as_bytes());
270392
handshake.extend_from_slice(self.e.public.as_bytes());
393+
// s
271394
let s = self.remote_s.unwrap();
272395
self.xoodyak.absorb(s.as_bytes());
396+
// es
273397
let es = self.e.diffie_hellman(&s);
274398
self.xoodyak.absorb(&es);
399+
// initialize keyed session transcript
275400
let mut key = [0; 32];
276401
self.xoodyak.squeeze(&mut key);
277402
self.xoodyak = Xoodyak::keyed(&key, None, None, None);
278-
self.state = State::Handshake;
403+
// s
279404
let mut s = [0; 32];
280405
self.xoodyak.encrypt(self.s.public.as_bytes(), &mut s);
281406
handshake.extend_from_slice(&s);
407+
// ss
282408
let s = self.remote_s.unwrap();
283409
let ss = self.s.diffie_hellman(&s);
284410
self.xoodyak.absorb(&ss);
411+
// psk
285412
self.xoodyak.absorb(&self.psk);
413+
// alpn
414+
let alpn = self.alpn.as_ref().expect("invalid config");
415+
handshake.extend_from_slice(&[alpn.len() as u8]);
416+
let pos = handshake.len();
417+
handshake.extend_from_slice(alpn);
418+
self.xoodyak.encrypt_in_place(&mut handshake[pos..]);
419+
// transport parameters
286420
let mut transport_parameters = vec![];
287421
self.transport_parameters.write(&mut transport_parameters);
288422
self.xoodyak.encrypt_in_place(&mut transport_parameters);
289423
handshake.extend_from_slice(&transport_parameters);
424+
// tag
290425
let mut tag = [0; 16];
291426
self.xoodyak.squeeze(&mut tag);
292427
handshake.extend_from_slice(&tag);
428+
// 0-rtt
293429
self.state = State::ZeroRtt;
294430
None
295431
}
@@ -303,20 +439,26 @@ impl Session for NoiseSession {
303439
})
304440
}
305441
(State::Handshake, Side::Server) => {
442+
// e
306443
let mut e = [0; 32];
307444
self.xoodyak.encrypt(self.e.public.as_bytes(), &mut e);
308445
handshake.extend_from_slice(&e);
446+
// ee
309447
let ee = self.e.diffie_hellman(&self.remote_e.unwrap());
310448
self.xoodyak.absorb(&ee);
449+
// se
311450
let se = self.e.diffie_hellman(&self.remote_s.unwrap());
312451
self.xoodyak.absorb(&se);
452+
// transport parameters
313453
let mut transport_parameters = vec![];
314454
self.transport_parameters.write(&mut transport_parameters);
315455
self.xoodyak.encrypt_in_place(&mut transport_parameters);
316456
handshake.extend_from_slice(&transport_parameters);
457+
// tag
317458
let mut tag = [0; 16];
318459
self.xoodyak.squeeze(&mut tag);
319460
handshake.extend_from_slice(&tag);
461+
// 1-rtt keys
320462
let packet = self.next_1rtt_keys();
321463
self.state = State::Data;
322464
Some(Keys {
@@ -353,7 +495,7 @@ impl Session for NoiseSession {
353495
}
354496

355497
fn handshake_data(&self) -> Option<Self::HandshakeData> {
356-
Some(())
498+
self.alpn.clone()
357499
}
358500

359501
fn export_keying_material(

0 commit comments

Comments
 (0)