diff --git a/packages/ripple-keypairs/.babelrc b/packages/ripple-keypairs/.babelrc new file mode 100644 index 00000000..ea34d50b --- /dev/null +++ b/packages/ripple-keypairs/.babelrc @@ -0,0 +1,3 @@ +{ + "stage" : 1 +} diff --git a/packages/ripple-keypairs/package.json b/packages/ripple-keypairs/package.json index 1b7fec4a..39835a7d 100644 --- a/packages/ripple-keypairs/package.json +++ b/packages/ripple-keypairs/package.json @@ -14,7 +14,7 @@ "test": "test" }, "dependencies": { - "babel-runtime": "^5.3.2", + "babel-runtime": "^5.8.20", "bn.js": "^3.1.1", "brorand": "^1.0.5", "elliptic": "^5.1.0", @@ -23,9 +23,10 @@ }, "devDependencies": { "assert-diff": "^1.0.1", - "babel": "^5.3.3", - "babel-core": "^5.3.2", - "babel-loader": "^5.0.0", + "babel": "^5.8.20", + "babel-core": "^5.8.20", + "babel-eslint": "^4.0.5", + "babel-loader": "^5.3.2", "coveralls": "~2.10.0", "eslint": "^1.0.0", "eventemitter2": "^0.4.14", @@ -45,7 +46,7 @@ "prepublish": "npm test && npm run lint && npm run compile", "test": "istanbul test _mocha", "coveralls": "cat ./coverage/lcov.info | coveralls", - "lint": "if ! [ -f eslintrc ]; then curl -o eslintrc 'https://raw.githubusercontent.com/ripple/javascript-style-guide/es6/eslintrc'; fi; eslint -c eslintrc src/*.js test/*.js" + "lint": "if ! [ -f eslintrc ]; then curl -o eslintrc 'https://raw.githubusercontent.com/ripple/javascript-style-guide/es6/eslintrc'; echo 'parser: babel-eslint' >> eslintrc; fi; eslint -c eslintrc src/*.js test/*.js" }, "repository": { "type": "git", diff --git a/packages/ripple-keypairs/src/ed25519.js b/packages/ripple-keypairs/src/ed25519.js index e82b1b98..0b7949c3 100644 --- a/packages/ripple-keypairs/src/ed25519.js +++ b/packages/ripple-keypairs/src/ed25519.js @@ -1,14 +1,10 @@ '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, - cachedProperty -} = require('./utils'); +const {Sha512, cached} = require('./utils'); /* @param {Array} seed bytes @@ -17,51 +13,51 @@ function deriveEdKeyPairSeed(seed) { return new Sha512().add(seed).first256(); } -/* -* @class -*/ -function Ed25519Pair() { - KeyPair.apply(this, arguments); - this.type = KeyType.ed25519; -} - -util.inherits(Ed25519Pair, KeyPair); - -/** -* @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)}); -}; - -Ed25519Pair.prototype.sign = function(message) { - return this.key().sign(message).toBytes(); -}; - -Ed25519Pair.prototype.verify = function(message, signature) { - return this.key().verify(message, signature); -}; - -cachedProperty(Ed25519Pair, function key() { - if (this.seedBytes) { - const seed256 = deriveEdKeyPairSeed(this.seedBytes); - return Ed25519.keyFromSecret(seed256); +class Ed25519Pair extends KeyPair { + constructor(options) { + super(options); + this.type = KeyType.ed25519; } - return Ed25519.keyFromPublic(this.pubKeyCanonicalBytes().slice(1)); -}); -cachedProperty(Ed25519Pair, function pubKeyCanonicalBytes() { - return [0xED].concat(this.key().pubBytes()); -}); + /** + * @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} seedBytes - A 128 bit seed -* @return {Ed25519Pair} key pair -*/ -Ed25519Pair.fromSeed = function(seedBytes) { - return new Ed25519Pair({seedBytes}); -}; + /** + * @param {Array} 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 + key() { + if (this.seedBytes) { + const seed256 = deriveEdKeyPairSeed(this.seedBytes); + return Ed25519.keyFromSecret(seed256); + } + return Ed25519.keyFromPublic(this.pubKeyCanonicalBytes().slice(1)); + } + + @cached + pubKeyCanonicalBytes() { + return [0xED].concat(this.key().pubBytes()); + } +} module.exports = { Ed25519Pair diff --git a/packages/ripple-keypairs/src/keypair.js b/packages/ripple-keypairs/src/keypair.js index 0947dd46..7b88aca4 100644 --- a/packages/ripple-keypairs/src/keypair.js +++ b/packages/ripple-keypairs/src/keypair.js @@ -3,7 +3,7 @@ const codec = require('ripple-address-codec'); const { bytesToHex, - cachedProperty, + cached, isVirtual, createAccountID } = require('./utils'); @@ -13,51 +13,56 @@ const KeyType = { ed25519: 'ed25519' }; -function KeyPair({seedBytes, pubBytes}) { - this.seedBytes = seedBytes; - this._pubKeyCanonicalBytes = pubBytes; +class KeyPair { + constructor({seedBytes, pubBytes}) { + this.seedBytes = seedBytes; + this._pubKeyCanonicalBytes = pubBytes; + } + + /* + * @param {Array} message + */ + @isVirtual + sign() {} + + /* + * @param {Array} message + * @param {Array} signature + */ + @isVirtual + verify() {} + + /* + * @return {Array} of bytes, in canonical form, for signing + */ + @isVirtual + pubKeyCanonicalBytes() {} + + @cached + pubKeyHex() { + return bytesToHex(this.pubKeyCanonicalBytes()); + } + + @cached + accountBytes() { + return createAccountID(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)); + } } -/* -@param {Array} message -@virtual - */ -KeyPair.prototype.sign = isVirtual; - - -/* -@param {Array} message -@param {Array} signature -@virtual - */ -KeyPair.prototype.verify = isVirtual; - -/* -@return {Array} of bytes, in canonical form, for signing -@virtual -*/ -KeyPair.prototype.pubKeyCanonicalBytes = isVirtual; - -cachedProperty(KeyPair, function pubKeyHex() { - return bytesToHex(this.pubKeyCanonicalBytes()); -}); - -cachedProperty(KeyPair, function accountBytes() { - return createAccountID(this.pubKeyCanonicalBytes()); -}); - -cachedProperty(KeyPair, function accountID() { - return codec.encodeAccountID(this.accountBytes()); -}); - -cachedProperty(KeyPair, function seed() { - return codec.encodeSeed(this.seedBytes, this.type); -}); - -KeyPair.prototype.signHex = function(message) { - return bytesToHex(this.sign(message)); -}; - module.exports = { KeyPair, KeyType diff --git a/packages/ripple-keypairs/src/secp256k1.js b/packages/ripple-keypairs/src/secp256k1.js index 34e7a461..647daa56 100644 --- a/packages/ripple-keypairs/src/secp256k1.js +++ b/packages/ripple-keypairs/src/secp256k1.js @@ -1,14 +1,10 @@ '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, - cachedProperty -} = require('./utils'); +const {Sha512, cached} = require('./utils'); function deriveScalar(bytes, discrim) { const order = secp256k1.curve.n; @@ -65,65 +61,64 @@ function accountPublicFromPublicGenerator(publicGenBytes) { return offset.encodeCompressed(); } -/* -* @class -*/ -function K256Pair({validator}) { - KeyPair.apply(this, arguments); - this.type = KeyType.secp256k1; - this.validator = validator; +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} message (bytes) + */ + sign(message) { + return this._createSignature(message).toDER(); + } + + _createSignature(message) { + // The key.sign message silently discards options + return this.key().sign(this.hashMessage(message), {canonical: true}); + } + + /* + @param {Array} message - (bytes) + @return {Array} - 256 bit hash of the message + */ + hashMessage(message) { + return hashjs.sha512().update(message).digest().slice(0, 32); + } + + /* + @param {Array} message - bytes + @param {Array} signature - DER encoded signature bytes + */ + verify(message, signature) { + try { + return this.key().verify(this.hashMessage(message), signature); + } catch (e) { + return false; + } + } + + @cached + key() { + if (this.seedBytes) { + const options = {validator: this.validator}; + return secp256k1.keyFromPrivate(deriveSecret(this.seedBytes, options)); + } + return secp256k1.keyFromPublic(this.pubKeyCanonicalBytes()); + } + + @cached + pubKeyCanonicalBytes() { + return this.key().getPublic().encodeCompressed(); + } } -util.inherits(K256Pair, KeyPair); - -/* -@param {Array} 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} message - (bytes) -@return {Array} - 256 bit hash of the message - */ -K256Pair.prototype.hashMessage = function(message) { - return hashjs.sha512().update(message).digest().slice(0, 32); -}; - -/* -@param {Array} message - bytes -@param {Array} signature - DER encoded signature bytes - */ -K256Pair.prototype.verify = function(message, signature) { - try { - return this.key().verify(this.hashMessage(message), signature); - } catch (e) { - return false; - } -}; - -cachedProperty(K256Pair, function key() { - if (this.seedBytes) { - const options = {validator: this.validator}; - return secp256k1.keyFromPrivate(deriveSecret(this.seedBytes, options)); - } - return secp256k1.keyFromPublic(this.pubKeyCanonicalBytes()); -}); - -cachedProperty(K256Pair, function pubKeyCanonicalBytes() { - return this.key().getPublic().encodeCompressed(); -}); - -K256Pair.fromSeed = function(seedBytes, opts={}) { - return new K256Pair({seedBytes, validator: opts.validator}); -}; - module.exports = { K256Pair, accountPublicFromPublicGenerator diff --git a/packages/ripple-keypairs/src/utils.js b/packages/ripple-keypairs/src/utils.js index 6a2ffea1..866c732c 100644 --- a/packages/ripple-keypairs/src/utils.js +++ b/packages/ripple-keypairs/src/utils.js @@ -3,19 +3,21 @@ const hashjs = require('hash.js'); const Sha512 = require('./sha512'); -function isVirtual() { - throw new Error('virtual method not implemented '); +function isVirtual(target, name, descriptor) { + descriptor.value = function() { + throw new Error('virtual method not implemented '); + }; } -function cachedProperty(obj, computer) { - const name = computer.name; +function cached(target, name, descriptor) { + const computer = descriptor.value; const key = '_' + name; - obj.prototype[name] = function() { - let cached = this[key]; - if (cached === undefined) { - cached = this[key] = computer.call(this); + descriptor.value = function() { + let value = this[key]; + if (value === undefined) { + value = this[key] = computer.call(this); } - return cached; + return value; }; } @@ -45,8 +47,8 @@ function seedFromPhrase(phrase) { } module.exports = { + cached, bytesToHex, - cachedProperty, createAccountID, isVirtual, seedFromPhrase,