OP-01-019 Cleartext Message Spoofing in Armor Headers (Critical). Fix: throw error if unknown ASCII armor type.

This commit is contained in:
Thomas Oberndörfer 2014-03-21 16:10:10 +01:00
parent 329c92bc73
commit 93ca8b62fe
2 changed files with 32 additions and 12 deletions

View File

@ -27,7 +27,8 @@ var base64 = require('./base64.js'),
config = require('../config'); config = require('../config');
/** /**
* Finds out which Ascii Armoring type is used. This is an internal function * Finds out which Ascii Armoring type is used. Throws error if unknown type.
* @private
* @param {String} text [String] ascii armored text * @param {String} text [String] ascii armored text
* @returns {Integer} 0 = MESSAGE PART n of m * @returns {Integer} 0 = MESSAGE PART n of m
* 1 = MESSAGE PART n * 1 = MESSAGE PART n
@ -35,24 +36,27 @@ var base64 = require('./base64.js'),
* 3 = PGP MESSAGE * 3 = PGP MESSAGE
* 4 = PUBLIC KEY BLOCK * 4 = PUBLIC KEY BLOCK
* 5 = PRIVATE KEY BLOCK * 5 = PRIVATE KEY BLOCK
* null = unknown
*/ */
function getType(text) { function getType(text) {
var reHeader = /^-----([^-]+)-----$\n/m; var reHeader = /^-----BEGIN PGP (MESSAGE, PART \d+\/\d+|MESSAGE, PART \d+|SIGNED MESSAGE|MESSAGE|PUBLIC KEY BLOCK|PRIVATE KEY BLOCK)-----$\n/m;
var header = text.match(reHeader); var header = text.match(reHeader);
if (!header) {
throw new Error('Unknow ASCII armor type');
}
// BEGIN PGP MESSAGE, PART X/Y // BEGIN PGP MESSAGE, PART X/Y
// Used for multi-part messages, where the armor is split amongst Y // Used for multi-part messages, where the armor is split amongst Y
// parts, and this is the Xth part out of Y. // parts, and this is the Xth part out of Y.
if (header[1].match(/BEGIN PGP MESSAGE, PART \d+\/\d+/)) { if (header[1].match(/MESSAGE, PART \d+\/\d+/)) {
return enums.armor.multipart_section; return enums.armor.multipart_section;
} else } else
// BEGIN PGP MESSAGE, PART X // BEGIN PGP MESSAGE, PART X
// Used for multi-part messages, where this is the Xth part of an // Used for multi-part messages, where this is the Xth part of an
// unspecified number of parts. Requires the MESSAGE-ID Armor // unspecified number of parts. Requires the MESSAGE-ID Armor
// Header to be used. // Header to be used.
if (header[1].match(/BEGIN PGP MESSAGE, PART \d+/)) { if (header[1].match(/MESSAGE, PART \d+/)) {
return enums.armor.multipart_last; return enums.armor.multipart_last;
} else } else
@ -60,25 +64,25 @@ function getType(text) {
// 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.
if (header[1].match(/BEGIN PGP SIGNED MESSAGE/)) { if (header[1].match(/SIGNED MESSAGE/)) {
return enums.armor.signed; return enums.armor.signed;
} else } else
// BEGIN PGP MESSAGE // BEGIN PGP MESSAGE
// Used for signed, encrypted, or compressed files. // Used for signed, encrypted, or compressed files.
if (header[1].match(/BEGIN PGP MESSAGE/)) { if (header[1].match(/MESSAGE/)) {
return enums.armor.message; return enums.armor.message;
} else } else
// BEGIN PGP PUBLIC KEY BLOCK // BEGIN PGP PUBLIC KEY BLOCK
// Used for armoring public keys. // Used for armoring public keys.
if (header[1].match(/BEGIN PGP PUBLIC KEY BLOCK/)) { if (header[1].match(/PUBLIC KEY BLOCK/)) {
return enums.armor.public_key; return enums.armor.public_key;
} else } else
// BEGIN PGP PRIVATE KEY BLOCK // BEGIN PGP PRIVATE KEY BLOCK
// Used for armoring private keys. // Used for armoring private keys.
if (header[1].match(/BEGIN PGP PRIVATE KEY BLOCK/)) { if (header[1].match(/PRIVATE KEY BLOCK/)) {
return enums.armor.private_key; return enums.armor.private_key;
} }
} }
@ -277,9 +281,6 @@ function dearmor(text) {
text = text.replace(/\r/g, ''); text = text.replace(/\r/g, '');
var type = getType(text); var type = getType(text);
if (!type) {
throw new Error('Unknow ASCII armor type');
}
var splittext = text.split(reSplit); var splittext = text.split(reSplit);

View File

@ -112,6 +112,25 @@ describe("ASCII armor", function() {
expect(msg).to.throw(Error, /Improperly formatted armor header/); expect(msg).to.throw(Error, /Improperly formatted armor header/);
}); });
it('Exception if wrong armor header type', function () {
var msg =
['-----BEGIN PGP SIGNED MESSAGE\u2010\u2010\u2010\u2010\u2010\nHash:SHA1\n\nIs this properly-----',
'',
'sign this',
'-----BEGIN PGP SIGNATURE-----',
'Version: GnuPG v2.0.22 (GNU/Linux)',
'',
'iJwEAQECAAYFAlMrPj0ACgkQ4IT3RGwgLJfYkQQAgHMQieazCVdfGAfzQM69Egm5',
'HhcQszODD898wpoGCHgiNdNo1+5nujQAtXnkcxM+Vf7onfbTvUqut/siyO3fzqhK',
'LQ9DiQUwJMBE8nOwVR7Mpc4kLNngMTNaHAjZaVaDpTCrklPY+TPHIZnu0B6Ur+6t',
'skTzzVXIxMYw8ihbHfk=',
'=e/eA',
'-----END PGP SIGNATURE-----'].join('\n');
msg = openpgp.cleartext.readArmored.bind(null, msg);
expect(msg).to.throw(Error, /Unknow ASCII armor type/);
});
}); });