From 0dcf1a6f52407f98214e15d173bbaa9e72f528f7 Mon Sep 17 00:00:00 2001 From: David Dias Date: Sat, 22 Jul 2017 13:31:42 -0700 Subject: [PATCH] fix: circular circular dep -> DI --- package.json | 2 +- src/crypto.js | 89 ++++++++++++++++++ src/crypto/index.js | 85 ----------------- src/index.js | 205 ++++++++++++++++++++--------------------- test/secp256k1.spec.js | 28 ++++-- 5 files changed, 210 insertions(+), 199 deletions(-) create mode 100644 src/crypto.js delete mode 100644 src/crypto/index.js diff --git a/package.json b/package.json index c033d32..467484b 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,6 @@ "license": "MIT", "dependencies": { "async": "^2.5.0", - "libp2p-crypto": "~0.9.2", "multihashing-async": "~0.4.6", "nodeify": "^1.0.1", "safe-buffer": "^5.1.1", @@ -37,6 +36,7 @@ "devDependencies": { "aegir": "^11.0.2", "benchmark": "^2.1.4", + "libp2p-crypto": "~0.9.4", "chai": "^4.1.0", "dirty-chai": "^2.0.1", "pre-commit": "^1.2.2" diff --git a/src/crypto.js b/src/crypto.js new file mode 100644 index 0000000..a336ab4 --- /dev/null +++ b/src/crypto.js @@ -0,0 +1,89 @@ +'use strict' + +const secp256k1 = require('secp256k1') +const multihashing = require('multihashing-async') +const setImmediate = require('async/setImmediate') + +const HASH_ALGORITHM = 'sha2-256' + +module.exports = (randomBytes) => { + const privateKeyLength = 32 + + function generateKey (callback) { + const done = (err, res) => setImmediate(() => callback(err, res)) + + let privateKey + do { + privateKey = randomBytes(32) + } while (!secp256k1.privateKeyVerify(privateKey)) + + done(null, privateKey) + } + + function hashAndSign (key, msg, callback) { + const done = (err, res) => setImmediate(() => callback(err, res)) + + multihashing.digest(msg, HASH_ALGORITHM, (err, digest) => { + if (err) { return done(err) } + + try { + const sig = secp256k1.sign(digest, key) + const sigDER = secp256k1.signatureExport(sig.signature) + return done(null, sigDER) + } catch (err) { done(err) } + }) + } + + function hashAndVerify (key, sig, msg, callback) { + const done = (err, res) => setImmediate(() => callback(err, res)) + + multihashing.digest(msg, HASH_ALGORITHM, (err, digest) => { + if (err) { return done(err) } + try { + sig = secp256k1.signatureImport(sig) + const valid = secp256k1.verify(digest, sig, key) + return done(null, valid) + } catch (err) { done(err) } + }) + } + + function compressPublicKey (key) { + if (!secp256k1.publicKeyVerify(key)) { + throw new Error('Invalid public key') + } + return secp256k1.publicKeyConvert(key, true) + } + + function decompressPublicKey (key) { + return secp256k1.publicKeyConvert(key, false) + } + + function validatePrivateKey (key) { + if (!secp256k1.privateKeyVerify(key)) { + throw new Error('Invalid private key') + } + } + + function validatePublicKey (key) { + if (!secp256k1.publicKeyVerify(key)) { + throw new Error('Invalid public key') + } + } + + function computePublicKey (privateKey) { + validatePrivateKey(privateKey) + return secp256k1.publicKeyCreate(privateKey) + } + + return { + generateKey: generateKey, + privateKeyLength: privateKeyLength, + hashAndSign: hashAndSign, + hashAndVerify: hashAndVerify, + compressPublicKey: compressPublicKey, + decompressPublicKey: decompressPublicKey, + validatePrivateKey: validatePrivateKey, + validatePublicKey: validatePublicKey, + computePublicKey: computePublicKey + } +} diff --git a/src/crypto/index.js b/src/crypto/index.js deleted file mode 100644 index 7e2b494..0000000 --- a/src/crypto/index.js +++ /dev/null @@ -1,85 +0,0 @@ -'use strict' - -const secp256k1 = require('secp256k1') -const multihashing = require('multihashing-async') -const setImmediate = require('async/setImmediate') -const randomBytes = require('libp2p-crypto').randomBytes - -const HASH_ALGORITHM = 'sha2-256' - -exports.privateKeyLength = 32 - -exports.generateKey = function (callback) { - const done = (err, res) => setImmediate(() => { - callback(err, res) - }) - - let privateKey - do { - privateKey = randomBytes(32) - } while (!secp256k1.privateKeyVerify(privateKey)) - - done(null, privateKey) -} - -exports.hashAndSign = function (key, msg, callback) { - const done = (err, res) => setImmediate(() => { - callback(err, res) - }) - - multihashing.digest(msg, HASH_ALGORITHM, (err, digest) => { - if (err) { return done(err) } - try { - const sig = secp256k1.sign(digest, key) - const sigDER = secp256k1.signatureExport(sig.signature) - return done(null, sigDER) - } catch (err) { - done(err) - } - }) -} - -exports.hashAndVerify = function (key, sig, msg, callback) { - const done = (err, res) => setImmediate(() => { - callback(err, res) - }) - - multihashing.digest(msg, HASH_ALGORITHM, (err, digest) => { - if (err) { return done(err) } - try { - sig = secp256k1.signatureImport(sig) - const valid = secp256k1.verify(digest, sig, key) - return done(null, valid) - } catch (err) { - done(err) - } - }) -} - -exports.compressPublicKey = function compressPublicKey (key) { - if (!secp256k1.publicKeyVerify(key)) { - throw new Error('Invalid public key') - } - return secp256k1.publicKeyConvert(key, true) -} - -exports.decompressPublicKey = function decompressPublicKey (key) { - return secp256k1.publicKeyConvert(key, false) -} - -exports.validatePrivateKey = function validatePrivateKey (key) { - if (!secp256k1.privateKeyVerify(key)) { - throw new Error('Invalid private key') - } -} - -exports.validatePublicKey = function validatePublicKey (key) { - if (!secp256k1.publicKeyVerify(key)) { - throw new Error('Invalid public key') - } -} - -exports.computePublicKey = function computePublicKey (privateKey) { - exports.validatePrivateKey(privateKey) - return secp256k1.publicKeyCreate(privateKey) -} diff --git a/src/index.js b/src/index.js index 3d3c030..c3bb1d6 100644 --- a/src/index.js +++ b/src/index.js @@ -1,119 +1,118 @@ 'use strict' const multihashing = require('multihashing-async') -const crypto = require('./crypto') -const pbm = require('libp2p-crypto').keys.pbm -class Secp256k1PublicKey { - constructor (key) { - crypto.validatePublicKey(key) - this._key = key +module.exports = (keysProtobuf, randomBytes, crypto) => { + crypto = crypto || require('./crypto')(randomBytes) + + class Secp256k1PublicKey { + constructor (key) { + crypto.validatePublicKey(key) + this._key = key + } + + verify (data, sig, callback) { + ensure(callback) + crypto.hashAndVerify(this._key, sig, data, callback) + } + + marshal () { + return crypto.compressPublicKey(this._key) + } + + get bytes () { + return keysProtobuf.PublicKey.encode({ + Type: keysProtobuf.KeyType.Secp256k1, + Data: this.marshal() + }) + } + + equals (key) { + return this.bytes.equals(key.bytes) + } + + hash (callback) { + ensure(callback) + multihashing(this.bytes, 'sha2-256', callback) + } } - verify (data, sig, callback) { + class Secp256k1PrivateKey { + constructor (key, publicKey) { + this._key = key + this._publicKey = publicKey || crypto.computePublicKey(key) + crypto.validatePrivateKey(this._key) + crypto.validatePublicKey(this._publicKey) + } + + sign (message, callback) { + ensure(callback) + crypto.hashAndSign(this._key, message, callback) + } + + get public () { + return new Secp256k1PublicKey(this._publicKey) + } + + marshal () { + return this._key + } + + get bytes () { + return keysProtobuf.PrivateKey.encode({ + Type: keysProtobuf.KeyType.Secp256k1, + Data: this.marshal() + }) + } + + equals (key) { + return this.bytes.equals(key.bytes) + } + + hash (callback) { + ensure(callback) + multihashing(this.bytes, 'sha2-256', callback) + } + } + + function unmarshalSecp256k1PrivateKey (bytes, callback) { + callback(null, new Secp256k1PrivateKey(bytes), null) + } + + function unmarshalSecp256k1PublicKey (bytes) { + return new Secp256k1PublicKey(bytes) + } + + function generateKeyPair (_bits, callback) { + if (callback === undefined && typeof _bits === 'function') { + callback = _bits + } + ensure(callback) - crypto.hashAndVerify(this._key, sig, data, callback) - } - marshal () { - return crypto.compressPublicKey(this._key) - } + crypto.generateKey((err, privateKeyBytes) => { + if (err) { return callback(err) } - get bytes () { - return pbm.PublicKey.encode({ - Type: pbm.KeyType.Secp256k1, - Data: this.marshal() + let privkey + try { + privkey = new Secp256k1PrivateKey(privateKeyBytes) + } catch (err) { return callback(err) } + + callback(null, privkey) }) } - equals (key) { - return this.bytes.equals(key.bytes) - } - - hash (callback) { - ensure(callback) - multihashing(this.bytes, 'sha2-256', callback) - } -} - -class Secp256k1PrivateKey { - constructor (key, publicKey) { - this._key = key - this._publicKey = publicKey || crypto.computePublicKey(key) - crypto.validatePrivateKey(this._key) - crypto.validatePublicKey(this._publicKey) - } - - sign (message, callback) { - ensure(callback) - crypto.hashAndSign(this._key, message, callback) - } - - get public () { - return new Secp256k1PublicKey(this._publicKey) - } - - marshal () { - return this._key - } - - get bytes () { - return pbm.PrivateKey.encode({ - Type: pbm.KeyType.Secp256k1, - Data: this.marshal() - }) - } - - equals (key) { - return this.bytes.equals(key.bytes) - } - - hash (callback) { - ensure(callback) - multihashing(this.bytes, 'sha2-256', callback) - } -} - -function unmarshalSecp256k1PrivateKey (bytes, callback) { - callback(null, new Secp256k1PrivateKey(bytes), null) -} - -function unmarshalSecp256k1PublicKey (bytes) { - return new Secp256k1PublicKey(bytes) -} - -function generateKeyPair (_bits, cb) { - if (cb === undefined && typeof _bits === 'function') { - cb = _bits - } - ensure(cb) - - crypto.generateKey((err, privateKeyBytes) => { - if (err) { - return cb(err) - } - let privkey - try { - privkey = new Secp256k1PrivateKey(privateKeyBytes) - } catch (err) { - cb(err) - return + function ensure (callback) { + if (typeof callback !== 'function') { + throw new Error('callback is required') } + } - cb(null, privkey) - }) -} - -function ensure (cb) { - if (typeof cb !== 'function') { - throw new Error('callback is required') + return { + Secp256k1PublicKey, + Secp256k1PrivateKey, + unmarshalSecp256k1PrivateKey, + unmarshalSecp256k1PublicKey, + generateKeyPair } } - -module.exports = { - Secp256k1PublicKey, - Secp256k1PrivateKey, - unmarshalSecp256k1PrivateKey, - unmarshalSecp256k1PublicKey, - generateKeyPair -} diff --git a/test/secp256k1.spec.js b/test/secp256k1.spec.js index d68f653..85d250d 100644 --- a/test/secp256k1.spec.js +++ b/test/secp256k1.spec.js @@ -8,14 +8,15 @@ chai.use(dirtyChai) const Buffer = require('safe-buffer').Buffer -const secp256k1 = require('../src') -const crypto = require('../src/crypto') const libp2pCrypto = require('libp2p-crypto') -const pbm = libp2pCrypto.keys.pbm +const keysPBM = libp2pCrypto.keys.keysPBM const randomBytes = libp2pCrypto.randomBytes +const crypto = require('../src/crypto')(randomBytes) describe('secp256k1 keys', () => { let key + const secp256k1 = require('../src')(keysPBM, randomBytes) + before((done) => { secp256k1.generateKeyPair((err, _key) => { expect(err).to.not.exist() @@ -133,10 +134,13 @@ describe('secp256k1 keys', () => { describe('key generation error', () => { let generateKey + let secp256k1 before((done) => { generateKey = crypto.generateKey - crypto.generateKey = (callback) => { callback(new Error('Error generating key')) } + crypto.generateKey = (callback) => callback(new Error('Error generating key')) + secp256k1 = require('../src')(keysPBM, randomBytes, crypto) + done() }) @@ -156,10 +160,13 @@ describe('key generation error', () => { describe('handles generation of invalid key', () => { let generateKey + let secp256k1 before((done) => { generateKey = crypto.generateKey crypto.generateKey = (callback) => { callback(null, Buffer.from('not a real key')) } + secp256k1 = require('../src')(keysPBM, randomBytes, crypto) + done() }) @@ -280,13 +287,14 @@ describe('crypto functions', () => { }) describe('go interop', () => { + const secp256k1 = require('../src')(keysPBM, randomBytes) const fixtures = require('./fixtures/go-interop') it('loads a private key marshaled by go-libp2p-crypto', (done) => { // we need to first extract the key data from the protobuf, which is // normally handled by js-libp2p-crypto - const decoded = pbm.PrivateKey.decode(fixtures.privateKey) - expect(decoded.Type).to.eql(pbm.KeyType.Secp256k1) + const decoded = keysPBM.PrivateKey.decode(fixtures.privateKey) + expect(decoded.Type).to.eql(keysPBM.KeyType.Secp256k1) secp256k1.unmarshalSecp256k1PrivateKey(decoded.Data, (err, key) => { expect(err).to.not.exist() @@ -298,8 +306,8 @@ describe('go interop', () => { }) it('loads a public key marshaled by go-libp2p-crypto', (done) => { - const decoded = pbm.PublicKey.decode(fixtures.publicKey) - expect(decoded.Type).to.be.eql(pbm.KeyType.Secp256k1) + const decoded = keysPBM.PublicKey.decode(fixtures.publicKey) + expect(decoded.Type).to.be.eql(keysPBM.KeyType.Secp256k1) const key = secp256k1.unmarshalSecp256k1PublicKey(decoded.Data) expect(key).to.be.an.instanceof(secp256k1.Secp256k1PublicKey) @@ -308,8 +316,8 @@ describe('go interop', () => { }) it('generates the same signature as go-libp2p-crypto', (done) => { - const decoded = pbm.PrivateKey.decode(fixtures.privateKey) - expect(decoded.Type).to.eql(pbm.KeyType.Secp256k1) + const decoded = keysPBM.PrivateKey.decode(fixtures.privateKey) + expect(decoded.Type).to.eql(keysPBM.KeyType.Secp256k1) secp256k1.unmarshalSecp256k1PrivateKey(decoded.Data, (err, key) => { expect(err).to.not.exist()