Skip to content

Utils for creating canister signatures and handling canister signatures public keys.

License

Notifications You must be signed in to change notification settings

dfinity/ic-canister-sig-creation

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

27 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

IC Canister Signatures Creation

Crate for handling canister signatures public keys and creating canister signatures. Please refer to the ic-standalone-sig-verifier crate for canister signature verification.

Introduction

In order to create a canister signature, a canister needs to commit to a public key seed and a message_hash in its certified_data. This crate provides utilities to make this process as easy as possible.

For a more in-depth explanation of the concepts, see the official specification of canister signatures as well as the documentation of certified data.

Creating Signatures

Creating a signature is a two-step process:

  1. the signature has to be prepared in an update call
  2. the signature has to be retrieved in a query call

In order to bridge the two steps, the canister has to keep state about the prepared signatures:

use ic_canister_sig_creation::signature_map::SignatureMap;

thread_local! {
    /// Prepared canister signatures, no need to keep them in stable memory as they are only kept for one minute
    /// (to give clients time to do the query call).
    static SIGNATURES : RefCell<SignatureMap> = RefCell::new(SignatureMap::default());
}

Preparing a Signature

To prepare a signature on a message, add it's hash to the signature map together with the seed used to generate the public key:

use ic_canister_sig_creation::hash_bytes;

/// The signature domain should be unique for the context in which the signature is used.
const SIG_DOMAIN: &[u8] = b"ic-example-canister-sig";

fn add_signature(seed: &[u8], message: &[u8]) {
    let sig_inputs = CanisterSigInputs {
        domain: SIG_DOMAIN,
        seed,
        message,
    };
    SIGNATURES.with_borrow_mut(|sigs| {
        sigs.add_signature(&sig_inputs);
    });
}

Then update the certified_data to the new root hash of the signature map:

use ic_canister_sig_creation::signature_map::LABEL_SIG;
use ic_cdk::api::set_certified_data;

fn update_root_hash() {
    SIGNATURES.with_borrow(|sigs| {
        set_certified_data(&labeled_hash(LABEL_SIG, &sigs.root_hash()));
    })
}

Retrieving a Signature

To retrieve a prepared signature, use the get_signature_as_cbor on the SignatureMap instance:

/// The signature domain should be unique for the context in which the signature is used.
const SIG_DOMAIN: &[u8] = b"ic-example-canister-sig";

fn get_signature(seed: &[u8], message: &[u8]) -> Result<Vec<u8>, String> {
    let sig_inputs = CanisterSigInputs {
        domain: SIG_DOMAIN,
        seed,
        message,
    };
    SIGNATURES.with_borrow(|sigs| {
        sigs.get_signature_as_cbor(&sig_inputs, None)
    });
}

About

Utils for creating canister signatures and handling canister signatures public keys.

Resources

License

Code of conduct

Stars

Watchers

Forks

Contributors 3

  •  
  •  
  •  

Languages