This commit is contained in:
Neemek 2026-05-12 09:50:13 +02:00
parent 082adec86b
commit 7dc39dbb36
Signed by: neemek
GPG key ID: 84FFE4D7D40AB25E
6 changed files with 117 additions and 88 deletions

64
Cargo.lock generated
View file

@ -9,20 +9,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]] [[package]]
name = "cfg-if" name = "cmov"
version = "1.0.3" version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" checksum = "de0758edba32d61d1fd9f4d69491b47604b91ee2f7e6b33de7e54ca4ebe55dc3"
[[package]]
name = "cpubits"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ef0c543070d296ea414df2dd7625d1b24866ce206709d8a4a424f28377f5861"
[[package]] [[package]]
name = "crypto-bigint" name = "crypto-bigint"
version = "0.6.1" version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96272c2ff28b807e09250b180ad1fb7889a3258f7455759b5c3c58b719467130" checksum = "9fde2467e74147f492aebb834985186b2c74761927b8b9b3bd303bcb2e72199d"
dependencies = [ dependencies = [
"cpubits",
"ctutils",
"num-traits", "num-traits",
"rand_core", "rand_core",
"subtle", ]
[[package]]
name = "ctutils"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1005a6d4446f5120ef475ad3d2af2b30c49c2c9c6904258e3bb30219bebed5e4"
dependencies = [
"cmov",
] ]
[[package]] [[package]]
@ -33,23 +49,6 @@ dependencies = [
"zeroize", "zeroize",
] ]
[[package]]
name = "getrandom"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "libc"
version = "0.2.176"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174"
[[package]] [[package]]
name = "num-traits" name = "num-traits"
version = "0.2.19" version = "0.2.19"
@ -61,24 +60,9 @@ dependencies = [
[[package]] [[package]]
name = "rand_core" name = "rand_core"
version = "0.6.4" version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba"
dependencies = [
"getrandom",
]
[[package]]
name = "subtle"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "wasi"
version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]] [[package]]
name = "zeroize" name = "zeroize"

View file

@ -4,11 +4,11 @@ version = "0.1.0"
edition = "2024" edition = "2024"
[dependencies] [dependencies]
crypto-bigint = { version = "0.6.1" } crypto-bigint = { version = "0.7.1" }
zeroize = { version = "1.8.2", optional = true } zeroize = { version = "1.8.2", optional = true }
[features] [features]
rand = ["crypto-bigint/rand"] rand = ["crypto-bigint/rand_core"]
edwards = [] edwards = []
zeroize = ["dep:zeroize"] zeroize = ["dep:zeroize"]

View file

