1234567use snow::{8 Builder, Error, HandshakeState, TransportState,9 params::{CipherChoice, DHChoice, HashChoice, NoiseParams},10 resolvers::{CryptoResolver, DefaultResolver},11 types::{Cipher, Dh, Hash, Random},12};13use subtle::ConstantTimeEq;14use x25519_dalek::{PublicKey, StaticSecret};15use zeroize::{Zeroize, Zeroizing};1617const NK: &str = "Noise_NK_25519_ChaChaPoly_BLAKE2s";18const KK: &str = "Noise_KK_25519_ChaChaPoly_BLAKE2s";19const HASH_LEN: usize = 32;20const TAG_LEN: usize = 16;21const MAX_NOISE_MESSAGE: usize = 65_535;2223type CoreResult<T> = Result<T, String>;2425enum SessionState {26 Handshake(Option<HandshakeState>),27 Transport(TransportState),28 Closed,29}3031pub struct CoreNoiseSession {32 state: SessionState,33 handshake_hash: Option<[u8; HASH_LEN]>,34}3536impl CoreNoiseSession {37 pub fn nk_initiator(remote_public_key: &[u8]) -> CoreResult<Self> {38 Self::nk_initiator_inner(remote_public_key, None)39 }4041 #[doc(hidden)]42 pub fn nk_initiator_with_ephemeral_for_test(43 remote_public_key: &[u8],44 ephemeral_private_key: &[u8],45 ) -> CoreResult<Self> {46 Self::nk_initiator_inner(remote_public_key, Some(ephemeral_private_key))47 }4849 fn nk_initiator_inner(50 remote_public_key: &[u8],51 ephemeral_private_key: Option<&[u8]>,52 ) -> CoreResult<Self> {53 require_key(remote_public_key, "responder public key")?;54 let params: NoiseParams = NK.parse().map_err(display_error)?;55 let mut builder = core_builder(params)56 .remote_public_key(remote_public_key)57 .map_err(display_error)?;58 if let Some(ephemeral) = ephemeral_private_key {59 require_key(ephemeral, "initiator ephemeral private key")?;60 builder = builder.fixed_ephemeral_key_for_testing_only(ephemeral);61 }62 let handshake = builder.build_initiator().map_err(display_error)?;63 Ok(Self::handshake(handshake))64 }6566 pub fn nk_responder(local_private_key: &[u8], local_public_key: &[u8]) -> CoreResult<Self> {67 Self::nk_responder_inner(local_private_key, local_public_key, None)68 }6970 #[doc(hidden)]71 pub fn nk_responder_with_ephemeral_for_test(72 local_private_key: &[u8],73 local_public_key: &[u8],74 ephemeral_private_key: &[u8],75 ) -> CoreResult<Self> {76 Self::nk_responder_inner(77 local_private_key,78 local_public_key,79 Some(ephemeral_private_key),80 )81 }8283 fn nk_responder_inner(84 local_private_key: &[u8],85 local_public_key: &[u8],86 ephemeral_private_key: Option<&[u8]>,87 ) -> CoreResult<Self> {88 require_matching_keypair(local_private_key, local_public_key)?;89 let private = Zeroizing::new(local_private_key.to_vec());90 let params: NoiseParams = NK.parse().map_err(display_error)?;91 let mut builder = core_builder(params)92 .local_private_key(private.as_slice())93 .map_err(display_error)?;94 if let Some(ephemeral) = ephemeral_private_key {95 require_key(ephemeral, "responder ephemeral private key")?;96 builder = builder.fixed_ephemeral_key_for_testing_only(ephemeral);97 }98 let handshake = builder.build_responder().map_err(display_error)?;99 Ok(Self::handshake(handshake))100 }101102 pub fn kk_initiator(103 local_private_key: &[u8],104 local_public_key: &[u8],105 remote_public_key: &[u8],106 ) -> CoreResult<Self> {107 Self::kk_initiator_inner(local_private_key, local_public_key, remote_public_key, None)108 }109110 #[doc(hidden)]111 pub fn kk_initiator_with_ephemeral_for_test(112 local_private_key: &[u8],113 local_public_key: &[u8],114 remote_public_key: &[u8],115 ephemeral_private_key: &[u8],116 ) -> CoreResult<Self> {117 Self::kk_initiator_inner(118 local_private_key,119 local_public_key,120 remote_public_key,121 Some(ephemeral_private_key),122 )123 }124125 fn kk_initiator_inner(126 local_private_key: &[u8],127 local_public_key: &[u8],128 remote_public_key: &[u8],129 ephemeral_private_key: Option<&[u8]>,130 ) -> CoreResult<Self> {131 require_matching_keypair(local_private_key, local_public_key)?;132 require_key(remote_public_key, "responder public key")?;133 let private = Zeroizing::new(local_private_key.to_vec());134 let params: NoiseParams = KK.parse().map_err(display_error)?;135 let mut builder = core_builder(params)136 .local_private_key(private.as_slice())137 .map_err(display_error)?138 .remote_public_key(remote_public_key)139 .map_err(display_error)?;140 if let Some(ephemeral) = ephemeral_private_key {141 require_key(ephemeral, "initiator ephemeral private key")?;142 builder = builder.fixed_ephemeral_key_for_testing_only(ephemeral);143 }144 let handshake = builder.build_initiator().map_err(display_error)?;145 Ok(Self::handshake(handshake))146 }147148 pub fn kk_responder(149 local_private_key: &[u8],150 local_public_key: &[u8],151 remote_public_key: &[u8],152 ) -> CoreResult<Self> {153 Self::kk_responder_inner(local_private_key, local_public_key, remote_public_key, None)154 }155156 #[doc(hidden)]157 pub fn kk_responder_with_ephemeral_for_test(158 local_private_key: &[u8],159 local_public_key: &[u8],160 remote_public_key: &[u8],161 ephemeral_private_key: &[u8],162 ) -> CoreResult<Self> {163 Self::kk_responder_inner(164 local_private_key,165 local_public_key,166 remote_public_key,167 Some(ephemeral_private_key),168 )169 }170171 fn kk_responder_inner(172 local_private_key: &[u8],173 local_public_key: &[u8],174 remote_public_key: &[u8],175 ephemeral_private_key: Option<&[u8]>,176 ) -> CoreResult<Self> {177 require_matching_keypair(local_private_key, local_public_key)?;178 require_key(remote_public_key, "initiator public key")?;179 let private = Zeroizing::new(local_private_key.to_vec());180 let params: NoiseParams = KK.parse().map_err(display_error)?;181 let mut builder = core_builder(params)182 .local_private_key(private.as_slice())183 .map_err(display_error)?184 .remote_public_key(remote_public_key)185 .map_err(display_error)?;186 if let Some(ephemeral) = ephemeral_private_key {187 require_key(ephemeral, "responder ephemeral private key")?;188 builder = builder.fixed_ephemeral_key_for_testing_only(ephemeral);189 }190 let handshake = builder.build_responder().map_err(display_error)?;191 Ok(Self::handshake(handshake))192 }193194 fn handshake(handshake: HandshakeState) -> Self {195 Self {196 state: SessionState::Handshake(Some(handshake)),197 handshake_hash: None,198 }199 }200201 pub fn write_handshake(&mut self, payload: &[u8]) -> CoreResult<Vec<u8>> {202 let handshake = self.handshake_mut()?;203 if !handshake.is_my_turn() {204 return Err("Noise handshake write attempted out of turn".into());205 }206 let mut output = vec![0_u8; MAX_NOISE_MESSAGE];207 let size = handshake208 .write_message(payload, &mut output)209 .map_err(display_error)?;210 output.truncate(size);211 self.finish_if_ready()?;212 Ok(output)213 }214215 pub fn read_handshake(&mut self, message: &[u8]) -> CoreResult<Vec<u8>> {216 if message.len() > MAX_NOISE_MESSAGE {217 return Err("Noise handshake message exceeds 65535 bytes".into());218 }219 let handshake = self.handshake_mut()?;220 if handshake.is_my_turn() {221 return Err("Noise handshake read attempted out of turn".into());222 }223 let mut payload = vec![0_u8; MAX_NOISE_MESSAGE];224 let size = handshake225 .read_message(message, &mut payload)226 .map_err(display_error)?;227 payload.truncate(size);228 self.finish_if_ready()?;229 Ok(payload)230 }231232 pub fn encrypt(&mut self, plaintext: &[u8]) -> CoreResult<Vec<u8>> {233 if plaintext.len() > MAX_NOISE_MESSAGE - TAG_LEN {234 return Err("Noise plaintext exceeds transport limit".into());235 }236 let transport = self.transport_mut()?;237 let mut output = vec![0_u8; plaintext.len() + TAG_LEN];238 let size = transport239 .write_message(plaintext, &mut output)240 .map_err(display_error)?;241 output.truncate(size);242 Ok(output)243 }244245 pub fn decrypt(&mut self, ciphertext: &[u8]) -> CoreResult<Vec<u8>> {246 if ciphertext.len() > MAX_NOISE_MESSAGE {247 return Err("Noise ciphertext exceeds transport limit".into());248 }249 let transport = self.transport_mut()?;250 let mut output = vec![0_u8; ciphertext.len()];251 let size = transport252 .read_message(ciphertext, &mut output)253 .map_err(display_error)?;254 output.truncate(size);255 Ok(output)256 }257258 pub fn handshake_hash(&self) -> CoreResult<Vec<u8>> {259 self.handshake_hash260 .map(|hash| hash.to_vec())261 .ok_or_else(|| "Noise handshake is not complete".into())262 }263264 pub fn rekey_outgoing(&mut self) -> CoreResult<()> {265 self.transport_mut()?.rekey_outgoing();266 Ok(())267 }268269 pub fn rekey_incoming(&mut self) -> CoreResult<()> {270 self.transport_mut()?.rekey_incoming();271 Ok(())272 }273274 fn handshake_mut(&mut self) -> CoreResult<&mut HandshakeState> {275 match &mut self.state {276 SessionState::Handshake(Some(handshake)) => Ok(handshake),277 SessionState::Handshake(None) => Err("Noise handshake is transitioning".into()),278 SessionState::Transport(_) => Err("Noise handshake is already complete".into()),279 SessionState::Closed => Err("Noise session is closed".into()),280 }281 }282283 fn transport_mut(&mut self) -> CoreResult<&mut TransportState> {284 match &mut self.state {285 SessionState::Transport(transport) => Ok(transport),286 SessionState::Handshake(_) => Err("Noise handshake is not complete".into()),287 SessionState::Closed => Err("Noise session is closed".into()),288 }289 }290291 fn finish_if_ready(&mut self) -> CoreResult<()> {292 let ready = matches!(293 &self.state,294 SessionState::Handshake(Some(handshake)) if handshake.is_handshake_finished()295 );296 if !ready {297 return Ok(());298 }299300 let handshake = match std::mem::replace(&mut self.state, SessionState::Closed) {301 SessionState::Handshake(Some(handshake)) => handshake,302 _ => return Err("Noise session transition failed".into()),303 };304 let hash: [u8; HASH_LEN] = handshake305 .get_handshake_hash()306 .try_into()307 .map_err(|_| "Noise handshake hash must be 32 bytes".to_string())?;308 let transport = handshake.into_transport_mode().map_err(display_error)?;309 self.handshake_hash = Some(hash);310 self.state = SessionState::Transport(transport);311 Ok(())312 }313}314315impl Drop for CoreNoiseSession {316 fn drop(&mut self) {317 if let Some(hash) = &mut self.handshake_hash {318 hash.zeroize();319 }320 self.state = SessionState::Closed;321 }322}323324fn require_key(key: &[u8], label: &str) -> CoreResult<()> {325 if key.len() != 32 {326 return Err(format!("{label} must be 32 bytes"));327 }328 Ok(())329}330331fn require_matching_keypair(private_key: &[u8], public_key: &[u8]) -> CoreResult<()> {332 require_key(private_key, "local private key")?;333 require_key(public_key, "local public key")?;334 let private: [u8; 32] = private_key.try_into().expect("length checked");335 let derived = PublicKey::from(&StaticSecret::from(private));336 if derived.as_bytes().ct_eq(public_key).unwrap_u8() != 1 {337 return Err("local public key does not match local private key".into());338 }339 Ok(())340}341342fn display_error(error: impl std::fmt::Display) -> String {343 error.to_string()344}345346fn core_builder(params: NoiseParams) -> Builder<'static> {347 Builder::with_resolver(params, Box::new(RejectZeroResolver))348}349350struct RejectZeroResolver;351352impl CryptoResolver for RejectZeroResolver {353 fn resolve_rng(&self) -> Option<Box<dyn Random>> {354 DefaultResolver.resolve_rng()355 }356357 fn resolve_dh(&self, choice: &DHChoice) -> Option<Box<dyn Dh>> {358 DefaultResolver359 .resolve_dh(choice)360 .map(|inner| Box::new(RejectZeroDh { inner }) as Box<dyn Dh>)361 }362363 fn resolve_hash(&self, choice: &HashChoice) -> Option<Box<dyn Hash>> {364 DefaultResolver.resolve_hash(choice)365 }366367 fn resolve_cipher(&self, choice: &CipherChoice) -> Option<Box<dyn Cipher>> {368 DefaultResolver.resolve_cipher(choice)369 }370}371372struct RejectZeroDh {373 inner: Box<dyn Dh>,374}375376impl Dh for RejectZeroDh {377 fn name(&self) -> &'static str {378 self.inner.name()379 }380381 fn pub_len(&self) -> usize {382 self.inner.pub_len()383 }384385 fn priv_len(&self) -> usize {386 self.inner.priv_len()387 }388389 fn set(&mut self, private_key: &[u8]) {390 self.inner.set(private_key);391 }392393 fn generate(&mut self, rng: &mut dyn Random) -> Result<(), Error> {394 self.inner.generate(rng)395 }396397 fn pubkey(&self) -> &[u8] {398 self.inner.pubkey()399 }400401 fn privkey(&self) -> &[u8] {402 self.inner.privkey()403 }404405 fn dh(&self, public_key: &[u8], output: &mut [u8]) -> Result<(), Error> {406 self.inner.dh(public_key, output)?;407 let used = self.inner.dh_len();408 if output[..used].ct_eq(&vec![0_u8; used]).unwrap_u8() == 1 {409 output[..used].zeroize();410 return Err(Error::Dh);411 }412 Ok(())413 }414415 fn dh_len(&self) -> usize {416 self.inner.dh_len()417 }418}419420#[cfg(target_arch = "wasm32")]421mod wasm {422 use super::CoreNoiseSession;423 use wasm_bindgen::prelude::*;424425 fn js_error(message: String) -> JsError {426 JsError::new(&message)427 }428429 #[wasm_bindgen]430 pub struct WasmNoiseSession {431 inner: CoreNoiseSession,432 }433434 #[wasm_bindgen]435 impl WasmNoiseSession {436 #[wasm_bindgen(js_name = nkInitiator)]437 pub fn nk_initiator(remote_public_key: &[u8]) -> Result<WasmNoiseSession, JsError> {438 Ok(Self {439 inner: CoreNoiseSession::nk_initiator(remote_public_key).map_err(js_error)?,440 })441 }442443 #[wasm_bindgen(js_name = nkResponder)]444 pub fn nk_responder(445 local_private_key: &[u8],446 local_public_key: &[u8],447 ) -> Result<WasmNoiseSession, JsError> {448 Ok(Self {449 inner: CoreNoiseSession::nk_responder(local_private_key, local_public_key)450 .map_err(js_error)?,451 })452 }453454 #[wasm_bindgen(js_name = nkInitiatorWithEphemeralForTest)]455 pub fn nk_initiator_with_ephemeral_for_test(456 remote_public_key: &[u8],457 ephemeral_private_key: &[u8],458 ) -> Result<WasmNoiseSession, JsError> {459 Ok(Self {460 inner: CoreNoiseSession::nk_initiator_with_ephemeral_for_test(461 remote_public_key,462 ephemeral_private_key,463 )464 .map_err(js_error)?,465 })466 }467468 #[wasm_bindgen(js_name = nkResponderWithEphemeralForTest)]469 pub fn nk_responder_with_ephemeral_for_test(470 local_private_key: &[u8],471 local_public_key: &[u8],472 ephemeral_private_key: &[u8],473 ) -> Result<WasmNoiseSession, JsError> {474 Ok(Self {475 inner: CoreNoiseSession::nk_responder_with_ephemeral_for_test(476 local_private_key,477 local_public_key,478 ephemeral_private_key,479 )480 .map_err(js_error)?,481 })482 }483484 #[wasm_bindgen(js_name = kkInitiator)]485 pub fn kk_initiator(486 local_private_key: &[u8],487 local_public_key: &[u8],488 remote_public_key: &[u8],489 ) -> Result<WasmNoiseSession, JsError> {490 Ok(Self {491 inner: CoreNoiseSession::kk_initiator(492 local_private_key,493 local_public_key,494 remote_public_key,495 )496 .map_err(js_error)?,497 })498 }499500 #[wasm_bindgen(js_name = kkResponder)]501 pub fn kk_responder(502 local_private_key: &[u8],503 local_public_key: &[u8],504 remote_public_key: &[u8],505 ) -> Result<WasmNoiseSession, JsError> {506 Ok(Self {507 inner: CoreNoiseSession::kk_responder(508 local_private_key,509 local_public_key,510 remote_public_key,511 )512 .map_err(js_error)?,513 })514 }515516 #[wasm_bindgen(js_name = kkInitiatorWithEphemeralForTest)]517 pub fn kk_initiator_with_ephemeral_for_test(518 local_private_key: &[u8],519 local_public_key: &[u8],520 remote_public_key: &[u8],521 ephemeral_private_key: &[u8],522 ) -> Result<WasmNoiseSession, JsError> {523 Ok(Self {524 inner: CoreNoiseSession::kk_initiator_with_ephemeral_for_test(525 local_private_key,526 local_public_key,527 remote_public_key,528 ephemeral_private_key,529 )530 .map_err(js_error)?,531 })532 }533534 #[wasm_bindgen(js_name = kkResponderWithEphemeralForTest)]535 pub fn kk_responder_with_ephemeral_for_test(536 local_private_key: &[u8],537 local_public_key: &[u8],538 remote_public_key: &[u8],539 ephemeral_private_key: &[u8],540 ) -> Result<WasmNoiseSession, JsError> {541 Ok(Self {542 inner: CoreNoiseSession::kk_responder_with_ephemeral_for_test(543 local_private_key,544 local_public_key,545 remote_public_key,546 ephemeral_private_key,547 )548 .map_err(js_error)?,549 })550 }551552 #[wasm_bindgen(js_name = writeHandshake)]553 pub fn write_handshake(&mut self, payload: &[u8]) -> Result<Vec<u8>, JsError> {554 self.inner.write_handshake(payload).map_err(js_error)555 }556557 #[wasm_bindgen(js_name = readHandshake)]558 pub fn read_handshake(&mut self, message: &[u8]) -> Result<Vec<u8>, JsError> {559 self.inner.read_handshake(message).map_err(js_error)560 }561562 pub fn encrypt(&mut self, plaintext: &[u8]) -> Result<Vec<u8>, JsError> {563 self.inner.encrypt(plaintext).map_err(js_error)564 }565566 pub fn decrypt(&mut self, ciphertext: &[u8]) -> Result<Vec<u8>, JsError> {567 self.inner.decrypt(ciphertext).map_err(js_error)568 }569570 #[wasm_bindgen(getter, js_name = handshakeHash)]571 pub fn handshake_hash(&self) -> Result<Vec<u8>, JsError> {572 self.inner.handshake_hash().map_err(js_error)573 }574575 #[wasm_bindgen(js_name = rekeyOutgoing)]576 pub fn rekey_outgoing(&mut self) -> Result<(), JsError> {577 self.inner.rekey_outgoing().map_err(js_error)578 }579580 #[wasm_bindgen(js_name = rekeyIncoming)]581 pub fn rekey_incoming(&mut self) -> Result<(), JsError> {582 self.inner.rekey_incoming().map_err(js_error)583 }584 }585}586587#[cfg(not(target_arch = "wasm32"))]588mod jni_bridge;589590#[cfg(test)]591mod tests {592 use super::{CoreNoiseSession, KK, NK, core_builder};593 use blake2::{594 Blake2sMac256,595 digest::{KeyInit as BlakeKeyInit, Mac},596 };597 use chacha20poly1305::{598 ChaCha20Poly1305, Nonce,599 aead::{Aead, Payload},600 };601 use snow::params::NoiseParams;602 use x25519_dalek::{PublicKey, StaticSecret};603604 fn keypair(seed: u8) -> ([u8; 32], [u8; 32]) {605 let private = [seed; 32];606 let public = *PublicKey::from(&StaticSecret::from(private)).as_bytes();607 (private, public)608 }609610 fn hex(value: &str) -> Vec<u8> {611 assert!(value.len().is_multiple_of(2));612 value613 .as_bytes()614 .chunks_exact(2)615 .map(|pair| u8::from_str_radix(std::str::from_utf8(pair).unwrap(), 16).unwrap())616 .collect()617 }618619 fn official_pair(620 pattern: &str,621 initiator_static: Option<&str>,622 initiator_ephemeral: &str,623 initiator_remote_static: &str,624 responder_static: &str,625 responder_ephemeral: &str,626 responder_remote_static: Option<&str>,627 ) -> (CoreNoiseSession, CoreNoiseSession) {628 let prologue = hex("4a6f686e2047616c74");629 let responder_private = hex(responder_static);630 let responder_public = PublicKey::from(&StaticSecret::from(631 <[u8; 32]>::try_from(&responder_private[..]).unwrap(),632 ));633 assert_eq!(634 responder_public.as_bytes(),635 &hex(initiator_remote_static)[..]636 );637638 let params: NoiseParams = pattern.parse().unwrap();639 let initiator_ephemeral = hex(initiator_ephemeral);640 let initiator_static = initiator_static.map(hex);641 let responder_ephemeral = hex(responder_ephemeral);642 let responder_remote_static = responder_remote_static.map(hex);643 let mut initiator_builder = core_builder(params.clone())644 .prologue(&prologue)645 .unwrap()646 .remote_public_key(responder_public.as_bytes())647 .unwrap()648 .fixed_ephemeral_key_for_testing_only(&initiator_ephemeral);649 if let Some(private) = &initiator_static {650 initiator_builder = initiator_builder.local_private_key(private).unwrap();651 }652653 let mut responder_builder = core_builder(params)654 .prologue(&prologue)655 .unwrap()656 .local_private_key(&responder_private)657 .unwrap()658 .fixed_ephemeral_key_for_testing_only(&responder_ephemeral);659 if let Some(remote_public) = &responder_remote_static {660 responder_builder = responder_builder.remote_public_key(remote_public).unwrap();661 }662663 (664 CoreNoiseSession::handshake(initiator_builder.build_initiator().unwrap()),665 CoreNoiseSession::handshake(responder_builder.build_responder().unwrap()),666 )667 }668669 fn assert_cacophony_vector(670 mut initiator: CoreNoiseSession,671 mut responder: CoreNoiseSession,672 handshake_hash: &str,673 messages: &[(&str, &str)],674 ) {675 for (index, (payload, expected_ciphertext)) in messages.iter().enumerate() {676 let plaintext = hex(payload);677 let expected = hex(expected_ciphertext);678 if index < 2 {679 let (sender, receiver) = if index % 2 == 0 {680 (&mut initiator, &mut responder)681 } else {682 (&mut responder, &mut initiator)683 };684 let actual = sender.write_handshake(&plaintext).unwrap();685 assert_eq!(actual, expected, "handshake message {index}");686 assert_eq!(687 receiver.read_handshake(&actual).unwrap(),688 plaintext,689 "handshake payload {index}",690 );691 } else {692 let (sender, receiver) = if index % 2 == 0 {693 (&mut initiator, &mut responder)694 } else {695 (&mut responder, &mut initiator)696 };697 let actual = sender.encrypt(&plaintext).unwrap();698 assert_eq!(actual, expected, "transport message {index}");699 assert_eq!(700 receiver.decrypt(&actual).unwrap(),701 plaintext,702 "transport payload {index}",703 );704 }705 }706 let expected_hash = hex(handshake_hash);707 assert_eq!(initiator.handshake_hash().unwrap(), expected_hash);708 assert_eq!(responder.handshake_hash().unwrap(), expected_hash);709 }710711 fn complete(initiator: &mut CoreNoiseSession, responder: &mut CoreNoiseSession) {712 let message1 = initiator.write_handshake(&[]).unwrap();713 assert!(responder.read_handshake(&message1).unwrap().is_empty());714 let message2 = responder.write_handshake(&[]).unwrap();715 assert!(initiator.read_handshake(&message2).unwrap().is_empty());716 assert_eq!(717 initiator.handshake_hash().unwrap(),718 responder.handshake_hash().unwrap()719 );720 }721722 #[test]723 fn nk_round_trip_uses_one_state_machine() {724 let (responder_private, responder_public) = keypair(7);725 let mut initiator = CoreNoiseSession::nk_initiator(&responder_public).unwrap();726 let mut responder =727 CoreNoiseSession::nk_responder(&responder_private, &responder_public).unwrap();728 complete(&mut initiator, &mut responder);729730 let ciphertext = initiator.encrypt(b"peach").unwrap();731 assert_eq!(responder.decrypt(&ciphertext).unwrap(), b"peach");732 let response = responder.encrypt(b"sync").unwrap();733 assert_eq!(initiator.decrypt(&response).unwrap(), b"sync");734 }735736 #[test]737 fn kk_round_trip_authenticates_both_static_keys() {738 let (initiator_private, initiator_public) = keypair(11);739 let (responder_private, responder_public) = keypair(19);740 let mut initiator = CoreNoiseSession::kk_initiator(741 &initiator_private,742 &initiator_public,743 &responder_public,744 )745 .unwrap();746 let mut responder = CoreNoiseSession::kk_responder(747 &responder_private,748 &responder_public,749 &initiator_public,750 )751 .unwrap();752 complete(&mut initiator, &mut responder);753 }754755 #[test]756 fn mismatched_local_keypair_is_rejected() {757 let (private, _) = keypair(2);758 let (_, wrong_public) = keypair(3);759 let result = CoreNoiseSession::nk_responder(&private, &wrong_public);760 assert!(matches!(result, Err(error) if error.contains("does not match")));761 }762763 #[test]764 fn tampered_transport_frame_is_rejected() {765 let (responder_private, responder_public) = keypair(23);766 let mut initiator = CoreNoiseSession::nk_initiator(&responder_public).unwrap();767 let mut responder =768 CoreNoiseSession::nk_responder(&responder_private, &responder_public).unwrap();769 complete(&mut initiator, &mut responder);770 let mut ciphertext = initiator.encrypt(b"vault").unwrap();771 ciphertext[0] ^= 0x80;772 assert!(responder.decrypt(&ciphertext).is_err());773 }774775 #[test]776 fn cacophony_nk_vector_matches_shared_core() {777 let pair = official_pair(778 NK,779 None,780 "893e28b9dc6ca8d611ab664754b8ceb7bac5117349a4439a6b0569da977c464a",781 "31e0303fd6418d2f8c0e78b91f22e8caed0fbe48656dcf4767e4834f701b8f62",782 "4a3acbfdb163dec651dfa3194dece676d437029c62a408b4c5ea9114246e4893",783 "bbdb4cdbd309f1a1f2e1456967fe288cadd6f712d65dc7b7793d5e63da6b375b",784 None,785 );786 assert_cacophony_vector(787 pair.0,788 pair.1,789 "d7244d974066aae2376f7ba5534f60a6e4e82cd7c9751e226cae3928e6b49f14",790 &[791 (792 "4c756477696720766f6e204d69736573",793 "ca35def5ae56cec33dc2036731ab14896bc4c75dbb07a61f879f8e3afa4c794454ae7612d1724af42adb130160a9a94e67b5b169b4e00c189f6467cd17eb7cad",794 ),795 (796 "4d757272617920526f746862617264",797 "95ebc60d2b1fa672c1f46a8aa265ef51bfe38e7ccb39ec5be34069f144808843986a5c929337e337ac8b4a074af12ab9f76318a5f18c8b599a443af07383ce",798 ),799 (800 "462e20412e20486179656b",801 "550027c7a5d450017bcb5e12b8253b1c53fd2213aeda84891d5f95",802 ),803 (804 "4361726c204d656e676572",805 "dfbce0c38210ccee35e830aca9dd8b8b3997b933e75bfc8864b759",806 ),807 ],808 );809 }810811 #[test]812 fn cacophony_kk_vector_matches_shared_core() {813 let pair = official_pair(814 KK,815 Some("e61ef9919cde45dd5f82166404bd08e38bceb5dfdfded0a34c8df7ed542214d1"),816 "893e28b9dc6ca8d611ab664754b8ceb7bac5117349a4439a6b0569da977c464a",817 "31e0303fd6418d2f8c0e78b91f22e8caed0fbe48656dcf4767e4834f701b8f62",818 "4a3acbfdb163dec651dfa3194dece676d437029c62a408b4c5ea9114246e4893",819 "bbdb4cdbd309f1a1f2e1456967fe288cadd6f712d65dc7b7793d5e63da6b375b",820 Some("6bc3822a2aa7f4e6981d6538692b3cdf3e6df9eea6ed269eb41d93c22757b75a"),821 );822 assert_cacophony_vector(823 pair.0,824 pair.1,825 "1362b8627a00907ce11e558aba8ce7cbca88e83f0e84ce7db5159b1c3e25ab59",826 &[827 (828 "4c756477696720766f6e204d69736573",829 "ca35def5ae56cec33dc2036731ab14896bc4c75dbb07a61f879f8e3afa4c7944266a5f53784aa3becb0f7485c2759c328937867a4cbaafef07422b0725e098be",830 ),831 (832 "4d757272617920526f746862617264",833 "95ebc60d2b1fa672c1f46a8aa265ef51bfe38e7ccb39ec5be34069f144808843008aeea5d76d6abcbab87a18502c8a8352d9933ac11e2a7d228038d721e31e",834 ),835 (836 "462e20412e20486179656b",837 "5f92113edf78c3e56e6d67201f5f9e0c8f2930c3e1ffb64ede0358",838 ),839 (840 "4361726c204d656e676572",841 "30ebbd9cdcef7f40d99c8cd11e880dac28f5c9e5032c1059b3b56a",842 ),843 ],844 );845 }846847 #[test]848 fn rfc_8439_chacha20_poly1305_aead_vector_matches_snow_cipher() {849 let key = hex("808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f");850 let nonce = hex("070000004041424344454647");851 let aad = hex("50515253c0c1c2c3c4c5c6c7");852 let plaintext = hex(concat!(853 "4c616469657320616e642047656e746c656d656e206f662074686520636c6173",854 "73206f66202739393a204966204920636f756c64206f6666657220796f75206f",855 "6e6c79206f6e652074697020666f7220746865206675747572652c2073756e73",856 "637265656e20776f756c642062652069742e",857 ));858 let expected = hex(concat!(859 "d31a8d34648e60db7b86afbc53ef7ec2a4aded51296e08fea9e2b5a736ee62d6",860 "3dbea45e8ca9671282fafb69da92728b1a71de0a9e060b2905d6a5b67ecd3b36",861 "92ddbd7f2d778b8c9803aee328091b58fab324e4fad675945585808b4831d7bc",862 "3ff4def08e4b7a9de576d26586cec64b6116",863 "1ae10b594f09e26a7e902ecbd0600691",864 ));865 let cipher = ChaCha20Poly1305::new_from_slice(&key).unwrap();866 assert_eq!(867 cipher868 .encrypt(869 Nonce::from_slice(&nonce),870 Payload {871 msg: &plaintext,872 aad: &aad,873 },874 )875 .unwrap(),876 expected,877 );878 }879880 #[test]881 fn blake2_reference_keyed_vector_matches_snow_hash_dependency() {882 let key: Vec<u8> = (0_u8..32).collect();883 let mut mac = <Blake2sMac256 as BlakeKeyInit>::new_from_slice(&key).unwrap();884 Mac::update(&mut mac, &[]);885 assert_eq!(886 Mac::finalize(mac).into_bytes().as_slice(),887 &hex("48a8997da407876b3d79c0d92325ad3b89cbb754d86ab71aee047ad345fd2c49"),888 );889 }890}