From e734cc53b542cd1439c706ddcb148a38a5a409df Mon Sep 17 00:00:00 2001 From: Rohan Jadvani <5459049+rohanjadvani@users.noreply.github.com> Date: Tue, 13 Aug 2024 15:37:33 -0400 Subject: [PATCH] feat(ironfish): Add `encrypt` method for account (#5247) * feat(ironfish): Add `encrypt` method for account * fix(ironfish): Remove extra KEY_LENGTH constant * test(ironfish): Fix serialization test * test(ironfish): Update fixture * feat(ironfish): Fix encrypt test for account --- .../__fixtures__/account.test.ts.fixture | 31 +++++++++++++++++++ ironfish/src/wallet/account/account.test.ts | 13 ++++++++ ironfish/src/wallet/account/account.ts | 16 ++++++++-- .../wallet/account/encryptedAccount.test.ts | 26 +++------------- .../src/wallet/exporter/encoders/bech32.ts | 4 +-- ironfish/src/wallet/walletdb/accountValue.ts | 6 ++-- 6 files changed, 66 insertions(+), 30 deletions(-) diff --git a/ironfish/src/wallet/account/__fixtures__/account.test.ts.fixture b/ironfish/src/wallet/account/__fixtures__/account.test.ts.fixture index 71b3bf6140..4963fc106e 100644 --- a/ironfish/src/wallet/account/__fixtures__/account.test.ts.fixture +++ b/ironfish/src/wallet/account/__fixtures__/account.test.ts.fixture @@ -5810,5 +5810,36 @@ } ] } + ], + "Accounts encrypt returns an encrypted account that can be decrypted into the original account": [ + { + "value": { + "encrypted": false, + "version": 4, + "id": "1bbc8bfa-2393-41fd-b1ad-a3ca03aee8ac", + "name": "test", + "spendingKey": "6a0ae63bd630bb5b205737f49cb0778a872fab11f7e797935adbb9716917c612", + "viewKey": "0c1888cfbe1d0f02c3069df3350665c86d2f56cfc64242d53c43925d09e1012c3f0cf9098c9a9fb7f826509d15d7140eb6d76ccfcb2fff81a3dacd382cf7fe00", + "incomingViewKey": "781829745831663347945ff2ac60e0c6181ffa8d73d9bcc4f5fd24a40f1dcd06", + "outgoingViewKey": "1b3ace62d49d5c0c55773264e78aeb6125b451595cf33f8033dcce01d9507d2a", + "publicAddress": "a634ebb5367d5ef7196b7226a7cf6583980b689992c2f595febc4648d2ee7449", + "createdAt": { + "hash": { + "type": "Buffer", + "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" + }, + "sequence": 1 + }, + "scanningEnabled": true, + "proofAuthorizingKey": "cc1913dba277eb64857337a5144a5c2f7f4ebc4af1915b667aa1a46b3e49fe07" + }, + "head": { + "hash": { + "type": "Buffer", + "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" + }, + "sequence": 1 + } + } ] } \ No newline at end of file diff --git a/ironfish/src/wallet/account/account.test.ts b/ironfish/src/wallet/account/account.test.ts index 0d24f6fc4d..54385836b3 100644 --- a/ironfish/src/wallet/account/account.test.ts +++ b/ironfish/src/wallet/account/account.test.ts @@ -2596,4 +2596,17 @@ describe('Accounts', () => { expect(accountTransactionHashes).toEqual(blockTransactionHashes) }) }) + + describe('encrypt', () => { + it('returns an encrypted account that can be decrypted into the original account', async () => { + const { node } = nodeTest + const account = await useAccountFixture(node.wallet) + const passphrase = 'foo' + + const encryptedAccount = account.encrypt(passphrase) + const decryptedAccount = encryptedAccount.decrypt(passphrase) + + expect(account.serialize()).toMatchObject(decryptedAccount.serialize()) + }) + }) }) diff --git a/ironfish/src/wallet/account/account.ts b/ironfish/src/wallet/account/account.ts index ef14ecbb2f..51302e103d 100644 --- a/ironfish/src/wallet/account/account.ts +++ b/ironfish/src/wallet/account/account.ts @@ -1,7 +1,7 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import { multisig } from '@ironfish/rust-nodejs' +import { encrypt, multisig } from '@ironfish/rust-nodejs' import { Asset } from '@ironfish/rust-nodejs' import { BufferMap, BufferSet } from 'buffer-map' import MurmurHash3 from 'imurmurhash' @@ -15,7 +15,7 @@ import { WithNonNull, WithRequired } from '../../utils' import { DecryptedNote } from '../../workerPool/tasks/decryptNotes' import { AssetBalances } from '../assetBalances' import { MultisigKeys, MultisigSigner } from '../interfaces/multisigKeys' -import { DecryptedAccountValue } from '../walletdb/accountValue' +import { AccountValueEncoding, DecryptedAccountValue } from '../walletdb/accountValue' import { AssetValue } from '../walletdb/assetValue' import { BalanceValue } from '../walletdb/balanceValue' import { DecryptedNoteValue } from '../walletdb/decryptedNoteValue' @@ -23,6 +23,7 @@ import { HeadValue } from '../walletdb/headValue' import { isSignerMultisig } from '../walletdb/multisigKeys' import { TransactionValue } from '../walletdb/transactionValue' import { WalletDB } from '../walletdb/walletdb' +import { EncryptedAccount } from './encryptedAccount' export const ACCOUNT_KEY_LENGTH = 32 @@ -1289,6 +1290,17 @@ export class Account { const publicKeyPackage = new multisig.PublicKeyPackage(this.multisigKeys.publicKeyPackage) return publicKeyPackage.identities() } + + encrypt(passphrase: string): EncryptedAccount { + const encoder = new AccountValueEncoding() + const serialized = encoder.serialize(this.serialize()) + const data = encrypt(serialized, passphrase) + + return new EncryptedAccount({ + data, + walletDb: this.walletDb, + }) + } } export function calculateAccountPrefix(id: string): Buffer { diff --git a/ironfish/src/wallet/account/encryptedAccount.test.ts b/ironfish/src/wallet/account/encryptedAccount.test.ts index 75c1d3dfd7..9b91821a1c 100644 --- a/ironfish/src/wallet/account/encryptedAccount.test.ts +++ b/ironfish/src/wallet/account/encryptedAccount.test.ts @@ -1,12 +1,9 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import { encrypt } from '@ironfish/rust-nodejs' import { useAccountFixture } from '../../testUtilities/fixtures/account' import { createNodeTest } from '../../testUtilities/nodeTest' import { AccountDecryptionFailedError } from '../errors' -import { AccountValueEncoding } from '../walletdb/accountValue' -import { EncryptedAccount } from './encryptedAccount' describe('EncryptedAccount', () => { const nodeTest = createNodeTest() @@ -16,18 +13,10 @@ describe('EncryptedAccount', () => { const { node } = nodeTest const account = await useAccountFixture(node.wallet) - const encoder = new AccountValueEncoding() - const data = encoder.serialize(account.serialize()) - - const encryptedData = encrypt(data, passphrase) - const encryptedAccount = new EncryptedAccount({ - data: encryptedData, - walletDb: node.wallet.walletDb, - }) - + const encryptedAccount = account.encrypt(passphrase) const decryptedAccount = encryptedAccount.decrypt(passphrase) - const decryptedData = encoder.serialize(decryptedAccount.serialize()) - expect(data.toString('hex')).toEqual(decryptedData.toString('hex')) + + expect(account.serialize()).toMatchObject(decryptedAccount.serialize()) }) it('throws an error when an invalid passphrase is used', async () => { @@ -36,14 +25,7 @@ describe('EncryptedAccount', () => { const { node } = nodeTest const account = await useAccountFixture(node.wallet) - const encoder = new AccountValueEncoding() - const data = encoder.serialize(account.serialize()) - - const encryptedData = encrypt(data, passphrase) - const encryptedAccount = new EncryptedAccount({ - data: encryptedData, - walletDb: node.wallet.walletDb, - }) + const encryptedAccount = account.encrypt(passphrase) expect(() => encryptedAccount.decrypt(invalidPassphrase)).toThrow( AccountDecryptionFailedError, diff --git a/ironfish/src/wallet/exporter/encoders/bech32.ts b/ironfish/src/wallet/exporter/encoders/bech32.ts index a73b44e9cd..07961220ec 100644 --- a/ironfish/src/wallet/exporter/encoders/bech32.ts +++ b/ironfish/src/wallet/exporter/encoders/bech32.ts @@ -1,11 +1,11 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import { PUBLIC_ADDRESS_LENGTH } from '@ironfish/rust-nodejs' +import { KEY_LENGTH, PUBLIC_ADDRESS_LENGTH } from '@ironfish/rust-nodejs' import bufio, { EncodingError } from 'bufio' import { Bech32m } from '../../../utils' import { ACCOUNT_SCHEMA_VERSION } from '../../account/account' -import { KEY_LENGTH, VIEW_KEY_LENGTH } from '../../walletdb/accountValue' +import { VIEW_KEY_LENGTH } from '../../walletdb/accountValue' import { AccountImport } from '../accountImport' import { AccountDecodingOptions, AccountEncoder, DecodeFailed, DecodeInvalid } from '../encoder' import { MultisigKeysEncoding } from './multisigKeys' diff --git a/ironfish/src/wallet/walletdb/accountValue.ts b/ironfish/src/wallet/walletdb/accountValue.ts index fb5a6c935e..e72104f7f5 100644 --- a/ironfish/src/wallet/walletdb/accountValue.ts +++ b/ironfish/src/wallet/walletdb/accountValue.ts @@ -1,15 +1,13 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import { PUBLIC_ADDRESS_LENGTH } from '@ironfish/rust-nodejs' +import { KEY_LENGTH, PUBLIC_ADDRESS_LENGTH } from '@ironfish/rust-nodejs' import bufio from 'bufio' import { IDatabaseEncoding } from '../../storage' -import { ACCOUNT_KEY_LENGTH } from '../account/account' import { MultisigKeys } from '../interfaces/multisigKeys' import { HeadValue, NullableHeadValueEncoding } from './headValue' import { MultisigKeysEncoding } from './multisigKeys' -export const KEY_LENGTH = ACCOUNT_KEY_LENGTH export const VIEW_KEY_LENGTH = 64 const VERSION_LENGTH = 2 @@ -18,7 +16,7 @@ export interface EncryptedAccountValue { data: Buffer } -export interface DecryptedAccountValue { +export type DecryptedAccountValue = { encrypted: false version: number id: string