Implement MIME message type (Literal Data Packet format 'm')

This commit is contained in:
Daniel Huigens 2018-04-12 15:45:31 +02:00
parent 3b81088aaf
commit 6f2abdc2cf
5 changed files with 54 additions and 19 deletions

View File

@ -212,7 +212,9 @@ export default {
/** Text data 't' */ /** Text data 't' */
text: 't'.charCodeAt(), text: 't'.charCodeAt(),
/** Utf8 data 'u' */ /** Utf8 data 'u' */
utf8: 'u'.charCodeAt() utf8: 'u'.charCodeAt(),
/** MIME message body part 'm' */
mime: 'm'.charCodeAt()
}, },

View File

@ -652,13 +652,14 @@ export function read(input) {
* @param {String} text * @param {String} text
* @param {String} filename (optional) * @param {String} filename (optional)
* @param {Date} date (optional) * @param {Date} date (optional)
* @param {utf8|binary|text|mime} type (optional) data packet type
* @returns {module:message.Message} new message object * @returns {module:message.Message} new message object
* @static * @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); const literalDataPacket = new packet.Literal(date);
// text will be converted to UTF8 // text will be converted to UTF8
literalDataPacket.setText(text); literalDataPacket.setText(text, type);
if (filename !== undefined) { if (filename !== undefined) {
literalDataPacket.setFilename(filename); literalDataPacket.setFilename(filename);
} }
@ -672,19 +673,17 @@ export function fromText(text, filename, date=new Date()) {
* @param {Uint8Array} bytes * @param {Uint8Array} bytes
* @param {String} filename (optional) * @param {String} filename (optional)
* @param {Date} date (optional) * @param {Date} date (optional)
* @param {utf8|binary|text|mime} type (optional) data packet type
* @returns {module:message.Message} new message object * @returns {module:message.Message} new message object
* @static * @static
*/ */
export function fromBinary(bytes, filename, date=new Date()) { export function fromBinary(bytes, filename, date=new Date(), type='binary') {
if (!util.isUint8Array(bytes)) { if (!util.isUint8Array(bytes)) {
throw new Error('Data must be in the form of a Uint8Array'); throw new Error('Data must be in the form of a Uint8Array');
} }
const literalDataPacket = new packet.Literal(date); const literalDataPacket = new packet.Literal(date);
if (filename) { literalDataPacket.setBytes(bytes, type);
literalDataPacket.setFilename(filename);
}
literalDataPacket.setBytes(bytes, enums.read(enums.literal, enums.literal.binary));
if (filename !== undefined) { if (filename !== undefined) {
literalDataPacket.setFilename(filename); literalDataPacket.setFilename(filename);
} }

View File

@ -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 * 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. * 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 {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<Key>} publicKeys (optional) array of keys or single key, used to encrypt the message * @param {Key|Array<Key>} publicKeys (optional) array of keys or single key, used to encrypt the message
* @param {Key|Array<Key>} privateKeys (optional) private keys for signing. If omitted message will not be signed * @param {Key|Array<Key>} privateKeys (optional) private keys for signing. If omitted message will not be signed
* @param {String|Array<String>} passwords (optional) array of passwords or a single password to encrypt the message * @param {String|Array<String>} passwords (optional) array of passwords or a single password to encrypt the message
@ -231,15 +232,15 @@ export function encryptKey({ privateKey, passphrase }) {
* @async * @async
* @static * @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); checkData(data); publicKeys = toArray(publicKeys); privateKeys = toArray(privateKeys); passwords = toArray(passwords);
if (!nativeAEAD() && asyncProxy) { // use web worker if web crypto apis are not supported 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 = {}; const result = {};
return Promise.resolve().then(async function() { return Promise.resolve().then(async function() {
let message = createMessage(data, filename, date); let message = createMessage(data, filename, date, dataType);
if (!privateKeys) { if (!privateKeys) {
privateKeys = []; privateKeys = [];
} }
@ -314,6 +315,7 @@ export function decrypt({ message, privateKeys, passwords, sessionKeys, publicKe
/** /**
* Signs a cleartext message. * Signs a cleartext message.
* @param {String | Uint8Array} data cleartext input to be signed * @param {String | Uint8Array} data cleartext input to be signed
* @param {utf8|binary|text|mime} dataType (optional) data packet type
* @param {Key|Array<Key>} privateKeys array of keys or single key with decrypted secret key data to sign cleartext * @param {Key|Array<Key>} 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} 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 * @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 * @async
* @static * @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); checkData(data);
privateKeys = toArray(privateKeys); privateKeys = toArray(privateKeys);
if (asyncProxy) { // use web worker if available if (asyncProxy) { // use web worker if available
return asyncProxy.delegate('sign', { return asyncProxy.delegate('sign', {
data, privateKeys, armor, detached, date data, dataType, privateKeys, armor, detached, date
}); });
} }
const result = {}; const result = {};
return Promise.resolve().then(async function() { 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) { if (detached) {
const signature = await message.signDetached(privateKeys, undefined, date); 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|Uint8Array} data the payload for the message
* @param {String} filename the literal data packet's filename * @param {String} filename the literal data packet's filename
* @param {Date} date the creation date of the package * @param {Date} date the creation date of the package
* @param {utf8|binary|text|mime} type (optional) data packet type
* @returns {Message} a message object * @returns {Message} a message object
*/ */
function createMessage(data, filename, date=new Date()) { function createMessage(data, filename, date=new Date(), type) {
let msg; let msg;
if (util.isUint8Array(data)) { if (util.isUint8Array(data)) {
msg = messageLib.fromBinary(data, filename, date); msg = messageLib.fromBinary(data, filename, date, type);
} else if (util.isString(data)) { } else if (util.isString(data)) {
msg = messageLib.fromText(data, filename, date); msg = messageLib.fromText(data, filename, date, type);
} else { } else {
throw new Error('Data must be of type String or Uint8Array'); throw new Error('Data must be of type String or Uint8Array');
} }

View File

@ -46,7 +46,7 @@ function Literal(date=new Date()) {
* Set the packet data to a javascript native string, end of line * 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 * will be normalized to \r\n and by default text is converted to UTF8
* @param {String} text Any native javascript string * @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') { Literal.prototype.setText = function(text, format='utf8') {
this.format = format; this.format = format;
@ -73,7 +73,7 @@ Literal.prototype.getText = function() {
/** /**
* Set the packet data to value represented by the provided string of bytes. * Set the packet data to value represented by the provided string of bytes.
* @param {Uint8Array} bytes The 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) { Literal.prototype.setBytes = function(bytes, format) {
this.format = format; this.format = format;

View File

@ -1776,6 +1776,37 @@ describe('OpenPGP.js public api tests', function() {
}).then(function (packets) { }).then(function (packets) {
const literals = packets.packets.filterByTag(openpgp.enums.packet.literal); const literals = packets.packets.filterByTag(openpgp.enums.packet.literal);
expect(literals.length).to.equal(1); 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(+literals[0].date).to.equal(+future);
expect(packets.getLiteralData()).to.deep.equal(data); expect(packets.getLiteralData()).to.deep.equal(data);
return packets.verify(encryptOpt.publicKeys, future); return packets.verify(encryptOpt.publicKeys, future);