Move methods of Key and Message class to prototype. Clean up openpgp.js file.
This commit is contained in:
parent
89eb5dff2a
commit
be96de5188
1427
resources/openpgp.js
1427
resources/openpgp.js
File diff suppressed because one or more lines are too long
474
src/key.js
474
src/key.js
|
@ -22,272 +22,274 @@ var config = require('./config');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class
|
* @class
|
||||||
* @classdesc Class that represents an OpenPGP key. Must contain a master key.
|
* @classdesc Class that represents an OpenPGP key. Must contain a primary key.
|
||||||
* @param {packetlist} packetlist [description]
|
* Can contain additional subkeys, signatures, user ids, user attributes.
|
||||||
* Can contain additional subkeys, signatures,
|
* @param {packetlist} packetlist The packets that form this key
|
||||||
* user ids, user attributes.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function key(packetlist) {
|
function Key(packetlist) {
|
||||||
|
if (!(this instanceof Key)) {
|
||||||
|
return new Key(packetlist);
|
||||||
|
}
|
||||||
this.packets = packetlist || new packet.list();
|
this.packets = packetlist || new packet.list();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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}
|
||||||
*/
|
*/
|
||||||
this.getKeyPacket = function() {
|
Key.prototype.getKeyPacket = function() {
|
||||||
for (var i = 0; i < this.packets.length; i++) {
|
for (var i = 0; i < this.packets.length; i++) {
|
||||||
if (this.packets[i].tag == enums.packet.public_key ||
|
if (this.packets[i].tag == enums.packet.public_key ||
|
||||||
this.packets[i].tag == enums.packet.secret_key) {
|
this.packets[i].tag == enums.packet.secret_key) {
|
||||||
return this.packets[i];
|
return this.packets[i];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns all the private and public subkey packets
|
* Returns all the private and public subkey packets
|
||||||
* @returns {[public_subkey|secret_subkey]}
|
* @returns {[public_subkey|secret_subkey]}
|
||||||
*/
|
*/
|
||||||
this.getSubkeyPackets = function() {
|
Key.prototype.getSubkeyPackets = function() {
|
||||||
|
|
||||||
var subkeys = [];
|
var subkeys = [];
|
||||||
|
|
||||||
for (var i = 0; i < this.packets.length; i++) {
|
for (var i = 0; i < this.packets.length; i++) {
|
||||||
if (this.packets[i].tag == enums.packet.public_subkey ||
|
if (this.packets[i].tag == enums.packet.public_subkey ||
|
||||||
this.packets[i].tag == enums.packet.secret_subkey) {
|
this.packets[i].tag == enums.packet.secret_subkey) {
|
||||||
subkeys.push(this.packets[i]);
|
subkeys.push(this.packets[i]);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return subkeys;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
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() {
|
* Returns all the private and public key and subkey packets
|
||||||
return [this.getKeyPacket()].concat(this.getSubkeyPackets());
|
* @returns {[public_subkey|secret_subkey|packet_secret_key|packet_public_key]}
|
||||||
|
*/
|
||||||
|
Key.prototype.getAllKeyPackets = function() {
|
||||||
|
return [this.getKeyPacket()].concat(this.getSubkeyPackets());
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns key IDs of all key packets
|
||||||
|
* @returns {[keyid]}
|
||||||
|
*/
|
||||||
|
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) {
|
||||||
* Returns key IDs of all key packets
|
for (var i = 0; i < keys.length; i++) {
|
||||||
* @returns {[keyid]}
|
var keyId = keys[i].getKeyId();
|
||||||
*/
|
for (var j = 0; j < keyIds.length; j++) {
|
||||||
this.getKeyIds = function() {
|
if (keyId.equals(keyIds[j])) {
|
||||||
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++) {
|
|
||||||
var keyId = keys[i].getKeyId();
|
|
||||||
for (var j = 0; j < keyIds.length; j++) {
|
|
||||||
if (keyId.equals(keyIds[j])) {
|
|
||||||
return keys[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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
|
|
||||||
* @param {[keyid]} keyIds
|
|
||||||
* @return {secret_subkey|packet_secret_key|null}
|
|
||||||
*/
|
|
||||||
this.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() {
|
|
||||||
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() {
|
|
||||||
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() {
|
|
||||||
for (var i = 0; i < this.packets.length; i++) {
|
|
||||||
if (this.packets[i].tag == enums.packet.secret_key) {
|
|
||||||
var bytes = this.packets[i].writePublicKey();
|
|
||||||
var pubKeyPacket = new packet.public_key();
|
|
||||||
pubKeyPacket.read(bytes);
|
|
||||||
this.packets[i] = pubKeyPacket;
|
|
||||||
}
|
|
||||||
if (this.packets[i].tag == enums.packet.secret_subkey) {
|
|
||||||
var bytes = this.packets[i].writePublicKey();
|
|
||||||
var pubSubkeyPacket = new packet.public_subkey();
|
|
||||||
pubSubkeyPacket.read(bytes);
|
|
||||||
this.packets[i] = pubSubkeyPacket;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns ASCII armored text of key
|
|
||||||
* @return {String} ASCII armor
|
|
||||||
*/
|
|
||||||
this.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() {
|
|
||||||
|
|
||||||
var signing = [ enums.publicKey.rsa_encrypt_sign, enums.publicKey.rsa_sign, enums.publicKey.dsa];
|
|
||||||
|
|
||||||
signing = signing.map(function(s) {
|
|
||||||
return enums.read(enums.publicKey, s);
|
|
||||||
});
|
|
||||||
|
|
||||||
var keys = this.getAllKeyPackets();
|
|
||||||
|
|
||||||
for (var i = 0; i < keys.length; i++) {
|
|
||||||
if (signing.indexOf(keys[i].algorithm) !== -1) {
|
|
||||||
return keys[i];
|
return keys[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
/**
|
|
||||||
* Returns preferred signature hash algorithm of this key
|
|
||||||
* @return {String}
|
|
||||||
*/
|
|
||||||
function getPreferredSignatureHashAlgorithm() {
|
|
||||||
//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() {
|
|
||||||
// V4: by convention subkeys are prefered for encryption service
|
|
||||||
// V3: keys MUST NOT have subkeys
|
|
||||||
var isValidEncryptionKey = function(key) {
|
|
||||||
//TODO evaluate key flags: http://tools.ietf.org/html/rfc4880#section-5.2.3.21
|
|
||||||
return key.algorithm != enums.read(enums.publicKey, enums.publicKey.dsa) && key.algorithm != enums.read(enums.publicKey,
|
|
||||||
enums.publicKey.rsa_sign);
|
|
||||||
//TODO verify key
|
|
||||||
//&& keys.verifyKey()
|
|
||||||
};
|
|
||||||
|
|
||||||
var subkeys = this.getSubkeyPackets();
|
|
||||||
|
|
||||||
for (var j = 0; j < subkeys.length; j++) {
|
|
||||||
if (isValidEncryptionKey(subkeys[j])) {
|
|
||||||
return subkeys[j];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// if no valid subkey for encryption, use primary key
|
|
||||||
var primaryKey = this.getKeyPacket();
|
|
||||||
if (isValidEncryptionKey(primaryKey)) {
|
|
||||||
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) {
|
|
||||||
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
|
|
||||||
* @param {[keyid]} keyIds
|
|
||||||
* @param {String} passphrase
|
|
||||||
* @return {Boolean} true if all key packets decrypted successfully
|
|
||||||
*/
|
|
||||||
this.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++) {
|
|
||||||
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
|
|
||||||
this.verify = function() {
|
|
||||||
|
|
||||||
}
|
|
||||||
// TODO
|
|
||||||
this.revoke = function() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns first public key packet for given array of key IDs
|
||||||
|
* @param {[keyid]} keyIds
|
||||||
|
* @return {public_subkey|packet_public_key|null}
|
||||||
|
*/
|
||||||
|
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}
|
||||||
|
*/
|
||||||
|
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}
|
||||||
|
*/
|
||||||
|
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}
|
||||||
|
*/
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
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();
|
||||||
|
var pubKeyPacket = new packet.public_key();
|
||||||
|
pubKeyPacket.read(bytes);
|
||||||
|
this.packets[i] = pubKeyPacket;
|
||||||
|
}
|
||||||
|
if (this.packets[i].tag == enums.packet.secret_subkey) {
|
||||||
|
var bytes = this.packets[i].writePublicKey();
|
||||||
|
var pubSubkeyPacket = new packet.public_subkey();
|
||||||
|
pubSubkeyPacket.read(bytes);
|
||||||
|
this.packets[i] = pubSubkeyPacket;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns ASCII armored text of key
|
||||||
|
* @return {String} ASCII armor
|
||||||
|
*/
|
||||||
|
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}
|
||||||
|
*/
|
||||||
|
Key.prototype.getSigningKeyPacket = function() {
|
||||||
|
|
||||||
|
var signing = [ enums.publicKey.rsa_encrypt_sign, enums.publicKey.rsa_sign, enums.publicKey.dsa];
|
||||||
|
|
||||||
|
signing = signing.map(function(s) {
|
||||||
|
return enums.read(enums.publicKey, s);
|
||||||
|
});
|
||||||
|
|
||||||
|
var keys = this.getAllKeyPackets();
|
||||||
|
|
||||||
|
for (var i = 0; i < keys.length; i++) {
|
||||||
|
if (signing.indexOf(keys[i].algorithm) !== -1) {
|
||||||
|
return keys[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns preferred signature hash algorithm of this key
|
||||||
|
* @return {String}
|
||||||
|
*/
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
Key.prototype.getEncryptionKeyPacket = function() {
|
||||||
|
// V4: by convention subkeys are prefered for encryption service
|
||||||
|
// V3: keys MUST NOT have subkeys
|
||||||
|
var isValidEncryptionKey = function(key) {
|
||||||
|
//TODO evaluate key flags: http://tools.ietf.org/html/rfc4880#section-5.2.3.21
|
||||||
|
return key.algorithm != enums.read(enums.publicKey, enums.publicKey.dsa) && key.algorithm != enums.read(enums.publicKey,
|
||||||
|
enums.publicKey.rsa_sign);
|
||||||
|
//TODO verify key
|
||||||
|
//&& keys.verifyKey()
|
||||||
|
};
|
||||||
|
|
||||||
|
var subkeys = this.getSubkeyPackets();
|
||||||
|
|
||||||
|
for (var j = 0; j < subkeys.length; j++) {
|
||||||
|
if (isValidEncryptionKey(subkeys[j])) {
|
||||||
|
return subkeys[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if no valid subkey for encryption, use primary key
|
||||||
|
var primaryKey = this.getKeyPacket();
|
||||||
|
if (isValidEncryptionKey(primaryKey)) {
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
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
|
||||||
|
* @param {[keyid]} keyIds
|
||||||
|
* @param {String} passphrase
|
||||||
|
* @return {Boolean} true if all key packets decrypted successfully
|
||||||
|
*/
|
||||||
|
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++) {
|
||||||
|
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
|
||||||
|
Key.prototype.verify = function() {
|
||||||
|
|
||||||
|
};
|
||||||
|
// TODO
|
||||||
|
Key.prototype.revoke = function() {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* reads an OpenPGP armored text and returns a key object
|
* reads an OpenPGP armored text and returns a key object
|
||||||
* @param {String} armoredText text to be parsed
|
* @param {String} armoredText text to be parsed
|
||||||
* @return {key} new key object
|
* @return {key} new key object
|
||||||
*/
|
*/
|
||||||
key.readArmored = function(armoredText) {
|
function readArmored(armoredText) {
|
||||||
//TODO how do we want to handle bad text? Exception throwing
|
//TODO how do we want to handle bad text? Exception throwing
|
||||||
//TODO don't accept non-key armored texts
|
//TODO don't accept non-key armored texts
|
||||||
var input = armor.decode(armoredText).data;
|
var input = armor.decode(armoredText).data;
|
||||||
var packetlist = new packet.list();
|
var packetlist = new packet.list();
|
||||||
packetlist.read(input);
|
packetlist.read(input);
|
||||||
var newKey = new key(packetlist);
|
var newKey = new Key(packetlist);
|
||||||
return newKey;
|
return newKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = key;
|
exports.Key = Key;
|
||||||
|
exports.readArmored = readArmored;
|
||||||
|
|
517
src/message.js
517
src/message.js
|
@ -24,322 +24,249 @@ var util = require('./util');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class
|
* @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();
|
this.packets = packetlist || new packet.list();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the key IDs of the keys to which the session key is encrypted
|
* Returns the key IDs of the keys to which the session key is encrypted
|
||||||
* @return {[keyId]} array of keyid objects
|
* @return {[keyId]} array of keyid objects
|
||||||
*/
|
*/
|
||||||
this.getEncryptionKeyIds = function() {
|
Message.prototype.getEncryptionKeyIds = function() {
|
||||||
var keyIds = [];
|
var keyIds = [];
|
||||||
var pkESKeyPacketlist = this.packets.filterByTag(enums.packet.public_key_encrypted_session_key);
|
var pkESKeyPacketlist = this.packets.filterByTag(enums.packet.public_key_encrypted_session_key);
|
||||||
pkESKeyPacketlist.forEach(function(packet) {
|
pkESKeyPacketlist.forEach(function(packet) {
|
||||||
keyIds.push(packet.publicKeyId);
|
keyIds.push(packet.publicKeyId);
|
||||||
|
});
|
||||||
|
return keyIds;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the key IDs of the keys that signed the message
|
||||||
|
* @return {[keyId]} array of keyid objects
|
||||||
|
*/
|
||||||
|
Message.prototype.getSigningKeyIds = function() {
|
||||||
|
var keyIds = [];
|
||||||
|
var msg = this.unwrapCompressed();
|
||||||
|
// search for one pass signatures
|
||||||
|
var onePassSigList = msg.packets.filterByTag(enums.packet.one_pass_signature);
|
||||||
|
onePassSigList.forEach(function(packet) {
|
||||||
|
keyIds.push(packet.signingKeyId);
|
||||||
|
});
|
||||||
|
// if nothing found look for signature packets
|
||||||
|
if (!keyIds.length) {
|
||||||
|
var signatureList = msg.packets.filterByTag(enums.packet.signature);
|
||||||
|
signatureList.forEach(function(packet) {
|
||||||
|
keyIds.push(packet.issuerKeyId);
|
||||||
});
|
});
|
||||||
return keyIds;
|
}
|
||||||
|
return keyIds;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypt the message
|
||||||
|
* @param {key} privateKey private key with decrypted secret data
|
||||||
|
* @return {[message]} new message with decrypted content
|
||||||
|
*/
|
||||||
|
Message.prototype.decrypt = function(privateKey) {
|
||||||
|
var encryptionKeyIds = this.getEncryptionKeyIds();
|
||||||
|
if (!encryptionKeyIds.length) {
|
||||||
|
// nothing to decrypt return unmodified message
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
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 pkESKeyPacket;
|
||||||
|
for (var i = 0; i < pkESKeyPacketlist.length; i++) {
|
||||||
|
if (pkESKeyPacketlist[i].publicKeyId.equals(privateKeyPacket.getKeyId())) {
|
||||||
|
pkESKeyPacket = pkESKeyPacketlist[i];
|
||||||
|
pkESKeyPacket.decrypt(privateKeyPacket);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (pkESKeyPacket) {
|
||||||
|
var symEncryptedPacketlist = this.packets.filterByTag(enums.packet.symmetrically_encrypted, enums.packet.sym_encrypted_integrity_protected);
|
||||||
|
if (symEncryptedPacketlist.length !== 0) {
|
||||||
|
var symEncryptedPacket = symEncryptedPacketlist[0];
|
||||||
|
symEncryptedPacket.decrypt(pkESKeyPacket.sessionKeyAlgorithm, pkESKeyPacket.sessionKey);
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
Message.prototype.getText = function() {
|
||||||
|
var literal = this.packets.findPacket(enums.packet.literal);
|
||||||
|
if (literal) {
|
||||||
|
var data = literal.data;
|
||||||
|
if (literal.format == enums.read(enums.literal, enums.literal.binary)
|
||||||
|
|| literal.format == enums.read(enums.literal, enums.literal.text)) {
|
||||||
|
// text in a literal packet with format 'binary' or 'text' could be utf8, therefore decode
|
||||||
|
data = util.decode_utf8(data);
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encrypt the message
|
||||||
|
* @param {[key]} keys array of keys, used to encrypt the message
|
||||||
|
* @return {[message]} new message with encrypted content
|
||||||
|
*/
|
||||||
|
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));
|
||||||
|
keys.forEach(function(key) {
|
||||||
|
var encryptionKeyPacket = key.getEncryptionKeyPacket();
|
||||||
|
if (encryptionKeyPacket) {
|
||||||
|
var pkESKeyPacket = new packet.public_key_encrypted_session_key();
|
||||||
|
pkESKeyPacket.publicKeyId = encryptionKeyPacket.getKeyId();
|
||||||
|
pkESKeyPacket.publicKeyAlgorithm = encryptionKeyPacket.algorithm;
|
||||||
|
pkESKeyPacket.sessionKey = sessionKey;
|
||||||
|
//TODO get preferred algo from signature
|
||||||
|
pkESKeyPacket.sessionKeyAlgorithm = enums.read(enums.symmetric, config.encryption_cipher);
|
||||||
|
pkESKeyPacket.encrypt(encryptionKeyPacket);
|
||||||
|
packetlist.push(pkESKeyPacket);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var symEncryptedPacket;
|
||||||
|
if (config.integrity_protect) {
|
||||||
|
symEncryptedPacket = new packet.sym_encrypted_integrity_protected();
|
||||||
|
} else {
|
||||||
|
symEncryptedPacket = new packet.symmetrically_encrypted();
|
||||||
|
}
|
||||||
|
symEncryptedPacket.packets = this.packets;
|
||||||
|
//TODO get preferred algo from signature
|
||||||
|
symEncryptedPacket.encrypt(enums.read(enums.symmetric, config.encryption_cipher), sessionKey);
|
||||||
|
packetlist.push(symEncryptedPacket);
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
Message.prototype.sign = function(privateKeys) {
|
||||||
|
|
||||||
|
var packetlist = new packet.list();
|
||||||
|
|
||||||
|
var literalDataPacket = this.packets.findPacket(enums.packet.literal);
|
||||||
|
if (!literalDataPacket) throw new Error('No literal data packet to sign.');
|
||||||
|
|
||||||
|
var literalFormat = enums.write(enums.literal, literalDataPacket.format);
|
||||||
|
var signatureType = literalFormat == enums.literal.binary
|
||||||
|
? enums.signature.binary : enums.signature.text;
|
||||||
|
|
||||||
|
for (var i = 0; i < privateKeys.length; i++) {
|
||||||
|
var onePassSig = new packet.one_pass_signature();
|
||||||
|
onePassSig.type = signatureType;
|
||||||
|
//TODO get preferred hashg algo from key signature
|
||||||
|
onePassSig.hashAlgorithm = config.prefer_hash_algorithm;
|
||||||
|
var signingKeyPacket = privateKeys[i].getSigningKeyPacket();
|
||||||
|
onePassSig.publicKeyAlgorithm = signingKeyPacket.algorithm;
|
||||||
|
onePassSig.signingKeyId = signingKeyPacket.getKeyId();
|
||||||
|
packetlist.push(onePassSig);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
packetlist.push(literalDataPacket);
|
||||||
* Returns the key IDs of the keys that signed the message
|
|
||||||
* @return {[keyId]} array of keyid objects
|
for (var i = privateKeys.length - 1; i >= 0; i--) {
|
||||||
*/
|
var signaturePacket = new packet.signature();
|
||||||
this.getSigningKeyIds = function() {
|
signaturePacket.signatureType = signatureType;
|
||||||
var keyIds = [];
|
signaturePacket.hashAlgorithm = config.prefer_hash_algorithm;
|
||||||
var msg = this.unwrapCompressed();
|
signaturePacket.publicKeyAlgorithm = signingKeyPacket.algorithm;
|
||||||
// search for one pass signatures
|
if (!signingKeyPacket.isDecrypted) throw new Error('Private key is not decrypted.');
|
||||||
var onePassSigList = msg.packets.filterByTag(enums.packet.one_pass_signature);
|
signaturePacket.sign(signingKeyPacket, literalDataPacket);
|
||||||
onePassSigList.forEach(function(packet) {
|
packetlist.push(signaturePacket);
|
||||||
keyIds.push(packet.signingKeyId);
|
|
||||||
});
|
|
||||||
// if nothing found look for signature packets
|
|
||||||
if (!keyIds.length) {
|
|
||||||
var signatureList = msg.packets.filterByTag(enums.packet.signature);
|
|
||||||
signatureList.forEach(function(packet) {
|
|
||||||
keyIds.push(packet.issuerKeyId);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return keyIds;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
return new Message(packetlist);
|
||||||
* Decrypt the message
|
};
|
||||||
* @param {key} privateKey private key with decrypted secret data
|
|
||||||
* @return {[message]} new message with decrypted content
|
/**
|
||||||
*/
|
* Verify message signatures
|
||||||
this.decrypt = function(privateKey) {
|
* @param {[key]} publicKeys public keys to verify signatures
|
||||||
var encryptionKeyIds = this.getEncryptionKeyIds();
|
* @return {[{'keyid': keyid, 'valid': Boolean}]} list of signer's keyid and validity of signature
|
||||||
if (!encryptionKeyIds.length) {
|
*/
|
||||||
// nothing to decrypt return unmodified message
|
Message.prototype.verify = function(publicKeys) {
|
||||||
return this;
|
var result = [];
|
||||||
}
|
var msg = this.unwrapCompressed();
|
||||||
var privateKeyPacket = privateKey.getPrivateKeyPacket(encryptionKeyIds);
|
var literalDataList = msg.packets.filterByTag(enums.packet.literal);
|
||||||
if (!privateKeyPacket.isDecrypted) throw new Error('Private key is not decrypted.');
|
if (literalDataList.length !== 1) throw new Error('Can only verify message with one literal data packet.');
|
||||||
var pkESKeyPacketlist = this.packets.filterByTag(enums.packet.public_key_encrypted_session_key);
|
var signatureList = msg.packets.filterByTag(enums.packet.signature);
|
||||||
var pkESKeyPacket;
|
publicKeys.forEach(function(pubKey) {
|
||||||
for (var i = 0; i < pkESKeyPacketlist.length; i++) {
|
for (var i = 0; i < signatureList.length; i++) {
|
||||||
if (pkESKeyPacketlist[i].publicKeyId.equals(privateKeyPacket.getKeyId())) {
|
var publicKeyPacket = pubKey.getPublicKeyPacket([signatureList[i].issuerKeyId]);
|
||||||
pkESKeyPacket = pkESKeyPacketlist[i];
|
if (publicKeyPacket) {
|
||||||
pkESKeyPacket.decrypt(privateKeyPacket);
|
var verifiedSig = {};
|
||||||
|
verifiedSig.keyid = signatureList[i].issuerKeyId;
|
||||||
|
verifiedSig.status = signatureList[i].verify(publicKeyPacket, literalDataList[0]);
|
||||||
|
result.push(verifiedSig);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (pkESKeyPacket) {
|
});
|
||||||
var symEncryptedPacketlist = this.packets.filterByTag(enums.packet.symmetrically_encrypted, enums.packet.sym_encrypted_integrity_protected);
|
return result;
|
||||||
if (symEncryptedPacketlist.length !== 0) {
|
};
|
||||||
var symEncryptedPacket = symEncryptedPacketlist[0];
|
|
||||||
symEncryptedPacket.decrypt(pkESKeyPacket.sessionKeyAlgorithm, pkESKeyPacket.sessionKey);
|
/**
|
||||||
return new message(symEncryptedPacket.packets);
|
* Unwrap compressed message
|
||||||
}
|
* @return {message} message Content of compressed message
|
||||||
}
|
*/
|
||||||
|
Message.prototype.unwrapCompressed = function() {
|
||||||
|
var compressed = this.packets.filterByTag(enums.packet.compressed);
|
||||||
|
if (compressed.length) {
|
||||||
|
return new Message(compressed[0].packets);
|
||||||
|
} else {
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get literal data that is the body of the message
|
* Returns ASCII armored text of message
|
||||||
* @return {String|null} literal body of the message as string
|
* @return {String} ASCII armor
|
||||||
*/
|
*/
|
||||||
this.getLiteralData = function() {
|
Message.prototype.armor = function() {
|
||||||
var literal = this.packets.findPacket(enums.packet.literal);
|
return armor.encode(enums.armor.message, this.packets.write());
|
||||||
return literal && literal.data || null;
|
};
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get literal data as text
|
|
||||||
* @return {String|null} literal body of the message interpreted as text
|
|
||||||
*/
|
|
||||||
this.getText = function() {
|
|
||||||
var literal = this.packets.findPacket(enums.packet.literal);
|
|
||||||
if (literal) {
|
|
||||||
var data = literal.data;
|
|
||||||
if (literal.format == enums.read(enums.literal, enums.literal.binary)
|
|
||||||
|| literal.format == enums.read(enums.literal, enums.literal.text)) {
|
|
||||||
// text in a literal packet with format 'binary' or 'text' could be utf8, therefore decode
|
|
||||||
data = util.decode_utf8(data);
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
} 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) {
|
|
||||||
var packetlist = new packet.list();
|
|
||||||
//TODO get preferred algo from signature
|
|
||||||
var sessionKey = crypto.generateSessionKey(enums.read(enums.symmetric, config.encryption_cipher));
|
|
||||||
keys.forEach(function(key) {
|
|
||||||
var encryptionKeyPacket = key.getEncryptionKeyPacket();
|
|
||||||
if (encryptionKeyPacket) {
|
|
||||||
var pkESKeyPacket = new packet.public_key_encrypted_session_key();
|
|
||||||
pkESKeyPacket.publicKeyId = encryptionKeyPacket.getKeyId();
|
|
||||||
pkESKeyPacket.publicKeyAlgorithm = encryptionKeyPacket.algorithm;
|
|
||||||
pkESKeyPacket.sessionKey = sessionKey;
|
|
||||||
//TODO get preferred algo from signature
|
|
||||||
pkESKeyPacket.sessionKeyAlgorithm = enums.read(enums.symmetric, config.encryption_cipher);
|
|
||||||
pkESKeyPacket.encrypt(encryptionKeyPacket);
|
|
||||||
packetlist.push(pkESKeyPacket);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
var symEncryptedPacket;
|
|
||||||
if (config.integrity_protect) {
|
|
||||||
symEncryptedPacket = new packet.sym_encrypted_integrity_protected();
|
|
||||||
} else {
|
|
||||||
symEncryptedPacket = new packet.symmetrically_encrypted();
|
|
||||||
}
|
|
||||||
symEncryptedPacket.packets = this.packets;
|
|
||||||
//TODO get preferred algo from signature
|
|
||||||
symEncryptedPacket.encrypt(enums.read(enums.symmetric, config.encryption_cipher), sessionKey);
|
|
||||||
packetlist.push(symEncryptedPacket);
|
|
||||||
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) {
|
|
||||||
|
|
||||||
var packetlist = new packet.list();
|
|
||||||
|
|
||||||
var literalDataPacket = this.packets.findPacket(enums.packet.literal);
|
|
||||||
if (!literalDataPacket) throw new Error('No literal data packet to sign.');
|
|
||||||
|
|
||||||
var literalFormat = enums.write(enums.literal, literalDataPacket.format);
|
|
||||||
var signatureType = literalFormat == enums.literal.binary
|
|
||||||
? enums.signature.binary : enums.signature.text;
|
|
||||||
|
|
||||||
for (var i = 0; i < privateKeys.length; i++) {
|
|
||||||
var onePassSig = new packet.one_pass_signature();
|
|
||||||
onePassSig.type = signatureType;
|
|
||||||
//TODO get preferred hashg algo from key signature
|
|
||||||
onePassSig.hashAlgorithm = config.prefer_hash_algorithm;
|
|
||||||
var signingKeyPacket = privateKeys[i].getSigningKeyPacket();
|
|
||||||
onePassSig.publicKeyAlgorithm = signingKeyPacket.algorithm;
|
|
||||||
onePassSig.signingKeyId = signingKeyPacket.getKeyId();
|
|
||||||
packetlist.push(onePassSig);
|
|
||||||
}
|
|
||||||
|
|
||||||
packetlist.push(literalDataPacket);
|
|
||||||
|
|
||||||
for (var i = privateKeys.length - 1; i >= 0; i--) {
|
|
||||||
var signaturePacket = new packet.signature();
|
|
||||||
signaturePacket.signatureType = signatureType;
|
|
||||||
signaturePacket.hashAlgorithm = config.prefer_hash_algorithm;
|
|
||||||
signaturePacket.publicKeyAlgorithm = signingKeyPacket.algorithm;
|
|
||||||
if (!signingKeyPacket.isDecrypted) throw new Error('Private key is not decrypted.');
|
|
||||||
signaturePacket.sign(signingKeyPacket, literalDataPacket);
|
|
||||||
packetlist.push(signaturePacket);
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
var result = [];
|
|
||||||
var msg = this.unwrapCompressed();
|
|
||||||
var literalDataList = msg.packets.filterByTag(enums.packet.literal);
|
|
||||||
if (literalDataList.length !== 1) throw new Error('Can only verify message with one literal data packet.');
|
|
||||||
var signatureList = msg.packets.filterByTag(enums.packet.signature);
|
|
||||||
publicKeys.forEach(function(pubKey) {
|
|
||||||
for (var i = 0; i < signatureList.length; i++) {
|
|
||||||
var publicKeyPacket = pubKey.getPublicKeyPacket([signatureList[i].issuerKeyId]);
|
|
||||||
if (publicKeyPacket) {
|
|
||||||
var verifiedSig = {};
|
|
||||||
verifiedSig.keyid = signatureList[i].issuerKeyId;
|
|
||||||
verifiedSig.status = signatureList[i].verify(publicKeyPacket, literalDataList[0]);
|
|
||||||
result.push(verifiedSig);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unwrap compressed message
|
|
||||||
* @return {message} message Content of compressed message
|
|
||||||
*/
|
|
||||||
this.unwrapCompressed = function() {
|
|
||||||
var compressed = this.packets.filterByTag(enums.packet.compressed);
|
|
||||||
if (compressed.length) {
|
|
||||||
return new message(compressed[0].packets);
|
|
||||||
} else {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns ASCII armored text of message
|
|
||||||
* @return {String} ASCII armor
|
|
||||||
*/
|
|
||||||
this.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
|
* reads an OpenPGP armored message and returns a message object
|
||||||
* @param {String} armoredText text to be parsed
|
* @param {String} armoredText text to be parsed
|
||||||
* @return {message} new message object
|
* @return {message} new message object
|
||||||
*/
|
*/
|
||||||
message.readArmored = function(armoredText) {
|
function readArmored(armoredText) {
|
||||||
//TODO how do we want to handle bad text? Exception throwing
|
//TODO how do we want to handle bad text? Exception throwing
|
||||||
//TODO don't accept non-message armored texts
|
//TODO don't accept non-message armored texts
|
||||||
var input = armor.decode(armoredText).data;
|
var input = armor.decode(armoredText).data;
|
||||||
var packetlist = new packet.list();
|
var packetlist = new packet.list();
|
||||||
packetlist.read(input);
|
packetlist.read(input);
|
||||||
var newMessage = new message(packetlist);
|
var newMessage = new Message(packetlist);
|
||||||
return newMessage;
|
return newMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -348,14 +275,16 @@ message.readArmored = function(armoredText) {
|
||||||
* @param {String} text
|
* @param {String} text
|
||||||
* @return {message} new message object
|
* @return {message} new message object
|
||||||
*/
|
*/
|
||||||
message.fromText = function(text) {
|
function fromText(text) {
|
||||||
var literalDataPacket = new packet.literal();
|
var literalDataPacket = new packet.literal();
|
||||||
// text will be converted to UTF8
|
// text will be converted to UTF8
|
||||||
literalDataPacket.set(text);
|
literalDataPacket.set(text);
|
||||||
var literalDataPacketlist = new packet.list();
|
var literalDataPacketlist = new packet.list();
|
||||||
literalDataPacketlist.push(literalDataPacket);
|
literalDataPacketlist.push(literalDataPacket);
|
||||||
var newMessage = new message(literalDataPacketlist);
|
var newMessage = new Message(literalDataPacketlist);
|
||||||
return newMessage;
|
return newMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = message;
|
exports.Message = Message;
|
||||||
|
exports.readArmored = readArmored;
|
||||||
|
exports.fromText = fromText;
|
432
src/openpgp.js
432
src/openpgp.js
|
@ -16,296 +16,178 @@
|
||||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
// 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
|
* to consume the openpgp.js library. All additional classes are documented
|
||||||
* for extending and developing on top of the base library.
|
* for extending and developing on top of the base library.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var armor = require('./encoding/armor.js');
|
var armor = require('./encoding/armor.js');
|
||||||
var packet = require('./packet');
|
var packet = require('./packet');
|
||||||
var util = require('./util');
|
|
||||||
var enums = require('./enums.js');
|
var enums = require('./enums.js');
|
||||||
var config = require('./config');
|
var config = require('./config');
|
||||||
var message = require('./message.js');
|
var message = require('./message.js');
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GPG4Browsers Core interface. A single instance is hold
|
* Encrypts message text with keys
|
||||||
* from the beginning. To use this library call "openpgp.init()"
|
* @param {[key]} keys array of keys, used to encrypt the message
|
||||||
* @alias openpgp
|
* @param {String} text message as native JavaScript string
|
||||||
* @class
|
* @return {String} encrypted ASCII armored message
|
||||||
* @classdesc Main Openpgp.js class. Use this to initiate and make all calls to this library.
|
|
||||||
*/
|
*/
|
||||||
function _openpgp() {
|
function encryptMessage(keys, text) {
|
||||||
|
var msg = message.fromText(text);
|
||||||
|
msg = msg.encrypt(keys);
|
||||||
|
var armored = armor.encode(enums.armor.message, msg.packets.write());
|
||||||
|
return armored;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encrypts message text with keys
|
* Signs message text and encrypts it
|
||||||
* @param {[key]} keys array of keys, used to encrypt the message
|
* @param {[key]} publicKeys array of keys, used to encrypt the message
|
||||||
* @param {String} text message as native JavaScript string
|
* @param {key} privateKey private key with decrypted secret key data for signing
|
||||||
* @return {String} encrypted ASCII armored message
|
* @param {String} text message as native JavaScript string
|
||||||
*/
|
* @return {String} encrypted ASCII armored message
|
||||||
function encryptMessage(keys, text) {
|
*/
|
||||||
var msg = message.fromText(text);
|
function signAndEncryptMessage(publicKeys, privateKey, text) {
|
||||||
msg = msg.encrypt(keys);
|
var msg = message.fromText(text);
|
||||||
var armored = armor.encode(enums.armor.message, msg.packets.write());
|
msg = msg.sign([privateKey]);
|
||||||
return armored;
|
msg = msg.encrypt(publicKeys);
|
||||||
|
var armored = armor.encode(enums.armor.message, msg.packets.write());
|
||||||
|
return armored;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypts message
|
||||||
|
* @param {key} privateKey private key with decrypted secret key data
|
||||||
|
* @param {message} message the message object with the encrypted data
|
||||||
|
* @return {String|null} decrypted message as as native JavaScript string
|
||||||
|
* or null if no literal data found
|
||||||
|
*/
|
||||||
|
function decryptMessage(privateKey, message) {
|
||||||
|
message = message.decrypt(privateKey);
|
||||||
|
return message.getText();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypts message and verifies signatures
|
||||||
|
* @param {key} privateKey private key with decrypted secret key data
|
||||||
|
* @param {[key]} publicKeys public keys to verify signatures
|
||||||
|
* @param {message} message the message object with signed and encrypted data
|
||||||
|
* @return {{'text': String, signatures: [{'keyid': keyid, 'status': Boolean}]}}
|
||||||
|
* decrypted message as as native JavaScript string
|
||||||
|
* with verified signatures or null if no literal data found
|
||||||
|
*/
|
||||||
|
function decryptAndVerifyMessage(privateKey, publicKeys, message) {
|
||||||
|
var result = {};
|
||||||
|
message = message.decrypt(privateKey);
|
||||||
|
result.text = message.getText();
|
||||||
|
if (result.text) {
|
||||||
|
result.signatures = message.verify(publicKeys);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
function signClearMessage(privateKeys, text) {
|
||||||
* Signs message text and encrypts it
|
|
||||||
* @param {[key]} publicKeys array of keys, used to encrypt the message
|
|
||||||
* @param {key} privateKey private key with decrypted secret key data for signing
|
|
||||||
* @param {String} text message as native JavaScript string
|
|
||||||
* @return {String} encrypted ASCII armored message
|
|
||||||
*/
|
|
||||||
function signAndEncryptMessage(publicKeys, privateKey, text) {
|
|
||||||
var msg = message.fromText(text);
|
|
||||||
msg = msg.sign([privateKey]);
|
|
||||||
msg = msg.encrypt(publicKeys);
|
|
||||||
var armored = armor.encode(enums.armor.message, msg.packets.write());
|
|
||||||
return armored;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decrypts message
|
|
||||||
* @param {key} privateKey private key with decrypted secret key data
|
|
||||||
* @param {message} message the message object with the encrypted data
|
|
||||||
* @return {String|null} decrypted message as as native JavaScript string
|
|
||||||
* or null if no literal data found
|
|
||||||
*/
|
|
||||||
function decryptMessage(privateKey, message) {
|
|
||||||
message = message.decrypt(privateKey);
|
|
||||||
return message.getText();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decrypts message and verifies signatures
|
|
||||||
* @param {key} privateKey private key with decrypted secret key data
|
|
||||||
* @param {[key]} publicKeys public keys to verify signatures
|
|
||||||
* @param {message} message the message object with signed and encrypted data
|
|
||||||
* @return {{'text': String, signatures: [{'keyid': keyid, 'status': Boolean}]}}
|
|
||||||
* decrypted message as as native JavaScript string
|
|
||||||
* with verified signatures or null if no literal data found
|
|
||||||
*/
|
|
||||||
function decryptAndVerifyMessage(privateKey, publicKeys, message) {
|
|
||||||
var result = {};
|
|
||||||
message = message.decrypt(privateKey);
|
|
||||||
result.text = message.getText();
|
|
||||||
if (result.text) {
|
|
||||||
result.signatures = message.verify(publicKeys);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function verifyMessage(publicKeys, messagePacketlist) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function signMessage(privateKey, messagePacketlist) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO: update this doc
|
|
||||||
* generates a new key pair for openpgp. Beta stage. Currently only
|
|
||||||
* supports RSA keys, and no subkeys.
|
|
||||||
* @param {Integer} keyType to indicate what type of key to make.
|
|
||||||
* RSA is 1. Follows algorithms outlined in OpenPGP.
|
|
||||||
* @param {Integer} numBits number of bits for the key creation. (should
|
|
||||||
* be 1024+, generally)
|
|
||||||
* @param {String} userId assumes already in form of "User Name
|
|
||||||
* <username@email.com>"
|
|
||||||
* @param {String} passphrase The passphrase used to encrypt the resulting private key
|
|
||||||
* @return {Object} {privateKey: [openpgp_msg_privatekey],
|
|
||||||
* privateKeyArmored: [string], publicKeyArmored: [string]}
|
|
||||||
*/
|
|
||||||
function generateKeyPair(keyType, numBits, userId, passphrase) {
|
|
||||||
var packetlist = new packet.list();
|
|
||||||
|
|
||||||
var secretKeyPacket = new packet.secret_key();
|
|
||||||
secretKeyPacket.algorithm = enums.read(enums.publicKey, keyType);
|
|
||||||
secretKeyPacket.generate(numBits);
|
|
||||||
secretKeyPacket.encrypt(passphrase);
|
|
||||||
|
|
||||||
var userIdPacket = new packet.userid();
|
|
||||||
userIdPacket.read(userId);
|
|
||||||
|
|
||||||
var dataToSign = {};
|
|
||||||
dataToSign.userid = userIdPacket;
|
|
||||||
dataToSign.key = secretKeyPacket;
|
|
||||||
var signaturePacket = new packet.signature();
|
|
||||||
signaturePacket.signatureType = enums.signature.cert_generic;
|
|
||||||
signaturePacket.publicKeyAlgorithm = keyType;
|
|
||||||
//TODO we should load preferred hash from config, or as input to this function
|
|
||||||
signaturePacket.hashAlgorithm = enums.hash.sha256;
|
|
||||||
signaturePacket.keyFlags = [enums.keyFlags.certify_keys | enums.keyFlags.sign_data];
|
|
||||||
signaturePacket.sign(secretKeyPacket, dataToSign);
|
|
||||||
|
|
||||||
var secretSubkeyPacket = new packet.secret_subkey();
|
|
||||||
secretSubkeyPacket.algorithm = enums.read(enums.publicKey, keyType);
|
|
||||||
secretSubkeyPacket.generate(numBits);
|
|
||||||
secretSubkeyPacket.encrypt(passphrase);
|
|
||||||
|
|
||||||
dataToSign = {};
|
|
||||||
dataToSign.key = secretKeyPacket;
|
|
||||||
dataToSign.bind = secretSubkeyPacket;
|
|
||||||
var subkeySignaturePacket = new packet.signature();
|
|
||||||
subkeySignaturePacket.signatureType = enums.signature.subkey_binding;
|
|
||||||
subkeySignaturePacket.publicKeyAlgorithm = keyType;
|
|
||||||
//TODO we should load preferred hash from config, or as input to this function
|
|
||||||
subkeySignaturePacket.hashAlgorithm = enums.hash.sha256;
|
|
||||||
subkeySignaturePacket.keyFlags = [enums.keyFlags.encrypt_communication | enums.keyFlags.encrypt_storage];
|
|
||||||
subkeySignaturePacket.sign(secretKeyPacket, dataToSign);
|
|
||||||
|
|
||||||
packetlist.push(secretKeyPacket);
|
|
||||||
packetlist.push(userIdPacket);
|
|
||||||
packetlist.push(signaturePacket);
|
|
||||||
packetlist.push(secretSubkeyPacket);
|
|
||||||
packetlist.push(subkeySignaturePacket);
|
|
||||||
|
|
||||||
var armored = armor.encode(enums.armor.private_key, packetlist.write());
|
|
||||||
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.
|
|
||||||
* @param {Object} privatekey {obj: [openpgp_msg_privatekey]}
|
|
||||||
* - the private key to be used to sign the message
|
|
||||||
* @param {String} messagetext message text to sign
|
|
||||||
* @return {Object} {Object: text [String]}, openpgp: {String} a binary
|
|
||||||
* string representation of the message which can be 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);
|
|
||||||
var result = {
|
|
||||||
text: messagetext.replace(/\r\n/g, "\n").replace(/\n/, "\r\n"),
|
|
||||||
openpgp: sig.openpgp,
|
|
||||||
hash: sig.hash
|
|
||||||
};
|
|
||||||
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();
|
function verifyClearSignedMessage(publicKeys, message) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: update this doc
|
||||||
|
* generates a new key pair for openpgp. Beta stage. Currently only
|
||||||
|
* supports RSA keys, and no subkeys.
|
||||||
|
* @param {Integer} keyType to indicate what type of key to make.
|
||||||
|
* RSA is 1. Follows algorithms outlined in OpenPGP.
|
||||||
|
* @param {Integer} numBits number of bits for the key creation. (should
|
||||||
|
* be 1024+, generally)
|
||||||
|
* @param {String} userId assumes already in form of "User Name
|
||||||
|
* <username@email.com>"
|
||||||
|
* @param {String} passphrase The passphrase used to encrypt the resulting private key
|
||||||
|
* @return {Object} {privateKey: [openpgp_msg_privatekey],
|
||||||
|
* privateKeyArmored: [string], publicKeyArmored: [string]}
|
||||||
|
*/
|
||||||
|
function generateKeyPair(keyType, numBits, userId, passphrase) {
|
||||||
|
var packetlist = new packet.list();
|
||||||
|
|
||||||
|
var secretKeyPacket = new packet.secret_key();
|
||||||
|
secretKeyPacket.algorithm = enums.read(enums.publicKey, keyType);
|
||||||
|
secretKeyPacket.generate(numBits);
|
||||||
|
secretKeyPacket.encrypt(passphrase);
|
||||||
|
|
||||||
|
var userIdPacket = new packet.userid();
|
||||||
|
userIdPacket.read(userId);
|
||||||
|
|
||||||
|
var dataToSign = {};
|
||||||
|
dataToSign.userid = userIdPacket;
|
||||||
|
dataToSign.key = secretKeyPacket;
|
||||||
|
var signaturePacket = new packet.signature();
|
||||||
|
signaturePacket.signatureType = enums.signature.cert_generic;
|
||||||
|
signaturePacket.publicKeyAlgorithm = keyType;
|
||||||
|
//TODO we should load preferred hash from config, or as input to this function
|
||||||
|
signaturePacket.hashAlgorithm = enums.hash.sha256;
|
||||||
|
signaturePacket.keyFlags = [enums.keyFlags.certify_keys | enums.keyFlags.sign_data];
|
||||||
|
signaturePacket.sign(secretKeyPacket, dataToSign);
|
||||||
|
|
||||||
|
var secretSubkeyPacket = new packet.secret_subkey();
|
||||||
|
secretSubkeyPacket.algorithm = enums.read(enums.publicKey, keyType);
|
||||||
|
secretSubkeyPacket.generate(numBits);
|
||||||
|
secretSubkeyPacket.encrypt(passphrase);
|
||||||
|
|
||||||
|
dataToSign = {};
|
||||||
|
dataToSign.key = secretKeyPacket;
|
||||||
|
dataToSign.bind = secretSubkeyPacket;
|
||||||
|
var subkeySignaturePacket = new packet.signature();
|
||||||
|
subkeySignaturePacket.signatureType = enums.signature.subkey_binding;
|
||||||
|
subkeySignaturePacket.publicKeyAlgorithm = keyType;
|
||||||
|
//TODO we should load preferred hash from config, or as input to this function
|
||||||
|
subkeySignaturePacket.hashAlgorithm = enums.hash.sha256;
|
||||||
|
subkeySignaturePacket.keyFlags = [enums.keyFlags.encrypt_communication | enums.keyFlags.encrypt_storage];
|
||||||
|
subkeySignaturePacket.sign(secretKeyPacket, dataToSign);
|
||||||
|
|
||||||
|
packetlist.push(secretKeyPacket);
|
||||||
|
packetlist.push(userIdPacket);
|
||||||
|
packetlist.push(signaturePacket);
|
||||||
|
packetlist.push(secretSubkeyPacket);
|
||||||
|
packetlist.push(subkeySignaturePacket);
|
||||||
|
|
||||||
|
var armored = armor.encode(enums.armor.private_key, packetlist.write());
|
||||||
|
return armored;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* creates a binary string representation a signed message.
|
||||||
|
* The message will be signed with the specified private key.
|
||||||
|
* @param {Object} privatekey {obj: [openpgp_msg_privatekey]}
|
||||||
|
* - the private key to be used to sign the message
|
||||||
|
* @param {String} messagetext message text to sign
|
||||||
|
* @return {Object} {Object: text [String]}, openpgp: {String} a binary
|
||||||
|
* string representation of the message which can be 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);
|
||||||
|
var result = {
|
||||||
|
text: messagetext.replace(/\r\n/g, "\n").replace(/\n/, "\r\n"),
|
||||||
|
openpgp: sig.openpgp,
|
||||||
|
hash: sig.hash
|
||||||
|
};
|
||||||
|
return armor.encode(2, result, null, null);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
exports.encryptMessage = encryptMessage;
|
||||||
|
exports.signAndEncryptMessage = signAndEncryptMessage;
|
||||||
|
exports.decryptMessage = decryptMessage;
|
||||||
|
exports.decryptAndVerifyMessage = decryptAndVerifyMessage
|
||||||
|
exports.signClearMessage = signClearMessage;
|
||||||
|
exports.verifyClearSignedMessage = verifyClearSignedMessage;
|
||||||
|
exports.generateKeyPair = generateKeyPair;
|
||||||
|
|
1427
test/test-bundle.js
1427
test/test-bundle.js
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user