SIGMA-I Key Exchange (#378)
* Move `KeGroup` to `KeyExchange::Group` - Introduce `KeyExchange::Hash`, which separates the OPRF hash from the one used in `KeyExchange`. - Remove `De/Serialize` requirement on key exchange messages and states, which forced a lot of where bounds on downstream users. - Rename `KeGroup` to `Group`. - Replace `D` generic for hash with `H`. * Use `voprf::derive_key()` directly * Implement SIGMA-I key exchange * Improve `KeyExchange` for SIGMA-I and Ed25519 * Implement EdDSA * Un-qualify some method calls * SIGMA-I: only include client identity in client mac * SIGMA-I: include server mac in client signature * Expose key exchange types in `crate` & move modules * Implement Ed25519ph * Document `ed25519` crate feature * Remove `ristretto255-voprf` crate feature * Adjust CI crate feature testing * Fix Rustdoc * Remove unnecessary generic parameters from SIGMA-I * Properly mark to-do's with TODO * Assorted fixes * SIGMA-I: include context in signature * SIGMA-I: include identifiers in signature * Merge `ServerLoginStart/FinishParameters` * Re-export more necessary types * More carefully expose types * Add ECDSA test * SIGMA-I: share context hashing * De-duplicate client static public key storage * Hide `KeyExchange` better * Use the correct hash in the root documentation * Bump `derive-where` * Format documentation examples a bit further * Add remote OPRF seed documentation * Rename `deserialize_key_pair` to `deserialize_take_key_pair` * Add more key tests * Remove `SharedSecret` trait * SIGMA-I refactor message API * Share more implementation between 3DH and SIGMA-I * Remove unnecessary zero scalar check for Curve25519 * Use correct hash in test * Add some more TODOs * Exclude `tests` folder from Cargo publishing * Enable missing dependencies * Use right crate for testing Ed25519 * Remove unnecessary `Sized` constraints * Remove unnecessary `ecdsa` crate features * Move signature de/serialization to trait methods * Nit: move import to appropriate location * Add warning to SIGMA-I
This commit is contained in:
+400
-190
@@ -11,12 +11,16 @@
|
||||
#![allow(unsafe_code)]
|
||||
|
||||
use derive_where::derive_where;
|
||||
use digest::{Output, OutputSizeUser};
|
||||
use generic_array::{ArrayLength, GenericArray};
|
||||
use rand::{CryptoRng, RngCore};
|
||||
|
||||
use crate::ciphersuite::CipherSuite;
|
||||
use crate::errors::ProtocolError;
|
||||
use crate::key_exchange::group::KeGroup;
|
||||
use crate::key_exchange::tripledh::DiffieHellman;
|
||||
use crate::key_exchange::group::Group;
|
||||
use crate::key_exchange::shared::DiffieHellman;
|
||||
use crate::key_exchange::sigma_i::{Message, MessageBuilder, SignatureProtocol};
|
||||
use crate::serialization::SliceExt;
|
||||
|
||||
/// A Keypair trait with public-private verification
|
||||
#[cfg_attr(
|
||||
@@ -28,20 +32,24 @@ use crate::key_exchange::tripledh::DiffieHellman;
|
||||
))
|
||||
)]
|
||||
#[derive_where(Clone)]
|
||||
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; KG::Pk, SK)]
|
||||
pub struct KeyPair<KG: KeGroup, SK: Clone = PrivateKey<KG>> {
|
||||
pk: PublicKey<KG>,
|
||||
#[derive_where(Eq, Hash, Ord, PartialEq, PartialOrd; G::Pk, SK)]
|
||||
// `NonZeroScalar` doesn't implement `Debug`.
|
||||
// TODO: remove after `elliptic-curve` bump to v0.14.
|
||||
#[cfg_attr(not(test), derive_where(Debug; G::Pk, SK))]
|
||||
#[cfg_attr(test, derive_where(Debug), derive_where(skip_inner(Debug)))]
|
||||
pub struct KeyPair<G: Group, SK: Clone = PrivateKey<G>> {
|
||||
pk: PublicKey<G>,
|
||||
sk: SK,
|
||||
}
|
||||
|
||||
impl<KG: KeGroup, SK: Clone> KeyPair<KG, SK> {
|
||||
impl<G: Group, SK: Clone> KeyPair<G, SK> {
|
||||
/// Creates a new [`KeyPair`] from the given keys.
|
||||
pub fn new(sk: SK, pk: PublicKey<KG>) -> Self {
|
||||
pub fn new(sk: SK, pk: PublicKey<G>) -> Self {
|
||||
Self { pk, sk }
|
||||
}
|
||||
|
||||
/// The public key component
|
||||
pub fn public(&self) -> &PublicKey<KG> {
|
||||
pub fn public(&self) -> &PublicKey<G> {
|
||||
&self.pk
|
||||
}
|
||||
|
||||
@@ -51,15 +59,22 @@ impl<KG: KeGroup, SK: Clone> KeyPair<KG, SK> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<KG: KeGroup> KeyPair<KG> {
|
||||
impl<G: Group> KeyPair<G> {
|
||||
pub(crate) fn random<R: RngCore + CryptoRng>(rng: &mut R) -> Self {
|
||||
let sk = G::random_sk(rng);
|
||||
let pk = G::public_key(sk);
|
||||
Self {
|
||||
pk: PublicKey(pk),
|
||||
sk: PrivateKey(sk),
|
||||
}
|
||||
}
|
||||
|
||||
/// Generating a random key pair given a cryptographic rng
|
||||
pub(crate) fn generate_random<CS: voprf::CipherSuite, R: RngCore + CryptoRng>(
|
||||
rng: &mut R,
|
||||
) -> Self {
|
||||
let mut scalar_bytes = GenericArray::<_, <KG as KeGroup>::SkLen>::default();
|
||||
pub(crate) fn derive_random<R: RngCore + CryptoRng>(rng: &mut R) -> Self {
|
||||
let mut scalar_bytes = GenericArray::<_, <G as Group>::SkLen>::default();
|
||||
rng.fill_bytes(&mut scalar_bytes);
|
||||
let sk = KG::derive_auth_keypair::<CS>(scalar_bytes).unwrap();
|
||||
let pk = KG::public_key(sk);
|
||||
let sk = G::derive_scalar(scalar_bytes).unwrap();
|
||||
let pk = G::public_key(sk);
|
||||
Self {
|
||||
pk: PublicKey(pk),
|
||||
sk: PrivateKey(sk),
|
||||
@@ -67,16 +82,237 @@ impl<KG: KeGroup> KeyPair<KG> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
impl<KG: KeGroup> KeyPair<KG>
|
||||
/// Wrapper around a Key to enforce that it's a private one.
|
||||
#[derive_where(Clone, ZeroizeOnDrop)]
|
||||
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; G::Sk)]
|
||||
pub struct PrivateKey<G: Group>(G::Sk);
|
||||
|
||||
impl<G: Group> PrivateKey<G> {
|
||||
pub(crate) fn new(key: G::Sk) -> Self {
|
||||
Self(key)
|
||||
}
|
||||
|
||||
/// Returns public key from private key
|
||||
pub fn public_key(&self) -> PublicKey<G> {
|
||||
PublicKey(G::public_key(self.0))
|
||||
}
|
||||
|
||||
pub(crate) fn serialize(&self) -> GenericArray<u8, G::SkLen> {
|
||||
G::serialize_sk(self.0)
|
||||
}
|
||||
|
||||
/// Creates a [`PrivateKey`] from the given bytes.
|
||||
pub fn deserialize(mut input: &[u8]) -> Result<Self, ProtocolError> {
|
||||
Self::deserialize_take(&mut input)
|
||||
}
|
||||
|
||||
pub(crate) fn deserialize_take(input: &mut &[u8]) -> Result<Self, ProtocolError> {
|
||||
G::deserialize_take_sk(input).map(Self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<G: Group> PrivateKey<G>
|
||||
where
|
||||
KG::Pk: std::fmt::Debug,
|
||||
KG::Sk: std::fmt::Debug,
|
||||
G::Sk: DiffieHellman<G>,
|
||||
{
|
||||
/// Test-only strategy returning a proptest Strategy based on
|
||||
/// [`Self::generate_random`]
|
||||
fn uniform_keypair_strategy<CS: voprf::CipherSuite>() -> proptest::prelude::BoxedStrategy<Self>
|
||||
/// Diffie-Hellman key exchange implementation
|
||||
pub(crate) fn ke_diffie_hellman(&self, pk: &PublicKey<G>) -> GenericArray<u8, G::PkLen> {
|
||||
self.0.diffie_hellman(pk.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<G: Group> PrivateKey<G> {
|
||||
/// Private-key signing implementation
|
||||
pub(crate) fn sign<
|
||||
R: CryptoRng + RngCore,
|
||||
CS: CipherSuite,
|
||||
SIG: SignatureProtocol<Group = G>,
|
||||
KE: Group,
|
||||
>(
|
||||
&self,
|
||||
rng: &mut R,
|
||||
message: &Message<CS, KE>,
|
||||
) -> (SIG::Signature, SIG::VerifyState<CS, KE>) {
|
||||
SIG::sign(&self.0, rng, message)
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait to facilitate
|
||||
/// [`ServerSetup::de/serialize`](crate::ServerSetup::serialize).
|
||||
pub trait PrivateKeySerialization<G: Group>: Clone {
|
||||
/// Custom error type that can be passed down to `ProtocolError::Custom`
|
||||
type Error;
|
||||
/// Serialization size in bytes.
|
||||
type Len: ArrayLength<u8>;
|
||||
|
||||
/// Serialization into bytes
|
||||
fn serialize_key_pair(key_pair: &KeyPair<G, Self>) -> GenericArray<u8, Self::Len>;
|
||||
|
||||
/// Deserialization from bytes
|
||||
fn deserialize_take_key_pair(
|
||||
input: &mut &[u8],
|
||||
) -> Result<KeyPair<G, Self>, ProtocolError<Self::Error>>;
|
||||
}
|
||||
|
||||
impl<G: Group> PrivateKeySerialization<G> for PrivateKey<G> {
|
||||
type Error = core::convert::Infallible;
|
||||
type Len = G::SkLen;
|
||||
|
||||
fn serialize_key_pair(key_pair: &KeyPair<G, Self>) -> GenericArray<u8, Self::Len> {
|
||||
key_pair.private().serialize()
|
||||
}
|
||||
|
||||
fn deserialize_take_key_pair(input: &mut &[u8]) -> Result<KeyPair<G, Self>, ProtocolError> {
|
||||
let sk = PrivateKey::deserialize_take(input)?;
|
||||
let pk = sk.public_key();
|
||||
|
||||
Ok(KeyPair::new(sk, pk))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'de, G: Group> serde::Deserialize<'de> for PrivateKey<G> {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
use serde::de::Error;
|
||||
|
||||
G::deserialize_take_sk(
|
||||
&mut (GenericArray::<_, G::SkLen>::deserialize(deserializer)?.as_slice()),
|
||||
)
|
||||
.map(Self)
|
||||
.map_err(D::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<G: Group> serde::Serialize for PrivateKey<G> {
|
||||
fn serialize<SK>(&self, serializer: SK) -> Result<SK::Ok, SK::Error>
|
||||
where
|
||||
SK: serde::Serializer,
|
||||
{
|
||||
G::serialize_sk(self.0).serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper around a Key to enforce that it's a public one.
|
||||
#[derive_where(Clone, ZeroizeOnDrop)]
|
||||
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; G::Pk)]
|
||||
pub struct PublicKey<G: Group + ?Sized>(G::Pk);
|
||||
|
||||
impl<G: Group> PublicKey<G> {
|
||||
/// Convert from bytes
|
||||
pub fn deserialize(mut key_bytes: &[u8]) -> Result<Self, ProtocolError> {
|
||||
Self::deserialize_take(&mut key_bytes)
|
||||
}
|
||||
|
||||
pub(crate) fn deserialize_take(key_bytes: &mut &[u8]) -> Result<Self, ProtocolError> {
|
||||
G::deserialize_take_pk(key_bytes).map(Self)
|
||||
}
|
||||
|
||||
/// Convert to bytes
|
||||
pub fn serialize(&self) -> GenericArray<u8, G::PkLen> {
|
||||
G::serialize_pk(self.0)
|
||||
}
|
||||
|
||||
/// Returns the inner [`Group::Pk`].
|
||||
pub fn to_group_type(&self) -> G::Pk {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<G: Group> PublicKey<G> {
|
||||
/// Public-key verifying implementation
|
||||
pub(crate) fn verify<CS: CipherSuite, SIG: SignatureProtocol<Group = G>, KE: Group>(
|
||||
&self,
|
||||
message_builder: MessageBuilder<'_, CS>,
|
||||
state: SIG::VerifyState<CS, KE>,
|
||||
signature: &SIG::Signature,
|
||||
) -> Result<(), ProtocolError> {
|
||||
SIG::verify(&self.0, message_builder, state, signature)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'de, G: Group> serde::Deserialize<'de> for PublicKey<G> {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
use serde::de::Error;
|
||||
|
||||
G::deserialize_take_pk(
|
||||
&mut (GenericArray::<_, G::PkLen>::deserialize(deserializer)?.as_slice()),
|
||||
)
|
||||
.map(Self)
|
||||
.map_err(D::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<G: Group> serde::Serialize for PublicKey<G> {
|
||||
fn serialize<SK>(&self, serializer: SK) -> Result<SK::Ok, SK::Error>
|
||||
where
|
||||
SK: serde::Serializer,
|
||||
{
|
||||
G::serialize_pk(self.0).serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
/// Default OPRF seed container.
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
derive(serde::Deserialize, serde::Serialize),
|
||||
serde(bound = "")
|
||||
)]
|
||||
#[derive_where(Clone, Debug, Eq, Hash, PartialEq, ZeroizeOnDrop)]
|
||||
pub struct OprfSeed<H: OutputSizeUser>(pub(crate) Output<H>);
|
||||
|
||||
/// A trait to facilitate
|
||||
/// [`ServerSetup::de/serialize`](crate::ServerSetup::serialize).
|
||||
///
|
||||
/// Will be called with `E` being [`PrivateKeySerialization::Error`].
|
||||
pub trait OprfSeedSerialization<H, E>: Sized {
|
||||
/// Serialization size in bytes.
|
||||
type Len: ArrayLength<u8>;
|
||||
|
||||
/// Serialization into bytes
|
||||
fn serialize(&self) -> GenericArray<u8, Self::Len>;
|
||||
|
||||
/// Deserialization from bytes
|
||||
fn deserialize_take(input: &mut &[u8]) -> Result<Self, ProtocolError<E>>;
|
||||
}
|
||||
|
||||
impl<H: OutputSizeUser, E> OprfSeedSerialization<H, E> for OprfSeed<H> {
|
||||
type Len = H::OutputSize;
|
||||
|
||||
fn serialize(&self) -> GenericArray<u8, Self::Len> {
|
||||
self.0.clone()
|
||||
}
|
||||
|
||||
fn deserialize_take(input: &mut &[u8]) -> Result<Self, ProtocolError<E>> {
|
||||
Ok(Self(
|
||||
input
|
||||
.take_array("OPRF seed")
|
||||
.map_err(ProtocolError::into_custom)?,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
// Test Implementations //
|
||||
//===================== //
|
||||
//////////////////////////
|
||||
|
||||
#[cfg(test)]
|
||||
use crate::serialization::AssertZeroized;
|
||||
|
||||
#[cfg(test)]
|
||||
impl<G: Group> KeyPair<G> {
|
||||
/// Test-only strategy returning a proptest Strategy based on
|
||||
/// [`Self::derive_random`]
|
||||
fn uniform_keypair_strategy() -> proptest::prelude::BoxedStrategy<Self> {
|
||||
use proptest::prelude::*;
|
||||
use rand::rngs::StdRng;
|
||||
use rand::SeedableRng;
|
||||
@@ -86,158 +322,61 @@ where
|
||||
any::<[u8; 32]>()
|
||||
.prop_filter_map("valid random keypair", |seed| {
|
||||
let mut rng = StdRng::from_seed(seed);
|
||||
Some(Self::generate_random::<CS, _>(&mut rng))
|
||||
Some(Self::derive_random(&mut rng))
|
||||
})
|
||||
.no_shrink()
|
||||
.boxed()
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper around a Key to enforce that it's a private one.
|
||||
#[derive_where(Clone, ZeroizeOnDrop)]
|
||||
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; KG::Sk)]
|
||||
pub struct PrivateKey<KG: KeGroup>(KG::Sk);
|
||||
|
||||
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>
|
||||
#[cfg(test)]
|
||||
impl<G: Group> AssertZeroized for PublicKey<G>
|
||||
where
|
||||
KG::Sk: DiffieHellman<KG>,
|
||||
G::Pk: AssertZeroized,
|
||||
{
|
||||
/// 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)
|
||||
fn assert_zeroized(&self) {
|
||||
self.0.assert_zeroized();
|
||||
}
|
||||
}
|
||||
|
||||
/// 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>;
|
||||
|
||||
/// Serialization into bytes
|
||||
fn serialize_key_pair(key_pair: &KeyPair<KG, Self>) -> GenericArray<u8, Self::Len>;
|
||||
|
||||
/// Deserialization from bytes
|
||||
fn deserialize_key_pair(input: &[u8]) -> Result<KeyPair<KG, Self>, ProtocolError<Self::Error>>;
|
||||
}
|
||||
|
||||
impl<KG: KeGroup> PrivateKeySerialization<KG> for PrivateKey<KG> {
|
||||
type Error = core::convert::Infallible;
|
||||
type Len = KG::SkLen;
|
||||
|
||||
fn serialize_key_pair(key_pair: &KeyPair<KG, Self>) -> GenericArray<u8, Self::Len> {
|
||||
key_pair.private().serialize()
|
||||
}
|
||||
|
||||
fn deserialize_key_pair(input: &[u8]) -> Result<KeyPair<KG, Self>, ProtocolError> {
|
||||
let sk = PrivateKey::deserialize(input)?;
|
||||
let pk = sk.public_key();
|
||||
|
||||
Ok(KeyPair::new(sk, pk))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'de, KG: KeGroup> serde::Deserialize<'de> for PrivateKey<KG> {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
use serde::de::Error;
|
||||
|
||||
KG::deserialize_sk(&GenericArray::<_, KG::SkLen>::deserialize(deserializer)?)
|
||||
.map(Self)
|
||||
.map_err(D::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<KG: KeGroup> serde::Serialize for PrivateKey<KG> {
|
||||
fn serialize<SK>(&self, serializer: SK) -> Result<SK::Ok, SK::Error>
|
||||
where
|
||||
SK: serde::Serializer,
|
||||
{
|
||||
KG::serialize_sk(self.0).serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper around a Key to enforce that it's a public one.
|
||||
#[derive_where(Clone, ZeroizeOnDrop)]
|
||||
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; KG::Pk)]
|
||||
pub struct PublicKey<KG: KeGroup>(KG::Pk);
|
||||
|
||||
impl<KG: KeGroup> PublicKey<KG> {
|
||||
/// Convert from bytes
|
||||
pub fn deserialize(key_bytes: &[u8]) -> Result<Self, ProtocolError> {
|
||||
KG::deserialize_pk(key_bytes).map(Self)
|
||||
}
|
||||
|
||||
/// Convert to bytes
|
||||
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")]
|
||||
impl<'de, KG: KeGroup> serde::Deserialize<'de> for PublicKey<KG> {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
use serde::de::Error;
|
||||
|
||||
KG::deserialize_pk(&GenericArray::<_, KG::PkLen>::deserialize(deserializer)?)
|
||||
.map(Self)
|
||||
.map_err(D::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<KG: KeGroup> serde::Serialize for PublicKey<KG> {
|
||||
fn serialize<SK>(&self, serializer: SK) -> Result<SK::Ok, SK::Error>
|
||||
where
|
||||
SK: serde::Serializer,
|
||||
{
|
||||
KG::serialize_pk(self.0).serialize(serializer)
|
||||
#[cfg(test)]
|
||||
impl<G: Group> AssertZeroized for PrivateKey<G>
|
||||
where
|
||||
G::Sk: AssertZeroized,
|
||||
{
|
||||
fn assert_zeroized(&self) {
|
||||
self.0.assert_zeroized();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use core::ptr;
|
||||
|
||||
use hkdf::Hkdf;
|
||||
use rand::rngs::OsRng;
|
||||
|
||||
use super::*;
|
||||
use crate::util;
|
||||
use crate::ciphersuite::{KeGroup, OprfHash};
|
||||
use crate::serialization::AssertZeroized;
|
||||
use crate::{
|
||||
CipherSuite, ClientLogin, ClientLoginFinishParameters, ClientLoginFinishResult,
|
||||
ClientLoginStartResult, ClientRegistration, ClientRegistrationFinishParameters,
|
||||
ClientRegistrationFinishResult, ClientRegistrationStartResult, ServerLogin,
|
||||
ServerLoginParameters, ServerLoginStartResult, ServerRegistration,
|
||||
ServerRegistrationStartResult, ServerSetup,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_zeroize_key() {
|
||||
fn inner<G: KeGroup>() {
|
||||
fn inner<G: Group>()
|
||||
where
|
||||
G::Sk: AssertZeroized,
|
||||
{
|
||||
let mut rng = OsRng;
|
||||
let mut key = PrivateKey::<G>(G::random_sk(&mut rng));
|
||||
util::test_zeroize_on_drop(&mut key);
|
||||
unsafe { ptr::drop_in_place(&mut key) };
|
||||
key.0.assert_zeroized();
|
||||
}
|
||||
|
||||
#[cfg(feature = "ristretto255")]
|
||||
@@ -245,6 +384,10 @@ mod tests {
|
||||
inner::<::p256::NistP256>();
|
||||
inner::<::p384::NistP384>();
|
||||
inner::<::p521::NistP521>();
|
||||
#[cfg(feature = "curve25519")]
|
||||
inner::<crate::Curve25519>();
|
||||
#[cfg(feature = "ed25519")]
|
||||
inner::<crate::Ed25519>();
|
||||
}
|
||||
|
||||
macro_rules! test {
|
||||
@@ -258,15 +401,15 @@ mod tests {
|
||||
|
||||
proptest! {
|
||||
#[test]
|
||||
fn pub_from_priv(kp in KeyPair::<$point>::uniform_keypair_strategy::<$point>()) {
|
||||
fn pub_from_priv(kp in KeyPair::<$point>::uniform_keypair_strategy()) {
|
||||
let pk = kp.public();
|
||||
let sk = kp.private();
|
||||
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>()) {
|
||||
fn dh(kp1 in KeyPair::<$point>::uniform_keypair_strategy(),
|
||||
kp2 in KeyPair::<$point>::uniform_keypair_strategy()) {
|
||||
|
||||
let dh1 = kp2.private().ke_diffie_hellman(&kp1.public());
|
||||
let dh2 = kp1.private().ke_diffie_hellman(kp2.public());
|
||||
@@ -275,10 +418,10 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn private_key_slice(kp in KeyPair::<$point>::uniform_keypair_strategy::<$point>()) {
|
||||
fn private_key_slice(kp in KeyPair::<$point>::uniform_keypair_strategy()) {
|
||||
let sk_bytes = kp.private().serialize().to_vec();
|
||||
|
||||
let kp2 = PrivateKey::<$point>::deserialize_key_pair(&sk_bytes)?;
|
||||
let kp2 = PrivateKey::<$point>::deserialize_take_key_pair(&mut (sk_bytes.as_slice()))?;
|
||||
let kp2_private_bytes = kp2.private().serialize().to_vec();
|
||||
|
||||
prop_assert_eq!(sk_bytes, kp2_private_bytes);
|
||||
@@ -294,41 +437,31 @@ mod tests {
|
||||
test!(p384, ::p384::NistP384);
|
||||
test!(p521, ::p521::NistP521);
|
||||
|
||||
struct Default;
|
||||
|
||||
impl CipherSuite for Default {
|
||||
#[cfg(feature = "ristretto255")]
|
||||
type OprfCs = crate::Ristretto255;
|
||||
#[cfg(not(feature = "ristretto255"))]
|
||||
type OprfCs = ::p256::NistP256;
|
||||
#[cfg(feature = "ristretto255")]
|
||||
type KeyExchange = crate::TripleDh<crate::Ristretto255, sha2::Sha512>;
|
||||
#[cfg(not(feature = "ristretto255"))]
|
||||
type KeyExchange = crate::TripleDh<::p256::NistP256, sha2::Sha256>;
|
||||
type Ksf = crate::ksf::Identity;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct RemoteSeed<H: OutputSizeUser>(Output<H>);
|
||||
|
||||
#[derive(Clone)]
|
||||
struct RemoteKey(PrivateKey<KeGroup<Default>>);
|
||||
|
||||
const PASSWORD: &str = "password";
|
||||
|
||||
#[test]
|
||||
fn remote_key() {
|
||||
use rand::rngs::OsRng;
|
||||
|
||||
use crate::{
|
||||
CipherSuite, ClientLogin, ClientLoginFinishParameters, ClientLoginFinishResult,
|
||||
ClientLoginStartResult, ClientRegistration, ClientRegistrationFinishParameters,
|
||||
ClientRegistrationFinishResult, ClientRegistrationStartResult, ServerLogin,
|
||||
ServerLoginStartParameters, ServerLoginStartResult, ServerRegistration,
|
||||
ServerRegistrationStartResult, ServerSetup,
|
||||
};
|
||||
|
||||
struct Default;
|
||||
|
||||
impl CipherSuite for Default {
|
||||
#[cfg(feature = "ristretto255")]
|
||||
type OprfCs = crate::Ristretto255;
|
||||
#[cfg(not(feature = "ristretto255"))]
|
||||
type OprfCs = ::p256::NistP256;
|
||||
#[cfg(feature = "ristretto255")]
|
||||
type KeGroup = crate::Ristretto255;
|
||||
#[cfg(not(feature = "ristretto255"))]
|
||||
type KeGroup = ::p256::NistP256;
|
||||
type KeyExchange = crate::key_exchange::tripledh::TripleDh;
|
||||
type Ksf = crate::ksf::Identity;
|
||||
}
|
||||
|
||||
type KeCurve = <Default as CipherSuite>::KeGroup;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct RemoteKey(PrivateKey<KeCurve>);
|
||||
|
||||
const PASSWORD: &str = "password";
|
||||
|
||||
let sk = PrivateKey(KeCurve::random_sk(&mut OsRng));
|
||||
let sk = PrivateKey(KeGroup::<Default>::random_sk(&mut OsRng));
|
||||
let pk = sk.public_key();
|
||||
let sk = RemoteKey(sk);
|
||||
let keypair = KeyPair::new(sk, pk);
|
||||
@@ -362,7 +495,7 @@ mod tests {
|
||||
Some(file),
|
||||
message,
|
||||
&[],
|
||||
ServerLoginStartParameters::default(),
|
||||
ServerLoginParameters::default(),
|
||||
)
|
||||
.unwrap();
|
||||
let shared_secret = builder.private_key().0.ke_diffie_hellman(builder.data());
|
||||
@@ -373,11 +506,88 @@ mod tests {
|
||||
} = builder.build(shared_secret).unwrap();
|
||||
let ClientLoginFinishResult { message, .. } = client
|
||||
.finish(
|
||||
&mut OsRng,
|
||||
PASSWORD.as_bytes(),
|
||||
message,
|
||||
ClientLoginFinishParameters::default(),
|
||||
)
|
||||
.unwrap();
|
||||
server.finish(message).unwrap();
|
||||
server
|
||||
.finish(message, ServerLoginParameters::default())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remote_seed() {
|
||||
let mut oprf_seed = RemoteSeed::<OprfHash<Default>>(GenericArray::default());
|
||||
OsRng.fill_bytes(&mut oprf_seed.0);
|
||||
|
||||
let sk = PrivateKey(KeGroup::<Default>::random_sk(&mut OsRng));
|
||||
let pk = sk.public_key();
|
||||
let sk = RemoteKey(sk);
|
||||
let keypair = KeyPair::new(sk, pk);
|
||||
|
||||
let server_setup = ServerSetup::<Default, _, _>::new_with_key_pair_and_seed(
|
||||
&mut OsRng, keypair, oprf_seed,
|
||||
);
|
||||
|
||||
let ClientRegistrationStartResult {
|
||||
message,
|
||||
state: client,
|
||||
} = ClientRegistration::<Default>::start(&mut OsRng, PASSWORD.as_bytes()).unwrap();
|
||||
let km = server_setup.key_material_info(&[]);
|
||||
let mut ikm = GenericArray::default();
|
||||
Hkdf::<OprfHash<Default>>::from_prk(&km.ikm.0)
|
||||
.unwrap()
|
||||
.expand_multi_info(&km.info, &mut ikm)
|
||||
.unwrap();
|
||||
let ServerRegistrationStartResult { message, .. } =
|
||||
ServerRegistration::start_with_key_material(&server_setup, ikm, message).unwrap();
|
||||
let ClientRegistrationFinishResult { message, .. } = client
|
||||
.finish(
|
||||
&mut OsRng,
|
||||
PASSWORD.as_bytes(),
|
||||
message,
|
||||
ClientRegistrationFinishParameters::default(),
|
||||
)
|
||||
.unwrap();
|
||||
let file = ServerRegistration::finish(message);
|
||||
|
||||
let ClientLoginStartResult {
|
||||
message,
|
||||
state: client,
|
||||
} = ClientLogin::<Default>::start(&mut OsRng, PASSWORD.as_bytes()).unwrap();
|
||||
let km = server_setup.key_material_info(&[]);
|
||||
let mut ikm = GenericArray::default();
|
||||
Hkdf::<OprfHash<Default>>::from_prk(&km.ikm.0)
|
||||
.unwrap()
|
||||
.expand_multi_info(&km.info, &mut ikm)
|
||||
.unwrap();
|
||||
let builder = ServerLogin::builder_with_key_material(
|
||||
&mut OsRng,
|
||||
&server_setup,
|
||||
ikm,
|
||||
Some(file),
|
||||
message,
|
||||
ServerLoginParameters::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(
|
||||
&mut OsRng,
|
||||
PASSWORD.as_bytes(),
|
||||
message,
|
||||
ClientLoginFinishParameters::default(),
|
||||
)
|
||||
.unwrap();
|
||||
server
|
||||
.finish(message, ServerLoginParameters::default())
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user