*/
export class Vault {
static #locked: boolean = true
+ static #timeout: number | NodeJS.Timeout
static #type?: 'BIP-44' | 'BLAKE2b'
static #seed?: ArrayBuffer
static #mnemonic?: ArrayBuffer
break
}
case 'verify': {
- result = await this.verify(seed, mnemonicPhrase)
+ result = this.verify(seed, mnemonicPhrase)
break
}
default: {
if (typeof index !== 'number') {
throw new Error('Invalid wallet account index')
}
+ clearTimeout(this.#timeout)
const prv = this.#type === 'BIP-44'
? await Bip44.ckd(this.#seed, BIP44_COIN_NANO, index)
: await this.#deriveBlake2bPrivateKey(this.#seed, index)
const pub = await NanoNaCl.convert(new Uint8Array(prv))
+ this.#timeout = setTimeout(() => this.lock(), 300000)
return { index, publicKey: pub.buffer }
} catch (err) {
throw new Error('Failed to derive account', { cause: err })
}
static lock (): NamedData<boolean> {
+ clearTimeout(this.#timeout)
this.#mnemonic = undefined
this.#seed = undefined
this.#locked = true
if (data == null) {
throw new Error('Data to sign not found')
}
+ clearTimeout(this.#timeout)
const prv = this.#type === 'BIP-44'
? await Bip44.ckd(this.#seed, BIP44_COIN_NANO, index)
: await this.#deriveBlake2bPrivateKey(this.#seed, index)
const sig = await NanoNaCl.detached(new Uint8Array(data), new Uint8Array(prv))
+ this.#timeout = setTimeout(() => this.lock(), 300000)
return { signature: sig.buffer }
} catch (err) {
throw new Error('Failed to sign message', { cause: err })
if (encrypted == null) {
throw new TypeError('Wallet encrypted data is required')
}
+ clearTimeout(this.#timeout)
await this.#decryptWallet(type, key, iv, encrypted)
if (!(this.#seed instanceof ArrayBuffer)) {
throw new TypeError('Invalid seed')
throw new TypeError('Invalid mnemonic')
}
this.#locked = false
+ this.#timeout = setTimeout(() => this.lock(), 300000)
return { isUnlocked: !this.#locked }
} catch (err) {
console.error(err)
}
/**
- * Decrypts the input and sets the seed and, if it is included, the mnemonic.
+ * Re-encrypts the wallet with a new password.
*/
static async update (key?: CryptoKey, keySalt?: ArrayBuffer): Promise<NamedData<ArrayBuffer>> {
try {
if (key == null || keySalt == null) {
throw new TypeError('Wallet password is required')
}
+ clearTimeout(this.#timeout)
const { iv, encrypted } = await this.#encryptWallet(key)
+ this.#timeout = setTimeout(() => this.lock(), 300000)
return { iv, salt: keySalt, encrypted }
} catch (err) {
console.error(err)
* Checks the seed and, if it exists, the mnemonic against input. The wallet
* must be unlocked prior to verification.
*/
- static async verify (seed?: ArrayBuffer, mnemonicPhrase?: string): Promise<NamedData<boolean>> {
+ static verify (seed?: ArrayBuffer, mnemonicPhrase?: string): NamedData<boolean> {
try {
if (this.#locked) {
throw new Error('Wallet is locked')
throw new Error('Failed to delete wallet from database')\r
}\r
this.#vault.terminate()\r
- clearTimeout(this.#lockTimer)\r
} catch (err) {\r
console.error(err)\r
throw new Error('Failed to destroy wallet', { cause: err })\r
*/\r
lock (): void {\r
_lock(this.#vault)\r
- clearTimeout(this.#lockTimer)\r
}\r
\r
/**\r
*/\r
async sign (index: number, block: Block): Promise<void> {\r
await _sign(this.#vault, index, block)\r
- clearTimeout(this.#lockTimer)\r
- this.#lockTimer = setTimeout(() => this.lock(), 300000)\r
}\r
\r
/**\r
*/\r
async unlock (password: string): Promise<void> {\r
await _unlock(this, this.#vault, password)\r
- clearTimeout(this.#lockTimer)\r
- this.#lockTimer = setTimeout(() => this.lock(), 300000)\r
}\r
\r
/**\r
async verify (mnemonic: string): Promise<boolean>\r
async verify (secret: string): Promise<boolean> {\r
try {\r
- clearTimeout(this.#lockTimer)\r
const data: NamedData = {\r
action: 'verify'\r
}\r
return isVerified\r
} catch (err) {\r
throw new Error('Failed to verify wallet', { cause: err })\r
- } finally {\r
- this.#lockTimer = setTimeout(() => this.lock(), 300000)\r
}\r
}\r
\r
static #isInternal: boolean = false\r
#accounts: AccountList\r
- #lockTimer?: any\r
#id: string\r
#mnemonic?: ArrayBuffer\r
#vault: WorkerQueue\r
\r
await assert.resolves(wallet.destroy())\r
})\r
+\r
+ await test('fail to access a wallet after automatic lock', { skip: true }, async () => {\r
+ const wallet = await Wallet.load('BIP-44', NANO_TEST_VECTORS.PASSWORD, NANO_TEST_VECTORS.MNEMONIC, NANO_TEST_VECTORS.PASSWORD)\r
+ await wallet.unlock(NANO_TEST_VECTORS.PASSWORD)\r
+\r
+ assert.ok(await wallet.verify(NANO_TEST_VECTORS.MNEMONIC))\r
+ assert.ok(await wallet.verify(NANO_TEST_VECTORS.BIP39_SEED))\r
+\r
+ await new Promise(async (resolve) => {\r
+ console.log('Waiting 5 minutes...')\r
+ setTimeout(async () => {\r
+ await assert.rejects(wallet.verify(NANO_TEST_VECTORS.MNEMONIC))\r
+ await assert.rejects(wallet.verify(NANO_TEST_VECTORS.BIP39_SEED))\r
+ await assert.resolves(wallet.destroy())\r
+ resolve(null)\r
+ }, 301000)\r
+ })\r
+ })\r
})\r
])\r