Fix ascii dearmor and signature verification bugs

This commit is contained in:
Robert Nelson 2013-11-29 19:29:57 -08:00
parent b4676d2ad8
commit f59fa54ecf
4 changed files with 83 additions and 47 deletions

View File

@ -25,8 +25,6 @@
*/ */
function openpgp_encoding_deArmor(text) { function openpgp_encoding_deArmor(text) {
text = text.replace(/\r/g, ''); text = text.replace(/\r/g, '');
// remove whitespace of blank line to allow later split at \n\n
text = text.replace(/\n\s+\n/, '\n\n');
var type = openpgp_encoding_get_type(text); var type = openpgp_encoding_get_type(text);
@ -34,69 +32,99 @@ function openpgp_encoding_deArmor(text) {
var splittedtext = text.split('-----'); var splittedtext = text.split('-----');
// splittedtext[0] - should be the empty string // splittedtext[0] - should be the empty string
// splittedtext[1] - should be BEGIN... // splittedtext[1] - should be BEGIN...
// splittedtext[2] - the message and checksum // splittedtext[2] - \nthe message and checksum
// splittedtest[3] - should be END... // splittedtext[3] - should be END...
// chunks separated by blank lines // chunks separated by blank lines
var splittedChunks = splittedtext[2] var msg = openpgp_encoding_split_headers(splittedtext[2].slice(1));
.split('\n\n'); var msg_sum = openpgp_encoding_split_checksum(msg.body);
var messageAndChecksum = splittedtext[2]
.split('\n\n')[1]
.split('\n=');
var message = messageAndChecksum[0]
.replace(/\n- /g,"\n");
// sometimes, there's a blank line between message and checksum
var checksum;
if(messageAndChecksum.length == 1){
// blank line
checksum = splittedtext[2]
.replace(/[\n=]/g, "");
} else {
// no blank line
checksum = messageAndChecksum[1]
.split('\n')[0];
}
var data = { var data = {
openpgp: openpgp_encoding_base64_decode(message), openpgp: openpgp_encoding_base64_decode(msg_sum.body),
type: type type: type
}; };
if (verifyCheckSum(data.openpgp, checksum)) { if (verifyCheckSum(data.openpgp, msg_sum.checksum)) {
return data; return data;
} else { } else {
util.print_error("Ascii armor integrity check on message failed: '" util.print_error("Ascii armor integrity check on message failed: '"
+ checksum + msg_sum.checksum
+ "' should be '" + "' should be '"
+ getCheckSum(data) + "'"); + getCheckSum(data) + "'");
return false; return false;
} }
} else { } else {
var splittedtext = text.split('-----'); var splittedtext = text.split('-----');
// splittedtext[0] - should be the empty string
// splittedtext[1] - should be BEGIN PGP SIGNED MESSAGE
// splittedtext[2] - \nthe message
// splittedtext[3] - should be BEGIN PGP SIGNATURE
// splittedtext[4] - \nthe signature and checksum
// splittedtext[5] - should be END PGP SIGNATURE
var msg = openpgp_encoding_split_headers(splittedtext[2].slice(1));
var sig = openpgp_encoding_split_headers(splittedtext[4].slice(1));
var sig_sum = openpgp_encoding_split_checksum(sig.body);
var result = { var result = {
text: splittedtext[2] text: msg.body.replace(/\n\n$/, "\n").replace(/\n/g, "\r\n"),
.replace(/\n- /g,"\n") openpgp: openpgp_encoding_base64_decode(sig_sum.body),
.split("\n\n")[1],
openpgp: openpgp_encoding_base64_decode(splittedtext[4]
.split("\n\n")[1]
.split("\n=")[0]),
type: type type: type
}; };
if (verifyCheckSum(result.openpgp, splittedtext[4] if (verifyCheckSum(result.openpgp, sig_sum.checksum)) {
.split("\n\n")[1]
.split("\n=")[1]))
return result; return result;
else { } else {
util.print_error("Ascii armor integrity check on message failed"); util.print_error("Ascii armor integrity check on message failed");
return false; return false;
} }
} }
} }
/**
* Splits a message into two parts, the headers and the body. This is an internal function
* @param {String} text OpenPGP armored message part
* @returns {(Boolean|Object)} Either false in case of an error
* or an object with attribute "headers" containing the headers and
* and an attribute "body" containing the body.
*/
function openpgp_encoding_split_headers(text) {
var reEmptyLine = /^[\t ]*\n/m;
var headers = "";
var body = text;
var matchResult = reEmptyLine.exec(text);
if (matchResult != null) {
headers = text.slice(0, matchResult.index);
body = text.slice(matchResult.index + matchResult[0].length);
}
return { headers: headers, body: body };
}
/**
* Splits a message into two parts, the body and the checksum. This is an internal function
* @param {String} text OpenPGP armored message part
* @returns {(Boolean|Object)} Either false in case of an error
* or an object with attribute "body" containing the body
* and an attribute "checksum" containing the checksum.
*/
function openpgp_encoding_split_checksum(text) {
var reChecksumStart = /^=/m;
var body = text;
var checksum = "";
var matchResult = reChecksumStart.exec(text);
if (matchResult != null) {
body = text.slice(0, matchResult.index);
checksum = text.slice(matchResult.index + 1);
}
return { body: body, checksum: checksum };
}
/** /**
* Finds out which Ascii Armoring type is used. This is an internal function * Finds out which Ascii Armoring type is used. This is an internal function
* @param {String} text [String] ascii armored text * @param {String} text [String] ascii armored text
@ -124,7 +152,7 @@ function openpgp_encoding_get_type(text) {
return 1; return 1;
} else } else
// BEGIN PGP SIGNATURE // BEGIN PGP SIGNED MESSAGE
// Used for detached signatures, OpenPGP/MIME signatures, and // Used for detached signatures, OpenPGP/MIME signatures, and
// cleartext signatures. Note that PGP 2.x uses BEGIN PGP MESSAGE // cleartext signatures. Note that PGP 2.x uses BEGIN PGP MESSAGE
// for detached signatures. // for detached signatures.

View File

@ -130,7 +130,7 @@ function _openpgp () {
function read_message(armoredText) { function read_message(armoredText) {
var dearmored; var dearmored;
try{ try{
dearmored = openpgp_encoding_deArmor(armoredText.replace(/\r/g,'')); dearmored = openpgp_encoding_deArmor(armoredText);
} }
catch(e){ catch(e){
util.print_error('no message found!'); util.print_error('no message found!');

View File

@ -93,14 +93,14 @@ function openpgp_msg_message() {
return false; return false;
} }
} }
if (pubkey.length == 0) if (pubkey.length == 0) {
util.print_warning("Unable to verify signature of issuer: "+util.hexstrdump(this.signature.issuerKeyId)+". Public key not found in keyring."); util.print_warning("Unable to verify signature of issuer: "+util.hexstrdump(this.signature.issuerKeyId)+". Public key not found in keyring.");
else { } else {
for (var i = 0 ; i < pubkey.length; i++) { 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(this.text, pubkey[i])) {
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)+")"); util.print_info("Found Good Signature from "+pubkey[i].obj.userIds[0].text+" (0x"+util.hexstrdump(pubkey[i].obj.getKeyId()).substring(8)+")");
result = true; result = true;
break;
} else { } 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)+")"); util.print_error("Signature verification failed: Bad Signature from "+pubkey[i].obj.userIds[0].text+" (0x"+util.hexstrdump(pubkey[0].obj.getKeyId()).substring(8)+")");
} }

View File

@ -497,14 +497,21 @@ function openpgp_packet_signature() {
if (this.version == 4) { if (this.version == 4) {
this.verified = openpgp_crypto_verifySignature(this.publicKeyAlgorithm, this.hashAlgorithm, this.verified = openpgp_crypto_verifySignature(this.publicKeyAlgorithm, this.hashAlgorithm,
this.MPIs, key.obj.publicKeyPacket.MPIs, data+this.signatureData+trailer); this.MPIs, key.obj.publicKeyPacket.MPIs, data+this.signatureData+trailer);
} else {
this.verified = false;
} }
break; break;
case 1: // 0x01: Signature of a canonical text document. case 1: // 0x01: Signature of a canonical text document.
if (this.version == 4) { if (this.version == 4) {
var tohash = data
.replace(/\r\n/g,"\n")
.replace(/[\t ]+\n/g, "\n")
.replace(/\n/g,"\r\n");
this.verified = openpgp_crypto_verifySignature(this.publicKeyAlgorithm, this.hashAlgorithm, this.verified = openpgp_crypto_verifySignature(this.publicKeyAlgorithm, this.hashAlgorithm,
this.MPIs, key.obj.publicKeyPacket.MPIs, data+this.signatureData+trailer); this.MPIs, key.obj.publicKeyPacket.MPIs, tohash+this.signatureData+trailer);
return this.verified; } else {
this.verified = false;
} }
break; break;
@ -630,6 +637,7 @@ function openpgp_packet_signature() {
// document) that cannot include a target subpacket. // document) that cannot include a target subpacket.
default: default:
util.print_error("openpgp.packet.signature.js\n"+"signature verification for type"+ this.signatureType+" not implemented"); util.print_error("openpgp.packet.signature.js\n"+"signature verification for type"+ this.signatureType+" not implemented");
this.verified = false;
break; break;
} }
return this.verified; return this.verified;