Skip to content

Commit 4588cdc

Browse files
committed
Merge remote-tracking branch 'origin/master' into chore/centralize-dev-env-version
# Conflicts: # .github/workflows/vetkeys-password-manager-with-metadata.yml
2 parents f683bb5 + 279e6a9 commit 4588cdc

75 files changed

Lines changed: 2774 additions & 271 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/vetkeys-password-manager-with-metadata.yml

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ on:
66
- master
77
pull_request:
88
paths:
9+
- motoko/vetkeys/password_manager_with_metadata/**
910
- rust/vetkeys/password_manager_with_metadata/**
1011
- .github/workflows/vetkeys-password-manager-with-metadata.yml
1112
- .github/workflows/_run-example.yml
@@ -15,18 +16,20 @@ concurrency:
1516
cancel-in-progress: true
1617

1718
jobs:
18-
rust:
19+
motoko:
1920
uses: ./.github/workflows/_run-example.yml
2021
with:
21-
language: rust
22-
working-directory: rust/vetkeys/password_manager_with_metadata/rust
22+
language: motoko
23+
working-directory: motoko/vetkeys/password_manager_with_metadata
2324
run: |
24-
icp network start -d && icp deploy
25+
icp network start -d
26+
icp deploy
2527
26-
motoko:
28+
rust:
2729
uses: ./.github/workflows/_run-example.yml
2830
with:
29-
language: motoko
30-
working-directory: rust/vetkeys/password_manager_with_metadata/motoko
31+
language: rust
32+
working-directory: rust/vetkeys/password_manager_with_metadata
3133
run: |
32-
icp network start -d && icp deploy
34+
icp network start -d
35+
icp deploy
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# VetKey Password Manager with Metadata (Motoko)
2+
3+
[View this sample's code on GitHub](https://github.com/dfinity/examples/tree/master/motoko/vetkeys/password_manager_with_metadata)
4+
5+
Also available in: [Rust](../../../rust/vetkeys/password_manager_with_metadata)
6+
7+
The **VetKey Password Manager** is an example application demonstrating how to use **VetKeys** and **Encrypted Maps** to build a secure, decentralized password manager on the **Internet Computer (IC)**. This application allows users to create password vaults, store encrypted passwords, and share vaults with other users via their **Internet Identity Principal**.
8+
9+
This version extends the basic password manager by supporting unencrypted metadata, such as URLs and tags, alongside encrypted passwords. The goal is to demonstrate how to make atomic updates to the Encrypted Maps canister, storing both encrypted and unencrypted data in a single update call.
10+
11+
## Features
12+
13+
- **Secure Password Storage**: Uses VetKey to encrypt passwords before storing them in Encrypted Maps.
14+
- **Vault-Based Organization**: Users can create multiple vaults, each containing multiple passwords.
15+
- **Access Control**: Vaults can be shared with other users via their **Internet Identity Principal**.
16+
- **Atomic Updates**: Stores encrypted passwords along with unencrypted metadata in a single update call.
17+
18+
## Build and deploy from the command line
19+
20+
### Prerequisites
21+
22+
- Install [Node.js](https://nodejs.org/en/download/)
23+
- Install [icp-cli](https://cli.internetcomputer.org): `npm install -g @icp-sdk/icp-cli @icp-sdk/ic-wasm`
24+
- Install [ic-mops](https://mops.one): `npm install -g ic-mops`
25+
26+
### (Optionally) choose a different master key
27+
28+
This example uses `test_key_1` by default. To use a different [available master key](https://docs.internetcomputer.org/concepts/vetkeys/#api-overview), change the `init_args` value in `icp.yaml` before deploying.
29+
30+
### Install
31+
32+
```bash
33+
git clone https://github.com/dfinity/examples
34+
cd examples/motoko/vetkeys/password_manager_with_metadata
35+
```
36+
37+
### Deploy
38+
39+
```bash
40+
icp network start -d
41+
icp deploy
42+
```
43+
44+
Open the frontend URL printed by `icp deploy`.
45+
46+
To run the frontend in development mode with hot reloading (after `icp deploy`):
47+
48+
```bash
49+
npm run dev
50+
```
51+
52+
When done, stop the local network to free up the port for other projects:
53+
54+
```bash
55+
icp network stop
56+
```
57+
58+
## Example components
59+
60+
### Backend (`backend/`)
61+
62+
An **Encrypted Maps**-enabled Motoko canister that stores encrypted passwords together with unencrypted metadata (URLs, tags) in atomic update calls.
63+
64+
> **Note on naming.** The backend methods are snake_case (rather than the usual Motoko camelCase). The standard Encrypted Maps methods are called by these exact names by the `@icp-sdk/vetkeys` Encrypted Maps client, and the custom metadata methods follow the same convention — renaming them would break the frontend. An upstream Motoko actor mixin that generates the Encrypted Maps endpoint set automatically is in progress ([dfinity/vetkeys#405](https://github.com/dfinity/vetkeys/pull/405)).
65+
66+
### Frontend (`frontend/`)
67+
68+
A **Svelte** application for managing vaults and passwords. It uses the `@icp-sdk/vetkeys` Encrypted Maps client for the crypto operations and a canister actor (bindings generated from `backend/backend.did` by the `@icp-sdk/bindgen` Vite plugin) for the metadata methods.
69+
70+
## Limitations
71+
72+
This example app does not implement key rotation, which is strongly recommended in a production environment. Key rotation involves periodically changing encryption keys and re-encrypting data to enhance security. In a production app, key rotation would be useful to limit the impact of a potential key compromise, or to limit access when users are added to or removed from sharing.
73+
74+
## Additional resources
75+
76+
- **[Basic Password Manager](../../../rust/vetkeys/password_manager)** — a simpler example without metadata.
77+
- **[What are VetKeys](https://docs.internetcomputer.org/concepts/vetkeys)** — more information about VetKeys and VetKD.
78+
- [Security best practices](https://docs.internetcomputer.org/guides/security/overview/)

rust/vetkeys/password_manager_with_metadata/motoko/backend/src/Main.mo renamed to motoko/vetkeys/password_manager_with_metadata/backend/app.mo

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,18 @@ import Debug "mo:core/Debug";
1212
import Runtime "mo:core/Runtime";
1313
import VetKeys "mo:ic-vetkeys";
1414

15-
persistent actor class (keyName : Text) {
15+
// This canister combines the Encrypted Maps interface with extra metadata-aware
16+
// methods. Its public methods are intentionally snake_case (not the usual Motoko
17+
// camelCase): the standard Encrypted Maps methods are called by these exact names
18+
// by the `@icp-sdk/vetkeys` Encrypted Maps client, and the custom metadata
19+
// methods follow the same convention for a consistent interface — renaming to
20+
// camelCase would break the frontend. An upstream Motoko actor mixin that
21+
// generates the Encrypted Maps endpoint set automatically is in progress
22+
// (https://github.com/dfinity/vetkeys/pull/405).
23+
actor class (keyName : Text) {
1624

1725
// Global state
18-
let encryptedMapsState = VetKeys.EncryptedMaps.newEncryptedMapsState<VetKeys.AccessRights>({ curve = #bls12_381_g2; name = keyName }, "password_manager_example_dapp");
26+
let encryptedMapsState = VetKeys.EncryptedMaps.newEncryptedMapsState<VetKeys.AccessRights>({ curve = #bls12_381_g2; name = keyName }, "password_manager_example_app");
1927
transient let encryptedMaps = VetKeys.EncryptedMaps.EncryptedMaps<VetKeys.AccessRights>(encryptedMapsState, VetKeys.accessRightsOperations());
2028

2129
func compareMetadataKeys(a : MetadataKey, b : MetadataKey) : {
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
type _anon_class_15_1 =
2+
service {
3+
get_accessible_shared_map_names: () ->
4+
(vec record {
5+
principal;
6+
ByteBuf;
7+
}) query;
8+
get_encrypted_values_for_map_with_metadata: (map_owner: principal,
9+
map_name: ByteBuf) -> (Result_4) query;
10+
get_encrypted_vetkey: (map_owner: principal, map_name: ByteBuf,
11+
transport_key: ByteBuf) -> (Result_3);
12+
get_owned_non_empty_map_names: () -> (vec ByteBuf) query;
13+
get_shared_user_access_for_map: (map_owner: principal, map_name:
14+
ByteBuf) -> (Result_2) query;
15+
get_user_rights: (map_owner: principal, map_name: ByteBuf, user:
16+
principal) -> (Result) query;
17+
get_vetkey_verification_key: () -> (ByteBuf);
18+
insert_encrypted_value_with_metadata: (map_owner: principal, map_name:
19+
ByteBuf, map_key: ByteBuf, value: ByteBuf, tags: vec text, url: text) ->
20+
(Result_1);
21+
remove_encrypted_value_with_metadata: (map_owner: principal, map_name:
22+
ByteBuf, map_key: ByteBuf) -> (Result_1);
23+
remove_user: (map_owner: principal, map_name: ByteBuf, user: principal) ->
24+
(Result);
25+
set_user_rights: (map_owner: principal, map_name: ByteBuf, user:
26+
principal, access_rights: AccessRights) -> (Result);
27+
};
28+
type Result_4 =
29+
variant {
30+
Err: text;
31+
Ok: vec record {
32+
ByteBuf;
33+
ByteBuf;
34+
PasswordMetadata;
35+
};
36+
};
37+
type Result_3 =
38+
variant {
39+
Err: text;
40+
Ok: ByteBuf;
41+
};
42+
type Result_2 =
43+
variant {
44+
Err: text;
45+
Ok: vec record {
46+
principal;
47+
AccessRights;
48+
};
49+
};
50+
type Result_1 =
51+
variant {
52+
Err: text;
53+
Ok: opt record {
54+
ByteBuf;
55+
PasswordMetadata;
56+
};
57+
};
58+
type Result =
59+
variant {
60+
Err: text;
61+
Ok: opt AccessRights;
62+
};
63+
type PasswordMetadata =
64+
record {
65+
creation_date: nat64;
66+
last_modification_date: nat64;
67+
last_modified_principal: principal;
68+
number_of_modifications: nat64;
69+
tags: vec text;
70+
url: text;
71+
};
72+
type ByteBuf = record {inner: blob;};
73+
type AccessRights =
74+
variant {
75+
Read;
76+
ReadWrite;
77+
ReadWriteManage;
78+
};
79+
service : (keyName: text) -> _anon_class_15_1
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
node_modules/
2+
dist/
3+
src/bindings/
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7+
<title>Password Manager with Metadata based on vetKeys</title>
8+
</head>
9+
<body>
10+
<div id="app"></div>
11+
<script type="module" src="/src/main.ts"></script>
12+
<link rel="stylesheet" href="./bundle.css" />
13+
</body>
14+
</html>
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{
2+
"compilerOptions": {
3+
"moduleResolution": "bundler",
4+
"target": "ESNext",
5+
"module": "ESNext",
6+
/**
7+
* svelte-preprocess cannot figure out whether you have
8+
* a value or a type, so tell TypeScript to enforce using
9+
* `import type` instead of `import` for Types.
10+
*/
11+
"verbatimModuleSyntax": true,
12+
"isolatedModules": true,
13+
"resolveJsonModule": true,
14+
/**
15+
* To have warnings / errors of the Svelte compiler at the
16+
* correct position, enable source maps by default.
17+
*/
18+
"sourceMap": true,
19+
"esModuleInterop": true,
20+
"skipLibCheck": true,
21+
/**
22+
* Typecheck JS in `.svelte` and `.js` files by default.
23+
* Disable this if you'd like to use dynamic types.
24+
*/
25+
"checkJs": true
26+
},
27+
/**
28+
* Use global.d.ts instead of compilerOptions.types
29+
* to avoid limiting type declarations.
30+
*/
31+
"include": [
32+
"src/**/*.d.ts",
33+
"src/**/*.js",
34+
"src/**/*.svelte",
35+
"src/main.ts",
36+
"src/main.ts"
37+
]
38+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
{
2+
"name": "frontend",
3+
"private": true,
4+
"type": "module",
5+
"scripts": {
6+
"prebuild": "npm i --include=dev",
7+
"dev": "vite",
8+
"build": "vite build",
9+
"preview": "vite preview"
10+
},
11+
"devDependencies": {
12+
"@icp-sdk/bindgen": "~0.2.2",
13+
"@rollup/plugin-typescript": "^12.1.2",
14+
"@tailwindcss/postcss": "^4.0.6",
15+
"@tailwindcss/vite": "^4.0.0",
16+
"@tsconfig/svelte": "^5.0.4",
17+
"@typewriter/delta": "^1.2.4",
18+
"autoprefixer": "^10.4.20",
19+
"rollup-plugin-css-only": "^4.5.2",
20+
"tslib": "^2.8.1",
21+
"vite": "^5.4.21"
22+
},
23+
"dependencies": {
24+
"@icp-sdk/auth": "^7.1.0",
25+
"@icp-sdk/core": "^5.4.0",
26+
"@icp-sdk/vetkeys": "^0.5.0-beta.0",
27+
"@sveltejs/vite-plugin-svelte": "^3.0.2",
28+
"daisyui": "^4.12.23",
29+
"svelte": "^4.2.19",
30+
"svelte-icons": "^2.1.0",
31+
"svelte-spa-router": "^4.0.1",
32+
"tailwindcss": "^3.0.17",
33+
"typewriter-editor": "^0.9.4"
34+
}
35+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[
2+
{
3+
match: "**/*",
4+
security_policy: "hardened",
5+
headers: {
6+
"Content-Security-Policy": "default-src 'self';script-src 'self';connect-src 'self' http://localhost:* https://icp0.io https://*.icp0.io https://icp-api.io;img-src 'self' data:;style-src * 'unsafe-inline';object-src 'none';base-uri 'self';frame-ancestors 'none';form-action 'self';upgrade-insecure-requests;",
7+
},
8+
allow_raw_access: false
9+
},
10+
]
6.12 KB
Loading

0 commit comments

Comments
 (0)