Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/concepts/chain-key-cryptography.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ For ECDSA and BIP340, key derivation uses a generalized form of [BIP-32](https:/

Derivation is transparent — it happens inside the protocol as part of the signing and public-key-retrieval APIs. You provide a derivation path and the protocol handles the rest.

Because the derivation algorithm is deterministic and uses only public parameters (the master public key, the canister principal, and the derivation path), public key derivation can also be performed **offline** — no management canister call or network connection required. This is useful for building explorers, dashboards, or address-derivation tools that need a canister's public key or blockchain address without a live ICP connection. See the [offline key derivation guide](../guides/chain-fusion/offline-key-derivation.md) for TypeScript and Rust libraries.

<!-- ic-pub-key: known issue — @dfinity/ic-pub-key v1.0.1 npm package is missing .d.ts type declarations (https://github.com/dfinity/ic-pub-key/issues/197); verify this is fixed before editing TypeScript examples. Package may also move to the @icp-sdk/ namespace in a future release — update all references when that happens. -->

### Pre-signatures

Signing is split into two phases for performance. An expensive **pre-signature computation** runs asynchronously in the background, producing pre-computed values that are consumed by individual signing requests. This means the latency you experience when calling `sign_with_ecdsa` or `sign_with_schnorr` is dominated by a single consensus round, not the full multi-party computation.
Expand Down
1 change: 1 addition & 0 deletions docs/guides/chain-fusion/chain-fusion-signer.md
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@ These variants are defined by [papi](https://github.com/dfinity/papi), an open-s
- [Bitcoin integration guide](bitcoin.md) — build a full Bitcoin dapp with your own signing backend
- [Ethereum integration guide](ethereum.md) — EVM RPC canister for reading Ethereum state
- [Cycles Ledger](../../reference/system-canisters.md#cycles-ledger) — fund your account with cycles
- [Offline key derivation](offline-key-derivation.md) — derive ETH/BTC addresses for any canister principal without a management canister call
- [papi](https://github.com/dfinity/papi) — add the same `CallerPaysIcrc2Cycles` / `PatronPaysIcrc2Cycles` payment pattern to your own canister

<!-- Upstream: informed by dfinity/chain-fusion-signer — src/signer/canister/signer.did, src/signer/api/src/methods.rs, README.md, check-pricing.report.md; dfinity/papi — README.md (payment variants and patron pattern); dfinity/ic-pub-key — README.md, src/cli.ts -->
93 changes: 93 additions & 0 deletions docs/guides/chain-fusion/offline-key-derivation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
---
title: "Offline public key derivation"
description: "Derive canister threshold public keys and blockchain addresses offline — no management canister call or cycles required."
sidebar:
order: 6
---

ICP's threshold key derivation is deterministic: given the subnet's master public key, a canister principal, and a derivation path, anyone can compute the same canister public key locally. No secrets are involved and no on-chain call is needed.

This is useful for computing Ethereum or Bitcoin addresses for a canister, building explorers or dashboards, and testing locally without a live ICP connection.

## TypeScript

Install the library and its peer dependency:

```bash
npm install @dfinity/ic-pub-key @dfinity/principal
```

### ECDSA (secp256k1)

Used for Ethereum, EVM chains, and Bitcoin (legacy/SegWit).

```typescript
import { ecdsa } from "@dfinity/ic-pub-key";
import { Principal } from "@dfinity/principal";

const masterKey = ecdsa.secp256k1.PublicKeyWithChainCode.forMainnetKey("key_1");
const path = ecdsa.secp256k1.DerivationPath.withCanisterPrefix(
Principal.fromText("your-canister-id"),
[] // additional sub-path components, if any
);
const derived = masterKey.deriveSubkeyWithChainCode(path);
console.log(derived.public_key.toHex()); // SEC1-compressed hex public key
```

Use `forMainnetKey("test_key_1")` for the development key, or `forPocketIcKey("key_1")` for PocketIC tests.

### Schnorr (Ed25519)

Used for Solana, TON, Polkadot, Cardano, and NEAR.

```typescript
import { schnorr } from "@dfinity/ic-pub-key";
import { Principal } from "@dfinity/principal";

const masterKey = schnorr.ed25519.PublicKeyWithChainCode.forMainnetKey("key_1");
const path = schnorr.ed25519.DerivationPath.withCanisterPrefix(
Principal.fromText("your-canister-id"),
[]
);
const derived = masterKey.deriveSubkeyWithChainCode(path);
console.log(derived.public_key.toHex()); // 32-byte Ed25519 public key as hex
```

## Rust

```toml
# Cargo.toml
# Disable the vetkeys feature to avoid pulling in heavy transitive dependencies
# if VetKD support is not needed.
ic-pub-key = { version = "0.3.0", default-features = false, features = ["secp256k1", "ed25519"] }
```

See [docs.rs/ic-pub-key](https://docs.rs/ic-pub-key) for the full Rust API. The crate wraps `ic-secp256k1` and `ic-ed25519` from the ICP monorepo and exposes the same offline derivation logic.

## CLI

The `derive` commands accept a parent public key and chain code and output the derived key as JSON. Pass the hex values from `forMainnetKey()` above or from a prior `ecdsa_public_key` / `schnorr_public_key` call:

```bash
# ECDSA secp256k1
npx @dfinity/ic-pub-key derive ecdsa secp256k1 \
--pubkey <parent-public-key-hex> \
--chaincode <parent-chain-code-hex> \
--derivationpath <candid-blob-path>

# Schnorr Ed25519 (mainnet key_1 is the default — no flags needed for the master key)
npx @dfinity/ic-pub-key derive schnorr ed25519 \
--derivationpath <candid-blob-path>
```

For deriving Chain Fusion Signer addresses specifically (ETH/BTC for a given principal), use the `signer` commands instead — see the [Chain Fusion Signer guide](chain-fusion-signer.md#derive-offline-no-cycles).

## Next steps

- [Chain Fusion Signer](chain-fusion-signer.md) — sign transactions for Bitcoin and Ethereum from web apps and CLI
- [Management canister reference](../../reference/management-canister.md#chain-key-signing) — the on-chain `ecdsa_public_key` and `schnorr_public_key` methods
- [Chain-key cryptography](../../concepts/chain-key-cryptography.md) — how threshold key derivation works

<!-- ic-pub-key: known issue — @dfinity/ic-pub-key v1.0.1 npm package is missing .d.ts type declarations (https://github.com/dfinity/ic-pub-key/issues/197); verify this is fixed before editing TypeScript examples. Package may also move to the @icp-sdk/ namespace in a future release — update all references when that happens. -->

<!-- Upstream: informed by dfinity/ic-pub-key — src/ecdsa/secp256k1.ts, src/schnorr/ed25519.ts, src/cli.ts, README.md -->
4 changes: 4 additions & 0 deletions docs/reference/management-canister.md
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,10 @@ Signs a message using threshold Schnorr. The corresponding public key can be obt

For practical usage of chain-key signing in Bitcoin and Ethereum workflows, see the [Bitcoin guide](../guides/chain-fusion/bitcoin.md) and [Ethereum guide](../guides/chain-fusion/ethereum.md).

### Offline public key derivation

If you only need a public key — to derive a blockchain address or verify a signature — the management canister call can be avoided entirely. ICP's key derivation algorithm is deterministic and uses only public parameters, so derivation can be performed offline without cycles or a network connection. See the [offline key derivation guide](../guides/chain-fusion/offline-key-derivation.md) for TypeScript and Rust libraries.

## vetKD (Verifiable Encrypted Threshold Key Derivation)

### `vetkd_public_key`
Expand Down
Loading