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

fix: crypto in insecure browser context #149

Closed
wants to merge 2 commits into from
Closed
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
12 changes: 4 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,8 @@
"main": "src/index.js",
"leadMaintainer": "Friedel Ziegelmayer <dignifiedquire@gmail.com>",
"browser": {
"./src/hmac/index.js": "./src/hmac/index-browser.js",
"./src/keys/ecdh.js": "./src/keys/ecdh-browser.js",
"./src/aes/ciphers.js": "./src/aes/ciphers-browser.js",
"./src/keys/rsa.js": "./src/keys/rsa-browser.js"
"crypto": "crypto-browserify",
"./src/keys/keypair.js": "./src/keys/keypair-browser.js"
},
"files": [
"src",
Expand All @@ -35,25 +33,23 @@
],
"license": "MIT",
"dependencies": {
"asmcrypto.js": "^2.3.2",
"asn1.js": "^5.0.1",
"async": "^2.6.2",
"bn.js": "^4.11.8",
"browserify-aes": "^1.2.0",
"bs58": "^4.0.1",
"crypto-browserify": "^3.12.0",
"iso-random-stream": "^1.1.0",
"keypair": "^1.0.1",
"libp2p-crypto-secp256k1": "~0.3.0",
"multihashing-async": "~0.6.0",
"node-forge": "~0.7.6",
"pem-jwk": "^2.0.0",
"protons": "^1.0.1",
"rsa-pem-to-jwk": "^1.1.3",
"tweetnacl": "^1.0.1",
"ursa-optional": "~0.9.10"
},
"devDependencies": {
"aegir": "^18.2.2",
"bn.js": "^4.11.8",
"benchmark": "^2.1.4",
"bundlesize": "~0.17.1",
"chai": "^4.2.0",
Expand Down
8 changes: 0 additions & 8 deletions src/aes/ciphers-browser.js

This file was deleted.

55 changes: 0 additions & 55 deletions src/aes/index-browser.js

This file was deleted.

22 changes: 22 additions & 0 deletions src/hmac/index-crypto.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
'use strict'

const crypto = require('crypto')
const lengths = require('./lengths')
const nextTick = require('async/nextTick')

exports.create = function (hash, secret, callback) {
const res = {
digest (data, cb) {
const hmac = crypto.createHmac(hash.toLowerCase(), secret)

hmac.update(data)

nextTick(() => {
cb(null, hmac.digest())
})
},
length: lengths[hash]
}

callback(null, res)
}
File renamed without changes.
21 changes: 2 additions & 19 deletions src/hmac/index.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,5 @@
'use strict'

const crypto = require('crypto')
const lengths = require('./lengths')
const nextTick = require('async/nextTick')
const webcrypto = require('../webcrypto')

exports.create = function (hash, secret, callback) {
const res = {
digest (data, cb) {
const hmac = crypto.createHmac(hash.toLowerCase(), secret)

hmac.update(data)

nextTick(() => {
cb(null, hmac.digest())
})
},
length: lengths[hash]
}

callback(null, res)
}
module.exports = webcrypto ? require('./index-webcrypto') : require('./index-crypto')
41 changes: 41 additions & 0 deletions src/keys/ecdh-crypto.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
'use strict'

const crypto = require('crypto')
const nextTick = require('async/nextTick')

const curves = {
'P-256': 'prime256v1',
'P-384': 'secp384r1',
'P-521': 'secp521r1'
}

exports.generateEphmeralKeyPair = function (curve, callback) {
if (!curves[curve]) {
return callback(new Error(`Unkown curve: ${curve}`))
}
const ecdh = crypto.createECDH(curves[curve])
ecdh.generateKeys()

nextTick(() => callback(null, {
key: ecdh.getPublicKey(),
genSharedKey (theirPub, forcePrivate, cb) {
if (typeof forcePrivate === 'function') {
cb = forcePrivate
forcePrivate = null
}

if (forcePrivate) {
ecdh.setPrivateKey(forcePrivate.private)
}

let secret
try {
secret = ecdh.computeSecret(theirPub)
} catch (err) {
return cb(err)
}

nextTick(() => cb(null, secret))
}
}))
}
File renamed without changes.
40 changes: 2 additions & 38 deletions src/keys/ecdh.js
Original file line number Diff line number Diff line change
@@ -1,41 +1,5 @@
'use strict'

const crypto = require('crypto')
const nextTick = require('async/nextTick')
const webcrypto = require('../webcrypto')

const curves = {
'P-256': 'prime256v1',
'P-384': 'secp384r1',
'P-521': 'secp521r1'
}

exports.generateEphmeralKeyPair = function (curve, callback) {
if (!curves[curve]) {
return callback(new Error(`Unkown curve: ${curve}`))
}
const ecdh = crypto.createECDH(curves[curve])
ecdh.generateKeys()

nextTick(() => callback(null, {
key: ecdh.getPublicKey(),
genSharedKey (theirPub, forcePrivate, cb) {
if (typeof forcePrivate === 'function') {
cb = forcePrivate
forcePrivate = null
}

if (forcePrivate) {
ecdh.setPrivateKey(forcePrivate.private)
}

let secret
try {
secret = ecdh.computeSecret(theirPub)
} catch (err) {
return cb(err)
}

nextTick(() => cb(null, secret))
}
}))
}
module.exports = webcrypto ? require('./ecdh-webcrypto') : require('./ecdh-crypto')
3 changes: 3 additions & 0 deletions src/keys/keypair-browser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
'use strict'

module.exports = require('keypair')
26 changes: 26 additions & 0 deletions src/keys/keypair.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
'use strict'

let keypair

try {
if (process.env.LP2P_FORCE_CRYPTO_LIB === 'keypair') {
throw new Error('Force keypair usage')
}

const ursa = require('ursa-optional') // throws if not compiled
keypair = ({ bits }) => {
const key = ursa.generatePrivateKey(bits)
return {
private: key.toPrivatePem(),
public: key.toPublicPem()
}
}
} catch (e) {
if (process.env.LP2P_FORCE_CRYPTO_LIB === 'ursa') {
throw e
}

keypair = require('keypair')
}

module.exports = keypair
78 changes: 78 additions & 0 deletions src/keys/rsa-crypto.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
'use strict'

const crypto = require('crypto')
const randomBytes = require('../random-bytes')
const nextTick = require('async/nextTick')
const keypair = require('./keypair')
const pemToJwk = require('pem-jwk').pem2jwk
const jwkToPem = require('pem-jwk').jwk2pem

exports.utils = require('./rsa-utils')

exports.generateKey = function (bits, callback) {
nextTick(() => {
let result
try {
const key = keypair({ bits: bits })
result = {
privateKey: pemToJwk(key.private),
publicKey: pemToJwk(key.public)
}
} catch (err) {
return callback(err)
}

callback(null, result)
})
}

// Takes a jwk key
exports.unmarshalPrivateKey = function (key, callback) {
nextTick(() => {
if (!key) {
return callback(new Error('Key is invalid'))
}
callback(null, {
privateKey: key,
publicKey: {
kty: key.kty,
n: key.n,
e: key.e
}
})
})
}

exports.getRandomValues = randomBytes

exports.hashAndSign = function (key, msg, callback) {
nextTick(() => {
let result
try {
const sign = crypto.createSign('RSA-SHA256')
sign.update(msg)
const pem = jwkToPem(key)
result = sign.sign(pem)
} catch (err) {
return callback(new Error('Key or message is invalid!: ' + err.message))
}

callback(null, result)
})
}

exports.hashAndVerify = function (key, sig, msg, callback) {
nextTick(() => {
let result
try {
const verify = crypto.createVerify('RSA-SHA256')
verify.update(msg)
const pem = jwkToPem(key)
result = verify.verify(pem, sig)
} catch (err) {
return callback(new Error('Key or message is invalid!:' + err.message))
}

callback(null, result)
})
}
File renamed without changes.
Loading