Refactor keygen to use promises (Work in progress)

This commit is contained in:
Tankred Hase 2014-10-01 13:13:09 +02:00
parent 5d07ee1eb1
commit 0ac58356b5
6 changed files with 84 additions and 109 deletions
src
test/worker

View File

@ -178,19 +178,14 @@ module.exports = {
} }
}, },
generateMpi: function(algo, bits, callback) { generateMpi: function(algo, bits) {
switch (algo) { switch (algo) {
case 'rsa_encrypt': case 'rsa_encrypt':
case 'rsa_encrypt_sign': case 'rsa_encrypt_sign':
case 'rsa_sign': case 'rsa_sign':
//remember "publicKey" refers to the crypto/public_key dir //remember "publicKey" refers to the crypto/public_key dir
var rsa = new publicKey.rsa(); var rsa = new publicKey.rsa();
rsa.generate(bits, "10001", function(err, keyObject) { return rsa.generate(bits, "10001").then(function(keyObject) {
if (err) {
callback(err);
return;
}
var output = []; var output = [];
output.push(keyObject.n); output.push(keyObject.n);
output.push(keyObject.ee); output.push(keyObject.ee);
@ -198,12 +193,10 @@ module.exports = {
output.push(keyObject.p); output.push(keyObject.p);
output.push(keyObject.q); output.push(keyObject.q);
output.push(keyObject.u); output.push(keyObject.u);
return mapResult(output);
callback(null, mapResult(output));
}); });
break;
default: default:
callback(new Error('Unsupported algorithm for key generation.')); throw new Error('Unsupported algorithm for key generation.');
} }
function mapResult(result) { function mapResult(result) {

View File

@ -133,8 +133,9 @@ function RSA() {
// Generate a new random private key B bits long, using public expt E // Generate a new random private key B bits long, using public expt E
function generate(B, E, callback) { function generate(B, E) {
var webCrypto = util.getWebCrypto(); var webCrypto = util.getWebCrypto();
var promise;
// //
// Native RSA keygen using Web Crypto // Native RSA keygen using Web Crypto
@ -143,7 +144,6 @@ function RSA() {
if (webCrypto) { if (webCrypto) {
var Euint32 = new Uint32Array([parseInt(E, 16)]); // get integer of exponent var Euint32 = new Uint32Array([parseInt(E, 16)]); // get integer of exponent
var Euint8 = new Uint8Array(Euint32.buffer); // get bytes of exponent var Euint8 = new Uint8Array(Euint32.buffer); // get bytes of exponent
var keyGenOpt = { var keyGenOpt = {
name: 'RSASSA-PKCS1-v1_5', name: 'RSASSA-PKCS1-v1_5',
modulusLength: B, // the specified keysize in bits modulusLength: B, // the specified keysize in bits
@ -152,11 +152,8 @@ function RSA() {
name: 'SHA-1' // not required for actual RSA keys, but for crypto api 'sign' and 'verify' name: 'SHA-1' // not required for actual RSA keys, but for crypto api 'sign' and 'verify'
} }
}; };
promise = webCrypto.generateKey(keyGenOpt, true, ['sign', 'verify']);
var gen = webCrypto.generateKey(keyGenOpt, true, ['sign', 'verify']); return promise.then(exportKey).then(decodeKey);
gen.then(exportKey).then(decodeKey).catch(onError);
return;
} }
function exportKey(key) { function exportKey(key) {
@ -181,52 +178,53 @@ function RSA() {
return new BigInteger(hex, 16); return new BigInteger(hex, 16);
} }
callback(null, key); return key;
}
function onError() {
callback(new Error('Generating key failed!'));
} }
// //
// JS code // JS code
// //
var key = new keyObject(); promise = new Promise(function(resolve) {
var rng = new SecureRandom(); var key = new keyObject();
var qs = B >> 1; var rng = new SecureRandom();
key.e = parseInt(E, 16); var qs = B >> 1;
key.ee = new BigInteger(E, 16); key.e = parseInt(E, 16);
for (;;) { key.ee = new BigInteger(E, 16);
for (;;) {
key.p = new BigInteger(B - qs, 1, rng);
if (key.p.subtract(BigInteger.ONE).gcd(key.ee).compareTo(BigInteger.ONE) === 0 && key.p.isProbablePrime(10))
break;
}
for (;;) {
key.q = new BigInteger(qs, 1, rng);
if (key.q.subtract(BigInteger.ONE).gcd(key.ee).compareTo(BigInteger.ONE) === 0 && key.q.isProbablePrime(10))
break;
}
if (key.p.compareTo(key.q) <= 0) {
var t = key.p;
key.p = key.q;
key.q = t;
}
var p1 = key.p.subtract(BigInteger.ONE);
var q1 = key.q.subtract(BigInteger.ONE);
var phi = p1.multiply(q1);
if (phi.gcd(key.ee).compareTo(BigInteger.ONE) === 0) {
key.n = key.p.multiply(key.q);
key.d = key.ee.modInverse(phi);
key.dmp1 = key.d.mod(p1);
key.dmq1 = key.d.mod(q1);
key.u = key.p.modInverse(key.q);
break;
}
}
callback(null, key); for (;;) {
for (;;) {
key.p = new BigInteger(B - qs, 1, rng);
if (key.p.subtract(BigInteger.ONE).gcd(key.ee).compareTo(BigInteger.ONE) === 0 && key.p.isProbablePrime(10))
break;
}
for (;;) {
key.q = new BigInteger(qs, 1, rng);
if (key.q.subtract(BigInteger.ONE).gcd(key.ee).compareTo(BigInteger.ONE) === 0 && key.q.isProbablePrime(10))
break;
}
if (key.p.compareTo(key.q) <= 0) {
var t = key.p;
key.p = key.q;
key.q = t;
}
var p1 = key.p.subtract(BigInteger.ONE);
var q1 = key.q.subtract(BigInteger.ONE);
var phi = p1.multiply(q1);
if (phi.gcd(key.ee).compareTo(BigInteger.ONE) === 0) {
key.n = key.p.multiply(key.q);
key.d = key.ee.modInverse(phi);
key.dmp1 = key.d.mod(p1);
key.dmq1 = key.d.mod(q1);
key.u = key.p.modInverse(key.q);
break;
}
}
resolve(key);
});
return promise;
} }
this.encrypt = encrypt; this.encrypt = encrypt;

View File

@ -910,7 +910,7 @@ function readArmored(armoredText) {
* @return {module:key~Key} * @return {module:key~Key}
* @static * @static
*/ */
function generate(options, callback) { function generate(options) {
var packetlist, secretKeyPacket, userIdPacket, dataToSign, signaturePacket, secretSubkeyPacket, subkeySignaturePacket; var packetlist, secretKeyPacket, userIdPacket, dataToSign, signaturePacket, secretSubkeyPacket, subkeySignaturePacket;
options.keyType = options.keyType || enums.publicKey.rsa_encrypt_sign; options.keyType = options.keyType || enums.publicKey.rsa_encrypt_sign;
@ -923,22 +923,32 @@ function generate(options, callback) {
options.unlocked = true; options.unlocked = true;
} }
packetlist = new packet.List(); // generate
var genSecretKey = generateSecretKey();
var genSecretSubkey = generateSecretSubkey();
return Promise.all([genSecretKey, genSecretSubkey]).then(wrapKeyObject);
secretKeyPacket = new packet.SecretKey(); function generateSecretKey() {
secretKeyPacket.algorithm = enums.read(enums.publicKey, options.keyType); secretKeyPacket = new packet.SecretKey();
secretKeyPacket.generate(options.numBits, onSecretKeyGenerated); secretKeyPacket.algorithm = enums.read(enums.publicKey, options.keyType);
return secretKeyPacket.generate(options.numBits);
}
function onSecretKeyGenerated(err) { function generateSecretSubkey() {
if (err) { secretSubkeyPacket = new packet.SecretSubkey();
callback(err); secretSubkeyPacket.algorithm = enums.read(enums.publicKey, options.keyType);
return; return secretSubkeyPacket.generate(options.numBits);
} }
function wrapKeyObject() {
// set passphrase protection
if (options.passphrase) { if (options.passphrase) {
secretKeyPacket.encrypt(options.passphrase); secretKeyPacket.encrypt(options.passphrase);
secretSubkeyPacket.encrypt(options.passphrase);
} }
packetlist = new packet.List();
userIdPacket = new packet.Userid(); userIdPacket = new packet.Userid();
userIdPacket.read(options.userId); userIdPacket.read(options.userId);
@ -969,21 +979,6 @@ function generate(options, callback) {
} }
signaturePacket.sign(secretKeyPacket, dataToSign); signaturePacket.sign(secretKeyPacket, dataToSign);
secretSubkeyPacket = new packet.SecretSubkey();
secretSubkeyPacket.algorithm = enums.read(enums.publicKey, options.keyType);
secretSubkeyPacket.generate(options.numBits, onSecretSubkeyGenerated);
}
function onSecretSubkeyGenerated(err) {
if (err) {
callback(err);
return;
}
if (options.passphrase) {
secretSubkeyPacket.encrypt(options.passphrase);
}
dataToSign = {}; dataToSign = {};
dataToSign.key = secretKeyPacket; dataToSign.key = secretKeyPacket;
dataToSign.bind = secretSubkeyPacket; dataToSign.bind = secretSubkeyPacket;
@ -1005,7 +1000,7 @@ function generate(options, callback) {
secretSubkeyPacket.clearPrivateMPIs(); secretSubkeyPacket.clearPrivateMPIs();
} }
callback(null, new Key(packetlist)); return new Key(packetlist);
} }
} }

View File

@ -229,28 +229,23 @@ function verifyClearSignedMessage(publicKeys, msg, callback) {
* @return {Object} {key: module:key~Key, privateKeyArmored: String, publicKeyArmored: String} * @return {Object} {key: module:key~Key, privateKeyArmored: String, publicKeyArmored: String}
* @static * @static
*/ */
function generateKeyPair(options, callback) { function generateKeyPair(options) {
if (!callback) {
throw new Error('Callback must be set for key generation!');
}
// use web worker if web crypto apis are not supported // use web worker if web crypto apis are not supported
if (!util.getWebCrypto() && useWorker(callback)) { if (!util.getWebCrypto() && useWorker()) {
asyncProxy.generateKeyPair(options, callback); asyncProxy.generateKeyPair(options);
return; return;
} }
key.generate(options, function(err, newKey) { return key.generate(options).then(function(newKey) {
if (err) {
callback(err);
return;
}
var result = {}; var result = {};
result.key = newKey; result.key = newKey;
result.privateKeyArmored = newKey.armor(); result.privateKeyArmored = newKey.armor();
result.publicKeyArmored = newKey.toPublic().armor(); result.publicKeyArmored = newKey.toPublic().armor();
callback(null, result); return result;
}).catch(function(err) {
console.error(err.stack);
throw new Error('Error generating keypair!');
}); });
} }

View File

@ -270,18 +270,12 @@ SecretKey.prototype.decrypt = function (passphrase) {
return true; return true;
}; };
SecretKey.prototype.generate = function (bits, callback) { SecretKey.prototype.generate = function (bits) {
var self = this; var self = this;
crypto.generateMpi(self.algorithm, bits, function(err, mpi) { return crypto.generateMpi(self.algorithm, bits).then(function(mpi) {
if (err) {
callback(err);
return;
}
self.mpi = mpi; self.mpi = mpi;
self.isDecrypted = true; self.isDecrypted = true;
callback();
}); });
}; };

View File

@ -417,8 +417,8 @@ describe('High level API', function() {
describe('Key generation', function() { describe('Key generation', function() {
it('Generate 1024-bit RSA/RSA key async', function (done) { it('Generate 1024-bit RSA/RSA key async', function (done) {
openpgp.generateKeyPair({numBits: 1024, userId: 'Test McTestington <test@example.com>', passphrase: 'hello world'}, function(err, data) { var opt = {numBits: 1024, userId: 'Test McTestington <test@example.com>', passphrase: 'hello world'};
expect(err).to.not.exist; openpgp.generateKeyPair(opt).then(function(data) {
expect(data).to.exist; expect(data).to.exist;
expect(data.publicKeyArmored).to.match(/^-----BEGIN PGP PUBLIC/); expect(data.publicKeyArmored).to.match(/^-----BEGIN PGP PUBLIC/);
expect(data.privateKeyArmored).to.match(/^-----BEGIN PGP PRIVATE/); expect(data.privateKeyArmored).to.match(/^-----BEGIN PGP PRIVATE/);