mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-11-20 04:05:52 +00:00
125 lines
3.5 KiB
JavaScript
125 lines
3.5 KiB
JavaScript
'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
|
|
};
|