aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--common/openpgpdefs.h11
-rw-r--r--doc/DETAILS3
-rw-r--r--g10/decrypt-data.c498
-rw-r--r--g10/dek.h24
-rw-r--r--g10/encrypt.c2
-rw-r--r--g10/main.h6
-rw-r--r--g10/mainproc.c188
-rw-r--r--g10/misc.c78
-rw-r--r--g10/packet.h29
-rw-r--r--g10/parse-packet.c140
10 files changed, 909 insertions, 70 deletions
diff --git a/common/openpgpdefs.h b/common/openpgpdefs.h
index 4dcfc25c4..5cc437a19 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). */
}
@@ -125,6 +126,16 @@ typedef enum
sigsubpkttype_t;
+/* Note that we encode the AEAD algo in a 3 bit field at some places. */
+typedef enum
+ {
+ AEAD_ALGO_NONE = 0,
+ AEAD_ALGO_EAX = 1,
+ AEAD_ALGO_OCB = 2
+ }
+aead_algo_t;
+
+
typedef enum
{
CIPHER_ALGO_NONE = 0,
diff --git a/doc/DETAILS b/doc/DETAILS
index 5d97f4bd4..acca24215 100644
--- a/doc/DETAILS
+++ b/doc/DETAILS
@@ -545,9 +545,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
diff --git a/g10/decrypt-data.c b/g10/decrypt-data.c
index 426b12421..771d07417 100644
--- a/g10/decrypt-data.c
+++ b/g10/decrypt-data.c
@@ -34,6 +34,8 @@
#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,
@@ -53,6 +55,9 @@ struct decode_filter_context_s
/* The hash handle for use in MDC mode. */
gcry_md_hd_t mdc_hash;
+ /* The start IV for AEAD encryption. */
+ byte startiv[16];
+
/* The holdback buffer and its used length. For AEAD we need 32+1
* bytes but we use 48 byte. For MDC we need 22 bytes; here
* holdbacklen will either 0 or 22. */
@@ -68,6 +73,27 @@ struct decode_filter_context_s
* 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;
@@ -94,6 +120,99 @@ release_dfx_context (decode_filter_ctx_t dfx)
}
+/* Set the nonce and the additional data for the current chunk. This
+ * also reset the decryption machinery so that the handle can be
+ * used for a new chunk. If FINAL is set the final AEAD chunk is
+ * processed. */
+static gpg_error_t
+aead_set_nonce_and_ad (decode_filter_ctx_t dfx, int final)
+{
+ gpg_error_t err;
+ unsigned char ad[21];
+ 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;
+
+ if (DBG_CRYPTO)
+ log_printhex ("nonce:", nonce, i);
+ err = gcry_cipher_setiv (dfx->cipher_hd, nonce, i);
+ if (err)
+ return err;
+
+ 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;
+ }
+ if (DBG_CRYPTO)
+ log_printhex ("authdata:", ad, final? 21 : 13);
+ return gcry_cipher_authenticate (dfx->cipher_hd, ad, final? 21 : 13);
+}
+
+
+/* Helper to check the 16 byte tag in TAGBUF. The FINAL flag is only
+ * for debug messages. */
+static gpg_error_t
+aead_checktag (decode_filter_ctx_t dfx, int final, const void *tagbuf)
+{
+ gpg_error_t err;
+
+ if (DBG_FILTER)
+ log_printhex ("tag:", tagbuf, 16);
+ err = gcry_cipher_checktag (dfx->cipher_hd, tagbuf, 16);
+ if (err)
+ {
+ log_error ("gcry_cipher_checktag%s failed: %s\n",
+ final? " (final)":"", gpg_strerror (err));
+ return err;
+ }
+ if (DBG_FILTER)
+ log_debug ("%stag is valid\n", final?"final ":"");
+ return 0;
+}
+
/****************
* Decrypt the data, specified by ED with the key DEK.
@@ -160,7 +279,96 @@ 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 );
- if (1)
+
+ if (ed->aead_algo)
+ {
+ enum gcry_cipher_modes ciphermode;
+ unsigned int startivlen;
+
+ if (blocksize != 16)
+ {
+ rc = gpg_error (GPG_ERR_CIPHER_ALGO);
+ goto leave;
+ }
+
+ rc = openpgp_aead_algo_info (ed->aead_algo, &ciphermode, &startivlen);
+ if (rc)
+ goto leave;
+ log_assert (startivlen <= sizeof dfx->startiv);
+
+ if (ed->chunkbyte > 56)
+ {
+ log_error ("invalid AEAD chunkbyte %u\n", ed->chunkbyte);
+ rc = gpg_error (GPG_ERR_INV_PACKET);
+ 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;
+ }
+
+ dfx->cipher_algo = ed->cipher_algo;
+ dfx->aead_algo = ed->aead_algo;
+ dfx->chunkbyte = ed->chunkbyte;
+ dfx->chunksize = (uint64_t)1 << (dfx->chunkbyte + 6);
+
+ 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));
+
+ rc = openpgp_cipher_open (&dfx->cipher_hd,
+ dfx->cipher_algo,
+ ciphermode,
+ GCRY_CIPHER_SECURE);
+ if (rc)
+ goto leave; /* Should never happen. */
+
+ if (DBG_CRYPTO)
+ log_printhex ("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;
+ }
+
+ }
+ else /* CFB encryption. */
{
nprefix = blocksize;
if ( ed->len && ed->len < (nprefix+2) )
@@ -249,9 +457,11 @@ decrypt_data (ctrl_t ctrl, void *procctx, PKT_encrypted *ed, DEK *dek)
}
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 );
@@ -324,7 +534,6 @@ decrypt_data (ctrl_t ctrl, void *procctx, PKT_encrypted *ed, DEK *dek)
/* log_printhex("MDC calc:", gcry_md_read (dfx->mdc_hash,0), datalen); */
}
-
leave:
release_dfx_context (dfx);
return rc;
@@ -385,6 +594,287 @@ fill_buffer (decode_filter_ctx_t dfx, iobuf_t stream,
}
+/* 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 allocated size of BUF. */
+ gpg_error_t err;
+ size_t totallen = 0; /* The number of bytes to return on success or EOF. */
+ size_t off = 0; /* The offset into the buffer. */
+ size_t len; /* The current number of bytes in BUF+OFF. */
+
+ log_assert (size > 48); /* Our code requires at least this size. */
+
+ /* Copy the rest from the last call of this function into BUF. */
+ len = dfx->holdbacklen;
+ dfx->holdbacklen = 0;
+ memcpy (buf, dfx->holdback, len);
+
+ if (DBG_FILTER)
+ log_debug ("aead_underflow: size=%zu len=%zu%s%s\n", size, len,
+ dfx->partial? " partial":"", dfx->eof_seen? " eof":"");
+
+ /* Read and fill up BUF. We need to watch out for an EOF so that we
+ * can detect the last chunk which is commonly shorter than the
+ * chunksize. After the last data byte from the last chunk 32 more
+ * bytes are expected for the last chunk's tag and the following
+ * final chunk's tag. To detect the EOF we need to try reading at least
+ * one further byte; however we try to read 16 extra bytes to avoid
+ * single byte reads in some lower layers. The outcome is that we
+ * have up to 48 extra extra octets which we will later put into the
+ * holdback buffer for the next invocation (which handles the EOF
+ * case). */
+ len = fill_buffer (dfx, a, buf, size, len);
+ if (len < 32)
+ {
+ /* Not enough data for the last two tags. */
+ err = gpg_error (GPG_ERR_TRUNCATED);
+ goto leave;
+ }
+ if (dfx->eof_seen)
+ {
+ /* If have seen an EOF we copy only the last two auth tags into
+ * the holdback buffer. */
+ dfx->holdbacklen = 32;
+ memcpy (dfx->holdback, buf+len-32, 32);
+ len -= 32;
+ }
+ else
+ {
+ /* If have not seen an EOF we copy the entire extra 48 bytes
+ * into the holdback buffer for processing at the next call of
+ * this function. */
+ dfx->holdbacklen = len > 48? 48 : len;
+ memcpy (dfx->holdback, buf+len-dfx->holdbacklen, dfx->holdbacklen);
+ len -= dfx->holdbacklen;
+ }
+ /* log_printhex (dfx->holdback, dfx->holdbacklen, "holdback:"); */
+
+ /* Decrypt the buffer. This first requires a loop to handle the
+ * case when a chunk ends within the buffer. */
+ if (DBG_FILTER)
+ log_debug ("decrypt: chunklen=%ju total=%ju size=%zu len=%zu%s\n",
+ dfx->chunklen, dfx->total, size, len,
+ dfx->eof_seen? " eof":"");
+
+ while (len && dfx->chunklen + len >= dfx->chunksize)
+ {
+ size_t n = dfx->chunksize - dfx->chunklen;
+ byte tagbuf[16];
+
+ if (DBG_FILTER)
+ log_debug ("chunksize will be reached: n=%zu\n", n);
+
+ if (!dfx->chunklen)
+ {
+ /* First data for this chunk - prepare. */
+ err = aead_set_nonce_and_ad (dfx, 0);
+ if (err)
+ goto leave;
+ }
+
+ /* log_printhex (buf, n, "ciph:"); */
+ gcry_cipher_final (dfx->cipher_hd);
+ err = gcry_cipher_decrypt (dfx->cipher_hd, buf+off, n, NULL, 0);
+ if (err)
+ {
+ log_error ("gcry_cipher_decrypt failed (1): %s\n",
+ gpg_strerror (err));
+ goto leave;
+ }
+ /* log_printhex (buf, n, "plai:"); */
+ totallen += n;
+ dfx->chunklen += n;
+ dfx->total += n;
+ off += n;
+ len -= n;
+
+ if (DBG_FILTER)
+ log_debug ("ndecrypted: %zu (nchunk=%ju) bytes left: %zu at off=%zu\n",
+ totallen, dfx->chunklen, len, off);
+
+ /* Check the tag. */
+ if (len < 16)
+ {
+ /* The tag is not entirely in the buffer. Read the rest of
+ * the tag from the holdback buffer. Then shift the holdback
+ * buffer and fill it up again. */
+ memcpy (tagbuf, buf+off, len);
+ memcpy (tagbuf + len, dfx->holdback, 16 - len);
+ dfx->holdbacklen -= 16-len;
+ memmove (dfx->holdback, dfx->holdback + (16-len), dfx->holdbacklen);
+
+ if (dfx->eof_seen)
+ {
+ /* We should have the last chunk's tag in TAGBUF and the
+ * final tag in HOLDBACKBUF. */
+ if (len || dfx->holdbacklen != 16)
+ {
+ /* Not enough data for the last two tags. */
+ err = gpg_error (GPG_ERR_TRUNCATED);
+ goto leave;
+ }
+ }
+ else
+ {
+ len = 0;
+ dfx->holdbacklen = fill_buffer (dfx, a, dfx->holdback, 48,
+ dfx->holdbacklen);
+ if (dfx->holdbacklen < 32)
+ {
+ /* Not enough data for the last two tags. */
+ err = gpg_error (GPG_ERR_TRUNCATED);
+ goto leave;
+ }
+ }
+ }
+ else /* We already have the full tag. */
+ {
+ memcpy (tagbuf, buf+off, 16);
+ /* Remove that tag from the output. */
+ memmove (buf + off, buf + off + 16, len - 16);
+ len -= 16;
+ }
+ err = aead_checktag (dfx, 0, tagbuf);
+ if (err)
+ goto leave;
+ dfx->chunklen = 0;
+ dfx->chunkindex++;
+
+ continue;
+ }
+
+ /* The bulk decryption of our buffer. */
+ if (len)
+ {
+ if (!dfx->chunklen)
+ {
+ /* First data for this chunk - prepare. */
+ err = aead_set_nonce_and_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. */
+ gcry_cipher_final (dfx->cipher_hd);
+ }
+ err = gcry_cipher_decrypt (dfx->cipher_hd, buf + off, len, NULL, 0);
+ if (err)
+ {
+ log_error ("gcry_cipher_decrypt failed (2): %s\n",
+ gpg_strerror (err));
+ goto leave;
+ }
+ totallen += len;
+ dfx->chunklen += len;
+ dfx->total += len;
+ if (DBG_FILTER)
+ log_debug ("ndecrypted: %zu (nchunk=%ju)\n", totallen, dfx->chunklen);
+ }
+
+ if (dfx->eof_seen)
+ {
+
+ if (dfx->chunklen)
+ {
+ if (DBG_FILTER)
+ log_debug ("eof seen: holdback has the last and final tag\n");
+ log_assert (dfx->holdbacklen >= 32);
+ err = aead_checktag (dfx, 0, dfx->holdback);
+ if (err)
+ goto leave;
+ dfx->chunklen = 0;
+ dfx->chunkindex++;
+ off = 16;
+ }
+ else
+ {
+ if (DBG_FILTER)
+ log_debug ("eof seen: holdback has the final tag\n");
+ log_assert (dfx->holdbacklen >= 16);
+ off = 0;
+ }
+
+ /* Check the final chunk. */
+ err = aead_set_nonce_and_ad (dfx, 1);
+ if (err)
+ goto leave;
+ gcry_cipher_final (dfx->cipher_hd);
+ /* Decrypt an empty string (using HOLDBACK as a dummy). */
+ err = gcry_cipher_decrypt (dfx->cipher_hd, dfx->holdback, 0, NULL, 0);
+ if (err)
+ {
+ log_error ("gcry_cipher_decrypt failed (final): %s\n",
+ gpg_strerror (err));
+ goto leave;
+ }
+ err = aead_checktag (dfx, 1, dfx->holdback+off);
+ if (err)
+ goto leave;
+ err = gpg_error (GPG_ERR_EOF);
+ }
+
+ leave:
+ if (DBG_FILTER)
+ log_debug ("aead_underflow: returning %zu (%s)\n",
+ totallen, gpg_strerror (err));
+
+ /* In case of an auth error we map the error code to the same as
+ * used by the MDC decryption. */
+ if (gpg_err_code (err) == GPG_ERR_CHECKSUM)
+ err = gpg_error (GPG_ERR_BAD_SIGNATURE);
+
+ /* In case of an error we better wipe out the buffer than to convey
+ * partly decrypted data. */
+ if (err && gpg_err_code (err) != GPG_ERR_EOF)
+ memset (buf, 0, size);
+
+ *ret_len = totallen;
+
+ 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,
byte *buf, size_t *ret_len)
diff --git a/g10/dek.h b/g10/dek.h
index 666810c3d..365449179 100644
--- a/g10/dek.h
+++ b/g10/dek.h
@@ -19,21 +19,31 @@
#ifndef G10_DEK_H
#define G10_DEK_H
-
typedef struct
{
/* The algorithm (e.g., CIPHER_ALGO_AES). */
int algo;
/* The length of the key (in bytes). */
int keylen;
+
/* Whether we've already printed information about this key. This
- is currently only used in decrypt_data() and only if we are in
- verbose mode. */
- int algo_info_printed;
- int use_mdc;
+ * is currently only used in decrypt_data() and only if we are in
+ * verbose mode. */
+ unsigned int algo_info_printed : 1;
+
+ /* AEAD shall be used. The value is the AEAD algo. */
+ int use_aead : 4;
+
+ /* MDC shall be used. */
+ unsigned int use_mdc : 1;
+
/* This key was read from a SK-ESK packet (see proc_symkey_enc). */
- int symmetric;
- byte key[32]; /* This is the largest used keylen (256 bit). */
+ unsigned int symmetric : 1;
+
+ /* This is the largest used keylen (256 bit). */
+ byte key[32];
+
+ /* The cacheid for the S2K. */
char s2k_cacheid[1+16+1];
} DEK;
diff --git a/g10/encrypt.c b/g10/encrypt.c
index 543f1a737..7ec239b37 100644
--- a/g10/encrypt.c
+++ b/g10/encrypt.c
@@ -110,7 +110,7 @@ encrypt_seskey (DEK *dek, DEK **seskey, byte *enckey)
/* Shall we use the MDC? Yes - unless rfc-2440 compatibility is
- * requested. */
+ * requested. Must return 1 or 0. */
int
use_mdc (pk_list_t pk_list,int algo)
{
diff --git a/g10/main.h b/g10/main.h
index 250467a95..bd57d2d98 100644
--- a/g10/main.h
+++ b/g10/main.h
@@ -122,6 +122,12 @@ int openpgp_cipher_blocklen (cipher_algo_t algo);
int openpgp_cipher_test_algo(cipher_algo_t algo);
const char *openpgp_cipher_algo_name (cipher_algo_t algo);
+gpg_error_t openpgp_aead_test_algo (aead_algo_t algo);
+const char *openpgp_aead_algo_name (aead_algo_t algo);
+gpg_error_t openpgp_aead_algo_info (aead_algo_t algo,
+ enum gcry_cipher_modes *r_mode,
+ unsigned int *r_noncelen);
+
pubkey_algo_t map_pk_gcry_to_openpgp (enum gcry_pk_algos algo);
int openpgp_pk_test_algo (pubkey_algo_t algo);
int openpgp_pk_test_algo2 (pubkey_algo_t algo, unsigned int use);
diff --git a/g10/mainproc.c b/g10/mainproc.c
index d278c2dc7..a67efd676 100644
--- a/g10/mainproc.c
+++ b/g10/mainproc.c
@@ -97,6 +97,7 @@ struct mainproc_context
int trustletter; /* Temporary usage in list_node. */
ulong symkeys; /* Number of symmetrically encrypted session keys. */
struct kidlist_item *pkenc_list; /* List of encryption packets. */
+ int seen_pkt_encrypted_aead; /* PKT_ENCRYPTED_AEAD packet seen. */
struct {
unsigned int sig_seen:1; /* Set to true if a signature packet
has been seen. */
@@ -145,6 +146,7 @@ release_list( CTX c )
c->any.data = 0;
c->any.uncompress_failed = 0;
c->last_was_session_key = 0;
+ c->seen_pkt_encrypted_aead = 0;
xfree (c->dek);
c->dek = NULL;
}
@@ -252,47 +254,111 @@ add_signature (CTX c, PACKET *pkt)
return 1;
}
-static int
+
+static gpg_error_t
symkey_decrypt_seskey (DEK *dek, byte *seskey, size_t slen)
{
+ gpg_error_t err;
gcry_cipher_hd_t hd;
+ enum gcry_cipher_modes ciphermode;
+ unsigned int noncelen, keylen;
- if(slen < 17 || slen > 33)
+ if (dek->use_aead)
+ {
+ err = openpgp_aead_algo_info (dek->use_aead, &ciphermode, &noncelen);
+ if (err)
+ return err;
+ }
+ else
+ {
+ ciphermode = GCRY_CIPHER_MODE_CFB;
+ noncelen = 0;
+ }
+
+ /* Check that the session key has a size of 16 to 32 bytes. */
+ if ((dek->use_aead && (slen < (noncelen + 16 + 16)
+ || slen > (noncelen + 32 + 16)))
+ || (!dek->use_aead && (slen < 17 || slen > 33)))
{
log_error ( _("weird size for an encrypted session key (%d)\n"),
(int)slen);
- return GPG_ERR_BAD_KEY;
+ return gpg_error (GPG_ERR_BAD_KEY);
}
- if (openpgp_cipher_open (&hd, dek->algo, GCRY_CIPHER_MODE_CFB, 1))
- BUG ();
- if (gcry_cipher_setkey ( hd, dek->key, dek->keylen ))
- BUG ();
- gcry_cipher_setiv ( hd, NULL, 0 );
- gcry_cipher_decrypt ( hd, seskey, slen, NULL, 0 );
- gcry_cipher_close ( hd );
-
- /* Here we can only test whether the algo given in decrypted
- * session key is a valid OpenPGP algo. With 11 defined
- * symmetric algorithms we will miss 4.3% of wrong passphrases
- * here. The actual checking is done later during bulk
- * decryption; we can't bring this check forward easily. */
- if (openpgp_cipher_test_algo (seskey[0]))
- return gpg_error (GPG_ERR_BAD_KEY);
-
- /* Now we replace the dek components with the real session key to
- decrypt the contents of the sequencing packet. */
+ err = openpgp_cipher_open (&hd, dek->algo, ciphermode, 1);
+ if (!err)
+ err = gcry_cipher_setkey (hd, dek->key, dek->keylen);
+ if (!err)
+ err = gcry_cipher_setiv (hd, noncelen? seskey : NULL, noncelen);
+ if (err)
+ goto leave;
- dek->keylen=slen-1;
- dek->algo=seskey[0];
-
- if(dek->keylen > DIM(dek->key))
- BUG ();
+ if (dek->use_aead)
+ {
+ byte ad[4];
+
+ ad[0] = (0xc0 | PKT_SYMKEY_ENC);
+ ad[1] = 5;
+ ad[2] = dek->algo;
+ ad[3] = dek->use_aead;
+ err = gcry_cipher_authenticate (hd, ad, 4);
+ if (err)
+ goto leave;
+ gcry_cipher_final (hd);
+ keylen = slen - noncelen - 16;
+ err = gcry_cipher_decrypt (hd, seskey+noncelen, keylen, NULL, 0);
+ if (err)
+ goto leave;
+ err = gcry_cipher_checktag (hd, seskey+noncelen+keylen, 16);
+ if (err)
+ goto leave;
+ /* Now we replace the dek components with the real session key to
+ * decrypt the contents of the sequencing packet. */
+ if (keylen > DIM(dek->key))
+ {
+ err = gpg_error (GPG_ERR_TOO_LARGE);
+ goto leave;
+ }
+ dek->keylen = keylen;
+ memcpy (dek->key, seskey + noncelen, dek->keylen);
+ }
+ else
+ {
+ gcry_cipher_decrypt (hd, seskey, slen, NULL, 0);
+
+ /* Here we can only test whether the algo given in decrypted
+ * session key is a valid OpenPGP algo. With 11 defined
+ * symmetric algorithms we will miss 4.3% of wrong passphrases
+ * here. The actual checking is done later during bulk
+ * decryption; we can't bring this check forward easily. We
+ * need to use the GPG_ERR_CHECKSUM so that we won't run into
+ * the gnupg < 2.2 bug compatible case which would terminate the
+ * process on GPG_ERR_CIPHER_ALGO. Note that with AEAD (above)
+ * we will have a reliable test here. */
+ if (openpgp_cipher_test_algo (seskey[0])
+ || openpgp_cipher_get_algo_keylen (seskey[0]) != slen - 1)
+ {
+ err = gpg_error (GPG_ERR_CHECKSUM);
+ goto leave;
+ }
- memcpy(dek->key, seskey + 1, dek->keylen);
+ /* Now we replace the dek components with the real session key to
+ * decrypt the contents of the sequencing packet. */
+ keylen = slen-1;
+ if (keylen > DIM(dek->key))
+ {
+ err = gpg_error (GPG_ERR_TOO_LARGE);
+ goto leave;
+ }
+ dek->algo = seskey[0];
+ dek->keylen = slen-1;
+ memcpy (dek->key, seskey + 1, dek->keylen);
+ }
/*log_hexdump( "thekey", dek->key, dek->keylen );*/
+ leave:
+ gcry_cipher_close (hd);
return 0;
}
@@ -300,6 +366,7 @@ symkey_decrypt_seskey (DEK *dek, byte *seskey, size_t slen)
static void
proc_symkey_enc (CTX c, PACKET *pkt)
{
+ gpg_error_t err;
PKT_symkey_enc *enc;
enc = pkt->pkt.symkey_enc;
@@ -309,15 +376,20 @@ proc_symkey_enc (CTX c, PACKET *pkt)
{
int algo = enc->cipher_algo;
const char *s = openpgp_cipher_algo_name (algo);
+ const char *a = (enc->aead_algo ? openpgp_aead_algo_name (enc->aead_algo)
+ /**/ : "CFB");
if (!openpgp_cipher_test_algo (algo))
{
if (!opt.quiet)
{
+ /* Note: TMPSTR is only used to avoid i18n changes. */
+ char *tmpstr = xstrconcat (s, ".", a, NULL);
if (enc->seskeylen)
- log_info (_("%s encrypted session key\n"), s );
+ log_info (_("%s encrypted session key\n"), tmpstr);
else
- log_info (_("%s encrypted data\n"), s );
+ log_info (_("%s encrypted data\n"), tmpstr);
+ xfree (tmpstr);
}
}
else
@@ -349,6 +421,7 @@ proc_symkey_enc (CTX c, PACKET *pkt)
if (c->dek)
{
c->dek->symmetric = 1;
+ c->dek->use_aead = enc->aead_algo;
/* FIXME: This doesn't work perfectly if a symmetric key
comes before a public key in the message - if the
@@ -359,9 +432,16 @@ proc_symkey_enc (CTX c, PACKET *pkt)
come later. */
if (enc->seskeylen)
{
- if (symkey_decrypt_seskey (c->dek,
- enc->seskey, enc->seskeylen))
+ err = symkey_decrypt_seskey (c->dek,
+ enc->seskey, enc->seskeylen);
+ if (err)
{
+ log_info ("decryption of the symmetrically encrypted"
+ " session key failed: %s\n",
+ gpg_strerror (err));
+ if (gpg_err_code (err) != GPG_ERR_BAD_KEY
+ && gpg_err_code (err) != GPG_ERR_CHECKSUM)
+ log_fatal ("process terminated to be bug compatible\n");
if (c->dek->s2k_cacheid[0])
{
if (opt.debug)
@@ -550,6 +630,9 @@ proc_encrypted (CTX c, PACKET *pkt)
int result = 0;
int early_plaintext = literals_seen;
+ if (pkt->pkttype == PKT_ENCRYPTED_AEAD)
+ c->seen_pkt_encrypted_aead = 1;
+
if (early_plaintext)
{
log_info (_("WARNING: multiple plaintexts seen\n"));
@@ -683,7 +766,8 @@ proc_encrypted (CTX c, PACKET *pkt)
;
else if (!result
&& !opt.ignore_mdc_error
- && !pkt->pkt.encrypted->mdc_method)
+ && !pkt->pkt.encrypted->mdc_method
+ && !pkt->pkt.encrypted->aead_algo)
{
/* The message has been decrypted but does not carry an MDC.
* The option --ignore-mdc-error has also not been used. To
@@ -712,17 +796,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
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"));
@@ -732,6 +824,7 @@ proc_encrypted (CTX c, PACKET *pkt)
else
{
if ((gpg_err_code (result) == GPG_ERR_BAD_KEY
+ || gpg_err_code (result) == GPG_ERR_CHECKSUM
|| gpg_err_code (result) == GPG_ERR_CIPHER_ALGO)
&& *c->dek->s2k_cacheid != '\0')
{
@@ -761,6 +854,21 @@ proc_encrypted (CTX c, PACKET *pkt)
}
+static int
+have_seen_pkt_encrypted_aead( CTX c )
+{
+ CTX cc;
+
+ for (cc = c; cc; cc = cc->anchor)
+ {
+ if (cc->seen_pkt_encrypted_aead)
+ return 1;
+ }
+
+ return 0;
+}
+
+
static void
proc_plaintext( CTX c, PACKET *pkt )
{
@@ -836,7 +944,7 @@ proc_plaintext( CTX c, PACKET *pkt )
}
}
- if (!any && !opt.skip_verify)
+ if (!any && !opt.skip_verify && !have_seen_pkt_encrypted_aead(c))
{
/* This is for the old GPG LITERAL+SIG case. It's not legal
according to 2440, so hopefully it won't come up that often.
@@ -1467,7 +1575,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;
}
@@ -1483,6 +1592,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;
@@ -1510,7 +1620,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;
@@ -1537,7 +1648,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/misc.c b/g10/misc.c
index 291d36f2d..22ed47e7c 100644
--- a/g10/misc.c
+++ b/g10/misc.c
@@ -70,6 +70,10 @@
#include "../common/i18n.h"
#include "../common/zb32.h"
+/* FIXME: Libgcrypt 1.9 will support EAX. Until we name this a
+ * requirement we hardwire the enum used for EAX. */
+#define MY_GCRY_CIPHER_MODE_EAX 14
+
#ifdef ENABLE_SELINUX_HACKS
/* A object and a global variable to keep track of files marked as
@@ -602,6 +606,80 @@ openpgp_cipher_algo_name (cipher_algo_t algo)
}
+/* Return 0 if ALGO is supported. Return an error if not. */
+gpg_error_t
+openpgp_aead_test_algo (aead_algo_t algo)
+{
+ /* FIXME: We currently have no easy way to test whether libgcrypt
+ * implements a mode. The only way we can do this is to open a
+ * cipher context with that mode and close it immediately. That is
+ * a bit costly. So we look at the libgcrypt version and assume
+ * nothing has been patched out. */
+ switch (algo)
+ {
+ case AEAD_ALGO_NONE:
+ break;
+
+ case AEAD_ALGO_EAX:
+#if GCRYPT_VERSION_NUMBER < 0x010900
+ break;
+#else
+ return 0;
+#endif
+
+ case AEAD_ALGO_OCB:
+ return 0;
+ }
+
+ return gpg_error (GPG_ERR_INV_CIPHER_MODE);
+}
+
+
+/* Map the OpenPGP AEAD algorithm with ID ALGO to a string
+ * representation of the algorithm name. For unknown algorithm IDs
+ * this function returns "?". */
+const char *
+openpgp_aead_algo_name (aead_algo_t algo)
+{
+ switch (algo)
+ {
+ case AEAD_ALGO_NONE: break;
+ case AEAD_ALGO_EAX: return "EAX";
+ case AEAD_ALGO_OCB: return "OCB";
+ }
+
+ return "?";
+}
+
+
+/* Return information for the AEAD algorithm ALGO. The corresponding
+ * Libgcrypt ciphermode is stored at R_MODE and the required number of
+ * octets for the nonce at R_NONCELEN. On error and error code is
+ * returned. Note that the taglen is always 128 bits. */
+gpg_error_t
+openpgp_aead_algo_info (aead_algo_t algo, enum gcry_cipher_modes *r_mode,
+ unsigned int *r_noncelen)
+{
+ switch (algo)
+ {
+ case AEAD_ALGO_OCB:
+ *r_mode = GCRY_CIPHER_MODE_OCB;
+ *r_noncelen = 15;
+ break;
+
+ case AEAD_ALGO_EAX:
+ *r_mode = MY_GCRY_CIPHER_MODE_EAX;
+ *r_noncelen = 16;
+ break;
+
+ default:
+ log_error ("unsupported AEAD algo %d\n", algo);
+ return gpg_error (GPG_ERR_INV_CIPHER_MODE);
+ }
+ return 0;
+}
+
+
/* Return 0 if ALGO is a supported OpenPGP public key algorithm. */
int
openpgp_pk_test_algo (pubkey_algo_t algo)
diff --git a/g10/packet.h b/g10/packet.h
index 4c0655ca2..b7ceb6479 100644
--- a/g10/packet.h
+++ b/g10/packet.h
@@ -104,6 +104,8 @@ typedef struct {
be different from the algorithm that is used to encrypt the SED
packet.) */
byte cipher_algo;
+ /* The AEAD algorithm or 0 for CFB encryption. */
+ byte aead_algo;
/* The string-to-key specifier. */
STRING2KEY s2k;
/* The length of SESKEY in bytes or 0 if this packet does not
@@ -111,7 +113,8 @@ typedef struct {
S2K function on the password is the session key. See RFC 4880,
Section 5.3.) */
byte seskeylen;
- /* The session key as encrypted by the S2K specifier. */
+ /* The session key as encrypted by the S2K specifier. For AEAD this
+ * includes the nonce and the authentication tag. */
byte seskey[1];
} PKT_symkey_enc;
@@ -297,6 +300,7 @@ typedef struct
struct
{
unsigned int mdc:1;
+ unsigned int aead:1;
unsigned int ks_modify:1;
unsigned int compacted:1;
unsigned int primary:2; /* 2 if set via the primary flag, 1 if calculated */
@@ -393,6 +397,7 @@ typedef struct
struct
{
unsigned int mdc:1; /* MDC feature set. */
+ unsigned int aead:1; /* AEAD feature set. */
unsigned int disabled_valid:1;/* The next flag is valid. */
unsigned int disabled:1; /* The key has been disabled. */
unsigned int primary:1; /* This is a primary key. */
@@ -463,12 +468,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. */
@@ -480,6 +486,15 @@ typedef struct {
/* If 0, MDC is disabled. Otherwise, the MDC method that was used
(currently, only DIGEST_ALGO_SHA1 is supported). */
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 6646becd4..9cb254e24 100644
--- a/g10/parse-packet.c
+++ b/g10/parse-packet.c
@@ -81,6 +81,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,
@@ -665,6 +668,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. */
@@ -852,6 +856,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;
@@ -1127,19 +1134,17 @@ parse_symkeyenc (IOBUF inp, int pkttype, unsigned long pktlen,
{
PKT_symkey_enc *k;
int rc = 0;
- int i, version, s2kmode, cipher_algo, hash_algo, seskeylen, minlen;
+ int i, version, s2kmode, cipher_algo, aead_algo, hash_algo, seskeylen, minlen;
if (pktlen < 4)
- {
- log_error ("packet(%d) too short\n", pkttype);
- if (list_mode)
- es_fprintf (listfp, ":symkey enc packet: [too short]\n");
- rc = gpg_error (GPG_ERR_INV_PACKET);
- goto leave;
- }
+ goto too_short;
version = iobuf_get_noeof (inp);
pktlen--;
- if (version != 4)
+ if (version == 4)
+ ;
+ else if (version == 5)
+ ;
+ else
{
log_error ("packet(%d) with unknown version %d\n", pkttype, version);
if (list_mode)
@@ -1157,6 +1162,15 @@ parse_symkeyenc (IOBUF inp, int pkttype, unsigned long pktlen,
}
cipher_algo = iobuf_get_noeof (inp);
pktlen--;
+ if (version == 5)
+ {
+ aead_algo = iobuf_get_noeof (inp);
+ pktlen--;
+ }
+ else
+ aead_algo = 0;
+ if (pktlen < 2)
+ goto too_short;
s2kmode = iobuf_get_noeof (inp);
pktlen--;
hash_algo = iobuf_get_noeof (inp);
@@ -1191,6 +1205,7 @@ parse_symkeyenc (IOBUF inp, int pkttype, unsigned long pktlen,
+ seskeylen - 1);
k->version = version;
k->cipher_algo = cipher_algo;
+ k->aead_algo = aead_algo;
k->s2k.mode = s2kmode;
k->s2k.hash_algo = hash_algo;
if (s2kmode == 1 || s2kmode == 3)
@@ -1221,10 +1236,20 @@ parse_symkeyenc (IOBUF inp, int pkttype, unsigned long pktlen,
if (list_mode)
{
es_fprintf (listfp,
- ":symkey enc packet: version %d, cipher %d, s2k %d, hash %d",
- version, cipher_algo, s2kmode, hash_algo);
+ ":symkey enc packet: version %d, cipher %d, aead %d,"
+ "s2k %d, hash %d",
+ version, cipher_algo, aead_algo, s2kmode, hash_algo);
if (seskeylen)
- es_fprintf (listfp, ", seskey %d bits", (seskeylen - 1) * 8);
+ {
+ /* To compute the size of the session key we need to know
+ * the size of the AEAD nonce which we may not know. Thus
+ * we show only the size of the entire encrypted session
+ * key. */
+ if (aead_algo)
+ es_fprintf (listfp, ", encrypted seskey %d bytes", seskeylen);
+ else
+ es_fprintf (listfp, ", seskey %d bits", (seskeylen - 1) * 8);
+ }
es_fprintf (listfp, "\n");
if (s2kmode == 1 || s2kmode == 3)
{
@@ -1241,6 +1266,13 @@ parse_symkeyenc (IOBUF inp, int pkttype, unsigned long pktlen,
leave:
iobuf_skip_rest (inp, pktlen, 0);
return rc;
+
+ too_short:
+ log_error ("packet(%d) too short\n", pkttype);
+ if (list_mode)
+ es_fprintf (listfp, ":symkey enc packet: [too short]\n");
+ rc = gpg_error (GPG_ERR_INV_PACKET);
+ goto leave;
}
@@ -1421,6 +1453,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)
@@ -1601,6 +1638,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:
@@ -3253,6 +3291,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. */
@@ -3344,6 +3385,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