Merge pull request #772 from twiss/getLatestValidSignature
Check validity and keyid of signatures before using them
This commit is contained in:
commit
a35b4d28e0
178
src/key.js
178
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 {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()) {
|
||||
let signature = signatures[0];
|
||||
for (let i = 1; i < signatures.length; i++) {
|
||||
if (signatures[i].created >= signature.created &&
|
||||
(signatures[i].created <= date || date === null)) {
|
||||
async function getLatestValidSignature(signatures, primaryKey, dataToVerify, date=new Date()) {
|
||||
let signature;
|
||||
for (let i = signatures.length - 1; i >= 0; i--) {
|
||||
if (
|
||||
(!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];
|
||||
}
|
||||
}
|
||||
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
|
||||
* @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++) {
|
||||
if (!keyId || subKeys[i].getKeyId().equals(keyId)) {
|
||||
if (await subKeys[i].verify(primaryKey, date) === enums.keyStatus.valid) {
|
||||
const bindingSignature = getLatestSignature(subKeys[i].bindingSignatures, date);
|
||||
if (isValidSigningKeyPacket(subKeys[i].keyPacket, bindingSignature, date)) {
|
||||
const dataToVerify = { key: primaryKey, bind: subKeys[i].keyPacket };
|
||||
const bindingSignature = await getLatestValidSignature(subKeys[i].bindingSignatures, primaryKey, dataToVerify, date);
|
||||
if (bindingSignature && isValidSigningKeyPacket(subKeys[i].keyPacket, bindingSignature)) {
|
||||
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);
|
||||
if (primaryUser && (!keyId || primaryKey.getKeyId().equals(keyId)) &&
|
||||
isValidSigningKeyPacket(primaryKey, primaryUser.selfCertification, date)) {
|
||||
isValidSigningKeyPacket(primaryKey, primaryUser.selfCertification)) {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
function isValidEncryptionKeyPacket(keyPacket, signature, date=new Date()) {
|
||||
return keyPacket.algorithm !== enums.read(enums.publicKey, enums.publicKey.dsa) &&
|
||||
keyPacket.algorithm !== enums.read(enums.publicKey, enums.publicKey.rsa_sign) &&
|
||||
keyPacket.algorithm !== enums.read(enums.publicKey, enums.publicKey.ecdsa) &&
|
||||
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) &&
|
||||
signature.verified && !signature.revoked && !signature.isExpired(date) &&
|
||||
!isDataExpired(keyPacket, signature, date);
|
||||
function isValidSigningKeyPacket(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.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);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 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++) {
|
||||
if (!keyId || subKeys[i].getKeyId().equals(keyId)) {
|
||||
if (await subKeys[i].verify(primaryKey, date) === enums.keyStatus.valid) {
|
||||
const bindingSignature = getLatestSignature(subKeys[i].bindingSignatures, date);
|
||||
if (isValidEncryptionKeyPacket(subKeys[i].keyPacket, bindingSignature, date)) {
|
||||
const dataToVerify = { key: primaryKey, bind: subKeys[i].keyPacket };
|
||||
const bindingSignature = await getLatestValidSignature(subKeys[i].bindingSignatures, primaryKey, dataToVerify, date);
|
||||
if (bindingSignature && isValidEncryptionKeyPacket(subKeys[i].keyPacket, bindingSignature)) {
|
||||
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
|
||||
const primaryUser = await this.getPrimaryUser(date, userId);
|
||||
if (primaryUser && (!keyId || primaryKey.getKeyId().equals(keyId)) &&
|
||||
isValidEncryptionKeyPacket(primaryKey, primaryUser.selfCertification, date)) {
|
||||
isValidEncryptionKeyPacket(primaryKey, primaryUser.selfCertification)) {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
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.
|
||||
* 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.
|
||||
* @param {encrypt|sign|encrypt_sign} capabilities, optional
|
||||
* @param {module:type/keyid} keyId, optional
|
||||
* @param {Object} userId, optional user ID
|
||||
* @returns {Promise<Date>}
|
||||
* @returns {Promise<Date | Infinity | null>}
|
||||
* @async
|
||||
*/
|
||||
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') {
|
||||
const encryptKey = await this.getEncryptionKey(keyId, null, userId);
|
||||
if (!encryptKey) return null;
|
||||
const encryptExpiry = encryptKey.getExpirationTime();
|
||||
const encryptExpiry = await encryptKey.getExpirationTime(this.keyPacket);
|
||||
if (encryptExpiry < expiry) expiry = encryptExpiry;
|
||||
}
|
||||
if (capabilities === 'sign' || capabilities === 'encrypt_sign') {
|
||||
const signKey = await this.getSigningKey(keyId, null, userId);
|
||||
if (!signKey) return null;
|
||||
const signExpiry = signKey.getExpirationTime();
|
||||
const signExpiry = await signKey.getExpirationTime(this.keyPacket);
|
||||
if (signExpiry < expiry) expiry = signExpiry;
|
||||
}
|
||||
return expiry;
|
||||
|
@ -515,16 +526,20 @@ Key.prototype.getExpirationTime = async function(capabilities, keyId, userId) {
|
|||
* @async
|
||||
*/
|
||||
Key.prototype.getPrimaryUser = async function(date=new Date(), userId={}) {
|
||||
const users = this.users.map(function(user, index) {
|
||||
const selfCertification = getLatestSignature(user.selfCertifications, date);
|
||||
return { index, user, selfCertification };
|
||||
}).filter(({ user, selfCertification }) => {
|
||||
return user.userId && selfCertification && (
|
||||
const primaryKey = this.keyPacket;
|
||||
const users = [];
|
||||
for (let i = 0; i < this.users.length; i++) {
|
||||
const user = this.users[i];
|
||||
if (!user.userId || !(
|
||||
(userId.name === undefined || user.userId.name === userId.name) &&
|
||||
(userId.email === undefined || user.userId.email === userId.email) &&
|
||||
(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 (userId.name !== undefined || userId.email !== 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;
|
||||
}).pop();
|
||||
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)) {
|
||||
return null;
|
||||
}
|
||||
if (cert.isExpired(date)) {
|
||||
return null;
|
||||
}
|
||||
return primaryUser;
|
||||
};
|
||||
|
||||
|
@ -678,12 +684,15 @@ Key.prototype.revoke = async function({
|
|||
/**
|
||||
* Get revocation certificate from a revoked key.
|
||||
* (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() {
|
||||
if (this.revocationSignatures.length) {
|
||||
Key.prototype.getRevocationCertificate = async function() {
|
||||
const dataToVerify = { key: this.keyPacket };
|
||||
const revocationSignature = await getLatestValidSignature(this.revocationSignatures, this.keyPacket, dataToVerify);
|
||||
if (revocationSignature) {
|
||||
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');
|
||||
}
|
||||
};
|
||||
|
@ -1090,17 +1099,17 @@ SubKey.prototype.verify = async function(primaryKey, date=new Date()) {
|
|||
const that = this;
|
||||
const dataToVerify = { key: primaryKey, bind: this.keyPacket };
|
||||
// check subkey binding signatures
|
||||
const bindingSignature = getLatestSignature(this.bindingSignatures, date);
|
||||
const bindingSignature = await getLatestValidSignature(this.bindingSignatures, primaryKey, dataToVerify, date);
|
||||
// check binding signature is verified
|
||||
if (!(bindingSignature.verified || await bindingSignature.verify(primaryKey, dataToVerify))) {
|
||||
if (!bindingSignature) {
|
||||
return enums.keyStatus.invalid;
|
||||
}
|
||||
// check binding signature is not revoked
|
||||
if (bindingSignature.revoked || await that.isRevoked(primaryKey, bindingSignature, null, date)) {
|
||||
return enums.keyStatus.revoked;
|
||||
}
|
||||
// check binding signature is not expired (ie, check for V4 expiration time)
|
||||
if (bindingSignature.isExpired(date)) {
|
||||
// check for expiration time
|
||||
if (isDataExpired(this.keyPacket, bindingSignature, date)) {
|
||||
return enums.keyStatus.expired;
|
||||
}
|
||||
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 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
|
||||
* @returns {Date}
|
||||
* @returns {Promise<Date | Infinity | null>}
|
||||
* @async
|
||||
*/
|
||||
SubKey.prototype.getExpirationTime = function(date=new Date()) {
|
||||
const bindingSignature = getLatestSignature(this.bindingSignatures, date);
|
||||
SubKey.prototype.getExpirationTime = async function(primaryKey, date=new 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 sigExpiry = bindingSignature.getExpirationTime();
|
||||
return keyExpiry < sigExpiry ? keyExpiry : sigExpiry;
|
||||
|
@ -1186,8 +1201,6 @@ SubKey.prototype.revoke = async function(primaryKey, {
|
|||
return subKey;
|
||||
};
|
||||
|
||||
/**
|
||||
*/
|
||||
['getKeyId', 'getFingerprint', 'getAlgorithmInfo', 'getCreationTime', 'isDecrypted'].forEach(name => {
|
||||
Key.prototype[name] =
|
||||
SubKey.prototype[name] =
|
||||
|
@ -1207,9 +1220,16 @@ SubKey.prototype.revoke = async function(primaryKey, {
|
|||
export async function read(data) {
|
||||
const result = {};
|
||||
result.keys = [];
|
||||
const err = [];
|
||||
try {
|
||||
const packetlist = new packet.List();
|
||||
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);
|
||||
if (keyIndex.length === 0) {
|
||||
throw new Error('No key packet found');
|
||||
|
@ -1220,13 +1240,14 @@ export async function read(data) {
|
|||
const newKey = new Key(oneKeyList);
|
||||
result.keys.push(newKey);
|
||||
} catch (e) {
|
||||
result.err = result.err || [];
|
||||
result.err.push(e);
|
||||
err.push(e);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
result.err = result.err || [];
|
||||
result.err.push(e);
|
||||
err.push(e);
|
||||
}
|
||||
if (err.length) {
|
||||
result.err = err;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -1545,8 +1566,19 @@ async function isDataRevoked(primaryKey, dataToVerify, revocations, signature, k
|
|||
const normDate = util.normalizeDate(date);
|
||||
const revocationKeyIds = [];
|
||||
await Promise.all(revocations.map(async function(revocationSignature) {
|
||||
if (!(config.revocations_expire && revocationSignature.isExpired(normDate)) &&
|
||||
(revocationSignature.verified || await revocationSignature.verify(key, dataToVerify))) {
|
||||
if (
|
||||
// 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
|
||||
revocationKeyIds.push(revocationSignature.issuerKeyId);
|
||||
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
|
||||
if (signature) {
|
||||
signature.revoked = revocationKeyIds.some(keyId => keyId.equals(signature.issuerKeyId)) ? true :
|
||||
signature.revoked;
|
||||
signature.revoked || false;
|
||||
return signature.revoked;
|
||||
}
|
||||
return revocationKeyIds.length > 0;
|
||||
|
|
|
@ -124,7 +124,7 @@ export function generateKey({ userIds=[], passphrase="", numBits=2048, keyExpira
|
|||
}
|
||||
|
||||
return generate(options).then(async key => {
|
||||
const revocationCertificate = key.getRevocationCertificate();
|
||||
const revocationCertificate = await key.getRevocationCertificate();
|
||||
key.revocationSignatures = [];
|
||||
|
||||
return convertStreams({
|
||||
|
@ -159,8 +159,8 @@ export function reformatKey({privateKey, userIds=[], passphrase="", keyExpiratio
|
|||
|
||||
options.revoked = options.revocationCertificate;
|
||||
|
||||
return reformat(options).then(key => {
|
||||
const revocationCertificate = key.getRevocationCertificate();
|
||||
return reformat(options).then(async key => {
|
||||
const revocationCertificate = await key.getRevocationCertificate();
|
||||
key.revocationSignatures = [];
|
||||
|
||||
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 {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 {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 {Signature} signature (optional) detached signature for verification
|
||||
* @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.
|
||||
* @memberof module:packet
|
||||
* @constructor
|
||||
* @extends Array
|
||||
*/
|
||||
function List() {
|
||||
/**
|
||||
|
@ -31,6 +32,8 @@ function List() {
|
|||
this.length = 0;
|
||||
}
|
||||
|
||||
List.prototype = [];
|
||||
|
||||
/**
|
||||
* Reads a stream of binary data and interprents it as a list of packets.
|
||||
* @param {Uint8Array | ReadableStream<Uint8Array>} A Uint8Array of bytes.
|
||||
|
@ -145,37 +148,6 @@ List.prototype.push = function (packet) {
|
|||
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
|
||||
*/
|
||||
|
@ -193,58 +165,6 @@ List.prototype.filterByTag = function (...args) {
|
|||
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
|
||||
* @param {module:enums.packet} type The packet type
|
||||
|
@ -285,20 +205,6 @@ List.prototype.indexOfTag = function (...args) {
|
|||
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
|
||||
*/
|
||||
|
|
|
@ -201,6 +201,12 @@ Signature.prototype.sign = async function (key, data) {
|
|||
this.signature = stream.fromAsync(async () => crypto.signature.sign(
|
||||
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;
|
||||
};
|
||||
|
||||
|
|
|
@ -1223,6 +1223,111 @@ t/ia1kMpSEiOVLlX5dfHZzhR3WNtBqU=
|
|||
=C0fJ
|
||||
-----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() {
|
||||
it('Preferences of generated key', function() {
|
||||
const testPref = function(key) {
|
||||
|
@ -1389,7 +1494,7 @@ function versionSpecificTests() {
|
|||
const actual_delta = (new Date(expiration) - new Date()) / 1000;
|
||||
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;
|
||||
|
||||
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');
|
||||
});
|
||||
|
||||
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() {
|
||||
// Manually modified from https://gitlab.com/openpgp-wg/rfc4880bis/blob/00b2092/back.mkd#sample-eddsa-key
|
||||
let packetBytes = openpgp.util.hex_to_Uint8Array(`
|
||||
|
@ -1765,6 +1879,26 @@ describe('Key', function() {
|
|||
)).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() {
|
||||
const pubKeys = await openpgp.key.readArmored(pub_sig_test);
|
||||
expect(pubKeys).to.exist;
|
||||
|
@ -1799,7 +1933,7 @@ describe('Key', function() {
|
|||
const pubKey = (await openpgp.key.readArmored(twoKeys)).keys[1];
|
||||
expect(pubKey).to.exist;
|
||||
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');
|
||||
});
|
||||
|
||||
|
@ -1990,7 +2124,7 @@ describe('Key', 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 revocationCertificate = revKey.getRevocationCertificate();
|
||||
const revocationCertificate = await revKey.getRevocationCertificate();
|
||||
|
||||
const input = await openpgp.armor.decode(revocation_certificate_arm4);
|
||||
const packetlist = new openpgp.packet.List();
|
||||
|
@ -2002,7 +2136,7 @@ describe('Key', function() {
|
|||
|
||||
it('getRevocationCertificate() should have an appropriate comment', async function() {
|
||||
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(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() {
|
||||
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() {
|
||||
|
|
|
@ -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