12

I have a Pallet that should verify a signature for some input data and a specific public key.
It looks like this:

fn verify(_: OriginFor<T>, data: Vec<u8>, sig: ???, from: ???)

Is this possible? What kind of signature schemes can I use?

2 Answers 2

14

Substrate provides a Verify and an IdentifyAccount trait.
They work with any signature scheme since they are just traits and can be implemented for different crypto schemata.
This is very helpful when implementing off-chain protocols like Channels, Plasmas, Rollups etc.

The Config trait of your pallet needs to be extended to support it:

/// A Signature can be verified with a specific `PublicKey`.
/// The additional traits are boilerplate.
type Signature: Verify<Signer = Self::PublicKey> + Encode + Decode + Parameter;

/// A PublicKey can be converted into an `AccountId`. This is required by the
/// `Signature` type.
/// The additional traits are boilerplate.
type PublicKey: IdentifyAccount<AccountId = Self::PublicKey> + Encode + Decode + Parameter;

This makes it possible to verify signatures within the pallet.
Using it in an Extrinsic looks like this:

fn my_verify(_: OriginFor<T>, data: Vec<u8>, sig: T::Signature, from: T::PublicKey)
{
    let ok = sig.verify(&data, &from);
    // `ok` is a bool. Use in an `if` or `ensure!`.
}

On the runtime side you can then inject the concrete crypto implementation that you want:

type Signature = sp_core::ecdsa::Signature;
type PublicKey = sp_core::ecdsa::Public;

Available are ecdsa, sr25519 and ed25519 all from sp_core.
The Merline signing context for sr25519 is b"substrate" as defined here.

4
  • 1
    That's a great one, but I thinking adding "from a pallet" to the question's title would be more assertive. Other than that, based on the title only, users can verify signatures quickly using the PolkadotJS Developer -> Sign and Verify -> Verify Signature. Feb 18, 2022 at 11:01
  • 2
    Yes thanks! Will update it. I actually used PolkadotJS to debug my signature code 😊 Feb 18, 2022 at 11:02
  • 1
    I am not familiar with how verification works, but as far as I am aware, you need to verify that certain data X was signed by a certain private key to produce signed data Y, so you run that verification process using their pubkey to produce X'. but you still need X to prove X == X' right? So where is the field that includes the original data that was signed over?
    – RoboT
    Feb 18, 2022 at 11:25
  • 3
    Signatures and Encryption have theirs keys switched. Encryption uses the Public key to encode, Signatures use the Private key to sign. So to verify a Signature, you only need to know the Public key. Signing works like this SIG = sign(PrivateKey, DATA) and verifying ok = verify(PublicKey, DATA, SIG). Feb 18, 2022 at 11:32
5

You can also see fully integrated examples of signature verification in a Pallet with:

  1. The claims pallet, which verifies Ethereum Signatures
impl<T: Config> Pallet<T> {
    // Constructs the message that Ethereum RPC's `personal_sign` and `eth_sign` would sign.
    fn ethereum_signable_message(what: &[u8], extra: &[u8]) -> Vec<u8> {
        let prefix = T::Prefix::get();
        let mut l = prefix.len() + what.len() + extra.len();
        let mut rev = Vec::new();
        while l > 0 {
            rev.push(b'0' + (l % 10) as u8);
            l /= 10;
        }
        let mut v = b"\x19Ethereum Signed Message:\n".to_vec();
        v.extend(rev.into_iter().rev());
        v.extend_from_slice(&prefix[..]);
        v.extend_from_slice(what);
        v.extend_from_slice(extra);
        v
    }

    // Attempts to recover the Ethereum address from a message signature signed by using
    // the Ethereum RPC's `personal_sign` and `eth_sign`.
    fn eth_recover(s: &EcdsaSignature, what: &[u8], extra: &[u8]) -> Option<EthereumAddress> {
        let msg = keccak_256(&Self::ethereum_signable_message(what, extra));
        let mut res = EthereumAddress::default();
        res.0
            .copy_from_slice(&keccak_256(&secp256k1_ecdsa_recover(&s.0, &msg).ok()?[..])[12..]);
        Some(res)
    }

...
  1. The crowdloan pallet which verifies multiple different kinds of signatures using the Multisignature type:
if let Some(ref verifier) = fund.verifier {
    let signature = signature.ok_or(Error::<T>::InvalidSignature)?;
    let payload = (index, &who, old_balance, value);
    let valid = payload.using_encoded(|encoded| {
        signature.verify(encoded, &verifier.clone().into_account())
    });
    ensure!(valid, Error::<T>::InvalidSignature);
}
1

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.