Skip to content

Commit

Permalink
feat: use async/await
Browse files Browse the repository at this point in the history
This PR changes this module to remove callbacks and use async/await. The API is unchanged aside from the obvious removal of the `callback` parameter.

depends on `multihashing-async` PR TODO

refs ipfs/js-ipfs#1670

License: MIT
Signed-off-by: Alan Shaw <alan.shaw@protocol.ai>
  • Loading branch information
alanshaw committed Oct 28, 2018
1 parent 1602c44 commit b13bf71
Show file tree
Hide file tree
Showing 7 changed files with 212 additions and 304 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,5 @@ build
node_modules

dist

package-lock.json
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ language: node_js

matrix:
include:
- node_js: 6
env: CXX=g++-4.8
- node_js: 8
env: CXX=g++-4.8
- node_js: 10
env: CXX=g++-4.8
# - node_js: stable
# env: CXX=g++-4.8

Expand Down
60 changes: 31 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@

> Support for secp256k1 keys in js-libp2p-crypto
This repo contains a [js-libp2p-crypto](https://github.com/libp2p/js-libp2p-crypto)-compatible
implementation of cryptographic signature generation and verification using the
[secp256k1 elliptic curve](https://en.bitcoin.it/wiki/Secp256k1) popularized by Bitcoin and other
This repo contains a [js-libp2p-crypto](https://github.com/libp2p/js-libp2p-crypto)-compatible
implementation of cryptographic signature generation and verification using the
[secp256k1 elliptic curve](https://en.bitcoin.it/wiki/Secp256k1) popularized by Bitcoin and other
crypto currencies.

## Lead Captain
Expand All @@ -25,14 +25,14 @@ crypto currencies.
- [Usage](#usage)
- [Example](#example)
- [API](#api)
- [`generateKeyPair([bits,] callback)`](#generatekeypairbits-callback)
- [`generateKeyPair([bits])`](#generatekeypairbits)
- [`unmarshalSecp256k1PublicKey(bytes)`](#unmarshalsecp256k1publickeybytes)
- [`unmarshalSecp256k1PrivateKey(bytes, callback)`](#unmarshalsecp256k1privatekeybytes-callback)
- [`unmarshalSecp256k1PrivateKey(bytes)`](#unmarshalsecp256k1privatekeybytes)
- [`Secp256k1PublicKey`](#secp256k1publickey)
- [`.verify(data, sig, callback)`](#verifydata-sig-callback)
- [`.verify(data, sig)`](#verifydata-sig)
- [`Secp256k1PrivateKey`](#secp256k1privatekey)
- [`.public`](#public)
- [`.sign(data, callback)`](#signdata-callback)
- [`.sign(data)`](#signdata)
- [Contribute](#contribute)
- [License](#license)

Expand All @@ -54,63 +54,65 @@ instances of the `Secp256k1PublicKey` or `Secp256k1PrivateKey` classes provided

```js
const crypto = require('libp2p-crypto')

const msg = Buffer.from('Hello World')

crypto.generateKeyPair('secp256k1', 256, (err, key) => {
// assuming no error, key will be an instance of Secp256k1PrivateKey
// the public key is available as key.public
key.sign(msg, (err, sig) => {
key.public.verify(msg, sig, (err, valid) => {
assert(valid, 'Something went horribly wrong')
})
})
})
const key = await crypto.generateKeyPair('secp256k1', 256)
// assuming no error, key will be an instance of Secp256k1PrivateKey
// the public key is available as key.public
const sig = await key.sign(msg)

const valid = await key.public.verify(msg, sig)
assert(valid, 'Something went horribly wrong')
```

## API

The functions below are the public API of this module.
For usage within libp2p-crypto, see the [libp2p-crypto API documentation](https://github.com/libp2p/js-libp2p-crypto#api).
For usage within `libp2p-crypto`, see the [`libp2p-crypto` API documentation](https://github.com/libp2p/js-libp2p-crypto#api).

### `generateKeyPair([bits, ] callback)`
### `generateKeyPair([bits])`
- `bits: Number` - Optional, included for compatibility with js-libp2p-crypto. Ignored if present; private keys will always be 256 bits.
- `callback: Function`

Returns `Promise<Secp256k1PrivateKey>`

### `unmarshalSecp256k1PublicKey(bytes)`
- `bytes: Buffer`

Converts a serialized secp256k1 public key into an instance of `Secp256k1PublicKey` and returns it

### `unmarshalSecp256k1PrivateKey(bytes, callback)`
### `unmarshalSecp256k1PrivateKey(bytes)`
- `bytes: Buffer`
- `callback: Function`

Converts a serialized secp256k1 private key into an instance of `Secp256k1PrivateKey`, passing it to `callback` on success
Returns `Promise<Secp256k1PrivateKey>`

Converts a serialized secp256k1 private key into an instance of `Secp256k1PrivateKey`.

### `Secp256k1PublicKey`

#### `.verify(data, sig, callback)`
#### `.verify(data, sig)`
- `data: Buffer`
- `sig: Buffer`
- `callback: Function`

Calculates the SHA-256 hash of `data`, and verifies the DER-encoded signature in `sig`, passing the result to `callback`
Returns `Promise<Boolean>`

Calculates the SHA-256 hash of `data`, and verifies the DER-encoded signature in `sig`.

### `Secp256k1PrivateKey`

#### `.public`

Accessor for the `Secp256k1PublicKey` associated with this private key.

#### `.sign(data, callback)`
#### `.sign(data)`
- `data: Buffer`

Calculates the SHA-256 hash of `data` and signs it, passing the DER-encoded signature to `callback`
Returns `Promise<Buffer>`

Calculates the SHA-256 hash of `data` and signs it, resolves with the DER-encoded signature.

## Contribute

Feel free to join in. All welcome. Open an [issue](https://github.com/libp2p/js-libp2p-crypto/issues)!
Feel free to join in. All welcome. Open an [issue](https://github.com/libp2p/js-libp2p-crypto-secp256k1/issues)!

This repository falls under the IPFS [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md).

Expand Down
29 changes: 13 additions & 16 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@
"secp256k1": "secp256k1/js"
},
"scripts": {
"lint": "aegir-lint",
"build": "aegir-build",
"lint": "aegir lint",
"build": "aegir build",
"test": "npm run test:node && npm run test:browser",
"test:node": "aegir-test --env node",
"test:browser": "aegir-test --env browser",
"release": "aegir-release",
"release-minor": "aegir-release --type minor",
"release-major": "aegir-release --type major",
"coverage": "aegir-coverage",
"coverage-publish": "aegir-coverage publish"
"test:node": "aegir test -t node",
"test:browser": "aegir test -t browser",
"release": "aegir release",
"release-minor": "aegir release --type minor",
"release-major": "aegir release --type major",
"coverage": "aegir coverage",
"coverage-publish": "aegir coverage publish"
},
"keywords": [
"IPFS",
Expand All @@ -27,18 +27,15 @@
],
"license": "MIT",
"dependencies": {
"async": "^2.6.0",
"multihashing-async": "~0.4.8",
"nodeify": "^1.0.1",
"safe-buffer": "^5.1.2",
"multihashing-async": "^0.5.1",
"secp256k1": "^3.5.0"
},
"devDependencies": {
"aegir": "^13.1.0",
"aegir": "^17.0.0",
"benchmark": "^2.1.4",
"libp2p-crypto": "~0.13.0",
"chai": "^4.1.2",
"dirty-chai": "^2.0.1"
"dirty-chai": "^2.0.1",
"libp2p-crypto": "^0.14.0"
},
"engines": {
"node": ">=6.0.0",
Expand Down
63 changes: 27 additions & 36 deletions src/crypto.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,49 +2,40 @@

const secp256k1 = require('secp256k1')
const multihashing = require('multihashing-async')
const setImmediate = require('async/setImmediate')

const HASH_ALGORITHM = 'sha2-256'

module.exports = (randomBytes) => {
const privateKeyLength = 32

function generateKey (callback) {
const done = (err, res) => setImmediate(() => callback(err, res))

async function generateKey () {
let privateKey
do {
privateKey = randomBytes(32)
} while (!secp256k1.privateKeyVerify(privateKey))

done(null, privateKey)
return privateKey
}

function hashAndSign (key, msg, callback) {
const done = (err, res) => setImmediate(() => callback(err, res))

multihashing.digest(msg, HASH_ALGORITHM, (err, digest) => {
if (err) { return done(err) }

try {
const sig = secp256k1.sign(digest, key)
const sigDER = secp256k1.signatureExport(sig.signature)
return done(null, sigDER)
} catch (err) { done(err) }
async function hashAndSign (key, msg) {
const digest = await new Promise((resolve, reject) => {
multihashing.digest(msg, HASH_ALGORITHM, (err, digest) => {
if (err) return reject(err)
resolve(digest)
})
})
const sig = secp256k1.sign(digest, key)
return secp256k1.signatureExport(sig.signature)
}

function hashAndVerify (key, sig, msg, callback) {
const done = (err, res) => setImmediate(() => callback(err, res))

multihashing.digest(msg, HASH_ALGORITHM, (err, digest) => {
if (err) { return done(err) }
try {
sig = secp256k1.signatureImport(sig)
const valid = secp256k1.verify(digest, sig, key)
return done(null, valid)
} catch (err) { done(err) }
async function hashAndVerify (key, sig, msg) {
const digest = await new Promise((resolve, reject) => {
multihashing.digest(msg, HASH_ALGORITHM, (err, digest) => {
if (err) return reject(err)
resolve(digest)
})
})
sig = secp256k1.signatureImport(sig)
return secp256k1.verify(digest, sig, key)
}

function compressPublicKey (key) {
Expand Down Expand Up @@ -76,14 +67,14 @@ module.exports = (randomBytes) => {
}

return {
generateKey: generateKey,
privateKeyLength: privateKeyLength,
hashAndSign: hashAndSign,
hashAndVerify: hashAndVerify,
compressPublicKey: compressPublicKey,
decompressPublicKey: decompressPublicKey,
validatePrivateKey: validatePrivateKey,
validatePublicKey: validatePublicKey,
computePublicKey: computePublicKey
generateKey,
privateKeyLength,
hashAndSign,
hashAndVerify,
compressPublicKey,
decompressPublicKey,
validatePrivateKey,
validatePublicKey,
computePublicKey
}
}
60 changes: 23 additions & 37 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,8 @@ module.exports = (keysProtobuf, randomBytes, crypto) => {
this._key = key
}

verify (data, sig, callback) {
ensure(callback)
crypto.hashAndVerify(this._key, sig, data, callback)
async verify (data, sig) {
return crypto.hashAndVerify(this._key, sig, data)
}

marshal () {
Expand All @@ -31,9 +30,13 @@ module.exports = (keysProtobuf, randomBytes, crypto) => {
return this.bytes.equals(key.bytes)
}

hash (callback) {
ensure(callback)
multihashing(this.bytes, 'sha2-256', callback)
async hash () {
return new Promise((resolve, reject) => {
multihashing(this.bytes, 'sha2-256', (err, res) => {
if (err) return reject(err)
resolve(res)
})
})
}
}

Expand All @@ -45,9 +48,8 @@ module.exports = (keysProtobuf, randomBytes, crypto) => {
crypto.validatePublicKey(this._publicKey)
}

sign (message, callback) {
ensure(callback)
crypto.hashAndSign(this._key, message, callback)
async sign (message) {
return crypto.hashAndSign(this._key, message)
}

get public () {
Expand All @@ -69,43 +71,27 @@ module.exports = (keysProtobuf, randomBytes, crypto) => {
return this.bytes.equals(key.bytes)
}

hash (callback) {
ensure(callback)
multihashing(this.bytes, 'sha2-256', callback)
async hash () {
return new Promise((resolve, reject) => {
multihashing(this.bytes, 'sha2-256', (err, res) => {
if (err) return reject(err)
resolve(res)
})
})
}
}

function unmarshalSecp256k1PrivateKey (bytes, callback) {
callback(null, new Secp256k1PrivateKey(bytes), null)
async function unmarshalSecp256k1PrivateKey (bytes) {
return new Secp256k1PrivateKey(bytes)
}

function unmarshalSecp256k1PublicKey (bytes) {
return new Secp256k1PublicKey(bytes)
}

function generateKeyPair (_bits, callback) {
if (callback === undefined && typeof _bits === 'function') {
callback = _bits
}

ensure(callback)

crypto.generateKey((err, privateKeyBytes) => {
if (err) { return callback(err) }

let privkey
try {
privkey = new Secp256k1PrivateKey(privateKeyBytes)
} catch (err) { return callback(err) }

callback(null, privkey)
})
}

function ensure (callback) {
if (typeof callback !== 'function') {
throw new Error('callback is required')
}
async function generateKeyPair () {
const privateKeyBytes = await crypto.generateKey()
return new Secp256k1PrivateKey(privateKeyBytes)
}

return {
Expand Down
Loading

0 comments on commit b13bf71

Please sign in to comment.