mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-11-20 12:15:51 +00:00
Break up index.js into logical files
This commit is contained in:
@@ -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=
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
# ripple-keypairs
|
# ripple-keypairs
|
||||||
|
|
||||||
[](https://npmjs.org/package/ripple-keypairs)
|
[](https://npmjs.org/package/ripple-keypairs) [](https://travis-ci.org/sublimator/ripple-keypairs) [](https://coveralls.io/github/sublimator/ripple-keypairs?branch=master)
|
||||||
|
|
||||||
[](https://travis-ci.org/sublimator/ripple-keypairs) [](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
|
||||||
|
|||||||
@@ -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/*",
|
||||||
|
|||||||
69
packages/ripple-keypairs/src/ed25519.js
Normal file
69
packages/ripple-keypairs/src/ed25519.js
Normal 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};
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
60
packages/ripple-keypairs/src/keypair.js
Normal file
60
packages/ripple-keypairs/src/keypair.js
Normal 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
|
||||||
|
};
|
||||||
124
packages/ripple-keypairs/src/secp256k1.js
Normal file
124
packages/ripple-keypairs/src/secp256k1.js
Normal 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
|
||||||
|
};
|
||||||
@@ -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
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user