Compare commits

...

79 Commits

Author SHA1 Message Date
Jacob Heun
609297be65 chore: release version v0.17.8 2020-07-20 15:23:49 +02:00
Jacob Heun
89a297793d chore: update contributors 2020-07-20 15:23:49 +02:00
Jacob Heun
32fae9b505 chore: fix lint 2020-07-20 15:13:46 +02:00
Jacob Heun
c2dd0a535d chore(types): fix typing 2020-07-20 15:13:46 +02:00
Jacob Heun
2f18a077b4 fix: go ed25519 interop
fixes https://github.com/libp2p/js-libp2p-crypto/issues/175
2020-07-20 15:13:46 +02:00
Jacob Heun
4aa77a6b12 chore: release version v0.17.7 2020-06-09 13:00:09 +02:00
Jacob Heun
210dd27479 chore: update contributors 2020-06-09 13:00:09 +02:00
Alex Potsides
437a76fbe3 chore: update node forge version
Use `^` so it's the same as libp2p
2020-06-09 12:57:27 +02:00
dependabot-preview[bot]
75d250c876 chore(deps-dev): bump aegir from 21.10.2 to 22.0.0 (#174)
Bumps [aegir](https://github.com/ipfs/aegir) from 21.10.2 to 22.0.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/v21.10.2...v22.0.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-05-08 10:54:15 +02:00
Jacob Heun
e761427153 chore: release version v0.17.6 2020-04-07 16:15:48 +02:00
Jacob Heun
90d51ee8c7 chore: update contributors 2020-04-07 16:15:47 +02:00
Jacob Heun
ccda21fe91 Merge pull request #173 from ChainSafe/cayman/secp-migration
Integrate libp2p-crypto-secp256k1
2020-04-07 15:55:14 +02:00
Cayman
206999ce11 chore: fix secp256k1 references in readme 2020-04-06 15:44:36 -05:00
Cayman
3272688489 chore: integrate libp2p-crypto-secp256k1 2020-04-06 12:46:39 -05:00
Cayman
456a365378 Merge branch 'cayman/secp' into cayman/secp-migration 2020-04-06 11:56:27 -05:00
Cayman
42bd594068 chore: move files to secp256k1 directory 2020-04-06 11:55:35 -05:00
dependabot-preview[bot]
ab12e6f068 chore(deps-dev): bump @types/sinon from 7.5.2 to 9.0.0 (#172)
Bumps [@types/sinon](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/sinon) from 7.5.2 to 9.0.0.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/sinon)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-04-01 16:47:39 +02:00
Jacob Heun
6bbf12c169 chore: release version v0.4.3 2020-03-25 12:59:04 +01:00
Jacob Heun
a68fc2e98d chore: update contributors 2020-03-25 12:59:03 +01:00
dependabot-preview[bot]
d73a0ca52e 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] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-03-25 12:55:12 +01:00
Jacob Heun
1b0fac84a8 chore: release version v0.17.5 2020-03-24 14:27:31 +01:00
Jacob Heun
efaafa9c06 chore: update contributors 2020-03-24 14:27:30 +01:00
dependabot-preview[bot]
88b3018c9c chore(deps): bump multibase from 0.6.1 to 0.7.0 (#171)
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] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-03-24 14:11:07 +01:00
Jacob Heun
9b4231eb75 chore: release version v0.4.2 2020-03-17 12:11:28 +01:00
Jacob Heun
fdab19b7d9 chore: update contributors 2020-03-17 12:11:28 +01:00
Hugo Dias
35f196ea4d 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 <jacobheun@gmail.com>

* chore: remove commitlint from CI

Co-authored-by: Jacob Heun <jacobheun@gmail.com>
2020-03-17 11:59:23 +01:00
dependabot-preview[bot]
ae109d46f7 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] <support@dependabot.com>
2020-02-18 12:41:51 +01:00
dependabot-preview[bot]
41c9cd1691 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] <support@dependabot.com>
2020-02-05 13:18:46 +01:00
dependabot-preview[bot]
3e88839c2b 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] <support@dependabot.com>
2020-02-05 13:18:16 +01:00
Jacob Heun
89901f7097 chore: release version v0.4.1 2020-01-06 18:10:59 +01:00
Jacob Heun
9894c34f94 chore: update contributors 2020-01-06 18:10:49 +01:00
Alan Shaw
e36a9f6b79 chore: update multihashing-async dep (#19) 2020-01-06 10:07:09 -07:00
Jacob Heun
a521cd9b11 chore: release version v0.4.0 2019-07-10 11:45:02 +01:00
Jacob Heun
ed670209ad chore: update contributors 2019-07-10 11:45:02 +01:00
Alex Potsides
1974eb92be 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
2019-07-10 11:35:58 +01:00
Jacob Heun
fbd42385e3 chore: release version v0.3.1 2019-07-10 10:05:54 +01:00
Jacob Heun
9f01868c6c chore: update contributors 2019-07-10 10:05:53 +01:00
Arve Knudsen
3bb84514d1 fix(unmarshal): provide only one arg to callback (#17) 2019-07-10 09:56:11 +01:00
Vasco Santos
3f131d4a0a chore: add discourse badge (#15) 2019-04-18 19:30:06 +02:00
dignifiedquire
ce22cf13f0 chore: release version v0.3.0 2019-02-20 13:29:17 +01:00
dignifiedquire
5fc391c8e0 chore: update contributors 2019-02-20 13:29:17 +01:00
dignifiedquire
53a2b590a9 chore(dev-deps): update libp2p-crypto 2019-02-20 13:28:46 +01:00
Alberto Elias
f4dbd62e49 feat: add id() method to Secp256k1PrivateKey
Feature parity with ed25519 and rsa
2019-02-20 13:23:05 +01:00
David Dias
3a8bab9f44 chore: release version v0.2.3 2019-01-08 16:44:54 +01:00
David Dias
c983edfdb9 chore: update contributors 2019-01-08 16:44:54 +01:00
David Dias
6c9928abd6 chore: update deps 2019-01-08 16:43:36 +01:00
Hugo Dias
720246f012 chore: update deps 2019-01-08 16:42:39 +01:00
Hugo Dias
cfdcbe08d4 fix: update deps and repo setup 2019-01-08 16:42:39 +01:00
David Dias
1602c440ad chore: update deps 2018-06-04 09:48:43 +01:00
David Dias
937cc76714 add lead maintainer 2018-06-04 09:48:43 +01:00
ᴠɪᴄᴛᴏʀ ʙᴊᴇʟᴋʜᴏʟᴍ
651d08f67e Merge pull request #5 from libp2p/automatic-ci-script-update
Updating CI files
2017-12-22 15:00:13 +01:00
Victor Bjelkholm
f2b67f7d82 Updating CI files
This commit updates all CI scripts to the latest version
2017-12-22 14:16:17 +01:00
David Dias
98b285a840 chore: release version v0.2.2 2017-07-22 13:32:01 -07:00
David Dias
ce5fb8c1b9 chore: update contributors 2017-07-22 13:32:00 -07:00
David Dias
0dcf1a6f52 fix: circular circular dep -> DI 2017-07-22 13:31:42 -07:00
David Dias
8401154102 chore: update deps 2017-07-22 12:28:53 -07:00
David Dias
838ecdbaef chore: release version v0.2.1 2017-07-22 12:19:08 -07:00
David Dias
d14c11310e chore: update contributors 2017-07-22 12:19:08 -07:00
David Dias
41c03a86a5 chore: update deps 2017-07-22 11:40:00 -07:00
David Dias
1013becd66 chore: release version v0.2.0 2017-07-22 11:30:07 -07:00
David Dias
c21454c4e8 chore: update contributors 2017-07-22 11:30:07 -07:00
David Dias
4ee48a737a feat: next libp2p-crypto (#4)
* feat: next libp2p-crypto

* chore: update deps
2017-07-22 11:12:30 -07:00
David Dias
363cda56da chore: update ci 2017-07-21 11:38:33 -07:00
David Dias
d8b0c74ec9 chore: release version v0.1.4 2017-02-11 17:27:51 -08:00
David Dias
647fab7170 chore: update contributors 2017-02-11 17:27:51 -08:00
David Dias
e66383137f chore: add missing dependency 2017-02-11 17:27:29 -08:00
David Dias
638ea963e6 chore: release version v0.1.3 2017-02-10 18:36:19 -08:00
David Dias
35abf8fcb4 chore: update contributors 2017-02-10 18:36:19 -08:00
David Dias
9430e7f91e chore: update deps 2017-02-10 18:33:54 -08:00
David Dias
42678bba30 chore: release version v0.1.2 2017-02-09 14:08:25 -08:00
David Dias
f637ff2485 chore: update contributors 2017-02-09 14:08:25 -08:00
David Dias
4c2d42984d chore: release version v0.1.1 2017-02-09 08:05:13 -08:00
David Dias
de8b16b00b chore: update contributors 2017-02-09 08:05:13 -08:00
David Dias
73b8b39de5 chore: ^ to ~ 2017-02-09 08:03:23 -08:00
Friedel Ziegelmayer
288c9cff8f chore: release version v0.1.0 2017-02-09 12:43:44 +01:00
Friedel Ziegelmayer
418a0885ad chore: update contributors 2017-02-09 12:43:43 +01:00
Friedel Ziegelmayer
4c744d3b60 chore: update dependencies 2017-02-09 12:36:35 +01:00
Yusef Napora
4c36aeba17 feat: initial implementation 2017-02-09 12:35:39 +01:00
Friedel Ziegelmayer
e57e4ffa0d Initial commit 2017-01-19 17:50:23 +01:00
13 changed files with 602 additions and 111 deletions

View File

@@ -1,3 +1,57 @@
<a name="0.17.8"></a>
## [0.17.8](https://github.com/libp2p/js-libp2p-crypto/compare/v0.17.7...v0.17.8) (2020-07-20)
### Bug Fixes
* go ed25519 interop ([2f18a07](https://github.com/libp2p/js-libp2p-crypto/commit/2f18a07))
<a name="0.17.7"></a>
## [0.17.7](https://github.com/libp2p/js-libp2p-crypto/compare/v0.17.6...v0.17.7) (2020-06-09)
<a name="0.17.6"></a>
## [0.17.6](https://github.com/libp2p/js-libp2p-crypto/compare/v0.17.5...v0.17.6) (2020-04-07)
### Bug Fixes
* add buffer and update deps ([#25](https://github.com/libp2p/js-libp2p-crypto/issues/25)) ([35f196e](https://github.com/libp2p/js-libp2p-crypto/commit/35f196e))
* **unmarshal:** provide only one arg to callback ([#17](https://github.com/libp2p/js-libp2p-crypto/issues/17)) ([3bb8451](https://github.com/libp2p/js-libp2p-crypto/commit/3bb8451))
* circular circular dep -> DI ([0dcf1a6](https://github.com/libp2p/js-libp2p-crypto/commit/0dcf1a6))
* update deps and repo setup ([cfdcbe0](https://github.com/libp2p/js-libp2p-crypto/commit/cfdcbe0))
### Features
* add `id()` method to Secp256k1PrivateKey ([f4dbd62](https://github.com/libp2p/js-libp2p-crypto/commit/f4dbd62))
* initial implementation ([4c36aeb](https://github.com/libp2p/js-libp2p-crypto/commit/4c36aeb))
* next libp2p-crypto ([#4](https://github.com/libp2p/js-libp2p-crypto/issues/4)) ([4ee48a7](https://github.com/libp2p/js-libp2p-crypto/commit/4ee48a7))
* use async await ([#18](https://github.com/libp2p/js-libp2p-crypto/issues/18)) ([1974eb9](https://github.com/libp2p/js-libp2p-crypto/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.17.5"></a>
## [0.17.5](https://github.com/libp2p/js-libp2p-crypto/compare/v0.17.4...v0.17.5) (2020-03-24)
<a name="0.17.4"></a>
## [0.17.4](https://github.com/libp2p/js-libp2p-crypto/compare/v0.17.3...v0.17.4) (2020-03-23)

View File

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

View File

@@ -1,6 +1,6 @@
{
"name": "libp2p-crypto",
"version": "0.17.4",
"version": "0.17.8",
"description": "Crypto primitives for libp2p",
"main": "src/index.js",
"types": "src/index.d.ts",
@@ -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.6.0",
"multibase": "^0.7.0",
"multihashing-async": "^0.8.1",
"node-forge": "~0.9.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": {
@@ -54,8 +56,8 @@
"@types/chai-string": "^1.4.2",
"@types/dirty-chai": "^2.0.2",
"@types/mocha": "^7.0.1",
"@types/sinon": "^7.5.1",
"aegir": "^21.0.2",
"@types/sinon": "^9.0.0",
"aegir": "^22.0.0",
"benchmark": "^2.1.4",
"chai": "^4.2.0",
"chai-string": "^1.5.0",
@@ -77,23 +79,25 @@
"contributors": [
"David Dias <daviddias.p@gmail.com>",
"Friedel Ziegelmayer <dignifiedquire@gmail.com>",
"Maciej Krüger <mkg20001@gmail.com>",
"Jacob Heun <jacobheun@gmail.com>",
"Maciej Krüger <mkg20001@gmail.com>",
"dryajov <dryajov@gmail.com>",
"Alan Shaw <alan.shaw@protocol.ai>",
"Yusef Napora <yusef@napora.org>",
"Richard Littauer <richard.littauer@gmail.com>",
"Arve Knudsen <arve.knudsen@gmail.com>",
"Hugo Dias <hugomrdias@gmail.com>",
"Jack Kleeman <jackkleeman@gmail.com>",
"Richard Schneider <makaretu@gmail.com>",
"Yusef Napora <yusef@napora.org>",
"Cayman <caymannava@gmail.com>",
"Victor Bjelkholm <victorbjelkholm@gmail.com>",
"Arve Knudsen <arve.knudsen@gmail.com>",
"Vasco Santos <vasco.santos@ua.pt>",
"Jack Kleeman <jackkleeman@gmail.com>",
"Richard Littauer <richard.littauer@gmail.com>",
"Richard Schneider <makaretu@gmail.com>",
"Alex Potsides <alex@achingbrain.net>",
"dirkmc <dirkmdev@gmail.com>",
"Alberto Elias <hi@albertoelias.me>",
"nikuda <nikuda@gmail.com>",
"ᴠɪᴄᴛᴏʀ ʙᴊᴇʟᴋʜᴏʟᴍ <victorbjelkholm@gmail.com>",
"Tom Swindell <t.swindell@rubyx.co.uk>",
"Carson Farmer <carson.farmer@gmail.com>",
"Alberto Elias <hi@albertoelias.me>",
"Joao Santos <jrmsantos15@gmail.com>"
]
}

View File

@@ -89,9 +89,17 @@ class Ed25519PrivateKey {
}
function unmarshalEd25519PrivateKey (bytes) {
bytes = ensureKey(bytes, crypto.privateKeyLength + crypto.publicKeyLength)
// Try the old, redundant public key version
if (bytes.length > crypto.privateKeyLength) {
bytes = ensureKey(bytes, crypto.privateKeyLength + crypto.publicKeyLength)
const privateKeyBytes = bytes.slice(0, crypto.privateKeyLength)
const publicKeyBytes = bytes.slice(crypto.privateKeyLength, bytes.length)
return new Ed25519PrivateKey(privateKeyBytes, publicKeyBytes)
}
bytes = ensureKey(bytes, crypto.privateKeyLength)
const privateKeyBytes = bytes.slice(0, crypto.privateKeyLength)
const publicKeyBytes = bytes.slice(crypto.privateKeyLength, bytes.length)
const publicKeyBytes = bytes.slice(crypto.publicKeyLength)
return new Ed25519PrivateKey(privateKeyBytes, publicKeyBytes)
}
@@ -111,11 +119,9 @@ async function generateKeyPairFromSeed (seed) {
}
function ensureKey (key, length) {
if (Buffer.isBuffer(key)) {
key = new Uint8Array(key)
}
if (!(key instanceof Uint8Array) || key.length !== length) {
throw errcode(new Error('Key must be a Uint8Array or Buffer of length ' + length), 'ERR_INVALID_KEY_TYPE')
key = Uint8Array.from(key || [])
if (key.length !== length) {
throw errcode(new Error(`Key must be a Uint8Array or Buffer of length ${length}, got ${key.length}`), 'ERR_INVALID_KEY_TYPE')
}
return key
}

View File

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

109
src/keys/secp256k1-class.js Normal file
View File

@@ -0,0 +1,109 @@
'use strict'
const multibase = require('multibase')
const sha = require('multihashing-async/src/sha')
module.exports = (keysProtobuf, randomBytes, crypto) => {
crypto = crypto || require('./secp256k1')(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.
*
* @returns {Promise<string>}
*/
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
}
}

86
src/keys/secp256k1.js Normal file
View File

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

View File

@@ -98,7 +98,7 @@ describe('AES-CTR', () => {
// @ts-check
/**
* @type {function(Cipher): void}
* @type {function(Cipher): Promise<void>}
*/
async function encryptAndDecrypt (cipher) {
const data = Buffer.alloc(100)

View File

@@ -2,28 +2,42 @@
const { Buffer } = require('buffer')
module.exports = {
// These were generated in a gore (https://github.com/motemen/gore) repl session:
// Generation code from https://github.com/libp2p/js-libp2p-crypto/issues/175#issuecomment-634467463
//
// :import github.com/libp2p/go-libp2p-crypto
// :import crypto/rand
// priv, pub, err := crypto.GenerateEd25519Key(rand.Reader)
// pubkeyBytes, err := pub.Bytes()
// privkeyBytes, err := priv.Bytes()
// data := []byte("hello! and welcome to some awesome crypto primitives")
// sig, err := priv.Sign(data)
// package main
//
// :import io/ioutil
// ioutil.WriteFile("/tmp/pubkey_go.bin", pubkeyBytes, 0644)
// // etc..
// import (
// "crypto/rand"
// "fmt"
// "strings"
// "github.com/libp2p/go-libp2p-core/crypto"
// )
// func main() {
// priv, pub, _ := crypto.GenerateEd25519Key(rand.Reader)
// pubkeyBytes, _ := pub.Bytes()
// privkeyBytes, _ := priv.Bytes()
// data := []byte("hello! and welcome to some awesome crypto primitives")
// sig, _ := priv.Sign(data)
// fmt.Println("{\n publicKey: Buffer.from(", strings.Replace(fmt.Sprint(pubkeyBytes), " ", ",", -1), "),")
// fmt.Println(" privateKey: Buffer.from(", strings.Replace(fmt.Sprint(privkeyBytes), " ", ",", -1), "),")
// fmt.Println(" data: Buffer.from(", strings.Replace(fmt.Sprint(data), " ", ",", -1), "),")
// fmt.Println(" signature: Buffer.from(", strings.Replace(fmt.Sprint(sig), " ", ",", -1), ")\n}")
// }
//
// Then loaded into a node repl and dumped to arrays with:
//
// var pubkey = Array.from(fs.readFileSync('/tmp/pubkey_go.bin'))
// console.log(JSON.stringify(pubkey))
verify: {
// The legacy key unnecessarily appends the publickey. (It's already included) See https://github.com/libp2p/js-libp2p-crypto/issues/175
redundantPubKey: {
privateKey: Buffer.from([8, 1, 18, 96, 201, 208, 1, 110, 176, 16, 230, 37, 66, 184, 149, 252, 78, 56, 206, 136, 2, 38, 118, 152, 226, 197, 117, 200, 54, 189, 156, 218, 184, 7, 118, 57, 233, 49, 221, 97, 164, 158, 241, 129, 73, 166, 225, 255, 193, 118, 22, 84, 55, 15, 249, 168, 225, 180, 198, 191, 14, 75, 187, 243, 150, 91, 232, 37, 233, 49, 221, 97, 164, 158, 241, 129, 73, 166, 225, 255, 193, 118, 22, 84, 55, 15, 249, 168, 225, 180, 198, 191, 14, 75, 187, 243, 150, 91, 232, 37]),
publicKey: Buffer.from([8, 1, 18, 32, 233, 49, 221, 97, 164, 158, 241, 129, 73, 166, 225, 255, 193, 118, 22, 84, 55, 15, 249, 168, 225, 180, 198, 191, 14, 75, 187, 243, 150, 91, 232, 37]),
data: Buffer.from([104, 101, 108, 108, 111, 33, 32, 97, 110, 100, 32, 119, 101, 108, 99, 111, 109, 101, 32, 116, 111, 32, 115, 111, 109, 101, 32, 97, 119, 101, 115, 111, 109, 101, 32, 99, 114, 121, 112, 116, 111, 32, 112, 114, 105, 109, 105, 116, 105, 118, 101, 115]),
signature: Buffer.from([7, 230, 175, 164, 228, 58, 78, 208, 62, 243, 73, 142, 83, 195, 176, 217, 166, 62, 41, 165, 168, 164, 75, 179, 163, 86, 102, 32, 18, 84, 150, 237, 39, 207, 213, 20, 134, 237, 50, 41, 176, 183, 229, 133, 38, 255, 42, 228, 68, 186, 100, 14, 175, 156, 243, 118, 125, 125, 120, 212, 124, 103, 252, 12])
},
verify: {
publicKey: Buffer.from([8, 1, 18, 32, 163, 176, 195, 47, 254, 208, 49, 5, 192, 102, 32, 63, 58, 202, 171, 153, 146, 164, 25, 212, 25, 91, 146, 26, 117, 165, 148, 6, 207, 90, 217, 126]),
privateKey: Buffer.from([8, 1, 18, 64, 232, 56, 175, 20, 240, 160, 19, 47, 92, 88, 115, 221, 164, 13, 36, 162, 158, 136, 247, 31, 29, 231, 76, 143, 12, 91, 193, 4, 88, 33, 67, 23, 163, 176, 195, 47, 254, 208, 49, 5, 192, 102, 32, 63, 58, 202, 171, 153, 146, 164, 25, 212, 25, 91, 146, 26, 117, 165, 148, 6, 207, 90, 217, 126]),
data: Buffer.from([104, 101, 108, 108, 111, 33, 32, 97, 110, 100, 32, 119, 101, 108, 99, 111, 109, 101, 32, 116, 111, 32, 115, 111, 109, 101, 32, 97, 119, 101, 115, 111, 109, 101, 32, 99, 114, 121, 112, 116, 111, 32, 112, 114, 105, 109, 105, 116, 105, 118, 101, 115]),
signature: Buffer.from([160, 125, 30, 62, 213, 189, 239, 92, 87, 76, 205, 169, 251, 149, 187, 57, 96, 85, 175, 213, 22, 132, 229, 60, 196, 18, 117, 194, 12, 174, 135, 31, 39, 168, 174, 103, 78, 55, 37, 222, 37, 172, 222, 239, 153, 63, 197, 152, 67, 167, 191, 215, 161, 212, 216, 163, 81, 77, 45, 228, 151, 79, 101, 1])
}
}

31
test/fixtures/go-key-secp256k1.js vendored Normal file
View File

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

View File

@@ -131,25 +131,28 @@ describe('ed25519', function () {
describe('go interop', () => {
// @ts-check
/**
* @type {PrivateKey}
*/
let privateKey
before(async () => {
const key = await crypto.keys.unmarshalPrivateKey(fixtures.verify.privateKey)
privateKey = key
})
it('verifies with data from go', async () => {
const key = crypto.keys.unmarshalPublicKey(fixtures.verify.publicKey)
const ok = await key.verify(fixtures.verify.data, fixtures.verify.signature)
expect(ok).to.eql(true)
})
it('verifies with data from go with redundant public key', async () => {
const key = crypto.keys.unmarshalPublicKey(fixtures.redundantPubKey.publicKey)
const ok = await key.verify(fixtures.redundantPubKey.data, fixtures.redundantPubKey.signature)
expect(ok).to.eql(true)
})
it('generates the same signature as go', async () => {
const sig = await privateKey.sign(fixtures.verify.data)
const key = await crypto.keys.unmarshalPrivateKey(fixtures.verify.privateKey)
const sig = await key.sign(fixtures.verify.data)
expect(sig).to.eql(fixtures.verify.signature)
})
it('generates the same signature as go with redundant public key', async () => {
const key = await crypto.keys.unmarshalPrivateKey(fixtures.redundantPubKey.privateKey)
const sig = await key.sign(fixtures.redundantPubKey.data)
expect(sig).to.eql(fixtures.redundantPubKey.signature)
})
})
})

View File

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

View File

@@ -6,7 +6,7 @@ const expect = chai.expect
// @ts-check
/**
* @type {function(any, string): void}
* @type {function(any, string): Promise<void>}
*/
const expectErrCode = async (p, code) => {
try {