Break up index.js into logical files

This commit is contained in:
Nicholas Dudfield
2015-07-31 11:23:34 +07:00
parent 00191fc5b3
commit c6c92b18cc
9 changed files with 283 additions and 261 deletions

View File

@@ -4,6 +4,7 @@ node_js:
script: script:
- npm test --coverage - npm test --coverage
- npm run-script coveralls - npm run-script coveralls
- npm run-script lint
env: env:
global: global:
secure: VzkZvd0TM1m40yfahQKc5ITa/843l8BB0Sr8fA7O0RcMECTg/xBlWU4Paby2V+JWumgCJDx5YvxLlAdHeUaCWDkGlbJLPu8uPSRMZHRNH3PN2TNogNtruXmb9Z1ORZc5CwLNstWdK4MzMqErevPTxCxJ03scuzZxMiiEZpTOv+aQLZ7jEqTiA20q696QfsYpMvVB0XSpMEYqqFRdpMxGhf0QZ+bNlRN8h1SGw6ehw7jFKubQ1DozSKug10RU6aAGUHyaBisjc9tsSB1atJraeJUXSMucYD1L1oXWl0agE2RBSO8PAozwdJMoifHrVvQtW9cSuw9hPgiwd2qSNoH6IvhoIxeSEhqdVaS/0d22YKUxY+HkhYeIKCm+6kd0tcDfZ8+FqB7654ZhyIgCeOf1NcENu1OMaehbtKcALSBUi8yrDDVVntJFjBoKmPtdCAMq4iTL+28oBB7aDndUTjuyqEtPyN4qMY1R9bGcn/DMIwhBrRzIcM3MwPnn2S/+RUpr/NwyXXy7MgYXeVJE61sps/vA1JklwnRebL3c2nl/JZZrrWSaHquTMVDbkoxQHJhUR4vOQ/KzfQEQoqRZY9w/CO7HmVFMOo6RH3i5ZSNRN6YtOndGvUHFRmqIBf5iRaZiEgDb9xX1XmS4LzOR0dhHI3Gl/y3YAx8pbc7TeNrq4UQ= secure: VzkZvd0TM1m40yfahQKc5ITa/843l8BB0Sr8fA7O0RcMECTg/xBlWU4Paby2V+JWumgCJDx5YvxLlAdHeUaCWDkGlbJLPu8uPSRMZHRNH3PN2TNogNtruXmb9Z1ORZc5CwLNstWdK4MzMqErevPTxCxJ03scuzZxMiiEZpTOv+aQLZ7jEqTiA20q696QfsYpMvVB0XSpMEYqqFRdpMxGhf0QZ+bNlRN8h1SGw6ehw7jFKubQ1DozSKug10RU6aAGUHyaBisjc9tsSB1atJraeJUXSMucYD1L1oXWl0agE2RBSO8PAozwdJMoifHrVvQtW9cSuw9hPgiwd2qSNoH6IvhoIxeSEhqdVaS/0d22YKUxY+HkhYeIKCm+6kd0tcDfZ8+FqB7654ZhyIgCeOf1NcENu1OMaehbtKcALSBUi8yrDDVVntJFjBoKmPtdCAMq4iTL+28oBB7aDndUTjuyqEtPyN4qMY1R9bGcn/DMIwhBrRzIcM3MwPnn2S/+RUpr/NwyXXy7MgYXeVJE61sps/vA1JklwnRebL3c2nl/JZZrrWSaHquTMVDbkoxQHJhUR4vOQ/KzfQEQoqRZY9w/CO7HmVFMOo6RH3i5ZSNRN6YtOndGvUHFRmqIBf5iRaZiEgDb9xX1XmS4LzOR0dhHI3Gl/y3YAx8pbc7TeNrq4UQ=

View File

