[git] GnuPG - branch, master, updated. gnupg-2.2.3-117-g3f4ca85

by Werner Koch cvs at cvs.gnupg.org
Sun Jan 21 16:37:37 CET 2018


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "The GNU Privacy Guard".

The branch, master has been updated
       via  3f4ca85cb0cf58006417f4f7faafaa9a1f1bdf22 (commit)
      from  81d71818d054a5faa9153fd52a4b79bbbb71e9d5 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 3f4ca85cb0cf58006417f4f7faafaa9a1f1bdf22
Author: Werner Koch <wk at gnupg.org>
Date:   Sun Jan 21 16:24:43 2018 +0100

    gpg: First take on PKT_ENCRYPTED_AEAD.
    
    * common/openpgpdefs.h (PKT_ENCRYPTED_AEAD): New const.
    * g10/dek.h (DEK): Increase size of use_aead to 4 bits.
    * g10/filter.h (cipher_filter_context_t):  Add new fields for AEAD.
    * g10/packet.h (PKT_encrypted): Add fields aead_algo, cipher_algo, and
    chunkbyte.
    * g10/build-packet.c (do_encrypted_aead): New.
    (build_packet): Call it.
    * g10/parse-packet.c (dump_sig_subpkt): Handle SIGSUBPKT_PREF_AEAD.
    (parse_one_sig_subpkt, can_handle_critical): Ditto.
    (parse_encrypted): Clear new PKT_ENCRYPTED fields.
    (parse_encrypted_aead): New.
    (parse): Call it.
    * g10/gpg.c (main): Take care of --rfc4880bis option when checking
    compliance.
    * g10/cipher-aead.c: Replace the stub by real code.
    * g10/decrypt-data.c (decode_filter_ctx_t): Add fields for use with
    AEAD.
    (aead_set_nonce): New.
    (aead_set_ad): New.
    (decrypt_data): Support AEAD.
    (aead_underflow): New.
    (aead_decode_filter): New.
    * g10/encrypt.c (use_aead): Make that new fucntion work.
    (encrypt_simple): Use default_aead_algo() instead of EAX.
    * g10/mainproc.c (proc_encrypted): Support AEAD.
    (do_proc_packets): Support PKT_ENCRYPTED_AEAD.
    --
    
    This code has seen only a very few manual tests.  Encrypting always
    uses a 64k chunks and decryption has not been tested with larger
    chunks.  Those small chunks make debugging much faster.
    
    Tests can be done using:
    
      gpg --rfc4880bis --pinentry-mode=loopback --passphrase abc \
          --force-aead --aead-algo ocb --s2k-mode 0 --cipher AES \
          -v -z 0 --status-fd 2 -c <INFILE >OUTFILE
    
    and
    
      gpg --rfc4880bis --pinentry-mode=loopback --passphrase=abc \
          --status-fd 2 -v -d <INFILE >OUTFILE
    
    Signed-off-by: Werner Koch <wk at gnupg.org>

diff --git a/common/openpgpdefs.h b/common/openpgpdefs.h
index aeb3389..8699a17 100644
--- a/common/openpgpdefs.h
+++ b/common/openpgpdefs.h
@@ -51,6 +51,7 @@ typedef enum
     PKT_ATTRIBUTE     = 17, /* PGP's attribute packet. */
     PKT_ENCRYPTED_MDC = 18, /* Integrity protected encrypted data. */
     PKT_MDC 	      = 19, /* Manipulation detection code packet. */
+    PKT_ENCRYPTED_AEAD= 20, /* AEAD encrypted data packet. */
     PKT_COMMENT	      = 61, /* new comment packet (GnuPG specific). */
     PKT_GPG_CONTROL   = 63  /* internal control packet (GnuPG specific). */
   }
@@ -143,6 +144,7 @@ typedef enum
 cipher_algo_t;
 
 
+/* Note that we encode the AEAD algo in a 3 bit field at some places.  */
 typedef enum
   {
     AEAD_ALGO_NONE	    =  0,
diff --git a/doc/DETAILS b/doc/DETAILS
index 3c089b2..a4063b4 100644
--- a/doc/DETAILS
+++ b/doc/DETAILS
@@ -518,9 +518,10 @@ pkd:0:1024:B665B1435F4C2 .... FF26ABB:
     actual key used for descryption.  <fpr2> is the fingerprint of the
     primary key.  <otrust> is the letter with the ownertrust; this is
     in general a 'u' which stands for ultimately trusted.
-*** DECRYPTION_INFO <mdc_method> <sym_algo>
+*** DECRYPTION_INFO <mdc_method> <sym_algo> [<aead_algo>]
     Print information about the symmetric encryption algorithm and the
     MDC method.  This will be emitted even if the decryption fails.
+    For an AEAD algorithm AEAD_ALGO is not 0.
 
 *** DECRYPTION_FAILED
     The symmetric decryption failed - one reason could be a wrong
@@ -540,8 +541,10 @@ pkd:0:1024:B665B1435F4C2 .... FF26ABB:
     --override-session-key.  It is not an indication that the
     decryption will or has succeeded.
 
-*** BEGIN_ENCRYPTION  <mdc_method> <sym_algo>
+*** BEGIN_ENCRYPTION  <mdc_method> <sym_algo> [<aead_algo>]
     Mark the start of the actual encryption process.
+    MDC_METHOD shall be 0 if an AEAD_ALGO is not 0.  Users should
+    however ignore MDC_METHOD if AEAD_ALGO is not 0.
 
 *** END_ENCRYPTION
     Mark the end of the actual encryption process.
diff --git a/g10/build-packet.c b/g10/build-packet.c
index d4a1d6a..fc64c9c 100644
--- a/g10/build-packet.c
+++ b/g10/build-packet.c
@@ -42,6 +42,7 @@ static u32 calc_plaintext( PKT_plaintext *pt );
 static int do_plaintext( IOBUF out, int ctb, PKT_plaintext *pt );
 static int do_encrypted( IOBUF out, int ctb, PKT_encrypted *ed );
 static int do_encrypted_mdc( IOBUF out, int ctb, PKT_encrypted *ed );
+static int do_encrypted_aead (iobuf_t out, int ctb, PKT_encrypted *ed);
 static int do_compressed( IOBUF out, int ctb, PKT_compressed *cd );
 static int do_signature( IOBUF out, int ctb, PKT_signature *sig );
 static int do_onepass_sig( IOBUF out, int ctb, PKT_onepass_sig *ops );
@@ -106,6 +107,7 @@ build_packet (IOBUF out, PACKET *pkt)
       break;
     case PKT_ENCRYPTED:
     case PKT_ENCRYPTED_MDC:
+    case PKT_ENCRYPTED_AEAD:
       new_ctb = pkt->pkt.encrypted->new_ctb;
       break;
     case PKT_COMPRESSED:
@@ -158,6 +160,9 @@ build_packet (IOBUF out, PACKET *pkt)
     case PKT_ENCRYPTED_MDC:
       rc = do_encrypted_mdc (out, ctb, pkt->pkt.encrypted);
       break;
+    case PKT_ENCRYPTED_AEAD:
+      rc = do_encrypted_aead (out, ctb, pkt->pkt.encrypted);
+      break;
     case PKT_COMPRESSED:
       rc = do_compressed (out, ctb, pkt->pkt.compressed);
       break;
@@ -812,6 +817,32 @@ do_encrypted_mdc( IOBUF out, int ctb, PKT_encrypted *ed )
 }
 
 
