Merge pull request #772 from twiss/getLatestValidSignature
Check validity and keyid of signatures before using them
This commit is contained in:
commit
a35b4d28e0
180
src/key.js
180
src/key.js
|
@ -261,32 +261,28 @@ Key.prototype.armor = function() {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the signature that has the latest creation date, while ignoring signatures created in the future.
|
* Returns the valid and non-expired signature that has the latest creation date, while ignoring signatures created in the future.
|
||||||
* @param {Array<module:packet.Signature>} signatures List of signatures
|
* @param {Array<module:packet.Signature>} signatures List of 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
|
||||||
* @returns {module:packet.Signature} The latest signature
|
* @returns {Promise<module:packet.Signature>} The latest valid signature
|
||||||
|
* @async
|
||||||
*/
|
*/
|
||||||
function getLatestSignature(signatures, date=new Date()) {
|
async function getLatestValidSignature(signatures, primaryKey, dataToVerify, date=new Date()) {
|
||||||
let signature = signatures[0];
|
let signature;
|
||||||
for (let i = 1; i < signatures.length; i++) {
|
for (let i = signatures.length - 1; i >= 0; i--) {
|
||||||
if (signatures[i].created >= signature.created &&
|
if (
|
||||||
(signatures[i].created <= date || date === null)) {
|
(!signature || signatures[i].created >= signature.created) &&
|
||||||
|
// check binding signature is not expired (ie, check for V4 expiration time)
|
||||||
|
!signatures[i].isExpired(date) &&
|
||||||
|
// check binding signature is verified
|
||||||
|
(signatures[i].verified || await signatures[i].verify(primaryKey, dataToVerify))
|
||||||
|
) {
|
||||||
signature = signatures[i];
|
signature = signatures[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return signature;
|
return signature;
|
||||||
}
|
}
|
||||||
|
|
||||||
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) &&
|
|
||||||
signature.verified && !signature.revoked && !signature.isExpired(date) &&
|
|
||||||
!isDataExpired(keyPacket, signature, date);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns last created key or key by given keyId that is available for signing and verification
|
* Returns last created key or key by given keyId that is available for signing and verification
|
||||||
* @param {module:type/keyid} keyId, optional
|
* @param {module:type/keyid} keyId, optional
|
||||||
|
@ -302,8 +298,9 @@ Key.prototype.getSigningKey = async function (keyId=null, date=new Date(), userI
|
||||||
for (let i = 0; i < subKeys.length; i++) {
|
for (let i = 0; i < subKeys.length; i++) {
|
||||||
if (!keyId || subKeys[i].getKeyId().equals(keyId)) {
|
if (!keyId || subKeys[i].getKeyId().equals(keyId)) {
|
||||||
if (await subKeys[i].verify(primaryKey, date) === enums.keyStatus.valid) {
|
if (await subKeys[i].verify(primaryKey, date) === enums.keyStatus.valid) {
|
||||||
const bindingSignature = getLatestSignature(subKeys[i].bindingSignatures, date);
|
const dataToVerify = { key: primaryKey, bind: subKeys[i].keyPacket };
|
||||||
if (isValidSigningKeyPacket(subKeys[i].keyPacket, bindingSignature, date)) {
|
const bindingSignature = await getLatestValidSignature(subKeys[i].bindingSignatures, primaryKey, dataToVerify, date);
|
||||||
|
if (bindingSignature && isValidSigningKeyPacket(subKeys[i].keyPacket, bindingSignature)) {
|
||||||
return subKeys[i];
|
return subKeys[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -311,24 +308,23 @@ Key.prototype.getSigningKey = async function (keyId=null, date=new Date(), userI
|
||||||
}
|
}
|
||||||
const primaryUser = await this.getPrimaryUser(date, userId);
|
const primaryUser = await this.getPrimaryUser(date, userId);
|
||||||
if (primaryUser && (!keyId || primaryKey.getKeyId().equals(keyId)) &&
|
if (primaryUser && (!keyId || primaryKey.getKeyId().equals(keyId)) &&
|
||||||
isValidSigningKeyPacket(primaryKey, primaryUser.selfCertification, date)) {
|
isValidSigningKeyPacket(primaryKey, primaryUser.selfCertification)) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
};
|
|
||||||
|
|
||||||
function isValidEncryptionKeyPacket(keyPacket, signature, date=new Date()) {
|
function isValidSigningKeyPacket(keyPacket, signature) {
|
||||||
return keyPacket.algorithm !== enums.read(enums.publicKey, enums.publicKey.dsa) &&
|
if (!signature.verified || signature.revoked !== false) { // Sanity check
|
||||||
keyPacket.algorithm !== enums.read(enums.publicKey, enums.publicKey.rsa_sign) &&
|
throw new Error('Signature not verified');
|
||||||
keyPacket.algorithm !== enums.read(enums.publicKey, enums.publicKey.ecdsa) &&
|
}
|
||||||
keyPacket.algorithm !== enums.read(enums.publicKey, enums.publicKey.eddsa) &&
|
return keyPacket.algorithm !== enums.read(enums.publicKey, enums.publicKey.rsa_encrypt) &&
|
||||||
(!signature.keyFlags ||
|
keyPacket.algorithm !== enums.read(enums.publicKey, enums.publicKey.elgamal) &&
|
||||||
(signature.keyFlags[0] & enums.keyFlags.encrypt_communication) !== 0 ||
|
keyPacket.algorithm !== enums.read(enums.publicKey, enums.publicKey.ecdh) &&
|
||||||
(signature.keyFlags[0] & enums.keyFlags.encrypt_storage) !== 0) &&
|
(!signature.keyFlags ||
|
||||||
signature.verified && !signature.revoked && !signature.isExpired(date) &&
|
(signature.keyFlags[0] & enums.keyFlags.sign_data) !== 0);
|
||||||
!isDataExpired(keyPacket, signature, date);
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns last created key or key by given keyId that is available for encryption or decryption
|
* Returns last created key or key by given keyId that is available for encryption or decryption
|
||||||
|
@ -346,8 +342,9 @@ Key.prototype.getEncryptionKey = async function(keyId, date=new Date(), userId={
|
||||||
for (let i = 0; i < subKeys.length; i++) {
|
for (let i = 0; i < subKeys.length; i++) {
|
||||||
if (!keyId || subKeys[i].getKeyId().equals(keyId)) {
|
if (!keyId || subKeys[i].getKeyId().equals(keyId)) {
|
||||||
if (await subKeys[i].verify(primaryKey, date) === enums.keyStatus.valid) {
|
if (await subKeys[i].verify(primaryKey, date) === enums.keyStatus.valid) {
|
||||||
const bindingSignature = getLatestSignature(subKeys[i].bindingSignatures, date);
|
const dataToVerify = { key: primaryKey, bind: subKeys[i].keyPacket };
|
||||||
if (isValidEncryptionKeyPacket(subKeys[i].keyPacket, bindingSignature, date)) {
|
const bindingSignature = await getLatestValidSignature(subKeys[i].bindingSignatures, primaryKey, dataToVerify, date);
|
||||||
|
if (bindingSignature && isValidEncryptionKeyPacket(subKeys[i].keyPacket, bindingSignature)) {
|
||||||
return subKeys[i];
|
return subKeys[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -356,11 +353,24 @@ Key.prototype.getEncryptionKey = async function(keyId, date=new Date(), userId={
|
||||||
// if no valid subkey for encryption, evaluate primary key
|
// if no valid subkey for encryption, evaluate primary key
|
||||||
const primaryUser = await this.getPrimaryUser(date, userId);
|
const primaryUser = await this.getPrimaryUser(date, userId);
|
||||||
if (primaryUser && (!keyId || primaryKey.getKeyId().equals(keyId)) &&
|
if (primaryUser && (!keyId || primaryKey.getKeyId().equals(keyId)) &&
|
||||||
isValidEncryptionKeyPacket(primaryKey, primaryUser.selfCertification, date)) {
|
isValidEncryptionKeyPacket(primaryKey, primaryUser.selfCertification)) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
function isValidEncryptionKeyPacket(keyPacket, signature) {
|
||||||
|
if (!signature.verified || signature.revoked !== false) { // Sanity check
|
||||||
|
throw new Error('Signature not verified');
|
||||||
|
}
|
||||||
|
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) &&
|
||||||
|
keyPacket.algorithm !== enums.read(enums.publicKey, enums.publicKey.eddsa) &&
|
||||||
|
(!signature.keyFlags ||
|
||||||
|
(signature.keyFlags[0] & enums.keyFlags.encrypt_communication) !== 0 ||
|
||||||
|
(signature.keyFlags[0] & enums.keyFlags.encrypt_storage) !== 0);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -473,11 +483,12 @@ Key.prototype.verifyPrimaryKey = async function(date=new Date(), userId={}) {
|
||||||
/**
|
/**
|
||||||
* Returns the latest date when the key can be used for encrypting, signing, or both, depending on the `capabilities` paramater.
|
* Returns the latest date when the key can be used for encrypting, signing, or both, depending on the `capabilities` paramater.
|
||||||
* When `capabilities` is null, defaults to returning the expiry date of the primary key.
|
* When `capabilities` is null, defaults to returning the expiry date of the primary key.
|
||||||
|
* Returns null if `capabilities` is passed and the key does not have the specified capabilities or is revoked or invalid.
|
||||||
* Returns Infinity if the key doesn't expire.
|
* Returns Infinity if the key doesn't expire.
|
||||||
* @param {encrypt|sign|encrypt_sign} capabilities, optional
|
* @param {encrypt|sign|encrypt_sign} capabilities, optional
|
||||||
* @param {module:type/keyid} keyId, optional
|
* @param {module:type/keyid} keyId, optional
|
||||||
* @param {Object} userId, optional user ID
|
* @param {Object} userId, optional user ID
|
||||||
* @returns {Promise<Date>}
|
* @returns {Promise<Date | Infinity | null>}
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
Key.prototype.getExpirationTime = async function(capabilities, keyId, userId) {
|
Key.prototype.getExpirationTime = async function(capabilities, keyId, userId) {
|
||||||
|
@ -492,13 +503,13 @@ Key.prototype.getExpirationTime = async function(capabilities, keyId, userId) {
|
||||||
if (capabilities === 'encrypt' || capabilities === 'encrypt_sign') {
|
if (capabilities === 'encrypt' || capabilities === 'encrypt_sign') {
|
||||||
const encryptKey = await this.getEncryptionKey(keyId, null, userId);
|
const encryptKey = await this.getEncryptionKey(keyId, null, userId);
|
||||||
if (!encryptKey) return null;
|
if (!encryptKey) return null;
|
||||||
const encryptExpiry = encryptKey.getExpirationTime();
|
const encryptExpiry = await encryptKey.getExpirationTime(this.keyPacket);
|
||||||
if (encryptExpiry < expiry) expiry = encryptExpiry;
|
if (encryptExpiry < expiry) expiry = encryptExpiry;
|
||||||
}
|
}
|
||||||
if (capabilities === 'sign' || capabilities === 'encrypt_sign') {
|
if (capabilities === 'sign' || capabilities === 'encrypt_sign') {
|
||||||
const signKey = await this.getSigningKey(keyId, null, userId);
|
const signKey = await this.getSigningKey(keyId, null, userId);
|
||||||
if (!signKey) return null;
|
if (!signKey) return null;
|
||||||
const signExpiry = signKey.getExpirationTime();
|
const signExpiry = await signKey.getExpirationTime(this.keyPacket);
|
||||||
if (signExpiry < expiry) expiry = signExpiry;
|
if (signExpiry < expiry) expiry = signExpiry;
|
||||||
}
|
}
|
||||||
return expiry;
|
return expiry;
|
||||||
|
@ -515,16 +526,20 @@ Key.prototype.getExpirationTime = async function(capabilities, keyId, userId) {
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
Key.prototype.getPrimaryUser = async function(date=new Date(), userId={}) {
|
Key.prototype.getPrimaryUser = async function(date=new Date(), userId={}) {
|
||||||
const users = this.users.map(function(user, index) {
|
const primaryKey = this.keyPacket;
|
||||||
const selfCertification = getLatestSignature(user.selfCertifications, date);
|
const users = [];
|
||||||
return { index, user, selfCertification };
|
for (let i = 0; i < this.users.length; i++) {
|
||||||
}).filter(({ user, selfCertification }) => {
|
const user = this.users[i];
|
||||||
return user.userId && selfCertification && (
|
if (!user.userId || !(
|
||||||
(userId.name === undefined || user.userId.name === userId.name) &&
|
(userId.name === undefined || user.userId.name === userId.name) &&
|
||||||
(userId.email === undefined || user.userId.email === userId.email) &&
|
(userId.email === undefined || user.userId.email === userId.email) &&
|
||||||
(userId.comment === undefined || user.userId.comment === userId.comment)
|
(userId.comment === undefined || user.userId.comment === userId.comment)
|
||||||
);
|
)) continue;
|
||||||
});
|
const dataToVerify = { userId: user.userId, key: primaryKey };
|
||||||
|
const selfCertification = await getLatestValidSignature(user.selfCertifications, primaryKey, dataToVerify, date);
|
||||||
|
if (!selfCertification) continue;
|
||||||
|
users.push({ index: i, user, selfCertification });
|
||||||
|
}
|
||||||
if (!users.length) {
|
if (!users.length) {
|
||||||
if (userId.name !== undefined || userId.email !== undefined ||
|
if (userId.name !== undefined || userId.email !== undefined ||
|
||||||
userId.comment !== undefined) {
|
userId.comment !== undefined) {
|
||||||
|
@ -539,18 +554,9 @@ Key.prototype.getPrimaryUser = async function(date=new Date(), userId={}) {
|
||||||
return A.isPrimaryUserID - B.isPrimaryUserID || A.created - B.created;
|
return A.isPrimaryUserID - B.isPrimaryUserID || A.created - B.created;
|
||||||
}).pop();
|
}).pop();
|
||||||
const { user, selfCertification: cert } = primaryUser;
|
const { user, selfCertification: cert } = primaryUser;
|
||||||
const primaryKey = this.keyPacket;
|
|
||||||
const dataToVerify = { userId: user.userId, key: primaryKey };
|
|
||||||
// skip if certificates is invalid, revoked, or expired
|
|
||||||
if (!(cert.verified || await cert.verify(primaryKey, dataToVerify))) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (cert.revoked || await user.isRevoked(primaryKey, cert, null, date)) {
|
if (cert.revoked || await user.isRevoked(primaryKey, cert, null, date)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (cert.isExpired(date)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return primaryUser;
|
return primaryUser;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -678,12 +684,15 @@ Key.prototype.revoke = async function({
|
||||||
/**
|
/**
|
||||||
* Get revocation certificate from a revoked key.
|
* Get revocation certificate from a revoked key.
|
||||||
* (To get a revocation certificate for an unrevoked key, call revoke() first.)
|
* (To get a revocation certificate for an unrevoked key, call revoke() first.)
|
||||||
* @returns {String} armored revocation certificate
|
* @returns {Promise<String>} armored revocation certificate
|
||||||
|
* @async
|
||||||
*/
|
*/
|
||||||
Key.prototype.getRevocationCertificate = function() {
|
Key.prototype.getRevocationCertificate = async function() {
|
||||||
if (this.revocationSignatures.length) {
|
const dataToVerify = { key: this.keyPacket };
|
||||||
|
const revocationSignature = await getLatestValidSignature(this.revocationSignatures, this.keyPacket, dataToVerify);
|
||||||
|
if (revocationSignature) {
|
||||||
const packetlist = new packet.List();
|
const packetlist = new packet.List();
|
||||||
packetlist.push(getLatestSignature(this.revocationSignatures));
|
packetlist.push(revocationSignature);
|
||||||
return armor.encode(enums.armor.public_key, packetlist.write(), null, null, 'This is a revocation certificate');
|
return armor.encode(enums.armor.public_key, packetlist.write(), null, null, 'This is a revocation certificate');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1090,17 +1099,17 @@ SubKey.prototype.verify = async function(primaryKey, date=new Date()) {
|
||||||
const that = this;
|
const that = this;
|
||||||
const dataToVerify = { key: primaryKey, bind: this.keyPacket };
|
const dataToVerify = { key: primaryKey, bind: this.keyPacket };
|
||||||
// check subkey binding signatures
|
// check subkey binding signatures
|
||||||
const bindingSignature = getLatestSignature(this.bindingSignatures, date);
|
const bindingSignature = await getLatestValidSignature(this.bindingSignatures, primaryKey, dataToVerify, date);
|
||||||
// check binding signature is verified
|
// check binding signature is verified
|
||||||
if (!(bindingSignature.verified || await bindingSignature.verify(primaryKey, dataToVerify))) {
|
if (!bindingSignature) {
|
||||||
return enums.keyStatus.invalid;
|
return enums.keyStatus.invalid;
|
||||||
}
|
}
|
||||||
// check binding signature is not revoked
|
// check binding signature is not revoked
|
||||||
if (bindingSignature.revoked || await that.isRevoked(primaryKey, bindingSignature, null, date)) {
|
if (bindingSignature.revoked || await that.isRevoked(primaryKey, bindingSignature, null, date)) {
|
||||||
return enums.keyStatus.revoked;
|
return enums.keyStatus.revoked;
|
||||||
}
|
}
|
||||||
// check binding signature is not expired (ie, check for V4 expiration time)
|
// check for expiration time
|
||||||
if (bindingSignature.isExpired(date)) {
|
if (isDataExpired(this.keyPacket, bindingSignature, date)) {
|
||||||
return enums.keyStatus.expired;
|
return enums.keyStatus.expired;
|
||||||
}
|
}
|
||||||
return enums.keyStatus.valid; // binding signature passed all checks
|
return enums.keyStatus.valid; // binding signature passed all checks
|
||||||
|
@ -1108,11 +1117,17 @@ SubKey.prototype.verify = async function(primaryKey, date=new Date()) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the expiration time of the subkey or Infinity if key does not expire
|
* Returns the expiration time of the subkey or Infinity if key does not expire
|
||||||
|
* Returns null if the subkey is invalid.
|
||||||
|
* @param {module:packet.SecretKey|
|
||||||
|
* module:packet.PublicKey} 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
|
||||||
* @returns {Date}
|
* @returns {Promise<Date | Infinity | null>}
|
||||||
|
* @async
|
||||||
*/
|
*/
|
||||||
SubKey.prototype.getExpirationTime = function(date=new Date()) {
|
SubKey.prototype.getExpirationTime = async function(primaryKey, date=new Date()) {
|
||||||
const bindingSignature = getLatestSignature(this.bindingSignatures, date);
|
const dataToVerify = { key: primaryKey, bind: this.keyPacket };
|
||||||
|
const bindingSignature = await getLatestValidSignature(this.bindingSignatures, primaryKey, dataToVerify, date);
|
||||||
|
if (!bindingSignature) return null;
|
||||||
const keyExpiry = getExpirationTime(this.keyPacket, bindingSignature);
|
const keyExpiry = getExpirationTime(this.keyPacket, bindingSignature);
|
||||||
const sigExpiry = bindingSignature.getExpirationTime();
|
const sigExpiry = bindingSignature.getExpirationTime();
|
||||||
return keyExpiry < sigExpiry ? keyExpiry : sigExpiry;
|
return keyExpiry < sigExpiry ? keyExpiry : sigExpiry;
|
||||||
|
@ -1186,8 +1201,6 @@ SubKey.prototype.revoke = async function(primaryKey, {
|
||||||
return subKey;
|
return subKey;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
*/
|
|
||||||
['getKeyId', 'getFingerprint', 'getAlgorithmInfo', 'getCreationTime', 'isDecrypted'].forEach(name => {
|
['getKeyId', 'getFingerprint', 'getAlgorithmInfo', 'getCreationTime', 'isDecrypted'].forEach(name => {
|
||||||
Key.prototype[name] =
|
Key.prototype[name] =
|
||||||
SubKey.prototype[name] =
|
SubKey.prototype[name] =
|
||||||
|
@ -1207,9 +1220,16 @@ SubKey.prototype.revoke = async function(primaryKey, {
|
||||||
export async function read(data) {
|
export async function read(data) {
|
||||||
const result = {};
|
const result = {};
|
||||||
result.keys = [];
|
result.keys = [];
|
||||||
|
const err = [];
|
||||||
try {
|
try {
|
||||||
const packetlist = new packet.List();
|
const packetlist = new packet.List();
|
||||||
await packetlist.read(data);
|
await packetlist.read(data);
|
||||||
|
if (packetlist.filterByTag(enums.packet.signature).some(
|
||||||
|
signature => signature.revocationKeyClass !== null
|
||||||
|
)) {
|
||||||
|
// Indicate an error, but still parse the key.
|
||||||
|
err.push(new Error('This key is intended to be revoked with an authorized key, which OpenPGP.js does not support.'));
|
||||||
|
}
|
||||||
const keyIndex = packetlist.indexOfTag(enums.packet.publicKey, enums.packet.secretKey);
|
const keyIndex = packetlist.indexOfTag(enums.packet.publicKey, enums.packet.secretKey);
|
||||||
if (keyIndex.length === 0) {
|
if (keyIndex.length === 0) {
|
||||||
throw new Error('No key packet found');
|
throw new Error('No key packet found');
|
||||||
|
@ -1220,13 +1240,14 @@ export async function read(data) {
|
||||||
const newKey = new Key(oneKeyList);
|
const newKey = new Key(oneKeyList);
|
||||||
result.keys.push(newKey);
|
result.keys.push(newKey);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
result.err = result.err || [];
|
err.push(e);
|
||||||
result.err.push(e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
result.err = result.err || [];
|
err.push(e);
|
||||||
result.err.push(e);
|
}
|
||||||
|
if (err.length) {
|
||||||
|
result.err = err;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -1545,8 +1566,19 @@ async function isDataRevoked(primaryKey, dataToVerify, revocations, signature, k
|
||||||
const normDate = util.normalizeDate(date);
|
const normDate = util.normalizeDate(date);
|
||||||
const revocationKeyIds = [];
|
const revocationKeyIds = [];
|
||||||
await Promise.all(revocations.map(async function(revocationSignature) {
|
await Promise.all(revocations.map(async function(revocationSignature) {
|
||||||
if (!(config.revocations_expire && revocationSignature.isExpired(normDate)) &&
|
if (
|
||||||
(revocationSignature.verified || await revocationSignature.verify(key, dataToVerify))) {
|
// Note: a third-party revocation signature could legitimately revoke a
|
||||||
|
// self-signature if the signature has an authorized revocation key.
|
||||||
|
// However, we don't support passing authorized revocation keys, nor
|
||||||
|
// verifying such revocation signatures. Instead, we indicate an error
|
||||||
|
// when parsing a key with an authorized revocation key, and ignore
|
||||||
|
// third-party revocation signatures here. (It could also be revoking a
|
||||||
|
// third-party key certification, which should only affect
|
||||||
|
// `verifyAllCertifications`.)
|
||||||
|
(!signature || revocationSignature.issuerKeyId.equals(signature.issuerKeyId)) &&
|
||||||
|
!(config.revocations_expire && revocationSignature.isExpired(normDate)) &&
|
||||||
|
(revocationSignature.verified || await revocationSignature.verify(key, dataToVerify))
|
||||||
|
) {
|
||||||
// TODO get an identifier of the revoked object instead
|
// TODO get an identifier of the revoked object instead
|
||||||
revocationKeyIds.push(revocationSignature.issuerKeyId);
|
revocationKeyIds.push(revocationSignature.issuerKeyId);
|
||||||
return true;
|
return true;
|
||||||
|
@ -1556,7 +1588,7 @@ async function isDataRevoked(primaryKey, dataToVerify, revocations, signature, k
|
||||||
// TODO further verify that this is the signature that should be revoked
|
// TODO further verify that this is the signature that should be revoked
|
||||||
if (signature) {
|
if (signature) {
|
||||||
signature.revoked = revocationKeyIds.some(keyId => keyId.equals(signature.issuerKeyId)) ? true :
|
signature.revoked = revocationKeyIds.some(keyId => keyId.equals(signature.issuerKeyId)) ? true :
|
||||||
signature.revoked;
|
signature.revoked || false;
|
||||||
return signature.revoked;
|
return signature.revoked;
|
||||||
}
|
}
|
||||||
return revocationKeyIds.length > 0;
|
return revocationKeyIds.length > 0;
|
||||||
|
|
|
@ -124,7 +124,7 @@ export function generateKey({ userIds=[], passphrase="", numBits=2048, keyExpira
|
||||||
}
|
}
|
||||||
|
|
||||||
return generate(options).then(async key => {
|
return generate(options).then(async key => {
|
||||||
const revocationCertificate = key.getRevocationCertificate();
|
const revocationCertificate = await key.getRevocationCertificate();
|
||||||
key.revocationSignatures = [];
|
key.revocationSignatures = [];
|
||||||
|
|
||||||
return convertStreams({
|
return convertStreams({
|
||||||
|
@ -159,8 +159,8 @@ export function reformatKey({privateKey, userIds=[], passphrase="", keyExpiratio
|
||||||
|
|
||||||
options.revoked = options.revocationCertificate;
|
options.revoked = options.revocationCertificate;
|
||||||
|
|
||||||
return reformat(options).then(key => {
|
return reformat(options).then(async key => {
|
||||||
const revocationCertificate = key.getRevocationCertificate();
|
const revocationCertificate = await key.getRevocationCertificate();
|
||||||
key.revocationSignatures = [];
|
key.revocationSignatures = [];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -344,7 +344,7 @@ export function encrypt({ message, publicKeys, privateKeys, passwords, sessionKe
|
||||||
* @param {String|Array<String>} passwords (optional) passwords to decrypt the message
|
* @param {String|Array<String>} passwords (optional) passwords to decrypt the message
|
||||||
* @param {Object|Array<Object>} sessionKeys (optional) session keys in the form: { data:Uint8Array, algorithm:String }
|
* @param {Object|Array<Object>} sessionKeys (optional) session keys in the form: { data:Uint8Array, algorithm:String }
|
||||||
* @param {Key|Array<Key>} publicKeys (optional) array of public keys or single key, to verify signatures
|
* @param {Key|Array<Key>} 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 {'utf8'|'binary'} format (optional) whether to return data as a string(Stream) or Uint8Array(Stream). If 'utf8' (the default), also normalize newlines.
|
||||||
* @param {'web'|'node'|false} streaming (optional) whether to return data as a stream. Defaults to the type of stream `message` was created from, if any.
|
* @param {'web'|'node'|false} streaming (optional) whether to return data as a stream. Defaults to the type of stream `message` was created from, if any.
|
||||||
* @param {Signature} signature (optional) detached signature for verification
|
* @param {Signature} signature (optional) detached signature for verification
|
||||||
* @param {Date} date (optional) use the given date for verification instead of the current time
|
* @param {Date} date (optional) use the given date for verification instead of the current time
|
||||||
|
|
|
@ -21,6 +21,7 @@ import util from '../util';
|
||||||
* are stored as numerical indices.
|
* are stored as numerical indices.
|
||||||
* @memberof module:packet
|
* @memberof module:packet
|
||||||
* @constructor
|
* @constructor
|
||||||
|
* @extends Array
|
||||||
*/
|
*/
|
||||||
function List() {
|
function List() {
|
||||||
/**
|
/**
|
||||||
|
@ -31,6 +32,8 @@ function List() {
|
||||||
this.length = 0;
|
this.length = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List.prototype = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads a stream of binary data and interprents it as a list of packets.
|
* Reads a stream of binary data and interprents it as a list of packets.
|
||||||
* @param {Uint8Array | ReadableStream<Uint8Array>} A Uint8Array of bytes.
|
* @param {Uint8Array | ReadableStream<Uint8Array>} A Uint8Array of bytes.
|
||||||
|
@ -145,37 +148,6 @@ List.prototype.push = function (packet) {
|
||||||
this.length++;
|
this.length++;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove a packet from the list and return it.
|
|
||||||
* @returns {Object} The packet that was removed
|
|
||||||
*/
|
|
||||||
List.prototype.pop = function() {
|
|
||||||
if (this.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const packet = this[this.length - 1];
|
|
||||||
delete this[this.length - 1];
|
|
||||||
this.length--;
|
|
||||||
|
|
||||||
return packet;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new PacketList with all packets that pass the test implemented by the provided function.
|
|
||||||
*/
|
|
||||||
List.prototype.filter = function (callback) {
|
|
||||||
const filtered = new List();
|
|
||||||
|
|
||||||
for (let i = 0; i < this.length; i++) {
|
|
||||||
if (callback(this[i], i, this)) {
|
|
||||||
filtered.push(this[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return filtered;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new PacketList with all packets from the given types
|
* Creates a new PacketList with all packets from the given types
|
||||||
*/
|
*/
|
||||||
|
@ -193,58 +165,6 @@ List.prototype.filterByTag = function (...args) {
|
||||||
return filtered;
|
return filtered;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes the provided callback once for each element
|
|
||||||
*/
|
|
||||||
List.prototype.forEach = function (callback) {
|
|
||||||
for (let i = 0; i < this.length; i++) {
|
|
||||||
callback(this[i], i, this);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an array containing return values of callback
|
|
||||||
* on each element
|
|
||||||
*/
|
|
||||||
List.prototype.map = function (callback) {
|
|
||||||
const packetArray = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < this.length; i++) {
|
|
||||||
packetArray.push(callback(this[i], i, this));
|
|
||||||
}
|
|
||||||
|
|
||||||
return packetArray;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes the callback function once for each element
|
|
||||||
* until it finds one where callback returns a truthy value
|
|
||||||
* @param {Function} callback
|
|
||||||
* @returns {Promise<Boolean>}
|
|
||||||
* @async
|
|
||||||
*/
|
|
||||||
List.prototype.some = async function (callback) {
|
|
||||||
for (let i = 0; i < this.length; i++) {
|
|
||||||
if (await callback(this[i], i, this)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes the callback function once for each element,
|
|
||||||
* returns true if all callbacks returns a truthy value
|
|
||||||
*/
|
|
||||||
List.prototype.every = function (callback) {
|
|
||||||
for (let i = 0; i < this.length; i++) {
|
|
||||||
if (!callback(this[i], i, this)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Traverses packet tree and returns first matching packet
|
* Traverses packet tree and returns first matching packet
|
||||||
* @param {module:enums.packet} type The packet type
|
* @param {module:enums.packet} type The packet type
|
||||||
|
@ -285,20 +205,6 @@ List.prototype.indexOfTag = function (...args) {
|
||||||
return tagIndex;
|
return tagIndex;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns slice of packetlist
|
|
||||||
*/
|
|
||||||
List.prototype.slice = function (begin, end) {
|
|
||||||
if (!end) {
|
|
||||||
end = this.length;
|
|
||||||
}
|
|
||||||
const part = new List();
|
|
||||||
for (let i = begin; i < end; i++) {
|
|
||||||
part.push(this[i]);
|
|
||||||
}
|
|
||||||
return part;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Concatenates packetlist or array of packets
|
* Concatenates packetlist or array of packets
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -201,6 +201,12 @@ Signature.prototype.sign = async function (key, data) {
|
||||||
this.signature = stream.fromAsync(async () => crypto.signature.sign(
|
this.signature = stream.fromAsync(async () => crypto.signature.sign(
|
||||||
publicKeyAlgorithm, hashAlgorithm, params, toHash, await stream.readToEnd(hash)
|
publicKeyAlgorithm, hashAlgorithm, params, toHash, await stream.readToEnd(hash)
|
||||||
));
|
));
|
||||||
|
|
||||||
|
// Store the fact that this signature is valid, e.g. for when we call `await
|
||||||
|
// getLatestValidSignature(this.revocationSignatures, key, data)` later. Note
|
||||||
|
// that this only holds up if the key and data passed to verify are the same
|
||||||
|
// as the ones passed to sign.
|
||||||
|
this.verified = true;
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1223,6 +1223,111 @@ t/ia1kMpSEiOVLlX5dfHZzhR3WNtBqU=
|
||||||
=C0fJ
|
=C0fJ
|
||||||
-----END PGP PRIVATE KEY BLOCK-----`;
|
-----END PGP PRIVATE KEY BLOCK-----`;
|
||||||
|
|
||||||
|
const key_with_authorized_revocation_key = `-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||||
|
Version: OpenPGP.js VERSION
|
||||||
|
Comment: https://openpgpjs.org
|
||||||
|
|
||||||
|
xsBNBFujnwwBCADK1xX03tSCmktPDS9Ncij3O5wG+F5/5Zm7QJDc39Wt1t/K
|
||||||
|
szCSobWtm/UObQVZjsTGwg0ZUPgepgWGGDBL0dlc1NObwUiOhGYnJnd4V25P
|
||||||
|
iU5Mg3+DhRq+LzNK+oGlpVPDpwQ48S8HOphbswKUpuaDcEQ2f+NKIc0eXe5m
|
||||||
|
ut5x9uoVj8jneUNsHYq6FIlxh4knzpJWFj5+LNi7plMCwKip6srVNf8He/q0
|
||||||
|
0xA/4vjSIOfGIE7TCBH33CbHEr98p81Cf4g0E+kswEz5iWE2SDCyYgQkMrkz
|
||||||
|
H9mtVqk3nFT8NR0USxKqH9bGhaTx1AWq/HDgsphayPEWQ0usjQDrbQUnABEB
|
||||||
|
AAHNDnRlc3QgPGFAYi5jb20+wsCNBBABCABBBQJbo58MBgsJBwgDAhcMgAH0
|
||||||
|
cOUNyxrV8eZOCGRKY2E6TW5AlAkQWICi/ReDcrkEFQgKAgMWAgECGQECGwMC
|
||||||
|
HgEAADgHB/0WIHh6maX2LZ0u5ujk1tZxWMrCycccopdQNKN0RGD98X4fyY6J
|
||||||
|
wfmKb107gcidJBFct0sVWFW8GU42w9pVMU5qWD6kyFkgcmov319UL+7aZ19b
|
||||||
|
HOWVKUTb6rFG8/qAbq3BF7YB/cZIBWMFKAS3BRJ4Kz23GheAB2A9oVLmuq5o
|
||||||
|
gW5c2R1YC0T0XyXEFiw9uZ+AS6kEZymFPRQfPUIbJs1ct/lAN+mC9Qp0Y6CL
|
||||||
|
60Hd6jlKUb6TgljaQ6CtLfT9v72GeKznapKr9IEtsgYv69j0c/MRM2nmu50c
|
||||||
|
g+fICiiHrTbNS6jkUz0pZLe7hdhWHeEiqcA9+GC1DxOQCRCS/YNfzsBNBFuj
|
||||||
|
nwwBCADK1xX03tSCmktPDS9Ncij3O5wG+F5/5Zm7QJDc39Wt1t/KszCSobWt
|
||||||
|
m/UObQVZjsTGwg0ZUPgepgWGGDBL0dlc1NObwUiOhGYnJnd4V25PiU5Mg3+D
|
||||||
|
hRq+LzNK+oGlpVPDpwQ48S8HOphbswKUpuaDcEQ2f+NKIc0eXe5mut5x9uoV
|
||||||
|
j8jneUNsHYq6FIlxh4knzpJWFj5+LNi7plMCwKip6srVNf8He/q00xA/4vjS
|
||||||
|
IOfGIE7TCBH33CbHEr98p81Cf4g0E+kswEz5iWE2SDCyYgQkMrkzH9mtVqk3
|
||||||
|
nFT8NR0USxKqH9bGhaTx1AWq/HDgsphayPEWQ0usjQDrbQUnABEBAAHCwF8E
|
||||||
|
GAEIABMFAlujnwwJEFiAov0Xg3K5AhsMAACI/QgArvTcutod+7n1D8wCwM50
|
||||||
|
jo3x4KPuQw+NwbOnMbFwv0R8i8NqtSFf2bYkkZ7RLViTmphvSon4h2WgfczL
|
||||||
|
SBulZ1QZF7zCKXmXDg8/HZgRUflC1XMixpB8Hqouin5AVgMbsbHg30V2uPco
|
||||||
|
V3DeFQ8HWxQC9symaMW/20MkqNXgCjM0us7kVwTEJQqZ6KYrFVjKyprSQRyP
|
||||||
|
rfckEBuZnj91OS+kAHlZ+ScZIuV4QVF0e2U6oEuB+qFXppR030PJoO6WDOzm
|
||||||
|
67hkzfrc3VpKw/I+vVJnZb4GOhNTSpa+p8i4gTyWmrOZ0G85QoldHWlWaRBg
|
||||||
|
lbjwPj3QUTbLFvHisYzXEQ==
|
||||||
|
=aT8U
|
||||||
|
-----END PGP PUBLIC KEY BLOCK-----
|
||||||
|
`;
|
||||||
|
|
||||||
|
const key_with_revoked_third_party_cert = `-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||||
|
Comment: GPGTools - https://gpgtools.org
|
||||||
|
|
||||||
|
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
|
||||||
|
Yzxp9Mp9
|
||||||
|
=uVX6
|
||||||
|
-----END PGP PUBLIC KEY BLOCK-----
|
||||||
|
`;
|
||||||
|
|
||||||
|
const certifying_key = `-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||||
|
Comment: GPGTools - https://gpgtools.org
|
||||||
|
|
||||||
|
mQGiBEOd+/gRBACfqCfgQCmUzOr7iA1CerGVmFm8HcN+NVGSpkwF6pmPJh1XVGEA
|
||||||
|
Nz9Aok6Vx4MQ+QCKo9dTXMZWDE4W/vzaKaEmsirsxGgn7JhK0t/9VeXXieWiJirA
|
||||||
|
5iTQMsRjfnS6MLLUr56E7HmDZftiOcpJu81S943r+oeINhq37SlJM7Q47wCg8miR
|
||||||
|
egss26IZfW3RvBuNW1KEDh0D/195DH6sl+/qmgUAj3M7ai1iKOqkILdNuIkYRc18
|
||||||
|
bsBYIAOjY81guhlEabYEqv8FUKPh2A7dAe/4og89HrUsVxOKJ9EGyrcqyJj676gK
|
||||||
|
BL383t1dQvyJyWfV5z4umUMaF/xE46go3Trdyu86aJDe57B74RYbWL2aaGLtkPJ2
|
||||||
|
rHOnBACG5PmLM6EXWJQSfbRqJHlRysc3MOAXMnxu8DVz+Tm0nPcArUG+Mf3GmxT+
|
||||||
|
537nHqF2xEgVvwfkvGB51ideRRQMhHUzy583zkuPYV1cJ6ykfeA6EHzVbT4vRzO8
|
||||||
|
AW9ELBKTK9F4N4gGTOdAATcaMC0gwzCz+fofJEJqC/9CS2pYvrQlVGhvbWFzIE9i
|
||||||
|
ZXJuZMO2cmZlciA8dG9iZXJuZG9AZ214LmRlPohdBBMRAgAdBQJDnfv4BgsJCAcD
|
||||||
|
AgQVAggDBBYCAwECHgECF4AACgkQ4XkKb4wTV02nkACfWvWnRawPI9AmgXpg6jg1
|
||||||
|
/22exKkAoMJ+yFhjtuGobOrIAPcEYrwlTQXBiGsEEBECACsFAksJKNQFgwHihQAe
|
||||||
|
Gmh0dHA6Ly93d3cuY2FjZXJ0Lm9yZy9jcHMucGhwAAoJENK7DQFl0P1Y4VoAmgMc
|
||||||
|
2qWuBtOBb6Uj6DskTtXORlPgAKCB3Hqp8dJ3dbQh5m0Ebf8F3P71WrkCDQRDnfw1
|
||||||
|
EAgAkp+0jMpfKKF6Gxy63fP3tCFzn9vq3GBtbNnvp8b0dx6nf+ZxELt/q9K4Yz9g
|
||||||
|
+sXq0RFQGV+YwS2BGoogzRcT4PHmUBcEAbjZIs9lZdZDEF0/68d+32mHSkLZJxGI
|
||||||
|
ezXJK3+MpGPnCMbQ63UYpcY1BvL7Vbj6P4X75dJJReGIHQMBA0FEYB5AVm6HrWU5
|
||||||
|
eDvOZ2w8QAAUluFnD9/xNRqBpcwm5uoox7zq60W5coK6p6WX8t5+WMMrRKF2A1Ru
|
||||||
|
aTxYQKo3f8XQA4e6tEcdGFlk1K9W8Ov1xVRQa6EqQYZFesbuoo8HHuSNsJ7PQrP+
|
||||||
|
vyYcafohlO/q4QtJXoUimsrEywADBQf7BQWrEx9YlNNsUD2yuov8pYCymxfUVTzK
|
||||||
|
huxGHmNj1htXfWWScA2uqD97HOdFu5nvoL2tdaO1RQR/OXKRBcUg6FhOQqqxQSxi
|
||||||
|
Vcsoy3aofGi3CWVXgn7KlSopkhlb4ELjzt5H+BMneXdgowO4MimXAfivI7OZl2fN
|
||||||
|
ut7emyN9qaeY/e25UKmCYhmhE5hM2+lV8wEmmu/qTCPiZ2u0zH/PE9AAwRz/6X+p
|
||||||
|
gsW0WIQpI6iQSSq4KyJxebtJFmCSTFawuXB6rCGovDXo/BkRsDEj1rpZnkwKJPa0
|
||||||
|
dEhKK4EzNrUzpWHeE3gKPjFXVmcjIPWVAC3BJoJRHOHg8wqLKcX5MYhGBBgRAgAG
|
||||||
|
BQJDnfw1AAoJEOF5Cm+ME1dNChoAoMKa/qx/RKlu3iQPtN6p4NlhRA9IAJ94F/7l
|
||||||
|
cKFQz1DDfFCfVpSIJRGozQ==
|
||||||
|
=EYzO
|
||||||
|
-----END PGP PUBLIC KEY BLOCK-----
|
||||||
|
`;
|
||||||
|
|
||||||
function versionSpecificTests() {
|
function versionSpecificTests() {
|
||||||
it('Preferences of generated key', function() {
|
it('Preferences of generated key', function() {
|
||||||
const testPref = function(key) {
|
const testPref = function(key) {
|
||||||
|
@ -1389,7 +1494,7 @@ function versionSpecificTests() {
|
||||||
const actual_delta = (new Date(expiration) - new Date()) / 1000;
|
const actual_delta = (new Date(expiration) - new Date()) / 1000;
|
||||||
expect(Math.abs(actual_delta - expect_delta)).to.be.below(60);
|
expect(Math.abs(actual_delta - expect_delta)).to.be.below(60);
|
||||||
|
|
||||||
const subKeyExpiration = await key.subKeys[0].getExpirationTime();
|
const subKeyExpiration = await key.subKeys[0].getExpirationTime(key.primaryKey);
|
||||||
expect(subKeyExpiration).to.exist;
|
expect(subKeyExpiration).to.exist;
|
||||||
|
|
||||||
const actual_subKeyDelta = (new Date(subKeyExpiration) - new Date()) / 1000;
|
const actual_subKeyDelta = (new Date(subKeyExpiration) - new Date()) / 1000;
|
||||||
|
@ -1688,6 +1793,15 @@ describe('Key', function() {
|
||||||
expect(pubKeys.keys[1].getKeyId().toHex()).to.equal('dbf223e870534df4');
|
expect(pubKeys.keys[1].getKeyId().toHex()).to.equal('dbf223e870534df4');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Parsing armored key with an authorized revocation key', async function() {
|
||||||
|
const pubKeys = await openpgp.key.readArmored(key_with_authorized_revocation_key);
|
||||||
|
expect(pubKeys).to.exist;
|
||||||
|
expect(pubKeys.err).to.exist.and.have.length(1);
|
||||||
|
expect(pubKeys.err[0].message).to.equal('This key is intended to be revoked with an authorized key, which OpenPGP.js does not support.');
|
||||||
|
expect(pubKeys.keys).to.have.length(1);
|
||||||
|
expect(pubKeys.keys[0].getKeyId().toHex()).to.equal('5880a2fd178372b9');
|
||||||
|
});
|
||||||
|
|
||||||
it('Parsing V5 public key packet', async function() {
|
it('Parsing V5 public key packet', async function() {
|
||||||
// Manually modified from https://gitlab.com/openpgp-wg/rfc4880bis/blob/00b2092/back.mkd#sample-eddsa-key
|
// Manually modified from https://gitlab.com/openpgp-wg/rfc4880bis/blob/00b2092/back.mkd#sample-eddsa-key
|
||||||
let packetBytes = openpgp.util.hex_to_Uint8Array(`
|
let packetBytes = openpgp.util.hex_to_Uint8Array(`
|
||||||
|
@ -1765,6 +1879,26 @@ describe('Key', function() {
|
||||||
)).to.eventually.equal(openpgp.enums.keyStatus.revoked);
|
)).to.eventually.equal(openpgp.enums.keyStatus.revoked);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Verify status of key with non-self revocation signature', async function() {
|
||||||
|
const { keys: [pubKey] } = await openpgp.key.readArmored(key_with_revoked_third_party_cert);
|
||||||
|
const [selfCertification] = await pubKey.verifyPrimaryUser();
|
||||||
|
const publicSigningKey = await pubKey.getSigningKey();
|
||||||
|
expect(selfCertification.keyid.toHex()).to.equal(publicSigningKey.getKeyId().toHex());
|
||||||
|
expect(selfCertification.valid).to.be.true;
|
||||||
|
|
||||||
|
const { keys: [certifyingKey] } = await openpgp.key.readArmored(certifying_key);
|
||||||
|
const certifyingSigningKey = await certifyingKey.getSigningKey();
|
||||||
|
const signatures = await pubKey.verifyPrimaryUser([certifyingKey]);
|
||||||
|
expect(signatures.length).to.equal(2);
|
||||||
|
expect(signatures[0].keyid.toHex()).to.equal(publicSigningKey.getKeyId().toHex());
|
||||||
|
expect(signatures[0].valid).to.be.null;
|
||||||
|
expect(signatures[1].keyid.toHex()).to.equal(certifyingSigningKey.getKeyId().toHex());
|
||||||
|
expect(signatures[1].valid).to.be.false;
|
||||||
|
|
||||||
|
const { user } = await pubKey.getPrimaryUser();
|
||||||
|
expect(await user.verifyCertificate(pubKey.primaryKey, user.otherCertifications[0], [certifyingKey])).to.equal(openpgp.enums.keyStatus.revoked);
|
||||||
|
});
|
||||||
|
|
||||||
it('Evaluate key flags to find valid encryption key packet', async function() {
|
it('Evaluate key flags to find valid encryption key packet', async function() {
|
||||||
const pubKeys = await openpgp.key.readArmored(pub_sig_test);
|
const pubKeys = await openpgp.key.readArmored(pub_sig_test);
|
||||||
expect(pubKeys).to.exist;
|
expect(pubKeys).to.exist;
|
||||||
|
@ -1799,7 +1933,7 @@ describe('Key', function() {
|
||||||
const pubKey = (await openpgp.key.readArmored(twoKeys)).keys[1];
|
const pubKey = (await openpgp.key.readArmored(twoKeys)).keys[1];
|
||||||
expect(pubKey).to.exist;
|
expect(pubKey).to.exist;
|
||||||
expect(pubKey).to.be.an.instanceof(openpgp.key.Key);
|
expect(pubKey).to.be.an.instanceof(openpgp.key.Key);
|
||||||
const expirationTime = await pubKey.subKeys[0].getExpirationTime();
|
const expirationTime = await pubKey.subKeys[0].getExpirationTime(pubKey.primaryKey);
|
||||||
expect(expirationTime.toISOString()).to.be.equal('2018-11-26T10:58:29.000Z');
|
expect(expirationTime.toISOString()).to.be.equal('2018-11-26T10:58:29.000Z');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1990,7 +2124,7 @@ describe('Key', function() {
|
||||||
|
|
||||||
it('getRevocationCertificate() should produce the same revocation certificate as GnuPG', async function() {
|
it('getRevocationCertificate() should produce the same revocation certificate as GnuPG', async function() {
|
||||||
const revKey = (await openpgp.key.readArmored(revoked_key_arm4)).keys[0];
|
const revKey = (await openpgp.key.readArmored(revoked_key_arm4)).keys[0];
|
||||||
const revocationCertificate = revKey.getRevocationCertificate();
|
const revocationCertificate = await revKey.getRevocationCertificate();
|
||||||
|
|
||||||
const input = await openpgp.armor.decode(revocation_certificate_arm4);
|
const input = await openpgp.armor.decode(revocation_certificate_arm4);
|
||||||
const packetlist = new openpgp.packet.List();
|
const packetlist = new openpgp.packet.List();
|
||||||
|
@ -2002,7 +2136,7 @@ describe('Key', function() {
|
||||||
|
|
||||||
it('getRevocationCertificate() should have an appropriate comment', async function() {
|
it('getRevocationCertificate() should have an appropriate comment', async function() {
|
||||||
const revKey = (await openpgp.key.readArmored(revoked_key_arm4)).keys[0];
|
const revKey = (await openpgp.key.readArmored(revoked_key_arm4)).keys[0];
|
||||||
const revocationCertificate = revKey.getRevocationCertificate();
|
const revocationCertificate = await revKey.getRevocationCertificate();
|
||||||
|
|
||||||
expect(revocationCertificate).to.match(/Comment: This is a revocation certificate/);
|
expect(revocationCertificate).to.match(/Comment: This is a revocation certificate/);
|
||||||
expect(revKey.armor()).not.to.match(/Comment: This is a revocation certificate/);
|
expect(revKey.armor()).not.to.match(/Comment: This is a revocation certificate/);
|
||||||
|
@ -2170,7 +2304,27 @@ VYGdb3eNlV8CfoEC
|
||||||
|
|
||||||
it('Selects the most recent subkey binding signature', async function() {
|
it('Selects the most recent subkey binding signature', async function() {
|
||||||
const key = (await openpgp.key.readArmored(multipleBindingSignatures)).keys[0];
|
const key = (await openpgp.key.readArmored(multipleBindingSignatures)).keys[0];
|
||||||
expect(key.subKeys[0].getExpirationTime().toISOString()).to.equal('2015-10-18T07:41:30.000Z');
|
expect((await key.subKeys[0].getExpirationTime(key.primaryKey)).toISOString()).to.equal('2015-10-18T07:41:30.000Z');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Selects the most recent non-expired subkey binding signature', async function() {
|
||||||
|
const key = (await openpgp.key.readArmored(multipleBindingSignatures)).keys[0];
|
||||||
|
key.subKeys[0].bindingSignatures[1].signatureNeverExpires = false;
|
||||||
|
key.subKeys[0].bindingSignatures[1].signatureExpirationTime = 0;
|
||||||
|
expect((await key.subKeys[0].getExpirationTime(key.primaryKey)).toISOString()).to.equal('2018-09-07T06:03:37.000Z');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Selects the most recent valid subkey binding signature', async function() {
|
||||||
|
const key = (await openpgp.key.readArmored(multipleBindingSignatures)).keys[0];
|
||||||
|
key.subKeys[0].bindingSignatures[1].signatureData[0]++;
|
||||||
|
expect((await key.subKeys[0].getExpirationTime(key.primaryKey)).toISOString()).to.equal('2018-09-07T06:03:37.000Z');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Handles a key with no valid subkey binding signatures gracefully', async function() {
|
||||||
|
const key = (await openpgp.key.readArmored(multipleBindingSignatures)).keys[0];
|
||||||
|
key.subKeys[0].bindingSignatures[0].signatureData[0]++;
|
||||||
|
key.subKeys[0].bindingSignatures[1].signatureData[0]++;
|
||||||
|
expect(await key.subKeys[0].getExpirationTime(key.primaryKey)).to.be.null;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Reject encryption with revoked subkey', async function() {
|
it('Reject encryption with revoked subkey', async function() {
|
||||||
|
|
|
@ -1,47 +0,0 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// addEventListener('fetch', event => {
|
|
||||||
// console.log(event);
|
|
||||||
// const url = new URL(event.request.url);
|
|
||||||
// console.log(url);
|
|
||||||
// if (url.pathname === '/test/somedata') {
|
|
||||||
// let plaintext = [];
|
|
||||||
// let i = 0;
|
|
||||||
// let canceled = false;
|
|
||||||
// const data = new ReadableStream({
|
|
||||||
// /*start(_controller) {
|
|
||||||
// controller = _controller;
|
|
||||||
// },*/
|
|
||||||
// async pull(controller) {
|
|
||||||
// await new Promise(resolve => setTimeout(resolve, 1000));
|
|
||||||
// console.log(i);
|
|
||||||
// if (i++ < 10) {
|
|
||||||
// let randomBytes = new Uint8Array(1000);
|
|
||||||
// randomBytes.fill(i);
|
|
||||||
// controller.enqueue(randomBytes);
|
|
||||||
// plaintext.push(randomBytes);
|
|
||||||
// } else {
|
|
||||||
// controller.close();
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// cancel() {
|
|
||||||
// console.log('canceled!');
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
|
|
||||||
// const response = new Response(data, {
|
|
||||||
// headers: {
|
|
||||||
// 'Content-Type': 'application/octet-stream; charset=utf-8',
|
|
||||||
// 'Content-Disposition': 'Content-Disposition: attachment; filename=data.bin;'
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
// event.respondWith(response);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// });
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user