diff --git a/docs/references/ic-interface-spec/abstract-behavior.md b/docs/references/ic-interface-spec/abstract-behavior.md index 0961d2e..387682d 100644 --- a/docs/references/ic-interface-spec/abstract-behavior.md +++ b/docs/references/ic-interface-spec/abstract-behavior.md @@ -348,7 +348,8 @@ SignedDelegation = { delegation : { pubkey : PublicKey; targets : [CanisterId] | Unrestricted; - expiration : Timestamp + expiration : Timestamp; + permissions : Text | Unrestricted }; signature : Signature } @@ -692,7 +693,7 @@ that represents Candid encoding; this is implicitly taking the method types, as #### Envelope Authentication -The following predicate describes when an envelope `E` correctly signs the enclosed request with a key belonging to a user `U`, at time `T`: It returns which canister ids this envelope may be used at (as a set of principals). +The following predicate describes when an envelope `E` correctly signs the enclosed request with a key belonging to a user `U`, at time `T`: It returns which canister ids this envelope may be used at (as a set of principals). Every delegation whose `permissions` field is set must hold a supported value (`"queries"` or `"all"`); otherwise, the predicate fails for requests of any kind. Moreover, the predicate fails for update calls (requests of type `Request`) if any delegation in the chain restricts the sender to query calls and `read_state` requests (`permissions` field set to `"queries"`). ``` verify_envelope({ content = C }, U, T) = { p : p is CanisterID } if U = anonymous_id @@ -700,6 +701,7 @@ verify_envelope({ content = C }, U, T) verify_envelope({ content = C, sender_pubkey = PK, sender_sig = Sig, sender_delegation = DS}, U, T) = TS if U = mk_self_authenticating_id PK ∧ (PK', TS) = verify_delegations(DS, PK, T, { p : p is CanisterId }) + ∧ (C is Request ⇒ ∀ D ∈ DS. D.delegation.permissions ≠ "queries") ∧ verify_signature PK' Sig ("\x0Aic-request" · hash_of_map(C)) ∧ (if PK = canister_signature_pk Signing_canister_id _: C.sender_info = null @@ -711,6 +713,7 @@ verify_delegations([D] · DS, PK, T, TS) = verify_delegations(DS, D.pubkey, T, TS ∩ delegation_targets(D)) if verify_signature PK D.signature ("\x1Aic-request-auth-delegation" · hash_of_map(D.delegation)) ∧ D.delegation.expiration ≥ T + ∧ D.delegation.permissions ∈ { Unrestricted, "all", "queries" } delegation_targets(D) = if D.targets = Unrestricted then { p : p is CanisterId } diff --git a/docs/references/ic-interface-spec/changelog.md b/docs/references/ic-interface-spec/changelog.md index 118a5c8..cccd458 100644 --- a/docs/references/ic-interface-spec/changelog.md +++ b/docs/references/ic-interface-spec/changelog.md @@ -8,6 +8,15 @@ sidebar: ## Changelog {#changelog} + +### 0.63.0 (unreleased) {$0_63_0} +* New optional `permissions` field in request delegations restricting the kinds of requests + the delegation applies for: the value `"queries"` restricts the delegation to query calls + and `read_state` requests, so update calls carrying such a delegation in their chain of + delegations are not accepted; the value `"all"` permits all kinds of requests, same as + omitting the field. Requests of any kind carrying a delegation with any other value of + the `permissions` field are not accepted. + ### 0.62.0 (2025-05-26) {$0_62_0} * Inter-canister response callback messages might still be executed after the condition for `canister_on_low_wasm_memory` is triggered and before the function `canister_on_low_wasm_memory` is executed. diff --git a/docs/references/ic-interface-spec/https-interface.md b/docs/references/ic-interface-spec/https-interface.md index 28f47d1..3f0dd3f 100644 --- a/docs/references/ic-interface-spec/https-interface.md +++ b/docs/references/ic-interface-spec/https-interface.md @@ -545,6 +545,14 @@ Signing transactions can be delegated from one key to another one. If delegation - `targets` (`array` of `CanisterId`, optional): If this field is set, the delegation only applies for requests sent to the canisters in the list. The list must contain no more than 1000 elements; otherwise, the request will not be accepted by the IC. + - `permissions` (`text`, optional): If this field is set, the delegation only applies for the kinds of requests permitted by its value. The following values are supported: + + - `"queries"`: the delegation only applies for query calls and `read_state` requests. Requests to `/call` endpoints (i.e., update calls, including calls to query methods submitted as update calls) are not accepted by the IC if any delegation in the chain carries this value: a subsequent delegation cannot lift the restriction imposed by a previous one. + + - `"all"`: the delegation applies for all kinds of requests; this is the same as omitting the field. + + If this field is set to any other value, then the delegation is invalid and requests of any kind whose chain of delegations contains the delegation will not be accepted by the IC. + - `signature` (`blob`): Signature on the 32-byte [representation-independent hash](#hash-of-map) of the map contained in the `delegation` field as described in [Signatures](./index.md#signatures), using the 27 bytes `\x1Aic-request-auth-delegation` as the domain separator. For the first delegation in the array, this signature is created with the key corresponding to the public key from the `sender_pubkey` field, all subsequent delegations are signed with the key corresponding to the public key contained in the preceding delegation. @@ -571,7 +579,7 @@ Field values are hashed as follows: - Binary blobs (`canister_id`, `arg`, `nonce`, `module`) are hashed as-is. -- Strings (`request_type`, `method_name`) are hashed by hashing their binary encoding in UTF-8, without a terminal `\x00`. +- Strings (`request_type`, `method_name`, `permissions`) are hashed by hashing their binary encoding in UTF-8, without a terminal `\x00`. - Natural numbers (`compute_allocation`, `memory_allocation`, `ingress_expiry`) are hashed by hashing their binary encoding using the shortest form [Unsigned LEB128](https://en.wikipedia.org/wiki/LEB128#Unsigned_LEB128) encoding. For example, `0` should be encoded as a single zero byte `[0x00]` and `624485` should be encoded as byte sequence `[0xE5, 0x8E, 0x26]`.