From 4568d080d5e2cada2771afce978d1dff80ce5a8c Mon Sep 17 00:00:00 2001
From: Daniel Huigens <d.huigens@protonmail.com>
Date: Fri, 20 Apr 2018 17:28:15 +0200
Subject: [PATCH] Fix decryption with multiple chunks

---
 src/packet/sym_encrypted_aead_protected.js |  4 ++--
 test/general/openpgp.js                    | 18 ++++++++++++++++++
 2 files changed, 20 insertions(+), 2 deletions(-)

diff --git a/src/packet/sym_encrypted_aead_protected.js b/src/packet/sym_encrypted_aead_protected.js
index ede69d62..43836977 100644
--- a/src/packet/sym_encrypted_aead_protected.js
+++ b/src/packet/sym_encrypted_aead_protected.js
@@ -137,14 +137,14 @@ SymEncryptedAEADProtected.prototype.crypt = async function (fn, key, data, final
   const modeInstance = await mode(cipher, key);
   if (config.aead_protect_version === 4) {
     const tagLengthIfDecrypting = fn === 'decrypt' ? mode.tagLength : 0;
-    const chunkSize = 2 ** (this.chunkSizeByte + 6); // ((uint64_t)1 << (c + 6))
+    const chunkSize = 2 ** (this.chunkSizeByte + 6) + tagLengthIfDecrypting; // ((uint64_t)1 << (c + 6))
     const adataBuffer = new ArrayBuffer(21);
     const adataArray = new Uint8Array(adataBuffer, 0, 13);
     const adataTagArray = new Uint8Array(adataBuffer);
     const adataView = new DataView(adataBuffer);
     const chunkIndexArray = new Uint8Array(adataBuffer, 5, 8);
     adataArray.set([0xC0 | this.tag, this.version, this.cipherAlgo, this.aeadAlgo, this.chunkSizeByte], 0);
-    adataView.setInt32(13 + 4, data.length - tagLengthIfDecrypting); // Should be setInt64(13, ...)
+    adataView.setInt32(13 + 4, data.length - tagLengthIfDecrypting * Math.ceil(data.length / chunkSize)); // Should be setInt64(13, ...)
     const cryptedPromises = [];
     for (let chunkIndex = 0; chunkIndex === 0 || data.length;) {
       cryptedPromises.push(
diff --git a/test/general/openpgp.js b/test/general/openpgp.js
index 32a7cf72..d202a42c 100644
--- a/test/general/openpgp.js
+++ b/test/general/openpgp.js
@@ -599,6 +599,7 @@ describe('OpenPGP.js public api tests', function() {
     let aead_protectVal;
     let aead_protect_versionVal;
     let aead_modeVal;
+    let aead_chunk_size_byteVal;
 
     beforeEach(function(done) {
       publicKey = openpgp.key.readArmored(pub_key);
@@ -625,6 +626,7 @@ describe('OpenPGP.js public api tests', function() {
       aead_protectVal = openpgp.config.aead_protect;
       aead_protect_versionVal = openpgp.config.aead_protect_version;
       aead_modeVal = openpgp.config.aead_mode;
+      aead_chunk_size_byteVal = openpgp.config.aead_chunk_size_byte;
       done();
     });
 
@@ -634,6 +636,7 @@ describe('OpenPGP.js public api tests', function() {
       openpgp.config.aead_protect = aead_protectVal;
       openpgp.config.aead_protect_version = aead_protect_versionVal;
       openpgp.config.aead_mode = aead_modeVal;
+      openpgp.config.aead_chunk_size_byte = aead_chunk_size_byteVal;
     });
 
     it('Decrypting key with wrong passphrase rejected', async function () {
@@ -732,6 +735,21 @@ describe('OpenPGP.js public api tests', function() {
       }
     });
 
+    tryTests('EAX mode (small chunk size)', tests, {
+      if: openpgp.util.getWebCryptoAll() || openpgp.util.getNodeCrypto(),
+      beforeEach: function() {
+        openpgp.config.use_native = true;
+        openpgp.config.aead_protect = true;
+        openpgp.config.aead_protect_version = 4;
+        openpgp.config.aead_chunk_size_byte = 0;
+
+        // Monkey-patch AEAD feature flag
+        publicKey.keys[0].users[0].selfCertifications[0].features = [7];
+        publicKey_2000_2008.keys[0].users[0].selfCertifications[0].features = [7];
+        publicKey_2038_2045.keys[0].users[0].selfCertifications[0].features = [7];
+      }
+    });
+
     tryTests('OCB mode', tests, {
       if: true,
       beforeEach: function() {