diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c820d2..2ff524f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ Entries are listed in reverse chronological order. +# 4.0.4 (Unreleased) + +* Implement `PartialEq` and `Eq` in `SigningKey` and `VerificationKey`. + # 4.0.3 * Update `curve25519-dalek` to `4.1.0` diff --git a/Cargo.toml b/Cargo.toml index e5cc0f3..9513ccd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,7 @@ pkcs8 = { version = "0.10.1", optional = true, features = ["alloc", "pem"] } rand_core = "0.6" serde = { version = "1", default-features = false, optional = true, features = ["derive"] } sha2 = { version = "0.10", default-features = false } +subtle = { version = "2.5.0", default-features = false } zeroize = { version = "1.5", features = [ "zeroize_derive" ] } [dev-dependencies] @@ -43,7 +44,7 @@ default = ["serde", "std"] pem = ["der", "ed25519/pem"] pkcs8 = ["dep:pkcs8"] serde = ["dep:serde", "ed25519/serde"] -std = ["ed25519/std"] +std = ["ed25519/std", "subtle/std"] [[test]] name = "rfc8032" diff --git a/src/signing_key.rs b/src/signing_key.rs index 7679495..24cee11 100644 --- a/src/signing_key.rs +++ b/src/signing_key.rs @@ -13,6 +13,7 @@ use core::convert::TryInto; use curve25519_dalek::{constants, digest::Update, scalar::Scalar}; use rand_core::{CryptoRng, RngCore}; use sha2::{Digest, Sha512}; +use subtle::ConstantTimeEq; use zeroize::Zeroize; pub use ed25519::{ @@ -137,6 +138,20 @@ impl From<[u8; 32]> for SigningKey { } } +impl ConstantTimeEq for SigningKey { + fn ct_eq(&self, other: &Self) -> subtle::Choice { + self.seed.ct_eq(&other.seed) + } +} + +impl PartialEq for SigningKey { + fn eq(&self, other: &Self) -> bool { + self.ct_eq(other).into() + } +} + +impl Eq for SigningKey {} + #[cfg(feature = "pkcs8")] impl<'a> TryFrom> for SigningKey { type Error = Error; diff --git a/src/verification_key.rs b/src/verification_key.rs index 4c33862..39858b9 100644 --- a/src/verification_key.rs +++ b/src/verification_key.rs @@ -113,7 +113,7 @@ impl<'a> TryFrom> for VerificationKeyBytes { /// Curve25519, and non-canonical encodings MUST be accepted; /// /// [ps]: https://zips.z.cash/protocol/protocol.pdf#concreteed25519 -#[derive(Copy, Clone, Debug)] +#[derive(PartialEq, Eq, Copy, Clone, Debug)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(try_from = "VerificationKeyBytes"))] #[cfg_attr(feature = "serde", serde(into = "VerificationKeyBytes"))] diff --git a/tests/unit_tests.rs b/tests/unit_tests.rs index 302db3e..41630f1 100644 --- a/tests/unit_tests.rs +++ b/tests/unit_tests.rs @@ -11,31 +11,28 @@ fn parsing() { let pkb = VerificationKeyBytes::from(&sk); let sig = sk.sign(b"test"); - // Most of these types don't implement Eq, so we check a round trip - // conversion to bytes, using these as the reference points: - let sk_array: [u8; 32] = sk.into(); let pk_array: [u8; 32] = pk.into(); let pkb_array: [u8; 32] = pkb.into(); let sig_array: [u8; 64] = sig.into(); - let sk2 = SigningKey::try_from(sk.as_ref()).unwrap(); - let pk2 = VerificationKey::try_from(pk.as_ref()).unwrap(); - let pkb2 = VerificationKeyBytes::try_from(pkb.as_ref()).unwrap(); - let sig2 = Signature::try_from(<[u8; 64]>::from(sig).as_ref()).unwrap(); + let sk2 = SigningKey::try_from(sk_array).unwrap(); + let pk2 = VerificationKey::try_from(pk_array).unwrap(); + let pkb2 = VerificationKeyBytes::try_from(pkb_array).unwrap(); + let sig2 = Signature::try_from(sig_array).unwrap(); - assert_eq!(&sk_array[..], sk2.as_ref()); - assert_eq!(&pk_array[..], pk2.as_ref()); - assert_eq!(&pkb_array[..], pkb2.as_ref()); - assert_eq!(&sig_array[..], <[u8; 64]>::from(sig2).as_ref()); + assert_eq!(sk, sk2); + assert_eq!(pk, pk2); + assert_eq!(pkb, pkb2); + assert_eq!(sig, sig2); let sk3: SigningKey = bincode::deserialize(sk.as_ref()).unwrap(); let pk3: VerificationKey = bincode::deserialize(pk.as_ref()).unwrap(); let pkb3: VerificationKeyBytes = bincode::deserialize(pkb.as_ref()).unwrap(); - assert_eq!(&sk_array[..], sk3.as_ref()); - assert_eq!(&pk_array[..], pk3.as_ref()); - assert_eq!(&pkb_array[..], pkb3.as_ref()); + assert_eq!(sk, sk3); + assert_eq!(pk, pk3); + assert_eq!(pkb, pkb3); } #[test]