# Canister signatures available to canisters on all subnets

### deploy test ii

For the convenience of testing, I removed some restrictions in ii.
The canister id of test ii in mainnet is in vxmla-4aaaa-aaaah-aausa-cai
The subnet id of the canister is gmq5v-hbozq-uui6y-o55wc-ihop3-562wb-3qspg-nnijg-npqp5-he3cj-3ae

### prepare and get delegation

Using the user number 10000, and assuming FrontendHostname is “test”, the temporarily generated session public key is [1,2,3,4], max_time_to_live is null to use the default.
call prepare_delegation:

dfx canister --network ic call internet_identity prepare_delegation '(10000:nat64, "test", vec {1:nat8;2:nat8;3:nat8;4:nat8}, null)'
(
blob "0<0\0c\06\0a+\06\01\04\01\83\b8C\01\02\03,\00\0a\00\00\00\00\00\e0\05$\01\01\e5\0f\e0\01\9b/:\95\0eI \aeWON;\13k\aai\cdu\c2F\df~GP\95\c3\04v", 1_649_338_195_236_578_418 : nat64, )  here we get this delegation’s canister signature’s public key and expiration. Convert blob to hex string(If there is a better way to convert blob to hex string please let me know): dfx canister --network ic call certificate blob2vec '(blob "0<0\0c\06\0a+\06\01\04\01\83\b8C\01\02\03,\00\0a\00\00\00\00\00\e0\05$\01\01\e5\0f\e0\01\9b/:\95\0eI \aeWON;\13k\aai\cdu\c2F\df~GP\95\c3\04v")'
(
"303c300c060a2b0601040183b8430102032c000a0000000000e005240101e50fe0019b2f3a950e4920ae574f4e3b136baa69cd75c246df7e475095c30476",
)


get the delegation:

