Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

BEEFY: Define a BeefyVerify trait for signatures #12299

Merged
merged 12 commits into from
Oct 7, 2022
13 changes: 7 additions & 6 deletions client/beefy/src/keystore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

use sp_application_crypto::RuntimeAppPublic;
use sp_application_crypto::{RuntimeAppPublic, Wraps};
use sp_core::keccak_256;
use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr};
use sp_runtime::traits::{CustomVerify, Identity, Keccak256};

use log::warn;

Expand Down Expand Up @@ -98,11 +99,11 @@ impl BeefyKeystore {
///
/// Return `true` if the signature is authentic, `false` otherwise.
pub fn verify(public: &Public, sig: &Signature, message: &[u8]) -> bool {
let msg = keccak_256(message);
let sig = sig.as_ref();
let public = public.as_ref();

sp_core::ecdsa::Pair::verify_prehashed(sig, &msg, public)
CustomVerify::<Keccak256, _, Identity>::custom_verify(
sig.as_inner_ref(),
message,
public.as_inner_ref(),
)
}
}

Expand Down
8 changes: 2 additions & 6 deletions frame/beefy-mmr/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,8 @@ where
/// Convert BEEFY secp256k1 public keys into Ethereum addresses
pub struct BeefyEcdsaToEthereum;
impl Convert<beefy_primitives::crypto::AuthorityId, Vec<u8>> for BeefyEcdsaToEthereum {
fn convert(a: beefy_primitives::crypto::AuthorityId) -> Vec<u8> {
sp_core::ecdsa::Public::try_from(a.as_ref())
.map_err(|_| {
log::error!(target: "runtime::beefy", "Invalid BEEFY PublicKey format!");
})
.unwrap_or(sp_core::ecdsa::Public::from_raw([0u8; 33]))
fn convert(beefy_id: beefy_primitives::crypto::AuthorityId) -> Vec<u8> {
sp_core::ecdsa::Public::from(beefy_id)
.to_eth_address()
.map(|v| v.to_vec())
.map_err(|_| {
Expand Down
5 changes: 5 additions & 0 deletions primitives/core/src/crypto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -987,6 +987,11 @@ pub trait IsWrappedBy<Outer>: From<Outer> + Into<Outer> {
pub trait Wraps: Sized {
/// The inner type it is wrapping.
type Inner: IsWrappedBy<Self>;

/// Get a reference to the inner type that is wrapped.
fn as_inner_ref(&self) -> &Self::Inner {
Self::Inner::from_ref(self)
}
}

impl<T, Outer> IsWrappedBy<Outer> for T
Expand Down
27 changes: 14 additions & 13 deletions primitives/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,9 +176,8 @@ impl From<Justification> for Justifications {
}
}

use traits::{Lazy, Verify};
use traits::{BlakeTwo256, Convert, CustomVerify, IdentifyAccount, Lazy, Verify};

use crate::traits::IdentifyAccount;
#[cfg(feature = "std")]
pub use serde::{de::DeserializeOwned, Deserialize, Serialize};

Expand Down Expand Up @@ -297,6 +296,13 @@ impl TryFrom<MultiSignature> for ecdsa::Signature {
}
}

struct EcdsaPublicToBlakeTwo256;
impl Convert<ecdsa::Public, AccountId32> for EcdsaPublicToBlakeTwo256 {
fn convert(a: ecdsa::Public) -> AccountId32 {
sp_io::hashing::blake2_256(a.as_ref()).into()
}
}

/// Public key for any known crypto algorithm.
#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Encode, Decode, RuntimeDebug, TypeInfo)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
Expand Down Expand Up @@ -333,7 +339,7 @@ impl traits::IdentifyAccount for MultiSigner {
match self {
Self::Ed25519(who) => <[u8; 32]>::from(who).into(),
Self::Sr25519(who) => <[u8; 32]>::from(who).into(),
Self::Ecdsa(who) => sp_io::hashing::blake2_256(who.as_ref()).into(),
Self::Ecdsa(who) => EcdsaPublicToBlakeTwo256::convert(who),
}
}
}
Expand Down Expand Up @@ -402,7 +408,7 @@ impl std::fmt::Display for MultiSigner {

impl Verify for MultiSignature {
type Signer = MultiSigner;
fn verify<L: Lazy<[u8]>>(&self, mut msg: L, signer: &AccountId32) -> bool {
fn verify<L: Lazy<[u8]>>(&self, msg: L, signer: &AccountId32) -> bool {
match (self, signer) {
(Self::Ed25519(ref sig), who) => match ed25519::Public::from_slice(who.as_ref()) {
Ok(signer) => sig.verify(msg, &signer),
Expand All @@ -412,15 +418,10 @@ impl Verify for MultiSignature {
Ok(signer) => sig.verify(msg, &signer),
Err(()) => false,
},
(Self::Ecdsa(ref sig), who) => {
let m = sp_io::hashing::blake2_256(msg.get());
match sp_io::crypto::secp256k1_ecdsa_recover_compressed(sig.as_ref(), &m) {
Ok(pubkey) =>
&sp_io::hashing::blake2_256(pubkey.as_ref()) ==
<dyn AsRef<[u8; 32]>>::as_ref(who),
_ => false,
}
},
(Self::Ecdsa(ref sig), who) =>
CustomVerify::<BlakeTwo256, _, EcdsaPublicToBlakeTwo256>::custom_verify(
sig, msg, who,
),
}
}
}
Expand Down
51 changes: 42 additions & 9 deletions primitives/runtime/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,46 @@ impl IdentifyAccount for sp_core::ecdsa::Public {
}
}

/// Means of signature verification.
///
/// Accepts custom hashing fn for the message and custom convertor fn for the signer.
pub trait CustomVerify<
acatangiu marked this conversation as resolved.
Show resolved Hide resolved
MsgHash: Hash,
AccountId: PartialEq,
SignerToAccountId: Convert<Self::Signer, AccountId>,
>
{
/// Type of the signer.
type Signer: IdentifyAccount;

/// Verify a signature.
///
/// Return `true` if signature is valid for the value.
fn custom_verify<L: Lazy<[u8]>>(&self, msg: L, signer: &AccountId) -> bool;
}

impl<
MsgHash: Hash,
AccountId: PartialEq,
SignerToAccountId: Convert<sp_core::ecdsa::Public, AccountId>,
> CustomVerify<MsgHash, AccountId, SignerToAccountId> for sp_core::ecdsa::Signature
where
<MsgHash as Hash>::Output: Into<[u8; 32]>,
{
type Signer = sp_core::ecdsa::Public;

fn custom_verify<L: Lazy<[u8]>>(&self, mut msg: L, signer: &AccountId) -> bool {
use sp_application_crypto::ByteArray;
let msg_hash = <MsgHash as Hash>::hash(msg.get()).into();
match sp_io::crypto::secp256k1_ecdsa_recover_compressed(self.as_ref(), &msg_hash)
.map(|raw_pubkey| sp_core::ecdsa::Public::from_slice(raw_pubkey.as_ref()))
{
Ok(Ok(pubkey)) => signer == &SignerToAccountId::convert(pubkey),
_ => false,
}
}
}

/// Means of signature verification.
pub trait Verify {
/// Type of the signer.
Expand Down Expand Up @@ -126,14 +166,8 @@ impl Verify for sp_core::sr25519::Signature {

impl Verify for sp_core::ecdsa::Signature {
type Signer = sp_core::ecdsa::Public;
fn verify<L: Lazy<[u8]>>(&self, mut msg: L, signer: &sp_core::ecdsa::Public) -> bool {
match sp_io::crypto::secp256k1_ecdsa_recover_compressed(
self.as_ref(),
&sp_io::hashing::blake2_256(msg.get()),
) {
Ok(pubkey) => signer.as_ref() == &pubkey[..],
_ => false,
}
fn verify<L: Lazy<[u8]>>(&self, msg: L, signer: &sp_core::ecdsa::Public) -> bool {
CustomVerify::<BlakeTwo256, _, Identity>::custom_verify(self, msg, signer)
}
}

Expand Down Expand Up @@ -2016,6 +2050,5 @@ mod tests {
assert!(ecdsa::Pair::verify(&signature, msg, &pair.public()));

assert!(signature.verify(msg, &pair.public()));
assert!(signature.verify(msg, &pair.public()));
}
}