Updates: * Expose abstract KeyPair * Update ripple-address-codec dependency * Add keyPairFromSeed method, which can handle ed25519 seeds

This commit is contained in:
Nicholas Dudfield
2015-07-16 20:23:19 +08:00
parent 5c8be46b64
commit fd6df49f55
3 changed files with 44 additions and 24 deletions

View File

@@ -18,7 +18,7 @@
"bn.js": "^3.0.1", "bn.js": "^3.0.1",
"elliptic": "^5.0.0", "elliptic": "^5.0.0",
"hash.js": "^1.0.3", "hash.js": "^1.0.3",
"ripple-address-codec": "^1.0.3" "ripple-address-codec": "^1.1.0"
}, },
"devDependencies": { "devDependencies": {
"assert-diff": "^1.0.1", "assert-diff": "^1.0.1",

View File

@@ -188,46 +188,39 @@ Ed25519Pair.prototype.verify = function(message, signature) {
* @class * @class
* @private * @private
*/ */
function Secp256k1Pair(key) { function K256Pair(key) {
KeyPair.apply(this, arguments); KeyPair.apply(this, arguments);
this.type = KeyType.secp256k1; this.type = KeyType.secp256k1;
this.key = key; this.key = key;
} }
util.inherits(Secp256k1Pair, KeyPair); util.inherits(K256Pair, KeyPair);
Secp256k1Pair.fromSeed = function(seed) { K256Pair.fromSeed = function(seed) {
return new Secp256k1Pair(secp256k1.keyFromPrivate(derivek256Secret(seed))); return new K256Pair(secp256k1.keyFromPrivate(derivek256Secret(seed)));
}; };
hasCachedProperty(Secp256k1Pair, 'pubKeyCanonicalBytes', function() { hasCachedProperty(K256Pair, 'pubKeyCanonicalBytes', function() {
return this.key.getPublic(/*compact*/ true, /*enc*/ 'bytes'); return this.key.getPublic(/*compact*/ true, /*enc*/ 'bytes');
}); });
/* /*
@param {Array<Byte>} message (bytes) @param {Array<Byte>} message (bytes)
*/ */
Secp256k1Pair.prototype.sign = function(message) { K256Pair.prototype.sign = function(message) {
return this._createSignature(message).toDER(); return this._createSignature(message).toDER();
}; };
Secp256k1Pair.prototype._createSignature = function(message) { K256Pair.prototype._createSignature = function(message) {
// The key.sign message silently discards options // The key.sign message silently discards options
return secp256k1.sign(this.hashMessage(message), this.key, {canonical: true}); return secp256k1.sign(this.hashMessage(message), this.key, {canonical: true});
}; };
/* /*
@param {Array<Byte>} message (bytes) @param {Array<Byte>} message - (bytes)
@return {Array<Byte>} - 256 bit hash of the message
*/ */
Secp256k1Pair.prototype.signMessage = function(message) { K256Pair.prototype.hashMessage = function(message) {
return this.key.sign(this.hashMessage(message), {canonical: true});
};
/*
@param {Array<Byte>} message (bytes)
@return {Array<Byte>} 256 bit hash of the message
*/
Secp256k1Pair.prototype.hashMessage = function(message) {
return hashjs.sha512().update(message).digest().slice(0, 32); return hashjs.sha512().update(message).digest().slice(0, 32);
}; };
@@ -235,7 +228,7 @@ Secp256k1Pair.prototype.hashMessage = function(message) {
@param {Array<Byte>} message - bytes @param {Array<Byte>} message - bytes
@param {Array<Byte>} signature - DER encoded signature bytes @param {Array<Byte>} signature - DER encoded signature bytes
*/ */
Secp256k1Pair.prototype.verify = function(message, signature) { K256Pair.prototype.verify = function(message, signature) {
try { try {
return this.key.verify(this.hashMessage(message), signature); return this.key.verify(this.hashMessage(message), signature);
} catch (e) { } catch (e) {
@@ -243,10 +236,18 @@ Secp256k1Pair.prototype.verify = function(message, signature) {
} }
}; };
function keyPairFromSeed(seedString) {
var decoded = codec.decodeSeed(seedString);
var pair = decoded.type === 'EdSeed' ? Ed25519Pair : K256Pair;
return pair.fromSeed(decoded.bytes);
}
module.exports = { module.exports = {
Secp256k1Pair, KeyPair,
K256Pair,
Ed25519Pair, Ed25519Pair,
KeyType, KeyType,
seedFromPhrase, seedFromPhrase,
createAccountID createAccountID,
keyPairFromSeed
}; };

View File

@@ -2,7 +2,15 @@
const assert = require('assert'); const assert = require('assert');
const utils = require('../src/utils'); const utils = require('../src/utils');
const {KeyType, Secp256k1Pair, seedFromPhrase, Ed25519Pair} = require('../src'); const keypairs = require('../src');
const {
KeyType,
K256Pair,
seedFromPhrase,
Ed25519Pair,
keyPairFromSeed
} = keypairs
const {SerializedObject} = require('ripple-lib'); const {SerializedObject} = require('ripple-lib');
const TX_HASH_PREFIX_SIGN = [0x53, 0x54, 0x58, 0x00]; const TX_HASH_PREFIX_SIGN = [0x53, 0x54, 0x58, 0x00];
@@ -68,7 +76,18 @@ describe('ED25519Pair', function() {
}); });
}); });
describe('Secp256k1Pair', function() { describe('keyPairFromSeed', function() {
it('returns an Ed25519Pair from an ed25519 seed', function() {
const pair = keyPairFromSeed('sEdTM1uX8pu2do5XvTnutH6HsouMaM2');
assert(pair instanceof Ed25519Pair);
});
it('returns a K256Pair from an secp256k1 (default) seed', function() {
const pair = keyPairFromSeed('sn259rEFXrQrWyx3Q7XneWcwV6dfL');
assert(pair instanceof K256Pair);
});
});
describe('K256Pair', function() {
describe('generated tests', function() { describe('generated tests', function() {
/*eslint-disable max-len*/ /*eslint-disable max-len*/
const expected = [ const expected = [
@@ -79,7 +98,7 @@ describe('Secp256k1Pair', function() {
'3045022100998ABE378F4119D8BEE9843482C09F0D5CE5C6012921548182454C610C57A269022036BD8EB71235C4B2C67339DE6A59746B1F7E5975987B7AB99B313D124A69BB9F' '3045022100998ABE378F4119D8BEE9843482C09F0D5CE5C6012921548182454C610C57A269022036BD8EB71235C4B2C67339DE6A59746B1F7E5975987B7AB99B313D124A69BB9F'
]; ];
/*eslint-enable max-len*/ /*eslint-enable max-len*/
const key = Secp256k1Pair.fromSeed(seedFromPhrase('niq')); const key = K256Pair.fromSeed(seedFromPhrase('niq'));
function test_factory(i) { function test_factory(i) {
it('can deterministically sign/verify message [' + i + ']', function() { it('can deterministically sign/verify message [' + i + ']', function() {
const message = [i]; const message = [i];