refactor: the whole thing (#102)

This commit is contained in:
David Dias 2017-07-22 10:57:27 -07:00 committed by GitHub
parent c2c6fde394
commit 2f8e234044
43 changed files with 769 additions and 976 deletions

View File

@ -12,14 +12,13 @@ matrix:
# Make sure we have new NPM. # Make sure we have new NPM.
before_install: before_install:
- npm install -g npm - npm install -g npm@4
script: script:
- npm run lint - npm run lint
- npm test - npm test
- npm run coverage - npm run coverage
before_script: before_script:
- export DISPLAY=:99.0 - export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start - sh -e /etc/init.d/xvfb start

132
README.md
View File

@ -12,36 +12,30 @@
![](https://img.shields.io/badge/npm-%3E%3D3.0.0-orange.svg?style=flat-square) ![](https://img.shields.io/badge/npm-%3E%3D3.0.0-orange.svg?style=flat-square)
![](https://img.shields.io/badge/Node.js-%3E%3D4.0.0-orange.svg?style=flat-square) ![](https://img.shields.io/badge/Node.js-%3E%3D4.0.0-orange.svg?style=flat-square)
[![Sauce Test Status](https://saucelabs.com/browser-matrix/ipfs-js-libp2p-crypto.svg)](https://saucelabs.com/u/ipfs-js-
libp2p-crypto)
> Crypto primitives for libp2p in JavaScript > Crypto primitives for libp2p in JavaScript
This repo contains the JavaScript implementation of the crypto primitives 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).
needed for libp2p. This is based on this [go implementation](https://github.com/libp2p/go-libp2p-crypto).
## Table of Contents ## Table of Contents
- [Install](#install) - [Install](#install)
- [Usage](#usage)
- [Example](#example)
- [API](#api) - [API](#api)
- [`hmac`](#hmac) - [`crypto.hmac`](#hmac)
- [`create(hash, secret, callback)`](#createhash-secret-callback) - [`create(hash, secret, callback)`](#createhash-secret-callback)
- [`digest(data, callback)`](#digestdata-callback) - [`digest(data, callback)`](#digestdata-callback)
- [`aes`](#aes) - [`crypto.aes`](#aes)
- [`create(key, iv, callback)`](#createkey-iv-callback) - [`create(key, iv, callback)`](#createkey-iv-callback)
- [`encrypt(data, callback)`](#encryptdata-callback) - [`encrypt(data, callback)`](#encryptdata-callback)
- [`encrypt(data, callback)`](#encryptdata-callback) - [`decrypt(data, callback)`](#decryptdata-callback)
- [`webcrypto`](#webcrypto)
- [`keys`](#keys) - [`keys`](#keys)
- [`generateKeyPair(type, bits, callback)`](#generatekeypairtype-bits-callback) - [`generateKeyPair(type, bits, callback)`](#generatekeypairtype-bits-callback)
- [`generateEphemeralKeyPair(curve, callback)`](#generateephemeralkeypaircurve-callback) - [`generateEphemeralKeyPair(curve, callback)`](#generateephemeralkeypaircurve-callback)
- [`keyStretcher(cipherType, hashType, secret, callback)`](#keystretcherciphertype-hashtype-secret-callback) - [`keyStretcher(cipherType, hashType, secret, callback)`](#keystretcherciphertype-hashtype-secret-callback)
- [`marshalPublicKey(key[, type], callback)`](#marshalpublickeykey-type-callback) - [`marshalPublicKey(key[, type], callback)`](#marshalpublickeykey-type-callback)
- [`unmarshalPublicKey(buf)`](#unmarshalpublickeybuf) - [`unmarshalPublicKey(buf)`](#unmarshalpublickeybuf)
- [`marshalPrivateKey(key[, type])`](#marshalprivatekeykey-type) - [`marshalPrivateKey(key[, type])`](#marshalprivatekeykey-type)
- [`unmarshalPrivateKey(buf, callback)`](#unmarshalprivatekeybuf-callback) - [`unmarshalPrivateKey(buf, callback)`](#unmarshalprivatekeybuf-callback)
- [`webcrypto`](#webcrypto)
- [Contribute](#contribute) - [Contribute](#contribute)
- [License](#license) - [License](#license)
@ -51,24 +45,39 @@ needed for libp2p. This is based on this [go implementation](https://github.com/
npm install --save libp2p-crypto npm install --save libp2p-crypto
``` ```
## Usage
### Example
```js
const crypto = require('libp2p-crypto')
crypto.generateKeyPair('RSA', 2048, (err, key) => {
})
```
## API ## 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. 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` - `hash: String`
- `secret: Buffer` - `secret: Buffer`
@ -79,47 +88,23 @@ Exposes an interface to the Keyed-Hash Message Authentication Code (HMAC) as def
- `data: Buffer` - `data: Buffer`
- `callback: Function` - `callback: Function`
### `aes` Example:
Expoes an interface to AES encryption (formerly Rijndael), as defined in U.S. Federal Information Processing Standards Publication 197.
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. **Supported Key Types**
- `iv: Buffer` Must have length `16`.
- `callback: Function`
##### `encrypt(data, callback)` The [`generateKeyPair`](#generatekeypairtype-bits-callback), [`marshalPublicKey`](#marshalpublickeykey-type-callback), and [`marshalPrivateKey`](#marshalprivatekeykey-type) functions accept a string `type` argument.
- `data: Buffer` 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.
- `callback: Function`
##### `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` ### `crypto.keys.generateKeyPair(type, bits, callback)`
- `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)`
- `type: String`, see [Supported Key Types](#supported-key-types) above. - `type: String`, see [Supported Key Types](#supported-key-types) above.
- `bits: Number` Minimum of 1024 - `bits: Number` Minimum of 1024
@ -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. 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 - `curve: String`, one of `'P-256'`, `'P-384'`, `'P-521'` is currently supported
- `callback: Function` - `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'` - `cipherType: String`, one of `'AES-128'`, `'AES-256'`, `'Blowfish'`
- `hashType: String`, one of `'SHA1'`, `SHA256`, `SHA512` - `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. 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 ```js
{ {
k1: { 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` - `key: keys.rsa.RsaPublicKey | keys.ed25519.Ed25519PublicKey | require('libp2p-crypto-secp256k1').Secp256k1PublicKey`
- `type: String`, see [Supported Key Types](#supported-key-types) above. - `type: String`, see [Supported Key Types](#supported-key-types) above.
Converts a public key object into a protobuf serialized public key. Converts a public key object into a protobuf serialized public key.
### `unmarshalPublicKey(buf)` ### `crypto.keys.unmarshalPublicKey(buf)`
- `buf: Buffer` - `buf: Buffer`
Converts a protobuf serialized public key into its representative object. 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` - `key: keys.rsa.RsaPrivateKey | keys.ed25519.Ed25519PrivateKey | require('libp2p-crypto-secp256k1').Secp256k1PrivateKey`
- `type: String`, see [Supported Key Types](#supported-key-types) above. - `type: String`, see [Supported Key Types](#supported-key-types) above.
Converts a private key object into a protobuf serialized private key. Converts a private key object into a protobuf serialized private key.
### `unmarshalPrivateKey(buf, callback)` ### `crypto.keys.unmarshalPrivateKey(buf, callback)`
- `buf: Buffer` - `buf: Buffer`
- `callback: Function` - `callback: Function`
Converts a protobuf serialized private key into its representative object. Converts a protobuf serialized private key into its representative object.
### `randomBytes(number)` ### `crypto.randomBytes(number)`
- `number: Number` - `number: Number`

View File

@ -10,28 +10,17 @@ const curves = ['P-256', 'P-384', 'P-521']
curves.forEach((curve) => { curves.forEach((curve) => {
suite.add(`ephemeral key with secrect ${curve}`, (d) => { suite.add(`ephemeral key with secrect ${curve}`, (d) => {
crypto.generateEphemeralKeyPair('P-256', (err, res) => { crypto.keys.generateEphemeralKeyPair('P-256', (err, res) => {
if (err) { if (err) { throw err }
throw err
}
res.genSharedKey(res.key, (err, secret) => { res.genSharedKey(res.key, (err, secret) => {
if (err) { if (err) { throw err }
throw err
}
secrets.push(secret) secrets.push(secret)
d.resolve() d.resolve()
}) })
}) })
}, { }, { defer: true })
defer: true
})
}) })
suite suite.on('cycle', (event) => console.log(String(event.target)))
.on('cycle', (event) => { .run({async: true})
console.log(String(event.target))
})
.run({
async: true
})

View File

@ -13,37 +13,26 @@ const ciphers = ['AES-128', 'AES-256', 'Blowfish']
const hashes = ['SHA1', 'SHA256', 'SHA512'] const hashes = ['SHA1', 'SHA256', 'SHA512']
async.waterfall([ async.waterfall([
(cb) => crypto.generateEphemeralKeyPair('P-256', cb), (cb) => crypto.keys.generateEphemeralKeyPair('P-256', cb),
(res, cb) => res.genSharedKey(res.key, cb) (res, cb) => res.genSharedKey(res.key, cb)
], (err, secret) => { ], (err, secret) => {
if (err) { if (err) { throw err }
throw err
}
ciphers.forEach((cipher) => hashes.forEach((hash) => { ciphers.forEach((cipher) => hashes.forEach((hash) => {
setup(cipher, hash, secret) setup(cipher, hash, secret)
})) }))
suite suite.on('cycle', (event) => console.log(String(event.target)))
.on('cycle', (event) => { .run({async: true})
console.log(String(event.target))
})
.run({
async: true
})
}) })
function setup (cipher, hash, secret) { function setup (cipher, hash, secret) {
suite.add(`keyStretcher ${cipher} ${hash}`, (d) => { suite.add(`keyStretcher ${cipher} ${hash}`, (d) => {
crypto.keyStretcher(cipher, hash, secret, (err, k) => { crypto.keys.keyStretcher(cipher, hash, secret, (err, k) => {
if (err) { if (err) { throw err }
throw err
}
keys.push(k) keys.push(k)
d.resolve() d.resolve()
}) })
}, { }, { defer: true })
defer: true
})
} }

View File

@ -10,8 +10,8 @@ const bits = [1024, 2048, 4096]
bits.forEach((bit) => { bits.forEach((bit) => {
suite.add(`generateKeyPair ${bit}bits`, (d) => { suite.add(`generateKeyPair ${bit}bits`, (d) => {
crypto.generateKeyPair('RSA', bit, (err, key) => { crypto.keys.generateKeyPair('RSA', bit, (err, key) => {
if (err) throw err if (err) { throw err }
keys.push(key) keys.push(key)
d.resolve() d.resolve()
}) })
@ -25,17 +25,11 @@ suite.add('sign and verify', (d) => {
const text = key.genSecret() const text = key.genSecret()
key.sign(text, (err, sig) => { key.sign(text, (err, sig) => {
if (err) { if (err) { throw err }
throw err
}
key.public.verify(text, sig, (err, res) => { key.public.verify(text, sig, (err, res) => {
if (err) { if (err) { throw err }
throw err if (res !== true) { throw new Error('failed to verify') }
}
if (res !== true) {
throw new Error('failed to verify')
}
d.resolve() d.resolve()
}) })
}) })
@ -43,10 +37,5 @@ suite.add('sign and verify', (d) => {
defer: true defer: true
}) })
suite suite.on('cycle', (event) => console.log(String(event.target)))
.on('cycle', (event) => { .run({async: true})
console.log(String(event.target))
})
.run({
async: true
})

View File

@ -5,8 +5,10 @@ machine:
dependencies: dependencies:
pre: pre:
- google-chrome --version - google-chrome --version
- wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add - - curl -L -o google-chrome.deb https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
- sudo sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' - sudo dpkg -i google-chrome.deb || true
- sudo apt-get update - 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 - google-chrome --version

View File

@ -4,19 +4,16 @@
"description": "Crypto primitives for libp2p", "description": "Crypto primitives for libp2p",
"main": "src/index.js", "main": "src/index.js",
"browser": { "browser": {
"node-webcrypto-ossl": false, "./src/hmac/index.js": "./src/hmac/index-browser.js",
"./src/crypto/webcrypto.js": "./src/crypto/webcrypto-browser.js", "./src/keys/ecdh.js": "./src/keys/ecdh-browser.js",
"./src/crypto/hmac.js": "./src/crypto/hmac-browser.js", "./src/aes/ciphers.js": "./src/aes/ciphers-browser.js",
"./src/crypto/ecdh.js": "./src/crypto/ecdh-browser.js", "./src/keys/rsa.js": "./src/keys/rsa-browser.js"
"./src/crypto/ciphers.js": "./src/crypto/ciphers-browser.js",
"./src/crypto/rsa.js": "./src/crypto/rsa-browser.js"
}, },
"scripts": { "scripts": {
"lint": "aegir-lint", "lint": "aegir-lint",
"build": "aegir-build", "build": "aegir-build",
"test": "npm run test:node && npm run test:no-webcrypto && npm run test:browser", "test": "aegir-test",
"test:node": "aegir-test --env node", "test:node": "aegir-test --env node",
"test:no-webcrypto": "NO_WEBCRYPTO=true aegir-test --env node",
"test:browser": "aegir-test --env browser", "test:browser": "aegir-test --env browser",
"release": "aegir-release", "release": "aegir-release",
"release-minor": "aegir-release --type minor", "release-minor": "aegir-release --type minor",
@ -30,7 +27,7 @@
"crypto", "crypto",
"rsa" "rsa"
], ],
"author": "Friedel Ziegelmayer <dignifiedqurie@gmail.com>", "author": "Friedel Ziegelmayer <dignifiedquire@gmail.com>",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"asn1.js": "^4.9.1", "asn1.js": "^4.9.1",
@ -38,14 +35,14 @@
"browserify-aes": "^1.0.6", "browserify-aes": "^1.0.6",
"keypair": "^1.0.1", "keypair": "^1.0.1",
"libp2p-crypto-secp256k1": "^0.1.4", "libp2p-crypto-secp256k1": "^0.1.4",
"multihashing-async": "~0.4.5",
"nodeify": "^1.0.1", "nodeify": "^1.0.1",
"pem-jwk": "^1.5.1", "pem-jwk": "^1.5.1",
"protocol-buffers": "^3.2.1", "protocol-buffers": "^3.2.1",
"rsa-pem-to-jwk": "^1.1.3", "rsa-pem-to-jwk": "^1.1.3",
"safe-buffer": "^5.1.1", "safe-buffer": "^5.1.1",
"tweetnacl": "^1.0.0", "tweetnacl": "^1.0.0",
"webcrypto-shim": "github:dignifiedquire/webcrypto-shim#master" "webcrypto-shim": "github:dignifiedquire/webcrypto-shim#master",
"multihashing-async": "~0.4.5"
}, },
"devDependencies": { "devDependencies": {
"aegir": "^11.0.2", "aegir": "^11.0.2",
@ -54,9 +51,6 @@
"dirty-chai": "^2.0.1", "dirty-chai": "^2.0.1",
"pre-commit": "^1.2.2" "pre-commit": "^1.2.2"
}, },
"optionalDependencies": {
"node-webcrypto-ossl": "^1.0.30"
},
"pre-commit": [ "pre-commit": [
"lint", "lint",
"test" "test"
@ -85,4 +79,4 @@
"greenkeeperio-bot <support@greenkeeper.io>", "greenkeeperio-bot <support@greenkeeper.io>",
"nikuda <nikuda@gmail.com>" "nikuda <nikuda@gmail.com>"
] ]
} }

View File

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

View File

@ -1,51 +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)
}
// seed should be a 32 byte uint8array
exports.generateKeyFromSeed = function (seed, callback) {
const done = (err, res) => setImmediate(() => {
callback(err, res)
})
let keys
try {
keys = nacl.sign.keyPair.fromSeed(seed)
} catch (err) {
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))
})
}

View File

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

View File

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

View File

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

View File

@ -3,8 +3,8 @@
const nodeify = require('nodeify') const nodeify = require('nodeify')
const Buffer = require('safe-buffer').Buffer const Buffer = require('safe-buffer').Buffer
const crypto = require('./webcrypto')() const crypto = require('../webcrypto.js')()
const lengths = require('./hmac-lengths') const lengths = require('./lengths')
const hashTypes = { const hashTypes = {
SHA1: 'SHA-1', SHA1: 'SHA-1',
@ -27,11 +27,8 @@ exports.create = function (hashType, secret, callback) {
).then((key) => { ).then((key) => {
return { return {
digest (data, cb) { digest (data, cb) {
nodeify(crypto.subtle.sign( nodeify(crypto.subtle.sign({name: 'HMAC'}, key, data)
{name: 'HMAC'}, .then((raw) => Buffer.from(raw)), cb)
key,
data
).then((raw) => Buffer.from(raw)), cb)
}, },
length: lengths[hashType] length: lengths[hashType]
} }

View File

@ -1,13 +1,13 @@
'use strict' 'use strict'
const crypto = require('crypto') const crypto = require('crypto')
const lengths = require('./lengths')
const lengths = require('./hmac-lengths')
exports.create = function (hash, secret, callback) { exports.create = function (hash, secret, callback) {
const res = { const res = {
digest (data, cb) { digest (data, cb) {
const hmac = genFresh() const hmac = crypto.createHmac(hash.toLowerCase(), secret)
hmac.update(data) hmac.update(data)
setImmediate(() => { setImmediate(() => {
@ -17,8 +17,5 @@ exports.create = function (hash, secret, callback) {
length: lengths[hash] length: lengths[hash]
} }
function genFresh () {
return crypto.createHmac(hash.toLowerCase(), secret)
}
callback(null, res) callback(null, res)
} }

View File

@ -1,114 +1,20 @@
'use strict' 'use strict'
const protobuf = require('protocol-buffers') const hmac = require('./hmac')
const aes = require('./aes')
const keys = require('./keys')
const rsa = require('./keys/rsa')
const pbm = protobuf(require('./crypto.proto')) exports = module.exports
const c = require('./crypto')
exports.protobuf = pbm exports.aes = aes
exports.hmac = hmac
exports.hmac = c.hmac exports.keys = keys
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)
}
// Generates a keypair of the given type and bitsize
// seed is a 32 byte uint8array
exports.generateKeyPairFromSeed = (type, seed, bits, cb) => {
let key = keys[type.toLowerCase()]
if (!key) {
return cb(new Error('invalid or unsupported key type'))
}
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 = 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) => { exports.randomBytes = (number) => {
if (!number || typeof number !== 'number') { if (!number || typeof number !== 'number') {
throw new Error('first argument must be a Number bigger than 0') throw new Error('first argument must be a Number bigger than 0')
} }
return c.rsa.getRandomValues(new Uint8Array(number)) return rsa.getRandomValues(new Uint8Array(number))
} }

View File

@ -1,11 +1,11 @@
'use strict' 'use strict'
const crypto = require('./webcrypto')() const webcrypto = require('../webcrypto.js')()
const nodeify = require('nodeify') const nodeify = require('nodeify')
const BN = require('asn1.js').bignum const BN = require('asn1.js').bignum
const Buffer = require('safe-buffer').Buffer const Buffer = require('safe-buffer').Buffer
const util = require('./util') const util = require('../util')
const toBase64 = util.toBase64 const toBase64 = util.toBase64
const toBn = util.toBn const toBn = util.toBn
@ -16,7 +16,7 @@ const bits = {
} }
exports.generateEphmeralKeyPair = function (curve, callback) { exports.generateEphmeralKeyPair = function (curve, callback) {
nodeify(crypto.subtle.generateKey( nodeify(webcrypto.subtle.generateKey(
{ {
name: 'ECDH', name: 'ECDH',
namedCurve: curve namedCurve: curve
@ -34,7 +34,7 @@ exports.generateEphmeralKeyPair = function (curve, callback) {
let privateKey let privateKey
if (forcePrivate) { if (forcePrivate) {
privateKey = crypto.subtle.importKey( privateKey = webcrypto.subtle.importKey(
'jwk', 'jwk',
unmarshalPrivateKey(curve, forcePrivate), unmarshalPrivateKey(curve, forcePrivate),
{ {
@ -49,7 +49,7 @@ exports.generateEphmeralKeyPair = function (curve, callback) {
} }
const keys = Promise.all([ const keys = Promise.all([
crypto.subtle.importKey( webcrypto.subtle.importKey(
'jwk', 'jwk',
unmarshalPublicKey(curve, theirPub), unmarshalPublicKey(curve, theirPub),
{ {
@ -62,7 +62,7 @@ exports.generateEphmeralKeyPair = function (curve, callback) {
privateKey privateKey
]) ])
nodeify(keys.then((keys) => crypto.subtle.deriveBits( nodeify(keys.then((keys) => webcrypto.subtle.deriveBits(
{ {
name: 'ECDH', name: 'ECDH',
namedCurve: curve, namedCurve: curve,
@ -73,15 +73,13 @@ exports.generateEphmeralKeyPair = function (curve, callback) {
)).then((bits) => Buffer.from(bits)), cb) )).then((bits) => Buffer.from(bits)), cb)
} }
return crypto.subtle.exportKey( return webcrypto.subtle.exportKey('jwk', pair.publicKey)
'jwk', .then((publicKey) => {
pair.publicKey return {
).then((publicKey) => { key: marshalPublicKey(publicKey),
return { genSharedKey
key: marshalPublicKey(publicKey), }
genSharedKey })
}
})
}), callback) }), callback)
} }

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

@ -0,0 +1,164 @@
'use strict'
const multihashing = require('multihashing-async')
const protobuf = require('protocol-buffers')
const Buffer = require('safe-buffer').Buffer
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
}

View File

@ -1,164 +1,47 @@
'use strict' 'use strict'
const multihashing = require('multihashing-async') const nacl = require('tweetnacl')
const protobuf = require('protocol-buffers') const setImmediate = require('async/setImmediate')
const Buffer = require('safe-buffer').Buffer const Buffer = require('safe-buffer').Buffer
const crypto = require('../crypto').ed25519 exports.publicKeyLength = nacl.sign.publicKeyLength
const pbm = protobuf(require('../crypto.proto')) exports.privateKeyLength = nacl.sign.secretKeyLength
class Ed25519PublicKey { exports.generateKey = function (callback) {
constructor (key) { const done = (err, res) => setImmediate(() => {
this._key = ensureKey(key, crypto.publicKeyLength) callback(err, res)
} })
verify (data, sig, callback) { let keys
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 { try {
bytes = ensureKey(bytes, crypto.privateKeyLength + crypto.publicKeyLength) keys = nacl.sign.keyPair()
} catch (err) { } catch (err) {
return callback(err) return done(err)
} }
const privateKeyBytes = bytes.slice(0, crypto.privateKeyLength) done(null, keys)
const publicKeyBytes = bytes.slice(crypto.privateKeyLength, bytes.length)
callback(null, new Ed25519PrivateKey(privateKeyBytes, publicKeyBytes))
} }
function unmarshalEd25519PublicKey (bytes) { // seed should be a 32 byte uint8array
bytes = ensureKey(bytes, crypto.publicKeyLength) exports.generateKeyFromSeed = function (seed, callback) {
return new Ed25519PublicKey(bytes) const done = (err, res) => setImmediate(() => callback(err, res))
let keys
try {
keys = nacl.sign.keyPair.fromSeed(seed)
} catch (err) {
return done(err)
}
done(null, keys)
} }
function generateKeyPair (_bits, cb) { exports.hashAndSign = function (key, msg, callback) {
if (cb === undefined && typeof _bits === 'function') { setImmediate(() => {
cb = _bits callback(null, Buffer.from(nacl.sign.detached(msg, key)))
}
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) { exports.hashAndVerify = function (key, sig, msg, callback) {
if (cb === undefined && typeof _bits === 'function') { setImmediate(() => {
cb = _bits callback(null, nacl.sign.detached.verify(msg, sig, key))
}
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
}

View File

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

View File

@ -1,7 +1,104 @@
'use strict' 'use strict'
module.exports = { const protobuf = require('protocol-buffers')
rsa: require('./rsa'), const pbm = protobuf(require('./keys.proto'))
ed25519: require('./ed25519'),
secp256k1: require('libp2p-crypto-secp256k1') const keys = exports.keys = require('./keys')
exports = module.exports
exports.pbm = pbm
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)
}
// Generates a keypair of the given type and bitsize
// seed is a 32 byte uint8array
exports.generateKeyPairFromSeed = (type, seed, bits, cb) => {
let key = keys[type.toLowerCase()]
if (!key) {
return cb(new Error('invalid or unsupported key type'))
}
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 = 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
} }

View File

@ -1,8 +1,8 @@
'use strict' 'use strict'
const crypto = require('./crypto')
const whilst = require('async/whilst') const whilst = require('async/whilst')
const Buffer = require('safe-buffer').Buffer const Buffer = require('safe-buffer').Buffer
const hmac = require('../hmac')
const cipherMap = { const cipherMap = {
'AES-128': { 'AES-128': {
@ -38,7 +38,7 @@ module.exports = (cipherType, hash, secret, callback) => {
const seed = Buffer.from('key expansion') const seed = Buffer.from('key expansion')
const resultLength = 2 * (ivSize + cipherKeySize + hmacKeySize) const resultLength = 2 * (ivSize + cipherKeySize + hmacKeySize)
crypto.hmac.create(hash, secret, (err, m) => { hmac.create(hash, secret, (err, m) => {
if (err) { if (err) {
return callback(err) return callback(err)
} }

7
src/keys/keys.js Normal file
View File

@ -0,0 +1,7 @@
'use strict'
module.exports = {
rsa: require('./rsa-class'),
ed25519: require('./ed25519-class'),
secp256k1: require('libp2p-crypto-secp256k1')
}

View File

@ -3,12 +3,12 @@
const nodeify = require('nodeify') const nodeify = require('nodeify')
const Buffer = require('safe-buffer').Buffer const Buffer = require('safe-buffer').Buffer
const crypto = require('./webcrypto')() const webcrypto = require('../webcrypto.js')()
exports.utils = require('./rsa-utils') exports.utils = require('./rsa-utils')
exports.generateKey = function (bits, callback) { exports.generateKey = function (bits, callback) {
nodeify(crypto.subtle.generateKey( nodeify(webcrypto.subtle.generateKey(
{ {
name: 'RSASSA-PKCS1-v1_5', name: 'RSASSA-PKCS1-v1_5',
modulusLength: bits, modulusLength: bits,
@ -27,7 +27,7 @@ exports.generateKey = function (bits, callback) {
// Takes a jwk key // Takes a jwk key
exports.unmarshalPrivateKey = function (key, callback) { exports.unmarshalPrivateKey = function (key, callback) {
const privateKey = crypto.subtle.importKey( const privateKey = webcrypto.subtle.importKey(
'jwk', 'jwk',
key, key,
{ {
@ -51,11 +51,11 @@ exports.unmarshalPrivateKey = function (key, callback) {
} }
exports.getRandomValues = function (arr) { exports.getRandomValues = function (arr) {
return Buffer.from(crypto.getRandomValues(arr)) return Buffer.from(webcrypto.getRandomValues(arr))
} }
exports.hashAndSign = function (key, msg, callback) { exports.hashAndSign = function (key, msg, callback) {
nodeify(crypto.subtle.importKey( nodeify(webcrypto.subtle.importKey(
'jwk', 'jwk',
key, key,
{ {
@ -65,7 +65,7 @@ exports.hashAndSign = function (key, msg, callback) {
false, false,
['sign'] ['sign']
).then((privateKey) => { ).then((privateKey) => {
return crypto.subtle.sign( return webcrypto.subtle.sign(
{name: 'RSASSA-PKCS1-v1_5'}, {name: 'RSASSA-PKCS1-v1_5'},
privateKey, privateKey,
Uint8Array.from(msg) Uint8Array.from(msg)
@ -74,7 +74,7 @@ exports.hashAndSign = function (key, msg, callback) {
} }
exports.hashAndVerify = function (key, sig, msg, callback) { exports.hashAndVerify = function (key, sig, msg, callback) {
nodeify(crypto.subtle.importKey( nodeify(webcrypto.subtle.importKey(
'jwk', 'jwk',
key, key,
{ {
@ -84,7 +84,7 @@ exports.hashAndVerify = function (key, sig, msg, callback) {
false, false,
['verify'] ['verify']
).then((publicKey) => { ).then((publicKey) => {
return crypto.subtle.verify( return webcrypto.subtle.verify(
{name: 'RSASSA-PKCS1-v1_5'}, {name: 'RSASSA-PKCS1-v1_5'},
publicKey, publicKey,
sig, sig,
@ -95,13 +95,13 @@ exports.hashAndVerify = function (key, sig, msg, callback) {
function exportKey (pair) { function exportKey (pair) {
return Promise.all([ return Promise.all([
crypto.subtle.exportKey('jwk', pair.privateKey), webcrypto.subtle.exportKey('jwk', pair.privateKey),
crypto.subtle.exportKey('jwk', pair.publicKey) webcrypto.subtle.exportKey('jwk', pair.publicKey)
]) ])
} }
function derivePublicFromPrivate (jwKey) { function derivePublicFromPrivate (jwKey) {
return crypto.subtle.importKey( return webcrypto.subtle.importKey(
'jwk', 'jwk',
{ {
kty: jwKey.kty, kty: jwKey.kty,

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

@ -0,0 +1,133 @@
'use strict'
const multihashing = require('multihashing-async')
const protobuf = require('protocol-buffers')
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
}

View File

@ -2,7 +2,7 @@
const asn1 = require('asn1.js') const asn1 = require('asn1.js')
const util = require('./util') const util = require('./../util')
const toBase64 = util.toBase64 const toBase64 = util.toBase64
const toBn = util.toBn const toBn = util.toBn

View File

@ -1,133 +1,56 @@
'use strict' 'use strict'
const multihashing = require('multihashing-async') const crypto = require('crypto')
const protobuf = require('protocol-buffers') 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 exports.utils = require('./rsa-utils')
const pbm = protobuf(require('../crypto.proto'))
class RsaPublicKey { exports.generateKey = function (bits, callback) {
constructor (key) { const done = (err, res) => setImmediate(() => callback(err, res))
this._key = key
let key
try {
key = keypair({ bits: bits })
} catch (err) {
return done(err)
} }
verify (data, sig, callback) { done(null, {
ensure(callback) privateKey: pemToJwk(key.private),
crypto.hashAndVerify(this._key, sig, data, callback) publicKey: pemToJwk(key.public)
}
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) { // Takes a jwk key
const jwk = crypto.utils.pkixToJwk(bytes) exports.unmarshalPrivateKey = function (key, callback) {
callback(null, {
return new RsaPublicKey(jwk) privateKey: key,
} publicKey: {
kty: key.kty,
function generateKeyPair (bits, cb) { n: key.n,
crypto.generateKey(bits, (err, keys) => { e: key.e
if (err) {
return cb(err)
} }
cb(null, new RsaPrivateKey(keys.privateKey, keys.publicKey))
}) })
} }
function ensure (cb) { exports.getRandomValues = function (arr) {
if (typeof cb !== 'function') { return crypto.randomBytes(arr.length)
throw new Error('callback is required')
}
} }
module.exports = { exports.hashAndSign = function (key, msg, callback) {
RsaPublicKey, const sign = crypto.createSign('RSA-SHA256')
RsaPrivateKey,
unmarshalRsaPublicKey, sign.update(msg)
unmarshalRsaPrivateKey, setImmediate(() => callback(null, sign.sign(jwkToPem(key))))
generateKeyPair }
exports.hashAndVerify = function (key, sig, msg, callback) {
const verify = crypto.createVerify('RSA-SHA256')
verify.update(msg)
setImmediate(() => callback(null, verify.verify(jwkToPem(key), sig)))
} }

16
src/webcrypto.js Normal file
View 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')
}

View File

@ -9,9 +9,9 @@ chai.use(dirtyChai)
const series = require('async/series') const series = require('async/series')
const Buffer = require('safe-buffer').Buffer const Buffer = require('safe-buffer').Buffer
const crypto = require('../src') const crypto = require('../../src')
const fixtures = require('./fixtures/aes') const fixtures = require('./../fixtures/aes')
const goFixtures = require('./fixtures/go-aes') const goFixtures = require('./../fixtures/go-aes')
const bytes = { const bytes = {
16: 'AES-128', 16: 'AES-128',
@ -40,6 +40,7 @@ describe('AES-CTR', () => {
}) })
}) })
}) })
Object.keys(bytes).forEach((byte) => { Object.keys(bytes).forEach((byte) => {
it(`${bytes[byte]} - fixed - encrypt and decrypt`, (done) => { it(`${bytes[byte]} - fixed - encrypt and decrypt`, (done) => {
const key = Buffer.alloc(parseInt(byte, 10)) const key = Buffer.alloc(parseInt(byte, 10))
@ -57,10 +58,10 @@ describe('AES-CTR', () => {
cipher.encrypt(input, (err, res) => { 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.have.length(output.length)
expect(res).to.be.eql(output) expect(res).to.eql(output)
cipher.decrypt(res, (err, res) => { cipher.decrypt(res, (err, res) => {
expect(err).to.not.exist() expect(err).to.not.exist()
expect(res).to.be.eql(input) expect(res).to.eql(input)
cb() cb()
}) })
}) })

View File

@ -12,7 +12,7 @@ const fixtures = require('./fixtures/go-key-rsa')
describe('libp2p-crypto', () => { describe('libp2p-crypto', () => {
let key let key
before((done) => { before((done) => {
crypto.generateKeyPair('RSA', 2048, (err, _key) => { crypto.keys.generateKeyPair('RSA', 2048, (err, _key) => {
if (err) { if (err) {
return done(err) return done(err)
} }
@ -22,21 +22,22 @@ describe('libp2p-crypto', () => {
}) })
it('marshalPublicKey and unmarshalPublicKey', () => { 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(key2.equals(key.public)).to.be.eql(true)
expect(() => { expect(() => {
crypto.marshalPublicKey(key.public, 'invalid-key-type') crypto.keys.marshalPublicKey(key.public, 'invalid-key-type')
}).to.throw() }).to.throw()
}) })
it('marshalPrivateKey and unmarshalPrivateKey', (done) => { it('marshalPrivateKey and unmarshalPrivateKey', (done) => {
expect(() => { expect(() => {
crypto.marshalPrivateKey(key, 'invalid-key-type') crypto.keys.marshalPrivateKey(key, 'invalid-key-type')
}).to.throw() }).to.throw()
crypto.unmarshalPrivateKey(crypto.marshalPrivateKey(key), (err, key2) => { crypto.keys.unmarshalPrivateKey(crypto.keys.marshalPrivateKey(key), (err, key2) => {
if (err) { if (err) {
return done(err) return done(err)
} }
@ -52,60 +53,56 @@ describe('libp2p-crypto', () => {
// or a bug // or a bug
describe('go interop', () => { describe('go interop', () => {
it('unmarshals private key', (done) => { it('unmarshals private key', (done) => {
crypto.unmarshalPrivateKey(fixtures.private.key, (err, key) => { crypto.keys.unmarshalPrivateKey(fixtures.private.key, (err, key) => {
if (err) { if (err) {
return done(err) return done(err)
} }
const hash = fixtures.private.hash 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) => { key.hash((err, digest) => {
if (err) { if (err) {
return done(err) return done(err)
} }
expect(digest).to.be.eql(hash) expect(digest).to.eql(hash)
done() done()
}) })
}) })
}) })
it('unmarshals public key', (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 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) => { key.hash((err, digest) => {
if (err) { if (err) {
return done(err) return done(err)
} }
expect(digest).to.be.eql(hash) expect(digest).to.eql(hash)
done() done()
}) })
}) })
it('unmarshal -> marshal, private key', (done) => { it('unmarshal -> marshal, private key', (done) => {
crypto.unmarshalPrivateKey(fixtures.private.key, (err, key) => { crypto.keys.unmarshalPrivateKey(fixtures.private.key, (err, key) => {
if (err) { if (err) {
return done(err) return done(err)
} }
const marshalled = crypto.marshalPrivateKey(key) const marshalled = crypto.keys.marshalPrivateKey(key)
expect(marshalled).to.be.eql(fixtures.private.key) expect(marshalled).to.eql(fixtures.private.key)
done() done()
}) })
}) })
it('unmarshal -> marshal, public key', () => { it('unmarshal -> marshal, public key', () => {
const key = crypto.unmarshalPublicKey(fixtures.public.key) const key = crypto.keys.unmarshalPublicKey(fixtures.public.key)
const marshalled = crypto.marshalPublicKey(key) const marshalled = crypto.keys.marshalPublicKey(key)
expect( expect(fixtures.public.key.equals(marshalled)).to.eql(true)
fixtures.public.key.equals(marshalled)
).to.be.eql(
true
)
}) })
}) })

View File

@ -8,7 +8,7 @@ const dirtyChai = require('dirty-chai')
const expect = chai.expect const expect = chai.expect
chai.use(dirtyChai) chai.use(dirtyChai)
const crypto = require('../src') const crypto = require('../../src')
const hashes = ['SHA1', 'SHA256', 'SHA512'] const hashes = ['SHA1', 'SHA256', 'SHA512']

View File

@ -7,14 +7,14 @@ const expect = chai.expect
chai.use(dirtyChai) chai.use(dirtyChai)
const Buffer = require('safe-buffer').Buffer const Buffer = require('safe-buffer').Buffer
const crypto = require('../src') const crypto = require('../../src')
const ed25519 = crypto.keys.ed25519 const ed25519 = crypto.keys.keys.ed25519
const fixtures = require('./fixtures/go-key-ed25519') const fixtures = require('../fixtures/go-key-ed25519')
describe('ed25519', () => { describe('ed25519', () => {
let key let key
before((done) => { before((done) => {
crypto.generateKeyPair('Ed25519', 512, (err, _key) => { crypto.keys.generateKeyPair('Ed25519', 512, (err, _key) => {
if (err) return done(err) if (err) return done(err)
key = _key key = _key
done() done()
@ -22,11 +22,7 @@ describe('ed25519', () => {
}) })
it('generates a valid key', (done) => { it('generates a valid key', (done) => {
expect( expect(key).to.be.an.instanceof(ed25519.Ed25519PrivateKey)
key
).to.be.an.instanceof(
ed25519.Ed25519PrivateKey
)
key.hash((err, digest) => { key.hash((err, digest) => {
if (err) { if (err) {
@ -40,13 +36,9 @@ describe('ed25519', () => {
it('generates a valid key from seed', (done) => { it('generates a valid key from seed', (done) => {
var seed = crypto.randomBytes(32) var seed = crypto.randomBytes(32)
crypto.generateKeyPairFromSeed('Ed25519', seed, 512, (err, seededkey) => { crypto.keys.generateKeyPairFromSeed('Ed25519', seed, 512, (err, seededkey) => {
if (err) return done(err) if (err) return done(err)
expect( expect(seededkey).to.be.an.instanceof(ed25519.Ed25519PrivateKey)
seededkey
).to.be.an.instanceof(
ed25519.Ed25519PrivateKey
)
seededkey.hash((err, digest) => { seededkey.hash((err, digest) => {
if (err) { if (err) {
@ -61,42 +53,29 @@ describe('ed25519', () => {
it('generates the same key from the same seed', (done) => { it('generates the same key from the same seed', (done) => {
var seed = crypto.randomBytes(32) var seed = crypto.randomBytes(32)
crypto.generateKeyPairFromSeed('Ed25519', seed, 512, (err, seededkey1) => { crypto.keys.generateKeyPairFromSeed('Ed25519', seed, 512, (err, seededkey1) => {
if (err) return done(err) if (err) return done(err)
crypto.generateKeyPairFromSeed('Ed25519', seed, 512, (err, seededkey2) => { crypto.keys.generateKeyPairFromSeed('Ed25519', seed, 512, (err, seededkey2) => {
if (err) return done(err) if (err) return done(err)
expect( expect(seededkey1.equals(seededkey2)).to.eql(true)
seededkey1.equals(seededkey2) expect(seededkey1.public.equals(seededkey2.public)).to.eql(true)
).to.be.eql(
true
)
expect(
seededkey1.public.equals(seededkey2.public)
).to.be.eql(
true
)
done() done()
}) })
}) })
}) })
it('generates different keys for different seeds', (done) => { it('generates different keys for different seeds', (done) => {
var seed1 = crypto.randomBytes(32) const seed1 = crypto.randomBytes(32)
crypto.generateKeyPairFromSeed('Ed25519', seed1, 512, (err, seededkey1) => { crypto.keys.generateKeyPairFromSeed('Ed25519', seed1, 512, (err, seededkey1) => {
if (err) return done(err) expect(err).to.not.exist()
var seed2 = crypto.randomBytes(32)
crypto.generateKeyPairFromSeed('Ed25519', seed2, 512, (err, seededkey2) => { const seed2 = crypto.randomBytes(32)
if (err) return done(err) crypto.keys.generateKeyPairFromSeed('Ed25519', seed2, 512, (err, seededkey2) => {
expect( expect(err).to.not.exist()
seededkey1.equals(seededkey2)
).to.be.eql( expect(seededkey1.equals(seededkey2)).to.eql(false)
false expect(seededkey1.public.equals(seededkey2.public))
) .to.eql(false)
expect(
seededkey1.public.equals(seededkey2.public)
).to.be.eql(
false
)
done() done()
}) })
}) })
@ -106,15 +85,10 @@ describe('ed25519', () => {
const text = crypto.randomBytes(512) const text = crypto.randomBytes(512)
key.sign(text, (err, sig) => { key.sign(text, (err, sig) => {
if (err) { expect(err).to.not.exist()
return done(err)
}
key.public.verify(text, sig, (err, res) => { key.public.verify(text, sig, (err, res) => {
if (err) { expect(err).to.not.exist()
return done(err)
}
expect(res).to.be.eql(true) expect(res).to.be.eql(true)
done() done()
}) })
@ -129,22 +103,14 @@ describe('ed25519', () => {
} }
const keyMarshal2 = key2.marshal() const keyMarshal2 = key2.marshal()
expect( expect(keyMarshal).to.eql(keyMarshal2)
keyMarshal
).to.be.eql(
keyMarshal2
)
const pk = key.public const pk = key.public
const pkMarshal = pk.marshal() const pkMarshal = pk.marshal()
const pk2 = ed25519.unmarshalEd25519PublicKey(pkMarshal) const pk2 = ed25519.unmarshalEd25519PublicKey(pkMarshal)
const pkMarshal2 = pk2.marshal() const pkMarshal2 = pk2.marshal()
expect( expect(pkMarshal).to.eql(pkMarshal2)
pkMarshal
).to.be.eql(
pkMarshal2
)
done() done()
}) })
}) })
@ -153,44 +119,25 @@ describe('ed25519', () => {
it('equals itself', () => { it('equals itself', () => {
expect( expect(
key.equals(key) key.equals(key)
).to.be.eql( ).to.eql(
true true
) )
expect( expect(
key.public.equals(key.public) key.public.equals(key.public)
).to.be.eql( ).to.eql(
true true
) )
}) })
it('not equals other key', (done) => { it('not equals other key', (done) => {
crypto.generateKeyPair('Ed25519', 512, (err, key2) => { crypto.keys.generateKeyPair('Ed25519', 512, (err, key2) => {
if (err) return done(err) if (err) return done(err)
expect( expect(key.equals(key2)).to.eql(false)
key.equals(key2) expect(key2.equals(key)).to.eql(false)
).to.be.eql( expect(key.public.equals(key2.public)).to.eql(false)
false expect(key2.public.equals(key.public)).to.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() done()
}) })
}) })
@ -207,7 +154,7 @@ describe('ed25519', () => {
if (err) { if (err) {
return done(err) return done(err)
} }
expect(valid).to.be.eql(true) expect(valid).to.eql(true)
done() done()
}) })
}) })
@ -232,8 +179,9 @@ describe('ed25519', () => {
describe('go interop', () => { describe('go interop', () => {
let privateKey let privateKey
before((done) => { before((done) => {
crypto.unmarshalPrivateKey(fixtures.verify.privateKey, (err, key) => { crypto.keys.unmarshalPrivateKey(fixtures.verify.privateKey, (err, key) => {
expect(err).to.not.exist() expect(err).to.not.exist()
privateKey = key privateKey = key
done() done()
@ -241,12 +189,11 @@ describe('ed25519', () => {
}) })
it('verifies with data from go', (done) => { 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) => { key.verify(fixtures.verify.data, fixtures.verify.signature, (err, ok) => {
if (err) throw err
expect(err).to.not.exist() expect(err).to.not.exist()
expect(ok).to.be.eql(true) expect(ok).to.eql(true)
done() done()
}) })
}) })
@ -254,7 +201,7 @@ describe('ed25519', () => {
it('generates the same signature as go', (done) => { it('generates the same signature as go', (done) => {
privateKey.sign(fixtures.verify.data, (err, sig) => { privateKey.sign(fixtures.verify.data, (err, sig) => {
expect(err).to.not.exist() expect(err).to.not.exist()
expect(sig).to.deep.equal(fixtures.verify.signature) expect(sig).to.eql(fixtures.verify.signature)
done() done()
}) })
}) })

View File

@ -8,8 +8,8 @@ const expect = chai.expect
chai.use(dirtyChai) chai.use(dirtyChai)
const parallel = require('async/parallel') const parallel = require('async/parallel')
const fixtures = require('./fixtures/go-elliptic-key') const fixtures = require('../fixtures/go-elliptic-key')
const crypto = require('../src') const crypto = require('../../src')
const curves = ['P-256', 'P-384'] // 'P-521' fails in tests :( no clue why const curves = ['P-256', 'P-384'] // 'P-521' fails in tests :( no clue why
const lengths = { const lengths = {
@ -27,8 +27,8 @@ describe('generateEphemeralKeyPair', () => {
curves.forEach((curve) => { curves.forEach((curve) => {
it(`generate and shared key ${curve}`, (done) => { it(`generate and shared key ${curve}`, (done) => {
parallel([ parallel([
(cb) => crypto.generateEphemeralKeyPair(curve, cb), (cb) => crypto.keys.generateEphemeralKeyPair(curve, cb),
(cb) => crypto.generateEphemeralKeyPair(curve, cb) (cb) => crypto.keys.generateEphemeralKeyPair(curve, cb)
], (err, keys) => { ], (err, keys) => {
expect(err).to.not.exist() expect(err).to.not.exist()
expect(keys[0].key).to.have.length(lengths[curve]) expect(keys[0].key).to.have.length(lengths[curve])
@ -48,8 +48,8 @@ describe('generateEphemeralKeyPair', () => {
const curve = fixtures.curve const curve = fixtures.curve
parallel([ parallel([
(cb) => crypto.generateEphemeralKeyPair(curve, cb), (cb) => crypto.keys.generateEphemeralKeyPair(curve, cb),
(cb) => crypto.generateEphemeralKeyPair(curve, cb) (cb) => crypto.keys.generateEphemeralKeyPair(curve, cb)
], (err, res) => { ], (err, res) => {
expect(err).to.not.exist() expect(err).to.not.exist()
const alice = res[0] const alice = res[0]
@ -62,12 +62,7 @@ describe('generateEphemeralKeyPair', () => {
], (err, secrets) => { ], (err, secrets) => {
expect(err).to.not.exist() expect(err).to.not.exist()
expect( expect(secrets[0]).to.eql(secrets[1])
secrets[0]
).to.be.eql(
secrets[1]
)
expect(secrets[0]).to.have.length(32) expect(secrets[0]).to.have.length(32)
done() done()

View File

@ -6,8 +6,8 @@ const chai = require('chai')
const dirtyChai = require('dirty-chai') const dirtyChai = require('dirty-chai')
const expect = chai.expect const expect = chai.expect
chai.use(dirtyChai) chai.use(dirtyChai)
const crypto = require('../src') const crypto = require('../../src')
const fixtures = require('./fixtures/go-stretch-key') const fixtures = require('../fixtures/go-stretch-key')
describe('keyStretcher', () => { describe('keyStretcher', () => {
describe('generate', () => { describe('generate', () => {
@ -17,7 +17,7 @@ describe('keyStretcher', () => {
let secret let secret
before((done) => { before((done) => {
crypto.generateEphemeralKeyPair('P-256', (err, _res) => { crypto.keys.generateEphemeralKeyPair('P-256', (err, _res) => {
if (err) { if (err) {
return done(err) return done(err)
} }
@ -36,7 +36,7 @@ describe('keyStretcher', () => {
ciphers.forEach((cipher) => { ciphers.forEach((cipher) => {
hashes.forEach((hash) => { hashes.forEach((hash) => {
it(`${cipher} - ${hash}`, (done) => { it(`${cipher} - ${hash}`, (done) => {
crypto.keyStretcher(cipher, hash, secret, (err, keys) => { crypto.keys.keyStretcher(cipher, hash, secret, (err, keys) => {
if (err) { if (err) {
return done(err) return done(err)
} }
@ -56,7 +56,7 @@ describe('keyStretcher', () => {
const cipher = test.cipher const cipher = test.cipher
const hash = test.hash const hash = test.hash
const secret = test.secret const secret = test.secret
crypto.keyStretcher(cipher, hash, secret, (err, keys) => { crypto.keys.keyStretcher(cipher, hash, secret, (err, keys) => {
if (err) { if (err) {
return done(err) return done(err)
} }

View File

@ -7,26 +7,25 @@ const expect = chai.expect
chai.use(dirtyChai) chai.use(dirtyChai)
const Buffer = require('safe-buffer').Buffer const Buffer = require('safe-buffer').Buffer
const crypto = require('../src') const crypto = require('../../src')
const rsa = crypto.keys.rsa const rsa = crypto.keys.keys.rsa
const fixtures = require('./fixtures/go-key-rsa') const fixtures = require('../fixtures/go-key-rsa')
describe('RSA', () => { describe('RSA', () => {
let key let key
before((done) => { before((done) => {
crypto.generateKeyPair('RSA', 2048, (err, _key) => { crypto.keys.generateKeyPair('RSA', 2048, (err, _key) => {
if (err) return done(err) if (err) {
return done(err)
}
key = _key key = _key
done() done()
}) })
}) })
it('generates a valid key', (done) => { it('generates a valid key', (done) => {
expect( expect(key).to.be.an.instanceof(rsa.RsaPrivateKey)
key
).to.be.an.instanceof(
rsa.RsaPrivateKey
)
key.hash((err, digest) => { key.hash((err, digest) => {
if (err) { if (err) {
@ -65,68 +64,35 @@ describe('RSA', () => {
} }
const keyMarshal2 = key2.marshal() const keyMarshal2 = key2.marshal()
expect( expect(keyMarshal).to.eql(keyMarshal2)
keyMarshal
).to.be.eql(
keyMarshal2
)
const pk = key.public const pk = key.public
const pkMarshal = pk.marshal() const pkMarshal = pk.marshal()
const pk2 = rsa.unmarshalRsaPublicKey(pkMarshal) const pk2 = rsa.unmarshalRsaPublicKey(pkMarshal)
const pkMarshal2 = pk2.marshal() const pkMarshal2 = pk2.marshal()
expect( expect(pkMarshal).to.eql(pkMarshal2)
pkMarshal
).to.be.eql(
pkMarshal2
)
done() done()
}) })
}) })
describe('key equals', () => { describe('key equals', () => {
it('equals itself', () => { it('equals itself', () => {
expect( expect(key.equals(key)).to.eql(true)
key.equals(key)
).to.be.eql(
true
)
expect( expect(key.public.equals(key.public)).to.eql(true)
key.public.equals(key.public)
).to.be.eql(
true
)
}) })
it('not equals other key', (done) => { it('not equals other key', (done) => {
crypto.generateKeyPair('RSA', 2048, (err, key2) => { crypto.keys.generateKeyPair('RSA', 2048, (err, key2) => {
if (err) return done(err) if (err) {
return done(err)
}
expect( expect(key.equals(key2)).to.eql(false)
key.equals(key2) expect(key2.equals(key)).to.eql(false)
).to.be.eql( expect(key.public.equals(key2.public)).to.eql(false)
false expect(key2.public.equals(key.public)).to.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() done()
}) })
}) })
@ -168,7 +134,7 @@ describe('RSA', () => {
describe('go interop', () => { describe('go interop', () => {
it('verifies with data from go', (done) => { 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) => { key.verify(fixtures.verify.data, fixtures.verify.signature, (err, ok) => {
if (err) throw err if (err) throw err

View File

@ -5,8 +5,8 @@ const chai = require('chai')
const dirtyChai = require('dirty-chai') const dirtyChai = require('dirty-chai')
const expect = chai.expect const expect = chai.expect
chai.use(dirtyChai) chai.use(dirtyChai)
const fixtures = require('./fixtures/secp256k1') const fixtures = require('../fixtures/secp256k1')
const crypto = require('../src') const crypto = require('../../src')
const mockPublicKey = { const mockPublicKey = {
bytes: fixtures.pbmPublicKey bytes: fixtures.pbmPublicKey
@ -31,12 +31,38 @@ const mockSecp256k1Module = {
} }
} }
describe('without libp2p-crypto-secp256k1 module present', () => {
crypto.keys.keys['secp256k1'] = undefined
it('fails to generate a secp256k1 key', (done) => {
crypto.keys.generateKeyPair('secp256k1', 256, (err, key) => {
expect(err).to.exist()
expect(key).to.not.exist()
done()
})
})
it('fails to unmarshal a secp256k1 private key', (done) => {
crypto.keys.unmarshalPrivateKey(fixtures.pbmPrivateKey, (err, key) => {
expect(err).to.exist()
expect(key).to.not.exist()
done()
})
})
it('fails to unmarshal a secp256k1 public key', () => {
expect(() => {
crypto.keys.unmarshalPublicKey(fixtures.pbmPublicKey)
}).to.throw(Error)
})
})
describe('with libp2p-crypto-secp256k1 module present', () => { describe('with libp2p-crypto-secp256k1 module present', () => {
let key let key
before((done) => { before((done) => {
crypto.keys['secp256k1'] = mockSecp256k1Module crypto.keys.keys['secp256k1'] = mockSecp256k1Module
crypto.generateKeyPair('secp256k1', 256, (err, _key) => { crypto.keys.generateKeyPair('secp256k1', 256, (err, _key) => {
if (err) return done(err) if (err) return done(err)
key = _key key = _key
done() done()
@ -49,59 +75,25 @@ describe('with libp2p-crypto-secp256k1 module present', () => {
}) })
it('generates a valid key', (done) => { it('generates a valid key', (done) => {
expect( expect(key).to.exist()
key
).to.exist()
done() done()
}) })
it('protobuf encoding', (done) => { it('protobuf encoding', (done) => {
const keyMarshal = crypto.marshalPrivateKey(key) const keyMarshal = crypto.keys.marshalPrivateKey(key)
crypto.unmarshalPrivateKey(keyMarshal, (err, key2) => { crypto.keys.unmarshalPrivateKey(keyMarshal, (err, key2) => {
if (err) return done(err) if (err) return done(err)
const keyMarshal2 = crypto.marshalPrivateKey(key2) const keyMarshal2 = crypto.keys.marshalPrivateKey(key2)
expect( expect(keyMarshal).to.eql(keyMarshal2)
keyMarshal
).to.be.eql(
keyMarshal2
)
const pk = key.public const pk = key.public
const pkMarshal = crypto.marshalPublicKey(pk) const pkMarshal = crypto.keys.marshalPublicKey(pk)
const pk2 = crypto.unmarshalPublicKey(pkMarshal) const pk2 = crypto.keys.unmarshalPublicKey(pkMarshal)
const pkMarshal2 = crypto.marshalPublicKey(pk2) const pkMarshal2 = crypto.keys.marshalPublicKey(pk2)
expect( expect(pkMarshal).to.eql(pkMarshal2)
pkMarshal
).to.be.eql(
pkMarshal2
)
done() 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)
})
})

View File

@ -7,7 +7,7 @@ const dirtyChai = require('dirty-chai')
const expect = chai.expect const expect = chai.expect
chai.use(dirtyChai) chai.use(dirtyChai)
const util = require('../src/crypto/util') const util = require('../src/util')
const BN = require('bn.js') const BN = require('bn.js')
describe('Util', () => { describe('Util', () => {
@ -19,13 +19,13 @@ describe('Util', () => {
}) })
it('toBase64', (done) => { it('toBase64', (done) => {
expect(util.toBase64(bn)).to.be.eql('3q0') expect(util.toBase64(bn)).to.eql('3q0')
done() done()
}) })
it('toBase64 zero padding', (done) => { it('toBase64 zero padding', (done) => {
let bnpad = new BN('ff', 16) let bnpad = new BN('ff', 16)
expect(util.toBase64(bnpad, 2)).to.be.eql('AP8') expect(util.toBase64(bnpad, 2)).to.eql('AP8')
done() done()
}) })
}) })