gpg: Sanitize diagnostic with the original file name.
[gnupg.git] / g10 / decrypt-data.c
index 80e16ec..a3151b5 100644 (file)
@@ -56,11 +56,10 @@ struct decode_filter_context_s
   /* The start IV for AEAD encryption.   */
   byte startiv[16];
 
-  /* The holdback buffer.  For AEAD we need 32 bytes for MDC 22 bytes
-   * are enough.  The flag indicates whether the holdback buffer is
-   * filled. */
-  char defer[32];
-  unsigned int defer_filled : 1;
+  /* The holdback buffer and its used length.  For AEAD we need 32+1
+   * bytes but we use 48 byte.  For MDC we need 22 bytes.  */
+  char holdback[48];
+  unsigned int holdbacklen;
 
   /* Working on a partial length packet.  */
   unsigned int partial : 1;
@@ -118,11 +117,15 @@ release_dfx_context (decode_filter_ctx_t dfx)
 }
 
 
-/* Set the nonce for AEAD.  This also reset the decryption machinery
- * so that the handle can be used for a new chunk.  */
+/* Set the nonce and the additional data for the current chunk.  This
+ * also reset the decryption machinery * so that the handle can be
+ * used for a new chunk.  If FINAL is set the final AEAD chunk is
+ * processed.  */
 static gpg_error_t
-aead_set_nonce (decode_filter_ctx_t dfx)
+aead_set_nonce_and_ad (decode_filter_ctx_t dfx, int final)
 {
+  gpg_error_t err;
+  unsigned char ad[21];
   unsigned char nonce[16];
   int i;
 
@@ -150,17 +153,11 @@ aead_set_nonce (decode_filter_ctx_t dfx)
   nonce[i++] ^= dfx->chunkindex >>  8;
   nonce[i++] ^= dfx->chunkindex;
 
-  log_printhex (nonce, i, "nonce:");
-  return gcry_cipher_setiv (dfx->cipher_hd, nonce, i);
-}
-
-
-/* Set the additional data for the current chunk.  If FINAL is set the
- * final AEAD chunk is processed.  */
-static gpg_error_t
-aead_set_ad (decode_filter_ctx_t dfx, int final)
-{
-  unsigned char ad[21];
+  if (DBG_CRYPTO)
+    log_printhex (nonce, i, "nonce:");
+  err = gcry_cipher_setiv (dfx->cipher_hd, nonce, i);
+  if (err)
+    return err;
 
   ad[0] = (0xc0 | PKT_ENCRYPTED_AEAD);
   ad[1] = 1;
@@ -186,11 +183,34 @@ aead_set_ad (decode_filter_ctx_t dfx, int final)
       ad[19] = dfx->total >>  8;
       ad[20] = dfx->total;
     }
-  log_printhex (ad, final? 21 : 13, "authdata:");
+  if (DBG_CRYPTO)
+    log_printhex (ad, final? 21 : 13, "authdata:");
   return gcry_cipher_authenticate (dfx->cipher_hd, ad, final? 21 : 13);
 }
 
 
+/* Helper to check the 16 byte tag in TAGBUF.  The FINAL flag is only
+ * for debug messages.  */
+static gpg_error_t
+aead_checktag (decode_filter_ctx_t dfx, int final, const void *tagbuf)
+{
+  gpg_error_t err;
+
+  if (DBG_FILTER)
+    log_printhex (tagbuf, 16, "tag:");
+  err = gcry_cipher_checktag (dfx->cipher_hd, tagbuf, 16);
+  if (err)
+    {
+      log_error ("gcry_cipher_checktag%s failed: %s\n",
+                 final? " (final)":"", gpg_strerror (err));
+      return err;
+    }
+  if (DBG_FILTER)
+    log_debug ("%stag is valid\n", final?"final ":"");
+  return 0;
+}
+
+
 /****************
  * Decrypt the data, specified by ED with the key DEK.
  */
