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
10 changes: 7 additions & 3 deletions .github/workflows/backend-motoko-format-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,11 @@ jobs:
- name: Run MOPS Format Check Linux
run: |
for d in $(find . -type d -name '.mops' -prune -o -type f -name 'mops.toml' -printf '%h\n'); do
cd $d
echo "Checking directory: $(pwd)" && mops format --check
cd -
if [ -d "$d/src" ]; then
cd $d
echo "Checking directory: $(pwd)" && mops format --check
cd -
else
echo "Skipping directory $d (no src directory inside)"
fi
done
41 changes: 35 additions & 6 deletions .github/workflows/examples-password-manager.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
examples-password-manager-darwin:
examples-password-manager-rust-darwin:
runs-on: macos-15
steps:
- uses: actions/checkout@v4
Expand All @@ -31,11 +31,11 @@ jobs:
run: |
set -eExuo pipefail
npm i
pushd examples/password_manager
./deploy_locally.sh
cd examples/password_manager/rust
dfx start --background && dfx deploy
cd frontend
npm run lint
examples-password-manager-linux:
examples-password-manager-rust-linux:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
Expand All @@ -45,7 +45,36 @@ jobs:
run: |
set -eExuo pipefail
npm i
pushd examples/password_manager
./deploy_locally.sh
cd examples/password_manager/rust
dfx start --background && dfx deploy
cd frontend
npm run lint
examples-password-manager-motoko-darwin:
runs-on: macos-15
steps:
- uses: actions/checkout@v4
- name: Provision Darwin
run: |
bash .github/workflows/provision-darwin.sh
- name: Deploy Password Manager Darwin
run: |
set -eExuo pipefail
npm i
cd examples/password_manager/motoko
dfx start --background && dfx deploy
cd frontend
npm run lint
examples-password-manager-motoko-linux:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: Provision Linux
run: bash .github/workflows/provision-linux.sh
- name: Deploy Password Manager Linux
run: |
set -eExuo pipefail
npm i
cd examples/password_manager/motoko
dfx start --background && dfx deploy
cd frontend
npm run lint
42 changes: 36 additions & 6 deletions Cargo.lock

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

Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ license = "Apache-2.0"

