use crate::curve25519_field::{Curve25519FieldCt, CurveSetupError};
use crate::{P_BYTES, UnsignedModularInt};
use modmath::ResidueCt;
use subtle::Choice;
pub const A24_BYTES: [u8; 32] =
crate::hx_le("000000000000000000000000000000000000000000000000000000000001db41");
pub const BASE_U_BYTES: [u8; 32] =
crate::hx_le("0000000000000000000000000000000000000000000000000000000000000009");
pub const BLINDING_MODULUS_BYTES: [u8; 64] = crate::hx_le(
"0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb64c66bee483cf65c231138c2de80a413a110920000d1b1d90bf4b83b29b3cec8",
);
const BLINDED_SCALAR_BYTES: usize = 68;
const BLINDED_BIT_COUNT: usize = BLINDED_SCALAR_BYTES * 8;
pub const fn clamp(mut k: [u8; 32]) -> [u8; 32] {
k[0] &= 248;
k[31] &= 127;
k[31] |= 64;
k
}
pub fn x25519_base<T>(k: &[u8; 32]) -> Result<[u8; 32], CurveSetupError>
where
T: UnsignedModularInt
+ Copy
+ PartialEq
+ modmath::WideMul
+ modmath::CiosMontMulCt
+ modmath::Parity
+ const_num_traits::CtIsZero
+ subtle::ConditionallySelectable
+ subtle::ConstantTimeLess,
for<'a> &'a T: const_num_traits::WrappingAdd<Output = T>
+ const_num_traits::WrappingSub<Output = T>
+ const_num_traits::ToBytes<Bytes = <T as const_num_traits::ToBytes>::Bytes>,
<T as const_num_traits::ToBytes>::Bytes: zeroize::Zeroize,
{
x25519::<T>(k, &BASE_U_BYTES)
}
#[inline(never)]
pub fn x25519<T>(k: &[u8; 32], u_in: &[u8; 32]) -> Result<[u8; 32], CurveSetupError>
where
T: UnsignedModularInt
+ Copy
+ PartialEq
+ modmath::WideMul
+ modmath::CiosMontMulCt
+ modmath::Parity
+ const_num_traits::CtIsZero
+ subtle::ConditionallySelectable
+ subtle::ConstantTimeLess,
for<'a> &'a T: const_num_traits::WrappingAdd<Output = T>
+ const_num_traits::WrappingSub<Output = T>
+ const_num_traits::ToBytes<Bytes = <T as const_num_traits::ToBytes>::Bytes>,
<T as const_num_traits::ToBytes>::Bytes: zeroize::Zeroize,
{
let field = Curve25519FieldCt::curve25519()?;
let k = zeroize::Zeroizing::new(clamp(*k));
let mut u_bytes = *u_in;
u_bytes[31] &= 0x7f;
let u = crate::from_le_bytes::<T>(&u_bytes);
let x1 = field.reduce(&u);
let a24 = field.reduce(&crate::from_le_bytes::<T>(&A24_BYTES));
let (x2, z2) = montgomery_ladder(
&field,
&x1,
&a24,
&*k,
255,
field.one(),
field.zero(),
x1.clone(),
field.one(),
);
let z2_inv = field.inv(&z2);
let result_res = field.mul(&x2, &z2_inv);
let result = zeroize::Zeroizing::new(field.into_raw(&result_res));
let bytes = crate::to_le_bytes_ct(&*result);
let bytes_slice: &[u8] = bytes.as_ref();
let mut out = [0u8; 32];
out.copy_from_slice(&bytes_slice[..32]);
Ok(out)
}
#[inline(never)]
pub fn x25519_blinded<T, R>(
rng: &mut R,
k: &[u8; 32],
u_in: &[u8; 32],
) -> Result<[u8; 32], CurveSetupError>
where
T: UnsignedModularInt
+ Copy
+ PartialEq
+ modmath::WideMul
+ modmath::CiosMontMulCt
+ modmath::Parity
+ const_num_traits::CtIsZero
+ subtle::ConditionallySelectable
+ subtle::ConstantTimeLess,
for<'a> &'a T: const_num_traits::WrappingAdd<Output = T>
+ const_num_traits::WrappingSub<Output = T>
+ const_num_traits::ToBytes<Bytes = <T as const_num_traits::ToBytes>::Bytes>,
<T as const_num_traits::ToBytes>::Bytes: zeroize::Zeroize,
R: rand_core::CryptoRng,
{
let r = rng.next_u32();
let mut lambda_bytes = zeroize::Zeroizing::new([0u8; 32]);
rng.fill_bytes(&mut *lambda_bytes);
x25519_blinded_from_parts::<T>(k, u_in, r, &lambda_bytes)
}
#[inline(never)]
pub(crate) fn x25519_blinded_from_parts<T>(
k: &[u8; 32],
u_in: &[u8; 32],
r: u32,
lambda_bytes: &[u8; 32],
) -> Result<[u8; 32], CurveSetupError>
where
T: UnsignedModularInt
+ Copy
+ PartialEq
+ modmath::WideMul
+ modmath::CiosMontMulCt
+ modmath::Parity
+ const_num_traits::CtIsZero
+ subtle::ConditionallySelectable
+ subtle::ConstantTimeLess,
for<'a> &'a T: const_num_traits::WrappingAdd<Output = T>
+ const_num_traits::WrappingSub<Output = T>
+ const_num_traits::ToBytes<Bytes = <T as const_num_traits::ToBytes>::Bytes>,
<T as const_num_traits::ToBytes>::Bytes: zeroize::Zeroize,
{
let field = Curve25519FieldCt::curve25519()?;
let k_clamped = zeroize::Zeroizing::new(clamp(*k));
let k_prime =
crate::blind_scalar::<BLINDED_SCALAR_BYTES>(&k_clamped, r, &BLINDING_MODULUS_BYTES);
let mut u_bytes = *u_in;
u_bytes[31] &= 0x7f;
let u = crate::from_le_bytes::<T>(&u_bytes);
let x1 = field.reduce(&u);
let a24 = field.reduce(&crate::from_le_bytes::<T>(&A24_BYTES));
let mut lambda_bytes = zeroize::Zeroizing::new(*lambda_bytes);
lambda_bytes[31] &= 0x7f;
let p_t = crate::from_le_bytes::<T>(&P_BYTES);
let mut lambda_t = zeroize::Zeroizing::new(crate::from_le_bytes::<T>(&*lambda_bytes));
let is_zero = subtle::ConstantTimeEq::ct_eq(&*lambda_t, &T::zero());
let is_p = subtle::ConstantTimeEq::ct_eq(&*lambda_t, &p_t);
*lambda_t = T::conditional_select(&*lambda_t, &T::one(), is_zero | is_p);
let lambda = field.reduce(&*lambda_t);
let lx1 = field.mul(&lambda, &x1);
let (x2, z2) = montgomery_ladder(
&field,
&x1,
&a24,
&*k_prime,
BLINDED_BIT_COUNT,
lambda.clone(),
field.zero(),
lx1,
lambda,
);
let z2_inv = field.inv(&z2);
let result_res = field.mul(&x2, &z2_inv);
let result = zeroize::Zeroizing::new(field.into_raw(&result_res));
let bytes = crate::to_le_bytes_ct(&*result);
let bytes_slice: &[u8] = bytes.as_ref();
let mut out = [0u8; 32];
out.copy_from_slice(&bytes_slice[..32]);
Ok(out)
}
pub fn x25519_base_blinded<T, R>(rng: &mut R, k: &[u8; 32]) -> Result<[u8; 32], CurveSetupError>
where
T: UnsignedModularInt
+ Copy
+ PartialEq
+ modmath::WideMul
+ modmath::CiosMontMulCt
+ modmath::Parity
+ const_num_traits::CtIsZero
+ subtle::ConditionallySelectable
+ subtle::ConstantTimeLess,
for<'a> &'a T: const_num_traits::WrappingAdd<Output = T>
+ const_num_traits::WrappingSub<Output = T>
+ const_num_traits::ToBytes<Bytes = <T as const_num_traits::ToBytes>::Bytes>,
<T as const_num_traits::ToBytes>::Bytes: zeroize::Zeroize,
R: rand_core::CryptoRng,
{
x25519_blinded::<T, R>(rng, k, &BASE_U_BYTES)
}
#[inline(never)]
#[allow(clippy::too_many_arguments)]
fn montgomery_ladder<'f, T>(
field: &'f Curve25519FieldCt<T>,
x1: &ResidueCt<'f, T>,
a24: &ResidueCt<'f, T>,
scalar: &[u8],
bit_count: usize,
mut x2: ResidueCt<'f, T>,
mut z2: ResidueCt<'f, T>,
mut x3: ResidueCt<'f, T>,
mut z3: ResidueCt<'f, T>,
) -> (ResidueCt<'f, T>, ResidueCt<'f, T>)
where
T: UnsignedModularInt
+ Copy
+ PartialEq
+ modmath::WideMul
+ modmath::CiosMontMulCt
+ modmath::Parity
+ subtle::ConditionallySelectable
+ subtle::ConstantTimeLess,
for<'a> &'a T:
const_num_traits::WrappingAdd<Output = T> + const_num_traits::WrappingSub<Output = T>,
{
let mut swap: u8 = 0;
for t in (0..bit_count).rev() {
let k_t = (scalar.get(t >> 3).copied().unwrap_or(0) >> (t & 7)) & 1;
swap ^= k_t;
let choice = Choice::from(swap);
ResidueCt::cswap(choice, &mut x2, &mut x3);
ResidueCt::cswap(choice, &mut z2, &mut z3);
swap = k_t;
let a = field.add(&x2, &z2);
let aa = field.mul(&a, &a);
let b = field.sub(&x2, &z2);
let bb = field.mul(&b, &b);
let e = field.sub(&aa, &bb);
let c = field.add(&x3, &z3);
let d = field.sub(&x3, &z3);
let da = field.mul(&d, &a);
let cb = field.mul(&c, &b);
let da_plus_cb = field.add(&da, &cb);
x3 = field.mul(&da_plus_cb, &da_plus_cb);
let da_minus_cb = field.sub(&da, &cb);
let dmc_sq = field.mul(&da_minus_cb, &da_minus_cb);
z3 = field.mul(x1, &dmc_sq);
x2 = field.mul(&aa, &bb);
let a24e = field.mul(a24, &e);
let aa_plus_a24e = field.add(&aa, &a24e);
z2 = field.mul(&e, &aa_plus_a24e);
}
let final_choice = Choice::from(swap);
ResidueCt::cswap(final_choice, &mut x2, &mut x3);
ResidueCt::cswap(final_choice, &mut z2, &mut z3);
(x2, z2)
}