Add support for parsing transferable private keys with a primary public key and public subkeys

This commit is contained in:
larabr 2024-08-16 13:43:46 +02:00
parent ada794cab6
commit 1f574e0df7
4 changed files with 28 additions and 22 deletions

View File

@ -51,15 +51,18 @@ const allowedKeyPackets = /*#__PURE__*/ util.constructAllowedPackets([
* @throws if no key packet was found * @throws if no key packet was found
*/ */
function createKey(packetlist) { function createKey(packetlist) {
for (const packet of packetlist) { if (packetlist[0]?.constructor.tag === enums.packet.secretKey) {
switch (packet.constructor.tag) {
case enums.packet.secretKey:
return new PrivateKey(packetlist); return new PrivateKey(packetlist);
case enums.packet.publicKey: } else if (packetlist[0]?.constructor.tag === enums.packet.publicKey) {
if (packetlist.findPacket(enums.packet.secretSubkey)) {
return new PrivateKey(packetlist);
} else {
return new PublicKey(packetlist); 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 packetlist = await PacketList.fromBinary(input, allowedKeyPackets, config);
const keyIndex = packetlist.indexOfTag(enums.packet.publicKey, enums.packet.secretKey); const keyIndex = packetlist.indexOfTag(enums.packet.publicKey, enums.packet.secretKey);
for (let i = 0; i < keyIndex.length; i++) { 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; continue;
} }
const firstPrivateKeyList = packetlist.slice(keyIndex[i], keyIndex[i + 1]);
return new PrivateKey(firstPrivateKeyList); return new PrivateKey(firstPrivateKeyList);
} }
throw new Error('No secret key packet found'); 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 packetlist = await PacketList.fromBinary(input, allowedKeyPackets, config);
const keyIndex = packetlist.indexOfTag(enums.packet.publicKey, enums.packet.secretKey); const keyIndex = packetlist.indexOfTag(enums.packet.publicKey, enums.packet.secretKey);
for (let i = 0; i < keyIndex.length; i++) { 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; continue;
} }
const oneKeyList = packetlist.slice(keyIndex[i], keyIndex[i + 1]);
const newKey = new PrivateKey(oneKeyList); const newKey = new PrivateKey(oneKeyList);
keys.push(newKey); keys.push(newKey);
} }

View File

@ -49,13 +49,14 @@ class Key {
/** /**
* Transforms packetlist to structured key data * Transforms packetlist to structured key data
* @param {PacketList} packetlist - The packets that form a key * @param {PacketList} packetlist - The packets that form a key
* @param {Set<enums.packet>} 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 user;
let primaryKeyID; let primaryKeyID;
let subkey; let subkey;
let ignoreUntil; let ignoreUntil;
let isPrivateKey;
for (const packet of packetlist) { for (const packet of packetlist) {
@ -78,7 +79,8 @@ class Key {
if (!ignoreUntil.has(tag)) continue; if (!ignoreUntil.has(tag)) continue;
ignoreUntil = null; 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}`); throw new Error(`Unexpected packet type: ${tag}`);
} }
switch (tag) { switch (tag) {
@ -151,6 +153,13 @@ class Key {
break; break;
} }
} }
if (!this.keyPacket) {
throw new Error('Invalid key: missing primary key packet');
}
if (expectPrivateKey && !isPrivateKey) {
throw new Error('No secret key packet found');
}
} }
/** /**

View File

@ -18,10 +18,7 @@ class PrivateKey extends PublicKey {
*/ */
constructor(packetlist) { constructor(packetlist) {
super(); super();
this.packetListToStructure(packetlist, new Set([enums.packet.publicKey, enums.packet.publicSubkey])); this.packetListToStructure(packetlist, true);
if (!this.keyPacket) {
throw new Error('Invalid key: missing private-key packet');
}
} }
/** /**

View File

@ -32,10 +32,7 @@ class PublicKey extends Key {
this.users = []; this.users = [];
this.subkeys = []; this.subkeys = [];
if (packetlist) { if (packetlist) {
this.packetListToStructure(packetlist, new Set([enums.packet.secretKey, enums.packet.secretSubkey])); this.packetListToStructure(packetlist, false);
if (!this.keyPacket) {
throw new Error('Invalid key: missing public-key packet');
}
} }
} }