use core::marker::PhantomData;
use digest::Output;
use rand_core::CryptoRng;
use zeroize::Zeroize;
use crate::ciphersuite::{CipherSuite, Kem};
use crate::commitment;
use crate::error::Error;
use crate::initiator::{MessageOne, MessageThree};
use crate::sas::{compute_sas, derive_session_key};
use crate::verification::ProtocolOutput;
use crate::Nonce;
#[derive(Clone)]
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(bound(
serialize = "<CS::Kem as Kem>::Ciphertext: serde::Serialize",
deserialize = "<CS::Kem as Kem>::Ciphertext: serde::Deserialize<'de>",
))
)]
pub struct MessageTwo<CS: CipherSuite> {
pub(crate) ct: <CS::Kem as Kem>::Ciphertext,
pub(crate) responder_nonce: Nonce,
}
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(bound(
serialize = "<CS::Kem as Kem>::EncapsulationKey: serde::Serialize, <CS::Kem as Kem>::Ciphertext: serde::Serialize, <CS::Kem as Kem>::SharedSecret: serde::Serialize",
deserialize = "<CS::Kem as Kem>::EncapsulationKey: serde::Deserialize<'de>, <CS::Kem as Kem>::Ciphertext: serde::Deserialize<'de>, <CS::Kem as Kem>::SharedSecret: serde::Deserialize<'de>",
))
)]
pub struct Responder<CS: CipherSuite> {
ek: <CS::Kem as Kem>::EncapsulationKey,
commitment: Output<CS::Hash>,
responder_nonce: Nonce,
ct: <CS::Kem as Kem>::Ciphertext,
#[cfg_attr(feature = "serde", serde(deserialize_with = "require_some"))]
shared_secret: Option<<CS::Kem as Kem>::SharedSecret>,
_marker: PhantomData<CS>,
}
impl<CS: CipherSuite> Drop for Responder<CS> {
fn drop(&mut self) {
self.responder_nonce.zeroize();
self.ek.zeroize();
self.ct.zeroize();
self.commitment.as_mut_slice().zeroize();
if let Some(ref mut ss) = self.shared_secret {
ss.zeroize();
}
}
}
impl<CS: CipherSuite> Responder<CS> {
pub fn start(
rng: &mut impl CryptoRng,
msg1: MessageOne<CS>,
) -> Result<(Self, MessageTwo<CS>), Error> {
let (ct, shared_secret) =
CS::Kem::encaps(&msg1.ek, rng).map_err(|_| Error::EncapsulationFailed)?;
let mut responder_nonce = [0u8; 32];
rng.fill_bytes(&mut responder_nonce);
let state = Self {
ek: msg1.ek,
commitment: msg1.commitment,
responder_nonce,
ct: ct.clone(),
shared_secret: Some(shared_secret),
_marker: PhantomData,
};
let message = MessageTwo {
ct,
responder_nonce,
};
Ok((state, message))
}
pub fn finish(mut self, msg3: MessageThree) -> Result<ProtocolOutput<CS>, Error> {
commitment::open::<CS::Hash>(self.ek.as_ref(), &msg3.initiator_nonce, &self.commitment)?;
let sas = compute_sas::<CS::Hash>(
&self.responder_nonce,
&msg3.initiator_nonce,
self.ct.as_ref(),
);
let mut kem_ss = self
.shared_secret
.take()
.expect("shared_secret should always be Some");
let session_key = derive_session_key::<CS::Hash>(
self.ek.as_ref(),
self.ct.as_ref(),
&self.responder_nonce,
&msg3.initiator_nonce,
kem_ss.as_ref(),
);
kem_ss.zeroize();
Ok(ProtocolOutput {
sas,
session_key,
_marker: PhantomData,
})
}
}
#[cfg(feature = "serde")]
fn require_some<'de, D, T>(deserializer: D) -> Result<Option<T>, D::Error>
where
D: serde::Deserializer<'de>,
T: serde::Deserialize<'de>,
{
use serde::Deserialize;
let val = Option::<T>::deserialize(deserializer)?;
if val.is_none() {
return Err(serde::de::Error::custom("shared_secret must not be None"));
}
Ok(val)
}