1use crate::aead::{header_keypair, ChaCha8PacketKey, PlaintextHeaderKey};
2use crate::dh::DiffieHellman;
3use crate::keylog::KeyLog;
4use ed25519_dalek::{Keypair, PublicKey};
5use quinn_proto::crypto::{
6 ClientConfig, ExportKeyingMaterialError, HeaderKey, KeyPair, Keys, PacketKey, ServerConfig,
7 Session,
8};
9use quinn_proto::transport_parameters::TransportParameters;
10use quinn_proto::{ConnectError, ConnectionId, Side, TransportError, TransportErrorCode};
11use ring::aead;
12use std::any::Any;
13use std::io::Cursor;
14use std::sync::Arc;
15use subtle::ConstantTimeEq;
16use xoodoo::Xoodyak;
17
18pub struct NoiseClientConfig {
19 pub keypair: Keypair,
21 pub psk: Option<[u8; 32]>,
23 pub keylogger: Option<Arc<dyn KeyLog>>,
25 pub remote_public_key: PublicKey,
27 pub alpn: Vec<u8>,
29}
30
31impl From<NoiseClientConfig> for NoiseConfig {
32 fn from(config: NoiseClientConfig) -> Self {
33 Self {
34 keypair: Some(config.keypair),
35 psk: config.psk,
36 keylogger: config.keylogger,
37 remote_public_key: Some(config.remote_public_key),
38 alpn: Some(config.alpn),
39 supported_protocols: None,
40 }
41 }
42}
43
44pub struct NoiseServerConfig {
45 pub keypair: Keypair,
47 pub psk: Option<[u8; 32]>,
49 pub keylogger: Option<Arc<dyn KeyLog>>,
51 pub supported_protocols: Vec<Vec<u8>>,
53}
54
55impl From<NoiseServerConfig> for NoiseConfig {
56 fn from(config: NoiseServerConfig) -> Self {
57 Self {
58 keypair: Some(config.keypair),
59 psk: config.psk,
60 keylogger: config.keylogger,
61 remote_public_key: None,
62 alpn: None,
63 supported_protocols: Some(config.supported_protocols),
64 }
65 }
66}
67
68#[derive(Default)]
70pub struct NoiseConfig {
71 keypair: Option<Keypair>,
73 psk: Option<[u8; 32]>,
75 keylogger: Option<Arc<dyn KeyLog>>,
77 remote_public_key: Option<PublicKey>,
79 alpn: Option<Vec<u8>>,
81 supported_protocols: Option<Vec<Vec<u8>>>,
83}
84
85impl ClientConfig for NoiseConfig {
86 fn start_session(
87 self: Arc<Self>,
88 version: u32,
89 server_name: &str,
90 params: &TransportParameters,
91 ) -> Result<Box<dyn Session>, ConnectError> {
92 Ok(Box::new(NoiseConfig::start_session(
93 &self,
94 Side::Client,
95 params,
96 )))
97 }
98}
99
100impl ServerConfig for NoiseConfig {
101 fn start_session(
102 self: Arc<Self>,
103 version: u32,
104 params: &TransportParameters,
105 ) -> Box<dyn Session> {
106 Box::new(NoiseConfig::start_session(&self, Side::Server, params))
107 }
108
109 fn initial_keys(
110 &self,
111 version: u32,
112 dst_cid: &ConnectionId,
113 side: Side,
114 ) -> Result<Keys, quinn_proto::crypto::UnsupportedVersion> {
115 Ok(Keys {
116 header: header_keypair(),
117 packet: KeyPair {
118 local: Box::new(ChaCha8PacketKey::new([0; 32])),
119 remote: Box::new(ChaCha8PacketKey::new([0; 32])),
120 },
121 })
122 }
123
124 fn retry_tag(&self, version: u32, orig_dst_cid: &ConnectionId, packet: &[u8]) -> [u8; 16] {
125 let mut pseudo_packet = Vec::with_capacity(packet.len() + orig_dst_cid.len() + 1);
126 pseudo_packet.push(orig_dst_cid.len() as u8);
127 pseudo_packet.extend_from_slice(orig_dst_cid);
128 pseudo_packet.extend_from_slice(packet);
129
130 let nonce = aead::Nonce::assume_unique_for_key(RETRY_INTEGRITY_NONCE);
131 let key = aead::LessSafeKey::new(
132 aead::UnboundKey::new(&aead::AES_128_GCM, &RETRY_INTEGRITY_KEY).unwrap(),
133 );
134
135 let tag = key
136 .seal_in_place_separate_tag(nonce, aead::Aad::from(pseudo_packet), &mut [])
137 .unwrap();
138 let mut result = [0; 16];
139 result.copy_from_slice(tag.as_ref());
140 result
141 }
142}
143
144impl NoiseConfig {
145 fn start_session(&self, side: Side, params: &TransportParameters) -> NoiseSession {
146 let mut rng = rand_core::OsRng {};
147 let s = if let Some(keypair) = self.keypair.as_ref() {
148 Keypair::from_bytes(&keypair.to_bytes()).unwrap()
149 } else {
150 Keypair::generate(&mut rng)
151 };
152 let e = Keypair::generate(&mut rng);
153 NoiseSession {
154 xoodyak: Xoodyak::hash(),
155 state: State::Initial,
156 side,
157 e,
158 s,
159 psk: self.psk.unwrap_or_default(),
160 alpn: self.alpn.clone(),
161 supported_protocols: self.supported_protocols.clone(),
162 transport_parameters: *params,
163 remote_transport_parameters: None,
164 remote_e: None,
165 remote_s: self.remote_public_key,
166 zero_rtt_key: None,
167 keylogger: self.keylogger.clone(),
168 }
169 }
170}
171
172impl Clone for NoiseConfig {
173 fn clone(&self) -> Self {
174 let keypair = self
175 .keypair
176 .as_ref()
177 .map(|keypair| Keypair::from_bytes(&keypair.to_bytes()).unwrap());
178 Self {
179 keypair,
180 psk: self.psk,
181 keylogger: self.keylogger.clone(),
182 remote_public_key: self.remote_public_key,
183 alpn: self.alpn.clone(),
184 supported_protocols: self.supported_protocols.clone(),
185 }
186 }
187}
188
189pub struct NoiseSession {
190 xoodyak: Xoodyak,
191 state: State,
192 side: Side,
193 e: Keypair,
194 s: Keypair,
195 psk: [u8; 32],
196 alpn: Option<Vec<u8>>,
197 supported_protocols: Option<Vec<Vec<u8>>>,
198 transport_parameters: TransportParameters,
199 remote_transport_parameters: Option<TransportParameters>,
200 remote_e: Option<PublicKey>,
201 remote_s: Option<PublicKey>,
202 zero_rtt_key: Option<ChaCha8PacketKey>,
203 keylogger: Option<Arc<dyn KeyLog>>,
204}
205
206impl NoiseSession {
207 fn conn_id(&self) -> Option<&[u8; 32]> {
208 match self.side {
209 Side::Client => Some(self.e.public.as_bytes()),
210 Side::Server => Some(self.remote_e.as_ref()?.as_bytes()),
211 }
212 }
213}
214
215#[derive(Clone, Copy, Debug, PartialEq)]
216enum State {
217 Initial,
218 ZeroRtt,
219 Handshake,
220 OneRtt,
221 Data,
222}
223
224fn connection_refused(reason: &str) -> TransportError {
225 TransportError {
226 code: TransportErrorCode::CONNECTION_REFUSED,
227 frame: None,
228 reason: reason.to_string(),
229 }
230}
231
232impl NoiseSession {
233 fn next_1rtt_keys0(&mut self) -> KeyPair<ChaCha8PacketKey> {
234 if !self.is_handshaking() {
235 self.xoodyak.ratchet();
236 }
237 let mut client = [0; 32];
238 self.xoodyak.squeeze_key(&mut client);
239 let mut server = [0; 32];
240 self.xoodyak.squeeze_key(&mut server);
241 if let Some(keylogger) = self.keylogger.as_ref() {
242 keylogger.log("CLIENT_KEY", self.conn_id().unwrap(), &client[..]);
243 keylogger.log("SERVER_KEY", self.conn_id().unwrap(), &server[..]);
244 }
245 let client = ChaCha8PacketKey::new(client);
246 let server = ChaCha8PacketKey::new(server);
247 match self.side {
248 Side::Client => KeyPair {
249 local: client,
250 remote: server,
251 },
252 Side::Server => KeyPair {
253 local: server,
254 remote: client,
255 },
256 }
257 }
258}
259
260impl Session for NoiseSession {
261 fn initial_keys(&self, _: &ConnectionId, _: Side) -> Keys {
262 Keys {
263 header: header_keypair(),
264 packet: KeyPair {
265 local: Box::new(ChaCha8PacketKey::new([0; 32])),
266 remote: Box::new(ChaCha8PacketKey::new([0; 32])),
267 },
268 }
269 }
270
271 fn next_1rtt_keys(&mut self) -> Option<KeyPair<Box<dyn PacketKey>>> {
272 let key = self.next_1rtt_keys0();
273 Some(KeyPair {
274 local: Box::new(key.local),
275 remote: Box::new(key.remote),
276 })
277 }
278
279 fn read_handshake(&mut self, handshake: &[u8]) -> Result<bool, TransportError> {
280 tracing::trace!("read_handshake {:?} {:?}", self.state, self.side);
281 match (self.state, self.side) {
282 (State::Initial, Side::Server) => {
283 if handshake.is_empty() {
285 return Err(connection_refused("invalid crypto frame"));
286 }
287 let (len, rest) = handshake.split_at(1);
288 let len = len[0] as usize;
289 if rest.len() < len {
290 return Err(connection_refused("invalid crypto frame"));
291 }
292 let (protocol_id, rest) = rest.split_at(len);
293 if protocol_id != b"Noise_IKpsk1_Edx25519_ChaCha8Poly" {
294 return Err(connection_refused("unsupported protocol id"));
295 }
296 self.xoodyak.absorb(protocol_id);
297 if rest.len() < 32 {
299 return Err(connection_refused("invalid crypto frame"));
300 }
301 let (e, rest) = rest.split_at(32);
302 self.xoodyak.absorb(e);
303 let e = PublicKey::from_bytes(e)
304 .map_err(|_| connection_refused("invalid ephemeral public key"))?;
305 self.remote_e = Some(e);
306 self.xoodyak.absorb(self.s.public.as_bytes());
308 let es = self.s.diffie_hellman(&e);
310 self.xoodyak.absorb(&es);
311 let mut key = [0; 32];
313 self.xoodyak.squeeze(&mut key);
314 self.xoodyak = Xoodyak::keyed(&key, None, None, None);
315 if rest.len() < 32 {
317 return Err(connection_refused("invalid crypto frame"));
318 }
319 let (remote_s, rest) = rest.split_at(32);
320 let mut s = [0; 32];
321 self.xoodyak.decrypt(&remote_s, &mut s);
322 let s = PublicKey::from_bytes(&s)
323 .map_err(|_| connection_refused("invalid static public key"))?;
324 self.remote_s = Some(s);
325 let ss = self.s.diffie_hellman(&s);
327 self.xoodyak.absorb(&ss);
328 self.xoodyak.absorb(&self.psk);
330 if rest.is_empty() {
332 return Err(connection_refused("invalid crypto frame"));
333 }
334 let (len, rest) = rest.split_at(1);
335 let len = len[0] as usize;
336 if rest.len() < len {
337 return Err(connection_refused("invalid crypto frame"));
338 }
339 let (alpn, rest) = rest.split_at(len);
340 let mut alpn = alpn.to_vec();
341 self.xoodyak.decrypt_in_place(&mut alpn);
342 let is_supported = self
343 .supported_protocols
344 .as_ref()
345 .expect("invalid config")
346 .into_iter()
347 .find(|proto| proto.as_slice() == alpn)
348 .is_some();
349 if !is_supported {
350 return Err(connection_refused("unsupported alpn"));
351 }
352 self.alpn = Some(alpn);
353 if rest.len() < 16 {
355 return Err(connection_refused("invalid crypto frame"));
356 }
357 let (params, auth) = rest.split_at(rest.len() - 16);
358 let mut transport_parameters = vec![0; params.len()];
359 self.xoodyak.decrypt(¶ms, &mut transport_parameters);
360 let mut tag = [0; 16];
362 self.xoodyak.squeeze(&mut tag);
363 if !bool::from(tag.ct_eq(&auth)) {
364 return Err(connection_refused("invalid authentication tag"));
365 }
366 self.remote_transport_parameters = Some(TransportParameters::read(
367 Side::Server,
368 &mut Cursor::new(&mut transport_parameters),
369 )?);
370 self.state = State::ZeroRtt;
371 Ok(true)
372 }
373 (State::Handshake, Side::Client) => {
374 if handshake.len() < 32 {
376 return Err(connection_refused("invalid crypto frame"));
377 }
378 let (remote_e, rest) = handshake.split_at(32);
379 let mut e = [0; 32];
380 self.xoodyak.decrypt(&remote_e, &mut e);
381 let e = PublicKey::from_bytes(&e)
382 .map_err(|_| connection_refused("invalid ephemeral public key"))?;
383 self.remote_e = Some(e);
384 let ee = self.e.diffie_hellman(&e);
386 self.xoodyak.absorb(&ee);
387 let se = self.s.diffie_hellman(&e);
389 self.xoodyak.absorb(&se);
390 if rest.len() < 16 {
392 return Err(connection_refused("invalid crypto frame"));
393 }
394 let (params, auth) = rest.split_at(rest.len() - 16);
395 let mut transport_parameters = vec![0; params.len()];
396 self.xoodyak.decrypt(¶ms, &mut transport_parameters);
397 let mut tag = [0; 16];
399 self.xoodyak.squeeze(&mut tag);
400 if !bool::from(tag.ct_eq(&auth)) {
401 return Err(connection_refused("invalid authentication tag"));
402 }
403 self.remote_transport_parameters = Some(TransportParameters::read(
404 Side::Client,
405 &mut Cursor::new(&mut transport_parameters),
406 )?);
407 self.state = State::OneRtt;
408 Ok(true)
409 }
410 _ => Err(TransportError {
411 code: TransportErrorCode::CONNECTION_REFUSED,
412 frame: None,
413 reason: "unexpected crypto frame".to_string(),
414 }),
415 }
416 }
417
418 fn write_handshake(&mut self, handshake: &mut Vec<u8>) -> Option<Keys> {
419 tracing::trace!("write_handshake {:?} {:?}", self.state, self.side);
420 match (self.state, self.side) {
421 (State::Initial, Side::Client) => {
422 let protocol_id = b"Noise_IKpsk1_Edx25519_ChaCha8Poly";
424 self.xoodyak.absorb(protocol_id);
425 handshake.extend_from_slice(&[protocol_id.len() as u8]);
426 handshake.extend_from_slice(protocol_id);
427 self.xoodyak.absorb(self.e.public.as_bytes());
429 handshake.extend_from_slice(self.e.public.as_bytes());
430 let s = self.remote_s.unwrap();
432 self.xoodyak.absorb(s.as_bytes());
433 let es = self.e.diffie_hellman(&s);
435 self.xoodyak.absorb(&es);
436 let mut key = [0; 32];
438 self.xoodyak.squeeze(&mut key);
439 self.xoodyak = Xoodyak::keyed(&key, None, None, None);
440 let mut s = [0; 32];
442 self.xoodyak.encrypt(self.s.public.as_bytes(), &mut s);
443 handshake.extend_from_slice(&s);
444 let s = self.remote_s.unwrap();
446 let ss = self.s.diffie_hellman(&s);
447 self.xoodyak.absorb(&ss);
448 self.xoodyak.absorb(&self.psk);
450 let alpn = self.alpn.as_ref().expect("invalid config");
452 handshake.extend_from_slice(&[alpn.len() as u8]);
453 let pos = handshake.len();
454 handshake.extend_from_slice(alpn);
455 self.xoodyak.encrypt_in_place(&mut handshake[pos..]);
456 let mut transport_parameters = vec![];
458 self.transport_parameters.write(&mut transport_parameters);
459 self.xoodyak.encrypt_in_place(&mut transport_parameters);
460 handshake.extend_from_slice(&transport_parameters);
461 let mut tag = [0; 16];
463 self.xoodyak.squeeze(&mut tag);
464 handshake.extend_from_slice(&tag);
465 self.state = State::ZeroRtt;
467 None
468 }
469 (State::ZeroRtt, _) => {
470 let packet = self.next_1rtt_keys0();
471 self.state = State::Handshake;
472 self.zero_rtt_key = Some(packet.local.clone());
473 Some(Keys {
474 header: header_keypair(),
475 packet: KeyPair {
476 local: Box::new(packet.local),
477 remote: Box::new(packet.remote),
478 },
479 })
480 }
481 (State::Handshake, Side::Server) => {
482 let mut e = [0; 32];
484 self.xoodyak.encrypt(self.e.public.as_bytes(), &mut e);
485 handshake.extend_from_slice(&e);
486 let ee = self.e.diffie_hellman(&self.remote_e.unwrap());
488 self.xoodyak.absorb(&ee);
489 let se = self.e.diffie_hellman(&self.remote_s.unwrap());
491 self.xoodyak.absorb(&se);
492 let mut transport_parameters = vec![];
494 self.transport_parameters.write(&mut transport_parameters);
495 self.xoodyak.encrypt_in_place(&mut transport_parameters);
496 handshake.extend_from_slice(&transport_parameters);
497 let mut tag = [0; 16];
499 self.xoodyak.squeeze(&mut tag);
500 handshake.extend_from_slice(&tag);
501 let packet = self.next_1rtt_keys().unwrap();
503 self.state = State::Data;
504 Some(Keys {
505 header: header_keypair(),
506 packet,
507 })
508 }
509 (State::OneRtt, _) => {
510 let packet = self.next_1rtt_keys().unwrap();
511 self.state = State::Data;
512 Some(Keys {
513 header: header_keypair(),
514 packet,
515 })
516 }
517 _ => None,
518 }
519 }
520
521 fn is_handshaking(&self) -> bool {
522 self.state != State::Data
523 }
524
525 fn peer_identity(&self) -> Option<Box<dyn Any>> {
526 Some(Box::new(self.remote_s?))
527 }
528
529 fn transport_parameters(&self) -> Result<Option<TransportParameters>, TransportError> {
530 if self.state == State::Handshake && self.side == Side::Client {
531 Ok(Some(self.transport_parameters))
532 } else {
533 Ok(self.remote_transport_parameters)
534 }
535 }
536
537 fn handshake_data(&self) -> Option<Box<dyn Any>> {
538 Some(Box::new(self.alpn.clone()?))
539 }
540
541 fn export_keying_material(
542 &self,
543 output: &mut [u8],
544 label: &[u8],
545 context: &[u8],
546 ) -> Result<(), ExportKeyingMaterialError> {
547 let mut xoodyak = self.xoodyak.clone();
548 xoodyak.absorb(label);
549 xoodyak.absorb(context);
550 xoodyak.squeeze_key(output);
551 Ok(())
552 }
553
554 fn early_crypto(&self) -> Option<(Box<dyn HeaderKey>, Box<dyn PacketKey>)> {
555 Some((
556 Box::new(PlaintextHeaderKey),
557 Box::new(self.zero_rtt_key.clone()?),
558 ))
559 }
560
561 fn early_data_accepted(&self) -> Option<bool> {
562 Some(true)
563 }
564
565 fn is_valid_retry(&self, orig_dst_cid: &ConnectionId, header: &[u8], payload: &[u8]) -> bool {
566 let tag_start = match payload.len().checked_sub(16) {
567 Some(x) => x,
568 None => return false,
569 };
570
571 let mut pseudo_packet =
572 Vec::with_capacity(header.len() + payload.len() + orig_dst_cid.len() + 1);
573 pseudo_packet.push(orig_dst_cid.len() as u8);
574 pseudo_packet.extend_from_slice(orig_dst_cid);
575 pseudo_packet.extend_from_slice(header);
576 let tag_start = tag_start + pseudo_packet.len();
577 pseudo_packet.extend_from_slice(payload);
578
579 let nonce = aead::Nonce::assume_unique_for_key(RETRY_INTEGRITY_NONCE);
580 let key = aead::LessSafeKey::new(
581 aead::UnboundKey::new(&aead::AES_128_GCM, &RETRY_INTEGRITY_KEY).unwrap(),
582 );
583
584 let (aad, tag) = pseudo_packet.split_at_mut(tag_start);
585 key.open_in_place(nonce, aead::Aad::from(aad), tag).is_ok()
586 }
587}
588
589const RETRY_INTEGRITY_KEY: [u8; 16] = [
590 0xcc, 0xce, 0x18, 0x7e, 0xd0, 0x9a, 0x09, 0xd0, 0x57, 0x28, 0x15, 0x5a, 0x6c, 0xb9, 0x6b, 0xe1,
591];
592const RETRY_INTEGRITY_NONCE: [u8; 12] = [
593 0xe5, 0x49, 0x30, 0xf9, 0x7f, 0x21, 0x36, 0xf0, 0x53, 0x0a, 0x8c, 0x1c,
594];