use alloc::vec::Vec;
use ecdsa::EncodedPoint;
use elliptic_curve::{
sec1::{FromEncodedPoint, ToEncodedPoint, ValidatePublicKey as _},
FieldBytes, SecretKey,
};
use generic_array::typenum::Unsigned as _;
use k256::Secp256k1;
use p256::NistP256;
use p384::NistP384;
use rand_core::OsRng;
use secrecy::{ExposeSecret as _, SecretSlice};
use signature::{RandomizedSigner as _, Verifier as _};
use zeroize::Zeroizing;
use crate::{
crypto::{backend::interface::ec, Result},
jwa::{self, EcDSA},
};
#[derive(Clone)]
enum ErasedPrivateKey {
P256(SecretKey<NistP256>),
P384(SecretKey<NistP384>),
Secp256k1(SecretKey<Secp256k1>),
}
#[derive(Clone)]
enum ErasedPublicKey {
P256(elliptic_curve::PublicKey<NistP256>),
P384(elliptic_curve::PublicKey<NistP384>),
Secp256k1(elliptic_curve::PublicKey<Secp256k1>),
}
#[derive(Clone)]
pub(crate) enum ErasedSignature {
P256(ecdsa::SignatureBytes<NistP256>),
P384(ecdsa::SignatureBytes<NistP384>),
Secp256k1(ecdsa::SignatureBytes<Secp256k1>),
}
impl From<ErasedSignature> for Vec<u8> {
fn from(value: ErasedSignature) -> Self {
match value {
ErasedSignature::P256(sig) => sig.to_vec(),
ErasedSignature::P384(sig) => sig.to_vec(),
ErasedSignature::Secp256k1(sig) => sig.to_vec(),
}
}
}
impl AsRef<[u8]> for ErasedSignature {
fn as_ref(&self) -> &[u8] {
match self {
ErasedSignature::P256(sig) => sig.as_ref(),
ErasedSignature::P384(sig) => sig.as_ref(),
ErasedSignature::Secp256k1(sig) => sig.as_ref(),
}
}
}
fn to_field_bytes<C: elliptic_curve::Curve>(
bytes: &[u8],
) -> Result<&FieldBytes<C>, super::BackendError> {
if bytes.len() != C::FieldBytesSize::USIZE {
return Err(super::BackendError::InvalidEcPoint {
expected: C::FieldBytesSize::USIZE,
actual: bytes.len(),
});
}
Ok(FieldBytes::<C>::from_slice(bytes))
}
#[derive(Clone)]
#[repr(transparent)]
pub(crate) struct PrivateKey {
inner: ErasedPrivateKey,
}
impl ec::PrivateKey for PrivateKey {
type PublicKey = PublicKey;
type Signature = ErasedSignature;
fn generate(alg: jwa::EcDSA) -> Result<Self> {
let mut rng = OsRng;
let key = match alg {
EcDSA::Es256 => ErasedPrivateKey::P256(SecretKey::<NistP256>::random(&mut rng)),
EcDSA::Es384 => ErasedPrivateKey::P384(SecretKey::<NistP384>::random(&mut rng)),
EcDSA::Es512 => return Err(super::BackendError::CurveNotSupported("P-521").into()),
EcDSA::Es256K => ErasedPrivateKey::Secp256k1(SecretKey::<Secp256k1>::random(&mut rng)),
};
Ok(Self { inner: key })
}
fn new(alg: EcDSA, x: Vec<u8>, y: Vec<u8>, d: SecretSlice<u8>) -> Result<Self> {
fn new_typed<C: elliptic_curve::Curve + elliptic_curve::CurveArithmetic>(
x: Vec<u8>,
y: Vec<u8>,
d: SecretSlice<u8>,
) -> Result<elliptic_curve::SecretKey<C>>
where
<C as elliptic_curve::Curve>::FieldBytesSize: elliptic_curve::sec1::ModulusSize,
<C as elliptic_curve::CurveArithmetic>::AffinePoint: FromEncodedPoint<C>,
<C as elliptic_curve::CurveArithmetic>::AffinePoint: ToEncodedPoint<C>,
{
let x = to_field_bytes::<C>(&x)?;
let y = to_field_bytes::<C>(&y)?;
let d = d.expose_secret();
let d = to_field_bytes::<C>(d)?;
let point = EncodedPoint::<C>::from_affine_coordinates(x, y, false);
let secret = elliptic_curve::SecretKey::<C>::from_bytes(d)
.map_err(super::BackendError::EllipticCurve)?;
C::validate_public_key(&secret, &point).map_err(super::BackendError::EllipticCurve)?;
Ok(secret)
}
match alg {
EcDSA::Es256 => Ok(Self {
inner: ErasedPrivateKey::P256(new_typed::<NistP256>(x, y, d)?),
}),
EcDSA::Es384 => Ok(Self {
inner: ErasedPrivateKey::P384(new_typed::<NistP384>(x, y, d)?),
}),
EcDSA::Es512 => Err(super::BackendError::CurveNotSupported("P-521").into()),
EcDSA::Es256K => Ok(Self {
inner: ErasedPrivateKey::Secp256k1(new_typed::<Secp256k1>(x, y, d)?),
}),
}
}
fn private_material(&self) -> SecretSlice<u8> {
match self.inner {
ErasedPrivateKey::P256(ref key) => {
let material = Zeroizing::new(key.to_bytes());
let material = material.to_vec();
SecretSlice::from(material)
}
ErasedPrivateKey::P384(ref key) => {
let material = Zeroizing::new(key.to_bytes());
let material = material.to_vec();
SecretSlice::from(material)
}
ErasedPrivateKey::Secp256k1(ref key) => {
let material = Zeroizing::new(key.to_bytes());
let material = material.to_vec();
SecretSlice::from(material)
}
}
}
#[inline]
fn public_point(&self) -> (Vec<u8>, Vec<u8>) {
ec::PublicKey::to_point(&self.to_public_key())
}
fn to_public_key(&self) -> Self::PublicKey {
match self.inner {
ErasedPrivateKey::P256(ref key) => PublicKey {
inner: ErasedPublicKey::P256(key.public_key()),
},
ErasedPrivateKey::P384(ref key) => PublicKey {
inner: ErasedPublicKey::P384(key.public_key()),
},
ErasedPrivateKey::Secp256k1(ref key) => PublicKey {
inner: ErasedPublicKey::Secp256k1(key.public_key()),
},
}
}
fn sign(&mut self, data: &[u8], deterministic: bool) -> Result<Self::Signature> {
let sig = match self.inner {
ErasedPrivateKey::P256(ref key) => {
let key = ecdsa::SigningKey::<NistP256>::from(key);
let sig = if deterministic {
key.sign_recoverable(data)
.map_err(super::BackendError::Ecdsa)?
.0
} else {
key.try_sign_with_rng(&mut OsRng, data)
.map_err(super::BackendError::Ecdsa)?
};
ErasedSignature::P256(sig.to_bytes())
}
ErasedPrivateKey::P384(ref key) => {
let key = ecdsa::SigningKey::<NistP384>::from(key);
let sig = if deterministic {
key.sign_recoverable(data)
.map_err(super::BackendError::Ecdsa)?
.0
} else {
key.try_sign_with_rng(&mut OsRng, data)
.map_err(super::BackendError::Ecdsa)?
};
ErasedSignature::P384(sig.to_bytes())
}
ErasedPrivateKey::Secp256k1(ref key) => {
let key = ecdsa::SigningKey::<Secp256k1>::from(key);
let sig = if deterministic {
key.sign_recoverable(data)
.map_err(super::BackendError::Ecdsa)?
.0
} else {
key.try_sign_with_rng(&mut OsRng, data)
.map_err(super::BackendError::Ecdsa)?
};
ErasedSignature::Secp256k1(sig.to_bytes())
}
};
Ok(sig)
}
}
#[derive(Clone)]
#[repr(transparent)]
pub(crate) struct PublicKey {
inner: ErasedPublicKey,
}
impl ec::PublicKey for PublicKey {
fn new(alg: EcDSA, x: Vec<u8>, y: Vec<u8>) -> Result<Self> {
fn new_typed<C: elliptic_curve::Curve + elliptic_curve::CurveArithmetic>(
x: Vec<u8>,
y: Vec<u8>,
) -> Result<elliptic_curve::PublicKey<C>>
where
<C as elliptic_curve::Curve>::FieldBytesSize: elliptic_curve::sec1::ModulusSize,
<C as elliptic_curve::CurveArithmetic>::AffinePoint: FromEncodedPoint<C>,
<C as elliptic_curve::CurveArithmetic>::AffinePoint: ToEncodedPoint<C>,
{
let x = to_field_bytes::<C>(&x)?;
let y = to_field_bytes::<C>(&y)?;
let point = EncodedPoint::<C>::from_affine_coordinates(x, y, false);
let key: Option<_> = elliptic_curve::PublicKey::<C>::from_encoded_point(&point).into();
let key = key.ok_or(super::BackendError::InvalidEcKey)?;
Ok(key)
}
match alg {
EcDSA::Es256 => Ok(Self {
inner: ErasedPublicKey::P256(new_typed::<NistP256>(x, y)?),
}),
EcDSA::Es384 => Ok(Self {
inner: ErasedPublicKey::P384(new_typed::<NistP384>(x, y)?),
}),
EcDSA::Es512 => Err(super::BackendError::CurveNotSupported("P-521").into()),
EcDSA::Es256K => Ok(Self {
inner: ErasedPublicKey::Secp256k1(new_typed::<Secp256k1>(x, y)?),
}),
}
}
fn to_point(&self) -> (Vec<u8>, Vec<u8>) {
let identity_point = || alloc::vec![0u8];
match self.inner {
ErasedPublicKey::P256(ref key) => {
let point = key.to_encoded_point(false);
(
point.x().map(|a| a.to_vec()).unwrap_or_else(identity_point),
point.y().map(|a| a.to_vec()).unwrap_or_else(identity_point),
)
}
ErasedPublicKey::P384(ref key) => {
let point = key.to_encoded_point(false);
(
point.x().map(|a| a.to_vec()).unwrap_or_else(identity_point),
point.y().map(|a| a.to_vec()).unwrap_or_else(identity_point),
)
}
ErasedPublicKey::Secp256k1(ref key) => {
let point = key.to_encoded_point(false);
(
point.x().map(|a| a.to_vec()).unwrap_or_else(identity_point),
point.y().map(|a| a.to_vec()).unwrap_or_else(identity_point),
)
}
}
}
fn verify(&mut self, msg: &[u8], signature: &[u8]) -> Result<bool> {
Ok(match self.inner {
ErasedPublicKey::P256(ref key) => {
let Ok(sig) = ecdsa::Signature::<NistP256>::try_from(signature) else {
return Ok(false);
};
let key = ecdsa::VerifyingKey::<NistP256>::from(key);
key.verify(msg, &sig).is_ok()
}
ErasedPublicKey::P384(ref key) => {
let Ok(sig) = ecdsa::Signature::<NistP384>::try_from(signature) else {
return Ok(false);
};
let key = ecdsa::VerifyingKey::<NistP384>::from(key);
key.verify(msg, &sig).is_ok()
}
ErasedPublicKey::Secp256k1(ref key) => {
let Ok(sig) = ecdsa::Signature::<Secp256k1>::try_from(signature) else {
return Ok(false);
};
let key = ecdsa::VerifyingKey::<Secp256k1>::from(key);
key.verify(msg, &sig).is_ok()
}
})
}
}