Compare commits

...

42 Commits

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

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

* chore: fix linting

* chore: remove non jenkins ci

* refactor: use nextTick over setImmediate

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

* chore: update deps

* Update package.json
2018-05-29 14:22:48 +01:00
David Dias
e689a402a3 chore: release version v0.13.0 2018-04-05 17:32:18 +01:00
David Dias
4bd032a6ae chore: update contributors 2018-04-05 17:32:18 +01:00
David Dias
50c61ba46e chore: update deps 2018-04-05 17:30:34 +01:00
David Dias
3a90f70350 chore: release version v0.12.1 2018-02-12 09:27:25 +00:00
David Dias
743c69524c chore: update contributors 2018-02-12 09:27:24 +00:00
David Dias
1a347fa04c chore: update deps 2018-02-12 09:25:50 +00:00
42 changed files with 1009 additions and 1415 deletions

View File

@@ -1,34 +0,0 @@
**/node_modules/
**/*.log
test/repo-tests*
# Logs
logs
*.log
coverage
# Runtime data
pids
*.pid
*.seed
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# node-waf configuration
.lock-wscript
build
# Dependency directory
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
node_modules
test

View File

@@ -1,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
matrix:
cache: npm
stages:
- check
- test
- cov
node_js:
- '10'
- '12'
os:
- linux
- osx
- windows
script: npx nyc -s npm run test:node -- --bail
after_success: npx nyc report --reporter=text-lcov > coverage.lcov && npx codecov
jobs:
include:
- node_js: 6
env: CXX=g++-4.8
- node_js: 8
env: CXX=g++-4.8
# - node_js: stable
# env: CXX=g++-4.8
- stage: check
script:
- npx aegir commitlint --travis
- npx aegir dep-check
- npm run lint
script:
- npm run lint
- npm run test
- npm run coverage
- stage: test
name: chrome
addons:
chrome: stable
script:
- npx aegir test -t browser
before_script:
- export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start
- stage: test
name: firefox
addons:
firefox: latest
script:
- npx aegir test -t browser -- --browsers FirefoxHeadless
after_success:
- npm run coverage-publish
addons:
firefox: 'latest'
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-4.8
notifications:
email: false

View File

@@ -1,3 +1,101 @@
<a name="0.17.0"></a>
# [0.17.0](https://github.com/libp2p/js-libp2p-crypto/compare/v0.16.1...v0.17.0) (2019-07-11)
### Bug Fixes
* **deps:** update to ursa-optiona@0.10 ([26b6217](https://github.com/libp2p/js-libp2p-crypto/commit/26b6217))
* fix links in README ([#148](https://github.com/libp2p/js-libp2p-crypto/issues/148)) ([5cd0e8c](https://github.com/libp2p/js-libp2p-crypto/commit/5cd0e8c))
* put optional args last for key export ([#154](https://github.com/libp2p/js-libp2p-crypto/issues/154)) ([d675670](https://github.com/libp2p/js-libp2p-crypto/commit/d675670))
### Features
* refactor to use async/await ([#131](https://github.com/libp2p/js-libp2p-crypto/issues/131)) ([ad71072](https://github.com/libp2p/js-libp2p-crypto/commit/ad71072))
### BREAKING CHANGES
* key export arguments are now swapped so that the optional format is last
* API refactored to use async/await
feat: WIP use async await
fix: passing tests
chore: update travis node.js versions
fix: skip ursa optional tests on windows
fix: benchmarks
docs: update docs
fix: remove broken and intested private key decrypt
chore: update deps
<a name="0.16.1"></a>
## [0.16.1](https://github.com/libp2p/js-libp2p-crypto/compare/v0.16.0...v0.16.1) (2019-02-26)
<a name="0.16.0"></a>
# [0.16.0](https://github.com/libp2p/js-libp2p-crypto/compare/v0.15.0...v0.16.0) (2019-01-08)
### Bug Fixes
* clean up, bundle size reduction ([8d8294d](https://github.com/libp2p/js-libp2p-crypto/commit/8d8294d))
### BREAKING CHANGES
* getRandomValues method exported from src/keys/rsa-browser.js and src/keys/rsa.js signature has changed from accepting an array to a number for random byte length
<a name="0.15.0"></a>
# [0.15.0](https://github.com/libp2p/js-libp2p-crypto/compare/v0.14.1...v0.15.0) (2019-01-03)
### Features
* nextTick instead of setImmediate, and fix sync in async ([#136](https://github.com/libp2p/js-libp2p-crypto/issues/136)) ([c54ea20](https://github.com/libp2p/js-libp2p-crypto/commit/c54ea20))
<a name="0.14.1"></a>
## [0.14.1](https://github.com/libp2p/js-libp2p-crypto/compare/v0.14.0...v0.14.1) (2018-11-05)
### Bug Fixes
* dont setimmediate when its not needed ([9e57786](https://github.com/libp2p/js-libp2p-crypto/commit/9e57786))
<a name="0.14.0"></a>
# [0.14.0](https://github.com/libp2p/js-libp2p-crypto/compare/v0.13.0...v0.14.0) (2018-09-17)
### Bug Fixes
* windows build ([c7e0409](https://github.com/libp2p/js-libp2p-crypto/commit/c7e0409))
* **lint:** use ~ for ursa-optional version ([e8cbf13](https://github.com/libp2p/js-libp2p-crypto/commit/e8cbf13))
### Features
* use ursa-optional for lightning fast key generation ([b05e77f](https://github.com/libp2p/js-libp2p-crypto/commit/b05e77f))
<a name="0.13.0"></a>
# [0.13.0](https://github.com/libp2p/js-libp2p-crypto/compare/v0.12.1...v0.13.0) (2018-04-05)
<a name="0.12.1"></a>
## [0.12.1](https://github.com/libp2p/js-libp2p-crypto/compare/v0.12.0...v0.12.1) (2018-02-12)
<a name="0.12.0"></a>
# [0.12.0](https://github.com/libp2p/js-libp2p-crypto/compare/v0.11.0...v0.12.0) (2018-01-27)

184
README.md
View File

@@ -1,44 +1,49 @@
# js-libp2p-crypto
[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io)
[![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/)
[![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23ipfs)
[![standard-readme compliant](https://img.shields.io/badge/standard--readme-OK-green.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme)
[![Coverage Status](https://coveralls.io/repos/github/libp2p/js-libp2p-crypto/badge.svg?branch=master)](https://coveralls.io/github/libp2p/js-libp2p-crypto?branch=master)
[![Travis CI](https://travis-ci.org/libp2p/js-libp2p-crypto.svg?branch=master)](https://travis-ci.org/libp2p/js-libp2p-crypto)
[![Circle CI](https://circleci.com/gh/libp2p/js-libp2p-crypto.svg?style=svg)](https://circleci.com/gh/libp2p/js-libp2p-crypto)
[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://protocol.ai)
[![](https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square)](http://libp2p.io/)
[![](https://img.shields.io/badge/freenode-%23libp2p-yellow.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23libp2p)
[![Discourse posts](https://img.shields.io/discourse/https/discuss.libp2p.io/posts.svg)](https://discuss.libp2p.io)
[![](https://img.shields.io/codecov/c/github/libp2p/js-libp2p-crypto.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-crypto)
[![](https://img.shields.io/travis/libp2p/js-libp2p-crypto.svg?style=flat-square)](https://travis-ci.com/libp2p/js-libp2p-crypto)
[![Dependency Status](https://david-dm.org/libp2p/js-libp2p-crypto.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-crypto)
[![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square)](https://github.com/feross/standard)
![](https://img.shields.io/badge/npm-%3E%3D3.0.0-orange.svg?style=flat-square)
![](https://img.shields.io/badge/Node.js-%3E%3D6.0.0-orange.svg?style=flat-square)
> Crypto primitives for libp2p in JavaScript
This repo contains the JavaScript implementation of the crypto primitives needed for libp2p. This is based on this [go implementation](https://github.com/libp2p/go-libp2p-crypto).
## Lead Maintainer
[Friedel Ziegelmayer](https://github.com/dignifiedquire/)
## Table of Contents
- [Install](#install)
- [API](#api)
- [`crypto.hmac`](#hmac)
- [`create(hash, secret, callback)`](#createhash-secret-callback)
- [`digest(data, callback)`](#digestdata-callback)
- [`crypto.aes`](#aes)
- [`create(key, iv, callback)`](#createkey-iv-callback)
- [`encrypt(data, callback)`](#encryptdata-callback)
- [`decrypt(data, callback)`](#decryptdata-callback)
- [`keys`](#keys)
- [`generateKeyPair(type, bits, callback)`](#generatekeypairtype-bits-callback)
- [`generateEphemeralKeyPair(curve, callback)`](#generateephemeralkeypaircurve-callback)
- [`keyStretcher(cipherType, hashType, secret, callback)`](#keystretcherciphertype-hashtype-secret-callback)
- [`marshalPublicKey(key[, type], callback)`](#marshalpublickeykey-type-callback)
- [`unmarshalPublicKey(buf)`](#unmarshalpublickeybuf)
- [`marshalPrivateKey(key[, type])`](#marshalprivatekeykey-type)
- [`unmarshalPrivateKey(buf, callback)`](#unmarshalprivatekeybuf-callback)
- [`import(pem, password, callback)`](#importpem-password-callback)
- [`webcrypto`](#webcrypto)
- [Contribute](#contribute)
- [License](#license)
- [js-libp2p-crypto](#js-libp2p-crypto)
- [Lead Maintainer](#Lead-Maintainer)
- [Table of Contents](#Table-of-Contents)
- [Install](#Install)
- [API](#API)
- [`crypto.aes`](#cryptoaes)
- [`crypto.aes.create(key, iv)`](#cryptoaescreatekey-iv)
- [`decrypt(data)`](#decryptdata)
- [`encrypt(data)`](#encryptdata)
- [`crypto.hmac`](#cryptohmac)
- [`crypto.hmac.create(hash, secret)`](#cryptohmaccreatehash-secret)
- [`digest(data)`](#digestdata)
- [`crypto.keys`](#cryptokeys)
- [`crypto.keys.generateKeyPair(type, bits)`](#cryptokeysgenerateKeyPairtype-bits)
- [`crypto.keys.generateEphemeralKeyPair(curve)`](#cryptokeysgenerateEphemeralKeyPaircurve)
- [`crypto.keys.keyStretcher(cipherType, hashType, secret)`](#cryptokeyskeyStretchercipherType-hashType-secret)
- [`crypto.keys.marshalPublicKey(key, [type])`](#cryptokeysmarshalPublicKeykey-type)
- [`crypto.keys.unmarshalPublicKey(buf)`](#cryptokeysunmarshalPublicKeybuf)
- [`crypto.keys.marshalPrivateKey(key, [type])`](#cryptokeysmarshalPrivateKeykey-type)
- [`crypto.keys.unmarshalPrivateKey(buf)`](#cryptokeysunmarshalPrivateKeybuf)
- [`crypto.keys.import(pem, password)`](#cryptokeysimportpem-password)
- [`crypto.randomBytes(number)`](#cryptorandomBytesnumber)
- [`crypto.pbkdf2(password, salt, iterations, keySize, hash)`](#cryptopbkdf2password-salt-iterations-keySize-hash)
- [Contribute](#Contribute)
- [License](#License)
## Install
@@ -54,75 +59,121 @@ Expoes an interface to AES encryption (formerly Rijndael), as defined in U.S. Fe
This uses `CTR` mode.
#### `crypto.aes.create(key, iv, callback)`
#### `crypto.aes.create(key, iv)`
- `key: Buffer` The key, if length `16` then `AES 128` is used. For length `32`, `AES 256` is used.
- `iv: Buffer` Must have length `16`.
- `callback: Function`
##### `decrypt(data, callback)`
Returns `Promise<{decrypt<Function>, encrypt<Function>}>`
##### `decrypt(data)`
- `data: Buffer`
- `callback: Function`
##### `encrypt(data, callback)`
Returns `Promise<Buffer>`
##### `encrypt(data)`
- `data: Buffer`
- `callback: Function`
```
TODO: Example of using aes
Returns `Promise<Buffer>`
```js
const crypto = require('libp2p-crypto')
// Setting up Key and IV
// A 16 bytes array, 128 Bits, AES-128 is chosen
const key128 = Buffer.from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15])
// A 16 bytes array, 128 Bits,
const IV = Buffer.from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15])
async function main () {
const decryptedMessage = 'Hello, world!'
// Encrypting
const cipher = await crypto.aes.create(key128, IV)
const encryptedBuffer = await cipher.encrypt(Buffer.from(decryptedMessage))
console.log(encryptedBuffer)
// prints: <Buffer 42 f1 67 d9 2e 42 d0 32 9e b1 f8 3c>
// Decrypting
const decipher = await crypto.aes.create(key128, IV)
const decryptedBuffer = await cipher.decrypt(encryptedBuffer)
console.log(decryptedBuffer)
// prints: <Buffer 42 f1 67 d9 2e 42 d0 32 9e b1 f8 3c>
console.log(decryptedBuffer.toString('utf-8'))
// prints: Hello, world!
}
main()
```
### `crypto.hmac`
Exposes an interface to the Keyed-Hash Message Authentication Code (HMAC) as defined in U.S. Federal Information Processing Standards Publication 198. An HMAC is a cryptographic hash that uses a key to sign a message. The receiver verifies the hash by recomputing it using the same key.
#### `crypto.hmac.create(hash, secret, callback)`
#### `crypto.hmac.create(hash, secret)`
- `hash: String`
- `secret: Buffer`
- `callback: Function`
##### `digest(data, callback)`
Returns `Promise<{digest<Function>}>`
##### `digest(data)`
- `data: Buffer`
- `callback: Function`
Returns `Promise<Buffer>`
Example:
```
TODO: Example of using hmac
```js
const crypto = require('libp2p-crypto')
async function main () {
const hash = 'SHA1' // 'SHA256' || 'SHA512'
const hmac = await crypto.hmac.create(hash, Buffer.from('secret'))
const sig = await hmac.digest(Buffer.from('hello world'))
console.log(sig)
}
main()
```
### `crypto.keys`
**Supported Key Types**
The [`generateKeyPair`](#generatekeypairtype-bits-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.
Installing the [libp2p-crypto-secp256k1](https://github.com/libp2p/js-libp2p-crypto-secp256k1) module adds support for the `'secp256k1'` type, which supports ECDSA signatures using the secp256k1 elliptic curve popularized by Bitcoin. This module is not installed by default, and should be explicitly depended on if your project requires secp256k1 support.
### `crypto.keys.generateKeyPair(type, bits, callback)`
### `crypto.keys.generateKeyPair(type, bits)`
- `type: String`, see [Supported Key Types](#supported-key-types) above.
- `bits: Number` Minimum of 1024
- `callback: Function`
Returns `Promise<{privateKey<Buffer>, publicKey<Buffer>}>`
Generates a keypair of the given type and bitsize.
### `crypto.keys.generateEphemeralKeyPair(curve, callback)`
### `crypto.keys.generateEphemeralKeyPair(curve)`
- `curve: String`, one of `'P-256'`, `'P-384'`, `'P-521'` is currently supported
- `callback: Function`
Returns `Promise`
Generates an ephemeral public key and returns a function that will compute the shared secret key.
Focuses only on ECDH now, but can be made more general in the future.
Calls back with an object of the form
Resolves to an object of the form:
```js
{
@@ -131,16 +182,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'`
- `hashType: String`, one of `'SHA1'`, `SHA256`, `SHA512`
- `secret: Buffer`
- `callback: Function`
Returns `Promise`
Generates a set of keys for each party by stretching the shared key.
Calls back with an object of the form:
Resolves to an object of the form:
```js
{
@@ -157,10 +209,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`
- `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.
@@ -168,27 +222,33 @@ Converts a public key object into a protobuf serialized public key.
- `buf: Buffer`
Converts a protobuf serialized public key into its representative object.
Returns `RsaPublicKey|Ed25519PublicKey|Secp256k1PublicKey`
### `crypto.keys.marshalPrivateKey(key[, type])`
Converts a protobuf serialized public key into its representative object.
### `crypto.keys.marshalPrivateKey(key, [type])`
- `key: keys.rsa.RsaPrivateKey | keys.ed25519.Ed25519PrivateKey | require('libp2p-crypto-secp256k1').Secp256k1PrivateKey`
- `type: String`, see [Supported Key Types](#supported-key-types) above.
Returns `Buffer`
Converts a private key object into a protobuf serialized private key.
### `crypto.keys.unmarshalPrivateKey(buf, callback)`
### `crypto.keys.unmarshalPrivateKey(buf)`
- `buf: Buffer`
- `callback: Function`
Returns `Promise<RsaPrivateKey|Ed25519PrivateKey|Secp256k1PrivateKey>`
Converts a protobuf serialized private key into its representative object.
### `crypto.keys.import(pem, password, callback)`
### `crypto.keys.import(pem, password)`
- `pem: string`
- `password: string`
- `callback: Function`
Returns `Promise<RsaPrivateKey>`
Converts a PEM password protected private key into its representative object.
@@ -196,6 +256,8 @@ Converts a PEM password protected private key into its representative object.
- `number: Number`
Returns `Buffer`
Generates a Buffer with length `number` populated by random bytes.
### `crypto.pbkdf2(password, salt, iterations, keySize, hash)`

View File

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

View File

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

View File

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

View File

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

2
ci/Jenkinsfile vendored
View File

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

View File

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

View File

@@ -1,14 +1,19 @@
{
"name": "libp2p-crypto",
"version": "0.12.0",
"version": "0.17.0",
"description": "Crypto primitives for libp2p",
"main": "src/index.js",
"leadMaintainer": "Friedel Ziegelmayer <dignifiedquire@gmail.com>",
"browser": {
"./src/hmac/index.js": "./src/hmac/index-browser.js",
"./src/keys/ecdh.js": "./src/keys/ecdh-browser.js",
"./src/aes/ciphers.js": "./src/aes/ciphers-browser.js",
"./src/keys/rsa.js": "./src/keys/rsa-browser.js"
},
"files": [
"src",
"dist"
],
"scripts": {
"lint": "aegir lint",
"build": "aegir build",
@@ -19,7 +24,8 @@
"release": "aegir release",
"release-minor": "aegir release --type minor",
"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": "bundlesize -f dist/index.min.js -s 139kB"
},
"keywords": [
"IPFS",
@@ -27,38 +33,36 @@
"crypto",
"rsa"
],
"author": "Friedel Ziegelmayer <dignifiedquire@gmail.com>",
"license": "MIT",
"dependencies": {
"asn1.js": "^5.0.0",
"async": "^2.6.0",
"browserify-aes": "^1.1.1",
"asmcrypto.js": "^2.3.2",
"asn1.js": "^5.0.1",
"bn.js": "^5.0.0",
"browserify-aes": "^1.2.0",
"bs58": "^4.0.1",
"iso-random-stream": "^1.1.0",
"keypair": "^1.0.1",
"libp2p-crypto-secp256k1": "~0.2.2",
"multihashing-async": "~0.4.7",
"node-forge": "^0.7.1",
"pem-jwk": "^1.5.1",
"libp2p-crypto-secp256k1": "~0.4.0",
"multihashing-async": "~0.7.0",
"node-forge": "~0.8.5",
"pem-jwk": "^2.0.0",
"protons": "^1.0.1",
"rsa-pem-to-jwk": "^1.1.3",
"tweetnacl": "^1.0.0",
"webcrypto-shim": "github:dignifiedquire/webcrypto-shim#master"
"tweetnacl": "^1.0.1",
"ursa-optional": "~0.10.0"
},
"devDependencies": {
"aegir": "^12.3.0",
"aegir": "^19.0.5",
"benchmark": "^2.1.4",
"chai": "^4.1.2",
"chai-string": "^1.4.0",
"bundlesize": "~0.18.0",
"chai": "^4.2.0",
"chai-string": "^1.5.0",
"dirty-chai": "^2.0.1",
"pre-commit": "^1.2.2"
"sinon": "^7.3.2"
},
"pre-commit": [
"lint",
"test"
],
"engines": {
"node": ">=6.0.0",
"npm": ">=3.0.0"
"node": ">=10.0.0",
"npm": ">=6.0.0"
},
"repository": {
"type": "git",
@@ -69,17 +73,26 @@
},
"homepage": "https://github.com/libp2p/js-libp2p-crypto",
"contributors": [
"Alan Shaw <alan.shaw@protocol.ai>",
"Alberto Elias <hi@albertoelias.me>",
"Arve Knudsen <arve.knudsen@gmail.com>",
"David Dias <daviddias.p@gmail.com>",
"Dmitriy Ryajov <dryajov@gmail.com>",
"Friedel Ziegelmayer <dignifiedquire@gmail.com>",
"Greenkeeper <support@greenkeeper.io>",
"Hugo Dias <hugomrdias@gmail.com>",
"Jack Kleeman <jackkleeman@gmail.com>",
"Jacob Heun <jacobheun@gmail.com>",
"Joao Santos <jrmsantos15@gmail.com>",
"Maciej Krüger <mkg20001@gmail.com>",
"Richard Littauer <richard.littauer@gmail.com>",
"Richard Schneider <makaretu@gmail.com>",
"Tom Swindell <t.swindell@rubyx.co.uk>",
"Vasco Santos <vasco.santos@ua.pt>",
"Victor Bjelkholm <victorbjelkholm@gmail.com>",
"Yusef Napora <yusef@napora.org>",
"dignifiedquire <dignifiedquire@users.noreply.github.com>",
"dirkmc <dirkmdev@gmail.com>",
"greenkeeper[bot] <greenkeeper[bot]@users.noreply.github.com>",
"nikuda <nikuda@gmail.com>"
]

View File

@@ -1,13 +1,10 @@
'use strict'
const asm = require('asmcrypto.js')
const setImmediate = require('async/setImmediate')
exports.create = function (key, iv, callback) {
const done = (err, res) => setImmediate(() => callback(err, res))
exports.create = async function (key, iv) { // eslint-disable-line require-await
if (key.length !== 16 && key.length !== 32) {
return done(new Error('Invalid key length'))
throw new Error('Invalid key length')
}
const enc = new asm.AES_CTR.Encrypt({
@@ -20,36 +17,18 @@ exports.create = function (key, iv, callback) {
})
const res = {
encrypt (data, cb) {
const done = (err, res) => setImmediate(() => cb(err, res))
let res
try {
res = Buffer.from(
enc.process(data).result
)
} catch (err) {
return done(err)
}
done(null, res)
async encrypt (data) { // eslint-disable-line require-await
return Buffer.from(
enc.process(data).result
)
},
decrypt (data, cb) {
const done = (err, res) => setImmediate(() => cb(err, res))
let res
try {
res = Buffer.from(
dec.process(data).result
)
} catch (err) {
return done(err)
}
done(null, res)
async decrypt (data) { // eslint-disable-line require-await
return Buffer.from(
dec.process(data).result
)
}
}
done(null, res)
return res
}

View File

@@ -7,24 +7,24 @@ const CIPHER_MODES = {
32: 'aes-256-ctr'
}
exports.create = function (key, iv, callback) {
exports.create = async function (key, iv) { // eslint-disable-line require-await
const mode = CIPHER_MODES[key.length]
if (!mode) {
return callback(new Error('Invalid key length'))
throw new Error('Invalid key length')
}
const cipher = ciphers.createCipheriv(mode, key, iv)
const decipher = ciphers.createDecipheriv(mode, key, iv)
const res = {
encrypt (data, cb) {
cb(null, cipher.update(data))
async encrypt (data) { // eslint-disable-line require-await
return cipher.update(data)
},
decrypt (data, cb) {
cb(null, decipher.update(data))
async decrypt (data) { // eslint-disable-line require-await
return decipher.update(data)
}
}
callback(null, res)
return res
}

View File

@@ -1,8 +1,6 @@
'use strict'
const nodeify = require('../nodeify')
const crypto = require('../webcrypto.js')()
const webcrypto = require('../webcrypto.js')
const lengths = require('./lengths')
const hashTypes = {
@@ -11,29 +9,28 @@ const hashTypes = {
SHA512: 'SHA-512'
}
const sign = (key, data, cb) => {
nodeify(crypto.subtle.sign({name: 'HMAC'}, key, data)
.then((raw) => Buffer.from(raw)), cb)
const sign = async (key, data) => {
return Buffer.from(await webcrypto.subtle.sign({ name: 'HMAC' }, key, data))
}
exports.create = function (hashType, secret, callback) {
exports.create = async function (hashType, secret) {
const hash = hashTypes[hashType]
nodeify(crypto.subtle.importKey(
const key = await webcrypto.subtle.importKey(
'raw',
secret,
{
name: 'HMAC',
hash: {name: hash}
hash: { name: hash }
},
false,
['sign']
).then((key) => {
return {
digest (data, cb) {
sign(key, data, cb)
},
length: lengths[hashType]
}
}), callback)
)
return {
async digest (data) { // eslint-disable-line require-await
return sign(key, data)
},
length: lengths[hashType]
}
}

View File

@@ -3,19 +3,15 @@
const crypto = require('crypto')
const lengths = require('./lengths')
exports.create = function (hash, secret, callback) {
exports.create = async function (hash, secret) { // eslint-disable-line require-await
const res = {
digest (data, cb) {
async digest (data) { // eslint-disable-line require-await
const hmac = crypto.createHmac(hash.toLowerCase(), secret)
hmac.update(data)
setImmediate(() => {
cb(null, hmac.digest())
})
return hmac.digest()
},
length: lengths[hash]
}
callback(null, res)
return res
}

View File

@@ -4,8 +4,6 @@ const hmac = require('./hmac')
const aes = require('./aes')
const keys = require('./keys')
exports = module.exports
exports.aes = aes
exports.hmac = hmac
exports.keys = keys

View File

@@ -1,12 +1,8 @@
'use strict'
const webcrypto = require('../webcrypto.js')()
const nodeify = require('../nodeify')
const webcrypto = require('../webcrypto.js')
const BN = require('asn1.js').bignum
const util = require('../util')
const toBase64 = util.toBase64
const toBn = util.toBn
const { toBase64, toBn } = require('../util')
const bits = {
'P-256': 256,
@@ -14,72 +10,66 @@ const bits = {
'P-521': 521
}
exports.generateEphmeralKeyPair = function (curve, callback) {
nodeify(webcrypto.subtle.generateKey(
exports.generateEphmeralKeyPair = async function (curve) {
const pair = await webcrypto.subtle.generateKey(
{
name: 'ECDH',
namedCurve: curve
},
true,
['deriveBits']
).then((pair) => {
// forcePrivate is used for testing only
const genSharedKey = (theirPub, forcePrivate, cb) => {
if (typeof forcePrivate === 'function') {
cb = forcePrivate
forcePrivate = undefined
}
)
let privateKey
// forcePrivate is used for testing only
const genSharedKey = async (theirPub, forcePrivate) => {
let privateKey
if (forcePrivate) {
privateKey = webcrypto.subtle.importKey(
'jwk',
unmarshalPrivateKey(curve, forcePrivate),
{
name: 'ECDH',
namedCurve: curve
},
false,
['deriveBits']
)
} else {
privateKey = Promise.resolve(pair.privateKey)
}
const keys = Promise.all([
webcrypto.subtle.importKey(
'jwk',
unmarshalPublicKey(curve, theirPub),
{
name: 'ECDH',
namedCurve: curve
},
false,
[]
),
privateKey
])
nodeify(keys.then((keys) => webcrypto.subtle.deriveBits(
if (forcePrivate) {
privateKey = await webcrypto.subtle.importKey(
'jwk',
unmarshalPrivateKey(curve, forcePrivate),
{
name: 'ECDH',
namedCurve: curve,
public: keys[0]
namedCurve: curve
},
keys[1],
bits[curve]
)).then((bits) => Buffer.from(bits)), cb)
false,
['deriveBits']
)
} else {
privateKey = pair.privateKey
}
return webcrypto.subtle.exportKey('jwk', pair.publicKey)
.then((publicKey) => {
return {
key: marshalPublicKey(publicKey),
genSharedKey
}
})
}), callback)
const keys = [
await webcrypto.subtle.importKey(
'jwk',
unmarshalPublicKey(curve, theirPub),
{
name: 'ECDH',
namedCurve: curve
},
false,
[]
),
privateKey
]
return Buffer.from(await webcrypto.subtle.deriveBits(
{
name: 'ECDH',
namedCurve: curve,
public: keys[0]
},
keys[1],
bits[curve]
))
}
const publicKey = await webcrypto.subtle.exportKey('jwk', pair.publicKey)
return {
key: marshalPublicKey(publicKey),
genSharedKey
}
}
const curveLengths = {

View File

@@ -1,7 +1,6 @@
'use strict'
const crypto = require('crypto')
const setImmediate = require('async/setImmediate')
const curves = {
'P-256': 'prime256v1',
@@ -9,33 +8,21 @@ const curves = {
'P-521': 'secp521r1'
}
exports.generateEphmeralKeyPair = function (curve, callback) {
exports.generateEphmeralKeyPair = async function (curve) { // eslint-disable-line require-await
if (!curves[curve]) {
return callback(new Error(`Unkown curve: ${curve}`))
throw new Error(`Unkown curve: ${curve}`)
}
const ecdh = crypto.createECDH(curves[curve])
ecdh.generateKeys()
setImmediate(() => callback(null, {
return {
key: ecdh.getPublicKey(),
genSharedKey (theirPub, forcePrivate, cb) {
if (typeof forcePrivate === 'function') {
cb = forcePrivate
forcePrivate = null
}
async genSharedKey (theirPub, forcePrivate) { // eslint-disable-line require-await
if (forcePrivate) {
ecdh.setPrivateKey(forcePrivate.private)
}
let secret
try {
secret = ecdh.computeSecret(theirPub)
} catch (err) {
return cb(err)
}
setImmediate(() => cb(null, secret))
return ecdh.computeSecret(theirPub)
}
}))
}
}

View File

@@ -12,9 +12,8 @@ class Ed25519PublicKey {
this._key = ensureKey(key, crypto.publicKeyLength)
}
verify (data, sig, callback) {
ensure(callback)
crypto.hashAndVerify(this._key, sig, data, callback)
async verify (data, sig) { // eslint-disable-line require-await
return crypto.hashAndVerify(this._key, sig, data)
}
marshal () {
@@ -32,9 +31,8 @@ class Ed25519PublicKey {
return this.bytes.equals(key.bytes)
}
hash (callback) {
ensure(callback)
multihashing(this.bytes, 'sha2-256', callback)
async hash () { // eslint-disable-line require-await
return multihashing(this.bytes, 'sha2-256')
}
}
@@ -46,9 +44,8 @@ class Ed25519PrivateKey {
this._publicKey = ensureKey(publicKey, crypto.publicKeyLength)
}
sign (message, callback) {
ensure(callback)
crypto.hashAndSign(this._key, message, callback)
async sign (message) { // eslint-disable-line require-await
return crypto.hashAndSign(this._key, message)
}
get public () {
@@ -74,9 +71,8 @@ class Ed25519PrivateKey {
return this.bytes.equals(key.bytes)
}
hash (callback) {
ensure(callback)
multihashing(this.bytes, 'sha2-256', callback)
async hash () { // eslint-disable-line require-await
return multihashing(this.bytes, 'sha2-256')
}
/**
@@ -86,28 +82,19 @@ class Ed25519PrivateKey {
* The public key is a protobuf encoding containing a type and the DER encoding
* of the PKCS SubjectPublicKeyInfo.
*
* @param {function(Error, id)} callback
* @returns {undefined}
* @returns {Promise<String>}
*/
id (callback) {
this.public.hash((err, hash) => {
if (err) {
return callback(err)
}
callback(null, bs58.encode(hash))
})
async id () {
const hash = await this.public.hash()
return bs58.encode(hash)
}
}
function unmarshalEd25519PrivateKey (bytes, callback) {
try {
bytes = ensureKey(bytes, crypto.privateKeyLength + crypto.publicKeyLength)
} catch (err) {
return callback(err)
}
function unmarshalEd25519PrivateKey (bytes) {
bytes = ensureKey(bytes, crypto.privateKeyLength + crypto.publicKeyLength)
const privateKeyBytes = bytes.slice(0, crypto.privateKeyLength)
const publicKeyBytes = bytes.slice(crypto.privateKeyLength, bytes.length)
callback(null, new Ed25519PrivateKey(privateKeyBytes, publicKeyBytes))
return new Ed25519PrivateKey(privateKeyBytes, publicKeyBytes)
}
function unmarshalEd25519PublicKey (bytes) {
@@ -115,52 +102,14 @@ function unmarshalEd25519PublicKey (bytes) {
return new Ed25519PublicKey(bytes)
}
function generateKeyPair (_bits, cb) {
if (cb === undefined && typeof _bits === 'function') {
cb = _bits
}
crypto.generateKey((err, keys) => {
if (err) {
return cb(err)
}
let privkey
try {
privkey = new Ed25519PrivateKey(keys.secretKey, keys.publicKey)
} catch (err) {
cb(err)
return
}
cb(null, privkey)
})
async function generateKeyPair () {
const { secretKey, publicKey } = await crypto.generateKey()
return new Ed25519PrivateKey(secretKey, publicKey)
}
function generateKeyPairFromSeed (seed, _bits, cb) {
if (cb === undefined && typeof _bits === 'function') {
cb = _bits
}
crypto.generateKeyFromSeed(seed, (err, keys) => {
if (err) {
return cb(err)
}
let privkey
try {
privkey = new Ed25519PrivateKey(keys.secretKey, keys.publicKey)
} catch (err) {
cb(err)
return
}
cb(null, privkey)
})
}
function ensure (cb) {
if (typeof cb !== 'function') {
throw new Error('callback is required')
}
async function generateKeyPairFromSeed (seed) {
const { secretKey, publicKey } = await crypto.generateKeyFromSeed(seed)
return new Ed25519PrivateKey(secretKey, publicKey)
}
function ensureKey (key, length) {

View File

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

View File

@@ -6,6 +6,4 @@ const ecdh = require('./ecdh')
// the shared secret key.
//
// Focuses only on ECDH now, but can be made more general in the future.
module.exports = (curve, callback) => {
ecdh.generateEphmeralKeyPair(curve, callback)
}
module.exports = async (curve) => ecdh.generateEphmeralKeyPair(curve) // eslint-disable-line require-await

View File

@@ -2,7 +2,10 @@
const protobuf = require('protons')
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')
exports = module.exports
@@ -24,27 +27,27 @@ exports.keyStretcher = require('./key-stretcher')
exports.generateEphemeralKeyPair = require('./ephemeral-keys')
// 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()]
if (!key) {
return cb(new Error('invalid or unsupported key type'))
throw new Error('invalid or unsupported key type')
}
key.generateKeyPair(bits, cb)
return key.generateKeyPair(bits)
}
// Generates a keypair of the given type and bitsize
// 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()]
if (!key) {
return cb(new Error('invalid or unsupported key type'))
throw new Error('invalid or unsupported key type')
}
if (type.toLowerCase() !== 'ed25519') {
return cb(new Error('Seed key derivation is unimplemented for RSA or secp256k1'))
throw new Error('Seed key derivation is unimplemented for RSA or secp256k1')
}
key.generateKeyPairFromSeed(seed, bits, cb)
return key.generateKeyPairFromSeed(seed, bits)
}
// Converts a protobuf serialized public key into its
@@ -81,29 +84,23 @@ exports.marshalPublicKey = (key, type) => {
// Converts a protobuf serialized private key into its
// representative object
exports.unmarshalPrivateKey = (buf, callback) => {
let decoded
try {
decoded = keysPBM.PrivateKey.decode(buf)
} catch (err) {
return callback(err)
}
exports.unmarshalPrivateKey = async (buf) => { // eslint-disable-line require-await
const decoded = keysPBM.PrivateKey.decode(buf)
const data = decoded.Data
switch (decoded.Type) {
case keysPBM.KeyType.RSA:
return supportedKeys.rsa.unmarshalRsaPrivateKey(data, callback)
return supportedKeys.rsa.unmarshalRsaPrivateKey(data)
case keysPBM.KeyType.Ed25519:
return supportedKeys.ed25519.unmarshalEd25519PrivateKey(data, callback)
return supportedKeys.ed25519.unmarshalEd25519PrivateKey(data)
case keysPBM.KeyType.Secp256k1:
if (supportedKeys.secp256k1) {
return supportedKeys.secp256k1.unmarshalSecp256k1PrivateKey(data, callback)
return supportedKeys.secp256k1.unmarshalSecp256k1PrivateKey(data)
} else {
return callback(new Error('secp256k1 support requires libp2p-crypto-secp256k1 package'))
throw new Error('secp256k1 support requires libp2p-crypto-secp256k1 package')
}
default:
callback(new Error('invalid or unsupported key type'))
throw new Error('invalid or unsupported key type')
}
}
@@ -117,16 +114,12 @@ exports.marshalPrivateKey = (key, type) => {
return key.bytes
}
exports.import = (pem, password, callback) => {
try {
const key = forge.pki.decryptRsaPrivateKey(pem, password)
if (key === null) {
throw new Error('Cannot read the key, most likely the password is wrong or not a RSA key')
}
let der = forge.asn1.toDer(forge.pki.privateKeyToAsn1(key))
der = Buffer.from(der.getBytes(), 'binary')
return supportedKeys.rsa.unmarshalRsaPrivateKey(der, callback)
} catch (err) {
callback(err)
exports.import = async (pem, password) => { // eslint-disable-line require-await
const key = forge.pki.decryptRsaPrivateKey(pem, password)
if (key === null) {
throw new Error('Cannot read the key, most likely the password is wrong or not a RSA key')
}
let der = forge.asn1.toDer(forge.pki.privateKeyToAsn1(key))
der = Buffer.from(der.getBytes(), 'binary')
return supportedKeys.rsa.unmarshalRsaPrivateKey(der)
}

View File

@@ -1,6 +1,5 @@
'use strict'
const whilst = require('async/whilst')
const hmac = require('../hmac')
const cipherMap = {
@@ -20,15 +19,15 @@ const cipherMap = {
// Generates a set of keys for each party by stretching the shared key.
// (myIV, theirIV, myCipherKey, theirCipherKey, myMACKey, theirMACKey)
module.exports = (cipherType, hash, secret, callback) => {
module.exports = async (cipherType, hash, secret) => {
const cipher = cipherMap[cipherType]
if (!cipher) {
return callback(new Error('unkown cipherType passed'))
throw new Error('unkown cipherType passed')
}
if (!hash) {
return callback(new Error('unkown hashType passed'))
throw new Error('unkown hashType passed')
}
const cipherKeySize = cipher.keySize
@@ -37,72 +36,38 @@ module.exports = (cipherType, hash, secret, callback) => {
const seed = Buffer.from('key expansion')
const resultLength = 2 * (ivSize + cipherKeySize + hmacKeySize)
hmac.create(hash, secret, (err, m) => {
if (err) {
return callback(err)
const m = await hmac.create(hash, secret)
let a = await m.digest(seed)
let result = []
let j = 0
while (j < resultLength) {
const b = await m.digest(Buffer.concat([a, seed]))
let todo = b.length
if (j + todo > resultLength) {
todo = resultLength - j
}
m.digest(seed, (err, a) => {
if (err) {
return callback(err)
}
result.push(b)
j += todo
a = await m.digest(a)
}
let result = []
let j = 0
const half = resultLength / 2
const resultBuffer = Buffer.concat(result)
const r1 = resultBuffer.slice(0, half)
const r2 = resultBuffer.slice(half, resultLength)
whilst(
() => j < resultLength,
stretch,
finish
)
function stretch (cb) {
m.digest(Buffer.concat([a, seed]), (err, b) => {
if (err) {
return cb(err)
}
let todo = b.length
if (j + todo > resultLength) {
todo = resultLength - j
}
result.push(b)
j += todo
m.digest(a, (err, _a) => {
if (err) {
return cb(err)
}
a = _a
cb()
})
})
}
function finish (err) {
if (err) {
return callback(err)
}
const half = resultLength / 2
const resultBuffer = Buffer.concat(result)
const r1 = resultBuffer.slice(0, half)
const r2 = resultBuffer.slice(half, resultLength)
const createKey = (res) => ({
iv: res.slice(0, ivSize),
cipherKey: res.slice(ivSize, ivSize + cipherKeySize),
macKey: res.slice(ivSize + cipherKeySize)
})
callback(null, {
k1: createKey(r1),
k2: createKey(r2)
})
}
})
const createKey = (res) => ({
iv: res.slice(0, ivSize),
cipherKey: res.slice(ivSize, ivSize + cipherKeySize),
macKey: res.slice(ivSize + cipherKeySize)
})
return {
k1: createKey(r1),
k2: createKey(r2)
}
}

View File

@@ -12,4 +12,4 @@ message PublicKey {
message PrivateKey {
required KeyType Type = 1;
required bytes Data = 2;
}`
}`

View File

@@ -1,95 +1,100 @@
'use strict'
const nodeify = require('../nodeify')
const webcrypto = require('../webcrypto.js')()
const webcrypto = require('../webcrypto.js')
const randomBytes = require('../random-bytes')
exports.utils = require('./rsa-utils')
exports.generateKey = function (bits, callback) {
nodeify(webcrypto.subtle.generateKey(
exports.generateKey = async function (bits) {
const pair = await webcrypto.subtle.generateKey(
{
name: 'RSASSA-PKCS1-v1_5',
modulusLength: bits,
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
hash: {name: 'SHA-256'}
hash: { name: 'SHA-256' }
},
true,
['sign', 'verify']
)
.then(exportKey)
.then((keys) => ({
privateKey: keys[0],
publicKey: keys[1]
})), callback)
const keys = await exportKey(pair)
return {
privateKey: keys[0],
publicKey: keys[1]
}
}
// Takes a jwk key
exports.unmarshalPrivateKey = function (key, callback) {
const privateKey = webcrypto.subtle.importKey(
exports.unmarshalPrivateKey = async function (key) {
const privateKey = await webcrypto.subtle.importKey(
'jwk',
key,
{
name: 'RSASSA-PKCS1-v1_5',
hash: {name: 'SHA-256'}
hash: { name: 'SHA-256' }
},
true,
['sign']
)
nodeify(Promise.all([
const pair = [
privateKey,
derivePublicFromPrivate(key)
]).then((keys) => exportKey({
await derivePublicFromPrivate(key)
]
const keys = await exportKey({
privateKey: pair[0],
publicKey: pair[1]
})
return {
privateKey: keys[0],
publicKey: keys[1]
})).then((keys) => ({
privateKey: keys[0],
publicKey: keys[1]
})), callback)
}
}
exports.getRandomValues = function (arr) {
return Buffer.from(webcrypto.getRandomValues(arr))
}
exports.getRandomValues = randomBytes
exports.hashAndSign = function (key, msg, callback) {
nodeify(webcrypto.subtle.importKey(
exports.hashAndSign = async function (key, msg) {
const privateKey = await webcrypto.subtle.importKey(
'jwk',
key,
{
name: 'RSASSA-PKCS1-v1_5',
hash: {name: 'SHA-256'}
hash: { name: 'SHA-256' }
},
false,
['sign']
).then((privateKey) => {
return webcrypto.subtle.sign(
{name: 'RSASSA-PKCS1-v1_5'},
privateKey,
Uint8Array.from(msg)
)
}).then((sig) => Buffer.from(sig)), callback)
)
const sig = await webcrypto.subtle.sign(
{ name: 'RSASSA-PKCS1-v1_5' },
privateKey,
Uint8Array.from(msg)
)
return Buffer.from(sig)
}
exports.hashAndVerify = function (key, sig, msg, callback) {
nodeify(webcrypto.subtle.importKey(
exports.hashAndVerify = async function (key, sig, msg) {
const publicKey = await webcrypto.subtle.importKey(
'jwk',
key,
{
name: 'RSASSA-PKCS1-v1_5',
hash: {name: 'SHA-256'}
hash: { name: 'SHA-256' }
},
false,
['verify']
).then((publicKey) => {
return webcrypto.subtle.verify(
{name: 'RSASSA-PKCS1-v1_5'},
publicKey,
sig,
msg
)
}), callback)
)
return webcrypto.subtle.verify(
{ name: 'RSASSA-PKCS1-v1_5' },
publicKey,
sig,
msg
)
}
function exportKey (pair) {
@@ -109,7 +114,7 @@ function derivePublicFromPrivate (jwKey) {
},
{
name: 'RSASSA-PKCS1-v1_5',
hash: {name: 'SHA-256'}
hash: { name: 'SHA-256' }
},
true,
['verify']

View File

@@ -6,17 +6,17 @@ const bs58 = require('bs58')
const crypto = require('./rsa')
const pbm = protobuf(require('./keys.proto'))
const forge = require('node-forge')
const setImmediate = require('async/setImmediate')
require('node-forge/lib/sha512')
require('node-forge/lib/pbe')
const forge = require('node-forge/lib/forge')
class RsaPublicKey {
constructor (key) {
this._key = key
}
verify (data, sig, callback) {
ensure(callback)
crypto.hashAndVerify(this._key, sig, data, callback)
async verify (data, sig) { // eslint-disable-line require-await
return crypto.hashAndVerify(this._key, sig, data)
}
marshal () {
@@ -38,9 +38,8 @@ class RsaPublicKey {
return this.bytes.equals(key.bytes)
}
hash (callback) {
ensure(callback)
multihashing(this.bytes, 'sha2-256', callback)
async hash () { // eslint-disable-line require-await
return multihashing(this.bytes, 'sha2-256')
}
}
@@ -53,12 +52,11 @@ class RsaPrivateKey {
}
genSecret () {
return crypto.getRandomValues(new Uint8Array(16))
return crypto.getRandomValues(16)
}
sign (message, callback) {
ensure(callback)
crypto.hashAndSign(this._key, message, callback)
async sign (message) { // eslint-disable-line require-await
return crypto.hashAndSign(this._key, message)
}
get public () {
@@ -69,10 +67,6 @@ class RsaPrivateKey {
return new RsaPublicKey(this._publicKey)
}
decrypt (msg, callback) {
crypto.decrypt(this._key, msg, callback)
}
marshal () {
return crypto.utils.jwkToPkcs1(this._key)
}
@@ -88,9 +82,8 @@ class RsaPrivateKey {
return this.bytes.equals(key.bytes)
}
hash (callback) {
ensure(callback)
multihashing(this.bytes, 'sha2-256', callback)
async hash () { // eslint-disable-line require-await
return multihashing(this.bytes, 'sha2-256')
}
/**
@@ -100,105 +93,62 @@ class RsaPrivateKey {
* The public key is a protobuf encoding containing a type and the DER encoding
* of the PKCS SubjectPublicKeyInfo.
*
* @param {function(Error, id)} callback
* @returns {undefined}
* @returns {Promise<String>}
*/
id (callback) {
this.public.hash((err, hash) => {
if (err) {
return callback(err)
}
callback(null, bs58.encode(hash))
})
async id () {
const hash = await this.public.hash()
return bs58.encode(hash)
}
/**
* 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 {function(Error, KeyInfo)} callback
* @returns {undefined}
* @param {string} [format] - Defaults to 'pkcs-8'.
* @returns {KeyInfo}
*/
export (format, password, callback) {
if (typeof password === 'function') {
callback = password
password = format
format = 'pkcs-8'
async export (password, format = 'pkcs-8') { // eslint-disable-line require-await
let pem = null
const buffer = new forge.util.ByteBuffer(this.marshal())
const asn1 = forge.asn1.fromDer(buffer)
const privateKey = forge.pki.privateKeyFromAsn1(asn1)
if (format === 'pkcs-8') {
const options = {
algorithm: 'aes256',
count: 10000,
saltSize: 128 / 8,
prfAlgorithm: 'sha512'
}
pem = forge.pki.encryptRsaPrivateKey(privateKey, password, options)
} else {
throw new Error(`Unknown export format '${format}'`)
}
ensure(callback)
setImmediate(() => {
let err = null
let pem = null
try {
const buffer = new forge.util.ByteBuffer(this.marshal())
const asn1 = forge.asn1.fromDer(buffer)
const privateKey = forge.pki.privateKeyFromAsn1(asn1)
if (format === 'pkcs-8') {
const options = {
algorithm: 'aes256',
count: 10000,
saltSize: 128 / 8,
prfAlgorithm: 'sha512'
}
pem = forge.pki.encryptRsaPrivateKey(privateKey, password, options)
} else {
err = new Error(`Unknown export format '${format}'`)
}
} catch (_err) {
err = _err
}
callback(err, pem)
})
return pem
}
}
function unmarshalRsaPrivateKey (bytes, callback) {
async function unmarshalRsaPrivateKey (bytes) {
const jwk = crypto.utils.pkcs1ToJwk(bytes)
crypto.unmarshalPrivateKey(jwk, (err, keys) => {
if (err) {
return callback(err)
}
callback(null, new RsaPrivateKey(keys.privateKey, keys.publicKey))
})
const keys = await crypto.unmarshalPrivateKey(jwk)
return new RsaPrivateKey(keys.privateKey, keys.publicKey)
}
function unmarshalRsaPublicKey (bytes) {
const jwk = crypto.utils.pkixToJwk(bytes)
return new RsaPublicKey(jwk)
}
function fromJwk (jwk, callback) {
crypto.unmarshalPrivateKey(jwk, (err, keys) => {
if (err) {
return callback(err)
}
callback(null, new RsaPrivateKey(keys.privateKey, keys.publicKey))
})
async function fromJwk (jwk) {
const keys = await crypto.unmarshalPrivateKey(jwk)
return new RsaPrivateKey(keys.privateKey, keys.publicKey)
}
function generateKeyPair (bits, callback) {
crypto.generateKey(bits, (err, keys) => {
if (err) {
return callback(err)
}
callback(null, new RsaPrivateKey(keys.privateKey, keys.publicKey))
})
}
function ensure (callback) {
if (typeof callback !== 'function') {
throw new Error('callback is required')
}
async function generateKeyPair (bits) {
const keys = await crypto.generateKey(bits)
return new RsaPrivateKey(keys.privateKey, keys.publicKey)
}
module.exports = {

View File

@@ -1,79 +1,69 @@
'use strict'
const crypto = require('crypto')
const keypair = require('keypair')
const setImmediate = require('async/setImmediate')
const randomBytes = require('../random-bytes')
let keypair
try {
if (process.env.LP2P_FORCE_CRYPTO_LIB === 'keypair') {
throw new Error('Force keypair usage')
}
const ursa = require('ursa-optional') // throws if not compiled
keypair = ({ bits }) => {
const key = ursa.generatePrivateKey(bits)
return {
private: key.toPrivatePem(),
public: key.toPublicPem()
}
}
} catch (e) {
if (process.env.LP2P_FORCE_CRYPTO_LIB === 'ursa') {
throw e
}
keypair = require('keypair')
}
const pemToJwk = require('pem-jwk').pem2jwk
const jwkToPem = require('pem-jwk').jwk2pem
exports.utils = require('./rsa-utils')
exports.generateKey = function (bits, callback) {
setImmediate(() => {
let result
try {
const key = keypair({ bits: bits })
result = {
privateKey: pemToJwk(key.private),
publicKey: pemToJwk(key.public)
}
} catch (err) {
return callback(err)
}
callback(null, result)
})
exports.generateKey = async function (bits) { // eslint-disable-line require-await
const key = keypair({ bits })
return {
privateKey: pemToJwk(key.private),
publicKey: pemToJwk(key.public)
}
}
// Takes a jwk key
exports.unmarshalPrivateKey = function (key, callback) {
setImmediate(() => {
if (!key) {
return callback(new Error('Key is invalid'))
exports.unmarshalPrivateKey = async function (key) { // eslint-disable-line require-await
if (!key) {
throw new Error('Key is invalid')
}
return {
privateKey: key,
publicKey: {
kty: key.kty,
n: key.n,
e: key.e
}
callback(null, {
privateKey: key,
publicKey: {
kty: key.kty,
n: key.n,
e: key.e
}
})
})
}
}
exports.getRandomValues = function (arr) {
return crypto.randomBytes(arr.length)
exports.getRandomValues = randomBytes
exports.hashAndSign = async function (key, msg) { // eslint-disable-line require-await
const sign = crypto.createSign('RSA-SHA256')
sign.update(msg)
const pem = jwkToPem(key)
return sign.sign(pem)
}
exports.hashAndSign = function (key, msg, callback) {
setImmediate(() => {
let result
try {
const sign = crypto.createSign('RSA-SHA256')
sign.update(msg)
const pem = jwkToPem(key)
result = 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) {
setImmediate(() => {
let result
try {
const verify = crypto.createVerify('RSA-SHA256')
verify.update(msg)
const pem = jwkToPem(key)
result = verify.verify(pem, sig)
} catch (err) {
return callback(new Error('Key or message is invalid!:' + err.message))
}
callback(null, result)
})
exports.hashAndVerify = async function (key, sig, msg) { // eslint-disable-line require-await
const verify = crypto.createVerify('RSA-SHA256')
verify.update(msg)
const pem = jwkToPem(key)
return verify.verify(pem, sig)
}

View File

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

View File

@@ -1,6 +1,7 @@
'use strict'
const forge = require('node-forge')
const forgePbkdf2 = require('node-forge/lib/pbkdf2')
const forgeUtil = require('node-forge/lib/util')
/**
* Maps an IPFS hash name to its node-forge equivalent.
@@ -30,13 +31,13 @@ function pbkdf2 (password, salt, iterations, keySize, hash) {
if (!hasher) {
throw new Error(`Hash '${hash}' is unknown or not supported`)
}
const dek = forge.pkcs5.pbkdf2(
const dek = forgePbkdf2(
password,
salt,
iterations,
keySize,
hasher)
return forge.util.encode64(dek)
return forgeUtil.encode64(dek)
}
module.exports = pbkdf2

View File

@@ -1,13 +1,9 @@
'use strict'
const randomBytes = require('iso-random-stream/src/random')
const rsa = require('./keys/rsa')
function randomBytes (number) {
module.exports = function (number) {
if (!number || typeof number !== 'number') {
throw new Error('first argument must be a Number bigger than 0')
}
return rsa.getRandomValues(new Uint8Array(number))
return randomBytes(number)
}
module.exports = randomBytes

View File

@@ -2,15 +2,4 @@
'use strict'
module.exports = () => {
// This is only a shim for interfaces, not for functionality
if (typeof self !== 'undefined') {
require('webcrypto-shim')(self)
if (self.crypto) {
return self.crypto
}
}
throw new Error('Please use an environment with crypto support')
}
module.exports = self.crypto || self.msCrypto

View File

@@ -6,7 +6,6 @@ const chai = require('chai')
const dirtyChai = require('dirty-chai')
const expect = chai.expect
chai.use(dirtyChai)
const series = require('async/series')
const crypto = require('../../src')
const fixtures = require('./../fixtures/aes')
@@ -19,53 +18,43 @@ const bytes = {
describe('AES-CTR', () => {
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))
key.fill(5)
const iv = Buffer.alloc(16)
iv.fill(1)
crypto.aes.create(key, iv, (err, cipher) => {
expect(err).to.not.exist()
const cipher = await crypto.aes.create(key, iv)
series([
encryptAndDecrypt(cipher),
encryptAndDecrypt(cipher),
encryptAndDecrypt(cipher),
encryptAndDecrypt(cipher),
encryptAndDecrypt(cipher)
], done)
})
await encryptAndDecrypt(cipher)
await encryptAndDecrypt(cipher)
await encryptAndDecrypt(cipher)
await encryptAndDecrypt(cipher)
await encryptAndDecrypt(cipher)
})
})
Object.keys(bytes).forEach((byte) => {
it(`${bytes[byte]} - fixed - encrypt and decrypt`, (done) => {
it(`${bytes[byte]} - fixed - encrypt and decrypt`, async () => {
const key = Buffer.alloc(parseInt(byte, 10))
key.fill(5)
const iv = Buffer.alloc(16)
iv.fill(1)
crypto.aes.create(key, iv, (err, cipher) => {
expect(err).to.not.exist()
const cipher = await crypto.aes.create(key, iv)
series(fixtures[byte].inputs.map((rawIn, i) => (cb) => {
const input = Buffer.from(rawIn)
const output = Buffer.from(fixtures[byte].outputs[i])
cipher.encrypt(input, (err, res) => {
expect(err).to.not.exist()
expect(res).to.have.length(output.length)
expect(res).to.eql(output)
cipher.decrypt(res, (err, res) => {
expect(err).to.not.exist()
expect(res).to.eql(input)
cb()
})
})
}), done)
})
for (let i = 0; i < fixtures[byte].inputs.length; i++) {
const rawIn = fixtures[byte].inputs[i]
const input = Buffer.from(rawIn)
const output = Buffer.from(fixtures[byte].outputs[i])
const encrypted = await cipher.encrypt(input)
expect(encrypted).to.have.length(output.length)
expect(encrypted).to.eql(output)
const decrypted = await cipher.decrypt(encrypted)
expect(decrypted).to.eql(input)
}
})
})
@@ -74,46 +63,35 @@ describe('AES-CTR', () => {
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))
key.fill(5)
const iv = Buffer.alloc(16)
iv.fill(1)
crypto.aes.create(key, iv, (err, cipher) => {
expect(err).to.not.exist()
const cipher = await crypto.aes.create(key, iv)
series(goFixtures[byte].inputs.map((rawIn, i) => (cb) => {
const input = Buffer.from(rawIn)
const output = Buffer.from(goFixtures[byte].outputs[i])
cipher.encrypt(input, (err, res) => {
expect(err).to.not.exist()
expect(res).to.have.length(output.length)
expect(res).to.be.eql(output)
cipher.decrypt(res, (err, res) => {
expect(err).to.not.exist()
expect(res).to.be.eql(input)
cb()
})
})
}), done)
})
for (let i = 0; i < goFixtures[byte].inputs.length; i++) {
const rawIn = goFixtures[byte].inputs[i]
const input = Buffer.from(rawIn)
const output = Buffer.from(goFixtures[byte].outputs[i])
const encrypted = await cipher.encrypt(input)
expect(encrypted).to.have.length(output.length)
expect(encrypted).to.eql(output)
const decrypted = await cipher.decrypt(encrypted)
expect(decrypted).to.eql(input)
}
})
})
})
function encryptAndDecrypt (cipher) {
async function encryptAndDecrypt (cipher) {
const data = Buffer.alloc(100)
data.fill(Math.ceil(Math.random() * 100))
return (cb) => {
cipher.encrypt(data, (err, res) => {
expect(err).to.not.exist()
cipher.decrypt(res, (err, res) => {
expect(err).to.not.exist()
expect(res).to.be.eql(data)
cb()
})
})
}
const encrypted = await cipher.encrypt(data)
const decrypted = await cipher.decrypt(encrypted)
expect(decrypted).to.be.eql(data)
}

View File

@@ -12,14 +12,8 @@ const fixtures = require('./fixtures/go-key-rsa')
describe('libp2p-crypto', function () {
this.timeout(20 * 1000)
let key
before((done) => {
crypto.keys.generateKeyPair('RSA', 512, (err, _key) => {
if (err) {
return done(err)
}
key = _key
done()
})
before(async () => {
key = await crypto.keys.generateKeyPair('RSA', 512)
})
it('marshalPublicKey and unmarshalPublicKey', () => {
@@ -33,71 +27,41 @@ describe('libp2p-crypto', function () {
}).to.throw()
})
it('marshalPrivateKey and unmarshalPrivateKey', (done) => {
it('marshalPrivateKey and unmarshalPrivateKey', async () => {
expect(() => {
crypto.keys.marshalPrivateKey(key, 'invalid-key-type')
}).to.throw()
crypto.keys.unmarshalPrivateKey(crypto.keys.marshalPrivateKey(key), (err, key2) => {
if (err) {
return done(err)
}
const key2 = await crypto.keys.unmarshalPrivateKey(crypto.keys.marshalPrivateKey(key))
expect(key2.equals(key)).to.be.eql(true)
expect(key2.public.equals(key.public)).to.be.eql(true)
done()
})
expect(key2.equals(key)).to.be.eql(true)
expect(key2.public.equals(key.public)).to.be.eql(true)
})
// marshalled keys seem to be slightly different
// unsure as to if this is just a difference in encoding
// or a bug
describe('go interop', () => {
it('unmarshals private key', (done) => {
crypto.keys.unmarshalPrivateKey(fixtures.private.key, (err, key) => {
if (err) {
return done(err)
}
const hash = fixtures.private.hash
expect(fixtures.private.key).to.eql(key.bytes)
key.hash((err, digest) => {
if (err) {
return done(err)
}
expect(digest).to.eql(hash)
done()
})
})
it('unmarshals private key', async () => {
const key = await crypto.keys.unmarshalPrivateKey(fixtures.private.key)
const hash = fixtures.private.hash
expect(fixtures.private.key).to.eql(key.bytes)
const digest = await key.hash()
expect(digest).to.eql(hash)
})
it('unmarshals public key', (done) => {
it('unmarshals public key', async () => {
const key = crypto.keys.unmarshalPublicKey(fixtures.public.key)
const hash = fixtures.public.hash
expect(crypto.keys.marshalPublicKey(key)).to.eql(fixtures.public.key)
key.hash((err, digest) => {
if (err) {
return done(err)
}
expect(digest).to.eql(hash)
done()
})
const digest = await key.hash()
expect(digest).to.eql(hash)
})
it('unmarshal -> marshal, private key', (done) => {
crypto.keys.unmarshalPrivateKey(fixtures.private.key, (err, key) => {
if (err) {
return done(err)
}
const marshalled = crypto.keys.marshalPrivateKey(key)
expect(marshalled).to.eql(fixtures.private.key)
done()
})
it('unmarshal -> marshal, private key', async () => {
const key = await crypto.keys.unmarshalPrivateKey(fixtures.private.key)
const marshalled = crypto.keys.marshalPrivateKey(key)
expect(marshalled).to.eql(fixtures.private.key)
})
it('unmarshal -> marshal, public key', () => {

View File

@@ -1,11 +1,6 @@
/* eslint-env mocha */
'use strict'
const chai = require('chai')
const dirtyChai = require('dirty-chai')
const expect = chai.expect
chai.use(dirtyChai)
const util = require('util')
const garbage = [Buffer.from('00010203040506070809', 'hex'), {}, null, false, undefined, true, 1, 0, Buffer.from(''), 'aGVsbG93b3JsZA==', 'helloworld', '']
@@ -23,14 +18,13 @@ function doTests (fncName, fnc, num, skipBuffersAndStrings) {
for (let i = 0; i < num; i++) {
args.push(garbage)
}
it(fncName + '(' + args.map(garbage => util.inspect(garbage)).join(', ') + ')', cb => {
args.push((err, res) => {
expect(err).to.exist()
expect(res).to.not.exist()
cb()
})
fnc.apply(null, args)
it(fncName + '(' + args.map(garbage => util.inspect(garbage)).join(', ') + ')', async () => {
try {
await fnc.apply(null, args)
} catch (err) {
return // expected
}
throw new Error('Expected error to be thrown')
})
})
}

View File

@@ -13,16 +13,10 @@ const hashes = ['SHA1', 'SHA256', 'SHA512']
describe('HMAC', () => {
hashes.forEach((hash) => {
it(`${hash} - sign and verify`, (done) => {
crypto.hmac.create(hash, Buffer.from('secret'), (err, hmac) => {
expect(err).to.not.exist()
hmac.digest(Buffer.from('hello world'), (err, sig) => {
expect(err).to.not.exist()
expect(sig).to.have.length(hmac.length)
done()
})
})
it(`${hash} - sign and verify`, async () => {
const hmac = await crypto.hmac.create(hash, Buffer.from('secret'))
const sig = await hmac.digest(Buffer.from('hello world'))
expect(sig).to.have.length(hmac.length)
})
})
})

View File

@@ -15,115 +15,67 @@ const testGarbage = require('../helpers/test-garbage-error-handling')
describe('ed25519', function () {
this.timeout(20 * 1000)
let key
before((done) => {
crypto.keys.generateKeyPair('Ed25519', 512, (err, _key) => {
if (err) return done(err)
key = _key
done()
})
before(async () => {
key = await crypto.keys.generateKeyPair('Ed25519', 512)
})
it('generates a valid key', (done) => {
it('generates a valid key', async () => {
expect(key).to.be.an.instanceof(ed25519.Ed25519PrivateKey)
key.hash((err, digest) => {
if (err) {
return done(err)
}
expect(digest).to.have.length(34)
done()
})
const digest = await key.hash()
expect(digest).to.have.length(34)
})
it('generates a valid key from seed', (done) => {
it('generates a valid key from seed', async () => {
var seed = crypto.randomBytes(32)
crypto.keys.generateKeyPairFromSeed('Ed25519', seed, 512, (err, seededkey) => {
if (err) return done(err)
expect(seededkey).to.be.an.instanceof(ed25519.Ed25519PrivateKey)
seededkey.hash((err, digest) => {
if (err) {
return done(err)
}
expect(digest).to.have.length(34)
done()
})
})
const seededkey = await crypto.keys.generateKeyPairFromSeed('Ed25519', seed, 512)
expect(seededkey).to.be.an.instanceof(ed25519.Ed25519PrivateKey)
const digest = await seededkey.hash()
expect(digest).to.have.length(34)
})
it('generates the same key from the same seed', (done) => {
var seed = crypto.randomBytes(32)
crypto.keys.generateKeyPairFromSeed('Ed25519', seed, 512, (err, seededkey1) => {
if (err) return done(err)
crypto.keys.generateKeyPairFromSeed('Ed25519', seed, 512, (err, seededkey2) => {
if (err) return done(err)
expect(seededkey1.equals(seededkey2)).to.eql(true)
expect(seededkey1.public.equals(seededkey2.public)).to.eql(true)
done()
})
})
it('generates the same key from the same seed', async () => {
const seed = crypto.randomBytes(32)
const seededkey1 = await crypto.keys.generateKeyPairFromSeed('Ed25519', seed, 512)
const seededkey2 = await crypto.keys.generateKeyPairFromSeed('Ed25519', seed, 512)
expect(seededkey1.equals(seededkey2)).to.eql(true)
expect(seededkey1.public.equals(seededkey2.public)).to.eql(true)
})
it('generates different keys for different seeds', (done) => {
it('generates different keys for different seeds', async () => {
const seed1 = crypto.randomBytes(32)
crypto.keys.generateKeyPairFromSeed('Ed25519', seed1, 512, (err, seededkey1) => {
expect(err).to.not.exist()
const seed2 = crypto.randomBytes(32)
crypto.keys.generateKeyPairFromSeed('Ed25519', seed2, 512, (err, seededkey2) => {
expect(err).to.not.exist()
expect(seededkey1.equals(seededkey2)).to.eql(false)
expect(seededkey1.public.equals(seededkey2.public))
.to.eql(false)
done()
})
})
const seededkey1 = await crypto.keys.generateKeyPairFromSeed('Ed25519', seed1, 512)
const seed2 = crypto.randomBytes(32)
const seededkey2 = await crypto.keys.generateKeyPairFromSeed('Ed25519', seed2, 512)
expect(seededkey1.equals(seededkey2)).to.eql(false)
expect(seededkey1.public.equals(seededkey2.public)).to.eql(false)
})
it('signs', (done) => {
it('signs', async () => {
const text = crypto.randomBytes(512)
key.sign(text, (err, sig) => {
expect(err).to.not.exist()
key.public.verify(text, sig, (err, res) => {
expect(err).to.not.exist()
expect(res).to.be.eql(true)
done()
})
})
const sig = await key.sign(text)
const res = await key.public.verify(text, sig)
expect(res).to.be.eql(true)
})
it('encoding', (done) => {
it('encoding', async () => {
const keyMarshal = key.marshal()
ed25519.unmarshalEd25519PrivateKey(keyMarshal, (err, key2) => {
if (err) {
return done(err)
}
const keyMarshal2 = key2.marshal()
const key2 = await ed25519.unmarshalEd25519PrivateKey(keyMarshal)
const keyMarshal2 = key2.marshal()
expect(keyMarshal).to.eql(keyMarshal2)
expect(keyMarshal).to.eql(keyMarshal2)
const pk = key.public
const pkMarshal = pk.marshal()
const pk2 = ed25519.unmarshalEd25519PublicKey(pkMarshal)
const pkMarshal2 = pk2.marshal()
const pk = key.public
const pkMarshal = pk.marshal()
const pk2 = ed25519.unmarshalEd25519PublicKey(pkMarshal)
const pkMarshal2 = pk2.marshal()
expect(pkMarshal).to.eql(pkMarshal2)
done()
})
expect(pkMarshal).to.eql(pkMarshal2)
})
it('key id', (done) => {
key.id((err, id) => {
expect(err).to.not.exist()
expect(id).to.exist()
expect(id).to.be.a('string')
done()
})
it('key id', async () => {
const id = await key.id()
expect(id).to.exist()
expect(id).to.be.a('string')
})
describe('key equals', () => {
@@ -141,51 +93,27 @@ describe('ed25519', function () {
)
})
it('not equals other key', (done) => {
crypto.keys.generateKeyPair('Ed25519', 512, (err, key2) => {
if (err) return done(err)
expect(key.equals(key2)).to.eql(false)
expect(key2.equals(key)).to.eql(false)
expect(key.public.equals(key2.public)).to.eql(false)
expect(key2.public.equals(key.public)).to.eql(false)
done()
})
it('not equals other key', async () => {
const key2 = await crypto.keys.generateKeyPair('Ed25519', 512)
expect(key.equals(key2)).to.eql(false)
expect(key2.equals(key)).to.eql(false)
expect(key.public.equals(key2.public)).to.eql(false)
expect(key2.public.equals(key.public)).to.eql(false)
})
})
it('sign and verify', (done) => {
it('sign and verify', async () => {
const data = Buffer.from('hello world')
key.sign(data, (err, sig) => {
if (err) {
return done(err)
}
key.public.verify(data, sig, (err, valid) => {
if (err) {
return done(err)
}
expect(valid).to.eql(true)
done()
})
})
const sig = await key.sign(data)
const valid = await key.public.verify(data, sig)
expect(valid).to.eql(true)
})
it('fails to verify for different data', (done) => {
it('fails to verify for different data', async () => {
const data = Buffer.from('hello world')
key.sign(data, (err, sig) => {
if (err) {
return done(err)
}
key.public.verify(Buffer.from('hello'), sig, (err, valid) => {
if (err) {
return done(err)
}
expect(valid).to.be.eql(false)
done()
})
})
const sig = await key.sign(data)
const valid = await key.public.verify(Buffer.from('hello'), sig)
expect(valid).to.be.eql(false)
})
describe('returns error via cb instead of crashing', () => {
@@ -197,30 +125,20 @@ describe('ed25519', function () {
describe('go interop', () => {
let privateKey
before((done) => {
crypto.keys.unmarshalPrivateKey(fixtures.verify.privateKey, (err, key) => {
expect(err).to.not.exist()
privateKey = key
done()
})
before(async () => {
const key = await crypto.keys.unmarshalPrivateKey(fixtures.verify.privateKey)
privateKey = key
})
it('verifies with data from go', (done) => {
it('verifies with data from go', async () => {
const key = crypto.keys.unmarshalPublicKey(fixtures.verify.publicKey)
key.verify(fixtures.verify.data, fixtures.verify.signature, (err, ok) => {
expect(err).to.not.exist()
expect(ok).to.eql(true)
done()
})
const ok = await key.verify(fixtures.verify.data, fixtures.verify.signature)
expect(ok).to.eql(true)
})
it('generates the same signature as go', (done) => {
privateKey.sign(fixtures.verify.data, (err, sig) => {
expect(err).to.not.exist()
expect(sig).to.eql(fixtures.verify.signature)
done()
})
it('generates the same signature as go', async () => {
const sig = await privateKey.sign(fixtures.verify.data)
expect(sig).to.eql(fixtures.verify.signature)
})
})
})

View File

@@ -6,7 +6,6 @@ const chai = require('chai')
const dirtyChai = require('dirty-chai')
const expect = chai.expect
chai.use(dirtyChai)
const parallel = require('async/parallel')
const fixtures = require('../fixtures/go-elliptic-key')
const crypto = require('../../src')
@@ -25,49 +24,40 @@ const secretLengths = {
describe('generateEphemeralKeyPair', () => {
curves.forEach((curve) => {
it(`generate and shared key ${curve}`, (done) => {
parallel([
(cb) => crypto.keys.generateEphemeralKeyPair(curve, cb),
(cb) => crypto.keys.generateEphemeralKeyPair(curve, cb)
], (err, keys) => {
expect(err).to.not.exist()
expect(keys[0].key).to.have.length(lengths[curve])
expect(keys[1].key).to.have.length(lengths[curve])
it(`generate and shared key ${curve}`, async () => {
const keys = await Promise.all([
crypto.keys.generateEphemeralKeyPair(curve),
crypto.keys.generateEphemeralKeyPair(curve)
])
keys[0].genSharedKey(keys[1].key, (err, shared) => {
expect(err).to.not.exist()
expect(shared).to.have.length(secretLengths[curve])
done()
})
})
expect(keys[0].key).to.have.length(lengths[curve])
expect(keys[1].key).to.have.length(lengths[curve])
const shared = await keys[0].genSharedKey(keys[1].key)
expect(shared).to.have.length(secretLengths[curve])
})
})
describe('go interop', () => {
it('generates a shared secret', (done) => {
it('generates a shared secret', async () => {
const curve = fixtures.curve
parallel([
(cb) => crypto.keys.generateEphemeralKeyPair(curve, cb),
(cb) => crypto.keys.generateEphemeralKeyPair(curve, cb)
], (err, res) => {
expect(err).to.not.exist()
const alice = res[0]
const bob = res[1]
bob.key = fixtures.bob.public
const keys = await Promise.all([
crypto.keys.generateEphemeralKeyPair(curve),
crypto.keys.generateEphemeralKeyPair(curve)
])
parallel([
(cb) => alice.genSharedKey(bob.key, cb),
(cb) => bob.genSharedKey(alice.key, fixtures.bob, cb)
], (err, secrets) => {
expect(err).to.not.exist()
const alice = keys[0]
const bob = keys[1]
bob.key = fixtures.bob.public
expect(secrets[0]).to.eql(secrets[1])
expect(secrets[0]).to.have.length(32)
const secrets = await Promise.all([
alice.genSharedKey(bob.key),
bob.genSharedKey(alice.key, fixtures.bob)
])
done()
})
})
expect(secrets[0]).to.eql(secrets[1])
expect(secrets[0]).to.have.length(32)
})
})
})

View File

@@ -16,35 +16,17 @@ describe('keyStretcher', () => {
let res
let secret
before((done) => {
crypto.keys.generateEphemeralKeyPair('P-256', (err, _res) => {
if (err) {
return done(err)
}
res = _res
res.genSharedKey(res.key, (err, _secret) => {
if (err) {
return done(err)
}
secret = _secret
done()
})
})
before(async () => {
res = await crypto.keys.generateEphemeralKeyPair('P-256')
secret = await res.genSharedKey(res.key)
})
ciphers.forEach((cipher) => {
hashes.forEach((hash) => {
it(`${cipher} - ${hash}`, (done) => {
crypto.keys.keyStretcher(cipher, hash, secret, (err, keys) => {
if (err) {
return done(err)
}
expect(keys.k1).to.exist()
expect(keys.k2).to.exist()
done()
})
it(`${cipher} - ${hash}`, async () => {
const keys = await crypto.keys.keyStretcher(cipher, hash, secret)
expect(keys.k1).to.exist()
expect(keys.k2).to.exist()
})
})
})
@@ -52,24 +34,19 @@ describe('keyStretcher', () => {
describe('go interop', () => {
fixtures.forEach((test) => {
it(`${test.cipher} - ${test.hash}`, (done) => {
it(`${test.cipher} - ${test.hash}`, async () => {
const cipher = test.cipher
const hash = test.hash
const secret = test.secret
crypto.keys.keyStretcher(cipher, hash, secret, (err, keys) => {
if (err) {
return done(err)
}
const keys = await crypto.keys.keyStretcher(cipher, hash, secret)
expect(keys.k1.iv).to.be.eql(test.k1.iv)
expect(keys.k1.cipherKey).to.be.eql(test.k1.cipherKey)
expect(keys.k1.macKey).to.be.eql(test.k1.macKey)
expect(keys.k1.iv).to.be.eql(test.k1.iv)
expect(keys.k1.cipherKey).to.be.eql(test.k1.cipherKey)
expect(keys.k1.macKey).to.be.eql(test.k1.macKey)
expect(keys.k2.iv).to.be.eql(test.k2.iv)
expect(keys.k2.cipherKey).to.be.eql(test.k2.cipherKey)
expect(keys.k2.macKey).to.be.eql(test.k2.macKey)
done()
})
expect(keys.k2.iv).to.be.eql(test.k2.iv)
expect(keys.k2.cipherKey).to.be.eql(test.k2.cipherKey)
expect(keys.k2.macKey).to.be.eql(test.k2.macKey)
})
})
})

View File

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

View File

@@ -18,75 +18,42 @@ describe('RSA', function () {
this.timeout(20 * 1000)
let key
before((done) => {
crypto.keys.generateKeyPair('RSA', 512, (err, _key) => {
if (err) {
return done(err)
}
key = _key
done()
})
before(async () => {
key = await crypto.keys.generateKeyPair('RSA', 512)
})
it('generates a valid key', (done) => {
it('generates a valid key', async () => {
expect(key).to.be.an.instanceof(rsa.RsaPrivateKey)
key.hash((err, digest) => {
if (err) {
return done(err)
}
expect(digest).to.have.length(34)
done()
})
const digest = await key.hash()
expect(digest).to.have.length(34)
})
it('signs', (done) => {
it('signs', async () => {
const text = key.genSecret()
key.sign(text, (err, sig) => {
if (err) {
return done(err)
}
key.public.verify(text, sig, (err, res) => {
if (err) {
return done(err)
}
expect(res).to.be.eql(true)
done()
})
})
const sig = await key.sign(text)
const res = await key.public.verify(text, sig)
expect(res).to.be.eql(true)
})
it('encoding', (done) => {
it('encoding', async () => {
const keyMarshal = key.marshal()
rsa.unmarshalRsaPrivateKey(keyMarshal, (err, key2) => {
if (err) {
return done(err)
}
const keyMarshal2 = key2.marshal()
const key2 = await rsa.unmarshalRsaPrivateKey(keyMarshal)
const keyMarshal2 = key2.marshal()
expect(keyMarshal).to.eql(keyMarshal2)
expect(keyMarshal).to.eql(keyMarshal2)
const pk = key.public
const pkMarshal = pk.marshal()
const pk2 = rsa.unmarshalRsaPublicKey(pkMarshal)
const pkMarshal2 = pk2.marshal()
const pk = key.public
const pkMarshal = pk.marshal()
const pk2 = rsa.unmarshalRsaPublicKey(pkMarshal)
const pkMarshal2 = pk2.marshal()
expect(pkMarshal).to.eql(pkMarshal2)
done()
})
expect(pkMarshal).to.eql(pkMarshal2)
})
it('key id', (done) => {
key.id((err, id) => {
expect(err).to.not.exist()
expect(id).to.exist()
expect(id).to.be.a('string')
done()
})
it('key id', async () => {
const id = await key.id()
expect(id).to.exist()
expect(id).to.be.a('string')
})
describe('key equals', () => {
@@ -96,90 +63,54 @@ describe('RSA', function () {
expect(key.public.equals(key.public)).to.eql(true)
})
it('not equals other key', (done) => {
crypto.keys.generateKeyPair('RSA', 512, (err, key2) => {
if (err) {
return done(err)
}
expect(key.equals(key2)).to.eql(false)
expect(key2.equals(key)).to.eql(false)
expect(key.public.equals(key2.public)).to.eql(false)
expect(key2.public.equals(key.public)).to.eql(false)
done()
})
it('not equals other key', async () => {
const key2 = await crypto.keys.generateKeyPair('RSA', 512)
expect(key.equals(key2)).to.eql(false)
expect(key2.equals(key)).to.eql(false)
expect(key.public.equals(key2.public)).to.eql(false)
expect(key2.public.equals(key.public)).to.eql(false)
})
})
it('sign and verify', (done) => {
it('sign and verify', async () => {
const data = Buffer.from('hello world')
key.sign(data, (err, sig) => {
if (err) {
return done(err)
}
key.public.verify(data, sig, (err, valid) => {
if (err) {
return done(err)
}
expect(valid).to.be.eql(true)
done()
})
})
const sig = await key.sign(data)
const valid = await key.public.verify(data, sig)
expect(valid).to.be.eql(true)
})
it('fails to verify for different data', (done) => {
it('fails to verify for different data', async () => {
const data = Buffer.from('hello world')
key.sign(data, (err, sig) => {
if (err) {
return done(err)
}
key.public.verify(Buffer.from('hello'), sig, (err, valid) => {
if (err) {
return done(err)
}
expect(valid).to.be.eql(false)
done()
})
})
const sig = await key.sign(data)
const valid = await key.public.verify(Buffer.from('hello'), sig)
expect(valid).to.be.eql(false)
})
describe('export and import', () => {
it('password protected PKCS #8', (done) => {
key.export('pkcs-8', 'my secret', (err, pem) => {
expect(err).to.not.exist()
expect(pem).to.startsWith('-----BEGIN ENCRYPTED PRIVATE KEY-----')
crypto.keys.import(pem, 'my secret', (err, clone) => {
expect(err).to.not.exist()
expect(clone).to.exist()
expect(key.equals(clone)).to.eql(true)
done()
})
})
it('password protected PKCS #8', async () => {
const pem = await key.export('my secret', 'pkcs-8')
expect(pem).to.startsWith('-----BEGIN ENCRYPTED PRIVATE KEY-----')
const clone = await crypto.keys.import(pem, 'my secret')
expect(clone).to.exist()
expect(key.equals(clone)).to.eql(true)
})
it('defaults to PKCS #8', (done) => {
key.export('another secret', (err, pem) => {
expect(err).to.not.exist()
expect(pem).to.startsWith('-----BEGIN ENCRYPTED PRIVATE KEY-----')
crypto.keys.import(pem, 'another secret', (err, clone) => {
expect(err).to.not.exist()
expect(clone).to.exist()
expect(key.equals(clone)).to.eql(true)
done()
})
})
it('defaults to PKCS #8', async () => {
const pem = await key.export('another secret')
expect(pem).to.startsWith('-----BEGIN ENCRYPTED PRIVATE KEY-----')
const clone = await crypto.keys.import(pem, 'another secret')
expect(clone).to.exist()
expect(key.equals(clone)).to.eql(true)
})
it('needs correct password', (done) => {
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()
})
})
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')
})
})
@@ -190,20 +121,15 @@ describe('RSA', function () {
})
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)
key.verify(fixtures.verify.data, fixtures.verify.signature, (err, ok) => {
if (err) throw err
expect(err).to.not.exist()
expect(ok).to.equal(true)
done()
})
const ok = await key.verify(fixtures.verify.data, fixtures.verify.signature)
expect(ok).to.equal(true)
})
})
describe('openssl interop', () => {
it('can read a private key', (done) => {
it('can read a private key', async () => {
/*
* Generated with
* openssl genpkey -algorithm RSA
@@ -251,19 +177,14 @@ gnjREs10u7zyqBIZH7KYVgyh27WxLr859ap8cKAH6Fb+UOPtZo3sUeeume60aebn
4pMwXeXP+LO8NIfRXV8mgrm86g==
-----END PRIVATE KEY-----
`
crypto.keys.import(pem, '', (err, key) => {
expect(err).to.not.exist()
expect(key).to.exist()
key.id((err, id) => {
expect(err).to.not.exist()
expect(id).to.equal('QmfWu2Xp8DZzCkZZzoPB9rcrq4R4RZid6AWE6kmrUAzuHy')
done()
})
})
const key = await crypto.keys.import(pem, '')
expect(key).to.exist()
const id = await key.id()
expect(id).to.equal('QmfWu2Xp8DZzCkZZzoPB9rcrq4R4RZid6AWE6kmrUAzuHy')
})
// AssertionError: expected 'this only supports pkcs5PBES2' to not exist
it.skip('can read a private encrypted key (v1)', (done) => {
it.skip('can read a private encrypted key (v1)', async () => {
/*
* Generated with
* openssl genpkey -algorithm RSA
@@ -290,14 +211,11 @@ mBdkD5r+ixWF174naw53L8U9wF8kiK7pIE1N9TR4USEeovLwX6Ni/2MMDZedOfof
0uxzo5Y=
-----END ENCRYPTED PRIVATE KEY-----
`
crypto.keys.import(pem, 'mypassword', (err, key) => {
expect(err).to.not.exist()
expect(key).to.exist()
done()
})
const key = await crypto.keys.import(pem, 'mypassword')
expect(key).to.exist()
})
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
* openssl genpkey -algorithm RSA
@@ -325,14 +243,11 @@ ePGbz+UtSb9xczvqpRCOiFLh2MG1dUgWuHazjOtUcVWvilKnkjCMzZ9s1qG0sUDj
nPyn
-----END ENCRYPTED PRIVATE KEY-----
`
crypto.keys.import(pem, 'mypassword', (err, key) => {
expect(err).to.not.exist()
expect(key).to.exist()
done()
})
const key = await crypto.keys.import(pem, 'mypassword')
expect(key).to.exist()
})
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
* openssl genpkey -algorithm RSA
@@ -360,14 +275,11 @@ mBUuWAZMpz7njBi7h+JDfmSW/GAaMwrVFC2gef5375R0TejAh+COAjItyoeYEvv8
DQd8
-----END ENCRYPTED PRIVATE KEY-----
`
crypto.keys.import(pem, 'mypassword', (err, key) => {
expect(err).to.not.exist()
expect(key).to.exist()
done()
})
const key = await crypto.keys.import(pem, 'mypassword')
expect(key).to.exist()
})
it('can read a private encrypted key (v2 des)', (done) => {
it('can read a private encrypted key (v2 des)', async () => {
/*
* Generated with
* openssl genpkey -algorithm RSA
@@ -394,14 +306,11 @@ A+qCKbprxyL8SKI5vug2hE+mfC1leXVRtUYm1DnE+oet99bFd0fN20NwTw0rOeRg
EBqQkwAUXR1tNekF8CWLOrfC/wbLRxVRkayb8bQUfdgukLpz0bgw
-----END ENCRYPTED PRIVATE KEY-----
`
crypto.keys.import(pem, 'mypassword', (err, key) => {
expect(err).to.not.exist()
expect(key).to.exist()
done()
})
const key = await crypto.keys.import(pem, 'mypassword')
expect(key).to.exist()
})
it('can read a private encrypted key (v2 des3)', (done) => {
it('can read a private encrypted key (v2 des3)', async () => {
/*
* Generated with
* openssl genpkey -algorithm RSA
@@ -428,11 +337,8 @@ NT2TO3kSzXpQ5M2VjOoHPm2fqxD/js+ThDB3QLi4+C7HqakfiTY1lYzXl9/vayt6
DUD29r9pYL9ErB9tYko2rat54EY7k7Ts6S5jf+8G7Zz234We1APhvqaG
-----END ENCRYPTED PRIVATE KEY-----
`
crypto.keys.import(pem, 'mypassword', (err, key) => {
expect(err).to.not.exist()
expect(key).to.exist()
done()
})
const key = await crypto.keys.import(pem, 'mypassword')
expect(key).to.exist()
})
})
})

View File

@@ -5,49 +5,36 @@ const chai = require('chai')
const dirtyChai = require('dirty-chai')
const expect = chai.expect
chai.use(dirtyChai)
const sinon = require('sinon')
const fixtures = require('../fixtures/secp256k1')
const crypto = require('../../src')
const mockPublicKey = {
bytes: fixtures.pbmPublicKey
}
const mockPrivateKey = {
bytes: fixtures.pbmPrivateKey,
public: mockPublicKey
}
const mockSecp256k1Module = {
generateKeyPair (bits, callback) {
callback(null, mockPrivateKey)
},
unmarshalSecp256k1PrivateKey (buf, callback) {
callback(null, mockPrivateKey)
},
unmarshalSecp256k1PublicKey (buf) {
return mockPublicKey
}
}
describe('without libp2p-crypto-secp256k1 module present', () => {
crypto.keys.supportedKeys.secp256k1 = undefined
it('fails to generate a secp256k1 key', (done) => {
crypto.keys.generateKeyPair('secp256k1', 256, (err, key) => {
expect(err).to.exist()
expect(key).to.not.exist()
done()
})
before(() => {
sinon.replace(crypto.keys.supportedKeys, 'secp256k1', null)
})
it('fails to unmarshal a secp256k1 private key', (done) => {
crypto.keys.unmarshalPrivateKey(fixtures.pbmPrivateKey, (err, key) => {
expect(err).to.exist()
expect(key).to.not.exist()
done()
})
after(() => {
sinon.restore()
})
it('fails to generate a secp256k1 key', async () => {
try {
await crypto.keys.generateKeyPair('secp256k1', 256)
} catch (err) {
return // expected
}
throw new Error('Expected error to be thrown')
})
it('fails to unmarshal a secp256k1 private key', async () => {
try {
await crypto.keys.unmarshalPrivateKey(fixtures.pbmPrivateKey)
} catch (err) {
return // expected
}
throw new Error('Expected error to be thrown')
})
it('fails to unmarshal a secp256k1 public key', () => {
@@ -58,42 +45,36 @@ describe('without libp2p-crypto-secp256k1 module present', () => {
})
describe('with libp2p-crypto-secp256k1 module present', () => {
let key
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) => {
it('generates a valid key', async () => {
const key = await crypto.keys.generateKeyPair('secp256k1', 256)
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)
crypto.keys.unmarshalPrivateKey(keyMarshal, (err, key2) => {
if (err) return done(err)
const keyMarshal2 = crypto.keys.marshalPrivateKey(key2)
const key2 = await crypto.keys.unmarshalPrivateKey(keyMarshal)
const keyMarshal2 = crypto.keys.marshalPrivateKey(key2)
expect(keyMarshal).to.eql(keyMarshal2)
expect(keyMarshal).to.eql(keyMarshal2)
const pk = key.public
const pkMarshal = crypto.keys.marshalPublicKey(pk)
const pk2 = crypto.keys.unmarshalPublicKey(pkMarshal)
const pkMarshal2 = crypto.keys.marshalPublicKey(pk2)
const pk = key.public
const pkMarshal = crypto.keys.marshalPublicKey(pk)
const pk2 = crypto.keys.unmarshalPublicKey(pkMarshal)
const pkMarshal2 = crypto.keys.marshalPublicKey(pk2)
expect(pkMarshal).to.eql(pkMarshal2)
done()
})
expect(pkMarshal).to.eql(pkMarshal2)
})
it('unmarshals a secp256k1 private key', async () => {
const key = await crypto.keys.unmarshalPrivateKey(fixtures.pbmPrivateKey)
expect(key).to.exist()
})
it('unmarshals a secp256k1 public key', () => {
const key = crypto.keys.unmarshalPublicKey(fixtures.pbmPublicKey)
expect(key).to.exist()
})
})

3
test/node.js Normal file
View File

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