use digest::{array::Array, typenum::Unsigned};
use elliptic_curve::point::AffineCoordinates;
use p256::NistP256;
use p384::NistP384;
use p521::NistP521;
use sha2::{Digest as _, Sha256};
use signature::hazmat::{PrehashVerifier, RandomizedPrehashSigner};
use crate::Digest;
pub enum EllipticVerifyingKey {
Prime256(p256::ecdsa::VerifyingKey),
Prime384(p384::ecdsa::VerifyingKey),
Prime521(p521::ecdsa::VerifyingKey),
}
pub enum EllipticSigningKey {
Prime256(p256::ecdsa::SigningKey),
Prime384(p384::ecdsa::SigningKey),
Prime521(p521::ecdsa::SigningKey),
}
impl EllipticVerifyingKey {
pub fn x(&self) -> Vec<u8> {
match self {
Self::Prime256(key) => key.as_affine().x().to_vec(),
Self::Prime384(key) => key.as_affine().x().to_vec(),
Self::Prime521(key) => key.as_affine().x().to_vec(),
}
}
pub fn y(&self) -> Vec<u8> {
match self {
Self::Prime256(key) => key.as_affine().y().to_vec(),
Self::Prime384(key) => key.as_affine().y().to_vec(),
Self::Prime521(key) => key.as_affine().y().to_vec(),
}
}
pub fn verifies<D: Digest>(&self, signature: &[u8], message: &[u8]) -> bool {
match &self {
Self::Prime256(key) => {
let Ok(signature) = p256::ecdsa::Signature::from_slice(signature)
.or_else(|_| p256::ecdsa::Signature::from_der(signature))
else {
return false;
};
let digest = D::digest(message);
key.verify_prehash(&digest, &signature).is_ok()
}
Self::Prime384(key) => {
let Ok(signature) = p384::ecdsa::Signature::from_slice(signature)
.or_else(|_| p384::ecdsa::Signature::from_der(signature))
else {
return false;
};
let digest = D::digest(message);
key.verify_prehash(&digest, &signature).is_ok()
}
Self::Prime521(key) => {
let Ok(signature) = p521::ecdsa::Signature::from_slice(signature)
.or_else(|_| p521::ecdsa::Signature::from_der(signature))
else {
return false;
};
let digest = D::digest(message);
key.verify_prehash(&digest, &signature).is_ok()
}
}
}
pub fn from_coordinates(x: &[u8], y: &[u8]) -> Option<Self> {
if x.len() != y.len() {
return None;
}
match x.len() {
<NistP256 as elliptic_curve::Curve>::FieldBytesSize::USIZE => {
let x = Array::try_from(x).ok()?;
let y = Array::try_from(y).ok()?;
let point = p256::AffinePoint::from_coordinates(&x, &y).into_option()?;
let key = p256::ecdsa::VerifyingKey::from_affine(point).ok()?;
Some(Self::Prime256(key))
}
<NistP384 as elliptic_curve::Curve>::FieldBytesSize::USIZE => {
let x = Array::try_from(x).ok()?;
let y = Array::try_from(y).ok()?;
let point = p384::AffinePoint::from_coordinates(&x, &y).into_option()?;
let key = p384::ecdsa::VerifyingKey::from_affine(point).ok()?;
Some(Self::Prime384(key))
}
<NistP521 as elliptic_curve::Curve>::FieldBytesSize::USIZE => {
let x = Array::try_from(x).ok()?;
let y = Array::try_from(y).ok()?;
let point = p521::AffinePoint::from_coordinates(&x, &y).into_option()?;
let key = p521::ecdsa::VerifyingKey::from_affine(point).ok()?;
Some(Self::Prime521(key))
}
_ => None,
}
}
pub fn key_id(&self) -> Vec<u8> {
let x = self.x();
let y = self.y();
let digest = Sha256::new().chain_update(x).chain_update(y).finalize();
digest.to_vec()
}
}
impl EllipticSigningKey {
pub fn sign<D: Digest>(&self, message: &[u8]) -> Vec<u8> {
let digest = D::digest(message);
match &self {
Self::Prime256(key) => {
let signature: p256::ecdsa::Signature =
key.sign_prehash_with_rng(&mut rand::rng(), &digest).expect(
"the digest algorithm should produce a hash that is valid for this curve",
);
signature.to_vec()
}
Self::Prime384(key) => {
let signature: p384::ecdsa::Signature =
key.sign_prehash_with_rng(&mut rand::rng(), &digest).expect(
"the digest algorithm should produce a hash that is valid for this curve",
);
signature.to_vec()
}
Self::Prime521(key) => {
let signature: p521::ecdsa::Signature =
key.sign_prehash_with_rng(&mut rand::rng(), &digest).expect(
"the digest algorithm should produce a hash that is valid for this curve",
);
signature.to_vec()
}
}
}
pub fn verifying_key(&self) -> EllipticVerifyingKey {
match &self {
Self::Prime256(key) => EllipticVerifyingKey::Prime256(*key.verifying_key()),
Self::Prime384(key) => EllipticVerifyingKey::Prime384(*key.verifying_key()),
Self::Prime521(key) => EllipticVerifyingKey::Prime521(*key.verifying_key()),
}
}
}