Merge pull request #673 from openpgpjs/exp_time

Calculate expiration time of already expired keys
This commit is contained in:
Sanjana Rajan 2018-03-17 09:05:10 -07:00 committed by GitHub
commit 66f9faaa63
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 76 additions and 26 deletions

View File

@ -437,11 +437,17 @@ Key.prototype.getExpirationTime = async function() {
return getExpirationTime(this.primaryKey); return getExpirationTime(this.primaryKey);
} }
if (this.primaryKey.version === 4) { if (this.primaryKey.version === 4) {
const primaryUser = await this.getPrimaryUser(); const validUsers = await this.getValidUsers(null, true);
if (!primaryUser) { let highest = null;
return null; for (let i = 0; i < validUsers.length; i++) {
const selfCert = validUsers[i].selfCertification;
const current = Math.min(+getExpirationTime(this.primaryKey, selfCert), +selfCert.getExpirationTime());
if (current === Infinity) {
return Infinity;
} }
return getExpirationTime(this.primaryKey, primaryUser.selfCertification); highest = current > highest ? current : highest;
}
return util.normalizeDate(highest);
} }
}; };
@ -450,13 +456,35 @@ Key.prototype.getExpirationTime = async function() {
* - if multiple primary users exist, returns the one with the latest self signature * - if multiple primary users exist, returns the one with the latest self signature
* - otherwise, returns the user with the latest self signature * - otherwise, returns the user with the latest self signature
* @param {Date} date use the given date for verification instead of the current time * @param {Date} date use the given date for verification instead of the current time
* @returns {Promise<{user: Array<module:key.User>, * @returns {Promise<{user: module:key.User,
* selfCertification: Array<module:packet.Signature>}>} The primary user and the self signature * selfCertification: module:packet.Signature}>} The primary user and the self signature
* @async * @async
*/ */
Key.prototype.getPrimaryUser = async function(date=new Date()) { Key.prototype.getPrimaryUser = async function(date=new Date()) {
let validUsers = await this.getValidUsers(date);
if (!validUsers.length) {
return null;
}
// sort by primary user flag and signature creation time
validUsers = validUsers.sort(function(a, b) {
const A = a.selfCertification;
const B = b.selfCertification;
return (A.isPrimaryUserID - B.isPrimaryUserID) || (A.created - B.created);
});
return validUsers.pop();
};
/**
* Returns an array containing all valid users for a key
* @param {Date} date use the given date for verification instead of the current time
* @param {bool} include users with expired certifications
* @returns {Promise<Array<{user: module:key.User,
* selfCertification: module:packet.Signature}>>} The valid user array
* @async
*/
Key.prototype.getValidUsers = async function(date=new Date(), allowExpired=false) {
const { primaryKey } = this; const { primaryKey } = this;
let primaryUsers = []; const validUsers = [];
let lastCreated = null; let lastCreated = null;
let lastPrimaryUserID = null; let lastPrimaryUserID = null;
// TODO replace when Promise.forEach is implemented // TODO replace when Promise.forEach is implemented
@ -482,23 +510,16 @@ Key.prototype.getPrimaryUser = async function(date=new Date()) {
if (cert.revoked || await user.isRevoked(primaryKey, cert, null, date)) { if (cert.revoked || await user.isRevoked(primaryKey, cert, null, date)) {
continue; continue;
} }
if (cert.isExpired(date)) { if (!allowExpired && cert.isExpired(date)) {
continue; continue;
} }
lastPrimaryUserID = cert.isPrimaryUserID; lastPrimaryUserID = cert.isPrimaryUserID;
lastCreated = cert.created; lastCreated = cert.created;
primaryUsers.push({ index: i, user: user, selfCertification: cert }); validUsers.push({ index: i, user: user, selfCertification: cert });
} }
} }
// sort by primary user flag and signature creation time return validUsers;
primaryUsers = primaryUsers.sort(function(a, b) {
const A = a.selfCertification;
const B = b.selfCertification;
return (A.isPrimaryUserID - B.isPrimaryUserID) || (A.created - B.created);
});
return primaryUsers.pop();
}; };
/** /**
* Update key with new components from specified key with same key ID: * Update key with new components from specified key with same key ID:
* users, subkeys, certificates are merged into the destination key, * users, subkeys, certificates are merged into the destination key,
@ -961,17 +982,15 @@ SubKey.prototype.verify = async function(primaryKey, date=new Date()) {
* @returns {Date} * @returns {Date}
*/ */
SubKey.prototype.getExpirationTime = function() { SubKey.prototype.getExpirationTime = function() {
let highest; let highest = null;
for (let i = 0; i < this.bindingSignatures.length; i++) { for (let i = 0; i < this.bindingSignatures.length; i++) {
const current = getExpirationTime(this.subKey, this.bindingSignatures[i]); const current = Math.min(+getExpirationTime(this.subKey, this.bindingSignatures[i]), +this.bindingSignatures[i].getExpirationTime());
if (current === null) { if (current === Infinity) {
return null; return Infinity;
} }
if (!highest || current > highest) { highest = current > highest ? current : highest;
highest = current;
} }
} return util.normalizeDate(highest);
return highest;
}; };
/** /**

View File

@ -673,12 +673,20 @@ Signature.prototype.verify = async function (key, data) {
Signature.prototype.isExpired = function (date=new Date()) { Signature.prototype.isExpired = function (date=new Date()) {
const normDate = util.normalizeDate(date); const normDate = util.normalizeDate(date);
if (normDate !== null) { if (normDate !== null) {
const expirationTime = !this.signatureNeverExpires ? this.created.getTime() + this.signatureExpirationTime*1000 : Infinity; const expirationTime = this.getExpirationTime();
return !(this.created <= normDate && normDate < expirationTime); return !(this.created <= normDate && normDate < expirationTime);
} }
return false; return false;
}; };
/**
* Returns the expiration time of the signature or Infinity if signature does not expire
* @returns {Date} expiration time
*/
Signature.prototype.getExpirationTime = function () {
return !this.signatureNeverExpires ? new Date(this.created.getTime() + this.signatureExpirationTime*1000) : Infinity;
};
/** /**
* Fix custom types after cloning * Fix custom types after cloning
*/ */