+/* Serialize the symmetrically AEAD encrypted data packet
+ * (rfc4880bis-03, Section 5.16) described by ED and write it to OUT.
+ *
+ * Note: this only writes only packet's header.  The caller must then
+ * follow up and write the actual encrypted data.  This should be done
+ * by pushing the the cipher_filter_aead.  */
+static int
+do_encrypted_aead (iobuf_t out, int ctb, PKT_encrypted *ed)
+{
+  u32 n;
+
+  log_assert (ctb_pkttype (ctb) == PKT_ENCRYPTED_AEAD);
+
+  n = ed->len ? (ed->len + ed->extralen + 4) : 0;
+  write_header (out, ctb, n );
+  iobuf_writebyte (out, 1); /* Version.  */
+  iobuf_writebyte (out, ed->cipher_algo);
+  iobuf_writebyte (out, ed->aead_algo);
+  iobuf_writebyte (out, ed->chunkbyte);
+
+  /* This is all. The caller has to write the encrypted data */
+
+  return 0;
+}
+
+
 /* Serialize the compressed packet (RFC 4880, Section 5.6) described
    by CD and write it to OUT.
 
diff --git a/g10/cipher-aead.c b/g10/cipher-aead.c
index bf0afcf..f247a83 100644
--- a/g10/cipher-aead.c
+++ b/g10/cipher-aead.c
@@ -33,13 +33,392 @@
 #include "options.h"
 #include "main.h"
 
+/* The size of the buffer we allocate to encrypt the data.  This must
+ * be a multiple of the OCB blocksize (16 byte).  */
+#define AEAD_ENC_BUFFER_SIZE (64*1024)
+
+
+/* Wrapper around iobuf_write to make sure that a proper error code is
+ * always returned.  */
+static gpg_error_t
+my_iobuf_write (iobuf_t a, const void *buffer, size_t buflen)
+{
+  if (iobuf_write (a, buffer, buflen))
+    {
+      gpg_error_t err = iobuf_error (a);
+      if (!err || !gpg_err_code (err)) /* (The latter should never happen) */
+        err = gpg_error (GPG_ERR_EIO);
+      return err;
+    }
+  return 0;
+}
+
+
+/* Set the additional data for the current chunk.  If FINAL is set the
+ * final AEAD chunk is processed.  */
+static gpg_error_t
+set_additional_data (cipher_filter_context_t *cfx, int final)
+{
+  unsigned char ad[21];
+
+  ad[0] = (0xc0 | PKT_ENCRYPTED_AEAD);
+  ad[1] = 1;
+  ad[2] = cfx->dek->algo;
+  ad[3] = cfx->dek->use_aead;
+  ad[4] = cfx->chunkbyte;
+  ad[5] = cfx->chunkindex >> 56;
+  ad[6] = cfx->chunkindex >> 48;
+  ad[7] = cfx->chunkindex >> 40;
+  ad[8] = cfx->chunkindex >> 32;
+  ad[9] = cfx->chunkindex >> 24;
+  ad[10]= cfx->chunkindex >> 16;
+  ad[11]= cfx->chunkindex >>  8;
+  ad[12]= cfx->chunkindex;
+  if (final)
+    {
+      ad[13] = cfx->total >> 56;
+      ad[14] = cfx->total >> 48;
+      ad[15] = cfx->total >> 40;
+      ad[16] = cfx->total >> 32;
+      ad[17] = cfx->total >> 24;
+      ad[18] = cfx->total >> 16;
+      ad[19] = cfx->total >>  8;
+      ad[20] = cfx->total;
+    }
+  log_printhex (ad, final? 21 : 13, "authdata:");
+  return gcry_cipher_authenticate (cfx->cipher_hd, ad, final? 21 : 13);
+}
+
+
+/* Set the nonce.  This also reset the encryption machinery so that
+ * the handle can be used for a new chunk.  */
+static gpg_error_t
+set_nonce (cipher_filter_context_t *cfx)
+{
+  unsigned char nonce[16];
+  int i;
+
+  switch (cfx->dek->use_aead)
+    {
+    case AEAD_ALGO_OCB:
+      memcpy (nonce, cfx->startiv, 15);
+      i = 7;
+      break;
+
+    case AEAD_ALGO_EAX:
+      memcpy (nonce, cfx->startiv, 16);
+      i = 8;
+      break;
+
+    default:
+      BUG ();
+    }
+
+  nonce[i++] ^= cfx->chunkindex >> 56;
+  nonce[i++] ^= cfx->chunkindex >> 48;
+  nonce[i++] ^= cfx->chunkindex >> 40;
+  nonce[i++] ^= cfx->chunkindex >> 32;
+  nonce[i++] ^= cfx->chunkindex >> 24;
+  nonce[i++] ^= cfx->chunkindex >> 16;
+  nonce[i++] ^= cfx->chunkindex >>  8;
+  nonce[i++] ^= cfx->chunkindex;
+
+  log_printhex (nonce, 15, "nonce:");
+  return gcry_cipher_setiv (cfx->cipher_hd, nonce, i);
+}
+
+
+static gpg_error_t
+write_header (cipher_filter_context_t *cfx, iobuf_t a)
+{
+  gpg_error_t err;
+  PACKET pkt;
+  PKT_encrypted ed;
+  unsigned int blocksize;
+  unsigned int startivlen;
+  enum gcry_cipher_modes ciphermode;
+
+  log_assert (cfx->dek->use_aead);
+
+  blocksize = openpgp_cipher_get_algo_blklen (cfx->dek->algo);
+  if (blocksize != 16 )
+    log_fatal ("unsupported blocksize %u for AEAD\n", blocksize);
+
+  switch (cfx->dek->use_aead)
+    {
+    case AEAD_ALGO_OCB:
+      ciphermode = GCRY_CIPHER_MODE_OCB;
+      startivlen = 15;
+      break;
+
+    default:
+      log_error ("unsupported AEAD algo %d\n", cfx->dek->use_aead);
+      err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+      goto leave;
+    }
+
+  cfx->chunkbyte = 10;
+  cfx->chunksize = (uint64_t)1 << (cfx->chunkbyte + 6);
+  cfx->chunklen = 0;
+  cfx->bufsize = AEAD_ENC_BUFFER_SIZE;
+  cfx->buflen = 0;
+  cfx->buffer = xtrymalloc (cfx->bufsize);
+  if (!cfx->buffer)
+    return gpg_error_from_syserror ();
+
+  memset (&ed, 0, sizeof ed);
+  ed.new_ctb = 1;  /* (Is anyway required for the packet type).  */
+  ed.len = 0; /* fixme: cfx->datalen */
+  ed.extralen    = startivlen + 16; /* (16 is the taglen) */
+  ed.cipher_algo = cfx->dek->algo;
+  ed.aead_algo   = cfx->dek->use_aead;
+  ed.chunkbyte   = cfx->chunkbyte;
+
+  init_packet (&pkt);
+  pkt.pkttype = PKT_ENCRYPTED_AEAD;
+  pkt.pkt.encrypted = &ed;
+
+  log_debug ("aead packet: len=%lu extralen=%d\n",
+             (unsigned long)ed.len, ed.extralen);
+
+  write_status_printf (STATUS_BEGIN_ENCRYPTION, "0 %d %d",
+                       cfx->dek->algo, ed.aead_algo);
+  print_cipher_algo_note (cfx->dek->algo);
+
+  if (build_packet( a, &pkt))
+    log_bug ("build_packet(ENCRYPTED_AEAD) failed\n");
+
+  log_assert (sizeof cfx->startiv >= startivlen);
+  gcry_randomize (cfx->startiv, startivlen, GCRY_STRONG_RANDOM);
+  err = my_iobuf_write (a, cfx->startiv, startivlen);
+  if (err)
+    goto leave;
+
+  err = openpgp_cipher_open (&cfx->cipher_hd,
+                             cfx->dek->algo,
+                             ciphermode,
+                             GCRY_CIPHER_SECURE);
+  if (err)
+    goto leave;
+
+  log_printhex (cfx->dek->key, cfx->dek->keylen, "thekey:");
+  err = gcry_cipher_setkey (cfx->cipher_hd, cfx->dek->key, cfx->dek->keylen);
+  if (err)
+    return err;
+
+  err = set_nonce (cfx);
+  if (err)
+    return err;
+
+  err = set_additional_data (cfx, 0);
+  if (err)
+    return err;
+
+  cfx->wrote_header = 1;
+
+ leave:
+  return err;
+}
+
+
+/* Get and write the auth tag to stream A.  */
+static gpg_error_t
+write_auth_tag (cipher_filter_context_t *cfx, iobuf_t a)
+{
+  gpg_error_t err;
+  char tag[16];
+
+  err = gcry_cipher_gettag (cfx->cipher_hd, tag, 16);
+  if (err)
+    goto leave;
+  err = my_iobuf_write (a, tag, 16);
+  if (err)
+    goto leave;
+  log_printhex (tag, 16, "wrote tag:");
+
+ leave:
+  return err;
+}
+
+
+/* Write the final chunk to stream A.  */
+static gpg_error_t
+write_final_chunk (cipher_filter_context_t *cfx, iobuf_t a)
+{
+  gpg_error_t err;
+  char dummy[1];
+
+  cfx->chunkindex++;
+
+  err = set_nonce (cfx);
+  if (err)
+    goto leave;
+  err = set_additional_data (cfx, 1);
+  if (err)
+    goto leave;
+
+  gcry_cipher_final (cfx->cipher_hd);
+
+  /* Encrypt an empty string.  */
+  err = gcry_cipher_encrypt (cfx->cipher_hd, dummy, 0, NULL, 0);
+  if (err)
+    goto leave;
+
+  err = write_auth_tag (cfx, a);
+
+ leave:
+  return err;
+}
+
+
+/* The core of the flush sub-function of cipher_filter_aead.   */
+static gpg_error_t
+do_flush (cipher_filter_context_t *cfx, iobuf_t a, byte *buf, size_t size)
+{
+  gpg_error_t err;
+  int newchunk = 0;
+  size_t n;
+
+  /* Put the data into a buffer, flush and encrypt as needed.  */
+  log_debug ("flushing %zu bytes (cur buflen=%zu)\n", size, cfx->buflen);
+  do
+    {
+      if (cfx->buflen + size < cfx->bufsize)
+        n = size;
+      else
+        n = cfx->bufsize - cfx->buflen;
+
+      if (cfx->chunklen + n >= cfx->chunksize)
+        {
+          size_t n1 = cfx->chunksize - cfx->chunklen;
+          newchunk = 1;
+          log_debug ("chunksize %ju reached;"
+                     " cur buflen=%zu using %zu of %zu\n",
+                     (uintmax_t)cfx->chunksize, (uintmax_t)cfx->buflen,
+                     n1, n);
+          n = n1;
+        }
+
+      memcpy (cfx->buffer + cfx->buflen, buf, n);
+      cfx->buflen += n;
+      buf  += n;
+      size -= n;
+
+      if (cfx->buflen == cfx->bufsize || newchunk)
+        {
+          log_debug ("encrypting: buflen=%zu %s %p\n",
+                     cfx->buflen, newchunk?"(newchunk)":"", cfx->cipher_hd);
+          if (newchunk)
+            gcry_cipher_final (cfx->cipher_hd);
+          if (newchunk)
+            log_printhex (cfx->buffer, cfx->buflen, "plain(1):");
+          else if (cfx->buflen > 32)
+            log_printhex (cfx->buffer + cfx->buflen - 32, 32,
+                          "plain(last 32):");
+
+          /* Take care: even with a buflen of zero an encrypt needs to
+           * be called after gcry_cipher_final and before
+           * gcry_cipher_gettag - at least with libgcrypt 1.8 and OCB
+           * mode.  */
+          gcry_cipher_encrypt (cfx->cipher_hd, cfx->buffer, cfx->buflen,
+                               NULL, 0);
+          if (newchunk)
+            log_printhex (cfx->buffer, cfx->buflen, "ciphr(1):");
+          err = my_iobuf_write (a, cfx->buffer, cfx->buflen);
+          if (err)
+            goto leave;
+          cfx->chunklen += cfx->buflen;
+          cfx->total += cfx->buflen;
+          cfx->buflen = 0;
+
+          if (newchunk)
+            {
+              log_debug ("chunklen=%ju  total=%ju\n",
+                         (uintmax_t)cfx->chunklen, (uintmax_t)cfx->total);
+              err = write_auth_tag (cfx, a);
+              if (err)
+                {
+                  log_debug ("gcry_cipher_gettag failed: %s\n",
+                             gpg_strerror (err));
+                  goto leave;
+                }
+
+              log_debug ("starting a new chunk (cur size=%zu)\n", size);
+              log_printhex (buf, size, "cur buf:");
+              cfx->chunkindex++;
+              cfx->chunklen = 0;
+              err = set_nonce (cfx);
+              if (err)
+                goto leave;
+              err = set_additional_data (cfx, 0);
+              if (err)
+                goto leave;
+              newchunk = 0;
+            }
+        }
+    }
+  while (size);
+
+ leave:
+  return err;
+}
+
+
+/* The core of the free sub-function of cipher_filter_aead.   */
+static gpg_error_t
+do_free (cipher_filter_context_t *cfx, iobuf_t a)
+{
+  gpg_error_t err = 0;
+
+  /* FIXME: Check what happens if we just wrote the last chunk and no
+   * more bytes were to encrypt.  We should then not call finalize and
+   * write the auth tag again, right?  May this at all happen?  */
+
+  /* Call finalize which will also allow us to flush out and encrypt
+   * the last arbitrary length buffer.  */
+  gcry_cipher_final (cfx->cipher_hd);
+
+  /* Encrypt any remaining bytes.  */
+  if (cfx->buflen)
+    {
+      log_debug ("processing last %zu bytes of the last chunk\n", cfx->buflen);
+      log_printhex (cfx->buffer, cfx->buflen, "plain(2):");
+      gcry_cipher_encrypt (cfx->cipher_hd, cfx->buffer, cfx->buflen, NULL, 0);
+      log_printhex (cfx->buffer, cfx->buflen, "ciphr(2):");
+      err = my_iobuf_write (a, cfx->buffer, cfx->buflen);
+      if (err)
+        goto leave;
+      /* log_printhex (cfx->buffer, cfx->buflen, "wrote:"); */
+      cfx->chunklen += cfx->buflen;
+      cfx->total += cfx->buflen;
+    }
+
+  /* Get and write the authentication tag.  */
+  log_debug ("chunklen=%ju  total=%ju\n",
+             (uintmax_t)cfx->chunklen, (uintmax_t)cfx->total);
+  err = write_auth_tag (cfx, a);
+  if (err)
+    goto leave;
+
+  /* Write the final chunk.  */
+  log_debug ("creating final chunk\n");
+  err = write_final_chunk (cfx, a);
+
+ leave:
+  xfree (cfx->buffer);
+  cfx->buffer = NULL;
+  /* gcry_cipher_close (cfx->cipher_hd); */
+  /* cfx->cipher_hd = NULL; */
+  return err;
+}
+
 
 /*
- * This filter is used to encipher data with an AEAD algorithm
+ * This filter is used to encrypt data with an AEAD algorithm
  */
 int
 cipher_filter_aead (void *opaque, int control,
-                   iobuf_t a, byte *buf, size_t *ret_len)
+                    iobuf_t a, byte *buf, size_t *ret_len)
 {
   cipher_filter_context_t *cfx = opaque;
   size_t size = *ret_len;
@@ -47,16 +426,18 @@ cipher_filter_aead (void *opaque, int control,
 
   if (control == IOBUFCTRL_UNDERFLOW) /* decrypt */
     {
-      rc = -1; /* not yet used */
+      rc = -1; /* not used */
     }
   else if (control == IOBUFCTRL_FLUSH) /* encrypt */
     {
-      log_assert (a);
-      rc = GPG_ERR_NOT_IMPLEMENTED;
+      if (!cfx->wrote_header && (rc=write_header (cfx, a)))
+        ;
+      else
+        rc = do_flush (cfx, a, buf, size);
     }
   else if (control == IOBUFCTRL_FREE)
     {
-      gcry_cipher_close (cfx->cipher_hd);
+      rc = do_free (cfx, a);
     }
   else if (control == IOBUFCTRL_DESC)
     {
diff --git a/g10/decrypt-data.c b/g10/decrypt-data.c
index 736534d..80e16ec 100644
--- a/g10/decrypt-data.c
+++ b/g10/decrypt-data.c
@@ -1,6 +1,6 @@
 /* decrypt-data.c - Decrypt an encrypted data packet
- * Copyright (C) 1998, 1999, 2000, 2001, 2005,
- *               2006, 2009 Free Software Foundation, Inc.
+ * Copyright (C) 1998-2001, 2005-2006, 2009 Free Software Foundation, Inc.
+ * Copyright (C) 1998-2001, 2005-2006, 2009, 2018 Werner Koch
  *
  * This file is part of GnuPG.
  *
@@ -32,22 +32,71 @@
 #include "../common/compliance.h"
 
 
+static int aead_decode_filter (void *opaque, int control, iobuf_t a,
+                               byte *buf, size_t *ret_len);
 static int mdc_decode_filter ( void *opaque, int control, IOBUF a,
                                byte *buf, size_t *ret_len);
 static int decode_filter ( void *opaque, int control, IOBUF a,
 					byte *buf, size_t *ret_len);
 
-typedef struct decode_filter_context_s
+/* Our context object.  */
+struct decode_filter_context_s
 {
+  /* Recounter (max value is 2).  We need it becuase we do not know
+   * whether the iobuf or the outer control code frees this object
+   * first.  */
+  int  refcount;
+
+  /* The cipher handle.  */
   gcry_cipher_hd_t cipher_hd;
+
+  /* The hash handle for use in MDC mode.  */
   gcry_md_hd_t mdc_hash;
-  char defer[22];
-  int  defer_filled;
-  int  eof_seen;
-  int  refcount;
-  int  partial;   /* Working on a partial length packet.  */
-  size_t length;  /* If !partial: Remaining bytes in the packet.  */
-} *decode_filter_ctx_t;
+
+  /* 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;
+
+  /* Working on a partial length packet.  */
+  unsigned int partial : 1;
+
+  /* EOF indicator with these true values:
+   *   1 = normal EOF
+   *   2 = premature EOF (tag incomplete)
+   *   3 = premature EOF (general)       */
+  unsigned int eof_seen : 2;
+
+  /* The actually used cipher algo for AEAD.  */
+  byte cipher_algo;
+
+  /* The AEAD algo.  */
+  byte aead_algo;
+
+  /* The encoded chunk byte for AEAD.  */
+  byte chunkbyte;
+
+  /* The decoded CHUNKBYTE.  */
+  uint64_t chunksize;
+
+  /* The chunk index for AEAD.  */
+  uint64_t chunkindex;
+
+  /* The number of bytes in the current chunk.  */
+  uint64_t chunklen;
+
+  /* The total count of decrypted plaintext octets.  */
+  uint64_t total;
+
+  /* Remaining bytes in the packet according to the packet header.
+   * Not used if PARTIAL is true.  */
+  size_t length;
+};
+typedef struct decode_filter_context_s *decode_filter_ctx_t;
 
 
 /* Helper to release the decode context.  */
@@ -69,6 +118,78 @@ 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.  */
+static gpg_error_t
+aead_set_nonce (decode_filter_ctx_t dfx)
+{
+  unsigned char nonce[16];
+  int i;
+
+  switch (dfx->aead_algo)
+    {
+    case AEAD_ALGO_OCB:
+      memcpy (nonce, dfx->startiv, 15);
+      i = 7;
+      break;
+
+    case AEAD_ALGO_EAX:
+      memcpy (nonce, dfx->startiv, 16);
+      i = 8;
+      break;
+
+    default:
+      BUG ();
+    }
+  nonce[i++] ^= dfx->chunkindex >> 56;
+  nonce[i++] ^= dfx->chunkindex >> 48;
+  nonce[i++] ^= dfx->chunkindex >> 40;
+  nonce[i++] ^= dfx->chunkindex >> 32;
+  nonce[i++] ^= dfx->chunkindex >> 24;
+  nonce[i++] ^= dfx->chunkindex >> 16;
+  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];
+
+  ad[0] = (0xc0 | PKT_ENCRYPTED_AEAD);
+  ad[1] = 1;
+  ad[2] = dfx->cipher_algo;
+  ad[3] = dfx->aead_algo;
+  ad[4] = dfx->chunkbyte;
+  ad[5] = dfx->chunkindex >> 56;
+  ad[6] = dfx->chunkindex >> 48;
+  ad[7] = dfx->chunkindex >> 40;
+  ad[8] = dfx->chunkindex >> 32;
+  ad[9] = dfx->chunkindex >> 24;
+  ad[10]= dfx->chunkindex >> 16;
+  ad[11]= dfx->chunkindex >>  8;
+  ad[12]= dfx->chunkindex;
+  if (final)
+    {
+      ad[13] = dfx->total >> 56;
+      ad[14] = dfx->total >> 48;
+      ad[15] = dfx->total >> 40;
+      ad[16] = dfx->total >> 32;
+      ad[17] = dfx->total >> 24;
+      ad[18] = dfx->total >> 16;
+      ad[19] = dfx->total >>  8;
+      ad[20] = dfx->total;
+    }
+  log_printhex (ad, final? 21 : 13, "authdata:");
+  return gcry_cipher_authenticate (dfx->cipher_hd, ad, final? 21 : 13);
+}
+
 
 /****************
  * Decrypt the data, specified by ED with the key DEK.
@@ -80,8 +201,8 @@ decrypt_data (ctrl_t ctrl, void *procctx, PKT_encrypted *ed, DEK *dek)
   byte *p;
   int rc=0, c, i;
   byte temp[32];
-  unsigned blocksize;
-  unsigned nprefix;
+  unsigned int blocksize;
+  unsigned int nprefix;
 
   dfx = xtrycalloc (1, sizeof *dfx);
   if (!dfx)
@@ -109,19 +230,18 @@ decrypt_data (ctrl_t ctrl, void *procctx, PKT_encrypted *ed, DEK *dek)
       goto leave;
     }
 
-  {
-    char buf[20];
-
-    snprintf (buf, sizeof buf, "%d %d", ed->mdc_method, dek->algo);
-    write_status_text (STATUS_DECRYPTION_INFO, buf);
-  }
+  write_status_printf (STATUS_DECRYPTION_INFO, "%d %d %d",
+                       ed->mdc_method, dek->algo, ed->aead_algo);
 
   if (opt.show_session_key)
     {
-      char numbuf[25];
+      char numbuf[30];
       char *hexbuf;
 
-      snprintf (numbuf, sizeof numbuf, "%d:", dek->algo);
+      if (ed->aead_algo)
+        snprintf (numbuf, sizeof numbuf, "%d.%u:", dek->algo, ed->aead_algo);
+      else
+        snprintf (numbuf, sizeof numbuf, "%d:", dek->algo);
       hexbuf = bin2hex (dek->key, dek->keylen, NULL);
       if (!hexbuf)
         {
@@ -139,95 +259,209 @@ decrypt_data (ctrl_t ctrl, void *procctx, PKT_encrypted *ed, DEK *dek)
   blocksize = openpgp_cipher_get_algo_blklen (dek->algo);
   if ( !blocksize || blocksize > 16 )
     log_fatal ("unsupported blocksize %u\n", blocksize );
-  nprefix = blocksize;
-  if ( ed->len && ed->len < (nprefix+2) )
+  if (ed->aead_algo)
     {
-       /* An invalid message.  We can't check that during parsing
-          because we may not know the used cipher then.  */
-      rc = gpg_error (GPG_ERR_INV_PACKET);
-      goto leave;
-    }
+      enum gcry_cipher_modes ciphermode;
+      unsigned int startivlen;
 
