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
29 changes: 29 additions & 0 deletions docs/references/_attachments/ic.did
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ type ecdsa_curve = variant {
secp256k1;
};

type vetkd_curve = variant {
bls12_381_g2;
};

type schnorr_algorithm = variant {
bip340secp256k1;
ed25519;
Expand Down Expand Up @@ -352,6 +356,27 @@ type sign_with_schnorr_result = record {
signature : blob;
};

type vetkd_public_key_args = record {
canister_id : opt canister_id;
context : blob;
key_id : record { curve : vetkd_curve; name : text };
};

type vetkd_public_key_result = record {
public_key : blob;
};

type vetkd_derive_key_args = record {
input : blob;
context : blob;
transport_public_key : blob;
key_id : record { curve : vetkd_curve; name : text };
};

type vetkd_derive_key_result = record {
encrypted_key : blob;
};

type node_metrics_history_args = record {
subnet_id : principal;
start_at_timestamp_nanos : nat64;
Expand Down Expand Up @@ -462,6 +487,10 @@ service ic : {
schnorr_public_key : (schnorr_public_key_args) -> (schnorr_public_key_result);
sign_with_schnorr : (sign_with_schnorr_args) -> (sign_with_schnorr_result);

// Threshold key derivation
vetkd_public_key : (vetkd_public_key_args) -> (vetkd_public_key_result);
vetkd_derive_key : (vetkd_derive_key_args) -> (vetkd_derive_key_result);

// bitcoin interface
bitcoin_get_balance : (bitcoin_get_balance_args) -> (bitcoin_get_balance_result);
bitcoin_get_utxos : (bitcoin_get_utxos_args) -> (bitcoin_get_utxos_result);
Expand Down
3 changes: 3 additions & 0 deletions docs/references/_attachments/interface-spec-changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
## Changelog {#changelog}

### 0.41.0 (2025-06-02) {#0_41_0}
* Management canister API for threshold key derivation (vetKD).

### 0.40.0 (2025-05-30) {#0_40_0}
* Non-ASCII characters are allowed in the URL of canister http outcalls.
* The transformed response size of canister http outcalls must not exceeded `max_response_bytes` (if provided).
Expand Down
71 changes: 68 additions & 3 deletions docs/references/ic-interface-spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -1543,7 +1543,7 @@ defaulting to `I = i32` if the canister declares no memory.
ic0.cost_http_request : (request_size : i64, max_res_bytes : i64, dst : I) -> (); // * s
ic0.cost_sign_with_ecdsa : (src : I, size : I, ecdsa_curve: i32, dst : I) -> i32; // * s
ic0.cost_sign_with_schnorr : (src : I, size : I, algorithm: i32, dst : I) -> i32; // * s
ic0.cost_vetkd_derive_encrypted_key : (src : I, size : I, vetkd_curve: i32, dst : I) -> i32; // * s
ic0.cost_vetkd_derive_key : (src : I, size : I, vetkd_curve: i32, dst : I) -> i32; // * s

ic0.debug_print : (src : I, size : I) -> (); // * s
ic0.trap : (src : I, size : I) -> (); // * s
Expand Down Expand Up @@ -2134,7 +2134,7 @@ These system calls return costs in Cycles, represented by 128 bits, which will b

- `ic0.cost_sign_with_schnorr(src : I, size : I, algorithm: i32, dst : I) -> i32`; `I ∈ {i32, i64}`

- `ic0.cost_vetkd_derive_encrypted_key(src : I, size : I, vetkd_curve: i32, dst : I) -> i32`; `I ∈ {i32, i64}`
- `ic0.cost_vetkd_derive_key(src : I, size : I, vetkd_curve: i32, dst : I) -> i32`; `I ∈ {i32, i64}`

These system calls accept a key name via a textual representation for the specific signing scheme / key of a given size stored in the heap memory starting at offset `src`. They also accept an `i32` with the following interpretations:
- `ecdsa_curve: 0 → secp256k1`
Expand Down Expand Up @@ -2652,6 +2652,71 @@ When using BIP341 signatures, the actual signature that is created will be relat

Cycles to pay for the call must be explicitly transferred with the call, i.e., they are not automatically deducted from the caller's balance implicitly (e.g., as for inter-canister calls).

### IC method `vetkd_public_key` {#ic-vetkd_public_key}

:::note

The vetKD API is considered EXPERIMENTAL. Canister developers must be aware that the API may evolve in a non-backward-compatible way.

:::

This method can only be called by canisters, i.e., it cannot be called by external users via ingress messages.

This method returns the vetKD public (verification) key derived from the vetKD master public key with ID `key_id` for the canister with the given `canister_id` and the given `context`.

If the `canister_id` is unspecified, it will default to the canister id of the caller. The `context` is a byte string of variable length. The `key_id` is a struct specifying both a curve and a name. The availability of a particular `key_id` depends on the implementation.

The public key returned for an empty `context` is called _canister public key_. Given this canister public key, the public key for a particular `context` can also be derived offline.

For curve `bls12_381_g2`, the returned `public_key` is a G<sub>2</sub> element in compressed form in [BLS Signatures Draft RFC](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-bls-signature-05#name-bls12-381) encoding.

This call requires that a vetKD master key with ID `key_id` was generated by the IC and the key derivation functionality for that key was enabled, and that the `canister_id` meets the requirement of a canister id. Otherwise, the call is is rejected.

### IC method `vetkd_derive_key` {#ic-vetkd_derive_key}

:::note

The vetKD API is considered EXPERIMENTAL. Canister developers must be aware that the API may evolve in a non-backward-compatible way.

:::

This method can only be called by canisters, i.e., it cannot be called by external users via ingress messages.

This method returns a vetKD key (aka vetKey) encrypted under `transport_public_key` and derived from the vetKD master key with ID `key_id` based on the caller's `input` for a given `context`.

Both the `input` and the `context` are byte strings of variable length. While both are inputs to the underlying key derivation algorithm (implicitly together with the calling canister's ID), `input` is intended as the primary differentiator when deriving different keys, while `context` is intended as domain separator.
The `key_id` is a struct specifying both a curve and a name. The availability of a particular `key_id` depends on the implementation.

Both the encrypted and the decrypted form of the vetKD key can be verified by using the respective vetKD public (verification) key, which can be obtained by calling the IC method `vetkd_public_key`.

For curve `bls12_381_g2`, the following holds:

- The `transport_public_key` is a G<sub>1</sub> element in compressed form in [BLS Signatures Draft RFC](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-bls-signature-05#name-bls12-381) encoding. Transport public keys are created by calculating *tpk = g<sub>1</sub><sup>tsk</sup>*, where the transport secret key *tsk* is chosen uniformly at random from Z<sub>p</sub>.

- The returned `encrypted_key` is the blob `E1 · E2 · E3`, where E<sub>1</sub> and E<sub>3</sub> are G<sub>1</sub> elements, and E<sub>2</sub> is a G<sub>2</sub> element, all in compressed form in [BLS Signatures Draft RFC](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-bls-signature-05#name-bls12-381) encoding.

The encrypted key can be verified by ensuring *e(E<sub>1</sub>, g<sub>2</sub>) == e(g<sub>1</sub>, E<sub>2</sub>)*, and *e(E<sub>3</sub>, g<sub>2</sub>) == e(tpk, E<sub>2</sub>) \* e(H(dpk · `input`), dpk)*, where *dpk* is the derived (vetKD) public key associated with the respective `context`, `key_id`, and the canister ID of the caller.

- The decrypted vetKD key *k* is obtained by calculating E<sub>3</sub> \* E<sub>1</sub><sup>-tsk</sup>, where tsk ∈ Z<sub>p</sub> is the transport secret key that was used to generate the `transport_public_key`.

The key can be verified by ensuring *e(k, g<sub>2</sub>) == e(H(dpk · `input`), dpk)*, where *dpk* is the derived (vetKD) public key associated with the respective `context`, `key_id`, and the canister ID of the caller. Such verification protects against untrusted canisters returning invalid keys.

where

- g<sub>1</sub>, g<sub>2</sub> are generators of G<sub>1</sub>, G<sub>2</sub>, which are groups of prime order *p*,

- \* denotes the group operation in G<sub>1</sub>, G<sub>2</sub>, and G<sub>T</sub>,

- e: `G1 x G2 → GT` is the pairing (see [BLS Signatures Draft RFC, Appendix A](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-bls-signature-05#name-bls12-381)),

- H hashes into G<sub>1</sub> according to the [BLS12-381 message augmentation scheme ciphersuite in the BLS Signatures Draft RFC](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-bls-signature#name-message-augmentation-2) (see also [Hashing to Elliptic Curves Draft RFC](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve#name-suites-for-bls12-381)),

- `·` and · denote concatenation

This call requires that a vetKD master key with ID `key_id` was generated by the IC and the key derivation functionality for that key was enabled. Otherwise, the call is is rejected.

Cycles to pay for the call must be explicitly transferred with the call, i.e., they are not automatically deducted from the caller's balance implicitly (e.g., as for inter-canister calls).

### IC method `http_request` {#ic-http_request}

This method can only be called by canisters, i.e., it cannot be called by external users via ingress messages.
Expand Down Expand Up @@ -7958,7 +8023,7 @@ ic0.cost_sign_with_schnorr<es>(src: I, size: I, algorithm: i32, dst: I) : i32 =
return 0

I ∈ {i32, i64}
ic0.cost_vetkd_derive_encrypted_key<es>(src: I, size: I, vetkd_curve: i32, dst: I) : i32 =
ic0.cost_vetkd_derive_key<es>(src: I, size: I, vetkd_curve: i32, dst: I) : i32 =
known_keys = arbitrary()
known_curves = arbitrary()
key_name = copy_from_canister<es>(src, size)
Expand Down