Skip to content

Commit 292f119

Browse files
committed
feat: support vetkeys
1 parent b487927 commit 292f119

File tree

17 files changed

+241
-99
lines changed

17 files changed

+241
-99
lines changed

Cargo.lock

Lines changed: 130 additions & 72 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ strip = true
1414
opt-level = 's'
1515

1616
[workspace.package]
17-
version = "0.8.5"
17+
version = "0.8.6"
1818
edition = "2021"
1919
repository = "https://github.com/ldclabs/ic-cose"
2020
keywords = ["config", "cbor", "canister", "icp", "encryption"]
@@ -30,11 +30,12 @@ serde = "1"
3030
serde_bytes = "0.11"
3131
serde_with = "3.12"
3232
k256 = { version = "0.13", features = ["ecdsa", "schnorr"] }
33+
ed25519-consensus = "2.1"
3334
ed25519-dalek = "2"
3435
x25519-dalek = { version = "2", features = ["static_secrets"] }
3536
hmac = "0.12"
3637
hkdf = "0.12"
37-
const-hex = "1"
38+
hex = "0.4"
3839
sha2 = "0.10"
3940
sha3 = "0.10"
4041
num-traits = "0.2"
@@ -45,16 +46,17 @@ icrc-ledger-types = "0.1"
4546
ic-certification = "3.0"
4647
ic-canister-sig-creation = "1.3"
4748
ic-agent = "0.40"
48-
ic_auth_types = "0.4"
49-
ic_auth_verifier = { version = "0.4" }
50-
# ic-vetkeys = "0.1"
49+
ic_auth_types = "0.5"
50+
ic_auth_verifier = { version = "0.5" }
51+
ic-vetkeys = "0.2"
5152
rand = "0.9"
5253
coset = "0.3"
5354
aes-gcm = "0.10"
5455
thiserror = "2"
5556
ic-secp256k1 = { version = "0.1" }
5657
ic-ed25519 = { version = "0.2" }
5758
ic-dummy-getrandom-for-wasm = "0.1"
59+
tokio = { version = "1" }
5860

5961
[workspace.metadata.cargo-shear]
6062
ignored = ["ic-dummy-getrandom-for-wasm"]

src/ic_cose/Cargo.toml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,9 @@ rand = { workspace = true }
1818
ic-agent = { workspace = true }
1919
x25519-dalek = { workspace = true }
2020
ic_auth_types = { workspace = true }
21-
# ic-vetkeys = { workspace = true }
21+
ic-vetkeys = { workspace = true }
22+
23+
[dev-dependencies]
24+
hex = { workspace = true }
25+
ed25519-consensus = { workspace = true }
26+
tokio = { workspace = true, features = ["full"] }

src/ic_cose/examples/vetkeys.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
use candid::Principal;
2+
use ed25519_consensus::SigningKey;
3+
use ic_agent::{identity::BasicIdentity, Identity};
4+
use ic_cose::agent::build_agent;
5+
use ic_cose::client::{Client, CoseSDK};
6+
use ic_cose::rand_bytes;
7+
use ic_cose::vetkeys::{IbeCiphertext, IbeIdentity, IbeSeed};
8+
use ic_cose_types::types::SettingPath;
9+
use std::sync::Arc;
10+
11+
const IS_LOCAL: bool = false;
12+
13+
// cargo run --example vetkeys
14+
#[tokio::main]
15+
async fn main() {
16+
let canister = Principal::from_text("53cyg-yyaaa-aaaap-ahpua-cai").unwrap();
17+
let sk =
18+
hex::decode("5b3770cbfd16d3ac610cc3cda0bc292a448f2c78d6634de6ee280df0a65e4c04").unwrap();
19+
let sk: [u8; 32] = sk.try_into().unwrap();
20+
let sk = SigningKey::try_from(sk).unwrap();
21+
let id = BasicIdentity::from_signing_key(sk);
22+
println!("Principal: {}", id.sender().unwrap());
23+
// "pxfqr-x3orr-z5yip-7yzdd-hyxgd-dktgh-3awsk-ohzma-lfjzi-753j7-tae"
24+
25+
let host = if IS_LOCAL {
26+
"http://127.0.0.1:4943"
27+
} else {
28+
"https://icp-api.io"
29+
};
30+
let agent = build_agent(host, Arc::new(id)).await.unwrap();
31+
let cli = Client::new(Arc::new(agent), canister);
32+
33+
let key = vec![0u8, 1, 2, 3].into();
34+
let path = SettingPath {
35+
ns: "_".to_string(),
36+
user_owned: true,
37+
subject: None,
38+
key,
39+
version: 1,
40+
};
41+
42+
let (vk, dkp) = cli.vetkey(&path).await.unwrap();
43+
println!("VetKey: {:?}", hex::encode(vk.signature_bytes()));
44+
// VetKey: "8a4554dec6eeb1ab95574005c477ed5a8dadb0acb5d4c7c911771a16d974bcd61db63bd2a89eeb174fc96b58ca9d5eca"
45+
println!("Derived Public Key: {:?}", hex::encode(dkp.serialize()));
46+
// Derived Public Key: "81b09cdf3a525448978fd72532e19b9fbc8ec7d025af4b5fa2c1f85ef007fdb8946be1ccc288c623acf1bf1fa43cac5f1098012a4f91663eaa73894487c94b4b335af8a224e9e30ca136bad8bfdc2b7fc16f0424f66e88553713852ea04b27a8"
47+
48+
let ibe_seed: [u8; 32] = rand_bytes();
49+
let ibe_seed = IbeSeed::from_bytes(&ibe_seed).unwrap();
50+
let ibe_id = IbeIdentity::from_bytes(&path.key);
51+
let msg = b"Hello, LDC Labs!";
52+
let ciphertext = IbeCiphertext::encrypt(&dkp, &ibe_id, msg, &ibe_seed);
53+
let data = ciphertext.serialize();
54+
println!("Ciphertext: {:?}", hex::encode(&data));
55+
56+
let ciphertext = IbeCiphertext::deserialize(&data).unwrap();
57+
let decrypted = ciphertext.decrypt(&vk).unwrap();
58+
assert_eq!(&decrypted, msg);
59+
}

