quinn_noise/
session.rs

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    /// Keypair to use.
20    pub keypair: Keypair,
21    /// Optional private shared key usable as a password for private networks.
22    pub psk: Option<[u8; 32]>,
23    /// Enables keylogging for debugging purposes to the path provided by `SSLKEYLOGFILE`.
24    pub keylogger: Option<Arc<dyn KeyLog>>,
25    /// The remote public key. This needs to be set.
26    pub remote_public_key: PublicKey,
27    /// ALPN string to use.
28    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    /// Keypair to use.
46    pub keypair: Keypair,
47    /// Optional private shared key usable as a password for private networks.
48    pub psk: Option<[u8; 32]>,
49    /// Enables keylogging for debugging purposes to the path provided by `SSLKEYLOGFILE`.
50    pub keylogger: Option<Arc<dyn KeyLog>>,
51    /// Supported ALPN identifiers.
52    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/// Noise configuration struct.
69#[derive(Default)]
70pub struct NoiseConfig {
71    /// Keypair to use.
72    keypair: Option<Keypair>,
73    /// Optional private shared key usable as a password for private networks.
74    psk: Option<[u8; 32]>,
75    /// Enables keylogging for debugging purposes to the path provided by `SSLKEYLOGFILE`.
76    keylogger: Option<Arc<dyn KeyLog>>,
77    /// The remote public key. This needs to be set.
78    remote_public_key: Option<PublicKey>,
79    /// ALPN string to use.
80    alpn: Option<Vec<u8>>,
81    /// Supported ALPN identifiers.
82    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                // protocol identifier
284                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                // e
298                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                // s
307                self.xoodyak.absorb(self.s.public.as_bytes());
308                // es
309                let es = self.s.diffie_hellman(&e);
310                self.xoodyak.absorb(&es);
311                // initialize keyed session transcript
312                let mut key = [0; 32];
313                self.xoodyak.squeeze(&mut key);
314                self.xoodyak = Xoodyak::keyed(&key, None, None, None);
315                // s
316                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                // ss
326                let ss = self.s.diffie_hellman(&s);
327                self.xoodyak.absorb(&ss);
328                // psk
329                self.xoodyak.absorb(&self.psk);
330                // alpn
331                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                // transport parameters
354                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(&params, &mut transport_parameters);
360                // check tag
361                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                // e
375                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                // ee
385                let ee = self.e.diffie_hellman(&e);
386                self.xoodyak.absorb(&ee);
387                // se
388                let se = self.s.diffie_hellman(&e);
389                self.xoodyak.absorb(&se);
390                // transport parameters
391                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(&params, &mut transport_parameters);
397                // check tag
398                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                // protocol identifier
423                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                // e
428                self.xoodyak.absorb(self.e.public.as_bytes());
429                handshake.extend_from_slice(self.e.public.as_bytes());
430                // s
431                let s = self.remote_s.unwrap();
432                self.xoodyak.absorb(s.as_bytes());
433                // es
434                let es = self.e.diffie_hellman(&s);
435                self.xoodyak.absorb(&es);
436                // initialize keyed session transcript
437                let mut key = [0; 32];
438                self.xoodyak.squeeze(&mut key);
439                self.xoodyak = Xoodyak::keyed(&key, None, None, None);
440                // s
441                let mut s = [0; 32];
442                self.xoodyak.encrypt(self.s.public.as_bytes(), &mut s);
443                handshake.extend_from_slice(&s);
444                // ss
445                let s = self.remote_s.unwrap();
446                let ss = self.s.diffie_hellman(&s);
447                self.xoodyak.absorb(&ss);
448                // psk
449                self.xoodyak.absorb(&self.psk);
450                // alpn
451                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                // transport parameters
457                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                // tag
462                let mut tag = [0; 16];
463                self.xoodyak.squeeze(&mut tag);
464                handshake.extend_from_slice(&tag);
465                // 0-rtt
466                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                // e
483                let mut e = [0; 32];
484                self.xoodyak.encrypt(self.e.public.as_bytes(), &mut e);
485                handshake.extend_from_slice(&e);
486                // ee
487                let ee = self.e.diffie_hellman(&self.remote_e.unwrap());
488                self.xoodyak.absorb(&ee);
489                // se
490                let se = self.e.diffie_hellman(&self.remote_s.unwrap());
491                self.xoodyak.absorb(&se);
492                // transport parameters
493                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                // tag
498                let mut tag = [0; 16];
499                self.xoodyak.squeeze(&mut tag);
500                handshake.extend_from_slice(&tag);
501                // 1-rtt keys
502                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];