@ -6,13 +6,12 @@ pub mod edwards;
pub mod montgomery; pub mod montgomery;
pub mod traits; pub mod traits;
use crypto_bigint::subtle::{Choice, ConstantTimeEq}; pub use crypto_bigint::{Encoding, NonZero, U256};
pub use crypto_bigint::{ConstChoice, Encoding, NonZero, U256};
#[cfg(feature = "rand")]
use crypto_bigint::{rand_core::OsRng, Random};
use crate::traits::{Curve, Point}; use crate::traits::{Curve, Point};
use crypto_bigint::{Choice, CtEq, EncodedUint};
#[cfg(feature = "rand")]
use crypto_bigint::{Random, rand_core::OsRng};
pub(crate) use montgomery::*; pub(crate) use montgomery::*;
#[derive(Debug, Clone, Copy, Eq, PartialEq)] #[derive(Debug, Clone, Copy, Eq, PartialEq)]
@ -29,13 +28,13 @@ impl From<&Secret> for Public {
impl From<[u8; 32]> for Public { impl From<[u8; 32]> for Public {
fn from(bytes: [u8; 32]) -> Public { fn from(bytes: [u8; 32]) -> Public {
Public(U256::from_le_bytes(bytes)) Public(U256::from_le_bytes(EncodedUint::from(bytes)))
} }
} }
impl Public { impl Public {
pub fn as_bytes(&self) -> [u8; 32] { pub fn as_bytes(&self) -> [u8; 32] {
self.0.to_le_bytes() self.0.to_le_bytes().into()
} }
} }
@ -53,7 +52,7 @@ impl Secret {
let point = CURVE_25519.point(public.0); let point = CURVE_25519.point(public.0);
let p = point * self.0; let p = point * self.0;
SharedSecret(p.get_x().to_le_bytes()) SharedSecret(p.get_x().to_le_bytes().into())
} }
} }
@ -72,7 +71,7 @@ impl SharedSecret {
} }
} }
impl ConstantTimeEq for SharedSecret { impl CtEq for SharedSecret {
fn ct_eq(&self, other: &Self) -> Choice { fn ct_eq(&self, other: &Self) -> Choice {
self.0.ct_eq(&other.0) self.0.ct_eq(&other.0)
} }
@ -109,7 +108,7 @@ mod tests {
let x_k = let x_k =
U256::from_be_hex("0933dc6ed7122bdf2a5f1b0516e218b743868769778787cfa1ac0ae2dfa89891"); U256::from_be_hex("0933dc6ed7122bdf2a5f1b0516e218b743868769778787cfa1ac0ae2dfa89891");
assert_eq!(x.0, x_k.to_le_bytes()); assert_eq!(x.0, <[u8; 32]>::from(x_k.to_le_bytes()));
} }
#[test] #[test]
@ -125,6 +124,9 @@ mod tests {
let sa = a.diffie_hellman(&pb); let sa = a.diffie_hellman(&pb);
let sb = b.diffie_hellman(&pa); let sb = b.diffie_hellman(&pa);
assert!(bool::from(sa.ct_eq(&sb))); assert!(
bool::from(sa.ct_eq(&sb)),
"Didn't compute same shared secret"
);
} }
} }

View file

