Skip to content

Commit

Permalink
feat(MemoryAccount): Add validation of keypair (#594)
Browse files Browse the repository at this point in the history
* feat(MemoryAccount): Add validation of keypair.
Cover by tests

* fix(Account): condition in selectAccount
  • Loading branch information
nduchak committed Aug 9, 2019
1 parent 9aaa05c commit b8c2b20
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 12 deletions.
9 changes: 7 additions & 2 deletions es/account/memory.js
Expand Up @@ -24,6 +24,7 @@

import Account from './'
import * as Crypto from '../utils/crypto'
import { isHex } from '../utils/string'

const secrets = new WeakMap()

Expand All @@ -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
})
}
Expand All @@ -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')
}

/**
Expand Down
2 changes: 1 addition & 1 deletion es/account/selector.js
Expand Up @@ -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
}

Expand Down
2 changes: 2 additions & 0 deletions es/utils/crypto.js
Expand Up @@ -454,13 +454,15 @@ 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) {
if (RegExp(`^${type}_.+$`).test(data)) {
return data.split('_')[1]
} else {
if (!forceError) throw Error(`Data doesn't match expected type ${type}`)
return false
}
}

Expand Down
10 changes: 1 addition & 9 deletions es/utils/keystore.js
Expand Up @@ -2,6 +2,7 @@ import nacl from 'tweetnacl'
import uuid from 'uuid'

import { encodeBase58Check, isBase64 } from './crypto'
import { isHex } from './string'

/**
* KeyStore module
Expand Down Expand Up @@ -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
Expand Down
9 changes: 9 additions & 0 deletions es/utils/string.js
Expand Up @@ -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))
}
84 changes: 84 additions & 0 deletions 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)
})
})

0 comments on commit b8c2b20

Please sign in to comment.