-  if ( ed->mdc_method )
-    {
-      if (gcry_md_open (&dfx->mdc_hash, ed->mdc_method, 0 ))
-        BUG ();
-      if ( DBG_HASHING )
-        gcry_md_debug (dfx->mdc_hash, "checkmdc");
-    }
+      if (blocksize != 16)
+        {
+          rc = gpg_error (GPG_ERR_CIPHER_ALGO);
+          goto leave;
+        }
 
-  rc = openpgp_cipher_open (&dfx->cipher_hd, dek->algo,
-			    GCRY_CIPHER_MODE_CFB,
-			    (GCRY_CIPHER_SECURE
-			     | ((ed->mdc_method || dek->algo >= 100)?
-				0 : GCRY_CIPHER_ENABLE_SYNC)));
-  if (rc)
-    {
-      /* We should never get an error here cause we already checked
-       * that the algorithm is available.  */
-      BUG();
-    }
+      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;
+        }
+      log_assert (startivlen <= sizeof dfx->startiv);
 
+      if (ed->chunkbyte != 10)
+        {
+          /* FIXME */
+          log_error ("unsupported chunkbyte %u\n", ed->chunkbyte);
+          rc = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+          goto leave;
+        }
 
-  /* log_hexdump( "thekey", dek->key, dek->keylen );*/
-  rc = gcry_cipher_setkey (dfx->cipher_hd, dek->key, dek->keylen);
-  if ( gpg_err_code (rc) == GPG_ERR_WEAK_KEY )
-    {
-      log_info(_("WARNING: message was encrypted with"
-                 " a weak key in the symmetric cipher.\n"));
-      rc=0;
-    }
-  else if( rc )
-    {
-      log_error("key setup failed: %s\n", gpg_strerror (rc) );
-      goto leave;
-    }
+      /* Read the Start-IV. */
+      if (ed->len)
+        {
+          for (i=0; i < startivlen && ed->len; i++, ed->len--)
+            {
+              if ((c=iobuf_get (ed->buf)) == -1)
+                break;
+              dfx->startiv[i] = c;
+            }
+        }
+      else
+        {
+          for (i=0; i < startivlen; i++ )
+            if ( (c=iobuf_get (ed->buf)) == -1 )
+              break;
+            else
+              dfx->startiv[i] = c;
+        }
+      if (i != startivlen)
+        {
+          log_error ("Start-IV in AEAD packet too short (%d/%u)\n",
+                     i, startivlen);
+          rc = gpg_error (GPG_ERR_TOO_SHORT);
+          goto leave;
+        }
 