View File

@ -721,6 +721,21 @@ describe('Key', function() {
'=6XMW', '=6XMW',
'-----END PGP PUBLIC KEY BLOCK-----'].join('\n'); '-----END PGP PUBLIC KEY BLOCK-----'].join('\n');
const expiredKey =
`-----BEGIN PGP PRIVATE KEY BLOCK-----
xcA4BAAAAAEBAgCgONc0J8rfO6cJw5YTP38x1ze2tAYIO7EcmRCNYwMkXngb
0Qdzg34Q5RW0rNiR56VB6KElPUhePRPVklLFiIvHABEBAAEAAf9qabYMzsz/
/LeRVZSsTgTljmJTdzd2ambUbpi+vt8MXJsbaWh71vjoLMWSXajaKSPDjVU5
waFNt9kLqwGGGLqpAQD5ZdMH2XzTq6GU9Ka69iZs6Pbnzwdz59Vc3i8hXlUj
zQEApHargCTsrtvSrm+hK/pN51/BHAy9lxCAw9f2etx+AeMA/RGrijkFZtYt
jeWdv/usXL3mgHvEcJv63N5zcEvDX5X4W1bND3Rlc3QxIDxhQGIuY29tPsJ7
BBABCAAvBQIAAAABBQMAAAU5BgsJBwgDAgkQzcF99nGrkAkEFQgKAgMWAgEC
GQECGwMCHgEAABAlAfwPehmLZs+gOhOTTaSslqQ50bl/REjmv42Nyr1ZBlQS
DECl1Qu4QyeXin29uEXWiekMpNlZVsEuc8icCw6ABhIZ
=/7PI
-----END PGP PRIVATE KEY BLOCK-----`;
it('Parsing armored text with two keys', function(done) { it('Parsing armored text with two keys', function(done) {
const pubKeys = openpgp.key.readArmored(twoKeys); const pubKeys = openpgp.key.readArmored(twoKeys);
expect(pubKeys).to.exist; expect(pubKeys).to.exist;
@ -823,6 +838,14 @@ describe('Key', function() {
expect(expirationTime.toISOString()).to.be.equal('2018-11-26T10:58:29.000Z'); expect(expirationTime.toISOString()).to.be.equal('2018-11-26T10:58:29.000Z');
}); });
it('Method getExpirationTime expired V4 Key', async function() {
const pubKey = openpgp.key.readArmored(expiredKey).keys[0];
expect(pubKey).to.exist;
expect(pubKey).to.be.an.instanceof(openpgp.key.Key);
const expirationTime = await pubKey.getExpirationTime();
expect(expirationTime.toISOString()).to.be.equal('1970-01-01T00:22:18.000Z');
});
it('Method getExpirationTime V4 SubKey', async function() { it('Method getExpirationTime V4 SubKey', async function() {
const pubKey = openpgp.key.readArmored(twoKeys).keys[1]; const pubKey = openpgp.key.readArmored(twoKeys).keys[1];
expect(pubKey).to.exist; expect(pubKey).to.exist;