Make PacketList a valid subtype of Array and update Packet.tag types (#1289)

Changes:
- Implementation:
  - Remove `PacketList.prototype.concat` and `push`
    (we solely rely on `Array.push` instead)
  - Fix https://github.com/openpgpjs/openpgpjs/issues/907 by
    correctly handling result of `filterByTag`
  - Implement `write()` method for `Trust` and `Marker` packets,
    to make them compatible with the `BasePacket` interface
- Types:
  - Simplify and updated `PacketList` type definitions
  - Fix types for `Packet.tag`, which is `static` since
    https://github.com/openpgpjs/openpgpjs/pull/1268
  - Prevent passing SubkeyPackets where KeyPackets are expected,
    and vice versa
This commit is contained in:
larabr 2021-04-29 17:18:39 +02:00 committed by GitHub
parent 2d07c43030
commit aeddac438e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 189 additions and 154 deletions

83
openpgp.d.ts vendored
View File

@ -292,7 +292,7 @@ interface PartialConfig extends Partial<Config> {}
/* ############## v5 PACKET #################### */ /* ############## v5 PACKET #################### */
declare abstract class BasePacket { declare abstract class BasePacket {
public tag: enums.packet; static readonly tag: enums.packet;
public read(bytes: Uint8Array): void; public read(bytes: Uint8Array): void;
public write(): Uint8Array; public write(): Uint8Array;
} }
@ -314,14 +314,20 @@ declare abstract class BasePublicKeyPacket extends BasePacket {
public getKeyID(): KeyID; public getKeyID(): KeyID;
public isDecrypted(): boolean; public isDecrypted(): boolean;
public publicParams: object; public publicParams: object;
// `isSubkey` is a dummy method to ensure that Subkey packets are not accepted as Key one, and vice versa.
// The key class hierarchy is already modelled to cover this, but the concrete key packet classes
// have compatible structure and TS can't detect the difference.
protected isSubkey(): boolean;
} }
export class PublicKeyPacket extends BasePublicKeyPacket { export class PublicKeyPacket extends BasePublicKeyPacket {
public tag: enums.packet.publicKey; static readonly tag: enums.packet.publicKey;
protected isSubkey(): false;
} }
export class PublicSubkeyPacket extends BasePublicKeyPacket { export class PublicSubkeyPacket extends BasePublicKeyPacket {
public tag: enums.packet.publicSubkey; static readonly tag: enums.packet.publicSubkey;
protected isSubkey(): true;
} }
declare abstract class BaseSecretKeyPacket extends BasePublicKeyPacket { declare abstract class BaseSecretKeyPacket extends BasePublicKeyPacket {
@ -334,56 +340,78 @@ declare abstract class BaseSecretKeyPacket extends BasePublicKeyPacket {
} }
export class SecretKeyPacket extends BaseSecretKeyPacket { export class SecretKeyPacket extends BaseSecretKeyPacket {
public tag: enums.packet.secretKey; static readonly tag: enums.packet.secretKey;
protected isSubkey(): false;
} }
export class SecretSubkeyPacket extends BaseSecretKeyPacket { export class SecretSubkeyPacket extends BaseSecretKeyPacket {
public tag: enums.packet.secretSubkey; static readonly tag: enums.packet.secretSubkey;
protected isSubkey(): true;
} }
export class CompressedDataPacket extends BasePacket { export class CompressedDataPacket extends BasePacket {
public tag: enums.packet.compressedData; static readonly tag: enums.packet.compressedData;
private compress(): void;
private decompress(): void;
} }
export class SymEncryptedIntegrityProtectedDataPacket extends BasePacket { export class SymEncryptedIntegrityProtectedDataPacket extends BasePacket {
public tag: enums.packet.symEncryptedIntegrityProtectedData; static readonly tag: enums.packet.symEncryptedIntegrityProtectedData;
} }
export class AEADEncryptedDataPacket extends BasePacket { export class AEADEncryptedDataPacket extends BasePacket {
public tag: enums.packet.aeadEncryptedData; static readonly tag: enums.packet.aeadEncryptedData;
private decrypt(sessionKeyAlgorithm: string, sessionKey: Uint8Array): void;
private encrypt(sessionKeyAlgorithm: string, sessionKey: Uint8Array, config?: Config): void;
private crypt(fn: Function, sessionKey: Uint8Array, data: MaybeStream<Uint8Array>): MaybeStream<Uint8Array>
} }
export class PublicKeyEncryptedSessionKeyPaclet extends BasePacket { export class PublicKeyEncryptedSessionKeyPaclet extends BasePacket {
public tag: enums.packet.publicKeyEncryptedSessionKey; static readonly tag: enums.packet.publicKeyEncryptedSessionKey;
private decrypt(keyPacket: SecretKeyPacket): Promise<true>; // throws on error
private encrypt(keyPacket: PublicKeyPacket): Promise<true>; // throws on error
} }
export class SymEncryptedSessionKey extends BasePacket { export class SymEncryptedSessionKey extends BasePacket {
public tag: enums.packet.symEncryptedSessionKey; static readonly tag: enums.packet.symEncryptedSessionKey;
private decrypt(passphrase: string): Promise<void>;
private encrypt(passphrase: string, config?: Config): Promise<void>;
} }
export class LiteralDataPacket extends BasePacket { export class LiteralDataPacket extends BasePacket {
public tag: enums.packet.literalData; static readonly tag: enums.packet.literalData;
private getText(clone?: boolean): MaybeStream<string>;
private getBytes(clone?: boolean): MaybeStream<Uint8Array>;
private setText(text: MaybeStream<string>, format?: DataPacketType);
private setBytes(bytes: MaybeStream<Uint8Array>, format?: DataPacketType);
private setFilename(filename: string);
private getFilename(): string;
private writeHeader(): Uint8Array;
} }
export class SymmetricallyEncryptedDataPacket extends BasePacket { export class SymmetricallyEncryptedDataPacket extends BasePacket {
public tag: enums.packet.symmetricallyEncryptedData; static readonly tag: enums.packet.symmetricallyEncryptedData;
private decrypt(sessionKeyAlgorithm: enums.symmetric, sessionKey: Uint8Array, config?: Config): void;
private encrypt(sessionKeyAlgorithm: enums.symmetric, sessionKey: Uint8Array, config?: Config): void;
} }
export class MarkerPacket extends BasePacket { export class MarkerPacket extends BasePacket {
public tag: enums.packet.marker; static readonly tag: enums.packet.marker;
} }
export class UserAttributePacket extends BasePacket { export class UserAttributePacket extends BasePacket {
public tag: enums.packet.userAttribute; static readonly tag: enums.packet.userAttribute;
private equals(packet: UserAttributePacket): boolean;
} }
export class OnePassSignaturePacket extends BasePacket { export class OnePassSignaturePacket extends BasePacket {
public tag: enums.packet.onePassSignature; static readonly tag: enums.packet.onePassSignature;
public correspondingSig?: Promise<SignaturePacket>; public correspondingSig?: Promise<SignaturePacket>;
private verify: SignaturePacket['verify'];
} }
export class UserIDPacket extends BasePacket { export class UserIDPacket extends BasePacket {
public readonly tag: enums.packet.userID; static readonly tag: enums.packet.userID;
public readonly name: string; public readonly name: string;
public readonly comment: string; public readonly comment: string;
public readonly email: string; public readonly email: string;
@ -392,7 +420,7 @@ export class UserIDPacket extends BasePacket {
} }
export class SignaturePacket extends BasePacket { export class SignaturePacket extends BasePacket {
public tag: enums.packet.signature; static readonly tag: enums.packet.signature;
public version: number; public version: number;
public signatureType: enums.signature | null; public signatureType: enums.signature | null;
public hashAlgorithm: enums.hash | null; public hashAlgorithm: enums.hash | null;
@ -443,7 +471,7 @@ export class SignaturePacket extends BasePacket {
} }
export class TrustPacket extends BasePacket { export class TrustPacket extends BasePacket {
public tag: enums.packet.trust; static readonly tag: enums.packet.trust;
} }
export type AnyPacket = BasePacket; export type AnyPacket = BasePacket;
@ -453,24 +481,13 @@ export type AnyKeyPacket = BasePublicKeyPacket;
type DataPacketType = 'utf8' | 'binary' | 'text' | 'mime'; type DataPacketType = 'utf8' | 'binary' | 'text' | 'mime';
export class PacketList<PACKET_TYPE> extends Array<PACKET_TYPE> { export class PacketList<T extends AnyPacket> extends Array<T> {
[index: number]: PACKET_TYPE;
public length: number; public length: number;
public read(bytes: Uint8Array, allowedPackets?: object, config?: Config): void; public read(bytes: Uint8Array, allowedPackets?: object, config?: Config): void;
public write(): Uint8Array; public write(): Uint8Array;
public push(...packet: PACKET_TYPE[]): number; public filterByTag(...args: enums.packet[]): PacketList<T>;
public pop(): PACKET_TYPE; public indexOfTag(...tags: enums.packet[]): number[];
public filter(callback: (packet: PACKET_TYPE, i: number, self: PacketList<PACKET_TYPE>) => void): PacketList<PACKET_TYPE>; public findPacket(tag: enums.packet): T | undefined;
public filterByTag(...args: enums.packet[]): PacketList<PACKET_TYPE>;
public forEach(callback: (packet: PACKET_TYPE, i: number, self: PacketList<PACKET_TYPE>) => void): void;
public map<RETURN_TYPE>(callback: (packet: PACKET_TYPE, i: number, self: PacketList<PACKET_TYPE>) => RETURN_TYPE): PacketList<RETURN_TYPE>;
// some()
// every()
// findPacket()
// indexOfTag()
// slice()
// concat()
// fromStructuredClone()
} }
/* ############## v5 STREAM #################### */ /* ############## v5 STREAM #################### */

View File

@ -150,10 +150,10 @@ class Key {
toPacketlist() { toPacketlist() {
const packetlist = new PacketList(); const packetlist = new PacketList();
packetlist.push(this.keyPacket); packetlist.push(this.keyPacket);
packetlist.concat(this.revocationSignatures); packetlist.push(...this.revocationSignatures);
packetlist.concat(this.directSignatures); packetlist.push(...this.directSignatures);
this.users.map(user => packetlist.concat(user.toPacketlist())); this.users.map(user => packetlist.push(...user.toPacketlist()));
this.subKeys.map(subKey => packetlist.concat(subKey.toPacketlist())); this.subKeys.map(subKey => packetlist.push(...subKey.toPacketlist()));
return packetlist; return packetlist;
} }

View File

@ -34,8 +34,8 @@ class SubKey {
toPacketlist() { toPacketlist() {
const packetlist = new PacketList(); const packetlist = new PacketList();
packetlist.push(this.keyPacket); packetlist.push(this.keyPacket);
packetlist.concat(this.revocationSignatures); packetlist.push(...this.revocationSignatures);
packetlist.concat(this.bindingSignatures); packetlist.push(...this.bindingSignatures);
return packetlist; return packetlist;
} }

View File

@ -30,9 +30,9 @@ class User {
toPacketlist() { toPacketlist() {
const packetlist = new PacketList(); const packetlist = new PacketList();
packetlist.push(this.userID || this.userAttribute); packetlist.push(this.userID || this.userAttribute);
packetlist.concat(this.revocationSignatures); packetlist.push(...this.revocationSignatures);
packetlist.concat(this.selfCertifications); packetlist.push(...this.selfCertifications);
packetlist.concat(this.otherCertifications); packetlist.push(...this.otherCertifications);
return packetlist; return packetlist;
} }

View File

@ -170,7 +170,7 @@ export class Message {
let exception; let exception;
if (passwords) { if (passwords) {
const symESKeyPacketlist = this.packets.filterByTag(enums.packet.symEncryptedSessionKey); const symESKeyPacketlist = this.packets.filterByTag(enums.packet.symEncryptedSessionKey);
if (!symESKeyPacketlist) { if (symESKeyPacketlist.length === 0) {
throw new Error('No symmetrically encrypted session key packet found.'); throw new Error('No symmetrically encrypted session key packet found.');
} }
await Promise.all(passwords.map(async function(password, i) { await Promise.all(passwords.map(async function(password, i) {
@ -192,7 +192,7 @@ export class Message {
})); }));
} else if (privateKeys) { } else if (privateKeys) {
const pkESKeyPacketlist = this.packets.filterByTag(enums.packet.publicKeyEncryptedSessionKey); const pkESKeyPacketlist = this.packets.filterByTag(enums.packet.publicKeyEncryptedSessionKey);
if (!pkESKeyPacketlist) { if (pkESKeyPacketlist.length === 0) {
throw new Error('No public key encrypted session key packet found.'); throw new Error('No public key encrypted session key packet found.');
} }
await Promise.all(pkESKeyPacketlist.map(async function(keyPacket) { await Promise.all(pkESKeyPacketlist.map(async function(keyPacket) {
@ -385,7 +385,7 @@ export class Message {
delete pkESKeyPacket.sessionKey; // delete plaintext session key after encryption delete pkESKeyPacket.sessionKey; // delete plaintext session key after encryption
return pkESKeyPacket; return pkESKeyPacket;
})); }));
packetlist.concat(results); packetlist.push(...results);
} }
if (passwords) { if (passwords) {
const testDecrypt = async function(keyPacket, password) { const testDecrypt = async function(keyPacket, password) {
@ -420,7 +420,7 @@ export class Message {
}; };
const results = await Promise.all(passwords.map(pwd => encryptPassword(sessionKey, algorithm, aeadAlgorithm, pwd))); const results = await Promise.all(passwords.map(pwd => encryptPassword(sessionKey, algorithm, aeadAlgorithm, pwd)));
packetlist.concat(results); packetlist.push(...results);
} }
return new Message(packetlist); return new Message(packetlist);
@ -487,7 +487,7 @@ export class Message {
}); });
packetlist.push(literalDataPacket); packetlist.push(literalDataPacket);
packetlist.concat(await createSignaturePackets(literalDataPacket, privateKeys, signature, signingKeyIDs, date, userIDs, false, config)); packetlist.push(...(await createSignaturePackets(literalDataPacket, privateKeys, signature, signingKeyIDs, date, userIDs, false, config)));
return new Message(packetlist); return new Message(packetlist);
} }
@ -551,7 +551,7 @@ export class Message {
throw new Error('Can only verify message with one literal data packet.'); throw new Error('Can only verify message with one literal data packet.');
} }
if (stream.isArrayStream(msg.packets.stream)) { if (stream.isArrayStream(msg.packets.stream)) {
msg.packets.concat(await stream.readToEnd(msg.packets.stream, _ => _)); msg.packets.push(...await stream.readToEnd(msg.packets.stream, _ => _ || []));
} }
const onePassSigList = msg.packets.filterByTag(enums.packet.onePassSignature).reverse(); const onePassSigList = msg.packets.filterByTag(enums.packet.onePassSignature).reverse();
const signatureList = msg.packets.filterByTag(enums.packet.signature); const signatureList = msg.packets.filterByTag(enums.packet.signature);
@ -686,7 +686,7 @@ export async function createSignaturePackets(literalDataPacket, privateKeys, sig
if (signature) { if (signature) {
const existingSigPacketlist = signature.packets.filterByTag(enums.packet.signature); const existingSigPacketlist = signature.packets.filterByTag(enums.packet.signature);
packetlist.concat(existingSigPacketlist); packetlist.push(...existingSigPacketlist);
} }
return packetlist; return packetlist;
} }
@ -752,7 +752,7 @@ async function createVerificationObject(signature, literalDataList, keys, date =
signature: (async () => { signature: (async () => {
const sig = await signaturePacket; const sig = await signaturePacket;
const packetlist = new PacketList(); const packetlist = new PacketList();
packetlist.push(sig); sig && packetlist.push(sig);
return new Signature(packetlist); return new Signature(packetlist);
})() })()
}; };

View File

@ -25,6 +25,7 @@ import LiteralDataPacket from './literal_data';
import CompressedDataPacket from './compressed_data'; import CompressedDataPacket from './compressed_data';
import OnePassSignaturePacket from './one_pass_signature'; import OnePassSignaturePacket from './one_pass_signature';
import SignaturePacket from './signature'; import SignaturePacket from './signature';
import PacketList from './packetlist';
// An AEAD-encrypted Data packet can contain the following packet types // An AEAD-encrypted Data packet can contain the following packet types
const allowedPackets = /*#__PURE__*/ util.constructAllowedPackets([ const allowedPackets = /*#__PURE__*/ util.constructAllowedPackets([
@ -93,6 +94,7 @@ class AEADEncryptedDataPacket {
* @async * @async
*/ */
async decrypt(sessionKeyAlgorithm, key) { async decrypt(sessionKeyAlgorithm, key) {
this.packets = new PacketList();
await this.packets.read(await this.crypt('decrypt', key, stream.clone(this.encrypted)), allowedPackets); await this.packets.read(await this.crypt('decrypt', key, stream.clone(this.encrypted)), allowedPackets);
} }

View File

@ -27,6 +27,7 @@ import defaultConfig from '../config';
import LiteralDataPacket from './literal_data'; import LiteralDataPacket from './literal_data';
import OnePassSignaturePacket from './one_pass_signature'; import OnePassSignaturePacket from './one_pass_signature';
import SignaturePacket from './signature'; import SignaturePacket from './signature';
import PacketList from './packetlist';
// A Compressed Data packet can contain the following packet types // A Compressed Data packet can contain the following packet types
const allowedPackets = /*#__PURE__*/ util.constructAllowedPackets([ const allowedPackets = /*#__PURE__*/ util.constructAllowedPackets([
@ -116,6 +117,7 @@ class CompressedDataPacket {
throw new Error(this.algorithm + ' decompression not supported'); throw new Error(this.algorithm + ' decompression not supported');
} }
this.packets = new PacketList();
await this.packets.read(decompress_fns[this.algorithm](this.compressed), allowedPackets); await this.packets.read(decompress_fns[this.algorithm](this.compressed), allowedPackets);
} }

View File

@ -28,7 +28,13 @@ import enums from '../enums';
* tag. With PGP 5.x, this packet has been reassigned and is reserved for use as * tag. With PGP 5.x, this packet has been reassigned and is reserved for use as
* the Marker packet. * the Marker packet.
* *
* Such a packet MUST be ignored when received. * The body of this packet consists of:
* The three octets 0x50, 0x47, 0x50 (which spell "PGP" in UTF-8).
*
* Such a packet MUST be ignored when received. It may be placed at the
* beginning of a message that uses features not available in PGP
* version 2.6 in order to cause that version to report that newer
* software is necessary to process the message.
*/ */
class MarkerPacket { class MarkerPacket {
static get tag() { static get tag() {
@ -36,15 +42,9 @@ class MarkerPacket {
} }
/** /**
* Parsing function for a literal data packet (tag 10). * Parsing function for a marker data packet (tag 10).
* * @param {Uint8Array} bytes - Payload of a tag 10 packet
* @param {String} input - Payload of a tag 10 packet * @returns {Boolean} whether the packet payload contains "PGP"
* @param {Integer} position
* Position to start reading from the input string
* @param {Integer} len
* Length of the packet or the remaining length of
* input at position
* @returns {MarkerPacket} Object representation.
*/ */
read(bytes) { read(bytes) {
if (bytes[0] === 0x50 && // P if (bytes[0] === 0x50 && // P
@ -52,9 +52,13 @@ class MarkerPacket {
bytes[2] === 0x50) { // P bytes[2] === 0x50) { // P
return true; return true;
} }
// marker packet does not contain "PGP"
return false; return false;
} }
// eslint-disable-next-line class-methods-use-this
write() {
return new Uint8Array([0x50, 0x47, 0x50]);
}
} }
export default MarkerPacket; export default MarkerPacket;

View File

@ -127,30 +127,17 @@ class PacketList extends Array {
} }
/** /**
* Adds a packet to the list. This is the only supported method of doing so; * Creates a new PacketList with all packets matching the given tag(s)
* writing to packetlist[i] directly will result in an error. * @param {...module:enums.packet} tags - packet tags to look for
* @param {Object} packet - Packet to push * @returns {PacketList}
*/ */
push(packet) { filterByTag(...tags) {
if (!packet) {
return;
}
packet.packets = packet.packets || new PacketList();
super.push(packet);
}
/**
* Creates a new PacketList with all packets from the given types
*/
filterByTag(...args) {
const filtered = new PacketList(); const filtered = new PacketList();
const handle = tag => packetType => tag === packetType; const handle = tag => packetType => tag === packetType;
for (let i = 0; i < this.length; i++) { for (let i = 0; i < this.length; i++) {
if (args.some(handle(this[i].constructor.tag))) { if (tags.some(handle(this[i].constructor.tag))) {
filtered.push(this[i]); filtered.push(this[i]);
} }
} }
@ -159,42 +146,32 @@ class PacketList extends Array {
} }
/** /**
* Traverses packet tree and returns first matching packet * Traverses packet list and returns first packet with matching tag
* @param {module:enums.packet} type - The packet type * @param {module:enums.packet} tag - The packet tag
* @returns {Packet|undefined} * @returns {Packet|undefined}
*/ */
findPacket(type) { findPacket(tag) {
return this.find(packet => packet.constructor.tag === type); return this.find(packet => packet.constructor.tag === tag);
} }
/** /**
* Returns array of found indices by tag * Find indices of packets with the given tag(s)
* @param {...module:enums.packet} tags - packet tags to look for
* @returns {Integer[]} packet indices
*/ */
indexOfTag(...args) { indexOfTag(...tags) {
const tagIndex = []; const tagIndex = [];
const that = this; const that = this;
const handle = tag => packetType => tag === packetType; const handle = tag => packetType => tag === packetType;
for (let i = 0; i < this.length; i++) { for (let i = 0; i < this.length; i++) {
if (args.some(handle(that[i].constructor.tag))) { if (tags.some(handle(that[i].constructor.tag))) {
tagIndex.push(i); tagIndex.push(i);
} }
} }
return tagIndex; return tagIndex;
} }
/**
* Concatenates packetlist or array of packets
*/
concat(packetlist) {
if (packetlist) {
for (let i = 0; i < packetlist.length; i++) {
this.push(packetlist[i]);
}
}
return this;
}
} }
export default PacketList; export default PacketList;

View File

@ -25,6 +25,7 @@ import LiteralDataPacket from './literal_data';
import CompressedDataPacket from './compressed_data'; import CompressedDataPacket from './compressed_data';
import OnePassSignaturePacket from './one_pass_signature'; import OnePassSignaturePacket from './one_pass_signature';
import SignaturePacket from './signature'; import SignaturePacket from './signature';
import PacketList from './packetlist';
// A SEIP packet can contain the following packet types // A SEIP packet can contain the following packet types
const allowedPackets = /*#__PURE__*/ util.constructAllowedPackets([ const allowedPackets = /*#__PURE__*/ util.constructAllowedPackets([
@ -53,15 +54,7 @@ class SymEncryptedIntegrityProtectedDataPacket {
constructor() { constructor() {
this.version = VERSION; this.version = VERSION;
/** The encrypted payload. */ this.encrypted = null;
this.encrypted = null; // string
/**
* If after decrypting the packet this is set to true,
* a modification has been detected and thus the contents
* should be discarded.
* @type {Boolean}
*/
this.modification = false;
this.packets = null; this.packets = null;
} }
@ -138,6 +131,7 @@ class SymEncryptedIntegrityProtectedDataPacket {
if (!util.isStream(encrypted) || !config.allowUnauthenticatedStream) { if (!util.isStream(encrypted) || !config.allowUnauthenticatedStream) {
packetbytes = await stream.readToEnd(packetbytes); packetbytes = await stream.readToEnd(packetbytes);
} }
this.packets = new PacketList();
await this.packets.read(packetbytes, allowedPackets); await this.packets.read(packetbytes, allowedPackets);
return true; return true;
} }

View File

@ -25,6 +25,7 @@ import LiteralDataPacket from './literal_data';
import CompressedDataPacket from './compressed_data'; import CompressedDataPacket from './compressed_data';
import OnePassSignaturePacket from './one_pass_signature'; import OnePassSignaturePacket from './one_pass_signature';
import SignaturePacket from './signature'; import SignaturePacket from './signature';
import PacketList from './packetlist';
// A SE packet can contain the following packet types // A SE packet can contain the following packet types
const allowedPackets = /*#__PURE__*/ util.constructAllowedPackets([ const allowedPackets = /*#__PURE__*/ util.constructAllowedPackets([
@ -91,6 +92,7 @@ class SymmetricallyEncryptedDataPacket {
encrypted.subarray(2, crypto.cipher[sessionKeyAlgorithm].blockSize + 2) encrypted.subarray(2, crypto.cipher[sessionKeyAlgorithm].blockSize + 2)
); );
this.packets = new PacketList();
await this.packets.read(decrypted, allowedPackets); await this.packets.read(decrypted, allowedPackets);
} }

View File

@ -28,6 +28,11 @@ class TrustPacket {
* @param {String} byptes - Payload of a tag 12 packet * @param {String} byptes - Payload of a tag 12 packet
*/ */
read() {} // TODO read() {} // TODO
// eslint-disable-next-line class-methods-use-this
write() {
throw new Error('Trust packets are not supported');
}
} }
export default TrustPacket; export default TrustPacket;

View File

@ -2722,7 +2722,7 @@ module.exports = () => describe('OpenPGP.js public api tests', function() {
}; };
return openpgp.sign(signOpt).then(async function (signed) { return openpgp.sign(signOpt).then(async function (signed) {
const message = await openpgp.readMessage({ binaryMessage: signed }); const message = await openpgp.readMessage({ binaryMessage: signed });
message.packets.concat(await openpgp.stream.readToEnd(message.packets.stream, _ => _)); message.packets.push(...await openpgp.stream.readToEnd(message.packets.stream, _ => _));
const packets = new openpgp.PacketList(); const packets = new openpgp.PacketList();
packets.push(message.packets.findPacket(openpgp.enums.packet.signature)); packets.push(message.packets.findPacket(openpgp.enums.packet.signature));
packets.push(message.packets.findPacket(openpgp.enums.packet.literalData)); packets.push(message.packets.findPacket(openpgp.enums.packet.literalData));
@ -2762,7 +2762,7 @@ module.exports = () => describe('OpenPGP.js public api tests', function() {
return openpgp.sign(signOpt).then(async function (signed) { return openpgp.sign(signOpt).then(async function (signed) {
expect(openpgp.stream.isStream(signed)).to.equal(global.ReadableStream ? 'web' : 'node'); expect(openpgp.stream.isStream(signed)).to.equal(global.ReadableStream ? 'web' : 'node');
const message = await openpgp.readMessage({ binaryMessage: signed }); const message = await openpgp.readMessage({ binaryMessage: signed });
message.packets.concat(await openpgp.stream.readToEnd(message.packets.stream, _ => _)); message.packets.push(...await openpgp.stream.readToEnd(message.packets.stream, _ => _));
const packets = new openpgp.PacketList(); const packets = new openpgp.PacketList();
packets.push(message.packets.findPacket(openpgp.enums.packet.signature)); packets.push(message.packets.findPacket(openpgp.enums.packet.signature));
packets.push(message.packets.findPacket(openpgp.enums.packet.literalData)); packets.push(message.packets.findPacket(openpgp.enums.packet.literalData));

View File

@ -81,8 +81,9 @@ module.exports = () => describe("Packet", function() {
try { try {
const enc = new openpgp.SymmetricallyEncryptedDataPacket(); const enc = new openpgp.SymmetricallyEncryptedDataPacket();
message.push(enc); enc.packets = new openpgp.PacketList();
enc.packets.push(literal); enc.packets.push(literal);
message.push(enc);
const key = new Uint8Array([1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2]); const key = new Uint8Array([1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2]);
const algo = 'aes256'; const algo = 'aes256';
@ -112,8 +113,9 @@ module.exports = () => describe("Packet", function() {
literal.setText(testText); literal.setText(testText);
const enc = new openpgp.SymmetricallyEncryptedDataPacket(); const enc = new openpgp.SymmetricallyEncryptedDataPacket();
enc.packets = new openpgp.PacketList();
enc.packets.push(literal);
message.push(enc); message.push(enc);
await enc.packets.push(literal);
const key = new Uint8Array([1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2]); const key = new Uint8Array([1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2]);
const algo = 'aes256'; const algo = 'aes256';
@ -135,11 +137,12 @@ module.exports = () => describe("Packet", function() {
const literal = new openpgp.LiteralDataPacket(); const literal = new openpgp.LiteralDataPacket();
const enc = new openpgp.SymEncryptedIntegrityProtectedDataPacket(); const enc = new openpgp.SymEncryptedIntegrityProtectedDataPacket();
const msg = new openpgp.PacketList(); enc.packets = new openpgp.PacketList();
msg.push(enc);
literal.setText(testText);
enc.packets.push(literal); enc.packets.push(literal);
const msg = new openpgp.PacketList();
msg.push(enc);
literal.setText(testText);
await enc.encrypt(algo, key, undefined, openpgp.config); await enc.encrypt(algo, key, undefined, openpgp.config);
const msg2 = new openpgp.PacketList(); const msg2 = new openpgp.PacketList();
@ -158,12 +161,12 @@ module.exports = () => describe("Packet", function() {
const algo = 'aes256'; const algo = 'aes256';
const testText = input.createSomeMessage(); const testText = input.createSomeMessage();
const literal = new openpgp.LiteralDataPacket(); const literal = new openpgp.LiteralDataPacket();
const enc = new openpgp.AEADEncryptedDataPacket();
const msg = new openpgp.PacketList();
msg.push(enc);
literal.setText(testText); literal.setText(testText);
const enc = new openpgp.AEADEncryptedDataPacket();
enc.packets = new openpgp.PacketList();
enc.packets.push(literal); enc.packets.push(literal);
const msg = new openpgp.PacketList();
msg.push(enc);
const msg2 = new openpgp.PacketList(); const msg2 = new openpgp.PacketList();
@ -210,13 +213,13 @@ module.exports = () => describe("Packet", function() {
const algo = 'aes256'; const algo = 'aes256';
const literal = new openpgp.LiteralDataPacket(); const literal = new openpgp.LiteralDataPacket();
const enc = new openpgp.AEADEncryptedDataPacket();
const msg = new openpgp.PacketList();
enc.aeadAlgorithm = 'experimentalGCM';
msg.push(enc);
literal.setText(testText); literal.setText(testText);
const enc = new openpgp.AEADEncryptedDataPacket();
enc.aeadAlgorithm = 'experimentalGCM';
enc.packets = new openpgp.PacketList();
enc.packets.push(literal); enc.packets.push(literal);
const msg = new openpgp.PacketList();
msg.push(enc);
const msg2 = new openpgp.PacketList(); const msg2 = new openpgp.PacketList();
@ -252,13 +255,13 @@ module.exports = () => describe("Packet", function() {
const algo = 'aes128'; const algo = 'aes128';
const literal = new openpgp.LiteralDataPacket(0); const literal = new openpgp.LiteralDataPacket(0);
const enc = new openpgp.AEADEncryptedDataPacket();
const msg = new openpgp.PacketList();
msg.push(enc);
literal.setBytes(util.stringToUint8Array('Hello, world!\n'), openpgp.enums.literal.binary); literal.setBytes(util.stringToUint8Array('Hello, world!\n'), openpgp.enums.literal.binary);
literal.filename = ''; literal.filename = '';
const enc = new openpgp.AEADEncryptedDataPacket();
enc.packets = new openpgp.PacketList();
enc.packets.push(literal); enc.packets.push(literal);
const msg = new openpgp.PacketList();
msg.push(enc);
const msg2 = new openpgp.PacketList(); const msg2 = new openpgp.PacketList();
@ -446,8 +449,11 @@ module.exports = () => describe("Packet", function() {
const testText = input.createSomeMessage(); const testText = input.createSomeMessage();
const literal = new openpgp.LiteralDataPacket(); const literal = new openpgp.LiteralDataPacket();
literal.setText(testText);
const skesk = new openpgp.SymEncryptedSessionKeyPacket(); const skesk = new openpgp.SymEncryptedSessionKeyPacket();
const seip = new openpgp.SymEncryptedIntegrityProtectedDataPacket(); const seip = new openpgp.SymEncryptedIntegrityProtectedDataPacket();
seip.packets = new openpgp.PacketList();
seip.packets.push(literal);
const msg = new openpgp.PacketList(); const msg = new openpgp.PacketList();
msg.push(skesk); msg.push(skesk);
@ -457,9 +463,6 @@ module.exports = () => describe("Packet", function() {
await skesk.encrypt(passphrase, openpgp.config); await skesk.encrypt(passphrase, openpgp.config);
const key = skesk.sessionKey; const key = skesk.sessionKey;
literal.setText(testText);
seip.packets.push(literal);
await seip.encrypt(algo, key, undefined, openpgp.config); await seip.encrypt(algo, key, undefined, openpgp.config);
const msg2 = new openpgp.PacketList(); const msg2 = new openpgp.PacketList();
@ -485,10 +488,12 @@ module.exports = () => describe("Packet", function() {
const testText = input.createSomeMessage(); const testText = input.createSomeMessage();
const literal = new openpgp.LiteralDataPacket(); const literal = new openpgp.LiteralDataPacket();
literal.setText(testText);
const skesk = new openpgp.SymEncryptedSessionKeyPacket(); const skesk = new openpgp.SymEncryptedSessionKeyPacket();
const aeadEnc = new openpgp.AEADEncryptedDataPacket(); const aeadEnc = new openpgp.AEADEncryptedDataPacket();
aeadEnc.packets = new openpgp.PacketList();
aeadEnc.packets.push(literal);
const msg = new openpgp.PacketList(); const msg = new openpgp.PacketList();
msg.push(skesk); msg.push(skesk);
msg.push(aeadEnc); msg.push(aeadEnc);
@ -496,9 +501,6 @@ module.exports = () => describe("Packet", function() {
await skesk.encrypt(passphrase, openpgp.config); await skesk.encrypt(passphrase, openpgp.config);
const key = skesk.sessionKey; const key = skesk.sessionKey;
literal.setText(testText);
aeadEnc.packets.push(literal);
await aeadEnc.encrypt(algo, key, undefined, openpgp.config); await aeadEnc.encrypt(algo, key, undefined, openpgp.config);
const msg2 = new openpgp.PacketList(); const msg2 = new openpgp.PacketList();
@ -556,21 +558,20 @@ module.exports = () => describe("Packet", function() {
const algo = 'aes128'; const algo = 'aes128';
const literal = new openpgp.LiteralDataPacket(0); const literal = new openpgp.LiteralDataPacket(0);
literal.setBytes(util.stringToUint8Array('Hello, world!\n'), openpgp.enums.literal.binary);
literal.filename = '';
const skesk = new openpgp.SymEncryptedSessionKeyPacket(); const skesk = new openpgp.SymEncryptedSessionKeyPacket();
skesk.sessionKeyAlgorithm = algo;
const encData = new openpgp.AEADEncryptedDataPacket(); const encData = new openpgp.AEADEncryptedDataPacket();
encData.packets = new openpgp.PacketList();
encData.packets.push(literal);
const msg = new openpgp.PacketList(); const msg = new openpgp.PacketList();
msg.push(skesk); msg.push(skesk);
msg.push(encData); msg.push(encData);
skesk.sessionKeyAlgorithm = algo;
await skesk.encrypt(passphrase, openpgp.config); await skesk.encrypt(passphrase, openpgp.config);
const key = skesk.sessionKey; const key = skesk.sessionKey;
literal.setBytes(util.stringToUint8Array('Hello, world!\n'), openpgp.enums.literal.binary);
literal.filename = '';
encData.packets.push(literal);
await encData.encrypt(algo, key, undefined, openpgp.config); await encData.encrypt(algo, key, undefined, openpgp.config);
const data = msg.write(); const data = msg.write();
@ -634,22 +635,21 @@ module.exports = () => describe("Packet", function() {
const algo = 'aes128'; const algo = 'aes128';
const literal = new openpgp.LiteralDataPacket(0); const literal = new openpgp.LiteralDataPacket(0);
literal.setBytes(util.stringToUint8Array('Hello, world!\n'), openpgp.enums.literal.binary);
literal.filename = '';
const key_enc = new openpgp.SymEncryptedSessionKeyPacket(); const key_enc = new openpgp.SymEncryptedSessionKeyPacket();
key_enc.sessionKeyAlgorithm = algo;
const enc = new openpgp.AEADEncryptedDataPacket(); const enc = new openpgp.AEADEncryptedDataPacket();
const msg = new openpgp.PacketList(); enc.packets = new openpgp.PacketList();
enc.packets.push(literal);
enc.aeadAlgorithm = key_enc.aeadAlgorithm = 'ocb'; enc.aeadAlgorithm = key_enc.aeadAlgorithm = 'ocb';
const msg = new openpgp.PacketList();
msg.push(key_enc); msg.push(key_enc);
msg.push(enc); msg.push(enc);
key_enc.sessionKeyAlgorithm = algo;
await key_enc.encrypt(passphrase, openpgp.config); await key_enc.encrypt(passphrase, openpgp.config);
const key = key_enc.sessionKey; const key = key_enc.sessionKey;
literal.setBytes(util.stringToUint8Array('Hello, world!\n'), openpgp.enums.literal.binary);
literal.filename = '';
enc.packets.push(literal);
await enc.encrypt(algo, key, undefined, openpgp.config); await enc.encrypt(algo, key, undefined, openpgp.config);
const data = msg.write(); const data = msg.write();
@ -747,7 +747,7 @@ module.exports = () => describe("Packet", function() {
await encData.decrypt(pkesk.sessionKeyAlgorithm, pkesk.sessionKey); await encData.decrypt(pkesk.sessionKeyAlgorithm, pkesk.sessionKey);
const payload = encData.packets[0].packets; const payload = encData.packets[0].packets;
payload.concat(await openpgp.stream.readToEnd(payload.stream, arr => arr)); payload.push(...await openpgp.stream.readToEnd(payload.stream, arr => arr));
const literal = payload[1]; const literal = payload[1];
const signature = payload[2]; const signature = payload[2];
@ -942,7 +942,7 @@ V+HOQJQxXJkVRYa3QrFUehiMzTeqqMdgC6ZqJy7+
const signed2 = new openpgp.PacketList(); const signed2 = new openpgp.PacketList();
await signed2.read(raw, allAllowedPackets); await signed2.read(raw, allAllowedPackets);
signed2.concat(await openpgp.stream.readToEnd(signed2.stream, arr => arr)); signed2.push(...await openpgp.stream.readToEnd(signed2.stream, arr => arr));
await Promise.all([ await Promise.all([
signed2[1].verify(key, openpgp.enums.signature.text, signed2[0]), signed2[1].verify(key, openpgp.enums.signature.text, signed2[0]),

View File

@ -56,7 +56,7 @@ async function testSubkeyTrust() {
fakeBindingSignature.keyFlags = [enums.keyFlags.signData]; fakeBindingSignature.keyFlags = [enums.keyFlags.signData];
await fakeBindingSignature.sign(attackerPrivKey.keyPacket, dataToSign); await fakeBindingSignature.sign(attackerPrivKey.keyPacket, dataToSign);
const newList = new PacketList(); const newList = new PacketList();
newList.concat([ newList.push(...[
pktPrivAttacker[0], // attacker private key pktPrivAttacker[0], // attacker private key
pktPrivAttacker[1], // attacker user pktPrivAttacker[1], // attacker user
pktPrivAttacker[2], // attacker self signature pktPrivAttacker[2], // attacker self signature

View File

@ -81,7 +81,7 @@ async function makeKeyValid() {
pusersig.readSubPackets(fake.writeHashedSubPackets(), false); pusersig.readSubPackets(fake.writeHashedSubPackets(), false);
// reconstruct the modified key // reconstruct the modified key
const newlist = new PacketList(); const newlist = new PacketList();
newlist.concat([pubkey, puser, pusersig]); newlist.push(...[pubkey, puser, pusersig]);
let modifiedkey = new Key(newlist); let modifiedkey = new Key(newlist);
// re-read the message to eliminate any // re-read the message to eliminate any
// behaviour due to cached values. // behaviour due to cached values.

View File

@ -6,9 +6,13 @@
* - if it fails to run, edit this file to match the actual library API, then edit the definitions file (openpgp.d.ts) accordingly. * - if it fails to run, edit this file to match the actual library API, then edit the definitions file (openpgp.d.ts) accordingly.
*/ */
import { generateKey, readKey, readKeys, Key, readMessage, createMessage, Message, createCleartextMessage, encrypt, decrypt, sign, verify, config } from '../..';
import { expect } from 'chai'; import { expect } from 'chai';
import {
generateKey, readKey, readKeys, Key,
readMessage, createMessage, Message, createCleartextMessage,
encrypt, decrypt, sign, verify, config, enums,
LiteralDataPacket, PacketList, CompressedDataPacket, PublicKeyPacket, PublicSubkeyPacket, SecretKeyPacket, SecretSubkeyPacket
} from '../..';
(async () => { (async () => {
@ -78,6 +82,34 @@ import { expect } from 'chai';
const verifiedBinaryData: Uint8Array = verifiedBinary.data; const verifiedBinaryData: Uint8Array = verifiedBinary.data;
expect(verifiedBinaryData).to.deep.equal(binary); expect(verifiedBinaryData).to.deep.equal(binary);
// Generic packetlist
const packets = new PacketList();
expect(packets.push()).to.equal(0);
expect(packets.push(new LiteralDataPacket())).to.equal(1);
packets.map(packet => packet.write);
// @ts-expect-error for unsafe downcasting
packets.map((packet: LiteralDataPacket) => packet.getText());
// @ts-expect-error for non-packet element
try { new PacketList().push(1); } catch (e) {}
// Packetlist of specific type
const literalPackets = new PacketList<LiteralDataPacket>();
literalPackets.push(new LiteralDataPacket());
literalPackets[0].write();
literalPackets.map((packet: LiteralDataPacket) => packet);
packets.push(...literalPackets);
// @ts-expect-error for incompatible packetlist type
literalPackets.push(...packets);
// @ts-expect-error for incompatible packet type
new PacketList<LiteralDataPacket>().push(new CompressedDataPacket());
// @ts-expect-error for incompatible packet type
new PacketList<PublicKeyPacket>().push(new PublicSubkeyPacket());
// @ts-expect-error for incompatible packet type
new PacketList<SecretKeyPacket>().push(new SecretSubkeyPacket());
expect(LiteralDataPacket.tag).to.equal(enums.packet.literalData);
// // Detached - sign cleartext message (armored) // // Detached - sign cleartext message (armored)
// import { Message, sign } from 'openpgp'; // import { Message, sign } from 'openpgp';
// const message = await createMessage({ text: util.removeTrailingSpaces(text) }); // const message = await createMessage({ text: util.removeTrailingSpaces(text) });