diff --git a/.gitignore b/.gitignore index 7666be2..6b4786b 100644 --- a/.gitignore +++ b/.gitignore @@ -15,5 +15,4 @@ target/ debug .dfx -.env -src/declarations \ No newline at end of file +.env \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 85bc229..59efa22 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -213,14 +213,14 @@ dependencies = [ "lazy_static", "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] name = "cc" -version = "1.1.15" +version = "1.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6" +checksum = "e9d013ecb737093c0e86b151a7b837993cf9ec6c502946cfb44bedc392421e0b" dependencies = [ "shlex", ] @@ -449,7 +449,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -698,7 +698,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -929,7 +929,7 @@ dependencies = [ "quote", "serde", "serde_tokenstream", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -943,7 +943,7 @@ dependencies = [ "quote", "serde", "serde_tokenstream", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -1204,7 +1204,7 @@ checksum = "8de254dd67bbd58073e23dc1c8553ba12fa1dc610a19de94ad2bbcd0460c067f" [[package]] name = "ic_cose_canister" -version = "0.3.1" +version = "0.3.2" dependencies = [ "candid", "ciborium", @@ -1222,7 +1222,7 @@ dependencies = [ [[package]] name = "ic_cose_types" -version = "0.3.1" +version = "0.3.2" dependencies = [ "aes-gcm", "candid", @@ -1640,14 +1640,14 @@ dependencies = [ "itertools", "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] name = "psm" -version = "0.1.21" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" +checksum = "aa37f80ca58604976033fae9515a8a2989fc13797d953f7c04fb8fa36a11f205" dependencies = [ "cc", ] @@ -1799,7 +1799,7 @@ checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -1823,7 +1823,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -1972,7 +1972,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -1994,9 +1994,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.76" +version = "2.0.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578e081a14e0cefc3279b0472138c513f37b41a08d5a3cca9b6e4e8ceb6cd525" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" dependencies = [ "proc-macro2", "quote", @@ -2020,7 +2020,7 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -2146,7 +2146,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", "wasm-bindgen-shared", ] @@ -2168,7 +2168,7 @@ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2297,7 +2297,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -2317,5 +2317,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] diff --git a/Cargo.toml b/Cargo.toml index 6c837e1..4a33855 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ strip = true opt-level = 's' [workspace.package] -version = "0.3.1" +version = "0.3.2" edition = "2021" repository = "https://github.com/ldclabs/ic-cose" keywords = ["config", "cbor", "canister", "icp", "encryption"] diff --git a/src/declarations/ic_cose_canister/ic_cose_canister.did b/src/declarations/ic_cose_canister/ic_cose_canister.did new file mode 100644 index 0000000..4fa0405 --- /dev/null +++ b/src/declarations/ic_cose_canister/ic_cose_canister.did @@ -0,0 +1,177 @@ +type ChainArgs = variant { Upgrade : UpgradeArgs; Init : InitArgs }; +type CreateNamespaceInput = record { + managers : vec principal; + desc : opt text; + name : text; + max_payload_size : opt nat64; + auditors : vec principal; + users : vec principal; + visibility : nat8; +}; +type CreateSettingInput = record { + dek : opt blob; + status : opt int8; + desc : opt text; + tags : opt vec record { text; text }; + payload : opt blob; +}; +type CreateSettingOutput = record { + updated_at : nat64; + created_at : nat64; + version : nat32; +}; +type ECDHInput = record { public_key : blob; nonce : blob }; +type ECDHOutput = record { public_key : blob; payload : blob }; +type InitArgs = record { + freezing_threshold : nat64; + ecdsa_key_name : text; + name : text; + schnorr_key_name : text; + allowed_apis : vec text; + subnet_size : nat64; + vetkd_key_name : text; +}; +type NamespaceInfo = record { + status : int8; + updated_at : nat64; + managers : vec principal; + payload_bytes_total : nat64; + desc : text; + name : text; + max_payload_size : nat64; + created_at : nat64; + auditors : vec principal; + settings_total : nat64; + user_settings_total : nat64; + users : vec principal; + visibility : nat8; + gas_balance : nat; +}; +type PublicKeyInput = record { ns : text; derivation_path : vec blob }; +type PublicKeyOutput = record { public_key : blob; chain_code : blob }; +type Result = variant { Ok; Err : text }; +type Result_1 = variant { Ok : NamespaceInfo; Err : text }; +type Result_10 = variant { Ok : StateInfo; Err : text }; +type Result_2 = variant { Ok : vec NamespaceInfo; Err : text }; +type Result_3 = variant { Ok : ECDHOutput; Err : text }; +type Result_4 = variant { Ok : PublicKeyOutput; Err : text }; +type Result_5 = variant { Ok : blob; Err : text }; +type Result_6 = variant { Ok : nat; Err : text }; +type Result_7 = variant { Ok : CreateSettingOutput; Err : text }; +type Result_8 = variant { Ok : SettingInfo; Err : text }; +type Result_9 = variant { Ok : SettingArchivedPayload; Err : text }; +type SchnorrAlgorithm = variant { ed25519; bip340secp256k1 }; +type SettingArchivedPayload = record { + dek : opt blob; + version : nat32; + deprecated : bool; + archived_at : nat64; + payload : opt blob; +}; +type SettingInfo = record { + dek : opt blob; + key : blob; + readers : vec principal; + status : int8; + updated_at : nat64; + subject : principal; + desc : text; + tags : vec record { text; text }; + created_at : nat64; + version : nat32; + payload : opt blob; +}; +type SettingPath = record { + ns : text; + key : blob; + subject : opt principal; + version : nat32; + user_owned : bool; +}; +type SignIdentityInput = record { ns : text; audience : text }; +type SignInput = record { + ns : text; + derivation_path : vec blob; + message : blob; +}; +type StateInfo = record { + freezing_threshold : nat64; + ecdsa_key_name : text; + managers : vec principal; + name : text; + auditors : vec principal; + schnorr_key_name : text; + allowed_apis : vec text; + subnet_size : nat64; + namespace_total : nat64; + vetkd_key_name : text; +}; +type UpdateNamespaceInput = record { + status : opt int8; + desc : opt text; + name : text; + max_payload_size : opt nat64; + visibility : opt nat8; +}; +type UpdateSettingInfoInput = record { + status : opt int8; + desc : opt text; + tags : opt vec record { text; text }; +}; +type UpdateSettingPayloadInput = record { + dek : opt blob; + status : opt int8; + deprecate_current : opt bool; + payload : opt blob; +}; +type UpgradeArgs = record { + freezing_threshold : opt nat64; + name : opt text; + subnet_size : opt nat64; +}; +service : (opt ChainArgs) -> { + admin_add_allowed_apis : (vec text) -> (Result); + admin_add_auditors : (vec principal) -> (Result); + admin_add_managers : (vec principal) -> (Result); + admin_create_namespace : (CreateNamespaceInput) -> (Result_1); + admin_list_namespace : (opt text, opt nat32) -> (Result_2) query; + admin_remove_allowed_apis : (vec text) -> (Result); + admin_remove_auditors : (vec principal) -> (Result); + admin_remove_managers : (vec principal) -> (Result); + ecdh_cose_encrypted_key : (SettingPath, ECDHInput) -> (Result_3); + ecdsa_public_key : (opt PublicKeyInput) -> (Result_4) query; + ecdsa_sign : (SignInput) -> (Result_5); + namespace_add_auditors : (text, vec principal) -> (Result); + namespace_add_managers : (text, vec principal) -> (Result); + namespace_add_users : (text, vec principal) -> (Result); + namespace_get_info : (text) -> (Result_1) query; + namespace_remove_auditors : (text, vec principal) -> (Result); + namespace_remove_managers : (text, vec principal) -> (Result); + namespace_remove_users : (text, vec principal) -> (Result); + namespace_top_up : (text, nat) -> (Result_6); + namespace_update_info : (UpdateNamespaceInput) -> (Result); + schnorr_public_key : (SchnorrAlgorithm, opt PublicKeyInput) -> ( + Result_4, + ) query; + schnorr_sign : (SchnorrAlgorithm, SignInput) -> (Result_5); + schnorr_sign_identity : (SchnorrAlgorithm, SignIdentityInput) -> (Result_5); + setting_add_readers : (SettingPath, vec principal) -> (Result); + setting_create : (SettingPath, CreateSettingInput) -> (Result_7); + setting_get : (SettingPath) -> (Result_8) query; + setting_get_archived_payload : (SettingPath) -> (Result_9) query; + setting_get_info : (SettingPath) -> (Result_8) query; + setting_remove_readers : (SettingPath, vec principal) -> (Result); + setting_update_info : (SettingPath, UpdateSettingInfoInput) -> (Result_7); + setting_update_payload : (SettingPath, UpdateSettingPayloadInput) -> ( + Result_7, + ); + state_get_info : () -> (Result_10) query; + validate_admin_add_allowed_apis : (vec text) -> (Result); + validate_admin_add_auditors : (vec principal) -> (Result); + validate_admin_add_managers : (vec principal) -> (Result); + validate_admin_remove_allowed_apis : (vec text) -> (Result); + validate_admin_remove_auditors : (vec principal) -> (Result); + validate_admin_remove_managers : (vec principal) -> (Result); + vetkd_encrypted_key : (SettingPath, blob) -> (Result_5); + vetkd_public_key : (SettingPath) -> (Result_5); +} diff --git a/src/declarations/ic_cose_canister/ic_cose_canister.did.d.ts b/src/declarations/ic_cose_canister/ic_cose_canister.did.d.ts new file mode 100644 index 0000000..d6d56c3 --- /dev/null +++ b/src/declarations/ic_cose_canister/ic_cose_canister.did.d.ts @@ -0,0 +1,225 @@ +import type { Principal } from '@dfinity/principal'; +import type { ActorMethod } from '@dfinity/agent'; +import type { IDL } from '@dfinity/candid'; + +export type ChainArgs = { 'Upgrade' : UpgradeArgs } | + { 'Init' : InitArgs }; +export interface CreateNamespaceInput { + 'managers' : Array, + 'desc' : [] | [string], + 'name' : string, + 'max_payload_size' : [] | [bigint], + 'auditors' : Array, + 'users' : Array, + 'visibility' : number, +} +export interface CreateSettingInput { + 'dek' : [] | [Uint8Array | number[]], + 'status' : [] | [number], + 'desc' : [] | [string], + 'tags' : [] | [Array<[string, string]>], + 'payload' : [] | [Uint8Array | number[]], +} +export interface CreateSettingOutput { + 'updated_at' : bigint, + 'created_at' : bigint, + 'version' : number, +} +export interface ECDHInput { + 'public_key' : Uint8Array | number[], + 'nonce' : Uint8Array | number[], +} +export interface ECDHOutput { + 'public_key' : Uint8Array | number[], + 'payload' : Uint8Array | number[], +} +export interface InitArgs { + 'freezing_threshold' : bigint, + 'ecdsa_key_name' : string, + 'name' : string, + 'schnorr_key_name' : string, + 'allowed_apis' : Array, + 'subnet_size' : bigint, + 'vetkd_key_name' : string, +} +export interface NamespaceInfo { + 'status' : number, + 'updated_at' : bigint, + 'managers' : Array, + 'payload_bytes_total' : bigint, + 'desc' : string, + 'name' : string, + 'max_payload_size' : bigint, + 'created_at' : bigint, + 'auditors' : Array, + 'settings_total' : bigint, + 'user_settings_total' : bigint, + 'users' : Array, + 'visibility' : number, + 'gas_balance' : bigint, +} +export interface PublicKeyInput { + 'ns' : string, + 'derivation_path' : Array, +} +export interface PublicKeyOutput { + 'public_key' : Uint8Array | number[], + 'chain_code' : Uint8Array | number[], +} +export type Result = { 'Ok' : null } | + { 'Err' : string }; +export type Result_1 = { 'Ok' : NamespaceInfo } | + { 'Err' : string }; +export type Result_10 = { 'Ok' : StateInfo } | + { 'Err' : string }; +export type Result_2 = { 'Ok' : Array } | + { 'Err' : string }; +export type Result_3 = { 'Ok' : ECDHOutput } | + { 'Err' : string }; +export type Result_4 = { 'Ok' : PublicKeyOutput } | + { 'Err' : string }; +export type Result_5 = { 'Ok' : Uint8Array | number[] } | + { 'Err' : string }; +export type Result_6 = { 'Ok' : bigint } | + { 'Err' : string }; +export type Result_7 = { 'Ok' : CreateSettingOutput } | + { 'Err' : string }; +export type Result_8 = { 'Ok' : SettingInfo } | + { 'Err' : string }; +export type Result_9 = { 'Ok' : SettingArchivedPayload } | + { 'Err' : string }; +export type SchnorrAlgorithm = { 'ed25519' : null } | + { 'bip340secp256k1' : null }; +export interface SettingArchivedPayload { + 'dek' : [] | [Uint8Array | number[]], + 'version' : number, + 'deprecated' : boolean, + 'archived_at' : bigint, + 'payload' : [] | [Uint8Array | number[]], +} +export interface SettingInfo { + 'dek' : [] | [Uint8Array | number[]], + 'key' : Uint8Array | number[], + 'readers' : Array, + 'status' : number, + 'updated_at' : bigint, + 'subject' : Principal, + 'desc' : string, + 'tags' : Array<[string, string]>, + 'created_at' : bigint, + 'version' : number, + 'payload' : [] | [Uint8Array | number[]], +} +export interface SettingPath { + 'ns' : string, + 'key' : Uint8Array | number[], + 'subject' : [] | [Principal], + 'version' : number, + 'user_owned' : boolean, +} +export interface SignIdentityInput { 'ns' : string, 'audience' : string } +export interface SignInput { + 'ns' : string, + 'derivation_path' : Array, + 'message' : Uint8Array | number[], +} +export interface StateInfo { + 'freezing_threshold' : bigint, + 'ecdsa_key_name' : string, + 'managers' : Array, + 'name' : string, + 'auditors' : Array, + 'schnorr_key_name' : string, + 'allowed_apis' : Array, + 'subnet_size' : bigint, + 'namespace_total' : bigint, + 'vetkd_key_name' : string, +} +export interface UpdateNamespaceInput { + 'status' : [] | [number], + 'desc' : [] | [string], + 'name' : string, + 'max_payload_size' : [] | [bigint], + 'visibility' : [] | [number], +} +export interface UpdateSettingInfoInput { + 'status' : [] | [number], + 'desc' : [] | [string], + 'tags' : [] | [Array<[string, string]>], +} +export interface UpdateSettingPayloadInput { + 'dek' : [] | [Uint8Array | number[]], + 'status' : [] | [number], + 'deprecate_current' : [] | [boolean], + 'payload' : [] | [Uint8Array | number[]], +} +export interface UpgradeArgs { + 'freezing_threshold' : [] | [bigint], + 'name' : [] | [string], + 'subnet_size' : [] | [bigint], +} +export interface _SERVICE { + 'admin_add_allowed_apis' : ActorMethod<[Array], Result>, + 'admin_add_auditors' : ActorMethod<[Array], Result>, + 'admin_add_managers' : ActorMethod<[Array], Result>, + 'admin_create_namespace' : ActorMethod<[CreateNamespaceInput], Result_1>, + 'admin_list_namespace' : ActorMethod< + [[] | [string], [] | [number]], + Result_2 + >, + 'admin_remove_allowed_apis' : ActorMethod<[Array], Result>, + 'admin_remove_auditors' : ActorMethod<[Array], Result>, + 'admin_remove_managers' : ActorMethod<[Array], Result>, + 'ecdh_cose_encrypted_key' : ActorMethod<[SettingPath, ECDHInput], Result_3>, + 'ecdsa_public_key' : ActorMethod<[[] | [PublicKeyInput]], Result_4>, + 'ecdsa_sign' : ActorMethod<[SignInput], Result_5>, + 'namespace_add_auditors' : ActorMethod<[string, Array], Result>, + 'namespace_add_managers' : ActorMethod<[string, Array], Result>, + 'namespace_add_users' : ActorMethod<[string, Array], Result>, + 'namespace_get_info' : ActorMethod<[string], Result_1>, + 'namespace_remove_auditors' : ActorMethod<[string, Array], Result>, + 'namespace_remove_managers' : ActorMethod<[string, Array], Result>, + 'namespace_remove_users' : ActorMethod<[string, Array], Result>, + 'namespace_top_up' : ActorMethod<[string, bigint], Result_6>, + 'namespace_update_info' : ActorMethod<[UpdateNamespaceInput], Result>, + 'schnorr_public_key' : ActorMethod< + [SchnorrAlgorithm, [] | [PublicKeyInput]], + Result_4 + >, + 'schnorr_sign' : ActorMethod<[SchnorrAlgorithm, SignInput], Result_5>, + 'schnorr_sign_identity' : ActorMethod< + [SchnorrAlgorithm, SignIdentityInput], + Result_5 + >, + 'setting_add_readers' : ActorMethod<[SettingPath, Array], Result>, + 'setting_create' : ActorMethod<[SettingPath, CreateSettingInput], Result_7>, + 'setting_get' : ActorMethod<[SettingPath], Result_8>, + 'setting_get_archived_payload' : ActorMethod<[SettingPath], Result_9>, + 'setting_get_info' : ActorMethod<[SettingPath], Result_8>, + 'setting_remove_readers' : ActorMethod< + [SettingPath, Array], + Result + >, + 'setting_update_info' : ActorMethod< + [SettingPath, UpdateSettingInfoInput], + Result_7 + >, + 'setting_update_payload' : ActorMethod< + [SettingPath, UpdateSettingPayloadInput], + Result_7 + >, + 'state_get_info' : ActorMethod<[], Result_10>, + 'validate_admin_add_allowed_apis' : ActorMethod<[Array], Result>, + 'validate_admin_add_auditors' : ActorMethod<[Array], Result>, + 'validate_admin_add_managers' : ActorMethod<[Array], Result>, + 'validate_admin_remove_allowed_apis' : ActorMethod<[Array], Result>, + 'validate_admin_remove_auditors' : ActorMethod<[Array], Result>, + 'validate_admin_remove_managers' : ActorMethod<[Array], Result>, + 'vetkd_encrypted_key' : ActorMethod< + [SettingPath, Uint8Array | number[]], + Result_5 + >, + 'vetkd_public_key' : ActorMethod<[SettingPath], Result_5>, +} +export declare const idlFactory: IDL.InterfaceFactory; +export declare const init: (args: { IDL: typeof IDL }) => IDL.Type[]; diff --git a/src/declarations/ic_cose_canister/ic_cose_canister.did.js b/src/declarations/ic_cose_canister/ic_cose_canister.did.js new file mode 100644 index 0000000..d2ae96c --- /dev/null +++ b/src/declarations/ic_cose_canister/ic_cose_canister.did.js @@ -0,0 +1,316 @@ +export const idlFactory = ({ IDL }) => { + const UpgradeArgs = IDL.Record({ + 'freezing_threshold' : IDL.Opt(IDL.Nat64), + 'name' : IDL.Opt(IDL.Text), + 'subnet_size' : IDL.Opt(IDL.Nat64), + }); + const InitArgs = IDL.Record({ + 'freezing_threshold' : IDL.Nat64, + 'ecdsa_key_name' : IDL.Text, + 'name' : IDL.Text, + 'schnorr_key_name' : IDL.Text, + 'allowed_apis' : IDL.Vec(IDL.Text), + 'subnet_size' : IDL.Nat64, + 'vetkd_key_name' : IDL.Text, + }); + const ChainArgs = IDL.Variant({ 'Upgrade' : UpgradeArgs, 'Init' : InitArgs }); + const Result = IDL.Variant({ 'Ok' : IDL.Null, 'Err' : IDL.Text }); + const CreateNamespaceInput = IDL.Record({ + 'managers' : IDL.Vec(IDL.Principal), + 'desc' : IDL.Opt(IDL.Text), + 'name' : IDL.Text, + 'max_payload_size' : IDL.Opt(IDL.Nat64), + 'auditors' : IDL.Vec(IDL.Principal), + 'users' : IDL.Vec(IDL.Principal), + 'visibility' : IDL.Nat8, + }); + const NamespaceInfo = IDL.Record({ + 'status' : IDL.Int8, + 'updated_at' : IDL.Nat64, + 'managers' : IDL.Vec(IDL.Principal), + 'payload_bytes_total' : IDL.Nat64, + 'desc' : IDL.Text, + 'name' : IDL.Text, + 'max_payload_size' : IDL.Nat64, + 'created_at' : IDL.Nat64, + 'auditors' : IDL.Vec(IDL.Principal), + 'settings_total' : IDL.Nat64, + 'user_settings_total' : IDL.Nat64, + 'users' : IDL.Vec(IDL.Principal), + 'visibility' : IDL.Nat8, + 'gas_balance' : IDL.Nat, + }); + const Result_1 = IDL.Variant({ 'Ok' : NamespaceInfo, 'Err' : IDL.Text }); + const Result_2 = IDL.Variant({ + 'Ok' : IDL.Vec(NamespaceInfo), + 'Err' : IDL.Text, + }); + const SettingPath = IDL.Record({ + 'ns' : IDL.Text, + 'key' : IDL.Vec(IDL.Nat8), + 'subject' : IDL.Opt(IDL.Principal), + 'version' : IDL.Nat32, + 'user_owned' : IDL.Bool, + }); + const ECDHInput = IDL.Record({ + 'public_key' : IDL.Vec(IDL.Nat8), + 'nonce' : IDL.Vec(IDL.Nat8), + }); + const ECDHOutput = IDL.Record({ + 'public_key' : IDL.Vec(IDL.Nat8), + 'payload' : IDL.Vec(IDL.Nat8), + }); + const Result_3 = IDL.Variant({ 'Ok' : ECDHOutput, 'Err' : IDL.Text }); + const PublicKeyInput = IDL.Record({ + 'ns' : IDL.Text, + 'derivation_path' : IDL.Vec(IDL.Vec(IDL.Nat8)), + }); + const PublicKeyOutput = IDL.Record({ + 'public_key' : IDL.Vec(IDL.Nat8), + 'chain_code' : IDL.Vec(IDL.Nat8), + }); + const Result_4 = IDL.Variant({ 'Ok' : PublicKeyOutput, 'Err' : IDL.Text }); + const SignInput = IDL.Record({ + 'ns' : IDL.Text, + 'derivation_path' : IDL.Vec(IDL.Vec(IDL.Nat8)), + 'message' : IDL.Vec(IDL.Nat8), + }); + const Result_5 = IDL.Variant({ 'Ok' : IDL.Vec(IDL.Nat8), 'Err' : IDL.Text }); + const Result_6 = IDL.Variant({ 'Ok' : IDL.Nat, 'Err' : IDL.Text }); + const UpdateNamespaceInput = IDL.Record({ + 'status' : IDL.Opt(IDL.Int8), + 'desc' : IDL.Opt(IDL.Text), + 'name' : IDL.Text, + 'max_payload_size' : IDL.Opt(IDL.Nat64), + 'visibility' : IDL.Opt(IDL.Nat8), + }); + const SchnorrAlgorithm = IDL.Variant({ + 'ed25519' : IDL.Null, + 'bip340secp256k1' : IDL.Null, + }); + const SignIdentityInput = IDL.Record({ + 'ns' : IDL.Text, + 'audience' : IDL.Text, + }); + const CreateSettingInput = IDL.Record({ + 'dek' : IDL.Opt(IDL.Vec(IDL.Nat8)), + 'status' : IDL.Opt(IDL.Int8), + 'desc' : IDL.Opt(IDL.Text), + 'tags' : IDL.Opt(IDL.Vec(IDL.Tuple(IDL.Text, IDL.Text))), + 'payload' : IDL.Opt(IDL.Vec(IDL.Nat8)), + }); + const CreateSettingOutput = IDL.Record({ + 'updated_at' : IDL.Nat64, + 'created_at' : IDL.Nat64, + 'version' : IDL.Nat32, + }); + const Result_7 = IDL.Variant({ + 'Ok' : CreateSettingOutput, + 'Err' : IDL.Text, + }); + const SettingInfo = IDL.Record({ + 'dek' : IDL.Opt(IDL.Vec(IDL.Nat8)), + 'key' : IDL.Vec(IDL.Nat8), + 'readers' : IDL.Vec(IDL.Principal), + 'status' : IDL.Int8, + 'updated_at' : IDL.Nat64, + 'subject' : IDL.Principal, + 'desc' : IDL.Text, + 'tags' : IDL.Vec(IDL.Tuple(IDL.Text, IDL.Text)), + 'created_at' : IDL.Nat64, + 'version' : IDL.Nat32, + 'payload' : IDL.Opt(IDL.Vec(IDL.Nat8)), + }); + const Result_8 = IDL.Variant({ 'Ok' : SettingInfo, 'Err' : IDL.Text }); + const SettingArchivedPayload = IDL.Record({ + 'dek' : IDL.Opt(IDL.Vec(IDL.Nat8)), + 'version' : IDL.Nat32, + 'deprecated' : IDL.Bool, + 'archived_at' : IDL.Nat64, + 'payload' : IDL.Opt(IDL.Vec(IDL.Nat8)), + }); + const Result_9 = IDL.Variant({ + 'Ok' : SettingArchivedPayload, + 'Err' : IDL.Text, + }); + const UpdateSettingInfoInput = IDL.Record({ + 'status' : IDL.Opt(IDL.Int8), + 'desc' : IDL.Opt(IDL.Text), + 'tags' : IDL.Opt(IDL.Vec(IDL.Tuple(IDL.Text, IDL.Text))), + }); + const UpdateSettingPayloadInput = IDL.Record({ + 'dek' : IDL.Opt(IDL.Vec(IDL.Nat8)), + 'status' : IDL.Opt(IDL.Int8), + 'deprecate_current' : IDL.Opt(IDL.Bool), + 'payload' : IDL.Opt(IDL.Vec(IDL.Nat8)), + }); + const StateInfo = IDL.Record({ + 'freezing_threshold' : IDL.Nat64, + 'ecdsa_key_name' : IDL.Text, + 'managers' : IDL.Vec(IDL.Principal), + 'name' : IDL.Text, + 'auditors' : IDL.Vec(IDL.Principal), + 'schnorr_key_name' : IDL.Text, + 'allowed_apis' : IDL.Vec(IDL.Text), + 'subnet_size' : IDL.Nat64, + 'namespace_total' : IDL.Nat64, + 'vetkd_key_name' : IDL.Text, + }); + const Result_10 = IDL.Variant({ 'Ok' : StateInfo, 'Err' : IDL.Text }); + return IDL.Service({ + 'admin_add_allowed_apis' : IDL.Func([IDL.Vec(IDL.Text)], [Result], []), + 'admin_add_auditors' : IDL.Func([IDL.Vec(IDL.Principal)], [Result], []), + 'admin_add_managers' : IDL.Func([IDL.Vec(IDL.Principal)], [Result], []), + 'admin_create_namespace' : IDL.Func([CreateNamespaceInput], [Result_1], []), + 'admin_list_namespace' : IDL.Func( + [IDL.Opt(IDL.Text), IDL.Opt(IDL.Nat32)], + [Result_2], + ['query'], + ), + 'admin_remove_allowed_apis' : IDL.Func([IDL.Vec(IDL.Text)], [Result], []), + 'admin_remove_auditors' : IDL.Func([IDL.Vec(IDL.Principal)], [Result], []), + 'admin_remove_managers' : IDL.Func([IDL.Vec(IDL.Principal)], [Result], []), + 'ecdh_cose_encrypted_key' : IDL.Func( + [SettingPath, ECDHInput], + [Result_3], + [], + ), + 'ecdsa_public_key' : IDL.Func( + [IDL.Opt(PublicKeyInput)], + [Result_4], + ['query'], + ), + 'ecdsa_sign' : IDL.Func([SignInput], [Result_5], []), + 'namespace_add_auditors' : IDL.Func( + [IDL.Text, IDL.Vec(IDL.Principal)], + [Result], + [], + ), + 'namespace_add_managers' : IDL.Func( + [IDL.Text, IDL.Vec(IDL.Principal)], + [Result], + [], + ), + 'namespace_add_users' : IDL.Func( + [IDL.Text, IDL.Vec(IDL.Principal)], + [Result], + [], + ), + 'namespace_get_info' : IDL.Func([IDL.Text], [Result_1], ['query']), + 'namespace_remove_auditors' : IDL.Func( + [IDL.Text, IDL.Vec(IDL.Principal)], + [Result], + [], + ), + 'namespace_remove_managers' : IDL.Func( + [IDL.Text, IDL.Vec(IDL.Principal)], + [Result], + [], + ), + 'namespace_remove_users' : IDL.Func( + [IDL.Text, IDL.Vec(IDL.Principal)], + [Result], + [], + ), + 'namespace_top_up' : IDL.Func([IDL.Text, IDL.Nat], [Result_6], []), + 'namespace_update_info' : IDL.Func([UpdateNamespaceInput], [Result], []), + 'schnorr_public_key' : IDL.Func( + [SchnorrAlgorithm, IDL.Opt(PublicKeyInput)], + [Result_4], + ['query'], + ), + 'schnorr_sign' : IDL.Func([SchnorrAlgorithm, SignInput], [Result_5], []), + 'schnorr_sign_identity' : IDL.Func( + [SchnorrAlgorithm, SignIdentityInput], + [Result_5], + [], + ), + 'setting_add_readers' : IDL.Func( + [SettingPath, IDL.Vec(IDL.Principal)], + [Result], + [], + ), + 'setting_create' : IDL.Func( + [SettingPath, CreateSettingInput], + [Result_7], + [], + ), + 'setting_get' : IDL.Func([SettingPath], [Result_8], ['query']), + 'setting_get_archived_payload' : IDL.Func( + [SettingPath], + [Result_9], + ['query'], + ), + 'setting_get_info' : IDL.Func([SettingPath], [Result_8], ['query']), + 'setting_remove_readers' : IDL.Func( + [SettingPath, IDL.Vec(IDL.Principal)], + [Result], + [], + ), + 'setting_update_info' : IDL.Func( + [SettingPath, UpdateSettingInfoInput], + [Result_7], + [], + ), + 'setting_update_payload' : IDL.Func( + [SettingPath, UpdateSettingPayloadInput], + [Result_7], + [], + ), + 'state_get_info' : IDL.Func([], [Result_10], ['query']), + 'validate_admin_add_allowed_apis' : IDL.Func( + [IDL.Vec(IDL.Text)], + [Result], + [], + ), + 'validate_admin_add_auditors' : IDL.Func( + [IDL.Vec(IDL.Principal)], + [Result], + [], + ), + 'validate_admin_add_managers' : IDL.Func( + [IDL.Vec(IDL.Principal)], + [Result], + [], + ), + 'validate_admin_remove_allowed_apis' : IDL.Func( + [IDL.Vec(IDL.Text)], + [Result], + [], + ), + 'validate_admin_remove_auditors' : IDL.Func( + [IDL.Vec(IDL.Principal)], + [Result], + [], + ), + 'validate_admin_remove_managers' : IDL.Func( + [IDL.Vec(IDL.Principal)], + [Result], + [], + ), + 'vetkd_encrypted_key' : IDL.Func( + [SettingPath, IDL.Vec(IDL.Nat8)], + [Result_5], + [], + ), + 'vetkd_public_key' : IDL.Func([SettingPath], [Result_5], []), + }); +}; +export const init = ({ IDL }) => { + const UpgradeArgs = IDL.Record({ + 'freezing_threshold' : IDL.Opt(IDL.Nat64), + 'name' : IDL.Opt(IDL.Text), + 'subnet_size' : IDL.Opt(IDL.Nat64), + }); + const InitArgs = IDL.Record({ + 'freezing_threshold' : IDL.Nat64, + 'ecdsa_key_name' : IDL.Text, + 'name' : IDL.Text, + 'schnorr_key_name' : IDL.Text, + 'allowed_apis' : IDL.Vec(IDL.Text), + 'subnet_size' : IDL.Nat64, + 'vetkd_key_name' : IDL.Text, + }); + const ChainArgs = IDL.Variant({ 'Upgrade' : UpgradeArgs, 'Init' : InitArgs }); + return [IDL.Opt(ChainArgs)]; +}; diff --git a/src/declarations/ic_cose_canister/index.d.ts b/src/declarations/ic_cose_canister/index.d.ts new file mode 100644 index 0000000..420380e --- /dev/null +++ b/src/declarations/ic_cose_canister/index.d.ts @@ -0,0 +1,50 @@ +import type { + ActorSubclass, + HttpAgentOptions, + ActorConfig, + Agent, +} from "@dfinity/agent"; +import type { Principal } from "@dfinity/principal"; +import type { IDL } from "@dfinity/candid"; + +import { _SERVICE } from './ic_cose_canister.did'; + +export declare const idlFactory: IDL.InterfaceFactory; +export declare const canisterId: string; + +export declare interface CreateActorOptions { + /** + * @see {@link Agent} + */ + agent?: Agent; + /** + * @see {@link HttpAgentOptions} + */ + agentOptions?: HttpAgentOptions; + /** + * @see {@link ActorConfig} + */ + actorOptions?: ActorConfig; +} + +/** + * Intializes an {@link ActorSubclass}, configured with the provided SERVICE interface of a canister. + * @constructs {@link ActorSubClass} + * @param {string | Principal} canisterId - ID of the canister the {@link Actor} will talk to + * @param {CreateActorOptions} options - see {@link CreateActorOptions} + * @param {CreateActorOptions["agent"]} options.agent - a pre-configured agent you'd like to use. Supercedes agentOptions + * @param {CreateActorOptions["agentOptions"]} options.agentOptions - options to set up a new agent + * @see {@link HttpAgentOptions} + * @param {CreateActorOptions["actorOptions"]} options.actorOptions - options for the Actor + * @see {@link ActorConfig} + */ +export declare const createActor: ( + canisterId: string | Principal, + options?: CreateActorOptions +) => ActorSubclass<_SERVICE>; + +/** + * Intialized Actor using default settings, ready to talk to a canister using its candid interface + * @constructs {@link ActorSubClass} + */ +export declare const ic_cose_canister: ActorSubclass<_SERVICE>; diff --git a/src/declarations/ic_cose_canister/index.js b/src/declarations/ic_cose_canister/index.js new file mode 100644 index 0000000..1fcf3d5 --- /dev/null +++ b/src/declarations/ic_cose_canister/index.js @@ -0,0 +1,40 @@ +import { Actor, HttpAgent } from "@dfinity/agent"; + +// Imports and re-exports candid interface +import { idlFactory } from "./ic_cose_canister.did.js"; +export { idlFactory } from "./ic_cose_canister.did.js"; + +/* CANISTER_ID is replaced by webpack based on node environment + * Note: canister environment variable will be standardized as + * process.env.CANISTER_ID_ + * beginning in dfx 0.15.0 + */ +export const canisterId = + process.env.CANISTER_ID_IC_COSE_CANISTER; + +export const createActor = (canisterId, options = {}) => { + const agent = options.agent || new HttpAgent({ ...options.agentOptions }); + + if (options.agent && options.agentOptions) { + console.warn( + "Detected both agent and agentOptions passed to createActor. Ignoring agentOptions and proceeding with the provided agent." + ); + } + + // Fetch root key for certificate validation during development + if (process.env.DFX_NETWORK !== "ic") { + agent.fetchRootKey().catch((err) => { + console.warn( + "Unable to fetch root key. Check to ensure that your local replica is running" + ); + console.error(err); + }); + } + + // Creates an actor with using the candid interface and the HttpAgent + return Actor.createActor(idlFactory, { + agent, + canisterId, + ...options.actorOptions, + }); +}; diff --git a/src/ic_cose_canister/ic_cose_canister.did b/src/ic_cose_canister/ic_cose_canister.did index 4a08582..4fa0405 100644 --- a/src/ic_cose_canister/ic_cose_canister.did +++ b/src/ic_cose_canister/ic_cose_canister.did @@ -66,7 +66,7 @@ type SettingArchivedPayload = record { version : nat32; deprecated : bool; archived_at : nat64; - payload : blob; + payload : opt blob; }; type SettingInfo = record { dek : opt blob; @@ -119,9 +119,10 @@ type UpdateSettingInfoInput = record { tags : opt vec record { text; text }; }; type UpdateSettingPayloadInput = record { + dek : opt blob; status : opt int8; deprecate_current : opt bool; - payload : blob; + payload : opt blob; }; type UpgradeArgs = record { freezing_threshold : opt nat64; diff --git a/src/ic_cose_canister/src/store.rs b/src/ic_cose_canister/src/store.rs index 533ee81..f4e9027 100644 --- a/src/ic_cose_canister/src/store.rs +++ b/src/ic_cose_canister/src/store.rs @@ -335,7 +335,7 @@ pub struct SettingArchived { #[serde(rename = "d")] pub deprecated: bool, // true if the payload should not be used for some reason #[serde(rename = "p")] - pub payload: ByteBuf, + pub payload: Option, #[serde(rename = "k")] pub dek: Option, } @@ -345,12 +345,12 @@ impl Storable for SettingArchived { fn to_bytes(&self) -> Cow<[u8]> { let mut buf = vec![]; - into_writer(self, &mut buf).expect("failed to encode SettingArchivedPayload data"); + into_writer(self, &mut buf).expect("failed to encode SettingArchived data"); Cow::Owned(buf) } fn from_bytes(bytes: Cow<'_, [u8]>) -> Self { - from_reader(&bytes[..]).expect("failed to decode SettingArchivedPayload data") + from_reader(&bytes[..]).expect("failed to decode SettingArchived data") } } @@ -831,7 +831,7 @@ pub mod ns { .check_and_get_setting(&caller, &spk) .ok_or_else(|| format!("setting {} not found or no permission", &spk))?; - if spk.4 != 0 || spk.4 != setting.version { + if spk.4 != 0 && spk.4 != setting.version { Err("version mismatch".to_string())?; }; @@ -886,6 +886,12 @@ pub mod ns { Err("version mismatch".to_string())?; } + if let Some(ref payload) = input.payload { + if payload.len() as u64 > ns.max_payload_size { + Err("payload size exceeds the limit".to_string())?; + } + } + let size = match input.dek { Some(ref dek) => { // should be valid COSE encrypt0 dek @@ -904,7 +910,7 @@ pub mod ns { None => { // try to validate plain payload if let Some(ref payload) = input.payload { - try_decode_payload(ns.max_payload_size, payload)?; + try_decode_payload(payload)?; payload.len() } else { 0 @@ -950,9 +956,20 @@ pub mod ns { if !ns.can_write_setting(&caller, &spk) { Err("no permission".to_string())?; } - let size = input.payload.len(); + + let mut size = if let Some(ref payload) = input.payload { + payload.len() + } else { + 0 + }; + if size as u64 > ns.max_payload_size { + Err("payload size exceeds the limit".to_string())?; + } + if let Some(ref dek) = input.dek { + size += dek.len(); + } + let output = { - let max_payload_size = ns.max_payload_size; let setting = ns .get_setting_mut(&spk) .ok_or_else(|| format!("setting {} not found", &spk))?; @@ -963,18 +980,14 @@ pub mod ns { Err("readonly setting can not be updated".to_string())?; } - match setting.dek { - Some(_) => { - if input.payload.len() as u64 > max_payload_size { - Err("payload size exceeds the limit".to_string())?; - } + if setting.dek.is_some() || input.dek.is_some() { + if let Some(ref payload) = input.payload { // should be valid COSE encrypt0 payload - try_decode_encrypt0(&input.payload)?; - } - None => { - // try to validate plain payload - try_decode_payload(max_payload_size, &input.payload)?; + try_decode_encrypt0(payload)?; } + } else if let Some(ref payload) = input.payload { + // try to validate plain payload + try_decode_payload(payload)?; } if let Some(payload) = setting.payload.as_ref() { @@ -984,19 +997,24 @@ pub mod ns { SettingArchived { archived_at: now_ms, deprecated: input.deprecate_current.unwrap_or(false), - payload: payload.clone(), + payload: Some(payload.clone()), dek: setting.dek.clone(), }, ); }); } + setting.version = setting.version.saturating_add(1); + setting.updated_at = now_ms; if let Some(status) = input.status { setting.status = status; } - setting.version = setting.version.saturating_add(1); - setting.payload = Some(input.payload); - setting.updated_at = now_ms; + if let Some(payload) = input.payload { + setting.payload = Some(payload); + } + if let Some(dek) = input.dek { + setting.dek = Some(dek); + } UpdateSettingOutput { created_at: setting.created_at, updated_at: setting.updated_at, diff --git a/src/ic_cose_types/src/cose/kdf.rs b/src/ic_cose_types/src/cose/kdf.rs index 5edf5a3..a6eb3c4 100644 --- a/src/ic_cose_types/src/cose/kdf.rs +++ b/src/ic_cose_types/src/cose/kdf.rs @@ -12,7 +12,7 @@ pub fn hkdf256(secret: &[u8], salt: Option<&[u8]>, info: &[u8]) // HKDF-SHA-256 with Context Information Structure // https://datatracker.ietf.org/doc/html/rfc9053#name-context-information-structu -pub fn hkdf256_context(secret: &[u8], salt: Option<&[u8]>, ecdh: bool) -> [u8; 32] { +pub fn derive_aesgcm256_secret(secret: &[u8], salt: Option<&[u8]>) -> [u8; 32] { let ctx = CoseKdfContextBuilder::new() .algorithm(iana::Algorithm::A256GCM) .supp_pub_info( @@ -20,11 +20,7 @@ pub fn hkdf256_context(secret: &[u8], salt: Option<&[u8]>, ecdh: bool) -> [u8; 3 .key_data_length(256) .protected( HeaderBuilder::new() - .algorithm(if ecdh { - iana::Algorithm::ECDH_ES_HKDF_256 - } else { - iana::Algorithm::Direct_HKDF_SHA_256 - }) + .algorithm(iana::Algorithm::Direct_HKDF_SHA_256) .build(), ) .build(), diff --git a/src/ic_cose_types/src/types/setting.rs b/src/ic_cose_types/src/types/setting.rs index 39135a2..20a3d25 100644 --- a/src/ic_cose_types/src/types/setting.rs +++ b/src/ic_cose_types/src/types/setting.rs @@ -7,6 +7,7 @@ use std::collections::{BTreeMap, BTreeSet}; use crate::validate_key; pub const CHUNK_SIZE: u32 = 256 * 1024; +pub const MAX_DEK_SIZE: u64 = 3 * 1024; #[derive(CandidType, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] pub struct SettingInfo { @@ -42,15 +43,7 @@ impl SettingPath { } } -pub fn try_decode_payload(max_size: u64, payload: &[u8]) -> Result { - if max_size > 0 && payload.len() as u64 > max_size { - return Err(format!( - "payload size {} exceeds the limit {}", - payload.len(), - max_size - )); - } - +pub fn try_decode_payload(payload: &[u8]) -> Result { from_reader(payload).map_err(|err| format!("decode CBOR payload failed: {:?}", err)) } @@ -75,6 +68,11 @@ impl CreateSettingInput { validate_key(k)?; } } + if let Some(ref dek) = self.dek { + if dek.len() > MAX_DEK_SIZE as usize { + Err("DEK size exceeds the limit".to_string())?; + } + } Ok(()) } } @@ -111,9 +109,10 @@ impl UpdateSettingInfoInput { #[derive(CandidType, Clone, Debug, Deserialize, Serialize)] pub struct UpdateSettingPayloadInput { - pub payload: ByteBuf, // plain or encrypted payload + pub payload: Option, // plain or encrypted payload pub status: Option, pub deprecate_current: Option, // deprecate the current version + pub dek: Option, } impl UpdateSettingPayloadInput { @@ -123,6 +122,14 @@ impl UpdateSettingPayloadInput { Err("status should be -1, 0 or 1".to_string())?; } } + if self.payload.is_none() && self.dek.is_none() { + Err("payload or dek should be provided".to_string())?; + } + if let Some(ref dek) = self.dek { + if dek.len() > MAX_DEK_SIZE as usize { + Err("DEK size exceeds the limit".to_string())?; + } + } Ok(()) } } @@ -134,6 +141,6 @@ pub struct SettingArchivedPayload { pub version: u32, pub archived_at: u64, pub deprecated: bool, // true if the payload should not be used for some reason - pub payload: ByteBuf, + pub payload: Option, pub dek: Option, // exist if the payload is encrypted }