From 814a0e53841f0b3b487b6007f10fc68056894804 Mon Sep 17 00:00:00 2001 From: Jack Lloyd Date: Fri, 26 Sep 2025 15:31:23 -0400 Subject: [PATCH] CRP-2814 Add examples of offline key derivation for threshold ECDSA and Schnorr Using the Rust `ic-pub-key` and TypeScript `ic-pub-key` libraries --- .../network-features/signatures/t-ecdsa.mdx | 54 ++++++++++++++++- .../network-features/signatures/t-schnorr.mdx | 59 +++++++++++++++++-- 2 files changed, 105 insertions(+), 8 deletions(-) diff --git a/docs/building-apps/network-features/signatures/t-ecdsa.mdx b/docs/building-apps/network-features/signatures/t-ecdsa.mdx index 1c55d67622..5d8a83ebe3 100644 --- a/docs/building-apps/network-features/signatures/t-ecdsa.mdx +++ b/docs/building-apps/network-features/signatures/t-ecdsa.mdx @@ -98,7 +98,49 @@ https://github.com/dfinity/examples/blob/master/rust/threshold-ecdsa/src/ecdsa_e ## Obtaining public keys To verify a signature, the public key that corresponds to the private key that was used to sign the message is needed. Furthermore, the public key can be used to derive addresses controlled by the canister on various blockchain networks. -ICP provides the `ecdsa_public_key` method to obtain public keys. This method uses the following Candid interface: + +There are two methods available for obtaining the public key associated with a particular canister ID and derivation path. The derivation process is deterministic and public; given the master public key along with the canister ID and derivation path, the same key is always produced, and no secret is required in order to compute the public key. The first method is *offline*: the key is computed using only local computation in a browser or other application. The other is *online*, using a call to the management canister interface. + +These example snippets demonstrate deriving keys without requiring management canister calls, using the `ic-pub-key` Rust and TypeScript libraries + + + + +```rust +let canister_id = ic_pub_key::CanisterId::from_str("h5jwf-5iaaa-aaaan-qmvoa-cai")?; + +let args = ic_pub_key::EcdsaPublicKeyArgs { + canister_id: Some(canister_id), + derivation_path: vec![b"derivation", b"path", b"values"], + key_id: ic_pub_key::EcdsaKeyId { + curve: ic_pub_key::EcdsaCurve::Secp256k1, + name: "key_1".to_string(), + }, +}; + +let dpk = ic_pub_key::derive_ecdsa_key(&args)?; +``` + + + + +```ts +const mk = PublicKeyWithChainCode.forMainnetKey('key_1'); +const canisterId = Principal.fromText('h5jwf-5iaaa-aaaan-qmvoa-cai'); +const path = DerivationPath.withCanisterPrefix(canisterId, [ + Buffer.from('derivation'), + Buffer.from('path'), + Buffer.from('value'), +]); + +// Derive the new key +const derivedKey = mk.deriveSubkeyWithChainCode(path); +``` + + + + +For online derivation, ICP provides the `ecdsa_public_key` method to obtain public keys. This method uses the following Candid interface: ```candid type ecdsa_public_key_args = record { @@ -140,6 +182,8 @@ https://github.com/dfinity/examples/blob/master/rust/threshold-ecdsa/src/ecdsa_e ## Master Public Keys +### Mainnet Keys + The master public keys used for ECDSA with secp256k1 are: - `key_1`: `02121bc3a5c38f38ca76487c72007ebbfd34bc6c4cb80a671655aa94585bbd0a02` @@ -148,7 +192,7 @@ The master public keys used for ECDSA with secp256k1 are: These can be used to perform key derivation offline without requiring using the `ecdsa_public_key` management canister call. -## PocketIC Hardcoded Keys +### PocketIC Test Keys PocketIC uses hardcoded keys for testing purposes. These are: @@ -164,4 +208,8 @@ PocketIC uses hardcoded keys for testing purposes. These are: - [Threshold ECDSA sample dapp - Motoko](https://github.com/dfinity/examples/blob/master/motoko/threshold-ecdsa/). -- [Threshold ECDSA sample dapp - Rust](https://github.com/dfinity/examples/tree/master/rust/threshold-ecdsa). \ No newline at end of file +- [Threshold ECDSA sample dapp - Rust](https://github.com/dfinity/examples/tree/master/rust/threshold-ecdsa). + +- [Rust ic-pub-key library](https://crates.io/crates/ic-pub-key) + +- [TS ic-pub-key library](https://www.npmjs.com/package/@dfinity/ic-pub-key) diff --git a/docs/building-apps/network-features/signatures/t-schnorr.mdx b/docs/building-apps/network-features/signatures/t-schnorr.mdx index 77170d058f..ec5ebc7ce5 100644 --- a/docs/building-apps/network-features/signatures/t-schnorr.mdx +++ b/docs/building-apps/network-features/signatures/t-schnorr.mdx @@ -136,7 +136,50 @@ Verification can be done in other languages using libraries and packages that su ## Obtaining public keys -To obtain public keys, you will need to call the `schnorr_public_key` method of the IC management canister (`aaaaa-aa`). You can deploy the threshold Schnorr sample [locally](https://github.com/dfinity/examples/tree/master/motoko/threshold-schnorr) and use the Candid UI to call this method, or you can call this method programmatically using Motoko or Rust. +There are two methods available for obtaining the public key associated with a particular canister ID and derivation path. The derivation process is deterministic and public; given the master public key along with the canister ID and derivation path, the same key is always produced, and no secret is required in order to compute the public key. + +The first method is to perform *offline* derivation, using one of the available libraries which implement the relevant derivation scheme. This allows derivation without having to invoke any services running on the Internet Computer. The alternative is to use *online* derivation, using a call to the management canister interface. + +The following snippets demonstrate deriving Schnorr public keys without requiring management canister calls, using the `ic-pub-key` Rust and TypeScript libraries. + + + + +```rust +let canister_id = ic_pub_key::CanisterId::from_str("h5jwf-5iaaa-aaaan-qmvoa-cai")?; + +let args = ic_pub_key::SchnorrPublicKeyArgs { + canister_id: Some(canister_id), + derivation_path: vec![b"derivation", b"path", b"values"], + key_id: ic_pub_key::SchnorrKeyId { + algorithm: ic_pub_key::SchnorrAlgorithm::Ed25519, + name: "key_1".to_string(), + }, +}; + +let dpk = ic_pub_key::derive_schnorr_key(&args)?; +``` + + + + +```ts +const mk = PublicKeyWithChainCode.forMainnetKey('key_1'); +const canisterId = Principal.fromText('h5jwf-5iaaa-aaaan-qmvoa-cai'); +const path = DerivationPath.withCanisterPrefix(canisterId, [ + Buffer.from('derivation'), + Buffer.from('path'), + Buffer.from('value'), +]); + +// Derive the new key +const derivedKey = mk.deriveSubkeyWithChainCode(path); +``` + + + + +As an alternative to offline derivation, you can call the `schnorr_public_key` method of the IC management canister (`aaaaa-aa`). You can deploy the threshold Schnorr sample [locally](https://github.com/dfinity/examples/tree/master/motoko/threshold-schnorr) and use the Candid UI to call this method, or you can call this method programmatically using Motoko or Rust. @@ -158,12 +201,14 @@ https://github.com/dfinity/examples/blob/master/rust/threshold-schnorr/src/schno ## Master Public Keys -The master public keys used for BIP-340 are: +### Mainnet Keys + +On the mainnet, the master public keys used for BIP-340 are: - `key_1`: `02246e29785f06d37a8a50c49f6152a34df74738f8c13a44f59fef4cbe90eb13ac` - `test_key_1`: `037a651a2e5ef3d1ef63e84c4c4caa029fa4a43a347a91e4d84a8e846853d51be1` -The master public keys used for Ed25519 are: +On the mainnet, the master public keys used for Ed25519 are: - `key_1`: `476374d9df3a8af28d3164dc2422cff894482eadd1295290b6d9ad92b2eeaa5c` - `test_key_1`: `6c0824beb37621bcca6eecc237ed1bc4e64c9c59dcb85344aa7f9cc8278ee31f` @@ -171,7 +216,7 @@ The master public keys used for Ed25519 are: These can be used to perform key derivation offline without requiring using the `schnorr_public_key` management canister call. -## PocketIC Hardcoded Keys +### PocketIC Test Keys PocketIC uses hardcoded keys for testing purposes. For BIP-340 these are the same as those used for ECDSA: @@ -194,4 +239,8 @@ For Ed25519 the PocketIC test keys are: - [Threshold Schnorr sample dapp - Motoko](https://github.com/dfinity/examples/tree/master/motoko/threshold-schnorr). -- [Threshold Schnorr sample dapp - Rust](https://github.com/dfinity/examples/tree/master/rust/threshold-schnorr). \ No newline at end of file +- [Threshold Schnorr sample dapp - Rust](https://github.com/dfinity/examples/tree/master/rust/threshold-schnorr). + +- [Rust ic-pub-key library](https://crates.io/crates/ic-pub-key) + +- [TS ic-pub-key library](https://www.npmjs.com/package/@dfinity/ic-pub-key)