gpg: Sanitize diagnostic with the original file name.
[gnupg.git] / g10 / decrypt-data.c
index 0b0051a..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,8 +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 last_chunk_done = 0; /* Flag that we processed the last chunk.  */
-  int c;
 
   log_assert (size > 48); /* Our code requires at least this size.  */
 
@@ -560,39 +612,12 @@ aead_underflow (decode_filter_ctx_t dfx, iobuf_t a, byte *buf, size_t *ret_len)
    * 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 ready 16 extra bytes to avoid
+   * 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.  */
@@ -618,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":"");
 
@@ -632,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);
@@ -673,47 +707,18 @@ aead_underflow (decode_filter_ctx_t dfx, iobuf_t a, byte *buf, size_t *ret_len)
                   err = gpg_error (GPG_ERR_TRUNCATED);
                   goto leave;
                 }
-              len = 0;
-              last_chunk_done = 1;
             }
           else
             {
-              len = dfx->holdbacklen;
-              if (dfx->partial)
-                {
-                  for (; len < 48; len++ )
-                    {
-                      if ((c = iobuf_get (a)) == -1)
-                        {
-                          dfx->eof_seen = 1; /* Normal EOF. */
-                          break;
-                        }
-                      dfx->holdback[len] = c;
-                    }
-                }
-              else
-                {
-                  for (; len < 48 && dfx->length; len++, dfx->length--)
-                    {
-                      c = iobuf_get (a);
-                      if (c == -1)
-                        {
-                          dfx->eof_seen = 3; /* Premature EOF. */
-                          break;
-                        }
-                      dfx->holdback[len] = c;
-                    }
-                  if (!dfx->length)
-                    dfx->eof_seen = 1; /* Normal EOF.  */
-                }
-              if (len < 32)
+              len = 0;
+              dfx->holdbacklen = fill_buffer (dfx, a, dfx->holdback, 48,
+                                              dfx->holdbacklen);
+              if (dfx->holdbacklen < 32)
                 {
                   /* Not enough data for the last two tags.  */
                   err = gpg_error (GPG_ERR_TRUNCATED);
                   goto leave;
                 }
-              dfx->holdbacklen = len;
-              len = 0;
             }
         }
       else /* We already have the full tag.  */
@@ -723,37 +728,26 @@ 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;
-        }
-      if (DBG_FILTER)
-        log_debug ("tag is valid\n");
+        goto leave;
+      dfx->chunklen = 0;
+      dfx->chunkindex++;
+
+      continue;
+    }
 
-      /* Prepare a new chunk.  */
-      if (!last_chunk_done)
+  /* The bulk decryption of our buffer.  */
+  if (len)
+    {
+      if (!dfx->chunklen)
         {
-          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;
         }
 
-      continue;
-    }
-
-  if (!last_chunk_done)
-    {
       if (dfx->eof_seen)
         {
           /* This is the last block of the last chunk.  Its length may
@@ -776,38 +770,33 @@ aead_underflow (decode_filter_ctx_t dfx, iobuf_t a, byte *buf, size_t *ret_len)
 
   if (dfx->eof_seen)
     {
-      if (DBG_FILTER)
-        log_debug ("eof seen: holdback buffer has the %s.\n",
-                   last_chunk_done? "final tag":"last and final tag");
 
-      if (!last_chunk_done)
+      if (dfx->chunklen)
         {
-          log_assert (dfx->holdbacklen >= 32);
-
           if (DBG_FILTER)
-            log_printhex (dfx->holdback, 16, "tag:");
-          err = gcry_cipher_checktag (dfx->cipher_hd, dfx->holdback, 16);
+            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)
-            {
-              log_error ("gcry_cipher_checktag failed (2): %s\n",
-                         gpg_strerror (err));
-              goto leave;
-            }
+            goto leave;
+          dfx->chunklen = 0;
+          dfx->chunkindex++;
+          off = 16;
+        }
+      else
+        {
           if (DBG_FILTER)
-            log_debug ("tag is valid\n");
+            log_debug ("eof seen: holdback has the final tag\n");
+          log_assert (dfx->holdbacklen >= 16);
+          off = 0;
         }
 
       /* Check the final chunk.  */
-      if (dfx->chunklen)
-        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)
         {
@@ -815,19 +804,9 @@ aead_underflow (decode_filter_ctx_t dfx, iobuf_t a, byte *buf, size_t *ret_len)
                      gpg_strerror (err));
           goto leave;
         }
-      if (DBG_CRYPTO)
-        log_printhex (dfx->holdback+(last_chunk_done?0:16), 16, "tag:");
-      err = gcry_cipher_checktag (dfx->cipher_hd,
-                                  dfx->holdback+(last_chunk_done?0: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;
-        }
-      if (DBG_FILTER)
-        log_debug ("final tag is valid\n");
+        goto leave;
       err = gpg_error (GPG_ERR_EOF);
     }