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

View File

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

View File

@ -133,8 +133,9 @@ function RSA() {
// 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 promise;
//
// Native RSA keygen using Web Crypto
@ -143,7 +144,6 @@ function RSA() {
if (webCrypto) {
var Euint32 = new Uint32Array([parseInt(E, 16)]); // get integer of exponent
var Euint8 = new Uint8Array(Euint32.buffer); // get bytes of exponent
var keyGenOpt = {
name: 'RSASSA-PKCS1-v1_5',
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'
}
};
var gen = webCrypto.generateKey(keyGenOpt, true, ['sign', 'verify']);
gen.then(exportKey).then(decodeKey).catch(onError);
return;
promise = webCrypto.generateKey(keyGenOpt, true, ['sign', 'verify']);
return promise.then(exportKey).then(decodeKey);
}
function exportKey(key) {
@ -181,52 +178,53 @@ function RSA() {
return new BigInteger(hex, 16);
}
callback(null, key);
}
function onError() {
callback(new Error('Generating key failed!'));
return key;
}
//
// JS code
//
var key = new keyObject();
var rng = new SecureRandom();
var qs = B >> 1;
key.e = parseInt(E, 16);
key.ee = new BigInteger(E, 16);
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;
}
}
promise = new Promise(function(resolve) {
var key = new keyObject();
var rng = new SecureRandom();
var qs = B >> 1;
key.e = parseInt(E, 16);
key.ee = new BigInteger(E, 16);
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;

View File

@ -5,7 +5,7 @@
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 3.0 of the License, or (at your option) any later version.
//
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
@ -910,7 +910,7 @@ function readArmored(armoredText) {
* @return {module:key~Key}
* @static
*/
function generate(options, callback) {
function generate(options) {
var packetlist, secretKeyPacket, userIdPacket, dataToSign, signaturePacket, secretSubkeyPacket, subkeySignaturePacket;
options.keyType = options.keyType || enums.publicKey.rsa_encrypt_sign;
@ -923,22 +923,32 @@ function generate(options, callback) {
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();
secretKeyPacket.algorithm = enums.read(enums.publicKey, options.keyType);
secretKeyPacket.generate(options.numBits, onSecretKeyGenerated);
function generateSecretKey() {
secretKeyPacket = new packet.SecretKey();
secretKeyPacket.algorithm = enums.read(enums.publicKey, options.keyType);
return secretKeyPacket.generate(options.numBits);
}
function onSecretKeyGenerated(err) {
if (err) {
callback(err);
return;
}
function generateSecretSubkey() {
secretSubkeyPacket = new packet.SecretSubkey();
secretSubkeyPacket.algorithm = enums.read(enums.publicKey, options.keyType);
return secretSubkeyPacket.generate(options.numBits);
}
function wrapKeyObject() {
// set passphrase protection
if (options.passphrase) {
secretKeyPacket.encrypt(options.passphrase);
secretSubkeyPacket.encrypt(options.passphrase);
}
packetlist = new packet.List();
userIdPacket = new packet.Userid();
userIdPacket.read(options.userId);
@ -969,21 +979,6 @@ function generate(options, callback) {
}
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.key = secretKeyPacket;
dataToSign.bind = secretSubkeyPacket;
@ -1005,7 +1000,7 @@ function generate(options, callback) {
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}
* @static
*/
function generateKeyPair(options, callback) {
if (!callback) {
throw new Error('Callback must be set for key generation!');
}
function generateKeyPair(options) {
// use web worker if web crypto apis are not supported
if (!util.getWebCrypto() && useWorker(callback)) {
asyncProxy.generateKeyPair(options, callback);
if (!util.getWebCrypto() && useWorker()) {
asyncProxy.generateKeyPair(options);
return;
}
key.generate(options, function(err, newKey) {
if (err) {
callback(err);
return;
}
return key.generate(options).then(function(newKey) {
var result = {};
result.key = newKey;
result.privateKeyArmored = newKey.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

@ -5,7 +5,7 @@
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 3.0 of the License, or (at your option) any later version.
//
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
@ -270,18 +270,12 @@ SecretKey.prototype.decrypt = function (passphrase) {
return true;
};
SecretKey.prototype.generate = function (bits, callback) {
SecretKey.prototype.generate = function (bits) {
var self = this;
crypto.generateMpi(self.algorithm, bits, function(err, mpi) {
if (err) {
callback(err);
return;
}
return crypto.generateMpi(self.algorithm, bits).then(function(mpi) {
self.mpi = mpi;
self.isDecrypted = true;
callback();
});
};

View File

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