[dependencies]
base = "0.14.6"
ic-vetkeys = "0.2.0"
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import IcVetkeys "../../../ic_vetkeys/src";
import Types "../../../ic_vetkeys/src/Types";
import IcVetkeys "mo:ic-vetkeys";
import Types "mo:ic-vetkeys/Types";
import Principal "mo:base/Principal";
import Text "mo:base/Text";
import Blob "mo:base/Blob";
Expand All @@ -12,6 +12,13 @@ actor class (keyName : Text) {
/// Unfortunately, the `Blob` type cannot be serialized/deserialized in the current Rust implementation efficiently without nesting it in another type.
public type ByteBuf = { inner : Blob };

public type EncryptedMapData = {
map_owner : Principal;
map_name : ByteBuf;
keyvals : [(ByteBuf, ByteBuf)];
access_control : [(Principal, Types.AccessRights)];
};

/// The result type compatible with Rust's `Result`.
public type Result<Ok, Err> = {
#Ok : Ok;
Expand Down Expand Up @@ -72,8 +79,23 @@ actor class (keyName : Text) {
);
};

public query (msg) func get_all_accessible_encrypted_maps() : async [IcVetkeys.EncryptedMaps.EncryptedMapData<Types.AccessRights>] {
encryptedMaps.getAllAccessibleEncryptedMaps(msg.caller);
public query (msg) func get_all_accessible_encrypted_maps() : async [EncryptedMapData] {
Array.map<IcVetkeys.EncryptedMaps.EncryptedMapData<Types.AccessRights>, EncryptedMapData>(
encryptedMaps.getAllAccessibleEncryptedMaps(msg.caller),
func(map : IcVetkeys.EncryptedMaps.EncryptedMapData<Types.AccessRights>) : EncryptedMapData {
{
map_owner = map.map_owner;
map_name = { inner = map.map_name };
keyvals = Array.map<(Blob, Blob), (ByteBuf, ByteBuf)>(
map.keyvals,
func((blob1, blob2) : (Blob, Blob)) {
({ inner = blob1 }, { inner = blob2 });
},
);
access_control = map.access_control;
};
},
);
};

public query (msg) func get_encrypted_value(
Expand Down
2 changes: 2 additions & 0 deletions backend/mo/ic_vetkeys/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## [Unreleased]

- BREAKING CHANGE: Fixed a few inconsistencies with the Rust backend of encrypted maps.

### Adds
- Sign with BLS and VetKD helper functions.

Expand Down
33 changes: 12 additions & 21 deletions backend/mo/ic_vetkeys/src/encrypted_maps/EncryptedMaps.mo
Original file line number Diff line number Diff line change
Expand Up @@ -124,35 +124,26 @@ module {
/// Returns the removed keys.
/// The caller must have write permissions to perform this operation.
public func removeMapValues(caller : Caller, mapId : MapId) : Result.Result<[MapKey], Text> {
switch (keyManager.getUserRights(caller, mapId, caller)) {
switch (keyManager.ensureUserCanWrite(caller, mapId)) {
case (#err(msg)) { #err(msg) };
case (#ok(optRights)) {
switch (optRights) {
case (null) { #err("unauthorized") };
case (?rights) {
if (accessRightsOperations.canWrite(rights)) {
let keys = switch (mapKeysMapOps().get(mapKeys, mapId)) {
case (null) { [] };
case (?ks) { ks };
};
for (key in keys.vals()) {
mapKeyVals := mapKeyValsMapOps().delete(mapKeyVals, (mapId, key));
};
mapKeys := mapKeysMapOps().delete(mapKeys, mapId);
#ok(keys);
} else {
#err("unauthorized");
};
};
case (#ok(_)) {
let keys = switch (mapKeysMapOps().get(mapKeys, mapId)) {
case (null) { [] };
case (?ks) { ks };
};
for (key in keys.vals()) {
mapKeyVals := mapKeyValsMapOps().delete(mapKeyVals, (mapId, key));
};
mapKeys := mapKeysMapOps().delete(mapKeys, mapId);
#ok(keys);
};
};
};

/// Retrieves all encrypted key-value pairs from a map.
/// The caller must have read permissions to access the map values.
public func getEncryptedValuesForMap(caller : Caller, mapId : MapId) : Result.Result<[(MapKey, EncryptedMapValue)], Text> {
switch (keyManager.getUserRights(caller, mapId, caller)) {
switch (keyManager.ensureUserCanRead(caller, mapId)) {
case (#err(msg)) { #err(msg) };
case (#ok(_)) {
let values = Buffer.Buffer<(MapKey, EncryptedMapValue)>(0);
Expand Down Expand Up @@ -276,7 +267,7 @@ module {
mapId : MapId,
key : MapKey,
) : Result.Result<?EncryptedMapValue, Text> {
switch (keyManager.getUserRights(caller, mapId, caller)) {
switch (keyManager.ensureUserCanWrite(caller, mapId)) {
case (#err(msg)) { #err(msg) };
case (#ok(_)) {
let oldValue = mapKeyValsMapOps().get(mapKeyVals, (mapId, key));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
[package]
name = "ic-vetkeys-encrypted-maps-canister"
authors.workspace = true
description.workspace = true
documentation.workspace = true
edition.workspace = true
version.workspace = true
license.workspace = true
authors = ["DFINITY Stiftung"]
version = "0.1.0"
edition = "2021"
license = "Apache-2.0"
description = "# Basic Identity Based Encryption"
repository = "https://github.com/dfinity/vetkeys"
rust-version = "1.75.0"

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

[dependencies]
candid = { workspace = true }
ic-cdk = { workspace = true }
ic-cdk-macros = { workspace = true }
ic-dummy-getrandom-for-wasm = { workspace = true }
ic-stable-structures = { workspace = true }
ic-vetkeys = { path = "../../ic_vetkeys" }
serde = { workspace = true }
candid = "0.10.2"
ic-cdk = "0.18.3"
ic-cdk-macros = "0.18.3"
ic-dummy-getrandom-for-wasm = "0.1.0"
ic-stable-structures = "0.6.8"
ic-vetkeys = "0.2.0"
serde = "1.0.217"

[dev-dependencies]
ic-vetkeys-test-utils = { path = "../../ic_vetkeys_test_utils" }
pocket-ic = { workspace = true }
rand = { workspace = true }
rand_chacha = { workspace = true }
pocket-ic = "9.0.0"
rand = "0.8.5"
rand_chacha = "0.3.1"
reqwest = "0.12.12"
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ use ic_vetkeys::encrypted_maps::{VetKey, VetKeyVerificationKey};
use ic_vetkeys::key_manager::key_id_to_vetkd_input;
use ic_vetkeys::types::{AccessRights, ByteBuf, TransportKey};
use ic_vetkeys::{DerivedPublicKey, EncryptedVetKey, TransportSecretKey};
use ic_vetkeys_test_utils::{git_root_dir, random_self_authenticating_principal, reproducible_rng};
use pocket_ic::{PocketIc, PocketIcBuilder};
use rand::{CryptoRng, Rng};
use rand::{CryptoRng, Rng, SeedableRng};
use rand_chacha::ChaCha20Rng;
use std::path::Path;

#[test]
Expand Down Expand Up @@ -154,6 +154,12 @@ fn map_sharing_should_work() {
assert_eq!(get_vetkey(env.principal_0), get_vetkey(env.principal_1));
}

pub fn reproducible_rng() -> ChaCha20Rng {
let seed = rand::thread_rng().gen();
println!("RNG seed: {seed:?}");
ChaCha20Rng::from_seed(seed)
}

struct TestEnvironment {
pic: PocketIc,
example_canister_id: Principal,
Expand Down Expand Up @@ -254,3 +260,20 @@ fn random_map_name<R: Rng + CryptoRng>(rng: &mut R) -> ByteBuf {
rng.fill_bytes(&mut map_name);
ByteBuf::from(map_name)
}

pub fn random_self_authenticating_principal<R: Rng + CryptoRng>(rng: &mut R) -> Principal {
let mut fake_public_key = vec![0u8; 32];
rng.fill_bytes(&mut fake_public_key);
Principal::self_authenticating::<&[u8]>(fake_public_key.as_ref())
}

fn git_root_dir() -> String {
let output = std::process::Command::new("git")
.args(["rev-parse", "--show-toplevel"])
.output()
.expect("Failed to execute git command");
assert!(output.status.success());
let root_dir_with_newline =
String::from_utf8(output.stdout).expect("Failed to convert stdout to string");
root_dir_with_newline.trim_end_matches('\n').to_string()
}
6 changes: 0 additions & 6 deletions examples/basic_ibe/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,6 @@ A canister functionality for decrypting secrets can be detected by inspecting th
- [Internet Computer software development kit](https://internetcomputer.org/docs/building-apps/getting-started/install)
- [npm](https://www.npmjs.com/package/npm)

### Install Dependencies

```bash
npm install
```

### Deploy the Canisters

If you want to deploy this project locally with a Motoko backend, then run:
Expand Down
6 changes: 0 additions & 6 deletions examples/basic_timelock_ibe/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,6 @@ A canister functionality for decrypting secrets can be detected by inspecting th
- [Internet Computer software development kit](https://internetcomputer.org/docs/building-apps/getting-started/install)
- [npm](https://www.npmjs.com/package/npm)

### Install Dependencies

```bash
npm install
```

### Deploy the Canisters Locally

```bash
Expand Down
Loading