diff --git a/es/account/memory.js b/es/account/memory.js index acf800d37d..0ae9b20068 100644 --- a/es/account/memory.js +++ b/es/account/memory.js @@ -24,6 +24,7 @@ import Account from './' import * as Crypto from '../utils/crypto' +import { isHex } from '../utils/string' const secrets = new WeakMap() @@ -37,7 +38,7 @@ async function address (format = Crypto.ADDRESS_FORMAT.api) { function setSecret (keyPair) { secrets.set(this, { - secretKey: Buffer.from(keyPair.secretKey, 'hex'), + secretKey: Buffer.isBuffer(keyPair.secretKey) ? keyPair.secretKey : Buffer.from(keyPair.secretKey, 'hex'), publicKey: keyPair.publicKey }) } @@ -48,7 +49,11 @@ function validateKeyPair (keyPair) { keyPair = { publicKey: keyPair.pub, secretKey: keyPair.priv } } if (!keyPair.secretKey || !keyPair.publicKey) throw new Error('KeyPair must must have "secretKey", "publicKey" properties') - if (typeof keyPair.publicKey !== 'string' || keyPair.publicKey.indexOf('ak_') === -1) throw new Error('Public Key must be a string with "ak_" prefix') + if (typeof keyPair.publicKey !== 'string' || keyPair.publicKey.indexOf('ak_') === -1) throw new Error('Public Key must be a base58c string with "ak_" prefix') + if ( + !Buffer.isBuffer(keyPair.secretKey) && + (typeof keyPair.secretKey === 'string' && !isHex(keyPair.secretKey)) + ) throw new Error('Secret key must be hex string or Buffer') } /** diff --git a/es/account/selector.js b/es/account/selector.js index d2ed697aec..e174d56784 100644 --- a/es/account/selector.js +++ b/es/account/selector.js @@ -49,7 +49,7 @@ async function address ({ onAccount } = {}) { * @example selectAccount('ak_xxxxxxxx') */ function selectAccount (address) { - if (address && !assertedType(address, 'ak', true)) throw new Error(`Invalid account address`) + if (!address || !assertedType(address, 'ak', true)) throw new Error(`Invalid account address`) this.Selector.address = address } diff --git a/es/utils/crypto.js b/es/utils/crypto.js index c4c55c8830..b2f45280ba 100644 --- a/es/utils/crypto.js +++ b/es/utils/crypto.js @@ -454,6 +454,7 @@ export function decryptPubKey (password, encrypted) { * @rtype (data: String, type: String) => String, throws: Error * @param {String} data - ae data * @param {String} type - Prefix + * @param forceError * @return {String} Payload */ export function assertedType (data, type, forceError = false) { @@ -461,6 +462,7 @@ export function assertedType (data, type, forceError = false) { return data.split('_')[1] } else { if (!forceError) throw Error(`Data doesn't match expected type ${type}`) + return false } } diff --git a/es/utils/keystore.js b/es/utils/keystore.js index 7c27b4f78c..64cac70f23 100644 --- a/es/utils/keystore.js +++ b/es/utils/keystore.js @@ -2,6 +2,7 @@ import nacl from 'tweetnacl' import uuid from 'uuid' import { encodeBase58Check, isBase64 } from './crypto' +import { isHex } from './string' /** * KeyStore module @@ -70,15 +71,6 @@ function decryptXsalsa20Poly1305 ({ ciphertext, key, nonce }) { return res } -/** - * Check whether a string is valid hex. - * @param {string} str String to validate. - * @return {boolean} True if the string is valid hex, false otherwise. - */ -function isHex (str) { - return !!(str.length % 2 === 0 && str.match(/^[0-9a-f]+$/i)) -} - /** * Convert a string to a Buffer. If encoding is not specified, hex-encoding * will be used if the input is valid hex. If the input is valid base64 but diff --git a/es/utils/string.js b/es/utils/string.js index b0d7b4b0a8..9c2a2678c1 100644 --- a/es/utils/string.js +++ b/es/utils/string.js @@ -46,3 +46,12 @@ export function snakeOrKebabToPascal (s) { export function pascalToSnake (s) { return s.replace(/[A-Z]/g, match => `_${R.toLower(match)}`) } + +/** + * Check whether a string is valid hex. + * @param {string} str String to validate. + * @return {boolean} True if the string is valid hex, false otherwise. + */ +export function isHex (str) { + return !!(str.length % 2 === 0 && str.match(/^[0-9a-f]+$/i)) +} diff --git a/test/unit/memory-account.js b/test/unit/memory-account.js new file mode 100644 index 0000000000..160bf0a4e2 --- /dev/null +++ b/test/unit/memory-account.js @@ -0,0 +1,84 @@ +/* + * ISC License (ISC) + * Copyright (c) 2018 aeternity developers + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +import '../' +import { describe, it } from 'mocha' +import MemoryAccount from '../../es/account/memory' +import { generateKeyPair } from '../../es/utils/crypto' + +const testAcc = generateKeyPair() + +describe('MemoryAccount', function () { + describe('Fail on invalid params', () => { + it('Fail on empty keypair', async () => { + try { + MemoryAccount({}) + } catch (e) { + e.message.should.be.equal('KeyPair must be an object') + } + }) + it('Fail on empty keypair object', async () => { + try { + MemoryAccount({ keypair: {} }) + } catch (e) { + e.message.should.be.equal('KeyPair must must have "secretKey", "publicKey" properties') + } + }) + it('Fail on invalid secret key', async () => { + try { + MemoryAccount({ + keypair: { + publicKey: testAcc.publicKey, + secretKey: ' ' + } + }) + } catch (e) { + e.message.should.be.equal('Secret key must be hex string or Buffer') + } + }) + it('Fail on invalid publicKey', async () => { + try { + MemoryAccount({ + keypair: { + publicKey: ' ', + secretKey: testAcc.secretKey + } + }) + } catch (e) { + e.message.should.be.equal('Public Key must be a base58c string with "ak_" prefix') + } + }) + }) + it('Init with secretKey as hex string', async () => { + const acc = MemoryAccount({ + keypair: { + publicKey: testAcc.publicKey, + secretKey: testAcc.secretKey + } + }) + return acc.address().should.eventually.be.equal(testAcc.publicKey) + }) + it('Init with secretKey as hex Buffer', async () => { + const acc = MemoryAccount({ + keypair: { + publicKey: testAcc.publicKey, + secretKey: Buffer.from(testAcc.secretKey, 'hex') + } + }) + return acc.address().should.eventually.be.equal(testAcc.publicKey) + }) +})