From 15e4caa6e34aef311506386352a49a0d758c36bc Mon Sep 17 00:00:00 2001 From: Martin Raszyk Date: Fri, 15 Nov 2024 12:34:57 +0100 Subject: [PATCH 1/4] System API for threshold key derivation (vetKD) --- docs/references/_attachments/ic.did | 17 +++++++++ docs/references/ic-interface-spec.md | 54 ++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/docs/references/_attachments/ic.did b/docs/references/_attachments/ic.did index 8c43e699bc..f59d8fee44 100644 --- a/docs/references/_attachments/ic.did +++ b/docs/references/_attachments/ic.did @@ -83,6 +83,10 @@ type ecdsa_curve = variant { secp256k1; }; +type vetkd_curve = variant { + bls12_381; +}; + type schnorr_algorithm = variant { bip340secp256k1; ed25519; @@ -435,6 +439,19 @@ 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 : (record { + canister_id : opt canister_id; + derivation_path : vec blob; + key_id : record { curve : vetkd_curve; name : text }; + }) -> (record { public_key : blob; }); + vetkd_encrypted_key : (record { + public_key_derivation_path : vec blob; + derivation_id : blob; + key_id : record { curve : vetkd_curve; name : text }; + encryption_public_key : blob; + }) -> (record { encrypted_key : blob; }); + // bitcoin interface bitcoin_get_balance : (bitcoin_get_balance_args) -> (bitcoin_get_balance_result); bitcoin_get_utxos : (bitcoin_get_utxos_args) -> (bitcoin_get_utxos_result); diff --git a/docs/references/ic-interface-spec.md b/docs/references/ic-interface-spec.md index 81fa75be62..b07cc0a6e4 100644 --- a/docs/references/ic-interface-spec.md +++ b/docs/references/ic-interface-spec.md @@ -2451,6 +2451,60 @@ This call requires that a Schnorr key with ID `key_id` was generated by the IC a 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 returns a (derived) vetKD public key for the given canister using the given derivation path. + +If the `canister_id` is unspecified, it will default to the canister id of the caller. The `derivation_path` is a vector of variable length byte strings, containing at most 255 byte strings. The `key_id` is a struct specifying both a curve and a name. The availability of a particular `key_id` depends on the implementation. + +For curve `bls12_381`, the returned `public_key` is a G2 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 the vetKD feature is enabled, and the `canister_id` meets the requirement of a canister id. Otherwise it will be rejected. + +### IC method `vetkd_encrypted_key` {#ic-vetkd_encrypted_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 returns a vetKD key encrypted under `encryption_public_key` for the given derivation information consisting of `public_key_derivation_path` and `derivation_id`. + +The `public_key_derivation_path` is a vector of variable length byte strings, containing at most 255 byte strings, and (together with the calling canister's ID) acts as domain separator to derive different public (verification) keys with the IC method `vetkd_public_key`. The `derivation_id` is used to derive different encrypted keys. The `key_id` is a struct specifying both a curve and a name. The availability of a particular `key_id` depends on the implementation. + +For curve `bls12_381`, the following holds: + +- The `encryption_public_key` is a G1 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. Encryption public keys are created by calculating *epk = g1esk*, where the encryption secret key *esk* is chosen uniformly at random from Zp. + +- The returned `encrypted_key` is the blob `E1 · E2 · E3`, where E1 and E3 are G1 elements, and E2 is a G2 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(E1, g2) == e(g1, E2)*, and *e(E3, g2) == e(epk, E2) \* e(H(dpk · `derivation_id`), dpk)*, where the derived public key *dpk* is obtained by calling IC method `vetkd_public_key` with the same `derivation_path` and `key_id`, and the canister id of the caller. + +- The decrypted vetKD key *k* is obtained by calculating E3 \* E1-esk, where esk ∈ Zp is the encryption secret key that was used to generate the `encryption_public_key`. + + The key can be verified by ensuring *e(k, g2) == e(H(dpk · `derivation_id`), dpk)*, where *dpk* is obtained by calling IC method `vetkd_public_key` with the same `derivation_path` and `key_id`, and the canister id of the caller. Such verification protects against untrusted canisters returning invalid keys. + +where + +- g1, g2 are generators of G1, G2, which are groups of prime order *p*, + +- \* denotes the group operation in G1, G2, and GT, + +- 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 G1 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 the vetKD feature is enabled and the caller is a canister. Otherwise it will be rejected. + ### 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. From 4402d56ac9f0d9f49dfa0824d7e89433f86507dd Mon Sep 17 00:00:00 2001 From: Franz-Stefan Preiss Date: Mon, 27 Jan 2025 12:43:54 +0100 Subject: [PATCH 2/4] Add info regarding cycles deduction --- docs/references/ic-interface-spec.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/references/ic-interface-spec.md b/docs/references/ic-interface-spec.md index b07cc0a6e4..7f28d807d9 100644 --- a/docs/references/ic-interface-spec.md +++ b/docs/references/ic-interface-spec.md @@ -2505,6 +2505,8 @@ where This call requires that the vetKD feature is enabled and the caller is a canister. Otherwise it will be 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. From 9c0178eae658dab77de1d69e68103eb305378207 Mon Sep 17 00:00:00 2001 From: Franz-Stefan Preiss Date: Mon, 27 Jan 2025 16:19:31 +0100 Subject: [PATCH 3/4] Minor reformulations, refactorings, and clarifications --- docs/references/_attachments/ic.did | 34 +++++++++++++++++++--------- docs/references/ic-interface-spec.md | 12 ++++++---- 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/docs/references/_attachments/ic.did b/docs/references/_attachments/ic.did index f59d8fee44..7e00ee8901 100644 --- a/docs/references/_attachments/ic.did +++ b/docs/references/_attachments/ic.did @@ -337,6 +337,27 @@ type sign_with_schnorr_result = record { signature : blob; }; +type vetkd_public_key_args = record { + canister_id : opt canister_id; + derivation_path : vec blob; + key_id : record { curve : vetkd_curve; name : text }; +}; + +type vetkd_public_key_result = record { + public_key : blob; +}; + +type vetkd_encrypted_key_args = record { + derivation_id : blob; + encryption_public_key : blob; + public_key_derivation_path : vec blob; + key_id : record { curve : vetkd_curve; name : text }; +}; + +type vetkd_encrypted_key_result = record { + encrypted_key : blob; +}; + type node_metrics_history_args = record { subnet_id : principal; start_at_timestamp_nanos : nat64; @@ -440,17 +461,8 @@ service ic : { sign_with_schnorr : (sign_with_schnorr_args) -> (sign_with_schnorr_result); // Threshold key derivation - vetkd_public_key : (record { - canister_id : opt canister_id; - derivation_path : vec blob; - key_id : record { curve : vetkd_curve; name : text }; - }) -> (record { public_key : blob; }); - vetkd_encrypted_key : (record { - public_key_derivation_path : vec blob; - derivation_id : blob; - key_id : record { curve : vetkd_curve; name : text }; - encryption_public_key : blob; - }) -> (record { encrypted_key : blob; }); + vetkd_public_key : (vetkd_public_key_args) -> (vetkd_public_key_result); + vetkd_encrypted_key : (vetkd_encrypted_key_args) -> (vetkd_encrypted_key_result); // bitcoin interface bitcoin_get_balance : (bitcoin_get_balance_args) -> (bitcoin_get_balance_result); diff --git a/docs/references/ic-interface-spec.md b/docs/references/ic-interface-spec.md index 7f28d807d9..9ae0e8cc76 100644 --- a/docs/references/ic-interface-spec.md +++ b/docs/references/ic-interface-spec.md @@ -2459,13 +2459,15 @@ The vetKD API is considered EXPERIMENTAL. Canister developers must be aware that ::: +This method can only be called by canisters, i.e., it cannot be called by external users via ingress messages. + This method returns a (derived) vetKD public key for the given canister using the given derivation path. If the `canister_id` is unspecified, it will default to the canister id of the caller. The `derivation_path` is a vector of variable length byte strings, containing at most 255 byte strings. The `key_id` is a struct specifying both a curve and a name. The availability of a particular `key_id` depends on the implementation. For curve `bls12_381`, the returned `public_key` is a G2 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 the vetKD feature is enabled, and the `canister_id` meets the requirement of a canister id. Otherwise it will be rejected. +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_encrypted_key` {#ic-vetkd_encrypted_key} @@ -2475,9 +2477,11 @@ The vetKD API is considered EXPERIMENTAL. Canister developers must be aware that ::: -This method returns a vetKD key encrypted under `encryption_public_key` for the given derivation information consisting of `public_key_derivation_path` and `derivation_id`. +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 `encryption_public_key` derived from the master key with ID `key_id` for the given derivation information consisting of `derivation_id` and `public_key_derivation_path`. -The `public_key_derivation_path` is a vector of variable length byte strings, containing at most 255 byte strings, and (together with the calling canister's ID) acts as domain separator to derive different public (verification) keys with the IC method `vetkd_public_key`. The `derivation_id` is used to derive different encrypted keys. 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 `derivation_id` is used to derive different encrypted keys. The `public_key_derivation_path` is a vector of variable length byte strings, containing at most 255 byte strings, and acts (together with the calling canister's ID) as domain separator to derive different public (verification) keys with the IC method `vetkd_public_key`. The `key_id` is a struct specifying both a curve and a name. The availability of a particular `key_id` depends on the implementation. For curve `bls12_381`, the following holds: @@ -2503,7 +2507,7 @@ where - `·` and · denote concatenation -This call requires that the vetKD feature is enabled and the caller is a canister. Otherwise it will be rejected. +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). From dff6e57af420780f09ef19bb3e0e2078347426d9 Mon Sep 17 00:00:00 2001 From: Franz-Stefan Preiss Date: Mon, 27 Jan 2025 16:28:35 +0100 Subject: [PATCH 4/4] Minor renamings --- docs/references/_attachments/ic.did | 10 +++++----- docs/references/ic-interface-spec.md | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/references/_attachments/ic.did b/docs/references/_attachments/ic.did index 7e00ee8901..0026df41e9 100644 --- a/docs/references/_attachments/ic.did +++ b/docs/references/_attachments/ic.did @@ -84,7 +84,7 @@ type ecdsa_curve = variant { }; type vetkd_curve = variant { - bls12_381; + bls12_381_g2; }; type schnorr_algorithm = variant { @@ -347,14 +347,14 @@ type vetkd_public_key_result = record { public_key : blob; }; -type vetkd_encrypted_key_args = record { +type vetkd_derive_encrypted_key_args = record { derivation_id : blob; encryption_public_key : blob; - public_key_derivation_path : vec blob; + derivation_path : vec blob; key_id : record { curve : vetkd_curve; name : text }; }; -type vetkd_encrypted_key_result = record { +type vetkd_derive_encrypted_key_result = record { encrypted_key : blob; }; @@ -462,7 +462,7 @@ service ic : { // Threshold key derivation vetkd_public_key : (vetkd_public_key_args) -> (vetkd_public_key_result); - vetkd_encrypted_key : (vetkd_encrypted_key_args) -> (vetkd_encrypted_key_result); + vetkd_derive_encrypted_key : (vetkd_derive_encrypted_key_args) -> (vetkd_derive_encrypted_key_result); // bitcoin interface bitcoin_get_balance : (bitcoin_get_balance_args) -> (bitcoin_get_balance_result); diff --git a/docs/references/ic-interface-spec.md b/docs/references/ic-interface-spec.md index 9ae0e8cc76..fe00e2d72a 100644 --- a/docs/references/ic-interface-spec.md +++ b/docs/references/ic-interface-spec.md @@ -2465,11 +2465,11 @@ This method returns a (derived) vetKD public key for the given canister using th If the `canister_id` is unspecified, it will default to the canister id of the caller. The `derivation_path` is a vector of variable length byte strings, containing at most 255 byte strings. The `key_id` is a struct specifying both a curve and a name. The availability of a particular `key_id` depends on the implementation. -For curve `bls12_381`, the returned `public_key` is a G2 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. +For curve `bls12_381_g2`, the returned `public_key` is a G2 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_encrypted_key` {#ic-vetkd_encrypted_key} +### IC method `vetkd_derive_encrypted_key` {#ic-vetkd_derive_encrypted_key} :::note @@ -2479,11 +2479,11 @@ The vetKD API is considered EXPERIMENTAL. Canister developers must be aware that 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 `encryption_public_key` derived from the master key with ID `key_id` for the given derivation information consisting of `derivation_id` and `public_key_derivation_path`. +This method returns a vetKD key (aka vetKey) encrypted under `encryption_public_key` derived from the master key with ID `key_id` for the given derivation information consisting of `derivation_id` and `derivation_path`. -The `derivation_id` is used to derive different encrypted keys. The `public_key_derivation_path` is a vector of variable length byte strings, containing at most 255 byte strings, and acts (together with the calling canister's ID) as domain separator to derive different public (verification) keys with the IC method `vetkd_public_key`. 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 `derivation_id` is used to derive different encrypted keys. The `derivation_path` is a vector of variable length byte strings, containing at most 255 byte strings, and acts (together with the calling canister's ID) as domain separator to derive different public (verification) keys with the IC method `vetkd_public_key`. The `key_id` is a struct specifying both a curve and a name. The availability of a particular `key_id` depends on the implementation. -For curve `bls12_381`, the following holds: +For curve `bls12_381_g2`, the following holds: - The `encryption_public_key` is a G1 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. Encryption public keys are created by calculating *epk = g1esk*, where the encryption secret key *esk* is chosen uniformly at random from Zp.