mirror of
https://github.com/fluencelabs/js-libp2p-crypto
synced 2025-07-16 20:52:13 +00:00
Compare commits
206 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
dee91b8d97 | ||
|
a05e870c45 | ||
|
bc681c76f4 | ||
|
1c16dd3dec | ||
|
4e5a05a12c | ||
|
c067685f45 | ||
|
bc337698b6 | ||
|
b07978dbea | ||
|
0cc270dc4a | ||
|
b28c232e22 | ||
|
04a4e81317 | ||
|
29df292338 | ||
|
253cca2799 | ||
|
afcffc8115 | ||
|
a0f387aeab | ||
|
8b3dc56dc2 | ||
|
7888afada6 | ||
|
7273739f04 | ||
|
609297be65 | ||
|
89a297793d | ||
|
32fae9b505 | ||
|
c2dd0a535d | ||
|
2f18a077b4 | ||
|
4aa77a6b12 | ||
|
210dd27479 | ||
|
437a76fbe3 | ||
|
75d250c876 | ||
|
e761427153 | ||
|
90d51ee8c7 | ||
|
ccda21fe91 | ||
|
206999ce11 | ||
|
3272688489 | ||
|
456a365378 | ||
|
42bd594068 | ||
|
ab12e6f068 | ||
|
6bbf12c169 | ||
|
a68fc2e98d | ||
|
d73a0ca52e | ||
|
1b0fac84a8 | ||
|
efaafa9c06 | ||
|
88b3018c9c | ||
|
9aacb478c4 | ||
|
269d169f7c | ||
|
c956d1ad2a | ||
|
9b4231eb75 | ||
|
fdab19b7d9 | ||
|
35f196ea4d | ||
|
d3601fa936 | ||
|
f01e3812e9 | ||
|
00477e3bcb | ||
|
0f4c533dfa | ||
|
d566e7ef3b | ||
|
78e2ddd2bd | ||
|
0ad513887a | ||
|
e7468d830d | ||
|
ae109d46f7 | ||
|
41c9cd1691 | ||
|
3e88839c2b | ||
|
cc2094975b | ||
|
ad4bf3b357 | ||
|
730d762717 | ||
|
e01977c5a3 | ||
|
89901f7097 | ||
|
9894c34f94 | ||
|
e36a9f6b79 | ||
|
b5d94ecae7 | ||
|
1f9c2ddadb | ||
|
d6d06a8404 | ||
|
8b8d0c1510 | ||
|
b998f63aec | ||
|
adc6eb478c | ||
|
2c1bac5ce9 | ||
|
027a5a9332 | ||
|
2c294b56ab | ||
|
487cd076fb | ||
|
b8e2414420 | ||
|
9f747a173f | ||
|
34c5f5c8f0 | ||
|
a008bc2fcb | ||
|
b68060388f | ||
|
afe94ded6b | ||
|
a5e05603ef | ||
|
0b686d363c | ||
|
2d15e717e4 | ||
|
6775dbf670 | ||
|
4b09aae8ca | ||
|
764d8bff3e | ||
|
26b6217041 | ||
|
5500ac4a6e | ||
|
d675670ed9 | ||
|
ad7107233e | ||
|
a521cd9b11 | ||
|
ed670209ad | ||
|
1974eb92be | ||
|
fbd42385e3 | ||
|
9f01868c6c | ||
|
3bb84514d1 | ||
|
5cd0e8cc1a | ||
|
3f131d4a0a | ||
|
0ffe31821a | ||
|
7b3625888c | ||
|
b7bce77ad5 | ||
|
c1f867bd9c | ||
|
e3f02eb6f1 | ||
|
a0874389a9 | ||
|
ce22cf13f0 | ||
|
5fc391c8e0 | ||
|
53a2b590a9 | ||
|
f4dbd62e49 | ||
|
567d68c855 | ||
|
af782c5906 | ||
|
f0593c9e6d | ||
|
8d8294dc3f | ||
|
3a8bab9f44 | ||
|
c983edfdb9 | ||
|
6c9928abd6 | ||
|
720246f012 | ||
|
cfdcbe08d4 | ||
|
df23d634c5 | ||
|
88e1bcf75f | ||
|
c54ea206f0 | ||
|
857d2bd902 | ||
|
200110cb9d | ||
|
9e5778694c | ||
|
87e8f1c86f | ||
|
df75980a88 | ||
|
934390acd3 | ||
|
8b80b46667 | ||
|
e8efad546f | ||
|
e8cbf13d85 | ||
|
c7e0409c1c | ||
|
f4c00893ad | ||
|
b05e77f375 | ||
|
1602c440ad | ||
|
937cc76714 | ||
|
ad478454d8 | ||
|
8c69ffb20f | ||
|
e689a402a3 | ||
|
4bd032a6ae | ||
|
50c61ba46e | ||
|
3a90f70350 | ||
|
743c69524c | ||
|
1a347fa04c | ||
|
71339e08e7 | ||
|
0ab2c2d2d6 | ||
|
cdcca5f828 | ||
|
651d08f67e | ||
|
f2b67f7d82 | ||
|
2c0dc706b7 | ||
|
21d4c8b74e | ||
|
285b6ca392 | ||
|
7d96dd3243 | ||
|
b3421284f9 | ||
|
f3cb8ced36 | ||
|
0a6f63dce5 | ||
|
0ce5f34a08 | ||
|
a826968e71 | ||
|
7608fdd858 | ||
|
bf9b532067 | ||
|
5296f8a42f | ||
|
3a91ae2ed8 | ||
|
e7c11a8e01 | ||
|
9bb96dc2bf | ||
|
957fdd37e9 | ||
|
83257bc4bd | ||
|
cb7fae7fcc | ||
|
7669847c17 | ||
|
dc2793f138 | ||
|
e0b916ace9 | ||
|
0a71af7b89 | ||
|
46adafb207 | ||
|
9e977c7d44 | ||
|
f20267b962 | ||
|
98b285a840 | ||
|
ce5fb8c1b9 | ||
|
0dcf1a6f52 | ||
|
1b6a070fa8 | ||
|
1471e07bf9 | ||
|
bc554d1407 | ||
|
83d2d52205 | ||
|
301c779f49 | ||
|
d552fd423a | ||
|
8401154102 | ||
|
838ecdbaef | ||
|
d14c11310e | ||
|
41c03a86a5 | ||
|
1013becd66 | ||
|
c21454c4e8 | ||
|
4ee48a737a | ||
|
363cda56da | ||
|
d8b0c74ec9 | ||
|
647fab7170 | ||
|
e66383137f | ||
|
638ea963e6 | ||
|
35abf8fcb4 | ||
|
9430e7f91e | ||
|
42678bba30 | ||
|
f637ff2485 | ||
|
4c2d42984d | ||
|
de8b16b00b | ||
|
73b8b39de5 | ||
|
288c9cff8f | ||
|
418a0885ad | ||
|
4c744d3b60 | ||
|
4c36aeba17 | ||
|
e57e4ffa0d |
3
.aegir.js
Normal file
3
.aegir.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
bundlesize: { maxSize: '124kB' }
|
||||
}
|
13
.gitignore
vendored
13
.gitignore
vendored
@@ -1,6 +1,11 @@
|
||||
docs
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
|
||||
**/node_modules/
|
||||
**/*.log
|
||||
test/repo-tests*
|
||||
**/bundle.js
|
||||
|
||||
# Logs
|
||||
logs
|
||||
@@ -31,4 +36,12 @@ build
|
||||
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
|
||||
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
|
||||
|
34
.npmignore
34
.npmignore
@@ -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
|
68
.travis.yml
68
.travis.yml
@@ -1,35 +1,45 @@
|
||||
sudo: false
|
||||
language: node_js
|
||||
|
||||
matrix:
|
||||
cache: npm
|
||||
|
||||
stages:
|
||||
- check
|
||||
- test
|
||||
- cov
|
||||
|
||||
node_js:
|
||||
- '10'
|
||||
- '12'
|
||||
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
- windows
|
||||
|
||||
script: npx nyc -s npm run test:node -- --bail
|
||||
after_success: npx nyc report --reporter=text-lcov > coverage.lcov && npx codecov
|
||||
|
||||
jobs:
|
||||
include:
|
||||
- node_js: 4
|
||||
env: CXX=g++-4.8
|
||||
- node_js: 6
|
||||
env: CXX=g++-4.8
|
||||
- node_js: stable
|
||||
env: CXX=g++-4.8
|
||||
- stage: check
|
||||
script:
|
||||
- npx aegir build --bundlesize
|
||||
- npx aegir dep-check
|
||||
- npm run lint
|
||||
|
||||
# Make sure we have new NPM.
|
||||
before_install:
|
||||
- npm install -g npm@4
|
||||
- stage: test
|
||||
name: chrome
|
||||
addons:
|
||||
chrome: stable
|
||||
script:
|
||||
- npx aegir test -t browser
|
||||
|
||||
script:
|
||||
- npm run lint
|
||||
- npm test
|
||||
- npm run coverage
|
||||
- stage: test
|
||||
name: firefox
|
||||
addons:
|
||||
firefox: latest
|
||||
script:
|
||||
- npx aegir test -t browser -- --browsers FirefoxHeadless
|
||||
|
||||
before_script:
|
||||
- export DISPLAY=:99.0
|
||||
- sh -e /etc/init.d/xvfb start
|
||||
|
||||
after_success:
|
||||
- npm run coverage-publish
|
||||
|
||||
addons:
|
||||
firefox: 'latest'
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- g++-4.8
|
||||
notifications:
|
||||
email: false
|
||||
|
357
CHANGELOG.md
357
CHANGELOG.md
@@ -1,3 +1,360 @@
|
||||
<a name="0.19.2"></a>
|
||||
## [0.19.2](https://github.com/libp2p/js-libp2p-crypto/compare/v0.19.1...v0.19.2) (2021-03-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* ed25519 PeerID generation ([#186](https://github.com/libp2p/js-libp2p-crypto/issues/186)) ([1c16dd3](https://github.com/libp2p/js-libp2p-crypto/commit/1c16dd3)), closes [ipfs/js-ipfs#3591](https://github.com/ipfs/js-ipfs/issues/3591) [libp2p/js-libp2p-crypto#185](https://github.com/libp2p/js-libp2p-crypto/issues/185)
|
||||
|
||||
|
||||
|
||||
<a name="0.19.1"></a>
|
||||
## [0.19.1](https://github.com/libp2p/js-libp2p-crypto/compare/v0.19.0...v0.19.1) (2021-03-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* ed25519 key ID generation ([bc33769](https://github.com/libp2p/js-libp2p-crypto/commit/bc33769))
|
||||
|
||||
|
||||
|
||||
<a name="0.19.0"></a>
|
||||
# [0.19.0](https://github.com/libp2p/js-libp2p-crypto/compare/v0.18.0...v0.19.0) (2021-01-15)
|
||||
|
||||
|
||||
|
||||
<a name="0.18.0"></a>
|
||||
# [0.18.0](https://github.com/libp2p/js-libp2p-crypto/compare/v0.17.9...v0.18.0) (2020-08-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* remove rendundant public key ([#181](https://github.com/libp2p/js-libp2p-crypto/issues/181)) ([afcffc8](https://github.com/libp2p/js-libp2p-crypto/commit/afcffc8))
|
||||
* replace node buffers with uint8arrays ([#180](https://github.com/libp2p/js-libp2p-crypto/issues/180)) ([a0f387a](https://github.com/libp2p/js-libp2p-crypto/commit/a0f387a))
|
||||
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* The private ed25519 key will no longer include the redundant public key
|
||||
|
||||
* chore: fix lint
|
||||
* - Where node Buffers were returned, now Uint8Arrays are
|
||||
|
||||
* chore: remove commented code
|
||||
|
||||
|
||||
|
||||
<a name="0.17.9"></a>
|
||||
## [0.17.9](https://github.com/libp2p/js-libp2p-crypto/compare/v0.17.8...v0.17.9) (2020-08-05)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add exporting/importing of non rsa keys in libp2p-key format ([#179](https://github.com/libp2p/js-libp2p-crypto/issues/179)) ([7273739](https://github.com/libp2p/js-libp2p-crypto/commit/7273739))
|
||||
|
||||
|
||||
|
||||
<a name="0.17.8"></a>
|
||||
## [0.17.8](https://github.com/libp2p/js-libp2p-crypto/compare/v0.17.7...v0.17.8) (2020-07-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* go ed25519 interop ([2f18a07](https://github.com/libp2p/js-libp2p-crypto/commit/2f18a07))
|
||||
|
||||
|
||||
|
||||
<a name="0.17.7"></a>
|
||||
## [0.17.7](https://github.com/libp2p/js-libp2p-crypto/compare/v0.17.6...v0.17.7) (2020-06-09)
|
||||
|
||||
|
||||
|
||||
<a name="0.17.6"></a>
|
||||
## [0.17.6](https://github.com/libp2p/js-libp2p-crypto/compare/v0.17.5...v0.17.6) (2020-04-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add buffer and update deps ([#25](https://github.com/libp2p/js-libp2p-crypto/issues/25)) ([35f196e](https://github.com/libp2p/js-libp2p-crypto/commit/35f196e))
|
||||
* **unmarshal:** provide only one arg to callback ([#17](https://github.com/libp2p/js-libp2p-crypto/issues/17)) ([3bb8451](https://github.com/libp2p/js-libp2p-crypto/commit/3bb8451))
|
||||
* circular circular dep -> DI ([0dcf1a6](https://github.com/libp2p/js-libp2p-crypto/commit/0dcf1a6))
|
||||
* update deps and repo setup ([cfdcbe0](https://github.com/libp2p/js-libp2p-crypto/commit/cfdcbe0))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add `id()` method to Secp256k1PrivateKey ([f4dbd62](https://github.com/libp2p/js-libp2p-crypto/commit/f4dbd62))
|
||||
* initial implementation ([4c36aeb](https://github.com/libp2p/js-libp2p-crypto/commit/4c36aeb))
|
||||
* next libp2p-crypto ([#4](https://github.com/libp2p/js-libp2p-crypto/issues/4)) ([4ee48a7](https://github.com/libp2p/js-libp2p-crypto/commit/4ee48a7))
|
||||
* use async await ([#18](https://github.com/libp2p/js-libp2p-crypto/issues/18)) ([1974eb9](https://github.com/libp2p/js-libp2p-crypto/commit/1974eb9))
|
||||
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* Callback support has been dropped in favor of async/await.
|
||||
|
||||
* feat: use async/await
|
||||
|
||||
This PR changes this module to remove callbacks and use async/await. The API is unchanged aside from the obvious removal of the `callback` parameter.
|
||||
|
||||
refs https://github.com/ipfs/js-ipfs/issues/1670
|
||||
|
||||
* fix: use latest multihashing-async as it is all promises now
|
||||
|
||||
|
||||
|
||||
<a name="0.17.5"></a>
|
||||
## [0.17.5](https://github.com/libp2p/js-libp2p-crypto/compare/v0.17.4...v0.17.5) (2020-03-24)
|
||||
|
||||
|
||||
|
||||
<a name="0.17.4"></a>
|
||||
## [0.17.4](https://github.com/libp2p/js-libp2p-crypto/compare/v0.17.3...v0.17.4) (2020-03-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add buffer, cleanup, reduce size ([#170](https://github.com/libp2p/js-libp2p-crypto/issues/170)) ([c956d1a](https://github.com/libp2p/js-libp2p-crypto/commit/c956d1a))
|
||||
|
||||
|
||||
|
||||
<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)
|
||||
|
||||
|
276
README.md
276
README.md
@@ -1,43 +1,52 @@
|
||||
# js-libp2p-crypto
|
||||
|
||||
[](http://ipn.io)
|
||||
[](http://ipfs.io/)
|
||||
[](http://webchat.freenode.net/?channels=%23ipfs)
|
||||
[](https://github.com/RichardLitt/standard-readme)
|
||||
[](https://coveralls.io/github/libp2p/js-libp2p-crypto?branch=master)
|
||||
[](https://travis-ci.org/libp2p/js-libp2p-crypto)
|
||||
[](https://circleci.com/gh/libp2p/js-libp2p-crypto)
|
||||
[](http://protocol.ai)
|
||||
[](http://libp2p.io/)
|
||||
[](http://webchat.freenode.net/?channels=%23libp2p)
|
||||
[](https://discuss.libp2p.io)
|
||||
[](https://codecov.io/gh/libp2p/js-libp2p-crypto)
|
||||
[](https://travis-ci.com/libp2p/js-libp2p-crypto)
|
||||
[](https://david-dm.org/libp2p/js-libp2p-crypto)
|
||||
[](https://github.com/feross/standard)
|
||||

|
||||

|
||||
|
||||
> Crypto primitives for libp2p in JavaScript
|
||||
|
||||
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).
|
||||
|
||||
## Lead Maintainer
|
||||
|
||||
[Jacob Heun](https://github.com/jacobheun/)
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Install](#install)
|
||||
- [API](#api)
|
||||
- [`crypto.hmac`](#hmac)
|
||||
- [`create(hash, secret, callback)`](#createhash-secret-callback)
|
||||
- [`digest(data, callback)`](#digestdata-callback)
|
||||
- [`crypto.aes`](#aes)
|
||||
- [`create(key, iv, callback)`](#createkey-iv-callback)
|
||||
- [`encrypt(data, callback)`](#encryptdata-callback)
|
||||
- [`decrypt(data, callback)`](#decryptdata-callback)
|
||||
- [`keys`](#keys)
|
||||
- [`generateKeyPair(type, bits, callback)`](#generatekeypairtype-bits-callback)
|
||||
- [`generateEphemeralKeyPair(curve, callback)`](#generateephemeralkeypaircurve-callback)
|
||||
- [`keyStretcher(cipherType, hashType, secret, callback)`](#keystretcherciphertype-hashtype-secret-callback)
|
||||
- [`marshalPublicKey(key[, type], callback)`](#marshalpublickeykey-type-callback)
|
||||
- [`unmarshalPublicKey(buf)`](#unmarshalpublickeybuf)
|
||||
- [`marshalPrivateKey(key[, type])`](#marshalprivatekeykey-type)
|
||||
- [`unmarshalPrivateKey(buf, callback)`](#unmarshalprivatekeybuf-callback)
|
||||
- [`webcrypto`](#webcrypto)
|
||||
- [Contribute](#contribute)
|
||||
- [License](#license)
|
||||
- [js-libp2p-crypto](#js-libp2p-crypto)
|
||||
- [Lead Maintainer](#lead-maintainer)
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [Install](#install)
|
||||
- [Usage](#usage)
|
||||
- [Web Crypto API](#web-crypto-api)
|
||||
- [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(encryptedKey, password)`](#cryptokeysimportencryptedkey-password)
|
||||
- [`privateKey.export(password, format)`](#privatekeyexportpassword-format)
|
||||
- [`crypto.randomBytes(number)`](#cryptorandombytesnumber)
|
||||
- [`crypto.pbkdf2(password, salt, iterations, keySize, hash)`](#cryptopbkdf2password-salt-iterations-keysize-hash)
|
||||
- [Contribute](#contribute)
|
||||
- [License](#license)
|
||||
|
||||
## Install
|
||||
|
||||
@@ -45,149 +54,252 @@ This repo contains the JavaScript implementation of the crypto primitives needed
|
||||
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
|
||||
|
||||
### `crypto.aes`
|
||||
|
||||
Expoes an interface to AES encryption (formerly Rijndael), as defined in U.S. Federal Information Processing Standards Publication 197.
|
||||
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, callback)`
|
||||
#### `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`.
|
||||
- `callback: Function`
|
||||
- `key: Uint8Array` The key, if length `16` then `AES 128` is used. For length `32`, `AES 256` is used.
|
||||
- `iv: Uint8Array` Must have length `16`.
|
||||
|
||||
##### `decrypt(data, callback)`
|
||||
Returns `Promise<{decrypt<Function>, encrypt<Function>}>`
|
||||
|
||||
- `data: Buffer`
|
||||
- `callback: Function`
|
||||
##### `decrypt(data)`
|
||||
|
||||
##### `encrypt(data, callback)`
|
||||
- `data: Uint8Array`
|
||||
|
||||
- `data: Buffer`
|
||||
- `callback: Function`
|
||||
Returns `Promise<Uint8Array>`
|
||||
|
||||
```
|
||||
TODO: Example of using aes
|
||||
##### `encrypt(data)`
|
||||
|
||||
- `data: Uint8Array`
|
||||
|
||||
Returns `Promise<Uint8Array>`
|
||||
|
||||
```js
|
||||
const crypto = require('libp2p-crypto')
|
||||
|
||||
// Setting up Key and IV
|
||||
|
||||
// A 16 bytes array, 128 Bits, AES-128 is chosen
|
||||
const key128 = Uint8Array.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 = Uint8Array.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(Uint8Array.from(decryptedMessage))
|
||||
console.log(encryptedBuffer)
|
||||
// prints: <Uint8Array 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: <Uint8Array 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, callback)`
|
||||
#### `crypto.hmac.create(hash, secret)`
|
||||
|
||||
- `hash: String`
|
||||
- `secret: Buffer`
|
||||
- `callback: Function`
|
||||
- `secret: Uint8Array`
|
||||
|
||||
##### `digest(data, callback)`
|
||||
Returns `Promise<{digest<Function>}>`
|
||||
|
||||
- `data: Buffer`
|
||||
- `callback: Function`
|
||||
##### `digest(data)`
|
||||
|
||||
- `data: Uint8Array`
|
||||
|
||||
Returns `Promise<Uint8Array>`
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
TODO: Example of using hmac
|
||||
```js
|
||||
const crypto = require('libp2p-crypto')
|
||||
|
||||
async function main () {
|
||||
const hash = 'SHA1' // 'SHA256' || 'SHA512'
|
||||
const hmac = await crypto.hmac.create(hash, uint8ArrayFromString('secret'))
|
||||
const sig = await hmac.digest(uint8ArrayFromString('hello world'))
|
||||
console.log(sig)
|
||||
}
|
||||
|
||||
main()
|
||||
```
|
||||
|
||||
### `crypto.keys`
|
||||
|
||||
**Supported Key Types**
|
||||
|
||||
The [`generateKeyPair`](#generatekeypairtype-bits-callback), [`marshalPublicKey`](#marshalpublickeykey-type-callback), and [`marshalPrivateKey`](#marshalprivatekeykey-type) functions accept a string `type` argument.
|
||||
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.
|
||||
Currently the `'RSA'`, `'ed25519'`, and `secp256k1` types are supported, although ed25519 and secp256k1 keys support only signing and verification of messages. For encryption / decryption support, RSA keys should be used.
|
||||
|
||||
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, callback)`
|
||||
### `crypto.keys.generateKeyPair(type, bits)`
|
||||
|
||||
- `type: String`, see [Supported Key Types](#supported-key-types) above.
|
||||
- `bits: Number` Minimum of 1024
|
||||
- `callback: Function`
|
||||
|
||||
Returns `Promise<{privateKey<Uint8Array>, publicKey<Uint8Array>}>`
|
||||
|
||||
Generates a keypair of the given type and bitsize.
|
||||
|
||||
### `crypto.keys.generateEphemeralKeyPair(curve, callback)`
|
||||
### `crypto.keys.generateEphemeralKeyPair(curve)`
|
||||
|
||||
- `curve: String`, one of `'P-256'`, `'P-384'`, `'P-521'` is currently supported
|
||||
- `callback: Function`
|
||||
|
||||
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.
|
||||
|
||||
Calls back with an object of the form
|
||||
Resolves to an object of the form:
|
||||
|
||||
```js
|
||||
{
|
||||
key: Buffer,
|
||||
key: Uint8Array,
|
||||
genSharedKey: Function
|
||||
}
|
||||
```
|
||||
|
||||
### `crypto.keys.keyStretcher(cipherType, hashType, secret, callback)`
|
||||
### `crypto.keys.keyStretcher(cipherType, hashType, secret)`
|
||||
|
||||
- `cipherType: String`, one of `'AES-128'`, `'AES-256'`, `'Blowfish'`
|
||||
- `hashType: String`, one of `'SHA1'`, `SHA256`, `SHA512`
|
||||
- `secret: Buffer`
|
||||
- `callback: Function`
|
||||
- `secret: Uint8Array`
|
||||
|
||||
Returns `Promise`
|
||||
|
||||
Generates a set of keys for each party by stretching the shared key.
|
||||
|
||||
Calls back with an object of the form:
|
||||
Resolves to an object of the form:
|
||||
|
||||
```js
|
||||
{
|
||||
k1: {
|
||||
iv: Buffer,
|
||||
cipherKey: Buffer,
|
||||
macKey: Buffer
|
||||
iv: Uint8Array,
|
||||
cipherKey: Uint8Array,
|
||||
macKey: Uint8Array
|
||||
},
|
||||
k2: {
|
||||
iv: Buffer,
|
||||
cipherKey: Buffer,
|
||||
macKey: Buffer
|
||||
iv: Uint8Array,
|
||||
cipherKey: Uint8Array,
|
||||
macKey: Uint8Array
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `crypto.keys.marshalPublicKey(key[, type], callback)`
|
||||
### `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.
|
||||
- `key: keys.rsa.RsaPublicKey | keys.ed25519.Ed25519PublicKey | keys.secp256k1.Secp256k1PublicKey`
|
||||
- `type: String`, see [Supported Key Types](#supported-key-types) above. Defaults to 'rsa'.
|
||||
|
||||
Returns `Uint8Array`
|
||||
|
||||
Converts a public key object into a protobuf serialized public key.
|
||||
|
||||
### `crypto.keys.unmarshalPublicKey(buf)`
|
||||
|
||||
- `buf: Buffer`
|
||||
- `buf: Uint8Array`
|
||||
|
||||
Converts a protobuf serialized public key into its representative object.
|
||||
Returns `RsaPublicKey|Ed25519PublicKey|Secp256k1PublicKey`
|
||||
|
||||
### `crypto.keys.marshalPrivateKey(key[, type])`
|
||||
Converts a protobuf serialized public key into its representative object.
|
||||
|
||||
- `key: keys.rsa.RsaPrivateKey | keys.ed25519.Ed25519PrivateKey | require('libp2p-crypto-secp256k1').Secp256k1PrivateKey`
|
||||
### `crypto.keys.marshalPrivateKey(key, [type])`
|
||||
|
||||
- `key: keys.rsa.RsaPrivateKey | keys.ed25519.Ed25519PrivateKey | keys.secp256k1.Secp256k1PrivateKey`
|
||||
- `type: String`, see [Supported Key Types](#supported-key-types) above.
|
||||
|
||||
Returns `Uint8Array`
|
||||
|
||||
Converts a private key object into a protobuf serialized private key.
|
||||
|
||||
### `crypto.keys.unmarshalPrivateKey(buf, callback)`
|
||||
### `crypto.keys.unmarshalPrivateKey(buf)`
|
||||
|
||||
- `buf: Buffer`
|
||||
- `callback: Function`
|
||||
- `buf: Uint8Array`
|
||||
|
||||
Returns `Promise<RsaPrivateKey|Ed25519PrivateKey|Secp256k1PrivateKey>`
|
||||
|
||||
Converts a protobuf serialized private key into its representative object.
|
||||
|
||||
### `crypto.keys.import(encryptedKey, password)`
|
||||
|
||||
- `encryptedKey: string`
|
||||
- `password: string`
|
||||
|
||||
Returns `Promise<PrivateKey>`
|
||||
|
||||
Converts an exported private key into its representative object. Supported formats are 'pem' (RSA only) and 'libp2p-key'.
|
||||
|
||||
### `privateKey.export(password, format)`
|
||||
|
||||
- `password: string`
|
||||
- `format: string` the format to export to: 'pem' (rsa only), 'libp2p-key'
|
||||
|
||||
Returns `string`
|
||||
|
||||
Exports the password protected `PrivateKey`. RSA keys will be exported as password protected PEM by default. Ed25519 and Secp256k1 keys will be exported as password protected AES-GCM base64 encoded strings ('libp2p-key' format).
|
||||
|
||||
### `crypto.randomBytes(number)`
|
||||
|
||||
- `number: Number`
|
||||
|
||||
Generates a Buffer with length `number` populated by random bytes.
|
||||
Returns `Uint8Array`
|
||||
|
||||
Generates a Uint8Array 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
|
||||
|
||||
@@ -199,4 +311,4 @@ This repository falls under the IPFS [Code of Conduct](https://github.com/ipfs/c
|
||||
|
||||
## License
|
||||
|
||||
[MIT](LICENSE)
|
||||
[MIT](./LICENSE)
|
||||
|
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable no-console */
|
||||
'use strict'
|
||||
|
||||
const Benchmark = require('benchmark')
|
||||
@@ -9,18 +10,14 @@ const secrets = []
|
||||
const curves = ['P-256', 'P-384', 'P-521']
|
||||
|
||||
curves.forEach((curve) => {
|
||||
suite.add(`ephemeral key with secrect ${curve}`, (d) => {
|
||||
crypto.keys.generateEphemeralKeyPair('P-256', (err, res) => {
|
||||
if (err) { throw err }
|
||||
res.genSharedKey(res.key, (err, secret) => {
|
||||
if (err) { throw err }
|
||||
secrets.push(secret)
|
||||
|
||||
d.resolve()
|
||||
})
|
||||
})
|
||||
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})
|
||||
suite
|
||||
.on('cycle', (event) => console.log(String(event.target)))
|
||||
.run({ async: true })
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/* eslint-disable no-console */
|
||||
'use strict'
|
||||
|
||||
const Benchmark = require('benchmark')
|
||||
const async = require('async')
|
||||
|
||||
const crypto = require('../src')
|
||||
|
||||
@@ -12,27 +12,23 @@ const keys = []
|
||||
const ciphers = ['AES-128', 'AES-256', 'Blowfish']
|
||||
const hashes = ['SHA1', 'SHA256', 'SHA512']
|
||||
|
||||
async.waterfall([
|
||||
(cb) => crypto.keys.generateEphemeralKeyPair('P-256', cb),
|
||||
(res, cb) => res.genSharedKey(res.key, cb)
|
||||
], (err, secret) => {
|
||||
if (err) { throw err }
|
||||
;(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})
|
||||
})
|
||||
suite
|
||||
.on('cycle', (event) => console.log(String(event.target)))
|
||||
.run({ async: true })
|
||||
})()
|
||||
|
||||
function setup (cipher, hash, secret) {
|
||||
suite.add(`keyStretcher ${cipher} ${hash}`, (d) => {
|
||||
crypto.keys.keyStretcher(cipher, hash, secret, (err, k) => {
|
||||
if (err) { throw err }
|
||||
|
||||
keys.push(k)
|
||||
d.resolve()
|
||||
})
|
||||
suite.add(`keyStretcher ${cipher} ${hash}`, async (d) => {
|
||||
const k = await crypto.keys.keyStretcher(cipher, hash, secret)
|
||||
keys.push(k)
|
||||
d.resolve()
|
||||
}, { defer: true })
|
||||
}
|
||||
|
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable no-console */
|
||||
'use strict'
|
||||
|
||||
const Benchmark = require('benchmark')
|
||||
@@ -9,33 +10,28 @@ const keys = []
|
||||
const bits = [1024, 2048, 4096]
|
||||
|
||||
bits.forEach((bit) => {
|
||||
suite.add(`generateKeyPair ${bit}bits`, (d) => {
|
||||
crypto.keys.generateKeyPair('RSA', bit, (err, key) => {
|
||||
if (err) { throw err }
|
||||
keys.push(key)
|
||||
d.resolve()
|
||||
})
|
||||
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', (d) => {
|
||||
suite.add('sign and verify', async (d) => {
|
||||
const key = keys[0]
|
||||
const text = key.genSecret()
|
||||
|
||||
key.sign(text, (err, sig) => {
|
||||
if (err) { throw err }
|
||||
const sig = await key.sign(text)
|
||||
const res = await key.public.verify(text, sig)
|
||||
|
||||
key.public.verify(text, sig, (err, res) => {
|
||||
if (err) { throw err }
|
||||
if (res !== true) { throw new Error('failed to verify') }
|
||||
d.resolve()
|
||||
})
|
||||
})
|
||||
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})
|
||||
suite
|
||||
.on('cycle', (event) => console.log(String(event.target)))
|
||||
.run({ async: true })
|
||||
|
14
circle.yml
14
circle.yml
@@ -1,14 +0,0 @@
|
||||
machine:
|
||||
node:
|
||||
version: stable
|
||||
|
||||
dependencies:
|
||||
pre:
|
||||
- google-chrome --version
|
||||
- curl -L -o google-chrome.deb https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
|
||||
- sudo dpkg -i google-chrome.deb || true
|
||||
- sudo apt-get update
|
||||
- sudo apt-get install -f
|
||||
- sudo apt-get install --only-upgrade lsb-base
|
||||
- sudo dpkg -i google-chrome.deb
|
||||
- google-chrome --version
|
110
package.json
110
package.json
@@ -1,63 +1,74 @@
|
||||
{
|
||||
"name": "libp2p-crypto",
|
||||
"version": "0.9.2",
|
||||
"version": "0.19.2",
|
||||
"description": "Crypto primitives for libp2p",
|
||||
"main": "src/index.js",
|
||||
"types": "src/index.d.ts",
|
||||
"leadMaintainer": "Jacob Heun <jacobheun@gmail.com>",
|
||||
"browser": {
|
||||
"./src/aes/ciphers.js": "./src/aes/ciphers-browser.js",
|
||||
"./src/ciphers/aes-gcm.js": "./src/ciphers/aes-gcm.browser.js",
|
||||
"./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",
|
||||
"libp2p",
|
||||
"crypto",
|
||||
"rsa"
|
||||
"rsa",
|
||||
"secp256k1"
|
||||
],
|
||||
"author": "Friedel Ziegelmayer <dignifiedquire@gmail.com>",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"asn1.js": "^4.9.1",
|
||||
"async": "^2.5.0",
|
||||
"browserify-aes": "^1.0.6",
|
||||
"err-code": "^2.0.0",
|
||||
"is-typedarray": "^1.0.0",
|
||||
"iso-random-stream": "^1.1.0",
|
||||
"keypair": "^1.0.1",
|
||||
"libp2p-crypto-secp256k1": "^0.2.1",
|
||||
"nodeify": "^1.0.1",
|
||||
"pem-jwk": "^1.5.1",
|
||||
"protocol-buffers": "^3.2.1",
|
||||
"rsa-pem-to-jwk": "^1.1.3",
|
||||
"safe-buffer": "^5.1.1",
|
||||
"tweetnacl": "^1.0.0",
|
||||
"webcrypto-shim": "github:dignifiedquire/webcrypto-shim#master",
|
||||
"multihashing-async": "~0.4.6"
|
||||
"multibase": "^4.0.3",
|
||||
"multicodec": "^3.0.1",
|
||||
"multihashes": "^4.0.2",
|
||||
"multihashing-async": "^2.0.1",
|
||||
"node-forge": "^0.10.0",
|
||||
"pem-jwk": "^2.0.0",
|
||||
"protons": "^2.0.0",
|
||||
"secp256k1": "^4.0.0",
|
||||
"uint8arrays": "^2.1.4",
|
||||
"ursa-optional": "^0.10.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"aegir": "^11.0.2",
|
||||
"@types/chai": "^4.2.12",
|
||||
"@types/chai-string": "^1.4.2",
|
||||
"@types/dirty-chai": "^2.0.2",
|
||||
"@types/mocha": "^8.0.1",
|
||||
"aegir": "^25.0.0",
|
||||
"benchmark": "^2.1.4",
|
||||
"chai": "^4.1.0",
|
||||
"chai": "^4.2.0",
|
||||
"chai-string": "^1.5.0",
|
||||
"dirty-chai": "^2.0.1",
|
||||
"pre-commit": "^1.2.2"
|
||||
"sinon": "^9.0.0"
|
||||
},
|
||||
"pre-commit": [
|
||||
"lint",
|
||||
"test"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=4.0.0",
|
||||
"npm": ">=3.0.0"
|
||||
"node": ">=10.0.0",
|
||||
"npm": ">=6.0.0"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -69,14 +80,27 @@
|
||||
"homepage": "https://github.com/libp2p/js-libp2p-crypto",
|
||||
"contributors": [
|
||||
"David Dias <daviddias.p@gmail.com>",
|
||||
"Dmitriy Ryajov <dryajov@gmail.com>",
|
||||
"Friedel Ziegelmayer <dignifiedquire@gmail.com>",
|
||||
"Greenkeeper <support@greenkeeper.io>",
|
||||
"Jack Kleeman <jackkleeman@gmail.com>",
|
||||
"Richard Littauer <richard.littauer@gmail.com>",
|
||||
"Tom Swindell <t.swindell@rubyx.co.uk>",
|
||||
"Jacob Heun <jacobheun@gmail.com>",
|
||||
"Maciej Krüger <mkg20001@gmail.com>",
|
||||
"dryajov <dryajov@gmail.com>",
|
||||
"Alan Shaw <alan.shaw@protocol.ai>",
|
||||
"Hugo Dias <hugomrdias@gmail.com>",
|
||||
"Cayman <caymannava@gmail.com>",
|
||||
"Yusef Napora <yusef@napora.org>",
|
||||
"greenkeeper[bot] <greenkeeper[bot]@users.noreply.github.com>",
|
||||
"nikuda <nikuda@gmail.com>"
|
||||
"ᴠɪᴄᴛᴏʀ ʙᴊᴇʟᴋʜᴏʟᴍ <victorbjelkholm@gmail.com>",
|
||||
"Arve Knudsen <arve.knudsen@gmail.com>",
|
||||
"Alex Potsides <alex@achingbrain.net>",
|
||||
"Vasco Santos <vasco.santos@ua.pt>",
|
||||
"Alberto Elias <hi@albertoelias.me>",
|
||||
"Jack Kleeman <jackkleeman@gmail.com>",
|
||||
"Nadim Kobeissi <nadim@symbolic.software>",
|
||||
"Richard Littauer <richard.littauer@gmail.com>",
|
||||
"Richard Schneider <makaretu@gmail.com>",
|
||||
"dirkmc <dirkmdev@gmail.com>",
|
||||
"nikuda <nikuda@gmail.com>",
|
||||
"Joao Santos <jrmsantos15@gmail.com>",
|
||||
"Carson Farmer <carson.farmer@gmail.com>",
|
||||
"Tom Swindell <t.swindell@rubyx.co.uk>"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
17
src/aes/cipher-mode.js
Normal file
17
src/aes/cipher-mode.js
Normal 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
|
||||
}
|
@@ -1,8 +1,29 @@
|
||||
'use strict'
|
||||
|
||||
const crypto = require('browserify-aes')
|
||||
require('node-forge/lib/aes')
|
||||
const forge = require('node-forge/lib/forge')
|
||||
const uint8ArrayToString = require('uint8arrays/to-string')
|
||||
const uint8ArrayFromString = require('uint8arrays/from-string')
|
||||
|
||||
module.exports = {
|
||||
createCipheriv: crypto.createCipheriv,
|
||||
createDecipheriv: crypto.createDecipheriv
|
||||
createCipheriv: (mode, key, iv) => {
|
||||
const cipher2 = forge.cipher.createCipher('AES-CTR', uint8ArrayToString(key, 'ascii'))
|
||||
cipher2.start({ iv: uint8ArrayToString(iv, 'ascii') })
|
||||
return {
|
||||
update: (data) => {
|
||||
cipher2.update(forge.util.createBuffer(uint8ArrayToString(data, 'ascii')))
|
||||
return uint8ArrayFromString(cipher2.output.getBytes(), 'ascii')
|
||||
}
|
||||
}
|
||||
},
|
||||
createDecipheriv: (mode, key, iv) => {
|
||||
const cipher2 = forge.cipher.createDecipher('AES-CTR', uint8ArrayToString(key, 'ascii'))
|
||||
cipher2.start({ iv: uint8ArrayToString(iv, 'ascii') })
|
||||
return {
|
||||
update: (data) => {
|
||||
cipher2.update(forge.util.createBuffer(uint8ArrayToString(data, 'ascii')))
|
||||
return uint8ArrayFromString(cipher2.output.getBytes(), 'ascii')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,30 +1,22 @@
|
||||
'use strict'
|
||||
|
||||
const ciphers = require('./ciphers')
|
||||
const cipherMode = require('./cipher-mode')
|
||||
|
||||
const CIPHER_MODES = {
|
||||
16: 'aes-128-ctr',
|
||||
32: 'aes-256-ctr'
|
||||
}
|
||||
|
||||
exports.create = function (key, iv, callback) {
|
||||
const mode = CIPHER_MODES[key.length]
|
||||
if (!mode) {
|
||||
return callback(new Error('Invalid key length'))
|
||||
}
|
||||
|
||||
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 = {
|
||||
encrypt (data, cb) {
|
||||
cb(null, cipher.update(data))
|
||||
async encrypt (data) { // eslint-disable-line require-await
|
||||
return cipher.update(data)
|
||||
},
|
||||
|
||||
decrypt (data, cb) {
|
||||
cb(null, decipher.update(data))
|
||||
async decrypt (data) { // eslint-disable-line require-await
|
||||
return decipher.update(data)
|
||||
}
|
||||
}
|
||||
|
||||
callback(null, res)
|
||||
return res
|
||||
}
|
||||
|
89
src/ciphers/aes-gcm.browser.js
Normal file
89
src/ciphers/aes-gcm.browser.js
Normal file
@@ -0,0 +1,89 @@
|
||||
'use strict'
|
||||
|
||||
const concat = require('uint8arrays/concat')
|
||||
const fromString = require('uint8arrays/from-string')
|
||||
|
||||
const webcrypto = require('../webcrypto')
|
||||
|
||||
// Based off of code from https://github.com/luke-park/SecureCompatibleEncryptionExamples
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {object} [options]
|
||||
* @param {string} [options.algorithm=AES-GCM]
|
||||
* @param {Number} [options.nonceLength=12]
|
||||
* @param {Number} [options.keyLength=16]
|
||||
* @param {string} [options.digest=sha256]
|
||||
* @param {Number} [options.saltLength=16]
|
||||
* @param {Number} [options.iterations=32767]
|
||||
* @returns {*}
|
||||
*/
|
||||
function create ({
|
||||
algorithm = 'AES-GCM',
|
||||
nonceLength = 12,
|
||||
keyLength = 16,
|
||||
digest = 'SHA-256',
|
||||
saltLength = 16,
|
||||
iterations = 32767
|
||||
} = {}) {
|
||||
const crypto = webcrypto.get()
|
||||
keyLength *= 8 // Browser crypto uses bits instead of bytes
|
||||
|
||||
/**
|
||||
* Uses the provided password to derive a pbkdf2 key. The key
|
||||
* will then be used to encrypt the data.
|
||||
*
|
||||
* @param {Uint8Array} data The data to decrypt
|
||||
* @param {string} password A plain password
|
||||
* @returns {Promise<Uint8Array>}
|
||||
*/
|
||||
async function encrypt (data, password) { // eslint-disable-line require-await
|
||||
const salt = crypto.getRandomValues(new Uint8Array(saltLength))
|
||||
const nonce = crypto.getRandomValues(new Uint8Array(nonceLength))
|
||||
const aesGcm = { name: algorithm, iv: nonce }
|
||||
|
||||
// Derive a key using PBKDF2.
|
||||
const deriveParams = { name: 'PBKDF2', salt, iterations, hash: { name: digest } }
|
||||
const rawKey = await crypto.subtle.importKey('raw', fromString(password), { name: 'PBKDF2' }, false, ['deriveKey', 'deriveBits'])
|
||||
const cryptoKey = await crypto.subtle.deriveKey(deriveParams, rawKey, { name: algorithm, length: keyLength }, true, ['encrypt'])
|
||||
|
||||
// Encrypt the string.
|
||||
const ciphertext = await crypto.subtle.encrypt(aesGcm, cryptoKey, data)
|
||||
return concat([salt, aesGcm.iv, new Uint8Array(ciphertext)])
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the provided password to derive a pbkdf2 key. The key
|
||||
* will then be used to decrypt the data. The options used to create
|
||||
* this decryption cipher must be the same as those used to create
|
||||
* the encryption cipher.
|
||||
*
|
||||
* @param {Uint8Array} data The data to decrypt
|
||||
* @param {string} password A plain password
|
||||
* @returns {Promise<Uint8Array>}
|
||||
*/
|
||||
async function decrypt (data, password) {
|
||||
const salt = data.slice(0, saltLength)
|
||||
const nonce = data.slice(saltLength, saltLength + nonceLength)
|
||||
const ciphertext = data.slice(saltLength + nonceLength)
|
||||
const aesGcm = { name: algorithm, iv: nonce }
|
||||
|
||||
// Derive the key using PBKDF2.
|
||||
const deriveParams = { name: 'PBKDF2', salt, iterations, hash: { name: digest } }
|
||||
const rawKey = await crypto.subtle.importKey('raw', fromString(password), { name: 'PBKDF2' }, false, ['deriveKey', 'deriveBits'])
|
||||
const cryptoKey = await crypto.subtle.deriveKey(deriveParams, rawKey, { name: algorithm, length: keyLength }, true, ['decrypt'])
|
||||
|
||||
// Decrypt the string.
|
||||
const plaintext = await crypto.subtle.decrypt(aesGcm, cryptoKey, ciphertext)
|
||||
return new Uint8Array(plaintext)
|
||||
}
|
||||
|
||||
return {
|
||||
encrypt,
|
||||
decrypt
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
create
|
||||
}
|
130
src/ciphers/aes-gcm.js
Normal file
130
src/ciphers/aes-gcm.js
Normal file
@@ -0,0 +1,130 @@
|
||||
'use strict'
|
||||
|
||||
const crypto = require('crypto')
|
||||
const uint8ArrayConcat = require('uint8arrays/concat')
|
||||
const uint8ArrayFromString = require('uint8arrays/from-string')
|
||||
|
||||
// Based off of code from https://github.com/luke-park/SecureCompatibleEncryptionExamples
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {object} [options]
|
||||
* @param {Number} [options.algorithmTagLength=16]
|
||||
* @param {Number} [options.nonceLength=12]
|
||||
* @param {Number} [options.keyLength=16]
|
||||
* @param {string} [options.digest=sha256]
|
||||
* @param {Number} [options.saltLength=16]
|
||||
* @param {Number} [options.iterations=32767]
|
||||
* @returns {*}
|
||||
*/
|
||||
function create ({
|
||||
algorithmTagLength = 16,
|
||||
nonceLength = 12,
|
||||
keyLength = 16,
|
||||
digest = 'sha256',
|
||||
saltLength = 16,
|
||||
iterations = 32767
|
||||
} = {}) {
|
||||
const algorithm = 'aes-128-gcm'
|
||||
/**
|
||||
*
|
||||
* @private
|
||||
* @param {Uint8Array} data
|
||||
* @param {Uint8Array} key
|
||||
* @returns {Promise<Uint8Array>}
|
||||
*/
|
||||
async function encryptWithKey (data, key) { // eslint-disable-line require-await
|
||||
const nonce = crypto.randomBytes(nonceLength)
|
||||
|
||||
// Create the cipher instance.
|
||||
const cipher = crypto.createCipheriv(algorithm, key, nonce)
|
||||
|
||||
// Encrypt and prepend nonce.
|
||||
const ciphertext = uint8ArrayConcat([cipher.update(data), cipher.final()])
|
||||
|
||||
return uint8ArrayConcat([nonce, ciphertext, cipher.getAuthTag()])
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the provided password to derive a pbkdf2 key. The key
|
||||
* will then be used to encrypt the data.
|
||||
*
|
||||
* @param {Uint8Array} data The data to decrypt
|
||||
* @param {string|Uint8Array} password A plain password
|
||||
* @returns {Promise<Uint8Array>}
|
||||
*/
|
||||
async function encrypt (data, password) { // eslint-disable-line require-await
|
||||
// Generate a 128-bit salt using a CSPRNG.
|
||||
const salt = crypto.randomBytes(saltLength)
|
||||
|
||||
if (typeof password === 'string' || password instanceof String) {
|
||||
password = uint8ArrayFromString(password)
|
||||
}
|
||||
|
||||
// Derive a key using PBKDF2.
|
||||
const key = crypto.pbkdf2Sync(password, salt, iterations, keyLength, digest)
|
||||
|
||||
// Encrypt and prepend salt.
|
||||
return uint8ArrayConcat([salt, await encryptWithKey(Uint8Array.from(data), key)])
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypts the given cipher text with the provided key. The `key` should
|
||||
* be a cryptographically safe key and not a plaintext password. To use
|
||||
* a plaintext password, use `decrypt`. The options used to create
|
||||
* this decryption cipher must be the same as those used to create
|
||||
* the encryption cipher.
|
||||
*
|
||||
* @private
|
||||
* @param {Uint8Array} ciphertextAndNonce The data to decrypt
|
||||
* @param {Uint8Array} key
|
||||
* @returns {Promise<Uint8Array>}
|
||||
*/
|
||||
async function decryptWithKey (ciphertextAndNonce, key) { // eslint-disable-line require-await
|
||||
// Create Uint8Arrays of nonce, ciphertext and tag.
|
||||
const nonce = ciphertextAndNonce.slice(0, nonceLength)
|
||||
const ciphertext = ciphertextAndNonce.slice(nonceLength, ciphertextAndNonce.length - algorithmTagLength)
|
||||
const tag = ciphertextAndNonce.slice(ciphertext.length + nonceLength)
|
||||
|
||||
// Create the cipher instance.
|
||||
const cipher = crypto.createDecipheriv(algorithm, key, nonce)
|
||||
|
||||
// Decrypt and return result.
|
||||
cipher.setAuthTag(tag)
|
||||
return uint8ArrayConcat([cipher.update(ciphertext), cipher.final()])
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the provided password to derive a pbkdf2 key. The key
|
||||
* will then be used to decrypt the data. The options used to create
|
||||
* this decryption cipher must be the same as those used to create
|
||||
* the encryption cipher.
|
||||
*
|
||||
* @param {Uint8Array} data The data to decrypt
|
||||
* @param {string|Uint8Array} password A plain password
|
||||
*/
|
||||
async function decrypt (data, password) { // eslint-disable-line require-await
|
||||
// Create Uint8Arrays of salt and ciphertextAndNonce.
|
||||
const salt = data.slice(0, saltLength)
|
||||
const ciphertextAndNonce = data.slice(saltLength)
|
||||
|
||||
if (typeof password === 'string' || password instanceof String) {
|
||||
password = uint8ArrayFromString(password)
|
||||
}
|
||||
|
||||
// Derive the key using PBKDF2.
|
||||
const key = crypto.pbkdf2Sync(password, salt, iterations, keyLength, digest)
|
||||
|
||||
// Decrypt and return result.
|
||||
return decryptWithKey(ciphertextAndNonce, key)
|
||||
}
|
||||
|
||||
return {
|
||||
encrypt,
|
||||
decrypt
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
create
|
||||
}
|
@@ -1,9 +1,6 @@
|
||||
'use strict'
|
||||
|
||||
const nodeify = require('nodeify')
|
||||
const Buffer = require('safe-buffer').Buffer
|
||||
|
||||
const crypto = require('../webcrypto.js')()
|
||||
const webcrypto = require('../webcrypto')
|
||||
const lengths = require('./lengths')
|
||||
|
||||
const hashTypes = {
|
||||
@@ -12,25 +9,29 @@ const hashTypes = {
|
||||
SHA512: 'SHA-512'
|
||||
}
|
||||
|
||||
exports.create = function (hashType, secret, callback) {
|
||||
const sign = async (key, data) => {
|
||||
const buf = await webcrypto.get().subtle.sign({ name: 'HMAC' }, key, data)
|
||||
return new Uint8Array(buf, buf.byteOffset, buf.byteLength)
|
||||
}
|
||||
|
||||
exports.create = async function (hashType, secret) {
|
||||
const hash = hashTypes[hashType]
|
||||
|
||||
nodeify(crypto.subtle.importKey(
|
||||
const key = await webcrypto.get().subtle.importKey(
|
||||
'raw',
|
||||
secret,
|
||||
{
|
||||
name: 'HMAC',
|
||||
hash: {name: hash}
|
||||
hash: { name: hash }
|
||||
},
|
||||
false,
|
||||
['sign']
|
||||
).then((key) => {
|
||||
return {
|
||||
digest (data, cb) {
|
||||
nodeify(crypto.subtle.sign({name: 'HMAC'}, key, data)
|
||||
.then((raw) => Buffer.from(raw)), cb)
|
||||
},
|
||||
length: lengths[hashType]
|
||||
}
|
||||
}), callback)
|
||||
)
|
||||
|
||||
return {
|
||||
async digest (data) { // eslint-disable-line require-await
|
||||
return sign(key, data)
|
||||
},
|
||||
length: lengths[hashType]
|
||||
}
|
||||
}
|
||||
|
@@ -3,19 +3,15 @@
|
||||
const crypto = require('crypto')
|
||||
const lengths = require('./lengths')
|
||||
|
||||
exports.create = function (hash, secret, callback) {
|
||||
exports.create = async function (hash, secret) { // eslint-disable-line require-await
|
||||
const res = {
|
||||
digest (data, cb) {
|
||||
async digest (data) { // eslint-disable-line require-await
|
||||
const hmac = crypto.createHmac(hash.toLowerCase(), secret)
|
||||
|
||||
hmac.update(data)
|
||||
|
||||
setImmediate(() => {
|
||||
cb(null, hmac.digest())
|
||||
})
|
||||
return hmac.digest()
|
||||
},
|
||||
length: lengths[hash]
|
||||
}
|
||||
|
||||
callback(null, res)
|
||||
return res
|
||||
}
|
||||
|
338
src/index.d.ts
vendored
Normal file
338
src/index.d.ts
vendored
Normal file
@@ -0,0 +1,338 @@
|
||||
/**
|
||||
* 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: Uint8Array): Promise<Uint8Array>;
|
||||
decrypt(data: Uint8Array): Promise<Uint8Array>;
|
||||
}
|
||||
/**
|
||||
* 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: Uint8Array, iv: Uint8Array): 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: Uint8Array): Promise<Uint8Array>;
|
||||
length: 20 | 32 | 64 | number;
|
||||
}
|
||||
/**
|
||||
* Create a new HMAC Digest.
|
||||
*/
|
||||
function create(
|
||||
hash: "SHA1" | "SHA256" | "SHA512" | string,
|
||||
secret: Uint8Array
|
||||
): Promise<Digest>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic public key interface.
|
||||
*/
|
||||
export interface PublicKey {
|
||||
readonly bytes: Uint8Array;
|
||||
verify(data: Uint8Array, sig: Uint8Array): Promise<boolean>;
|
||||
marshal(): Uint8Array;
|
||||
equals(key: PublicKey): boolean;
|
||||
hash(): Promise<Uint8Array>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic private key interface.
|
||||
*/
|
||||
export interface PrivateKey {
|
||||
readonly public: PublicKey;
|
||||
readonly bytes: Uint8Array;
|
||||
sign(data: Uint8Array): Promise<Uint8Array>;
|
||||
marshal(): Uint8Array;
|
||||
equals(key: PrivateKey): boolean;
|
||||
hash(): Promise<Uint8Array>;
|
||||
/**
|
||||
* 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>;
|
||||
/**
|
||||
* Exports the password protected key in the format specified.
|
||||
*/
|
||||
export(password: string, format?: "pkcs-8" | string): Promise<string>;
|
||||
}
|
||||
|
||||
export interface Keystretcher {
|
||||
(res: Uint8Array): Keystretcher;
|
||||
iv: Uint8Array;
|
||||
cipherKey: Uint8Array;
|
||||
macKey: Uint8Array;
|
||||
}
|
||||
|
||||
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: Uint8Array);
|
||||
readonly bytes: Uint8Array;
|
||||
verify(data: Uint8Array, sig: Uint8Array): Promise<boolean>;
|
||||
marshal(): Uint8Array;
|
||||
encrypt(bytes: Uint8Array): Uint8Array;
|
||||
equals(key: PublicKey): boolean;
|
||||
hash(): Promise<Uint8Array>;
|
||||
}
|
||||
|
||||
class RsaPrivateKey implements PrivateKey {
|
||||
constructor(key: any, publicKey: Uint8Array);
|
||||
readonly public: RsaPublicKey;
|
||||
readonly bytes: Uint8Array;
|
||||
genSecret(): Uint8Array;
|
||||
sign(data: Uint8Array): Promise<Uint8Array>;
|
||||
decrypt(bytes: Uint8Array): Uint8Array;
|
||||
marshal(): Uint8Array;
|
||||
equals(key: PrivateKey): boolean;
|
||||
hash(): Promise<Uint8Array>;
|
||||
id(): Promise<string>;
|
||||
export(password: string, format?: string): Promise<string>;
|
||||
}
|
||||
function unmarshalRsaPublicKey(buf: Uint8Array): RsaPublicKey;
|
||||
function unmarshalRsaPrivateKey(buf: Uint8Array): Promise<RsaPrivateKey>;
|
||||
function generateKeyPair(bits: number): Promise<RsaPrivateKey>;
|
||||
function fromJwk(jwk: Uint8Array): Promise<RsaPrivateKey>;
|
||||
}
|
||||
|
||||
namespace ed25519 {
|
||||
class Ed25519PublicKey implements PublicKey {
|
||||
constructor(key: Uint8Array);
|
||||
readonly bytes: Uint8Array;
|
||||
verify(data: Uint8Array, sig: Uint8Array): Promise<boolean>;
|
||||
marshal(): Uint8Array;
|
||||
encrypt(bytes: Uint8Array): Uint8Array;
|
||||
equals(key: PublicKey): boolean;
|
||||
hash(): Promise<Uint8Array>;
|
||||
}
|
||||
|
||||
class Ed25519PrivateKey implements PrivateKey {
|
||||
constructor(key: Uint8Array, publicKey: Uint8Array);
|
||||
readonly public: Ed25519PublicKey;
|
||||
readonly bytes: Uint8Array;
|
||||
sign(data: Uint8Array): Promise<Uint8Array>;
|
||||
marshal(): Uint8Array;
|
||||
equals(key: PrivateKey): boolean;
|
||||
hash(): Promise<Uint8Array>;
|
||||
id(): Promise<string>;
|
||||
export(password: string, format?: string): Promise<string>;
|
||||
}
|
||||
|
||||
function unmarshalEd25519PrivateKey(
|
||||
buf: Uint8Array
|
||||
): Promise<Ed25519PrivateKey>;
|
||||
function unmarshalEd25519PublicKey(buf: Uint8Array): Ed25519PublicKey;
|
||||
function generateKeyPair(): Promise<Ed25519PrivateKey>;
|
||||
function generateKeyPairFromSeed(
|
||||
seed: Uint8Array
|
||||
): Promise<Ed25519PrivateKey>;
|
||||
}
|
||||
|
||||
namespace secp256k1 {
|
||||
class Secp256k1PublicKey implements PublicKey {
|
||||
constructor(key: Uint8Array);
|
||||
readonly bytes: Uint8Array;
|
||||
verify(data: Uint8Array, sig: Uint8Array): Promise<boolean>;
|
||||
marshal(): Uint8Array;
|
||||
encrypt(bytes: Uint8Array): Uint8Array;
|
||||
equals(key: PublicKey): boolean;
|
||||
hash(): Promise<Uint8Array>;
|
||||
}
|
||||
|
||||
class Secp256k1PrivateKey implements PrivateKey {
|
||||
constructor(key: Uint8Array, publicKey: Uint8Array);
|
||||
readonly public: Secp256k1PublicKey;
|
||||
readonly bytes: Uint8Array;
|
||||
sign(data: Uint8Array): Promise<Uint8Array>;
|
||||
marshal(): Uint8Array;
|
||||
equals(key: PrivateKey): boolean;
|
||||
hash(): Promise<Uint8Array>;
|
||||
id(): Promise<string>;
|
||||
export(password: string, format?: string): Promise<string>;
|
||||
}
|
||||
|
||||
function unmarshalSecp256k1PrivateKey(
|
||||
bytes: Uint8Array
|
||||
): Promise<Secp256k1PrivateKey>;
|
||||
function unmarshalSecp256k1PublicKey(bytes: Uint8Array): 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"
|
||||
): Promise<keys.supportedKeys.ed25519.Ed25519PrivateKey>;
|
||||
export function generateKeyPair(
|
||||
type: "RSA",
|
||||
bits: number
|
||||
): Promise<keys.supportedKeys.rsa.RsaPrivateKey>;
|
||||
export function generateKeyPair(
|
||||
type: "secp256k1"
|
||||
): 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: Uint8Array;
|
||||
genSharedKey: (theirPub: Uint8Array, forcePrivate?: any) => Promise<Uint8Array>;
|
||||
}>;
|
||||
|
||||
/**
|
||||
* 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: Uint8Array | string
|
||||
): Promise<StretchPair>;
|
||||
|
||||
/**
|
||||
* Converts a protobuf serialized public key into its representative object.
|
||||
* @param buf The protobuf serialized public key.
|
||||
*/
|
||||
export function unmarshalPublicKey(buf: Uint8Array): 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): Uint8Array;
|
||||
|
||||
/**
|
||||
* Converts a protobuf serialized private key into its representative object.
|
||||
* @param buf The protobuf serialized private key.
|
||||
*/
|
||||
export function unmarshalPrivateKey(buf: Uint8Array): 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): Uint8Array;
|
||||
|
||||
/**
|
||||
* 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, format?: string): Promise<supportedKeys.rsa.RsaPrivateKey>;
|
||||
export { _import as import };
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a Uint8Array populated by random bytes.
|
||||
* @param The size of the random bytes Uint8Array.
|
||||
*/
|
||||
export function randomBytes(number: number): Uint8Array;
|
||||
|
||||
/**
|
||||
* 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 | Uint8Array,
|
||||
salt: string | Uint8Array,
|
||||
iterations: number,
|
||||
keySize: number,
|
||||
hash: string
|
||||
): Uint8Array;
|
13
src/index.js
13
src/index.js
@@ -3,18 +3,9 @@
|
||||
const hmac = require('./hmac')
|
||||
const aes = require('./aes')
|
||||
const keys = require('./keys')
|
||||
const rsa = require('./keys/rsa')
|
||||
|
||||
exports = module.exports
|
||||
|
||||
exports.aes = aes
|
||||
exports.hmac = hmac
|
||||
exports.keys = keys
|
||||
|
||||
exports.randomBytes = (number) => {
|
||||
if (!number || typeof number !== 'number') {
|
||||
throw new Error('first argument must be a Number bigger than 0')
|
||||
}
|
||||
|
||||
return rsa.getRandomValues(new Uint8Array(number))
|
||||
}
|
||||
exports.randomBytes = require('./random-bytes')
|
||||
exports.pbkdf2 = require('./pbkdf2')
|
||||
|
@@ -1,13 +1,12 @@
|
||||
'use strict'
|
||||
|
||||
const webcrypto = require('../webcrypto.js')()
|
||||
const nodeify = require('nodeify')
|
||||
const BN = require('asn1.js').bignum
|
||||
const Buffer = require('safe-buffer').Buffer
|
||||
|
||||
const util = require('../util')
|
||||
const toBase64 = util.toBase64
|
||||
const toBn = util.toBn
|
||||
const errcode = require('err-code')
|
||||
const webcrypto = require('../webcrypto')
|
||||
const { base64urlToBuffer } = require('../util')
|
||||
const validateCurveType = require('./validate-curve-type')
|
||||
const uint8ArrayToString = require('uint8arrays/to-string')
|
||||
const uint8ArrayConcat = require('uint8arrays/concat')
|
||||
const uint8ArrayEquals = require('uint8arrays/equals')
|
||||
|
||||
const bits = {
|
||||
'P-256': 256,
|
||||
@@ -15,72 +14,69 @@ const bits = {
|
||||
'P-521': 521
|
||||
}
|
||||
|
||||
exports.generateEphmeralKeyPair = function (curve, callback) {
|
||||
nodeify(webcrypto.subtle.generateKey(
|
||||
exports.generateEphmeralKeyPair = async function (curve) {
|
||||
validateCurveType(Object.keys(bits), curve)
|
||||
const pair = await webcrypto.get().subtle.generateKey(
|
||||
{
|
||||
name: 'ECDH',
|
||||
namedCurve: curve
|
||||
},
|
||||
true,
|
||||
['deriveBits']
|
||||
).then((pair) => {
|
||||
// forcePrivate is used for testing only
|
||||
const genSharedKey = (theirPub, forcePrivate, cb) => {
|
||||
if (typeof forcePrivate === 'function') {
|
||||
cb = forcePrivate
|
||||
forcePrivate = undefined
|
||||
}
|
||||
)
|
||||
|
||||
let privateKey
|
||||
// forcePrivate is used for testing only
|
||||
const genSharedKey = async (theirPub, forcePrivate) => {
|
||||
let privateKey
|
||||
|
||||
if (forcePrivate) {
|
||||
privateKey = webcrypto.subtle.importKey(
|
||||
'jwk',
|
||||
unmarshalPrivateKey(curve, forcePrivate),
|
||||
{
|
||||
name: 'ECDH',
|
||||
namedCurve: curve
|
||||
},
|
||||
false,
|
||||
['deriveBits']
|
||||
)
|
||||
} else {
|
||||
privateKey = Promise.resolve(pair.privateKey)
|
||||
}
|
||||
|
||||
const keys = Promise.all([
|
||||
webcrypto.subtle.importKey(
|
||||
'jwk',
|
||||
unmarshalPublicKey(curve, theirPub),
|
||||
{
|
||||
name: 'ECDH',
|
||||
namedCurve: curve
|
||||
},
|
||||
false,
|
||||
[]
|
||||
),
|
||||
privateKey
|
||||
])
|
||||
|
||||
nodeify(keys.then((keys) => webcrypto.subtle.deriveBits(
|
||||
if (forcePrivate) {
|
||||
privateKey = await webcrypto.get().subtle.importKey(
|
||||
'jwk',
|
||||
unmarshalPrivateKey(curve, forcePrivate),
|
||||
{
|
||||
name: 'ECDH',
|
||||
namedCurve: curve,
|
||||
public: keys[0]
|
||||
namedCurve: curve
|
||||
},
|
||||
keys[1],
|
||||
bits[curve]
|
||||
)).then((bits) => Buffer.from(bits)), cb)
|
||||
false,
|
||||
['deriveBits']
|
||||
)
|
||||
} else {
|
||||
privateKey = pair.privateKey
|
||||
}
|
||||
|
||||
return webcrypto.subtle.exportKey('jwk', pair.publicKey)
|
||||
.then((publicKey) => {
|
||||
return {
|
||||
key: marshalPublicKey(publicKey),
|
||||
genSharedKey
|
||||
}
|
||||
})
|
||||
}), callback)
|
||||
const keys = [
|
||||
await webcrypto.get().subtle.importKey(
|
||||
'jwk',
|
||||
unmarshalPublicKey(curve, theirPub),
|
||||
{
|
||||
name: 'ECDH',
|
||||
namedCurve: curve
|
||||
},
|
||||
false,
|
||||
[]
|
||||
),
|
||||
privateKey
|
||||
]
|
||||
|
||||
const buffer = await webcrypto.get().subtle.deriveBits(
|
||||
{
|
||||
name: 'ECDH',
|
||||
namedCurve: curve,
|
||||
public: keys[0]
|
||||
},
|
||||
keys[1],
|
||||
bits[curve]
|
||||
)
|
||||
|
||||
return new Uint8Array(buffer, buffer.byteOffset, buffer.byteLength)
|
||||
}
|
||||
|
||||
const publicKey = await webcrypto.get().subtle.exportKey('jwk', pair.publicKey)
|
||||
|
||||
return {
|
||||
key: marshalPublicKey(publicKey),
|
||||
genSharedKey
|
||||
}
|
||||
}
|
||||
|
||||
const curveLengths = {
|
||||
@@ -95,10 +91,10 @@ const curveLengths = {
|
||||
function marshalPublicKey (jwk) {
|
||||
const byteLen = curveLengths[jwk.crv]
|
||||
|
||||
return Buffer.concat([
|
||||
Buffer.from([4]), // uncompressed point
|
||||
toBn(jwk.x).toArrayLike(Buffer, 'be', byteLen),
|
||||
toBn(jwk.y).toArrayLike(Buffer, 'be', byteLen)
|
||||
return uint8ArrayConcat([
|
||||
Uint8Array.from([4]), // uncompressed point
|
||||
base64urlToBuffer(jwk.x, byteLen),
|
||||
base64urlToBuffer(jwk.y, byteLen)
|
||||
], 1 + byteLen * 2)
|
||||
}
|
||||
|
||||
@@ -106,23 +102,20 @@ function marshalPublicKey (jwk) {
|
||||
function unmarshalPublicKey (curve, key) {
|
||||
const byteLen = curveLengths[curve]
|
||||
|
||||
if (!key.slice(0, 1).equals(Buffer.from([4]))) {
|
||||
throw new Error('Invalid key format')
|
||||
if (uint8ArrayEquals(!key.slice(0, 1), Uint8Array.from([4]))) {
|
||||
throw errcode(new Error('Cannot unmarshal public key - invalid key format'), 'ERR_INVALID_KEY_FORMAT')
|
||||
}
|
||||
const x = new BN(key.slice(1, byteLen + 1))
|
||||
const y = new BN(key.slice(1 + byteLen))
|
||||
|
||||
return {
|
||||
kty: 'EC',
|
||||
crv: curve,
|
||||
x: toBase64(x, byteLen),
|
||||
y: toBase64(y, byteLen),
|
||||
x: uint8ArrayToString(key.slice(1, byteLen + 1), 'base64url'),
|
||||
y: uint8ArrayToString(key.slice(1 + byteLen), 'base64url'),
|
||||
ext: true
|
||||
}
|
||||
}
|
||||
|
||||
function unmarshalPrivateKey (curve, key) {
|
||||
const result = unmarshalPublicKey(curve, key.public)
|
||||
result.d = toBase64(new BN(key.private))
|
||||
return result
|
||||
}
|
||||
const unmarshalPrivateKey = (curve, key) => ({
|
||||
...unmarshalPublicKey(curve, key.public),
|
||||
d: uint8ArrayToString(key.private, 'base64url')
|
||||
})
|
||||
|
@@ -1,7 +1,7 @@
|
||||
'use strict'
|
||||
|
||||
const crypto = require('crypto')
|
||||
const setImmediate = require('async/setImmediate')
|
||||
const validateCurveType = require('./validate-curve-type')
|
||||
|
||||
const curves = {
|
||||
'P-256': 'prime256v1',
|
||||
@@ -9,33 +9,20 @@ const curves = {
|
||||
'P-521': 'secp521r1'
|
||||
}
|
||||
|
||||
exports.generateEphmeralKeyPair = function (curve, callback) {
|
||||
if (!curves[curve]) {
|
||||
return callback(new Error(`Unkown curve: ${curve}`))
|
||||
}
|
||||
exports.generateEphmeralKeyPair = async function (curve) { // eslint-disable-line require-await
|
||||
validateCurveType(Object.keys(curves), curve)
|
||||
|
||||
const ecdh = crypto.createECDH(curves[curve])
|
||||
ecdh.generateKeys()
|
||||
|
||||
setImmediate(() => callback(null, {
|
||||
return {
|
||||
key: ecdh.getPublicKey(),
|
||||
genSharedKey (theirPub, forcePrivate, cb) {
|
||||
if (typeof forcePrivate === 'function') {
|
||||
cb = forcePrivate
|
||||
forcePrivate = null
|
||||
}
|
||||
|
||||
async genSharedKey (theirPub, forcePrivate) { // eslint-disable-line require-await
|
||||
if (forcePrivate) {
|
||||
ecdh.setPrivateKey(forcePrivate.private)
|
||||
}
|
||||
|
||||
let secret
|
||||
try {
|
||||
secret = ecdh.computeSecret(theirPub)
|
||||
} catch (err) {
|
||||
return cb(err)
|
||||
}
|
||||
|
||||
setImmediate(() => cb(null, secret))
|
||||
return ecdh.computeSecret(theirPub)
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
@@ -1,24 +1,25 @@
|
||||
'use strict'
|
||||
|
||||
const multihashing = require('multihashing-async')
|
||||
const protobuf = require('protocol-buffers')
|
||||
const Buffer = require('safe-buffer').Buffer
|
||||
|
||||
const sha = require('multihashing-async/src/sha')
|
||||
const protobuf = require('protons')
|
||||
const errcode = require('err-code')
|
||||
const uint8ArrayEquals = require('uint8arrays/equals')
|
||||
const mh = require('multihashes')
|
||||
const crypto = require('./ed25519')
|
||||
const pbm = protobuf(require('./keys.proto'))
|
||||
const exporter = require('./exporter')
|
||||
|
||||
class Ed25519PublicKey {
|
||||
constructor (key) {
|
||||
this._key = ensureKey(key, crypto.publicKeyLength)
|
||||
}
|
||||
|
||||
verify (data, sig, callback) {
|
||||
ensure(callback)
|
||||
crypto.hashAndVerify(this._key, sig, data, callback)
|
||||
async verify (data, sig) { // eslint-disable-line require-await
|
||||
return crypto.hashAndVerify(this._key, sig, data)
|
||||
}
|
||||
|
||||
marshal () {
|
||||
return Buffer.from(this._key)
|
||||
return this._key
|
||||
}
|
||||
|
||||
get bytes () {
|
||||
@@ -29,38 +30,32 @@ class Ed25519PublicKey {
|
||||
}
|
||||
|
||||
equals (key) {
|
||||
return this.bytes.equals(key.bytes)
|
||||
return uint8ArrayEquals(this.bytes, key.bytes)
|
||||
}
|
||||
|
||||
hash (callback) {
|
||||
ensure(callback)
|
||||
multihashing(this.bytes, 'sha2-256', callback)
|
||||
async hash () { // eslint-disable-line require-await
|
||||
return sha.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
|
||||
// key - 64 byte Uint8Array containing private key
|
||||
// publicKey - 32 byte Uint8Array containing public key
|
||||
constructor (key, publicKey) {
|
||||
this._key = ensureKey(key, crypto.privateKeyLength)
|
||||
this._publicKey = ensureKey(publicKey, crypto.publicKeyLength)
|
||||
}
|
||||
|
||||
sign (message, callback) {
|
||||
ensure(callback)
|
||||
crypto.hashAndSign(this._key, message, callback)
|
||||
async sign (message) { // eslint-disable-line require-await
|
||||
return crypto.hashAndSign(this._key, message)
|
||||
}
|
||||
|
||||
get public () {
|
||||
if (!this._publicKey) {
|
||||
throw new Error('public key not provided')
|
||||
}
|
||||
|
||||
return new Ed25519PublicKey(this._publicKey)
|
||||
}
|
||||
|
||||
marshal () {
|
||||
return Buffer.concat([Buffer.from(this._key), Buffer.from(this._publicKey)])
|
||||
return this._key
|
||||
}
|
||||
|
||||
get bytes () {
|
||||
@@ -71,24 +66,56 @@ class Ed25519PrivateKey {
|
||||
}
|
||||
|
||||
equals (key) {
|
||||
return this.bytes.equals(key.bytes)
|
||||
return uint8ArrayEquals(this.bytes, key.bytes)
|
||||
}
|
||||
|
||||
hash (callback) {
|
||||
ensure(callback)
|
||||
multihashing(this.bytes, 'sha2-256', callback)
|
||||
async hash () { // eslint-disable-line require-await
|
||||
return sha.multihashing(this.bytes, 'sha2-256')
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the ID of the key.
|
||||
*
|
||||
* The key id is the base58 encoding of the SHA-256 multihash of its public key.
|
||||
* The public key is a protobuf encoding containing a type and the DER encoding
|
||||
* of the PKCS SubjectPublicKeyInfo.
|
||||
*
|
||||
* @returns {Promise<String>}
|
||||
*/
|
||||
async id () {
|
||||
const encoding = mh.encode(this.public.bytes, 'identity')
|
||||
return await mh.toB58String(encoding)
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports the key into a password protected `format`
|
||||
*
|
||||
* @param {string} password - The password to encrypt the key
|
||||
* @param {string} [format=libp2p-key] - The format in which to export as
|
||||
* @returns {Promise<Uint8Array>} The encrypted private key
|
||||
*/
|
||||
async export (password, format = 'libp2p-key') { // eslint-disable-line require-await
|
||||
if (format === 'libp2p-key') {
|
||||
return exporter.export(this.bytes, password)
|
||||
} else {
|
||||
throw errcode(new Error(`export format '${format}' is not supported`), 'ERR_INVALID_EXPORT_FORMAT')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function unmarshalEd25519PrivateKey (bytes, callback) {
|
||||
try {
|
||||
function unmarshalEd25519PrivateKey (bytes) {
|
||||
// Try the old, redundant public key version
|
||||
if (bytes.length > crypto.privateKeyLength) {
|
||||
bytes = ensureKey(bytes, crypto.privateKeyLength + crypto.publicKeyLength)
|
||||
} catch (err) {
|
||||
return callback(err)
|
||||
const privateKeyBytes = bytes.slice(0, crypto.privateKeyLength)
|
||||
const publicKeyBytes = bytes.slice(crypto.privateKeyLength, bytes.length)
|
||||
return new Ed25519PrivateKey(privateKeyBytes, publicKeyBytes)
|
||||
}
|
||||
|
||||
bytes = ensureKey(bytes, crypto.privateKeyLength)
|
||||
const privateKeyBytes = bytes.slice(0, crypto.privateKeyLength)
|
||||
const publicKeyBytes = bytes.slice(crypto.privateKeyLength, bytes.length)
|
||||
callback(null, new Ed25519PrivateKey(privateKeyBytes, publicKeyBytes))
|
||||
const publicKeyBytes = bytes.slice(crypto.publicKeyLength)
|
||||
return new Ed25519PrivateKey(privateKeyBytes, publicKeyBytes)
|
||||
}
|
||||
|
||||
function unmarshalEd25519PublicKey (bytes) {
|
||||
@@ -96,60 +123,20 @@ function unmarshalEd25519PublicKey (bytes) {
|
||||
return new Ed25519PublicKey(bytes)
|
||||
}
|
||||
|
||||
function generateKeyPair (_bits, cb) {
|
||||
if (cb === undefined && typeof _bits === 'function') {
|
||||
cb = _bits
|
||||
}
|
||||
|
||||
crypto.generateKey((err, keys) => {
|
||||
if (err) {
|
||||
return cb(err)
|
||||
}
|
||||
let privkey
|
||||
try {
|
||||
privkey = new Ed25519PrivateKey(keys.secretKey, keys.publicKey)
|
||||
} catch (err) {
|
||||
cb(err)
|
||||
return
|
||||
}
|
||||
|
||||
cb(null, privkey)
|
||||
})
|
||||
async function generateKeyPair () {
|
||||
const { privateKey, publicKey } = await crypto.generateKey()
|
||||
return new Ed25519PrivateKey(privateKey, publicKey)
|
||||
}
|
||||
|
||||
function generateKeyPairFromSeed (seed, _bits, cb) {
|
||||
if (cb === undefined && typeof _bits === 'function') {
|
||||
cb = _bits
|
||||
}
|
||||
|
||||
crypto.generateKeyFromSeed(seed, (err, keys) => {
|
||||
if (err) {
|
||||
return cb(err)
|
||||
}
|
||||
let privkey
|
||||
try {
|
||||
privkey = new Ed25519PrivateKey(keys.secretKey, keys.publicKey)
|
||||
} catch (err) {
|
||||
cb(err)
|
||||
return
|
||||
}
|
||||
|
||||
cb(null, privkey)
|
||||
})
|
||||
}
|
||||
|
||||
function ensure (cb) {
|
||||
if (typeof cb !== 'function') {
|
||||
throw new Error('callback is required')
|
||||
}
|
||||
async function generateKeyPairFromSeed (seed) {
|
||||
const { privateKey, publicKey } = await crypto.generateKeyFromSeed(seed)
|
||||
return new Ed25519PrivateKey(privateKey, publicKey)
|
||||
}
|
||||
|
||||
function ensureKey (key, length) {
|
||||
if (Buffer.isBuffer(key)) {
|
||||
key = new Uint8Array(key)
|
||||
}
|
||||
if (!(key instanceof Uint8Array) || key.length !== length) {
|
||||
throw new Error('Key must be a Uint8Array or Buffer of length ' + length)
|
||||
key = Uint8Array.from(key || [])
|
||||
if (key.length !== length) {
|
||||
throw errcode(new Error(`Key must be a Uint8Array of length ${length}, got ${key.length}`), 'ERR_INVALID_KEY_TYPE')
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
@@ -1,47 +1,24 @@
|
||||
'use strict'
|
||||
|
||||
const nacl = require('tweetnacl')
|
||||
const setImmediate = require('async/setImmediate')
|
||||
const Buffer = require('safe-buffer').Buffer
|
||||
require('node-forge/lib/ed25519')
|
||||
const forge = require('node-forge/lib/forge')
|
||||
exports.publicKeyLength = forge.pki.ed25519.constants.PUBLIC_KEY_BYTE_LENGTH
|
||||
exports.privateKeyLength = forge.pki.ed25519.constants.PRIVATE_KEY_BYTE_LENGTH
|
||||
|
||||
exports.publicKeyLength = nacl.sign.publicKeyLength
|
||||
exports.privateKeyLength = nacl.sign.secretKeyLength
|
||||
|
||||
exports.generateKey = function (callback) {
|
||||
const done = (err, res) => setImmediate(() => {
|
||||
callback(err, res)
|
||||
})
|
||||
|
||||
let keys
|
||||
try {
|
||||
keys = nacl.sign.keyPair()
|
||||
} catch (err) {
|
||||
return done(err)
|
||||
}
|
||||
done(null, keys)
|
||||
exports.generateKey = async function () { // eslint-disable-line require-await
|
||||
return forge.pki.ed25519.generateKeyPair()
|
||||
}
|
||||
|
||||
// seed should be a 32 byte uint8array
|
||||
exports.generateKeyFromSeed = function (seed, callback) {
|
||||
const done = (err, res) => setImmediate(() => callback(err, res))
|
||||
|
||||
let keys
|
||||
try {
|
||||
keys = nacl.sign.keyPair.fromSeed(seed)
|
||||
} catch (err) {
|
||||
return done(err)
|
||||
}
|
||||
done(null, keys)
|
||||
exports.generateKeyFromSeed = async function (seed) { // eslint-disable-line require-await
|
||||
return forge.pki.ed25519.generateKeyPair({ seed })
|
||||
}
|
||||
|
||||
exports.hashAndSign = function (key, msg, callback) {
|
||||
setImmediate(() => {
|
||||
callback(null, Buffer.from(nacl.sign.detached(msg, key)))
|
||||
})
|
||||
exports.hashAndSign = async function (key, msg) { // eslint-disable-line require-await
|
||||
return forge.pki.ed25519.sign({ message: msg, privateKey: key })
|
||||
// return Uint8Array.from(nacl.sign.detached(msg, key))
|
||||
}
|
||||
|
||||
exports.hashAndVerify = function (key, sig, msg, callback) {
|
||||
setImmediate(() => {
|
||||
callback(null, nacl.sign.detached.verify(msg, sig, key))
|
||||
})
|
||||
exports.hashAndVerify = async function (key, sig, msg) { // eslint-disable-line require-await
|
||||
return forge.pki.ed25519.verify({ signature: sig, message: msg, publicKey: key })
|
||||
}
|
||||
|
@@ -6,6 +6,4 @@ const ecdh = require('./ecdh')
|
||||
// the shared secret key.
|
||||
//
|
||||
// Focuses only on ECDH now, but can be made more general in the future.
|
||||
module.exports = (curve, callback) => {
|
||||
ecdh.generateEphmeralKeyPair(curve, callback)
|
||||
}
|
||||
module.exports = async (curve) => ecdh.generateEphmeralKeyPair(curve) // eslint-disable-line require-await
|
||||
|
22
src/keys/exporter.js
Normal file
22
src/keys/exporter.js
Normal file
@@ -0,0 +1,22 @@
|
||||
'use strict'
|
||||
|
||||
const multibase = require('multibase')
|
||||
const ciphers = require('../ciphers/aes-gcm')
|
||||
|
||||
module.exports = {
|
||||
/**
|
||||
* Exports the given PrivateKey as a base64 encoded string.
|
||||
* The PrivateKey is encrypted via a password derived PBKDF2 key
|
||||
* leveraging the aes-gcm cipher algorithm.
|
||||
*
|
||||
* @param {Uint8Array} privateKey The PrivateKey protobuf
|
||||
* @param {string} password
|
||||
* @returns {Promise<string>} A base64 encoded string
|
||||
*/
|
||||
export: async function (privateKey, password) {
|
||||
const cipher = ciphers.create()
|
||||
const encryptedKey = await cipher.encrypt(privateKey, password)
|
||||
const base64 = multibase.names.base64
|
||||
return base64.encode(encryptedKey)
|
||||
}
|
||||
}
|
22
src/keys/importer.js
Normal file
22
src/keys/importer.js
Normal file
@@ -0,0 +1,22 @@
|
||||
'use strict'
|
||||
|
||||
const multibase = require('multibase')
|
||||
const ciphers = require('../ciphers/aes-gcm')
|
||||
|
||||
module.exports = {
|
||||
/**
|
||||
* Attempts to decrypt a base64 encoded PrivateKey string
|
||||
* with the given password. The privateKey must have been exported
|
||||
* using the same password and underlying cipher (aes-gcm)
|
||||
*
|
||||
* @param {string} privateKey A base64 encoded encrypted key
|
||||
* @param {string} password
|
||||
* @returns {Promise<Uint8Array>} The private key protobuf
|
||||
*/
|
||||
import: async function (privateKey, password) {
|
||||
const base64 = multibase.names.base64
|
||||
const encryptedKey = base64.decode(privateKey)
|
||||
const cipher = ciphers.create()
|
||||
return await cipher.decrypt(encryptedKey, password)
|
||||
}
|
||||
}
|
@@ -1,104 +1,135 @@
|
||||
'use strict'
|
||||
|
||||
const protobuf = require('protocol-buffers')
|
||||
const pbm = protobuf(require('./keys.proto'))
|
||||
const protobuf = require('protons')
|
||||
const keysPBM = protobuf(require('./keys.proto'))
|
||||
require('node-forge/lib/asn1')
|
||||
require('node-forge/lib/pbe')
|
||||
const forge = require('node-forge/lib/forge')
|
||||
const errcode = require('err-code')
|
||||
const uint8ArrayFromString = require('uint8arrays/from-string')
|
||||
|
||||
const keys = exports.keys = require('./keys')
|
||||
const importer = require('./importer')
|
||||
|
||||
exports = module.exports
|
||||
|
||||
exports.pbm = pbm
|
||||
const supportedKeys = {
|
||||
rsa: require('./rsa-class'),
|
||||
ed25519: require('./ed25519-class'),
|
||||
secp256k1: require('./secp256k1-class')(keysPBM, require('../random-bytes'))
|
||||
}
|
||||
|
||||
function isValidKeyType (keyType) {
|
||||
const key = keys[keyType.toLowerCase()]
|
||||
return key !== undefined
|
||||
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 = (type, bits, cb) => {
|
||||
let key = keys[type.toLowerCase()]
|
||||
|
||||
if (!key) {
|
||||
return cb(new Error('invalid or unsupported key type'))
|
||||
}
|
||||
|
||||
key.generateKeyPair(bits, cb)
|
||||
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 = (type, seed, bits, cb) => {
|
||||
let key = keys[type.toLowerCase()]
|
||||
if (!key) {
|
||||
return cb(new Error('invalid or unsupported key type'))
|
||||
}
|
||||
exports.generateKeyPairFromSeed = async (type, seed, bits) => { // eslint-disable-line require-await
|
||||
const key = typeToKey(type)
|
||||
if (type.toLowerCase() !== 'ed25519') {
|
||||
return cb(new Error('Seed key derivation is unimplemented for RSA or secp256k1'))
|
||||
throw errcode(new Error('Seed key derivation is unimplemented for RSA or secp256k1'), 'ERR_UNSUPPORTED_KEY_DERIVATION_TYPE')
|
||||
}
|
||||
key.generateKeyPairFromSeed(seed, bits, cb)
|
||||
return key.generateKeyPairFromSeed(seed, bits)
|
||||
}
|
||||
|
||||
// Converts a protobuf serialized public key into its
|
||||
// representative object
|
||||
exports.unmarshalPublicKey = (buf) => {
|
||||
const decoded = pbm.PublicKey.decode(buf)
|
||||
const decoded = keysPBM.PublicKey.decode(buf)
|
||||
const data = decoded.Data
|
||||
|
||||
switch (decoded.Type) {
|
||||
case pbm.KeyType.RSA:
|
||||
return keys.rsa.unmarshalRsaPublicKey(decoded.Data)
|
||||
case pbm.KeyType.Ed25519:
|
||||
return keys.ed25519.unmarshalEd25519PublicKey(decoded.Data)
|
||||
case pbm.KeyType.Secp256k1:
|
||||
if (keys.secp256k1) {
|
||||
return keys.secp256k1.unmarshalSecp256k1PublicKey(decoded.Data)
|
||||
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 new Error('secp256k1 support requires libp2p-crypto-secp256k1 package')
|
||||
throw errcode(new Error(ErrMissingSecp256K1.message), ErrMissingSecp256K1.code)
|
||||
}
|
||||
default:
|
||||
throw new Error('invalid or unsupported key type')
|
||||
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()
|
||||
if (!isValidKeyType(type)) {
|
||||
throw new Error('invalid or unsupported key type')
|
||||
}
|
||||
|
||||
typeToKey(type) // check type
|
||||
return key.bytes
|
||||
}
|
||||
|
||||
// Converts a protobuf serialized private key into its
|
||||
// representative object
|
||||
exports.unmarshalPrivateKey = (buf, callback) => {
|
||||
const decoded = pbm.PrivateKey.decode(buf)
|
||||
exports.unmarshalPrivateKey = async (buf) => { // eslint-disable-line require-await
|
||||
const decoded = keysPBM.PrivateKey.decode(buf)
|
||||
const data = decoded.Data
|
||||
|
||||
switch (decoded.Type) {
|
||||
case pbm.KeyType.RSA:
|
||||
return keys.rsa.unmarshalRsaPrivateKey(decoded.Data, callback)
|
||||
case pbm.KeyType.Ed25519:
|
||||
return keys.ed25519.unmarshalEd25519PrivateKey(decoded.Data, callback)
|
||||
case pbm.KeyType.Secp256k1:
|
||||
if (keys.secp256k1) {
|
||||
return keys.secp256k1.unmarshalSecp256k1PrivateKey(decoded.Data, callback)
|
||||
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 {
|
||||
return callback(new Error('secp256k1 support requires libp2p-crypto-secp256k1 package'))
|
||||
throw errcode(new Error(ErrMissingSecp256K1.message), ErrMissingSecp256K1.code)
|
||||
}
|
||||
default:
|
||||
callback(new Error('invalid or unsupported key type'))
|
||||
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()
|
||||
if (!isValidKeyType(type)) {
|
||||
throw new Error('invalid or unsupported key type')
|
||||
}
|
||||
|
||||
typeToKey(type) // check type
|
||||
return key.bytes
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} encryptedKey
|
||||
* @param {string} password
|
||||
*/
|
||||
exports.import = async (encryptedKey, password) => { // eslint-disable-line require-await
|
||||
try {
|
||||
const key = await importer.import(encryptedKey, password)
|
||||
return exports.unmarshalPrivateKey(key)
|
||||
} catch (_) {
|
||||
// Ignore and try the old pem decrypt
|
||||
}
|
||||
|
||||
// Only rsa supports pem right now
|
||||
const key = forge.pki.decryptRsaPrivateKey(encryptedKey, 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 = uint8ArrayFromString(der.getBytes(), 'ascii')
|
||||
return supportedKeys.rsa.unmarshalRsaPrivateKey(der)
|
||||
}
|
||||
|
22
src/keys/jwk2pem.js
Normal file
22
src/keys/jwk2pem.js
Normal 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
|
||||
}
|
@@ -1,7 +1,8 @@
|
||||
'use strict'
|
||||
|
||||
const whilst = require('async/whilst')
|
||||
const Buffer = require('safe-buffer').Buffer
|
||||
const errcode = require('err-code')
|
||||
const uint8ArrayConcat = require('uint8arrays/concat')
|
||||
const uint8ArrayFromString = require('uint8arrays/from-string')
|
||||
const hmac = require('../hmac')
|
||||
|
||||
const cipherMap = {
|
||||
@@ -21,89 +22,56 @@ const cipherMap = {
|
||||
|
||||
// Generates a set of keys for each party by stretching the shared key.
|
||||
// (myIV, theirIV, myCipherKey, theirCipherKey, myMACKey, theirMACKey)
|
||||
module.exports = (cipherType, hash, secret, callback) => {
|
||||
module.exports = async (cipherType, hash, secret) => {
|
||||
const cipher = cipherMap[cipherType]
|
||||
|
||||
if (!cipher) {
|
||||
return callback(new Error('unkown cipherType passed'))
|
||||
const allowed = Object.keys(cipherMap).join(' / ')
|
||||
throw errcode(new Error(`unknown cipher type '${cipherType}'. Must be ${allowed}`), 'ERR_INVALID_CIPHER_TYPE')
|
||||
}
|
||||
|
||||
if (!hash) {
|
||||
return callback(new Error('unkown hashType passed'))
|
||||
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 seed = uint8ArrayFromString('key expansion')
|
||||
const resultLength = 2 * (ivSize + cipherKeySize + hmacKeySize)
|
||||
|
||||
hmac.create(hash, secret, (err, m) => {
|
||||
if (err) {
|
||||
return callback(err)
|
||||
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(uint8ArrayConcat([a, seed]))
|
||||
let todo = b.length
|
||||
|
||||
if (j + todo > resultLength) {
|
||||
todo = resultLength - j
|
||||
}
|
||||
|
||||
m.digest(seed, (err, a) => {
|
||||
if (err) {
|
||||
return callback(err)
|
||||
}
|
||||
result.push(b)
|
||||
j += todo
|
||||
a = await m.digest(a)
|
||||
}
|
||||
|
||||
let result = []
|
||||
let j = 0
|
||||
const half = resultLength / 2
|
||||
const resultBuffer = uint8ArrayConcat(result)
|
||||
const r1 = resultBuffer.slice(0, half)
|
||||
const r2 = resultBuffer.slice(half, resultLength)
|
||||
|
||||
whilst(
|
||||
() => j < resultLength,
|
||||
stretch,
|
||||
finish
|
||||
)
|
||||
|
||||
function stretch (cb) {
|
||||
m.digest(Buffer.concat([a, seed]), (err, b) => {
|
||||
if (err) {
|
||||
return cb(err)
|
||||
}
|
||||
|
||||
let todo = b.length
|
||||
|
||||
if (j + todo > resultLength) {
|
||||
todo = resultLength - j
|
||||
}
|
||||
|
||||
result.push(b)
|
||||
|
||||
j += todo
|
||||
|
||||
m.digest(a, (err, _a) => {
|
||||
if (err) {
|
||||
return cb(err)
|
||||
}
|
||||
a = _a
|
||||
cb()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function finish (err) {
|
||||
if (err) {
|
||||
return callback(err)
|
||||
}
|
||||
|
||||
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)
|
||||
})
|
||||
|
||||
callback(null, {
|
||||
k1: createKey(r1),
|
||||
k2: createKey(r2)
|
||||
})
|
||||
}
|
||||
})
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@@ -1,7 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
module.exports = {
|
||||
rsa: require('./rsa-class'),
|
||||
ed25519: require('./ed25519-class'),
|
||||
secp256k1: require('libp2p-crypto-secp256k1')
|
||||
}
|
@@ -5,12 +5,10 @@ module.exports = `enum KeyType {
|
||||
Ed25519 = 1;
|
||||
Secp256k1 = 2;
|
||||
}
|
||||
|
||||
message PublicKey {
|
||||
required KeyType Type = 1;
|
||||
required bytes Data = 2;
|
||||
}
|
||||
|
||||
message PrivateKey {
|
||||
required KeyType Type = 1;
|
||||
required bytes Data = 2;
|
||||
|
@@ -1,120 +1,153 @@
|
||||
'use strict'
|
||||
|
||||
const nodeify = require('nodeify')
|
||||
const Buffer = require('safe-buffer').Buffer
|
||||
|
||||
const webcrypto = require('../webcrypto.js')()
|
||||
const webcrypto = require('../webcrypto')
|
||||
const randomBytes = require('../random-bytes')
|
||||
const uint8ArrayToString = require('uint8arrays/to-string')
|
||||
const uint8ArrayFromString = require('uint8arrays/from-string')
|
||||
|
||||
exports.utils = require('./rsa-utils')
|
||||
|
||||
exports.generateKey = function (bits, callback) {
|
||||
nodeify(webcrypto.subtle.generateKey(
|
||||
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'}
|
||||
hash: { name: 'SHA-256' }
|
||||
},
|
||||
true,
|
||||
['sign', 'verify']
|
||||
)
|
||||
.then(exportKey)
|
||||
.then((keys) => ({
|
||||
|
||||
const keys = await exportKey(pair)
|
||||
|
||||
return {
|
||||
privateKey: keys[0],
|
||||
publicKey: keys[1]
|
||||
})), callback)
|
||||
}
|
||||
}
|
||||
|
||||
// Takes a jwk key
|
||||
exports.unmarshalPrivateKey = function (key, callback) {
|
||||
const privateKey = webcrypto.subtle.importKey(
|
||||
exports.unmarshalPrivateKey = async function (key) {
|
||||
const privateKey = await webcrypto.get().subtle.importKey(
|
||||
'jwk',
|
||||
key,
|
||||
{
|
||||
name: 'RSASSA-PKCS1-v1_5',
|
||||
hash: {name: 'SHA-256'}
|
||||
hash: { name: 'SHA-256' }
|
||||
},
|
||||
true,
|
||||
['sign']
|
||||
)
|
||||
|
||||
nodeify(Promise.all([
|
||||
const pair = [
|
||||
privateKey,
|
||||
derivePublicFromPrivate(key)
|
||||
]).then((keys) => exportKey({
|
||||
await derivePublicFromPrivate(key)
|
||||
]
|
||||
|
||||
const keys = await exportKey({
|
||||
privateKey: pair[0],
|
||||
publicKey: pair[1]
|
||||
})
|
||||
|
||||
return {
|
||||
privateKey: keys[0],
|
||||
publicKey: keys[1]
|
||||
})).then((keys) => ({
|
||||
privateKey: keys[0],
|
||||
publicKey: keys[1]
|
||||
})), callback)
|
||||
}
|
||||
}
|
||||
|
||||
exports.getRandomValues = function (arr) {
|
||||
return Buffer.from(webcrypto.getRandomValues(arr))
|
||||
}
|
||||
exports.getRandomValues = randomBytes
|
||||
|
||||
exports.hashAndSign = function (key, msg, callback) {
|
||||
nodeify(webcrypto.subtle.importKey(
|
||||
exports.hashAndSign = async function (key, msg) {
|
||||
const privateKey = await webcrypto.get().subtle.importKey(
|
||||
'jwk',
|
||||
key,
|
||||
{
|
||||
name: 'RSASSA-PKCS1-v1_5',
|
||||
hash: {name: 'SHA-256'}
|
||||
hash: { name: 'SHA-256' }
|
||||
},
|
||||
false,
|
||||
['sign']
|
||||
).then((privateKey) => {
|
||||
return webcrypto.subtle.sign(
|
||||
{name: 'RSASSA-PKCS1-v1_5'},
|
||||
privateKey,
|
||||
Uint8Array.from(msg)
|
||||
)
|
||||
}).then((sig) => Buffer.from(sig)), callback)
|
||||
)
|
||||
|
||||
const sig = await webcrypto.get().subtle.sign(
|
||||
{ name: 'RSASSA-PKCS1-v1_5' },
|
||||
privateKey,
|
||||
Uint8Array.from(msg)
|
||||
)
|
||||
|
||||
return new Uint8Array(sig, sig.byteOffset, sig.byteLength)
|
||||
}
|
||||
|
||||
exports.hashAndVerify = function (key, sig, msg, callback) {
|
||||
nodeify(webcrypto.subtle.importKey(
|
||||
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'}
|
||||
hash: { name: 'SHA-256' }
|
||||
},
|
||||
false,
|
||||
['verify']
|
||||
).then((publicKey) => {
|
||||
return webcrypto.subtle.verify(
|
||||
{name: 'RSASSA-PKCS1-v1_5'},
|
||||
publicKey,
|
||||
sig,
|
||||
msg
|
||||
)
|
||||
}), callback)
|
||||
)
|
||||
|
||||
return webcrypto.get().subtle.verify(
|
||||
{ name: 'RSASSA-PKCS1-v1_5' },
|
||||
publicKey,
|
||||
sig,
|
||||
msg
|
||||
)
|
||||
}
|
||||
|
||||
function exportKey (pair) {
|
||||
return Promise.all([
|
||||
webcrypto.subtle.exportKey('jwk', pair.privateKey),
|
||||
webcrypto.subtle.exportKey('jwk', pair.publicKey)
|
||||
webcrypto.get().subtle.exportKey('jwk', pair.privateKey),
|
||||
webcrypto.get().subtle.exportKey('jwk', pair.publicKey)
|
||||
])
|
||||
}
|
||||
|
||||
function derivePublicFromPrivate (jwKey) {
|
||||
return webcrypto.subtle.importKey(
|
||||
return webcrypto.get().subtle.importKey(
|
||||
'jwk',
|
||||
{
|
||||
kty: jwKey.kty,
|
||||
n: jwKey.n,
|
||||
e: jwKey.e,
|
||||
alg: jwKey.alg,
|
||||
kid: jwKey.kid
|
||||
e: jwKey.e
|
||||
},
|
||||
{
|
||||
name: 'RSASSA-PKCS1-v1_5',
|
||||
hash: {name: 'SHA-256'}
|
||||
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 Uint8Array to nodeForge buffer: ByteBuffer is a "binary-string backed buffer", so let's make our Uint8Array a binary string
|
||||
- Convert resulting nodeForge buffer to Uint8Array: it returns a binary string, turn that into a Uint8Array
|
||||
|
||||
*/
|
||||
|
||||
const { jwk2pub, jwk2priv } = require('./jwk2pem')
|
||||
|
||||
function convertKey (key, pub, msg, handle) {
|
||||
const fkey = pub ? jwk2pub(key) : jwk2priv(key)
|
||||
const fmsg = uint8ArrayToString(Uint8Array.from(msg), 'ascii')
|
||||
const fomsg = handle(fmsg, fkey)
|
||||
return uint8ArrayFromString(fomsg, 'ascii')
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
|
@@ -1,19 +1,26 @@
|
||||
'use strict'
|
||||
|
||||
const multihashing = require('multihashing-async')
|
||||
const protobuf = require('protocol-buffers')
|
||||
const sha = require('multihashing-async/src/sha')
|
||||
const protobuf = require('protons')
|
||||
const errcode = require('err-code')
|
||||
const uint8ArrayEquals = require('uint8arrays/equals')
|
||||
const uint8ArrayToString = require('uint8arrays/to-string')
|
||||
|
||||
require('node-forge/lib/sha512')
|
||||
require('node-forge/lib/ed25519')
|
||||
const forge = require('node-forge/lib/forge')
|
||||
|
||||
const crypto = require('./rsa')
|
||||
const pbm = protobuf(require('./keys.proto'))
|
||||
const exporter = require('./exporter')
|
||||
|
||||
class RsaPublicKey {
|
||||
constructor (key) {
|
||||
this._key = key
|
||||
}
|
||||
|
||||
verify (data, sig, callback) {
|
||||
ensure(callback)
|
||||
crypto.hashAndVerify(this._key, sig, data, callback)
|
||||
async verify (data, sig) { // eslint-disable-line require-await
|
||||
return crypto.hashAndVerify(this._key, sig, data)
|
||||
}
|
||||
|
||||
marshal () {
|
||||
@@ -28,46 +35,44 @@ class RsaPublicKey {
|
||||
}
|
||||
|
||||
encrypt (bytes) {
|
||||
return this._key.encrypt(bytes, 'RSAES-PKCS1-V1_5')
|
||||
return crypto.encrypt(this._key, bytes)
|
||||
}
|
||||
|
||||
equals (key) {
|
||||
return this.bytes.equals(key.bytes)
|
||||
return uint8ArrayEquals(this.bytes, key.bytes)
|
||||
}
|
||||
|
||||
hash (callback) {
|
||||
ensure(callback)
|
||||
multihashing(this.bytes, 'sha2-256', callback)
|
||||
async hash () { // eslint-disable-line require-await
|
||||
return sha.multihashing(this.bytes, 'sha2-256')
|
||||
}
|
||||
}
|
||||
|
||||
class RsaPrivateKey {
|
||||
// key - Object of the jwk format
|
||||
// publicKey - Buffer of the spki format
|
||||
// publicKey - Uint8Array of the spki format
|
||||
constructor (key, publicKey) {
|
||||
this._key = key
|
||||
this._publicKey = publicKey
|
||||
}
|
||||
|
||||
genSecret () {
|
||||
return crypto.getRandomValues(new Uint8Array(16))
|
||||
return crypto.getRandomValues(16)
|
||||
}
|
||||
|
||||
sign (message, callback) {
|
||||
ensure(callback)
|
||||
crypto.hashAndSign(this._key, message, callback)
|
||||
async sign (message) { // eslint-disable-line require-await
|
||||
return crypto.hashAndSign(this._key, message)
|
||||
}
|
||||
|
||||
get public () {
|
||||
if (!this._publicKey) {
|
||||
throw new Error('public key not provided')
|
||||
throw errcode(new Error('public key not provided'), 'ERR_PUBKEY_NOT_PROVIDED')
|
||||
}
|
||||
|
||||
return new RsaPublicKey(this._publicKey)
|
||||
}
|
||||
|
||||
decrypt (msg, callback) {
|
||||
crypto.decrypt(this._key, msg, callback)
|
||||
decrypt (bytes) {
|
||||
return crypto.decrypt(this._key, bytes)
|
||||
}
|
||||
|
||||
marshal () {
|
||||
@@ -82,46 +87,73 @@ class RsaPrivateKey {
|
||||
}
|
||||
|
||||
equals (key) {
|
||||
return this.bytes.equals(key.bytes)
|
||||
return uint8ArrayEquals(this.bytes, key.bytes)
|
||||
}
|
||||
|
||||
hash (callback) {
|
||||
ensure(callback)
|
||||
multihashing(this.bytes, 'sha2-256', callback)
|
||||
async hash () { // eslint-disable-line require-await
|
||||
return sha.multihashing(this.bytes, 'sha2-256')
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the ID of the key.
|
||||
*
|
||||
* The key id is the base58 encoding of the SHA-256 multihash of its public key.
|
||||
* The public key is a protobuf encoding containing a type and the DER encoding
|
||||
* of the PKCS SubjectPublicKeyInfo.
|
||||
*
|
||||
* @returns {Promise<String>}
|
||||
*/
|
||||
async id () {
|
||||
const hash = await this.public.hash()
|
||||
return uint8ArrayToString(hash, 'base58btc')
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports the key into a password protected PEM format
|
||||
*
|
||||
* @param {string} password - The password to read the encrypted PEM
|
||||
* @param {string} [format=pkcs-8] - The format in which to export as
|
||||
*/
|
||||
async export (password, format = 'pkcs-8') { // eslint-disable-line require-await
|
||||
if (format === 'pkcs-8') {
|
||||
const buffer = new forge.util.ByteBuffer(this.marshal())
|
||||
const asn1 = forge.asn1.fromDer(buffer)
|
||||
const privateKey = forge.pki.privateKeyFromAsn1(asn1)
|
||||
|
||||
const options = {
|
||||
algorithm: 'aes256',
|
||||
count: 10000,
|
||||
saltSize: 128 / 8,
|
||||
prfAlgorithm: 'sha512'
|
||||
}
|
||||
return forge.pki.encryptRsaPrivateKey(privateKey, password, options)
|
||||
} else if (format === 'libp2p-key') {
|
||||
return exporter.export(this.bytes, password)
|
||||
} else {
|
||||
throw errcode(new Error(`export format '${format}' is not supported`), 'ERR_INVALID_EXPORT_FORMAT')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function unmarshalRsaPrivateKey (bytes, callback) {
|
||||
async function unmarshalRsaPrivateKey (bytes) {
|
||||
const jwk = crypto.utils.pkcs1ToJwk(bytes)
|
||||
crypto.unmarshalPrivateKey(jwk, (err, keys) => {
|
||||
if (err) {
|
||||
return callback(err)
|
||||
}
|
||||
|
||||
callback(null, new RsaPrivateKey(keys.privateKey, keys.publicKey))
|
||||
})
|
||||
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)
|
||||
}
|
||||
|
||||
function generateKeyPair (bits, cb) {
|
||||
crypto.generateKey(bits, (err, keys) => {
|
||||
if (err) {
|
||||
return cb(err)
|
||||
}
|
||||
|
||||
cb(null, new RsaPrivateKey(keys.privateKey, keys.publicKey))
|
||||
})
|
||||
async function fromJwk (jwk) {
|
||||
const keys = await crypto.unmarshalPrivateKey(jwk)
|
||||
return new RsaPrivateKey(keys.privateKey, keys.publicKey)
|
||||
}
|
||||
|
||||
function ensure (cb) {
|
||||
if (typeof cb !== 'function') {
|
||||
throw new Error('callback is required')
|
||||
}
|
||||
async function generateKeyPair (bits) {
|
||||
const keys = await crypto.generateKey(bits)
|
||||
return new RsaPrivateKey(keys.privateKey, keys.publicKey)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
@@ -129,5 +161,6 @@ module.exports = {
|
||||
RsaPrivateKey,
|
||||
unmarshalRsaPublicKey,
|
||||
unmarshalRsaPrivateKey,
|
||||
generateKeyPair
|
||||
generateKeyPair,
|
||||
fromJwk
|
||||
}
|
||||
|
@@ -1,68 +1,28 @@
|
||||
'use strict'
|
||||
|
||||
const asn1 = require('asn1.js')
|
||||
|
||||
const util = require('./../util')
|
||||
const toBase64 = util.toBase64
|
||||
const toBn = util.toBn
|
||||
|
||||
const RSAPrivateKey = asn1.define('RSAPrivateKey', function () {
|
||||
this.seq().obj(
|
||||
this.key('version').int(),
|
||||
this.key('modulus').int(),
|
||||
this.key('publicExponent').int(),
|
||||
this.key('privateExponent').int(),
|
||||
this.key('prime1').int(),
|
||||
this.key('prime2').int(),
|
||||
this.key('exponent1').int(),
|
||||
this.key('exponent2').int(),
|
||||
this.key('coefficient').int()
|
||||
)
|
||||
})
|
||||
|
||||
const AlgorithmIdentifier = asn1.define('AlgorithmIdentifier', function () {
|
||||
this.seq().obj(
|
||||
this.key('algorithm').objid({
|
||||
'1.2.840.113549.1.1.1': 'rsa'
|
||||
}),
|
||||
this.key('none').optional().null_(),
|
||||
this.key('curve').optional().objid(),
|
||||
this.key('params').optional().seq().obj(
|
||||
this.key('p').int(),
|
||||
this.key('q').int(),
|
||||
this.key('g').int()
|
||||
)
|
||||
)
|
||||
})
|
||||
|
||||
const PublicKey = asn1.define('RSAPublicKey', function () {
|
||||
this.seq().obj(
|
||||
this.key('algorithm').use(AlgorithmIdentifier),
|
||||
this.key('subjectPublicKey').bitstr()
|
||||
)
|
||||
})
|
||||
|
||||
const RSAPublicKey = asn1.define('RSAPublicKey', function () {
|
||||
this.seq().obj(
|
||||
this.key('modulus').int(),
|
||||
this.key('publicExponent').int()
|
||||
)
|
||||
})
|
||||
require('node-forge/lib/asn1')
|
||||
require('node-forge/lib/rsa')
|
||||
const forge = require('node-forge/lib/forge')
|
||||
const { bigIntegerToUintBase64url, base64urlToBigInteger } = require('./../util')
|
||||
const uint8ArrayFromString = require('uint8arrays/from-string')
|
||||
const uint8ArrayToString = require('uint8arrays/to-string')
|
||||
|
||||
// Convert a PKCS#1 in ASN1 DER format to a JWK key
|
||||
exports.pkcs1ToJwk = function (bytes) {
|
||||
const asn1 = RSAPrivateKey.decode(bytes, 'der')
|
||||
const asn1 = forge.asn1.fromDer(uint8ArrayToString(bytes, 'ascii'))
|
||||
const privateKey = forge.pki.privateKeyFromAsn1(asn1)
|
||||
|
||||
// https://tools.ietf.org/html/rfc7518#section-6.3.1
|
||||
return {
|
||||
kty: 'RSA',
|
||||
n: toBase64(asn1.modulus),
|
||||
e: toBase64(asn1.publicExponent),
|
||||
d: toBase64(asn1.privateExponent),
|
||||
p: toBase64(asn1.prime1),
|
||||
q: toBase64(asn1.prime2),
|
||||
dp: toBase64(asn1.exponent1),
|
||||
dq: toBase64(asn1.exponent2),
|
||||
qi: toBase64(asn1.coefficient),
|
||||
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'
|
||||
}
|
||||
@@ -70,28 +30,29 @@ exports.pkcs1ToJwk = function (bytes) {
|
||||
|
||||
// Convert a JWK key into PKCS#1 in ASN1 DER format
|
||||
exports.jwkToPkcs1 = function (jwk) {
|
||||
return RSAPrivateKey.encode({
|
||||
version: 0,
|
||||
modulus: toBn(jwk.n),
|
||||
publicExponent: toBn(jwk.e),
|
||||
privateExponent: toBn(jwk.d),
|
||||
prime1: toBn(jwk.p),
|
||||
prime2: toBn(jwk.q),
|
||||
exponent1: toBn(jwk.dp),
|
||||
exponent2: toBn(jwk.dq),
|
||||
coefficient: toBn(jwk.qi)
|
||||
}, 'der')
|
||||
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 uint8ArrayFromString(forge.asn1.toDer(asn1).getBytes(), 'ascii')
|
||||
}
|
||||
|
||||
// Convert a PKCIX in ASN1 DER format to a JWK key
|
||||
exports.pkixToJwk = function (bytes) {
|
||||
const ndata = PublicKey.decode(bytes, 'der')
|
||||
const asn1 = RSAPublicKey.decode(ndata.subjectPublicKey.data, 'der')
|
||||
const asn1 = forge.asn1.fromDer(uint8ArrayToString(bytes, 'ascii'))
|
||||
const publicKey = forge.pki.publicKeyFromAsn1(asn1)
|
||||
|
||||
return {
|
||||
kty: 'RSA',
|
||||
n: toBase64(asn1.modulus),
|
||||
e: toBase64(asn1.publicExponent),
|
||||
n: bigIntegerToUintBase64url(publicKey.n),
|
||||
e: bigIntegerToUintBase64url(publicKey.e),
|
||||
alg: 'RS256',
|
||||
kid: '2011-04-29'
|
||||
}
|
||||
@@ -99,16 +60,10 @@ exports.pkixToJwk = function (bytes) {
|
||||
|
||||
// Convert a JWK key to PKCIX in ASN1 DER format
|
||||
exports.jwkToPkix = function (jwk) {
|
||||
return PublicKey.encode({
|
||||
algorithm: {
|
||||
algorithm: 'rsa',
|
||||
none: null
|
||||
},
|
||||
subjectPublicKey: {
|
||||
data: RSAPublicKey.encode({
|
||||
modulus: toBn(jwk.n),
|
||||
publicExponent: toBn(jwk.e)
|
||||
}, 'der')
|
||||
}
|
||||
}, 'der')
|
||||
const asn1 = forge.pki.publicKeyToAsn1({
|
||||
n: base64urlToBigInteger(jwk.n),
|
||||
e: base64urlToBigInteger(jwk.e)
|
||||
})
|
||||
|
||||
return uint8ArrayFromString(forge.asn1.toDer(asn1).getBytes(), 'ascii')
|
||||
}
|
||||
|
@@ -1,56 +1,83 @@
|
||||
'use strict'
|
||||
|
||||
const crypto = require('crypto')
|
||||
const keypair = require('keypair')
|
||||
const setImmediate = require('async/setImmediate')
|
||||
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')
|
||||
}
|
||||
|
||||
const ursa = require('ursa-optional') // throws if not compiled
|
||||
keypair = ({ bits }) => {
|
||||
const key = ursa.generatePrivateKey(bits)
|
||||
return {
|
||||
private: key.toPrivatePem(),
|
||||
public: key.toPublicPem()
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
if (process.env.LP2P_FORCE_CRYPTO_LIB === 'ursa') {
|
||||
throw e
|
||||
}
|
||||
|
||||
keypair = require('keypair')
|
||||
}
|
||||
const pemToJwk = require('pem-jwk').pem2jwk
|
||||
const jwkToPem = require('pem-jwk').jwk2pem
|
||||
|
||||
exports.utils = require('./rsa-utils')
|
||||
|
||||
exports.generateKey = function (bits, callback) {
|
||||
const done = (err, res) => setImmediate(() => callback(err, res))
|
||||
|
||||
let key
|
||||
try {
|
||||
key = keypair({ bits: bits })
|
||||
} catch (err) {
|
||||
return done(err)
|
||||
}
|
||||
|
||||
done(null, {
|
||||
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 = function (key, callback) {
|
||||
callback(null, {
|
||||
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
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
exports.getRandomValues = function (arr) {
|
||||
return crypto.randomBytes(arr.length)
|
||||
}
|
||||
exports.getRandomValues = randomBytes
|
||||
|
||||
exports.hashAndSign = function (key, msg, callback) {
|
||||
exports.hashAndSign = async function (key, msg) { // eslint-disable-line require-await
|
||||
const sign = crypto.createSign('RSA-SHA256')
|
||||
|
||||
sign.update(msg)
|
||||
setImmediate(() => callback(null, sign.sign(jwkToPem(key))))
|
||||
const pem = jwkToPem(key)
|
||||
return sign.sign(pem)
|
||||
}
|
||||
|
||||
exports.hashAndVerify = function (key, sig, msg, callback) {
|
||||
exports.hashAndVerify = async function (key, sig, msg) { // eslint-disable-line require-await
|
||||
const verify = crypto.createVerify('RSA-SHA256')
|
||||
|
||||
verify.update(msg)
|
||||
|
||||
setImmediate(() => callback(null, verify.verify(jwkToPem(key), sig)))
|
||||
const pem = jwkToPem(key)
|
||||
return verify.verify(pem, sig)
|
||||
}
|
||||
|
||||
const padding = crypto.constants.RSA_PKCS1_PADDING
|
||||
|
||||
exports.encrypt = function (key, bytes) {
|
||||
return crypto.publicEncrypt({ key: jwkToPem(key), padding }, bytes)
|
||||
}
|
||||
|
||||
exports.decrypt = function (key, bytes) {
|
||||
return crypto.privateDecrypt({ key: jwkToPem(key), padding }, bytes)
|
||||
}
|
||||
|
128
src/keys/secp256k1-class.js
Normal file
128
src/keys/secp256k1-class.js
Normal file
@@ -0,0 +1,128 @@
|
||||
'use strict'
|
||||
|
||||
const sha = require('multihashing-async/src/sha')
|
||||
const errcode = require('err-code')
|
||||
const uint8ArrayEquals = require('uint8arrays/equals')
|
||||
const uint8ArrayToString = require('uint8arrays/to-string')
|
||||
|
||||
const exporter = require('./exporter')
|
||||
|
||||
module.exports = (keysProtobuf, randomBytes, crypto) => {
|
||||
crypto = crypto || require('./secp256k1')(randomBytes)
|
||||
|
||||
class Secp256k1PublicKey {
|
||||
constructor (key) {
|
||||
crypto.validatePublicKey(key)
|
||||
this._key = key
|
||||
}
|
||||
|
||||
verify (data, sig) {
|
||||
return crypto.hashAndVerify(this._key, sig, data)
|
||||
}
|
||||
|
||||
marshal () {
|
||||
return crypto.compressPublicKey(this._key)
|
||||
}
|
||||
|
||||
get bytes () {
|
||||
return keysProtobuf.PublicKey.encode({
|
||||
Type: keysProtobuf.KeyType.Secp256k1,
|
||||
Data: this.marshal()
|
||||
})
|
||||
}
|
||||
|
||||
equals (key) {
|
||||
return uint8ArrayEquals(this.bytes, key.bytes)
|
||||
}
|
||||
|
||||
hash () {
|
||||
return sha.multihashing(this.bytes, 'sha2-256')
|
||||
}
|
||||
}
|
||||
|
||||
class Secp256k1PrivateKey {
|
||||
constructor (key, publicKey) {
|
||||
this._key = key
|
||||
this._publicKey = publicKey || crypto.computePublicKey(key)
|
||||
crypto.validatePrivateKey(this._key)
|
||||
crypto.validatePublicKey(this._publicKey)
|
||||
}
|
||||
|
||||
sign (message) {
|
||||
return crypto.hashAndSign(this._key, message)
|
||||
}
|
||||
|
||||
get public () {
|
||||
return new Secp256k1PublicKey(this._publicKey)
|
||||
}
|
||||
|
||||
marshal () {
|
||||
return this._key
|
||||
}
|
||||
|
||||
get bytes () {
|
||||
return keysProtobuf.PrivateKey.encode({
|
||||
Type: keysProtobuf.KeyType.Secp256k1,
|
||||
Data: this.marshal()
|
||||
})
|
||||
}
|
||||
|
||||
equals (key) {
|
||||
return uint8ArrayEquals(this.bytes, key.bytes)
|
||||
}
|
||||
|
||||
hash () {
|
||||
return sha.multihashing(this.bytes, 'sha2-256')
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the ID of the key.
|
||||
*
|
||||
* The key id is the base58 encoding of the SHA-256 multihash of its public key.
|
||||
* The public key is a protobuf encoding containing a type and the DER encoding
|
||||
* of the PKCS SubjectPublicKeyInfo.
|
||||
*
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
async id () {
|
||||
const hash = await this.public.hash()
|
||||
return uint8ArrayToString(hash, 'base58btc')
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports the key into a password protected `format`
|
||||
*
|
||||
* @param {string} password - The password to encrypt the key
|
||||
* @param {string} [format=libp2p-key] - The format in which to export as
|
||||
* @returns {Promise<string>} The encrypted private key
|
||||
*/
|
||||
async export (password, format = 'libp2p-key') { // eslint-disable-line require-await
|
||||
if (format === 'libp2p-key') {
|
||||
return exporter.export(this.bytes, password)
|
||||
} else {
|
||||
throw errcode(new Error(`export format '${format}' is not supported`), 'ERR_INVALID_EXPORT_FORMAT')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function unmarshalSecp256k1PrivateKey (bytes) {
|
||||
return new Secp256k1PrivateKey(bytes)
|
||||
}
|
||||
|
||||
function unmarshalSecp256k1PublicKey (bytes) {
|
||||
return new Secp256k1PublicKey(bytes)
|
||||
}
|
||||
|
||||
async function generateKeyPair () {
|
||||
const privateKeyBytes = await crypto.generateKey()
|
||||
return new Secp256k1PrivateKey(privateKeyBytes)
|
||||
}
|
||||
|
||||
return {
|
||||
Secp256k1PublicKey,
|
||||
Secp256k1PrivateKey,
|
||||
unmarshalSecp256k1PrivateKey,
|
||||
unmarshalSecp256k1PublicKey,
|
||||
generateKeyPair
|
||||
}
|
||||
}
|
69
src/keys/secp256k1.js
Normal file
69
src/keys/secp256k1.js
Normal file
@@ -0,0 +1,69 @@
|
||||
'use strict'
|
||||
|
||||
const secp256k1 = require('secp256k1')
|
||||
const sha = require('multihashing-async/src/sha')
|
||||
const HASH_ALGORITHM = 'sha2-256'
|
||||
|
||||
module.exports = (randomBytes) => {
|
||||
const privateKeyLength = 32
|
||||
|
||||
function generateKey () {
|
||||
let privateKey
|
||||
do {
|
||||
privateKey = randomBytes(32)
|
||||
} while (!secp256k1.privateKeyVerify(privateKey))
|
||||
return privateKey
|
||||
}
|
||||
|
||||
async function hashAndSign (key, msg) {
|
||||
const digest = await sha.digest(msg, HASH_ALGORITHM)
|
||||
const sig = secp256k1.ecdsaSign(digest, key)
|
||||
return secp256k1.signatureExport(sig.signature)
|
||||
}
|
||||
|
||||
async function hashAndVerify (key, sig, msg) {
|
||||
const digest = await sha.digest(msg, HASH_ALGORITHM)
|
||||
sig = secp256k1.signatureImport(sig)
|
||||
return secp256k1.ecdsaVerify(sig, digest, key)
|
||||
}
|
||||
|
||||
function compressPublicKey (key) {
|
||||
if (!secp256k1.publicKeyVerify(key)) {
|
||||
throw new Error('Invalid public key')
|
||||
}
|
||||
return secp256k1.publicKeyConvert(key, true)
|
||||
}
|
||||
|
||||
function decompressPublicKey (key) {
|
||||
return secp256k1.publicKeyConvert(key, false)
|
||||
}
|
||||
|
||||
function validatePrivateKey (key) {
|
||||
if (!secp256k1.privateKeyVerify(key)) {
|
||||
throw new Error('Invalid private key')
|
||||
}
|
||||
}
|
||||
|
||||
function validatePublicKey (key) {
|
||||
if (!secp256k1.publicKeyVerify(key)) {
|
||||
throw new Error('Invalid public key')
|
||||
}
|
||||
}
|
||||
|
||||
function computePublicKey (privateKey) {
|
||||
validatePrivateKey(privateKey)
|
||||
return secp256k1.publicKeyCreate(privateKey)
|
||||
}
|
||||
|
||||
return {
|
||||
generateKey,
|
||||
privateKeyLength,
|
||||
hashAndSign,
|
||||
hashAndVerify,
|
||||
compressPublicKey,
|
||||
decompressPublicKey,
|
||||
validatePrivateKey,
|
||||
validatePublicKey,
|
||||
computePublicKey
|
||||
}
|
||||
}
|
10
src/keys/validate-curve-type.js
Normal file
10
src/keys/validate-curve-type.js
Normal 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
45
src/pbkdf2.js
Normal 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
10
src/random-bytes.js
Normal 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)
|
||||
}
|
50
src/util.js
50
src/util.js
@@ -1,21 +1,43 @@
|
||||
'use strict'
|
||||
|
||||
const BN = require('asn1.js').bignum
|
||||
const Buffer = require('safe-buffer').Buffer
|
||||
require('node-forge/lib/util')
|
||||
require('node-forge/lib/jsbn')
|
||||
const forge = require('node-forge/lib/forge')
|
||||
const uint8ArrayFromString = require('uint8arrays/from-string')
|
||||
const uint8ArrayToString = require('uint8arrays/to-string')
|
||||
const uint8ArrayConcat = require('uint8arrays/concat')
|
||||
|
||||
// Convert a BN.js instance to a base64 encoded string without padding
|
||||
// Adapted from https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-41#appendix-C
|
||||
exports.toBase64 = function toBase64 (bn, len) {
|
||||
// if len is defined then the bytes are leading-0 padded to the length
|
||||
let s = bn.toArrayLike(Buffer, 'be', len).toString('base64')
|
||||
exports.bigIntegerToUintBase64url = (num, len) => {
|
||||
// Call `.abs()` to convert to unsigned
|
||||
let buf = Uint8Array.from(num.abs().toByteArray()) // toByteArray converts to big endian
|
||||
|
||||
return s
|
||||
.replace(/(=*)$/, '') // Remove any trailing '='s
|
||||
.replace(/\+/g, '-') // 62nd char of encoding
|
||||
.replace(/\//g, '_') // 63rd char of encoding
|
||||
// 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 = uint8ArrayConcat([new Uint8Array(len - buf.length), buf])
|
||||
}
|
||||
|
||||
return uint8ArrayToString(buf, 'base64url')
|
||||
}
|
||||
|
||||
// Convert a base64 encoded string to a BN.js instance
|
||||
exports.toBn = function toBn (str) {
|
||||
return new BN(Buffer.from(str, 'base64'))
|
||||
// Convert a base64url encoded string to a BigInteger
|
||||
exports.base64urlToBigInteger = str => {
|
||||
const buf = exports.base64urlToBuffer(str)
|
||||
return new forge.jsbn.BigInteger(uint8ArrayToString(buf, 'base16'), 16)
|
||||
}
|
||||
|
||||
exports.base64urlToBuffer = (str, len) => {
|
||||
let buf = uint8ArrayFromString(str, 'base64urlpad')
|
||||
|
||||
if (len != null) {
|
||||
if (buf.length > len) throw new Error('byte array longer than desired length')
|
||||
buf = uint8ArrayConcat([new Uint8Array(len - buf.length), buf])
|
||||
}
|
||||
|
||||
return buf
|
||||
}
|
||||
|
@@ -1,16 +1,24 @@
|
||||
/* global self */
|
||||
/* eslint-env browser */
|
||||
|
||||
'use strict'
|
||||
|
||||
module.exports = () => {
|
||||
// This is only a shim for interfaces, not for functionality
|
||||
if (typeof self !== 'undefined') {
|
||||
require('webcrypto-shim')(self)
|
||||
// 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 (self.crypto) {
|
||||
return self.crypto
|
||||
}
|
||||
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' }
|
||||
)
|
||||
}
|
||||
|
||||
throw new Error('Please use an environment with crypto support')
|
||||
return nativeCrypto
|
||||
}
|
||||
|
@@ -1,4 +1,5 @@
|
||||
/* eslint max-nested-callbacks: ["error", 8] */
|
||||
/* eslint-disable valid-jsdoc */
|
||||
/* eslint-env mocha */
|
||||
'use strict'
|
||||
|
||||
@@ -6,8 +7,7 @@ const chai = require('chai')
|
||||
const dirtyChai = require('dirty-chai')
|
||||
const expect = chai.expect
|
||||
chai.use(dirtyChai)
|
||||
const series = require('async/series')
|
||||
const Buffer = require('safe-buffer').Buffer
|
||||
const { expectErrCode } = require('../util')
|
||||
|
||||
const crypto = require('../../src')
|
||||
const fixtures = require('./../fixtures/aes')
|
||||
@@ -18,55 +18,47 @@ const bytes = {
|
||||
32: 'AES-256'
|
||||
}
|
||||
|
||||
/** @typedef {import("libp2p-crypto").aes.Cipher} Cipher */
|
||||
|
||||
describe('AES-CTR', () => {
|
||||
Object.keys(bytes).forEach((byte) => {
|
||||
it(`${bytes[byte]} - encrypt and decrypt`, (done) => {
|
||||
const key = Buffer.alloc(parseInt(byte, 10))
|
||||
it(`${bytes[byte]} - encrypt and decrypt`, async () => {
|
||||
const key = new Uint8Array(parseInt(byte, 10))
|
||||
key.fill(5)
|
||||
|
||||
const iv = Buffer.alloc(16)
|
||||
const iv = new Uint8Array(16)
|
||||
iv.fill(1)
|
||||
|
||||
crypto.aes.create(key, iv, (err, cipher) => {
|
||||
expect(err).to.not.exist()
|
||||
const cipher = await crypto.aes.create(key, iv)
|
||||
|
||||
series([
|
||||
encryptAndDecrypt(cipher),
|
||||
encryptAndDecrypt(cipher),
|
||||
encryptAndDecrypt(cipher),
|
||||
encryptAndDecrypt(cipher),
|
||||
encryptAndDecrypt(cipher)
|
||||
], done)
|
||||
})
|
||||
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`, (done) => {
|
||||
const key = Buffer.alloc(parseInt(byte, 10))
|
||||
it(`${bytes[byte]} - fixed - encrypt and decrypt`, async () => {
|
||||
const key = new Uint8Array(parseInt(byte, 10))
|
||||
key.fill(5)
|
||||
|
||||
const iv = Buffer.alloc(16)
|
||||
const iv = new Uint8Array(16)
|
||||
iv.fill(1)
|
||||
|
||||
crypto.aes.create(key, iv, (err, cipher) => {
|
||||
expect(err).to.not.exist()
|
||||
const cipher = await crypto.aes.create(key, iv)
|
||||
|
||||
series(fixtures[byte].inputs.map((rawIn, i) => (cb) => {
|
||||
const input = Buffer.from(rawIn)
|
||||
const output = Buffer.from(fixtures[byte].outputs[i])
|
||||
cipher.encrypt(input, (err, res) => {
|
||||
expect(err).to.not.exist()
|
||||
expect(res).to.have.length(output.length)
|
||||
expect(res).to.eql(output)
|
||||
cipher.decrypt(res, (err, res) => {
|
||||
expect(err).to.not.exist()
|
||||
expect(res).to.eql(input)
|
||||
cb()
|
||||
})
|
||||
})
|
||||
}), done)
|
||||
})
|
||||
for (let i = 0; i < fixtures[byte].inputs.length; i++) {
|
||||
const rawIn = fixtures[byte].inputs[i]
|
||||
const input = Uint8Array.from(rawIn.data)
|
||||
const output = Uint8Array.from(fixtures[byte].outputs[i].data)
|
||||
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)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -75,46 +67,45 @@ describe('AES-CTR', () => {
|
||||
return
|
||||
}
|
||||
|
||||
it(`${bytes[byte]} - go interop - encrypt and decrypt`, (done) => {
|
||||
const key = Buffer.alloc(parseInt(byte, 10))
|
||||
it(`${bytes[byte]} - go interop - encrypt and decrypt`, async () => {
|
||||
const key = new Uint8Array(parseInt(byte, 10))
|
||||
key.fill(5)
|
||||
|
||||
const iv = Buffer.alloc(16)
|
||||
const iv = new Uint8Array(16)
|
||||
iv.fill(1)
|
||||
|
||||
crypto.aes.create(key, iv, (err, cipher) => {
|
||||
expect(err).to.not.exist()
|
||||
const cipher = await crypto.aes.create(key, iv)
|
||||
|
||||
series(goFixtures[byte].inputs.map((rawIn, i) => (cb) => {
|
||||
const input = Buffer.from(rawIn)
|
||||
const output = Buffer.from(goFixtures[byte].outputs[i])
|
||||
cipher.encrypt(input, (err, res) => {
|
||||
expect(err).to.not.exist()
|
||||
expect(res).to.have.length(output.length)
|
||||
expect(res).to.be.eql(output)
|
||||
cipher.decrypt(res, (err, res) => {
|
||||
expect(err).to.not.exist()
|
||||
expect(res).to.be.eql(input)
|
||||
cb()
|
||||
})
|
||||
})
|
||||
}), done)
|
||||
})
|
||||
for (let i = 0; i < goFixtures[byte].inputs.length; i++) {
|
||||
const rawIn = goFixtures[byte].inputs[i]
|
||||
const input = Uint8Array.from(rawIn)
|
||||
const output = Uint8Array.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 = new Uint8Array(5)
|
||||
const iv = new Uint8Array(16)
|
||||
return expectErrCode(crypto.aes.create(key, iv), 'ERR_INVALID_KEY_LENGTH')
|
||||
})
|
||||
})
|
||||
|
||||
function encryptAndDecrypt (cipher) {
|
||||
const data = Buffer.alloc(100)
|
||||
// @ts-check
|
||||
/**
|
||||
* @type {function(Cipher): Promise<void>}
|
||||
*/
|
||||
async function encryptAndDecrypt (cipher) {
|
||||
const data = new Uint8Array(100)
|
||||
data.fill(Math.ceil(Math.random() * 100))
|
||||
return (cb) => {
|
||||
cipher.encrypt(data, (err, res) => {
|
||||
expect(err).to.not.exist()
|
||||
cipher.decrypt(res, (err, res) => {
|
||||
expect(err).to.not.exist()
|
||||
expect(res).to.be.eql(data)
|
||||
cb()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const encrypted = await cipher.encrypt(data)
|
||||
const decrypted = await cipher.decrypt(encrypted)
|
||||
|
||||
expect(decrypted).to.be.eql(data)
|
||||
}
|
||||
|
62
test/browser.js
Normal file
62
test/browser.js
Normal file
@@ -0,0 +1,62 @@
|
||||
/* eslint-env mocha */
|
||||
'use strict'
|
||||
|
||||
const chai = require('chai')
|
||||
chai.use(require('dirty-chai'))
|
||||
const expect = chai.expect
|
||||
const uint8ArrayFromString = require('uint8arrays/from-string')
|
||||
|
||||
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', uint8ArrayFromString('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(uint8ArrayFromString('test')))
|
||||
})
|
||||
|
||||
it('should error for verify RSA public key when web crypto is missing', () => {
|
||||
return expectMissingWebCrypto(() => rsaPrivateKey.public.verify(uint8ArrayFromString('test'), uint8ArrayFromString('test')))
|
||||
})
|
||||
})
|
@@ -8,17 +8,20 @@ const expect = chai.expect
|
||||
chai.use(dirtyChai)
|
||||
const crypto = require('../src')
|
||||
const fixtures = require('./fixtures/go-key-rsa')
|
||||
const { expectErrCode } = require('./util')
|
||||
const uint8ArrayEquals = require('uint8arrays/equals')
|
||||
|
||||
describe('libp2p-crypto', () => {
|
||||
/** @typedef {import("libp2p-crypto").PrivateKey} PrivateKey */
|
||||
|
||||
describe('libp2p-crypto', function () {
|
||||
this.timeout(20 * 1000)
|
||||
// @ts-check
|
||||
/**
|
||||
* @type {PrivateKey}
|
||||
*/
|
||||
let key
|
||||
before((done) => {
|
||||
crypto.keys.generateKeyPair('RSA', 2048, (err, _key) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
||||
key = _key
|
||||
done()
|
||||
})
|
||||
before(async () => {
|
||||
key = await crypto.keys.generateKeyPair('RSA', 512)
|
||||
})
|
||||
|
||||
it('marshalPublicKey and unmarshalPublicKey', () => {
|
||||
@@ -32,84 +35,90 @@ describe('libp2p-crypto', () => {
|
||||
}).to.throw()
|
||||
})
|
||||
|
||||
it('marshalPrivateKey and unmarshalPrivateKey', (done) => {
|
||||
it('marshalPrivateKey and unmarshalPrivateKey', async () => {
|
||||
expect(() => {
|
||||
crypto.keys.marshalPrivateKey(key, 'invalid-key-type')
|
||||
}).to.throw()
|
||||
|
||||
crypto.keys.unmarshalPrivateKey(crypto.keys.marshalPrivateKey(key), (err, key2) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
||||
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)
|
||||
done()
|
||||
})
|
||||
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', (done) => {
|
||||
crypto.keys.unmarshalPrivateKey(fixtures.private.key, (err, key) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
||||
const hash = fixtures.private.hash
|
||||
expect(fixtures.private.key).to.eql(key.bytes)
|
||||
|
||||
key.hash((err, digest) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
||||
|
||||
expect(digest).to.eql(hash)
|
||||
done()
|
||||
})
|
||||
})
|
||||
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', (done) => {
|
||||
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)
|
||||
|
||||
key.hash((err, digest) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
||||
|
||||
expect(digest).to.eql(hash)
|
||||
done()
|
||||
})
|
||||
const digest = await key.hash()
|
||||
expect(digest).to.eql(hash)
|
||||
})
|
||||
|
||||
it('unmarshal -> marshal, private key', (done) => {
|
||||
crypto.keys.unmarshalPrivateKey(fixtures.private.key, (err, key) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
||||
|
||||
const marshalled = crypto.keys.marshalPrivateKey(key)
|
||||
expect(marshalled).to.eql(fixtures.private.key)
|
||||
done()
|
||||
})
|
||||
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)
|
||||
expect(uint8ArrayEquals(fixtures.public.key, 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 no number passed', () => {
|
||||
it('throws with invalid number passed', () => {
|
||||
expect(() => {
|
||||
crypto.randomBytes()
|
||||
crypto.randomBytes(-1)
|
||||
}).to.throw()
|
||||
})
|
||||
|
||||
|
6
test/fixtures/go-elliptic-key.js
vendored
6
test/fixtures/go-elliptic-key.js
vendored
@@ -1,14 +1,12 @@
|
||||
'use strict'
|
||||
|
||||
const Buffer = require('safe-buffer').Buffer
|
||||
|
||||
module.exports = {
|
||||
curve: 'P-256',
|
||||
bob: {
|
||||
private: Buffer.from([
|
||||
private: Uint8Array.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([
|
||||
public: Uint8Array.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
|
||||
])
|
||||
}
|
||||
|
54
test/fixtures/go-key-ed25519.js
vendored
54
test/fixtures/go-key-ed25519.js
vendored
@@ -1,30 +1,42 @@
|
||||
'use strict'
|
||||
|
||||
const Buffer = require('safe-buffer').Buffer
|
||||
|
||||
module.exports = {
|
||||
// These were generated in a gore (https://github.com/motemen/gore) repl session:
|
||||
// Generation code from https://github.com/libp2p/js-libp2p-crypto/issues/175#issuecomment-634467463
|
||||
//
|
||||
// :import github.com/libp2p/go-libp2p-crypto
|
||||
// :import crypto/rand
|
||||
// priv, pub, err := crypto.GenerateEd25519Key(rand.Reader)
|
||||
// pubkeyBytes, err := pub.Bytes()
|
||||
// privkeyBytes, err := priv.Bytes()
|
||||
// data := []byte("hello! and welcome to some awesome crypto primitives")
|
||||
// sig, err := priv.Sign(data)
|
||||
// package main
|
||||
//
|
||||
// :import io/ioutil
|
||||
// ioutil.WriteFile("/tmp/pubkey_go.bin", pubkeyBytes, 0644)
|
||||
// // etc..
|
||||
// import (
|
||||
// "crypto/rand"
|
||||
// "fmt"
|
||||
// "strings"
|
||||
|
||||
// "github.com/libp2p/go-libp2p-core/crypto"
|
||||
// )
|
||||
|
||||
// func main() {
|
||||
// priv, pub, _ := crypto.GenerateEd25519Key(rand.Reader)
|
||||
// pubkeyBytes, _ := pub.Bytes()
|
||||
// privkeyBytes, _ := priv.Bytes()
|
||||
// data := []byte("hello! and welcome to some awesome crypto primitives")
|
||||
// sig, _ := priv.Sign(data)
|
||||
// fmt.Println("{\n publicKey: Uint8Array.from(", strings.Replace(fmt.Sprint(pubkeyBytes), " ", ",", -1), "),")
|
||||
// fmt.Println(" privateKey: Uint8Array.from(", strings.Replace(fmt.Sprint(privkeyBytes), " ", ",", -1), "),")
|
||||
// fmt.Println(" data: Uint8Array.from(", strings.Replace(fmt.Sprint(data), " ", ",", -1), "),")
|
||||
// fmt.Println(" signature: Uint8Array.from(", strings.Replace(fmt.Sprint(sig), " ", ",", -1), ")\n}")
|
||||
// }
|
||||
//
|
||||
// Then loaded into a node repl and dumped to arrays with:
|
||||
//
|
||||
// var pubkey = Array.from(fs.readFileSync('/tmp/pubkey_go.bin'))
|
||||
// console.log(JSON.stringify(pubkey))
|
||||
|
||||
// The legacy key unnecessarily appends the publickey. (It's already included) See https://github.com/libp2p/js-libp2p-crypto/issues/175
|
||||
redundantPubKey: {
|
||||
privateKey: Uint8Array.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: Uint8Array.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: Uint8Array.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: Uint8Array.from([7, 230, 175, 164, 228, 58, 78, 208, 62, 243, 73, 142, 83, 195, 176, 217, 166, 62, 41, 165, 168, 164, 75, 179, 163, 86, 102, 32, 18, 84, 150, 237, 39, 207, 213, 20, 134, 237, 50, 41, 176, 183, 229, 133, 38, 255, 42, 228, 68, 186, 100, 14, 175, 156, 243, 118, 125, 125, 120, 212, 124, 103, 252, 12])
|
||||
},
|
||||
verify: {
|
||||
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])
|
||||
publicKey: Uint8Array.from([8, 1, 18, 32, 163, 176, 195, 47, 254, 208, 49, 5, 192, 102, 32, 63, 58, 202, 171, 153, 146, 164, 25, 212, 25, 91, 146, 26, 117, 165, 148, 6, 207, 90, 217, 126]),
|
||||
privateKey: Uint8Array.from([8, 1, 18, 64, 232, 56, 175, 20, 240, 160, 19, 47, 92, 88, 115, 221, 164, 13, 36, 162, 158, 136, 247, 31, 29, 231, 76, 143, 12, 91, 193, 4, 88, 33, 67, 23, 163, 176, 195, 47, 254, 208, 49, 5, 192, 102, 32, 63, 58, 202, 171, 153, 146, 164, 25, 212, 25, 91, 146, 26, 117, 165, 148, 6, 207, 90, 217, 126]),
|
||||
data: Uint8Array.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: Uint8Array.from([160, 125, 30, 62, 213, 189, 239, 92, 87, 76, 205, 169, 251, 149, 187, 57, 96, 85, 175, 213, 22, 132, 229, 60, 196, 18, 117, 194, 12, 174, 135, 31, 39, 168, 174, 103, 78, 55, 37, 222, 37, 172, 222, 239, 153, 63, 197, 152, 67, 167, 191, 215, 161, 212, 216, 163, 81, 77, 45, 228, 151, 79, 101, 1])
|
||||
}
|
||||
}
|
||||
|
16
test/fixtures/go-key-rsa.js
vendored
16
test/fixtures/go-key-rsa.js
vendored
@@ -1,32 +1,30 @@
|
||||
'use strict'
|
||||
|
||||
const Buffer = require('safe-buffer').Buffer
|
||||
|
||||
module.exports = {
|
||||
private: {
|
||||
hash: Buffer.from([
|
||||
hash: Uint8Array.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: Buffer.from([
|
||||
key: Uint8Array.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: Buffer.from([
|
||||
hash: Uint8Array.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: Buffer.from([
|
||||
key: Uint8Array.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([
|
||||
signature: Uint8Array.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([
|
||||
data: Uint8Array.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([
|
||||
publicKey: Uint8Array.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
|
||||
])
|
||||
}
|
||||
|
31
test/fixtures/go-key-secp256k1.js
vendored
Normal file
31
test/fixtures/go-key-secp256k1.js
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
'use strict'
|
||||
|
||||
const uint8ArrayFromString = require('uint8arrays/from-string')
|
||||
|
||||
// The keypair and signature below were generated in a gore repl session (https://github.com/motemen/gore)
|
||||
// using the secp256k1 fork of go-libp2p-crypto by github user @vyzo
|
||||
//
|
||||
// gore> :import github.com/vyzo/go-libp2p-crypto
|
||||
// gore> :import crypto/rand
|
||||
// gore> :import io/ioutil
|
||||
// gore> priv, pub, err := crypto.GenerateKeyPairWithReader(crypto.Secp256k1, 256, rand.Reader)
|
||||
// gore> privBytes, err := priv.Bytes()
|
||||
// gore> pubBytes, err := pub.Bytes()
|
||||
// gore> msg := []byte("hello! and welcome to some awesome crypto primitives")
|
||||
// gore> sig, err := priv.Sign(msg)
|
||||
// gore> ioutil.WriteFile("/tmp/secp-go-priv.bin", privBytes, 0644)
|
||||
// gore> ioutil.WriteFile("/tmp/secp-go-pub.bin", pubBytes, 0644)
|
||||
// gore> ioutil.WriteFile("/tmp/secp-go-sig.bin", sig, 0644)
|
||||
//
|
||||
// The generated files were then read in a node repl with e.g.:
|
||||
// > fs.readFileSync('/tmp/secp-go-pub.bin').toString('hex')
|
||||
// '08021221029c0ce5d53646ed47112560297a3e59b78b8cbd4bae37c7a0c236eeb91d0fbeaf'
|
||||
//
|
||||
// and the results copy/pasted in here
|
||||
|
||||
module.exports = {
|
||||
privateKey: uint8ArrayFromString('08021220358f15db8c2014d570e8e3a622454e2273975a3cca443ec0c45375b13d381d18', 'base16'),
|
||||
publicKey: uint8ArrayFromString('08021221029c0ce5d53646ed47112560297a3e59b78b8cbd4bae37c7a0c236eeb91d0fbeaf', 'base16'),
|
||||
message: uint8ArrayFromString('hello! and welcome to some awesome crypto primitives'),
|
||||
signature: uint8ArrayFromString('304402200e4c629e9f5d99439115e60989cd40087f6978c36078b0b50cf3d30af5c38d4102204110342c8e7f0809897c1c7a66e49e1c6b7cb0a6ed6993640ec2fe742c1899a9', 'base16')
|
||||
}
|
16
test/fixtures/go-stretch-key.js
vendored
16
test/fixtures/go-stretch-key.js
vendored
@@ -1,32 +1,30 @@
|
||||
'use strict'
|
||||
|
||||
const Buffer = require('safe-buffer').Buffer
|
||||
|
||||
module.exports = [{
|
||||
cipher: 'AES-256',
|
||||
hash: 'SHA256',
|
||||
secret: Buffer.from([
|
||||
secret: Uint8Array.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: Buffer.from([
|
||||
iv: Uint8Array.from([
|
||||
208, 132, 203, 169, 253, 52, 40, 83, 161, 91, 17, 71, 33, 136, 67, 96
|
||||
]),
|
||||
cipherKey: Buffer.from([
|
||||
cipherKey: Uint8Array.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: Buffer.from([
|
||||
macKey: Uint8Array.from([
|
||||
6, 179, 91, 245, 224, 56, 153, 120, 77, 140, 29, 5, 15, 213, 187, 65, 137, 230, 202, 120
|
||||
])
|
||||
},
|
||||
k2: {
|
||||
iv: Buffer.from([
|
||||
iv: Uint8Array.from([
|
||||
236, 17, 34, 141, 90, 106, 197, 56, 197, 184, 157, 135, 91, 88, 112, 19
|
||||
]),
|
||||
cipherKey: Buffer.from([
|
||||
cipherKey: Uint8Array.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: Buffer.from([
|
||||
macKey: Uint8Array.from([
|
||||
3, 229, 77, 212, 241, 217, 23, 113, 220, 126, 38, 255, 18, 117, 108, 205, 198, 89, 1, 236
|
||||
])
|
||||
}
|
||||
|
7
test/fixtures/secp256k1.js
vendored
7
test/fixtures/secp256k1.js
vendored
@@ -1,11 +1,10 @@
|
||||
'use strict'
|
||||
|
||||
const Buffer = require('safe-buffer').Buffer
|
||||
const uint8ArrayFromString = require('uint8arrays/from-string')
|
||||
|
||||
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')
|
||||
pbmPrivateKey: uint8ArrayFromString('08021220e0600103010000000100000000000000be1dc82c2e000000e8d6030301000000', 'base16'),
|
||||
pbmPublicKey: uint8ArrayFromString('0802122103a9a7272a726fa083abf31ba44037f8347fbc5e5d3113d62a7c6bc26752fd8ee1', 'base16')
|
||||
}
|
||||
|
33
test/helpers/test-garbage-error-handling.js
Normal file
33
test/helpers/test-garbage-error-handling.js
Normal file
@@ -0,0 +1,33 @@
|
||||
/* eslint-env mocha */
|
||||
'use strict'
|
||||
|
||||
const uint8ArrayFromString = require('uint8arrays/from-string')
|
||||
const util = require('util')
|
||||
const garbage = [uint8ArrayFromString('00010203040506070809', 'base16'), {}, null, false, undefined, true, 1, 0, uint8ArrayFromString(''), 'aGVsbG93b3JsZA==', 'helloworld', '']
|
||||
|
||||
function doTests (fncName, fnc, num, skipBuffersAndStrings) {
|
||||
if (!num) {
|
||||
num = 1
|
||||
}
|
||||
|
||||
garbage.forEach((garbage) => {
|
||||
if (skipBuffersAndStrings && (garbage instanceof Uint8Array || (typeof garbage) === 'string')) {
|
||||
// skip this garbage because it's a Uint8Array 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 }
|
@@ -2,11 +2,11 @@
|
||||
/* eslint-env mocha */
|
||||
'use strict'
|
||||
|
||||
const Buffer = require('safe-buffer').Buffer
|
||||
const chai = require('chai')
|
||||
const dirtyChai = require('dirty-chai')
|
||||
const expect = chai.expect
|
||||
chai.use(dirtyChai)
|
||||
const uint8ArrayFromString = require('uint8arrays/from-string')
|
||||
|
||||
const crypto = require('../../src')
|
||||
|
||||
@@ -14,16 +14,10 @@ const hashes = ['SHA1', 'SHA256', 'SHA512']
|
||||
|
||||
describe('HMAC', () => {
|
||||
hashes.forEach((hash) => {
|
||||
it(`${hash} - sign and verify`, (done) => {
|
||||
crypto.hmac.create(hash, Buffer.from('secret'), (err, hmac) => {
|
||||
expect(err).to.not.exist()
|
||||
|
||||
hmac.digest(Buffer.from('hello world'), (err, sig) => {
|
||||
expect(err).to.not.exist()
|
||||
expect(sig).to.have.length(hmac.length)
|
||||
done()
|
||||
})
|
||||
})
|
||||
it(`${hash} - sign and verify`, async () => {
|
||||
const hmac = await crypto.hmac.create(hash, uint8ArrayFromString('secret'))
|
||||
const sig = await hmac.digest(uint8ArrayFromString('hello world'))
|
||||
expect(sig).to.have.length(hmac.length)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@@ -5,114 +5,104 @@ const chai = require('chai')
|
||||
const dirtyChai = require('dirty-chai')
|
||||
const expect = chai.expect
|
||||
chai.use(dirtyChai)
|
||||
const Buffer = require('safe-buffer').Buffer
|
||||
const uint8ArrayFromString = require('uint8arrays/from-string')
|
||||
|
||||
const crypto = require('../../src')
|
||||
const ed25519 = crypto.keys.keys.ed25519
|
||||
const ed25519 = crypto.keys.supportedKeys.ed25519
|
||||
const fixtures = require('../fixtures/go-key-ed25519')
|
||||
|
||||
describe('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((done) => {
|
||||
crypto.keys.generateKeyPair('Ed25519', 512, (err, _key) => {
|
||||
if (err) return done(err)
|
||||
key = _key
|
||||
done()
|
||||
})
|
||||
before(async () => {
|
||||
key = await crypto.keys.generateKeyPair('Ed25519', 512)
|
||||
})
|
||||
|
||||
it('generates a valid key', (done) => {
|
||||
it('generates a valid key', async () => {
|
||||
expect(key).to.be.an.instanceof(ed25519.Ed25519PrivateKey)
|
||||
|
||||
key.hash((err, digest) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
||||
|
||||
expect(digest).to.have.length(34)
|
||||
done()
|
||||
})
|
||||
const digest = await key.hash()
|
||||
expect(digest).to.have.length(34)
|
||||
})
|
||||
|
||||
it('generates a valid key from seed', (done) => {
|
||||
it('generates a valid key from seed', async () => {
|
||||
var seed = crypto.randomBytes(32)
|
||||
crypto.keys.generateKeyPairFromSeed('Ed25519', seed, 512, (err, seededkey) => {
|
||||
if (err) return done(err)
|
||||
expect(seededkey).to.be.an.instanceof(ed25519.Ed25519PrivateKey)
|
||||
|
||||
seededkey.hash((err, digest) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
||||
|
||||
expect(digest).to.have.length(34)
|
||||
done()
|
||||
})
|
||||
})
|
||||
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', (done) => {
|
||||
var seed = crypto.randomBytes(32)
|
||||
crypto.keys.generateKeyPairFromSeed('Ed25519', seed, 512, (err, seededkey1) => {
|
||||
if (err) return done(err)
|
||||
crypto.keys.generateKeyPairFromSeed('Ed25519', seed, 512, (err, seededkey2) => {
|
||||
if (err) return done(err)
|
||||
expect(seededkey1.equals(seededkey2)).to.eql(true)
|
||||
expect(seededkey1.public.equals(seededkey2.public)).to.eql(true)
|
||||
done()
|
||||
})
|
||||
})
|
||||
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', (done) => {
|
||||
it('generates different keys for different seeds', async () => {
|
||||
const seed1 = crypto.randomBytes(32)
|
||||
crypto.keys.generateKeyPairFromSeed('Ed25519', seed1, 512, (err, seededkey1) => {
|
||||
expect(err).to.not.exist()
|
||||
|
||||
const seed2 = crypto.randomBytes(32)
|
||||
crypto.keys.generateKeyPairFromSeed('Ed25519', seed2, 512, (err, seededkey2) => {
|
||||
expect(err).to.not.exist()
|
||||
|
||||
expect(seededkey1.equals(seededkey2)).to.eql(false)
|
||||
expect(seededkey1.public.equals(seededkey2.public))
|
||||
.to.eql(false)
|
||||
done()
|
||||
})
|
||||
})
|
||||
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', (done) => {
|
||||
it('signs', async () => {
|
||||
const text = crypto.randomBytes(512)
|
||||
|
||||
key.sign(text, (err, sig) => {
|
||||
expect(err).to.not.exist()
|
||||
|
||||
key.public.verify(text, sig, (err, res) => {
|
||||
expect(err).to.not.exist()
|
||||
expect(res).to.be.eql(true)
|
||||
done()
|
||||
})
|
||||
})
|
||||
const sig = await key.sign(text)
|
||||
const res = await key.public.verify(text, sig)
|
||||
expect(res).to.be.eql(true)
|
||||
})
|
||||
|
||||
it('encoding', (done) => {
|
||||
it('encoding', async () => {
|
||||
const keyMarshal = key.marshal()
|
||||
ed25519.unmarshalEd25519PrivateKey(keyMarshal, (err, key2) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
||||
const keyMarshal2 = key2.marshal()
|
||||
const key2 = await ed25519.unmarshalEd25519PrivateKey(keyMarshal)
|
||||
const keyMarshal2 = key2.marshal()
|
||||
|
||||
expect(keyMarshal).to.eql(keyMarshal2)
|
||||
expect(keyMarshal).to.eql(keyMarshal2)
|
||||
|
||||
const pk = key.public
|
||||
const pkMarshal = pk.marshal()
|
||||
const pk2 = ed25519.unmarshalEd25519PublicKey(pkMarshal)
|
||||
const pkMarshal2 = pk2.marshal()
|
||||
const pk = key.public
|
||||
const pkMarshal = pk.marshal()
|
||||
const pk2 = ed25519.unmarshalEd25519PublicKey(pkMarshal)
|
||||
const pkMarshal2 = pk2.marshal()
|
||||
|
||||
expect(pkMarshal).to.eql(pkMarshal2)
|
||||
done()
|
||||
})
|
||||
expect(pkMarshal).to.eql(pkMarshal2)
|
||||
})
|
||||
|
||||
it('key id', async () => {
|
||||
const key = await crypto.keys.unmarshalPrivateKey(fixtures.verify.privateKey)
|
||||
const id = await key.id()
|
||||
expect(id).to.eql('12D3KooWLqLxEfJ9nDdEe8Kh8PFvNPQRYDQBwyL7CMM7HhVd5LsX')
|
||||
})
|
||||
|
||||
it('should export a password encrypted libp2p-key', async () => {
|
||||
const key = await crypto.keys.generateKeyPair('Ed25519')
|
||||
const encryptedKey = await key.export('my secret')
|
||||
// Import the key
|
||||
const importedKey = await crypto.keys.import(encryptedKey, 'my secret')
|
||||
expect(key.equals(importedKey)).to.equal(true)
|
||||
})
|
||||
|
||||
it('should fail to import libp2p-key with wrong password', async () => {
|
||||
const key = await crypto.keys.generateKeyPair('Ed25519')
|
||||
const encryptedKey = await key.export('my secret', 'libp2p-key')
|
||||
try {
|
||||
await crypto.keys.import(encryptedKey, 'not my secret')
|
||||
} catch (err) {
|
||||
expect(err).to.exist()
|
||||
return
|
||||
}
|
||||
expect.fail('should have thrown')
|
||||
})
|
||||
|
||||
describe('key equals', () => {
|
||||
@@ -130,80 +120,66 @@ describe('ed25519', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('not equals other key', (done) => {
|
||||
crypto.keys.generateKeyPair('Ed25519', 512, (err, key2) => {
|
||||
if (err) return done(err)
|
||||
|
||||
expect(key.equals(key2)).to.eql(false)
|
||||
expect(key2.equals(key)).to.eql(false)
|
||||
expect(key.public.equals(key2.public)).to.eql(false)
|
||||
expect(key2.public.equals(key.public)).to.eql(false)
|
||||
done()
|
||||
})
|
||||
it('not equals other key', async () => {
|
||||
const key2 = await 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', (done) => {
|
||||
const data = Buffer.from('hello world')
|
||||
key.sign(data, (err, sig) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
||||
|
||||
key.public.verify(data, sig, (err, valid) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
||||
expect(valid).to.eql(true)
|
||||
done()
|
||||
})
|
||||
})
|
||||
it('sign and verify', async () => {
|
||||
const data = uint8ArrayFromString('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', (done) => {
|
||||
const data = Buffer.from('hello world')
|
||||
key.sign(data, (err, sig) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
||||
it('fails to verify for different data', async () => {
|
||||
const data = uint8ArrayFromString('hello world')
|
||||
const sig = await key.sign(data)
|
||||
const valid = await key.public.verify(uint8ArrayFromString('hello'), sig)
|
||||
expect(valid).to.be.eql(false)
|
||||
})
|
||||
|
||||
key.public.verify(Buffer.from('hello'), sig, (err, valid) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
||||
expect(valid).to.be.eql(false)
|
||||
done()
|
||||
})
|
||||
})
|
||||
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', () => {
|
||||
let privateKey
|
||||
|
||||
before((done) => {
|
||||
crypto.keys.unmarshalPrivateKey(fixtures.verify.privateKey, (err, key) => {
|
||||
expect(err).to.not.exist()
|
||||
privateKey = key
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('verifies with data from go', (done) => {
|
||||
// @ts-check
|
||||
it('verifies with data from go', async () => {
|
||||
const key = crypto.keys.unmarshalPublicKey(fixtures.verify.publicKey)
|
||||
|
||||
key.verify(fixtures.verify.data, fixtures.verify.signature, (err, ok) => {
|
||||
expect(err).to.not.exist()
|
||||
expect(ok).to.eql(true)
|
||||
done()
|
||||
})
|
||||
const ok = await key.verify(fixtures.verify.data, fixtures.verify.signature)
|
||||
expect(ok).to.eql(true)
|
||||
})
|
||||
|
||||
it('generates the same signature as go', (done) => {
|
||||
privateKey.sign(fixtures.verify.data, (err, sig) => {
|
||||
expect(err).to.not.exist()
|
||||
expect(sig).to.eql(fixtures.verify.signature)
|
||||
done()
|
||||
})
|
||||
it('does not include the redundant public key when marshalling privatekey', async () => {
|
||||
const key = await crypto.keys.unmarshalPrivateKey(fixtures.redundantPubKey.privateKey)
|
||||
const bytes = key.marshal()
|
||||
expect(bytes.length).to.equal(64)
|
||||
expect(bytes.slice(32)).to.eql(key.public.marshal())
|
||||
})
|
||||
|
||||
it('verifies with data from go with redundant public key', async () => {
|
||||
const key = crypto.keys.unmarshalPublicKey(fixtures.redundantPubKey.publicKey)
|
||||
const ok = await key.verify(fixtures.redundantPubKey.data, fixtures.redundantPubKey.signature)
|
||||
expect(ok).to.eql(true)
|
||||
})
|
||||
|
||||
it('generates the same signature as go', async () => {
|
||||
const key = await crypto.keys.unmarshalPrivateKey(fixtures.verify.privateKey)
|
||||
const sig = await key.sign(fixtures.verify.data)
|
||||
expect(sig).to.eql(fixtures.verify.signature)
|
||||
})
|
||||
|
||||
it('generates the same signature as go with redundant public key', async () => {
|
||||
const key = await crypto.keys.unmarshalPrivateKey(fixtures.redundantPubKey.privateKey)
|
||||
const sig = await key.sign(fixtures.redundantPubKey.data)
|
||||
expect(sig).to.eql(fixtures.redundantPubKey.signature)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@@ -6,12 +6,15 @@ const chai = require('chai')
|
||||
const dirtyChai = require('dirty-chai')
|
||||
const expect = chai.expect
|
||||
chai.use(dirtyChai)
|
||||
const parallel = require('async/parallel')
|
||||
|
||||
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,
|
||||
@@ -25,49 +28,50 @@ const secretLengths = {
|
||||
|
||||
describe('generateEphemeralKeyPair', () => {
|
||||
curves.forEach((curve) => {
|
||||
it(`generate and shared key ${curve}`, (done) => {
|
||||
parallel([
|
||||
(cb) => crypto.keys.generateEphemeralKeyPair(curve, cb),
|
||||
(cb) => crypto.keys.generateEphemeralKeyPair(curve, cb)
|
||||
], (err, keys) => {
|
||||
expect(err).to.not.exist()
|
||||
expect(keys[0].key).to.have.length(lengths[curve])
|
||||
expect(keys[1].key).to.have.length(lengths[curve])
|
||||
it(`generate and shared key ${curve}`, async () => {
|
||||
const keys = await Promise.all([
|
||||
crypto.keys.generateEphemeralKeyPair(curve),
|
||||
crypto.keys.generateEphemeralKeyPair(curve)
|
||||
])
|
||||
|
||||
keys[0].genSharedKey(keys[1].key, (err, shared) => {
|
||||
expect(err).to.not.exist()
|
||||
expect(shared).to.have.length(secretLengths[curve])
|
||||
done()
|
||||
})
|
||||
})
|
||||
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', (done) => {
|
||||
it('generates a shared secret', async () => {
|
||||
const curve = fixtures.curve
|
||||
|
||||
parallel([
|
||||
(cb) => crypto.keys.generateEphemeralKeyPair(curve, cb),
|
||||
(cb) => crypto.keys.generateEphemeralKeyPair(curve, cb)
|
||||
], (err, res) => {
|
||||
expect(err).to.not.exist()
|
||||
const alice = res[0]
|
||||
const bob = res[1]
|
||||
bob.key = fixtures.bob.public
|
||||
const keys = await Promise.all([
|
||||
crypto.keys.generateEphemeralKeyPair(curve),
|
||||
crypto.keys.generateEphemeralKeyPair(curve)
|
||||
])
|
||||
|
||||
parallel([
|
||||
(cb) => alice.genSharedKey(bob.key, cb),
|
||||
(cb) => bob.genSharedKey(alice.key, fixtures.bob, cb)
|
||||
], (err, secrets) => {
|
||||
expect(err).to.not.exist()
|
||||
const alice = keys[0]
|
||||
const bob = keys[1]
|
||||
bob.key = fixtures.bob.public
|
||||
|
||||
expect(secrets[0]).to.eql(secrets[1])
|
||||
expect(secrets[0]).to.have.length(32)
|
||||
const secrets = await Promise.all([
|
||||
alice.genSharedKey(bob.key),
|
||||
bob.genSharedKey(alice.key, fixtures.bob)
|
||||
])
|
||||
|
||||
done()
|
||||
})
|
||||
})
|
||||
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')
|
||||
})
|
||||
})
|
||||
|
@@ -6,6 +6,7 @@ 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')
|
||||
|
||||
@@ -14,62 +15,51 @@ describe('keyStretcher', () => {
|
||||
const ciphers = ['AES-128', 'AES-256', 'Blowfish']
|
||||
const hashes = ['SHA1', 'SHA256', 'SHA512']
|
||||
let res
|
||||
// @ts-check
|
||||
/**
|
||||
* @type {Uint8Array}
|
||||
*/
|
||||
let secret
|
||||
|
||||
before((done) => {
|
||||
crypto.keys.generateEphemeralKeyPair('P-256', (err, _res) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
||||
res = _res
|
||||
res.genSharedKey(res.key, (err, _secret) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
||||
|
||||
secret = _secret
|
||||
done()
|
||||
})
|
||||
})
|
||||
before(async () => {
|
||||
res = await crypto.keys.generateEphemeralKeyPair('P-256')
|
||||
secret = await res.genSharedKey(res.key)
|
||||
})
|
||||
|
||||
ciphers.forEach((cipher) => {
|
||||
hashes.forEach((hash) => {
|
||||
it(`${cipher} - ${hash}`, (done) => {
|
||||
crypto.keys.keyStretcher(cipher, hash, secret, (err, keys) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
||||
|
||||
expect(keys.k1).to.exist()
|
||||
expect(keys.k2).to.exist()
|
||||
done()
|
||||
})
|
||||
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}`, (done) => {
|
||||
it(`${test.cipher} - ${test.hash}`, async () => {
|
||||
const cipher = test.cipher
|
||||
const hash = test.hash
|
||||
const secret = test.secret
|
||||
crypto.keys.keyStretcher(cipher, hash, secret, (err, keys) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
||||
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.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)
|
||||
done()
|
||||
})
|
||||
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)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
53
test/keys/rsa-crypto-libs.js
Normal file
53
test/keys/rsa-crypto-libs.js
Normal 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
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
@@ -1,3 +1,4 @@
|
||||
/* eslint max-nested-callbacks: ["error", 8] */
|
||||
/* eslint-env mocha */
|
||||
'use strict'
|
||||
|
||||
@@ -5,75 +6,62 @@ const chai = require('chai')
|
||||
const dirtyChai = require('dirty-chai')
|
||||
const expect = chai.expect
|
||||
chai.use(dirtyChai)
|
||||
const Buffer = require('safe-buffer').Buffer
|
||||
chai.use(require('chai-string'))
|
||||
const { expectErrCode } = require('../util')
|
||||
const uint8ArrayFromString = require('uint8arrays/from-string')
|
||||
|
||||
const crypto = require('../../src')
|
||||
const rsa = crypto.keys.keys.rsa
|
||||
const rsa = crypto.keys.supportedKeys.rsa
|
||||
const fixtures = require('../fixtures/go-key-rsa')
|
||||
|
||||
describe('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((done) => {
|
||||
crypto.keys.generateKeyPair('RSA', 2048, (err, _key) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
||||
key = _key
|
||||
done()
|
||||
})
|
||||
before(async () => {
|
||||
key = await rsa.generateKeyPair(512)
|
||||
})
|
||||
|
||||
it('generates a valid key', (done) => {
|
||||
it('generates a valid key', async () => {
|
||||
expect(key).to.be.an.instanceof(rsa.RsaPrivateKey)
|
||||
|
||||
key.hash((err, digest) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
||||
|
||||
expect(digest).to.have.length(34)
|
||||
done()
|
||||
})
|
||||
const digest = await key.hash()
|
||||
expect(digest).to.have.length(34)
|
||||
})
|
||||
|
||||
it('signs', (done) => {
|
||||
it('signs', async () => {
|
||||
const text = key.genSecret()
|
||||
|
||||
key.sign(text, (err, sig) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
||||
|
||||
key.public.verify(text, sig, (err, res) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
||||
|
||||
expect(res).to.be.eql(true)
|
||||
done()
|
||||
})
|
||||
})
|
||||
const sig = await key.sign(text)
|
||||
const res = await key.public.verify(text, sig)
|
||||
expect(res).to.be.eql(true)
|
||||
})
|
||||
|
||||
it('encoding', (done) => {
|
||||
it('encoding', async () => {
|
||||
const keyMarshal = key.marshal()
|
||||
rsa.unmarshalRsaPrivateKey(keyMarshal, (err, key2) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
||||
const keyMarshal2 = key2.marshal()
|
||||
const key2 = await rsa.unmarshalRsaPrivateKey(keyMarshal)
|
||||
const keyMarshal2 = key2.marshal()
|
||||
|
||||
expect(keyMarshal).to.eql(keyMarshal2)
|
||||
expect(keyMarshal).to.eql(keyMarshal2)
|
||||
|
||||
const pk = key.public
|
||||
const pkMarshal = pk.marshal()
|
||||
const pk2 = rsa.unmarshalRsaPublicKey(pkMarshal)
|
||||
const pkMarshal2 = pk2.marshal()
|
||||
const pk = key.public
|
||||
const pkMarshal = pk.marshal()
|
||||
const pk2 = rsa.unmarshalRsaPublicKey(pkMarshal)
|
||||
const pkMarshal2 = pk2.marshal()
|
||||
|
||||
expect(pkMarshal).to.eql(pkMarshal2)
|
||||
done()
|
||||
})
|
||||
expect(pkMarshal).to.eql(pkMarshal2)
|
||||
})
|
||||
|
||||
it('key id', async () => {
|
||||
const key = await crypto.keys.unmarshalPrivateKey(uint8ArrayFromString('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', 'base64pad'))
|
||||
const id = await key.id()
|
||||
expect(id).to.eql('QmQgsppVMDUpe83wcAqaemKbYvHeF127gnSFQ1xFnBodVw')
|
||||
})
|
||||
|
||||
describe('key equals', () => {
|
||||
@@ -83,65 +71,333 @@ describe('RSA', () => {
|
||||
expect(key.public.equals(key.public)).to.eql(true)
|
||||
})
|
||||
|
||||
it('not equals other key', (done) => {
|
||||
crypto.keys.generateKeyPair('RSA', 2048, (err, key2) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
||||
|
||||
expect(key.equals(key2)).to.eql(false)
|
||||
expect(key2.equals(key)).to.eql(false)
|
||||
expect(key.public.equals(key2.public)).to.eql(false)
|
||||
expect(key2.public.equals(key.public)).to.eql(false)
|
||||
done()
|
||||
})
|
||||
it('not equals other key', async () => {
|
||||
const key2 = await 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', (done) => {
|
||||
const data = Buffer.from('hello world')
|
||||
key.sign(data, (err, sig) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
||||
it('sign and verify', async () => {
|
||||
const data = uint8ArrayFromString('hello world')
|
||||
const sig = await key.sign(data)
|
||||
const valid = await key.public.verify(data, sig)
|
||||
expect(valid).to.be.eql(true)
|
||||
})
|
||||
|
||||
key.public.verify(data, sig, (err, valid) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
||||
expect(valid).to.be.eql(true)
|
||||
done()
|
||||
})
|
||||
it('encrypt and decrypt', async () => {
|
||||
const data = uint8ArrayFromString('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(uint8ArrayFromString('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', 'base64pad'))
|
||||
|
||||
const msg = uint8ArrayFromString('hello')
|
||||
|
||||
// browser
|
||||
const dec1 = id.decrypt(uint8ArrayFromString('YRFUDx8UjbWSfDS84cDA4WowaaOmd1qFNAv5QutodCKYb9uPtU/tDiAvJzOGu5DCJRo2J0l/35P2weiB4/C2Cb1aZgXKMx/QQC+2jSJiymhqcZaYerjTvkCFwkjCaqthoVo/YXxsaFZ1q7bdTZUDH1TaJR7hWfSyzyPcA8c0w43MIsw16pY8ZaPSclvnCwhoTg1JGjMk6te3we7+wR8QU7VrPhs54mZWxrpu3NQ8xZ6xQqIedsEiNhBUccrCSzYghgsP0Ae/8iKyGyl3U6IegsJNn8jcocvzOJrmU03rgIFPjvuBdaqB38xDSTjbA123KadB28jNoSZh18q/yH3ZIg==', 'base64pad'))
|
||||
expect(dec1).to.be.eql(msg)
|
||||
// node
|
||||
const dec2 = id.decrypt(uint8ArrayFromString('e6yxssqXsWc27ozDy0PGKtMkCS28KwFyES2Ijz89yiz+w6bSFkNOhHPKplpPzgQEuNoUGdbseKlJFyRYHjIT8FQFBHZM8UgSkgoimbY5on4xSxXs7E5/+twjqKdB7oNveTaTf7JCwaeUYnKSjbiYFEawtMiQE91F8sTT7TmSzOZ48tUhnddAAZ3Ac/O3Z9MSAKOCDipi+JdZtXRT8KimGt36/7hjjosYmPuHR1Xy/yMTL6SMbXtBM3yAuEgbQgP+q/7kHMHji3/JvTpYdIUU+LVtkMusXNasRA+UWG2zAht18vqjFMsm9JTiihZw9jRHD4vxAhf75M992tnC+0ZuQg==', 'base64pad'))
|
||||
expect(dec2).to.be.eql(msg)
|
||||
})
|
||||
|
||||
it('fails to verify for different data', async () => {
|
||||
const data = uint8ArrayFromString('hello world')
|
||||
const sig = await key.sign(data)
|
||||
const valid = await key.public.verify(uint8ArrayFromString('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('should export a password encrypted libp2p-key', async () => {
|
||||
const encryptedKey = await key.export('my secret', 'libp2p-key')
|
||||
// Import the key
|
||||
const importedKey = await crypto.keys.import(encryptedKey, 'my secret')
|
||||
expect(key.equals(importedKey)).to.equal(true)
|
||||
})
|
||||
|
||||
it('should fail to import libp2p-key with wrong password', async () => {
|
||||
const encryptedKey = await key.export('my secret', 'libp2p-key')
|
||||
try {
|
||||
await crypto.keys.import(encryptedKey, 'not my secret')
|
||||
} catch (err) {
|
||||
expect(err).to.exist()
|
||||
return
|
||||
}
|
||||
expect.fail('should have thrown')
|
||||
})
|
||||
|
||||
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')
|
||||
})
|
||||
})
|
||||
|
||||
it('fails to verify for different data', (done) => {
|
||||
const data = Buffer.from('hello world')
|
||||
key.sign(data, (err, sig) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
||||
|
||||
key.public.verify(Buffer.from('hello'), sig, (err, valid) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
||||
expect(valid).to.be.eql(false)
|
||||
done()
|
||||
})
|
||||
})
|
||||
describe('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', (done) => {
|
||||
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)
|
||||
})
|
||||
})
|
||||
|
||||
key.verify(fixtures.verify.data, fixtures.verify.signature, (err, ok) => {
|
||||
if (err) throw err
|
||||
expect(err).to.not.exist()
|
||||
expect(ok).to.equal(true)
|
||||
done()
|
||||
})
|
||||
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()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@@ -5,95 +5,283 @@ const chai = require('chai')
|
||||
const dirtyChai = require('dirty-chai')
|
||||
const expect = chai.expect
|
||||
chai.use(dirtyChai)
|
||||
const fixtures = require('../fixtures/secp256k1')
|
||||
const crypto = require('../../src')
|
||||
const secp256k1 = crypto.keys.supportedKeys.secp256k1
|
||||
const keysPBM = crypto.keys.keysPBM
|
||||
const randomBytes = crypto.randomBytes
|
||||
const secp256k1Crypto = require('../../src/keys/secp256k1')(randomBytes)
|
||||
const uint8ArrayFromString = require('uint8arrays/from-string')
|
||||
const fixtures = require('../fixtures/go-key-secp256k1')
|
||||
|
||||
const mockPublicKey = {
|
||||
bytes: fixtures.pbmPublicKey
|
||||
}
|
||||
|
||||
const mockPrivateKey = {
|
||||
bytes: fixtures.pbmPrivateKey,
|
||||
public: mockPublicKey
|
||||
}
|
||||
|
||||
const mockSecp256k1Module = {
|
||||
generateKeyPair (bits, callback) {
|
||||
callback(null, mockPrivateKey)
|
||||
},
|
||||
|
||||
unmarshalSecp256k1PrivateKey (buf, callback) {
|
||||
callback(null, mockPrivateKey)
|
||||
},
|
||||
|
||||
unmarshalSecp256k1PublicKey (buf) {
|
||||
return mockPublicKey
|
||||
}
|
||||
}
|
||||
|
||||
describe('without libp2p-crypto-secp256k1 module present', () => {
|
||||
crypto.keys.keys['secp256k1'] = undefined
|
||||
|
||||
it('fails to generate a secp256k1 key', (done) => {
|
||||
crypto.keys.generateKeyPair('secp256k1', 256, (err, key) => {
|
||||
expect(err).to.exist()
|
||||
expect(key).to.not.exist()
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('fails to unmarshal a secp256k1 private key', (done) => {
|
||||
crypto.keys.unmarshalPrivateKey(fixtures.pbmPrivateKey, (err, key) => {
|
||||
expect(err).to.exist()
|
||||
expect(key).to.not.exist()
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('fails to unmarshal a secp256k1 public key', () => {
|
||||
expect(() => {
|
||||
crypto.keys.unmarshalPublicKey(fixtures.pbmPublicKey)
|
||||
}).to.throw(Error)
|
||||
})
|
||||
})
|
||||
|
||||
describe('with libp2p-crypto-secp256k1 module present', () => {
|
||||
describe('secp256k1 keys', () => {
|
||||
let key
|
||||
|
||||
before((done) => {
|
||||
crypto.keys.keys['secp256k1'] = mockSecp256k1Module
|
||||
crypto.keys.generateKeyPair('secp256k1', 256, (err, _key) => {
|
||||
if (err) return done(err)
|
||||
key = _key
|
||||
done()
|
||||
before(async () => {
|
||||
key = await secp256k1.generateKeyPair()
|
||||
})
|
||||
|
||||
it('generates a valid key', async () => {
|
||||
expect(key).to.be.an.instanceof(secp256k1.Secp256k1PrivateKey)
|
||||
expect(key.public).to.be.an.instanceof(secp256k1.Secp256k1PublicKey)
|
||||
|
||||
const digest = await key.hash()
|
||||
expect(digest).to.have.length(34)
|
||||
|
||||
const publicDigest = await key.public.hash()
|
||||
expect(publicDigest).to.have.length(34)
|
||||
})
|
||||
|
||||
it('optionally accepts a `bits` argument when generating a key', async () => {
|
||||
const _key = await secp256k1.generateKeyPair()
|
||||
expect(_key).to.be.an.instanceof(secp256k1.Secp256k1PrivateKey)
|
||||
})
|
||||
|
||||
it('signs', async () => {
|
||||
const text = randomBytes(512)
|
||||
const sig = await key.sign(text)
|
||||
const res = await key.public.verify(text, sig)
|
||||
expect(res).to.equal(true)
|
||||
})
|
||||
|
||||
it('encoding', async () => {
|
||||
const keyMarshal = key.marshal()
|
||||
const key2 = await secp256k1.unmarshalSecp256k1PrivateKey(keyMarshal)
|
||||
const keyMarshal2 = key2.marshal()
|
||||
|
||||
expect(keyMarshal).to.eql(keyMarshal2)
|
||||
|
||||
const pk = key.public
|
||||
const pkMarshal = pk.marshal()
|
||||
const pk2 = secp256k1.unmarshalSecp256k1PublicKey(pkMarshal)
|
||||
const pkMarshal2 = pk2.marshal()
|
||||
|
||||
expect(pkMarshal).to.eql(pkMarshal2)
|
||||
})
|
||||
|
||||
it('key id', async () => {
|
||||
const decoded = keysPBM.PrivateKey.decode(fixtures.privateKey)
|
||||
const key = await secp256k1.unmarshalSecp256k1PrivateKey(decoded.Data)
|
||||
const id = await key.id()
|
||||
expect(id).to.eql('QmPCyMBGEyifPtx5aa6k6wkY9N1eBf9vHK1eKfNc35q9uq')
|
||||
})
|
||||
|
||||
it('should export a password encrypted libp2p-key', async () => {
|
||||
const key = await crypto.keys.generateKeyPair('secp256k1')
|
||||
const encryptedKey = await key.export('my secret')
|
||||
// Import the key
|
||||
const importedKey = await crypto.keys.import(encryptedKey, 'my secret')
|
||||
expect(key.equals(importedKey)).to.equal(true)
|
||||
})
|
||||
|
||||
it('should fail to import libp2p-key with wrong password', async () => {
|
||||
const key = await crypto.keys.generateKeyPair('secp256k1')
|
||||
const encryptedKey = await key.export('my secret', 'libp2p-key')
|
||||
try {
|
||||
await crypto.keys.import(encryptedKey, 'not my secret')
|
||||
} catch (err) {
|
||||
expect(err).to.exist()
|
||||
return
|
||||
}
|
||||
expect.fail('should have thrown')
|
||||
})
|
||||
|
||||
describe('key equals', () => {
|
||||
it('equals itself', () => {
|
||||
expect(key.equals(key)).to.eql(true)
|
||||
|
||||
expect(key.public.equals(key.public)).to.eql(true)
|
||||
})
|
||||
|
||||
it('not equals other key', async () => {
|
||||
const key2 = await secp256k1.generateKeyPair()
|
||||
expect(key.equals(key2)).to.eql(false)
|
||||
expect(key2.equals(key)).to.eql(false)
|
||||
expect(key.public.equals(key2.public)).to.eql(false)
|
||||
expect(key2.public.equals(key.public)).to.eql(false)
|
||||
})
|
||||
})
|
||||
|
||||
after((done) => {
|
||||
delete crypto.keys['secp256k1']
|
||||
done()
|
||||
it('sign and verify', async () => {
|
||||
const data = uint8ArrayFromString('hello world')
|
||||
const sig = await key.sign(data)
|
||||
const valid = await key.public.verify(data, sig)
|
||||
expect(valid).to.eql(true)
|
||||
})
|
||||
|
||||
it('generates a valid key', (done) => {
|
||||
expect(key).to.exist()
|
||||
done()
|
||||
})
|
||||
|
||||
it('protobuf encoding', (done) => {
|
||||
const keyMarshal = crypto.keys.marshalPrivateKey(key)
|
||||
crypto.keys.unmarshalPrivateKey(keyMarshal, (err, key2) => {
|
||||
if (err) return done(err)
|
||||
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)
|
||||
done()
|
||||
})
|
||||
it('fails to verify for different data', async () => {
|
||||
const data = uint8ArrayFromString('hello world')
|
||||
const sig = await key.sign(data)
|
||||
const valid = await key.public.verify(uint8ArrayFromString('hello'), sig)
|
||||
expect(valid).to.eql(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('key generation error', () => {
|
||||
let generateKey
|
||||
let secp256k1
|
||||
|
||||
before(() => {
|
||||
generateKey = secp256k1Crypto.generateKey
|
||||
secp256k1 = require('../../src/keys/secp256k1-class')(keysPBM, randomBytes, secp256k1Crypto)
|
||||
secp256k1Crypto.generateKey = () => { throw new Error('Error generating key') }
|
||||
})
|
||||
|
||||
after(() => {
|
||||
secp256k1Crypto.generateKey = generateKey
|
||||
})
|
||||
|
||||
it('returns an error if key generation fails', async () => {
|
||||
try {
|
||||
await secp256k1.generateKeyPair()
|
||||
} catch (err) {
|
||||
return expect(err.message).to.equal('Error generating key')
|
||||
}
|
||||
throw new Error('Expected error to be thrown')
|
||||
})
|
||||
})
|
||||
|
||||
describe('handles generation of invalid key', () => {
|
||||
let generateKey
|
||||
let secp256k1
|
||||
|
||||
before(() => {
|
||||
generateKey = secp256k1Crypto.generateKey
|
||||
secp256k1 = require('../../src/keys/secp256k1-class')(keysPBM, randomBytes, secp256k1Crypto)
|
||||
secp256k1Crypto.generateKey = () => uint8ArrayFromString('not a real key')
|
||||
})
|
||||
|
||||
after(() => {
|
||||
secp256k1Crypto.generateKey = generateKey
|
||||
})
|
||||
|
||||
it('returns an error if key generator returns an invalid key', async () => {
|
||||
try {
|
||||
await secp256k1.generateKeyPair()
|
||||
} catch (err) {
|
||||
return expect(err.message).to.equal('Expected private key to be an Uint8Array with length 32')
|
||||
}
|
||||
throw new Error('Expected error to be thrown')
|
||||
})
|
||||
})
|
||||
|
||||
describe('crypto functions', () => {
|
||||
let privKey
|
||||
let pubKey
|
||||
|
||||
before(async () => {
|
||||
privKey = await secp256k1Crypto.generateKey()
|
||||
pubKey = secp256k1Crypto.computePublicKey(privKey)
|
||||
})
|
||||
|
||||
it('generates valid keys', () => {
|
||||
expect(() => {
|
||||
secp256k1Crypto.validatePrivateKey(privKey)
|
||||
secp256k1Crypto.validatePublicKey(pubKey)
|
||||
}).to.not.throw()
|
||||
})
|
||||
|
||||
it('does not validate an invalid key', () => {
|
||||
expect(() => secp256k1Crypto.validatePublicKey(uint8ArrayFromString('42'))).to.throw()
|
||||
expect(() => secp256k1Crypto.validatePrivateKey(uint8ArrayFromString('42'))).to.throw()
|
||||
})
|
||||
|
||||
it('validates a correct signature', async () => {
|
||||
const sig = await secp256k1Crypto.hashAndSign(privKey, uint8ArrayFromString('hello'))
|
||||
const valid = await secp256k1Crypto.hashAndVerify(pubKey, sig, uint8ArrayFromString('hello'))
|
||||
expect(valid).to.equal(true)
|
||||
})
|
||||
|
||||
it('errors if given a null Uint8Array to sign', async () => {
|
||||
try {
|
||||
await secp256k1Crypto.hashAndSign(privKey, null)
|
||||
} catch (err) {
|
||||
return // expected
|
||||
}
|
||||
throw new Error('Expected error to be thrown')
|
||||
})
|
||||
|
||||
it('errors when signing with an invalid key', async () => {
|
||||
try {
|
||||
await secp256k1Crypto.hashAndSign(uint8ArrayFromString('42'), uint8ArrayFromString('Hello'))
|
||||
} catch (err) {
|
||||
return expect(err.message).to.equal('Expected private key to be an Uint8Array with length 32')
|
||||
}
|
||||
throw new Error('Expected error to be thrown')
|
||||
})
|
||||
|
||||
it('errors if given a null Uint8Array to validate', async () => {
|
||||
const sig = await secp256k1Crypto.hashAndSign(privKey, uint8ArrayFromString('hello'))
|
||||
|
||||
try {
|
||||
await secp256k1Crypto.hashAndVerify(privKey, sig, null)
|
||||
} catch (err) {
|
||||
return // expected
|
||||
}
|
||||
throw new Error('Expected error to be thrown')
|
||||
})
|
||||
|
||||
it('errors when validating a message with an invalid signature', async () => {
|
||||
try {
|
||||
await secp256k1Crypto.hashAndVerify(pubKey, uint8ArrayFromString('invalid-sig'), uint8ArrayFromString('hello'))
|
||||
} catch (err) {
|
||||
return expect(err.message).to.equal('Signature could not be parsed')
|
||||
}
|
||||
throw new Error('Expected error to be thrown')
|
||||
})
|
||||
|
||||
it('errors when signing with an invalid key', async () => {
|
||||
try {
|
||||
await secp256k1Crypto.hashAndSign(uint8ArrayFromString('42'), uint8ArrayFromString('Hello'))
|
||||
} catch (err) {
|
||||
return expect(err.message).to.equal('Expected private key to be an Uint8Array with length 32')
|
||||
}
|
||||
throw new Error('Expected error to be thrown')
|
||||
})
|
||||
|
||||
it('throws when compressing an invalid public key', () => {
|
||||
expect(() => secp256k1Crypto.compressPublicKey(uint8ArrayFromString('42'))).to.throw()
|
||||
})
|
||||
|
||||
it('throws when decompressing an invalid public key', () => {
|
||||
expect(() => secp256k1Crypto.decompressPublicKey(uint8ArrayFromString('42'))).to.throw()
|
||||
})
|
||||
|
||||
it('compresses/decompresses a valid public key', () => {
|
||||
const decompressed = secp256k1Crypto.decompressPublicKey(pubKey)
|
||||
expect(decompressed).to.exist()
|
||||
expect(decompressed.length).to.be.eql(65)
|
||||
const recompressed = secp256k1Crypto.compressPublicKey(decompressed)
|
||||
expect(recompressed).to.eql(pubKey)
|
||||
})
|
||||
})
|
||||
|
||||
describe('go interop', () => {
|
||||
it('loads a private key marshaled by go-libp2p-crypto', async () => {
|
||||
// we need to first extract the key data from the protobuf, which is
|
||||
// normally handled by js-libp2p-crypto
|
||||
const decoded = keysPBM.PrivateKey.decode(fixtures.privateKey)
|
||||
expect(decoded.Type).to.eql(keysPBM.KeyType.Secp256k1)
|
||||
|
||||
const key = await secp256k1.unmarshalSecp256k1PrivateKey(decoded.Data)
|
||||
expect(key).to.be.an.instanceof(secp256k1.Secp256k1PrivateKey)
|
||||
expect(key.bytes).to.eql(fixtures.privateKey)
|
||||
})
|
||||
|
||||
it('loads a public key marshaled by go-libp2p-crypto', () => {
|
||||
const decoded = keysPBM.PublicKey.decode(fixtures.publicKey)
|
||||
expect(decoded.Type).to.be.eql(keysPBM.KeyType.Secp256k1)
|
||||
|
||||
const key = secp256k1.unmarshalSecp256k1PublicKey(decoded.Data)
|
||||
expect(key).to.be.an.instanceof(secp256k1.Secp256k1PublicKey)
|
||||
expect(key.bytes).to.eql(fixtures.publicKey)
|
||||
})
|
||||
|
||||
it('generates the same signature as go-libp2p-crypto', async () => {
|
||||
const decoded = keysPBM.PrivateKey.decode(fixtures.privateKey)
|
||||
expect(decoded.Type).to.eql(keysPBM.KeyType.Secp256k1)
|
||||
|
||||
const key = await secp256k1.unmarshalSecp256k1PrivateKey(decoded.Data)
|
||||
const sig = await key.sign(fixtures.message)
|
||||
expect(sig).to.eql(fixtures.signature)
|
||||
})
|
||||
})
|
||||
|
3
test/node.js
Normal file
3
test/node.js
Normal file
@@ -0,0 +1,3 @@
|
||||
'use strict'
|
||||
|
||||
require('./keys/rsa-crypto-libs')
|
27
test/random-bytes.spec.js
Normal file
27
test/random-bytes.spec.js
Normal 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')
|
||||
})
|
||||
})
|
@@ -6,26 +6,33 @@ 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')
|
||||
const BN = require('bn.js')
|
||||
|
||||
describe('Util', () => {
|
||||
let bn
|
||||
|
||||
before((done) => {
|
||||
bn = new BN('dead', 16)
|
||||
done()
|
||||
before(() => {
|
||||
bn = new forge.jsbn.BigInteger('dead', 16)
|
||||
})
|
||||
|
||||
it('toBase64', (done) => {
|
||||
expect(util.toBase64(bn)).to.eql('3q0')
|
||||
done()
|
||||
it('should convert BigInteger to a uint base64url encoded string', () => {
|
||||
expect(util.bigIntegerToUintBase64url(bn)).to.eql('3q0')
|
||||
})
|
||||
|
||||
it('toBase64 zero padding', (done) => {
|
||||
let bnpad = new BN('ff', 16)
|
||||
expect(util.toBase64(bnpad, 2)).to.eql('AP8')
|
||||
done()
|
||||
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 Uint8Array with padding', () => {
|
||||
const buf = util.base64urlToBuffer('AP8', 2)
|
||||
expect(Uint8Array.from([0, 255])).to.eql(buf)
|
||||
})
|
||||
})
|
||||
|
21
test/util/index.js
Normal file
21
test/util/index.js
Normal 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): Promise<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
30
tsconfig.json
Normal 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"]
|
||||
}
|
Reference in New Issue
Block a user