From 1f574e0df73abbb76000bdbd6febad1a9262ac7d Mon Sep 17 00:00:00 2001 From: larabr <7375870+larabr@users.noreply.github.com> Date: Fri, 16 Aug 2024 13:43:46 +0200 Subject: [PATCH] Add support for parsing transferable private keys with a primary public key and public subkeys --- src/key/factory.js | 25 ++++++++++++++----------- src/key/key.js | 15 ++++++++++++--- src/key/private_key.js | 5 +---- src/key/public_key.js | 5 +---- 4 files changed, 28 insertions(+), 22 deletions(-) diff --git a/src/key/factory.js b/src/key/factory.js index e66eaa10..6326e2eb 100644 --- a/src/key/factory.js +++ b/src/key/factory.js @@ -51,15 +51,18 @@ const allowedKeyPackets = /*#__PURE__*/ util.constructAllowedPackets([ * @throws if no key packet was found */ function createKey(packetlist) { - for (const packet of packetlist) { - switch (packet.constructor.tag) { - case enums.packet.secretKey: - return new PrivateKey(packetlist); - case enums.packet.publicKey: - return new PublicKey(packetlist); + if (packetlist[0]?.constructor.tag === enums.packet.secretKey) { + return new PrivateKey(packetlist); + } else if (packetlist[0]?.constructor.tag === enums.packet.publicKey) { + if (packetlist.findPacket(enums.packet.secretSubkey)) { + return new PrivateKey(packetlist); + } else { + return new PublicKey(packetlist); } + } else { + throw new Error('No primary key packet found'); } - throw new Error('No key packet found'); + } @@ -384,10 +387,10 @@ export async function readPrivateKey({ armoredKey, binaryKey, config, ...rest }) const packetlist = await PacketList.fromBinary(input, allowedKeyPackets, config); const keyIndex = packetlist.indexOfTag(enums.packet.publicKey, enums.packet.secretKey); for (let i = 0; i < keyIndex.length; i++) { - if (packetlist[keyIndex[i]].constructor.tag === enums.packet.publicKey) { + const firstPrivateKeyList = packetlist.slice(keyIndex[i], keyIndex[i + 1]); + if (packetlist[keyIndex[i]].constructor.tag === enums.packet.publicKey && !firstPrivateKeyList.findPacket(enums.packet.secretSubkey)) { continue; } - const firstPrivateKeyList = packetlist.slice(keyIndex[i], keyIndex[i + 1]); return new PrivateKey(firstPrivateKeyList); } throw new Error('No secret key packet found'); @@ -471,10 +474,10 @@ export async function readPrivateKeys({ armoredKeys, binaryKeys, config }) { const packetlist = await PacketList.fromBinary(input, allowedKeyPackets, config); const keyIndex = packetlist.indexOfTag(enums.packet.publicKey, enums.packet.secretKey); for (let i = 0; i < keyIndex.length; i++) { - if (packetlist[keyIndex[i]].constructor.tag === enums.packet.publicKey) { + const oneKeyList = packetlist.slice(keyIndex[i], keyIndex[i + 1]); + if (packetlist[keyIndex[i]].constructor.tag === enums.packet.publicKey && !oneKeyList.findPacket(enums.packet.secretSubkey)) { continue; } - const oneKeyList = packetlist.slice(keyIndex[i], keyIndex[i + 1]); const newKey = new PrivateKey(oneKeyList); keys.push(newKey); } diff --git a/src/key/key.js b/src/key/key.js index 8e608572..b99d94ad 100644 --- a/src/key/key.js +++ b/src/key/key.js @@ -49,13 +49,14 @@ class Key { /** * Transforms packetlist to structured key data * @param {PacketList} packetlist - The packets that form a key - * @param {Set} disallowedPackets - disallowed packet tags + * @param {Boolean} expectPrivateKey - if a private key is expected, a SecretKeyPacket or SecretKeySubpacket must be present. */ - packetListToStructure(packetlist, disallowedPackets = new Set()) { + packetListToStructure(packetlist, expectPrivateKey) { let user; let primaryKeyID; let subkey; let ignoreUntil; + let isPrivateKey; for (const packet of packetlist) { @@ -78,7 +79,8 @@ class Key { if (!ignoreUntil.has(tag)) continue; ignoreUntil = null; } - if (disallowedPackets.has(tag)) { + isPrivateKey = isPrivateKey || tag === enums.packet.secretKey || tag === enums.packet.secretSubkey; + if (!expectPrivateKey && isPrivateKey) { throw new Error(`Unexpected packet type: ${tag}`); } switch (tag) { @@ -151,6 +153,13 @@ class Key { break; } } + + if (!this.keyPacket) { + throw new Error('Invalid key: missing primary key packet'); + } + if (expectPrivateKey && !isPrivateKey) { + throw new Error('No secret key packet found'); + } } /** diff --git a/src/key/private_key.js b/src/key/private_key.js index 37cd4588..a90f02b7 100644 --- a/src/key/private_key.js +++ b/src/key/private_key.js @@ -18,10 +18,7 @@ class PrivateKey extends PublicKey { */ constructor(packetlist) { super(); - this.packetListToStructure(packetlist, new Set([enums.packet.publicKey, enums.packet.publicSubkey])); - if (!this.keyPacket) { - throw new Error('Invalid key: missing private-key packet'); - } + this.packetListToStructure(packetlist, true); } /** diff --git a/src/key/public_key.js b/src/key/public_key.js index 7996a32c..964c7a2e 100644 --- a/src/key/public_key.js +++ b/src/key/public_key.js @@ -32,10 +32,7 @@ class PublicKey extends Key { this.users = []; this.subkeys = []; if (packetlist) { - this.packetListToStructure(packetlist, new Set([enums.packet.secretKey, enums.packet.secretSubkey])); - if (!this.keyPacket) { - throw new Error('Invalid key: missing public-key packet'); - } + this.packetListToStructure(packetlist, false); } }