dfx canister --network ic call internet_identity get_delegation '(10000:nat64, "test", vec {1:nat8;2:nat8;3:nat8;4:nat8}, 1_649_338_195_236_578_418 : nat64)'
(
variant {
signed_delegation = record {
signature = blob "\d9\d9\f7\a2kcertificateY\05\c8\d9\d9\f7\a3dtree\83\01\83\01\83\01\83\02Hcanister\83\01\83\01\82\04X \87I\1dA\d4\99 \fe<)\c6\b2(\8b$\be\ac\8c\f3\f5\a2*\d9\f8m\a6\e8\83\82\b1\60)\83\01\83\01\83\01\82\04X {V\041\c3\9b\f1\9c\d2\85L\e4\101\92\efU3\d5L\b4\b3\a7b\11\ae\cd\baN\5c\ed\92\83\01\83\01\82\04X \fa\27\f1\9c\84A4\e3\f5\fb\e1\8b\fd\a2T\e7\8d)\9c5&QR\87\85\f2\e3O\d5Hi\11\83\01\82\04X \cb\b6\11\bb,\8c\bd/\04\c0\cc\a4\c3\f7c\8e\dd\a7\e3\1c<\d7\c0\bef\9e \01\cc\9d/\15\83\01\83\01\83\01\83\01\82\04X \01_|o\c5\8a\c0\cc\9d\e2\b2\86\fa\97q\d2\a0\fe\ba\1b\90q\a0\c4\fe\e6cO\f6nl\9e\83\02J\00\00\00\00\00\e0\05$\01\01\83\01\83\01\83\01\83\02Ncertified_data\82\03X ^\27\15\0cK(\ad\93\88\da\c3f+9\10\cb\1c/t\1dcC\cf_G\89\b7y\16\88%\e7\82\04X \83\c5k\f1M\de=(\def\c6\92\b5\fc\9d\97\e9\dd\98[j\d7+\0f\e6\f8N\8a\8d\f3\dc\b2\82\04X \cas\a0s\11\de\dc\8a\e0<2\ccc\f6\9a~\15,\cdP%\b7\15\f2\f5\edW\d4\b4\e9\a5\07\82\04X \ecIX\8e\b57\d6g\8eqq\07%^v\86\feh\09\90\06\e6\f7\f4q\ae\a4\d3\a3\8d\e3F\82\04X \b4I\8e\97X%\deBV\b4\0b\f6\e4\de\e9\db\e4z\aa\ee\04\f1\eb\afC1\c4\ee\ber\b9\9e\82\04X \ce\cd\ac\95\b21\bb\b7\07\df\00\11S\f5\83.\88H\c0#r\f29\da8\c7>\c8\efh%\d3\82\04X \a1\b9\9b\b0\ea\af8\belD\b9\e9N\60a\ba{\e6\ea\07\1a?e\93\f2\0dJ\ee\f7\9a\aaM\82\04X &\d1]z\de\86\15\e5\0aR\af \b1r|O\ccf\87f\ee\cfnmp\27=\a9\c6\df\9c\1d\82\04X \d6\b9\d8l\a6/\02\f8\fc\b4\05\e6\9c\7f\c63=\a4\60T\8b\0f\e0\80\bf\04\fb&\1f\a7+U\82\04X ?\d7\80R\8a\c7B\da\f3\1e\0c\17\5c\aaru\8b\fd9?\f2\c3\b9%\c9\cb\ae\1a\bb\e9\d2\09\82\04X FC\b1&\16\df\080u\f1\89\22\ec;SA\c1\12\f4\d3\a5$\06v\c0\b7\02\f6$\8c\7f\9c\82\04X \b7\d0$X8\98\11i\dcbX#\eb\c5\15!c\e0\05\81\d7\19\9awZ\97\1c\93\88\ce\0f\e4\82\04X \89=<\b7\5c\19A\8825\a4\1d\c6\132\c1\8f\10\99|\c6\91\a4\9d\07\09*Kq\d1\8f\91\83\01\82\04X \86 \e0\0e&W\c0\db\ddV\9cN\f5\7f\a9\8cU\cd\83x\d3\e1\93I%1C\07\98i\c4#\83\02Dtime\82\03I\d8\f2\dd\ba\ba\dc\e7\f1\16isignatureX0\83<\a2\01\96\84y\8a\fb\9b\80\f5R\e84A\d1Tr\bf\dc\bcG\00\b7\1d+mch\c0\8b\82\e0,\0eNJ\8d]\81\8a\f5\b4\99:\f4\05jdelegation\a2isubnet_idX\1d.\cc)D{\0e\efl$\1d\cf\df}\ab\07p\93\cc\d6\a1&k\e0\fe\9c\9b\12v\02kcertificateY\021\d9\d9\f7\a2dtree\83\01\82\04X ?\bd\95\14c\82\1bOg\e3\da_\91zvR\bf\1e\b1-|4\a9\dc\9d\9ev%>\1am\c2\83\01\83\02Fsubnet\83\01\83\01\82\04X \82\db\90=w:\9f \c0lUY\fe\ce\d6\e1\aa\fe\b3(\ef\ad\1aH\b31c\a1\07{\86\5c\83\01\83\01\83\01\83\02X\1d.\cc)D{\0e\efl$\1d\cf\df}\ab\07p\93\cc\d6\a1&k\e0\fe\9c\9b\12v\02\83\01\83\02Ocanister_ranges\82\03X\1b\d9\d9\f7\81\82J\00\00\00\00\00\e0\00\00\01\01J\00\00\00\00\00\ef\ff\ff\01\01\83\02Jpublic_key\82\03X\850\81\820\1d\06\0d+\06\01\04\01\82\dc|\05\03\01\02\01\06\0c+\06\01\04\01\82\dc|\05\03\02\01\03a\00\91T\1c\dc{e\c4\82\82\86\c9\11\60-\948\deVI\d6\98\b6\0f\c0j\ecsX\93\95\d0\bc\a7\17FRN\d2\ff\17\b2\c8\da\9f\bc\89\7f\0f\07\a4\0b Hq\b6\fe\96\d4^\f1\0bQ\d1\f1\d50\d0g\9a]\b8-\e9i)\80_\a1|sy\94\eb\cc#\12\d2\a2[\d9GG\ec\f8\f3K\82\04X 6\97}.\b5x\1a0\f3\92\aaI\b6\8a\99\e7R\e3\f1\80\e7\d6\c6]\c1\15[\ac\27 \96\03\82\04X p\ff\c8\b0t\ec?\16\c6<N\f6{\ff\fa\08o\81\ab\d7\1c\92\ca+\fbX\a0\fb_o\9a\18\82\04X \c8\16F\e7\cf\13\afK\bdBv\c1\d1&F\f1\c7\86\ca~\fc\e5\05\b9\9f\1e\e9\22\a1mw>\82\04X \60\0b8U\a1dvn\e7\1c\11\8a\9a?\ed\7f\eaq\f5\d0\fb\cb\5c\e6\e2\b4\27\01$\abG\85\83\02Dtime\82\03I\bc\88\9e\9f\96\f1\d5\f1\16isignatureX0\a5\d6\8b;\c1\88\11+f\ce\90O\bc\12\1dd\ea\1a\d7\b5\81\92!P\92\e7\0cl\a2\bb\e0\1b\82[\b0j\05\89\99\f5\10\8d\f6\c9E%G2dtree\83\01\82\04X 3\a4%\ffd\97\a3syQBZ\0bD\e5_}\1b|\d4D\a1_\ab\8fU\ac\a6\82v\d5k\83\02Csig\83\02X \f4\f2Ctlm\0a\89F\12\9a\a9\98qi \16\b3\d5\b9\99\f4\e6N\b5\8e<\ec\11\03\ef\cf\83\02X \90]\da\a2\c5\0e\e2q\22\d5\1b\b1\1d\fa0\b5\c7\f8\ff\5cES\18\08_2>\b9\e0\ee\c9\b1\82\[email protected]";
delegation = record {
pubkey = blob "\01\02\03\04";
targets = null;
expiration = 1_649_338_195_236_578_418 : nat64;
};
}
},
)


