Merge a3a934d06b7ff4540b3afb3371a70c51e043e65c into b374d37d4b8270d433b4f5c0e1a79a68807576cd

This commit is contained in:
Daniel Huigens 2025-11-14 12:31:46 +01:00 committed by GitHub
commit 4cf1bb835f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 40 additions and 10 deletions

View File

@ -37,8 +37,8 @@ export class CleartextMessage {
* @param {Signature} signature - The detached signature or an empty signature for unsigned messages
*/
constructor(text, signature) {
// remove trailing whitespace and normalize EOL to canonical form <CR><LF>
this.text = util.removeTrailingSpaces(text).replace(/\r?\n/g, '\r\n');
// remove trailing whitespace from each line and normalize line endings to LF
this.text = util.removeTrailingSpaces(text);
if (signature && !(signature instanceof Signature)) {
throw new Error('Invalid signature input');
}
@ -74,7 +74,8 @@ export class CleartextMessage {
*/
async sign(signingKeys, recipientKeys = [], signature = null, signingKeyIDs = [], date = new Date(), signingUserIDs = [], recipientUserIDs = [], notations = [], config = defaultConfig) {
const literalDataPacket = new LiteralDataPacket();
literalDataPacket.setText(this.text);
// normalize EOL to canonical form <CR><LF>
literalDataPacket.setText(this.text.replace(/\n/g, '\r\n'));
const newSignature = new Signature(await createSignaturePackets(literalDataPacket, signingKeys, recipientKeys, signature, signingKeyIDs, date, signingUserIDs, recipientUserIDs, notations, true, config));
return new CleartextMessage(this.text, newSignature);
}
@ -94,8 +95,8 @@ export class CleartextMessage {
verify(keys, date = new Date(), config = defaultConfig) {
const signatureList = this.signature.packets.filterByTag(enums.packet.signature); // drop UnparsablePackets
const literalDataPacket = new LiteralDataPacket();
// we assume that cleartext signature is generated based on UTF8 cleartext
literalDataPacket.setText(this.text);
// cleartext signatures should be generated over UTF8 cleartext with canonical newlines
literalDataPacket.setText(this.text.replace(/\n/g, '\r\n'));
return createVerificationObjects(signatureList, [literalDataPacket], keys, date, true, config);
}
@ -104,8 +105,7 @@ export class CleartextMessage {
* @returns {String} Cleartext of message.
*/
getText() {
// normalize end of line to \n
return this.text.replace(/\r\n/g, '\n');
return this.text;
}
/**

View File

@ -273,7 +273,7 @@ export function unarmor(input) {
// Reverse dash-escaping for msg
text.push(line.replace(/^- /, ''));
} else {
text = text.join('\r\n');
text = text.join('\n');
textDone = true;
verifyHeaders(lastHeaders);
lastHeaders = [];

View File

@ -252,7 +252,7 @@ export default () => describe('ASCII armor', function() {
it('Do not filter blank lines after header', async function () {
let msg = getArmor(['Hash: SHA1', '']);
msg = await openpgp.readCleartextMessage({ cleartextMessage: msg });
expect(msg.text).to.equal('\r\nsign this');
expect(msg.getText()).to.equal('\nsign this');
});
it('Selectively output CRC checksum', async function () {
@ -353,9 +353,39 @@ NJCB6+LWtabSoVIjNVgKwyKqyTLaESNwC2ogZwkdE8qPGiDFEHo4Gg9zuRof
).to.equal(
pubKey
.replace('\n-', '-')
.replace(/\n\r/g, '\n')
);
});
it('Armored messages use LF newlines', async function () {
const usesLF = armoredData => {
return !armoredData.includes('\r');
};
const { privateKey: v4Key } = await openpgp.generateKey({ userIDs: { email: 'v4@armor.test' }, format: 'object' });
expect(usesLF(v4Key.armor())).to.be.true;
const { privateKey: v6Key } = await openpgp.generateKey({ userIDs: { email: 'v6@armor.test' }, config: { v6Keys: true, aeadProtect: true }, format: 'object' });
expect(usesLF(v6Key.armor())).to.be.true;
const messageWithSEIPDv1 = await openpgp.encrypt({ message: await openpgp.createMessage({ text: 'test' }), encryptionKeys: v4Key });
expect(usesLF(messageWithSEIPDv1)).to.be.true;
const messageWithSEIPDv2 = await openpgp.encrypt({ message: await openpgp.createMessage({ text: 'test' }), encryptionKeys: v6Key });
expect(usesLF(messageWithSEIPDv2)).to.be.true;
const signatureV4V6 = await openpgp.sign({ message: await openpgp.createMessage({ text: 'test' }), signingKeys: [v4Key, v6Key] });
expect(usesLF(signatureV4V6)).to.be.true;
const signatureV6 = await openpgp.sign({ message: await openpgp.createMessage({ text: 'test' }), signingKeys: v6Key });
expect(usesLF(signatureV6)).to.be.true;
const detachedSignatureV4V6 = await openpgp.sign({ message: await openpgp.createMessage({ text: 'test' }), signingKeys: [v4Key, v6Key], detached: true });
expect(usesLF(detachedSignatureV4V6)).to.be.true;
const detachedSignatureV6 = await openpgp.sign({ message: await openpgp.createMessage({ text: 'test' }), signingKeys: v6Key, detached: true });
expect(usesLF(detachedSignatureV6)).to.be.true;
const cleartextSignatureV4V6 = await openpgp.sign({ message: await openpgp.createCleartextMessage({ text: 'test' }), signingKeys: [v4Key, v6Key] });
expect(usesLF(cleartextSignatureV4V6)).to.be.true;
const cleartextSignatureV6 = await openpgp.sign({ message: await openpgp.createCleartextMessage({ text: 'test' }), signingKeys: v6Key });
expect(usesLF(cleartextSignatureV6)).to.be.true;
});
});