| // Copyright 2024 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| // use this file except in compliance with the License. You may obtain a copy of |
| // the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| // License for the specific language governing permissions and limitations under |
| // the License. |
| |
| //! An encrypted session implementation which uses |
| //! Noise_NK_X25519_AESGCM_SHA512 and Noise_NNpsk0_X25519_AESGCM_SHA512. |
| |
| use crate::crypto::{ |
| Commit, Counter, DhPrivateKey, DhPublicKey, HandshakeMessage, |
| HandshakePayload, Hash, SessionCrypto, |
| }; |
| use crate::error::{DpeResult, ErrCode}; |
| use crate::memory::Message; |
| use core::marker::PhantomData; |
| use log::{debug, error}; |
| use noise_protocol::{HandshakeStateBuilder, Hash as NoiseHash, U8Array}; |
| |
| impl From<noise_protocol::Error> for ErrCode { |
| fn from(_err: noise_protocol::Error) -> Self { |
| ErrCode::InvalidArgument |
| } |
| } |
| |
| impl<NoiseHash> From<&NoiseHash> for Hash |
| where |
| NoiseHash: U8Array, |
| { |
| fn from(value: &NoiseHash) -> Self { |
| // The Noise hash size may not match HASH_SIZE. |
| Hash::from_slice_infallible(value.as_slice()) |
| } |
| } |
| |
| /// A cipher state type that can be used as a |
| /// [`SessionCipherState`](crate::crypto::SessionCrypto::SessionCipherState). |
| pub struct NoiseCipherState<C: noise_protocol::Cipher> { |
| k: C::Key, |
| n: u64, |
| n_staged: u64, |
| } |
| |
| impl<C: noise_protocol::Cipher> Clone for NoiseCipherState<C> { |
| fn clone(&self) -> Self { |
| Self { k: self.k.clone(), n: self.n, n_staged: self.n_staged } |
| } |
| } |
| |
| impl<C: noise_protocol::Cipher> Default for NoiseCipherState<C> { |
| fn default() -> Self { |
| Self { k: C::Key::new(), n: 0, n_staged: 0 } |
| } |
| } |
| |
| impl<C: noise_protocol::Cipher> core::fmt::Debug for NoiseCipherState<C> { |
| fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
| write!(f, "k: redacted, n: {}", self.n)?; |
| Ok(()) |
| } |
| } |
| |
| impl<C: noise_protocol::Cipher> core::hash::Hash for NoiseCipherState<C> { |
| fn hash<H: core::hash::Hasher>(&self, state: &mut H) { |
| self.k.as_slice().hash(state); |
| self.n.hash(state); |
| self.n_staged.hash(state); |
| } |
| } |
| |
| #[cfg(test)] |
| impl<C: noise_protocol::Cipher> PartialEq for NoiseCipherState<C> { |
| fn eq(&self, other: &Self) -> bool { |
| self.k.as_slice() == other.k.as_slice() |
| && self.n == other.n |
| && self.n_staged == other.n_staged |
| } |
| } |
| |
| #[cfg(test)] |
| impl<C: noise_protocol::Cipher> Eq for NoiseCipherState<C> {} |
| |
| impl<C: noise_protocol::Cipher> Counter for NoiseCipherState<C> { |
| fn n(&self) -> u64 { |
| self.n |
| } |
| fn set_n(&mut self, n: u64) { |
| self.n = n; |
| } |
| } |
| |
| impl<C: noise_protocol::Cipher> Commit for NoiseCipherState<C> { |
| // Called when an encrypted message is finalized to commit the new cipher |
| // state. |
| fn commit(&mut self) { |
| self.n = self.n_staged; |
| } |
| } |
| |
| impl<C: noise_protocol::Cipher> From<&noise_protocol::CipherState<C>> |
| for NoiseCipherState<C> |
| { |
| fn from(cs: &noise_protocol::CipherState<C>) -> Self { |
| let (key, counter) = cs.clone().extract(); |
| NoiseCipherState { k: key, n: counter, n_staged: counter } |
| } |
| } |
| |
| /// Returns the public key corresponding to a given `dh_private_key`. |
| pub fn get_dh_public_key<D: noise_protocol::DH>( |
| dh_private_key: &DhPrivateKey, |
| ) -> DpeResult<DhPublicKey> { |
| DhPublicKey::from_slice( |
| D::pubkey(&D::Key::from_slice(dh_private_key.as_slice())).as_slice(), |
| ) |
| } |
| |
| /// A trait representing [`NoiseSessionCrypto`] dependencies. |
| pub trait NoiseCryptoDeps { |
| /// Cipher type |
| type Cipher: noise_protocol::Cipher; |
| /// DH type |
| type DH: noise_protocol::DH; |
| /// Hash type |
| type Hash: noise_protocol::Hash; |
| } |
| |
| /// A Noise implementation of the [`SessionCrypto`] trait. |
| pub struct NoiseSessionCrypto<D: NoiseCryptoDeps> { |
| #[allow(dead_code)] |
| phantom: PhantomData<D>, |
| } |
| |
| impl<D> Clone for NoiseSessionCrypto<D> |
| where |
| D: NoiseCryptoDeps, |
| { |
| fn clone(&self) -> Self { |
| Self { phantom: Default::default() } |
| } |
| } |
| |
| impl<D> Default for NoiseSessionCrypto<D> |
| where |
| D: NoiseCryptoDeps, |
| { |
| fn default() -> Self { |
| Self { phantom: Default::default() } |
| } |
| } |
| |
| impl<D> core::fmt::Debug for NoiseSessionCrypto<D> |
| where |
| D: NoiseCryptoDeps, |
| { |
| fn fmt(&self, _: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
| Ok(()) |
| } |
| } |
| |
| impl<D> core::hash::Hash for NoiseSessionCrypto<D> |
| where |
| D: NoiseCryptoDeps, |
| { |
| fn hash<Hr: core::hash::Hasher>(&self, _: &mut Hr) {} |
| } |
| |
| impl<D> PartialEq for NoiseSessionCrypto<D> |
| where |
| D: NoiseCryptoDeps, |
| { |
| fn eq(&self, _: &Self) -> bool { |
| true |
| } |
| } |
| |
| impl<D> Eq for NoiseSessionCrypto<D> where D: NoiseCryptoDeps {} |
| |
| impl<D> SessionCrypto for NoiseSessionCrypto<D> |
| where |
| D: NoiseCryptoDeps, |
| { |
| type SessionCipherState = NoiseCipherState<D::Cipher>; |
| |
| /// Implements the responder role of a Noise_NK handshake. |
| fn new_session_handshake( |
| static_dh_key: &DhPrivateKey, |
| initiator_handshake: &HandshakeMessage, |
| payload: &HandshakePayload, |
| responder_handshake: &mut HandshakeMessage, |
| decrypt_cipher_state: &mut NoiseCipherState<D::Cipher>, |
| encrypt_cipher_state: &mut NoiseCipherState<D::Cipher>, |
| psk_seed: &mut Hash, |
| ) -> DpeResult<()> { |
| #[allow(unused_results)] |
| let mut handshake: noise_protocol::HandshakeState< |
| D::DH, |
| D::Cipher, |
| D::Hash, |
| > = { |
| let mut builder = HandshakeStateBuilder::new(); |
| builder.set_pattern(noise_protocol::patterns::noise_nk()); |
| builder.set_is_initiator(false); |
| builder.set_prologue(&[]); |
| builder.set_s(<D::DH as noise_protocol::DH>::Key::from_slice( |
| static_dh_key.as_slice(), |
| )); |
| builder.build_handshake_state() |
| }; |
| handshake.read_message(initiator_handshake.as_slice(), &mut [])?; |
| handshake.write_message( |
| payload.as_slice(), |
| responder_handshake.as_mut_sized( |
| handshake.get_next_message_overhead() + payload.len(), |
| )?, |
| )?; |
| assert!(handshake.completed()); |
| let ciphers = handshake.get_ciphers(); |
| *decrypt_cipher_state = (&ciphers.0).into(); |
| *encrypt_cipher_state = (&ciphers.1).into(); |
| debug!("get_hash"); |
| *psk_seed = Hash::from_slice(handshake.get_hash())?; |
| Ok(()) |
| } |
| |
| /// Implements the responder role of a Noise_NNpsk0 handshake. |
| fn derive_session_handshake( |
| psk: &Hash, |
| initiator_handshake: &HandshakeMessage, |
| payload: &HandshakePayload, |
| responder_handshake: &mut HandshakeMessage, |
| decrypt_cipher_state: &mut NoiseCipherState<D::Cipher>, |
| encrypt_cipher_state: &mut NoiseCipherState<D::Cipher>, |
| psk_seed: &mut Hash, |
| ) -> DpeResult<()> { |
| #[allow(unused_results)] |
| let mut handshake: noise_protocol::HandshakeState< |
| D::DH, |
| D::Cipher, |
| D::Hash, |
| > = { |
| let mut builder = HandshakeStateBuilder::new(); |
| builder.set_pattern(noise_protocol::patterns::noise_nn_psk0()); |
| builder.set_is_initiator(false); |
| builder.set_prologue(&[]); |
| builder.build_handshake_state() |
| }; |
| handshake |
| .push_psk(psk.as_slice().get(..32).ok_or(ErrCode::InternalError)?); |
| handshake.read_message(initiator_handshake.as_slice(), &mut [])?; |
| handshake.write_message( |
| payload.as_slice(), |
| responder_handshake.as_mut_sized( |
| handshake.get_next_message_overhead() + payload.len(), |
| )?, |
| )?; |
| let ciphers = handshake.get_ciphers(); |
| *decrypt_cipher_state = (&ciphers.0).into(); |
| *encrypt_cipher_state = (&ciphers.1).into(); |
| *psk_seed = Hash::from_slice(handshake.get_hash())?; |
| Ok(()) |
| } |
| |
| /// Encrypts a Noise transport message in place. |
| fn session_encrypt( |
| cipher_state: &mut NoiseCipherState<D::Cipher>, |
| in_place_buffer: &mut Message, |
| ) -> DpeResult<()> { |
| let mut cs = noise_protocol::CipherState::<D::Cipher>::new( |
| cipher_state.k.as_slice(), |
| cipher_state.n, |
| ); |
| let plaintext_len = in_place_buffer.len(); |
| let _ = cs.encrypt_in_place( |
| in_place_buffer.as_mut_sized( |
| plaintext_len |
| + <D::Cipher as noise_protocol::Cipher>::tag_len(), |
| )?, |
| plaintext_len, |
| ); |
| // Encrypting a message is usually not the final step in preparing |
| // the message for transport. If a subsequent step fails, it is |
| // better for 'n' to remain unchanged so we don't get out of sync. |
| (_, cipher_state.n_staged) = cs.extract(); |
| Ok(()) |
| } |
| |
| /// Decrypts a Noise transport message in place. |
| fn session_decrypt( |
| cipher_state: &mut NoiseCipherState<D::Cipher>, |
| in_place_buffer: &mut Message, |
| ) -> DpeResult<()> { |
| let mut cs = noise_protocol::CipherState::<D::Cipher>::new( |
| cipher_state.k.as_slice(), |
| cipher_state.n, |
| ); |
| let ciphertext_len = in_place_buffer.len(); |
| let plaintext_len = match cs |
| .decrypt_in_place(in_place_buffer.vec.as_mut(), ciphertext_len) |
| { |
| Ok(length) => length, |
| _ => { |
| error!("Session decrypt failed"); |
| return Err(ErrCode::InvalidCommand); |
| } |
| }; |
| in_place_buffer.vec.truncate(plaintext_len); |
| (_, cipher_state.n) = cs.extract(); |
| Ok(()) |
| } |
| |
| /// Derives a responder-side PSK. |
| fn derive_psk_from_session( |
| psk_seed: &Hash, |
| decrypt_cipher_state: &NoiseCipherState<D::Cipher>, |
| encrypt_cipher_state: &NoiseCipherState<D::Cipher>, |
| ) -> DpeResult<Hash> { |
| let mut hasher: D::Hash = Default::default(); |
| hasher.input(psk_seed.as_slice()); |
| // Use the decrypt state as it was before we decrypted the current |
| // command message. This allows clients to compute the PSK using |
| // the cipher states as they are before the client sends the |
| // command. |
| hasher.input(&(decrypt_cipher_state.n() - 1).to_le_bytes()); |
| hasher.input(&encrypt_cipher_state.n().to_le_bytes()); |
| Ok((&hasher.result()).into()) |
| } |
| } |
| |
| /// A SessionClient implements the initiator side of an encrypted session. A |
| /// DPE does not use this itself, it is useful for clients and testing. |
| pub struct SessionClient<D> |
| where |
| D: NoiseCryptoDeps, |
| { |
| handshake_state: |
| Option<noise_protocol::HandshakeState<D::DH, D::Cipher, D::Hash>>, |
| /// Cipher state for encrypting messages to a DPE. |
| pub encrypt_cipher_state: NoiseCipherState<D::Cipher>, |
| /// Cipher state for decrypting messages from a DPE. |
| pub decrypt_cipher_state: NoiseCipherState<D::Cipher>, |
| /// PSK seed for deriving sessions. See [`derive_psk`]. |
| /// |
| /// [`derive_psk`]: #method.derive_psk |
| pub psk_seed: Hash, |
| } |
| |
| impl<D> Clone for SessionClient<D> |
| where |
| D: NoiseCryptoDeps, |
| { |
| fn clone(&self) -> Self { |
| Self { |
| handshake_state: self.handshake_state.clone(), |
| encrypt_cipher_state: self.encrypt_cipher_state.clone(), |
| decrypt_cipher_state: self.decrypt_cipher_state.clone(), |
| psk_seed: self.psk_seed.clone(), |
| } |
| } |
| } |
| |
| impl<D> Default for SessionClient<D> |
| where |
| D: NoiseCryptoDeps, |
| { |
| fn default() -> Self { |
| Self::new() |
| } |
| } |
| |
| impl<D> core::fmt::Debug for SessionClient<D> |
| where |
| D: NoiseCryptoDeps, |
| { |
| fn fmt(&self, _: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
| Ok(()) |
| } |
| } |
| |
| impl<D> SessionClient<D> |
| where |
| D: NoiseCryptoDeps, |
| { |
| /// Creates a new SessionClient instance. Set up by starting and finishing a |
| /// handshake. |
| pub fn new() -> Self { |
| Self { |
| handshake_state: Default::default(), |
| encrypt_cipher_state: Default::default(), |
| decrypt_cipher_state: Default::default(), |
| psk_seed: Default::default(), |
| } |
| } |
| |
| /// Starts a handshake using a known `public_key` and returns a message that |
| /// works with the DPE OpenSession command. |
| pub fn start_handshake_with_known_public_key( |
| &mut self, |
| public_key: &DhPublicKey, |
| ) -> DpeResult<HandshakeMessage> { |
| #[allow(unused_results)] |
| let mut handshake_state = { |
| let mut builder = HandshakeStateBuilder::new(); |
| builder.set_pattern(noise_protocol::patterns::noise_nk()); |
| builder.set_is_initiator(true); |
| builder.set_prologue(&[]); |
| builder.set_rs(<D::DH as noise_protocol::DH>::Pubkey::from_slice( |
| public_key.as_slice(), |
| )); |
| builder.build_handshake_state() |
| }; |
| let mut message = HandshakeMessage::new(); |
| handshake_state.write_message( |
| &[], |
| message |
| .as_mut_sized(handshake_state.get_next_message_overhead())?, |
| )?; |
| self.handshake_state = Some(handshake_state); |
| Ok(message) |
| } |
| |
| /// Starts a handshake using a `psk` and returns a message that works with |
| /// the DPE DeriveContext command. Use [`derive_psk`] to obtain this value |
| /// from an existing session. |
| /// |
| /// [`derive_psk`]: #method.derive_psk |
| pub fn start_handshake_with_psk( |
| &mut self, |
| psk: &Hash, |
| ) -> DpeResult<HandshakeMessage> { |
| #[allow(unused_results)] |
| let mut handshake_state = { |
| let mut builder = HandshakeStateBuilder::new(); |
| builder.set_pattern(noise_protocol::patterns::noise_nn_psk0()); |
| builder.set_is_initiator(true); |
| builder.set_prologue(&[]); |
| builder.build_handshake_state() |
| }; |
| handshake_state |
| .push_psk(psk.as_slice().get(..32).ok_or(ErrCode::InternalError)?); |
| let mut message = HandshakeMessage::new(); |
| handshake_state.write_message( |
| &[], |
| message |
| .as_mut_sized(handshake_state.get_next_message_overhead())?, |
| )?; |
| self.handshake_state = Some(handshake_state); |
| Ok(message) |
| } |
| |
| /// Finishes a handshake started using one of the start_handshake_* methods. |
| /// On success, returns the handshake payload from the responder and sets up |
| /// internal state for subsequent calls to encrypt and decrypt. |
| pub fn finish_handshake( |
| &mut self, |
| responder_handshake: &HandshakeMessage, |
| ) -> DpeResult<HandshakePayload> { |
| match self.handshake_state { |
| None => Err(ErrCode::InvalidArgument), |
| Some(ref mut handshake) => { |
| let mut payload = HandshakePayload::new(); |
| handshake.read_message( |
| responder_handshake.as_slice(), |
| payload.as_mut_sized( |
| responder_handshake.len() |
| - handshake.get_next_message_overhead(), |
| )?, |
| )?; |
| let ciphers = handshake.get_ciphers(); |
| self.encrypt_cipher_state = (&ciphers.0).into(); |
| self.decrypt_cipher_state = (&ciphers.1).into(); |
| self.psk_seed = Hash::from_slice(handshake.get_hash())?; |
| Ok(payload) |
| } |
| } |
| } |
| |
| /// Derives a PSK from the current session. |
| pub fn derive_psk(&self) -> Hash { |
| // Note this is from a client perspective so the counters are hashed |
| // encrypt first and unmodified from their current state. A DPE will |
| // reverse the order and decrement the first counter in order to derive |
| // the same value (see derive_psk_from_session). |
| let mut hasher: D::Hash = Default::default(); |
| hasher.input(self.psk_seed.as_slice()); |
| hasher.input(&self.encrypt_cipher_state.n().to_le_bytes()); |
| hasher.input(&self.decrypt_cipher_state.n().to_le_bytes()); |
| (&hasher.result()).into() |
| } |
| |
| /// Encrypts a message to send to a DPE and commits cipher state changes. |
| pub fn encrypt(&mut self, in_place_buffer: &mut Message) -> DpeResult<()> { |
| NoiseSessionCrypto::<D>::session_encrypt( |
| &mut self.encrypt_cipher_state, |
| in_place_buffer, |
| )?; |
| self.encrypt_cipher_state.commit(); |
| Ok(()) |
| } |
| |
| /// Decrypts a message from a DPE. |
| pub fn decrypt(&mut self, in_place_buffer: &mut Message) -> DpeResult<()> { |
| NoiseSessionCrypto::<D>::session_decrypt( |
| &mut self.decrypt_cipher_state, |
| in_place_buffer, |
| ) |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| |
| struct DepsForTesting {} |
| impl NoiseCryptoDeps for DepsForTesting { |
| type Cipher = noise_rust_crypto::Aes256Gcm; |
| type DH = noise_rust_crypto::X25519; |
| type Hash = noise_rust_crypto::Sha512; |
| } |
| |
| type SessionCryptoForTesting = NoiseSessionCrypto<DepsForTesting>; |
| |
| type SessionClientForTesting = SessionClient<DepsForTesting>; |
| |
| type CipherStateForTesting = NoiseCipherState<noise_rust_crypto::Aes256Gcm>; |
| |
| #[test] |
| fn end_to_end_session() { |
| let mut client = SessionClientForTesting::new(); |
| let dh_key: DhPrivateKey = Default::default(); |
| let dh_public_key = get_dh_public_key::< |
| <DepsForTesting as NoiseCryptoDeps>::DH, |
| >(&dh_key) |
| .unwrap(); |
| let handshake1 = client |
| .start_handshake_with_known_public_key(&dh_public_key) |
| .unwrap(); |
| let mut dpe_decrypt_cs: CipherStateForTesting = Default::default(); |
| let mut dpe_encrypt_cs: CipherStateForTesting = Default::default(); |
| let mut psk_seed = Default::default(); |
| let mut handshake2 = Default::default(); |
| let payload = HandshakePayload::from_slice("pay".as_bytes()).unwrap(); |
| SessionCryptoForTesting::new_session_handshake( |
| &dh_key, |
| &handshake1, |
| &payload, |
| &mut handshake2, |
| &mut dpe_decrypt_cs, |
| &mut dpe_encrypt_cs, |
| &mut psk_seed, |
| ) |
| .unwrap(); |
| assert_eq!(payload, client.finish_handshake(&handshake2).unwrap()); |
| |
| // Check that the session works. |
| let mut buffer = Message::from_slice("message".as_bytes()).unwrap(); |
| client.encrypt(&mut buffer).unwrap(); |
| SessionCryptoForTesting::session_decrypt( |
| &mut dpe_decrypt_cs, |
| &mut buffer, |
| ) |
| .unwrap(); |
| assert_eq!("message".as_bytes(), buffer.as_slice()); |
| SessionCryptoForTesting::session_encrypt( |
| &mut dpe_encrypt_cs, |
| &mut buffer, |
| ) |
| .unwrap(); |
| dpe_encrypt_cs.commit(); |
| client.decrypt(&mut buffer).unwrap(); |
| assert_eq!("message".as_bytes(), buffer.as_slice()); |
| |
| // Do it again to check session state still works. |
| client.encrypt(&mut buffer).unwrap(); |
| SessionCryptoForTesting::session_decrypt( |
| &mut dpe_decrypt_cs, |
| &mut buffer, |
| ) |
| .unwrap(); |
| assert_eq!("message".as_bytes(), buffer.as_slice()); |
| SessionCryptoForTesting::session_encrypt( |
| &mut dpe_encrypt_cs, |
| &mut buffer, |
| ) |
| .unwrap(); |
| dpe_encrypt_cs.commit(); |
| client.decrypt(&mut buffer).unwrap(); |
| assert_eq!("message".as_bytes(), buffer.as_slice()); |
| } |
| |
| #[test] |
| fn derived_session() { |
| // Set up a session from which to derive. |
| let mut client = SessionClientForTesting::new(); |
| let dh_key: DhPrivateKey = Default::default(); |
| let dh_public_key = get_dh_public_key::< |
| <DepsForTesting as NoiseCryptoDeps>::DH, |
| >(&dh_key) |
| .unwrap(); |
| let handshake1 = client |
| .start_handshake_with_known_public_key(&dh_public_key) |
| .unwrap(); |
| let mut dpe_decrypt_cs = Default::default(); |
| let mut dpe_encrypt_cs = Default::default(); |
| let mut psk_seed = Default::default(); |
| let mut handshake2 = Default::default(); |
| let payload = HandshakePayload::from_slice("pay".as_bytes()).unwrap(); |
| SessionCryptoForTesting::new_session_handshake( |
| &dh_key, |
| &handshake1, |
| &payload, |
| &mut handshake2, |
| &mut dpe_decrypt_cs, |
| &mut dpe_encrypt_cs, |
| &mut psk_seed, |
| ) |
| .unwrap(); |
| assert_eq!(payload, client.finish_handshake(&handshake2).unwrap()); |
| |
| // Derive a second session. |
| let mut client2 = SessionClientForTesting::new(); |
| let client_psk = client.derive_psk(); |
| // Simulate the session state after command decryption on the DPE side |
| // as expected by the DPE PSK logic. |
| let mut buffer = Message::from_slice("message".as_bytes()).unwrap(); |
| client.encrypt(&mut buffer).unwrap(); |
| SessionCryptoForTesting::session_decrypt( |
| &mut dpe_decrypt_cs, |
| &mut buffer, |
| ) |
| .unwrap(); |
| let dpe_psk = SessionCryptoForTesting::derive_psk_from_session( |
| &psk_seed, |
| &dpe_decrypt_cs, |
| &dpe_encrypt_cs, |
| ) |
| .unwrap(); |
| let handshake1 = client2.start_handshake_with_psk(&client_psk).unwrap(); |
| let mut dpe_decrypt_cs2 = Default::default(); |
| let mut dpe_encrypt_cs2 = Default::default(); |
| let mut psk_seed2 = Default::default(); |
| SessionCryptoForTesting::derive_session_handshake( |
| &dpe_psk, |
| &handshake1, |
| &payload, |
| &mut handshake2, |
| &mut dpe_decrypt_cs2, |
| &mut dpe_encrypt_cs2, |
| &mut psk_seed2, |
| ) |
| .unwrap(); |
| assert_eq!(payload, client2.finish_handshake(&handshake2).unwrap()); |
| |
| // Check that the second session works. |
| let mut buffer = Message::from_slice("message".as_bytes()).unwrap(); |
| client2.encrypt(&mut buffer).unwrap(); |
| SessionCryptoForTesting::session_decrypt( |
| &mut dpe_decrypt_cs2, |
| &mut buffer, |
| ) |
| .unwrap(); |
| assert_eq!("message".as_bytes(), buffer.as_slice()); |
| SessionCryptoForTesting::session_encrypt( |
| &mut dpe_encrypt_cs2, |
| &mut buffer, |
| ) |
| .unwrap(); |
| dpe_encrypt_cs2.commit(); |
| client2.decrypt(&mut buffer).unwrap(); |
| assert_eq!("message".as_bytes(), buffer.as_slice()); |
| |
| // Check that the first session also still works. |
| let mut buffer = Message::from_slice("message".as_bytes()).unwrap(); |
| client.encrypt(&mut buffer).unwrap(); |
| SessionCryptoForTesting::session_decrypt( |
| &mut dpe_decrypt_cs, |
| &mut buffer, |
| ) |
| .unwrap(); |
| assert_eq!("message".as_bytes(), buffer.as_slice()); |
| SessionCryptoForTesting::session_encrypt( |
| &mut dpe_encrypt_cs, |
| &mut buffer, |
| ) |
| .unwrap(); |
| dpe_encrypt_cs.commit(); |
| client.decrypt(&mut buffer).unwrap(); |
| assert_eq!("message".as_bytes(), buffer.as_slice()); |
| } |
| } |