the signature hex string:

d9d9f7a26b63657274696669636174655905c8d9d9f7a3647472656583018301830183024863616e6973746572830183018204582087491d41d49920fe3c29c6b2288b24beac8cf3f5a22ad9f86da6e88382b16029830183018301820458207b560431c39bf19cd2854ce4103192ef5533d54cb4b3a76211aecdba4e5ced928301830182045820fa27f19c844134e3f5fbe18bfda254e78d299c352651528785f2e34fd5486911830182045820cbb611bb2c8cbd2f04c0cca4c3f7638edda7e31c3cd7c0be669e2001cc9d2f15830183018301830182045820015f7c6fc58ac0cc9de2b286fa9771d2a0feba1b9071a0c4fee6634ff66e6c9e83024a0000000000e00524010183018301830183024e6365727469666965645f64617461820358205e27150c4b28ad9388dac3662b3910cb1c2f741d6343cf5f4789b779168825e78204582083c56bf14dde3d28de66c692b5fc9d97e9dd985b6ad72b0fe6f84e8a8df3dcb282045820ca73a07311dedc8ae03c32cc63f69a7e152ccd5025b715f2f5ed57d4b4e9a50782045820ec49588eb537d6678e717107255e7686fe68099006e6f7f471aea4d3a38de34682045820b4498e975825de4256b40bf6e4dee9dbe47aaaee04f1ebaf4331c4eebe72b99e82045820cecdac95b231bbb707df001153f5832e8848c02372f239da38c73ec8ef6825d382045820a1b99bb0eaaf38be6c44b9e94e6061ba7be6ea071a3f6593f20d4aeef79aaa4d8204582026d15d7ade8615e50a52af20b1727c4fcc668766eecf6e6d70273da9c6df9c1d82045820d6b9d86ca62f02f8fcb405e69c7fc6333da460548b0fe080bf04fb261fa72b55820458203fd780528ac742daf31e0c175caa72758bfd393ff2c3b925c9cbae1abbe9d209820458204643b12616df083075f18922ec3b5341c112f4d3a5240676c0b702f6248c7f9c82045820b7d0245838981169dc625823ebc5152163e00581d7199a775a971c9388ce0fe482045820893d3cb75c1941883235a41dc61332c18f10997cc691a49d07092a4b71d18f918301820458208620e00e2657c0dbdd569c4ef57fa98c55cd8378d3e19349253143079869c42383024474696d65820349d8f2ddbabadce7f116697369676e61747572655830833ca2019684798afb9b80f552e83441d15472bfdcbc4700b71d2b6d6368c08b82e02c0e4e4a8d5d818af5b4993af4056a64656c65676174696f6ea2697375626e65745f6964581d2ecc29447b0eef6c241dcfdf7dab077093ccd6a1266be0fe9c9b1276026b6365727469666963617465590231d9d9f7a264747265658301820458203fbd951463821b4f67e3da5f917a7652bf1eb12d7c34a9dc9d9e76253e1a6dc283018302467375626e6574830183018204582082db903d773a9f20c06c5559feced6e1aafeb328efad1a48b33163a1077b865c8301830183018302581d2ecc29447b0eef6c241dcfdf7dab077093ccd6a1266be0fe9c9b127602830183024f63616e69737465725f72616e6765738203581bd9d9f781824a0000000000e0000001014a0000000000efffff010183024a7075626c69635f6b657982035885308182301d060d2b0601040182dc7c0503010201060c2b0601040182dc7c0503020103610091541cdc7b65c4828286c911602d9438de5649d698b60fc06aec73589395d0bca71746524ed2ff17b2c8da9fbc897f0f07a40b204871b6fe96d45ef10b51d1f1d530d0679a5db82de96929805fa17c737994ebcc2312d2a25bd94747ecf8f34b8204582036977d2eb5781a30f392aa49b68a99e752e3f180e7d6c65dc1155bac272096038204582070ffc8b074ec3f16c63c4ef67bfffa086f81abd71c92ca2bfb58a0fb5f6f9a1882045820c81646e7cf13af4bbd4276c1d12646f1c786ca7efce505b99f1ee922a16d773e82045820600b3855a164766ee71c118a9a3fed7fea71f5d0fbcb5ce6e2b4270124ab478583024474696d65820349bc889e9f96f1d5f116697369676e61747572655830a5d68b3bc188112b66ce904fbc121d64ea1ad7b58192215092e70c6ca2bbe01b825bb06a058999f5108df6c945254732647472656583018204582033a425ff6497a3737951425a0b44e55f7d1b7cd444a15fab8f55aca68276d56b83024373696783025820f4f243746c6d0a8946129aa99871692016b3d5b999f4e64eb58e3cec1103efcf83025820905ddaa2c50ee27122d51bb11dfa30b5c7f8ff5c455318085f323eb9e0eec9b1820340