-  if (!ed->buf)
-    {
-      log_error(_("problem handling encrypted packet\n"));
-      goto leave;
-    }
+      dfx->cipher_algo = ed->cipher_algo;
+      dfx->aead_algo = ed->aead_algo;
+      dfx->chunkbyte = ed->chunkbyte;
+      dfx->chunksize = (uint64_t)1 << (dfx->chunkbyte + 6);
 
-  gcry_cipher_setiv (dfx->cipher_hd, NULL, 0);
+      if (dek->algo != dfx->cipher_algo)
+        log_info ("Note: different cipher algorithms used (%s/%s)\n",
+                  openpgp_cipher_algo_name (dek->algo),
+                  openpgp_cipher_algo_name (dfx->cipher_algo));
 
-  if ( ed->len )
-    {
-      for (i=0; i < (nprefix+2) && ed->len; i++, ed->len-- )
+      rc = openpgp_cipher_open (&dfx->cipher_hd,
+                                dfx->cipher_algo,
+                                ciphermode,
+                                GCRY_CIPHER_SECURE);
+      if (rc)
+        goto leave; /* Should never happen.  */
+
+      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)
         {
-          if ( (c=iobuf_get(ed->buf)) == -1 )
-            break;
-          else
-            temp[i] = c;
+          log_info (_("WARNING: message was encrypted with"
+                      " a weak key in the symmetric cipher.\n"));
+          rc = 0;
         }
