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.
This commit is contained in:
Daniel Huigens 2018-04-12 16:36:07 +02:00
parent 2f849063f9
commit 3b81088aaf
3 changed files with 34 additions and 21 deletions

View File

@ -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();

View File

@ -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;
};

View File

@ -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);