diff --git a/README.md b/README.md index 54b84c8a..c0d51772 100644 --- a/README.md +++ b/README.md @@ -19,28 +19,31 @@ This repo contains the JavaScript implementation of the crypto primitives needed ## Table of Contents -- [Install](#install) -- [API](#api) - - [`crypto.hmac`](#cryptohmac) - - [`create(hash, secret, callback)`](#cryptohmaccreatehash-secret-callback) - - [`digest(data, callback)`](#digestdata-callback) - - [`crypto.aes`](#cryptoaes) - - [`create(key, iv, callback)`](#cryptoaescreatekey-iv-callback) - - [`encrypt(data, callback)`](#encryptdata-callback) - - [`decrypt(data, callback)`](#decryptdata-callback) - - [`keys`](#cryptokeys) - - [`generateKeyPair(type, bits, callback)`](#cryptokeysgeneratekeypairtype-bits-callback) - - [`generateEphemeralKeyPair(curve, callback)`](#cryptokeysgenerateephemeralkeypaircurve-callback) - - [`keyStretcher(cipherType, hashType, secret, callback)`](#cryptokeyskeystretcherciphertype-hashtype-secret-callback) - - [`marshalPublicKey(key[, type], callback)`](#cryptokeysmarshalpublickeykey-type-callback) - - [`unmarshalPublicKey(buf)`](#cryptokeysunmarshalpublickeybuf) - - [`marshalPrivateKey(key[, type])`](#cryptokeysmarshalprivatekeykey-type) - - [`unmarshalPrivateKey(buf, callback)`](#cryptokeysunmarshalprivatekeybuf-callback) - - [`import(pem, password, callback)`](#cryptokeysimportpem-password-callback) - - [`randomBytes(number)`](#cryptorandombytesnumber) - - [`pbkdf2(password, salt, iterations, keySize, hash)`](#cryptopbkdf2password-salt-iterations-keysize-hash) -- [Contribute](#contribute) -- [License](#license) +- [js-libp2p-crypto](#js-libp2p-crypto) + - [Lead Maintainer](#Lead-Maintainer) + - [Table of Contents](#Table-of-Contents) + - [Install](#Install) + - [API](#API) + - [`crypto.aes`](#cryptoaes) + - [`crypto.aes.create(key, iv)`](#cryptoaescreatekey-iv) + - [`decrypt(data)`](#decryptdata) + - [`encrypt(data)`](#encryptdata) + - [`crypto.hmac`](#cryptohmac) + - [`crypto.hmac.create(hash, secret)`](#cryptohmaccreatehash-secret) + - [`digest(data)`](#digestdata) + - [`crypto.keys`](#cryptokeys) + - [`crypto.keys.generateKeyPair(type, bits)`](#cryptokeysgenerateKeyPairtype-bits) + - [`crypto.keys.generateEphemeralKeyPair(curve)`](#cryptokeysgenerateEphemeralKeyPaircurve) + - [`crypto.keys.keyStretcher(cipherType, hashType, secret)`](#cryptokeyskeyStretchercipherType-hashType-secret) + - [`crypto.keys.marshalPublicKey(key, [type])`](#cryptokeysmarshalPublicKeykey-type) + - [`crypto.keys.unmarshalPublicKey(buf)`](#cryptokeysunmarshalPublicKeybuf) + - [`crypto.keys.marshalPrivateKey(key, [type])`](#cryptokeysmarshalPrivateKeykey-type) + - [`crypto.keys.unmarshalPrivateKey(buf)`](#cryptokeysunmarshalPrivateKeybuf) + - [`crypto.keys.import(pem, password)`](#cryptokeysimportpem-password) + - [`crypto.randomBytes(number)`](#cryptorandomBytesnumber) + - [`crypto.pbkdf2(password, salt, iterations, keySize, hash)`](#cryptopbkdf2password-salt-iterations-keySize-hash) + - [Contribute](#Contribute) + - [License](#License) ## Install @@ -56,130 +59,121 @@ Expoes an interface to AES encryption (formerly Rijndael), as defined in U.S. Fe This uses `CTR` mode. -#### `crypto.aes.create(key, iv, callback)` +#### `crypto.aes.create(key, iv)` - `key: Buffer` The key, if length `16` then `AES 128` is used. For length `32`, `AES 256` is used. - `iv: Buffer` Must have length `16`. -- `callback: Function` -##### `decrypt(data, callback)` +Returns `Promise<{decrypt, encrypt}>` + +##### `decrypt(data)` - `data: Buffer` -- `callback: Function` -##### `encrypt(data, callback)` +Returns `Promise` + +##### `encrypt(data)` - `data: Buffer` -- `callback: Function` + +Returns `Promise` ```js -var crypto = require('libp2p-crypto') +const crypto = require('libp2p-crypto') // Setting up Key and IV // A 16 bytes array, 128 Bits, AES-128 is chosen -var key128 = Buffer.from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]) +const key128 = Buffer.from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]) // A 16 bytes array, 128 Bits, -var IV = Buffer.from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]) +const IV = Buffer.from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]) async function main () { - let decryptedMessage = 'Hello, world!' - let encryptedMessage + const decryptedMessage = 'Hello, world!' // Encrypting - await crypto.aes.create(key128, IV, (err, cipher) => { - if (!err) { - cipher.encrypt(Buffer.from(decryptedMessage), (err, encryptedBuffer) => { - if (!err) { - console.log(encryptedBuffer) - // prints: - encryptedMessage = encryptedBuffer - } - }) - } - }) + const cipher = await crypto.aes.create(key128, IV) + const encryptedBuffer = await cipher.encrypt(Buffer.from(decryptedMessage)) + console.log(encryptedBuffer) + // prints: // Decrypting - await crypto.aes.create(key128, IV, (err, cipher) => { - if (!err) { - cipher.decrypt(encryptedMessage, (err, decryptedBuffer) => { - if (!err) { - console.log(decryptedBuffer) - // prints: - - console.log(decryptedBuffer.toString('utf-8')) - // prints: Hello, world! - } - }) - } - }) + const decipher = await crypto.aes.create(key128, IV) + const decryptedBuffer = await cipher.decrypt(encryptedBuffer) + + console.log(decryptedBuffer) + // prints: + + console.log(decryptedBuffer.toString('utf-8')) + // prints: Hello, world! } -main() +main() ``` ### `crypto.hmac` Exposes an interface to the Keyed-Hash Message Authentication Code (HMAC) as defined in U.S. Federal Information Processing Standards Publication 198. An HMAC is a cryptographic hash that uses a key to sign a message. The receiver verifies the hash by recomputing it using the same key. -#### `crypto.hmac.create(hash, secret, callback)` +#### `crypto.hmac.create(hash, secret)` - `hash: String` - `secret: Buffer` -- `callback: Function` -##### `digest(data, callback)` +Returns `Promise<{digest}>` + +##### `digest(data)` - `data: Buffer` -- `callback: Function` + +Returns `Promise` Example: ```js -var crypto = require('libp2p-crypto') +const crypto = require('libp2p-crypto') -let hash = 'SHA1' // 'SHA256' || 'SHA512' +async function main () { + const hash = 'SHA1' // 'SHA256' || 'SHA512' + const hmac = await crypto.hmac.create(hash, Buffer.from('secret')) + const sig = await hmac.digest(Buffer.from('hello world')) + console.log(sig) +} -crypto.hmac.create(hash, Buffer.from('secret'), (err, hmac) => { - if (!err) { - hmac.digest(Buffer.from('hello world'), (err, sig) => { - if (!err) { - console.log(sig) - } - }) - } -}) +main() ``` ### `crypto.keys` **Supported Key Types** -The [`generateKeyPair`](#cryptokeysgeneratekeypairtype-bits-callback), [`marshalPublicKey`](#cryptokeysmarshalpublickeykey-type-callback), and [`marshalPrivateKey`](#cryptokeysmarshalprivatekeykey-type) functions accept a string `type` argument. +The [`generateKeyPair`](#generatekeypairtype-bits), [`marshalPublicKey`](#marshalpublickeykey-type), and [`marshalPrivateKey`](#marshalprivatekeykey-type) functions accept a string `type` argument. Currently the `'RSA'` and `'ed25519'` types are supported, although ed25519 keys support only signing and verification of messages. For encryption / decryption support, RSA keys should be used. Installing the [libp2p-crypto-secp256k1](https://github.com/libp2p/js-libp2p-crypto-secp256k1) module adds support for the `'secp256k1'` type, which supports ECDSA signatures using the secp256k1 elliptic curve popularized by Bitcoin. This module is not installed by default, and should be explicitly depended on if your project requires secp256k1 support. -### `crypto.keys.generateKeyPair(type, bits, callback)` +### `crypto.keys.generateKeyPair(type, bits)` - `type: String`, see [Supported Key Types](#supported-key-types) above. - `bits: Number` Minimum of 1024 -- `callback: Function` + +Returns `Promise, publicKey>` Generates a keypair of the given type and bitsize. -### `crypto.keys.generateEphemeralKeyPair(curve, callback)` +### `crypto.keys.generateEphemeralKeyPair(curve)` - `curve: String`, one of `'P-256'`, `'P-384'`, `'P-521'` is currently supported -- `callback: Function` + +Returns `Promise` Generates an ephemeral public key and returns a function that will compute the shared secret key. Focuses only on ECDH now, but can be made more general in the future. -Calls back with an object of the form +Resolves to an object of the form: ```js { @@ -188,16 +182,17 @@ Calls back with an object of the form } ``` -### `crypto.keys.keyStretcher(cipherType, hashType, secret, callback)` +### `crypto.keys.keyStretcher(cipherType, hashType, secret)` - `cipherType: String`, one of `'AES-128'`, `'AES-256'`, `'Blowfish'` - `hashType: String`, one of `'SHA1'`, `SHA256`, `SHA512` - `secret: Buffer` -- `callback: Function` + +Returns `Promise` Generates a set of keys for each party by stretching the shared key. -Calls back with an object of the form: +Resolves to an object of the form: ```js { @@ -214,38 +209,46 @@ Calls back with an object of the form: } ``` -### `crypto.keys.marshalPublicKey(key[, type], callback)` +### `crypto.keys.marshalPublicKey(key, [type])` - `key: keys.rsa.RsaPublicKey | keys.ed25519.Ed25519PublicKey | require('libp2p-crypto-secp256k1').Secp256k1PublicKey` - `type: String`, see [Supported Key Types](#supported-key-types) above. +Returns `Buffer` + Converts a public key object into a protobuf serialized public key. ### `crypto.keys.unmarshalPublicKey(buf)` - `buf: Buffer` -Converts a protobuf serialized public key into its representative object. +Returns `RsaPublicKey|Ed25519PublicKey|Secp256k1PublicKey` -### `crypto.keys.marshalPrivateKey(key[, type])` +Converts a protobuf serialized public key into its representative object. + +### `crypto.keys.marshalPrivateKey(key, [type])` - `key: keys.rsa.RsaPrivateKey | keys.ed25519.Ed25519PrivateKey | require('libp2p-crypto-secp256k1').Secp256k1PrivateKey` - `type: String`, see [Supported Key Types](#supported-key-types) above. +Returns `Buffer` + Converts a private key object into a protobuf serialized private key. -### `crypto.keys.unmarshalPrivateKey(buf, callback)` +### `crypto.keys.unmarshalPrivateKey(buf)` - `buf: Buffer` -- `callback: Function` + +Returns `Promise` Converts a protobuf serialized private key into its representative object. -### `crypto.keys.import(pem, password, callback)` +### `crypto.keys.import(pem, password)` - `pem: string` - `password: string` -- `callback: Function` + +Returns `Promise` Converts a PEM password protected private key into its representative object. @@ -253,6 +256,8 @@ Converts a PEM password protected private key into its representative object. - `number: Number` +Returns `Buffer` + Generates a Buffer with length `number` populated by random bytes. ### `crypto.pbkdf2(password, salt, iterations, keySize, hash)` diff --git a/benchmarks/ephemeral-keys.js b/benchmarks/ephemeral-keys.js index 45bea896..5ed7dd23 100644 --- a/benchmarks/ephemeral-keys.js +++ b/benchmarks/ephemeral-keys.js @@ -10,16 +10,11 @@ const secrets = [] const curves = ['P-256', 'P-384', 'P-521'] curves.forEach((curve) => { - suite.add(`ephemeral key with secrect ${curve}`, (d) => { - crypto.keys.generateEphemeralKeyPair('P-256', (err, res) => { - if (err) { throw err } - res.genSharedKey(res.key, (err, secret) => { - if (err) { throw err } - secrets.push(secret) - - d.resolve() - }) - }) + suite.add(`ephemeral key with secrect ${curve}`, async (d) => { + const res = await crypto.keys.generateEphemeralKeyPair('P-256') + const secret = await res.genSharedKey(res.key) + secrets.push(secret) + d.resolve() }, { defer: true }) }) diff --git a/benchmarks/key-stretcher.js b/benchmarks/key-stretcher.js index 067369bf..cb7603c2 100644 --- a/benchmarks/key-stretcher.js +++ b/benchmarks/key-stretcher.js @@ -2,7 +2,6 @@ 'use strict' const Benchmark = require('benchmark') -const async = require('async') const crypto = require('../src') @@ -13,11 +12,9 @@ const keys = [] const ciphers = ['AES-128', 'AES-256', 'Blowfish'] const hashes = ['SHA1', 'SHA256', 'SHA512'] -async.waterfall([ - (cb) => crypto.keys.generateEphemeralKeyPair('P-256', cb), - (res, cb) => res.genSharedKey(res.key, cb) -], (err, secret) => { - if (err) { throw err } +;(async () => { + const res = await crypto.keys.generateEphemeralKeyPair('P-256') + const secret = await res.genSharedKey(res.key) ciphers.forEach((cipher) => hashes.forEach((hash) => { setup(cipher, hash, secret) @@ -26,15 +23,12 @@ async.waterfall([ suite .on('cycle', (event) => console.log(String(event.target))) .run({ async: true }) -}) +})() function setup (cipher, hash, secret) { - suite.add(`keyStretcher ${cipher} ${hash}`, (d) => { - crypto.keys.keyStretcher(cipher, hash, secret, (err, k) => { - if (err) { throw err } - - keys.push(k) - d.resolve() - }) + suite.add(`keyStretcher ${cipher} ${hash}`, async (d) => { + const k = await crypto.keys.keyStretcher(cipher, hash, secret) + keys.push(k) + d.resolve() }, { defer: true }) } diff --git a/benchmarks/rsa.js b/benchmarks/rsa.js index 172f17f3..74090960 100644 --- a/benchmarks/rsa.js +++ b/benchmarks/rsa.js @@ -10,30 +10,24 @@ const keys = [] const bits = [1024, 2048, 4096] bits.forEach((bit) => { - suite.add(`generateKeyPair ${bit}bits`, (d) => { - crypto.keys.generateKeyPair('RSA', bit, (err, key) => { - if (err) { throw err } - keys.push(key) - d.resolve() - }) + suite.add(`generateKeyPair ${bit}bits`, async (d) => { + const key = await crypto.keys.generateKeyPair('RSA', bit) + keys.push(key) + d.resolve() }, { defer: true }) }) -suite.add('sign and verify', (d) => { +suite.add('sign and verify', async (d) => { const key = keys[0] const text = key.genSecret() - key.sign(text, (err, sig) => { - if (err) { throw err } + const sig = await key.sign(text) + const res = await key.public.verify(text, sig) - key.public.verify(text, sig, (err, res) => { - if (err) { throw err } - if (res !== true) { throw new Error('failed to verify') } - d.resolve() - }) - }) + if (res !== true) { throw new Error('failed to verify') } + d.resolve() }, { defer: true }) diff --git a/package.json b/package.json index 5f0c8ceb..cd1760c9 100644 --- a/package.json +++ b/package.json @@ -37,15 +37,14 @@ "dependencies": { "asmcrypto.js": "^2.3.2", "asn1.js": "^5.0.1", - "async": "^2.6.2", - "bn.js": "^4.11.8", + "bn.js": "^5.0.0", "browserify-aes": "^1.2.0", "bs58": "^4.0.1", "iso-random-stream": "^1.1.0", "keypair": "^1.0.1", - "libp2p-crypto-secp256k1": "~0.3.0", - "multihashing-async": "~0.6.0", - "node-forge": "~0.7.6", + "libp2p-crypto-secp256k1": "~0.4.0", + "multihashing-async": "~0.7.0", + "node-forge": "~0.8.5", "pem-jwk": "^2.0.0", "protons": "^1.0.1", "rsa-pem-to-jwk": "^1.1.3", @@ -53,9 +52,9 @@ "ursa-optional": "~0.9.10" }, "devDependencies": { - "aegir": "^18.2.2", + "aegir": "^19.0.5", "benchmark": "^2.1.4", - "bundlesize": "~0.17.1", + "bundlesize": "~0.18.0", "chai": "^4.2.0", "chai-string": "^1.5.0", "dirty-chai": "^2.0.1" diff --git a/src/aes/index-browser.js b/src/aes/index-browser.js index ee3af5d7..7f4960be 100644 --- a/src/aes/index-browser.js +++ b/src/aes/index-browser.js @@ -1,13 +1,10 @@ 'use strict' const asm = require('asmcrypto.js') -const nextTick = require('async/nextTick') - -exports.create = function (key, iv, callback) { - const done = (err, res) => nextTick(() => callback(err, res)) +exports.create = async function (key, iv) { // eslint-disable-line require-await if (key.length !== 16 && key.length !== 32) { - return done(new Error('Invalid key length')) + throw new Error('Invalid key length') } const enc = new asm.AES_CTR.Encrypt({ @@ -20,36 +17,18 @@ exports.create = function (key, iv, callback) { }) const res = { - encrypt (data, cb) { - const done = (err, res) => nextTick(() => cb(err, res)) - - let res - try { - res = Buffer.from( - enc.process(data).result - ) - } catch (err) { - return done(err) - } - - done(null, res) + async encrypt (data) { // eslint-disable-line require-await + return Buffer.from( + enc.process(data).result + ) }, - decrypt (data, cb) { - const done = (err, res) => nextTick(() => cb(err, res)) - - let res - try { - res = Buffer.from( - dec.process(data).result - ) - } catch (err) { - return done(err) - } - - done(null, res) + async decrypt (data) { // eslint-disable-line require-await + return Buffer.from( + dec.process(data).result + ) } } - done(null, res) + return res } diff --git a/src/aes/index.js b/src/aes/index.js index 54005ff8..079ddc6a 100644 --- a/src/aes/index.js +++ b/src/aes/index.js @@ -7,24 +7,24 @@ const CIPHER_MODES = { 32: 'aes-256-ctr' } -exports.create = function (key, iv, callback) { +exports.create = async function (key, iv) { // eslint-disable-line require-await const mode = CIPHER_MODES[key.length] if (!mode) { - return callback(new Error('Invalid key length')) + throw new Error('Invalid key length') } const cipher = ciphers.createCipheriv(mode, key, iv) const decipher = ciphers.createDecipheriv(mode, key, iv) const res = { - encrypt (data, cb) { - cb(null, cipher.update(data)) + async encrypt (data) { // eslint-disable-line require-await + return cipher.update(data) }, - decrypt (data, cb) { - cb(null, decipher.update(data)) + async decrypt (data) { // eslint-disable-line require-await + return decipher.update(data) } } - callback(null, res) + return res } diff --git a/src/hmac/index-browser.js b/src/hmac/index-browser.js index c3c40c33..3592291d 100644 --- a/src/hmac/index-browser.js +++ b/src/hmac/index-browser.js @@ -1,8 +1,6 @@ 'use strict' -const nodeify = require('../nodeify') - -const crypto = require('../webcrypto') +const webcrypto = require('../webcrypto.js') const lengths = require('./lengths') const hashTypes = { @@ -11,15 +9,14 @@ const hashTypes = { SHA512: 'SHA-512' } -const sign = (key, data, cb) => { - nodeify(crypto.subtle.sign({ name: 'HMAC' }, key, data) - .then((raw) => Buffer.from(raw)), cb) +const sign = async (key, data) => { + return Buffer.from(await webcrypto.subtle.sign({ name: 'HMAC' }, key, data)) } -exports.create = function (hashType, secret, callback) { +exports.create = async function (hashType, secret) { const hash = hashTypes[hashType] - nodeify(crypto.subtle.importKey( + const key = await webcrypto.subtle.importKey( 'raw', secret, { @@ -28,12 +25,12 @@ exports.create = function (hashType, secret, callback) { }, false, ['sign'] - ).then((key) => { - return { - digest (data, cb) { - sign(key, data, cb) - }, - length: lengths[hashType] - } - }), callback) + ) + + return { + async digest (data) { // eslint-disable-line require-await + return sign(key, data) + }, + length: lengths[hashType] + } } diff --git a/src/hmac/index.js b/src/hmac/index.js index 61b61b10..ae4efe6e 100644 --- a/src/hmac/index.js +++ b/src/hmac/index.js @@ -2,21 +2,16 @@ const crypto = require('crypto') const lengths = require('./lengths') -const nextTick = require('async/nextTick') -exports.create = function (hash, secret, callback) { +exports.create = async function (hash, secret) { // eslint-disable-line require-await const res = { - digest (data, cb) { + async digest (data) { // eslint-disable-line require-await const hmac = crypto.createHmac(hash.toLowerCase(), secret) - hmac.update(data) - - nextTick(() => { - cb(null, hmac.digest()) - }) + return hmac.digest() }, length: lengths[hash] } - callback(null, res) + return res } diff --git a/src/index.js b/src/index.js index e0a52d74..863625ed 100644 --- a/src/index.js +++ b/src/index.js @@ -4,8 +4,6 @@ const hmac = require('./hmac') const aes = require('./aes') const keys = require('./keys') -exports = module.exports - exports.aes = aes exports.hmac = hmac exports.keys = keys diff --git a/src/keys/ecdh-browser.js b/src/keys/ecdh-browser.js index ab54759a..ee7c2edd 100644 --- a/src/keys/ecdh-browser.js +++ b/src/keys/ecdh-browser.js @@ -1,12 +1,8 @@ 'use strict' -const webcrypto = require('../webcrypto') -const nodeify = require('../nodeify') +const webcrypto = require('../webcrypto.js') const BN = require('asn1.js').bignum - -const util = require('../util') -const toBase64 = util.toBase64 -const toBn = util.toBn +const { toBase64, toBn } = require('../util') const bits = { 'P-256': 256, @@ -14,72 +10,66 @@ const bits = { 'P-521': 521 } -exports.generateEphmeralKeyPair = function (curve, callback) { - nodeify(webcrypto.subtle.generateKey( +exports.generateEphmeralKeyPair = async function (curve) { + const pair = await webcrypto.subtle.generateKey( { name: 'ECDH', namedCurve: curve }, true, ['deriveBits'] - ).then((pair) => { - // forcePrivate is used for testing only - const genSharedKey = (theirPub, forcePrivate, cb) => { - if (typeof forcePrivate === 'function') { - cb = forcePrivate - forcePrivate = undefined - } - - let privateKey - - if (forcePrivate) { - privateKey = webcrypto.subtle.importKey( - 'jwk', - unmarshalPrivateKey(curve, forcePrivate), - { - name: 'ECDH', - namedCurve: curve - }, - false, - ['deriveBits'] - ) - } else { - privateKey = Promise.resolve(pair.privateKey) - } - - const keys = Promise.all([ - webcrypto.subtle.importKey( - 'jwk', - unmarshalPublicKey(curve, theirPub), - { - name: 'ECDH', - namedCurve: curve - }, - false, - [] - ), - privateKey - ]) - - nodeify(keys.then((keys) => webcrypto.subtle.deriveBits( + ) + + // forcePrivate is used for testing only + const genSharedKey = async (theirPub, forcePrivate) => { + let privateKey + + if (forcePrivate) { + privateKey = await webcrypto.subtle.importKey( + 'jwk', + unmarshalPrivateKey(curve, forcePrivate), { name: 'ECDH', - namedCurve: curve, - public: keys[0] + namedCurve: curve }, - keys[1], - bits[curve] - )).then((bits) => Buffer.from(bits)), cb) + false, + ['deriveBits'] + ) + } else { + privateKey = pair.privateKey } - return webcrypto.subtle.exportKey('jwk', pair.publicKey) - .then((publicKey) => { - return { - key: marshalPublicKey(publicKey), - genSharedKey - } - }) - }), callback) + const keys = [ + await webcrypto.subtle.importKey( + 'jwk', + unmarshalPublicKey(curve, theirPub), + { + name: 'ECDH', + namedCurve: curve + }, + false, + [] + ), + privateKey + ] + + return Buffer.from(await webcrypto.subtle.deriveBits( + { + name: 'ECDH', + namedCurve: curve, + public: keys[0] + }, + keys[1], + bits[curve] + )) + } + + const publicKey = await webcrypto.subtle.exportKey('jwk', pair.publicKey) + + return { + key: marshalPublicKey(publicKey), + genSharedKey + } } const curveLengths = { diff --git a/src/keys/ecdh.js b/src/keys/ecdh.js index b0fda097..4efec3e8 100644 --- a/src/keys/ecdh.js +++ b/src/keys/ecdh.js @@ -1,7 +1,6 @@ 'use strict' const crypto = require('crypto') -const nextTick = require('async/nextTick') const curves = { 'P-256': 'prime256v1', @@ -9,33 +8,21 @@ const curves = { 'P-521': 'secp521r1' } -exports.generateEphmeralKeyPair = function (curve, callback) { +exports.generateEphmeralKeyPair = async function (curve) { // eslint-disable-line require-await if (!curves[curve]) { - return callback(new Error(`Unkown curve: ${curve}`)) + throw new Error(`Unkown curve: ${curve}`) } const ecdh = crypto.createECDH(curves[curve]) ecdh.generateKeys() - nextTick(() => callback(null, { + return { key: ecdh.getPublicKey(), - genSharedKey (theirPub, forcePrivate, cb) { - if (typeof forcePrivate === 'function') { - cb = forcePrivate - forcePrivate = null - } - + async genSharedKey (theirPub, forcePrivate) { // eslint-disable-line require-await if (forcePrivate) { ecdh.setPrivateKey(forcePrivate.private) } - let secret - try { - secret = ecdh.computeSecret(theirPub) - } catch (err) { - return cb(err) - } - - nextTick(() => cb(null, secret)) + return ecdh.computeSecret(theirPub) } - })) + } } diff --git a/src/keys/ed25519-class.js b/src/keys/ed25519-class.js index 0dff4b0b..3724e0a2 100644 --- a/src/keys/ed25519-class.js +++ b/src/keys/ed25519-class.js @@ -12,9 +12,8 @@ class Ed25519PublicKey { this._key = ensureKey(key, crypto.publicKeyLength) } - verify (data, sig, callback) { - ensure(callback) - crypto.hashAndVerify(this._key, sig, data, callback) + async verify (data, sig) { // eslint-disable-line require-await + return crypto.hashAndVerify(this._key, sig, data) } marshal () { @@ -32,9 +31,8 @@ class Ed25519PublicKey { return this.bytes.equals(key.bytes) } - hash (callback) { - ensure(callback) - multihashing(this.bytes, 'sha2-256', callback) + async hash () { // eslint-disable-line require-await + return multihashing(this.bytes, 'sha2-256') } } @@ -46,9 +44,8 @@ class Ed25519PrivateKey { this._publicKey = ensureKey(publicKey, crypto.publicKeyLength) } - sign (message, callback) { - ensure(callback) - crypto.hashAndSign(this._key, message, callback) + async sign (message) { // eslint-disable-line require-await + return crypto.hashAndSign(this._key, message) } get public () { @@ -74,9 +71,8 @@ class Ed25519PrivateKey { return this.bytes.equals(key.bytes) } - hash (callback) { - ensure(callback) - multihashing(this.bytes, 'sha2-256', callback) + async hash () { // eslint-disable-line require-await + return multihashing(this.bytes, 'sha2-256') } /** @@ -86,28 +82,19 @@ class Ed25519PrivateKey { * The public key is a protobuf encoding containing a type and the DER encoding * of the PKCS SubjectPublicKeyInfo. * - * @param {function(Error, id)} callback - * @returns {undefined} + * @returns {Promise} */ - id (callback) { - this.public.hash((err, hash) => { - if (err) { - return callback(err) - } - callback(null, bs58.encode(hash)) - }) + async id () { + const hash = await this.public.hash() + return bs58.encode(hash) } } -function unmarshalEd25519PrivateKey (bytes, callback) { - try { - bytes = ensureKey(bytes, crypto.privateKeyLength + crypto.publicKeyLength) - } catch (err) { - return callback(err) - } +function unmarshalEd25519PrivateKey (bytes) { + bytes = ensureKey(bytes, crypto.privateKeyLength + crypto.publicKeyLength) const privateKeyBytes = bytes.slice(0, crypto.privateKeyLength) const publicKeyBytes = bytes.slice(crypto.privateKeyLength, bytes.length) - callback(null, new Ed25519PrivateKey(privateKeyBytes, publicKeyBytes)) + return new Ed25519PrivateKey(privateKeyBytes, publicKeyBytes) } function unmarshalEd25519PublicKey (bytes) { @@ -115,52 +102,14 @@ function unmarshalEd25519PublicKey (bytes) { return new Ed25519PublicKey(bytes) } -function generateKeyPair (_bits, cb) { - if (cb === undefined && typeof _bits === 'function') { - cb = _bits - } - - crypto.generateKey((err, keys) => { - if (err) { - return cb(err) - } - let privkey - try { - privkey = new Ed25519PrivateKey(keys.secretKey, keys.publicKey) - } catch (err) { - cb(err) - return - } - - cb(null, privkey) - }) +async function generateKeyPair () { + const { secretKey, publicKey } = await crypto.generateKey() + return new Ed25519PrivateKey(secretKey, publicKey) } -function generateKeyPairFromSeed (seed, _bits, cb) { - if (cb === undefined && typeof _bits === 'function') { - cb = _bits - } - - crypto.generateKeyFromSeed(seed, (err, keys) => { - if (err) { - return cb(err) - } - let privkey - try { - privkey = new Ed25519PrivateKey(keys.secretKey, keys.publicKey) - } catch (err) { - cb(err) - return - } - - cb(null, privkey) - }) -} - -function ensure (cb) { - if (typeof cb !== 'function') { - throw new Error('callback is required') - } +async function generateKeyPairFromSeed (seed) { + const { secretKey, publicKey } = await crypto.generateKeyFromSeed(seed) + return new Ed25519PrivateKey(secretKey, publicKey) } function ensureKey (key, length) { diff --git a/src/keys/ed25519.js b/src/keys/ed25519.js index a01f1570..cbe6c865 100644 --- a/src/keys/ed25519.js +++ b/src/keys/ed25519.js @@ -1,51 +1,23 @@ 'use strict' const nacl = require('tweetnacl') -const nextTick = require('async/nextTick') exports.publicKeyLength = nacl.sign.publicKeyLength exports.privateKeyLength = nacl.sign.secretKeyLength -exports.generateKey = function (callback) { - nextTick(() => { - let result - try { - result = nacl.sign.keyPair() - } catch (err) { - return callback(err) - } - callback(null, result) - }) +exports.generateKey = async function () { // eslint-disable-line require-await + return nacl.sign.keyPair() } // seed should be a 32 byte uint8array -exports.generateKeyFromSeed = function (seed, callback) { - nextTick(() => { - let result - try { - result = nacl.sign.keyPair.fromSeed(seed) - } catch (err) { - return callback(err) - } - callback(null, result) - }) +exports.generateKeyFromSeed = async function (seed) { // eslint-disable-line require-await + return nacl.sign.keyPair.fromSeed(seed) } -exports.hashAndSign = function (key, msg, callback) { - nextTick(() => { - callback(null, Buffer.from(nacl.sign.detached(msg, key))) - }) +exports.hashAndSign = async function (key, msg) { // eslint-disable-line require-await + return Buffer.from(nacl.sign.detached(msg, key)) } -exports.hashAndVerify = function (key, sig, msg, callback) { - nextTick(() => { - let result - try { - result = nacl.sign.detached.verify(msg, sig, key) - } catch (err) { - return callback(err) - } - - callback(null, result) - }) +exports.hashAndVerify = async function (key, sig, msg) { // eslint-disable-line require-await + return nacl.sign.detached.verify(msg, sig, key) } diff --git a/src/keys/ephemeral-keys.js b/src/keys/ephemeral-keys.js index 68097e16..5a19819a 100644 --- a/src/keys/ephemeral-keys.js +++ b/src/keys/ephemeral-keys.js @@ -6,6 +6,4 @@ const ecdh = require('./ecdh') // the shared secret key. // // Focuses only on ECDH now, but can be made more general in the future. -module.exports = (curve, callback) => { - ecdh.generateEphmeralKeyPair(curve, callback) -} +module.exports = async (curve) => ecdh.generateEphmeralKeyPair(curve) // eslint-disable-line require-await diff --git a/src/keys/index.js b/src/keys/index.js index 7d3e3c6c..a14d6dbc 100644 --- a/src/keys/index.js +++ b/src/keys/index.js @@ -27,27 +27,27 @@ exports.keyStretcher = require('./key-stretcher') exports.generateEphemeralKeyPair = require('./ephemeral-keys') // Generates a keypair of the given type and bitsize -exports.generateKeyPair = (type, bits, cb) => { +exports.generateKeyPair = async (type, bits) => { // eslint-disable-line require-await let key = supportedKeys[type.toLowerCase()] if (!key) { - return cb(new Error('invalid or unsupported key type')) + throw new Error('invalid or unsupported key type') } - key.generateKeyPair(bits, cb) + return key.generateKeyPair(bits) } // Generates a keypair of the given type and bitsize // seed is a 32 byte uint8array -exports.generateKeyPairFromSeed = (type, seed, bits, cb) => { +exports.generateKeyPairFromSeed = async (type, seed, bits) => { // eslint-disable-line require-await let key = supportedKeys[type.toLowerCase()] if (!key) { - return cb(new Error('invalid or unsupported key type')) + throw new Error('invalid or unsupported key type') } if (type.toLowerCase() !== 'ed25519') { - return cb(new Error('Seed key derivation is unimplemented for RSA or secp256k1')) + throw new Error('Seed key derivation is unimplemented for RSA or secp256k1') } - key.generateKeyPairFromSeed(seed, bits, cb) + return key.generateKeyPairFromSeed(seed, bits) } // Converts a protobuf serialized public key into its @@ -84,29 +84,23 @@ exports.marshalPublicKey = (key, type) => { // Converts a protobuf serialized private key into its // representative object -exports.unmarshalPrivateKey = (buf, callback) => { - let decoded - try { - decoded = keysPBM.PrivateKey.decode(buf) - } catch (err) { - return callback(err) - } - +exports.unmarshalPrivateKey = async (buf) => { // eslint-disable-line require-await + const decoded = keysPBM.PrivateKey.decode(buf) const data = decoded.Data switch (decoded.Type) { case keysPBM.KeyType.RSA: - return supportedKeys.rsa.unmarshalRsaPrivateKey(data, callback) + return supportedKeys.rsa.unmarshalRsaPrivateKey(data) case keysPBM.KeyType.Ed25519: - return supportedKeys.ed25519.unmarshalEd25519PrivateKey(data, callback) + return supportedKeys.ed25519.unmarshalEd25519PrivateKey(data) case keysPBM.KeyType.Secp256k1: if (supportedKeys.secp256k1) { - return supportedKeys.secp256k1.unmarshalSecp256k1PrivateKey(data, callback) + return supportedKeys.secp256k1.unmarshalSecp256k1PrivateKey(data) } else { - return callback(new Error('secp256k1 support requires libp2p-crypto-secp256k1 package')) + throw new Error('secp256k1 support requires libp2p-crypto-secp256k1 package') } default: - callback(new Error('invalid or unsupported key type')) + throw new Error('invalid or unsupported key type') } } @@ -120,16 +114,12 @@ exports.marshalPrivateKey = (key, type) => { return key.bytes } -exports.import = (pem, password, callback) => { - try { - const key = forge.pki.decryptRsaPrivateKey(pem, password) - if (key === null) { - throw new Error('Cannot read the key, most likely the password is wrong or not a RSA key') - } - let der = forge.asn1.toDer(forge.pki.privateKeyToAsn1(key)) - der = Buffer.from(der.getBytes(), 'binary') - return supportedKeys.rsa.unmarshalRsaPrivateKey(der, callback) - } catch (err) { - callback(err) +exports.import = async (pem, password) => { // eslint-disable-line require-await + const key = forge.pki.decryptRsaPrivateKey(pem, password) + if (key === null) { + throw new Error('Cannot read the key, most likely the password is wrong or not a RSA key') } + let der = forge.asn1.toDer(forge.pki.privateKeyToAsn1(key)) + der = Buffer.from(der.getBytes(), 'binary') + return supportedKeys.rsa.unmarshalRsaPrivateKey(der) } diff --git a/src/keys/key-stretcher.js b/src/keys/key-stretcher.js index 2d7f9956..b8e1c940 100644 --- a/src/keys/key-stretcher.js +++ b/src/keys/key-stretcher.js @@ -1,6 +1,5 @@ 'use strict' -const whilst = require('async/whilst') const hmac = require('../hmac') const cipherMap = { @@ -20,15 +19,15 @@ const cipherMap = { // Generates a set of keys for each party by stretching the shared key. // (myIV, theirIV, myCipherKey, theirCipherKey, myMACKey, theirMACKey) -module.exports = (cipherType, hash, secret, callback) => { +module.exports = async (cipherType, hash, secret) => { const cipher = cipherMap[cipherType] if (!cipher) { - return callback(new Error('unkown cipherType passed')) + throw new Error('unkown cipherType passed') } if (!hash) { - return callback(new Error('unkown hashType passed')) + throw new Error('unkown hashType passed') } const cipherKeySize = cipher.keySize @@ -37,72 +36,38 @@ module.exports = (cipherType, hash, secret, callback) => { const seed = Buffer.from('key expansion') const resultLength = 2 * (ivSize + cipherKeySize + hmacKeySize) - hmac.create(hash, secret, (err, m) => { - if (err) { - return callback(err) - } - - m.digest(seed, (err, a) => { - if (err) { - return callback(err) - } - - let result = [] - let j = 0 - - whilst( - () => j < resultLength, - stretch, - finish - ) - - function stretch (cb) { - m.digest(Buffer.concat([a, seed]), (err, b) => { - if (err) { - return cb(err) - } - - let todo = b.length + const m = await hmac.create(hash, secret) + let a = await m.digest(seed) - if (j + todo > resultLength) { - todo = resultLength - j - } + let result = [] + let j = 0 - result.push(b) + while (j < resultLength) { + const b = await m.digest(Buffer.concat([a, seed])) + let todo = b.length - j += todo - - m.digest(a, (err, _a) => { - if (err) { - return cb(err) - } - a = _a - cb() - }) - }) - } - - function finish (err) { - if (err) { - return callback(err) - } + if (j + todo > resultLength) { + todo = resultLength - j + } - const half = resultLength / 2 - const resultBuffer = Buffer.concat(result) - const r1 = resultBuffer.slice(0, half) - const r2 = resultBuffer.slice(half, resultLength) + result.push(b) + j += todo + a = await m.digest(a) + } - const createKey = (res) => ({ - iv: res.slice(0, ivSize), - cipherKey: res.slice(ivSize, ivSize + cipherKeySize), - macKey: res.slice(ivSize + cipherKeySize) - }) + const half = resultLength / 2 + const resultBuffer = Buffer.concat(result) + const r1 = resultBuffer.slice(0, half) + const r2 = resultBuffer.slice(half, resultLength) - callback(null, { - k1: createKey(r1), - k2: createKey(r2) - }) - } - }) + const createKey = (res) => ({ + iv: res.slice(0, ivSize), + cipherKey: res.slice(ivSize, ivSize + cipherKeySize), + macKey: res.slice(ivSize + cipherKeySize) }) + + return { + k1: createKey(r1), + k2: createKey(r2) + } } diff --git a/src/keys/keys.proto.js b/src/keys/keys.proto.js index 0fb3d5ca..4e0ab088 100644 --- a/src/keys/keys.proto.js +++ b/src/keys/keys.proto.js @@ -12,4 +12,4 @@ message PublicKey { message PrivateKey { required KeyType Type = 1; required bytes Data = 2; -}` \ No newline at end of file +}` diff --git a/src/keys/rsa-browser.js b/src/keys/rsa-browser.js index 6cf12865..5cefbdb3 100644 --- a/src/keys/rsa-browser.js +++ b/src/keys/rsa-browser.js @@ -1,13 +1,12 @@ 'use strict' -const nodeify = require('../nodeify') -const webcrypto = require('../webcrypto') +const webcrypto = require('../webcrypto.js') const randomBytes = require('../random-bytes') exports.utils = require('./rsa-utils') -exports.generateKey = function (bits, callback) { - nodeify(webcrypto.subtle.generateKey( +exports.generateKey = async function (bits) { + const pair = await webcrypto.subtle.generateKey( { name: 'RSASSA-PKCS1-v1_5', modulusLength: bits, @@ -17,16 +16,18 @@ exports.generateKey = function (bits, callback) { true, ['sign', 'verify'] ) - .then(exportKey) - .then((keys) => ({ - privateKey: keys[0], - publicKey: keys[1] - })), callback) + + const keys = await exportKey(pair) + + return { + privateKey: keys[0], + publicKey: keys[1] + } } // Takes a jwk key -exports.unmarshalPrivateKey = function (key, callback) { - const privateKey = webcrypto.subtle.importKey( +exports.unmarshalPrivateKey = async function (key) { + const privateKey = await webcrypto.subtle.importKey( 'jwk', key, { @@ -37,22 +38,26 @@ exports.unmarshalPrivateKey = function (key, callback) { ['sign'] ) - nodeify(Promise.all([ + const pair = [ privateKey, - derivePublicFromPrivate(key) - ]).then((keys) => exportKey({ - privateKey: keys[0], - publicKey: keys[1] - })).then((keys) => ({ + await derivePublicFromPrivate(key) + ] + + const keys = await exportKey({ + privateKey: pair[0], + publicKey: pair[1] + }) + + return { privateKey: keys[0], publicKey: keys[1] - })), callback) + } } exports.getRandomValues = randomBytes -exports.hashAndSign = function (key, msg, callback) { - nodeify(webcrypto.subtle.importKey( +exports.hashAndSign = async function (key, msg) { + const privateKey = await webcrypto.subtle.importKey( 'jwk', key, { @@ -61,17 +66,19 @@ exports.hashAndSign = function (key, msg, callback) { }, false, ['sign'] - ).then((privateKey) => { - return webcrypto.subtle.sign( - { name: 'RSASSA-PKCS1-v1_5' }, - privateKey, - Uint8Array.from(msg) - ) - }).then((sig) => Buffer.from(sig)), callback) + ) + + const sig = await webcrypto.subtle.sign( + { name: 'RSASSA-PKCS1-v1_5' }, + privateKey, + Uint8Array.from(msg) + ) + + return Buffer.from(sig) } -exports.hashAndVerify = function (key, sig, msg, callback) { - nodeify(webcrypto.subtle.importKey( +exports.hashAndVerify = async function (key, sig, msg) { + const publicKey = await webcrypto.subtle.importKey( 'jwk', key, { @@ -80,14 +87,14 @@ exports.hashAndVerify = function (key, sig, msg, callback) { }, false, ['verify'] - ).then((publicKey) => { - return webcrypto.subtle.verify( - { name: 'RSASSA-PKCS1-v1_5' }, - publicKey, - sig, - msg - ) - }), callback) + ) + + return webcrypto.subtle.verify( + { name: 'RSASSA-PKCS1-v1_5' }, + publicKey, + sig, + msg + ) } function exportKey (pair) { diff --git a/src/keys/rsa-class.js b/src/keys/rsa-class.js index ff67731e..59592683 100644 --- a/src/keys/rsa-class.js +++ b/src/keys/rsa-class.js @@ -3,22 +3,18 @@ const multihashing = require('multihashing-async') const protobuf = require('protons') const bs58 = require('bs58') -const nextTick = require('async/nextTick') const crypto = require('./rsa') const pbm = protobuf(require('./keys.proto')) -require('node-forge/lib/sha512') -require('node-forge/lib/pbe') -const forge = require('node-forge/lib/forge') +const forge = require('node-forge') class RsaPublicKey { constructor (key) { this._key = key } - verify (data, sig, callback) { - ensure(callback) - crypto.hashAndVerify(this._key, sig, data, callback) + async verify (data, sig) { // eslint-disable-line require-await + return crypto.hashAndVerify(this._key, sig, data) } marshal () { @@ -40,9 +36,8 @@ class RsaPublicKey { return this.bytes.equals(key.bytes) } - hash (callback) { - ensure(callback) - multihashing(this.bytes, 'sha2-256', callback) + async hash () { // eslint-disable-line require-await + return multihashing(this.bytes, 'sha2-256') } } @@ -58,9 +53,8 @@ class RsaPrivateKey { return crypto.getRandomValues(16) } - sign (message, callback) { - ensure(callback) - crypto.hashAndSign(this._key, message, callback) + async sign (message) { // eslint-disable-line require-await + return crypto.hashAndSign(this._key, message) } get public () { @@ -71,10 +65,6 @@ class RsaPrivateKey { return new RsaPublicKey(this._publicKey) } - decrypt (msg, callback) { - crypto.decrypt(this._key, msg, callback) - } - marshal () { return crypto.utils.jwkToPkcs1(this._key) } @@ -90,9 +80,8 @@ class RsaPrivateKey { return this.bytes.equals(key.bytes) } - hash (callback) { - ensure(callback) - multihashing(this.bytes, 'sha2-256', callback) + async hash () { // eslint-disable-line require-await + return multihashing(this.bytes, 'sha2-256') } /** @@ -102,16 +91,11 @@ class RsaPrivateKey { * The public key is a protobuf encoding containing a type and the DER encoding * of the PKCS SubjectPublicKeyInfo. * - * @param {function(Error, id)} callback - * @returns {undefined} + * @returns {Promise} */ - id (callback) { - this.public.hash((err, hash) => { - if (err) { - return callback(err) - } - callback(null, bs58.encode(hash)) - }) + async id () { + const hash = await this.public.hash() + return bs58.encode(hash) } /** @@ -119,87 +103,55 @@ class RsaPrivateKey { * * @param {string} [format] - Defaults to 'pkcs-8'. * @param {string} password - The password to read the encrypted PEM - * @param {function(Error, KeyInfo)} callback - * @returns {undefined} + * @returns {KeyInfo} */ - export (format, password, callback) { - if (typeof password === 'function') { - callback = password + async export (format, password) { // eslint-disable-line require-await + if (password == null) { password = format format = 'pkcs-8' } - ensure(callback) - - nextTick(() => { - let err = null - let pem = null - try { - const buffer = new forge.util.ByteBuffer(this.marshal()) - const asn1 = forge.asn1.fromDer(buffer) - const privateKey = forge.pki.privateKeyFromAsn1(asn1) - if (format === 'pkcs-8') { - const options = { - algorithm: 'aes256', - count: 10000, - saltSize: 128 / 8, - prfAlgorithm: 'sha512' - } - pem = forge.pki.encryptRsaPrivateKey(privateKey, password, options) - } else { - err = new Error(`Unknown export format '${format}'`) - } - } catch (_err) { - err = _err + let pem = null + + const buffer = new forge.util.ByteBuffer(this.marshal()) + const asn1 = forge.asn1.fromDer(buffer) + const privateKey = forge.pki.privateKeyFromAsn1(asn1) + + if (format === 'pkcs-8') { + const options = { + algorithm: 'aes256', + count: 10000, + saltSize: 128 / 8, + prfAlgorithm: 'sha512' } + pem = forge.pki.encryptRsaPrivateKey(privateKey, password, options) + } else { + throw new Error(`Unknown export format '${format}'`) + } - callback(err, pem) - }) + return pem } } -function unmarshalRsaPrivateKey (bytes, callback) { +async function unmarshalRsaPrivateKey (bytes) { const jwk = crypto.utils.pkcs1ToJwk(bytes) - - crypto.unmarshalPrivateKey(jwk, (err, keys) => { - if (err) { - return callback(err) - } - - callback(null, new RsaPrivateKey(keys.privateKey, keys.publicKey)) - }) + const keys = await crypto.unmarshalPrivateKey(jwk) + return new RsaPrivateKey(keys.privateKey, keys.publicKey) } function unmarshalRsaPublicKey (bytes) { const jwk = crypto.utils.pkixToJwk(bytes) - return new RsaPublicKey(jwk) } -function fromJwk (jwk, callback) { - crypto.unmarshalPrivateKey(jwk, (err, keys) => { - if (err) { - return callback(err) - } - - callback(null, new RsaPrivateKey(keys.privateKey, keys.publicKey)) - }) +async function fromJwk (jwk) { + const keys = await crypto.unmarshalPrivateKey(jwk) + return new RsaPrivateKey(keys.privateKey, keys.publicKey) } -function generateKeyPair (bits, callback) { - crypto.generateKey(bits, (err, keys) => { - if (err) { - return callback(err) - } - - callback(null, new RsaPrivateKey(keys.privateKey, keys.publicKey)) - }) -} - -function ensure (callback) { - if (typeof callback !== 'function') { - throw new Error('callback is required') - } +async function generateKeyPair (bits) { + const keys = await crypto.generateKey(bits) + return new RsaPrivateKey(keys.privateKey, keys.publicKey) } module.exports = { diff --git a/src/keys/rsa.js b/src/keys/rsa.js index 67ea7665..23919828 100644 --- a/src/keys/rsa.js +++ b/src/keys/rsa.js @@ -2,7 +2,6 @@ const crypto = require('crypto') const randomBytes = require('../random-bytes') -const nextTick = require('async/nextTick') let keypair try { @@ -30,70 +29,41 @@ const jwkToPem = require('pem-jwk').jwk2pem exports.utils = require('./rsa-utils') -exports.generateKey = function (bits, callback) { - nextTick(() => { - let result - try { - const key = keypair({ bits: bits }) - result = { - privateKey: pemToJwk(key.private), - publicKey: pemToJwk(key.public) - } - } catch (err) { - return callback(err) - } - - callback(null, result) - }) +exports.generateKey = async function (bits) { // eslint-disable-line require-await + const key = keypair({ bits }) + return { + privateKey: pemToJwk(key.private), + publicKey: pemToJwk(key.public) + } } // Takes a jwk key -exports.unmarshalPrivateKey = function (key, callback) { - nextTick(() => { - if (!key) { - return callback(new Error('Key is invalid')) +exports.unmarshalPrivateKey = async function (key) { // eslint-disable-line require-await + if (!key) { + throw new Error('Key is invalid') + } + return { + privateKey: key, + publicKey: { + kty: key.kty, + n: key.n, + e: key.e } - callback(null, { - privateKey: key, - publicKey: { - kty: key.kty, - n: key.n, - e: key.e - } - }) - }) + } } exports.getRandomValues = randomBytes -exports.hashAndSign = function (key, msg, callback) { - nextTick(() => { - let result - try { - const sign = crypto.createSign('RSA-SHA256') - sign.update(msg) - const pem = jwkToPem(key) - result = sign.sign(pem) - } catch (err) { - return callback(new Error('Key or message is invalid!: ' + err.message)) - } - - callback(null, result) - }) +exports.hashAndSign = async function (key, msg) { // eslint-disable-line require-await + const sign = crypto.createSign('RSA-SHA256') + sign.update(msg) + const pem = jwkToPem(key) + return sign.sign(pem) } -exports.hashAndVerify = function (key, sig, msg, callback) { - nextTick(() => { - let result - try { - const verify = crypto.createVerify('RSA-SHA256') - verify.update(msg) - const pem = jwkToPem(key) - result = verify.verify(pem, sig) - } catch (err) { - return callback(new Error('Key or message is invalid!:' + err.message)) - } - - callback(null, result) - }) +exports.hashAndVerify = async function (key, sig, msg) { // eslint-disable-line require-await + const verify = crypto.createVerify('RSA-SHA256') + verify.update(msg) + const pem = jwkToPem(key) + return verify.verify(pem, sig) } diff --git a/src/nodeify.js b/src/nodeify.js deleted file mode 100644 index 4a741b39..00000000 --- a/src/nodeify.js +++ /dev/null @@ -1,11 +0,0 @@ -'use strict' - -// Based on npmjs.com/nodeify but without additional `nextTick` calls -// to keep the overhead low -module.exports = function nodeify (promise, cb) { - return promise.then((res) => { - cb(null, res) - }, (err) => { - cb(err) - }) -} diff --git a/test/aes/aes.spec.js b/test/aes/aes.spec.js index 25824f7b..edd13742 100644 --- a/test/aes/aes.spec.js +++ b/test/aes/aes.spec.js @@ -6,7 +6,6 @@ const chai = require('chai') const dirtyChai = require('dirty-chai') const expect = chai.expect chai.use(dirtyChai) -const series = require('async/series') const crypto = require('../../src') const fixtures = require('./../fixtures/aes') @@ -19,53 +18,43 @@ const bytes = { describe('AES-CTR', () => { Object.keys(bytes).forEach((byte) => { - it(`${bytes[byte]} - encrypt and decrypt`, (done) => { + it(`${bytes[byte]} - encrypt and decrypt`, async () => { const key = Buffer.alloc(parseInt(byte, 10)) key.fill(5) const iv = Buffer.alloc(16) iv.fill(1) - crypto.aes.create(key, iv, (err, cipher) => { - expect(err).to.not.exist() - - series([ - encryptAndDecrypt(cipher), - encryptAndDecrypt(cipher), - encryptAndDecrypt(cipher), - encryptAndDecrypt(cipher), - encryptAndDecrypt(cipher) - ], done) - }) + const cipher = await crypto.aes.create(key, iv) + + await encryptAndDecrypt(cipher) + await encryptAndDecrypt(cipher) + await encryptAndDecrypt(cipher) + await encryptAndDecrypt(cipher) + await encryptAndDecrypt(cipher) }) }) Object.keys(bytes).forEach((byte) => { - it(`${bytes[byte]} - fixed - encrypt and decrypt`, (done) => { + it(`${bytes[byte]} - fixed - encrypt and decrypt`, async () => { const key = Buffer.alloc(parseInt(byte, 10)) key.fill(5) const iv = Buffer.alloc(16) iv.fill(1) - crypto.aes.create(key, iv, (err, cipher) => { - expect(err).to.not.exist() - - series(fixtures[byte].inputs.map((rawIn, i) => (cb) => { - const input = Buffer.from(rawIn) - const output = Buffer.from(fixtures[byte].outputs[i]) - cipher.encrypt(input, (err, res) => { - expect(err).to.not.exist() - expect(res).to.have.length(output.length) - expect(res).to.eql(output) - cipher.decrypt(res, (err, res) => { - expect(err).to.not.exist() - expect(res).to.eql(input) - cb() - }) - }) - }), done) - }) + const cipher = await crypto.aes.create(key, iv) + + for (let i = 0; i < fixtures[byte].inputs.length; i++) { + const rawIn = fixtures[byte].inputs[i] + const input = Buffer.from(rawIn) + const output = Buffer.from(fixtures[byte].outputs[i]) + const encrypted = await cipher.encrypt(input) + expect(encrypted).to.have.length(output.length) + expect(encrypted).to.eql(output) + const decrypted = await cipher.decrypt(encrypted) + expect(decrypted).to.eql(input) + } }) }) @@ -74,46 +63,35 @@ describe('AES-CTR', () => { return } - it(`${bytes[byte]} - go interop - encrypt and decrypt`, (done) => { + it(`${bytes[byte]} - go interop - encrypt and decrypt`, async () => { const key = Buffer.alloc(parseInt(byte, 10)) key.fill(5) const iv = Buffer.alloc(16) iv.fill(1) - crypto.aes.create(key, iv, (err, cipher) => { - expect(err).to.not.exist() - - series(goFixtures[byte].inputs.map((rawIn, i) => (cb) => { - const input = Buffer.from(rawIn) - const output = Buffer.from(goFixtures[byte].outputs[i]) - cipher.encrypt(input, (err, res) => { - expect(err).to.not.exist() - expect(res).to.have.length(output.length) - expect(res).to.be.eql(output) - cipher.decrypt(res, (err, res) => { - expect(err).to.not.exist() - expect(res).to.be.eql(input) - cb() - }) - }) - }), done) - }) + const cipher = await crypto.aes.create(key, iv) + + for (let i = 0; i < goFixtures[byte].inputs.length; i++) { + const rawIn = goFixtures[byte].inputs[i] + const input = Buffer.from(rawIn) + const output = Buffer.from(goFixtures[byte].outputs[i]) + const encrypted = await cipher.encrypt(input) + expect(encrypted).to.have.length(output.length) + expect(encrypted).to.eql(output) + const decrypted = await cipher.decrypt(encrypted) + expect(decrypted).to.eql(input) + } }) }) }) -function encryptAndDecrypt (cipher) { +async function encryptAndDecrypt (cipher) { const data = Buffer.alloc(100) data.fill(Math.ceil(Math.random() * 100)) - return (cb) => { - cipher.encrypt(data, (err, res) => { - expect(err).to.not.exist() - cipher.decrypt(res, (err, res) => { - expect(err).to.not.exist() - expect(res).to.be.eql(data) - cb() - }) - }) - } + + const encrypted = await cipher.encrypt(data) + const decrypted = await cipher.decrypt(encrypted) + + expect(decrypted).to.be.eql(data) } diff --git a/test/crypto.spec.js b/test/crypto.spec.js index b37e43cb..146983eb 100644 --- a/test/crypto.spec.js +++ b/test/crypto.spec.js @@ -12,14 +12,8 @@ const fixtures = require('./fixtures/go-key-rsa') describe('libp2p-crypto', function () { this.timeout(20 * 1000) let key - before((done) => { - crypto.keys.generateKeyPair('RSA', 512, (err, _key) => { - if (err) { - return done(err) - } - key = _key - done() - }) + before(async () => { + key = await crypto.keys.generateKeyPair('RSA', 512) }) it('marshalPublicKey and unmarshalPublicKey', () => { @@ -33,71 +27,41 @@ describe('libp2p-crypto', function () { }).to.throw() }) - it('marshalPrivateKey and unmarshalPrivateKey', (done) => { + it('marshalPrivateKey and unmarshalPrivateKey', async () => { expect(() => { crypto.keys.marshalPrivateKey(key, 'invalid-key-type') }).to.throw() - crypto.keys.unmarshalPrivateKey(crypto.keys.marshalPrivateKey(key), (err, key2) => { - if (err) { - return done(err) - } + const key2 = await crypto.keys.unmarshalPrivateKey(crypto.keys.marshalPrivateKey(key)) - expect(key2.equals(key)).to.be.eql(true) - expect(key2.public.equals(key.public)).to.be.eql(true) - done() - }) + expect(key2.equals(key)).to.be.eql(true) + expect(key2.public.equals(key.public)).to.be.eql(true) }) // marshalled keys seem to be slightly different // unsure as to if this is just a difference in encoding // or a bug describe('go interop', () => { - it('unmarshals private key', (done) => { - crypto.keys.unmarshalPrivateKey(fixtures.private.key, (err, key) => { - if (err) { - return done(err) - } - const hash = fixtures.private.hash - expect(fixtures.private.key).to.eql(key.bytes) - - key.hash((err, digest) => { - if (err) { - return done(err) - } - - expect(digest).to.eql(hash) - done() - }) - }) + it('unmarshals private key', async () => { + const key = await crypto.keys.unmarshalPrivateKey(fixtures.private.key) + const hash = fixtures.private.hash + expect(fixtures.private.key).to.eql(key.bytes) + const digest = await key.hash() + expect(digest).to.eql(hash) }) - it('unmarshals public key', (done) => { + it('unmarshals public key', async () => { const key = crypto.keys.unmarshalPublicKey(fixtures.public.key) const hash = fixtures.public.hash - expect(crypto.keys.marshalPublicKey(key)).to.eql(fixtures.public.key) - - key.hash((err, digest) => { - if (err) { - return done(err) - } - - expect(digest).to.eql(hash) - done() - }) + const digest = await key.hash() + expect(digest).to.eql(hash) }) - it('unmarshal -> marshal, private key', (done) => { - crypto.keys.unmarshalPrivateKey(fixtures.private.key, (err, key) => { - if (err) { - return done(err) - } - - const marshalled = crypto.keys.marshalPrivateKey(key) - expect(marshalled).to.eql(fixtures.private.key) - done() - }) + it('unmarshal -> marshal, private key', async () => { + const key = await crypto.keys.unmarshalPrivateKey(fixtures.private.key) + const marshalled = crypto.keys.marshalPrivateKey(key) + expect(marshalled).to.eql(fixtures.private.key) }) it('unmarshal -> marshal, public key', () => { diff --git a/test/helpers/test-garbage-error-handling.js b/test/helpers/test-garbage-error-handling.js index 46fedea6..50a5cf47 100644 --- a/test/helpers/test-garbage-error-handling.js +++ b/test/helpers/test-garbage-error-handling.js @@ -1,11 +1,6 @@ /* eslint-env mocha */ 'use strict' -const chai = require('chai') -const dirtyChai = require('dirty-chai') -const expect = chai.expect -chai.use(dirtyChai) - const util = require('util') const garbage = [Buffer.from('00010203040506070809', 'hex'), {}, null, false, undefined, true, 1, 0, Buffer.from(''), 'aGVsbG93b3JsZA==', 'helloworld', ''] @@ -23,14 +18,13 @@ function doTests (fncName, fnc, num, skipBuffersAndStrings) { for (let i = 0; i < num; i++) { args.push(garbage) } - it(fncName + '(' + args.map(garbage => util.inspect(garbage)).join(', ') + ')', cb => { - args.push((err, res) => { - expect(err).to.exist() - expect(res).to.not.exist() - cb() - }) - - fnc.apply(null, args) + it(fncName + '(' + args.map(garbage => util.inspect(garbage)).join(', ') + ')', async () => { + try { + await fnc.apply(null, args) + } catch (err) { + return // expected + } + throw new Error('Expected error to be thrown') }) }) } diff --git a/test/hmac/hmac.spec.js b/test/hmac/hmac.spec.js index 90be5465..72d53729 100644 --- a/test/hmac/hmac.spec.js +++ b/test/hmac/hmac.spec.js @@ -13,16 +13,10 @@ const hashes = ['SHA1', 'SHA256', 'SHA512'] describe('HMAC', () => { hashes.forEach((hash) => { - it(`${hash} - sign and verify`, (done) => { - crypto.hmac.create(hash, Buffer.from('secret'), (err, hmac) => { - expect(err).to.not.exist() - - hmac.digest(Buffer.from('hello world'), (err, sig) => { - expect(err).to.not.exist() - expect(sig).to.have.length(hmac.length) - done() - }) - }) + it(`${hash} - sign and verify`, async () => { + const hmac = await crypto.hmac.create(hash, Buffer.from('secret')) + const sig = await hmac.digest(Buffer.from('hello world')) + expect(sig).to.have.length(hmac.length) }) }) }) diff --git a/test/keys/ed25519.spec.js b/test/keys/ed25519.spec.js index b803de64..621c24f8 100644 --- a/test/keys/ed25519.spec.js +++ b/test/keys/ed25519.spec.js @@ -15,115 +15,67 @@ const testGarbage = require('../helpers/test-garbage-error-handling') describe('ed25519', function () { this.timeout(20 * 1000) let key - before((done) => { - crypto.keys.generateKeyPair('Ed25519', 512, (err, _key) => { - if (err) return done(err) - key = _key - done() - }) + before(async () => { + key = await crypto.keys.generateKeyPair('Ed25519', 512) }) - it('generates a valid key', (done) => { + it('generates a valid key', async () => { expect(key).to.be.an.instanceof(ed25519.Ed25519PrivateKey) - - key.hash((err, digest) => { - if (err) { - return done(err) - } - - expect(digest).to.have.length(34) - done() - }) + const digest = await key.hash() + expect(digest).to.have.length(34) }) - it('generates a valid key from seed', (done) => { + it('generates a valid key from seed', async () => { var seed = crypto.randomBytes(32) - crypto.keys.generateKeyPairFromSeed('Ed25519', seed, 512, (err, seededkey) => { - if (err) return done(err) - expect(seededkey).to.be.an.instanceof(ed25519.Ed25519PrivateKey) - - seededkey.hash((err, digest) => { - if (err) { - return done(err) - } - - expect(digest).to.have.length(34) - done() - }) - }) + const seededkey = await crypto.keys.generateKeyPairFromSeed('Ed25519', seed, 512) + expect(seededkey).to.be.an.instanceof(ed25519.Ed25519PrivateKey) + const digest = await seededkey.hash() + expect(digest).to.have.length(34) }) - it('generates the same key from the same seed', (done) => { - var seed = crypto.randomBytes(32) - crypto.keys.generateKeyPairFromSeed('Ed25519', seed, 512, (err, seededkey1) => { - if (err) return done(err) - crypto.keys.generateKeyPairFromSeed('Ed25519', seed, 512, (err, seededkey2) => { - if (err) return done(err) - expect(seededkey1.equals(seededkey2)).to.eql(true) - expect(seededkey1.public.equals(seededkey2.public)).to.eql(true) - done() - }) - }) + it('generates the same key from the same seed', async () => { + const seed = crypto.randomBytes(32) + const seededkey1 = await crypto.keys.generateKeyPairFromSeed('Ed25519', seed, 512) + const seededkey2 = await crypto.keys.generateKeyPairFromSeed('Ed25519', seed, 512) + expect(seededkey1.equals(seededkey2)).to.eql(true) + expect(seededkey1.public.equals(seededkey2.public)).to.eql(true) }) - it('generates different keys for different seeds', (done) => { + it('generates different keys for different seeds', async () => { const seed1 = crypto.randomBytes(32) - crypto.keys.generateKeyPairFromSeed('Ed25519', seed1, 512, (err, seededkey1) => { - expect(err).to.not.exist() - - const seed2 = crypto.randomBytes(32) - crypto.keys.generateKeyPairFromSeed('Ed25519', seed2, 512, (err, seededkey2) => { - expect(err).to.not.exist() - - expect(seededkey1.equals(seededkey2)).to.eql(false) - expect(seededkey1.public.equals(seededkey2.public)) - .to.eql(false) - done() - }) - }) + const seededkey1 = await crypto.keys.generateKeyPairFromSeed('Ed25519', seed1, 512) + const seed2 = crypto.randomBytes(32) + const seededkey2 = await crypto.keys.generateKeyPairFromSeed('Ed25519', seed2, 512) + expect(seededkey1.equals(seededkey2)).to.eql(false) + expect(seededkey1.public.equals(seededkey2.public)).to.eql(false) }) - it('signs', (done) => { + it('signs', async () => { const text = crypto.randomBytes(512) - - key.sign(text, (err, sig) => { - expect(err).to.not.exist() - - key.public.verify(text, sig, (err, res) => { - expect(err).to.not.exist() - expect(res).to.be.eql(true) - done() - }) - }) + const sig = await key.sign(text) + const res = await key.public.verify(text, sig) + expect(res).to.be.eql(true) }) - it('encoding', (done) => { + it('encoding', async () => { const keyMarshal = key.marshal() - ed25519.unmarshalEd25519PrivateKey(keyMarshal, (err, key2) => { - if (err) { - return done(err) - } - const keyMarshal2 = key2.marshal() + const key2 = await ed25519.unmarshalEd25519PrivateKey(keyMarshal) + const keyMarshal2 = key2.marshal() - expect(keyMarshal).to.eql(keyMarshal2) + expect(keyMarshal).to.eql(keyMarshal2) - const pk = key.public - const pkMarshal = pk.marshal() - const pk2 = ed25519.unmarshalEd25519PublicKey(pkMarshal) - const pkMarshal2 = pk2.marshal() + const pk = key.public + const pkMarshal = pk.marshal() + const pk2 = ed25519.unmarshalEd25519PublicKey(pkMarshal) + const pkMarshal2 = pk2.marshal() - expect(pkMarshal).to.eql(pkMarshal2) - done() - }) + expect(pkMarshal).to.eql(pkMarshal2) }) - it('key id', (done) => { - key.id((err, id) => { - expect(err).to.not.exist() - expect(id).to.exist() - expect(id).to.be.a('string') - done() - }) + it('key id', async () => { + const id = await key.id() + expect(id).to.exist() + expect(id).to.be.a('string') }) describe('key equals', () => { @@ -141,51 +93,27 @@ describe('ed25519', function () { ) }) - it('not equals other key', (done) => { - crypto.keys.generateKeyPair('Ed25519', 512, (err, key2) => { - if (err) return done(err) - - expect(key.equals(key2)).to.eql(false) - expect(key2.equals(key)).to.eql(false) - expect(key.public.equals(key2.public)).to.eql(false) - expect(key2.public.equals(key.public)).to.eql(false) - done() - }) + it('not equals other key', async () => { + const key2 = await crypto.keys.generateKeyPair('Ed25519', 512) + expect(key.equals(key2)).to.eql(false) + expect(key2.equals(key)).to.eql(false) + expect(key.public.equals(key2.public)).to.eql(false) + expect(key2.public.equals(key.public)).to.eql(false) }) }) - it('sign and verify', (done) => { + it('sign and verify', async () => { const data = Buffer.from('hello world') - key.sign(data, (err, sig) => { - if (err) { - return done(err) - } - - key.public.verify(data, sig, (err, valid) => { - if (err) { - return done(err) - } - expect(valid).to.eql(true) - done() - }) - }) + const sig = await key.sign(data) + const valid = await key.public.verify(data, sig) + expect(valid).to.eql(true) }) - it('fails to verify for different data', (done) => { + it('fails to verify for different data', async () => { const data = Buffer.from('hello world') - key.sign(data, (err, sig) => { - if (err) { - return done(err) - } - - key.public.verify(Buffer.from('hello'), sig, (err, valid) => { - if (err) { - return done(err) - } - expect(valid).to.be.eql(false) - done() - }) - }) + const sig = await key.sign(data) + const valid = await key.public.verify(Buffer.from('hello'), sig) + expect(valid).to.be.eql(false) }) describe('returns error via cb instead of crashing', () => { @@ -197,30 +125,20 @@ describe('ed25519', function () { describe('go interop', () => { let privateKey - before((done) => { - crypto.keys.unmarshalPrivateKey(fixtures.verify.privateKey, (err, key) => { - expect(err).to.not.exist() - privateKey = key - done() - }) + before(async () => { + const key = await crypto.keys.unmarshalPrivateKey(fixtures.verify.privateKey) + privateKey = key }) - it('verifies with data from go', (done) => { + it('verifies with data from go', async () => { const key = crypto.keys.unmarshalPublicKey(fixtures.verify.publicKey) - - key.verify(fixtures.verify.data, fixtures.verify.signature, (err, ok) => { - expect(err).to.not.exist() - expect(ok).to.eql(true) - done() - }) + const ok = await key.verify(fixtures.verify.data, fixtures.verify.signature) + expect(ok).to.eql(true) }) - it('generates the same signature as go', (done) => { - privateKey.sign(fixtures.verify.data, (err, sig) => { - expect(err).to.not.exist() - expect(sig).to.eql(fixtures.verify.signature) - done() - }) + it('generates the same signature as go', async () => { + const sig = await privateKey.sign(fixtures.verify.data) + expect(sig).to.eql(fixtures.verify.signature) }) }) }) diff --git a/test/keys/ephemeral-keys.spec.js b/test/keys/ephemeral-keys.spec.js index b645440d..d353f0d0 100644 --- a/test/keys/ephemeral-keys.spec.js +++ b/test/keys/ephemeral-keys.spec.js @@ -6,7 +6,6 @@ const chai = require('chai') const dirtyChai = require('dirty-chai') const expect = chai.expect chai.use(dirtyChai) -const parallel = require('async/parallel') const fixtures = require('../fixtures/go-elliptic-key') const crypto = require('../../src') @@ -25,49 +24,40 @@ const secretLengths = { describe('generateEphemeralKeyPair', () => { curves.forEach((curve) => { - it(`generate and shared key ${curve}`, (done) => { - parallel([ - (cb) => crypto.keys.generateEphemeralKeyPair(curve, cb), - (cb) => crypto.keys.generateEphemeralKeyPair(curve, cb) - ], (err, keys) => { - expect(err).to.not.exist() - expect(keys[0].key).to.have.length(lengths[curve]) - expect(keys[1].key).to.have.length(lengths[curve]) + it(`generate and shared key ${curve}`, async () => { + const keys = await Promise.all([ + crypto.keys.generateEphemeralKeyPair(curve), + crypto.keys.generateEphemeralKeyPair(curve) + ]) - keys[0].genSharedKey(keys[1].key, (err, shared) => { - expect(err).to.not.exist() - expect(shared).to.have.length(secretLengths[curve]) - done() - }) - }) + expect(keys[0].key).to.have.length(lengths[curve]) + expect(keys[1].key).to.have.length(lengths[curve]) + + const shared = await keys[0].genSharedKey(keys[1].key) + expect(shared).to.have.length(secretLengths[curve]) }) }) describe('go interop', () => { - it('generates a shared secret', (done) => { + it('generates a shared secret', async () => { const curve = fixtures.curve - parallel([ - (cb) => crypto.keys.generateEphemeralKeyPair(curve, cb), - (cb) => crypto.keys.generateEphemeralKeyPair(curve, cb) - ], (err, res) => { - expect(err).to.not.exist() - const alice = res[0] - const bob = res[1] - bob.key = fixtures.bob.public + const keys = await Promise.all([ + crypto.keys.generateEphemeralKeyPair(curve), + crypto.keys.generateEphemeralKeyPair(curve) + ]) - parallel([ - (cb) => alice.genSharedKey(bob.key, cb), - (cb) => bob.genSharedKey(alice.key, fixtures.bob, cb) - ], (err, secrets) => { - expect(err).to.not.exist() + const alice = keys[0] + const bob = keys[1] + bob.key = fixtures.bob.public - expect(secrets[0]).to.eql(secrets[1]) - expect(secrets[0]).to.have.length(32) + const secrets = await Promise.all([ + alice.genSharedKey(bob.key), + bob.genSharedKey(alice.key, fixtures.bob) + ]) - done() - }) - }) + expect(secrets[0]).to.eql(secrets[1]) + expect(secrets[0]).to.have.length(32) }) }) }) diff --git a/test/keys/key-stretcher.spec.js b/test/keys/key-stretcher.spec.js index ac9da032..514029cb 100644 --- a/test/keys/key-stretcher.spec.js +++ b/test/keys/key-stretcher.spec.js @@ -16,35 +16,17 @@ describe('keyStretcher', () => { let res let secret - before((done) => { - crypto.keys.generateEphemeralKeyPair('P-256', (err, _res) => { - if (err) { - return done(err) - } - res = _res - res.genSharedKey(res.key, (err, _secret) => { - if (err) { - return done(err) - } - - secret = _secret - done() - }) - }) + before(async () => { + res = await crypto.keys.generateEphemeralKeyPair('P-256') + secret = await res.genSharedKey(res.key) }) ciphers.forEach((cipher) => { hashes.forEach((hash) => { - it(`${cipher} - ${hash}`, (done) => { - crypto.keys.keyStretcher(cipher, hash, secret, (err, keys) => { - if (err) { - return done(err) - } - - expect(keys.k1).to.exist() - expect(keys.k2).to.exist() - done() - }) + it(`${cipher} - ${hash}`, async () => { + const keys = await crypto.keys.keyStretcher(cipher, hash, secret) + expect(keys.k1).to.exist() + expect(keys.k2).to.exist() }) }) }) @@ -52,24 +34,19 @@ describe('keyStretcher', () => { describe('go interop', () => { fixtures.forEach((test) => { - it(`${test.cipher} - ${test.hash}`, (done) => { + it(`${test.cipher} - ${test.hash}`, async () => { const cipher = test.cipher const hash = test.hash const secret = test.secret - crypto.keys.keyStretcher(cipher, hash, secret, (err, keys) => { - if (err) { - return done(err) - } + const keys = await crypto.keys.keyStretcher(cipher, hash, secret) - expect(keys.k1.iv).to.be.eql(test.k1.iv) - expect(keys.k1.cipherKey).to.be.eql(test.k1.cipherKey) - expect(keys.k1.macKey).to.be.eql(test.k1.macKey) + expect(keys.k1.iv).to.be.eql(test.k1.iv) + expect(keys.k1.cipherKey).to.be.eql(test.k1.cipherKey) + expect(keys.k1.macKey).to.be.eql(test.k1.macKey) - expect(keys.k2.iv).to.be.eql(test.k2.iv) - expect(keys.k2.cipherKey).to.be.eql(test.k2.cipherKey) - expect(keys.k2.macKey).to.be.eql(test.k2.macKey) - done() - }) + expect(keys.k2.iv).to.be.eql(test.k2.iv) + expect(keys.k2.cipherKey).to.be.eql(test.k2.cipherKey) + expect(keys.k2.macKey).to.be.eql(test.k2.macKey) }) }) }) diff --git a/test/keys/rsa-crypto-libs.js b/test/keys/rsa-crypto-libs.js index 87eca9dc..1492397e 100644 --- a/test/keys/rsa-crypto-libs.js +++ b/test/keys/rsa-crypto-libs.js @@ -32,23 +32,11 @@ describe('RSA crypto libs', function () { rsa = crypto.keys.supportedKeys.rsa }) - it('generates a valid key', (done) => { - crypto.keys.generateKeyPair('RSA', 512, (err, key) => { - if (err) { - return done(err) - } - - expect(key).to.be.an.instanceof(rsa.RsaPrivateKey) - - key.hash((err, digest) => { - if (err) { - return done(err) - } - - expect(digest).to.have.length(34) - done() - }) - }) + it('generates a valid key', async () => { + const key = await crypto.keys.generateKeyPair('RSA', 512) + expect(key).to.be.an.instanceof(rsa.RsaPrivateKey) + const digest = await key.hash() + expect(digest).to.have.length(34) }) after(() => { diff --git a/test/keys/rsa.spec.js b/test/keys/rsa.spec.js index b46c5f74..4c19fdc6 100644 --- a/test/keys/rsa.spec.js +++ b/test/keys/rsa.spec.js @@ -18,75 +18,42 @@ describe('RSA', function () { this.timeout(20 * 1000) let key - before((done) => { - crypto.keys.generateKeyPair('RSA', 512, (err, _key) => { - if (err) { - return done(err) - } - key = _key - done() - }) + before(async () => { + key = await crypto.keys.generateKeyPair('RSA', 512) }) - it('generates a valid key', (done) => { + it('generates a valid key', async () => { expect(key).to.be.an.instanceof(rsa.RsaPrivateKey) - - key.hash((err, digest) => { - if (err) { - return done(err) - } - - expect(digest).to.have.length(34) - done() - }) + const digest = await key.hash() + expect(digest).to.have.length(34) }) - it('signs', (done) => { + it('signs', async () => { const text = key.genSecret() - - key.sign(text, (err, sig) => { - if (err) { - return done(err) - } - - key.public.verify(text, sig, (err, res) => { - if (err) { - return done(err) - } - - expect(res).to.be.eql(true) - done() - }) - }) + const sig = await key.sign(text) + const res = await key.public.verify(text, sig) + expect(res).to.be.eql(true) }) - it('encoding', (done) => { + it('encoding', async () => { const keyMarshal = key.marshal() - rsa.unmarshalRsaPrivateKey(keyMarshal, (err, key2) => { - if (err) { - return done(err) - } - const keyMarshal2 = key2.marshal() + const key2 = await rsa.unmarshalRsaPrivateKey(keyMarshal) + const keyMarshal2 = key2.marshal() - expect(keyMarshal).to.eql(keyMarshal2) + expect(keyMarshal).to.eql(keyMarshal2) - const pk = key.public - const pkMarshal = pk.marshal() - const pk2 = rsa.unmarshalRsaPublicKey(pkMarshal) - const pkMarshal2 = pk2.marshal() + const pk = key.public + const pkMarshal = pk.marshal() + const pk2 = rsa.unmarshalRsaPublicKey(pkMarshal) + const pkMarshal2 = pk2.marshal() - expect(pkMarshal).to.eql(pkMarshal2) - done() - }) + expect(pkMarshal).to.eql(pkMarshal2) }) - it('key id', (done) => { - key.id((err, id) => { - expect(err).to.not.exist() - expect(id).to.exist() - expect(id).to.be.a('string') - done() - }) + it('key id', async () => { + const id = await key.id() + expect(id).to.exist() + expect(id).to.be.a('string') }) describe('key equals', () => { @@ -96,90 +63,54 @@ describe('RSA', function () { expect(key.public.equals(key.public)).to.eql(true) }) - it('not equals other key', (done) => { - crypto.keys.generateKeyPair('RSA', 512, (err, key2) => { - if (err) { - return done(err) - } - - expect(key.equals(key2)).to.eql(false) - expect(key2.equals(key)).to.eql(false) - expect(key.public.equals(key2.public)).to.eql(false) - expect(key2.public.equals(key.public)).to.eql(false) - done() - }) + it('not equals other key', async () => { + const key2 = await crypto.keys.generateKeyPair('RSA', 512) + expect(key.equals(key2)).to.eql(false) + expect(key2.equals(key)).to.eql(false) + expect(key.public.equals(key2.public)).to.eql(false) + expect(key2.public.equals(key.public)).to.eql(false) }) }) - it('sign and verify', (done) => { + it('sign and verify', async () => { const data = Buffer.from('hello world') - key.sign(data, (err, sig) => { - if (err) { - return done(err) - } - - key.public.verify(data, sig, (err, valid) => { - if (err) { - return done(err) - } - expect(valid).to.be.eql(true) - done() - }) - }) + const sig = await key.sign(data) + const valid = await key.public.verify(data, sig) + expect(valid).to.be.eql(true) }) - it('fails to verify for different data', (done) => { + it('fails to verify for different data', async () => { const data = Buffer.from('hello world') - key.sign(data, (err, sig) => { - if (err) { - return done(err) - } - - key.public.verify(Buffer.from('hello'), sig, (err, valid) => { - if (err) { - return done(err) - } - expect(valid).to.be.eql(false) - done() - }) - }) + const sig = await key.sign(data) + const valid = await key.public.verify(Buffer.from('hello'), sig) + expect(valid).to.be.eql(false) }) describe('export and import', () => { - it('password protected PKCS #8', (done) => { - key.export('pkcs-8', 'my secret', (err, pem) => { - expect(err).to.not.exist() - expect(pem).to.startsWith('-----BEGIN ENCRYPTED PRIVATE KEY-----') - crypto.keys.import(pem, 'my secret', (err, clone) => { - expect(err).to.not.exist() - expect(clone).to.exist() - expect(key.equals(clone)).to.eql(true) - done() - }) - }) + it('password protected PKCS #8', async () => { + const pem = await key.export('pkcs-8', 'my secret') + expect(pem).to.startsWith('-----BEGIN ENCRYPTED PRIVATE KEY-----') + const clone = await crypto.keys.import(pem, 'my secret') + expect(clone).to.exist() + expect(key.equals(clone)).to.eql(true) }) - it('defaults to PKCS #8', (done) => { - key.export('another secret', (err, pem) => { - expect(err).to.not.exist() - expect(pem).to.startsWith('-----BEGIN ENCRYPTED PRIVATE KEY-----') - crypto.keys.import(pem, 'another secret', (err, clone) => { - expect(err).to.not.exist() - expect(clone).to.exist() - expect(key.equals(clone)).to.eql(true) - done() - }) - }) + it('defaults to PKCS #8', async () => { + const pem = await key.export('another secret') + expect(pem).to.startsWith('-----BEGIN ENCRYPTED PRIVATE KEY-----') + const clone = await crypto.keys.import(pem, 'another secret') + expect(clone).to.exist() + expect(key.equals(clone)).to.eql(true) }) - it('needs correct password', (done) => { - key.export('another secret', (err, pem) => { - expect(err).to.not.exist() - crypto.keys.import(pem, 'not the secret', (err, clone) => { - expect(err).to.exist() - done() - }) - }) + it('needs correct password', async () => { + const pem = await key.export('another secret') + try { + await crypto.keys.import(pem, 'not the secret') + } catch (err) { + return // expected + } + throw new Error('Expected error to be thrown') }) }) @@ -190,20 +121,15 @@ describe('RSA', function () { }) describe('go interop', () => { - it('verifies with data from go', (done) => { + it('verifies with data from go', async () => { const key = crypto.keys.unmarshalPublicKey(fixtures.verify.publicKey) - - key.verify(fixtures.verify.data, fixtures.verify.signature, (err, ok) => { - if (err) throw err - expect(err).to.not.exist() - expect(ok).to.equal(true) - done() - }) + const ok = await key.verify(fixtures.verify.data, fixtures.verify.signature) + expect(ok).to.equal(true) }) }) describe('openssl interop', () => { - it('can read a private key', (done) => { + it('can read a private key', async () => { /* * Generated with * openssl genpkey -algorithm RSA @@ -251,19 +177,14 @@ gnjREs10u7zyqBIZH7KYVgyh27WxLr859ap8cKAH6Fb+UOPtZo3sUeeume60aebn 4pMwXeXP+LO8NIfRXV8mgrm86g== -----END PRIVATE KEY----- ` - crypto.keys.import(pem, '', (err, key) => { - expect(err).to.not.exist() - expect(key).to.exist() - key.id((err, id) => { - expect(err).to.not.exist() - expect(id).to.equal('QmfWu2Xp8DZzCkZZzoPB9rcrq4R4RZid6AWE6kmrUAzuHy') - done() - }) - }) + const key = await crypto.keys.import(pem, '') + expect(key).to.exist() + const id = await key.id() + expect(id).to.equal('QmfWu2Xp8DZzCkZZzoPB9rcrq4R4RZid6AWE6kmrUAzuHy') }) // AssertionError: expected 'this only supports pkcs5PBES2' to not exist - it.skip('can read a private encrypted key (v1)', (done) => { + it.skip('can read a private encrypted key (v1)', async () => { /* * Generated with * openssl genpkey -algorithm RSA @@ -290,14 +211,11 @@ mBdkD5r+ixWF174naw53L8U9wF8kiK7pIE1N9TR4USEeovLwX6Ni/2MMDZedOfof 0uxzo5Y= -----END ENCRYPTED PRIVATE KEY----- ` - crypto.keys.import(pem, 'mypassword', (err, key) => { - expect(err).to.not.exist() - expect(key).to.exist() - done() - }) + const key = await crypto.keys.import(pem, 'mypassword') + expect(key).to.exist() }) - it('can read a private encrypted key (v2 aes-128-cbc)', (done) => { + it('can read a private encrypted key (v2 aes-128-cbc)', async () => { /* * Generated with * openssl genpkey -algorithm RSA @@ -325,14 +243,11 @@ ePGbz+UtSb9xczvqpRCOiFLh2MG1dUgWuHazjOtUcVWvilKnkjCMzZ9s1qG0sUDj nPyn -----END ENCRYPTED PRIVATE KEY----- ` - crypto.keys.import(pem, 'mypassword', (err, key) => { - expect(err).to.not.exist() - expect(key).to.exist() - done() - }) + const key = await crypto.keys.import(pem, 'mypassword') + expect(key).to.exist() }) - it('can read a private encrypted key (v2 aes-256-cbc)', (done) => { + it('can read a private encrypted key (v2 aes-256-cbc)', async () => { /* * Generated with * openssl genpkey -algorithm RSA @@ -360,14 +275,11 @@ mBUuWAZMpz7njBi7h+JDfmSW/GAaMwrVFC2gef5375R0TejAh+COAjItyoeYEvv8 DQd8 -----END ENCRYPTED PRIVATE KEY----- ` - crypto.keys.import(pem, 'mypassword', (err, key) => { - expect(err).to.not.exist() - expect(key).to.exist() - done() - }) + const key = await crypto.keys.import(pem, 'mypassword') + expect(key).to.exist() }) - it('can read a private encrypted key (v2 des)', (done) => { + it('can read a private encrypted key (v2 des)', async () => { /* * Generated with * openssl genpkey -algorithm RSA @@ -394,14 +306,11 @@ A+qCKbprxyL8SKI5vug2hE+mfC1leXVRtUYm1DnE+oet99bFd0fN20NwTw0rOeRg EBqQkwAUXR1tNekF8CWLOrfC/wbLRxVRkayb8bQUfdgukLpz0bgw -----END ENCRYPTED PRIVATE KEY----- ` - crypto.keys.import(pem, 'mypassword', (err, key) => { - expect(err).to.not.exist() - expect(key).to.exist() - done() - }) + const key = await crypto.keys.import(pem, 'mypassword') + expect(key).to.exist() }) - it('can read a private encrypted key (v2 des3)', (done) => { + it('can read a private encrypted key (v2 des3)', async () => { /* * Generated with * openssl genpkey -algorithm RSA @@ -428,11 +337,8 @@ NT2TO3kSzXpQ5M2VjOoHPm2fqxD/js+ThDB3QLi4+C7HqakfiTY1lYzXl9/vayt6 DUD29r9pYL9ErB9tYko2rat54EY7k7Ts6S5jf+8G7Zz234We1APhvqaG -----END ENCRYPTED PRIVATE KEY----- ` - crypto.keys.import(pem, 'mypassword', (err, key) => { - expect(err).to.not.exist() - expect(key).to.exist() - done() - }) + const key = await crypto.keys.import(pem, 'mypassword') + expect(key).to.exist() }) }) }) diff --git a/test/keys/secp256k1.spec.js b/test/keys/secp256k1.spec.js index cbb4a6d3..60af5d90 100644 --- a/test/keys/secp256k1.spec.js +++ b/test/keys/secp256k1.spec.js @@ -18,12 +18,12 @@ const mockPrivateKey = { } const mockSecp256k1Module = { - generateKeyPair (bits, callback) { - callback(null, mockPrivateKey) + generateKeyPair (bits) { + return mockPrivateKey }, - unmarshalSecp256k1PrivateKey (buf, callback) { - callback(null, mockPrivateKey) + unmarshalSecp256k1PrivateKey (buf) { + return mockPrivateKey }, unmarshalSecp256k1PublicKey (buf) { @@ -34,20 +34,22 @@ const mockSecp256k1Module = { describe('without libp2p-crypto-secp256k1 module present', () => { crypto.keys.supportedKeys.secp256k1 = undefined - it('fails to generate a secp256k1 key', (done) => { - crypto.keys.generateKeyPair('secp256k1', 256, (err, key) => { - expect(err).to.exist() - expect(key).to.not.exist() - done() - }) + it('fails to generate a secp256k1 key', async () => { + try { + await crypto.keys.generateKeyPair('secp256k1', 256) + } catch (err) { + return // expected + } + throw new Error('Expected error to be thrown') }) - it('fails to unmarshal a secp256k1 private key', (done) => { - crypto.keys.unmarshalPrivateKey(fixtures.pbmPrivateKey, (err, key) => { - expect(err).to.exist() - expect(key).to.not.exist() - done() - }) + it('fails to unmarshal a secp256k1 private key', async () => { + try { + await crypto.keys.unmarshalPrivateKey(fixtures.pbmPrivateKey) + } catch (err) { + return // expected + } + throw new Error('Expected error to be thrown') }) it('fails to unmarshal a secp256k1 public key', () => { @@ -60,40 +62,31 @@ describe('without libp2p-crypto-secp256k1 module present', () => { describe('with libp2p-crypto-secp256k1 module present', () => { let key - before((done) => { + before(async () => { crypto.keys.supportedKeys.secp256k1 = mockSecp256k1Module - crypto.keys.generateKeyPair('secp256k1', 256, (err, _key) => { - if (err) return done(err) - key = _key - done() - }) + key = await crypto.keys.generateKeyPair('secp256k1', 256) }) - after((done) => { + after(() => { delete crypto.keys.secp256k1 - done() }) - it('generates a valid key', (done) => { + it('generates a valid key', () => { expect(key).to.exist() - done() }) - it('protobuf encoding', (done) => { + it('protobuf encoding', async () => { const keyMarshal = crypto.keys.marshalPrivateKey(key) - crypto.keys.unmarshalPrivateKey(keyMarshal, (err, key2) => { - if (err) return done(err) - const keyMarshal2 = crypto.keys.marshalPrivateKey(key2) + const key2 = await crypto.keys.unmarshalPrivateKey(keyMarshal) + const keyMarshal2 = crypto.keys.marshalPrivateKey(key2) - expect(keyMarshal).to.eql(keyMarshal2) + expect(keyMarshal).to.eql(keyMarshal2) - const pk = key.public - const pkMarshal = crypto.keys.marshalPublicKey(pk) - const pk2 = crypto.keys.unmarshalPublicKey(pkMarshal) - const pkMarshal2 = crypto.keys.marshalPublicKey(pk2) + const pk = key.public + const pkMarshal = crypto.keys.marshalPublicKey(pk) + const pk2 = crypto.keys.unmarshalPublicKey(pkMarshal) + const pkMarshal2 = crypto.keys.marshalPublicKey(pk2) - expect(pkMarshal).to.eql(pkMarshal2) - done() - }) + expect(pkMarshal).to.eql(pkMarshal2) }) })