+      else if (rc)
+        {
+          log_error("key setup failed: %s\n", gpg_strerror (rc));
+          goto leave;
+        }
+
+      if (!ed->buf)
+        {
+          log_error(_("problem handling encrypted packet\n"));
+          goto leave;
+        }
+
+      rc = aead_set_nonce (dfx);
+      if (rc)
+        goto leave;
+
+      rc = aead_set_ad (dfx, 0);
+      if (rc)
+        goto leave;
+
     }
-  else
+  else /* CFB encryption.  */
     {
-      for (i=0; i < (nprefix+2); i++ )
-        if ( (c=iobuf_get(ed->buf)) == -1 )
-          break;
-        else
-          temp[i] = c;
-    }
+      nprefix = blocksize;
+      if ( ed->len && ed->len < (nprefix+2) )
+        {
+          /* An invalid message.  We can't check that during parsing
+             because we may not know the used cipher then.  */
+          rc = gpg_error (GPG_ERR_INV_PACKET);
+          goto leave;
+        }
+
+      if ( ed->mdc_method )
+        {
+          if (gcry_md_open (&dfx->mdc_hash, ed->mdc_method, 0 ))
+            BUG ();
+          if ( DBG_HASHING )
+            gcry_md_debug (dfx->mdc_hash, "checkmdc");
+        }
+
+      rc = openpgp_cipher_open (&dfx->cipher_hd, dek->algo,
+                                GCRY_CIPHER_MODE_CFB,
+                                (GCRY_CIPHER_SECURE
+                                 | ((ed->mdc_method || dek->algo >= 100)?
+                                    0 : GCRY_CIPHER_ENABLE_SYNC)));
+      if (rc)
+        {
+          /* We should never get an error here cause we already checked
+           * that the algorithm is available.  */
+          BUG();
+        }
 
-  gcry_cipher_decrypt (dfx->cipher_hd, temp, nprefix+2, NULL, 0);
-  gcry_cipher_sync (dfx->cipher_hd);
-  p = temp;
-  /* log_hexdump( "prefix", temp, nprefix+2 ); */
-  if (dek->symmetric
-      && (p[nprefix-2] != p[nprefix] || p[nprefix-1] != p[nprefix+1]) )
-    {
-      rc = gpg_error (GPG_ERR_BAD_KEY);
-      goto leave;
-    }
 
-  if ( dfx->mdc_hash )
-    gcry_md_write (dfx->mdc_hash, temp, nprefix+2);
+      /* log_hexdump( "thekey", dek->key, dek->keylen );*/
+      rc = gcry_cipher_setkey (dfx->cipher_hd, dek->key, dek->keylen);
+      if ( gpg_err_code (rc) == GPG_ERR_WEAK_KEY )
+        {
+          log_info(_("WARNING: message was encrypted with"
+                     " a weak key in the symmetric cipher.\n"));
+          rc=0;
+        }
+      else if( rc )
+        {
+          log_error("key setup failed: %s\n", gpg_strerror (rc) );
+          goto leave;
+        }
+
+      if (!ed->buf)
+        {
+          log_error(_("problem handling encrypted packet\n"));
+          goto leave;
+        }
+
+      gcry_cipher_setiv (dfx->cipher_hd, NULL, 0);
+
+      if ( ed->len )
+        {
+          for (i=0; i < (nprefix+2) && ed->len; i++, ed->len-- )
+            {
+              if ( (c=iobuf_get(ed->buf)) == -1 )
+                break;
+              else
+                temp[i] = c;
+            }
+        }
+      else
+        {
+          for (i=0; i < (nprefix+2); i++ )
+            if ( (c=iobuf_get(ed->buf)) == -1 )
+              break;
+            else
+              temp[i] = c;
+        }
+
+      gcry_cipher_decrypt (dfx->cipher_hd, temp, nprefix+2, NULL, 0);
+      gcry_cipher_sync (dfx->cipher_hd);
+      p = temp;
+      /* log_hexdump( "prefix", temp, nprefix+2 ); */
+      if (dek->symmetric
+          && (p[nprefix-2] != p[nprefix] || p[nprefix-1] != p[nprefix+1]) )
+        {
+          rc = gpg_error (GPG_ERR_BAD_KEY);
+          goto leave;
+        }
+
+      if ( dfx->mdc_hash )
+        gcry_md_write (dfx->mdc_hash, temp, nprefix+2);
+    }
 
   dfx->refcount++;
-  dfx->partial = ed->is_partial;
+  dfx->partial = !!ed->is_partial;
   dfx->length = ed->len;
-  if ( ed->mdc_method )
+  if (ed->aead_algo)
+    iobuf_push_filter ( ed->buf, aead_decode_filter, dfx );
+  else if (ed->mdc_method)
     iobuf_push_filter ( ed->buf, mdc_decode_filter, dfx );
   else
     iobuf_push_filter ( ed->buf, decode_filter, dfx );
