Skip to content

feat(crypto): add the mainnet and PocketIC master keys to ic-secp256r1 - #11895

Merged
eichhorl merged 2 commits into
masterfrom
eichhorl/ic-secp256r1-master-keys
Oct 21, 2026
Merged

eichhorl merged 2 commits into
masterfrom
eichhorl/ic-secp256r1-master-keys

Conversation

@eichhorl

@eichhorl eichhorl commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Why

ic-secp256k1 and ic-ed25519 ship the mainnet and PocketIC master public keys of their algorithms, and ic-pub-key uses them to derive keys offline. The mainnet ECDSA keys key_1 and test_key_1 now also exist over secp256r1 (P-256), and PocketIC holds secp256r1 keys too (#11741). But ic-secp256r1 has none of them.

This PR adds the keys and bumps the version, so the crate can be released. ic-pub-key can only use the keys once that release is out (#11894). That's the same order as #7148 before #7194 for the secp256k1 and Ed25519 PocketIC keys.

What

  • PublicKey::mainnet_key(MasterPublicKeyId) returns key_1 and test_key_1.
  • PublicKey::pocketic_key(PocketIcMasterPublicKeyId) returns the keys PocketIC uses for key_1, test_key_1 and dfx_test_key.
  • PublicKey::derive_mainnet_key and PublicKey::derive_pocketic_key derive a canister's public key from those master keys. They are offline equivalents of ecdsa_public_key. For their &CanisterId argument, the crate re-exports ic_principal::Principal as CanisterId, as ic-secp256k1 and ic-ed25519 do.
  • hex-literal becomes a regular dependency, to embed the keys, and ic_principal a new one.
  • The version goes from 0.2.0 to 0.3.0, with a dated changelog entry, for a release after merging. In-repo dependents use the crate by path without a version, so nothing else changes.
  • The cup_explorer README now lists ic-secp256r1 among the crates with hardcoded master keys.

Where the keys come from:

  • Mainnet: the CUPs of subnets uzr34 (key_1) and fuqsr (test_key_1). In that same output, the secp256k1, BIP340, Ed25519 and VetKD master keys match the values ic-secp256k1, ic-ed25519 and ic-vetkeys already ship.
  • PocketIC: derived the way the StateMachine does it, from PrivateKey::generate_insecure_key_for_testing(42) and the key name.

Tests

  • mainnet_keys_round_trip: the mainnet keys round-trip.
  • A test derives the PocketIC keys the StateMachine's way and compares them with the hardcoded ones.
  • derive_mainnet_key and derive_pocketic_key have known-answer tests for all five keys. The expected keys are what the replica's derive_threshold_public_key (the function that answers ecdsa_public_key) returns for the same master key, canister and path.
  • The SLIP-10 retry for nist256p1 (index 33941) is now tested. It's the only branch of the derivation the existing tests didn't reach: it runs when iL >= n, about once in 2^32 steps on P-256. The vector is the one the replica's own key derivation test uses.
  • Locally, in the dev container, all of these pass: cargo check, cargo fmt, rust-lint.sh, bazel build //... --nobuild, //bazel:buildifier.check, and the 37 tests from the depth-2 rdeps query (this package, ic-pub-key, rs/crypto, the P-256 basic-sig crate, standalone-sig-verifier, rs/validator).

🤖 Generated with Claude Code

`ic-secp256k1` and `ic-ed25519` ship the mainnet and PocketIC master
public keys of their algorithms, which `ic-pub-key` uses for offline key
derivation. The mainnet ECDSA keys `key_1` and `test_key_1` now also
exist over secp256r1, and PocketIC holds secp256r1 keys too, but
`ic-secp256r1` has none of them.

* `PublicKey::mainnet_key` returns the master public keys of
  `ecdsa:Secp256r1:key_1` and `ecdsa:Secp256r1:test_key_1`, read from
  the CUPs of subnets uzr34 and fuqsr.
* `PublicKey::pocketic_key` returns the ones PocketIC uses for `key_1`,
  `test_key_1` and `dfx_test_key`, and a test derives them the way
  PocketIC does.
* The version moves to 0.3.0, to be released before `ic-pub-key` can
  depend on these keys.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The mainnet key constants still need independent confirmation against their authoritative source CUPs before release.

No open issues 🎉

What changed in this PR

Adds mainnet and PocketIC P-256 master public keys to ic-secp256r1, preparing a release needed for offline key derivation in ic-pub-key.

Changes:

  • Adds key identifiers and constructors for two mainnet and three PocketIC keys.
  • Adds mainnet serialization checks and PocketIC derivation tests.
  • Prepares version 0.3.0 with dependency and changelog updates.
File Description
packages/​ic-secp256r1/​tests/​tests.rs Tests embedded keys and PocketIC derivation.
packages/​ic-secp256r1/​src/​lib.rs Adds key identifiers, constructors, and constants.
packages/​ic-secp256r1/​CHANGELOG.md Documents the 0.3.0 release.
packages/​ic-secp256r1/​Cargo.toml Bumps version and adds the runtime dependency.
packages/​ic-secp256r1/​BUILD.bazel Aligns Bazel version and dependencies.
Cargo.lock Records version 0.3.0.

Review effort: Balanced

@eichhorl
eichhorl marked this pull request as ready for review October 20, 2026 07:44
@eichhorl
eichhorl requested a review from a team as a code owner October 20, 2026 07:44

@pierugo-dfinity pierugo-dfinity left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified that the provided mainnet keys match the ones in the live subnets' CUPs.

Comment thread packages/ic-secp256r1/tests/tests.rs Outdated
Comment thread packages/ic-secp256r1/src/lib.rs
* Add `PublicKey::derive_mainnet_key` and `PublicKey::derive_pocketic_key`,
  and re-export `ic_principal::Principal` as `CanisterId` for them, as
  `ic-secp256k1` and `ic-ed25519` do. Their tests expect what the
  replica's `derive_threshold_public_key` returns for the same master
  keys, canisters and paths.
* Test the SLIP-10 retry for nist256p1 (index 33941), the only branch of
  the derivation the tests didn't reach. It runs when iL >= n, about once
  in 2^32 steps on P-256.
* Rename `mainnet_keys_are_the_ones_in_the_cups` to
  `mainnet_keys_round_trip`, which is what it checks.
* Name the two new enums in the changelog, drop the duplicate
  `hex-literal` dev-dependency, and list `ic-secp256r1` among the crates
  with hardcoded master keys in the `cup_explorer` README.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
eichhorl added a commit that referenced this pull request Oct 20, 2026
…-key-secp256r1

ic-pub-key needs the ic-secp256r1 0.3.0 of #11895: take its version of
ic-secp256r1 and raise the requirement to 0.3.0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@eichhorl
eichhorl requested a balanced review from Copilot October 20, 2026 12:04

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The embedded mainnet master keys need independent verification against authenticated CUPs before release.

No open issues 🎉

Review effort: Balanced

@eichhorl
eichhorl added this pull request to stack #11902 October 20, 2026 12:13
@eichhorl
eichhorl added this pull request to the merge queue Oct 21, 2026
Merged via the queue into master with commit e623ec3 Oct 21, 2026
47 checks passed
@eichhorl
eichhorl deleted the eichhorl/ic-secp256r1-master-keys branch October 21, 2026 06:37
eichhorl added a commit that referenced this pull request Oct 23, 2026
Resolve two conflicts with #11909:
- CHANGELOG.md: keep #11909's line for ic-management-canister-types
  0.5.0 -> 0.11.0 once, next to this PR's entries.
- Cargo.lock: master's, with cargo re-applying this PR's change to the
  ic-pub-key entry.

The ic-secp256r1 changes this branch carried from #11895 are identical to
master's squash commit, so they drop out of the diff.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
AleDema pushed a commit to AleDema/ic that referenced this pull request Oct 24, 2026
…finity#11894)

### Why

The mainnet ECDSA keys `key_1` and `test_key_1` now also exist over
secp256r1 (P-256), the curve dfinity#11573 added, and PocketIC holds secp256r1
keys too (dfinity#11741). `ic-pub-key` derives canister public keys offline
from hardcoded master keys, but for ECDSA only over secp256k1: any other
curve returns `AlgorithmNotSupported`.

`ic-secp256r1` 0.3.0 (dfinity#11895) has the secp256r1 master keys, and the
workspace's `ic-management-canister-types` 0.11.0 (dfinity#11909) has
`EcdsaCurve::Secp256r1`. Both are published.

### What

* A new `secp256r1` feature, on by default, derives keys for `key_1`,
`test_key_1`, `pocketic_key_1`, `pocketic_test_key_1` and
`dfx_test_key`, following the secp256k1 code paths. It requires
`ic-secp256r1` 0.3.0.
* `TryFrom<&EcdsaKeyId>` checks each curve in its own feature-gated
block, like `TryFrom<&SchnorrKeyId>`, instead of depending on the order
of its checks.
* `serialize` and `chain_code` share one implementation on
`DerivedPublicKeyInner`, instead of one each in `CanisterMasterKey` and
`DerivedPublicKey`.
* The crate docs and README describe the features, including
`default-features = false`, and `derive_ecdsa_key` names the feature
each curve needs.
* The changelog adds the secp256r1 support and the changes since the
published 0.3.0 that weren't recorded yet: `ic-vetkeys` 0.6.0 → 0.7.0
and the `ic-cdk-management-canister` dependency of the `vetkeys`
feature. dfinity#11909 already recorded `ic-management-canister-types` 0.5.0 →
0.11.0, which makes the next release a breaking one.

### Tests

* Known-answer tests for all five secp256r1 keys, using the same
canisters and path shape as the secp256k1 tests.
* All ECDSA tests now also check the chain code, and that unknown key
names are rejected.
* All expected public keys and chain codes are what the replica's
`derive_threshold_public_key`, the function that answers
`ecdsa_public_key`, returns for these master keys. A one-off check, not
committed, confirmed this for all 11 ECDSA vectors:
  * `ic-pub-key` matches the replica on both public key and chain code.
  * The replica reproduces the existing secp256k1 vectors.
* The offline derivation also matches mainnet itself: for a canister on
mainnet, `ecdsa_public_key` returned exactly the public keys
`ic-pub-key` derives offline, for `key_1` and `test_key_1` on both
curves, each with an empty and a 7-element path. Mainnet doesn't expose
chain codes, so those rest on the replica check.
* The test helpers follow the Schnorr ones and are feature-gated. With a
single feature, the tests build without warnings, and so does the
library, except with only `vetkeys`: the two warnings master already has
in `CanisterMasterKey::derive_key` remain. This PR removes a third, in
`TryFrom<&SchnorrKeyId>`.
* Locally, in the dev container, all of these pass: `cargo check`
(default and each single feature), `rust-lint.sh`, `bazel build //...
--nobuild`, `//bazel:buildifier.check`, and the 3 affected tests.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants