Crate for handling canister signatures public keys and creating canister signatures. Please refer to the ic-standalone-sig-verifier crate for canister signature verification.
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 a signature is a two-step process:
- the signature has to be prepared in an
update
call - 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());
}
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()));
})
}
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)
});
}