gpg: Sanitize diagnostic with the original file name.
[gnupg.git] / g10 / decrypt-data.c
index afdedcb..a3151b5 100644 (file)
@@ -117,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;
 
@@ -151,16 +155,9 @@ aead_set_nonce (decode_filter_ctx_t dfx)
 
   if (DBG_CRYPTO)
     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];
+  err = gcry_cipher_setiv (dfx->cipher_hd, nonce, i);
+  if (err)
+    return err;
 
   ad[0] = (0xc0 | PKT_ENCRYPTED_AEAD);
   ad[1] = 1;
@@ -192,6 +189,28 @@ aead_set_ad (decode_filter_ctx_t dfx, int final)
 }
 
 
+/* 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.
  */
@@ -349,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.  */
     {
@@ -531,6 +542,49 @@ decrypt_data (ctrl_t ctrl, void *procctx, PKT_encrypted *ed, DEK *dek)
 }
 
 
+/* 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)
+{
+  size_t nread = offset;
+  int c;
+
+  if (dfx->partial)
+    {
+      for (; nread < nbytes; nread++ )
+        {
+          if ((c = iobuf_get (stream)) == -1)
+            {
+              dfx->eof_seen = 1; /* Normal EOF. */
+              break;
+            }
+          buffer[nread] = c;
+        }
+    }
+  else
+    {
+      for (; nread < nbytes && dfx->length; nread++, dfx->length--)
+        {
+          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.  */
+    }
+
+  return nread;
+}
+
+
 /* The core of the AEAD decryption.  This is the underflow function of
  * the aead_decode_filter.  */
 static gpg_error_t
@@ -541,7 +595,6 @@ aead_underflow (decode_filter_ctx_t dfx, iobuf_t a, byte *buf, size_t *ret_len)
   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.  */
-  int c;
 
   log_assert (size > 48); /* Our code requires at least this size.  */
 
@@ -551,47 +604,20 @@ aead_underflow (decode_filter_ctx_t dfx, iobuf_t a, byte *buf, size_t *ret_len)
   memcpy (buf, dfx->holdback, len);
 
   if (DBG_FILTER)
-    log_debug ("aead_underflow: size=%zu len=%zu%s\n",
-               size, len, dfx->eof_seen? " eof":"");
+    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 watchout for an EOF so that we
+  /* 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 read at least
-   * one further byte; however we try to ready 16 extra bytes to avoid
-   * singel byte reads in some lower layers.  The outcome is that we
+   * 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).  */
-  if (dfx->partial)
-    {
-      for (; len < size; len++ )
-        {
-          if ((c = iobuf_get (a)) == -1)
-            {
-              dfx->eof_seen = 1; /* Normal EOF. */
-              break;
-            }
-          buf[len] = c;
-        }
-    }
-  else
-    {
-      for (; len < size && dfx->length; len++, dfx->length--)
-        {
-          c = iobuf_get (a);
-          if (c == -1)
-            {
-              dfx->eof_seen = 3; /* Premature EOF. */
-              break;
-            }
-          buf[len] = c;
-        }
-      if (!dfx->length)
-        dfx->eof_seen = 1; /* Normal EOF.  */
-    }
-
+  len = fill_buffer (dfx, a, buf, size, len);
   if (len < 32)
     {
       /* Not enough data for the last two tags.  */
@@ -617,10 +643,10 @@ aead_underflow (decode_filter_ctx_t dfx, iobuf_t a, byte *buf, size_t *ret_len)
     }
   /* log_printhex (dfx->holdback, dfx->holdbacklen, "holdback:"); */
 
