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

feat: use async await #131

Merged
merged 1 commit into from
Jul 10, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
185 changes: 95 additions & 90 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.
- `type: String`, see [Supported Key Types](#supported-key-types) above. Defaults to 'rsa'.

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