From e57e4ffa0d2aa90a5b65988babcf0e194a715ffe Mon Sep 17 00:00:00 2001 From: Friedel Ziegelmayer Date: Thu, 19 Jan 2017 17:50:23 +0100 Subject: [PATCH 01/61] Initial commit --- .gitignore | 37 +++++++++++++++++++++++++++++++++++++ LICENSE | 21 +++++++++++++++++++++ README.md | 1 + 3 files changed, 59 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5148e52 --- /dev/null +++ b/.gitignore @@ -0,0 +1,37 @@ +# Logs +logs +*.log +npm-debug.log* + +# Runtime data +pids +*.pid +*.seed + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules +jspm_packages + +# Optional npm cache directory +.npm + +# Optional REPL history +.node_repl_history diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..bbfffbf --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 libp2p + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..f05b59b --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# js-libp2p-crypto-secp256k1 \ No newline at end of file From 4c36aeba174ce7e1cf8ff8a7fbc7ee39820e0356 Mon Sep 17 00:00:00 2001 From: Yusef Napora Date: Thu, 9 Feb 2017 06:35:39 -0500 Subject: [PATCH 02/61] feat: initial implementation --- .gitignore | 23 +-- .npmignore | 34 ++++ .travis.yml | 38 ++++ README.md | 118 ++++++++++- circle.yml | 12 ++ package.json | 65 ++++++ src/crypto/index.js | 85 ++++++++ src/index.js | 119 +++++++++++ test/fixtures/go-interop.js | 31 +++ test/secp256k1.spec.js | 391 ++++++++++++++++++++++++++++++++++++ 10 files changed, 902 insertions(+), 14 deletions(-) create mode 100644 .npmignore create mode 100644 .travis.yml create mode 100644 circle.yml create mode 100644 package.json create mode 100644 src/crypto/index.js create mode 100644 src/index.js create mode 100644 test/fixtures/go-interop.js create mode 100644 test/secp256k1.spec.js diff --git a/.gitignore b/.gitignore index 5148e52..fb8d1c9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,12 @@ +**/node_modules/ +**/*.log +test/repo-tests* + # Logs logs *.log -npm-debug.log* + +coverage # Runtime data pids @@ -14,24 +19,16 @@ lib-cov # Coverage directory used by tools like istanbul coverage -# nyc test coverage -.nyc_output - # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) .grunt # node-waf configuration .lock-wscript -# Compiled binary addons (http://nodejs.org/api/addons.html) -build/Release +build -# Dependency directories +# Dependency directory +# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git node_modules -jspm_packages -# Optional npm cache directory -.npm - -# Optional REPL history -.node_repl_history +dist diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..59335fd --- /dev/null +++ b/.npmignore @@ -0,0 +1,34 @@ +**/node_modules/ +**/*.log +test/repo-tests* + +# Logs +logs +*.log + +coverage + +# Runtime data +pids +*.pid +*.seed + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# node-waf configuration +.lock-wscript + +build + +# Dependency directory +# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git +node_modules + +test diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..c601607 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,38 @@ +sudo: false +language: node_js + +matrix: + include: + - node_js: 4 + env: CXX=g++-4.8 + - node_js: 6 + env: + - SAUCE=true + - CXX=g++-4.8 + - node_js: stable + env: CXX=g++-4.8 + +# Make sure we have new NPM. +before_install: + - npm install -g npm + +script: + - npm run lint + - npm test + - npm run coverage + + +before_script: + - export DISPLAY=:99.0 + - sh -e /etc/init.d/xvfb start + +after_success: + - npm run coverage-publish + +addons: + firefox: 'latest' + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-4.8 diff --git a/README.md b/README.md index f05b59b..3670b05 100644 --- a/README.md +++ b/README.md @@ -1 +1,117 @@ -# js-libp2p-crypto-secp256k1 \ No newline at end of file +# js-libp2p-crypto + +[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io) +[![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/) +[![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23ipfs) +[![standard-readme compliant](https://img.shields.io/badge/standard--readme-OK-green.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme) +[![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square)](https://github.com/feross/standard) +![](https://img.shields.io/badge/npm-%3E%3D3.0.0-orange.svg?style=flat-square) +![](https://img.shields.io/badge/Node.js-%3E%3D4.0.0-orange.svg?style=flat-square) + +> Support for secp256k1 keys in js-libp2p-crypto + +This repo contains a [js-libp2p-crypto](https://github.com/libp2p/js-libp2p-crypto)-compatible +implementation of cryptographic signature generation and verification using the +[secp256k1 elliptic curve](https://en.bitcoin.it/wiki/Secp256k1) popularized by Bitcoin and other +crypto currencies. + +## Table of Contents + +- [Install](#install) +- [Usage](#usage) + - [Example](#example) +- [API](#api) + - [`generateKeyPair([bits,] callback)`](#generatekeypairbits-callback) + - [`unmarshalSecp256k1PublicKey(bytes)`](#unmarshalsecp256k1publickeybytes) + - [`unmarshalSecp256k1PrivateKey(bytes, callback)`](#unmarshalsecp256k1privatekeybytes-callback) + - [`Secp256k1PublicKey`](#secp256k1publickey) + - [`.verify(data, sig, callback)`](#verifydata-sig-callback) + - [`Secp256k1PrivateKey`](#secp256k1privatekey) + - [`.public`](#public) + - [`.sign(data, callback)`](#signdata-callback) +- [Contribute](#contribute) +- [License](#license) + +## Install + +```sh +npm install --save libp2p-crypto-secp256k1 +``` + +## Usage + +This module is designed to work with [js-libp2p-crypto](https://github.com/libp2p/js-libp2p-crypto). +Installing `libp2p-crypto-secp256k1` will automatically add support for the `'secp256k1'` key type, which +can be used as an argument to the [libp2p-crypto API functions](https://github.com/libp2p/js-libp2p-crypto#api) +`generateKeyPair`, `unmarshalPublicKey`, and `marshalPrivateKey`. The keys returned from those functions will be +instances of the `Secp256k1PublicKey` or `Secp256k1PrivateKey` classes provided by this module. + +### Example + +```js +const crypto = require('libp2p-crypto') + +const msg = Buffer.from('Hello World') + +crypto.generateKeyPair('secp256k1', 256, (err, key) => { + // assuming no error, key will be an instance of Secp256k1PrivateKey + // the public key is available as key.public + key.sign(msg, (err, sig) => { + key.public.verify(msg, sig, (err, valid) => { + assert(valid, 'Something went horribly wrong') + }) + }) +}) +``` + +## API + +The functions below are the public API of this module. +For usage within libp2p-crypto, see the [libp2p-crypto API documentation](https://github.com/libp2p/js-libp2p-crypto#api). + +### `generateKeyPair([bits, ] callback)` +- `bits: Number` - Optional, included for compatibility with js-libp2p-crypto. Ignored if present; private keys will always be 256 bits. +- `callback: Function` + +### `unmarshalSecp256k1PublicKey(bytes)` +- `bytes: Buffer` + +Converts a serialized secp256k1 public key into an instance of `Secp256k1PublicKey` and returns it + +### `unmarshalSecp256k1PrivateKey(bytes, callback)` +- `bytes: Buffer` +- `callback: Function` + +Converts a serialized secp256k1 private key into an instance of `Secp256k1PrivateKey`, passing it to `callback` on success + +### `Secp256k1PublicKey` + +#### `.verify(data, sig, callback)` +- `data: Buffer` +- `sig: Buffer` +- `callback: Function` + +Calculates the SHA-256 hash of `data`, and verifies the DER-encoded signature in `sig`, passing the result to `callback` + +### `Secp256k1PrivateKey` + +#### `.public` + +Accessor for the `Secp256k1PublicKey` associated with this private key. + +#### `.sign(data, callback)` +- `data: Buffer` + +Calculates the SHA-256 hash of `data` and signs it, passing the DER-encoded signature to `callback` + +## Contribute + +Feel free to join in. All welcome. Open an [issue](https://github.com/libp2p/js-libp2p-crypto/issues)! + +This repository falls under the IPFS [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md). + +[![](https://cdn.rawgit.com/jbenet/contribute-ipfs-gif/master/img/contribute.gif)](https://github.com/ipfs/community/blob/master/contributing.md) + +## License + +[MIT](LICENSE) diff --git a/circle.yml b/circle.yml new file mode 100644 index 0000000..434211a --- /dev/null +++ b/circle.yml @@ -0,0 +1,12 @@ +machine: + node: + version: stable + +dependencies: + pre: + - google-chrome --version + - wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add - + - sudo sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' + - sudo apt-get update + - sudo apt-get --only-upgrade install google-chrome-stable + - google-chrome --version diff --git a/package.json b/package.json new file mode 100644 index 0000000..a3a0d1c --- /dev/null +++ b/package.json @@ -0,0 +1,65 @@ +{ + "name": "libp2p-crypto-secp256k1", + "version": "0.0.1", + "description": "Support for secp256k1 keys in libp2p-crypto", + "main": "src/index.js", + "browser": { + "secp256k1": "secp256k1/js" + }, + "scripts": { + "lint": "aegir-lint", + "build": "aegir-build", + "test": "npm run test:node && npm run test:browser", + "test:node": "aegir-test --env node", + "test:browser": "aegir-test --env browser", + "release": "aegir-release", + "release-minor": "aegir-release --type minor", + "release-major": "aegir-release --type major", + "coverage": "aegir-coverage", + "coverage-publish": "aegir-coverage publish" + }, + "keywords": [ + "IPFS", + "libp2p", + "crypto", + "secp256k1" + ], + "author": "Yusef Napora ", + "license": "MIT", + "dependencies": { + "libp2p-crypto": "^0.8.0", + "multihashing-async": "^0.3.0", + "nodeify": "^1.0.0", + "safe-buffer": "^5.0.1", + "secp256k1": "^3.2.5" + }, + "devDependencies": { + "aegir": "^9.2.1", + "benchmark": "^2.1.2", + "chai": "^3.5.0", + "pre-commit": "^1.1.3" + }, + "pre-commit": [ + "lint", + "test" + ], + "engines": { + "node": ">=4.0.0" + }, + "repository": { + "type": "git", + "url": "https://github.com/libp2p/js-libp2p-crypto-secp256k1.git" + }, + "bugs": { + "url": "https://github.com/libp2p/js-libp2p-crypto-secp256k1/issues" + }, + "homepage": "https://github.com/libp2p/js-libp2p-crypto-secp256k1", + "contributors": [ + "David Dias ", + "Friedel Ziegelmayer ", + "Greenkeeper ", + "Richard Littauer ", + "Yusef Napora ", + "nikuda " + ] +} diff --git a/src/crypto/index.js b/src/crypto/index.js new file mode 100644 index 0000000..ca09fbe --- /dev/null +++ b/src/crypto/index.js @@ -0,0 +1,85 @@ +'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 new file mode 100644 index 0000000..e5dafd0 --- /dev/null +++ b/src/index.js @@ -0,0 +1,119 @@ +'use strict' + +const multihashing = require('multihashing-async') +const crypto = require('./crypto') +const pbm = require('libp2p-crypto').protobuf + +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 pbm.PublicKey.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) + } +} + +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 + } + + cb(null, privkey) + }) +} + +function ensure (cb) { + if (typeof cb !== 'function') { + throw new Error('callback is required') + } +} + +module.exports = { + Secp256k1PublicKey, + Secp256k1PrivateKey, + unmarshalSecp256k1PrivateKey, + unmarshalSecp256k1PublicKey, + generateKeyPair +} diff --git a/test/fixtures/go-interop.js b/test/fixtures/go-interop.js new file mode 100644 index 0000000..8f18c1a --- /dev/null +++ b/test/fixtures/go-interop.js @@ -0,0 +1,31 @@ +'use strict' + +const Buffer = require('safe-buffer').Buffer + +// The keypair and signature below were generated in a gore repl session (https://github.com/motemen/gore) +// using the secp256k1 fork of go-libp2p-crypto by github user @vyzo +// +// gore> :import github.com/vyzo/go-libp2p-crypto +// gore> :import crypto/rand +// gore> :import io/ioutil +// gore> priv, pub, err := crypto.GenerateKeyPairWithReader(crypto.Secp256k1, 256, rand.Reader) +// gore> privBytes, err := priv.Bytes() +// gore> pubBytes, err := pub.Bytes() +// gore> msg := []byte("hello! and welcome to some awesome crypto primitives") +// gore> sig, err := priv.Sign(msg) +// gore> ioutil.WriteFile("/tmp/secp-go-priv.bin", privBytes, 0644) +// gore> ioutil.WriteFile("/tmp/secp-go-pub.bin", pubBytes, 0644) +// gore> ioutil.WriteFile("/tmp/secp-go-sig.bin", sig, 0644) +// +// The generated files were then read in a node repl with e.g.: +// > fs.readFileSync('/tmp/secp-go-pub.bin').toString('hex') +// '08021221029c0ce5d53646ed47112560297a3e59b78b8cbd4bae37c7a0c236eeb91d0fbeaf' +// +// and the results copy/pasted in here + +module.exports = { + privateKey: Buffer.from('08021220358f15db8c2014d570e8e3a622454e2273975a3cca443ec0c45375b13d381d18', 'hex'), + publicKey: Buffer.from('08021221029c0ce5d53646ed47112560297a3e59b78b8cbd4bae37c7a0c236eeb91d0fbeaf', 'hex'), + message: Buffer.from('hello! and welcome to some awesome crypto primitives', 'utf-8'), + signature: Buffer.from('304402200e4c629e9f5d99439115e60989cd40087f6978c36078b0b50cf3d30af5c38d4102204110342c8e7f0809897c1c7a66e49e1c6b7cb0a6ed6993640ec2fe742c1899a9', 'hex') +} diff --git a/test/secp256k1.spec.js b/test/secp256k1.spec.js new file mode 100644 index 0000000..65a95e8 --- /dev/null +++ b/test/secp256k1.spec.js @@ -0,0 +1,391 @@ +/* eslint-env mocha */ +'use strict' + +const expect = require('chai').expect +const Buffer = require('safe-buffer').Buffer + +const secp256k1 = require('../src') +const crypto = require('../src/crypto') +const libp2pCrypto = require('libp2p-crypto') +const pbm = libp2pCrypto.protobuf +const randomBytes = libp2pCrypto.randomBytes + +describe('secp256k1 keys', () => { + let key + before((done) => { + secp256k1.generateKeyPair((err, _key) => { + if (err) return done(err) + key = _key + done() + }) + }) + + it('generates a valid key', (done) => { + expect( + key + ).to.be.an.instanceof( + secp256k1.Secp256k1PrivateKey + ) + expect( + key.public + ).to.be.an.instanceof( + secp256k1.Secp256k1PublicKey + ) + + key.hash((err, digest) => { + if (err) return done(err) + + expect(digest).to.have.length(34) + + key.public.hash((err, digest) => { + if (err) return done(err) + + expect(digest).to.have.length(34) + done() + }) + }) + }) + + it('optionally accepts a `bits` argument when generating a key', (done) => { + secp256k1.generateKeyPair(256, (err, _key) => { + expect(err).to.not.exist + expect(_key).to.be.an.instanceof(secp256k1.Secp256k1PrivateKey) + done() + }) + }) + + it('requires a callback to generate a key', (done) => { + expect(() => + secp256k1.generateKeyPair() + ).to.throw() + done() + }) + + it('signs', (done) => { + const text = randomBytes(512) + + key.sign(text, (err, sig) => { + if (err) { + return done(err) + } + + key.public.verify(text, sig, (err, res) => { + if (err) { + return done(err) + } + + expect(res).to.be.eql(true) + done() + }) + }) + }) + + it('encoding', (done) => { + const keyMarshal = key.marshal() + secp256k1.unmarshalSecp256k1PrivateKey(keyMarshal, (err, key2) => { + if (err) { + return done(err) + } + const keyMarshal2 = key2.marshal() + + expect( + keyMarshal + ).to.be.eql( + keyMarshal2 + ) + + const pk = key.public + const pkMarshal = pk.marshal() + const pk2 = secp256k1.unmarshalSecp256k1PublicKey(pkMarshal) + const pkMarshal2 = pk2.marshal() + + expect( + pkMarshal + ).to.be.eql( + pkMarshal2 + ) + done() + }) + }) + + describe('key equals', () => { + it('equals itself', () => { + expect( + key.equals(key) + ).to.be.eql( + true + ) + + expect( + key.public.equals(key.public) + ).to.be.eql( + true + ) + }) + + it('not equals other key', (done) => { + secp256k1.generateKeyPair(256, (err, key2) => { + if (err) return done(err) + + expect( + key.equals(key2) + ).to.be.eql( + false + ) + + expect( + key2.equals(key) + ).to.be.eql( + false + ) + + expect( + key.public.equals(key2.public) + ).to.be.eql( + false + ) + + expect( + key2.public.equals(key.public) + ).to.be.eql( + false + ) + done() + }) + }) + }) + + it('sign and verify', (done) => { + const data = Buffer.from('hello world') + key.sign(data, (err, sig) => { + if (err) { + return done(err) + } + + key.public.verify(data, sig, (err, valid) => { + if (err) { + return done(err) + } + expect(valid).to.be.eql(true) + done() + }) + }) + }) + + it('fails to verify for different data', (done) => { + const data = Buffer.from('hello world') + key.sign(data, (err, sig) => { + if (err) { + return done(err) + } + + key.public.verify(Buffer.from('hello'), sig, (err, valid) => { + if (err) { + return done(err) + } + expect(valid).to.be.eql(false) + done() + }) + }) + }) +}) + +describe('key generation error', () => { + let generateKey + + before((done) => { + generateKey = crypto.generateKey + crypto.generateKey = (callback) => { callback(new Error('Error generating key')) } + done() + }) + + after((done) => { + crypto.generateKey = generateKey + done() + }) + + it('returns an error if key generation fails', (done) => { + secp256k1.generateKeyPair((err, key) => { + expect(err).to.exist + expect(key).to.not.exist + done() + }) + }) +}) + +describe('handles generation of invalid key', () => { + let generateKey + + before((done) => { + generateKey = crypto.generateKey + crypto.generateKey = (callback) => { callback(null, Buffer.from('not a real key')) } + done() + }) + + after((done) => { + crypto.generateKey = generateKey + done() + }) + + it('returns an error if key generator returns an invalid key', (done) => { + secp256k1.generateKeyPair((err, key) => { + expect(err).to.exist + expect(key).to.not.exist + done() + }) + }) +}) + +describe('crypto functions', () => { + let privKey, pubKey + + before((done) => { + crypto.generateKey((err, _key) => { + if (err) return done(err) + privKey = _key + pubKey = crypto.computePublicKey(privKey) + done() + }) + }) + + it('generates valid keys', (done) => { + expect(() => { + crypto.validatePrivateKey(privKey) + crypto.validatePublicKey(pubKey) + }).to.not.throw() + done() + }) + + it('does not validate an invalid key', (done) => { + expect(() => { + crypto.validatePublicKey(Buffer.from('42')) + }).to.throw() + + expect(() => { + crypto.validatePrivateKey(Buffer.from('42')) + }).to.throw() + done() + }) + + it('validates a correct signature', (done) => { + crypto.hashAndSign(privKey, Buffer.from('hello'), (err, sig) => { + if (err) return done(err) + crypto.hashAndVerify(pubKey, sig, Buffer.from('hello'), (err, valid) => { + if (err) return done(err) + expect(valid).to.be.eql(true) + done() + }) + }) + }) + + it('errors if given a null buffer to sign', (done) => { + crypto.hashAndSign(privKey, null, (err, sig) => { + expect(err).to.exist + expect(sig).to.not.exist + done() + }) + }) + + it('errors when signing with an invalid key', (done) => { + crypto.hashAndSign(Buffer.from('42'), Buffer.from('Hello'), (err, sig) => { + expect(err).to.exist + expect(sig).to.not.exist + done() + }) + }) + + it('errors if given a null buffer to validate', (done) => { + crypto.hashAndSign(privKey, Buffer.from('hello'), (err, sig) => { + if (err) return done(err) + + crypto.hashAndVerify(privKey, sig, null, (err, valid) => { + expect(err).to.exist + expect(valid).to.not.exist + done() + }) + }) + }) + + it('errors when validating a message with an invalid signature', (done) => { + crypto.hashAndVerify(pubKey, Buffer.from('invalid-sig'), Buffer.from('hello'), (err, valid) => { + expect(err).to.exist + expect(valid).to.not.exist + done() + }) + }) + + it('errors when signing with an invalid key', (done) => { + crypto.hashAndSign(Buffer.from('42'), Buffer.from('Hello'), (err, sig) => { + expect(err).to.exist + expect(sig).to.not.exist + done() + }) + }) + + it('throws when compressing an invalid public key', (done) => { + expect(() => { + crypto.compressPublicKey(Buffer.from('42')) + }).to.throw() + done() + }) + + it('throws when decompressing an invalid public key', (done) => { + expect(() => { + crypto.decompressPublicKey(Buffer.from('42')) + }).to.throw() + done() + }) + + it('compresses/decompresses a valid public key', (done) => { + const decompressed = crypto.decompressPublicKey(pubKey) + expect(decompressed).to.exist + expect(decompressed.length).to.be.eql(65) + const recompressed = crypto.compressPublicKey(decompressed) + expect(recompressed).to.be.eql(pubKey) + done() + }) +}) + +describe('go interop', () => { + 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.be.eql(pbm.KeyType.Secp256k1) + + secp256k1.unmarshalSecp256k1PrivateKey(decoded.Data, (err, key) => { + if (err) return done(err) + + expect(key).to.be.an.instanceof(secp256k1.Secp256k1PrivateKey) + expect(key.bytes).to.be.eql(fixtures.privateKey) + done() + }) + }) + + 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 key = secp256k1.unmarshalSecp256k1PublicKey(decoded.Data) + expect(key).to.be.an.instanceof(secp256k1.Secp256k1PublicKey) + expect(key.bytes).to.be.eql(fixtures.publicKey) + done() + }) + + it('generates the same signature as go-libp2p-crypto', (done) => { + const decoded = pbm.PrivateKey.decode(fixtures.privateKey) + expect(decoded.Type).to.be.eql(pbm.KeyType.Secp256k1) + + secp256k1.unmarshalSecp256k1PrivateKey(decoded.Data, (err, key) => { + if (err) return done(err) + + key.sign(fixtures.message, (err, sig) => { + if (err) return done(err) + expect(sig).to.be.eql(fixtures.signature) + done() + }) + }) + }) +}) From 4c744d3b606de75bc225e1533b67254ccd188ef4 Mon Sep 17 00:00:00 2001 From: Friedel Ziegelmayer Date: Thu, 9 Feb 2017 12:36:35 +0100 Subject: [PATCH 03/61] chore: update dependencies --- package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index a3a0d1c..59e1f25 100644 --- a/package.json +++ b/package.json @@ -28,16 +28,16 @@ "license": "MIT", "dependencies": { "libp2p-crypto": "^0.8.0", - "multihashing-async": "^0.3.0", + "multihashing-async": "^0.4.1", "nodeify": "^1.0.0", "safe-buffer": "^5.0.1", "secp256k1": "^3.2.5" }, "devDependencies": { - "aegir": "^9.2.1", - "benchmark": "^2.1.2", + "aegir": "^10.0.0", + "benchmark": "^2.1.3", "chai": "^3.5.0", - "pre-commit": "^1.1.3" + "pre-commit": "^1.2.2" }, "pre-commit": [ "lint", From 418a0885ad3ab5c95173751277207b9330ea810c Mon Sep 17 00:00:00 2001 From: Friedel Ziegelmayer Date: Thu, 9 Feb 2017 12:43:43 +0100 Subject: [PATCH 04/61] chore: update contributors --- package.json | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 59e1f25..fccb61a 100644 --- a/package.json +++ b/package.json @@ -55,11 +55,7 @@ }, "homepage": "https://github.com/libp2p/js-libp2p-crypto-secp256k1", "contributors": [ - "David Dias ", "Friedel Ziegelmayer ", - "Greenkeeper ", - "Richard Littauer ", - "Yusef Napora ", - "nikuda " + "Yusef Napora " ] -} +} \ No newline at end of file From 288c9cff8f129355b526e0eed6c5b509cf4292b9 Mon Sep 17 00:00:00 2001 From: Friedel Ziegelmayer Date: Thu, 9 Feb 2017 12:43:44 +0100 Subject: [PATCH 05/61] chore: release version v0.1.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index fccb61a..b84d24c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "libp2p-crypto-secp256k1", - "version": "0.0.1", + "version": "0.1.0", "description": "Support for secp256k1 keys in libp2p-crypto", "main": "src/index.js", "browser": { From 73b8b39de5108644002bf9d88f454478e944718a Mon Sep 17 00:00:00 2001 From: David Dias Date: Thu, 9 Feb 2017 08:03:23 -0800 Subject: [PATCH 06/61] chore: ^ to ~ --- package.json | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index b84d24c..14f997b 100644 --- a/package.json +++ b/package.json @@ -27,8 +27,8 @@ "author": "Yusef Napora ", "license": "MIT", "dependencies": { - "libp2p-crypto": "^0.8.0", - "multihashing-async": "^0.4.1", + "libp2p-crypto": "~0.8.0", + "multihashing-async": "~0.4.2", "nodeify": "^1.0.0", "safe-buffer": "^5.0.1", "secp256k1": "^3.2.5" @@ -44,7 +44,8 @@ "test" ], "engines": { - "node": ">=4.0.0" + "node": ">=4.0.0", + "npm": ">=3.0.0" }, "repository": { "type": "git", @@ -58,4 +59,4 @@ "Friedel Ziegelmayer ", "Yusef Napora " ] -} \ No newline at end of file +} From de8b16b00bf75f7c386b9e10347833d434b0028d Mon Sep 17 00:00:00 2001 From: David Dias Date: Thu, 9 Feb 2017 08:05:13 -0800 Subject: [PATCH 07/61] chore: update contributors --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 14f997b..9d368b2 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,8 @@ }, "homepage": "https://github.com/libp2p/js-libp2p-crypto-secp256k1", "contributors": [ + "David Dias ", "Friedel Ziegelmayer ", "Yusef Napora " ] -} +} \ No newline at end of file From 4c2d42984daa20b2445d4cf2ba4e81c31422c5c7 Mon Sep 17 00:00:00 2001 From: David Dias Date: Thu, 9 Feb 2017 08:05:13 -0800 Subject: [PATCH 08/61] chore: release version v0.1.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9d368b2..d76b22a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "libp2p-crypto-secp256k1", - "version": "0.1.0", + "version": "0.1.1", "description": "Support for secp256k1 keys in libp2p-crypto", "main": "src/index.js", "browser": { From 42678bba302ca12bddfb73892751fb89e4ae61a1 Mon Sep 17 00:00:00 2001 From: David Dias Date: Thu, 9 Feb 2017 14:08:25 -0800 Subject: [PATCH 10/61] chore: release version v0.1.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d76b22a..31d0e41 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "libp2p-crypto-secp256k1", - "version": "0.1.1", + "version": "0.1.2", "description": "Support for secp256k1 keys in libp2p-crypto", "main": "src/index.js", "browser": { From 9430e7f91e09539675f50dfa6b932e897ab2f3b8 Mon Sep 17 00:00:00 2001 From: David Dias Date: Fri, 10 Feb 2017 18:33:54 -0800 Subject: [PATCH 11/61] chore: update deps --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 31d0e41..98dcee5 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "author": "Yusef Napora ", "license": "MIT", "dependencies": { - "libp2p-crypto": "~0.8.0", + "libp2p-crypto": "~0.8.4", "multihashing-async": "~0.4.2", "nodeify": "^1.0.0", "safe-buffer": "^5.0.1", @@ -60,4 +60,4 @@ "Friedel Ziegelmayer ", "Yusef Napora " ] -} \ No newline at end of file +} From 35abf8fcb40fe17cec0e1277b9a66a0ddc3fbc18 Mon Sep 17 00:00:00 2001 From: David Dias Date: Fri, 10 Feb 2017 18:36:19 -0800 Subject: [PATCH 12/61] chore: update contributors --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 98dcee5..9cf9ffb 100644 --- a/package.json +++ b/package.json @@ -60,4 +60,4 @@ "Friedel Ziegelmayer ", "Yusef Napora " ] -} +} \ No newline at end of file From 638ea963e6c5184cc21f7490ed36ec9d5fe9b1ca Mon Sep 17 00:00:00 2001 From: David Dias Date: Fri, 10 Feb 2017 18:36:19 -0800 Subject: [PATCH 13/61] chore: release version v0.1.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9cf9ffb..517fe75 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "libp2p-crypto-secp256k1", - "version": "0.1.2", + "version": "0.1.3", "description": "Support for secp256k1 keys in libp2p-crypto", "main": "src/index.js", "browser": { From e66383137f05b29f5506543ee2af4e74f37daf1a Mon Sep 17 00:00:00 2001 From: David Dias Date: Sat, 11 Feb 2017 17:27:29 -0800 Subject: [PATCH 14/61] chore: add missing dependency --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 517fe75..16eaa5b 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "author": "Yusef Napora ", "license": "MIT", "dependencies": { + "async": "^2.1.4", "libp2p-crypto": "~0.8.4", "multihashing-async": "~0.4.2", "nodeify": "^1.0.0", @@ -60,4 +61,4 @@ "Friedel Ziegelmayer ", "Yusef Napora " ] -} \ No newline at end of file +} From 647fab7170f8f0d20d06b7b2496d2fa05badde79 Mon Sep 17 00:00:00 2001 From: David Dias Date: Sat, 11 Feb 2017 17:27:51 -0800 Subject: [PATCH 15/61] chore: update contributors --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 16eaa5b..b3f940e 100644 --- a/package.json +++ b/package.json @@ -61,4 +61,4 @@ "Friedel Ziegelmayer ", "Yusef Napora " ] -} +} \ No newline at end of file From d8b0c74ec9c22ffa7a2356ec593bc812ea79b81e Mon Sep 17 00:00:00 2001 From: David Dias Date: Sat, 11 Feb 2017 17:27:51 -0800 Subject: [PATCH 16/61] chore: release version v0.1.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b3f940e..dc96e55 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "libp2p-crypto-secp256k1", - "version": "0.1.3", + "version": "0.1.4", "description": "Support for secp256k1 keys in libp2p-crypto", "main": "src/index.js", "browser": { From 363cda56da2687a38ea404162c395371d98fd3cf Mon Sep 17 00:00:00 2001 From: David Dias Date: Fri, 21 Jul 2017 11:38:33 -0700 Subject: [PATCH 17/61] chore: update ci --- .travis.yml | 5 ++--- circle.yml | 8 +++++--- package.json | 18 +++++++++--------- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/.travis.yml b/.travis.yml index c601607..5cc672b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,20 +7,19 @@ matrix: env: CXX=g++-4.8 - node_js: 6 env: - - SAUCE=true - CXX=g++-4.8 - node_js: stable env: CXX=g++-4.8 # Make sure we have new NPM. before_install: - - npm install -g npm + - npm install -g npm@4 script: - npm run lint - npm test - npm run coverage - + - make test before_script: - export DISPLAY=:99.0 diff --git a/circle.yml b/circle.yml index 434211a..56f7efb 100644 --- a/circle.yml +++ b/circle.yml @@ -5,8 +5,10 @@ machine: dependencies: pre: - google-chrome --version - - wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add - - - sudo sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' + - curl -L -o google-chrome.deb https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb + - sudo dpkg -i google-chrome.deb || true - sudo apt-get update - - sudo apt-get --only-upgrade install google-chrome-stable + - sudo apt-get install -f + - sudo apt-get install --only-upgrade lsb-base + - sudo dpkg -i google-chrome.deb - google-chrome --version diff --git a/package.json b/package.json index dc96e55..9722641 100644 --- a/package.json +++ b/package.json @@ -27,17 +27,17 @@ "author": "Yusef Napora ", "license": "MIT", "dependencies": { - "async": "^2.1.4", - "libp2p-crypto": "~0.8.4", - "multihashing-async": "~0.4.2", - "nodeify": "^1.0.0", - "safe-buffer": "^5.0.1", - "secp256k1": "^3.2.5" + "async": "^2.5.0", + "libp2p-crypto": "~0.8.8", + "multihashing-async": "~0.4.6", + "nodeify": "^1.0.1", + "safe-buffer": "^5.1.1", + "secp256k1": "^3.3.0" }, "devDependencies": { - "aegir": "^10.0.0", - "benchmark": "^2.1.3", - "chai": "^3.5.0", + "aegir": "^11.0.2", + "benchmark": "^2.1.4", + "chai": "^4.1.0", "pre-commit": "^1.2.2" }, "pre-commit": [ From 4ee48a737a34bebff2693db83a49e5c851e39529 Mon Sep 17 00:00:00 2001 From: David Dias Date: Sat, 22 Jul 2017 11:12:30 -0700 Subject: [PATCH 18/61] feat: next libp2p-crypto (#4) * feat: next libp2p-crypto * chore: update deps --- package.json | 5 +- src/crypto/index.js | 4 +- src/index.js | 2 +- test/secp256k1.spec.js | 203 ++++++++++++++--------------------------- 4 files changed, 74 insertions(+), 140 deletions(-) diff --git a/package.json b/package.json index 9722641..e859743 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "license": "MIT", "dependencies": { "async": "^2.5.0", - "libp2p-crypto": "~0.8.8", + "libp2p-crypto": "~0.9.0", "multihashing-async": "~0.4.6", "nodeify": "^1.0.1", "safe-buffer": "^5.1.1", @@ -38,6 +38,7 @@ "aegir": "^11.0.2", "benchmark": "^2.1.4", "chai": "^4.1.0", + "dirty-chai": "^2.0.1", "pre-commit": "^1.2.2" }, "pre-commit": [ @@ -61,4 +62,4 @@ "Friedel Ziegelmayer ", "Yusef Napora " ] -} \ No newline at end of file +} diff --git a/src/crypto/index.js b/src/crypto/index.js index ca09fbe..7e2b494 100644 --- a/src/crypto/index.js +++ b/src/crypto/index.js @@ -28,7 +28,7 @@ exports.hashAndSign = function (key, msg, callback) { }) multihashing.digest(msg, HASH_ALGORITHM, (err, digest) => { - if (err) return done(err) + if (err) { return done(err) } try { const sig = secp256k1.sign(digest, key) const sigDER = secp256k1.signatureExport(sig.signature) @@ -45,7 +45,7 @@ exports.hashAndVerify = function (key, sig, msg, callback) { }) multihashing.digest(msg, HASH_ALGORITHM, (err, digest) => { - if (err) return done(err) + if (err) { return done(err) } try { sig = secp256k1.signatureImport(sig) const valid = secp256k1.verify(digest, sig, key) diff --git a/src/index.js b/src/index.js index e5dafd0..3d3c030 100644 --- a/src/index.js +++ b/src/index.js @@ -2,7 +2,7 @@ const multihashing = require('multihashing-async') const crypto = require('./crypto') -const pbm = require('libp2p-crypto').protobuf +const pbm = require('libp2p-crypto').keys.pbm class Secp256k1PublicKey { constructor (key) { diff --git a/test/secp256k1.spec.js b/test/secp256k1.spec.js index 65a95e8..d68f653 100644 --- a/test/secp256k1.spec.js +++ b/test/secp256k1.spec.js @@ -1,45 +1,39 @@ /* eslint-env mocha */ 'use strict' -const expect = require('chai').expect +const chai = require('chai') +const dirtyChai = require('dirty-chai') +const expect = chai.expect +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.protobuf +const pbm = libp2pCrypto.keys.pbm const randomBytes = libp2pCrypto.randomBytes describe('secp256k1 keys', () => { let key before((done) => { secp256k1.generateKeyPair((err, _key) => { - if (err) return done(err) + expect(err).to.not.exist() key = _key done() }) }) it('generates a valid key', (done) => { - expect( - key - ).to.be.an.instanceof( - secp256k1.Secp256k1PrivateKey - ) - expect( - key.public - ).to.be.an.instanceof( - secp256k1.Secp256k1PublicKey - ) + expect(key).to.be.an.instanceof(secp256k1.Secp256k1PrivateKey) + expect(key.public).to.be.an.instanceof(secp256k1.Secp256k1PublicKey) key.hash((err, digest) => { - if (err) return done(err) - + expect(err).to.not.exist() expect(digest).to.have.length(34) key.public.hash((err, digest) => { - if (err) return done(err) - + expect(err).to.not.exist() expect(digest).to.have.length(34) done() }) @@ -48,33 +42,25 @@ describe('secp256k1 keys', () => { it('optionally accepts a `bits` argument when generating a key', (done) => { secp256k1.generateKeyPair(256, (err, _key) => { - expect(err).to.not.exist + expect(err).to.not.exist() expect(_key).to.be.an.instanceof(secp256k1.Secp256k1PrivateKey) done() }) }) - it('requires a callback to generate a key', (done) => { - expect(() => - secp256k1.generateKeyPair() - ).to.throw() - done() + it('requires a callback to generate a key', () => { + expect(() => secp256k1.generateKeyPair()).to.throw() }) it('signs', (done) => { const text = randomBytes(512) key.sign(text, (err, sig) => { - if (err) { - return done(err) - } + expect(err).to.not.exist() key.public.verify(text, sig, (err, res) => { - if (err) { - return done(err) - } - - expect(res).to.be.eql(true) + expect(err).to.not.exist() + expect(res).to.equal(true) done() }) }) @@ -83,73 +69,36 @@ describe('secp256k1 keys', () => { it('encoding', (done) => { const keyMarshal = key.marshal() secp256k1.unmarshalSecp256k1PrivateKey(keyMarshal, (err, key2) => { - if (err) { - return done(err) - } + expect(err).to.not.exist() const keyMarshal2 = key2.marshal() - expect( - keyMarshal - ).to.be.eql( - keyMarshal2 - ) + expect(keyMarshal).to.eql(keyMarshal2) const pk = key.public const pkMarshal = pk.marshal() const pk2 = secp256k1.unmarshalSecp256k1PublicKey(pkMarshal) const pkMarshal2 = pk2.marshal() - expect( - pkMarshal - ).to.be.eql( - pkMarshal2 - ) + expect(pkMarshal).to.eql(pkMarshal2) done() }) }) describe('key equals', () => { it('equals itself', () => { - expect( - key.equals(key) - ).to.be.eql( - true - ) + expect(key.equals(key)).to.eql(true) - expect( - key.public.equals(key.public) - ).to.be.eql( - true - ) + expect(key.public.equals(key.public)).to.eql(true) }) it('not equals other key', (done) => { secp256k1.generateKeyPair(256, (err, key2) => { - if (err) return done(err) + expect(err).to.not.exist() - expect( - key.equals(key2) - ).to.be.eql( - false - ) - - expect( - key2.equals(key) - ).to.be.eql( - false - ) - - expect( - key.public.equals(key2.public) - ).to.be.eql( - false - ) - - expect( - key2.public.equals(key.public) - ).to.be.eql( - false - ) + expect(key.equals(key2)).to.eql(false) + expect(key2.equals(key)).to.eql(false) + expect(key.public.equals(key2.public)).to.eql(false) + expect(key2.public.equals(key.public)).to.eql(false) done() }) }) @@ -158,15 +107,11 @@ describe('secp256k1 keys', () => { it('sign and verify', (done) => { const data = Buffer.from('hello world') key.sign(data, (err, sig) => { - if (err) { - return done(err) - } + expect(err).to.not.exist() key.public.verify(data, sig, (err, valid) => { - if (err) { - return done(err) - } - expect(valid).to.be.eql(true) + expect(err).to.not.exist() + expect(valid).to.eql(true) done() }) }) @@ -175,15 +120,11 @@ describe('secp256k1 keys', () => { it('fails to verify for different data', (done) => { const data = Buffer.from('hello world') key.sign(data, (err, sig) => { - if (err) { - return done(err) - } + expect(err).to.not.exist() key.public.verify(Buffer.from('hello'), sig, (err, valid) => { - if (err) { - return done(err) - } - expect(valid).to.be.eql(false) + expect(err).to.not.exist() + expect(valid).to.eql(false) done() }) }) @@ -206,8 +147,8 @@ describe('key generation error', () => { it('returns an error if key generation fails', (done) => { secp256k1.generateKeyPair((err, key) => { - expect(err).to.exist - expect(key).to.not.exist + expect(err).to.exist() + expect(key).to.not.exist() done() }) }) @@ -229,19 +170,20 @@ describe('handles generation of invalid key', () => { it('returns an error if key generator returns an invalid key', (done) => { secp256k1.generateKeyPair((err, key) => { - expect(err).to.exist - expect(key).to.not.exist + expect(err).to.exist() + expect(key).to.not.exist() done() }) }) }) describe('crypto functions', () => { - let privKey, pubKey + let privKey + let pubKey before((done) => { crypto.generateKey((err, _key) => { - if (err) return done(err) + expect(err).to.not.exist() privKey = _key pubKey = crypto.computePublicKey(privKey) done() @@ -257,22 +199,17 @@ describe('crypto functions', () => { }) it('does not validate an invalid key', (done) => { - expect(() => { - crypto.validatePublicKey(Buffer.from('42')) - }).to.throw() - - expect(() => { - crypto.validatePrivateKey(Buffer.from('42')) - }).to.throw() + expect(() => crypto.validatePublicKey(Buffer.from('42'))).to.throw() + expect(() => crypto.validatePrivateKey(Buffer.from('42'))).to.throw() done() }) it('validates a correct signature', (done) => { crypto.hashAndSign(privKey, Buffer.from('hello'), (err, sig) => { - if (err) return done(err) + expect(err).to.not.exist() crypto.hashAndVerify(pubKey, sig, Buffer.from('hello'), (err, valid) => { - if (err) return done(err) - expect(valid).to.be.eql(true) + expect(err).to.not.exist() + expect(valid).to.equal(true) done() }) }) @@ -280,27 +217,27 @@ describe('crypto functions', () => { it('errors if given a null buffer to sign', (done) => { crypto.hashAndSign(privKey, null, (err, sig) => { - expect(err).to.exist - expect(sig).to.not.exist + expect(err).to.exist() + expect(sig).to.not.exist() done() }) }) it('errors when signing with an invalid key', (done) => { crypto.hashAndSign(Buffer.from('42'), Buffer.from('Hello'), (err, sig) => { - expect(err).to.exist - expect(sig).to.not.exist + expect(err).to.exist() + expect(sig).to.not.exist() done() }) }) it('errors if given a null buffer to validate', (done) => { crypto.hashAndSign(privKey, Buffer.from('hello'), (err, sig) => { - if (err) return done(err) + expect(err).to.not.exist() crypto.hashAndVerify(privKey, sig, null, (err, valid) => { - expect(err).to.exist - expect(valid).to.not.exist + expect(err).to.exist() + expect(valid).to.not.exist() done() }) }) @@ -308,40 +245,36 @@ describe('crypto functions', () => { it('errors when validating a message with an invalid signature', (done) => { crypto.hashAndVerify(pubKey, Buffer.from('invalid-sig'), Buffer.from('hello'), (err, valid) => { - expect(err).to.exist - expect(valid).to.not.exist + expect(err).to.exist() + expect(valid).to.not.exist() done() }) }) it('errors when signing with an invalid key', (done) => { crypto.hashAndSign(Buffer.from('42'), Buffer.from('Hello'), (err, sig) => { - expect(err).to.exist - expect(sig).to.not.exist + expect(err).to.exist() + expect(sig).to.not.exist() done() }) }) it('throws when compressing an invalid public key', (done) => { - expect(() => { - crypto.compressPublicKey(Buffer.from('42')) - }).to.throw() + expect(() => crypto.compressPublicKey(Buffer.from('42'))).to.throw() done() }) it('throws when decompressing an invalid public key', (done) => { - expect(() => { - crypto.decompressPublicKey(Buffer.from('42')) - }).to.throw() + expect(() => crypto.decompressPublicKey(Buffer.from('42'))).to.throw() done() }) it('compresses/decompresses a valid public key', (done) => { const decompressed = crypto.decompressPublicKey(pubKey) - expect(decompressed).to.exist + expect(decompressed).to.exist() expect(decompressed.length).to.be.eql(65) const recompressed = crypto.compressPublicKey(decompressed) - expect(recompressed).to.be.eql(pubKey) + expect(recompressed).to.eql(pubKey) done() }) }) @@ -353,13 +286,13 @@ describe('go interop', () => { // 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.be.eql(pbm.KeyType.Secp256k1) + expect(decoded.Type).to.eql(pbm.KeyType.Secp256k1) secp256k1.unmarshalSecp256k1PrivateKey(decoded.Data, (err, key) => { - if (err) return done(err) + expect(err).to.not.exist() expect(key).to.be.an.instanceof(secp256k1.Secp256k1PrivateKey) - expect(key.bytes).to.be.eql(fixtures.privateKey) + expect(key.bytes).to.eql(fixtures.privateKey) done() }) }) @@ -370,20 +303,20 @@ describe('go interop', () => { const key = secp256k1.unmarshalSecp256k1PublicKey(decoded.Data) expect(key).to.be.an.instanceof(secp256k1.Secp256k1PublicKey) - expect(key.bytes).to.be.eql(fixtures.publicKey) + expect(key.bytes).to.eql(fixtures.publicKey) done() }) it('generates the same signature as go-libp2p-crypto', (done) => { const decoded = pbm.PrivateKey.decode(fixtures.privateKey) - expect(decoded.Type).to.be.eql(pbm.KeyType.Secp256k1) + expect(decoded.Type).to.eql(pbm.KeyType.Secp256k1) secp256k1.unmarshalSecp256k1PrivateKey(decoded.Data, (err, key) => { - if (err) return done(err) + expect(err).to.not.exist() key.sign(fixtures.message, (err, sig) => { - if (err) return done(err) - expect(sig).to.be.eql(fixtures.signature) + expect(err).to.not.exist() + expect(sig).to.eql(fixtures.signature) done() }) }) From c21454c4e84b5effd97b21ef98c7afe4430d7b79 Mon Sep 17 00:00:00 2001 From: David Dias Date: Sat, 22 Jul 2017 11:30:07 -0700 Subject: [PATCH 19/61] chore: update contributors --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e859743..6762e9b 100644 --- a/package.json +++ b/package.json @@ -62,4 +62,4 @@ "Friedel Ziegelmayer ", "Yusef Napora " ] -} +} \ No newline at end of file From 1013becd668adad0d740cbc4b63622cbddab2236 Mon Sep 17 00:00:00 2001 From: David Dias Date: Sat, 22 Jul 2017 11:30:07 -0700 Subject: [PATCH 20/61] chore: release version v0.2.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6762e9b..e7052b9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "libp2p-crypto-secp256k1", - "version": "0.1.4", + "version": "0.2.0", "description": "Support for secp256k1 keys in libp2p-crypto", "main": "src/index.js", "browser": { From 41c03a86a5ab840a119039883b492c5e90745b29 Mon Sep 17 00:00:00 2001 From: David Dias Date: Sat, 22 Jul 2017 11:40:00 -0700 Subject: [PATCH 21/61] chore: update deps --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e7052b9..117069a 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "license": "MIT", "dependencies": { "async": "^2.5.0", - "libp2p-crypto": "~0.9.0", + "libp2p-crypto": "~0.9.1", "multihashing-async": "~0.4.6", "nodeify": "^1.0.1", "safe-buffer": "^5.1.1", From 838ecdbaef6c902361b4d30a9867fefaeb62fd31 Mon Sep 17 00:00:00 2001 From: David Dias Date: Sat, 22 Jul 2017 12:19:08 -0700 Subject: [PATCH 23/61] chore: release version v0.2.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 117069a..5068d0d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "libp2p-crypto-secp256k1", - "version": "0.2.0", + "version": "0.2.1", "description": "Support for secp256k1 keys in libp2p-crypto", "main": "src/index.js", "browser": { From 8401154102fb2f88c08652f9ff2d8677b75561f4 Mon Sep 17 00:00:00 2001 From: David Dias Date: Sat, 22 Jul 2017 12:28:53 -0700 Subject: [PATCH 24/61] chore: update deps --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 5068d0d..c033d32 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "license": "MIT", "dependencies": { "async": "^2.5.0", - "libp2p-crypto": "~0.9.1", + "libp2p-crypto": "~0.9.2", "multihashing-async": "~0.4.6", "nodeify": "^1.0.1", "safe-buffer": "^5.1.1", @@ -62,4 +62,4 @@ "Friedel Ziegelmayer ", "Yusef Napora " ] -} \ No newline at end of file +} From 0dcf1a6f52407f98214e15d173bbaa9e72f528f7 Mon Sep 17 00:00:00 2001 From: David Dias Date: Sat, 22 Jul 2017 13:31:42 -0700 Subject: [PATCH 25/61] 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() From ce5fb8c1b9905cc6816049066a57f500bf4cf05a Mon Sep 17 00:00:00 2001 From: David Dias Date: Sat, 22 Jul 2017 13:32:00 -0700 Subject: [PATCH 26/61] chore: update contributors --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 467484b..7132880 100644 --- a/package.json +++ b/package.json @@ -62,4 +62,4 @@ "Friedel Ziegelmayer ", "Yusef Napora " ] -} +} \ No newline at end of file From 98b285a840896cc51a78defb90ca863695985ecf Mon Sep 17 00:00:00 2001 From: David Dias Date: Sat, 22 Jul 2017 13:32:01 -0700 Subject: [PATCH 27/61] chore: release version v0.2.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7132880..e3c7902 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "libp2p-crypto-secp256k1", - "version": "0.2.1", + "version": "0.2.2", "description": "Support for secp256k1 keys in libp2p-crypto", "main": "src/index.js", "browser": { From f2b67f7d82014882bf4a4afd9c87fbe717ad8cf0 Mon Sep 17 00:00:00 2001 From: Victor Bjelkholm Date: Fri, 22 Dec 2017 14:16:17 +0100 Subject: [PATCH 28/61] Updating CI files This commit updates all CI scripts to the latest version --- .travis.yml | 17 ++++++----------- appveyor.yml | 29 +++++++++++++++++++++++++++++ ci/Jenkinsfile | 2 ++ circle.yml | 1 + 4 files changed, 38 insertions(+), 11 deletions(-) create mode 100644 appveyor.yml create mode 100644 ci/Jenkinsfile diff --git a/.travis.yml b/.travis.yml index 5cc672b..5102ee5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,25 +1,20 @@ +# Warning: This file is automatically synced from https://github.com/ipfs/ci-sync so if you want to change it, please change it there and ask someone to sync all repositories. sudo: false language: node_js matrix: include: - - node_js: 4 - env: CXX=g++-4.8 - node_js: 6 - env: - - CXX=g++-4.8 - - node_js: stable env: CXX=g++-4.8 - -# Make sure we have new NPM. -before_install: - - npm install -g npm@4 + - node_js: 8 + env: CXX=g++-4.8 + # - node_js: stable + # env: CXX=g++-4.8 script: - npm run lint - - npm test + - npm run test - npm run coverage - - make test before_script: - export DISPLAY=:99.0 diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000..046bf91 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,29 @@ +# Warning: This file is automatically synced from https://github.com/ipfs/ci-sync so if you want to change it, please change it there and ask someone to sync all repositories. +version: "{build}" + +environment: + matrix: + - nodejs_version: "6" + - nodejs_version: "8" + +matrix: + fast_finish: true + +install: + # Install Node.js + - ps: Install-Product node $env:nodejs_version + + # Upgrade npm + - npm install -g npm + + # Output our current versions for debugging + - node --version + - npm --version + + # Install our package dependencies + - npm install + +test_script: + - npm run test:node + +build: off diff --git a/ci/Jenkinsfile b/ci/Jenkinsfile new file mode 100644 index 0000000..a7da2e5 --- /dev/null +++ b/ci/Jenkinsfile @@ -0,0 +1,2 @@ +// Warning: This file is automatically synced from https://github.com/ipfs/ci-sync so if you want to change it, please change it there and ask someone to sync all repositories. +javascript() diff --git a/circle.yml b/circle.yml index 56f7efb..0009693 100644 --- a/circle.yml +++ b/circle.yml @@ -1,3 +1,4 @@ +# Warning: This file is automatically synced from https://github.com/ipfs/ci-sync so if you want to change it, please change it there and ask someone to sync all repositories. machine: node: version: stable From 937cc767140e2be16613f4d66b873f268c5adfb0 Mon Sep 17 00:00:00 2001 From: David Dias Date: Tue, 1 May 2018 14:41:12 +0100 Subject: [PATCH 29/61] add lead maintainer --- README.md | 6 +++++- package.json | 13 ++++--------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 3670b05..e583c7d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# js-libp2p-crypto +# js-libp2p-crypto-secp256k1 [![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io) [![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/) @@ -15,6 +15,10 @@ implementation of cryptographic signature generation and verification using the [secp256k1 elliptic curve](https://en.bitcoin.it/wiki/Secp256k1) popularized by Bitcoin and other crypto currencies. +## Lead Captain + +[Friedel Ziegelmayer](https://github.com/dignifiedquire/) + ## Table of Contents - [Install](#install) diff --git a/package.json b/package.json index e3c7902..67def9d 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,7 @@ "name": "libp2p-crypto-secp256k1", "version": "0.2.2", "description": "Support for secp256k1 keys in libp2p-crypto", + "leadMaintainer": "Friedel Ziegelmayer ", "main": "src/index.js", "browser": { "secp256k1": "secp256k1/js" @@ -24,7 +25,6 @@ "crypto", "secp256k1" ], - "author": "Yusef Napora ", "license": "MIT", "dependencies": { "async": "^2.5.0", @@ -38,15 +38,10 @@ "benchmark": "^2.1.4", "libp2p-crypto": "~0.9.4", "chai": "^4.1.0", - "dirty-chai": "^2.0.1", - "pre-commit": "^1.2.2" + "dirty-chai": "^2.0.1" }, - "pre-commit": [ - "lint", - "test" - ], "engines": { - "node": ">=4.0.0", + "node": ">=6.0.0", "npm": ">=3.0.0" }, "repository": { @@ -62,4 +57,4 @@ "Friedel Ziegelmayer ", "Yusef Napora " ] -} \ No newline at end of file +} From 1602c440ad10434030aa1ffdaed168af2b9a04cd Mon Sep 17 00:00:00 2001 From: David Dias Date: Tue, 1 May 2018 14:41:37 +0100 Subject: [PATCH 30/61] chore: update deps --- package.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 67def9d..8a29d34 100644 --- a/package.json +++ b/package.json @@ -27,17 +27,17 @@ ], "license": "MIT", "dependencies": { - "async": "^2.5.0", - "multihashing-async": "~0.4.6", + "async": "^2.6.0", + "multihashing-async": "~0.4.8", "nodeify": "^1.0.1", - "safe-buffer": "^5.1.1", - "secp256k1": "^3.3.0" + "safe-buffer": "^5.1.2", + "secp256k1": "^3.5.0" }, "devDependencies": { - "aegir": "^11.0.2", + "aegir": "^13.1.0", "benchmark": "^2.1.4", - "libp2p-crypto": "~0.9.4", - "chai": "^4.1.0", + "libp2p-crypto": "~0.13.0", + "chai": "^4.1.2", "dirty-chai": "^2.0.1" }, "engines": { From cfdcbe08d4a02586dc636ff69df43f20543f3bb4 Mon Sep 17 00:00:00 2001 From: Hugo Dias Date: Mon, 17 Dec 2018 11:54:05 +0000 Subject: [PATCH 31/61] fix: update deps and repo setup --- .gitignore | 2 ++ .travis.yml | 32 -------------------------------- appveyor.yml | 29 ----------------------------- circle.yml | 15 --------------- package.json | 26 +++++++++++++------------- 5 files changed, 15 insertions(+), 89 deletions(-) delete mode 100644 .travis.yml delete mode 100644 appveyor.yml delete mode 100644 circle.yml diff --git a/.gitignore b/.gitignore index fb8d1c9..23e606c 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,5 @@ build node_modules dist +package-lock.json +yarn.lock \ No newline at end of file diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 5102ee5..0000000 --- a/.travis.yml +++ /dev/null @@ -1,32 +0,0 @@ -# Warning: This file is automatically synced from https://github.com/ipfs/ci-sync so if you want to change it, please change it there and ask someone to sync all repositories. -sudo: false -language: node_js - -matrix: - include: - - node_js: 6 - env: CXX=g++-4.8 - - node_js: 8 - env: CXX=g++-4.8 - # - node_js: stable - # env: CXX=g++-4.8 - -script: - - npm run lint - - npm run test - - npm run coverage - -before_script: - - export DISPLAY=:99.0 - - sh -e /etc/init.d/xvfb start - -after_success: - - npm run coverage-publish - -addons: - firefox: 'latest' - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - g++-4.8 diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 046bf91..0000000 --- a/appveyor.yml +++ /dev/null @@ -1,29 +0,0 @@ -# Warning: This file is automatically synced from https://github.com/ipfs/ci-sync so if you want to change it, please change it there and ask someone to sync all repositories. -version: "{build}" - -environment: - matrix: - - nodejs_version: "6" - - nodejs_version: "8" - -matrix: - fast_finish: true - -install: - # Install Node.js - - ps: Install-Product node $env:nodejs_version - - # Upgrade npm - - npm install -g npm - - # Output our current versions for debugging - - node --version - - npm --version - - # Install our package dependencies - - npm install - -test_script: - - npm run test:node - -build: off diff --git a/circle.yml b/circle.yml deleted file mode 100644 index 0009693..0000000 --- a/circle.yml +++ /dev/null @@ -1,15 +0,0 @@ -# Warning: This file is automatically synced from https://github.com/ipfs/ci-sync so if you want to change it, please change it there and ask someone to sync all repositories. -machine: - node: - version: stable - -dependencies: - pre: - - google-chrome --version - - curl -L -o google-chrome.deb https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb - - sudo dpkg -i google-chrome.deb || true - - sudo apt-get update - - sudo apt-get install -f - - sudo apt-get install --only-upgrade lsb-base - - sudo dpkg -i google-chrome.deb - - google-chrome --version diff --git a/package.json b/package.json index 8a29d34..d5359a6 100644 --- a/package.json +++ b/package.json @@ -8,16 +8,16 @@ "secp256k1": "secp256k1/js" }, "scripts": { - "lint": "aegir-lint", - "build": "aegir-build", + "lint": "aegir lint", + "build": "aegir build", "test": "npm run test:node && npm run test:browser", - "test:node": "aegir-test --env node", - "test:browser": "aegir-test --env browser", - "release": "aegir-release", - "release-minor": "aegir-release --type minor", - "release-major": "aegir-release --type major", - "coverage": "aegir-coverage", - "coverage-publish": "aegir-coverage publish" + "test:node": "aegir test --env node", + "test:browser": "aegir test --env browser", + "release": "aegir release", + "release-minor": "aegir release --type minor", + "release-major": "aegir release --type major", + "coverage": "aegir coverage", + "coverage-publish": "aegir coverage publish" }, "keywords": [ "IPFS", @@ -28,17 +28,17 @@ "license": "MIT", "dependencies": { "async": "^2.6.0", - "multihashing-async": "~0.4.8", + "multihashing-async": "~0.5.1", "nodeify": "^1.0.1", "safe-buffer": "^5.1.2", "secp256k1": "^3.5.0" }, "devDependencies": { - "aegir": "^13.1.0", + "aegir": "github:ipfs/aegir#feat/make-exp-build-test-default", "benchmark": "^2.1.4", - "libp2p-crypto": "~0.13.0", "chai": "^4.1.2", - "dirty-chai": "^2.0.1" + "dirty-chai": "^2.0.1", + "libp2p-crypto": "~0.14.1" }, "engines": { "node": ">=6.0.0", From 720246f012ef22fa90e52726191c8af0c6d9e534 Mon Sep 17 00:00:00 2001 From: Hugo Dias Date: Tue, 8 Jan 2019 14:54:21 +0000 Subject: [PATCH 32/61] chore: update deps --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d5359a6..5cfbd45 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "secp256k1": "^3.5.0" }, "devDependencies": { - "aegir": "github:ipfs/aegir#feat/make-exp-build-test-default", + "aegir": "^18.0.2", "benchmark": "^2.1.4", "chai": "^4.1.2", "dirty-chai": "^2.0.1", From 6c9928abd658af037d5145b90e8b96887cb70954 Mon Sep 17 00:00:00 2001 From: David Dias Date: Tue, 8 Jan 2019 16:43:36 +0100 Subject: [PATCH 33/61] chore: update deps --- package.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 5cfbd45..d677589 100644 --- a/package.json +++ b/package.json @@ -27,18 +27,18 @@ ], "license": "MIT", "dependencies": { - "async": "^2.6.0", + "async": "^2.6.1", "multihashing-async": "~0.5.1", "nodeify": "^1.0.1", "safe-buffer": "^5.1.2", - "secp256k1": "^3.5.0" + "secp256k1": "^3.6.1" }, "devDependencies": { - "aegir": "^18.0.2", + "aegir": "^18.0.3", "benchmark": "^2.1.4", - "chai": "^4.1.2", + "chai": "^4.2.0", "dirty-chai": "^2.0.1", - "libp2p-crypto": "~0.14.1" + "libp2p-crypto": "~0.15.0" }, "engines": { "node": ">=6.0.0", From c983edfdb9cbd9cc35d7db2b15ff500955f32475 Mon Sep 17 00:00:00 2001 From: David Dias Date: Tue, 8 Jan 2019 16:44:54 +0100 Subject: [PATCH 34/61] chore: update contributors --- package.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index d677589..eb51be3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "libp2p-crypto-secp256k1", - "version": "0.2.2", + "version": "0.2.3", "description": "Support for secp256k1 keys in libp2p-crypto", "leadMaintainer": "Friedel Ziegelmayer ", "main": "src/index.js", @@ -55,6 +55,8 @@ "contributors": [ "David Dias ", "Friedel Ziegelmayer ", + "Hugo Dias ", + "Victor Bjelkholm ", "Yusef Napora " ] } From 3a8bab9f44c218d42c88e4ff70a52b09851da673 Mon Sep 17 00:00:00 2001 From: David Dias Date: Tue, 8 Jan 2019 16:44:54 +0100 Subject: [PATCH 35/61] chore: release version v0.2.3 --- CHANGELOG.md | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..c7e9a20 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,65 @@ + +## [0.2.3](https://github.com/libp2p/js-libp2p-crypto-secp256k1/compare/v0.2.2...v0.2.3) (2019-01-08) + + +### Bug Fixes + +* update deps and repo setup ([cfdcbe0](https://github.com/libp2p/js-libp2p-crypto-secp256k1/commit/cfdcbe0)) + + + + +## [0.2.2](https://github.com/libp2p/js-libp2p-crypto-secp256k1/compare/v0.2.1...v0.2.2) (2017-07-22) + + +### Bug Fixes + +* circular circular dep -> DI ([0dcf1a6](https://github.com/libp2p/js-libp2p-crypto-secp256k1/commit/0dcf1a6)) + + + + +## [0.2.1](https://github.com/libp2p/js-libp2p-crypto-secp256k1/compare/v0.2.0...v0.2.1) (2017-07-22) + + + + +# [0.2.0](https://github.com/libp2p/js-libp2p-crypto-secp256k1/compare/v0.1.4...v0.2.0) (2017-07-22) + + +### Features + +* next libp2p-crypto ([#4](https://github.com/libp2p/js-libp2p-crypto-secp256k1/issues/4)) ([4ee48a7](https://github.com/libp2p/js-libp2p-crypto-secp256k1/commit/4ee48a7)) + + + + +## [0.1.4](https://github.com/libp2p/js-libp2p-crypto-secp256k1/compare/v0.1.3...v0.1.4) (2017-02-12) + + + + +## [0.1.3](https://github.com/libp2p/js-libp2p-crypto-secp256k1/compare/v0.1.2...v0.1.3) (2017-02-11) + + + + +## [0.1.2](https://github.com/libp2p/js-libp2p-crypto-secp256k1/compare/v0.1.1...v0.1.2) (2017-02-09) + + + + +## [0.1.1](https://github.com/libp2p/js-libp2p-crypto-secp256k1/compare/v0.1.0...v0.1.1) (2017-02-09) + + + + +# [0.1.0](https://github.com/libp2p/js-libp2p-crypto-secp256k1/compare/4c36aeb...v0.1.0) (2017-02-09) + + +### Features + +* initial implementation ([4c36aeb](https://github.com/libp2p/js-libp2p-crypto-secp256k1/commit/4c36aeb)) + + + From f4dbd62e49cc4a7ab044fc1d723888d5b2d43973 Mon Sep 17 00:00:00 2001 From: Alberto Elias Date: Wed, 20 Feb 2019 13:23:05 +0100 Subject: [PATCH 36/61] feat: add `id()` method to Secp256k1PrivateKey Feature parity with ed25519 and rsa --- package.json | 1 + src/index.js | 20 ++++++++++++++++++++ test/secp256k1.spec.js | 9 +++++++++ 3 files changed, 30 insertions(+) diff --git a/package.json b/package.json index eb51be3..43d2dea 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "license": "MIT", "dependencies": { "async": "^2.6.1", + "bs58": "^4.0.1", "multihashing-async": "~0.5.1", "nodeify": "^1.0.1", "safe-buffer": "^5.1.2", diff --git a/src/index.js b/src/index.js index c3bb1d6..727a0e3 100644 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,6 @@ 'use strict' +const bs58 = require('bs58') const multihashing = require('multihashing-async') module.exports = (keysProtobuf, randomBytes, crypto) => { @@ -73,6 +74,25 @@ module.exports = (keysProtobuf, randomBytes, crypto) => { ensure(callback) multihashing(this.bytes, 'sha2-256', callback) } + + /** + * Gets the ID of the key. + * + * The key id is the base58 encoding of the SHA-256 multihash of its public key. + * The public key is a protobuf encoding containing a type and the DER encoding + * of the PKCS SubjectPublicKeyInfo. + * + * @param {function(Error, id)} callback + * @returns {undefined} + */ + id (callback) { + this.public.hash((err, hash) => { + if (err) { + return callback(err) + } + callback(null, bs58.encode(hash)) + }) + } } function unmarshalSecp256k1PrivateKey (bytes, callback) { diff --git a/test/secp256k1.spec.js b/test/secp256k1.spec.js index 85d250d..7ce0780 100644 --- a/test/secp256k1.spec.js +++ b/test/secp256k1.spec.js @@ -85,6 +85,15 @@ describe('secp256k1 keys', () => { }) }) + it('key id', (done) => { + key.id((err, id) => { + expect(err).to.not.exist() + expect(id).to.exist() + expect(id).to.be.a('string') + done() + }) + }) + describe('key equals', () => { it('equals itself', () => { expect(key.equals(key)).to.eql(true) From 53a2b590a937380218a84969a4a0214b59f0b84e Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Wed, 20 Feb 2019 13:28:46 +0100 Subject: [PATCH 37/61] chore(dev-deps): update libp2p-crypto --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 43d2dea..8e789e9 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "benchmark": "^2.1.4", "chai": "^4.2.0", "dirty-chai": "^2.0.1", - "libp2p-crypto": "~0.15.0" + "libp2p-crypto": "~0.16.0" }, "engines": { "node": ">=6.0.0", From 5fc391c8e0e876fd2688b2fc451dd48c47da8ae7 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Wed, 20 Feb 2019 13:29:17 +0100 Subject: [PATCH 38/61] chore: update contributors --- package.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 8e789e9..3e56b5d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "libp2p-crypto-secp256k1", - "version": "0.2.3", + "version": "0.3.0", "description": "Support for secp256k1 keys in libp2p-crypto", "leadMaintainer": "Friedel Ziegelmayer ", "main": "src/index.js", @@ -54,10 +54,12 @@ }, "homepage": "https://github.com/libp2p/js-libp2p-crypto-secp256k1", "contributors": [ + "Alberto Elias ", "David Dias ", "Friedel Ziegelmayer ", "Hugo Dias ", "Victor Bjelkholm ", - "Yusef Napora " + "Yusef Napora ", + "dignifiedquire " ] } From ce22cf13f0c332fb9078c49ed4e596de95b020e7 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Wed, 20 Feb 2019 13:29:17 +0100 Subject: [PATCH 39/61] chore: release version v0.3.0 --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c7e9a20..18d8798 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ + +# [0.3.0](https://github.com/libp2p/js-libp2p-crypto-secp256k1/compare/v0.2.3...v0.3.0) (2019-02-20) + + +### Features + +* add `id()` method to Secp256k1PrivateKey ([f4dbd62](https://github.com/libp2p/js-libp2p-crypto-secp256k1/commit/f4dbd62)) + + + ## [0.2.3](https://github.com/libp2p/js-libp2p-crypto-secp256k1/compare/v0.2.2...v0.2.3) (2019-01-08) From 3f131d4a0a3b4ea8357144c2d73cc69508e306c0 Mon Sep 17 00:00:00 2001 From: Vasco Santos Date: Thu, 18 Apr 2019 18:30:06 +0100 Subject: [PATCH 40/61] chore: add discourse badge (#15) --- .travis.yml | 44 ++++++++++++++++++++++++++++++++++++++++++++ README.md | 11 +++++++---- ci/Jenkinsfile | 2 -- package.json | 16 ++++++++-------- 4 files changed, 59 insertions(+), 14 deletions(-) create mode 100644 .travis.yml delete mode 100644 ci/Jenkinsfile diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..264f189 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,44 @@ +language: node_js + +cache: npm + +stages: + - check + - test + - cov + +node_js: + - '10' + +os: + - linux + - osx + - windows + +script: npx nyc -s npm run test:node -- --bail +after_success: npx nyc report --reporter=text-lcov > coverage.lcov && npx codecov + +jobs: + include: + - stage: check + script: + - npx aegir commitlint --travis + - npx aegir dep-check + - npm run lint + + - stage: test + name: chrome + addons: + chrome: stable + script: + - npx aegir test -t browser + + - stage: test + name: firefox + addons: + firefox: latest + script: + - npx aegir test -t browser -- --browsers FirefoxHeadless + +notifications: + email: false diff --git a/README.md b/README.md index e583c7d..d3cafac 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,12 @@ # js-libp2p-crypto-secp256k1 -[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io) -[![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/) -[![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23ipfs) -[![standard-readme compliant](https://img.shields.io/badge/standard--readme-OK-green.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme) +[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://protocol.ai) +[![](https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square)](http://libp2p.io/) +[![](https://img.shields.io/badge/freenode-%23libp2p-yellow.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23libp2p) +[![Discourse posts](https://img.shields.io/discourse/https/discuss.libp2p.io/posts.svg)](https://discuss.libp2p.io) +[![](https://img.shields.io/codecov/c/github/libp2p/js-libp2p-crypto-secp256k1.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-crypto-secp256k1) +[![](https://img.shields.io/travis/libp2p/js-libp2p-crypto-secp256k1.svg?style=flat-square)](https://travis-ci.com/libp2p/js-libp2p-crypto-secp256k1) +[![Dependency Status](https://david-dm.org/libp2p/js-libp2p-crypto-secp256k1.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-crypto-secp256k1) [![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square)](https://github.com/feross/standard) ![](https://img.shields.io/badge/npm-%3E%3D3.0.0-orange.svg?style=flat-square) ![](https://img.shields.io/badge/Node.js-%3E%3D4.0.0-orange.svg?style=flat-square) diff --git a/ci/Jenkinsfile b/ci/Jenkinsfile deleted file mode 100644 index a7da2e5..0000000 --- a/ci/Jenkinsfile +++ /dev/null @@ -1,2 +0,0 @@ -// Warning: This file is automatically synced from https://github.com/ipfs/ci-sync so if you want to change it, please change it there and ask someone to sync all repositories. -javascript() diff --git a/package.json b/package.json index 3e56b5d..f1823fb 100644 --- a/package.json +++ b/package.json @@ -10,9 +10,9 @@ "scripts": { "lint": "aegir lint", "build": "aegir build", - "test": "npm run test:node && npm run test:browser", - "test:node": "aegir test --env node", - "test:browser": "aegir test --env browser", + "test": "aegir test -t node -t browser", + "test:node": "aegir test -t node", + "test:browser": "aegir test -t browser", "release": "aegir release", "release-minor": "aegir release --type minor", "release-major": "aegir release --type major", @@ -27,19 +27,19 @@ ], "license": "MIT", "dependencies": { - "async": "^2.6.1", + "async": "^2.6.2", "bs58": "^4.0.1", - "multihashing-async": "~0.5.1", + "multihashing-async": "~0.6.0", "nodeify": "^1.0.1", "safe-buffer": "^5.1.2", - "secp256k1": "^3.6.1" + "secp256k1": "^3.6.2" }, "devDependencies": { - "aegir": "^18.0.3", + "aegir": "^18.2.2", "benchmark": "^2.1.4", "chai": "^4.2.0", "dirty-chai": "^2.0.1", - "libp2p-crypto": "~0.16.0" + "libp2p-crypto": "~0.16.1" }, "engines": { "node": ">=6.0.0", From 3bb84514d19e649afc89d103a1af83f52e8f63c7 Mon Sep 17 00:00:00 2001 From: Arve Knudsen Date: Wed, 10 Jul 2019 10:56:11 +0200 Subject: [PATCH 41/61] fix(unmarshal): provide only one arg to callback (#17) --- src/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.js b/src/index.js index 727a0e3..5d23e01 100644 --- a/src/index.js +++ b/src/index.js @@ -96,7 +96,7 @@ module.exports = (keysProtobuf, randomBytes, crypto) => { } function unmarshalSecp256k1PrivateKey (bytes, callback) { - callback(null, new Secp256k1PrivateKey(bytes), null) + callback(null, new Secp256k1PrivateKey(bytes)) } function unmarshalSecp256k1PublicKey (bytes) { From 9f01868c6c3e58255ab93113214e633799cc529e Mon Sep 17 00:00:00 2001 From: Jacob Heun Date: Wed, 10 Jul 2019 10:05:53 +0100 Subject: [PATCH 42/61] chore: update contributors --- package.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index f1823fb..09dcc60 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "libp2p-crypto-secp256k1", - "version": "0.3.0", + "version": "0.3.1", "description": "Support for secp256k1 keys in libp2p-crypto", "leadMaintainer": "Friedel Ziegelmayer ", "main": "src/index.js", @@ -55,9 +55,11 @@ "homepage": "https://github.com/libp2p/js-libp2p-crypto-secp256k1", "contributors": [ "Alberto Elias ", + "Arve Knudsen ", "David Dias ", "Friedel Ziegelmayer ", "Hugo Dias ", + "Vasco Santos ", "Victor Bjelkholm ", "Yusef Napora ", "dignifiedquire " From fbd42385e33f149eff41cfa16fa1e849de53c85b Mon Sep 17 00:00:00 2001 From: Jacob Heun Date: Wed, 10 Jul 2019 10:05:54 +0100 Subject: [PATCH 43/61] chore: release version v0.3.1 --- CHANGELOG.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 18d8798..f9d82b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,19 @@ + +## [0.3.1](https://github.com/libp2p/js-libp2p-crypto-secp256k1/compare/v0.2.2...v0.3.1) (2019-07-10) + + +### Bug Fixes + +* update deps and repo setup ([cfdcbe0](https://github.com/libp2p/js-libp2p-crypto-secp256k1/commit/cfdcbe0)) +* **unmarshal:** provide only one arg to callback ([#17](https://github.com/libp2p/js-libp2p-crypto-secp256k1/issues/17)) ([3bb8451](https://github.com/libp2p/js-libp2p-crypto-secp256k1/commit/3bb8451)) + + +### Features + +* add `id()` method to Secp256k1PrivateKey ([f4dbd62](https://github.com/libp2p/js-libp2p-crypto-secp256k1/commit/f4dbd62)) + + + # [0.3.0](https://github.com/libp2p/js-libp2p-crypto-secp256k1/compare/v0.2.3...v0.3.0) (2019-02-20) From 1974eb92be8d8e439286b564bc871006bbce5b02 Mon Sep 17 00:00:00 2001 From: Alex Potsides Date: Wed, 10 Jul 2019 11:35:58 +0100 Subject: [PATCH 44/61] feat: use async await (#18) BREAKING CHANGE: Callback support has been dropped in favor of async/await. * feat: use async/await This PR changes this module to remove callbacks and use async/await. The API is unchanged aside from the obvious removal of the `callback` parameter. refs https://github.com/ipfs/js-ipfs/issues/1670 * fix: use latest multihashing-async as it is all promises now --- .gitignore | 3 +- .travis.yml | 3 +- README.md | 60 ++++---- package.json | 5 +- src/crypto.js | 57 +++----- src/index.js | 61 +++----- test/secp256k1.spec.js | 309 ++++++++++++++++------------------------- 7 files changed, 191 insertions(+), 307 deletions(-) diff --git a/.gitignore b/.gitignore index 23e606c..452c69d 100644 --- a/.gitignore +++ b/.gitignore @@ -33,4 +33,5 @@ node_modules dist package-lock.json -yarn.lock \ No newline at end of file +yarn.lock +.vscode diff --git a/.travis.yml b/.travis.yml index 264f189..11e06a6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,4 @@ + language: node_js cache: npm @@ -41,4 +42,4 @@ jobs: - npx aegir test -t browser -- --browsers FirefoxHeadless notifications: - email: false + email: false \ No newline at end of file diff --git a/README.md b/README.md index d3cafac..7063200 100644 --- a/README.md +++ b/README.md @@ -13,9 +13,9 @@ > Support for secp256k1 keys in js-libp2p-crypto -This repo contains a [js-libp2p-crypto](https://github.com/libp2p/js-libp2p-crypto)-compatible -implementation of cryptographic signature generation and verification using the -[secp256k1 elliptic curve](https://en.bitcoin.it/wiki/Secp256k1) popularized by Bitcoin and other +This repo contains a [js-libp2p-crypto](https://github.com/libp2p/js-libp2p-crypto)-compatible +implementation of cryptographic signature generation and verification using the +[secp256k1 elliptic curve](https://en.bitcoin.it/wiki/Secp256k1) popularized by Bitcoin and other crypto currencies. ## Lead Captain @@ -28,14 +28,14 @@ crypto currencies. - [Usage](#usage) - [Example](#example) - [API](#api) - - [`generateKeyPair([bits,] callback)`](#generatekeypairbits-callback) + - [`generateKeyPair([bits])`](#generatekeypairbits) - [`unmarshalSecp256k1PublicKey(bytes)`](#unmarshalsecp256k1publickeybytes) - - [`unmarshalSecp256k1PrivateKey(bytes, callback)`](#unmarshalsecp256k1privatekeybytes-callback) + - [`unmarshalSecp256k1PrivateKey(bytes)`](#unmarshalsecp256k1privatekeybytes) - [`Secp256k1PublicKey`](#secp256k1publickey) - - [`.verify(data, sig, callback)`](#verifydata-sig-callback) + - [`.verify(data, sig)`](#verifydata-sig) - [`Secp256k1PrivateKey`](#secp256k1privatekey) - [`.public`](#public) - - [`.sign(data, callback)`](#signdata-callback) + - [`.sign(data)`](#signdata) - [Contribute](#contribute) - [License](#license) @@ -57,48 +57,48 @@ instances of the `Secp256k1PublicKey` or `Secp256k1PrivateKey` classes provided ```js const crypto = require('libp2p-crypto') - const msg = Buffer.from('Hello World') -crypto.generateKeyPair('secp256k1', 256, (err, key) => { - // assuming no error, key will be an instance of Secp256k1PrivateKey - // the public key is available as key.public - key.sign(msg, (err, sig) => { - key.public.verify(msg, sig, (err, valid) => { - assert(valid, 'Something went horribly wrong') - }) - }) -}) +const key = await crypto.generateKeyPair('secp256k1', 256) +// assuming no error, key will be an instance of Secp256k1PrivateKey +// the public key is available as key.public +const sig = await key.sign(msg) + +const valid = await key.public.verify(msg, sig) +assert(valid, 'Something went horribly wrong') ``` ## API The functions below are the public API of this module. -For usage within libp2p-crypto, see the [libp2p-crypto API documentation](https://github.com/libp2p/js-libp2p-crypto#api). +For usage within `libp2p-crypto`, see the [`libp2p-crypto` API documentation](https://github.com/libp2p/js-libp2p-crypto#api). -### `generateKeyPair([bits, ] callback)` +### `generateKeyPair([bits])` - `bits: Number` - Optional, included for compatibility with js-libp2p-crypto. Ignored if present; private keys will always be 256 bits. -- `callback: Function` + +Returns `Promise` ### `unmarshalSecp256k1PublicKey(bytes)` - `bytes: Buffer` Converts a serialized secp256k1 public key into an instance of `Secp256k1PublicKey` and returns it -### `unmarshalSecp256k1PrivateKey(bytes, callback)` +### `unmarshalSecp256k1PrivateKey(bytes)` - `bytes: Buffer` -- `callback: Function` -Converts a serialized secp256k1 private key into an instance of `Secp256k1PrivateKey`, passing it to `callback` on success +Returns `Promise` + +Converts a serialized secp256k1 private key into an instance of `Secp256k1PrivateKey`. ### `Secp256k1PublicKey` -#### `.verify(data, sig, callback)` +#### `.verify(data, sig)` - `data: Buffer` - `sig: Buffer` -- `callback: Function` -Calculates the SHA-256 hash of `data`, and verifies the DER-encoded signature in `sig`, passing the result to `callback` +Returns `Promise` + +Calculates the SHA-256 hash of `data`, and verifies the DER-encoded signature in `sig`. ### `Secp256k1PrivateKey` @@ -106,14 +106,16 @@ Calculates the SHA-256 hash of `data`, and verifies the DER-encoded signature in Accessor for the `Secp256k1PublicKey` associated with this private key. -#### `.sign(data, callback)` +#### `.sign(data)` - `data: Buffer` -Calculates the SHA-256 hash of `data` and signs it, passing the DER-encoded signature to `callback` +Returns `Promise` + +Calculates the SHA-256 hash of `data` and signs it, resolves with the DER-encoded signature. ## Contribute -Feel free to join in. All welcome. Open an [issue](https://github.com/libp2p/js-libp2p-crypto/issues)! +Feel free to join in. All welcome. Open an [issue](https://github.com/libp2p/js-libp2p-crypto-secp256k1/issues)! This repository falls under the IPFS [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md). diff --git a/package.json b/package.json index 09dcc60..3af690d 100644 --- a/package.json +++ b/package.json @@ -27,15 +27,14 @@ ], "license": "MIT", "dependencies": { - "async": "^2.6.2", "bs58": "^4.0.1", - "multihashing-async": "~0.6.0", + "multihashing-async": "^0.7.0", "nodeify": "^1.0.1", "safe-buffer": "^5.1.2", "secp256k1": "^3.6.2" }, "devDependencies": { - "aegir": "^18.2.2", + "aegir": "^19.0.5", "benchmark": "^2.1.4", "chai": "^4.2.0", "dirty-chai": "^2.0.1", diff --git a/src/crypto.js b/src/crypto.js index a336ab4..fbafb91 100644 --- a/src/crypto.js +++ b/src/crypto.js @@ -2,49 +2,30 @@ 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)) - + function generateKey () { let privateKey do { privateKey = randomBytes(32) } while (!secp256k1.privateKeyVerify(privateKey)) - - done(null, privateKey) + return 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) } - }) + async function hashAndSign (key, msg) { + const digest = await multihashing.digest(msg, HASH_ALGORITHM) + const sig = secp256k1.sign(digest, key) + return secp256k1.signatureExport(sig.signature) } - 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) } - }) + async function hashAndVerify (key, sig, msg) { + const digest = await multihashing.digest(msg, HASH_ALGORITHM) + sig = secp256k1.signatureImport(sig) + return secp256k1.verify(digest, sig, key) } function compressPublicKey (key) { @@ -76,14 +57,14 @@ module.exports = (randomBytes) => { } return { - generateKey: generateKey, - privateKeyLength: privateKeyLength, - hashAndSign: hashAndSign, - hashAndVerify: hashAndVerify, - compressPublicKey: compressPublicKey, - decompressPublicKey: decompressPublicKey, - validatePrivateKey: validatePrivateKey, - validatePublicKey: validatePublicKey, - computePublicKey: computePublicKey + generateKey, + privateKeyLength, + hashAndSign, + hashAndVerify, + compressPublicKey, + decompressPublicKey, + validatePrivateKey, + validatePublicKey, + computePublicKey } } diff --git a/src/index.js b/src/index.js index 5d23e01..a1299b2 100644 --- a/src/index.js +++ b/src/index.js @@ -12,9 +12,8 @@ module.exports = (keysProtobuf, randomBytes, crypto) => { this._key = key } - verify (data, sig, callback) { - ensure(callback) - crypto.hashAndVerify(this._key, sig, data, callback) + verify (data, sig) { + return crypto.hashAndVerify(this._key, sig, data) } marshal () { @@ -32,9 +31,8 @@ module.exports = (keysProtobuf, randomBytes, crypto) => { return this.bytes.equals(key.bytes) } - hash (callback) { - ensure(callback) - multihashing(this.bytes, 'sha2-256', callback) + hash () { + return multihashing(this.bytes, 'sha2-256') } } @@ -46,9 +44,8 @@ module.exports = (keysProtobuf, randomBytes, crypto) => { crypto.validatePublicKey(this._publicKey) } - sign (message, callback) { - ensure(callback) - crypto.hashAndSign(this._key, message, callback) + sign (message) { + return crypto.hashAndSign(this._key, message) } get public () { @@ -70,9 +67,8 @@ module.exports = (keysProtobuf, randomBytes, crypto) => { return this.bytes.equals(key.bytes) } - hash (callback) { - ensure(callback) - multihashing(this.bytes, 'sha2-256', callback) + hash () { + return multihashing(this.bytes, 'sha2-256') } /** @@ -85,47 +81,24 @@ module.exports = (keysProtobuf, randomBytes, crypto) => { * @param {function(Error, id)} callback * @returns {undefined} */ - id (callback) { - this.public.hash((err, hash) => { - if (err) { - return callback(err) - } - callback(null, bs58.encode(hash)) - }) + async id () { + const hash = await this.public.hash() + + return bs58.encode(hash) } } - function unmarshalSecp256k1PrivateKey (bytes, callback) { - callback(null, new Secp256k1PrivateKey(bytes)) + function unmarshalSecp256k1PrivateKey (bytes) { + return new Secp256k1PrivateKey(bytes) } function unmarshalSecp256k1PublicKey (bytes) { return new Secp256k1PublicKey(bytes) } - function generateKeyPair (_bits, callback) { - if (callback === undefined && typeof _bits === 'function') { - callback = _bits - } - - ensure(callback) - - crypto.generateKey((err, privateKeyBytes) => { - if (err) { return callback(err) } - - let privkey - try { - privkey = new Secp256k1PrivateKey(privateKeyBytes) - } catch (err) { return callback(err) } - - callback(null, privkey) - }) - } - - function ensure (callback) { - if (typeof callback !== 'function') { - throw new Error('callback is required') - } + async function generateKeyPair () { + const privateKeyBytes = await crypto.generateKey() + return new Secp256k1PrivateKey(privateKeyBytes) } return { diff --git a/test/secp256k1.spec.js b/test/secp256k1.spec.js index 7ce0780..c039216 100644 --- a/test/secp256k1.spec.js +++ b/test/secp256k1.spec.js @@ -6,8 +6,6 @@ const dirtyChai = require('dirty-chai') const expect = chai.expect chai.use(dirtyChai) -const Buffer = require('safe-buffer').Buffer - const libp2pCrypto = require('libp2p-crypto') const keysPBM = libp2pCrypto.keys.keysPBM const randomBytes = libp2pCrypto.randomBytes @@ -17,81 +15,52 @@ describe('secp256k1 keys', () => { let key const secp256k1 = require('../src')(keysPBM, randomBytes) - before((done) => { - secp256k1.generateKeyPair((err, _key) => { - expect(err).to.not.exist() - key = _key - done() - }) + before(async () => { + key = await secp256k1.generateKeyPair() }) - it('generates a valid key', (done) => { + it('generates a valid key', async () => { expect(key).to.be.an.instanceof(secp256k1.Secp256k1PrivateKey) expect(key.public).to.be.an.instanceof(secp256k1.Secp256k1PublicKey) - key.hash((err, digest) => { - expect(err).to.not.exist() - expect(digest).to.have.length(34) + const digest = await key.hash() + expect(digest).to.have.length(34) - key.public.hash((err, digest) => { - expect(err).to.not.exist() - expect(digest).to.have.length(34) - done() - }) - }) + const publicDigest = await key.public.hash() + expect(publicDigest).to.have.length(34) }) - it('optionally accepts a `bits` argument when generating a key', (done) => { - secp256k1.generateKeyPair(256, (err, _key) => { - expect(err).to.not.exist() - expect(_key).to.be.an.instanceof(secp256k1.Secp256k1PrivateKey) - done() - }) + it('optionally accepts a `bits` argument when generating a key', async () => { + const _key = await secp256k1.generateKeyPair(256) + expect(_key).to.be.an.instanceof(secp256k1.Secp256k1PrivateKey) }) - it('requires a callback to generate a key', () => { - expect(() => secp256k1.generateKeyPair()).to.throw() - }) - - it('signs', (done) => { + it('signs', async () => { const text = randomBytes(512) - - key.sign(text, (err, sig) => { - expect(err).to.not.exist() - - key.public.verify(text, sig, (err, res) => { - expect(err).to.not.exist() - expect(res).to.equal(true) - done() - }) - }) + const sig = await key.sign(text) + const res = await key.public.verify(text, sig) + expect(res).to.equal(true) }) - it('encoding', (done) => { + it('encoding', async () => { const keyMarshal = key.marshal() - secp256k1.unmarshalSecp256k1PrivateKey(keyMarshal, (err, key2) => { - expect(err).to.not.exist() - const keyMarshal2 = key2.marshal() + const key2 = await secp256k1.unmarshalSecp256k1PrivateKey(keyMarshal) + const keyMarshal2 = key2.marshal() - expect(keyMarshal).to.eql(keyMarshal2) + expect(keyMarshal).to.eql(keyMarshal2) - const pk = key.public - const pkMarshal = pk.marshal() - const pk2 = secp256k1.unmarshalSecp256k1PublicKey(pkMarshal) - const pkMarshal2 = pk2.marshal() + const pk = key.public + const pkMarshal = pk.marshal() + const pk2 = secp256k1.unmarshalSecp256k1PublicKey(pkMarshal) + const pkMarshal2 = pk2.marshal() - expect(pkMarshal).to.eql(pkMarshal2) - done() - }) + expect(pkMarshal).to.eql(pkMarshal2) }) - it('key id', (done) => { - key.id((err, id) => { - expect(err).to.not.exist() - expect(id).to.exist() - expect(id).to.be.a('string') - done() - }) + it('key id', async () => { + const id = await key.id() + expect(id).to.exist() + expect(id).to.be.a('string') }) describe('key equals', () => { @@ -101,43 +70,27 @@ describe('secp256k1 keys', () => { expect(key.public.equals(key.public)).to.eql(true) }) - it('not equals other key', (done) => { - secp256k1.generateKeyPair(256, (err, key2) => { - expect(err).to.not.exist() - - expect(key.equals(key2)).to.eql(false) - expect(key2.equals(key)).to.eql(false) - expect(key.public.equals(key2.public)).to.eql(false) - expect(key2.public.equals(key.public)).to.eql(false) - done() - }) + it('not equals other key', async () => { + const key2 = await secp256k1.generateKeyPair(256) + expect(key.equals(key2)).to.eql(false) + expect(key2.equals(key)).to.eql(false) + expect(key.public.equals(key2.public)).to.eql(false) + expect(key2.public.equals(key.public)).to.eql(false) }) }) - it('sign and verify', (done) => { + it('sign and verify', async () => { const data = Buffer.from('hello world') - key.sign(data, (err, sig) => { - expect(err).to.not.exist() - - key.public.verify(data, sig, (err, valid) => { - expect(err).to.not.exist() - expect(valid).to.eql(true) - done() - }) - }) + const sig = await key.sign(data) + const valid = await key.public.verify(data, sig) + expect(valid).to.eql(true) }) - it('fails to verify for different data', (done) => { + it('fails to verify for different data', async () => { const data = Buffer.from('hello world') - key.sign(data, (err, sig) => { - expect(err).to.not.exist() - - key.public.verify(Buffer.from('hello'), sig, (err, valid) => { - expect(err).to.not.exist() - expect(valid).to.eql(false) - done() - }) - }) + const sig = await key.sign(data) + const valid = await key.public.verify(Buffer.from('hello'), sig) + expect(valid).to.eql(false) }) }) @@ -145,25 +98,23 @@ describe('key generation error', () => { let generateKey let secp256k1 - before((done) => { + before(() => { generateKey = crypto.generateKey - crypto.generateKey = (callback) => callback(new Error('Error generating key')) + crypto.generateKey = () => { throw new Error('Error generating key') } secp256k1 = require('../src')(keysPBM, randomBytes, crypto) - - done() }) - after((done) => { + after(() => { crypto.generateKey = generateKey - done() }) - it('returns an error if key generation fails', (done) => { - secp256k1.generateKeyPair((err, key) => { - expect(err).to.exist() - expect(key).to.not.exist() - done() - }) + it('returns an error if key generation fails', async () => { + try { + await secp256k1.generateKeyPair() + } catch (err) { + return expect(err.message).to.equal('Error generating key') + } + throw new Error('Expected error to be thrown') }) }) @@ -171,25 +122,23 @@ describe('handles generation of invalid key', () => { let generateKey let secp256k1 - before((done) => { + before(() => { generateKey = crypto.generateKey - crypto.generateKey = (callback) => { callback(null, Buffer.from('not a real key')) } + crypto.generateKey = () => Buffer.from('not a real key') secp256k1 = require('../src')(keysPBM, randomBytes, crypto) - - done() }) - after((done) => { + after(() => { crypto.generateKey = generateKey - done() }) - it('returns an error if key generator returns an invalid key', (done) => { - secp256k1.generateKeyPair((err, key) => { - expect(err).to.exist() - expect(key).to.not.exist() - done() - }) + it('returns an error if key generator returns an invalid key', async () => { + try { + await secp256k1.generateKeyPair() + } catch (err) { + return expect(err.message).to.equal('Invalid private key') + } + throw new Error('Expected error to be thrown') }) }) @@ -197,101 +146,90 @@ describe('crypto functions', () => { let privKey let pubKey - before((done) => { - crypto.generateKey((err, _key) => { - expect(err).to.not.exist() - privKey = _key - pubKey = crypto.computePublicKey(privKey) - done() - }) + before(async () => { + privKey = await crypto.generateKey() + pubKey = crypto.computePublicKey(privKey) }) - it('generates valid keys', (done) => { + it('generates valid keys', () => { expect(() => { crypto.validatePrivateKey(privKey) crypto.validatePublicKey(pubKey) }).to.not.throw() - done() }) - it('does not validate an invalid key', (done) => { + it('does not validate an invalid key', () => { expect(() => crypto.validatePublicKey(Buffer.from('42'))).to.throw() expect(() => crypto.validatePrivateKey(Buffer.from('42'))).to.throw() - done() }) - it('validates a correct signature', (done) => { - crypto.hashAndSign(privKey, Buffer.from('hello'), (err, sig) => { - expect(err).to.not.exist() - crypto.hashAndVerify(pubKey, sig, Buffer.from('hello'), (err, valid) => { - expect(err).to.not.exist() - expect(valid).to.equal(true) - done() - }) - }) + it('validates a correct signature', async () => { + const sig = await crypto.hashAndSign(privKey, Buffer.from('hello')) + const valid = await crypto.hashAndVerify(pubKey, sig, Buffer.from('hello')) + expect(valid).to.equal(true) }) - it('errors if given a null buffer to sign', (done) => { - crypto.hashAndSign(privKey, null, (err, sig) => { - expect(err).to.exist() - expect(sig).to.not.exist() - done() - }) + it('errors if given a null buffer to sign', async () => { + try { + await crypto.hashAndSign(privKey, null) + } catch (err) { + return // expected + } + throw new Error('Expected error to be thrown') }) - it('errors when signing with an invalid key', (done) => { - crypto.hashAndSign(Buffer.from('42'), Buffer.from('Hello'), (err, sig) => { - expect(err).to.exist() - expect(sig).to.not.exist() - done() - }) + it('errors when signing with an invalid key', async () => { + try { + await crypto.hashAndSign(Buffer.from('42'), Buffer.from('Hello')) + } catch (err) { + return expect(err.message).to.equal('private key length is invalid') + } + throw new Error('Expected error to be thrown') }) - it('errors if given a null buffer to validate', (done) => { - crypto.hashAndSign(privKey, Buffer.from('hello'), (err, sig) => { - expect(err).to.not.exist() + it('errors if given a null buffer to validate', async () => { + const sig = await crypto.hashAndSign(privKey, Buffer.from('hello')) - crypto.hashAndVerify(privKey, sig, null, (err, valid) => { - expect(err).to.exist() - expect(valid).to.not.exist() - done() - }) - }) + try { + await crypto.hashAndVerify(privKey, sig, null) + } catch (err) { + return // expected + } + throw new Error('Expected error to be thrown') }) - it('errors when validating a message with an invalid signature', (done) => { - crypto.hashAndVerify(pubKey, Buffer.from('invalid-sig'), Buffer.from('hello'), (err, valid) => { - expect(err).to.exist() - expect(valid).to.not.exist() - done() - }) + it('errors when validating a message with an invalid signature', async () => { + try { + await crypto.hashAndVerify(pubKey, Buffer.from('invalid-sig'), Buffer.from('hello')) + } catch (err) { + return expect(err.message).to.equal('couldn\'t parse DER signature') + } + throw new Error('Expected error to be thrown') }) - it('errors when signing with an invalid key', (done) => { - crypto.hashAndSign(Buffer.from('42'), Buffer.from('Hello'), (err, sig) => { - expect(err).to.exist() - expect(sig).to.not.exist() - done() - }) + it('errors when signing with an invalid key', async () => { + try { + await crypto.hashAndSign(Buffer.from('42'), Buffer.from('Hello')) + } catch (err) { + return expect(err.message).to.equal('private key length is invalid') + } + throw new Error('Expected error to be thrown') }) - it('throws when compressing an invalid public key', (done) => { + it('throws when compressing an invalid public key', () => { expect(() => crypto.compressPublicKey(Buffer.from('42'))).to.throw() - done() }) - it('throws when decompressing an invalid public key', (done) => { + it('throws when decompressing an invalid public key', () => { expect(() => crypto.decompressPublicKey(Buffer.from('42'))).to.throw() - done() }) - it('compresses/decompresses a valid public key', (done) => { + it('compresses/decompresses a valid public key', () => { const decompressed = crypto.decompressPublicKey(pubKey) expect(decompressed).to.exist() expect(decompressed.length).to.be.eql(65) const recompressed = crypto.compressPublicKey(decompressed) expect(recompressed).to.eql(pubKey) - done() }) }) @@ -299,43 +237,32 @@ 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) => { + it('loads a private key marshaled by go-libp2p-crypto', async () => { // we need to first extract the key data from the protobuf, which is // normally handled by js-libp2p-crypto 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() - - expect(key).to.be.an.instanceof(secp256k1.Secp256k1PrivateKey) - expect(key.bytes).to.eql(fixtures.privateKey) - done() - }) + const key = await secp256k1.unmarshalSecp256k1PrivateKey(decoded.Data) + expect(key).to.be.an.instanceof(secp256k1.Secp256k1PrivateKey) + expect(key.bytes).to.eql(fixtures.privateKey) }) - it('loads a public key marshaled by go-libp2p-crypto', (done) => { + it('loads a public key marshaled by go-libp2p-crypto', () => { 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) expect(key.bytes).to.eql(fixtures.publicKey) - done() }) - it('generates the same signature as go-libp2p-crypto', (done) => { + it('generates the same signature as go-libp2p-crypto', async () => { 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() - - key.sign(fixtures.message, (err, sig) => { - expect(err).to.not.exist() - expect(sig).to.eql(fixtures.signature) - done() - }) - }) + const key = await secp256k1.unmarshalSecp256k1PrivateKey(decoded.Data) + const sig = await key.sign(fixtures.message) + expect(sig).to.eql(fixtures.signature) }) }) From ed670209adaa2268a8847752f3db211ff3f8c534 Mon Sep 17 00:00:00 2001 From: Jacob Heun Date: Wed, 10 Jul 2019 11:45:02 +0100 Subject: [PATCH 45/61] chore: update contributors --- package.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 3af690d..ca95aed 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "libp2p-crypto-secp256k1", - "version": "0.3.1", + "version": "0.4.0", "description": "Support for secp256k1 keys in libp2p-crypto", "leadMaintainer": "Friedel Ziegelmayer ", "main": "src/index.js", @@ -54,10 +54,12 @@ "homepage": "https://github.com/libp2p/js-libp2p-crypto-secp256k1", "contributors": [ "Alberto Elias ", + "Alex Potsides ", "Arve Knudsen ", "David Dias ", "Friedel Ziegelmayer ", "Hugo Dias ", + "Jacob Heun ", "Vasco Santos ", "Victor Bjelkholm ", "Yusef Napora ", From a521cd9b11a35ebd94897c8723842f20122ce2a1 Mon Sep 17 00:00:00 2001 From: Jacob Heun Date: Wed, 10 Jul 2019 11:45:02 +0100 Subject: [PATCH 46/61] chore: release version v0.4.0 --- CHANGELOG.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f9d82b8..63827c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,26 @@ + +# [0.4.0](https://github.com/libp2p/js-libp2p-crypto-secp256k1/compare/v0.3.1...v0.4.0) (2019-07-10) + + +### Features + +* use async await ([#18](https://github.com/libp2p/js-libp2p-crypto-secp256k1/issues/18)) ([1974eb9](https://github.com/libp2p/js-libp2p-crypto-secp256k1/commit/1974eb9)) + + +### BREAKING CHANGES + +* Callback support has been dropped in favor of async/await. + +* feat: use async/await + +This PR changes this module to remove callbacks and use async/await. The API is unchanged aside from the obvious removal of the `callback` parameter. + +refs https://github.com/ipfs/js-ipfs/issues/1670 + +* fix: use latest multihashing-async as it is all promises now + + + ## [0.3.1](https://github.com/libp2p/js-libp2p-crypto-secp256k1/compare/v0.2.2...v0.3.1) (2019-07-10) From e36a9f6b79dd27eb1f8285f036a755517f54bd76 Mon Sep 17 00:00:00 2001 From: Alan Shaw Date: Mon, 6 Jan 2020 17:07:09 +0000 Subject: [PATCH 47/61] chore: update multihashing-async dep (#19) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ca95aed..9ff764b 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "license": "MIT", "dependencies": { "bs58": "^4.0.1", - "multihashing-async": "^0.7.0", + "multihashing-async": "^0.8.0", "nodeify": "^1.0.1", "safe-buffer": "^5.1.2", "secp256k1": "^3.6.2" From 9894c34f9495d061e4fb34acb290fd4e9237b7b7 Mon Sep 17 00:00:00 2001 From: Jacob Heun Date: Mon, 6 Jan 2020 18:10:49 +0100 Subject: [PATCH 48/61] chore: update contributors --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 9ff764b..2ad27a4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "libp2p-crypto-secp256k1", - "version": "0.4.0", + "version": "0.4.1", "description": "Support for secp256k1 keys in libp2p-crypto", "leadMaintainer": "Friedel Ziegelmayer ", "main": "src/index.js", @@ -53,6 +53,7 @@ }, "homepage": "https://github.com/libp2p/js-libp2p-crypto-secp256k1", "contributors": [ + "Alan Shaw ", "Alberto Elias ", "Alex Potsides ", "Arve Knudsen ", From 89901f70973d8d13dc13621ab4c70f777ebface7 Mon Sep 17 00:00:00 2001 From: Jacob Heun Date: Mon, 6 Jan 2020 18:10:59 +0100 Subject: [PATCH 49/61] chore: release version v0.4.1 --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 63827c0..0f4d72b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ + +## [0.4.1](https://github.com/libp2p/js-libp2p-crypto-secp256k1/compare/v0.4.0...v0.4.1) (2020-01-06) + + + # [0.4.0](https://github.com/libp2p/js-libp2p-crypto-secp256k1/compare/v0.3.1...v0.4.0) (2019-07-10) From 3e88839c2b18ad7f0fb41e613356bfb1decc9e6b Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 4 Feb 2020 12:45:26 +0000 Subject: [PATCH 50/61] chore(deps-dev): bump libp2p-crypto from 0.16.3 to 0.17.2 Bumps [libp2p-crypto](https://github.com/libp2p/js-libp2p-crypto) from 0.16.3 to 0.17.2. - [Release notes](https://github.com/libp2p/js-libp2p-crypto/releases) - [Changelog](https://github.com/libp2p/js-libp2p-crypto/blob/master/CHANGELOG.md) - [Commits](https://github.com/libp2p/js-libp2p-crypto/compare/v0.16.3...v0.17.2) Signed-off-by: dependabot-preview[bot] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2ad27a4..3561def 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "benchmark": "^2.1.4", "chai": "^4.2.0", "dirty-chai": "^2.0.1", - "libp2p-crypto": "~0.16.1" + "libp2p-crypto": "~0.17.2" }, "engines": { "node": ">=6.0.0", From 41c9cd1691b2678093ea5deee5b3287e7f02f57d Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 5 Feb 2020 12:11:00 +0000 Subject: [PATCH 51/61] chore(deps-dev): bump aegir from 19.0.5 to 20.6.0 Bumps [aegir](https://github.com/ipfs/aegir) from 19.0.5 to 20.6.0. - [Release notes](https://github.com/ipfs/aegir/releases) - [Changelog](https://github.com/ipfs/aegir/blob/master/CHANGELOG.md) - [Commits](https://github.com/ipfs/aegir/compare/v19.0.5...v20.6.0) Signed-off-by: dependabot-preview[bot] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3561def..19d0dbe 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "secp256k1": "^3.6.2" }, "devDependencies": { - "aegir": "^19.0.5", + "aegir": "^20.6.0", "benchmark": "^2.1.4", "chai": "^4.2.0", "dirty-chai": "^2.0.1", From ae109d46f7849dd5f32d369c5ca57c8f50342a0e Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 17 Feb 2020 13:36:19 +0000 Subject: [PATCH 52/61] chore(deps-dev): bump aegir from 20.6.1 to 21.0.2 Bumps [aegir](https://github.com/ipfs/aegir) from 20.6.1 to 21.0.2. - [Release notes](https://github.com/ipfs/aegir/releases) - [Changelog](https://github.com/ipfs/aegir/blob/master/CHANGELOG.md) - [Commits](https://github.com/ipfs/aegir/compare/v20.6.1...v21.0.2) Signed-off-by: dependabot-preview[bot] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 19d0dbe..e1499b9 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "secp256k1": "^3.6.2" }, "devDependencies": { - "aegir": "^20.6.0", + "aegir": "^21.0.2", "benchmark": "^2.1.4", "chai": "^4.2.0", "dirty-chai": "^2.0.1", From 35f196ea4deffd9f0de53afff6bd8aba5e4db9f2 Mon Sep 17 00:00:00 2001 From: Hugo Dias Date: Tue, 17 Mar 2020 10:59:23 +0000 Subject: [PATCH 53/61] fix: add buffer and update deps (#25) * fix: add buffer and update deps update secp256k1 dep and fix code use multibase to encode b58 avoid un-necessary circular dependency no libp2p-crypto use only sha256 from multihashing-async * Update src/crypto.js Co-Authored-By: Jacob Heun * chore: remove commitlint from CI Co-authored-by: Jacob Heun --- .gitignore | 1 + .travis.yml | 2 +- package.json | 16 +++++++--------- src/crypto.js | 38 ++++++++++++++++++++++++++----------- src/index.js | 11 +++++------ test/fixtures/go-interop.js | 2 +- test/secp256k1.spec.js | 16 ++++++++-------- 7 files changed, 50 insertions(+), 36 deletions(-) diff --git a/.gitignore b/.gitignore index 452c69d..f99b5ca 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,7 @@ build node_modules dist +docs package-lock.json yarn.lock .vscode diff --git a/.travis.yml b/.travis.yml index 11e06a6..cb1d170 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,6 +10,7 @@ stages: node_js: - '10' + - '12' os: - linux @@ -23,7 +24,6 @@ jobs: include: - stage: check script: - - npx aegir commitlint --travis - npx aegir dep-check - npm run lint diff --git a/package.json b/package.json index e1499b9..9c90f28 100644 --- a/package.json +++ b/package.json @@ -4,9 +4,6 @@ "description": "Support for secp256k1 keys in libp2p-crypto", "leadMaintainer": "Friedel Ziegelmayer ", "main": "src/index.js", - "browser": { - "secp256k1": "secp256k1/js" - }, "scripts": { "lint": "aegir lint", "build": "aegir build", @@ -27,18 +24,19 @@ ], "license": "MIT", "dependencies": { - "bs58": "^4.0.1", - "multihashing-async": "^0.8.0", - "nodeify": "^1.0.1", - "safe-buffer": "^5.1.2", - "secp256k1": "^3.6.2" + "buffer": "^5.5.0", + "is-typedarray": "^1.0.0", + "multibase": "^0.6.0", + "multihashing-async": "^0.8.1", + "secp256k1": "^4.0.0" }, "devDependencies": { "aegir": "^21.0.2", "benchmark": "^2.1.4", "chai": "^4.2.0", "dirty-chai": "^2.0.1", - "libp2p-crypto": "~0.17.2" + "libp2p-crypto": "~0.17.2", + "protons": "^1.1.0" }, "engines": { "node": ">=6.0.0", diff --git a/src/crypto.js b/src/crypto.js index fbafb91..1db25c4 100644 --- a/src/crypto.js +++ b/src/crypto.js @@ -1,10 +1,26 @@ 'use strict' +const { Buffer } = require('buffer') +var isTypedArray = require('is-typedarray').strict const secp256k1 = require('secp256k1') -const multihashing = require('multihashing-async') - +const sha = require('multihashing-async/src/sha') const HASH_ALGORITHM = 'sha2-256' +function typedArrayTobuffer (arr) { + if (isTypedArray(arr)) { + // To avoid a copy, use the typed array's underlying ArrayBuffer to back new Buffer + var buf = Buffer.from(arr.buffer) + if (arr.byteLength !== arr.buffer.byteLength) { + // Respect the "view", i.e. byteOffset and byteLength, without doing a copy + buf = buf.slice(arr.byteOffset, arr.byteOffset + arr.byteLength) + } + return buf + } else { + // Pass through all other types to `Buffer.from` + return Buffer.from(arr) + } +} + module.exports = (randomBytes) => { const privateKeyLength = 32 @@ -17,26 +33,26 @@ module.exports = (randomBytes) => { } async function hashAndSign (key, msg) { - const digest = await multihashing.digest(msg, HASH_ALGORITHM) - const sig = secp256k1.sign(digest, key) - return secp256k1.signatureExport(sig.signature) + const digest = await sha.digest(msg, HASH_ALGORITHM) + const sig = secp256k1.ecdsaSign(digest, key) + return typedArrayTobuffer(secp256k1.signatureExport(sig.signature)) } async function hashAndVerify (key, sig, msg) { - const digest = await multihashing.digest(msg, HASH_ALGORITHM) - sig = secp256k1.signatureImport(sig) - return secp256k1.verify(digest, sig, key) + const digest = await sha.digest(msg, HASH_ALGORITHM) + sig = typedArrayTobuffer(secp256k1.signatureImport(sig)) + return secp256k1.ecdsaVerify(sig, digest, key) } function compressPublicKey (key) { if (!secp256k1.publicKeyVerify(key)) { throw new Error('Invalid public key') } - return secp256k1.publicKeyConvert(key, true) + return typedArrayTobuffer(secp256k1.publicKeyConvert(key, true)) } function decompressPublicKey (key) { - return secp256k1.publicKeyConvert(key, false) + return typedArrayTobuffer(secp256k1.publicKeyConvert(key, false)) } function validatePrivateKey (key) { @@ -53,7 +69,7 @@ module.exports = (randomBytes) => { function computePublicKey (privateKey) { validatePrivateKey(privateKey) - return secp256k1.publicKeyCreate(privateKey) + return typedArrayTobuffer(secp256k1.publicKeyCreate(privateKey)) } return { diff --git a/src/index.js b/src/index.js index a1299b2..13ebfe8 100644 --- a/src/index.js +++ b/src/index.js @@ -1,7 +1,7 @@ 'use strict' -const bs58 = require('bs58') -const multihashing = require('multihashing-async') +const multibase = require('multibase') +const sha = require('multihashing-async/src/sha') module.exports = (keysProtobuf, randomBytes, crypto) => { crypto = crypto || require('./crypto')(randomBytes) @@ -32,7 +32,7 @@ module.exports = (keysProtobuf, randomBytes, crypto) => { } hash () { - return multihashing(this.bytes, 'sha2-256') + return sha.multihashing(this.bytes, 'sha2-256') } } @@ -68,7 +68,7 @@ module.exports = (keysProtobuf, randomBytes, crypto) => { } hash () { - return multihashing(this.bytes, 'sha2-256') + return sha.multihashing(this.bytes, 'sha2-256') } /** @@ -83,8 +83,7 @@ module.exports = (keysProtobuf, randomBytes, crypto) => { */ async id () { const hash = await this.public.hash() - - return bs58.encode(hash) + return multibase.encode('base58btc', hash).toString().slice(1) } } diff --git a/test/fixtures/go-interop.js b/test/fixtures/go-interop.js index 8f18c1a..bebda0b 100644 --- a/test/fixtures/go-interop.js +++ b/test/fixtures/go-interop.js @@ -1,6 +1,6 @@ 'use strict' -const Buffer = require('safe-buffer').Buffer +const { Buffer } = require('buffer') // The keypair and signature below were generated in a gore repl session (https://github.com/motemen/gore) // using the secp256k1 fork of go-libp2p-crypto by github user @vyzo diff --git a/test/secp256k1.spec.js b/test/secp256k1.spec.js index c039216..0863ca4 100644 --- a/test/secp256k1.spec.js +++ b/test/secp256k1.spec.js @@ -1,14 +1,14 @@ /* eslint-env mocha */ 'use strict' +const { Buffer } = require('buffer') const chai = require('chai') const dirtyChai = require('dirty-chai') const expect = chai.expect chai.use(dirtyChai) - -const libp2pCrypto = require('libp2p-crypto') -const keysPBM = libp2pCrypto.keys.keysPBM -const randomBytes = libp2pCrypto.randomBytes +const protobuf = require('protons') +const keysPBM = protobuf(require('libp2p-crypto/src/keys/keys.proto')) +const randomBytes = require('libp2p-crypto/src/random-bytes') const crypto = require('../src/crypto')(randomBytes) describe('secp256k1 keys', () => { @@ -136,7 +136,7 @@ describe('handles generation of invalid key', () => { try { await secp256k1.generateKeyPair() } catch (err) { - return expect(err.message).to.equal('Invalid private key') + return expect(err.message).to.equal('Expected private key to be an Uint8Array with length 32') } throw new Error('Expected error to be thrown') }) @@ -182,7 +182,7 @@ describe('crypto functions', () => { try { await crypto.hashAndSign(Buffer.from('42'), Buffer.from('Hello')) } catch (err) { - return expect(err.message).to.equal('private key length is invalid') + return expect(err.message).to.equal('Expected private key to be an Uint8Array with length 32') } throw new Error('Expected error to be thrown') }) @@ -202,7 +202,7 @@ describe('crypto functions', () => { try { await crypto.hashAndVerify(pubKey, Buffer.from('invalid-sig'), Buffer.from('hello')) } catch (err) { - return expect(err.message).to.equal('couldn\'t parse DER signature') + return expect(err.message).to.equal('Signature could not be parsed') } throw new Error('Expected error to be thrown') }) @@ -211,7 +211,7 @@ describe('crypto functions', () => { try { await crypto.hashAndSign(Buffer.from('42'), Buffer.from('Hello')) } catch (err) { - return expect(err.message).to.equal('private key length is invalid') + return expect(err.message).to.equal('Expected private key to be an Uint8Array with length 32') } throw new Error('Expected error to be thrown') }) From fdab19b7d9af813fb625778960da1fcf12bc033c Mon Sep 17 00:00:00 2001 From: Jacob Heun Date: Tue, 17 Mar 2020 12:11:28 +0100 Subject: [PATCH 54/61] chore: update contributors --- package.json | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index 9c90f28..8c13453 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "libp2p-crypto-secp256k1", - "version": "0.4.1", + "version": "0.4.2", "description": "Support for secp256k1 keys in libp2p-crypto", "leadMaintainer": "Friedel Ziegelmayer ", "main": "src/index.js", @@ -51,17 +51,16 @@ }, "homepage": "https://github.com/libp2p/js-libp2p-crypto-secp256k1", "contributors": [ + "David Dias ", + "Jacob Heun ", + "Friedel Ziegelmayer ", + "Hugo Dias ", + "ᴠɪᴄᴛᴏʀ ʙᴊᴇʟᴋʜᴏʟᴍ ", + "Yusef Napora ", "Alan Shaw ", "Alberto Elias ", "Alex Potsides ", "Arve Knudsen ", - "David Dias ", - "Friedel Ziegelmayer ", - "Hugo Dias ", - "Jacob Heun ", - "Vasco Santos ", - "Victor Bjelkholm ", - "Yusef Napora ", - "dignifiedquire " + "Vasco Santos " ] } From 9b4231eb75be6f56c5dfbbe20bdd2b5c3412beca Mon Sep 17 00:00:00 2001 From: Jacob Heun Date: Tue, 17 Mar 2020 12:11:28 +0100 Subject: [PATCH 55/61] chore: release version v0.4.2 --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f4d72b..4152010 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ + +## [0.4.2](https://github.com/libp2p/js-libp2p-crypto-secp256k1/compare/v0.4.1...v0.4.2) (2020-03-17) + + +### Bug Fixes + +* add buffer and update deps ([#25](https://github.com/libp2p/js-libp2p-crypto-secp256k1/issues/25)) ([35f196e](https://github.com/libp2p/js-libp2p-crypto-secp256k1/commit/35f196e)) + + + ## [0.4.1](https://github.com/libp2p/js-libp2p-crypto-secp256k1/compare/v0.4.0...v0.4.1) (2020-01-06) From d73a0ca52ec329c7a6affd317022b7b65f5ea70a Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 25 Mar 2020 12:55:12 +0100 Subject: [PATCH 56/61] chore(deps): bump multibase from 0.6.1 to 0.7.0 (#26) Bumps [multibase](https://github.com/multiformats/js-multibase) from 0.6.1 to 0.7.0. - [Release notes](https://github.com/multiformats/js-multibase/releases) - [Changelog](https://github.com/multiformats/js-multibase/blob/master/CHANGELOG.md) - [Commits](https://github.com/multiformats/js-multibase/compare/v0.6.1...v0.7.0) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8c13453..21a97fe 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "dependencies": { "buffer": "^5.5.0", "is-typedarray": "^1.0.0", - "multibase": "^0.6.0", + "multibase": "^0.7.0", "multihashing-async": "^0.8.1", "secp256k1": "^4.0.0" }, From a68fc2e98d13030f43ab9965336c112fd58fb465 Mon Sep 17 00:00:00 2001 From: Jacob Heun Date: Wed, 25 Mar 2020 12:59:03 +0100 Subject: [PATCH 57/61] chore: update contributors --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 21a97fe..0a41bd6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "libp2p-crypto-secp256k1", - "version": "0.4.2", + "version": "0.4.3", "description": "Support for secp256k1 keys in libp2p-crypto", "leadMaintainer": "Friedel Ziegelmayer ", "main": "src/index.js", From 6bbf12c169140d5abbb367fa1608a4436e6f50c5 Mon Sep 17 00:00:00 2001 From: Jacob Heun Date: Wed, 25 Mar 2020 12:59:04 +0100 Subject: [PATCH 58/61] chore: release version v0.4.3 --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4152010..4dd03a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ + +## [0.4.3](https://github.com/libp2p/js-libp2p-crypto-secp256k1/compare/v0.4.2...v0.4.3) (2020-03-25) + + + ## [0.4.2](https://github.com/libp2p/js-libp2p-crypto-secp256k1/compare/v0.4.1...v0.4.2) (2020-03-17) From 42bd594068c62ab7276c34cd6d319d1b3b279a99 Mon Sep 17 00:00:00 2001 From: Cayman Date: Mon, 6 Apr 2020 11:55:35 -0500 Subject: [PATCH 59/61] chore: move files to secp256k1 directory --- .gitignore => secp256k1/.gitignore | 0 .npmignore => secp256k1/.npmignore | 0 .travis.yml => secp256k1/.travis.yml | 0 CHANGELOG.md => secp256k1/CHANGELOG.md | 0 LICENSE => secp256k1/LICENSE | 0 README.md => secp256k1/README.md | 0 package.json => secp256k1/package.json | 0 {src => secp256k1/src}/crypto.js | 0 {src => secp256k1/src}/index.js | 0 {test => secp256k1/test}/fixtures/go-interop.js | 0 {test => secp256k1/test}/secp256k1.spec.js | 0 11 files changed, 0 insertions(+), 0 deletions(-) rename .gitignore => secp256k1/.gitignore (100%) rename .npmignore => secp256k1/.npmignore (100%) rename .travis.yml => secp256k1/.travis.yml (100%) rename CHANGELOG.md => secp256k1/CHANGELOG.md (100%) rename LICENSE => secp256k1/LICENSE (100%) rename README.md => secp256k1/README.md (100%) rename package.json => secp256k1/package.json (100%) rename {src => secp256k1/src}/crypto.js (100%) rename {src => secp256k1/src}/index.js (100%) rename {test => secp256k1/test}/fixtures/go-interop.js (100%) rename {test => secp256k1/test}/secp256k1.spec.js (100%) diff --git a/.gitignore b/secp256k1/.gitignore similarity index 100% rename from .gitignore rename to secp256k1/.gitignore diff --git a/.npmignore b/secp256k1/.npmignore similarity index 100% rename from .npmignore rename to secp256k1/.npmignore diff --git a/.travis.yml b/secp256k1/.travis.yml similarity index 100% rename from .travis.yml rename to secp256k1/.travis.yml diff --git a/CHANGELOG.md b/secp256k1/CHANGELOG.md similarity index 100% rename from CHANGELOG.md rename to secp256k1/CHANGELOG.md diff --git a/LICENSE b/secp256k1/LICENSE similarity index 100% rename from LICENSE rename to secp256k1/LICENSE diff --git a/README.md b/secp256k1/README.md similarity index 100% rename from README.md rename to secp256k1/README.md diff --git a/package.json b/secp256k1/package.json similarity index 100% rename from package.json rename to secp256k1/package.json diff --git a/src/crypto.js b/secp256k1/src/crypto.js similarity index 100% rename from src/crypto.js rename to secp256k1/src/crypto.js diff --git a/src/index.js b/secp256k1/src/index.js similarity index 100% rename from src/index.js rename to secp256k1/src/index.js diff --git a/test/fixtures/go-interop.js b/secp256k1/test/fixtures/go-interop.js similarity index 100% rename from test/fixtures/go-interop.js rename to secp256k1/test/fixtures/go-interop.js diff --git a/test/secp256k1.spec.js b/secp256k1/test/secp256k1.spec.js similarity index 100% rename from test/secp256k1.spec.js rename to secp256k1/test/secp256k1.spec.js From 32726884896c154bd4ef99c6777f8d4e7cda5221 Mon Sep 17 00:00:00 2001 From: Cayman Date: Mon, 6 Apr 2020 12:46:39 -0500 Subject: [PATCH 60/61] chore: integrate libp2p-crypto-secp256k1 --- package.json | 6 +- secp256k1/.gitignore | 38 --- secp256k1/.npmignore | 34 -- secp256k1/.travis.yml | 45 --- secp256k1/CHANGELOG.md | 134 -------- secp256k1/LICENSE | 21 -- secp256k1/README.md | 126 -------- secp256k1/package.json | 66 ---- secp256k1/test/secp256k1.spec.js | 268 ---------------- src/keys/index.js | 2 +- .../index.js => src/keys/secp256k1-class.js | 2 +- .../src/crypto.js => src/keys/secp256k1.js | 0 .../fixtures/go-key-secp256k1.js | 0 test/keys/secp256k1.spec.js | 294 ++++++++++++++---- 14 files changed, 246 insertions(+), 790 deletions(-) delete mode 100644 secp256k1/.gitignore delete mode 100644 secp256k1/.npmignore delete mode 100644 secp256k1/.travis.yml delete mode 100644 secp256k1/CHANGELOG.md delete mode 100644 secp256k1/LICENSE delete mode 100644 secp256k1/README.md delete mode 100644 secp256k1/package.json delete mode 100644 secp256k1/test/secp256k1.spec.js rename secp256k1/src/index.js => src/keys/secp256k1-class.js (97%) rename secp256k1/src/crypto.js => src/keys/secp256k1.js (100%) rename secp256k1/test/fixtures/go-interop.js => test/fixtures/go-key-secp256k1.js (100%) diff --git a/package.json b/package.json index 484bed6..143070f 100644 --- a/package.json +++ b/package.json @@ -33,20 +33,22 @@ "IPFS", "libp2p", "crypto", - "rsa" + "rsa", + "secp256k1" ], "license": "MIT", "dependencies": { "buffer": "^5.5.0", "err-code": "^2.0.0", + "is-typedarray": "^1.0.0", "iso-random-stream": "^1.1.0", "keypair": "^1.0.1", - "libp2p-crypto-secp256k1": "^0.4.2", "multibase": "^0.7.0", "multihashing-async": "^0.8.1", "node-forge": "~0.9.1", "pem-jwk": "^2.0.0", "protons": "^1.0.1", + "secp256k1": "^4.0.0", "ursa-optional": "~0.10.1" }, "devDependencies": { diff --git a/secp256k1/.gitignore b/secp256k1/.gitignore deleted file mode 100644 index f99b5ca..0000000 --- a/secp256k1/.gitignore +++ /dev/null @@ -1,38 +0,0 @@ -**/node_modules/ -**/*.log -test/repo-tests* - -# Logs -logs -*.log - -coverage - -# Runtime data -pids -*.pid -*.seed - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage - -# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# node-waf configuration -.lock-wscript - -build - -# Dependency directory -# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git -node_modules - -dist -docs -package-lock.json -yarn.lock -.vscode diff --git a/secp256k1/.npmignore b/secp256k1/.npmignore deleted file mode 100644 index 59335fd..0000000 --- a/secp256k1/.npmignore +++ /dev/null @@ -1,34 +0,0 @@ -**/node_modules/ -**/*.log -test/repo-tests* - -# Logs -logs -*.log - -coverage - -# Runtime data -pids -*.pid -*.seed - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage - -# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# node-waf configuration -.lock-wscript - -build - -# Dependency directory -# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git -node_modules - -test diff --git a/secp256k1/.travis.yml b/secp256k1/.travis.yml deleted file mode 100644 index cb1d170..0000000 --- a/secp256k1/.travis.yml +++ /dev/null @@ -1,45 +0,0 @@ - -language: node_js - -cache: npm - -stages: - - check - - test - - cov - -node_js: - - '10' - - '12' - -os: - - linux - - osx - - windows - -script: npx nyc -s npm run test:node -- --bail -after_success: npx nyc report --reporter=text-lcov > coverage.lcov && npx codecov - -jobs: - include: - - stage: check - script: - - npx aegir dep-check - - npm run lint - - - stage: test - name: chrome - addons: - chrome: stable - script: - - npx aegir test -t browser - - - stage: test - name: firefox - addons: - firefox: latest - script: - - npx aegir test -t browser -- --browsers FirefoxHeadless - -notifications: - email: false \ No newline at end of file diff --git a/secp256k1/CHANGELOG.md b/secp256k1/CHANGELOG.md deleted file mode 100644 index 4dd03a4..0000000 --- a/secp256k1/CHANGELOG.md +++ /dev/null @@ -1,134 +0,0 @@ - -## [0.4.3](https://github.com/libp2p/js-libp2p-crypto-secp256k1/compare/v0.4.2...v0.4.3) (2020-03-25) - - - - -## [0.4.2](https://github.com/libp2p/js-libp2p-crypto-secp256k1/compare/v0.4.1...v0.4.2) (2020-03-17) - - -### Bug Fixes - -* add buffer and update deps ([#25](https://github.com/libp2p/js-libp2p-crypto-secp256k1/issues/25)) ([35f196e](https://github.com/libp2p/js-libp2p-crypto-secp256k1/commit/35f196e)) - - - - -## [0.4.1](https://github.com/libp2p/js-libp2p-crypto-secp256k1/compare/v0.4.0...v0.4.1) (2020-01-06) - - - - -# [0.4.0](https://github.com/libp2p/js-libp2p-crypto-secp256k1/compare/v0.3.1...v0.4.0) (2019-07-10) - - -### Features - -* use async await ([#18](https://github.com/libp2p/js-libp2p-crypto-secp256k1/issues/18)) ([1974eb9](https://github.com/libp2p/js-libp2p-crypto-secp256k1/commit/1974eb9)) - - -### BREAKING CHANGES - -* Callback support has been dropped in favor of async/await. - -* feat: use async/await - -This PR changes this module to remove callbacks and use async/await. The API is unchanged aside from the obvious removal of the `callback` parameter. - -refs https://github.com/ipfs/js-ipfs/issues/1670 - -* fix: use latest multihashing-async as it is all promises now - - - - -## [0.3.1](https://github.com/libp2p/js-libp2p-crypto-secp256k1/compare/v0.2.2...v0.3.1) (2019-07-10) - - -### Bug Fixes - -* update deps and repo setup ([cfdcbe0](https://github.com/libp2p/js-libp2p-crypto-secp256k1/commit/cfdcbe0)) -* **unmarshal:** provide only one arg to callback ([#17](https://github.com/libp2p/js-libp2p-crypto-secp256k1/issues/17)) ([3bb8451](https://github.com/libp2p/js-libp2p-crypto-secp256k1/commit/3bb8451)) - - -### Features - -* add `id()` method to Secp256k1PrivateKey ([f4dbd62](https://github.com/libp2p/js-libp2p-crypto-secp256k1/commit/f4dbd62)) - - - - -# [0.3.0](https://github.com/libp2p/js-libp2p-crypto-secp256k1/compare/v0.2.3...v0.3.0) (2019-02-20) - - -### Features - -* add `id()` method to Secp256k1PrivateKey ([f4dbd62](https://github.com/libp2p/js-libp2p-crypto-secp256k1/commit/f4dbd62)) - - - - -## [0.2.3](https://github.com/libp2p/js-libp2p-crypto-secp256k1/compare/v0.2.2...v0.2.3) (2019-01-08) - - -### Bug Fixes - -* update deps and repo setup ([cfdcbe0](https://github.com/libp2p/js-libp2p-crypto-secp256k1/commit/cfdcbe0)) - - - - -## [0.2.2](https://github.com/libp2p/js-libp2p-crypto-secp256k1/compare/v0.2.1...v0.2.2) (2017-07-22) - - -### Bug Fixes - -* circular circular dep -> DI ([0dcf1a6](https://github.com/libp2p/js-libp2p-crypto-secp256k1/commit/0dcf1a6)) - - - - -## [0.2.1](https://github.com/libp2p/js-libp2p-crypto-secp256k1/compare/v0.2.0...v0.2.1) (2017-07-22) - - - - -# [0.2.0](https://github.com/libp2p/js-libp2p-crypto-secp256k1/compare/v0.1.4...v0.2.0) (2017-07-22) - - -### Features - -* next libp2p-crypto ([#4](https://github.com/libp2p/js-libp2p-crypto-secp256k1/issues/4)) ([4ee48a7](https://github.com/libp2p/js-libp2p-crypto-secp256k1/commit/4ee48a7)) - - - - -## [0.1.4](https://github.com/libp2p/js-libp2p-crypto-secp256k1/compare/v0.1.3...v0.1.4) (2017-02-12) - - - - -## [0.1.3](https://github.com/libp2p/js-libp2p-crypto-secp256k1/compare/v0.1.2...v0.1.3) (2017-02-11) - - - - -## [0.1.2](https://github.com/libp2p/js-libp2p-crypto-secp256k1/compare/v0.1.1...v0.1.2) (2017-02-09) - - - - -## [0.1.1](https://github.com/libp2p/js-libp2p-crypto-secp256k1/compare/v0.1.0...v0.1.1) (2017-02-09) - - - - -# [0.1.0](https://github.com/libp2p/js-libp2p-crypto-secp256k1/compare/4c36aeb...v0.1.0) (2017-02-09) - - -### Features - -* initial implementation ([4c36aeb](https://github.com/libp2p/js-libp2p-crypto-secp256k1/commit/4c36aeb)) - - - diff --git a/secp256k1/LICENSE b/secp256k1/LICENSE deleted file mode 100644 index bbfffbf..0000000 --- a/secp256k1/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2017 libp2p - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/secp256k1/README.md b/secp256k1/README.md deleted file mode 100644 index 7063200..0000000 --- a/secp256k1/README.md +++ /dev/null @@ -1,126 +0,0 @@ -# js-libp2p-crypto-secp256k1 - -[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://protocol.ai) -[![](https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square)](http://libp2p.io/) -[![](https://img.shields.io/badge/freenode-%23libp2p-yellow.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23libp2p) -[![Discourse posts](https://img.shields.io/discourse/https/discuss.libp2p.io/posts.svg)](https://discuss.libp2p.io) -[![](https://img.shields.io/codecov/c/github/libp2p/js-libp2p-crypto-secp256k1.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-crypto-secp256k1) -[![](https://img.shields.io/travis/libp2p/js-libp2p-crypto-secp256k1.svg?style=flat-square)](https://travis-ci.com/libp2p/js-libp2p-crypto-secp256k1) -[![Dependency Status](https://david-dm.org/libp2p/js-libp2p-crypto-secp256k1.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-crypto-secp256k1) -[![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square)](https://github.com/feross/standard) -![](https://img.shields.io/badge/npm-%3E%3D3.0.0-orange.svg?style=flat-square) -![](https://img.shields.io/badge/Node.js-%3E%3D4.0.0-orange.svg?style=flat-square) - -> Support for secp256k1 keys in js-libp2p-crypto - -This repo contains a [js-libp2p-crypto](https://github.com/libp2p/js-libp2p-crypto)-compatible -implementation of cryptographic signature generation and verification using the -[secp256k1 elliptic curve](https://en.bitcoin.it/wiki/Secp256k1) popularized by Bitcoin and other -crypto currencies. - -## Lead Captain - -[Friedel Ziegelmayer](https://github.com/dignifiedquire/) - -## Table of Contents - -- [Install](#install) -- [Usage](#usage) - - [Example](#example) -- [API](#api) - - [`generateKeyPair([bits])`](#generatekeypairbits) - - [`unmarshalSecp256k1PublicKey(bytes)`](#unmarshalsecp256k1publickeybytes) - - [`unmarshalSecp256k1PrivateKey(bytes)`](#unmarshalsecp256k1privatekeybytes) - - [`Secp256k1PublicKey`](#secp256k1publickey) - - [`.verify(data, sig)`](#verifydata-sig) - - [`Secp256k1PrivateKey`](#secp256k1privatekey) - - [`.public`](#public) - - [`.sign(data)`](#signdata) -- [Contribute](#contribute) -- [License](#license) - -## Install - -```sh -npm install --save libp2p-crypto-secp256k1 -``` - -## Usage - -This module is designed to work with [js-libp2p-crypto](https://github.com/libp2p/js-libp2p-crypto). -Installing `libp2p-crypto-secp256k1` will automatically add support for the `'secp256k1'` key type, which -can be used as an argument to the [libp2p-crypto API functions](https://github.com/libp2p/js-libp2p-crypto#api) -`generateKeyPair`, `unmarshalPublicKey`, and `marshalPrivateKey`. The keys returned from those functions will be -instances of the `Secp256k1PublicKey` or `Secp256k1PrivateKey` classes provided by this module. - -### Example - -```js -const crypto = require('libp2p-crypto') -const msg = Buffer.from('Hello World') - -const key = await crypto.generateKeyPair('secp256k1', 256) -// assuming no error, key will be an instance of Secp256k1PrivateKey -// the public key is available as key.public -const sig = await key.sign(msg) - -const valid = await key.public.verify(msg, sig) -assert(valid, 'Something went horribly wrong') -``` - -## API - -The functions below are the public API of this module. -For usage within `libp2p-crypto`, see the [`libp2p-crypto` API documentation](https://github.com/libp2p/js-libp2p-crypto#api). - -### `generateKeyPair([bits])` -- `bits: Number` - Optional, included for compatibility with js-libp2p-crypto. Ignored if present; private keys will always be 256 bits. - -Returns `Promise` - -### `unmarshalSecp256k1PublicKey(bytes)` -- `bytes: Buffer` - -Converts a serialized secp256k1 public key into an instance of `Secp256k1PublicKey` and returns it - -### `unmarshalSecp256k1PrivateKey(bytes)` -- `bytes: Buffer` - -Returns `Promise` - -Converts a serialized secp256k1 private key into an instance of `Secp256k1PrivateKey`. - -### `Secp256k1PublicKey` - -#### `.verify(data, sig)` -- `data: Buffer` -- `sig: Buffer` - -Returns `Promise` - -Calculates the SHA-256 hash of `data`, and verifies the DER-encoded signature in `sig`. - -### `Secp256k1PrivateKey` - -#### `.public` - -Accessor for the `Secp256k1PublicKey` associated with this private key. - -#### `.sign(data)` -- `data: Buffer` - -Returns `Promise` - -Calculates the SHA-256 hash of `data` and signs it, resolves with the DER-encoded signature. - -## Contribute - -Feel free to join in. All welcome. Open an [issue](https://github.com/libp2p/js-libp2p-crypto-secp256k1/issues)! - -This repository falls under the IPFS [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md). - -[![](https://cdn.rawgit.com/jbenet/contribute-ipfs-gif/master/img/contribute.gif)](https://github.com/ipfs/community/blob/master/contributing.md) - -## License - -[MIT](LICENSE) diff --git a/secp256k1/package.json b/secp256k1/package.json deleted file mode 100644 index 0a41bd6..0000000 --- a/secp256k1/package.json +++ /dev/null @@ -1,66 +0,0 @@ -{ - "name": "libp2p-crypto-secp256k1", - "version": "0.4.3", - "description": "Support for secp256k1 keys in libp2p-crypto", - "leadMaintainer": "Friedel Ziegelmayer ", - "main": "src/index.js", - "scripts": { - "lint": "aegir lint", - "build": "aegir build", - "test": "aegir test -t node -t browser", - "test:node": "aegir test -t node", - "test:browser": "aegir test -t browser", - "release": "aegir release", - "release-minor": "aegir release --type minor", - "release-major": "aegir release --type major", - "coverage": "aegir coverage", - "coverage-publish": "aegir coverage publish" - }, - "keywords": [ - "IPFS", - "libp2p", - "crypto", - "secp256k1" - ], - "license": "MIT", - "dependencies": { - "buffer": "^5.5.0", - "is-typedarray": "^1.0.0", - "multibase": "^0.7.0", - "multihashing-async": "^0.8.1", - "secp256k1": "^4.0.0" - }, - "devDependencies": { - "aegir": "^21.0.2", - "benchmark": "^2.1.4", - "chai": "^4.2.0", - "dirty-chai": "^2.0.1", - "libp2p-crypto": "~0.17.2", - "protons": "^1.1.0" - }, - "engines": { - "node": ">=6.0.0", - "npm": ">=3.0.0" - }, - "repository": { - "type": "git", - "url": "https://github.com/libp2p/js-libp2p-crypto-secp256k1.git" - }, - "bugs": { - "url": "https://github.com/libp2p/js-libp2p-crypto-secp256k1/issues" - }, - "homepage": "https://github.com/libp2p/js-libp2p-crypto-secp256k1", - "contributors": [ - "David Dias ", - "Jacob Heun ", - "Friedel Ziegelmayer ", - "Hugo Dias ", - "ᴠɪᴄᴛᴏʀ ʙᴊᴇʟᴋʜᴏʟᴍ ", - "Yusef Napora ", - "Alan Shaw ", - "Alberto Elias ", - "Alex Potsides ", - "Arve Knudsen ", - "Vasco Santos " - ] -} diff --git a/secp256k1/test/secp256k1.spec.js b/secp256k1/test/secp256k1.spec.js deleted file mode 100644 index 0863ca4..0000000 --- a/secp256k1/test/secp256k1.spec.js +++ /dev/null @@ -1,268 +0,0 @@ -/* eslint-env mocha */ -'use strict' - -const { Buffer } = require('buffer') -const chai = require('chai') -const dirtyChai = require('dirty-chai') -const expect = chai.expect -chai.use(dirtyChai) -const protobuf = require('protons') -const keysPBM = protobuf(require('libp2p-crypto/src/keys/keys.proto')) -const randomBytes = require('libp2p-crypto/src/random-bytes') -const crypto = require('../src/crypto')(randomBytes) - -describe('secp256k1 keys', () => { - let key - const secp256k1 = require('../src')(keysPBM, randomBytes) - - before(async () => { - key = await secp256k1.generateKeyPair() - }) - - it('generates a valid key', async () => { - expect(key).to.be.an.instanceof(secp256k1.Secp256k1PrivateKey) - expect(key.public).to.be.an.instanceof(secp256k1.Secp256k1PublicKey) - - const digest = await key.hash() - expect(digest).to.have.length(34) - - const publicDigest = await key.public.hash() - expect(publicDigest).to.have.length(34) - }) - - it('optionally accepts a `bits` argument when generating a key', async () => { - const _key = await secp256k1.generateKeyPair(256) - expect(_key).to.be.an.instanceof(secp256k1.Secp256k1PrivateKey) - }) - - it('signs', async () => { - const text = randomBytes(512) - const sig = await key.sign(text) - const res = await key.public.verify(text, sig) - expect(res).to.equal(true) - }) - - it('encoding', async () => { - const keyMarshal = key.marshal() - const key2 = await secp256k1.unmarshalSecp256k1PrivateKey(keyMarshal) - const keyMarshal2 = key2.marshal() - - expect(keyMarshal).to.eql(keyMarshal2) - - const pk = key.public - const pkMarshal = pk.marshal() - const pk2 = secp256k1.unmarshalSecp256k1PublicKey(pkMarshal) - const pkMarshal2 = pk2.marshal() - - expect(pkMarshal).to.eql(pkMarshal2) - }) - - it('key id', async () => { - const id = await key.id() - expect(id).to.exist() - expect(id).to.be.a('string') - }) - - describe('key equals', () => { - it('equals itself', () => { - expect(key.equals(key)).to.eql(true) - - expect(key.public.equals(key.public)).to.eql(true) - }) - - it('not equals other key', async () => { - const key2 = await secp256k1.generateKeyPair(256) - expect(key.equals(key2)).to.eql(false) - expect(key2.equals(key)).to.eql(false) - expect(key.public.equals(key2.public)).to.eql(false) - expect(key2.public.equals(key.public)).to.eql(false) - }) - }) - - it('sign and verify', async () => { - const data = Buffer.from('hello world') - const sig = await key.sign(data) - const valid = await key.public.verify(data, sig) - expect(valid).to.eql(true) - }) - - it('fails to verify for different data', async () => { - const data = Buffer.from('hello world') - const sig = await key.sign(data) - const valid = await key.public.verify(Buffer.from('hello'), sig) - expect(valid).to.eql(false) - }) -}) - -describe('key generation error', () => { - let generateKey - let secp256k1 - - before(() => { - generateKey = crypto.generateKey - crypto.generateKey = () => { throw new Error('Error generating key') } - secp256k1 = require('../src')(keysPBM, randomBytes, crypto) - }) - - after(() => { - crypto.generateKey = generateKey - }) - - it('returns an error if key generation fails', async () => { - try { - await secp256k1.generateKeyPair() - } catch (err) { - return expect(err.message).to.equal('Error generating key') - } - throw new Error('Expected error to be thrown') - }) -}) - -describe('handles generation of invalid key', () => { - let generateKey - let secp256k1 - - before(() => { - generateKey = crypto.generateKey - crypto.generateKey = () => Buffer.from('not a real key') - secp256k1 = require('../src')(keysPBM, randomBytes, crypto) - }) - - after(() => { - crypto.generateKey = generateKey - }) - - it('returns an error if key generator returns an invalid key', async () => { - try { - await secp256k1.generateKeyPair() - } catch (err) { - return expect(err.message).to.equal('Expected private key to be an Uint8Array with length 32') - } - throw new Error('Expected error to be thrown') - }) -}) - -describe('crypto functions', () => { - let privKey - let pubKey - - before(async () => { - privKey = await crypto.generateKey() - pubKey = crypto.computePublicKey(privKey) - }) - - it('generates valid keys', () => { - expect(() => { - crypto.validatePrivateKey(privKey) - crypto.validatePublicKey(pubKey) - }).to.not.throw() - }) - - it('does not validate an invalid key', () => { - expect(() => crypto.validatePublicKey(Buffer.from('42'))).to.throw() - expect(() => crypto.validatePrivateKey(Buffer.from('42'))).to.throw() - }) - - it('validates a correct signature', async () => { - const sig = await crypto.hashAndSign(privKey, Buffer.from('hello')) - const valid = await crypto.hashAndVerify(pubKey, sig, Buffer.from('hello')) - expect(valid).to.equal(true) - }) - - it('errors if given a null buffer to sign', async () => { - try { - await crypto.hashAndSign(privKey, null) - } catch (err) { - return // expected - } - throw new Error('Expected error to be thrown') - }) - - it('errors when signing with an invalid key', async () => { - try { - await crypto.hashAndSign(Buffer.from('42'), Buffer.from('Hello')) - } catch (err) { - return expect(err.message).to.equal('Expected private key to be an Uint8Array with length 32') - } - throw new Error('Expected error to be thrown') - }) - - it('errors if given a null buffer to validate', async () => { - const sig = await crypto.hashAndSign(privKey, Buffer.from('hello')) - - try { - await crypto.hashAndVerify(privKey, sig, null) - } catch (err) { - return // expected - } - throw new Error('Expected error to be thrown') - }) - - it('errors when validating a message with an invalid signature', async () => { - try { - await crypto.hashAndVerify(pubKey, Buffer.from('invalid-sig'), Buffer.from('hello')) - } catch (err) { - return expect(err.message).to.equal('Signature could not be parsed') - } - throw new Error('Expected error to be thrown') - }) - - it('errors when signing with an invalid key', async () => { - try { - await crypto.hashAndSign(Buffer.from('42'), Buffer.from('Hello')) - } catch (err) { - return expect(err.message).to.equal('Expected private key to be an Uint8Array with length 32') - } - throw new Error('Expected error to be thrown') - }) - - it('throws when compressing an invalid public key', () => { - expect(() => crypto.compressPublicKey(Buffer.from('42'))).to.throw() - }) - - it('throws when decompressing an invalid public key', () => { - expect(() => crypto.decompressPublicKey(Buffer.from('42'))).to.throw() - }) - - it('compresses/decompresses a valid public key', () => { - const decompressed = crypto.decompressPublicKey(pubKey) - expect(decompressed).to.exist() - expect(decompressed.length).to.be.eql(65) - const recompressed = crypto.compressPublicKey(decompressed) - expect(recompressed).to.eql(pubKey) - }) -}) - -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', async () => { - // we need to first extract the key data from the protobuf, which is - // normally handled by js-libp2p-crypto - const decoded = keysPBM.PrivateKey.decode(fixtures.privateKey) - expect(decoded.Type).to.eql(keysPBM.KeyType.Secp256k1) - - const key = await secp256k1.unmarshalSecp256k1PrivateKey(decoded.Data) - expect(key).to.be.an.instanceof(secp256k1.Secp256k1PrivateKey) - expect(key.bytes).to.eql(fixtures.privateKey) - }) - - it('loads a public key marshaled by go-libp2p-crypto', () => { - 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) - expect(key.bytes).to.eql(fixtures.publicKey) - }) - - it('generates the same signature as go-libp2p-crypto', async () => { - const decoded = keysPBM.PrivateKey.decode(fixtures.privateKey) - expect(decoded.Type).to.eql(keysPBM.KeyType.Secp256k1) - - const key = await secp256k1.unmarshalSecp256k1PrivateKey(decoded.Data) - const sig = await key.sign(fixtures.message) - expect(sig).to.eql(fixtures.signature) - }) -}) diff --git a/src/keys/index.js b/src/keys/index.js index a05d8cc..ad92cd1 100644 --- a/src/keys/index.js +++ b/src/keys/index.js @@ -13,7 +13,7 @@ exports = module.exports const supportedKeys = { rsa: require('./rsa-class'), ed25519: require('./ed25519-class'), - secp256k1: require('libp2p-crypto-secp256k1')(keysPBM, require('../random-bytes')) + secp256k1: require('./secp256k1-class')(keysPBM, require('../random-bytes')) } exports.supportedKeys = supportedKeys diff --git a/secp256k1/src/index.js b/src/keys/secp256k1-class.js similarity index 97% rename from secp256k1/src/index.js rename to src/keys/secp256k1-class.js index 13ebfe8..e8c5390 100644 --- a/secp256k1/src/index.js +++ b/src/keys/secp256k1-class.js @@ -4,7 +4,7 @@ const multibase = require('multibase') const sha = require('multihashing-async/src/sha') module.exports = (keysProtobuf, randomBytes, crypto) => { - crypto = crypto || require('./crypto')(randomBytes) + crypto = crypto || require('./secp256k1')(randomBytes) class Secp256k1PublicKey { constructor (key) { diff --git a/secp256k1/src/crypto.js b/src/keys/secp256k1.js similarity index 100% rename from secp256k1/src/crypto.js rename to src/keys/secp256k1.js diff --git a/secp256k1/test/fixtures/go-interop.js b/test/fixtures/go-key-secp256k1.js similarity index 100% rename from secp256k1/test/fixtures/go-interop.js rename to test/fixtures/go-key-secp256k1.js diff --git a/test/keys/secp256k1.spec.js b/test/keys/secp256k1.spec.js index 2f17613..da245ab 100644 --- a/test/keys/secp256k1.spec.js +++ b/test/keys/secp256k1.spec.js @@ -1,81 +1,267 @@ /* eslint-env mocha */ 'use strict' +const { Buffer } = require('buffer') const chai = require('chai') const dirtyChai = require('dirty-chai') const expect = chai.expect chai.use(dirtyChai) -const sinon = require('sinon') - -const fixtures = require('../fixtures/secp256k1') const crypto = require('../../src') +const secp256k1 = crypto.keys.supportedKeys.secp256k1 +const keysPBM = crypto.keys.keysPBM +const randomBytes = crypto.randomBytes +const secp256k1Crypto = require('../../src/keys/secp256k1')(randomBytes) -describe('without libp2p-crypto-secp256k1 module present', () => { - before(() => { - const empty = null - sinon.replace(crypto.keys.supportedKeys, 'secp256k1', empty) +describe('secp256k1 keys', () => { + let key + + before(async () => { + key = await secp256k1.generateKeyPair() }) - after(() => { - sinon.restore() - }) - - it('fails to generate a secp256k1 key', async () => { - try { - await crypto.keys.generateKeyPair('secp256k1', 256) - } catch (err) { - return // expected - } - throw new Error('Expected error to be thrown') - }) - - it('fails to unmarshal a secp256k1 private key', async () => { - try { - await crypto.keys.unmarshalPrivateKey(fixtures.pbmPrivateKey) - } catch (err) { - return // expected - } - throw new Error('Expected error to be thrown') - }) - - it('fails to unmarshal a secp256k1 public key', () => { - expect(() => { - crypto.keys.unmarshalPublicKey(fixtures.pbmPublicKey) - }).to.throw(Error) - }) -}) - -describe('with libp2p-crypto-secp256k1 module present', () => { it('generates a valid key', async () => { - const key = await crypto.keys.generateKeyPair('secp256k1', 256) - expect(key).to.exist() + expect(key).to.be.an.instanceof(secp256k1.Secp256k1PrivateKey) + expect(key.public).to.be.an.instanceof(secp256k1.Secp256k1PublicKey) + + const digest = await key.hash() + expect(digest).to.have.length(34) + + const publicDigest = await key.public.hash() + expect(publicDigest).to.have.length(34) }) - it('protobuf encoding', async () => { - const key = await crypto.keys.generateKeyPair('secp256k1', 256) - expect(key).to.exist() + it('optionally accepts a `bits` argument when generating a key', async () => { + const _key = await secp256k1.generateKeyPair(256) + expect(_key).to.be.an.instanceof(secp256k1.Secp256k1PrivateKey) + }) - const keyMarshal = crypto.keys.marshalPrivateKey(key) - const key2 = await crypto.keys.unmarshalPrivateKey(keyMarshal) - const keyMarshal2 = crypto.keys.marshalPrivateKey(key2) + it('signs', async () => { + const text = randomBytes(512) + const sig = await key.sign(text) + const res = await key.public.verify(text, sig) + expect(res).to.equal(true) + }) + + it('encoding', async () => { + const keyMarshal = key.marshal() + const key2 = await secp256k1.unmarshalSecp256k1PrivateKey(keyMarshal) + const keyMarshal2 = key2.marshal() expect(keyMarshal).to.eql(keyMarshal2) const pk = key.public - const pkMarshal = crypto.keys.marshalPublicKey(pk) - const pk2 = crypto.keys.unmarshalPublicKey(pkMarshal) - const pkMarshal2 = crypto.keys.marshalPublicKey(pk2) + const pkMarshal = pk.marshal() + const pk2 = secp256k1.unmarshalSecp256k1PublicKey(pkMarshal) + const pkMarshal2 = pk2.marshal() expect(pkMarshal).to.eql(pkMarshal2) }) - it('unmarshals a secp256k1 private key', async () => { - const key = await crypto.keys.unmarshalPrivateKey(fixtures.pbmPrivateKey) - expect(key).to.exist() + it('key id', async () => { + const id = await key.id() + expect(id).to.exist() + expect(id).to.be.a('string') }) - it('unmarshals a secp256k1 public key', () => { - const key = crypto.keys.unmarshalPublicKey(fixtures.pbmPublicKey) - expect(key).to.exist() + describe('key equals', () => { + it('equals itself', () => { + expect(key.equals(key)).to.eql(true) + + expect(key.public.equals(key.public)).to.eql(true) + }) + + it('not equals other key', async () => { + const key2 = await secp256k1.generateKeyPair(256) + expect(key.equals(key2)).to.eql(false) + expect(key2.equals(key)).to.eql(false) + expect(key.public.equals(key2.public)).to.eql(false) + expect(key2.public.equals(key.public)).to.eql(false) + }) + }) + + it('sign and verify', async () => { + const data = Buffer.from('hello world') + const sig = await key.sign(data) + const valid = await key.public.verify(data, sig) + expect(valid).to.eql(true) + }) + + it('fails to verify for different data', async () => { + const data = Buffer.from('hello world') + const sig = await key.sign(data) + const valid = await key.public.verify(Buffer.from('hello'), sig) + expect(valid).to.eql(false) + }) +}) + +describe('key generation error', () => { + let generateKey + let secp256k1 + + before(() => { + generateKey = secp256k1Crypto.generateKey + secp256k1 = require('../../src/keys/secp256k1-class')(keysPBM, randomBytes, secp256k1Crypto) + secp256k1Crypto.generateKey = () => { throw new Error('Error generating key') } + }) + + after(() => { + secp256k1Crypto.generateKey = generateKey + }) + + it('returns an error if key generation fails', async () => { + try { + await secp256k1.generateKeyPair() + } catch (err) { + return expect(err.message).to.equal('Error generating key') + } + throw new Error('Expected error to be thrown') + }) +}) + +describe('handles generation of invalid key', () => { + let generateKey + let secp256k1 + + before(() => { + generateKey = secp256k1Crypto.generateKey + secp256k1 = require('../../src/keys/secp256k1-class')(keysPBM, randomBytes, secp256k1Crypto) + secp256k1Crypto.generateKey = () => Buffer.from('not a real key') + }) + + after(() => { + secp256k1Crypto.generateKey = generateKey + }) + + it('returns an error if key generator returns an invalid key', async () => { + try { + await secp256k1.generateKeyPair() + } catch (err) { + return expect(err.message).to.equal('Expected private key to be an Uint8Array with length 32') + } + throw new Error('Expected error to be thrown') + }) +}) + +describe('crypto functions', () => { + let privKey + let pubKey + + before(async () => { + privKey = await secp256k1Crypto.generateKey() + pubKey = secp256k1Crypto.computePublicKey(privKey) + }) + + it('generates valid keys', () => { + expect(() => { + secp256k1Crypto.validatePrivateKey(privKey) + secp256k1Crypto.validatePublicKey(pubKey) + }).to.not.throw() + }) + + it('does not validate an invalid key', () => { + expect(() => secp256k1Crypto.validatePublicKey(Buffer.from('42'))).to.throw() + expect(() => secp256k1Crypto.validatePrivateKey(Buffer.from('42'))).to.throw() + }) + + it('validates a correct signature', async () => { + const sig = await secp256k1Crypto.hashAndSign(privKey, Buffer.from('hello')) + const valid = await secp256k1Crypto.hashAndVerify(pubKey, sig, Buffer.from('hello')) + expect(valid).to.equal(true) + }) + + it('errors if given a null buffer to sign', async () => { + try { + await secp256k1Crypto.hashAndSign(privKey, null) + } catch (err) { + return // expected + } + throw new Error('Expected error to be thrown') + }) + + it('errors when signing with an invalid key', async () => { + try { + await secp256k1Crypto.hashAndSign(Buffer.from('42'), Buffer.from('Hello')) + } catch (err) { + return expect(err.message).to.equal('Expected private key to be an Uint8Array with length 32') + } + throw new Error('Expected error to be thrown') + }) + + it('errors if given a null buffer to validate', async () => { + const sig = await secp256k1Crypto.hashAndSign(privKey, Buffer.from('hello')) + + try { + await secp256k1Crypto.hashAndVerify(privKey, sig, null) + } catch (err) { + return // expected + } + throw new Error('Expected error to be thrown') + }) + + it('errors when validating a message with an invalid signature', async () => { + try { + await secp256k1Crypto.hashAndVerify(pubKey, Buffer.from('invalid-sig'), Buffer.from('hello')) + } catch (err) { + return expect(err.message).to.equal('Signature could not be parsed') + } + throw new Error('Expected error to be thrown') + }) + + it('errors when signing with an invalid key', async () => { + try { + await secp256k1Crypto.hashAndSign(Buffer.from('42'), Buffer.from('Hello')) + } catch (err) { + return expect(err.message).to.equal('Expected private key to be an Uint8Array with length 32') + } + throw new Error('Expected error to be thrown') + }) + + it('throws when compressing an invalid public key', () => { + expect(() => secp256k1Crypto.compressPublicKey(Buffer.from('42'))).to.throw() + }) + + it('throws when decompressing an invalid public key', () => { + expect(() => secp256k1Crypto.decompressPublicKey(Buffer.from('42'))).to.throw() + }) + + it('compresses/decompresses a valid public key', () => { + const decompressed = secp256k1Crypto.decompressPublicKey(pubKey) + expect(decompressed).to.exist() + expect(decompressed.length).to.be.eql(65) + const recompressed = secp256k1Crypto.compressPublicKey(decompressed) + expect(recompressed).to.eql(pubKey) + }) +}) + +describe('go interop', () => { + const fixtures = require('../fixtures/go-key-secp256k1') + + it('loads a private key marshaled by go-libp2p-crypto', async () => { + // we need to first extract the key data from the protobuf, which is + // normally handled by js-libp2p-crypto + const decoded = keysPBM.PrivateKey.decode(fixtures.privateKey) + expect(decoded.Type).to.eql(keysPBM.KeyType.Secp256k1) + + const key = await secp256k1.unmarshalSecp256k1PrivateKey(decoded.Data) + expect(key).to.be.an.instanceof(secp256k1.Secp256k1PrivateKey) + expect(key.bytes).to.eql(fixtures.privateKey) + }) + + it('loads a public key marshaled by go-libp2p-crypto', () => { + 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) + expect(key.bytes).to.eql(fixtures.publicKey) + }) + + it('generates the same signature as go-libp2p-crypto', async () => { + const decoded = keysPBM.PrivateKey.decode(fixtures.privateKey) + expect(decoded.Type).to.eql(keysPBM.KeyType.Secp256k1) + + const key = await secp256k1.unmarshalSecp256k1PrivateKey(decoded.Data) + const sig = await key.sign(fixtures.message) + expect(sig).to.eql(fixtures.signature) }) }) From 206999ce115cfaf7163d0a53f037a403dd058428 Mon Sep 17 00:00:00 2001 From: Cayman Date: Mon, 6 Apr 2020 15:44:36 -0500 Subject: [PATCH 61/61] chore: fix secp256k1 references in readme --- README.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index e76b924..9e1c50a 100644 --- a/README.md +++ b/README.md @@ -171,9 +171,7 @@ main() The [`generateKeyPair`](#generatekeypairtype-bits), [`marshalPublicKey`](#marshalpublickeykey-type), and [`marshalPrivateKey`](#marshalprivatekeykey-type) functions accept a string `type` argument. -Currently the `'RSA'` and `'ed25519'` types are supported, although ed25519 keys support only signing and verification of messages. For encryption / decryption support, RSA keys should be used. - -Installing the [libp2p-crypto-secp256k1](https://github.com/libp2p/js-libp2p-crypto-secp256k1) module adds support for the `'secp256k1'` type, which supports ECDSA signatures using the secp256k1 elliptic curve popularized by Bitcoin. This module is not installed by default, and should be explicitly depended on if your project requires secp256k1 support. +Currently the `'RSA'`, `'ed25519'`, and `secp256k1` types are supported, although ed25519 and secp256k1 keys support only signing and verification of messages. For encryption / decryption support, RSA keys should be used. ### `crypto.keys.generateKeyPair(type, bits)` @@ -232,7 +230,7 @@ Resolves to an object of the form: ### `crypto.keys.marshalPublicKey(key, [type])` -- `key: keys.rsa.RsaPublicKey | keys.ed25519.Ed25519PublicKey | require('libp2p-crypto-secp256k1').Secp256k1PublicKey` +- `key: keys.rsa.RsaPublicKey | keys.ed25519.Ed25519PublicKey | keys.secp256k1.Secp256k1PublicKey` - `type: String`, see [Supported Key Types](#supported-key-types) above. Defaults to 'rsa'. Returns `Buffer` @@ -249,7 +247,7 @@ Converts a protobuf serialized public key into its representative object. ### `crypto.keys.marshalPrivateKey(key, [type])` -- `key: keys.rsa.RsaPrivateKey | keys.ed25519.Ed25519PrivateKey | require('libp2p-crypto-secp256k1').Secp256k1PrivateKey` +- `key: keys.rsa.RsaPrivateKey | keys.ed25519.Ed25519PrivateKey | keys.secp256k1.Secp256k1PrivateKey` - `type: String`, see [Supported Key Types](#supported-key-types) above. Returns `Buffer`