From 6cc44a2003439d651ebe857797115f8bcf009747 Mon Sep 17 00:00:00 2001 From: larabr <7375870+larabr@users.noreply.github.com> Date: Mon, 8 Mar 2021 17:48:16 +0100 Subject: [PATCH 01/13] Add config setting to blacklist weak public key algorithms --- src/config/config.js | 18 +++++++++++++----- src/key/factory.js | 5 ++++- src/key/helper.js | 35 ++++++++++++++++++++++++++--------- src/key/key.js | 12 ++++++------ src/key/user.js | 7 +++++-- src/packet/public_key.js | 2 +- src/util.js | 21 +++++++++++++++++---- 7 files changed, 72 insertions(+), 28 deletions(-) diff --git a/src/config/config.js b/src/config/config.js index 006969e24..5d0757255 100644 --- a/src/config/config.js +++ b/src/config/config.js @@ -105,7 +105,7 @@ export default { checksumRequired: false, /** * @memberof module:config - * @property {Number} minRsaBits Minimum RSA key size allowed for key generation + * @property {Number} minRsaBits Minimum RSA key size allowed for key generation and message signing, verification and encryption */ minRsaBits: 2048, /** @@ -180,13 +180,21 @@ export default { */ useIndutnyElliptic: true, /** + * Reject insecure hash algorithms * @memberof module:config - * @property {Set} reject_hash_algorithms Reject insecure hash algorithms {@link module:enums.hash} + * @property {Set} rejectHashAlgorithms {@link module:enums.hash} */ - rejectHashAlgorithms: new globalThis.Set([enums.hash.md5, enums.hash.ripemd]), + rejectHashAlgorithms: new Set([enums.hash.md5, enums.hash.ripemd]), /** + * Reject insecure message hash algorithms * @memberof module:config - * @property {Set} reject_message_hash_algorithms Reject insecure message hash algorithms {@link module:enums.hash} + * @property {Set} rejectMessageHashAlgorithms {@link module:enums.hash} */ - rejectMessageHashAlgorithms: new globalThis.Set([enums.hash.md5, enums.hash.ripemd, enums.hash.sha1]) + rejectMessageHashAlgorithms: new Set([enums.hash.md5, enums.hash.ripemd, enums.hash.sha1]), + /** + * Reject insecure public key algorithms for message encryption, signing or verification + * @memberof module:config + * @property {Set} rejectPublicKeyAlgorithms {@link module:enums.publicKey} {@link module:enums.hash} + */ + rejectPublicKeyAlgorithms: new Set([enums.publicKey.elgamal, enums.publicKey.dsa]) }; diff --git a/src/key/factory.js b/src/key/factory.js index d6eeaecfd..a783f50d5 100644 --- a/src/key/factory.js +++ b/src/key/factory.js @@ -93,7 +93,10 @@ export async function reformat(options, config) { if (!options.subkeys) { options.subkeys = await Promise.all(secretSubkeyPackets.map(async secretSubkeyPacket => ({ - sign: await options.privateKey.getSigningKey(secretSubkeyPacket.getKeyId(), null, undefined, config).catch(() => {}) && + sign: await options.privateKey.getSigningKey( + secretSubkeyPacket.getKeyId(), null, undefined, + { ...config, rejectPublicKeyAlgorithms: new Set([]) } + ).catch(() => {}) && !await options.privateKey.getEncryptionKey(secretSubkeyPacket.getKeyId(), null, undefined, config).catch(() => {}) }))); } diff --git a/src/key/helper.js b/src/key/helper.js index 739ca5444..96b3ba416 100644 --- a/src/key/helper.js +++ b/src/key/helper.js @@ -366,25 +366,30 @@ export function sanitizeKeyOptions(options, subkeyDefaults = {}) { return options; } -export function isValidSigningKeyPacket(keyPacket, signature) { +export function isValidSigningKeyPacket(keyPacket, signature, config) { if (!signature.verified || signature.revoked !== false) { // Sanity check throw new Error('Signature not verified'); } - return keyPacket.algorithm !== enums.read(enums.publicKey, enums.publicKey.rsaEncrypt) && - keyPacket.algorithm !== enums.read(enums.publicKey, enums.publicKey.elgamal) && - keyPacket.algorithm !== enums.read(enums.publicKey, enums.publicKey.ecdh) && + const keyAlgo = enums.write(enums.publicKey, keyPacket.algorithm); + return checkKeyStrength(keyPacket, config) && + keyAlgo !== enums.publicKey.rsaEncrypt && + keyAlgo !== enums.publicKey.elgamal && + keyAlgo !== enums.publicKey.ecdh && (!signature.keyFlags || (signature.keyFlags[0] & enums.keyFlags.signData) !== 0); } -export function isValidEncryptionKeyPacket(keyPacket, signature) { +export function isValidEncryptionKeyPacket(keyPacket, signature, config) { 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.rsaSign) && - keyPacket.algorithm !== enums.read(enums.publicKey, enums.publicKey.ecdsa) && - keyPacket.algorithm !== enums.read(enums.publicKey, enums.publicKey.eddsa) && + + const keyAlgo = enums.write(enums.publicKey, keyPacket.algorithm); + return checkKeyStrength(keyPacket, config) && + keyAlgo !== enums.publicKey.dsa && + keyAlgo !== enums.publicKey.rsaSign && + keyAlgo !== enums.publicKey.ecdsa && + keyAlgo !== enums.publicKey.eddsa && (!signature.keyFlags || (signature.keyFlags[0] & enums.keyFlags.encryptCommunication) !== 0 || (signature.keyFlags[0] & enums.keyFlags.encryptStorage) !== 0); @@ -404,3 +409,15 @@ export function isValidDecryptionKeyPacket(signature, config) { (signature.keyFlags[0] & enums.keyFlags.encryptCommunication) !== 0 || (signature.keyFlags[0] & enums.keyFlags.encryptStorage) !== 0; } + +function checkKeyStrength(keyPacket, config) { + const keyAlgo = enums.write(enums.publicKey, keyPacket.algorithm); + if (config.rejectPublicKeyAlgorithms.has(keyAlgo)) { + throw new Error(`${keyPacket.algorithm} keys are considered too weak.`); + } + const rsaAlgos = new Set([enums.publicKey.rsaEncryptSign, enums.publicKey.rsaSign, enums.publicKey.rsaEncrypt]); + if (rsaAlgos.has(keyAlgo) && util.uint8ArrayBitLength(keyPacket.publicParams.n) < config.minRsaBits) { + throw new Error(`RSA keys shorter than ${config.minRsaBits} bits are considered too weak.`); + } + return true; +} diff --git a/src/key/key.js b/src/key/key.js index 8713b0466..4c87457d2 100644 --- a/src/key/key.js +++ b/src/key/key.js @@ -300,7 +300,7 @@ class Key { if ( bindingSignature && bindingSignature.embeddedSignature && - helper.isValidSigningKeyPacket(subKeys[i].keyPacket, bindingSignature) && + helper.isValidSigningKeyPacket(subKeys[i].keyPacket, bindingSignature, config) && await helper.getLatestValidSignature([bindingSignature.embeddedSignature], subKeys[i].keyPacket, enums.signature.keyBinding, dataToVerify, date, config) ) { return subKeys[i]; @@ -312,7 +312,7 @@ class Key { } const primaryUser = await this.getPrimaryUser(date, userId, config); if ((!keyId || primaryKey.getKeyId().equals(keyId)) && - helper.isValidSigningKeyPacket(primaryKey, primaryUser.selfCertification)) { + helper.isValidSigningKeyPacket(primaryKey, primaryUser.selfCertification, config)) { return this; } throw util.wrapError('Could not find valid signing key packet in key ' + this.getKeyId().toHex(), exception); @@ -339,7 +339,7 @@ class Key { await subKeys[i].verify(primaryKey, date, config); const dataToVerify = { key: primaryKey, bind: subKeys[i].keyPacket }; const bindingSignature = await helper.getLatestValidSignature(subKeys[i].bindingSignatures, primaryKey, enums.signature.subkeyBinding, dataToVerify, date, config); - if (bindingSignature && helper.isValidEncryptionKeyPacket(subKeys[i].keyPacket, bindingSignature)) { + if (bindingSignature && helper.isValidEncryptionKeyPacket(subKeys[i].keyPacket, bindingSignature, config)) { return subKeys[i]; } } catch (e) { @@ -350,7 +350,7 @@ class Key { // if no valid subkey for encryption, evaluate primary key const primaryUser = await this.getPrimaryUser(date, userId, config); if ((!keyId || primaryKey.getKeyId().equals(keyId)) && - helper.isValidEncryptionKeyPacket(primaryKey, primaryUser.selfCertification)) { + helper.isValidEncryptionKeyPacket(primaryKey, primaryUser.selfCertification, config)) { return this; } throw util.wrapError('Could not find valid encryption key packet in key ' + this.getKeyId().toHex(), exception); @@ -589,8 +589,8 @@ class Key { } if (capabilities === 'sign' || capabilities === 'encrypt_sign') { const signKey = - await this.getSigningKey(keyId, expiry, userId, config).catch(() => {}) || - await this.getSigningKey(keyId, null, userId, config).catch(() => {}); + await this.getSigningKey(keyId, expiry, userId, { ...config, rejectPublicKeyAlgorithms: new Set([]) }).catch(() => {}) || + await this.getSigningKey(keyId, null, userId, { ...config, rejectPublicKeyAlgorithms: new Set([]) }).catch(() => {}); if (!signKey) return null; const signExpiry = await signKey.getExpirationTime(this.keyPacket, undefined, config); if (signExpiry < expiry) expiry = signExpiry; diff --git a/src/key/user.js b/src/key/user.js index b422557e7..5fc0b2abc 100644 --- a/src/key/user.js +++ b/src/key/user.js @@ -59,7 +59,10 @@ class User { if (privateKey.hasSameFingerprintAs(primaryKey)) { throw new Error('Not implemented for self signing'); } - const signingKey = await privateKey.getSigningKey(undefined, undefined, undefined, config); + const signingKey = await privateKey.getSigningKey( + undefined, undefined, undefined, + { ...config, rejectPublicKeyAlgorithms: new Set([]) } + ); return createSignaturePacket(dataToSign, privateKey, signingKey.keyPacket, { // Most OpenPGP implementations use generic certification (0x10) signatureType: enums.signature.certGeneric, @@ -117,7 +120,7 @@ class User { if (!key.getKeyIds().some(id => id.equals(keyid))) { return null; } - const signingKey = await key.getSigningKey(keyid, date, undefined, config); + const signingKey = await key.getSigningKey(keyid, date, undefined, { ...config, rejectPublicKeyAlgorithms: new Set([]) }); if (certificate.revoked || await that.isRevoked(primaryKey, certificate, signingKey.keyPacket, date, config)) { throw new Error('User certificate is revoked'); } diff --git a/src/packet/public_key.js b/src/packet/public_key.js index 98ff67692..be1bdb4e7 100644 --- a/src/packet/public_key.js +++ b/src/packet/public_key.js @@ -234,7 +234,7 @@ class PublicKeyPacket { // RSA, DSA or ElGamal public modulo const modulo = this.publicParams.n || this.publicParams.p; if (modulo) { - result.bits = modulo.length * 8; + result.bits = util.uint8ArrayBitLength(modulo); } else { result.curve = this.publicParams.oid.getName(); } diff --git a/src/util.js b/src/util.js index 03c03c677..605a4c4a9 100644 --- a/src/util.js +++ b/src/util.js @@ -192,15 +192,28 @@ const util = { * @returns {Uint8Array} MPI-formatted Uint8Array. */ uint8ArrayToMpi: function (bin) { + const bitSize = util.uint8ArrayBitLength(bin); + if (bitSize === 0) { + throw new Error('Zero MPI'); + } + const stripped = bin.subarray(bin.length - ((bitSize + 7) / 8)); + const prefix = Uint8Array.from([(bitSize & 0xFF00) >> 8, bitSize & 0xFF]); + return util.concatUint8Array([prefix, stripped]); + }, + + /** + * Return bit length of the input data + * @param {Uint8Array} bin input data (big endian) + * @returns bit length + */ + uint8ArrayBitLength: function (bin) { let i; // index of leading non-zero byte for (i = 0; i < bin.length; i++) if (bin[i] !== 0) break; if (i === bin.length) { - throw new Error('Zero MPI'); + return 0; } const stripped = bin.subarray(i); - const size = (stripped.length - 1) * 8 + util.nbits(stripped[0]); - const prefix = Uint8Array.from([(size & 0xFF00) >> 8, size & 0xFF]); - return util.concatUint8Array([prefix, stripped]); + return (stripped.length - 1) * 8 + util.nbits(stripped[0]); }, /** From 10b9f6238a99e90d35ebe21c76501377dc21777e Mon Sep 17 00:00:00 2001 From: larabr <7375870+larabr@users.noreply.github.com> Date: Mon, 8 Mar 2021 19:00:03 +0100 Subject: [PATCH 02/13] Throw message verification error when issuer key not found --- .eslintrc.js | 2 +- src/key/helper.js | 2 +- src/message.js | 26 ++++++++++++++++---------- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index ce6cad066..05e42b260 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -215,7 +215,7 @@ module.exports = { "no-restricted-imports": "error", "no-restricted-modules": "error", "no-restricted-properties": "error", - "no-restricted-syntax": "error", + "no-restricted-syntax": ["error", "ForInStatement", "LabeledStatement", "WithStatement"], "no-return-assign": "error", "no-return-await": "error", "no-script-url": "error", diff --git a/src/key/helper.js b/src/key/helper.js index 96b3ba416..1d7b4be5a 100644 --- a/src/key/helper.js +++ b/src/key/helper.js @@ -401,7 +401,7 @@ export function isValidDecryptionKeyPacket(signature, config) { } if (config.allowInsecureDecryptionWithSigningKeys) { - // This is only relevant for RSA keys, all other signing ciphers cannot decrypt + // This is only relevant for RSA keys, all other signing algorithms cannot decrypt return true; } diff --git a/src/message.js b/src/message.js index 0bd806f2b..d8ef754b9 100644 --- a/src/message.js +++ b/src/message.js @@ -739,22 +739,28 @@ export async function createSignaturePackets(literalDataPacket, privateKeys, sig * @private */ async function createVerificationObject(signature, literalDataList, keys, date = new Date(), detached = false, streaming = false, config = defaultConfig) { - let primaryKey = null; - let signingKey = null; - await Promise.all(keys.map(async function(key) { - // Look for the unique key that matches issuerKeyId of signature - try { - signingKey = await key.getSigningKey(signature.issuerKeyId, null, undefined, config); + let primaryKey; + let signingKey; + + for (const key of keys) { + const issuerKeys = key.getKeys(signature.issuerKeyId); + if (issuerKeys.length > 0) { primaryKey = key; - } catch (e) {} - })); + break; + } + } const signaturePacket = signature.correspondingSig || signature; const verifiedSig = { keyid: signature.issuerKeyId, verified: (async () => { - if (!signingKey) { - return null; + if (!primaryKey) { + throw new Error(`Could not find key with id ${signature.issuerKeyId.toHex()}`); + } + try { + signingKey = await primaryKey.getSigningKey(signature.issuerKeyId, null, undefined, config); + } catch (e) { + throw new Error(`Key with id ${signature.issuerKeyId.toHex()} is not suitable for verification: ${e.message}`); } await signature.verify(signingKey.keyPacket, signature.signatureType, literalDataList[0], detached, streaming, config); const sig = await signaturePacket; From a5dc42d62544f12cb018f8d45f51c8ebe46d2e15 Mon Sep 17 00:00:00 2001 From: larabr <7375870+larabr@users.noreply.github.com> Date: Tue, 9 Mar 2021 23:00:08 +0100 Subject: [PATCH 03/13] Allow key reformat and certification sig generation/verification with weak keys --- src/cleartext.js | 4 +++- src/config/config.js | 2 +- src/key/factory.js | 16 +++++++++------- src/key/key.js | 10 +++++----- src/key/user.js | 4 ++-- src/message.js | 38 ++++++++++++++++++++++++-------------- src/packet/signature.js | 2 +- 7 files changed, 45 insertions(+), 31 deletions(-) diff --git a/src/cleartext.js b/src/cleartext.js index a38901af4..8b9dd794b 100644 --- a/src/cleartext.js +++ b/src/cleartext.js @@ -77,7 +77,9 @@ export class CleartextMessage { * @param {Array} keys - Array of keys to verify signatures * @param {Date} [date] - Verify the signature against the given date, i.e. check signature creation time < date < expiration time * @param {Object} [config] - Full configuration, defaults to openpgp.config - * @returns {Array<{keyid: module:type/keyid~Keyid, valid: Boolean}>} List of signer's keyid and validity of signature. + * @returns {Array<{keyid: module:type/keyid~Keyid, + * signature: Promise, + * verified: Promise}>} List of signer's keyid and validity of signature. * @async */ verify(keys, date = new Date(), config = defaultConfig) { diff --git a/src/config/config.js b/src/config/config.js index 5d0757255..d960faace 100644 --- a/src/config/config.js +++ b/src/config/config.js @@ -194,7 +194,7 @@ export default { /** * Reject insecure public key algorithms for message encryption, signing or verification * @memberof module:config - * @property {Set} rejectPublicKeyAlgorithms {@link module:enums.publicKey} {@link module:enums.hash} + * @property {Set} rejectPublicKeyAlgorithms {@link module:enums.publicKey} */ rejectPublicKeyAlgorithms: new Set([enums.publicKey.elgamal, enums.publicKey.dsa]) }; diff --git a/src/key/factory.js b/src/key/factory.js index a783f50d5..a3308b7d6 100644 --- a/src/key/factory.js +++ b/src/key/factory.js @@ -92,13 +92,15 @@ export async function reformat(options, config) { } if (!options.subkeys) { - options.subkeys = await Promise.all(secretSubkeyPackets.map(async secretSubkeyPacket => ({ - sign: await options.privateKey.getSigningKey( - secretSubkeyPacket.getKeyId(), null, undefined, - { ...config, rejectPublicKeyAlgorithms: new Set([]) } - ).catch(() => {}) && - !await options.privateKey.getEncryptionKey(secretSubkeyPacket.getKeyId(), null, undefined, config).catch(() => {}) - }))); + options.subkeys = await Promise.all(secretSubkeyPackets.map(async secretSubkeyPacket => { + const canSign = await options.privateKey.getSigningKey( + secretSubkeyPacket.getKeyId(), null, undefined, { ...config, rejectPublicKeyAlgorithms: new Set(), minRsaBits: 0 } + ).catch(() => {}); + const canEncrypt = await options.privateKey.getEncryptionKey( + secretSubkeyPacket.getKeyId(), null, undefined, { ...config, rejectPublicKeyAlgorithms: new Set(), minRsaBits: 0 } + ).catch(() => {}); + return { sign: canSign && !canEncrypt }; + })); } if (options.subkeys.length !== secretSubkeyPackets.length) { diff --git a/src/key/key.js b/src/key/key.js index 4c87457d2..585c8d62b 100644 --- a/src/key/key.js +++ b/src/key/key.js @@ -486,7 +486,7 @@ class Key { * It is enough to validate any signing keys * since its binding signatures are also checked */ - const signingKey = await this.getSigningKey(null, null, undefined, config); + const signingKey = await this.getSigningKey(null, null, undefined, { ...config, rejectPublicKeyAlgorithms: new Set(), minRsaBits: 0 }); // This could again be a dummy key if (signingKey && !signingKey.keyPacket.isDummy()) { signingKeyPacket = signingKey.keyPacket; @@ -581,16 +581,16 @@ class Key { let expiry = keyExpiry < sigExpiry ? keyExpiry : sigExpiry; if (capabilities === 'encrypt' || capabilities === 'encrypt_sign') { const encryptKey = - await this.getEncryptionKey(keyId, expiry, userId, config).catch(() => {}) || - await this.getEncryptionKey(keyId, null, userId, config).catch(() => {}); + await this.getEncryptionKey(keyId, expiry, userId, { ...config, rejectPublicKeyAlgorithms: new Set(), minRsaBits: 0 }).catch(() => {}) || + await this.getEncryptionKey(keyId, null, userId, { ...config, rejectPublicKeyAlgorithms: new Set(), minRsaBits: 0 }).catch(() => {}); if (!encryptKey) return null; const encryptExpiry = await encryptKey.getExpirationTime(this.keyPacket, undefined, config); if (encryptExpiry < expiry) expiry = encryptExpiry; } if (capabilities === 'sign' || capabilities === 'encrypt_sign') { const signKey = - await this.getSigningKey(keyId, expiry, userId, { ...config, rejectPublicKeyAlgorithms: new Set([]) }).catch(() => {}) || - await this.getSigningKey(keyId, null, userId, { ...config, rejectPublicKeyAlgorithms: new Set([]) }).catch(() => {}); + await this.getSigningKey(keyId, expiry, userId, { ...config, rejectPublicKeyAlgorithms: new Set(), minRsaBits: 0 }).catch(() => {}) || + await this.getSigningKey(keyId, null, userId, { ...config, rejectPublicKeyAlgorithms: new Set(), minRsaBits: 0 }).catch(() => {}); if (!signKey) return null; const signExpiry = await signKey.getExpirationTime(this.keyPacket, undefined, config); if (signExpiry < expiry) expiry = signExpiry; diff --git a/src/key/user.js b/src/key/user.js index 5fc0b2abc..92d428164 100644 --- a/src/key/user.js +++ b/src/key/user.js @@ -61,7 +61,7 @@ class User { } const signingKey = await privateKey.getSigningKey( undefined, undefined, undefined, - { ...config, rejectPublicKeyAlgorithms: new Set([]) } + { ...config, rejectPublicKeyAlgorithms: new Set(), minRsaBits: 0 } ); return createSignaturePacket(dataToSign, privateKey, signingKey.keyPacket, { // Most OpenPGP implementations use generic certification (0x10) @@ -120,7 +120,7 @@ class User { if (!key.getKeyIds().some(id => id.equals(keyid))) { return null; } - const signingKey = await key.getSigningKey(keyid, date, undefined, { ...config, rejectPublicKeyAlgorithms: new Set([]) }); + const signingKey = await key.getSigningKey(keyid, date, undefined, { ...config, rejectPublicKeyAlgorithms: new Set(), minRsaBits: 0 }); if (certificate.revoked || await that.isRevoked(primaryKey, certificate, signingKey.keyPacket, date, config)) { throw new Error('User certificate is revoked'); } diff --git a/src/message.js b/src/message.js index d8ef754b9..d66813e5b 100644 --- a/src/message.js +++ b/src/message.js @@ -523,7 +523,9 @@ export class Message { * @param {Date} [date] - Verify the signature against the given date, i.e. check signature creation time < date < expiration time * @param {Boolean} [streaming] - Whether to process data as a stream * @param {Object} [config] - Full configuration, defaults to openpgp.config - * @returns {Array<({keyid: module:type/keyid~Keyid, valid: Boolean})>} List of signer's keyid and validity of signature. + * @returns {Array<{keyid: module:type/keyid~Keyid, + * signature: Promise, + * verified: Promise}>} List of signer's keyid and validity of signatures. * @async */ async verify(keys, date = new Date(), streaming, config = defaultConfig) { @@ -576,7 +578,9 @@ export class Message { * @param {Signature} signature * @param {Date} date - Verify the signature against the given date, i.e. check signature creation time < date < expiration time * @param {Object} [config] - Full configuration, defaults to openpgp.config - * @returns {Array<({keyid: module:type/keyid~Keyid, valid: Boolean})>} List of signer's keyid and validity of signature. + * @returns {Array<{keyid: module:type/keyid~Keyid, + * signature: Promise, + * verified: Promise}>} List of signer's keyid and validity of signature. * @async */ verifyDetached(signature, keys, date = new Date(), streaming, config = defaultConfig) { @@ -733,14 +737,16 @@ export async function createSignaturePackets(literalDataPacket, privateKeys, sig * i.e. check signature creation time < date < expiration time * @param {Boolean} [detached] - Whether to verify detached signature packets * @param {Object} [config] - Full configuration, defaults to openpgp.config - * @returns {Promise>} list of signer's keyid and validity of signature + * @returns {{keyid: module:type/keyid~Keyid, + * signature: Promise, + * verified: Promise}} signer's keyid and validity of signature * @async * @private */ async function createVerificationObject(signature, literalDataList, keys, date = new Date(), detached = false, streaming = false, config = defaultConfig) { let primaryKey; let signingKey; + let keyError; for (const key of keys) { const issuerKeys = key.getKeys(signature.issuerKeyId); @@ -749,18 +755,21 @@ async function createVerificationObject(signature, literalDataList, keys, date = break; } } - + if (!primaryKey) { + keyError = new Error(`Could not find key with id ${signature.issuerKeyId.toHex()}`); + } else { + try { + signingKey = await primaryKey.getSigningKey(signature.issuerKeyId, null, undefined, config); + } catch (e) { + keyError = new Error(`Key with id ${signature.issuerKeyId.toHex()} is not suitable for verification: ${e.message}`); + } + } const signaturePacket = signature.correspondingSig || signature; const verifiedSig = { keyid: signature.issuerKeyId, verified: (async () => { - if (!primaryKey) { - throw new Error(`Could not find key with id ${signature.issuerKeyId.toHex()}`); - } - try { - signingKey = await primaryKey.getSigningKey(signature.issuerKeyId, null, undefined, config); - } catch (e) { - throw new Error(`Key with id ${signature.issuerKeyId.toHex()} is not suitable for verification: ${e.message}`); + if (keyError) { + throw keyError; } await signature.verify(signingKey.keyPacket, signature.signatureType, literalDataList[0], detached, streaming, config); const sig = await signaturePacket; @@ -802,8 +811,9 @@ async function createVerificationObject(signature, literalDataList, keys, date = * i.e. check signature creation time < date < expiration time * @param {Boolean} [detached] - Whether to verify detached signature packets * @param {Object} [config] - Full configuration, defaults to openpgp.config - * @returns {Promise>} list of signer's keyid and validity of signature + * @returns {Array<{keyid: module:type/keyid~Keyid, + * signature: Promise, + * verified: Promise}>} list of signer's keyid and validity of signatures * @async * @private */ diff --git a/src/packet/signature.js b/src/packet/signature.js index e406d474b..1e3485227 100644 --- a/src/packet/signature.js +++ b/src/packet/signature.js @@ -652,7 +652,7 @@ class SignaturePacket { /** * verifies the signature packet. Note: not all signature types are implemented * @param {PublicSubkeyPacket|PublicKeyPacket| - * SecretSubkeyPacket|SecretKeyPacket} key the public key to verify the signature + * SecretSubkeyPacket|SecretKeyPacket} key - the public key to verify the signature * @param {module:enums.signature} signatureType - Expected signature type * @param {String|Object} data - Data which on the signature applies * @param {Boolean} [detached] - Whether to verify a detached signature From 8d2b0770f5844ab674628c6f2d52169a67a8ed7f Mon Sep 17 00:00:00 2001 From: larabr <7375870+larabr@users.noreply.github.com> Date: Tue, 9 Mar 2021 23:00:55 +0100 Subject: [PATCH 04/13] Fix some tests --- test/general/key.js | 50 ++++-- test/general/signature.js | 362 +++++++++++++++++++------------------- test/general/x25519.js | 4 +- 3 files changed, 216 insertions(+), 200 deletions(-) diff --git a/test/general/key.js b/test/general/key.js index 1410bc2ce..eb5f0cbe2 100644 --- a/test/general/key.js +++ b/test/general/key.js @@ -2423,8 +2423,8 @@ function versionSpecificTests() { await privateKey.decrypt('hello world'); publicKey = await publicKey.signPrimaryUser([privateKey]); const signatures = await publicKey.verifyPrimaryUser([privateKey]); - const publicSigningKey = await publicKey.getSigningKey(); - const privateSigningKey = await privateKey.getSigningKey(); + const publicSigningKey = await publicKey.getSigningKey(undefined, undefined, undefined, { ...openpgp.config, minRsaBits: 1024 }); + const privateSigningKey = await privateKey.getSigningKey(undefined, undefined, undefined, { ...openpgp.config, minRsaBits: 1024 }); expect(signatures.length).to.equal(2); expect(signatures[0].keyid.toHex()).to.equal(publicSigningKey.getKeyId().toHex()); expect(signatures[0].valid).to.be.null; @@ -2439,8 +2439,8 @@ function versionSpecificTests() { await privateKey.decrypt('hello world'); publicKey = await publicKey.signPrimaryUser([privateKey]); const signatures = await publicKey.verifyPrimaryUser([wrongKey]); - const publicSigningKey = await publicKey.getSigningKey(); - const privateSigningKey = await privateKey.getSigningKey(); + const publicSigningKey = await publicKey.getSigningKey(undefined, undefined, undefined, { ...openpgp.config, minRsaBits: 1024 }); + const privateSigningKey = await privateKey.getSigningKey(undefined, undefined, undefined, { ...openpgp.config, minRsaBits: 1024 }); expect(signatures.length).to.equal(2); expect(signatures[0].keyid.toHex()).to.equal(publicSigningKey.getKeyId().toHex()); expect(signatures[0].valid).to.be.null; @@ -2454,8 +2454,8 @@ function versionSpecificTests() { await privateKey.decrypt('hello world'); publicKey = await publicKey.signAllUsers([privateKey]); const signatures = await publicKey.verifyAllUsers([privateKey]); - const publicSigningKey = await publicKey.getSigningKey(); - const privateSigningKey = await privateKey.getSigningKey(); + const publicSigningKey = await publicKey.getSigningKey(undefined, undefined, undefined, { ...openpgp.config, minRsaBits: 1024 }); + const privateSigningKey = await privateKey.getSigningKey(undefined, undefined, undefined, { ...openpgp.config, minRsaBits: 1024 }); expect(signatures.length).to.equal(4); expect(signatures[0].userid).to.equal(publicKey.users[0].userId.userid); expect(signatures[0].keyid.toHex()).to.equal(publicSigningKey.getKeyId().toHex()); @@ -2478,8 +2478,8 @@ function versionSpecificTests() { await privateKey.decrypt('hello world'); publicKey = await publicKey.signAllUsers([privateKey]); const signatures = await publicKey.verifyAllUsers([wrongKey]); - const publicSigningKey = await publicKey.getSigningKey(); - const privateSigningKey = await privateKey.getSigningKey(); + const publicSigningKey = await publicKey.getSigningKey(undefined, undefined, undefined, { ...openpgp.config, minRsaBits: 1024 }); + const privateSigningKey = await privateKey.getSigningKey(undefined, undefined, undefined, { ...openpgp.config, minRsaBits: 1024 }); expect(signatures.length).to.equal(4); expect(signatures[0].userid).to.equal(publicKey.users[0].userId.userid); expect(signatures[0].keyid.toHex()).to.equal(publicSigningKey.getKeyId().toHex()); @@ -2784,12 +2784,12 @@ module.exports = () => describe('Key', function() { it('Verify status of key with non-self revocation signature', async function() { const pubKey = await openpgp.readKey({ armoredKey: key_with_revoked_third_party_cert }); const [selfCertification] = await pubKey.verifyPrimaryUser(); - const publicSigningKey = await pubKey.getSigningKey(); + const publicSigningKey = await pubKey.getSigningKey(undefined, undefined, undefined, { ...openpgp.config, rejectPublicKeyAlgorithms: new Set() }); expect(selfCertification.keyid.toHex()).to.equal(publicSigningKey.getKeyId().toHex()); expect(selfCertification.valid).to.be.true; const certifyingKey = await openpgp.readKey({ armoredKey: certifying_key }); - const certifyingSigningKey = await certifyingKey.getSigningKey(); + const certifyingSigningKey = await certifyingKey.getSigningKey(undefined, undefined, undefined, { ...openpgp.config, rejectPublicKeyAlgorithms: new Set() }); const signatures = await pubKey.verifyPrimaryUser([certifyingKey]); expect(signatures.length).to.equal(2); expect(signatures[0].keyid.toHex()).to.equal(publicSigningKey.getKeyId().toHex()); @@ -2798,13 +2798,13 @@ module.exports = () => describe('Key', function() { expect(signatures[1].valid).to.be.false; const { user } = await pubKey.getPrimaryUser(); - await expect(user.verifyCertificate(pubKey.primaryKey, user.otherCertifications[0], [certifyingKey])).to.be.rejectedWith('User certificate is revoked'); + await expect(user.verifyCertificate(pubKey.primaryKey, user.otherCertifications[0], [certifyingKey], undefined, openpgp.config)).to.be.rejectedWith('User certificate is revoked'); }); it('Verify certificate of key with future creation date', async function() { const pubKey = await openpgp.readKey({ armoredKey: key_created_2030 }); const user = pubKey.users[0]; - await user.verifyCertificate(pubKey.primaryKey, user.selfCertifications[0], [pubKey], pubKey.primaryKey.created); + await user.verifyCertificate(pubKey.primaryKey, user.selfCertifications[0], [pubKey], pubKey.primaryKey.created, openpgp.config); const verifyAllResult = await user.verifyAllCertifications(pubKey.primaryKey, [pubKey], pubKey.primaryKey.created); expect(verifyAllResult[0].valid).to.be.true; await user.verify(pubKey.primaryKey, pubKey.primaryKey.created); @@ -2976,7 +2976,7 @@ module.exports = () => describe('Key', function() { expect(key.primaryKey.isDummy()).to.be.false; key.primaryKey.makeDummy(); expect(key.primaryKey.isDummy()).to.be.true; - await expect(openpgp.sign({ message: openpgp.Message.fromText('test'), privateKeys: [key] })).to.be.fulfilled; + await expect(openpgp.sign({ message: openpgp.Message.fromText('test'), privateKeys: [key], config: { minRsaBits: 1024 } })).to.be.fulfilled; }); it('makeDummy() - should work for encrypted keys', async function() { @@ -3381,8 +3381,13 @@ VYGdb3eNlV8CfoEC publicKey.users[1].selfCertifications[0].preferredSymmetricAlgorithms = [openpgp.enums.symmetric.aes128]; const sessionKey = await openpgp.generateSessionKey({ publicKeys: publicKey, toUserIds: { name: 'Test User', email: 'b@c.com' } }); expect(sessionKey.algorithm).to.equal('aes128'); - await openpgp.encrypt({ message: openpgp.Message.fromText('hello'), publicKeys: publicKey, privateKeys: privateKey, toUserIds: { name: 'Test User', email: 'b@c.com' }, armor: false }); - await expect(openpgp.encrypt({ message: openpgp.Message.fromText('hello'), publicKeys: publicKey, privateKeys: privateKey, toUserIds: { name: 'Test User', email: 'c@c.com' }, armor: false })).to.be.rejectedWith('Could not find user that matches that user ID'); + const config = { minRsaBits: 1024 } + await openpgp.encrypt({ + message: openpgp.Message.fromText('hello'), publicKeys: publicKey, privateKeys: privateKey, toUserIds: { name: 'Test User', email: 'b@c.com' }, armor: false, config + }); + await expect(openpgp.encrypt({ + message: openpgp.Message.fromText('hello'), publicKeys: publicKey, privateKeys: privateKey, toUserIds: { name: 'Test User', email: 'c@c.com' }, armor: false, config + })).to.be.rejectedWith('Could not find user that matches that user ID'); }); it('Fails to encrypt to User ID-less key', async function() { @@ -3406,18 +3411,25 @@ VYGdb3eNlV8CfoEC privateKey.users[0].userId = openpgp.UserIDPacket.fromObject({ name: 'Test User', email: 'b@c.com' }); // Set second user to prefer aes128. We will select this user. privateKey.users[1].selfCertifications[0].preferredHashAlgorithms = [openpgp.enums.hash.sha512]; - const signed = await openpgp.sign({ message: openpgp.Message.fromText('hello'), privateKeys: privateKey, fromUserIds: { name: 'Test McTestington', email: 'test@example.com' }, armor: false }); + const config = { minRsaBits: 1024 }; + const signed = await openpgp.sign({ + message: openpgp.Message.fromText('hello'), privateKeys: privateKey, fromUserIds: { name: 'Test McTestington', email: 'test@example.com' }, armor: false, config + }); const signature = await openpgp.readMessage({ binaryMessage: signed }); expect(signature.packets[0].hashAlgorithm).to.equal(openpgp.enums.hash.sha512); - const encrypted = await openpgp.encrypt({ message: openpgp.Message.fromText('hello'), passwords: 'test', privateKeys: privateKey, fromUserIds: { name: 'Test McTestington', email: 'test@example.com' }, armor: false }); + const encrypted = await openpgp.encrypt({ + message: openpgp.Message.fromText('hello'), passwords: 'test', privateKeys: privateKey, fromUserIds: { name: 'Test McTestington', email: 'test@example.com' }, armor: false, config + }); const { signatures } = await openpgp.decrypt({ message: await openpgp.readMessage({ binaryMessage: encrypted }), passwords: 'test' }); expect(signatures[0].signature.packets[0].hashAlgorithm).to.equal(openpgp.enums.hash.sha512); - await expect(openpgp.encrypt({ message: openpgp.Message.fromText('hello'), publicKeys: publicKey, privateKeys: privateKey, fromUserIds: { name: 'Not Test McTestington', email: 'test@example.com' }, armor: false })).to.be.rejectedWith('Could not find user that matches that user ID'); + await expect(openpgp.encrypt({ + message: openpgp.Message.fromText('hello'), publicKeys: publicKey, privateKeys: privateKey, fromUserIds: { name: 'Not Test McTestington', email: 'test@example.com' }, armor: false, config + })).to.be.rejectedWith('Could not find user that matches that user ID'); }); it('Find a valid subkey binding signature among many invalid ones', async function() { const key = await openpgp.readKey({ armoredKey: valid_binding_sig_among_many_expired_sigs_pub }); - expect(await key.getEncryptionKey()).to.not.be.null; + expect(await key.getEncryptionKey(undefined, undefined, undefined, { ...openpgp.config, minRsaBits: 1024 })).to.not.be.null; }); it('Selects the most recent subkey binding signature', async function() { diff --git a/test/general/signature.js b/test/general/signature.js index d63065270..dcb7302cf 100644 --- a/test/general/signature.js +++ b/test/general/signature.js @@ -847,20 +847,18 @@ hUhMKMuiM3pRwdIyDOItkUWQmjEEw7/XmhgInkXsCw== `; it('Testing signature checking on CAST5-enciphered message', async function() { - const { rejectMessageHashAlgorithms } = openpgp.config; - Object.assign(openpgp.config, { rejectMessageHashAlgorithms: new Set([openpgp.enums.hash.md5, openpgp.enums.hash.ripemd]) }); - try { - const priv_key = await openpgp.readKey({ armoredKey: priv_key_arm1 }); - const pub_key = await openpgp.readKey({ armoredKey: pub_key_arm1 }); - const msg = await openpgp.readMessage({ armoredMessage: 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, { rejectMessageHashAlgorithms }); - } + const priv_key = await openpgp.readKey({ armoredKey: priv_key_arm1 }); + const pub_key = await openpgp.readKey({ armoredKey: pub_key_arm1 }); + const msg = await openpgp.readMessage({ armoredMessage: msg_arm1 }); + await priv_key.decrypt("abcd"); + const config = { + rejectMessageHashAlgorithms: new Set([openpgp.enums.hash.md5, openpgp.enums.hash.ripemd]), + rejectPublicKeyAlgorithms: new Set() + }; + const decrypted = await openpgp.decrypt({ privateKeys: priv_key, publicKeys:[pub_key], message:msg, config }); + expect(decrypted.data).to.exist; + expect(decrypted.signatures[0].valid).to.be.true; + expect(decrypted.signatures[0].signature.packets.length).to.equal(1); }); it('Supports decrypting with GnuPG stripped-key extension', async function() { @@ -878,12 +876,14 @@ hUhMKMuiM3pRwdIyDOItkUWQmjEEw7/XmhgInkXsCw== 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] }); + const msg = await openpgp.decrypt({ message, privateKeys: [priv_key_gnupg_ext], publicKeys: [pub_key], config: { rejectPublicKeyAlgorithms: new Set() } }); 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(/Cannot sign with a gnu-dummy key/); + await expect(openpgp.sign({ + message: openpgp.Message.fromText('test'), privateKeys: [priv_key_gnupg_ext], config: { rejectPublicKeyAlgorithms: new Set() } + })).to.eventually.be.rejectedWith(/Cannot sign with a gnu-dummy key/); await expect(openpgp.reformatKey({ userIds: { name: 'test' }, privateKey: priv_key_gnupg_ext })).to.eventually.be.rejectedWith(/Cannot reformat a gnu-dummy primary key/); await expect(openpgp.reformatKey({ userIds: { name: 'test' }, privateKey: priv_key_gnupg_ext_2, passphrase: 'test' })).to.eventually.be.rejectedWith(/Cannot reformat a gnu-dummy primary key/); await priv_key_gnupg_ext.encrypt("abcd"); @@ -918,8 +918,11 @@ hUhMKMuiM3pRwdIyDOItkUWQmjEEw7/XmhgInkXsCw== }); it('Verify V4 signature. Hash: SHA1. PK: RSA. Signature Type: 0x00 (binary document)', async function() { - const { rejectMessageHashAlgorithms } = openpgp.config; - Object.assign(openpgp.config, { rejectMessageHashAlgorithms: new Set([openpgp.enums.hash.md5, openpgp.enums.hash.ripemd]) }); + const { rejectMessageHashAlgorithms, minRsaBits } = openpgp.config; + Object.assign(openpgp.config, { + rejectMessageHashAlgorithms: new Set([openpgp.enums.hash.md5, openpgp.enums.hash.ripemd]), + minRsaBits: 1024 + }); try { const signedArmor = ['-----BEGIN PGP MESSAGE-----', @@ -942,7 +945,7 @@ hUhMKMuiM3pRwdIyDOItkUWQmjEEw7/XmhgInkXsCw== expect(await verified[0].verified).to.be.true; expect((await verified[0].signature).packets.length).to.equal(1); } finally { - Object.assign(openpgp.config, { rejectMessageHashAlgorithms }); + Object.assign(openpgp.config, { rejectMessageHashAlgorithms, minRsaBits }); } }); @@ -970,7 +973,7 @@ hUhMKMuiM3pRwdIyDOItkUWQmjEEw7/XmhgInkXsCw== await Promise.all(esMsg.getEncryptionKeyIds().map(keyId => privKey.decrypt('hello world', keyId))); - return openpgp.decrypt({ privateKeys: privKey, publicKeys:[pubKey], message:esMsg }).then(function(decrypted) { + return openpgp.decrypt({ privateKeys: privKey, publicKeys:[pubKey], message:esMsg, config: { minRsaBits: 1024 } }).then(function(decrypted) { expect(decrypted.data).to.exist; expect(decrypted.data).to.equal(plaintext); expect(decrypted.signatures).to.have.length(1); @@ -997,46 +1000,44 @@ hUhMKMuiM3pRwdIyDOItkUWQmjEEw7/XmhgInkXsCw== 's8PvkyWmVM0O0fB/ZSHovHNNPffDg/rWhzOmXQ9/7vTn477F+aWm5sYzJ75/BQA=', '=+L0S', '-----END PGP MESSAGE-----'].join('\n'); - const plaintext = 'short message\nnext line\n한국어/조선말'; const sMsg = await openpgp.readMessage({ armoredMessage: msg_armor }); const pubKey2 = await openpgp.readKey({ armoredKey: pub_key_arm2 }); const pubKey3 = await openpgp.readKey({ armoredKey: pub_key_arm3 }); const keyids = sMsg.getSigningKeyIds(); - expect(pubKey2.getKeys(keyids[1])).to.not.be.empty; expect(pubKey3.getKeys(keyids[0])).to.not.be.empty; - return sMsg.verify([pubKey2, pubKey3]).then(async verifiedSig => { - expect(await openpgp.stream.readToEnd(sMsg.getText())).to.equal(plaintext); - expect(verifiedSig).to.exist; - expect(verifiedSig).to.have.length(2); - expect(await verifiedSig[0].verified).to.be.true; - expect(await verifiedSig[1].verified).to.be.true; - expect((await verifiedSig[0].signature).packets.length).to.equal(1); - expect((await verifiedSig[1].signature).packets.length).to.equal(1); - }); + const { data, signatures } = await openpgp.verify({ message: sMsg, publicKeys: [pubKey2, pubKey3], config: { minRsaBits: 1024 } }); + expect(data).to.equal(plaintext); + expect(signatures).to.exist; + expect(signatures).to.have.length(2); + expect(await signatures[0].verified).to.be.true; + expect(await signatures[1].verified).to.be.true; + expect((await signatures[0].signature).packets.length).to.equal(1); + expect((await signatures[1].signature).packets.length).to.equal(1); }); it('Verify fails with signed message with critical notations', async function() { const message = await openpgp.readMessage({ armoredMessage: signature_with_critical_notation }); const key = await openpgp.readKey({ armoredKey: pub_key_arm2 }); - const { signatures: [sig] } = await openpgp.verify({ message, publicKeys: key }); + const { signatures: [sig] } = await openpgp.verify({ message, publicKeys: key, config: { minRsaBits: 1024 } }); expect(sig.valid).to.be.false; expect(sig.error).to.match(/Unknown critical notation: test@example.com/); }); it('Verify succeeds with known signed message with critical notations', async function() { - const config = { knownNotations: ['test@example.com'] }; const message = await openpgp.readMessage({ armoredMessage: signature_with_critical_notation }); const key = await openpgp.readKey({ armoredKey: pub_key_arm2 }); + + const config = { knownNotations: ['test@example.com'], minRsaBits: 1024 }; const { signatures: [sig] } = await openpgp.verify({ message, publicKeys: key, config }); expect(sig.valid).to.be.true; }); it('Verify cleartext signed message with two signatures with openpgp.verify', async function() { - const msg_armor = + const cleartextMessage = ['-----BEGIN PGP SIGNED MESSAGE-----', 'Hash: SHA256', '', @@ -1060,16 +1061,16 @@ hUhMKMuiM3pRwdIyDOItkUWQmjEEw7/XmhgInkXsCw== '-----END PGP SIGNATURE-----'].join('\n'); const plaintext = 'short message\nnext line\n한국어/조선말'; - const csMsg = await openpgp.readCleartextMessage({ cleartextMessage: msg_armor }); + const message = await openpgp.readCleartextMessage({ cleartextMessage }); const pubKey2 = await openpgp.readKey({ armoredKey: pub_key_arm2 }); const pubKey3 = await openpgp.readKey({ armoredKey: pub_key_arm3 }); - const keyids = csMsg.getSigningKeyIds(); + const keyids = message.getSigningKeyIds(); expect(pubKey2.getKeys(keyids[0])).to.not.be.empty; expect(pubKey3.getKeys(keyids[1])).to.not.be.empty; - return openpgp.verify({ publicKeys:[pubKey2, pubKey3], message:csMsg }).then(function(cleartextSig) { + return openpgp.verify({ publicKeys:[pubKey2, pubKey3], message, config: { minRsaBits: 1024 } }).then(function(cleartextSig) { expect(cleartextSig).to.exist; expect(cleartextSig.data).to.equal(plaintext); expect(cleartextSig.signatures).to.have.length(2); @@ -1114,17 +1115,14 @@ PAAeuQTUrcJdZeJ86eQ9cCUB216HCwSKOWTQRzL+hBWKXij4WD4= it('Verify cleartext signed message with trailing spaces from GPG', async function() { - const { rejectMessageHashAlgorithms } = openpgp.config; - Object.assign(openpgp.config, { rejectMessageHashAlgorithms: new Set([openpgp.enums.hash.md5, openpgp.enums.hash.ripemd]) }); - try { - const msg_armor = - `-----BEGIN PGP SIGNED MESSAGE----- + const cleartextMessage = + `-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 space: space and tab: \t no trailing space - + tab:\t tab and space:\t -----BEGIN PGP SIGNATURE----- @@ -1137,28 +1135,29 @@ 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.readCleartextMessage({ cleartextMessage: msg_armor }); - const pubKey = await openpgp.readKey({ armoredKey: pub_key_arm2 }); + const plaintext = 'space: \nspace and tab: \t\nno trailing space\n \ntab:\t\ntab and space:\t '; + const message = await openpgp.readCleartextMessage({ cleartextMessage }); + const pubKey = await openpgp.readKey({ armoredKey: pub_key_arm2 }); - const keyids = csMsg.getSigningKeyIds(); + const keyids = message.getSigningKeyIds(); - expect(pubKey.getKeys(keyids[0])).to.not.be.empty; + expect(pubKey.getKeys(keyids[0])).to.not.be.empty; - 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, { rejectMessageHashAlgorithms }); - } + const cleartextSig = await openpgp.verify({ + publicKeys:[pubKey], + message, + config: { minRsaBits: 1024, rejectMessageHashAlgorithms: new Set() } + }); + 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); }); function tests() { it('Verify signed message with trailing spaces from GPG', async function() { - const msg_armor = + const armoredMessage = `-----BEGIN PGP MESSAGE----- Version: GnuPG v1 @@ -1172,24 +1171,22 @@ yYDnCgA= -----END PGP MESSAGE-----`; const plaintext = 'space: \nspace and tab: \t\nno trailing space\n \ntab:\t\ntab and space:\t '; - const sMsg = await openpgp.readMessage({ armoredMessage: msg_armor }); + const message = await openpgp.readMessage({ armoredMessage }); const pubKey = await openpgp.readKey({ armoredKey: pub_key_arm2 }); - const keyids = sMsg.getSigningKeyIds(); - + const keyids = message.getSigningKeyIds(); expect(pubKey.getKeys(keyids[0])).to.not.be.empty; - return openpgp.verify({ publicKeys: [pubKey], message: sMsg }).then(function(cleartextSig) { - expect(cleartextSig).to.exist; - expect(cleartextSig.data).to.equal(plaintext); - expect(cleartextSig.signatures).to.have.length(1); - expect(cleartextSig.signatures[0].valid).to.equal(!openpgp.config.rejectMessageHashAlgorithms.has(openpgp.enums.hash.sha1)); - expect(cleartextSig.signatures[0].signature.packets.length).to.equal(1); + return openpgp.verify({ publicKeys: [pubKey], message, config: { minRsaBits: 1024 } }).then(({ data, signatures }) => { + expect(data).to.equal(plaintext); + expect(signatures).to.have.length(1); + expect(signatures[0].valid).to.equal(!openpgp.config.rejectMessageHashAlgorithms.has(openpgp.enums.hash.sha1)); + expect(signatures[0].signature.packets.length).to.equal(1); }); }); it('Streaming verify signed message with trailing spaces from GPG', async function() { - const msg_armor = + const armoredMessage = `-----BEGIN PGP MESSAGE----- Version: GnuPG v1 @@ -1203,22 +1200,21 @@ yYDnCgA= -----END PGP MESSAGE-----`.split(''); const plaintext = 'space: \nspace and tab: \t\nno trailing space\n \ntab:\t\ntab and space:\t '; - const sMsg = await openpgp.readMessage({ + const message = await openpgp.readMessage({ armoredMessage: new openpgp.stream.ReadableStream({ async pull(controller) { await new Promise(setTimeout); - controller.enqueue(msg_armor.shift()); - if (!msg_armor.length) controller.close(); + controller.enqueue(armoredMessage.shift()); + if (!armoredMessage.length) controller.close(); } }) }); const pubKey = await openpgp.readKey({ armoredKey: pub_key_arm2 }); - const keyids = sMsg.getSigningKeyIds(); - + const keyids = message.getSigningKeyIds(); expect(pubKey.getKeys(keyids[0])).to.not.be.empty; - return openpgp.verify({ publicKeys: [pubKey], message: sMsg }).then(async function(cleartextSig) { + return openpgp.verify({ publicKeys: [pubKey], message, config: { minRsaBits: 1024 } }).then(async function(cleartextSig) { expect(cleartextSig).to.exist; expect(await openpgp.stream.readToEnd(cleartextSig.data)).to.equal(plaintext); expect(cleartextSig.signatures).to.have.length(1); @@ -1232,7 +1228,7 @@ yYDnCgA= }); it('Verify signed message with missing signature packet', async function() { - const msg_armor = + const armoredMessage = `-----BEGIN PGP MESSAGE----- Version: OpenPGP.js v3.1.3 Comment: https://openpgpjs.org @@ -1244,22 +1240,20 @@ hkJiXopCSWKSlQInL1devkJJUWJmTmZeugJYlpdLAagQJM0JpsCqIQZwKgAA -----END PGP MESSAGE-----`; const plaintext = 'space: \nspace and tab: \t\nno trailing space\n \ntab:\t\ntab and space:\t '; - const sMsg = await openpgp.readMessage({ armoredMessage: msg_armor }); + const message = await openpgp.readMessage({ armoredMessage }); const pubKey = await openpgp.readKey({ armoredKey: pub_key_arm2 }); - const keyids = sMsg.getSigningKeyIds(); - + const keyids = message.getSigningKeyIds(); expect(pubKey.getKeys(keyids[0])).to.not.be.empty; - return openpgp.verify({ publicKeys: [pubKey], message: sMsg }).then(async function(cleartextSig) { - expect(cleartextSig).to.exist; - expect(await openpgp.stream.readToEnd(cleartextSig.data)).to.equal(plaintext); - expect(cleartextSig.signatures).to.have.length(0); + return openpgp.verify({ publicKeys: [pubKey], message, config: { minRsaBits: 1024 } }).then(async ({ data, signatures }) => { + expect(data).to.equal(plaintext); + expect(signatures).to.have.length(0); }); }); it('Streaming verify signed message with missing signature packet', async function() { - const msg_armor = + const armoredMessage = `-----BEGIN PGP MESSAGE----- Version: OpenPGP.js v3.1.3 Comment: https://openpgpjs.org @@ -1271,27 +1265,25 @@ hkJiXopCSWKSlQInL1devkJJUWJmTmZeugJYlpdLAagQJM0JpsCqIQZwKgAA -----END PGP MESSAGE-----`.split(''); const plaintext = 'space: \nspace and tab: \t\nno trailing space\n \ntab:\t\ntab and space:\t '; - const sMsg = await openpgp.readMessage({ + const message = await openpgp.readMessage({ armoredMessage: new openpgp.stream.ReadableStream({ async pull(controller) { await new Promise(setTimeout); - controller.enqueue(msg_armor.shift()); - if (!msg_armor.length) controller.close(); + controller.enqueue(armoredMessage.shift()); + if (!armoredMessage.length) controller.close(); } }) }); const pubKey = await openpgp.readKey({ armoredKey: pub_key_arm2 }); - const keyids = sMsg.getSigningKeyIds(); - + const keyids = message.getSigningKeyIds(); expect(pubKey.getKeys(keyids[0])).to.not.be.empty; - return openpgp.verify({ publicKeys: [pubKey], message: sMsg }).then(async function(cleartextSig) { - expect(cleartextSig).to.exist; - expect(await openpgp.stream.readToEnd(cleartextSig.data)).to.equal(plaintext); - expect(cleartextSig.signatures).to.have.length(1); - await expect(cleartextSig.signatures[0].verified).to.be.rejectedWith('Corresponding signature packet missing'); - expect((await cleartextSig.signatures[0].signature).packets.length).to.equal(0); + return openpgp.verify({ publicKeys: [pubKey], message, config: { minRsaBits: 1024 } }).then(async ({ data, signatures }) => { + expect(await openpgp.stream.readToEnd(data)).to.equal(plaintext); + expect(signatures).to.have.length(1); + await expect(signatures[0].verified).to.be.rejectedWith('Corresponding signature packet missing'); + expect((await signatures[0].signature).packets.length).to.equal(0); }); }); } @@ -1316,17 +1308,17 @@ hkJiXopCSWKSlQInL1devkJJUWJmTmZeugJYlpdLAagQJM0JpsCqIQZwKgAA const privKey = await openpgp.readKey({ armoredKey: priv_key_arm2 }); await privKey.decrypt('hello world'); - return openpgp.sign({ privateKeys:[privKey], message: openpgp.CleartextMessage.fromText(plaintext) }).then(async function(signed) { + const config = { minRsaBits: 1024 }; + return openpgp.sign({ privateKeys:[privKey], message: openpgp.CleartextMessage.fromText(plaintext), config }).then(async function(signed) { - const csMsg = await openpgp.readCleartextMessage({ cleartextMessage: signed }); - return openpgp.verify({ publicKeys:[pubKey], message:csMsg }); + const message = await openpgp.readCleartextMessage({ cleartextMessage: signed }); + return openpgp.verify({ publicKeys:[pubKey], message, config }); - }).then(function(cleartextSig) { - expect(cleartextSig).to.exist; - expect(cleartextSig.data).to.equal(plaintext.replace(/[ \t\r]+$/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); + }).then(function({ data, signatures }) { + expect(data).to.equal(plaintext.replace(/[ \t\r]+$/mg, '')); + expect(signatures).to.have.length(1); + expect(signatures[0].valid).to.be.true; + expect(signatures[0].signature.packets.length).to.equal(1); }); }); @@ -1336,17 +1328,17 @@ hkJiXopCSWKSlQInL1devkJJUWJmTmZeugJYlpdLAagQJM0JpsCqIQZwKgAA const privKey = await openpgp.readKey({ armoredKey: priv_key_arm2 }); await privKey.decrypt('hello world'); - return openpgp.sign({ privateKeys:[privKey], message: openpgp.CleartextMessage.fromText(plaintext) }).then(async function(signed) { + const config = { minRsaBits: 1024 }; + return openpgp.sign({ privateKeys:[privKey], message: openpgp.CleartextMessage.fromText(plaintext), config }).then(async function(signed) { - const csMsg = await openpgp.readCleartextMessage({ cleartextMessage: signed }); - return openpgp.verify({ publicKeys:[pubKey], message:csMsg }); + const message = await openpgp.readCleartextMessage({ cleartextMessage: signed }); + return openpgp.verify({ publicKeys:[pubKey], message, config }); - }).then(function(cleartextSig) { - expect(cleartextSig).to.exist; - expect(cleartextSig.data).to.equal(plaintext); - 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); + }).then(function({ data, signatures }) { + expect(data).to.equal(plaintext); + expect(signatures).to.have.length(1); + expect(signatures[0].valid).to.be.true; + expect(signatures[0].signature.packets.length).to.equal(1); }); }); @@ -1356,17 +1348,17 @@ hkJiXopCSWKSlQInL1devkJJUWJmTmZeugJYlpdLAagQJM0JpsCqIQZwKgAA const privKey = await openpgp.readKey({ armoredKey: priv_key_arm2 }); await privKey.decrypt('hello world'); - return openpgp.sign({ privateKeys:[privKey], message: openpgp.CleartextMessage.fromText(plaintext) }).then(async function(signed) { + const config = { minRsaBits: 1024 }; + return openpgp.sign({ privateKeys:[privKey], message: openpgp.CleartextMessage.fromText(plaintext), config }).then(async function(signed) { - const csMsg = await openpgp.readCleartextMessage({ cleartextMessage: signed }); - return openpgp.verify({ publicKeys:[pubKey], message:csMsg }); + const message = await openpgp.readCleartextMessage({ cleartextMessage: signed }); + return openpgp.verify({ publicKeys:[pubKey], message, config }); - }).then(function(cleartextSig) { - 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); + }).then(function({ data, signatures }) { + expect(data).to.equal(plaintext.replace(/[ \t]+$/mg, '')); + expect(signatures).to.have.length(1); + expect(signatures[0].valid).to.be.true; + expect(signatures[0].signature.packets.length).to.equal(1); }); }); @@ -1376,17 +1368,17 @@ hkJiXopCSWKSlQInL1devkJJUWJmTmZeugJYlpdLAagQJM0JpsCqIQZwKgAA const privKey = await openpgp.readKey({ armoredKey: priv_key_arm2 }); await privKey.decrypt('hello world'); - return openpgp.sign({ privateKeys:[privKey], message: openpgp.Message.fromBinary(plaintext) }).then(async function(signed) { + const config = { minRsaBits: 1024 }; + return openpgp.sign({ privateKeys:[privKey], message: openpgp.Message.fromBinary(plaintext), config }).then(async function(signed) { - const csMsg = await openpgp.readMessage({ armoredMessage: signed }); - return openpgp.verify({ publicKeys:[pubKey], message:csMsg, format: 'binary' }); + const message = await openpgp.readMessage({ armoredMessage: signed }); + return openpgp.verify({ publicKeys:[pubKey], message, format: 'binary', config }); - }).then(async function(cleartextSig) { - expect(cleartextSig).to.exist; - expect(cleartextSig.data).to.deep.equal(plaintext); - 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); + }).then(function({ data, signatures }) { + expect(data).to.deep.equal(plaintext); + expect(signatures).to.have.length(1); + expect(signatures[0].valid).to.be.true; + expect(signatures[0].signature.packets.length).to.equal(1); }); }); @@ -1396,17 +1388,17 @@ hkJiXopCSWKSlQInL1devkJJUWJmTmZeugJYlpdLAagQJM0JpsCqIQZwKgAA const privKey = await openpgp.readKey({ armoredKey: priv_key_arm2 }); await privKey.decrypt('hello world'); - return openpgp.sign({ privateKeys:[privKey], message: openpgp.Message.fromBinary(plaintext), armor:false }).then(async function(signed) { + const config = { minRsaBits: 1024 }; + return openpgp.sign({ privateKeys:[privKey], message: openpgp.Message.fromBinary(plaintext), armor:false, config }).then(async function(signed) { - const csMsg = await openpgp.readMessage({ binaryMessage: signed }); - return openpgp.verify({ publicKeys:[pubKey], message:csMsg, format: 'binary' }); + const message = await openpgp.readMessage({ binaryMessage: signed }); + return openpgp.verify({ publicKeys:[pubKey], message, format: 'binary', config }); - }).then(function(cleartextSig) { - expect(cleartextSig).to.exist; - expect(cleartextSig.data).to.deep.equal(plaintext); - 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); + }).then(function({ data, signatures }) { + expect(data).to.deep.equal(plaintext); + expect(signatures).to.have.length(1); + expect(signatures[0].valid).to.be.true; + expect(signatures[0].signature.packets.length).to.equal(1); }); }); @@ -1415,31 +1407,35 @@ hkJiXopCSWKSlQInL1devkJJUWJmTmZeugJYlpdLAagQJM0JpsCqIQZwKgAA const pubKey = await openpgp.readKey({ armoredKey: pub_key_arm2 }); const privKey = await openpgp.readKey({ armoredKey: priv_key_arm2 }); await privKey.decrypt('hello world'); - return openpgp.sign({ privateKeys:[privKey], message: openpgp.Message.fromText(plaintext), detached: true }).then(async function(armoredSignature) { + + const config = { minRsaBits: 1024 }; + return openpgp.sign({ privateKeys:[privKey], message: openpgp.Message.fromText(plaintext), detached: true, config }).then(async function(armoredSignature) { const signature = await openpgp.readSignature({ armoredSignature }); - return openpgp.verify({ publicKeys:[pubKey], message: openpgp.Message.fromBinary(util.encodeUtf8(plaintext)), signature: signature }); - }).then(function(cleartextSig) { - expect(cleartextSig).to.exist; - 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); + return openpgp.verify({ publicKeys:[pubKey], message: openpgp.Message.fromBinary(util.encodeUtf8(plaintext)), signature, config }); + }).then(function({ data, signatures }) { + expect(data).to.equal(plaintext); + expect(signatures).to.have.length(1); + expect(signatures[0].valid).to.be.true; + expect(signatures[0].signature.packets.length).to.equal(1); }); }); it('Should verify cleartext message correctly when using a detached binary signature and text literal data', async function () { - const plaintext = 'short message\nnext line \n한국어/조선말'; - const plaintextArray = util.encodeUtf8(plaintext); + const plaintext = util.encodeUtf8('short message\nnext line \n한국어/조선말'); + const binaryPlaintext = util.encodeUtf8(plaintext); const pubKey = await openpgp.readKey({ armoredKey: pub_key_arm2 }); const privKey = await openpgp.readKey({ armoredKey: priv_key_arm2 }); await privKey.decrypt('hello world'); - return openpgp.sign({ privateKeys:[privKey], message:openpgp.Message.fromBinary(plaintextArray), detached: true }).then(async function(armoredSignature) { + + const config = { minRsaBits: 1024 }; + return openpgp.sign({ privateKeys:[privKey], message:openpgp.Message.fromBinary(binaryPlaintext), detached: true, config }).then(async function(armoredSignature) { const signature = await openpgp.readSignature({ armoredSignature }); - return openpgp.verify({ publicKeys:[pubKey], message: openpgp.Message.fromText(plaintext), signature: signature }); - }).then(function(cleartextSig) { - expect(cleartextSig).to.exist; - 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); + return openpgp.verify({ publicKeys:[pubKey], message: openpgp.Message.fromText(plaintext), signature, config }); + }).then(function({ data, signatures }) { + expect(data).to.equal(plaintext); + expect(signatures).to.have.length(1); + expect(signatures[0].valid).to.be.true; + expect(signatures[0].signature.packets.length).to.equal(1); }); }); @@ -1447,18 +1443,22 @@ hkJiXopCSWKSlQInL1devkJJUWJmTmZeugJYlpdLAagQJM0JpsCqIQZwKgAA const plaintext = 'short message\nnext line \n한국어/조선말'; const pubKey = await openpgp.readKey({ armoredKey: pub_key_arm2 }); const privKey = await openpgp.readKey({ armoredKey: priv_key_arm2 }); - await Promise.all([privKey.primaryKey.decrypt('hello world'), privKey.subKeys[0].keyPacket.decrypt('hello world')]); - return openpgp.sign({ privateKeys:[privKey], message: openpgp.Message.fromText(plaintext), detached: true }).then(async function(armoredSignature) { + await privKey.decrypt('hello world'); + + const config = { minRsaBits: 1024 }; + return openpgp.sign({ privateKeys:[privKey], message: openpgp.Message.fromText(plaintext), detached: true, config }).then(async function(armoredSignature) { const signature = await openpgp.readSignature({ armoredSignature }); - return openpgp.encrypt({ message: openpgp.Message.fromBinary(util.encodeUtf8(plaintext)), publicKeys: [pubKey], signature }); + return openpgp.encrypt({ message: openpgp.Message.fromBinary(util.encodeUtf8(plaintext)), publicKeys: [pubKey], signature, config }); + }).then(async armoredMessage => { - const csMsg = await openpgp.readMessage({ armoredMessage }); - return openpgp.decrypt({ message: csMsg, privateKeys: [privKey], publicKeys: [pubKey] }); - }).then(function(cleartextSig) { - expect(cleartextSig).to.exist; - 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); + const message = await openpgp.readMessage({ armoredMessage }); + return openpgp.decrypt({ message, privateKeys: [privKey], publicKeys: [pubKey], config }); + + }).then(function({ data, signatures }) { + expect(data).to.equal(plaintext); + expect(signatures).to.have.length(1); + expect(signatures[0].valid).to.be.true; + expect(signatures[0].signature.packets.length).to.equal(1); }); }); @@ -1565,27 +1565,30 @@ hkJiXopCSWKSlQInL1devkJJUWJmTmZeugJYlpdLAagQJM0JpsCqIQZwKgAA const publicKeyArmored = '-----BEGIN PGP PUBLIC KEY BLOCK-----\r\nVersion: OpenPGP.js v.1.20131116\r\nComment: Whiteout Mail - https://whiteout.io\r\n\r\nxsBNBFKODs4BB/9iOF4THsjQMY+WEpT7ShgKxj4bHzRRaQkqczS4nZvP0U3g\r\nqeqCnbpagyeKXA+bhWFQW4GmXtgAoeD5PXs6AZYrw3tWNxLKu2Oe6Tp9K/XI\r\nxTMQ2wl4qZKDXHvuPsJ7cmgaWqpPyXtxA4zHHS3WrkI/6VzHAcI/y6x4szSB\r\nKgSuhI3hjh3s7TybUC1U6AfoQGx/S7e3WwlCOrK8GTClirN/2mCPRC5wuIft\r\nnkoMfA6jK8d2OPrJ63shy5cgwHOjQg/xuk46dNS7tkvGmbaa+X0PgqSKB+Hf\r\nYPPNS/ylg911DH9qa8BqYU2QpNh9jUKXSF+HbaOM+plWkCSAL7czV+R3ABEB\r\nAAHNLVdoaXRlb3V0IFVzZXIgPHNhZmV3aXRobWUudGVzdHVzZXJAZ21haWwu\r\nY29tPsLAXAQQAQgAEAUCUo4O2gkQ1/uT/N+/wjwAAN2cB/9gFRmAfvEQ2qz+\r\nWubmT2EsSSnjPMxzG4uyykFoa+TaZCWo2Xa2tQghmU103kEkQb1OEjRjpgwJ\r\nYX9Kghnl8DByM686L5AXnRyHP78qRJCLXSXl0AGicboUDp5sovaa4rswQceH\r\nvcdWgZ/mgHTRoiQeJddy9k+H6MPFiyFaVcFwegVsmpc+dCcC8yT+qh8ZIbyG\r\nRJU60PmKKN7LUusP+8DbSv39zCGJCBlVVKyA4MzdF5uM+sqTdXbKzOrT5DGd\r\nCZaox4s+w16Sq1rHzZKFWfQPfKLDB9pyA0ufCVRA3AF6BUi7G3ZqhZiHNhMP\r\nNvE45V/hS1PbZcfPVoUjE2qc1Ix1\r\n=7Wpe\r\n-----END PGP PUBLIC KEY BLOCK-----'; const publicKey = await openpgp.readKey({ armoredKey: publicKeyArmored }); - // Text - const msg = openpgp.Message.fromText(content); - await msg.appendSignature(detachedSig); - return msg.verify([publicKey]).then(async result => { - openpgp.stream.pipe(msg.getLiteralData(), new openpgp.stream.WritableStream()); - expect(await result[0].verified).to.be.true; - }); + const message = openpgp.Message.fromText(content); + await message.appendSignature(detachedSig); + const { data, signatures } = await openpgp.verify({ publicKeys:[publicKey], message, config: { minRsaBits: 1024 } }); + expect(data).to.equal(content); + expect(signatures).to.have.length(1); + expect(signatures[0].valid).to.be.true; + expect(signatures[0].signature.packets.length).to.equal(1); + expect(await signatures[0].verified).to.be.true; }); it('Detached signature signing and verification', async function() { - const msg = openpgp.Message.fromText('hello'); + const message = openpgp.Message.fromText('hello'); const pubKey2 = await openpgp.readKey({ armoredKey: pub_key_arm2 }); const privKey2 = await openpgp.readKey({ armoredKey: priv_key_arm2 }); await privKey2.decrypt('hello world'); const opt = { rsaBits: 2048, userIds: { name:'test', email:'a@b.com' }, passphrase: null }; const { key: generatedKey } = await openpgp.generateKey(opt); - const detachedSig = await msg.signDetached([generatedKey, privKey2]); - const result = await msg.verifyDetached(detachedSig, [generatedKey.toPublic(), pubKey2]); - expect(await result[0].verified).to.be.true; - expect(await result[1].verified).to.be.true; + const armoredSignature = await openpgp.sign({ privateKeys:[generatedKey, privKey2], message, detached: true, config: { minRsaBits: 1024 } }); + const signature = await openpgp.readSignature({ armoredSignature }); + const { data, signatures } = await openpgp.verify({ publicKeys:[generatedKey.toPublic(), pubKey2], message, signature, config: { minRsaBits: 1024 } }); + expect(data).to.equal('hello'); + expect(await signatures[0].verified).to.be.true; + expect(await signatures[1].verified).to.be.true; }); it('Sign message with key without password', function() { @@ -1724,11 +1727,12 @@ CftAJDBez44ZofZ8ahPfkAhJe6opxaqgS47s4FIQVOEJcF9RgwLTU6uooSzA oaBUyhCKt8tz6Q== =52k1 -----END PGP PRIVATE KEY BLOCK-----`; - const key = await openpgp.readKey({ armoredKey: armoredKey }); + const key = await openpgp.readKey({ armoredKey }); const decrypted = await openpgp.decrypt({ message: await openpgp.readMessage({ armoredMessage: encrypted }), publicKeys: key, - privateKeys: key + privateKeys: key, + config: { minRsaBits: 1024 } }); expect(decrypted.signatures[0].valid).to.be.true; }); diff --git a/test/general/x25519.js b/test/general/x25519.js index efe29150f..d10cc5c62 100644 --- a/test/general/x25519.js +++ b/test/general/x25519.js @@ -409,7 +409,7 @@ function omnibus() { certificate.verified = null; await user.verifyCertificate( - primaryKey, certificate, [hi.toPublic()] + primaryKey, certificate, [hi.toPublic()], undefined, openpgp.config ).then(async () => expect(certificate.verified).to.be.true); const options = { @@ -432,7 +432,7 @@ function omnibus() { ).then(async () => expect(certificate.verified).to.be.true); certificate.verified = null; await user.verifyCertificate( - bye.primaryKey, user.selfCertifications[0], [bye.toPublic()] + bye.primaryKey, user.selfCertifications[0], [bye.toPublic()], undefined, openpgp.config ).then(async () => expect(certificate.verified).to.be.true); return Promise.all([ From 6f44a675ee102f63ee2bb99d658f373a67b31afc Mon Sep 17 00:00:00 2001 From: larabr <7375870+larabr@users.noreply.github.com> Date: Wed, 10 Mar 2021 00:00:24 +0100 Subject: [PATCH 05/13] Fix more tests --- test/general/openpgp.js | 343 +++++++++++++++++++++++----------------- 1 file changed, 194 insertions(+), 149 deletions(-) diff --git a/test/general/openpgp.js b/test/general/openpgp.js index f8d3dab7d..55ee53371 100644 --- a/test/general/openpgp.js +++ b/test/general/openpgp.js @@ -821,11 +821,13 @@ module.exports = () => describe('OpenPGP.js public api tests', function() { let privateKey; let publicKey; let publicKeyNoAEAD; + let privateKeyMismatchingParams; + let aeadProtectVal; let preferredAeadAlgorithmVal; let aeadChunkSizeByteVal; let v5KeysVal; - let privateKeyMismatchingParams; + let minRsaBitsVal; beforeEach(async function() { publicKey = await openpgp.readKey({ armoredKey: pub_key }); @@ -843,6 +845,9 @@ module.exports = () => describe('OpenPGP.js public api tests', function() { preferredAeadAlgorithmVal = openpgp.config.preferredAeadAlgorithm; aeadChunkSizeByteVal = openpgp.config.aeadChunkSizeByte; v5KeysVal = openpgp.config.v5Keys; + minRsaBitsVal = openpgp.config.minRsaBits; + + openpgp.config.minRsaBits = 512; }); afterEach(function() { @@ -850,6 +855,7 @@ module.exports = () => describe('OpenPGP.js public api tests', function() { openpgp.config.preferredAeadAlgorithm = preferredAeadAlgorithmVal; openpgp.config.aeadChunkSizeByte = aeadChunkSizeByteVal; openpgp.config.v5Keys = v5KeysVal; + openpgp.config.minRsaBits = minRsaBitsVal; }); it('Configuration', async function() { @@ -1547,48 +1553,55 @@ module.exports = () => describe('OpenPGP.js public api tests', function() { }); it('should encrypt and decrypt/verify with detached signature as input for encryption', async function () { - const plaintext = "  \t┍ͤ޵၂༫዇◧˘˻ᙑ᎚⏴ំந⛑nٓኵΉⅶ⋋ŵ⋲΂ͽᣏ₅ᄶɼ┋⌔û᬴Ƚᔡᧅ≃ṱἆ⃷݂૿ӌ᰹෇ٹჵ⛇໶⛌  \t\n한국어/조선말"; + const { rejectPublicKeyAlgorithms } = openpgp.config; + try { + openpgp.config.rejectPublicKeyAlgorithms = new Set(); - const privKeyDE = await openpgp.readKey({ armoredKey: priv_key_de }); - await privKeyDE.decrypt(passphrase); + const plaintext = "  \t┍ͤ޵၂༫዇◧˘˻ᙑ᎚⏴ំந⛑nٓኵΉⅶ⋋ŵ⋲΂ͽᣏ₅ᄶɼ┋⌔û᬴Ƚᔡᧅ≃ṱἆ⃷݂૿ӌ᰹෇ٹჵ⛇໶⛌  \t\n한국어/조선말"; - const pubKeyDE = await openpgp.readKey({ armoredKey: pub_key_de }); + const privKeyDE = await openpgp.readKey({ armoredKey: priv_key_de }); + await privKeyDE.decrypt(passphrase); - const signOpt = { - message: openpgp.Message.fromText(plaintext), - privateKeys: privKeyDE, - detached: true - }; + const pubKeyDE = await openpgp.readKey({ armoredKey: pub_key_de }); - const encOpt = { - message: openpgp.Message.fromText(plaintext), - publicKeys: publicKey, - privateKeys: privateKey - }; + const signOpt = { + message: openpgp.Message.fromText(plaintext), + privateKeys: privKeyDE, + detached: true + }; - const decOpt = { - privateKeys: privateKey, - publicKeys: [publicKey, pubKeyDE] - }; + const encOpt = { + message: openpgp.Message.fromText(plaintext), + publicKeys: publicKey, + privateKeys: privateKey + }; - return openpgp.sign(signOpt).then(async function (armoredSignature) { - encOpt.signature = await openpgp.readSignature({ armoredSignature }); - return openpgp.encrypt(encOpt); - }).then(async function (armoredMessage) { - decOpt.message = await openpgp.readMessage({ armoredMessage }); - return openpgp.decrypt(decOpt); - }).then(async function (decrypted) { - let signingKey; - expect(decrypted.data).to.equal(plaintext); - expect(decrypted.signatures[0].valid).to.be.true; - signingKey = await privateKey.getSigningKey(); - expect(decrypted.signatures[0].keyid.toHex()).to.equal(signingKey.getKeyId().toHex()); - expect(decrypted.signatures[0].signature.packets.length).to.equal(1); - expect(decrypted.signatures[1].valid).to.be.true; - signingKey = await privKeyDE.getSigningKey(); - expect(decrypted.signatures[1].keyid.toHex()).to.equal(signingKey.getKeyId().toHex()); - expect(decrypted.signatures[1].signature.packets.length).to.equal(1); - }); + const decOpt = { + privateKeys: privateKey, + publicKeys: [publicKey, pubKeyDE] + }; + + await openpgp.sign(signOpt).then(async function (armoredSignature) { + encOpt.signature = await openpgp.readSignature({ armoredSignature }); + return openpgp.encrypt(encOpt); + }).then(async function (armoredMessage) { + decOpt.message = await openpgp.readMessage({ armoredMessage }); + return openpgp.decrypt(decOpt); + }).then(async function (decrypted) { + let signingKey; + expect(decrypted.data).to.equal(plaintext); + expect(decrypted.signatures[0].valid).to.be.true; + signingKey = await privateKey.getSigningKey(); + expect(decrypted.signatures[0].keyid.toHex()).to.equal(signingKey.getKeyId().toHex()); + expect(decrypted.signatures[0].signature.packets.length).to.equal(1); + expect(decrypted.signatures[1].valid).to.be.true; + signingKey = await privKeyDE.getSigningKey(); + expect(decrypted.signatures[1].keyid.toHex()).to.equal(signingKey.getKeyId().toHex()); + expect(decrypted.signatures[1].signature.packets.length).to.equal(1); + }); + } finally { + openpgp.config.rejectPublicKeyAlgorithms = rejectPublicKeyAlgorithms; + } }); it('should fail to encrypt and decrypt/verify with detached signature as input for encryption with wrong public key', async function () { @@ -1614,12 +1627,13 @@ module.exports = () => describe('OpenPGP.js public api tests', function() { }).then(async function (armoredMessage) { decOpt.message = await openpgp.readMessage({ armoredMessage }); return openpgp.decrypt(decOpt); - }).then(async function (decrypted) { - expect(decrypted.data).to.equal(plaintext); - expect(decrypted.signatures[0].valid).to.be.null; + }).then(async function ({ signatures, data }) { + expect(data).to.equal(plaintext); + expect(signatures[0].valid).to.be.false; + expect(signatures[0].error).to.match(/Could not find key/); const signingKey = await privateKey.getSigningKey(); - expect(decrypted.signatures[0].keyid.toHex()).to.equal(signingKey.getKeyId().toHex()); - expect(decrypted.signatures[0].signature.packets.length).to.equal(1); + expect(signatures[0].keyid.toHex()).to.equal(signingKey.getKeyId().toHex()); + expect(signatures[0].signature.packets.length).to.equal(1); }); }); @@ -1636,12 +1650,13 @@ module.exports = () => describe('OpenPGP.js public api tests', function() { return openpgp.encrypt(encOpt).then(async function (encrypted) { decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted }); return openpgp.decrypt(decOpt); - }).then(async function (decrypted) { - expect(decrypted.data).to.equal(plaintext); - expect(decrypted.signatures[0].valid).to.be.null; + }).then(async function ({ signatures, data }) { + expect(data).to.equal(plaintext); + expect(signatures[0].valid).to.be.false; + expect(signatures[0].error).to.match(/Could not find key/); const signingKey = await privateKey.getSigningKey(); - expect(decrypted.signatures[0].keyid.toHex()).to.equal(signingKey.getKeyId().toHex()); - expect(decrypted.signatures[0].signature.packets.length).to.equal(1); + expect(signatures[0].keyid.toHex()).to.equal(signingKey.getKeyId().toHex()); + expect(signatures[0].signature.packets.length).to.equal(1); }); }); @@ -1658,12 +1673,13 @@ module.exports = () => describe('OpenPGP.js public api tests', function() { return openpgp.encrypt(encOpt).then(async function (encrypted) { decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted }); return openpgp.decrypt(decOpt); - }).then(async function (decrypted) { - expect(decrypted.data).to.equal(''); - expect(decrypted.signatures[0].valid).to.be.null; + }).then(async function ({ signatures, data }) { + expect(data).to.equal(''); + expect(signatures[0].valid).to.be.false; + expect(signatures[0].error).to.match(/Could not find key/); const signingKey = await privateKey.getSigningKey(); - expect(decrypted.signatures[0].keyid.toHex()).to.equal(signingKey.getKeyId().toHex()); - expect(decrypted.signatures[0].signature.packets.length).to.equal(1); + expect(signatures[0].keyid.toHex()).to.equal(signingKey.getKeyId().toHex()); + expect(signatures[0].signature.packets.length).to.equal(1); }); }); @@ -1679,12 +1695,13 @@ module.exports = () => describe('OpenPGP.js public api tests', function() { return openpgp.encrypt(encOpt).then(async function (encrypted) { decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted }); return openpgp.decrypt(decOpt); - }).then(async function (decrypted) { - expect(decrypted.data).to.equal(plaintext); - expect(decrypted.signatures[0].valid).to.be.null; + }).then(async function ({ signatures, data }) { + expect(data).to.equal(plaintext); + expect(signatures[0].valid).to.be.false; + expect(signatures[0].error).to.match(/Could not find key/); const signingKey = await privateKey.getSigningKey(); - expect(decrypted.signatures[0].keyid.toHex()).to.equal(signingKey.getKeyId().toHex()); - expect(decrypted.signatures[0].signature.packets.length).to.equal(1); + expect(signatures[0].keyid.toHex()).to.equal(signingKey.getKeyId().toHex()); + expect(signatures[0].signature.packets.length).to.equal(1); }); }); @@ -1698,51 +1715,59 @@ module.exports = () => describe('OpenPGP.js public api tests', function() { privateKeys: privateKey, detached: true }); - const decrypted = await openpgp.decrypt({ + const { signatures, data } = await openpgp.decrypt({ message: await openpgp.readMessage({ armoredMessage: encrypted }), signature: await openpgp.readSignature({ armoredSignature: signed }), privateKeys: privateKey, publicKeys: await openpgp.readKey({ armoredKey: wrong_pubkey }) }); - expect(decrypted.data).to.equal(plaintext); - expect(decrypted.signatures[0].valid).to.be.null; + expect(data).to.equal(plaintext); + expect(signatures[0].valid).to.be.false; + expect(signatures[0].error).to.match(/Could not find key/); const signingKey = await privateKey.getSigningKey(); - expect(decrypted.signatures[0].keyid.toHex()).to.equal(signingKey.getKeyId().toHex()); - expect(decrypted.signatures[0].signature.packets.length).to.equal(1); + expect(signatures[0].keyid.toHex()).to.equal(signingKey.getKeyId().toHex()); + expect(signatures[0].signature.packets.length).to.equal(1); }); it('should encrypt and decrypt/verify both signatures when signed with two private keys', async function () { - const privKeyDE = await openpgp.readKey({ armoredKey: priv_key_de }); - await privKeyDE.decrypt(passphrase); + const { rejectPublicKeyAlgorithms } = openpgp.config; + try { + openpgp.config.rejectPublicKeyAlgorithms = new Set(); - const pubKeyDE = await openpgp.readKey({ armoredKey: pub_key_de }); + const privKeyDE = await openpgp.readKey({ armoredKey: priv_key_de }); + await privKeyDE.decrypt(passphrase); - const encOpt = { - message: openpgp.Message.fromText(plaintext), - publicKeys: publicKey, - privateKeys: [privateKey, privKeyDE] - }; + const pubKeyDE = await openpgp.readKey({ armoredKey: pub_key_de }); - const decOpt = { - privateKeys: privateKey, - publicKeys: [publicKey, pubKeyDE] - }; + const encOpt = { + message: openpgp.Message.fromText(plaintext), + publicKeys: publicKey, + privateKeys: [privateKey, privKeyDE] + }; - return openpgp.encrypt(encOpt).then(async function (encrypted) { - decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted }); - return openpgp.decrypt(decOpt); - }).then(async function (decrypted) { - let signingKey; - expect(decrypted.data).to.equal(plaintext); - expect(decrypted.signatures[0].valid).to.be.true; - signingKey = await privateKey.getSigningKey(); - expect(decrypted.signatures[0].keyid.toHex()).to.equal(signingKey.getKeyId().toHex()); - expect(decrypted.signatures[0].signature.packets.length).to.equal(1); - expect(decrypted.signatures[1].valid).to.be.true; - signingKey = await privKeyDE.getSigningKey(); - expect(decrypted.signatures[1].keyid.toHex()).to.equal(signingKey.getKeyId().toHex()); - expect(decrypted.signatures[1].signature.packets.length).to.equal(1); - }); + const decOpt = { + privateKeys: privateKey, + publicKeys: [publicKey, pubKeyDE] + }; + + await openpgp.encrypt(encOpt).then(async function (encrypted) { + decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted }); + return openpgp.decrypt(decOpt); + }).then(async function (decrypted) { + let signingKey; + expect(decrypted.data).to.equal(plaintext); + expect(decrypted.signatures[0].valid).to.be.true; + signingKey = await privateKey.getSigningKey(); + expect(decrypted.signatures[0].keyid.toHex()).to.equal(signingKey.getKeyId().toHex()); + expect(decrypted.signatures[0].signature.packets.length).to.equal(1); + expect(decrypted.signatures[1].valid).to.be.true; + signingKey = await privKeyDE.getSigningKey(); + expect(decrypted.signatures[1].keyid.toHex()).to.equal(signingKey.getKeyId().toHex()); + expect(decrypted.signatures[1].signature.packets.length).to.equal(1); + }); + } finally { + openpgp.config.rejectPublicKeyAlgorithms = rejectPublicKeyAlgorithms; + } }); it('should fail to decrypt modified message', async function() { @@ -1812,28 +1837,35 @@ module.exports = () => describe('OpenPGP.js public api tests', function() { describe('ELG / DSA encrypt, decrypt, sign, verify', function() { it('round trip test', async function () { - const pubKeyDE = await openpgp.readKey({ armoredKey: pub_key_de }); - const privKeyDE = await openpgp.readKey({ armoredKey: priv_key_de }); - await privKeyDE.decrypt(passphrase); - pubKeyDE.users[0].selfCertifications[0].features = [7]; // Monkey-patch AEAD feature flag - return openpgp.encrypt({ - publicKeys: pubKeyDE, - privateKeys: privKeyDE, - message: openpgp.Message.fromText(plaintext) - }).then(async function (encrypted) { - return openpgp.decrypt({ - privateKeys: privKeyDE, + const { rejectPublicKeyAlgorithms } = openpgp.config; + try { + openpgp.config.rejectPublicKeyAlgorithms = new Set(); + + const pubKeyDE = await openpgp.readKey({ armoredKey: pub_key_de }); + const privKeyDE = await openpgp.readKey({ armoredKey: priv_key_de }); + await privKeyDE.decrypt(passphrase); + pubKeyDE.users[0].selfCertifications[0].features = [7]; // Monkey-patch AEAD feature flag + await openpgp.encrypt({ publicKeys: pubKeyDE, - message: await openpgp.readMessage({ armoredMessage: encrypted }) + privateKeys: privKeyDE, + message: openpgp.Message.fromText(plaintext) + }).then(async function (encrypted) { + return openpgp.decrypt({ + privateKeys: privKeyDE, + publicKeys: pubKeyDE, + message: await openpgp.readMessage({ armoredMessage: encrypted }) + }); + }).then(async function (decrypted) { + expect(decrypted.data).to.exist; + expect(decrypted.data).to.equal(plaintext); + expect(decrypted.signatures[0].valid).to.be.true; + const signingKey = await privKeyDE.getSigningKey(); + expect(decrypted.signatures[0].keyid.toHex()).to.equal(signingKey.getKeyId().toHex()); + expect(decrypted.signatures[0].signature.packets.length).to.equal(1); }); - }).then(async function (decrypted) { - expect(decrypted.data).to.exist; - expect(decrypted.data).to.equal(plaintext); - expect(decrypted.signatures[0].valid).to.be.true; - const signingKey = await privKeyDE.getSigningKey(); - expect(decrypted.signatures[0].keyid.toHex()).to.equal(signingKey.getKeyId().toHex()); - expect(decrypted.signatures[0].signature.packets.length).to.equal(1); - }); + } finally { + openpgp.config.rejectPublicKeyAlgorithms = rejectPublicKeyAlgorithms; + } }); }); @@ -2078,33 +2110,40 @@ module.exports = () => describe('OpenPGP.js public api tests', function() { }); it('should sign and verify cleartext message with multiple private keys', async function () { - const privKeyDE = await openpgp.readKey({ armoredKey: priv_key_de }); - await privKeyDE.decrypt(passphrase); + const { rejectPublicKeyAlgorithms } = openpgp.config; + try { + openpgp.config.rejectPublicKeyAlgorithms = new Set(); - const message = openpgp.CleartextMessage.fromText(plaintext); - const signOpt = { - message, - privateKeys: [privateKey, privKeyDE] - }; - const verifyOpt = { - publicKeys: [publicKey, privKeyDE.toPublic()] - }; - return openpgp.sign(signOpt).then(async function (signed) { - expect(signed).to.match(/-----BEGIN PGP SIGNED MESSAGE-----/); - verifyOpt.message = await openpgp.readCleartextMessage({ cleartextMessage: signed }); - return openpgp.verify(verifyOpt); - }).then(async function (verified) { - let signingKey; - expect(verified.data).to.equal(plaintext.replace(/[ \t]+$/mg, '')); - expect(verified.signatures[0].valid).to.be.true; - signingKey = await privateKey.getSigningKey(); - expect(verified.signatures[0].keyid.toHex()).to.equal(signingKey.getKeyId().toHex()); - expect(verified.signatures[0].signature.packets.length).to.equal(1); - expect(verified.signatures[1].valid).to.be.true; - signingKey = await privKeyDE.getSigningKey(); - expect(verified.signatures[1].keyid.toHex()).to.equal(signingKey.getKeyId().toHex()); - expect(verified.signatures[1].signature.packets.length).to.equal(1); - }); + const privKeyDE = await openpgp.readKey({ armoredKey: priv_key_de }); + await privKeyDE.decrypt(passphrase); + + const message = openpgp.CleartextMessage.fromText(plaintext); + const signOpt = { + message, + privateKeys: [privateKey, privKeyDE] + }; + const verifyOpt = { + publicKeys: [publicKey, privKeyDE.toPublic()] + }; + await openpgp.sign(signOpt).then(async function (signed) { + expect(signed).to.match(/-----BEGIN PGP SIGNED MESSAGE-----/); + verifyOpt.message = await openpgp.readCleartextMessage({ cleartextMessage: signed }); + return openpgp.verify(verifyOpt); + }).then(async function (verified) { + let signingKey; + expect(verified.data).to.equal(plaintext.replace(/[ \t]+$/mg, '')); + expect(verified.signatures[0].valid).to.be.true; + signingKey = await privateKey.getSigningKey(); + expect(verified.signatures[0].keyid.toHex()).to.equal(signingKey.getKeyId().toHex()); + expect(verified.signatures[0].signature.packets.length).to.equal(1); + expect(verified.signatures[1].valid).to.be.true; + signingKey = await privKeyDE.getSigningKey(); + expect(verified.signatures[1].keyid.toHex()).to.equal(signingKey.getKeyId().toHex()); + expect(verified.signatures[1].signature.packets.length).to.equal(1); + }); + } finally { + openpgp.config.rejectPublicKeyAlgorithms = rejectPublicKeyAlgorithms; + } }); it('should sign and verify data with detached signatures', function () { @@ -2142,12 +2181,13 @@ module.exports = () => describe('OpenPGP.js public api tests', function() { return openpgp.sign(signOpt).then(async function (signed) { verifyOpt.message = await openpgp.readCleartextMessage({ cleartextMessage: signed }); return openpgp.verify(verifyOpt); - }).then(async function (verified) { - expect(verified.data).to.equal(plaintext.replace(/[ \t]+$/mg, '')); - expect(verified.signatures[0].valid).to.be.null; + }).then(async function ({ data, signatures }) { + expect(data).to.equal(plaintext.replace(/[ \t]+$/mg, '')); + expect(signatures[0].valid).to.be.false; + expect(signatures[0].error).to.match(/Could not find key/); const signingKey = await privateKey.getSigningKey(); - expect(verified.signatures[0].keyid.toHex()).to.equal(signingKey.getKeyId().toHex()); - expect(verified.signatures[0].signature.packets.length).to.equal(1); + expect(signatures[0].keyid.toHex()).to.equal(signingKey.getKeyId().toHex()); + expect(signatures[0].signature.packets.length).to.equal(1); }); }); @@ -2165,12 +2205,13 @@ module.exports = () => describe('OpenPGP.js public api tests', function() { return openpgp.sign(signOpt).then(async function (armoredSignature) { verifyOpt.signature = await openpgp.readSignature({ armoredSignature }); return openpgp.verify(verifyOpt); - }).then(async function (verified) { - expect(verified.data).to.equal(plaintext); - expect(verified.signatures[0].valid).to.be.null; + }).then(async function ({ data, signatures }) { + expect(data).to.equal(plaintext); + expect(signatures[0].valid).to.be.false; + expect(signatures[0].error).to.match(/Could not find key/); const signingKey = await privateKey.getSigningKey(); - expect(verified.signatures[0].keyid.toHex()).to.equal(signingKey.getKeyId().toHex()); - expect(verified.signatures[0].signature.packets.length).to.equal(1); + expect(signatures[0].keyid.toHex()).to.equal(signingKey.getKeyId().toHex()); + expect(signatures[0].signature.packets.length).to.equal(1); }); }); @@ -2500,7 +2541,8 @@ module.exports = () => describe('OpenPGP.js public api tests', function() { pubKeyDE.subKeys[0] = revSubKey; return openpgp.encrypt({ message: openpgp.Message.fromText(plaintext), - publicKeys: pubKeyDE + publicKeys: pubKeyDE, + config: { rejectPublicKeyAlgorithms: new Set() } }).then(function() { throw new Error('Should not encrypt with revoked subkey'); }).catch(function(error) { @@ -2515,12 +2557,14 @@ module.exports = () => describe('OpenPGP.js public api tests', function() { await privKeyDE.decrypt(passphrase); const encrypted = await openpgp.encrypt({ message: openpgp.Message.fromText(plaintext), - publicKeys: pubKeyDE + publicKeys: pubKeyDE, + config: { rejectPublicKeyAlgorithms: new Set() } }); privKeyDE.subKeys[0] = await privKeyDE.subKeys[0].revoke(privKeyDE.primaryKey); const decOpt = { message: await openpgp.readMessage({ armoredMessage: encrypted }), - privateKeys: privKeyDE + privateKeys: privKeyDE, + config: { rejectPublicKeyAlgorithms: new Set() } }; const decrypted = await openpgp.decrypt(decOpt); expect(decrypted.data).to.equal(plaintext); @@ -2535,7 +2579,8 @@ module.exports = () => describe('OpenPGP.js public api tests', function() { await privKeyDE.decrypt(passphrase); const encrypted = await openpgp.encrypt({ message: openpgp.Message.fromText(plaintext), - publicKeys: pubKeyDE + publicKeys: pubKeyDE, + config: { rejectPublicKeyAlgorithms: new Set() } }); const decOpt = { message: await openpgp.readMessage({ armoredMessage: encrypted }), From 0d66b3f984f4d852810b204bdcba761010205c4b Mon Sep 17 00:00:00 2001 From: larabr <7375870+larabr@users.noreply.github.com> Date: Wed, 10 Mar 2021 11:21:15 +0100 Subject: [PATCH 06/13] Dreamt of integer division in js --- src/util.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util.js b/src/util.js index 605a4c4a9..612d6c8d9 100644 --- a/src/util.js +++ b/src/util.js @@ -196,7 +196,7 @@ const util = { if (bitSize === 0) { throw new Error('Zero MPI'); } - const stripped = bin.subarray(bin.length - ((bitSize + 7) / 8)); + const stripped = bin.subarray(bin.length - Math.ceil(bitSize / 8)); const prefix = Uint8Array.from([(bitSize & 0xFF00) >> 8, bitSize & 0xFF]); return util.concatUint8Array([prefix, stripped]); }, From 83cb80c601b006afebee9d304e0463585e109e92 Mon Sep 17 00:00:00 2001 From: larabr <7375870+larabr@users.noreply.github.com> Date: Wed, 10 Mar 2021 12:59:25 +0100 Subject: [PATCH 07/13] Finish with tests --- test/general/config.js | 57 ++++++++++++++++++++++++++++++++++- test/general/streaming.js | 50 +++++++++++++++++++----------- test/security/subkey_trust.js | 3 +- 3 files changed, 91 insertions(+), 19 deletions(-) diff --git a/test/general/config.js b/test/general/config.js index f43f8df22..aa9b79e99 100644 --- a/test/general/config.js +++ b/test/general/config.js @@ -62,7 +62,8 @@ module.exports = () => describe('Custom configuration', function() { const config = { showComment: true, preferredCompressionAlgorithm: openpgp.enums.compression.zip, - preferredHashAlgorithm: openpgp.enums.hash.sha512 + preferredHashAlgorithm: openpgp.enums.hash.sha512, + rejectPublicKeyAlgorithms: new Set([openpgp.enums.publicKey.eddsa]) // should not matter in this context }; const opt2 = { privateKey: origKey, userIds, config }; const { key: refKey2, privateKeyArmored: refKeyArmored2 } = await openpgp.reformatKey(opt2); @@ -171,12 +172,54 @@ module.exports = () => describe('Custom configuration', function() { const { packets: [compressed] } = await encrypted2.decrypt(null, passwords, null, encrypted2.fromStream, openpgp.config); expect(compressed.tag).to.equal(openpgp.enums.packet.compressedData); expect(compressed.algorithm).to.equal("zip"); + + const userIds = { name: 'Test User', email: 'text2@example.com' }; + const { key } = await openpgp.generateKey({ userIds }); + await expect(openpgp.encrypt({ + message, publicKeys: [key], config: { rejectPublicKeyAlgorithms: new Set([openpgp.enums.publicKey.ecdh]) } + })).to.be.eventually.rejectedWith(/ecdh keys are considered too weak/); } finally { openpgp.config.aeadProtect = aeadProtectVal; openpgp.config.preferredCompressionAlgorithm = preferredCompressionAlgorithmVal; } }); + it('openpgp.decrypt', async function() { + const plaintext = 'test'; + const message = openpgp.Message.fromText(plaintext); + const userIds = { name: 'Test User', email: 'text2@example.com' }; + const { key } = await openpgp.generateKey({ userIds, type: 'rsa', rsaBits: 2048 }); + + const armoredMessage = await openpgp.encrypt({ message, publicKeys:[key], privateKeys: [key] }); + const { data, signatures } = await openpgp.decrypt({ + message: await openpgp.readMessage({ armoredMessage }), + privateKeys: [key], + publicKeys: [key] + }); + expect(data).to.equal(plaintext); + expect(signatures[0].valid).to.be.true; + + const { data: data2, signatures: signatures2 } = await openpgp.decrypt({ + message: await openpgp.readMessage({ armoredMessage }), + privateKeys: [key], + publicKeys: [key], + config: { minRsaBits: 4096 } + }); + expect(data2).to.equal(plaintext); + expect(signatures2[0].valid).to.be.false; + expect(signatures2[0].error).to.match(/keys shorter than 4096 bits are considered too weak/); + + const { data: data3, signatures: signatures3 } = await openpgp.decrypt({ + message: await openpgp.readMessage({ armoredMessage }), + privateKeys: [key], + publicKeys: [key], + config: { rejectPublicKeyAlgorithms: new Set([openpgp.enums.publicKey.rsaEncryptSign]) } + }); + expect(data3).to.equal(plaintext); + expect(signatures3[0].valid).to.be.false; + expect(signatures3[0].error).to.match(/rsaEncryptSign keys are considered too weak/); + }); + it('openpgp.sign', async function() { const userIds = { name: 'Test User', email: 'text2@example.com' }; const { privateKeyArmored } = await openpgp.generateKey({ userIds }); @@ -199,6 +242,10 @@ module.exports = () => describe('Custom configuration', function() { config: { rejectHashAlgorithms: new Set([openpgp.enums.hash.sha256, openpgp.enums.hash.sha512]) } }; await expect(openpgp.sign(opt2)).to.be.rejectedWith(/Insecure hash algorithm/); + + await expect(openpgp.sign({ + message, privateKeys: [key], config: { rejectPublicKeyAlgorithms: new Set([openpgp.enums.publicKey.eddsa]) } + })).to.be.eventually.rejectedWith(/eddsa keys are considered too weak/); }); it('openpgp.verify', async function() { @@ -237,6 +284,14 @@ module.exports = () => describe('Custom configuration', function() { const { signatures: [sig3] } = await openpgp.verify(opt3); await expect(sig3.error).to.match(/Insecure message hash algorithm/); + const opt4 = { + message: await openpgp.readMessage({ armoredMessage: signed }), + publicKeys: [key], + config: { rejectPublicKeyAlgorithms: new Set([openpgp.enums.publicKey.eddsa]) } + }; + const { signatures: [sig4] } = await openpgp.verify(opt4); + await expect(sig4.valid).to.be.false; + await expect(sig4.error).to.match(/eddsa keys are considered too weak/); }); }); diff --git a/test/general/streaming.js b/test/general/streaming.js index 132317f3b..0076f1109 100644 --- a/test/general/streaming.js +++ b/test/general/streaming.js @@ -237,7 +237,8 @@ function tests() { it('Sign: Input stream should be canceled when canceling encrypted stream', async function() { const signed = await openpgp.sign({ message: openpgp.Message.fromBinary(data), - privateKeys: privKey + privateKeys: privKey, + config: { minRsaBits: 1024 } }); const reader = openpgp.stream.getReader(signed); expect(await reader.readBytes(1024)).to.match(/^-----BEGIN PGP MESSAGE-----\n/); @@ -312,7 +313,8 @@ function tests() { message: openpgp.Message.fromBinary(data), publicKeys: pubKey, privateKeys: privKey, - armor: false + armor: false, + config: { minRsaBits: 1024 } }); expect(openpgp.stream.isStream(encrypted)).to.equal(expectedType); @@ -443,7 +445,8 @@ function tests() { const encrypted = await openpgp.encrypt({ message: openpgp.Message.fromBinary(data), publicKeys: pubKey, - privateKeys: privKey + privateKeys: privKey, + config: { minRsaBits: 1024 } }); expect(openpgp.stream.isStream(encrypted)).to.equal(expectedType); @@ -480,7 +483,8 @@ function tests() { const encrypted = await openpgp.encrypt({ message: openpgp.Message.fromBinary(data), publicKeys: pubKey, - privateKeys: privKey + privateKeys: privKey, + config: { minRsaBits: 1024 } }); expect(openpgp.stream.isStream(encrypted)).to.equal(expectedType); @@ -504,7 +508,7 @@ function tests() { dataArrived(); await expect(reader.readToEnd()).to.be.rejectedWith('Ascii armor integrity check on message failed'); expect(decrypted.signatures).to.exist.and.have.length(1); - expect(await decrypted.signatures[0].verified).to.be.null; + await expect(decrypted.signatures[0].verified).to.be.eventually.rejectedWith(/Could not find key/); } finally { openpgp.config.allowUnauthenticatedStream = allowUnauthenticatedStreamValue; } @@ -513,7 +517,8 @@ function tests() { it('Sign/verify: Detect armor checksum error', async function() { const signed = await openpgp.sign({ message: openpgp.Message.fromBinary(data), - privateKeys: privKey + privateKeys: privKey, + config: { minRsaBits: 1024 } }); expect(openpgp.stream.isStream(signed)).to.equal(expectedType); @@ -529,7 +534,8 @@ function tests() { publicKeys: pubKey, message, streaming: expectedType, - format: 'binary' + format: 'binary', + config: { minRsaBits: 1024 } }); expect(openpgp.stream.isStream(verified.data)).to.equal(expectedType); const reader = openpgp.stream.getReader(verified.data); @@ -567,7 +573,8 @@ function tests() { it('Sign/verify: Input stream should be canceled when canceling verified stream', async function() { const signed = await openpgp.sign({ message: openpgp.Message.fromBinary(data), - privateKeys: privKey + privateKeys: privKey, + config: { minRsaBits: 1024 } }); expect(openpgp.stream.isStream(signed)).to.equal(expectedType); @@ -575,7 +582,8 @@ function tests() { const verified = await openpgp.verify({ publicKeys: pubKey, message, - format: 'binary' + format: 'binary', + config: { minRsaBits: 1024 } }); expect(openpgp.stream.isStream(verified.data)).to.equal(expectedType); const reader = openpgp.stream.getReader(verified.data); @@ -605,7 +613,8 @@ function tests() { it("Sign: Don't pull entire input stream when we're not pulling signed stream", async function() { const signed = await openpgp.sign({ message: openpgp.Message.fromBinary(data), - privateKeys: privKey + privateKeys: privKey, + config: { minRsaBits: 1024 } }); expect(openpgp.stream.isStream(signed)).to.equal(expectedType); @@ -619,7 +628,8 @@ function tests() { it("Sign/verify: Don't pull entire input stream when we're not pulling verified stream", async function() { const signed = await openpgp.sign({ message: openpgp.Message.fromBinary(data), - privateKeys: privKey + privateKeys: privKey, + config: { minRsaBits: 1024 } }); expect(openpgp.stream.isStream(signed)).to.equal(expectedType); const message = await openpgp.readMessage({ armoredMessage: signed }); @@ -649,7 +659,8 @@ function tests() { message: openpgp.Message.fromBinary(data), privateKeys: privKey, detached: true, - streaming: expectedType + streaming: expectedType, + config: { minRsaBits: 1024 } }); expect(openpgp.stream.isStream(signed)).to.equal(expectedType); const armoredSignature = await openpgp.stream.readToEnd(signed); @@ -657,7 +668,8 @@ function tests() { const verified = await openpgp.verify({ signature, publicKeys: pubKey, - message: openpgp.Message.fromText('hello world') + message: openpgp.Message.fromText('hello world'), + config: { minRsaBits: 1024 } }); expect(verified.data).to.equal('hello world'); expect(verified.signatures).to.exist.and.have.length(1); @@ -678,14 +690,16 @@ function tests() { privateKeys: privKey, detached: true, streaming: false, - armor: false + armor: false, + config: { minRsaBits: 1024 } }); expect(openpgp.stream.isStream(signed)).to.be.false; const signature = await openpgp.readMessage({ binaryMessage: signed }); const verified = await openpgp.verify({ signature, publicKeys: pubKey, - message: openpgp.Message.fromText('hello world') + message: openpgp.Message.fromText('hello world'), + config: { minRsaBits: 1024 } }); expect(verified.data).to.equal('hello world'); expect(verified.signatures).to.exist.and.have.length(1); @@ -758,7 +772,8 @@ function tests() { const signed = await openpgp.sign({ message: openpgp.Message.fromBinary(data), privateKeys: privKey, - detached: true + detached: true, + config: { minRsaBits: 1024 } }); expect(openpgp.stream.isStream(signed)).to.equal(expectedType); const reader = openpgp.stream.getReader(signed); @@ -772,7 +787,8 @@ function tests() { const signed = await openpgp.sign({ message: openpgp.Message.fromBinary(data), privateKeys: privKey, - detached: true + detached: true, + config: { minRsaBits: 1024 } }); expect(openpgp.stream.isStream(signed)).to.equal(expectedType); const reader = openpgp.stream.getReader(signed); diff --git a/test/security/subkey_trust.js b/test/security/subkey_trust.js index c0ee35e88..3293e2838 100644 --- a/test/security/subkey_trust.js +++ b/test/security/subkey_trust.js @@ -72,7 +72,8 @@ async function testSubkeyTrust() { streaming: false }); expect(verifyAttackerIsBatman.signatures[0].keyid.equals(victimPubKey.subKeys[0].getKeyId())).to.be.true; - expect(verifyAttackerIsBatman.signatures[0].valid).to.be.null; + expect(verifyAttackerIsBatman.signatures[0].valid).to.be.false; + expect(verifyAttackerIsBatman.signatures[0].error).to.match(/Could not find valid signing key packet/); } module.exports = () => it('Does not trust subkeys without Primary Key Binding Signature', testSubkeyTrust); From 893a878c349bc7a568ad62c3d727d7e0b4345103 Mon Sep 17 00:00:00 2001 From: larabr <7375870+larabr@users.noreply.github.com> Date: Wed, 10 Mar 2021 13:31:53 +0100 Subject: [PATCH 08/13] fix linter --- test/general/key.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/general/key.js b/test/general/key.js index eb5f0cbe2..9cb6923b0 100644 --- a/test/general/key.js +++ b/test/general/key.js @@ -3381,7 +3381,7 @@ VYGdb3eNlV8CfoEC publicKey.users[1].selfCertifications[0].preferredSymmetricAlgorithms = [openpgp.enums.symmetric.aes128]; const sessionKey = await openpgp.generateSessionKey({ publicKeys: publicKey, toUserIds: { name: 'Test User', email: 'b@c.com' } }); expect(sessionKey.algorithm).to.equal('aes128'); - const config = { minRsaBits: 1024 } + const config = { minRsaBits: 1024 }; await openpgp.encrypt({ message: openpgp.Message.fromText('hello'), publicKeys: publicKey, privateKeys: privateKey, toUserIds: { name: 'Test User', email: 'b@c.com' }, armor: false, config }); From a51bc739977ef73c9cde0fc8c23f910d98a10413 Mon Sep 17 00:00:00 2001 From: larabr <7375870+larabr@users.noreply.github.com> Date: Fri, 12 Mar 2021 14:29:44 +0100 Subject: [PATCH 09/13] Check key strength and capabilities in separately --- src/key/factory.js | 41 +++++++++++--------------- src/key/helper.js | 12 ++++---- src/key/key.js | 62 ++++++++++++++++++++++++--------------- src/message.js | 4 +-- test/general/openpgp.js | 14 ++++----- test/general/streaming.js | 2 +- 6 files changed, 70 insertions(+), 65 deletions(-) diff --git a/src/key/factory.js b/src/key/factory.js index a3308b7d6..fb0d660e0 100644 --- a/src/key/factory.js +++ b/src/key/factory.js @@ -67,47 +67,40 @@ export async function generate(options, config) { */ export async function reformat(options, config) { options = sanitize(options); + const { privateKey } = options; - if (options.privateKey.primaryKey.isDummy()) { + if (privateKey.isPublic()) { + throw new Error('Cannot reformat a public key'); + } + + if (privateKey.primaryKey.isDummy()) { throw new Error('Cannot reformat a gnu-dummy primary key'); } - const isDecrypted = options.privateKey.getKeys().every(({ keyPacket }) => keyPacket.isDecrypted()); + const isDecrypted = privateKey.getKeys().every(({ keyPacket }) => keyPacket.isDecrypted()); if (!isDecrypted) { throw new Error('Key is not decrypted'); } - const packetlist = options.privateKey.toPacketlist(); - let secretKeyPacket; - const secretSubkeyPackets = []; - for (let i = 0; i < packetlist.length; i++) { - if (packetlist[i].tag === enums.packet.secretKey) { - secretKeyPacket = packetlist[i]; - } else if (packetlist[i].tag === enums.packet.secretSubkey) { - secretSubkeyPackets.push(packetlist[i]); - } - } - if (!secretKeyPacket) { - throw new Error('Key does not contain a secret key packet'); - } + const secretKeyPacket = privateKey.keyPacket; if (!options.subkeys) { - options.subkeys = await Promise.all(secretSubkeyPackets.map(async secretSubkeyPacket => { - const canSign = await options.privateKey.getSigningKey( - secretSubkeyPacket.getKeyId(), null, undefined, { ...config, rejectPublicKeyAlgorithms: new Set(), minRsaBits: 0 } - ).catch(() => {}); - const canEncrypt = await options.privateKey.getEncryptionKey( - secretSubkeyPacket.getKeyId(), null, undefined, { ...config, rejectPublicKeyAlgorithms: new Set(), minRsaBits: 0 } - ).catch(() => {}); - return { sign: canSign && !canEncrypt }; + options.subkeys = await Promise.all(privateKey.subKeys.map(async subkey => { + const secretSubkeyPacket = subkey.keyPacket; + const dataToVerify = { key: secretKeyPacket, bind: secretSubkeyPacket }; + const bindingSignature = await helper.getLatestValidSignature(subkey.bindingSignatures, secretKeyPacket, enums.signature.subkeyBinding, dataToVerify, null, config); + return { + sign: bindingSignature.keyFlags && (bindingSignature.keyFlags[0] & enums.keyFlags.signData) + }; })); } + const secretSubkeyPackets = privateKey.subKeys.map(subkey => subkey.keyPacket); if (options.subkeys.length !== secretSubkeyPackets.length) { throw new Error('Number of subkey options does not match number of subkeys'); } - options.subkeys = options.subkeys.map(function(subkey, index) { return sanitize(options.subkeys[index], options); }); + options.subkeys = options.subkeys.map(subkeyOptions => sanitize(subkeyOptions, options)); return wrapKeyObject(secretKeyPacket, secretSubkeyPackets, options, config); diff --git a/src/key/helper.js b/src/key/helper.js index 1d7b4be5a..61da5a4de 100644 --- a/src/key/helper.js +++ b/src/key/helper.js @@ -366,27 +366,25 @@ export function sanitizeKeyOptions(options, subkeyDefaults = {}) { return options; } -export function isValidSigningKeyPacket(keyPacket, signature, config) { +export function isValidSigningKeyPacket(keyPacket, signature) { if (!signature.verified || signature.revoked !== false) { // Sanity check throw new Error('Signature not verified'); } const keyAlgo = enums.write(enums.publicKey, keyPacket.algorithm); - return checkKeyStrength(keyPacket, config) && - keyAlgo !== enums.publicKey.rsaEncrypt && + return keyAlgo !== enums.publicKey.rsaEncrypt && keyAlgo !== enums.publicKey.elgamal && keyAlgo !== enums.publicKey.ecdh && (!signature.keyFlags || (signature.keyFlags[0] & enums.keyFlags.signData) !== 0); } -export function isValidEncryptionKeyPacket(keyPacket, signature, config) { +export function isValidEncryptionKeyPacket(keyPacket, signature) { if (!signature.verified || signature.revoked !== false) { // Sanity check throw new Error('Signature not verified'); } const keyAlgo = enums.write(enums.publicKey, keyPacket.algorithm); - return checkKeyStrength(keyPacket, config) && - keyAlgo !== enums.publicKey.dsa && + return keyAlgo !== enums.publicKey.dsa && keyAlgo !== enums.publicKey.rsaSign && keyAlgo !== enums.publicKey.ecdsa && keyAlgo !== enums.publicKey.eddsa && @@ -410,7 +408,7 @@ export function isValidDecryptionKeyPacket(signature, config) { (signature.keyFlags[0] & enums.keyFlags.encryptStorage) !== 0; } -function checkKeyStrength(keyPacket, config) { +export function assertKeyStrength(keyPacket, config) { const keyAlgo = enums.write(enums.publicKey, keyPacket.algorithm); if (config.rejectPublicKeyAlgorithms.has(keyAlgo)) { throw new Error(`${keyPacket.algorithm} keys are considered too weak.`); diff --git a/src/key/key.js b/src/key/key.js index 585c8d62b..fbda2157d 100644 --- a/src/key/key.js +++ b/src/key/key.js @@ -291,20 +291,26 @@ class Key { const primaryKey = this.keyPacket; 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)) { + for (const subKey of subKeys) { + if (!keyId || subKey.getKeyId().equals(keyId)) { try { - await subKeys[i].verify(primaryKey, date, config); - const dataToVerify = { key: primaryKey, bind: subKeys[i].keyPacket }; - const bindingSignature = await helper.getLatestValidSignature(subKeys[i].bindingSignatures, primaryKey, enums.signature.subkeyBinding, dataToVerify, date, config); - if ( - bindingSignature && - bindingSignature.embeddedSignature && - helper.isValidSigningKeyPacket(subKeys[i].keyPacket, bindingSignature, config) && - await helper.getLatestValidSignature([bindingSignature.embeddedSignature], subKeys[i].keyPacket, enums.signature.keyBinding, dataToVerify, date, config) - ) { - return subKeys[i]; + await subKey.verify(primaryKey, date, config); + const dataToVerify = { key: primaryKey, bind: subKey.keyPacket }; + const bindingSignature = await helper.getLatestValidSignature( + subKey.bindingSignatures, primaryKey, enums.signature.subkeyBinding, dataToVerify, date, config + ); + if (!bindingSignature || !helper.isValidSigningKeyPacket(subKey.keyPacket, bindingSignature)) { + continue; } + if (!bindingSignature.embeddedSignature) { + throw new Error('Missing embedded signature'); + } + // verify embedded signature + await helper.getLatestValidSignature( + [bindingSignature.embeddedSignature], subKey.keyPacket, enums.signature.keyBinding, dataToVerify, date, config + ); + helper.assertKeyStrength(subKey.keyPacket, config); + return subKey; } catch (e) { exception = e; } @@ -313,6 +319,7 @@ class Key { const primaryUser = await this.getPrimaryUser(date, userId, config); if ((!keyId || primaryKey.getKeyId().equals(keyId)) && helper.isValidSigningKeyPacket(primaryKey, primaryUser.selfCertification, config)) { + helper.assertKeyStrength(primaryKey, config); return this; } throw util.wrapError('Could not find valid signing key packet in key ' + this.getKeyId().toHex(), exception); @@ -333,25 +340,32 @@ class Key { // 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)) { + for (const subKey of subKeys) { + if (!keyId || subKey.getKeyId().equals(keyId)) { try { - await subKeys[i].verify(primaryKey, date, config); - const dataToVerify = { key: primaryKey, bind: subKeys[i].keyPacket }; - const bindingSignature = await helper.getLatestValidSignature(subKeys[i].bindingSignatures, primaryKey, enums.signature.subkeyBinding, dataToVerify, date, config); - if (bindingSignature && helper.isValidEncryptionKeyPacket(subKeys[i].keyPacket, bindingSignature, config)) { - return subKeys[i]; + await subKey.verify(primaryKey, date, config); + const dataToVerify = { key: primaryKey, bind: subKey.keyPacket }; + const bindingSignature = await helper.getLatestValidSignature(subKey.bindingSignatures, primaryKey, enums.signature.subkeyBinding, dataToVerify, date, config); + if (bindingSignature && helper.isValidEncryptionKeyPacket(subKey.keyPacket, bindingSignature)) { + helper.assertKeyStrength(subKey.keyPacket, config); + return subKey; } } catch (e) { exception = e; } } } - // if no valid subkey for encryption, evaluate primary key - const primaryUser = await this.getPrimaryUser(date, userId, config); - if ((!keyId || primaryKey.getKeyId().equals(keyId)) && - helper.isValidEncryptionKeyPacket(primaryKey, primaryUser.selfCertification, config)) { - return this; + + try { + // if no valid subkey for encryption, evaluate primary key + const primaryUser = await this.getPrimaryUser(date, userId, config); + if ((!keyId || primaryKey.getKeyId().equals(keyId)) && + helper.isValidEncryptionKeyPacket(primaryKey, primaryUser.selfCertification)) { + helper.assertKeyStrength(primaryKey, config); + return this; + } + } catch (e) { + exception = e; } throw util.wrapError('Could not find valid encryption key packet in key ' + this.getKeyId().toHex(), exception); } diff --git a/src/message.js b/src/message.js index d66813e5b..0f6431cec 100644 --- a/src/message.js +++ b/src/message.js @@ -756,12 +756,12 @@ async function createVerificationObject(signature, literalDataList, keys, date = } } if (!primaryKey) { - keyError = new Error(`Could not find key with id ${signature.issuerKeyId.toHex()}`); + keyError = new Error(`Could not find signing key with key ID ${signature.issuerKeyId.toHex()}`); } else { try { signingKey = await primaryKey.getSigningKey(signature.issuerKeyId, null, undefined, config); } catch (e) { - keyError = new Error(`Key with id ${signature.issuerKeyId.toHex()} is not suitable for verification: ${e.message}`); + keyError = e; } } const signaturePacket = signature.correspondingSig || signature; diff --git a/test/general/openpgp.js b/test/general/openpgp.js index 55ee53371..b36fabe24 100644 --- a/test/general/openpgp.js +++ b/test/general/openpgp.js @@ -1630,7 +1630,7 @@ module.exports = () => describe('OpenPGP.js public api tests', function() { }).then(async function ({ signatures, data }) { expect(data).to.equal(plaintext); expect(signatures[0].valid).to.be.false; - expect(signatures[0].error).to.match(/Could not find key/); + expect(signatures[0].error).to.match(/Could not find signing key/); const signingKey = await privateKey.getSigningKey(); expect(signatures[0].keyid.toHex()).to.equal(signingKey.getKeyId().toHex()); expect(signatures[0].signature.packets.length).to.equal(1); @@ -1653,7 +1653,7 @@ module.exports = () => describe('OpenPGP.js public api tests', function() { }).then(async function ({ signatures, data }) { expect(data).to.equal(plaintext); expect(signatures[0].valid).to.be.false; - expect(signatures[0].error).to.match(/Could not find key/); + expect(signatures[0].error).to.match(/Could not find signing key/); const signingKey = await privateKey.getSigningKey(); expect(signatures[0].keyid.toHex()).to.equal(signingKey.getKeyId().toHex()); expect(signatures[0].signature.packets.length).to.equal(1); @@ -1676,7 +1676,7 @@ module.exports = () => describe('OpenPGP.js public api tests', function() { }).then(async function ({ signatures, data }) { expect(data).to.equal(''); expect(signatures[0].valid).to.be.false; - expect(signatures[0].error).to.match(/Could not find key/); + expect(signatures[0].error).to.match(/Could not find signing key/); const signingKey = await privateKey.getSigningKey(); expect(signatures[0].keyid.toHex()).to.equal(signingKey.getKeyId().toHex()); expect(signatures[0].signature.packets.length).to.equal(1); @@ -1698,7 +1698,7 @@ module.exports = () => describe('OpenPGP.js public api tests', function() { }).then(async function ({ signatures, data }) { expect(data).to.equal(plaintext); expect(signatures[0].valid).to.be.false; - expect(signatures[0].error).to.match(/Could not find key/); + expect(signatures[0].error).to.match(/Could not find signing key/); const signingKey = await privateKey.getSigningKey(); expect(signatures[0].keyid.toHex()).to.equal(signingKey.getKeyId().toHex()); expect(signatures[0].signature.packets.length).to.equal(1); @@ -1723,7 +1723,7 @@ module.exports = () => describe('OpenPGP.js public api tests', function() { }); expect(data).to.equal(plaintext); expect(signatures[0].valid).to.be.false; - expect(signatures[0].error).to.match(/Could not find key/); + expect(signatures[0].error).to.match(/Could not find signing key/); const signingKey = await privateKey.getSigningKey(); expect(signatures[0].keyid.toHex()).to.equal(signingKey.getKeyId().toHex()); expect(signatures[0].signature.packets.length).to.equal(1); @@ -2184,7 +2184,7 @@ module.exports = () => describe('OpenPGP.js public api tests', function() { }).then(async function ({ data, signatures }) { expect(data).to.equal(plaintext.replace(/[ \t]+$/mg, '')); expect(signatures[0].valid).to.be.false; - expect(signatures[0].error).to.match(/Could not find key/); + expect(signatures[0].error).to.match(/Could not find signing key/); const signingKey = await privateKey.getSigningKey(); expect(signatures[0].keyid.toHex()).to.equal(signingKey.getKeyId().toHex()); expect(signatures[0].signature.packets.length).to.equal(1); @@ -2208,7 +2208,7 @@ module.exports = () => describe('OpenPGP.js public api tests', function() { }).then(async function ({ data, signatures }) { expect(data).to.equal(plaintext); expect(signatures[0].valid).to.be.false; - expect(signatures[0].error).to.match(/Could not find key/); + expect(signatures[0].error).to.match(/Could not find signing key/); const signingKey = await privateKey.getSigningKey(); expect(signatures[0].keyid.toHex()).to.equal(signingKey.getKeyId().toHex()); expect(signatures[0].signature.packets.length).to.equal(1); diff --git a/test/general/streaming.js b/test/general/streaming.js index 0076f1109..74bc29ab4 100644 --- a/test/general/streaming.js +++ b/test/general/streaming.js @@ -508,7 +508,7 @@ function tests() { dataArrived(); await expect(reader.readToEnd()).to.be.rejectedWith('Ascii armor integrity check on message failed'); expect(decrypted.signatures).to.exist.and.have.length(1); - await expect(decrypted.signatures[0].verified).to.be.eventually.rejectedWith(/Could not find key/); + await expect(decrypted.signatures[0].verified).to.be.eventually.rejectedWith(/Could not find signing key/); } finally { openpgp.config.allowUnauthenticatedStream = allowUnauthenticatedStreamValue; } From e2ab82a854ea38aa9ce997a54dfbd9f107cfadf1 Mon Sep 17 00:00:00 2001 From: larabr <7375870+larabr@users.noreply.github.com> Date: Fri, 12 Mar 2021 15:14:39 +0100 Subject: [PATCH 10/13] Do not sign/verify users with weak keys --- src/key/user.js | 2 +- test/general/key.js | 173 +++++++++++++++++++++++--------------- test/general/signature.js | 2 +- 3 files changed, 106 insertions(+), 71 deletions(-) diff --git a/src/key/user.js b/src/key/user.js index 92d428164..80e429c62 100644 --- a/src/key/user.js +++ b/src/key/user.js @@ -120,7 +120,7 @@ class User { if (!key.getKeyIds().some(id => id.equals(keyid))) { return null; } - const signingKey = await key.getSigningKey(keyid, date, undefined, { ...config, rejectPublicKeyAlgorithms: new Set(), minRsaBits: 0 }); + const signingKey = await key.getSigningKey(keyid, date, undefined, config); if (certificate.revoked || await that.isRevoked(primaryKey, certificate, signingKey.keyPacket, date, config)) { throw new Error('User certificate is revoked'); } diff --git a/test/general/key.js b/test/general/key.js index 9cb6923b0..bbfbfae9d 100644 --- a/test/general/key.js +++ b/test/general/key.js @@ -2421,15 +2421,22 @@ function versionSpecificTests() { let publicKey = await openpgp.readKey({ armoredKey: pub_sig_test }); const privateKey = await openpgp.readKey({ armoredKey: priv_key_rsa }); await privateKey.decrypt('hello world'); - publicKey = await publicKey.signPrimaryUser([privateKey]); - const signatures = await publicKey.verifyPrimaryUser([privateKey]); - const publicSigningKey = await publicKey.getSigningKey(undefined, undefined, undefined, { ...openpgp.config, minRsaBits: 1024 }); - const privateSigningKey = await privateKey.getSigningKey(undefined, undefined, undefined, { ...openpgp.config, minRsaBits: 1024 }); - 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(privateSigningKey.getKeyId().toHex()); - expect(signatures[1].valid).to.be.true; + + const { minRsaBits } = openpgp.config; + openpgp.config.minRsaBits = 1024; + try { + publicKey = await publicKey.signPrimaryUser([privateKey]); + const signatures = await publicKey.verifyPrimaryUser([privateKey]); + const publicSigningKey = await publicKey.getSigningKey(); + const privateSigningKey = await privateKey.getSigningKey(); + 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(privateSigningKey.getKeyId().toHex()); + expect(signatures[1].valid).to.be.true; + } finally { + openpgp.config.minRsaBits = minRsaBits; + } }); it('Sign key and verify with wrong key - primary user', async function() { @@ -2437,38 +2444,52 @@ function versionSpecificTests() { const privateKey = await openpgp.readKey({ armoredKey: priv_key_rsa }); const wrongKey = await openpgp.readKey({ armoredKey: wrong_key }); await privateKey.decrypt('hello world'); - publicKey = await publicKey.signPrimaryUser([privateKey]); - const signatures = await publicKey.verifyPrimaryUser([wrongKey]); - const publicSigningKey = await publicKey.getSigningKey(undefined, undefined, undefined, { ...openpgp.config, minRsaBits: 1024 }); - const privateSigningKey = await privateKey.getSigningKey(undefined, undefined, undefined, { ...openpgp.config, minRsaBits: 1024 }); - 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(privateSigningKey.getKeyId().toHex()); - expect(signatures[1].valid).to.be.null; + + const { minRsaBits } = openpgp.config; + openpgp.config.minRsaBits = 1024; + try { + publicKey = await publicKey.signPrimaryUser([privateKey]); + const signatures = await publicKey.verifyPrimaryUser([wrongKey]); + const publicSigningKey = await publicKey.getSigningKey(); + const privateSigningKey = await privateKey.getSigningKey(); + 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(privateSigningKey.getKeyId().toHex()); + expect(signatures[1].valid).to.be.null; + } finally { + openpgp.config.minRsaBits = minRsaBits; + } }); it('Sign and verify key - all users', async function() { let publicKey = await openpgp.readKey({ armoredKey: multi_uid_key }); const privateKey = await openpgp.readKey({ armoredKey: priv_key_rsa }); await privateKey.decrypt('hello world'); - publicKey = await publicKey.signAllUsers([privateKey]); - const signatures = await publicKey.verifyAllUsers([privateKey]); - const publicSigningKey = await publicKey.getSigningKey(undefined, undefined, undefined, { ...openpgp.config, minRsaBits: 1024 }); - const privateSigningKey = await privateKey.getSigningKey(undefined, undefined, undefined, { ...openpgp.config, minRsaBits: 1024 }); - expect(signatures.length).to.equal(4); - expect(signatures[0].userid).to.equal(publicKey.users[0].userId.userid); - expect(signatures[0].keyid.toHex()).to.equal(publicSigningKey.getKeyId().toHex()); - expect(signatures[0].valid).to.be.null; - expect(signatures[1].userid).to.equal(publicKey.users[0].userId.userid); - expect(signatures[1].keyid.toHex()).to.equal(privateSigningKey.getKeyId().toHex()); - expect(signatures[1].valid).to.be.true; - expect(signatures[2].userid).to.equal(publicKey.users[1].userId.userid); - expect(signatures[2].keyid.toHex()).to.equal(publicSigningKey.getKeyId().toHex()); - expect(signatures[2].valid).to.be.null; - expect(signatures[3].userid).to.equal(publicKey.users[1].userId.userid); - expect(signatures[3].keyid.toHex()).to.equal(privateSigningKey.getKeyId().toHex()); - expect(signatures[3].valid).to.be.true; + + const { minRsaBits } = openpgp.config; + openpgp.config.minRsaBits = 1024; + try { + publicKey = await publicKey.signAllUsers([privateKey]); + const signatures = await publicKey.verifyAllUsers([privateKey]); + const publicSigningKey = await publicKey.getSigningKey(); + const privateSigningKey = await privateKey.getSigningKey(); + expect(signatures.length).to.equal(4); + expect(signatures[0].userid).to.equal(publicKey.users[0].userId.userid); + expect(signatures[0].keyid.toHex()).to.equal(publicSigningKey.getKeyId().toHex()); + expect(signatures[0].valid).to.be.null; + expect(signatures[1].userid).to.equal(publicKey.users[0].userId.userid); + expect(signatures[1].keyid.toHex()).to.equal(privateSigningKey.getKeyId().toHex()); + expect(signatures[1].valid).to.be.true; + expect(signatures[2].userid).to.equal(publicKey.users[1].userId.userid); + expect(signatures[2].keyid.toHex()).to.equal(publicSigningKey.getKeyId().toHex()); + expect(signatures[2].valid).to.be.null; + expect(signatures[3].userid).to.equal(publicKey.users[1].userId.userid); + expect(signatures[3].keyid.toHex()).to.equal(privateSigningKey.getKeyId().toHex()); + expect(signatures[3].valid).to.be.true; + } finally { + openpgp.config.minRsaBits = minRsaBits; + } }); it('Sign key and verify with wrong key - all users', async function() { @@ -2476,23 +2497,30 @@ function versionSpecificTests() { const privateKey = await openpgp.readKey({ armoredKey: priv_key_rsa }); const wrongKey = await openpgp.readKey({ armoredKey: wrong_key }); await privateKey.decrypt('hello world'); - publicKey = await publicKey.signAllUsers([privateKey]); - const signatures = await publicKey.verifyAllUsers([wrongKey]); - const publicSigningKey = await publicKey.getSigningKey(undefined, undefined, undefined, { ...openpgp.config, minRsaBits: 1024 }); - const privateSigningKey = await privateKey.getSigningKey(undefined, undefined, undefined, { ...openpgp.config, minRsaBits: 1024 }); - expect(signatures.length).to.equal(4); - expect(signatures[0].userid).to.equal(publicKey.users[0].userId.userid); - expect(signatures[0].keyid.toHex()).to.equal(publicSigningKey.getKeyId().toHex()); - expect(signatures[0].valid).to.be.null; - expect(signatures[1].userid).to.equal(publicKey.users[0].userId.userid); - expect(signatures[1].keyid.toHex()).to.equal(privateSigningKey.getKeyId().toHex()); - expect(signatures[1].valid).to.be.null; - expect(signatures[2].userid).to.equal(publicKey.users[1].userId.userid); - expect(signatures[2].keyid.toHex()).to.equal(publicSigningKey.getKeyId().toHex()); - expect(signatures[2].valid).to.be.null; - expect(signatures[3].userid).to.equal(publicKey.users[1].userId.userid); - expect(signatures[3].keyid.toHex()).to.equal(privateSigningKey.getKeyId().toHex()); - expect(signatures[3].valid).to.be.null; + + const { minRsaBits } = openpgp.config; + openpgp.config.minRsaBits = 1024; + try { + publicKey = await publicKey.signAllUsers([privateKey]); + const signatures = await publicKey.verifyAllUsers([wrongKey]); + const publicSigningKey = await publicKey.getSigningKey(); + const privateSigningKey = await privateKey.getSigningKey(); + expect(signatures.length).to.equal(4); + expect(signatures[0].userid).to.equal(publicKey.users[0].userId.userid); + expect(signatures[0].keyid.toHex()).to.equal(publicSigningKey.getKeyId().toHex()); + expect(signatures[0].valid).to.be.null; + expect(signatures[1].userid).to.equal(publicKey.users[0].userId.userid); + expect(signatures[1].keyid.toHex()).to.equal(privateSigningKey.getKeyId().toHex()); + expect(signatures[1].valid).to.be.null; + expect(signatures[2].userid).to.equal(publicKey.users[1].userId.userid); + expect(signatures[2].keyid.toHex()).to.equal(publicSigningKey.getKeyId().toHex()); + expect(signatures[2].valid).to.be.null; + expect(signatures[3].userid).to.equal(publicKey.users[1].userId.userid); + expect(signatures[3].keyid.toHex()).to.equal(privateSigningKey.getKeyId().toHex()); + expect(signatures[3].valid).to.be.null; + } finally { + openpgp.config.minRsaBits = minRsaBits; + } }); it('Reformat key without passphrase', function() { @@ -2782,23 +2810,30 @@ module.exports = () => describe('Key', function() { }); it('Verify status of key with non-self revocation signature', async function() { - const pubKey = await openpgp.readKey({ armoredKey: key_with_revoked_third_party_cert }); - const [selfCertification] = await pubKey.verifyPrimaryUser(); - const publicSigningKey = await pubKey.getSigningKey(undefined, undefined, undefined, { ...openpgp.config, rejectPublicKeyAlgorithms: new Set() }); - expect(selfCertification.keyid.toHex()).to.equal(publicSigningKey.getKeyId().toHex()); - expect(selfCertification.valid).to.be.true; - - const certifyingKey = await openpgp.readKey({ armoredKey: certifying_key }); - const certifyingSigningKey = await certifyingKey.getSigningKey(undefined, undefined, undefined, { ...openpgp.config, rejectPublicKeyAlgorithms: new Set() }); - 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(); - await expect(user.verifyCertificate(pubKey.primaryKey, user.otherCertifications[0], [certifyingKey], undefined, openpgp.config)).to.be.rejectedWith('User certificate is revoked'); + const { rejectPublicKeyAlgorithms } = openpgp.config; + openpgp.config.rejectPublicKeyAlgorithms = new Set(); + + try { + const pubKey = await openpgp.readKey({ armoredKey: 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 certifyingKey = await openpgp.readKey({ armoredKey: 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(); + await expect(user.verifyCertificate(pubKey.primaryKey, user.otherCertifications[0], [certifyingKey], undefined, openpgp.config)).to.be.rejectedWith('User certificate is revoked'); + } finally { + openpgp.config.rejectPublicKeyAlgorithms = rejectPublicKeyAlgorithms; + } }); it('Verify certificate of key with future creation date', async function() { diff --git a/test/general/signature.js b/test/general/signature.js index dcb7302cf..7fbf34495 100644 --- a/test/general/signature.js +++ b/test/general/signature.js @@ -1630,7 +1630,7 @@ hkJiXopCSWKSlQInL1devkJJUWJmTmZeugJYlpdLAagQJM0JpsCqIQZwKgAA const signedKey = await openpgp.readKey({ armoredKey: signedArmor }); const signerKey = await openpgp.readKey({ armoredKey: priv_key_arm1 }); - return signedKey.verifyPrimaryUser([signerKey]).then(signatures => { + return signedKey.verifyPrimaryUser([signerKey], undefined, undefined, { ...openpgp.config, rejectPublicKeyAlgorithms: new Set() }).then(signatures => { expect(signatures[0].valid).to.be.null; expect(signatures[0].keyid.toHex()).to.equal(signedKey.getKeyId().toHex()); expect(signatures[1].valid).to.be.true; From 1ce7292dcd8ca15751e89be80d22f25741cbe08a Mon Sep 17 00:00:00 2001 From: larabr <7375870+larabr@users.noreply.github.com> Date: Thu, 18 Mar 2021 19:51:27 +0100 Subject: [PATCH 11/13] Fixes --- src/key/factory.js | 4 +++- src/key/helper.js | 2 +- src/key/key.js | 21 +++++++++++++-------- src/key/user.js | 5 +---- 4 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/key/factory.js b/src/key/factory.js index fb0d660e0..2345e6ef4 100644 --- a/src/key/factory.js +++ b/src/key/factory.js @@ -88,7 +88,9 @@ export async function reformat(options, config) { options.subkeys = await Promise.all(privateKey.subKeys.map(async subkey => { const secretSubkeyPacket = subkey.keyPacket; const dataToVerify = { key: secretKeyPacket, bind: secretSubkeyPacket }; - const bindingSignature = await helper.getLatestValidSignature(subkey.bindingSignatures, secretKeyPacket, enums.signature.subkeyBinding, dataToVerify, null, config); + const bindingSignature = await ( + helper.getLatestValidSignature(subkey.bindingSignatures, secretKeyPacket, enums.signature.subkeyBinding, dataToVerify, null, config) + ).catch(() => ({})); return { sign: bindingSignature.keyFlags && (bindingSignature.keyFlags[0] & enums.keyFlags.signData) }; diff --git a/src/key/helper.js b/src/key/helper.js index 61da5a4de..4c55eca32 100644 --- a/src/key/helper.js +++ b/src/key/helper.js @@ -370,6 +370,7 @@ export function isValidSigningKeyPacket(keyPacket, signature) { if (!signature.verified || signature.revoked !== false) { // Sanity check throw new Error('Signature not verified'); } + const keyAlgo = enums.write(enums.publicKey, keyPacket.algorithm); return keyAlgo !== enums.publicKey.rsaEncrypt && keyAlgo !== enums.publicKey.elgamal && @@ -417,5 +418,4 @@ export function assertKeyStrength(keyPacket, config) { if (rsaAlgos.has(keyAlgo) && util.uint8ArrayBitLength(keyPacket.publicParams.n) < config.minRsaBits) { throw new Error(`RSA keys shorter than ${config.minRsaBits} bits are considered too weak.`); } - return true; } diff --git a/src/key/key.js b/src/key/key.js index fbda2157d..5a6db9372 100644 --- a/src/key/key.js +++ b/src/key/key.js @@ -299,7 +299,7 @@ class Key { const bindingSignature = await helper.getLatestValidSignature( subKey.bindingSignatures, primaryKey, enums.signature.subkeyBinding, dataToVerify, date, config ); - if (!bindingSignature || !helper.isValidSigningKeyPacket(subKey.keyPacket, bindingSignature)) { + if (!helper.isValidSigningKeyPacket(subKey.keyPacket, bindingSignature)) { continue; } if (!bindingSignature.embeddedSignature) { @@ -316,11 +316,16 @@ class Key { } } } - const primaryUser = await this.getPrimaryUser(date, userId, config); - if ((!keyId || primaryKey.getKeyId().equals(keyId)) && - helper.isValidSigningKeyPacket(primaryKey, primaryUser.selfCertification, config)) { - helper.assertKeyStrength(primaryKey, config); - return this; + + try { + const primaryUser = await this.getPrimaryUser(date, userId, config); + if ((!keyId || primaryKey.getKeyId().equals(keyId)) && + helper.isValidSigningKeyPacket(primaryKey, primaryUser.selfCertification, config)) { + helper.assertKeyStrength(primaryKey, config); + return this; + } + } catch (e) { + exception = e; } throw util.wrapError('Could not find valid signing key packet in key ' + this.getKeyId().toHex(), exception); } @@ -346,7 +351,7 @@ class Key { await subKey.verify(primaryKey, date, config); const dataToVerify = { key: primaryKey, bind: subKey.keyPacket }; const bindingSignature = await helper.getLatestValidSignature(subKey.bindingSignatures, primaryKey, enums.signature.subkeyBinding, dataToVerify, date, config); - if (bindingSignature && helper.isValidEncryptionKeyPacket(subKey.keyPacket, bindingSignature)) { + if (helper.isValidEncryptionKeyPacket(subKey.keyPacket, bindingSignature)) { helper.assertKeyStrength(subKey.keyPacket, config); return subKey; } @@ -388,7 +393,7 @@ class Key { try { const dataToVerify = { key: primaryKey, bind: this.subKeys[i].keyPacket }; const bindingSignature = await helper.getLatestValidSignature(this.subKeys[i].bindingSignatures, primaryKey, enums.signature.subkeyBinding, dataToVerify, date, config); - if (bindingSignature && helper.isValidDecryptionKeyPacket(bindingSignature, config)) { + if (helper.isValidDecryptionKeyPacket(bindingSignature, config)) { keys.push(this.subKeys[i]); } } catch (e) {} diff --git a/src/key/user.js b/src/key/user.js index 80e429c62..b422557e7 100644 --- a/src/key/user.js +++ b/src/key/user.js @@ -59,10 +59,7 @@ class User { if (privateKey.hasSameFingerprintAs(primaryKey)) { throw new Error('Not implemented for self signing'); } - const signingKey = await privateKey.getSigningKey( - undefined, undefined, undefined, - { ...config, rejectPublicKeyAlgorithms: new Set(), minRsaBits: 0 } - ); + const signingKey = await privateKey.getSigningKey(undefined, undefined, undefined, config); return createSignaturePacket(dataToSign, privateKey, signingKey.keyPacket, { // Most OpenPGP implementations use generic certification (0x10) signatureType: enums.signature.certGeneric, From 1c166c344ffe95749275168b5474ec74a2662ba9 Mon Sep 17 00:00:00 2001 From: larabr <7375870+larabr@users.noreply.github.com> Date: Thu, 18 Mar 2021 20:05:00 +0100 Subject: [PATCH 12/13] Fix linting error --- .eslintrc.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.eslintrc.js b/.eslintrc.js index 05e42b260..06d0c8032 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -344,7 +344,7 @@ module.exports = { "no-use-before-define": [ 2, { "functions": false, "classes": true, "variables": false }], "no-constant-condition": [ 2, { "checkLoops": false } ], "new-cap": [ 2, { "properties": false, "capIsNewExceptionPattern": "EAX|OCB|GCM|CMAC|CBC|OMAC|CTR", "newIsCapExceptionPattern": "type|hash*"}], - "max-lines": [ 2, { "max": 600, "skipBlankLines": true, "skipComments": true } ], + "max-lines": [ 2, { "max": 620, "skipBlankLines": true, "skipComments": true } ], "no-unused-expressions": 0, "chai-friendly/no-unused-expressions": [ 2, { "allowShortCircuit": true } ], From 3eec8e5ffb7d601615b7d227a0a4b29cb1423092 Mon Sep 17 00:00:00 2001 From: larabr <7375870+larabr@users.noreply.github.com> Date: Thu, 25 Mar 2021 14:47:00 +0100 Subject: [PATCH 13/13] Change function name --- src/key/helper.js | 2 +- src/key/key.js | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/key/helper.js b/src/key/helper.js index 4c55eca32..14d71cb70 100644 --- a/src/key/helper.js +++ b/src/key/helper.js @@ -409,7 +409,7 @@ export function isValidDecryptionKeyPacket(signature, config) { (signature.keyFlags[0] & enums.keyFlags.encryptStorage) !== 0; } -export function assertKeyStrength(keyPacket, config) { +export function checkKeyStrength(keyPacket, config) { const keyAlgo = enums.write(enums.publicKey, keyPacket.algorithm); if (config.rejectPublicKeyAlgorithms.has(keyAlgo)) { throw new Error(`${keyPacket.algorithm} keys are considered too weak.`); diff --git a/src/key/key.js b/src/key/key.js index 5a6db9372..b60d64839 100644 --- a/src/key/key.js +++ b/src/key/key.js @@ -309,7 +309,7 @@ class Key { await helper.getLatestValidSignature( [bindingSignature.embeddedSignature], subKey.keyPacket, enums.signature.keyBinding, dataToVerify, date, config ); - helper.assertKeyStrength(subKey.keyPacket, config); + helper.checkKeyStrength(subKey.keyPacket, config); return subKey; } catch (e) { exception = e; @@ -321,7 +321,7 @@ class Key { const primaryUser = await this.getPrimaryUser(date, userId, config); if ((!keyId || primaryKey.getKeyId().equals(keyId)) && helper.isValidSigningKeyPacket(primaryKey, primaryUser.selfCertification, config)) { - helper.assertKeyStrength(primaryKey, config); + helper.checkKeyStrength(primaryKey, config); return this; } } catch (e) { @@ -352,7 +352,7 @@ class Key { const dataToVerify = { key: primaryKey, bind: subKey.keyPacket }; const bindingSignature = await helper.getLatestValidSignature(subKey.bindingSignatures, primaryKey, enums.signature.subkeyBinding, dataToVerify, date, config); if (helper.isValidEncryptionKeyPacket(subKey.keyPacket, bindingSignature)) { - helper.assertKeyStrength(subKey.keyPacket, config); + helper.checkKeyStrength(subKey.keyPacket, config); return subKey; } } catch (e) { @@ -366,7 +366,7 @@ class Key { const primaryUser = await this.getPrimaryUser(date, userId, config); if ((!keyId || primaryKey.getKeyId().equals(keyId)) && helper.isValidEncryptionKeyPacket(primaryKey, primaryUser.selfCertification)) { - helper.assertKeyStrength(primaryKey, config); + helper.checkKeyStrength(primaryKey, config); return this; } } catch (e) {