Skip to content
This repository has been archived by the owner on Jul 21, 2023. It is now read-only.

Commit

Permalink
feat: refactor to use async/await
Browse files Browse the repository at this point in the history
BREAKING CHANGE: API refactored to use async/await

feat: WIP use async await
fix: passing tests
chore: update travis node.js versions
fix: skip ursa optional tests on windows
fix: benchmarks
docs: update docs
fix: remove broken and intested private key decrypt
chore: update deps
  • Loading branch information
alanshaw authored and achingbrain committed Jul 10, 2019
1 parent 5cd0e8c commit d2b8d7a
Show file tree
Hide file tree
Showing 32 changed files with 695 additions and 1,268 deletions.
183 changes: 94 additions & 89 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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<Function>, encrypt<Function>}>`

##### `decrypt(data)`

- `data: Buffer`
- `callback: Function`

##### `encrypt(data, callback)`
Returns `Promise<Buffer>`

##### `encrypt(data)`

- `data: Buffer`
- `callback: Function`

Returns `Promise<Buffer>`

```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: <Buffer 42 f1 67 d9 2e 42 d0 32 9e b1 f8 3c>
encryptedMessage = encryptedBuffer
}
})
}
})
const cipher = await crypto.aes.create(key128, IV)
const encryptedBuffer = await cipher.encrypt(Buffer.from(decryptedMessage))
console.log(encryptedBuffer)
// prints: <Buffer 42 f1 67 d9 2e 42 d0 32 9e b1 f8 3c>

// Decrypting
await crypto.aes.create(key128, IV, (err, cipher) => {
if (!err) {
cipher.decrypt(encryptedMessage, (err, decryptedBuffer) => {
if (!err) {
console.log(decryptedBuffer)
// prints: <Buffer 42 f1 67 d9 2e 42 d0 32 9e b1 f8 3c>

console.log(decryptedBuffer.toString('utf-8'))
// prints: Hello, world!
}
})
}
})
const decipher = await crypto.aes.create(key128, IV)
const decryptedBuffer = await cipher.decrypt(encryptedBuffer)

console.log(decryptedBuffer)
// prints: <Buffer 42 f1 67 d9 2e 42 d0 32 9e b1 f8 3c>

console.log(decryptedBuffer.toString('utf-8'))
// prints: Hello, world!
}
main()

main()
```

### `crypto.hmac`

Exposes an interface to the Keyed-Hash Message Authentication Code (HMAC) as defined in U.S. Federal Information Processing Standards Publication 198. An HMAC is a cryptographic hash that uses a key to sign a message. The receiver verifies the hash by recomputing it using the same key.

#### `crypto.hmac.create(hash, secret, callback)`
#### `crypto.hmac.create(hash, secret)`

- `hash: String`
- `secret: Buffer`
- `callback: Function`

##### `digest(data, callback)`
Returns `Promise<{digest<Function>}>`

##### `digest(data)`

- `data: Buffer`
- `callback: Function`

Returns `Promise<Buffer>`

Example:

```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<privateKey<Buffer>, publicKey<Buffer>>`

Generates a keypair of the given type and bitsize.

### `crypto.keys.generateEphemeralKeyPair(curve, callback)`
### `crypto.keys.generateEphemeralKeyPair(curve)`

- `curve: String`, one of `'P-256'`, `'P-384'`, `'P-521'` is currently supported
- `callback: Function`

Returns `Promise`

Generates an ephemeral public key and returns a function that will compute the shared secret key.

Focuses only on ECDH now, but can be made more general in the future.

Calls back with an object of the form
Resolves to an object of the form:

```js
{
Expand All @@ -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
{
Expand All @@ -214,45 +209,55 @@ 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<RsaPrivateKey|Ed25519PrivateKey|Secp256k1PrivateKey>`

Converts a protobuf serialized private key into its representative object.

### `crypto.keys.import(pem, password, callback)`
### `crypto.keys.import(pem, password)`

- `pem: string`
- `password: string`
- `callback: Function`

Returns `Promise<RsaPrivateKey>`

Converts a PEM password protected private key into its representative object.

### `crypto.randomBytes(number)`

- `number: Number`

Returns `Buffer`

Generates a Buffer with length `number` populated by random bytes.

### `crypto.pbkdf2(password, salt, iterations, keySize, hash)`
Expand Down
15 changes: 5 additions & 10 deletions benchmarks/ephemeral-keys.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 })
})

Expand Down
22 changes: 8 additions & 14 deletions benchmarks/key-stretcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
'use strict'

const Benchmark = require('benchmark')
const async = require('async')

const crypto = require('../src')

Expand All @@ -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)
Expand All @@ -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 })
}
Loading

0 comments on commit d2b8d7a

Please sign in to comment.