Skip to content
This repository was archived by the owner on Jul 21, 2023. It is now read-only.
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 92c78f4

Browse files
author
Alan Shaw
committedJul 4, 2019
fix: crypto in insecure browser context
This PR adds `crypto-browserify` to the dependencies and replaces `crypto` with `crypto-browserify` when bundled in the browser. In files that require webcrypto we check to see if it's available. If it is not we require the Node.js implementation (which has `crypto` replaced with `crypto-browserify`) and if it is available then we use the webcrypto version (so we get fast crypto). Shipping `crypto-browserify` adds to the bundle size: Current gzipped size: 142,824 bytes New gzipped size: 214,499 bytes Difference: **+71,675 bytes** It's not an insignificant addition so we need to decide whether this is worth it. If not accepted, we need to add checks when libp2p-crypto methods are called and callback with an appropriate error message. JS IPFS will continue to have issues opened with confusion around this otherwise! See ipfs/js-ipfs#963 ipfs/js-ipfs#964 ipfs/js-ipfs#2153 resolves #105 License: MIT Signed-off-by: Alan Shaw <alan.shaw@protocol.ai>
1 parent 0ffe318 commit 92c78f4

15 files changed

+177
-225
lines changed
 

‎package.json

+4-8
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,8 @@
55
"main": "src/index.js",
66
"leadMaintainer": "Friedel Ziegelmayer <dignifiedquire@gmail.com>",
77
"browser": {
8-
"./src/hmac/index.js": "./src/hmac/index-browser.js",
9-
"./src/keys/ecdh.js": "./src/keys/ecdh-browser.js",
10-
"./src/aes/ciphers.js": "./src/aes/ciphers-browser.js",
11-
"./src/keys/rsa.js": "./src/keys/rsa-browser.js"
8+
"crypto": "crypto-browserify",
9+
"./src/keys/keypair.js": "./src/keys/keypair-browser.js"
1210
},
1311
"files": [
1412
"src",
@@ -35,25 +33,23 @@
3533
],
3634
"license": "MIT",
3735
"dependencies": {
38-
"asmcrypto.js": "^2.3.2",
3936
"asn1.js": "^5.0.1",
4037
"async": "^2.6.2",
41-
"bn.js": "^4.11.8",
42-
"browserify-aes": "^1.2.0",
4338
"bs58": "^4.0.1",
39+
"crypto-browserify": "^3.12.0",
4440
"iso-random-stream": "^1.1.0",
4541
"keypair": "^1.0.1",
4642
"libp2p-crypto-secp256k1": "~0.3.0",
4743
"multihashing-async": "~0.6.0",
4844
"node-forge": "~0.7.6",
4945
"pem-jwk": "^2.0.0",
5046
"protons": "^1.0.1",
51-
"rsa-pem-to-jwk": "^1.1.3",
5247
"tweetnacl": "^1.0.1",
5348
"ursa-optional": "~0.9.10"
5449
},
5550
"devDependencies": {
5651
"aegir": "^18.2.2",
52+
"bn.js": "^4.11.8",
5753
"benchmark": "^2.1.4",
5854
"bundlesize": "~0.17.1",
5955
"chai": "^4.2.0",

‎src/aes/ciphers-browser.js

-8
This file was deleted.

‎src/aes/index-browser.js

-55
This file was deleted.

‎src/hmac/index-crypto.js

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
'use strict'
2+
3+
const crypto = require('crypto')
4+
const lengths = require('./lengths')
5+
const nextTick = require('async/nextTick')
6+
7+
exports.create = function (hash, secret, callback) {
8+
const res = {
9+
digest (data, cb) {
10+
const hmac = crypto.createHmac(hash.toLowerCase(), secret)
11+
12+
hmac.update(data)
13+
14+
nextTick(() => {
15+
cb(null, hmac.digest())
16+
})
17+
},
18+
length: lengths[hash]
19+
}
20+
21+
callback(null, res)
22+
}
File renamed without changes.

‎src/hmac/index.js

+2-19
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,5 @@
11
'use strict'
22

3-
const crypto = require('crypto')
4-
const lengths = require('./lengths')
5-
const nextTick = require('async/nextTick')
3+
const webcrypto = require('../webcrypto')
64

7-
exports.create = function (hash, secret, callback) {
8-
const res = {
9-
digest (data, cb) {
10-
const hmac = crypto.createHmac(hash.toLowerCase(), secret)
11-
12-
hmac.update(data)
13-
14-
nextTick(() => {
15-
cb(null, hmac.digest())
16-
})
17-
},
18-
length: lengths[hash]
19-
}
20-
21-
callback(null, res)
22-
}
5+
module.exports = webcrypto ? require('./index-webcrypto') : require('./index-crypto')

‎src/keys/ecdh-crypto.js

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
'use strict'
2+
3+
const crypto = require('crypto')
4+
const nextTick = require('async/nextTick')
5+
6+
const curves = {
7+
'P-256': 'prime256v1',
8+
'P-384': 'secp384r1',
9+
'P-521': 'secp521r1'
10+
}
11+
12+
exports.generateEphmeralKeyPair = function (curve, callback) {
13+
if (!curves[curve]) {
14+
return callback(new Error(`Unkown curve: ${curve}`))
15+
}
16+
const ecdh = crypto.createECDH(curves[curve])
17+
ecdh.generateKeys()
18+
19+
nextTick(() => callback(null, {
20+
key: ecdh.getPublicKey(),
21+
genSharedKey (theirPub, forcePrivate, cb) {
22+
if (typeof forcePrivate === 'function') {
23+
cb = forcePrivate
24+
forcePrivate = null
25+
}
26+
27+
if (forcePrivate) {
28+
ecdh.setPrivateKey(forcePrivate.private)
29+
}
30+
31+
let secret
32+
try {
33+
secret = ecdh.computeSecret(theirPub)
34+
} catch (err) {
35+
return cb(err)
36+
}
37+
38+
nextTick(() => cb(null, secret))
39+
}
40+
}))
41+
}
File renamed without changes.

‎src/keys/ecdh.js

+2-38
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,5 @@
11
'use strict'
22

3-
const crypto = require('crypto')
4-
const nextTick = require('async/nextTick')
3+
const webcrypto = require('../webcrypto')
54

6-
const curves = {
7-
'P-256': 'prime256v1',
8-
'P-384': 'secp384r1',
9-
'P-521': 'secp521r1'
10-
}
11-
12-
exports.generateEphmeralKeyPair = function (curve, callback) {
13-
if (!curves[curve]) {
14-
return callback(new Error(`Unkown curve: ${curve}`))
15-
}
16-
const ecdh = crypto.createECDH(curves[curve])
17-
ecdh.generateKeys()
18-
19-
nextTick(() => callback(null, {
20-
key: ecdh.getPublicKey(),
21-
genSharedKey (theirPub, forcePrivate, cb) {
22-
if (typeof forcePrivate === 'function') {
23-
cb = forcePrivate
24-
forcePrivate = null
25-
}
26-
27-
if (forcePrivate) {
28-
ecdh.setPrivateKey(forcePrivate.private)
29-
}
30-
31-
let secret
32-
try {
33-
secret = ecdh.computeSecret(theirPub)
34-
} catch (err) {
35-
return cb(err)
36-
}
37-
38-
nextTick(() => cb(null, secret))
39-
}
40-
}))
41-
}
5+
module.exports = webcrypto ? require('./ecdh-webcrypto') : require('./ecdh-crypto')

‎src/keys/keypair-browser.js

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = require('keypair')

‎src/keys/keypair.js

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
let keypair
2+
3+
try {
4+
if (process.env.LP2P_FORCE_CRYPTO_LIB === 'keypair') {
5+
throw new Error('Force keypair usage')
6+
}
7+
8+
const ursa = require('ursa-optional') // throws if not compiled
9+
keypair = ({ bits }) => {
10+
const key = ursa.generatePrivateKey(bits)
11+
return {
12+
private: key.toPrivatePem(),
13+
public: key.toPublicPem()
14+
}
15+
}
16+
} catch (e) {
17+
if (process.env.LP2P_FORCE_CRYPTO_LIB === 'ursa') {
18+
throw e
19+
}
20+
21+
keypair = require('keypair')
22+
}
23+
24+
module.exports = keypair

‎src/keys/rsa-crypto.js

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
'use strict'
2+
3+
const crypto = require('crypto')
4+
const randomBytes = require('../random-bytes')
5+
const nextTick = require('async/nextTick')
6+
const keypair = require('./keypair')
7+
const pemToJwk = require('pem-jwk').pem2jwk
8+
const jwkToPem = require('pem-jwk').jwk2pem
9+
10+
exports.utils = require('./rsa-utils')
11+
12+
exports.generateKey = function (bits, callback) {
13+
nextTick(() => {
14+
let result
15+
try {
16+
const key = keypair({ bits: bits })
17+
result = {
18+
privateKey: pemToJwk(key.private),
19+
publicKey: pemToJwk(key.public)
20+
}
21+
} catch (err) {
22+
return callback(err)
23+
}
24+
25+
callback(null, result)
26+
})
27+
}
28+
29+
// Takes a jwk key
30+
exports.unmarshalPrivateKey = function (key, callback) {
31+
nextTick(() => {
32+
if (!key) {
33+
return callback(new Error('Key is invalid'))
34+
}
35+
callback(null, {
36+
privateKey: key,
37+
publicKey: {
38+
kty: key.kty,
39+
n: key.n,
40+
e: key.e
41+
}
42+
})
43+
})
44+
}
45+
46+
exports.getRandomValues = randomBytes
47+
48+
exports.hashAndSign = function (key, msg, callback) {
49+
nextTick(() => {
50+
let result
51+
try {
52+
const sign = crypto.createSign('RSA-SHA256')
53+
sign.update(msg)
54+
const pem = jwkToPem(key)
55+
result = sign.sign(pem)
56+
} catch (err) {
57+
return callback(new Error('Key or message is invalid!: ' + err.message))
58+
}
59+
60+
callback(null, result)
61+
})
62+
}
63+
64+
exports.hashAndVerify = function (key, sig, msg, callback) {
65+
nextTick(() => {
66+
let result
67+
try {
68+
const verify = crypto.createVerify('RSA-SHA256')
69+
verify.update(msg)
70+
const pem = jwkToPem(key)
71+
result = verify.verify(pem, sig)
72+
} catch (err) {
73+
return callback(new Error('Key or message is invalid!:' + err.message))
74+
}
75+
76+
callback(null, result)
77+
})
78+
}
File renamed without changes.

‎src/keys/rsa.js

+2-96
Original file line numberDiff line numberDiff line change
@@ -1,99 +1,5 @@
11
'use strict'
22

3-
const crypto = require('crypto')
4-
const randomBytes = require('../random-bytes')
5-
const nextTick = require('async/nextTick')
3+
const webcrypto = require('../webcrypto')
64

7-
let keypair
8-
try {
9-
if (process.env.LP2P_FORCE_CRYPTO_LIB === 'keypair') {
10-
throw new Error('Force keypair usage')
11-
}
12-
13-
const ursa = require('ursa-optional') // throws if not compiled
14-
keypair = ({ bits }) => {
15-
const key = ursa.generatePrivateKey(bits)
16-
return {
17-
private: key.toPrivatePem(),
18-
public: key.toPublicPem()
19-
}
20-
}
21-
} catch (e) {
22-
if (process.env.LP2P_FORCE_CRYPTO_LIB === 'ursa') {
23-
throw e
24-
}
25-
26-
keypair = require('keypair')
27-
}
28-
const pemToJwk = require('pem-jwk').pem2jwk
29-
const jwkToPem = require('pem-jwk').jwk2pem
30-
31-
exports.utils = require('./rsa-utils')
32-
33-
exports.generateKey = function (bits, callback) {
34-
nextTick(() => {
35-
let result
36-
try {
37-
const key = keypair({ bits: bits })
38-
result = {
39-
privateKey: pemToJwk(key.private),
40-
publicKey: pemToJwk(key.public)
41-
}
42-
} catch (err) {
43-
return callback(err)
44-
}
45-
46-
callback(null, result)
47-
})
48-
}
49-
50-
// Takes a jwk key
51-
exports.unmarshalPrivateKey = function (key, callback) {
52-
nextTick(() => {
53-
if (!key) {
54-
return callback(new Error('Key is invalid'))
55-
}
56-
callback(null, {
57-
privateKey: key,
58-
publicKey: {
59-
kty: key.kty,
60-
n: key.n,
61-
e: key.e
62-
}
63-
})
64-
})
65-
}
66-
67-
exports.getRandomValues = randomBytes
68-
69-
exports.hashAndSign = function (key, msg, callback) {
70-
nextTick(() => {
71-
let result
72-
try {
73-
const sign = crypto.createSign('RSA-SHA256')
74-
sign.update(msg)
75-
const pem = jwkToPem(key)
76-
result = sign.sign(pem)
77-
} catch (err) {
78-
return callback(new Error('Key or message is invalid!: ' + err.message))
79-
}
80-
81-
callback(null, result)
82-
})
83-
}
84-
85-
exports.hashAndVerify = function (key, sig, msg, callback) {
86-
nextTick(() => {
87-
let result
88-
try {
89-
const verify = crypto.createVerify('RSA-SHA256')
90-
verify.update(msg)
91-
const pem = jwkToPem(key)
92-
result = verify.verify(pem, sig)
93-
} catch (err) {
94-
return callback(new Error('Key or message is invalid!:' + err.message))
95-
}
96-
97-
callback(null, result)
98-
})
99-
}
5+
module.exports = webcrypto ? require('./rsa-webcrypto') : require('./rsa-crypto')

‎src/webcrypto.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22

33
'use strict'
44

5-
module.exports = self.crypto || self.msCrypto
5+
module.exports = typeof self === 'undefined' ? null : (self.crypto || self.msCrypto)

0 commit comments

Comments
 (0)
This repository has been archived.