1#![allow(clippy::integer_arithmetic)]
59
60use crate::{hash::HashValue, hkdf::Hkdf, traits::Uniform as _, x25519};
61use aes_gcm::{
62 aead::{generic_array::GenericArray, Aead, AeadInPlace, NewAead, Payload},
63 Aes256Gcm,
64};
65use sha2::Digest;
66use std::{
67 convert::TryFrom as _,
68 io::{Cursor, Read as _, Write as _},
69};
70use thiserror::Error;
71
72pub const MAX_SIZE_NOISE_MSG: usize = 65535;
79
80pub const AES_GCM_TAGLEN: usize = 16;
82
83const PROTOCOL_NAME: &[u8] = b"Noise_IK_25519_AESGCM_SHA256\0\0\0\0";
85
86const AES_NONCE_SIZE: usize = 12;
88
89pub const fn encrypted_len(plaintext_len: usize) -> usize {
91 plaintext_len + AES_GCM_TAGLEN
92}
93
94pub const fn decrypted_len(ciphertext_len: usize) -> usize {
96 ciphertext_len - AES_GCM_TAGLEN
97}
98
99pub const fn handshake_init_msg_len(payload_len: usize) -> usize {
101 let e_len = x25519::PUBLIC_KEY_SIZE;
103 let enc_s_len = encrypted_len(x25519::PUBLIC_KEY_SIZE);
105 let enc_payload_len = encrypted_len(payload_len);
107 e_len + enc_s_len + enc_payload_len
109}
110
111pub const fn handshake_resp_msg_len(payload_len: usize) -> usize {
113 let e_len = x25519::PUBLIC_KEY_SIZE;
115 let enc_payload_len = encrypted_len(payload_len);
117 e_len + enc_payload_len
119}
120
121#[rustfmt::skip]
123const _: [(); 32] = [(); HashValue::LENGTH];
124
125#[derive(Debug, Error)]
132pub enum NoiseError {
133 #[error("noise: the received message is too short to contain the expected data")]
135 MsgTooShort,
136
137 #[error("noise: HKDF has failed")]
139 Hkdf,
140
141 #[error("noise: encryption has failed")]
143 Encrypt,
144
145 #[error("noise: could not decrypt the received data")]
147 Decrypt,
148
149 #[error("noise: the public key received is of the wrong format")]
151 WrongPublicKeyReceived,
152
153 #[error("noise: session was closed due to decrypt error")]
155 SessionClosed,
156
157 #[error("noise: the payload that we are trying to send is too large")]
159 PayloadTooLarge,
160
161 #[error("noise: the message we received is too large")]
163 ReceivedMsgTooLarge,
164
165 #[error("noise: the response buffer passed as argument is too small")]
167 ResponseBufferTooSmall,
168
169 #[error("noise: the nonce exceeds the maximum u64 value")]
171 NonceOverflow,
172}
173
174fn hash(data: &[u8]) -> Vec<u8> {
180 sha2::Sha256::digest(data).to_vec()
181}
182
183fn hkdf(ck: &[u8], dh_output: Option<&[u8]>) -> Result<(Vec<u8>, Vec<u8>), NoiseError> {
184 let dh_output = dh_output.unwrap_or_else(|| &[]);
185 let hkdf_output = if dh_output.is_empty() {
186 Hkdf::<sha2::Sha256>::extract_then_expand_no_ikm(Some(ck), None, 64)
187 } else {
188 Hkdf::<sha2::Sha256>::extract_then_expand(Some(ck), dh_output, None, 64)
189 };
190
191 let hkdf_output = hkdf_output.map_err(|_| NoiseError::Hkdf)?;
192 let (k1, k2) = hkdf_output.split_at(32);
193 Ok((k1.to_vec(), k2.to_vec()))
194}
195
196fn mix_hash(h: &mut Vec<u8>, data: &[u8]) {
197 h.extend_from_slice(data);
198 *h = hash(h);
199}
200
201fn mix_key(ck: &mut Vec<u8>, dh_output: &[u8]) -> Result<Vec<u8>, NoiseError> {
202 let (new_ck, k) = hkdf(ck, Some(dh_output))?;
203 *ck = new_ck;
204 Ok(k)
205}
206
207#[derive(Debug)]
214pub struct NoiseConfig {
215 private_key: x25519::PrivateKey,
216 public_key: x25519::PublicKey,
217}
218
219#[cfg_attr(test, derive(Clone))]
221pub struct InitiatorHandshakeState {
222 h: Vec<u8>,
224 ck: Vec<u8>,
226 e: x25519::PrivateKey,
228 rs: x25519::PublicKey,
230}
231
232#[cfg_attr(test, derive(Clone))]
234pub struct ResponderHandshakeState {
235 h: Vec<u8>,
237 ck: Vec<u8>,
239 rs: x25519::PublicKey,
241 re: x25519::PublicKey,
243}
244
245impl NoiseConfig {
246 pub fn new(private_key: x25519::PrivateKey) -> Self {
248 let public_key = private_key.public_key();
250 Self {
251 private_key,
252 public_key,
253 }
254 }
255
256 pub fn public_key(&self) -> x25519::PublicKey {
258 self.public_key
259 }
260
261 pub fn initiate_connection(
267 &self,
268 rng: &mut (impl rand::RngCore + rand::CryptoRng),
269 prologue: &[u8],
270 remote_public: x25519::PublicKey,
271 payload: Option<&[u8]>,
272 response_buffer: &mut [u8],
273 ) -> Result<InitiatorHandshakeState, NoiseError> {
274 let payload_len = payload.map(<[u8]>::len).unwrap_or(0);
276 let buffer_size_required = handshake_init_msg_len(payload_len);
277 if buffer_size_required > MAX_SIZE_NOISE_MSG {
278 return Err(NoiseError::PayloadTooLarge);
279 }
280 if response_buffer.len() < buffer_size_required {
281 return Err(NoiseError::ResponseBufferTooSmall);
282 }
283 let mut h = PROTOCOL_NAME.to_vec();
285 let mut ck = PROTOCOL_NAME.to_vec();
286 let rs = remote_public; mix_hash(&mut h, prologue);
288 mix_hash(&mut h, rs.as_slice());
289
290 let e = x25519::PrivateKey::generate(rng);
292 let e_pub = e.public_key();
293
294 mix_hash(&mut h, e_pub.as_slice());
295 let mut response_buffer = Cursor::new(response_buffer);
296 response_buffer
297 .write(e_pub.as_slice())
298 .map_err(|_| NoiseError::ResponseBufferTooSmall)?;
299
300 let dh_output = e.diffie_hellman(&rs);
302 let k = mix_key(&mut ck, &dh_output)?;
303
304 let aead = Aes256Gcm::new(GenericArray::from_slice(&k));
306
307 let msg_and_ad = Payload {
308 msg: self.public_key.as_slice(),
309 aad: &h,
310 };
311 let nonce = GenericArray::from_slice(&[0u8; AES_NONCE_SIZE]);
312 let encrypted_static = aead
313 .encrypt(nonce, msg_and_ad)
314 .map_err(|_| NoiseError::Encrypt)?;
315
316 mix_hash(&mut h, &encrypted_static);
317 response_buffer
318 .write(&encrypted_static)
319 .map_err(|_| NoiseError::ResponseBufferTooSmall)?;
320
321 let dh_output = self.private_key.diffie_hellman(&rs);
323 let k = mix_key(&mut ck, &dh_output)?;
324
325 let aead = Aes256Gcm::new(GenericArray::from_slice(&k));
327
328 let msg_and_ad = Payload {
329 msg: payload.unwrap_or_else(|| &[]),
330 aad: &h,
331 };
332 let nonce = GenericArray::from_slice(&[0u8; AES_NONCE_SIZE]);
333 let encrypted_payload = aead
334 .encrypt(nonce, msg_and_ad)
335 .map_err(|_| NoiseError::Encrypt)?;
336
337 mix_hash(&mut h, &encrypted_payload);
338
339 response_buffer
340 .write(&encrypted_payload)
341 .map_err(|_| NoiseError::ResponseBufferTooSmall)?;
342
343 let handshake_state = InitiatorHandshakeState { h, ck, e, rs };
345 Ok(handshake_state)
346 }
347
348 pub fn finalize_connection(
350 &self,
351 handshake_state: InitiatorHandshakeState,
352 received_message: &[u8],
353 ) -> Result<(Vec<u8>, NoiseSession), NoiseError> {
354 if received_message.len() > MAX_SIZE_NOISE_MSG {
356 return Err(NoiseError::ReceivedMsgTooLarge);
357 }
358 let InitiatorHandshakeState {
360 mut h,
361 mut ck,
362 e,
363 rs,
364 } = handshake_state;
365
366 let mut re = [0u8; x25519::PUBLIC_KEY_SIZE];
368 let mut cursor = Cursor::new(received_message);
369 cursor
370 .read_exact(&mut re)
371 .map_err(|_| NoiseError::MsgTooShort)?;
372 mix_hash(&mut h, &re);
373 let re = x25519::PublicKey::from(re);
374
375 let dh_output = e.diffie_hellman(&re);
377 mix_key(&mut ck, &dh_output)?;
378
379 let dh_output = self.private_key.diffie_hellman(&re);
381 let k = mix_key(&mut ck, &dh_output)?;
382
383 let offset = cursor.position() as usize;
385 let received_encrypted_payload = &cursor.into_inner()[offset..];
386
387 let aead = Aes256Gcm::new(GenericArray::from_slice(&k));
388
389 let nonce = GenericArray::from_slice(&[0u8; AES_NONCE_SIZE]);
390 let ct_and_ad = Payload {
391 msg: received_encrypted_payload,
392 aad: &h,
393 };
394 let received_payload = aead
395 .decrypt(nonce, ct_and_ad)
396 .map_err(|_| NoiseError::Decrypt)?;
397
398 let (k1, k2) = hkdf(&ck, None)?;
400 let session = NoiseSession::new(k1, k2, rs);
401
402 Ok((received_payload, session))
404 }
405
406 pub fn parse_client_init_message(
420 &self,
421 prologue: &[u8],
422 received_message: &[u8],
423 ) -> Result<
424 (
425 x25519::PublicKey, ResponderHandshakeState, Vec<u8>, ),
429 NoiseError,
430 > {
431 if received_message.len() > MAX_SIZE_NOISE_MSG {
433 return Err(NoiseError::ReceivedMsgTooLarge);
434 }
435 let mut h = PROTOCOL_NAME.to_vec();
437 let mut ck = PROTOCOL_NAME.to_vec();
438 mix_hash(&mut h, prologue);
439 mix_hash(&mut h, self.public_key.as_slice());
440
441 let mut cursor = Cursor::new(received_message);
443
444 let mut re = [0u8; x25519::PUBLIC_KEY_SIZE];
446 cursor
447 .read_exact(&mut re)
448 .map_err(|_| NoiseError::MsgTooShort)?;
449 mix_hash(&mut h, &re);
450 let re = x25519::PublicKey::from(re);
451
452 let dh_output = self.private_key.diffie_hellman(&re);
454 let k = mix_key(&mut ck, &dh_output)?;
455
456 let mut encrypted_remote_static = [0u8; x25519::PUBLIC_KEY_SIZE + AES_GCM_TAGLEN];
458 cursor
459 .read_exact(&mut encrypted_remote_static)
460 .map_err(|_| NoiseError::MsgTooShort)?;
461
462 let aead = Aes256Gcm::new(GenericArray::from_slice(&k));
463
464 let nonce = GenericArray::from_slice(&[0u8; AES_NONCE_SIZE]);
465 let ct_and_ad = Payload {
466 msg: &encrypted_remote_static,
467 aad: &h,
468 };
469 let rs = aead
470 .decrypt(nonce, ct_and_ad)
471 .map_err(|_| NoiseError::Decrypt)?;
472 let rs = x25519::PublicKey::try_from(rs.as_slice())
473 .map_err(|_| NoiseError::WrongPublicKeyReceived)?;
474 mix_hash(&mut h, &encrypted_remote_static);
475
476 let dh_output = self.private_key.diffie_hellman(&rs);
478 let k = mix_key(&mut ck, &dh_output)?;
479
480 let offset = cursor.position() as usize;
482 let received_encrypted_payload = &cursor.into_inner()[offset..];
483
484 let aead = Aes256Gcm::new(GenericArray::from_slice(&k));
485
486 let nonce = GenericArray::from_slice(&[0u8; AES_NONCE_SIZE]);
487 let ct_and_ad = Payload {
488 msg: received_encrypted_payload,
489 aad: &h,
490 };
491 let received_payload = aead
492 .decrypt(nonce, ct_and_ad)
493 .map_err(|_| NoiseError::Decrypt)?;
494 mix_hash(&mut h, received_encrypted_payload);
495
496 let handshake_state = ResponderHandshakeState { h, ck, rs, re };
498 Ok((rs, handshake_state, received_payload))
499 }
500
501 pub fn respond_to_client(
504 &self,
505 rng: &mut (impl rand::RngCore + rand::CryptoRng),
506 handshake_state: ResponderHandshakeState,
507 payload: Option<&[u8]>,
508 response_buffer: &mut [u8],
509 ) -> Result<NoiseSession, NoiseError> {
510 let payload_len = payload.map(<[u8]>::len).unwrap_or(0);
512 let buffer_size_required = handshake_resp_msg_len(payload_len);
513 if buffer_size_required > MAX_SIZE_NOISE_MSG {
514 return Err(NoiseError::PayloadTooLarge);
515 }
516 if response_buffer.len() < buffer_size_required {
517 return Err(NoiseError::ResponseBufferTooSmall);
518 }
519
520 let ResponderHandshakeState {
522 mut h,
523 mut ck,
524 rs,
525 re,
526 } = handshake_state;
527
528 let e = x25519::PrivateKey::generate(rng);
530 let e_pub = e.public_key();
531
532 mix_hash(&mut h, e_pub.as_slice());
533 let mut response_buffer = Cursor::new(response_buffer);
534 response_buffer
535 .write(e_pub.as_slice())
536 .map_err(|_| NoiseError::ResponseBufferTooSmall)?;
537
538 let dh_output = e.diffie_hellman(&re);
540 mix_key(&mut ck, &dh_output)?;
541
542 let dh_output = e.diffie_hellman(&rs);
544 let k = mix_key(&mut ck, &dh_output)?;
545
546 let aead = Aes256Gcm::new(GenericArray::from_slice(&k));
548
549 let msg_and_ad = Payload {
550 msg: payload.unwrap_or_else(|| &[]),
551 aad: &h,
552 };
553 let nonce = GenericArray::from_slice(&[0u8; AES_NONCE_SIZE]);
554 let encrypted_payload = aead
555 .encrypt(nonce, msg_and_ad)
556 .map_err(|_| NoiseError::Encrypt)?;
557 mix_hash(&mut h, &encrypted_payload);
558 response_buffer
559 .write(&encrypted_payload)
560 .map_err(|_| NoiseError::ResponseBufferTooSmall)?;
561
562 let (k1, k2) = hkdf(&ck, None)?;
564 let session = NoiseSession::new(k2, k1, rs);
565
566 Ok(session)
568 }
569
570 pub fn respond_to_client_and_finalize(
573 &self,
574 rng: &mut (impl rand::RngCore + rand::CryptoRng),
575 prologue: &[u8],
576 received_message: &[u8],
577 payload: Option<&[u8]>,
578 response_buffer: &mut [u8],
579 ) -> Result<
580 (
581 Vec<u8>, NoiseSession, ),
584 NoiseError,
585 > {
586 let (_, handshake_state, received_payload) =
587 self.parse_client_init_message(prologue, received_message)?;
588 let session = self.respond_to_client(rng, handshake_state, payload, response_buffer)?;
589 Ok((received_payload, session))
590 }
591}
592
593#[cfg_attr(test, derive(Clone))]
599pub struct NoiseSession {
600 valid: bool,
602 remote_public_key: x25519::PublicKey,
604 write_key: Vec<u8>,
606 write_nonce: u64,
608 read_key: Vec<u8>,
610 read_nonce: u64,
612}
613
614impl NoiseSession {
615 fn new(write_key: Vec<u8>, read_key: Vec<u8>, remote_public_key: x25519::PublicKey) -> Self {
616 Self {
617 valid: true,
618 remote_public_key,
619 write_key,
620 write_nonce: 0,
621 read_key,
622 read_nonce: 0,
623 }
624 }
625
626 #[cfg(any(test, feature = "fuzzing"))]
628 pub fn new_for_testing() -> Self {
629 Self::new(
630 vec![0u8; 32],
631 vec![0u8; 32],
632 [0u8; x25519::PUBLIC_KEY_SIZE].into(),
633 )
634 }
635
636 pub fn get_remote_static(&self) -> x25519::PublicKey {
638 self.remote_public_key
639 }
640
641 pub fn write_message_in_place(&mut self, message: &mut [u8]) -> Result<Vec<u8>, NoiseError> {
644 if !self.valid {
646 return Err(NoiseError::SessionClosed);
647 }
648 if message.len() > MAX_SIZE_NOISE_MSG - AES_GCM_TAGLEN {
649 return Err(NoiseError::PayloadTooLarge);
650 }
651
652 let aead = Aes256Gcm::new(GenericArray::from_slice(&self.write_key));
654 let mut nonce = [0u8; 4].to_vec();
655 nonce.extend_from_slice(&self.write_nonce.to_be_bytes());
656 let nonce = GenericArray::from_slice(&nonce);
657
658 let authentication_tag = aead
659 .encrypt_in_place_detached(nonce, b"", message)
660 .map_err(|_| NoiseError::Encrypt)?;
661
662 self.write_nonce = self
664 .write_nonce
665 .checked_add(1)
666 .ok_or(NoiseError::NonceOverflow)?;
667
668 Ok(authentication_tag.to_vec())
670 }
671
672 pub fn read_message_in_place<'a>(
675 &mut self,
676 message: &'a mut [u8],
677 ) -> Result<&'a [u8], NoiseError> {
678 if !self.valid {
680 return Err(NoiseError::SessionClosed);
681 }
682 if message.len() > MAX_SIZE_NOISE_MSG {
683 self.valid = false;
684 return Err(NoiseError::ReceivedMsgTooLarge);
685 }
686 if message.len() < AES_GCM_TAGLEN {
687 self.valid = false;
688 return Err(NoiseError::ResponseBufferTooSmall);
689 }
690
691 let aead = Aes256Gcm::new(GenericArray::from_slice(&self.read_key));
693
694 let mut nonce = [0u8; 4].to_vec();
695 nonce.extend_from_slice(&self.read_nonce.to_be_bytes());
696 let nonce = GenericArray::from_slice(&nonce);
697
698 let (buffer, authentication_tag) = message.split_at_mut(message.len() - AES_GCM_TAGLEN);
699 let authentication_tag = GenericArray::from_slice(authentication_tag);
700 aead.decrypt_in_place_detached(nonce, b"", buffer, authentication_tag)
701 .map_err(|_| {
702 self.valid = false;
703 NoiseError::Decrypt
704 })?;
705
706 self.read_nonce = self
708 .read_nonce
709 .checked_add(1)
710 .ok_or(NoiseError::NonceOverflow)?;
711
712 Ok(buffer)
714 }
715}
716
717impl std::fmt::Debug for NoiseSession {
718 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
719 write!(f, "NoiseSession[...]")
720 }
721}