From bb0ac83cb7eaf2c66b5bbd04ea5b24a9b8e09965 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Obernd=C3=B6rfer?= Date: Mon, 28 Apr 2014 16:15:27 +0200 Subject: [PATCH] Generate key by default without unlocking secret part. Use options parameter for generate method. --- src/key.js | 40 ++++++++++++++++++++++++--------------- src/openpgp.js | 15 ++++++++------- src/packet/secret_key.js | 12 ++++++++++-- src/worker/async_proxy.js | 7 ++----- src/worker/worker.js | 2 +- test/general/basic.js | 4 ++-- test/general/key.js | 9 ++++++++- test/worker/api.js | 4 ++-- 8 files changed, 58 insertions(+), 35 deletions(-) diff --git a/src/key.js b/src/key.js index c6d0f8b1..e8f77a43 100644 --- a/src/key.js +++ b/src/key.js @@ -928,36 +928,41 @@ function readArmored(armoredText) { /** * Generates a new OpenPGP key. Currently only supports RSA keys. * Primary and subkey will be of same type. - * @param {module:enums.publicKey} keyType to indicate what type of key to make. + * @param {module:enums.publicKey} [options.keyType=module:enums.publicKey.rsa_encrypt_sign] to indicate what type of key to make. * RSA is 1. See {@link http://tools.ietf.org/html/rfc4880#section-9.1} - * @param {Integer} numBits number of bits for the key creation. - * @param {String} userId assumes already in form of "User Name " - * @param {String} passphrase The passphrase used to encrypt the resulting private key + * @param {Integer} options.numBits number of bits for the key creation. + * @param {String} options.userId assumes already in form of "User Name " + * @param {String} options.passphrase The passphrase used to encrypt the resulting private key + * @param {Boolean} [options.unlocked=false] The secret part of the generated key is unlocked * @return {module:key~Key} * @static */ -function generate(keyType, numBits, userId, passphrase) { +function generate(options) { + options.keyType = options.keyType || enums.publicKey.rsa_encrypt_sign; // RSA Encrypt-Only and RSA Sign-Only are deprecated and SHOULD NOT be generated - if (keyType !== enums.publicKey.rsa_encrypt_sign) { + if (options.keyType !== enums.publicKey.rsa_encrypt_sign) { throw new Error('Only RSA Encrypt or Sign supported'); } + if (!options.passphrase) { + throw new Error('Parameter options.passphrase required'); + } var packetlist = new packet.List(); var secretKeyPacket = new packet.SecretKey(); - secretKeyPacket.algorithm = enums.read(enums.publicKey, keyType); - secretKeyPacket.generate(numBits); - secretKeyPacket.encrypt(passphrase); + secretKeyPacket.algorithm = enums.read(enums.publicKey, options.keyType); + secretKeyPacket.generate(options.numBits); + secretKeyPacket.encrypt(options.passphrase); var userIdPacket = new packet.Userid(); - userIdPacket.read(userId); + userIdPacket.read(options.userId); var dataToSign = {}; dataToSign.userid = userIdPacket; dataToSign.key = secretKeyPacket; var signaturePacket = new packet.Signature(); signaturePacket.signatureType = enums.signature.cert_generic; - signaturePacket.publicKeyAlgorithm = keyType; + signaturePacket.publicKeyAlgorithm = options.keyType; signaturePacket.hashAlgorithm = config.prefer_hash_algorithm; signaturePacket.keyFlags = [enums.keyFlags.certify_keys | enums.keyFlags.sign_data]; signaturePacket.preferredSymmetricAlgorithms = []; @@ -980,16 +985,16 @@ function generate(keyType, numBits, userId, passphrase) { signaturePacket.sign(secretKeyPacket, dataToSign); var secretSubkeyPacket = new packet.SecretSubkey(); - secretSubkeyPacket.algorithm = enums.read(enums.publicKey, keyType); - secretSubkeyPacket.generate(numBits); - secretSubkeyPacket.encrypt(passphrase); + secretSubkeyPacket.algorithm = enums.read(enums.publicKey, options.keyType); + secretSubkeyPacket.generate(options.numBits); + secretSubkeyPacket.encrypt(options.passphrase); dataToSign = {}; dataToSign.key = secretKeyPacket; dataToSign.bind = secretSubkeyPacket; var subkeySignaturePacket = new packet.Signature(); subkeySignaturePacket.signatureType = enums.signature.subkey_binding; - subkeySignaturePacket.publicKeyAlgorithm = keyType; + subkeySignaturePacket.publicKeyAlgorithm = options.keyType; subkeySignaturePacket.hashAlgorithm = config.prefer_hash_algorithm; subkeySignaturePacket.keyFlags = [enums.keyFlags.encrypt_communication | enums.keyFlags.encrypt_storage]; subkeySignaturePacket.sign(secretKeyPacket, dataToSign); @@ -1000,6 +1005,11 @@ function generate(keyType, numBits, userId, passphrase) { packetlist.push(secretSubkeyPacket); packetlist.push(subkeySignaturePacket); + if (!options.unlocked) { + secretKeyPacket.clearPrivateMPIs(); + secretSubkeyPacket.clearPrivateMPIs(); + } + return new Key(packetlist); } diff --git a/src/openpgp.js b/src/openpgp.js index 95e66db8..e826620e 100644 --- a/src/openpgp.js +++ b/src/openpgp.js @@ -198,24 +198,25 @@ function verifyClearSignedMessage(publicKeys, msg, callback) { /** * Generates a new OpenPGP key pair. Currently only supports RSA keys. * Primary and subkey will be of same type. - * @param {module:enums.publicKey} keyType to indicate what type of key to make. + * @param {module:enums.publicKey} [options.keyType=module:enums.publicKey.rsa_encrypt_sign] to indicate what type of key to make. * RSA is 1. See {@link http://tools.ietf.org/html/rfc4880#section-9.1} - * @param {Integer} numBits number of bits for the key creation. (should be 1024+, generally) - * @param {String} userId assumes already in form of "User Name " - * @param {String} passphrase The passphrase used to encrypt the resulting private key + * @param {Integer} options.numBits number of bits for the key creation. (should be 1024+, generally) + * @param {String} options.userId assumes already in form of "User Name " + * @param {String} options.passphrase The passphrase used to encrypt the resulting private key + * @param {Boolean} [options.unlocked=false] The secret part of the generated key is unlocked * @param {function} callback (optional) callback(error, result) for async style * @return {Object} {key: module:key~Key, privateKeyArmored: String, publicKeyArmored: String} * @static */ -function generateKeyPair(keyType, numBits, userId, passphrase, callback) { +function generateKeyPair(options, callback) { if (useWorker(callback)) { - asyncProxy.generateKeyPair(keyType, numBits, userId, passphrase, callback); + asyncProxy.generateKeyPair(options, callback); return; } return execute(function() { var result = {}; - var newKey = key.generate(keyType, numBits, userId, passphrase); + var newKey = key.generate(options); result.key = newKey; result.privateKeyArmored = newKey.armor(); result.publicKeyArmored = newKey.toPublic().armor(); diff --git a/src/packet/secret_key.js b/src/packet/secret_key.js index 74a17aa6..f48a7545 100644 --- a/src/packet/secret_key.js +++ b/src/packet/secret_key.js @@ -182,7 +182,6 @@ SecretKey.prototype.encrypt = function (passphrase) { blockLen = crypto.cipher[symmetric].blockSize, iv = crypto.random.getRandomBytes(blockLen); - this.encrypted = ''; this.encrypted += String.fromCharCode(254); this.encrypted += String.fromCharCode(enums.write(enums.symmetric, symmetric)); @@ -255,8 +254,9 @@ SecretKey.prototype.decrypt = function (passphrase) { 'mod'; var parsedMPI = parse_cleartext_mpi(hash, cleartext, this.algorithm); - if (parsedMPI instanceof Error) + if (parsedMPI instanceof Error) { return false; + } this.mpi = this.mpi.concat(parsedMPI); this.isDecrypted = true; return true; @@ -266,3 +266,11 @@ SecretKey.prototype.generate = function (bits) { this.mpi = crypto.generateMpi(this.algorithm, bits); this.isDecrypted = true; }; + +/** + * Clear private MPIs, return to initial state + */ +SecretKey.prototype.clearPrivateMPIs = function () { + this.mpi = this.mpi.slice(0, crypto.getPublicMpiCount(this.algorithm)); + this.isDecrypted = false; +}; diff --git a/src/worker/async_proxy.js b/src/worker/async_proxy.js index 59507545..6580b0bc 100644 --- a/src/worker/async_proxy.js +++ b/src/worker/async_proxy.js @@ -234,13 +234,10 @@ AsyncProxy.prototype.verifyClearSignedMessage = function(publicKeys, message, ca * @param {String} passphrase The passphrase used to encrypt the resulting private key * @param {Function} callback receives object with key and public and private armored texts */ -AsyncProxy.prototype.generateKeyPair = function(keyType, numBits, userId, passphrase, callback) { +AsyncProxy.prototype.generateKeyPair = function(options, callback) { this.worker.postMessage({ event: 'generate-key-pair', - keyType: keyType, - numBits: numBits, - userId: userId, - passphrase: passphrase + options: options }); this.tasks.push(function(err, data) { if (data) { diff --git a/src/worker/worker.js b/src/worker/worker.js index 3ab531ad..b93ee4d5 100644 --- a/src/worker/worker.js +++ b/src/worker/worker.js @@ -98,7 +98,7 @@ onmessage = function (event) { break; case 'generate-key-pair': try { - data = window.openpgp.generateKeyPair(msg.keyType, msg.numBits, msg.userId, msg.passphrase); + data = window.openpgp.generateKeyPair(msg.options); data.key = data.key.toPacketlist(); } catch (e) { err = e.message; diff --git a/test/general/basic.js b/test/general/basic.js index a9f1e621..b4018a6b 100644 --- a/test/general/basic.js +++ b/test/general/basic.js @@ -9,7 +9,7 @@ describe('Basic', function() { describe("Key generation/encryption/decryption", function() { var testHelper = function(passphrase, userid, message) { - var key = openpgp.generateKeyPair(openpgp.enums.publicKey.rsa_encrypt_sign, 512, userid, passphrase); + var key = openpgp.generateKeyPair({numBits: 512, userId: userid, passphrase: passphrase}); expect(key).to.exist; expect(key.key).to.exist; expect(key.privateKeyArmored).to.exist; @@ -72,7 +72,7 @@ describe('Basic', function() { var userid = 'Test McTestington '; var passphrase = 'password'; - var key = openpgp.generateKeyPair(openpgp.enums.publicKey.rsa_encrypt_sign, 512, userid, passphrase); + var key = openpgp.generateKeyPair({numBits: 512, userId: userid, passphrase: passphrase}); var info = '\npassphrase: ' + passphrase + '\n' + 'userid: ' + userid + '\n' + 'message: ' + message; diff --git a/test/general/key.js b/test/general/key.js index 234fd09d..cc03348d 100644 --- a/test/general/key.js +++ b/test/general/key.js @@ -634,7 +634,7 @@ var pgp_desktop_priv = expect(key.users[0].selfCertifications[0].preferredCompressionAlgorithms).to.eql([compr.zlib, compr.zip]); expect(key.users[0].selfCertifications[0].features).to.eql(openpgp.config.integrity_protect ? [1] : null); // modification detection } - var key = openpgp.generateKeyPair(openpgp.enums.publicKey.rsa_encrypt_sign, 512, 'test', 'hello'); + var key = openpgp.generateKeyPair({numBits: 512, userId: 'test', passphrase: 'hello'}); testPref(key.key); testPref(openpgp.key.readArmored(key.publicKeyArmored).keys[0]); }); @@ -653,5 +653,12 @@ var pgp_desktop_priv = expect(primUser.selfCertificate).to.be.an.instanceof(openpgp.packet.Signature); }); + it('Generated key is not unlocked by default', function() { + var key = openpgp.generateKeyPair({numBits: 512, userId: 'test', passphrase: '123'}); + var msg = openpgp.message.fromText('hello').encrypt([key.key]); + msg = msg.decrypt.bind(msg, key.key); + expect(msg).to.throw('Private key is not decrypted.'); + }); + }); diff --git a/test/worker/api.js b/test/worker/api.js index 159ba31b..3c87cfa2 100644 --- a/test/worker/api.js +++ b/test/worker/api.js @@ -398,7 +398,7 @@ describe('High level API', function() { describe('Key generation', function() { it('Generate 1024-bit RSA/RSA key async', function (done) { - openpgp.generateKeyPair(openpgp.enums.publicKey.rsa_encrypt_sign, 1024, 'Test McTestington ', 'hello world', function(err, data) { + openpgp.generateKeyPair({numBits: 1024, userId: 'Test McTestington ', passphrase: 'hello world'}, function(err, data) { expect(err).to.not.exist; expect(data).to.exist; expect(data.publicKeyArmored).to.match(/^-----BEGIN PGP PUBLIC/); @@ -409,7 +409,7 @@ describe('High level API', function() { }); it('Generate 1024-bit RSA/RSA key sync', function () { - var key = openpgp.generateKeyPair(openpgp.enums.publicKey.rsa_encrypt_sign, 1024, 'Test McTestington ', 'hello world'); + var key = openpgp.generateKeyPair({numBits: 1024, userId: 'Test McTestington ', passphrase: 'hello world'}); expect(key).to.exist; expect(key.publicKeyArmored).to.match(/^-----BEGIN PGP PUBLIC/); expect(key.privateKeyArmored).to.match(/^-----BEGIN PGP PRIVATE/);