#[cfg(all(test, feature = "client", feature = "server"))]
#[path = "../../tests/crypto/asymmetric.rs"]
mod tests;
use blake3::Hasher;
use blake3::hazmat::hash_derive_key_context;
use cfg_if::cfg_if;
#[cfg(feature = "client")]
use classic_mceliece_rust::encapsulate;
use classic_mceliece_rust::{CRYPTO_BYTES, CRYPTO_CIPHERTEXTBYTES};
use ed25519_dalek::Signature;
use x25519_dalek::{EphemeralSecret, PublicKey};
use crate::bytes::{ByteBuffer, ByteBufferMut, BytePool, DynamicByteBuffer, FixedByteBuffer};
use crate::certificate::ObfuscationBufferContainer;
#[cfg(any(feature = "client", feature = "full_software", feature = "full_hardware"))]
use crate::crypto::error::HandshakeError;
use crate::crypto::symmetric::{ANONYMOUS_NONCE_LEN, Symmetric, decrypt_anonymously, encrypt_anonymously};
#[cfg(any(feature = "full_software", feature = "full_hardware"))]
use crate::crypto::symmetric::{NONCE_LEN, SYMMETRIC_BUILT_IN_AUTH_LEN};
use crate::utils::random::{SupportRng, get_rng};
cfg_if! {
if #[cfg(feature = "server")] {
use classic_mceliece_rust::{Ciphertext, decapsulate};
use ed25519_dalek::ed25519::signature::Signer;
use crate::certificate::ServerSecret;
use crate::crypto::ServerData;
}
}
#[cfg(all(feature = "client", any(feature = "full_software", feature = "full_hardware")))]
use x25519_dalek::StaticSecret;
#[cfg(feature = "client")]
use crate::certificate::ClientCertificate;
#[cfg(feature = "client")]
use crate::crypto::ClientData;
const X25519_KEY_LENGTH: usize = 32;
const NONCE_LENGTH: usize = 32;
#[cfg(any(feature = "full_software", feature = "full_hardware"))]
pub(crate) const TAILER_C2S_OVERHEAD: usize = NONCE_LEN + SYMMETRIC_BUILT_IN_AUTH_LEN + X25519_KEY_LENGTH + ANONYMOUS_NONCE_LEN + NONCE_LENGTH;
const CLIENT_HANDSHAKE_HEADER_SIZE: usize = X25519_KEY_LENGTH + CRYPTO_CIPHERTEXTBYTES + 2 * ANONYMOUS_NONCE_LEN + NONCE_LENGTH;
const SERVER_HANDSHAKE_HEADER_SIZE: usize = X25519_KEY_LENGTH + Signature::BYTE_SIZE + ANONYMOUS_NONCE_LEN + NONCE_LENGTH;
const INITIAL_DATA_KEY: &str = "initial data obfuscation key";
const CLIENT_HANDSHAKE_OBFUSCATION_KEY: &str = "handshake client obfuscation key";
const SERVER_HANDSHAKE_OBFUSCATION_KEY: &str = "handshake server obfuscation key";
const HANDSHAKE_TRANSCRIPT_KEY: &str = "handshake transcript key";
const SESSION_KEY: &str = "session key";
#[cfg(any(feature = "full_software", feature = "full_hardware"))]
const MARSHALLING_OBFUSCATION_KEY: &str = "marshalling obfuscation key";
#[cfg(any(feature = "full_software", feature = "full_hardware"))]
const MARSHALLING_ENCRYPTION_KEY: &str = "marshalling encryption key";
#[cfg(feature = "client")]
impl ClientCertificate {
pub(crate) fn encapsulate_handshake_client(&self, pool: &BytePool, initial_data: &[u8]) -> (ClientData, DynamicByteBuffer, FixedByteBuffer<32>) {
let nonce = get_rng().random_byte_buffer::<NONCE_LENGTH>();
let ephemeral_secret = EphemeralSecret::random_from_rng(get_rng());
let mut ephemeral_public = pool.allocate_precise_from_array_with_capacity(&PublicKey::from(&ephemeral_secret).to_bytes(), 0, ANONYMOUS_NONCE_LEN);
let mut shared_secret_buffer = [0u8; CRYPTO_BYTES];
let (ciphertext, shared_secret) = encapsulate(&self.epk, &mut shared_secret_buffer, &mut get_rng());
let mut ciphertext_buffer = pool.allocate_precise_from_array_with_capacity(ciphertext.as_array(), 0, ANONYMOUS_NONCE_LEN);
let shared_fixed = FixedByteBuffer::from(shared_secret.as_array());
let masking_key_hash = Hasher::new_keyed(&hash_derive_key_context(CLIENT_HANDSHAKE_OBFUSCATION_KEY)).update(self.obfuscation_buffer().slice()).update(nonce.slice()).finalize();
let ephemeral_public_obfuscated = encrypt_anonymously(masking_key_hash.as_bytes(), &mut ephemeral_public);
let ciphertext_obfuscated = encrypt_anonymously(masking_key_hash.as_bytes(), &mut ciphertext_buffer);
let initial_encryption_key_hash = Hasher::new_keyed(&hash_derive_key_context(INITIAL_DATA_KEY)).update(shared_fixed.slice()).update(ephemeral_public_obfuscated.slice()).update(nonce.slice()).finalize();
let initial_encryption_key = FixedByteBuffer::from(*initial_encryption_key_hash.as_bytes());
let client_data = ClientData {
ephemeral_key: ephemeral_secret,
shared_secret: shared_fixed,
nonce,
initial_key: initial_encryption_key,
};
let handshake_buffer = pool.allocate_precise(0, pool.before_cap(), CLIENT_HANDSHAKE_HEADER_SIZE);
let handshake_secret = handshake_buffer.append_buf(&ephemeral_public_obfuscated).append_buf(&ciphertext_obfuscated).append_buf(&nonce);
let handshake_secret = if initial_data.is_empty() {
handshake_secret
} else {
let plaintext = pool.allocate_precise_from_slice_with_capacity(initial_data, 0, 0);
let mut cipher = Symmetric::new(&initial_encryption_key);
let encrypted = cipher.encrypt_auth(plaintext, None::<&DynamicByteBuffer>).expect("initial data encryption failed");
handshake_secret.append_buf(&encrypted)
};
(client_data, handshake_secret, initial_encryption_key)
}
pub(crate) fn decapsulate_handshake_client(&self, data: ClientData, handshake_secret: DynamicByteBuffer, pool: &BytePool) -> Result<(FixedByteBuffer<32>, DynamicByteBuffer), HandshakeError> {
if handshake_secret.len() < SERVER_HANDSHAKE_HEADER_SIZE {
return Err(HandshakeError::handshake_authentication_error(&format!("server handshake response too short: {} < {SERVER_HANDSHAKE_HEADER_SIZE}", handshake_secret.len())));
}
let (crypto_header, encrypted_initial_data) = if handshake_secret.len() > SERVER_HANDSHAKE_HEADER_SIZE {
let (header, enc_data) = handshake_secret.split_buf_start(SERVER_HANDSHAKE_HEADER_SIZE);
(header, Some(enc_data))
} else {
(handshake_secret, None)
};
let (mut ephemeral_public_obfuscated, rest) = crypto_header.split_buf_start(X25519_KEY_LENGTH + ANONYMOUS_NONCE_LEN);
let (transcript_signed, nonce) = rest.split_buf_start(Signature::BYTE_SIZE);
let masking_key_hash = Hasher::new_keyed(&hash_derive_key_context(SERVER_HANDSHAKE_OBFUSCATION_KEY)).update(self.obfuscation_buffer().slice()).update(nonce.slice()).finalize();
let ephemeral_public_deobfuscated = decrypt_anonymously(masking_key_hash.as_bytes(), &mut ephemeral_public_obfuscated);
let ephemeral_public_bytes: [u8; X25519_KEY_LENGTH] = (&ephemeral_public_deobfuscated).into();
let ephemeral_public = PublicKey::from(ephemeral_public_bytes);
let shared_secret = data.ephemeral_key.diffie_hellman(&ephemeral_public);
let transcript_signed_bytes: [u8; Signature::BYTE_SIZE] = (&transcript_signed).into();
let transcript_signed = Signature::from_bytes(&transcript_signed_bytes);
let transcript = Hasher::new_keyed(&hash_derive_key_context(HANDSHAKE_TRANSCRIPT_KEY)).update(data.shared_secret.slice()).update(shared_secret.as_bytes()).update(data.nonce.slice()).update(nonce.slice()).finalize();
if let Err(err) = self.vpk.verify_strict(transcript.as_bytes(), &transcript_signed) {
return Err(HandshakeError::handshake_authentication_error(&format!("server identity verification error: {err}")));
}
let session_key_hash = Hasher::new_keyed(&hash_derive_key_context(SESSION_KEY)).update(data.shared_secret.slice()).update(shared_secret.as_bytes()).update(transcript.as_bytes()).finalize();
let session_key = FixedByteBuffer::from(*session_key_hash.as_bytes());
let initial_data = if let Some(encrypted) = encrypted_initial_data {
let mut cipher = Symmetric::new(&data.initial_key);
cipher.decrypt_auth(encrypted, None::<&DynamicByteBuffer>).map_err(|e| HandshakeError::handshake_crypto_error("decrypting server initial data", e))?
} else {
pool.allocate(Some(0))
};
Ok((session_key, initial_data))
}
#[cfg(any(feature = "full_software", feature = "full_hardware"))]
pub fn encrypt_obfuscate(&self, plaintext: DynamicByteBuffer, pool: &BytePool) -> Result<DynamicByteBuffer, HandshakeError> {
let nonce = get_rng().random_byte_buffer::<NONCE_LENGTH>();
let ephemeral_secret = StaticSecret::random_from_rng(get_rng());
let mut ephemeral_public = pool.allocate_precise_from_array_with_capacity(&PublicKey::from(&ephemeral_secret).to_bytes(), 0, ANONYMOUS_NONCE_LEN);
let shared_secret = ephemeral_secret.diffie_hellman(&self.opk);
let masking_key_hash = Hasher::new_keyed(&hash_derive_key_context(MARSHALLING_OBFUSCATION_KEY)).update(self.opk.as_bytes()).update(nonce.slice()).finalize();
let ephemeral_public_obfuscated = encrypt_anonymously(masking_key_hash.as_bytes(), &mut ephemeral_public);
let encryption_key_hash = Hasher::new_keyed(&hash_derive_key_context(MARSHALLING_ENCRYPTION_KEY)).update(shared_secret.as_bytes()).finalize();
let encryption_key = FixedByteBuffer::from(*encryption_key_hash.as_bytes());
let ciphertext = Symmetric::new(&encryption_key).encrypt_auth(plaintext, Some(&nonce)).map_err(|e| HandshakeError::handshake_crypto_error("encrypting plaintext", e))?;
let payload = ciphertext.append_buf(&ephemeral_public_obfuscated).append_buf(&nonce);
Ok(payload)
}
}
#[cfg(feature = "server")]
impl ServerSecret<'_> {
pub fn decapsulate_handshake_server(&self, handshake_secret: DynamicByteBuffer, pool: &BytePool) -> Option<(ServerData, FixedByteBuffer<32>, DynamicByteBuffer)> {
if handshake_secret.len() < CLIENT_HANDSHAKE_HEADER_SIZE {
return None;
}
let (crypto_header, encrypted_initial_data) = if handshake_secret.len() > CLIENT_HANDSHAKE_HEADER_SIZE {
let (header, enc_data) = handshake_secret.split_buf_start(CLIENT_HANDSHAKE_HEADER_SIZE);
(header, Some(enc_data))
} else {
(handshake_secret, None)
};
let (mut ephemeral_public_obfuscated, rest) = crypto_header.split_buf_start(X25519_KEY_LENGTH + ANONYMOUS_NONCE_LEN);
let (mut ciphertext_obfuscated, nonce) = rest.split_buf_start(CRYPTO_CIPHERTEXTBYTES + ANONYMOUS_NONCE_LEN);
let masking_key_hash = Hasher::new_keyed(&hash_derive_key_context(CLIENT_HANDSHAKE_OBFUSCATION_KEY)).update(self.obfuscation_buffer().slice()).update(nonce.slice()).finalize();
let ciphertext_buffer = decrypt_anonymously(masking_key_hash.as_bytes(), &mut ciphertext_obfuscated);
let ciphertext_bytes: [u8; CRYPTO_CIPHERTEXTBYTES] = (&ciphertext_buffer).into();
let ciphertext = Ciphertext::from(ciphertext_bytes);
let mut shared_secret_buffer = [0u8; CRYPTO_BYTES];
let shared_secret = decapsulate(&ciphertext, &self.esk, &mut shared_secret_buffer);
let initial_encryption_key_hash = Hasher::new_keyed(&hash_derive_key_context(INITIAL_DATA_KEY)).update(shared_secret.as_array()).update(ephemeral_public_obfuscated.slice()).update(nonce.slice()).finalize();
let initial_encryption_key = FixedByteBuffer::from(*initial_encryption_key_hash.as_bytes());
let client_initial_data = if let Some(encrypted) = encrypted_initial_data {
let mut cipher = Symmetric::new(&initial_encryption_key);
cipher.decrypt_auth(encrypted, None::<&DynamicByteBuffer>).unwrap_or_else(|_| pool.allocate(Some(0)))
} else {
pool.allocate(Some(0))
};
let ephemeral_public_deobfuscated = decrypt_anonymously(masking_key_hash.as_bytes(), &mut ephemeral_public_obfuscated);
let ephemeral_public_bytes: [u8; X25519_KEY_LENGTH] = (&ephemeral_public_deobfuscated).into();
let ephemeral_public = PublicKey::from(ephemeral_public_bytes);
let nonce_fixed = FixedByteBuffer::from(<[u8; NONCE_LENGTH]>::try_from(nonce.slice()).expect("nonce must be NONCE_LENGTH bytes"));
let server_data = ServerData {
ephemeral_key: ephemeral_public,
shared_secret: FixedByteBuffer::from(shared_secret.as_array()),
nonce: nonce_fixed,
};
Some((server_data, initial_encryption_key, client_initial_data))
}
pub fn encapsulate_handshake_server(&self, data: ServerData, pool: &BytePool, initial_data: &[u8], initial_key: &impl ByteBuffer) -> (DynamicByteBuffer, FixedByteBuffer<32>) {
let nonce = get_rng().random_byte_buffer::<NONCE_LENGTH>();
let ephemeral_secret = EphemeralSecret::random_from_rng(get_rng());
let mut ephemeral_public = pool.allocate_precise_from_array_with_capacity(&PublicKey::from(&ephemeral_secret).to_bytes(), 0, ANONYMOUS_NONCE_LEN);
let shared_secret = ephemeral_secret.diffie_hellman(&data.ephemeral_key);
let transcript = Hasher::new_keyed(&hash_derive_key_context(HANDSHAKE_TRANSCRIPT_KEY)).update(data.shared_secret.slice()).update(shared_secret.as_bytes()).update(data.nonce.slice()).update(nonce.slice()).finalize();
let transcript_signed = self.vsk.sign(transcript.as_bytes());
let masking_key_hash = Hasher::new_keyed(&hash_derive_key_context(SERVER_HANDSHAKE_OBFUSCATION_KEY)).update(self.obfuscation_buffer().slice()).update(nonce.slice()).finalize();
let ephemeral_public_obfuscated = encrypt_anonymously(masking_key_hash.as_bytes(), &mut ephemeral_public);
let session_key_hash = Hasher::new_keyed(&hash_derive_key_context(SESSION_KEY)).update(data.shared_secret.slice()).update(shared_secret.as_bytes()).update(transcript.as_bytes()).finalize();
let session_key = FixedByteBuffer::from(*session_key_hash.as_bytes());
let handshake_buffer = pool.allocate_precise(0, pool.before_cap(), SERVER_HANDSHAKE_HEADER_SIZE);
let handshake_secret = handshake_buffer.append_buf(&ephemeral_public_obfuscated).append(&transcript_signed.to_bytes()).append_buf(&nonce);
let handshake_secret = if initial_data.is_empty() {
handshake_secret
} else {
let plaintext = pool.allocate_precise_from_slice_with_capacity(initial_data, 0, 0);
let mut cipher = Symmetric::new(initial_key);
let encrypted = cipher.encrypt_auth(plaintext, None::<&DynamicByteBuffer>).expect("server initial data encryption failed");
handshake_secret.append_buf(&encrypted)
};
(handshake_secret, session_key)
}
#[cfg(any(feature = "full_software", feature = "full_hardware"))]
pub fn decrypt_deobfuscate(&self, ciphertext: DynamicByteBuffer) -> Result<DynamicByteBuffer, HandshakeError> {
let (ciphertext, rest) = ciphertext.split_buf_end(X25519_KEY_LENGTH + ANONYMOUS_NONCE_LEN + NONCE_LENGTH);
let (mut ephemeral_public_obfuscated, nonce) = rest.split_buf_end(NONCE_LENGTH);
let masking_key_hash = Hasher::new_keyed(&hash_derive_key_context(MARSHALLING_OBFUSCATION_KEY)).update(self.opk.as_bytes()).update(nonce.slice()).finalize();
let ephemeral_public_deobfuscated = decrypt_anonymously(masking_key_hash.as_bytes(), &mut ephemeral_public_obfuscated);
let ephemeral_public_bytes: [u8; X25519_KEY_LENGTH] = (&ephemeral_public_deobfuscated).into();
let ephemeral_public = PublicKey::from(ephemeral_public_bytes);
let shared_secret = self.osk.diffie_hellman(&ephemeral_public);
let encryption_key_hash = Hasher::new_keyed(&hash_derive_key_context(MARSHALLING_ENCRYPTION_KEY)).update(shared_secret.as_bytes()).finalize();
let encryption_key = FixedByteBuffer::from(*encryption_key_hash.as_bytes());
Symmetric::new(&encryption_key).decrypt_auth(ciphertext, Some(&nonce)).map_err(|e| HandshakeError::handshake_crypto_error("decrypting plaintext", e))
}
}