Move methods of Key and Message class to prototype. Clean up openpgp.js file.

This commit is contained in:
Thomas Oberndörfer 2013-12-02 14:54:05 +01:00
parent 89eb5dff2a
commit be96de5188
5 changed files with 1858 additions and 2419 deletions

File diff suppressed because one or more lines are too long

View File

@ -22,21 +22,23 @@ var config = require('./config');
/**
* @class
* @classdesc Class that represents an OpenPGP key. Must contain a master key.
* @param {packetlist} packetlist [description]
* Can contain additional subkeys, signatures,
* user ids, user attributes.
* @classdesc Class that represents an OpenPGP key. Must contain a primary key.
* Can contain additional subkeys, signatures, user ids, user attributes.
* @param {packetlist} packetlist The packets that form this key
*/
function key(packetlist) {
function Key(packetlist) {
if (!(this instanceof Key)) {
return new Key(packetlist);
}
this.packets = packetlist || new packet.list();
}
/**
* Returns the primary key packet (secret or public)
* @returns {packet_secret_key|packet_public_key|null}
*/
this.getKeyPacket = function() {
Key.prototype.getKeyPacket = function() {
for (var i = 0; i < this.packets.length; i++) {
if (this.packets[i].tag == enums.packet.public_key ||
this.packets[i].tag == enums.packet.secret_key) {
@ -44,13 +46,13 @@ var config = require('./config');
}
}
return null;
}
};
/**
* Returns all the private and public subkey packets
* @returns {[public_subkey|secret_subkey]}
*/
this.getSubkeyPackets = function() {
Key.prototype.getSubkeyPackets = function() {
var subkeys = [];
@ -62,28 +64,28 @@ var config = require('./config');
}
return subkeys;
}
};
/**
* Returns all the private and public key and subkey packets
* @returns {[public_subkey|secret_subkey|packet_secret_key|packet_public_key]}
*/
this.getAllKeyPackets = function() {
Key.prototype.getAllKeyPackets = function() {
return [this.getKeyPacket()].concat(this.getSubkeyPackets());
}
};
/**
* Returns key IDs of all key packets
* @returns {[keyid]}
*/
this.getKeyIds = function() {
Key.prototype.getKeyIds = function() {
var keyIds = [];
var keys = this.getAllKeyPackets();
for (var i = 0; i < keys.length; i++) {
keyIds.push(keys[i].getKeyId());
}
return keyIds;
}
};
function findKey(keys, keyIds) {
for (var i = 0; i < keys.length; i++) {
@ -102,44 +104,44 @@ var config = require('./config');
* @param {[keyid]} keyIds
* @return {public_subkey|packet_public_key|null}
*/
this.getPublicKeyPacket = function(keyIds) {
Key.prototype.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
* @param {[keyid]} keyIds
* @return {secret_subkey|packet_secret_key|null}
*/
this.getPrivateKeyPacket = function(keyIds) {
Key.prototype.getPrivateKeyPacket = function(keyIds) {
var keys = this.packets.filterByTag(enums.packet.secret_key, enums.packet.secret_subkey);
return findKey(keys, keyIds);
}
};
/**
* Returns true if this is a public key
* @return {Boolean}
*/
this.isPublic = function() {
Key.prototype.isPublic = function() {
var publicKeyPackets = this.packets.filterByTag(enums.packet.public_key);
return publicKeyPackets.length ? true : false;
}
};
/**
* Returns true if this is a private key
* @return {Boolean}
*/
this.isPrivate = function() {
Key.prototype.isPrivate = function() {
var privateKeyPackets = this.packets.filterByTag(enums.packet.private_key);
return privateKeyPackets.length ? true : false;
}
};
/**
* Returns key as public key
* @return {key} public key
*/
this.toPublic = function() {
Key.prototype.toPublic = function() {
for (var i = 0; i < this.packets.length; i++) {
if (this.packets[i].tag == enums.packet.secret_key) {
var bytes = this.packets[i].writePublicKey();
@ -155,22 +157,22 @@ var config = require('./config');
}
}
return this;
}
};
/**
* Returns ASCII armored text of key
* @return {String} ASCII armor
*/
this.armor = function() {
Key.prototype.armor = function() {
var type = this.isPublic() ? enums.armor.public_key : enums.armor.private_key;
return armor.encode(type, this.packets.write());
}
};
/**
* Returns first key packet that is available for signing
* @return {public_subkey|secret_subkey|packet_secret_key|packet_public_key|null}
*/
this.getSigningKeyPacket = function() {
Key.prototype.getSigningKeyPacket = function() {
var signing = [ enums.publicKey.rsa_encrypt_sign, enums.publicKey.rsa_sign, enums.publicKey.dsa];
@ -187,23 +189,23 @@ var config = require('./config');
}
return null;
}
};
/**
* Returns preferred signature hash algorithm of this key
* @return {String}
*/
function getPreferredSignatureHashAlgorithm() {
Key.prototype.getPreferredSignatureHashAlgorithm = function() {
//TODO implement: https://tools.ietf.org/html/rfc4880#section-5.2.3.8
//separate private key preference from digest preferences
return config.prefer_hash_algorithm;
}
};
/**
* Returns the first valid encryption key packet for this key
* @returns {public_subkey|secret_subkey|packet_secret_key|packet_public_key|null} key packet or null if no encryption key has been found
*/
this.getEncryptionKeyPacket = function() {
Key.prototype.getEncryptionKeyPacket = function() {
// V4: by convention subkeys are prefered for encryption service
// V3: keys MUST NOT have subkeys
var isValidEncryptionKey = function(key) {
@ -227,21 +229,21 @@ var config = require('./config');
return primaryKey;
}
return null;
}
};
/**
* Decrypts all secret key and subkey packets
* @param {String} passphrase
* @return {Boolean} true if all key and subkey packets decrypted successfully
*/
this.decrypt = function(passphrase) {
Key.prototype.decrypt = function(passphrase) {
var keys = this.packets.filterByTag(enums.packet.secret_key, enums.packet.secret_subkey);
for (var i = 0; i < keys.length; i++) {
var success = keys[i].decrypt(passphrase);
if (!success) return false;
}
return true;
}
};
/**
* Decrypts specific key packets by key ID
@ -249,7 +251,7 @@ var config = require('./config');
* @param {String} passphrase
* @return {Boolean} true if all key packets decrypted successfully
*/
this.decryptKeyPacket = function(keyIds, passphrase) {
Key.prototype.decryptKeyPacket = function(keyIds, 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++) {
@ -262,32 +264,32 @@ var config = require('./config');
}
}
return true;
}
};
// TODO
this.verify = function() {
Key.prototype.verify = function() {
}
};
// TODO
this.revoke = function() {
Key.prototype.revoke = function() {
}
};
}
/**
* reads an OpenPGP armored text and returns a key object
* @param {String} armoredText text to be parsed
* @return {key} new key object
*/
key.readArmored = function(armoredText) {
function readArmored(armoredText) {
//TODO how do we want to handle bad text? Exception throwing
//TODO don't accept non-key armored texts
var input = armor.decode(armoredText).data;
var packetlist = new packet.list();
packetlist.read(input);
var newKey = new key(packetlist);
var newKey = new Key(packetlist);
return newKey;
}
module.exports = key;
exports.Key = Key;
exports.readArmored = readArmored;

View File

@ -24,31 +24,36 @@ var util = require('./util');
/**
* @class
* @classdesc A generic message containing one or more literal packets.
* @classdesc Class that represents an OpenPGP message.
* Can be an encrypted message, signed message, compressed message or literal message
* See http://tools.ietf.org/html/rfc4880#section-11.3
*/
function message(packetlist) {
function Message(packetlist) {
if (!(this instanceof Message)) {
return new Message(packetlist);
}
this.packets = packetlist || new packet.list();
}
/**
* Returns the key IDs of the keys to which the session key is encrypted
* @return {[keyId]} array of keyid objects
*/
this.getEncryptionKeyIds = function() {
Message.prototype.getEncryptionKeyIds = function() {
var keyIds = [];
var pkESKeyPacketlist = this.packets.filterByTag(enums.packet.public_key_encrypted_session_key);
pkESKeyPacketlist.forEach(function(packet) {
keyIds.push(packet.publicKeyId);
});
return keyIds;
}
};
/**
* Returns the key IDs of the keys that signed the message
* @return {[keyId]} array of keyid objects
*/
this.getSigningKeyIds = function() {
Message.prototype.getSigningKeyIds = function() {
var keyIds = [];
var msg = this.unwrapCompressed();
// search for one pass signatures
@ -64,14 +69,14 @@ function message(packetlist) {
});
}
return keyIds;
}
};
/**
* Decrypt the message
* @param {key} privateKey private key with decrypted secret data
* @return {[message]} new message with decrypted content
*/
this.decrypt = function(privateKey) {
Message.prototype.decrypt = function(privateKey) {
var encryptionKeyIds = this.getEncryptionKeyIds();
if (!encryptionKeyIds.length) {
// nothing to decrypt return unmodified message
@ -93,25 +98,25 @@ function message(packetlist) {
if (symEncryptedPacketlist.length !== 0) {
var symEncryptedPacket = symEncryptedPacketlist[0];
symEncryptedPacket.decrypt(pkESKeyPacket.sessionKeyAlgorithm, pkESKeyPacket.sessionKey);
return new message(symEncryptedPacket.packets);
}
return new Message(symEncryptedPacket.packets);
}
}
};
/**
* Get literal data that is the body of the message
* @return {String|null} literal body of the message as string
*/
this.getLiteralData = function() {
Message.prototype.getLiteralData = function() {
var literal = this.packets.findPacket(enums.packet.literal);
return literal && literal.data || null;
}
};
/**
* Get literal data as text
* @return {String|null} literal body of the message interpreted as text
*/
this.getText = function() {
Message.prototype.getText = function() {
var literal = this.packets.findPacket(enums.packet.literal);
if (literal) {
var data = literal.data;
@ -124,14 +129,14 @@ function message(packetlist) {
} else {
return null;
}
}
};
/**
* Encrypt the message
* @param {[key]} keys array of keys, used to encrypt the message
* @return {[message]} new message with encrypted content
*/
this.encrypt = function(keys) {
Message.prototype.encrypt = function(keys) {
var packetlist = new packet.list();
//TODO get preferred algo from signature
var sessionKey = crypto.generateSessionKey(enums.read(enums.symmetric, config.encryption_cipher));
@ -158,15 +163,15 @@ function message(packetlist) {
//TODO get preferred algo from signature
symEncryptedPacket.encrypt(enums.read(enums.symmetric, config.encryption_cipher), sessionKey);
packetlist.push(symEncryptedPacket);
return new message(packetlist);
}
return new Message(packetlist);
};
/**
* Sign the message (the literal data packet of the message)
* @param {[key]} privateKey private keys with decrypted secret key data for signing
* @return {message} new message with signed content
*/
this.sign = function(privateKeys) {
Message.prototype.sign = function(privateKeys) {
var packetlist = new packet.list();
@ -200,15 +205,15 @@ function message(packetlist) {
packetlist.push(signaturePacket);
}
return new message(packetlist);
}
return new Message(packetlist);
};
/**
* Verify message signatures
* @param {[key]} publicKeys public keys to verify signatures
* @return {[{'keyid': keyid, 'valid': Boolean}]} list of signer's keyid and validity of signature
*/
this.verify = function(publicKeys) {
Message.prototype.verify = function(publicKeys) {
var result = [];
var msg = this.unwrapCompressed();
var literalDataList = msg.packets.filterByTag(enums.packet.literal);
@ -227,119 +232,41 @@ function message(packetlist) {
}
});
return result;
}
};
/**
* Unwrap compressed message
* @return {message} message Content of compressed message
*/
this.unwrapCompressed = function() {
Message.prototype.unwrapCompressed = function() {
var compressed = this.packets.filterByTag(enums.packet.compressed);
if (compressed.length) {
return new message(compressed[0].packets);
return new Message(compressed[0].packets);
} else {
return this;
}
}
};
/**
* Returns ASCII armored text of message
* @return {String} ASCII armor
*/
this.armor = function() {
Message.prototype.armor = function() {
return armor.encode(enums.armor.message, this.packets.write());
}
/**
* Decrypts a message and generates user interface message out of the found.
* MDC will be verified as well as message signatures
* @param {openpgp_msg_privatekey} private_key the private the message is encrypted with (corresponding to the session key)
* @param {openpgp_packet_encryptedsessionkey} sessionkey the session key to be used to decrypt the message
* @param {openpgp_msg_publickey} pubkey Array of public keys to check signature against. If not provided, checks local keystore.
* @return {String} plaintext of the message or null on error
*/
function decryptAndVerifySignature(private_key, sessionkey, pubkey) {
if (private_key == null || sessionkey == null || sessionkey == "")
return null;
var decrypted = sessionkey.decrypt(this, private_key.keymaterial);
if (decrypted == null)
return null;
var packet;
var position = 0;
var len = decrypted.length;
var validSignatures = new Array();
util.print_debug_hexstr_dump("openpgp.msg.messge decrypt:\n", decrypted);
var messages = openpgp.read_messages_dearmored({
text: decrypted,
openpgp: decrypted
});
for (var m in messages) {
if (messages[m].data) {
this.text = messages[m].data;
}
if (messages[m].signature) {
validSignatures.push(messages[m].verifySignature(pubkey));
}
}
return {
text: this.text,
validSignatures: validSignatures
};
}
/**
* Verifies a message signature. This function can be called after read_message if the message was signed only.
* @param {openpgp_msg_publickey} pubkey Array of public keys to check signature against. If not provided, checks local keystore.
* @return {boolean} true if the signature was correct; otherwise false
*/
function verifySignature(pubkey) {
var result = false;
if (this.signature.tagType == 2) {
if (!pubkey || pubkey.length == 0) {
var pubkey;
if (this.signature.version == 4) {
pubkey = openpgp.keyring.getPublicKeysForKeyId(this.signature.issuerKeyId);
} else if (this.signature.version == 3) {
pubkey = openpgp.keyring.getPublicKeysForKeyId(this.signature.keyId);
} else {
util.print_error("unknown signature type on message!");
return false;
}
}
if (pubkey.length == 0)
util.print_warning("Unable to verify signature of issuer: " + util.hexstrdump(this.signature.issuerKeyId) +
". Public key not found in keyring.");
else {
for (var i = 0; i < pubkey.length; i++) {
var tohash = this.text.replace(/\r\n/g, "\n").replace(/\n/g, "\r\n");
if (this.signature.verify(tohash, pubkey[i])) {
util.print_info("Found Good Signature from " + pubkey[i].obj.userIds[0].text + " (0x" + util.hexstrdump(
pubkey[i].obj.getKeyId()).substring(8) + ")");
result = true;
} else {
util.print_error("Signature verification failed: Bad Signature from " + pubkey[i].obj.userIds[0].text +
" (0x" + util.hexstrdump(pubkey[0].obj.getKeyId()).substring(8) + ")");
}
}
}
}
return result;
}
}
/**
* reads an OpenPGP armored message and returns a message object
* @param {String} armoredText text to be parsed
* @return {message} new message object
*/
message.readArmored = function(armoredText) {
function readArmored(armoredText) {
//TODO how do we want to handle bad text? Exception throwing
//TODO don't accept non-message armored texts
var input = armor.decode(armoredText).data;
var packetlist = new packet.list();
packetlist.read(input);
var newMessage = new message(packetlist);
var newMessage = new Message(packetlist);
return newMessage;
}
@ -348,14 +275,16 @@ message.readArmored = function(armoredText) {
* @param {String} text
* @return {message} new message object
*/
message.fromText = function(text) {
function fromText(text) {
var literalDataPacket = new packet.literal();
// text will be converted to UTF8
literalDataPacket.set(text);
var literalDataPacketlist = new packet.list();
literalDataPacketlist.push(literalDataPacket);
var newMessage = new message(literalDataPacketlist);
var newMessage = new Message(literalDataPacketlist);
return newMessage;
}
module.exports = message;
exports.Message = Message;
exports.readArmored = readArmored;
exports.fromText = fromText;

View File

@ -16,26 +16,17 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
/**
* @fileoverview The openpgp base class should provide all of the functionality
* @fileoverview The openpgp base module should provide all of the functionality
* to consume the openpgp.js library. All additional classes are documented
* for extending and developing on top of the base library.
*/
var armor = require('./encoding/armor.js');
var packet = require('./packet');
var util = require('./util');
var enums = require('./enums.js');
var config = require('./config');
var message = require('./message.js');
/**
* GPG4Browsers Core interface. A single instance is hold
* from the beginning. To use this library call "openpgp.init()"
* @alias openpgp
* @class
* @classdesc Main Openpgp.js class. Use this to initiate and make all calls to this library.
*/
function _openpgp() {
/**
* Encrypts message text with keys
@ -97,14 +88,16 @@ function _openpgp() {
return null;
}
function verifyMessage(publicKeys, messagePacketlist) {
function signClearMessage(privateKeys, text) {
}
function signMessage(privateKey, messagePacketlist) {
function verifyClearSignedMessage(publicKeys, message) {
}
/**
* TODO: update this doc
* generates a new key pair for openpgp. Beta stage. Currently only
@ -167,116 +160,6 @@ function _openpgp() {
return armored;
}
/**
* creates a binary string representation of an encrypted and signed message.
* The message will be encrypted with the public keys specified and signed
* with the specified private key.
* @param {Object} privatekey {obj: [openpgp_msg_privatekey]} Private key
* to be used to sign the message
* @param {Object[]} publickeys An arraf of {obj: [openpgp_msg_publickey]}
* - public keys to be used to encrypt the message
* @param {String} messagetext message text to encrypt and sign
* @return {String} a binary string representation of the message which
* can be OpenPGP armored
*/
function write_signed_and_encrypted_message(privatekey, publickeys, messagetext) {
var result = "";
var i;
var literal = new openpgp_packet_literaldata().write_packet(messagetext.replace(/\r\n/g, "\n").replace(/\n/g,
"\r\n"));
util.print_debug_hexstr_dump("literal_packet: |" + literal + "|\n", literal);
for (i = 0; i < publickeys.length; i++) {
var onepasssignature = new openpgp_packet_onepasssignature();
var onepasssigstr = "";
if (i === 0)
onepasssigstr = onepasssignature.write_packet(1, openpgp.config.config.prefer_hash_algorithm, privatekey, false);
else
onepasssigstr = onepasssignature.write_packet(1, openpgp.config.config.prefer_hash_algorithm, privatekey, false);
util.print_debug_hexstr_dump("onepasssigstr: |" + onepasssigstr + "|\n", onepasssigstr);
var datasignature = new openpgp_packet_signature().write_message_signature(1, messagetext.replace(/\r\n/g, "\n").replace(
/\n/g, "\r\n"), privatekey);
util.print_debug_hexstr_dump("datasignature: |" + datasignature.openpgp + "|\n", datasignature.openpgp);
if (i === 0) {
result = onepasssigstr + literal + datasignature.openpgp;
} else {
result = onepasssigstr + result + datasignature.openpgp;
}
}
util.print_debug_hexstr_dump("signed packet: |" + result + "|\n", result);
// signatures done.. now encryption
var sessionkey = openpgp_crypto_generateSessionKey(openpgp.config.config.encryption_cipher);
var result2 = "";
// creating session keys for each recipient
for (i = 0; i < publickeys.length; i++) {
var pkey = publickeys[i].getEncryptionKey();
if (pkey === null) {
util.print_error("no encryption key found! Key is for signing only.");
return null;
}
result2 += new openpgp_packet_encryptedsessionkey().
write_pub_key_packet(
pkey.getKeyId(),
pkey.MPIs,
pkey.publicKeyAlgorithm,
openpgp.config.config.encryption_cipher,
sessionkey);
}
if (openpgp.config.config.integrity_protect) {
result2 += new openpgp_packet_encryptedintegrityprotecteddata().write_packet(openpgp.config.config.encryption_cipher,
sessionkey, result);
} else {
result2 += new openpgp_packet_encrypteddata().write_packet(openpgp.config.config.encryption_cipher, sessionkey,
result);
}
return armor.encode(3, result2, null, null);
}
/**
* creates a binary string representation of an encrypted message.
* The message will be encrypted with the public keys specified
* @param {Object[]} publickeys An array of {obj: [openpgp_msg_publickey]}
* -public keys to be used to encrypt the message
* @param {String} messagetext message text to encrypt
* @return {String} a binary string representation of the message
* which can be OpenPGP armored
*/
function write_encrypted_message(publickeys, messagetext) {
var result = "";
var literal = new openpgp_packet_literaldata().write_packet(messagetext.replace(/\r\n/g, "\n").replace(/\n/g,
"\r\n"));
util.print_debug_hexstr_dump("literal_packet: |" + literal + "|\n", literal);
result = literal;
// signatures done.. now encryption
var sessionkey = openpgp_crypto_generateSessionKey(openpgp.config.config.encryption_cipher);
var result2 = "";
// creating session keys for each recipient
for (var i = 0; i < publickeys.length; i++) {
var pkey = publickeys[i].getEncryptionKey();
if (pkey === null) {
util.print_error("no encryption key found! Key is for signing only.");
return null;
}
result2 += new openpgp_packet_encryptedsessionkey().
write_pub_key_packet(
pkey.getKeyId(),
pkey.MPIs,
pkey.publicKeyAlgorithm,
openpgp.config.config.encryption_cipher,
sessionkey);
}
if (openpgp.config.config.integrity_protect) {
result2 += new openpgp_packet_encryptedintegrityprotecteddata().write_packet(openpgp.config.config.encryption_cipher,
sessionkey, result);
} else {
result2 += new openpgp_packet_encrypteddata().write_packet(openpgp.config.config.encryption_cipher, sessionkey,
result);
}
return armor.encode(3, result2, null, null);
}
/**
* creates a binary string representation a signed message.
* The message will be signed with the specified private key.
@ -288,6 +171,7 @@ function _openpgp() {
* armored(openpgp) and a text representation of the message (text).
* This can be directly used to OpenPGP armor the message
*/
/*
function write_signed_message(privatekey, messagetext) {
var sig = new openpgp_packet_signature().write_message_signature(1, messagetext.replace(/\r\n/g, "\n").replace(/\n/,
"\r\n"), privatekey);
@ -298,14 +182,12 @@ function _openpgp() {
};
return armor.encode(2, result, null, null);
}
*/
this.generateKeyPair = generateKeyPair;
this.write_signed_message = write_signed_message;
this.signAndEncryptMessage = signAndEncryptMessage;
this.decryptAndVerifyMessage = decryptAndVerifyMessage
this.encryptMessage = encryptMessage;
this.decryptMessage = decryptMessage;
}
module.exports = new _openpgp();
exports.encryptMessage = encryptMessage;
exports.signAndEncryptMessage = signAndEncryptMessage;
exports.decryptMessage = decryptMessage;
exports.decryptAndVerifyMessage = decryptAndVerifyMessage
exports.signClearMessage = signClearMessage;
exports.verifyClearSignedMessage = verifyClearSignedMessage;
exports.generateKeyPair = generateKeyPair;

File diff suppressed because one or more lines are too long