gpg: First take on PKT_ENCRYPTED_AEAD.

* common/openpgpdefs.h (PKT_ENCRYPTED_AEAD): New const.
* g10/dek.h (DEK): Increase size of use_aead to 4 bits.
* g10/filter.h (cipher_filter_context_t):  Add new fields for AEAD.
* g10/packet.h (PKT_encrypted): Add fields aead_algo, cipher_algo, and
chunkbyte.
* g10/build-packet.c (do_encrypted_aead): New.
(build_packet): Call it.
* g10/parse-packet.c (dump_sig_subpkt): Handle SIGSUBPKT_PREF_AEAD.
(parse_one_sig_subpkt, can_handle_critical): Ditto.
(parse_encrypted): Clear new PKT_ENCRYPTED fields.
(parse_encrypted_aead): New.
(parse): Call it.
* g10/gpg.c (main): Take care of --rfc4880bis option when checking
compliance.
* g10/cipher-aead.c: Replace the stub by real code.
* g10/decrypt-data.c (decode_filter_ctx_t): Add fields for use with
AEAD.
(aead_set_nonce): New.
(aead_set_ad): New.
(decrypt_data): Support AEAD.
(aead_underflow): New.
(aead_decode_filter): New.
* g10/encrypt.c (use_aead): Make that new fucntion work.
(encrypt_simple): Use default_aead_algo() instead of EAX.
* g10/mainproc.c (proc_encrypted): Support AEAD.
(do_proc_packets): Support PKT_ENCRYPTED_AEAD.
--

This code has seen only a very few manual tests.  Encrypting always
uses a 64k chunks and decryption has not been tested with larger
chunks.  Those small chunks make debugging much faster.

Tests can be done using:

  gpg --rfc4880bis --pinentry-mode=loopback --passphrase abc \
      --force-aead --aead-algo ocb --s2k-mode 0 --cipher AES \
      -v -z 0 --status-fd 2 -c <INFILE >OUTFILE

and

  gpg --rfc4880bis --pinentry-mode=loopback --passphrase=abc \
      --status-fd 2 -v -d <INFILE >OUTFILE

Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
Werner Koch 2018-01-21 16:24:43 +01:00
parent 81d71818d0
commit 3f4ca85cb0
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
12 changed files with 1235 additions and 138 deletions

View 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,

View 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.

View 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.

View File

@ -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)
{

View 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.
*
@ -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
{
gcry_cipher_hd_t cipher_hd;
gcry_md_hd_t mdc_hash;
char defer[22];
int defer_filled;
int eof_seen;
/* 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;
int partial; /* Working on a partial length packet. */
size_t length; /* If !partial: Remaining bytes in the packet. */
} *decode_filter_ctx_t;
/* The cipher handle. */
gcry_cipher_hd_t cipher_hd;
/* 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. 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");
}
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();
}
/* 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 (blocksize != 16)
{
if ( (c=iobuf_get(ed->buf)) == -1 )
break;
else
temp[i] = c;
rc = gpg_error (GPG_ERR_CIPHER_ALGO);
goto leave;
}
}
else
{
for (i=0; i < (nprefix+2); i++ )
if ( (c=iobuf_get(ed->buf)) == -1 )
switch (ed->aead_algo)
{
case AEAD_ALGO_OCB:
startivlen = 15;
ciphermode = GCRY_CIPHER_MODE_OCB;
break;
else
temp[i] = c;
}
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);
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]) )
if (ed->chunkbyte != 10)
{
/* FIXME */
log_error ("unsupported chunkbyte %u\n", ed->chunkbyte);
rc = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
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. */
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)
{
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 /* CFB encryption. */
{
rc = gpg_error (GPG_ERR_BAD_KEY);
goto leave;
}
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 ( dfx->mdc_hash )
gcry_md_write (dfx->mdc_hash, temp, nprefix+2);
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();
}
/* 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. */

View File

@ -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;

View 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);
}

View 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;

View File

@ -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;

View 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;

View 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;

View 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