Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
4 changes: 2 additions & 2 deletions .github/workflows/backend-rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jobs:
run: |
set -eExuo pipefail
export CARGO_TERM_COLOR=always # ensure output has colors
cargo build --release --target wasm32-unknown-unknown -p ic-vetkeys-manager-canister -p ic-vetkeys-encrypted-maps-canister -p ic-vetkeys-canisters-tests
cargo build --release --target wasm32-unknown-unknown -p ic-vetkeys-manager-canister -p ic-vetkeys-encrypted-maps-canister -p ic-vetkeys-encrypted-maps-custom-canister -p ic-vetkeys-canisters-tests
cargo test
cargo test --doc
cargo-test-backend-darwin:
Expand All @@ -46,6 +46,6 @@ jobs:
run: |
set -eExuo pipefail
export CARGO_TERM_COLOR=always # ensure output has colors
cargo build --release --target wasm32-unknown-unknown -p ic-vetkeys-manager-canister -p ic-vetkeys-encrypted-maps-canister -p ic-vetkeys-canisters-tests
cargo build --release --target wasm32-unknown-unknown -p ic-vetkeys-manager-canister -p ic-vetkeys-encrypted-maps-canister -p ic-vetkeys-encrypted-maps-custom-canister -p ic-vetkeys-canisters-tests
cargo test
cargo test --doc
13 changes: 13 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ members = [
"backend/rs/ic_vetkeys",
"backend/rs/ic_vetkeys_test_utils",
"backend/rs/canisters/ic_vetkeys_encrypted_maps_canister",
"backend/rs/canisters/ic_vetkeys_encrypted_maps_custom_canister",
"backend/rs/canisters/ic_vetkeys_manager_canister",
"backend/rs/canisters/tests",
"backend/rs/benchmarks",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@ The canister implemented in this folder directly exposes the methods of the encr
This is useful for:

1. running canister tests
2. implementing dapps that only require encrypted maps
2. implementing dapps that only require encrypted maps

It uses the full form of the [`export_encrypted_maps_canister!`](https://docs.rs/ic-vetkeys/latest/ic_vetkeys/macro.export_encrypted_maps_canister.html) macro. If your dapp keeps state linked to each value and needs to provide its own value endpoints, see [`ic-vetkeys-encrypted-maps-custom-canister`](../ic_vetkeys_encrypted_maps_custom_canister) and the macro's `custom_value_endpoints` form.
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[package]
name = "ic-vetkeys-encrypted-maps-custom-canister"
authors = ["DFINITY Stiftung"]
version = "0.1.0"
edition = "2021"
license = "Apache-2.0"
description = "Reference canister for the export_encrypted_maps_canister! custom_value_endpoints form"
repository = "https://github.com/dfinity/vetkeys"
rust-version = "1.85.0"

[lib]
path = "src/lib.rs"
crate-type = ["cdylib"]

[dependencies]
candid = "0.10.2"
ic-cdk = { workspace = true }
ic-cdk-management-canister = { workspace = true }
ic-dummy-getrandom-for-wasm = "0.1.0"
ic-stable-structures = { workspace = true }
ic-vetkeys = { workspace = true }
serde = "1.0.217"
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# ic-vetkeys-encrypted-maps-custom-canister

A reference canister for the `custom_value_endpoints` form of the
[`export_encrypted_maps_canister!`](https://docs.rs/ic-vetkeys/latest/ic_vetkeys/macro.export_encrypted_maps_canister.html)
macro.

Unlike [`ic-vetkeys-encrypted-maps-canister`](../ic_vetkeys_encrypted_maps_canister),
which exposes the full EncryptedMaps interface, this canister generates only the
control-plane endpoints (vetKD keys, access control, map-name enumeration) plus
the state, lifecycle hooks, and the `with_encrypted_maps`/`with_encrypted_maps_mut`
accessors, and provides its own value read/write endpoints.

This is the pattern for dapps that keep state linked to each encrypted value
(e.g. a metadata row per entry) and therefore must own the value endpoints to
keep the two stores consistent — see `password_manager_with_metadata` in
[dfinity/examples](https://github.com/dfinity/examples). The endpoints here are
kept minimal (a plain insert/get against the accessor) to exercise the macro
form; they are not a full metadata implementation.
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Reference canister for the `custom_value_endpoints` form of
// `export_encrypted_maps_canister!`. It generates the control-plane endpoints
// plus the state, lifecycle hooks, and the `with_encrypted_maps`/`_mut`
// accessors, but NOT the value read/write endpoints — the canister exposes its
// own. This is the pattern an app uses when it keeps state linked to each value
// (e.g. a metadata row per entry) and must own the value endpoints to keep that
// state consistent.
//
// This minimal example just re-implements a plain insert/get against the
// accessor to exercise the surface; a full linked-metadata example lives in
// `password_manager_with_metadata` in dfinity/examples.
use candid::Principal;
use ic_stable_structures::memory_manager::{MemoryId, MemoryManager, VirtualMemory};
use ic_stable_structures::storable::Blob;
use ic_stable_structures::DefaultMemoryImpl;
use ic_vetkeys::types::{ByteBuf, EncryptedMapValue};
use std::cell::RefCell;

type Memory = VirtualMemory<DefaultMemoryImpl>;

thread_local! {
static MEMORY_MANAGER: RefCell<MemoryManager<DefaultMemoryImpl>> =
RefCell::new(MemoryManager::init(DefaultMemoryImpl::default()));
}

fn memory(id: u8) -> Memory {
MEMORY_MANAGER.with(|m| m.borrow().get(MemoryId::new(id)))
}

// Control plane + state + lifecycle + accessors; no value endpoints.
ic_vetkeys::export_encrypted_maps_canister!(
"encrypted_maps_custom_dapp",
[memory(0), memory(1), memory(2), memory(3)],
custom_value_endpoints,
);

fn to_blob(bytes: ByteBuf) -> Result<Blob<32>, String> {
Blob::try_from(bytes.as_ref()).map_err(|_| "too large input".to_string())
}

// A canister-owned value write, wrapping the library's own via the accessor.
// This is where linked side-state (metadata, counters, …) would be maintained
// in the same call — kept minimal here.
#[ic_cdk::update]
fn insert_encrypted_value_custom(
map_owner: Principal,
map_name: ByteBuf,
map_key: ByteBuf,
value: EncryptedMapValue,
) -> Result<Option<EncryptedMapValue>, String> {
with_encrypted_maps_mut(|encrypted_maps| {
encrypted_maps.insert_encrypted_value(
ic_cdk::api::msg_caller(),
(map_owner, to_blob(map_name)?),
Comment thread
marc0olo marked this conversation as resolved.
to_blob(map_key)?,
value,
)
})
}

// A canister-owned value read via the shared-reference accessor.
#[ic_cdk::query]
fn get_encrypted_value_custom(
map_owner: Principal,
map_name: ByteBuf,
map_key: ByteBuf,
) -> Result<Option<EncryptedMapValue>, String> {
with_encrypted_maps(|encrypted_maps| {
encrypted_maps.get_encrypted_value(
ic_cdk::api::msg_caller(),
(map_owner, to_blob(map_name)?),
to_blob(map_key)?,
)
})
}

ic_cdk::export_candid!();
13 changes: 13 additions & 0 deletions backend/rs/ic_vetkeys/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
# Change Log

## [Unreleased]

### Added

- `export_encrypted_maps_canister!` gained a `custom_value_endpoints` form. It
generates the control-plane endpoints, the stable state, the
`#[init]`/`#[post_upgrade]` hooks, and `with_encrypted_maps`/`_mut` accessors,
but omits every endpoint that reads or writes encrypted map values — for
canisters that keep state linked to each value (e.g. a metadata row per entry)
and must own the value endpoints to keep that state consistent. Reuse the
library's vetKD/crypto/access-control logic from your own endpoints via the
accessors. The full form is unchanged and its generated Candid is identical.

## [0.8.1] - 2026-07-28

### Fixed
Expand Down
14 changes: 14 additions & 0 deletions backend/rs/ic_vetkeys/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,20 @@ A canister library for derivation of encrypted vetkeys from arbitrary strings. I
## [Encrypted Maps](https://docs.rs/ic-vetkeys/latest/ic_vetkeys/encrypted_maps/struct.EncryptedMaps.html)
An efficient canister library facilitating access control and encrypted storage for a collection of maps contatining key-value pairs. It can be used in combination with the [frontend encrypted maps library](https://dfinity.github.io/vetkeys/classes/_icp-sdk_vetkeys_encrypted_maps.EncryptedMaps.html).

## Ready-made EncryptedMaps canister

The [`export_encrypted_maps_canister!`](https://docs.rs/ic-vetkeys/latest/ic_vetkeys/macro.export_encrypted_maps_canister.html) macro generates a complete EncryptedMaps canister — the `#[init]`/`#[post_upgrade]`, the stable state, and every endpoint the `@icp-sdk/vetkeys` frontend expects — in a few lines instead of ~200 of hand-written boilerplate:

```ignore
ic_vetkeys::export_encrypted_maps_canister!(
"my_app_domain_separator",
[memory(0), memory(1), memory(2), memory(3)],
);
ic_cdk::export_candid!();
```

If your canister keeps state linked to each value (e.g. a metadata row per entry) and must own the value read/write endpoints, pass `custom_value_endpoints`: the macro then generates the control-plane endpoints, state, lifecycle hooks, and `with_encrypted_maps`/`with_encrypted_maps_mut` accessors, but omits the value endpoints so you can provide your own. See the [macro documentation](https://docs.rs/ic-vetkeys/latest/ic_vetkeys/macro.export_encrypted_maps_canister.html) for the full setup and rationale.

## [Utils](https://docs.rs/ic-vetkeys/latest/)
For obtaining and decrypting verifiably-encrypted threshold keys via the Internet Computer vetKD system API. The API is located in the crate root.

Expand Down
Loading
Loading