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
59 changes: 59 additions & 0 deletions rust/vetkeys/password_manager/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# VetKey Password Manager

| Motoko backend | [![](https://icp.ninja/assets/open.svg)](http://icp.ninja/editor?g=https://github.com/dfinity/examples/tree/master/rust/vetkeys/password_manager/motoko)|
| --- | --- |
| Rust backend | [![](https://icp.ninja/assets/open.svg)](http://icp.ninja/editor?g=https://github.com/dfinity/examples/tree/master/rust/vetkeys/password_manager/rust) |

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**.

## Features

- **Secure Password Storage**: Uses VetKey to encrypt passwords before storing them in Encrypted Maps.
- **Vault-Based Organization**: Users can create multiple vaults, each containing multiple passwords.
- **Access Control**: Vaults can be shared with other users via their **Internet Identity Principal**.

## Setup

### Prerequisites

- [Local Internet Computer dev environment](https://internetcomputer.org/docs/building-apps/getting-started/install)
- [npm](https://www.npmjs.com/package/npm)

### (Optionally) Choose a Different Master Key

This example uses `test_key_1` by default. To use a different [available master key](https://internetcomputer.org/docs/building-apps/network-features/vetkeys/api#available-master-keys), change the `"init_arg": "(\"test_key_1\")"` line in `dfx.json` to the desired key before running `dfx deploy` in the next step.

### Deploy the Canisters Locally
If you want to deploy this project locally with a Motoko backend, then run:
```bash
dfx start --background && dfx deploy
```
from the `motoko` folder.

To use the Rust backend instead of Motoko, run the same command in the `rust` folder.

## Running the Project

### Backend

The backend consists of an **Encrypted Maps**-enabled canister that securely stores passwords. It is automatically deployed with `dfx deploy`.

### Frontend

The frontend is a **Svelte** application providing a user-friendly interface for managing vaults and passwords.

To run the frontend in development mode with hot reloading:

```bash
npm run dev
```

## Limitations

This example dapp 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 dapp, key rotation would be useful to limit the impact of potential key compromise if a malicious party gains access to a key, or to limit access when users are added or removed from note sharing.

## Additional Resources

- **[Password Manager with Metadata](../password_manager_with_metadata/)** - If you need to store additional metadata alongside passwords.
7 changes: 7 additions & 0 deletions rust/vetkeys/password_manager/frontend/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Ignore artifacts:
build
coverage
dist
README.md
**/declarations/

5 changes: 5 additions & 0 deletions rust/vetkeys/password_manager/frontend/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"plugins": ["prettier-plugin-svelte"],
"tabWidth": 4,
"overrides": [{ "files": ["*.svelte"], "options": { "parser": "svelte" } }]
}
17 changes: 17 additions & 0 deletions rust/vetkeys/password_manager/frontend/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# VetKD Password Manager frontend
Uses the defaults provided by the devkit to implement a VetKD-based password
manager. This utilizes the encrypted maps canister example to realize the
password manager, i.e., there is no dedicated canister implementation, only the
frontend implementation that uses all defaults from the SDK.

## Step 1: Deploy `encrypted_maps_example` canister and the internet identity canister.

## Step 2: Tell `frontend` what canisters to communicate with, so the following environment variables must be defined. For a local deployment, one can run `deploy_locally.sh` from that folder.
* `CANISTER_ID_IC_VETKEYS_ENCRYPTED_MAPS_CANISTER`

## Step 3: Deploy frontend. This returns a link that can be used to access the frontend from the asset canister.
```shell
dfx deploy www
```
Note: if this returns a URL with the IP `0.0.0.0` and the fronetned does not
work, a potential fix is to replace it with `localhost`.
56 changes: 56 additions & 0 deletions rust/vetkeys/password_manager/frontend/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// @ts-check

import eslint from "@eslint/js";
import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended";
import globals from "globals";
import tseslint from "typescript-eslint";
import svelteConfig from "./svelte.config.js";
import svelte from "eslint-plugin-svelte";

export default tseslint.config(
eslint.configs.recommended,
tseslint.configs.recommendedTypeChecked,
...svelte.configs.recommended,
eslintPluginPrettierRecommended,
{
languageOptions: {
parserOptions: {
projectService: {
defaultProject: "./tsconfig.json",
},
tsconfigRootDir: import.meta.dirname,
},
globals: {
...globals.browser,
...globals.es2020,
},
},
},
{
files: ["**/*.svelte", "**/*.svelte.ts", "**/*.svelte.js"],
languageOptions: {
parserOptions: {
projectService: true,
extraFileExtensions: [".svelte"],
parser: tseslint.parser,
svelteConfig,
},
},
},
{
ignores: [
"dist/",
"src/declarations",
"*.config.js",
"*.config.cjs",
"*.config.mjs",
"*.config.ts",
],
},
{
rules: {
"@typescript-eslint/no-unsafe-argument": "off",
"@typescript-eslint/no-unsafe-member-access": "off",
},
},
);
14 changes: 14 additions & 0 deletions rust/vetkeys/password_manager/frontend/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>VetKeys Password Manager</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
<link rel="stylesheet" href="./bundle.css" />
</body>
</html>
47 changes: 47 additions & 0 deletions rust/vetkeys/password_manager/frontend/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
"name": "password-manager-frontend",
"version": "0.1.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"lint": "eslint",
"prettier": "prettier --write .",
"prettier-check": "prettier --check .",
"preview": "vite preview"
},
"devDependencies": {
"@rollup/plugin-typescript": "^12.1.2",
"@tsconfig/svelte": "^5.0.4",
"@typewriter/delta": "^1.2.4",
"daisyui": "^4.12.23",
"eslint-config-prettier": "^10.1.5",
"eslint-plugin-prettier": "^5.2.6",
"eslint-plugin-svelte": "^3.5.1",
"globals": "^16.0.0",
"prettier-plugin-svelte": "^3.4.0",
"svelte": "^4.2.19",
"tslib": "^2.8.1",
"typescript-eslint": "^8.35.1",
"vite": "^5.4.21",
"vite-plugin-environment": "^1.1.3"
},
"dependencies": {
"@dfinity/agent": "^2.3.0",
"@dfinity/auth-client": "^2.3.0",
"@dfinity/candid": "^2.3.0",
"@dfinity/identity": "^2.3.0",
"@dfinity/principal": "^2.3.0",
"@dfinity/vetkeys": "^0.3.0",
"@popperjs/core": "^2.11.8",
"@sveltejs/vite-plugin-svelte": "^3.0.2",
"@tailwindcss/postcss": "^4.0.6",
"@tailwindcss/vite": "^4.0.0",
"autoprefixer": "^10.4.20",
"rollup-plugin-css-only": "^4.5.2",
"svelte-icons": "^2.1.0",
"svelte-spa-router": "^4.0.1",
"tailwindcss": "^3.0.17",
"typewriter-editor": "^0.9.4"
}
}
10 changes: 10 additions & 0 deletions rust/vetkeys/password_manager/frontend/public/.ic-assets.json5
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[
{
match: "**/*",
security_policy: "hardened",
headers: {
"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;",
},
allow_raw_access: false
},
]
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions rust/vetkeys/password_manager/frontend/public/vite.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 13 additions & 0 deletions rust/vetkeys/password_manager/frontend/src/App.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<script lang="ts">
import Hero from "./components/Hero.svelte";
import LayoutAuthenticated from "./components/LayoutAuthenticated.svelte";
import Notifications from "./components/Notifications.svelte";
import { auth } from "./store/auth";
</script>

{#if $auth.state === "initialized"}
<LayoutAuthenticated />
{:else}
<Hero auth={$auth} />
{/if}
<Notifications />
3 changes: 3 additions & 0 deletions rust/vetkeys/password_manager/frontend/src/app.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<script lang="ts">
import { fly } from "svelte/transition";
import DisclaimerCopy from "./DisclaimerCopy.svelte";
let isDismissed = !!window.localStorage.getItem("disclaimer-dismissed");

function dismiss() {
window.localStorage.setItem("disclaimer-dismissed", "yes");
isDismissed = true;
}
</script>

{#if !isDismissed}
<div
class="sticky bottom-0 p-4 text-xs bg-base-300 mt-4 sm:flex"
out:fly={{ y: 50 }}
>
<p class="opacity-90 sm:flex-1">
<DisclaimerCopy />
</p>

<button
class="btn btn-outline btn-xs sm:btn-sm sm:self-start mt-4 sm:mt-0 sm:ml-4 opacity-90"
on:click={dismiss}>I understand</button
>
</div>
{/if}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<script lang="ts"></script>

<strong>Disclaimer:</strong> This sample dapp is intended exclusively for experimental
purpose. You are advised not to use this dapp for storing your critical data such
as keys or passwords.
Loading