use core::ops::Mul;
use subtle::{Choice, ConstantTimeEq};
use tari_utilities::ByteArrayError;
use zeroize::{Zeroize, ZeroizeOnDrop};
use crate::keys::PublicKey;
#[derive(Zeroize, ZeroizeOnDrop)]
pub struct DiffieHellmanSharedSecret<P>(P)
where P: PublicKey;
impl<P> DiffieHellmanSharedSecret<P>
where
P: PublicKey,
for<'a> &'a <P as PublicKey>::K: Mul<&'a P, Output = P>,
{
pub fn new(sk: &P::K, pk: &P) -> Self {
Self(sk * pk)
}
pub fn from_canonical_bytes(bytes: &[u8]) -> Result<Self, ByteArrayError> {
let pk = P::from_canonical_bytes(bytes)?;
Ok(Self(pk))
}
pub fn as_bytes(&self) -> &[u8] {
self.0.as_bytes()
}
}
impl<P> ConstantTimeEq for DiffieHellmanSharedSecret<P>
where P: PublicKey
{
fn ct_eq(&self, other: &DiffieHellmanSharedSecret<P>) -> Choice {
self.0.ct_eq(&other.0)
}
}
impl<P> Eq for DiffieHellmanSharedSecret<P> where P: PublicKey {}
impl<P> PartialEq for DiffieHellmanSharedSecret<P>
where P: PublicKey
{
fn eq(&self, other: &Self) -> bool {
self.0.ct_eq(&other.0).into()
}
}
#[cfg(test)]
mod test {
use rand::rng;
use super::DiffieHellmanSharedSecret;
use crate::{
keys::{PublicKey, SecretKey},
ristretto::{RistrettoPublicKey, RistrettoSecretKey},
};
#[test]
fn test_dhke() {
let mut rng = rng();
let sk1 = RistrettoSecretKey::random(&mut rng);
let pk1 = RistrettoPublicKey::from_secret_key(&sk1);
let sk2 = RistrettoSecretKey::random(&mut rng);
let pk2 = RistrettoPublicKey::from_secret_key(&sk2);
let left = DiffieHellmanSharedSecret::<RistrettoPublicKey>::new(&sk1, &pk2);
let right = DiffieHellmanSharedSecret::<RistrettoPublicKey>::new(&sk2, &pk1);
assert_eq!(left.as_bytes(), right.as_bytes());
let left_bytes = left.as_bytes();
let new_left = DiffieHellmanSharedSecret::<RistrettoPublicKey>::from_canonical_bytes(left_bytes).unwrap();
assert_eq!(left.as_bytes(), new_left.as_bytes());
}
}