use crate::Secret;
use commonware_codec::{FixedSize, Read, ReadExt, Write};
use rand_core::CryptoRngCore;
pub struct SharedSecret {
pub(crate) secret: Secret<x25519_dalek::SharedSecret>,
}
impl SharedSecret {
const fn new(secret: x25519_dalek::SharedSecret) -> Self {
Self {
secret: Secret::new(secret),
}
}
}
#[cfg_attr(test, derive(Debug, PartialEq))]
pub struct EphemeralPublicKey {
inner: x25519_dalek::PublicKey,
}
impl Write for EphemeralPublicKey {
fn write(&self, buf: &mut impl bytes::BufMut) {
buf.put_slice(self.inner.as_bytes());
}
}
impl FixedSize for EphemeralPublicKey {
const SIZE: usize = 32;
}
impl Read for EphemeralPublicKey {
type Cfg = ();
fn read_cfg(
buf: &mut impl bytes::Buf,
_cfg: &Self::Cfg,
) -> Result<Self, commonware_codec::Error> {
let bytes: [u8; 32] = ReadExt::read(buf)?;
Ok(Self {
inner: bytes.into(),
})
}
}
#[cfg(feature = "arbitrary")]
impl<'a> arbitrary::Arbitrary<'a> for EphemeralPublicKey {
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
let bytes: [u8; 32] = u.arbitrary()?;
Ok(Self {
inner: bytes.into(),
})
}
}
pub struct SecretKey {
inner: Secret<x25519_dalek::EphemeralSecret>,
}
impl SecretKey {
pub fn new(rng: impl CryptoRngCore) -> Self {
Self {
inner: Secret::new(x25519_dalek::EphemeralSecret::random_from_rng(rng)),
}
}
pub fn public(&self) -> EphemeralPublicKey {
self.inner.expose(|secret| EphemeralPublicKey {
inner: x25519_dalek::PublicKey::from(secret),
})
}
pub fn exchange(self, other: &EphemeralPublicKey) -> Option<SharedSecret> {
let secret = self.inner.expose_unwrap();
let out = secret.diffie_hellman(&other.inner);
if !out.was_contributory() {
return None;
}
Some(SharedSecret::new(out))
}
}
#[cfg(all(test, feature = "arbitrary"))]
mod conformance {
use super::*;
use commonware_codec::conformance::CodecConformance;
commonware_conformance::conformance_tests! {
CodecConformance<EphemeralPublicKey>,
}
}