Remove primaryKey
argument from User
methods, rename User.sign
to User.certify
(#1329)
- Add `User.mainKey` field to store a reference to the corresponding `Key`, allowing to simplify calling some `User` methods. - Rename `User.sign` to `User.certify`, since it's used for third-party certifications and not as a counterpart of `User.verify`, which deals with self-signatures. - Change `Key.update` behaviour to store a copy of newly added users and subkeys. Pointing to the same instance could give issues as the lists of certifications and signatures could be altered by both the source key and the updated one. Breaking changes in `User` methods: - `User.constructor(userPacket)` -> `constructor(userPacket, mainKey)` - `User.sign(primaryKey, signingKeys, date, config)` -> `.certify(signingKeys, date, config)` - `User.verify(primaryKey, date = new Date(), config)` -> `.verify(date = new Date(), config)` - `User.verifyCertificate(primaryKey, certificate, verificationKeys, date = new Date(), config)` -> `.verifyCertificate(certificate, verificationKeys, date = new Date(), config)` - `User.verifyAllCertifications(primaryKey, verificationKeys, date = new Date(), config)` -> `.verifyAllCertifications(verificationKeys, date = new Date(), config)` - `User.isRevoked(primaryKey, certificate, keyPacket, date = new Date(), config)` -> `.isRevoked(certificate, keyPacket, date = new Date(), config)` - `User.update(sourceUser, primaryKey, date, config)` -> `.update(sourceUser, date, config)`
This commit is contained in:
parent
e785df4c8f
commit
1166de205c
|
@ -247,7 +247,7 @@ export async function mergeSignatures(source, dest, attr, date = new Date(), che
|
||||||
* @param {PublicSubkeyPacket|
|
* @param {PublicSubkeyPacket|
|
||||||
* SecretSubkeyPacket|
|
* SecretSubkeyPacket|
|
||||||
* PublicKeyPacket|
|
* PublicKeyPacket|
|
||||||
* SecretKeyPacket} key, optional The key packet to check the signature
|
* SecretKeyPacket} key, optional The key packet to verify the signature, instead of the primary key
|
||||||
* @param {Date} date - Use the given date instead of the current time
|
* @param {Date} date - Use the given date instead of the current time
|
||||||
* @param {Object} config - Full configuration
|
* @param {Object} config - Full configuration
|
||||||
* @returns {Promise<Boolean>} True if the signature revokes the data.
|
* @returns {Promise<Boolean>} True if the signature revokes the data.
|
||||||
|
|
|
@ -70,7 +70,7 @@ class Key {
|
||||||
break;
|
break;
|
||||||
case enums.packet.userID:
|
case enums.packet.userID:
|
||||||
case enums.packet.userAttribute:
|
case enums.packet.userAttribute:
|
||||||
user = new User(packet);
|
user = new User(packet, this);
|
||||||
this.users.push(user);
|
this.users.push(user);
|
||||||
break;
|
break;
|
||||||
case enums.packet.publicSubkey:
|
case enums.packet.publicSubkey:
|
||||||
|
@ -440,7 +440,7 @@ class Key {
|
||||||
throw exception || new Error('Could not find primary user');
|
throw exception || new Error('Could not find primary user');
|
||||||
}
|
}
|
||||||
await Promise.all(users.map(async function (a) {
|
await Promise.all(users.map(async function (a) {
|
||||||
return a.user.revoked || a.user.isRevoked(primaryKey, a.selfCertification, null, date, config);
|
return a.user.revoked || a.user.isRevoked(a.selfCertification, null, date, config);
|
||||||
}));
|
}));
|
||||||
// sort by primary user flag and signature creation time
|
// sort by primary user flag and signature creation time
|
||||||
const primaryUser = users.sort(function(a, b) {
|
const primaryUser = users.sort(function(a, b) {
|
||||||
|
@ -449,7 +449,7 @@ class Key {
|
||||||
return B.revoked - A.revoked || A.isPrimaryUserID - B.isPrimaryUserID || A.created - B.created;
|
return B.revoked - A.revoked || A.isPrimaryUserID - B.isPrimaryUserID || A.created - B.created;
|
||||||
}).pop();
|
}).pop();
|
||||||
const { user, selfCertification: cert } = primaryUser;
|
const { user, selfCertification: cert } = primaryUser;
|
||||||
if (cert.revoked || await user.isRevoked(primaryKey, cert, null, date, config)) {
|
if (cert.revoked || await user.isRevoked(cert, null, date, config)) {
|
||||||
throw new Error('Primary user is revoked');
|
throw new Error('Primary user is revoked');
|
||||||
}
|
}
|
||||||
return primaryUser;
|
return primaryUser;
|
||||||
|
@ -507,10 +507,12 @@ class Key {
|
||||||
));
|
));
|
||||||
if (usersToUpdate.length > 0) {
|
if (usersToUpdate.length > 0) {
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
usersToUpdate.map(userToUpdate => userToUpdate.update(srcUser, updatedKey.keyPacket, date, config))
|
usersToUpdate.map(userToUpdate => userToUpdate.update(srcUser, date, config))
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
updatedKey.users.push(srcUser);
|
const newUser = srcUser.clone();
|
||||||
|
newUser.mainKey = updatedKey;
|
||||||
|
updatedKey.users.push(newUser);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
// update subkeys
|
// update subkeys
|
||||||
|
@ -524,7 +526,9 @@ class Key {
|
||||||
subkeysToUpdate.map(subkeyToUpdate => subkeyToUpdate.update(srcSubkey, date, config))
|
subkeysToUpdate.map(subkeyToUpdate => subkeyToUpdate.update(srcSubkey, date, config))
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
updatedKey.subkeys.push(srcSubkey);
|
const newSubkey = srcSubkey.clone();
|
||||||
|
newSubkey.mainKey = updatedKey;
|
||||||
|
updatedKey.subkeys.push(newSubkey);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -588,7 +592,7 @@ class Key {
|
||||||
*/
|
*/
|
||||||
async signPrimaryUser(privateKeys, date, userID, config = defaultConfig) {
|
async signPrimaryUser(privateKeys, date, userID, config = defaultConfig) {
|
||||||
const { index, user } = await this.getPrimaryUser(date, userID, config);
|
const { index, user } = await this.getPrimaryUser(date, userID, config);
|
||||||
const userSign = await user.sign(this.keyPacket, privateKeys, date, config);
|
const userSign = await user.certify(privateKeys, date, config);
|
||||||
const key = this.clone();
|
const key = this.clone();
|
||||||
key.users[index] = userSign;
|
key.users[index] = userSign;
|
||||||
return key;
|
return key;
|
||||||
|
@ -603,10 +607,9 @@ class Key {
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
async signAllUsers(privateKeys, date = new Date(), config = defaultConfig) {
|
async signAllUsers(privateKeys, date = new Date(), config = defaultConfig) {
|
||||||
const that = this;
|
|
||||||
const key = this.clone();
|
const key = this.clone();
|
||||||
key.users = await Promise.all(this.users.map(function(user) {
|
key.users = await Promise.all(this.users.map(function(user) {
|
||||||
return user.sign(that.keyPacket, privateKeys, date, config);
|
return user.certify(privateKeys, date, config);
|
||||||
}));
|
}));
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
@ -615,21 +618,23 @@ class Key {
|
||||||
* Verifies primary user of key
|
* Verifies primary user of key
|
||||||
* - if no arguments are given, verifies the self certificates;
|
* - if no arguments are given, verifies the self certificates;
|
||||||
* - otherwise, verifies all certificates signed with given keys.
|
* - otherwise, verifies all certificates signed with given keys.
|
||||||
* @param {Array<Key>} keys - array of keys to verify certificate signatures
|
* @param {Array<PublicKey>} [verificationKeys] - array of keys to verify certificate signatures, instead of the primary key
|
||||||
* @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
|
||||||
* @param {Object} [userID] - User ID to get instead of the primary user, if it exists
|
* @param {Object} [userID] - User ID to get instead of the primary user, if it exists
|
||||||
* @param {Object} [config] - Full configuration, defaults to openpgp.config
|
* @param {Object} [config] - Full configuration, defaults to openpgp.config
|
||||||
* @returns {Promise<Array<{
|
* @returns {Promise<Array<{
|
||||||
* keyID: module:type/keyid~KeyID,
|
* keyID: module:type/keyid~KeyID,
|
||||||
* valid: Boolean
|
* valid: Boolean|null
|
||||||
* }>>} List of signer's keyID and validity of signature
|
* }>>} List of signer's keyID and validity of signature.
|
||||||
|
* Signature validity is null if the verification keys do not correspond to the certificate.
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
async verifyPrimaryUser(keys, date = new Date(), userID, config = defaultConfig) {
|
async verifyPrimaryUser(verificationKeys, date = new Date(), userID, config = defaultConfig) {
|
||||||
const primaryKey = this.keyPacket;
|
const primaryKey = this.keyPacket;
|
||||||
const { user } = await this.getPrimaryUser(date, userID, config);
|
const { user } = await this.getPrimaryUser(date, userID, config);
|
||||||
const results = keys ? await user.verifyAllCertifications(primaryKey, keys, date, config) :
|
const results = verificationKeys ?
|
||||||
[{ keyID: primaryKey.getKeyID(), valid: await user.verify(primaryKey, date, config).catch(() => false) }];
|
await user.verifyAllCertifications(verificationKeys, date, config) :
|
||||||
|
[{ keyID: primaryKey.getKeyID(), valid: await user.verify(date, config).catch(() => false) }];
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -637,29 +642,32 @@ class Key {
|
||||||
* Verifies all users of key
|
* Verifies all users of key
|
||||||
* - if no arguments are given, verifies the self certificates;
|
* - if no arguments are given, verifies the self certificates;
|
||||||
* - otherwise, verifies all certificates signed with given keys.
|
* - otherwise, verifies all certificates signed with given keys.
|
||||||
* @param {Array<Key>} keys - array of keys to verify certificate signatures
|
* @param {Array<PublicKey>} [verificationKeys] - array of keys to verify certificate signatures
|
||||||
* @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
|
||||||
* @param {Object} [config] - Full configuration, defaults to openpgp.config
|
* @param {Object} [config] - Full configuration, defaults to openpgp.config
|
||||||
* @returns {Promise<Array<{
|
* @returns {Promise<Array<{
|
||||||
* userID: String,
|
* userID: String,
|
||||||
* keyID: module:type/keyid~KeyID,
|
* keyID: module:type/keyid~KeyID,
|
||||||
* valid: Boolean
|
* valid: Boolean|null
|
||||||
* }>>} List of userID, signer's keyID and validity of signature
|
* }>>} List of userID, signer's keyID and validity of signature.
|
||||||
|
* Signature validity is null if the verification keys do not correspond to the certificate.
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
async verifyAllUsers(keys, date = new Date(), config = defaultConfig) {
|
async verifyAllUsers(verificationKeys, date = new Date(), config = defaultConfig) {
|
||||||
const results = [];
|
|
||||||
const primaryKey = this.keyPacket;
|
const primaryKey = this.keyPacket;
|
||||||
await Promise.all(this.users.map(async function(user) {
|
const results = [];
|
||||||
const signatures = keys ? await user.verifyAllCertifications(primaryKey, keys, date, config) :
|
await Promise.all(this.users.map(async user => {
|
||||||
[{ keyID: primaryKey.getKeyID(), valid: await user.verify(primaryKey, date, config).catch(() => false) }];
|
const signatures = verificationKeys ?
|
||||||
signatures.forEach(signature => {
|
await user.verifyAllCertifications(verificationKeys, date, config) :
|
||||||
results.push({
|
[{ keyID: primaryKey.getKeyID(), valid: await user.verify(date, config).catch(() => false) }];
|
||||||
|
|
||||||
|
results.push(...signatures.map(
|
||||||
|
signature => ({
|
||||||
userID: user.userID.userID,
|
userID: user.userID.userID,
|
||||||
keyID: signature.keyID,
|
keyID: signature.keyID,
|
||||||
valid: signature.valid
|
valid: signature.valid
|
||||||
});
|
}))
|
||||||
});
|
);
|
||||||
}));
|
}));
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,17 @@ class Subkey {
|
||||||
return packetlist;
|
return packetlist;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shallow clone
|
||||||
|
* @return {Subkey}
|
||||||
|
*/
|
||||||
|
clone() {
|
||||||
|
const subkey = new Subkey(this.keyPacket, this.mainKey);
|
||||||
|
subkey.bindingSignatures = [...this.bindingSignatures];
|
||||||
|
subkey.revocationSignatures = [...this.revocationSignatures];
|
||||||
|
return subkey;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if a binding signature of a subkey is revoked
|
* Checks if a binding signature of a subkey is revoked
|
||||||
* @param {SignaturePacket} signature - The binding signature to verify
|
* @param {SignaturePacket} signature - The binding signature to verify
|
||||||
|
|
136
src/key/user.js
136
src/key/user.js
|
@ -10,14 +10,17 @@ import { mergeSignatures, isDataRevoked, createSignaturePacket } from './helper'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class that represents an user ID or attribute packet and the relevant signatures.
|
* Class that represents an user ID or attribute packet and the relevant signatures.
|
||||||
|
* @param {UserIDPacket|UserAttributePacket} userPacket - packet containing the user info
|
||||||
|
* @param {Key} mainKey - reference to main Key object containing the primary key and subkeys that the user is associated with
|
||||||
*/
|
*/
|
||||||
class User {
|
class User {
|
||||||
constructor(userPacket) {
|
constructor(userPacket, mainKey) {
|
||||||
this.userID = userPacket.constructor.tag === enums.packet.userID ? userPacket : null;
|
this.userID = userPacket.constructor.tag === enums.packet.userID ? userPacket : null;
|
||||||
this.userAttribute = userPacket.constructor.tag === enums.packet.userAttribute ? userPacket : null;
|
this.userAttribute = userPacket.constructor.tag === enums.packet.userAttribute ? userPacket : null;
|
||||||
this.selfCertifications = [];
|
this.selfCertifications = [];
|
||||||
this.otherCertifications = [];
|
this.otherCertifications = [];
|
||||||
this.revocationSignatures = [];
|
this.revocationSignatures = [];
|
||||||
|
this.mainKey = mainKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -34,28 +37,39 @@ class User {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signs user
|
* Shallow clone
|
||||||
* @param {SecretKeyPacket|
|
* @returns {User}
|
||||||
* PublicKeyPacket} primaryKey The primary key packet
|
*/
|
||||||
* @param {Array<Key>} privateKeys - Decrypted private keys for signing
|
clone() {
|
||||||
* @param {Date} date - Date to overwrite creation date of the signature
|
const user = new User(this.userID || this.userAttribute, this.mainKey);
|
||||||
|
user.selfCertifications = [...this.selfCertifications];
|
||||||
|
user.otherCertifications = [...this.otherCertifications];
|
||||||
|
user.revocationSignatures = [...this.revocationSignatures];
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate third-party certifications over this user and its primary key
|
||||||
|
* @param {Array<PrivateKey>} signingKeys - Decrypted private keys for signing
|
||||||
|
* @param {Date} [date] - Date to use as creation date of the certificate, instead of the current time
|
||||||
* @param {Object} config - Full configuration
|
* @param {Object} config - Full configuration
|
||||||
* @returns {Promise<Key>} New user with new certificate signatures.
|
* @returns {Promise<User>} New user with new certifications.
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
async sign(primaryKey, privateKeys, date, config) {
|
async certify(signingKeys, date, config) {
|
||||||
|
const primaryKey = this.mainKey.keyPacket;
|
||||||
const dataToSign = {
|
const dataToSign = {
|
||||||
userID: this.userID,
|
userID: this.userID,
|
||||||
userAttribute: this.userAttribute,
|
userAttribute: this.userAttribute,
|
||||||
key: primaryKey
|
key: primaryKey
|
||||||
};
|
};
|
||||||
const user = new User(dataToSign.userID || dataToSign.userAttribute);
|
const user = new User(dataToSign.userID || dataToSign.userAttribute, this.mainKey);
|
||||||
user.otherCertifications = await Promise.all(privateKeys.map(async function(privateKey) {
|
user.otherCertifications = await Promise.all(signingKeys.map(async function(privateKey) {
|
||||||
if (privateKey.isPublic()) {
|
if (privateKey.isPublic()) {
|
||||||
throw new Error('Need private key for signing');
|
throw new Error('Need private key for signing');
|
||||||
}
|
}
|
||||||
if (privateKey.hasSameFingerprintAs(primaryKey)) {
|
if (privateKey.hasSameFingerprintAs(primaryKey)) {
|
||||||
throw new Error('Not implemented for self signing');
|
throw new Error("The user's own key can only be used for self-certifications");
|
||||||
}
|
}
|
||||||
const signingKey = await privateKey.getSigningKey(undefined, date, undefined, config);
|
const signingKey = await privateKey.getSigningKey(undefined, date, undefined, config);
|
||||||
return createSignaturePacket(dataToSign, privateKey, signingKey.keyPacket, {
|
return createSignaturePacket(dataToSign, privateKey, signingKey.keyPacket, {
|
||||||
|
@ -64,59 +78,57 @@ class User {
|
||||||
keyFlags: [enums.keyFlags.certifyKeys | enums.keyFlags.signData]
|
keyFlags: [enums.keyFlags.certifyKeys | enums.keyFlags.signData]
|
||||||
}, date, undefined, undefined, config);
|
}, date, undefined, undefined, config);
|
||||||
}));
|
}));
|
||||||
await user.update(this, primaryKey, date, config);
|
await user.update(this, date, config);
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if a given certificate of the user is revoked
|
* Checks if a given certificate of the user is revoked
|
||||||
* @param {SecretKeyPacket|
|
|
||||||
* PublicKeyPacket} primaryKey The primary key packet
|
|
||||||
* @param {SignaturePacket} certificate - The certificate to verify
|
* @param {SignaturePacket} certificate - The certificate to verify
|
||||||
* @param {PublicSubkeyPacket|
|
* @param {PublicSubkeyPacket|
|
||||||
* SecretSubkeyPacket|
|
* SecretSubkeyPacket|
|
||||||
* PublicKeyPacket|
|
* PublicKeyPacket|
|
||||||
* SecretKeyPacket} key, optional The key to verify the signature
|
* SecretKeyPacket} [keyPacket] The key packet to verify the signature, instead of the primary key
|
||||||
* @param {Date} date - Use the given date instead of the current time
|
* @param {Date} [date] - Use the given date for verification instead of the current time
|
||||||
* @param {Object} config - Full configuration
|
* @param {Object} config - Full configuration
|
||||||
* @returns {Promise<Boolean>} True if the certificate is revoked.
|
* @returns {Promise<Boolean>} True if the certificate is revoked.
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
async isRevoked(primaryKey, certificate, key, date = new Date(), config) {
|
async isRevoked(certificate, keyPacket, date = new Date(), config) {
|
||||||
return isDataRevoked(
|
const primaryKey = this.mainKey.keyPacket;
|
||||||
primaryKey, enums.signature.certRevocation, {
|
return isDataRevoked(primaryKey, enums.signature.certRevocation, {
|
||||||
key: primaryKey,
|
key: primaryKey,
|
||||||
userID: this.userID,
|
userID: this.userID,
|
||||||
userAttribute: this.userAttribute
|
userAttribute: this.userAttribute
|
||||||
}, this.revocationSignatures, certificate, key, date, config
|
}, this.revocationSignatures, certificate, keyPacket, date, config);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verifies the user certificate. Throws if the user certificate is invalid.
|
* Verifies the user certificate.
|
||||||
* @param {SecretKeyPacket|
|
|
||||||
* PublicKeyPacket} primaryKey The primary key packet
|
|
||||||
* @param {SignaturePacket} certificate - A certificate of this user
|
* @param {SignaturePacket} certificate - A certificate of this user
|
||||||
* @param {Array<Key>} keys - Array of keys to verify certificate signatures
|
* @param {Array<PublicKey>} verificationKeys - Array of keys to verify certificate signatures
|
||||||
* @param {Date} date - Use the given date instead of the current time
|
* @param {Date} [date] - Use the given date instead of the current time
|
||||||
* @param {Object} config - Full configuration
|
* @param {Object} config - Full configuration
|
||||||
* @returns {Promise<true|null>} Status of the certificate.
|
* @returns {Promise<true|null>} true if the certificate could be verified, or null if the verification keys do not correspond to the certificate
|
||||||
|
* @throws if the user certificate is invalid.
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
async verifyCertificate(primaryKey, certificate, keys, date = new Date(), config) {
|
async verifyCertificate(certificate, verificationKeys, date = new Date(), config) {
|
||||||
const that = this;
|
const that = this;
|
||||||
const keyID = certificate.issuerKeyID;
|
const primaryKey = this.mainKey.keyPacket;
|
||||||
const dataToVerify = {
|
const dataToVerify = {
|
||||||
userID: this.userID,
|
userID: this.userID,
|
||||||
userAttribute: this.userAttribute,
|
userAttribute: this.userAttribute,
|
||||||
key: primaryKey
|
key: primaryKey
|
||||||
};
|
};
|
||||||
const results = await Promise.all(keys.map(async function(key) {
|
const { issuerKeyID } = certificate;
|
||||||
if (!key.getKeyIDs().some(id => id.equals(keyID))) {
|
const issuerKeys = verificationKeys.filter(key => key.getKeys(issuerKeyID).length > 0);
|
||||||
return null;
|
if (issuerKeys.length === 0) {
|
||||||
}
|
return null;
|
||||||
const signingKey = await key.getSigningKey(keyID, certificate.created, undefined, config);
|
}
|
||||||
if (certificate.revoked || await that.isRevoked(primaryKey, certificate, signingKey.keyPacket, date, config)) {
|
await Promise.all(issuerKeys.map(async key => {
|
||||||
|
const signingKey = await key.getSigningKey(issuerKeyID, certificate.created, undefined, config);
|
||||||
|
if (certificate.revoked || await that.isRevoked(certificate, signingKey.keyPacket, date, config)) {
|
||||||
throw new Error('User certificate is revoked');
|
throw new Error('User certificate is revoked');
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
@ -124,51 +136,46 @@ class User {
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw util.wrapError('User certificate is invalid', e);
|
throw util.wrapError('User certificate is invalid', e);
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}));
|
}));
|
||||||
return results.find(result => result !== null) || null;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verifies all user certificates
|
* Verifies all user certificates
|
||||||
* @param {SecretKeyPacket|
|
* @param {Array<PublicKey>} verificationKeys - Array of keys to verify certificate signatures
|
||||||
* PublicKeyPacket} primaryKey The primary key packet
|
* @param {Date} [date] - Use the given date instead of the current time
|
||||||
* @param {Array<Key>} keys - Array of keys to verify certificate signatures
|
|
||||||
* @param {Date} date - Use the given date instead of the current time
|
|
||||||
* @param {Object} config - Full configuration
|
* @param {Object} config - Full configuration
|
||||||
* @returns {Promise<Array<{
|
* @returns {Promise<Array<{
|
||||||
* keyID: module:type/keyid~KeyID,
|
* keyID: module:type/keyid~KeyID,
|
||||||
* valid: Boolean
|
* valid: Boolean | null
|
||||||
* }>>} List of signer's keyID and validity of signature
|
* }>>} List of signer's keyID and validity of signature.
|
||||||
|
* Signature validity is null if the verification keys do not correspond to the certificate.
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
async verifyAllCertifications(primaryKey, keys, date = new Date(), config) {
|
async verifyAllCertifications(verificationKeys, date = new Date(), config) {
|
||||||
const that = this;
|
const that = this;
|
||||||
const certifications = this.selfCertifications.concat(this.otherCertifications);
|
const certifications = this.selfCertifications.concat(this.otherCertifications);
|
||||||
return Promise.all(certifications.map(async function(certification) {
|
return Promise.all(certifications.map(async certification => ({
|
||||||
return {
|
keyID: certification.issuerKeyID,
|
||||||
keyID: certification.issuerKeyID,
|
valid: await that.verifyCertificate(certification, verificationKeys, date, config).catch(() => false)
|
||||||
valid: await that.verifyCertificate(primaryKey, certification, keys, date, config).catch(() => false)
|
})));
|
||||||
};
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verify User. Checks for existence of self signatures, revocation signatures
|
* Verify User. Checks for existence of self signatures, revocation signatures
|
||||||
* and validity of self signature.
|
* and validity of self signature.
|
||||||
* @param {SecretKeyPacket|
|
|
||||||
* PublicKeyPacket} primaryKey The primary key packet
|
|
||||||
* @param {Date} date - Use the given date instead of the current time
|
* @param {Date} date - Use the given date instead of the current time
|
||||||
* @param {Object} config - Full configuration
|
* @param {Object} config - Full configuration
|
||||||
* @returns {Promise<true>} Status of user.
|
* @returns {Promise<true>} Status of user.
|
||||||
* @throws {Error} if there are no valid self signatures.
|
* @throws {Error} if there are no valid self signatures.
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
async verify(primaryKey, date = new Date(), config) {
|
async verify(date = new Date(), config) {
|
||||||
if (!this.selfCertifications.length) {
|
if (!this.selfCertifications.length) {
|
||||||
throw new Error('No self-certifications');
|
throw new Error('No self-certifications found');
|
||||||
}
|
}
|
||||||
const that = this;
|
const that = this;
|
||||||
|
const primaryKey = this.mainKey.keyPacket;
|
||||||
const dataToVerify = {
|
const dataToVerify = {
|
||||||
userID: this.userID,
|
userID: this.userID,
|
||||||
userAttribute: this.userAttribute,
|
userAttribute: this.userAttribute,
|
||||||
|
@ -179,7 +186,7 @@ class User {
|
||||||
for (let i = this.selfCertifications.length - 1; i >= 0; i--) {
|
for (let i = this.selfCertifications.length - 1; i >= 0; i--) {
|
||||||
try {
|
try {
|
||||||
const selfCertification = this.selfCertifications[i];
|
const selfCertification = this.selfCertifications[i];
|
||||||
if (selfCertification.revoked || await that.isRevoked(primaryKey, selfCertification, undefined, date, config)) {
|
if (selfCertification.revoked || await that.isRevoked(selfCertification, undefined, date, config)) {
|
||||||
throw new Error('Self-certification is revoked');
|
throw new Error('Self-certification is revoked');
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
@ -197,22 +204,21 @@ class User {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update user with new components from specified user
|
* Update user with new components from specified user
|
||||||
* @param {User} user - Source user to merge
|
* @param {User} sourceUser - Source user to merge
|
||||||
* @param {SecretKeyPacket|
|
|
||||||
* SecretSubkeyPacket} primaryKey primary key used for validation
|
|
||||||
* @param {Date} date - Date to verify the validity of signatures
|
* @param {Date} date - Date to verify the validity of signatures
|
||||||
* @param {Object} config - Full configuration
|
* @param {Object} config - Full configuration
|
||||||
* @returns {Promise<undefined>}
|
* @returns {Promise<undefined>}
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
async update(user, primaryKey, date, config) {
|
async update(sourceUser, date, config) {
|
||||||
|
const primaryKey = this.mainKey.keyPacket;
|
||||||
const dataToVerify = {
|
const dataToVerify = {
|
||||||
userID: this.userID,
|
userID: this.userID,
|
||||||
userAttribute: this.userAttribute,
|
userAttribute: this.userAttribute,
|
||||||
key: primaryKey
|
key: primaryKey
|
||||||
};
|
};
|
||||||
// self signatures
|
// self signatures
|
||||||
await mergeSignatures(user, this, 'selfCertifications', date, async function(srcSelfSig) {
|
await mergeSignatures(sourceUser, this, 'selfCertifications', date, async function(srcSelfSig) {
|
||||||
try {
|
try {
|
||||||
await srcSelfSig.verify(primaryKey, enums.signature.certGeneric, dataToVerify, date, false, config);
|
await srcSelfSig.verify(primaryKey, enums.signature.certGeneric, dataToVerify, date, false, config);
|
||||||
return true;
|
return true;
|
||||||
|
@ -221,9 +227,9 @@ class User {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// other signatures
|
// other signatures
|
||||||
await mergeSignatures(user, this, 'otherCertifications', date);
|
await mergeSignatures(sourceUser, this, 'otherCertifications', date);
|
||||||
// revocation signatures
|
// revocation signatures
|
||||||
await mergeSignatures(user, this, 'revocationSignatures', date, function(srcRevSig) {
|
await mergeSignatures(sourceUser, this, 'revocationSignatures', date, function(srcRevSig) {
|
||||||
return isDataRevoked(primaryKey, enums.signature.certRevocation, dataToVerify, [srcRevSig], undefined, undefined, date, config);
|
return isDataRevoked(primaryKey, enums.signature.certRevocation, dataToVerify, [srcRevSig], undefined, undefined, date, config);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -2840,7 +2840,7 @@ module.exports = () => describe('Key', function() {
|
||||||
expect(signatures[1].valid).to.be.false;
|
expect(signatures[1].valid).to.be.false;
|
||||||
|
|
||||||
const { user } = await pubKey.getPrimaryUser();
|
const { user } = await pubKey.getPrimaryUser();
|
||||||
await expect(user.verifyCertificate(pubKey.keyPacket, user.otherCertifications[0], [certifyingKey], undefined, openpgp.config)).to.be.rejectedWith('User certificate is revoked');
|
await expect(user.verifyCertificate(user.otherCertifications[0], [certifyingKey], undefined, openpgp.config)).to.be.rejectedWith('User certificate is revoked');
|
||||||
} finally {
|
} finally {
|
||||||
openpgp.config.rejectPublicKeyAlgorithms = rejectPublicKeyAlgorithms;
|
openpgp.config.rejectPublicKeyAlgorithms = rejectPublicKeyAlgorithms;
|
||||||
}
|
}
|
||||||
|
@ -2854,10 +2854,10 @@ module.exports = () => describe('Key', function() {
|
||||||
it('Verify certificate of key with future creation date', async function() {
|
it('Verify certificate of key with future creation date', async function() {
|
||||||
const pubKey = await openpgp.readKey({ armoredKey: key_created_2030 });
|
const pubKey = await openpgp.readKey({ armoredKey: key_created_2030 });
|
||||||
const user = pubKey.users[0];
|
const user = pubKey.users[0];
|
||||||
await user.verifyCertificate(pubKey.keyPacket, user.selfCertifications[0], [pubKey], pubKey.keyPacket.created, openpgp.config);
|
await user.verifyCertificate(user.selfCertifications[0], [pubKey], pubKey.keyPacket.created, openpgp.config);
|
||||||
const verifyAllResult = await user.verifyAllCertifications(pubKey.keyPacket, [pubKey], pubKey.keyPacket.created);
|
const verifyAllResult = await user.verifyAllCertifications([pubKey], pubKey.keyPacket.created, openpgp.config);
|
||||||
expect(verifyAllResult[0].valid).to.be.true;
|
expect(verifyAllResult[0].valid).to.be.true;
|
||||||
await user.verify(pubKey.keyPacket, pubKey.keyPacket.created);
|
await user.verify(pubKey.keyPacket.created, openpgp.config);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Evaluate key flags to find valid encryption key packet', async function() {
|
it('Evaluate key flags to find valid encryption key packet', async function() {
|
||||||
|
@ -3118,10 +3118,16 @@ module.exports = () => describe('Key', function() {
|
||||||
const dest = await openpgp.readKey({ armoredKey: pub_sig_test });
|
const dest = await openpgp.readKey({ armoredKey: pub_sig_test });
|
||||||
expect(source.users[1]).to.exist;
|
expect(source.users[1]).to.exist;
|
||||||
dest.users.pop();
|
dest.users.pop();
|
||||||
return dest.update(source).then(updated => {
|
const updated = await dest.update(source);
|
||||||
expect(updated.users[1]).to.exist;
|
expect(updated.users[1]).to.exist;
|
||||||
expect(updated.users[1].userID).to.equal(source.users[1].userID);
|
expect(updated.users[1].userID).to.equal(source.users[1].userID);
|
||||||
});
|
expect(updated.users[1].selfCertifications.length).to.equal(source.users[1].selfCertifications.length);
|
||||||
|
// check that the added users stores certifications separately
|
||||||
|
updated.users[1].selfCertifications.pop();
|
||||||
|
expect(updated.users[1].selfCertifications.length).to.not.equal(source.users[1].selfCertifications.length);
|
||||||
|
// merge self-signatures
|
||||||
|
const updatedAgain = await updated.update(source);
|
||||||
|
expect(updatedAgain.users[1].selfCertifications.length).to.equal(source.users[1].selfCertifications.length);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('update() - merge user - other and certification revocation signatures', async function() {
|
it('update() - merge user - other and certification revocation signatures', async function() {
|
||||||
|
@ -3151,6 +3157,13 @@ module.exports = () => describe('Key', function() {
|
||||||
).to.equal(
|
).to.equal(
|
||||||
source.subkeys[1].getKeyID().toHex()
|
source.subkeys[1].getKeyID().toHex()
|
||||||
);
|
);
|
||||||
|
expect(updated.subkeys[1].bindingSignatures.length).to.equal(source.subkeys[1].bindingSignatures.length);
|
||||||
|
// check that the added subkey stores binding signatures separately
|
||||||
|
updated.subkeys[1].bindingSignatures.pop();
|
||||||
|
expect(updated.subkeys[1].bindingSignatures.length).to.not.equal(source.subkeys[1].bindingSignatures.length);
|
||||||
|
// merge binding signature
|
||||||
|
const updatedAgain = await updated.update(source);
|
||||||
|
expect(updatedAgain.subkeys[1].bindingSignatures.length).to.equal(source.subkeys[1].bindingSignatures.length);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('update() - merge subkey - revocation signature', async function() {
|
it('update() - merge subkey - revocation signature', async function() {
|
||||||
|
|
|
@ -1623,7 +1623,7 @@ iTuGu4fEU1UligAXSrZmCdE=
|
||||||
|
|
||||||
const key = await openpgp.readKey({ armoredKey: armoredKeyWithPhoto });
|
const key = await openpgp.readKey({ armoredKey: armoredKeyWithPhoto });
|
||||||
await Promise.all(key.users.map(async user => {
|
await Promise.all(key.users.map(async user => {
|
||||||
await user.verify(key.keyPacket);
|
await user.verify(undefined, openpgp.config);
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -407,9 +407,7 @@ function omnibus() {
|
||||||
await certificate.verify(
|
await certificate.verify(
|
||||||
primaryKey, openpgp.enums.signature.certGeneric, { userID: user.userID, key: primaryKey }
|
primaryKey, openpgp.enums.signature.certGeneric, { userID: user.userID, key: primaryKey }
|
||||||
);
|
);
|
||||||
await user.verifyCertificate(
|
await user.verifyCertificate(certificate, [hi.toPublic()], undefined, openpgp.config);
|
||||||
primaryKey, certificate, [hi.toPublic()], undefined, openpgp.config
|
|
||||||
);
|
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
userIDs: { name: "Bye", email: "bye@good.bye" },
|
userIDs: { name: "Bye", email: "bye@good.bye" },
|
||||||
|
@ -428,9 +426,7 @@ function omnibus() {
|
||||||
await certificate.verify(
|
await certificate.verify(
|
||||||
bye.keyPacket, openpgp.enums.signature.certGeneric, { userID: user.userID, key: bye.keyPacket }
|
bye.keyPacket, openpgp.enums.signature.certGeneric, { userID: user.userID, key: bye.keyPacket }
|
||||||
);
|
);
|
||||||
await user.verifyCertificate(
|
await user.verifyCertificate(user.selfCertifications[0], [bye.toPublic()], undefined, openpgp.config);
|
||||||
bye.keyPacket, user.selfCertifications[0], [bye.toPublic()], undefined, openpgp.config
|
|
||||||
);
|
|
||||||
|
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
// Hi trusts Bye!
|
// Hi trusts Bye!
|
||||||
|
|
Loading…
Reference in New Issue
Block a user