mirror of
https://github.com/saymrwulf/curve25519-dalek-source.git
synced 2026-09-06 20:41:14 +00:00
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:
parent
cccf389467
commit
25fa593df2
5 changed files with 35 additions and 51 deletions
|
|
@ -4,6 +4,11 @@ Entries are listed in reverse chronological order.
|
||||||
|
|
||||||
# 2.x Series
|
# 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
|
## 2.0.0-rc.2
|
||||||
|
|
||||||
* Update MSRV to 1.60.
|
* Update MSRV to 1.60.
|
||||||
|
|
|
||||||
3
Cargo.lock
generated
3
Cargo.lock
generated
|
|
@ -188,8 +188,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "curve25519-dalek"
|
name = "curve25519-dalek"
|
||||||
version = "4.0.0-rc.2"
|
version = "4.0.0-rc.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "git+https://github.com/dalek-cryptography/curve25519-dalek.git?rev=f460ae149b0000695205cc78f560d74a2d3918eb#f460ae149b0000695205cc78f560d74a2d3918eb"
|
||||||
checksum = "03d928d978dbec61a1167414f5ec534f24bea0d7a0d24dd9b6233d3d8223e585"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"fiat-crypto",
|
"fiat-crypto",
|
||||||
|
|
|
||||||
|
|
@ -61,3 +61,7 @@ alloc = ["curve25519-dalek/alloc", "serde?/alloc", "zeroize?/alloc"]
|
||||||
precomputed-tables = ["curve25519-dalek/precomputed-tables"]
|
precomputed-tables = ["curve25519-dalek/precomputed-tables"]
|
||||||
reusable_secrets = []
|
reusable_secrets = []
|
||||||
static_secrets = []
|
static_secrets = []
|
||||||
|
|
||||||
|
[patch.crates-io.curve25519-dalek]
|
||||||
|
git = "https://github.com/dalek-cryptography/curve25519-dalek.git"
|
||||||
|
rev = "f460ae149b0000695205cc78f560d74a2d3918eb"
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,7 @@
|
||||||
//! This implements x25519 key exchange as specified by Mike Hamburg
|
//! This implements x25519 key exchange as specified by Mike Hamburg
|
||||||
//! and Adam Langley in [RFC7748](https://tools.ietf.org/html/rfc7748).
|
//! and Adam Langley in [RFC7748](https://tools.ietf.org/html/rfc7748).
|
||||||
|
|
||||||
use curve25519_dalek::{
|
use curve25519_dalek::{edwards::EdwardsPoint, montgomery::MontgomeryPoint, traits::IsIdentity};
|
||||||
edwards::EdwardsPoint, montgomery::MontgomeryPoint, scalar::Scalar, traits::IsIdentity,
|
|
||||||
};
|
|
||||||
|
|
||||||
use rand_core::CryptoRng;
|
use rand_core::CryptoRng;
|
||||||
use rand_core::RngCore;
|
use rand_core::RngCore;
|
||||||
|
|
@ -74,13 +72,13 @@ impl AsRef<[u8]> for PublicKey {
|
||||||
/// secret is used at most once.
|
/// secret is used at most once.
|
||||||
#[cfg_attr(feature = "zeroize", derive(Zeroize))]
|
#[cfg_attr(feature = "zeroize", derive(Zeroize))]
|
||||||
#[cfg_attr(feature = "zeroize", zeroize(drop))]
|
#[cfg_attr(feature = "zeroize", zeroize(drop))]
|
||||||
pub struct EphemeralSecret(pub(crate) Scalar);
|
pub struct EphemeralSecret(pub(crate) [u8; 32]);
|
||||||
|
|
||||||
impl EphemeralSecret {
|
impl EphemeralSecret {
|
||||||
/// Perform a Diffie-Hellman key agreement between `self` and
|
/// Perform a Diffie-Hellman key agreement between `self` and
|
||||||
/// `their_public` key to produce a [`SharedSecret`].
|
/// `their_public` key to produce a [`SharedSecret`].
|
||||||
pub fn diffie_hellman(self, their_public: &PublicKey) -> 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.
|
/// Generate a new [`EphemeralSecret`] with the supplied RNG.
|
||||||
|
|
@ -94,11 +92,10 @@ impl EphemeralSecret {
|
||||||
|
|
||||||
/// Generate a new [`EphemeralSecret`] with the supplied RNG.
|
/// Generate a new [`EphemeralSecret`] with the supplied RNG.
|
||||||
pub fn random_from_rng<T: RngCore + CryptoRng>(mut csprng: T) -> Self {
|
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];
|
let mut bytes = [0u8; 32];
|
||||||
|
|
||||||
csprng.fill_bytes(&mut bytes);
|
csprng.fill_bytes(&mut bytes);
|
||||||
|
EphemeralSecret(bytes)
|
||||||
EphemeralSecret(Scalar::from_bits_clamped(bytes))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate a new [`EphemeralSecret`].
|
/// Generate a new [`EphemeralSecret`].
|
||||||
|
|
@ -111,7 +108,7 @@ impl EphemeralSecret {
|
||||||
impl<'a> From<&'a EphemeralSecret> for PublicKey {
|
impl<'a> From<&'a EphemeralSecret> for PublicKey {
|
||||||
/// Given an x25519 [`EphemeralSecret`] key, compute its corresponding [`PublicKey`].
|
/// Given an x25519 [`EphemeralSecret`] key, compute its corresponding [`PublicKey`].
|
||||||
fn from(secret: &'a EphemeralSecret) -> 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", derive(Zeroize))]
|
||||||
#[cfg_attr(feature = "zeroize", zeroize(drop))]
|
#[cfg_attr(feature = "zeroize", zeroize(drop))]
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ReusableSecret(pub(crate) Scalar);
|
pub struct ReusableSecret(pub(crate) [u8; 32]);
|
||||||
|
|
||||||
#[cfg(feature = "reusable_secrets")]
|
#[cfg(feature = "reusable_secrets")]
|
||||||
impl ReusableSecret {
|
impl ReusableSecret {
|
||||||
/// Perform a Diffie-Hellman key agreement between `self` and
|
/// Perform a Diffie-Hellman key agreement between `self` and
|
||||||
/// `their_public` key to produce a [`SharedSecret`].
|
/// `their_public` key to produce a [`SharedSecret`].
|
||||||
pub fn diffie_hellman(&self, their_public: &PublicKey) -> 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.
|
/// Generate a new [`ReusableSecret`] with the supplied RNG.
|
||||||
|
|
@ -158,11 +155,10 @@ impl ReusableSecret {
|
||||||
|
|
||||||
/// Generate a new [`ReusableSecret`] with the supplied RNG.
|
/// Generate a new [`ReusableSecret`] with the supplied RNG.
|
||||||
pub fn random_from_rng<T: RngCore + CryptoRng>(mut csprng: T) -> Self {
|
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];
|
let mut bytes = [0u8; 32];
|
||||||
|
|
||||||
csprng.fill_bytes(&mut bytes);
|
csprng.fill_bytes(&mut bytes);
|
||||||
|
ReusableSecret(bytes)
|
||||||
ReusableSecret(Scalar::from_bits_clamped(bytes))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate a new [`ReusableSecret`].
|
/// Generate a new [`ReusableSecret`].
|
||||||
|
|
@ -176,7 +172,7 @@ impl ReusableSecret {
|
||||||
impl<'a> From<&'a ReusableSecret> for PublicKey {
|
impl<'a> From<&'a ReusableSecret> for PublicKey {
|
||||||
/// Given an x25519 [`ReusableSecret`] key, compute its corresponding [`PublicKey`].
|
/// Given an x25519 [`ReusableSecret`] key, compute its corresponding [`PublicKey`].
|
||||||
fn from(secret: &'a ReusableSecret) -> 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", derive(Zeroize))]
|
||||||
#[cfg_attr(feature = "zeroize", zeroize(drop))]
|
#[cfg_attr(feature = "zeroize", zeroize(drop))]
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct StaticSecret(
|
pub struct StaticSecret([u8; 32]);
|
||||||
#[cfg_attr(feature = "serde", serde(with = "AllowUnreducedScalarBytes"))] pub(crate) Scalar,
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(feature = "static_secrets")]
|
#[cfg(feature = "static_secrets")]
|
||||||
impl StaticSecret {
|
impl StaticSecret {
|
||||||
/// Perform a Diffie-Hellman key agreement between `self` and
|
/// Perform a Diffie-Hellman key agreement between `self` and
|
||||||
/// `their_public` key to produce a `SharedSecret`.
|
/// `their_public` key to produce a `SharedSecret`.
|
||||||
pub fn diffie_hellman(&self, their_public: &PublicKey) -> 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.
|
/// Generate a new [`StaticSecret`] with the supplied RNG.
|
||||||
|
|
@ -222,11 +216,10 @@ impl StaticSecret {
|
||||||
|
|
||||||
/// Generate a new [`StaticSecret`] with the supplied RNG.
|
/// Generate a new [`StaticSecret`] with the supplied RNG.
|
||||||
pub fn random_from_rng<T: RngCore + CryptoRng>(mut csprng: T) -> Self {
|
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];
|
let mut bytes = [0u8; 32];
|
||||||
|
|
||||||
csprng.fill_bytes(&mut bytes);
|
csprng.fill_bytes(&mut bytes);
|
||||||
|
StaticSecret(bytes)
|
||||||
StaticSecret(Scalar::from_bits_clamped(bytes))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate a new [`StaticSecret`].
|
/// Generate a new [`StaticSecret`].
|
||||||
|
|
@ -238,13 +231,13 @@ impl StaticSecret {
|
||||||
/// Extract this key's bytes for serialization.
|
/// Extract this key's bytes for serialization.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn to_bytes(&self) -> [u8; 32] {
|
pub fn to_bytes(&self) -> [u8; 32] {
|
||||||
self.0.to_bytes()
|
self.0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// View this key as a byte array.
|
/// View this key as a byte array.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn as_bytes(&self) -> &[u8; 32] {
|
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 {
|
impl From<[u8; 32]> for StaticSecret {
|
||||||
/// Load a secret key from a byte array.
|
/// Load a secret key from a byte array.
|
||||||
fn from(bytes: [u8; 32]) -> StaticSecret {
|
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 {
|
impl<'a> From<&'a StaticSecret> for PublicKey {
|
||||||
/// Given an x25519 [`StaticSecret`] key, compute its corresponding [`PublicKey`].
|
/// Given an x25519 [`StaticSecret`] key, compute its corresponding [`PublicKey`].
|
||||||
fn from(secret: &'a StaticSecret) -> 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);
|
/// assert_eq!(alice_shared, bob_shared);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn x25519(k: [u8; 32], u: [u8; 32]) -> [u8; 32] {
|
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
|
/// 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] = [
|
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,
|
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use curve25519_dalek::{edwards::EdwardsPoint, scalar::Scalar};
|
use curve25519_dalek::edwards::EdwardsPoint;
|
||||||
|
|
||||||
use x25519_dalek::*;
|
use x25519_dalek::*;
|
||||||
|
|
||||||
|
|
@ -10,11 +10,9 @@ fn byte_basepoint_matches_edwards_scalar_mul() {
|
||||||
scalar_bytes[i] += 2;
|
scalar_bytes[i] += 2;
|
||||||
|
|
||||||
let result = x25519(scalar_bytes, X25519_BASEPOINT_BYTES);
|
let result = x25519(scalar_bytes, X25519_BASEPOINT_BYTES);
|
||||||
|
let expected = EdwardsPoint::mul_base_clamped(scalar_bytes)
|
||||||
let expected = {
|
.to_montgomery()
|
||||||
let scalar = Scalar::from_bits_clamped(scalar_bytes);
|
.to_bytes();
|
||||||
EdwardsPoint::mul_base(&scalar).to_montgomery().to_bytes()
|
|
||||||
};
|
|
||||||
|
|
||||||
assert_eq!(result, expected);
|
assert_eq!(result, expected);
|
||||||
}
|
}
|
||||||
|
|
@ -64,8 +62,7 @@ fn serde_bincode_static_secret_matches_from_bytes() {
|
||||||
use bincode;
|
use bincode;
|
||||||
|
|
||||||
let expected = StaticSecret::from([0x24; 32]);
|
let expected = StaticSecret::from([0x24; 32]);
|
||||||
let clamped_bytes = Scalar::from_bits_clamped([0x24; 32]).to_bytes();
|
let decoded: StaticSecret = bincode::deserialize(&[0x24; 32]).unwrap();
|
||||||
let decoded: StaticSecret = bincode::deserialize(&clamped_bytes).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(decoded.to_bytes(), expected.to_bytes());
|
assert_eq!(decoded.to_bytes(), expected.to_bytes());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue