Skip to content

Signer

The Karak SDK provides signers that can be used to sign messages using a key pair. It also provides traits that can be used to create custom signers for keypairs that are not yet supported in Karak SDK.

Secp256k1 Signer

For the Secp256k1 curve, we recommend using the signer implementation provided by Alloy, which is fully compatible with the Karak SDK.

BN254 Signer

For signing with the BN254 keypair, We have created a KeypairSigner that takes in the BN254 keypair, and can be used to sign messages of length 32 Bytes.

use eyre::Result;
use karak_rs::{
    bls::{keypair_signer::KeypairSigner, signature::Signature},
    kms::{
        keypair::{bn254::Keypair, traits::Keypair as KeypairTrait},
        signer::traits::Signer,
    },
};
 
fn sign_using_bn254_keypair() -> Result<Signature> {
    // generate a random keypair for the BN254 curve
    let keypair = keypair::generate();
    
    //generate a KeypairSigner from the BN254 keypair
    let keypair_signer = KeypairSigner::from(keypair.clone());
 
    // random message: that has to be of length 32
    let message = [42u8; 32];
 
    // sign message using the signer
    Ok(keypair_signer.sign_message(message).unwrap())
}

Signer Traits

The Signer trait is a generic interface for signing messages. This trait allows for the implementation of various signers that can sign messages of any type specified by the user. Any signer created using this trait would be compatible with the Karak SDK.

pub trait Signer<M: Sized> {
    type Error;
    type Signature;
 
    fn sign_message(&self, message: M) -> Result<Self::Signature, Self::Error>;
}

Generic Type

  • M: The type of message that the signer will sign. This must implement the Sized trait, ensuring that the message size is known at compile time.

Associated Types

  • Error: The error type returned if the signing process fails. This allows each implementation to define its own error types, providing detailed feedback specific to the signer.
  • Signature: The type of signature that is produced by the signer. This enables flexibility in defining different signature formats, depending on the cryptographic algorithm used.

Example Usage

The Signer trait can be implemented for various cryptographic signing mechanisms. Here is a example:

struct MySigner;
 
impl Signer<Vec<u8>> for MySigner {
    type Error = MySignerError;
    type Signature = Vec<u8>;
 
    fn sign_message(&self, message: Vec<u8>) -> Result<Self::Signature, Self::Error> {
        // Implementation of the signing logic goes here
        Ok(vec![0u8; 64]) // Placeholder signature
    }
}

Implementing Signer trait for BN254

The way the Signer is setup for the Keypair of BN254 is:

pub struct KeypairSigner {
    keypair: bn254::Keypair,
}
 
impl From<bn254::Keypair> for KeypairSigner {
    fn from(value: bn254::Keypair) -> Self {
        Self { keypair: value }
    }
}
 
impl<B: Borrow<[u8; 32]>> Signer<B> for KeypairSigner {
    type Error = KeypairSignerError;
    type Signature = Signature;
 
    /// Caller is responsible for ensuring `hash` is a 32-byte hash of some arbitrary sized message
    fn sign_message(&self, bytes: B) -> KeypairSignerResult<Signature> {
        let sk = self.keypair.secret_key();
        let hm = hash_to_g1_point(bytes.borrow());
        let sig = (hm * sk).into_affine();
 
        Ok(Signature::from(sig))
    }
}
  • hash_to_g1_point(): is a function that takes in a bytes of len 32 and converts it to a point on the base group