mirror of
https://github.com/fluencelabs/js-libp2p-crypto
synced 2025-07-25 23:02:08 +00:00
Compare commits
45 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
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 | ||
|
5cd0e8cc1a | ||
|
0ffe31821a | ||
|
7b3625888c | ||
|
b7bce77ad5 | ||
|
c1f867bd9c | ||
|
e3f02eb6f1 | ||
|
a0874389a9 | ||
|
567d68c855 | ||
|
af782c5906 | ||
|
f0593c9e6d | ||
|
8d8294dc3f | ||
|
df23d634c5 | ||
|
88e1bcf75f | ||
|
c54ea206f0 | ||
|
857d2bd902 | ||
|
200110cb9d | ||
|
9e5778694c | ||
|
87e8f1c86f | ||
|
df75980a88 |
3
.aegir.js
Normal file
3
.aegir.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
module.exports = {
|
||||||
|
bundlesize: { maxSize: '155kB' }
|
||||||
|
}
|
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
|
|
63
.travis.yml
63
.travis.yml
@@ -1,32 +1,45 @@
|
|||||||
# Warning: This file is automatically synced from https://github.com/ipfs/ci-sync so if you want to change it, please change it there and ask someone to sync all repositories.
|
|
||||||
sudo: false
|
|
||||||
language: node_js
|
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:
|
include:
|
||||||
- node_js: 6
|
- stage: check
|
||||||
env: CXX=g++-4.8
|
script:
|
||||||
- node_js: 8
|
- npx aegir build --bundlesize
|
||||||
env: CXX=g++-4.8
|
- npx aegir dep-check
|
||||||
# - node_js: stable
|
|
||||||
# env: CXX=g++-4.8
|
|
||||||
|
|
||||||
script:
|
|
||||||
- npm run lint
|
- npm run lint
|
||||||
- npm run test
|
|
||||||
- npm run coverage
|
|
||||||
|
|
||||||
before_script:
|
- stage: test
|
||||||
- export DISPLAY=:99.0
|
name: chrome
|
||||||
- sh -e /etc/init.d/xvfb start
|
addons:
|
||||||
|
chrome: stable
|
||||||
|
script:
|
||||||
|
- npx aegir test -t browser
|
||||||
|
|
||||||
after_success:
|
- stage: test
|
||||||
- npm run coverage-publish
|
name: firefox
|
||||||
|
addons:
|
||||||
|
firefox: latest
|
||||||
|
script:
|
||||||
|
- npx aegir test -t browser -- --browsers FirefoxHeadless
|
||||||
|
|
||||||
addons:
|
notifications:
|
||||||
firefox: 'latest'
|
email: false
|
||||||
apt:
|
|
||||||
sources:
|
|
||||||
- ubuntu-toolchain-r-test
|
|
||||||
packages:
|
|
||||||
- g++-4.8
|
|
||||||
|
94
CHANGELOG.md
94
CHANGELOG.md
@@ -1,3 +1,97 @@
|
|||||||
|
<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>
|
<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)
|
# [0.14.0](https://github.com/libp2p/js-libp2p-crypto/compare/v0.13.0...v0.14.0) (2018-09-17)
|
||||||
|
|
||||||
|
208
README.md
208
README.md
@@ -1,16 +1,13 @@
|
|||||||
# js-libp2p-crypto
|
# js-libp2p-crypto
|
||||||
|
|
||||||
[](http://ipn.io)
|
[](http://protocol.ai)
|
||||||
[](http://ipfs.io/)
|
[](http://libp2p.io/)
|
||||||
[](http://webchat.freenode.net/?channels=%23ipfs)
|
[](http://webchat.freenode.net/?channels=%23libp2p)
|
||||||
[](https://github.com/RichardLitt/standard-readme)
|
[](https://discuss.libp2p.io)
|
||||||
[](https://coveralls.io/github/libp2p/js-libp2p-crypto?branch=master)
|
[](https://codecov.io/gh/libp2p/js-libp2p-crypto)
|
||||||
[](https://travis-ci.org/libp2p/js-libp2p-crypto)
|
[](https://travis-ci.com/libp2p/js-libp2p-crypto)
|
||||||
[](https://circleci.com/gh/libp2p/js-libp2p-crypto)
|
|
||||||
[](https://david-dm.org/libp2p/js-libp2p-crypto)
|
[](https://david-dm.org/libp2p/js-libp2p-crypto)
|
||||||
[](https://github.com/feross/standard)
|
[](https://github.com/feross/standard)
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
> Crypto primitives for libp2p in JavaScript
|
> Crypto primitives for libp2p in JavaScript
|
||||||
|
|
||||||
@@ -18,31 +15,35 @@ This repo contains the JavaScript implementation of the crypto primitives needed
|
|||||||
|
|
||||||
## Lead Maintainer
|
## Lead Maintainer
|
||||||
|
|
||||||
[Friedel Ziegelmayer](https://github.com/dignifiedquire/)
|
[Jacob Heun](https://github.com/jacobheun/)
|
||||||
|
|
||||||
## Table of Contents
|
## Table of Contents
|
||||||
|
|
||||||
- [Install](#install)
|
- [js-libp2p-crypto](#js-libp2p-crypto)
|
||||||
- [API](#api)
|
- [Lead Maintainer](#Lead-Maintainer)
|
||||||
- [`crypto.hmac`](#hmac)
|
- [Table of Contents](#Table-of-Contents)
|
||||||
- [`create(hash, secret, callback)`](#createhash-secret-callback)
|
- [Install](#Install)
|
||||||
- [`digest(data, callback)`](#digestdata-callback)
|
- [API](#API)
|
||||||
- [`crypto.aes`](#aes)
|
- [`crypto.aes`](#cryptoaes)
|
||||||
- [`create(key, iv, callback)`](#createkey-iv-callback)
|
- [`crypto.aes.create(key, iv)`](#cryptoaescreatekey-iv)
|
||||||
- [`encrypt(data, callback)`](#encryptdata-callback)
|
- [`decrypt(data)`](#decryptdata)
|
||||||
- [`decrypt(data, callback)`](#decryptdata-callback)
|
- [`encrypt(data)`](#encryptdata)
|
||||||
- [`keys`](#keys)
|
- [`crypto.hmac`](#cryptohmac)
|
||||||
- [`generateKeyPair(type, bits, callback)`](#generatekeypairtype-bits-callback)
|
- [`crypto.hmac.create(hash, secret)`](#cryptohmaccreatehash-secret)
|
||||||
- [`generateEphemeralKeyPair(curve, callback)`](#generateephemeralkeypaircurve-callback)
|
- [`digest(data)`](#digestdata)
|
||||||
- [`keyStretcher(cipherType, hashType, secret, callback)`](#keystretcherciphertype-hashtype-secret-callback)
|
- [`crypto.keys`](#cryptokeys)
|
||||||
- [`marshalPublicKey(key[, type], callback)`](#marshalpublickeykey-type-callback)
|
- [`crypto.keys.generateKeyPair(type, bits)`](#cryptokeysgenerateKeyPairtype-bits)
|
||||||
- [`unmarshalPublicKey(buf)`](#unmarshalpublickeybuf)
|
- [`crypto.keys.generateEphemeralKeyPair(curve)`](#cryptokeysgenerateEphemeralKeyPaircurve)
|
||||||
- [`marshalPrivateKey(key[, type])`](#marshalprivatekeykey-type)
|
- [`crypto.keys.keyStretcher(cipherType, hashType, secret)`](#cryptokeyskeyStretchercipherType-hashType-secret)
|
||||||
- [`unmarshalPrivateKey(buf, callback)`](#unmarshalprivatekeybuf-callback)
|
- [`crypto.keys.marshalPublicKey(key, [type])`](#cryptokeysmarshalPublicKeykey-type)
|
||||||
- [`import(pem, password, callback)`](#importpem-password-callback)
|
- [`crypto.keys.unmarshalPublicKey(buf)`](#cryptokeysunmarshalPublicKeybuf)
|
||||||
- [`webcrypto`](#webcrypto)
|
- [`crypto.keys.marshalPrivateKey(key, [type])`](#cryptokeysmarshalPrivateKeykey-type)
|
||||||
- [Contribute](#contribute)
|
- [`crypto.keys.unmarshalPrivateKey(buf)`](#cryptokeysunmarshalPrivateKeybuf)
|
||||||
- [License](#license)
|
- [`crypto.keys.import(pem, password)`](#cryptokeysimportpem-password)
|
||||||
|
- [`crypto.randomBytes(number)`](#cryptorandomBytesnumber)
|
||||||
|
- [`crypto.pbkdf2(password, salt, iterations, keySize, hash)`](#cryptopbkdf2password-salt-iterations-keySize-hash)
|
||||||
|
- [Contribute](#Contribute)
|
||||||
|
- [License](#License)
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
@@ -50,138 +51,150 @@ This repo contains the JavaScript implementation of the crypto primitives needed
|
|||||||
npm install --save libp2p-crypto
|
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
|
## API
|
||||||
|
|
||||||
### `crypto.aes`
|
### `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.
|
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.
|
- `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`.
|
- `iv: Buffer` Must have length `16`.
|
||||||
- `callback: Function`
|
|
||||||
|
|
||||||
##### `decrypt(data, callback)`
|
Returns `Promise<{decrypt<Function>, encrypt<Function>}>`
|
||||||
|
|
||||||
|
##### `decrypt(data)`
|
||||||
|
|
||||||
- `data: Buffer`
|
- `data: Buffer`
|
||||||
- `callback: Function`
|
|
||||||
|
|
||||||
##### `encrypt(data, callback)`
|
Returns `Promise<Buffer>`
|
||||||
|
|
||||||
|
##### `encrypt(data)`
|
||||||
|
|
||||||
- `data: Buffer`
|
- `data: Buffer`
|
||||||
- `callback: Function`
|
|
||||||
|
Returns `Promise<Buffer>`
|
||||||
|
|
||||||
```js
|
```js
|
||||||
var crypto = require('libp2p-crypto')
|
const crypto = require('libp2p-crypto')
|
||||||
|
|
||||||
// Setting up Key and IV
|
// Setting up Key and IV
|
||||||
|
|
||||||
// A 16 bytes array, 128 Bits, AES-128 is chosen
|
// A 16 bytes array, 128 Bits, AES-128 is chosen
|
||||||
var key128 = Buffer.from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15])
|
const key128 = Buffer.from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15])
|
||||||
|
|
||||||
// A 16 bytes array, 128 Bits,
|
// A 16 bytes array, 128 Bits,
|
||||||
var IV = Buffer.from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15])
|
const IV = Buffer.from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15])
|
||||||
|
|
||||||
async function main () {
|
async function main () {
|
||||||
let decryptedMessage = 'Hello, world!'
|
const decryptedMessage = 'Hello, world!'
|
||||||
let encryptedMessage
|
|
||||||
|
|
||||||
// Encrypting
|
// Encrypting
|
||||||
await crypto.aes.create(key128, IV, (err, cipher) => {
|
const cipher = await crypto.aes.create(key128, IV)
|
||||||
if (!err) {
|
const encryptedBuffer = await cipher.encrypt(Buffer.from(decryptedMessage))
|
||||||
cipher.encrypt(Buffer.from(decryptedMessage), (err, encryptedBuffer) => {
|
|
||||||
if (!err) {
|
|
||||||
console.log(encryptedBuffer)
|
console.log(encryptedBuffer)
|
||||||
// prints: <Buffer 42 f1 67 d9 2e 42 d0 32 9e b1 f8 3c>
|
// prints: <Buffer 42 f1 67 d9 2e 42 d0 32 9e b1 f8 3c>
|
||||||
encryptedMessage = encryptedBuffer
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Decrypting
|
// Decrypting
|
||||||
await crypto.aes.create(key128, IV, (err, cipher) => {
|
const decipher = await crypto.aes.create(key128, IV)
|
||||||
if (!err) {
|
const decryptedBuffer = await cipher.decrypt(encryptedBuffer)
|
||||||
cipher.decrypt(encryptedMessage, (err, decryptedBuffer) => {
|
|
||||||
if (!err) {
|
|
||||||
console.log(decryptedBuffer)
|
console.log(decryptedBuffer)
|
||||||
// prints: <Buffer 42 f1 67 d9 2e 42 d0 32 9e b1 f8 3c>
|
// prints: <Buffer 42 f1 67 d9 2e 42 d0 32 9e b1 f8 3c>
|
||||||
|
|
||||||
console.log(decryptedBuffer.toString('utf-8'))
|
console.log(decryptedBuffer.toString('utf-8'))
|
||||||
// prints: Hello, world!
|
// prints: Hello, world!
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
main()
|
|
||||||
|
|
||||||
|
main()
|
||||||
```
|
```
|
||||||
|
|
||||||
### `crypto.hmac`
|
### `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.
|
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`
|
- `hash: String`
|
||||||
- `secret: Buffer`
|
- `secret: Buffer`
|
||||||
- `callback: Function`
|
|
||||||
|
|
||||||
##### `digest(data, callback)`
|
Returns `Promise<{digest<Function>}>`
|
||||||
|
|
||||||
|
##### `digest(data)`
|
||||||
|
|
||||||
- `data: Buffer`
|
- `data: Buffer`
|
||||||
- `callback: Function`
|
|
||||||
|
Returns `Promise<Buffer>`
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
var crypto = require('libp2p-crypto')
|
const crypto = require('libp2p-crypto')
|
||||||
|
|
||||||
let hash = 'SHA1' // 'SHA256' || 'SHA512'
|
async function main () {
|
||||||
|
const hash = 'SHA1' // 'SHA256' || 'SHA512'
|
||||||
crypto.hmac.create(hash, Buffer.from('secret'), (err, hmac) => {
|
const hmac = await crypto.hmac.create(hash, Buffer.from('secret'))
|
||||||
if (!err) {
|
const sig = await hmac.digest(Buffer.from('hello world'))
|
||||||
hmac.digest(Buffer.from('hello world'), (err, sig) => {
|
|
||||||
if (!err) {
|
|
||||||
console.log(sig)
|
console.log(sig)
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
main()
|
||||||
})
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### `crypto.keys`
|
### `crypto.keys`
|
||||||
|
|
||||||
**Supported Key Types**
|
**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'` and `'ed25519'` types are supported, although ed25519 keys support only signing and verification of messages. For encryption / decryption support, RSA keys should be used.
|
||||||
|
|
||||||
Installing the [libp2p-crypto-secp256k1](https://github.com/libp2p/js-libp2p-crypto-secp256k1) module adds support for the `'secp256k1'` type, which supports ECDSA signatures using the secp256k1 elliptic curve popularized by Bitcoin. This module is not installed by default, and should be explicitly depended on if your project requires secp256k1 support.
|
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.
|
- `type: String`, see [Supported Key Types](#supported-key-types) above.
|
||||||
- `bits: Number` Minimum of 1024
|
- `bits: Number` Minimum of 1024
|
||||||
- `callback: Function`
|
|
||||||
|
Returns `Promise<{privateKey<Buffer>, publicKey<Buffer>}>`
|
||||||
|
|
||||||
Generates a keypair of the given type and bitsize.
|
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
|
- `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.
|
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.
|
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
|
```js
|
||||||
{
|
{
|
||||||
@@ -190,16 +203,17 @@ Calls back with an object of the form
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### `crypto.keys.keyStretcher(cipherType, hashType, secret, callback)`
|
### `crypto.keys.keyStretcher(cipherType, hashType, secret)`
|
||||||
|
|
||||||
- `cipherType: String`, one of `'AES-128'`, `'AES-256'`, `'Blowfish'`
|
- `cipherType: String`, one of `'AES-128'`, `'AES-256'`, `'Blowfish'`
|
||||||
- `hashType: String`, one of `'SHA1'`, `SHA256`, `SHA512`
|
- `hashType: String`, one of `'SHA1'`, `SHA256`, `SHA512`
|
||||||
- `secret: Buffer`
|
- `secret: Buffer`
|
||||||
- `callback: Function`
|
|
||||||
|
Returns `Promise`
|
||||||
|
|
||||||
Generates a set of keys for each party by stretching the shared key.
|
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
|
```js
|
||||||
{
|
{
|
||||||
@@ -216,10 +230,12 @@ Calls back with an object of the form:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### `crypto.keys.marshalPublicKey(key[, type], callback)`
|
### `crypto.keys.marshalPublicKey(key, [type])`
|
||||||
|
|
||||||
- `key: keys.rsa.RsaPublicKey | keys.ed25519.Ed25519PublicKey | require('libp2p-crypto-secp256k1').Secp256k1PublicKey`
|
- `key: keys.rsa.RsaPublicKey | keys.ed25519.Ed25519PublicKey | require('libp2p-crypto-secp256k1').Secp256k1PublicKey`
|
||||||
- `type: String`, see [Supported Key Types](#supported-key-types) above.
|
- `type: String`, see [Supported Key Types](#supported-key-types) above. Defaults to 'rsa'.
|
||||||
|
|
||||||
|
Returns `Buffer`
|
||||||
|
|
||||||
Converts a public key object into a protobuf serialized public key.
|
Converts a public key object into a protobuf serialized public key.
|
||||||
|
|
||||||
@@ -227,27 +243,33 @@ Converts a public key object into a protobuf serialized public key.
|
|||||||
|
|
||||||
- `buf: Buffer`
|
- `buf: Buffer`
|
||||||
|
|
||||||
|
Returns `RsaPublicKey|Ed25519PublicKey|Secp256k1PublicKey`
|
||||||
|
|
||||||
Converts a protobuf serialized public key into its representative object.
|
Converts a protobuf serialized public key into its representative object.
|
||||||
|
|
||||||
### `crypto.keys.marshalPrivateKey(key[, type])`
|
### `crypto.keys.marshalPrivateKey(key, [type])`
|
||||||
|
|
||||||
- `key: keys.rsa.RsaPrivateKey | keys.ed25519.Ed25519PrivateKey | require('libp2p-crypto-secp256k1').Secp256k1PrivateKey`
|
- `key: keys.rsa.RsaPrivateKey | keys.ed25519.Ed25519PrivateKey | require('libp2p-crypto-secp256k1').Secp256k1PrivateKey`
|
||||||
- `type: String`, see [Supported Key Types](#supported-key-types) above.
|
- `type: String`, see [Supported Key Types](#supported-key-types) above.
|
||||||
|
|
||||||
|
Returns `Buffer`
|
||||||
|
|
||||||
Converts a private key object into a protobuf serialized private key.
|
Converts a private key object into a protobuf serialized private key.
|
||||||
|
|
||||||
### `crypto.keys.unmarshalPrivateKey(buf, callback)`
|
### `crypto.keys.unmarshalPrivateKey(buf)`
|
||||||
|
|
||||||
- `buf: Buffer`
|
- `buf: Buffer`
|
||||||
- `callback: Function`
|
|
||||||
|
Returns `Promise<RsaPrivateKey|Ed25519PrivateKey|Secp256k1PrivateKey>`
|
||||||
|
|
||||||
Converts a protobuf serialized private key into its representative object.
|
Converts a protobuf serialized private key into its representative object.
|
||||||
|
|
||||||
### `crypto.keys.import(pem, password, callback)`
|
### `crypto.keys.import(pem, password)`
|
||||||
|
|
||||||
- `pem: string`
|
- `pem: string`
|
||||||
- `password: string`
|
- `password: string`
|
||||||
- `callback: Function`
|
|
||||||
|
Returns `Promise<RsaPrivateKey>`
|
||||||
|
|
||||||
Converts a PEM password protected private key into its representative object.
|
Converts a PEM password protected private key into its representative object.
|
||||||
|
|
||||||
@@ -255,6 +277,8 @@ Converts a PEM password protected private key into its representative object.
|
|||||||
|
|
||||||
- `number: Number`
|
- `number: Number`
|
||||||
|
|
||||||
|
Returns `Buffer`
|
||||||
|
|
||||||
Generates a Buffer with length `number` populated by random bytes.
|
Generates a Buffer with length `number` populated by random bytes.
|
||||||
|
|
||||||
### `crypto.pbkdf2(password, salt, iterations, keySize, hash)`
|
### `crypto.pbkdf2(password, salt, iterations, keySize, hash)`
|
||||||
|
29
appveyor.yml
29
appveyor.yml
@@ -1,29 +0,0 @@
|
|||||||
# Warning: This file is automatically synced from https://github.com/ipfs/ci-sync so if you want to change it, please change it there and ask someone to sync all repositories.
|
|
||||||
version: "{build}"
|
|
||||||
|
|
||||||
environment:
|
|
||||||
matrix:
|
|
||||||
- nodejs_version: "6"
|
|
||||||
- nodejs_version: "8"
|
|
||||||
|
|
||||||
matrix:
|
|
||||||
fast_finish: true
|
|
||||||
|
|
||||||
install:
|
|
||||||
# Install Node.js
|
|
||||||
- ps: Install-Product node $env:nodejs_version
|
|
||||||
|
|
||||||
# Upgrade npm
|
|
||||||
- npm install -g npm
|
|
||||||
|
|
||||||
# Output our current versions for debugging
|
|
||||||
- node --version
|
|
||||||
- npm --version
|
|
||||||
|
|
||||||
# Install our package dependencies
|
|
||||||
- npm install
|
|
||||||
|
|
||||||
test_script:
|
|
||||||
- npm run test:node
|
|
||||||
|
|
||||||
build: off
|
|
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable no-console */
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
const Benchmark = require('benchmark')
|
const Benchmark = require('benchmark')
|
||||||
@@ -9,19 +10,14 @@ const secrets = []
|
|||||||
const curves = ['P-256', 'P-384', 'P-521']
|
const curves = ['P-256', 'P-384', 'P-521']
|
||||||
|
|
||||||
curves.forEach((curve) => {
|
curves.forEach((curve) => {
|
||||||
suite.add(`ephemeral key with secrect ${curve}`, (d) => {
|
suite.add(`ephemeral key with secrect ${curve}`, async (d) => {
|
||||||
crypto.keys.generateEphemeralKeyPair('P-256', (err, res) => {
|
const res = await crypto.keys.generateEphemeralKeyPair('P-256')
|
||||||
if (err) { throw err }
|
const secret = await res.genSharedKey(res.key)
|
||||||
res.genSharedKey(res.key, (err, secret) => {
|
|
||||||
if (err) { throw err }
|
|
||||||
secrets.push(secret)
|
secrets.push(secret)
|
||||||
|
|
||||||
d.resolve()
|
d.resolve()
|
||||||
})
|
|
||||||
})
|
|
||||||
}, { defer: true })
|
}, { defer: true })
|
||||||
})
|
})
|
||||||
|
|
||||||
suite
|
suite
|
||||||
.on('cycle', (event) => console.log(String(event.target)))
|
.on('cycle', (event) => console.log(String(event.target)))
|
||||||
.run({async: true})
|
.run({ async: true })
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
|
/* eslint-disable no-console */
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
const Benchmark = require('benchmark')
|
const Benchmark = require('benchmark')
|
||||||
const async = require('async')
|
|
||||||
|
|
||||||
const crypto = require('../src')
|
const crypto = require('../src')
|
||||||
|
|
||||||
@@ -12,11 +12,9 @@ const keys = []
|
|||||||
const ciphers = ['AES-128', 'AES-256', 'Blowfish']
|
const ciphers = ['AES-128', 'AES-256', 'Blowfish']
|
||||||
const hashes = ['SHA1', 'SHA256', 'SHA512']
|
const hashes = ['SHA1', 'SHA256', 'SHA512']
|
||||||
|
|
||||||
async.waterfall([
|
;(async () => {
|
||||||
(cb) => crypto.keys.generateEphemeralKeyPair('P-256', cb),
|
const res = await crypto.keys.generateEphemeralKeyPair('P-256')
|
||||||
(res, cb) => res.genSharedKey(res.key, cb)
|
const secret = await res.genSharedKey(res.key)
|
||||||
], (err, secret) => {
|
|
||||||
if (err) { throw err }
|
|
||||||
|
|
||||||
ciphers.forEach((cipher) => hashes.forEach((hash) => {
|
ciphers.forEach((cipher) => hashes.forEach((hash) => {
|
||||||
setup(cipher, hash, secret)
|
setup(cipher, hash, secret)
|
||||||
@@ -24,16 +22,13 @@ async.waterfall([
|
|||||||
|
|
||||||
suite
|
suite
|
||||||
.on('cycle', (event) => console.log(String(event.target)))
|
.on('cycle', (event) => console.log(String(event.target)))
|
||||||
.run({async: true})
|
.run({ async: true })
|
||||||
})
|
})()
|
||||||
|
|
||||||
function setup (cipher, hash, secret) {
|
function setup (cipher, hash, secret) {
|
||||||
suite.add(`keyStretcher ${cipher} ${hash}`, (d) => {
|
suite.add(`keyStretcher ${cipher} ${hash}`, async (d) => {
|
||||||
crypto.keys.keyStretcher(cipher, hash, secret, (err, k) => {
|
const k = await crypto.keys.keyStretcher(cipher, hash, secret)
|
||||||
if (err) { throw err }
|
|
||||||
|
|
||||||
keys.push(k)
|
keys.push(k)
|
||||||
d.resolve()
|
d.resolve()
|
||||||
})
|
|
||||||
}, { defer: true })
|
}, { defer: true })
|
||||||
}
|
}
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable no-console */
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
const Benchmark = require('benchmark')
|
const Benchmark = require('benchmark')
|
||||||
@@ -9,34 +10,28 @@ const keys = []
|
|||||||
const bits = [1024, 2048, 4096]
|
const bits = [1024, 2048, 4096]
|
||||||
|
|
||||||
bits.forEach((bit) => {
|
bits.forEach((bit) => {
|
||||||
suite.add(`generateKeyPair ${bit}bits`, (d) => {
|
suite.add(`generateKeyPair ${bit}bits`, async (d) => {
|
||||||
crypto.keys.generateKeyPair('RSA', bit, (err, key) => {
|
const key = await crypto.keys.generateKeyPair('RSA', bit)
|
||||||
if (err) { throw err }
|
|
||||||
keys.push(key)
|
keys.push(key)
|
||||||
d.resolve()
|
d.resolve()
|
||||||
})
|
|
||||||
}, {
|
}, {
|
||||||
defer: true
|
defer: true
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
suite.add('sign and verify', (d) => {
|
suite.add('sign and verify', async (d) => {
|
||||||
const key = keys[0]
|
const key = keys[0]
|
||||||
const text = key.genSecret()
|
const text = key.genSecret()
|
||||||
|
|
||||||
key.sign(text, (err, sig) => {
|
const sig = await key.sign(text)
|
||||||
if (err) { throw err }
|
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') }
|
if (res !== true) { throw new Error('failed to verify') }
|
||||||
d.resolve()
|
d.resolve()
|
||||||
})
|
|
||||||
})
|
|
||||||
}, {
|
}, {
|
||||||
defer: true
|
defer: true
|
||||||
})
|
})
|
||||||
|
|
||||||
suite
|
suite
|
||||||
.on('cycle', (event) => console.log(String(event.target)))
|
.on('cycle', (event) => console.log(String(event.target)))
|
||||||
.run({async: true})
|
.run({ async: true })
|
||||||
|
2
ci/Jenkinsfile
vendored
2
ci/Jenkinsfile
vendored
@@ -1,2 +0,0 @@
|
|||||||
// Warning: This file is automatically synced from https://github.com/ipfs/ci-sync so if you want to change it, please change it there and ask someone to sync all repositories.
|
|
||||||
javascript()
|
|
15
circle.yml
15
circle.yml
@@ -1,15 +0,0 @@
|
|||||||
# Warning: This file is automatically synced from https://github.com/ipfs/ci-sync so if you want to change it, please change it there and ask someone to sync all repositories.
|
|
||||||
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
|
|
51
package.json
51
package.json
@@ -1,15 +1,19 @@
|
|||||||
{
|
{
|
||||||
"name": "libp2p-crypto",
|
"name": "libp2p-crypto",
|
||||||
"version": "0.14.0",
|
"version": "0.17.1",
|
||||||
"description": "Crypto primitives for libp2p",
|
"description": "Crypto primitives for libp2p",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"leadMaintainer": "Friedel Ziegelmayer <dignifiedquire@gmail.com>",
|
"leadMaintainer": "Jacob Heun <jacobheun@gmail.com>",
|
||||||
"browser": {
|
"browser": {
|
||||||
"./src/hmac/index.js": "./src/hmac/index-browser.js",
|
"./src/hmac/index.js": "./src/hmac/index-browser.js",
|
||||||
"./src/keys/ecdh.js": "./src/keys/ecdh-browser.js",
|
"./src/keys/ecdh.js": "./src/keys/ecdh-browser.js",
|
||||||
"./src/aes/ciphers.js": "./src/aes/ciphers-browser.js",
|
"./src/aes/ciphers.js": "./src/aes/ciphers-browser.js",
|
||||||
"./src/keys/rsa.js": "./src/keys/rsa-browser.js"
|
"./src/keys/rsa.js": "./src/keys/rsa-browser.js"
|
||||||
},
|
},
|
||||||
|
"files": [
|
||||||
|
"src",
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint": "aegir lint",
|
"lint": "aegir lint",
|
||||||
"build": "aegir build",
|
"build": "aegir build",
|
||||||
@@ -20,7 +24,8 @@
|
|||||||
"release": "aegir release",
|
"release": "aegir release",
|
||||||
"release-minor": "aegir release --type minor",
|
"release-minor": "aegir release --type minor",
|
||||||
"release-major": "aegir release --type major",
|
"release-major": "aegir release --type major",
|
||||||
"coverage": "aegir coverage --ignore src/keys/keys.proto.js"
|
"coverage": "aegir coverage --ignore src/keys/keys.proto.js",
|
||||||
|
"size": "aegir build --bundlesize"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"IPFS",
|
"IPFS",
|
||||||
@@ -30,31 +35,34 @@
|
|||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"asn1.js": "^5.0.1",
|
"asmcrypto.js": "^2.3.2",
|
||||||
"async": "^2.6.1",
|
"asn1.js": "^5.2.0",
|
||||||
|
"bn.js": "^5.0.0",
|
||||||
"browserify-aes": "^1.2.0",
|
"browserify-aes": "^1.2.0",
|
||||||
"bs58": "^4.0.1",
|
"bs58": "^4.0.1",
|
||||||
|
"err-code": "^1.1.2",
|
||||||
|
"iso-random-stream": "^1.1.0",
|
||||||
"keypair": "^1.0.1",
|
"keypair": "^1.0.1",
|
||||||
"libp2p-crypto-secp256k1": "~0.2.2",
|
"libp2p-crypto-secp256k1": "~0.4.0",
|
||||||
"multihashing-async": "~0.5.1",
|
"multihashing-async": "~0.8.0",
|
||||||
"node-forge": "~0.7.6",
|
"node-forge": "~0.9.1",
|
||||||
"pem-jwk": "^1.5.1",
|
"pem-jwk": "^2.0.0",
|
||||||
"protons": "^1.0.1",
|
"protons": "^1.0.1",
|
||||||
"rsa-pem-to-jwk": "^1.1.3",
|
"rsa-pem-to-jwk": "^1.1.3",
|
||||||
"tweetnacl": "^1.0.0",
|
"tweetnacl": "^1.0.1",
|
||||||
"ursa-optional": "~0.9.8",
|
"ursa-optional": "~0.10.1"
|
||||||
"webcrypto-shim": "github:dignifiedquire/webcrypto-shim#master"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"aegir": "^15.2.0",
|
"aegir": "^20.4.1",
|
||||||
"benchmark": "^2.1.4",
|
"benchmark": "^2.1.4",
|
||||||
"chai": "^4.1.2",
|
"chai": "^4.2.0",
|
||||||
"chai-string": "^1.5.0",
|
"chai-string": "^1.5.0",
|
||||||
"dirty-chai": "^2.0.1"
|
"dirty-chai": "^2.0.1",
|
||||||
|
"sinon": "^7.5.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.0.0",
|
"node": ">=10.0.0",
|
||||||
"npm": ">=3.0.0"
|
"npm": ">=6.0.0"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@@ -65,18 +73,27 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/libp2p/js-libp2p-crypto",
|
"homepage": "https://github.com/libp2p/js-libp2p-crypto",
|
||||||
"contributors": [
|
"contributors": [
|
||||||
|
"Alan Shaw <alan.shaw@protocol.ai>",
|
||||||
|
"Alan Shaw <alan@tableflip.io>",
|
||||||
|
"Alberto Elias <hi@albertoelias.me>",
|
||||||
|
"Arve Knudsen <arve.knudsen@gmail.com>",
|
||||||
"David Dias <daviddias.p@gmail.com>",
|
"David Dias <daviddias.p@gmail.com>",
|
||||||
"Dmitriy Ryajov <dryajov@gmail.com>",
|
"Dmitriy Ryajov <dryajov@gmail.com>",
|
||||||
"Friedel Ziegelmayer <dignifiedquire@gmail.com>",
|
"Friedel Ziegelmayer <dignifiedquire@gmail.com>",
|
||||||
"Greenkeeper <support@greenkeeper.io>",
|
"Greenkeeper <support@greenkeeper.io>",
|
||||||
|
"Hugo Dias <hugomrdias@gmail.com>",
|
||||||
"Jack Kleeman <jackkleeman@gmail.com>",
|
"Jack Kleeman <jackkleeman@gmail.com>",
|
||||||
|
"Jacob Heun <jacobheun@gmail.com>",
|
||||||
"Joao Santos <jrmsantos15@gmail.com>",
|
"Joao Santos <jrmsantos15@gmail.com>",
|
||||||
"Maciej Krüger <mkg20001@gmail.com>",
|
"Maciej Krüger <mkg20001@gmail.com>",
|
||||||
"Richard Littauer <richard.littauer@gmail.com>",
|
"Richard Littauer <richard.littauer@gmail.com>",
|
||||||
"Richard Schneider <makaretu@gmail.com>",
|
"Richard Schneider <makaretu@gmail.com>",
|
||||||
"Tom Swindell <t.swindell@rubyx.co.uk>",
|
"Tom Swindell <t.swindell@rubyx.co.uk>",
|
||||||
|
"Vasco Santos <vasco.santos@ua.pt>",
|
||||||
"Victor Bjelkholm <victorbjelkholm@gmail.com>",
|
"Victor Bjelkholm <victorbjelkholm@gmail.com>",
|
||||||
"Yusef Napora <yusef@napora.org>",
|
"Yusef Napora <yusef@napora.org>",
|
||||||
|
"dignifiedquire <dignifiedquire@users.noreply.github.com>",
|
||||||
|
"dirkmc <dirkmdev@gmail.com>",
|
||||||
"greenkeeper[bot] <greenkeeper[bot]@users.noreply.github.com>",
|
"greenkeeper[bot] <greenkeeper[bot]@users.noreply.github.com>",
|
||||||
"nikuda <nikuda@gmail.com>"
|
"nikuda <nikuda@gmail.com>"
|
||||||
]
|
]
|
||||||
|
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,14 +1,11 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
const asm = require('asmcrypto.js')
|
const asm = require('asmcrypto.js')
|
||||||
const setImmediate = require('async/setImmediate')
|
const validateCipherMode = require('./cipher-mode')
|
||||||
|
|
||||||
exports.create = function (key, iv, callback) {
|
exports.create = async function (key, iv) { // eslint-disable-line require-await
|
||||||
const done = (err, res) => setImmediate(() => callback(err, res))
|
// Throws an error if mode is invalid
|
||||||
|
validateCipherMode(key)
|
||||||
if (key.length !== 16 && key.length !== 32) {
|
|
||||||
return done(new Error('Invalid key length'))
|
|
||||||
}
|
|
||||||
|
|
||||||
const enc = new asm.AES_CTR.Encrypt({
|
const enc = new asm.AES_CTR.Encrypt({
|
||||||
key: key,
|
key: key,
|
||||||
@@ -20,36 +17,18 @@ exports.create = function (key, iv, callback) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const res = {
|
const res = {
|
||||||
encrypt (data, cb) {
|
async encrypt (data) { // eslint-disable-line require-await
|
||||||
const done = (err, res) => setImmediate(() => cb(err, res))
|
return Buffer.from(
|
||||||
|
|
||||||
let res
|
|
||||||
try {
|
|
||||||
res = Buffer.from(
|
|
||||||
enc.process(data).result
|
enc.process(data).result
|
||||||
)
|
)
|
||||||
} catch (err) {
|
|
||||||
return done(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
done(null, res)
|
|
||||||
},
|
},
|
||||||
|
|
||||||
decrypt (data, cb) {
|
async decrypt (data) { // eslint-disable-line require-await
|
||||||
const done = (err, res) => setImmediate(() => cb(err, res))
|
return Buffer.from(
|
||||||
|
|
||||||
let res
|
|
||||||
try {
|
|
||||||
res = Buffer.from(
|
|
||||||
dec.process(data).result
|
dec.process(data).result
|
||||||
)
|
)
|
||||||
} catch (err) {
|
|
||||||
return done(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
done(null, res)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
done(null, res)
|
return res
|
||||||
}
|
}
|
||||||
|
@@ -1,30 +1,22 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
const ciphers = require('./ciphers')
|
const ciphers = require('./ciphers')
|
||||||
|
const cipherMode = require('./cipher-mode')
|
||||||
|
|
||||||
const CIPHER_MODES = {
|
exports.create = async function (key, iv) { // eslint-disable-line require-await
|
||||||
16: 'aes-128-ctr',
|
const mode = cipherMode(key)
|
||||||
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'))
|
|
||||||
}
|
|
||||||
|
|
||||||
const cipher = ciphers.createCipheriv(mode, key, iv)
|
const cipher = ciphers.createCipheriv(mode, key, iv)
|
||||||
const decipher = ciphers.createDecipheriv(mode, key, iv)
|
const decipher = ciphers.createDecipheriv(mode, key, iv)
|
||||||
|
|
||||||
const res = {
|
const res = {
|
||||||
encrypt (data, cb) {
|
async encrypt (data) { // eslint-disable-line require-await
|
||||||
cb(null, cipher.update(data))
|
return cipher.update(data)
|
||||||
},
|
},
|
||||||
|
|
||||||
decrypt (data, cb) {
|
async decrypt (data) { // eslint-disable-line require-await
|
||||||
cb(null, decipher.update(data))
|
return decipher.update(data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(null, res)
|
return res
|
||||||
}
|
}
|
||||||
|
@@ -1,8 +1,6 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
const nodeify = require('../nodeify')
|
const webcrypto = require('../webcrypto')
|
||||||
|
|
||||||
const crypto = require('../webcrypto.js')()
|
|
||||||
const lengths = require('./lengths')
|
const lengths = require('./lengths')
|
||||||
|
|
||||||
const hashTypes = {
|
const hashTypes = {
|
||||||
@@ -11,29 +9,28 @@ const hashTypes = {
|
|||||||
SHA512: 'SHA-512'
|
SHA512: 'SHA-512'
|
||||||
}
|
}
|
||||||
|
|
||||||
const sign = (key, data, cb) => {
|
const sign = async (key, data) => {
|
||||||
nodeify(crypto.subtle.sign({name: 'HMAC'}, key, data)
|
return Buffer.from(await webcrypto.get().subtle.sign({ name: 'HMAC' }, key, data))
|
||||||
.then((raw) => Buffer.from(raw)), cb)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.create = function (hashType, secret, callback) {
|
exports.create = async function (hashType, secret) {
|
||||||
const hash = hashTypes[hashType]
|
const hash = hashTypes[hashType]
|
||||||
|
|
||||||
nodeify(crypto.subtle.importKey(
|
const key = await webcrypto.get().subtle.importKey(
|
||||||
'raw',
|
'raw',
|
||||||
secret,
|
secret,
|
||||||
{
|
{
|
||||||
name: 'HMAC',
|
name: 'HMAC',
|
||||||
hash: {name: hash}
|
hash: { name: hash }
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
['sign']
|
['sign']
|
||||||
).then((key) => {
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
digest (data, cb) {
|
async digest (data) { // eslint-disable-line require-await
|
||||||
sign(key, data, cb)
|
return sign(key, data)
|
||||||
},
|
},
|
||||||
length: lengths[hashType]
|
length: lengths[hashType]
|
||||||
}
|
}
|
||||||
}), callback)
|
|
||||||
}
|
}
|
||||||
|
@@ -3,19 +3,15 @@
|
|||||||
const crypto = require('crypto')
|
const crypto = require('crypto')
|
||||||
const lengths = require('./lengths')
|
const lengths = require('./lengths')
|
||||||
|
|
||||||
exports.create = function (hash, secret, callback) {
|
exports.create = async function (hash, secret) { // eslint-disable-line require-await
|
||||||
const res = {
|
const res = {
|
||||||
digest (data, cb) {
|
async digest (data) { // eslint-disable-line require-await
|
||||||
const hmac = crypto.createHmac(hash.toLowerCase(), secret)
|
const hmac = crypto.createHmac(hash.toLowerCase(), secret)
|
||||||
|
|
||||||
hmac.update(data)
|
hmac.update(data)
|
||||||
|
return hmac.digest()
|
||||||
setImmediate(() => {
|
|
||||||
cb(null, hmac.digest())
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
length: lengths[hash]
|
length: lengths[hash]
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(null, res)
|
return res
|
||||||
}
|
}
|
||||||
|
@@ -4,8 +4,6 @@ const hmac = require('./hmac')
|
|||||||
const aes = require('./aes')
|
const aes = require('./aes')
|
||||||
const keys = require('./keys')
|
const keys = require('./keys')
|
||||||
|
|
||||||
exports = module.exports
|
|
||||||
|
|
||||||
exports.aes = aes
|
exports.aes = aes
|
||||||
exports.hmac = hmac
|
exports.hmac = hmac
|
||||||
exports.keys = keys
|
exports.keys = keys
|
||||||
|
@@ -1,12 +1,10 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
const webcrypto = require('../webcrypto.js')()
|
const errcode = require('err-code')
|
||||||
const nodeify = require('../nodeify')
|
const webcrypto = require('../webcrypto')
|
||||||
const BN = require('asn1.js').bignum
|
const BN = require('asn1.js').bignum
|
||||||
|
const { toBase64, toBn } = require('../util')
|
||||||
const util = require('../util')
|
const validateCurveType = require('./validate-curve-type')
|
||||||
const toBase64 = util.toBase64
|
|
||||||
const toBn = util.toBn
|
|
||||||
|
|
||||||
const bits = {
|
const bits = {
|
||||||
'P-256': 256,
|
'P-256': 256,
|
||||||
@@ -14,26 +12,23 @@ const bits = {
|
|||||||
'P-521': 521
|
'P-521': 521
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.generateEphmeralKeyPair = function (curve, callback) {
|
exports.generateEphmeralKeyPair = async function (curve) {
|
||||||
nodeify(webcrypto.subtle.generateKey(
|
validateCurveType(Object.keys(bits), curve)
|
||||||
|
const pair = await webcrypto.get().subtle.generateKey(
|
||||||
{
|
{
|
||||||
name: 'ECDH',
|
name: 'ECDH',
|
||||||
namedCurve: curve
|
namedCurve: curve
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
['deriveBits']
|
['deriveBits']
|
||||||
).then((pair) => {
|
)
|
||||||
// forcePrivate is used for testing only
|
|
||||||
const genSharedKey = (theirPub, forcePrivate, cb) => {
|
|
||||||
if (typeof forcePrivate === 'function') {
|
|
||||||
cb = forcePrivate
|
|
||||||
forcePrivate = undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// forcePrivate is used for testing only
|
||||||
|
const genSharedKey = async (theirPub, forcePrivate) => {
|
||||||
let privateKey
|
let privateKey
|
||||||
|
|
||||||
if (forcePrivate) {
|
if (forcePrivate) {
|
||||||
privateKey = webcrypto.subtle.importKey(
|
privateKey = await webcrypto.get().subtle.importKey(
|
||||||
'jwk',
|
'jwk',
|
||||||
unmarshalPrivateKey(curve, forcePrivate),
|
unmarshalPrivateKey(curve, forcePrivate),
|
||||||
{
|
{
|
||||||
@@ -44,11 +39,11 @@ exports.generateEphmeralKeyPair = function (curve, callback) {
|
|||||||
['deriveBits']
|
['deriveBits']
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
privateKey = Promise.resolve(pair.privateKey)
|
privateKey = pair.privateKey
|
||||||
}
|
}
|
||||||
|
|
||||||
const keys = Promise.all([
|
const keys = [
|
||||||
webcrypto.subtle.importKey(
|
await webcrypto.get().subtle.importKey(
|
||||||
'jwk',
|
'jwk',
|
||||||
unmarshalPublicKey(curve, theirPub),
|
unmarshalPublicKey(curve, theirPub),
|
||||||
{
|
{
|
||||||
@@ -59,9 +54,9 @@ exports.generateEphmeralKeyPair = function (curve, callback) {
|
|||||||
[]
|
[]
|
||||||
),
|
),
|
||||||
privateKey
|
privateKey
|
||||||
])
|
]
|
||||||
|
|
||||||
nodeify(keys.then((keys) => webcrypto.subtle.deriveBits(
|
return Buffer.from(await webcrypto.get().subtle.deriveBits(
|
||||||
{
|
{
|
||||||
name: 'ECDH',
|
name: 'ECDH',
|
||||||
namedCurve: curve,
|
namedCurve: curve,
|
||||||
@@ -69,17 +64,15 @@ exports.generateEphmeralKeyPair = function (curve, callback) {
|
|||||||
},
|
},
|
||||||
keys[1],
|
keys[1],
|
||||||
bits[curve]
|
bits[curve]
|
||||||
)).then((bits) => Buffer.from(bits)), cb)
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
return webcrypto.subtle.exportKey('jwk', pair.publicKey)
|
const publicKey = await webcrypto.get().subtle.exportKey('jwk', pair.publicKey)
|
||||||
.then((publicKey) => {
|
|
||||||
return {
|
return {
|
||||||
key: marshalPublicKey(publicKey),
|
key: marshalPublicKey(publicKey),
|
||||||
genSharedKey
|
genSharedKey
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}), callback)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const curveLengths = {
|
const curveLengths = {
|
||||||
@@ -106,7 +99,7 @@ function unmarshalPublicKey (curve, key) {
|
|||||||
const byteLen = curveLengths[curve]
|
const byteLen = curveLengths[curve]
|
||||||
|
|
||||||
if (!key.slice(0, 1).equals(Buffer.from([4]))) {
|
if (!key.slice(0, 1).equals(Buffer.from([4]))) {
|
||||||
throw new Error('Invalid key format')
|
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 x = new BN(key.slice(1, byteLen + 1))
|
||||||
const y = new BN(key.slice(1 + byteLen))
|
const y = new BN(key.slice(1 + byteLen))
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
const crypto = require('crypto')
|
const crypto = require('crypto')
|
||||||
const setImmediate = require('async/setImmediate')
|
const validateCurveType = require('./validate-curve-type')
|
||||||
|
|
||||||
const curves = {
|
const curves = {
|
||||||
'P-256': 'prime256v1',
|
'P-256': 'prime256v1',
|
||||||
@@ -9,33 +9,20 @@ const curves = {
|
|||||||
'P-521': 'secp521r1'
|
'P-521': 'secp521r1'
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.generateEphmeralKeyPair = function (curve, callback) {
|
exports.generateEphmeralKeyPair = async function (curve) { // eslint-disable-line require-await
|
||||||
if (!curves[curve]) {
|
validateCurveType(Object.keys(curves), curve)
|
||||||
return callback(new Error(`Unkown curve: ${curve}`))
|
|
||||||
}
|
|
||||||
const ecdh = crypto.createECDH(curves[curve])
|
const ecdh = crypto.createECDH(curves[curve])
|
||||||
ecdh.generateKeys()
|
ecdh.generateKeys()
|
||||||
|
|
||||||
setImmediate(() => callback(null, {
|
return {
|
||||||
key: ecdh.getPublicKey(),
|
key: ecdh.getPublicKey(),
|
||||||
genSharedKey (theirPub, forcePrivate, cb) {
|
async genSharedKey (theirPub, forcePrivate) { // eslint-disable-line require-await
|
||||||
if (typeof forcePrivate === 'function') {
|
|
||||||
cb = forcePrivate
|
|
||||||
forcePrivate = null
|
|
||||||
}
|
|
||||||
|
|
||||||
if (forcePrivate) {
|
if (forcePrivate) {
|
||||||
ecdh.setPrivateKey(forcePrivate.private)
|
ecdh.setPrivateKey(forcePrivate.private)
|
||||||
}
|
}
|
||||||
|
|
||||||
let secret
|
return ecdh.computeSecret(theirPub)
|
||||||
try {
|
|
||||||
secret = ecdh.computeSecret(theirPub)
|
|
||||||
} catch (err) {
|
|
||||||
return cb(err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setImmediate(() => cb(null, secret))
|
|
||||||
}
|
}
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
@@ -3,6 +3,7 @@
|
|||||||
const multihashing = require('multihashing-async')
|
const multihashing = require('multihashing-async')
|
||||||
const protobuf = require('protons')
|
const protobuf = require('protons')
|
||||||
const bs58 = require('bs58')
|
const bs58 = require('bs58')
|
||||||
|
const errcode = require('err-code')
|
||||||
|
|
||||||
const crypto = require('./ed25519')
|
const crypto = require('./ed25519')
|
||||||
const pbm = protobuf(require('./keys.proto'))
|
const pbm = protobuf(require('./keys.proto'))
|
||||||
@@ -12,9 +13,8 @@ class Ed25519PublicKey {
|
|||||||
this._key = ensureKey(key, crypto.publicKeyLength)
|
this._key = ensureKey(key, crypto.publicKeyLength)
|
||||||
}
|
}
|
||||||
|
|
||||||
verify (data, sig, callback) {
|
async verify (data, sig) { // eslint-disable-line require-await
|
||||||
ensure(callback)
|
return crypto.hashAndVerify(this._key, sig, data)
|
||||||
crypto.hashAndVerify(this._key, sig, data, callback)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
marshal () {
|
marshal () {
|
||||||
@@ -32,9 +32,8 @@ class Ed25519PublicKey {
|
|||||||
return this.bytes.equals(key.bytes)
|
return this.bytes.equals(key.bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
hash (callback) {
|
async hash () { // eslint-disable-line require-await
|
||||||
ensure(callback)
|
return multihashing(this.bytes, 'sha2-256')
|
||||||
multihashing(this.bytes, 'sha2-256', callback)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,16 +45,11 @@ class Ed25519PrivateKey {
|
|||||||
this._publicKey = ensureKey(publicKey, crypto.publicKeyLength)
|
this._publicKey = ensureKey(publicKey, crypto.publicKeyLength)
|
||||||
}
|
}
|
||||||
|
|
||||||
sign (message, callback) {
|
async sign (message) { // eslint-disable-line require-await
|
||||||
ensure(callback)
|
return crypto.hashAndSign(this._key, message)
|
||||||
crypto.hashAndSign(this._key, message, callback)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get public () {
|
get public () {
|
||||||
if (!this._publicKey) {
|
|
||||||
throw new Error('public key not provided')
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Ed25519PublicKey(this._publicKey)
|
return new Ed25519PublicKey(this._publicKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,9 +68,8 @@ class Ed25519PrivateKey {
|
|||||||
return this.bytes.equals(key.bytes)
|
return this.bytes.equals(key.bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
hash (callback) {
|
async hash () { // eslint-disable-line require-await
|
||||||
ensure(callback)
|
return multihashing(this.bytes, 'sha2-256')
|
||||||
multihashing(this.bytes, 'sha2-256', callback)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -86,28 +79,19 @@ class Ed25519PrivateKey {
|
|||||||
* The public key is a protobuf encoding containing a type and the DER encoding
|
* The public key is a protobuf encoding containing a type and the DER encoding
|
||||||
* of the PKCS SubjectPublicKeyInfo.
|
* of the PKCS SubjectPublicKeyInfo.
|
||||||
*
|
*
|
||||||
* @param {function(Error, id)} callback
|
* @returns {Promise<String>}
|
||||||
* @returns {undefined}
|
|
||||||
*/
|
*/
|
||||||
id (callback) {
|
async id () {
|
||||||
this.public.hash((err, hash) => {
|
const hash = await this.public.hash()
|
||||||
if (err) {
|
return bs58.encode(hash)
|
||||||
return callback(err)
|
|
||||||
}
|
|
||||||
callback(null, bs58.encode(hash))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function unmarshalEd25519PrivateKey (bytes, callback) {
|
function unmarshalEd25519PrivateKey (bytes) {
|
||||||
try {
|
|
||||||
bytes = ensureKey(bytes, crypto.privateKeyLength + crypto.publicKeyLength)
|
bytes = ensureKey(bytes, crypto.privateKeyLength + crypto.publicKeyLength)
|
||||||
} catch (err) {
|
|
||||||
return callback(err)
|
|
||||||
}
|
|
||||||
const privateKeyBytes = bytes.slice(0, crypto.privateKeyLength)
|
const privateKeyBytes = bytes.slice(0, crypto.privateKeyLength)
|
||||||
const publicKeyBytes = bytes.slice(crypto.privateKeyLength, bytes.length)
|
const publicKeyBytes = bytes.slice(crypto.privateKeyLength, bytes.length)
|
||||||
callback(null, new Ed25519PrivateKey(privateKeyBytes, publicKeyBytes))
|
return new Ed25519PrivateKey(privateKeyBytes, publicKeyBytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
function unmarshalEd25519PublicKey (bytes) {
|
function unmarshalEd25519PublicKey (bytes) {
|
||||||
@@ -115,52 +99,14 @@ function unmarshalEd25519PublicKey (bytes) {
|
|||||||
return new Ed25519PublicKey(bytes)
|
return new Ed25519PublicKey(bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateKeyPair (_bits, cb) {
|
async function generateKeyPair () {
|
||||||
if (cb === undefined && typeof _bits === 'function') {
|
const { secretKey, publicKey } = await crypto.generateKey()
|
||||||
cb = _bits
|
return new Ed25519PrivateKey(secretKey, publicKey)
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateKeyPairFromSeed (seed, _bits, cb) {
|
async function generateKeyPairFromSeed (seed) {
|
||||||
if (cb === undefined && typeof _bits === 'function') {
|
const { secretKey, publicKey } = await crypto.generateKeyFromSeed(seed)
|
||||||
cb = _bits
|
return new Ed25519PrivateKey(secretKey, publicKey)
|
||||||
}
|
|
||||||
|
|
||||||
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')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function ensureKey (key, length) {
|
function ensureKey (key, length) {
|
||||||
@@ -168,7 +114,7 @@ function ensureKey (key, length) {
|
|||||||
key = new Uint8Array(key)
|
key = new Uint8Array(key)
|
||||||
}
|
}
|
||||||
if (!(key instanceof Uint8Array) || key.length !== length) {
|
if (!(key instanceof Uint8Array) || key.length !== length) {
|
||||||
throw new Error('Key must be a Uint8Array or Buffer of length ' + length)
|
throw errcode(new Error('Key must be a Uint8Array or Buffer of length ' + length), 'ERR_INVALID_KEY_TYPE')
|
||||||
}
|
}
|
||||||
return key
|
return key
|
||||||
}
|
}
|
||||||
|
@@ -1,51 +1,23 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
const nacl = require('tweetnacl')
|
const nacl = require('tweetnacl')
|
||||||
const setImmediate = require('async/setImmediate')
|
|
||||||
|
|
||||||
exports.publicKeyLength = nacl.sign.publicKeyLength
|
exports.publicKeyLength = nacl.sign.publicKeyLength
|
||||||
exports.privateKeyLength = nacl.sign.secretKeyLength
|
exports.privateKeyLength = nacl.sign.secretKeyLength
|
||||||
|
|
||||||
exports.generateKey = function (callback) {
|
exports.generateKey = async function () { // eslint-disable-line require-await
|
||||||
setImmediate(() => {
|
return nacl.sign.keyPair()
|
||||||
let result
|
|
||||||
try {
|
|
||||||
result = nacl.sign.keyPair()
|
|
||||||
} catch (err) {
|
|
||||||
return callback(err)
|
|
||||||
}
|
|
||||||
callback(null, result)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// seed should be a 32 byte uint8array
|
// seed should be a 32 byte uint8array
|
||||||
exports.generateKeyFromSeed = function (seed, callback) {
|
exports.generateKeyFromSeed = async function (seed) { // eslint-disable-line require-await
|
||||||
setImmediate(() => {
|
return nacl.sign.keyPair.fromSeed(seed)
|
||||||
let result
|
|
||||||
try {
|
|
||||||
result = nacl.sign.keyPair.fromSeed(seed)
|
|
||||||
} catch (err) {
|
|
||||||
return callback(err)
|
|
||||||
}
|
|
||||||
callback(null, result)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.hashAndSign = function (key, msg, callback) {
|
exports.hashAndSign = async function (key, msg) { // eslint-disable-line require-await
|
||||||
setImmediate(() => {
|
return Buffer.from(nacl.sign.detached(msg, key))
|
||||||
callback(null, Buffer.from(nacl.sign.detached(msg, key)))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.hashAndVerify = function (key, sig, msg, callback) {
|
exports.hashAndVerify = async function (key, sig, msg) { // eslint-disable-line require-await
|
||||||
setImmediate(() => {
|
return nacl.sign.detached.verify(msg, sig, key)
|
||||||
let result
|
|
||||||
try {
|
|
||||||
result = nacl.sign.detached.verify(msg, sig, key)
|
|
||||||
} catch (err) {
|
|
||||||
return callback(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(null, result)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
@@ -6,6 +6,4 @@ const ecdh = require('./ecdh')
|
|||||||
// the shared secret key.
|
// the shared secret key.
|
||||||
//
|
//
|
||||||
// Focuses only on ECDH now, but can be made more general in the future.
|
// Focuses only on ECDH now, but can be made more general in the future.
|
||||||
module.exports = (curve, callback) => {
|
module.exports = async (curve) => ecdh.generateEphmeralKeyPair(curve) // eslint-disable-line require-await
|
||||||
ecdh.generateEphmeralKeyPair(curve, callback)
|
|
||||||
}
|
|
||||||
|
@@ -2,7 +2,11 @@
|
|||||||
|
|
||||||
const protobuf = require('protons')
|
const protobuf = require('protons')
|
||||||
const keysPBM = protobuf(require('./keys.proto'))
|
const keysPBM = protobuf(require('./keys.proto'))
|
||||||
const forge = require('node-forge')
|
require('node-forge/lib/asn1')
|
||||||
|
require('node-forge/lib/rsa')
|
||||||
|
require('node-forge/lib/pbe')
|
||||||
|
const forge = require('node-forge/lib/forge')
|
||||||
|
const errcode = require('err-code')
|
||||||
|
|
||||||
exports = module.exports
|
exports = module.exports
|
||||||
|
|
||||||
@@ -15,36 +19,36 @@ const supportedKeys = {
|
|||||||
exports.supportedKeys = supportedKeys
|
exports.supportedKeys = supportedKeys
|
||||||
exports.keysPBM = keysPBM
|
exports.keysPBM = keysPBM
|
||||||
|
|
||||||
function isValidKeyType (keyType) {
|
const ErrMissingSecp256K1 = {
|
||||||
const key = supportedKeys[keyType.toLowerCase()]
|
message: 'secp256k1 support requires libp2p-crypto-secp256k1 package',
|
||||||
return key !== undefined
|
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.keyStretcher = require('./key-stretcher')
|
||||||
exports.generateEphemeralKeyPair = require('./ephemeral-keys')
|
exports.generateEphemeralKeyPair = require('./ephemeral-keys')
|
||||||
|
|
||||||
// Generates a keypair of the given type and bitsize
|
// Generates a keypair of the given type and bitsize
|
||||||
exports.generateKeyPair = (type, bits, cb) => {
|
exports.generateKeyPair = async (type, bits) => { // eslint-disable-line require-await
|
||||||
let key = supportedKeys[type.toLowerCase()]
|
return typeToKey(type).generateKeyPair(bits)
|
||||||
|
|
||||||
if (!key) {
|
|
||||||
return cb(new Error('invalid or unsupported key type'))
|
|
||||||
}
|
|
||||||
|
|
||||||
key.generateKeyPair(bits, cb)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generates a keypair of the given type and bitsize
|
// Generates a keypair of the given type and bitsize
|
||||||
// seed is a 32 byte uint8array
|
// seed is a 32 byte uint8array
|
||||||
exports.generateKeyPairFromSeed = (type, seed, bits, cb) => {
|
exports.generateKeyPairFromSeed = async (type, seed, bits) => { // eslint-disable-line require-await
|
||||||
let key = supportedKeys[type.toLowerCase()]
|
const key = typeToKey(type)
|
||||||
if (!key) {
|
|
||||||
return cb(new Error('invalid or unsupported key type'))
|
|
||||||
}
|
|
||||||
if (type.toLowerCase() !== 'ed25519') {
|
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
|
// Converts a protobuf serialized public key into its
|
||||||
@@ -62,71 +66,55 @@ exports.unmarshalPublicKey = (buf) => {
|
|||||||
if (supportedKeys.secp256k1) {
|
if (supportedKeys.secp256k1) {
|
||||||
return supportedKeys.secp256k1.unmarshalSecp256k1PublicKey(data)
|
return supportedKeys.secp256k1.unmarshalSecp256k1PublicKey(data)
|
||||||
} else {
|
} else {
|
||||||
throw new Error('secp256k1 support requires libp2p-crypto-secp256k1 package')
|
throw errcode(new Error(ErrMissingSecp256K1.message), ErrMissingSecp256K1.code)
|
||||||
}
|
}
|
||||||
default:
|
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
|
// Converts a public key object into a protobuf serialized public key
|
||||||
exports.marshalPublicKey = (key, type) => {
|
exports.marshalPublicKey = (key, type) => {
|
||||||
type = (type || 'rsa').toLowerCase()
|
type = (type || 'rsa').toLowerCase()
|
||||||
if (!isValidKeyType(type)) {
|
typeToKey(type) // check type
|
||||||
throw new Error('invalid or unsupported key type')
|
|
||||||
}
|
|
||||||
|
|
||||||
return key.bytes
|
return key.bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
// Converts a protobuf serialized private key into its
|
// Converts a protobuf serialized private key into its
|
||||||
// representative object
|
// representative object
|
||||||
exports.unmarshalPrivateKey = (buf, callback) => {
|
exports.unmarshalPrivateKey = async (buf) => { // eslint-disable-line require-await
|
||||||
let decoded
|
const decoded = keysPBM.PrivateKey.decode(buf)
|
||||||
try {
|
|
||||||
decoded = keysPBM.PrivateKey.decode(buf)
|
|
||||||
} catch (err) {
|
|
||||||
return callback(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = decoded.Data
|
const data = decoded.Data
|
||||||
|
|
||||||
switch (decoded.Type) {
|
switch (decoded.Type) {
|
||||||
case keysPBM.KeyType.RSA:
|
case keysPBM.KeyType.RSA:
|
||||||
return supportedKeys.rsa.unmarshalRsaPrivateKey(data, callback)
|
return supportedKeys.rsa.unmarshalRsaPrivateKey(data)
|
||||||
case keysPBM.KeyType.Ed25519:
|
case keysPBM.KeyType.Ed25519:
|
||||||
return supportedKeys.ed25519.unmarshalEd25519PrivateKey(data, callback)
|
return supportedKeys.ed25519.unmarshalEd25519PrivateKey(data)
|
||||||
case keysPBM.KeyType.Secp256k1:
|
case keysPBM.KeyType.Secp256k1:
|
||||||
if (supportedKeys.secp256k1) {
|
if (supportedKeys.secp256k1) {
|
||||||
return supportedKeys.secp256k1.unmarshalSecp256k1PrivateKey(data, callback)
|
return supportedKeys.secp256k1.unmarshalSecp256k1PrivateKey(data)
|
||||||
} else {
|
} else {
|
||||||
return callback(new Error('secp256k1 support requires libp2p-crypto-secp256k1 package'))
|
throw errcode(new Error(ErrMissingSecp256K1.message), ErrMissingSecp256K1.code)
|
||||||
}
|
}
|
||||||
default:
|
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
|
// Converts a private key object into a protobuf serialized private key
|
||||||
exports.marshalPrivateKey = (key, type) => {
|
exports.marshalPrivateKey = (key, type) => {
|
||||||
type = (type || 'rsa').toLowerCase()
|
type = (type || 'rsa').toLowerCase()
|
||||||
if (!isValidKeyType(type)) {
|
typeToKey(type) // check type
|
||||||
throw new Error('invalid or unsupported key type')
|
|
||||||
}
|
|
||||||
|
|
||||||
return key.bytes
|
return key.bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.import = (pem, password, callback) => {
|
exports.import = async (pem, password) => { // eslint-disable-line require-await
|
||||||
try {
|
|
||||||
const key = forge.pki.decryptRsaPrivateKey(pem, password)
|
const key = forge.pki.decryptRsaPrivateKey(pem, password)
|
||||||
if (key === null) {
|
if (key === null) {
|
||||||
throw new Error('Cannot read the key, most likely the password is wrong or not a RSA key')
|
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))
|
let der = forge.asn1.toDer(forge.pki.privateKeyToAsn1(key))
|
||||||
der = Buffer.from(der.getBytes(), 'binary')
|
der = Buffer.from(der.getBytes(), 'binary')
|
||||||
return supportedKeys.rsa.unmarshalRsaPrivateKey(der, callback)
|
return supportedKeys.rsa.unmarshalRsaPrivateKey(der)
|
||||||
} catch (err) {
|
|
||||||
callback(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
42
src/keys/jwk2pem.js
Normal file
42
src/keys/jwk2pem.js
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
'use strict'
|
||||||
|
|
||||||
|
const forge = {
|
||||||
|
util: require('node-forge/lib/util'),
|
||||||
|
pki: require('node-forge/lib/pki'),
|
||||||
|
jsbn: require('node-forge/lib/jsbn')
|
||||||
|
}
|
||||||
|
|
||||||
|
function base64urlToBigInteger (str) {
|
||||||
|
var bytes = forge.util.decode64(
|
||||||
|
(str + '==='.slice((str.length + 3) % 4))
|
||||||
|
.replace(/-/g, '+')
|
||||||
|
.replace(/_/g, '/'))
|
||||||
|
return new forge.jsbn.BigInteger(forge.util.bytesToHex(bytes), 16)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 jwk2privPem (key) {
|
||||||
|
return forge.pki.privateKeyToPem(jwk2priv(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
function jwk2pub (key) {
|
||||||
|
return forge.pki.setRsaPublicKey(...convert(key, ['n', 'e']))
|
||||||
|
}
|
||||||
|
|
||||||
|
function jwk2pubPem (key) {
|
||||||
|
return forge.pki.publicKeyToPem(jwk2pub(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
jwk2pub,
|
||||||
|
jwk2pubPem,
|
||||||
|
jwk2priv,
|
||||||
|
jwk2privPem
|
||||||
|
}
|
@@ -1,6 +1,6 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
const whilst = require('async/whilst')
|
const errcode = require('err-code')
|
||||||
const hmac = require('../hmac')
|
const hmac = require('../hmac')
|
||||||
|
|
||||||
const cipherMap = {
|
const cipherMap = {
|
||||||
@@ -20,15 +20,16 @@ const cipherMap = {
|
|||||||
|
|
||||||
// Generates a set of keys for each party by stretching the shared key.
|
// Generates a set of keys for each party by stretching the shared key.
|
||||||
// (myIV, theirIV, myCipherKey, theirCipherKey, myMACKey, theirMACKey)
|
// (myIV, theirIV, myCipherKey, theirCipherKey, myMACKey, theirMACKey)
|
||||||
module.exports = (cipherType, hash, secret, callback) => {
|
module.exports = async (cipherType, hash, secret) => {
|
||||||
const cipher = cipherMap[cipherType]
|
const cipher = cipherMap[cipherType]
|
||||||
|
|
||||||
if (!cipher) {
|
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) {
|
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 cipherKeySize = cipher.keySize
|
||||||
@@ -37,31 +38,14 @@ module.exports = (cipherType, hash, secret, callback) => {
|
|||||||
const seed = Buffer.from('key expansion')
|
const seed = Buffer.from('key expansion')
|
||||||
const resultLength = 2 * (ivSize + cipherKeySize + hmacKeySize)
|
const resultLength = 2 * (ivSize + cipherKeySize + hmacKeySize)
|
||||||
|
|
||||||
hmac.create(hash, secret, (err, m) => {
|
const m = await hmac.create(hash, secret)
|
||||||
if (err) {
|
let a = await m.digest(seed)
|
||||||
return callback(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
m.digest(seed, (err, a) => {
|
const result = []
|
||||||
if (err) {
|
|
||||||
return callback(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
let result = []
|
|
||||||
let j = 0
|
let j = 0
|
||||||
|
|
||||||
whilst(
|
while (j < resultLength) {
|
||||||
() => j < resultLength,
|
const b = await m.digest(Buffer.concat([a, seed]))
|
||||||
stretch,
|
|
||||||
finish
|
|
||||||
)
|
|
||||||
|
|
||||||
function stretch (cb) {
|
|
||||||
m.digest(Buffer.concat([a, seed]), (err, b) => {
|
|
||||||
if (err) {
|
|
||||||
return cb(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
let todo = b.length
|
let todo = b.length
|
||||||
|
|
||||||
if (j + todo > resultLength) {
|
if (j + todo > resultLength) {
|
||||||
@@ -69,22 +53,8 @@ module.exports = (cipherType, hash, secret, callback) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
result.push(b)
|
result.push(b)
|
||||||
|
|
||||||
j += todo
|
j += todo
|
||||||
|
a = await m.digest(a)
|
||||||
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 half = resultLength / 2
|
||||||
@@ -98,11 +68,8 @@ module.exports = (cipherType, hash, secret, callback) => {
|
|||||||
macKey: res.slice(ivSize + cipherKeySize)
|
macKey: res.slice(ivSize + cipherKeySize)
|
||||||
})
|
})
|
||||||
|
|
||||||
callback(null, {
|
return {
|
||||||
k1: createKey(r1),
|
k1: createKey(r1),
|
||||||
k2: createKey(r2)
|
k2: createKey(r2)
|
||||||
})
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
@@ -1,106 +1,111 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
const nodeify = require('../nodeify')
|
const webcrypto = require('../webcrypto')
|
||||||
|
const randomBytes = require('../random-bytes')
|
||||||
const webcrypto = require('../webcrypto.js')()
|
|
||||||
|
|
||||||
exports.utils = require('./rsa-utils')
|
exports.utils = require('./rsa-utils')
|
||||||
|
|
||||||
exports.generateKey = function (bits, callback) {
|
exports.generateKey = async function (bits) {
|
||||||
nodeify(webcrypto.subtle.generateKey(
|
const pair = await webcrypto.get().subtle.generateKey(
|
||||||
{
|
{
|
||||||
name: 'RSASSA-PKCS1-v1_5',
|
name: 'RSASSA-PKCS1-v1_5',
|
||||||
modulusLength: bits,
|
modulusLength: bits,
|
||||||
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
|
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
|
||||||
hash: {name: 'SHA-256'}
|
hash: { name: 'SHA-256' }
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
['sign', 'verify']
|
['sign', 'verify']
|
||||||
)
|
)
|
||||||
.then(exportKey)
|
|
||||||
.then((keys) => ({
|
const keys = await exportKey(pair)
|
||||||
|
|
||||||
|
return {
|
||||||
privateKey: keys[0],
|
privateKey: keys[0],
|
||||||
publicKey: keys[1]
|
publicKey: keys[1]
|
||||||
})), callback)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Takes a jwk key
|
// Takes a jwk key
|
||||||
exports.unmarshalPrivateKey = function (key, callback) {
|
exports.unmarshalPrivateKey = async function (key) {
|
||||||
const privateKey = webcrypto.subtle.importKey(
|
const privateKey = await webcrypto.get().subtle.importKey(
|
||||||
'jwk',
|
'jwk',
|
||||||
key,
|
key,
|
||||||
{
|
{
|
||||||
name: 'RSASSA-PKCS1-v1_5',
|
name: 'RSASSA-PKCS1-v1_5',
|
||||||
hash: {name: 'SHA-256'}
|
hash: { name: 'SHA-256' }
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
['sign']
|
['sign']
|
||||||
)
|
)
|
||||||
|
|
||||||
nodeify(Promise.all([
|
const pair = [
|
||||||
privateKey,
|
privateKey,
|
||||||
derivePublicFromPrivate(key)
|
await derivePublicFromPrivate(key)
|
||||||
]).then((keys) => exportKey({
|
]
|
||||||
|
|
||||||
|
const keys = await exportKey({
|
||||||
|
privateKey: pair[0],
|
||||||
|
publicKey: pair[1]
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
privateKey: keys[0],
|
privateKey: keys[0],
|
||||||
publicKey: keys[1]
|
publicKey: keys[1]
|
||||||
})).then((keys) => ({
|
}
|
||||||
privateKey: keys[0],
|
|
||||||
publicKey: keys[1]
|
|
||||||
})), callback)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.getRandomValues = function (arr) {
|
exports.getRandomValues = randomBytes
|
||||||
return Buffer.from(webcrypto.getRandomValues(arr))
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.hashAndSign = function (key, msg, callback) {
|
exports.hashAndSign = async function (key, msg) {
|
||||||
nodeify(webcrypto.subtle.importKey(
|
const privateKey = await webcrypto.get().subtle.importKey(
|
||||||
'jwk',
|
'jwk',
|
||||||
key,
|
key,
|
||||||
{
|
{
|
||||||
name: 'RSASSA-PKCS1-v1_5',
|
name: 'RSASSA-PKCS1-v1_5',
|
||||||
hash: {name: 'SHA-256'}
|
hash: { name: 'SHA-256' }
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
['sign']
|
['sign']
|
||||||
).then((privateKey) => {
|
)
|
||||||
return webcrypto.subtle.sign(
|
|
||||||
{name: 'RSASSA-PKCS1-v1_5'},
|
const sig = await webcrypto.get().subtle.sign(
|
||||||
|
{ name: 'RSASSA-PKCS1-v1_5' },
|
||||||
privateKey,
|
privateKey,
|
||||||
Uint8Array.from(msg)
|
Uint8Array.from(msg)
|
||||||
)
|
)
|
||||||
}).then((sig) => Buffer.from(sig)), callback)
|
|
||||||
|
return Buffer.from(sig)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.hashAndVerify = function (key, sig, msg, callback) {
|
exports.hashAndVerify = async function (key, sig, msg) {
|
||||||
nodeify(webcrypto.subtle.importKey(
|
const publicKey = await webcrypto.get().subtle.importKey(
|
||||||
'jwk',
|
'jwk',
|
||||||
key,
|
key,
|
||||||
{
|
{
|
||||||
name: 'RSASSA-PKCS1-v1_5',
|
name: 'RSASSA-PKCS1-v1_5',
|
||||||
hash: {name: 'SHA-256'}
|
hash: { name: 'SHA-256' }
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
['verify']
|
['verify']
|
||||||
).then((publicKey) => {
|
)
|
||||||
return webcrypto.subtle.verify(
|
|
||||||
{name: 'RSASSA-PKCS1-v1_5'},
|
return webcrypto.get().subtle.verify(
|
||||||
|
{ name: 'RSASSA-PKCS1-v1_5' },
|
||||||
publicKey,
|
publicKey,
|
||||||
sig,
|
sig,
|
||||||
msg
|
msg
|
||||||
)
|
)
|
||||||
}), callback)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function exportKey (pair) {
|
function exportKey (pair) {
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
webcrypto.subtle.exportKey('jwk', pair.privateKey),
|
webcrypto.get().subtle.exportKey('jwk', pair.privateKey),
|
||||||
webcrypto.subtle.exportKey('jwk', pair.publicKey)
|
webcrypto.get().subtle.exportKey('jwk', pair.publicKey)
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
function derivePublicFromPrivate (jwKey) {
|
function derivePublicFromPrivate (jwKey) {
|
||||||
return webcrypto.subtle.importKey(
|
return webcrypto.get().subtle.importKey(
|
||||||
'jwk',
|
'jwk',
|
||||||
{
|
{
|
||||||
kty: jwKey.kty,
|
kty: jwKey.kty,
|
||||||
@@ -109,9 +114,38 @@ function derivePublicFromPrivate (jwKey) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'RSASSA-PKCS1-v1_5',
|
name: 'RSASSA-PKCS1-v1_5',
|
||||||
hash: {name: 'SHA-256'}
|
hash: { name: 'SHA-256' }
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
['verify']
|
['verify']
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
RSA encryption/decryption for the browser with webcrypto workarround
|
||||||
|
"bloody dark magic. webcrypto's why."
|
||||||
|
|
||||||
|
Explanation:
|
||||||
|
- Convert JWK to nodeForge
|
||||||
|
- Convert msg buffer to nodeForge buffer: ByteBuffer is a "binary-string backed buffer", so let's make our buffer a binary string
|
||||||
|
- Convert resulting nodeForge buffer to buffer: it returns a binary string, turn that into a uint8array(buffer)
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
const { jwk2pub, jwk2priv } = require('./jwk2pem')
|
||||||
|
|
||||||
|
function convertKey (key, pub, msg, handle) {
|
||||||
|
const fkey = pub ? jwk2pub(key) : jwk2priv(key)
|
||||||
|
const fmsg = Buffer.from(msg).toString('binary')
|
||||||
|
const fomsg = handle(fmsg, fkey)
|
||||||
|
return Buffer.from(fomsg, 'binary')
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.encrypt = function (key, msg) {
|
||||||
|
return convertKey(key, true, msg, (msg, key) => key.encrypt(msg))
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.decrypt = function (key, msg) {
|
||||||
|
return convertKey(key, false, msg, (msg, key) => key.decrypt(msg))
|
||||||
|
}
|
||||||
|
@@ -3,20 +3,21 @@
|
|||||||
const multihashing = require('multihashing-async')
|
const multihashing = require('multihashing-async')
|
||||||
const protobuf = require('protons')
|
const protobuf = require('protons')
|
||||||
const bs58 = require('bs58')
|
const bs58 = require('bs58')
|
||||||
|
const errcode = require('err-code')
|
||||||
|
|
||||||
const crypto = require('./rsa')
|
const crypto = require('./rsa')
|
||||||
const pbm = protobuf(require('./keys.proto'))
|
const pbm = protobuf(require('./keys.proto'))
|
||||||
const forge = require('node-forge')
|
require('node-forge/lib/sha512')
|
||||||
const setImmediate = require('async/setImmediate')
|
require('node-forge/lib/pbe')
|
||||||
|
const forge = require('node-forge/lib/forge')
|
||||||
|
|
||||||
class RsaPublicKey {
|
class RsaPublicKey {
|
||||||
constructor (key) {
|
constructor (key) {
|
||||||
this._key = key
|
this._key = key
|
||||||
}
|
}
|
||||||
|
|
||||||
verify (data, sig, callback) {
|
async verify (data, sig) { // eslint-disable-line require-await
|
||||||
ensure(callback)
|
return crypto.hashAndVerify(this._key, sig, data)
|
||||||
crypto.hashAndVerify(this._key, sig, data, callback)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
marshal () {
|
marshal () {
|
||||||
@@ -31,16 +32,15 @@ class RsaPublicKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
encrypt (bytes) {
|
encrypt (bytes) {
|
||||||
return this._key.encrypt(bytes, 'RSAES-PKCS1-V1_5')
|
return crypto.encrypt(this._key, bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
equals (key) {
|
equals (key) {
|
||||||
return this.bytes.equals(key.bytes)
|
return this.bytes.equals(key.bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
hash (callback) {
|
async hash () { // eslint-disable-line require-await
|
||||||
ensure(callback)
|
return multihashing(this.bytes, 'sha2-256')
|
||||||
multihashing(this.bytes, 'sha2-256', callback)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,24 +53,23 @@ class RsaPrivateKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
genSecret () {
|
genSecret () {
|
||||||
return crypto.getRandomValues(new Uint8Array(16))
|
return crypto.getRandomValues(16)
|
||||||
}
|
}
|
||||||
|
|
||||||
sign (message, callback) {
|
async sign (message) { // eslint-disable-line require-await
|
||||||
ensure(callback)
|
return crypto.hashAndSign(this._key, message)
|
||||||
crypto.hashAndSign(this._key, message, callback)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get public () {
|
get public () {
|
||||||
if (!this._publicKey) {
|
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)
|
return new RsaPublicKey(this._publicKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
decrypt (msg, callback) {
|
decrypt (bytes) {
|
||||||
crypto.decrypt(this._key, msg, callback)
|
return crypto.decrypt(this._key, bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
marshal () {
|
marshal () {
|
||||||
@@ -88,9 +87,8 @@ class RsaPrivateKey {
|
|||||||
return this.bytes.equals(key.bytes)
|
return this.bytes.equals(key.bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
hash (callback) {
|
async hash () { // eslint-disable-line require-await
|
||||||
ensure(callback)
|
return multihashing(this.bytes, 'sha2-256')
|
||||||
multihashing(this.bytes, 'sha2-256', callback)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -100,39 +98,23 @@ class RsaPrivateKey {
|
|||||||
* The public key is a protobuf encoding containing a type and the DER encoding
|
* The public key is a protobuf encoding containing a type and the DER encoding
|
||||||
* of the PKCS SubjectPublicKeyInfo.
|
* of the PKCS SubjectPublicKeyInfo.
|
||||||
*
|
*
|
||||||
* @param {function(Error, id)} callback
|
* @returns {Promise<String>}
|
||||||
* @returns {undefined}
|
|
||||||
*/
|
*/
|
||||||
id (callback) {
|
async id () {
|
||||||
this.public.hash((err, hash) => {
|
const hash = await this.public.hash()
|
||||||
if (err) {
|
return bs58.encode(hash)
|
||||||
return callback(err)
|
|
||||||
}
|
|
||||||
callback(null, bs58.encode(hash))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exports the key into a password protected PEM format
|
* Exports the key into a password protected PEM format
|
||||||
*
|
*
|
||||||
* @param {string} [format] - Defaults to 'pkcs-8'.
|
|
||||||
* @param {string} password - The password to read the encrypted PEM
|
* @param {string} password - The password to read the encrypted PEM
|
||||||
* @param {function(Error, KeyInfo)} callback
|
* @param {string} [format] - Defaults to 'pkcs-8'.
|
||||||
* @returns {undefined}
|
* @returns {KeyInfo}
|
||||||
*/
|
*/
|
||||||
export (format, password, callback) {
|
async export (password, format = 'pkcs-8') { // eslint-disable-line require-await
|
||||||
if (typeof password === 'function') {
|
|
||||||
callback = password
|
|
||||||
password = format
|
|
||||||
format = 'pkcs-8'
|
|
||||||
}
|
|
||||||
|
|
||||||
ensure(callback)
|
|
||||||
|
|
||||||
setImmediate(() => {
|
|
||||||
let err = null
|
|
||||||
let pem = null
|
let pem = null
|
||||||
try {
|
|
||||||
const buffer = new forge.util.ByteBuffer(this.marshal())
|
const buffer = new forge.util.ByteBuffer(this.marshal())
|
||||||
const asn1 = forge.asn1.fromDer(buffer)
|
const asn1 = forge.asn1.fromDer(buffer)
|
||||||
const privateKey = forge.pki.privateKeyFromAsn1(asn1)
|
const privateKey = forge.pki.privateKeyFromAsn1(asn1)
|
||||||
@@ -146,59 +128,32 @@ class RsaPrivateKey {
|
|||||||
}
|
}
|
||||||
pem = forge.pki.encryptRsaPrivateKey(privateKey, password, options)
|
pem = forge.pki.encryptRsaPrivateKey(privateKey, password, options)
|
||||||
} else {
|
} else {
|
||||||
err = new Error(`Unknown export format '${format}'`)
|
throw errcode(new Error(`Unknown export format '${format}'. Must be pkcs-8`), 'ERR_INVALID_EXPORT_FORMAT')
|
||||||
}
|
|
||||||
} catch (_err) {
|
|
||||||
err = _err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(err, pem)
|
return pem
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function unmarshalRsaPrivateKey (bytes, callback) {
|
async function unmarshalRsaPrivateKey (bytes) {
|
||||||
const jwk = crypto.utils.pkcs1ToJwk(bytes)
|
const jwk = crypto.utils.pkcs1ToJwk(bytes)
|
||||||
|
const keys = await crypto.unmarshalPrivateKey(jwk)
|
||||||
crypto.unmarshalPrivateKey(jwk, (err, keys) => {
|
return new RsaPrivateKey(keys.privateKey, keys.publicKey)
|
||||||
if (err) {
|
|
||||||
return callback(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(null, new RsaPrivateKey(keys.privateKey, keys.publicKey))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function unmarshalRsaPublicKey (bytes) {
|
function unmarshalRsaPublicKey (bytes) {
|
||||||
const jwk = crypto.utils.pkixToJwk(bytes)
|
const jwk = crypto.utils.pkixToJwk(bytes)
|
||||||
|
|
||||||
return new RsaPublicKey(jwk)
|
return new RsaPublicKey(jwk)
|
||||||
}
|
}
|
||||||
|
|
||||||
function fromJwk (jwk, callback) {
|
async function fromJwk (jwk) {
|
||||||
crypto.unmarshalPrivateKey(jwk, (err, keys) => {
|
const keys = await crypto.unmarshalPrivateKey(jwk)
|
||||||
if (err) {
|
return new RsaPrivateKey(keys.privateKey, keys.publicKey)
|
||||||
return callback(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(null, new RsaPrivateKey(keys.privateKey, keys.publicKey))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateKeyPair (bits, callback) {
|
async function generateKeyPair (bits) {
|
||||||
crypto.generateKey(bits, (err, keys) => {
|
const keys = await crypto.generateKey(bits)
|
||||||
if (err) {
|
return new RsaPrivateKey(keys.privateKey, keys.publicKey)
|
||||||
return callback(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(null, new RsaPrivateKey(keys.privateKey, keys.publicKey))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function ensure (callback) {
|
|
||||||
if (typeof callback !== 'function') {
|
|
||||||
throw new Error('callback is required')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
@@ -1,6 +1,9 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
const crypto = require('crypto')
|
const crypto = require('crypto')
|
||||||
|
const errcode = require('err-code')
|
||||||
|
const randomBytes = require('../random-bytes')
|
||||||
|
|
||||||
let keypair
|
let keypair
|
||||||
try {
|
try {
|
||||||
if (process.env.LP2P_FORCE_CRYPTO_LIB === 'keypair') {
|
if (process.env.LP2P_FORCE_CRYPTO_LIB === 'keypair') {
|
||||||
@@ -8,7 +11,7 @@ try {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ursa = require('ursa-optional') // throws if not compiled
|
const ursa = require('ursa-optional') // throws if not compiled
|
||||||
keypair = ({bits}) => {
|
keypair = ({ bits }) => {
|
||||||
const key = ursa.generatePrivateKey(bits)
|
const key = ursa.generatePrivateKey(bits)
|
||||||
return {
|
return {
|
||||||
private: key.toPrivatePem(),
|
private: key.toPrivatePem(),
|
||||||
@@ -22,78 +25,56 @@ try {
|
|||||||
|
|
||||||
keypair = require('keypair')
|
keypair = require('keypair')
|
||||||
}
|
}
|
||||||
const setImmediate = require('async/setImmediate')
|
|
||||||
const pemToJwk = require('pem-jwk').pem2jwk
|
const pemToJwk = require('pem-jwk').pem2jwk
|
||||||
const jwkToPem = require('pem-jwk').jwk2pem
|
const jwkToPem = require('pem-jwk').jwk2pem
|
||||||
|
|
||||||
exports.utils = require('./rsa-utils')
|
exports.utils = require('./rsa-utils')
|
||||||
|
|
||||||
exports.generateKey = function (bits, callback) {
|
exports.generateKey = async function (bits) { // eslint-disable-line require-await
|
||||||
setImmediate(() => {
|
const key = keypair({ bits })
|
||||||
let result
|
return {
|
||||||
try {
|
|
||||||
const key = keypair({ bits: bits })
|
|
||||||
result = {
|
|
||||||
privateKey: pemToJwk(key.private),
|
privateKey: pemToJwk(key.private),
|
||||||
publicKey: pemToJwk(key.public)
|
publicKey: pemToJwk(key.public)
|
||||||
}
|
}
|
||||||
} catch (err) {
|
|
||||||
return callback(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(null, result)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Takes a jwk key
|
// Takes a jwk key
|
||||||
exports.unmarshalPrivateKey = function (key, callback) {
|
exports.unmarshalPrivateKey = async function (key) { // eslint-disable-line require-await
|
||||||
setImmediate(() => {
|
|
||||||
if (!key) {
|
if (!key) {
|
||||||
return callback(new Error('Key is invalid'))
|
throw errcode(new Error('Missing key parameter'), 'ERR_MISSING_KEY')
|
||||||
}
|
}
|
||||||
callback(null, {
|
return {
|
||||||
privateKey: key,
|
privateKey: key,
|
||||||
publicKey: {
|
publicKey: {
|
||||||
kty: key.kty,
|
kty: key.kty,
|
||||||
n: key.n,
|
n: key.n,
|
||||||
e: key.e
|
e: key.e
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.getRandomValues = function (arr) {
|
exports.getRandomValues = randomBytes
|
||||||
return crypto.randomBytes(arr.length)
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.hashAndSign = function (key, msg, callback) {
|
exports.hashAndSign = async function (key, msg) { // eslint-disable-line require-await
|
||||||
setImmediate(() => {
|
|
||||||
let result
|
|
||||||
try {
|
|
||||||
const sign = crypto.createSign('RSA-SHA256')
|
const sign = crypto.createSign('RSA-SHA256')
|
||||||
sign.update(msg)
|
sign.update(msg)
|
||||||
const pem = jwkToPem(key)
|
const pem = jwkToPem(key)
|
||||||
result = sign.sign(pem)
|
return sign.sign(pem)
|
||||||
} catch (err) {
|
|
||||||
return callback(new Error('Key or message is invalid!: ' + err.message))
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(null, result)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.hashAndVerify = function (key, sig, msg, callback) {
|
exports.hashAndVerify = async function (key, sig, msg) { // eslint-disable-line require-await
|
||||||
setImmediate(() => {
|
|
||||||
let result
|
|
||||||
try {
|
|
||||||
const verify = crypto.createVerify('RSA-SHA256')
|
const verify = crypto.createVerify('RSA-SHA256')
|
||||||
verify.update(msg)
|
verify.update(msg)
|
||||||
const pem = jwkToPem(key)
|
const pem = jwkToPem(key)
|
||||||
result = verify.verify(pem, sig)
|
return verify.verify(pem, sig)
|
||||||
} catch (err) {
|
}
|
||||||
return callback(new Error('Key or message is invalid!:' + err.message))
|
|
||||||
}
|
const padding = crypto.constants.RSA_PKCS1_PADDING
|
||||||
|
|
||||||
callback(null, result)
|
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)
|
||||||
}
|
}
|
||||||
|
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')
|
||||||
|
}
|
||||||
|
}
|
@@ -1,11 +0,0 @@
|
|||||||
'use strict'
|
|
||||||
|
|
||||||
// Based on npmjs.com/nodeify but without additional `nextTick` calls
|
|
||||||
// to keep the overhead low
|
|
||||||
module.exports = function nodeify (promise, cb) {
|
|
||||||
return promise.then((res) => {
|
|
||||||
cb(null, res)
|
|
||||||
}, (err) => {
|
|
||||||
cb(err)
|
|
||||||
})
|
|
||||||
}
|
|
@@ -1,6 +1,8 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
const forge = require('node-forge')
|
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.
|
* Maps an IPFS hash name to its node-forge equivalent.
|
||||||
@@ -28,15 +30,16 @@ const hashName = {
|
|||||||
function pbkdf2 (password, salt, iterations, keySize, hash) {
|
function pbkdf2 (password, salt, iterations, keySize, hash) {
|
||||||
const hasher = hashName[hash]
|
const hasher = hashName[hash]
|
||||||
if (!hasher) {
|
if (!hasher) {
|
||||||
throw new Error(`Hash '${hash}' is unknown or not supported`)
|
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 = forge.pkcs5.pbkdf2(
|
const dek = forgePbkdf2(
|
||||||
password,
|
password,
|
||||||
salt,
|
salt,
|
||||||
iterations,
|
iterations,
|
||||||
keySize,
|
keySize,
|
||||||
hasher)
|
hasher)
|
||||||
return forge.util.encode64(dek)
|
return forgeUtil.encode64(dek)
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = pbkdf2
|
module.exports = pbkdf2
|
||||||
|
@@ -1,13 +1,10 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
|
const randomBytes = require('iso-random-stream/src/random')
|
||||||
|
const errcode = require('err-code')
|
||||||
|
|
||||||
const rsa = require('./keys/rsa')
|
module.exports = function (length) {
|
||||||
|
if (isNaN(length) || length <= 0) {
|
||||||
function randomBytes (number) {
|
throw errcode(new Error('random bytes length must be a Number bigger than 0'), 'ERR_INVALID_LENGTH')
|
||||||
if (!number || typeof number !== 'number') {
|
|
||||||
throw new Error('first argument must be a Number bigger than 0')
|
|
||||||
}
|
}
|
||||||
|
return randomBytes(length)
|
||||||
return rsa.getRandomValues(new Uint8Array(number))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = randomBytes
|
|
||||||
|
@@ -6,7 +6,7 @@ const BN = require('asn1.js').bignum
|
|||||||
// Adapted from https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-41#appendix-C
|
// Adapted from https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-41#appendix-C
|
||||||
exports.toBase64 = function toBase64 (bn, len) {
|
exports.toBase64 = function toBase64 (bn, len) {
|
||||||
// if len is defined then the bytes are leading-0 padded to the length
|
// if len is defined then the bytes are leading-0 padded to the length
|
||||||
let s = bn.toArrayLike(Buffer, 'be', len).toString('base64')
|
const s = bn.toArrayLike(Buffer, 'be', len).toString('base64')
|
||||||
|
|
||||||
return s
|
return s
|
||||||
.replace(/(=*)$/, '') // Remove any trailing '='s
|
.replace(/(=*)$/, '') // Remove any trailing '='s
|
||||||
|
@@ -1,16 +1,24 @@
|
|||||||
/* global self */
|
/* eslint-env browser */
|
||||||
|
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
module.exports = () => {
|
// Check native crypto exists and is enabled (In insecure context `self.crypto`
|
||||||
// This is only a shim for interfaces, not for functionality
|
// exists but `self.crypto.subtle` does not).
|
||||||
if (typeof self !== 'undefined') {
|
exports.get = (win = self) => {
|
||||||
require('webcrypto-shim')(self)
|
const nativeCrypto = win.crypto || win.msCrypto
|
||||||
|
|
||||||
if (self.crypto) {
|
if (!nativeCrypto || !nativeCrypto.subtle) {
|
||||||
return self.crypto
|
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
|
||||||
}
|
}
|
||||||
|
@@ -6,7 +6,7 @@ const chai = require('chai')
|
|||||||
const dirtyChai = require('dirty-chai')
|
const dirtyChai = require('dirty-chai')
|
||||||
const expect = chai.expect
|
const expect = chai.expect
|
||||||
chai.use(dirtyChai)
|
chai.use(dirtyChai)
|
||||||
const series = require('async/series')
|
const { expectErrCode } = require('../util')
|
||||||
|
|
||||||
const crypto = require('../../src')
|
const crypto = require('../../src')
|
||||||
const fixtures = require('./../fixtures/aes')
|
const fixtures = require('./../fixtures/aes')
|
||||||
@@ -19,53 +19,43 @@ const bytes = {
|
|||||||
|
|
||||||
describe('AES-CTR', () => {
|
describe('AES-CTR', () => {
|
||||||
Object.keys(bytes).forEach((byte) => {
|
Object.keys(bytes).forEach((byte) => {
|
||||||
it(`${bytes[byte]} - encrypt and decrypt`, (done) => {
|
it(`${bytes[byte]} - encrypt and decrypt`, async () => {
|
||||||
const key = Buffer.alloc(parseInt(byte, 10))
|
const key = Buffer.alloc(parseInt(byte, 10))
|
||||||
key.fill(5)
|
key.fill(5)
|
||||||
|
|
||||||
const iv = Buffer.alloc(16)
|
const iv = Buffer.alloc(16)
|
||||||
iv.fill(1)
|
iv.fill(1)
|
||||||
|
|
||||||
crypto.aes.create(key, iv, (err, cipher) => {
|
const cipher = await crypto.aes.create(key, iv)
|
||||||
expect(err).to.not.exist()
|
|
||||||
|
|
||||||
series([
|
await encryptAndDecrypt(cipher)
|
||||||
encryptAndDecrypt(cipher),
|
await encryptAndDecrypt(cipher)
|
||||||
encryptAndDecrypt(cipher),
|
await encryptAndDecrypt(cipher)
|
||||||
encryptAndDecrypt(cipher),
|
await encryptAndDecrypt(cipher)
|
||||||
encryptAndDecrypt(cipher),
|
await encryptAndDecrypt(cipher)
|
||||||
encryptAndDecrypt(cipher)
|
|
||||||
], done)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
Object.keys(bytes).forEach((byte) => {
|
Object.keys(bytes).forEach((byte) => {
|
||||||
it(`${bytes[byte]} - fixed - encrypt and decrypt`, (done) => {
|
it(`${bytes[byte]} - fixed - encrypt and decrypt`, async () => {
|
||||||
const key = Buffer.alloc(parseInt(byte, 10))
|
const key = Buffer.alloc(parseInt(byte, 10))
|
||||||
key.fill(5)
|
key.fill(5)
|
||||||
|
|
||||||
const iv = Buffer.alloc(16)
|
const iv = Buffer.alloc(16)
|
||||||
iv.fill(1)
|
iv.fill(1)
|
||||||
|
|
||||||
crypto.aes.create(key, iv, (err, cipher) => {
|
const cipher = await crypto.aes.create(key, iv)
|
||||||
expect(err).to.not.exist()
|
|
||||||
|
|
||||||
series(fixtures[byte].inputs.map((rawIn, i) => (cb) => {
|
for (let i = 0; i < fixtures[byte].inputs.length; i++) {
|
||||||
|
const rawIn = fixtures[byte].inputs[i]
|
||||||
const input = Buffer.from(rawIn)
|
const input = Buffer.from(rawIn)
|
||||||
const output = Buffer.from(fixtures[byte].outputs[i])
|
const output = Buffer.from(fixtures[byte].outputs[i])
|
||||||
cipher.encrypt(input, (err, res) => {
|
const encrypted = await cipher.encrypt(input)
|
||||||
expect(err).to.not.exist()
|
expect(encrypted).to.have.length(output.length)
|
||||||
expect(res).to.have.length(output.length)
|
expect(encrypted).to.eql(output)
|
||||||
expect(res).to.eql(output)
|
const decrypted = await cipher.decrypt(encrypted)
|
||||||
cipher.decrypt(res, (err, res) => {
|
expect(decrypted).to.eql(input)
|
||||||
expect(err).to.not.exist()
|
}
|
||||||
expect(res).to.eql(input)
|
|
||||||
cb()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}), done)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -74,46 +64,41 @@ describe('AES-CTR', () => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
it(`${bytes[byte]} - go interop - encrypt and decrypt`, (done) => {
|
it(`${bytes[byte]} - go interop - encrypt and decrypt`, async () => {
|
||||||
const key = Buffer.alloc(parseInt(byte, 10))
|
const key = Buffer.alloc(parseInt(byte, 10))
|
||||||
key.fill(5)
|
key.fill(5)
|
||||||
|
|
||||||
const iv = Buffer.alloc(16)
|
const iv = Buffer.alloc(16)
|
||||||
iv.fill(1)
|
iv.fill(1)
|
||||||
|
|
||||||
crypto.aes.create(key, iv, (err, cipher) => {
|
const cipher = await crypto.aes.create(key, iv)
|
||||||
expect(err).to.not.exist()
|
|
||||||
|
|
||||||
series(goFixtures[byte].inputs.map((rawIn, i) => (cb) => {
|
for (let i = 0; i < goFixtures[byte].inputs.length; i++) {
|
||||||
|
const rawIn = goFixtures[byte].inputs[i]
|
||||||
const input = Buffer.from(rawIn)
|
const input = Buffer.from(rawIn)
|
||||||
const output = Buffer.from(goFixtures[byte].outputs[i])
|
const output = Buffer.from(goFixtures[byte].outputs[i])
|
||||||
cipher.encrypt(input, (err, res) => {
|
const encrypted = await cipher.encrypt(input)
|
||||||
expect(err).to.not.exist()
|
expect(encrypted).to.have.length(output.length)
|
||||||
expect(res).to.have.length(output.length)
|
expect(encrypted).to.eql(output)
|
||||||
expect(res).to.be.eql(output)
|
const decrypted = await cipher.decrypt(encrypted)
|
||||||
cipher.decrypt(res, (err, res) => {
|
expect(decrypted).to.eql(input)
|
||||||
expect(err).to.not.exist()
|
}
|
||||||
expect(res).to.be.eql(input)
|
|
||||||
cb()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}), done)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('checks key length', () => {
|
||||||
|
const key = Buffer.alloc(5)
|
||||||
|
const iv = Buffer.alloc(16)
|
||||||
|
return expectErrCode(crypto.aes.create(key, iv), 'ERR_INVALID_KEY_LENGTH')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
function encryptAndDecrypt (cipher) {
|
async function encryptAndDecrypt (cipher) {
|
||||||
const data = Buffer.alloc(100)
|
const data = Buffer.alloc(100)
|
||||||
data.fill(Math.ceil(Math.random() * 100))
|
data.fill(Math.ceil(Math.random() * 100))
|
||||||
return (cb) => {
|
|
||||||
cipher.encrypt(data, (err, res) => {
|
const encrypted = await cipher.encrypt(data)
|
||||||
expect(err).to.not.exist()
|
const decrypted = await cipher.decrypt(encrypted)
|
||||||
cipher.decrypt(res, (err, res) => {
|
|
||||||
expect(err).to.not.exist()
|
expect(decrypted).to.be.eql(data)
|
||||||
expect(res).to.be.eql(data)
|
|
||||||
cb()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
61
test/browser.js
Normal file
61
test/browser.js
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
/* eslint-env mocha */
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
const chai = require('chai')
|
||||||
|
const dirtyChai = require('dirty-chai')
|
||||||
|
const expect = chai.expect
|
||||||
|
chai.use(dirtyChai)
|
||||||
|
const crypto = require('../')
|
||||||
|
const webcrypto = require('../src/webcrypto')
|
||||||
|
|
||||||
|
async function expectMissingWebCrypto (fn) {
|
||||||
|
try {
|
||||||
|
await fn()
|
||||||
|
} catch (err) {
|
||||||
|
expect(err.code).to.equal('ERR_MISSING_WEB_CRYPTO')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
throw new Error('Expected missing web crypto error')
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('Missing web crypto', () => {
|
||||||
|
let webcryptoGet
|
||||||
|
let rsaPrivateKey
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
rsaPrivateKey = await crypto.keys.generateKeyPair('RSA', 512)
|
||||||
|
})
|
||||||
|
|
||||||
|
before(() => {
|
||||||
|
webcryptoGet = webcrypto.get
|
||||||
|
webcrypto.get = () => webcryptoGet({})
|
||||||
|
})
|
||||||
|
|
||||||
|
after(() => {
|
||||||
|
webcrypto.get = webcryptoGet
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should error for hmac create when web crypto is missing', () => {
|
||||||
|
return expectMissingWebCrypto(() => crypto.hmac.create('SHA256', Buffer.from('secret')))
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should error for generate ephemeral key pair when web crypto is missing', () => {
|
||||||
|
return expectMissingWebCrypto(() => crypto.keys.generateEphemeralKeyPair('P-256'))
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should error for generate rsa key pair when web crypto is missing', () => {
|
||||||
|
return expectMissingWebCrypto(() => crypto.keys.generateKeyPair('rsa', 256))
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should error for unmarshal RSA private key when web crypto is missing', () => {
|
||||||
|
return expectMissingWebCrypto(() => crypto.keys.unmarshalPrivateKey(crypto.keys.marshalPrivateKey(rsaPrivateKey)))
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should error for sign RSA private key when web crypto is missing', () => {
|
||||||
|
return expectMissingWebCrypto(() => rsaPrivateKey.sign(Buffer.from('test')))
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should error for verify RSA public key when web crypto is missing', () => {
|
||||||
|
return expectMissingWebCrypto(() => rsaPrivateKey.public.verify(Buffer.from('test'), Buffer.from('test')))
|
||||||
|
})
|
||||||
|
})
|
@@ -8,18 +8,13 @@ const expect = chai.expect
|
|||||||
chai.use(dirtyChai)
|
chai.use(dirtyChai)
|
||||||
const crypto = require('../src')
|
const crypto = require('../src')
|
||||||
const fixtures = require('./fixtures/go-key-rsa')
|
const fixtures = require('./fixtures/go-key-rsa')
|
||||||
|
const { expectErrCode } = require('./util')
|
||||||
|
|
||||||
describe('libp2p-crypto', function () {
|
describe('libp2p-crypto', function () {
|
||||||
this.timeout(20 * 1000)
|
this.timeout(20 * 1000)
|
||||||
let key
|
let key
|
||||||
before((done) => {
|
before(async () => {
|
||||||
crypto.keys.generateKeyPair('RSA', 512, (err, _key) => {
|
key = await crypto.keys.generateKeyPair('RSA', 512)
|
||||||
if (err) {
|
|
||||||
return done(err)
|
|
||||||
}
|
|
||||||
key = _key
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('marshalPublicKey and unmarshalPublicKey', () => {
|
it('marshalPublicKey and unmarshalPublicKey', () => {
|
||||||
@@ -33,71 +28,50 @@ describe('libp2p-crypto', function () {
|
|||||||
}).to.throw()
|
}).to.throw()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('marshalPrivateKey and unmarshalPrivateKey', (done) => {
|
it('marshalPrivateKey and unmarshalPrivateKey', async () => {
|
||||||
expect(() => {
|
expect(() => {
|
||||||
crypto.keys.marshalPrivateKey(key, 'invalid-key-type')
|
crypto.keys.marshalPrivateKey(key, 'invalid-key-type')
|
||||||
}).to.throw()
|
}).to.throw()
|
||||||
|
|
||||||
crypto.keys.unmarshalPrivateKey(crypto.keys.marshalPrivateKey(key), (err, key2) => {
|
const key2 = await crypto.keys.unmarshalPrivateKey(crypto.keys.marshalPrivateKey(key))
|
||||||
if (err) {
|
|
||||||
return done(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(key2.equals(key)).to.be.eql(true)
|
expect(key2.equals(key)).to.be.eql(true)
|
||||||
expect(key2.public.equals(key.public)).to.be.eql(true)
|
expect(key2.public.equals(key.public)).to.be.eql(true)
|
||||||
done()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
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
|
// marshalled keys seem to be slightly different
|
||||||
// unsure as to if this is just a difference in encoding
|
// unsure as to if this is just a difference in encoding
|
||||||
// or a bug
|
// or a bug
|
||||||
describe('go interop', () => {
|
describe('go interop', () => {
|
||||||
it('unmarshals private key', (done) => {
|
it('unmarshals private key', async () => {
|
||||||
crypto.keys.unmarshalPrivateKey(fixtures.private.key, (err, key) => {
|
const key = await crypto.keys.unmarshalPrivateKey(fixtures.private.key)
|
||||||
if (err) {
|
|
||||||
return done(err)
|
|
||||||
}
|
|
||||||
const hash = fixtures.private.hash
|
const hash = fixtures.private.hash
|
||||||
expect(fixtures.private.key).to.eql(key.bytes)
|
expect(fixtures.private.key).to.eql(key.bytes)
|
||||||
|
const digest = await key.hash()
|
||||||
key.hash((err, digest) => {
|
|
||||||
if (err) {
|
|
||||||
return done(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(digest).to.eql(hash)
|
expect(digest).to.eql(hash)
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('unmarshals public key', (done) => {
|
it('unmarshals public key', async () => {
|
||||||
const key = crypto.keys.unmarshalPublicKey(fixtures.public.key)
|
const key = crypto.keys.unmarshalPublicKey(fixtures.public.key)
|
||||||
const hash = fixtures.public.hash
|
const hash = fixtures.public.hash
|
||||||
|
|
||||||
expect(crypto.keys.marshalPublicKey(key)).to.eql(fixtures.public.key)
|
expect(crypto.keys.marshalPublicKey(key)).to.eql(fixtures.public.key)
|
||||||
|
const digest = await key.hash()
|
||||||
key.hash((err, digest) => {
|
|
||||||
if (err) {
|
|
||||||
return done(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(digest).to.eql(hash)
|
expect(digest).to.eql(hash)
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('unmarshal -> marshal, private key', (done) => {
|
it('unmarshal -> marshal, private key', async () => {
|
||||||
crypto.keys.unmarshalPrivateKey(fixtures.private.key, (err, key) => {
|
const key = await crypto.keys.unmarshalPrivateKey(fixtures.private.key)
|
||||||
if (err) {
|
|
||||||
return done(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
const marshalled = crypto.keys.marshalPrivateKey(key)
|
const marshalled = crypto.keys.marshalPrivateKey(key)
|
||||||
expect(marshalled).to.eql(fixtures.private.key)
|
expect(marshalled).to.eql(fixtures.private.key)
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('unmarshal -> marshal, public key', () => {
|
it('unmarshal -> marshal, public key', () => {
|
||||||
@@ -129,7 +103,8 @@ describe('libp2p-crypto', function () {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('throws on invalid hash name', () => {
|
it('throws on invalid hash name', () => {
|
||||||
expect(() => crypto.pbkdf2('password', 'at least 16 character salt', 500, 512 / 8, 'shaX-xxx')).to.throw()
|
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')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@@ -1,11 +1,6 @@
|
|||||||
/* eslint-env mocha */
|
/* eslint-env mocha */
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
const chai = require('chai')
|
|
||||||
const dirtyChai = require('dirty-chai')
|
|
||||||
const expect = chai.expect
|
|
||||||
chai.use(dirtyChai)
|
|
||||||
|
|
||||||
const util = require('util')
|
const util = require('util')
|
||||||
const garbage = [Buffer.from('00010203040506070809', 'hex'), {}, null, false, undefined, true, 1, 0, Buffer.from(''), 'aGVsbG93b3JsZA==', 'helloworld', '']
|
const garbage = [Buffer.from('00010203040506070809', 'hex'), {}, null, false, undefined, true, 1, 0, Buffer.from(''), 'aGVsbG93b3JsZA==', 'helloworld', '']
|
||||||
|
|
||||||
@@ -19,28 +14,19 @@ function doTests (fncName, fnc, num, skipBuffersAndStrings) {
|
|||||||
// skip this garbage because it's a buffer or a string and we were told do do that
|
// skip this garbage because it's a buffer or a string and we were told do do that
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let args = []
|
const args = []
|
||||||
for (let i = 0; i < num; i++) {
|
for (let i = 0; i < num; i++) {
|
||||||
args.push(garbage)
|
args.push(garbage)
|
||||||
}
|
}
|
||||||
it(fncName + '(' + args.map(garbage => util.inspect(garbage)).join(', ') + ')', cb => {
|
it(fncName + '(' + args.map(garbage => util.inspect(garbage)).join(', ') + ')', async () => {
|
||||||
args.push((err, res) => {
|
try {
|
||||||
expect(err).to.exist()
|
await fnc.apply(null, args)
|
||||||
expect(res).to.not.exist()
|
} catch (err) {
|
||||||
cb()
|
return // expected
|
||||||
})
|
}
|
||||||
|
throw new Error('Expected error to be thrown')
|
||||||
fnc.apply(null, args)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = (obj, fncs, num) => {
|
module.exports = { doTests }
|
||||||
describe('returns error via cb instead of crashing', () => {
|
|
||||||
fncs.forEach(fnc => {
|
|
||||||
doTests(fnc, obj[fnc], num)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports.doTests = doTests
|
|
||||||
|
@@ -13,16 +13,10 @@ const hashes = ['SHA1', 'SHA256', 'SHA512']
|
|||||||
|
|
||||||
describe('HMAC', () => {
|
describe('HMAC', () => {
|
||||||
hashes.forEach((hash) => {
|
hashes.forEach((hash) => {
|
||||||
it(`${hash} - sign and verify`, (done) => {
|
it(`${hash} - sign and verify`, async () => {
|
||||||
crypto.hmac.create(hash, Buffer.from('secret'), (err, hmac) => {
|
const hmac = await crypto.hmac.create(hash, Buffer.from('secret'))
|
||||||
expect(err).to.not.exist()
|
const sig = await hmac.digest(Buffer.from('hello world'))
|
||||||
|
|
||||||
hmac.digest(Buffer.from('hello world'), (err, sig) => {
|
|
||||||
expect(err).to.not.exist()
|
|
||||||
expect(sig).to.have.length(hmac.length)
|
expect(sig).to.have.length(hmac.length)
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@@ -15,94 +15,51 @@ const testGarbage = require('../helpers/test-garbage-error-handling')
|
|||||||
describe('ed25519', function () {
|
describe('ed25519', function () {
|
||||||
this.timeout(20 * 1000)
|
this.timeout(20 * 1000)
|
||||||
let key
|
let key
|
||||||
before((done) => {
|
before(async () => {
|
||||||
crypto.keys.generateKeyPair('Ed25519', 512, (err, _key) => {
|
key = await crypto.keys.generateKeyPair('Ed25519', 512)
|
||||||
if (err) return done(err)
|
|
||||||
key = _key
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('generates a valid key', (done) => {
|
it('generates a valid key', async () => {
|
||||||
expect(key).to.be.an.instanceof(ed25519.Ed25519PrivateKey)
|
expect(key).to.be.an.instanceof(ed25519.Ed25519PrivateKey)
|
||||||
|
const digest = await key.hash()
|
||||||
key.hash((err, digest) => {
|
|
||||||
if (err) {
|
|
||||||
return done(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(digest).to.have.length(34)
|
expect(digest).to.have.length(34)
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('generates a valid key from seed', (done) => {
|
it('generates a valid key from seed', async () => {
|
||||||
var seed = crypto.randomBytes(32)
|
var seed = crypto.randomBytes(32)
|
||||||
crypto.keys.generateKeyPairFromSeed('Ed25519', seed, 512, (err, seededkey) => {
|
const seededkey = await crypto.keys.generateKeyPairFromSeed('Ed25519', seed, 512)
|
||||||
if (err) return done(err)
|
|
||||||
expect(seededkey).to.be.an.instanceof(ed25519.Ed25519PrivateKey)
|
expect(seededkey).to.be.an.instanceof(ed25519.Ed25519PrivateKey)
|
||||||
|
const digest = await seededkey.hash()
|
||||||
seededkey.hash((err, digest) => {
|
|
||||||
if (err) {
|
|
||||||
return done(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(digest).to.have.length(34)
|
expect(digest).to.have.length(34)
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('generates the same key from the same seed', (done) => {
|
it('generates the same key from the same seed', async () => {
|
||||||
var seed = crypto.randomBytes(32)
|
const seed = crypto.randomBytes(32)
|
||||||
crypto.keys.generateKeyPairFromSeed('Ed25519', seed, 512, (err, seededkey1) => {
|
const seededkey1 = await crypto.keys.generateKeyPairFromSeed('Ed25519', seed, 512)
|
||||||
if (err) return done(err)
|
const seededkey2 = await crypto.keys.generateKeyPairFromSeed('Ed25519', seed, 512)
|
||||||
crypto.keys.generateKeyPairFromSeed('Ed25519', seed, 512, (err, seededkey2) => {
|
|
||||||
if (err) return done(err)
|
|
||||||
expect(seededkey1.equals(seededkey2)).to.eql(true)
|
expect(seededkey1.equals(seededkey2)).to.eql(true)
|
||||||
expect(seededkey1.public.equals(seededkey2.public)).to.eql(true)
|
expect(seededkey1.public.equals(seededkey2.public)).to.eql(true)
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('generates different keys for different seeds', (done) => {
|
it('generates different keys for different seeds', async () => {
|
||||||
const seed1 = crypto.randomBytes(32)
|
const seed1 = crypto.randomBytes(32)
|
||||||
crypto.keys.generateKeyPairFromSeed('Ed25519', seed1, 512, (err, seededkey1) => {
|
const seededkey1 = await crypto.keys.generateKeyPairFromSeed('Ed25519', seed1, 512)
|
||||||
expect(err).to.not.exist()
|
|
||||||
|
|
||||||
const seed2 = crypto.randomBytes(32)
|
const seed2 = crypto.randomBytes(32)
|
||||||
crypto.keys.generateKeyPairFromSeed('Ed25519', seed2, 512, (err, seededkey2) => {
|
const seededkey2 = await crypto.keys.generateKeyPairFromSeed('Ed25519', seed2, 512)
|
||||||
expect(err).to.not.exist()
|
|
||||||
|
|
||||||
expect(seededkey1.equals(seededkey2)).to.eql(false)
|
expect(seededkey1.equals(seededkey2)).to.eql(false)
|
||||||
expect(seededkey1.public.equals(seededkey2.public))
|
expect(seededkey1.public.equals(seededkey2.public)).to.eql(false)
|
||||||
.to.eql(false)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('signs', (done) => {
|
it('signs', async () => {
|
||||||
const text = crypto.randomBytes(512)
|
const text = crypto.randomBytes(512)
|
||||||
|
const sig = await key.sign(text)
|
||||||
key.sign(text, (err, sig) => {
|
const res = await key.public.verify(text, sig)
|
||||||
expect(err).to.not.exist()
|
|
||||||
|
|
||||||
key.public.verify(text, sig, (err, res) => {
|
|
||||||
expect(err).to.not.exist()
|
|
||||||
expect(res).to.be.eql(true)
|
expect(res).to.be.eql(true)
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('encoding', (done) => {
|
it('encoding', async () => {
|
||||||
const keyMarshal = key.marshal()
|
const keyMarshal = key.marshal()
|
||||||
ed25519.unmarshalEd25519PrivateKey(keyMarshal, (err, key2) => {
|
const key2 = await ed25519.unmarshalEd25519PrivateKey(keyMarshal)
|
||||||
if (err) {
|
|
||||||
return done(err)
|
|
||||||
}
|
|
||||||
const keyMarshal2 = key2.marshal()
|
const keyMarshal2 = key2.marshal()
|
||||||
|
|
||||||
expect(keyMarshal).to.eql(keyMarshal2)
|
expect(keyMarshal).to.eql(keyMarshal2)
|
||||||
@@ -113,17 +70,12 @@ describe('ed25519', function () {
|
|||||||
const pkMarshal2 = pk2.marshal()
|
const pkMarshal2 = pk2.marshal()
|
||||||
|
|
||||||
expect(pkMarshal).to.eql(pkMarshal2)
|
expect(pkMarshal).to.eql(pkMarshal2)
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('key id', (done) => {
|
it('key id', async () => {
|
||||||
key.id((err, id) => {
|
const id = await key.id()
|
||||||
expect(err).to.not.exist()
|
|
||||||
expect(id).to.exist()
|
expect(id).to.exist()
|
||||||
expect(id).to.be.a('string')
|
expect(id).to.be.a('string')
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('key equals', () => {
|
describe('key equals', () => {
|
||||||
@@ -141,54 +93,30 @@ describe('ed25519', function () {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('not equals other key', (done) => {
|
it('not equals other key', async () => {
|
||||||
crypto.keys.generateKeyPair('Ed25519', 512, (err, key2) => {
|
const key2 = await crypto.keys.generateKeyPair('Ed25519', 512)
|
||||||
if (err) return done(err)
|
|
||||||
|
|
||||||
expect(key.equals(key2)).to.eql(false)
|
expect(key.equals(key2)).to.eql(false)
|
||||||
expect(key2.equals(key)).to.eql(false)
|
expect(key2.equals(key)).to.eql(false)
|
||||||
expect(key.public.equals(key2.public)).to.eql(false)
|
expect(key.public.equals(key2.public)).to.eql(false)
|
||||||
expect(key2.public.equals(key.public)).to.eql(false)
|
expect(key2.public.equals(key.public)).to.eql(false)
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('sign and verify', (done) => {
|
it('sign and verify', async () => {
|
||||||
const data = Buffer.from('hello world')
|
const data = Buffer.from('hello world')
|
||||||
key.sign(data, (err, sig) => {
|
const sig = await key.sign(data)
|
||||||
if (err) {
|
const valid = await key.public.verify(data, sig)
|
||||||
return done(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
key.public.verify(data, sig, (err, valid) => {
|
|
||||||
if (err) {
|
|
||||||
return done(err)
|
|
||||||
}
|
|
||||||
expect(valid).to.eql(true)
|
expect(valid).to.eql(true)
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('fails to verify for different data', (done) => {
|
it('fails to verify for different data', async () => {
|
||||||
const data = Buffer.from('hello world')
|
const data = Buffer.from('hello world')
|
||||||
key.sign(data, (err, sig) => {
|
const sig = await key.sign(data)
|
||||||
if (err) {
|
const valid = await key.public.verify(Buffer.from('hello'), sig)
|
||||||
return done(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
key.public.verify(Buffer.from('hello'), sig, (err, valid) => {
|
|
||||||
if (err) {
|
|
||||||
return done(err)
|
|
||||||
}
|
|
||||||
expect(valid).to.be.eql(false)
|
expect(valid).to.be.eql(false)
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('returns error via cb instead of crashing', () => {
|
describe('throws error instead of crashing', () => {
|
||||||
const key = crypto.keys.unmarshalPublicKey(fixtures.verify.publicKey)
|
const key = crypto.keys.unmarshalPublicKey(fixtures.verify.publicKey)
|
||||||
testGarbage.doTests('key.verify', key.verify.bind(key), 2)
|
testGarbage.doTests('key.verify', key.verify.bind(key), 2)
|
||||||
testGarbage.doTests('crypto.keys.unmarshalPrivateKey', crypto.keys.unmarshalPrivateKey.bind(crypto.keys))
|
testGarbage.doTests('crypto.keys.unmarshalPrivateKey', crypto.keys.unmarshalPrivateKey.bind(crypto.keys))
|
||||||
@@ -197,30 +125,20 @@ describe('ed25519', function () {
|
|||||||
describe('go interop', () => {
|
describe('go interop', () => {
|
||||||
let privateKey
|
let privateKey
|
||||||
|
|
||||||
before((done) => {
|
before(async () => {
|
||||||
crypto.keys.unmarshalPrivateKey(fixtures.verify.privateKey, (err, key) => {
|
const key = await crypto.keys.unmarshalPrivateKey(fixtures.verify.privateKey)
|
||||||
expect(err).to.not.exist()
|
|
||||||
privateKey = key
|
privateKey = key
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('verifies with data from go', (done) => {
|
it('verifies with data from go', async () => {
|
||||||
const key = crypto.keys.unmarshalPublicKey(fixtures.verify.publicKey)
|
const key = crypto.keys.unmarshalPublicKey(fixtures.verify.publicKey)
|
||||||
|
const ok = await key.verify(fixtures.verify.data, fixtures.verify.signature)
|
||||||
key.verify(fixtures.verify.data, fixtures.verify.signature, (err, ok) => {
|
|
||||||
expect(err).to.not.exist()
|
|
||||||
expect(ok).to.eql(true)
|
expect(ok).to.eql(true)
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('generates the same signature as go', (done) => {
|
it('generates the same signature as go', async () => {
|
||||||
privateKey.sign(fixtures.verify.data, (err, sig) => {
|
const sig = await privateKey.sign(fixtures.verify.data)
|
||||||
expect(err).to.not.exist()
|
|
||||||
expect(sig).to.eql(fixtures.verify.signature)
|
expect(sig).to.eql(fixtures.verify.signature)
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@@ -6,7 +6,6 @@ const chai = require('chai')
|
|||||||
const dirtyChai = require('dirty-chai')
|
const dirtyChai = require('dirty-chai')
|
||||||
const expect = chai.expect
|
const expect = chai.expect
|
||||||
chai.use(dirtyChai)
|
chai.use(dirtyChai)
|
||||||
const parallel = require('async/parallel')
|
|
||||||
|
|
||||||
const fixtures = require('../fixtures/go-elliptic-key')
|
const fixtures = require('../fixtures/go-elliptic-key')
|
||||||
const crypto = require('../../src')
|
const crypto = require('../../src')
|
||||||
@@ -25,49 +24,50 @@ const secretLengths = {
|
|||||||
|
|
||||||
describe('generateEphemeralKeyPair', () => {
|
describe('generateEphemeralKeyPair', () => {
|
||||||
curves.forEach((curve) => {
|
curves.forEach((curve) => {
|
||||||
it(`generate and shared key ${curve}`, (done) => {
|
it(`generate and shared key ${curve}`, async () => {
|
||||||
parallel([
|
const keys = await Promise.all([
|
||||||
(cb) => crypto.keys.generateEphemeralKeyPair(curve, cb),
|
crypto.keys.generateEphemeralKeyPair(curve),
|
||||||
(cb) => crypto.keys.generateEphemeralKeyPair(curve, cb)
|
crypto.keys.generateEphemeralKeyPair(curve)
|
||||||
], (err, keys) => {
|
])
|
||||||
expect(err).to.not.exist()
|
|
||||||
expect(keys[0].key).to.have.length(lengths[curve])
|
expect(keys[0].key).to.have.length(lengths[curve])
|
||||||
expect(keys[1].key).to.have.length(lengths[curve])
|
expect(keys[1].key).to.have.length(lengths[curve])
|
||||||
|
|
||||||
keys[0].genSharedKey(keys[1].key, (err, shared) => {
|
const shared = await keys[0].genSharedKey(keys[1].key)
|
||||||
expect(err).to.not.exist()
|
|
||||||
expect(shared).to.have.length(secretLengths[curve])
|
expect(shared).to.have.length(secretLengths[curve])
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('go interop', () => {
|
describe('go interop', () => {
|
||||||
it('generates a shared secret', (done) => {
|
it('generates a shared secret', async () => {
|
||||||
const curve = fixtures.curve
|
const curve = fixtures.curve
|
||||||
|
|
||||||
parallel([
|
const keys = await Promise.all([
|
||||||
(cb) => crypto.keys.generateEphemeralKeyPair(curve, cb),
|
crypto.keys.generateEphemeralKeyPair(curve),
|
||||||
(cb) => crypto.keys.generateEphemeralKeyPair(curve, cb)
|
crypto.keys.generateEphemeralKeyPair(curve)
|
||||||
], (err, res) => {
|
])
|
||||||
expect(err).to.not.exist()
|
|
||||||
const alice = res[0]
|
const alice = keys[0]
|
||||||
const bob = res[1]
|
const bob = keys[1]
|
||||||
bob.key = fixtures.bob.public
|
bob.key = fixtures.bob.public
|
||||||
|
|
||||||
parallel([
|
const secrets = await Promise.all([
|
||||||
(cb) => alice.genSharedKey(bob.key, cb),
|
alice.genSharedKey(bob.key),
|
||||||
(cb) => bob.genSharedKey(alice.key, fixtures.bob, cb)
|
bob.genSharedKey(alice.key, fixtures.bob)
|
||||||
], (err, secrets) => {
|
])
|
||||||
expect(err).to.not.exist()
|
|
||||||
|
|
||||||
expect(secrets[0]).to.eql(secrets[1])
|
expect(secrets[0]).to.eql(secrets[1])
|
||||||
expect(secrets[0]).to.have.length(32)
|
expect(secrets[0]).to.have.length(32)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
done()
|
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 dirtyChai = require('dirty-chai')
|
||||||
const expect = chai.expect
|
const expect = chai.expect
|
||||||
chai.use(dirtyChai)
|
chai.use(dirtyChai)
|
||||||
|
const { expectErrCode } = require('../util')
|
||||||
const crypto = require('../../src')
|
const crypto = require('../../src')
|
||||||
const fixtures = require('../fixtures/go-stretch-key')
|
const fixtures = require('../fixtures/go-stretch-key')
|
||||||
|
|
||||||
@@ -16,50 +17,37 @@ describe('keyStretcher', () => {
|
|||||||
let res
|
let res
|
||||||
let secret
|
let secret
|
||||||
|
|
||||||
before((done) => {
|
before(async () => {
|
||||||
crypto.keys.generateEphemeralKeyPair('P-256', (err, _res) => {
|
res = await crypto.keys.generateEphemeralKeyPair('P-256')
|
||||||
if (err) {
|
secret = await res.genSharedKey(res.key)
|
||||||
return done(err)
|
|
||||||
}
|
|
||||||
res = _res
|
|
||||||
res.genSharedKey(res.key, (err, _secret) => {
|
|
||||||
if (err) {
|
|
||||||
return done(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
secret = _secret
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
ciphers.forEach((cipher) => {
|
ciphers.forEach((cipher) => {
|
||||||
hashes.forEach((hash) => {
|
hashes.forEach((hash) => {
|
||||||
it(`${cipher} - ${hash}`, (done) => {
|
it(`${cipher} - ${hash}`, async () => {
|
||||||
crypto.keys.keyStretcher(cipher, hash, secret, (err, keys) => {
|
const keys = await crypto.keys.keyStretcher(cipher, hash, secret)
|
||||||
if (err) {
|
|
||||||
return done(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(keys.k1).to.exist()
|
expect(keys.k1).to.exist()
|
||||||
expect(keys.k2).to.exist()
|
expect(keys.k2).to.exist()
|
||||||
done()
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
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', () => {
|
describe('go interop', () => {
|
||||||
fixtures.forEach((test) => {
|
fixtures.forEach((test) => {
|
||||||
it(`${test.cipher} - ${test.hash}`, (done) => {
|
it(`${test.cipher} - ${test.hash}`, async () => {
|
||||||
const cipher = test.cipher
|
const cipher = test.cipher
|
||||||
const hash = test.hash
|
const hash = test.hash
|
||||||
const secret = test.secret
|
const secret = test.secret
|
||||||
crypto.keys.keyStretcher(cipher, hash, secret, (err, keys) => {
|
const keys = await crypto.keys.keyStretcher(cipher, hash, secret)
|
||||||
if (err) {
|
|
||||||
return done(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(keys.k1.iv).to.be.eql(test.k1.iv)
|
expect(keys.k1.iv).to.be.eql(test.k1.iv)
|
||||||
expect(keys.k1.cipherKey).to.be.eql(test.k1.cipherKey)
|
expect(keys.k1.cipherKey).to.be.eql(test.k1.cipherKey)
|
||||||
@@ -68,8 +56,6 @@ describe('keyStretcher', () => {
|
|||||||
expect(keys.k2.iv).to.be.eql(test.k2.iv)
|
expect(keys.k2.iv).to.be.eql(test.k2.iv)
|
||||||
expect(keys.k2.cipherKey).to.be.eql(test.k2.cipherKey)
|
expect(keys.k2.cipherKey).to.be.eql(test.k2.cipherKey)
|
||||||
expect(keys.k2.macKey).to.be.eql(test.k2.macKey)
|
expect(keys.k2.macKey).to.be.eql(test.k2.macKey)
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@@ -32,23 +32,11 @@ describe('RSA crypto libs', function () {
|
|||||||
rsa = crypto.keys.supportedKeys.rsa
|
rsa = crypto.keys.supportedKeys.rsa
|
||||||
})
|
})
|
||||||
|
|
||||||
it('generates a valid key', (done) => {
|
it('generates a valid key', async () => {
|
||||||
crypto.keys.generateKeyPair('RSA', 512, (err, key) => {
|
const key = await crypto.keys.generateKeyPair('RSA', 512)
|
||||||
if (err) {
|
|
||||||
return done(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(key).to.be.an.instanceof(rsa.RsaPrivateKey)
|
expect(key).to.be.an.instanceof(rsa.RsaPrivateKey)
|
||||||
|
const digest = await key.hash()
|
||||||
key.hash((err, digest) => {
|
|
||||||
if (err) {
|
|
||||||
return done(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(digest).to.have.length(34)
|
expect(digest).to.have.length(34)
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
after(() => {
|
after(() => {
|
||||||
|
@@ -7,6 +7,7 @@ const dirtyChai = require('dirty-chai')
|
|||||||
const expect = chai.expect
|
const expect = chai.expect
|
||||||
chai.use(dirtyChai)
|
chai.use(dirtyChai)
|
||||||
chai.use(require('chai-string'))
|
chai.use(require('chai-string'))
|
||||||
|
const { expectErrCode } = require('../util')
|
||||||
|
|
||||||
const crypto = require('../../src')
|
const crypto = require('../../src')
|
||||||
const rsa = crypto.keys.supportedKeys.rsa
|
const rsa = crypto.keys.supportedKeys.rsa
|
||||||
@@ -18,54 +19,26 @@ describe('RSA', function () {
|
|||||||
this.timeout(20 * 1000)
|
this.timeout(20 * 1000)
|
||||||
let key
|
let key
|
||||||
|
|
||||||
before((done) => {
|
before(async () => {
|
||||||
crypto.keys.generateKeyPair('RSA', 512, (err, _key) => {
|
key = await crypto.keys.generateKeyPair('RSA', 512)
|
||||||
if (err) {
|
|
||||||
return done(err)
|
|
||||||
}
|
|
||||||
key = _key
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('generates a valid key', (done) => {
|
it('generates a valid key', async () => {
|
||||||
expect(key).to.be.an.instanceof(rsa.RsaPrivateKey)
|
expect(key).to.be.an.instanceof(rsa.RsaPrivateKey)
|
||||||
|
const digest = await key.hash()
|
||||||
key.hash((err, digest) => {
|
|
||||||
if (err) {
|
|
||||||
return done(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(digest).to.have.length(34)
|
expect(digest).to.have.length(34)
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('signs', (done) => {
|
it('signs', async () => {
|
||||||
const text = key.genSecret()
|
const text = key.genSecret()
|
||||||
|
const sig = await key.sign(text)
|
||||||
key.sign(text, (err, sig) => {
|
const res = await key.public.verify(text, sig)
|
||||||
if (err) {
|
|
||||||
return done(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
key.public.verify(text, sig, (err, res) => {
|
|
||||||
if (err) {
|
|
||||||
return done(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(res).to.be.eql(true)
|
expect(res).to.be.eql(true)
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('encoding', (done) => {
|
it('encoding', async () => {
|
||||||
const keyMarshal = key.marshal()
|
const keyMarshal = key.marshal()
|
||||||
rsa.unmarshalRsaPrivateKey(keyMarshal, (err, key2) => {
|
const key2 = await rsa.unmarshalRsaPrivateKey(keyMarshal)
|
||||||
if (err) {
|
|
||||||
return done(err)
|
|
||||||
}
|
|
||||||
const keyMarshal2 = key2.marshal()
|
const keyMarshal2 = key2.marshal()
|
||||||
|
|
||||||
expect(keyMarshal).to.eql(keyMarshal2)
|
expect(keyMarshal).to.eql(keyMarshal2)
|
||||||
@@ -76,17 +49,12 @@ describe('RSA', function () {
|
|||||||
const pkMarshal2 = pk2.marshal()
|
const pkMarshal2 = pk2.marshal()
|
||||||
|
|
||||||
expect(pkMarshal).to.eql(pkMarshal2)
|
expect(pkMarshal).to.eql(pkMarshal2)
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('key id', (done) => {
|
it('key id', async () => {
|
||||||
key.id((err, id) => {
|
const id = await key.id()
|
||||||
expect(err).to.not.exist()
|
|
||||||
expect(id).to.exist()
|
expect(id).to.exist()
|
||||||
expect(id).to.be.a('string')
|
expect(id).to.be.a('string')
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('key equals', () => {
|
describe('key equals', () => {
|
||||||
@@ -96,114 +64,97 @@ describe('RSA', function () {
|
|||||||
expect(key.public.equals(key.public)).to.eql(true)
|
expect(key.public.equals(key.public)).to.eql(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('not equals other key', (done) => {
|
it('not equals other key', async () => {
|
||||||
crypto.keys.generateKeyPair('RSA', 512, (err, key2) => {
|
const key2 = await crypto.keys.generateKeyPair('RSA', 512)
|
||||||
if (err) {
|
|
||||||
return done(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(key.equals(key2)).to.eql(false)
|
expect(key.equals(key2)).to.eql(false)
|
||||||
expect(key2.equals(key)).to.eql(false)
|
expect(key2.equals(key)).to.eql(false)
|
||||||
expect(key.public.equals(key2.public)).to.eql(false)
|
expect(key.public.equals(key2.public)).to.eql(false)
|
||||||
expect(key2.public.equals(key.public)).to.eql(false)
|
expect(key2.public.equals(key.public)).to.eql(false)
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('sign and verify', (done) => {
|
it('sign and verify', async () => {
|
||||||
const data = Buffer.from('hello world')
|
const data = Buffer.from('hello world')
|
||||||
key.sign(data, (err, sig) => {
|
const sig = await key.sign(data)
|
||||||
if (err) {
|
const valid = await key.public.verify(data, sig)
|
||||||
return done(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
key.public.verify(data, sig, (err, valid) => {
|
|
||||||
if (err) {
|
|
||||||
return done(err)
|
|
||||||
}
|
|
||||||
expect(valid).to.be.eql(true)
|
expect(valid).to.be.eql(true)
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('fails to verify for different data', (done) => {
|
it('encrypt and decrypt', async () => {
|
||||||
const data = Buffer.from('hello world')
|
const data = Buffer.from('hello world')
|
||||||
key.sign(data, (err, sig) => {
|
const enc = await key.public.encrypt(data)
|
||||||
if (err) {
|
const dec = await key.decrypt(enc)
|
||||||
return done(err)
|
expect(dec).to.be.eql(data)
|
||||||
}
|
})
|
||||||
|
|
||||||
key.public.verify(Buffer.from('hello'), sig, (err, valid) => {
|
it('encrypt decrypt browser/node interop', async () => {
|
||||||
if (err) {
|
const id = await crypto.keys.unmarshalPrivateKey(Buffer.from('CAASqAkwggSkAgEAAoIBAQCk0O+6oNRxhcdZe2GxEDrFBkDV4TZFZnp2ly/dL1cGMBql/8oXPZgei6h7+P5zzfDq2YCfwbjbf0IVY1AshRl6B5VGE1WS+9p1y1OZxJf5os6V1ENnTi6FTcyuBl4BN8dmIKOif0hqgqflaT5OhfYZDXfbJyVQj4vb2+Stu2Xpph3nwqAnTw/7GC/7jrt2Cq6Tu1PoZi36wSwEPYW3eQ1HAYxZjTYYDXl2iyHygnTcbkGRwAQ7vjk+mW7u60zyoolCm9f6Y7c/orJ33DDUocbaGJLlHcfd8bioBwaZy/2m7q43X8pQs0Q1/iwUt0HHZj1YARmHKbh0zR31ciFiV37dAgMBAAECggEADtJBNKnA4QKURj47r0YT2uLwkqtBi6UnDyISalQXAdXyl4n0nPlrhBewC5H9I+HZr+zmTbeIjaiYgz7el1pSy7AB4v7bG7AtWZlyx6mvtwHGjR+8/f3AXjl8Vgv5iSeAdXUq8fJ7SyS7v3wi38HZOzCEXj9bci6ud5ODMYJgLE4gZD0+i1+/V9cpuYfGpS/gLTLEMQLiw/9o8NSZ7sAnxg0UlYhotqaQY23hvXPBOe+0oa95zl2n6XTxCafa3dQl/B6CD1tUq9dhbQew4bxqMq/mhRO9pREEqZ083Uh+u4PTc1BeHgIQaS864pHPb+AY1F7KDvPtHhdojnghp8d70QKBgQDeRYFxo6sd04ohY86Z/i9icVYIyCvfXAKnaMKeGUjK7ou6sDJwFX8W97+CzXpZ/vffsk/l5GGhC50KqrITxHAy/h5IjyDODfps7NMIp0Dm9sO4PWibbw3OOVBRc8w3b3i7I8MrUUA1nLHE1T1HA1rKOTz5jYhE0fi9XKiT1ciKOQKBgQC903w+n9y7M7eaMW7Z5/13kZ7PS3HlM681eaPrk8J4J+c6miFF40/8HOsmarS38v0fgTeKkriPz5A7aLzRHhSiOnp350JNM6c3sLwPEs2qx/CRuWWx1rMERatfDdUH6mvlK6QHu0QgSfQR27EO6a6XvVSJXbvFmimjmtIaz/IpxQKBgQDWJ9HYVAGC81abZTaiWK3/A4QJYhQjWNuVwPICsgnYvI4Uib+PDqcs0ffLZ38DRw48kek5bxpBuJbOuDhro1EXUJCNCJpq7jzixituovd9kTRyR3iKii2bDM2+LPwOTXDdnk9lZRugjCEbrPkleq33Ob7uEtfAty4aBTTHe6uEwQKBgQCB+2q8RyMSXNuADhFlzOFXGrOwJm0bEUUMTPrduRQUyt4e1qOqA3klnXe3mqGcxBpnlEe/76/JacvNom6Ikxx16a0qpYRU8OWz0KU1fR6vrrEgV98241k5t6sdL4+MGA1Bo5xyXtzLb1hdUh3vpDwVU2OrnC+To3iXus/b5EBiMQKBgEI1OaBcFiyjgLGEyFKoZbtzH1mdatTExfrAQqCjOVjQByoMpGhHTXwEaosvyYu63Pa8AJPT7juSGaiKYEJFcXO9BiNyVfmQiqSHJcYeuh+fmO9IlHRHgy5xaIIC00AHS2vC/gXwmXAdPis6BZqDJeiCuOLWJ94QXn8JBT8IgGAI', 'base64'))
|
||||||
return done(err)
|
|
||||||
}
|
const msg = Buffer.from('hello')
|
||||||
|
|
||||||
|
// browser
|
||||||
|
const dec1 = await id.decrypt(Buffer.from('YRFUDx8UjbWSfDS84cDA4WowaaOmd1qFNAv5QutodCKYb9uPtU/tDiAvJzOGu5DCJRo2J0l/35P2weiB4/C2Cb1aZgXKMx/QQC+2jSJiymhqcZaYerjTvkCFwkjCaqthoVo/YXxsaFZ1q7bdTZUDH1TaJR7hWfSyzyPcA8c0w43MIsw16pY8ZaPSclvnCwhoTg1JGjMk6te3we7+wR8QU7VrPhs54mZWxrpu3NQ8xZ6xQqIedsEiNhBUccrCSzYghgsP0Ae/8iKyGyl3U6IegsJNn8jcocvzOJrmU03rgIFPjvuBdaqB38xDSTjbA123KadB28jNoSZh18q/yH3ZIg==', 'base64'))
|
||||||
|
expect(dec1).to.be.eql(msg)
|
||||||
|
// node
|
||||||
|
const dec2 = await id.decrypt(Buffer.from('e6yxssqXsWc27ozDy0PGKtMkCS28KwFyES2Ijz89yiz+w6bSFkNOhHPKplpPzgQEuNoUGdbseKlJFyRYHjIT8FQFBHZM8UgSkgoimbY5on4xSxXs7E5/+twjqKdB7oNveTaTf7JCwaeUYnKSjbiYFEawtMiQE91F8sTT7TmSzOZ48tUhnddAAZ3Ac/O3Z9MSAKOCDipi+JdZtXRT8KimGt36/7hjjosYmPuHR1Xy/yMTL6SMbXtBM3yAuEgbQgP+q/7kHMHji3/JvTpYdIUU+LVtkMusXNasRA+UWG2zAht18vqjFMsm9JTiihZw9jRHD4vxAhf75M992tnC+0ZuQg==', 'base64'))
|
||||||
|
expect(dec2).to.be.eql(msg)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('fails to verify for different data', async () => {
|
||||||
|
const data = Buffer.from('hello world')
|
||||||
|
const sig = await key.sign(data)
|
||||||
|
const valid = await key.public.verify(Buffer.from('hello'), sig)
|
||||||
expect(valid).to.be.eql(false)
|
expect(valid).to.be.eql(false)
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('export and import', () => {
|
describe('export and import', () => {
|
||||||
it('password protected PKCS #8', (done) => {
|
it('password protected PKCS #8', async () => {
|
||||||
key.export('pkcs-8', 'my secret', (err, pem) => {
|
const pem = await key.export('my secret', 'pkcs-8')
|
||||||
expect(err).to.not.exist()
|
|
||||||
expect(pem).to.startsWith('-----BEGIN ENCRYPTED PRIVATE KEY-----')
|
expect(pem).to.startsWith('-----BEGIN ENCRYPTED PRIVATE KEY-----')
|
||||||
crypto.keys.import(pem, 'my secret', (err, clone) => {
|
const clone = await crypto.keys.import(pem, 'my secret')
|
||||||
expect(err).to.not.exist()
|
|
||||||
expect(clone).to.exist()
|
expect(clone).to.exist()
|
||||||
expect(key.equals(clone)).to.eql(true)
|
expect(key.equals(clone)).to.eql(true)
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('defaults to PKCS #8', (done) => {
|
it('defaults to PKCS #8', async () => {
|
||||||
key.export('another secret', (err, pem) => {
|
const pem = await key.export('another secret')
|
||||||
expect(err).to.not.exist()
|
|
||||||
expect(pem).to.startsWith('-----BEGIN ENCRYPTED PRIVATE KEY-----')
|
expect(pem).to.startsWith('-----BEGIN ENCRYPTED PRIVATE KEY-----')
|
||||||
crypto.keys.import(pem, 'another secret', (err, clone) => {
|
const clone = await crypto.keys.import(pem, 'another secret')
|
||||||
expect(err).to.not.exist()
|
|
||||||
expect(clone).to.exist()
|
expect(clone).to.exist()
|
||||||
expect(key.equals(clone)).to.eql(true)
|
expect(key.equals(clone)).to.eql(true)
|
||||||
done()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
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('needs correct password', (done) => {
|
describe('throws error instead of crashing', () => {
|
||||||
key.export('another secret', (err, pem) => {
|
|
||||||
expect(err).to.not.exist()
|
|
||||||
crypto.keys.import(pem, 'not the secret', (err, clone) => {
|
|
||||||
expect(err).to.exist()
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('returns error via cb instead of crashing', () => {
|
|
||||||
const key = crypto.keys.unmarshalPublicKey(fixtures.verify.publicKey)
|
const key = crypto.keys.unmarshalPublicKey(fixtures.verify.publicKey)
|
||||||
testGarbage.doTests('key.verify', key.verify.bind(key), 2, true)
|
testGarbage.doTests('key.verify', key.verify.bind(key), 2, true)
|
||||||
testGarbage.doTests('crypto.keys.unmarshalPrivateKey', crypto.keys.unmarshalPrivateKey.bind(crypto.keys))
|
testGarbage.doTests('crypto.keys.unmarshalPrivateKey', crypto.keys.unmarshalPrivateKey.bind(crypto.keys))
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('go interop', () => {
|
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 key = crypto.keys.unmarshalPublicKey(fixtures.verify.publicKey)
|
||||||
|
const ok = await key.verify(fixtures.verify.data, fixtures.verify.signature)
|
||||||
key.verify(fixtures.verify.data, fixtures.verify.signature, (err, ok) => {
|
|
||||||
if (err) throw err
|
|
||||||
expect(err).to.not.exist()
|
|
||||||
expect(ok).to.equal(true)
|
expect(ok).to.equal(true)
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('openssl interop', () => {
|
describe('openssl interop', () => {
|
||||||
it('can read a private key', (done) => {
|
it('can read a private key', async () => {
|
||||||
/*
|
/*
|
||||||
* Generated with
|
* Generated with
|
||||||
* openssl genpkey -algorithm RSA
|
* openssl genpkey -algorithm RSA
|
||||||
@@ -251,19 +202,14 @@ gnjREs10u7zyqBIZH7KYVgyh27WxLr859ap8cKAH6Fb+UOPtZo3sUeeume60aebn
|
|||||||
4pMwXeXP+LO8NIfRXV8mgrm86g==
|
4pMwXeXP+LO8NIfRXV8mgrm86g==
|
||||||
-----END PRIVATE KEY-----
|
-----END PRIVATE KEY-----
|
||||||
`
|
`
|
||||||
crypto.keys.import(pem, '', (err, key) => {
|
const key = await crypto.keys.import(pem, '')
|
||||||
expect(err).to.not.exist()
|
|
||||||
expect(key).to.exist()
|
expect(key).to.exist()
|
||||||
key.id((err, id) => {
|
const id = await key.id()
|
||||||
expect(err).to.not.exist()
|
|
||||||
expect(id).to.equal('QmfWu2Xp8DZzCkZZzoPB9rcrq4R4RZid6AWE6kmrUAzuHy')
|
expect(id).to.equal('QmfWu2Xp8DZzCkZZzoPB9rcrq4R4RZid6AWE6kmrUAzuHy')
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// AssertionError: expected 'this only supports pkcs5PBES2' to not exist
|
// AssertionError: expected 'this only supports pkcs5PBES2' to not exist
|
||||||
it.skip('can read a private encrypted key (v1)', (done) => {
|
it.skip('can read a private encrypted key (v1)', async () => {
|
||||||
/*
|
/*
|
||||||
* Generated with
|
* Generated with
|
||||||
* openssl genpkey -algorithm RSA
|
* openssl genpkey -algorithm RSA
|
||||||
@@ -290,14 +236,11 @@ mBdkD5r+ixWF174naw53L8U9wF8kiK7pIE1N9TR4USEeovLwX6Ni/2MMDZedOfof
|
|||||||
0uxzo5Y=
|
0uxzo5Y=
|
||||||
-----END ENCRYPTED PRIVATE KEY-----
|
-----END ENCRYPTED PRIVATE KEY-----
|
||||||
`
|
`
|
||||||
crypto.keys.import(pem, 'mypassword', (err, key) => {
|
const key = await crypto.keys.import(pem, 'mypassword')
|
||||||
expect(err).to.not.exist()
|
|
||||||
expect(key).to.exist()
|
expect(key).to.exist()
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can read a private encrypted key (v2 aes-128-cbc)', (done) => {
|
it('can read a private encrypted key (v2 aes-128-cbc)', async () => {
|
||||||
/*
|
/*
|
||||||
* Generated with
|
* Generated with
|
||||||
* openssl genpkey -algorithm RSA
|
* openssl genpkey -algorithm RSA
|
||||||
@@ -325,14 +268,11 @@ ePGbz+UtSb9xczvqpRCOiFLh2MG1dUgWuHazjOtUcVWvilKnkjCMzZ9s1qG0sUDj
|
|||||||
nPyn
|
nPyn
|
||||||
-----END ENCRYPTED PRIVATE KEY-----
|
-----END ENCRYPTED PRIVATE KEY-----
|
||||||
`
|
`
|
||||||
crypto.keys.import(pem, 'mypassword', (err, key) => {
|
const key = await crypto.keys.import(pem, 'mypassword')
|
||||||
expect(err).to.not.exist()
|
|
||||||
expect(key).to.exist()
|
expect(key).to.exist()
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can read a private encrypted key (v2 aes-256-cbc)', (done) => {
|
it('can read a private encrypted key (v2 aes-256-cbc)', async () => {
|
||||||
/*
|
/*
|
||||||
* Generated with
|
* Generated with
|
||||||
* openssl genpkey -algorithm RSA
|
* openssl genpkey -algorithm RSA
|
||||||
@@ -360,14 +300,11 @@ mBUuWAZMpz7njBi7h+JDfmSW/GAaMwrVFC2gef5375R0TejAh+COAjItyoeYEvv8
|
|||||||
DQd8
|
DQd8
|
||||||
-----END ENCRYPTED PRIVATE KEY-----
|
-----END ENCRYPTED PRIVATE KEY-----
|
||||||
`
|
`
|
||||||
crypto.keys.import(pem, 'mypassword', (err, key) => {
|
const key = await crypto.keys.import(pem, 'mypassword')
|
||||||
expect(err).to.not.exist()
|
|
||||||
expect(key).to.exist()
|
expect(key).to.exist()
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can read a private encrypted key (v2 des)', (done) => {
|
it('can read a private encrypted key (v2 des)', async () => {
|
||||||
/*
|
/*
|
||||||
* Generated with
|
* Generated with
|
||||||
* openssl genpkey -algorithm RSA
|
* openssl genpkey -algorithm RSA
|
||||||
@@ -394,14 +331,11 @@ A+qCKbprxyL8SKI5vug2hE+mfC1leXVRtUYm1DnE+oet99bFd0fN20NwTw0rOeRg
|
|||||||
EBqQkwAUXR1tNekF8CWLOrfC/wbLRxVRkayb8bQUfdgukLpz0bgw
|
EBqQkwAUXR1tNekF8CWLOrfC/wbLRxVRkayb8bQUfdgukLpz0bgw
|
||||||
-----END ENCRYPTED PRIVATE KEY-----
|
-----END ENCRYPTED PRIVATE KEY-----
|
||||||
`
|
`
|
||||||
crypto.keys.import(pem, 'mypassword', (err, key) => {
|
const key = await crypto.keys.import(pem, 'mypassword')
|
||||||
expect(err).to.not.exist()
|
|
||||||
expect(key).to.exist()
|
expect(key).to.exist()
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can read a private encrypted key (v2 des3)', (done) => {
|
it('can read a private encrypted key (v2 des3)', async () => {
|
||||||
/*
|
/*
|
||||||
* Generated with
|
* Generated with
|
||||||
* openssl genpkey -algorithm RSA
|
* openssl genpkey -algorithm RSA
|
||||||
@@ -428,11 +362,8 @@ NT2TO3kSzXpQ5M2VjOoHPm2fqxD/js+ThDB3QLi4+C7HqakfiTY1lYzXl9/vayt6
|
|||||||
DUD29r9pYL9ErB9tYko2rat54EY7k7Ts6S5jf+8G7Zz234We1APhvqaG
|
DUD29r9pYL9ErB9tYko2rat54EY7k7Ts6S5jf+8G7Zz234We1APhvqaG
|
||||||
-----END ENCRYPTED PRIVATE KEY-----
|
-----END ENCRYPTED PRIVATE KEY-----
|
||||||
`
|
`
|
||||||
crypto.keys.import(pem, 'mypassword', (err, key) => {
|
const key = await crypto.keys.import(pem, 'mypassword')
|
||||||
expect(err).to.not.exist()
|
|
||||||
expect(key).to.exist()
|
expect(key).to.exist()
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@@ -5,49 +5,36 @@ const chai = require('chai')
|
|||||||
const dirtyChai = require('dirty-chai')
|
const dirtyChai = require('dirty-chai')
|
||||||
const expect = chai.expect
|
const expect = chai.expect
|
||||||
chai.use(dirtyChai)
|
chai.use(dirtyChai)
|
||||||
|
const sinon = require('sinon')
|
||||||
|
|
||||||
const fixtures = require('../fixtures/secp256k1')
|
const fixtures = require('../fixtures/secp256k1')
|
||||||
const crypto = require('../../src')
|
const crypto = require('../../src')
|
||||||
|
|
||||||
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', () => {
|
describe('without libp2p-crypto-secp256k1 module present', () => {
|
||||||
crypto.keys.supportedKeys.secp256k1 = undefined
|
before(() => {
|
||||||
|
sinon.replace(crypto.keys.supportedKeys, 'secp256k1', null)
|
||||||
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) => {
|
after(() => {
|
||||||
crypto.keys.unmarshalPrivateKey(fixtures.pbmPrivateKey, (err, key) => {
|
sinon.restore()
|
||||||
expect(err).to.exist()
|
|
||||||
expect(key).to.not.exist()
|
|
||||||
done()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('fails to generate a secp256k1 key', async () => {
|
||||||
|
try {
|
||||||
|
await crypto.keys.generateKeyPair('secp256k1', 256)
|
||||||
|
} catch (err) {
|
||||||
|
return // expected
|
||||||
|
}
|
||||||
|
throw new Error('Expected error to be thrown')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('fails to unmarshal a secp256k1 private key', async () => {
|
||||||
|
try {
|
||||||
|
await crypto.keys.unmarshalPrivateKey(fixtures.pbmPrivateKey)
|
||||||
|
} catch (err) {
|
||||||
|
return // expected
|
||||||
|
}
|
||||||
|
throw new Error('Expected error to be thrown')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('fails to unmarshal a secp256k1 public key', () => {
|
it('fails to unmarshal a secp256k1 public key', () => {
|
||||||
@@ -58,31 +45,17 @@ describe('without libp2p-crypto-secp256k1 module present', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('with libp2p-crypto-secp256k1 module present', () => {
|
describe('with libp2p-crypto-secp256k1 module present', () => {
|
||||||
let key
|
it('generates a valid key', async () => {
|
||||||
|
const key = await crypto.keys.generateKeyPair('secp256k1', 256)
|
||||||
before((done) => {
|
|
||||||
crypto.keys.supportedKeys.secp256k1 = mockSecp256k1Module
|
|
||||||
crypto.keys.generateKeyPair('secp256k1', 256, (err, _key) => {
|
|
||||||
if (err) return done(err)
|
|
||||||
key = _key
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
after((done) => {
|
|
||||||
delete crypto.keys.secp256k1
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('generates a valid key', (done) => {
|
|
||||||
expect(key).to.exist()
|
expect(key).to.exist()
|
||||||
done()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('protobuf encoding', (done) => {
|
it('protobuf encoding', async () => {
|
||||||
|
const key = await crypto.keys.generateKeyPair('secp256k1', 256)
|
||||||
|
expect(key).to.exist()
|
||||||
|
|
||||||
const keyMarshal = crypto.keys.marshalPrivateKey(key)
|
const keyMarshal = crypto.keys.marshalPrivateKey(key)
|
||||||
crypto.keys.unmarshalPrivateKey(keyMarshal, (err, key2) => {
|
const key2 = await crypto.keys.unmarshalPrivateKey(keyMarshal)
|
||||||
if (err) return done(err)
|
|
||||||
const keyMarshal2 = crypto.keys.marshalPrivateKey(key2)
|
const keyMarshal2 = crypto.keys.marshalPrivateKey(key2)
|
||||||
|
|
||||||
expect(keyMarshal).to.eql(keyMarshal2)
|
expect(keyMarshal).to.eql(keyMarshal2)
|
||||||
@@ -93,7 +66,15 @@ describe('with libp2p-crypto-secp256k1 module present', () => {
|
|||||||
const pkMarshal2 = crypto.keys.marshalPublicKey(pk2)
|
const pkMarshal2 = crypto.keys.marshalPublicKey(pk2)
|
||||||
|
|
||||||
expect(pkMarshal).to.eql(pkMarshal2)
|
expect(pkMarshal).to.eql(pkMarshal2)
|
||||||
done()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('unmarshals a secp256k1 private key', async () => {
|
||||||
|
const key = await crypto.keys.unmarshalPrivateKey(fixtures.pbmPrivateKey)
|
||||||
|
expect(key).to.exist()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('unmarshals a secp256k1 public key', () => {
|
||||||
|
const key = crypto.keys.unmarshalPublicKey(fixtures.pbmPublicKey)
|
||||||
|
expect(key).to.exist()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
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')
|
||||||
|
})
|
||||||
|
})
|
@@ -24,7 +24,7 @@ describe('Util', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('toBase64 zero padding', (done) => {
|
it('toBase64 zero padding', (done) => {
|
||||||
let bnpad = new BN('ff', 16)
|
const bnpad = new BN('ff', 16)
|
||||||
expect(util.toBase64(bnpad, 2)).to.eql('AP8')
|
expect(util.toBase64(bnpad, 2)).to.eql('AP8')
|
||||||
done()
|
done()
|
||||||
})
|
})
|
||||||
|
16
test/util/index.js
Normal file
16
test/util/index.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
'use strict'
|
||||||
|
|
||||||
|
const chai = require('chai')
|
||||||
|
const expect = chai.expect
|
||||||
|
|
||||||
|
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 }
|
Reference in New Issue
Block a user