diff --git a/src/crypto/crypto.js b/src/crypto/crypto.js index d40eb900..c4a586e7 100644 --- a/src/crypto/crypto.js +++ b/src/crypto/crypto.js @@ -468,17 +468,22 @@ export function generateSessionKey(algo) { /** * Get implementation of the given AEAD mode * @param {enums.aead} algo + * @param {Boolean} [acceptExperimentalGCM] - whether to allow the non-standard, legacy `experimentalGCM` algo * @returns {Object} * @throws {Error} on invalid algo */ -export function getAEADMode(algo) { +export function getAEADMode(algo, acceptExperimentalGCM = false) { switch (algo) { case enums.aead.eax: return mode.eax; case enums.aead.ocb: return mode.ocb; case enums.aead.gcm: + return mode.gcm; case enums.aead.experimentalGCM: + if (!acceptExperimentalGCM) { + throw new Error('Unexpected non-standard `experimentalGCM` AEAD algorithm provided in `config.preferredAEADAlgorithm`: use `gcm` instead'); + } return mode.gcm; default: throw new Error('Unsupported AEAD mode'); diff --git a/src/packet/aead_encrypted_data.js b/src/packet/aead_encrypted_data.js index 60bdc3a7..aa8d3429 100644 --- a/src/packet/aead_encrypted_data.js +++ b/src/packet/aead_encrypted_data.js @@ -78,7 +78,7 @@ class AEADEncryptedDataPacket { this.aeadAlgorithm = await reader.readByte(); this.chunkSizeByte = await reader.readByte(); - const mode = crypto.getAEADMode(this.aeadAlgorithm); + const mode = crypto.getAEADMode(this.aeadAlgorithm, true); this.iv = await reader.readBytes(mode.ivLength); this.encrypted = reader.remainder(); }); @@ -119,7 +119,7 @@ class AEADEncryptedDataPacket { async encrypt(sessionKeyAlgorithm, key, config = defaultConfig) { this.cipherAlgorithm = sessionKeyAlgorithm; - const { ivLength } = crypto.getAEADMode(this.aeadAlgorithm); + const { ivLength } = crypto.getAEADMode(this.aeadAlgorithm, true); this.iv = crypto.random.getRandomBytes(ivLength); // generate new random IV this.chunkSizeByte = config.aeadChunkSizeByte; const data = this.packets.write(); diff --git a/src/packet/secret_key.js b/src/packet/secret_key.js index ddddae5f..48b31f56 100644 --- a/src/packet/secret_key.js +++ b/src/packet/secret_key.js @@ -454,7 +454,7 @@ class SecretKeyPacket extends PublicKeyPacket { let cleartext; if (this.s2kUsage === 253) { - const mode = crypto.getAEADMode(this.aead); + const mode = crypto.getAEADMode(this.aead, true); const modeInstance = await mode(this.symmetric, key); try { const associateData = this.isLegacyAEAD ? diff --git a/src/packet/sym_encrypted_integrity_protected_data.js b/src/packet/sym_encrypted_integrity_protected_data.js index c2762c83..5e3417a4 100644 --- a/src/packet/sym_encrypted_integrity_protected_data.js +++ b/src/packet/sym_encrypted_integrity_protected_data.js @@ -234,7 +234,10 @@ export async function runAEAD(packet, fn, key, data) { const isAEADP = !isSEIPDv2 && packet.constructor.tag === enums.packet.aeadEncryptedData; // no `instanceof` to avoid importing the corresponding class (circular import) if (!isSEIPDv2 && !isAEADP) throw new Error('Unexpected packet type'); - const mode = crypto.getAEADMode(packet.aeadAlgorithm); + // we allow `experimentalGCM` for AEADP for backwards compatibility, since v5 keys from OpenPGP.js v5 might be declaring + // that preference, as the `gcm` ID had not been standardized at the time. + // NB: AEADP are never automatically generate as part of message encryption by OpenPGP.js, the packet must be manually created. + const mode = crypto.getAEADMode(packet.aeadAlgorithm, isAEADP); const tagLengthIfDecrypting = fn === 'decrypt' ? mode.tagLength : 0; const tagLengthIfEncrypting = fn === 'encrypt' ? mode.tagLength : 0; const chunkSize = 2 ** (packet.chunkSizeByte + 6) + tagLengthIfDecrypting; // ((uint64_t)1 << (c + 6)) diff --git a/src/packet/sym_encrypted_session_key.js b/src/packet/sym_encrypted_session_key.js index 4ea592f9..475d6011 100644 --- a/src/packet/sym_encrypted_session_key.js +++ b/src/packet/sym_encrypted_session_key.js @@ -105,7 +105,7 @@ class SymEncryptedSessionKeyPacket { offset += this.s2k.read(bytes.subarray(offset, bytes.length)); if (this.version >= 5) { - const mode = crypto.getAEADMode(this.aeadAlgorithm); + const mode = crypto.getAEADMode(this.aeadAlgorithm, true); // A starting initialization vector of size specified by the AEAD // algorithm. @@ -167,7 +167,7 @@ class SymEncryptedSessionKeyPacket { const key = await this.s2k.produceKey(passphrase, keySize); if (this.version >= 5) { - const mode = crypto.getAEADMode(this.aeadAlgorithm); + const mode = crypto.getAEADMode(this.aeadAlgorithm, true); const adata = new Uint8Array([0xC0 | SymEncryptedSessionKeyPacket.tag, this.version, this.sessionKeyEncryptionAlgorithm, this.aeadAlgorithm]); const encryptionKey = this.version === 6 ? await computeHKDF(enums.hash.sha256, key, new Uint8Array(), adata, keySize) : key; const modeInstance = await mode(algo, encryptionKey); diff --git a/test/general/key.js b/test/general/key.js index 46e566e2..c2a43dc5 100644 --- a/test/general/key.js +++ b/test/general/key.js @@ -2286,7 +2286,7 @@ function versionSpecificTests() { openpgp.config.preferredSymmetricAlgorithm = openpgp.enums.symmetric.aes192; openpgp.config.preferredHashAlgorithm = openpgp.enums.hash.sha224; openpgp.config.preferredCompressionAlgorithm = openpgp.enums.compression.zlib; - openpgp.config.preferredAEADAlgorithm = openpgp.enums.aead.experimentalGCM; + openpgp.config.preferredAEADAlgorithm = openpgp.enums.aead.eax; const testPref = function(key) { const selfSignature = openpgp.config.v6Keys ? key.directSignatures[0] : key.users[0].selfCertifications[0]; @@ -2301,15 +2301,12 @@ function versionSpecificTests() { if (openpgp.config.aeadProtect) { const aead = openpgp.enums.aead; expect(selfSignature.preferredCipherSuites).to.eql([ - [sym.aes192, aead.experimentalGCM], - [sym.aes256, aead.experimentalGCM], - [sym.aes128, aead.experimentalGCM], - [sym.aes192, aead.gcm], - [sym.aes256, aead.gcm], - [sym.aes128, aead.gcm], [sym.aes192, aead.eax], [sym.aes256, aead.eax], [sym.aes128, aead.eax], + [sym.aes192, aead.gcm], + [sym.aes256, aead.gcm], + [sym.aes128, aead.gcm], [sym.aes192, aead.ocb], [sym.aes256, aead.ocb], [sym.aes128, aead.ocb]