use crate::{
dhkex::{DhError, DhKeyExchange},
kdf::{labeled_extract, Kdf as KdfTrait, LabeledExpand},
util::{enforce_equal_len, enforce_outbuf_len, KemSuiteId},
Deserializable, HpkeError, Serializable,
};
use generic_array::typenum::{self, Unsigned};
use subtle::{Choice, ConstantTimeEq};
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct PublicKey(x25519_dalek::PublicKey);
#[derive(Clone)]
pub struct PrivateKey(x25519_dalek::StaticSecret);
impl ConstantTimeEq for PrivateKey {
fn ct_eq(&self, other: &Self) -> Choice {
self.0.to_bytes().ct_eq(&other.0.to_bytes())
}
}
impl PartialEq for PrivateKey {
fn eq(&self, other: &Self) -> bool {
self.ct_eq(other).into()
}
}
impl Eq for PrivateKey {}
pub struct KexResult(x25519_dalek::SharedSecret);
impl Serializable for PublicKey {
type OutputSize = typenum::U32;
fn write_exact(&self, buf: &mut [u8]) {
enforce_outbuf_len::<Self>(buf);
buf.copy_from_slice(self.0.as_bytes());
}
}
impl Deserializable for PublicKey {
fn from_bytes(encoded: &[u8]) -> Result<Self, HpkeError> {
enforce_equal_len(Self::OutputSize::to_usize(), encoded.len())?;
let mut arr = [0u8; 32];
arr.copy_from_slice(encoded);
Ok(PublicKey(x25519_dalek::PublicKey::from(arr)))
}
}
impl Serializable for PrivateKey {
type OutputSize = typenum::U32;
fn write_exact(&self, buf: &mut [u8]) {
enforce_outbuf_len::<Self>(buf);
buf.copy_from_slice(self.0.as_bytes());
}
}
impl Deserializable for PrivateKey {
fn from_bytes(encoded: &[u8]) -> Result<Self, HpkeError> {
enforce_equal_len(Self::OutputSize::to_usize(), encoded.len())?;
let mut arr = [0u8; 32];
arr.copy_from_slice(encoded);
Ok(PrivateKey(x25519_dalek::StaticSecret::from(arr)))
}
}
impl Serializable for KexResult {
type OutputSize = typenum::U32;
fn write_exact(&self, buf: &mut [u8]) {
enforce_outbuf_len::<Self>(buf);
buf.copy_from_slice(self.0.as_bytes());
}
}
pub struct X25519 {}
impl DhKeyExchange for X25519 {
#[doc(hidden)]
type PublicKey = PublicKey;
#[doc(hidden)]
type PrivateKey = PrivateKey;
#[doc(hidden)]
type KexResult = KexResult;
#[doc(hidden)]
fn sk_to_pk(sk: &PrivateKey) -> PublicKey {
PublicKey(x25519_dalek::PublicKey::from(&sk.0))
}
#[doc(hidden)]
fn dh(sk: &PrivateKey, pk: &PublicKey) -> Result<KexResult, DhError> {
let res = sk.0.diffie_hellman(&pk.0);
if res.as_bytes().ct_eq(&[0u8; 32]).into() {
Err(DhError)
} else {
Ok(KexResult(res))
}
}
#[doc(hidden)]
fn derive_keypair<Kdf: KdfTrait>(suite_id: &KemSuiteId, ikm: &[u8]) -> (PrivateKey, PublicKey) {
let (_, hkdf_ctx) = labeled_extract::<Kdf>(&[], suite_id, b"dkp_prk", ikm);
let mut buf = [0u8; 32];
hkdf_ctx
.labeled_expand(suite_id, b"sk", &[], &mut buf)
.unwrap();
let sk = x25519_dalek::StaticSecret::from(buf);
let pk = x25519_dalek::PublicKey::from(&sk);
(PrivateKey(sk), PublicKey(pk))
}
}
#[cfg(test)]
mod tests {
use crate::{
dhkex::{x25519::X25519, Deserializable, DhKeyExchange, Serializable},
test_util::dhkex_gen_keypair,
};
use generic_array::typenum::Unsigned;
use rand::{rngs::StdRng, RngCore, SeedableRng};
#[test]
fn test_pubkey_serialize_correctness() {
type Kex = X25519;
let mut csprng = StdRng::from_os_rng();
let orig_bytes = {
let mut buf =
[0u8; <<Kex as DhKeyExchange>::PublicKey as Serializable>::OutputSize::USIZE];
csprng.fill_bytes(buf.as_mut_slice());
buf
};
let pk = <Kex as DhKeyExchange>::PublicKey::from_bytes(&orig_bytes).unwrap();
let pk_bytes = pk.to_bytes();
assert_eq!(orig_bytes.as_slice(), pk_bytes.as_slice());
}
#[test]
fn test_dh_serialize_correctness() {
type Kex = X25519;
let mut csprng = StdRng::from_os_rng();
let (sk, pk) = dhkex_gen_keypair::<Kex, _>(&mut csprng);
let (sk_bytes, pk_bytes) = (sk.to_bytes(), pk.to_bytes());
let new_sk = <Kex as DhKeyExchange>::PrivateKey::from_bytes(&sk_bytes).unwrap();
let new_pk = <Kex as DhKeyExchange>::PublicKey::from_bytes(&pk_bytes).unwrap();
assert!(new_sk == sk, "private key doesn't serialize correctly");
assert!(new_pk == pk, "public key doesn't serialize correctly");
}
}