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

Commit

Permalink
feat: improve perf (#117)
Browse files Browse the repository at this point in the history
  • Loading branch information
richardschneider authored and daviddias committed Jan 27, 2018
1 parent 2c0dc70 commit cdcca5f
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 39 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@
"async": "^2.6.0",
"browserify-aes": "^1.1.1",
"bs58": "^4.0.1",
"jsrsasign": "^8.0.4",
"keypair": "^1.0.1",
"libp2p-crypto-secp256k1": "~0.2.2",
"multihashing-async": "~0.4.7",
"node-forge": "^0.7.1",
"pem-jwk": "^1.5.1",
"protons": "^1.0.1",
"rsa-pem-to-jwk": "^1.1.3",
Expand Down
15 changes: 7 additions & 8 deletions src/keys/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@

const protobuf = require('protons')
const keysPBM = protobuf(require('./keys.proto'))
const jsrsasign = require('jsrsasign')
const KEYUTIL = jsrsasign.KEYUTIL
const forge = require('node-forge')

exports = module.exports

Expand Down Expand Up @@ -120,13 +119,13 @@ exports.marshalPrivateKey = (key, type) => {

exports.import = (pem, password, callback) => {
try {
const key = KEYUTIL.getKey(pem, password)
if (key instanceof jsrsasign.RSAKey) {
const jwk = KEYUTIL.getJWKFromKey(key)
return supportedKeys.rsa.fromJwk(jwk, callback)
} else {
throw new Error(`Unknown key type '${key.prototype.toString()}'`)
const key = forge.pki.decryptRsaPrivateKey(pem, password)
if (key === null) {
throw new Error('Cannot read the key, most likely the password is wrong or not a RSA key')
}
let der = forge.asn1.toDer(forge.pki.privateKeyToAsn1(key))
der = Buffer.from(der.getBytes(), 'binary')
return supportedKeys.rsa.unmarshalRsaPrivateKey(der, callback)
} catch (err) {
callback(err)
}
Expand Down
34 changes: 22 additions & 12 deletions src/keys/rsa-class.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const bs58 = require('bs58')

const crypto = require('./rsa')
const pbm = protobuf(require('./keys.proto'))
const KEYUTIL = require('jsrsasign').KEYUTIL
const forge = require('node-forge')
const setImmediate = require('async/setImmediate')

class RsaPublicKey {
Expand Down Expand Up @@ -127,20 +127,29 @@ class RsaPrivateKey {
format = 'pkcs-8'
}

setImmediate(() => {
ensure(callback)
ensure(callback)

setImmediate(() => {
let err = null
let pem = null
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') {
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 {
err = new Error(`Unknown export format '${format}'`)
}
} catch (e) {
err = e
} catch (_err) {
err = _err
}

callback(err, pem)
Expand All @@ -150,6 +159,7 @@ class RsaPrivateKey {

function unmarshalRsaPrivateKey (bytes, callback) {
const jwk = crypto.utils.pkcs1ToJwk(bytes)

crypto.unmarshalPrivateKey(jwk, (err, keys) => {
if (err) {
return callback(err)
Expand All @@ -175,18 +185,18 @@ function fromJwk (jwk, callback) {
})
}

function generateKeyPair (bits, cb) {
function generateKeyPair (bits, callback) {
crypto.generateKey(bits, (err, keys) => {
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) {
if (typeof cb !== 'function') {
function ensure (callback) {
if (typeof callback !== 'function') {
throw new Error('callback is required')
}
}
Expand Down
27 changes: 14 additions & 13 deletions src/pbkdf2.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
'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
*
* @private
*/
const hashName = {
sha1: crypto.algo.SHA1,
'sha2-256': crypto.algo.SHA256,
'sha2-512': crypto.algo.SHA512
sha1: 'sha1',
'sha2-256': 'sha256',
'sha2-512': 'sha512'
}

/**
Expand All @@ -26,16 +26,17 @@ const hashName = {
* @returns {string} - A new password
*/
function pbkdf2 (password, salt, iterations, keySize, hash) {
const opts = {
iterations: iterations,
keySize: keySize / 4, // convert bytes to words (32 bits)
hasher: hashName[hash]
}
if (!opts.hasher) {
const hasher = hashName[hash]
if (!hasher) {
throw new Error(`Hash '${hash}' is unknown or not supported`)
}
const words = crypto.PBKDF2(password, salt, opts)
return crypto.enc.Base64.stringify(words)
const dek = forge.pkcs5.pbkdf2(
password,
salt,
iterations,
keySize,
hasher)
return forge.util.encode64(dek)
}

module.exports = pbkdf2
2 changes: 1 addition & 1 deletion test/crypto.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ describe('libp2p-crypto', function () {
this.timeout(20 * 1000)
let key
before((done) => {
crypto.keys.generateKeyPair('RSA', 2048, (err, _key) => {
crypto.keys.generateKeyPair('RSA', 512, (err, _key) => {
if (err) {
return done(err)
}
Expand Down
76 changes: 72 additions & 4 deletions test/keys/rsa.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ describe('RSA', function () {
let key

before((done) => {
crypto.keys.generateKeyPair('RSA', 2048, (err, _key) => {
crypto.keys.generateKeyPair('RSA', 512, (err, _key) => {
if (err) {
return done(err)
}
Expand Down Expand Up @@ -97,7 +97,7 @@ describe('RSA', function () {
})

it('not equals other key', (done) => {
crypto.keys.generateKeyPair('RSA', 2048, (err, key2) => {
crypto.keys.generateKeyPair('RSA', 512, (err, key2) => {
if (err) {
return done(err)
}
Expand Down Expand Up @@ -297,8 +297,42 @@ mBdkD5r+ixWF174naw53L8U9wF8kiK7pIE1N9TR4USEeovLwX6Ni/2MMDZedOfof
})
})

// AssertionError: expected 'this only supports TripleDES' to not exist
it.skip('can read a private encrypted key (v2 aes-256-cbc)', (done) => {
it('can read a private encrypted key (v2 aes-128-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
* openssl genpkey -algorithm RSA
Expand Down Expand Up @@ -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) => {
/*
* Generated with
Expand Down

0 comments on commit cdcca5f

Please sign in to comment.