use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey};
use rand::rngs::OsRng;
use sha2::{Digest, Sha512};
use x25519_dalek::{PublicKey as X25519PublicKey, StaticSecret};
use crate::crypto::aes::AesCtr;
use crate::error::CryptoError;
const SALT_KEY: &[u8] = b"Pair-Verify-AES-Key";
const SALT_IV: &[u8] = b"Pair-Verify-AES-IV";
pub struct Pairing {
signing_key: SigningKey,
}
impl Pairing {
pub fn generate() -> Result<Self, CryptoError> {
Ok(Self {
signing_key: SigningKey::generate(&mut OsRng),
})
}
pub fn from_seed(seed: &[u8; 32]) -> Self {
Self {
signing_key: SigningKey::from_bytes(seed),
}
}
pub fn public_key(&self) -> [u8; 32] {
self.signing_key.verifying_key().to_bytes()
}
pub fn create_session(&self) -> PairingSession {
PairingSession {
status: Status::Initial,
ed_signing_key: self.signing_key.clone(),
ed_theirs: [0u8; 32],
ecdh_ours: [0u8; 32],
ecdh_theirs: [0u8; 32],
ecdh_secret: [0u8; 32],
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
enum Status {
Initial,
Handshake,
Finished,
}
pub struct PairingSession {
status: Status,
ed_signing_key: SigningKey,
ed_theirs: [u8; 32],
ecdh_ours: [u8; 32],
ecdh_theirs: [u8; 32],
ecdh_secret: [u8; 32],
}
impl PairingSession {
pub fn ecdh_secret(&self) -> &[u8; 32] {
&self.ecdh_secret
}
fn derive_key_internal(&self, salt: &[u8], key_len: usize) -> Result<Vec<u8>, CryptoError> {
if key_len > 64 {
return Err(CryptoError::PairingHandshake("key_len > 64".into()));
}
let mut hasher = Sha512::new();
hasher.update(salt);
hasher.update(self.ecdh_secret);
let hash = hasher.finalize();
Ok(hash[..key_len].to_vec())
}
fn derive_aes(&self) -> Result<(AesCtr, [u8; 16], [u8; 16]), CryptoError> {
let k = self.derive_key_internal(SALT_KEY, 16)?;
let i = self.derive_key_internal(SALT_IV, 16)?;
let mut key = [0u8; 16];
let mut iv = [0u8; 16];
key.copy_from_slice(&k);
iv.copy_from_slice(&i);
Ok((AesCtr::new(&key, &iv), key, iv))
}
pub fn handshake(&mut self, ecdh_key: &[u8; 32], ed_key: &[u8; 32]) -> Result<(), CryptoError> {
if self.status == Status::Finished {
return Err(CryptoError::PairingHandshake("already finished".into()));
}
let our_secret = StaticSecret::random_from_rng(OsRng);
let our_public = X25519PublicKey::from(&our_secret);
let their_public = X25519PublicKey::from(*ecdh_key);
let shared = our_secret.diffie_hellman(&their_public);
self.ecdh_theirs = *ecdh_key;
self.ed_theirs = *ed_key;
self.ecdh_ours = our_public.to_bytes();
self.ecdh_secret = *shared.as_bytes();
self.status = Status::Handshake;
Ok(())
}
pub fn get_public_key(&self) -> Result<[u8; 32], CryptoError> {
if self.status != Status::Handshake {
return Err(CryptoError::PairingHandshake("not in handshake state".into()));
}
Ok(self.ecdh_ours)
}
pub fn get_signature(&self) -> Result<[u8; 64], CryptoError> {
if self.status != Status::Handshake {
return Err(CryptoError::PairingHandshake("not in handshake state".into()));
}
let mut sig_msg = [0u8; 64];
sig_msg[..32].copy_from_slice(&self.ecdh_ours);
sig_msg[32..].copy_from_slice(&self.ecdh_theirs);
let signature = self.ed_signing_key.sign(&sig_msg);
let mut sig_bytes = signature.to_bytes();
let (mut aes, _, _) = self.derive_aes()?;
aes.encrypt(&mut sig_bytes);
Ok(sig_bytes)
}
pub fn finish(&mut self, signature: &[u8; 64]) -> Result<(), CryptoError> {
if self.status != Status::Handshake {
return Err(CryptoError::PairingHandshake("not in handshake state".into()));
}
let (mut aes, _, _) = self.derive_aes()?;
let mut dummy = [0u8; 64];
aes.encrypt(&mut dummy);
let mut sig_buf = *signature;
aes.encrypt(&mut sig_buf);
let mut sig_msg = [0u8; 64];
sig_msg[..32].copy_from_slice(&self.ecdh_theirs);
sig_msg[32..].copy_from_slice(&self.ecdh_ours);
let verifying_key = VerifyingKey::from_bytes(&self.ed_theirs).map_err(|_| CryptoError::PairingVerify)?;
let sig = Signature::from_bytes(&sig_buf);
verifying_key
.verify(&sig_msg, &sig)
.map_err(|_| CryptoError::PairingVerify)?;
self.status = Status::Finished;
Ok(())
}
pub fn derive_key(&self, salt: &[u8], key_len: usize) -> Result<Vec<u8>, CryptoError> {
self.derive_key_internal(salt, key_len)
}
}