@ -1,15 +1,17 @@
#[macro_export] #[macro_export]
macro_rules! ladder_impl { macro_rules! ladder_impl {
($point:ident, $swap_fn:ident) => { ($point:ident, $swap_fn:ident) => {
impl<N> $point<N> impl<N: BitOps> $point<N>
where where
N: AddMod N: AddMod<Output = N>
+ SubMod + SubMod<Output = N>
+ MulMod<Output = N> + MulMod<Output = N>
+ InvMod<Output = N> + InvertMod<Output = N>
+ Copy + Copy
+ Integer + Integer
+ Div<Output = N>, + Div<Output = N>,
N: std::convert::From<u8>,
{ {
pub fn ladder(&self, rhs: &N) -> $point<N> { pub fn ladder(&self, rhs: &N) -> $point<N> {
let mut x_0 = *self; let mut x_0 = *self;

View file

@ -1,6 +1,7 @@
use crate::traits::{Curve, Point}; use crate::traits::{Curve, Point};
use crypto_bigint::{AddMod, Encoding, NonZero, SubMod, U256}; use crypto_bigint::{AddMod, EncodedUint, Encoding, InvertMod, NonZero, SubMod, U256};
use crypto_bigint::{Integer, InvMod, MulMod}; use crypto_bigint::{BitOps, Uint};
use crypto_bigint::{Integer, MulMod};
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use std::ops::{BitXor, Div, Mul}; use std::ops::{BitXor, Div, Mul};
@ -8,18 +9,23 @@ use std::ops::{BitXor, Div, Mul};
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub struct MontgomeryCurve<N> pub struct MontgomeryCurve<N>
where where
N: AddMod + SubMod + MulMod + InvMod + Integer + Copy, N: AddMod + SubMod + MulMod + InvertMod + Integer + Copy,
{ {
pub a: N, pub a: N,
pub b: N, pub b: N,
pub p: N, pub p: NonZero<N>,
} }
impl<N> MontgomeryCurve<N> impl<N> MontgomeryCurve<N>
where where
N: AddMod + SubMod + MulMod + InvMod + Copy + Integer, N: AddMod<Output = N>
+ SubMod<Output = N>
+ MulMod<Output = N>
+ InvertMod<Output = N>
+ Copy
+ Integer,
{ {
pub const fn new(a: N, b: N, p: N) -> MontgomeryCurve<N> { pub const fn new(a: N, b: N, p: NonZero<N>) -> MontgomeryCurve<N> {
MontgomeryCurve { a, b, p } MontgomeryCurve { a, b, p }
} }
@ -36,7 +42,15 @@ where
impl<N> Curve<MontgomeryPoint<N>, N> for MontgomeryCurve<N> impl<N> Curve<MontgomeryPoint<N>, N> for MontgomeryCurve<N>
where where
N: AddMod + SubMod + MulMod + InvMod<Output = N> + Copy + Integer + Div<Output = N>, N: AddMod<Output = N>
+ SubMod<Output = N>
+ MulMod<Output = N>
+ InvertMod<Output = N>
+ Copy
+ Integer
+ Div<Output = N>
+ std::convert::From<u8>
+ BitOps,
{ {
/// Get the point at an x-coordinate with parameters from this curve. /// Get the point at an x-coordinate with parameters from this curve.
fn point(self, x: N) -> MontgomeryPoint<N> { fn point(self, x: N) -> MontgomeryPoint<N> {
@ -53,20 +67,27 @@ where
} }
pub fn clamp_u256(x: U256) -> U256 { pub fn clamp_u256(x: U256) -> U256 {
let mut bytes: [u8; 32] = x.to_le_bytes(); let mut bytes: [u8; 32] = x.to_le_bytes().into();
// clamp value // clamp value
bytes[0] &= 248; bytes[0] &= 248;
bytes[31] &= 127; bytes[31] &= 127;
bytes[31] |= 64; bytes[31] |= 64;
U256::from_le_bytes(bytes) U256::from_le_bytes(EncodedUint::from(bytes))
} }
#[derive(Copy, Debug, Clone)] #[derive(Copy, Debug, Clone)]
pub struct MontgomeryPoint<N> pub struct MontgomeryPoint<N>
where where
N: AddMod + SubMod + Div<Output = N> + MulMod<Output = N> + InvMod<Output = N> + Copy + Integer, N: AddMod<Output = N>
+ SubMod<Output = N>
+ Div<Output = N>
+ MulMod<Output = N>
+ InvertMod<Output = N>
+ Copy
+ Integer
+ From<u8>,
{ {
pub x: N, pub x: N,
pub z: N, pub z: N,
@ -76,24 +97,23 @@ where
impl<N> Point<N> for MontgomeryPoint<N> impl<N> Point<N> for MontgomeryPoint<N>
where where
N: AddMod N: AddMod<Output = N>
+ SubMod + SubMod<Output = N>
+ Div<Output = N> + Div<Output = N>
+ MulMod<Output = N> + MulMod<Output = N>
+ InvMod<Output = N> + InvertMod<Output = N>
+ Copy + Copy
+ Integer + Integer
+ From<u8>, + From<u8>
+ BitOps,
{ {
fn get_x(self) -> N { fn get_x(self) -> N {
self.x.mul_mod( self.x
&self.z.inv_mod(&self.curve.p).unwrap(), .mul_mod(&self.z.invert_mod(&self.curve.p).unwrap(), &self.curve.p)
&NonZero::new(self.curve.p).unwrap(),
)
} }
fn add(&self, rhs: &Self, neg: &Self) -> Self { fn add(&self, rhs: &Self, neg: &Self) -> Self {
let p = &NonZero::new(self.curve.p).unwrap(); let p = &self.curve.p;
let mut v_0 = self.x.add_mod(&self.z, p); // 1: V_0 = X_P + Z_P let mut v_0 = self.x.add_mod(&self.z, p); // 1: V_0 = X_P + Z_P
let mut v_1 = rhs.x.sub_mod(&rhs.z, p); // 2: V_1 = X_Q - Z_Q let mut v_1 = rhs.x.sub_mod(&rhs.z, p); // 2: V_1 = X_Q - Z_Q
@ -120,7 +140,7 @@ where
} }
fn double(&self) -> MontgomeryPoint<N> { fn double(&self) -> MontgomeryPoint<N> {
let p = &NonZero::new(self.curve.p).unwrap(); let p = &self.curve.p;
let mut v_1 = self.x.add_mod(&self.z, p); let mut v_1 = self.x.add_mod(&self.z, p);
v_1 = v_1.mul_mod(&v_1, p); v_1 = v_1.mul_mod(&v_1, p);
@ -162,7 +182,16 @@ ladder_impl!(MontgomeryPoint, swap_montgomery);
fn swap_montgomery<N>(a: &mut MontgomeryPoint<N>, b: &mut MontgomeryPoint<N>, c: u8, max: N) fn swap_montgomery<N>(a: &mut MontgomeryPoint<N>, b: &mut MontgomeryPoint<N>, c: u8, max: N)
where where
N: BitXor + Copy + Mul<Output = N> + From<u8> + Div<Output = N> + Integer + InvMod<Output = N>, N: BitXor
+ Copy
+ Mul<Output = N>
+ From<u8>
+ Div<Output = N>
+ Integer
+ InvertMod<Output = N>
+ AddMod<Output = N>
+ MulMod<Output = N>
+ SubMod<Output = N>,
{ {
let m = max * N::from(c); let m = max * N::from(c);
@ -178,14 +207,15 @@ where
impl<N> Display for MontgomeryPoint<N> impl<N> Display for MontgomeryPoint<N>
where where
N: AddMod N: AddMod<Output = N>
+ SubMod + SubMod<Output = N>
+ MulMod + MulMod<Output = N>
+ InvMod<Output = N> + InvertMod<Output = N>
+ Copy + Copy
+ Integer + Integer
+ Display + Display
+ std::ops::Div<Output = N>, + Div<Output = N>
+ From<u8>,
{ {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!("({} : {})", self.x, self.z)) f.write_fmt(format_args!("({} : {})", self.x, self.z))
@ -194,7 +224,15 @@ where
impl<N> Mul<N> for MontgomeryPoint<N> impl<N> Mul<N> for MontgomeryPoint<N>
where where
N: AddMod + SubMod + MulMod + InvMod<Output = N> + Copy + Integer + std::ops::Div<Output = N>, N: AddMod<Output = N>
+ SubMod<Output = N>
+ MulMod<Output = N>
+ InvertMod<Output = N>
+ Copy
+ Integer
+ Div<Output = N>
+ std::convert::From<u8>
+ BitOps,
{ {
type Output = MontgomeryPoint<N>; type Output = MontgomeryPoint<N>;
@ -211,7 +249,10 @@ where
pub const CURVE_25519: MontgomeryCurve<U256> = MontgomeryCurve::new( pub const CURVE_25519: MontgomeryCurve<U256> = MontgomeryCurve::new(
U256::from_u32(486662u32), U256::from_u32(486662u32),
U256::ONE, U256::ONE,
U256::ONE.shl(255).sub_mod(&U256::from_u8(19), &U256::MAX), NonZero::<Uint<4>>::new_unwrap(U256::ONE.shl(255).sub_mod(
&U256::from_u8(19),
&NonZero::<Uint<4>>::new_unwrap(U256::MAX),
)),
); );
#[cfg(test)] #[cfg(test)]

View file

@ -1,9 +1,9 @@
use crypto_bigint::{AddMod, InvMod, MulMod, SubMod}; use crypto_bigint::{AddMod, InvertMod, MulMod, SubMod};
pub trait Curve<T, N> pub trait Curve<T, N>
where where
T: Point<N>, T: Point<N>,
N: AddMod<Output = N> + SubMod<Output = N> + MulMod<Output = N> + InvMod<Output = N> + Copy, N: AddMod<Output = N> + SubMod<Output = N> + MulMod<Output = N> + InvertMod<Output = N> + Copy,
{ {
fn point(self, x: N) -> T; fn point(self, x: N) -> T;
fn generator(self) -> T; fn generator(self) -> T;
@ -11,7 +11,7 @@ where
pub trait Point<N> pub trait Point<N>
where where
N: AddMod + SubMod + MulMod + InvMod + Copy, N: AddMod<Output = N> + SubMod<Output = N> + MulMod<Output = N> + InvertMod<Output = N> + Copy,
{ {
fn get_x(self) -> N; fn get_x(self) -> N;