use crate::hash::HashFunction;
use crate::secp256k1::{DefaultHash, Secp256k1KeyPair, Secp256k1PublicKey, Secp256k1Signature};
use crate::traits::{RecoverableSignature, RecoverableSigner, VerifyRecoverable};
use crate::{
encoding::{Base64, Encoding},
error::FastCryptoError,
impl_base64_display_fmt, serialize_deserialize_with_to_from_bytes,
traits::{EncodeDecodeBase64, ToFromBytes},
};
use once_cell::sync::{Lazy, OnceCell};
pub use rust_secp256k1::ecdsa::Signature as Secp256k1Sig;
use rust_secp256k1::{
constants,
ecdsa::{RecoverableSignature as ExternalRecoverableSignature, RecoveryId},
All, Message, Secp256k1,
};
use std::fmt::{self, Debug};
pub static SECP256K1: Lazy<Secp256k1<All>> = Lazy::new(rust_secp256k1::Secp256k1::new);
pub const SECP256K1_RECOVERABLE_SIGNATURE_SIZE: usize = constants::COMPACT_SIGNATURE_SIZE + 1;
#[readonly::make]
#[derive(Debug, Clone)]
pub struct Secp256k1RecoverableSignature {
pub sig: ExternalRecoverableSignature,
pub bytes: OnceCell<[u8; SECP256K1_RECOVERABLE_SIGNATURE_SIZE]>,
}
serialize_deserialize_with_to_from_bytes!(
Secp256k1RecoverableSignature,
SECP256K1_RECOVERABLE_SIGNATURE_SIZE
);
impl ToFromBytes for Secp256k1RecoverableSignature {
fn from_bytes(bytes: &[u8]) -> Result<Self, FastCryptoError> {
if bytes.len() != SECP256K1_RECOVERABLE_SIGNATURE_SIZE {
return Err(FastCryptoError::InputLengthWrong(
SECP256K1_RECOVERABLE_SIGNATURE_SIZE,
));
}
RecoveryId::from_i32(bytes[SECP256K1_RECOVERABLE_SIGNATURE_SIZE - 1] as i32)
.and_then(|rec_id| {
ExternalRecoverableSignature::from_compact(
&bytes[..(SECP256K1_RECOVERABLE_SIGNATURE_SIZE - 1)],
rec_id,
)
.map(|sig| Secp256k1RecoverableSignature {
sig,
bytes: OnceCell::new(),
})
})
.map_err(|_| FastCryptoError::InvalidInput)
}
}
impl AsRef<[u8]> for Secp256k1RecoverableSignature {
fn as_ref(&self) -> &[u8] {
self.bytes.get_or_init::<_>(|| {
let mut bytes = [0u8; SECP256K1_RECOVERABLE_SIGNATURE_SIZE];
let (recovery_id, sig) = self.sig.serialize_compact();
bytes[..(SECP256K1_RECOVERABLE_SIGNATURE_SIZE - 1)].copy_from_slice(&sig);
bytes[SECP256K1_RECOVERABLE_SIGNATURE_SIZE - 1] = recovery_id.to_i32() as u8;
bytes
})
}
}
impl std::hash::Hash for Secp256k1RecoverableSignature {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.as_ref().hash(state);
}
}
impl PartialEq for Secp256k1RecoverableSignature {
fn eq(&self, other: &Self) -> bool {
self.sig == other.sig
}
}
impl Eq for Secp256k1RecoverableSignature {}
impl_base64_display_fmt!(Secp256k1RecoverableSignature);
impl Secp256k1RecoverableSignature {
pub fn try_from_nonrecoverable(
signature: &Secp256k1Signature,
pk: &Secp256k1PublicKey,
message: &[u8],
) -> Result<Self, FastCryptoError> {
let mut recoverable_signature_bytes = [0u8; SECP256K1_RECOVERABLE_SIGNATURE_SIZE];
recoverable_signature_bytes[0..SECP256K1_RECOVERABLE_SIGNATURE_SIZE - 1]
.copy_from_slice(signature.as_ref());
for recovery_id in 0..4 {
recoverable_signature_bytes[SECP256K1_RECOVERABLE_SIGNATURE_SIZE - 1] = recovery_id;
let recoverable_signature = <Secp256k1RecoverableSignature as ToFromBytes>::from_bytes(
&recoverable_signature_bytes,
)?;
if pk
.verify_recoverable(message, &recoverable_signature)
.is_ok()
{
return Ok(recoverable_signature);
}
}
Err(FastCryptoError::InvalidInput)
}
}
impl RecoverableSignature for Secp256k1RecoverableSignature {
type PubKey = Secp256k1PublicKey;
type Signer = Secp256k1KeyPair;
type DefaultHash = DefaultHash;
fn recover_with_hash<H: HashFunction<32>>(
&self,
msg: &[u8],
) -> Result<Secp256k1PublicKey, FastCryptoError> {
match Message::from_slice(&H::digest(msg).digest) {
Ok(message) => match self.sig.recover(&message) {
Ok(pubkey) => Secp256k1PublicKey::from_bytes(pubkey.serialize().as_slice()),
Err(_) => Err(FastCryptoError::GeneralOpaqueError),
},
Err(_) => Err(FastCryptoError::InvalidInput),
}
}
}
impl RecoverableSigner for Secp256k1KeyPair {
type PubKey = Secp256k1PublicKey;
type Sig = Secp256k1RecoverableSignature;
fn sign_recoverable_with_hash<H: HashFunction<32>>(
&self,
msg: &[u8],
) -> Secp256k1RecoverableSignature {
let secp = Secp256k1::signing_only();
let message = Message::from_slice(H::digest(msg).as_ref()).unwrap();
Secp256k1RecoverableSignature {
sig: secp.sign_ecdsa_recoverable(&message, &self.secret.privkey),
bytes: OnceCell::new(),
}
}
}
impl VerifyRecoverable for Secp256k1PublicKey {
type Sig = Secp256k1RecoverableSignature;
}