@@ -212,8 +232,10 @@ decrypt_data (ctrl_t ctrl, void *procctx, PKT_encrypted *ed, DEK *dek)
   if ( opt.verbose && !dek->algo_info_printed )
     {
       if (!openpgp_cipher_test_algo (dek->algo))
-        log_info (_("%s encrypted data\n"),
-                  openpgp_cipher_algo_name (dek->algo));
+        log_info (_("%s.%s encrypted data\n"),
+                  openpgp_cipher_algo_name (dek->algo),
+                  ed->aead_algo? openpgp_aead_algo_name (ed->aead_algo)
+                  /**/         : "CFB");
       else
         log_info (_("encrypted with unknown algorithm %d\n"), dek->algo );
       dek->algo_info_printed = 1;
@@ -270,29 +292,15 @@ decrypt_data (ctrl_t ctrl, void *procctx, PKT_encrypted *ed, DEK *dek)
           goto leave;
         }
 
-      switch (ed->aead_algo)
-        {
-        case AEAD_ALGO_OCB:
-          startivlen = 15;
-          ciphermode = GCRY_CIPHER_MODE_OCB;
-          break;
-        case AEAD_ALGO_EAX:
-          startivlen = 16;
-          log_error ("unsupported AEAD algo %d\n", ed->aead_algo);
-          rc = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
-          goto leave;
-        default:
-          log_error ("unknown AEAD algo %d\n", ed->aead_algo);
-          rc = gpg_error (GPG_ERR_INV_CIPHER_MODE);
-          goto leave;
-        }
+      rc = openpgp_aead_algo_info (ed->aead_algo, &ciphermode, &startivlen);
+      if (rc)
+        goto leave;
       log_assert (startivlen <= sizeof dfx->startiv);
 
-      if (ed->chunkbyte != 10)
+      if (ed->chunkbyte > 56)
         {
-          /* FIXME */
-          log_error ("unsupported chunkbyte %u\n", ed->chunkbyte);
-          rc = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+          log_error ("invalid AEAD chunkbyte %u\n", ed->chunkbyte);
+          rc = gpg_error (GPG_ERR_INV_PACKET);
           goto leave;
         }
 
@@ -339,7 +347,8 @@ decrypt_data (ctrl_t ctrl, void *procctx, PKT_encrypted *ed, DEK *dek)
       if (rc)
         goto leave; /* Should never happen.  */
 
-      log_printhex (dek->key, dek->keylen, "thekey:");
+      if (DBG_CRYPTO)
+        log_printhex (dek->key, dek->keylen, "thekey:");
       rc = gcry_cipher_setkey (dfx->cipher_hd, dek->key, dek->keylen);
       if (gpg_err_code (rc) == GPG_ERR_WEAK_KEY)
         {
@@ -359,14 +368,6 @@ decrypt_data (ctrl_t ctrl, void *procctx, PKT_encrypted *ed, DEK *dek)
           goto leave;
         }
 
-      rc = aead_set_nonce (dfx);
-      if (rc)
-        goto leave;
-
-      rc = aead_set_ad (dfx, 0);
-      if (rc)
-        goto leave;
-
     }
   else /* CFB encryption.  */
     {
@@ -521,16 +522,16 @@ decrypt_data (ctrl_t ctrl, void *procctx, PKT_encrypted *ed, DEK *dek)
 
       log_assert (dfx->cipher_hd);
       log_assert (dfx->mdc_hash);
-      gcry_cipher_decrypt (dfx->cipher_hd, dfx->defer, 22, NULL, 0);
-      gcry_md_write (dfx->mdc_hash, dfx->defer, 2);
+      gcry_cipher_decrypt (dfx->cipher_hd, dfx->holdback, 22, NULL, 0);
+      gcry_md_write (dfx->mdc_hash, dfx->holdback, 2);
       gcry_md_final (dfx->mdc_hash);
 
-      if (   dfx->defer[0] != '\xd3'
-          || dfx->defer[1] != '\x14'
+      if (   dfx->holdback[0] != '\xd3'
+          || dfx->holdback[1] != '\x14'
           || datalen != 20
-          || memcmp (gcry_md_read (dfx->mdc_hash, 0), dfx->defer+2, datalen))
+          || memcmp (gcry_md_read (dfx->mdc_hash, 0), dfx->holdback+2, datalen))
         rc = gpg_error (GPG_ERR_BAD_SIGNATURE);
-      /* log_printhex("MDC message:", dfx->defer, 22); */
+      /* log_printhex("MDC message:", dfx->holdback, 22); */
       /* log_printhex("MDC calc:", gcry_md_read (dfx->mdc_hash,0), datalen); */
     }
 
