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

Commit 08c5df5

Browse files
feat: use webcrypto in favor of node-forge
BREAKING CHANGE: generateKeyPair is now async
1 parent 73a5258 commit 08c5df5

32 files changed

+2738
-344
lines changed

.aegir.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ module.exports = {
88
alias: {
99
'node-forge': path.resolve(__dirname, 'vendor/forge.bundle.js')
1010
}
11+
},
12+
externals: {
13+
ursa: '{}'
1114
}
1215
}
1316
}

README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ needed for libp2p. This is based on this [go implementation](https://github.com/
2121
- [Usage](#usage)
2222
- [Example](#example)
2323
- [API](#api)
24-
- [`generateKeyPair(type, bits)`](#generatekeypairtype-bits)
24+
- [`generateKeyPair(type, bits, cb)`](#generatekeypairtype-bits-cb)
2525
- [`generateEphemeralKeyPair(curve)`](#generateephemeralkeypaircurve)
2626
- [`keyStretcher(cipherType, hashType, secret)`](#keystretcherciphertype-hashtype-secret)
2727
- [`marshalPublicKey(key[, type])`](#marshalpublickeykey-type)
@@ -44,15 +44,17 @@ npm install --save libp2p-crypto
4444
```js
4545
const crypto = require('libp2p-crypto')
4646

47-
var keyPair = crypto.generateKeyPair('RSA', 2048)
47+
crypto.generateKeyPair('RSA', 2048, (err, key) => {
48+
})
4849
```
4950

5051
## API
5152

52-
### `generateKeyPair(type, bits)`
53+
### `generateKeyPair(type, bits, cb)`
5354

5455
- `type: String`, only `'RSA'` is currently supported
5556
- `bits: Number`
57+
- `cb: Function`
5658

5759
Generates a keypair of the given type and bitsize.
5860

benchmarks/ephemeral-keys.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
'use strict'
2+
3+
const Benchmark = require('benchmark')
4+
const crypto = require('../src')
5+
6+
const suite = new Benchmark.Suite('ephemeral-keys')
7+
8+
const secrets = []
9+
const curves = ['P-256', 'P-384', 'P-521']
10+
11+
curves.forEach((curve) => {
12+
suite.add(`ephemeral key with secrect ${curve}`, (d) => {
13+
crypto.generateEphemeralKeyPair('P-256', (err, res) => {
14+
if (err) throw err
15+
res.genSharedKey(res.key, (err, secret) => {
16+
if (err) throw err
17+
secrets.push(secret)
18+
19+
d.resolve()
20+
})
21+
})
22+
}, {
23+
defer: true
24+
})
25+
})
26+
27+
suite
28+
.on('cycle', (event) => {
29+
console.log(String(event.target))
30+
})
31+
.run({
32+
'async': true
33+
})

benchmarks/key-stretcher.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
'use strict'
2+
3+
const Benchmark = require('benchmark')
4+
const crypto = require('../src')
5+
6+
const suite = new Benchmark.Suite('key-stretcher')
7+
8+
const keys = []
9+
10+
const ciphers = ['AES-128', 'AES-256', 'Blowfish']
11+
const hashes = ['SHA1', 'SHA256', 'SHA512']
12+
13+
crypto.generateEphemeralKeyPair('P-256', (err, res) => {
14+
if (err) throw err
15+
16+
res.genSharedKey(res.key, (err, secret) => {
17+
if (err) throw err
18+
ciphers.forEach((cipher) => {
19+
hashes.forEach((hash) => {
20+
suite.add(`keyStretcher ${cipher} ${hash}`, (d) => {
21+
crypto.keyStretcher(cipher, hash, secret, (err, k) => {
22+
if (err) {
23+
throw err
24+
}
25+
26+
keys.push(k)
27+
d.resolve()
28+
})
29+
}, {
30+
defer: true
31+
})
32+
})
33+
})
34+
35+
suite
36+
.on('cycle', (event) => {
37+
console.log(String(event.target))
38+
})
39+
.run({
40+
'async': true
41+
})
42+
})
43+
})

benchmarks/rsa.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
'use strict'
2+
3+
const Benchmark = require('benchmark')
4+
const crypto = require('../src')
5+
6+
const suite = new Benchmark.Suite('rsa')
7+
8+
const keys = []
9+
10+
const bits = [1024, 2048, 4096]
11+
12+
bits.forEach((bit) => {
13+
suite.add(`generateKeyPair ${bit}bits`, (d) => {
14+
crypto.generateKeyPair('RSA', bit, (err, key) => {
15+
if (err) throw err
16+
keys.push(key)
17+
d.resolve()
18+
})
19+
}, {
20+
defer: true
21+
})
22+
})
23+
24+
suite.add('sign and verify', (d) => {
25+
const key = keys[0]
26+
const text = key.genSecret()
27+
28+
key.sign(text, (err, sig) => {
29+
if (err) throw err
30+
31+
key.public.verify(text, sig, (err, res) => {
32+
if (err) throw err
33+
if (res !== true) throw new Error('failed to verify')
34+
d.resolve()
35+
})
36+
})
37+
}, {
38+
defer: true
39+
})
40+
41+
suite
42+
.on('cycle', (event) => {
43+
console.log(String(event.target))
44+
})
45+
.run({
46+
'async': true
47+
})

package.json

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,26 @@
22
"name": "libp2p-crypto",
33
"version": "0.6.1",
44
"description": "Crypto primitives for libp2p",
5-
"main": "lib/index.js",
5+
"main": "src/index.js",
66
"jsnext:main": "src/index.js",
7+
"browser": {
8+
"node-webcrypto-ossl": false,
9+
"./src/crypto/webcrypto.js": "./src/crypto/webcrypto-browser.js",
10+
"./lib/crypto/webcrypto.js": "./lib/crypto/webcrypto-browser.js",
11+
"./src/crypto/hmac.js": "./src/crypto/hmac-browser.js",
12+
"./lib/crypto/hmac.js": "./lib/crypto/hmac-browser.js",
13+
"./src/crypto/aes.js": "./src/crypto/aes-browser.js",
14+
"./lib/crypto/aes.js": "./lib/crypto/aes-browser.js"
15+
},
716
"scripts": {
817
"lint": "aegir-lint",
918
"build": "aegir-build",
10-
"test": "aegir-test",
19+
"test": "PHANTOM=off aegir-test",
1120
"test:node": "aegir-test --env node",
12-
"test:browser": "aegir-test --env browser",
13-
"release": "aegir-release",
14-
"release-minor": "aegir-release --type minor",
15-
"release-major": "aegir-release --type major",
21+
"test:browser": "PHANTOM=off aegir-test --env browser",
22+
"release": "PHANTOM=off aegir-release",
23+
"release-minor": "PHANTOM=off aegir-release --type minor",
24+
"release-major": "PHANTOM=off aegir-release --type major",
1625
"coverage": "aegir-coverage",
1726
"coverage-publish": "aegir-coverage publish"
1827
},
@@ -25,13 +34,18 @@
2534
"author": "Friedel Ziegelmayer <[email protected]>",
2635
"license": "MIT",
2736
"dependencies": {
28-
"elliptic": "^6.3.2",
37+
"asn1.js": "^4.8.1",
38+
"async": "^2.0.1",
39+
"bn.js": "^4.11.6",
2940
"multihashing": "^0.2.1",
30-
"node-forge": "^0.6.39",
31-
"protocol-buffers": "^3.1.6"
41+
"node-webcrypto-ossl": "^1.0.7",
42+
"nodeify": "^1.0.0",
43+
"protocol-buffers": "^3.1.6",
44+
"webcrypto-shim": "^0.1.1"
3245
},
3346
"devDependencies": {
3447
"aegir": "^8.0.0",
48+
"benchmark": "^2.1.1",
3549
"chai": "^3.5.0",
3650
"pre-commit": "^1.1.3"
3751
},
@@ -56,4 +70,4 @@
5670
"Richard Littauer <[email protected]>",
5771
"greenkeeperio-bot <[email protected]>"
5872
]
59-
}
73+
}

src/crypto.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
'use strict'
2+
3+
exports.webcrypto = require('./crypto/webcrypto')()
4+
exports.hmac = require('./crypto/hmac')
5+
exports.ecdh = require('./crypto/ecdh')
6+
exports.aes = require('./crypto/aes')
7+
exports.rsa = require('./crypto/rsa')

src/crypto.proto

Lines changed: 0 additions & 13 deletions
This file was deleted.

src/crypto.proto.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
'use strict'
2+
3+
module.exports = new Buffer(`
4+
enum KeyType {
5+
RSA = 0;
6+
}
7+
8+
message PublicKey {
9+
required KeyType Type = 1;
10+
required bytes Data = 2;
11+
}
12+
13+
message PrivateKey {
14+
required KeyType Type = 1;
15+
required bytes Data = 2;
16+
}
17+
`)

src/crypto/aes-browser.js

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
'use strict'
2+
3+
const nodeify = require('nodeify')
4+
5+
const crypto = require('./webcrypto')()
6+
7+
exports.create = function (key, iv, callback) {
8+
nodeify(crypto.subtle.importKey(
9+
'raw',
10+
key,
11+
{
12+
name: 'AES-CTR'
13+
},
14+
false,
15+
['encrypt', 'decrypt']
16+
).then((key) => {
17+
const counter = copy(iv)
18+
19+
return {
20+
encrypt (data, cb) {
21+
nodeify(crypto.subtle.encrypt(
22+
{
23+
name: 'AES-CTR',
24+
counter: counter,
25+
length: 128
26+
},
27+
key,
28+
data
29+
).then((raw) => Buffer.from(raw)), cb)
30+
},
31+
32+
decrypt (data, cb) {
33+
nodeify(crypto.subtle.decrypt(
34+
{
35+
name: 'AES-CTR',
36+
counter: counter,
37+
length: 128
38+
},
39+
key,
40+
data
41+
).then((raw) => Buffer.from(raw)), cb)
42+
}
43+
}
44+
}), callback)
45+
}
46+
47+
function copy (buf) {
48+
const fresh = new Buffer(buf.length)
49+
buf.copy(fresh)
50+
51+
return fresh
52+
}

src/crypto/aes.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
'use strict'
2+
3+
const crypto = require('crypto')
4+
5+
const ciphers = {
6+
16: 'aes-128-ctr',
7+
32: 'aes-256-ctr'
8+
}
9+
10+
exports.create = function (key, iv, callback) {
11+
const name = ciphers[key.length]
12+
if (!name) {
13+
return callback(new Error('Invalid key length'))
14+
}
15+
16+
const cipher = crypto.createCipheriv(name, key, iv)
17+
const decipher = crypto.createDecipheriv(name, key, iv)
18+
19+
const res = {
20+
encrypt (data, cb) {
21+
cb(null, cipher.update(data))
22+
},
23+
24+
decrypt (data, cb) {
25+
cb(null, decipher.update(data))
26+
}
27+
}
28+
29+
callback(null, res)
30+
}

0 commit comments

Comments
 (0)