Update to new Scalar API (#120)

* Updated to new curve25519 scalar API

* Removed clamping from constructors; clamping is always done during scalar-point multiplication

* Updated test to reflect new functionality

* Updated changelog
This commit is contained in:
Michael Rosenberg 2023-03-31 16:58:35 -04:00 committed by GitHub
parent cccf389467
commit 25fa593df2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 35 additions and 51 deletions

View file

@ -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.

3
Cargo.lock generated
View file

@ -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",

View file

@ -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"

View file

@ -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<T: RngCore + CryptoRng>(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<T: RngCore + CryptoRng>(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<T: RngCore + CryptoRng>(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<AllowUnreducedScalarBytes> for Scalar {
fn from(bytes: AllowUnreducedScalarBytes) -> Scalar {
Scalar::from_bits_clamped(bytes.0)
}
}

View file

@ -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());
}