--- /dev/null
+//! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@zoso.dev>
+//! SPDX-License-Identifier: GPL-3.0-or-later
+
+export async function createKeyFromPassword (action: string, salt: ArrayBuffer, data: { [key: string]: unknown }): Promise<CryptoKey | undefined> {
+ // Allowlisted wallet actions
+ if (['create', 'load', 'unlock', 'update'].includes(action)) {
+
+ // Create local copy of password ASAP, then clear bytes from original buffer
+ if (!(data.password instanceof ArrayBuffer)) {
+ throw new TypeError('Password must be ArrayBuffer')
+ }
+
+ const password = data.password.slice()
+ new Uint8Array(data.password).fill(0)
+ delete data.password
+
+ // Only unlocking should decrypt the vault; other sensitive actions should
+ // throw if the vault is still locked and encrypted
+ const purpose = action === 'unlock' ? 'decrypt' : 'encrypt'
+
+ return crypto.subtle
+ .importKey('raw', password, 'PBKDF2', false, ['deriveKey'])
+ .then(derivationKey => {
+ new Uint8Array(password).fill(0).buffer.transfer?.()
+ const derivationAlgorithm: Pbkdf2Params = {
+ name: 'PBKDF2',
+ hash: 'SHA-512',
+ iterations: 210000,
+ salt
+ }
+ const derivedKeyType: AesKeyGenParams = {
+ name: 'AES-GCM',
+ length: 256
+ }
+ return crypto.subtle
+ .deriveKey(derivationAlgorithm, derivationKey, derivedKeyType, false, [purpose])
+ })
+ .catch(err => {
+ console.error(err)
+ throw new Error('Failed to derive CryptoKey from password', { cause: err })
+ })
+ } else if (data.password !== undefined) {
+ throw new Error('Password is not allowed for this action', { cause: action })
+ } else {
+ return Promise.resolve(undefined)
+ }
+}
import { BIP44_COIN_NANO } from '../constants'
import { utf8 } from '../convert'
import { Bip39, Bip44, Blake2b, NanoNaCl } from '../crypto'
+import { createKeyFromPassword } from './passkey'
import { VaultTimer } from './vault-timer'
/**
this.#seed = undefined
this.#mnemonic = undefined
NODE: this.#parentPort = parentPort
+
const listener = (event: MessageEvent<any>): void => {
const data = this.#parseData(event.data)
const action = this.#parseAction(data)
- const type = this.#parseType(action, data)
const keySalt = this.#parseKeySalt(action, data)
- const iv = this.#parseIv(action, data)
- const { seed, mnemonicPhrase, mnemonicSalt, index, encrypted, message } = this.#extractData(action, data)
- this.#createPasskey(action, keySalt, data)
+ createKeyFromPassword(action, keySalt, data)
.then((key: CryptoKey | undefined): Promise<NamedData> => {
+ const type = this.#parseType(action, data)
+ const iv = this.#parseIv(action, data)
+ const { seed, mnemonicPhrase, mnemonicSalt, index, encrypted, message } = this.#extractData(action, data)
switch (action) {
case 'STOP': {
BROWSER: close()
}
}
- #createPasskey (action: string, salt: ArrayBuffer, data: { [key: string]: unknown }) {
- // Allowlisted wallet actions
- if (['create', 'load', 'unlock', 'update'].includes(action)) {
-
- // Create local copy of password ASAP, then clear bytes from original buffer
- if (!(data.password instanceof ArrayBuffer)) {
- throw new TypeError('Password must be ArrayBuffer')
- }
-
- const password = data.password.slice()
- new Uint8Array(data.password).fill(0)
- delete data.password
-
- // Only unlocking should decrypt the vault; other sensitive actions should
- // throw if the vault is still locked and encrypted
- const purpose = action === 'unlock' ? 'decrypt' : 'encrypt'
-
- return crypto.subtle
- .importKey('raw', password, 'PBKDF2', false, ['deriveKey'])
- .then(derivationKey => {
- new Uint8Array(password).fill(0).buffer.transfer?.()
- const derivationAlgorithm: Pbkdf2Params = {
- name: 'PBKDF2',
- hash: 'SHA-512',
- iterations: 210000,
- salt
- }
- const derivedKeyType: AesKeyGenParams = {
- name: 'AES-GCM',
- length: 256
- }
- return crypto.subtle
- .deriveKey(derivationAlgorithm, derivationKey, derivedKeyType, false, [purpose])
- })
- .catch(err => {
- console.error(err)
- throw new Error('Failed to derive CryptoKey from password', { cause: err })
- })
- } else if (data.password !== undefined) {
- throw new Error('Password is not allowed for this action', { cause: action })
- } else {
- return Promise.resolve(undefined)
- }
- }
-
#decryptWallet (type: string, key: CryptoKey, iv: ArrayBuffer, encrypted: ArrayBuffer): Promise<void> {
const seedLength = type === 'BIP-44' ? 64 : 32
const additionalData = utf8.toBytes(type)