gpg: First take on PKT_ENCRYPTED_AEAD.
authorWerner Koch <wk@gnupg.org>
Sun, 21 Jan 2018 15:24:43 +0000 (16:24 +0100)
committerWerner Koch <wk@gnupg.org>
Sun, 21 Jan 2018 15:30:53 +0000 (16:30 +0100)
* 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@gnupg.org>
12 files changed:
common/openpgpdefs.h
doc/DETAILS
g10/build-packet.c
g10/cipher-aead.c
g10/decrypt-data.c
g10/dek.h
g10/encrypt.c
g10/filter.h
g10/gpg.c
g10/mainproc.c
g10/packet.h
g10/parse-packet.c

index aeb3389..8699a17 100644 (file)
@@ -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,
index 3c089b2..a4063b4 100644 (file)
@@ -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.
index d4a1d6a..fc64c9c 100644 (file)
@@ -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.
 
index bf0afcf..f247a83 100644 (file)
 #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)
     {
index 736534d..80e16ec 100644 (file)
@@ -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.
  *
 #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. */
index 64e98fc..1e861f5 100644 (file)
--- 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;
index 01feb4a..8568497 100644 (file)
@@ -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);
     }
index 2924355..cd177f4 100644 (file)
@@ -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;
 
 
index a643388..2ae3e8a 100644 (file)
--- 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;
index b712e60..92da982 100644 (file)
@@ -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;
index 894b389..4d15574 100644 (file)
@@ -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;
index eee14f6..de51770 100644 (file)
@@ -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