-  /* Decrypt the buffer.  This requires a loop because a chunk may end
-   * within the buffer.  */
+  /* 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 loop: chunklen=%ju total=%ju size=%zu len=%zu%s\n",
+    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":"");
 
@@ -631,6 +657,15 @@ aead_underflow (decode_filter_ctx_t dfx, iobuf_t a, byte *buf, size_t *ret_len)
 
       if (DBG_FILTER)
         log_debug ("chunksize will be reached: n=%zu\n", n);
+
+      if (!dfx->chunklen)
+        {
+          /* First data for this chunk - prepare.  */
+          err = aead_set_nonce_and_ad (dfx, 0);
+          if (err)
+            goto leave;
+        }
+
       /* log_printhex (buf, n, "ciph:"); */
       gcry_cipher_final (dfx->cipher_hd);
       err = gcry_cipher_decrypt (dfx->cipher_hd, buf+off, n, NULL, 0);
@@ -648,56 +683,43 @@ aead_underflow (decode_filter_ctx_t dfx, iobuf_t a, byte *buf, size_t *ret_len)
       len -= n;
 
       if (DBG_FILTER)
-        log_debug ("bytes left: %zu at off=%zu\n", len, off);
+        log_debug ("ndecrypted: %zu (nchunk=%zu) bytes left: %zu at off=%zu\n",
+                   totallen, dfx->chunklen, len, off);
 
       /* Check the tag.  */
       if (len < 16)
         {
           /* The tag is not entirely in the buffer.  Read the rest of
-           * the tag from the holdback buffer.  The shift the holdback
+           * 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);
 
-          len = dfx->holdbacklen;
-          if (dfx->partial)
+          if (dfx->eof_seen)
             {
-              for (; len < 48; len++ )
+              /* We should have the last chunk's tag in TAGBUF and the
+               * final tag in HOLDBACKBUF.  */
+              if (len || dfx->holdbacklen != 16)
                 {
-                  if ((c = iobuf_get (a)) == -1)
-                    {
-                      dfx->eof_seen = 1; /* Normal EOF. */
-                      break;
-                    }
-                  dfx->holdback[len] = c;
+                  /* Not enough data for the last two tags.  */
+                  err = gpg_error (GPG_ERR_TRUNCATED);
+                  goto leave;
                 }
             }
           else
             {
-              for (; len < 48 && dfx->length; len++, dfx->length--)
+              len = 0;
+              dfx->holdbacklen = fill_buffer (dfx, a, dfx->holdback, 48,
+                                              dfx->holdbacklen);
+              if (dfx->holdbacklen < 32)
                 {
-                  c = iobuf_get (a);
-                  if (c == -1)
-                    {
-                      dfx->eof_seen = 3; /* Premature EOF. */
-                      break;
-                    }
-                  dfx->holdback[len] = c;
+                  /* Not enough data for the last two tags.  */
+                  err = gpg_error (GPG_ERR_TRUNCATED);
+                  goto leave;
                 }
-              if (!dfx->length)
-                dfx->eof_seen = 1; /* Normal EOF.  */
             }
-          if (len < 32)
-            {
-              /* Not enough data for the last two tags.  */
-              err = gpg_error (GPG_ERR_TRUNCATED);
-              goto leave;
-            }
-          dfx->holdbacklen = len;
-          /* log_printhex (dfx->holdback, dfx->holdbacklen, "holdback:"); */
-          len = 0;
         }
       else /* We already have the full tag.  */
         {
@@ -706,72 +728,75 @@ aead_underflow (decode_filter_ctx_t dfx, iobuf_t a, byte *buf, size_t *ret_len)
           memmove (buf + off, buf + off + 16, len - 16);
           len -= 16;
         }
-      if (DBG_CRYPTO)
-        log_printhex (tagbuf, 16, "tag:");
-      err = gcry_cipher_checktag (dfx->cipher_hd, tagbuf, 16);
+      err = aead_checktag (dfx, 0, tagbuf);
       if (err)
-        {
-          if (DBG_FILTER)
-            log_debug ("gcry_cipher_checktag failed (1): %s\n",
-                       gpg_strerror (err));
-          goto leave;
-        }
-
-      /* Prepare a new chunk.  */
+        goto leave;
       dfx->chunklen = 0;
       dfx->chunkindex++;
