Skip to content

Commit

Permalink
feat: use async await (libp2p#18)
Browse files Browse the repository at this point in the history
BREAKING CHANGE: Callback support has been dropped in favor of async/await.

* feat: use async/await

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.

refs ipfs/js-ipfs#1670

* fix: use latest multihashing-async as it is all promises now
  • Loading branch information
achingbrain authored and jacobheun committed Jul 10, 2019
1 parent fbd4238 commit 1974eb9
Show file tree
Hide file tree
Showing 7 changed files with 191 additions and 307 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,5 @@ node_modules

dist
package-lock.json
yarn.lock
yarn.lock
.vscode
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

language: node_js

cache: npm
Expand Down Expand Up @@ -41,4 +42,4 @@ jobs:
- npx aegir test -t browser -- --browsers FirefoxHeadless

notifications:
email: false
email: false
60 changes: 31 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,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 @@ -28,14 +28,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 @@ -57,63 +57,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
5 changes: 2 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,14 @@
],
"license": "MIT",
"dependencies": {
"async": "^2.6.2",
"bs58": "^4.0.1",
"multihashing-async": "~0.6.0",
"multihashing-async": "^0.7.0",
"nodeify": "^1.0.1",
"safe-buffer": "^5.1.2",
"secp256k1": "^3.6.2"
},
"devDependencies": {
"aegir": "^18.2.2",
"aegir": "^19.0.5",
"benchmark": "^2.1.4",
"chai": "^4.2.0",
"dirty-chai": "^2.0.1",
Expand Down
57 changes: 19 additions & 38 deletions src/crypto.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,49 +2,30 @@

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))

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 multihashing.digest(msg, HASH_ALGORITHM)
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 multihashing.digest(msg, HASH_ALGORITHM)
sig = secp256k1.signatureImport(sig)
return secp256k1.verify(digest, sig, key)
}

function compressPublicKey (key) {
Expand Down Expand Up @@ -76,14 +57,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
}
}
61 changes: 17 additions & 44 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,8 @@ module.exports = (keysProtobuf, randomBytes, crypto) => {
this._key = key
}

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

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

hash (callback) {
ensure(callback)
multihashing(this.bytes, 'sha2-256', callback)
hash () {
return multihashing(this.bytes, 'sha2-256')
}
}

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

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

get public () {
Expand All @@ -70,9 +67,8 @@ module.exports = (keysProtobuf, randomBytes, crypto) => {
return this.bytes.equals(key.bytes)
}

hash (callback) {
ensure(callback)
multihashing(this.bytes, 'sha2-256', callback)
hash () {
return multihashing(this.bytes, 'sha2-256')
}

/**
Expand All @@ -85,47 +81,24 @@ module.exports = (keysProtobuf, randomBytes, crypto) => {
* @param {function(Error, id)} callback
* @returns {undefined}
*/
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 unmarshalSecp256k1PrivateKey (bytes, callback) {
callback(null, new Secp256k1PrivateKey(bytes))
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 1974eb9

Please sign in to comment.