use crate::errors::UnknownCryptoError;
use crate::generics::GenerateSecret;
use crate::generics::{ByteArrayData, TypeSpec};
use core::ops::{Add, Mul, Sub};
pub use crate::generics::Public;
pub use crate::generics::Secret;
#[cfg(feature = "safe_api")]
use crate::generics::sealed::Data;
use crate::generics::sealed::Sealed;
use fiat_crypto::curve25519_64 as fiat_curve25519_u64;
use fiat_curve25519_u64::{
fiat_25519_add, fiat_25519_carry, fiat_25519_carry_mul, fiat_25519_carry_scmul_121666,
fiat_25519_carry_square, fiat_25519_loose_field_element, fiat_25519_relax, fiat_25519_sub,
fiat_25519_tight_field_element,
};
pub const PUBLIC_KEY_SIZE: usize = 32;
pub const PRIVATE_KEY_SIZE: usize = 32;
pub const SHARED_KEY_SIZE: usize = 32;
pub(crate) const BASEPOINT: FieldElement = FieldElement::from_bytes(&[
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,
]);
const LOW_ORDER_POINT_RESULT: [u8; 32] = [0u8; 32];
#[derive(Clone, Copy)]
pub(crate) struct FieldElement(fiat_25519_tight_field_element);
impl core::fmt::Debug for FieldElement {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "FieldElement({:?})", self.0.0)
}
}
impl Mul for FieldElement {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
let mut ret = fiat_25519_tight_field_element([0u64; 5]);
let mut self_relaxed = fiat_25519_loose_field_element([0u64; 5]);
let mut rhs_relaxed = fiat_25519_loose_field_element([0u64; 5]);
fiat_25519_relax(&mut self_relaxed, &self.0);
fiat_25519_relax(&mut rhs_relaxed, &rhs.0);
fiat_25519_carry_mul(&mut ret, &self_relaxed, &rhs_relaxed);
Self(ret)
}
}
impl Add for FieldElement {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
let mut ret = fiat_25519_tight_field_element([0u64; 5]);
let mut ret_add = fiat_25519_loose_field_element([0u64; 5]);
fiat_25519_add(&mut ret_add, &self.0, &rhs.0);
fiat_25519_carry(&mut ret, &ret_add);
Self(ret)
}
}
impl Sub for FieldElement {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
let mut ret = fiat_25519_tight_field_element([0u64; 5]);
let mut ret_sub = fiat_25519_loose_field_element([0u64; 5]);
fiat_25519_sub(&mut ret_sub, &self.0, &rhs.0);
fiat_25519_carry(&mut ret, &ret_sub);
Self(ret)
}
}
impl FieldElement {
fn zero() -> Self {
Self(fiat_25519_tight_field_element([
0u64, 0u64, 0u64, 0u64, 0u64,
]))
}
fn one() -> Self {
Self(fiat_25519_tight_field_element([
1u64, 0u64, 0u64, 0u64, 0u64,
]))
}
pub(crate) fn as_bytes(&self) -> [u8; 32] {
use fiat_curve25519_u64::fiat_25519_to_bytes;
let mut ret = [0u8; 32];
fiat_25519_to_bytes(&mut ret, &self.0);
ret
}
const fn from_bytes(bytes: &[u8; 32]) -> Self {
use fiat_curve25519_u64::fiat_25519_from_bytes;
let mut temp = [0u8; 32];
temp.copy_from_slice(bytes);
temp[31] &= 127u8;
let mut ret = fiat_25519_tight_field_element([0u64; 5]);
fiat_25519_from_bytes(&mut ret, &temp);
Self(ret)
}
fn conditional_swap(swap: u8, a: &mut Self, b: &mut Self) {
use fiat_curve25519_u64::fiat_25519_selectznz;
debug_assert!(swap == 1 || swap == 0);
let tmp_a = *a;
let tmp_b = *b;
fiat_25519_selectznz(&mut a.0.0, swap, &tmp_a.0.0, &tmp_b.0.0);
fiat_25519_selectznz(&mut b.0.0, swap, &tmp_b.0.0, &tmp_a.0.0);
}
fn square(&self) -> Self {
let mut self_relaxed = fiat_25519_loose_field_element([0u64; 5]);
let mut ret = fiat_25519_tight_field_element([0u64; 5]);
fiat_25519_relax(&mut self_relaxed, &self.0);
fiat_25519_carry_square(&mut ret, &self_relaxed);
Self(ret)
}
fn mul_121666(&self) -> Self {
let mut self_relaxed = fiat_25519_loose_field_element([0u64; 5]);
let mut ret = fiat_25519_tight_field_element([0u64; 5]);
fiat_25519_relax(&mut self_relaxed, &self.0);
fiat_25519_carry_scmul_121666(&mut ret, &self_relaxed);
Self(ret)
}
fn invert(&mut self) {
let mut t0 = self.square();
let mut t1 = t0.square();
t1 = t1.square();
t1 = *self * t1;
t0 = t0 * t1;
let mut t2 = t0.square();
t1 = t1 * t2;
t2 = t1.square();
for _ in 1..5 {
t2 = t2.square();
}
t1 = t2 * t1;
t2 = t1.square();
for _ in 1..10 {
t2 = t2.square();
}
t2 = t2 * t1;
let mut t3 = t2.square();
for _ in 1..20 {
t3 = t3.square();
}
t2 = t3 * t2;
t2 = t2.square();
for _ in 1..10 {
t2 = t2.square();
}
t1 = t2 * t1;
t2 = t1.square();
for _ in 1..50 {
t2 = t2.square();
}
t2 = t2 * t1;
t3 = t2.square();
for _ in 1..100 {
t3 = t3.square();
}
t2 = t3 * t2;
t2 = t2.square();
for _ in 1..50 {
t2 = t2.square();
}
t1 = t2 * t1;
t1 = t1.square();
for _ in 1..5 {
t1 = t1.square();
}
*self = t1 * t0;
}
}
pub(crate) fn mont_ladder(scalar: &[u8; PRIVATE_KEY_SIZE], point: FieldElement) -> FieldElement {
debug_assert_eq!(
point.as_bytes()[31] & 0x80,
0,
"FieldElement missing highbit mask!"
);
debug_assert!(X25519PrivateKey::is_clamped(scalar));
let x1 = point;
let mut x2 = FieldElement::one();
let mut x3 = x1;
let mut z3 = FieldElement::one();
let mut z2 = FieldElement::zero();
let mut tmp0: FieldElement;
let mut tmp1: FieldElement;
let mut swap: u8 = 0;
for idx in (0..=254).rev() {
let bit = (scalar[idx >> 3] >> (idx & 7)) & 1;
swap ^= bit;
FieldElement::conditional_swap(swap, &mut x2, &mut x3);
FieldElement::conditional_swap(swap, &mut z2, &mut z3);
swap = bit;
tmp0 = x3 - z3;
tmp1 = x2 - z2;
x2 = x2 + z2;
z2 = x3 + z3;
z3 = tmp0 * x2;
z2 = z2 * tmp1;
tmp0 = tmp1.square();
tmp1 = x2.square();
x3 = z3 + z2;
z2 = z3 - z2;
x2 = tmp1 * tmp0;
tmp1 = tmp1 - tmp0;
z2 = z2.square();
z3 = tmp1.mul_121666();
x3 = x3.square();
tmp0 = tmp0 + z3;
z3 = x1 * z2;
z2 = tmp1 * tmp0;
}
FieldElement::conditional_swap(swap, &mut x2, &mut x3);
FieldElement::conditional_swap(swap, &mut z2, &mut z3);
z2.invert();
x2 = x2 * z2;
x2
}
#[derive(Debug)]
pub struct X25519PrivateKey {}
impl Sealed for X25519PrivateKey {}
impl X25519PrivateKey {
pub(crate) const fn clamp_mut(k: &mut [u8; PRIVATE_KEY_SIZE]) {
k[0] &= 248;
k[31] &= 127;
k[31] |= 64;
}
const fn clamp(k: &[u8; PRIVATE_KEY_SIZE]) -> [u8; PRIVATE_KEY_SIZE] {
let mut scalar = *k;
Self::clamp_mut(&mut scalar);
scalar
}
pub(crate) fn is_clamped(scalar: &[u8; PRIVATE_KEY_SIZE]) -> bool {
use subtle::ConstantTimeEq;
((scalar[0] & 7).ct_eq(&0) & ((scalar[31] & 0xC0).ct_eq(&0x40))).into()
}
}
impl TypeSpec for X25519PrivateKey {
const NAME: &'static str = stringify!(PrivateKey);
type TypeData = ByteArrayData<PRIVATE_KEY_SIZE>;
}
impl From<[u8; PRIVATE_KEY_SIZE]> for Secret<X25519PrivateKey> {
fn from(value: [u8; PRIVATE_KEY_SIZE]) -> Self {
Self::from_data(<X25519PrivateKey as TypeSpec>::TypeData::from(value))
}
}
impl GenerateSecret for X25519PrivateKey {
#[cfg(feature = "safe_api")]
#[cfg_attr(docsrs, doc(cfg(feature = "safe_api")))]
fn generate() -> Result<Secret<X25519PrivateKey>, UnknownCryptoError> {
let mut data = Self::TypeData::new(PRIVATE_KEY_SIZE)?;
crate::util::secure_rand_bytes(&mut data.bytes)?;
Ok(Secret::from_data(data))
}
}
pub type PrivateKey = Secret<X25519PrivateKey>;
#[derive(Debug, Clone, Copy)]
pub struct X25519PublicKey {}
impl Sealed for X25519PublicKey {}
impl TypeSpec for X25519PublicKey {
const NAME: &'static str = stringify!(PublicKey);
type TypeData = ByteArrayData<PUBLIC_KEY_SIZE>;
fn vartime_partial_eq(lhs: &Self::TypeData, rhs: &[u8]) -> bool {
debug_assert_eq!(lhs.bytes.len(), PUBLIC_KEY_SIZE);
if lhs.bytes.len() != rhs.len() {
return false;
}
lhs.bytes[..31] == rhs[..31] && (lhs.bytes[31] & 127u8) == (rhs[31] & 127u8)
}
}
impl From<[u8; PUBLIC_KEY_SIZE]> for Public<X25519PublicKey> {
fn from(value: [u8; PUBLIC_KEY_SIZE]) -> Self {
Self::from_data(<X25519PublicKey as TypeSpec>::TypeData::from(value))
}
}
impl TryFrom<&PrivateKey> for Public<X25519PublicKey> {
type Error = UnknownCryptoError;
fn try_from(private_key: &PrivateKey) -> Result<Self, Self::Error> {
Ok(PublicKey::from(
mont_ladder(&X25519PrivateKey::clamp(&private_key.data.bytes), BASEPOINT).as_bytes(),
))
}
}
impl From<&Public<X25519PublicKey>> for FieldElement {
fn from(value: &Public<X25519PublicKey>) -> Self {
FieldElement::from_bytes(&value.data.bytes)
}
}
impl From<&FieldElement> for Public<X25519PublicKey> {
fn from(value: &FieldElement) -> Self {
let bytes = value.as_bytes();
debug_assert_eq!(
bytes[31] & 0x80,
0,
"parsed FieldElement for PublicKey with missing highbit mask!"
);
Self::from(bytes)
}
}
pub type PublicKey = Public<X25519PublicKey>;
#[derive(Debug)]
pub struct X25519SharedKey {}
impl Sealed for X25519SharedKey {}
impl TypeSpec for X25519SharedKey {
const NAME: &'static str = stringify!(SharedKey);
type TypeData = ByteArrayData<SHARED_KEY_SIZE>;
}
impl From<[u8; SHARED_KEY_SIZE]> for Secret<X25519SharedKey> {
fn from(value: [u8; SHARED_KEY_SIZE]) -> Self {
Self::from_data(<X25519SharedKey as TypeSpec>::TypeData::from(value))
}
}
impl From<FieldElement> for Secret<X25519SharedKey> {
fn from(value: FieldElement) -> Self {
let bytes = value.as_bytes();
debug_assert_eq!(
bytes[31] & 0x80,
0,
"parsed FieldElement for SharedSecret with missing highbit mask!"
);
Self::from(bytes)
}
}
pub type SharedKey = Secret<X25519SharedKey>;
pub fn key_agreement(
private_key: &PrivateKey,
public_key: &PublicKey,
) -> Result<SharedKey, UnknownCryptoError> {
let u_coord = FieldElement::from(public_key);
let shared_key = SharedKey::from(mont_ladder(
&X25519PrivateKey::clamp(&private_key.data.bytes),
u_coord,
));
debug_assert_eq!(shared_key.data.bytes[31] & 0b1000_0000u8, 0u8);
if shared_key == &LOW_ORDER_POINT_RESULT {
return Err(UnknownCryptoError);
}
Ok(shared_key)
}
#[cfg(test)]
mod public {
use crate::hazardous::ecc::x25519::{
BASEPOINT, FieldElement, PRIVATE_KEY_SIZE, PUBLIC_KEY_SIZE, PrivateKey, PublicKey,
SHARED_KEY_SIZE, SharedKey, X25519PrivateKey, X25519PublicKey, X25519SharedKey,
key_agreement,
};
const 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,
];
#[test]
fn correctness_clamping() {
let mut preclamp = [0u8; PRIVATE_KEY_SIZE];
for b0 in 0u8..=u8::MAX {
preclamp[0] = b0;
for b31 in 0u8..=u8::MAX {
preclamp[31] = b31;
if X25519PrivateKey::is_clamped(&preclamp) {
assert_eq!(&preclamp, &X25519PrivateKey::clamp(&preclamp));
}
assert!(X25519PrivateKey::is_clamped(&X25519PrivateKey::clamp(
&preclamp
)));
}
}
}
#[test]
fn test_shared_secret() {
use crate::test_framework::newtypes::secret::SecretNewtype;
SecretNewtype::test_no_generate::<SHARED_KEY_SIZE, SHARED_KEY_SIZE, X25519SharedKey>();
assert_ne!(
SharedKey::from([0u8; SHARED_KEY_SIZE]),
SharedKey::from([1u8; SHARED_KEY_SIZE])
);
}
#[test]
fn test_private_key() {
use crate::test_framework::newtypes::secret::SecretNewtype;
SecretNewtype::test_with_generate::<
PRIVATE_KEY_SIZE,
PRIVATE_KEY_SIZE,
PRIVATE_KEY_SIZE,
X25519PrivateKey,
>();
assert_ne!(
PrivateKey::from([0u8; PRIVATE_KEY_SIZE]),
PrivateKey::from([1u8; PRIVATE_KEY_SIZE])
);
}
#[test]
fn test_public_key() {
use crate::test_framework::newtypes::public::PublicNewtype;
PublicNewtype::test_no_generate::<PUBLIC_KEY_SIZE, PUBLIC_KEY_SIZE, X25519PublicKey>();
assert_ne!(
PublicKey::from([0u8; PUBLIC_KEY_SIZE]),
PublicKey::from([1u8; PUBLIC_KEY_SIZE])
);
#[cfg(feature = "serde")]
PublicNewtype::test_serialization::<PUBLIC_KEY_SIZE, X25519PublicKey>();
}
#[test]
#[cfg(feature = "safe_api")]
fn test_field_element_debug_impl() {
let secret = format!("{:?}", [1u8; 32].as_ref());
let test_debug_contents =
format!("{:?}", PrivateKey::try_from([1u8; 32].as_slice()).unwrap());
assert!(!test_debug_contents.contains(&secret));
}
#[test]
#[cfg(feature = "safe_api")]
fn test_privatekey_debug_impl() {
use super::FieldElement;
let value = format!("{:?}", [1u64, 0u64, 0u64, 0u64, 0u64,].as_ref());
let test_debug_contents = format!("{:?}", FieldElement::one());
assert!(test_debug_contents.contains(&value));
}
#[test]
fn test_public_key_ignores_highbit() {
let u = [0u8; 32];
let mut msb_zero = u;
msb_zero[31] &= 127u8;
let mut msb_one = u;
msb_one[31] |= 128u8;
let pk_msb_zero = PublicKey::from(msb_zero);
assert_eq!(pk_msb_zero, &msb_zero);
assert_eq!(pk_msb_zero, &msb_one);
let pk_msb_one = PublicKey::from(msb_one);
assert_eq!(pk_msb_one, &msb_zero);
assert_eq!(pk_msb_one, &msb_one);
assert_eq!(pk_msb_zero, pk_msb_one.as_ref());
assert_ne!(pk_msb_one.as_ref(), pk_msb_zero.as_ref());
}
#[test]
#[cfg(feature = "safe_api")]
fn test_highbit_ignored() {
let k = PrivateKey::generate().unwrap();
let mut u = [0u8; 32];
crate::util::secure_rand_bytes(&mut u).unwrap();
debug_assert_ne!(u[31] & 127u8, (u[31] & 127u8) | 128u8);
let mut u_msb_zero = u;
u_msb_zero[31] &= 127u8;
let mut u_msb_one = u;
u_msb_one[31] |= 128u8;
let msb_zero = key_agreement(&k, &PublicKey::from(u_msb_zero)).unwrap();
let msb_one = key_agreement(&k, &PublicKey::from(u_msb_one)).unwrap();
assert_eq!(msb_zero, msb_one);
}
#[test]
#[cfg(feature = "safe_api")]
fn test_scalar_clamp() {
let private = PrivateKey::generate().unwrap();
assert_eq!(X25519PrivateKey::clamp(&private.data.bytes)[0] & !248, 0);
assert_eq!(X25519PrivateKey::clamp(&private.data.bytes)[31] & !127, 0);
assert_eq!(X25519PrivateKey::clamp(&private.data.bytes)[31] & 64, 64);
let private = PrivateKey::try_from(&[0xFFu8; PRIVATE_KEY_SIZE]).unwrap();
assert_eq!(X25519PrivateKey::clamp(&private.data.bytes)[0] & !248, 0);
assert_eq!(X25519PrivateKey::clamp(&private.data.bytes)[31] & !127, 0);
assert_eq!(X25519PrivateKey::clamp(&private.data.bytes)[31] & 64, 64);
let private = PrivateKey::from([0xFFu8; PRIVATE_KEY_SIZE]);
assert_eq!(X25519PrivateKey::clamp(&private.data.bytes)[0] & !248, 0);
assert_eq!(X25519PrivateKey::clamp(&private.data.bytes)[31] & !127, 0);
assert_eq!(X25519PrivateKey::clamp(&private.data.bytes)[31] & 64, 64);
}
#[test]
fn test_rfc_section_5() {
let mut scalar = [0u8; 32];
let mut point = [0u8; 32];
let mut expected = SharedKey::from([0u8; 32]);
hex::decode_to_slice(
"a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4",
&mut scalar,
)
.unwrap();
hex::decode_to_slice(
"e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c",
&mut point,
)
.unwrap();
hex::decode_to_slice(
"c3da55379de9c6908e94ea4df28d084f32eccf03491c71f754b4075577a28552",
expected.data.as_mut(),
)
.unwrap();
let actual = key_agreement(&PrivateKey::from(scalar), &PublicKey::from(point)).unwrap();
assert_eq!(actual, expected);
hex::decode_to_slice(
"4b66e9d4d1b4673c5ad22691957d6af5c11b6421e0ea01d42ca4169e7918ba0d",
&mut scalar,
)
.unwrap();
hex::decode_to_slice(
"e5210f12786811d3f4b7959d0538ae2c31dbe7106fc03c3efc4cd549c715a493",
&mut point,
)
.unwrap();
hex::decode_to_slice(
"95cbde9476e8907d7aade45cb4b873f88b595a68799fa152e6f8f7647aac7957",
expected.data.as_mut(),
)
.unwrap();
let actual = key_agreement(&PrivateKey::from(scalar), &PublicKey::from(point)).unwrap();
assert_eq!(actual, expected);
}
#[test]
fn test_rfc_section_5_iter() {
let mut k = BASEPOINT_BYTES;
let mut u = BASEPOINT;
let ret = key_agreement(&PrivateKey::from(k), &PublicKey::from(&u)).unwrap();
u = FieldElement::from_bytes(&k);
k = ret.data.bytes;
let mut expected = SharedKey::from([0u8; 32]);
hex::decode_to_slice(
"422c8e7a6227d7bca1350b3e2bb7279f7897b87bb6854b783c60e80311ae3079",
expected.data.as_mut(),
)
.unwrap();
assert_eq!(k, expected.unprotected_as_ref(), "Failed after 1 iter");
for _ in 0..999 {
let ret = key_agreement(&PrivateKey::from(k), &PublicKey::from(&u)).unwrap();
u = FieldElement::from_bytes(&k);
k = ret.data.bytes;
}
hex::decode_to_slice(
"684cf59ba83309552800ef566f2f4d3c1c3887c49360e3875f2eb94d99532c51",
expected.data.as_mut(),
)
.unwrap();
assert_eq!(k, expected.unprotected_as_ref(), "Failed after 1.000 iter");
}
#[test]
fn test_rfc_section_6_pub_priv_basepoint() {
let mut alice_pub = [0u8; 32];
let mut alice_priv = [0u8; 32];
let mut bob_pub = [0u8; 32];
let mut bob_priv = [0u8; 32];
let mut shared = SharedKey::from([0u8; 32]);
hex::decode_to_slice(
"77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a",
&mut alice_priv,
)
.unwrap();
hex::decode_to_slice(
"8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a",
&mut alice_pub,
)
.unwrap();
assert_eq!(
PublicKey::from(alice_pub),
key_agreement(
&PrivateKey::from(alice_priv),
&PublicKey::from(BASEPOINT_BYTES)
)
.unwrap()
.unprotected_as_ref()
);
hex::decode_to_slice(
"5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb",
&mut bob_priv,
)
.unwrap();
hex::decode_to_slice(
"de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f",
&mut bob_pub,
)
.unwrap();
assert_eq!(
PublicKey::from(bob_pub),
key_agreement(
&PrivateKey::from(bob_priv),
&PublicKey::from(BASEPOINT_BYTES)
)
.unwrap()
.unprotected_as_ref()
);
hex::decode_to_slice(
"4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742",
shared.data.as_mut(),
)
.unwrap();
assert_eq!(
key_agreement(&PrivateKey::from(alice_priv), &PublicKey::from(bob_pub)).unwrap(),
shared
);
assert_eq!(
key_agreement(&PrivateKey::from(bob_priv), &PublicKey::from(alice_pub)).unwrap(),
shared
);
}
}