Switch to new API

This commit is contained in:
Chris Clark
2015-09-21 15:09:54 -07:00
parent 5198a98736
commit de8ea104d9
11 changed files with 135 additions and 819 deletions

View File

@@ -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
};

View File

@@ -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
};

View File

@@ -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
};

View File

@@ -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
};

View File

@@ -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
};

View File

@@ -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
};