src/ic_cose/src/client.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use std::{collections::BTreeSet, sync::Arc};
2323
use x25519_dalek::{PublicKey, StaticSecret};
2424

2525
use crate::rand_bytes;
26+
use crate::vetkeys::{DerivedPublicKey, EncryptedVetKey, TransportSecretKey, VetKey};
2627

2728
#[derive(Clone)]
2829
pub struct Client {
@@ -250,7 +251,7 @@ pub trait CoseSDK: CanisterCaller + Sized {
250251
async fn vetkd_encrypted_key(
251252
&self,
252253
path: &SettingPath,
253-
transport_public_key: &ByteArray<48>,
254+
transport_public_key: &ByteBuf,
254255
) -> Result<ByteBuf, String> {
255256
self.canister_update(
256257
self.canister(),
@@ -261,6 +262,19 @@ pub trait CoseSDK: CanisterCaller + Sized {
261262
.map_err(format_error)?
262263
}
263264

265+
async fn vetkey(&self, path: &SettingPath) -> Result<(VetKey, DerivedPublicKey), String> {
266+
let pk = self.vetkd_public_key(path).await?;
267+
let dpk = DerivedPublicKey::deserialize(&pk).map_err(|err| format!("{err:?}"))?;
268+
let seed: [u8; 32] = rand_bytes();
269+
let tsk = TransportSecretKey::from_seed(seed.into())?;
270+
let ek = self
271+
.vetkd_encrypted_key(path, &tsk.public_key().into())
272+
.await?;
273+
let evk = EncryptedVetKey::deserialize(&ek).map_err(|err| format!("{err:?}"))?;
274+
let vk = evk.decrypt_and_verify(&tsk, &dpk, &path.key)?;
275+
Ok((vk, dpk))
276+
}
277+
264278
async fn namespace_get_fixed_identity(
265279
&self,
266280
namespace: &str,

src/ic_cose/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use rand::RngCore;
22

33
pub mod agent;
44
pub mod client;
5-
// pub mod vetkeys;
5+
pub mod vetkeys;
66

77
pub fn rand_bytes<const N: usize>() -> [u8; N] {
88
let mut rng = rand::rng();

src/ic_cose_canister/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ crate-type = ["cdylib"]
1818
ic_cose_types = { path = "../ic_cose_types", version = "0.8" }
1919
candid = { workspace = true }
2020
ciborium = { workspace = true }
21-
const-hex = { workspace = true }
21+
hex = { workspace = true }
2222
ic-cdk = { workspace = true }
2323
serde = { workspace = true }
2424
serde_bytes = { workspace = true }

src/ic_cose_canister/src/store.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ impl fmt::Display for SettingPathKey {
378378
self.0,
379379
self.1,
380380
self.2.to_text(),
381-
const_hex::encode(&self.3),
381+
hex::encode(&self.3),
382382
self.4
383383
)
384384
}

src/ic_cose_types/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,5 @@ sha2 = { workspace = true }
2626
sha3 = { workspace = true }
2727
coset = { workspace = true }
2828
aes-gcm = { workspace = true }
29-
const-hex = { workspace = true }
29+
hex = { workspace = true }
3030
thiserror = { workspace = true }

src/ic_cose_types/src/cose/cwt.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ pub fn get_scope(claims: &ClaimsSet) -> Result<String, String> {
6464
#[cfg(test)]
6565
mod test {
6666
use super::*;
67-
use const_hex::decode;
67+
use hex::decode;
6868

6969
#[test]
7070
fn cwt_works() {

0 commit comments

Comments
 (0)