Implement packet criticality check

The Packet Tag space is now partitioned into critical packets and non-critical packets.
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.

See https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#section-4.3.1 .
This commit is contained in:
larabr
2023-07-25 11:41:20 +02:00
parent 95fd04db8e
commit 0b8501427b
3 changed files with 41 additions and 2 deletions

View File

@@ -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;

View File

@@ -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)) {

View File

@@ -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());