diff --git a/src/crypto/crypto.js b/src/crypto/crypto.js index 6ae3f64e..4667291d 100644 --- a/src/crypto/crypto.js +++ b/src/crypto/crypto.js @@ -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) { diff --git a/src/crypto/public_key/rsa.js b/src/crypto/public_key/rsa.js index 05da060e..71e68479 100644 --- a/src/crypto/public_key/rsa.js +++ b/src/crypto/public_key/rsa.js @@ -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; diff --git a/src/key.js b/src/key.js index 2a730375..d51ee41b 100644 --- a/src/key.js +++ b/src/key.js @@ -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); } } diff --git a/src/openpgp.js b/src/openpgp.js index d71cfb92..f742c84d 100644 --- a/src/openpgp.js +++ b/src/openpgp.js @@ -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!'); }); } diff --git a/src/packet/secret_key.js b/src/packet/secret_key.js index 21addf8f..f2c9cab0 100644 --- a/src/packet/secret_key.js +++ b/src/packet/secret_key.js @@ -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(); }); }; diff --git a/test/worker/api.js b/test/worker/api.js index cba322f5..23a0389e 100644 --- a/test/worker/api.js +++ b/test/worker/api.js @@ -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 ', passphrase: 'hello world'}, function(err, data) { - expect(err).to.not.exist; + var opt = {numBits: 1024, userId: 'Test McTestington ', 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/);