diff --git a/secp256k1/.gitignore b/secp256k1/.gitignore
new file mode 100644
index 0000000..f99b5ca
--- /dev/null
+++ b/secp256k1/.gitignore
@@ -0,0 +1,38 @@
+**/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
new file mode 100644
index 0000000..59335fd
--- /dev/null
+++ b/secp256k1/.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/secp256k1/.travis.yml b/secp256k1/.travis.yml
new file mode 100644
index 0000000..cb1d170
--- /dev/null
+++ b/secp256k1/.travis.yml
@@ -0,0 +1,45 @@
+
+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
new file mode 100644
index 0000000..4dd03a4
--- /dev/null
+++ b/secp256k1/CHANGELOG.md
@@ -0,0 +1,134 @@
+
+## [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
new file mode 100644
index 0000000..bbfffbf
--- /dev/null
+++ b/secp256k1/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/secp256k1/README.md b/secp256k1/README.md
new file mode 100644
index 0000000..7063200
--- /dev/null
+++ b/secp256k1/README.md
@@ -0,0 +1,126 @@
+# js-libp2p-crypto-secp256k1
+
+[](http://protocol.ai)
+[](http://libp2p.io/)
+[](http://webchat.freenode.net/?channels=%23libp2p)
+[](https://discuss.libp2p.io)
+[](https://codecov.io/gh/libp2p/js-libp2p-crypto-secp256k1)
+[](https://travis-ci.com/libp2p/js-libp2p-crypto-secp256k1)
+[](https://david-dm.org/libp2p/js-libp2p-crypto-secp256k1)
+[](https://github.com/feross/standard)
+
+
+
+> 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://github.com/ipfs/community/blob/master/contributing.md)
+
+## License
+
+[MIT](LICENSE)
diff --git a/secp256k1/package.json b/secp256k1/package.json
new file mode 100644
index 0000000..0a41bd6
--- /dev/null
+++ b/secp256k1/package.json
@@ -0,0 +1,66 @@
+{
+ "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/src/crypto.js b/secp256k1/src/crypto.js
new file mode 100644
index 0000000..1db25c4
--- /dev/null
+++ b/secp256k1/src/crypto.js
@@ -0,0 +1,86 @@
+'use strict'
+
+const { Buffer } = require('buffer')
+var isTypedArray = require('is-typedarray').strict
+const secp256k1 = require('secp256k1')
+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
+
+ function generateKey () {
+ let privateKey
+ do {
+ privateKey = randomBytes(32)
+ } while (!secp256k1.privateKeyVerify(privateKey))
+ return privateKey
+ }
+
+ async function hashAndSign (key, msg) {
+ 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 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 typedArrayTobuffer(secp256k1.publicKeyConvert(key, true))
+ }
+
+ function decompressPublicKey (key) {
+ return typedArrayTobuffer(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 typedArrayTobuffer(secp256k1.publicKeyCreate(privateKey))
+ }
+
+ return {
+ generateKey,
+ privateKeyLength,
+ hashAndSign,
+ hashAndVerify,
+ compressPublicKey,
+ decompressPublicKey,
+ validatePrivateKey,
+ validatePublicKey,
+ computePublicKey
+ }
+}
diff --git a/secp256k1/src/index.js b/secp256k1/src/index.js
new file mode 100644
index 0000000..13ebfe8
--- /dev/null
+++ b/secp256k1/src/index.js
@@ -0,0 +1,110 @@
+'use strict'
+
+const multibase = require('multibase')
+const sha = require('multihashing-async/src/sha')
+
+module.exports = (keysProtobuf, randomBytes, crypto) => {
+ crypto = crypto || require('./crypto')(randomBytes)
+
+ class Secp256k1PublicKey {
+ constructor (key) {
+ crypto.validatePublicKey(key)
+ this._key = key
+ }
+
+ verify (data, sig) {
+ return crypto.hashAndVerify(this._key, sig, data)
+ }
+
+ 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 () {
+ return sha.multihashing(this.bytes, 'sha2-256')
+ }
+ }
+
+ class Secp256k1PrivateKey {
+ constructor (key, publicKey) {
+ this._key = key
+ this._publicKey = publicKey || crypto.computePublicKey(key)
+ crypto.validatePrivateKey(this._key)
+ crypto.validatePublicKey(this._publicKey)
+ }
+
+ sign (message) {
+ return crypto.hashAndSign(this._key, message)
+ }
+
+ 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 () {
+ return sha.multihashing(this.bytes, 'sha2-256')
+ }
+
+ /**
+ * 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}
+ */
+ async id () {
+ const hash = await this.public.hash()
+ return multibase.encode('base58btc', hash).toString().slice(1)
+ }
+ }
+
+ function unmarshalSecp256k1PrivateKey (bytes) {
+ return new Secp256k1PrivateKey(bytes)
+ }
+
+ function unmarshalSecp256k1PublicKey (bytes) {
+ return new Secp256k1PublicKey(bytes)
+ }
+
+ async function generateKeyPair () {
+ const privateKeyBytes = await crypto.generateKey()
+ return new Secp256k1PrivateKey(privateKeyBytes)
+ }
+
+ return {
+ Secp256k1PublicKey,
+ Secp256k1PrivateKey,
+ unmarshalSecp256k1PrivateKey,
+ unmarshalSecp256k1PublicKey,
+ generateKeyPair
+ }
+}
diff --git a/secp256k1/test/fixtures/go-interop.js b/secp256k1/test/fixtures/go-interop.js
new file mode 100644
index 0000000..bebda0b
--- /dev/null
+++ b/secp256k1/test/fixtures/go-interop.js
@@ -0,0 +1,31 @@
+'use strict'
+
+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
+//
+// 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/secp256k1/test/secp256k1.spec.js b/secp256k1/test/secp256k1.spec.js
new file mode 100644
index 0000000..0863ca4
--- /dev/null
+++ b/secp256k1/test/secp256k1.spec.js
@@ -0,0 +1,268 @@
+/* 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)
+ })
+})