Newer
Older
CrypticOreWallet / src / wallets / WalletManager.ts
@Drew Lemmy Drew Lemmy on 28 Sep 2020 2 KB feat: start on wallets in redux
import { toHex } from "@utils";
import { aesGcmEncrypt, aesGcmDecrypt } from "@utils/crypto";

import { AppDispatch } from "@app/App";
import * as actions from "@actions/WalletManagerActions";

import { loadWallets } from "../wallets/Wallet";

export function browseAsGuest(dispatch: AppDispatch): void {
  dispatch(actions.browseAsGuest());
}

/** Verifies that the given password is correct, and dispatches the login
 * action to the Redux store. */
export async function login(dispatch: AppDispatch, salt: string | undefined, tester: string | undefined, password: string): Promise<void> {
  if (!password) throw new Error("masterPassword.errorPasswordRequired");
  if (!salt || !tester) throw new Error("masterPassword.errorPasswordUnset");

  try {
    // Attempt to decrypt the tester with the given password
    const testerDec = await aesGcmDecrypt(tester, password);

    // Verify that the decrypted tester is equal to the salt, if not, the
    // provided master password is incorrect.
    if (testerDec !== salt) throw new Error("masterPassword.errorPasswordIncorrect");
  } catch (e) {
    // OperationError usually means decryption failure
    if (e.name === "OperationError") throw new Error("masterPassword.errorPasswordIncorrect");
    else throw e;
  }

  // Load the wallets, dispatching the wallets to the Redux store
  loadWallets(dispatch, password);

  // Dispatch the login state changes to the Redux store
  dispatch(actions.login(password));
}

/** Generates a salt and tester, sets the master password, and dispatches the
 * action to the Redux store. */
export async function setMasterPassword(dispatch: AppDispatch, password: string): Promise<void> {
  if (!password) throw new Error("Password is required.");

  // Generate the salt (to be encrypted with the master password)
  const salt = window.crypto.getRandomValues(new Uint8Array(32));
  const saltHex = toHex(salt);

  // Generate the encryption tester
  const tester = await aesGcmEncrypt(saltHex, password);

  // Store them in local storage
  localStorage.setItem("salt", saltHex);
  localStorage.setItem("tester", tester);

  // Dispatch the login state changes to the Redux store
  dispatch(actions.setMasterPassword(saltHex, tester, password));
}