@@ -1,8 +1,6 @@
# ripple-keypairs # ripple-keypairs
[![NPM](https://nodei.co/npm/ripple-keypairs.png)](https://npmjs.org/package/ripple-keypairs) [![NPM](https://img.shields.io/npm/v/ripple-keypairs.svg)](https://npmjs.org/package/ripple-keypairs) [![Build Status](https://img.shields.io/travis/sublimator/ripple-keypairs/master.svg)](https://travis-ci.org/sublimator/ripple-keypairs) [![Coverage Status](https://img.shields.io/coveralls/sublimator/ripple-keypairs/master.svg)](https://coveralls.io/github/sublimator/ripple-keypairs?branch=master)
[![Build Status](https://travis-ci.org/sublimator/ripple-keypairs.svg?branch=master)](https://travis-ci.org/sublimator/ripple-keypairs) [![Coverage Status](https://coveralls.io/repos/sublimator/ripple-keypairs/badge.svg?branch=master&service=github&v=1)](https://coveralls.io/github/sublimator/ripple-keypairs?branch=master)
An implementation of ripple keypairs & wallet generation using An implementation of ripple keypairs & wallet generation using
[elliptic](https://github.com/indutny/elliptic) which supports rfc6979 and [elliptic](https://github.com/indutny/elliptic) which supports rfc6979 and

View File

@@ -1,6 +1,6 @@
{ {
"name": "ripple-keypairs", "name": "ripple-keypairs",
"version": "0.5.1", "version": "0.5.2",
"description": "ripple key pairs", "description": "ripple key pairs",
"files": [ "files": [
"dist/npm/*", "dist/npm/*",

View File

@@ -0,0 +1,69 @@
'use strict';
const util = require('util');
const elliptic = require('elliptic');
const {utils: {parseBytes}} = elliptic;
const Ed25519 = elliptic.eddsa('ed25519');
const {KeyPair, KeyType} = require('./keypair');
const {
Sha512,
hasCachedProperty,
} = require('./utils');
/*
@private
@param {Array} seed bytes
*/
function deriveEdKeyPairSeed(seed) {
return new Sha512().add(seed).first256();
}
/*
* @class
* @private
* @param {Object} - key
*/
function Ed25519Pair() {
KeyPair.apply(this, arguments);
this.type = KeyType.ed25519;
}
util.inherits(Ed25519Pair, KeyPair);
/**
* @param {Array<Number>} seedBytes - A 128 bit seed
* @return {Ed25519Pair} key pair
*/
Ed25519Pair.fromSeed = function(seedBytes) {
return new Ed25519Pair({seedBytes});
};
/**
* @param {Seed} publicKey - public key in canonical form (0xED + 32 bytes)
* @return {Ed25519Pair} key pair
*/
Ed25519Pair.fromPublic = function(publicKey) {
return new Ed25519Pair({pubBytes: parseBytes(publicKey)});
};
hasCachedProperty(Ed25519Pair, 'key', function() {
if (this.seedBytes) {
const seed256 = deriveEdKeyPairSeed(this.seedBytes);
return Ed25519.keyFromSecret(seed256);
}
return Ed25519.keyFromPublic(this.pubKeyCanonicalBytes().slice(1));
});
hasCachedProperty(Ed25519Pair, 'pubKeyCanonicalBytes', function() {
return [0xED].concat(this.key().pubBytes());
});
Ed25519Pair.prototype.sign = function(message) {
return this.key().sign(message).toBytes();
};
Ed25519Pair.prototype.verify = function(message, signature) {
return this.key().verify(message, signature);
};
module.exports = {Ed25519Pair};

View File

@@ -2,259 +2,14 @@
/* -------------------------------- REQUIRES -------------------------------- */ /* -------------------------------- REQUIRES -------------------------------- */
const util = require('util');
const assert = require('assert'); const assert = require('assert');
const elliptic = require('elliptic');
const hashjs = require('hash.js');
const codec = require('ripple-address-codec'); const codec = require('ripple-address-codec');
const rand = require('brorand'); const rand = require('brorand');
// elliptic const {seedFromPhrase, createAccountID} = require('./utils');
const secp256k1 = elliptic.ec('secp256k1'); const {KeyPair, KeyType} = require('./keypair');
const Ed25519 = elliptic.eddsa('ed25519'); const {Ed25519Pair} = require('./ed25519');
const {utils: {parseBytes}} = elliptic; const {K256Pair} = require('./secp256k1');
const {
bytesToHex,
hasCachedProperty,
isVirtual,
Sha512
} = require('./utils');
/* ---------------------------------- ENUMS --------------------------------- */
const KeyType = {
secp256k1: 'secp256k1',
ed25519: 'ed25519'
};
/* ----------------------------- KEY DERIVATION ----------------------------- */
function seedFromPhrase(phrase) {
return hashjs.sha512().update(phrase).digest().slice(0, 16);
}
function findk256Key(bytes, discrim) {
const order = secp256k1.curve.n;
for (let i = 0; i <= 0xFFFFFFFF; i++) {
// We hash the bytes to find a 256 bit number, looping until we are sure it
// is less than the order of the curve.
const hasher = new Sha512().add(bytes);
// If the optional discriminator index was passed in, update the hash.
if (discrim !== undefined) {
hasher.addU32(discrim);
}
hasher.addU32(i);
const key = hasher.finish256BN();
if (key.cmpn(0) > 0 && key.cmp(order) < 0) {
return key;
}
}
throw new Error('impossible unicorn ;)');
}
/* eslint-disable valid-jsdoc */
/**
* @param {Object} [options] -
* @param {Number} [options.accountIndex=0] - the account number to generate
* @param {Boolean} [options.validator=false] - generate root key-pair,
* as used by validators.
* @return {new bn.js} -
*
*/
/* eslint-enable valid-jsdoc */
function derivek256Secret(seed, opts={}) {
const root = opts.validator;
const order = secp256k1.curve.n;
// This private generator represents the `root` private key, and is what's
// used by validators for signing when a keypair is generated from a seed.
const privateGen = findk256Key(seed);
if (root) {
// As returned by validation_create for a given seed
return privateGen;
}
const publicGen = secp256k1.g.mul(privateGen);
// A seed can generate many keypairs as a function of the seed and a uint32.
// Almost everyone just uses the first account, `0`.
const accountIndex = opts.accountIndex || 0;
return findk256Key(publicGen.encodeCompressed(), accountIndex)
.add(privateGen).mod(order);
}
function createAccountID(pubKeyBytes) {
const hash256 = hashjs.sha256().update(pubKeyBytes).digest();
const hash160 = hashjs.ripemd160().update(hash256).digest();
return hash160;
}
/*
@private
@param {Array} seed bytes
*/
function deriveEdKeyPairSeed(seed) {
return new Sha512().add(seed).finish256();
}
/* --------------------------------- KEYPAIR -------------------------------- */
function KeyPair({seedBytes, pubBytes}) {
this.seedBytes = seedBytes;
this.pubKeyCanonicalBytes__ = pubBytes;
}
/*
@param {Array} message
@virtual
*/
KeyPair.prototype.sign = isVirtual;
/*
@param {Array<Byte>} message
@param {Array<Byte>} signature
@virtual
*/
KeyPair.prototype.verify = isVirtual;
/*
@return {Array<Byte>} of bytes, in canonical form, for signing
@virtual
*/
KeyPair.prototype.pubKeyCanonicalBytes = isVirtual;
hasCachedProperty(KeyPair, 'pubKeyHex', function() {
return bytesToHex(this.pubKeyCanonicalBytes());
});
hasCachedProperty(KeyPair, 'accountBytes', function() {
return createAccountID(this.pubKeyCanonicalBytes());
});
hasCachedProperty(KeyPair, 'accountID', function() {
return codec.encodeAccountID(this.accountBytes());
});
KeyPair.prototype.signHex = function(message) {
return bytesToHex(this.sign(message));
};
/* ---------------------------- SECP256K1 KEYPAIR --------------------------- */
/*
* @class
* @private
* @param {Object} - key
*/
function Ed25519Pair() {
KeyPair.apply(this, arguments);
this.type = KeyType.ed25519;
}
util.inherits(Ed25519Pair, KeyPair);
/**
* @param {Array<Number>} seedBytes - A 128 bit seed
* @return {Ed25519Pair} key pair
*/
Ed25519Pair.fromSeed = function(seedBytes) {
return new Ed25519Pair({seedBytes});
};
/**
* @param {Seed} publicKey - public key in canonical form (0xED + 32 bytes)
* @return {Ed25519Pair} key pair
*/
Ed25519Pair.fromPublic = function (publicKey) {
return new Ed25519Pair({pubBytes: parseBytes(publicKey)});
};
hasCachedProperty(Ed25519Pair, 'key', function() {
if (this.seedBytes) {
const seed256 = deriveEdKeyPairSeed(this.seedBytes);
return Ed25519.keyFromSecret(seed256);
} else {
return Ed25519.keyFromPublic(this.pubKeyCanonicalBytes().slice(1));
}
});
hasCachedProperty(Ed25519Pair, 'pubKeyCanonicalBytes', function() {
return [0xED].concat(this.key().pubBytes());
});
Ed25519Pair.prototype.sign = function(message) {
return this.key().sign(message).toBytes();
};
Ed25519Pair.prototype.verify = function(message, signature) {
return this.key().verify(message, signature);
};
/* ---------------------------- SECP256K1 KEYPAIR --------------------------- */
/*
* @class
* @private
*/
function K256Pair({validator}) {
KeyPair.apply(this, arguments);
this.type = KeyType.secp256k1;
this.validator = validator;
}
util.inherits(K256Pair, KeyPair);
K256Pair.fromSeed = function(seedBytes, opts={}) {
return new K256Pair({seedBytes, validator: opts.validator});
};
hasCachedProperty(K256Pair, 'key', function() {
if (this.seedBytes) {
const options = {validator: this.validator};
return secp256k1.keyFromPrivate(derivek256Secret(this.seedBytes, options));
} else {
return secp256k1.keyFromPublic(this.pubKeyCanonicalBytes());
}
});
hasCachedProperty(K256Pair, 'pubKeyCanonicalBytes', function() {
return this.key().getPublic().encodeCompressed();
});
/*
@param {Array<Byte>} message (bytes)
*/
K256Pair.prototype.sign = function(message) {
return this._createSignature(message).toDER();
};
K256Pair.prototype._createSignature = function(message) {
// The key.sign message silently discards options
return this.key().sign(this.hashMessage(message), {canonical: true});
};
/*
@param {Array<Byte>} message - (bytes)
@return {Array<Byte>} - 256 bit hash of the message
*/
K256Pair.prototype.hashMessage = function(message) {
return hashjs.sha512().update(message).digest().slice(0, 32);
};
/*
@param {Array<Byte>} message - bytes
@param {Array<Byte>} signature - DER encoded signature bytes
*/
K256Pair.prototype.verify = function(message, signature) {
try {
return this.key().verify(this.hashMessage(message), signature);
} catch (e) {
return false;
}
};
function keyPairFromSeed(seedString, options) { function keyPairFromSeed(seedString, options) {
const decoded = codec.decodeSeed(seedString); const decoded = codec.decodeSeed(seedString);
@@ -309,7 +64,7 @@ function generateValidatorKeys(opts={}) {
function validatorKeysFromSeed(seed) { function validatorKeysFromSeed(seed) {
const {type, bytes} = codec.decodeSeed(seed); const {type, bytes} = codec.decodeSeed(seed);
assert(type == KeyType.secp256k1); assert(type === KeyType.secp256k1);
return deriveValidator(bytes); return deriveValidator(bytes);
} }

View File

@@ -0,0 +1,60 @@
'use strict';
const codec = require('ripple-address-codec');
const {
bytesToHex,
hasCachedProperty,
isVirtual,
createAccountID
} = require('./utils');
const KeyType = {
secp256k1: 'secp256k1',
ed25519: 'ed25519'
};
function KeyPair({seedBytes, pubBytes}) {
this.seedBytes = seedBytes;
this.pubKeyCanonicalBytes__ = pubBytes;
}
/*
@param {Array} message
@virtual
*/
KeyPair.prototype.sign = isVirtual;
/*
@param {Array<Byte>} message
@param {Array<Byte>} signature
@virtual
*/
KeyPair.prototype.verify = isVirtual;
/*
@return {Array<Byte>} of bytes, in canonical form, for signing
@virtual
*/
KeyPair.prototype.pubKeyCanonicalBytes = isVirtual;
hasCachedProperty(KeyPair, 'pubKeyHex', function() {
return bytesToHex(this.pubKeyCanonicalBytes());
});
hasCachedProperty(KeyPair, 'accountBytes', function() {
return createAccountID(this.pubKeyCanonicalBytes());
});
hasCachedProperty(KeyPair, 'accountID', function() {
return codec.encodeAccountID(this.accountBytes());
});
KeyPair.prototype.signHex = function(message) {
return bytesToHex(this.sign(message));
};
module.exports = {
KeyPair,
KeyType
};

View File

@@ -0,0 +1,124 @@
'use strict';
const util = require('util');
const elliptic = require('elliptic');
const secp256k1 = elliptic.ec('secp256k1');
const hashjs = require('hash.js');
const {KeyPair, KeyType} = require('./keypair');
const {
Sha512,
hasCachedProperty
} = require('./utils');
function findk256Key(bytes, discrim) {
const order = secp256k1.curve.n;
for (let i = 0; i <= 0xFFFFFFFF; i++) {
// We hash the bytes to find a 256 bit number, looping until we are sure it
// is less than the order of the curve.
const hasher = new Sha512().add(bytes);
// If the optional discriminator index was passed in, update the hash.
if (discrim !== undefined) {
hasher.addU32(discrim);
}
hasher.addU32(i);
const key = hasher.first256BN();
if (key.cmpn(0) > 0 && key.cmp(order) < 0) {
return key;
}
}
throw new Error('impossible unicorn ;)');
}
/**
* @param {Array} seed - bytes
* @param {Object} [opts] - object
* @param {Number} [opts.accountIndex=0] - the account number to generate
* @param {Boolean} [opts.validator=false] - generate root key-pair,
* as used by validators.
* @return {bn.js} - bignumber
*
*/
function derivek256Secret(seed, opts={}) {
const root = opts.validator;
const order = secp256k1.curve.n;
// This private generator represents the `root` private key, and is what's
// used by validators for signing when a keypair is generated from a seed.
const privateGen = findk256Key(seed);
if (root) {
// As returned by validation_create for a given seed
return privateGen;
}
const publicGen = secp256k1.g.mul(privateGen);
// A seed can generate many keypairs as a function of the seed and a uint32.
// Almost everyone just uses the first account, `0`.
const accountIndex = opts.accountIndex || 0;
return findk256Key(publicGen.encodeCompressed(), accountIndex)
.add(privateGen).mod(order);
}
/*
* @class
* @private
*/
function K256Pair({validator}) {
KeyPair.apply(this, arguments);
this.type = KeyType.secp256k1;
this.validator = validator;
}
util.inherits(K256Pair, KeyPair);
K256Pair.fromSeed = function(seedBytes, opts={}) {
return new K256Pair({seedBytes, validator: opts.validator});
};
hasCachedProperty(K256Pair, 'key', function() {
if (this.seedBytes) {
const options = {validator: this.validator};
return secp256k1.keyFromPrivate(derivek256Secret(this.seedBytes, options));
}
return secp256k1.keyFromPublic(this.pubKeyCanonicalBytes());
});
hasCachedProperty(K256Pair, 'pubKeyCanonicalBytes', function() {
return this.key().getPublic().encodeCompressed();
});
/*
@param {Array<Byte>} message (bytes)
*/
K256Pair.prototype.sign = function(message) {
return this._createSignature(message).toDER();
};
K256Pair.prototype._createSignature = function(message) {
// The key.sign message silently discards options
return this.key().sign(this.hashMessage(message), {canonical: true});
};
/*
@param {Array<Byte>} message - (bytes)
@return {Array<Byte>} - 256 bit hash of the message
*/
K256Pair.prototype.hashMessage = function(message) {
return hashjs.sha512().update(message).digest().slice(0, 32);
};
/*
@param {Array<Byte>} message - bytes
@param {Array<Byte>} signature - DER encoded signature bytes
*/
K256Pair.prototype.verify = function(message, signature) {
try {
return this.key().verify(this.hashMessage(message), signature);
} catch (e) {
return false;
}
};
module.exports = {
derivek256Secret,
findk256Key,
K256Pair
};

View File

@@ -25,9 +25,11 @@ function arrayToHex(a) {
}).join(''); }).join('');
} }
function toGenericArray(typedArray) { function toGenericArray(sequence) {
const generic = []; const generic = [];
Array.prototype.push.apply(generic, typedArray); for (let i = 0; i < sequence.length; i++) {
generic.push(sequence[i]);
}
return generic; return generic;
} }
@@ -50,19 +52,31 @@ class Sha512 {
finish() { finish() {
return this.hash.digest(); return this.hash.digest();
} }
finish256() { first256() {
return this.finish().slice(0, 32); return this.finish().slice(0, 32);
} }
finish256BN() { first256BN() {
return new BigNum(this.finish256()); return new BigNum(this.first256());
} }
} }
function seedFromPhrase(phrase) {
return hashjs.sha512().update(phrase).digest().slice(0, 16);
}
function createAccountID(pubKeyBytes) {
const hash256 = hashjs.sha256().update(pubKeyBytes).digest();
const hash160 = hashjs.ripemd160().update(hash256).digest();
return hash160;
}
module.exports = { module.exports = {
arrayToHex, arrayToHex,
bytesToHex, bytesToHex,
hasCachedProperty, hasCachedProperty,
isVirtual, isVirtual,
Sha512, Sha512,
toGenericArray toGenericArray,
seedFromPhrase,
createAccountID
}; };

View File

@@ -141,7 +141,8 @@ describe('generateValidatorKeys', function() {
"result" : { "result" : {
"status" : "success", "status" : "success",
"validation_key" : "A A A A A A A A A A A A", "validation_key" : "A A A A A A A A A A A A",
"validation_public_key" : "n9LPxYzbDpWBZ1bC3J3Fdkgqoa3FEhVKCnS8yKp7RFQFwuvd8Q2c", "validation_public_key" :
"n9LPxYzbDpWBZ1bC3J3Fdkgqoa3FEhVKCnS8yKp7RFQFwuvd8Q2c",
"validation_seed" : "sp6JS7f14BuwFY8Mw6bTtLKWauoUs" "validation_seed" : "sp6JS7f14BuwFY8Mw6bTtLKWauoUs"
} }
} }