Reject signatures using insecure hash algorithms
Also, switch from returning false to throwing errors in most verify*() functions, as well as in `await signatures[*].verified`, in order to be able to show more informative error messages.
This commit is contained in:
parent
3af8e32bf0
commit
8c3bcd1f21
|
@ -210,5 +210,15 @@ export default {
|
|||
* @memberof module:config
|
||||
* @property {Object} indutny_elliptic_fetch_options Options object to pass to `fetch` when loading the indutny/elliptic library. Only has an effect if `config.external_indutny_elliptic` is true.
|
||||
*/
|
||||
indutny_elliptic_fetch_options: {}
|
||||
indutny_elliptic_fetch_options: {},
|
||||
/**
|
||||
* @memberof module:config
|
||||
* @property {Set<Integer>} reject_hash_algorithms Reject insecure hash algorithms {@link module:enums.hash}
|
||||
*/
|
||||
reject_hash_algorithms: new Set([enums.hash.md5, enums.hash.ripemd]),
|
||||
/**
|
||||
* @memberof module:config
|
||||
* @property {Set<Integer>} reject_message_hash_algorithms Reject insecure message hash algorithms {@link module:enums.hash}
|
||||
*/
|
||||
reject_message_hash_algorithms: new Set([enums.hash.md5, enums.hash.ripemd, enums.hash.sha1])
|
||||
};
|
||||
|
|
12
src/enums.js
12
src/enums.js
|
@ -403,18 +403,6 @@ export default {
|
|||
shared_private_key: 128
|
||||
},
|
||||
|
||||
/** Key status
|
||||
* @enum {Integer}
|
||||
* @readonly
|
||||
*/
|
||||
keyStatus: {
|
||||
invalid: 0,
|
||||
expired: 1,
|
||||
revoked: 2,
|
||||
valid: 3,
|
||||
no_self_cert: 4
|
||||
},
|
||||
|
||||
/** Armor type
|
||||
* @enum {Integer}
|
||||
* @readonly
|
||||
|
|
|
@ -111,8 +111,8 @@ export async function reformat(options) {
|
|||
|
||||
if (!options.subkeys) {
|
||||
options.subkeys = await Promise.all(secretSubkeyPackets.map(async secretSubkeyPacket => ({
|
||||
sign: await options.privateKey.getSigningKey(secretSubkeyPacket.getKeyId(), null) &&
|
||||
!await options.privateKey.getEncryptionKey(secretSubkeyPacket.getKeyId(), null)
|
||||
sign: await options.privateKey.getSigningKey(secretSubkeyPacket.getKeyId(), null).catch(() => {}) &&
|
||||
!await options.privateKey.getEncryptionKey(secretSubkeyPacket.getKeyId(), null).catch(() => {})
|
||||
})));
|
||||
}
|
||||
|
||||
|
|
|
@ -38,17 +38,29 @@ export async function generateSecretKey(options) {
|
|||
*/
|
||||
export async function getLatestValidSignature(signatures, primaryKey, signatureType, dataToVerify, date = new Date()) {
|
||||
let signature;
|
||||
let exception;
|
||||
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, signatureType, dataToVerify))
|
||||
) {
|
||||
signature = signatures[i];
|
||||
try {
|
||||
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, signatureType, dataToVerify))
|
||||
) {
|
||||
signature = signatures[i];
|
||||
}
|
||||
} catch (e) {
|
||||
exception = e;
|
||||
}
|
||||
}
|
||||
if (!signature) {
|
||||
throw util.wrapError(
|
||||
`Could not find valid ${enums.read(enums.signature, signatureType)} signature in key ${primaryKey.getKeyId().toHex()}`
|
||||
.replace('cert_generic ', 'self-')
|
||||
.replace('_', ' ')
|
||||
, exception);
|
||||
}
|
||||
return signature;
|
||||
}
|
||||
|
||||
|
@ -106,7 +118,7 @@ export async function getPreferredHashAlgo(key, keyPacket, date = new Date(), us
|
|||
let pref_algo = hash_algo;
|
||||
if (key) {
|
||||
const primaryUser = await key.getPrimaryUser(date, userId);
|
||||
if (primaryUser && primaryUser.selfCertification.preferredHashAlgorithms) {
|
||||
if (primaryUser.selfCertification.preferredHashAlgorithms) {
|
||||
[pref_algo] = primaryUser.selfCertification.preferredHashAlgorithms;
|
||||
hash_algo = crypto.hash.getHashByteLength(hash_algo) <= crypto.hash.getHashByteLength(pref_algo) ?
|
||||
pref_algo : hash_algo;
|
||||
|
@ -143,7 +155,7 @@ export async function getPreferredAlgo(type, keys, date = new Date(), userIds =
|
|||
const prioMap = {};
|
||||
await Promise.all(keys.map(async function(key, i) {
|
||||
const primaryUser = await key.getPrimaryUser(date, userIds[i]);
|
||||
if (!primaryUser || !primaryUser.selfCertification[prefProperty]) {
|
||||
if (!primaryUser.selfCertification[prefProperty]) {
|
||||
return defaultAlgo;
|
||||
}
|
||||
primaryUser.selfCertification[prefProperty].forEach(function(algo, index) {
|
||||
|
@ -237,24 +249,24 @@ export async function isDataRevoked(primaryKey, signatureType, dataToVerify, rev
|
|||
const normDate = util.normalizeDate(date);
|
||||
const revocationKeyIds = [];
|
||||
await Promise.all(revocations.map(async function(revocationSignature) {
|
||||
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, signatureType, dataToVerify))
|
||||
) {
|
||||
// TODO get an identifier of the revoked object instead
|
||||
revocationKeyIds.push(revocationSignature.issuerKeyId);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
try {
|
||||
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, signatureType, dataToVerify))
|
||||
) {
|
||||
// TODO get an identifier of the revoked object instead
|
||||
revocationKeyIds.push(revocationSignature.issuerKeyId);
|
||||
}
|
||||
} catch (e) {}
|
||||
}));
|
||||
// TODO further verify that this is the signature that should be revoked
|
||||
if (signature) {
|
||||
|
@ -287,7 +299,7 @@ export async function isAeadSupported(keys, date = new Date(), userIds = []) {
|
|||
// TODO replace when Promise.some or Promise.any are implemented
|
||||
await Promise.all(keys.map(async function(key, i) {
|
||||
const primaryUser = await key.getPrimaryUser(date, userIds[i]);
|
||||
if (!primaryUser || !primaryUser.selfCertification.features ||
|
||||
if (!primaryUser.selfCertification.features ||
|
||||
!(primaryUser.selfCertification.features[0] & enums.features.aead)) {
|
||||
supported = false;
|
||||
}
|
||||
|
|
182
src/key/key.js
182
src/key/key.js
|
@ -271,32 +271,35 @@ Key.prototype.armor = function() {
|
|||
* @async
|
||||
*/
|
||||
Key.prototype.getSigningKey = async function (keyId = null, date = new Date(), userId = {}) {
|
||||
await this.verifyPrimaryKey(date, userId);
|
||||
const primaryKey = this.keyPacket;
|
||||
if (await this.verifyPrimaryKey(date, userId) === enums.keyStatus.valid) {
|
||||
const subKeys = this.subKeys.slice().sort((a, b) => b.keyPacket.created - a.keyPacket.created);
|
||||
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 dataToVerify = { key: primaryKey, bind: subKeys[i].keyPacket };
|
||||
const bindingSignature = await helper.getLatestValidSignature(subKeys[i].bindingSignatures, primaryKey, enums.signature.subkey_binding, dataToVerify, date);
|
||||
if (
|
||||
bindingSignature &&
|
||||
bindingSignature.embeddedSignature &&
|
||||
helper.isValidSigningKeyPacket(subKeys[i].keyPacket, bindingSignature) &&
|
||||
await helper.getLatestValidSignature([bindingSignature.embeddedSignature], subKeys[i].keyPacket, enums.signature.key_binding, dataToVerify, date)
|
||||
) {
|
||||
return subKeys[i];
|
||||
}
|
||||
const subKeys = this.subKeys.slice().sort((a, b) => b.keyPacket.created - a.keyPacket.created);
|
||||
let exception;
|
||||
for (let i = 0; i < subKeys.length; i++) {
|
||||
if (!keyId || subKeys[i].getKeyId().equals(keyId)) {
|
||||
try {
|
||||
await subKeys[i].verify(primaryKey, date);
|
||||
const dataToVerify = { key: primaryKey, bind: subKeys[i].keyPacket };
|
||||
const bindingSignature = await helper.getLatestValidSignature(subKeys[i].bindingSignatures, primaryKey, enums.signature.subkey_binding, dataToVerify, date);
|
||||
if (
|
||||
bindingSignature &&
|
||||
bindingSignature.embeddedSignature &&
|
||||
helper.isValidSigningKeyPacket(subKeys[i].keyPacket, bindingSignature) &&
|
||||
await helper.getLatestValidSignature([bindingSignature.embeddedSignature], subKeys[i].keyPacket, enums.signature.key_binding, dataToVerify, date)
|
||||
) {
|
||||
return subKeys[i];
|
||||
}
|
||||
} catch (e) {
|
||||
exception = e;
|
||||
}
|
||||
}
|
||||
const primaryUser = await this.getPrimaryUser(date, userId);
|
||||
if (primaryUser && (!keyId || primaryKey.getKeyId().equals(keyId)) &&
|
||||
helper.isValidSigningKeyPacket(primaryKey, primaryUser.selfCertification)) {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
const primaryUser = await this.getPrimaryUser(date, userId);
|
||||
if ((!keyId || primaryKey.getKeyId().equals(keyId)) &&
|
||||
helper.isValidSigningKeyPacket(primaryKey, primaryUser.selfCertification)) {
|
||||
return this;
|
||||
}
|
||||
throw util.wrapError('Could not find valid signing key packet in key ' + this.getKeyId().toHex(), exception);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -308,30 +311,32 @@ Key.prototype.getSigningKey = async function (keyId = null, date = new Date(), u
|
|||
* @async
|
||||
*/
|
||||
Key.prototype.getEncryptionKey = async function(keyId, date = new Date(), userId = {}) {
|
||||
await this.verifyPrimaryKey(date, userId);
|
||||
const primaryKey = this.keyPacket;
|
||||
if (await this.verifyPrimaryKey(date, userId) === enums.keyStatus.valid) {
|
||||
// V4: by convention subkeys are preferred for encryption service
|
||||
const subKeys = this.subKeys.slice().sort((a, b) => b.keyPacket.created - a.keyPacket.created);
|
||||
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 dataToVerify = { key: primaryKey, bind: subKeys[i].keyPacket };
|
||||
const bindingSignature = await helper.getLatestValidSignature(subKeys[i].bindingSignatures, primaryKey, enums.signature.subkey_binding, dataToVerify, date);
|
||||
if (bindingSignature && helper.isValidEncryptionKeyPacket(subKeys[i].keyPacket, bindingSignature)) {
|
||||
return subKeys[i];
|
||||
}
|
||||
// V4: by convention subkeys are preferred for encryption service
|
||||
const subKeys = this.subKeys.slice().sort((a, b) => b.keyPacket.created - a.keyPacket.created);
|
||||
let exception;
|
||||
for (let i = 0; i < subKeys.length; i++) {
|
||||
if (!keyId || subKeys[i].getKeyId().equals(keyId)) {
|
||||
try {
|
||||
await subKeys[i].verify(primaryKey, date);
|
||||
const dataToVerify = { key: primaryKey, bind: subKeys[i].keyPacket };
|
||||
const bindingSignature = await helper.getLatestValidSignature(subKeys[i].bindingSignatures, primaryKey, enums.signature.subkey_binding, dataToVerify, date);
|
||||
if (bindingSignature && helper.isValidEncryptionKeyPacket(subKeys[i].keyPacket, bindingSignature)) {
|
||||
return subKeys[i];
|
||||
}
|
||||
} catch (e) {
|
||||
exception = e;
|
||||
}
|
||||
}
|
||||
// if no valid subkey for encryption, evaluate primary key
|
||||
const primaryUser = await this.getPrimaryUser(date, userId);
|
||||
if (primaryUser && (!keyId || primaryKey.getKeyId().equals(keyId)) &&
|
||||
helper.isValidEncryptionKeyPacket(primaryKey, primaryUser.selfCertification)) {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
// if no valid subkey for encryption, evaluate primary key
|
||||
const primaryUser = await this.getPrimaryUser(date, userId);
|
||||
if ((!keyId || primaryKey.getKeyId().equals(keyId)) &&
|
||||
helper.isValidEncryptionKeyPacket(primaryKey, primaryUser.selfCertification)) {
|
||||
return this;
|
||||
}
|
||||
throw util.wrapError('Could not find valid encryption key packet in key ' + this.getKeyId().toHex(), exception);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -413,7 +418,7 @@ Key.prototype.validate = async function() {
|
|||
const signatureType = enums.signature.binary;
|
||||
signature.signatureType = signatureType;
|
||||
await signature.sign(signingKeyPacket, data);
|
||||
return signature.verify(signingKeyPacket, signatureType, data);
|
||||
await signature.verify(signingKeyPacket, signatureType, data);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -450,33 +455,29 @@ Key.prototype.isRevoked = async function(signature, key, date = new Date()) {
|
|||
|
||||
/**
|
||||
* Verify primary key. Checks for revocation signatures, expiration time
|
||||
* and valid self signature
|
||||
* and valid self signature. Throws if the primary key is invalid.
|
||||
* @param {Date} date (optional) use the given date for verification instead of the current time
|
||||
* @param {Object} userId (optional) user ID
|
||||
* @returns {Promise<module:enums.keyStatus>} The status of the primary key
|
||||
* @returns {Promise<true>} The status of the primary key
|
||||
* @async
|
||||
*/
|
||||
Key.prototype.verifyPrimaryKey = async function(date = new Date(), userId = {}) {
|
||||
const primaryKey = this.keyPacket;
|
||||
// check for key revocation signatures
|
||||
if (await this.isRevoked(null, null, date)) {
|
||||
return enums.keyStatus.revoked;
|
||||
throw new Error('Primary key is revoked');
|
||||
}
|
||||
// check for at least one self signature. Self signature of user ID not mandatory
|
||||
// See {@link https://tools.ietf.org/html/rfc4880#section-11.1}
|
||||
if (!this.users.some(user => user.userId && user.selfCertifications.length)) {
|
||||
return enums.keyStatus.no_self_cert;
|
||||
throw new Error('No self-certifications');
|
||||
}
|
||||
// check for valid, unrevoked, unexpired self signature
|
||||
const { user, selfCertification } = await this.getPrimaryUser(date, userId) || {};
|
||||
if (!user) {
|
||||
return enums.keyStatus.invalid;
|
||||
}
|
||||
const { selfCertification } = await this.getPrimaryUser(date, userId);
|
||||
// check for expiration time
|
||||
if (helper.isDataExpired(primaryKey, selfCertification, date)) {
|
||||
return enums.keyStatus.expired;
|
||||
throw new Error('Primary key is expired');
|
||||
}
|
||||
return enums.keyStatus.valid;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -492,25 +493,22 @@ Key.prototype.verifyPrimaryKey = async function(date = new Date(), userId = {})
|
|||
*/
|
||||
Key.prototype.getExpirationTime = async function(capabilities, keyId, userId) {
|
||||
const primaryUser = await this.getPrimaryUser(null, userId);
|
||||
if (!primaryUser) {
|
||||
throw new Error('Could not find primary user');
|
||||
}
|
||||
const selfCert = primaryUser.selfCertification;
|
||||
const keyExpiry = helper.getExpirationTime(this.keyPacket, selfCert);
|
||||
const sigExpiry = selfCert.getExpirationTime();
|
||||
let expiry = keyExpiry < sigExpiry ? keyExpiry : sigExpiry;
|
||||
if (capabilities === 'encrypt' || capabilities === 'encrypt_sign') {
|
||||
const encryptKey =
|
||||
await this.getEncryptionKey(keyId, expiry, userId) ||
|
||||
await this.getEncryptionKey(keyId, null, userId);
|
||||
await this.getEncryptionKey(keyId, expiry, userId).catch(() => {}) ||
|
||||
await this.getEncryptionKey(keyId, null, userId).catch(() => {});
|
||||
if (!encryptKey) return null;
|
||||
const encryptExpiry = await encryptKey.getExpirationTime(this.keyPacket);
|
||||
if (encryptExpiry < expiry) expiry = encryptExpiry;
|
||||
}
|
||||
if (capabilities === 'sign' || capabilities === 'encrypt_sign') {
|
||||
const signKey =
|
||||
await this.getSigningKey(keyId, expiry, userId) ||
|
||||
await this.getSigningKey(keyId, null, userId);
|
||||
await this.getSigningKey(keyId, expiry, userId).catch(() => {}) ||
|
||||
await this.getSigningKey(keyId, null, userId).catch(() => {});
|
||||
if (!signKey) return null;
|
||||
const signExpiry = await signKey.getExpirationTime(this.keyPacket);
|
||||
if (signExpiry < expiry) expiry = signExpiry;
|
||||
|
@ -531,24 +529,29 @@ Key.prototype.getExpirationTime = async function(capabilities, keyId, userId) {
|
|||
Key.prototype.getPrimaryUser = async function(date = new Date(), userId = {}) {
|
||||
const primaryKey = this.keyPacket;
|
||||
const users = [];
|
||||
let exception;
|
||||
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 helper.getLatestValidSignature(user.selfCertifications, primaryKey, enums.signature.cert_generic, dataToVerify, date);
|
||||
if (!selfCertification) continue;
|
||||
users.push({ index: i, user, selfCertification });
|
||||
try {
|
||||
const user = this.users[i];
|
||||
if (!user.userId) {
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
(userId.name !== undefined && user.userId.name !== userId.name) ||
|
||||
(userId.email !== undefined && user.userId.email !== userId.email) ||
|
||||
(userId.comment !== undefined && user.userId.comment !== userId.comment)
|
||||
) {
|
||||
throw new Error('Could not find user that matches that user ID');
|
||||
}
|
||||
const dataToVerify = { userId: user.userId, key: primaryKey };
|
||||
const selfCertification = await helper.getLatestValidSignature(user.selfCertifications, primaryKey, enums.signature.cert_generic, dataToVerify, date);
|
||||
users.push({ index: i, user, selfCertification });
|
||||
} catch (e) {
|
||||
exception = e;
|
||||
}
|
||||
}
|
||||
if (!users.length) {
|
||||
if (userId.name !== undefined || userId.email !== undefined ||
|
||||
userId.comment !== undefined) {
|
||||
throw new Error('Could not find user that matches that user ID');
|
||||
}
|
||||
return null;
|
||||
throw exception || new Error('Could not find primary user');
|
||||
}
|
||||
await Promise.all(users.map(async function (a) {
|
||||
return a.user.revoked || a.user.isRevoked(primaryKey, a.selfCertification, null, date);
|
||||
|
@ -561,7 +564,7 @@ Key.prototype.getPrimaryUser = async function(date = new Date(), userId = {}) {
|
|||
}).pop();
|
||||
const { user, selfCertification: cert } = primaryUser;
|
||||
if (cert.revoked || await user.isRevoked(primaryKey, cert, null, date)) {
|
||||
return null;
|
||||
throw new Error('Primary user is revoked');
|
||||
}
|
||||
return primaryUser;
|
||||
};
|
||||
|
@ -578,9 +581,6 @@ Key.prototype.getPrimaryUser = async function(date = new Date(), userId = {}) {
|
|||
* @async
|
||||
*/
|
||||
Key.prototype.update = async function(key) {
|
||||
if (await key.verifyPrimaryKey() === enums.keyStatus.invalid) {
|
||||
return;
|
||||
}
|
||||
if (!this.hasSameFingerprintAs(key)) {
|
||||
throw new Error('Key update method: fingerprints of keys not equal');
|
||||
}
|
||||
|
@ -670,11 +670,9 @@ Key.prototype.revoke = async function({
|
|||
Key.prototype.getRevocationCertificate = async function() {
|
||||
const dataToVerify = { key: this.keyPacket };
|
||||
const revocationSignature = await helper.getLatestValidSignature(this.revocationSignatures, this.keyPacket, enums.signature.key_revocation, dataToVerify);
|
||||
if (revocationSignature) {
|
||||
const packetlist = new packet.List();
|
||||
packetlist.push(revocationSignature);
|
||||
return armor.encode(enums.armor.public_key, packetlist.write(), null, null, 'This is a revocation certificate');
|
||||
}
|
||||
const packetlist = new packet.List();
|
||||
packetlist.push(revocationSignature);
|
||||
return armor.encode(enums.armor.public_key, packetlist.write(), null, null, 'This is a revocation certificate');
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -699,8 +697,10 @@ Key.prototype.applyRevocationCertificate = async function(revocationCertificate)
|
|||
if (revocationSignature.isExpired()) {
|
||||
throw new Error('Revocation signature is expired');
|
||||
}
|
||||
if (!await revocationSignature.verify(this.keyPacket, enums.signature.key_revocation, { key: this.keyPacket })) {
|
||||
throw new Error('Could not verify revocation signature');
|
||||
try {
|
||||
await revocationSignature.verify(this.keyPacket, enums.signature.key_revocation, { key: this.keyPacket });
|
||||
} catch (e) {
|
||||
throw util.wrapError('Could not verify revocation signature', e);
|
||||
}
|
||||
const key = new Key(this.toPacketlist());
|
||||
key.revocationSignatures.push(revocationSignature);
|
||||
|
@ -716,10 +716,7 @@ Key.prototype.applyRevocationCertificate = async function(revocationCertificate)
|
|||
* @async
|
||||
*/
|
||||
Key.prototype.signPrimaryUser = async function(privateKeys, date, userId) {
|
||||
const { index, user } = await this.getPrimaryUser(date, userId) || {};
|
||||
if (!user) {
|
||||
throw new Error('Could not find primary user');
|
||||
}
|
||||
const { index, user } = await this.getPrimaryUser(date, userId);
|
||||
const userSign = await user.sign(this.keyPacket, privateKeys);
|
||||
const key = new Key(this.toPacketlist());
|
||||
key.users[index] = userSign;
|
||||
|
@ -754,12 +751,9 @@ Key.prototype.signAllUsers = async function(privateKeys) {
|
|||
*/
|
||||
Key.prototype.verifyPrimaryUser = async function(keys, date, userId) {
|
||||
const primaryKey = this.keyPacket;
|
||||
const { user } = await this.getPrimaryUser(date, userId) || {};
|
||||
if (!user) {
|
||||
throw new Error('Could not find primary user');
|
||||
}
|
||||
const { user } = await this.getPrimaryUser(date, userId);
|
||||
const results = keys ? await user.verifyAllCertifications(primaryKey, keys) :
|
||||
[{ keyid: primaryKey.keyid, valid: await user.verify(primaryKey) === enums.keyStatus.valid }];
|
||||
[{ keyid: primaryKey.keyid, valid: await user.verify(primaryKey).catch(() => false) }];
|
||||
return results;
|
||||
};
|
||||
|
||||
|
@ -778,7 +772,7 @@ Key.prototype.verifyAllUsers = async function(keys) {
|
|||
const primaryKey = this.keyPacket;
|
||||
await Promise.all(this.users.map(async function(user) {
|
||||
const signatures = keys ? await user.verifyAllCertifications(primaryKey, keys) :
|
||||
[{ keyid: primaryKey.keyid, valid: await user.verify(primaryKey) === enums.keyStatus.valid }];
|
||||
[{ keyid: primaryKey.keyid, valid: await user.verify(primaryKey).catch(() => false) }];
|
||||
signatures.forEach(signature => {
|
||||
results.push({
|
||||
userid: user.userId.userid,
|
||||
|
|
|
@ -65,31 +65,25 @@ SubKey.prototype.isRevoked = async function(primaryKey, signature, key, date = n
|
|||
|
||||
/**
|
||||
* Verify subkey. Checks for revocation signatures, expiration time
|
||||
* and valid binding signature
|
||||
* and valid binding signature. Throws 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 {Promise<module:enums.keyStatus>} The status of the subkey
|
||||
* @returns {Promise<true>} The status of the subkey
|
||||
* @async
|
||||
*/
|
||||
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 = await helper.getLatestValidSignature(this.bindingSignatures, primaryKey, enums.signature.subkey_binding, dataToVerify, date);
|
||||
// check binding signature is verified
|
||||
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;
|
||||
if (bindingSignature.revoked || await this.isRevoked(primaryKey, bindingSignature, null, date)) {
|
||||
throw new Error('Subkey is revoked');
|
||||
}
|
||||
// check for expiration time
|
||||
if (helper.isDataExpired(this.keyPacket, bindingSignature, date)) {
|
||||
return enums.keyStatus.expired;
|
||||
throw new Error('Subkey is expired');
|
||||
}
|
||||
return enums.keyStatus.valid; // binding signature passed all checks
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -103,8 +97,12 @@ SubKey.prototype.verify = async function(primaryKey, date = new Date()) {
|
|||
*/
|
||||
SubKey.prototype.getExpirationTime = async function(primaryKey, date = new Date()) {
|
||||
const dataToVerify = { key: primaryKey, bind: this.keyPacket };
|
||||
const bindingSignature = await helper.getLatestValidSignature(this.bindingSignatures, primaryKey, enums.signature.subkey_binding, dataToVerify, date);
|
||||
if (!bindingSignature) return null;
|
||||
let bindingSignature;
|
||||
try {
|
||||
bindingSignature = await helper.getLatestValidSignature(this.bindingSignatures, primaryKey, enums.signature.subkey_binding, dataToVerify, date);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
const keyExpiry = helper.getExpirationTime(this.keyPacket, bindingSignature);
|
||||
const sigExpiry = bindingSignature.getExpirationTime();
|
||||
return keyExpiry < sigExpiry ? keyExpiry : sigExpiry;
|
||||
|
@ -119,9 +117,6 @@ SubKey.prototype.getExpirationTime = async function(primaryKey, date = new Date(
|
|||
* @async
|
||||
*/
|
||||
SubKey.prototype.update = async function(subKey, primaryKey) {
|
||||
if (await subKey.verify(primaryKey) === enums.keyStatus.invalid) {
|
||||
return;
|
||||
}
|
||||
if (!this.hasSameFingerprintAs(subKey)) {
|
||||
throw new Error('SubKey update method: fingerprints of subkeys not equal');
|
||||
}
|
||||
|
@ -134,9 +129,6 @@ SubKey.prototype.update = async function(subKey, primaryKey) {
|
|||
const that = this;
|
||||
const dataToVerify = { key: primaryKey, bind: that.keyPacket };
|
||||
await helper.mergeSignatures(subKey, this, 'bindingSignatures', async function(srcBindSig) {
|
||||
if (!(srcBindSig.verified || await srcBindSig.verify(primaryKey, enums.signature.subkey_binding, dataToVerify))) {
|
||||
return false;
|
||||
}
|
||||
for (let i = 0; i < that.bindingSignatures.length; i++) {
|
||||
if (that.bindingSignatures[i].issuerKeyId.equals(srcBindSig.issuerKeyId)) {
|
||||
if (srcBindSig.created > that.bindingSignatures[i].created) {
|
||||
|
@ -145,7 +137,11 @@ SubKey.prototype.update = async function(subKey, primaryKey) {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
try {
|
||||
return srcBindSig.verified || await srcBindSig.verify(primaryKey, enums.signature.subkey_binding, dataToVerify);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
// revocation signatures
|
||||
await helper.mergeSignatures(subKey, this, 'revocationSignatures', function(srcRevSig) {
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
/**
|
||||
* @requires enums
|
||||
* @requires util
|
||||
* @requires packet
|
||||
* @requires key/helper
|
||||
* @module key/User
|
||||
*/
|
||||
|
||||
import packet from '../packet';
|
||||
import enums from '../enums';
|
||||
import util from '../util';
|
||||
import packet from '../packet';
|
||||
import { mergeSignatures, isDataRevoked, createSignaturePacket } from './helper';
|
||||
|
||||
/**
|
||||
|
@ -60,10 +62,6 @@ User.prototype.sign = async function(primaryKey, privateKeys) {
|
|||
throw new Error('Not implemented for self signing');
|
||||
}
|
||||
const signingKey = await privateKey.getSigningKey();
|
||||
if (!signingKey) {
|
||||
throw new Error('Could not find valid signing key packet in key ' +
|
||||
privateKey.getKeyId().toHex());
|
||||
}
|
||||
return createSignaturePacket(dataToSign, privateKey, signingKey.keyPacket, {
|
||||
// Most OpenPGP implementations use generic certification (0x10)
|
||||
signatureType: enums.signature.cert_generic,
|
||||
|
@ -99,13 +97,13 @@ User.prototype.isRevoked = async function(primaryKey, certificate, key, date = n
|
|||
|
||||
|
||||
/**
|
||||
* Verifies the user certificate
|
||||
* Verifies the user certificate. Throws if the user certificate is invalid.
|
||||
* @param {module:packet.SecretKey|
|
||||
* module:packet.PublicKey} primaryKey The primary key packet
|
||||
* @param {module:packet.Signature} certificate A certificate of this user
|
||||
* @param {Array<module:key.Key>} keys Array of keys to verify certificate signatures
|
||||
* @param {Date} date Use the given date instead of the current time
|
||||
* @returns {Promise<module:enums.keyStatus>} status of the certificate
|
||||
* @returns {Promise<true>} status of the certificate
|
||||
* @async
|
||||
*/
|
||||
User.prototype.verifyCertificate = async function(primaryKey, certificate, keys, date = new Date()) {
|
||||
|
@ -117,20 +115,24 @@ User.prototype.verifyCertificate = async function(primaryKey, certificate, keys,
|
|||
key: primaryKey
|
||||
};
|
||||
const results = await Promise.all(keys.map(async function(key) {
|
||||
if (!key.getKeyIds().some(id => id.equals(keyid))) { return; }
|
||||
if (!key.getKeyIds().some(id => id.equals(keyid))) {
|
||||
return null;
|
||||
}
|
||||
const signingKey = await key.getSigningKey(keyid, date);
|
||||
if (certificate.revoked || await that.isRevoked(primaryKey, certificate, signingKey.keyPacket, date)) {
|
||||
return enums.keyStatus.revoked;
|
||||
throw new Error('User certificate is revoked');
|
||||
}
|
||||
if (!(certificate.verified || await certificate.verify(signingKey.keyPacket, enums.signature.cert_generic, dataToVerify))) {
|
||||
return enums.keyStatus.invalid;
|
||||
try {
|
||||
certificate.verified || await certificate.verify(signingKey.keyPacket, enums.signature.cert_generic, dataToVerify);
|
||||
} catch (e) {
|
||||
throw util.wrapError('User certificate is invalid', e);
|
||||
}
|
||||
if (certificate.isExpired(date)) {
|
||||
return enums.keyStatus.expired;
|
||||
throw new Error('User certificate is expired');
|
||||
}
|
||||
return enums.keyStatus.valid;
|
||||
return true;
|
||||
}));
|
||||
return results.find(result => result !== undefined);
|
||||
return results.find(result => result !== null) || null;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -147,26 +149,25 @@ User.prototype.verifyAllCertifications = async function(primaryKey, keys, date =
|
|||
const that = this;
|
||||
const certifications = this.selfCertifications.concat(this.otherCertifications);
|
||||
return Promise.all(certifications.map(async function(certification) {
|
||||
const status = await that.verifyCertificate(primaryKey, certification, keys, date);
|
||||
return {
|
||||
keyid: certification.issuerKeyId,
|
||||
valid: status === undefined ? null : status === enums.keyStatus.valid
|
||||
valid: await that.verifyCertificate(primaryKey, certification, keys, date).catch(() => false)
|
||||
};
|
||||
}));
|
||||
};
|
||||
|
||||
/**
|
||||
* Verify User. Checks for existence of self signatures, revocation signatures
|
||||
* and validity of self signature
|
||||
* and validity of self signature. Throws when there are no valid self signatures.
|
||||
* @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 {Promise<module:enums.keyStatus>} Status of user
|
||||
* @returns {Promise<true>} Status of user
|
||||
* @async
|
||||
*/
|
||||
User.prototype.verify = async function(primaryKey, date = new Date()) {
|
||||
if (!this.selfCertifications.length) {
|
||||
return enums.keyStatus.no_self_cert;
|
||||
throw new Error('No self-certifications');
|
||||
}
|
||||
const that = this;
|
||||
const dataToVerify = {
|
||||
|
@ -175,21 +176,27 @@ User.prototype.verify = async function(primaryKey, date = new Date()) {
|
|||
key: primaryKey
|
||||
};
|
||||
// TODO replace when Promise.some or Promise.any are implemented
|
||||
const results = [enums.keyStatus.invalid].concat(
|
||||
await Promise.all(this.selfCertifications.map(async function(selfCertification) {
|
||||
let exception;
|
||||
for (let i = this.selfCertifications.length - 1; i >= 0; i--) {
|
||||
try {
|
||||
const selfCertification = this.selfCertifications[i];
|
||||
if (selfCertification.revoked || await that.isRevoked(primaryKey, selfCertification, undefined, date)) {
|
||||
return enums.keyStatus.revoked;
|
||||
throw new Error('Self-certification is revoked');
|
||||
}
|
||||
if (!(selfCertification.verified || await selfCertification.verify(primaryKey, enums.signature.cert_generic, dataToVerify))) {
|
||||
return enums.keyStatus.invalid;
|
||||
try {
|
||||
selfCertification.verified || await selfCertification.verify(primaryKey, enums.signature.cert_generic, dataToVerify);
|
||||
} catch (e) {
|
||||
throw util.wrapError('Self-certification is invalid', e);
|
||||
}
|
||||
if (selfCertification.isExpired(date)) {
|
||||
return enums.keyStatus.expired;
|
||||
throw new Error('Self-certification is expired');
|
||||
}
|
||||
return enums.keyStatus.valid;
|
||||
})));
|
||||
return results.some(status => status === enums.keyStatus.valid) ?
|
||||
enums.keyStatus.valid : results.pop();
|
||||
return true;
|
||||
} catch (e) {
|
||||
exception = e;
|
||||
}
|
||||
}
|
||||
throw exception;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -208,7 +215,11 @@ User.prototype.update = async function(user, primaryKey) {
|
|||
};
|
||||
// self signatures
|
||||
await mergeSignatures(user, this, 'selfCertifications', async function(srcSelfSig) {
|
||||
return srcSelfSig.verified || srcSelfSig.verify(primaryKey, enums.signature.cert_generic, dataToVerify);
|
||||
try {
|
||||
return srcSelfSig.verified || srcSelfSig.verify(primaryKey, enums.signature.cert_generic, dataToVerify);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
// other signatures
|
||||
await mergeSignatures(user, this, 'otherCertifications');
|
||||
|
|
|
@ -183,16 +183,18 @@ Message.prototype.decryptSessionKeys = async function(privateKeys, passwords) {
|
|||
}
|
||||
await Promise.all(pkESKeyPacketlist.map(async function(keyPacket) {
|
||||
await Promise.all(privateKeys.map(async function(privateKey) {
|
||||
const primaryUser = await privateKey.getPrimaryUser(); // TODO: Pass userId from somewhere.
|
||||
let algos = [
|
||||
enums.symmetric.aes256, // Old OpenPGP.js default fallback
|
||||
enums.symmetric.aes128, // RFC4880bis fallback
|
||||
enums.symmetric.tripledes, // RFC4880 fallback
|
||||
enums.symmetric.cast5 // Golang OpenPGP fallback
|
||||
];
|
||||
if (primaryUser && primaryUser.selfCertification.preferredSymmetricAlgorithms) {
|
||||
algos = algos.concat(primaryUser.selfCertification.preferredSymmetricAlgorithms);
|
||||
}
|
||||
try {
|
||||
const primaryUser = await privateKey.getPrimaryUser(); // TODO: Pass userId from somewhere.
|
||||
if (primaryUser.selfCertification.preferredSymmetricAlgorithms) {
|
||||
algos = algos.concat(primaryUser.selfCertification.preferredSymmetricAlgorithms);
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
const privateKeyPackets = privateKey.getKeys(keyPacket.publicKeyId).map(key => key.keyPacket);
|
||||
await Promise.all(privateKeyPackets.map(async function(privateKeyPacket) {
|
||||
|
@ -358,10 +360,6 @@ export async function encryptSessionKey(sessionKey, symAlgo, aeadAlgo, publicKey
|
|||
if (publicKeys) {
|
||||
const results = await Promise.all(publicKeys.map(async function(publicKey) {
|
||||
const encryptionKey = await publicKey.getEncryptionKey(undefined, date, userIds);
|
||||
if (!encryptionKey) {
|
||||
throw new Error('Could not find valid key packet for encryption in key ' +
|
||||
publicKey.getKeyId().toHex());
|
||||
}
|
||||
const pkESKeyPacket = new packet.PublicKeyEncryptedSessionKey();
|
||||
pkESKeyPacket.publicKeyId = wildcard ? type_keyid.wildcard() : encryptionKey.getKeyId();
|
||||
pkESKeyPacket.publicKeyAlgorithm = encryptionKey.keyPacket.algorithm;
|
||||
|
@ -457,10 +455,6 @@ Message.prototype.sign = async function(privateKeys = [], signature = null, date
|
|||
throw new Error('Need private key for signing');
|
||||
}
|
||||
const signingKey = await privateKey.getSigningKey(undefined, date, userIds);
|
||||
if (!signingKey) {
|
||||
throw new Error('Could not find valid key packet for signing in key ' +
|
||||
privateKey.getKeyId().toHex());
|
||||
}
|
||||
const onePassSig = new packet.OnePassSignature();
|
||||
onePassSig.signatureType = signatureType;
|
||||
onePassSig.hashAlgorithm = await getPreferredHashAlgo(privateKey, signingKey.keyPacket, date, userIds);
|
||||
|
@ -543,10 +537,6 @@ export async function createSignaturePackets(literalDataPacket, privateKeys, sig
|
|||
throw new Error('Need private key for signing');
|
||||
}
|
||||
const signingKey = await privateKey.getSigningKey(undefined, date, userId);
|
||||
if (!signingKey) {
|
||||
throw new Error(`Could not find valid signing key packet in key ${
|
||||
privateKey.getKeyId().toHex()}`);
|
||||
}
|
||||
return createSignaturePacket(literalDataPacket, privateKey, signingKey.keyPacket, { signatureType }, date, userId, detached, streaming);
|
||||
})).then(signatureList => {
|
||||
signatureList.forEach(signaturePacket => packetlist.push(signaturePacket));
|
||||
|
@ -646,11 +636,10 @@ async function createVerificationObject(signature, literalDataList, keys, date =
|
|||
let signingKey = null;
|
||||
await Promise.all(keys.map(async function(key) {
|
||||
// Look for the unique key that matches issuerKeyId of signature
|
||||
const result = await key.getSigningKey(signature.issuerKeyId, null);
|
||||
if (result) {
|
||||
try {
|
||||
signingKey = await key.getSigningKey(signature.issuerKeyId, null);
|
||||
primaryKey = key;
|
||||
signingKey = result;
|
||||
}
|
||||
} catch (e) {}
|
||||
}));
|
||||
|
||||
const signaturePacket = signature.correspondingSig || signature;
|
||||
|
@ -669,7 +658,7 @@ async function createVerificationObject(signature, literalDataList, keys, date =
|
|||
signingKey.getExpirationTime(primaryKey, date)
|
||||
)
|
||||
)) {
|
||||
return null;
|
||||
throw new Error('Signature is expired');
|
||||
}
|
||||
return verified;
|
||||
})(),
|
||||
|
|
|
@ -683,7 +683,7 @@ async function prepareSignatures(signatures) {
|
|||
try {
|
||||
signature.valid = await signature.verified;
|
||||
} catch (e) {
|
||||
signature.valid = null;
|
||||
signature.valid = false;
|
||||
signature.error = e;
|
||||
util.print_debug_error(e);
|
||||
}
|
||||
|
|
|
@ -86,9 +86,11 @@ function verificationObjectToClone(verObject) {
|
|||
const packets = (await signature).packets;
|
||||
try {
|
||||
await verified;
|
||||
} catch (e) {}
|
||||
if (packets && packets[0]) {
|
||||
delete packets[0].signature;
|
||||
delete packets[0].hashed;
|
||||
} catch (e) {}
|
||||
}
|
||||
return packets;
|
||||
});
|
||||
} else {
|
||||
|
|
|
@ -706,46 +706,55 @@ Signature.prototype.verify = async function (key, signatureType, data, detached
|
|||
hash = await this.hash(signatureType, data, toHash);
|
||||
}
|
||||
hash = await stream.readToEnd(hash);
|
||||
let verified;
|
||||
if (this.signedHashValue[0] !== hash[0] ||
|
||||
this.signedHashValue[1] !== hash[1]) {
|
||||
verified = false;
|
||||
} else {
|
||||
let mpicount = 0;
|
||||
// Algorithm-Specific Fields for RSA signatures:
|
||||
// - multiprecision number (MPI) of RSA signature value m**d mod n.
|
||||
if (publicKeyAlgorithm > 0 && publicKeyAlgorithm < 4) {
|
||||
mpicount = 1;
|
||||
|
||||
// Algorithm-Specific Fields for DSA, ECDSA, and EdDSA signatures:
|
||||
// - MPI of DSA value r.
|
||||
// - MPI of DSA value s.
|
||||
} else if (publicKeyAlgorithm === enums.publicKey.dsa ||
|
||||
publicKeyAlgorithm === enums.publicKey.ecdsa ||
|
||||
publicKeyAlgorithm === enums.publicKey.eddsa) {
|
||||
mpicount = 2;
|
||||
}
|
||||
|
||||
// EdDSA signature parameters are encoded in little-endian format
|
||||
// https://tools.ietf.org/html/rfc8032#section-5.1.2
|
||||
const endian = publicKeyAlgorithm === enums.publicKey.eddsa ? 'le' : 'be';
|
||||
const mpi = [];
|
||||
let i = 0;
|
||||
this.signature = await stream.readToEnd(this.signature);
|
||||
for (let j = 0; j < mpicount; j++) {
|
||||
mpi[j] = new type_mpi();
|
||||
i += mpi[j].read(this.signature.subarray(i, this.signature.length), endian);
|
||||
}
|
||||
verified = await crypto.signature.verify(
|
||||
publicKeyAlgorithm, hashAlgorithm, mpi, key.params,
|
||||
toHash, hash
|
||||
);
|
||||
if (verified && this.revocationKeyClass !== null) {
|
||||
throw new Error('This key is intended to be revoked with an authorized key, which OpenPGP.js does not support.');
|
||||
}
|
||||
throw new Error('Message digest did not match');
|
||||
}
|
||||
this.verified = verified;
|
||||
return verified;
|
||||
|
||||
let mpicount = 0;
|
||||
// Algorithm-Specific Fields for RSA signatures:
|
||||
// - multiprecision number (MPI) of RSA signature value m**d mod n.
|
||||
if (publicKeyAlgorithm > 0 && publicKeyAlgorithm < 4) {
|
||||
mpicount = 1;
|
||||
|
||||
// Algorithm-Specific Fields for DSA, ECDSA, and EdDSA signatures:
|
||||
// - MPI of DSA value r.
|
||||
// - MPI of DSA value s.
|
||||
} else if (publicKeyAlgorithm === enums.publicKey.dsa ||
|
||||
publicKeyAlgorithm === enums.publicKey.ecdsa ||
|
||||
publicKeyAlgorithm === enums.publicKey.eddsa) {
|
||||
mpicount = 2;
|
||||
}
|
||||
|
||||
// EdDSA signature parameters are encoded in little-endian format
|
||||
// https://tools.ietf.org/html/rfc8032#section-5.1.2
|
||||
const endian = publicKeyAlgorithm === enums.publicKey.eddsa ? 'le' : 'be';
|
||||
const mpi = [];
|
||||
let i = 0;
|
||||
this.signature = await stream.readToEnd(this.signature);
|
||||
for (let j = 0; j < mpicount; j++) {
|
||||
mpi[j] = new type_mpi();
|
||||
i += mpi[j].read(this.signature.subarray(i, this.signature.length), endian);
|
||||
}
|
||||
const verified = await crypto.signature.verify(
|
||||
publicKeyAlgorithm, hashAlgorithm, mpi, key.params,
|
||||
toHash, hash
|
||||
);
|
||||
if (!verified) {
|
||||
throw new Error('Signature verification failed');
|
||||
}
|
||||
if (config.reject_hash_algorithms.has(hashAlgorithm)) {
|
||||
throw new Error('Insecure hash algorithm: ' + enums.read(enums.hash, hashAlgorithm).toUpperCase());
|
||||
}
|
||||
if (config.reject_message_hash_algorithms.has(hashAlgorithm) &&
|
||||
[enums.signature.binary, enums.signature.text].includes(this.signatureType)) {
|
||||
throw new Error('Insecure message hash algorithm: ' + enums.read(enums.hash, hashAlgorithm).toUpperCase());
|
||||
}
|
||||
if (this.revocationKeyClass !== null) {
|
||||
throw new Error('This key is intended to be revoked with an authorized key, which OpenPGP.js does not support.');
|
||||
}
|
||||
this.verified = true;
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
13
src/util.js
13
src/util.js
|
@ -753,5 +753,18 @@ export default {
|
|||
result += ALPHABET[MASK & (buffer >> bitsLeft)];
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
wrapError: function(message, error) {
|
||||
if (!error) {
|
||||
return new Error(message);
|
||||
}
|
||||
|
||||
// update error message
|
||||
try {
|
||||
error.message = message + ': ' + error.message;
|
||||
} catch (e) {}
|
||||
|
||||
return error;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -2310,13 +2310,11 @@ function versionSpecificTests() {
|
|||
const opt = {numBits: 512, userIds: 'test1 <a@b.com>', passphrase: '1234'};
|
||||
if (openpgp.util.getWebCryptoAll()) { opt.numBits = 2048; } // webkit webcrypto accepts minimum 2048 bit keys
|
||||
return openpgp.generateKey(opt).then(function(original) {
|
||||
return openpgp.revokeKey({key: original.key.toPublic(), revocationCertificate: original.revocationCertificate}).then(function(revKey) {
|
||||
return openpgp.revokeKey({key: original.key.toPublic(), revocationCertificate: original.revocationCertificate}).then(async function(revKey) {
|
||||
revKey = revKey.publicKey;
|
||||
expect(revKey.revocationSignatures[0].reasonForRevocationFlag).to.equal(openpgp.enums.reasonForRevocation.no_reason);
|
||||
expect(revKey.revocationSignatures[0].reasonForRevocationString).to.equal('');
|
||||
return revKey.verifyPrimaryKey().then(function(status) {
|
||||
expect(status).to.equal(openpgp.enums.keyStatus.revoked);
|
||||
});
|
||||
await expect(revKey.verifyPrimaryKey()).to.be.rejectedWith('Primary key is revoked');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -2326,13 +2324,11 @@ function versionSpecificTests() {
|
|||
if (openpgp.util.getWebCryptoAll()) { opt.numBits = 2048; } // webkit webcrypto accepts minimum 2048 bit keys
|
||||
return openpgp.generateKey(opt).then(async function(original) {
|
||||
await original.key.decrypt('1234');
|
||||
return openpgp.revokeKey({key: original.key, reasonForRevocation: {string: 'Testing key revocation'}}).then(function(revKey) {
|
||||
return openpgp.revokeKey({key: original.key, reasonForRevocation: {string: 'Testing key revocation'}}).then(async function(revKey) {
|
||||
revKey = revKey.publicKey;
|
||||
expect(revKey.revocationSignatures[0].reasonForRevocationFlag).to.equal(openpgp.enums.reasonForRevocation.no_reason);
|
||||
expect(revKey.revocationSignatures[0].reasonForRevocationString).to.equal('Testing key revocation');
|
||||
return revKey.verifyPrimaryKey().then(function(status) {
|
||||
expect(status).to.equal(openpgp.enums.keyStatus.revoked);
|
||||
});
|
||||
await expect(revKey.verifyPrimaryKey()).to.be.rejectedWith('Primary key is revoked');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -2346,7 +2342,7 @@ function versionSpecificTests() {
|
|||
const { keys: [key] } = await openpgp.key.readArmored(v5_sample_key);
|
||||
expect(key.primaryKey.getFingerprint()).to.equal('19347bc9872464025f99df3ec2e0000ed9884892e1f7b3ea4c94009159569b54');
|
||||
expect(key.subKeys[0].getFingerprint()).to.equal('e4557c2b02ffbf4b04f87401ec336af7133d0f85be7fd09baefd9caeb8c93965');
|
||||
expect(await key.verifyPrimaryKey()).to.equal(openpgp.enums.keyStatus.valid);
|
||||
await key.verifyPrimaryKey();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -2480,7 +2476,7 @@ describe('Key', function() {
|
|||
|
||||
it('Verify status of revoked primary key', async function() {
|
||||
const pubKey = (await openpgp.key.readArmored(pub_revoked_subkeys)).keys[0];
|
||||
expect(pubKey.verifyPrimaryKey()).to.eventually.equal(openpgp.enums.keyStatus.revoked);
|
||||
await expect(pubKey.verifyPrimaryKey()).to.be.rejectedWith('Primary key is revoked');
|
||||
});
|
||||
|
||||
it('Verify status of revoked subkey', async function() {
|
||||
|
@ -2496,7 +2492,7 @@ describe('Key', function() {
|
|||
|
||||
await expect(pubKey.subKeys[0].verify(
|
||||
pubKey.primaryKey
|
||||
)).to.eventually.equal(openpgp.enums.keyStatus.revoked);
|
||||
)).to.be.rejectedWith('Subkey is revoked');
|
||||
});
|
||||
|
||||
it('Verify status of key with non-self revocation signature', async function() {
|
||||
|
@ -2516,16 +2512,16 @@ describe('Key', function() {
|
|||
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);
|
||||
await expect(user.verifyCertificate(pubKey.primaryKey, user.otherCertifications[0], [certifyingKey])).to.be.rejectedWith('User certificate is revoked');
|
||||
});
|
||||
|
||||
it('Verify certificate of key with future creation date', async function() {
|
||||
const { keys: [pubKey] } = await openpgp.key.readArmored(key_created_2030);
|
||||
const user = pubKey.users[0];
|
||||
expect(await user.verifyCertificate(pubKey.primaryKey, user.selfCertifications[0], [pubKey], pubKey.primaryKey.created)).to.equal(openpgp.enums.keyStatus.valid);
|
||||
await user.verifyCertificate(pubKey.primaryKey, user.selfCertifications[0], [pubKey], pubKey.primaryKey.created);
|
||||
const verifyAllResult = await user.verifyAllCertifications(pubKey.primaryKey, [pubKey], pubKey.primaryKey.created);
|
||||
expect(verifyAllResult[0].valid).to.be.true;
|
||||
expect(await user.verify(pubKey.primaryKey, pubKey.primaryKey.created)).to.equal(openpgp.enums.keyStatus.valid);
|
||||
await user.verify(pubKey.primaryKey, pubKey.primaryKey.created);
|
||||
});
|
||||
|
||||
it('Evaluate key flags to find valid encryption key packet', async function() {
|
||||
|
@ -2538,8 +2534,7 @@ describe('Key', function() {
|
|||
// remove subkeys
|
||||
pubKey.subKeys = [];
|
||||
// primary key has only key flags for signing
|
||||
const encryptionKey = await pubKey.getEncryptionKey();
|
||||
expect(encryptionKey).to.not.exist;
|
||||
await expect(pubKey.getEncryptionKey()).to.be.rejectedWith('Could not find valid encryption key packet in key c076e634d32b498d');
|
||||
});
|
||||
|
||||
it('Method getExpirationTime V4 Key', async function() {
|
||||
|
@ -2587,15 +2582,15 @@ describe('Key', function() {
|
|||
expect(encryptExpirationTime).to.equal(Infinity);
|
||||
});
|
||||
|
||||
it('validate() - return true if key parameters correspond', async function() {
|
||||
it("validate() - don't throw if key parameters correspond", async function() {
|
||||
const { key } = await openpgp.generateKey({ userIds: {}, curve: 'ed25519' });
|
||||
expect(await key.validate()).to.be.true;
|
||||
await key.validate();
|
||||
});
|
||||
|
||||
it('validate() - return false if key parameters do not correspond', async function() {
|
||||
it("validate() - throw if key parameters don't correspond", async function() {
|
||||
const { keys: [key] } = await openpgp.key.readArmored(mismatchingKeyParams);
|
||||
await key.decrypt('userpass');
|
||||
expect(await key.validate()).to.be.false;
|
||||
await expect(key.validate()).to.be.rejectedWith('Signature verification failed');
|
||||
});
|
||||
|
||||
it('clearPrivateParams() - check that private key can no longer be used', async function() {
|
||||
|
@ -2625,7 +2620,7 @@ describe('Key', function() {
|
|||
const use_nativeVal = openpgp.config.use_native;
|
||||
openpgp.config.use_native = false;
|
||||
try {
|
||||
expect(await key.validate()).to.be.false;
|
||||
await expect(key.validate()).to.be.rejectedWith('Signature verification failed');
|
||||
} finally {
|
||||
openpgp.config.use_native = use_nativeVal;
|
||||
}
|
||||
|
@ -2743,7 +2738,7 @@ describe('Key', function() {
|
|||
source.subKeys = [];
|
||||
expect(dest.subKeys).to.exist;
|
||||
expect(dest.isPublic()).to.be.true;
|
||||
await expect(dest.update.bind(dest, source)())
|
||||
await expect(dest.update(source))
|
||||
.to.be.rejectedWith('Cannot update public key with private key if subkey mismatch');
|
||||
});
|
||||
|
||||
|
@ -2751,14 +2746,11 @@ describe('Key', function() {
|
|||
const source = (await openpgp.key.readArmored(pgp_desktop_pub)).keys[0];
|
||||
const dest = (await openpgp.key.readArmored(pgp_desktop_priv)).keys[0];
|
||||
expect(source.subKeys[0].bindingSignatures[0]).to.exist;
|
||||
await expect(source.subKeys[0].verify(source.primaryKey))
|
||||
.to.eventually.equal(openpgp.enums.keyStatus.valid);
|
||||
await source.subKeys[0].verify(source.primaryKey);
|
||||
expect(dest.subKeys[0].bindingSignatures[0]).to.not.exist;
|
||||
return dest.update(source).then(async () => {
|
||||
expect(dest.subKeys[0].bindingSignatures[0]).to.exist;
|
||||
await expect(dest.subKeys[0].verify(source.primaryKey))
|
||||
.to.eventually.equal(openpgp.enums.keyStatus.valid);
|
||||
});
|
||||
await dest.update(source);
|
||||
expect(dest.subKeys[0].bindingSignatures[0]).to.exist;
|
||||
await dest.subKeys[0].verify(source.primaryKey);
|
||||
});
|
||||
|
||||
it('update() - merge multiple subkey binding signatures', async function() {
|
||||
|
@ -2788,8 +2780,8 @@ describe('Key', function() {
|
|||
expect(revKey.revocationSignatures[0].reasonForRevocationFlag).to.equal(openpgp.enums.reasonForRevocation.key_retired);
|
||||
expect(revKey.revocationSignatures[0].reasonForRevocationString).to.equal('Testing key revocation');
|
||||
|
||||
expect(await privKey.verifyPrimaryKey()).to.equal(openpgp.enums.keyStatus.valid);
|
||||
expect(await revKey.verifyPrimaryKey()).to.equal(openpgp.enums.keyStatus.revoked);
|
||||
await privKey.verifyPrimaryKey();
|
||||
await expect(revKey.verifyPrimaryKey()).to.be.rejectedWith('Primary key is revoked');
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -2807,8 +2799,8 @@ describe('Key', function() {
|
|||
expect(revKey.revocationSignatures[0].reasonForRevocationFlag).to.equal(openpgp.enums.reasonForRevocation.key_superseded);
|
||||
expect(revKey.revocationSignatures[0].reasonForRevocationString).to.equal('');
|
||||
|
||||
expect(await subKey.verify(pubKey.primaryKey)).to.equal(openpgp.enums.keyStatus.valid);
|
||||
expect(await revKey.verify(pubKey.primaryKey)).to.equal(openpgp.enums.keyStatus.revoked);
|
||||
await subKey.verify(pubKey.primaryKey);
|
||||
await expect(revKey.verify(pubKey.primaryKey)).to.be.rejectedWith('Subkey is revoked');
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -2922,7 +2914,7 @@ describe('Key', function() {
|
|||
expect(primUser.selfCertification).to.be.an.instanceof(openpgp.packet.Signature);
|
||||
});
|
||||
|
||||
it('getPrimaryUser() should return null if no UserIDs are bound', async function() {
|
||||
it('getPrimaryUser() should throw if no UserIDs are bound', async function() {
|
||||
const keyWithoutUserID = `-----BEGIN PGP PRIVATE KEY BLOCK-----
|
||||
|
||||
xVgEWxpFkRYJKwYBBAHaRw8BAQdAYjjZLkp4qG7KAqJeVQlxQT6uCPq6rylV02nC
|
||||
|
@ -2936,8 +2928,7 @@ VYGdb3eNlV8CfoEC
|
|||
=FYbP
|
||||
-----END PGP PRIVATE KEY BLOCK-----`;
|
||||
const key = (await openpgp.key.readArmored(keyWithoutUserID)).keys[0];
|
||||
const primUser = await key.getPrimaryUser();
|
||||
expect(primUser).to.be.null;
|
||||
await expect(key.getPrimaryUser()).to.be.rejectedWith('Could not find valid self-signature in key 3ce893915c44212f');
|
||||
});
|
||||
|
||||
it('Encrypt - latest created user', async function() {
|
||||
|
@ -3025,12 +3016,23 @@ VYGdb3eNlV8CfoEC
|
|||
expect(await key.subKeys[0].getExpirationTime(key.primaryKey)).to.be.null;
|
||||
});
|
||||
|
||||
it('Reject encryption with revoked subkey', async function() {
|
||||
it('Reject encryption with revoked primary user', async function() {
|
||||
const key = (await openpgp.key.readArmored(pub_revoked_subkeys)).keys[0];
|
||||
return openpgp.encrypt({publicKeys: [key], message: openpgp.message.fromText('random data')}).then(() => {
|
||||
throw new Error('encryptSessionKey should not encrypt with revoked public key');
|
||||
}).catch(function(error) {
|
||||
expect(error.message).to.equal('Error encrypting message: Could not find valid key packet for encryption in key ' + key.getKeyId().toHex());
|
||||
expect(error.message).to.equal('Error encrypting message: Primary user is revoked');
|
||||
});
|
||||
});
|
||||
|
||||
it('Reject encryption with revoked subkey', async function() {
|
||||
const key = (await openpgp.key.readArmored(pub_revoked_subkeys)).keys[0];
|
||||
key.revocationSignatures = [];
|
||||
key.users[0].revocationSignatures = [];
|
||||
return openpgp.encrypt({publicKeys: [key], message: openpgp.message.fromText('random data'), date: new Date(1386842743000)}).then(() => {
|
||||
throw new Error('encryptSessionKey should not encrypt with revoked public key');
|
||||
}).catch(function(error) {
|
||||
expect(error.message).to.equal('Error encrypting message: Could not find valid encryption key packet in key ' + key.getKeyId().toHex() + ': Subkey is revoked');
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -3039,7 +3041,7 @@ VYGdb3eNlV8CfoEC
|
|||
return openpgp.encrypt({publicKeys: [key], message: openpgp.message.fromText('random data')}).then(() => {
|
||||
throw new Error('encryptSessionKey should not encrypt with revoked public key');
|
||||
}).catch(function(error) {
|
||||
expect(error.message).to.equal('Error encrypting message: Could not find valid key packet for encryption in key ' + key.getKeyId().toHex());
|
||||
expect(error.message).to.equal('Error encrypting message: Primary key is revoked');
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -3090,7 +3092,7 @@ describe('addSubkey functionality testing', function(){
|
|||
expect(subkeyN.byteLength()).to.be.equal(rsaBits ? (rsaBits / 8) : pkN.byteLength());
|
||||
expect(subKey.getAlgorithmInfo().algorithm).to.be.equal('rsa_encrypt_sign');
|
||||
expect(subKey.getAlgorithmInfo().rsaBits).to.be.equal(rsaBits || privateKey.getAlgorithmInfo().rsaBits);
|
||||
expect(await subKey.verify(newPrivateKey.primaryKey)).to.be.equal(openpgp.enums.keyStatus.valid);
|
||||
await subKey.verify(newPrivateKey.primaryKey);
|
||||
});
|
||||
|
||||
it('should throw when trying to encrypt a subkey separately from key', async function() {
|
||||
|
@ -3113,7 +3115,7 @@ describe('addSubkey functionality testing', function(){
|
|||
const subKey = importedPrivateKey.subKeys[total];
|
||||
expect(subKey).to.exist;
|
||||
expect(importedPrivateKey.subKeys.length).to.be.equal(total+1);
|
||||
expect(await subKey.verify(importedPrivateKey.primaryKey)).to.be.equal(openpgp.enums.keyStatus.valid);
|
||||
await subKey.verify(importedPrivateKey.primaryKey);
|
||||
});
|
||||
|
||||
it('create and add a new ec subkey to a ec key', async function() {
|
||||
|
@ -3137,7 +3139,7 @@ describe('addSubkey functionality testing', function(){
|
|||
const pkOid = privateKey.primaryKey.params[0];
|
||||
expect(subkeyOid.getName()).to.be.equal(pkOid.getName());
|
||||
expect(subKey.getAlgorithmInfo().algorithm).to.be.equal('eddsa');
|
||||
expect(await subKey.verify(privateKey.primaryKey)).to.be.equal(openpgp.enums.keyStatus.valid);
|
||||
await subKey.verify(privateKey.primaryKey);
|
||||
});
|
||||
|
||||
it('create and add a new ec subkey to a rsa key', async function() {
|
||||
|
@ -3153,7 +3155,7 @@ describe('addSubkey functionality testing', function(){
|
|||
expect(newPrivateKey.subKeys.length).to.be.equal(total+1);
|
||||
expect(subKey.keyPacket.params[0].getName()).to.be.equal(openpgp.enums.curve.curve25519);
|
||||
expect(subKey.getAlgorithmInfo().algorithm).to.be.equal('ecdh');
|
||||
expect(await subKey.verify(privateKey.primaryKey)).to.be.equal(openpgp.enums.keyStatus.valid);
|
||||
await subKey.verify(privateKey.primaryKey);
|
||||
});
|
||||
|
||||
it('sign/verify data with the new subkey correctly using curve25519', async function() {
|
||||
|
@ -3170,7 +3172,7 @@ describe('addSubkey functionality testing', function(){
|
|||
const pkOid = newPrivateKey.primaryKey.params[0];
|
||||
expect(subkeyOid.getName()).to.be.equal(pkOid.getName());
|
||||
expect(subKey.getAlgorithmInfo().algorithm).to.be.equal('eddsa');
|
||||
expect(await subKey.verify(newPrivateKey.primaryKey)).to.be.equal(openpgp.enums.keyStatus.valid);
|
||||
await subKey.verify(newPrivateKey.primaryKey);
|
||||
expect(await newPrivateKey.getSigningKey()).to.be.equal(subKey);
|
||||
const signed = await openpgp.sign({message: openpgp.cleartext.fromText('the data to signed'), privateKeys: newPrivateKey, armor:false});
|
||||
const verified = await signed.message.verify([newPrivateKey.toPublic()]);
|
||||
|
@ -3191,7 +3193,7 @@ describe('addSubkey functionality testing', function(){
|
|||
newPrivateKey = (await openpgp.key.readArmored(armoredKey)).keys[0];
|
||||
const subKey = newPrivateKey.subKeys[total];
|
||||
const publicKey = newPrivateKey.toPublic();
|
||||
expect(await subKey.verify(newPrivateKey.primaryKey)).to.be.equal(openpgp.enums.keyStatus.valid);
|
||||
await subKey.verify(newPrivateKey.primaryKey);
|
||||
expect(await newPrivateKey.getEncryptionKey()).to.be.equal(subKey);
|
||||
const encrypted = await openpgp.encrypt({message: openpgp.message.fromText(vData), publicKeys: publicKey, armor:false});
|
||||
expect(encrypted.message).to.be.exist;
|
||||
|
@ -3214,7 +3216,7 @@ describe('addSubkey functionality testing', function(){
|
|||
newPrivateKey = (await openpgp.key.readArmored(armoredKey)).keys[0];
|
||||
const subKey = newPrivateKey.subKeys[total];
|
||||
expect(subKey.getAlgorithmInfo().algorithm).to.be.equal('rsa_encrypt_sign');
|
||||
expect(await subKey.verify(newPrivateKey.primaryKey)).to.be.equal(openpgp.enums.keyStatus.valid);
|
||||
await subKey.verify(newPrivateKey.primaryKey);
|
||||
expect(await newPrivateKey.getSigningKey()).to.be.equal(subKey);
|
||||
const signed = await openpgp.sign({message: openpgp.cleartext.fromText('the data to signed'), privateKeys: newPrivateKey, armor:false});
|
||||
const verified = await signed.message.verify([newPrivateKey.toPublic()]);
|
||||
|
|
|
@ -2388,7 +2388,7 @@ describe('OpenPGP.js public api tests', function() {
|
|||
}).then(function(encrypted) {
|
||||
throw new Error('Should not encrypt with revoked key');
|
||||
}).catch(function(error) {
|
||||
expect(error.message).to.match(/Could not find valid key packet for encryption/);
|
||||
expect(error.message).to.match(/Error encrypting message: Primary key is revoked/);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -2405,7 +2405,7 @@ describe('OpenPGP.js public api tests', function() {
|
|||
}).then(function(encrypted) {
|
||||
throw new Error('Should not encrypt with revoked subkey');
|
||||
}).catch(function(error) {
|
||||
expect(error.message).to.match(/Could not find valid key packet for encryption/);
|
||||
expect(error.message).to.match(/Could not find valid encryption key packet/);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -478,41 +478,52 @@ vwjE8mqJXetNMfj8r2SCyvkEnlVRYR+/mnge+ib56FdJ8uKtqSxyvgA=
|
|||
-----END PGP MESSAGE-----`;
|
||||
|
||||
it('Testing signature checking on CAST5-enciphered message', async function() {
|
||||
const priv_key = (await openpgp.key.readArmored(priv_key_arm1)).keys[0];
|
||||
const pub_key = (await openpgp.key.readArmored(pub_key_arm1)).keys[0];
|
||||
const msg = await openpgp.message.readArmored(msg_arm1);
|
||||
await priv_key.decrypt("abcd");
|
||||
return openpgp.decrypt({ privateKeys: priv_key, publicKeys:[pub_key], message:msg }).then(function(decrypted) {
|
||||
const { reject_message_hash_algorithms } = openpgp.config;
|
||||
Object.assign(openpgp.config, { reject_message_hash_algorithms: new Set([openpgp.enums.hash.md5, openpgp.enums.hash.ripemd]) });
|
||||
try {
|
||||
const priv_key = (await openpgp.key.readArmored(priv_key_arm1)).keys[0];
|
||||
const pub_key = (await openpgp.key.readArmored(pub_key_arm1)).keys[0];
|
||||
const msg = await openpgp.message.readArmored(msg_arm1);
|
||||
await priv_key.decrypt("abcd");
|
||||
const decrypted = await openpgp.decrypt({ privateKeys: priv_key, publicKeys:[pub_key], message:msg });
|
||||
expect(decrypted.data).to.exist;
|
||||
expect(decrypted.signatures[0].valid).to.be.true;
|
||||
expect(decrypted.signatures[0].signature.packets.length).to.equal(1);
|
||||
});
|
||||
} finally {
|
||||
Object.assign(openpgp.config, { reject_message_hash_algorithms });
|
||||
}
|
||||
});
|
||||
|
||||
it('Supports decrypting with GnuPG stripped-key extension', async function() {
|
||||
// exercises the GnuPG s2k type 1001 extension:
|
||||
// the secrets on the primary key have been stripped.
|
||||
const priv_key_gnupg_ext = (await openpgp.key.readArmored(priv_key_arm1_stripped)).keys[0];
|
||||
const priv_key_gnupg_ext_2 = (await openpgp.key.readArmored(priv_key_arm1_stripped)).keys[0];
|
||||
const pub_key = (await openpgp.key.readArmored(pub_key_arm1)).keys[0];
|
||||
const message = await openpgp.message.readArmored(msg_arm1);
|
||||
const primaryKey_packet = priv_key_gnupg_ext.primaryKey.write();
|
||||
expect(priv_key_gnupg_ext.isDecrypted()).to.be.false;
|
||||
await priv_key_gnupg_ext.decrypt("abcd");
|
||||
await priv_key_gnupg_ext_2.decrypt("abcd");
|
||||
expect(priv_key_gnupg_ext.isDecrypted()).to.be.true;
|
||||
const msg = await openpgp.decrypt({ message, privateKeys: [priv_key_gnupg_ext], publicKeys: [pub_key] });
|
||||
expect(msg.signatures).to.exist;
|
||||
expect(msg.signatures).to.have.length(1);
|
||||
expect(msg.signatures[0].valid).to.be.true;
|
||||
expect(msg.signatures[0].signature.packets.length).to.equal(1);
|
||||
await expect(openpgp.sign({ message: openpgp.message.fromText('test'), privateKeys: [priv_key_gnupg_ext] })).to.eventually.be.rejectedWith('Missing private key parameters');
|
||||
await expect(openpgp.reformatKey({ userIds: { name: 'test' }, privateKey: priv_key_gnupg_ext })).to.eventually.be.rejectedWith('Missing private key parameters');
|
||||
await expect(openpgp.reformatKey({ userIds: { name: 'test' }, privateKey: priv_key_gnupg_ext_2, passphrase: 'test' })).to.eventually.be.rejectedWith('Missing private key parameters');
|
||||
await priv_key_gnupg_ext.encrypt("abcd");
|
||||
expect(priv_key_gnupg_ext.isDecrypted()).to.be.false;
|
||||
const primaryKey_packet2 = priv_key_gnupg_ext.primaryKey.write();
|
||||
expect(primaryKey_packet).to.deep.equal(primaryKey_packet2);
|
||||
const { reject_message_hash_algorithms } = openpgp.config;
|
||||
Object.assign(openpgp.config, { reject_message_hash_algorithms: new Set([openpgp.enums.hash.md5, openpgp.enums.hash.ripemd]) });
|
||||
try {
|
||||
// exercises the GnuPG s2k type 1001 extension:
|
||||
// the secrets on the primary key have been stripped.
|
||||
const priv_key_gnupg_ext = (await openpgp.key.readArmored(priv_key_arm1_stripped)).keys[0];
|
||||
const priv_key_gnupg_ext_2 = (await openpgp.key.readArmored(priv_key_arm1_stripped)).keys[0];
|
||||
const pub_key = (await openpgp.key.readArmored(pub_key_arm1)).keys[0];
|
||||
const message = await openpgp.message.readArmored(msg_arm1);
|
||||
const primaryKey_packet = priv_key_gnupg_ext.primaryKey.write();
|
||||
expect(priv_key_gnupg_ext.isDecrypted()).to.be.false;
|
||||
await priv_key_gnupg_ext.decrypt("abcd");
|
||||
await priv_key_gnupg_ext_2.decrypt("abcd");
|
||||
expect(priv_key_gnupg_ext.isDecrypted()).to.be.true;
|
||||
const msg = await openpgp.decrypt({ message, privateKeys: [priv_key_gnupg_ext], publicKeys: [pub_key] });
|
||||
expect(msg.signatures).to.exist;
|
||||
expect(msg.signatures).to.have.length(1);
|
||||
expect(msg.signatures[0].valid).to.be.true;
|
||||
expect(msg.signatures[0].signature.packets.length).to.equal(1);
|
||||
await expect(openpgp.sign({ message: openpgp.message.fromText('test'), privateKeys: [priv_key_gnupg_ext] })).to.eventually.be.rejectedWith('Missing private key parameters');
|
||||
await expect(openpgp.reformatKey({ userIds: { name: 'test' }, privateKey: priv_key_gnupg_ext })).to.eventually.be.rejectedWith('Missing private key parameters');
|
||||
await expect(openpgp.reformatKey({ userIds: { name: 'test' }, privateKey: priv_key_gnupg_ext_2, passphrase: 'test' })).to.eventually.be.rejectedWith('Missing private key parameters');
|
||||
await priv_key_gnupg_ext.encrypt("abcd");
|
||||
expect(priv_key_gnupg_ext.isDecrypted()).to.be.false;
|
||||
const primaryKey_packet2 = priv_key_gnupg_ext.primaryKey.write();
|
||||
expect(primaryKey_packet).to.deep.equal(primaryKey_packet2);
|
||||
} finally {
|
||||
Object.assign(openpgp.config, { reject_message_hash_algorithms });
|
||||
}
|
||||
});
|
||||
|
||||
it('Supports signing with GnuPG stripped-key extension', async function() {
|
||||
|
@ -523,27 +534,32 @@ vwjE8mqJXetNMfj8r2SCyvkEnlVRYR+/mnge+ib56FdJ8uKtqSxyvgA=
|
|||
});
|
||||
|
||||
it('Verify V4 signature. Hash: SHA1. PK: RSA. Signature Type: 0x00 (binary document)', async function() {
|
||||
const signedArmor =
|
||||
['-----BEGIN PGP MESSAGE-----',
|
||||
'Version: GnuPG v2.0.19 (GNU/Linux)',
|
||||
'',
|
||||
'owGbwMvMwMT4oOW7S46CznTGNeZJLCWpFSVBU3ZGF2fkF5Uo5KYWFyemp3LlAUUV',
|
||||
'cjLzUrneTp3zauvaN9O26L9ZuOFNy4LXyydwcXXMYWFgZGJgY2UCaWXg4hSAmblK',
|
||||
'nPmfsXYxd58Ka9eVrEnSpzilr520fXBrJsf2P/oTqzTj3hzyLG0o3TTzxFfrtOXf',
|
||||
'cw6U57n3/Z4X0pEZ68C5/o/6NpPICD7fuEOz3936raZ6wXGzueY8pfPnVjY0ajAc',
|
||||
'PtJzvvqj+ubYaT1sK9wWhd9lL3/V+9Zuua9QjOWC22buchsCroh8fLoZAA==',
|
||||
'=VH8F',
|
||||
'-----END PGP MESSAGE-----'].join('\n');
|
||||
const { reject_message_hash_algorithms } = openpgp.config;
|
||||
Object.assign(openpgp.config, { reject_message_hash_algorithms: new Set([openpgp.enums.hash.md5, openpgp.enums.hash.ripemd]) });
|
||||
try {
|
||||
const signedArmor =
|
||||
['-----BEGIN PGP MESSAGE-----',
|
||||
'Version: GnuPG v2.0.19 (GNU/Linux)',
|
||||
'',
|
||||
'owGbwMvMwMT4oOW7S46CznTGNeZJLCWpFSVBU3ZGF2fkF5Uo5KYWFyemp3LlAUUV',
|
||||
'cjLzUrneTp3zauvaN9O26L9ZuOFNy4LXyydwcXXMYWFgZGJgY2UCaWXg4hSAmblK',
|
||||
'nPmfsXYxd58Ka9eVrEnSpzilr520fXBrJsf2P/oTqzTj3hzyLG0o3TTzxFfrtOXf',
|
||||
'cw6U57n3/Z4X0pEZ68C5/o/6NpPICD7fuEOz3936raZ6wXGzueY8pfPnVjY0ajAc',
|
||||
'PtJzvvqj+ubYaT1sK9wWhd9lL3/V+9Zuua9QjOWC22buchsCroh8fLoZAA==',
|
||||
'=VH8F',
|
||||
'-----END PGP MESSAGE-----'].join('\n');
|
||||
|
||||
const sMsg = await openpgp.message.readArmored(signedArmor);
|
||||
const pub_key = (await openpgp.key.readArmored(pub_key_arm2)).keys[0];
|
||||
return sMsg.verify([pub_key]).then(async verified => {
|
||||
const sMsg = await openpgp.message.readArmored(signedArmor);
|
||||
const pub_key = (await openpgp.key.readArmored(pub_key_arm2)).keys[0];
|
||||
const verified = await sMsg.verify([pub_key]);
|
||||
openpgp.stream.pipe(sMsg.getLiteralData(), new WritableStream());
|
||||
expect(verified).to.exist;
|
||||
expect(verified).to.have.length(1);
|
||||
expect(await verified[0].verified).to.be.true;
|
||||
expect((await verified[0].signature).packets.length).to.equal(1);
|
||||
});
|
||||
} finally {
|
||||
Object.assign(openpgp.config, { reject_message_hash_algorithms });
|
||||
}
|
||||
});
|
||||
|
||||
it('Verify signature of signed and encrypted message from GPG2 with openpgp.decrypt', async function() {
|
||||
|
@ -698,8 +714,11 @@ vwjE8mqJXetNMfj8r2SCyvkEnlVRYR+/mnge+ib56FdJ8uKtqSxyvgA=
|
|||
});
|
||||
|
||||
it('Verify cleartext signed message with trailing spaces from GPG', async function() {
|
||||
const msg_armor =
|
||||
`-----BEGIN PGP SIGNED MESSAGE-----
|
||||
const { reject_message_hash_algorithms } = openpgp.config;
|
||||
Object.assign(openpgp.config, { reject_message_hash_algorithms: new Set([openpgp.enums.hash.md5, openpgp.enums.hash.ripemd]) });
|
||||
try {
|
||||
const msg_armor =
|
||||
`-----BEGIN PGP SIGNED MESSAGE-----
|
||||
Hash: SHA1
|
||||
|
||||
space:
|
||||
|
@ -718,21 +737,23 @@ zmuVOdNuWQqxT9Sqa84=
|
|||
=bqAR
|
||||
-----END PGP SIGNATURE-----`;
|
||||
|
||||
const plaintext = 'space: \nspace and tab: \t\nno trailing space\n \ntab:\t\ntab and space:\t ';
|
||||
const csMsg = await openpgp.cleartext.readArmored(msg_armor);
|
||||
const pubKey = (await openpgp.key.readArmored(pub_key_arm2)).keys[0];
|
||||
const plaintext = 'space: \nspace and tab: \t\nno trailing space\n \ntab:\t\ntab and space:\t ';
|
||||
const csMsg = await openpgp.cleartext.readArmored(msg_armor);
|
||||
const pubKey = (await openpgp.key.readArmored(pub_key_arm2)).keys[0];
|
||||
|
||||
const keyids = csMsg.getSigningKeyIds();
|
||||
const keyids = csMsg.getSigningKeyIds();
|
||||
|
||||
expect(pubKey.getKeys(keyids[0])).to.not.be.empty;
|
||||
expect(pubKey.getKeys(keyids[0])).to.not.be.empty;
|
||||
|
||||
return openpgp.verify({ publicKeys:[pubKey], message:csMsg }).then(function(cleartextSig) {
|
||||
const cleartextSig = await openpgp.verify({ publicKeys:[pubKey], message:csMsg });
|
||||
expect(cleartextSig).to.exist;
|
||||
expect(cleartextSig.data).to.equal(plaintext.replace(/[ \t]+$/mg, ''));
|
||||
expect(cleartextSig.signatures).to.have.length(1);
|
||||
expect(cleartextSig.signatures[0].valid).to.be.true;
|
||||
expect(cleartextSig.signatures[0].signature.packets.length).to.equal(1);
|
||||
});
|
||||
} finally {
|
||||
Object.assign(openpgp.config, { reject_message_hash_algorithms });
|
||||
}
|
||||
});
|
||||
|
||||
function tests() {
|
||||
|
@ -762,7 +783,7 @@ yYDnCgA=
|
|||
expect(cleartextSig).to.exist;
|
||||
expect(openpgp.util.nativeEOL(openpgp.util.Uint8Array_to_str(cleartextSig.data))).to.equal(plaintext);
|
||||
expect(cleartextSig.signatures).to.have.length(1);
|
||||
expect(cleartextSig.signatures[0].valid).to.be.true;
|
||||
expect(cleartextSig.signatures[0].valid).to.equal(!openpgp.config.reject_message_hash_algorithms.has(openpgp.enums.hash.sha1));
|
||||
expect(cleartextSig.signatures[0].signature.packets.length).to.equal(1);
|
||||
});
|
||||
});
|
||||
|
@ -799,7 +820,11 @@ yYDnCgA=
|
|||
expect(cleartextSig).to.exist;
|
||||
expect(openpgp.util.nativeEOL(openpgp.util.Uint8Array_to_str(await openpgp.stream.readToEnd(cleartextSig.data)))).to.equal(plaintext);
|
||||
expect(cleartextSig.signatures).to.have.length(1);
|
||||
expect(await cleartextSig.signatures[0].verified).to.be.true;
|
||||
if (!openpgp.config.reject_message_hash_algorithms.has(openpgp.enums.hash.sha1)) {
|
||||
expect(await cleartextSig.signatures[0].verified).to.be.true;
|
||||
} else {
|
||||
await expect(cleartextSig.signatures[0].verified).to.be.rejectedWith('Insecure message hash algorithm: SHA1');
|
||||
}
|
||||
expect((await cleartextSig.signatures[0].signature).packets.length).to.equal(1);
|
||||
});
|
||||
});
|
||||
|
@ -879,6 +904,18 @@ hkJiXopCSWKSlQInL1devkJJUWJmTmZeugJYlpdLAagQJM0JpsCqIQZwKgAA
|
|||
}
|
||||
});
|
||||
|
||||
let reject_message_hash_algorithms;
|
||||
tryTests('Accept SHA-1 signatures', tests, {
|
||||
if: true,
|
||||
before: function() {
|
||||
({ reject_message_hash_algorithms } = openpgp.config);
|
||||
Object.assign(openpgp.config, { reject_message_hash_algorithms: new Set([openpgp.enums.hash.md5, openpgp.enums.hash.ripemd]) });
|
||||
},
|
||||
after: function() {
|
||||
Object.assign(openpgp.config, { reject_message_hash_algorithms });
|
||||
}
|
||||
});
|
||||
|
||||
it('Sign text with openpgp.sign and verify with openpgp.verify leads to same string cleartext and valid signatures', async function() {
|
||||
const plaintext = 'short message\nnext line \n한국어/조선말';
|
||||
const pubKey = (await openpgp.key.readArmored(pub_key_arm2)).keys[0];
|
||||
|
@ -1234,7 +1271,7 @@ iTuGu4fEU1UligAXSrZmCdE=
|
|||
|
||||
const key = (await openpgp.key.readArmored(armoredKeyWithPhoto)).keys[0];
|
||||
for (const user of key.users) {
|
||||
expect(await user.verify(key.primaryKey)).to.equal(openpgp.enums.keyStatus.valid);
|
||||
await user.verify(key.primaryKey);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -425,9 +425,9 @@ const input = require('./testInputs');
|
|||
expect(user.selfCertifications[0].verify(
|
||||
hi.primaryKey, {userId: user.userId, key: hi.primaryKey}
|
||||
)).to.eventually.be.true;
|
||||
expect(user.verifyCertificate(
|
||||
await user.verifyCertificate(
|
||||
hi.primaryKey, user.selfCertifications[0], [hi]
|
||||
)).to.eventually.equal(openpgp.enums.keyStatus.valid);
|
||||
);
|
||||
}); */
|
||||
});
|
||||
|
||||
|
@ -460,9 +460,9 @@ function omnibus() {
|
|||
await expect(user.selfCertifications[0].verify(
|
||||
primaryKey, openpgp.enums.signature.cert_generic, { userId: user.userId, key: primaryKey }
|
||||
)).to.eventually.be.true;
|
||||
await expect(user.verifyCertificate(
|
||||
await user.verifyCertificate(
|
||||
primaryKey, user.selfCertifications[0], [hi.toPublic()]
|
||||
)).to.eventually.equal(openpgp.enums.keyStatus.valid);
|
||||
);
|
||||
|
||||
const options = {
|
||||
userIds: { name: "Bye", email: "bye@good.bye" },
|
||||
|
@ -480,9 +480,9 @@ function omnibus() {
|
|||
await expect(user.selfCertifications[0].verify(
|
||||
bye.primaryKey, openpgp.enums.signature.cert_generic, { userId: user.userId, key: bye.primaryKey }
|
||||
)).to.eventually.be.true;
|
||||
await expect(user.verifyCertificate(
|
||||
await user.verifyCertificate(
|
||||
bye.primaryKey, user.selfCertifications[0], [bye.toPublic()]
|
||||
)).to.eventually.equal(openpgp.enums.keyStatus.valid);
|
||||
);
|
||||
|
||||
return Promise.all([
|
||||
// Hi trusts Bye!
|
||||
|
|
|
@ -12,7 +12,7 @@ function tests() {
|
|||
|
||||
it('Should support loading OpenPGP.js from inside a Web Worker', async function() {
|
||||
try {
|
||||
eval('async function() {}');
|
||||
eval('(async function() {})');
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
this.skip();
|
||||
|
|
Loading…
Reference in New Issue
Block a user