From 6f2abdc2cffe647f7571f1b34f019ef0f9276c9a Mon Sep 17 00:00:00 2001 From: Daniel Huigens Date: Thu, 12 Apr 2018 15:45:31 +0200 Subject: [PATCH] Implement MIME message type (Literal Data Packet format 'm') --- src/enums.js | 4 +++- src/message.js | 13 ++++++------- src/openpgp.js | 21 ++++++++++++--------- src/packet/literal.js | 4 ++-- test/general/openpgp.js | 31 +++++++++++++++++++++++++++++++ 5 files changed, 54 insertions(+), 19 deletions(-) diff --git a/src/enums.js b/src/enums.js index 12df0646..ae08ff49 100644 --- a/src/enums.js +++ b/src/enums.js @@ -212,7 +212,9 @@ export default { /** Text data 't' */ text: 't'.charCodeAt(), /** Utf8 data 'u' */ - utf8: 'u'.charCodeAt() + utf8: 'u'.charCodeAt(), + /** MIME message body part 'm' */ + mime: 'm'.charCodeAt() }, diff --git a/src/message.js b/src/message.js index c2feb5f5..ec7944ef 100644 --- a/src/message.js +++ b/src/message.js @@ -652,13 +652,14 @@ export function read(input) { * @param {String} text * @param {String} filename (optional) * @param {Date} date (optional) + * @param {utf8|binary|text|mime} type (optional) data packet type * @returns {module:message.Message} new message object * @static */ -export function fromText(text, filename, date=new Date()) { +export function fromText(text, filename, date=new Date(), type='utf8') { const literalDataPacket = new packet.Literal(date); // text will be converted to UTF8 - literalDataPacket.setText(text); + literalDataPacket.setText(text, type); if (filename !== undefined) { literalDataPacket.setFilename(filename); } @@ -672,19 +673,17 @@ export function fromText(text, filename, date=new Date()) { * @param {Uint8Array} bytes * @param {String} filename (optional) * @param {Date} date (optional) + * @param {utf8|binary|text|mime} type (optional) data packet type * @returns {module:message.Message} new message object * @static */ -export function fromBinary(bytes, filename, date=new Date()) { +export function fromBinary(bytes, filename, date=new Date(), type='binary') { if (!util.isUint8Array(bytes)) { throw new Error('Data must be in the form of a Uint8Array'); } const literalDataPacket = new packet.Literal(date); - if (filename) { - literalDataPacket.setFilename(filename); - } - literalDataPacket.setBytes(bytes, enums.read(enums.literal, enums.literal.binary)); + literalDataPacket.setBytes(bytes, type); if (filename !== undefined) { literalDataPacket.setFilename(filename); } diff --git a/src/openpgp.js b/src/openpgp.js index fbbb4148..b9049fab 100644 --- a/src/openpgp.js +++ b/src/openpgp.js @@ -213,6 +213,7 @@ export function encryptKey({ privateKey, passphrase }) { * Encrypts message text/data with public keys, passwords or both at once. At least either public keys or passwords * must be specified. If private keys are specified, those will be used to sign the message. * @param {String|Uint8Array} data text/data to be encrypted as JavaScript binary string or Uint8Array + * @param {utf8|binary|text|mime} dataType (optional) data packet type * @param {Key|Array} publicKeys (optional) array of keys or single key, used to encrypt the message * @param {Key|Array} privateKeys (optional) private keys for signing. If omitted message will not be signed * @param {String|Array} passwords (optional) array of passwords or a single password to encrypt the message @@ -231,15 +232,15 @@ export function encryptKey({ privateKey, passphrase }) { * @async * @static */ -export function encrypt({ data, publicKeys, privateKeys, passwords, sessionKey, filename, compression=config.compression, armor=true, detached=false, signature=null, returnSessionKey=false, wildcard=false, date=new Date()}) { +export function encrypt({ data, dataType, publicKeys, privateKeys, passwords, sessionKey, filename, compression=config.compression, armor=true, detached=false, signature=null, returnSessionKey=false, wildcard=false, date=new Date()}) { checkData(data); publicKeys = toArray(publicKeys); privateKeys = toArray(privateKeys); passwords = toArray(passwords); if (!nativeAEAD() && asyncProxy) { // use web worker if web crypto apis are not supported - return asyncProxy.delegate('encrypt', { data, publicKeys, privateKeys, passwords, sessionKey, filename, compression, armor, detached, signature, returnSessionKey, wildcard, date }); + return asyncProxy.delegate('encrypt', { data, dataType, publicKeys, privateKeys, passwords, sessionKey, filename, compression, armor, detached, signature, returnSessionKey, wildcard, date }); } const result = {}; return Promise.resolve().then(async function() { - let message = createMessage(data, filename, date); + let message = createMessage(data, filename, date, dataType); if (!privateKeys) { privateKeys = []; } @@ -314,6 +315,7 @@ export function decrypt({ message, privateKeys, passwords, sessionKeys, publicKe /** * Signs a cleartext message. * @param {String | Uint8Array} data cleartext input to be signed + * @param {utf8|binary|text|mime} dataType (optional) data packet type * @param {Key|Array} privateKeys array of keys or single key with decrypted secret key data to sign cleartext * @param {Boolean} armor (optional) if the return value should be ascii armored or the message object * @param {Boolean} detached (optional) if the return value should contain a detached signature @@ -324,19 +326,19 @@ export function decrypt({ message, privateKeys, passwords, sessionKeys, publicKe * @async * @static */ -export function sign({ data, privateKeys, armor=true, detached=false, date=new Date() }) { +export function sign({ data, dataType, privateKeys, armor=true, detached=false, date=new Date() }) { checkData(data); privateKeys = toArray(privateKeys); if (asyncProxy) { // use web worker if available return asyncProxy.delegate('sign', { - data, privateKeys, armor, detached, date + data, dataType, privateKeys, armor, detached, date }); } const result = {}; return Promise.resolve().then(async function() { - let message = util.isString(data) ? new CleartextMessage(data) : messageLib.fromBinary(data); + let message = util.isString(data) ? new CleartextMessage(data) : messageLib.fromBinary(data, dataType); if (detached) { const signature = await message.signDetached(privateKeys, undefined, date); @@ -527,14 +529,15 @@ function toArray(param) { * @param {String|Uint8Array} data the payload for the message * @param {String} filename the literal data packet's filename * @param {Date} date the creation date of the package + * @param {utf8|binary|text|mime} type (optional) data packet type * @returns {Message} a message object */ -function createMessage(data, filename, date=new Date()) { +function createMessage(data, filename, date=new Date(), type) { let msg; if (util.isUint8Array(data)) { - msg = messageLib.fromBinary(data, filename, date); + msg = messageLib.fromBinary(data, filename, date, type); } else if (util.isString(data)) { - msg = messageLib.fromText(data, filename, date); + msg = messageLib.fromText(data, filename, date, type); } else { throw new Error('Data must be of type String or Uint8Array'); } diff --git a/src/packet/literal.js b/src/packet/literal.js index 8fdd99a1..6a0cad1b 100644 --- a/src/packet/literal.js +++ b/src/packet/literal.js @@ -46,7 +46,7 @@ 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 + * @param {utf8|binary|text|mime} format (optional) The format of the string of bytes */ Literal.prototype.setText = function(text, format='utf8') { this.format = format; @@ -73,7 +73,7 @@ Literal.prototype.getText = function() { /** * Set the packet data to value represented by the provided string of bytes. * @param {Uint8Array} bytes The string of bytes - * @param {utf8|binary|text} format The format of the string of bytes + * @param {utf8|binary|text|mime} format The format of the string of bytes */ Literal.prototype.setBytes = function(bytes, format) { this.format = format; diff --git a/test/general/openpgp.js b/test/general/openpgp.js index d08bffa9..fff0f5de 100644 --- a/test/general/openpgp.js +++ b/test/general/openpgp.js @@ -1776,6 +1776,37 @@ describe('OpenPGP.js public api tests', function() { }).then(function (packets) { const literals = packets.packets.filterByTag(openpgp.enums.packet.literal); expect(literals.length).to.equal(1); + expect(literals[0].format).to.equal('binary'); + expect(+literals[0].date).to.equal(+future); + expect(packets.getLiteralData()).to.deep.equal(data); + return packets.verify(encryptOpt.publicKeys, future); + }).then(function (signatures) { + expect(+signatures[0].signature.packets[0].created).to.equal(+future); + expect(signatures[0].valid).to.be.true; + expect(encryptOpt.privateKeys[0].getSigningKeyPacket(signatures[0].keyid, future)) + .to.be.not.null; + expect(signatures[0].signature.packets.length).to.equal(1); + }); + }); + + it('should sign, encrypt and decrypt, verify mime data with a date in the future', function () { + const future = new Date(2040, 5, 5, 5, 5, 5, 0); + const data = new Uint8Array([3, 14, 15, 92, 65, 35, 59]); + const encryptOpt = { + data, + dataType: 'mime', + publicKeys: publicKey_2038_2045.keys, + privateKeys: privateKey_2038_2045.keys, + date: future, + armor: false + }; + + return openpgp.encrypt(encryptOpt).then(function (encrypted) { + return encrypted.message.decrypt(encryptOpt.privateKeys); + }).then(function (packets) { + const literals = packets.packets.filterByTag(openpgp.enums.packet.literal); + expect(literals.length).to.equal(1); + expect(literals[0].format).to.equal('mime'); expect(+literals[0].date).to.equal(+future); expect(packets.getLiteralData()).to.deep.equal(data); return packets.verify(encryptOpt.publicKeys, future);