diff --git a/src/cleartext.js b/src/cleartext.js index a99155e3..77f60f7e 100644 --- a/src/cleartext.js +++ b/src/cleartext.js @@ -171,30 +171,27 @@ function verifyHeaders(headers, packetlist) { return true; }; - let oneHeader = null; - let hashAlgos = []; - headers.forEach(function(header) { - oneHeader = header.match(/^Hash: (.+)$/); // get header value - if (oneHeader) { - oneHeader = oneHeader[1].replace(/\s/g, ''); // remove whitespace - oneHeader = oneHeader.split(','); - oneHeader = oneHeader.map(function(hash) { - hash = hash.toLowerCase(); - try { - return enums.write(enums.hash, hash); - } catch (e) { - throw new Error('Unknown hash algorithm in armor header: ' + hash); - } - }); - hashAlgos = hashAlgos.concat(oneHeader); + const hashAlgos = []; + headers.forEach(header => { + const hashHeader = header.match(/^Hash: (.+)$/); // get header value + if (hashHeader) { + const parsedHashIDs = hashHeader[1] + .replace(/\s/g, '') // remove whitespace + .split(',') + .map(hashName => { + try { + return enums.write(enums.hash, hashName.toLowerCase()); + } catch (e) { + throw new Error('Unknown hash algorithm in armor header: ' + hashName.toLowerCase()); + } + }); + hashAlgos.push(...parsedHashIDs); } else { throw new Error('Only "Hash" header allowed in cleartext signed message'); } }); - if (!hashAlgos.length && !checkHashAlgos([enums.hash.md5])) { - throw new Error('If no "Hash" header in cleartext signed message, then only MD5 signatures allowed'); - } else if (hashAlgos.length && !checkHashAlgos(hashAlgos)) { + if (hashAlgos.length && !checkHashAlgos(hashAlgos)) { throw new Error('Hash algorithm mismatch in armor header and signature'); } } diff --git a/test/general/armor.js b/test/general/armor.js index 2c620d35..62773792 100644 --- a/test/general/armor.js +++ b/test/general/armor.js @@ -36,12 +36,6 @@ export default () => describe('ASCII armor', function() { await expect(msg).to.be.rejectedWith(Error, /Hash algorithm mismatch in armor header and signature/); }); - it('Exception if no header and non-MD5 signature', async function () { - let msg = getArmor(null); - msg = openpgp.readCleartextMessage({ cleartextMessage: msg }); - await expect(msg).to.be.rejectedWith(Error, /If no "Hash" header in cleartext signed message, then only MD5 signatures allowed/); - }); - it('Exception if unknown hash algorithm', async function () { let msg = getArmor(['Hash: LAV750']); msg = openpgp.readCleartextMessage({ cleartextMessage: msg }); @@ -66,18 +60,6 @@ export default () => describe('ASCII armor', function() { await expect(msg).to.be.rejectedWith(Error, /Only "Hash" header allowed in cleartext signed message/); }); - it('Multiple wrong hash values', async function () { - let msg = getArmor(['Hash: SHA512, SHA256']); - msg = openpgp.readCleartextMessage({ cleartextMessage: msg }); - await expect(msg).to.be.rejectedWith(Error, /Hash algorithm mismatch in armor header and signature/); - }); - - it('Multiple wrong hash values', async function () { - let msg = getArmor(['Hash: SHA512, SHA256']); - msg = openpgp.readCleartextMessage({ cleartextMessage: msg }); - await expect(msg).to.be.rejectedWith(Error, /Hash algorithm mismatch in armor header and signature/); - }); - it('Filter whitespace in blank line', async function () { let msg = [ '-----BEGIN PGP SIGNED MESSAGE-----', @@ -99,11 +81,6 @@ export default () => describe('ASCII armor', function() { expect(msg).to.be.an.instanceof(openpgp.CleartextMessage); }); - it('Exception if header is not Hash in cleartext signed message', async function () { - const msg = openpgp.readCleartextMessage({ cleartextMessage: getArmor(['Ha sh: SHA256']) }); - await expect(msg).to.be.rejectedWith(Error, /Only "Hash" header allowed in cleartext signed message/); - }); - it('Ignore improperly formatted armor header', async function () { await Promise.all(['Space : trailing', 'Space :switched', ': empty', 'none', 'Space:missing'].map(async function (invalidHeader) { expect(await openpgp.readCleartextMessage({ cleartextMessage: getArmor(['Hash: SHA1'], [invalidHeader]) })).to.be.an.instanceof(openpgp.CleartextMessage); diff --git a/test/general/signature.js b/test/general/signature.js index 73e1425e..c94acd8f 100644 --- a/test/general/signature.js +++ b/test/general/signature.js @@ -1365,6 +1365,47 @@ DAAKCRDyMVUMT0fjjlnQAQDFHUs6TIcxrNTtEZFjUFm1M0PJ1Dng/cDW4xN80fsn expect(notations[1].critical).to.be.false; }); + it('Verify v6 cleartext signed message with openpgp.verify', async function() { + // test vector from https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-08.html#name-sample-v6-certificate-trans + const cleartextMessage = `-----BEGIN PGP SIGNED MESSAGE----- + +What we need from the grocery store: + +- - tofu +- - vegetables +- - noodles + +-----BEGIN PGP SIGNATURE----- + +wpgGARsKAAAAKQWCY5ijYyIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6 +2azJAAAAAGk2IHZJX1AhiJD39eLuPBgiUU9wUA9VHYblySHkBONKU/usJ9BvuAqo +/FvLFuGWMbKAdA+epq7V4HOtAPlBWmU8QOd6aud+aSunHQaaEJ+iTFjP2OMW0KBr +NK2ay45cX1IVAQ== +-----END PGP SIGNATURE-----`; + + const publicKey = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PUBLIC KEY BLOCK----- + +xioGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laPCsQYf +GwoAAABCBYJjh3/jAwsJBwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxy +KwwfHifBilZwj2Ul7Ce62azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lw +gyU2kCcUmKfvBXbAf6rhRYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaE +QsiPlR4zxP/TP7mhfVEe7XWPxtnMUMtf15OyA51YBM4qBmOHf+MZAAAAIIaTJINn ++eUBXbki+PSAld2nhJh/LVmFsS+60WyvXkQ1wpsGGBsKAAAALAWCY4d/4wKbDCIh +BssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce62azJAAAAAAQBIKbpGG2dWTX8 +j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDEM0g12vYxoWM8Y81W+bHBw805 +I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUrk0mXubZvyl4GBg== +-----END PGP PUBLIC KEY BLOCK-----` }); + + const plaintext = 'What we need from the grocery store:\n\n- tofu\n- vegetables\n- noodles\n'; + const message = await openpgp.readCleartextMessage({ cleartextMessage }); + + const { signatures, data } = await openpgp.verify({ message, verificationKeys: publicKey }); + expect(data).to.equal(plaintext); + expect(signatures).to.have.length(1); + expect(await signatures[0].verified).to.be.true; + expect((await signatures[0].signature).packets.length).to.equal(1); + }); + it('Verify cleartext signed message with two signatures with openpgp.verify', async function() { const cleartextMessage = ['-----BEGIN PGP SIGNED MESSAGE-----',