Skip to content

Commit d3c95c0

Browse files
authored
chore(vetkeys): split password_manager into self-contained motoko + rust examples (#1446)
1 parent 8afd2a7 commit d3c95c0

76 files changed

Lines changed: 2529 additions & 280 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.yml

Lines changed: 16 additions & 10 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/**
910
- rust/vetkeys/password_manager/**
1011
- .github/workflows/vetkeys-password-manager.yml
1112

@@ -14,23 +15,28 @@ concurrency:
1415
cancel-in-progress: true
1516

1617
jobs:
17-
rust:
18+
motoko:
1819
runs-on: ubuntu-24.04
19-
container: ghcr.io/dfinity/icp-dev-env-rust:1.0.1
20+
container: ghcr.io/dfinity/icp-dev-env-motoko:1.0.1
2021
env:
2122
ICP_CLI_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
2223
steps:
2324
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
24-
- name: Deploy Password Manager Rust
25-
working-directory: rust/vetkeys/password_manager/rust
26-
run: icp network start -d && icp deploy
27-
motoko:
25+
- name: Deploy
26+
working-directory: motoko/vetkeys/password_manager
27+
run: |
28+
icp network start -d
29+
icp deploy
30+
31+
rust:
2832
runs-on: ubuntu-24.04
29-
container: ghcr.io/dfinity/icp-dev-env-motoko:1.0.1
33+
container: ghcr.io/dfinity/icp-dev-env-rust:1.0.1
3034
env:
3135
ICP_CLI_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
3236
steps:
3337
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
34-
- name: Deploy Password Manager Motoko
35-
working-directory: rust/vetkeys/password_manager/motoko
36-
run: icp network start -d && icp deploy
38+
- name: Deploy
39+
working-directory: rust/vetkeys/password_manager
40+
run: |
41+
icp network start -d
42+
icp deploy
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# VetKey Password Manager (Motoko)
2+
3+
[View this sample's code on GitHub](https://github.com/dfinity/examples/tree/master/motoko/vetkeys/password_manager)
4+
5+
Also available in: [Rust](../../../rust/vetkeys/password_manager)
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+
## Features
10+
11+
- **Secure Password Storage**: Uses VetKey to encrypt passwords before storing them in Encrypted Maps.
12+
- **Vault-Based Organization**: Users can create multiple vaults, each containing multiple passwords.
13+
- **Access Control**: Vaults can be shared with other users via their **Internet Identity Principal**.
14+
15+
## Build and deploy from the command line
16+
17+
### Prerequisites
18+
19+
- Install [Node.js](https://nodejs.org/en/download/)
20+
- Install [icp-cli](https://cli.internetcomputer.org): `npm install -g @icp-sdk/icp-cli @icp-sdk/ic-wasm`
21+
- Install [ic-mops](https://mops.one): `npm install -g ic-mops`
22+
23+
### (Optionally) choose a different master key
24+
25+
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.
26+
27+
### Install
28+
29+
```bash
30+
git clone https://github.com/dfinity/examples
31+
cd examples/motoko/vetkeys/password_manager
32+
```
33+
34+
### Deploy
35+
36+
```bash
37+
icp network start -d
38+
icp deploy
39+
```
40+
41+
Open the frontend URL printed by `icp deploy`.
42+
43+
To run the frontend in development mode with hot reloading (after `icp deploy`):
44+
45+
```bash
46+
npm run dev
47+
```
48+
49+
When done, stop the local network to free up the port for other projects:
50+
51+
```bash
52+
icp network stop
53+
```
54+
55+
## Example components
56+
57+
### Backend (`backend/`)
58+
59+
An **Encrypted Maps**-enabled Motoko canister that securely stores passwords.
60+
61+
> **Note on naming.** The backend methods are snake_case (rather than the usual Motoko camelCase) because the `@icp-sdk/vetkeys` Encrypted Maps client calls the canister by these exact names — renaming them would break the frontend. The delegation methods are hand-written for now; an upstream Motoko actor mixin that generates this endpoint set automatically is in progress ([dfinity/vetkeys#405](https://github.com/dfinity/vetkeys/pull/405)).
62+
63+
### Frontend (`frontend/`)
64+
65+
A **Svelte** application providing a user-friendly interface for managing vaults and passwords. It talks to the backend through the `@icp-sdk/vetkeys` Encrypted Maps client.
66+
67+
## Limitations
68+
69+
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.
70+
71+
## Additional resources
72+
73+
- **[Password Manager with Metadata](../../../rust/vetkeys/password_manager_with_metadata)** — if you need to store additional metadata alongside passwords.
74+
- **[What are VetKeys](https://docs.internetcomputer.org/concepts/vetkeys)** — more information about VetKeys and VetKD.
75+
- [Security best practices](https://docs.internetcomputer.org/guides/security/overview/)

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

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,15 @@ import Blob "mo:core/Blob";
66
import Result "mo:core/Result";
77
import Array "mo:core/Array";
88

9-
persistent actor class (keyName : Text) {
10-
let encryptedMapsState = IcVetkeys.EncryptedMaps.newEncryptedMapsState<Types.AccessRights>({ curve = #bls12_381_g2; name = keyName }, "password_manager_example_dapp");
9+
// This canister exposes the Encrypted Maps interface. Its public methods are
10+
// intentionally snake_case (not the usual Motoko camelCase) because the
11+
// `@icp-sdk/vetkeys` Encrypted Maps client calls the canister by these exact
12+
// names — renaming them to camelCase would break the frontend. The delegation
13+
// methods below are hand-written today; an upstream Motoko actor mixin that
14+
// generates this endpoint set automatically is in progress
15+
// (https://github.com/dfinity/vetkeys/pull/405).
16+
actor class (keyName : Text) {
17+
let encryptedMapsState = IcVetkeys.EncryptedMaps.newEncryptedMapsState<Types.AccessRights>({ curve = #bls12_381_g2; name = keyName }, "password_manager_example_app");
1118
transient let encryptedMaps = IcVetkeys.EncryptedMaps.EncryptedMaps<Types.AccessRights>(encryptedMapsState, Types.accessRightsOperations());
1219

1320
/// In this canister, we use the `ByteBuf` type to represent blobs. The reason is that we want to be consistent with the Rust canister implementation.
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
type _anon_class_9_1 =
2+
service {
3+
get_accessible_shared_map_names: () ->
4+
(vec record {
5+
principal;
6+
ByteBuf;
7+
}) query;
8+
get_all_accessible_encrypted_maps: () -> (vec EncryptedMapData) query;
9+
get_all_accessible_encrypted_values: () ->
10+
(vec record {
11+
record {
12+
principal;
13+
ByteBuf;
14+
};
15+
vec record {
16+
ByteBuf;
17+
ByteBuf;
18+
};
19+
}) query;
20+
get_encrypted_value: (map_owner: principal, map_name: ByteBuf, map_key:
21+
ByteBuf) -> (Result_2) query;
22+
get_encrypted_values_for_map: (map_owner: principal, map_name: ByteBuf) ->
23+
(Result_5) query;
24+
get_encrypted_vetkey: (map_owner: principal, map_name: ByteBuf,
25+
transport_key: ByteBuf) -> (Result_4);
26+
get_owned_non_empty_map_names: () -> (vec ByteBuf) query;
27+
get_shared_user_access_for_map: (map_owner: principal, map_name:
28+
ByteBuf) -> (Result_3) query;
29+
get_user_rights: (map_owner: principal, map_name: ByteBuf, user:
30+
principal) -> (Result) query;
31+
get_vetkey_verification_key: () -> (ByteBuf);
32+
insert_encrypted_value: (map_owner: principal, map_name: ByteBuf, map_key:
33+
ByteBuf, value: ByteBuf) -> (Result_2);
34+
remove_encrypted_value: (map_owner: principal, map_name: ByteBuf, map_key:
35+
ByteBuf) -> (Result_2);
36+
remove_map_values: (map_owner: principal, map_name: ByteBuf) -> (Result_1);
37+
remove_user: (map_owner: principal, map_name: ByteBuf, user: principal) ->
38+
(Result);
39+
set_user_rights: (map_owner: principal, map_name: ByteBuf, user:
40+
principal, access_rights: AccessRights) -> (Result);
41+
};
42+
type Result_5 =
43+
variant {
44+
Err: text;
45+
Ok: vec record {
46+
ByteBuf;
47+
ByteBuf;
48+
};
49+
};
50+
type Result_4 =
51+
variant {
52+
Err: text;
53+
Ok: ByteBuf;
54+
};
55+
type Result_3 =
56+
variant {
57+
Err: text;
58+
Ok: vec record {
59+
principal;
60+
AccessRights;
61+
};
62+
};
63+
type Result_2 =
64+
variant {
65+
Err: text;
66+
Ok: opt ByteBuf;
67+
};
68+
type Result_1 =
69+
variant {
70+
Err: text;
71+
Ok: vec ByteBuf;
72+
};
73+
type Result =
74+
variant {
75+
Err: text;
76+
Ok: opt AccessRights;
77+
};
78+
type EncryptedMapData =
79+
record {
80+
access_control: vec record {
81+
principal;
82+
AccessRights;
83+
};
84+
keyvals: vec record {
85+
ByteBuf;
86+
ByteBuf;
87+
};
88+
map_name: ByteBuf;
89+
map_owner: principal;
90+
};
91+
type ByteBuf = record {inner: blob;};
92+
type AccessRights =
93+
variant {
94+
Read;
95+
ReadWrite;
96+
ReadWriteManage;
97+
};
98+
service : (keyName: text) -> _anon_class_9_1
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
node_modules/
2+
dist/
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>VetKeys Password Manager</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: 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+
"@rollup/plugin-typescript": "^12.1.2",
13+
"@tsconfig/svelte": "^5.0.4",
14+
"@typewriter/delta": "^1.2.4",
15+
"daisyui": "^4.12.23",
16+
"svelte": "^4.2.19",
17+
"tslib": "^2.8.1",
18+
"vite": "^5.4.21"
19+
},
20+
"dependencies": {
21+
"@icp-sdk/auth": "^7.1.0",
22+
"@icp-sdk/core": "^5.4.0",
23+
"@icp-sdk/vetkeys": "^0.5.0-beta.0",
24+
"@popperjs/core": "^2.11.8",
25+
"@sveltejs/vite-plugin-svelte": "^3.0.2",
26+
"@tailwindcss/postcss": "^4.0.6",
27+
"@tailwindcss/vite": "^4.0.0",
28+
"autoprefixer": "^10.4.20",
29+
"rollup-plugin-css-only": "^4.5.2",
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
7.49 KB
Loading

0 commit comments

Comments
 (0)