mirror of
https://github.com/openpgpjs/openpgpjs.git
synced 2025-06-08 07:06:42 +00:00

Mocha v10 requires the lib to be esm compliant. ESM mandates the use of file extensions in imports, so to minimize the changes (for now), we rely on the flag `experimental-specifier-resolution=node` and on `ts-node` (needed only for Node 20). Breaking changes: downstream bundlers might be affected by the package.json changes depending on how they load the library. NB: legacy package.json entrypoints are still available.
73 lines
2.8 KiB
JavaScript
73 lines
2.8 KiB
JavaScript
import { use as chaiUse, expect } from 'chai';
|
|
import chaiAsPromised from 'chai-as-promised';
|
|
|
|
chaiUse(chaiAsPromised);
|
|
|
|
const openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : await import('openpgp');
|
|
|
|
const { readKey, PublicKey, readCleartextMessage, createCleartextMessage, enums, PacketList, SignaturePacket } = openpgp;
|
|
|
|
async function generateTestData() {
|
|
const { privateKey: victimPrivKey } = await openpgp.generateKey({
|
|
userIDs: [{ name: 'Victim', email: 'victim@example.com' }],
|
|
type: 'rsa',
|
|
rsaBits: 2048,
|
|
subkeys: [{ sign: true }],
|
|
format: 'object'
|
|
});
|
|
|
|
const { privateKey: attackerPrivKey } = await openpgp.generateKey({
|
|
userIDs: [{ name: 'Attacker', email: 'attacker@example.com' }],
|
|
type: 'rsa',
|
|
rsaBits: 2048,
|
|
subkeys: [],
|
|
format: 'object'
|
|
});
|
|
|
|
const signed = await openpgp.sign({
|
|
message: await createCleartextMessage({ text: 'I am batman' }),
|
|
signingKeys: victimPrivKey
|
|
});
|
|
return {
|
|
victimPubKey: victimPrivKey.toPublic(),
|
|
attackerPrivKey,
|
|
signed
|
|
};
|
|
}
|
|
|
|
export default () => it('Does not trust subkeys without Primary Key Binding Signature', async function() {
|
|
// attacker only has his own private key,
|
|
// the victim's public key and a signed message
|
|
const { victimPubKey, attackerPrivKey, signed } = await generateTestData();
|
|
|
|
const pktPubVictim = victimPubKey.toPacketList();
|
|
const pktPubAttacker = attackerPrivKey.toPublic().toPacketList();
|
|
const dataToSign = {
|
|
key: attackerPrivKey.toPublic().keyPacket,
|
|
bind: pktPubVictim[3] // victim subkey
|
|
};
|
|
const fakeBindingSignature = new SignaturePacket();
|
|
fakeBindingSignature.signatureType = enums.signature.subkeyBinding;
|
|
fakeBindingSignature.publicKeyAlgorithm = attackerPrivKey.keyPacket.algorithm;
|
|
fakeBindingSignature.hashAlgorithm = enums.hash.sha256;
|
|
fakeBindingSignature.keyFlags = [enums.keyFlags.signData];
|
|
await fakeBindingSignature.sign(attackerPrivKey.keyPacket, dataToSign);
|
|
const newList = new PacketList();
|
|
newList.push(
|
|
pktPubAttacker[0], // attacker private key
|
|
pktPubAttacker[1], // attacker user
|
|
pktPubAttacker[2], // attacker self signature
|
|
pktPubVictim[3], // victim subkey
|
|
fakeBindingSignature // faked key binding
|
|
);
|
|
let fakeKey = new PublicKey(newList);
|
|
fakeKey = await readKey({ armoredKey: await fakeKey.toPublic().armor() });
|
|
const verifyAttackerIsBatman = await openpgp.verify({
|
|
message: await readCleartextMessage({ cleartextMessage: signed }),
|
|
verificationKeys: fakeKey
|
|
});
|
|
// expect the signature to have the expected keyID, but be invalid due to fake key binding signature in the subkey
|
|
expect(verifyAttackerIsBatman.signatures[0].keyID.equals(victimPubKey.subkeys[0].getKeyID())).to.be.true;
|
|
await expect(verifyAttackerIsBatman.signatures[0].verified).to.be.rejectedWith(/Could not find valid signing key packet/);
|
|
});
|