@@ -541,170 +542,208 @@ decrypt_data (ctrl_t ctrl, void *procctx, PKT_encrypted *ed, DEK *dek)
 }
 
 
-/* The core of the AEAD decryption.  This is the underflow function of
- * the aead_decode_filter.  */
-static gpg_error_t
-aead_underflow (decode_filter_ctx_t dfx, iobuf_t a, byte *buf, size_t *ret_len)
+/* Fill BUFFER with up to NBYTES-OFFSET from STREAM utilizing
+ * information from the context DFX.  Returns the new offset which is
+ * the number of bytes read plus the original offset.  On EOF the
+ * respective flag in DFX is set. */
+static size_t
+fill_buffer (decode_filter_ctx_t dfx, iobuf_t stream,
+             byte *buffer, size_t nbytes, size_t offset)
 {
-  const size_t size = *ret_len; /* The initial length of BUF.  */
-  gpg_error_t err;
-  size_t n; /* Finally the number of decrypted bytes in BUF.  */
+  size_t nread = offset;
   int c;
 
-  log_assert (size > 64); /* Our code requires at least this size.  */
-
-  /* Get at least 32 bytes and put it ahead in the buffer.  */
   if (dfx->partial)
     {
-      for (n=32; n < 64; n++)
+      for (; nread < nbytes; nread++ )
         {
-          if ((c = iobuf_get (a)) == -1)
-            break;
-          buf[n] = c;
+          if ((c = iobuf_get (stream)) == -1)
+            {
+              dfx->eof_seen = 1; /* Normal EOF. */
+              break;
+            }
+          buffer[nread] = c;
         }
     }
   else
     {
-      for (n=32; n < 64 && dfx->length; n++, dfx->length--)
+      for (; nread < nbytes && dfx->length; nread++, dfx->length--)
         {
-          if ((c = iobuf_get (a)) == -1)
-            break; /* Premature EOF.  */
-          buf[n] = c;
+          c = iobuf_get (stream);
+          if (c == -1)
+            {
+              dfx->eof_seen = 3; /* Premature EOF. */
+              break;
+            }
+          buffer[nread] = c;
         }
+      if (!dfx->length)
+        dfx->eof_seen = 1; /* Normal EOF.  */
     }
 
-  if (n == 64)
+  return nread;
+}
+
+
+/* The core of the AEAD decryption.  This is the underflow function of
+ * the aead_decode_filter.  */
+static gpg_error_t
+aead_underflow (decode_filter_ctx_t dfx, iobuf_t a, byte *buf, size_t *ret_len)
+{
+  const size_t size = *ret_len; /* The allocated size of BUF.  */
+  gpg_error_t err;
+  size_t totallen = 0; /* The number of bytes to return on success or EOF.  */
+  size_t off = 0;      /* The offset into the buffer.  */
+  size_t len;          /* The current number of bytes in BUF+OFF.  */
+
+  log_assert (size > 48); /* Our code requires at least this size.  */
+
+  /* Copy the rest from the last call of this function into BUF.  */
+  len = dfx->holdbacklen;
+  dfx->holdbacklen = 0;
+  memcpy (buf, dfx->holdback, len);
+
+  if (DBG_FILTER)
+    log_debug ("aead_underflow: size=%zu len=%zu%s%s\n", size, len,
+               dfx->partial? " partial":"", dfx->eof_seen? " eof":"");
+
+  /* Read and fill up BUF.  We need to watch out for an EOF so that we
+   * can detect the last chunk which is commonly shorter than the
+   * chunksize.  After the last data byte from the last chunk 32 more
+   * bytes are expected for the last chunk's tag and the following
+   * final chunk's tag.  To detect the EOF we need to try reading at least
+   * one further byte; however we try to read 16 extra bytes to avoid
+   * single byte reads in some lower layers.  The outcome is that we
+   * have up to 48 extra extra octets which we will later put into the
+   * holdback buffer for the next invocation (which handles the EOF
+   * case).  */
+  len = fill_buffer (dfx, a, buf, size, len);
+  if (len < 32)
+    {
+      /* Not enough data for the last two tags.  */
+      err = gpg_error (GPG_ERR_TRUNCATED);
+      goto leave;
+    }
+  if (dfx->eof_seen)
     {
-      /* We got 32 bytes from A which are good for the last chunk's
-       * auth tag and the final chunk's auth tag.  On the first time
-       * we don't have anything in the defer buffer and thus we move
-       * those 32 bytes to the start of the buffer.  All further calls
-       * will copy the deferred 32 bytes to the start of the
-       * buffer.  */
-      if (!dfx->defer_filled)
+      /* If have seen an EOF we copy only the last two auth tags into
+       * the holdback buffer.  */
+      dfx->holdbacklen = 32;
+      memcpy (dfx->holdback, buf+len-32, 32);
+      len -= 32;
+    }
+  else
+    {
+      /* If have not seen an EOF we copy the entire extra 48 bytes
+       * into the holdback buffer for processing at the next call of
+       * this function.  */
+      dfx->holdbacklen = len > 48? 48 : len;
+      memcpy (dfx->holdback, buf+len-dfx->holdbacklen, dfx->holdbacklen);
+      len -= dfx->holdbacklen;
+    }
+  /* log_printhex (dfx->holdback, dfx->holdbacklen, "holdback:"); */
+
+  /* Decrypt the buffer.  This first requires a loop to handle the
+   * case when a chunk ends within the buffer.  */
+  if (DBG_FILTER)
+    log_debug ("decrypt: chunklen=%ju total=%ju size=%zu len=%zu%s\n",
+               (uintmax_t)dfx->chunklen, (uintmax_t)dfx->total, size, len,
+               dfx->eof_seen? " eof":"");
+
+  while (len && dfx->chunklen + len >= dfx->chunksize)
+    {
+      size_t n = dfx->chunksize - dfx->chunklen;
+      byte tagbuf[16];
+
+      if (DBG_FILTER)
+        log_debug ("chunksize will be reached: n=%zu\n", n);
+
+      if (!dfx->chunklen)
         {
-          memcpy (buf, buf+32, 32);
-          n = 32;  /* Continue at this position.  */
+          /* First data for this chunk - prepare.  */
+          err = aead_set_nonce_and_ad (dfx, 0);
+          if (err)
+            goto leave;
         }
-      else
+
+      /* log_printhex (buf, n, "ciph:"); */
+      gcry_cipher_final (dfx->cipher_hd);
+      err = gcry_cipher_decrypt (dfx->cipher_hd, buf+off, n, NULL, 0);
+      if (err)
         {
-          memcpy (buf, dfx->defer, 32);
+          log_error ("gcry_cipher_decrypt failed (1): %s\n",
+                     gpg_strerror (err));
+          goto leave;
         }
+      /* log_printhex (buf, n, "plai:"); */
+      totallen += n;
+      dfx->chunklen += n;
+      dfx->total += n;
+      off += n;
+      len -= n;
 
-      /* Now fill up the provided buffer.  */
-      if (dfx->partial)
+      if (DBG_FILTER)
+        log_debug ("ndecrypted: %zu (nchunk=%zu) bytes left: %zu at off=%zu\n",
+                   totallen, dfx->chunklen, len, off);
+
+      /* Check the tag.  */
+      if (len < 16)
         {
-          for (; n < size; n++ )
+          /* The tag is not entirely in the buffer.  Read the rest of
+           * the tag from the holdback buffer.  Then shift the holdback
+           * buffer and fill it up again.  */
+          memcpy (tagbuf, buf+off, len);
+          memcpy (tagbuf + len, dfx->holdback, 16 - len);
+          dfx->holdbacklen -= 16-len;
+          memmove (dfx->holdback, dfx->holdback + (16-len), dfx->holdbacklen);
+
+          if (dfx->eof_seen)
             {
-              if ((c = iobuf_get (a)) == -1)
+              /* We should have the last chunk's tag in TAGBUF and the
+               * final tag in HOLDBACKBUF.  */
+              if (len || dfx->holdbacklen != 16)
                 {
-                  dfx->eof_seen = 1; /* Normal EOF. */
-                  break;
+                  /* Not enough data for the last two tags.  */
+                  err = gpg_error (GPG_ERR_TRUNCATED);
+                  goto leave;
                 }
-              buf[n] = c;
             }
-        }
-      else
-        {
-          for (; n < size && dfx->length; n++, dfx->length--)
+          else
             {
-              c = iobuf_get (a);
-              if (c == -1)
+              len = 0;
+              dfx->holdbacklen = fill_buffer (dfx, a, dfx->holdback, 48,
+                                              dfx->holdbacklen);
+              if (dfx->holdbacklen < 32)
                 {
-                  dfx->eof_seen = 3; /* Premature EOF. */
-                  break;
+                  /* Not enough data for the last two tags.  */
+                  err = gpg_error (GPG_ERR_TRUNCATED);
+                  goto leave;
                 }
-              buf[n] = c;
             }
-          if (!dfx->length)
-            dfx->eof_seen = 1; /* Normal EOF.  */
         }
+      else /* We already have the full tag.  */
+        {
+          memcpy (tagbuf, buf+off, 16);
+          /* Remove that tag from the output.  */
+          memmove (buf + off, buf + off + 16, len - 16);
+          len -= 16;
+        }
+      err = aead_checktag (dfx, 0, tagbuf);
+      if (err)
+        goto leave;
+      dfx->chunklen = 0;
+      dfx->chunkindex++;
 
-      /* Move the trailing 32 bytes back to the defer buffer.  We
-       * got at least 64 bytes and thus a memmove is not needed.  */
-      n -= 32;
-      memcpy (dfx->defer, buf+n, 32);
-      dfx->defer_filled = 1;
-    }
-  else if (!dfx->defer_filled)
-    {
-      /* EOF seen but empty defer buffer.  This means that we did not
-       * read enough for the two auth tags.  */
-      n -= 32;
-      memcpy (buf, buf+32, n );
-      dfx->eof_seen = 2; /* EOF with incomplete tag.  */
-    }
-  else
-    {
-      /* EOF seen (i.e. read less than 32 bytes). */
-      memcpy (buf, dfx->defer, 32);
-      n -= 32;
-      memcpy (dfx->defer, buf+n, 32);
-      dfx->eof_seen = 1; /* Normal EOF. */
+      continue;
     }
 
-  log_debug ("decrypt: chunklen=%ju total=%ju size=%zu n=%zu%s\n",
-             (uintmax_t)dfx->chunklen, (uintmax_t)dfx->total, size, n,
-             dfx->eof_seen? " eof":"");
-
-  /* Now decrypt the buffer.  */
-  if (n && dfx->eof_seen > 1)
-    {
-      err = gpg_error (GPG_ERR_TRUNCATED);
-    }
-  else if (!n)
+  /* The bulk decryption of our buffer.  */
+  if (len)
     {
-      log_assert (dfx->eof_seen);
-      err = gpg_error (GPG_ERR_EOF);
-    }
-  else
-    {
-      size_t off = 0;
-
-      if (dfx->chunklen + n >= dfx->chunksize)
+      if (!dfx->chunklen)
         {
-          size_t n0 = dfx->chunksize - dfx->chunklen;
-
-          log_debug ("chunksize will be reached: n0=%zu\n", n0);
-          gcry_cipher_final (dfx->cipher_hd);
-          err = gcry_cipher_decrypt (dfx->cipher_hd, buf, n0, NULL, 0);
-          if (err)
-            {
-              log_debug ("gcry_cipher_decrypt failed (1): %s\n",
-                         gpg_strerror (err));
-              goto leave;
-            }
-          /*log_printhex (buf, n, "buf:");*/
-          dfx->chunklen += n0;
-          dfx->total += n0;
-          off = n0;
-          n -= n0;
-
-          log_debug ("bytes left: %zu  off=%zu\n", n, off);
-          log_assert (n >= 16);
-          log_assert (dfx->defer_filled);
-          log_printhex (buf+off, 16, "tag:");
-          err = gcry_cipher_checktag (dfx->cipher_hd, buf + off, 16);
-          if (err)
-            {
-              log_debug ("gcry_cipher_checktag failed (1): %s\n",
-                         gpg_strerror (err));
-              /* Return Bad Signature like we do with MDC encryption. */
-              if (gpg_err_code (err) == GPG_ERR_CHECKSUM)
-                err = gpg_error (GPG_ERR_BAD_SIGNATURE);
-              goto leave;
-            }
-          /* Remove that tag from the output.  */
-          memmove (buf + off, buf + off + 16, n - 16);
-          n -= 16;
-
-          /* Prepare a new chunk.  */
-          dfx->chunklen = 0;
-          dfx->chunkindex++;
-          err = aead_set_nonce (dfx);
-          if (err)
-            goto leave;
-          err = aead_set_ad (dfx, 0);
+          /* First data for this chunk - prepare.  */
+          err = aead_set_nonce_and_ad (dfx, 0);
           if (err)
             goto leave;
         }
@@ -712,86 +751,81 @@ aead_underflow (decode_filter_ctx_t dfx, iobuf_t a, byte *buf, size_t *ret_len)
       if (dfx->eof_seen)
         {
           /* This is the last block of the last chunk.  Its length may
-           * not be a multiple of the block length.  We expect that it
-           * is followed by two authtags.  The first being the one
-           * from the current chunk and the second form the final
-           * chunk encrypting the empty string.  Note that for the
-           * other blocks we assume a multiple of the block length
-           * which is only true because the filter is called with
-           * large 2^n sized buffers.  There is no assert because
-           * gcry_cipher_decrypt would detect such an error.  */
+           * not be a multiple of the block length.  */
           gcry_cipher_final (dfx->cipher_hd);
-          /*log_printhex (buf+off, n, "buf+off:");*/
         }
-      err = gcry_cipher_decrypt (dfx->cipher_hd, buf + off, n, NULL, 0);
+      err = gcry_cipher_decrypt (dfx->cipher_hd, buf + off, len, NULL, 0);
       if (err)
         {
-          log_debug ("gcry_cipher_decrypt failed (2): %s\n",gpg_strerror (err));
+          log_error ("gcry_cipher_decrypt failed (2): %s\n",
+                     gpg_strerror (err));
           goto leave;
         }
-      dfx->chunklen += n;
-      dfx->total += n;
-
-      if (dfx->eof_seen)
-        {
-          /* log_printhex (buf+off, n, "buf+off:"); */
-          log_debug ("eof seen: chunklen=%ju total=%ju off=%zu n=%zu\n",
-                     (uintmax_t)dfx->chunklen, (uintmax_t)dfx->total, off, n);
+      totallen += len;
+      dfx->chunklen += len;
+      dfx->total += len;
+      if (DBG_FILTER)
+        log_debug ("ndecrypted: %zu (nchunk=%zu)\n", totallen, dfx->chunklen);
+    }
 
-          log_assert (dfx->defer_filled);
-          err = gcry_cipher_checktag (dfx->cipher_hd, dfx->defer, 16);
-          if (err)
-            {
-              log_debug ("gcry_cipher_checktag failed (2): %s\n",
-                         gpg_strerror (err));
-              /* Return Bad Signature like we do with MDC encryption. */
-              if (gpg_err_code (err) == GPG_ERR_CHECKSUM)
-                err = gpg_error (GPG_ERR_BAD_SIGNATURE);
-              goto leave;
-            }
+  if (dfx->eof_seen)
+    {
 
-          /* Check the final chunk.  */
-          dfx->chunkindex++;
-          err = aead_set_nonce (dfx);
-          if (err)
-            goto leave;
-          err = aead_set_ad (dfx, 1);
+      if (dfx->chunklen)
+        {
+          if (DBG_FILTER)
+            log_debug ("eof seen: holdback has the last and final tag\n");
+          log_assert (dfx->holdbacklen >= 32);
+          err = aead_checktag (dfx, 0, dfx->holdback);
           if (err)
             goto leave;
-          gcry_cipher_final (dfx->cipher_hd);
-          /* decrypt an empty string.  */
-          err = gcry_cipher_decrypt (dfx->cipher_hd, buf, 0, NULL, 0);
-          if (err)
-            {
-              log_debug ("gcry_cipher_decrypt failed (final): %s\n",
-                         gpg_strerror (err));
-              goto leave;
-            }
-          err = gcry_cipher_checktag (dfx->cipher_hd, dfx->defer+16, 16);
-          if (err)
-            {
-              log_debug ("gcry_cipher_checktag failed (final): %s\n",
-                         gpg_strerror (err));
-              /* Return Bad Signature like we do with MDC encryption. */
-              if (gpg_err_code (err) == GPG_ERR_CHECKSUM)
-                err = gpg_error (GPG_ERR_BAD_SIGNATURE);
-              goto leave;
-            }
-
-          n += off;
-          log_debug ("eof seen: returning %zu\n", n);
-          /* log_printhex (buf, n, "buf:"); */
+          dfx->chunklen = 0;
+          dfx->chunkindex++;
+          off = 16;
         }
       else
-        n += off;
+        {
+          if (DBG_FILTER)
+            log_debug ("eof seen: holdback has the final tag\n");
+          log_assert (dfx->holdbacklen >= 16);
+          off = 0;
+        }
+
+      /* Check the final chunk.  */
+      err = aead_set_nonce_and_ad (dfx, 1);
+      if (err)
+        goto leave;
+      gcry_cipher_final (dfx->cipher_hd);
+      /* Decrypt an empty string (using HOLDBACK as a dummy).  */
+      err = gcry_cipher_decrypt (dfx->cipher_hd, dfx->holdback, 0, NULL, 0);
+      if (err)
+        {
+          log_error ("gcry_cipher_decrypt failed (final): %s\n",
+                     gpg_strerror (err));
+          goto leave;
+        }
+      err = aead_checktag (dfx, 1, dfx->holdback+off);
+      if (err)
+        goto leave;
+      err = gpg_error (GPG_ERR_EOF);
     }
 
  leave:
-  /* In case of a real error we better wipe out the buffer than to
-   * keep partly encrypted data.  */
+  if (DBG_FILTER)
+    log_debug ("aead_underflow: returning %zu (%s)\n",
+               totallen, gpg_strerror (err));
+
+  /* In case of an auth error we map the error code to the same as
+   * used by the MDC decryption.  */
+  if (gpg_err_code (err) == GPG_ERR_CHECKSUM)
+    err = gpg_error (GPG_ERR_BAD_SIGNATURE);
+
+  /* In case of an error we better wipe out the buffer than to convey
+   * partly decrypted data.  */
   if (err && gpg_err_code (err) != GPG_ERR_EOF)
     memset (buf, 0, size);
-  *ret_len = n;
+
+  *ret_len = totallen;
 
   return err;
 }
@@ -881,8 +915,8 @@ mdc_decode_filter (void *opaque, int control, IOBUF a,
         }
       if (n == 44)
         {
-          /* We have enough stuff - flush the deferred stuff.  */
-          if ( !dfx->defer_filled )  /* First time. */
+          /* We have enough stuff - flush the holdback buffer.  */
+          if ( !dfx->holdbacklen )  /* First time. */
             {
               memcpy (buf, buf+22, 22);
               n = 22;
@@ -890,7 +924,7 @@ mdc_decode_filter (void *opaque, int control, IOBUF a,
           else
             {
 
-              memcpy (buf, dfx->defer, 22);
+              memcpy (buf, dfx->holdback, 22);
            }
           /* Fill up the buffer. */
           if (dfx->partial)
@@ -921,13 +955,13 @@ mdc_decode_filter (void *opaque, int control, IOBUF a,
                 dfx->eof_seen = 1; /* Normal EOF.  */
             }
 
-          /* Move the trailing 22 bytes back to the defer buffer.  We
+          /* Move the trailing 22 bytes back to the holdback buffer.  We
              have at least 44 bytes thus a memmove is not needed.  */
           n -= 22;
-          memcpy (dfx->defer, buf+n, 22 );
-          dfx->defer_filled = 1;
+          memcpy (dfx->holdback, buf+n, 22 );
+          dfx->holdbacklen = 22;
        }
-      else if ( !dfx->defer_filled )  /* EOF seen but empty defer buffer. */
+      else if ( !dfx->holdbacklen ) /* EOF seen but empty holdback. */
         {
           /* This is bad because it means an incomplete hash. */
           n -= 22;
@@ -936,9 +970,9 @@ mdc_decode_filter (void *opaque, int control, IOBUF a,
        }
       else  /* EOF seen (i.e. read less than 22 bytes). */
         {
-          memcpy (buf, dfx->defer, 22 );
+          memcpy (buf, dfx->holdback, 22 );
           n -= 22;
-          memcpy (dfx->defer, buf+n, 22 );
+          memcpy (dfx->holdback, buf+n, 22 );
           dfx->eof_seen = 1; /* Normal EOF. */
        }