use crate::groups::multiplier::ScalarMultiplier;
use crate::groups::secp256r1;
use crate::groups::secp256r1::ProjectivePoint;
use crate::hash::HashFunction;
use crate::secp256r1::conversion::{
affine_pt_arkworks_to_p256, affine_pt_p256_to_projective_arkworks, fr_p256_to_arkworks,
reduce_bytes,
};
use crate::secp256r1::{
DefaultHash, Secp256r1KeyPair, Secp256r1PublicKey, Secp256r1Signature, MULTIPLIER,
SECP256R1_SIGNATURE_LENTH,
};
use crate::traits::{RecoverableSignature, RecoverableSigner, VerifyRecoverable};
use crate::{
encoding::{Base64, Encoding},
error::FastCryptoError,
traits::{EncodeDecodeBase64, ToFromBytes},
};
use crate::{impl_base64_display_fmt, serialize_deserialize_with_to_from_bytes};
use ark_ec::CurveGroup;
use ark_ff::Field;
use ecdsa::elliptic_curve::subtle::Choice;
use ecdsa::RecoveryId;
use once_cell::sync::OnceCell;
use p256::ecdsa::{Signature as ExternalSignature, VerifyingKey};
use p256::elliptic_curve::bigint::ArrayEncoding;
use p256::elliptic_curve::point::DecompressPoint;
use p256::elliptic_curve::Curve;
use p256::{AffinePoint, NistP256, U256};
use std::fmt::{self, Debug};
pub const SECP256R1_RECOVERABLE_SIGNATURE_LENGTH: usize = SECP256R1_SIGNATURE_LENTH + 1;
#[readonly::make]
#[derive(Debug, Clone)]
pub struct Secp256r1RecoverableSignature {
pub sig: ExternalSignature,
pub bytes: OnceCell<[u8; SECP256R1_RECOVERABLE_SIGNATURE_LENGTH]>,
pub recovery_id: u8,
}
serialize_deserialize_with_to_from_bytes!(
Secp256r1RecoverableSignature,
SECP256R1_RECOVERABLE_SIGNATURE_LENGTH
);
impl ToFromBytes for Secp256r1RecoverableSignature {
fn from_bytes(bytes: &[u8]) -> Result<Self, FastCryptoError> {
if bytes.len() != SECP256R1_RECOVERABLE_SIGNATURE_LENGTH {
return Err(FastCryptoError::InputLengthWrong(
SECP256R1_RECOVERABLE_SIGNATURE_LENGTH,
));
}
let recovery_id = bytes[SECP256R1_RECOVERABLE_SIGNATURE_LENGTH - 1];
if recovery_id > 3 {
return Err(FastCryptoError::InvalidInput);
}
ExternalSignature::try_from(&bytes[..SECP256R1_RECOVERABLE_SIGNATURE_LENGTH - 1])
.map(|sig| Secp256r1RecoverableSignature {
sig,
recovery_id,
bytes: OnceCell::new(),
})
.map_err(|_| FastCryptoError::InvalidInput)
}
}
impl AsRef<[u8]> for Secp256r1RecoverableSignature {
fn as_ref(&self) -> &[u8] {
self.bytes.get_or_init::<_>(|| {
let mut bytes = [0u8; SECP256R1_RECOVERABLE_SIGNATURE_LENGTH];
bytes[..SECP256R1_RECOVERABLE_SIGNATURE_LENGTH - 1]
.copy_from_slice(self.sig.to_bytes().as_slice());
bytes[SECP256R1_RECOVERABLE_SIGNATURE_LENGTH - 1] = self.recovery_id;
bytes
})
}
}
impl std::hash::Hash for Secp256r1RecoverableSignature {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.as_ref().hash(state);
}
}
impl PartialEq for Secp256r1RecoverableSignature {
fn eq(&self, other: &Self) -> bool {
self.sig == other.sig
}
}
impl Eq for Secp256r1RecoverableSignature {}
impl_base64_display_fmt!(Secp256r1RecoverableSignature);
impl Secp256r1RecoverableSignature {
pub fn try_from_nonrecoverable(
signature: &Secp256r1Signature,
pk: &Secp256r1PublicKey,
message: &[u8],
) -> Result<Self, FastCryptoError> {
let mut recoverable_signature_bytes = [0u8; SECP256R1_RECOVERABLE_SIGNATURE_LENGTH];
recoverable_signature_bytes[0..SECP256R1_RECOVERABLE_SIGNATURE_LENGTH - 1]
.copy_from_slice(signature.as_ref());
for recovery_id in 0..4 {
recoverable_signature_bytes[SECP256R1_RECOVERABLE_SIGNATURE_LENGTH - 1] = recovery_id;
let recoverable_signature = <Secp256r1RecoverableSignature as ToFromBytes>::from_bytes(
&recoverable_signature_bytes,
)?;
if pk
.verify_recoverable(message, &recoverable_signature)
.is_ok()
{
return Ok(recoverable_signature);
}
}
Err(FastCryptoError::InvalidInput)
}
#[cfg(test)]
pub(crate) fn from_uncompressed(bytes: &[u8]) -> Result<Self, FastCryptoError> {
ExternalSignature::try_from(bytes)
.map(|sig| Secp256r1RecoverableSignature {
sig,
recovery_id: 0u8,
bytes: OnceCell::new(),
})
.map_err(|_| FastCryptoError::InvalidInput)
}
}
impl RecoverableSigner for Secp256r1KeyPair {
type PubKey = Secp256r1PublicKey;
type Sig = Secp256r1RecoverableSignature;
fn sign_recoverable_with_hash<H: HashFunction<32>>(
&self,
msg: &[u8],
) -> Secp256r1RecoverableSignature {
let (signature, is_y_odd, is_x_reduced) = self.sign_common::<H>(msg);
let recovery_id = RecoveryId::new(is_y_odd, is_x_reduced);
Secp256r1RecoverableSignature {
sig: signature,
bytes: OnceCell::new(),
recovery_id: recovery_id.to_byte(),
}
}
}
impl RecoverableSignature for Secp256r1RecoverableSignature {
type PubKey = Secp256r1PublicKey;
type Signer = Secp256r1KeyPair;
type DefaultHash = DefaultHash;
fn recover_with_hash<H: HashFunction<32>>(
&self,
msg: &[u8],
) -> Result<Secp256r1PublicKey, FastCryptoError> {
let (r, s) = self.sig.split_scalars();
let v = RecoveryId::from_byte(self.recovery_id).ok_or(FastCryptoError::InvalidInput)?;
let r_bytes = match v.is_x_reduced() {
true => U256::from(r.as_ref())
.wrapping_add(&NistP256::ORDER)
.to_be_byte_array(),
false => r.to_bytes(),
};
let big_r = AffinePoint::decompress(&r_bytes, Choice::from(v.is_y_odd() as u8));
if big_r.is_none().into() {
return Err(FastCryptoError::GeneralOpaqueError);
}
let r = fr_p256_to_arkworks(&r);
let s = fr_p256_to_arkworks(&s);
let z = reduce_bytes(&H::digest(msg).digest);
let big_r = affine_pt_p256_to_projective_arkworks(&big_r.unwrap());
let r_inv = r.inverse().ok_or(FastCryptoError::InvalidSignature)?;
let u1 = -(r_inv * z);
let u2 = r_inv * s;
let pk = MULTIPLIER
.two_scalar_mul(
&secp256r1::Scalar(u1),
&ProjectivePoint(big_r),
&secp256r1::Scalar(u2),
)
.0;
Ok(Secp256r1PublicKey {
pubkey: VerifyingKey::from_affine(affine_pt_arkworks_to_p256(&pk.into_affine()))
.map_err(|_| FastCryptoError::GeneralOpaqueError)?,
bytes: OnceCell::new(),
})
}
}
impl VerifyRecoverable for Secp256r1PublicKey {
type Sig = Secp256r1RecoverableSignature;
}