├── .gitignore
├── banner.png
├── tsconfig.json
├── rollup.config.mjs
├── package.json
├── LICENSE.spdx
├── Cargo.toml
├── LICENSE_MIT
├── src
├── kdf.rs
├── stronghold.rs
└── lib.rs
├── README.md
├── dist-js
├── index.mjs.map
├── index.d.ts
├── index.mjs
├── index.min.js.map
└── index.min.js
├── LICENSE_APACHE-2.0
└── guest-js
└── index.ts
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
--------------------------------------------------------------------------------
/banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tauri-apps/tauri-plugin-stronghold/HEAD/banner.png
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "include": ["guest-js/*.ts"]
4 | }
5 |
--------------------------------------------------------------------------------
/rollup.config.mjs:
--------------------------------------------------------------------------------
1 | import { readFileSync } from "fs";
2 |
3 | import { createConfig } from "../../shared/rollup.config.mjs";
4 |
5 | export default createConfig({
6 | input: "guest-js/index.ts",
7 | pkg: JSON.parse(
8 | readFileSync(new URL("./package.json", import.meta.url), "utf8"),
9 | ),
10 | external: [/^@tauri-apps\/api/],
11 | });
12 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tauri-plugin-stronghold-api",
3 | "version": "0.0.0",
4 | "description": "Store secrets and keys using the IOTA Stronghold encrypted database.",
5 | "license": "MIT or APACHE-2.0",
6 | "authors": [
7 | "Tauri Programme within The Commons Conservancy"
8 | ],
9 | "type": "module",
10 | "browser": "dist-js/index.min.js",
11 | "module": "dist-js/index.mjs",
12 | "types": "dist-js/index.d.ts",
13 | "exports": {
14 | "import": "./dist-js/index.mjs",
15 | "types": "./dist-js/index.d.ts",
16 | "browser": "./dist-js/index.min.js"
17 | },
18 | "scripts": {
19 | "build": "rollup -c"
20 | },
21 | "files": [
22 | "dist-js",
23 | "!dist-js/**/*.map",
24 | "README.md",
25 | "LICENSE"
26 | ],
27 | "devDependencies": {
28 | "tslib": "2.8.1"
29 | },
30 | "dependencies": {
31 | "@tauri-apps/api": "1.6.0"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/LICENSE.spdx:
--------------------------------------------------------------------------------
1 | SPDXVersion: SPDX-2.1
2 | DataLicense: CC0-1.0
3 | PackageName: tauri
4 | DataFormat: SPDXRef-1
5 | PackageSupplier: Organization: The Tauri Programme in the Commons Conservancy
6 | PackageHomePage: https://tauri.app
7 | PackageLicenseDeclared: Apache-2.0
8 | PackageLicenseDeclared: MIT
9 | PackageCopyrightText: 2019-2022, The Tauri Programme in the Commons Conservancy
10 | PackageSummary: Tauri is a rust project that enables developers to make secure
11 | and small desktop applications using a web frontend.
12 |
13 | PackageComment: The package includes the following libraries; see
14 | Relationship information.
15 |
16 | Created: 2019-05-20T09:00:00Z
17 | PackageDownloadLocation: git://github.com/tauri-apps/tauri
18 | PackageDownloadLocation: git+https://github.com/tauri-apps/tauri.git
19 | PackageDownloadLocation: git+ssh://github.com/tauri-apps/tauri.git
20 | Creator: Person: Daniel Thompson-Yvetot
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "tauri-plugin-stronghold"
3 | version = "0.0.0"
4 | description = "Store secrets and keys using the IOTA Stronghold encrypted database."
5 | authors = { workspace = true }
6 | license = { workspace = true }
7 | edition = { workspace = true }
8 | rust-version = { workspace = true }
9 |
10 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
11 |
12 | [dependencies]
13 | serde = { workspace = true }
14 | serde_json = { workspace = true }
15 | tauri = { workspace = true }
16 | log = { workspace = true }
17 | thiserror = { workspace = true }
18 | iota_stronghold = "1"
19 | iota-crypto = "0.23"
20 | hex = "0.4"
21 | zeroize = { version = "1", features = ["zeroize_derive"] }
22 |
23 | # kdf dependencies
24 | rust-argon2 = { version = "1", optional = true }
25 | rand_chacha = { version = "0.3.1", optional = true }
26 | rand_core = { version = "0.6.4", features = ["getrandom"], optional = true }
27 |
28 | [dev-dependencies]
29 | rand = "0.8"
30 | rusty-fork = "0.3"
31 |
32 | [features]
33 | default = ["kdf"]
34 | kdf = ["dep:rust-argon2", "dep:rand_chacha", "dep:rand_core"]
35 |
--------------------------------------------------------------------------------
/LICENSE_MIT:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 - Present Tauri Apps Contributors
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/src/kdf.rs:
--------------------------------------------------------------------------------
1 | use rand_chacha::ChaCha20Rng;
2 | use rand_core::{RngCore, SeedableRng};
3 | use std::path::Path;
4 |
5 | /// NOTE: Hash supplied to Stronghold must be 32bits long.
6 | /// This is a current limitation of Stronghold.
7 | const HASH_LENGTH: usize = 32;
8 |
9 | pub struct KeyDerivation {}
10 |
11 | impl KeyDerivation {
12 | /// Will create a key from [`password`] and a generated salt.
13 | /// Salt will be generated to file [`salt_path`] or taken from it
14 | /// if file already exists
15 | pub fn argon2(password: &str, salt_path: &Path) -> Vec {
16 | let mut salt = [0u8; HASH_LENGTH];
17 | create_or_get_salt(&mut salt, salt_path);
18 |
19 | argon2::hash_raw(password.as_bytes(), &salt, &Default::default())
20 | .expect("Failed to generate hash for password")
21 | }
22 | }
23 |
24 | fn create_or_get_salt(salt: &mut [u8], salt_path: &Path) {
25 | if salt_path.is_file() {
26 | // Get existing salt
27 | let tmp = std::fs::read(salt_path).unwrap();
28 | salt.clone_from_slice(&tmp);
29 | } else {
30 | // Generate new salt
31 | let mut gen = ChaCha20Rng::from_entropy();
32 | gen.fill_bytes(salt);
33 | std::fs::write(salt_path, salt).expect("Failed to write salt for Stronghold")
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/stronghold.rs:
--------------------------------------------------------------------------------
1 | use std::{convert::TryFrom, ops::Deref, path::Path};
2 |
3 | use iota_stronghold::{KeyProvider, SnapshotPath};
4 | use serde::{Serialize, Serializer};
5 |
6 | pub type Result = std::result::Result;
7 |
8 | #[derive(Debug, thiserror::Error)]
9 | pub enum Error {
10 | #[error("stronghold not initialized")]
11 | StrongholdNotInitialized,
12 | #[error(transparent)]
13 | Stronghold(#[from] iota_stronghold::ClientError),
14 | #[error(transparent)]
15 | Memory(#[from] iota_stronghold::MemoryError),
16 | #[error(transparent)]
17 | Procedure(#[from] iota_stronghold::procedures::ProcedureError),
18 | }
19 |
20 | impl Serialize for Error {
21 | fn serialize(&self, serializer: S) -> std::result::Result
22 | where
23 | S: Serializer,
24 | {
25 | serializer.serialize_str(self.to_string().as_str())
26 | }
27 | }
28 |
29 | pub struct Stronghold {
30 | inner: iota_stronghold::Stronghold,
31 | path: SnapshotPath,
32 | keyprovider: KeyProvider,
33 | }
34 |
35 | impl Stronghold {
36 | pub fn new>(path: P, password: Vec) -> Result {
37 | let path = SnapshotPath::from_path(path);
38 | let stronghold = iota_stronghold::Stronghold::default();
39 | let keyprovider = KeyProvider::try_from(password)?;
40 | if path.exists() {
41 | stronghold.load_snapshot(&keyprovider, &path)?;
42 | }
43 | Ok(Self {
44 | inner: stronghold,
45 | path,
46 | keyprovider,
47 | })
48 | }
49 |
50 | pub fn save(&self) -> Result<()> {
51 | self.inner
52 | .commit_with_keyprovider(&self.path, &self.keyprovider)?;
53 | Ok(())
54 | }
55 |
56 | pub fn inner(&self) -> &iota_stronghold::Stronghold {
57 | &self.inner
58 | }
59 | }
60 |
61 | impl Deref for Stronghold {
62 | type Target = iota_stronghold::Stronghold;
63 | fn deref(&self) -> &Self::Target {
64 | &self.inner
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | Store secrets and keys using the [IOTA Stronghold](https://github.com/iotaledger/stronghold.rs) encrypted database and secure runtime.
4 |
5 | ## Install
6 |
7 | _This plugin requires a Rust version of at least **1.67**_
8 |
9 | There are three general methods of installation that we can recommend.
10 |
11 | 1. Use crates.io and npm (easiest, and requires you to trust that our publishing pipeline worked)
12 | 2. Pull sources directly from Github using git tags / revision hashes (most secure)
13 | 3. Git submodule install this repo in your tauri project and then use file protocol to ingest the source (most secure, but inconvenient to use)
14 |
15 | Install the Core plugin by adding the following to your `Cargo.toml` file:
16 |
17 | `src-tauri/Cargo.toml`
18 |
19 | ```toml
20 | [dependencies]
21 | tauri-plugin-stronghold = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" }
22 | ```
23 |
24 | You can install the JavaScript Guest bindings using your preferred JavaScript package manager:
25 |
26 | > Note: If your JavaScript package manager cannot install packages from git monorepos, you can still use the code by manually copying the [Guest bindings](./guest-js/index.ts) into your source files.
27 |
28 | ```sh
29 | pnpm add https://github.com/tauri-apps/tauri-plugin-stronghold#v1
30 | # or
31 | npm add https://github.com/tauri-apps/tauri-plugin-stronghold#v1
32 | # or
33 | yarn add https://github.com/tauri-apps/tauri-plugin-stronghold#v1
34 | ```
35 |
36 | ## Usage
37 |
38 | First you need to register the core plugin with Tauri:
39 |
40 | `src-tauri/src/main.rs`
41 |
42 | ```rust
43 | fn main() {
44 | tauri::Builder::default()
45 | .plugin(tauri_plugin_stronghold::Builder::new(|password| {
46 | // Hash the password here with e.g. argon2, blake2b or any other secure algorithm
47 | // Here is an example implementation using the `rust-argon2` crate for hashing the password
48 |
49 | use argon2::{hash_raw, Config, Variant, Version};
50 |
51 | let config = Config {
52 | lanes: 4,
53 | mem_cost: 10_000,
54 | time_cost: 10,
55 | variant: Variant::Argon2id,
56 | version: Version::Version13,
57 | ..Default::default()
58 | };
59 |
60 | let salt = "your-salt".as_bytes();
61 |
62 | let key = hash_raw(password.as_ref(), salt, &config).expect("failed to hash password");
63 |
64 | key.to_vec()
65 | })
66 | .build())
67 | .run(tauri::generate_context!())
68 | .expect("error while running tauri application");
69 | }
70 | ```
71 |
72 | Afterwards all the plugin's APIs are available through the JavaScript guest bindings:
73 |
74 | ```javascript
75 | import { Stronghold, Location, Client } from "tauri-plugin-stronghold-api";
76 | import { appDataDir } from "@tauri-apps/api/path";
77 |
78 | const initStronghold = async () => {
79 | const vaultPath = `${await appDataDir()}/vault.hold`;
80 |
81 | const vaultKey = "The key to the vault";
82 |
83 | const stronghold = await Stronghold.load(vaultPath, vaultKey);
84 |
85 | let client: Client;
86 |
87 | const clientName = "name your client";
88 |
89 | try {
90 | client = await hold.loadClient(clientName);
91 | } catch {
92 | client = await hold.createClient(clientName);
93 | }
94 |
95 | return {
96 | stronghold,
97 | client,
98 | };
99 | };
100 |
101 | const { stronghold, client } = await initStronghold();
102 |
103 | const store = client.getStore();
104 |
105 | const key = "my_key";
106 |
107 | // Insert a record to the store
108 |
109 | const data = Array.from(new TextEncoder().encode("Hello, World!"));
110 |
111 | await store.insert(key, data);
112 |
113 | // Read a record from store
114 |
115 | const data = await store.get(key);
116 |
117 | const value = new TextDecoder().decode(new Uint8Array(data));
118 |
119 | // Save your updates
120 |
121 | await stronghold.save();
122 |
123 | // Remove a record from store
124 |
125 | await store.remove(key);
126 | ```
127 |
128 | ## Contributing
129 |
130 | PRs accepted. Please make sure to read the Contributing Guide before making a pull request.
131 |
132 | ## Partners
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 | |
142 |
143 |
144 |
145 |
146 | For the complete list of sponsors please visit our [website](https://tauri.app#sponsors) and [Open Collective](https://opencollective.com/tauri).
147 |
148 | ## License
149 |
150 | Code: (c) 2015 - Present - The Tauri Programme within The Commons Conservancy.
151 |
152 | MIT or MIT/Apache 2.0 where applicable.
153 |
--------------------------------------------------------------------------------
/dist-js/index.mjs.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"index.mjs","sources":["../guest-js/index.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
--------------------------------------------------------------------------------
/dist-js/index.d.ts:
--------------------------------------------------------------------------------
1 | type BytesDto = string | number[];
2 | export type ClientPath = string | Iterable | ArrayLike | ArrayBuffer;
3 | export type VaultPath = string | Iterable | ArrayLike | ArrayBuffer;
4 | export type RecordPath = string | Iterable | ArrayLike | ArrayBuffer;
5 | export type StoreKey = string | Iterable | ArrayLike | ArrayBuffer;
6 | export interface ConnectionLimits {
7 | maxPendingIncoming?: number;
8 | maxPendingOutgoing?: number;
9 | maxEstablishedIncoming?: number;
10 | maxEstablishedOutgoing?: number;
11 | maxEstablishedPerPeer?: number;
12 | maxEstablishedTotal?: number;
13 | }
14 | export interface PeerAddress {
15 | known: string[];
16 | use_relay_fallback: boolean;
17 | }
18 | export interface AddressInfo {
19 | peers: Map;
20 | relays: string[];
21 | }
22 | export interface ClientAccess {
23 | useVaultDefault?: boolean;
24 | useVaultExceptions?: Map;
25 | writeVaultDefault?: boolean;
26 | writeVaultExceptions?: Map;
27 | cloneVaultDefault?: boolean;
28 | cloneVaultExceptions?: Map;
29 | readStore?: boolean;
30 | writeStore?: boolean;
31 | }
32 | export interface Permissions {
33 | default?: ClientAccess;
34 | exceptions?: Map;
35 | }
36 | export interface NetworkConfig {
37 | requestTimeout?: Duration;
38 | connectionTimeout?: Duration;
39 | connectionsLimit?: ConnectionLimits;
40 | enableMdns?: boolean;
41 | enableRelay?: boolean;
42 | addresses?: AddressInfo;
43 | peerPermissions?: Map;
44 | permissionsDefault?: Permissions;
45 | }
46 | /** A duration definition. */
47 | export interface Duration {
48 | /** The number of whole seconds contained by this Duration. */
49 | secs: number;
50 | /** The fractional part of this Duration, in nanoseconds. Must be greater or equal to 0 and smaller than 1e+9 (the max number of nanoseoncds in a second)*/
51 | nanos: number;
52 | }
53 | export declare class Location {
54 | type: string;
55 | payload: Record;
56 | constructor(type: string, payload: Record);
57 | static generic(vault: VaultPath, record: RecordPath): Location;
58 | static counter(vault: VaultPath, counter: number): Location;
59 | }
60 | declare class ProcedureExecutor {
61 | procedureArgs: Record;
62 | constructor(procedureArgs: Record);
63 | /**
64 | * Generate a SLIP10 seed for the given location.
65 | * @param outputLocation Location of the record where the seed will be stored.
66 | * @param sizeBytes The size in bytes of the SLIP10 seed.
67 | * @param hint The record hint.
68 | * @returns
69 | */
70 | generateSLIP10Seed(outputLocation: Location, sizeBytes?: number): Promise;
71 | /**
72 | * Derive a SLIP10 private key using a seed or key.
73 | * @param chain The chain path.
74 | * @param source The source type, either 'Seed' or 'Key'.
75 | * @param sourceLocation The source location, must be the `outputLocation` of a previous call to `generateSLIP10Seed` or `deriveSLIP10`.
76 | * @param outputLocation Location of the record where the private key will be stored.
77 | * @param hint The record hint.
78 | * @returns
79 | */
80 | deriveSLIP10(chain: number[], source: "Seed" | "Key", sourceLocation: Location, outputLocation: Location): Promise;
81 | /**
82 | * Store a BIP39 mnemonic.
83 | * @param mnemonic The mnemonic string.
84 | * @param outputLocation The location of the record where the BIP39 mnemonic will be stored.
85 | * @param passphrase The optional mnemonic passphrase.
86 | * @param hint The record hint.
87 | * @returns
88 | */
89 | recoverBIP39(mnemonic: string, outputLocation: Location, passphrase?: string): Promise;
90 | /**
91 | * Generate a BIP39 seed.
92 | * @param outputLocation The location of the record where the BIP39 seed will be stored.
93 | * @param passphrase The optional mnemonic passphrase.
94 | * @param hint The record hint.
95 | * @returns
96 | */
97 | generateBIP39(outputLocation: Location, passphrase?: string): Promise;
98 | /**
99 | * Gets the Ed25519 public key of a SLIP10 private key.
100 | * @param privateKeyLocation The location of the private key. Must be the `outputLocation` of a previous call to `deriveSLIP10`.
101 | * @returns A promise resolving to the public key hex string.
102 | */
103 | getEd25519PublicKey(privateKeyLocation: Location): Promise;
104 | /**
105 | * Creates a Ed25519 signature from a private key.
106 | * @param privateKeyLocation The location of the record where the private key is stored. Must be the `outputLocation` of a previous call to `deriveSLIP10`.
107 | * @param msg The message to sign.
108 | * @returns A promise resolving to the signature hex string.
109 | */
110 | signEd25519(privateKeyLocation: Location, msg: string): Promise;
111 | }
112 | export declare class Client {
113 | path: string;
114 | name: BytesDto;
115 | constructor(path: string, name: ClientPath);
116 | /**
117 | * Get a vault by name.
118 | * @param name
119 | * @param flags
120 | * @returns
121 | */
122 | getVault(name: VaultPath): Vault;
123 | getStore(): Store;
124 | }
125 | export declare class Store {
126 | path: string;
127 | client: BytesDto;
128 | constructor(path: string, client: BytesDto);
129 | get(key: StoreKey): Promise;
130 | insert(key: StoreKey, value: number[], lifetime?: Duration): Promise;
131 | remove(key: StoreKey): Promise;
132 | }
133 | /**
134 | * A key-value storage that allows create, update and delete operations.
135 | * It does not allow reading the data, so one of the procedures must be used to manipulate
136 | * the stored data, allowing secure storage of secrets.
137 | */
138 | export declare class Vault extends ProcedureExecutor {
139 | /** The vault path. */
140 | path: string;
141 | client: BytesDto;
142 | /** The vault name. */
143 | name: BytesDto;
144 | constructor(path: string, client: ClientPath, name: VaultPath);
145 | /**
146 | * Insert a record to this vault.
147 | * @param location The record location.
148 | * @param record The record data.
149 | * @param recordHint The record hint.
150 | * @returns
151 | */
152 | insert(recordPath: RecordPath, secret: number[]): Promise;
153 | /**
154 | * Remove a record from the vault.
155 | * @param location The record location.
156 | * @param gc Whether to additionally perform the gargage collection or not.
157 | * @returns
158 | */
159 | remove(location: Location): Promise;
160 | }
161 | /**
162 | * A representation of an access to a stronghold.
163 | */
164 | export declare class Stronghold {
165 | path: string;
166 | /**
167 | * Initializes a stronghold.
168 | * If the snapshot path located at `path` exists, the password must match.
169 | * @param path
170 | * @param password
171 | */
172 | private constructor();
173 | /**
174 | * Load the snapshot if it exists (password must match), or start a fresh stronghold instance otherwise.
175 | * @param password
176 | * @returns
177 | */
178 | static load(path: string, password: string): Promise;
179 | /**
180 | * Remove this instance from the cache.
181 | */
182 | unload(): Promise;
183 | loadClient(client: ClientPath): Promise;
184 | createClient(client: ClientPath): Promise;
185 | /**
186 | * Persists the stronghold state to the snapshot.
187 | * @returns
188 | */
189 | save(): Promise;
190 | }
191 | export {};
192 |
--------------------------------------------------------------------------------
/LICENSE_APACHE-2.0:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
--------------------------------------------------------------------------------
/dist-js/index.mjs:
--------------------------------------------------------------------------------
1 | import { invoke } from '@tauri-apps/api/tauri';
2 |
3 | function toBytesDto(v) {
4 | if (typeof v === "string") {
5 | return v;
6 | }
7 | return Array.from(v instanceof ArrayBuffer ? new Uint8Array(v) : v);
8 | }
9 | class Location {
10 | constructor(type, payload) {
11 | this.type = type;
12 | this.payload = payload;
13 | }
14 | static generic(vault, record) {
15 | return new Location("Generic", {
16 | vault: toBytesDto(vault),
17 | record: toBytesDto(record),
18 | });
19 | }
20 | static counter(vault, counter) {
21 | return new Location("Counter", {
22 | vault: toBytesDto(vault),
23 | counter,
24 | });
25 | }
26 | }
27 | class ProcedureExecutor {
28 | constructor(procedureArgs) {
29 | this.procedureArgs = procedureArgs;
30 | }
31 | /**
32 | * Generate a SLIP10 seed for the given location.
33 | * @param outputLocation Location of the record where the seed will be stored.
34 | * @param sizeBytes The size in bytes of the SLIP10 seed.
35 | * @param hint The record hint.
36 | * @returns
37 | */
38 | async generateSLIP10Seed(outputLocation, sizeBytes) {
39 | return await invoke("plugin:stronghold|execute_procedure", {
40 | ...this.procedureArgs,
41 | procedure: {
42 | type: "SLIP10Generate",
43 | payload: {
44 | output: outputLocation,
45 | sizeBytes,
46 | },
47 | },
48 | }).then((n) => Uint8Array.from(n));
49 | }
50 | /**
51 | * Derive a SLIP10 private key using a seed or key.
52 | * @param chain The chain path.
53 | * @param source The source type, either 'Seed' or 'Key'.
54 | * @param sourceLocation The source location, must be the `outputLocation` of a previous call to `generateSLIP10Seed` or `deriveSLIP10`.
55 | * @param outputLocation Location of the record where the private key will be stored.
56 | * @param hint The record hint.
57 | * @returns
58 | */
59 | async deriveSLIP10(chain, source, sourceLocation, outputLocation) {
60 | return await invoke("plugin:stronghold|execute_procedure", {
61 | ...this.procedureArgs,
62 | procedure: {
63 | type: "SLIP10Derive",
64 | payload: {
65 | chain,
66 | input: {
67 | type: source,
68 | payload: sourceLocation,
69 | },
70 | output: outputLocation,
71 | },
72 | },
73 | }).then((n) => Uint8Array.from(n));
74 | }
75 | /**
76 | * Store a BIP39 mnemonic.
77 | * @param mnemonic The mnemonic string.
78 | * @param outputLocation The location of the record where the BIP39 mnemonic will be stored.
79 | * @param passphrase The optional mnemonic passphrase.
80 | * @param hint The record hint.
81 | * @returns
82 | */
83 | async recoverBIP39(mnemonic, outputLocation, passphrase) {
84 | return await invoke("plugin:stronghold|execute_procedure", {
85 | ...this.procedureArgs,
86 | procedure: {
87 | type: "BIP39Recover",
88 | payload: {
89 | mnemonic,
90 | passphrase,
91 | output: outputLocation,
92 | },
93 | },
94 | }).then((n) => Uint8Array.from(n));
95 | }
96 | /**
97 | * Generate a BIP39 seed.
98 | * @param outputLocation The location of the record where the BIP39 seed will be stored.
99 | * @param passphrase The optional mnemonic passphrase.
100 | * @param hint The record hint.
101 | * @returns
102 | */
103 | async generateBIP39(outputLocation, passphrase) {
104 | return await invoke("plugin:stronghold|execute_procedure", {
105 | ...this.procedureArgs,
106 | procedure: {
107 | type: "BIP39Generate",
108 | payload: {
109 | output: outputLocation,
110 | passphrase,
111 | },
112 | },
113 | }).then((n) => Uint8Array.from(n));
114 | }
115 | /**
116 | * Gets the Ed25519 public key of a SLIP10 private key.
117 | * @param privateKeyLocation The location of the private key. Must be the `outputLocation` of a previous call to `deriveSLIP10`.
118 | * @returns A promise resolving to the public key hex string.
119 | */
120 | async getEd25519PublicKey(privateKeyLocation) {
121 | return await invoke("plugin:stronghold|execute_procedure", {
122 | ...this.procedureArgs,
123 | procedure: {
124 | type: "PublicKey",
125 | payload: {
126 | type: "Ed25519",
127 | privateKey: privateKeyLocation,
128 | },
129 | },
130 | }).then((n) => Uint8Array.from(n));
131 | }
132 | /**
133 | * Creates a Ed25519 signature from a private key.
134 | * @param privateKeyLocation The location of the record where the private key is stored. Must be the `outputLocation` of a previous call to `deriveSLIP10`.
135 | * @param msg The message to sign.
136 | * @returns A promise resolving to the signature hex string.
137 | */
138 | async signEd25519(privateKeyLocation, msg) {
139 | return await invoke("plugin:stronghold|execute_procedure", {
140 | ...this.procedureArgs,
141 | procedure: {
142 | type: "Ed25519Sign",
143 | payload: {
144 | privateKey: privateKeyLocation,
145 | msg,
146 | },
147 | },
148 | }).then((n) => Uint8Array.from(n));
149 | }
150 | }
151 | class Client {
152 | constructor(path, name) {
153 | this.path = path;
154 | this.name = toBytesDto(name);
155 | }
156 | /**
157 | * Get a vault by name.
158 | * @param name
159 | * @param flags
160 | * @returns
161 | */
162 | getVault(name) {
163 | return new Vault(this.path, this.name, toBytesDto(name));
164 | }
165 | getStore() {
166 | return new Store(this.path, this.name);
167 | }
168 | }
169 | class Store {
170 | constructor(path, client) {
171 | this.path = path;
172 | this.client = client;
173 | }
174 | async get(key) {
175 | return await invoke("plugin:stronghold|get_store_record", {
176 | snapshotPath: this.path,
177 | client: this.client,
178 | key: toBytesDto(key),
179 | }).then((v) => {
180 | if (v) {
181 | return Uint8Array.from(v);
182 | }
183 | else {
184 | return null;
185 | }
186 | });
187 | }
188 | async insert(key, value, lifetime) {
189 | return await invoke("plugin:stronghold|save_store_record", {
190 | snapshotPath: this.path,
191 | client: this.client,
192 | key: toBytesDto(key),
193 | value,
194 | lifetime,
195 | });
196 | }
197 | async remove(key) {
198 | return await invoke("plugin:stronghold|remove_store_record", {
199 | snapshotPath: this.path,
200 | client: this.client,
201 | key: toBytesDto(key),
202 | }).then((v) => (v != null ? Uint8Array.from(v) : null));
203 | }
204 | }
205 | /**
206 | * A key-value storage that allows create, update and delete operations.
207 | * It does not allow reading the data, so one of the procedures must be used to manipulate
208 | * the stored data, allowing secure storage of secrets.
209 | */
210 | class Vault extends ProcedureExecutor {
211 | constructor(path, client, name) {
212 | super({
213 | snapshotPath: path,
214 | client,
215 | vault: name,
216 | });
217 | this.path = path;
218 | this.client = toBytesDto(client);
219 | this.name = toBytesDto(name);
220 | }
221 | /**
222 | * Insert a record to this vault.
223 | * @param location The record location.
224 | * @param record The record data.
225 | * @param recordHint The record hint.
226 | * @returns
227 | */
228 | async insert(recordPath, secret) {
229 | return await invoke("plugin:stronghold|save_secret", {
230 | snapshotPath: this.path,
231 | client: this.client,
232 | vault: this.name,
233 | recordPath: toBytesDto(recordPath),
234 | secret,
235 | });
236 | }
237 | /**
238 | * Remove a record from the vault.
239 | * @param location The record location.
240 | * @param gc Whether to additionally perform the gargage collection or not.
241 | * @returns
242 | */
243 | async remove(location) {
244 | return await invoke("plugin:stronghold|remove_secret", {
245 | snapshotPath: this.path,
246 | client: this.client,
247 | vault: this.name,
248 | recordPath: location.payload.record,
249 | });
250 | }
251 | }
252 | /**
253 | * A representation of an access to a stronghold.
254 | */
255 | class Stronghold {
256 | /**
257 | * Initializes a stronghold.
258 | * If the snapshot path located at `path` exists, the password must match.
259 | * @param path
260 | * @param password
261 | */
262 | constructor(path) {
263 | this.path = path;
264 | }
265 | /**
266 | * Load the snapshot if it exists (password must match), or start a fresh stronghold instance otherwise.
267 | * @param password
268 | * @returns
269 | */
270 | static async load(path, password) {
271 | return await invoke("plugin:stronghold|initialize", {
272 | snapshotPath: path,
273 | password,
274 | }).then(() => new Stronghold(path));
275 | }
276 | /**
277 | * Remove this instance from the cache.
278 | */
279 | async unload() {
280 | return await invoke("plugin:stronghold|destroy", {
281 | snapshotPath: this.path,
282 | });
283 | }
284 | async loadClient(client) {
285 | return await invoke("plugin:stronghold|load_client", {
286 | snapshotPath: this.path,
287 | client: toBytesDto(client),
288 | }).then(() => new Client(this.path, client));
289 | }
290 | async createClient(client) {
291 | return await invoke("plugin:stronghold|create_client", {
292 | snapshotPath: this.path,
293 | client: toBytesDto(client),
294 | }).then(() => new Client(this.path, client));
295 | }
296 | /**
297 | * Persists the stronghold state to the snapshot.
298 | * @returns
299 | */
300 | async save() {
301 | return await invoke("plugin:stronghold|save", {
302 | snapshotPath: this.path,
303 | });
304 | }
305 | }
306 |
307 | export { Client, Location, Store, Stronghold, Vault };
308 | //# sourceMappingURL=index.mjs.map
309 |
--------------------------------------------------------------------------------
/dist-js/index.min.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"index.min.js","sources":["../../../node_modules/.pnpm/@tauri-apps+api@1.6.0/node_modules/@tauri-apps/api/tauri.js","../guest-js/index.ts"],"sourcesContent":["// Copyright 2019-2023 Tauri Programme within The Commons Conservancy\n// SPDX-License-Identifier: Apache-2.0\n// SPDX-License-Identifier: MIT\n/** @ignore */\nfunction uid() {\n return window.crypto.getRandomValues(new Uint32Array(1))[0];\n}\n/**\n * Transforms a callback function to a string identifier that can be passed to the backend.\n * The backend uses the identifier to `eval()` the callback.\n *\n * @return A unique identifier associated with the callback function.\n *\n * @since 1.0.0\n */\nfunction transformCallback(callback, once = false) {\n const identifier = uid();\n const prop = `_${identifier}`;\n Object.defineProperty(window, prop, {\n value: (result) => {\n if (once) {\n Reflect.deleteProperty(window, prop);\n }\n return callback === null || callback === void 0 ? void 0 : callback(result);\n },\n writable: false,\n configurable: true\n });\n return identifier;\n}\n/**\n * Sends a message to the backend.\n * @example\n * ```typescript\n * import { invoke } from '@tauri-apps/api/tauri';\n * await invoke('login', { user: 'tauri', password: 'poiwe3h4r5ip3yrhtew9ty' });\n * ```\n *\n * @param cmd The command name.\n * @param args The optional arguments to pass to the command.\n * @return A promise resolving or rejecting to the backend response.\n *\n * @since 1.0.0\n */\nasync function invoke(cmd, args = {}) {\n return new Promise((resolve, reject) => {\n const callback = transformCallback((e) => {\n resolve(e);\n Reflect.deleteProperty(window, `_${error}`);\n }, true);\n const error = transformCallback((e) => {\n reject(e);\n Reflect.deleteProperty(window, `_${callback}`);\n }, true);\n window.__TAURI_IPC__({\n cmd,\n callback,\n error,\n ...args\n });\n });\n}\n/**\n * Convert a device file path to an URL that can be loaded by the webview.\n * Note that `asset:` and `https://asset.localhost` must be added to [`tauri.security.csp`](https://tauri.app/v1/api/config/#securityconfig.csp) in `tauri.conf.json`.\n * Example CSP value: `\"csp\": \"default-src 'self'; img-src 'self' asset: https://asset.localhost\"` to use the asset protocol on image sources.\n *\n * Additionally, `asset` must be added to [`tauri.allowlist.protocol`](https://tauri.app/v1/api/config/#allowlistconfig.protocol)\n * in `tauri.conf.json` and its access scope must be defined on the `assetScope` array on the same `protocol` object.\n * For example:\n * ```json\n * {\n * \"tauri\": {\n * \"allowlist\": {\n * \"protocol\": {\n * \"asset\": true,\n * \"assetScope\": [\"$APPDATA/assets/*\"]\n * }\n * }\n * }\n * }\n * ```\n *\n * @param filePath The file path.\n * @param protocol The protocol to use. Defaults to `asset`. You only need to set this when using a custom protocol.\n * @example\n * ```typescript\n * import { appDataDir, join } from '@tauri-apps/api/path';\n * import { convertFileSrc } from '@tauri-apps/api/tauri';\n * const appDataDirPath = await appDataDir();\n * const filePath = await join(appDataDirPath, 'assets/video.mp4');\n * const assetUrl = convertFileSrc(filePath);\n *\n * const video = document.getElementById('my-video');\n * const source = document.createElement('source');\n * source.type = 'video/mp4';\n * source.src = assetUrl;\n * video.appendChild(source);\n * video.load();\n * ```\n *\n * @return the URL that can be used as source on the webview.\n *\n * @since 1.0.0\n */\nfunction convertFileSrc(filePath, protocol = 'asset') {\n return window.__TAURI__.convertFileSrc(filePath, protocol);\n}\n\nexport { convertFileSrc, invoke, transformCallback };\n",null],"names":[],"mappings":"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","x_google_ignoreList":[0]}
--------------------------------------------------------------------------------
/dist-js/index.min.js:
--------------------------------------------------------------------------------
1 | // Copyright 2019-2023 Tauri Programme within The Commons Conservancy
2 | // SPDX-License-Identifier: Apache-2.0
3 | // SPDX-License-Identifier: MIT
4 | /** @ignore */
5 | function uid() {
6 | return window.crypto.getRandomValues(new Uint32Array(1))[0];
7 | }
8 | /**
9 | * Transforms a callback function to a string identifier that can be passed to the backend.
10 | * The backend uses the identifier to `eval()` the callback.
11 | *
12 | * @return A unique identifier associated with the callback function.
13 | *
14 | * @since 1.0.0
15 | */
16 | function transformCallback(callback, once = false) {
17 | const identifier = uid();
18 | const prop = `_${identifier}`;
19 | Object.defineProperty(window, prop, {
20 | value: (result) => {
21 | if (once) {
22 | Reflect.deleteProperty(window, prop);
23 | }
24 | return callback === null || callback === void 0 ? void 0 : callback(result);
25 | },
26 | writable: false,
27 | configurable: true
28 | });
29 | return identifier;
30 | }
31 | /**
32 | * Sends a message to the backend.
33 | * @example
34 | * ```typescript
35 | * import { invoke } from '@tauri-apps/api/tauri';
36 | * await invoke('login', { user: 'tauri', password: 'poiwe3h4r5ip3yrhtew9ty' });
37 | * ```
38 | *
39 | * @param cmd The command name.
40 | * @param args The optional arguments to pass to the command.
41 | * @return A promise resolving or rejecting to the backend response.
42 | *
43 | * @since 1.0.0
44 | */
45 | async function invoke(cmd, args = {}) {
46 | return new Promise((resolve, reject) => {
47 | const callback = transformCallback((e) => {
48 | resolve(e);
49 | Reflect.deleteProperty(window, `_${error}`);
50 | }, true);
51 | const error = transformCallback((e) => {
52 | reject(e);
53 | Reflect.deleteProperty(window, `_${callback}`);
54 | }, true);
55 | window.__TAURI_IPC__({
56 | cmd,
57 | callback,
58 | error,
59 | ...args
60 | });
61 | });
62 | }
63 |
64 | function toBytesDto(v) {
65 | if (typeof v === "string") {
66 | return v;
67 | }
68 | return Array.from(v instanceof ArrayBuffer ? new Uint8Array(v) : v);
69 | }
70 | class Location {
71 | constructor(type, payload) {
72 | this.type = type;
73 | this.payload = payload;
74 | }
75 | static generic(vault, record) {
76 | return new Location("Generic", {
77 | vault: toBytesDto(vault),
78 | record: toBytesDto(record),
79 | });
80 | }
81 | static counter(vault, counter) {
82 | return new Location("Counter", {
83 | vault: toBytesDto(vault),
84 | counter,
85 | });
86 | }
87 | }
88 | class ProcedureExecutor {
89 | constructor(procedureArgs) {
90 | this.procedureArgs = procedureArgs;
91 | }
92 | /**
93 | * Generate a SLIP10 seed for the given location.
94 | * @param outputLocation Location of the record where the seed will be stored.
95 | * @param sizeBytes The size in bytes of the SLIP10 seed.
96 | * @param hint The record hint.
97 | * @returns
98 | */
99 | async generateSLIP10Seed(outputLocation, sizeBytes) {
100 | return await invoke("plugin:stronghold|execute_procedure", {
101 | ...this.procedureArgs,
102 | procedure: {
103 | type: "SLIP10Generate",
104 | payload: {
105 | output: outputLocation,
106 | sizeBytes,
107 | },
108 | },
109 | }).then((n) => Uint8Array.from(n));
110 | }
111 | /**
112 | * Derive a SLIP10 private key using a seed or key.
113 | * @param chain The chain path.
114 | * @param source The source type, either 'Seed' or 'Key'.
115 | * @param sourceLocation The source location, must be the `outputLocation` of a previous call to `generateSLIP10Seed` or `deriveSLIP10`.
116 | * @param outputLocation Location of the record where the private key will be stored.
117 | * @param hint The record hint.
118 | * @returns
119 | */
120 | async deriveSLIP10(chain, source, sourceLocation, outputLocation) {
121 | return await invoke("plugin:stronghold|execute_procedure", {
122 | ...this.procedureArgs,
123 | procedure: {
124 | type: "SLIP10Derive",
125 | payload: {
126 | chain,
127 | input: {
128 | type: source,
129 | payload: sourceLocation,
130 | },
131 | output: outputLocation,
132 | },
133 | },
134 | }).then((n) => Uint8Array.from(n));
135 | }
136 | /**
137 | * Store a BIP39 mnemonic.
138 | * @param mnemonic The mnemonic string.
139 | * @param outputLocation The location of the record where the BIP39 mnemonic will be stored.
140 | * @param passphrase The optional mnemonic passphrase.
141 | * @param hint The record hint.
142 | * @returns
143 | */
144 | async recoverBIP39(mnemonic, outputLocation, passphrase) {
145 | return await invoke("plugin:stronghold|execute_procedure", {
146 | ...this.procedureArgs,
147 | procedure: {
148 | type: "BIP39Recover",
149 | payload: {
150 | mnemonic,
151 | passphrase,
152 | output: outputLocation,
153 | },
154 | },
155 | }).then((n) => Uint8Array.from(n));
156 | }
157 | /**
158 | * Generate a BIP39 seed.
159 | * @param outputLocation The location of the record where the BIP39 seed will be stored.
160 | * @param passphrase The optional mnemonic passphrase.
161 | * @param hint The record hint.
162 | * @returns
163 | */
164 | async generateBIP39(outputLocation, passphrase) {
165 | return await invoke("plugin:stronghold|execute_procedure", {
166 | ...this.procedureArgs,
167 | procedure: {
168 | type: "BIP39Generate",
169 | payload: {
170 | output: outputLocation,
171 | passphrase,
172 | },
173 | },
174 | }).then((n) => Uint8Array.from(n));
175 | }
176 | /**
177 | * Gets the Ed25519 public key of a SLIP10 private key.
178 | * @param privateKeyLocation The location of the private key. Must be the `outputLocation` of a previous call to `deriveSLIP10`.
179 | * @returns A promise resolving to the public key hex string.
180 | */
181 | async getEd25519PublicKey(privateKeyLocation) {
182 | return await invoke("plugin:stronghold|execute_procedure", {
183 | ...this.procedureArgs,
184 | procedure: {
185 | type: "PublicKey",
186 | payload: {
187 | type: "Ed25519",
188 | privateKey: privateKeyLocation,
189 | },
190 | },
191 | }).then((n) => Uint8Array.from(n));
192 | }
193 | /**
194 | * Creates a Ed25519 signature from a private key.
195 | * @param privateKeyLocation The location of the record where the private key is stored. Must be the `outputLocation` of a previous call to `deriveSLIP10`.
196 | * @param msg The message to sign.
197 | * @returns A promise resolving to the signature hex string.
198 | */
199 | async signEd25519(privateKeyLocation, msg) {
200 | return await invoke("plugin:stronghold|execute_procedure", {
201 | ...this.procedureArgs,
202 | procedure: {
203 | type: "Ed25519Sign",
204 | payload: {
205 | privateKey: privateKeyLocation,
206 | msg,
207 | },
208 | },
209 | }).then((n) => Uint8Array.from(n));
210 | }
211 | }
212 | class Client {
213 | constructor(path, name) {
214 | this.path = path;
215 | this.name = toBytesDto(name);
216 | }
217 | /**
218 | * Get a vault by name.
219 | * @param name
220 | * @param flags
221 | * @returns
222 | */
223 | getVault(name) {
224 | return new Vault(this.path, this.name, toBytesDto(name));
225 | }
226 | getStore() {
227 | return new Store(this.path, this.name);
228 | }
229 | }
230 | class Store {
231 | constructor(path, client) {
232 | this.path = path;
233 | this.client = client;
234 | }
235 | async get(key) {
236 | return await invoke("plugin:stronghold|get_store_record", {
237 | snapshotPath: this.path,
238 | client: this.client,
239 | key: toBytesDto(key),
240 | }).then((v) => {
241 | if (v) {
242 | return Uint8Array.from(v);
243 | }
244 | else {
245 | return null;
246 | }
247 | });
248 | }
249 | async insert(key, value, lifetime) {
250 | return await invoke("plugin:stronghold|save_store_record", {
251 | snapshotPath: this.path,
252 | client: this.client,
253 | key: toBytesDto(key),
254 | value,
255 | lifetime,
256 | });
257 | }
258 | async remove(key) {
259 | return await invoke("plugin:stronghold|remove_store_record", {
260 | snapshotPath: this.path,
261 | client: this.client,
262 | key: toBytesDto(key),
263 | }).then((v) => (v != null ? Uint8Array.from(v) : null));
264 | }
265 | }
266 | /**
267 | * A key-value storage that allows create, update and delete operations.
268 | * It does not allow reading the data, so one of the procedures must be used to manipulate
269 | * the stored data, allowing secure storage of secrets.
270 | */
271 | class Vault extends ProcedureExecutor {
272 | constructor(path, client, name) {
273 | super({
274 | snapshotPath: path,
275 | client,
276 | vault: name,
277 | });
278 | this.path = path;
279 | this.client = toBytesDto(client);
280 | this.name = toBytesDto(name);
281 | }
282 | /**
283 | * Insert a record to this vault.
284 | * @param location The record location.
285 | * @param record The record data.
286 | * @param recordHint The record hint.
287 | * @returns
288 | */
289 | async insert(recordPath, secret) {
290 | return await invoke("plugin:stronghold|save_secret", {
291 | snapshotPath: this.path,
292 | client: this.client,
293 | vault: this.name,
294 | recordPath: toBytesDto(recordPath),
295 | secret,
296 | });
297 | }
298 | /**
299 | * Remove a record from the vault.
300 | * @param location The record location.
301 | * @param gc Whether to additionally perform the gargage collection or not.
302 | * @returns
303 | */
304 | async remove(location) {
305 | return await invoke("plugin:stronghold|remove_secret", {
306 | snapshotPath: this.path,
307 | client: this.client,
308 | vault: this.name,
309 | recordPath: location.payload.record,
310 | });
311 | }
312 | }
313 | /**
314 | * A representation of an access to a stronghold.
315 | */
316 | class Stronghold {
317 | /**
318 | * Initializes a stronghold.
319 | * If the snapshot path located at `path` exists, the password must match.
320 | * @param path
321 | * @param password
322 | */
323 | constructor(path) {
324 | this.path = path;
325 | }
326 | /**
327 | * Load the snapshot if it exists (password must match), or start a fresh stronghold instance otherwise.
328 | * @param password
329 | * @returns
330 | */
331 | static async load(path, password) {
332 | return await invoke("plugin:stronghold|initialize", {
333 | snapshotPath: path,
334 | password,
335 | }).then(() => new Stronghold(path));
336 | }
337 | /**
338 | * Remove this instance from the cache.
339 | */
340 | async unload() {
341 | return await invoke("plugin:stronghold|destroy", {
342 | snapshotPath: this.path,
343 | });
344 | }
345 | async loadClient(client) {
346 | return await invoke("plugin:stronghold|load_client", {
347 | snapshotPath: this.path,
348 | client: toBytesDto(client),
349 | }).then(() => new Client(this.path, client));
350 | }
351 | async createClient(client) {
352 | return await invoke("plugin:stronghold|create_client", {
353 | snapshotPath: this.path,
354 | client: toBytesDto(client),
355 | }).then(() => new Client(this.path, client));
356 | }
357 | /**
358 | * Persists the stronghold state to the snapshot.
359 | * @returns
360 | */
361 | async save() {
362 | return await invoke("plugin:stronghold|save", {
363 | snapshotPath: this.path,
364 | });
365 | }
366 | }
367 |
368 | export { Client, Location, Store, Stronghold, Vault };
369 | //# sourceMappingURL=index.min.js.map
370 |
--------------------------------------------------------------------------------
/guest-js/index.ts:
--------------------------------------------------------------------------------
1 | import { invoke } from "@tauri-apps/api/tauri";
2 |
3 | type BytesDto = string | number[];
4 | export type ClientPath =
5 | | string
6 | | Iterable
7 | | ArrayLike
8 | | ArrayBuffer;
9 | export type VaultPath =
10 | | string
11 | | Iterable
12 | | ArrayLike
13 | | ArrayBuffer;
14 | export type RecordPath =
15 | | string
16 | | Iterable
17 | | ArrayLike
18 | | ArrayBuffer;
19 | export type StoreKey =
20 | | string
21 | | Iterable
22 | | ArrayLike
23 | | ArrayBuffer;
24 |
25 | function toBytesDto(
26 | v: ClientPath | VaultPath | RecordPath | StoreKey,
27 | ): string | number[] {
28 | if (typeof v === "string") {
29 | return v;
30 | }
31 | return Array.from(v instanceof ArrayBuffer ? new Uint8Array(v) : v);
32 | }
33 |
34 | export interface ConnectionLimits {
35 | maxPendingIncoming?: number;
36 | maxPendingOutgoing?: number;
37 | maxEstablishedIncoming?: number;
38 | maxEstablishedOutgoing?: number;
39 | maxEstablishedPerPeer?: number;
40 | maxEstablishedTotal?: number;
41 | }
42 |
43 | export interface PeerAddress {
44 | known: string[]; // multiaddr
45 | use_relay_fallback: boolean;
46 | }
47 |
48 | export interface AddressInfo {
49 | peers: Map;
50 | relays: string[]; // peers
51 | }
52 |
53 | export interface ClientAccess {
54 | useVaultDefault?: boolean;
55 | useVaultExceptions?: Map;
56 | writeVaultDefault?: boolean;
57 | writeVaultExceptions?: Map;
58 | cloneVaultDefault?: boolean;
59 | cloneVaultExceptions?: Map;
60 | readStore?: boolean;
61 | writeStore?: boolean;
62 | }
63 |
64 | export interface Permissions {
65 | default?: ClientAccess;
66 | exceptions?: Map;
67 | }
68 |
69 | export interface NetworkConfig {
70 | requestTimeout?: Duration;
71 | connectionTimeout?: Duration;
72 | connectionsLimit?: ConnectionLimits;
73 | enableMdns?: boolean;
74 | enableRelay?: boolean;
75 | addresses?: AddressInfo;
76 | peerPermissions?: Map;
77 | permissionsDefault?: Permissions;
78 | }
79 |
80 | /** A duration definition. */
81 | export interface Duration {
82 | /** The number of whole seconds contained by this Duration. */
83 | secs: number;
84 | /** The fractional part of this Duration, in nanoseconds. Must be greater or equal to 0 and smaller than 1e+9 (the max number of nanoseoncds in a second)*/
85 | nanos: number;
86 | }
87 |
88 | export class Location {
89 | type: string;
90 | payload: Record;
91 |
92 | constructor(type: string, payload: Record) {
93 | this.type = type;
94 | this.payload = payload;
95 | }
96 |
97 | static generic(vault: VaultPath, record: RecordPath): Location {
98 | return new Location("Generic", {
99 | vault: toBytesDto(vault),
100 | record: toBytesDto(record),
101 | });
102 | }
103 |
104 | static counter(vault: VaultPath, counter: number): Location {
105 | return new Location("Counter", {
106 | vault: toBytesDto(vault),
107 | counter,
108 | });
109 | }
110 | }
111 |
112 | class ProcedureExecutor {
113 | procedureArgs: Record;
114 |
115 | constructor(procedureArgs: Record) {
116 | this.procedureArgs = procedureArgs;
117 | }
118 |
119 | /**
120 | * Generate a SLIP10 seed for the given location.
121 | * @param outputLocation Location of the record where the seed will be stored.
122 | * @param sizeBytes The size in bytes of the SLIP10 seed.
123 | * @param hint The record hint.
124 | * @returns
125 | */
126 | async generateSLIP10Seed(
127 | outputLocation: Location,
128 | sizeBytes?: number,
129 | ): Promise {
130 | return await invoke("plugin:stronghold|execute_procedure", {
131 | ...this.procedureArgs,
132 | procedure: {
133 | type: "SLIP10Generate",
134 | payload: {
135 | output: outputLocation,
136 | sizeBytes,
137 | },
138 | },
139 | }).then((n) => Uint8Array.from(n));
140 | }
141 |
142 | /**
143 | * Derive a SLIP10 private key using a seed or key.
144 | * @param chain The chain path.
145 | * @param source The source type, either 'Seed' or 'Key'.
146 | * @param sourceLocation The source location, must be the `outputLocation` of a previous call to `generateSLIP10Seed` or `deriveSLIP10`.
147 | * @param outputLocation Location of the record where the private key will be stored.
148 | * @param hint The record hint.
149 | * @returns
150 | */
151 | async deriveSLIP10(
152 | chain: number[],
153 | source: "Seed" | "Key",
154 | sourceLocation: Location,
155 | outputLocation: Location,
156 | ): Promise {
157 | return await invoke("plugin:stronghold|execute_procedure", {
158 | ...this.procedureArgs,
159 | procedure: {
160 | type: "SLIP10Derive",
161 | payload: {
162 | chain,
163 | input: {
164 | type: source,
165 | payload: sourceLocation,
166 | },
167 | output: outputLocation,
168 | },
169 | },
170 | }).then((n) => Uint8Array.from(n));
171 | }
172 |
173 | /**
174 | * Store a BIP39 mnemonic.
175 | * @param mnemonic The mnemonic string.
176 | * @param outputLocation The location of the record where the BIP39 mnemonic will be stored.
177 | * @param passphrase The optional mnemonic passphrase.
178 | * @param hint The record hint.
179 | * @returns
180 | */
181 | async recoverBIP39(
182 | mnemonic: string,
183 | outputLocation: Location,
184 | passphrase?: string,
185 | ): Promise {
186 | return await invoke("plugin:stronghold|execute_procedure", {
187 | ...this.procedureArgs,
188 | procedure: {
189 | type: "BIP39Recover",
190 | payload: {
191 | mnemonic,
192 | passphrase,
193 | output: outputLocation,
194 | },
195 | },
196 | }).then((n) => Uint8Array.from(n));
197 | }
198 |
199 | /**
200 | * Generate a BIP39 seed.
201 | * @param outputLocation The location of the record where the BIP39 seed will be stored.
202 | * @param passphrase The optional mnemonic passphrase.
203 | * @param hint The record hint.
204 | * @returns
205 | */
206 | async generateBIP39(
207 | outputLocation: Location,
208 | passphrase?: string,
209 | ): Promise {
210 | return await invoke("plugin:stronghold|execute_procedure", {
211 | ...this.procedureArgs,
212 | procedure: {
213 | type: "BIP39Generate",
214 | payload: {
215 | output: outputLocation,
216 | passphrase,
217 | },
218 | },
219 | }).then((n) => Uint8Array.from(n));
220 | }
221 |
222 | /**
223 | * Gets the Ed25519 public key of a SLIP10 private key.
224 | * @param privateKeyLocation The location of the private key. Must be the `outputLocation` of a previous call to `deriveSLIP10`.
225 | * @returns A promise resolving to the public key hex string.
226 | */
227 | async getEd25519PublicKey(privateKeyLocation: Location): Promise {
228 | return await invoke("plugin:stronghold|execute_procedure", {
229 | ...this.procedureArgs,
230 | procedure: {
231 | type: "PublicKey",
232 | payload: {
233 | type: "Ed25519",
234 | privateKey: privateKeyLocation,
235 | },
236 | },
237 | }).then((n) => Uint8Array.from(n));
238 | }
239 |
240 | /**
241 | * Creates a Ed25519 signature from a private key.
242 | * @param privateKeyLocation The location of the record where the private key is stored. Must be the `outputLocation` of a previous call to `deriveSLIP10`.
243 | * @param msg The message to sign.
244 | * @returns A promise resolving to the signature hex string.
245 | */
246 | async signEd25519(
247 | privateKeyLocation: Location,
248 | msg: string,
249 | ): Promise {
250 | return await invoke("plugin:stronghold|execute_procedure", {
251 | ...this.procedureArgs,
252 | procedure: {
253 | type: "Ed25519Sign",
254 | payload: {
255 | privateKey: privateKeyLocation,
256 | msg,
257 | },
258 | },
259 | }).then((n) => Uint8Array.from(n));
260 | }
261 | }
262 |
263 | export class Client {
264 | path: string;
265 | name: BytesDto;
266 |
267 | constructor(path: string, name: ClientPath) {
268 | this.path = path;
269 | this.name = toBytesDto(name);
270 | }
271 |
272 | /**
273 | * Get a vault by name.
274 | * @param name
275 | * @param flags
276 | * @returns
277 | */
278 | getVault(name: VaultPath): Vault {
279 | return new Vault(this.path, this.name, toBytesDto(name));
280 | }
281 |
282 | getStore(): Store {
283 | return new Store(this.path, this.name);
284 | }
285 | }
286 |
287 | export class Store {
288 | path: string;
289 | client: BytesDto;
290 |
291 | constructor(path: string, client: BytesDto) {
292 | this.path = path;
293 | this.client = client;
294 | }
295 |
296 | async get(key: StoreKey): Promise {
297 | return await invoke("plugin:stronghold|get_store_record", {
298 | snapshotPath: this.path,
299 | client: this.client,
300 | key: toBytesDto(key),
301 | }).then((v) => {
302 | if (v) {
303 | return Uint8Array.from(v);
304 | } else {
305 | return null;
306 | }
307 | });
308 | }
309 |
310 | async insert(
311 | key: StoreKey,
312 | value: number[],
313 | lifetime?: Duration,
314 | ): Promise {
315 | return await invoke("plugin:stronghold|save_store_record", {
316 | snapshotPath: this.path,
317 | client: this.client,
318 | key: toBytesDto(key),
319 | value,
320 | lifetime,
321 | });
322 | }
323 |
324 | async remove(key: StoreKey): Promise {
325 | return await invoke(
326 | "plugin:stronghold|remove_store_record",
327 | {
328 | snapshotPath: this.path,
329 | client: this.client,
330 | key: toBytesDto(key),
331 | },
332 | ).then((v) => (v != null ? Uint8Array.from(v) : null));
333 | }
334 | }
335 |
336 | /**
337 | * A key-value storage that allows create, update and delete operations.
338 | * It does not allow reading the data, so one of the procedures must be used to manipulate
339 | * the stored data, allowing secure storage of secrets.
340 | */
341 | export class Vault extends ProcedureExecutor {
342 | /** The vault path. */
343 | path: string;
344 | client: BytesDto;
345 | /** The vault name. */
346 | name: BytesDto;
347 |
348 | constructor(path: string, client: ClientPath, name: VaultPath) {
349 | super({
350 | snapshotPath: path,
351 | client,
352 | vault: name,
353 | });
354 | this.path = path;
355 | this.client = toBytesDto(client);
356 | this.name = toBytesDto(name);
357 | }
358 |
359 | /**
360 | * Insert a record to this vault.
361 | * @param location The record location.
362 | * @param record The record data.
363 | * @param recordHint The record hint.
364 | * @returns
365 | */
366 | async insert(recordPath: RecordPath, secret: number[]): Promise {
367 | return await invoke("plugin:stronghold|save_secret", {
368 | snapshotPath: this.path,
369 | client: this.client,
370 | vault: this.name,
371 | recordPath: toBytesDto(recordPath),
372 | secret,
373 | });
374 | }
375 |
376 | /**
377 | * Remove a record from the vault.
378 | * @param location The record location.
379 | * @param gc Whether to additionally perform the gargage collection or not.
380 | * @returns
381 | */
382 | async remove(location: Location): Promise {
383 | return await invoke("plugin:stronghold|remove_secret", {
384 | snapshotPath: this.path,
385 | client: this.client,
386 | vault: this.name,
387 | recordPath: location.payload.record,
388 | });
389 | }
390 | }
391 |
392 | /**
393 | * A representation of an access to a stronghold.
394 | */
395 | export class Stronghold {
396 | path: string;
397 |
398 | /**
399 | * Initializes a stronghold.
400 | * If the snapshot path located at `path` exists, the password must match.
401 | * @param path
402 | * @param password
403 | */
404 | private constructor(path: string) {
405 | this.path = path;
406 | }
407 |
408 | /**
409 | * Load the snapshot if it exists (password must match), or start a fresh stronghold instance otherwise.
410 | * @param password
411 | * @returns
412 | */
413 | static async load(path: string, password: string): Promise {
414 | return await invoke("plugin:stronghold|initialize", {
415 | snapshotPath: path,
416 | password,
417 | }).then(() => new Stronghold(path));
418 | }
419 |
420 | /**
421 | * Remove this instance from the cache.
422 | */
423 | async unload(): Promise {
424 | return await invoke("plugin:stronghold|destroy", {
425 | snapshotPath: this.path,
426 | });
427 | }
428 |
429 | async loadClient(client: ClientPath): Promise {
430 | return await invoke("plugin:stronghold|load_client", {
431 | snapshotPath: this.path,
432 | client: toBytesDto(client),
433 | }).then(() => new Client(this.path, client));
434 | }
435 |
436 | async createClient(client: ClientPath): Promise {
437 | return await invoke("plugin:stronghold|create_client", {
438 | snapshotPath: this.path,
439 | client: toBytesDto(client),
440 | }).then(() => new Client(this.path, client));
441 | }
442 |
443 | /**
444 | * Persists the stronghold state to the snapshot.
445 | * @returns
446 | */
447 | async save(): Promise {
448 | return await invoke("plugin:stronghold|save", {
449 | snapshotPath: this.path,
450 | });
451 | }
452 | }
453 |
--------------------------------------------------------------------------------
/src/lib.rs:
--------------------------------------------------------------------------------
1 | use std::{
2 | collections::HashMap,
3 | fmt,
4 | path::PathBuf,
5 | sync::{Arc, Mutex},
6 | time::Duration,
7 | };
8 |
9 | use iota_stronghold::{
10 | procedures::{
11 | BIP39Generate, BIP39Recover, Chain, Ed25519Sign, KeyType as StrongholdKeyType,
12 | MnemonicLanguage, PublicKey, Slip10Derive, Slip10DeriveInput, Slip10Generate,
13 | StrongholdProcedure,
14 | },
15 | Client, Location,
16 | };
17 | use serde::{de::Visitor, Deserialize, Deserializer};
18 | use stronghold::{Error, Result, Stronghold};
19 | use tauri::{
20 | plugin::{Builder as PluginBuilder, TauriPlugin},
21 | Manager, Runtime, State,
22 | };
23 | use zeroize::Zeroize;
24 |
25 | #[cfg(feature = "kdf")]
26 | pub mod kdf;
27 |
28 | pub mod stronghold;
29 |
30 | type PasswordHashFn = dyn Fn(&str) -> Vec + Send + Sync;
31 |
32 | #[derive(Default)]
33 | struct StrongholdCollection(Arc>>);
34 |
35 | struct PasswordHashFunction(Box);
36 |
37 | #[derive(Deserialize, Hash, Eq, PartialEq, Ord, PartialOrd)]
38 | #[serde(untagged)]
39 | enum BytesDto {
40 | Text(String),
41 | Raw(Vec),
42 | }
43 |
44 | impl AsRef<[u8]> for BytesDto {
45 | fn as_ref(&self) -> &[u8] {
46 | match self {
47 | Self::Text(t) => t.as_ref(),
48 | Self::Raw(b) => b.as_ref(),
49 | }
50 | }
51 | }
52 |
53 | impl From for Vec {
54 | fn from(v: BytesDto) -> Self {
55 | match v {
56 | BytesDto::Text(t) => t.as_bytes().to_vec(),
57 | BytesDto::Raw(b) => b,
58 | }
59 | }
60 | }
61 |
62 | #[derive(Deserialize)]
63 | #[serde(tag = "type", content = "payload")]
64 | enum LocationDto {
65 | Generic { vault: BytesDto, record: BytesDto },
66 | Counter { vault: BytesDto, counter: usize },
67 | }
68 |
69 | impl From for Location {
70 | fn from(dto: LocationDto) -> Location {
71 | match dto {
72 | LocationDto::Generic { vault, record } => Location::generic(vault, record),
73 | LocationDto::Counter { vault, counter } => Location::counter(vault, counter),
74 | }
75 | }
76 | }
77 |
78 | #[derive(Deserialize)]
79 | #[serde(tag = "type", content = "payload")]
80 | #[allow(clippy::upper_case_acronyms)]
81 | enum Slip10DeriveInputDto {
82 | Seed(LocationDto),
83 | Key(LocationDto),
84 | }
85 |
86 | impl From for Slip10DeriveInput {
87 | fn from(dto: Slip10DeriveInputDto) -> Slip10DeriveInput {
88 | match dto {
89 | Slip10DeriveInputDto::Seed(location) => Slip10DeriveInput::Seed(location.into()),
90 | Slip10DeriveInputDto::Key(location) => Slip10DeriveInput::Key(location.into()),
91 | }
92 | }
93 | }
94 |
95 | pub enum KeyType {
96 | Ed25519,
97 | X25519,
98 | }
99 |
100 | impl From for StrongholdKeyType {
101 | fn from(ty: KeyType) -> StrongholdKeyType {
102 | match ty {
103 | KeyType::Ed25519 => StrongholdKeyType::Ed25519,
104 | KeyType::X25519 => StrongholdKeyType::X25519,
105 | }
106 | }
107 | }
108 |
109 | impl<'de> Deserialize<'de> for KeyType {
110 | fn deserialize(deserializer: D) -> std::result::Result
111 | where
112 | D: Deserializer<'de>,
113 | {
114 | struct KeyTypeVisitor;
115 |
116 | impl Visitor<'_> for KeyTypeVisitor {
117 | type Value = KeyType;
118 |
119 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
120 | formatter.write_str("ed25519 or x25519")
121 | }
122 |
123 | fn visit_str(self, value: &str) -> std::result::Result
124 | where
125 | E: serde::de::Error,
126 | {
127 | match value.to_lowercase().as_str() {
128 | "ed25519" => Ok(KeyType::Ed25519),
129 | "x25519" => Ok(KeyType::X25519),
130 | _ => Err(serde::de::Error::custom("unknown key type")),
131 | }
132 | }
133 | }
134 |
135 | deserializer.deserialize_str(KeyTypeVisitor)
136 | }
137 | }
138 |
139 | #[derive(Deserialize)]
140 | #[serde(tag = "type", content = "payload")]
141 | #[allow(clippy::upper_case_acronyms)]
142 | enum ProcedureDto {
143 | SLIP10Generate {
144 | output: LocationDto,
145 | #[serde(rename = "sizeBytes")]
146 | size_bytes: Option,
147 | },
148 | SLIP10Derive {
149 | chain: Vec,
150 | input: Slip10DeriveInputDto,
151 | output: LocationDto,
152 | },
153 | BIP39Recover {
154 | mnemonic: String,
155 | passphrase: Option,
156 | output: LocationDto,
157 | },
158 | BIP39Generate {
159 | passphrase: Option,
160 | output: LocationDto,
161 | },
162 | PublicKey {
163 | #[serde(rename = "type")]
164 | ty: KeyType,
165 | #[serde(rename = "privateKey")]
166 | private_key: LocationDto,
167 | },
168 | Ed25519Sign {
169 | #[serde(rename = "privateKey")]
170 | private_key: LocationDto,
171 | msg: String,
172 | },
173 | }
174 |
175 | impl From for StrongholdProcedure {
176 | fn from(dto: ProcedureDto) -> StrongholdProcedure {
177 | match dto {
178 | ProcedureDto::SLIP10Generate { output, size_bytes } => {
179 | StrongholdProcedure::Slip10Generate(Slip10Generate {
180 | output: output.into(),
181 | size_bytes,
182 | })
183 | }
184 | ProcedureDto::SLIP10Derive {
185 | chain,
186 | input,
187 | output,
188 | } => StrongholdProcedure::Slip10Derive(Slip10Derive {
189 | chain: Chain::from_u32_hardened(chain),
190 | input: input.into(),
191 | output: output.into(),
192 | }),
193 | ProcedureDto::BIP39Recover {
194 | mnemonic,
195 | passphrase,
196 | output,
197 | } => StrongholdProcedure::BIP39Recover(BIP39Recover {
198 | mnemonic,
199 | passphrase,
200 | output: output.into(),
201 | }),
202 | ProcedureDto::BIP39Generate { passphrase, output } => {
203 | StrongholdProcedure::BIP39Generate(BIP39Generate {
204 | passphrase,
205 | output: output.into(),
206 | language: MnemonicLanguage::English,
207 | })
208 | }
209 | ProcedureDto::PublicKey { ty, private_key } => {
210 | StrongholdProcedure::PublicKey(PublicKey {
211 | ty: ty.into(),
212 | private_key: private_key.into(),
213 | })
214 | }
215 | ProcedureDto::Ed25519Sign { private_key, msg } => {
216 | StrongholdProcedure::Ed25519Sign(Ed25519Sign {
217 | private_key: private_key.into(),
218 | msg: msg.as_bytes().to_vec(),
219 | })
220 | }
221 | }
222 | }
223 | }
224 |
225 | #[tauri::command]
226 | async fn initialize(
227 | collection: State<'_, StrongholdCollection>,
228 | hash_function: State<'_, PasswordHashFunction>,
229 | snapshot_path: PathBuf,
230 | mut password: String,
231 | ) -> Result<()> {
232 | let hash = (hash_function.0)(&password);
233 | password.zeroize();
234 | let stronghold = Stronghold::new(snapshot_path.clone(), hash)?;
235 |
236 | collection
237 | .0
238 | .lock()
239 | .unwrap()
240 | .insert(snapshot_path, stronghold);
241 |
242 | Ok(())
243 | }
244 |
245 | #[tauri::command]
246 | async fn destroy(
247 | collection: State<'_, StrongholdCollection>,
248 | snapshot_path: PathBuf,
249 | ) -> Result<()> {
250 | let mut collection = collection.0.lock().unwrap();
251 | if let Some(stronghold) = collection.remove(&snapshot_path) {
252 | if let Err(e) = stronghold.save() {
253 | collection.insert(snapshot_path, stronghold);
254 | return Err(e);
255 | }
256 | }
257 | Ok(())
258 | }
259 |
260 | #[tauri::command]
261 | async fn save(collection: State<'_, StrongholdCollection>, snapshot_path: PathBuf) -> Result<()> {
262 | let collection = collection.0.lock().unwrap();
263 | if let Some(stronghold) = collection.get(&snapshot_path) {
264 | stronghold.save()?;
265 | }
266 | Ok(())
267 | }
268 |
269 | #[tauri::command]
270 | async fn create_client(
271 | collection: State<'_, StrongholdCollection>,
272 | snapshot_path: PathBuf,
273 | client: BytesDto,
274 | ) -> Result<()> {
275 | let stronghold = get_stronghold(collection, snapshot_path)?;
276 | stronghold.create_client(client)?;
277 | Ok(())
278 | }
279 |
280 | #[tauri::command]
281 | async fn load_client(
282 | collection: State<'_, StrongholdCollection>,
283 | snapshot_path: PathBuf,
284 | client: BytesDto,
285 | ) -> Result<()> {
286 | let stronghold = get_stronghold(collection, snapshot_path)?;
287 | stronghold.load_client(client)?;
288 | Ok(())
289 | }
290 |
291 | #[tauri::command]
292 | async fn get_store_record(
293 | collection: State<'_, StrongholdCollection>,
294 | snapshot_path: PathBuf,
295 | client: BytesDto,
296 | key: String,
297 | ) -> Result