Skip to content

Commit

Permalink
Basic traits for keypairs (#42)
Browse files Browse the repository at this point in the history
Made PublicKey impl Debug+Eq; made PrivateKey impl Eq
  • Loading branch information
rozbb authored Mar 9, 2023
1 parent 0316589 commit 1232a8f
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 55 deletions.
36 changes: 10 additions & 26 deletions src/dhkex/ecdh_nistp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,24 @@ use generic_array::{
GenericArray,
};
use p256::elliptic_curve::{ecdh::diffie_hellman, sec1::ToEncodedPoint};
use subtle::{Choice, ConstantTimeEq};

/// An ECDH-P256 public key. This is never the point at infinity.
#[derive(Clone)]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct PublicKey(p256::PublicKey);

// This is only ever constructed via its Deserializable::from_bytes, which checks for the 0 value.
// Also, the underlying type is zeroize-on-drop.
/// An ECDH-P256 private key. This is a scalar in the range `[1,p)` where `p` is the group order.
#[derive(Clone)]
#[derive(Clone, Eq, PartialEq)]
pub struct PrivateKey(p256::SecretKey);

impl ConstantTimeEq for PrivateKey {
fn ct_eq(&self, other: &Self) -> Choice {
self.0.ct_eq(&other.0)
}
}

// The underlying type is zeroize-on-drop
/// A bare DH computation result
pub struct KexResult(p256::ecdh::SharedSecret);
Expand Down Expand Up @@ -182,36 +189,13 @@ impl DhKeyExchange for DhP256 {
#[cfg(test)]
mod tests {
use crate::{
dhkex::{
ecdh_nistp::{DhP256, PrivateKey, PublicKey},
DhKeyExchange,
},
dhkex::{ecdh_nistp::DhP256, DhKeyExchange},
test_util::dhkex_gen_keypair,
Deserializable, Serializable,
};

use rand::{rngs::StdRng, SeedableRng};

// We need this in our serialize-deserialize tests
impl PartialEq for PrivateKey {
fn eq(&self, other: &PrivateKey) -> bool {
self.to_bytes() == other.to_bytes()
}
}

// We need this in our serialize-deserialize tests
impl PartialEq for PublicKey {
fn eq(&self, other: &PublicKey) -> bool {
self.0 == other.0
}
}

impl core::fmt::Debug for PublicKey {
fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
write!(f, "PublicKey({:?})", self.0)
}
}

// Test vector comes from RFC 5903 §8.1
// https://tools.ietf.org/html/rfc5903
/// Tests the ECDH op against a known answer
Expand Down
44 changes: 17 additions & 27 deletions src/dhkex/x25519.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,33 @@ use generic_array::{
typenum::{self, Unsigned},
GenericArray,
};
use subtle::ConstantTimeEq;
use subtle::{Choice, ConstantTimeEq};

// We wrap the types in order to abstract away the dalek dep

/// An X25519 public key
#[derive(Clone)]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct PublicKey(x25519_dalek::PublicKey);

// The underlying type is zeroize-on-drop
/// An X25519 private key
#[derive(Clone)]
pub struct PrivateKey(x25519_dalek::StaticSecret);

impl ConstantTimeEq for PrivateKey {
fn ct_eq(&self, other: &Self) -> Choice {
// We can use to_bytes because StaticSecret is only ever constructed from a clamped scalar
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 {}

// The underlying type is zeroize-on-drop
/// A bare DH computation result
pub struct KexResult(x25519_dalek::SharedSecret);
Expand Down Expand Up @@ -155,36 +169,12 @@ impl DhKeyExchange for X25519 {
#[cfg(test)]
mod tests {
use crate::{
dhkex::{
x25519::{PrivateKey, PublicKey, X25519},
Deserializable, DhKeyExchange, Serializable,
},
dhkex::{x25519::X25519, Deserializable, DhKeyExchange, Serializable},
test_util::dhkex_gen_keypair,
};
use generic_array::typenum::Unsigned;
use rand::{rngs::StdRng, RngCore, SeedableRng};

// We need this in our serialize-deserialize tests
impl PartialEq for PrivateKey {
fn eq(&self, other: &PrivateKey) -> bool {
self.0.to_bytes() == other.0.to_bytes()
}
}

// We need this in our serialize-deserialize tests
impl PartialEq for PublicKey {
fn eq(&self, other: &PublicKey) -> bool {
self.0.as_bytes() == other.0.as_bytes()
}
}

// For KEM tests
impl core::fmt::Debug for PublicKey {
fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
write!(f, "PublicKey({:?})", self.0)
}
}

/// Tests that an serialize-deserialize round-trip ends up at the same pubkey
#[test]
fn test_pubkey_serialize_correctness() {
Expand Down
11 changes: 9 additions & 2 deletions src/kem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
use crate::{Deserializable, HpkeError, Serializable};

use core::fmt::Debug;

use generic_array::{ArrayLength, GenericArray};
use rand_core::{CryptoRng, RngCore};
use zeroize::Zeroize;
Expand All @@ -18,19 +20,24 @@ pub trait Kem: Sized {
/// `Kem::gen_keypair` or `Kem::derive_keypair`
#[cfg(feature = "serde_impls")]
type PublicKey: Clone
+ Debug
+ PartialEq
+ Eq
+ Serializable
+ Deserializable
+ SerdeSerialize
+ for<'a> SerdeDeserialize<'a>;
/// The key exchange's public key type. If you want to generate a keypair, see
/// `Kem::gen_keypair` or `Kem::derive_keypair`
#[cfg(not(feature = "serde_impls"))]
type PublicKey: Clone + Serializable + Deserializable;
type PublicKey: Clone + Debug + PartialEq + Eq + Serializable + Deserializable;

/// The key exchange's private key type. If you want to generate a keypair, see
/// `Kem::gen_keypair` or `Kem::derive_keypair`
#[cfg(feature = "serde_impls")]
type PrivateKey: Clone
+ PartialEq
+ Eq
+ Serializable
+ Deserializable
+ SerdeSerialize
Expand All @@ -39,7 +46,7 @@ pub trait Kem: Sized {
/// The key exchange's private key type. If you want to generate a keypair, see
/// `Kem::gen_keypair` or `Kem::derive_keypair`
#[cfg(not(feature = "serde_impls"))]
type PrivateKey: Clone + Serializable + Deserializable;
type PrivateKey: Clone + PartialEq + Eq + Serializable + Deserializable;

/// The encapsulated key for this KEM. This is used by the recipient to derive the shared
/// secret.
Expand Down

0 comments on commit 1232a8f

Please sign in to comment.