Group revamp (#261)

* Revamp `KeGroup` trait

* Update dependencies

* Fix `hash_to_scalar` using `OprfGroup` instead of `KeGroup`

* Relax constraints on associated types of `KeGroup`

* Improve `KeGroup` implementation on `Curve`

* Improve `KeyExchange` trait

* Fix new Clippy 1.59 warnings
This commit is contained in:
daxpedda
2022-02-24 22:13:22 -08:00
committed by GitHub
parent 47a26a19c5
commit b2f10858e0
28 changed files with 2133 additions and 1574 deletions
+74 -162
View File
@@ -9,16 +9,14 @@
#![allow(unsafe_code)]
use core::ops::Deref;
use derive_where::DeriveWhere;
use generic_array::typenum::Unsigned;
use derive_where::derive_where;
use generic_array::{ArrayLength, GenericArray};
use rand::{CryptoRng, RngCore};
use zeroize::Zeroize;
use zeroize::ZeroizeOnDrop;
use crate::errors::{InternalError, ProtocolError};
use crate::key_exchange::group::KeGroup;
use crate::serialization::GenericArrayExt;
/// A Keypair trait with public-private verification
#[cfg_attr(
@@ -26,15 +24,14 @@ use crate::key_exchange::group::KeGroup;
derive(serde_::Deserialize, serde_::Serialize),
serde(
bound(
deserialize = "S: serde_::Deserialize<'de>",
serialize = "S: serde_::Serialize"
deserialize = "KG::Pk: serde_::Deserialize<'de>, S: serde_::Deserialize<'de>",
serialize = "KG::Pk: serde_::Serialize, S: serde_::Serialize"
),
crate = "serde_"
)
)]
#[derive(DeriveWhere)]
#[derive_where(Clone, Zeroize(drop))]
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; S)]
#[derive_where(Clone)]
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; KG::Pk, S)]
pub struct KeyPair<KG: KeGroup, S: SecretKey<KG> = PrivateKey<KG>> {
pk: PublicKey<KG>,
sk: S,
@@ -51,14 +48,6 @@ impl<KG: KeGroup, S: SecretKey<KG>> KeyPair<KG, S> {
&self.sk
}
/// Check whether a public key is valid. This is meant to be applied on
/// material provided through the network which fits the key representation
/// (i.e. can be mapped to a curve point), but presents some risk - e.g.
/// small subgroup check
pub(crate) fn check_public_key(key: PublicKey<KG>) -> Result<PublicKey<KG>, InternalError> {
KG::from_pk_slice(GenericArray::from_slice(&key.0)).map(|_| key)
}
/// 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)?)
@@ -77,14 +66,18 @@ impl<KG: KeGroup> KeyPair<KG> {
let sk = KG::random_sk(rng);
let pk = KG::public_key(&sk);
Self {
pk: PublicKey(Key(pk.to_arr())),
sk: PrivateKey(Key(sk)),
pk: PublicKey(pk),
sk: PrivateKey(sk),
}
}
}
#[cfg(test)]
impl<KG: KeGroup> KeyPair<KG> {
impl<KG: KeGroup> KeyPair<KG>
where
KG::Pk: std::fmt::Debug,
KG::Sk: std::fmt::Debug,
{
/// Test-only strategy returning a proptest Strategy based on
/// generate_random
fn uniform_keypair_strategy() -> proptest::prelude::BoxedStrategy<Self> {
@@ -104,70 +97,39 @@ impl<KG: KeGroup> KeyPair<KG> {
}
}
/// A minimalist key type built around a \[u8; 32\]
#[cfg_attr(
feature = "serde",
derive(serde_::Deserialize, serde_::Serialize),
serde(bound = "", crate = "serde_")
)]
#[derive(DeriveWhere)]
#[derive_where(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Zeroize(drop))]
pub struct Key<L: ArrayLength<u8>>(GenericArray<u8, L>);
impl<L: ArrayLength<u8>> Deref for Key<L> {
type Target = GenericArray<u8, L>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
// Don't make it implement SizedBytes so that it's not constructible outside of
// this module.
impl<L: ArrayLength<u8>> Key<L> {
/// Convert to bytes
pub fn to_arr(&self) -> GenericArray<u8, L> {
self.0.clone()
}
}
/// Wrapper around a Key to enforce that it's a private one.
#[cfg_attr(
feature = "serde",
derive(serde_::Deserialize, serde_::Serialize),
serde(bound = "", crate = "serde_")
serde(
bound(
deserialize = "KG::Sk: serde_::Deserialize<'de>",
serialize = "KG::Sk: serde_::Serialize"
),
crate = "serde_"
)
)]
#[derive(DeriveWhere)]
#[derive_where(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Zeroize(drop))]
pub struct PrivateKey<KG: KeGroup>(Key<KG::SkLen>);
#[derive_where(Clone)]
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; KG::Sk)]
pub struct PrivateKey<KG: KeGroup>(KG::Sk);
// This can't be derived because of the use of a generic parameter
impl<KG: KeGroup> Deref for PrivateKey<KG> {
type Target = Key<KG::SkLen>;
fn deref(&self) -> &Self::Target {
&self.0
impl<KG: KeGroup> Drop for PrivateKey<KG> {
fn drop(&mut self) {
KG::zeroize_sk_on_drop(&mut self.0)
}
}
impl<KG: KeGroup> ZeroizeOnDrop for PrivateKey<KG> {}
impl<KG: KeGroup> PrivateKey<KG> {
/// Convert from bytes
pub fn from_arr(key_bytes: GenericArray<u8, KG::SkLen>) -> Self {
PrivateKey(Key(key_bytes))
}
/// Convert from slice
pub fn from_bytes(key_bytes: &[u8]) -> Result<Self, InternalError> {
if key_bytes.len() == KG::SkLen::USIZE {
Ok(Self::from_arr(GenericArray::from_slice(key_bytes).clone()))
} else {
Err(InternalError::InvalidByteSequence)
}
pub fn from_bytes(key_bytes: &GenericArray<u8, KG::SkLen>) -> Result<Self, InternalError> {
KG::deserialize_sk(key_bytes).map(Self)
}
}
/// A trait specifying the requirements for a private key container
pub trait SecretKey<KG: KeGroup>: Clone + Sized + Zeroize {
pub trait SecretKey<KG: KeGroup>: Clone + Sized {
/// Custom error type that can be passed down to `InternalError::Custom`
type Error;
/// Serialization size in bytes.
@@ -197,20 +159,19 @@ impl<KG: KeGroup> SecretKey<KG> for PrivateKey<KG> {
&self,
pk: PublicKey<KG>,
) -> Result<GenericArray<u8, KG::PkLen>, InternalError> {
let pk = KG::from_pk_slice(&pk)?;
Ok(pk.diffie_hellman(self))
Ok(KG::diffie_hellman(&pk.0, &self.0))
}
fn public_key(&self) -> Result<PublicKey<KG>, InternalError> {
Ok(PublicKey(Key(KG::public_key(&self.0).to_arr())))
Ok(PublicKey(KG::public_key(&self.0)))
}
fn serialize(&self) -> GenericArray<u8, Self::Len> {
self.to_arr()
KG::serialize_sk(&self.0)
}
fn deserialize(input: &[u8]) -> Result<Self, InternalError> {
PrivateKey::from_bytes(input).map_err(InternalError::from)
GenericArray::try_from_slice(input).and_then(Self::from_bytes)
}
}
@@ -218,93 +179,54 @@ impl<KG: KeGroup> SecretKey<KG> for PrivateKey<KG> {
#[cfg_attr(
feature = "serde",
derive(serde_::Deserialize, serde_::Serialize),
serde(bound = "", crate = "serde_")
serde(
bound(
deserialize = "KG::Pk: serde_::Deserialize<'de>",
serialize = "KG::Pk: serde_::Serialize"
),
crate = "serde_"
)
)]
#[derive(DeriveWhere)]
#[derive_where(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Zeroize(drop))]
pub struct PublicKey<KG: KeGroup>(Key<KG::PkLen>);
impl<KG: KeGroup> Deref for PublicKey<KG> {
type Target = Key<KG::PkLen>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive_where(Clone)]
#[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 from_arr(key_bytes: GenericArray<u8, KG::PkLen>) -> Self {
Self(Key(key_bytes))
pub fn from_bytes(key_bytes: &GenericArray<u8, KG::PkLen>) -> Result<Self, InternalError> {
KG::deserialize_pk(key_bytes).map(Self)
}
/// Convert to bytes
pub fn to_bytes(&self) -> GenericArray<u8, KG::PkLen> {
KG::serialize_pk(&self.0)
}
/// Convert from slice
pub fn from_bytes(key_bytes: &[u8]) -> Result<Self, InternalError> {
if key_bytes.len() == KG::PkLen::USIZE {
Ok(Self::from_arr(GenericArray::from_slice(key_bytes).clone()))
} else {
Err(InternalError::InvalidByteSequence)
}
pub fn deserialize(input: &[u8]) -> Result<Self, InternalError> {
GenericArray::try_from_slice(input).and_then(Self::from_bytes)
}
}
#[cfg(test)]
mod tests {
use core::slice::from_raw_parts;
use std::vec;
use generic_array::typenum::Unsigned;
use rand::rngs::OsRng;
use super::*;
use crate::errors::*;
use crate::util;
#[test]
fn test_zeroize_key() -> Result<(), ProtocolError> {
fn inner<G: KeGroup>() -> Result<(), ProtocolError> {
let key_len = G::PkLen::USIZE;
let mut key = Key::<G::PkLen>(GenericArray::clone_from_slice(&vec![1u8; key_len]));
let ptr = key.as_ptr();
Zeroize::zeroize(&mut key);
let bytes = unsafe { from_raw_parts(ptr, key_len) };
assert!(bytes.iter().all(|&x| x == 0));
Ok(())
}
#[cfg(feature = "ristretto255")]
inner::<curve25519_dalek::ristretto::RistrettoPoint>()?;
#[cfg(feature = "p256")]
inner::<p256_::PublicKey>()?;
Ok(())
}
#[test]
fn test_zeroize_keypair() {
fn test_zeroize_key() {
fn inner<G: KeGroup>() {
let mut rng = OsRng;
let mut keypair = KeyPair::<G>::generate_random(&mut rng);
let pk_ptr = keypair.pk.as_ptr();
let sk_ptr = keypair.sk.as_ptr();
let pk_len = G::PkLen::USIZE;
let sk_len = G::SkLen::USIZE;
Zeroize::zeroize(&mut keypair);
let pk_bytes = unsafe { from_raw_parts(pk_ptr, pk_len) };
let sk_bytes = unsafe { from_raw_parts(sk_ptr, sk_len) };
assert!(pk_bytes.iter().all(|&x| x == 0));
assert!(sk_bytes.iter().all(|&x| x == 0));
let mut key = PrivateKey::<G>(G::random_sk(&mut rng));
util::test_zeroize_on_drop(&mut key);
}
#[cfg(feature = "ristretto255")]
inner::<curve25519_dalek::ristretto::RistrettoPoint>();
#[cfg(feature = "p256")]
inner::<p256_::PublicKey>();
inner::<crate::Ristretto255>();
inner::<::p256::NistP256>();
}
macro_rules! test {
@@ -317,12 +239,6 @@ mod tests {
use super::*;
proptest! {
#[test]
fn check(kp in KeyPair::<$point>::uniform_keypair_strategy()) {
let pk = kp.public();
prop_assert!(KeyPair::<$point>::check_public_key(pk.clone()).is_ok());
}
#[test]
fn pub_from_priv(kp in KeyPair::<$point>::uniform_keypair_strategy()) {
let pk = kp.public();
@@ -342,10 +258,10 @@ mod tests {
#[test]
fn private_key_slice(kp in KeyPair::<$point>::uniform_keypair_strategy()) {
let sk_bytes = kp.private().to_vec();
let sk_bytes = kp.private().serialize().to_vec();
let kp2 = KeyPair::<$point>::from_private_key_slice(&sk_bytes)?;
let kp2_private_bytes = kp2.private().to_vec();
let kp2_private_bytes = kp2.private().serialize().to_vec();
prop_assert_eq!(sk_bytes, kp2_private_bytes);
}
@@ -355,16 +271,11 @@ mod tests {
}
#[cfg(feature = "ristretto255")]
test!(ristretto, curve25519_dalek::ristretto::RistrettoPoint);
#[cfg(feature = "p256")]
test!(p256, p256_::PublicKey);
test!(ristretto, crate::Ristretto255);
test!(p256, ::p256::NistP256);
#[test]
fn remote_key() {
#[cfg(feature = "ristretto255")]
use curve25519_dalek::ristretto::RistrettoPoint as KeCurve;
#[cfg(not(feature = "ristretto255"))]
use p256_::PublicKey as KeCurve;
use rand::rngs::OsRng;
use crate::{
@@ -379,19 +290,20 @@ mod tests {
impl CipherSuite for Default {
#[cfg(feature = "ristretto255")]
type OprfGroup = KeCurve;
type OprfGroup = crate::Ristretto255;
#[cfg(not(feature = "ristretto255"))]
type OprfGroup = p256_::ProjectivePoint;
type KeGroup = KeCurve;
type KeyExchange = crate::key_exchange::tripledh::TripleDH;
type OprfGroup = ::p256::NistP256;
#[cfg(feature = "ristretto255")]
type Hash = sha2::Sha512;
type KeGroup = crate::Ristretto255;
#[cfg(not(feature = "ristretto255"))]
type Hash = sha2::Sha256;
type KeGroup = ::p256::NistP256;
type KeyExchange = crate::key_exchange::tripledh::TripleDH;
type SlowHash = crate::slow_hash::NoOpHash;
}
#[derive(Clone, Zeroize)]
type KeCurve = <Default as CipherSuite>::KeGroup;
#[derive(Clone)]
struct RemoteKey(PrivateKey<KeCurve>);
impl SecretKey<KeCurve> for RemoteKey {
@@ -422,7 +334,7 @@ mod tests {
const PASSWORD: &str = "password";
let sk = KeCurve::random_sk(&mut OsRng);
let sk = RemoteKey(PrivateKey(Key(sk)));
let sk = RemoteKey(PrivateKey(sk));
let keypair = KeyPair::from_private_key(sk).unwrap();
let server_setup = ServerSetup::<Default, RemoteKey>::new_with_key(&mut OsRng, keypair);