#![cfg_attr(not(feature = "std"), no_std)]
pub(crate) mod curve25519_field;
#[cfg(any(feature = "sha512-hmac-sha512", feature = "sha512-sha2"))]
pub(crate) mod jsf;
#[cfg(any(feature = "sha512-hmac-sha512", feature = "sha512-sha2"))]
pub(crate) mod scalar_field;
#[cfg(any(feature = "sha512-hmac-sha512", feature = "sha512-sha2"))]
pub(crate) mod signing_key;
#[cfg(any(feature = "sha512-hmac-sha512", feature = "sha512-sha2"))]
pub(crate) mod strict;
#[cfg(any(feature = "sha512-hmac-sha512", feature = "sha512-sha2"))]
pub(crate) mod strict_sign;
#[cfg(any(feature = "sha512-hmac-sha512", feature = "sha512-sha2"))]
pub use signing_key::{SignError, SigningKey};
#[cfg(any(feature = "sha512-hmac-sha512", feature = "sha512-sha2"))]
use signing_key::sign;
pub(crate) mod x25519;
pub mod x25519_kem;
pub use curve25519_field::{
Curve25519Field, Curve25519FieldCt, CurveSetupError, VerifyField, curve25519_schoolbook,
};
pub use modmath::{Field, FieldCt, FieldNct, Residue, ResidueCt, ResidueNct};
use core::marker::PhantomData;
pub trait UnsignedModularInt:
Sized
+ Clone
+ core::cmp::PartialOrd
+ const_num_traits::One
+ const_num_traits::Zero
+ const_num_traits::BitsPrecision
+ const_num_traits::WithPrecision
+ const_num_traits::ops::overflowing::OverflowingAdd<Output = Self>
+ const_num_traits::WrappingAdd<Output = Self>
+ const_num_traits::WrappingSub<Output = Self>
+ const_num_traits::WrappingMul<Output = Self>
+ core::ops::Shr<usize, Output = Self>
+ core::ops::BitAnd<Output = Self>
+ core::ops::ShrAssign<usize>
+ modmath::MontStorage
+ modmath::Parity
+ const_num_traits::FromByteSlice
+ const_num_traits::ToBytes
{
}
impl<T> UnsignedModularInt for T where
T: Sized
+ Clone
+ core::cmp::PartialOrd
+ const_num_traits::One
+ const_num_traits::Zero
+ const_num_traits::BitsPrecision
+ const_num_traits::WithPrecision
+ const_num_traits::ops::overflowing::OverflowingAdd<Output = Self>
+ const_num_traits::WrappingAdd<Output = Self>
+ const_num_traits::WrappingSub<Output = Self>
+ const_num_traits::WrappingMul<Output = Self>
+ core::ops::Shr<usize, Output = Self>
+ core::ops::BitAnd<Output = Self>
+ core::ops::ShrAssign<usize>
+ modmath::MontStorage
+ modmath::Parity
+ const_num_traits::FromByteSlice
+ const_num_traits::ToBytes
{
}
pub trait VerifyBackend:
Clone
+ PartialOrd
+ const_num_traits::One
+ const_num_traits::Zero
+ const_num_traits::BitsPrecision
+ const_num_traits::WithPrecision
+ const_num_traits::ops::overflowing::OverflowingAdd<Output = Self>
+ const_num_traits::WrappingAdd<Output = Self>
+ const_num_traits::WrappingSub<Output = Self>
+ core::ops::Shr<usize, Output = Self>
+ core::ops::ShrAssign<usize>
+ modmath::Parity
+ const_num_traits::FromByteSlice
{
}
impl<T> VerifyBackend for T where
T: Clone
+ PartialOrd
+ const_num_traits::One
+ const_num_traits::Zero
+ const_num_traits::BitsPrecision
+ const_num_traits::WithPrecision
+ const_num_traits::ops::overflowing::OverflowingAdd<Output = Self>
+ const_num_traits::WrappingAdd<Output = Self>
+ const_num_traits::WrappingSub<Output = Self>
+ core::ops::Shr<usize, Output = Self>
+ core::ops::ShrAssign<usize>
+ modmath::Parity
+ const_num_traits::FromByteSlice
{
}
#[inline]
pub(crate) fn from_le_bytes<T>(bytes: &[u8]) -> T
where
T: const_num_traits::FromByteSlice + const_num_traits::Zero,
{
<T as const_num_traits::FromByteSlice>::from_le_slice(bytes).unwrap_or_else(|_| T::zero())
}
#[inline]
pub(crate) fn to_le_bytes_ct<T>(
x: &T,
) -> zeroize::Zeroizing<<T as const_num_traits::ToBytes>::Bytes>
where
T: const_num_traits::ToBytes,
for<'a> &'a T: const_num_traits::ToBytes<Bytes = <T as const_num_traits::ToBytes>::Bytes>,
<T as const_num_traits::ToBytes>::Bytes: zeroize::Zeroize,
{
zeroize::Zeroizing::new(<&T as const_num_traits::ToBytes>::to_le_bytes(x))
}
pub(crate) fn blind_scalar<const N: usize>(
scalar: &[u8; 32],
blinder: u32,
modulus: &[u8],
) -> zeroize::Zeroizing<[u8; N]> {
fn read_le_u32(c: &[u8]) -> u32 {
<[u8; 4]>::try_from(c).map(u32::from_le_bytes).unwrap_or(0)
}
debug_assert_eq!(N % 4, 0);
debug_assert_eq!(modulus.len() % 4, 0);
let mut out = zeroize::Zeroizing::new([0u8; N]);
let mut mod_limbs = modulus.chunks_exact(4);
let mut scalar_limbs = scalar.chunks_exact(4);
let mut carry: u32 = 0;
for out_limb in out.chunks_exact_mut(4) {
let m = mod_limbs.next().map(read_le_u32).unwrap_or(0);
let s = scalar_limbs.next().map(read_le_u32).unwrap_or(0);
let (lo, hi) = const_num_traits::CarryingMul::carrying_mul_add(blinder, m, carry, s);
out_limb.copy_from_slice(&lo.to_le_bytes());
carry = hi;
}
debug_assert_eq!(carry, 0);
out
}
#[cfg(any(feature = "sha512-hmac-sha512", feature = "sha512-sha2"))]
pub trait SignBackend:
UnsignedModularInt
+ Copy
+ modmath::WideMul
+ modmath::CiosMontMulCt
+ const_num_traits::CtIsZero
+ subtle::ConditionallySelectable
+ subtle::ConstantTimeLess
+ zeroize::DefaultIsZeroes
{
}
#[cfg(any(feature = "sha512-hmac-sha512", feature = "sha512-sha2"))]
impl<T> SignBackend for T where
T: UnsignedModularInt
+ Copy
+ modmath::WideMul
+ modmath::CiosMontMulCt
+ const_num_traits::CtIsZero
+ subtle::ConditionallySelectable
+ subtle::ConstantTimeLess
+ zeroize::DefaultIsZeroes
{
}
pub(crate) const fn hx_le<const N: usize>(s: &str) -> [u8; N] {
const fn nib(c: u8) -> u8 {
match c {
b'0'..=b'9' => c - b'0',
b'a'..=b'f' => c - b'a' + 10,
b'A'..=b'F' => c - b'A' + 10,
_ => panic!("bad hex digit in curve constant"),
}
}
let s = s.as_bytes();
assert!(s.len() == 2 * N, "hex length must be 2*N chars");
let mut out = [0u8; N];
let mut i = 0;
while i < N {
out[N - 1 - i] = (nib(s[2 * i]) << 4) | nib(s[2 * i + 1]);
i += 1;
}
out
}
pub const P_BYTES: [u8; 32] =
hx_le("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed");
pub const D_BYTES: [u8; 32] =
hx_le("52036cee2b6ffe738cc740797779e89800700a4d4141d8ab75eb4dca135978a3");
pub const Q_BYTES: [u8; 32] =
hx_le("1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed");
pub const G_X_BYTES: [u8; 32] =
hx_le("216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51a");
pub const G_Y_BYTES: [u8; 32] =
hx_le("6666666666666666666666666666666666666666666666666666666666666658");
pub const G_T_BYTES: [u8; 32] =
hx_le("67875f0fd78b766566ea4e8e64abe37d20f09f80775152f56dde8ab3a5b7dda3");
pub const MODP_SQRT_M1_BYTES: [u8; 32] =
hx_le("2b8324804fc1df0b2b4d00993dfbd7a72f431806ad2fe478c4ee1b274a0ea0b0");
#[cfg(any(feature = "sha512-hmac-sha512", feature = "sha512-sha2"))]
use strict::verify;
pub use x25519::{A24_BYTES, BASE_U_BYTES, BLINDING_MODULUS_BYTES};
pub mod hazmat {
pub use crate::x25519::{clamp, x25519, x25519_base, x25519_base_blinded, x25519_blinded};
#[cfg(any(feature = "sha512-hmac-sha512", feature = "sha512-sha2"))]
pub use crate::signing_key::sign_with_fields;
#[cfg(any(feature = "sha512-hmac-sha512", feature = "sha512-sha2"))]
pub use crate::strict::verify_with_field;
}
pub struct VerifyingKey<T> {
public: [u8; 32],
_marker: PhantomData<T>,
}
impl<T> VerifyingKey<T> {
pub const fn from_bytes(public: [u8; 32]) -> Self {
Self {
public,
_marker: PhantomData,
}
}
pub const fn to_bytes(&self) -> [u8; 32] {
self.public
}
}
impl<T> From<[u8; 32]> for VerifyingKey<T> {
fn from(public: [u8; 32]) -> Self {
Self::from_bytes(public)
}
}
impl<T> Copy for VerifyingKey<T> {}
impl<T> Clone for VerifyingKey<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> PartialEq for VerifyingKey<T> {
fn eq(&self, other: &Self) -> bool {
self.public == other.public
}
}
impl<T> Eq for VerifyingKey<T> {}
impl<T> core::fmt::Debug for VerifyingKey<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("VerifyingKey")
.field("public", &self.public)
.finish()
}
}
#[cfg(any(feature = "sha512-hmac-sha512", feature = "sha512-sha2"))]
fn parse_signature(signature: &[u8]) -> Result<[u8; 64], signature::Error> {
signature.try_into().map_err(|_| signature::Error::new())
}
#[cfg(any(feature = "sha512-hmac-sha512", feature = "sha512-sha2"))]
impl<T, S> signature::Verifier<S> for VerifyingKey<T>
where
S: AsRef<[u8]>,
T: UnsignedModularInt + Copy + modmath::WideMul + modmath::CiosMontMul + modmath::NonCt,
for<'a> &'a T: core::ops::BitAnd<Output = T>
+ const_num_traits::WrappingAdd<Output = T>
+ const_num_traits::WrappingSub<Output = T>,
{
fn verify(&self, msg: &[u8], signature: &S) -> Result<(), signature::Error> {
let signature = parse_signature(signature.as_ref())?;
if verify::<T>(self.public, msg, signature) {
Ok(())
} else {
Err(signature::Error::new())
}
}
}
#[cfg(any(feature = "sha512-hmac-sha512", feature = "sha512-sha2"))]
impl<T> signature::Signer<[u8; 64]> for SigningKey<T>
where
T: SignBackend,
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,
{
fn try_sign(&self, msg: &[u8]) -> Result<[u8; 64], signature::Error> {
sign::<T>(self, msg).map_err(|_| signature::Error::new())
}
}
#[cfg(any(feature = "sha512-hmac-sha512", feature = "sha512-sha2"))]
impl<T> signature::RandomizedSigner<[u8; 64]> for SigningKey<T>
where
T: SignBackend,
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,
{
fn try_sign_with_rng<R: rand_core::TryCryptoRng + ?Sized>(
&self,
rng: &mut R,
msg: &[u8],
) -> Result<[u8; 64], signature::Error> {
signing_key::sign_blinded::<T, R>(rng, self, msg).map_err(|_| signature::Error::new())
}
}