For v6 keys, check direct-key signature for key properties

Key flags, expiration time, algorithm preferences, et cetera, are now
read from the direct-key signature instead of the primary User ID
binding signature for v6 keys.

This also requires a direct-key signature to be present for v6 keys.
This commit is contained in:
Daniel Huigens 2022-12-06 14:33:10 +01:00 committed by larabr
parent 5391bcc1bc
commit 6f1eb06119
4 changed files with 53 additions and 28 deletions

View File

@ -116,9 +116,9 @@ export async function getPreferredHashAlgo(key, keyPacket, date = new Date(), us
let hashAlgo = config.preferredHashAlgorithm;
let prefAlgo = hashAlgo;
if (key) {
const primaryUser = await key.getPrimaryUser(date, userID, config);
if (primaryUser.selfCertification.preferredHashAlgorithms) {
[prefAlgo] = primaryUser.selfCertification.preferredHashAlgorithms;
const selfCertification = await key.getPrimarySelfSignature(date, userID, config);
if (selfCertification.preferredHashAlgorithms) {
[prefAlgo] = selfCertification.preferredHashAlgorithms;
hashAlgo = crypto.hash.getHashByteLength(hashAlgo) <= crypto.hash.getHashByteLength(prefAlgo) ?
prefAlgo : hashAlgo;
}
@ -164,8 +164,8 @@ export async function getPreferredAlgo(type, keys = [], date = new Date(), userI
// otherwise we use the default algo
// if no keys are available, preferredSenderAlgo is returned
const senderAlgoSupport = await Promise.all(keys.map(async function(key, i) {
const primaryUser = await key.getPrimaryUser(date, userIDs[i], config);
const recipientPrefs = primaryUser.selfCertification[prefPropertyName];
const selfCertification = await key.getPrimarySelfSignature(date, userIDs[i], config);
const recipientPrefs = selfCertification[prefPropertyName];
return !!recipientPrefs && recipientPrefs.indexOf(preferredSenderAlgo) >= 0;
}));
return senderAlgoSupport.every(Boolean) ? preferredSenderAlgo : defaultAlgo;
@ -306,9 +306,9 @@ export async function isAEADSupported(keys, date = new Date(), userIDs = [], con
let supported = true;
// TODO replace when Promise.some or Promise.any are implemented
await Promise.all(keys.map(async function(key, i) {
const primaryUser = await key.getPrimaryUser(date, userIDs[i], config);
if (!primaryUser.selfCertification.features ||
!(primaryUser.selfCertification.features[0] & enums.features.aead)) {
const selfCertification = await key.getPrimarySelfSignature(date, userIDs[i], config);
if (!selfCertification.features ||
!(selfCertification.features[0] & enums.features.aead)) {
supported = false;
}
}));

View File

@ -288,9 +288,9 @@ class Key {
}
try {
const primaryUser = await this.getPrimaryUser(date, userID, config);
const selfCertification = await this.getPrimarySelfSignature(date, userID, config);
if ((!keyID || primaryKey.getKeyID().equals(keyID)) &&
helper.isValidSigningKeyPacket(primaryKey, primaryUser.selfCertification, config)) {
helper.isValidSigningKeyPacket(primaryKey, selfCertification, config)) {
helper.checkKeyRequirements(primaryKey, config);
return this;
}
@ -334,9 +334,9 @@ class Key {
try {
// if no valid subkey for encryption, evaluate primary key
const primaryUser = await this.getPrimaryUser(date, userID, config);
const selfCertification = await this.getPrimarySelfSignature(date, userID, config);
if ((!keyID || primaryKey.getKeyID().equals(keyID)) &&
helper.isValidEncryptionKeyPacket(primaryKey, primaryUser.selfCertification)) {
helper.isValidEncryptionKeyPacket(primaryKey, selfCertification)) {
helper.checkKeyRequirements(primaryKey, config);
return this;
}
@ -380,18 +380,20 @@ class Key {
throw new Error('Primary key is revoked');
}
// check for valid, unrevoked, unexpired self signature
const { selfCertification } = await this.getPrimaryUser(date, userID, config);
const selfCertification = await this.getPrimarySelfSignature(date, userID, config);
// check for expiration time in binding signatures
if (helper.isDataExpired(primaryKey, selfCertification, date)) {
throw new Error('Primary key is expired');
}
// check for expiration time in direct signatures
const directSignature = await helper.getLatestValidSignature(
this.directSignatures, primaryKey, enums.signature.key, { key: primaryKey }, date, config
).catch(() => {}); // invalid signatures are discarded, to avoid breaking the key
if (primaryKey.version !== 6) {
// check for expiration time in direct signatures (for V6 keys, the above already did so)
const directSignature = await helper.getLatestValidSignature(
this.directSignatures, primaryKey, enums.signature.key, { key: primaryKey }, date, config
).catch(() => {}); // invalid signatures are discarded, to avoid breaking the key
if (directSignature && helper.isDataExpired(primaryKey, directSignature, date)) {
throw new Error('Primary key is expired');
if (directSignature && helper.isDataExpired(primaryKey, directSignature, date)) {
throw new Error('Primary key is expired');
}
}
}
@ -406,12 +408,13 @@ class Key {
async getExpirationTime(userID, config = defaultConfig) {
let primaryKeyExpiry;
try {
const { selfCertification } = await this.getPrimaryUser(null, userID, config);
const selfCertification = await this.getPrimarySelfSignature(null, userID, config);
const selfSigKeyExpiry = helper.getKeyExpirationTime(this.keyPacket, selfCertification);
const selfSigExpiry = selfCertification.getExpirationTime();
const directSignature = await helper.getLatestValidSignature(
this.directSignatures, this.keyPacket, enums.signature.key, { key: this.keyPacket }, null, config
).catch(() => {});
const directSignature = this.keyPacket.version !== 6 && // For V6 keys, the above already returns the direct-key signature.
await helper.getLatestValidSignature(
this.directSignatures, this.keyPacket, enums.signature.key, { key: this.keyPacket }, null, config
).catch(() => {});
if (directSignature) {
const directSigKeyExpiry = helper.getKeyExpirationTime(this.keyPacket, directSignature);
// We do not support the edge case where the direct signature expires, since it would invalidate the corresponding key expiration,
@ -428,6 +431,28 @@ class Key {
}
/**
* For V4 keys, returns the self-signature of the primary user.
* For V5 keys, returns the latest valid direct-key self-signature.
* This self-signature is to be used to check the key expiration,
* algorithm preferences, and so on.
* @param {Date} [date] - Use the given date for verification instead of the current time
* @param {Object} [userID] - User ID to get instead of the primary user for V4 keys, if it exists
* @param {Object} [config] - Full configuration, defaults to openpgp.config
* @returns {Promise<SignaturePacket>} The primary self-signature
* @async
*/
async getPrimarySelfSignature(date = new Date(), userID = {}, config = defaultConfig) {
const primaryKey = this.keyPacket;
if (primaryKey.version === 6) {
return helper.getLatestValidSignature(
this.directSignatures, primaryKey, enums.signature.key, { key: primaryKey }, date, config
);
}
const { selfCertification } = await this.getPrimaryUser(date, userID, config);
return selfCertification;
}
/**
* Returns primary user and most significant (latest valid) self signature
* - if multiple primary users exist, returns the one with the latest self signature

View File

@ -93,9 +93,9 @@ class PrivateKey extends PublicKey {
}
// evaluate primary key
const primaryUser = await this.getPrimaryUser(date, userID, config);
const selfCertification = await this.getPrimarySelfSignature(date, userID, config);
if ((!keyID || primaryKey.getKeyID().equals(keyID, true)) &&
helper.isValidDecryptionKeyPacket(primaryUser.selfCertification, config)) {
helper.isValidDecryptionKeyPacket(selfCertification, config)) {
keys.push(this);
}

View File

@ -204,9 +204,9 @@ export class Message {
enums.symmetric.cast5 // Golang OpenPGP fallback
];
try {
const primaryUser = await decryptionKey.getPrimaryUser(date, undefined, config); // TODO: Pass userID from somewhere.
if (primaryUser.selfCertification.preferredSymmetricAlgorithms) {
algos = algos.concat(primaryUser.selfCertification.preferredSymmetricAlgorithms);
const selfCertification = await decryptionKey.getPrimarySelfSignature(date, undefined, config); // TODO: Pass userID from somewhere.
if (selfCertification.preferredSymmetricAlgorithms) {
algos = algos.concat(selfCertification.preferredSymmetricAlgorithms);
}
} catch (e) {}