use crate::{
types::{Address, H256, U256},
utils::hash_message,
};
use elliptic_curve::{consts::U32, sec1::ToEncodedPoint};
use generic_array::GenericArray;
use k256::{
ecdsa::{
recoverable::{Id as RecoveryId, Signature as RecoverableSignature},
Error as K256SignatureError, Signature as K256Signature,
},
PublicKey as K256PublicKey,
};
use open_fastrlp::Decodable;
use serde::{Deserialize, Serialize};
use std::{convert::TryFrom, fmt, str::FromStr};
use thiserror::Error;
#[derive(Debug, Error)]
pub enum SignatureError {
#[error("invalid signature length, got {0}, expected 65")]
InvalidLength(usize),
#[error(transparent)]
DecodingError(#[from] hex::FromHexError),
#[error("Signature verification failed. Expected {0}, got {1}")]
VerificationError(Address, Address),
#[error(transparent)]
K256Error(#[from] K256SignatureError),
#[error("Public key recovery error")]
RecoveryError,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RecoveryMessage {
Data(Vec<u8>),
Hash(H256),
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Copy, Hash)]
pub struct Signature {
pub r: U256,
pub s: U256,
pub v: u64,
}
impl fmt::Display for Signature {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let sig = <[u8; 65]>::from(self);
write!(f, "{}", hex::encode(&sig[..]))
}
}
impl Signature {
pub fn verify<M, A>(&self, message: M, address: A) -> Result<(), SignatureError>
where
M: Into<RecoveryMessage>,
A: Into<Address>,
{
let address = address.into();
let recovered = self.recover(message)?;
if recovered != address {
return Err(SignatureError::VerificationError(address, recovered))
}
Ok(())
}
pub fn recover<M>(&self, message: M) -> Result<Address, SignatureError>
where
M: Into<RecoveryMessage>,
{
let message = message.into();
let message_hash = match message {
RecoveryMessage::Data(ref message) => hash_message(message),
RecoveryMessage::Hash(hash) => hash,
};
let (recoverable_sig, _recovery_id) = self.as_signature()?;
let verify_key = recoverable_sig
.recover_verifying_key_from_digest_bytes(message_hash.as_ref().into())?;
let public_key = K256PublicKey::from(&verify_key);
let public_key = public_key.to_encoded_point( false);
let public_key = public_key.as_bytes();
debug_assert_eq!(public_key[0], 0x04);
let hash = crate::utils::keccak256(&public_key[1..]);
Ok(Address::from_slice(&hash[12..]))
}
fn as_signature(&self) -> Result<(RecoverableSignature, RecoveryId), SignatureError> {
let recovery_id = self.recovery_id()?;
let signature = {
let mut r_bytes = [0u8; 32];
let mut s_bytes = [0u8; 32];
self.r.to_big_endian(&mut r_bytes);
self.s.to_big_endian(&mut s_bytes);
let gar: &GenericArray<u8, U32> = GenericArray::from_slice(&r_bytes);
let gas: &GenericArray<u8, U32> = GenericArray::from_slice(&s_bytes);
let sig = K256Signature::from_scalars(*gar, *gas)?;
RecoverableSignature::new(&sig, recovery_id)?
};
Ok((signature, recovery_id))
}
pub fn recovery_id(&self) -> Result<RecoveryId, SignatureError> {
let standard_v = normalize_recovery_id(self.v);
Ok(RecoveryId::new(standard_v)?)
}
#[allow(clippy::wrong_self_convention)]
pub fn to_vec(&self) -> Vec<u8> {
self.into()
}
pub(crate) fn decode_signature(buf: &mut &[u8]) -> Result<Self, open_fastrlp::DecodeError> {
let v = u64::decode(buf)?;
Ok(Self { r: U256::decode(buf)?, s: U256::decode(buf)?, v })
}
}
impl open_fastrlp::Decodable for Signature {
fn decode(buf: &mut &[u8]) -> Result<Self, open_fastrlp::DecodeError> {
Self::decode_signature(buf)
}
}
impl open_fastrlp::Encodable for Signature {
fn length(&self) -> usize {
self.r.length() + self.s.length() + self.v.length()
}
fn encode(&self, out: &mut dyn bytes::BufMut) {
self.v.encode(out);
self.r.encode(out);
self.s.encode(out);
}
}
fn normalize_recovery_id(v: u64) -> u8 {
match v {
0 => 0,
1 => 1,
27 => 0,
28 => 1,
v if v >= 35 => ((v - 1) % 2) as _,
_ => 4,
}
}
impl<'a> TryFrom<&'a [u8]> for Signature {
type Error = SignatureError;
fn try_from(bytes: &'a [u8]) -> Result<Self, Self::Error> {
if bytes.len() != 65 {
return Err(SignatureError::InvalidLength(bytes.len()))
}
let v = bytes[64];
let r = U256::from_big_endian(&bytes[0..32]);
let s = U256::from_big_endian(&bytes[32..64]);
Ok(Signature { r, s, v: v.into() })
}
}
impl FromStr for Signature {
type Err = SignatureError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.strip_prefix("0x").unwrap_or(s);
let bytes = hex::decode(s)?;
Signature::try_from(&bytes[..])
}
}
impl From<&Signature> for [u8; 65] {
fn from(src: &Signature) -> [u8; 65] {
let mut sig = [0u8; 65];
let mut r_bytes = [0u8; 32];
let mut s_bytes = [0u8; 32];
src.r.to_big_endian(&mut r_bytes);
src.s.to_big_endian(&mut s_bytes);
sig[..32].copy_from_slice(&r_bytes);
sig[32..64].copy_from_slice(&s_bytes);
sig[64] = src.v as u8;
sig
}
}
impl From<Signature> for [u8; 65] {
fn from(src: Signature) -> [u8; 65] {
<[u8; 65]>::from(&src)
}
}
impl From<&Signature> for Vec<u8> {
fn from(src: &Signature) -> Vec<u8> {
<[u8; 65]>::from(src).to_vec()
}
}
impl From<Signature> for Vec<u8> {
fn from(src: Signature) -> Vec<u8> {
<[u8; 65]>::from(&src).to_vec()
}
}
impl From<&[u8]> for RecoveryMessage {
fn from(s: &[u8]) -> Self {
s.to_owned().into()
}
}
impl From<Vec<u8>> for RecoveryMessage {
fn from(s: Vec<u8>) -> Self {
RecoveryMessage::Data(s)
}
}
impl From<&str> for RecoveryMessage {
fn from(s: &str) -> Self {
s.as_bytes().to_owned().into()
}
}
impl From<String> for RecoveryMessage {
fn from(s: String) -> Self {
RecoveryMessage::Data(s.into_bytes())
}
}
impl From<[u8; 32]> for RecoveryMessage {
fn from(hash: [u8; 32]) -> Self {
H256(hash).into()
}
}
impl From<H256> for RecoveryMessage {
fn from(hash: H256) -> Self {
RecoveryMessage::Hash(hash)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn recover_web3_signature() {
let signature = Signature::from_str(
"b91467e570a6466aa9e9876cbcd013baba02900b8979d43fe208a4a4f339f5fd6007e74cd82e037b800186422fc2da167c747ef045e5d18a5f5d4300f8e1a0291c"
).expect("could not parse signature");
assert_eq!(
signature.recover("Some data").unwrap(),
Address::from_str("2c7536E3605D9C16a7a3D7b1898e529396a65c23").unwrap()
);
}
#[test]
fn signature_from_str() {
let s1 = Signature::from_str(
"0xaa231fbe0ed2b5418e6ba7c19bee2522852955ec50996c02a2fe3e71d30ddaf1645baf4823fea7cb4fcc7150842493847cfb6a6d63ab93e8ee928ee3f61f503500"
).expect("could not parse 0x-prefixed signature");
let s2 = Signature::from_str(
"aa231fbe0ed2b5418e6ba7c19bee2522852955ec50996c02a2fe3e71d30ddaf1645baf4823fea7cb4fcc7150842493847cfb6a6d63ab93e8ee928ee3f61f503500"
).expect("could not parse non-prefixed signature");
assert_eq!(s1, s2);
}
}