From 88cc32b68779860a40cb4d57c99a55e6ac0b8b16 Mon Sep 17 00:00:00 2001 From: Michael Rosenberg Date: Sun, 29 Jan 2023 04:29:07 -0500 Subject: [PATCH] Updated to curve25519-dalek rc0 --- Cargo.toml | 14 +++++----- src/x25519.rs | 63 +++++++++++++++++-------------------------- tests/x25519_tests.rs | 20 +++++--------- 3 files changed, 37 insertions(+), 60 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d7b6845..5441ff1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,10 +35,10 @@ travis-ci = { repository = "dalek-cryptography/x25519-dalek", branch = "master"} features = ["nightly", "reusable_secrets", "serde"] [dependencies] -curve25519-dalek = { version = "4.0.0-pre.2", default-features = false } +curve25519-dalek = { version = "4.0.0-rc.0", default-features = false } rand_core = { version = "0.6", default-features = false, features = ["getrandom"] } serde = { version = "1", default-features = false, optional = true, features = ["derive"] } -zeroize = { version = "1", default-features = false, features = ["zeroize_derive"] } +zeroize = { version = "1", default-features = false, optional = true, features = ["zeroize_derive"] } [dev-dependencies] bincode = "1" @@ -49,11 +49,9 @@ name = "x25519" harness = false [features] -default = ["alloc"] +default = ["alloc", "precomputed-tables", "zeroize"] +zeroize = ["dep:zeroize", "curve25519-dalek/zeroize"] serde = ["dep:serde", "curve25519-dalek/serde"] -alloc = ["curve25519-dalek/alloc", "serde?/alloc"] +alloc = ["curve25519-dalek/alloc", "serde?/alloc", "zeroize?/alloc"] +precomputed-tables = ["curve25519-dalek/precomputed-tables"] reusable_secrets = [] - - -[patch.crates-io] -curve25519-dalek = { git = "https://github.com/dalek-cryptography/curve25519-dalek", branch = "release/4.0" } \ No newline at end of file diff --git a/src/x25519.rs b/src/x25519.rs index d63405e..442f17d 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -14,14 +14,14 @@ //! This implements x25519 key exchange as specified by Mike Hamburg //! and Adam Langley in [RFC7748](https://tools.ietf.org/html/rfc7748). -use curve25519_dalek::constants::ED25519_BASEPOINT_TABLE; -use curve25519_dalek::montgomery::MontgomeryPoint; -use curve25519_dalek::scalar::Scalar; -use curve25519_dalek::traits::IsIdentity; +use curve25519_dalek::{ + edwards::EdwardsPoint, montgomery::MontgomeryPoint, scalar::Scalar, traits::IsIdentity, +}; use rand_core::CryptoRng; use rand_core::RngCore; +#[cfg(feature = "zeroize")] use zeroize::Zeroize; /// A Diffie-Hellman public key, corresponding to an [`EphemeralSecret`] or @@ -32,7 +32,8 @@ use zeroize::Zeroize; /// (in this crate) does *not* automatically happen, but either must be derived /// for Drop or explicitly called. #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug, Zeroize)] +#[cfg_attr(feature = "zeroize", derive(Zeroize))] +#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)] pub struct PublicKey(pub(crate) MontgomeryPoint); impl From<[u8; 32]> for PublicKey { @@ -64,8 +65,8 @@ impl PublicKey { /// are no serialization methods defined. This means that [`EphemeralSecret`]s can only be /// generated from fresh randomness by [`EphemeralSecret::new`] and the compiler statically checks /// that the resulting secret is used at most once. -#[derive(Zeroize)] -#[zeroize(drop)] +#[cfg_attr(feature = "zeroize", derive(Zeroize))] +#[cfg_attr(feature = "zeroize", zeroize(drop))] pub struct EphemeralSecret(pub(crate) Scalar); impl EphemeralSecret { @@ -81,14 +82,14 @@ impl EphemeralSecret { csprng.fill_bytes(&mut bytes); - EphemeralSecret(clamp_scalar(bytes)) + EphemeralSecret(Scalar::from_bits_clamped(bytes)) } } impl<'a> From<&'a EphemeralSecret> for PublicKey { /// Given an x25519 [`EphemeralSecret`] key, compute its corresponding [`PublicKey`]. fn from(secret: &'a EphemeralSecret) -> PublicKey { - PublicKey((&ED25519_BASEPOINT_TABLE * &secret.0).to_montgomery()) + PublicKey(EdwardsPoint::mul_base(&secret.0).to_montgomery()) } } @@ -111,8 +112,9 @@ impl<'a> From<&'a EphemeralSecret> for PublicKey { /// secret keys are never reused, which can have very serious security /// implications for many protocols. #[cfg(feature = "reusable_secrets")] -#[derive(Clone, Zeroize)] -#[zeroize(drop)] +#[cfg_attr(feature = "zeroize", derive(Zeroize))] +#[cfg_attr(feature = "zeroize", zeroize(drop))] +#[derive(Clone)] pub struct ReusableSecret(pub(crate) Scalar); #[cfg(feature = "reusable_secrets")] @@ -129,7 +131,7 @@ impl ReusableSecret { csprng.fill_bytes(&mut bytes); - ReusableSecret(clamp_scalar(bytes)) + ReusableSecret(Scalar::from_bits_clamped(bytes)) } } @@ -137,7 +139,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((&ED25519_BASEPOINT_TABLE * &secret.0).to_montgomery()) + PublicKey(EdwardsPoint::mul_base(&secret.0).to_montgomery()) } } @@ -156,8 +158,9 @@ impl<'a> From<&'a ReusableSecret> for PublicKey { /// secret keys are never reused, which can have very serious security /// implications for many protocols. #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[derive(Clone, Zeroize)] -#[zeroize(drop)] +#[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, ); @@ -175,7 +178,7 @@ impl StaticSecret { csprng.fill_bytes(&mut bytes); - StaticSecret(clamp_scalar(bytes)) + StaticSecret(Scalar::from_bits_clamped(bytes)) } /// Extract this key's bytes for serialization. @@ -187,14 +190,14 @@ impl StaticSecret { impl From<[u8; 32]> for StaticSecret { /// Load a secret key from a byte array. fn from(bytes: [u8; 32]) -> StaticSecret { - StaticSecret(clamp_scalar(bytes)) + StaticSecret(Scalar::from_bits_clamped(bytes)) } } impl<'a> From<&'a StaticSecret> for PublicKey { /// Given an x25519 [`StaticSecret`] key, compute its corresponding [`PublicKey`]. fn from(secret: &'a StaticSecret) -> PublicKey { - PublicKey((&ED25519_BASEPOINT_TABLE * &secret.0).to_montgomery()) + PublicKey(EdwardsPoint::mul_base(&secret.0).to_montgomery()) } } @@ -202,8 +205,8 @@ impl<'a> From<&'a StaticSecret> for PublicKey { /// /// Each party computes this using their [`EphemeralSecret`] or [`StaticSecret`] and their /// counterparty's [`PublicKey`]. -#[derive(Zeroize)] -#[zeroize(drop)] +#[cfg_attr(feature = "zeroize", derive(Zeroize))] +#[cfg_attr(feature = "zeroize", zeroize(drop))] pub struct SharedSecret(pub(crate) MontgomeryPoint); impl SharedSecret { @@ -258,22 +261,6 @@ impl SharedSecret { } } -/// "Decode" a scalar from a 32-byte array. -/// -/// By "decode" here, what is really meant is applying key clamping by twiddling -/// some bits. -/// -/// # Returns -/// -/// A `Scalar`. -fn clamp_scalar(mut scalar: [u8; 32]) -> Scalar { - scalar[0] &= 248; - scalar[31] &= 127; - scalar[31] |= 64; - - Scalar::from_bits(scalar) -} - /// The bare, byte-oriented x25519 function, exactly as specified in RFC7748. /// /// This can be used with [`X25519_BASEPOINT_BYTES`] for people who @@ -305,7 +292,7 @@ fn clamp_scalar(mut scalar: [u8; 32]) -> Scalar { /// assert_eq!(alice_shared, bob_shared); /// ``` pub fn x25519(k: [u8; 32], u: [u8; 32]) -> [u8; 32] { - (clamp_scalar(k) * MontgomeryPoint(u)).to_bytes() + (Scalar::from_bits_clamped(k) * MontgomeryPoint(u)).to_bytes() } /// The X25519 basepoint, for use with the bare, byte-oriented x25519 @@ -325,6 +312,6 @@ struct AllowUnreducedScalarBytes( ); impl From for Scalar { fn from(bytes: AllowUnreducedScalarBytes) -> Scalar { - clamp_scalar(bytes.0) + Scalar::from_bits_clamped(bytes.0) } } diff --git a/tests/x25519_tests.rs b/tests/x25519_tests.rs index 181eedb..21eeb43 100644 --- a/tests/x25519_tests.rs +++ b/tests/x25519_tests.rs @@ -1,16 +1,7 @@ -use curve25519_dalek::constants::ED25519_BASEPOINT_TABLE; -use curve25519_dalek::scalar::Scalar; +use curve25519_dalek::{edwards::EdwardsPoint, scalar::Scalar}; use x25519_dalek::*; -fn clamp_scalar(mut scalar: [u8; 32]) -> Scalar { - scalar[0] &= 248; - scalar[31] &= 127; - scalar[31] |= 64; - - Scalar::from_bits(scalar) -} - #[test] fn byte_basepoint_matches_edwards_scalar_mul() { let mut scalar_bytes = [0x37; 32]; @@ -20,9 +11,10 @@ fn byte_basepoint_matches_edwards_scalar_mul() { let result = x25519(scalar_bytes, X25519_BASEPOINT_BYTES); - let expected = (&ED25519_BASEPOINT_TABLE * &clamp_scalar(scalar_bytes)) - .to_montgomery() - .to_bytes(); + let expected = { + let scalar = Scalar::from_bits_clamped(scalar_bytes); + EdwardsPoint::mul_base(&scalar).to_montgomery().to_bytes() + }; assert_eq!(result, expected); } @@ -72,7 +64,7 @@ fn serde_bincode_static_secret_matches_from_bytes() { use bincode; let expected = StaticSecret::from([0x24; 32]); - let clamped_bytes = clamp_scalar([0x24; 32]).to_bytes(); + let clamped_bytes = Scalar::from_bits_clamped([0x24; 32]).to_bytes(); let decoded: StaticSecret = bincode::deserialize(&clamped_bytes).unwrap(); assert_eq!(decoded.to_bytes(), expected.to_bytes());