-      err = aead_set_nonce (dfx);
-      if (err)
-        goto leave;
-      err = aead_set_ad (dfx, 0);
-      if (err)
-        goto leave;
 
       continue;
     }
 
-  if (dfx->eof_seen)
+  /* The bulk decryption of our buffer.  */
+  if (len)
     {
-      /* This is the last block of the last chunk.  Its length may
-       * not be a multiple of the block length.  */
-      gcry_cipher_final (dfx->cipher_hd);
-    }
-  err = gcry_cipher_decrypt (dfx->cipher_hd, buf + off, len, NULL, 0);
-  if (err)
-    {
-      log_error ("gcry_cipher_decrypt failed (2): %s\n", gpg_strerror (err));
-      goto leave;
-    }
-  totallen += len;
-  dfx->chunklen += len;
-  dfx->total += len;
-  if (dfx->eof_seen)
-    {
-      if (DBG_FILTER)
-        log_debug ("eof seen: holdback buffer has the tags.\n");
-
-      log_assert (dfx->holdbacklen >= 32);
+      if (!dfx->chunklen)
+        {
+          /* First data for this chunk - prepare.  */
+          err = aead_set_nonce_and_ad (dfx, 0);
+          if (err)
+            goto leave;
+        }
 
-      if (DBG_FILTER)
-        log_printhex (dfx->holdback, 16, "tag:");
-      err = gcry_cipher_checktag (dfx->cipher_hd, dfx->holdback, 16);
+      if (dfx->eof_seen)
+        {
+          /* This is the last block of the last chunk.  Its length may
+           * not be a multiple of the block length.  */
+          gcry_cipher_final (dfx->cipher_hd);
+        }
+      err = gcry_cipher_decrypt (dfx->cipher_hd, buf + off, len, NULL, 0);
       if (err)
         {
-          log_error ("gcry_cipher_checktag failed (2): %s\n",
+          log_error ("gcry_cipher_decrypt failed (2): %s\n",
                      gpg_strerror (err));
           goto leave;
         }
+      totallen += len;
+      dfx->chunklen += len;
+      dfx->total += len;
+      if (DBG_FILTER)
+        log_debug ("ndecrypted: %zu (nchunk=%zu)\n", totallen, dfx->chunklen);
+    }
+
+  if (dfx->eof_seen)
+    {
+
+      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;
+          dfx->chunklen = 0;
+          dfx->chunkindex++;
+          off = 16;
+        }
+      else
+        {
+          if (DBG_FILTER)
+            log_debug ("eof seen: holdback has the final tag\n");
+          log_assert (dfx->holdbacklen >= 16);
+          off = 0;
+        }
 
       /* Check the final chunk.  */
-      dfx->chunkindex++;
-      err = aead_set_nonce (dfx);
-      if (err)
-        goto leave;
-      err = aead_set_ad (dfx, 1);
+      err = aead_set_nonce_and_ad (dfx, 1);
       if (err)
         goto leave;
       gcry_cipher_final (dfx->cipher_hd);
-      /* decrypt an empty string.  */
+      /* Decrypt an empty string (using HOLDBACK as a dummy).  */
       err = gcry_cipher_decrypt (dfx->cipher_hd, dfx->holdback, 0, NULL, 0);
       if (err)
         {
@@ -779,15 +804,9 @@ aead_underflow (decode_filter_ctx_t dfx, iobuf_t a, byte *buf, size_t *ret_len)
                      gpg_strerror (err));
           goto leave;
         }
-      /* log_printhex (dfx->holdback+16, 16, "tag:"); */
-      err = gcry_cipher_checktag (dfx->cipher_hd, dfx->holdback+16, 16);
+      err = aead_checktag (dfx, 1, dfx->holdback+off);
       if (err)
-        {
-          if (DBG_FILTER)
-            log_debug ("gcry_cipher_checktag failed (final): %s\n",
-                       gpg_strerror (err));
-          goto leave;
-        }
+        goto leave;
       err = gpg_error (GPG_ERR_EOF);
     }