use crate::registration::handshake::error::HandshakeError;
use crate::registration::handshake::KDF_SALT_LENGTH;
use nym_crypto::asymmetric::{ed25519, x25519};
use nym_crypto::symmetric::aead::{nonce_size, tag_size, Nonce};
use nym_sphinx::params::GatewayEncryptionAlgorithm;
pub trait HandshakeMessage {
fn into_bytes(self) -> Vec<u8>;
fn try_from_bytes(bytes: &[u8]) -> Result<Self, HandshakeError>
where
Self: Sized;
}
#[derive(Debug)]
pub struct Initialisation {
pub identity: ed25519::PublicKey,
pub ephemeral_dh: x25519::PublicKey,
pub initiator_salt: Vec<u8>,
}
#[derive(Debug)]
pub struct MaterialExchange {
pub signature_ciphertext: Vec<u8>,
pub nonce: Nonce<GatewayEncryptionAlgorithm>,
}
impl MaterialExchange {
#[cfg(not(target_arch = "wasm32"))]
pub fn attach_ephemeral_dh(self, ephemeral_dh: x25519::PublicKey) -> GatewayMaterialExchange {
GatewayMaterialExchange {
ephemeral_dh,
materials: self,
}
}
}
#[derive(Debug)]
pub struct GatewayMaterialExchange {
pub ephemeral_dh: x25519::PublicKey,
pub materials: MaterialExchange,
}
#[derive(Debug)]
pub struct Finalization {
pub success: bool,
}
impl Finalization {
pub fn ensure_success(&self) -> Result<(), HandshakeError> {
if !self.success {
return Err(HandshakeError::HandshakeFailure);
}
Ok(())
}
}
impl HandshakeMessage for Initialisation {
fn into_bytes(self) -> Vec<u8> {
self.identity
.to_bytes()
.into_iter()
.chain(self.ephemeral_dh.to_bytes())
.chain(self.initiator_salt)
.collect()
}
fn try_from_bytes(bytes: &[u8]) -> Result<Self, HandshakeError>
where
Self: Sized,
{
let current_len = ed25519::PUBLIC_KEY_LENGTH + x25519::PUBLIC_KEY_SIZE + KDF_SALT_LENGTH;
if bytes.len() != current_len {
return Err(HandshakeError::MalformedRequest);
}
let identity = ed25519::PublicKey::from_bytes(&bytes[..ed25519::PUBLIC_KEY_LENGTH])
.map_err(|_| HandshakeError::MalformedRequest)?;
#[allow(clippy::unwrap_used)]
let ephemeral_dh = x25519::PublicKey::from_bytes(
&bytes
[ed25519::PUBLIC_KEY_LENGTH..ed25519::PUBLIC_KEY_LENGTH + x25519::PUBLIC_KEY_SIZE],
)
.unwrap();
let initiator_salt = bytes[ed25519::PUBLIC_KEY_LENGTH + x25519::PUBLIC_KEY_SIZE..].to_vec();
Ok(Initialisation {
identity,
ephemeral_dh,
initiator_salt,
})
}
}
impl HandshakeMessage for MaterialExchange {
fn into_bytes(self) -> Vec<u8> {
self.signature_ciphertext
.iter()
.cloned()
.chain(self.nonce)
.collect()
}
fn try_from_bytes(bytes: &[u8]) -> Result<Self, HandshakeError>
where
Self: Sized,
{
let current_len = ed25519::SIGNATURE_LENGTH
+ tag_size::<GatewayEncryptionAlgorithm>()
+ nonce_size::<GatewayEncryptionAlgorithm>();
if bytes.len() != current_len {
return Err(HandshakeError::MalformedResponse);
}
let ciphertext_len = ed25519::SIGNATURE_LENGTH + tag_size::<GatewayEncryptionAlgorithm>();
let signature_ciphertext = bytes[..ciphertext_len].to_vec();
let nonce = Nonce::<GatewayEncryptionAlgorithm>::clone_from_slice(&bytes[ciphertext_len..]);
Ok(MaterialExchange {
signature_ciphertext,
nonce,
})
}
}
impl HandshakeMessage for GatewayMaterialExchange {
fn into_bytes(self) -> Vec<u8> {
self.ephemeral_dh
.to_bytes()
.into_iter()
.chain(self.materials.into_bytes())
.collect()
}
fn try_from_bytes(bytes: &[u8]) -> Result<Self, HandshakeError>
where
Self: Sized,
{
let legacy_len = x25519::PUBLIC_KEY_SIZE + ed25519::SIGNATURE_LENGTH;
let current_len = legacy_len
+ nonce_size::<GatewayEncryptionAlgorithm>()
+ tag_size::<GatewayEncryptionAlgorithm>();
if bytes.len() != legacy_len && bytes.len() != current_len {
return Err(HandshakeError::MalformedResponse);
}
#[allow(clippy::unwrap_used)]
let ephemeral_dh =
x25519::PublicKey::from_bytes(&bytes[..x25519::PUBLIC_KEY_SIZE]).unwrap();
let materials = MaterialExchange::try_from_bytes(&bytes[x25519::PUBLIC_KEY_SIZE..])?;
Ok(GatewayMaterialExchange {
ephemeral_dh,
materials,
})
}
}
impl HandshakeMessage for Finalization {
fn into_bytes(self) -> Vec<u8> {
if self.success {
vec![1]
} else {
vec![0]
}
}
fn try_from_bytes(bytes: &[u8]) -> Result<Self, HandshakeError>
where
Self: Sized,
{
if bytes.len() != 1 {
return Err(HandshakeError::MalformedResponse);
}
let success = bytes[0] == 1;
Ok(Finalization { success })
}
}