### get the subnet root key

result(the root key of subnets):

PublicKey { version: 0, algorithm: ThresBls12381, key_value: [145, 84, 28, 220, 123, 101, 196, 130, 130, 134, 201, 17, 96, 45, 148, 56, 222, 86, 73, 214, 152, 182, 15, 192, 106, 236, 115, 88, 147, 149, 208, 188, 167, 23, 70, 82, 78, 210, 255, 23, 178, 200, 218, 159, 188, 137, 127, 15, 7, 164, 11, 32, 72, 113, 182, 254, 150, 212, 94, 241, 11, 81, 209, 241, 213, 48, 208, 103, 154, 93, 184, 45, 233, 105, 41, 128, 95, 161, 124, 115, 121, 148, 235, 204, 35, 18, 210, 162, 91, 217, 71, 71, 236, 248, 243, 75], proof_data: None }


### get the msg_hash

the delegation_signature_msg_hash_candid is the query interface for delegation_signature_msg_hash

dfx canister --network ic call internet_identity delegation_signature_msg_hash_candid '(record {pubkey=vec{1:nat8;2:nat8;3:nat8;4:nat8;};targets=null;expiration=1_649_338_195_236_578_418:nat64;})'
(
blob "\90]\da\a2\c5\0e\e2q\22\d5\1b\b1\1d\fa0\b5\c7\f8\ff\5cES\18\08_2>\b9\e0\ee\c9\b1",
)


the hex string is 905ddaa2c50ee27122d51bb11dfa30b5c7f8ff5c455318085f323eb9e0eec9b1

### verify

use ic_crypto_internal_basic_sig_iccsa::{api::{verify, public_key_bytes_from_der}, types::{SignatureBytes, PublicKeyBytes}};
use ic_crypto_internal_types::sign::threshold_sig::public_key::bls12_381;
use ic_types::crypto::threshold_sig::ThresholdSigPublicKey;

