diff --git a/apps/extension/src/background/index.ts b/apps/extension/src/background/index.ts index b34b5ca5d..91c998061 100644 --- a/apps/extension/src/background/index.ts +++ b/apps/extension/src/background/index.ts @@ -25,12 +25,11 @@ import { init as initKeyRing, SDK_KEY, PARENT_ACCOUNT_ID_KEY, - UtilityStore, } from "./keyring"; const store = new IndexedDBKVStore(KVPrefix.IndexedDB); -const utilityStore = new IndexedDBKVStore(KVPrefix.Utility); +const activeAccountStore = new IndexedDBKVStore(KVPrefix.ActiveAccount); // TODO: For now we will be running two stores side by side const sdkStore = new IndexedDBKVStore(KVPrefix.SDK); const extensionStore = new ExtensionKVStore(KVPrefix.LocalStorage, { @@ -72,7 +71,9 @@ const { REACT_APP_NAMADA_URL = DEFAULT_URL } = process.env; const sdkData: Record | undefined = await sdkStore.get( SDK_KEY ); - const activeAccount = await utilityStore.get(PARENT_ACCOUNT_ID_KEY); + const activeAccount = await activeAccountStore.get( + PARENT_ACCOUNT_ID_KEY + ); if (sdkData && activeAccount) { const data = new TextEncoder().encode(sdkData[activeAccount]); @@ -83,7 +84,7 @@ const { REACT_APP_NAMADA_URL = DEFAULT_URL } = process.env; const keyRingService = new KeyRingService( store, sdkStore, - utilityStore, + activeAccountStore, connectedTabsStore, extensionStore, defaultChainId, diff --git a/apps/extension/src/background/keyring/crypto.ts b/apps/extension/src/background/keyring/crypto.ts index ce8bc5c92..f8732cd4f 100644 --- a/apps/extension/src/background/keyring/crypto.ts +++ b/apps/extension/src/background/keyring/crypto.ts @@ -1,15 +1,7 @@ -import { - AES, - Argon2, - Argon2Params, - ByteSize, - Rng, - Salt, - VecU8Pointer, -} from "@anoma/crypto"; +import { AES, Argon2, Argon2Params, ByteSize, Rng, Salt } from "@anoma/crypto"; import { AccountType, Bip44Path } from "@anoma/types"; import { Argon2Config } from "config"; -import { CryptoRecord, KdfType, KeyStore } from "./types"; +import { KdfType, KeyStore } from "./types"; import { readVecU8Pointer } from "@anoma/crypto/src/utils"; type CryptoArgs = { @@ -39,10 +31,21 @@ export class Crypto { text, type, } = args; + const saltInstance = Salt.generate(); + const salt = saltInstance.as_string(); + const { m_cost, t_cost, p_cost } = Argon2Config; + const argon2Params = new Argon2Params(m_cost, t_cost, p_cost); + const argon2 = new Argon2(password, salt, argon2Params); + const params = argon2.params(); + const key = argon2.key(); - const { params, key, iv, salt } = this.encryptionParams(password); - const cipherText = this.encryptWithAES(key, iv, text); - const crypto = this.cryptoRecord(cipherText, params, iv, salt); + const iv = Rng.generate_bytes(ByteSize.N12); + const aes = new AES(key, iv); + const encrypted = aes.encrypt(text); + + saltInstance.free(); + argon2.free(); + aes.free(); return { alias, @@ -52,46 +55,32 @@ export class Crypto { id, parentId, path, - crypto, - type, - }; - } - - private cryptoRecord( - cipherText: Uint8Array, - { m_cost, t_cost, p_cost }: Argon2Params, - iv: Uint8Array, - salt: string - ): CryptoRecord { - return { - cipher: { - type: "aes-256-gcm", - iv, - text: cipherText, - }, - kdf: { - type: KdfType.Argon2, - params: { - m_cost, - t_cost, - p_cost, - salt, + crypto: { + cipher: { + type: "aes-256-gcm", + iv, + text: encrypted, + }, + kdf: { + type: KdfType.Argon2, + params: { + m_cost: params.m_cost, + t_cost: params.t_cost, + p_cost: params.p_cost, + salt, + }, }, }, + type, }; } - public encryptAuthKey(password: string, uuid: string): CryptoRecord { - const { params, key, iv, salt } = this.encryptionParams(password); - const cipherText = this.encryptWithAES(key, iv, uuid); - return this.cryptoRecord(cipherText, params, iv, salt); - } - public decrypt( - crypto: CryptoRecord, + store: KeyStore, password: string, cryptoMemory: WebAssembly.Memory ): string { + const { crypto } = store; const { cipher, kdf } = crypto; const { m_cost, t_cost, p_cost, salt } = kdf.params; @@ -102,46 +91,11 @@ export class Crypto { const aes = new AES(newKey, cipher.iv); const vecU8Pointer = aes.decrypt(cipher.text); const decrypted = readVecU8Pointer(vecU8Pointer, cryptoMemory); - const plainText = new TextDecoder().decode(decrypted); + const phrase = new TextDecoder().decode(decrypted); aes.free(); vecU8Pointer.free(); - return plainText; - } - - private encryptionParams(password: string): { - params: Argon2Params; - key: VecU8Pointer; - salt: string; - iv: Uint8Array; - } { - const saltInstance = Salt.generate(); - - const salt = saltInstance.as_string(); - saltInstance.free(); - - const { m_cost, t_cost, p_cost } = Argon2Config; - const argon2Params = new Argon2Params(m_cost, t_cost, p_cost); - const argon2 = new Argon2(password, salt, argon2Params); - const params = argon2.params(); - const key = argon2.key(); - argon2.free(); - - const iv = Rng.generate_bytes(ByteSize.N12); - - return { params, key, salt, iv }; - } - - private encryptWithAES( - key: VecU8Pointer, - iv: Uint8Array, - plainText: string - ): Uint8Array { - const aes = new AES(key, iv); - const cipherText = aes.encrypt(plainText); - aes.free(); - - return cipherText; + return phrase; } } diff --git a/apps/extension/src/background/keyring/keyring.ts b/apps/extension/src/background/keyring/keyring.ts index 34311cf75..88a82a89b 100644 --- a/apps/extension/src/background/keyring/keyring.ts +++ b/apps/extension/src/background/keyring/keyring.ts @@ -1,5 +1,5 @@ +import { v5 as uuid } from "uuid"; import BigNumber from "bignumber.js"; -import { v5 as uuid, v4 as uuidV4 } from "uuid"; import { HDWallet, @@ -32,8 +32,6 @@ import { KeyStore, ResetPasswordError, DeleteAccountError, - CryptoRecord, - UtilityStore, } from "./types"; import { readVecStringPointer, @@ -63,7 +61,6 @@ const getId = (name: string, ...args: (number | string)[]): string => { export const KEYSTORE_KEY = "key-store"; export const SDK_KEY = "sdk-store"; export const PARENT_ACCOUNT_ID_KEY = "parent-account-id"; -export const AUTHKEY_KEY = "auth-key-store"; const crypto = new Crypto(); @@ -86,7 +83,7 @@ export class KeyRing { constructor( protected readonly kvStore: KVStore, protected readonly sdkStore: KVStore>, - protected readonly utilityStore: KVStore, + protected readonly activeAccountStore: KVStore, protected readonly extensionStore: KVStore, protected readonly chainId: string, protected readonly sdk: Sdk, @@ -118,11 +115,11 @@ export class KeyRing { } public async getActiveAccountId(): Promise { - return await this.utilityStore.get(PARENT_ACCOUNT_ID_KEY); + return await this.activeAccountStore.get(PARENT_ACCOUNT_ID_KEY); } public async setActiveAccountId(parentId: string): Promise { - await this.utilityStore.set(PARENT_ACCOUNT_ID_KEY, parentId); + await this.activeAccountStore.set(PARENT_ACCOUNT_ID_KEY, parentId); // To sync sdk wallet with DB const sdkData = await this.sdkStore.get(SDK_KEY); @@ -137,16 +134,12 @@ export class KeyRing { ): Promise { // default to active account if no account provided const idToCheck = accountId ?? (await this.getActiveAccountId()); - if (!idToCheck) { - throw new Error("No account to check password against"); - } - const authKeys = await this.utilityStore.get<{ - [id: string]: CryptoRecord; - }>(AUTHKEY_KEY); - if (authKeys) { + const mnemonic = await this._keyStore.getRecord("id", idToCheck); + // TODO: Generate arbitrary data to check decryption against + if (mnemonic) { try { - crypto.decrypt(authKeys[idToCheck], password, this._cryptoMemory); + crypto.decrypt(mnemonic, password, this._cryptoMemory); return true; } catch (error) { console.warn(error); @@ -179,7 +172,7 @@ export class KeyRing { for (const account of allAccounts) { const decryptedSecret = crypto.decrypt( - account.crypto, + account, currentPassword, this._cryptoMemory ); @@ -271,7 +264,6 @@ export class KeyRing { }); await this._keyStore.append(mnemonicStore); - await this.generateAuthKey(id, password); // When we are adding new top level account we have to clear the storage // to prevent adding top level secret key to existing keys this.sdk.clear_storage(); @@ -286,21 +278,6 @@ export class KeyRing { return false; } - public async generateAuthKey( - accountId: string, - password: string - ): Promise { - const id = uuidV4(); - const authKey = crypto.encryptAuthKey(password, id); - const entries = await this.utilityStore.get<{ [id: string]: CryptoRecord }>( - AUTHKEY_KEY - ); - await this.utilityStore.set(AUTHKEY_KEY, { - ...entries, - [accountId]: authKey, - }); - } - public deriveTransparentAccount( seed: VecU8Pointer, path: Bip44Path, @@ -488,7 +465,7 @@ export class KeyRing { const parentId = storedMnemonic.id; try { const phrase = crypto.decrypt( - storedMnemonic.crypto, + storedMnemonic, password, this._cryptoMemory ); @@ -639,7 +616,7 @@ export class KeyRing { let pk: string; try { - pk = crypto.decrypt(account.crypto, this._password, this._cryptoMemory); + pk = crypto.decrypt(account, this._password, this._cryptoMemory); } catch (e) { throw new Error(`Could not unlock account for ${address}: ${e}`); } @@ -696,11 +673,7 @@ export class KeyRing { if (!account) { throw new Error(`Account not found.`); } - const text = crypto.decrypt( - account.crypto, - this._password, - this._cryptoMemory - ); + const text = crypto.decrypt(account, this._password, this._cryptoMemory); // For shielded accounts we need to return the spending key as well. const extendedSpendingKey = diff --git a/apps/extension/src/background/keyring/service.ts b/apps/extension/src/background/keyring/service.ts index 85278bddc..558824546 100644 --- a/apps/extension/src/background/keyring/service.ts +++ b/apps/extension/src/background/keyring/service.ts @@ -13,7 +13,6 @@ import { TabStore, ResetPasswordError, DeleteAccountError, - UtilityStore, } from "./types"; import { syncTabs, updateTabStorage } from "./utils"; import { ExtensionRequester, getAnomaRouterId } from "extension"; @@ -38,7 +37,7 @@ export class KeyRingService { constructor( protected readonly kvStore: KVStore, protected readonly sdkStore: KVStore>, - protected readonly utilityStore: KVStore, + protected readonly accountAccountStore: KVStore, protected readonly connectedTabsStore: KVStore, protected readonly extensionStore: KVStore, protected readonly chainId: string, @@ -50,7 +49,7 @@ export class KeyRingService { this._keyRing = new KeyRing( kvStore, sdkStore, - utilityStore, + accountAccountStore, extensionStore, chainId, sdk, diff --git a/apps/extension/src/background/keyring/types.ts b/apps/extension/src/background/keyring/types.ts index 42cf222ff..4768c786e 100644 --- a/apps/extension/src/background/keyring/types.ts +++ b/apps/extension/src/background/keyring/types.ts @@ -13,7 +13,6 @@ export type Argon2Params = KdfParams & { m_cost: number; t_cost: number; p_cost: number; - salt: string; }; export type ScryptParams = KdfParams & { @@ -22,25 +21,23 @@ export type ScryptParams = KdfParams & { p: number; }; -export type CryptoRecord = { - cipher: { - type: "aes-256-gcm"; - iv: Uint8Array; - text: Uint8Array; - }; - kdf: { - type: KdfType; - params: T; - }; -}; - export interface KeyStore { id: string; alias: string; address: string; owner: string; chainId: string; - crypto: CryptoRecord; + crypto: { + cipher: { + type: "aes-256-gcm"; + iv: Uint8Array; + text: Uint8Array; + }; + kdf: { + type: KdfType; + params: T; + }; + }; meta?: { [key: string]: string; }; @@ -67,8 +64,6 @@ export type TabStore = { timestamp: number; }; -export type UtilityStore = string | { [id: string]: CryptoRecord }; - export enum ResetPasswordError { BadPassword, KeyStoreError, diff --git a/apps/extension/src/background/web-workers/submit-transfer-web-worker.ts b/apps/extension/src/background/web-workers/submit-transfer-web-worker.ts index ac0293b17..2a5b9f5b8 100644 --- a/apps/extension/src/background/web-workers/submit-transfer-web-worker.ts +++ b/apps/extension/src/background/web-workers/submit-transfer-web-worker.ts @@ -14,7 +14,7 @@ import { (async function init() { await initShared(); const sdkStore = new IndexedDBKVStore(KVPrefix.SDK); - const utilityStore = new IndexedDBKVStore(KVPrefix.Utility); + const activeAccountStore = new IndexedDBKVStore(KVPrefix.ActiveAccount); const sdk = new Sdk(chains[defaultChainId].rpc); await sdk.load_masp_params(); @@ -22,7 +22,9 @@ import { const sdkData: Record | undefined = await sdkStore.get( "sdk-store" ); - const activeAccount = await utilityStore.get("parent-account-id"); + const activeAccount = await activeAccountStore.get( + "parent-account-id" + ); if (sdkData && activeAccount) { const data = new TextEncoder().encode(sdkData[activeAccount]); diff --git a/apps/extension/src/provider/Anoma.test.ts b/apps/extension/src/provider/Anoma.test.ts index 7175841b8..a272a1a79 100644 --- a/apps/extension/src/provider/Anoma.test.ts +++ b/apps/extension/src/provider/Anoma.test.ts @@ -26,7 +26,6 @@ import { KeyStore, KEYSTORE_KEY, PARENT_ACCOUNT_ID_KEY, - UtilityStore, } from "background/keyring"; import { Sdk } from "@anoma/shared"; import * as utils from "extension/utils"; @@ -37,12 +36,12 @@ jest.mock("webextension-polyfill", () => ({})); describe("Anoma", () => { let anoma: Anoma; let iDBStore: KVStoreMock; - let utilityStore: KVStoreMock; + let activeAccountStore: KVStoreMock; let keyRingService: KeyRingService; beforeAll(async () => { jest.spyOn(utils, "getAnomaRouterId").mockResolvedValue(1); - ({ anoma, iDBStore, utilityStore, keyRingService } = await init()); + ({ anoma, iDBStore, activeAccountStore, keyRingService } = await init()); jest .spyOn(KeyRing.prototype, "checkPassword") @@ -71,7 +70,7 @@ describe("Anoma", () => { it("should return all accounts", async () => { iDBStore.set(KEYSTORE_KEY, keyStore); - utilityStore.set(PARENT_ACCOUNT_ID_KEY, ACTIVE_ACCOUNT_ID); + activeAccountStore.set(PARENT_ACCOUNT_ID_KEY, ACTIVE_ACCOUNT_ID); const storedKeyStore = keyStore.map( ({ crypto: _crypto, owner: _owner, ...account }) => account ); diff --git a/apps/extension/src/router/types/enums.ts b/apps/extension/src/router/types/enums.ts index 759428b8b..81e3f3516 100644 --- a/apps/extension/src/router/types/enums.ts +++ b/apps/extension/src/router/types/enums.ts @@ -18,7 +18,7 @@ export enum KVPrefix { LocalStorage = "Anoma::LocalStorage", Memory = "Anoma::Memory", SDK = "Anoma::SDK", - Utility = "Anoma::Utility", + ActiveAccount = "Anoma::ActiveAccount", ConnectedTabs = "Anoma::ConnectedTabs", } diff --git a/apps/extension/src/test/init.ts b/apps/extension/src/test/init.ts index e1086a33a..d70b7ad79 100644 --- a/apps/extension/src/test/init.ts +++ b/apps/extension/src/test/init.ts @@ -14,7 +14,6 @@ import { init as initKeyRing, KeyStore, TabStore, - UtilityStore, } from "../background/keyring"; import { @@ -34,15 +33,12 @@ const chainId = "namada-75a7e12.69483d59a9fb174"; export class KVStoreMock implements KVStore { private storage: { [key: string]: T | null } = {}; - constructor(readonly _prefix: string) {} + constructor(private readonly _prefix: string) {} - get(key: string): Promise { - return new Promise((resolve) => { - const data = this.storage[key]; - return resolve(data ? (data as U) : undefined); - }); + get(key: string): Promise { + return Promise.resolve(this.storage[key] || undefined); } - set(key: string, data: U | null): Promise { + set(key: string, data: T | null): Promise { this.storage[key] = data; return Promise.resolve(); } @@ -55,7 +51,7 @@ export const init = async (): Promise<{ anoma: Anoma; iDBStore: KVStoreMock; extStore: KVStoreMock; - utilityStore: KVStoreMock; + activeAccountStore: KVStoreMock; chainsService: ChainsService; keyRingService: KeyRingService; }> => { @@ -63,7 +59,7 @@ export const init = async (): Promise<{ const iDBStore = new KVStoreMock(KVPrefix.IndexedDB); const sdkStore = new KVStoreMock>(KVPrefix.SDK); const extStore = new KVStoreMock(KVPrefix.IndexedDB); - const utilityStore = new KVStoreMock(KVPrefix.Utility); + const activeAccountStore = new KVStoreMock(KVPrefix.ActiveAccount); const connectedTabsStore = new KVStoreMock( KVPrefix.ConnectedTabs ); @@ -93,7 +89,7 @@ export const init = async (): Promise<{ const keyRingService = new KeyRingService( iDBStore as KVStore, sdkStore, - utilityStore, + activeAccountStore, connectedTabsStore, extStore, chainId, @@ -124,7 +120,7 @@ export const init = async (): Promise<{ anoma, iDBStore, extStore, - utilityStore, + activeAccountStore, chainsService, keyRingService, }; diff --git a/packages/crypto/lib/Cargo.toml b/packages/crypto/lib/Cargo.toml index c98f49f67..86510649b 100644 --- a/packages/crypto/lib/Cargo.toml +++ b/packages/crypto/lib/Cargo.toml @@ -40,7 +40,8 @@ lto = true opt-level = "s" [profile.dev] -opt-level = "s" +# We do not wan't to make any optimizations for dev +opt-level = 0 # wasm-pack sepcific configuration [package.metadata.wasm-pack.profile.release] diff --git a/packages/shared/lib/Cargo.toml b/packages/shared/lib/Cargo.toml index f730150bb..052bb1d18 100644 --- a/packages/shared/lib/Cargo.toml +++ b/packages/shared/lib/Cargo.toml @@ -62,7 +62,8 @@ lto = true opt-level = "s" [profile.dev] -opt-level = "s" +# We do not wan't to make any optimizations for dev +opt-level = 0 # wasm-pack sepcific configuration [package.metadata.wasm-pack.profile.release] diff --git a/packages/storage/src/store/IndexedDBKVStore.ts b/packages/storage/src/store/IndexedDBKVStore.ts index 59a568e12..33e4371c6 100644 --- a/packages/storage/src/store/IndexedDBKVStore.ts +++ b/packages/storage/src/store/IndexedDBKVStore.ts @@ -1,11 +1,11 @@ import { KVStore } from "./types"; -export class IndexedDBKVStore implements KVStore { +export class IndexedDBKVStore implements KVStore { protected cachedDB?: IDBDatabase; constructor(protected readonly _prefix: string) {} - public async get(key: string): Promise { + public async get(key: string): Promise { const tx = (await this.getDB()).transaction([this.prefix()], "readonly"); const store = tx.objectStore(this.prefix()); @@ -26,7 +26,7 @@ export class IndexedDBKVStore implements KVStore { }); } - public async set(key: string, data: U | null): Promise { + public async set(key: string, data: T | null): Promise { if (data === null) { const tx = (await this.getDB()).transaction([this.prefix()], "readwrite"); const store = tx.objectStore(this.prefix()); diff --git a/packages/storage/src/store/types.ts b/packages/storage/src/store/types.ts index d8f3229eb..dde7e6c3a 100644 --- a/packages/storage/src/store/types.ts +++ b/packages/storage/src/store/types.ts @@ -1,6 +1,6 @@ export interface KVStore { - get(key: string): Promise; - set(key: string, data: U | null): Promise; + get(key: string): Promise; + set(key: string, data: T | null): Promise; prefix(): string; }