Skip to content

Commit 41c87fd

Browse files
authored
chore(crypto): CRP-2838 use published vetKeys utils in vetKD examples (#1157)
Use the published vetKeys utils in both the Rust and Motoko vetKD examples. Also updates ic_cdk to the latest 0.18.3 in all vetKD examples.
1 parent e30c25d commit 41c87fd

File tree

19 files changed

+174
-218
lines changed

19 files changed

+174
-218
lines changed

motoko/encrypted-notes-dapp-vetkd/Cargo.lock

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

motoko/encrypted-notes-dapp-vetkd/src/encrypted_notes_rust/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
99

1010
[dependencies]
1111
candid = "0.10"
12-
ic-cdk = "0.18.2-alpha.1"
12+
ic-cdk = "0.18.3"
1313
ic-stable-structures = "0.6.4"
1414
lazy_static = "1.4.0"
1515
serde_json = "1.0.108"

motoko/vetkd/README.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,7 @@ It includes:
1111

1212
* An example frontend (`src/app_frontend_js`) that uses the backend from Javascript in the browser.
1313

14-
The frontend uses the [ic-vetkd-utils](https://github.com/dfinity/ic/tree/master/packages/ic-vetkd-utils) to create a transport key pair that is used to obtain a verifiably encrypted key from the system API, to decrypt this key, and to derive a symmetric key to be used for AES encryption/decryption.
15-
16-
Because the `ic-vetkd-utils` are not yet published as NPM package at [npmjs.com](https://npmjs.com), a respective package file (`ic-vetkd-utils-0.1.0.tgz`) is included in this repository.
14+
The frontend uses [@dfinity/vetkeys](https://www.npmjs.com/package/@dfinity/vetkeys) ([docs](https://dfinity.github.io/vetkeys/modules/_dfinity_vetkeys.html)) to create a transport key pair that is used to obtain a verifiably encrypted key from the system API, to decrypt this key, and to derive a symmetric key to be used for AES encryption/decryption.
1715

1816
## Prerequisites
1917
- [x] Install the [IC SDK](https://internetcomputer.org/docs/current/developer-docs/getting-started/install).

motoko/vetkd/ic-vetkd-utils-0.1.0.tgz

-93.5 KB
Binary file not shown.

motoko/vetkd/package-lock.json

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

motoko/vetkd/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
"@dfinity/auth-client": "^2.1.3",
2222
"@dfinity/candid": "^2.1.3",
2323
"@dfinity/principal": "^2.1.3",
24-
"ic-vetkd-utils": "file:ic-vetkd-utils-0.1.0.tgz"
24+
"@dfinity/vetkeys": "^0.1.0"
2525
},
2626
"devDependencies": {
2727
"assert": "2.0.0",

motoko/vetkd/src/app_frontend_js/assets/main.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ button[type="submit"] {
4242
text-align: center;
4343
}
4444

45-
#get_symmetric_key_result {
45+
#get_vetkey_result {
4646
text-align: center;
4747
}
4848

motoko/vetkd/src/app_frontend_js/src/index.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@ <h1 id="vetkddemo">vetKD Demo</h1>
1919
</form>
2020
<hr />
2121
<h2 id="symmetric_encryption_demo">Encryption with symmetric (AES-GCM-256) key</h1>
22-
<form action="#" id="get_symmetric_key_form">
23-
<button type="submit">Fetch symmetric key for local usage</button>
22+
<form action="#" id="get_vetkey_form">
23+
<button type="submit">Fetch vetKey for local usage</button>
2424
</form>
25-
<section id="get_symmetric_key_result"></section>
25+
<section id="get_vetkey_result"></section>
2626
<form action="#" id="encrypt_form">
2727
<label for="plaintext">Plaintext:</label>
2828
<input type="text" id="plaintext"><br><br>

motoko/vetkd/src/app_frontend_js/src/index.js

Lines changed: 48 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,27 @@
11
import { createActor, app_backend } from "../../declarations/app_backend";
2-
import * as vetkd from "ic-vetkd-utils";
2+
import { TransportSecretKey, EncryptedVetKey, DerivedPublicKey, IbeCiphertext, IbeIdentity, IbeSeed } from "@dfinity/vetkeys";
33
import { AuthClient } from "@dfinity/auth-client"
44
import { HttpAgent, Actor } from "@dfinity/agent";
55
import { Principal } from "@dfinity/principal";
66

7-
let fetched_symmetric_key = null;
7+
let fetched_derived_key_material = null;
88
let app_backend_actor = app_backend;
99
let app_backend_principal = await Actor.agentOf(app_backend_actor).getPrincipal();
1010
document.getElementById("principal").innerHTML = annotated_principal(app_backend_principal);
1111

12-
document.getElementById("get_symmetric_key_form").addEventListener("submit", async (e) => {
12+
document.getElementById("get_vetkey_form").addEventListener("submit", async (e) => {
1313
e.preventDefault();
1414
const button = e.target.querySelector("button");
1515
button.setAttribute("disabled", true);
16-
const result = document.getElementById("get_symmetric_key_result");
16+
const result = document.getElementById("get_vetkey_result");
1717

18-
result.innerText = "Fetching symmetric key...";
19-
const aes_256_key = await get_aes_256_gcm_key();
20-
result.innerText = "Done. AES-GCM-256 key available for local usage.";
18+
result.innerText = "Fetching vetKey...";
19+
const derived_key_material = await get_derived_key_material();
20+
result.innerText = "Done. vetKey available for local usage.";
2121

2222
button.removeAttribute("disabled");
2323

24-
fetched_symmetric_key = aes_256_key;
24+
fetched_derived_key_material = derived_key_material;
2525
update_plaintext_button_state();
2626
update_ciphertext_button_state();
2727

@@ -35,9 +35,11 @@ document.getElementById("encrypt_form").addEventListener("submit", async (e) =>
3535
const result = document.getElementById("encrypt_result");
3636

3737
result.innerText = "Encrypting...";
38-
const ciphertext = await aes_gcm_encrypt(document.getElementById("plaintext").value, fetched_symmetric_key);
38+
const message = document.getElementById("plaintext").value;
39+
const message_encoded = new TextEncoder().encode(message);
40+
const ciphertext_hex = hex_encode(await fetched_derived_key_material.encryptMessage(message_encoded, "vetkd-demo"));
3941

40-
result.innerText = "ciphertext: " + ciphertext;
42+
result.innerText = "ciphertext: " + ciphertext_hex;
4143

4244
button.removeAttribute("disabled");
4345
return false;
@@ -51,8 +53,10 @@ document.getElementById("decrypt_form").addEventListener("submit", async (e) =>
5153

5254
result.innerText = "Decrypting...";
5355
try {
54-
const plaintext = await aes_gcm_decrypt(document.getElementById("ciphertext").value, fetched_symmetric_key);
55-
result.innerText = "plaintext: " + plaintext;
56+
const ciphertext_hex = document.getElementById("ciphertext").value;
57+
const plaintext_bytes = await fetched_derived_key_material.decryptMessage(hex_decode(ciphertext_hex), "vetkd-demo");
58+
const plaintext_string = new TextDecoder().decode(plaintext_bytes);
59+
result.innerText = "plaintext: " + plaintext_string;
5660
} catch (e) {
5761
result.innerText = "Error: " + e;
5862
}
@@ -71,7 +75,7 @@ document.getElementById("ciphertext").addEventListener("keyup", async (e) => {
7175

7276
function update_plaintext_button_state() {
7377
const submit_plaintext_button = document.getElementById("submit_plaintext");
74-
if (document.getElementById("plaintext").value === "" || fetched_symmetric_key === null) {
78+
if (document.getElementById("plaintext").value === "" || fetched_derived_key_material === null) {
7579
submit_plaintext_button.setAttribute("disabled", true);
7680
} else {
7781
submit_plaintext_button.removeAttribute("disabled");
@@ -80,54 +84,25 @@ function update_plaintext_button_state() {
8084

8185
function update_ciphertext_button_state() {
8286
const submit_ciphertext_button = document.getElementById("submit_ciphertext");
83-
if (document.getElementById("ciphertext").value === "" || fetched_symmetric_key === null) {
87+
if (document.getElementById("ciphertext").value === "" || fetched_derived_key_material === null) {
8488
submit_ciphertext_button.setAttribute("disabled", true);
8589
} else {
8690
submit_ciphertext_button.removeAttribute("disabled");
8791
}
8892
}
8993

90-
async function get_aes_256_gcm_key() {
91-
const seed = window.crypto.getRandomValues(new Uint8Array(32));
92-
const tsk = new vetkd.TransportSecretKey(seed);
93-
const ek_bytes_hex = await app_backend_actor.encrypted_symmetric_key_for_caller(tsk.public_key());
94+
async function get_derived_key_material() {
95+
const tsk = TransportSecretKey.random();
96+
97+
const ek_bytes_hex = await app_backend_actor.encrypted_symmetric_key_for_caller(tsk.publicKeyBytes());
98+
const encryptedVetKey = new EncryptedVetKey(hex_decode(ek_bytes_hex));
99+
94100
const pk_bytes_hex = await app_backend_actor.symmetric_key_verification_key();
95-
return tsk.decrypt_and_hash(
96-
hex_decode(ek_bytes_hex),
97-
hex_decode(pk_bytes_hex),
98-
app_backend_principal.toUint8Array(),
99-
32,
100-
new TextEncoder().encode("aes-256-gcm")
101-
);
102-
}
101+
const dpk = DerivedPublicKey.deserialize(hex_decode(pk_bytes_hex));
103102

104-
async function aes_gcm_encrypt(message, rawKey) {
105-
const iv = window.crypto.getRandomValues(new Uint8Array(12)); // 96-bits; unique per message
106-
const aes_key = await window.crypto.subtle.importKey("raw", rawKey, "AES-GCM", false, ["encrypt"]);
107-
const message_encoded = new TextEncoder().encode(message);
108-
const ciphertext_buffer = await window.crypto.subtle.encrypt(
109-
{ name: "AES-GCM", iv: iv },
110-
aes_key,
111-
message_encoded
112-
);
113-
const ciphertext = new Uint8Array(ciphertext_buffer);
114-
var iv_and_ciphertext = new Uint8Array(iv.length + ciphertext.length);
115-
iv_and_ciphertext.set(iv, 0);
116-
iv_and_ciphertext.set(ciphertext, iv.length);
117-
return hex_encode(iv_and_ciphertext);
118-
}
103+
const vetKey = encryptedVetKey.decryptAndVerify(tsk, dpk, app_backend_principal.toUint8Array());
119104

120-
async function aes_gcm_decrypt(ciphertext_hex, rawKey) {
121-
const iv_and_ciphertext = hex_decode(ciphertext_hex);
122-
const iv = iv_and_ciphertext.subarray(0, 12); // 96-bits; unique per message
123-
const ciphertext = iv_and_ciphertext.subarray(12);
124-
const aes_key = await window.crypto.subtle.importKey("raw", rawKey, "AES-GCM", false, ["decrypt"]);
125-
let decrypted = await window.crypto.subtle.decrypt(
126-
{ name: "AES-GCM", iv: iv },
127-
aes_key,
128-
ciphertext
129-
);
130-
return new TextDecoder().decode(decrypted);
105+
return await vetKey.asDerivedKeyMaterial();
131106
}
132107

133108
document.getElementById("ibe_encrypt_form").addEventListener("submit", async (e) => {
@@ -197,39 +172,42 @@ function update_ibe_decrypt_button_state() {
197172
async function ibe_encrypt(message) {
198173
document.getElementById("ibe_encrypt_result").innerText = "Fetching IBE encryption key..."
199174
const pk_bytes_hex = await app_backend_actor.ibe_encryption_key();
175+
const dpk = DerivedPublicKey.deserialize(hex_decode(pk_bytes_hex));
200176

201177
document.getElementById("ibe_encrypt_result").innerText = "Preparing IBE-encryption..."
202178
const message_encoded = new TextEncoder().encode(message);
203-
const seed = window.crypto.getRandomValues(new Uint8Array(32));
204179
let ibe_principal = Principal.fromText(document.getElementById("ibe_principal").value);
205180

206181
document.getElementById("ibe_encrypt_result").innerText = "IBE-encrypting for principal" + ibe_principal.toText() + "...";
207-
const ibe_ciphertext = vetkd.IBECiphertext.encrypt(
208-
hex_decode(pk_bytes_hex),
209-
ibe_principal.toUint8Array(),
182+
const ibe_ciphertext = IbeCiphertext.encrypt(
183+
dpk,
184+
IbeIdentity.fromPrincipal(ibe_principal),
210185
message_encoded,
211-
seed
186+
IbeSeed.random(),
212187
);
213188
return hex_encode(ibe_ciphertext.serialize());
214189
}
215190

216191
async function ibe_decrypt(ibe_ciphertext_hex) {
217-
document.getElementById("ibe_decrypt_result").innerText = "Preparing IBE-decryption..."
218-
const tsk_seed = window.crypto.getRandomValues(new Uint8Array(32));
219-
const tsk = new vetkd.TransportSecretKey(tsk_seed);
220-
document.getElementById("ibe_decrypt_result").innerText = "Fetching IBE decryption key..."
221-
const ek_bytes_hex = await app_backend_actor.encrypted_ibe_decryption_key_for_caller(tsk.public_key());
222192
document.getElementById("ibe_decrypt_result").innerText = "Fetching IBE enryption key (needed for verification)..."
223193
const pk_bytes_hex = await app_backend_actor.ibe_encryption_key();
194+
const dpk = DerivedPublicKey.deserialize(hex_decode(pk_bytes_hex));
224195

225-
const k_bytes = tsk.decrypt(
226-
hex_decode(ek_bytes_hex),
227-
hex_decode(pk_bytes_hex),
196+
document.getElementById("ibe_decrypt_result").innerText = "Fetching IBE decryption key..."
197+
const tsk = TransportSecretKey.random();
198+
const ek_bytes_hex = await app_backend_actor.encrypted_ibe_decryption_key_for_caller(tsk.publicKeyBytes());
199+
const encryptedVetKey = new EncryptedVetKey(hex_decode(ek_bytes_hex));
200+
201+
document.getElementById("ibe_decrypt_result").innerText = "Decrypting and verifying IBE decryption key..."
202+
const vetKey = encryptedVetKey.decryptAndVerify(
203+
tsk,
204+
dpk,
228205
app_backend_principal.toUint8Array()
229206
);
230207

231-
const ibe_ciphertext = vetkd.IBECiphertext.deserialize(hex_decode(ibe_ciphertext_hex));
232-
const ibe_plaintext = ibe_ciphertext.decrypt(k_bytes);
208+
document.getElementById("ibe_decrypt_result").innerText = "Using IBE decryption key to decrypt ciphertext..."
209+
const ibe_ciphertext = IbeCiphertext.deserialize(hex_decode(ibe_ciphertext_hex));
210+
const ibe_plaintext = ibe_ciphertext.decrypt(vetKey);
233211
return new TextDecoder().decode(ibe_plaintext);
234212
}
235213

@@ -244,7 +222,7 @@ document.getElementById("login").onclick = async (e) => {
244222
// Safari detection rules are according to: https://developer.mozilla.org/en-US/docs/Web/HTTP/Browser_detection_using_the_user_agent#browser_name_and_version
245223
let isSafari = /^(?!.*chrome\/\d+)(?!.*chromium\/\d+).*safari\/\d+/i.test(navigator.userAgent);
246224
let identityProvider = isSafari ?
247-
`http://localhost:4943/?canisterId=${process.env.CANISTER_ID_INTERNET_IDENTITY}` :
225+
`http://127.0.0.1:4943/?canisterId=${process.env.CANISTER_ID_INTERNET_IDENTITY}` :
248226
`http://${process.env.CANISTER_ID_INTERNET_IDENTITY}.localhost:4943/`;
249227

250228
let authClient = await AuthClient.create();
@@ -266,8 +244,8 @@ document.getElementById("login").onclick = async (e) => {
266244

267245
document.getElementById("principal").innerHTML = annotated_principal(app_backend_principal);
268246

269-
fetched_symmetric_key = null;
270-
document.getElementById("get_symmetric_key_result").innerText = "";
247+
fetched_derived_key_material = null;
248+
document.getElementById("get_vetkey_result").innerText = "";
271249
update_plaintext_button_state();
272250
update_ciphertext_button_state();
273251

0 commit comments

Comments
 (0)