#dfinity #canister #utility

no-std ic-canister-sig-creation

Library for creating canister signatures on the Internet Computer

4 stable releases

1.3.0 May 15, 2025
1.2.0 Mar 10, 2025
1.1.0 Aug 7, 2024
1.0.1 Jun 28, 2024

#23 in #canister

Download history 1407/week @ 2025-05-27 833/week @ 2025-06-03 1314/week @ 2025-06-10 761/week @ 2025-06-17 911/week @ 2025-06-24 1093/week @ 2025-07-01 872/week @ 2025-07-08 678/week @ 2025-07-15 848/week @ 2025-07-22 577/week @ 2025-07-29 853/week @ 2025-08-05 1084/week @ 2025-08-12 1987/week @ 2025-08-19 2939/week @ 2025-08-26 2558/week @ 2025-09-02 2296/week @ 2025-09-09

9,988 downloads per month
Used in 16 crates (4 directly)

Apache-2.0

35KB
601 lines

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)
    });
}

Dependencies

~3–12MB
~124K SLoC