mirror of
https://github.com/fluencelabs/js-libp2p-crypto
synced 2025-07-16 11:22:09 +00:00
Compare commits
58 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
f3cb8ced36 | ||
|
0a6f63dce5 | ||
|
0ce5f34a08 | ||
|
a826968e71 | ||
|
7608fdd858 | ||
|
bf9b532067 | ||
|
5296f8a42f | ||
|
3a91ae2ed8 | ||
|
e7c11a8e01 | ||
|
9bb96dc2bf | ||
|
957fdd37e9 | ||
|
83257bc4bd | ||
|
cb7fae7fcc | ||
|
7669847c17 | ||
|
dc2793f138 | ||
|
e0b916ace9 | ||
|
0a71af7b89 | ||
|
46adafb207 | ||
|
9e977c7d44 | ||
|
f20267b962 | ||
|
1b6a070fa8 | ||
|
1471e07bf9 | ||
|
bc554d1407 | ||
|
83d2d52205 | ||
|
301c779f49 | ||
|
d552fd423a | ||
|
306504c386 | ||
|
bf80e8e511 | ||
|
4e619e60f5 | ||
|
a958f52a5c | ||
|
dfc476c4f6 | ||
|
71eb33a44f | ||
|
310733726e | ||
|
0efc94bd20 | ||
|
68f4b5f496 | ||
|
2f8e234044 | ||
|
c2c6fde394 | ||
|
14dde32177 | ||
|
5bc30297de | ||
|
a5ad8cf444 | ||
|
be64372a5e | ||
|
2b0b7abd78 | ||
|
17f2065d21 | ||
|
2f065167fa | ||
|
e0ecce18ce | ||
|
cea1140ec6 | ||
|
e5b7c1f622 | ||
|
20326199d9 | ||
|
4e56e1724f | ||
|
7d63f698c0 | ||
|
a49df7786c | ||
|
c73adb00cc | ||
|
8a95de4700 | ||
|
c35a65133f | ||
|
4eb4aa05de | ||
|
21f96a4eb7 | ||
|
39b5124526 | ||
|
93e50dde10 |
1
.eslintignore
Normal file
1
.eslintignore
Normal file
@@ -0,0 +1 @@
|
||||
src/keys/keys.proto.js
|
13
.gitignore
vendored
13
.gitignore
vendored
@@ -1,6 +1,11 @@
|
||||
docs
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
|
||||
**/node_modules/
|
||||
**/*.log
|
||||
test/repo-tests*
|
||||
**/bundle.js
|
||||
|
||||
# Logs
|
||||
logs
|
||||
@@ -31,4 +36,12 @@ build
|
||||
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
|
||||
node_modules
|
||||
|
||||
lib
|
||||
dist
|
||||
test/test-data/go-ipfs-repo/LOCK
|
||||
test/test-data/go-ipfs-repo/LOG
|
||||
test/test-data/go-ipfs-repo/LOG.old
|
||||
|
||||
# while testing npm5
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
|
21
.travis.yml
21
.travis.yml
@@ -3,32 +3,21 @@ language: node_js
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- node_js: 4
|
||||
env: CXX=g++-4.8
|
||||
- node_js: 6
|
||||
env:
|
||||
- SAUCE=true
|
||||
- CXX=g++-4.8
|
||||
- node_js: stable
|
||||
env: CXX=g++-4.8
|
||||
|
||||
# Make sure we have new NPM.
|
||||
before_install:
|
||||
- npm install -g npm
|
||||
- node_js: 8
|
||||
env: CXX=g++-4.8
|
||||
# - node_js: stable
|
||||
# env: CXX=g++-4.8
|
||||
|
||||
script:
|
||||
- npm run lint
|
||||
- npm test
|
||||
- npm run coverage
|
||||
|
||||
- npm run test
|
||||
|
||||
before_script:
|
||||
- export DISPLAY=:99.0
|
||||
- sh -e /etc/init.d/xvfb start
|
||||
|
||||
after_success:
|
||||
- npm run coverage-publish
|
||||
|
||||
addons:
|
||||
firefox: 'latest'
|
||||
apt:
|
||||
|
116
CHANGELOG.md
Normal file
116
CHANGELOG.md
Normal file
@@ -0,0 +1,116 @@
|
||||
<a name="0.10.4"></a>
|
||||
## [0.10.4](https://github.com/libp2p/js-libp2p-crypto/compare/v0.10.3...v0.10.4) (2017-12-01)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* catch error when unmarshaling instead of crashing ([#113](https://github.com/libp2p/js-libp2p-crypto/issues/113)) ([7608fdd](https://github.com/libp2p/js-libp2p-crypto/commit/7608fdd))
|
||||
|
||||
|
||||
|
||||
<a name="0.10.3"></a>
|
||||
## [0.10.3](https://github.com/libp2p/js-libp2p-crypto/compare/v0.10.2...v0.10.3) (2017-09-07)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* switch protocol-buffers to protons ([#110](https://github.com/libp2p/js-libp2p-crypto/issues/110)) ([3a91ae2](https://github.com/libp2p/js-libp2p-crypto/commit/3a91ae2))
|
||||
|
||||
|
||||
|
||||
<a name="0.10.2"></a>
|
||||
## [0.10.2](https://github.com/libp2p/js-libp2p-crypto/compare/v0.10.1...v0.10.2) (2017-09-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* use regular protocol-buffers until protobufjs is fixed ([#109](https://github.com/libp2p/js-libp2p-crypto/issues/109)) ([957fdd3](https://github.com/libp2p/js-libp2p-crypto/commit/957fdd3))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **deps:** upgrade to aegir@12 and browserify-aes@1.0.8 ([83257bc](https://github.com/libp2p/js-libp2p-crypto/commit/83257bc))
|
||||
|
||||
|
||||
|
||||
<a name="0.10.1"></a>
|
||||
## [0.10.1](https://github.com/libp2p/js-libp2p-crypto/compare/v0.10.0...v0.10.1) (2017-09-05)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* switch to protobufjs ([#107](https://github.com/libp2p/js-libp2p-crypto/issues/107)) ([dc2793f](https://github.com/libp2p/js-libp2p-crypto/commit/dc2793f))
|
||||
|
||||
|
||||
|
||||
<a name="0.10.0"></a>
|
||||
# [0.10.0](https://github.com/libp2p/js-libp2p-crypto/compare/v0.9.4...v0.10.0) (2017-09-03)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* p2p addrs situation ([#106](https://github.com/libp2p/js-libp2p-crypto/issues/106)) ([9e977c7](https://github.com/libp2p/js-libp2p-crypto/commit/9e977c7))
|
||||
* skip nextTick in nodeify ([#103](https://github.com/libp2p/js-libp2p-crypto/issues/103)) ([f20267b](https://github.com/libp2p/js-libp2p-crypto/commit/f20267b))
|
||||
|
||||
|
||||
|
||||
<a name="0.9.4"></a>
|
||||
## [0.9.4](https://github.com/libp2p/js-libp2p-crypto/compare/v0.9.3...v0.9.4) (2017-07-22)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* circular circular dep -> DI ([bc554d1](https://github.com/libp2p/js-libp2p-crypto/commit/bc554d1))
|
||||
|
||||
|
||||
|
||||
<a name="0.9.3"></a>
|
||||
## [0.9.3](https://github.com/libp2p/js-libp2p-crypto/compare/v0.9.2...v0.9.3) (2017-07-22)
|
||||
|
||||
|
||||
|
||||
<a name="0.9.2"></a>
|
||||
## [0.9.2](https://github.com/libp2p/js-libp2p-crypto/compare/v0.9.1...v0.9.2) (2017-07-22)
|
||||
|
||||
|
||||
|
||||
<a name="0.9.1"></a>
|
||||
## [0.9.1](https://github.com/libp2p/js-libp2p-crypto/compare/v0.9.0...v0.9.1) (2017-07-22)
|
||||
|
||||
|
||||
|
||||
<a name="0.9.0"></a>
|
||||
# [0.9.0](https://github.com/libp2p/js-libp2p-crypto/compare/v0.8.8...v0.9.0) (2017-07-22)
|
||||
|
||||
|
||||
|
||||
<a name="0.8.8"></a>
|
||||
## [0.8.8](https://github.com/libp2p/js-libp2p-crypto/compare/v0.8.7...v0.8.8) (2017-04-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **ecdh:** allow base64 to be left-0-padded, needed for JWK format ([be64372](https://github.com/libp2p/js-libp2p-crypto/commit/be64372)), closes [#97](https://github.com/libp2p/js-libp2p-crypto/issues/97)
|
||||
|
||||
|
||||
|
||||
<a name="0.8.7"></a>
|
||||
## [0.8.7](https://github.com/libp2p/js-libp2p-crypto/compare/v0.8.6...v0.8.7) (2017-03-21)
|
||||
|
||||
|
||||
|
||||
<a name="0.8.6"></a>
|
||||
## [0.8.6](https://github.com/libp2p/js-libp2p-crypto/compare/v0.8.5...v0.8.6) (2017-03-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **package:** update tweetnacl to version 1.0.0-rc.1 ([4e56e17](https://github.com/libp2p/js-libp2p-crypto/commit/4e56e17))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **keys:** implement generateKeyPairFromSeed for ed25519 ([e5b7c1f](https://github.com/libp2p/js-libp2p-crypto/commit/e5b7c1f))
|
||||
|
||||
|
||||
|
134
README.md
134
README.md
@@ -10,38 +10,32 @@
|
||||
[](https://david-dm.org/libp2p/js-libp2p-crypto)
|
||||
[](https://github.com/feross/standard)
|
||||

|
||||

|
||||
|
||||
[](https://saucelabs.com/u/ipfs-js-
|
||||
libp2p-crypto)
|
||||

|
||||
|
||||
> 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).
|
||||
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).
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Install](#install)
|
||||
- [Usage](#usage)
|
||||
- [Example](#example)
|
||||
- [API](#api)
|
||||
- [`hmac`](#hmac)
|
||||
- [`crypto.hmac`](#hmac)
|
||||
- [`create(hash, secret, callback)`](#createhash-secret-callback)
|
||||
- [`digest(data, callback)`](#digestdata-callback)
|
||||
- [`aes`](#aes)
|
||||
- [`crypto.aes`](#aes)
|
||||
- [`create(key, iv, callback)`](#createkey-iv-callback)
|
||||
- [`encrypt(data, callback)`](#encryptdata-callback)
|
||||
- [`encrypt(data, callback)`](#encryptdata-callback)
|
||||
- [`webcrypto`](#webcrypto)
|
||||
- [`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)
|
||||
- [`generateKeyPair(type, bits, callback)`](#generatekeypairtype-bits-callback)
|
||||
- [`generateEphemeralKeyPair(curve, callback)`](#generateephemeralkeypaircurve-callback)
|
||||
- [`keyStretcher(cipherType, hashType, secret, callback)`](#keystretcherciphertype-hashtype-secret-callback)
|
||||
- [`marshalPublicKey(key[, type], callback)`](#marshalpublickeykey-type-callback)
|
||||
- [`unmarshalPublicKey(buf)`](#unmarshalpublickeybuf)
|
||||
- [`marshalPrivateKey(key[, type])`](#marshalprivatekeykey-type)
|
||||
- [`unmarshalPrivateKey(buf, callback)`](#unmarshalprivatekeybuf-callback)
|
||||
- [`webcrypto`](#webcrypto)
|
||||
- [Contribute](#contribute)
|
||||
- [License](#license)
|
||||
|
||||
@@ -51,24 +45,39 @@ needed for libp2p. This is based on this [go implementation](https://github.com/
|
||||
npm install --save libp2p-crypto
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Example
|
||||
|
||||
```js
|
||||
const crypto = require('libp2p-crypto')
|
||||
|
||||
crypto.generateKeyPair('RSA', 2048, (err, key) => {
|
||||
})
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### `hmac`
|
||||
### `crypto.aes`
|
||||
|
||||
Expoes an interface to AES encryption (formerly Rijndael), as defined in U.S. Federal Information Processing Standards Publication 197.
|
||||
|
||||
This uses `CTR` mode.
|
||||
|
||||
#### `crypto.aes.create(key, iv, callback)`
|
||||
|
||||
- `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)`
|
||||
|
||||
- `data: Buffer`
|
||||
- `callback: Function`
|
||||
|
||||
##### `encrypt(data, callback)`
|
||||
|
||||
- `data: Buffer`
|
||||
- `callback: Function`
|
||||
|
||||
```
|
||||
TODO: Example of using aes
|
||||
```
|
||||
|
||||
### `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.
|
||||
|
||||
#### `create(hash, secret, callback)`
|
||||
#### `crypto.hmac.create(hash, secret, callback)`
|
||||
|
||||
- `hash: String`
|
||||
- `secret: Buffer`
|
||||
@@ -79,47 +88,23 @@ Exposes an interface to the Keyed-Hash Message Authentication Code (HMAC) as def
|
||||
- `data: Buffer`
|
||||
- `callback: Function`
|
||||
|
||||
### `aes`
|
||||
Expoes an interface to AES encryption (formerly Rijndael), as defined in U.S. Federal Information Processing Standards Publication 197.
|
||||
Example:
|
||||
|
||||
This uses `CTR` mode.
|
||||
```
|
||||
TODO: Example of using hmac
|
||||
```
|
||||
|
||||
#### `create(key, iv, callback)`
|
||||
### `crypto.keys`
|
||||
|
||||
- `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`
|
||||
**Supported Key Types**
|
||||
|
||||
##### `encrypt(data, callback)`
|
||||
The [`generateKeyPair`](#generatekeypairtype-bits-callback), [`marshalPublicKey`](#marshalpublickeykey-type-callback), and [`marshalPrivateKey`](#marshalprivatekeykey-type) functions accept a string `type` argument.
|
||||
|
||||
- `data: Buffer`
|
||||
- `callback: Function`
|
||||
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.
|
||||
|
||||
##### `encrypt(data, callback)`
|
||||
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.
|
||||
|
||||
- `data: Buffer`
|
||||
- `callback: Function`
|
||||
|
||||
|
||||
### `webcrypto`
|
||||
|
||||
Depending on the environment this is either an instance of [node-webcrypto-ossl](https://github.com/PeculiarVentures/node-webcrypto-ossl) or the result of `window.crypto`.
|
||||
|
||||
### `keys`
|
||||
|
||||
#### Supported Key Types
|
||||
|
||||
The [`generateKeyPair`](#generatekeypairtype-bits-callback), [`marshalPublicKey`](#marshalpublickeykey-type-callback), 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.
|
||||
|
||||
### `generateKeyPair(type, bits, callback)`
|
||||
### `crypto.keys.generateKeyPair(type, bits, callback)`
|
||||
|
||||
- `type: String`, see [Supported Key Types](#supported-key-types) above.
|
||||
- `bits: Number` Minimum of 1024
|
||||
@@ -127,7 +112,7 @@ is not installed by default, and should be explicitly depended on if your projec
|
||||
|
||||
Generates a keypair of the given type and bitsize.
|
||||
|
||||
### `generateEphemeralKeyPair(curve, callback)`
|
||||
### `crypto.keys.generateEphemeralKeyPair(curve, callback)`
|
||||
|
||||
- `curve: String`, one of `'P-256'`, `'P-384'`, `'P-521'` is currently supported
|
||||
- `callback: Function`
|
||||
@@ -145,7 +130,7 @@ Calls back with an object of the form
|
||||
}
|
||||
```
|
||||
|
||||
### `keyStretcher(cipherType, hashType, secret, callback)`
|
||||
### `crypto.keys.keyStretcher(cipherType, hashType, secret, callback)`
|
||||
|
||||
- `cipherType: String`, one of `'AES-128'`, `'AES-256'`, `'Blowfish'`
|
||||
- `hashType: String`, one of `'SHA1'`, `SHA256`, `SHA512`
|
||||
@@ -154,7 +139,8 @@ Calls back with an object of the form
|
||||
|
||||
Generates a set of keys for each party by stretching the shared key.
|
||||
|
||||
Calls back with an object of the form
|
||||
Calls back with an object of the form:
|
||||
|
||||
```js
|
||||
{
|
||||
k1: {
|
||||
@@ -170,34 +156,34 @@ Calls back with an object of the form
|
||||
}
|
||||
```
|
||||
|
||||
### `marshalPublicKey(key[, type], callback)`
|
||||
### `crypto.keys.marshalPublicKey(key[, type], callback)`
|
||||
|
||||
- `key: keys.rsa.RsaPublicKey | keys.ed25519.Ed25519PublicKey | require('libp2p-crypto-secp256k1').Secp256k1PublicKey`
|
||||
- `type: String`, see [Supported Key Types](#supported-key-types) above.
|
||||
|
||||
Converts a public key object into a protobuf serialized public key.
|
||||
|
||||
### `unmarshalPublicKey(buf)`
|
||||
### `crypto.keys.unmarshalPublicKey(buf)`
|
||||
|
||||
- `buf: Buffer`
|
||||
|
||||
Converts a protobuf serialized public key into its representative object.
|
||||
|
||||
### `marshalPrivateKey(key[, type])`
|
||||
### `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.
|
||||
|
||||
Converts a private key object into a protobuf serialized private key.
|
||||
|
||||
### `unmarshalPrivateKey(buf, callback)`
|
||||
### `crypto.keys.unmarshalPrivateKey(buf, callback)`
|
||||
|
||||
- `buf: Buffer`
|
||||
- `callback: Function`
|
||||
|
||||
Converts a protobuf serialized private key into its representative object.
|
||||
|
||||
### `randomBytes(number)`
|
||||
### `crypto.randomBytes(number)`
|
||||
|
||||
- `number: Number`
|
||||
|
||||
|
@@ -10,28 +10,18 @@ const curves = ['P-256', 'P-384', 'P-521']
|
||||
|
||||
curves.forEach((curve) => {
|
||||
suite.add(`ephemeral key with secrect ${curve}`, (d) => {
|
||||
crypto.generateEphemeralKeyPair('P-256', (err, res) => {
|
||||
if (err) {
|
||||
throw err
|
||||
}
|
||||
crypto.keys.generateEphemeralKeyPair('P-256', (err, res) => {
|
||||
if (err) { throw err }
|
||||
res.genSharedKey(res.key, (err, secret) => {
|
||||
if (err) {
|
||||
throw err
|
||||
}
|
||||
if (err) { throw err }
|
||||
secrets.push(secret)
|
||||
|
||||
d.resolve()
|
||||
})
|
||||
})
|
||||
}, {
|
||||
defer: true
|
||||
})
|
||||
}, { defer: true })
|
||||
})
|
||||
|
||||
suite
|
||||
.on('cycle', (event) => {
|
||||
console.log(String(event.target))
|
||||
})
|
||||
.run({
|
||||
async: true
|
||||
})
|
||||
.on('cycle', (event) => console.log(String(event.target)))
|
||||
.run({async: true})
|
||||
|
@@ -13,37 +13,27 @@ const ciphers = ['AES-128', 'AES-256', 'Blowfish']
|
||||
const hashes = ['SHA1', 'SHA256', 'SHA512']
|
||||
|
||||
async.waterfall([
|
||||
(cb) => crypto.generateEphemeralKeyPair('P-256', cb),
|
||||
(cb) => crypto.keys.generateEphemeralKeyPair('P-256', cb),
|
||||
(res, cb) => res.genSharedKey(res.key, cb)
|
||||
], (err, secret) => {
|
||||
if (err) {
|
||||
throw err
|
||||
}
|
||||
if (err) { throw err }
|
||||
|
||||
ciphers.forEach((cipher) => hashes.forEach((hash) => {
|
||||
setup(cipher, hash, secret)
|
||||
}))
|
||||
|
||||
suite
|
||||
.on('cycle', (event) => {
|
||||
console.log(String(event.target))
|
||||
})
|
||||
.run({
|
||||
async: true
|
||||
})
|
||||
.on('cycle', (event) => console.log(String(event.target)))
|
||||
.run({async: true})
|
||||
})
|
||||
|
||||
function setup (cipher, hash, secret) {
|
||||
suite.add(`keyStretcher ${cipher} ${hash}`, (d) => {
|
||||
crypto.keyStretcher(cipher, hash, secret, (err, k) => {
|
||||
if (err) {
|
||||
throw err
|
||||
}
|
||||
crypto.keys.keyStretcher(cipher, hash, secret, (err, k) => {
|
||||
if (err) { throw err }
|
||||
|
||||
keys.push(k)
|
||||
d.resolve()
|
||||
})
|
||||
}, {
|
||||
defer: true
|
||||
})
|
||||
}, { defer: true })
|
||||
}
|
||||
|
@@ -10,8 +10,8 @@ const bits = [1024, 2048, 4096]
|
||||
|
||||
bits.forEach((bit) => {
|
||||
suite.add(`generateKeyPair ${bit}bits`, (d) => {
|
||||
crypto.generateKeyPair('RSA', bit, (err, key) => {
|
||||
if (err) throw err
|
||||
crypto.keys.generateKeyPair('RSA', bit, (err, key) => {
|
||||
if (err) { throw err }
|
||||
keys.push(key)
|
||||
d.resolve()
|
||||
})
|
||||
@@ -25,17 +25,11 @@ suite.add('sign and verify', (d) => {
|
||||
const text = key.genSecret()
|
||||
|
||||
key.sign(text, (err, sig) => {
|
||||
if (err) {
|
||||
throw err
|
||||
}
|
||||
if (err) { throw err }
|
||||
|
||||
key.public.verify(text, sig, (err, res) => {
|
||||
if (err) {
|
||||
throw err
|
||||
}
|
||||
if (res !== true) {
|
||||
throw new Error('failed to verify')
|
||||
}
|
||||
if (err) { throw err }
|
||||
if (res !== true) { throw new Error('failed to verify') }
|
||||
d.resolve()
|
||||
})
|
||||
})
|
||||
@@ -44,9 +38,5 @@ suite.add('sign and verify', (d) => {
|
||||
})
|
||||
|
||||
suite
|
||||
.on('cycle', (event) => {
|
||||
console.log(String(event.target))
|
||||
})
|
||||
.run({
|
||||
async: true
|
||||
})
|
||||
.on('cycle', (event) => console.log(String(event.target)))
|
||||
.run({async: true})
|
||||
|
12
circle.yml
12
circle.yml
@@ -2,11 +2,17 @@ machine:
|
||||
node:
|
||||
version: stable
|
||||
|
||||
post:
|
||||
test:
|
||||
- npm run coverage -- --upload
|
||||
|
||||
dependencies:
|
||||
pre:
|
||||
- google-chrome --version
|
||||
- wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add -
|
||||
- sudo sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list'
|
||||
- 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 --only-upgrade install google-chrome-stable
|
||||
- sudo apt-get install -f
|
||||
- sudo apt-get install --only-upgrade lsb-base
|
||||
- sudo dpkg -i google-chrome.deb
|
||||
- google-chrome --version
|
||||
|
75
package.json
75
package.json
@@ -1,28 +1,25 @@
|
||||
{
|
||||
"name": "libp2p-crypto",
|
||||
"version": "0.8.2",
|
||||
"version": "0.10.4",
|
||||
"description": "Crypto primitives for libp2p",
|
||||
"main": "src/index.js",
|
||||
"browser": {
|
||||
"node-webcrypto-ossl": false,
|
||||
"./src/crypto/webcrypto.js": "./src/crypto/webcrypto-browser.js",
|
||||
"./src/crypto/hmac.js": "./src/crypto/hmac-browser.js",
|
||||
"./src/crypto/ecdh.js": "./src/crypto/ecdh-browser.js",
|
||||
"./src/crypto/ciphers.js": "./src/crypto/ciphers-browser.js",
|
||||
"./src/crypto/rsa.js": "./src/crypto/rsa-browser.js"
|
||||
"./src/hmac/index.js": "./src/hmac/index-browser.js",
|
||||
"./src/keys/ecdh.js": "./src/keys/ecdh-browser.js",
|
||||
"./src/aes/ciphers.js": "./src/aes/ciphers-browser.js",
|
||||
"./src/keys/rsa.js": "./src/keys/rsa-browser.js"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "aegir-lint",
|
||||
"build": "aegir-build",
|
||||
"test": "npm run test:node && npm run test:no-webcrypto && npm run test:browser",
|
||||
"test:node": "aegir-test --env node",
|
||||
"test:no-webcrypto": "NO_WEBCRYPTO=true aegir-test --env node",
|
||||
"test:browser": "aegir-test --env browser",
|
||||
"release": "aegir-release",
|
||||
"release-minor": "aegir-release --type minor",
|
||||
"release-major": "aegir-release --type major",
|
||||
"coverage": "aegir-coverage",
|
||||
"coverage-publish": "aegir-coverage publish"
|
||||
"lint": "aegir lint",
|
||||
"build": "aegir build",
|
||||
"build-proto": "pbjs --wrap commonjs --target static-module src/keys/keys.proto > src/keys/keys.proto.js",
|
||||
"test": "aegir test",
|
||||
"test:node": "aegir test -t node",
|
||||
"test:browser": "aegir test -t browser -t webworker",
|
||||
"release": "aegir release",
|
||||
"release-minor": "aegir release --type minor",
|
||||
"release-major": "aegir release --type major",
|
||||
"coverage": "aegir coverage --ignore src/keys/keys.proto.js"
|
||||
},
|
||||
"keywords": [
|
||||
"IPFS",
|
||||
@@ -30,45 +27,36 @@
|
||||
"crypto",
|
||||
"rsa"
|
||||
],
|
||||
"author": "Friedel Ziegelmayer <dignifiedqurie@gmail.com>",
|
||||
"author": "Friedel Ziegelmayer <dignifiedquire@gmail.com>",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"multihashing-async": "~0.4.2",
|
||||
"asn1.js": "^4.9.1",
|
||||
"async": "^2.1.4",
|
||||
"browserify-aes": "^1.0.6",
|
||||
"asn1.js": "^5.0.0",
|
||||
"async": "^2.6.0",
|
||||
"browserify-aes": "^1.1.1",
|
||||
"keypair": "^1.0.1",
|
||||
"nodeify": "^1.0.0",
|
||||
"libp2p-crypto-secp256k1": "~0.2.2",
|
||||
"multihashing-async": "~0.4.7",
|
||||
"pem-jwk": "^1.5.1",
|
||||
"protocol-buffers": "^3.2.1",
|
||||
"protons": "^1.0.0",
|
||||
"rsa-pem-to-jwk": "^1.1.3",
|
||||
"safe-buffer": "^5.0.1",
|
||||
"tweetnacl": "^0.14.5",
|
||||
"tweetnacl": "^1.0.0",
|
||||
"webcrypto-shim": "github:dignifiedquire/webcrypto-shim#master"
|
||||
},
|
||||
"devDependencies": {
|
||||
"aegir": "^10.0.0",
|
||||
"benchmark": "^2.1.3",
|
||||
"browserify-optional": "^1.0.0",
|
||||
"chai": "^3.5.0",
|
||||
"aegir": "^12.2.0",
|
||||
"benchmark": "^2.1.4",
|
||||
"chai": "^4.1.2",
|
||||
"dirty-chai": "^2.0.1",
|
||||
"pre-commit": "^1.2.2"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"node-webcrypto-ossl": "^1.0.17"
|
||||
},
|
||||
"pre-commit": [
|
||||
"lint",
|
||||
"test"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=4.0.0",
|
||||
"node": ">=6.0.0",
|
||||
"npm": ">=3.0.0"
|
||||
},
|
||||
"browserify": {
|
||||
"transform": [
|
||||
"browserify-optional"
|
||||
]
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/libp2p/js-libp2p-crypto.git"
|
||||
@@ -81,10 +69,13 @@
|
||||
"David Dias <daviddias.p@gmail.com>",
|
||||
"Dmitriy Ryajov <dryajov@gmail.com>",
|
||||
"Friedel Ziegelmayer <dignifiedquire@gmail.com>",
|
||||
"Greenkeeper <support@greenkeeper.io>",
|
||||
"Jack Kleeman <jackkleeman@gmail.com>",
|
||||
"Maciej Krüger <mkg20001@gmail.com>",
|
||||
"Richard Littauer <richard.littauer@gmail.com>",
|
||||
"Tom Swindell <t.swindell@rubyx.co.uk>",
|
||||
"Yusef Napora <yusef@napora.org>",
|
||||
"greenkeeperio-bot <support@greenkeeper.io>",
|
||||
"greenkeeper[bot] <greenkeeper[bot]@users.noreply.github.com>",
|
||||
"nikuda <nikuda@gmail.com>"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
55
src/aes/index-browser.js
Normal file
55
src/aes/index-browser.js
Normal file
@@ -0,0 +1,55 @@
|
||||
'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))
|
||||
|
||||
if (key.length !== 16 && key.length !== 32) {
|
||||
return done(new Error('Invalid key length'))
|
||||
}
|
||||
|
||||
const enc = new asm.AES_CTR.Encrypt({
|
||||
key: key,
|
||||
nonce: iv
|
||||
})
|
||||
const dec = new asm.AES_CTR.Decrypt({
|
||||
key: key,
|
||||
nonce: iv
|
||||
})
|
||||
|
||||
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)
|
||||
},
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
done(null, res)
|
||||
}
|
@@ -1,8 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
exports.webcrypto = require('./crypto/webcrypto')()
|
||||
exports.hmac = require('./crypto/hmac')
|
||||
exports.ecdh = require('./crypto/ecdh')
|
||||
exports.aes = require('./crypto/aes')
|
||||
exports.rsa = require('./crypto/rsa')
|
||||
exports.ed25519 = require('./crypto/ed25519')
|
@@ -1,35 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
const nacl = require('tweetnacl')
|
||||
const setImmediate = require('async/setImmediate')
|
||||
const Buffer = require('safe-buffer').Buffer
|
||||
|
||||
exports.publicKeyLength = nacl.sign.publicKeyLength
|
||||
exports.privateKeyLength = nacl.sign.secretKeyLength
|
||||
|
||||
exports.generateKey = function (callback) {
|
||||
const done = (err, res) => setImmediate(() => {
|
||||
callback(err, res)
|
||||
})
|
||||
|
||||
let keys
|
||||
try {
|
||||
keys = nacl.sign.keyPair()
|
||||
} catch (err) {
|
||||
done(err)
|
||||
return
|
||||
}
|
||||
done(null, keys)
|
||||
}
|
||||
|
||||
exports.hashAndSign = function (key, msg, callback) {
|
||||
setImmediate(() => {
|
||||
callback(null, Buffer.from(nacl.sign.detached(msg, key)))
|
||||
})
|
||||
}
|
||||
|
||||
exports.hashAndVerify = function (key, sig, msg, callback) {
|
||||
setImmediate(() => {
|
||||
callback(null, nacl.sign.detached.verify(msg, sig, key))
|
||||
})
|
||||
}
|
@@ -1,80 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
// Node.js land
|
||||
// First we look if node-webrypto-ossl is available
|
||||
// otherwise we fall back to using keypair + node core
|
||||
|
||||
let webcrypto
|
||||
try {
|
||||
webcrypto = require('node-webcrypto-ossl')
|
||||
} catch (err) {
|
||||
// not available, use the code below
|
||||
}
|
||||
|
||||
if (webcrypto && !process.env.NO_WEBCRYPTO) {
|
||||
module.exports = require('./rsa-browser')
|
||||
} else {
|
||||
const crypto = require('crypto')
|
||||
const keypair = require('keypair')
|
||||
const setImmediate = require('async/setImmediate')
|
||||
const pemToJwk = require('pem-jwk').pem2jwk
|
||||
const jwkToPem = require('pem-jwk').jwk2pem
|
||||
|
||||
exports.utils = require('./rsa-utils')
|
||||
|
||||
exports.generateKey = function (bits, callback) {
|
||||
const done = (err, res) => setImmediate(() => {
|
||||
callback(err, res)
|
||||
})
|
||||
|
||||
let key
|
||||
try {
|
||||
key = keypair({
|
||||
bits: bits
|
||||
})
|
||||
} catch (err) {
|
||||
done(err)
|
||||
return
|
||||
}
|
||||
|
||||
done(null, {
|
||||
privateKey: pemToJwk(key.private),
|
||||
publicKey: pemToJwk(key.public)
|
||||
})
|
||||
}
|
||||
|
||||
// Takes a jwk key
|
||||
exports.unmarshalPrivateKey = function (key, callback) {
|
||||
callback(null, {
|
||||
privateKey: key,
|
||||
publicKey: {
|
||||
kty: key.kty,
|
||||
n: key.n,
|
||||
e: key.e
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
exports.getRandomValues = function (arr) {
|
||||
return crypto.randomBytes(arr.length)
|
||||
}
|
||||
|
||||
exports.hashAndSign = function (key, msg, callback) {
|
||||
const sign = crypto.createSign('RSA-SHA256')
|
||||
|
||||
sign.update(msg)
|
||||
setImmediate(() => {
|
||||
callback(null, sign.sign(jwkToPem(key)))
|
||||
})
|
||||
}
|
||||
|
||||
exports.hashAndVerify = function (key, sig, msg, callback) {
|
||||
const verify = crypto.createVerify('RSA-SHA256')
|
||||
|
||||
verify.update(msg)
|
||||
|
||||
setImmediate(() => {
|
||||
callback(null, verify.verify(jwkToPem(key), sig))
|
||||
})
|
||||
}
|
||||
}
|
@@ -1,24 +0,0 @@
|
||||
/* global self */
|
||||
|
||||
'use strict'
|
||||
|
||||
module.exports = function getWebCrypto () {
|
||||
if (typeof self !== 'undefined') {
|
||||
// This is only a shim for interfaces, not for functionality
|
||||
require('webcrypto-shim')(self)
|
||||
|
||||
if (self.crypto) {
|
||||
return self.crypto
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof self !== 'undefined') {
|
||||
require('webcrypto-shim')(self)
|
||||
|
||||
if (self.crypto) {
|
||||
return self.crypto
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error('Please use an environment with crypto support')
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
module.exports = function getWebCrypto () {
|
||||
try {
|
||||
const WebCrypto = require('node-webcrypto-ossl')
|
||||
const webCrypto = new WebCrypto()
|
||||
return webCrypto
|
||||
} catch (err) {
|
||||
// fallback to other things
|
||||
}
|
||||
}
|
@@ -1,10 +1,9 @@
|
||||
'use strict'
|
||||
|
||||
const nodeify = require('nodeify')
|
||||
const Buffer = require('safe-buffer').Buffer
|
||||
const nodeify = require('../nodeify')
|
||||
|
||||
const crypto = require('./webcrypto')()
|
||||
const lengths = require('./hmac-lengths')
|
||||
const crypto = require('../webcrypto.js')()
|
||||
const lengths = require('./lengths')
|
||||
|
||||
const hashTypes = {
|
||||
SHA1: 'SHA-1',
|
||||
@@ -12,6 +11,11 @@ const hashTypes = {
|
||||
SHA512: 'SHA-512'
|
||||
}
|
||||
|
||||
const sign = (key, data, cb) => {
|
||||
nodeify(crypto.subtle.sign({name: 'HMAC'}, key, data)
|
||||
.then((raw) => Buffer.from(raw)), cb)
|
||||
}
|
||||
|
||||
exports.create = function (hashType, secret, callback) {
|
||||
const hash = hashTypes[hashType]
|
||||
|
||||
@@ -27,11 +31,7 @@ exports.create = function (hashType, secret, callback) {
|
||||
).then((key) => {
|
||||
return {
|
||||
digest (data, cb) {
|
||||
nodeify(crypto.subtle.sign(
|
||||
{name: 'HMAC'},
|
||||
key,
|
||||
data
|
||||
).then((raw) => Buffer.from(raw)), cb)
|
||||
sign(key, data, cb)
|
||||
},
|
||||
length: lengths[hashType]
|
||||
}
|
@@ -1,13 +1,13 @@
|
||||
'use strict'
|
||||
|
||||
const crypto = require('crypto')
|
||||
|
||||
const lengths = require('./hmac-lengths')
|
||||
const lengths = require('./lengths')
|
||||
|
||||
exports.create = function (hash, secret, callback) {
|
||||
const res = {
|
||||
digest (data, cb) {
|
||||
const hmac = genFresh()
|
||||
const hmac = crypto.createHmac(hash.toLowerCase(), secret)
|
||||
|
||||
hmac.update(data)
|
||||
|
||||
setImmediate(() => {
|
||||
@@ -17,8 +17,5 @@ exports.create = function (hash, secret, callback) {
|
||||
length: lengths[hash]
|
||||
}
|
||||
|
||||
function genFresh () {
|
||||
return crypto.createHmac(hash.toLowerCase(), secret)
|
||||
}
|
||||
callback(null, res)
|
||||
}
|
105
src/index.js
105
src/index.js
@@ -1,101 +1,12 @@
|
||||
'use strict'
|
||||
|
||||
const protobuf = require('protocol-buffers')
|
||||
const hmac = require('./hmac')
|
||||
const aes = require('./aes')
|
||||
const keys = require('./keys')
|
||||
|
||||
const pbm = protobuf(require('./crypto.proto'))
|
||||
const c = require('./crypto')
|
||||
exports = module.exports
|
||||
|
||||
exports.protobuf = pbm
|
||||
|
||||
exports.hmac = c.hmac
|
||||
exports.aes = c.aes
|
||||
exports.webcrypto = c.webcrypto
|
||||
|
||||
const keys = exports.keys = require('./keys')
|
||||
function isValidKeyType (keyType) {
|
||||
const key = keys[keyType.toLowerCase()]
|
||||
return key !== undefined
|
||||
}
|
||||
|
||||
exports.keyStretcher = require('./key-stretcher')
|
||||
exports.generateEphemeralKeyPair = require('./ephemeral-keys')
|
||||
|
||||
// Generates a keypair of the given type and bitsize
|
||||
exports.generateKeyPair = (type, bits, cb) => {
|
||||
let key = keys[type.toLowerCase()]
|
||||
if (!key) {
|
||||
return cb(new Error('invalid or unsupported key type'))
|
||||
}
|
||||
|
||||
key.generateKeyPair(bits, cb)
|
||||
}
|
||||
|
||||
// Converts a protobuf serialized public key into its
|
||||
// representative object
|
||||
exports.unmarshalPublicKey = (buf) => {
|
||||
const decoded = pbm.PublicKey.decode(buf)
|
||||
|
||||
switch (decoded.Type) {
|
||||
case pbm.KeyType.RSA:
|
||||
return keys.rsa.unmarshalRsaPublicKey(decoded.Data)
|
||||
case pbm.KeyType.Ed25519:
|
||||
return keys.ed25519.unmarshalEd25519PublicKey(decoded.Data)
|
||||
case pbm.KeyType.Secp256k1:
|
||||
if (keys.secp256k1) {
|
||||
return keys.secp256k1.unmarshalSecp256k1PublicKey(decoded.Data)
|
||||
} else {
|
||||
throw new Error('secp256k1 support requires libp2p-crypto-secp256k1 package')
|
||||
}
|
||||
default:
|
||||
throw new Error('invalid or unsupported key type')
|
||||
}
|
||||
}
|
||||
|
||||
// Converts a public key object into a protobuf serialized public key
|
||||
exports.marshalPublicKey = (key, type) => {
|
||||
type = (type || 'rsa').toLowerCase()
|
||||
if (!isValidKeyType(type)) {
|
||||
throw new Error('invalid or unsupported key type')
|
||||
}
|
||||
|
||||
return key.bytes
|
||||
}
|
||||
|
||||
// Converts a protobuf serialized private key into its
|
||||
// representative object
|
||||
exports.unmarshalPrivateKey = (buf, callback) => {
|
||||
const decoded = pbm.PrivateKey.decode(buf)
|
||||
|
||||
switch (decoded.Type) {
|
||||
case pbm.KeyType.RSA:
|
||||
return keys.rsa.unmarshalRsaPrivateKey(decoded.Data, callback)
|
||||
case pbm.KeyType.Ed25519:
|
||||
return keys.ed25519.unmarshalEd25519PrivateKey(decoded.Data, callback)
|
||||
case pbm.KeyType.Secp256k1:
|
||||
if (keys.secp256k1) {
|
||||
return keys.secp256k1.unmarshalSecp256k1PrivateKey(decoded.Data, callback)
|
||||
} else {
|
||||
return callback(new Error('secp256k1 support requires libp2p-crypto-secp256k1 package'))
|
||||
}
|
||||
default:
|
||||
callback(new Error('invalid or unsupported key type'))
|
||||
}
|
||||
}
|
||||
|
||||
// Converts a private key object into a protobuf serialized private key
|
||||
exports.marshalPrivateKey = (key, type) => {
|
||||
type = (type || 'rsa').toLowerCase()
|
||||
if (!isValidKeyType(type)) {
|
||||
throw new Error('invalid or unsupported key type')
|
||||
}
|
||||
|
||||
return key.bytes
|
||||
}
|
||||
|
||||
exports.randomBytes = (number) => {
|
||||
if (!number || typeof number !== 'number') {
|
||||
throw new Error('first argument must be a Number bigger than 0')
|
||||
}
|
||||
|
||||
return c.rsa.getRandomValues(new Uint8Array(number))
|
||||
}
|
||||
exports.aes = aes
|
||||
exports.hmac = hmac
|
||||
exports.keys = keys
|
||||
exports.randomBytes = require('./random-bytes')
|
||||
|
@@ -1,11 +1,10 @@
|
||||
'use strict'
|
||||
|
||||
const crypto = require('./webcrypto')()
|
||||
const nodeify = require('nodeify')
|
||||
const webcrypto = require('../webcrypto.js')()
|
||||
const nodeify = require('../nodeify')
|
||||
const BN = require('asn1.js').bignum
|
||||
const Buffer = require('safe-buffer').Buffer
|
||||
|
||||
const util = require('./util')
|
||||
const util = require('../util')
|
||||
const toBase64 = util.toBase64
|
||||
const toBn = util.toBn
|
||||
|
||||
@@ -16,7 +15,7 @@ const bits = {
|
||||
}
|
||||
|
||||
exports.generateEphmeralKeyPair = function (curve, callback) {
|
||||
nodeify(crypto.subtle.generateKey(
|
||||
nodeify(webcrypto.subtle.generateKey(
|
||||
{
|
||||
name: 'ECDH',
|
||||
namedCurve: curve
|
||||
@@ -34,7 +33,7 @@ exports.generateEphmeralKeyPair = function (curve, callback) {
|
||||
let privateKey
|
||||
|
||||
if (forcePrivate) {
|
||||
privateKey = crypto.subtle.importKey(
|
||||
privateKey = webcrypto.subtle.importKey(
|
||||
'jwk',
|
||||
unmarshalPrivateKey(curve, forcePrivate),
|
||||
{
|
||||
@@ -49,7 +48,7 @@ exports.generateEphmeralKeyPair = function (curve, callback) {
|
||||
}
|
||||
|
||||
const keys = Promise.all([
|
||||
crypto.subtle.importKey(
|
||||
webcrypto.subtle.importKey(
|
||||
'jwk',
|
||||
unmarshalPublicKey(curve, theirPub),
|
||||
{
|
||||
@@ -62,7 +61,7 @@ exports.generateEphmeralKeyPair = function (curve, callback) {
|
||||
privateKey
|
||||
])
|
||||
|
||||
nodeify(keys.then((keys) => crypto.subtle.deriveBits(
|
||||
nodeify(keys.then((keys) => webcrypto.subtle.deriveBits(
|
||||
{
|
||||
name: 'ECDH',
|
||||
namedCurve: curve,
|
||||
@@ -73,15 +72,13 @@ exports.generateEphmeralKeyPair = function (curve, callback) {
|
||||
)).then((bits) => Buffer.from(bits)), cb)
|
||||
}
|
||||
|
||||
return crypto.subtle.exportKey(
|
||||
'jwk',
|
||||
pair.publicKey
|
||||
).then((publicKey) => {
|
||||
return {
|
||||
key: marshalPublicKey(publicKey),
|
||||
genSharedKey
|
||||
}
|
||||
})
|
||||
return webcrypto.subtle.exportKey('jwk', pair.publicKey)
|
||||
.then((publicKey) => {
|
||||
return {
|
||||
key: marshalPublicKey(publicKey),
|
||||
genSharedKey
|
||||
}
|
||||
})
|
||||
}), callback)
|
||||
}
|
||||
|
||||
@@ -117,8 +114,8 @@ function unmarshalPublicKey (curve, key) {
|
||||
return {
|
||||
kty: 'EC',
|
||||
crv: curve,
|
||||
x: toBase64(x),
|
||||
y: toBase64(y),
|
||||
x: toBase64(x, byteLen),
|
||||
y: toBase64(y, byteLen),
|
||||
ext: true
|
||||
}
|
||||
}
|
163
src/keys/ed25519-class.js
Normal file
163
src/keys/ed25519-class.js
Normal file
@@ -0,0 +1,163 @@
|
||||
'use strict'
|
||||
|
||||
const multihashing = require('multihashing-async')
|
||||
const protobuf = require('protons')
|
||||
|
||||
const crypto = require('./ed25519')
|
||||
const pbm = protobuf(require('./keys.proto'))
|
||||
|
||||
class Ed25519PublicKey {
|
||||
constructor (key) {
|
||||
this._key = ensureKey(key, crypto.publicKeyLength)
|
||||
}
|
||||
|
||||
verify (data, sig, callback) {
|
||||
ensure(callback)
|
||||
crypto.hashAndVerify(this._key, sig, data, callback)
|
||||
}
|
||||
|
||||
marshal () {
|
||||
return Buffer.from(this._key)
|
||||
}
|
||||
|
||||
get bytes () {
|
||||
return pbm.PublicKey.encode({
|
||||
Type: pbm.KeyType.Ed25519,
|
||||
Data: this.marshal()
|
||||
})
|
||||
}
|
||||
|
||||
equals (key) {
|
||||
return this.bytes.equals(key.bytes)
|
||||
}
|
||||
|
||||
hash (callback) {
|
||||
ensure(callback)
|
||||
multihashing(this.bytes, 'sha2-256', callback)
|
||||
}
|
||||
}
|
||||
|
||||
class Ed25519PrivateKey {
|
||||
// key - 64 byte Uint8Array or Buffer containing private key
|
||||
// publicKey - 32 byte Uint8Array or Buffer containing public key
|
||||
constructor (key, publicKey) {
|
||||
this._key = ensureKey(key, crypto.privateKeyLength)
|
||||
this._publicKey = ensureKey(publicKey, crypto.publicKeyLength)
|
||||
}
|
||||
|
||||
sign (message, callback) {
|
||||
ensure(callback)
|
||||
crypto.hashAndSign(this._key, message, callback)
|
||||
}
|
||||
|
||||
get public () {
|
||||
if (!this._publicKey) {
|
||||
throw new Error('public key not provided')
|
||||
}
|
||||
|
||||
return new Ed25519PublicKey(this._publicKey)
|
||||
}
|
||||
|
||||
marshal () {
|
||||
return Buffer.concat([Buffer.from(this._key), Buffer.from(this._publicKey)])
|
||||
}
|
||||
|
||||
get bytes () {
|
||||
return pbm.PrivateKey.encode({
|
||||
Type: pbm.KeyType.Ed25519,
|
||||
Data: this.marshal()
|
||||
})
|
||||
}
|
||||
|
||||
equals (key) {
|
||||
return this.bytes.equals(key.bytes)
|
||||
}
|
||||
|
||||
hash (callback) {
|
||||
ensure(callback)
|
||||
multihashing(this.bytes, 'sha2-256', callback)
|
||||
}
|
||||
}
|
||||
|
||||
function unmarshalEd25519PrivateKey (bytes, callback) {
|
||||
try {
|
||||
bytes = ensureKey(bytes, crypto.privateKeyLength + crypto.publicKeyLength)
|
||||
} catch (err) {
|
||||
return callback(err)
|
||||
}
|
||||
const privateKeyBytes = bytes.slice(0, crypto.privateKeyLength)
|
||||
const publicKeyBytes = bytes.slice(crypto.privateKeyLength, bytes.length)
|
||||
callback(null, new Ed25519PrivateKey(privateKeyBytes, publicKeyBytes))
|
||||
}
|
||||
|
||||
function unmarshalEd25519PublicKey (bytes) {
|
||||
bytes = ensureKey(bytes, crypto.publicKeyLength)
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
||||
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')
|
||||
}
|
||||
}
|
||||
|
||||
function ensureKey (key, length) {
|
||||
if (Buffer.isBuffer(key)) {
|
||||
key = new Uint8Array(key)
|
||||
}
|
||||
if (!(key instanceof Uint8Array) || key.length !== length) {
|
||||
throw new Error('Key must be a Uint8Array or Buffer of length ' + length)
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
Ed25519PublicKey,
|
||||
Ed25519PrivateKey,
|
||||
unmarshalEd25519PrivateKey,
|
||||
unmarshalEd25519PublicKey,
|
||||
generateKeyPair,
|
||||
generateKeyPairFromSeed
|
||||
}
|
@@ -1,142 +1,51 @@
|
||||
'use strict'
|
||||
|
||||
const multihashing = require('multihashing-async')
|
||||
const protobuf = require('protocol-buffers')
|
||||
const Buffer = require('safe-buffer').Buffer
|
||||
const nacl = require('tweetnacl')
|
||||
const setImmediate = require('async/setImmediate')
|
||||
|
||||
const crypto = require('../crypto').ed25519
|
||||
const pbm = protobuf(require('../crypto.proto'))
|
||||
exports.publicKeyLength = nacl.sign.publicKeyLength
|
||||
exports.privateKeyLength = nacl.sign.secretKeyLength
|
||||
|
||||
class Ed25519PublicKey {
|
||||
constructor (key) {
|
||||
this._key = ensureKey(key, crypto.publicKeyLength)
|
||||
}
|
||||
|
||||
verify (data, sig, callback) {
|
||||
ensure(callback)
|
||||
crypto.hashAndVerify(this._key, sig, data, callback)
|
||||
}
|
||||
|
||||
marshal () {
|
||||
return Buffer.from(this._key)
|
||||
}
|
||||
|
||||
get bytes () {
|
||||
return pbm.PublicKey.encode({
|
||||
Type: pbm.KeyType.Ed25519,
|
||||
Data: this.marshal()
|
||||
})
|
||||
}
|
||||
|
||||
equals (key) {
|
||||
return this.bytes.equals(key.bytes)
|
||||
}
|
||||
|
||||
hash (callback) {
|
||||
ensure(callback)
|
||||
multihashing(this.bytes, 'sha2-256', callback)
|
||||
}
|
||||
}
|
||||
|
||||
class Ed25519PrivateKey {
|
||||
// key - 64 byte Uint8Array or Buffer containing private key
|
||||
// publicKey - 32 byte Uint8Array or Buffer containing public key
|
||||
constructor (key, publicKey) {
|
||||
this._key = ensureKey(key, crypto.privateKeyLength)
|
||||
this._publicKey = ensureKey(publicKey, crypto.publicKeyLength)
|
||||
}
|
||||
|
||||
sign (message, callback) {
|
||||
ensure(callback)
|
||||
crypto.hashAndSign(this._key, message, callback)
|
||||
}
|
||||
|
||||
get public () {
|
||||
if (!this._publicKey) {
|
||||
throw new Error('public key not provided')
|
||||
}
|
||||
|
||||
return new Ed25519PublicKey(this._publicKey)
|
||||
}
|
||||
|
||||
marshal () {
|
||||
return Buffer.concat([Buffer.from(this._key), Buffer.from(this._publicKey)])
|
||||
}
|
||||
|
||||
get bytes () {
|
||||
return pbm.PrivateKey.encode({
|
||||
Type: pbm.KeyType.Ed25519,
|
||||
Data: this.marshal()
|
||||
})
|
||||
}
|
||||
|
||||
equals (key) {
|
||||
return this.bytes.equals(key.bytes)
|
||||
}
|
||||
|
||||
hash (callback) {
|
||||
ensure(callback)
|
||||
multihashing(this.bytes, 'sha2-256', callback)
|
||||
}
|
||||
}
|
||||
|
||||
function unmarshalEd25519PrivateKey (bytes, callback) {
|
||||
try {
|
||||
bytes = ensureKey(bytes, crypto.privateKeyLength + crypto.publicKeyLength)
|
||||
} catch (err) {
|
||||
return callback(err)
|
||||
}
|
||||
const privateKeyBytes = bytes.slice(0, crypto.privateKeyLength)
|
||||
const publicKeyBytes = bytes.slice(crypto.privateKeyLength, bytes.length)
|
||||
callback(null, new Ed25519PrivateKey(privateKeyBytes, publicKeyBytes))
|
||||
}
|
||||
|
||||
function unmarshalEd25519PublicKey (bytes) {
|
||||
bytes = ensureKey(bytes, crypto.publicKeyLength)
|
||||
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
|
||||
exports.generateKey = function (callback) {
|
||||
setImmediate(() => {
|
||||
let result
|
||||
try {
|
||||
privkey = new Ed25519PrivateKey(keys.secretKey, keys.publicKey)
|
||||
result = nacl.sign.keyPair()
|
||||
} catch (err) {
|
||||
cb(err)
|
||||
return
|
||||
return callback(err)
|
||||
}
|
||||
|
||||
cb(null, privkey)
|
||||
callback(null, result)
|
||||
})
|
||||
}
|
||||
|
||||
function ensure (cb) {
|
||||
if (typeof cb !== 'function') {
|
||||
throw new Error('callback is required')
|
||||
}
|
||||
// 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)
|
||||
})
|
||||
}
|
||||
|
||||
function ensureKey (key, length) {
|
||||
if (Buffer.isBuffer(key)) {
|
||||
key = new Uint8Array(key)
|
||||
}
|
||||
if (!(key instanceof Uint8Array) || key.length !== length) {
|
||||
throw new Error('Key must be a Uint8Array or Buffer of length ' + length)
|
||||
}
|
||||
return key
|
||||
exports.hashAndSign = function (key, msg, callback) {
|
||||
setImmediate(() => {
|
||||
callback(null, Buffer.from(nacl.sign.detached(msg, key)))
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
Ed25519PublicKey,
|
||||
Ed25519PrivateKey,
|
||||
unmarshalEd25519PrivateKey,
|
||||
unmarshalEd25519PublicKey,
|
||||
generateKeyPair
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
@@ -1,11 +1,11 @@
|
||||
'use strict'
|
||||
|
||||
const crypto = require('./crypto')
|
||||
const ecdh = require('./ecdh')
|
||||
|
||||
// Generates an ephemeral public key and returns a function that will compute
|
||||
// the shared secret key.
|
||||
//
|
||||
// Focuses only on ECDH now, but can be made more general in the future.
|
||||
module.exports = (curve, callback) => {
|
||||
crypto.ecdh.generateEphmeralKeyPair(curve, callback)
|
||||
ecdh.generateEphmeralKeyPair(curve, callback)
|
||||
}
|
@@ -1,11 +1,117 @@
|
||||
'use strict'
|
||||
|
||||
module.exports = {
|
||||
rsa: require('./rsa'),
|
||||
ed25519: require('./ed25519')
|
||||
const protobuf = require('protons')
|
||||
const keysPBM = protobuf(require('./keys.proto'))
|
||||
|
||||
exports = module.exports
|
||||
|
||||
const supportedKeys = {
|
||||
rsa: require('./rsa-class'),
|
||||
ed25519: require('./ed25519-class'),
|
||||
secp256k1: require('libp2p-crypto-secp256k1')(keysPBM, require('../random-bytes'))
|
||||
}
|
||||
|
||||
try {
|
||||
module.exports.secp256k1 = require('libp2p-crypto-secp256k1')
|
||||
} catch (err) {
|
||||
exports.supportedKeys = supportedKeys
|
||||
exports.keysPBM = keysPBM
|
||||
|
||||
function isValidKeyType (keyType) {
|
||||
const key = supportedKeys[keyType.toLowerCase()]
|
||||
return key !== undefined
|
||||
}
|
||||
|
||||
exports.keyStretcher = require('./key-stretcher')
|
||||
exports.generateEphemeralKeyPair = require('./ephemeral-keys')
|
||||
|
||||
// Generates a keypair of the given type and bitsize
|
||||
exports.generateKeyPair = (type, bits, cb) => {
|
||||
let key = supportedKeys[type.toLowerCase()]
|
||||
|
||||
if (!key) {
|
||||
return cb(new Error('invalid or unsupported key type'))
|
||||
}
|
||||
|
||||
key.generateKeyPair(bits, cb)
|
||||
}
|
||||
|
||||
// Generates a keypair of the given type and bitsize
|
||||
// seed is a 32 byte uint8array
|
||||
exports.generateKeyPairFromSeed = (type, seed, bits, cb) => {
|
||||
let key = supportedKeys[type.toLowerCase()]
|
||||
if (!key) {
|
||||
return cb(new Error('invalid or unsupported key type'))
|
||||
}
|
||||
if (type.toLowerCase() !== 'ed25519') {
|
||||
return cb(new Error('Seed key derivation is unimplemented for RSA or secp256k1'))
|
||||
}
|
||||
key.generateKeyPairFromSeed(seed, bits, cb)
|
||||
}
|
||||
|
||||
// Converts a protobuf serialized public key into its
|
||||
// representative object
|
||||
exports.unmarshalPublicKey = (buf) => {
|
||||
const decoded = keysPBM.PublicKey.decode(buf)
|
||||
const data = decoded.Data
|
||||
|
||||
switch (decoded.Type) {
|
||||
case keysPBM.KeyType.RSA:
|
||||
return supportedKeys.rsa.unmarshalRsaPublicKey(data)
|
||||
case keysPBM.KeyType.Ed25519:
|
||||
return supportedKeys.ed25519.unmarshalEd25519PublicKey(data)
|
||||
case keysPBM.KeyType.Secp256k1:
|
||||
if (supportedKeys.secp256k1) {
|
||||
return supportedKeys.secp256k1.unmarshalSecp256k1PublicKey(data)
|
||||
} else {
|
||||
throw new Error('secp256k1 support requires libp2p-crypto-secp256k1 package')
|
||||
}
|
||||
default:
|
||||
throw new Error('invalid or unsupported key type')
|
||||
}
|
||||
}
|
||||
|
||||
// Converts a public key object into a protobuf serialized public key
|
||||
exports.marshalPublicKey = (key, type) => {
|
||||
type = (type || 'rsa').toLowerCase()
|
||||
if (!isValidKeyType(type)) {
|
||||
throw new Error('invalid or unsupported key type')
|
||||
}
|
||||
|
||||
return key.bytes
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
const data = decoded.Data
|
||||
|
||||
switch (decoded.Type) {
|
||||
case keysPBM.KeyType.RSA:
|
||||
return supportedKeys.rsa.unmarshalRsaPrivateKey(data, callback)
|
||||
case keysPBM.KeyType.Ed25519:
|
||||
return supportedKeys.ed25519.unmarshalEd25519PrivateKey(data, callback)
|
||||
case keysPBM.KeyType.Secp256k1:
|
||||
if (supportedKeys.secp256k1) {
|
||||
return supportedKeys.secp256k1.unmarshalSecp256k1PrivateKey(data, callback)
|
||||
} else {
|
||||
return callback(new Error('secp256k1 support requires libp2p-crypto-secp256k1 package'))
|
||||
}
|
||||
default:
|
||||
callback(new Error('invalid or unsupported key type'))
|
||||
}
|
||||
}
|
||||
|
||||
// Converts a private key object into a protobuf serialized private key
|
||||
exports.marshalPrivateKey = (key, type) => {
|
||||
type = (type || 'rsa').toLowerCase()
|
||||
if (!isValidKeyType(type)) {
|
||||
throw new Error('invalid or unsupported key type')
|
||||
}
|
||||
|
||||
return key.bytes
|
||||
}
|
||||
|
@@ -1,8 +1,7 @@
|
||||
'use strict'
|
||||
|
||||
const crypto = require('./crypto')
|
||||
const whilst = require('async/whilst')
|
||||
const Buffer = require('safe-buffer').Buffer
|
||||
const hmac = require('../hmac')
|
||||
|
||||
const cipherMap = {
|
||||
'AES-128': {
|
||||
@@ -38,7 +37,7 @@ module.exports = (cipherType, hash, secret, callback) => {
|
||||
const seed = Buffer.from('key expansion')
|
||||
const resultLength = 2 * (ivSize + cipherKeySize + hmacKeySize)
|
||||
|
||||
crypto.hmac.create(hash, secret, (err, m) => {
|
||||
hmac.create(hash, secret, (err, m) => {
|
||||
if (err) {
|
||||
return callback(err)
|
||||
}
|
@@ -5,13 +5,11 @@ module.exports = `enum KeyType {
|
||||
Ed25519 = 1;
|
||||
Secp256k1 = 2;
|
||||
}
|
||||
|
||||
message PublicKey {
|
||||
required KeyType Type = 1;
|
||||
required bytes Data = 2;
|
||||
}
|
||||
|
||||
message PrivateKey {
|
||||
required KeyType Type = 1;
|
||||
required bytes Data = 2;
|
||||
}`
|
||||
}`
|
@@ -1,14 +1,13 @@
|
||||
'use strict'
|
||||
|
||||
const nodeify = require('nodeify')
|
||||
const Buffer = require('safe-buffer').Buffer
|
||||
const nodeify = require('../nodeify')
|
||||
|
||||
const crypto = require('./webcrypto')()
|
||||
const webcrypto = require('../webcrypto.js')()
|
||||
|
||||
exports.utils = require('./rsa-utils')
|
||||
|
||||
exports.generateKey = function (bits, callback) {
|
||||
nodeify(crypto.subtle.generateKey(
|
||||
nodeify(webcrypto.subtle.generateKey(
|
||||
{
|
||||
name: 'RSASSA-PKCS1-v1_5',
|
||||
modulusLength: bits,
|
||||
@@ -18,16 +17,16 @@ exports.generateKey = function (bits, callback) {
|
||||
true,
|
||||
['sign', 'verify']
|
||||
)
|
||||
.then(exportKey)
|
||||
.then((keys) => ({
|
||||
privateKey: keys[0],
|
||||
publicKey: keys[1]
|
||||
})), callback)
|
||||
.then(exportKey)
|
||||
.then((keys) => ({
|
||||
privateKey: keys[0],
|
||||
publicKey: keys[1]
|
||||
})), callback)
|
||||
}
|
||||
|
||||
// Takes a jwk key
|
||||
exports.unmarshalPrivateKey = function (key, callback) {
|
||||
const privateKey = crypto.subtle.importKey(
|
||||
const privateKey = webcrypto.subtle.importKey(
|
||||
'jwk',
|
||||
key,
|
||||
{
|
||||
@@ -51,11 +50,11 @@ exports.unmarshalPrivateKey = function (key, callback) {
|
||||
}
|
||||
|
||||
exports.getRandomValues = function (arr) {
|
||||
return Buffer.from(crypto.getRandomValues(arr))
|
||||
return Buffer.from(webcrypto.getRandomValues(arr))
|
||||
}
|
||||
|
||||
exports.hashAndSign = function (key, msg, callback) {
|
||||
nodeify(crypto.subtle.importKey(
|
||||
nodeify(webcrypto.subtle.importKey(
|
||||
'jwk',
|
||||
key,
|
||||
{
|
||||
@@ -65,7 +64,7 @@ exports.hashAndSign = function (key, msg, callback) {
|
||||
false,
|
||||
['sign']
|
||||
).then((privateKey) => {
|
||||
return crypto.subtle.sign(
|
||||
return webcrypto.subtle.sign(
|
||||
{name: 'RSASSA-PKCS1-v1_5'},
|
||||
privateKey,
|
||||
Uint8Array.from(msg)
|
||||
@@ -74,7 +73,7 @@ exports.hashAndSign = function (key, msg, callback) {
|
||||
}
|
||||
|
||||
exports.hashAndVerify = function (key, sig, msg, callback) {
|
||||
nodeify(crypto.subtle.importKey(
|
||||
nodeify(webcrypto.subtle.importKey(
|
||||
'jwk',
|
||||
key,
|
||||
{
|
||||
@@ -84,7 +83,7 @@ exports.hashAndVerify = function (key, sig, msg, callback) {
|
||||
false,
|
||||
['verify']
|
||||
).then((publicKey) => {
|
||||
return crypto.subtle.verify(
|
||||
return webcrypto.subtle.verify(
|
||||
{name: 'RSASSA-PKCS1-v1_5'},
|
||||
publicKey,
|
||||
sig,
|
||||
@@ -95,13 +94,13 @@ exports.hashAndVerify = function (key, sig, msg, callback) {
|
||||
|
||||
function exportKey (pair) {
|
||||
return Promise.all([
|
||||
crypto.subtle.exportKey('jwk', pair.privateKey),
|
||||
crypto.subtle.exportKey('jwk', pair.publicKey)
|
||||
webcrypto.subtle.exportKey('jwk', pair.privateKey),
|
||||
webcrypto.subtle.exportKey('jwk', pair.publicKey)
|
||||
])
|
||||
}
|
||||
|
||||
function derivePublicFromPrivate (jwKey) {
|
||||
return crypto.subtle.importKey(
|
||||
return webcrypto.subtle.importKey(
|
||||
'jwk',
|
||||
{
|
||||
kty: jwKey.kty,
|
133
src/keys/rsa-class.js
Normal file
133
src/keys/rsa-class.js
Normal file
@@ -0,0 +1,133 @@
|
||||
'use strict'
|
||||
|
||||
const multihashing = require('multihashing-async')
|
||||
const protobuf = require('protons')
|
||||
|
||||
const crypto = require('./rsa')
|
||||
const pbm = protobuf(require('./keys.proto'))
|
||||
|
||||
class RsaPublicKey {
|
||||
constructor (key) {
|
||||
this._key = key
|
||||
}
|
||||
|
||||
verify (data, sig, callback) {
|
||||
ensure(callback)
|
||||
crypto.hashAndVerify(this._key, sig, data, callback)
|
||||
}
|
||||
|
||||
marshal () {
|
||||
return crypto.utils.jwkToPkix(this._key)
|
||||
}
|
||||
|
||||
get bytes () {
|
||||
return pbm.PublicKey.encode({
|
||||
Type: pbm.KeyType.RSA,
|
||||
Data: this.marshal()
|
||||
})
|
||||
}
|
||||
|
||||
encrypt (bytes) {
|
||||
return this._key.encrypt(bytes, 'RSAES-PKCS1-V1_5')
|
||||
}
|
||||
|
||||
equals (key) {
|
||||
return this.bytes.equals(key.bytes)
|
||||
}
|
||||
|
||||
hash (callback) {
|
||||
ensure(callback)
|
||||
multihashing(this.bytes, 'sha2-256', callback)
|
||||
}
|
||||
}
|
||||
|
||||
class RsaPrivateKey {
|
||||
// key - Object of the jwk format
|
||||
// publicKey - Buffer of the spki format
|
||||
constructor (key, publicKey) {
|
||||
this._key = key
|
||||
this._publicKey = publicKey
|
||||
}
|
||||
|
||||
genSecret () {
|
||||
return crypto.getRandomValues(new Uint8Array(16))
|
||||
}
|
||||
|
||||
sign (message, callback) {
|
||||
ensure(callback)
|
||||
crypto.hashAndSign(this._key, message, callback)
|
||||
}
|
||||
|
||||
get public () {
|
||||
if (!this._publicKey) {
|
||||
throw new Error('public key not provided')
|
||||
}
|
||||
|
||||
return new RsaPublicKey(this._publicKey)
|
||||
}
|
||||
|
||||
decrypt (msg, callback) {
|
||||
crypto.decrypt(this._key, msg, callback)
|
||||
}
|
||||
|
||||
marshal () {
|
||||
return crypto.utils.jwkToPkcs1(this._key)
|
||||
}
|
||||
|
||||
get bytes () {
|
||||
return pbm.PrivateKey.encode({
|
||||
Type: pbm.KeyType.RSA,
|
||||
Data: this.marshal()
|
||||
})
|
||||
}
|
||||
|
||||
equals (key) {
|
||||
return this.bytes.equals(key.bytes)
|
||||
}
|
||||
|
||||
hash (callback) {
|
||||
ensure(callback)
|
||||
multihashing(this.bytes, 'sha2-256', callback)
|
||||
}
|
||||
}
|
||||
|
||||
function unmarshalRsaPrivateKey (bytes, callback) {
|
||||
const jwk = crypto.utils.pkcs1ToJwk(bytes)
|
||||
crypto.unmarshalPrivateKey(jwk, (err, keys) => {
|
||||
if (err) {
|
||||
return callback(err)
|
||||
}
|
||||
|
||||
callback(null, new RsaPrivateKey(keys.privateKey, keys.publicKey))
|
||||
})
|
||||
}
|
||||
|
||||
function unmarshalRsaPublicKey (bytes) {
|
||||
const jwk = crypto.utils.pkixToJwk(bytes)
|
||||
|
||||
return new RsaPublicKey(jwk)
|
||||
}
|
||||
|
||||
function generateKeyPair (bits, cb) {
|
||||
crypto.generateKey(bits, (err, keys) => {
|
||||
if (err) {
|
||||
return cb(err)
|
||||
}
|
||||
|
||||
cb(null, new RsaPrivateKey(keys.privateKey, keys.publicKey))
|
||||
})
|
||||
}
|
||||
|
||||
function ensure (cb) {
|
||||
if (typeof cb !== 'function') {
|
||||
throw new Error('callback is required')
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
RsaPublicKey,
|
||||
RsaPrivateKey,
|
||||
unmarshalRsaPublicKey,
|
||||
unmarshalRsaPrivateKey,
|
||||
generateKeyPair
|
||||
}
|
@@ -2,7 +2,7 @@
|
||||
|
||||
const asn1 = require('asn1.js')
|
||||
|
||||
const util = require('./util')
|
||||
const util = require('./../util')
|
||||
const toBase64 = util.toBase64
|
||||
const toBn = util.toBn
|
||||
|
174
src/keys/rsa.js
174
src/keys/rsa.js
@@ -1,133 +1,79 @@
|
||||
'use strict'
|
||||
|
||||
const multihashing = require('multihashing-async')
|
||||
const protobuf = require('protocol-buffers')
|
||||
const crypto = require('crypto')
|
||||
const keypair = require('keypair')
|
||||
const setImmediate = require('async/setImmediate')
|
||||
const pemToJwk = require('pem-jwk').pem2jwk
|
||||
const jwkToPem = require('pem-jwk').jwk2pem
|
||||
|
||||
const crypto = require('../crypto').rsa
|
||||
const pbm = protobuf(require('../crypto.proto'))
|
||||
exports.utils = require('./rsa-utils')
|
||||
|
||||
class RsaPublicKey {
|
||||
constructor (key) {
|
||||
this._key = key
|
||||
}
|
||||
|
||||
verify (data, sig, callback) {
|
||||
ensure(callback)
|
||||
crypto.hashAndVerify(this._key, sig, data, callback)
|
||||
}
|
||||
|
||||
marshal () {
|
||||
return crypto.utils.jwkToPkix(this._key)
|
||||
}
|
||||
|
||||
get bytes () {
|
||||
return pbm.PublicKey.encode({
|
||||
Type: pbm.KeyType.RSA,
|
||||
Data: this.marshal()
|
||||
})
|
||||
}
|
||||
|
||||
encrypt (bytes) {
|
||||
return this._key.encrypt(bytes, 'RSAES-PKCS1-V1_5')
|
||||
}
|
||||
|
||||
equals (key) {
|
||||
return this.bytes.equals(key.bytes)
|
||||
}
|
||||
|
||||
hash (callback) {
|
||||
ensure(callback)
|
||||
multihashing(this.bytes, 'sha2-256', callback)
|
||||
}
|
||||
}
|
||||
|
||||
class RsaPrivateKey {
|
||||
// key - Object of the jwk format
|
||||
// publicKey - Buffer of the spki format
|
||||
constructor (key, publicKey) {
|
||||
this._key = key
|
||||
this._publicKey = publicKey
|
||||
}
|
||||
|
||||
genSecret () {
|
||||
return crypto.getRandomValues(new Uint8Array(16))
|
||||
}
|
||||
|
||||
sign (message, callback) {
|
||||
ensure(callback)
|
||||
crypto.hashAndSign(this._key, message, callback)
|
||||
}
|
||||
|
||||
get public () {
|
||||
if (!this._publicKey) {
|
||||
throw new Error('public key not provided')
|
||||
}
|
||||
|
||||
return new RsaPublicKey(this._publicKey)
|
||||
}
|
||||
|
||||
decrypt (msg, callback) {
|
||||
crypto.decrypt(this._key, msg, callback)
|
||||
}
|
||||
|
||||
marshal () {
|
||||
return crypto.utils.jwkToPkcs1(this._key)
|
||||
}
|
||||
|
||||
get bytes () {
|
||||
return pbm.PrivateKey.encode({
|
||||
Type: pbm.KeyType.RSA,
|
||||
Data: this.marshal()
|
||||
})
|
||||
}
|
||||
|
||||
equals (key) {
|
||||
return this.bytes.equals(key.bytes)
|
||||
}
|
||||
|
||||
hash (callback) {
|
||||
ensure(callback)
|
||||
multihashing(this.bytes, 'sha2-256', callback)
|
||||
}
|
||||
}
|
||||
|
||||
function unmarshalRsaPrivateKey (bytes, callback) {
|
||||
const jwk = crypto.utils.pkcs1ToJwk(bytes)
|
||||
crypto.unmarshalPrivateKey(jwk, (err, keys) => {
|
||||
if (err) {
|
||||
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, new RsaPrivateKey(keys.privateKey, keys.publicKey))
|
||||
callback(null, result)
|
||||
})
|
||||
}
|
||||
|
||||
function unmarshalRsaPublicKey (bytes) {
|
||||
const jwk = crypto.utils.pkixToJwk(bytes)
|
||||
|
||||
return new RsaPublicKey(jwk)
|
||||
// Takes a jwk key
|
||||
exports.unmarshalPrivateKey = function (key, callback) {
|
||||
setImmediate(() => {
|
||||
if (!key) {
|
||||
return callback(new Error('Key is invalid'))
|
||||
}
|
||||
callback(null, {
|
||||
privateKey: key,
|
||||
publicKey: {
|
||||
kty: key.kty,
|
||||
n: key.n,
|
||||
e: key.e
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function generateKeyPair (bits, cb) {
|
||||
crypto.generateKey(bits, (err, keys) => {
|
||||
if (err) {
|
||||
return cb(err)
|
||||
exports.getRandomValues = function (arr) {
|
||||
return crypto.randomBytes(arr.length)
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
cb(null, new RsaPrivateKey(keys.privateKey, keys.publicKey))
|
||||
callback(null, result)
|
||||
})
|
||||
}
|
||||
|
||||
function ensure (cb) {
|
||||
if (typeof cb !== 'function') {
|
||||
throw new Error('callback is required')
|
||||
}
|
||||
}
|
||||
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))
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
RsaPublicKey,
|
||||
RsaPrivateKey,
|
||||
unmarshalRsaPublicKey,
|
||||
unmarshalRsaPrivateKey,
|
||||
generateKeyPair
|
||||
callback(null, result)
|
||||
})
|
||||
}
|
||||
|
11
src/nodeify.js
Normal file
11
src/nodeify.js
Normal file
@@ -0,0 +1,11 @@
|
||||
'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)
|
||||
})
|
||||
}
|
13
src/random-bytes.js
Normal file
13
src/random-bytes.js
Normal file
@@ -0,0 +1,13 @@
|
||||
'use strict'
|
||||
|
||||
const rsa = require('./keys/rsa')
|
||||
|
||||
function randomBytes (number) {
|
||||
if (!number || typeof number !== 'number') {
|
||||
throw new Error('first argument must be a Number bigger than 0')
|
||||
}
|
||||
|
||||
return rsa.getRandomValues(new Uint8Array(number))
|
||||
}
|
||||
|
||||
module.exports = randomBytes
|
@@ -1,17 +1,17 @@
|
||||
'use strict'
|
||||
|
||||
const BN = require('asn1.js').bignum
|
||||
const Buffer = require('safe-buffer').Buffer
|
||||
|
||||
// Convert a BN.js instance to a base64 encoded string without padding
|
||||
// Adapted from https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-41#appendix-C
|
||||
exports.toBase64 = function toBase64 (bn) {
|
||||
let s = bn.toArrayLike(Buffer, 'be').toString('base64')
|
||||
exports.toBase64 = function toBase64 (bn, len) {
|
||||
// if len is defined then the bytes are leading-0 padded to the length
|
||||
let s = bn.toArrayLike(Buffer, 'be', len).toString('base64')
|
||||
|
||||
return s
|
||||
.replace(/(=*)$/, '') // Remove any trailing '='s
|
||||
.replace(/\+/g, '-') // 62nd char of encoding
|
||||
.replace(/\//g, '_') // 63rd char of encoding
|
||||
.replace(/\+/g, '-') // 62nd char of encoding
|
||||
.replace(/\//g, '_') // 63rd char of encoding
|
||||
}
|
||||
|
||||
// Convert a base64 encoded string to a BN.js instance
|
16
src/webcrypto.js
Normal file
16
src/webcrypto.js
Normal file
@@ -0,0 +1,16 @@
|
||||
/* global self */
|
||||
|
||||
'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')
|
||||
}
|
@@ -2,13 +2,15 @@
|
||||
/* eslint-env mocha */
|
||||
'use strict'
|
||||
|
||||
const expect = require('chai').expect
|
||||
const chai = require('chai')
|
||||
const dirtyChai = require('dirty-chai')
|
||||
const expect = chai.expect
|
||||
chai.use(dirtyChai)
|
||||
const series = require('async/series')
|
||||
const Buffer = require('safe-buffer').Buffer
|
||||
|
||||
const crypto = require('../src')
|
||||
const fixtures = require('./fixtures/aes')
|
||||
const goFixtures = require('./fixtures/go-aes')
|
||||
const crypto = require('../../src')
|
||||
const fixtures = require('./../fixtures/aes')
|
||||
const goFixtures = require('./../fixtures/go-aes')
|
||||
|
||||
const bytes = {
|
||||
16: 'AES-128',
|
||||
@@ -25,7 +27,7 @@ describe('AES-CTR', () => {
|
||||
iv.fill(1)
|
||||
|
||||
crypto.aes.create(key, iv, (err, cipher) => {
|
||||
expect(err).to.not.exist
|
||||
expect(err).to.not.exist()
|
||||
|
||||
series([
|
||||
encryptAndDecrypt(cipher),
|
||||
@@ -37,6 +39,7 @@ describe('AES-CTR', () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Object.keys(bytes).forEach((byte) => {
|
||||
it(`${bytes[byte]} - fixed - encrypt and decrypt`, (done) => {
|
||||
const key = Buffer.alloc(parseInt(byte, 10))
|
||||
@@ -46,18 +49,18 @@ describe('AES-CTR', () => {
|
||||
iv.fill(1)
|
||||
|
||||
crypto.aes.create(key, iv, (err, cipher) => {
|
||||
expect(err).to.not.exist
|
||||
expect(err).to.not.exist()
|
||||
|
||||
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(err).to.not.exist()
|
||||
expect(res).to.have.length(output.length)
|
||||
expect(res).to.be.eql(output)
|
||||
expect(res).to.eql(output)
|
||||
cipher.decrypt(res, (err, res) => {
|
||||
expect(err).to.not.exist
|
||||
expect(res).to.be.eql(input)
|
||||
expect(err).to.not.exist()
|
||||
expect(res).to.eql(input)
|
||||
cb()
|
||||
})
|
||||
})
|
||||
@@ -79,17 +82,17 @@ describe('AES-CTR', () => {
|
||||
iv.fill(1)
|
||||
|
||||
crypto.aes.create(key, iv, (err, cipher) => {
|
||||
expect(err).to.not.exist
|
||||
expect(err).to.not.exist()
|
||||
|
||||
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(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(err).to.not.exist()
|
||||
expect(res).to.be.eql(input)
|
||||
cb()
|
||||
})
|
||||
@@ -105,9 +108,9 @@ function encryptAndDecrypt (cipher) {
|
||||
data.fill(Math.ceil(Math.random() * 100))
|
||||
return (cb) => {
|
||||
cipher.encrypt(data, (err, res) => {
|
||||
expect(err).to.not.exist
|
||||
expect(err).to.not.exist()
|
||||
cipher.decrypt(res, (err, res) => {
|
||||
expect(err).to.not.exist
|
||||
expect(err).to.not.exist()
|
||||
expect(res).to.be.eql(data)
|
||||
cb()
|
||||
})
|
@@ -2,15 +2,18 @@
|
||||
/* eslint-env mocha */
|
||||
'use strict'
|
||||
|
||||
const expect = require('chai').expect
|
||||
|
||||
const chai = require('chai')
|
||||
const dirtyChai = require('dirty-chai')
|
||||
const expect = chai.expect
|
||||
chai.use(dirtyChai)
|
||||
const crypto = require('../src')
|
||||
const fixtures = require('./fixtures/go-key-rsa')
|
||||
|
||||
describe('libp2p-crypto', () => {
|
||||
describe('libp2p-crypto', function () {
|
||||
this.timeout(20 * 1000)
|
||||
let key
|
||||
before((done) => {
|
||||
crypto.generateKeyPair('RSA', 2048, (err, _key) => {
|
||||
crypto.keys.generateKeyPair('RSA', 2048, (err, _key) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
||||
@@ -20,21 +23,22 @@ describe('libp2p-crypto', () => {
|
||||
})
|
||||
|
||||
it('marshalPublicKey and unmarshalPublicKey', () => {
|
||||
const key2 = crypto.unmarshalPublicKey(crypto.marshalPublicKey(key.public))
|
||||
const key2 = crypto.keys.unmarshalPublicKey(
|
||||
crypto.keys.marshalPublicKey(key.public))
|
||||
|
||||
expect(key2.equals(key.public)).to.be.eql(true)
|
||||
|
||||
expect(() => {
|
||||
crypto.marshalPublicKey(key.public, 'invalid-key-type')
|
||||
crypto.keys.marshalPublicKey(key.public, 'invalid-key-type')
|
||||
}).to.throw()
|
||||
})
|
||||
|
||||
it('marshalPrivateKey and unmarshalPrivateKey', (done) => {
|
||||
expect(() => {
|
||||
crypto.marshalPrivateKey(key, 'invalid-key-type')
|
||||
crypto.keys.marshalPrivateKey(key, 'invalid-key-type')
|
||||
}).to.throw()
|
||||
|
||||
crypto.unmarshalPrivateKey(crypto.marshalPrivateKey(key), (err, key2) => {
|
||||
crypto.keys.unmarshalPrivateKey(crypto.keys.marshalPrivateKey(key), (err, key2) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
||||
@@ -50,60 +54,56 @@ describe('libp2p-crypto', () => {
|
||||
// or a bug
|
||||
describe('go interop', () => {
|
||||
it('unmarshals private key', (done) => {
|
||||
crypto.unmarshalPrivateKey(fixtures.private.key, (err, key) => {
|
||||
crypto.keys.unmarshalPrivateKey(fixtures.private.key, (err, key) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
||||
const hash = fixtures.private.hash
|
||||
expect(fixtures.private.key).to.be.eql(key.bytes)
|
||||
expect(fixtures.private.key).to.eql(key.bytes)
|
||||
|
||||
key.hash((err, digest) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
||||
|
||||
expect(digest).to.be.eql(hash)
|
||||
expect(digest).to.eql(hash)
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('unmarshals public key', (done) => {
|
||||
const key = crypto.unmarshalPublicKey(fixtures.public.key)
|
||||
const key = crypto.keys.unmarshalPublicKey(fixtures.public.key)
|
||||
const hash = fixtures.public.hash
|
||||
|
||||
expect(crypto.marshalPublicKey(key)).to.be.eql(fixtures.public.key)
|
||||
expect(crypto.keys.marshalPublicKey(key)).to.eql(fixtures.public.key)
|
||||
|
||||
key.hash((err, digest) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
||||
|
||||
expect(digest).to.be.eql(hash)
|
||||
expect(digest).to.eql(hash)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('unmarshal -> marshal, private key', (done) => {
|
||||
crypto.unmarshalPrivateKey(fixtures.private.key, (err, key) => {
|
||||
crypto.keys.unmarshalPrivateKey(fixtures.private.key, (err, key) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
||||
|
||||
const marshalled = crypto.marshalPrivateKey(key)
|
||||
expect(marshalled).to.be.eql(fixtures.private.key)
|
||||
const marshalled = crypto.keys.marshalPrivateKey(key)
|
||||
expect(marshalled).to.eql(fixtures.private.key)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('unmarshal -> marshal, public key', () => {
|
||||
const key = crypto.unmarshalPublicKey(fixtures.public.key)
|
||||
const marshalled = crypto.marshalPublicKey(key)
|
||||
expect(
|
||||
fixtures.public.key.equals(marshalled)
|
||||
).to.be.eql(
|
||||
true
|
||||
)
|
||||
const key = crypto.keys.unmarshalPublicKey(fixtures.public.key)
|
||||
const marshalled = crypto.keys.marshalPublicKey(key)
|
||||
expect(fixtures.public.key.equals(marshalled)).to.eql(true)
|
||||
})
|
||||
})
|
||||
|
@@ -1,195 +0,0 @@
|
||||
/* eslint-env mocha */
|
||||
'use strict'
|
||||
|
||||
const expect = require('chai').expect
|
||||
const Buffer = require('safe-buffer').Buffer
|
||||
|
||||
const crypto = require('../src')
|
||||
const ed25519 = crypto.keys.ed25519
|
||||
const fixtures = require('./fixtures/go-key-ed25519')
|
||||
|
||||
describe('ed25519', () => {
|
||||
let key
|
||||
before((done) => {
|
||||
crypto.generateKeyPair('Ed25519', 512, (err, _key) => {
|
||||
if (err) return done(err)
|
||||
key = _key
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('generates a valid key', (done) => {
|
||||
expect(
|
||||
key
|
||||
).to.be.an.instanceof(
|
||||
ed25519.Ed25519PrivateKey
|
||||
)
|
||||
|
||||
key.hash((err, digest) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
||||
|
||||
expect(digest).to.have.length(34)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('signs', (done) => {
|
||||
const text = crypto.randomBytes(512)
|
||||
|
||||
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()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('encoding', (done) => {
|
||||
const keyMarshal = key.marshal()
|
||||
ed25519.unmarshalEd25519PrivateKey(keyMarshal, (err, key2) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
||||
const keyMarshal2 = key2.marshal()
|
||||
|
||||
expect(
|
||||
keyMarshal
|
||||
).to.be.eql(
|
||||
keyMarshal2
|
||||
)
|
||||
|
||||
const pk = key.public
|
||||
const pkMarshal = pk.marshal()
|
||||
const pk2 = ed25519.unmarshalEd25519PublicKey(pkMarshal)
|
||||
const pkMarshal2 = pk2.marshal()
|
||||
|
||||
expect(
|
||||
pkMarshal
|
||||
).to.be.eql(
|
||||
pkMarshal2
|
||||
)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
describe('key equals', () => {
|
||||
it('equals itself', () => {
|
||||
expect(
|
||||
key.equals(key)
|
||||
).to.be.eql(
|
||||
true
|
||||
)
|
||||
|
||||
expect(
|
||||
key.public.equals(key.public)
|
||||
).to.be.eql(
|
||||
true
|
||||
)
|
||||
})
|
||||
|
||||
it('not equals other key', (done) => {
|
||||
crypto.generateKeyPair('Ed25519', 512, (err, key2) => {
|
||||
if (err) return done(err)
|
||||
|
||||
expect(
|
||||
key.equals(key2)
|
||||
).to.be.eql(
|
||||
false
|
||||
)
|
||||
|
||||
expect(
|
||||
key2.equals(key)
|
||||
).to.be.eql(
|
||||
false
|
||||
)
|
||||
|
||||
expect(
|
||||
key.public.equals(key2.public)
|
||||
).to.be.eql(
|
||||
false
|
||||
)
|
||||
|
||||
expect(
|
||||
key2.public.equals(key.public)
|
||||
).to.be.eql(
|
||||
false
|
||||
)
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('sign and verify', (done) => {
|
||||
const data = Buffer.from('hello world')
|
||||
key.sign(data, (err, sig) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
||||
|
||||
key.public.verify(data, sig, (err, valid) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
||||
expect(valid).to.be.eql(true)
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('fails to verify for different data', (done) => {
|
||||
const data = Buffer.from('hello world')
|
||||
key.sign(data, (err, sig) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
||||
|
||||
key.public.verify(Buffer.from('hello'), sig, (err, valid) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
||||
expect(valid).to.be.eql(false)
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('go interop', () => {
|
||||
let privateKey
|
||||
before((done) => {
|
||||
crypto.unmarshalPrivateKey(fixtures.verify.privateKey, (err, key) => {
|
||||
expect(err).to.not.exist
|
||||
privateKey = key
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('verifies with data from go', (done) => {
|
||||
const key = crypto.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.be.eql(true)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('generates the same signature as go', (done) => {
|
||||
privateKey.sign(fixtures.verify.data, (err, sig) => {
|
||||
expect(err).to.not.exist
|
||||
expect(sig).to.deep.equal(fixtures.verify.signature)
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
2
test/fixtures/go-elliptic-key.js
vendored
2
test/fixtures/go-elliptic-key.js
vendored
@@ -1,7 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
const Buffer = require('safe-buffer').Buffer
|
||||
|
||||
module.exports = {
|
||||
curve: 'P-256',
|
||||
bob: {
|
||||
|
2
test/fixtures/go-key-ed25519.js
vendored
2
test/fixtures/go-key-ed25519.js
vendored
@@ -1,7 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
const Buffer = require('safe-buffer').Buffer
|
||||
|
||||
module.exports = {
|
||||
// These were generated in a gore (https://github.com/motemen/gore) repl session:
|
||||
//
|
||||
|
2
test/fixtures/go-key-rsa.js
vendored
2
test/fixtures/go-key-rsa.js
vendored
@@ -1,7 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
const Buffer = require('safe-buffer').Buffer
|
||||
|
||||
module.exports = {
|
||||
private: {
|
||||
hash: Buffer.from([
|
||||
|
2
test/fixtures/go-stretch-key.js
vendored
2
test/fixtures/go-stretch-key.js
vendored
@@ -1,7 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
const Buffer = require('safe-buffer').Buffer
|
||||
|
||||
module.exports = [{
|
||||
cipher: 'AES-256',
|
||||
hash: 'SHA256',
|
||||
|
3
test/fixtures/secp256k1.js
vendored
3
test/fixtures/secp256k1.js
vendored
@@ -1,9 +1,6 @@
|
||||
'use strict'
|
||||
|
||||
const Buffer = require('safe-buffer').Buffer
|
||||
|
||||
module.exports = {
|
||||
|
||||
// protobuf marshaled key pair generated with libp2p-crypto-secp256k1
|
||||
// and marshaled with libp2p-crypto.marshalPublicKey / marshalPrivateKey
|
||||
pbmPrivateKey: Buffer.from('08021220e0600103010000000100000000000000be1dc82c2e000000e8d6030301000000', 'hex'),
|
||||
|
46
test/helpers/test-garbage-error-handling.js
Normal file
46
test/helpers/test-garbage-error-handling.js
Normal file
@@ -0,0 +1,46 @@
|
||||
/* 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', '']
|
||||
|
||||
function doTests (fncName, fnc, num, skipBuffersAndStrings) {
|
||||
if (!num) {
|
||||
num = 1
|
||||
}
|
||||
|
||||
garbage.forEach((garbage) => {
|
||||
if (skipBuffersAndStrings && (Buffer.isBuffer(garbage) || (typeof garbage) === 'string')) {
|
||||
// skip this garbage because it's a buffer or a string and we were told do do that
|
||||
return
|
||||
}
|
||||
let args = []
|
||||
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)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = (obj, fncs, num) => {
|
||||
describe('returns error via cb instead of crashing', () => {
|
||||
fncs.forEach(fnc => {
|
||||
doTests(fnc, obj[fnc], num)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
module.exports.doTests = doTests
|
@@ -2,10 +2,12 @@
|
||||
/* eslint-env mocha */
|
||||
'use strict'
|
||||
|
||||
const Buffer = require('safe-buffer').Buffer
|
||||
const expect = require('chai').expect
|
||||
const chai = require('chai')
|
||||
const dirtyChai = require('dirty-chai')
|
||||
const expect = chai.expect
|
||||
chai.use(dirtyChai)
|
||||
|
||||
const crypto = require('../src')
|
||||
const crypto = require('../../src')
|
||||
|
||||
const hashes = ['SHA1', 'SHA256', 'SHA512']
|
||||
|
||||
@@ -13,10 +15,10 @@ 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
|
||||
expect(err).to.not.exist()
|
||||
|
||||
hmac.digest(Buffer.from('hello world'), (err, sig) => {
|
||||
expect(err).to.not.exist
|
||||
expect(err).to.not.exist()
|
||||
expect(sig).to.have.length(hmac.length)
|
||||
done()
|
||||
})
|
217
test/keys/ed25519.spec.js
Normal file
217
test/keys/ed25519.spec.js
Normal file
@@ -0,0 +1,217 @@
|
||||
/* eslint-env mocha */
|
||||
'use strict'
|
||||
|
||||
const chai = require('chai')
|
||||
const dirtyChai = require('dirty-chai')
|
||||
const expect = chai.expect
|
||||
chai.use(dirtyChai)
|
||||
|
||||
const crypto = require('../../src')
|
||||
const ed25519 = crypto.keys.supportedKeys.ed25519
|
||||
const fixtures = require('../fixtures/go-key-ed25519')
|
||||
|
||||
const testGarbage = require('../helpers/test-garbage-error-handling')
|
||||
|
||||
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()
|
||||
})
|
||||
})
|
||||
|
||||
it('generates a valid key', (done) => {
|
||||
expect(key).to.be.an.instanceof(ed25519.Ed25519PrivateKey)
|
||||
|
||||
key.hash((err, digest) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
||||
|
||||
expect(digest).to.have.length(34)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('generates a valid key from seed', (done) => {
|
||||
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()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
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 different keys for different seeds', (done) => {
|
||||
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()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('signs', (done) => {
|
||||
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()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('encoding', (done) => {
|
||||
const keyMarshal = key.marshal()
|
||||
ed25519.unmarshalEd25519PrivateKey(keyMarshal, (err, key2) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
||||
const keyMarshal2 = key2.marshal()
|
||||
|
||||
expect(keyMarshal).to.eql(keyMarshal2)
|
||||
|
||||
const pk = key.public
|
||||
const pkMarshal = pk.marshal()
|
||||
const pk2 = ed25519.unmarshalEd25519PublicKey(pkMarshal)
|
||||
const pkMarshal2 = pk2.marshal()
|
||||
|
||||
expect(pkMarshal).to.eql(pkMarshal2)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
describe('key equals', () => {
|
||||
it('equals itself', () => {
|
||||
expect(
|
||||
key.equals(key)
|
||||
).to.eql(
|
||||
true
|
||||
)
|
||||
|
||||
expect(
|
||||
key.public.equals(key.public)
|
||||
).to.eql(
|
||||
true
|
||||
)
|
||||
})
|
||||
|
||||
it('not equals other key', (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('sign and verify', (done) => {
|
||||
const data = Buffer.from('hello world')
|
||||
key.sign(data, (err, sig) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
||||
|
||||
key.public.verify(data, sig, (err, valid) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
||||
expect(valid).to.eql(true)
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('fails to verify for different data', (done) => {
|
||||
const data = Buffer.from('hello world')
|
||||
key.sign(data, (err, sig) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
||||
|
||||
key.public.verify(Buffer.from('hello'), sig, (err, valid) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
||||
expect(valid).to.be.eql(false)
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('returns error via cb instead of crashing', () => {
|
||||
const key = crypto.keys.unmarshalPublicKey(fixtures.verify.publicKey)
|
||||
testGarbage.doTests('key.verify', key.verify.bind(key), 2)
|
||||
testGarbage.doTests('crypto.keys.unmarshalPrivateKey', crypto.keys.unmarshalPrivateKey.bind(crypto.keys))
|
||||
})
|
||||
|
||||
describe('go interop', () => {
|
||||
let privateKey
|
||||
|
||||
before((done) => {
|
||||
crypto.keys.unmarshalPrivateKey(fixtures.verify.privateKey, (err, key) => {
|
||||
expect(err).to.not.exist()
|
||||
privateKey = key
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('verifies with data from go', (done) => {
|
||||
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()
|
||||
})
|
||||
})
|
||||
|
||||
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()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
@@ -2,11 +2,14 @@
|
||||
/* eslint-env mocha */
|
||||
'use strict'
|
||||
|
||||
const expect = require('chai').expect
|
||||
const chai = require('chai')
|
||||
const dirtyChai = require('dirty-chai')
|
||||
const expect = chai.expect
|
||||
chai.use(dirtyChai)
|
||||
const parallel = require('async/parallel')
|
||||
|
||||
const fixtures = require('./fixtures/go-elliptic-key')
|
||||
const crypto = require('../src')
|
||||
const fixtures = require('../fixtures/go-elliptic-key')
|
||||
const crypto = require('../../src')
|
||||
|
||||
const curves = ['P-256', 'P-384'] // 'P-521' fails in tests :( no clue why
|
||||
const lengths = {
|
||||
@@ -24,15 +27,15 @@ describe('generateEphemeralKeyPair', () => {
|
||||
curves.forEach((curve) => {
|
||||
it(`generate and shared key ${curve}`, (done) => {
|
||||
parallel([
|
||||
(cb) => crypto.generateEphemeralKeyPair(curve, cb),
|
||||
(cb) => crypto.generateEphemeralKeyPair(curve, cb)
|
||||
(cb) => crypto.keys.generateEphemeralKeyPair(curve, cb),
|
||||
(cb) => crypto.keys.generateEphemeralKeyPair(curve, cb)
|
||||
], (err, keys) => {
|
||||
expect(err).to.not.exist
|
||||
expect(err).to.not.exist()
|
||||
expect(keys[0].key).to.have.length(lengths[curve])
|
||||
expect(keys[1].key).to.have.length(lengths[curve])
|
||||
|
||||
keys[0].genSharedKey(keys[1].key, (err, shared) => {
|
||||
expect(err).to.not.exist
|
||||
expect(err).to.not.exist()
|
||||
expect(shared).to.have.length(secretLengths[curve])
|
||||
done()
|
||||
})
|
||||
@@ -45,10 +48,10 @@ describe('generateEphemeralKeyPair', () => {
|
||||
const curve = fixtures.curve
|
||||
|
||||
parallel([
|
||||
(cb) => crypto.generateEphemeralKeyPair(curve, cb),
|
||||
(cb) => crypto.generateEphemeralKeyPair(curve, cb)
|
||||
(cb) => crypto.keys.generateEphemeralKeyPair(curve, cb),
|
||||
(cb) => crypto.keys.generateEphemeralKeyPair(curve, cb)
|
||||
], (err, res) => {
|
||||
expect(err).to.not.exist
|
||||
expect(err).to.not.exist()
|
||||
const alice = res[0]
|
||||
const bob = res[1]
|
||||
bob.key = fixtures.bob.public
|
||||
@@ -57,14 +60,9 @@ describe('generateEphemeralKeyPair', () => {
|
||||
(cb) => alice.genSharedKey(bob.key, cb),
|
||||
(cb) => bob.genSharedKey(alice.key, fixtures.bob, cb)
|
||||
], (err, secrets) => {
|
||||
expect(err).to.not.exist
|
||||
|
||||
expect(
|
||||
secrets[0]
|
||||
).to.be.eql(
|
||||
secrets[1]
|
||||
)
|
||||
expect(err).to.not.exist()
|
||||
|
||||
expect(secrets[0]).to.eql(secrets[1])
|
||||
expect(secrets[0]).to.have.length(32)
|
||||
|
||||
done()
|
@@ -2,10 +2,12 @@
|
||||
/* eslint-env mocha */
|
||||
'use strict'
|
||||
|
||||
const expect = require('chai').expect
|
||||
|
||||
const crypto = require('../src')
|
||||
const fixtures = require('./fixtures/go-stretch-key')
|
||||
const chai = require('chai')
|
||||
const dirtyChai = require('dirty-chai')
|
||||
const expect = chai.expect
|
||||
chai.use(dirtyChai)
|
||||
const crypto = require('../../src')
|
||||
const fixtures = require('../fixtures/go-stretch-key')
|
||||
|
||||
describe('keyStretcher', () => {
|
||||
describe('generate', () => {
|
||||
@@ -15,7 +17,7 @@ describe('keyStretcher', () => {
|
||||
let secret
|
||||
|
||||
before((done) => {
|
||||
crypto.generateEphemeralKeyPair('P-256', (err, _res) => {
|
||||
crypto.keys.generateEphemeralKeyPair('P-256', (err, _res) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
||||
@@ -34,13 +36,13 @@ describe('keyStretcher', () => {
|
||||
ciphers.forEach((cipher) => {
|
||||
hashes.forEach((hash) => {
|
||||
it(`${cipher} - ${hash}`, (done) => {
|
||||
crypto.keyStretcher(cipher, hash, secret, (err, keys) => {
|
||||
crypto.keys.keyStretcher(cipher, hash, secret, (err, keys) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
||||
|
||||
expect(keys.k1).to.exist
|
||||
expect(keys.k2).to.exist
|
||||
expect(keys.k1).to.exist()
|
||||
expect(keys.k2).to.exist()
|
||||
done()
|
||||
})
|
||||
})
|
||||
@@ -54,7 +56,7 @@ describe('keyStretcher', () => {
|
||||
const cipher = test.cipher
|
||||
const hash = test.hash
|
||||
const secret = test.secret
|
||||
crypto.keyStretcher(cipher, hash, secret, (err, keys) => {
|
||||
crypto.keys.keyStretcher(cipher, hash, secret, (err, keys) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
@@ -1,29 +1,33 @@
|
||||
/* eslint-env mocha */
|
||||
'use strict'
|
||||
|
||||
const expect = require('chai').expect
|
||||
const Buffer = require('safe-buffer').Buffer
|
||||
const chai = require('chai')
|
||||
const dirtyChai = require('dirty-chai')
|
||||
const expect = chai.expect
|
||||
chai.use(dirtyChai)
|
||||
|
||||
const crypto = require('../src')
|
||||
const rsa = crypto.keys.rsa
|
||||
const fixtures = require('./fixtures/go-key-rsa')
|
||||
const crypto = require('../../src')
|
||||
const rsa = crypto.keys.supportedKeys.rsa
|
||||
const fixtures = require('../fixtures/go-key-rsa')
|
||||
|
||||
describe('RSA', () => {
|
||||
const testGarbage = require('../helpers/test-garbage-error-handling')
|
||||
|
||||
describe('RSA', function () {
|
||||
this.timeout(20 * 1000)
|
||||
let key
|
||||
|
||||
before((done) => {
|
||||
crypto.generateKeyPair('RSA', 2048, (err, _key) => {
|
||||
if (err) return done(err)
|
||||
crypto.keys.generateKeyPair('RSA', 2048, (err, _key) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
||||
key = _key
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('generates a valid key', (done) => {
|
||||
expect(
|
||||
key
|
||||
).to.be.an.instanceof(
|
||||
rsa.RsaPrivateKey
|
||||
)
|
||||
expect(key).to.be.an.instanceof(rsa.RsaPrivateKey)
|
||||
|
||||
key.hash((err, digest) => {
|
||||
if (err) {
|
||||
@@ -62,68 +66,35 @@ describe('RSA', () => {
|
||||
}
|
||||
const keyMarshal2 = key2.marshal()
|
||||
|
||||
expect(
|
||||
keyMarshal
|
||||
).to.be.eql(
|
||||
keyMarshal2
|
||||
)
|
||||
expect(keyMarshal).to.eql(keyMarshal2)
|
||||
|
||||
const pk = key.public
|
||||
const pkMarshal = pk.marshal()
|
||||
const pk2 = rsa.unmarshalRsaPublicKey(pkMarshal)
|
||||
const pkMarshal2 = pk2.marshal()
|
||||
|
||||
expect(
|
||||
pkMarshal
|
||||
).to.be.eql(
|
||||
pkMarshal2
|
||||
)
|
||||
expect(pkMarshal).to.eql(pkMarshal2)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
describe('key equals', () => {
|
||||
it('equals itself', () => {
|
||||
expect(
|
||||
key.equals(key)
|
||||
).to.be.eql(
|
||||
true
|
||||
)
|
||||
expect(key.equals(key)).to.eql(true)
|
||||
|
||||
expect(
|
||||
key.public.equals(key.public)
|
||||
).to.be.eql(
|
||||
true
|
||||
)
|
||||
expect(key.public.equals(key.public)).to.eql(true)
|
||||
})
|
||||
|
||||
it('not equals other key', (done) => {
|
||||
crypto.generateKeyPair('RSA', 2048, (err, key2) => {
|
||||
if (err) return done(err)
|
||||
crypto.keys.generateKeyPair('RSA', 2048, (err, key2) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
||||
|
||||
expect(
|
||||
key.equals(key2)
|
||||
).to.be.eql(
|
||||
false
|
||||
)
|
||||
|
||||
expect(
|
||||
key2.equals(key)
|
||||
).to.be.eql(
|
||||
false
|
||||
)
|
||||
|
||||
expect(
|
||||
key.public.equals(key2.public)
|
||||
).to.be.eql(
|
||||
false
|
||||
)
|
||||
|
||||
expect(
|
||||
key2.public.equals(key.public)
|
||||
).to.be.eql(
|
||||
false
|
||||
)
|
||||
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()
|
||||
})
|
||||
})
|
||||
@@ -163,14 +134,20 @@ describe('RSA', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('returns error via cb instead of crashing', () => {
|
||||
const key = crypto.keys.unmarshalPublicKey(fixtures.verify.publicKey)
|
||||
testGarbage.doTests('key.verify', key.verify.bind(key), 2, true)
|
||||
testGarbage.doTests('crypto.keys.unmarshalPrivateKey', crypto.keys.unmarshalPrivateKey.bind(crypto.keys))
|
||||
})
|
||||
|
||||
describe('go interop', () => {
|
||||
it('verifies with data from go', (done) => {
|
||||
const key = crypto.unmarshalPublicKey(fixtures.verify.publicKey)
|
||||
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.be.eql(true)
|
||||
expect(err).to.not.exist()
|
||||
expect(ok).to.equal(true)
|
||||
done()
|
||||
})
|
||||
})
|
99
test/keys/secp256k1.spec.js
Normal file
99
test/keys/secp256k1.spec.js
Normal file
@@ -0,0 +1,99 @@
|
||||
/* eslint-env mocha */
|
||||
'use strict'
|
||||
|
||||
const chai = require('chai')
|
||||
const dirtyChai = require('dirty-chai')
|
||||
const expect = chai.expect
|
||||
chai.use(dirtyChai)
|
||||
const fixtures = require('../fixtures/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()
|
||||
})
|
||||
})
|
||||
|
||||
it('fails to unmarshal a secp256k1 private key', (done) => {
|
||||
crypto.keys.unmarshalPrivateKey(fixtures.pbmPrivateKey, (err, key) => {
|
||||
expect(err).to.exist()
|
||||
expect(key).to.not.exist()
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('fails to unmarshal a secp256k1 public key', () => {
|
||||
expect(() => {
|
||||
crypto.keys.unmarshalPublicKey(fixtures.pbmPublicKey)
|
||||
}).to.throw(Error)
|
||||
})
|
||||
})
|
||||
|
||||
describe('with libp2p-crypto-secp256k1 module present', () => {
|
||||
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) => {
|
||||
expect(key).to.exist()
|
||||
done()
|
||||
})
|
||||
|
||||
it('protobuf encoding', (done) => {
|
||||
const keyMarshal = crypto.keys.marshalPrivateKey(key)
|
||||
crypto.keys.unmarshalPrivateKey(keyMarshal, (err, key2) => {
|
||||
if (err) return done(err)
|
||||
const keyMarshal2 = crypto.keys.marshalPrivateKey(key2)
|
||||
|
||||
expect(keyMarshal).to.eql(keyMarshal2)
|
||||
|
||||
const pk = key.public
|
||||
const pkMarshal = crypto.keys.marshalPublicKey(pk)
|
||||
const pk2 = crypto.keys.unmarshalPublicKey(pkMarshal)
|
||||
const pkMarshal2 = crypto.keys.marshalPublicKey(pk2)
|
||||
|
||||
expect(pkMarshal).to.eql(pkMarshal2)
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
@@ -1,104 +0,0 @@
|
||||
/* eslint-env mocha */
|
||||
'use strict'
|
||||
|
||||
const expect = require('chai').expect
|
||||
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('with libp2p-crypto-secp256k1 module present', () => {
|
||||
let key
|
||||
|
||||
before((done) => {
|
||||
crypto.keys['secp256k1'] = mockSecp256k1Module
|
||||
crypto.generateKeyPair('secp256k1', 256, (err, _key) => {
|
||||
if (err) return done(err)
|
||||
key = _key
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
after((done) => {
|
||||
delete crypto.keys['secp256k1']
|
||||
done()
|
||||
})
|
||||
|
||||
it('generates a valid key', (done) => {
|
||||
expect(
|
||||
key
|
||||
).to.exist
|
||||
done()
|
||||
})
|
||||
|
||||
it('protobuf encoding', (done) => {
|
||||
const keyMarshal = crypto.marshalPrivateKey(key)
|
||||
crypto.unmarshalPrivateKey(keyMarshal, (err, key2) => {
|
||||
if (err) return done(err)
|
||||
const keyMarshal2 = crypto.marshalPrivateKey(key2)
|
||||
|
||||
expect(
|
||||
keyMarshal
|
||||
).to.be.eql(
|
||||
keyMarshal2
|
||||
)
|
||||
|
||||
const pk = key.public
|
||||
const pkMarshal = crypto.marshalPublicKey(pk)
|
||||
const pk2 = crypto.unmarshalPublicKey(pkMarshal)
|
||||
const pkMarshal2 = crypto.marshalPublicKey(pk2)
|
||||
|
||||
expect(
|
||||
pkMarshal
|
||||
).to.be.eql(
|
||||
pkMarshal2
|
||||
)
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('without libp2p-crypto-secp256k1 module present', () => {
|
||||
it('fails to generate a secp256k1 key', (done) => {
|
||||
crypto.generateKeyPair('secp256k1', 256, (err, key) => {
|
||||
expect(err).to.exist
|
||||
expect(key).to.not.exist
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('fails to unmarshal a secp256k1 private key', (done) => {
|
||||
crypto.unmarshalPrivateKey(fixtures.pbmPrivateKey, (err, key) => {
|
||||
expect(err).to.exist
|
||||
expect(key).to.not.exist
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('fails to unmarshal a secp256k1 public key', () => {
|
||||
expect(() => {
|
||||
crypto.unmarshalPublicKey(fixtures.pbmPublicKey)
|
||||
}).to.throw(Error)
|
||||
})
|
||||
})
|
@@ -2,8 +2,12 @@
|
||||
/* eslint-env mocha */
|
||||
'use strict'
|
||||
|
||||
const expect = require('chai').expect
|
||||
const util = require('../src/crypto/util')
|
||||
const chai = require('chai')
|
||||
const dirtyChai = require('dirty-chai')
|
||||
const expect = chai.expect
|
||||
chai.use(dirtyChai)
|
||||
|
||||
const util = require('../src/util')
|
||||
const BN = require('bn.js')
|
||||
|
||||
describe('Util', () => {
|
||||
@@ -15,7 +19,13 @@ describe('Util', () => {
|
||||
})
|
||||
|
||||
it('toBase64', (done) => {
|
||||
expect(util.toBase64(bn)).to.be.eql('3q0')
|
||||
expect(util.toBase64(bn)).to.eql('3q0')
|
||||
done()
|
||||
})
|
||||
|
||||
it('toBase64 zero padding', (done) => {
|
||||
let bnpad = new BN('ff', 16)
|
||||
expect(util.toBase64(bnpad, 2)).to.eql('AP8')
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
Reference in New Issue
Block a user