diff --git a/packages/ripple-keypairs/package.json b/packages/ripple-keypairs/package.json index 4611a080..50164c39 100644 --- a/packages/ripple-keypairs/package.json +++ b/packages/ripple-keypairs/package.json @@ -15,39 +15,27 @@ }, "dependencies": { "babel-runtime": "^5.3.2", - "bn.js": "^2.0.5", - "elliptic": "sublimator/elliptic#derivations-test", + "bn.js": "^2.1.0", + "elliptic": "^4.0.0", "hash.js": "^1.0.3", - "ripple-address-codec": "github:sublimator/ripple-address-codec" + "ripple-address-codec": "^1.0.1" }, "devDependencies": { "assert-diff": "^1.0.1", "babel": "^5.3.3", "babel-core": "^5.3.2", - "lodash": "^3.9.3", "babel-loader": "^5.0.0", "coveralls": "~2.10.0", - "eslint": "^0.18.0", + "eslint": "^0.23.0", "eventemitter2": "^0.4.14", - "gulp": "~3.8.10", - "gulp-bump": "~0.1.13", - "gulp-clean-dest": "^0.1.0", - "gulp-filelog": "^0.4.1", - "gulp-flowtype": "^0.4.1", - "gulp-plumber": "^0.6.6", - "gulp-react": "^2.0.0", - "gulp-rename": "~1.2.0", - "gulp-uglify": "~1.1.0", - "gulp-util": "^3.0.3", - "gulp-watch": "^4.1.0", "istanbul": "~0.3.5", + "lodash": "^3.9.3", "map-stream": "~0.1.0", "mocha": "~2.1.0", "nock": "^0.34.1", + "ripple-lib": "ripple/ripple-lib#8d98e443c5302a87d70410d834758099595d46df", "webpack": "~1.5.3", - "yargs": "~1.3.1", - "hash.js": "^1.0.3", - "bn.js": "^2.0.5" + "yargs": "~1.3.1" }, "scripts": { "build": "gulp", @@ -56,7 +44,7 @@ "prepublish": "npm run compile", "test": "istanbul test _mocha", "coveralls": "cat ./coverage/lcov.info | coveralls", - "lint": "if ! [ -f eslintrc ]; then curl -o eslintrc 'https://raw.githubusercontent.com/ripple/javascript-style-guide/es6/eslintrc'; fi; eslint --reset -c eslintrc src/*.js" + "lint": "if ! [ -f eslintrc ]; then curl -o eslintrc 'https://raw.githubusercontent.com/ripple/javascript-style-guide/es6/eslintrc'; fi; eslint --reset -c eslintrc src/*.js test/*.js" }, "repository": { "type": "git", diff --git a/packages/ripple-keypairs/src/index.js b/packages/ripple-keypairs/src/index.js index 471e6937..30800f72 100644 --- a/packages/ripple-keypairs/src/index.js +++ b/packages/ripple-keypairs/src/index.js @@ -4,7 +4,6 @@ const util = require('util'); const elliptic = require('elliptic'); -const bnjs = require('bn.js'); const hashjs = require('hash.js'); const codec = require('ripple-address-codec'); @@ -13,12 +12,10 @@ const secp256k1 = elliptic.ec('secp256k1'); const Ed25519 = elliptic.eddsa('ed25519'); const { - arrayToHex, bytesToHex, hasCachedProperty, isVirtual, Sha512, - toGenericArray } = require('./utils'); /* ---------------------------------- ENUMS --------------------------------- */ @@ -58,7 +55,9 @@ function findk256Key(bytes, discrim) { function compressedPoint(p) { const x = p.getX().toArray(); const y = p.getY(); - while (x.length < 32) x.unshift(0) + while (x.length < 32) { + x.unshift(0); + } return [y.isEven() ? 0x02 : 0x03].concat(x); } @@ -92,7 +91,7 @@ function derivek256Secret(seed, opts={}) { } function createAccountId(pubKeyBytes) { - const hash256 = hashjs.sha256().update(pubKeyBytes).digest() + const hash256 = hashjs.sha256().update(pubKeyBytes).digest(); const hash160 = hashjs.ripemd160().update(hash256).digest(); return hash160; } @@ -192,7 +191,7 @@ Ed25519Pair.prototype.verify = function(message, signature) { function Secp256k1Pair(key) { KeyPair.apply(this, arguments); this.type = KeyType.secp256k1; - this.key = key + this.key = key; } util.inherits(Secp256k1Pair, KeyPair); @@ -249,4 +248,4 @@ module.exports = { Ed25519Pair, KeyType, seedFromPhrase -} +}; diff --git a/packages/ripple-keypairs/src/utils.js b/packages/ripple-keypairs/src/utils.js index d93fd112..dc835e97 100644 --- a/packages/ripple-keypairs/src/utils.js +++ b/packages/ripple-keypairs/src/utils.js @@ -1,6 +1,6 @@ -/* -------------------------------- REQUIRES -------------------------------- */ +'use strict'; -const bnjs = require('bn.js'); +const BigNum = require('bn.js'); const hashjs = require('hash.js'); function isVirtual() { @@ -11,7 +11,7 @@ function hasCachedProperty(obj, name, computer) { const key = name + '__'; obj.prototype[name] = function() { let cached = this[key]; - if(cached === undefined) { + if (cached === undefined) { cached = this[key] = computer.apply(this, arguments); } return cached; @@ -36,25 +36,25 @@ function bytesToHex(bytes) { } class Sha512 { - constructor () { + constructor() { this.hash = hashjs.sha512(); } - add (bytes) { + add(bytes) { this.hash.update(bytes); return this; } - addU32 (i) { + addU32(i) { return this.add([(i >>> 24) & 0xFF, (i >>> 16) & 0xFF, - (i >>> 8) & 0xFF, i & 0xFF ]); + (i >>> 8) & 0xFF, i & 0xFF]); } - finish () { + finish() { return this.hash.digest(); } - finish256 () { + finish256() { return this.finish().slice(0, 32); } - finish256BN () { - return new bnjs(this.finish256()); + finish256BN() { + return new BigNum(this.finish256()); } } @@ -65,4 +65,4 @@ module.exports = { isVirtual, Sha512, toGenericArray -} \ No newline at end of file +}; diff --git a/packages/ripple-keypairs/test/keypairs-test.js b/packages/ripple-keypairs/test/keypairs-test.js index 00bd51e7..66d05f8c 100644 --- a/packages/ripple-keypairs/test/keypairs-test.js +++ b/packages/ripple-keypairs/test/keypairs-test.js @@ -1,50 +1,96 @@ -const assert = require('assert'); -const _ = require('lodash'); -const utils = require('../src/utils'); -const codec = require('ripple-address-codec'); -const {Secp256k1Pair, seedFromPhrase, Ed25519Pair} = require('../src'); -const ZEROES = [0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; +'use strict'; -describe('KeyPair', function() { +const assert = require('assert'); +const utils = require('../src/utils'); +const {KeyType, Secp256k1Pair, seedFromPhrase, Ed25519Pair} = require('../src'); + +const {SerializedObject} = require('ripple-lib'); +const TX_HASH_PREFIX_SIGN = [0x53, 0x54, 0x58, 0x00]; + +const FIXTURES = { + message: [0xB, 0xE, 0xE, 0xF], + + tx_json: { + Account: 'rJZdUusLDtY9NEsGea7ijqhVrXv98rYBYN', + Amount: '1000', + Destination: 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', + Fee: '10', + Flags: 2147483648, + Sequence: 1, + SigningPubKey: 'EDD3993CDC6647896C455F136648B7750' + + '723B011475547AF60691AA3D7438E021D', + TransactionType: 'Payment', + expected_sig: 'C3646313B08EED6AF4392261A31B961F' + + '10C66CB733DB7F6CD9EAB079857834C8' + + 'B0334270A2C037E63CDCCC1932E08328' + + '82B7B7066ECD2FAEDEB4A83DF8AE6303' + } +}; + +describe('ED25519Pair', function() { let pair; - it('can be created from a passphrase', function() { - const seed = seedFromPhrase('niq'); - const key = Ed25519Pair.fromSeed(seed); - assert.equal(key.accountID(), 'rJZdUusLDtY9NEsGea7ijqhVrXv98rYBYN'); - assert.equal(codec.encodeSeed(seed), 'shQUG1pmPYrcnSUGeuJFJTA1b3JSL'); - }) - - before(function function_name () { - pair = Secp256k1Pair.fromSeed(ZEROES); + before(function() { + pair = Ed25519Pair.fromSeed(seedFromPhrase('niq')); }); - it('can be instantiated', function() { - const sig = pair.sign(ZEROES); - assert(pair.verify(ZEROES, sig)); + it('has a String member `type` equal to KeyPair.ed25519 constant', + function() { + assert.equal(pair.type, KeyType.ed25519); + }); + + it('has a public key representation beginning with ED', function() { + const pub_hex = pair.pubKeyHex(); + assert(pub_hex.length === 66); + assert(pub_hex.slice(0, 2) === 'ED'); + }); + it('derives the same keypair for a given passphrase as rippled', function() { + const pub_hex = pair.pubKeyHex(); + const target_hex = 'EDD3993CDC6647896C455F136648B7750' + + '723B011475547AF60691AA3D7438E021D'; + assert.equal(pub_hex, target_hex); + }); + it('generates the same account_id as rippled for a given keypair', + function() { + assert.equal(pair.accountID(), + 'rJZdUusLDtY9NEsGea7ijqhVrXv98rYBYN'); + }); + it('creates signatures that are a function of secret/message', function() { + const signature = pair.sign(FIXTURES.message); + assert(Array.isArray(signature)); + assert(pair.verify(FIXTURES.message, signature)); + }); + it('signs transactions exactly as rippled', function() { + const so = SerializedObject.from_json(FIXTURES.tx_json); + const message = TX_HASH_PREFIX_SIGN.concat(so.buffer); + const sig = pair.signHex(message); + assert.equal(sig, FIXTURES.tx_json.expected_sig); }); }); -describe('generated tests', function() { - const expected = [ - '30440220312B2E0894B81A2E070ACE566C5DFC70CDD18E67D44E2CFEF2EB5495F7DE2DAC02205E155C0019502948C265209DFDD7D84C4A05BD2C38CEE6ECD7C33E9C9B12BEC2', - '304402202A5860A12C15EBB8E91AA83F8E19D85D4AC05B272FC0C4083519339A7A76F2B802200852F9889E1284CF407DC7F73D646E62044C5AB432EAEF3FFF3F6F8EE9A0F24C', - '3045022100B1658C88D1860D9F8BEB25B79B3E5137BBC2C382D08FE7A068FFC6AB8978C8040220644F64B97EA144EE7D5CCB71C2372DD730FA0A659E4C18241A80D6C915350263', - '3045022100F3E541330FF79FFC42EB0491EDE1E47106D94ECFE3CDB2D9DD3BC0E8861F6D45022013F62942DD626D6C9731E317F372EC5C1F72885C4727FDBEE9D9321BC530D7B2', - '3045022100998ABE378F4119D8BEE9843482C09F0D5CE5C6012921548182454C610C57A269022036BD8EB71235C4B2C67339DE6A59746B1F7E5975987B7AB99B313D124A69BB9F' - ]; - const key = Secp256k1Pair.fromSeed(seedFromPhrase('niq')); - function test_factory(i) { - it('can deterministically sign/verify message [' + i + - ']', function() { - const message = [i]; - const sig = key.sign(message); - assert.equal(utils.arrayToHex(sig), expected[i]); - assert(key.verify(message, sig)); - }); - } +describe('Secp256k1Pair', function() { + describe('generated tests', function() { + /*eslint-disable max-len*/ + const expected = [ + '30440220312B2E0894B81A2E070ACE566C5DFC70CDD18E67D44E2CFEF2EB5495F7DE2DAC02205E155C0019502948C265209DFDD7D84C4A05BD2C38CEE6ECD7C33E9C9B12BEC2', + '304402202A5860A12C15EBB8E91AA83F8E19D85D4AC05B272FC0C4083519339A7A76F2B802200852F9889E1284CF407DC7F73D646E62044C5AB432EAEF3FFF3F6F8EE9A0F24C', + '3045022100B1658C88D1860D9F8BEB25B79B3E5137BBC2C382D08FE7A068FFC6AB8978C8040220644F64B97EA144EE7D5CCB71C2372DD730FA0A659E4C18241A80D6C915350263', + '3045022100F3E541330FF79FFC42EB0491EDE1E47106D94ECFE3CDB2D9DD3BC0E8861F6D45022013F62942DD626D6C9731E317F372EC5C1F72885C4727FDBEE9D9321BC530D7B2', + '3045022100998ABE378F4119D8BEE9843482C09F0D5CE5C6012921548182454C610C57A269022036BD8EB71235C4B2C67339DE6A59746B1F7E5975987B7AB99B313D124A69BB9F' + ]; + /*eslint-enable max-len*/ + const key = Secp256k1Pair.fromSeed(seedFromPhrase('niq')); + function test_factory(i) { + it('can deterministically sign/verify message [' + i + ']', function() { + const message = [i]; + const sig = key.sign(message); + assert.equal(utils.arrayToHex(sig), expected[i]); + assert(key.verify(message, sig)); + }); + } - for (let n = 0; n < 5; n++) { - test_factory(n); - } -}); \ No newline at end of file + for (let n = 0; n < 5; n++) { + test_factory(n); + } + }); +});