fn main() {
let msg = hex::decode("905ddaa2c50ee27122d51bb11dfa30b5c7f8ff5c455318085f323eb9e0eec9b1").unwrap();
let sig = SignatureBytes(sig_vec);

let pub_vec = hex::decode("303c300c060a2b0601040183b8430102032c000a0000000000e005240101e50fe0019b2f3a950e4920ae574f4e3b136baa69cd75c246df7e475095c30476").unwrap();
let pk = public_key_bytes_from_der(&pub_vec).unwrap();
let root_public_key_bytes:[u8;96] = [145, 84, 28, 220, 123, 101, 196, 130, 130, 134, 201, 17, 96, 45, 148, 56, 222, 86, 73, 214, 152, 182, 15, 192, 106, 236, 115, 88, 147, 149, 208, 188, 167, 23, 70, 82, 78, 210, 255, 23, 178, 200, 218, 159, 188, 137, 127, 15, 7, 164, 11, 32, 72, 113, 182, 254, 150, 212, 94, 241, 11, 81, 209, 241, 213, 48, 208, 103, 154, 93, 184, 45, 233, 105, 41, 128, 95, 161, 124, 115, 121, 148, 235, 204, 35, 18, 210, 162, 91, 217, 71, 71, 236, 248, 243, 75];
let bls_pub = bls12_381::PublicKeyBytes(root_public_key_bytes);
let root_pubkey = ThresholdSigPublicKey::from(bls_pub);
let result = verify(&msg, sig, pk, &root_pubkey);
println!("{:?}", result);
}


### result

     Finished dev [unoptimized + debuginfo] target(s) in 1.73s
Running /Users/flyq/workspace/github/q/cansig/target/debug/delegation


verify failed.
Please where did I make a mistake and why is it not verified?

1 Like

Hey @flyq! Let’s start at the top. What you see in prepare_delegation looks good, the hex string you see there is actually a DER-encoded “public key” type that we specified. The delegation signature also looks reasonable, it’s a huge CBOR map – there is an online converter for that if you want to peek into it. It looks like a canister signature to me. In particular, if you peek into the CBOR map, you see that you’re computing the correct value in your function delegation_signature_msg_hash. Which is good.

I think what is probably the issue is that the (ingress message) delegation you have a certificate from gmq5v, and since that subnet is not the root subnet, the certificate also contains a (subnet) delegation from the root subnet. You can see that in the CBOR: The signature contains a certificate, which is again a CBOR map. If you paste that to cbor.me again, you see that that again contains a delegation. So the right way of verifying this would be with respect to the root subnet key (i.e. tdb26).

@frederikrothenberger knows the implementation better than I do, though, so he may want to have a look as well.

Thanks!

Yes, the first time I used

let pk = PublicKeyBytes(pub_vec);


then the good error message tells me that the public key is malformed and the buffer length is too long. Then I changed to

let pk = public_key_bytes_from_der(&pub_vec).unwrap();


I wasn’t sure whether the msg parameter required by verify() is the hash returned by delegation_signature_msg_hash before. The first time I used the session public key, which is [1,2,3,4], and then guessed it was delegation_signature_msg_hash

Yes! probably! I will try it and try decoding the signature according to your suggestion,

1 Like

Somewhat related question, but what is the difference between “certified data” and “certified variables”? Are they used interchangeably?

Yes. I’ve been using the term “certified variable” since that’s how we’ve been calling the feature during the design phase. “Certified data” is the correct name according to the specification. Sorry for any confusion caused by this.

I change the root key to nns subnet, and it pass the verify_certified_vars_certificate, but it faild in lookup_path_in_tree, So there is a probability that the problem occurs on msg

what happened when call ic_cdk::api::set_certified_data(Hash);, is there any src code or doc?

After reading this, I’m still not sure how a canister signature is used.

Certified data is a mechanism for a subnet to sign an arbitrary 32-byte blob. It can be used to implement certified assets, which allows a client to verify that an asset is indeed certified by the Internet Computer.

In the case of canister signatures, they seem to not be returned in ingress responses but rather passed to a canister in ingress requests. Is that right? (I’m unclear if inter-canister calls can also use canister signatures.)

