Add decryptKeyPacket to key class. Used to decrypt specific key

packets and replacing the unlock mechanism. Decrypt method of packet_secret_key
returns boolean to indicate successful decryption. Add config to util class
and cleanup comments. Update tests.
This commit is contained in:
Thomas Oberndörfer 2013-11-19 13:50:14 +01:00
parent 097e602fd0
commit 93376b6e13
8 changed files with 237 additions and 248 deletions

File diff suppressed because one or more lines are too long

View File

@ -32,8 +32,6 @@ var config = require('./config');
this.packets = packetlist || new packet.list(); this.packets = packetlist || new packet.list();
this.passphrase = null;
/** /**
* Returns the primary key packet (secret or public) * Returns the primary key packet (secret or public)
* @returns {packet_secret_key|packet_public_key|null} * @returns {packet_secret_key|packet_public_key|null}
@ -87,18 +85,11 @@ var config = require('./config');
return keyIds; return keyIds;
} }
/** function findKey(keys, keyIds) {
* Returns first key packet for given array of key IDs
* @param {[keyid]} keyIds
* @return {public_subkey|secret_subkey|packet_secret_key|packet_public_key|null}
*/
this.getKeyPacket = function(keyIds) {
var keys = this.getAllKeyPackets();
for (var i = 0; i < keys.length; i++) { for (var i = 0; i < keys.length; i++) {
var keyId = keys[i].getKeyId(); var keyId = keys[i].getKeyId();
for (var j = 0; j < keyIds.length; j++) { for (var j = 0; j < keyIds.length; j++) {
if (keyId.equals(keyIds[j])) { if (keyId.equals(keyIds[j])) {
//TODO return only verified keys
return keys[i]; return keys[i];
} }
} }
@ -106,28 +97,24 @@ var config = require('./config');
return null; return null;
} }
/**
* Returns first public key packet for given array of key IDs
* @param {[keyid]} keyIds
* @return {public_subkey|packet_public_key|null}
*/
this.getPublicKeyPacket = function(keyIds) {
var keys = this.packets.filterByTag(enums.packet.public_key, enums.packet.public_subkey);
return findKey(keys, keyIds);
}
/** /**
* Returns first private key packet for given array of key IDs * Returns first private key packet for given array of key IDs
* @param {[keyid]} keyIds * @param {[keyid]} keyIds
* @param {Boolean} decrypted decrypt private key packet
* @return {secret_subkey|packet_secret_key|null} * @return {secret_subkey|packet_secret_key|null}
*/ */
this.getPrivateKeyPacket = function(keyIds, decrypted) { this.getPrivateKeyPacket = function(keyIds) {
var keys = this.packets.filterByTag(enums.packet.secret_key, enums.packet.secret_subkey); var keys = this.packets.filterByTag(enums.packet.secret_key, enums.packet.secret_subkey);
for (var i = 0; i < keys.length; i++) { return findKey(keys, keyIds);
var keyId = keys[i].getKeyId();
for (var j = 0; j < keyIds.length; j++) {
if (keyId.equals(keyIds[j])) {
//TODO return only verified keys
if (decrypted) {
if (!this.passphrase) throw new Error('No passphrase to decrypt key.');
keys[i].decrypt(this.passphrase);
}
return keys[i];
}
}
}
return null;
} }
/** /**
@ -214,27 +201,36 @@ var config = require('./config');
/** /**
* Decrypts all secret key and subkey packets * Decrypts all secret key and subkey packets
* @param {String} passphrase * @param {String} passphrase
* @return {undefined} * @return {Boolean} true if all key and subkey packets decrypted successfully
*/ */
this.decrypt = function(passphrase) { this.decrypt = function(passphrase) {
//TODO return value var keys = this.packets.filterByTag(enums.packet.secret_key, enums.packet.secret_subkey);
var keys = this.getAllKeyPackets(); for (var i = 0; i < keys.length; i++) {
for (var i in keys) { var success = keys[i].decrypt(passphrase);
if (keys[i].tag == enums.packet.secret_subkey || if (!success) return false;
keys[i].tag == enums.packet.secret_key) {
if (!passphrase && !this.passphrase) throw new Error('No passphrase to decrypt key.');
keys[i].decrypt(passphrase || this.passphrase);
}
} }
return true;
} }
/** /**
* Unlocks the key with passphrase, decryption of secret keys deferred. This allows to decrypt the required private key packets on demand * Decrypts specific key packets by key ID
* @param {[keyid]} keyIds
* @param {String} passphrase * @param {String} passphrase
* @return {undefined} * @return {Boolean} true if all key packets decrypted successfully
*/ */
this.unlock = function(passphrase) { this.decryptKeyPacket = function(keyIds, passphrase) {
this.passphrase = passphrase; //TODO return value
var keys = this.packets.filterByTag(enums.packet.secret_key, enums.packet.secret_subkey);
for (var i = 0; i < keys.length; i++) {
var keyId = keys[i].getKeyId();
for (var j = 0; j < keyIds.length; j++) {
if (keyId.equals(keyIds[j])) {
var success = keys[i].decrypt(passphrase);
if (!success) return false;
}
}
}
return true;
} }
// TODO // TODO

View File

@ -45,7 +45,7 @@ function message(packetlist) {
/** /**
* Decrypt the message * Decrypt the message
* @param {key} privateKey unlocked private key for which the message is encrypted (corresponding to the session key) * @param {key} privateKey private key with decrypted secret data
* @return {[message]} new message with decrypted content * @return {[message]} new message with decrypted content
*/ */
this.decrypt = function(privateKey) { this.decrypt = function(privateKey) {
@ -54,7 +54,8 @@ function message(packetlist) {
// nothing to decrypt return unmodified message // nothing to decrypt return unmodified message
return this; return this;
} }
var privateKeyPacket = privateKey.getPrivateKeyPacket(encryptionKeyIds, true); var privateKeyPacket = privateKey.getPrivateKeyPacket(encryptionKeyIds);
if (!privateKeyPacket.isDecrypted) throw new Error('Private key is not decrypted.');
var pkESKeyPacketlist = this.packets.filterByTag(enums.packet.public_key_encrypted_session_key); var pkESKeyPacketlist = this.packets.filterByTag(enums.packet.public_key_encrypted_session_key);
var pkESKeyPacket; var pkESKeyPacket;
for (var i = 0; i < pkESKeyPacketlist.length; i++) { for (var i = 0; i < pkESKeyPacketlist.length; i++) {

View File

@ -56,7 +56,7 @@ function _openpgp() {
/** /**
* decrypts message * decrypts message
* @param {key} privateKey unlocked private key * @param {key} privateKey private key with decrypted secret key data
* @param {message} message the message object with the encrypted data * @param {message} message the message object with the encrypted data
* @return {String|null} decrypted message as as native JavaScript string * @return {String|null} decrypted message as as native JavaScript string
* or null if no literal data found * or null if no literal data found

View File

@ -67,7 +67,7 @@ function packet_secret_key() {
var hash = hashfn(cleartext); var hash = hashfn(cleartext);
if (hash != hashtext) if (hash != hashtext)
throw new Error("Hash mismatch."); return new Error("Hash mismatch.");
var mpis = crypto.getPrivateMpiCount(algorithm); var mpis = crypto.getPrivateMpiCount(algorithm);
@ -126,9 +126,10 @@ function packet_secret_key() {
// - Plain or encrypted multiprecision integers comprising the secret // - Plain or encrypted multiprecision integers comprising the secret
// key data. These algorithm-specific fields are as described // key data. These algorithm-specific fields are as described
// below. // below.
var parsedMPI = parse_cleartext_mpi('mod', bytes.substr(1), this.algorithm);
this.mpi = this.mpi.concat(parse_cleartext_mpi('mod', bytes.substr(1), if (parsedMPI instanceof Error)
this.algorithm)); throw parsedMPI;
this.mpi = this.mpi.concat(parsedMPI);
this.isDecrypted = true; this.isDecrypted = true;
} }
@ -200,11 +201,12 @@ function packet_secret_key() {
* *
* @param {String} str_passphrase The passphrase for this private key * @param {String} str_passphrase The passphrase for this private key
* as string * as string
* @return {Boolean} True if the passphrase was correct; false if not * @return {Boolean} True if the passphrase was correct or MPI already
* decrypted; false if not
*/ */
this.decrypt = function(passphrase) { this.decrypt = function(passphrase) {
if (this.isDecrypted) if (this.isDecrypted)
return; return true;
var i = 0, var i = 0,
symmetric, symmetric,
@ -249,10 +251,12 @@ function packet_secret_key() {
'sha1' : 'sha1' :
'mod'; 'mod';
var parsedMPI = parse_cleartext_mpi(hash, cleartext, this.algorithm);
this.mpi = this.mpi.concat(parse_cleartext_mpi(hash, cleartext, if (parsedMPI instanceof Error)
this.algorithm)); return false;
this.mpi = this.mpi.concat(parsedMPI);
this.isDecrypted = true; this.isDecrypted = true;
return true;
}; };
this.generate = function(bits, passphrase) { this.generate = function(bits, passphrase) {

View File

@ -15,6 +15,8 @@
// License along with this library; if not, write to the Free Software // License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
var config = require('../config');
var Util = function() { var Util = function() {
this.readNumber = function(bytes) { this.readNumber = function(bytes) {
@ -221,16 +223,11 @@ var Util = function() {
/** /**
* Helper function to print a debug message. Debug * Helper function to print a debug message. Debug
* messages are only printed if * messages are only printed if
* openpgp.config.debug is set to true. The calling * openpgp.config.debug is set to true.
* Javascript context MUST define
* a "showMessages(text)" function. Line feeds ('\n')
* are automatically converted to HTML line feeds '<br/>'
* @param {String} str String of the debug message * @param {String} str String of the debug message
* @return {String} An HTML tt entity containing a paragraph with a
* style attribute where the debug message is HTMLencoded in.
*/ */
this.print_debug = function(str) { this.print_debug = function(str) {
if (this.debug) { if (config.debug) {
console.log(str); console.log(str);
} }
}; };
@ -238,17 +235,12 @@ var Util = function() {
/** /**
* Helper function to print a debug message. Debug * Helper function to print a debug message. Debug
* messages are only printed if * messages are only printed if
* openpgp.config.debug is set to true. The calling * openpgp.config.debug is set to true.
* Javascript context MUST define
* a "showMessages(text)" function. Line feeds ('\n')
* are automatically converted to HTML line feeds '<br/>'
* Different than print_debug because will call hexstrdump iff necessary. * Different than print_debug because will call hexstrdump iff necessary.
* @param {String} str String of the debug message * @param {String} str String of the debug message
* @return {String} An HTML tt entity containing a paragraph with a
* style attribute where the debug message is HTMLencoded in.
*/ */
this.print_debug_hexstr_dump = function(str, strToHex) { this.print_debug_hexstr_dump = function(str, strToHex) {
if (this.debug) { if (config.debug) {
str = str + this.hexstrdump(strToHex); str = str + this.hexstrdump(strToHex);
console.log(str); console.log(str);
} }
@ -256,30 +248,20 @@ var Util = function() {
/** /**
* Helper function to print an error message. * Helper function to print an error message.
* The calling Javascript context MUST define
* a "showMessages(text)" function. Line feeds ('\n')
* are automatically converted to HTML line feeds '<br/>'
* @param {String} str String of the error message * @param {String} str String of the error message
* @return {String} A HTML paragraph entity with a style attribute
* containing the HTML encoded error message
*/ */
this.print_error = function(str) { this.print_error = function(str) {
if (this.debug) if (config.debug)
throw str; throw str;
console.log(str); console.log(str);
}; };
/** /**
* Helper function to print an info message. * Helper function to print an info message.
* The calling Javascript context MUST define
* a "showMessages(text)" function. Line feeds ('\n')
* are automatically converted to HTML line feeds '<br/>'.
* @param {String} str String of the info message * @param {String} str String of the info message
* @return {String} A HTML paragraph entity with a style attribute
* containing the HTML encoded info message
*/ */
this.print_info = function(str) { this.print_info = function(str) {
if (this.debug) if (config.debug)
console.log(str); console.log(str);
}; };

View File

@ -17,7 +17,9 @@ unit.register("Key generation/encryption/decryption", function() {
var msg = openpgp.message.readArmored(encrypted); var msg = openpgp.message.readArmored(encrypted);
privKey.unlock(passphrase); var keyids = msg.getEncryptionKeyIds();
privKey.decryptKeyPacket(keyids, passphrase);
try { try {
var decrypted = openpgp.decryptMessage(privKey, msg); var decrypted = openpgp.decryptMessage(privKey, msg);
@ -34,7 +36,7 @@ unit.register("Key generation/encryption/decryption", function() {
return result; return result;
}); });
unit.register("Encryption/decryption", function() { unit.register("Message encryption/decryption", function() {
var openpgp = require('../../'); var openpgp = require('../../');
var result = []; var result = [];
@ -117,11 +119,29 @@ unit.register("Encryption/decryption", function() {
var privKey = openpgp.key.readArmored(priv_key); var privKey = openpgp.key.readArmored(priv_key);
privKey.unlock('hello world'); // get key IDs the message is encrypted for
var keyids = message.getEncryptionKeyIds();
var decrypted = openpgp.decryptMessage(privKey, message); // decrypt only required key packets
var success = privKey.decryptKeyPacket(keyids, 'hello what?')
result[0] = new unit.result('Encrypt plain text and afterwards decrypt leads to same result', plaintext == decrypted); result.push(new unit.result('Decrypting key packet with wrong password returns false', !success));
var decrypted, error;
try {
decrypted = openpgp.decryptMessage(privKey, message);
} catch (e) {
error = e;
}
result.push(new unit.result('Calling decryptMessage with not encrypted key packet leads to exception: \'' + (error || '') + '\'', error));
success = privKey.decryptKeyPacket(keyids, 'hello world');
result.push(new unit.result('Decrypting key packet with correct password returns true', success));
decrypted = openpgp.decryptMessage(privKey, message);
result.push(new unit.result('Encrypt plain text and afterwards decrypt leads to same result', plaintext == decrypted));
return result; return result;

File diff suppressed because one or more lines are too long