use core::fmt::Debug;
use p256::elliptic_curve::ff::{FromUniformBytes, PrimeField};
use p256::elliptic_curve::rand_core::CryptoRng;
use p256::elliptic_curve::sec1::ToSec1Point;
use p256::elliptic_curve::{Group as _, PublicKey};
use p256::{NistP256, ProjectivePoint};
use zeroize::Zeroize;
pub trait Suite: sealed::Suite + Copy + Debug + Eq + core::hash::Hash + Send + Sync {
const NAME: &'static str;
const MIN_PBKDF_OUTPUT_LEN: usize;
const MAX_PBKDF_OUTPUT_LEN: usize;
type Share: Bytes;
type Confirmation: Bytes;
type SharedKey: Bytes;
type Record: Bytes;
}
pub trait Bytes:
sealed::Bytes
+ Copy
+ Debug
+ Eq
+ core::hash::Hash
+ Send
+ Sync
+ AsRef<[u8]>
+ AsMut<[u8]>
+ Zeroize
+ for<'a> TryFrom<&'a [u8]>
+ 'static
{
}
impl<const N: usize> Bytes for [u8; N] {}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum P256Sha256 {}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum P256Sha512 {}
const P256_POINT_LEN: usize = 65;
const P256_SCALAR_LEN: usize = 32;
const P256_MIN_HALF: usize = 40;
const P256_MAX_HALF: usize = 64;
impl Suite for P256Sha256 {
const NAME: &'static str = "P256-SHA256-HKDF-SHA256-HMAC-SHA256";
const MIN_PBKDF_OUTPUT_LEN: usize = 2 * P256_MIN_HALF;
const MAX_PBKDF_OUTPUT_LEN: usize = 2 * P256_MAX_HALF;
type Share = [u8; P256_POINT_LEN];
type Confirmation = [u8; 32];
type SharedKey = [u8; 32];
type Record = [u8; P256_SCALAR_LEN + P256_POINT_LEN];
}
impl sealed::Suite for P256Sha256 {
type Group = NistP256;
type Hash = sha2::Sha256;
type ConfirmationKeys = [u8; 64];
}
impl Suite for P256Sha512 {
const NAME: &'static str = "P256-SHA512-HKDF-SHA512-HMAC-SHA512";
const MIN_PBKDF_OUTPUT_LEN: usize = 2 * P256_MIN_HALF;
const MAX_PBKDF_OUTPUT_LEN: usize = 2 * P256_MAX_HALF;
type Share = [u8; P256_POINT_LEN];
type Confirmation = [u8; 64];
type SharedKey = [u8; 64];
type Record = [u8; P256_SCALAR_LEN + P256_POINT_LEN];
}
impl sealed::Suite for P256Sha512 {
type Group = NistP256;
type Hash = sha2::Sha512;
type ConfirmationKeys = [u8; 128];
}
const P256_M: [u8; P256_POINT_LEN] = [
0x04, 0x88, 0x6e, 0x2f, 0x97, 0xac, 0xe4, 0x6e, 0x55, 0xba, 0x9d, 0xd7, 0x24, 0x25, 0x79, 0xf2,
0x99, 0x3b, 0x64, 0xe1, 0x6e, 0xf3, 0xdc, 0xab, 0x95, 0xaf, 0xd4, 0x97, 0x33, 0x3d, 0x8f, 0xa1,
0x2f, 0x5f, 0xf3, 0x55, 0x16, 0x3e, 0x43, 0xce, 0x22, 0x4e, 0x0b, 0x0e, 0x65, 0xff, 0x02, 0xac,
0x8e, 0x5c, 0x7b, 0xe0, 0x94, 0x19, 0xc7, 0x85, 0xe0, 0xca, 0x54, 0x7d, 0x55, 0xa1, 0x2e, 0x2d,
0x20,
];
const P256_N: [u8; P256_POINT_LEN] = [
0x04, 0xd8, 0xbb, 0xd6, 0xc6, 0x39, 0xc6, 0x29, 0x37, 0xb0, 0x4d, 0x99, 0x7f, 0x38, 0xc3, 0x77,
0x07, 0x19, 0xc6, 0x29, 0xd7, 0x01, 0x4d, 0x49, 0xa2, 0x4b, 0x4f, 0x98, 0xba, 0xa1, 0x29, 0x2b,
0x49, 0x07, 0xd6, 0x0a, 0xa6, 0xbf, 0xad, 0xe4, 0x50, 0x08, 0xa6, 0x36, 0x33, 0x7f, 0x51, 0x68,
0xc6, 0x4d, 0x9b, 0xd3, 0x60, 0x34, 0x80, 0x8c, 0xd5, 0x64, 0x49, 0x0b, 0x1e, 0x65, 0x6e, 0xdb,
0xe7,
];
impl sealed::Group for NistP256 {
type Scalar = p256::Scalar;
type Point = ProjectivePoint;
type ScalarBytes = [u8; P256_SCALAR_LEN];
const M: &'static [u8] = &P256_M;
const N: &'static [u8] = &P256_N;
fn decode(bytes: &[u8]) -> Option<Self::Point> {
if bytes.first() != Some(&0x04) {
return None;
}
PublicKey::<NistP256>::from_sec1_bytes(bytes)
.ok()
.map(|key| key.to_projective())
}
fn encode(point: &Self::Point, out: &mut [u8]) -> Option<()> {
if bool::from(point.is_identity()) {
return None;
}
let encoded = point.to_sec1_point(false);
let encoded = encoded.as_bytes();
if out.len() != encoded.len() {
return None;
}
out.copy_from_slice(encoded);
Some(())
}
fn random<R: CryptoRng + ?Sized>(rng: &mut R) -> Self::Scalar {
let mut wide = [0; P256_MAX_HALF];
rng.fill_bytes(&mut wide);
let scalar = p256::Scalar::from_uniform_bytes(&wide);
wide.zeroize();
scalar
}
fn reduce(bytes: &[u8]) -> Option<Self::Scalar> {
let mut wide = [0; 2 * P256_SCALAR_LEN];
let start = wide.len().checked_sub(bytes.len())?;
wide.get_mut(start..)?.copy_from_slice(bytes);
let scalar = p256::Scalar::from_uniform_bytes(&wide);
wide.zeroize();
Some(scalar)
}
fn scalar_from_bytes(bytes: &[u8]) -> Option<Self::Scalar> {
let bytes: [u8; P256_SCALAR_LEN] = bytes.try_into().ok()?;
p256::Scalar::from_repr(bytes.into()).into()
}
fn scalar_to_bytes(scalar: &Self::Scalar) -> Self::ScalarBytes {
scalar.to_repr().into()
}
}
pub(crate) mod sealed {
use core::ops::{Add, Mul, Sub};
use hmac::digest::Digest;
use hmac::digest::block_api::EagerHash;
use p256::elliptic_curve::Group as _;
use p256::elliptic_curve::rand_core::CryptoRng;
use zeroize::Zeroize;
pub trait Suite: 'static {
type Group: Group;
type Hash: EagerHash + Digest + Clone;
type ConfirmationKeys: super::Bytes;
}
pub trait Group: 'static {
type Scalar: p256::elliptic_curve::ff::Field + Zeroize;
type Point: p256::elliptic_curve::Group<Scalar = Self::Scalar>
+ Add<Output = Self::Point>
+ Sub<Output = Self::Point>
+ Mul<Self::Scalar, Output = Self::Point>
+ Zeroize;
type ScalarBytes: super::Bytes;
const M: &'static [u8];
const N: &'static [u8];
fn decode(bytes: &[u8]) -> Option<Self::Point>;
fn encode(point: &Self::Point, out: &mut [u8]) -> Option<()>;
fn random<R: CryptoRng + ?Sized>(rng: &mut R) -> Self::Scalar;
fn reduce(bytes: &[u8]) -> Option<Self::Scalar>;
fn scalar_from_bytes(bytes: &[u8]) -> Option<Self::Scalar>;
fn scalar_to_bytes(scalar: &Self::Scalar) -> Self::ScalarBytes;
fn fixed(bytes: &[u8]) -> Self::Point {
Self::decode(bytes).unwrap_or_else(Self::Point::identity)
}
}
pub trait Bytes {
fn zeroed() -> Self;
}
impl<const N: usize> Bytes for [u8; N] {
fn zeroed() -> Self {
[0; N]
}
}
}
pub(crate) type Group<S> = <S as sealed::Suite>::Group;
pub(crate) type Point<S> = <Group<S> as sealed::Group>::Point;
pub(crate) type Hash<S> = <S as sealed::Suite>::Hash;
pub(crate) type ConfirmationKeys<S> = <S as sealed::Suite>::ConfirmationKeys;
pub(crate) type ScalarBytes<S> = <Group<S> as sealed::Group>::ScalarBytes;
pub(crate) type Scalar<S> = <Group<S> as sealed::Group>::Scalar;