diff --git a/__tests__/common/data.js b/__tests__/common/data.js index 747b8d5..17d0330 100644 --- a/__tests__/common/data.js +++ b/__tests__/common/data.js @@ -4,11 +4,7 @@ const INVALID_SEEDS = [ 'z947ee0115014a4d49a804e7fc7248e31690b80033ce7a6e3a07bdf93b2584ff' ] -const INVALID_INDEXES = [ - '0', - -1, - 1.1 -] +const INVALID_INDEXES = ['0', -1, 1.1] const INVALID_SECRET_KEYS = [ 12, @@ -28,14 +24,11 @@ const INVALID_HASHES = [ 'zf7122e843b27524f4f1d6bd14aefd1c8f01d36ae8653d37417533c0d4bc2be6' ] -const INVALID_WORKS = [ - 12, - '000000000995bc3', - 'z000000000995bc3' -] +const INVALID_WORKS = [12, '000000000995bc3', 'z000000000995bc3'] const INVALID_ADDRESSES = [ 12, + 'xrb_1mbj7xi6yrwcuwetzd5535pdqjea5rfpsoqo9nw4gxg8itycgntucp49i1n4', // bad checksum 'zrb_1mbj7xi6yrwcuwetzd5535pdqjea5rfpsoqo9nw4gxg8itycgntucp49i1nz', 'xrb_2mbj7xi6yrwcuwetzd5535pdqjea5rfpsoqo9nw4gxg8itycgntucp49i1nz', 'xrb_1mbj7xi6yrwcuwetzd5535pdqjea5rfpsoqo9nw4gxg8itycgntucp49i1n', diff --git a/package.json b/package.json index 07e73e5..4547f5d 100644 --- a/package.json +++ b/package.json @@ -16,10 +16,9 @@ }, "bugs": "https://github.com/marvinroger/nanocurrency-js/issues", "dependencies": { - "base32-decode": "^1.0.0", - "base32-encode": "^1.0.0", "bignumber.js": "^6.0.0", - "blakejs": "^1.1.0" + "blakejs": "^1.1.0", + "nano-base32": "^1.0.0" }, "devDependencies": { "cross-env": "^5.1.3", diff --git a/src/check.js b/src/check.js index 93f47af..b6e80fb 100644 --- a/src/check.js +++ b/src/check.js @@ -3,6 +3,11 @@ * Copyright (c) 2018 Marvin ROGER * Licensed under GPL-3.0 (https://git.io/vAZsK) */ +import { blake2b } from 'blakejs' +import nanoBase32 from 'nano-base32' + +import { compareArrays } from './utils' + export function checkString (candidate) { return typeof candidate === 'string' } @@ -79,7 +84,16 @@ export function checkKey (key) { * @return {boolean} Valid */ export function checkAddress (address) { - return checkString(address) && /xrb_[13][0-9a-km-uw-z]{59}/.test(address) + if (!checkString(address) || !/xrb_[13][0-9a-km-uw-z]{59}/.test(address)) { + return false + } + + const publicKeyBytes = nanoBase32.decode(address.substr(4, 52)) + const checksumBytes = nanoBase32.decode(address.substr(56, 8)) + + const computedChecksumBytes = blake2b(publicKeyBytes, null, 5).reverse() + + return compareArrays(checksumBytes, computedChecksumBytes) } /** diff --git a/src/keys.js b/src/keys.js index 7b6c65a..fd6a950 100644 --- a/src/keys.js +++ b/src/keys.js @@ -4,16 +4,11 @@ * Licensed under GPL-3.0 (https://git.io/vAZsK) */ import { blake2b, blake2bInit, blake2bUpdate, blake2bFinal } from 'blakejs' +import nanoBase32 from 'nano-base32' import { checkSeed, checkKey, checkAddress } from './check' import { derivePublicFromSecret } from './nacl' -import { - getRandomBytes, - hexToByteArray, - byteArrayToHex, - byteArrayToBase32, - base32ToByteArray -} from './utils' +import { getRandomBytes, hexToByteArray, byteArrayToHex } from './utils' /** * Generate a cryptographically secure seed. @@ -77,18 +72,15 @@ export function derivePublicKey (secretKeyOrAddress) { throw new Error('Secret key or address is not valid') } + let publicKeyBytes if (isSecretKey) { const secretKeyBytes = hexToByteArray(secretKeyOrAddress) - const publicKeyBytes = derivePublicFromSecret(secretKeyBytes) - - return byteArrayToHex(publicKeyBytes) + publicKeyBytes = derivePublicFromSecret(secretKeyBytes) } else if (isAddress) { - const publicKeyPart = secretKeyOrAddress.substr(4, 52) + '1' - const paddedPublicKeyBytes = base32ToByteArray(publicKeyPart) - const publicKeyHex = byteArrayToHex(paddedPublicKeyBytes).substr(1, 64) - - return publicKeyHex + publicKeyBytes = nanoBase32.decode(secretKeyOrAddress.substr(4, 52)) } + + return byteArrayToHex(publicKeyBytes) } /** @@ -102,13 +94,13 @@ export function deriveAddress (publicKey) { if (!checkKey(publicKey)) throw new Error('Public key is not valid') const publicKeyBytes = hexToByteArray(publicKey) - const paddedPublicKeyBytes = hexToByteArray('0' + publicKey + '0') + const paddedPublicKeyBytes = hexToByteArray(publicKey) - const encodedPublicKey = byteArrayToBase32(paddedPublicKeyBytes).slice(0, -1) + const encodedPublicKey = nanoBase32.encode(paddedPublicKeyBytes) const checksum = blake2b(publicKeyBytes, null, 5).reverse() - const encodedChecksum = byteArrayToBase32(checksum) + const encodedChecksum = nanoBase32.encode(checksum) return 'xrb_' + encodedPublicKey + encodedChecksum } diff --git a/src/utils.js b/src/utils.js index ecc2761..7c5d26d 100644 --- a/src/utils.js +++ b/src/utils.js @@ -3,9 +3,6 @@ * Copyright (c) 2018 Marvin ROGER * Licensed under GPL-3.0 (https://git.io/vAZsK) */ -import base32Encode from 'base32-encode' -import base32Decode from 'base32-decode' - const IS_NODE = Object.prototype.toString.call( typeof process !== 'undefined' ? process : 0 @@ -35,61 +32,6 @@ export function getRandomBytes (count) { }) } -const BASE32_MAPPING = { - A: '1', - B: '3', - C: '4', - D: '5', - E: '6', - F: '7', - G: '8', - H: '9', - I: 'a', - J: 'b', - K: 'c', - L: 'd', - M: 'e', - N: 'f', - O: 'g', - P: 'h', - Q: 'i', - R: 'j', - S: 'k', - T: 'm', - U: 'n', - V: 'o', - W: 'p', - X: 'q', - Y: 'r', - Z: 's', - 2: 't', - 3: 'u', - 4: 'w', - 5: 'x', - 6: 'y', - 7: 'z' -} - -export function byteArrayToBase32 (byteArray) { - return base32Encode(byteArray, 'RFC4648') - .split('') - .map(c => BASE32_MAPPING[c]) - .join('') -} - -export function base32ToByteArray (base32) { - const mapped = base32 - .split('') - .map(c => - Object.keys(BASE32_MAPPING).find(key => BASE32_MAPPING[key] === c) - ) - .join('') - - const decoded = base32Decode(mapped, 'RFC4648') - - return new Uint8Array(decoded) -} - export function byteArrayToHex (byteArray) { if (!byteArray) { return '' @@ -117,3 +59,11 @@ export function hexToByteArray (hex) { return new Uint8Array(a) } + +export function compareArrays (array1, array2) { + for (let i = 0; i < array1.length; i++) { + if (array1[i] !== array2[i]) return false + } + + return true +} diff --git a/yarn.lock b/yarn.lock index 84c9fdf..0392a4e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1073,14 +1073,6 @@ balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" -base32-decode@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/base32-decode/-/base32-decode-1.0.0.tgz#2a821d6a664890c872f20aa9aca95a4b4b80e2a7" - -base32-encode@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/base32-encode/-/base32-encode-1.0.0.tgz#8fda8e42ab5e213e8ec30e5c440a26ae0507eb03" - base@^0.11.1: version "0.11.2" resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" @@ -4164,6 +4156,10 @@ nan@^2.3.0: version "2.9.2" resolved "https://registry.yarnpkg.com/nan/-/nan-2.9.2.tgz#f564d75f5f8f36a6d9456cca7a6c4fe488ab7866" +nano-base32@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/nano-base32/-/nano-base32-1.0.0.tgz#eda445dba06aeb18cff786fdb9fa4f6c5f5c6791" + nanomatch@^1.2.9: version "1.2.9" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.9.tgz#879f7150cb2dab7a471259066c104eee6e0fa7c2"