Rework SecretKey API to facilitate async (#371)

* Rework `SecretKey` API to facilitate async

* Remove left-over constraints
This commit is contained in:
daxpedda
2025-04-22 00:08:17 -07:00
committed by GitHub
parent 6b69e93dc9
commit d324584d79
20 changed files with 952 additions and 382 deletions
+71 -86
View File
@@ -14,8 +14,9 @@ use derive_where::derive_where;
use generic_array::{ArrayLength, GenericArray};
use rand::{CryptoRng, RngCore};
use crate::errors::{InternalError, ProtocolError};
use crate::errors::ProtocolError;
use crate::key_exchange::group::KeGroup;
use crate::key_exchange::tripledh::DiffieHellman;
/// A Keypair trait with public-private verification
#[cfg_attr(
@@ -28,12 +29,17 @@ use crate::key_exchange::group::KeGroup;
)]
#[derive_where(Clone)]
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; KG::Pk, S)]
pub struct KeyPair<KG: KeGroup, S: SecretKey<KG> = PrivateKey<KG>> {
pub struct KeyPair<KG: KeGroup, S: Clone = PrivateKey<KG>> {
pk: PublicKey<KG>,
sk: S,
}
impl<KG: KeGroup, S: SecretKey<KG>> KeyPair<KG, S> {
impl<KG: KeGroup, S: Clone> KeyPair<KG, S> {
/// Creates a new [`KeyPair`] from the given keys.
pub fn new(sk: S, pk: PublicKey<KG>) -> Self {
Self { pk, sk }
}
/// The public key component
pub fn public(&self) -> &PublicKey<KG> {
&self.pk
@@ -43,20 +49,6 @@ impl<KG: KeGroup, S: SecretKey<KG>> KeyPair<KG, S> {
pub fn private(&self) -> &S {
&self.sk
}
/// Obtains a [`KeyPair`] from a slice representing the private key
pub fn from_private_key_slice(input: &[u8]) -> Result<Self, ProtocolError<S::Error>> {
Self::from_private_key(S::deserialize(input)?)
}
/// Obtains a [`KeyPair`] from a private key
pub fn from_private_key(private_key: S) -> Result<Self, ProtocolError<S::Error>> {
let pk = private_key.public_key()?;
Ok(Self {
pk,
sk: private_key,
})
}
}
impl<KG: KeGroup> KeyPair<KG> {
@@ -106,50 +98,60 @@ where
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; KG::Sk)]
pub struct PrivateKey<KG: KeGroup>(KG::Sk);
/// A trait specifying the requirements for a private key container
pub trait SecretKey<KG: KeGroup>: Clone + Sized {
/// Custom error type that can be passed down to `InternalError::Custom`
impl<KG: KeGroup> PrivateKey<KG> {
/// Returns public key from private key
pub fn public_key(&self) -> PublicKey<KG> {
PublicKey(KG::public_key(self.0))
}
pub(crate) fn serialize(&self) -> GenericArray<u8, KG::SkLen> {
KG::serialize_sk(self.0)
}
/// Creates a [`PrivateKey`] from the given bytes.
pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
KG::deserialize_sk(input).map(Self)
}
}
impl<KG: KeGroup> PrivateKey<KG>
where
KG::Sk: DiffieHellman<KG>,
{
/// Diffie-Hellman key exchange implementation
pub(crate) fn ke_diffie_hellman(&self, pk: &PublicKey<KG>) -> GenericArray<u8, KG::PkLen> {
self.0.diffie_hellman(pk.0)
}
}
/// A trait to facilitate
/// [`ServerSetup::de/serialize`](crate::ServerSetup::serialize).
pub trait PrivateKeySerialization<KG: KeGroup>: Clone {
/// Custom error type that can be passed down to `ProtocolError::Custom`
type Error;
/// Serialization size in bytes.
type Len: ArrayLength<u8>;
/// Diffie-Hellman key exchange implementation
fn diffie_hellman(
&self,
pk: PublicKey<KG>,
) -> Result<GenericArray<u8, KG::PkLen>, InternalError<Self::Error>>;
/// Returns public key from private key
fn public_key(&self) -> Result<PublicKey<KG>, InternalError<Self::Error>>;
/// Serialization into bytes
fn serialize(&self) -> GenericArray<u8, Self::Len>;
fn serialize_key_pair(key_pair: &KeyPair<KG, Self>) -> GenericArray<u8, Self::Len>;
/// Deserialization from bytes
fn deserialize(input: &[u8]) -> Result<Self, InternalError<Self::Error>>;
fn deserialize_key_pair(input: &[u8]) -> Result<KeyPair<KG, Self>, ProtocolError<Self::Error>>;
}
impl<KG: KeGroup> SecretKey<KG> for PrivateKey<KG> {
impl<KG: KeGroup> PrivateKeySerialization<KG> for PrivateKey<KG> {
type Error = core::convert::Infallible;
type Len = KG::SkLen;
fn diffie_hellman(
&self,
pk: PublicKey<KG>,
) -> Result<GenericArray<u8, KG::PkLen>, InternalError> {
Ok(KG::diffie_hellman(pk.0, self.0))
fn serialize_key_pair(key_pair: &KeyPair<KG, Self>) -> GenericArray<u8, Self::Len> {
key_pair.private().serialize()
}
fn public_key(&self) -> Result<PublicKey<KG>, InternalError> {
Ok(PublicKey(KG::public_key(self.0)))
}
fn deserialize_key_pair(input: &[u8]) -> Result<KeyPair<KG, Self>, ProtocolError> {
let sk = PrivateKey::deserialize(input)?;
let pk = sk.public_key();
fn serialize(&self) -> GenericArray<u8, Self::Len> {
KG::serialize_sk(self.0)
}
fn deserialize(input: &[u8]) -> Result<Self, InternalError> {
KG::deserialize_sk(input).map(Self)
Ok(KeyPair::new(sk, pk))
}
}
@@ -184,7 +186,7 @@ pub struct PublicKey<KG: KeGroup>(KG::Pk);
impl<KG: KeGroup> PublicKey<KG> {
/// Convert from bytes
pub fn deserialize(key_bytes: &[u8]) -> Result<Self, InternalError> {
pub fn deserialize(key_bytes: &[u8]) -> Result<Self, ProtocolError> {
KG::deserialize_pk(key_bytes).map(Self)
}
@@ -192,6 +194,11 @@ impl<KG: KeGroup> PublicKey<KG> {
pub fn serialize(&self) -> GenericArray<u8, KG::PkLen> {
KG::serialize_pk(self.0)
}
/// Returns the inner [`KeGroup::Pk`].
pub fn to_group_type(&self) -> KG::Pk {
self.0
}
}
#[cfg(feature = "serde")]
@@ -223,7 +230,6 @@ mod tests {
use rand::rngs::OsRng;
use super::*;
use crate::errors::*;
use crate::util;
#[test]
@@ -255,15 +261,15 @@ mod tests {
fn pub_from_priv(kp in KeyPair::<$point>::uniform_keypair_strategy::<$point>()) {
let pk = kp.public();
let sk = kp.private();
prop_assert_eq!(&sk.public_key()?, pk);
prop_assert_eq!(&sk.public_key(), pk);
}
#[test]
fn dh(kp1 in KeyPair::<$point>::uniform_keypair_strategy::<$point>(),
kp2 in KeyPair::<$point>::uniform_keypair_strategy::<$point>()) {
let dh1 = kp2.private().diffie_hellman(kp1.public().clone())?;
let dh2 = kp1.private().diffie_hellman(kp2.public().clone())?;
let dh1 = kp2.private().ke_diffie_hellman(&kp1.public());
let dh2 = kp1.private().ke_diffie_hellman(kp2.public());
prop_assert_eq!(dh1, dh2);
}
@@ -272,7 +278,7 @@ mod tests {
fn private_key_slice(kp in KeyPair::<$point>::uniform_keypair_strategy::<$point>()) {
let sk_bytes = kp.private().serialize().to_vec();
let kp2 = KeyPair::<$point>::from_private_key_slice(&sk_bytes)?;
let kp2 = PrivateKey::<$point>::deserialize_key_pair(&sk_bytes)?;
let kp2_private_bytes = kp2.private().serialize().to_vec();
prop_assert_eq!(sk_bytes, kp2_private_bytes);
@@ -320,38 +326,15 @@ mod tests {
#[derive(Clone)]
struct RemoteKey(PrivateKey<KeCurve>);
impl SecretKey<KeCurve> for RemoteKey {
type Error = core::convert::Infallible;
type Len = <KeCurve as KeGroup>::SkLen;
fn diffie_hellman(
&self,
pk: PublicKey<KeCurve>,
) -> Result<GenericArray<u8, <KeCurve as KeGroup>::PkLen>, InternalError<Self::Error>>
{
self.0.diffie_hellman(pk)
}
fn public_key(&self) -> Result<PublicKey<KeCurve>, InternalError<Self::Error>> {
self.0.public_key()
}
fn serialize(&self) -> GenericArray<u8, Self::Len> {
self.0.serialize()
}
fn deserialize(input: &[u8]) -> Result<Self, InternalError<Self::Error>> {
PrivateKey::deserialize(input).map(Self)
}
}
const PASSWORD: &str = "password";
let sk = KeCurve::random_sk(&mut OsRng);
let sk = RemoteKey(PrivateKey(sk));
let keypair = KeyPair::from_private_key(sk).unwrap();
let sk = PrivateKey(KeCurve::random_sk(&mut OsRng));
let pk = sk.public_key();
let sk = RemoteKey(sk);
let keypair = KeyPair::new(sk, pk);
let server_setup = ServerSetup::<Default, RemoteKey>::new_with_key(&mut OsRng, keypair);
let server_setup =
ServerSetup::<Default, RemoteKey>::new_with_key_pair(&mut OsRng, keypair);
let ClientRegistrationStartResult {
message,
@@ -373,11 +356,7 @@ mod tests {
message,
state: client,
} = ClientLogin::<Default>::start(&mut OsRng, PASSWORD.as_bytes()).unwrap();
let ServerLoginStartResult {
message,
state: server,
..
} = ServerLogin::start(
let builder = ServerLogin::builder(
&mut OsRng,
&server_setup,
Some(file),
@@ -386,6 +365,12 @@ mod tests {
ServerLoginStartParameters::default(),
)
.unwrap();
let shared_secret = builder.private_key().0.ke_diffie_hellman(builder.data());
let ServerLoginStartResult {
message,
state: server,
..
} = builder.build(shared_secret).unwrap();
let ClientLoginFinishResult { message, .. } = client
.finish(
PASSWORD.as_bytes(),