@@ -307,6 +541,296 @@ 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)
+{
+  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.  */
+  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++)
+        {
+          if ((c = iobuf_get (a)) == -1)
+            break;
+          buf[n] = c;
+        }
+    }
+  else
+    {
+      for (n=32; n < 64 && dfx->length; n++, dfx->length--)
+        {
+          if ((c = iobuf_get (a)) == -1)
+            break; /* Premature EOF.  */
+          buf[n] = c;
+        }
+    }
+
+  if (n == 64)
+    {
+      /* 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)
+        {
+          memcpy (buf, buf+32, 32);
+          n = 32;  /* Continue at this position.  */
+        }
+      else
+        {
+          memcpy (buf, dfx->defer, 32);
+        }
+
+      /* Now fill up the provided buffer.  */
+      if (dfx->partial)
+        {
+          for (; n < size; n++ )
+            {
+              if ((c = iobuf_get (a)) == -1)
+                {
+                  dfx->eof_seen = 1; /* Normal EOF. */
+                  break;
+                }
+              buf[n] = c;
+            }
+        }
+      else
+        {
+          for (; n < size && dfx->length; n++, dfx->length--)
+            {
+              c = iobuf_get (a);
+              if (c == -1)
+                {
+                  dfx->eof_seen = 3; /* Premature EOF. */
+                  break;
+                }
+              buf[n] = c;
+            }
+          if (!dfx->length)
+            dfx->eof_seen = 1; /* Normal EOF.  */
+        }
+
+      /* 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. */
+    }
+
+  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)
+    {
+      log_assert (dfx->eof_seen);
+      err = gpg_error (GPG_ERR_EOF);
+    }
+  else
+    {
+      size_t off = 0;
+
+      if (dfx->chunklen + n >= dfx->chunksize)
+        {
+          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);
+          if (err)
+            goto leave;
+        }
+
+      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.  */
+          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);
+      if (err)
+        {
+          log_debug ("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);
+
+          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;
+            }
+
+          /* Check the final chunk.  */
+          dfx->chunkindex++;
+          err = aead_set_nonce (dfx);
+          if (err)
+            goto leave;
+          err = aead_set_ad (dfx, 1);
+          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:"); */
+        }
+      else
+        n += off;
+    }
+
+ leave:
+  /* In case of a real error we better wipe out the buffer than to
+   * keep partly encrypted data.  */
+  if (err && gpg_err_code (err) != GPG_ERR_EOF)
+    memset (buf, 0, size);
+  *ret_len = n;
+
+  return err;
+}
+
+
+/* The IOBUF filter used to decrypt AEAD encrypted data.  */
+static int
+aead_decode_filter (void *opaque, int control, IOBUF a,
+                    byte *buf, size_t *ret_len)
+{
+  decode_filter_ctx_t dfx = opaque;
+  int rc = 0;
+
+  if ( control == IOBUFCTRL_UNDERFLOW && dfx->eof_seen )
+    {
+      *ret_len = 0;
+      rc = -1;
+    }
+  else if ( control == IOBUFCTRL_UNDERFLOW )
+    {
+      log_assert (a);
+
+      rc = aead_underflow (dfx, a, buf, ret_len);
+      if (gpg_err_code (rc) == GPG_ERR_EOF)
+        rc = -1; /* We need to use the old convention in the filter.  */
+
+    }
+  else if ( control == IOBUFCTRL_FREE )
+    {
+      release_dfx_context (dfx);
+    }
+  else if ( control == IOBUFCTRL_DESC )
+    {
+      mem2str (buf, "aead_decode_filter", *ret_len);
+    }
+
+  return rc;
+}
+
 
 static int
 mdc_decode_filter (void *opaque, int control, IOBUF a,
@@ -365,6 +889,7 @@ mdc_decode_filter (void *opaque, int control, IOBUF a,
 	    }
           else
             {
+
               memcpy (buf, dfx->defer, 22);
 	    }
           /* Fill up the buffer. */
diff --git a/g10/dek.h b/g10/dek.h
index 64e98fc..1e861f5 100644
--- a/g10/dek.h
+++ b/g10/dek.h
@@ -32,8 +32,8 @@ typedef struct
    * verbose mode.  */
   int algo_info_printed : 1;
 
-  /* AEAD shall be used.  */
-  int use_aead : 1;
+  /* AEAD shall be used.  The value is the AEAD algo. */
+  int use_aead : 4;
 
   /* MDC shall be used.  */
   int use_mdc : 1;
diff --git a/g10/encrypt.c b/g10/encrypt.c
index 01feb4a..8568497 100644
--- a/g10/encrypt.c
+++ b/g10/encrypt.c
@@ -123,7 +123,7 @@ use_aead (pk_list_t pk_list, int algo)
       return 0;
     }
 
-  can_use = openpgp_cipher_get_algo_blklen (algo) != 16;
+  can_use = openpgp_cipher_get_algo_blklen (algo) == 16;
 
   /* With --force-mdc we clearly do not want AEAD.  */
   if (opt.force_mdc)
@@ -133,12 +133,15 @@ use_aead (pk_list_t pk_list, int algo)
   if (opt.force_aead)
     {
       if (!can_use)
-        log_info ("Warning: request to use AEAD ignored for cipher '%s'\n",
-                  openpgp_cipher_algo_name (algo));
+        {
+          log_info ("Warning: request to use AEAD ignored for cipher '%s'\n",
+                    openpgp_cipher_algo_name (algo));
+          return 0;
+        }
       return 1;
     }
 
-  /* AEAD does noly work with 128 bit cipher blocklength.  */
+  /* AEAD does only work with 128 bit cipher blocklength.  */
   if (!can_use)
     return 0;
 
@@ -307,7 +310,7 @@ encrypt_simple (const char *filename, int mode, int use_seskey)
                  openpgp_cipher_algo_name (cfx.dek->algo));
 
       if (use_aead (NULL, cfx.dek->algo))
-        cfx.dek->use_aead = 1;
+        cfx.dek->use_aead = default_aead_algo ();
       else
         cfx.dek->use_mdc = !!use_mdc (NULL, cfx.dek->algo);
     }
