Compare commits

...

225 Commits

Author SHA1 Message Date
Jacob Heun
d3601fa936 chore: release version v0.17.3 2020-02-26 17:21:45 +01:00
Jacob Heun
f01e3812e9 chore: update contributors 2020-02-26 17:21:45 +01:00
Alan Shaw
00477e3bcb perf: remove asn1.js and use node-forge (#166)
* perf: remove asn1.js from rsa

* fix: tweaks

* fix: it works, but I do not know 100% why

* chore: remove asn1.js

* fix: ensure jwk params encoded as uint

* fix: util tests

* fix: zero pad base64urlToBuffer

* fix: more zero pad

* test: add round trip test

* test: base64url to Buffer with padding
2020-02-26 17:16:32 +01:00
dependabot-preview[bot]
0f4c533dfa chore(deps-dev): bump sinon from 8.1.1 to 9.0.0
Bumps [sinon](https://github.com/sinonjs/sinon) from 8.1.1 to 9.0.0.
- [Release notes](https://github.com/sinonjs/sinon/releases)
- [Changelog](https://github.com/sinonjs/sinon/blob/master/CHANGELOG.md)
- [Commits](https://github.com/sinonjs/sinon/compare/v8.1.1...v9.0.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-19 08:27:36 -05:00
dependabot-preview[bot]
d566e7ef3b chore(deps-dev): bump @types/mocha from 5.2.7 to 7.0.1
Bumps [@types/mocha](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/mocha) from 5.2.7 to 7.0.1.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/mocha)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-18 08:31:56 -05:00
dependabot-preview[bot]
78e2ddd2bd chore(deps-dev): bump sinon from 7.5.0 to 8.1.1
Bumps [sinon](https://github.com/sinonjs/sinon) from 7.5.0 to 8.1.1.
- [Release notes](https://github.com/sinonjs/sinon/releases)
- [Changelog](https://github.com/sinonjs/sinon/blob/master/CHANGELOG.md)
- [Commits](https://github.com/sinonjs/sinon/compare/v7.5.0...v8.1.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-18 08:31:41 -05:00
dependabot-preview[bot]
0ad513887a chore(deps): bump err-code from 1.1.2 to 2.0.0 (#165)
Bumps [err-code](https://github.com/IndigoUnited/js-err-code) from 1.1.2 to 2.0.0.
- [Release notes](https://github.com/IndigoUnited/js-err-code/releases)
- [Commits](https://github.com/IndigoUnited/js-err-code/compare/1.1.2...v2.0.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-18 07:57:07 -05:00
dependabot-preview[bot]
e7468d830d chore(deps-dev): bump aegir from 20.6.1 to 21.0.2 (#167)
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 07:56:49 -05:00
Alan Shaw
cc2094975b perf: remove jwk2privPem and jwk2pubPem (#162)
These 2 unused functions required us to import the whole of the node-forge PKI implementation when we only use some RSA stuffs.

BREAKING CHANGE: removes unused jwk2pem methods `jwk2pubPem` and `jwk2privPem`. These methods are not being used in any js libp2p modules, so only users referencing these directly will be impacted.
2020-02-03 14:28:23 +01:00
Jacob Heun
ad4bf3b357 chore: release version v0.17.2 2020-01-17 12:08:41 +01:00
Jacob Heun
730d762717 chore: update contributors 2020-01-17 12:08:40 +01:00
Carson Farmer
e01977c5a3 feat: add typescript types + linting/tests (#161)
* feat: adds typescript types + linting/tests

Signed-off-by: Carson Farmer <carson.farmer@gmail.com>

* feat: much better types testing

Signed-off-by: Carson Farmer <carson.farmer@gmail.com>

* chore: revert eslintignore

Signed-off-by: Carson Farmer <carson.farmer@gmail.com>

* feat: update types entry

Signed-off-by: Carson Farmer <carson.farmer@gmail.com>

* chore: exclude has no effect here

Signed-off-by: Carson Farmer <carson.farmer@gmail.com>

* feat: more nuanced return types on keypair

Signed-off-by: Carson Farmer <carson.farmer@gmail.com>
2020-01-17 12:04:52 +01:00
Jacob Heun
b5d94ecae7 chore: release version v0.17.1 2019-10-25 13:56:54 +02:00
Jacob Heun
1f9c2ddadb chore: update contributors 2019-10-25 13:56:53 +02:00
Jacob Heun
d6d06a8404 chore: remove commitlint
chore: update deps

chore: add bundlesize to ci
2019-10-25 13:51:00 +02:00
Maciej Krüger
8b8d0c1510 fix: jwk var naming 2019-10-25 13:51:00 +02:00
Maciej Krüger
b998f63aec feat: use forge to convert jwk2forge 2019-10-25 13:51:00 +02:00
Maciej Krüger
adc6eb478c test: add interop test 2019-10-25 13:51:00 +02:00
Maciej Krüger
2c1bac5ce9 fix: padding error 2019-10-25 13:51:00 +02:00
Maciej Krüger
027a5a9332 fix: use direct buffers instead of converting to hex 2019-10-25 13:51:00 +02:00
Maciej Krüger
2c294b56ab fix: lint 2019-10-25 13:51:00 +02:00
Maciej Krüger
487cd076fb refactor: cleanup 2019-10-25 13:51:00 +02:00
Maciej Krüger
b8e2414420 fix: browser rsa enc/dec 2019-10-25 13:51:00 +02:00
Maciej Krüger
9f747a173f feat: browser enc/dec 2019-10-25 13:51:00 +02:00
Maciej Krüger
34c5f5c8f0 feat: add (rsa)pubKey.encrypt and (rsa)privKey.decrypt
nodeJS only for now
2019-10-25 13:51:00 +02:00
Jacob Heun
a008bc2fcb chore: update lead maintainer (#159) 2019-09-26 12:51:16 +02:00
Friedel Ziegelmayer
b68060388f Merge pull request #157 from libp2p/fix/better-err-missing-webcrypto
fix: better error for missing web crypto
2019-08-13 15:28:33 +02:00
Alan Shaw
afe94ded6b docs: problem resolution 2019-07-22 11:21:24 +01:00
Alan Shaw
a5e05603ef fix: better error for missing web crypto
This PR simply detects missing web crypto and throws an error with an appropriate message.

This is a stepping stone that will help users understand the problem until we have time to do a refactor of this module and of all the modules that use it to enable optionally passing your own crypto implementation.

refs https://github.com/libp2p/js-libp2p-crypto/pull/149
refs https://github.com/libp2p/js-libp2p-crypto/pull/150
refs https://github.com/libp2p/js-libp2p-crypto/issues/105
refs https://github.com/ipfs/js-ipfs/issues/2153
refs https://github.com/ipfs/js-ipfs/issues/2017

License: MIT
Signed-off-by: Alan Shaw <alan@tableflip.io>
2019-07-22 11:21:22 +01:00
dirkmc
0b686d363c chore: add error codes (#155)
* chore: add error codes

* chore: create errors with new Error()

* fix: better error testin

* refactor: simplify random bytes error checks
2019-07-22 11:16:02 +01:00
dignifiedquire
2d15e717e4 chore: release version v0.17.0 2019-07-11 15:23:54 +02:00
dignifiedquire
6775dbf670 chore: update contributors 2019-07-11 15:23:54 +02:00
Friedel Ziegelmayer
4b09aae8ca fix(deps): update to ursa-optiona@0.10 (#156)
fix(deps): update to ursa-optiona@0.10
2019-07-11 15:22:40 +02:00
dignifiedquire
764d8bff3e chore(ci): run on node@12 2019-07-11 15:05:24 +02:00
dignifiedquire
26b6217041 fix(deps): update to ursa-optiona@0.10
This brings compatiability with node@12 for the native dependency
2019-07-11 15:04:08 +02:00
Arve Knudsen
5500ac4a6e test: upgrade libp2p-crypto-secp256k1; ensure secp256k1 is tested (#151) 2019-07-10 23:32:51 +01:00
dirkmc
d675670ed9 fix: put optional args last for key export (#154)
BREAKING CHANGE: key export arguments are now swapped so that the optional format is last
2019-07-10 18:03:34 +01:00
Alan Shaw
ad7107233e feat: refactor to use async/await (#131)
BREAKING CHANGE: API refactored to use async/await

feat: WIP use async await
fix: passing tests
chore: update travis node.js versions
fix: skip ursa optional tests on windows
fix: benchmarks
docs: update docs
fix: remove broken and intested private key decrypt
chore: update deps
2019-07-10 17:15:26 +01:00
Arve Knudsen
5cd0e8cc1a fix: fix links in README (#148) 2019-07-04 14:31:43 +02:00
Vasco Santos
0ffe31821a chore: add discourse badge (#147) 2019-04-18 19:29:45 +02:00
dignifiedquire
7b3625888c chore: release version v0.16.1 2019-02-26 09:28:41 +01:00
dignifiedquire
b7bce77ad5 chore: update contributors 2019-02-26 09:28:41 +01:00
Yusef Napora
c1f867bd9c chore: update badges for consistency across libp2p org 2019-02-26 09:25:15 +01:00
Alberto Elias
e3f02eb6f1 chore: updates libp2p-crypto-secp256k1 2019-02-26 09:24:40 +01:00
Vasco Santos
a0874389a9 chore(ci): use travis
This PR changes Jenkins to travis for CI. In addition, 2 dependencies were missing in the `package.json` and the `dep-check` was not passing.
2019-02-20 20:57:36 +01:00
David Dias
567d68c855 chore: release version v0.16.0 2019-01-08 21:33:49 +01:00
David Dias
af782c5906 chore: update contributors 2019-01-08 21:33:49 +01:00
David Dias
f0593c9e6d chore: update deps 2019-01-08 21:33:02 +01:00
Hugo Dias
8d8294dc3f fix: clean up, bundle size reduction
BREAKING CHANGE: getRandomValues method exported from src/keys/rsa-browser.js and src/keys/rsa.js signature has changed from accepting an array to a number for random byte length
2019-01-08 21:29:42 +01:00
David Dias
df23d634c5 chore: release version v0.15.0 2019-01-03 16:15:45 +00:00
David Dias
88e1bcf75f chore: update contributors 2019-01-03 16:15:45 +00:00
Jacob Heun
c54ea206f0 feat: nextTick instead of setImmediate, and fix sync in async (#136)
* fix: avoid sync callback in async function

* chore: fix linting

* chore: remove non jenkins ci

* refactor: use nextTick over setImmediate

* refactor: async/nextTick for better browser support
2019-01-03 16:13:07 +00:00
David Dias
857d2bd902 chore: release version v0.14.1 2018-11-05 18:29:00 +00:00
David Dias
200110cb9d chore: update contributors 2018-11-05 18:29:00 +00:00
Jacob Heun
9e5778694c fix: dont setimmediate when its not needed 2018-11-05 19:26:45 +01:00
Jacob Heun
87e8f1c86f chore: remove lint warnings 2018-11-05 19:26:45 +01:00
Jacob Heun
df75980a88 chore: update deps 2018-11-05 19:26:45 +01:00
David Dias
934390acd3 chore: release version v0.14.0 2018-09-17 15:35:20 -07:00
David Dias
8b80b46667 chore: update contributors 2018-09-17 15:35:20 -07:00
David Dias
e8efad546f chore: update deps 2018-09-17 15:33:43 -07:00
Maciej Krüger
e8cbf13d85 fix(lint): use ~ for ursa-optional version 2018-09-17 15:32:13 -07:00
Maciej Krüger
c7e0409c1c fix: windows build 2018-09-17 15:32:13 -07:00
Maciej Krüger
f4c00893ad test: add test for different rsa crypto libs 2018-09-17 15:32:13 -07:00
Maciej Krüger
b05e77f375 feat: use ursa-optional for lightning fast key generation
The difference between ursa and ursa-optional is that ursa-optional does not cause any problems if it fails to compile
2018-09-17 15:32:13 -07:00
Joao Santos
ad478454d8 docs: adds usage examples to AES and HMAC 2018-05-29 14:23:29 +01:00
David Dias
8c69ffb20f docs: Lead maintainer (#119)
* add lead maintainer

* chore: update deps

* Update package.json
2018-05-29 14:22:48 +01:00
David Dias
e689a402a3 chore: release version v0.13.0 2018-04-05 17:32:18 +01:00
David Dias
4bd032a6ae chore: update contributors 2018-04-05 17:32:18 +01:00
David Dias
50c61ba46e chore: update deps 2018-04-05 17:30:34 +01:00
David Dias
3a90f70350 chore: release version v0.12.1 2018-02-12 09:27:25 +00:00
David Dias
743c69524c chore: update contributors 2018-02-12 09:27:24 +00:00
David Dias
1a347fa04c chore: update deps 2018-02-12 09:25:50 +00:00
David Dias
71339e08e7 chore: release version v0.12.0 2018-01-27 10:55:54 -08:00
David Dias
0ab2c2d2d6 chore: update contributors 2018-01-27 10:55:54 -08:00
Richard Schneider
cdcca5f828 feat: improve perf (#117) 2018-01-27 10:54:04 -08:00
ᴠɪᴄᴛᴏʀ ʙᴊᴇʟᴋʜᴏʟᴍ
2c0dc706b7 Merge pull request #116 from libp2p/automatic-ci-script-update
Updating CI files
2017-12-21 17:29:43 +01:00
Victor Bjelkholm
21d4c8b74e Updating CI files
This commit updates all CI scripts to the latest version
2017-12-21 17:20:14 +01:00
David Dias
285b6ca392 chore: release version v0.11.0 2017-12-20 08:12:56 +00:00
David Dias
7d96dd3243 chore: update contributors 2017-12-20 08:12:56 +00:00
Richard Schneider
b3421284f9 feat: key exchange with jsrsasign (#115)
* feat: export/import password protected RSA key

* docs: crypto.key.import

* test: import with wrong password

* fix: lint

* test: importing openssl keys

* just to trigger circle with new ubuntu

* feat: get the RSA key id

* feat: get the ED 25519 key id

* feat: pbkdf2

* fix(pbkdf2): base64 has more chars to guess than hex

* chore: update deps
2017-12-20 08:11:47 +00:00
David Dias
f3cb8ced36 chore: release version v0.10.4 2017-12-01 08:42:19 +00:00
David Dias
0a6f63dce5 chore: update contributors 2017-12-01 08:42:18 +00:00
David Dias
0ce5f34a08 chore: gitignore 2017-12-01 08:42:06 +00:00
David Dias
a826968e71 chore: update deps 2017-12-01 08:41:45 +00:00
Maciej Krüger
7608fdd858 fix: catch error when unmarshaling instead of crashing (#113)
* test: Add failing tests

* fix: Fix some failing tests

* fix: various fixes on garbage error handling and respective tests

* tests: increased timeout for test timing out in CI

* tests: increasing test timeout to please the CI gods

* tests: increasing test timeout to please the CI gods

* fix: for when unMarshallPrivateKey is called with null or undefined key
2017-12-01 08:36:29 +00:00
David Dias
bf9b532067 chore: release version v0.10.3 2017-09-07 10:38:29 +01:00
David Dias
5296f8a42f chore: update contributors 2017-09-07 10:38:29 +01:00
Friedel Ziegelmayer
3a91ae2ed8 feat: switch protocol-buffers to protons (#110)
Ref https://github.com/ipfs/js-ipfs/issues/991
2017-09-07 10:37:56 +01:00
David Dias
e7c11a8e01 chore: release version v0.10.2 2017-09-06 08:30:10 +01:00
David Dias
9bb96dc2bf chore: update contributors 2017-09-06 08:30:09 +01:00
David Dias
957fdd37e9 fix: use regular protocol-buffers until protobufjs is fixed (#109) 2017-09-06 08:29:45 +01:00
Friedel Ziegelmayer
83257bc4bd feat(deps): upgrade to aegir@12 and browserify-aes@1.0.8 2017-09-05 15:28:43 +02:00
David Dias
cb7fae7fcc chore: release version v0.10.1 2017-09-05 11:08:46 +01:00
David Dias
7669847c17 chore: update contributors 2017-09-05 11:08:46 +01:00
Friedel Ziegelmayer
dc2793f138 fix: switch to protobufjs (#107)
rm unsafe-eval
2017-09-05 11:05:47 +01:00
David Dias
e0b916ace9 chore: release version v0.10.0 2017-09-03 12:19:51 +01:00
David Dias
0a71af7b89 chore: update contributors 2017-09-03 12:19:51 +01:00
David Dias
46adafb207 chore: update deps 2017-09-03 12:19:03 +01:00
David Dias
9e977c7d44 feat: p2p addrs situation (#106)
* chore: update deps

* chore: update gitignore

* chore: update CI

* chore: update deps
2017-09-03 12:18:18 +01:00
Friedel Ziegelmayer
f20267b962 feat: skip nextTick in nodeify (#103) 2017-08-17 06:38:26 +02:00
David Dias
1b6a070fa8 chore: release version v0.9.4 2017-07-22 13:26:07 -07:00
David Dias
1471e07bf9 chore: update contributors 2017-07-22 13:26:06 -07:00
David Dias
bc554d1407 fix: circular circular dep -> DI 2017-07-22 13:25:15 -07:00
David Dias
83d2d52205 chore: release version v0.9.3 2017-07-22 12:36:06 -07:00
David Dias
301c779f49 chore: update contributors 2017-07-22 12:36:06 -07:00
David Dias
d552fd423a chore: use ~ instead of ^ 2017-07-22 12:33:24 -07:00
David Dias
306504c386 chore: release version v0.9.2 2017-07-22 12:23:30 -07:00
David Dias
bf80e8e511 chore: update contributors 2017-07-22 12:23:29 -07:00
David Dias
4e619e60f5 chore: update deps 2017-07-22 12:21:55 -07:00
David Dias
a958f52a5c chore: release version v0.9.1 2017-07-22 11:37:45 -07:00
David Dias
dfc476c4f6 chore: update contributors 2017-07-22 11:37:45 -07:00
David Dias
71eb33a44f chore: update deps 2017-07-22 11:34:27 -07:00
David Dias
310733726e chore: update deps 2017-07-22 11:31:04 -07:00
David Dias
0efc94bd20 chore: release version v0.9.0 2017-07-22 11:02:48 -07:00
David Dias
68f4b5f496 chore: update contributors 2017-07-22 11:02:47 -07:00
David Dias
2f8e234044 refactor: the whole thing (#102) 2017-07-22 10:57:27 -07:00
David Dias
c2c6fde394 chore: update deps 2017-07-20 20:13:52 -07:00
Greenkeeper
14dde32177 chore(package): update chai to version 4.0.1 (#100)
https://greenkeeper.io/
2017-06-01 10:24:43 +01:00
Friedel Ziegelmayer
5bc30297de chore: release version v0.8.8 2017-04-11 11:34:11 +02:00
Friedel Ziegelmayer
a5ad8cf444 chore: update contributors 2017-04-11 11:34:11 +02:00
Jack Kleeman
be64372a5e fix(ecdh): allow base64 to be left-0-padded, needed for JWK format
Fixes #97
2017-04-11 11:14:00 +02:00
David Dias
2b0b7abd78 chore: release version v0.8.7 2017-03-21 15:07:57 +00:00
David Dias
17f2065d21 chore: update contributors 2017-03-21 15:07:57 +00:00
David Dias
2f065167fa chore: update aegir 2017-03-21 15:05:22 +00:00
Friedel Ziegelmayer
e0ecce18ce chore: release version v0.8.6 2017-03-03 21:44:03 +01:00
Friedel Ziegelmayer
cea1140ec6 chore: update contributors 2017-03-03 21:44:03 +01:00
Jack Kleeman
e5b7c1f622 feat(keys): implement generateKeyPairFromSeed for ed25519
Implement generateKeyPairFromSeed for ed25519 - this will produce the same keypair for the same seed (or first 32 bytes of reader) as given to GenerateKeyPairWithReader in go-libp2p.
2017-03-03 21:38:51 +01:00
Friedel Ziegelmayer
20326199d9 Merge pull request #80 from libp2p/greenkeeper/tweetnacl-1.0.0-rc.1
Update tweetnacl to the latest version 🚀
2017-02-24 17:56:59 +01:00
greenkeeper[bot]
4e56e1724f fix(package): update tweetnacl to version 1.0.0-rc.1
https://greenkeeper.io/
2017-02-22 11:39:32 +00:00
David Dias
7d63f698c0 chore: release version v0.8.5 2017-02-10 18:54:16 -08:00
David Dias
a49df7786c chore: update contributors 2017-02-10 18:54:16 -08:00
David Dias
c73adb00cc fix: add libp2p-crypto-secp256k1 2017-02-10 18:53:36 -08:00
David Dias
8a95de4700 chore: release version v0.8.4 2017-02-10 17:54:47 -08:00
David Dias
c35a65133f chore: update contributors 2017-02-10 17:54:47 -08:00
David Dias
4eb4aa05de fix: rm browserify-optional and transforms 2017-02-10 17:54:11 -08:00
David Dias
21f96a4eb7 chore: release version v0.8.3 2017-02-10 16:16:49 -08:00
David Dias
39b5124526 chore: update contributors 2017-02-10 16:16:49 -08:00
David Dias
93e50dde10 fix: browserify-optional needs to be dep 2017-02-10 16:16:11 -08:00
David Dias
e383da5d44 chore: release version v0.8.2 2017-02-10 10:28:44 -08:00
David Dias
9b687f380c chore: update contributors 2017-02-10 10:28:44 -08:00
Friedel Ziegelmayer
82abede64a fix: use browserify-optional (#72)
This works around the issue that browserify throws on missing modules.
2017-02-10 10:21:56 -08:00
David Dias
a56ab406e4 chore: release version v0.8.1 2017-02-09 08:32:12 -08:00
David Dias
d753941c6e chore: update contributors 2017-02-09 08:32:12 -08:00
David Dias
6022eb0838 chore: ^ to ~ 2017-02-09 08:29:55 -08:00
Dmitriy Ryajov
1c6d0912cd chore: removing --webworker from build steps 2017-02-09 12:14:26 +01:00
Friedel Ziegelmayer
e18804c31e chore: release version v0.8.0 2017-02-07 21:03:56 +01:00
Friedel Ziegelmayer
133a2c8833 chore: update contributors 2017-02-07 21:03:55 +01:00
Friedel Ziegelmayer
fad0865b2b chore: drop webworker flag 2017-02-07 20:46:58 +01:00
Friedel Ziegelmayer
4edef07182 Merge pull request #67 from libp2p/greenkeeper-aegir-10.0.0
Update aegir to version 10.0.0 🚀
2017-02-07 20:45:13 +01:00
Friedel Ziegelmayer
19c6ce7c06 Merge pull request #65 from dryajov/master
feat: change window to self for webworker support
2017-02-07 20:45:01 +01:00
Friedel Ziegelmayer
3fa76a874f Merge pull request #66 from libp2p/expose-proto
feat: expose protobuf
2017-02-07 20:44:36 +01:00
greenkeeperio-bot
6e0fd6f257 chore(package): update aegir to version 10.0.0
https://greenkeeper.io/
2017-02-07 18:45:52 +01:00
Friedel Ziegelmayer
c91d9b61c8 feat: expose protobuf
this allows other modules to reuse the protobuf definition
2017-02-07 17:51:43 +01:00
Yusef Napora
76eeb5aa18 feat: add support for secp256k1 keys through the libp2p-crypto-secp256k1 module 2017-02-04 10:23:38 +01:00
dryajov
843b5e33d6 WIP: use self instead of window for WebWorker compatibility 2017-01-31 02:09:46 -08:00
Friedel Ziegelmayer
308ac7cd1a chore: release version v0.7.7 2017-01-27 14:07:42 +01:00
Friedel Ziegelmayer
6c8f978ea4 chore: update contributors 2017-01-27 14:07:42 +01:00
Friedel Ziegelmayer
b93b410357 Merge pull request #63 from libp2p/dryajov-master
Dryajov master
2017-01-27 13:53:26 +01:00
Friedel Ziegelmayer
99a5245fa3 update deps 2017-01-27 12:15:19 +01:00
dryajov
34856d58ab removing contributors 2017-01-26 17:20:51 -08:00
dryajov
d59c6af7c0 Merge branch 'master' of https://github.com/libp2p/js-libp2p-crypto 2017-01-23 18:39:51 -08:00
dryajov
1683bf1dc8 reverting version 2017-01-23 18:32:52 -08:00
dryajov
a4edf8b99b chore: release version v0.7.6 2017-01-23 18:30:30 -08:00
dryajov
27524354af chore: update contributors 2017-01-23 18:30:30 -08:00
dryajov
a1f054ca8e adding webworker test task 2017-01-22 19:39:58 -08:00
dryajov
5c61c89391 fixing lint issues with self 2017-01-20 16:20:23 -08:00
David Dias
c138a04d2d chore: release version v0.7.6 2017-01-16 04:21:42 +00:00
David Dias
7c913c0769 chore: update contributors 2017-01-16 04:21:42 +00:00
Friedel Ziegelmayer
1a2d468369 fix: consistent buffer usage (#56)
* fix: consistent buffer usage

Closes #49

* more fixes for node 4
2017-01-16 05:17:50 +01:00
Tom Swindell
98bc0bbc5f Support for WebWorker.
Signed-off-by: Tom Swindell <t.swindell@rubyx.co.uk>
2017-01-14 12:45:52 +00:00
Yusef Napora
c45bdf602e feat(keys): add Ed25519 support for signing & verification
Closes #43
2016-12-23 14:52:40 +01:00
Greenkeeper
e92bab1736 chore(package): update node-webcrypto-ossl to version 1.0.15 (#54)
https://greenkeeper.io/
2016-12-18 11:37:21 +00:00
Friedel Ziegelmayer
c57d1e4d4f chore: release version v0.7.5 2016-12-13 12:56:43 +01:00
Friedel Ziegelmayer
04682acd86 chore: update contributors 2016-12-13 12:56:42 +01:00
Friedel Ziegelmayer
fba4833aef Merge pull request #53 from libp2p/fix/another-buffer-bites-the-dust
fix: use toArrayLike instead of toBuffer for browserify
2016-12-13 12:55:58 +01:00
Friedel Ziegelmayer
2842df7944 fix: use toArrayLike instead of toBuffer for browserify 2016-12-13 12:52:43 +01:00
Greenkeeper
74c0d28f86 chore(package): update aegir to version 9.2.1 (#50)
https://greenkeeper.io/
2016-12-10 11:59:47 -08:00
David Dias
cf7ed6fa49 chore: release version v0.7.4 2016-12-06 00:07:11 +00:00
David Dias
c1ffa41697 chore: update contributors 2016-12-06 00:07:11 +00:00
nikuda
933119445f fix(utils): make util.toBase64 browserify compatible
`bn.toArrayLike` is used instead of `bn.toBuffer`, to ensure compatibility with browserify.
2016-12-03 09:32:07 +01:00
David Dias
a4e6f9dd83 chore: release version v0.7.3 2016-12-01 11:43:46 +00:00
David Dias
e252db300c chore: update contributors 2016-12-01 11:43:46 +00:00
David Dias
98b37d49c4 feat: add randomBytes function (#45)
* feat: add randomBytes function

* fix: apply CR
2016-12-01 11:42:19 +00:00
David Dias
f979fcd3c2 chore: release version v0.7.2 2016-11-30 11:27:17 +00:00
David Dias
0af48bbebc chore: update contributors 2016-11-30 11:27:16 +00:00
David Dias
3f9d8d557c Merge pull request #42 from libp2p/feat/better-compat
Better compatability
2016-11-30 11:23:14 +00:00
Friedel Ziegelmayer
91a3b50ac9 feat(deps): update to multihashing-async@0.3.0 2016-11-29 18:11:58 +01:00
Friedel Ziegelmayer
148d16ab25 feat(rsa): add fallback pure js fallback for webcrypto-ossl 2016-11-29 17:54:41 +01:00
Friedel Ziegelmayer
6d15450438 feat(ecdh): use node core instead of webcrypto-ossl 2016-11-29 16:36:56 +01:00
David Dias
ebe1cecdeb Merge pull request #39 from libp2p/greenkeeper-protocol-buffers-3.2.1
protocol-buffers@3.2.1
2016-11-26 13:21:08 +01:00
greenkeeperio-bot
904cfb27bd chore(package): update protocol-buffers to version 3.2.1
https://greenkeeper.io/
2016-11-26 06:11:28 +01:00
Friedel Ziegelmayer
7790beb207 Merge pull request #37 from libp2p/greenkeeper-node-webcrypto-ossl-1.0.13
node-webcrypto-ossl@1.0.13 breaks build 🚨
2016-11-23 23:26:31 +01:00
greenkeeperio-bot
6308461f0f chore(package): update node-webcrypto-ossl to version 1.0.13
https://greenkeeper.io/
2016-11-23 22:20:49 +01:00
Friedel Ziegelmayer
95a0f1f0c2 Merge pull request #34 from libp2p/greenkeeper-node-webcrypto-ossl-1.0.10
node-webcrypto-ossl@1.0.10 breaks build 🚨
2016-11-16 10:53:36 +01:00
greenkeeperio-bot
0c64122342 chore(package): update node-webcrypto-ossl to version 1.0.10
https://greenkeeper.io/
2016-11-15 07:46:24 +01:00
David Dias
ce5044b4d7 chore: release version v0.7.1 2016-11-11 08:38:28 +00:00
David Dias
1daf429a74 chore: update contributors 2016-11-11 08:38:27 +00:00
David Dias
22e95bc8a4 Merge pull request #29 from libp2p/aes-interop
AES Interop
2016-11-11 08:30:18 +00:00
Friedel Ziegelmayer
9994023490 fix(aes): replace subtle.crypto with browserify-aes
Due to the design of `AES-CTR` in the webcrypto spec, there
is no streaming mode provided. This results in the counter
not being reused between subsequent calls to `encrypt` or
`decrypt`. As both the node.js and the go implementation rely
on this webcrypto had to be replaced.
2016-11-10 17:19:45 +01:00
Friedel Ziegelmayer
393fa17512 test(aes): add failing interop tests 2016-11-10 12:55:49 +01:00
Friedel Ziegelmayer
3cc26d167f chore(benchmarks): lint happy 2016-11-07 11:37:32 +01:00
David Dias
a7c2567ba2 chore: release version v0.7.0 2016-11-03 07:46:51 +00:00
David Dias
69edfff49b chore: update contributors 2016-11-03 07:46:51 +00:00
David Dias
9a0e2585d8 chore: update deps 2016-11-03 07:46:34 +00:00
David Dias
12630dc514 Merge pull request #12 from libp2p/webcrypto
Async Crypto Endeavour
2016-11-03 07:42:05 +00:00
nikuda
b088bab80f feat: replace lib multihashing with multihashing-async 2016-11-01 12:48:36 +01:00
Friedel Ziegelmayer
cecadba14d chore: remove unused .aegir config 2016-11-01 12:47:59 +01:00
Friedel Ziegelmayer
6ebc408f73 docs: update the docs 2016-11-01 12:47:58 +01:00
Friedel Ziegelmayer
08c5df5e79 feat: use webcrypto in favor of node-forge
BREAKING CHANGE: generateKeyPair is now async
2016-11-01 12:47:58 +01:00
David Dias
1cd614237a Merge pull request #13 from RichardLitt/master
Update README URLs based on HTTP redirects
2016-10-17 20:54:27 +01:00
Richard Littauer
9795ac0722 Update README URLs based on HTTP redirects 2016-09-30 14:43:40 -04:00
David Dias
73a5258876 Merge pull request #11 from libp2p/greenkeeper-elliptic-6.3.2
elliptic@6.3.2
2016-09-26 18:59:05 +08:00
greenkeeperio-bot
c35a988331 chore(package): update elliptic to version 6.3.2
https://greenkeeper.io/
2016-09-15 13:31:22 +02:00
David Dias
106e08f142 chore: release version v0.6.1 2016-09-11 12:58:07 -04:00
David Dias
4c35fe844a chore: update contributors 2016-09-11 12:58:06 -04:00
David Dias
62bedc18b5 chore: fix badges 2016-09-11 12:55:41 -04:00
David Dias
79aabf3686 chore: release version v0.6.0 2016-09-11 12:52:15 -04:00
David Dias
5879861253 chore: update contributors 2016-09-11 12:52:15 -04:00
Friedel Ziegelmayer
ee2ba5418f Merge pull request #8 from libp2p/greenkeeper-aegir-8.0.0
Update aegir to version 8.0.0 🚀
2016-09-08 15:38:59 +02:00
Friedel Ziegelmayer
34b5d0ebed style: lint happy 2016-09-08 15:28:50 +02:00
greenkeeperio-bot
261b3bdc12 chore(package): update aegir to version 8.0.0
https://greenkeeper.io/
2016-09-06 15:12:03 +02:00
Friedel Ziegelmayer
16d8790472 Merge pull request #7 from libp2p/be
big-endian
2016-09-05 20:30:42 +02:00
David Dias
51cff12c3f big-endian 2016-09-05 14:26:28 -04:00
Friedel Ziegelmayer
54df375df1 Merge pull request #2 from ipfs/feature/standardize-readme
Standardize README
2016-06-26 15:00:30 +01:00
Richard Littauer
6a4d8c94c0 Standardize README
See ipfs/community#124
2016-06-24 15:54:08 +01:00
83 changed files with 6049 additions and 30349 deletions

View File

@@ -1,13 +1,3 @@
'use strict'
const path = require('path')
module.exports = {
webpack: {
resolve: {
alias: {
'node-forge': path.resolve(__dirname, 'vendor/forge.bundle.js')
}
}
}
bundlesize: { maxSize: '155kB' }
}

1
.eslintignore Normal file
View File

@@ -0,0 +1 @@
src/keys/keys.proto.js

12
.gitignore vendored
View File

@@ -1,6 +1,11 @@
docs
package-lock.json
yarn.lock
**/node_modules/
**/*.log
test/repo-tests*
**/bundle.js
# Logs
logs
@@ -33,3 +38,10 @@ node_modules
lib
dist
test/test-data/go-ipfs-repo/LOCK
test/test-data/go-ipfs-repo/LOG
test/test-data/go-ipfs-repo/LOG.old
# while testing npm5
package-lock.json
yarn.lock

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,34 +1,45 @@
sudo: false
language: node_js
cache: npm
stages:
- check
- test
- cov
node_js:
- 4
- 5
- stable
- '10'
- '12'
# Make sure we have new NPM.
before_install:
- npm install -g npm
os:
- linux
- osx
- windows
script:
- npm run lint
- npm test
- npm run coverage
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 build --bundlesize
- npx aegir dep-check
- npm run lint
before_script:
- export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start
- stage: test
name: chrome
addons:
chrome: stable
script:
- npx aegir test -t browser
after_success:
- npm run coverage-publish
- stage: test
name: firefox
addons:
firefox: latest
script:
- npx aegir test -t browser -- --browsers FirefoxHeadless
env:
- CXX=g++-4.8
addons:
firefox: 'latest'
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-4.8
notifications:
email: false

282
CHANGELOG.md Normal file
View File

@@ -0,0 +1,282 @@
<a name="0.17.3"></a>
## [0.17.3](https://github.com/libp2p/js-libp2p-crypto/compare/v0.17.2...v0.17.3) (2020-02-26)
### Performance Improvements
* remove asn1.js and use node-forge ([#166](https://github.com/libp2p/js-libp2p-crypto/issues/166)) ([00477e3](https://github.com/libp2p/js-libp2p-crypto/commit/00477e3))
* remove jwk2privPem and jwk2pubPem ([#162](https://github.com/libp2p/js-libp2p-crypto/issues/162)) ([cc20949](https://github.com/libp2p/js-libp2p-crypto/commit/cc20949))
### BREAKING CHANGES
* removes unused jwk2pem methods `jwk2pubPem` and `jwk2privPem`. These methods are not being used in any js libp2p modules, so only users referencing these directly will be impacted.
<a name="0.17.2"></a>
## [0.17.2](https://github.com/libp2p/js-libp2p-crypto/compare/v0.17.1...v0.17.2) (2020-01-17)
### Features
* add typescript types + linting/tests ([#161](https://github.com/libp2p/js-libp2p-crypto/issues/161)) ([e01977c](https://github.com/libp2p/js-libp2p-crypto/commit/e01977c))
<a name="0.17.1"></a>
## [0.17.1](https://github.com/libp2p/js-libp2p-crypto/compare/v0.17.0...v0.17.1) (2019-10-25)
### Bug Fixes
* better error for missing web crypto ([a5e0560](https://github.com/libp2p/js-libp2p-crypto/commit/a5e0560))
* browser rsa enc/dec ([b8e2414](https://github.com/libp2p/js-libp2p-crypto/commit/b8e2414))
* jwk var naming ([8b8d0c1](https://github.com/libp2p/js-libp2p-crypto/commit/8b8d0c1))
* lint ([2c294b5](https://github.com/libp2p/js-libp2p-crypto/commit/2c294b5))
* padding error ([2c1bac5](https://github.com/libp2p/js-libp2p-crypto/commit/2c1bac5))
* use direct buffers instead of converting to hex ([027a5a9](https://github.com/libp2p/js-libp2p-crypto/commit/027a5a9))
### Features
* add (rsa)pubKey.encrypt and (rsa)privKey.decrypt ([34c5f5c](https://github.com/libp2p/js-libp2p-crypto/commit/34c5f5c))
* browser enc/dec ([9f747a1](https://github.com/libp2p/js-libp2p-crypto/commit/9f747a1))
* use forge to convert jwk2forge ([b998f63](https://github.com/libp2p/js-libp2p-crypto/commit/b998f63))
<a name="0.17.0"></a>
# [0.17.0](https://github.com/libp2p/js-libp2p-crypto/compare/v0.16.1...v0.17.0) (2019-07-11)
### Bug Fixes
* **deps:** update to ursa-optiona@0.10 ([26b6217](https://github.com/libp2p/js-libp2p-crypto/commit/26b6217))
* fix links in README ([#148](https://github.com/libp2p/js-libp2p-crypto/issues/148)) ([5cd0e8c](https://github.com/libp2p/js-libp2p-crypto/commit/5cd0e8c))
* put optional args last for key export ([#154](https://github.com/libp2p/js-libp2p-crypto/issues/154)) ([d675670](https://github.com/libp2p/js-libp2p-crypto/commit/d675670))
### Features
* refactor to use async/await ([#131](https://github.com/libp2p/js-libp2p-crypto/issues/131)) ([ad71072](https://github.com/libp2p/js-libp2p-crypto/commit/ad71072))
### BREAKING CHANGES
* key export arguments are now swapped so that the optional format is last
* API refactored to use async/await
feat: WIP use async await
fix: passing tests
chore: update travis node.js versions
fix: skip ursa optional tests on windows
fix: benchmarks
docs: update docs
fix: remove broken and intested private key decrypt
chore: update deps
<a name="0.16.1"></a>
## [0.16.1](https://github.com/libp2p/js-libp2p-crypto/compare/v0.16.0...v0.16.1) (2019-02-26)
<a name="0.16.0"></a>
# [0.16.0](https://github.com/libp2p/js-libp2p-crypto/compare/v0.15.0...v0.16.0) (2019-01-08)
### Bug Fixes
* clean up, bundle size reduction ([8d8294d](https://github.com/libp2p/js-libp2p-crypto/commit/8d8294d))
### BREAKING CHANGES
* getRandomValues method exported from src/keys/rsa-browser.js and src/keys/rsa.js signature has changed from accepting an array to a number for random byte length
<a name="0.15.0"></a>
# [0.15.0](https://github.com/libp2p/js-libp2p-crypto/compare/v0.14.1...v0.15.0) (2019-01-03)
### Features
* nextTick instead of setImmediate, and fix sync in async ([#136](https://github.com/libp2p/js-libp2p-crypto/issues/136)) ([c54ea20](https://github.com/libp2p/js-libp2p-crypto/commit/c54ea20))
<a name="0.14.1"></a>
## [0.14.1](https://github.com/libp2p/js-libp2p-crypto/compare/v0.14.0...v0.14.1) (2018-11-05)
### Bug Fixes
* dont setimmediate when its not needed ([9e57786](https://github.com/libp2p/js-libp2p-crypto/commit/9e57786))
<a name="0.14.0"></a>
# [0.14.0](https://github.com/libp2p/js-libp2p-crypto/compare/v0.13.0...v0.14.0) (2018-09-17)
### Bug Fixes
* windows build ([c7e0409](https://github.com/libp2p/js-libp2p-crypto/commit/c7e0409))
* **lint:** use ~ for ursa-optional version ([e8cbf13](https://github.com/libp2p/js-libp2p-crypto/commit/e8cbf13))
### Features
* use ursa-optional for lightning fast key generation ([b05e77f](https://github.com/libp2p/js-libp2p-crypto/commit/b05e77f))
<a name="0.13.0"></a>
# [0.13.0](https://github.com/libp2p/js-libp2p-crypto/compare/v0.12.1...v0.13.0) (2018-04-05)
<a name="0.12.1"></a>
## [0.12.1](https://github.com/libp2p/js-libp2p-crypto/compare/v0.12.0...v0.12.1) (2018-02-12)
<a name="0.12.0"></a>
# [0.12.0](https://github.com/libp2p/js-libp2p-crypto/compare/v0.11.0...v0.12.0) (2018-01-27)
### Features
* improve perf ([#117](https://github.com/libp2p/js-libp2p-crypto/issues/117)) ([cdcca5f](https://github.com/libp2p/js-libp2p-crypto/commit/cdcca5f))
<a name="0.11.0"></a>
# [0.11.0](https://github.com/libp2p/js-libp2p-crypto/compare/v0.10.4...v0.11.0) (2017-12-20)
### Features
* key exchange with jsrsasign ([#115](https://github.com/libp2p/js-libp2p-crypto/issues/115)) ([b342128](https://github.com/libp2p/js-libp2p-crypto/commit/b342128))
<a name="0.10.4"></a>
## [0.10.4](https://github.com/libp2p/js-libp2p-crypto/compare/v0.10.3...v0.10.4) (2017-12-01)
### Bug Fixes
* catch error when unmarshaling instead of crashing ([#113](https://github.com/libp2p/js-libp2p-crypto/issues/113)) ([7608fdd](https://github.com/libp2p/js-libp2p-crypto/commit/7608fdd))
<a name="0.10.3"></a>
## [0.10.3](https://github.com/libp2p/js-libp2p-crypto/compare/v0.10.2...v0.10.3) (2017-09-07)
### Features
* switch protocol-buffers to protons ([#110](https://github.com/libp2p/js-libp2p-crypto/issues/110)) ([3a91ae2](https://github.com/libp2p/js-libp2p-crypto/commit/3a91ae2))
<a name="0.10.2"></a>
## [0.10.2](https://github.com/libp2p/js-libp2p-crypto/compare/v0.10.1...v0.10.2) (2017-09-06)
### Bug Fixes
* use regular protocol-buffers until protobufjs is fixed ([#109](https://github.com/libp2p/js-libp2p-crypto/issues/109)) ([957fdd3](https://github.com/libp2p/js-libp2p-crypto/commit/957fdd3))
### Features
* **deps:** upgrade to aegir@12 and browserify-aes@1.0.8 ([83257bc](https://github.com/libp2p/js-libp2p-crypto/commit/83257bc))
<a name="0.10.1"></a>
## [0.10.1](https://github.com/libp2p/js-libp2p-crypto/compare/v0.10.0...v0.10.1) (2017-09-05)
### Bug Fixes
* switch to protobufjs ([#107](https://github.com/libp2p/js-libp2p-crypto/issues/107)) ([dc2793f](https://github.com/libp2p/js-libp2p-crypto/commit/dc2793f))
<a name="0.10.0"></a>
# [0.10.0](https://github.com/libp2p/js-libp2p-crypto/compare/v0.9.4...v0.10.0) (2017-09-03)
### Features
* p2p addrs situation ([#106](https://github.com/libp2p/js-libp2p-crypto/issues/106)) ([9e977c7](https://github.com/libp2p/js-libp2p-crypto/commit/9e977c7))
* skip nextTick in nodeify ([#103](https://github.com/libp2p/js-libp2p-crypto/issues/103)) ([f20267b](https://github.com/libp2p/js-libp2p-crypto/commit/f20267b))
<a name="0.9.4"></a>
## [0.9.4](https://github.com/libp2p/js-libp2p-crypto/compare/v0.9.3...v0.9.4) (2017-07-22)
### Bug Fixes
* circular circular dep -> DI ([bc554d1](https://github.com/libp2p/js-libp2p-crypto/commit/bc554d1))
<a name="0.9.3"></a>
## [0.9.3](https://github.com/libp2p/js-libp2p-crypto/compare/v0.9.2...v0.9.3) (2017-07-22)
<a name="0.9.2"></a>
## [0.9.2](https://github.com/libp2p/js-libp2p-crypto/compare/v0.9.1...v0.9.2) (2017-07-22)
<a name="0.9.1"></a>
## [0.9.1](https://github.com/libp2p/js-libp2p-crypto/compare/v0.9.0...v0.9.1) (2017-07-22)
<a name="0.9.0"></a>
# [0.9.0](https://github.com/libp2p/js-libp2p-crypto/compare/v0.8.8...v0.9.0) (2017-07-22)
<a name="0.8.8"></a>
## [0.8.8](https://github.com/libp2p/js-libp2p-crypto/compare/v0.8.7...v0.8.8) (2017-04-11)
### Bug Fixes
* **ecdh:** allow base64 to be left-0-padded, needed for JWK format ([be64372](https://github.com/libp2p/js-libp2p-crypto/commit/be64372)), closes [#97](https://github.com/libp2p/js-libp2p-crypto/issues/97)
<a name="0.8.7"></a>
## [0.8.7](https://github.com/libp2p/js-libp2p-crypto/compare/v0.8.6...v0.8.7) (2017-03-21)
<a name="0.8.6"></a>
## [0.8.6](https://github.com/libp2p/js-libp2p-crypto/compare/v0.8.5...v0.8.6) (2017-03-03)
### Bug Fixes
* **package:** update tweetnacl to version 1.0.0-rc.1 ([4e56e17](https://github.com/libp2p/js-libp2p-crypto/commit/4e56e17))
### Features
* **keys:** implement generateKeyPairFromSeed for ed25519 ([e5b7c1f](https://github.com/libp2p/js-libp2p-crypto/commit/e5b7c1f))

269
README.md
View File

@@ -1,39 +1,201 @@
# JavaScript libp2p Crytpo
# js-libp2p-crypto
[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io)
[![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/)
[![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23ipfs)
[![Coverage Status](https://coveralls.io/repos/github/ipfs/js-libp2p-crypto/badge.svg?branch=master)](https://coveralls.io/github/ipfs/js-libp2p-crypto?branch=master)
[![Travis CI](https://travis-ci.org/ipfs/js-libp2p-crypto.svg?branch=master)](https://travis-ci.org/ipfs/js-libp2p-crypto)
[![Circle CI](https://circleci.com/gh/ipfs/js-libp2p-crypto.svg?style=svg)](https://circleci.com/gh/ipfs/js-libp2p-crypto)
[![Dependency Status](https://david-dm.org/ipfs/js-libp2p-crypto.svg?style=flat-square)](https://david-dm.org/ipfs/js-libp2p-crypto) [![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/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.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-crypto)
[![](https://img.shields.io/travis/libp2p/js-libp2p-crypto.svg?style=flat-square)](https://travis-ci.com/libp2p/js-libp2p-crypto)
[![Dependency Status](https://david-dm.org/libp2p/js-libp2p-crypto.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-crypto)
[![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square)](https://github.com/feross/standard)
> Crypto primitives for libp2p in JavaScript
## Description
This repo contains the JavaScript implementation of the crypto primitives needed for libp2p. This is based on this [go implementation](https://github.com/libp2p/go-libp2p-crypto).
This repo contains the JavaScript implementation of the crypto primitives
needed for libp2p. This is based on this [go implementation](https://github.com/ipfs/go-libp2p-crypto).
## Lead Maintainer
[Jacob Heun](https://github.com/jacobheun/)
## Table of Contents
- [js-libp2p-crypto](#js-libp2p-crypto)
- [Lead Maintainer](#Lead-Maintainer)
- [Table of Contents](#Table-of-Contents)
- [Install](#Install)
- [API](#API)
- [`crypto.aes`](#cryptoaes)
- [`crypto.aes.create(key, iv)`](#cryptoaescreatekey-iv)
- [`decrypt(data)`](#decryptdata)
- [`encrypt(data)`](#encryptdata)
- [`crypto.hmac`](#cryptohmac)
- [`crypto.hmac.create(hash, secret)`](#cryptohmaccreatehash-secret)
- [`digest(data)`](#digestdata)
- [`crypto.keys`](#cryptokeys)
- [`crypto.keys.generateKeyPair(type, bits)`](#cryptokeysgenerateKeyPairtype-bits)
- [`crypto.keys.generateEphemeralKeyPair(curve)`](#cryptokeysgenerateEphemeralKeyPaircurve)
- [`crypto.keys.keyStretcher(cipherType, hashType, secret)`](#cryptokeyskeyStretchercipherType-hashType-secret)
- [`crypto.keys.marshalPublicKey(key, [type])`](#cryptokeysmarshalPublicKeykey-type)
- [`crypto.keys.unmarshalPublicKey(buf)`](#cryptokeysunmarshalPublicKeybuf)
- [`crypto.keys.marshalPrivateKey(key, [type])`](#cryptokeysmarshalPrivateKeykey-type)
- [`crypto.keys.unmarshalPrivateKey(buf)`](#cryptokeysunmarshalPrivateKeybuf)
- [`crypto.keys.import(pem, password)`](#cryptokeysimportpem-password)
- [`crypto.randomBytes(number)`](#cryptorandomBytesnumber)
- [`crypto.pbkdf2(password, salt, iterations, keySize, hash)`](#cryptopbkdf2password-salt-iterations-keySize-hash)
- [Contribute](#Contribute)
- [License](#License)
## Install
```sh
npm install --save libp2p-crypto
```
## Usage
```js
const crypto = require('libp2p-crypto')
// Now available to you:
//
// crypto.aes
// crypto.hmac
// crypto.keys
// etc.
//
// See full API details below...
```
### Web Crypto API
The `libp2p-crypto` library depends on the [Web Crypto API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API) in the browser. Web Crypto is available in all modern browsers, however browsers restrict its usage to [Secure Contexts](https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts).
**This means you will not be able to use some `libp2p-crypto` functions in the browser when the page is served over HTTP.** To enable the Web Crypto API and allow `libp2p-crypto` to work fully, please serve your page over HTTPS.
## API
### `generateKeyPair(type, bits)`
### `crypto.aes`
- `type: String`, only `'RSA'` is currently supported
- `bits: Number`
Exposes an interface to AES encryption (formerly Rijndael), as defined in U.S. Federal Information Processing Standards Publication 197.
This uses `CTR` mode.
#### `crypto.aes.create(key, iv)`
- `key: Buffer` The key, if length `16` then `AES 128` is used. For length `32`, `AES 256` is used.
- `iv: Buffer` Must have length `16`.
Returns `Promise<{decrypt<Function>, encrypt<Function>}>`
##### `decrypt(data)`
- `data: Buffer`
Returns `Promise<Buffer>`
##### `encrypt(data)`
- `data: Buffer`
Returns `Promise<Buffer>`
```js
const crypto = require('libp2p-crypto')
// Setting up Key and IV
// A 16 bytes array, 128 Bits, AES-128 is chosen
const key128 = Buffer.from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15])
// A 16 bytes array, 128 Bits,
const IV = Buffer.from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15])
async function main () {
const decryptedMessage = 'Hello, world!'
// Encrypting
const cipher = await crypto.aes.create(key128, IV)
const encryptedBuffer = await cipher.encrypt(Buffer.from(decryptedMessage))
console.log(encryptedBuffer)
// prints: <Buffer 42 f1 67 d9 2e 42 d0 32 9e b1 f8 3c>
// Decrypting
const decipher = await crypto.aes.create(key128, IV)
const decryptedBuffer = await cipher.decrypt(encryptedBuffer)
console.log(decryptedBuffer)
// prints: <Buffer 42 f1 67 d9 2e 42 d0 32 9e b1 f8 3c>
console.log(decryptedBuffer.toString('utf-8'))
// prints: Hello, world!
}
main()
```
### `crypto.hmac`
Exposes an interface to the Keyed-Hash Message Authentication Code (HMAC) as defined in U.S. Federal Information Processing Standards Publication 198. An HMAC is a cryptographic hash that uses a key to sign a message. The receiver verifies the hash by recomputing it using the same key.
#### `crypto.hmac.create(hash, secret)`
- `hash: String`
- `secret: Buffer`
Returns `Promise<{digest<Function>}>`
##### `digest(data)`
- `data: Buffer`
Returns `Promise<Buffer>`
Example:
```js
const crypto = require('libp2p-crypto')
async function main () {
const hash = 'SHA1' // 'SHA256' || 'SHA512'
const hmac = await crypto.hmac.create(hash, Buffer.from('secret'))
const sig = await hmac.digest(Buffer.from('hello world'))
console.log(sig)
}
main()
```
### `crypto.keys`
**Supported Key Types**
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.
### `crypto.keys.generateKeyPair(type, bits)`
- `type: String`, see [Supported Key Types](#supported-key-types) above.
- `bits: Number` Minimum of 1024
Returns `Promise<{privateKey<Buffer>, publicKey<Buffer>}>`
Generates a keypair of the given type and bitsize.
### `generateEphemeralKeyPair(curve)`
### `crypto.keys.generateEphemeralKeyPair(curve)`
- `curve: String`, one of `'P-256'`, `'P-384'`, `'P-521'` is currently supported
Returns `Promise`
Generates an ephemeral public key and returns a function that will compute the shared secret key.
Focuses only on ECDH now, but can be made more general in the future.
Returns an object of the form
Resolves to an object of the form:
```js
{
key: Buffer,
@@ -41,15 +203,18 @@ Returns an object of the form
}
```
### `keyStretcher(cipherType, hashType, secret)`
### `crypto.keys.keyStretcher(cipherType, hashType, secret)`
- `cipherType: String`, one of `'AES-128'`, `'AES-256'`, `'Blowfish'`
- `hashType: String`, one of `'SHA1'`, `SHA256`, `SHA512`
- `secret: Buffer`
Returns `Promise`
Generates a set of keys for each party by stretching the shared key.
Returns an object of the form
Resolves to an object of the form:
```js
{
k1: {
@@ -64,28 +229,76 @@ Returns an object of the form
}
}
```
### `marshalPublicKey(key[, type])`
- `key: crypto.rsa.RsaPublicKey`
- `type: String`, only `'RSA'` is currently supported
### `crypto.keys.marshalPublicKey(key, [type])`
- `key: keys.rsa.RsaPublicKey | keys.ed25519.Ed25519PublicKey | require('libp2p-crypto-secp256k1').Secp256k1PublicKey`
- `type: String`, see [Supported Key Types](#supported-key-types) above. Defaults to 'rsa'.
Returns `Buffer`
Converts a public key object into a protobuf serialized public key.
### `unmarshalPublicKey(buf)`
### `crypto.keys.unmarshalPublicKey(buf)`
- `buf: Buffer`
Converts a protobuf serialized public key into its representative object.
Returns `RsaPublicKey|Ed25519PublicKey|Secp256k1PublicKey`
### `marshalPrivateKey(key[, type])`
Converts a protobuf serialized public key into its representative object.
- `key: crypto.rsa.RsaPrivateKey`
- `type: String`, only `'RSA'` is currently supported
### `crypto.keys.marshalPrivateKey(key, [type])`
- `key: keys.rsa.RsaPrivateKey | keys.ed25519.Ed25519PrivateKey | require('libp2p-crypto-secp256k1').Secp256k1PrivateKey`
- `type: String`, see [Supported Key Types](#supported-key-types) above.
Returns `Buffer`
Converts a private key object into a protobuf serialized private key.
### `unmarshalPrivateKey(buf)`
### `crypto.keys.unmarshalPrivateKey(buf)`
- `buf: Buffer`
Converts a protobuf serialized private key into its representative object.
Returns `Promise<RsaPrivateKey|Ed25519PrivateKey|Secp256k1PrivateKey>`
Converts a protobuf serialized private key into its representative object.
### `crypto.keys.import(pem, password)`
- `pem: string`
- `password: string`
Returns `Promise<RsaPrivateKey>`
Converts a PEM password protected private key into its representative object.
### `crypto.randomBytes(number)`
- `number: Number`
Returns `Buffer`
Generates a Buffer with length `number` populated by random bytes.
### `crypto.pbkdf2(password, salt, iterations, keySize, hash)`
- `password: String`
- `salt: String`
- `iterations: Number`
- `keySize: Number` in bytes
- `hash: String` the hashing algorithm ('sha1', 'sha2-512', ...)
Computes the Password Based Key Derivation Function 2; returning a new password.
## Contribute
Feel free to join in. All welcome. Open an [issue](https://github.com/libp2p/js-libp2p-crypto/issues)!
This repository falls under the IPFS [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md).
[![](https://cdn.rawgit.com/jbenet/contribute-ipfs-gif/master/img/contribute.gif)](https://github.com/ipfs/community/blob/master/contributing.md)
## License
[MIT](./LICENSE)

View File

@@ -0,0 +1,23 @@
/* eslint-disable no-console */
'use strict'
const Benchmark = require('benchmark')
const crypto = require('../src')
const suite = new Benchmark.Suite('ephemeral-keys')
const secrets = []
const curves = ['P-256', 'P-384', 'P-521']
curves.forEach((curve) => {
suite.add(`ephemeral key with secrect ${curve}`, async (d) => {
const res = await crypto.keys.generateEphemeralKeyPair('P-256')
const secret = await res.genSharedKey(res.key)
secrets.push(secret)
d.resolve()
}, { defer: true })
})
suite
.on('cycle', (event) => console.log(String(event.target)))
.run({ async: true })

View File

@@ -0,0 +1,34 @@
/* eslint-disable no-console */
'use strict'
const Benchmark = require('benchmark')
const crypto = require('../src')
const suite = new Benchmark.Suite('key-stretcher')
const keys = []
const ciphers = ['AES-128', 'AES-256', 'Blowfish']
const hashes = ['SHA1', 'SHA256', 'SHA512']
;(async () => {
const res = await crypto.keys.generateEphemeralKeyPair('P-256')
const secret = await res.genSharedKey(res.key)
ciphers.forEach((cipher) => hashes.forEach((hash) => {
setup(cipher, hash, secret)
}))
suite
.on('cycle', (event) => console.log(String(event.target)))
.run({ async: true })
})()
function setup (cipher, hash, secret) {
suite.add(`keyStretcher ${cipher} ${hash}`, async (d) => {
const k = await crypto.keys.keyStretcher(cipher, hash, secret)
keys.push(k)
d.resolve()
}, { defer: true })
}

37
benchmarks/rsa.js Normal file
View File

@@ -0,0 +1,37 @@
/* eslint-disable no-console */
'use strict'
const Benchmark = require('benchmark')
const crypto = require('../src')
const suite = new Benchmark.Suite('rsa')
const keys = []
const bits = [1024, 2048, 4096]
bits.forEach((bit) => {
suite.add(`generateKeyPair ${bit}bits`, async (d) => {
const key = await crypto.keys.generateKeyPair('RSA', bit)
keys.push(key)
d.resolve()
}, {
defer: true
})
})
suite.add('sign and verify', async (d) => {
const key = keys[0]
const text = key.genSecret()
const sig = await key.sign(text)
const res = await key.public.verify(text, sig)
if (res !== true) { throw new Error('failed to verify') }
d.resolve()
}, {
defer: true
})
suite
.on('cycle', (event) => console.log(String(event.target)))
.run({ async: true })

View File

@@ -1,12 +0,0 @@
machine:
node:
version: stable
dependencies:
pre:
- google-chrome --version
- wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add -
- sudo sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list'
- sudo apt-get update
- sudo apt-get --only-upgrade install google-chrome-stable
- google-chrome --version

View File

@@ -1,20 +1,33 @@
{
"name": "libp2p-crypto",
"version": "0.5.0",
"version": "0.17.3",
"description": "Crypto primitives for libp2p",
"main": "lib/index.js",
"jsnext:main": "src/index.js",
"main": "src/index.js",
"types": "src/index.d.ts",
"leadMaintainer": "Jacob Heun <jacobheun@gmail.com>",
"browser": {
"./src/hmac/index.js": "./src/hmac/index-browser.js",
"./src/keys/ecdh.js": "./src/keys/ecdh-browser.js",
"./src/aes/ciphers.js": "./src/aes/ciphers-browser.js",
"./src/keys/rsa.js": "./src/keys/rsa-browser.js"
},
"files": [
"src",
"dist"
],
"scripts": {
"lint": "aegir-lint",
"build": "aegir-build",
"test": "aegir-test",
"test:node": "aegir-test --env node",
"test:browser": "aegir-test --env browser",
"release": "aegir-release",
"release-minor": "aegir-release --type minor",
"release-major": "aegir-release --type major",
"coverage": "aegir-coverage",
"coverage-publish": "aegir-coverage publish"
"lint": "aegir lint",
"build": "aegir build",
"build-proto": "pbjs --wrap commonjs --target static-module src/keys/keys.proto > src/keys/keys.proto.js",
"test": "aegir test",
"test:node": "aegir test -t node",
"test:browser": "aegir test -t browser -t webworker",
"release": "aegir release",
"release-minor": "aegir release --type minor",
"release-major": "aegir release --type major",
"coverage": "aegir coverage --ignore src/keys/keys.proto.js",
"size": "aegir build --bundlesize",
"test:types": "npx tsc"
},
"keywords": [
"IPFS",
@@ -22,35 +35,70 @@
"crypto",
"rsa"
],
"author": "Friedel Ziegelmayer <dignifiedqurie@gmail.com>",
"license": "MIT",
"dependencies": {
"elliptic": "^6.2.3",
"multihashing": "^0.2.1",
"node-forge": "^0.6.39",
"protocol-buffers": "^3.1.6"
"asmcrypto.js": "^2.3.2",
"bn.js": "^5.0.0",
"browserify-aes": "^1.2.0",
"bs58": "^4.0.1",
"err-code": "^2.0.0",
"iso-random-stream": "^1.1.0",
"keypair": "^1.0.1",
"libp2p-crypto-secp256k1": "~0.4.0",
"multihashing-async": "~0.8.0",
"node-forge": "~0.9.1",
"pem-jwk": "^2.0.0",
"protons": "^1.0.1",
"rsa-pem-to-jwk": "^1.1.3",
"tweetnacl": "^1.0.1",
"ursa-optional": "~0.10.1"
},
"devDependencies": {
"aegir": "^3.0.4",
"chai": "^3.5.0",
"pre-commit": "^1.1.3"
"@types/bn.js": "^4.11.6",
"@types/chai": "^4.2.7",
"@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",
"benchmark": "^2.1.4",
"chai": "^4.2.0",
"chai-string": "^1.5.0",
"dirty-chai": "^2.0.1",
"sinon": "^9.0.0"
},
"pre-commit": [
"lint",
"test"
],
"engines": {
"node": "^4.0.0"
"node": ">=10.0.0",
"npm": ">=6.0.0"
},
"repository": {
"type": "git",
"url": "https://github.com/ipfs/js-libp2p-crypto.git"
"url": "https://github.com/libp2p/js-libp2p-crypto.git"
},
"bugs": {
"url": "https://github.com/ipfs/js-libp2p-crypto/issues"
"url": "https://github.com/libp2p/js-libp2p-crypto/issues"
},
"homepage": "https://github.com/ipfs/js-libp2p-crypto",
"homepage": "https://github.com/libp2p/js-libp2p-crypto",
"contributors": [
"dignifiedquire <dignifiedquire@gmail.com>"
"David Dias <daviddias.p@gmail.com>",
"Friedel Ziegelmayer <dignifiedquire@gmail.com>",
"Maciej Krüger <mkg20001@gmail.com>",
"Jacob Heun <jacobheun@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>",
"Jack Kleeman <jackkleeman@gmail.com>",
"Richard Schneider <makaretu@gmail.com>",
"Vasco Santos <vasco.santos@ua.pt>",
"dirkmc <dirkmdev@gmail.com>",
"nikuda <nikuda@gmail.com>",
"Victor Bjelkholm <victorbjelkholm@gmail.com>",
"Hugo Dias <hugomrdias@gmail.com>",
"Carson Farmer <carson.farmer@gmail.com>",
"Alberto Elias <hi@albertoelias.me>",
"Tom Swindell <t.swindell@rubyx.co.uk>",
"Joao Santos <jrmsantos15@gmail.com>"
]
}

17
src/aes/cipher-mode.js Normal file
View File

@@ -0,0 +1,17 @@
'use strict'
const errcode = require('err-code')
const CIPHER_MODES = {
16: 'aes-128-ctr',
32: 'aes-256-ctr'
}
module.exports = function (key) {
const mode = CIPHER_MODES[key.length]
if (!mode) {
const modes = Object.entries(CIPHER_MODES).map(([k, v]) => `${k} (${v})`).join(' / ')
throw errcode(new Error(`Invalid key length ${key.length} bytes. Must be ${modes}`), 'ERR_INVALID_KEY_LENGTH')
}
return mode
}

View File

@@ -0,0 +1,8 @@
'use strict'
const crypto = require('browserify-aes')
module.exports = {
createCipheriv: crypto.createCipheriv,
createDecipheriv: crypto.createDecipheriv
}

8
src/aes/ciphers.js Normal file
View File

@@ -0,0 +1,8 @@
'use strict'
const crypto = require('crypto')
module.exports = {
createCipheriv: crypto.createCipheriv,
createDecipheriv: crypto.createDecipheriv
}

34
src/aes/index-browser.js Normal file
View File

@@ -0,0 +1,34 @@
'use strict'
const asm = require('asmcrypto.js')
const validateCipherMode = require('./cipher-mode')
exports.create = async function (key, iv) { // eslint-disable-line require-await
// Throws an error if mode is invalid
validateCipherMode(key)
const enc = new asm.AES_CTR.Encrypt({
key: key,
nonce: iv
})
const dec = new asm.AES_CTR.Decrypt({
key: key,
nonce: iv
})
const res = {
async encrypt (data) { // eslint-disable-line require-await
return Buffer.from(
enc.process(data).result
)
},
async decrypt (data) { // eslint-disable-line require-await
return Buffer.from(
dec.process(data).result
)
}
}
return res
}

22
src/aes/index.js Normal file
View File

@@ -0,0 +1,22 @@
'use strict'
const ciphers = require('./ciphers')
const cipherMode = require('./cipher-mode')
exports.create = async function (key, iv) { // eslint-disable-line require-await
const mode = cipherMode(key)
const cipher = ciphers.createCipheriv(mode, key, iv)
const decipher = ciphers.createDecipheriv(mode, key, iv)
const res = {
async encrypt (data) { // eslint-disable-line require-await
return cipher.update(data)
},
async decrypt (data) { // eslint-disable-line require-await
return decipher.update(data)
}
}
return res
}

View File

@@ -1,13 +0,0 @@
enum KeyType {
RSA = 0;
}
message PublicKey {
required KeyType Type = 1;
required bytes Data = 2;
}
message PrivateKey {
required KeyType Type = 1;
required bytes Data = 2;
}

View File

@@ -1,36 +0,0 @@
'use strict'
const EC = require('elliptic').ec
const curveMap = {
'P-256': 'p256',
'P-384': 'p384',
'P-521': 'p521'
}
// Generates an ephemeral public key and returns a function that will compute
// the shared secret key.
//
// Focuses only on ECDH now, but can be made more general in the future.
module.exports = (curveName) => {
const curve = curveMap[curveName]
if (!curve) {
throw new Error('unsupported curve passed')
}
const ec = new EC(curve)
const priv = ec.genKeyPair()
// forcePrivate is used for testing only
const genSharedKey = (theirPub, forcePrivate) => {
const pub = ec.keyFromPublic(theirPub, 'hex')
const p = forcePrivate || priv
return p.derive(pub.getPublic()).toBuffer('le')
}
return {
key: new Buffer(priv.getPublic('hex'), 'hex'),
genSharedKey
}
}

36
src/hmac/index-browser.js Normal file
View File

@@ -0,0 +1,36 @@
'use strict'
const webcrypto = require('../webcrypto')
const lengths = require('./lengths')
const hashTypes = {
SHA1: 'SHA-1',
SHA256: 'SHA-256',
SHA512: 'SHA-512'
}
const sign = async (key, data) => {
return Buffer.from(await webcrypto.get().subtle.sign({ name: 'HMAC' }, key, data))
}
exports.create = async function (hashType, secret) {
const hash = hashTypes[hashType]
const key = await webcrypto.get().subtle.importKey(
'raw',
secret,
{
name: 'HMAC',
hash: { name: hash }
},
false,
['sign']
)
return {
async digest (data) { // eslint-disable-line require-await
return sign(key, data)
},
length: lengths[hashType]
}
}

17
src/hmac/index.js Normal file
View File

@@ -0,0 +1,17 @@
'use strict'
const crypto = require('crypto')
const lengths = require('./lengths')
exports.create = async function (hash, secret) { // eslint-disable-line require-await
const res = {
async digest (data) { // eslint-disable-line require-await
const hmac = crypto.createHmac(hash.toLowerCase(), secret)
hmac.update(data)
return hmac.digest()
},
length: lengths[hash]
}
return res
}

7
src/hmac/lengths.js Normal file
View File

@@ -0,0 +1,7 @@
'use strict'
module.exports = {
SHA1: 20,
SHA256: 32,
SHA512: 64
}

345
src/index.d.ts vendored Normal file
View File

@@ -0,0 +1,345 @@
/// <reference types="node" />
/**
* Supported key types.
*/
export type KeyType = "Ed25519" | "RSA" | "secp256k1";
/**
* Maps an IPFS hash name to its node-forge equivalent.
* See https://github.com/multiformats/multihash/blob/master/hashtable.csv
*/
export type HashType = "SHA1" | "SHA256" | "SHA512";
/**
* Supported curve types.
*/
export type CurveType = "P-256" | "P-384" | "P-521";
/**
* Supported cipher types.
*/
export type CipherType = "AES-128" | "AES-256" | "Blowfish";
/**
* Exposes an interface to AES encryption (formerly Rijndael),
* as defined in U.S. Federal Information Processing Standards Publication 197.
* This uses CTR mode.
*/
export namespace aes {
/**
* AES Cipher in CTR mode.
*/
interface Cipher {
encrypt(data: Buffer): Promise<Buffer>;
decrypt(data: Buffer): Promise<Buffer>;
}
/**
* Create a new AES Cipher.
* @param key The key, if length 16 then AES 128 is used. For length 32, AES 256 is used.
* @param iv Must have length 16.
*/
function create(key: Buffer, iv: Buffer): Promise<Cipher>;
}
/**
* Exposes an interface to the Keyed-Hash Message Authentication Code (HMAC)
* as defined in U.S. Federal Information Processing Standards Publication 198.
* An HMAC is a cryptographic hash that uses a key to sign a message.
* The receiver verifies the hash by recomputing it using the same key.
*/
export namespace hmac {
/**
* HMAC Digest.
*/
interface Digest {
digest(data: Buffer): Promise<Buffer>;
length: 20 | 32 | 64 | number;
}
/**
* Create a new HMAC Digest.
*/
function create(
hash: "SHA1" | "SHA256" | "SHA512" | string,
secret: Buffer
): Promise<Digest>;
}
/**
* Generic public key interface.
*/
export interface PublicKey {
readonly bytes: Buffer;
verify(data: Buffer, sig: Buffer): Promise<boolean>;
marshal(): Buffer;
equals(key: PublicKey): boolean;
hash(): Promise<Buffer>;
}
/**
* Generic private key interface.
*/
export interface PrivateKey {
readonly public: PublicKey;
readonly bytes: Buffer;
sign(data: Buffer): Promise<Buffer>;
marshal(): Buffer;
equals(key: PrivateKey): boolean;
hash(): Promise<Buffer>;
/**
* 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.
*/
id(): Promise<string>;
}
export interface Keystretcher {
(res: Buffer): Keystretcher;
iv: Buffer;
cipherKey: Buffer;
macKey: Buffer;
}
export interface StretchPair {
k1: Keystretcher;
k2: Keystretcher;
}
/**
* Exposes an interface to various cryptographic key generation routines.
* 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 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.
*/
export namespace keys {
export {};
export namespace supportedKeys {
namespace rsa {
class RsaPublicKey implements PublicKey {
constructor(key: Buffer);
readonly bytes: Buffer;
verify(data: Buffer, sig: Buffer): Promise<boolean>;
marshal(): Buffer;
encrypt(bytes: Buffer): Buffer;
equals(key: PublicKey): boolean;
hash(): Promise<Buffer>;
}
// Type alias for export method
export type KeyInfo = any;
class RsaPrivateKey implements PrivateKey {
constructor(key: any, publicKey: Buffer);
readonly public: RsaPublicKey;
readonly bytes: Buffer;
genSecret(): Buffer;
sign(data: Buffer): Promise<Buffer>;
decrypt(bytes: Buffer): Buffer;
marshal(): Buffer;
equals(key: PrivateKey): boolean;
hash(): Promise<Buffer>;
id(): Promise<string>;
/**
* Exports the key into a password protected PEM format
*
* @param password The password to read the encrypted PEM
* @param format Defaults to 'pkcs-8'.
*/
export(password: string, format?: "pkcs-8" | string): KeyInfo;
}
function unmarshalRsaPublicKey(buf: Buffer): RsaPublicKey;
function unmarshalRsaPrivateKey(buf: Buffer): Promise<RsaPrivateKey>;
function generateKeyPair(bits: number): Promise<RsaPrivateKey>;
function fromJwk(jwk: Buffer): Promise<RsaPrivateKey>;
}
namespace ed25519 {
class Ed25519PublicKey implements PublicKey {
constructor(key: Buffer);
readonly bytes: Buffer;
verify(data: Buffer, sig: Buffer): Promise<boolean>;
marshal(): Buffer;
encrypt(bytes: Buffer): Buffer;
equals(key: PublicKey): boolean;
hash(): Promise<Buffer>;
}
class Ed25519PrivateKey implements PrivateKey {
constructor(key: Buffer, publicKey: Buffer);
readonly public: Ed25519PublicKey;
readonly bytes: Buffer;
sign(data: Buffer): Promise<Buffer>;
marshal(): Buffer;
equals(key: PrivateKey): boolean;
hash(): Promise<Buffer>;
id(): Promise<string>;
}
function unmarshalEd25519PrivateKey(
buf: Buffer
): Promise<Ed25519PrivateKey>;
function unmarshalEd25519PublicKey(buf: Buffer): Ed25519PublicKey;
function generateKeyPair(): Promise<Ed25519PrivateKey>;
function generateKeyPairFromSeed(
seed: Buffer
): Promise<Ed25519PrivateKey>;
}
namespace secp256k1 {
class Secp256k1PublicKey implements PublicKey {
constructor(key: Buffer);
readonly bytes: Buffer;
verify(data: Buffer, sig: Buffer): Promise<boolean>;
marshal(): Buffer;
encrypt(bytes: Buffer): Buffer;
equals(key: PublicKey): boolean;
hash(): Promise<Buffer>;
}
class Secp256k1PrivateKey implements PrivateKey {
constructor(key: Uint8Array | Buffer, publicKey: Uint8Array | Buffer);
readonly public: Secp256k1PublicKey;
readonly bytes: Buffer;
sign(data: Buffer): Promise<Buffer>;
marshal(): Buffer;
equals(key: PrivateKey): boolean;
hash(): Promise<Buffer>;
id(): Promise<string>;
}
function unmarshalSecp256k1PrivateKey(
bytes: Buffer
): Promise<Secp256k1PrivateKey>;
function unmarshalSecp256k1PublicKey(bytes: Buffer): Secp256k1PublicKey;
function generateKeyPair(): Promise<Secp256k1PrivateKey>;
}
}
export const keysPBM: any;
/**
* Generates a keypair of the given type and bitsize.
* @param type One of the supported key types.
* @param bits Number of bits. Minimum of 1024.
*/
export function generateKeyPair(
type: KeyType | string,
bits: number
): Promise<PrivateKey>;
export function generateKeyPair(
type: "Ed25519",
bits: number
): Promise<keys.supportedKeys.ed25519.Ed25519PrivateKey>;
export function generateKeyPair(
type: "RSA",
bits: number
): Promise<keys.supportedKeys.rsa.RsaPrivateKey>;
export function generateKeyPair(
type: "secp256k1",
bits: number
): Promise<keys.supportedKeys.secp256k1.Secp256k1PrivateKey>;
/**
* Generates a keypair of the given type and bitsize.
* @param type One of the supported key types. Currently only 'Ed25519' is supported.
* @param seed A 32 byte uint8array.
* @param bits Number of bits. Minimum of 1024.
*/
export function generateKeyPairFromSeed(
type: KeyType | string,
seed: Uint8Array,
bits: number
): Promise<PrivateKey>;
export function generateKeyPairFromSeed(
type: "Ed25519",
seed: Uint8Array,
bits: number
): Promise<keys.supportedKeys.ed25519.Ed25519PrivateKey>;
/**
* Generates an ephemeral public key and returns a function that will compute the shared secret key.
* Focuses only on ECDH now, but can be made more general in the future.
* @param curve The curve to use. One of 'P-256', 'P-384', 'P-521' is currently supported.
*/
export function generateEphemeralKeyPair(
curve: CurveType | string
): Promise<{
key: Buffer;
genSharedKey: (theirPub: Buffer, forcePrivate?: any) => Promise<Buffer>;
}>;
/**
* Generates a set of keys for each party by stretching the shared key.
* @param cipherType The cipher type to use. One of 'AES-128', 'AES-256', or 'Blowfish'
* @param hashType The hash type to use. One of 'SHA1', 'SHA2256', or 'SHA2512'.
* @param secret The shared key secret.
*/
export function keyStretcher(
cipherType: CipherType | string,
hashType: HashType | string,
secret: Buffer | string
): Promise<StretchPair>;
/**
* Converts a protobuf serialized public key into its representative object.
* @param buf The protobuf serialized public key.
*/
export function unmarshalPublicKey(buf: Buffer): PublicKey;
/**
* Converts a public key object into a protobuf serialized public key.
* @param key An RSA, Ed25519, or Secp256k1 public key object.
* @param type One of the supported key types.
*/
export function marshalPublicKey(key: PublicKey, type?: KeyType | string): Buffer;
/**
* Converts a protobuf serialized private key into its representative object.
* @param buf The protobuf serialized private key.
*/
export function unmarshalPrivateKey(buf: Buffer): Promise<PrivateKey>;
/**
* Converts a private key object into a protobuf serialized private key.
* @param key An RSA, Ed25519, or Secp256k1 private key object.
* @param type One of the supported key types.
*/
export function marshalPrivateKey(key: PrivateKey, type?: KeyType | string): Buffer;
/**
* Converts a PEM password protected private key into its representative object.
* @param pem Password protected private key in PEM format.
* @param password The password used to protect the key.
*/
function _import(pem: string, password: string): Promise<supportedKeys.rsa.RsaPrivateKey>;
export { _import as import };
}
/**
* Generates a Buffer populated by random bytes.
* @param The size of the random bytes Buffer.
*/
export function randomBytes(number: number): Buffer;
/**
* Computes the Password-Based Key Derivation Function 2.
* @param password The password.
* @param salt The salt.
* @param iterations Number of iterations to use.
* @param keySize The size of the output key in bytes.
* @param hash The hash name ('sha1', 'sha2-512, ...)
*/
export function pbkdf2(
password: string | Buffer,
salt: string | Buffer,
iterations: number,
keySize: number,
hash: string
): Buffer;

View File

@@ -1,78 +1,11 @@
'use strict'
const protobuf = require('protocol-buffers')
const fs = require('fs')
const path = require('path')
const pbm = protobuf(fs.readFileSync(path.join(__dirname, './crypto.proto')))
const hmac = require('./hmac')
const aes = require('./aes')
const keys = require('./keys')
exports.utils = require('./utils')
const keys = exports.keys = require('./keys')
exports.keyStretcher = require('./key-stretcher')
exports.generateEphemeralKeyPair = require('./ephemeral-keys')
// Generates a keypair of the given type and bitsize
exports.generateKeyPair = (type, bits) => {
let key = keys[type.toLowerCase()]
if (!key) {
throw new Error('invalid or unsupported key type')
}
return key.generateKeyPair(bits)
}
// Converts a protobuf serialized public key into its
// representative object
exports.unmarshalPublicKey = (buf) => {
const decoded = pbm.PublicKey.decode(buf)
switch (decoded.Type) {
case pbm.KeyType.RSA:
return keys.rsa.unmarshalRsaPublicKey(decoded.Data)
default:
throw new Error('invalid or unsupported key type')
}
}
// Converts a public key object into a protobuf serialized public key
exports.marshalPublicKey = (key, type) => {
type = (type || 'rsa').toLowerCase()
// for now only rsa is supported
if (type !== 'rsa') {
throw new Error('invalid or unsupported key type')
}
return pbm.PublicKey.encode({
Type: pbm.KeyType.RSA,
Data: key.marshal()
})
}
// Converts a protobuf serialized private key into its
// representative object
exports.unmarshalPrivateKey = (buf) => {
const decoded = pbm.PrivateKey.decode(buf)
switch (decoded.Type) {
case pbm.KeyType.RSA:
return keys.rsa.unmarshalRsaPrivateKey(decoded.Data)
default:
throw new Error('invalid or unsupported key type')
}
}
// Converts a private key object into a protobuf serialized private key
exports.marshalPrivateKey = (key, type) => {
type = (type || 'rsa').toLowerCase()
// for now only rsa is supported
if (type !== 'rsa') {
throw new Error('invalid or unsupported key type')
}
return pbm.PrivateKey.encode({
Type: pbm.KeyType.RSA,
Data: key.marshal()
})
}
exports.aes = aes
exports.hmac = hmac
exports.keys = keys
exports.randomBytes = require('./random-bytes')
exports.pbkdf2 = require('./pbkdf2')

View File

@@ -1,95 +0,0 @@
'use strict'
const forge = require('node-forge')
const createBuffer = forge.util.createBuffer
const cipherMap = {
'AES-128': {
ivSize: 16,
keySize: 16
},
'AES-256': {
ivSize: 16,
keySize: 32
},
Blowfish: {
ivSize: 8,
cipherKeySize: 32
}
}
const hashMap = {
SHA1: 'sha1',
SHA256: 'sha256',
// workaround for https://github.com/digitalbazaar/forge/issues/401
SHA512: forge.md.sha512.create()
}
// Generates a set of keys for each party by stretching the shared key.
// (myIV, theirIV, myCipherKey, theirCipherKey, myMACKey, theirMACKey)
module.exports = (cipherType, hashType, secret) => {
const cipher = cipherMap[cipherType]
const hash = hashMap[hashType]
if (!cipher) {
throw new Error('unkown cipherType passed')
}
if (!hash) {
throw new Error('unkown hashType passed')
}
if (Buffer.isBuffer(secret)) {
secret = createBuffer(secret.toString('binary'))
}
const cipherKeySize = cipher.keySize
const ivSize = cipher.ivSize
const hmacKeySize = 20
const seed = 'key expansion'
const resultLength = 2 * (ivSize + cipherKeySize + hmacKeySize)
const m = forge.hmac.create()
m.start(hash, secret)
m.update(seed)
let a = m.digest().bytes()
const result = createBuffer()
let j = 0
for (; j < resultLength;) {
m.start(hash, secret)
m.update(a)
m.update(seed)
const b = createBuffer(m.digest(), 'raw')
let todo = b.length()
if (j + todo > resultLength) {
todo = resultLength - j
}
result.putBytes(b.getBytes(todo))
j += todo
m.start(hash, secret)
m.update(a)
a = m.digest().bytes()
}
const half = resultLength / 2
const r1 = createBuffer(result.getBytes(half))
const r2 = createBuffer(result.getBytes())
const createKey = (res) => ({
iv: new Buffer(res.getBytes(ivSize), 'binary'),
cipherKey: new Buffer(res.getBytes(cipherKeySize), 'binary'),
macKey: new Buffer(res.getBytes(), 'binary')
})
return {
k1: createKey(r1),
k2: createKey(r2)
}
}

117
src/keys/ecdh-browser.js Normal file
View File

@@ -0,0 +1,117 @@
'use strict'
const errcode = require('err-code')
const { Buffer } = require('buffer')
const webcrypto = require('../webcrypto')
const { bufferToBase64url, base64urlToBuffer } = require('../util')
const validateCurveType = require('./validate-curve-type')
const bits = {
'P-256': 256,
'P-384': 384,
'P-521': 521
}
exports.generateEphmeralKeyPair = async function (curve) {
validateCurveType(Object.keys(bits), curve)
const pair = await webcrypto.get().subtle.generateKey(
{
name: 'ECDH',
namedCurve: curve
},
true,
['deriveBits']
)
// forcePrivate is used for testing only
const genSharedKey = async (theirPub, forcePrivate) => {
let privateKey
if (forcePrivate) {
privateKey = await webcrypto.get().subtle.importKey(
'jwk',
unmarshalPrivateKey(curve, forcePrivate),
{
name: 'ECDH',
namedCurve: curve
},
false,
['deriveBits']
)
} else {
privateKey = pair.privateKey
}
const keys = [
await webcrypto.get().subtle.importKey(
'jwk',
unmarshalPublicKey(curve, theirPub),
{
name: 'ECDH',
namedCurve: curve
},
false,
[]
),
privateKey
]
return Buffer.from(await webcrypto.get().subtle.deriveBits(
{
name: 'ECDH',
namedCurve: curve,
public: keys[0]
},
keys[1],
bits[curve]
))
}
const publicKey = await webcrypto.get().subtle.exportKey('jwk', pair.publicKey)
return {
key: marshalPublicKey(publicKey),
genSharedKey
}
}
const curveLengths = {
'P-256': 32,
'P-384': 48,
'P-521': 66
}
// Marshal converts a jwk encodec ECDH public key into the
// form specified in section 4.3.6 of ANSI X9.62. (This is the format
// go-ipfs uses)
function marshalPublicKey (jwk) {
const byteLen = curveLengths[jwk.crv]
return Buffer.concat([
Buffer.from([4]), // uncompressed point
base64urlToBuffer(jwk.x, byteLen),
base64urlToBuffer(jwk.y, byteLen)
], 1 + byteLen * 2)
}
// Unmarshal converts a point, serialized by Marshal, into an jwk encoded key
function unmarshalPublicKey (curve, key) {
const byteLen = curveLengths[curve]
if (!key.slice(0, 1).equals(Buffer.from([4]))) {
throw errcode(new Error('Cannot unmarshal public key - invalid key format'), 'ERR_INVALID_KEY_FORMAT')
}
return {
kty: 'EC',
crv: curve,
x: bufferToBase64url(key.slice(1, byteLen + 1), byteLen),
y: bufferToBase64url(key.slice(1 + byteLen), byteLen),
ext: true
}
}
const unmarshalPrivateKey = (curve, key) => ({
...unmarshalPublicKey(curve, key.public),
d: bufferToBase64url(key.private)
})

28
src/keys/ecdh.js Normal file
View File

@@ -0,0 +1,28 @@
'use strict'
const crypto = require('crypto')
const validateCurveType = require('./validate-curve-type')
const curves = {
'P-256': 'prime256v1',
'P-384': 'secp384r1',
'P-521': 'secp521r1'
}
exports.generateEphmeralKeyPair = async function (curve) { // eslint-disable-line require-await
validateCurveType(Object.keys(curves), curve)
const ecdh = crypto.createECDH(curves[curve])
ecdh.generateKeys()
return {
key: ecdh.getPublicKey(),
async genSharedKey (theirPub, forcePrivate) { // eslint-disable-line require-await
if (forcePrivate) {
ecdh.setPrivateKey(forcePrivate.private)
}
return ecdh.computeSecret(theirPub)
}
}
}

129
src/keys/ed25519-class.js Normal file
View File

@@ -0,0 +1,129 @@
'use strict'
const multihashing = require('multihashing-async')
const protobuf = require('protons')
const bs58 = require('bs58')
const errcode = require('err-code')
const crypto = require('./ed25519')
const pbm = protobuf(require('./keys.proto'))
class Ed25519PublicKey {
constructor (key) {
this._key = ensureKey(key, crypto.publicKeyLength)
}
async verify (data, sig) { // eslint-disable-line require-await
return crypto.hashAndVerify(this._key, sig, data)
}
marshal () {
return Buffer.from(this._key)
}
get bytes () {
return pbm.PublicKey.encode({
Type: pbm.KeyType.Ed25519,
Data: this.marshal()
})
}
equals (key) {
return this.bytes.equals(key.bytes)
}
async hash () { // eslint-disable-line require-await
return multihashing(this.bytes, 'sha2-256')
}
}
class Ed25519PrivateKey {
// key - 64 byte Uint8Array or Buffer containing private key
// publicKey - 32 byte Uint8Array or Buffer containing public key
constructor (key, publicKey) {
this._key = ensureKey(key, crypto.privateKeyLength)
this._publicKey = ensureKey(publicKey, crypto.publicKeyLength)
}
async sign (message) { // eslint-disable-line require-await
return crypto.hashAndSign(this._key, message)
}
get public () {
return new Ed25519PublicKey(this._publicKey)
}
marshal () {
return Buffer.concat([Buffer.from(this._key), Buffer.from(this._publicKey)])
}
get bytes () {
return pbm.PrivateKey.encode({
Type: pbm.KeyType.Ed25519,
Data: this.marshal()
})
}
equals (key) {
return this.bytes.equals(key.bytes)
}
async hash () { // eslint-disable-line require-await
return 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 bs58.encode(hash)
}
}
function unmarshalEd25519PrivateKey (bytes) {
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)
}
function unmarshalEd25519PublicKey (bytes) {
bytes = ensureKey(bytes, crypto.publicKeyLength)
return new Ed25519PublicKey(bytes)
}
async function generateKeyPair () {
const { secretKey, publicKey } = await crypto.generateKey()
return new Ed25519PrivateKey(secretKey, publicKey)
}
async function generateKeyPairFromSeed (seed) {
const { secretKey, publicKey } = await crypto.generateKeyFromSeed(seed)
return new Ed25519PrivateKey(secretKey, publicKey)
}
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')
}
return key
}
module.exports = {
Ed25519PublicKey,
Ed25519PrivateKey,
unmarshalEd25519PrivateKey,
unmarshalEd25519PublicKey,
generateKeyPair,
generateKeyPairFromSeed
}

23
src/keys/ed25519.js Normal file
View File

@@ -0,0 +1,23 @@
'use strict'
const nacl = require('tweetnacl')
exports.publicKeyLength = nacl.sign.publicKeyLength
exports.privateKeyLength = nacl.sign.secretKeyLength
exports.generateKey = async function () { // eslint-disable-line require-await
return nacl.sign.keyPair()
}
// seed should be a 32 byte uint8array
exports.generateKeyFromSeed = async function (seed) { // eslint-disable-line require-await
return nacl.sign.keyPair.fromSeed(seed)
}
exports.hashAndSign = async function (key, msg) { // eslint-disable-line require-await
return Buffer.from(nacl.sign.detached(msg, key))
}
exports.hashAndVerify = async function (key, sig, msg) { // eslint-disable-line require-await
return nacl.sign.detached.verify(msg, sig, key)
}

View File

@@ -0,0 +1,9 @@
'use strict'
const ecdh = require('./ecdh')
// Generates an ephemeral public key and returns a function that will compute
// the shared secret key.
//
// Focuses only on ECDH now, but can be made more general in the future.
module.exports = async (curve) => ecdh.generateEphmeralKeyPair(curve) // eslint-disable-line require-await

View File

@@ -1,5 +1,120 @@
'use strict'
module.exports = {
rsa: require('./rsa')
const protobuf = require('protons')
const keysPBM = protobuf(require('./keys.proto'))
require('node-forge/lib/asn1')
require('node-forge/lib/rsa')
require('node-forge/lib/pbe')
const forge = require('node-forge/lib/forge')
const errcode = require('err-code')
exports = module.exports
const supportedKeys = {
rsa: require('./rsa-class'),
ed25519: require('./ed25519-class'),
secp256k1: require('libp2p-crypto-secp256k1')(keysPBM, require('../random-bytes'))
}
exports.supportedKeys = supportedKeys
exports.keysPBM = keysPBM
const ErrMissingSecp256K1 = {
message: 'secp256k1 support requires libp2p-crypto-secp256k1 package',
code: 'ERR_MISSING_PACKAGE'
}
function typeToKey (type) {
const key = supportedKeys[type.toLowerCase()]
if (!key) {
const supported = Object.keys(supportedKeys).join(' / ')
throw errcode(new Error(`invalid or unsupported key type ${type}. Must be ${supported}`), 'ERR_UNSUPPORTED_KEY_TYPE')
}
return key
}
exports.keyStretcher = require('./key-stretcher')
exports.generateEphemeralKeyPair = require('./ephemeral-keys')
// Generates a keypair of the given type and bitsize
exports.generateKeyPair = async (type, bits) => { // eslint-disable-line require-await
return typeToKey(type).generateKeyPair(bits)
}
// Generates a keypair of the given type and bitsize
// seed is a 32 byte uint8array
exports.generateKeyPairFromSeed = async (type, seed, bits) => { // eslint-disable-line require-await
const key = typeToKey(type)
if (type.toLowerCase() !== 'ed25519') {
throw errcode(new Error('Seed key derivation is unimplemented for RSA or secp256k1'), 'ERR_UNSUPPORTED_KEY_DERIVATION_TYPE')
}
return key.generateKeyPairFromSeed(seed, bits)
}
// Converts a protobuf serialized public key into its
// representative object
exports.unmarshalPublicKey = (buf) => {
const decoded = keysPBM.PublicKey.decode(buf)
const data = decoded.Data
switch (decoded.Type) {
case keysPBM.KeyType.RSA:
return supportedKeys.rsa.unmarshalRsaPublicKey(data)
case keysPBM.KeyType.Ed25519:
return supportedKeys.ed25519.unmarshalEd25519PublicKey(data)
case keysPBM.KeyType.Secp256k1:
if (supportedKeys.secp256k1) {
return supportedKeys.secp256k1.unmarshalSecp256k1PublicKey(data)
} else {
throw errcode(new Error(ErrMissingSecp256K1.message), ErrMissingSecp256K1.code)
}
default:
typeToKey(decoded.Type) // throws because type is not supported
}
}
// Converts a public key object into a protobuf serialized public key
exports.marshalPublicKey = (key, type) => {
type = (type || 'rsa').toLowerCase()
typeToKey(type) // check type
return key.bytes
}
// Converts a protobuf serialized private key into its
// representative object
exports.unmarshalPrivateKey = async (buf) => { // eslint-disable-line require-await
const decoded = keysPBM.PrivateKey.decode(buf)
const data = decoded.Data
switch (decoded.Type) {
case keysPBM.KeyType.RSA:
return supportedKeys.rsa.unmarshalRsaPrivateKey(data)
case keysPBM.KeyType.Ed25519:
return supportedKeys.ed25519.unmarshalEd25519PrivateKey(data)
case keysPBM.KeyType.Secp256k1:
if (supportedKeys.secp256k1) {
return supportedKeys.secp256k1.unmarshalSecp256k1PrivateKey(data)
} else {
throw errcode(new Error(ErrMissingSecp256K1.message), ErrMissingSecp256K1.code)
}
default:
typeToKey(decoded.Type) // throws because type is not supported
}
}
// Converts a private key object into a protobuf serialized private key
exports.marshalPrivateKey = (key, type) => {
type = (type || 'rsa').toLowerCase()
typeToKey(type) // check type
return key.bytes
}
exports.import = async (pem, password) => { // eslint-disable-line require-await
const key = forge.pki.decryptRsaPrivateKey(pem, password)
if (key === null) {
throw errcode(new Error('Cannot read the key, most likely the password is wrong or not a RSA key'), 'ERR_CANNOT_DECRYPT_PEM')
}
let der = forge.asn1.toDer(forge.pki.privateKeyToAsn1(key))
der = Buffer.from(der.getBytes(), 'binary')
return supportedKeys.rsa.unmarshalRsaPrivateKey(der)
}

22
src/keys/jwk2pem.js Normal file
View File

@@ -0,0 +1,22 @@
'use strict'
require('node-forge/lib/rsa')
const forge = require('node-forge/lib/forge')
const { base64urlToBigInteger } = require('../util')
function convert (key, types) {
return types.map(t => base64urlToBigInteger(key[t]))
}
function jwk2priv (key) {
return forge.pki.setRsaPrivateKey(...convert(key, ['n', 'e', 'd', 'p', 'q', 'dp', 'dq', 'qi']))
}
function jwk2pub (key) {
return forge.pki.setRsaPublicKey(...convert(key, ['n', 'e']))
}
module.exports = {
jwk2pub,
jwk2priv
}

75
src/keys/key-stretcher.js Normal file
View File

@@ -0,0 +1,75 @@
'use strict'
const errcode = require('err-code')
const hmac = require('../hmac')
const cipherMap = {
'AES-128': {
ivSize: 16,
keySize: 16
},
'AES-256': {
ivSize: 16,
keySize: 32
},
Blowfish: {
ivSize: 8,
cipherKeySize: 32
}
}
// Generates a set of keys for each party by stretching the shared key.
// (myIV, theirIV, myCipherKey, theirCipherKey, myMACKey, theirMACKey)
module.exports = async (cipherType, hash, secret) => {
const cipher = cipherMap[cipherType]
if (!cipher) {
const allowed = Object.keys(cipherMap).join(' / ')
throw errcode(new Error(`unknown cipher type '${cipherType}'. Must be ${allowed}`), 'ERR_INVALID_CIPHER_TYPE')
}
if (!hash) {
throw errcode(new Error('missing hash type'), 'ERR_MISSING_HASH_TYPE')
}
const cipherKeySize = cipher.keySize
const ivSize = cipher.ivSize
const hmacKeySize = 20
const seed = Buffer.from('key expansion')
const resultLength = 2 * (ivSize + cipherKeySize + hmacKeySize)
const m = await hmac.create(hash, secret)
let a = await m.digest(seed)
const result = []
let j = 0
while (j < resultLength) {
const b = await m.digest(Buffer.concat([a, seed]))
let todo = b.length
if (j + todo > resultLength) {
todo = resultLength - j
}
result.push(b)
j += todo
a = await m.digest(a)
}
const half = resultLength / 2
const resultBuffer = Buffer.concat(result)
const r1 = resultBuffer.slice(0, half)
const r2 = resultBuffer.slice(half, resultLength)
const createKey = (res) => ({
iv: res.slice(0, ivSize),
cipherKey: res.slice(ivSize, ivSize + cipherKeySize),
macKey: res.slice(ivSize + cipherKeySize)
})
return {
k1: createKey(r1),
k2: createKey(r2)
}
}

15
src/keys/keys.proto.js Normal file
View File

@@ -0,0 +1,15 @@
'use strict'
module.exports = `enum KeyType {
RSA = 0;
Ed25519 = 1;
Secp256k1 = 2;
}
message PublicKey {
required KeyType Type = 1;
required bytes Data = 2;
}
message PrivateKey {
required KeyType Type = 1;
required bytes Data = 2;
}`

151
src/keys/rsa-browser.js Normal file
View File

@@ -0,0 +1,151 @@
'use strict'
const webcrypto = require('../webcrypto')
const randomBytes = require('../random-bytes')
exports.utils = require('./rsa-utils')
exports.generateKey = async function (bits) {
const pair = await webcrypto.get().subtle.generateKey(
{
name: 'RSASSA-PKCS1-v1_5',
modulusLength: bits,
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
hash: { name: 'SHA-256' }
},
true,
['sign', 'verify']
)
const keys = await exportKey(pair)
return {
privateKey: keys[0],
publicKey: keys[1]
}
}
// Takes a jwk key
exports.unmarshalPrivateKey = async function (key) {
const privateKey = await webcrypto.get().subtle.importKey(
'jwk',
key,
{
name: 'RSASSA-PKCS1-v1_5',
hash: { name: 'SHA-256' }
},
true,
['sign']
)
const pair = [
privateKey,
await derivePublicFromPrivate(key)
]
const keys = await exportKey({
privateKey: pair[0],
publicKey: pair[1]
})
return {
privateKey: keys[0],
publicKey: keys[1]
}
}
exports.getRandomValues = randomBytes
exports.hashAndSign = async function (key, msg) {
const privateKey = await webcrypto.get().subtle.importKey(
'jwk',
key,
{
name: 'RSASSA-PKCS1-v1_5',
hash: { name: 'SHA-256' }
},
false,
['sign']
)
const sig = await webcrypto.get().subtle.sign(
{ name: 'RSASSA-PKCS1-v1_5' },
privateKey,
Uint8Array.from(msg)
)
return Buffer.from(sig)
}
exports.hashAndVerify = async function (key, sig, msg) {
const publicKey = await webcrypto.get().subtle.importKey(
'jwk',
key,
{
name: 'RSASSA-PKCS1-v1_5',
hash: { name: 'SHA-256' }
},
false,
['verify']
)
return webcrypto.get().subtle.verify(
{ name: 'RSASSA-PKCS1-v1_5' },
publicKey,
sig,
msg
)
}
function exportKey (pair) {
return Promise.all([
webcrypto.get().subtle.exportKey('jwk', pair.privateKey),
webcrypto.get().subtle.exportKey('jwk', pair.publicKey)
])
}
function derivePublicFromPrivate (jwKey) {
return webcrypto.get().subtle.importKey(
'jwk',
{
kty: jwKey.kty,
n: jwKey.n,
e: jwKey.e
},
{
name: 'RSASSA-PKCS1-v1_5',
hash: { name: 'SHA-256' }
},
true,
['verify']
)
}
/*
RSA encryption/decryption for the browser with webcrypto workarround
"bloody dark magic. webcrypto's why."
Explanation:
- Convert JWK to nodeForge
- Convert msg buffer to nodeForge buffer: ByteBuffer is a "binary-string backed buffer", so let's make our buffer a binary string
- Convert resulting nodeForge buffer to buffer: it returns a binary string, turn that into a uint8array(buffer)
*/
const { jwk2pub, jwk2priv } = require('./jwk2pem')
function convertKey (key, pub, msg, handle) {
const fkey = pub ? jwk2pub(key) : jwk2priv(key)
const fmsg = Buffer.from(msg).toString('binary')
const fomsg = handle(fmsg, fkey)
return Buffer.from(fomsg, 'binary')
}
exports.encrypt = function (key, msg) {
return convertKey(key, true, msg, (msg, key) => key.encrypt(msg))
}
exports.decrypt = function (key, msg) {
return convertKey(key, false, msg, (msg, key) => key.decrypt(msg))
}

165
src/keys/rsa-class.js Normal file
View File

@@ -0,0 +1,165 @@
'use strict'
const multihashing = require('multihashing-async')
const protobuf = require('protons')
const bs58 = require('bs58')
const errcode = require('err-code')
const crypto = require('./rsa')
const pbm = protobuf(require('./keys.proto'))
require('node-forge/lib/sha512')
require('node-forge/lib/pbe')
const forge = require('node-forge/lib/forge')
class RsaPublicKey {
constructor (key) {
this._key = key
}
async verify (data, sig) { // eslint-disable-line require-await
return crypto.hashAndVerify(this._key, sig, data)
}
marshal () {
return crypto.utils.jwkToPkix(this._key)
}
get bytes () {
return pbm.PublicKey.encode({
Type: pbm.KeyType.RSA,
Data: this.marshal()
})
}
encrypt (bytes) {
return crypto.encrypt(this._key, bytes)
}
equals (key) {
return this.bytes.equals(key.bytes)
}
async hash () { // eslint-disable-line require-await
return multihashing(this.bytes, 'sha2-256')
}
}
class RsaPrivateKey {
// key - Object of the jwk format
// publicKey - Buffer of the spki format
constructor (key, publicKey) {
this._key = key
this._publicKey = publicKey
}
genSecret () {
return crypto.getRandomValues(16)
}
async sign (message) { // eslint-disable-line require-await
return crypto.hashAndSign(this._key, message)
}
get public () {
if (!this._publicKey) {
throw errcode(new Error('public key not provided'), 'ERR_PUBKEY_NOT_PROVIDED')
}
return new RsaPublicKey(this._publicKey)
}
decrypt (bytes) {
return crypto.decrypt(this._key, bytes)
}
marshal () {
return crypto.utils.jwkToPkcs1(this._key)
}
get bytes () {
return pbm.PrivateKey.encode({
Type: pbm.KeyType.RSA,
Data: this.marshal()
})
}
equals (key) {
return this.bytes.equals(key.bytes)
}
async hash () { // eslint-disable-line require-await
return 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 bs58.encode(hash)
}
/**
* Exports the key into a password protected PEM format
*
* @param {string} password - The password to read the encrypted PEM
* @param {string} [format] - Defaults to 'pkcs-8'.
*/
async export (password, format = 'pkcs-8') { // eslint-disable-line require-await
let pem = null
const buffer = new forge.util.ByteBuffer(this.marshal())
const asn1 = forge.asn1.fromDer(buffer)
const privateKey = forge.pki.privateKeyFromAsn1(asn1)
if (format === 'pkcs-8') {
const options = {
algorithm: 'aes256',
count: 10000,
saltSize: 128 / 8,
prfAlgorithm: 'sha512'
}
pem = forge.pki.encryptRsaPrivateKey(privateKey, password, options)
} else {
throw errcode(new Error(`Unknown export format '${format}'. Must be pkcs-8`), 'ERR_INVALID_EXPORT_FORMAT')
}
return pem
}
}
async function unmarshalRsaPrivateKey (bytes) {
const jwk = crypto.utils.pkcs1ToJwk(bytes)
const keys = await crypto.unmarshalPrivateKey(jwk)
return new RsaPrivateKey(keys.privateKey, keys.publicKey)
}
function unmarshalRsaPublicKey (bytes) {
const jwk = crypto.utils.pkixToJwk(bytes)
return new RsaPublicKey(jwk)
}
async function fromJwk (jwk) {
const keys = await crypto.unmarshalPrivateKey(jwk)
return new RsaPrivateKey(keys.privateKey, keys.publicKey)
}
async function generateKeyPair (bits) {
const keys = await crypto.generateKey(bits)
return new RsaPrivateKey(keys.privateKey, keys.publicKey)
}
module.exports = {
RsaPublicKey,
RsaPrivateKey,
unmarshalRsaPublicKey,
unmarshalRsaPrivateKey,
generateKeyPair,
fromJwk
}

68
src/keys/rsa-utils.js Normal file
View File

@@ -0,0 +1,68 @@
'use strict'
const { Buffer } = require('buffer')
require('node-forge/lib/asn1')
require('node-forge/lib/rsa')
const forge = require('node-forge/lib/forge')
const { bigIntegerToUintBase64url, base64urlToBigInteger } = require('./../util')
// Convert a PKCS#1 in ASN1 DER format to a JWK key
exports.pkcs1ToJwk = function (bytes) {
const asn1 = forge.asn1.fromDer(bytes.toString('binary'))
const privateKey = forge.pki.privateKeyFromAsn1(asn1)
// https://tools.ietf.org/html/rfc7518#section-6.3.1
return {
kty: 'RSA',
n: bigIntegerToUintBase64url(privateKey.n),
e: bigIntegerToUintBase64url(privateKey.e),
d: bigIntegerToUintBase64url(privateKey.d),
p: bigIntegerToUintBase64url(privateKey.p),
q: bigIntegerToUintBase64url(privateKey.q),
dp: bigIntegerToUintBase64url(privateKey.dP),
dq: bigIntegerToUintBase64url(privateKey.dQ),
qi: bigIntegerToUintBase64url(privateKey.qInv),
alg: 'RS256',
kid: '2011-04-29'
}
}
// Convert a JWK key into PKCS#1 in ASN1 DER format
exports.jwkToPkcs1 = function (jwk) {
const asn1 = forge.pki.privateKeyToAsn1({
n: base64urlToBigInteger(jwk.n),
e: base64urlToBigInteger(jwk.e),
d: base64urlToBigInteger(jwk.d),
p: base64urlToBigInteger(jwk.p),
q: base64urlToBigInteger(jwk.q),
dP: base64urlToBigInteger(jwk.dp),
dQ: base64urlToBigInteger(jwk.dq),
qInv: base64urlToBigInteger(jwk.qi)
})
return Buffer.from(forge.asn1.toDer(asn1).getBytes(), 'binary')
}
// Convert a PKCIX in ASN1 DER format to a JWK key
exports.pkixToJwk = function (bytes) {
const asn1 = forge.asn1.fromDer(bytes.toString('binary'))
const publicKey = forge.pki.publicKeyFromAsn1(asn1)
return {
kty: 'RSA',
n: bigIntegerToUintBase64url(publicKey.n),
e: bigIntegerToUintBase64url(publicKey.e),
alg: 'RS256',
kid: '2011-04-29'
}
}
// Convert a JWK key to PKCIX in ASN1 DER format
exports.jwkToPkix = function (jwk) {
const asn1 = forge.pki.publicKeyToAsn1({
n: base64urlToBigInteger(jwk.n),
e: base64urlToBigInteger(jwk.e)
})
return Buffer.from(forge.asn1.toDer(asn1).getBytes(), 'binary')
}

View File

@@ -1,141 +1,83 @@
'use strict'
const forge = require('node-forge')
const protobuf = require('protocol-buffers')
const fs = require('fs')
const path = require('path')
const utils = require('../utils')
const pki = forge.pki
const rsa = pki.rsa
const pbm = protobuf(fs.readFileSync(path.join(__dirname, '../crypto.proto')))
class RsaPublicKey {
constructor (k) {
this._key = k
const crypto = require('crypto')
const errcode = require('err-code')
const randomBytes = require('../random-bytes')
// @ts-check
/**
* @type {PrivateKey}
*/
let keypair
try {
if (process.env.LP2P_FORCE_CRYPTO_LIB === 'keypair') {
throw new Error('Force keypair usage')
}
verify (data, sig) {
const md = forge.md.sha256.create()
if (Buffer.isBuffer(data)) {
md.update(data.toString('binary'), 'binary')
} else {
md.update(data)
}
return this._key.verify(md.digest().bytes(), sig)
}
marshal () {
return new Buffer(forge.asn1.toDer(pki.publicKeyToAsn1(this._key)).bytes(), 'binary')
}
get bytes () {
return pbm.PublicKey.encode({
Type: pbm.KeyType.RSA,
Data: this.marshal()
})
}
encrypt (bytes) {
return this._key.encrypt(bytes, 'RSAES-PKCS1-V1_5')
}
equals (key) {
return this.bytes.equals(key.bytes)
}
hash () {
return utils.keyHash(this.bytes)
}
}
class RsaPrivateKey {
constructor (privKey, pubKey) {
this._privateKey = privKey
if (pubKey) {
this._publicKey = pubKey
} else {
this._publicKey = forge.pki.setRsaPublicKey(privKey.n, privKey.e)
const ursa = require('ursa-optional') // throws if not compiled
keypair = ({ bits }) => {
const key = ursa.generatePrivateKey(bits)
return {
private: key.toPrivatePem(),
public: key.toPublicPem()
}
}
genSecret () {
return forge.random.getBytesSync(16)
} catch (e) {
if (process.env.LP2P_FORCE_CRYPTO_LIB === 'ursa') {
throw e
}
sign (message) {
const md = forge.md.sha256.create()
if (Buffer.isBuffer(message)) {
md.update(message.toString('binary'), 'binary')
} else {
md.update(message)
keypair = require('keypair')
}
const pemToJwk = require('pem-jwk').pem2jwk
const jwkToPem = require('pem-jwk').jwk2pem
exports.utils = require('./rsa-utils')
exports.generateKey = async function (bits) { // eslint-disable-line require-await
const key = keypair({ bits })
return {
privateKey: pemToJwk(key.private),
publicKey: pemToJwk(key.public)
}
}
// Takes a jwk key
exports.unmarshalPrivateKey = async function (key) { // eslint-disable-line require-await
if (!key) {
throw errcode(new Error('Missing key parameter'), 'ERR_MISSING_KEY')
}
return {
privateKey: key,
publicKey: {
kty: key.kty,
n: key.n,
e: key.e
}
const raw = this._privateKey.sign(md, 'RSASSA-PKCS1-V1_5')
return new Buffer(raw, 'binary')
}
get public () {
if (!this._publicKey) {
throw new Error('public key not provided')
}
return new RsaPublicKey(this._publicKey)
}
decrypt (bytes) {
return this._privateKey.decrypt(bytes, 'RSAES-PKCS1-V1_5')
}
marshal () {
return new Buffer(forge.asn1.toDer(pki.privateKeyToAsn1(this._privateKey)).bytes(), 'binary')
}
get bytes () {
return pbm.PrivateKey.encode({
Type: pbm.KeyType.RSA,
Data: this.marshal()
})
}
equals (key) {
return this.bytes.equals(key.bytes)
}
hash () {
return utils.keyHash(this.bytes)
}
}
function unmarshalRsaPrivateKey (bytes) {
if (Buffer.isBuffer(bytes)) {
bytes = forge.util.createBuffer(bytes.toString('binary'))
}
const key = pki.privateKeyFromAsn1(forge.asn1.fromDer(bytes))
exports.getRandomValues = randomBytes
return new RsaPrivateKey(key)
exports.hashAndSign = async function (key, msg) { // eslint-disable-line require-await
const sign = crypto.createSign('RSA-SHA256')
sign.update(msg)
const pem = jwkToPem(key)
return sign.sign(pem)
}
function unmarshalRsaPublicKey (bytes) {
if (Buffer.isBuffer(bytes)) {
bytes = forge.util.createBuffer(bytes.toString('binary'))
}
const key = pki.publicKeyFromAsn1(forge.asn1.fromDer(bytes))
return new RsaPublicKey(key)
exports.hashAndVerify = async function (key, sig, msg) { // eslint-disable-line require-await
const verify = crypto.createVerify('RSA-SHA256')
verify.update(msg)
const pem = jwkToPem(key)
return verify.verify(pem, sig)
}
function generateKeyPair (bits) {
const p = rsa.generateKeyPair({bits})
return new RsaPrivateKey(p.privateKey, p.publicKey)
const padding = crypto.constants.RSA_PKCS1_PADDING
exports.encrypt = function (key, bytes) {
return crypto.publicEncrypt({ key: jwkToPem(key), padding }, bytes)
}
module.exports = {
RsaPublicKey,
RsaPrivateKey,
unmarshalRsaPublicKey,
unmarshalRsaPrivateKey,
generateKeyPair
exports.decrypt = function (key, bytes) {
return crypto.privateDecrypt({ key: jwkToPem(key), padding }, bytes)
}

View File

@@ -0,0 +1,10 @@
'use strict'
const errcode = require('err-code')
module.exports = function (curveTypes, type) {
if (!curveTypes.includes(type)) {
const names = curveTypes.join(' / ')
throw errcode(new Error(`Unknown curve: ${type}. Must be ${names}`), 'ERR_INVALID_CURVE')
}
}

45
src/pbkdf2.js Normal file
View File

@@ -0,0 +1,45 @@
'use strict'
const forgePbkdf2 = require('node-forge/lib/pbkdf2')
const forgeUtil = require('node-forge/lib/util')
const errcode = require('err-code')
/**
* Maps an IPFS hash name to its node-forge equivalent.
*
* See https://github.com/multiformats/multihash/blob/master/hashtable.csv
*
* @private
*/
const hashName = {
sha1: 'sha1',
'sha2-256': 'sha256',
'sha2-512': 'sha512'
}
/**
* Computes the Password-Based Key Derivation Function 2.
*
* @param {string} password
* @param {string} salt
* @param {number} iterations
* @param {number} keySize (in bytes)
* @param {string} hash - The hash name ('sha1', 'sha2-512, ...)
* @returns {string} - A new password
*/
function pbkdf2 (password, salt, iterations, keySize, hash) {
const hasher = hashName[hash]
if (!hasher) {
const types = Object.keys(hashName).join(' / ')
throw errcode(new Error(`Hash '${hash}' is unknown or not supported. Must be ${types}`), 'ERR_UNSUPPORTED_HASH_TYPE')
}
const dek = forgePbkdf2(
password,
salt,
iterations,
keySize,
hasher)
return forgeUtil.encode64(dek)
}
module.exports = pbkdf2

10
src/random-bytes.js Normal file
View File

@@ -0,0 +1,10 @@
'use strict'
const randomBytes = require('iso-random-stream/src/random')
const errcode = require('err-code')
module.exports = function (length) {
if (isNaN(length) || length <= 0) {
throw errcode(new Error('random bytes length must be a Number bigger than 0'), 'ERR_INVALID_LENGTH')
}
return randomBytes(length)
}

55
src/util.js Normal file
View File

@@ -0,0 +1,55 @@
'use strict'
const { Buffer } = require('buffer')
require('node-forge/lib/util')
require('node-forge/lib/jsbn')
const forge = require('node-forge/lib/forge')
exports.bigIntegerToUintBase64url = (num, len) => {
// Call `.abs()` to convert to unsigned
let buf = Buffer.from(num.abs().toByteArray()) // toByteArray converts to big endian
// toByteArray() gives us back a signed array, which will include a leading 0
// byte if the most significant bit of the number is 1:
// https://docs.microsoft.com/en-us/windows/win32/seccertenroll/about-integer
// Our number will always be positive so we should remove the leading padding.
buf = buf[0] === 0 ? buf.slice(1) : buf
if (len != null) {
if (buf.length > len) throw new Error('byte array longer than desired length')
buf = Buffer.concat([Buffer.alloc(len - buf.length), buf])
}
return exports.bufferToBase64url(buf)
}
// Convert a Buffer to a base64 encoded string without padding
// Adapted from https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-41#appendix-C
exports.bufferToBase64url = buf => {
return buf
.toString('base64')
.split('=')[0] // Remove any trailing '='s
.replace(/\+/g, '-') // 62nd char of encoding
.replace(/\//g, '_') // 63rd char of encoding
}
// Convert a base64url encoded string to a BigInteger
exports.base64urlToBigInteger = str => {
const buf = exports.base64urlToBuffer(str)
return new forge.jsbn.BigInteger(buf.toString('hex'), 16)
}
exports.base64urlToBuffer = (str, len) => {
str = (str + '==='.slice((str.length + 3) % 4))
.replace(/-/g, '+')
.replace(/_/g, '/')
let buf = Buffer.from(str, 'base64')
if (len != null) {
if (buf.length > len) throw new Error('byte array longer than desired length')
buf = Buffer.concat([Buffer.alloc(len - buf.length), buf])
}
return buf
}

View File

@@ -1,8 +0,0 @@
'use strict'
const multihashing = require('multihashing')
// Hashes a key
exports.keyHash = (bytes) => {
return multihashing(bytes, 'sha2-256')
}

24
src/webcrypto.js Normal file
View File

@@ -0,0 +1,24 @@
/* eslint-env browser */
'use strict'
// Check native crypto exists and is enabled (In insecure context `self.crypto`
// exists but `self.crypto.subtle` does not).
exports.get = (win = self) => {
const nativeCrypto = win.crypto || win.msCrypto
if (!nativeCrypto || !nativeCrypto.subtle) {
throw Object.assign(
new Error(
'Missing Web Crypto API. ' +
'The most likely cause of this error is that this page is being accessed ' +
'from an insecure context (i.e. not HTTPS). For more information and ' +
'possible resolutions see ' +
'https://github.com/libp2p/js-libp2p-crypto/blob/master/README.md#web-crypto-api'
),
{ code: 'ERR_MISSING_WEB_CRYPTO' }
)
}
return nativeCrypto
}

153
stats.md Normal file
View File

@@ -0,0 +1,153 @@
# Stats
## Size
| | non-minified | minified |
|-------|--------------|----------|
|before | `1.8M` | `949K` |
|after | `606K` | `382K` |
## Performance
### RSA
#### Before
##### Node `6.6.0`
```
generateKeyPair 1024bits x 3.51 ops/sec ±29.45% (22 runs sampled)
generateKeyPair 2048bits x 0.17 ops/sec ±145.40% (5 runs sampled)
generateKeyPair 4096bits x 0.02 ops/sec ±96.53% (5 runs sampled)
sign and verify x 95.98 ops/sec ±1.51% (71 runs sampled)
```
##### Browser (Chrome `53.0.2785.116`)
```
generateKeyPair 1024bits x 3.56 ops/sec ±27.16% (23 runs sampled)
generateKeyPair 2048bits x 0.49 ops/sec ±69.32% (8 runs sampled)
generateKeyPair 4096bits x 0.03 ops/sec ±77.11% (5 runs sampled)
sign and verify x 109 ops/sec ±2.00% (53 runs sampled)
```
#### After
##### Node `6.6.0`
```
generateKeyPair 1024bits x 42.45 ops/sec ±9.87% (52 runs sampled)
generateKeyPair 2048bits x 7.46 ops/sec ±23.80% (16 runs sampled)
generateKeyPair 4096bits x 1.50 ops/sec ±58.59% (13 runs sampled)
sign and verify x 1,080 ops/sec ±2.23% (74 runs sampled)
```
##### Browser (Chrome `53.0.2785.116`)
```
generateKeyPair 1024bits x 5.89 ops/sec ±18.94% (19 runs sampled)
generateKeyPair 2048bits x 1.32 ops/sec ±36.84% (10 runs sampled)
generateKeyPair 4096bits x 0.20 ops/sec ±62.49% (5 runs sampled)
sign and verify x 608 ops/sec ±6.75% (56 runs sampled)
```
### Key Stretcher
#### Before
##### Node `6.6.0`
```
keyStretcher AES-128 SHA1 x 3,863 ops/sec ±3.80% (70 runs sampled)
keyStretcher AES-128 SHA256 x 3,862 ops/sec ±5.33% (64 runs sampled)
keyStretcher AES-128 SHA512 x 3,369 ops/sec ±1.73% (73 runs sampled)
keyStretcher AES-256 SHA1 x 3,008 ops/sec ±4.81% (67 runs sampled)
keyStretcher AES-256 SHA256 x 2,900 ops/sec ±7.01% (64 runs sampled)
keyStretcher AES-256 SHA512 x 2,553 ops/sec ±4.45% (73 runs sampled)
keyStretcher Blowfish SHA1 x 28,045 ops/sec ±7.32% (61 runs sampled)
keyStretcher Blowfish SHA256 x 18,860 ops/sec ±5.36% (67 runs sampled)
keyStretcher Blowfish SHA512 x 12,142 ops/sec ±12.44% (72 runs sampled)
```
##### Browser (Chrome `53.0.2785.116`)
```
keyStretcher AES-128 SHA1 x 4,168 ops/sec ±4.08% (49 runs sampled)
keyStretcher AES-128 SHA256 x 4,239 ops/sec ±6.36% (48 runs sampled)
keyStretcher AES-128 SHA512 x 3,600 ops/sec ±5.15% (51 runs sampled)
keyStretcher AES-256 SHA1 x 3,009 ops/sec ±6.82% (48 runs sampled)
keyStretcher AES-256 SHA256 x 3,086 ops/sec ±9.56% (19 runs sampled)
keyStretcher AES-256 SHA512 x 2,470 ops/sec ±2.22% (54 runs sampled)
keyStretcher Blowfish SHA1 x 7,143 ops/sec ±15.17% (9 runs sampled)
keyStretcher Blowfish SHA256 x 17,846 ops/sec ±4.74% (46 runs sampled)
keyStretcher Blowfish SHA512 x 7,726 ops/sec ±1.81% (50 runs sampled)
```
#### After
##### Node `6.6.0`
```
keyStretcher AES-128 SHA1 x 6,680 ops/sec ±3.62% (65 runs sampled)
keyStretcher AES-128 SHA256 x 8,124 ops/sec ±4.37% (66 runs sampled)
keyStretcher AES-128 SHA512 x 11,683 ops/sec ±4.56% (66 runs sampled)
keyStretcher AES-256 SHA1 x 5,531 ops/sec ±4.69% (68 runs sampled)
keyStretcher AES-256 SHA256 x 6,725 ops/sec ±4.87% (66 runs sampled)
keyStretcher AES-256 SHA512 x 9,042 ops/sec ±3.87% (64 runs sampled)
keyStretcher Blowfish SHA1 x 40,757 ops/sec ±5.38% (60 runs sampled)
keyStretcher Blowfish SHA256 x 41,845 ops/sec ±4.89% (64 runs sampled)
keyStretcher Blowfish SHA512 x 42,345 ops/sec ±4.86% (63 runs sampled)
```
##### Browser (Chrome `53.0.2785.116`)
```
keyStretcher AES-128 SHA1 x 479 ops/sec ±2.12% (54 runs sampled)
keyStretcher AES-128 SHA256 x 668 ops/sec ±2.02% (53 runs sampled)
keyStretcher AES-128 SHA512 x 1,112 ops/sec ±1.61% (54 runs sampled)
keyStretcher AES-256 SHA1 x 460 ops/sec ±1.37% (54 runs sampled)
keyStretcher AES-256 SHA256 x 596 ops/sec ±1.56% (54 runs sampled)
keyStretcher AES-256 SHA512 x 808 ops/sec ±3.27% (52 runs sampled)
keyStretcher Blowfish SHA1 x 3,015 ops/sec ±3.51% (52 runs sampled)
keyStretcher Blowfish SHA256 x 2,755 ops/sec ±3.82% (53 runs sampled)
keyStretcher Blowfish SHA512 x 2,955 ops/sec ±5.35% (51 runs sampled)
```
### Ephemeral Keys
#### Before
##### Node `6.6.0`
```
ephemeral key with secrect P-256 x 89.93 ops/sec ±39.45% (72 runs sampled)
ephemeral key with secrect P-384 x 110 ops/sec ±1.28% (71 runs sampled)
ephemeral key with secrect P-521 x 112 ops/sec ±1.70% (72 runs sampled)
```
##### Browser (Chrome `53.0.2785.116`)
```
ephemeral key with secrect P-256 x 6.27 ops/sec ±15.89% (35 runs sampled)
ephemeral key with secrect P-384 x 6.84 ops/sec ±1.21% (35 runs sampled)
ephemeral key with secrect P-521 x 6.60 ops/sec ±1.84% (34 runs sampled)
```
#### After
##### Node `6.6.0`
```
ephemeral key with secrect P-256 x 555 ops/sec ±1.61% (75 runs sampled)
ephemeral key with secrect P-384 x 547 ops/sec ±4.40% (68 runs sampled)
ephemeral key with secrect P-521 x 583 ops/sec ±4.84% (72 runs sampled)
```
##### Browser (Chrome `53.0.2785.116`)
```
ephemeral key with secrect P-256 x 796 ops/sec ±2.36% (53 runs sampled)
ephemeral key with secrect P-384 x 788 ops/sec ±2.66% (53 runs sampled)
ephemeral key with secrect P-521 x 808 ops/sec ±1.83% (54 runs sampled)
```

111
test/aes/aes.spec.js Normal file
View File

@@ -0,0 +1,111 @@
/* eslint max-nested-callbacks: ["error", 8] */
/* eslint-disable valid-jsdoc */
/* eslint-env mocha */
'use strict'
const chai = require('chai')
const dirtyChai = require('dirty-chai')
const expect = chai.expect
chai.use(dirtyChai)
const { expectErrCode } = require('../util')
const crypto = require('../../src')
const fixtures = require('./../fixtures/aes')
const goFixtures = require('./../fixtures/go-aes')
const bytes = {
16: 'AES-128',
32: 'AES-256'
}
/** @typedef {import("libp2p-crypto").aes.Cipher} Cipher */
describe('AES-CTR', () => {
Object.keys(bytes).forEach((byte) => {
it(`${bytes[byte]} - encrypt and decrypt`, async () => {
const key = Buffer.alloc(parseInt(byte, 10))
key.fill(5)
const iv = Buffer.alloc(16)
iv.fill(1)
const cipher = await crypto.aes.create(key, iv)
await encryptAndDecrypt(cipher)
await encryptAndDecrypt(cipher)
await encryptAndDecrypt(cipher)
await encryptAndDecrypt(cipher)
await encryptAndDecrypt(cipher)
})
})
Object.keys(bytes).forEach((byte) => {
it(`${bytes[byte]} - fixed - encrypt and decrypt`, async () => {
const key = Buffer.alloc(parseInt(byte, 10))
key.fill(5)
const iv = Buffer.alloc(16)
iv.fill(1)
const cipher = await crypto.aes.create(key, iv)
for (let i = 0; i < fixtures[byte].inputs.length; i++) {
const rawIn = fixtures[byte].inputs[i]
const input = Buffer.from(rawIn)
const output = Buffer.from(fixtures[byte].outputs[i])
const encrypted = await cipher.encrypt(input)
expect(encrypted).to.have.length(output.length)
expect(encrypted).to.eql(output)
const decrypted = await cipher.decrypt(encrypted)
expect(decrypted).to.eql(input)
}
})
})
Object.keys(bytes).forEach((byte) => {
if (!goFixtures[byte]) {
return
}
it(`${bytes[byte]} - go interop - encrypt and decrypt`, async () => {
const key = Buffer.alloc(parseInt(byte, 10))
key.fill(5)
const iv = Buffer.alloc(16)
iv.fill(1)
const cipher = await crypto.aes.create(key, iv)
for (let i = 0; i < goFixtures[byte].inputs.length; i++) {
const rawIn = goFixtures[byte].inputs[i]
const input = Buffer.from(rawIn)
const output = Buffer.from(goFixtures[byte].outputs[i])
const encrypted = await cipher.encrypt(input)
expect(encrypted).to.have.length(output.length)
expect(encrypted).to.eql(output)
const decrypted = await cipher.decrypt(encrypted)
expect(decrypted).to.eql(input)
}
})
})
it('checks key length', () => {
const key = Buffer.alloc(5)
const iv = Buffer.alloc(16)
return expectErrCode(crypto.aes.create(key, iv), 'ERR_INVALID_KEY_LENGTH')
})
})
// @ts-check
/**
* @type {function(Cipher): void}
*/
async function encryptAndDecrypt (cipher) {
const data = Buffer.alloc(100)
data.fill(Math.ceil(Math.random() * 100))
const encrypted = await cipher.encrypt(data)
const decrypted = await cipher.decrypt(encrypted)
expect(decrypted).to.be.eql(data)
}

61
test/browser.js Normal file
View File

@@ -0,0 +1,61 @@
/* eslint-env mocha */
'use strict'
const chai = require('chai')
const dirtyChai = require('dirty-chai')
const expect = chai.expect
chai.use(dirtyChai)
const crypto = require('../')
const webcrypto = require('../src/webcrypto')
async function expectMissingWebCrypto (fn) {
try {
await fn()
} catch (err) {
expect(err.code).to.equal('ERR_MISSING_WEB_CRYPTO')
return
}
throw new Error('Expected missing web crypto error')
}
describe('Missing web crypto', () => {
let webcryptoGet
let rsaPrivateKey
before(async () => {
rsaPrivateKey = await crypto.keys.generateKeyPair('RSA', 512)
})
before(() => {
webcryptoGet = webcrypto.get
webcrypto.get = () => webcryptoGet({})
})
after(() => {
webcrypto.get = webcryptoGet
})
it('should error for hmac create when web crypto is missing', () => {
return expectMissingWebCrypto(() => crypto.hmac.create('SHA256', Buffer.from('secret')))
})
it('should error for generate ephemeral key pair when web crypto is missing', () => {
return expectMissingWebCrypto(() => crypto.keys.generateEphemeralKeyPair('P-256'))
})
it('should error for generate rsa key pair when web crypto is missing', () => {
return expectMissingWebCrypto(() => crypto.keys.generateKeyPair('rsa', 256))
})
it('should error for unmarshal RSA private key when web crypto is missing', () => {
return expectMissingWebCrypto(() => crypto.keys.unmarshalPrivateKey(crypto.keys.marshalPrivateKey(rsaPrivateKey)))
})
it('should error for sign RSA private key when web crypto is missing', () => {
return expectMissingWebCrypto(() => rsaPrivateKey.sign(Buffer.from('test')))
})
it('should error for verify RSA public key when web crypto is missing', () => {
return expectMissingWebCrypto(() => rsaPrivateKey.public.verify(Buffer.from('test'), Buffer.from('test')))
})
})

131
test/crypto.spec.js Normal file
View File

@@ -0,0 +1,131 @@
/* eslint max-nested-callbacks: ["error", 8] */
/* eslint-env mocha */
'use strict'
const chai = require('chai')
const dirtyChai = require('dirty-chai')
const expect = chai.expect
chai.use(dirtyChai)
const crypto = require('../src')
const fixtures = require('./fixtures/go-key-rsa')
const { expectErrCode } = require('./util')
/** @typedef {import("libp2p-crypto").PrivateKey} PrivateKey */
describe('libp2p-crypto', function () {
this.timeout(20 * 1000)
// @ts-check
/**
* @type {PrivateKey}
*/
let key
before(async () => {
key = await crypto.keys.generateKeyPair('RSA', 512)
})
it('marshalPublicKey and unmarshalPublicKey', () => {
const key2 = crypto.keys.unmarshalPublicKey(
crypto.keys.marshalPublicKey(key.public))
expect(key2.equals(key.public)).to.be.eql(true)
expect(() => {
crypto.keys.marshalPublicKey(key.public, 'invalid-key-type')
}).to.throw()
})
it('marshalPrivateKey and unmarshalPrivateKey', async () => {
expect(() => {
crypto.keys.marshalPrivateKey(key, 'invalid-key-type')
}).to.throw()
const key2 = await crypto.keys.unmarshalPrivateKey(crypto.keys.marshalPrivateKey(key))
expect(key2.equals(key)).to.be.eql(true)
expect(key2.public.equals(key.public)).to.be.eql(true)
})
it('generateKeyPair', () => {
return expectErrCode(crypto.keys.generateKeyPair('invalid-key-type', 512), 'ERR_UNSUPPORTED_KEY_TYPE')
})
it('generateKeyPairFromSeed', () => {
var seed = crypto.randomBytes(32)
return expectErrCode(crypto.keys.generateKeyPairFromSeed('invalid-key-type', seed, 512), 'ERR_UNSUPPORTED_KEY_TYPE')
})
// marshalled keys seem to be slightly different
// unsure as to if this is just a difference in encoding
// or a bug
describe('go interop', () => {
it('unmarshals private key', async () => {
const key = await crypto.keys.unmarshalPrivateKey(fixtures.private.key)
const hash = fixtures.private.hash
expect(fixtures.private.key).to.eql(key.bytes)
const digest = await key.hash()
expect(digest).to.eql(hash)
})
it('unmarshals public key', async () => {
const key = crypto.keys.unmarshalPublicKey(fixtures.public.key)
const hash = fixtures.public.hash
expect(crypto.keys.marshalPublicKey(key)).to.eql(fixtures.public.key)
const digest = await key.hash()
expect(digest).to.eql(hash)
})
it('unmarshal -> marshal, private key', async () => {
const key = await crypto.keys.unmarshalPrivateKey(fixtures.private.key)
const marshalled = crypto.keys.marshalPrivateKey(key)
expect(marshalled).to.eql(fixtures.private.key)
})
it('unmarshal -> marshal, public key', () => {
const key = crypto.keys.unmarshalPublicKey(fixtures.public.key)
const marshalled = crypto.keys.marshalPublicKey(key)
expect(fixtures.public.key.equals(marshalled)).to.eql(true)
})
})
describe('pbkdf2', () => {
it('generates a derived password using sha1', () => {
const p1 = crypto.pbkdf2('password', 'at least 16 character salt', 500, 512 / 8, 'sha1')
expect(p1).to.exist()
expect(p1).to.be.a('string')
})
it('generates a derived password using sha2-512', () => {
const p1 = crypto.pbkdf2('password', 'at least 16 character salt', 500, 512 / 8, 'sha2-512')
expect(p1).to.exist()
expect(p1).to.be.a('string')
})
it('generates the same derived password with the same options', () => {
const p1 = crypto.pbkdf2('password', 'at least 16 character salt', 10, 512 / 8, 'sha1')
const p2 = crypto.pbkdf2('password', 'at least 16 character salt', 10, 512 / 8, 'sha1')
const p3 = crypto.pbkdf2('password', 'at least 16 character salt', 11, 512 / 8, 'sha1')
expect(p2).to.equal(p1)
expect(p3).to.not.equal(p2)
})
it('throws on invalid hash name', () => {
const fn = () => crypto.pbkdf2('password', 'at least 16 character salt', 500, 512 / 8, 'shaX-xxx')
expect(fn).to.throw().with.property('code', 'ERR_UNSUPPORTED_HASH_TYPE')
})
})
describe('randomBytes', () => {
it('throws with invalid number passed', () => {
expect(() => {
crypto.randomBytes(-1)
}).to.throw()
})
it('generates different random things', () => {
const buf1 = crypto.randomBytes(10)
expect(buf1.length).to.equal(10)
const buf2 = crypto.randomBytes(10)
expect(buf1).to.not.eql(buf2)
})
})
})

View File

@@ -1,44 +0,0 @@
/* eslint-env mocha */
'use strict'
const expect = require('chai').expect
const EC = require('elliptic').ec
const crypto = require('../src')
const fixtures = require('./fixtures/go-elliptic-key')
describe('generateEphemeralKeyPair', () => {
it('returns a function that generates a shared secret', () => {
const res = crypto.generateEphemeralKeyPair('P-256')
const ourPublic = '044374add0df35706db7dade25f3959fc051d2ef5166f8a6a0aa632d0ab41cdb4d30e1a064e121ac56155235a6b8d4c5d8fe35e019f507f4e2ff1445e229d7af43'
expect(
res.genSharedKey(ourPublic)
).to.have.length(32)
expect(
res.key
).to.exist
})
describe('go interop', () => {
it('generates a shared secret', () => {
const curve = fixtures.curve
const ec = new EC(fixtures.curveJs)
const bobPrivate = ec.keyFromPrivate(fixtures.bob.private, 'binary')
const alice = crypto.generateEphemeralKeyPair(curve)
const bob = {
key: fixtures.bob.public,
// this is using bobs private key from go ipfs
// instead of alices
genSharedKey: (key) => alice.genSharedKey(key, bobPrivate)
}
const s1 = alice.genSharedKey(bob.key)
const s2 = bob.genSharedKey(alice.key)
expect(s1.equals(s2)).to.be.eql(true)
})
})
})

27
test/fixtures/aes.js vendored Normal file
View File

@@ -0,0 +1,27 @@
'use strict'
const fixes16 = [
require('./fix1.json'),
require('./fix2.json'),
require('./fix3.json'),
require('./fix4.json'),
require('./fix5.json')
]
const fixes32 = [
require('./fix6.json'),
require('./fix7.json'),
require('./fix8.json'),
require('./fix9.json'),
require('./fix10.json')
]
module.exports = {
16: {
inputs: fixes16.map((f) => f.input),
outputs: fixes16.map((f) => f.output)
},
32: {
inputs: fixes32.map((f) => f.input),
outputs: fixes32.map((f) => f.output)
}
}

212
test/fixtures/fix1.json vendored Normal file
View File

@@ -0,0 +1,212 @@
{
"input": {
"type": "Buffer",
"data": [
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47,
47
]
},
"output": {
"type": "Buffer",
"data": [
121,
104,
0,
151,
57,
137,
22,
239,
234,
151,
58,
15,
100,
22,
228,
110,
85,
248,
249,
15,
145,
128,
223,
25,
192,
175,
132,
169,
98,
203,
231,
106,
224,
102,
206,
244,
29,
213,
36,
2,
26,
213,
94,
29,
134,
219,
136,
73,
212,
176,
33,
95,
198,
91,
148,
139,
132,
252,
182,
115,
116,
160,
146,
194,
0,
97,
181,
0,
193,
149,
21,
51,
248,
97,
32,
62,
86,
153,
238,
67,
38,
34,
55,
143,
70,
193,
99,
107,
31,
67,
90,
97,
55,
63,
69,
203,
33,
233,
74,
237
]
}
}

212
test/fixtures/fix10.json vendored Normal file
View File

@@ -0,0 +1,212 @@
{
"input": {
"type": "Buffer",
"data": [
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72,
72
]
},
"output": {
"type": "Buffer",
"data": [
201,
224,
56,
64,
167,
224,
124,
49,
19,
51,
208,
226,
216,
50,
12,
73,
15,
255,
156,
108,
98,
179,
144,
110,
33,
151,
43,
64,
227,
153,
120,
39,
21,
151,
41,
61,
245,
20,
31,
23,
109,
213,
109,
55,
35,
40,
122,
109,
41,
10,
32,
176,
25,
184,
91,
176,
177,
134,
138,
252,
160,
2,
108,
43,
222,
239,
174,
2,
145,
74,
34,
131,
237,
214,
235,
102,
26,
204,
124,
64,
101,
134,
186,
45,
55,
29,
52,
55,
171,
95,
172,
86,
242,
101,
141,
153,
222,
161,
128,
83
]
}
}

212
test/fixtures/fix2.json vendored Normal file
View File

@@ -0,0 +1,212 @@
{
"input": {
"type": "Buffer",
"data": [
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24
]
},
"output": {
"type": "Buffer",
"data": [
109,
172,
127,
179,
110,
222,
79,
219,
87,
76,
70,
204,
166,
110,
184,
229,
90,
49,
160,
252,
78,
58,
73,
51,
152,
218,
3,
200,
10,
124,
152,
117,
137,
40,
23,
127,
56,
57,
203,
31,
101,
227,
31,
198,
223,
86,
98,
120,
100,
86,
116,
144,
142,
127,
68,
175,
249,
232,
22,
83,
22,
68,
60,
230,
146,
22,
153,
193,
67,
5,
51,
253,
239,
210,
80,
31,
254,
103,
185,
145,
123,
99,
205,
175,
156,
144,
191,
67,
31,
236,
43,
98,
197,
235,
31,
50,
69,
228,
100,
64
]
}
}

212
test/fixtures/fix3.json vendored Normal file
View File

@@ -0,0 +1,212 @@
{
"input": {
"type": "Buffer",
"data": [
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7
]
},
"output": {
"type": "Buffer",
"data": [
191,
93,
126,
191,
180,
42,
118,
144,
28,
188,
211,
191,
211,
130,
170,
153,
134,
240,
179,
83,
75,
23,
42,
68,
158,
200,
123,
155,
57,
169,
152,
133,
33,
114,
90,
29,
131,
91,
70,
105,
83,
45,
40,
47,
77,
96,
97,
8,
40,
1,
110,
245,
106,
172,
152,
146,
5,
114,
132,
0,
179,
31,
44,
78,
19,
109,
92,
199,
226,
36,
12,
74,
180,
241,
224,
107,
83,
13,
167,
27,
251,
101,
193,
98,
49,
90,
225,
197,
75,
213,
144,
52,
235,
130,
92,
247,
219,
139,
209,
132
]
}
}

212
test/fixtures/fix4.json vendored Normal file
View File

@@ -0,0 +1,212 @@
{
"input": {
"type": "Buffer",
"data": [
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25,
25
]
},
"output": {
"type": "Buffer",
"data": [
191,
110,
53,
190,
250,
44,
190,
136,
22,
209,
131,
200,
112,
31,
61,
183,
247,
95,
4,
249,
69,
147,
238,
74,
18,
71,
197,
115,
141,
226,
102,
92,
91,
128,
181,
172,
67,
109,
17,
165,
52,
10,
55,
31,
55,
225,
253,
140,
154,
35,
104,
62,
119,
103,
197,
152,
125,
134,
140,
181,
170,
76,
75,
114,
195,
188,
68,
197,
28,
47,
116,
82,
34,
128,
232,
122,
14,
229,
122,
161,
36,
212,
161,
164,
145,
86,
215,
233,
222,
50,
143,
89,
131,
32,
130,
196,
109,
36,
204,
254
]
}
}

212
test/fixtures/fix5.json vendored Normal file
View File

@@ -0,0 +1,212 @@
{
"input": {
"type": "Buffer",
"data": [
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48,
48
]
},
"output": {
"type": "Buffer",
"data": [
97,
142,
8,
37,
69,
173,
124,
180,
97,
70,
45,
91,
196,
126,
184,
135,
213,
104,
171,
89,
231,
63,
43,
42,
7,
245,
75,
165,
205,
182,
24,
50,
170,
217,
128,
112,
114,
215,
209,
145,
135,
235,
179,
212,
5,
81,
142,
199,
53,
221,
39,
239,
167,
21,
237,
168,
145,
249,
250,
108,
2,
247,
89,
73,
228,
227,
255,
155,
121,
157,
205,
96,
43,
32,
112,
209,
173,
96,
143,
43,
220,
140,
26,
205,
34,
34,
53,
157,
41,
167,
125,
235,
243,
85,
13,
14,
93,
109,
233,
186
]
}
}

212
test/fixtures/fix6.json vendored Normal file
View File

@@ -0,0 +1,212 @@
{
"input": {
"type": "Buffer",
"data": [
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68,
68
]
},
"output": {
"type": "Buffer",
"data": [
198,
59,
91,
107,
222,
214,
31,
230,
112,
1,
199,
174,
17,
224,
194,
225,
138,
65,
58,
160,
31,
249,
102,
16,
74,
46,
85,
30,
232,
249,
120,
108,
38,
165,
105,
27,
12,
98,
98,
108,
130,
163,
16,
137,
116,
104,
153,
188,
5,
251,
163,
244,
0,
58,
1,
207,
25,
78,
188,
210,
205,
221,
48,
132,
72,
209,
11,
67,
14,
42,
218,
71,
148,
252,
126,
183,
60,
32,
93,
36,
125,
103,
191,
117,
82,
241,
190,
176,
1,
129,
118,
112,
139,
153,
178,
56,
61,
91,
52,
198
]
}
}

212
test/fixtures/fix7.json vendored Normal file
View File

@@ -0,0 +1,212 @@
{
"input": {
"type": "Buffer",
"data": [
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10
]
},
"output": {
"type": "Buffer",
"data": [
206,
241,
99,
120,
248,
163,
57,
30,
216,
86,
255,
192,
89,
193,
176,
17,
78,
62,
80,
149,
189,
235,
27,
157,
175,
248,
7,
19,
222,
64,
111,
199,
204,
163,
26,
16,
95,
221,
70,
32,
239,
4,
58,
162,
253,
237,
8,
51,
94,
3,
165,
186,
223,
210,
116,
101,
228,
82,
103,
76,
74,
44,
51,
117,
189,
140,
132,
230,
188,
243,
24,
158,
149,
93,
147,
226,
113,
195,
31,
24,
9,
19,
27,
132,
180,
152,
26,
65,
125,
144,
86,
167,
41,
144,
158,
204,
76,
46,
181,
110
]
}
}

212
test/fixtures/fix8.json vendored Normal file
View File

@@ -0,0 +1,212 @@
{
"input": {
"type": "Buffer",
"data": [
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75,
75
]
},
"output": {
"type": "Buffer",
"data": [
153,
161,
201,
66,
87,
46,
242,
87,
136,
238,
151,
198,
129,
122,
62,
205,
142,
252,
109,
187,
238,
183,
63,
95,
240,
97,
220,
209,
37,
144,
237,
84,
251,
149,
152,
222,
89,
200,
208,
119,
213,
38,
65,
19,
2,
252,
193,
125,
94,
76,
182,
198,
243,
121,
253,
16,
45,
254,
82,
47,
146,
206,
41,
105,
254,
237,
236,
141,
118,
214,
197,
228,
59,
125,
45,
200,
61,
24,
110,
26,
235,
92,
175,
255,
85,
119,
57,
160,
145,
107,
206,
28,
62,
10,
166,
89,
7,
20,
246,
243
]
}
}

212
test/fixtures/fix9.json vendored Normal file
View File

@@ -0,0 +1,212 @@
{
"input": {
"type": "Buffer",
"data": [
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35,
35
]
},
"output": {
"type": "Buffer",
"data": [
79,
215,
22,
29,
132,
130,
214,
248,
237,
124,
234,
54,
215,
78,
0,
211,
246,
222,
54,
91,
53,
2,
49,
40,
0,
202,
188,
25,
184,
121,
164,
235,
189,
97,
14,
0,
83,
166,
88,
62,
55,
49,
79,
153,
136,
193,
133,
206,
172,
99,
207,
73,
246,
216,
192,
107,
50,
206,
167,
242,
25,
180,
63,
184,
201,
61,
90,
242,
223,
192,
13,
140,
31,
240,
112,
157,
250,
90,
142,
3,
40,
12,
106,
63,
73,
42,
79,
82,
20,
41,
187,
173,
23,
85,
59,
253,
212,
191,
109,
46
]
}
}

20
test/fixtures/go-aes.js vendored Normal file
View File

@@ -0,0 +1,20 @@
'use strict'
module.exports = {
16: {
inputs: [
[47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47],
[24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25],
[48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48]
],
outputs: [
[121, 104, 0, 151, 57, 137, 22, 239, 234, 151, 58, 15, 100, 22, 228, 110, 85, 248, 249, 15, 145, 128, 223, 25, 192, 175, 132, 169, 98, 203, 231, 106, 224, 102, 206, 244, 29, 213, 36, 2, 26, 213, 94, 29, 134, 219, 136, 73, 212, 176, 33, 95, 198, 91, 148, 139, 132, 252, 182, 115, 116, 160, 146, 194, 0, 97, 181, 0, 193, 149, 21, 51, 248, 97, 32, 62, 86, 153, 238, 67, 38, 34, 55, 143, 70, 193, 99, 107, 31, 67, 90, 97, 55, 63, 69, 203, 33, 233, 74, 237],
[109, 172, 127, 179, 110, 222, 79, 219, 87, 76, 70, 204, 166, 110, 184, 229, 90, 49, 160, 252, 78, 58, 73, 51, 152, 218, 3, 200, 10, 124, 152, 117, 137, 40, 23, 127, 56, 57, 203, 31, 101, 227, 31, 198, 223, 86, 98, 120, 100, 86, 116, 144, 142, 127, 68, 175, 249, 232, 22, 83, 22, 68, 60, 230, 146, 22, 153, 193, 67, 5, 51, 253, 239, 210, 80, 31, 254, 103, 185, 145, 123, 99, 205, 175, 156, 144, 191, 67, 31, 236, 43, 98, 197, 235, 31, 50, 69, 228, 100, 64],
[191, 93, 126, 191, 180, 42, 118, 144, 28, 188, 211, 191, 211, 130, 170, 153, 134, 240, 179, 83, 75, 23, 42, 68, 158, 200, 123, 155, 57, 169, 152, 133, 33, 114, 90, 29, 131, 91, 70, 105, 83, 45, 40, 47, 77, 96, 97, 8, 40, 1, 110, 245, 106, 172, 152, 146, 5, 114, 132, 0, 179, 31, 44, 78, 19, 109, 92, 199, 226, 36, 12, 74, 180, 241, 224, 107, 83, 13, 167, 27, 251, 101, 193, 98, 49, 90, 225, 197, 75, 213, 144, 52, 235, 130, 92, 247, 219, 139, 209, 132],
[191, 110, 53, 190, 250, 44, 190, 136, 22, 209, 131, 200, 112, 31, 61, 183, 247, 95, 4, 249, 69, 147, 238, 74, 18, 71, 197, 115, 141, 226, 102, 92, 91, 128, 181, 172, 67, 109, 17, 165, 52, 10, 55, 31, 55, 225, 253, 140, 154, 35, 104, 62, 119, 103, 197, 152, 125, 134, 140, 181, 170, 76, 75, 114, 195, 188, 68, 197, 28, 47, 116, 82, 34, 128, 232, 122, 14, 229, 122, 161, 36, 212, 161, 164, 145, 86, 215, 233, 222, 50, 143, 89, 131, 32, 130, 196, 109, 36, 204, 254],
[97, 142, 8, 37, 69, 173, 124, 180, 97, 70, 45, 91, 196, 126, 184, 135, 213, 104, 171, 89, 231, 63, 43, 42, 7, 245, 75, 165, 205, 182, 24, 50, 170, 217, 128, 112, 114, 215, 209, 145, 135, 235, 179, 212, 5, 81, 142, 199, 53, 221, 39, 239, 167, 21, 237, 168, 145, 249, 250, 108, 2, 247, 89, 73, 228, 227, 255, 155, 121, 157, 205, 96, 43, 32, 112, 209, 173, 96, 143, 43, 220, 140, 26, 205, 34, 34, 53, 157, 41, 167, 125, 235, 243, 85, 13, 14, 93, 109, 233, 186]
]
}
}

View File

@@ -2,13 +2,12 @@
module.exports = {
curve: 'P-256',
curveJs: 'p256',
bob: {
private: [
231, 236, 69, 16, 13, 92, 76, 83, 75, 40, 32, 71, 235, 187, 29, 214, 98, 231, 42, 5, 80, 89, 58, 175, 8, 95, 86, 50, 44, 214, 4, 172
],
public: new Buffer([
4, 160, 169, 215, 85, 152, 11, 209, 69, 105, 17, 51, 49, 83, 214, 171, 157, 73, 165, 85, 28, 196, 161, 234, 87, 149, 139, 76, 123, 37, 174, 194, 67, 167, 18, 34, 164, 35, 171, 164, 238, 141, 199, 206, 86, 130, 183, 88, 63, 121, 110, 150, 229, 10, 213, 176, 181, 1, 98, 20, 246, 85, 212, 200, 229
private: Buffer.from([
181, 217, 162, 151, 225, 36, 53, 253, 107, 66, 27, 27, 232, 72, 0, 0, 103, 167, 84, 62, 203, 91, 97, 137, 131, 193, 230, 126, 98, 242, 216, 170
]),
public: Buffer.from([
4, 53, 59, 128, 56, 162, 250, 72, 141, 206, 117, 232, 57, 96, 39, 39, 247, 7, 27, 57, 251, 232, 120, 186, 21, 239, 176, 139, 195, 129, 125, 85, 11, 188, 191, 32, 227, 0, 6, 163, 101, 68, 208, 1, 43, 131, 124, 112, 102, 91, 104, 79, 16, 119, 152, 208, 4, 147, 155, 83, 20, 146, 104, 55, 90
])
}
}

28
test/fixtures/go-key-ed25519.js vendored Normal file
View File

@@ -0,0 +1,28 @@
'use strict'
module.exports = {
// These were generated in a gore (https://github.com/motemen/gore) repl session:
//
// :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)
//
// :import io/ioutil
// ioutil.WriteFile("/tmp/pubkey_go.bin", pubkeyBytes, 0644)
// // etc..
//
// 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: {
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])
}
}

View File

@@ -2,19 +2,30 @@
module.exports = {
private: {
hash: new Buffer([
hash: Buffer.from([
18, 32, 168, 125, 165, 65, 34, 157, 209, 4, 24, 158, 80, 196, 125, 86, 103, 0, 228, 145, 109, 252, 153, 7, 189, 9, 16, 37, 239, 36, 48, 78, 214, 212
]),
key: new Buffer([
key: Buffer.from([
8, 0, 18, 192, 2, 48, 130, 1, 60, 2, 1, 0, 2, 65, 0, 230, 157, 160, 242, 74, 222, 87, 0, 77, 180, 91, 175, 217, 166, 2, 95, 193, 239, 195, 140, 224, 57, 84, 207, 46, 172, 113, 196, 20, 133, 117, 205, 45, 7, 224, 41, 40, 195, 254, 124, 14, 84, 223, 147, 67, 198, 48, 36, 53, 161, 112, 46, 153, 90, 19, 123, 94, 247, 5, 116, 1, 238, 32, 15, 2, 3, 1, 0, 1, 2, 65, 0, 191, 59, 140, 255, 254, 23, 123, 91, 148, 19, 240, 71, 213, 26, 181, 51, 68, 181, 150, 153, 214, 65, 148, 83, 45, 103, 239, 250, 225, 237, 125, 173, 111, 244, 37, 124, 87, 178, 86, 10, 14, 207, 63, 105, 213, 37, 81, 23, 230, 4, 222, 179, 144, 40, 252, 163, 190, 7, 241, 221, 28, 54, 225, 209, 2, 33, 0, 235, 132, 229, 150, 99, 182, 176, 194, 198, 65, 210, 160, 184, 70, 82, 49, 235, 199, 14, 11, 92, 66, 237, 45, 220, 72, 235, 1, 244, 145, 205, 57, 2, 33, 0, 250, 171, 146, 180, 188, 194, 14, 152, 52, 64, 38, 52, 158, 86, 46, 109, 66, 100, 122, 43, 88, 167, 143, 98, 104, 143, 160, 60, 171, 185, 31, 135, 2, 33, 0, 206, 47, 255, 203, 100, 170, 137, 31, 75, 240, 78, 84, 212, 95, 4, 16, 158, 73, 27, 27, 136, 255, 50, 163, 166, 169, 211, 204, 87, 111, 217, 201, 2, 33, 0, 177, 51, 194, 213, 3, 175, 7, 84, 47, 115, 189, 206, 106, 180, 47, 195, 203, 48, 110, 112, 224, 14, 43, 189, 124, 127, 51, 222, 79, 226, 225, 87, 2, 32, 67, 23, 190, 222, 106, 22, 115, 139, 217, 244, 178, 53, 153, 99, 5, 176, 72, 77, 193, 61, 67, 134, 37, 238, 69, 66, 159, 28, 39, 5, 238, 125
])
},
public: {
hash: new Buffer([
hash: Buffer.from([
18, 32, 112, 151, 163, 167, 204, 243, 175, 123, 208, 162, 90, 84, 199, 174, 202, 110, 0, 119, 27, 202, 7, 149, 161, 251, 215, 168, 163, 54, 93, 54, 195, 20
]),
key: new Buffer([
key: Buffer.from([
8, 0, 18, 94, 48, 92, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0, 3, 75, 0, 48, 72, 2, 65, 0, 230, 157, 160, 242, 74, 222, 87, 0, 77, 180, 91, 175, 217, 166, 2, 95, 193, 239, 195, 140, 224, 57, 84, 207, 46, 172, 113, 196, 20, 133, 117, 205, 45, 7, 224, 41, 40, 195, 254, 124, 14, 84, 223, 147, 67, 198, 48, 36, 53, 161, 112, 46, 153, 90, 19, 123, 94, 247, 5, 116, 1, 238, 32, 15, 2, 3, 1, 0, 1
])
},
verify: {
signature: Buffer.from([
3, 116, 81, 57, 91, 194, 7, 1, 230, 236, 229, 142, 36, 209, 208, 107, 47, 52, 164, 236, 139, 35, 155, 97, 43, 64, 145, 91, 19, 218, 149, 63, 99, 164, 191, 110, 145, 37, 18, 7, 98, 112, 144, 35, 29, 186, 169, 150, 165, 88, 145, 170, 197, 110, 42, 163, 188, 10, 42, 63, 34, 93, 91, 94, 199, 110, 10, 82, 238, 80, 93, 93, 77, 130, 22, 216, 229, 172, 36, 229, 82, 162, 20, 78, 19, 46, 82, 243, 43, 80, 115, 125, 145, 231, 194, 224, 30, 187, 55, 228, 74, 52, 203, 191, 254, 148, 136, 218, 62, 147, 171, 130, 251, 181, 105, 29, 238, 207, 197, 249, 61, 105, 202, 172, 160, 174, 43, 124, 115, 130, 169, 30, 76, 41, 52, 200, 2, 26, 53, 190, 43, 20, 203, 10, 217, 250, 47, 102, 92, 103, 197, 22, 108, 184, 74, 218, 82, 202, 180, 98, 13, 114, 12, 92, 1, 139, 150, 170, 8, 92, 32, 116, 168, 219, 157, 162, 28, 77, 29, 29, 74, 136, 144, 49, 173, 245, 253, 76, 167, 200, 169, 163, 7, 49, 133, 120, 99, 191, 53, 10, 66, 26, 234, 240, 139, 235, 134, 30, 55, 248, 150, 100, 242, 150, 159, 198, 44, 78, 150, 7, 133, 139, 59, 76, 3, 225, 94, 13, 89, 122, 34, 95, 95, 107, 74, 169, 171, 169, 222, 25, 191, 182, 148, 116, 66, 67, 102, 12, 193, 217, 247, 243, 148, 233, 161, 157
]),
data: Buffer.from([
10, 16, 27, 128, 228, 220, 147, 176, 53, 105, 175, 171, 32, 213, 35, 236, 203, 60, 18, 171, 2, 8, 0, 18, 166, 2, 48, 130, 1, 34, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0, 3, 130, 1, 15, 0, 48, 130, 1, 10, 2, 130, 1, 1, 0, 181, 113, 138, 108, 208, 103, 166, 102, 37, 36, 204, 250, 228, 165, 44, 64, 176, 210, 205, 141, 241, 55, 200, 110, 98, 68, 85, 199, 254, 19, 86, 204, 63, 250, 167, 38, 59, 249, 146, 228, 73, 171, 63, 18, 96, 104, 191, 137, 186, 244, 255, 90, 16, 119, 195, 52, 177, 213, 254, 187, 174, 84, 174, 173, 12, 236, 53, 234, 3, 209, 82, 37, 78, 111, 214, 135, 76, 195, 9, 242, 134, 188, 153, 84, 139, 231, 51, 146, 177, 60, 12, 25, 158, 91, 215, 152, 7, 0, 84, 35, 36, 230, 227, 67, 198, 72, 50, 110, 37, 209, 98, 193, 65, 93, 173, 199, 4, 198, 102, 99, 148, 144, 224, 217, 114, 53, 144, 245, 251, 114, 211, 20, 82, 163, 123, 75, 16, 192, 106, 213, 128, 2, 11, 200, 203, 84, 41, 199, 224, 155, 171, 217, 64, 109, 116, 188, 151, 183, 173, 52, 205, 164, 93, 13, 251, 65, 182, 160, 154, 185, 239, 33, 184, 84, 159, 105, 101, 173, 194, 251, 123, 84, 92, 66, 61, 180, 45, 104, 162, 224, 214, 233, 64, 220, 165, 2, 104, 116, 150, 2, 234, 203, 112, 21, 124, 23, 48, 66, 30, 63, 30, 36, 246, 135, 203, 218, 115, 22, 189, 39, 39, 125, 205, 65, 222, 220, 77, 18, 84, 121, 161, 153, 125, 25, 139, 137, 170, 239, 150, 106, 119, 168, 216, 140, 113, 121, 26, 53, 118, 110, 53, 192, 244, 252, 145, 85, 2, 3, 1, 0, 1, 26, 17, 80, 45, 50, 53, 54, 44, 80, 45, 51, 56, 52, 44, 80, 45, 53, 50, 49, 34, 24, 65, 69, 83, 45, 50, 53, 54, 44, 65, 69, 83, 45, 49, 50, 56, 44, 66, 108, 111, 119, 102, 105, 115, 104, 42, 13, 83, 72, 65, 50, 53, 54, 44, 83, 72, 65, 53, 49, 50, 10, 16, 220, 83, 240, 105, 6, 203, 78, 83, 210, 115, 6, 106, 98, 82, 1, 161, 18, 171, 2, 8, 0, 18, 166, 2, 48, 130, 1, 34, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0, 3, 130, 1, 15, 0, 48, 130, 1, 10, 2, 130, 1, 1, 0, 185, 234, 19, 191, 164, 33, 65, 94, 87, 42, 74, 83, 224, 25, 142, 44, 26, 7, 92, 242, 189, 42, 170, 197, 178, 92, 45, 240, 107, 141, 128, 59, 122, 252, 48, 140, 4, 85, 85, 203, 3, 197, 8, 127, 120, 98, 44, 169, 135, 196, 70, 137, 117, 180, 177, 134, 170, 35, 165, 88, 105, 30, 114, 138, 11, 96, 68, 99, 18, 149, 223, 166, 105, 12, 176, 77, 48, 214, 22, 236, 17, 154, 213, 209, 158, 169, 202, 5, 100, 210, 83, 90, 201, 38, 205, 246, 231, 106, 63, 86, 222, 143, 157, 173, 62, 4, 85, 232, 20, 188, 6, 209, 186, 132, 192, 117, 146, 181, 233, 26, 0, 240, 138, 206, 91, 170, 114, 137, 217, 132, 139, 242, 144, 213, 103, 101, 190, 146, 188, 250, 188, 134, 255, 70, 125, 78, 65, 136, 239, 190, 206, 139, 155, 140, 163, 233, 170, 247, 205, 87, 209, 19, 29, 173, 10, 147, 43, 28, 90, 46, 6, 197, 217, 186, 66, 68, 126, 76, 64, 184, 8, 170, 23, 79, 243, 223, 119, 133, 118, 50, 226, 44, 246, 176, 10, 161, 219, 83, 54, 68, 248, 5, 14, 177, 114, 54, 63, 11, 71, 136, 142, 56, 151, 123, 230, 61, 80, 15, 180, 42, 49, 220, 148, 99, 231, 20, 230, 220, 85, 207, 187, 37, 210, 137, 171, 125, 71, 14, 53, 100, 91, 83, 209, 50, 132, 165, 253, 25, 161, 5, 97, 164, 163, 83, 95, 53, 2, 3, 1, 0, 1, 26, 17, 80, 45, 50, 53, 54, 44, 80, 45, 51, 56, 52, 44, 80, 45, 53, 50, 49, 34, 15, 65, 69, 83, 45, 50, 53, 54, 44, 65, 69, 83, 45, 49, 50, 56, 42, 13, 83, 72, 65, 50, 53, 54, 44, 83, 72, 65, 53, 49, 50, 4, 97, 54, 203, 112, 136, 34, 231, 162, 19, 154, 131, 27, 105, 26, 121, 238, 120, 25, 203, 66, 232, 53, 198, 20, 19, 96, 119, 218, 90, 64, 170, 3, 132, 116, 1, 87, 116, 232, 165, 161, 198, 117, 167, 60, 145, 1, 253, 108, 50, 150, 117, 8, 140, 133, 48, 30, 236, 36, 84, 186, 22, 144, 87, 101
]),
publicKey: Buffer.from([
8, 0, 18, 166, 2, 48, 130, 1, 34, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0, 3, 130, 1, 15, 0, 48, 130, 1, 10, 2, 130, 1, 1, 0, 181, 113, 138, 108, 208, 103, 166, 102, 37, 36, 204, 250, 228, 165, 44, 64, 176, 210, 205, 141, 241, 55, 200, 110, 98, 68, 85, 199, 254, 19, 86, 204, 63, 250, 167, 38, 59, 249, 146, 228, 73, 171, 63, 18, 96, 104, 191, 137, 186, 244, 255, 90, 16, 119, 195, 52, 177, 213, 254, 187, 174, 84, 174, 173, 12, 236, 53, 234, 3, 209, 82, 37, 78, 111, 214, 135, 76, 195, 9, 242, 134, 188, 153, 84, 139, 231, 51, 146, 177, 60, 12, 25, 158, 91, 215, 152, 7, 0, 84, 35, 36, 230, 227, 67, 198, 72, 50, 110, 37, 209, 98, 193, 65, 93, 173, 199, 4, 198, 102, 99, 148, 144, 224, 217, 114, 53, 144, 245, 251, 114, 211, 20, 82, 163, 123, 75, 16, 192, 106, 213, 128, 2, 11, 200, 203, 84, 41, 199, 224, 155, 171, 217, 64, 109, 116, 188, 151, 183, 173, 52, 205, 164, 93, 13, 251, 65, 182, 160, 154, 185, 239, 33, 184, 84, 159, 105, 101, 173, 194, 251, 123, 84, 92, 66, 61, 180, 45, 104, 162, 224, 214, 233, 64, 220, 165, 2, 104, 116, 150, 2, 234, 203, 112, 21, 124, 23, 48, 66, 30, 63, 30, 36, 246, 135, 203, 218, 115, 22, 189, 39, 39, 125, 205, 65, 222, 220, 77, 18, 84, 121, 161, 153, 125, 25, 139, 137, 170, 239, 150, 106, 119, 168, 216, 140, 113, 121, 26, 53, 118, 110, 53, 192, 244, 252, 145, 85, 2, 3, 1, 0, 1
])
}
}

View File

@@ -3,28 +3,28 @@
module.exports = [{
cipher: 'AES-256',
hash: 'SHA256',
secret: new Buffer([
secret: Buffer.from([
195, 191, 209, 165, 209, 201, 127, 122, 136, 111, 31, 66, 111, 68, 38, 155, 216, 204, 46, 181, 200, 188, 170, 204, 104, 74, 239, 251, 173, 114, 222, 234
]),
k1: {
iv: new Buffer([
iv: Buffer.from([
208, 132, 203, 169, 253, 52, 40, 83, 161, 91, 17, 71, 33, 136, 67, 96
]),
cipherKey: new Buffer([
cipherKey: Buffer.from([
156, 48, 241, 157, 92, 248, 153, 186, 114, 127, 195, 114, 106, 104, 215, 133, 35, 11, 131, 137, 123, 70, 74, 26, 15, 60, 189, 32, 67, 221, 115, 137
]),
macKey: new Buffer([
macKey: Buffer.from([
6, 179, 91, 245, 224, 56, 153, 120, 77, 140, 29, 5, 15, 213, 187, 65, 137, 230, 202, 120
])
},
k2: {
iv: new Buffer([
iv: Buffer.from([
236, 17, 34, 141, 90, 106, 197, 56, 197, 184, 157, 135, 91, 88, 112, 19
]),
cipherKey: new Buffer([
cipherKey: Buffer.from([
151, 145, 195, 219, 76, 195, 102, 109, 187, 231, 100, 150, 132, 245, 251, 130, 254, 37, 178, 55, 227, 34, 114, 39, 238, 34, 2, 193, 107, 130, 32, 87
]),
macKey: new Buffer([
macKey: Buffer.from([
3, 229, 77, 212, 241, 217, 23, 113, 220, 126, 38, 255, 18, 117, 108, 205, 198, 89, 1, 236
])
}

8
test/fixtures/secp256k1.js vendored Normal file
View File

@@ -0,0 +1,8 @@
'use strict'
module.exports = {
// protobuf marshaled key pair generated with libp2p-crypto-secp256k1
// and marshaled with libp2p-crypto.marshalPublicKey / marshalPrivateKey
pbmPrivateKey: Buffer.from('08021220e0600103010000000100000000000000be1dc82c2e000000e8d6030301000000', 'hex'),
pbmPublicKey: Buffer.from('0802122103a9a7272a726fa083abf31ba44037f8347fbc5e5d3113d62a7c6bc26752fd8ee1', 'hex')
}

View File

@@ -0,0 +1,32 @@
/* eslint-env mocha */
'use strict'
const util = require('util')
const garbage = [Buffer.from('00010203040506070809', 'hex'), {}, null, false, undefined, true, 1, 0, Buffer.from(''), 'aGVsbG93b3JsZA==', 'helloworld', '']
function doTests (fncName, fnc, num, skipBuffersAndStrings) {
if (!num) {
num = 1
}
garbage.forEach((garbage) => {
if (skipBuffersAndStrings && (Buffer.isBuffer(garbage) || (typeof garbage) === 'string')) {
// skip this garbage because it's a buffer or a string and we were told do do that
return
}
const args = []
for (let i = 0; i < num; i++) {
args.push(garbage)
}
it(fncName + '(' + args.map(garbage => util.inspect(garbage)).join(', ') + ')', async () => {
try {
await fnc.apply(null, args)
} catch (err) {
return // expected
}
throw new Error('Expected error to be thrown')
})
})
}
module.exports = { doTests }

22
test/hmac/hmac.spec.js Normal file
View File

@@ -0,0 +1,22 @@
/* eslint max-nested-callbacks: ["error", 8] */
/* eslint-env mocha */
'use strict'
const chai = require('chai')
const dirtyChai = require('dirty-chai')
const expect = chai.expect
chai.use(dirtyChai)
const crypto = require('../../src')
const hashes = ['SHA1', 'SHA256', 'SHA512']
describe('HMAC', () => {
hashes.forEach((hash) => {
it(`${hash} - sign and verify`, async () => {
const hmac = await crypto.hmac.create(hash, Buffer.from('secret'))
const sig = await hmac.digest(Buffer.from('hello world'))
expect(sig).to.have.length(hmac.length)
})
})
})

View File

@@ -1,70 +0,0 @@
/* eslint-env mocha */
'use strict'
const expect = require('chai').expect
const crypto = require('../src')
const fixtures = require('./fixtures/go-key-rsa')
describe('libp2p-crypto', () => {
let key
before(() => {
key = crypto.generateKeyPair('RSA', 2048)
})
it('marshalPublicKey and unmarshalPublicKey', () => {
const key2 = crypto.unmarshalPublicKey(crypto.marshalPublicKey(key.public))
expect(key2.equals(key.public)).to.be.eql(true)
})
it('marshalPrivateKey and unmarshalPrivateKey', () => {
const key2 = crypto.unmarshalPrivateKey(crypto.marshalPrivateKey(key))
expect(key2.equals(key)).to.be.eql(true)
})
describe('go interop', () => {
it('unmarshals private key', () => {
const key = crypto.unmarshalPrivateKey(fixtures.private.key)
const hash = fixtures.private.hash
expect(
key.hash()
).to.be.eql(
hash
)
})
it('unmarshals public key', () => {
const key = crypto.unmarshalPublicKey(fixtures.public.key)
const hash = fixtures.public.hash
expect(
key.hash()
).to.be.eql(
hash
)
})
it('unmarshal -> marshal, private key', () => {
const key = crypto.unmarshalPrivateKey(fixtures.private.key)
const marshalled = crypto.marshalPrivateKey(key)
expect(
fixtures.private.key.equals(marshalled)
).to.be.eql(
true
)
})
it('unmarshal -> marshal, public key', () => {
const key = crypto.unmarshalPublicKey(fixtures.public.key)
const marshalled = crypto.marshalPublicKey(key)
expect(
fixtures.public.key.equals(marshalled)
).to.be.eql(
true
)
})
})
})

View File

@@ -1,45 +0,0 @@
/* eslint-env mocha */
'use strict'
const expect = require('chai').expect
const crypto = require('../src')
const fixtures = require('./fixtures/go-stretch-key')
describe('keyStretcher', () => {
describe('generate', () => {
const ciphers = ['AES-128', 'AES-256', 'Blowfish']
const hashes = ['SHA1', 'SHA256', 'SHA512']
const res = crypto.generateEphemeralKeyPair('P-256')
const secret = res.genSharedKey(res.key)
ciphers.forEach((cipher) => {
hashes.forEach((hash) => {
it(`${cipher} - ${hash}`, () => {
const keys = crypto.keyStretcher(cipher, hash, secret)
expect(keys.k1).to.exist
expect(keys.k2).to.exist
})
})
})
})
describe('go interop', () => {
fixtures.forEach((test) => {
it(`${test.cipher} - ${test.hash}`, () => {
const cipher = test.cipher
const hash = test.hash
const secret = test.secret
const keys = crypto.keyStretcher(cipher, hash, secret)
expect(keys.k1.iv).to.be.eql(test.k1.iv)
expect(keys.k1.cipherKey).to.be.eql(test.k1.cipherKey)
expect(keys.k1.macKey).to.be.eql(test.k1.macKey)
expect(keys.k2.iv).to.be.eql(test.k2.iv)
expect(keys.k2.cipherKey).to.be.eql(test.k2.cipherKey)
expect(keys.k2.macKey).to.be.eql(test.k2.macKey)
})
})
})
})

154
test/keys/ed25519.spec.js Normal file
View File

@@ -0,0 +1,154 @@
/* eslint-env mocha */
'use strict'
const chai = require('chai')
const dirtyChai = require('dirty-chai')
const expect = chai.expect
chai.use(dirtyChai)
const crypto = require('../../src')
const ed25519 = crypto.keys.supportedKeys.ed25519
const fixtures = require('../fixtures/go-key-ed25519')
const testGarbage = require('../helpers/test-garbage-error-handling')
/** @typedef {import("libp2p-crypto").PrivateKey} PrivateKey */
describe('ed25519', function () {
this.timeout(20 * 1000)
// @ts-check
/**
* @type {PrivateKey}
*/
let key
before(async () => {
key = await crypto.keys.generateKeyPair('Ed25519', 512)
})
it('generates a valid key', async () => {
expect(key).to.be.an.instanceof(ed25519.Ed25519PrivateKey)
const digest = await key.hash()
expect(digest).to.have.length(34)
})
it('generates a valid key from seed', async () => {
var seed = crypto.randomBytes(32)
const seededkey = await crypto.keys.generateKeyPairFromSeed('Ed25519', seed, 512)
expect(seededkey).to.be.an.instanceof(ed25519.Ed25519PrivateKey)
const digest = await seededkey.hash()
expect(digest).to.have.length(34)
})
it('generates the same key from the same seed', async () => {
const seed = crypto.randomBytes(32)
const seededkey1 = await crypto.keys.generateKeyPairFromSeed('Ed25519', seed, 512)
const seededkey2 = await crypto.keys.generateKeyPairFromSeed('Ed25519', seed, 512)
expect(seededkey1.equals(seededkey2)).to.eql(true)
expect(seededkey1.public.equals(seededkey2.public)).to.eql(true)
})
it('generates different keys for different seeds', async () => {
const seed1 = crypto.randomBytes(32)
const seededkey1 = await crypto.keys.generateKeyPairFromSeed('Ed25519', seed1, 512)
const seed2 = crypto.randomBytes(32)
const seededkey2 = await crypto.keys.generateKeyPairFromSeed('Ed25519', seed2, 512)
expect(seededkey1.equals(seededkey2)).to.eql(false)
expect(seededkey1.public.equals(seededkey2.public)).to.eql(false)
})
it('signs', async () => {
const text = crypto.randomBytes(512)
const sig = await key.sign(text)
const res = await key.public.verify(text, sig)
expect(res).to.be.eql(true)
})
it('encoding', async () => {
const keyMarshal = key.marshal()
const key2 = await ed25519.unmarshalEd25519PrivateKey(keyMarshal)
const keyMarshal2 = key2.marshal()
expect(keyMarshal).to.eql(keyMarshal2)
const pk = key.public
const pkMarshal = pk.marshal()
const pk2 = ed25519.unmarshalEd25519PublicKey(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 crypto.keys.generateKeyPair('Ed25519', 512)
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.be.eql(false)
})
describe('throws error instead of crashing', () => {
const key = crypto.keys.unmarshalPublicKey(fixtures.verify.publicKey)
testGarbage.doTests('key.verify', key.verify.bind(key), 2, null)
testGarbage.doTests('crypto.keys.unmarshalPrivateKey', crypto.keys.unmarshalPrivateKey.bind(crypto.keys), null, null)
})
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('generates the same signature as go', async () => {
const sig = await privateKey.sign(fixtures.verify.data)
expect(sig).to.eql(fixtures.verify.signature)
})
})
})

View File

@@ -0,0 +1,77 @@
/* eslint max-nested-callbacks: ["error", 8] */
/* eslint-env mocha */
'use strict'
const chai = require('chai')
const dirtyChai = require('dirty-chai')
const expect = chai.expect
chai.use(dirtyChai)
const fixtures = require('../fixtures/go-elliptic-key')
const crypto = require('../../src')
const curves = ['P-256', 'P-384'] // 'P-521' fails in tests :( no clue why
// @ts-check
/**
* @type {Record<string, number>}
*/
const lengths = {
'P-256': 65,
'P-384': 97,
'P-521': 133
}
const secretLengths = {
'P-256': 32,
'P-384': 48,
'P-521': 66
}
describe('generateEphemeralKeyPair', () => {
curves.forEach((curve) => {
it(`generate and shared key ${curve}`, async () => {
const keys = await Promise.all([
crypto.keys.generateEphemeralKeyPair(curve),
crypto.keys.generateEphemeralKeyPair(curve)
])
expect(keys[0].key).to.have.length(lengths[curve])
expect(keys[1].key).to.have.length(lengths[curve])
const shared = await keys[0].genSharedKey(keys[1].key)
expect(shared).to.have.length(secretLengths[curve])
})
})
describe('go interop', () => {
it('generates a shared secret', async () => {
const curve = fixtures.curve
const keys = await Promise.all([
crypto.keys.generateEphemeralKeyPair(curve),
crypto.keys.generateEphemeralKeyPair(curve)
])
const alice = keys[0]
const bob = keys[1]
bob.key = fixtures.bob.public
const secrets = await Promise.all([
alice.genSharedKey(bob.key),
bob.genSharedKey(alice.key, fixtures.bob)
])
expect(secrets[0]).to.eql(secrets[1])
expect(secrets[0]).to.have.length(32)
})
})
it('handles bad curve name', async () => {
try {
await crypto.keys.generateEphemeralKeyPair('bad name')
} catch (err) {
expect(err.code).equals('ERR_INVALID_CURVE')
return
}
expect.fail('Did not throw error')
})
})

View File

@@ -0,0 +1,66 @@
/* eslint max-nested-callbacks: ["error", 8] */
/* eslint-env mocha */
'use strict'
const chai = require('chai')
const dirtyChai = require('dirty-chai')
const expect = chai.expect
chai.use(dirtyChai)
const { expectErrCode } = require('../util')
const crypto = require('../../src')
const fixtures = require('../fixtures/go-stretch-key')
describe('keyStretcher', () => {
describe('generate', () => {
const ciphers = ['AES-128', 'AES-256', 'Blowfish']
const hashes = ['SHA1', 'SHA256', 'SHA512']
let res
// @ts-check
/**
* @type {Buffer}
*/
let secret
before(async () => {
res = await crypto.keys.generateEphemeralKeyPair('P-256')
secret = await res.genSharedKey(res.key)
})
ciphers.forEach((cipher) => {
hashes.forEach((hash) => {
it(`${cipher} - ${hash}`, async () => {
const keys = await crypto.keys.keyStretcher(cipher, hash, secret)
expect(keys.k1).to.exist()
expect(keys.k2).to.exist()
})
})
})
it('handles invalid cipher type', () => {
return expectErrCode(crypto.keys.keyStretcher('invalid-cipher', 'SHA256', 'secret'), 'ERR_INVALID_CIPHER_TYPE')
})
it('handles missing hash type', () => {
return expectErrCode(crypto.keys.keyStretcher('AES-128', '', 'secret'), 'ERR_MISSING_HASH_TYPE')
})
})
describe('go interop', () => {
fixtures.forEach((test) => {
it(`${test.cipher} - ${test.hash}`, async () => {
const cipher = test.cipher
const hash = test.hash
const secret = test.secret
const keys = await crypto.keys.keyStretcher(cipher, hash, secret)
expect(keys.k1.iv).to.be.eql(test.k1.iv)
expect(keys.k1.cipherKey).to.be.eql(test.k1.cipherKey)
expect(keys.k1.macKey).to.be.eql(test.k1.macKey)
expect(keys.k2.iv).to.be.eql(test.k2.iv)
expect(keys.k2.cipherKey).to.be.eql(test.k2.cipherKey)
expect(keys.k2.macKey).to.be.eql(test.k2.macKey)
})
})
})
})

View File

@@ -0,0 +1,53 @@
'use strict'
/* eslint-env mocha */
/* eslint max-nested-callbacks: ["error", 8] */
const chai = require('chai')
const dirtyChai = require('dirty-chai')
const expect = chai.expect
chai.use(dirtyChai)
chai.use(require('chai-string'))
const LIBS = ['ursa', 'keypair']
describe('RSA crypto libs', function () {
this.timeout(20 * 1000)
LIBS.forEach(lib => {
describe(lib, () => {
let crypto
let rsa
before(() => {
process.env.LP2P_FORCE_CRYPTO_LIB = lib
for (const path in require.cache) { // clear module cache
if (path.endsWith('.js')) {
delete require.cache[path]
}
}
crypto = require('../../src')
rsa = crypto.keys.supportedKeys.rsa
})
it('generates a valid key', async () => {
const key = await crypto.keys.generateKeyPair('RSA', 512)
expect(key).to.be.an.instanceof(rsa.RsaPrivateKey)
const digest = await key.hash()
expect(digest).to.have.length(34)
})
after(() => {
for (const path in require.cache) { // clear module cache
if (path.endsWith('.js')) {
delete require.cache[path]
}
}
delete process.env.LP2P_FORCE_CRYPTO_LIB
})
})
})
})

384
test/keys/rsa.spec.js Normal file
View File

@@ -0,0 +1,384 @@
/* eslint max-nested-callbacks: ["error", 8] */
/* eslint-env mocha */
'use strict'
const chai = require('chai')
const dirtyChai = require('dirty-chai')
const expect = chai.expect
chai.use(dirtyChai)
chai.use(require('chai-string'))
const { expectErrCode } = require('../util')
const crypto = require('../../src')
const rsa = crypto.keys.supportedKeys.rsa
const fixtures = require('../fixtures/go-key-rsa')
const testGarbage = require('../helpers/test-garbage-error-handling')
/** @typedef {import('libp2p-crypto').keys.supportedKeys.rsa.RsaPrivateKey} RsaPrivateKey */
describe('RSA', function () {
this.timeout(20 * 1000)
// @ts-check
/**
* @type {RsaPrivateKey}
*/
let key
before(async () => {
key = await rsa.generateKeyPair(512)
})
it('generates a valid key', async () => {
expect(key).to.be.an.instanceof(rsa.RsaPrivateKey)
const digest = await key.hash()
expect(digest).to.have.length(34)
})
it('signs', async () => {
const text = key.genSecret()
const sig = await key.sign(text)
const res = await key.public.verify(text, sig)
expect(res).to.be.eql(true)
})
it('encoding', async () => {
const keyMarshal = key.marshal()
const key2 = await rsa.unmarshalRsaPrivateKey(keyMarshal)
const keyMarshal2 = key2.marshal()
expect(keyMarshal).to.eql(keyMarshal2)
const pk = key.public
const pkMarshal = pk.marshal()
const pk2 = rsa.unmarshalRsaPublicKey(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 crypto.keys.generateKeyPair('RSA', 512)
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.be.eql(true)
})
it('encrypt and decrypt', async () => {
const data = Buffer.from('hello world')
const enc = await key.public.encrypt(data)
const dec = await key.decrypt(enc)
expect(dec).to.be.eql(data)
})
it('encrypt decrypt browser/node interop', async () => {
// @ts-check
/**
* @type {any}
*/
const id = await crypto.keys.unmarshalPrivateKey(Buffer.from('CAASqAkwggSkAgEAAoIBAQCk0O+6oNRxhcdZe2GxEDrFBkDV4TZFZnp2ly/dL1cGMBql/8oXPZgei6h7+P5zzfDq2YCfwbjbf0IVY1AshRl6B5VGE1WS+9p1y1OZxJf5os6V1ENnTi6FTcyuBl4BN8dmIKOif0hqgqflaT5OhfYZDXfbJyVQj4vb2+Stu2Xpph3nwqAnTw/7GC/7jrt2Cq6Tu1PoZi36wSwEPYW3eQ1HAYxZjTYYDXl2iyHygnTcbkGRwAQ7vjk+mW7u60zyoolCm9f6Y7c/orJ33DDUocbaGJLlHcfd8bioBwaZy/2m7q43X8pQs0Q1/iwUt0HHZj1YARmHKbh0zR31ciFiV37dAgMBAAECggEADtJBNKnA4QKURj47r0YT2uLwkqtBi6UnDyISalQXAdXyl4n0nPlrhBewC5H9I+HZr+zmTbeIjaiYgz7el1pSy7AB4v7bG7AtWZlyx6mvtwHGjR+8/f3AXjl8Vgv5iSeAdXUq8fJ7SyS7v3wi38HZOzCEXj9bci6ud5ODMYJgLE4gZD0+i1+/V9cpuYfGpS/gLTLEMQLiw/9o8NSZ7sAnxg0UlYhotqaQY23hvXPBOe+0oa95zl2n6XTxCafa3dQl/B6CD1tUq9dhbQew4bxqMq/mhRO9pREEqZ083Uh+u4PTc1BeHgIQaS864pHPb+AY1F7KDvPtHhdojnghp8d70QKBgQDeRYFxo6sd04ohY86Z/i9icVYIyCvfXAKnaMKeGUjK7ou6sDJwFX8W97+CzXpZ/vffsk/l5GGhC50KqrITxHAy/h5IjyDODfps7NMIp0Dm9sO4PWibbw3OOVBRc8w3b3i7I8MrUUA1nLHE1T1HA1rKOTz5jYhE0fi9XKiT1ciKOQKBgQC903w+n9y7M7eaMW7Z5/13kZ7PS3HlM681eaPrk8J4J+c6miFF40/8HOsmarS38v0fgTeKkriPz5A7aLzRHhSiOnp350JNM6c3sLwPEs2qx/CRuWWx1rMERatfDdUH6mvlK6QHu0QgSfQR27EO6a6XvVSJXbvFmimjmtIaz/IpxQKBgQDWJ9HYVAGC81abZTaiWK3/A4QJYhQjWNuVwPICsgnYvI4Uib+PDqcs0ffLZ38DRw48kek5bxpBuJbOuDhro1EXUJCNCJpq7jzixituovd9kTRyR3iKii2bDM2+LPwOTXDdnk9lZRugjCEbrPkleq33Ob7uEtfAty4aBTTHe6uEwQKBgQCB+2q8RyMSXNuADhFlzOFXGrOwJm0bEUUMTPrduRQUyt4e1qOqA3klnXe3mqGcxBpnlEe/76/JacvNom6Ikxx16a0qpYRU8OWz0KU1fR6vrrEgV98241k5t6sdL4+MGA1Bo5xyXtzLb1hdUh3vpDwVU2OrnC+To3iXus/b5EBiMQKBgEI1OaBcFiyjgLGEyFKoZbtzH1mdatTExfrAQqCjOVjQByoMpGhHTXwEaosvyYu63Pa8AJPT7juSGaiKYEJFcXO9BiNyVfmQiqSHJcYeuh+fmO9IlHRHgy5xaIIC00AHS2vC/gXwmXAdPis6BZqDJeiCuOLWJ94QXn8JBT8IgGAI', 'base64'))
const msg = Buffer.from('hello')
// browser
const dec1 = id.decrypt(Buffer.from('YRFUDx8UjbWSfDS84cDA4WowaaOmd1qFNAv5QutodCKYb9uPtU/tDiAvJzOGu5DCJRo2J0l/35P2weiB4/C2Cb1aZgXKMx/QQC+2jSJiymhqcZaYerjTvkCFwkjCaqthoVo/YXxsaFZ1q7bdTZUDH1TaJR7hWfSyzyPcA8c0w43MIsw16pY8ZaPSclvnCwhoTg1JGjMk6te3we7+wR8QU7VrPhs54mZWxrpu3NQ8xZ6xQqIedsEiNhBUccrCSzYghgsP0Ae/8iKyGyl3U6IegsJNn8jcocvzOJrmU03rgIFPjvuBdaqB38xDSTjbA123KadB28jNoSZh18q/yH3ZIg==', 'base64'))
expect(dec1).to.be.eql(msg)
// node
const dec2 = id.decrypt(Buffer.from('e6yxssqXsWc27ozDy0PGKtMkCS28KwFyES2Ijz89yiz+w6bSFkNOhHPKplpPzgQEuNoUGdbseKlJFyRYHjIT8FQFBHZM8UgSkgoimbY5on4xSxXs7E5/+twjqKdB7oNveTaTf7JCwaeUYnKSjbiYFEawtMiQE91F8sTT7TmSzOZ48tUhnddAAZ3Ac/O3Z9MSAKOCDipi+JdZtXRT8KimGt36/7hjjosYmPuHR1Xy/yMTL6SMbXtBM3yAuEgbQgP+q/7kHMHji3/JvTpYdIUU+LVtkMusXNasRA+UWG2zAht18vqjFMsm9JTiihZw9jRHD4vxAhf75M992tnC+0ZuQg==', 'base64'))
expect(dec2).to.be.eql(msg)
})
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.be.eql(false)
})
describe('export and import', () => {
it('password protected PKCS #8', async () => {
const pem = await key.export('my secret', 'pkcs-8')
expect(pem).to.startsWith('-----BEGIN ENCRYPTED PRIVATE KEY-----')
const clone = await crypto.keys.import(pem, 'my secret')
expect(clone).to.exist()
expect(key.equals(clone)).to.eql(true)
})
it('defaults to PKCS #8', async () => {
const pem = await key.export('another secret')
expect(pem).to.startsWith('-----BEGIN ENCRYPTED PRIVATE KEY-----')
const clone = await crypto.keys.import(pem, 'another secret')
expect(clone).to.exist()
expect(key.equals(clone)).to.eql(true)
})
it('needs correct password', async () => {
const pem = await key.export('another secret')
try {
await crypto.keys.import(pem, 'not the secret')
} catch (err) {
return // expected
}
throw new Error('Expected error to be thrown')
})
it('handles invalid export type', () => {
return expectErrCode(key.export('secret', 'invalid-type'), 'ERR_INVALID_EXPORT_FORMAT')
})
})
describe('throws error instead of crashing', () => {
const key = crypto.keys.unmarshalPublicKey(fixtures.verify.publicKey)
testGarbage.doTests('key.verify', key.verify.bind(key), 2, true)
testGarbage.doTests(
'crypto.keys.unmarshalPrivateKey',
crypto.keys.unmarshalPrivateKey.bind(crypto.keys),
null,
null
)
})
describe('go interop', () => {
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.equal(true)
})
})
describe('openssl interop', () => {
it('can read a private key', async () => {
/*
* Generated with
* openssl genpkey -algorithm RSA
* -pkeyopt rsa_keygen_bits:3072
* -pkeyopt rsa_keygen_pubexp:65537
*/
const pem = `-----BEGIN PRIVATE KEY-----
MIIG/wIBADANBgkqhkiG9w0BAQEFAASCBukwggblAgEAAoIBgQDp0Whyqa8KmdvK
0MsQGJEBzDAEHAZc0C6cr0rkb6Xwo+yB5kjZBRDORk0UXtYGE1pYt4JhUTmMzcWO
v2xTIsdbVMQlNtput2U8kIqS1cSTkX5HxOJtCiIzntMzuR/bGPSOexkyFQ8nCUqb
ROS7cln/ixprra2KMAKldCApN3ue2jo/JI1gyoS8sekhOASAa0ufMPpC+f70sc75
Y53VLnGBNM43iM/2lsK+GI2a13d6rRy86CEM/ygnh/EDlyNDxo+SQmy6GmSv/lmR
xgWQE2dIfK504KIxFTOphPAQAr9AsmcNnCQLhbz7YTsBz8WcytHGQ0Z5pnBQJ9AV
CX9E6DFHetvs0CNLVw1iEO06QStzHulmNEI/3P8I1TIxViuESJxSu3pSNwG1bSJZ
+Qee24vvlz/slBzK5gZWHvdm46v7vl5z7SA+whncEtjrswd8vkJk9fI/YTUbgOC0
HWMdc2t/LTZDZ+LUSZ/b2n5trvdJSsOKTjEfuf0wICC08pUUk8MCAwEAAQKCAYEA
ywve+DQCneIezHGk5cVvp2/6ApeTruXalJZlIxsRr3eq2uNwP4X2oirKpPX2RjBo
NMKnpnsyzuOiu+Pf3hJFrTpfWzHXXm5Eq+OZcwnQO5YNY6XGO4qhSNKT9ka9Mzbo
qRKdPrCrB+s5rryVJXKYVSInP3sDSQ2IPsYpZ6GW6Mv56PuFCpjTzElzejV7M0n5
0bRmn+MZVMVUR54KYiaCywFgUzmr3yfs1cfcsKqMRywt2J58lRy/chTLZ6LILQMv
4V01neVJiRkTmUfIWvc1ENIFM9QJlky9AvA5ASvwTTRz8yOnxoOXE/y4OVyOePjT
cz9eumu9N5dPuUIMmsYlXmRNaeGZPD9bIgKY5zOlfhlfZSuOLNH6EHBNr6JAgfwL
pdP43sbg2SSNKpBZ0iSMvpyTpbigbe3OyhnFH/TyhcC2Wdf62S9/FRsvjlRPbakW
YhKAA2kmJoydcUDO5ccEga8b7NxCdhRiczbiU2cj70pMIuOhDlGAznyxsYbtyxaB
AoHBAPy6Cbt6y1AmuId/HYfvms6i8B+/frD1CKyn+sUDkPf81xSHV7RcNrJi1S1c
V55I0y96HulsR+GmcAW1DF3qivWkdsd/b4mVkizd/zJm3/Dm8p8QOnNTtdWvYoEB
VzfAhBGaR/xflSLxZh2WE8ZHQ3IcRCXV9ZFgJ7PMeTprBJXzl0lTptvrHyo9QK1v
obLrL/KuXWS0ql1uSnJr1vtDI5uW8WU4GDENeU5b/CJHpKpjVxlGg+7pmLknxlBl
oBnZnQKBwQDs2Ky29qZ69qnPWowKceMJ53Z6uoUeSffRZ7xuBjowpkylasEROjuL
nyAihIYB7fd7R74CnRVYLI+O2qXfNKJ8HN+TgcWv8LudkRcnZDSvoyPEJAPyZGfr
olRCXD3caqtarlZO7vXSAl09C6HcL2KZ8FuPIEsuO0Aw25nESMg9eVMaIC6s2eSU
NUt6xfZw1JC0c+f0LrGuFSjxT2Dr5WKND9ageI6afuauMuosjrrOMl2g0dMcSnVz
KrtYa7Wi1N8CgcBFnuJreUplDCWtfgEen40f+5b2yAQYr4fyOFxGxdK73jVJ/HbW
wsh2n+9mDZg9jIZQ/+1gFGpA6V7W06dSf/hD70ihcKPDXSbloUpaEikC7jxMQWY4
uwjOkwAp1bq3Kxu21a+bAKHO/H1LDTrpVlxoJQ1I9wYtRDXrvBpxU2XyASbeFmNT
FhSByFn27Ve4OD3/NrWXtoVwM5/ioX6ZvUcj55McdTWE3ddbFNACiYX9QlyOI/TY
bhWafDCPmU9fj6kCgcEAjyQEfi9jPj2FM0RODqH1zS6OdG31tfCOTYicYQJyeKSI
/hAezwKaqi9phHMDancfcupQ89Nr6vZDbNrIFLYC3W+1z7hGeabMPNZLYAs3rE60
dv4tRHlaNRbORazp1iTBmvRyRRI2js3O++3jzOb2eILDUyT5St+UU/LkY7R5EG4a
w1df3idx9gCftXufDWHqcqT6MqFl0QgIzo5izS68+PPxitpRlR3M3Mr4rCU20Rev
blphdF+rzAavYyj1hYuRAoHBANmxwbq+QqsJ19SmeGMvfhXj+T7fNZQFh2F0xwb2
rMlf4Ejsnx97KpCLUkoydqAs2q0Ws9Nkx2VEVx5KfUD7fWhgbpdnEPnQkfeXv9sD
vZTuAoqInN1+vj1TME6EKR/6D4OtQygSNpecv23EuqEvyXWqRVsRt9Qd2B0H4k7h
gnjREs10u7zyqBIZH7KYVgyh27WxLr859ap8cKAH6Fb+UOPtZo3sUeeume60aebn
4pMwXeXP+LO8NIfRXV8mgrm86g==
-----END PRIVATE KEY-----
`
const key = await crypto.keys.import(pem, '')
expect(key).to.exist()
const id = await key.id()
expect(id).to.equal('QmfWu2Xp8DZzCkZZzoPB9rcrq4R4RZid6AWE6kmrUAzuHy')
})
// AssertionError: expected 'this only supports pkcs5PBES2' to not exist
it.skip('can read a private encrypted key (v1)', async () => {
/*
* Generated with
* openssl genpkey -algorithm RSA
* -pkeyopt rsa_keygen_bits:1024
* -pkeyopt rsa_keygen_pubexp:65537
* -out foo.pem
* openssl pkcs8 -in foo.pem -topk8 -passout pass:mypassword
*/
const pem = `-----BEGIN ENCRYPTED PRIVATE KEY-----
MIICoTAbBgkqhkiG9w0BBQMwDgQI2563Jugj/KkCAggABIICgPxHkKtUUE8EWevq
eX9nTjqpbsv0QoXQMhegfxDELJLU8tj6V0bWNt7QDdfQ1n6FRgnNvNGick6gyqHH
yH9qC2oXwkDFP7OrHp2NEZd7DHQLLc+L4KJ/0dzsiZ1U9no7XzQMUay9Bc918ADE
pN2/EqigWkaG4gNjkAeKWr6+BNRevDXlSvls7YDboNcTiACi5zJkthivB9g3vT1m
gPdN6Gf/mmqtBTDHeqj5QsmXYqeCyo5b26JgYsziABVZDHph4ekPUsTvudRpE9Ex
baXwdYEAZxVpSbTvQ3A5qysjSZeM9ttfRTSSwL391q7dViz4+aujpk0Vj7piH+1B
CkfO8/XudRdRlnOe+KjMidktKCsMGCIOW92IlfMvIQ/Zn1GTYj9bRXONFNJ2WPND
UmCKnL7cmworwg/weRorrGKBWIGspU+tDASOPSvIGKo6Hoxm4CN1TpDRY7DAGlgm
Y3TEbMYfpXyzkPjvAhJDt03D3J9PrTO6uM5d7YUaaTmJ2TQFQVF2Lc3Uz8lDJLs0
ZYtfQ/4H+YY2RrX7ua7t6ArUcYXZtv0J4lRYWjwV8fGPUVc0d8xLJU0Yjf4BD7K8
rsavHo9b5YvBUX7SgUyxAEembEOe3SjQ+gPu2U5wovcjUuC9eItEEsXGrx30BQ0E
8BtK2+hp0eMkW5/BYckJkH+Yl8ypbzRGRRIZzLgeI4JveSx/mNhewfgTr+ORPThZ
mBdkD5r+ixWF174naw53L8U9wF8kiK7pIE1N9TR4USEeovLwX6Ni/2MMDZedOfof
2f77eUdLsK19/5/lcgAAYaXauXWhy2d2r3SayFrC9woy0lh2VLKRMBjcx1oWb7dp
0uxzo5Y=
-----END ENCRYPTED PRIVATE KEY-----
`
const key = await crypto.keys.import(pem, 'mypassword')
expect(key).to.exist()
})
it('can read a private encrypted key (v2 aes-128-cbc)', async () => {
/*
* Generated with
* openssl genpkey -algorithm RSA
* -pkeyopt rsa_keygen_bits:1024
* -pkeyopt rsa_keygen_pubexp:65537
* -out foo.pem
* openssl pkcs8 -in foo.pem -topk8 -v2 aes-128-cbc -passout pass:mypassword
*/
const pem = `-----BEGIN ENCRYPTED PRIVATE KEY-----
MIICzzBJBgkqhkiG9w0BBQ0wPDAbBgkqhkiG9w0BBQwwDgQIP5QK2RfqUl4CAggA
MB0GCWCGSAFlAwQBAgQQj3OyM9gnW2dd/eRHkxjGrgSCAoCpM5GZB0v27cxzZsGc
O4/xqgwB0c/bSJ6QogtYU2KVoc7ZNQ5q9jtzn3I4ONvneOkpm9arzYz0FWnJi2C3
BPiF0D1NkfvjvMLv56bwiG2A1oBECacyAb2pXYeJY7SdtYKvcbgs3jx65uCm6TF2
BylteH+n1ewTQN9DLfASp1n81Ajq9lQGaK03SN2MUtcAPp7N9gnxJrlmDGeqlPRs
KpQYRcot+kE6Ew8a5jAr7mAxwpqvr3SM4dMvADZmRQsM4Uc/9+YMUdI52DG87EWc
0OUB+fnQ8jw4DZgOE9KKM5/QTWc3aEw/dzXr/YJsrv01oLazhqVHnEMG0Nfr0+DP
q+qac1AsCsOb71VxaRlRZcVEkEfAq3gidSPD93qmlDrCnmLYTilcLanXUepda7ez
qhjkHtpwBLN5xRZxOn3oUuLGjk8VRwfmFX+RIMYCyihjdmbEDYpNUVkQVYFGi/F/
1hxOyl9yhGdL0hb9pKHH10GGIgoqo4jSTLlb4ennihGMHCjehAjLdx/GKJkOWShy
V9hj8rAuYnRNb+tUW7ChXm1nLq14x9x1tX0ciVVn3ap/NoMkbFTr8M3pJ4bQlpAn
wCT2erYqwQtgSpOJcrFeph9TjIrNRVE7Zlmr7vayJrB/8/oPssVdhf82TXkna4fB
PcmO0YWLa117rfdeNM/Duy0ThSdTl39Qd+4FxqRZiHjbt+l0iSa/nOjTv1TZ/QqF
wqrO6EtcM45fbFJ1Y79o2ptC2D6MB4HKJq9WCt064/8zQCVx3XPbb3X8Z5o/6koy
ePGbz+UtSb9xczvqpRCOiFLh2MG1dUgWuHazjOtUcVWvilKnkjCMzZ9s1qG0sUDj
nPyn
-----END ENCRYPTED PRIVATE KEY-----
`
const key = await crypto.keys.import(pem, 'mypassword')
expect(key).to.exist()
})
it('can read a private encrypted key (v2 aes-256-cbc)', async () => {
/*
* Generated with
* openssl genpkey -algorithm RSA
* -pkeyopt rsa_keygen_bits:1024
* -pkeyopt rsa_keygen_pubexp:65537
* -out foo.pem
* openssl pkcs8 -in foo.pem -topk8 -v2 aes-256-cbc -passout pass:mypassword
*/
const pem = `-----BEGIN ENCRYPTED PRIVATE KEY-----
MIICzzBJBgkqhkiG9w0BBQ0wPDAbBgkqhkiG9w0BBQwwDgQIhuL894loRucCAggA
MB0GCWCGSAFlAwQBKgQQEoEtsjW3iC9/u0uGvkxX7wSCAoAsX3l6JoR2OGbT8CkY
YT3RQFqquOgItYOHw6E3tir2YrmxEAo99nxoL8pdto37KSC32eAGnfv5R1zmHHSx
0M3/y2AWiCBTX95EEzdtGC1hK3PBa/qpp/xEmcrsjYN6NXxMAkhC0hMP/HdvqMAg
ee7upvaYJsJcl8QLFNayAWr8b8cZA/RBhGEIRl59Eyj6nNtxDt3bCrfe06o1CPCV
50/fRZEwFOi/C6GYvPN6MrPZO3ALBWgopLT2yQqycTKtfxYWIdOsMBkAjKf2D6Pk
u2mqBsaP4b71jIIeT4euSJLsoJV+O39s8YHXtW8GtOqp7V5kIlnm90lZ9wzeLTZ7
HJsD/jEdYto5J3YWm2wwEDccraffJSm7UDtJBvQdIx832kxeFCcGQjW38Zl1qqkg
iTH1PLTypxj2ZuviS2EkXVFb/kVU6leWwOt6fqWFC58UvJKeCk/6veazz3PDnTWM
92ClUqFd+CZn9VT4CIaJaAc6v5NLpPp+T9sRX9AtequPm7FyTeevY9bElfyk9gW9
JDKgKxs6DGWDa16RL5vzwtU+G3o6w6IU+mEwa6/c+hN+pRFs/KBNLLSP9OHBx7BJ
X/32Ft+VFhJaK+lQ+f+hve7od/bgKnz4c/Vtp7Dh51DgWgCpBgb8p0vqu02vTnxD
BXtDv3h75l5PhvdWfVIzpMWRYFvPR+vJi066FjAz2sjYc0NMLSYtZWyWoIInjhoX
Dp5CQujCtw/ZSSlwde1DKEWAW4SeDZAOQNvuz0rU3eosNUJxEmh3aSrcrRtDpw+Y
mBUuWAZMpz7njBi7h+JDfmSW/GAaMwrVFC2gef5375R0TejAh+COAjItyoeYEvv8
DQd8
-----END ENCRYPTED PRIVATE KEY-----
`
const key = await crypto.keys.import(pem, 'mypassword')
expect(key).to.exist()
})
it('can read a private encrypted key (v2 des)', async () => {
/*
* Generated with
* openssl genpkey -algorithm RSA
* -pkeyopt rsa_keygen_bits:1024
* -pkeyopt rsa_keygen_pubexp:65537
* -out foo.pem
* openssl pkcs8 -in foo.pem -topk8 -v2 des -passout pass:mypassword
*/
const pem = `-----BEGIN ENCRYPTED PRIVATE KEY-----
MIICwzA9BgkqhkiG9w0BBQ0wMDAbBgkqhkiG9w0BBQwwDgQI0lXp62ozXvwCAggA
MBEGBSsOAwIHBAiR3Id5vH0u4wSCAoDQQYOrrkPFPIa0S5fQGXnJw1F/66g92Gs1
TkGydn4ouabWb++Vbi2chee1oyZsN2l8YNzDi0Gb2PfjsGpg2aJk0a3/efgA0u6T
leEH1dA/7Hr9NVspgHkaXpHt3X6wdbznLYJeAelfj7sDXpOkULGWCkCst0Txb6bi
Oxv4c0yYykiuUrp+2xvHbF9c2PrcDb58u/OBZcCg3QB1gTugQKM+ZIBRhcTEFLrm
8gWbzBfwYiUm6aJce4zoafP0NSlEOBbpbr73A08Q1IK6pISwltOUhhTvspSZnK41
y2CHt5Drnpl1pfOw9Q0svO3VrUP+omxP1SFP17ZfaRGw2uHd08HJZs438x5dIQoH
QgjlZ8A5rcT3FjnytSh3fln2ZxAGuObghuzmOEL/+8fkGER9QVjmQlsL6OMfB4j4
ZAkLf74uaTdegF3SqDQaGUwWgk7LyualmUXWTBoeP9kRIsRQLGzAEmd6duBPypED
HhKXP/ZFA1kVp3x1fzJ2llMFB3m1JBwy4PiohqrIJoR+YvKUvzVQtbOjxtCEAj87
JFnlQj0wjTd6lfNn+okewMNjKINZx+08ui7XANNU/l18lHIIz3ssXJSmqMW+hRZ9
9oB2tntLrnRMhkVZDVHadq7eMFOPu0rkekuaZm9CO2vu4V7Qa2h+gOoeczYza0H7
A+qCKbprxyL8SKI5vug2hE+mfC1leXVRtUYm1DnE+oet99bFd0fN20NwTw0rOeRg
0Z+/ZpQNizrXxfd3sU7zaJypWCxZ6TD/U/AKBtcb2gqmUjObZhbfbWq6jU2Ye//w
EBqQkwAUXR1tNekF8CWLOrfC/wbLRxVRkayb8bQUfdgukLpz0bgw
-----END ENCRYPTED PRIVATE KEY-----
`
const key = await crypto.keys.import(pem, 'mypassword')
expect(key).to.exist()
})
it('can read a private encrypted key (v2 des3)', async () => {
/*
* Generated with
* openssl genpkey -algorithm RSA
* -pkeyopt rsa_keygen_bits:1024
* -pkeyopt rsa_keygen_pubexp:65537
* -out foo.pem
* openssl pkcs8 -in foo.pem -topk8 -v2 des3 -passout pass:mypassword
*/
const pem = `-----BEGIN ENCRYPTED PRIVATE KEY-----
MIICxjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQISznrfHd+D58CAggA
MBQGCCqGSIb3DQMHBAhx0DnnUvDiHASCAoCceplm+Cmwlgvn4hNsv6e4c/S1iA7w
2hU7Jt8JgRCIMWjP2FthXOAFLa2fD4g3qncYXcDAFBXNyoh25OgOwstO14YkxhDi
wG4TeppGUt9IlyyCol6Z4WhQs1TGm5OcD5xDta+zBXsBnlgmKLD5ZXPEYB+3v/Dg
SvM4sQz6NgkVHN52hchERsnknwSOghiK9mIBH0RZU5LgzlDy2VoBCiEPVdZ7m4F2
dft5e82zFS58vwDeNN/0r7fC54TyJf/8k3q94+4Hp0mseZ67LR39cvnEKuDuFROm
kLPLekWt5R2NGdunSQlA79BkrNB1ADruO8hQOOHMO9Y3/gNPWLKk+qrfHcUni+w3
Ofq+rdfakHRb8D6PUmsp3wQj6fSOwOyq3S50VwP4P02gKcZ1om1RvEzTbVMyL3sh
hZcVB3vViu3DO2/56wo29lPVTpj9bSYjw/CO5jNpPBab0B/Gv7JAR0z4Q8gn6OPy
qf+ddyW4Kcb6QUtMrYepghDthOiS3YJV/zCNdL3gTtVs5Ku9QwQ8FeM0/5oJZPlC
TxGuOFEJnYRWqIdByCP8mp/qXS5alSR4uoYQSd7vZG4vkhkPNSAwux/qK1IWfqiW
3XlZzrbD//9IzFVqGRs4nRIFq85ULK0zAR57HEKIwGyn2brEJzrxpV6xsHBp+m4w
6r0+PtwuWA0NauTCUzJ1biUdH8t0TgBL6YLaMjlrfU7JstH3TpcZzhJzsjfy0+zV
NT2TO3kSzXpQ5M2VjOoHPm2fqxD/js+ThDB3QLi4+C7HqakfiTY1lYzXl9/vayt6
DUD29r9pYL9ErB9tYko2rat54EY7k7Ts6S5jf+8G7Zz234We1APhvqaG
-----END ENCRYPTED PRIVATE KEY-----
`
const key = await crypto.keys.import(pem, 'mypassword')
expect(key).to.exist()
})
})
})

View File

@@ -0,0 +1,81 @@
/* eslint-env mocha */
'use strict'
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')
describe('without libp2p-crypto-secp256k1 module present', () => {
before(() => {
const empty = null
sinon.replace(crypto.keys.supportedKeys, 'secp256k1', empty)
})
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()
})
it('protobuf encoding', async () => {
const key = await crypto.keys.generateKeyPair('secp256k1', 256)
expect(key).to.exist()
const keyMarshal = crypto.keys.marshalPrivateKey(key)
const key2 = await crypto.keys.unmarshalPrivateKey(keyMarshal)
const keyMarshal2 = crypto.keys.marshalPrivateKey(key2)
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)
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('unmarshals a secp256k1 public key', () => {
const key = crypto.keys.unmarshalPublicKey(fixtures.pbmPublicKey)
expect(key).to.exist()
})
})

3
test/node.js Normal file
View File

@@ -0,0 +1,3 @@
'use strict'
require('./keys/rsa-crypto-libs')

27
test/random-bytes.spec.js Normal file
View File

@@ -0,0 +1,27 @@
/* eslint-env mocha */
'use strict'
const chai = require('chai')
const dirtyChai = require('dirty-chai')
const expect = chai.expect
chai.use(dirtyChai)
const randomBytes = require('../src/random-bytes')
describe('randomBytes', () => {
it('produces random bytes', () => {
expect(randomBytes(16)).to.have.length(16)
})
it('throws if length is 0', () => {
expect(() => randomBytes(0)).to.throw(Error).with.property('code', 'ERR_INVALID_LENGTH')
})
it('throws if length is < 0', () => {
expect(() => randomBytes(-1)).to.throw(Error).with.property('code', 'ERR_INVALID_LENGTH')
})
it('throws if length is not a number', () => {
expect(() => randomBytes('hi')).to.throw(Error).with.property('code', 'ERR_INVALID_LENGTH')
})
})

View File

@@ -1,129 +0,0 @@
/* eslint-env mocha */
'use strict'
const expect = require('chai').expect
const crypto = require('../src')
const rsa = crypto.keys.rsa
describe('RSA', () => {
let key
before(() => {
key = crypto.generateKeyPair('RSA', 2048)
})
it('generates a valid key', () => {
expect(
key
).to.be.an.instanceof(
rsa.RsaPrivateKey
)
expect(
key.hash()
).to.have.length(
34
)
})
it('signs', () => {
const pk = key.public
const text = key.genSecret()
const sig = key.sign(text)
expect(
pk.verify(text, sig)
).to.be.eql(
true
)
})
it('encoding', () => {
const keyMarshal = key.marshal()
const key2 = rsa.unmarshalRsaPrivateKey(keyMarshal)
const keyMarshal2 = key2.marshal()
expect(
keyMarshal
).to.be.eql(
keyMarshal2
)
const pk = key.public
const pkMarshal = pk.marshal()
const pk2 = rsa.unmarshalRsaPublicKey(pkMarshal)
const pkMarshal2 = pk2.marshal()
expect(
pkMarshal
).to.be.eql(
pkMarshal2
)
})
describe('key equals', () => {
it('equals itself', () => {
expect(
key.equals(key)
).to.be.eql(
true
)
expect(
key.public.equals(key.public)
).to.be.eql(
true
)
})
it('not equals other key', () => {
const key2 = crypto.generateKeyPair('RSA', 2048)
expect(
key.equals(key2)
).to.be.eql(
false
)
expect(
key2.equals(key)
).to.be.eql(
false
)
expect(
key.public.equals(key2.public)
).to.be.eql(
false
)
expect(
key2.public.equals(key.public)
).to.be.eql(
false
)
})
})
it('sign and verify', () => {
const data = new Buffer('hello world')
const sig = key.sign(data)
expect(
key.public.verify(data, sig)
).to.be.eql(
true
)
})
it('does fails to verify for different data', () => {
const data = new Buffer('hello world')
const sig = key.sign(data)
expect(
key.public.verify(new Buffer('hello'), sig)
).to.be.eql(
false
)
})
})

38
test/util.spec.js Normal file
View File

@@ -0,0 +1,38 @@
/* eslint max-nested-callbacks: ["error", 8] */
/* eslint-env mocha */
'use strict'
const chai = require('chai')
const dirtyChai = require('dirty-chai')
const expect = chai.expect
chai.use(dirtyChai)
require('node-forge/lib/jsbn')
const forge = require('node-forge/lib/forge')
const util = require('../src/util')
describe('Util', () => {
let bn
before(() => {
bn = new forge.jsbn.BigInteger('dead', 16)
})
it('should convert BigInteger to a uint base64url encoded string', () => {
expect(util.bigIntegerToUintBase64url(bn)).to.eql('3q0')
})
it('should convert BigInteger to a uint base64url encoded string with padding', () => {
const bnpad = new forge.jsbn.BigInteger('ff', 16)
expect(util.bigIntegerToUintBase64url(bnpad, 2)).to.eql('AP8')
})
it('should convert base64url encoded string to BigInteger', () => {
const num = util.base64urlToBigInteger('3q0')
expect(num.equals(bn)).to.be.true()
})
it('should convert base64url encoded string to Buffer with padding', () => {
const buf = util.base64urlToBuffer('AP8', 2)
expect(Buffer.from([0, 255])).to.eql(buf)
})
})

21
test/util/index.js Normal file
View File

@@ -0,0 +1,21 @@
/* eslint-disable valid-jsdoc */
'use strict'
const chai = require('chai')
const expect = chai.expect
// @ts-check
/**
* @type {function(any, string): void}
*/
const expectErrCode = async (p, code) => {
try {
await p
} catch (err) {
expect(err).to.have.property('code', code)
return
}
expect.fail(`Expected error with code ${code} but no error thrown`)
}
module.exports = { expectErrCode }

30
tsconfig.json Normal file
View File

@@ -0,0 +1,30 @@
{
"compilerOptions": {
"module": "commonjs",
"lib": [
"es6"
],
"target": "ES5",
"noImplicitAny": false,
"noImplicitThis": true,
"strictFunctionTypes": true,
"strictNullChecks": true,
"esModuleInterop": true,
"resolveJsonModule": true,
"allowJs": true,
"checkJs": true,
"baseUrl": ".",
"paths": {
"libp2p-crypto": [
"./src",
"../../src",
"../src"
]
},
"types": ["node", "mocha", "chai"],
"noEmit": true,
"forceConsistentCasingInFileNames": true
},
"files": ["./src/index.d.ts",],
"include": ["./test/**/*.spec.js"]
}

29552
vendor/forge.bundle.js vendored

File diff suppressed because it is too large Load Diff