1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-05-30 22:08:02 +02:00

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_ATTRIBUTE = 17, /* PGP's attribute packet. */
PKT_ENCRYPTED_MDC = 18, /* Integrity protected encrypted data. */ PKT_ENCRYPTED_MDC = 18, /* Integrity protected encrypted data. */
PKT_MDC = 19, /* Manipulation detection code packet. */ PKT_MDC = 19, /* Manipulation detection code packet. */
PKT_ENCRYPTED_AEAD= 20, /* AEAD encrypted data packet. */
PKT_COMMENT = 61, /* new comment packet (GnuPG specific). */ PKT_COMMENT = 61, /* new comment packet (GnuPG specific). */
PKT_GPG_CONTROL = 63 /* internal control packet (GnuPG specific). */ PKT_GPG_CONTROL = 63 /* internal control packet (GnuPG specific). */
} }
@ -143,6 +144,7 @@ typedef enum
cipher_algo_t; cipher_algo_t;
/* Note that we encode the AEAD algo in a 3 bit field at some places. */
typedef enum typedef enum
{ {
AEAD_ALGO_NONE = 0, 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 actual key used for descryption. <fpr2> is the fingerprint of the
primary key. <otrust> is the letter with the ownertrust; this is primary key. <otrust> is the letter with the ownertrust; this is
in general a 'u' which stands for ultimately trusted. 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 Print information about the symmetric encryption algorithm and the
MDC method. This will be emitted even if the decryption fails. MDC method. This will be emitted even if the decryption fails.
For an AEAD algorithm AEAD_ALGO is not 0.
*** DECRYPTION_FAILED *** DECRYPTION_FAILED
The symmetric decryption failed - one reason could be a wrong 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 --override-session-key. It is not an indication that the
decryption will or has succeeded. 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. 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 *** END_ENCRYPTION
Mark the end of the actual encryption process. 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_plaintext( IOBUF out, int ctb, PKT_plaintext *pt );
static int do_encrypted( IOBUF out, int ctb, PKT_encrypted *ed ); 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_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_compressed( IOBUF out, int ctb, PKT_compressed *cd );
static int do_signature( IOBUF out, int ctb, PKT_signature *sig ); static int do_signature( IOBUF out, int ctb, PKT_signature *sig );
static int do_onepass_sig( IOBUF out, int ctb, PKT_onepass_sig *ops ); static int do_onepass_sig( IOBUF out, int ctb, PKT_onepass_sig *ops );
@ -106,6 +107,7 @@ build_packet (IOBUF out, PACKET *pkt)
break; break;
case PKT_ENCRYPTED: case PKT_ENCRYPTED:
case PKT_ENCRYPTED_MDC: case PKT_ENCRYPTED_MDC:
case PKT_ENCRYPTED_AEAD:
new_ctb = pkt->pkt.encrypted->new_ctb; new_ctb = pkt->pkt.encrypted->new_ctb;
break; break;
case PKT_COMPRESSED: case PKT_COMPRESSED:
@ -158,6 +160,9 @@ build_packet (IOBUF out, PACKET *pkt)
case PKT_ENCRYPTED_MDC: case PKT_ENCRYPTED_MDC:
rc = do_encrypted_mdc (out, ctb, pkt->pkt.encrypted); rc = do_encrypted_mdc (out, ctb, pkt->pkt.encrypted);
break; break;
case PKT_ENCRYPTED_AEAD:
rc = do_encrypted_aead (out, ctb, pkt->pkt.encrypted);
break;
case PKT_COMPRESSED: case PKT_COMPRESSED:
rc = do_compressed (out, ctb, pkt->pkt.compressed); rc = do_compressed (out, ctb, pkt->pkt.compressed);
break; 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 /* Serialize the compressed packet (RFC 4880, Section 5.6) described
by CD and write it to OUT. by CD and write it to OUT.

View File

@ -33,13 +33,392 @@
#include "options.h" #include "options.h"
#include "main.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 int
cipher_filter_aead (void *opaque, int control, 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; cipher_filter_context_t *cfx = opaque;
size_t size = *ret_len; size_t size = *ret_len;
@ -47,16 +426,18 @@ cipher_filter_aead (void *opaque, int control,
if (control == IOBUFCTRL_UNDERFLOW) /* decrypt */ if (control == IOBUFCTRL_UNDERFLOW) /* decrypt */
{ {
rc = -1; /* not yet used */ rc = -1; /* not used */
} }
else if (control == IOBUFCTRL_FLUSH) /* encrypt */ else if (control == IOBUFCTRL_FLUSH) /* encrypt */
{ {
log_assert (a); if (!cfx->wrote_header && (rc=write_header (cfx, a)))
rc = GPG_ERR_NOT_IMPLEMENTED; ;
else
rc = do_flush (cfx, a, buf, size);
} }
else if (control == IOBUFCTRL_FREE) else if (control == IOBUFCTRL_FREE)
{ {
gcry_cipher_close (cfx->cipher_hd); rc = do_free (cfx, a);
} }
else if (control == IOBUFCTRL_DESC) else if (control == IOBUFCTRL_DESC)
{ {

View File

@ -1,6 +1,6 @@
/* decrypt-data.c - Decrypt an encrypted data packet /* decrypt-data.c - Decrypt an encrypted data packet
* Copyright (C) 1998, 1999, 2000, 2001, 2005, * Copyright (C) 1998-2001, 2005-2006, 2009 Free Software Foundation, Inc.
* 2006, 2009 Free Software Foundation, Inc. * Copyright (C) 1998-2001, 2005-2006, 2009, 2018 Werner Koch
* *
* This file is part of GnuPG. * This file is part of GnuPG.
* *
@ -32,22 +32,71 @@
#include "../common/compliance.h" #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, static int mdc_decode_filter ( void *opaque, int control, IOBUF a,
byte *buf, size_t *ret_len); byte *buf, size_t *ret_len);
static int decode_filter ( void *opaque, int control, IOBUF a, static int decode_filter ( void *opaque, int control, IOBUF a,
byte *buf, size_t *ret_len); 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; /* Recounter (max value is 2). We need it becuase we do not know
gcry_md_hd_t mdc_hash; * whether the iobuf or the outer control code frees this object
char defer[22]; * first. */
int defer_filled;
int eof_seen;
int refcount; int refcount;
int partial; /* Working on a partial length packet. */
size_t length; /* If !partial: Remaining bytes in the packet. */ /* The cipher handle. */
} *decode_filter_ctx_t; 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. */ /* 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. * 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; byte *p;
int rc=0, c, i; int rc=0, c, i;
byte temp[32]; byte temp[32];
unsigned blocksize; unsigned int blocksize;
unsigned nprefix; unsigned int nprefix;
dfx = xtrycalloc (1, sizeof *dfx); dfx = xtrycalloc (1, sizeof *dfx);
if (!dfx) if (!dfx)
@ -109,19 +230,18 @@ decrypt_data (ctrl_t ctrl, void *procctx, PKT_encrypted *ed, DEK *dek)
goto leave; goto leave;
} }
{ write_status_printf (STATUS_DECRYPTION_INFO, "%d %d %d",
char buf[20]; ed->mdc_method, dek->algo, ed->aead_algo);
snprintf (buf, sizeof buf, "%d %d", ed->mdc_method, dek->algo);
write_status_text (STATUS_DECRYPTION_INFO, buf);
}
if (opt.show_session_key) if (opt.show_session_key)
{ {
char numbuf[25]; char numbuf[30];
char *hexbuf; 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); hexbuf = bin2hex (dek->key, dek->keylen, NULL);
if (!hexbuf) 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); blocksize = openpgp_cipher_get_algo_blklen (dek->algo);
if ( !blocksize || blocksize > 16 ) if ( !blocksize || blocksize > 16 )
log_fatal ("unsupported blocksize %u\n", blocksize ); log_fatal ("unsupported blocksize %u\n", blocksize );
nprefix = blocksize; if (ed->aead_algo)
if ( ed->len && ed->len < (nprefix+2) )
{ {
/* An invalid message. We can't check that during parsing enum gcry_cipher_modes ciphermode;
because we may not know the used cipher then. */ unsigned int startivlen;
rc = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
if ( ed->mdc_method ) if (blocksize != 16)
{
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 ) rc = gpg_error (GPG_ERR_CIPHER_ALGO);
break; goto leave;
else
temp[i] = c;
} }
}
else switch (ed->aead_algo)
{ {
for (i=0; i < (nprefix+2); i++ ) case AEAD_ALGO_OCB:
if ( (c=iobuf_get(ed->buf)) == -1 ) startivlen = 15;
ciphermode = GCRY_CIPHER_MODE_OCB;
break; break;
else case AEAD_ALGO_EAX:
temp[i] = c; 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); if (ed->chunkbyte != 10)
gcry_cipher_sync (dfx->cipher_hd); {
p = temp; /* FIXME */
/* log_hexdump( "prefix", temp, nprefix+2 ); */ log_error ("unsupported chunkbyte %u\n", ed->chunkbyte);
if (dek->symmetric rc = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
&& (p[nprefix-2] != p[nprefix] || p[nprefix-1] != p[nprefix+1]) ) 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); nprefix = blocksize;
goto leave; 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 ) if ( ed->mdc_method )
gcry_md_write (dfx->mdc_hash, temp, nprefix+2); {
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->refcount++;
dfx->partial = ed->is_partial; dfx->partial = !!ed->is_partial;
dfx->length = ed->len; 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 ); iobuf_push_filter ( ed->buf, mdc_decode_filter, dfx );
else else
iobuf_push_filter ( ed->buf, decode_filter, dfx ); 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 static int
mdc_decode_filter (void *opaque, int control, IOBUF a, mdc_decode_filter (void *opaque, int control, IOBUF a,
@ -365,6 +889,7 @@ mdc_decode_filter (void *opaque, int control, IOBUF a,
} }
else else
{ {
memcpy (buf, dfx->defer, 22); memcpy (buf, dfx->defer, 22);
} }
/* Fill up the buffer. */ /* Fill up the buffer. */

View File

@ -32,8 +32,8 @@ typedef struct
* verbose mode. */ * verbose mode. */
int algo_info_printed : 1; int algo_info_printed : 1;
/* AEAD shall be used. */ /* AEAD shall be used. The value is the AEAD algo. */
int use_aead : 1; int use_aead : 4;
/* MDC shall be used. */ /* MDC shall be used. */
int use_mdc : 1; int use_mdc : 1;

View File

@ -123,7 +123,7 @@ use_aead (pk_list_t pk_list, int algo)
return 0; 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. */ /* With --force-mdc we clearly do not want AEAD. */
if (opt.force_mdc) if (opt.force_mdc)
@ -133,12 +133,15 @@ use_aead (pk_list_t pk_list, int algo)
if (opt.force_aead) if (opt.force_aead)
{ {
if (!can_use) 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; return 1;
} }
/* AEAD does noly work with 128 bit cipher blocklength. */ /* AEAD does only work with 128 bit cipher blocklength. */
if (!can_use) if (!can_use)
return 0; return 0;
@ -307,7 +310,7 @@ encrypt_simple (const char *filename, int mode, int use_seskey)
openpgp_cipher_algo_name (cfx.dek->algo)); openpgp_cipher_algo_name (cfx.dek->algo));
if (use_aead (NULL, cfx.dek->algo)) if (use_aead (NULL, cfx.dek->algo))
cfx.dek->use_aead = 1; cfx.dek->use_aead = default_aead_algo ();
else else
cfx.dek->use_mdc = !!use_mdc (NULL, cfx.dek->algo); 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 compress_filter_context_s compress_filter_context_t;
typedef struct { typedef struct
DEK *dek; {
u32 datalen; /* Object with the key and algo */
gcry_cipher_hd_t cipher_hd; DEK *dek;
unsigned int wrote_header : 1;
unsigned int short_blklen_warn : 1; /* Length of the data to encrypt if known - 32 bit because OpenPGP
unsigned long short_blklen_count; * requires partial encoding for a larger data size. */
gcry_md_hd_t mdc_hash; u32 datalen;
byte enchash[20];
/* 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; } cipher_filter_context_t;

View File

@ -3882,7 +3882,7 @@ main (int argc, char **argv)
/* Check our chosen algorithms against the list of legal /* Check our chosen algorithms against the list of legal
algorithms. */ algorithms. */
if(!GNUPG) if(!GNUPG && !opt.flags.rfc4880bis)
{ {
const char *badalg=NULL; const char *badalg=NULL;
preftype_t badtype=PREFTYPE_NONE; preftype_t badtype=PREFTYPE_NONE;

View File

@ -648,6 +648,7 @@ proc_encrypted (CTX c, PACKET *pkt)
else if (!result else if (!result
&& !opt.ignore_mdc_error && !opt.ignore_mdc_error
&& !pkt->pkt.encrypted->mdc_method && !pkt->pkt.encrypted->mdc_method
&& !pkt->pkt.encrypted->aead_algo
&& openpgp_cipher_get_algo_blklen (c->dek->algo) != 8 && openpgp_cipher_get_algo_blklen (c->dek->algo) != 8
&& c->dek->algo != CIPHER_ALGO_TWOFISH) && c->dek->algo != CIPHER_ALGO_TWOFISH)
{ {
@ -662,17 +663,25 @@ proc_encrypted (CTX c, PACKET *pkt)
write_status (STATUS_DECRYPTION_FAILED); write_status (STATUS_DECRYPTION_FAILED);
} }
else if (!result || (gpg_err_code (result) == GPG_ERR_BAD_SIGNATURE else if (!result || (gpg_err_code (result) == GPG_ERR_BAD_SIGNATURE
&& !pkt->pkt.encrypted->aead_algo
&& opt.ignore_mdc_error)) && 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); write_status (STATUS_DECRYPTION_OKAY);
if (opt.verbose > 1) if (opt.verbose > 1)
log_info(_("decryption okay\n")); 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); write_status (STATUS_GOODMDC);
else if (!opt.no_mdc_warn) else if (!opt.no_mdc_warn)
log_info (_("WARNING: message was not integrity protected\n")); 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; glo_ctrl.lasterr = result;
log_error (_("WARNING: encrypted message has been manipulated!\n")); 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_PUBKEY_ENC: proc_pubkey_enc (ctrl, c, pkt); break;
case PKT_SYMKEY_ENC: proc_symkey_enc (c, pkt); break; case PKT_SYMKEY_ENC: proc_symkey_enc (c, pkt); break;
case PKT_ENCRYPTED: 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; case PKT_COMPRESSED: rc = proc_compressed (c, pkt); break;
default: newpkt = 0; 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_PUBKEY_ENC:
case PKT_ENCRYPTED: case PKT_ENCRYPTED:
case PKT_ENCRYPTED_MDC: case PKT_ENCRYPTED_MDC:
case PKT_ENCRYPTED_AEAD:
write_status_text( STATUS_UNEXPECTED, "0" ); write_status_text( STATUS_UNEXPECTED, "0" );
rc = GPG_ERR_UNEXPECTED; rc = GPG_ERR_UNEXPECTED;
goto leave; 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_SYMKEY_ENC: proc_symkey_enc (c, pkt); break;
case PKT_PUBKEY_ENC: proc_pubkey_enc (ctrl, c, pkt); break; case PKT_PUBKEY_ENC: proc_pubkey_enc (ctrl, c, pkt); break;
case PKT_ENCRYPTED: 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_PLAINTEXT: proc_plaintext (c, pkt); break;
case PKT_COMPRESSED: rc = proc_compressed (c, pkt); break; case PKT_COMPRESSED: rc = proc_compressed (c, pkt); break;
case PKT_ONEPASS_SIG: newpkt = add_onepass_sig (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_PUBKEY_ENC: proc_pubkey_enc (ctrl, c, pkt); break;
case PKT_SYMKEY_ENC: proc_symkey_enc (c, pkt); break; case PKT_SYMKEY_ENC: proc_symkey_enc (c, pkt); break;
case PKT_ENCRYPTED: 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_PLAINTEXT: proc_plaintext (c, pkt); break;
case PKT_COMPRESSED: rc = proc_compressed (c, pkt); break; case PKT_COMPRESSED: rc = proc_compressed (c, pkt); break;
case PKT_ONEPASS_SIG: newpkt = add_onepass_sig (c, pkt); break; case PKT_ONEPASS_SIG: newpkt = add_onepass_sig (c, pkt); break;

View File

@ -459,12 +459,13 @@ typedef struct {
typedef struct { typedef struct {
/* Remaining length of encrypted data. */ /* Remaining length of encrypted data. */
u32 len; u32 len;
/* When encrypting, the first block size bytes of data are random /* When encrypting in CFB mode, the first block size bytes of data
data and the following 2 bytes are copies of the last two bytes * are random data and the following 2 bytes are copies of the last
of the random data (RFC 4880, Section 5.7). This provides a * two bytes of the random data (RFC 4880, Section 5.7). This
simple check that the key is correct. extralen is the size of * provides a simple check that the key is correct. EXTRALEN is the
this extra data. This is used by build_packet when writing out * size of this extra data or, in AEAD mode, the length of the
the packet's header. */ * headers and the tags. This is used by build_packet when writing
* out the packet's header. */
int extralen; int extralen;
/* Whether the serialized version of the packet used / should use /* Whether the serialized version of the packet used / should use
the new format. */ the new format. */
@ -476,6 +477,15 @@ typedef struct {
/* If 0, MDC is disabled. Otherwise, the MDC method that was used /* If 0, MDC is disabled. Otherwise, the MDC method that was used
(only DIGEST_ALGO_SHA1 has ever been defined). */ (only DIGEST_ALGO_SHA1 has ever been defined). */
byte mdc_method; 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 /* An iobuf holding the data to be decrypted. (This is not used for
encryption!) */ encryption!) */
iobuf_t buf; iobuf_t buf;

View File

@ -1,7 +1,6 @@
/* parse-packet.c - read packets /* parse-packet.c - read packets
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, * Copyright (C) 1998-2007, 2009-2010 Free Software Foundation, Inc.
* 2007, 2009, 2010 Free Software Foundation, Inc. * Copyright (C) 2014, 2018 Werner Koch
* Copyright (C) 2014 Werner Koch
* Copyright (C) 2015 g10 Code GmbH * Copyright (C) 2015 g10 Code GmbH
* *
* This file is part of GnuPG. * This file is part of GnuPG.
@ -18,6 +17,7 @@
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program; if not, see <https://www.gnu.org/licenses/>. * along with this program; if not, see <https://www.gnu.org/licenses/>.
* SPDX-License-Identifier: GPL-3.0+
*/ */
#include <config.h> #include <config.h>
@ -82,6 +82,9 @@ static int parse_compressed (IOBUF inp, int pkttype, unsigned long pktlen,
PACKET * packet, int new_ctb); PACKET * packet, int new_ctb);
static int parse_encrypted (IOBUF inp, int pkttype, unsigned long pktlen, static int parse_encrypted (IOBUF inp, int pkttype, unsigned long pktlen,
PACKET * packet, int new_ctb, int partial); 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, static int parse_mdc (IOBUF inp, int pkttype, unsigned long pktlen,
PACKET * packet, int new_ctb); PACKET * packet, int new_ctb);
static int parse_gpg_control (IOBUF inp, int pkttype, unsigned long pktlen, 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_PLAINTEXT:
case PKT_ENCRYPTED: case PKT_ENCRYPTED:
case PKT_ENCRYPTED_MDC: case PKT_ENCRYPTED_MDC:
case PKT_ENCRYPTED_AEAD:
case PKT_COMPRESSED: case PKT_COMPRESSED:
iobuf_set_partial_body_length_mode (inp, c & 0xff); iobuf_set_partial_body_length_mode (inp, c & 0xff);
pktlen = 0; /* To indicate partial length. */ 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: case PKT_MDC:
rc = parse_mdc (inp, pkttype, pktlen, pkt, new_ctb); rc = parse_mdc (inp, pkttype, pktlen, pkt, new_ctb);
break; break;
case PKT_ENCRYPTED_AEAD:
rc = parse_encrypted_aead (inp, pkttype, pktlen, pkt, partial);
break;
case PKT_GPG_CONTROL: case PKT_GPG_CONTROL:
rc = parse_gpg_control (inp, pkttype, pktlen, pkt, partial); rc = parse_gpg_control (inp, pkttype, pktlen, pkt, partial);
break; break;
@ -1392,6 +1399,11 @@ dump_sig_subpkt (int hashed, int type, int critical,
for (i = 0; i < length; i++) for (i = 0; i < length; i++)
es_fprintf (listfp, " %d", buffer[i]); es_fprintf (listfp, " %d", buffer[i]);
break; 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: case SIGSUBPKT_REV_KEY:
es_fputs ("revocation key: ", listfp); es_fputs ("revocation key: ", listfp);
if (length < 22) 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_KEY_FLAGS:
case SIGSUBPKT_KS_FLAGS: case SIGSUBPKT_KS_FLAGS:
case SIGSUBPKT_PREF_SYM: case SIGSUBPKT_PREF_SYM:
case SIGSUBPKT_PREF_AEAD:
case SIGSUBPKT_PREF_HASH: case SIGSUBPKT_PREF_HASH:
case SIGSUBPKT_PREF_COMPR: case SIGSUBPKT_PREF_COMPR:
case SIGSUBPKT_POLICY: 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: /* issuer key ID */
case SIGSUBPKT_ISSUER_FPR: /* issuer fingerprint */ case SIGSUBPKT_ISSUER_FPR: /* issuer fingerprint */
case SIGSUBPKT_PREF_SYM: case SIGSUBPKT_PREF_SYM:
case SIGSUBPKT_PREF_AEAD:
case SIGSUBPKT_PREF_HASH: case SIGSUBPKT_PREF_HASH:
case SIGSUBPKT_PREF_COMPR: case SIGSUBPKT_PREF_COMPR:
case SIGSUBPKT_KEY_FLAGS: case SIGSUBPKT_KEY_FLAGS:
@ -3161,6 +3175,9 @@ parse_encrypted (IOBUF inp, int pkttype, unsigned long pktlen,
ed->buf = NULL; ed->buf = NULL;
ed->new_ctb = new_ctb; ed->new_ctb = new_ctb;
ed->is_partial = partial; 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) if (pkttype == PKT_ENCRYPTED_MDC)
{ {
/* Fixme: add some pktlen sanity checks. */ /* 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 * This packet is internally generated by us (in armor.c) to transfer
* some information to the lower layer. To make sure that this packet * some information to the lower layer. To make sure that this packet