use core::fmt;
use libcrux_hacl_rs::bignum::bignum256;
use super::util;
use crate::error::{Error, KeyFormatError};
use crate::low;
use crate::mid::rng::{RandomSource, SystemRandom};
#[derive(Clone, Debug)]
pub(crate) struct PublicKey {
xy: [u8; 64],
}
impl PublicKey {
pub(crate) const BYTES: usize = 65;
pub(crate) fn from_x962_uncompressed(bytes: &[u8]) -> Result<Self, Error> {
let _entry = low::Entry::new_public();
match bytes.first() {
Some(&0x04) => (),
Some(_) => return Err(KeyFormatError::Compressed.into()),
None => return Err(KeyFormatError::WrongLength.into()),
}
let xy: [u8; 64] = bytes[1..]
.try_into()
.map_err(|_| KeyFormatError::WrongLength)?;
match libcrux_p256::validate_public_key(&xy) {
true => Ok(Self { xy }),
false => Err(KeyFormatError::NotOnCurve.into()),
}
}
pub(crate) fn raw_ecdsa_verify(&self, r: &Scalar, s: &Scalar, e: &Scalar) -> Result<(), Error> {
let e = e.as_bytes();
match libcrux_p256::ecdsa_verif_without_hash(
e.len() as u32,
&e,
&self.xy,
&r.as_bytes(),
&s.as_bytes(),
) {
true => Ok(()),
false => Err(Error::BadSignature),
}
}
}
pub(crate) type VerifyingKey = PublicKey;
pub(crate) struct PrivateKey {
scalar: Scalar,
}
impl PrivateKey {
pub(crate) fn new_random() -> Result<Self, Error> {
Self::generate(&mut SystemRandom)
}
pub(crate) fn public_key_uncompressed(&self) -> [u8; PublicKey::BYTES] {
let _entry = low::Entry::new_secret();
uncompressed(&self.public_point())
}
pub(crate) fn diffie_hellman(&self, peer: &PublicKey) -> Result<SharedSecret, Error> {
let _entry = low::Entry::new_secret();
let d = self.scalar.as_bytes();
let mut point = [0u8; 64];
let ok = libcrux_p256::dh_responder(&mut point, &peer.xy, &d);
let mut secret = SharedSecret([0u8; 32]);
secret.0.copy_from_slice(&point[..32]);
match low::ct::into_public(ok) {
true => Ok(secret),
false => Err(Error::Fault),
}
}
fn public_point(&self) -> [u8; 64] {
let d = self.scalar.as_bytes();
let mut xy = [0u8; 64];
let ok = libcrux_p256::dh_initiator(&mut xy, &d);
match low::ct::into_public(ok) {
true => low::ct::into_public(xy),
false => panic!("internal fault"),
}
}
pub(crate) fn generate(rng: &mut dyn RandomSource) -> Result<Self, Error> {
let _entry = low::Entry::new_secret();
for _ in 0..64 {
let mut r = [0u8; Scalar::BYTES];
rng.fill(&mut r)?;
low::ct::secret_slice(&r);
if let Ok(p) = Self::from_bytes(&r) {
return Ok(p);
}
}
Err(Error::RngFailed)
}
fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
Scalar::from_bytes_checked(bytes).map(|scalar| Self {
scalar: low::ct::into_secret(scalar),
})
}
}
impl fmt::Debug for PrivateKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PrivateKey").finish_non_exhaustive()
}
}
pub(crate) struct StaticPrivateKey(PrivateKey);
impl StaticPrivateKey {
#[cfg(test)]
pub(crate) fn new_random() -> Result<Self, Error> {
PrivateKey::generate(&mut SystemRandom).map(Self)
}
pub(crate) fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
let _entry = low::Entry::new_secret();
PrivateKey::from_bytes(bytes).map(Self)
}
pub(crate) fn secret_bytes(&self) -> [u8; Scalar::BYTES] {
self.0.scalar.as_bytes()
}
pub(crate) fn public_key_uncompressed(&self) -> [u8; PublicKey::BYTES] {
let _entry = low::Entry::new_secret();
uncompressed(&self.0.public_point())
}
#[cfg(test)]
pub(crate) fn diffie_hellman(&self, peer: &PublicKey) -> Result<SharedSecret, Error> {
let _entry = low::Entry::new_secret();
self.0.diffie_hellman(peer)
}
pub(crate) fn generate(rng: &mut dyn RandomSource) -> Result<Self, Error> {
let _entry = low::Entry::new_secret();
PrivateKey::generate(rng).map(Self)
}
pub(crate) fn public_key_x_scalar(&self) -> Scalar {
let _entry = low::Entry::new_secret();
Scalar::from_bytes_reduced(&self.0.public_point()[..32]).unwrap()
}
pub(crate) fn raw_ecdsa_sign(&self, k: &Self, e: &Scalar, _r: &Scalar) -> Scalar {
let d = self.0.scalar.as_bytes();
let k = k.0.scalar.as_bytes();
let e = e.as_bytes();
let mut sig = [0u8; 64];
let ok = libcrux_p256::ecdsa_sign_p256_without_hash(&mut sig, e.len() as u32, &e, &d, &k);
let sig = low::ct::into_public(sig);
match low::ct::into_public(ok) {
true => Scalar::from_bytes_checked(&sig[32..]).unwrap_or_default(),
false => Scalar::default(),
}
}
}
pub(crate) struct SharedSecret(pub(crate) [u8; 32]);
#[derive(Default)]
pub(crate) struct Scalar([u64; 4]);
impl Scalar {
pub(crate) const BYTES: usize = 32;
pub(crate) fn from_bytes_checked(bytes: &[u8]) -> Result<Self, Error> {
let full = Self(
util::big_endian_slice_any_size_to_u64x4(bytes)
.ok_or(KeyFormatError::ScalarOutOfRange)?,
);
let in_range = bignum256::lt_mask(&full.0, &CURVE_ORDER) == u64::MAX;
match low::ct::into_public(in_range & !full.is_zero()) {
true => Ok(full),
false => Err(KeyFormatError::ScalarOutOfRange.into()),
}
}
pub(crate) fn from_bytes_reduced(bytes: &[u8]) -> Result<Self, Error> {
let mut wide = [0u64; 8];
wide[..4].copy_from_slice(
&util::big_endian_slice_any_size_to_u64x4(bytes).ok_or(Error::WrongLength)?,
);
let mut r = Self::default();
bignum256::r#mod(&CURVE_ORDER, &wide, &mut r.0);
Ok(r)
}
pub(crate) fn as_bytes(&self) -> [u8; Self::BYTES] {
util::u64x4_to_big_endian(&self.0)
}
pub(crate) fn is_zero(&self) -> bool {
low::bignum_eq(&self.0, &[0; 4])
}
}
fn uncompressed(xy: &[u8; 64]) -> [u8; PublicKey::BYTES] {
let mut r = [0u8; PublicKey::BYTES];
r[0] = 0x04;
r[1..].copy_from_slice(xy);
r
}
const CURVE_ORDER: [u64; 4] = [
0xf3b9_cac2_fc63_2551,
0xbce6_faad_a717_9e84,
0xffff_ffff_ffff_ffff,
0xffff_ffff_0000_0000,
];
#[cfg(test)]
#[cfg_attr(coverage_nightly, coverage(off))]
mod tests {
use super::*;
#[test]
fn scalar_range() {
let n = util::u64x4_to_big_endian(&CURVE_ORDER);
assert_eq!(
Scalar::from_bytes_checked(&n).err(),
Some(KeyFormatError::ScalarOutOfRange.into())
);
assert_eq!(
Scalar::from_bytes_checked(&[0; 32]).err(),
Some(KeyFormatError::ScalarOutOfRange.into())
);
let mut n_minus_1 = n;
n_minus_1[31] -= 1;
assert!(Scalar::from_bytes_checked(&n_minus_1).is_ok());
}
#[test]
fn scalar_reduction() {
let n = util::u64x4_to_big_endian(&CURVE_ORDER);
assert!(Scalar::from_bytes_reduced(&n).unwrap().is_zero());
let mut n_plus_1 = n;
n_plus_1[31] += 1;
assert_eq!(
Scalar::from_bytes_reduced(&n_plus_1).unwrap().0,
[1, 0, 0, 0]
);
let max = Scalar::from_bytes_reduced(&[0xff; 32]).unwrap();
assert_eq!(
max.0,
[0x0c46_353d_039c_daae, 0x4319_0552_58e8_617b, 0, 0xffff_ffff]
);
}
#[test]
fn public_key_encoding() {
let mut bad = [0u8; 65];
assert_eq!(
PublicKey::from_x962_uncompressed(&bad).err(),
Some(KeyFormatError::Compressed.into())
);
bad[0] = 0x04;
assert_eq!(
PublicKey::from_x962_uncompressed(&bad).err(),
Some(KeyFormatError::NotOnCurve.into())
);
assert_eq!(
PublicKey::from_x962_uncompressed(&bad[..64]).err(),
Some(KeyFormatError::WrongLength.into())
);
assert_eq!(
PublicKey::from_x962_uncompressed(&[]).err(),
Some(KeyFormatError::WrongLength.into())
);
let key = StaticPrivateKey::new_random().unwrap();
let encoded = key.public_key_uncompressed();
let decoded = PublicKey::from_x962_uncompressed(&encoded).unwrap();
assert_eq!(decoded.xy, encoded[1..]);
}
}