Implement cleartext signed messages
This commit is contained in:
parent
be96de5188
commit
7e711510cc
File diff suppressed because one or more lines are too long
141
src/cleartext.js
Normal file
141
src/cleartext.js
Normal file
|
@ -0,0 +1,141 @@
|
|||
// GPG4Browsers - An OpenPGP implementation in javascript
|
||||
// Copyright (C) 2011 Recurity Labs GmbH
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
var config = require('./config');
|
||||
var packet = require('./packet');
|
||||
var enums = require('./enums.js');
|
||||
var armor = require('./encoding/armor.js');
|
||||
|
||||
/**
|
||||
* @class
|
||||
* @classdesc Class that represents an OpenPGP cleartext signed message.
|
||||
* See http://tools.ietf.org/html/rfc4880#section-7
|
||||
* @param {String} text The cleartext of the signed message
|
||||
* @param {packetlist} packetlist The packetlist with signature packets or undefined
|
||||
* if message not yet signed
|
||||
*/
|
||||
|
||||
function CleartextMessage(text, packetlist) {
|
||||
if (!(this instanceof CleartextMessage)) {
|
||||
return new CleartextMessage(packetlist);
|
||||
}
|
||||
this.text = text;
|
||||
this.packets = packetlist || new packet.list();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key IDs of the keys that signed the cleartext message
|
||||
* @return {[keyId]} array of keyid objects
|
||||
*/
|
||||
CleartextMessage.prototype.getSigningKeyIds = function() {
|
||||
var keyIds = [];
|
||||
var signatureList = this.packets.filterByTag(enums.packet.signature);
|
||||
signatureList.forEach(function(packet) {
|
||||
keyIds.push(packet.issuerKeyId);
|
||||
});
|
||||
return keyIds;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sign the cleartext message
|
||||
* @param {[key]} privateKeys private keys with decrypted secret key data for signing
|
||||
*/
|
||||
CleartextMessage.prototype.sign = function(privateKeys) {
|
||||
var packetlist = new packet.list();
|
||||
for (var i = 0; i < privateKeys.length; i++) {
|
||||
var signaturePacket = new packet.signature();
|
||||
signaturePacket.signatureType = enums.signature.text;
|
||||
signaturePacket.hashAlgorithm = config.prefer_hash_algorithm;
|
||||
var signingKeyPacket = privateKeys[i].getSigningKeyPacket();
|
||||
signaturePacket.publicKeyAlgorithm = signingKeyPacket.algorithm;
|
||||
if (!signingKeyPacket.isDecrypted) throw new Error('Private key is not decrypted.');
|
||||
// use literal data packet to convert to canonical <CR><LF> line endings
|
||||
var literalDataPacket = new packet.literal();
|
||||
literalDataPacket.setBytes(this.text, enums.read(enums.literal, enums.literal.text));
|
||||
signaturePacket.sign(signingKeyPacket, literalDataPacket);
|
||||
packetlist.push(signaturePacket);
|
||||
}
|
||||
this.packets = packetlist;
|
||||
};
|
||||
|
||||
/**
|
||||
* Verify signatures of cleartext signed message
|
||||
* @param {[key]} publicKeys public keys to verify signatures
|
||||
* @return {[{'keyid': keyid, 'valid': Boolean}]} list of signer's keyid and validity of signature
|
||||
*/
|
||||
CleartextMessage.prototype.verify = function(publicKeys) {
|
||||
var result = [];
|
||||
var signatureList = this.packets.filterByTag(enums.packet.signature);
|
||||
var that = this;
|
||||
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;
|
||||
// use literal data packet to convert to canonical <CR><LF> line endings
|
||||
var literalDataPacket = new packet.literal();
|
||||
literalDataPacket.setBytes(that.text, enums.read(enums.literal, enums.literal.text));
|
||||
verifiedSig.status = signatureList[i].verify(publicKeyPacket, literalDataPacket);
|
||||
result.push(verifiedSig);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get cleartext
|
||||
* @return {String} cleartext of message
|
||||
*/
|
||||
CleartextMessage.prototype.getText = function() {
|
||||
return this.text;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns ASCII armored text of cleartext signed message
|
||||
* @return {String} ASCII armor
|
||||
*/
|
||||
CleartextMessage.prototype.armor = function() {
|
||||
var body = {
|
||||
hash: enums.read(enums.hash, config.prefer_hash_algorithm).toUpperCase(),
|
||||
text: this.text,
|
||||
data: this.packets.write()
|
||||
}
|
||||
return armor.encode(enums.armor.signed, body);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* reads an OpenPGP cleartext signed message and returns a CleartextMessage object
|
||||
* @param {String} armoredText text to be parsed
|
||||
* @return {CleartextMessage} new cleartext message object
|
||||
*/
|
||||
function readArmored(armoredText) {
|
||||
var input = armor.decode(armoredText);
|
||||
if (input.type !== enums.armor.signed) {
|
||||
throw new Error('No cleartext signed message.');
|
||||
}
|
||||
var packetlist = new packet.list();
|
||||
packetlist.read(input.data);
|
||||
var newMessage = new CleartextMessage(input.text, packetlist);
|
||||
return newMessage;
|
||||
}
|
||||
|
||||
exports.CleartextMessage = CleartextMessage;
|
||||
exports.readArmored = readArmored;
|
|
@ -2,6 +2,7 @@
|
|||
module.exports = require('./openpgp.js');
|
||||
module.exports.key = require('./key.js');
|
||||
module.exports.message = require('./message.js');
|
||||
module.exports.cleartext = require('./cleartext.js');
|
||||
module.exports.util = require('./util');
|
||||
module.exports.packet = require('./packet');
|
||||
module.exports.mpi = require('./type/mpi.js');
|
||||
|
|
|
@ -26,6 +26,7 @@ var util = require('./util');
|
|||
* @class
|
||||
* @classdesc Class that represents an OpenPGP message.
|
||||
* Can be an encrypted message, signed message, compressed message or literal message
|
||||
* @param {packetlist} packetlist The packets that form this message
|
||||
* See http://tools.ietf.org/html/rfc4880#section-11.3
|
||||
*/
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ var packet = require('./packet');
|
|||
var enums = require('./enums.js');
|
||||
var config = require('./config');
|
||||
var message = require('./message.js');
|
||||
var cleartext = require('./cleartext.js');
|
||||
|
||||
|
||||
/**
|
||||
|
@ -88,16 +89,35 @@ function decryptAndVerifyMessage(privateKey, publicKeys, message) {
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Signs a cleartext message
|
||||
* @param {[Key]} privateKeys private key with decrypted secret key data to sign cleartext
|
||||
* @param {String} text cleartext
|
||||
* @return {String} ASCII armored message
|
||||
*/
|
||||
function signClearMessage(privateKeys, text) {
|
||||
|
||||
var cleartextMessage = new cleartext.CleartextMessage(text);
|
||||
cleartextMessage.sign(privateKeys);
|
||||
return cleartextMessage.armor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies signatures of cleartext signed message
|
||||
* @param {[Key]} publicKeys public keys to verify signatures
|
||||
* @param {CleartextMessage} message cleartext message object with signatures
|
||||
* @return {{'text': String, signatures: [{'keyid': keyid, 'status': Boolean}]}}
|
||||
* cleartext with status of verified signatures
|
||||
*/
|
||||
function verifyClearSignedMessage(publicKeys, message) {
|
||||
|
||||
var result = {};
|
||||
if (!(message instanceof cleartext.CleartextMessage)) {
|
||||
throw new Error('Parameter [message] needs to be of type CleartextMessage.');
|
||||
}
|
||||
result.text = message.getText();
|
||||
result.signatures = message.verify(publicKeys);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* TODO: update this doc
|
||||
* generates a new key pair for openpgp. Beta stage. Currently only
|
||||
|
|
|
@ -349,7 +349,7 @@ var pub_key_arm3 =
|
|||
|
||||
var keyids = sMsg.getSigningKeyIds();
|
||||
|
||||
var verified = pubKey2.getPublicKeyPacket(keyids) && pubKey3.getPublicKeyPacket(keyids);
|
||||
var verified = pubKey2.getPublicKeyPacket(keyids) !== null && pubKey3.getPublicKeyPacket(keyids) !== null;
|
||||
|
||||
verified = verified && sMsg.getText() == plaintext;
|
||||
|
||||
|
@ -358,6 +358,65 @@ var pub_key_arm3 =
|
|||
verified = verified && verifiedSig[0].status && verifiedSig[1].status;
|
||||
|
||||
return new unit.result("Verify signed message with two one pass signatures", verified);
|
||||
}, function() {
|
||||
|
||||
var msg_armor =
|
||||
['-----BEGIN PGP SIGNED MESSAGE-----',
|
||||
'Hash: SHA256',
|
||||
'',
|
||||
'short message',
|
||||
'next line',
|
||||
'한국어/조선말',
|
||||
'-----BEGIN PGP SIGNATURE-----',
|
||||
'Version: GnuPG v2.0.19 (GNU/Linux)',
|
||||
'',
|
||||
'iJwEAQEIAAYFAlKcju8ACgkQ4IT3RGwgLJci6gP/dCmIraUa6AGpJxzGfK+jYpjl',
|
||||
'G0KunFyGmyPxeJVnPi2bBp3EPIbiayQ71CcDe9DKpF046tora07AA9eo+/YbvJ9P',
|
||||
'PWeScw3oj/ejsmKQoDBGzyDMFUphevnhgc5lENjovJqmiu6FKjNmADTxcZ/qFTOq',
|
||||
'44EWTgdW3IqXFkNpKjeJARwEAQEIAAYFAlKcju8ACgkQ2/Ij6HBTTfQi6gf9HxhE',
|
||||
'ycLDhQ8iyC090TaYwsDytScU2vOMiI5rJCy2tfDV0pfn+UekYGMnKxZTpwtmno1j',
|
||||
'mVOlieENszz5IcehS5TYwk4lmRFjoba+Z8qwPEYhYxP29GMbmRIsH811sQHFTigo',
|
||||
'LI2t4pSSSUpAiXd9y6KtvkWcGGn8IfkNHCEHPh1ov28QvH0+ByIiKYK5N6ZB8hEo',
|
||||
'0uMYhKQPVJdPCvMkAxQCRPw84EvmxuJ0HMCeSB9tHQXpz5un2m8D9yiGpBQPnqlW',
|
||||
'vCCq7fgaUz8ksxvQ9bSwv0iIIbbBdTP7Z8y2c1Oof6NDl7irH+QCeNT7IIGs8Smn',
|
||||
'BEzv/FqkQAhjy3Krxg==',
|
||||
'=3Pkl',
|
||||
'-----END PGP SIGNATURE-----'].join('\n');
|
||||
|
||||
var plaintext = 'short message\nnext line\n한국어/조선말';
|
||||
var csMsg = openpgp.cleartext.readArmored(msg_armor);
|
||||
var pubKey2 = openpgp.key.readArmored(pub_key_arm2);
|
||||
var pubKey3 = openpgp.key.readArmored(pub_key_arm3);
|
||||
|
||||
var keyids = csMsg.getSigningKeyIds();
|
||||
|
||||
var verified = pubKey2.getPublicKeyPacket(keyids) !== null && pubKey3.getPublicKeyPacket(keyids) !== null;
|
||||
|
||||
var cleartextSig = openpgp.verifyClearSignedMessage([pubKey2, pubKey3], csMsg);
|
||||
|
||||
verified = verified && cleartextSig.text == plaintext;
|
||||
|
||||
verified = verified && cleartextSig.signatures[0].status && cleartextSig.signatures[1].status;
|
||||
|
||||
return new unit.result("Verify cleartext signed message with two signatures with openpgp.verifyClearSignedMessage", verified);
|
||||
}, function() {
|
||||
|
||||
var plaintext = 'short message\nnext line\n한국어/조선말';
|
||||
var pubKey = openpgp.key.readArmored(pub_key_arm2);
|
||||
var privKey = openpgp.key.readArmored(priv_key_arm2);
|
||||
privKey.getSigningKeyPacket().decrypt('hello world');
|
||||
|
||||
var clearSignedArmor = openpgp.signClearMessage([privKey], plaintext);
|
||||
|
||||
var csMsg = openpgp.cleartext.readArmored(clearSignedArmor);
|
||||
|
||||
var cleartextSig = openpgp.verifyClearSignedMessage([pubKey], csMsg);
|
||||
|
||||
var verified = cleartextSig.text == plaintext;
|
||||
|
||||
verified = verified && cleartextSig.signatures[0].status;
|
||||
|
||||
return new unit.result("Sign text with openpgp.signClearMessage and verify with openpgp.verifyClearSignedMessage leads to same cleartext and valid signatures", verified);
|
||||
}];
|
||||
|
||||
var results = [];
|
||||
|
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user