chore: integrate libp2p-crypto-secp256k1

This commit is contained in:
Cayman 2020-04-06 12:46:39 -05:00
parent 456a365378
commit 3272688489
No known key found for this signature in database
GPG Key ID: 54B21AEC3C53E1F5
14 changed files with 246 additions and 790 deletions

View File

@ -33,20 +33,22 @@
"IPFS", "IPFS",
"libp2p", "libp2p",
"crypto", "crypto",
"rsa" "rsa",
"secp256k1"
], ],
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"buffer": "^5.5.0", "buffer": "^5.5.0",
"err-code": "^2.0.0", "err-code": "^2.0.0",
"is-typedarray": "^1.0.0",
"iso-random-stream": "^1.1.0", "iso-random-stream": "^1.1.0",
"keypair": "^1.0.1", "keypair": "^1.0.1",
"libp2p-crypto-secp256k1": "^0.4.2",
"multibase": "^0.7.0", "multibase": "^0.7.0",
"multihashing-async": "^0.8.1", "multihashing-async": "^0.8.1",
"node-forge": "~0.9.1", "node-forge": "~0.9.1",
"pem-jwk": "^2.0.0", "pem-jwk": "^2.0.0",
"protons": "^1.0.1", "protons": "^1.0.1",
"secp256k1": "^4.0.0",
"ursa-optional": "~0.10.1" "ursa-optional": "~0.10.1"
}, },
"devDependencies": { "devDependencies": {

38
secp256k1/.gitignore vendored
View File

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

View File

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

View File

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

View File

@ -1,134 +0,0 @@
<a name="0.4.3"></a>
## [0.4.3](https://github.com/libp2p/js-libp2p-crypto-secp256k1/compare/v0.4.2...v0.4.3) (2020-03-25)
<a name="0.4.2"></a>
## [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))
<a name="0.4.1"></a>
## [0.4.1](https://github.com/libp2p/js-libp2p-crypto-secp256k1/compare/v0.4.0...v0.4.1) (2020-01-06)
<a name="0.4.0"></a>
# [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
<a name="0.3.1"></a>
## [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))
<a name="0.3.0"></a>
# [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))
<a name="0.2.3"></a>
## [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))
<a name="0.2.2"></a>
## [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))
<a name="0.2.1"></a>
## [0.2.1](https://github.com/libp2p/js-libp2p-crypto-secp256k1/compare/v0.2.0...v0.2.1) (2017-07-22)
<a name="0.2.0"></a>
# [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))
<a name="0.1.4"></a>
## [0.1.4](https://github.com/libp2p/js-libp2p-crypto-secp256k1/compare/v0.1.3...v0.1.4) (2017-02-12)
<a name="0.1.3"></a>
## [0.1.3](https://github.com/libp2p/js-libp2p-crypto-secp256k1/compare/v0.1.2...v0.1.3) (2017-02-11)
<a name="0.1.2"></a>
## [0.1.2](https://github.com/libp2p/js-libp2p-crypto-secp256k1/compare/v0.1.1...v0.1.2) (2017-02-09)
<a name="0.1.1"></a>
## [0.1.1](https://github.com/libp2p/js-libp2p-crypto-secp256k1/compare/v0.1.0...v0.1.1) (2017-02-09)
<a name="0.1.0"></a>
# [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))

View File

@ -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.

View File

@ -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<Secp256k1PrivateKey>`
### `unmarshalSecp256k1PublicKey(bytes)`
- `bytes: Buffer`
Converts a serialized secp256k1 public key into an instance of `Secp256k1PublicKey` and returns it
### `unmarshalSecp256k1PrivateKey(bytes)`
- `bytes: Buffer`
Returns `Promise<Secp256k1PrivateKey>`
Converts a serialized secp256k1 private key into an instance of `Secp256k1PrivateKey`.
### `Secp256k1PublicKey`
#### `.verify(data, sig)`
- `data: Buffer`
- `sig: Buffer`
Returns `Promise<Boolean>`
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<Buffer>`
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)

View File

@ -1,66 +0,0 @@
{
"name": "libp2p-crypto-secp256k1",
"version": "0.4.3",
"description": "Support for secp256k1 keys in libp2p-crypto",
"leadMaintainer": "Friedel Ziegelmayer <dignifiedquire@gmail.com>",
"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 <daviddias.p@gmail.com>",
"Jacob Heun <jacobheun@gmail.com>",
"Friedel Ziegelmayer <dignifiedquire@gmail.com>",
"Hugo Dias <hugomrdias@gmail.com>",
"ᴠɪᴄᴛᴏʀ ʙᴊᴇʟᴋʜᴏʟᴍ <victorbjelkholm@gmail.com>",
"Yusef Napora <yusef@napora.org>",
"Alan Shaw <alan.shaw@protocol.ai>",
"Alberto Elias <hi@albertoelias.me>",
"Alex Potsides <alex@achingbrain.net>",
"Arve Knudsen <arve.knudsen@gmail.com>",
"Vasco Santos <vasco.santos@ua.pt>"
]
}

