avalanche-types 0.0.54

Avalanche types
Documentation
use std::io::{self, Error, ErrorKind};

use ecdsa::signature::DigestVerifier;
use k256::ecdsa::{digest::Digest, VerifyingKey};

/// The length of recoverable ECDSA signature.
/// "github.com/decred/dcrd/dcrec/secp256k1/v3/ecdsa.SignCompact" outputs
/// 65-byte signature -- see "compactSigSize"
/// ref. "avalanchego/utils/crypto.PrivateKeySECP256K1R.SignHash"
/// ref. https://pkg.go.dev/github.com/ava-labs/avalanchego/utils/crypto#SECP256K1RSigLen
/// ref. "secp256k1::constants::SCHNORR_SIGNATURE_SIZE" + 1
pub const LEN: usize = 65;

/// Represents Ethereum-style "recoverable signatures".
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Sig(k256::ecdsa::recoverable::Signature);

impl Sig {
    /// Loads the recoverable signature from the bytes.
    pub fn from_bytes(b: &[u8]) -> io::Result<Self> {
        if b.len() != LEN {
            return Err(Error::new(
                ErrorKind::InvalidInput,
                "invalid signature length",
            ));
        }

        let sig = k256::ecdsa::recoverable::Signature::try_from(b).map_err(|e| {
            Error::new(
                ErrorKind::Other,
                format!("failed to load recoverable signature {}", e),
            )
        })?;
        Ok(Self(sig))
    }

    /// Converts the signature to bytes.
    pub fn to_bytes(&self) -> [u8; LEN] {
        let mut b = [0u8; LEN];
        b.copy_from_slice(self.0.as_ref());
        b
    }

    /// Recovers the public key from the 32-byte SHA256 output message using its signature.
    /// TODO: fix
    pub fn recover_public_key(
        &self,
        digest: &[u8],
    ) -> io::Result<(crate::key::secp256k1::public_key::Key, VerifyingKey)> {
        // NOT WORKING because "digest" is already hashed
        // let vkey = self.0.recover_verifying_key(digest).map_err(|e| {
        //     Error::new(
        //         ErrorKind::Other,
        //         format!("failed recover_verifying_key {}", e),
        //     )
        // })?;

        // NOT WORKING
        // use ecdsa::signature::PrehashSignature as PrehashSignatureT;
        // use k256::ecdsa::{digest::Digest, Signature};
        // let prehash = <Signature as PrehashSignatureT>::Digest::new_with_prefix(digest);
        // let vkey = self
        //     .0
        //     .recover_verifying_key_from_digest(prehash)
        //     .map_err(|e| {
        //         Error::new(
        //             ErrorKind::Other,
        //             format!("failed recover_verifying_key {}", e),
        //         )
        //     })?;

        // let mut prehash = sha2::Sha256::default();
        // prehash.update(digest);
        // or
        let prehash = sha2::Sha256::new_with_prefix(digest);

        let vkey = self
            .0
            .recover_verifying_key_from_digest(prehash.clone())
            .map_err(|e| {
                Error::new(
                    ErrorKind::Other,
                    format!("failed recover_verifying_key {}", e),
                )
            })?;
        assert!(vkey.verify_digest(prehash, &self.0).is_ok());

        Ok((vkey.into(), vkey))
    }
}

impl From<k256::ecdsa::recoverable::Signature> for Sig {
    fn from(sig: k256::ecdsa::recoverable::Signature) -> Self {
        Self(sig)
    }
}

impl From<Sig> for k256::ecdsa::recoverable::Signature {
    fn from(sig: Sig) -> Self {
        sig.0
    }
}

impl From<Sig> for [u8; LEN] {
    fn from(sig: Sig) -> Self {
        sig.to_bytes()
    }
}

/// RUST_LOG=debug cargo test --package avalanche-types --lib -- key::secp256k1::signature::test_signature --exact --show-output
#[test]
fn test_signature() {
    use ring::digest::{digest, SHA256};

    let _ = env_logger::builder()
        .filter_level(log::LevelFilter::Info)
        .is_test(true)
        .try_init();

    let pk = crate::key::secp256k1::private_key::Key::generate().unwrap();
    let _pubkey1 = pk.to_public_key();

    let msg: Vec<u8> = random_manager::bytes(100).unwrap();
    let hashed: Vec<u8> = digest(&SHA256, &msg).as_ref().into();

    let sig = pk.sign_digest(&hashed).unwrap();
    assert_eq!(sig.to_bytes().len(), crate::key::secp256k1::signature::LEN);

    // TODO: fix
    // let (pubkey2, _verifying_key) = sig.recover_public_key(&hashed).unwrap();
    // assert_eq!(pubkey1.to_eth_address(), pubkey2.to_eth_address());
    // assert_eq!(pubkey1, pubkey2);
}