mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-22 10:19:57 +01: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:
parent
81d71818d0
commit
3f4ca85cb0
@ -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,
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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. */
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
55
g10/filter.h
55
g10/filter.h
@ -88,15 +88,52 @@ struct compress_filter_context_s {
|
||||
typedef struct compress_filter_context_s compress_filter_context_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
DEK *dek;
|
||||
u32 datalen;
|
||||
gcry_cipher_hd_t cipher_hd;
|
||||
unsigned int wrote_header : 1;
|
||||
unsigned int short_blklen_warn : 1;
|
||||
unsigned long short_blklen_count;
|
||||
gcry_md_hd_t mdc_hash;
|
||||
byte enchash[20];
|
||||
typedef struct
|
||||
{
|
||||
/* Object with the key and algo */
|
||||
DEK *dek;
|
||||
|
||||
/* Length of the data to encrypt if known - 32 bit because OpenPGP
|
||||
* requires partial encoding for a larger data size. */
|
||||
u32 datalen;
|
||||
|
||||
/* The current cipher handle. */
|
||||
gcry_cipher_hd_t cipher_hd;
|
||||
|
||||
/* Various processing flags. */
|
||||
unsigned int wrote_header : 1;
|
||||
unsigned int short_blklen_warn : 1;
|
||||
unsigned long short_blklen_count;
|
||||
|
||||
/* The encoded chunk byte for AEAD. */
|
||||
byte chunkbyte;
|
||||
|
||||
/* The decoded CHUNKBYTE. */
|
||||
uint64_t chunksize;
|
||||
|
||||
/* The chunk index for AEAD. */
|
||||
uint64_t chunkindex;
|
||||
|
||||
/* The number of bytes in the current chunk. */
|
||||
uint64_t chunklen;
|
||||
|
||||
/* The total count of encrypted plaintext octets. Note that we
|
||||
* don't care about encrypting more than 16 Exabyte. */
|
||||
uint64_t total;
|
||||
|
||||
/* The hash context and a buffer used for MDC. */
|
||||
gcry_md_hd_t mdc_hash;
|
||||
byte enchash[20];
|
||||
|
||||
/* The start IV for AEAD encryption. */
|
||||
byte startiv[16];
|
||||
|
||||
/* Using a large buffer for encryption makes processing easier and
|
||||
* also makes sure the data is well aligned. */
|
||||
char *buffer;
|
||||
size_t bufsize; /* Allocated length. */
|
||||
size_t buflen; /* Used length. */
|
||||
|
||||
} cipher_filter_context_t;
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
22
g10/packet.h
22
g10/packet.h
@ -459,12 +459,13 @@ typedef struct {
|
||||
typedef struct {
|
||||
/* Remaining length of encrypted data. */
|
||||
u32 len;
|
||||
/* When encrypting, the first block size bytes of data are random
|
||||
data and the following 2 bytes are copies of the last two bytes
|
||||
of the random data (RFC 4880, Section 5.7). This provides a
|
||||
simple check that the key is correct. extralen is the size of
|
||||
this extra data. This is used by build_packet when writing out
|
||||
the packet's header. */
|
||||
/* When encrypting in CFB mode, the first block size bytes of data
|
||||
* are random data and the following 2 bytes are copies of the last
|
||||
* two bytes of the random data (RFC 4880, Section 5.7). This
|
||||
* provides a simple check that the key is correct. EXTRALEN is the
|
||||
* size of this extra data or, in AEAD mode, the length of the
|
||||
* headers and the tags. This is used by build_packet when writing
|
||||
* out the packet's header. */
|
||||
int extralen;
|
||||
/* Whether the serialized version of the packet used / should use
|
||||
the new format. */
|
||||
@ -476,6 +477,15 @@ typedef struct {
|
||||
/* If 0, MDC is disabled. Otherwise, the MDC method that was used
|
||||
(only DIGEST_ALGO_SHA1 has ever been defined). */
|
||||
byte mdc_method;
|
||||
/* If 0, AEAD is not used. Otherwise, the used AEAD algorithm.
|
||||
* MDC_METHOD (above) shall be zero if AEAD is used. */
|
||||
byte aead_algo;
|
||||
/* The cipher algo for/from the AEAD packet. 0 for other encryption
|
||||
* packets. */
|
||||
byte cipher_algo;
|
||||
/* The chunk byte from the AEAD packet. */
|
||||
byte chunkbyte;
|
||||
|
||||
/* An iobuf holding the data to be decrypted. (This is not used for
|
||||
encryption!) */
|
||||
iobuf_t buf;
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user