diff --git a/README.md b/README.md index ec6b643e..6ac4900e 100644 --- a/README.md +++ b/README.md @@ -111,7 +111,6 @@ Arguments: address Account address whose balance you want to query Options: - -p, --password Password for the account -e, --endpoint Runs entropy with the given endpoint and ignores network endpoints in config. Can also be given a stored endpoint name from config eg: `entropy --endpoint test-net`. (default: "ws://testnet.entropy.xyz:9944/", env: diff --git a/src/account/command.ts b/src/account/command.ts index a502a850..09ca2f9a 100644 --- a/src/account/command.ts +++ b/src/account/command.ts @@ -4,7 +4,7 @@ import { EntropyAccount } from "./main"; import { selectAndPersistNewAccount, addVerifyingKeyToAccountAndSelect } from "./utils"; import { ACCOUNTS_CONTENT } from './constants' import * as config from '../config' -import { cliWrite, accountOption, endpointOption, loadEntropy, passwordOption } from "../common/utils-cli"; +import { cliWrite, accountOption, endpointOption, loadEntropy } from "../common/utils-cli"; export function entropyAccountCommand () { return new Command('account') @@ -19,7 +19,6 @@ function entropyAccountCreate () { return new Command('create') .alias('new') .description('Create a new entropy account from scratch. Output is JSON of form {name, address}') - .addOption(passwordOption()) .argument('', 'A user friendly name for your new account.') .addOption( new Option( @@ -44,7 +43,6 @@ function entropyAccountCreate () { function entropyAccountImport () { return new Command('import') .description('Import an existing entropy account from seed. Output is JSON of form {name, address}') - .addOption(passwordOption()) .argument('', 'A user friendly name for your new account.') .argument('', 'The seed for the account you are importing') .addOption( @@ -72,7 +70,6 @@ function entropyAccountList () { .alias('ls') .description('List all accounts. Output is JSON of form [{ name, address, verifyingKeys }]') .action(async () => { - // TODO: test if it's an encrypted account, if no password provided, throw because later on there's no protection from a prompt coming up const storedConfig = await config.get() const accounts = EntropyAccount.list(storedConfig) cliWrite(accounts) @@ -84,7 +81,6 @@ function entropyAccountList () { function entropyAccountRegister () { return new Command('register') .description('Register an entropy account with a program') - .addOption(passwordOption()) .addOption(endpointOption()) .addOption(accountOption()) // Removing these options for now until we update the design to accept program configs diff --git a/src/account/main.ts b/src/account/main.ts index c1395dcb..638d1096 100644 --- a/src/account/main.ts +++ b/src/account/main.ts @@ -30,13 +30,11 @@ export class EntropyAccount extends EntropyBase { const data = fullAccount delete admin.pair - // const encryptedData = password ? passwordFlow.encrypt(data, password) : data return { name, address: admin.address, data - // data: encryptedData // TODO: replace once password input is added back } } diff --git a/src/balance/command.ts b/src/balance/command.ts index 4d0b023a..bb9b47f3 100644 --- a/src/balance/command.ts +++ b/src/balance/command.ts @@ -1,6 +1,6 @@ import { Command } from "commander"; import Entropy from "@entropyxyz/sdk"; -import { cliWrite, endpointOption, loadEntropy, passwordOption } from "src/common/utils-cli"; +import { cliWrite, endpointOption, loadEntropy } from "src/common/utils-cli"; import { EntropyBalance } from "./main"; export function entropyBalanceCommand () { @@ -8,7 +8,6 @@ export function entropyBalanceCommand () { balanceCommand .description('Command to retrieive the balance of an account on the Entropy Network') .argument('address', 'Account address whose balance you want to query') - .addOption(passwordOption()) .addOption(endpointOption()) .action(async (address, opts) => { const entropy: Entropy = await loadEntropy(address, opts.endpoint) diff --git a/src/common/initializeEntropy.ts b/src/common/initializeEntropy.ts index 13c2ffdf..48a75ab5 100644 --- a/src/common/initializeEntropy.ts +++ b/src/common/initializeEntropy.ts @@ -2,8 +2,6 @@ import Entropy, { wasmGlobalsReady } from "@entropyxyz/sdk" // TODO: fix importing of types from @entropy/sdk/keys // @ts-ignore import Keyring from "@entropyxyz/sdk/keys" -import inquirer from "inquirer" -import { decrypt, encrypt } from "../flows/password" import * as config from "../config" import { EntropyAccountData } from "../config/types" import { EntropyLogger } from "./logger" @@ -27,20 +25,19 @@ export function getKeyring (address?: string) { interface InitializeEntropyOpts { keyMaterial: MaybeKeyMaterial, - password?: string, endpoint: string, configPath?: string // for testing } type MaybeKeyMaterial = EntropyAccountData | string -// WARNING: in programatic cli mode this function should NEVER prompt users, but it will if no password was provided -// This is currently caught earlier in the code -export const initializeEntropy = async ({ keyMaterial, password, endpoint, configPath }: InitializeEntropyOpts): Promise => { +// WARNING: in programatic cli mode this function should NEVER prompt users + +export const initializeEntropy = async ({ keyMaterial, endpoint, configPath }: InitializeEntropyOpts): Promise => { const logger = new EntropyLogger('initializeEntropy', endpoint) try { await wasmGlobalsReady() - const { accountData, password: successfulPassword } = await getAccountDataAndPassword(keyMaterial, password) + const { accountData } = await getAccountData(keyMaterial) // check if there is no admin account and no seed so that we can throw an error if (!accountData.seed && !accountData.admin) { throw new Error("Data format is not recognized as either encrypted or unencrypted") @@ -52,12 +49,9 @@ export const initializeEntropy = async ({ keyMaterial, password, endpoint, confi const store = await config.get(configPath) store.accounts = store.accounts.map((account) => { if (account.address === accountData.admin.address) { - let data = accountData - // @ts-ignore - if (typeof account.data === 'string' ) data = encrypt(accountData, successfulPassword) account = { ...account, - data, + data: accountData, } } return account @@ -75,11 +69,9 @@ export const initializeEntropy = async ({ keyMaterial, password, endpoint, confi const store = await config.get(configPath) store.accounts = store.accounts.map((account) => { if (account.address === store.selectedAccount) { - let data = newAccountData - if (typeof account.data === 'string') data = encrypt(newAccountData, successfulPassword) const newAccount = { ...account, - data, + data: newAccountData, } return newAccount } @@ -122,10 +114,9 @@ export const initializeEntropy = async ({ keyMaterial, password, endpoint, confi // NOTE: frankie this was prettier before I had to refactor it for merge conflicts, promise -async function getAccountDataAndPassword (keyMaterial: MaybeKeyMaterial, password?: string): Promise<{ password: string | null, accountData: EntropyAccountData }> { +async function getAccountData (keyMaterial: MaybeKeyMaterial): Promise<{ accountData: EntropyAccountData }> { if (isEntropyAccountData(keyMaterial)) { return { - password: null, accountData: keyMaterial as EntropyAccountData } } @@ -133,54 +124,6 @@ async function getAccountDataAndPassword (keyMaterial: MaybeKeyMaterial, passwor if (typeof keyMaterial !== 'string') { throw new Error("Data format is not recognized as either encrypted or unencrypted") } - - /* Programmatic Mode */ - if (password) { - const decryptedData = decrypt(keyMaterial, password) - if (!isEntropyAccountData(decryptedData)) { - throw new Error("Failed to decrypt keyMaterial or decrypted keyMaterial is invalid") - } - // @ts-ignore TODO: some type work here - return { password, accountData: decryptedData } - } - - /* Interactive Mode */ - let sucessfulPassword: string - let decryptedData - let attempts = 0 - - while (attempts < 3) { - const answers = await inquirer.prompt([ - { - type: 'password', - name: 'password', - message: 'Enter password to decrypt keyMaterial:', - mask: '*', - } - ]) - - try { - decryptedData = decrypt(keyMaterial, answers.password) - //@ts-ignore - if (!isEntropyAccountData(decryptedData)) { - throw new Error("Failed to decrypt keyMaterial or decrypted keyMaterial is invalid") - } - - sucessfulPassword = answers.password - break - } catch (error) { - console.error("Incorrect password. Try again") - attempts++ - if (attempts >= 3) { - throw new Error("Failed to decrypt keyMaterial after 3 attempts.") - } - } - } - - return { - password: sucessfulPassword, - accountData: decryptedData as EntropyAccountData - } } function isEntropyAccountData (maybeAccountData: any) { diff --git a/src/common/utils-cli.ts b/src/common/utils-cli.ts index 3aec350b..8ef952e3 100644 --- a/src/common/utils-cli.ts +++ b/src/common/utils-cli.ts @@ -42,13 +42,6 @@ export function endpointOption () { // NOTE: argParser is only run IF an option is provided, so this cannot be 'test-net' } -export function passwordOption (description?: string) { - return new Option( - '-p, --password ', - description || 'Password for the account' - ) -} - export function accountOption () { const storedConfig = getConfigOrNull() @@ -76,17 +69,12 @@ export function accountOption () { // TODO: standardise whether selectedAccount is name or address. } -export async function loadEntropy (addressOrName: string, endpoint: string, password?: string): Promise { +export async function loadEntropy (addressOrName: string, endpoint: string): Promise { const accounts = getConfigOrNull()?.accounts || [] const selectedAccount = findAccountByAddressOrName(accounts, addressOrName) if (!selectedAccount) throw new Error(`No account with name or address: "${addressOrName}"`) - // check if data is encrypted + we have a password - if (typeof selectedAccount.data === 'string' && !password) { - throw new Error('AuthError: This account requires a password, add --password ') - } - - const entropy = await initializeEntropy({ keyMaterial: selectedAccount.data, endpoint, password }) + const entropy = await initializeEntropy({ keyMaterial: selectedAccount.data, endpoint }) if (!entropy?.keyring?.accounts?.registration?.pair) { throw new Error("Signer keypair is undefined or not properly initialized.") } diff --git a/src/flows/password/index.ts b/src/flows/password/index.ts deleted file mode 100644 index 876481c9..00000000 --- a/src/flows/password/index.ts +++ /dev/null @@ -1,61 +0,0 @@ -import * as crypto from 'crypto' - -export const questions = [ - { - type: 'password:', - name: 'password', - mask: '*', - when: ({ askForPassword }) => askForPassword, - }, - { - type: 'password:', - name: 'password', - mask: '*', - when: ({ newPassword }) => newPassword, - }, - { - type: 'password:', - message: 'confirm password', - name: 'confirmPassword', - mask: '*', - validate: (confirmPassword, { password }) => { return confirmPassword === password ? true : 'incorrect password' }, - when: ({ newPassword }) => newPassword - }, -] - - - -// Function to generate a key and IV using a password and salt -function generateKeyAndIV (password, salt) { - return crypto.scryptSync(password, salt, 32, { N: 2 ** 14 }).slice(0, 32) -} - -// Function to encrypt data with a password -export function encrypt (anyData, password) { - const data = JSON.stringify(anyData) - const salt = crypto.randomBytes(16) - const key = generateKeyAndIV(password, salt) - const cipher = crypto.createCipheriv('aes-256-gcm', key, Buffer.alloc(16)) - const encrypted = Buffer.concat([cipher.update(data, 'utf8'), cipher.final()]) - const tag = cipher.getAuthTag() - - return Buffer.concat([salt, tag, encrypted]).toString('base64') -} - -// Function to decrypt data with a password -export function decrypt (encryptedData, password) { - const buffer = Buffer.from(encryptedData, 'base64') - const salt = buffer.slice(0, 16) - const tag = buffer.slice(16, 32) - const data = buffer.slice(32) - - const key = generateKeyAndIV(password, salt) - const decipher = crypto.createDecipheriv('aes-256-gcm', key, Buffer.alloc(16)) - decipher.setAuthTag(tag) - - let returnData = decipher.update(data, null, 'utf8') + decipher.final('utf8') - try { - returnData = JSON.parse(returnData) - } catch (e) {/*swallo all parse errors*/} - return returnData -} diff --git a/src/sign/command.ts b/src/sign/command.ts index fc574228..ad66437d 100644 --- a/src/sign/command.ts +++ b/src/sign/command.ts @@ -1,12 +1,11 @@ import { Command, /* Option */ } from 'commander' -import { cliWrite, accountOption, endpointOption, loadEntropy, passwordOption } from '../common/utils-cli' +import { cliWrite, accountOption, endpointOption, loadEntropy } from '../common/utils-cli' import { EntropySign } from './main' export function entropySignCommand () { const signCommand = new Command('sign') .description('Sign a message using the Entropy network. Output is a JSON { verifyingKey, signature }') .argument('msg', 'Message you would like to sign (string)') - .addOption(passwordOption('Password for the source account (if required)')) .addOption(endpointOption()) .addOption(accountOption()) // .addOption( diff --git a/src/transfer/command.ts b/src/transfer/command.ts index b814e2f9..8832c8eb 100644 --- a/src/transfer/command.ts +++ b/src/transfer/command.ts @@ -1,5 +1,5 @@ import { Command } from "commander" -import { accountOption, endpointOption, loadEntropy, passwordOption } from "src/common/utils-cli" +import { accountOption, endpointOption, loadEntropy } from "src/common/utils-cli" import { EntropyTransfer } from "./main" export function entropyTransferCommand () { @@ -8,7 +8,6 @@ export function entropyTransferCommand () { .description('Transfer funds between two Entropy accounts.') // TODO: name the output .argument('destination', 'Account address funds will be sent to') .argument('amount', 'Amount of funds to be moved') - .addOption(passwordOption('Password for the source account (if required)')) .addOption(endpointOption()) .addOption(accountOption()) .action(async (destination, amount, opts) => {