use alloc::vec::Vec;
use p256::ecdsa::signature::{Signer as _, Verifier as _};
use sha2::Digest as _;
use super::{Hash, PncError, Sign, Suite, Verify};
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct Sha2;
impl Hash for Sha2 {
fn digest(&self, suite: Suite, data: &[u8]) -> Vec<u8> {
match suite {
Suite::EcdsaSha256 => sha2::Sha256::digest(data).to_vec(),
Suite::EcdsaSha512 => sha2::Sha512::digest(data).to_vec(),
}
}
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum SigningKey {
P256(p256::ecdsa::SigningKey),
P521(p521::ecdsa::SigningKey),
}
impl SigningKey {
pub fn p256(scalar: &[u8]) -> Result<Self, PncError> {
p256::ecdsa::SigningKey::from_slice(scalar)
.map(Self::P256)
.map_err(|_| PncError::Backend("not a valid P-256 private scalar"))
}
pub fn p521(scalar: &[u8]) -> Result<Self, PncError> {
p521::ecdsa::SigningKey::from_slice(scalar)
.map(Self::P521)
.map_err(|_| PncError::Backend("not a valid P-521 private scalar"))
}
#[must_use]
pub const fn suite(&self) -> Suite {
match self {
Self::P256(_) => Suite::EcdsaSha256,
Self::P521(_) => Suite::EcdsaSha512,
}
}
#[must_use]
pub fn verifying_key(&self) -> VerifyingKey {
match self {
Self::P256(k) => VerifyingKey::P256(*k.verifying_key()),
Self::P521(k) => VerifyingKey::P521(*k.verifying_key()),
}
}
}
impl Sign for SigningKey {
fn sign(&self, suite: Suite, data: &[u8]) -> Result<Vec<u8>, PncError> {
if suite != self.suite() {
return Err(PncError::Backend("the signing key is not on this suite's curve"));
}
Ok(match self {
Self::P256(k) => {
let sig: p256::ecdsa::Signature = k.sign(data);
sig.to_bytes().to_vec()
}
Self::P521(k) => {
let sig: p521::ecdsa::Signature = k.sign(data);
sig.to_bytes().to_vec()
}
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum VerifyingKey {
P256(p256::ecdsa::VerifyingKey),
P521(p521::ecdsa::VerifyingKey),
}
impl VerifyingKey {
pub fn p256_sec1(point: &[u8]) -> Result<Self, PncError> {
p256::ecdsa::VerifyingKey::from_sec1_bytes(point)
.map(Self::P256)
.map_err(|_| PncError::Backend("not a valid P-256 public point"))
}
pub fn p521_sec1(point: &[u8]) -> Result<Self, PncError> {
p521::ecdsa::VerifyingKey::from_sec1_bytes(point)
.map(Self::P521)
.map_err(|_| PncError::Backend("not a valid P-521 public point"))
}
#[must_use]
pub const fn suite(&self) -> Suite {
match self {
Self::P256(_) => Suite::EcdsaSha256,
Self::P521(_) => Suite::EcdsaSha512,
}
}
}
impl Verify for VerifyingKey {
fn verify(&self, suite: Suite, data: &[u8], signature: &[u8]) -> Result<(), PncError> {
if suite != self.suite() {
return Err(PncError::Backend("the verifying key is not on this suite's curve"));
}
match self {
Self::P256(k) => {
let sig = p256::ecdsa::Signature::from_slice(signature)
.map_err(|_| PncError::BadSignature)?;
k.verify(data, &sig).map_err(|_| PncError::BadSignature)
}
Self::P521(k) => {
let sig = p521::ecdsa::Signature::from_slice(signature)
.map_err(|_| PncError::BadSignature)?;
k.verify(data, &sig).map_err(|_| PncError::BadSignature)
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::pnc::Signed;
fn keys() -> (SigningKey, SigningKey) {
let mut p521_scalar = [0x17u8; 66];
p521_scalar[0] = 0x00;
(SigningKey::p256(&[0x42; 32]).unwrap(), SigningKey::p521(&p521_scalar).unwrap())
}
#[test]
fn digest_lengths_match_the_suites() {
assert_eq!(Sha2.digest(Suite::EcdsaSha256, b"x").len(), Suite::EcdsaSha256.digest_len());
assert_eq!(Sha2.digest(Suite::EcdsaSha512, b"x").len(), Suite::EcdsaSha512.digest_len());
}
#[test]
fn signatures_are_the_raw_r_s_pair() {
let (p256_key, p521_key) = keys();
assert_eq!(p256_key.sign(Suite::EcdsaSha256, b"data").unwrap().len(), 64);
assert_eq!(p521_key.sign(Suite::EcdsaSha512, b"data").unwrap().len(), 132);
}
#[test]
fn a_signature_verifies_against_its_own_key() {
for key in [keys().0, keys().1] {
let suite = key.suite();
let sig = key.sign(suite, b"data").unwrap();
key.verifying_key().verify(suite, b"data", &sig).unwrap();
assert_eq!(
key.verifying_key().verify(suite, b"other", &sig),
Err(PncError::BadSignature)
);
}
}
#[test]
fn a_public_point_round_trips_through_sec1() {
let (p256_key, p521_key) = keys();
let VerifyingKey::P256(vk) = p256_key.verifying_key() else { panic!("wrong curve") };
let point = vk.to_sec1_point(false);
assert_eq!(VerifyingKey::p256_sec1(point.as_bytes()).unwrap(), p256_key.verifying_key());
let VerifyingKey::P521(vk) = p521_key.verifying_key() else { panic!("wrong curve") };
let point = vk.to_sec1_point(false);
assert_eq!(VerifyingKey::p521_sec1(point.as_bytes()).unwrap(), p521_key.verifying_key());
}
#[test]
fn a_key_will_not_stand_in_for_the_other_curve() {
let (p256_key, p521_key) = keys();
assert!(p256_key.sign(Suite::EcdsaSha512, b"data").is_err());
assert!(p521_key.sign(Suite::EcdsaSha256, b"data").is_err());
let sig = p256_key.sign(Suite::EcdsaSha256, b"data").unwrap();
assert!(p521_key.verifying_key().verify(Suite::EcdsaSha256, b"data", &sig).is_err());
}
#[cfg(feature = "iso2")]
#[test]
fn a_real_iso2_signature_verifies() {
let key = SigningKey::p256(&[0x42; 32]).unwrap();
let fragment = [0x80, 0x11, 0x22, 0x33];
let elements = [Signed::new("ID1", &fragment)];
let signature = crate::pnc::iso2::sign(&elements, &Sha2, &key).unwrap();
crate::pnc::iso2::verify(&signature, &elements, &Sha2, &key.verifying_key()).unwrap();
let other = [0x80, 0x44];
assert!(
crate::pnc::iso2::verify(
&signature,
&[Signed::new("ID1", &other)],
&Sha2,
&key.verifying_key(),
)
.is_err()
);
}
}