diff --git a/g10/filter.h b/g10/filter.h
index 2924355..cd177f4 100644
--- a/g10/filter.h
+++ b/g10/filter.h
@@ -88,15 +88,52 @@ struct compress_filter_context_s {
 typedef struct compress_filter_context_s compress_filter_context_t;
 
 
-typedef struct {
-    DEK *dek;
-    u32 datalen;
-    gcry_cipher_hd_t cipher_hd;
-    unsigned int wrote_header : 1;
-    unsigned int short_blklen_warn : 1;
-    unsigned long short_blklen_count;
-    gcry_md_hd_t mdc_hash;
-    byte enchash[20];
+typedef struct
+{
+  /* Object with the key and algo */
+  DEK *dek;
+
+  /* Length of the data to encrypt if known - 32 bit because OpenPGP
+   * requires partial encoding for a larger data size.  */
+  u32 datalen;
+
+  /* The current cipher handle.  */
+  gcry_cipher_hd_t cipher_hd;
+
+  /* Various processing flags.  */
+  unsigned int wrote_header : 1;
+  unsigned int short_blklen_warn : 1;
+  unsigned long short_blklen_count;
+
+  /* The encoded chunk byte for AEAD.  */
+  byte chunkbyte;
+
+  /* The decoded CHUNKBYTE.  */
+  uint64_t chunksize;
+
+  /* The chunk index for AEAD.  */
+  uint64_t chunkindex;
+
+  /* The number of bytes in the current chunk.  */
+  uint64_t chunklen;
+
+  /* The total count of encrypted plaintext octets.  Note that we
+   * don't care about encrypting more than 16 Exabyte. */
+  uint64_t total;
+
+  /* The hash context and a buffer used for MDC.  */
+  gcry_md_hd_t mdc_hash;
+  byte enchash[20];
+
+  /* The start IV for AEAD encryption.   */
+  byte startiv[16];
+
+  /* Using a large buffer for encryption makes processing easier and
+   * also makes sure the data is well aligned.  */
+  char *buffer;
+  size_t bufsize;  /* Allocated length.  */
+  size_t buflen;   /* Used length.       */
+
 } cipher_filter_context_t;
 
 
diff --git a/g10/gpg.c b/g10/gpg.c
index a643388..2ae3e8a 100644
--- a/g10/gpg.c
+++ b/g10/gpg.c
@@ -3882,7 +3882,7 @@ main (int argc, char **argv)
     /* Check our chosen algorithms against the list of legal
        algorithms. */
 
-    if(!GNUPG)
+    if(!GNUPG && !opt.flags.rfc4880bis)
       {
 	const char *badalg=NULL;
 	preftype_t badtype=PREFTYPE_NONE;
diff --git a/g10/mainproc.c b/g10/mainproc.c
index b712e60..92da982 100644
--- a/g10/mainproc.c
+++ b/g10/mainproc.c
@@ -648,6 +648,7 @@ proc_encrypted (CTX c, PACKET *pkt)
   else if (!result
            && !opt.ignore_mdc_error
            && !pkt->pkt.encrypted->mdc_method
+           && !pkt->pkt.encrypted->aead_algo
            && openpgp_cipher_get_algo_blklen (c->dek->algo) != 8
            && c->dek->algo != CIPHER_ALGO_TWOFISH)
     {
@@ -662,17 +663,25 @@ proc_encrypted (CTX c, PACKET *pkt)
       write_status (STATUS_DECRYPTION_FAILED);
     }
   else if (!result || (gpg_err_code (result) == GPG_ERR_BAD_SIGNATURE
+                       && !pkt->pkt.encrypted->aead_algo
                        && opt.ignore_mdc_error))
     {
+      /* All is fine or for an MDC message the MDC failed but the
+       * --ignore-mdc-error option is active.  For compatibility
+       * reasons we issue GOODMDC also for AEAD messages.  */
       write_status (STATUS_DECRYPTION_OKAY);
       if (opt.verbose > 1)
         log_info(_("decryption okay\n"));
-      if (pkt->pkt.encrypted->mdc_method && !result)
+
+      if (pkt->pkt.encrypted->aead_algo)
+        write_status (STATUS_GOODMDC);
+      else if (pkt->pkt.encrypted->mdc_method && !result)
         write_status (STATUS_GOODMDC);
       else if (!opt.no_mdc_warn)
         log_info (_("WARNING: message was not integrity protected\n"));
     }
-  else if (gpg_err_code (result) == GPG_ERR_BAD_SIGNATURE)
+  else if (gpg_err_code (result) == GPG_ERR_BAD_SIGNATURE
+           || gpg_err_code (result) == GPG_ERR_TRUNCATED)
     {
       glo_ctrl.lasterr = result;
       log_error (_("WARNING: encrypted message has been manipulated!\n"));
@@ -1391,7 +1400,8 @@ do_proc_packets (ctrl_t ctrl, CTX c, iobuf_t a)
             case PKT_PUBKEY_ENC:    proc_pubkey_enc (ctrl, c, pkt); break;
             case PKT_SYMKEY_ENC:    proc_symkey_enc (c, pkt); break;
             case PKT_ENCRYPTED:
-            case PKT_ENCRYPTED_MDC: proc_encrypted (c, pkt); break;
+            case PKT_ENCRYPTED_MDC:
+            case PKT_ENCRYPTED_AEAD:proc_encrypted (c, pkt); break;
             case PKT_COMPRESSED:    rc = proc_compressed (c, pkt); break;
             default: newpkt = 0; break;
 	    }
@@ -1407,6 +1417,7 @@ do_proc_packets (ctrl_t ctrl, CTX c, iobuf_t a)
             case PKT_PUBKEY_ENC:
             case PKT_ENCRYPTED:
             case PKT_ENCRYPTED_MDC:
+            case PKT_ENCRYPTED_AEAD:
               write_status_text( STATUS_UNEXPECTED, "0" );
               rc = GPG_ERR_UNEXPECTED;
               goto leave;
@@ -1434,7 +1445,8 @@ do_proc_packets (ctrl_t ctrl, CTX c, iobuf_t a)
             case PKT_SYMKEY_ENC:  proc_symkey_enc (c, pkt); break;
             case PKT_PUBKEY_ENC:  proc_pubkey_enc (ctrl, c, pkt); break;
             case PKT_ENCRYPTED:
-            case PKT_ENCRYPTED_MDC: proc_encrypted (c, pkt); break;
+            case PKT_ENCRYPTED_MDC:
+            case PKT_ENCRYPTED_AEAD: proc_encrypted (c, pkt); break;
             case PKT_PLAINTEXT:   proc_plaintext (c, pkt); break;
             case PKT_COMPRESSED:  rc = proc_compressed (c, pkt); break;
             case PKT_ONEPASS_SIG: newpkt = add_onepass_sig (c, pkt); break;
@@ -1461,7 +1473,8 @@ do_proc_packets (ctrl_t ctrl, CTX c, iobuf_t a)
             case PKT_PUBKEY_ENC:  proc_pubkey_enc (ctrl, c, pkt); break;
             case PKT_SYMKEY_ENC:  proc_symkey_enc (c, pkt); break;
             case PKT_ENCRYPTED:
-            case PKT_ENCRYPTED_MDC: proc_encrypted (c, pkt); break;
+            case PKT_ENCRYPTED_MDC:
+            case PKT_ENCRYPTED_AEAD: proc_encrypted (c, pkt); break;
             case PKT_PLAINTEXT:   proc_plaintext (c, pkt); break;
             case PKT_COMPRESSED:  rc = proc_compressed (c, pkt); break;
             case PKT_ONEPASS_SIG: newpkt = add_onepass_sig (c, pkt); break;
diff --git a/g10/packet.h b/g10/packet.h
index 894b389..4d15574 100644
--- a/g10/packet.h
+++ b/g10/packet.h
@@ -459,12 +459,13 @@ typedef struct {
 typedef struct {
   /* Remaining length of encrypted data. */
   u32  len;
-  /* When encrypting, the first block size bytes of data are random
-     data and the following 2 bytes are copies of the last two bytes
-     of the random data (RFC 4880, Section 5.7).  This provides a
-     simple check that the key is correct.  extralen is the size of
-     this extra data.  This is used by build_packet when writing out
-     the packet's header. */
+  /* When encrypting in CFB mode, the first block size bytes of data
+   * are random data and the following 2 bytes are copies of the last
+   * two bytes of the random data (RFC 4880, Section 5.7).  This
+   * provides a simple check that the key is correct.  EXTRALEN is the
+   * size of this extra data or, in AEAD mode, the length of the
+   * headers and the tags.  This is used by build_packet when writing
+   * out the packet's header. */
   int  extralen;
   /* Whether the serialized version of the packet used / should use
      the new format.  */
@@ -476,6 +477,15 @@ typedef struct {
   /* If 0, MDC is disabled.  Otherwise, the MDC method that was used
      (only DIGEST_ALGO_SHA1 has ever been defined).  */
   byte mdc_method;
+  /* If 0, AEAD is not used.  Otherwise, the used AEAD algorithm.
+   * MDC_METHOD (above) shall be zero if AEAD is used.  */
+  byte aead_algo;
+  /* The cipher algo for/from the AEAD packet.  0 for other encryption
+   * packets. */
+  byte cipher_algo;
+  /* The chunk byte from the AEAD packet.  */
+  byte chunkbyte;
+
   /* An iobuf holding the data to be decrypted.  (This is not used for
      encryption!)  */
   iobuf_t buf;
diff --git a/g10/parse-packet.c b/g10/parse-packet.c
index eee14f6..de51770 100644
--- a/g10/parse-packet.c
+++ b/g10/parse-packet.c
@@ -1,7 +1,6 @@
 /* parse-packet.c  - read packets
- * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
- *               2007, 2009, 2010 Free Software Foundation, Inc.
- * Copyright (C) 2014 Werner Koch
+ * Copyright (C) 1998-2007, 2009-2010 Free Software Foundation, Inc.
+ * Copyright (C) 2014, 2018 Werner Koch
  * Copyright (C) 2015 g10 Code GmbH
  *
  * This file is part of GnuPG.
@@ -18,6 +17,7 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: GPL-3.0+
  */
 
 #include <config.h>
@@ -82,6 +82,9 @@ static int parse_compressed (IOBUF inp, int pkttype, unsigned long pktlen,
 			     PACKET * packet, int new_ctb);
 static int parse_encrypted (IOBUF inp, int pkttype, unsigned long pktlen,
 			    PACKET * packet, int new_ctb, int partial);
+static gpg_error_t parse_encrypted_aead (IOBUF inp, int pkttype,
+                                         unsigned long pktlen, PACKET *packet,
+                                         int partial);
 static int parse_mdc (IOBUF inp, int pkttype, unsigned long pktlen,
 		      PACKET * packet, int new_ctb);
 static int parse_gpg_control (IOBUF inp, int pkttype, unsigned long pktlen,
@@ -636,6 +639,7 @@ parse (parse_packet_ctx_t ctx, PACKET *pkt, int onlykeypkts, off_t * retpos,
             case PKT_PLAINTEXT:
             case PKT_ENCRYPTED:
             case PKT_ENCRYPTED_MDC:
+            case PKT_ENCRYPTED_AEAD:
             case PKT_COMPRESSED:
               iobuf_set_partial_body_length_mode (inp, c & 0xff);
               pktlen = 0;	/* To indicate partial length.  */
@@ -823,6 +827,9 @@ parse (parse_packet_ctx_t ctx, PACKET *pkt, int onlykeypkts, off_t * retpos,
     case PKT_MDC:
       rc = parse_mdc (inp, pkttype, pktlen, pkt, new_ctb);
       break;
+    case PKT_ENCRYPTED_AEAD:
+      rc = parse_encrypted_aead (inp, pkttype, pktlen, pkt, partial);
+      break;
     case PKT_GPG_CONTROL:
       rc = parse_gpg_control (inp, pkttype, pktlen, pkt, partial);
       break;
@@ -1392,6 +1399,11 @@ dump_sig_subpkt (int hashed, int type, int critical,
       for (i = 0; i < length; i++)
 	es_fprintf (listfp, " %d", buffer[i]);
       break;
+    case SIGSUBPKT_PREF_AEAD:
+      es_fputs ("pref-aead-algos:", listfp);
+      for (i = 0; i < length; i++)
+        es_fprintf (listfp, " %d", buffer[i]);
+      break;
     case SIGSUBPKT_REV_KEY:
       es_fputs ("revocation key: ", listfp);
       if (length < 22)
@@ -1554,6 +1566,7 @@ parse_one_sig_subpkt (const byte * buffer, size_t n, int type)
     case SIGSUBPKT_KEY_FLAGS:
     case SIGSUBPKT_KS_FLAGS:
     case SIGSUBPKT_PREF_SYM:
+    case SIGSUBPKT_PREF_AEAD:
     case SIGSUBPKT_PREF_HASH:
     case SIGSUBPKT_PREF_COMPR:
     case SIGSUBPKT_POLICY:
@@ -1636,6 +1649,7 @@ can_handle_critical (const byte * buffer, size_t n, int type)
     case SIGSUBPKT_ISSUER:	/* issuer key ID */
     case SIGSUBPKT_ISSUER_FPR:	/* issuer fingerprint */
     case SIGSUBPKT_PREF_SYM:
+    case SIGSUBPKT_PREF_AEAD:
     case SIGSUBPKT_PREF_HASH:
     case SIGSUBPKT_PREF_COMPR:
     case SIGSUBPKT_KEY_FLAGS:
@@ -3161,6 +3175,9 @@ parse_encrypted (IOBUF inp, int pkttype, unsigned long pktlen,
   ed->buf = NULL;
   ed->new_ctb = new_ctb;
   ed->is_partial = partial;
+  ed->aead_algo = 0;
+  ed->cipher_algo = 0; /* Only used with AEAD.  */
+  ed->chunkbyte = 0;   /* Only used with AEAD.  */
   if (pkttype == PKT_ENCRYPTED_MDC)
     {
       /* Fixme: add some pktlen sanity checks.  */
@@ -3252,6 +3269,81 @@ parse_mdc (IOBUF inp, int pkttype, unsigned long pktlen,
 }
 
 
+static gpg_error_t
+parse_encrypted_aead (iobuf_t inp, int pkttype, unsigned long pktlen,
+                      PACKET *pkt, int partial)
+{
+  int rc = 0;
+  PKT_encrypted *ed;
+  unsigned long orig_pktlen = pktlen;
+  int version;
+
+  ed = pkt->pkt.encrypted = xtrymalloc (sizeof *pkt->pkt.encrypted);
+  if (!ed)
+    return gpg_error_from_syserror ();
+  ed->len = 0;
+  ed->extralen = 0;  /* (only used in build_packet.)  */
+  ed->buf = NULL;
+  ed->new_ctb = 1;   /* (packet number requires a new CTB anyway.)  */
+  ed->is_partial = partial;
+  ed->mdc_method = 0;
+  /* A basic sanity check.  We need one version byte, one algo byte,
+   * one aead algo byte, one chunkbyte, at least 15 byte IV.  */
+  if (orig_pktlen && pktlen < 19)
+    {
+      log_error ("packet(%d) too short\n", pkttype);
+      if (list_mode)
+        es_fputs (":aead encrypted packet: [too short]\n", listfp);
+      rc = gpg_error (GPG_ERR_INV_PACKET);
+      iobuf_skip_rest (inp, pktlen, partial);
+      goto leave;
+    }
+
+  version = iobuf_get_noeof (inp);
+  if (orig_pktlen)
+    pktlen--;
+  if (version != 1)
+    {
+      log_error ("aead encrypted packet with unknown version %d\n",
+                 version);
+      if (list_mode)
+        es_fputs (":aead encrypted packet: [unknown version]\n", listfp);
+      /*skip_rest(inp, pktlen); should we really do this? */
+      rc = gpg_error (GPG_ERR_INV_PACKET);
+      goto leave;
+    }
+
+  ed->cipher_algo = iobuf_get_noeof (inp);
+  if (orig_pktlen)
+    pktlen--;
+  ed->aead_algo = iobuf_get_noeof (inp);
+  if (orig_pktlen)
+    pktlen--;
+  ed->chunkbyte = iobuf_get_noeof (inp);
+  if (orig_pktlen)
+    pktlen--;
+
+  /* Store the remaining length of the encrypted data.  We read the
+   * rest during decryption.  */
+  ed->len = pktlen;
+
+  if (list_mode)
+    {
+      es_fprintf (listfp, ":aead encrypted packet: cipher=%u aead=%u cb=%u\n",
+                  ed->cipher_algo, ed->aead_algo, ed->chunkbyte);
+      if (orig_pktlen)
+	es_fprintf (listfp, "\tlength: %lu\n", orig_pktlen);
+      else
+	es_fprintf (listfp, "\tlength: unknown\n");
+    }
+
+  ed->buf = inp;
+
+ leave:
+  return rc;
+}
+
+
 /*
  * This packet is internally generated by us (in armor.c) to transfer
  * some information to the lower layer.  To make sure that this packet

-----------------------------------------------------------------------

Summary of changes:
 common/openpgpdefs.h |   2 +
 doc/DETAILS          |   7 +-
 g10/build-packet.c   |  31 +++
 g10/cipher-aead.c    | 393 +++++++++++++++++++++++++++-
 g10/decrypt-data.c   | 709 ++++++++++++++++++++++++++++++++++++++++++++-------
 g10/dek.h            |   4 +-
 g10/encrypt.c        |  13 +-
 g10/filter.h         |  55 +++-
 g10/gpg.c            |   2 +-
 g10/mainproc.c       |  23 +-
 g10/packet.h         |  22 +-
 g10/parse-packet.c   |  98 ++++++-
 12 files changed, 1228 insertions(+), 131 deletions(-)


hooks/post-receive
-- 
The GNU Privacy Guard
http://git.gnupg.org




More information about the Gnupg-commits mailing list