1#![allow(clippy::integer_arithmetic)]
64
65use crate::{hash::HashValue, hkdf::Hkdf, traits::Uniform as _, x25519};
66use aes_gcm::{
67 aead::{generic_array::GenericArray, Aead, AeadInPlace, Payload},
68 Aes256Gcm, KeyInit,
69};
70use sha2::Digest;
71use std::{
72 convert::TryFrom as _,
73 io::{Cursor, Read as _, Write as _},
74};
75use thiserror::Error;
76
77pub const MAX_SIZE_NOISE_MSG: usize = 65535;
84
85pub const AES_GCM_TAGLEN: usize = 16;
87
88const PROTOCOL_NAME: &[u8] = b"Noise_IK_25519_AESGCM_SHA256\0\0\0\0";
90
91const AES_NONCE_SIZE: usize = 12;
93
94pub const fn encrypted_len(plaintext_len: usize) -> usize {
96 plaintext_len + AES_GCM_TAGLEN
97}
98
99pub const fn decrypted_len(ciphertext_len: usize) -> usize {
101 ciphertext_len - AES_GCM_TAGLEN
102}
103
104pub const fn handshake_init_msg_len(payload_len: usize) -> usize {
106 let e_len = x25519::PUBLIC_KEY_SIZE;
108 let enc_s_len = encrypted_len(x25519::PUBLIC_KEY_SIZE);
110 let enc_payload_len = encrypted_len(payload_len);
112 e_len + enc_s_len + enc_payload_len
114}
115
116pub const fn handshake_resp_msg_len(payload_len: usize) -> usize {
118 let e_len = x25519::PUBLIC_KEY_SIZE;
120 let enc_payload_len = encrypted_len(payload_len);
122 e_len + enc_payload_len
124}
125
126#[rustfmt::skip]
128const _: [(); 32] = [(); HashValue::LENGTH];
129
130#[derive(Debug, Error)]
138pub enum NoiseError {
139 #[error(
141 "noise: the received message is too short to contain the expected data"
142 )]
143 MsgTooShort,
144
145 #[error("noise: HKDF has failed")]
147 Hkdf,
148
149 #[error("noise: encryption has failed")]
152 Encrypt,
153
154 #[error("noise: could not decrypt the received data")]
157 Decrypt,
158
159 #[error("noise: the public key received is of the wrong format")]
161 WrongPublicKeyReceived,
162
163 #[error("noise: session was closed due to decrypt error")]
165 SessionClosed,
166
167 #[error("noise: the payload that we are trying to send is too large")]
169 PayloadTooLarge,
170
171 #[error("noise: the message we received is too large")]
173 ReceivedMsgTooLarge,
174
175 #[error("noise: the response buffer passed as argument is too small")]
177 ResponseBufferTooSmall,
178
179 #[error("noise: the nonce exceeds the maximum u64 value")]
182 NonceOverflow,
183}
184
185fn hash(data: &[u8]) -> Vec<u8> { sha2::Sha256::digest(data).to_vec() }
191
192fn hkdf(
193 ck: &[u8], dh_output: Option<&[u8]>,
194) -> Result<(Vec<u8>, Vec<u8>), NoiseError> {
195 let dh_output = dh_output.unwrap_or_else(|| &[]);
196 let hkdf_output = Hkdf::<sha2::Sha256>::extract_then_expand(
197 Some(ck),
198 dh_output,
199 None,
200 64,
201 );
202
203 let hkdf_output = hkdf_output.map_err(|_| NoiseError::Hkdf)?;
204 let (k1, k2) = hkdf_output.split_at(32);
205 Ok((k1.to_vec(), k2.to_vec()))
206}
207
208fn mix_hash(h: &mut Vec<u8>, data: &[u8]) {
209 h.extend_from_slice(data);
210 *h = hash(h);
211}
212
213fn mix_key(ck: &mut Vec<u8>, dh_output: &[u8]) -> Result<Vec<u8>, NoiseError> {
214 let (new_ck, k) = hkdf(ck, Some(dh_output))?;
215 *ck = new_ck;
216 Ok(k)
217}
218
219#[derive(Debug)]
226pub struct NoiseConfig {
227 private_key: x25519::PrivateKey,
228 public_key: x25519::PublicKey,
229}
230
231#[cfg_attr(test, derive(Clone))]
234pub struct InitiatorHandshakeState {
235 h: Vec<u8>,
237 ck: Vec<u8>,
239 e: x25519::PrivateKey,
241 rs: x25519::PublicKey,
243}
244
245#[cfg_attr(test, derive(Clone))]
248pub struct ResponderHandshakeState {
249 h: Vec<u8>,
251 ck: Vec<u8>,
253 rs: x25519::PublicKey,
255 re: x25519::PublicKey,
257}
258
259impl NoiseConfig {
260 pub fn new(private_key: x25519::PrivateKey) -> Self {
263 let public_key = private_key.public_key();
266 Self {
267 private_key,
268 public_key,
269 }
270 }
271
272 pub fn public_key(&self) -> x25519::PublicKey { self.public_key }
274
275 pub fn initiate_connection(
282 &self, rng: &mut (impl rand::RngCore + rand::CryptoRng),
283 prologue: &[u8], remote_public: x25519::PublicKey,
284 payload: Option<&[u8]>, response_buffer: &mut [u8],
285 ) -> Result<InitiatorHandshakeState, NoiseError> {
286 let payload_len = payload.map(<[u8]>::len).unwrap_or(0);
288 let buffer_size_required = handshake_init_msg_len(payload_len);
289 if buffer_size_required > MAX_SIZE_NOISE_MSG {
290 return Err(NoiseError::PayloadTooLarge);
291 }
292 if response_buffer.len() < buffer_size_required {
293 return Err(NoiseError::ResponseBufferTooSmall);
294 }
295 let mut h = PROTOCOL_NAME.to_vec();
297 let mut ck = PROTOCOL_NAME.to_vec();
298 let rs = remote_public; mix_hash(&mut h, &prologue);
300 mix_hash(&mut h, rs.as_slice());
301
302 let e = x25519::PrivateKey::generate(rng);
304 let e_pub = e.public_key();
305
306 mix_hash(&mut h, e_pub.as_slice());
307 let mut response_buffer = Cursor::new(response_buffer);
308 response_buffer
309 .write(e_pub.as_slice())
310 .map_err(|_| NoiseError::ResponseBufferTooSmall)?;
311
312 let dh_output = e.diffie_hellman(&rs);
314 let k = mix_key(&mut ck, &dh_output)?;
315
316 let aead = Aes256Gcm::new(GenericArray::from_slice(&k));
318
319 let msg_and_ad = Payload {
320 msg: self.public_key.as_slice(),
321 aad: &h,
322 };
323 let nonce = GenericArray::from_slice(&[0u8; AES_NONCE_SIZE]);
324 let encrypted_static = aead
325 .encrypt(nonce, msg_and_ad)
326 .map_err(|_| NoiseError::Encrypt)?;
327
328 mix_hash(&mut h, &encrypted_static);
329 response_buffer
330 .write(&encrypted_static)
331 .map_err(|_| NoiseError::ResponseBufferTooSmall)?;
332
333 let dh_output = self.private_key.diffie_hellman(&rs);
335 let k = mix_key(&mut ck, &dh_output)?;
336
337 let aead = Aes256Gcm::new(GenericArray::from_slice(&k));
339
340 let msg_and_ad = Payload {
341 msg: payload.unwrap_or_else(|| &[]),
342 aad: &h,
343 };
344 let nonce = GenericArray::from_slice(&[0u8; AES_NONCE_SIZE]);
345 let encrypted_payload = aead
346 .encrypt(nonce, msg_and_ad)
347 .map_err(|_| NoiseError::Encrypt)?;
348
349 mix_hash(&mut h, &encrypted_payload);
350
351 response_buffer
352 .write(&encrypted_payload)
353 .map_err(|_| NoiseError::ResponseBufferTooSmall)?;
354
355 let handshake_state = InitiatorHandshakeState { h, ck, e, rs };
357 Ok(handshake_state)
358 }
359
360 pub fn finalize_connection(
363 &self, handshake_state: InitiatorHandshakeState,
364 received_message: &[u8],
365 ) -> Result<(Vec<u8>, NoiseSession), NoiseError> {
366 if received_message.len() > MAX_SIZE_NOISE_MSG {
368 return Err(NoiseError::ReceivedMsgTooLarge);
369 }
370 let InitiatorHandshakeState {
372 mut h,
373 mut ck,
374 e,
375 rs,
376 } = handshake_state;
377
378 let mut re = [0u8; x25519::PUBLIC_KEY_SIZE];
380 let mut cursor = Cursor::new(received_message);
381 cursor
382 .read_exact(&mut re)
383 .map_err(|_| NoiseError::MsgTooShort)?;
384 mix_hash(&mut h, &re);
385 let re = x25519::PublicKey::from(re);
386
387 let dh_output = e.diffie_hellman(&re);
389 mix_key(&mut ck, &dh_output)?;
390
391 let dh_output = self.private_key.diffie_hellman(&re);
393 let k = mix_key(&mut ck, &dh_output)?;
394
395 let offset = cursor.position() as usize;
397 let received_encrypted_payload = &cursor.into_inner()[offset..];
398
399 let aead = Aes256Gcm::new(GenericArray::from_slice(&k));
400
401 let nonce = GenericArray::from_slice(&[0u8; AES_NONCE_SIZE]);
402 let ct_and_ad = Payload {
403 msg: received_encrypted_payload,
404 aad: &h,
405 };
406 let received_payload = aead
407 .decrypt(nonce, ct_and_ad)
408 .map_err(|_| NoiseError::Decrypt)?;
409
410 let (k1, k2) = hkdf(&ck, None)?;
412 let session = NoiseSession::new(k1, k2, rs);
413
414 Ok((received_payload, session))
416 }
417
418 pub fn parse_client_init_message(
434 &self, prologue: &[u8], received_message: &[u8],
435 ) -> Result<
436 (
437 x25519::PublicKey, ResponderHandshakeState, Vec<u8>, ),
441 NoiseError,
442 > {
443 if received_message.len() > MAX_SIZE_NOISE_MSG {
445 return Err(NoiseError::ReceivedMsgTooLarge);
446 }
447 let mut h = PROTOCOL_NAME.to_vec();
449 let mut ck = PROTOCOL_NAME.to_vec();
450 mix_hash(&mut h, prologue);
451 mix_hash(&mut h, self.public_key.as_slice());
452
453 let mut cursor = Cursor::new(received_message);
455
456 let mut re = [0u8; x25519::PUBLIC_KEY_SIZE];
458 cursor
459 .read_exact(&mut re)
460 .map_err(|_| NoiseError::MsgTooShort)?;
461 mix_hash(&mut h, &re);
462 let re = x25519::PublicKey::from(re);
463
464 let dh_output = self.private_key.diffie_hellman(&re);
466 let k = mix_key(&mut ck, &dh_output)?;
467
468 let mut encrypted_remote_static =
470 [0u8; x25519::PUBLIC_KEY_SIZE + AES_GCM_TAGLEN];
471 cursor
472 .read_exact(&mut encrypted_remote_static)
473 .map_err(|_| NoiseError::MsgTooShort)?;
474
475 let aead = Aes256Gcm::new(GenericArray::from_slice(&k));
476
477 let nonce = GenericArray::from_slice(&[0u8; AES_NONCE_SIZE]);
478 let ct_and_ad = Payload {
479 msg: &encrypted_remote_static,
480 aad: &h,
481 };
482 let rs = aead
483 .decrypt(nonce, ct_and_ad)
484 .map_err(|_| NoiseError::Decrypt)?;
485 let rs = x25519::PublicKey::try_from(rs.as_slice())
486 .map_err(|_| NoiseError::WrongPublicKeyReceived)?;
487 mix_hash(&mut h, &encrypted_remote_static);
488
489 let dh_output = self.private_key.diffie_hellman(&rs);
491 let k = mix_key(&mut ck, &dh_output)?;
492
493 let offset = cursor.position() as usize;
495 let received_encrypted_payload = &cursor.into_inner()[offset..];
496
497 let aead = Aes256Gcm::new(GenericArray::from_slice(&k));
498
499 let nonce = GenericArray::from_slice(&[0u8; AES_NONCE_SIZE]);
500 let ct_and_ad = Payload {
501 msg: received_encrypted_payload,
502 aad: &h,
503 };
504 let received_payload = aead
505 .decrypt(nonce, ct_and_ad)
506 .map_err(|_| NoiseError::Decrypt)?;
507 mix_hash(&mut h, received_encrypted_payload);
508
509 let handshake_state = ResponderHandshakeState { h, ck, rs, re };
511 Ok((rs, handshake_state, received_payload))
512 }
513
514 pub fn respond_to_client(
517 &self, rng: &mut (impl rand::RngCore + rand::CryptoRng),
518 handshake_state: ResponderHandshakeState, payload: Option<&[u8]>,
519 response_buffer: &mut [u8],
520 ) -> Result<NoiseSession, NoiseError> {
521 let payload_len = payload.map(<[u8]>::len).unwrap_or(0);
523 let buffer_size_required = handshake_resp_msg_len(payload_len);
524 if buffer_size_required > MAX_SIZE_NOISE_MSG {
525 return Err(NoiseError::PayloadTooLarge);
526 }
527 if response_buffer.len() < buffer_size_required {
528 return Err(NoiseError::ResponseBufferTooSmall);
529 }
530
531 let ResponderHandshakeState {
533 mut h,
534 mut ck,
535 rs,
536 re,
537 } = handshake_state;
538
539 let e = x25519::PrivateKey::generate(rng);
541 let e_pub = e.public_key();
542
543 mix_hash(&mut h, e_pub.as_slice());
544 let mut response_buffer = Cursor::new(response_buffer);
545 response_buffer
546 .write(e_pub.as_slice())
547 .map_err(|_| NoiseError::ResponseBufferTooSmall)?;
548
549 let dh_output = e.diffie_hellman(&re);
551 mix_key(&mut ck, &dh_output)?;
552
553 let dh_output = e.diffie_hellman(&rs);
555 let k = mix_key(&mut ck, &dh_output)?;
556
557 let aead = Aes256Gcm::new(GenericArray::from_slice(&k));
559
560 let msg_and_ad = Payload {
561 msg: payload.unwrap_or_else(|| &[]),
562 aad: &h,
563 };
564 let nonce = GenericArray::from_slice(&[0u8; AES_NONCE_SIZE]);
565 let encrypted_payload = aead
566 .encrypt(nonce, msg_and_ad)
567 .map_err(|_| NoiseError::Encrypt)?;
568 mix_hash(&mut h, &encrypted_payload);
569 response_buffer
570 .write(&encrypted_payload)
571 .map_err(|_| NoiseError::ResponseBufferTooSmall)?;
572
573 let (k1, k2) = hkdf(&ck, None)?;
575 let session = NoiseSession::new(k2, k1, rs);
576
577 Ok(session)
579 }
580
581 pub fn respond_to_client_and_finalize(
584 &self, rng: &mut (impl rand::RngCore + rand::CryptoRng),
585 prologue: &[u8], received_message: &[u8], payload: Option<&[u8]>,
586 response_buffer: &mut [u8],
587 ) -> Result<
588 (
589 Vec<u8>, NoiseSession, ),
592 NoiseError,
593 > {
594 let (_, handshake_state, received_payload) =
595 self.parse_client_init_message(prologue, received_message)?;
596 let session = self.respond_to_client(
597 rng,
598 handshake_state,
599 payload,
600 response_buffer,
601 )?;
602 Ok((received_payload, session))
603 }
604}
605
606#[cfg_attr(test, derive(Clone))]
613pub struct NoiseSession {
614 valid: bool,
616 remote_public_key: x25519::PublicKey,
618 write_key: Vec<u8>,
620 write_nonce: u64,
622 read_key: Vec<u8>,
624 read_nonce: u64,
626}
627
628impl NoiseSession {
629 fn new(
630 write_key: Vec<u8>, read_key: Vec<u8>,
631 remote_public_key: x25519::PublicKey,
632 ) -> Self {
633 Self {
634 valid: true,
635 remote_public_key,
636 write_key,
637 write_nonce: 0,
638 read_key,
639 read_nonce: 0,
640 }
641 }
642
643 #[cfg(any(test, feature = "fuzzing"))]
645 pub fn new_for_testing() -> Self {
646 Self::new(
647 vec![0u8; 32],
648 vec![0u8; 32],
649 [0u8; x25519::PUBLIC_KEY_SIZE].into(),
650 )
651 }
652
653 pub fn get_remote_static(&self) -> x25519::PublicKey {
655 self.remote_public_key
656 }
657
658 pub fn write_message_in_place(
662 &mut self, message: &mut [u8],
663 ) -> Result<Vec<u8>, NoiseError> {
664 if !self.valid {
666 return Err(NoiseError::SessionClosed);
667 }
668 if message.len() > MAX_SIZE_NOISE_MSG - AES_GCM_TAGLEN {
669 return Err(NoiseError::PayloadTooLarge);
670 }
671
672 let aead = Aes256Gcm::new(GenericArray::from_slice(&self.write_key));
674 let mut nonce = [0u8; 4].to_vec();
675 nonce.extend_from_slice(&self.write_nonce.to_be_bytes());
676 let nonce = GenericArray::from_slice(&nonce);
677
678 let authentication_tag = aead
679 .encrypt_in_place_detached(nonce, b"", message)
680 .map_err(|_| NoiseError::Encrypt)?;
681
682 self.write_nonce = self
684 .write_nonce
685 .checked_add(1)
686 .ok_or(NoiseError::NonceOverflow)?;
687
688 Ok(authentication_tag.to_vec())
690 }
691
692 pub fn read_message_in_place<'a>(
696 &mut self, message: &'a mut [u8],
697 ) -> Result<&'a [u8], NoiseError> {
698 if !self.valid {
700 return Err(NoiseError::SessionClosed);
701 }
702 if message.len() > MAX_SIZE_NOISE_MSG {
703 self.valid = false;
704 return Err(NoiseError::ReceivedMsgTooLarge);
705 }
706 if message.len() < AES_GCM_TAGLEN {
707 self.valid = false;
708 return Err(NoiseError::ResponseBufferTooSmall);
709 }
710
711 let aead = Aes256Gcm::new(GenericArray::from_slice(&self.read_key));
713
714 let mut nonce = [0u8; 4].to_vec();
715 nonce.extend_from_slice(&self.read_nonce.to_be_bytes());
716 let nonce = GenericArray::from_slice(&nonce);
717
718 let (buffer, authentication_tag) =
719 message.split_at_mut(message.len() - AES_GCM_TAGLEN);
720 let authentication_tag = GenericArray::from_slice(authentication_tag);
721 aead.decrypt_in_place_detached(nonce, b"", buffer, authentication_tag)
722 .map_err(|_| {
723 self.valid = false;
724 NoiseError::Decrypt
725 })?;
726
727 self.read_nonce = self
729 .read_nonce
730 .checked_add(1)
731 .ok_or(NoiseError::NonceOverflow)?;
732
733 Ok(buffer)
735 }
736}
737
738impl std::fmt::Debug for NoiseSession {
739 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
740 write!(f, "NoiseSession[...]")
741 }
742}