From 6ca8bc21808488008d8c0ccfd7e8243ad4294a22 Mon Sep 17 00:00:00 2001 From: KAYLukas Date: Thu, 15 Feb 2018 18:32:46 +0100 Subject: [PATCH] Add timeparameter for verification and remove verify_expired_keys --- src/cleartext.js | 12 +++---- src/config/config.js | 2 -- src/key.js | 70 +++++++++++++++++++---------------------- src/message.js | 63 +++++++++++++++++++------------------ src/openpgp.js | 44 +++++++++++++------------- src/packet/literal.js | 4 +-- src/packet/signature.js | 8 ++--- test/general/openpgp.js | 15 +++++---- 8 files changed, 107 insertions(+), 111 deletions(-) diff --git a/src/cleartext.js b/src/cleartext.js index 9e4da8ed..b9cd8d6c 100644 --- a/src/cleartext.js +++ b/src/cleartext.js @@ -69,25 +69,25 @@ CleartextMessage.prototype.getSigningKeyIds = function() { * Sign the cleartext message * @param {Array} privateKeys private keys with decrypted secret key data for signing * @param {Signature} signature (optional) any existing detached signature - * @param {Date} creationDate The creation time of the signature that should be created + * @param {Date} date The creation time of the signature that should be created * @return {module:message~CleartextMessage} new cleartext message with signed content */ -CleartextMessage.prototype.sign = async function(privateKeys, signature = null, creationDate = new Date()) { - return new CleartextMessage(this.text, await this.signDetached(privateKeys, signature, creationDate)); +CleartextMessage.prototype.sign = async function(privateKeys, signature = null, date = new Date()) { + return new CleartextMessage(this.text, await this.signDetached(privateKeys, signature, date)); }; /** * Sign the cleartext message * @param {Array} privateKeys private keys with decrypted secret key data for signing * @param {Signature} signature (optional) any existing detached signature - * @param {Date} creationDate The creation time of the signature that should be created + * @param {Date} date The creation time of the signature that should be created * @return {module:signature~Signature} new detached signature of message content */ -CleartextMessage.prototype.signDetached = async function(privateKeys, signature = null, creationDate = new Date()) { +CleartextMessage.prototype.signDetached = async function(privateKeys, signature = null, date = new Date()) { const literalDataPacket = new packet.Literal(); literalDataPacket.setText(this.text); - return new Signature(await createSignaturePackets(literalDataPacket, privateKeys, signature, creationDate)); + return new Signature(await createSignaturePackets(literalDataPacket, privateKeys, signature, date)); }; /** diff --git a/src/config/config.js b/src/config/config.js index 5a0a675d..97ede95e 100644 --- a/src/config/config.js +++ b/src/config/config.js @@ -46,8 +46,6 @@ export default { ignore_mdc_error: false, /** @property {Boolean} checksum_required Do not throw error when armor is missing a checksum */ checksum_required: false, - /** @property {Boolean} verify_expired_keys Allow signature verification with expired keys */ - verify_expired_keys: true, /** @property {Boolean} rsa_blinding */ rsa_blinding: true, /** Work-around for rare GPG decryption bug when encrypting with multiple passwords diff --git a/src/key.js b/src/key.js index 38b8e3c9..39efd5b0 100644 --- a/src/key.js +++ b/src/key.js @@ -300,21 +300,20 @@ Key.prototype.armor = function() { /** * Returns first key packet or key packet by given keyId that is available for signing or signature verification * @param {module:type/keyid} keyId, optional - * @param {Boolean} allowExpired allows signature verification with expired keys - * @param {Date} currentDate the current date + * @param {Date} date the current date * @return {(module:packet/secret_subkey|module:packet/secret_key|null)} key packet or null if no signing key has been found */ -Key.prototype.getSigningKeyPacket = function(keyId, allowExpired=false, currentDate = new Date()) { - const primaryUser = this.getPrimaryUser(allowExpired); +Key.prototype.getSigningKeyPacket = function (keyId = null, date = new Date()) { + const primaryUser = this.getPrimaryUser(); if (primaryUser && (!keyId || this.primaryKey.getKeyId().equals(keyId)) && - isValidSigningKeyPacket(this.primaryKey, primaryUser.selfCertificate, allowExpired, currentDate)) { + isValidSigningKeyPacket(this.primaryKey, primaryUser.selfCertificate, date)) { return this.primaryKey; } if (this.subKeys) { for (let i = 0; i < this.subKeys.length; i++) { if (!keyId || this.subKeys[i].subKey.getKeyId().equals(keyId)) { for (let j = 0; j < this.subKeys[i].bindingSignatures.length; j++) { - if (isValidSigningKeyPacket(this.subKeys[i].subKey, this.subKeys[i].bindingSignatures[j], allowExpired, currentDate)) { + if (isValidSigningKeyPacket(this.subKeys[i].subKey, this.subKeys[i].bindingSignatures[j], date)) { return this.subKeys[i].subKey; } } @@ -324,7 +323,7 @@ Key.prototype.getSigningKeyPacket = function(keyId, allowExpired=false, currentD return null; }; -function isValidEncryptionKeyPacket(keyPacket, signature, allowExpired=false, currentDate = new Date()) { +function isValidEncryptionKeyPacket(keyPacket, signature, date = new Date()) { return keyPacket.algorithm !== enums.read(enums.publicKey, enums.publicKey.dsa) && keyPacket.algorithm !== enums.read(enums.publicKey, enums.publicKey.rsa_sign) && keyPacket.algorithm !== enums.read(enums.publicKey, enums.publicKey.ecdsa) && @@ -332,44 +331,44 @@ function isValidEncryptionKeyPacket(keyPacket, signature, allowExpired=false, cu (!signature.keyFlags || (signature.keyFlags[0] & enums.keyFlags.encrypt_communication) !== 0 || (signature.keyFlags[0] & enums.keyFlags.encrypt_storage) !== 0) && - (allowExpired || (!signature.isExpired(currentDate) && + (!signature.isExpired(date) && // check expiration time of V3 key packet !(keyPacket.version === 3 && keyPacket.expirationTimeV3 !== 0 && - +currentDate > (keyPacket.created.getTime() + keyPacket.expirationTimeV3*24*3600*1000)) && + +date > (keyPacket.created.getTime() + keyPacket.expirationTimeV3*24*3600*1000)) && // check expiration time of V4 key packet !(keyPacket.version === 4 && signature.keyNeverExpires === false && - +currentDate > (keyPacket.created.getTime() + signature.keyExpirationTime*1000)))); + +date > (keyPacket.created.getTime() + signature.keyExpirationTime*1000))); } -function isValidSigningKeyPacket(keyPacket, signature, allowExpired=false, currentDate = new Date()) { +function isValidSigningKeyPacket(keyPacket, signature, date = new Date()) { return keyPacket.algorithm !== enums.read(enums.publicKey, enums.publicKey.rsa_encrypt) && keyPacket.algorithm !== enums.read(enums.publicKey, enums.publicKey.elgamal) && keyPacket.algorithm !== enums.read(enums.publicKey, enums.publicKey.ecdh) && (!signature.keyFlags || (signature.keyFlags[0] & enums.keyFlags.sign_data) !== 0) && - (allowExpired || (!signature.isExpired(currentDate) && + (!signature.isExpired(date) && // check expiration time of V3 key packet !(keyPacket.version === 3 && keyPacket.expirationTimeV3 !== 0 && - +currentDate > (keyPacket.created.getTime() + keyPacket.expirationTimeV3*24*3600*1000)) && + +date > (keyPacket.created.getTime() + keyPacket.expirationTimeV3*24*3600*1000)) && // check expiration time of V4 key packet !(keyPacket.version === 4 && signature.keyNeverExpires === false && - +currentDate > (keyPacket.created.getTime() + signature.keyExpirationTime*1000)))); + +date > (keyPacket.created.getTime() + signature.keyExpirationTime*1000))); } /** * Returns first key packet or key packet by given keyId that is available for encryption or decryption * @param {module:type/keyid} keyId, optional - * @param {Date} currentDate optional + * @param {Date} date optional * @returns {(module:packet/public_subkey|module:packet/secret_subkey|module:packet/secret_key|module:packet/public_key|null)} key packet or null if no encryption key has been found */ -Key.prototype.getEncryptionKeyPacket = function(keyId, currentDate = new Date()) { +Key.prototype.getEncryptionKeyPacket = function(keyId, date = new Date()) { // V4: by convention subkeys are preferred for encryption service // V3: keys MUST NOT have subkeys if (this.subKeys) { for (let i = 0; i < this.subKeys.length; i++) { if (!keyId || this.subKeys[i].subKey.getKeyId().equals(keyId)) { for (let j = 0; j < this.subKeys[i].bindingSignatures.length; j++) { - if (isValidEncryptionKeyPacket(this.subKeys[i].subKey, this.subKeys[i].bindingSignatures[j], false, currentDate)) { + if (isValidEncryptionKeyPacket(this.subKeys[i].subKey, this.subKeys[i].bindingSignatures[j], date)) { return this.subKeys[i].subKey; } } @@ -379,7 +378,7 @@ Key.prototype.getEncryptionKeyPacket = function(keyId, currentDate = new Date()) // if no valid subkey for encryption, evaluate primary key const primaryUser = this.getPrimaryUser(); if (primaryUser && (!keyId || this.primaryKey.getKeyId().equals(keyId)) && - isValidEncryptionKeyPacket(this.primaryKey, primaryUser.selfCertificate, false, currentDate)) { + isValidEncryptionKeyPacket(this.primaryKey, primaryUser.selfCertificate, date)) { return this.primaryKey; } return null; @@ -450,10 +449,9 @@ Key.prototype.decryptKeyPacket = function(keyIds, passphrase) { /** * Verify primary key. Checks for revocation signatures, expiration time * and valid self signature - * @param {Boolean} allowExpired allows signature verification with expired keys * @return {module:enums.keyStatus} The status of the primary key */ -Key.prototype.verifyPrimaryKey = async function(allowExpired=false) { +Key.prototype.verifyPrimaryKey = async function() { // TODO clarify OpenPGP's behavior given an expired revocation signature // check revocation signature if (this.revocationSignature && !this.revocationSignature.isExpired() && @@ -462,7 +460,7 @@ Key.prototype.verifyPrimaryKey = async function(allowExpired=false) { return enums.keyStatus.revoked; } // check V3 expiration time - if (!allowExpired && this.primaryKey.version === 3 && this.primaryKey.expirationTimeV3 !== 0 && + if (this.primaryKey.version === 3 && this.primaryKey.expirationTimeV3 !== 0 && Date.now() > (this.primaryKey.created.getTime() + this.primaryKey.expirationTimeV3*24*3600*1000)) { return enums.keyStatus.expired; } @@ -473,12 +471,12 @@ Key.prototype.verifyPrimaryKey = async function(allowExpired=false) { } // check for valid self signature await this.verifyPrimaryUser(); - const primaryUser = this.getPrimaryUser(allowExpired); + const primaryUser = this.getPrimaryUser(); if (!primaryUser) { return enums.keyStatus.invalid; } // check V4 expiration time - if (!allowExpired && this.primaryKey.version === 4 && primaryUser.selfCertificate.keyNeverExpires === false && + if (this.primaryKey.version === 4 && primaryUser.selfCertificate.keyNeverExpires === false && Date.now() > (this.primaryKey.created.getTime() + primaryUser.selfCertificate.keyExpirationTime*1000)) { return enums.keyStatus.expired; } @@ -519,10 +517,9 @@ function getExpirationTime(keyPacket, selfCertificate) { * Returns primary user and most significant (latest valid) self signature * - if multiple users are marked as primary users returns the one with the latest self signature * - if no primary user is found returns the user with the latest self signature - * @param {Boolean} allowExpired allows signature verification with expired keys * @return {{user: Array, selfCertificate: Array}|null} The primary user and the self signature */ -Key.prototype.getPrimaryUser = function(allowExpired=false) { +Key.prototype.getPrimaryUser = function() { let primaryUsers = []; for (let i = 0; i < this.users.length; i++) { // here we only check the primary user ID, ignoring the primary user attribute @@ -533,7 +530,7 @@ Key.prototype.getPrimaryUser = function(allowExpired=false) { // only consider already validated certificates if (!this.users[i].selfCertifications[j].verified || this.users[i].selfCertifications[j].revoked || - (this.users[i].selfCertifications[j].isExpired() && !allowExpired)) { + (this.users[i].selfCertifications[j].isExpired())) { continue; } primaryUsers.push({ index: i, user: this.users[i], selfCertificate: this.users[i].selfCertifications[j] }); @@ -842,17 +839,16 @@ User.prototype.sign = async function(primaryKey, privateKeys) { * @param {module:packet/secret_key|module:packet/public_key} primaryKey The primary key packet * @param {module:packet/signature} certificate A certificate of this user * @param {Array} keys array of keys to verify certificate signatures - * @param {Boolean} allowExpired allows signature verification with expired keys * @return {module:enums.keyStatus} status of the certificate */ -User.prototype.verifyCertificate = async function(primaryKey, certificate, keys, allowExpired=false) { +User.prototype.verifyCertificate = async function(primaryKey, certificate, keys) { const that = this; const keyid = certificate.issuerKeyId; const dataToVerify = { userid: this.userId || this.userAttribute, key: primaryKey }; const results = await Promise.all(keys.map(async function(key) { if (!key.getKeyIds().some(id => id.equals(keyid))) { return; } await key.verifyPrimaryUser(); - const keyPacket = key.getSigningKeyPacket(keyid, allowExpired); + const keyPacket = key.getSigningKeyPacket(keyid); if (certificate.revoked || await that.isRevoked(primaryKey, certificate, keyPacket)) { return enums.keyStatus.revoked; } @@ -978,15 +974,14 @@ SubKey.prototype.isValidEncryptionKey = async function(primaryKey) { /** * Returns true if the subkey can be used for signing of data * @param {module:packet/secret_key|module:packet/public_key} primaryKey The primary key packet - * @param {Boolean} allowExpired allows signature verification with expired keys * @return {Boolean} */ -SubKey.prototype.isValidSigningKey = async function(primaryKey, allowExpired=false) { - if (await this.verify(primaryKey, allowExpired) !== enums.keyStatus.valid) { +SubKey.prototype.isValidSigningKey = async function(primaryKey) { + if (await this.verify(primaryKey) !== enums.keyStatus.valid) { return false; } for (let i = 0; i < this.bindingSignatures.length; i++) { - if (isValidSigningKeyPacket(this.subKey, this.bindingSignatures[i], allowExpired)) { + if (isValidSigningKeyPacket(this.subKey, this.bindingSignatures[i])) { return true; } } @@ -997,10 +992,9 @@ SubKey.prototype.isValidSigningKey = async function(primaryKey, allowExpired=fal * Verify subkey. Checks for revocation signatures, expiration time * and valid binding signature * @param {module:packet/secret_key|module:packet/public_key} primaryKey The primary key packet - * @param {Boolean} allowExpired allows signature verification with expired keys * @return {module:enums.keyStatus} The status of the subkey */ -SubKey.prototype.verify = async function(primaryKey, allowExpired=false) { +SubKey.prototype.verify = async function(primaryKey) { const that = this; // TODO clarify OpenPGP's behavior given an expired revocation signature // check subkey revocation signature @@ -1010,7 +1004,7 @@ SubKey.prototype.verify = async function(primaryKey, allowExpired=false) { return enums.keyStatus.revoked; } // check V3 expiration time - if (!allowExpired && this.subKey.version === 3 && this.subKey.expirationTimeV3 !== 0 && + if (this.subKey.version === 3 && this.subKey.expirationTimeV3 !== 0 && Date.now() > (this.subKey.created.getTime() + this.subKey.expirationTimeV3*24*3600*1000)) { return enums.keyStatus.expired; } @@ -1018,7 +1012,7 @@ SubKey.prototype.verify = async function(primaryKey, allowExpired=false) { // TODO replace when Promise.some or Promise.any are implemented const results = [enums.keyStatus.invalid].concat(await Promise.all(this.bindingSignatures.map(async function(bindingSignature) { // check binding signature is not expired - if (!allowExpired && bindingSignature.isExpired()) { + if (bindingSignature.isExpired()) { return enums.keyStatus.expired; // last expired binding signature } // check binding signature can verify @@ -1028,7 +1022,7 @@ SubKey.prototype.verify = async function(primaryKey, allowExpired=false) { } // check V4 expiration time if (that.subKey.version === 4) { - if (!allowExpired && bindingSignature.keyNeverExpires === false && + if (bindingSignature.keyNeverExpires === false && Date.now() > (that.subKey.created.getTime() + bindingSignature.keyExpirationTime*1000)) { return enums.keyStatus.expired; // last V4 expired binding signature } diff --git a/src/message.js b/src/message.js index 2ec07f92..e2f990f8 100644 --- a/src/message.js +++ b/src/message.js @@ -239,10 +239,10 @@ Message.prototype.getText = function() { * @param {Array} passwords (optional) password(s) for message encryption * @param {Object} sessionKey (optional) session key in the form: { data:Uint8Array, algorithm:String } * @param {Boolean} wildcard (optional) use a key ID of 0 instead of the public key IDs - * @param {Date} creationDate (optional) the current date of encryption + * @param {Date} date (optional) the current date of encryption * @return {Message} new message with encrypted content */ -Message.prototype.encrypt = function(keys, passwords, sessionKey, wildcard = false, creationDate = new Date()) { +Message.prototype.encrypt = function(keys, passwords, sessionKey, wildcard = false, date = new Date()) { let symAlgo; let msg; let symEncryptedPacket; @@ -265,7 +265,7 @@ Message.prototype.encrypt = function(keys, passwords, sessionKey, wildcard = fal sessionKey = crypto.generateSessionKey(symAlgo); } - msg = await encryptSessionKey(sessionKey, symAlgo, keys, passwords, wildcard, creationDate); + msg = await encryptSessionKey(sessionKey, symAlgo, keys, passwords, wildcard, date); if (config.aead_protect) { symEncryptedPacket = new packet.SymEncryptedAEADProtected(); @@ -297,17 +297,17 @@ Message.prototype.encrypt = function(keys, passwords, sessionKey, wildcard = fal * @param {Array} publicKeys (optional) public key(s) for message encryption * @param {Array} passwords (optional) for message encryption * @param {Boolean} wildcard (optional) use a key ID of 0 instead of the public key IDs - * @param {Date} creationDate (optional) the date used to encrypt + * @param {Date} date (optional) the date used to encrypt * @return {Message} new message with encrypted content */ -export function encryptSessionKey(sessionKey, symAlgo, publicKeys, passwords, wildcard=false, creationDate = new Date()) { +export function encryptSessionKey(sessionKey, symAlgo, publicKeys, passwords, wildcard=false, date = new Date()) { const packetlist = new packet.List(); return Promise.resolve().then(async () => { if (publicKeys) { const results = await Promise.all(publicKeys.map(async function(key) { await key.verifyPrimaryUser(); - const encryptionKeyPacket = key.getEncryptionKeyPacket(undefined, creationDate); + const encryptionKeyPacket = key.getEncryptionKeyPacket(undefined, date); if (!encryptionKeyPacket) { throw new Error('Could not find valid key packet for encryption in key ' + key.primaryKey.getKeyId().toHex()); } @@ -362,10 +362,10 @@ export function encryptSessionKey(sessionKey, symAlgo, publicKeys, passwords, wi * Sign the message (the literal data packet of the message) * @param {Array} privateKeys private keys with decrypted secret key data for signing * @param {Signature} signature (optional) any existing detached signature to add to the message - * @param {Date} creationDate} (optional) the creation date of the signature used for creating a new signature + * @param {Date} date} (optional) the creation date of the signature used for creating a new signature * @return {module:message~Message} new message with signed content */ -Message.prototype.sign = async function(privateKeys=[], signature=null, creationDate = new Date()) { +Message.prototype.sign = async function(privateKeys=[], signature=null, date = new Date()) { const packetlist = new packet.List(); const literalDataPacket = this.packets.findPacket(enums.packet.literal); @@ -400,7 +400,7 @@ Message.prototype.sign = async function(privateKeys=[], signature=null, creation throw new Error('Need private key for signing'); } await privateKey.verifyPrimaryUser(); - const signingKeyPacket = privateKey.getSigningKeyPacket(undefined, undefined, creationDate); + const signingKeyPacket = privateKey.getSigningKeyPacket(null, date); if (!signingKeyPacket) { throw new Error('Could not find valid key packet for signing in key ' + privateKey.primaryKey.getKeyId().toHex()); @@ -419,7 +419,7 @@ Message.prototype.sign = async function(privateKeys=[], signature=null, creation }); packetlist.push(literalDataPacket); - packetlist.concat(await createSignaturePackets(literalDataPacket, privateKeys, signature, creationDate)); + packetlist.concat(await createSignaturePackets(literalDataPacket, privateKeys, signature, date)); return new Message(packetlist); }; @@ -448,17 +448,17 @@ Message.prototype.compress = function(compression) { * Create a detached signature for the message (the literal data packet of the message) * @param {Array} privateKeys private keys with decrypted secret key data for signing * @param {Signature} signature (optional) any existing detached signature - * @param {Date} creationDate (optional) the creation date to sign the message with + * @param {Date} date (optional) the creation date to sign the message with * @return {module:signature~Signature} new detached signature of message content */ -Message.prototype.signDetached = async function(privateKeys=[], signature=null, creationDate = new Date()) { +Message.prototype.signDetached = async function(privateKeys=[], signature=null, date = new Date()) { const packetlist = new packet.List(); const literalDataPacket = this.packets.findPacket(enums.packet.literal); if (!literalDataPacket) { throw new Error('No literal data packet to sign.'); } - return new Signature(await createSignaturePackets(literalDataPacket, privateKeys, signature, creationDate)); + return new Signature(await createSignaturePackets(literalDataPacket, privateKeys, signature, date)); }; /** @@ -466,10 +466,10 @@ Message.prototype.signDetached = async function(privateKeys=[], signature=null, * @param {module:packet/literal} literalDataPacket the literal data packet to sign * @param {Array} privateKeys private keys with decrypted secret key data for signing * @param {Signature} signature (optional) any existing detached signature to append - * @param {Date} creationDate (optional) the creation date to sign the message with + * @param {Date} date (optional) the creation date to sign the message with * @return {module:packet/packetlist} list of signature packets */ -export async function createSignaturePackets(literalDataPacket, privateKeys, signature=null, creationDate = new Date()) { +export async function createSignaturePackets(literalDataPacket, privateKeys, signature=null, date = new Date()) { const packetlist = new packet.List(); const literalFormat = enums.write(enums.literal, literalDataPacket.format); @@ -481,14 +481,14 @@ export async function createSignaturePackets(literalDataPacket, privateKeys, sig throw new Error('Need private key for signing'); } await privateKey.verifyPrimaryUser(); - const signingKeyPacket = privateKey.getSigningKeyPacket(undefined, undefined, creationDate); + const signingKeyPacket = privateKey.getSigningKeyPacket(null, date); if (!signingKeyPacket) { throw new Error('Could not find valid key packet for signing in key ' + privateKey.primaryKey.getKeyId().toHex()); } if (!signingKeyPacket.isDecrypted) { throw new Error('Private key is not decrypted.'); } - const signaturePacket = new packet.Signature(creationDate); + const signaturePacket = new packet.Signature(date); signaturePacket.signatureType = signatureType; signaturePacket.publicKeyAlgorithm = signingKeyPacket.algorithm; signaturePacket.hashAlgorithm = getPreferredHashAlgo(privateKey); @@ -508,32 +508,34 @@ export async function createSignaturePackets(literalDataPacket, privateKeys, sig /** * Verify message signatures * @param {Array} keys array of keys to verify signatures + * @param {Date} date the current date * @return {Array<({keyid: module:type/keyid, valid: Boolean})>} list of signer's keyid and validity of signature */ -Message.prototype.verify = function(keys) { +Message.prototype.verify = function(keys, date = new Date()) { const msg = this.unwrapCompressed(); const literalDataList = msg.packets.filterByTag(enums.packet.literal); if (literalDataList.length !== 1) { throw new Error('Can only verify message with one literal data packet.'); } const signatureList = msg.packets.filterByTag(enums.packet.signature); - return createVerificationObjects(signatureList, literalDataList, keys); + return createVerificationObjects(signatureList, literalDataList, keys, date); }; /** * Verify detached message signature * @param {Array} keys array of keys to verify signatures - * @param {Signature} + * @param {Signature} signature + * @param {Date} date the current date * @return {Array<({keyid: module:type/keyid, valid: Boolean})>} list of signer's keyid and validity of signature */ -Message.prototype.verifyDetached = function(signature, keys) { +Message.prototype.verifyDetached = function(signature, keys, date = new Date()) { const msg = this.unwrapCompressed(); const literalDataList = msg.packets.filterByTag(enums.packet.literal); if (literalDataList.length !== 1) { throw new Error('Can only verify message with one literal data packet.'); } const signatureList = signature.packets; - return createVerificationObjects(signatureList, literalDataList, keys); + return createVerificationObjects(signatureList, literalDataList, keys, date); }; /** @@ -541,15 +543,16 @@ Message.prototype.verifyDetached = function(signature, keys) { * @param {Array} signatureList array of signature packets * @param {Array} literalDataList array of literal data packets * @param {Array} keys array of keys to verify signatures + * @param {Date} date the current date * @return {Array<({keyid: module:type/keyid, valid: Boolean})>} list of signer's keyid and validity of signature */ -export async function createVerificationObjects(signatureList, literalDataList, keys) { +export async function createVerificationObjects(signatureList, literalDataList, keys, date = new Date()) { return Promise.all(signatureList.map(async function(signature) { let keyPacket = null; await Promise.all(keys.map(async function(key) { await key.verifyPrimaryUser(); // Look for the unique key packet that matches issuerKeyId of signature - const result = key.getSigningKeyPacket(signature.issuerKeyId, config.verify_expired_keys); + const result = key.getSigningKeyPacket(signature.issuerKeyId, date); if (result) { keyPacket = result; } @@ -633,12 +636,12 @@ export function readSignedContent(content, detachedSignature) { * creates new message object from text * @param {String} text * @param {String} filename (optional) - * @param {Date} creationDate (optional) + * @param {Date} date (optional) * @return {module:message~Message} new message object * @static */ -export function fromText(text, filename, creationDate = new Date()) { - const literalDataPacket = new packet.Literal(creationDate); +export function fromText(text, filename, date = new Date()) { + const literalDataPacket = new packet.Literal(date); // text will be converted to UTF8 literalDataPacket.setText(text); if (filename !== undefined) { @@ -653,16 +656,16 @@ export function fromText(text, filename, creationDate = new Date()) { * creates new message object from binary data * @param {Uint8Array} bytes * @param {String} filename (optional) - * @param {Date} creationDate (optional) + * @param {Date} date (optional) * @return {module:message~Message} new message object * @static */ -export function fromBinary(bytes, filename, creationDate = new Date()) { +export function fromBinary(bytes, filename, date = new Date()) { if (!util.isUint8Array(bytes)) { throw new Error('Data must be in the form of a Uint8Array'); } - const literalDataPacket = new packet.Literal(creationDate); + const literalDataPacket = new packet.Literal(date); if (filename) { literalDataPacket.setFilename(filename); } diff --git a/src/openpgp.js b/src/openpgp.js index 9a7ed8be..70784c91 100644 --- a/src/openpgp.js +++ b/src/openpgp.js @@ -202,34 +202,34 @@ export function decryptKey({ privateKey, passphrase }) { * @param {Signature} signature (optional) a detached signature to add to the encrypted message * @param {Boolean} returnSessionKey (optional) if the unencrypted session key should be added to returned object * @param {Boolean} wildcard (optional) use a key ID of 0 instead of the public key IDs - * @param {Date} creationDate (optional) the creation date used to encrypt and sign the message + * @param {Date} date (optional) the date used to encrypt and sign the message * @return {Promise} encrypted (and optionally signed message) in the form: * {data: ASCII armored message if 'armor' is true, * message: full Message object if 'armor' is false, signature: detached signature if 'detached' is true} * @static */ -export function encrypt({ data, publicKeys, privateKeys, passwords, sessionKey, filename, compression = config.compression, armor = true, detached = false, signature = null, returnSessionKey = false, wildcard = false, creationDate = new Date()}) { +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()}) { 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, armor, detached, signature, returnSessionKey, wildcard, creationDate }); + return asyncProxy.delegate('encrypt', { data, publicKeys, privateKeys, passwords, sessionKey, filename, armor, detached, signature, returnSessionKey, wildcard, date }); } const result = {}; return Promise.resolve().then(async function() { - let message = createMessage(data, filename, creationDate); + let message = createMessage(data, filename, date); if (!privateKeys) { privateKeys = []; } if (privateKeys.length || signature) { // sign the message only if private keys or signature is specified if (detached) { - const detachedSignature = await message.signDetached(privateKeys, signature, creationDate); + const detachedSignature = await message.signDetached(privateKeys, signature, date); result.signature = armor ? detachedSignature.armor() : detachedSignature; } else { - message = await message.sign(privateKeys, signature, creationDate); + message = await message.sign(privateKeys, signature, date); } } message = message.compress(compression); - return message.encrypt(publicKeys, passwords, sessionKey, wildcard, creationDate); + return message.encrypt(publicKeys, passwords, sessionKey, wildcard, date); }).then(encrypted => { if (armor) { @@ -254,15 +254,16 @@ export function encrypt({ data, publicKeys, privateKeys, passwords, sessionKey, * @param {Key|Array} publicKeys (optional) array of public keys or single key, to verify signatures * @param {String} format (optional) return data format either as 'utf8' or 'binary' * @param {Signature} signature (optional) detached signature for verification + * @param {Date} date (optional) the current date * @return {Promise} decrypted and verified message in the form: * { data:Uint8Array|String, filename:String, signatures:[{ keyid:String, valid:Boolean }] } * @static */ -export function decrypt({ message, privateKeys, passwords, sessionKeys, publicKeys, format='utf8', signature=null }) { +export function decrypt({ message, privateKeys, passwords, sessionKeys, publicKeys, format='utf8', signature=null, date=new Date() }) { checkMessage(message); publicKeys = toArray(publicKeys); privateKeys = toArray(privateKeys); passwords = toArray(passwords); sessionKeys = toArray(sessionKeys); if (!nativeAEAD() && asyncProxy) { // use web worker if web crypto apis are not supported - return asyncProxy.delegate('decrypt', { message, privateKeys, passwords, sessionKeys, publicKeys, format, signature }); + return asyncProxy.delegate('decrypt', { message, privateKeys, passwords, sessionKeys, publicKeys, format, signature, date }); } return message.decrypt(privateKeys, passwords, sessionKeys).then(async function(message) { @@ -273,7 +274,7 @@ export function decrypt({ message, privateKeys, passwords, sessionKeys, publicKe publicKeys = []; } - result.signatures = signature ? await message.verifyDetached(signature, publicKeys) : await message.verify(publicKeys); + result.signatures = signature ? await message.verifyDetached(signature, publicKeys, date) : await message.verify(publicKeys, date); return result; }).catch(onError.bind(null, 'Error decrypting message')); } @@ -292,21 +293,21 @@ export function decrypt({ message, privateKeys, passwords, sessionKeys, publicKe * @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 - * @param {Date} creationDate (optional) the creation date used to sign the message + * @param {Date} date (optional) the creation date used to sign the message * @return {Promise} signed cleartext in the form: * {data: ASCII armored message if 'armor' is true, * message: full Message object if 'armor' is false, signature: detached signature if 'detached' is true} * @static */ export function sign({ - data, privateKeys, armor=true, detached=false, creationDate = new Date() + data, 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, creationDate + data, privateKeys, armor, detached, date }); } @@ -315,10 +316,10 @@ export function sign({ let message = util.isString(data) ? new CleartextMessage(data) : messageLib.fromBinary(data); if (detached) { - const signature = await message.signDetached(privateKeys, undefined, creationDate); + const signature = await message.signDetached(privateKeys, undefined, date); result.signature = armor ? signature.armor() : signature; } else { - message = await message.sign(privateKeys, undefined, creationDate); + message = await message.sign(privateKeys, undefined, date); if (armor) { result.data = message.armor(); } else { @@ -334,11 +335,12 @@ export function sign({ * @param {Key|Array} publicKeys array of publicKeys or single key, to verify signatures * @param {CleartextMessage} message cleartext message object with signatures * @param {Signature} signature (optional) detached signature for verification + * @param {Date} date * @return {Promise} cleartext with status of verified signatures in the form of: * { data:String, signatures: [{ keyid:String, valid:Boolean }] } * @static */ -export function verify({ message, publicKeys, signature=null }) { +export function verify({ message, publicKeys, signature=null, date = new Date() }) { checkCleartextOrMessage(message); publicKeys = toArray(publicKeys); @@ -349,7 +351,7 @@ export function verify({ message, publicKeys, signature=null }) { return Promise.resolve().then(async function() { const result = {}; result.data = CleartextMessage.prototype.isPrototypeOf(message) ? message.getText() : message.getLiteralData(); - result.signatures = signature ? await message.verifyDetached(signature, publicKeys) : await message.verify(publicKeys); + result.signatures = signature ? await message.verifyDetached(signature, publicKeys, date) : await message.verify(publicKeys); return result; }).catch(onError.bind(null, 'Error verifying cleartext signed message')); } @@ -495,15 +497,15 @@ function toArray(param) { * Creates a message obejct either from a Uint8Array or a string. * @param {String|Uint8Array} data the payload for the message * @param {String} filename the literal data packet's filename - * @param {Date} creationDate the creation date of the package + * @param {Date} date the creation date of the package * @return {Message} a message object */ -function createMessage(data, filename, creationDate = new Date()) { +function createMessage(data, filename, date = new Date()) { let msg; if (util.isUint8Array(data)) { - msg = messageLib.fromBinary(data, filename, creationDate); + msg = messageLib.fromBinary(data, filename, date); } else if (util.isString(data)) { - msg = messageLib.fromText(data, filename, creationDate); + msg = messageLib.fromText(data, filename, date); } 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 ae27a996..810d4004 100644 --- a/src/packet/literal.js +++ b/src/packet/literal.js @@ -31,10 +31,10 @@ import enums from '../enums.js'; /** * @constructor */ -export default function Literal(creationDate = new Date()) { +export default function Literal(date = new Date()) { this.tag = enums.packet.literal; this.format = 'utf8'; // default format for literal data packets - this.date = creationDate; + this.date = date; this.data = new Uint8Array(0); // literal data representation this.filename = 'msg.txt'; } diff --git a/src/packet/signature.js b/src/packet/signature.js index 11c6cae4..7a9c557b 100644 --- a/src/packet/signature.js +++ b/src/packet/signature.js @@ -41,7 +41,7 @@ import type_keyid from '../type/keyid.js'; /** * @constructor */ -export default function Signature(creationDate = new Date()) { +export default function Signature(date = new Date()) { this.tag = enums.packet.signature; this.version = 4; this.signatureType = null; @@ -52,7 +52,7 @@ export default function Signature(creationDate = new Date()) { this.unhashedSubpackets = null; this.signedHashValue = null; - this.created = creationDate; + this.created = date; this.signatureExpirationTime = null; this.signatureNeverExpires = true; this.exportable = null; @@ -663,9 +663,9 @@ Signature.prototype.verify = async function (key, data) { * Verifies signature expiration date * @return {Boolean} true if expired */ -Signature.prototype.isExpired = function (currentDate = new Date()) { +Signature.prototype.isExpired = function (date = new Date()) { if (!this.signatureNeverExpires) { - return +currentDate > (this.created.getTime() + this.signatureExpirationTime*1000); + return +date > (this.created.getTime() + this.signatureExpirationTime*1000); } return false; }; diff --git a/test/general/openpgp.js b/test/general/openpgp.js index bc07f2f9..d668f8db 100644 --- a/test/general/openpgp.js +++ b/test/general/openpgp.js @@ -1583,7 +1583,7 @@ describe('OpenPGP.js public api tests', function() { expect(+verified.signatures[0].signature.packets[0].created).to.equal(+past); expect(verified.data).to.equal(plaintext); expect(verified.signatures[0].valid).to.be.true; - expect(signOpt.privateKeys[0].getSigningKeyPacket(verified.signatures[0].keyid, undefined, past)) + expect(signOpt.privateKeys[0].getSigningKeyPacket(verified.signatures[0].keyid, past)) .to.be.not.a('null'); expect(verified.signatures[0].signature.packets.length).to.equal(1); }); @@ -1611,7 +1611,7 @@ describe('OpenPGP.js public api tests', function() { expect(+verified.signatures[0].signature.packets[0].created).to.equal(+future); expect(verified.data).to.equal(data); expect(verified.signatures[0].valid).to.be.true; - expect(signOpt.privateKeys[0].getSigningKeyPacket(verified.signatures[0].keyid, undefined, future)) + expect(signOpt.privateKeys[0].getSigningKeyPacket(verified.signatures[0].keyid, future)) .to.be.not.a('null'); expect(verified.signatures[0].signature.packets.length).to.equal(1); }); @@ -1686,7 +1686,7 @@ describe('OpenPGP.js public api tests', function() { }).then(function (signatures) { expect(+signatures[0].signature.packets[0].created).to.equal(+past); expect(signatures[0].valid).to.be.true; - expect(encryptOpt.privateKeys[0].getSigningKeyPacket(signatures[0].keyid, undefined, past)) + expect(encryptOpt.privateKeys[0].getSigningKeyPacket(signatures[0].keyid, past)) .to.be.not.a('null'); expect(signatures[0].signature.packets.length).to.equal(1); }); @@ -1715,7 +1715,7 @@ describe('OpenPGP.js public api tests', function() { }).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, undefined, future)) + expect(encryptOpt.privateKeys[0].getSigningKeyPacket(signatures[0].keyid, future)) .to.be.not.a('null'); expect(signatures[0].signature.packets.length).to.equal(1); }); @@ -1741,7 +1741,7 @@ describe('OpenPGP.js public api tests', function() { expect(+verified.signatures[0].signature.packets[0].created).to.equal(+past); expect(verified.data).to.equal(plaintext); expect(verified.signatures[0].valid).to.be.true; - expect(signOpt.privateKeys[0].getSigningKeyPacket(verified.signatures[0].keyid, undefined, past)) + expect(signOpt.privateKeys[0].getSigningKeyPacket(verified.signatures[0].keyid, past)) .to.be.not.a('null'); expect(verified.signatures[0].signature.packets.length).to.equal(1); }); @@ -1816,7 +1816,7 @@ describe('OpenPGP.js public api tests', function() { }).then(function (signatures) { expect(+signatures[0].signature.packets[0].created).to.equal(+past); expect(signatures[0].valid).to.be.true; - expect(encryptOpt.privateKeys[0].getSigningKeyPacket(signatures[0].keyid, undefined, past)) + expect(encryptOpt.privateKeys[0].getSigningKeyPacket(signatures[0].keyid, past)) .to.be.not.a('null'); expect(signatures[0].signature.packets.length).to.equal(1); }); @@ -1845,7 +1845,7 @@ describe('OpenPGP.js public api tests', function() { }).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, undefined, future)) + expect(encryptOpt.privateKeys[0].getSigningKeyPacket(signatures[0].keyid, future)) .to.be.not.a('null'); expect(signatures[0].signature.packets.length).to.equal(1); }); @@ -2072,7 +2072,6 @@ describe('OpenPGP.js public api tests', function() { }); }); - } });