feat: improve perf (#117)

This commit is contained in:
Richard Schneider 2018-01-28 07:54:04 +13:00 committed by David Dias
parent 2c0dc706b7
commit cdcca5f828
6 changed files with 117 additions and 39 deletions

View File

@ -34,10 +34,10 @@
"async": "^2.6.0", "async": "^2.6.0",
"browserify-aes": "^1.1.1", "browserify-aes": "^1.1.1",
"bs58": "^4.0.1", "bs58": "^4.0.1",
"jsrsasign": "^8.0.4",
"keypair": "^1.0.1", "keypair": "^1.0.1",
"libp2p-crypto-secp256k1": "~0.2.2", "libp2p-crypto-secp256k1": "~0.2.2",
"multihashing-async": "~0.4.7", "multihashing-async": "~0.4.7",
"node-forge": "^0.7.1",
"pem-jwk": "^1.5.1", "pem-jwk": "^1.5.1",
"protons": "^1.0.1", "protons": "^1.0.1",
"rsa-pem-to-jwk": "^1.1.3", "rsa-pem-to-jwk": "^1.1.3",

View File

@ -2,8 +2,7 @@
const protobuf = require('protons') const protobuf = require('protons')
const keysPBM = protobuf(require('./keys.proto')) const keysPBM = protobuf(require('./keys.proto'))
const jsrsasign = require('jsrsasign') const forge = require('node-forge')
const KEYUTIL = jsrsasign.KEYUTIL
exports = module.exports exports = module.exports
@ -120,13 +119,13 @@ exports.marshalPrivateKey = (key, type) => {
exports.import = (pem, password, callback) => { exports.import = (pem, password, callback) => {
try { try {
const key = KEYUTIL.getKey(pem, password) const key = forge.pki.decryptRsaPrivateKey(pem, password)
if (key instanceof jsrsasign.RSAKey) { if (key === null) {
const jwk = KEYUTIL.getJWKFromKey(key) throw new Error('Cannot read the key, most likely the password is wrong or not a RSA key')
return supportedKeys.rsa.fromJwk(jwk, callback)
} else {
throw new Error(`Unknown key type '${key.prototype.toString()}'`)
} }
let der = forge.asn1.toDer(forge.pki.privateKeyToAsn1(key))
der = Buffer.from(der.getBytes(), 'binary')
return supportedKeys.rsa.unmarshalRsaPrivateKey(der, callback)
} catch (err) { } catch (err) {
callback(err) callback(err)
} }

View File

@ -6,7 +6,7 @@ const bs58 = require('bs58')
const crypto = require('./rsa') const crypto = require('./rsa')
const pbm = protobuf(require('./keys.proto')) const pbm = protobuf(require('./keys.proto'))
const KEYUTIL = require('jsrsasign').KEYUTIL const forge = require('node-forge')
const setImmediate = require('async/setImmediate') const setImmediate = require('async/setImmediate')
class RsaPublicKey { class RsaPublicKey {
@ -127,20 +127,29 @@ class RsaPrivateKey {
format = 'pkcs-8' format = 'pkcs-8'
} }
setImmediate(() => { ensure(callback)
ensure(callback)
setImmediate(() => {
let err = null let err = null
let pem = null let pem = null
try { try {
const key = KEYUTIL.getKey(this._key) // _key is a JWK (JSON Web Key) const buffer = new forge.util.ByteBuffer(this.marshal())
const asn1 = forge.asn1.fromDer(buffer)
const privateKey = forge.pki.privateKeyFromAsn1(asn1)
if (format === 'pkcs-8') { if (format === 'pkcs-8') {
pem = KEYUTIL.getPEM(key, 'PKCS8PRV', password) const options = {
algorithm: 'aes256',
count: 10000,
saltSize: 128 / 8,
prfAlgorithm: 'sha512'
}
pem = forge.pki.encryptRsaPrivateKey(privateKey, password, options)
} else { } else {
err = new Error(`Unknown export format '${format}'`) err = new Error(`Unknown export format '${format}'`)
} }
} catch (e) { } catch (_err) {
err = e err = _err
} }
callback(err, pem) callback(err, pem)
@ -150,6 +159,7 @@ class RsaPrivateKey {
function unmarshalRsaPrivateKey (bytes, callback) { function unmarshalRsaPrivateKey (bytes, callback) {
const jwk = crypto.utils.pkcs1ToJwk(bytes) const jwk = crypto.utils.pkcs1ToJwk(bytes)
crypto.unmarshalPrivateKey(jwk, (err, keys) => { crypto.unmarshalPrivateKey(jwk, (err, keys) => {
if (err) { if (err) {
return callback(err) return callback(err)
@ -175,18 +185,18 @@ function fromJwk (jwk, callback) {
}) })
} }
function generateKeyPair (bits, cb) { function generateKeyPair (bits, callback) {
crypto.generateKey(bits, (err, keys) => { crypto.generateKey(bits, (err, keys) => {
if (err) { if (err) {
return cb(err) return callback(err)
} }
cb(null, new RsaPrivateKey(keys.privateKey, keys.publicKey)) callback(null, new RsaPrivateKey(keys.privateKey, keys.publicKey))
}) })
} }
function ensure (cb) { function ensure (callback) {
if (typeof cb !== 'function') { if (typeof callback !== 'function') {
throw new Error('callback is required') throw new Error('callback is required')
} }
} }

View File

@ -1,18 +1,18 @@
'use strict' 'use strict'
const crypto = require('jsrsasign').CryptoJS const forge = require('node-forge')
/** /**
* Maps an IPFS hash name to its jsrsasign equivalent. * Maps an IPFS hash name to its node-forge equivalent.
* *
* See https://github.com/multiformats/multihash/blob/master/hashtable.csv * See https://github.com/multiformats/multihash/blob/master/hashtable.csv
* *
* @private * @private
*/ */
const hashName = { const hashName = {
sha1: crypto.algo.SHA1, sha1: 'sha1',
'sha2-256': crypto.algo.SHA256, 'sha2-256': 'sha256',
'sha2-512': crypto.algo.SHA512 'sha2-512': 'sha512'
} }
/** /**
@ -26,16 +26,17 @@ const hashName = {
* @returns {string} - A new password * @returns {string} - A new password
*/ */
function pbkdf2 (password, salt, iterations, keySize, hash) { function pbkdf2 (password, salt, iterations, keySize, hash) {
const opts = { const hasher = hashName[hash]
iterations: iterations, if (!hasher) {
keySize: keySize / 4, // convert bytes to words (32 bits)
hasher: hashName[hash]
}
if (!opts.hasher) {
throw new Error(`Hash '${hash}' is unknown or not supported`) throw new Error(`Hash '${hash}' is unknown or not supported`)
} }
const words = crypto.PBKDF2(password, salt, opts) const dek = forge.pkcs5.pbkdf2(
return crypto.enc.Base64.stringify(words) password,
salt,
iterations,
keySize,
hasher)
return forge.util.encode64(dek)
} }
module.exports = pbkdf2 module.exports = pbkdf2

View File

@ -13,7 +13,7 @@ describe('libp2p-crypto', function () {
this.timeout(20 * 1000) this.timeout(20 * 1000)
let key let key
before((done) => { before((done) => {
crypto.keys.generateKeyPair('RSA', 2048, (err, _key) => { crypto.keys.generateKeyPair('RSA', 512, (err, _key) => {
if (err) { if (err) {
return done(err) return done(err)
} }

View File

@ -19,7 +19,7 @@ describe('RSA', function () {
let key let key
before((done) => { before((done) => {
crypto.keys.generateKeyPair('RSA', 2048, (err, _key) => { crypto.keys.generateKeyPair('RSA', 512, (err, _key) => {
if (err) { if (err) {
return done(err) return done(err)
} }
@ -97,7 +97,7 @@ describe('RSA', function () {
}) })
it('not equals other key', (done) => { it('not equals other key', (done) => {
crypto.keys.generateKeyPair('RSA', 2048, (err, key2) => { crypto.keys.generateKeyPair('RSA', 512, (err, key2) => {
if (err) { if (err) {
return done(err) return done(err)
} }
@ -297,8 +297,42 @@ mBdkD5r+ixWF174naw53L8U9wF8kiK7pIE1N9TR4USEeovLwX6Ni/2MMDZedOfof
}) })
}) })
// AssertionError: expected 'this only supports TripleDES' to not exist it('can read a private encrypted key (v2 aes-128-cbc)', (done) => {
it.skip('can read a private encrypted key (v2 aes-256-cbc)', (done) => { /*
* Generated with
* openssl genpkey -algorithm RSA
* -pkeyopt rsa_keygen_bits:1024
* -pkeyopt rsa_keygen_pubexp:65537
* -out foo.pem
* openssl pkcs8 -in foo.pem -topk8 -v2 aes-128-cbc -passout pass:mypassword
*/
const pem = `-----BEGIN ENCRYPTED PRIVATE KEY-----
MIICzzBJBgkqhkiG9w0BBQ0wPDAbBgkqhkiG9w0BBQwwDgQIP5QK2RfqUl4CAggA
MB0GCWCGSAFlAwQBAgQQj3OyM9gnW2dd/eRHkxjGrgSCAoCpM5GZB0v27cxzZsGc
O4/xqgwB0c/bSJ6QogtYU2KVoc7ZNQ5q9jtzn3I4ONvneOkpm9arzYz0FWnJi2C3
BPiF0D1NkfvjvMLv56bwiG2A1oBECacyAb2pXYeJY7SdtYKvcbgs3jx65uCm6TF2
BylteH+n1ewTQN9DLfASp1n81Ajq9lQGaK03SN2MUtcAPp7N9gnxJrlmDGeqlPRs
KpQYRcot+kE6Ew8a5jAr7mAxwpqvr3SM4dMvADZmRQsM4Uc/9+YMUdI52DG87EWc
0OUB+fnQ8jw4DZgOE9KKM5/QTWc3aEw/dzXr/YJsrv01oLazhqVHnEMG0Nfr0+DP
q+qac1AsCsOb71VxaRlRZcVEkEfAq3gidSPD93qmlDrCnmLYTilcLanXUepda7ez
qhjkHtpwBLN5xRZxOn3oUuLGjk8VRwfmFX+RIMYCyihjdmbEDYpNUVkQVYFGi/F/
1hxOyl9yhGdL0hb9pKHH10GGIgoqo4jSTLlb4ennihGMHCjehAjLdx/GKJkOWShy
V9hj8rAuYnRNb+tUW7ChXm1nLq14x9x1tX0ciVVn3ap/NoMkbFTr8M3pJ4bQlpAn
wCT2erYqwQtgSpOJcrFeph9TjIrNRVE7Zlmr7vayJrB/8/oPssVdhf82TXkna4fB
PcmO0YWLa117rfdeNM/Duy0ThSdTl39Qd+4FxqRZiHjbt+l0iSa/nOjTv1TZ/QqF
wqrO6EtcM45fbFJ1Y79o2ptC2D6MB4HKJq9WCt064/8zQCVx3XPbb3X8Z5o/6koy
ePGbz+UtSb9xczvqpRCOiFLh2MG1dUgWuHazjOtUcVWvilKnkjCMzZ9s1qG0sUDj
nPyn
-----END ENCRYPTED PRIVATE KEY-----
`
crypto.keys.import(pem, 'mypassword', (err, key) => {
expect(err).to.not.exist()
expect(key).to.exist()
done()
})
})
it('can read a private encrypted key (v2 aes-256-cbc)', (done) => {
/* /*
* Generated with * Generated with
* openssl genpkey -algorithm RSA * openssl genpkey -algorithm RSA
@ -333,6 +367,40 @@ DQd8
}) })
}) })
it('can read a private encrypted key (v2 des)', (done) => {
/*
* Generated with
* openssl genpkey -algorithm RSA
* -pkeyopt rsa_keygen_bits:1024
* -pkeyopt rsa_keygen_pubexp:65537
* -out foo.pem
* openssl pkcs8 -in foo.pem -topk8 -v2 des -passout pass:mypassword
*/
const pem = `-----BEGIN ENCRYPTED PRIVATE KEY-----
MIICwzA9BgkqhkiG9w0BBQ0wMDAbBgkqhkiG9w0BBQwwDgQI0lXp62ozXvwCAggA
MBEGBSsOAwIHBAiR3Id5vH0u4wSCAoDQQYOrrkPFPIa0S5fQGXnJw1F/66g92Gs1
TkGydn4ouabWb++Vbi2chee1oyZsN2l8YNzDi0Gb2PfjsGpg2aJk0a3/efgA0u6T
leEH1dA/7Hr9NVspgHkaXpHt3X6wdbznLYJeAelfj7sDXpOkULGWCkCst0Txb6bi
Oxv4c0yYykiuUrp+2xvHbF9c2PrcDb58u/OBZcCg3QB1gTugQKM+ZIBRhcTEFLrm
8gWbzBfwYiUm6aJce4zoafP0NSlEOBbpbr73A08Q1IK6pISwltOUhhTvspSZnK41
y2CHt5Drnpl1pfOw9Q0svO3VrUP+omxP1SFP17ZfaRGw2uHd08HJZs438x5dIQoH
QgjlZ8A5rcT3FjnytSh3fln2ZxAGuObghuzmOEL/+8fkGER9QVjmQlsL6OMfB4j4
ZAkLf74uaTdegF3SqDQaGUwWgk7LyualmUXWTBoeP9kRIsRQLGzAEmd6duBPypED
HhKXP/ZFA1kVp3x1fzJ2llMFB3m1JBwy4PiohqrIJoR+YvKUvzVQtbOjxtCEAj87
JFnlQj0wjTd6lfNn+okewMNjKINZx+08ui7XANNU/l18lHIIz3ssXJSmqMW+hRZ9
9oB2tntLrnRMhkVZDVHadq7eMFOPu0rkekuaZm9CO2vu4V7Qa2h+gOoeczYza0H7
A+qCKbprxyL8SKI5vug2hE+mfC1leXVRtUYm1DnE+oet99bFd0fN20NwTw0rOeRg
0Z+/ZpQNizrXxfd3sU7zaJypWCxZ6TD/U/AKBtcb2gqmUjObZhbfbWq6jU2Ye//w
EBqQkwAUXR1tNekF8CWLOrfC/wbLRxVRkayb8bQUfdgukLpz0bgw
-----END ENCRYPTED PRIVATE KEY-----
`
crypto.keys.import(pem, 'mypassword', (err, key) => {
expect(err).to.not.exist()
expect(key).to.exist()
done()
})
})
it('can read a private encrypted key (v2 des3)', (done) => { it('can read a private encrypted key (v2 des3)', (done) => {
/* /*
* Generated with * Generated with