use alloc::vec::Vec;
use chacha20poly1305::aead::Aead;
use chacha20poly1305::{AeadCore, ChaCha20Poly1305, Key, KeyInit, Nonce};
use fmd::DetectionKey;
use rand_core::{CryptoRng, RngCore};
use serde::de::DeserializeOwned;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use thiserror::Error;
use crate::db::EncKey;
use crate::{ClientMsg, MsgToHost};
#[derive(Error, Debug)]
pub enum RatlsError {
#[error("Cannot perform Diffie-Hellman on a connection that is already initialized")]
AlreadyInitialized,
#[error("Shared Secret was non-contributory. This suggests a man-in-the-middle attack.")]
NonContributory,
#[error("Cannot encrypt to a non-initialized channel")]
NotInitialized,
#[error("Could not decrypt message")]
Decryption,
#[error("Failed to deserialize message with: {0}")]
Deserialize(serde_cbor::Error),
}
#[derive(Debug, Clone)]
pub struct TlsCiphertext {
payload: Vec<u8>,
nonce: Nonce,
}
#[derive(Deserialize, Serialize)]
pub struct FmdKeyRegistration {
pub fmd_key: DetectionKey,
pub enc_key: EncKey,
pub birthday: Option<u64>,
}
impl Serialize for TlsCiphertext {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
#[derive(Serialize)]
struct SimplifiedCiphertext {
payload: Vec<u8>,
nonce: Vec<u8>,
}
let simplified = SimplifiedCiphertext {
payload: self.payload.clone(),
nonce: self.nonce.as_slice().to_vec(),
};
simplified.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for TlsCiphertext {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
struct SimplifiedCiphertext {
payload: Vec<u8>,
nonce: Vec<u8>,
}
let simplified = SimplifiedCiphertext::deserialize(deserializer)?;
Ok(Self {
payload: simplified.payload,
nonce: *Nonce::from_slice(&simplified.nonce),
})
}
}
pub enum Connection {
Handshake {
ephemeral_key: x25519_dalek::EphemeralSecret,
},
Initialized {
shared_key: ChaCha20Poly1305,
},
}
impl Connection {
pub fn new(rng: impl CryptoRng + RngCore) -> Self {
Self::Handshake {
ephemeral_key: x25519_dalek::EphemeralSecret::random_from_rng(rng),
}
}
pub fn client_send(&self, nonce: u64) -> Result<ClientMsg, RatlsError> {
match &self {
Self::Handshake { ephemeral_key } => Ok(ClientMsg::RegisterKey {
nonce,
pk: x25519_dalek::PublicKey::from(ephemeral_key)
.to_bytes()
.into(),
}),
Self::Initialized { .. } => Err(RatlsError::AlreadyInitialized),
}
}
pub fn enclave_reply(&self, report: Vec<u8>) -> Result<MsgToHost, RatlsError> {
match &self {
Self::Handshake { .. } => Ok(MsgToHost::RATLS { report }),
Self::Initialized { .. } => Err(RatlsError::AlreadyInitialized),
}
}
pub fn initialize(self, pk: x25519_dalek::PublicKey) -> Result<Self, RatlsError> {
let Self::Handshake { ephemeral_key } = self else {
return Err(RatlsError::AlreadyInitialized);
};
let shared_secret = ephemeral_key.diffie_hellman(&pk);
let shared_key = if shared_secret.was_contributory() {
ChaCha20Poly1305::new(Key::from_slice(shared_secret.as_bytes()))
} else {
return Err(RatlsError::NonContributory);
};
Ok(Self::Initialized { shared_key })
}
pub fn encrypt_msg<T: CryptoRng + RngCore>(
&self,
payload: &[u8],
rng: &mut T,
) -> Result<TlsCiphertext, RatlsError> {
if let Self::Initialized { shared_key } = &self {
let nonce = ChaCha20Poly1305::generate_nonce(rng);
Ok(TlsCiphertext {
payload: shared_key.encrypt(&nonce, payload).unwrap(),
nonce,
})
} else {
Err(RatlsError::NotInitialized)
}
}
pub fn decrypt_msg<T: DeserializeOwned>(&self, msg: &TlsCiphertext) -> Result<T, RatlsError> {
if let Self::Initialized { shared_key } = &self {
shared_key
.decrypt(&msg.nonce, &*msg.payload)
.or(Err(RatlsError::Decryption))
.and_then(|p| serde_cbor::from_slice(p.as_slice()).map_err(RatlsError::Deserialize))
} else {
Err(RatlsError::NotInitialized)
}
}
}