mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-11-20 12:15:51 +00:00
Switch to new API
This commit is contained in:
@@ -1,113 +0,0 @@
|
||||
'use strict';
|
||||
const assert = require('assert');
|
||||
const brorand = require('brorand');
|
||||
const hashjs = require('hash.js');
|
||||
const elliptic = require('elliptic');
|
||||
const Ed25519 = elliptic.eddsa('ed25519');
|
||||
const Secp256k1 = elliptic.ec('secp256k1');
|
||||
const addressCodec = require('ripple-address-codec');
|
||||
const derivePrivateKey = require('./secp256k1').derivePrivateKey;
|
||||
const accountPublicFromPublicGenerator = require('./secp256k1')
|
||||
.accountPublicFromPublicGenerator;
|
||||
const utils = require('./utils');
|
||||
const hexToBytes = utils.hexToBytes;
|
||||
const bytesToHex = utils.bytesToHex;
|
||||
|
||||
function generateSeed(options = {}) {
|
||||
assert(!options.entropy || options.entropy.length >= 16, 'entropy too short');
|
||||
const entropy = options.entropy ? options.entropy.slice(0, 16) : brorand(16);
|
||||
const type = options.algorithm === 'ed25519' ? 'ed25519' : 'secp256k1';
|
||||
return addressCodec.encodeSeed(entropy, type);
|
||||
}
|
||||
|
||||
function hash(message) {
|
||||
return hashjs.sha512().update(message).digest().slice(0, 32);
|
||||
}
|
||||
|
||||
const secp256k1 = {
|
||||
deriveKeypair: function(entropy, options) {
|
||||
const prefix = '00';
|
||||
const privateKey = prefix + derivePrivateKey(entropy, options)
|
||||
.toString(16, 64).toUpperCase();
|
||||
const publicKey = bytesToHex(Secp256k1.keyFromPrivate(
|
||||
privateKey.slice(2)).getPublic().encodeCompressed());
|
||||
return {privateKey, publicKey};
|
||||
},
|
||||
sign: function(message, privateKey) {
|
||||
return bytesToHex(Secp256k1.sign(hash(message),
|
||||
hexToBytes(privateKey), {canonical: true}).toDER());
|
||||
},
|
||||
verify: function(message, signature, publicKey) {
|
||||
return Secp256k1.verify(hash(message), signature, hexToBytes(publicKey));
|
||||
}
|
||||
};
|
||||
|
||||
const ed25519 = {
|
||||
deriveKeypair: function(entropy) {
|
||||
const prefix = 'ED';
|
||||
const rawPrivateKey = hash(entropy);
|
||||
const privateKey = prefix + bytesToHex(rawPrivateKey);
|
||||
const publicKey = prefix + bytesToHex(
|
||||
Ed25519.keyFromSecret(rawPrivateKey).pubBytes());
|
||||
return {privateKey, publicKey};
|
||||
},
|
||||
sign: function(message, privateKey) {
|
||||
return bytesToHex(Ed25519.sign(
|
||||
message, hexToBytes(privateKey).slice(1)).toBytes());
|
||||
},
|
||||
verify: function(message, signature, publicKey) {
|
||||
return Ed25519.verify(message, hexToBytes(signature),
|
||||
hexToBytes(publicKey).slice(1));
|
||||
}
|
||||
};
|
||||
|
||||
function select(algorithm) {
|
||||
const methods = {'ecdsa-secp256k1': secp256k1, ed25519};
|
||||
return methods[algorithm];
|
||||
}
|
||||
|
||||
function deriveKeypair(seed, options) {
|
||||
const decoded = addressCodec.decodeSeed(seed);
|
||||
const algorithm = decoded.type === 'ed25519' ? 'ed25519' : 'ecdsa-secp256k1';
|
||||
return select(algorithm).deriveKeypair(decoded.bytes, options);
|
||||
}
|
||||
|
||||
function getAlgorithmFromKey(key) {
|
||||
const bytes = hexToBytes(key);
|
||||
return (bytes.length === 33 && bytes[0] === 0xED) ?
|
||||
'ed25519' : 'ecdsa-secp256k1';
|
||||
}
|
||||
|
||||
function sign(message, privateKey) {
|
||||
const algorithm = getAlgorithmFromKey(privateKey);
|
||||
return select(algorithm).sign(message, privateKey);
|
||||
}
|
||||
|
||||
function verify(message, signature, publicKey) {
|
||||
const algorithm = getAlgorithmFromKey(publicKey);
|
||||
return select(algorithm).verify(message, signature, publicKey);
|
||||
}
|
||||
|
||||
function deriveAddressFromBytes(publicKeyBytes) {
|
||||
return addressCodec.encodeAccountID(
|
||||
utils.computePublicKeyHash(publicKeyBytes));
|
||||
}
|
||||
|
||||
function deriveAddress(publicKey) {
|
||||
return deriveAddressFromBytes(hexToBytes(publicKey));
|
||||
}
|
||||
|
||||
function deriveNodeAddress(publicKey) {
|
||||
const generatorBytes = addressCodec.decodeNodePublic(publicKey);
|
||||
const accountPublicBytes = accountPublicFromPublicGenerator(generatorBytes);
|
||||
return deriveAddressFromBytes(accountPublicBytes);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
generateSeed,
|
||||
deriveKeypair,
|
||||
sign,
|
||||
verify,
|
||||
deriveAddress,
|
||||
deriveNodeAddress
|
||||
};
|
||||
@@ -1,63 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const elliptic = require('elliptic');
|
||||
const {utils: {parseBytes}} = elliptic;
|
||||
const Ed25519 = elliptic.eddsa('ed25519');
|
||||
const {KeyPair, KeyType} = require('./keypair');
|
||||
const {Sha512, cached} = require('./utils');
|
||||
|
||||
/*
|
||||
@param {Array} seed bytes
|
||||
*/
|
||||
function deriveEdKeyPairSecret(seed) {
|
||||
return new Sha512().add(seed).first256();
|
||||
}
|
||||
|
||||
class Ed25519Pair extends KeyPair {
|
||||
constructor(options) {
|
||||
super(options);
|
||||
this.type = KeyType.ed25519;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {String|Array} publicKey - public key in canonical form
|
||||
* (0xED + 32 bytes)
|
||||
* @return {Ed25519Pair} key pair
|
||||
*/
|
||||
static fromPublic(publicKey) {
|
||||
return new Ed25519Pair({pubBytes: parseBytes(publicKey)});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Array<Number>} seedBytes - A 128 bit seed
|
||||
* @return {Ed25519Pair} key pair
|
||||
*/
|
||||
static fromSeed(seedBytes) {
|
||||
return new Ed25519Pair({seedBytes});
|
||||
}
|
||||
|
||||
sign(message) {
|
||||
return this.key().sign(message).toBytes();
|
||||
}
|
||||
|
||||
verify(message, signature) {
|
||||
return this.key().verify(message, signature);
|
||||
}
|
||||
|
||||
@cached
|
||||
pubKeyCanonicalBytes() {
|
||||
return [0xED].concat(this.key().pubBytes());
|
||||
}
|
||||
|
||||
@cached
|
||||
key() {
|
||||
if (this.seedBytes) {
|
||||
return Ed25519.keyFromSecret(deriveEdKeyPairSecret(this.seedBytes));
|
||||
}
|
||||
return Ed25519.keyFromPublic(this.pubKeyCanonicalBytes().slice(1));
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
Ed25519Pair
|
||||
};
|
||||
@@ -1,105 +1,113 @@
|
||||
'use strict';
|
||||
|
||||
const assert = require('assert');
|
||||
const brorand = require('brorand');
|
||||
const codec = require('ripple-address-codec');
|
||||
const hashjs = require('hash.js');
|
||||
const elliptic = require('elliptic');
|
||||
const Ed25519 = elliptic.eddsa('ed25519');
|
||||
const Secp256k1 = elliptic.ec('secp256k1');
|
||||
const addressCodec = require('ripple-address-codec');
|
||||
const derivePrivateKey = require('./secp256k1').derivePrivateKey;
|
||||
const accountPublicFromPublicGenerator = require('./secp256k1')
|
||||
.accountPublicFromPublicGenerator;
|
||||
const utils = require('./utils');
|
||||
const hexToBytes = utils.hexToBytes;
|
||||
const bytesToHex = utils.bytesToHex;
|
||||
|
||||
const {seedFromPhrase, computePublicKeyHash} = require('./utils');
|
||||
const {KeyPair, KeyType} = require('./keypair');
|
||||
const {Ed25519Pair} = require('./ed25519');
|
||||
const {K256Pair, accountPublicFromPublicGenerator} = require('./secp256k1');
|
||||
const {decodeSeed, encodeNodePublic, decodeNodePublic, encodeAccountID} = codec;
|
||||
|
||||
function parseSeed(seed, type = KeyType.secp256k1) {
|
||||
if (typeof seed !== 'string') {
|
||||
return {bytes: seed, type};
|
||||
}
|
||||
return decodeSeed(seed);
|
||||
function generateSeed(options = {}) {
|
||||
assert(!options.entropy || options.entropy.length >= 16, 'entropy too short');
|
||||
const entropy = options.entropy ? options.entropy.slice(0, 16) : brorand(16);
|
||||
const type = options.algorithm === 'ed25519' ? 'ed25519' : 'secp256k1';
|
||||
return addressCodec.encodeSeed(entropy, type);
|
||||
}
|
||||
|
||||
KeyPair.fromSeed = function(seed, type = KeyType.secp256k1, options) {
|
||||
if (typeof seed === 'string') {
|
||||
const decoded = decodeSeed(seed);
|
||||
const optionsArg = type;
|
||||
return this.fromSeed(decoded.bytes, decoded.type, optionsArg);
|
||||
}
|
||||
function hash(message) {
|
||||
return hashjs.sha512().update(message).digest().slice(0, 32);
|
||||
}
|
||||
|
||||
assert(type === KeyType.secp256k1 || type === KeyType.ed25519);
|
||||
const Pair = type === 'ed25519' ? Ed25519Pair : K256Pair;
|
||||
return Pair.fromSeed(seed, options);
|
||||
const secp256k1 = {
|
||||
deriveKeypair: function(entropy, options) {
|
||||
const prefix = '00';
|
||||
const privateKey = prefix + derivePrivateKey(entropy, options)
|
||||
.toString(16, 64).toUpperCase();
|
||||
const publicKey = bytesToHex(Secp256k1.keyFromPrivate(
|
||||
privateKey.slice(2)).getPublic().encodeCompressed());
|
||||
return {privateKey, publicKey};
|
||||
},
|
||||
sign: function(message, privateKey) {
|
||||
return bytesToHex(Secp256k1.sign(hash(message),
|
||||
hexToBytes(privateKey), {canonical: true}).toDER());
|
||||
},
|
||||
verify: function(message, signature, publicKey) {
|
||||
return Secp256k1.verify(hash(message), signature, hexToBytes(publicKey));
|
||||
}
|
||||
};
|
||||
|
||||
function deriveWallet(seedBytes, type) {
|
||||
const pair = KeyPair.fromSeed(seedBytes, type);
|
||||
const ed25519 = {
|
||||
deriveKeypair: function(entropy) {
|
||||
const prefix = 'ED';
|
||||
const rawPrivateKey = hash(entropy);
|
||||
const privateKey = prefix + bytesToHex(rawPrivateKey);
|
||||
const publicKey = prefix + bytesToHex(
|
||||
Ed25519.keyFromSecret(rawPrivateKey).pubBytes());
|
||||
return {privateKey, publicKey};
|
||||
},
|
||||
sign: function(message, privateKey) {
|
||||
return bytesToHex(Ed25519.sign(
|
||||
message, hexToBytes(privateKey).slice(1)).toBytes());
|
||||
},
|
||||
verify: function(message, signature, publicKey) {
|
||||
return Ed25519.verify(message, hexToBytes(signature),
|
||||
hexToBytes(publicKey).slice(1));
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
seed: pair.seed(),
|
||||
accountID: pair.accountID(),
|
||||
publicKey: pair.pubKeyHex()
|
||||
};
|
||||
function select(algorithm) {
|
||||
const methods = {'ecdsa-secp256k1': secp256k1, ed25519};
|
||||
return methods[algorithm];
|
||||
}
|
||||
|
||||
function deriveValidator(seedBytes) {
|
||||
const pair = K256Pair.fromSeed(seedBytes, {validator: true});
|
||||
return {
|
||||
seed: pair.seed(),
|
||||
publicKey: encodeNodePublic(pair.pubKeyCanonicalBytes())
|
||||
};
|
||||
function deriveKeypair(seed, options) {
|
||||
const decoded = addressCodec.decodeSeed(seed);
|
||||
const algorithm = decoded.type === 'ed25519' ? 'ed25519' : 'ecdsa-secp256k1';
|
||||
return select(algorithm).deriveKeypair(decoded.bytes, options);
|
||||
}
|
||||
|
||||
function generateWallet(opts = {}) {
|
||||
const {type = 'secp256k1', random = brorand} = opts;
|
||||
const seedBytes = random(16);
|
||||
return deriveWallet(seedBytes, type);
|
||||
function getAlgorithmFromKey(key) {
|
||||
const bytes = hexToBytes(key);
|
||||
return (bytes.length === 33 && bytes[0] === 0xED) ?
|
||||
'ed25519' : 'ecdsa-secp256k1';
|
||||
}
|
||||
|
||||
function walletFromSeed(seed, seedType) {
|
||||
const {type, bytes} = parseSeed(seed, seedType);
|
||||
return deriveWallet(bytes, type);
|
||||
function sign(message, privateKey) {
|
||||
const algorithm = getAlgorithmFromKey(privateKey);
|
||||
return select(algorithm).sign(message, privateKey);
|
||||
}
|
||||
|
||||
function walletFromPhrase(phrase, type) {
|
||||
return walletFromSeed(seedFromPhrase(phrase), type);
|
||||
function verify(message, signature, publicKey) {
|
||||
const algorithm = getAlgorithmFromKey(publicKey);
|
||||
return select(algorithm).verify(message, signature, publicKey);
|
||||
}
|
||||
|
||||
function generateValidatorKeys(opts = {}) {
|
||||
const {random = brorand} = opts;
|
||||
return deriveValidator(random(16));
|
||||
function deriveAddressFromBytes(publicKeyBytes) {
|
||||
return addressCodec.encodeAccountID(
|
||||
utils.computePublicKeyHash(publicKeyBytes));
|
||||
}
|
||||
|
||||
function nodePublicAccountID(publicKey) {
|
||||
const generatorBytes = decodeNodePublic(publicKey);
|
||||
function deriveAddress(publicKey) {
|
||||
return deriveAddressFromBytes(hexToBytes(publicKey));
|
||||
}
|
||||
|
||||
function deriveNodeAddress(publicKey) {
|
||||
const generatorBytes = addressCodec.decodeNodePublic(publicKey);
|
||||
const accountPublicBytes = accountPublicFromPublicGenerator(generatorBytes);
|
||||
return encodeAccountID(computePublicKeyHash(accountPublicBytes));
|
||||
}
|
||||
|
||||
function validatorKeysFromSeed(seed, seedType) {
|
||||
const {type, bytes} = parseSeed(seed, seedType);
|
||||
assert(type === KeyType.secp256k1);
|
||||
return deriveValidator(bytes);
|
||||
}
|
||||
|
||||
function validatorKeysFromPhrase(phrase) {
|
||||
return deriveValidator(seedFromPhrase(phrase));
|
||||
}
|
||||
|
||||
function keyPairFromSeed(seedString, options) {
|
||||
return KeyPair.fromSeed(seedString, options);
|
||||
return deriveAddressFromBytes(accountPublicBytes);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
KeyPair,
|
||||
K256Pair,
|
||||
Ed25519Pair,
|
||||
KeyType,
|
||||
seedFromPhrase,
|
||||
computePublicKeyHash,
|
||||
keyPairFromSeed,
|
||||
generateWallet,
|
||||
generateValidatorKeys,
|
||||
walletFromSeed,
|
||||
walletFromPhrase,
|
||||
validatorKeysFromSeed,
|
||||
validatorKeysFromPhrase,
|
||||
nodePublicAccountID
|
||||
generateSeed,
|
||||
deriveKeypair,
|
||||
sign,
|
||||
verify,
|
||||
deriveAddress,
|
||||
deriveNodeAddress
|
||||
};
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const codec = require('ripple-address-codec');
|
||||
const {
|
||||
bytesToHex,
|
||||
cached,
|
||||
isVirtual,
|
||||
computePublicKeyHash
|
||||
} = require('./utils');
|
||||
|
||||
const KeyType = {
|
||||
secp256k1: 'secp256k1',
|
||||
ed25519: 'ed25519'
|
||||
};
|
||||
|
||||
class KeyPair {
|
||||
constructor({seedBytes, pubBytes}) {
|
||||
this.seedBytes = seedBytes;
|
||||
this._pubKeyCanonicalBytes = pubBytes;
|
||||
}
|
||||
|
||||
/*
|
||||
* @param {Array} message
|
||||
*/
|
||||
@isVirtual
|
||||
sign() {}
|
||||
|
||||
/*
|
||||
* @param {Array<Byte>} message
|
||||
* @param {Array<Byte>} signature
|
||||
*/
|
||||
@isVirtual
|
||||
verify() {}
|
||||
|
||||
/*
|
||||
* @return {Array<Byte>} of bytes, in canonical form, for signing
|
||||
*/
|
||||
@isVirtual
|
||||
pubKeyCanonicalBytes() {}
|
||||
|
||||
@cached
|
||||
pubKeyHex() {
|
||||
return bytesToHex(this.pubKeyCanonicalBytes());
|
||||
}
|
||||
|
||||
@cached
|
||||
accountBytes() {
|
||||
return computePublicKeyHash(this.pubKeyCanonicalBytes());
|
||||
}
|
||||
|
||||
@cached
|
||||
accountID() {
|
||||
return codec.encodeAccountID(this.accountBytes());
|
||||
}
|
||||
|
||||
@cached
|
||||
seed() {
|
||||
return codec.encodeSeed(this.seedBytes, this.type);
|
||||
}
|
||||
|
||||
signHex(message) {
|
||||
return bytesToHex(this.sign(message));
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
KeyPair,
|
||||
KeyType
|
||||
};
|
||||
@@ -2,9 +2,7 @@
|
||||
|
||||
const elliptic = require('elliptic');
|
||||
const secp256k1 = elliptic.ec('secp256k1');
|
||||
const hashjs = require('hash.js');
|
||||
const {KeyPair, KeyType} = require('./keypair');
|
||||
const {Sha512, cached} = require('./utils');
|
||||
const Sha512 = require('./sha512');
|
||||
|
||||
function deriveScalar(bytes, discrim) {
|
||||
const order = secp256k1.curve.n;
|
||||
@@ -61,69 +59,7 @@ function accountPublicFromPublicGenerator(publicGenBytes) {
|
||||
return offset.encodeCompressed();
|
||||
}
|
||||
|
||||
class K256Pair extends KeyPair {
|
||||
constructor(options) {
|
||||
super(options);
|
||||
this.type = KeyType.secp256k1;
|
||||
this.validator = options.validator;
|
||||
}
|
||||
|
||||
static fromSeed(seedBytes, opts = {}) {
|
||||
return new K256Pair({seedBytes, validator: opts.validator});
|
||||
}
|
||||
|
||||
/*
|
||||
@param {Array<Byte>} message (bytes)
|
||||
*/
|
||||
sign(message) {
|
||||
return this._createSignature(message).toDER();
|
||||
}
|
||||
|
||||
/*
|
||||
@param {Array<Byte>} message - bytes
|
||||
@param {Array<Byte>} signature - DER encoded signature bytes
|
||||
*/
|
||||
verify(message, signature) {
|
||||
try {
|
||||
return this.key().verify(this.hashMessage(message), signature);
|
||||
/* eslint-disable no-catch-shadow */
|
||||
} catch (e) {
|
||||
/* eslint-enable no-catch-shadow */
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@cached
|
||||
pubKeyCanonicalBytes() {
|
||||
return this.key().getPublic().encodeCompressed();
|
||||
}
|
||||
|
||||
_createSignature(message) {
|
||||
return this.key().sign(this.hashMessage(message), {canonical: true});
|
||||
}
|
||||
|
||||
/*
|
||||
@param {Array<Byte>} message - (bytes)
|
||||
@return {Array<Byte>} - 256 bit hash of the message
|
||||
*/
|
||||
hashMessage(message) {
|
||||
return hashjs.sha512().update(message).digest().slice(0, 32);
|
||||
}
|
||||
|
||||
@cached
|
||||
key() {
|
||||
if (this.seedBytes) {
|
||||
const options = {validator: this.validator};
|
||||
return secp256k1.keyFromPrivate(
|
||||
derivePrivateKey(this.seedBytes, options));
|
||||
}
|
||||
return secp256k1.keyFromPublic(this.pubKeyCanonicalBytes());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
K256Pair,
|
||||
derivePrivateKey,
|
||||
accountPublicFromPublicGenerator
|
||||
};
|
||||
|
||||
@@ -2,39 +2,6 @@
|
||||
const assert = require('assert');
|
||||
const hashjs = require('hash.js');
|
||||
const BN = require('bn.js');
|
||||
const Sha512 = require('./sha512');
|
||||
|
||||
function unused() {}
|
||||
|
||||
function isVirtual(_, __, descriptor) {
|
||||
unused(_, __);
|
||||
|
||||
descriptor.value = function() {
|
||||
throw new Error('virtual method not implemented ');
|
||||
};
|
||||
}
|
||||
|
||||
function cached(_, name, descriptor) {
|
||||
unused(_);
|
||||
|
||||
const computer = descriptor.value;
|
||||
const key = '_' + name;
|
||||
descriptor.value = function() {
|
||||
let value = this[key];
|
||||
if (value === undefined) {
|
||||
value = this[key] = computer.call(this);
|
||||
}
|
||||
return value;
|
||||
};
|
||||
}
|
||||
|
||||
function toGenericArray(sequence) {
|
||||
const generic = [];
|
||||
for (let i = 0; i < sequence.length; i++) {
|
||||
generic.push(sequence[i]);
|
||||
}
|
||||
return generic;
|
||||
}
|
||||
|
||||
function bytesToHex(a) {
|
||||
return a.map(function(byteValue) {
|
||||
@@ -59,12 +26,8 @@ function seedFromPhrase(phrase) {
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
cached,
|
||||
bytesToHex,
|
||||
hexToBytes,
|
||||
computePublicKeyHash,
|
||||
isVirtual,
|
||||
seedFromPhrase,
|
||||
Sha512,
|
||||
toGenericArray
|
||||
seedFromPhrase
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user