diff --git a/CHANGELOG.md b/CHANGELOG.md index 672e901..eaa5548 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ Entries are listed in reverse chronological order. # 2.x Series + +## 2.0.0-rc.3 + +* Change: `StaticSecret` serialization and `to_bytes()` no longer returns clamped integers. Clamping is still always done during scalar-point multiplication. + ## 2.0.0-rc.2 * Update MSRV to 1.60. diff --git a/Cargo.lock b/Cargo.lock index ce67227..258ad05 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -188,8 +188,7 @@ dependencies = [ [[package]] name = "curve25519-dalek" version = "4.0.0-rc.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03d928d978dbec61a1167414f5ec534f24bea0d7a0d24dd9b6233d3d8223e585" +source = "git+https://github.com/dalek-cryptography/curve25519-dalek.git?rev=f460ae149b0000695205cc78f560d74a2d3918eb#f460ae149b0000695205cc78f560d74a2d3918eb" dependencies = [ "cfg-if", "fiat-crypto", diff --git a/Cargo.toml b/Cargo.toml index d2f068a..7064976 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,3 +61,7 @@ alloc = ["curve25519-dalek/alloc", "serde?/alloc", "zeroize?/alloc"] precomputed-tables = ["curve25519-dalek/precomputed-tables"] reusable_secrets = [] static_secrets = [] + +[patch.crates-io.curve25519-dalek] +git = "https://github.com/dalek-cryptography/curve25519-dalek.git" +rev = "f460ae149b0000695205cc78f560d74a2d3918eb" diff --git a/src/x25519.rs b/src/x25519.rs index c8008ac..e1c79d4 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -14,9 +14,7 @@ //! This implements x25519 key exchange as specified by Mike Hamburg //! and Adam Langley in [RFC7748](https://tools.ietf.org/html/rfc7748). -use curve25519_dalek::{ - edwards::EdwardsPoint, montgomery::MontgomeryPoint, scalar::Scalar, traits::IsIdentity, -}; +use curve25519_dalek::{edwards::EdwardsPoint, montgomery::MontgomeryPoint, traits::IsIdentity}; use rand_core::CryptoRng; use rand_core::RngCore; @@ -74,13 +72,13 @@ impl AsRef<[u8]> for PublicKey { /// secret is used at most once. #[cfg_attr(feature = "zeroize", derive(Zeroize))] #[cfg_attr(feature = "zeroize", zeroize(drop))] -pub struct EphemeralSecret(pub(crate) Scalar); +pub struct EphemeralSecret(pub(crate) [u8; 32]); impl EphemeralSecret { /// Perform a Diffie-Hellman key agreement between `self` and /// `their_public` key to produce a [`SharedSecret`]. pub fn diffie_hellman(self, their_public: &PublicKey) -> SharedSecret { - SharedSecret(self.0 * their_public.0) + SharedSecret(their_public.0.mul_clamped(self.0)) } /// Generate a new [`EphemeralSecret`] with the supplied RNG. @@ -94,11 +92,10 @@ impl EphemeralSecret { /// Generate a new [`EphemeralSecret`] with the supplied RNG. pub fn random_from_rng(mut csprng: T) -> Self { + // The secret key is random bytes. Clamping is done later. let mut bytes = [0u8; 32]; - csprng.fill_bytes(&mut bytes); - - EphemeralSecret(Scalar::from_bits_clamped(bytes)) + EphemeralSecret(bytes) } /// Generate a new [`EphemeralSecret`]. @@ -111,7 +108,7 @@ impl EphemeralSecret { impl<'a> From<&'a EphemeralSecret> for PublicKey { /// Given an x25519 [`EphemeralSecret`] key, compute its corresponding [`PublicKey`]. fn from(secret: &'a EphemeralSecret) -> PublicKey { - PublicKey(EdwardsPoint::mul_base(&secret.0).to_montgomery()) + PublicKey(EdwardsPoint::mul_base_clamped(secret.0).to_montgomery()) } } @@ -137,14 +134,14 @@ impl<'a> From<&'a EphemeralSecret> for PublicKey { #[cfg_attr(feature = "zeroize", derive(Zeroize))] #[cfg_attr(feature = "zeroize", zeroize(drop))] #[derive(Clone)] -pub struct ReusableSecret(pub(crate) Scalar); +pub struct ReusableSecret(pub(crate) [u8; 32]); #[cfg(feature = "reusable_secrets")] impl ReusableSecret { /// Perform a Diffie-Hellman key agreement between `self` and /// `their_public` key to produce a [`SharedSecret`]. pub fn diffie_hellman(&self, their_public: &PublicKey) -> SharedSecret { - SharedSecret(self.0 * their_public.0) + SharedSecret(their_public.0.mul_clamped(self.0)) } /// Generate a new [`ReusableSecret`] with the supplied RNG. @@ -158,11 +155,10 @@ impl ReusableSecret { /// Generate a new [`ReusableSecret`] with the supplied RNG. pub fn random_from_rng(mut csprng: T) -> Self { + // The secret key is random bytes. Clamping is done later. let mut bytes = [0u8; 32]; - csprng.fill_bytes(&mut bytes); - - ReusableSecret(Scalar::from_bits_clamped(bytes)) + ReusableSecret(bytes) } /// Generate a new [`ReusableSecret`]. @@ -176,7 +172,7 @@ impl ReusableSecret { impl<'a> From<&'a ReusableSecret> for PublicKey { /// Given an x25519 [`ReusableSecret`] key, compute its corresponding [`PublicKey`]. fn from(secret: &'a ReusableSecret) -> PublicKey { - PublicKey(EdwardsPoint::mul_base(&secret.0).to_montgomery()) + PublicKey(EdwardsPoint::mul_base_clamped(secret.0).to_montgomery()) } } @@ -199,16 +195,14 @@ impl<'a> From<&'a ReusableSecret> for PublicKey { #[cfg_attr(feature = "zeroize", derive(Zeroize))] #[cfg_attr(feature = "zeroize", zeroize(drop))] #[derive(Clone)] -pub struct StaticSecret( - #[cfg_attr(feature = "serde", serde(with = "AllowUnreducedScalarBytes"))] pub(crate) Scalar, -); +pub struct StaticSecret([u8; 32]); #[cfg(feature = "static_secrets")] impl StaticSecret { /// Perform a Diffie-Hellman key agreement between `self` and /// `their_public` key to produce a `SharedSecret`. pub fn diffie_hellman(&self, their_public: &PublicKey) -> SharedSecret { - SharedSecret(self.0 * their_public.0) + SharedSecret(their_public.0.mul_clamped(self.0)) } /// Generate a new [`StaticSecret`] with the supplied RNG. @@ -222,11 +216,10 @@ impl StaticSecret { /// Generate a new [`StaticSecret`] with the supplied RNG. pub fn random_from_rng(mut csprng: T) -> Self { + // The secret key is random bytes. Clamping is done later. let mut bytes = [0u8; 32]; - csprng.fill_bytes(&mut bytes); - - StaticSecret(Scalar::from_bits_clamped(bytes)) + StaticSecret(bytes) } /// Generate a new [`StaticSecret`]. @@ -238,13 +231,13 @@ impl StaticSecret { /// Extract this key's bytes for serialization. #[inline] pub fn to_bytes(&self) -> [u8; 32] { - self.0.to_bytes() + self.0 } /// View this key as a byte array. #[inline] pub fn as_bytes(&self) -> &[u8; 32] { - self.0.as_bytes() + &self.0 } } @@ -252,7 +245,7 @@ impl StaticSecret { impl From<[u8; 32]> for StaticSecret { /// Load a secret key from a byte array. fn from(bytes: [u8; 32]) -> StaticSecret { - StaticSecret(Scalar::from_bits_clamped(bytes)) + StaticSecret(bytes) } } @@ -260,7 +253,7 @@ impl From<[u8; 32]> for StaticSecret { impl<'a> From<&'a StaticSecret> for PublicKey { /// Given an x25519 [`StaticSecret`] key, compute its corresponding [`PublicKey`]. fn from(secret: &'a StaticSecret) -> PublicKey { - PublicKey(EdwardsPoint::mul_base(&secret.0).to_montgomery()) + PublicKey(EdwardsPoint::mul_base_clamped(secret.0).to_montgomery()) } } @@ -373,7 +366,7 @@ impl AsRef<[u8]> for SharedSecret { /// assert_eq!(alice_shared, bob_shared); /// ``` pub fn x25519(k: [u8; 32], u: [u8; 32]) -> [u8; 32] { - (Scalar::from_bits_clamped(k) * MontgomeryPoint(u)).to_bytes() + MontgomeryPoint(u).mul_clamped(k).to_bytes() } /// The X25519 basepoint, for use with the bare, byte-oriented x25519 @@ -382,17 +375,3 @@ pub fn x25519(k: [u8; 32], u: [u8; 32]) -> [u8; 32] { pub const X25519_BASEPOINT_BYTES: [u8; 32] = [ 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]; - -/// Derived serialization methods will not work on a StaticSecret because x25519 requires -/// non-canonical scalars which are rejected by curve25519-dalek. Thus we provide a way to convert -/// the bytes directly to a scalar using Serde's remote derive functionality. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(remote = "Scalar"))] -struct AllowUnreducedScalarBytes( - #[cfg_attr(feature = "serde", serde(getter = "Scalar::to_bytes"))] [u8; 32], -); -impl From for Scalar { - fn from(bytes: AllowUnreducedScalarBytes) -> Scalar { - Scalar::from_bits_clamped(bytes.0) - } -} diff --git a/tests/x25519_tests.rs b/tests/x25519_tests.rs index d883915..d589b3e 100644 --- a/tests/x25519_tests.rs +++ b/tests/x25519_tests.rs @@ -1,4 +1,4 @@ -use curve25519_dalek::{edwards::EdwardsPoint, scalar::Scalar}; +use curve25519_dalek::edwards::EdwardsPoint; use x25519_dalek::*; @@ -10,11 +10,9 @@ fn byte_basepoint_matches_edwards_scalar_mul() { scalar_bytes[i] += 2; let result = x25519(scalar_bytes, X25519_BASEPOINT_BYTES); - - let expected = { - let scalar = Scalar::from_bits_clamped(scalar_bytes); - EdwardsPoint::mul_base(&scalar).to_montgomery().to_bytes() - }; + let expected = EdwardsPoint::mul_base_clamped(scalar_bytes) + .to_montgomery() + .to_bytes(); assert_eq!(result, expected); } @@ -64,8 +62,7 @@ fn serde_bincode_static_secret_matches_from_bytes() { use bincode; let expected = StaticSecret::from([0x24; 32]); - let clamped_bytes = Scalar::from_bits_clamped([0x24; 32]).to_bytes(); - let decoded: StaticSecret = bincode::deserialize(&clamped_bytes).unwrap(); + let decoded: StaticSecret = bincode::deserialize(&[0x24; 32]).unwrap(); assert_eq!(decoded.to_bytes(), expected.to_bytes()); }