diff --git a/src/packet/packet.js b/src/packet/packet.js index c40082f5..6eea1209 100644 --- a/src/packet/packet.js +++ b/src/packet/packet.js @@ -308,6 +308,19 @@ export class UnsupportedError extends Error { } } +// unknown packet types are handled differently depending on the packet criticality +export class UnknownPacketError extends UnsupportedError { + constructor(...params) { + super(...params); + + if (Error.captureStackTrace) { + Error.captureStackTrace(this, UnsupportedError); + } + + this.name = 'UnknownPacketError'; + } +} + export class UnparseablePacket { constructor(tag, rawContent) { this.tag = tag; diff --git a/src/packet/packetlist.js b/src/packet/packetlist.js index 6207ef99..2c8e2a08 100644 --- a/src/packet/packetlist.js +++ b/src/packet/packetlist.js @@ -4,7 +4,8 @@ import { writeTag, writeHeader, writePartialLength, writeSimpleLength, UnparseablePacket, - UnsupportedError + UnsupportedError, + UnknownPacketError } from './packet'; import util from '../util'; import enums from '../enums'; @@ -25,7 +26,7 @@ export function newPacketFromTag(tag, allowedPackets) { try { packetType = enums.read(enums.packet, tag); } catch (e) { - throw new UnsupportedError(`Unknown packet type with tag: ${tag}`); + throw new UnknownPacketError(`Unknown packet type with tag: ${tag}`); } throw new Error(`Packet not allowed in this context: ${packetType}`); } @@ -87,6 +88,17 @@ class PacketList extends Array { await packet.read(parsed.packet, config); await writer.write(packet); } catch (e) { + // If an implementation encounters a critical packet where the packet type is unknown in a packet sequence, + // it MUST reject the whole packet sequence. On the other hand, an unknown non-critical packet MUST be ignored. + // Packet Tags from 0 to 39 are critical. Packet Tags from 40 to 63 are non-critical. + if (e instanceof UnknownPacketError) { + if (parsed.tag <= 39) { + await writer.abort(e); + } else { + return; + } + } + const throwUnsupportedError = !config.ignoreUnsupportedPackets && e instanceof UnsupportedError; const throwMalformedError = !config.ignoreMalformedPackets && !(e instanceof UnsupportedError); if (throwUnsupportedError || throwMalformedError || supportsStreaming(parsed.tag)) { diff --git a/test/general/packet.js b/test/general/packet.js index 0e1cdaab..696d2846 100644 --- a/test/general/packet.js +++ b/test/general/packet.js @@ -1514,6 +1514,20 @@ kePFjAnu9cpynKXu3usf8+FuBw2zLsg1Id1n7ttxoAte416KjBN9lFBt8mcu ).to.be.rejectedWith(/Unsupported S2K type/); }); + it('Throws on critical packet even with tolerant mode enabled', async function() { + const unknownPacketTag39 = util.hexToUint8Array('e70a750064bf943d6e756c6c'); // critical tag + + await expect(openpgp.PacketList.fromBinary(unknownPacketTag39, allAllowedPackets, { ...openpgp.config, ignoreUnsupportedPackets: false, ignoreMalformedPackets: false })).to.be.rejectedWith(/Unknown packet type/); + await expect(openpgp.PacketList.fromBinary(unknownPacketTag39, allAllowedPackets, { ...openpgp.config, ignoreUnsupportedPackets: true, ignoreMalformedPackets: true })).to.be.rejectedWith(/Unknown packet type/); + }); + + it('Ignores non-critical packet even with tolerant mode disabled', async function() { + const unknownPacketTag63 = util.hexToUint8Array('ff0a750064bf943d6e756c6c'); // non-critical tag + + await expect(openpgp.PacketList.fromBinary(unknownPacketTag63, allAllowedPackets, { ...openpgp.config, ignoreUnsupportedPackets: false, ignoreMalformedPackets: false })).to.eventually.have.length(0); + await expect(openpgp.PacketList.fromBinary(unknownPacketTag63, allAllowedPackets, { ...openpgp.config, ignoreUnsupportedPackets: true, ignoreMalformedPackets: true })).to.eventually.have.length(0); + }); + it('Throws on disallowed packet even with tolerant mode enabled', async function() { const packets = new openpgp.PacketList(); packets.push(new openpgp.LiteralDataPacket());