use alloc::vec::Vec;
use p256::elliptic_curve::ff::PrimeField;
use p256::elliptic_curve::hash2curve::{ExpandMsgXmd, GroupDigest};
use p256::elliptic_curve::ops::Reduce;
use p256::elliptic_curve::sec1::{FromEncodedPoint, ToEncodedPoint};
use p256::{AffinePoint, EncodedPoint, NistP256, ProjectivePoint, Scalar};
use pakery_core::crypto::group::CpaceGroup;
use pakery_core::PakeError;
use rand_core::CryptoRng;
use zeroize::{Zeroize, Zeroizing};
const HASH_TO_CURVE_DST: &[u8] = b"PAKE-P256-HashToCurve-v1";
#[derive(Clone, PartialEq)]
pub struct P256Group {
point: ProjectivePoint,
}
impl CpaceGroup for P256Group {
type Scalar = Scalar;
fn scalar_mul(&self, scalar: &Scalar) -> Self {
Self {
point: self.point * scalar,
}
}
fn is_identity(&self) -> bool {
use subtle::ConstantTimeEq;
bool::from(self.point.ct_eq(&ProjectivePoint::IDENTITY))
}
fn to_bytes(&self) -> Vec<u8> {
self.point
.to_affine()
.to_encoded_point(false)
.as_bytes()
.to_vec()
}
fn from_bytes(bytes: &[u8]) -> Result<Self, PakeError> {
let encoded = EncodedPoint::from_bytes(bytes).map_err(|_| PakeError::InvalidPoint)?;
let affine = AffinePoint::from_encoded_point(&encoded);
if affine.is_none().into() {
return Err(PakeError::InvalidPoint);
}
Ok(Self {
point: affine.expect("validated by is_none check above").into(),
})
}
fn from_uniform_bytes(bytes: &[u8]) -> Result<Self, PakeError> {
if bytes.len() != 64 {
return Err(PakeError::InvalidInput(
"from_uniform_bytes requires 64 bytes",
));
}
let point =
NistP256::hash_from_bytes::<ExpandMsgXmd<sha2::Sha256>>(&[bytes], &[HASH_TO_CURVE_DST])
.map_err(|_| PakeError::ProtocolError("hash-to-curve failed"))?;
Ok(Self { point })
}
fn random_scalar(rng: &mut impl CryptoRng) -> Scalar {
loop {
let mut bytes = Zeroizing::new([0u8; 32]);
rng.fill_bytes(&mut *bytes);
let mut fb = p256::FieldBytes::from(*bytes);
let result = Option::<Scalar>::from(Scalar::from_repr(fb));
fb.zeroize();
if let Some(s) = result {
return s;
}
}
}
fn add(&self, other: &Self) -> Self {
Self {
point: self.point + other.point,
}
}
fn negate(&self) -> Self {
Self { point: -self.point }
}
fn basepoint_mul(scalar: &Scalar) -> Self {
Self {
point: ProjectivePoint::GENERATOR * scalar,
}
}
fn scalar_from_wide_bytes(bytes: &[u8]) -> Result<Scalar, PakeError> {
if bytes.len() != 64 {
return Err(PakeError::InvalidInput(
"scalar_from_wide_bytes requires 64 bytes",
));
}
let high_arr: [u8; 32] = bytes[..32].try_into().expect("first 32 bytes");
let high_fb = p256::FieldBytes::from(high_arr);
let low_arr: [u8; 32] = bytes[32..].try_into().expect("last 32 bytes");
let low_fb = p256::FieldBytes::from(low_arr);
let high = <Scalar as Reduce<p256::U256>>::reduce_bytes(&high_fb);
let low = <Scalar as Reduce<p256::U256>>::reduce_bytes(&low_fb);
Ok(high * r_constant() + low)
}
fn scalar_to_bytes(scalar: &Scalar) -> Vec<u8> {
scalar.to_repr().to_vec()
}
}
fn r_constant() -> Scalar {
Scalar::from_repr(p256::FieldBytes::from([
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x43, 0x19, 0x05, 0x52, 0x58, 0xE8, 0x61, 0x7B, 0x0C, 0x46, 0x35, 0x3D, 0x03, 0x9C,
0xDA, 0xAF,
]))
.expect("R constant is a valid P-256 scalar")
}