diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 1c08037..199314c 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -2,9 +2,9 @@ name: Rust on: push: - branches: [ '*' ] + branches: [ '**' ] pull_request: - branches: [ main, develop, release ] + branches: [ '**' ] env: CARGO_TERM_COLOR: always diff --git a/CHANGELOG.md b/CHANGELOG.md index eabe803..c3d6317 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ Entries are listed in reverse chronological order. # 2.x Series +* Update MSRV to 1.60. + ## 2.0.0-pre.1 * Loosen restriction on zeroize dependency version from =1.3 to 1. diff --git a/Cargo.toml b/Cargo.toml index 126d7be..5441ff1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "x25519-dalek" -edition = "2018" +edition = "2021" # Before changing this: # - update version in README.md # - update html_root_url @@ -25,6 +25,7 @@ exclude = [ ".travis.yml", "CONTRIBUTING.md", ] +rust-version = "1.60" [badges] travis-ci = { repository = "dalek-cryptography/x25519-dalek", branch = "master"} @@ -34,12 +35,10 @@ travis-ci = { repository = "dalek-cryptography/x25519-dalek", branch = "master"} features = ["nightly", "reusable_secrets", "serde"] [dependencies] -curve25519-dalek = { version = "3", default-features = false } -rand_core = { version = "0.6", default-features = false } -# `serde` is renamed to `our_serde` in order to avoid a name collision between -# importing the serde dependency and enabling the curve25519-dalek/serde feature -our_serde = { package = "serde", version = "1", default-features = false, optional = true, features = ["derive"] } -zeroize = { version = "1", default-features = false, features = ["zeroize_derive"] } +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, optional = true, features = ["zeroize_derive"] } [dev-dependencies] bincode = "1" @@ -50,12 +49,9 @@ name = "x25519" harness = false [features] -default = ["std", "u64_backend"] -serde = ["our_serde", "curve25519-dalek/serde"] -std = ["curve25519-dalek/std"] -nightly = ["curve25519-dalek/nightly"] +default = ["alloc", "precomputed-tables", "zeroize"] +zeroize = ["dep:zeroize", "curve25519-dalek/zeroize"] +serde = ["dep:serde", "curve25519-dalek/serde"] +alloc = ["curve25519-dalek/alloc", "serde?/alloc", "zeroize?/alloc"] +precomputed-tables = ["curve25519-dalek/precomputed-tables"] reusable_secrets = [] -u64_backend = ["curve25519-dalek/u64_backend"] -u32_backend = ["curve25519-dalek/u32_backend"] -fiat_u64_backend = ["curve25519-dalek/fiat_u64_backend"] -fiat_u32_backend = ["curve25519-dalek/fiat_u32_backend"] diff --git a/README.md b/README.md index c2c6fb6..7145f06 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,7 @@ x25519-dalek = "2.0.0-pre.0" # MSRV -Current MSRV is 1.51. +Current MSRV is 1.60. # Documentation diff --git a/benches/x25519.rs b/benches/x25519.rs index e5d77d2..dfcee4a 100644 --- a/benches/x25519.rs +++ b/benches/x25519.rs @@ -11,13 +11,7 @@ //! Benchmark the Diffie-Hellman operation. -#[macro_use] -extern crate criterion; -extern crate curve25519_dalek; -extern crate rand_core; -extern crate x25519_dalek; - -use criterion::Criterion; +use criterion::{criterion_group, criterion_main, Criterion}; use rand_core::OsRng; @@ -25,12 +19,12 @@ use x25519_dalek::EphemeralSecret; use x25519_dalek::PublicKey; fn bench_diffie_hellman(c: &mut Criterion) { - let bob_secret = EphemeralSecret::new(&mut OsRng); + let bob_secret = EphemeralSecret::new(OsRng); let bob_public = PublicKey::from(&bob_secret); c.bench_function("diffie_hellman", move |b| { b.iter_with_setup( - || EphemeralSecret::new(&mut OsRng), + || EphemeralSecret::new(OsRng), |alice_secret| alice_secret.diffie_hellman(&bob_public), ) }); diff --git a/src/lib.rs b/src/lib.rs index 0c6485a..01369d0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -129,7 +129,7 @@ //! //! # MSRV //! -//! Current MSRV is 1.41 for production builds, and 1.48 for running tests. +//! Current MSRV is 1.60. //! //! # Documentation //! @@ -155,12 +155,6 @@ //! //! [crypto_box]: https://github.com/RustCrypto/AEADs/tree/master/crypto_box -extern crate curve25519_dalek; - -extern crate rand_core; - -extern crate zeroize; - mod x25519; pub use crate::x25519::*; diff --git a/src/x25519.rs b/src/x25519.rs index ed4fe9d..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 @@ -31,12 +31,9 @@ use zeroize::Zeroize; /// should they wish to erase public keys from memory. Note that this erasure /// (in this crate) does *not* automatically happen, but either must be derived /// for Drop or explicitly called. -#[cfg_attr(feature = "serde", serde(crate = "our_serde"))] -#[cfg_attr( - feature = "serde", - derive(our_serde::Serialize, our_serde::Deserialize) -)] -#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug, Zeroize)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[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 { @@ -68,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 { @@ -85,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()) } } @@ -115,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")] @@ -124,7 +122,7 @@ 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(self.0 * their_public.0) } /// Generate a non-serializeable x25519 [`ReuseableSecret`] key. @@ -133,7 +131,7 @@ impl ReusableSecret { csprng.fill_bytes(&mut bytes); - ReusableSecret(clamp_scalar(bytes)) + ReusableSecret(Scalar::from_bits_clamped(bytes)) } } @@ -141,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()) } } @@ -159,13 +157,10 @@ impl<'a> From<&'a ReusableSecret> for PublicKey { /// [`EphemeralSecret`] at all times, as that type enforces at compile-time that /// secret keys are never reused, which can have very serious security /// implications for many protocols. -#[cfg_attr(feature = "serde", serde(crate = "our_serde"))] -#[cfg_attr( - feature = "serde", - derive(our_serde::Serialize, our_serde::Deserialize) -)] -#[derive(Clone, Zeroize)] -#[zeroize(drop)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[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, ); @@ -174,7 +169,7 @@ 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(self.0 * their_public.0) } /// Generate an x25519 key. @@ -183,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. @@ -195,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()) } } @@ -210,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 { @@ -266,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 @@ -289,8 +268,6 @@ fn clamp_scalar(mut scalar: [u8; 32]) -> Scalar { /// /// # Example /// ``` -/// # extern crate rand_core; -/// # /// use rand_core::OsRng; /// use rand_core::RngCore; /// @@ -315,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 @@ -328,17 +305,13 @@ pub const X25519_BASEPOINT_BYTES: [u8; 32] = [ /// 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", serde(crate = "our_serde"))] -#[cfg_attr( - feature = "serde", - derive(our_serde::Serialize, our_serde::Deserialize) -)] +#[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 { - clamp_scalar(bytes.0) + Scalar::from_bits_clamped(bytes.0) } } diff --git a/tests/x25519_tests.rs b/tests/x25519_tests.rs index 9fce935..21eeb43 100644 --- a/tests/x25519_tests.rs +++ b/tests/x25519_tests.rs @@ -1,17 +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]; @@ -21,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); } @@ -73,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()); @@ -88,19 +79,19 @@ fn do_rfc7748_ladder_test1(input_scalar: [u8; 32], input_point: [u8; 32], expect #[test] fn rfc7748_ladder_test1_vectorset1() { let input_scalar: [u8; 32] = [ - 0xa5, 0x46, 0xe3, 0x6b, 0xf0, 0x52, 0x7c, 0x9d, 0x3b, 0x16, 0x15, 0x4b, 0x82, 0x46, - 0x5e, 0xdd, 0x62, 0x14, 0x4c, 0x0a, 0xc1, 0xfc, 0x5a, 0x18, 0x50, 0x6a, 0x22, 0x44, - 0xba, 0x44, 0x9a, 0xc4, + 0xa5, 0x46, 0xe3, 0x6b, 0xf0, 0x52, 0x7c, 0x9d, 0x3b, 0x16, 0x15, 0x4b, 0x82, 0x46, 0x5e, + 0xdd, 0x62, 0x14, 0x4c, 0x0a, 0xc1, 0xfc, 0x5a, 0x18, 0x50, 0x6a, 0x22, 0x44, 0xba, 0x44, + 0x9a, 0xc4, ]; let input_point: [u8; 32] = [ - 0xe6, 0xdb, 0x68, 0x67, 0x58, 0x30, 0x30, 0xdb, 0x35, 0x94, 0xc1, 0xa4, 0x24, 0xb1, - 0x5f, 0x7c, 0x72, 0x66, 0x24, 0xec, 0x26, 0xb3, 0x35, 0x3b, 0x10, 0xa9, 0x03, 0xa6, - 0xd0, 0xab, 0x1c, 0x4c, + 0xe6, 0xdb, 0x68, 0x67, 0x58, 0x30, 0x30, 0xdb, 0x35, 0x94, 0xc1, 0xa4, 0x24, 0xb1, 0x5f, + 0x7c, 0x72, 0x66, 0x24, 0xec, 0x26, 0xb3, 0x35, 0x3b, 0x10, 0xa9, 0x03, 0xa6, 0xd0, 0xab, + 0x1c, 0x4c, ]; let expected: [u8; 32] = [ - 0xc3, 0xda, 0x55, 0x37, 0x9d, 0xe9, 0xc6, 0x90, 0x8e, 0x94, 0xea, 0x4d, 0xf2, 0x8d, - 0x08, 0x4f, 0x32, 0xec, 0xcf, 0x03, 0x49, 0x1c, 0x71, 0xf7, 0x54, 0xb4, 0x07, 0x55, - 0x77, 0xa2, 0x85, 0x52, + 0xc3, 0xda, 0x55, 0x37, 0x9d, 0xe9, 0xc6, 0x90, 0x8e, 0x94, 0xea, 0x4d, 0xf2, 0x8d, 0x08, + 0x4f, 0x32, 0xec, 0xcf, 0x03, 0x49, 0x1c, 0x71, 0xf7, 0x54, 0xb4, 0x07, 0x55, 0x77, 0xa2, + 0x85, 0x52, ]; do_rfc7748_ladder_test1(input_scalar, input_point, expected); @@ -109,19 +100,19 @@ fn rfc7748_ladder_test1_vectorset1() { #[test] fn rfc7748_ladder_test1_vectorset2() { let input_scalar: [u8; 32] = [ - 0x4b, 0x66, 0xe9, 0xd4, 0xd1, 0xb4, 0x67, 0x3c, 0x5a, 0xd2, 0x26, 0x91, 0x95, 0x7d, - 0x6a, 0xf5, 0xc1, 0x1b, 0x64, 0x21, 0xe0, 0xea, 0x01, 0xd4, 0x2c, 0xa4, 0x16, 0x9e, - 0x79, 0x18, 0xba, 0x0d, + 0x4b, 0x66, 0xe9, 0xd4, 0xd1, 0xb4, 0x67, 0x3c, 0x5a, 0xd2, 0x26, 0x91, 0x95, 0x7d, 0x6a, + 0xf5, 0xc1, 0x1b, 0x64, 0x21, 0xe0, 0xea, 0x01, 0xd4, 0x2c, 0xa4, 0x16, 0x9e, 0x79, 0x18, + 0xba, 0x0d, ]; let input_point: [u8; 32] = [ - 0xe5, 0x21, 0x0f, 0x12, 0x78, 0x68, 0x11, 0xd3, 0xf4, 0xb7, 0x95, 0x9d, 0x05, 0x38, - 0xae, 0x2c, 0x31, 0xdb, 0xe7, 0x10, 0x6f, 0xc0, 0x3c, 0x3e, 0xfc, 0x4c, 0xd5, 0x49, - 0xc7, 0x15, 0xa4, 0x93, + 0xe5, 0x21, 0x0f, 0x12, 0x78, 0x68, 0x11, 0xd3, 0xf4, 0xb7, 0x95, 0x9d, 0x05, 0x38, 0xae, + 0x2c, 0x31, 0xdb, 0xe7, 0x10, 0x6f, 0xc0, 0x3c, 0x3e, 0xfc, 0x4c, 0xd5, 0x49, 0xc7, 0x15, + 0xa4, 0x93, ]; let expected: [u8; 32] = [ - 0x95, 0xcb, 0xde, 0x94, 0x76, 0xe8, 0x90, 0x7d, 0x7a, 0xad, 0xe4, 0x5c, 0xb4, 0xb8, - 0x73, 0xf8, 0x8b, 0x59, 0x5a, 0x68, 0x79, 0x9f, 0xa1, 0x52, 0xe6, 0xf8, 0xf7, 0x64, - 0x7a, 0xac, 0x79, 0x57, + 0x95, 0xcb, 0xde, 0x94, 0x76, 0xe8, 0x90, 0x7d, 0x7a, 0xad, 0xe4, 0x5c, 0xb4, 0xb8, 0x73, + 0xf8, 0x8b, 0x59, 0x5a, 0x68, 0x79, 0x9f, 0xa1, 0x52, 0xe6, 0xf8, 0xf7, 0x64, 0x7a, 0xac, + 0x79, 0x57, ]; do_rfc7748_ladder_test1(input_scalar, input_point, expected); @@ -189,4 +180,3 @@ fn rfc7748_ladder_test2() { ] ); } -