diff --git a/.travis.yml b/.travis.yml index c1f4742..088e927 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,14 +12,13 @@ matrix: # Make sure we have new NPM. before_install: - - npm install -g npm + - npm install -g npm@4 script: - npm run lint - npm test - npm run coverage - before_script: - export DISPLAY=:99.0 - sh -e /etc/init.d/xvfb start diff --git a/README.md b/README.md index 362acb2..0943bd3 100644 --- a/README.md +++ b/README.md @@ -12,36 +12,30 @@ ![](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) -[![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 -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` diff --git a/benchmarks/ephemeral-keys.js b/benchmarks/ephemeral-keys.js index ade7677..9e40402 100644 --- a/benchmarks/ephemeral-keys.js +++ b/benchmarks/ephemeral-keys.js @@ -10,28 +10,17 @@ 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 - }) +suite.on('cycle', (event) => console.log(String(event.target))) + .run({async: true}) diff --git a/benchmarks/key-stretcher.js b/benchmarks/key-stretcher.js index 5b7f2a5..8cf981b 100644 --- a/benchmarks/key-stretcher.js +++ b/benchmarks/key-stretcher.js @@ -13,37 +13,26 @@ 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 - }) + suite.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 }) } diff --git a/benchmarks/rsa.js b/benchmarks/rsa.js index 8ad39e1..e7cf9fa 100644 --- a/benchmarks/rsa.js +++ b/benchmarks/rsa.js @@ -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() }) }) @@ -43,10 +37,5 @@ suite.add('sign and verify', (d) => { defer: true }) -suite - .on('cycle', (event) => { - console.log(String(event.target)) - }) - .run({ - async: true - }) +suite.on('cycle', (event) => console.log(String(event.target))) + .run({async: true}) diff --git a/circle.yml b/circle.yml index 434211a..56f7efb 100644 --- a/circle.yml +++ b/circle.yml @@ -5,8 +5,10 @@ machine: 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 diff --git a/package.json b/package.json index 40d141c..d19a07a 100644 --- a/package.json +++ b/package.json @@ -4,19 +4,16 @@ "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": "aegir-test", "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", @@ -30,7 +27,7 @@ "crypto", "rsa" ], - "author": "Friedel Ziegelmayer ", + "author": "Friedel Ziegelmayer ", "license": "MIT", "dependencies": { "asn1.js": "^4.9.1", @@ -38,14 +35,14 @@ "browserify-aes": "^1.0.6", "keypair": "^1.0.1", "libp2p-crypto-secp256k1": "^0.1.4", - "multihashing-async": "~0.4.5", "nodeify": "^1.0.1", "pem-jwk": "^1.5.1", "protocol-buffers": "^3.2.1", "rsa-pem-to-jwk": "^1.1.3", "safe-buffer": "^5.1.1", "tweetnacl": "^1.0.0", - "webcrypto-shim": "github:dignifiedquire/webcrypto-shim#master" + "webcrypto-shim": "github:dignifiedquire/webcrypto-shim#master", + "multihashing-async": "~0.4.5" }, "devDependencies": { "aegir": "^11.0.2", @@ -54,9 +51,6 @@ "dirty-chai": "^2.0.1", "pre-commit": "^1.2.2" }, - "optionalDependencies": { - "node-webcrypto-ossl": "^1.0.30" - }, "pre-commit": [ "lint", "test" @@ -85,4 +79,4 @@ "greenkeeperio-bot ", "nikuda " ] -} \ No newline at end of file +} diff --git a/src/crypto/ciphers-browser.js b/src/aes/ciphers-browser.js similarity index 100% rename from src/crypto/ciphers-browser.js rename to src/aes/ciphers-browser.js diff --git a/src/crypto/ciphers.js b/src/aes/ciphers.js similarity index 100% rename from src/crypto/ciphers.js rename to src/aes/ciphers.js diff --git a/src/crypto/aes.js b/src/aes/index.js similarity index 100% rename from src/crypto/aes.js rename to src/aes/index.js diff --git a/src/crypto.js b/src/crypto.js deleted file mode 100644 index c83c0c6..0000000 --- a/src/crypto.js +++ /dev/null @@ -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') diff --git a/src/crypto/ed25519.js b/src/crypto/ed25519.js deleted file mode 100644 index b46c1d2..0000000 --- a/src/crypto/ed25519.js +++ /dev/null @@ -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)) - }) -} diff --git a/src/crypto/rsa.js b/src/crypto/rsa.js deleted file mode 100644 index 835b4b9..0000000 --- a/src/crypto/rsa.js +++ /dev/null @@ -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)) - }) - } -} diff --git a/src/crypto/webcrypto-browser.js b/src/crypto/webcrypto-browser.js deleted file mode 100644 index e55cae5..0000000 --- a/src/crypto/webcrypto-browser.js +++ /dev/null @@ -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') -} diff --git a/src/crypto/webcrypto.js b/src/crypto/webcrypto.js deleted file mode 100644 index f664ad8..0000000 --- a/src/crypto/webcrypto.js +++ /dev/null @@ -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 - } -} diff --git a/src/crypto/hmac-browser.js b/src/hmac/index-browser.js similarity index 70% rename from src/crypto/hmac-browser.js rename to src/hmac/index-browser.js index 88a389a..2ac5cd6 100644 --- a/src/crypto/hmac-browser.js +++ b/src/hmac/index-browser.js @@ -3,8 +3,8 @@ const nodeify = require('nodeify') const Buffer = require('safe-buffer').Buffer -const crypto = require('./webcrypto')() -const lengths = require('./hmac-lengths') +const crypto = require('../webcrypto.js')() +const lengths = require('./lengths') const hashTypes = { SHA1: 'SHA-1', @@ -27,11 +27,8 @@ 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) + nodeify(crypto.subtle.sign({name: 'HMAC'}, key, data) + .then((raw) => Buffer.from(raw)), cb) }, length: lengths[hashType] } diff --git a/src/crypto/hmac.js b/src/hmac/index.js similarity index 65% rename from src/crypto/hmac.js rename to src/hmac/index.js index 9b62dd1..ad72398 100644 --- a/src/crypto/hmac.js +++ b/src/hmac/index.js @@ -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) } diff --git a/src/crypto/hmac-lengths.js b/src/hmac/lengths.js similarity index 100% rename from src/crypto/hmac-lengths.js rename to src/hmac/lengths.js diff --git a/src/index.js b/src/index.js index 9bf59a2..34daa92 100644 --- a/src/index.js +++ b/src/index.js @@ -1,114 +1,20 @@ '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')) -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) -} - -// 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.aes = aes +exports.hmac = hmac +exports.keys = keys exports.randomBytes = (number) => { if (!number || typeof number !== 'number') { throw new Error('first argument must be a Number bigger than 0') } - return c.rsa.getRandomValues(new Uint8Array(number)) + return rsa.getRandomValues(new Uint8Array(number)) } diff --git a/src/crypto/ecdh-browser.js b/src/keys/ecdh-browser.js similarity index 84% rename from src/crypto/ecdh-browser.js rename to src/keys/ecdh-browser.js index ca806b4..c63f4da 100644 --- a/src/crypto/ecdh-browser.js +++ b/src/keys/ecdh-browser.js @@ -1,11 +1,11 @@ 'use strict' -const crypto = require('./webcrypto')() +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 +16,7 @@ const bits = { } exports.generateEphmeralKeyPair = function (curve, callback) { - nodeify(crypto.subtle.generateKey( + nodeify(webcrypto.subtle.generateKey( { name: 'ECDH', namedCurve: curve @@ -34,7 +34,7 @@ exports.generateEphmeralKeyPair = function (curve, callback) { let privateKey if (forcePrivate) { - privateKey = crypto.subtle.importKey( + privateKey = webcrypto.subtle.importKey( 'jwk', unmarshalPrivateKey(curve, forcePrivate), { @@ -49,7 +49,7 @@ exports.generateEphmeralKeyPair = function (curve, callback) { } const keys = Promise.all([ - crypto.subtle.importKey( + webcrypto.subtle.importKey( 'jwk', unmarshalPublicKey(curve, theirPub), { @@ -62,7 +62,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 +73,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) } diff --git a/src/crypto/ecdh.js b/src/keys/ecdh.js similarity index 100% rename from src/crypto/ecdh.js rename to src/keys/ecdh.js diff --git a/src/keys/ed25519-class.js b/src/keys/ed25519-class.js new file mode 100644 index 0000000..4d6b2f1 --- /dev/null +++ b/src/keys/ed25519-class.js @@ -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 +} diff --git a/src/keys/ed25519.js b/src/keys/ed25519.js index 19cafb3..dc96acf 100644 --- a/src/keys/ed25519.js +++ b/src/keys/ed25519.js @@ -1,164 +1,47 @@ 'use strict' -const multihashing = require('multihashing-async') -const protobuf = require('protocol-buffers') +const nacl = require('tweetnacl') +const setImmediate = require('async/setImmediate') const Buffer = require('safe-buffer').Buffer -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) - } +exports.generateKey = function (callback) { + const done = (err, res) => setImmediate(() => { + callback(err, res) + }) - 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) { + let keys try { - bytes = ensureKey(bytes, crypto.privateKeyLength + crypto.publicKeyLength) + keys = nacl.sign.keyPair() } catch (err) { - return callback(err) + return done(err) } - const privateKeyBytes = bytes.slice(0, crypto.privateKeyLength) - const publicKeyBytes = bytes.slice(crypto.privateKeyLength, bytes.length) - callback(null, new Ed25519PrivateKey(privateKeyBytes, publicKeyBytes)) + done(null, keys) } -function unmarshalEd25519PublicKey (bytes) { - bytes = ensureKey(bytes, crypto.publicKeyLength) - return new Ed25519PublicKey(bytes) +// seed should be a 32 byte uint8array +exports.generateKeyFromSeed = function (seed, callback) { + const done = (err, res) => setImmediate(() => callback(err, res)) + + let keys + try { + keys = nacl.sign.keyPair.fromSeed(seed) + } catch (err) { + return done(err) + } + done(null, keys) } -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) +exports.hashAndSign = function (key, msg, callback) { + setImmediate(() => { + callback(null, Buffer.from(nacl.sign.detached(msg, key))) }) } -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) +exports.hashAndVerify = function (key, sig, msg, callback) { + setImmediate(() => { + callback(null, nacl.sign.detached.verify(msg, sig, key)) }) } - -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 -} diff --git a/src/ephemeral-keys.js b/src/keys/ephemeral-keys.js similarity index 72% rename from src/ephemeral-keys.js rename to src/keys/ephemeral-keys.js index 8f91b65..68097e1 100644 --- a/src/ephemeral-keys.js +++ b/src/keys/ephemeral-keys.js @@ -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) } diff --git a/src/keys/index.js b/src/keys/index.js index 2e4443a..b84b523 100644 --- a/src/keys/index.js +++ b/src/keys/index.js @@ -1,7 +1,104 @@ 'use strict' -module.exports = { - rsa: require('./rsa'), - ed25519: require('./ed25519'), - secp256k1: require('libp2p-crypto-secp256k1') +const protobuf = require('protocol-buffers') +const pbm = protobuf(require('./keys.proto')) + +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 } diff --git a/src/key-stretcher.js b/src/keys/key-stretcher.js similarity index 96% rename from src/key-stretcher.js rename to src/keys/key-stretcher.js index 1e6d58c..51e45e8 100644 --- a/src/key-stretcher.js +++ b/src/keys/key-stretcher.js @@ -1,8 +1,8 @@ '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 +38,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) } diff --git a/src/keys/keys.js b/src/keys/keys.js new file mode 100644 index 0000000..3f8acb3 --- /dev/null +++ b/src/keys/keys.js @@ -0,0 +1,7 @@ +'use strict' + +module.exports = { + rsa: require('./rsa-class'), + ed25519: require('./ed25519-class'), + secp256k1: require('libp2p-crypto-secp256k1') +} diff --git a/src/crypto.proto.js b/src/keys/keys.proto.js similarity index 100% rename from src/crypto.proto.js rename to src/keys/keys.proto.js diff --git a/src/crypto/rsa-browser.js b/src/keys/rsa-browser.js similarity index 80% rename from src/crypto/rsa-browser.js rename to src/keys/rsa-browser.js index a6c89d5..8df964a 100644 --- a/src/crypto/rsa-browser.js +++ b/src/keys/rsa-browser.js @@ -3,12 +3,12 @@ const nodeify = require('nodeify') const Buffer = require('safe-buffer').Buffer -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, @@ -27,7 +27,7 @@ exports.generateKey = function (bits, callback) { // Takes a jwk key exports.unmarshalPrivateKey = function (key, callback) { - const privateKey = crypto.subtle.importKey( + const privateKey = webcrypto.subtle.importKey( 'jwk', key, { @@ -51,11 +51,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 +65,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 +74,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 +84,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 +95,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, diff --git a/src/keys/rsa-class.js b/src/keys/rsa-class.js new file mode 100644 index 0000000..f3f3f0f --- /dev/null +++ b/src/keys/rsa-class.js @@ -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 +} diff --git a/src/crypto/rsa-utils.js b/src/keys/rsa-utils.js similarity index 98% rename from src/crypto/rsa-utils.js rename to src/keys/rsa-utils.js index 65c2c1e..d10f2a1 100644 --- a/src/crypto/rsa-utils.js +++ b/src/keys/rsa-utils.js @@ -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 diff --git a/src/keys/rsa.js b/src/keys/rsa.js index cd8aabe..3d66f86 100644 --- a/src/keys/rsa.js +++ b/src/keys/rsa.js @@ -1,133 +1,56 @@ '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 +exports.generateKey = function (bits, callback) { + const done = (err, res) => setImmediate(() => callback(err, res)) + + let key + try { + key = keypair({ bits: bits }) + } catch (err) { + return done(err) } - 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)) + done(null, { + privateKey: pemToJwk(key.private), + publicKey: pemToJwk(key.public) }) } -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) +// Takes a jwk key +exports.unmarshalPrivateKey = function (key, callback) { + callback(null, { + privateKey: key, + publicKey: { + kty: key.kty, + n: key.n, + e: key.e } - - cb(null, new RsaPrivateKey(keys.privateKey, keys.publicKey)) }) } -function ensure (cb) { - if (typeof cb !== 'function') { - throw new Error('callback is required') - } +exports.getRandomValues = function (arr) { + return crypto.randomBytes(arr.length) } -module.exports = { - RsaPublicKey, - RsaPrivateKey, - unmarshalRsaPublicKey, - unmarshalRsaPrivateKey, - generateKeyPair +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))) } diff --git a/src/crypto/util.js b/src/util.js similarity index 100% rename from src/crypto/util.js rename to src/util.js diff --git a/src/webcrypto.js b/src/webcrypto.js new file mode 100644 index 0000000..c736f02 --- /dev/null +++ b/src/webcrypto.js @@ -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') +} diff --git a/test/aes.spec.js b/test/aes/aes.spec.js similarity index 93% rename from test/aes.spec.js rename to test/aes/aes.spec.js index 719d2a6..4c7c524 100644 --- a/test/aes.spec.js +++ b/test/aes/aes.spec.js @@ -9,9 +9,9 @@ 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', @@ -40,6 +40,7 @@ describe('AES-CTR', () => { }) }) }) + Object.keys(bytes).forEach((byte) => { it(`${bytes[byte]} - fixed - encrypt and decrypt`, (done) => { const key = Buffer.alloc(parseInt(byte, 10)) @@ -57,10 +58,10 @@ describe('AES-CTR', () => { cipher.encrypt(input, (err, res) => { 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(res).to.eql(input) cb() }) }) diff --git a/test/index.spec.js b/test/crypto.spec.js similarity index 65% rename from test/index.spec.js rename to test/crypto.spec.js index 4b4b204..d6df5f4 100644 --- a/test/index.spec.js +++ b/test/crypto.spec.js @@ -12,7 +12,7 @@ const fixtures = require('./fixtures/go-key-rsa') describe('libp2p-crypto', () => { let key before((done) => { - crypto.generateKeyPair('RSA', 2048, (err, _key) => { + crypto.keys.generateKeyPair('RSA', 2048, (err, _key) => { if (err) { return done(err) } @@ -22,21 +22,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) } @@ -52,60 +53,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) }) }) diff --git a/test/hmac.spec.js b/test/hmac/hmac.spec.js similarity index 95% rename from test/hmac.spec.js rename to test/hmac/hmac.spec.js index 13d1d92..0643cd7 100644 --- a/test/hmac.spec.js +++ b/test/hmac/hmac.spec.js @@ -8,7 +8,7 @@ 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'] diff --git a/test/ed25519.spec.js b/test/keys/ed25519.spec.js similarity index 58% rename from test/ed25519.spec.js rename to test/keys/ed25519.spec.js index c7da837..9eb9a7b 100644 --- a/test/ed25519.spec.js +++ b/test/keys/ed25519.spec.js @@ -7,14 +7,14 @@ const expect = chai.expect chai.use(dirtyChai) const Buffer = require('safe-buffer').Buffer -const crypto = require('../src') -const ed25519 = crypto.keys.ed25519 -const fixtures = require('./fixtures/go-key-ed25519') +const crypto = require('../../src') +const ed25519 = crypto.keys.keys.ed25519 +const fixtures = require('../fixtures/go-key-ed25519') describe('ed25519', () => { let key before((done) => { - crypto.generateKeyPair('Ed25519', 512, (err, _key) => { + crypto.keys.generateKeyPair('Ed25519', 512, (err, _key) => { if (err) return done(err) key = _key done() @@ -22,11 +22,7 @@ describe('ed25519', () => { }) it('generates a valid key', (done) => { - expect( - key - ).to.be.an.instanceof( - ed25519.Ed25519PrivateKey - ) + expect(key).to.be.an.instanceof(ed25519.Ed25519PrivateKey) key.hash((err, digest) => { if (err) { @@ -40,13 +36,9 @@ describe('ed25519', () => { it('generates a valid key from seed', (done) => { 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) - expect( - seededkey - ).to.be.an.instanceof( - ed25519.Ed25519PrivateKey - ) + expect(seededkey).to.be.an.instanceof(ed25519.Ed25519PrivateKey) seededkey.hash((err, digest) => { if (err) { @@ -61,42 +53,29 @@ describe('ed25519', () => { it('generates the same key from the same seed', (done) => { 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) - crypto.generateKeyPairFromSeed('Ed25519', seed, 512, (err, seededkey2) => { + crypto.keys.generateKeyPairFromSeed('Ed25519', seed, 512, (err, seededkey2) => { if (err) return done(err) - expect( - seededkey1.equals(seededkey2) - ).to.be.eql( - true - ) - expect( - seededkey1.public.equals(seededkey2.public) - ).to.be.eql( - true - ) + 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) => { - var seed1 = crypto.randomBytes(32) - crypto.generateKeyPairFromSeed('Ed25519', seed1, 512, (err, seededkey1) => { - if (err) return done(err) - var seed2 = crypto.randomBytes(32) - crypto.generateKeyPairFromSeed('Ed25519', seed2, 512, (err, seededkey2) => { - if (err) return done(err) - expect( - seededkey1.equals(seededkey2) - ).to.be.eql( - false - ) - expect( - seededkey1.public.equals(seededkey2.public) - ).to.be.eql( - false - ) + 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() }) }) @@ -106,15 +85,10 @@ describe('ed25519', () => { const text = crypto.randomBytes(512) key.sign(text, (err, sig) => { - if (err) { - return done(err) - } + expect(err).to.not.exist() key.public.verify(text, sig, (err, res) => { - if (err) { - return done(err) - } - + expect(err).to.not.exist() expect(res).to.be.eql(true) done() }) @@ -129,22 +103,14 @@ describe('ed25519', () => { } 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 = ed25519.unmarshalEd25519PublicKey(pkMarshal) const pkMarshal2 = pk2.marshal() - expect( - pkMarshal - ).to.be.eql( - pkMarshal2 - ) + expect(pkMarshal).to.eql(pkMarshal2) done() }) }) @@ -153,44 +119,25 @@ describe('ed25519', () => { it('equals itself', () => { expect( key.equals(key) - ).to.be.eql( + ).to.eql( true ) expect( key.public.equals(key.public) - ).to.be.eql( + ).to.eql( true ) }) it('not equals other key', (done) => { - crypto.generateKeyPair('Ed25519', 512, (err, key2) => { + crypto.keys.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 - ) + 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() }) }) @@ -207,7 +154,7 @@ describe('ed25519', () => { if (err) { return done(err) } - expect(valid).to.be.eql(true) + expect(valid).to.eql(true) done() }) }) @@ -232,8 +179,9 @@ describe('ed25519', () => { describe('go interop', () => { let privateKey + before((done) => { - crypto.unmarshalPrivateKey(fixtures.verify.privateKey, (err, key) => { + crypto.keys.unmarshalPrivateKey(fixtures.verify.privateKey, (err, key) => { expect(err).to.not.exist() privateKey = key done() @@ -241,12 +189,11 @@ describe('ed25519', () => { }) 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(ok).to.eql(true) done() }) }) @@ -254,7 +201,7 @@ describe('ed25519', () => { 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) + expect(sig).to.eql(fixtures.verify.signature) done() }) }) diff --git a/test/ephemeral-keys.spec.js b/test/keys/ephemeral-keys.spec.js similarity index 78% rename from test/ephemeral-keys.spec.js rename to test/keys/ephemeral-keys.spec.js index 065661b..b645440 100644 --- a/test/ephemeral-keys.spec.js +++ b/test/keys/ephemeral-keys.spec.js @@ -8,8 +8,8 @@ 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 = { @@ -27,8 +27,8 @@ 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(keys[0].key).to.have.length(lengths[curve]) @@ -48,8 +48,8 @@ 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() const alice = res[0] @@ -62,12 +62,7 @@ describe('generateEphemeralKeyPair', () => { ], (err, secrets) => { expect(err).to.not.exist() - expect( - secrets[0] - ).to.be.eql( - secrets[1] - ) - + expect(secrets[0]).to.eql(secrets[1]) expect(secrets[0]).to.have.length(32) done() diff --git a/test/key-stretcher.spec.js b/test/keys/key-stretcher.spec.js similarity index 84% rename from test/key-stretcher.spec.js rename to test/keys/key-stretcher.spec.js index e55d7fb..ac9da03 100644 --- a/test/key-stretcher.spec.js +++ b/test/keys/key-stretcher.spec.js @@ -6,8 +6,8 @@ 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') +const crypto = require('../../src') +const fixtures = require('../fixtures/go-stretch-key') describe('keyStretcher', () => { describe('generate', () => { @@ -17,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) } @@ -36,7 +36,7 @@ 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) } @@ -56,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) } diff --git a/test/rsa.spec.js b/test/keys/rsa.spec.js similarity index 67% rename from test/rsa.spec.js rename to test/keys/rsa.spec.js index 4f9e9ce..0acca1f 100644 --- a/test/rsa.spec.js +++ b/test/keys/rsa.spec.js @@ -7,26 +7,25 @@ const expect = chai.expect chai.use(dirtyChai) const Buffer = require('safe-buffer').Buffer -const crypto = require('../src') -const rsa = crypto.keys.rsa -const fixtures = require('./fixtures/go-key-rsa') +const crypto = require('../../src') +const rsa = crypto.keys.keys.rsa +const fixtures = require('../fixtures/go-key-rsa') describe('RSA', () => { 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) { @@ -65,68 +64,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() }) }) @@ -168,7 +134,7 @@ describe('RSA', () => { 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 diff --git a/test/secp256k1.spec.js b/test/keys/secp256k1.spec.js similarity index 61% rename from test/secp256k1.spec.js rename to test/keys/secp256k1.spec.js index 362ddcb..c644dbc 100644 --- a/test/secp256k1.spec.js +++ b/test/keys/secp256k1.spec.js @@ -5,8 +5,8 @@ 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 fixtures = require('../fixtures/secp256k1') +const crypto = require('../../src') const mockPublicKey = { 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', () => { let key before((done) => { - crypto.keys['secp256k1'] = mockSecp256k1Module - crypto.generateKeyPair('secp256k1', 256, (err, _key) => { + crypto.keys.keys['secp256k1'] = mockSecp256k1Module + crypto.keys.generateKeyPair('secp256k1', 256, (err, _key) => { if (err) return done(err) key = _key done() @@ -49,59 +75,25 @@ describe('with libp2p-crypto-secp256k1 module present', () => { }) it('generates a valid key', (done) => { - expect( - key - ).to.exist() + expect(key).to.exist() done() }) it('protobuf encoding', (done) => { - const keyMarshal = crypto.marshalPrivateKey(key) - crypto.unmarshalPrivateKey(keyMarshal, (err, key2) => { + const keyMarshal = crypto.keys.marshalPrivateKey(key) + crypto.keys.unmarshalPrivateKey(keyMarshal, (err, key2) => { if (err) return done(err) - const keyMarshal2 = crypto.marshalPrivateKey(key2) + const keyMarshal2 = crypto.keys.marshalPrivateKey(key2) - expect( - keyMarshal - ).to.be.eql( - keyMarshal2 - ) + expect(keyMarshal).to.eql(keyMarshal2) const pk = key.public - const pkMarshal = crypto.marshalPublicKey(pk) - const pk2 = crypto.unmarshalPublicKey(pkMarshal) - const pkMarshal2 = crypto.marshalPublicKey(pk2) + const pkMarshal = crypto.keys.marshalPublicKey(pk) + const pk2 = crypto.keys.unmarshalPublicKey(pkMarshal) + const pkMarshal2 = crypto.keys.marshalPublicKey(pk2) - expect( - pkMarshal - ).to.be.eql( - pkMarshal2 - ) + expect(pkMarshal).to.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) - }) -}) diff --git a/test/util.spec.js b/test/util.spec.js index cb2c5a2..bf66ccd 100644 --- a/test/util.spec.js +++ b/test/util.spec.js @@ -7,7 +7,7 @@ const dirtyChai = require('dirty-chai') const expect = chai.expect chai.use(dirtyChai) -const util = require('../src/crypto/util') +const util = require('../src/util') const BN = require('bn.js') describe('Util', () => { @@ -19,13 +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.be.eql('AP8') + expect(util.toBase64(bnpad, 2)).to.eql('AP8') done() }) })