If so, where in the request would a client put the canister signature? I thought it would be sender_sig, but that seems to be signed by the private key corresponding to the client’s Principal, which is totally distinct from a canister signature.

The interface specification isn’t very clear about the high-level user flow.

I use “certified data” for the system level feature, and “certified variables” the canister level feature that can be built on top of the certified data mechanism.

Depends on whether there is a delegation involved. Without a delegation, it really is the sender_sig, and the principal is derived from the canister signature “public key” (quotation marks because it isn’t really a public key, as there is no private key). If there is a delegation involved then the sender_sig is, IIRC, the signature from the session key, and the canister signature signs the delegation to that session key.

I’m not sure I fully understand. I thought sender_sig was a request field that predates canister signatures.

Do you mean that this new canister signature feature simply leverages this existing field? Is it accurate to say that canister signatures only affect ingress messages and NOT inter-canister calls? Is it also accurate to say that a canister signature necessarily involves delegation, and always requires a separate query method to actually fetch the delegation (i.e. a standard ingress response doesn’t contain the delegation)?

@bjoern Do you mind clarifying? Thanks.

1 Like

Good questions! Maybe I should write one of these in-depths blog posts… (but it’s such a niche feature and with ECDSA support basically obsolete)

Yes! The sender_sig scheme was, from the beginning, “crypto-scheme-agile” and set up to support additional cryptographic schemes. The spec says about that:

The IC supports multiple signature schemes, with details given in the following subsections. For each scheme, we specify the data encoded in the public key (which is always DER-encoded, and indicates the scheme to use) as well as the form of the signatures (which are opaque blobs for the purposes of the rest of this specification).

So when we added the Canister Signature Scheme, we made sure it fits this model, by letting it’s “public keys” be DER-encoded with OID 1.3.6.1.4.1.56387.1.2. This way we “just” had to extend the crypto component of the replica with the new scheme, but didn’t have to change how requests or delegations work at all. Quite neat, isn’t it?

Absolutely accurate.

I could imagine allowing a canister to make an inter-canister call with the sender set to the principal derived from a Canister Signature public key owned by that canister, but that is not there yet.

No, the canister signature scheme could be used to sign a request directly. But the only use-case so far (Internet Identity) works better if the canister signs a delegation to a session key that then can be used for multiple requests.

Almost: Because Canister Signatures are built on Certified Data, they can be fetched from a canister only via a query call. But this is independent of whether the signature signs a delegation, a request, or something completely different – after all, you could use Canister Signatures for other things than ingress requests!

3 Likes

Almost: Because Canister Signatures are built on Certified Data, they can be fetched from a canister only via a query call. But this is independent of whether the signature signs a delegation, a request, or something completely different – after all, you could use Canister Signatures for other things than ingress requests!

At this point, isn’t a canister signature just “anything stored in a canister’s Certified Data that the canister’s subnet signs”? It doesn’t seem different from, say, a certified variable, which is signed as well.

### ECDSA:

advantage: Simple and clear interface, separated by derivation path. It is the only option if used for bitcoin, ethereum etc.

shortcoming： Complex calculations in system, expensive cycles consumption, and long wait for update call.

### Certificate Vars or Canister signature

advantiage: A sufficiently mature technology, verified by some scenarios. ic_cdk::api::set_certified_data cheap cycles consumption.

shortcoming：A complex tree needs to be maintained in the business logic, and the verification of the cert is also complex, higher learning and use threshold

Therefore, canister signature and ecdsa have different suitable scenarios. If dfinity-team add a system interface for bls signature like ecdsa, they can reuse some infrastructure of chainkey, and it is less computation and faster than ecdsa. In this way, you can get the advantages of canister signature and ecdsa.

I expect the team to consider this proposal @bjoern @nomeata @diegop

2 Likes

Very close: It is “something stored in the Certified Data” that follows a specific format that will be understood by the replica when an agent uses that as a signature.

Certified Data is a very generic feature, and you always need a protocol on top of it to give it meaning, such as Canister Signatures for signing ingress, or Certified HTTP Assets for the secure HTTP.

Yes, in all these applications the canister essentially signs “something”, but they are still different protocols (and need to be).

2 Likes

Great response, thanks for clarifying.