View File

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

View File

@ -13,7 +13,7 @@ exports = module.exports
const supportedKeys = { const supportedKeys = {
rsa: require('./rsa-class'), rsa: require('./rsa-class'),
ed25519: require('./ed25519-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 exports.supportedKeys = supportedKeys

View File

@ -4,7 +4,7 @@ const multibase = require('multibase')
const sha = require('multihashing-async/src/sha') const sha = require('multihashing-async/src/sha')
module.exports = (keysProtobuf, randomBytes, crypto) => { module.exports = (keysProtobuf, randomBytes, crypto) => {
crypto = crypto || require('./crypto')(randomBytes) crypto = crypto || require('./secp256k1')(randomBytes)
class Secp256k1PublicKey { class Secp256k1PublicKey {
constructor (key) { constructor (key) {

View File

@ -1,81 +1,267 @@
/* eslint-env mocha */ /* eslint-env mocha */
'use strict' 'use strict'
const { Buffer } = require('buffer')
const chai = require('chai') const chai = require('chai')
const dirtyChai = require('dirty-chai') const dirtyChai = require('dirty-chai')
const expect = chai.expect const expect = chai.expect
chai.use(dirtyChai) chai.use(dirtyChai)
const sinon = require('sinon')
const fixtures = require('../fixtures/secp256k1')
const crypto = require('../../src') 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', () => { describe('secp256k1 keys', () => {
before(() => { let key
const empty = null
sinon.replace(crypto.keys.supportedKeys, 'secp256k1', empty) 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 () => { it('generates a valid key', async () => {
const key = await crypto.keys.generateKeyPair('secp256k1', 256) expect(key).to.be.an.instanceof(secp256k1.Secp256k1PrivateKey)
expect(key).to.exist() 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 () => { it('optionally accepts a `bits` argument when generating a key', async () => {
const key = await crypto.keys.generateKeyPair('secp256k1', 256) const _key = await secp256k1.generateKeyPair(256)
expect(key).to.exist() expect(_key).to.be.an.instanceof(secp256k1.Secp256k1PrivateKey)
})
const keyMarshal = crypto.keys.marshalPrivateKey(key) it('signs', async () => {
const key2 = await crypto.keys.unmarshalPrivateKey(keyMarshal) const text = randomBytes(512)
const keyMarshal2 = crypto.keys.marshalPrivateKey(key2) 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) expect(keyMarshal).to.eql(keyMarshal2)
const pk = key.public const pk = key.public
const pkMarshal = crypto.keys.marshalPublicKey(pk) const pkMarshal = pk.marshal()
const pk2 = crypto.keys.unmarshalPublicKey(pkMarshal) const pk2 = secp256k1.unmarshalSecp256k1PublicKey(pkMarshal)
const pkMarshal2 = crypto.keys.marshalPublicKey(pk2) const pkMarshal2 = pk2.marshal()
expect(pkMarshal).to.eql(pkMarshal2) expect(pkMarshal).to.eql(pkMarshal2)
}) })
it('unmarshals a secp256k1 private key', async () => { it('key id', async () => {
const key = await crypto.keys.unmarshalPrivateKey(fixtures.pbmPrivateKey) const id = await key.id()
expect(key).to.exist() expect(id).to.exist()
expect(id).to.be.a('string')
}) })
it('unmarshals a secp256k1 public key', () => { describe('key equals', () => {
const key = crypto.keys.unmarshalPublicKey(fixtures.pbmPublicKey) it('equals itself', () => {
expect(key).to.exist() 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)
}) })
}) })