use super::{Error, P256Signature, P256r1PublicKey};
use crate::curve::SharedSecret;
use eccoxide::curve::sec2::p256r1::{Point, Scalar};
use rand_core::{CryptoRng, RngCore};
use std::fmt;
pub struct P256r1PrivateKey([u8; Self::SIZE]);
impl P256r1PrivateKey {
pub const SIZE: usize = 32;
const MAX_SCALAR_RETRIES: usize = 128;
#[inline]
pub fn from_bytes(bytes: [u8; Self::SIZE]) -> Result<Self, Error> {
if Self::scalar_of(&bytes).is_some() {
Ok(Self(bytes))
} else {
Err(Error::InvalidPrivateKey)
}
}
#[inline]
pub fn to_bytes(&self) -> &[u8; Self::SIZE] {
&self.0
}
pub fn generate<RNG>(mut rng: RNG) -> Result<Self, Error>
where
RNG: RngCore + CryptoRng,
{
let mut bytes = [0; Self::SIZE];
for _ in 0..Self::MAX_SCALAR_RETRIES {
rng.fill_bytes(&mut bytes);
if Self::scalar_of(&bytes).is_some() {
return Ok(Self(bytes));
}
}
Err(Error::ScalarSamplingFailed)
}
#[inline]
fn scalar_of(bytes: &[u8; Self::SIZE]) -> Option<Scalar> {
match Scalar::from_slice(bytes) {
Some(s) if s != Scalar::zero() => Some(s),
_ => None,
}
}
#[inline]
fn scalar(&self) -> Scalar {
Self::scalar_of(&self.0).expect("private key validated on construction")
}
pub fn public(&self) -> P256r1PublicKey {
let point = Point::mul_base(&self.scalar());
P256r1PublicKey::from_point(point)
}
pub fn sign(&self, data: impl AsRef<[u8]>) -> Result<P256Signature, Error> {
Ok(super::ecdsa_sign_rfc6979(&self.0, data.as_ref()))
}
pub fn dh(&self, other: &P256r1PublicKey) -> Result<SharedSecret<32>, Error> {
shared_secret_from(&self.scalar(), &other.to_point()?)
}
}
fn shared_secret_from(scalar: &Scalar, peer: &Point) -> Result<SharedSecret<32>, Error> {
let shared = (scalar * peer)
.to_affine()
.ok_or(Error::InvalidSharedSecret)?;
let (x, _) = shared.to_coordinate();
Ok(SharedSecret::new(x.to_bytes()))
}
#[cfg(not(test))]
impl fmt::Debug for P256r1PrivateKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("P256r1PrivateKey").finish_non_exhaustive()
}
}
#[cfg(test)]
impl fmt::Debug for P256r1PrivateKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("P256r1PrivateKey")
.field(&hex::encode(self.0))
.finish()
}
}
impl Drop for P256r1PrivateKey {
fn drop(&mut self) {
crate::zeroize::zeroize_array(&mut self.0);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::curve::p256::P256;
use crate::provider::{
CryptoKeyProviderAsync, DhProviderAsync, EphemeralOnly, ProviderExt, SigningProviderAsync,
};
use proptest::prelude::*;
use rand::{SeedableRng, rngs::StdRng};
fn arbitrary_secret_key() -> impl Strategy<Value = P256r1PrivateKey> {
any::<[u8; P256r1PrivateKey::SIZE]>().prop_filter_map("not a canonical scalar", |b| {
P256r1PrivateKey::from_bytes(b).ok()
})
}
#[test]
fn from_bytes_rejects_zero() {
let err = P256r1PrivateKey::from_bytes([0u8; 32]).unwrap_err();
assert!(matches!(err, Error::InvalidPrivateKey));
}
#[test]
fn from_bytes_rejects_value_at_or_above_order() {
let err = P256r1PrivateKey::from_bytes([0xFF; 32]).unwrap_err();
assert!(matches!(err, Error::InvalidPrivateKey));
}
#[test]
fn from_bytes_accepts_canonical_scalar_and_round_trips() {
let bytes = [0x11u8; 32]; let key = P256r1PrivateKey::from_bytes(bytes).expect("valid scalar");
assert_eq!(key.to_bytes(), &bytes);
}
#[test]
fn stored_bytes_are_the_scalar_not_a_seed() {
let bytes = [0x11u8; 32];
let k1 = P256r1PrivateKey::from_bytes(bytes).unwrap();
let k2 = P256r1PrivateKey::from_bytes(bytes).unwrap();
assert_eq!(k1.public(), k2.public());
let mut other = bytes;
other[31] ^= 0x01;
let k3 = P256r1PrivateKey::from_bytes(other).unwrap();
assert_ne!(k1.public(), k3.public());
}
proptest! {
#[test]
fn signing_verify_works(
signing_key in arbitrary_secret_key(),
message in any::<Vec<u8>>(),
) {
let public_key = signing_key.public();
let signature = signing_key.sign(&message).unwrap();
prop_assert!(
public_key.verify(signature, &message)
)
}
}
#[tokio::test]
async fn provider_sign_and_dh() {
let mut provider = EphemeralOnly::new(StdRng::from_os_rng());
let sk1 = CryptoKeyProviderAsync::<P256>::generate_static_key_async(&mut provider)
.await
.unwrap();
let pk1 = provider.public(&sk1).unwrap();
let sk2 = CryptoKeyProviderAsync::<P256>::generate_ephemeral_key_async(&mut provider)
.await
.unwrap();
let pk2 = provider.public(&sk2).unwrap();
const MSG: &[u8] = b"hello hiss";
let sig = SigningProviderAsync::<P256>::sign_async(&provider, &sk1, MSG)
.await
.unwrap();
assert!(pk1.verify(sig, MSG));
assert!(!pk2.verify(sig, MSG));
let ss1 = DhProviderAsync::<P256>::dh_async(&provider, &sk1, &pk2)
.await
.unwrap();
let ss2 = DhProviderAsync::<P256>::dh_async(&provider, &sk2, &pk1)
.await
.unwrap();
assert_eq!(ss1, ss2);
}
#[test]
fn dh_rejects_degenerate_shared_secret() {
let sk = P256r1PrivateKey::from_bytes([0x11; 32]).unwrap();
let err = shared_secret_from(&sk.scalar(), &Point::INFINITY).unwrap_err();
assert!(matches!(err, Error::InvalidSharedSecret));
}
proptest! {
#[test]
fn from_bytes_never_panics(bytes in any::<Vec<u8>>()) {
let _ = P256r1PublicKey::from_bytes(&bytes);
}
#[test]
fn dh_succeeds_for_any_valid_peer(
sk in arbitrary_secret_key(),
peer_sk in arbitrary_secret_key(),
) {
prop_assert!(sk.dh(&peer_sk.public()).is_ok());
}
}
}