diff --git a/Cargo.lock b/Cargo.lock
index f06cbed1d..725147b81 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -237,7 +237,6 @@ dependencies = [
"tauri-plugin-shell",
"tauri-plugin-sql",
"tauri-plugin-store",
- "tauri-plugin-stronghold",
"tauri-plugin-updater",
"tauri-plugin-upload",
"tauri-plugin-websocket",
diff --git a/Cargo.toml b/Cargo.toml
index 23e38cbac..d297e4f71 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -32,23 +32,6 @@ license = "Apache-2.0 OR MIT"
rust-version = "1.77.2"
repository = "https://github.com/tauri-apps/plugins-workspace"
-# Stronghold encrypts its snapshots with scrypt: unoptimized, saving or loading one
-# takes ~45s instead of ~1s, which stalls the (debug) example app and its e2e suite.
-[profile.dev.package.scrypt]
-opt-level = 3
-[profile.dev.package.salsa20]
-opt-level = 3
-[profile.dev.package.pbkdf2]
-opt-level = 3
-[profile.dev.package.sha2]
-opt-level = 3
-[profile.dev.package.hmac]
-opt-level = 3
-[profile.dev.package.iota-crypto]
-opt-level = 3
-[profile.dev.package.rust-argon2]
-opt-level = 3
-
# default to small, optimized release binaries
[profile.release]
panic = "abort"
diff --git a/examples/api/package.json b/examples/api/package.json
index c788209c6..193e1f152 100644
--- a/examples/api/package.json
+++ b/examples/api/package.json
@@ -32,7 +32,6 @@
"@tauri-apps/plugin-shell": "workspace:*",
"@tauri-apps/plugin-sql": "workspace:*",
"@tauri-apps/plugin-store": "workspace:*",
- "@tauri-apps/plugin-stronghold": "workspace:*",
"@tauri-apps/plugin-updater": "workspace:*",
"@tauri-apps/plugin-upload": "workspace:*",
"@tauri-apps/plugin-websocket": "workspace:*",
diff --git a/examples/api/src-tauri/Cargo.toml b/examples/api/src-tauri/Cargo.toml
index db762fce2..2f0b84bb1 100644
--- a/examples/api/src-tauri/Cargo.toml
+++ b/examples/api/src-tauri/Cargo.toml
@@ -43,7 +43,6 @@ tauri-plugin-deep-link = { path = "../../../plugins/deep-link", version = "2.4.1
tauri-plugin-sql = { path = "../../../plugins/sql", version = "2.4.1", features = [
"sqlite",
] }
-tauri-plugin-stronghold = { path = "../../../plugins/stronghold", version = "2.3.2" }
tauri-plugin-websocket = { path = "../../../plugins/websocket", version = "2.4.3" }
# WebDriver automation bridge, used by the plugins e2e suite (packages/api-e2e).
diff --git a/examples/api/src-tauri/capabilities/base.json b/examples/api/src-tauri/capabilities/base.json
index 90c6d976a..51bf9fd02 100644
--- a/examples/api/src-tauri/capabilities/base.json
+++ b/examples/api/src-tauri/capabilities/base.json
@@ -139,10 +139,6 @@
"deep-link:allow-is-registered",
"sql:default",
"sql:allow-execute",
- "stronghold:default",
- "stronghold:allow-destroy",
- "stronghold:allow-remove-secret",
- "stronghold:allow-remove-store-record",
"websocket:default"
]
}
diff --git a/examples/api/src-tauri/src/lib.rs b/examples/api/src-tauri/src/lib.rs
index 9a20067df..cb858201a 100644
--- a/examples/api/src-tauri/src/lib.rs
+++ b/examples/api/src-tauri/src/lib.rs
@@ -9,7 +9,7 @@ mod tray;
use serde::Serialize;
use tauri::{
webview::{PageLoadEvent, WebviewWindowBuilder},
- App, AppHandle, Emitter, Listener, Manager, RunEvent, WebviewUrl,
+ App, AppHandle, Emitter, Listener, RunEvent, WebviewUrl,
};
#[derive(Clone, Serialize)]
@@ -71,14 +71,6 @@ pub fn run() {
.build(),
)
.setup(move |app| {
- // the argon2 salt lives next to the snapshots the frontend creates
- let local_data_dir = app.path().app_local_data_dir()?;
- std::fs::create_dir_all(&local_data_dir)?;
- app.handle().plugin(
- tauri_plugin_stronghold::Builder::with_argon2(&local_data_dir.join("salt.txt"))
- .build(),
- )?;
-
#[cfg(desktop)]
{
// registered before the tray, whose events it tracks
diff --git a/examples/api/src/App.svelte b/examples/api/src/App.svelte
index 573ff7e19..205b46f07 100644
--- a/examples/api/src/App.svelte
+++ b/examples/api/src/App.svelte
@@ -37,7 +37,6 @@
import DeepLink from './views/DeepLink.svelte'
import Positioner from './views/Positioner.svelte'
import Sql from './views/Sql.svelte'
- import Stronghold from './views/Stronghold.svelte'
import WebSocket from './views/WebSocket.svelte'
import TitleBar from './lib/TitleBar.svelte'
@@ -147,11 +146,6 @@
component: Sql,
icon: 'i-ph-database'
},
- {
- label: 'Stronghold',
- component: Stronghold,
- icon: 'i-ph-lock-key'
- },
{
label: 'Deep link',
component: DeepLink,
diff --git a/examples/api/src/views/Stronghold.svelte b/examples/api/src/views/Stronghold.svelte
deleted file mode 100644
index 900b2bbba..000000000
--- a/examples/api/src/views/Stronghold.svelte
+++ /dev/null
@@ -1,71 +0,0 @@
-
-
-
- {#if stronghold}
-
-
-
-
-
- {:else}
-
- {/if}
-
diff --git a/packages/api-e2e/README.md b/packages/api-e2e/README.md
index e39fc1781..07e97920c 100644
--- a/packages/api-e2e/README.md
+++ b/packages/api-e2e/README.md
@@ -83,7 +83,6 @@ skipped on desktop, and the rest run everywhere with the odd test gated.
| `shell` | `execute`, `spawn` with stdout/stderr/close events, stdin, `kill`, scope enforcement. | Scope only on iOS, which cannot spawn a process at all. |
| `sql` | SQLite `load`/`execute`/`select`/`close`, bound values, column types, app-registered migrations, error paths. | Same. |
| `store` | CRUD, persistence, auto-save, defaults/reset, reload, `getStore`, `LazyStore`, change events. | Same. |
-| `stronghold` | Store records (persisted across reloads), wrong-password and unknown-client errors, vault secrets, BIP39/SLIP10 derivation and Ed25519 signing. | Same. |
| `updater` | `check` against the fixture manifest (update / 204 / older release). Installing is never exercised. | Skipped — desktop-only plugin. |
| `upload` | `download` and `upload` with progress, methods, headers and error paths. | Same (through `adb reverse` on Android). |
| `websocket` | Text/binary echo, ping/pong, handshake headers, listener removal, server and client close, connection and argument errors, against the fixture server. | Same (through `adb reverse` on Android). |
diff --git a/packages/api-e2e/package.json b/packages/api-e2e/package.json
index b8226b538..b4ce4b2e7 100644
--- a/packages/api-e2e/package.json
+++ b/packages/api-e2e/package.json
@@ -38,7 +38,6 @@
"@tauri-apps/plugin-shell": "workspace:*",
"@tauri-apps/plugin-sql": "workspace:*",
"@tauri-apps/plugin-store": "workspace:*",
- "@tauri-apps/plugin-stronghold": "workspace:*",
"@tauri-apps/plugin-updater": "workspace:*",
"@tauri-apps/plugin-upload": "workspace:*",
"@tauri-apps/plugin-websocket": "workspace:*",
diff --git a/packages/api-e2e/test/helpers/index.ts b/packages/api-e2e/test/helpers/index.ts
index 6da724c30..f22992ab7 100644
--- a/packages/api-e2e/test/helpers/index.ts
+++ b/packages/api-e2e/test/helpers/index.ts
@@ -26,7 +26,6 @@ import type * as Process from '@tauri-apps/plugin-process'
import type * as Shell from '@tauri-apps/plugin-shell'
import type * as Sql from '@tauri-apps/plugin-sql'
import type * as Store from '@tauri-apps/plugin-store'
-import type * as Stronghold from '@tauri-apps/plugin-stronghold'
import type * as Updater from '@tauri-apps/plugin-updater'
import type * as Upload from '@tauri-apps/plugin-upload'
import type * as WebSocket from '@tauri-apps/plugin-websocket'
@@ -52,7 +51,6 @@ export interface CommonPluginApi {
shell: typeof Shell
sql: typeof Sql.default
store: typeof Store
- stronghold: typeof Stronghold
upload: typeof Upload
websocket: typeof WebSocket.default
}
diff --git a/packages/api-e2e/test/specs/plugins.spec.ts b/packages/api-e2e/test/specs/plugins.spec.ts
index 7fdc00997..70b7bc0bc 100644
--- a/packages/api-e2e/test/specs/plugins.spec.ts
+++ b/packages/api-e2e/test/specs/plugins.spec.ts
@@ -108,7 +108,6 @@ const commonSurface: Surface = {
// the `Database` class itself
sql: ['load', 'get'],
store: ['load', 'getStore', 'LazyStore', 'Store'],
- stronghold: ['Location', 'Client', 'Store', 'Vault', 'Stronghold'],
upload: ['download', 'upload', 'HttpMethod'],
// the `WebSocket` class itself
websocket: ['connect']
diff --git a/packages/api-e2e/test/specs/stronghold.spec.ts b/packages/api-e2e/test/specs/stronghold.spec.ts
deleted file mode 100644
index 4a4960125..000000000
--- a/packages/api-e2e/test/specs/stronghold.spec.ts
+++ /dev/null
@@ -1,248 +0,0 @@
-// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
-// SPDX-License-Identifier: Apache-2.0
-// SPDX-License-Identifier: MIT
-
-import { expect } from '@wdio/globals'
-import {
- tauri,
- tauriError,
- describePlugin,
- scratchDir
-} from '../helpers/index.js'
-
-// The example derives the snapshot key from the password with argon2. Each
-// test works on its own snapshot file under the spec's scratch directory,
-// which the fs plugin cleans up; the snapshot path is resolved inside the page.
-
-const dir = scratchDir('stronghold')
-const password = 'e2e-password'
-const clientName = 'e2e-client'
-
-/** An arbitrary, valid BIP39 mnemonic, so derived keys are deterministic. */
-const mnemonic =
- 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about'
-
-describePlugin('stronghold', () => {
- before(async () => {
- await tauri(async (api, dir) => {
- const baseDir = api.fs.BaseDirectory.AppData
- if (await api.fs.exists(dir, { baseDir })) {
- await api.fs.remove(dir, { baseDir, recursive: true })
- }
- await api.fs.mkdir(dir, { baseDir, recursive: true })
- }, dir)
- })
-
- after(async () => {
- await tauri(async (api, dir) => {
- const baseDir = api.fs.BaseDirectory.AppData
- if (await api.fs.exists(dir, { baseDir })) {
- await api.fs.remove(dir, { baseDir, recursive: true })
- }
- }, dir)
- })
-
- it('store records round-trip and persist in the snapshot', async () => {
- const result = await tauri(
- async (api, dir, password, clientName) => {
- const path = await api.path.join(
- await api.path.appDataDir(),
- dir,
- 'store.stronghold'
- )
- const value = Array.from(new TextEncoder().encode('top secret'))
-
- const stronghold = await api.stronghold.Stronghold.load(path, password)
- const client = await stronghold.createClient(clientName)
- await client.getStore().insert('key', value)
- const sameSession = await client.getStore().get('key')
- await stronghold.save()
- await stronghold.unload()
-
- const reopened = await api.stronghold.Stronghold.load(path, password)
- const store = (await reopened.loadClient(clientName)).getStore()
- const persisted = await store.get('key')
- const missing = await store.get('missing')
- await reopened.unload()
-
- return {
- sameSession: sameSession && Array.from(sameSession),
- persisted: persisted && new TextDecoder().decode(persisted),
- missing,
- fileExists: await api.fs.exists(path)
- }
- },
- dir,
- password,
- clientName
- )
- expect(result.sameSession).toEqual(
- Array.from(new TextEncoder().encode('top secret'))
- )
- expect(result.persisted).toBe('top secret')
- expect(result.missing).toBeNull()
- expect(result.fileExists).toBe(true)
- })
-
- it('store.remove returns the removed value', async () => {
- const result = await tauri(
- async (api, dir, password, clientName) => {
- const path = await api.path.join(
- await api.path.appDataDir(),
- dir,
- 'remove.stronghold'
- )
- const stronghold = await api.stronghold.Stronghold.load(path, password)
- const store = (await stronghold.createClient(clientName)).getStore()
- await store.insert('key', [1, 2, 3])
- const removed = await store.remove('key')
- const after = await store.get('key')
- await stronghold.unload()
- return { removed: removed && Array.from(removed), after }
- },
- dir,
- password,
- clientName
- )
- expect(result).toEqual({ removed: [1, 2, 3], after: null })
- })
-
- it('a snapshot cannot be opened with the wrong password', async () => {
- const path = await tauri(
- async (api, dir, password, clientName) => {
- const path = await api.path.join(
- await api.path.appDataDir(),
- dir,
- 'password.stronghold'
- )
- const stronghold = await api.stronghold.Stronghold.load(path, password)
- await stronghold.createClient(clientName)
- await stronghold.unload()
- return path
- },
- dir,
- password,
- clientName
- )
- const error = await tauriError(
- (api, path) => api.stronghold.Stronghold.load(path, 'wrong-password'),
- path
- )
- expect(error).toMatch(/failed to decode\/decrypt/)
- })
-
- it('loading a client that was never created is rejected', async () => {
- const error = await tauriError(
- async (api, dir, password) => {
- const path = await api.path.join(
- await api.path.appDataDir(),
- dir,
- 'no-client.stronghold'
- )
- const stronghold = await api.stronghold.Stronghold.load(path, password)
- try {
- await stronghold.loadClient('never-created')
- } finally {
- await stronghold.unload()
- }
- },
- dir,
- password
- )
- expect(error).toMatch(/error loading client data/)
- })
-
- it('vault procedures derive keys and sign without exposing secrets', async () => {
- const result = await tauri(
- async (api, dir, password, clientName, mnemonic) => {
- const { Location } = api.stronghold
- const path = await api.path.join(
- await api.path.appDataDir(),
- dir,
- 'vault.stronghold'
- )
- const stronghold = await api.stronghold.Stronghold.load(path, password)
- const vault = (await stronghold.createClient(clientName)).getVault(
- 'vault'
- )
-
- // the same mnemonic recovered twice derives the same key
- const seedA = Location.generic('vault', 'seed-a')
- const seedB = Location.generic('vault', 'seed-b')
- await vault.recoverBIP39(mnemonic, seedA)
- await vault.recoverBIP39(mnemonic, seedB)
- // Ed25519 SLIP-10 only derives hardened indices (the high bit set).
- // (No named helper: the transpiler would wrap it in a `__name` call
- // that does not exist in the page.)
- const chain = [44, 4218, 0, 0, 0].map((i) => (i | 0x80000000) >>> 0)
- const otherChain = [44, 4218, 0, 0, 1].map(
- (i) => (i | 0x80000000) >>> 0
- )
- const keyA = Location.generic('vault', 'key-a')
- const keyB = Location.generic('vault', 'key-b')
- const keyOther = Location.generic('vault', 'key-other')
- await vault.deriveSLIP10(chain, 'Seed', seedA, keyA)
- await vault.deriveSLIP10(chain, 'Seed', seedB, keyB)
- await vault.deriveSLIP10(otherChain, 'Seed', seedA, keyOther)
-
- const publicA = Array.from(await vault.getEd25519PublicKey(keyA))
- const publicB = Array.from(await vault.getEd25519PublicKey(keyB))
- const publicOther = Array.from(
- await vault.getEd25519PublicKey(keyOther)
- )
- const signature = Array.from(await vault.signEd25519(keyA, 'message'))
- const signatureAgain = Array.from(
- await vault.signEd25519(keyA, 'message')
- )
-
- // a random seed and a raw secret can be stored and removed
- const random = Location.generic('vault', 'random')
- await vault.generateSLIP10Seed(random)
- await vault.insert('raw', [9, 9, 9])
- await vault.remove(Location.generic('vault', 'raw'))
-
- // a generated mnemonic recovers the seed generateBIP39 stored
- const generatedSeed = Location.generic('vault', 'generated')
- const mnemonicBytes = await vault.generateBIP39(generatedSeed)
- const recoveredSeed = Location.generic('vault', 'recovered')
- await vault.recoverBIP39(
- new TextDecoder().decode(mnemonicBytes),
- recoveredSeed
- )
- const generatedKey = Location.generic('vault', 'generated-key')
- const recoveredKey = Location.generic('vault', 'recovered-key')
- await vault.deriveSLIP10(chain, 'Seed', generatedSeed, generatedKey)
- await vault.deriveSLIP10(chain, 'Seed', recoveredSeed, recoveredKey)
- const generatedPublic = Array.from(
- await vault.getEd25519PublicKey(generatedKey)
- )
- const recoveredPublic = Array.from(
- await vault.getEd25519PublicKey(recoveredKey)
- )
-
- await stronghold.unload()
- return {
- publicA,
- publicB,
- publicOther,
- signature,
- signatureAgain,
- generatedPublic,
- recoveredPublic
- }
- },
- dir,
- password,
- clientName,
- mnemonic
- )
- expect(result.publicA).toHaveLength(32)
- expect(result.publicB).toEqual(result.publicA)
- expect(result.publicOther).not.toEqual(result.publicA)
- // Ed25519 signatures are 64 bytes and deterministic
- expect(result.signature).toHaveLength(64)
- expect(result.signatureAgain).toEqual(result.signature)
- expect(result.recoveredPublic).toEqual(result.generatedPublic)
- expect(result.generatedPublic).not.toEqual(result.publicA)
- })
-})
diff --git a/packages/api-e2e/wdio.mobile.ts b/packages/api-e2e/wdio.mobile.ts
index 815802ac8..4622ccfa6 100644
--- a/packages/api-e2e/wdio.mobile.ts
+++ b/packages/api-e2e/wdio.mobile.ts
@@ -303,33 +303,12 @@ function tauriCli(args: string[], env: NodeJS.ProcessEnv = {}): void {
* Environment for the Android build.
*
* - No debug info: the suite needs a debug build (webview debugging follows
- * `debug_assertions`), but with the stronghold, sql and websocket plugins the
+ * `debug_assertions`), but with the sql and websocket plugins the
* debug info alone grows the APK past what a default emulator can install
* ("not enough space").
- * - On a macOS host, `AR`/`RANLIB` point at the NDK's LLVM tools. Autotools-built
- * C dependencies (libsodium, through the stronghold plugin) otherwise fall back
- * to Apple's `ar`/`ranlib`, which silently produce an empty archive from the
- * Android (ELF) objects, and the app then fails to load its library with an
- * unresolved symbol. Linux hosts' GNU `ar` is fine.
*/
function androidBuildEnv(): NodeJS.ProcessEnv {
- const env: NodeJS.ProcessEnv = { CARGO_PROFILE_DEV_DEBUG: '0' }
- const ndk = process.env.NDK_HOME ?? process.env.ANDROID_NDK_HOME
- if (process.platform !== 'darwin' || !ndk) return env
- // the NDK only ships an x86_64 (Rosetta-compatible) macOS toolchain
- const bin = path.join(
- ndk,
- 'toolchains',
- 'llvm',
- 'prebuilt',
- 'darwin-x86_64',
- 'bin'
- )
- return {
- ...env,
- AR: path.join(bin, 'llvm-ar'),
- RANLIB: path.join(bin, 'llvm-ranlib')
- }
+ return { CARGO_PROFILE_DEV_DEBUG: '0' }
}
/** `adb` from the Android SDK, else whatever is on `PATH`. */
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index f445e0f2c..224f427b5 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -219,9 +219,6 @@ importers:
'@tauri-apps/plugin-store':
specifier: workspace:*
version: link:../../plugins/store
- '@tauri-apps/plugin-stronghold':
- specifier: workspace:*
- version: link:../../plugins/stronghold
'@tauri-apps/plugin-updater':
specifier: workspace:*
version: link:../../plugins/updater
@@ -337,9 +334,6 @@ importers:
'@tauri-apps/plugin-store':
specifier: workspace:*
version: link:../../plugins/store
- '@tauri-apps/plugin-stronghold':
- specifier: workspace:*
- version: link:../../plugins/stronghold
'@tauri-apps/plugin-updater':
specifier: workspace:*
version: link:../../plugins/updater