aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--common/openpgpdefs.h2
-rw-r--r--doc/DETAILS7
-rw-r--r--g10/build-packet.c31
-rw-r--r--g10/cipher-aead.c393
-rw-r--r--g10/decrypt-data.c709
-rw-r--r--g10/dek.h4
-rw-r--r--g10/encrypt.c13
-rw-r--r--g10/filter.h55
-rw-r--r--g10/gpg.c2
-rw-r--r--g10/mainproc.c23
-rw-r--r--g10/packet.h22
-rw-r--r--g10/parse-packet.c98
12 files changed, 1228 insertions, 131 deletions
diff --git a/common/openpgpdefs.h b/common/openpgpdefs.h
index aeb3389a7..8699a178d 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 3c089b278..a4063b4a6 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 d4a1d6a53..fc64c9c9f 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 bf0afcfcb..f247a83de 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 736534d75..80e16ecb7 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 64e98fca7..1e861f565 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 01feb4a7d..856849799 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 29243556e..cd177f4a4 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 a6433889e..2ae3e8aa0 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 b712e6048..92da98242 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 894b38946..4d155746e 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 eee14f64e..de51770af 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