Refactor keygen to use promises (Work in progress)
This commit is contained in:
parent
5d07ee1eb1
commit
0ac58356b5
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
47
src/key.js
47
src/key.js
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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!');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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/);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user