From 3b81088aafddce726ca3e2ab3781d31c13c71bea Mon Sep 17 00:00:00 2001 From: Daniel Huigens Date: Thu, 12 Apr 2018 16:36:07 +0200 Subject: [PATCH] Decouple signature type from data packet type Instead of creating a text signature for text packets and a binary signature for binary packets, we determine the signature type based on whether a String or Uint8Array was originally passed. This is useful for the new MIME data packet type (implemented in the next commit) which you can pass in either format. This also partly reverts a22c9e4. Instead of canonicalizing the literal data packet, we canonicalize the data when signing. This fixes a hypothetical case where an uncanonicalized text packet has both a text and a binary signature. This also partly reverts c28f7ad. GPG does not strip trailing whitespace when creating text signatures of literal data packets. --- src/message.js | 18 ++++++------------ src/packet/literal.js | 29 +++++++++++++++++++++-------- src/packet/signature.js | 8 +++++++- 3 files changed, 34 insertions(+), 21 deletions(-) diff --git a/src/message.js b/src/message.js index aca0de85..c2feb5f5 100644 --- a/src/message.js +++ b/src/message.js @@ -216,7 +216,7 @@ Message.prototype.decryptSessionKeys = async function(privateKeys, passwords) { */ Message.prototype.getLiteralData = function() { const literal = this.packets.findPacket(enums.packet.literal); - return (literal && literal.data) || null; + return (literal && literal.getBytes()) || null; }; /** @@ -395,8 +395,8 @@ Message.prototype.sign = async function(privateKeys=[], signature=null, date=new let i; let existingSigPacketlist; - const literalFormat = enums.write(enums.literal, literalDataPacket.format); - const signatureType = literalFormat === enums.literal.binary ? + // If data packet was created from Uint8Array, use binary, otherwise use text + const signatureType = literalDataPacket.text === null ? enums.signature.binary : enums.signature.text; if (signature) { @@ -491,8 +491,8 @@ Message.prototype.signDetached = async function(privateKeys=[], signature=null, export async function createSignaturePackets(literalDataPacket, privateKeys, signature=null, date=new Date()) { const packetlist = new packet.List(); - const literalFormat = enums.write(enums.literal, literalDataPacket.format); - const signatureType = literalFormat === enums.literal.binary ? + // If data packet was created from Uint8Array, use binary, otherwise use text + const signatureType = literalDataPacket.text === null ? enums.signature.binary : enums.signature.text; await Promise.all(privateKeys.map(async function(privateKey) { @@ -581,15 +581,9 @@ export async function createVerificationObjects(signatureList, literalDataList, } })); - // If this is a text signature, canonicalize line endings of the data - const literalDataPacket = literalDataList[0]; - if (signature.signatureType === enums.signature.text) { - literalDataPacket.setText(literalDataPacket.getText()); - } - const verifiedSig = { keyid: signature.issuerKeyId, - valid: keyPacket ? await signature.verify(keyPacket, literalDataPacket) : null + valid: keyPacket ? await signature.verify(keyPacket, literalDataList[0]) : null }; const packetlist = new packet.List(); diff --git a/src/packet/literal.js b/src/packet/literal.js index 0a6f8bee..8fdd99a1 100644 --- a/src/packet/literal.js +++ b/src/packet/literal.js @@ -37,7 +37,8 @@ function Literal(date=new Date()) { this.tag = enums.packet.literal; this.format = 'utf8'; // default format for literal data packets this.date = util.normalizeDate(date); - this.data = new Uint8Array(0); // literal data representation + this.text = null; // textual data representation + this.data = null; // literal data representation this.filename = 'msg.txt'; } @@ -45,13 +46,12 @@ function Literal(date=new Date()) { * Set the packet data to a javascript native string, end of line * will be normalized to \r\n and by default text is converted to UTF8 * @param {String} text Any native javascript string + * @param {utf8|binary|text} format (optional) The format of the string of bytes */ -Literal.prototype.setText = function(text) { - // normalize EOL to \r\n - text = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/[ \t]+\n/g, "\n").replace(/\n/g, "\r\n"); - this.format = 'utf8'; - // encode UTF8 - this.data = util.str_to_Uint8Array(util.encode_utf8(text)); +Literal.prototype.setText = function(text, format='utf8') { + this.format = format; + this.text = text; + this.data = null; }; /** @@ -60,10 +60,14 @@ Literal.prototype.setText = function(text) { * @returns {String} literal data as text */ Literal.prototype.getText = function() { + if (this.text !== null) { + return this.text; + } // decode UTF8 const text = util.decode_utf8(util.Uint8Array_to_str(this.data)); // normalize EOL to \n - return text.replace(/\r\n/g, '\n'); + this.text = text.replace(/\r\n/g, '\n'); + return this.text; }; /** @@ -74,6 +78,7 @@ Literal.prototype.getText = function() { Literal.prototype.setBytes = function(bytes, format) { this.format = format; this.data = bytes; + this.text = null; }; @@ -82,6 +87,14 @@ Literal.prototype.setBytes = function(bytes, format) { * @returns {Uint8Array} A sequence of bytes */ Literal.prototype.getBytes = function() { + if (this.data !== null) { + return this.data; + } + + // normalize EOL to \r\n + const text = this.text.replace(/\r\n/g, '\n').replace(/\r/g, '\n').replace(/\n/g, '\r\n'); + // encode UTF8 + this.data = util.str_to_Uint8Array(util.encode_utf8(text)); return this.data; }; diff --git a/src/packet/signature.js b/src/packet/signature.js index e3210e01..936a1520 100644 --- a/src/packet/signature.js +++ b/src/packet/signature.js @@ -551,9 +551,15 @@ Signature.prototype.toSign = function (type, data) { switch (type) { case t.binary: - case t.text: return data.getBytes(); + case t.text: { + let text = data.getText(); + // normalize EOL to \r\n + text = text.replace(/\r\n/g, '\n').replace(/\r/g, '\n').replace(/\n/g, '\r\n'); + // encode UTF8 + return util.str_to_Uint8Array(util.encode_utf8(text)); + } case t.standalone: return new Uint8Array(0);