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
4 changes: 2 additions & 2 deletions .github/workflows/backend-rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,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
cargo build --release --target wasm32-unknown-unknown -p ic-vetkeys-manager-canister -p ic-vetkeys-encrypted-maps-canister -p ic-vetkeys-canisters-tests
cargo test
cargo test --doc
cargo-test-backend-darwin:
Expand All @@ -39,6 +39,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
cargo build --release --target wasm32-unknown-unknown -p ic-vetkeys-manager-canister -p ic-vetkeys-encrypted-maps-canister -p ic-vetkeys-canisters-tests
cargo test
cargo test --doc
15 changes: 15 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 @@ -4,6 +4,7 @@ members = [
"backend/rs/ic_vetkeys_test_utils",
"backend/rs/canisters/ic_vetkeys_encrypted_maps_canister",
"backend/rs/canisters/ic_vetkeys_manager_canister",
"backend/rs/canisters/tests",
"examples/basic_ibe/backend",
"examples/basic_timelock_ibe/backend",
"examples/password_manager_with_metadata/backend"
Expand Down
25 changes: 25 additions & 0 deletions backend/rs/canisters/tests/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
[package]
name = "ic-vetkeys-canisters-tests"
authors.workspace = true
description.workspace = true
documentation.workspace = true
edition.workspace = true
version.workspace = true
license.workspace = true

[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-vetkeys = { path = "../../ic_vetkeys" }
serde = { workspace = true }

[dev-dependencies]
ic-vetkeys-test-utils = { path = "../../ic_vetkeys_test_utils" }
pocket-ic = { workspace = true }
rand = { workspace = true }
9 changes: 9 additions & 0 deletions backend/rs/canisters/tests/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.PHONY: build
.SILENT: build
build:
cargo build --release --target wasm32-unknown-unknown

.PHONY: test
.SILENT: test
test: build
cargo test
5 changes: 5 additions & 0 deletions backend/rs/canisters/tests/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Canister tests

Currently, we test:
* `ic_vetkeys::management_canister::sign_with_bls`
* `ic_vetkeys::management_canister::bls_public_key`
64 changes: 64 additions & 0 deletions backend/rs/canisters/tests/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
use ic_cdk::update;
use ic_vetkeys::vetkd_api_types::{
VetKDDeriveKeyReply, VetKDDeriveKeyRequest, VetKDKeyId, VetKDPublicKeyReply,
VetKDPublicKeyRequest,
};

#[update]
async fn sign_with_bls(input: Vec<u8>, context: Vec<u8>, key_id: VetKDKeyId) -> Vec<u8> {
ic_vetkeys::management_canister::sign_with_bls(input, context, key_id)
.await
.expect("sign_with_bls call failed")
}

#[update]
async fn bls_public_key(context: Vec<u8>, key_id: VetKDKeyId) -> Vec<u8> {
ic_vetkeys::management_canister::bls_public_key(None, context, key_id)
.await
.expect("bls_public_key call failed")
}

#[update]
async fn vetkd_derive_key(
input: Vec<u8>,
context: Vec<u8>,
key_id: VetKDKeyId,
transport_public_key: Vec<u8>,
) -> Vec<u8> {
let request = VetKDDeriveKeyRequest {
input,
context,
key_id,
transport_public_key,
};

let reply: (VetKDDeriveKeyReply,) = ic_cdk::api::call::call_with_payment128(
candid::Principal::management_canister(),
"vetkd_derive_key",
(request,),
26_153_846_153,
)
.await
.expect("vetkd_derive_key call failed");

reply.0.encrypted_key
}

#[update]
async fn vetkd_public_key(context: Vec<u8>, key_id: VetKDKeyId) -> Vec<u8> {
let request = VetKDPublicKeyRequest {
canister_id: None,
context,
key_id,
};

let reply: (VetKDPublicKeyReply,) = ic_cdk::api::call::call(
candid::Principal::management_canister(),
"vetkd_public_key",
(request,),
)
.await
.expect("vetkd_public_key call failed");

reply.0.public_key
}
145 changes: 145 additions & 0 deletions backend/rs/canisters/tests/tests/sign_with_bls.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
use candid::{decode_one, encode_args, CandidType, Principal};
use ic_vetkeys::vetkd_api_types::{VetKDCurve, VetKDKeyId};
use ic_vetkeys::{verify_bls_signature, DerivedPublicKey, EncryptedVetKey, TransportSecretKey};
use ic_vetkeys_test_utils::{git_root_dir, reproducible_rng};
use pocket_ic::{PocketIc, PocketIcBuilder};
use rand::{CryptoRng, Rng};
use std::path::Path;

#[test]
fn bls_signature_should_be_valid_and_equal_to_decrypted_vetkey() {
let rng = &mut reproducible_rng();
let env = TestEnvironment::new();
let input = random_bytes(rng, 10);
let context = random_bytes(rng, 10);
let key_id = VetKDKeyId {
curve: VetKDCurve::Bls12_381_G2,
name: "dfx_test_key".to_string(),
};
let transport_secret_key = random_transport_key(rng);
let transport_public_key = transport_secret_key.public_key();

let bls_signature: Vec<u8> = env.update(
Principal::anonymous(),
"sign_with_bls",
encode_args((input.clone(), context.clone(), key_id.clone())).unwrap(),
);

let verification_key: Vec<u8> = env.update(
Principal::anonymous(),
"vetkd_public_key",
encode_args((context.clone(), key_id.clone())).unwrap(),
);
let encrypted_vetkey_bytes: Vec<u8> = env.update(
Principal::anonymous(),
"vetkd_derive_key",
encode_args((input.clone(), context, key_id, transport_public_key)).unwrap(),
);
let encrypted_vetkey = EncryptedVetKey::deserialize(encrypted_vetkey_bytes.as_ref()).unwrap();
let derived_public_key = DerivedPublicKey::deserialize(verification_key.as_ref()).unwrap();
let decrypted_vetkey = encrypted_vetkey
.decrypt_and_verify(&transport_secret_key, &derived_public_key, &input)
.unwrap();

assert_eq!(bls_signature, decrypted_vetkey.signature_bytes().to_vec());
assert!(verify_bls_signature(
&derived_public_key,
&input,
&bls_signature
));
}

#[test]
fn bls_public_key_should_be_equal_to_verification_key() {
let rng = &mut reproducible_rng();
let env = TestEnvironment::new();
let context = random_bytes(rng, 10);
let key_id = VetKDKeyId {
curve: VetKDCurve::Bls12_381_G2,
name: "dfx_test_key".to_string(),
};
let bls_public_key: Vec<u8> = env.update(
Principal::anonymous(),
"bls_public_key",
encode_args((context.clone(), key_id.clone())).unwrap(),
);
let verification_key: Vec<u8> = env.update(
Principal::anonymous(),
"vetkd_public_key",
encode_args((context.clone(), key_id.clone())).unwrap(),
);
assert_eq!(bls_public_key, verification_key);
}
struct TestEnvironment {
pic: PocketIc,
canister_id: Principal,
}

impl TestEnvironment {
fn new() -> Self {
let pic = PocketIcBuilder::new()
.with_application_subnet()
.with_ii_subnet()
.with_fiduciary_subnet()
.with_nonmainnet_features(true)
.build();

let canister_id = pic.create_canister();
pic.add_cycles(canister_id, 2_000_000_000_000);

let wasm_bytes = load_canister_wasm();
pic.install_canister(canister_id, wasm_bytes, vec![], None);

// Make sure the canister is properly initialized
fast_forward(&pic, 5);

Self { pic, canister_id }
}

fn update<T: CandidType + for<'de> candid::Deserialize<'de>>(
&self,
caller: Principal,
method_name: &str,
args: Vec<u8>,
) -> T {
let reply = self
.pic
.update_call(self.canister_id, caller, method_name, args);
match reply {
Ok(data) => decode_one(&data).expect("failed to decode reply"),
Err(user_error) => panic!("canister returned a user error: {user_error}"),
}
}
}

fn load_canister_wasm() -> Vec<u8> {
let wasm_path_string = match std::env::var("CUSTOM_WASM_PATH") {
Ok(path) if !path.is_empty() => path,
_ => format!(
"{}/target/wasm32-unknown-unknown/release/ic_vetkeys_canisters_tests.wasm",
git_root_dir()
),
};
let wasm_path = Path::new(&wasm_path_string);
std::fs::read(wasm_path)
.expect("wasm does not exist - run `cargo build --release --target wasm32-unknown-unknown`")
}

fn random_transport_key<R: Rng + CryptoRng>(rng: &mut R) -> TransportSecretKey {
let mut seed = vec![0u8; 32];
rng.fill_bytes(&mut seed);
TransportSecretKey::from_seed(seed).unwrap()
}

fn random_bytes<R: Rng + CryptoRng>(rng: &mut R, max_length: usize) -> Vec<u8> {
let length = rng.gen_range(0..max_length);
let mut bytes = vec![0u8; length];
rng.fill_bytes(&mut bytes);
bytes
}

fn fast_forward(ic: &PocketIc, ticks: u64) {
for _ in 0..ticks - 1 {
ic.tick();
}
}
2 changes: 2 additions & 0 deletions backend/rs/ic_vetkeys/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ use ic_stable_structures::{
};
use serde::{Deserialize, Serialize};

pub type CanisterId = candid::Principal;

pub type KeyName = Blob<32>;
pub type MapName = KeyName;
pub type MapId = KeyId;
Expand Down
Loading