use alloc::vec::Vec;
use k256::ecdsa::{RecoveryId, Signature, VerifyingKey};
use crate::{Ecdsa, Signer};
impl Signer {
pub fn sign(&self, digest: &[u8]) -> anyhow::Result<Vec<u8>> {
anyhow::ensure!(digest.len() == 32, "unexpected digest length.");
match self {
Signer::SecretEccNistP256(secret) => {
let (sig, recid) = secret
.sign_prehash_recoverable(digest)
.map_err(|e| anyhow::anyhow!("failed to sign digest: {e}"))?;
Ok([sig.to_bytes().as_slice(), &[recid.to_byte()]].concat())
}
}
}
}
impl Ecdsa {
pub fn recover(sig: &[u8], digest: &[u8]) -> anyhow::Result<Vec<u8>> {
anyhow::ensure!(digest.len() == 32, "unexpected digest len");
anyhow::ensure!(sig.len() == 65, "expected signature with recovery id");
let (sig, recid) = sig
.split_at_checked(64)
.ok_or_else(|| anyhow::anyhow!("invalid signature length"))?;
let recid = recid[0];
let recid = if recid > 3 {
recid.saturating_sub(27)
} else {
recid
};
let sig = Signature::try_from(sig)
.map_err(|e| anyhow::anyhow!("failed to decode signature: {e}"))?;
let recid =
RecoveryId::try_from(recid).map_err(|e| anyhow::anyhow!("invalid recovery id: {e}"))?;
let vk = VerifyingKey::recover_from_prehash(digest, &sig, recid)
.map_err(|e| anyhow::anyhow!("failed to recover pk: {e}"))?;
Ok(vk.to_sec1_bytes().to_vec())
}
}
#[cfg(test)]
mod tests {
use k256::ecdsa::SigningKey;
use proptest::prelude::*;
use rand::{RngCore as _, SeedableRng as _, rngs::StdRng};
use super::*;
#[test]
fn recover_works() {
let secret = "b0cb16ba60d3b770eb5e001efaa8f5b94270f4e8e858f673f5e896db11d21698";
let digest = "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef";
let secret = const_hex::decode(secret).unwrap();
let digest = const_hex::decode(digest).unwrap();
let signer = Signer::from_secret(&secret).unwrap();
let public = signer.to_public();
let signature = signer.sign(&digest).unwrap();
let recovered = Ecdsa::recover(&signature, &digest).unwrap();
assert_eq!(public, recovered);
}
proptest! {
#[test]
fn recover_property_check(seed: u64) {
let rng = &mut StdRng::seed_from_u64(seed);
let secret = SigningKey::random(rng);
let mut digest = [0u8; 32];
rng.fill_bytes(&mut digest);
let signer = Signer::from(secret);
let public = signer.to_public();
let signature = signer.sign(&digest).unwrap();
let recovered = Ecdsa::recover(&signature, &digest).unwrap();
assert_eq!(public, recovered);
}
}
}