mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-22 10:19:57 +01:00
gpg: Fix corner cases in AEAD encryption.
* g10/cipher-aead.c (write_final_chunk): Do not bump up the chunk index if the previous chunk was empty. * g10/decrypt-data.c (aead_underflow): Likewise. Also handle a other corner cases. Add more debug output. -- GnuPG-bug-id: 3774 This fixes the reported case when the encrypted data is a multiple of the chunk size. Then the chunk index for the final chunk was wrongly incremented by 2. The actual fix makes use of the fact that the current dfx->CHUNKLEN is 0 in this case. There is also some other reorganizing to help with debugging. The thing seems to work now but the code is not very clean - should be reworked. Creating test files can be done with this script: --8<---------------cut here---------------start------------->8--- csize=6 for len in 0 55 56 57; do awk </dev/null -v i=$len 'BEGIN{while(i){i--;printf"~"}}' \ | gpg --no-options -v --rfc4880bis --batch --passphrase "abc" \ --s2k-count 1025 --s2k-digest-algo sha256 -z0 \ --force-aead --aead-algo eax --cipher aes -a \ --chunk-size $csize -c >symenc-aead-eax-c$csize-$len.asc done --8<---------------cut here---------------end--------------->8--- A LEN of 56 triggered the bug which can be seen by looking at the "authdata:" line in the --debug=crypt,filter output. Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
parent
cbc7bacf2f
commit
ebb0fcf6e0
@ -244,6 +244,7 @@ write_final_chunk (cipher_filter_context_t *cfx, iobuf_t a)
|
||||
gpg_error_t err;
|
||||
char dummy[1];
|
||||
|
||||
if (cfx->chunklen)
|
||||
cfx->chunkindex++;
|
||||
|
||||
err = set_nonce (cfx);
|
||||
|
@ -541,6 +541,7 @@ aead_underflow (decode_filter_ctx_t dfx, iobuf_t a, byte *buf, size_t *ret_len)
|
||||
size_t totallen = 0; /* The number of bytes to return on success or EOF. */
|
||||
size_t off = 0; /* The offset into the buffer. */
|
||||
size_t len; /* The current number of bytes in BUF+OFF. */
|
||||
int last_chunk_done = 0; /* Flag that we processed the last chunk. */
|
||||
int c;
|
||||
|
||||
log_assert (size > 48); /* Our code requires at least this size. */
|
||||
@ -551,16 +552,16 @@ aead_underflow (decode_filter_ctx_t dfx, iobuf_t a, byte *buf, size_t *ret_len)
|
||||
memcpy (buf, dfx->holdback, len);
|
||||
|
||||
if (DBG_FILTER)
|
||||
log_debug ("aead_underflow: size=%zu len=%zu%s\n",
|
||||
size, len, dfx->eof_seen? " eof":"");
|
||||
log_debug ("aead_underflow: size=%zu len=%zu%s%s\n", size, len,
|
||||
dfx->partial? " partial":"", dfx->eof_seen? " eof":"");
|
||||
|
||||
/* Read and fill up BUF. We need to watch out for an EOF so that we
|
||||
* can detect the last chunk which is commonly shorter than the
|
||||
* chunksize. After the last data byte from the last chunk 32 more
|
||||
* bytes are expected for the last chunk's tag and the following
|
||||
* final chunk's tag. To detect the EOF we need to read at least
|
||||
* final chunk's tag. To detect the EOF we need to try reading at least
|
||||
* one further byte; however we try to ready 16 extra bytes to avoid
|
||||
* singel byte reads in some lower layers. The outcome is that we
|
||||
* single byte reads in some lower layers. The outcome is that we
|
||||
* have up to 48 extra extra octets which we will later put into the
|
||||
* holdback buffer for the next invocation (which handles the EOF
|
||||
* case). */
|
||||
@ -648,19 +649,35 @@ aead_underflow (decode_filter_ctx_t dfx, iobuf_t a, byte *buf, size_t *ret_len)
|
||||
len -= n;
|
||||
|
||||
if (DBG_FILTER)
|
||||
log_debug ("bytes left: %zu at off=%zu\n", len, off);
|
||||
log_debug ("ndecrypted: %zu (nchunk=%zu) bytes left: %zu at off=%zu\n",
|
||||
totallen, dfx->chunklen, len, off);
|
||||
|
||||
/* Check the tag. */
|
||||
if (len < 16)
|
||||
{
|
||||
/* The tag is not entirely in the buffer. Read the rest of
|
||||
* the tag from the holdback buffer. The shift the holdback
|
||||
* the tag from the holdback buffer. Then shift the holdback
|
||||
* buffer and fill it up again. */
|
||||
memcpy (tagbuf, buf+off, len);
|
||||
memcpy (tagbuf + len, dfx->holdback, 16 - len);
|
||||
dfx->holdbacklen -= 16-len;
|
||||
memmove (dfx->holdback, dfx->holdback + (16-len), dfx->holdbacklen);
|
||||
|
||||
if (dfx->eof_seen)
|
||||
{
|
||||
/* We should have the last chunk's tag in TAGBUF and the
|
||||
* final tag in HOLDBACKBUF. */
|
||||
if (len || dfx->holdbacklen != 16)
|
||||
{
|
||||
/* Not enough data for the last two tags. */
|
||||
err = gpg_error (GPG_ERR_TRUNCATED);
|
||||
goto leave;
|
||||
}
|
||||
len = 0;
|
||||
last_chunk_done = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
len = dfx->holdbacklen;
|
||||
if (dfx->partial)
|
||||
{
|
||||
@ -696,9 +713,9 @@ aead_underflow (decode_filter_ctx_t dfx, iobuf_t a, byte *buf, size_t *ret_len)
|
||||
goto leave;
|
||||
}
|
||||
dfx->holdbacklen = len;
|
||||
/* log_printhex (dfx->holdback, dfx->holdbacklen, "holdback:"); */
|
||||
len = 0;
|
||||
}
|
||||
}
|
||||
else /* We already have the full tag. */
|
||||
{
|
||||
memcpy (tagbuf, buf+off, 16);
|
||||
@ -716,8 +733,12 @@ aead_underflow (decode_filter_ctx_t dfx, iobuf_t a, byte *buf, size_t *ret_len)
|
||||
gpg_strerror (err));
|
||||
goto leave;
|
||||
}
|
||||
if (DBG_FILTER)
|
||||
log_debug ("tag is valid\n");
|
||||
|
||||
/* Prepare a new chunk. */
|
||||
if (!last_chunk_done)
|
||||
{
|
||||
dfx->chunklen = 0;
|
||||
dfx->chunkindex++;
|
||||
err = aead_set_nonce (dfx);
|
||||
@ -726,10 +747,13 @@ aead_underflow (decode_filter_ctx_t dfx, iobuf_t a, byte *buf, size_t *ret_len)
|
||||
err = aead_set_ad (dfx, 0);
|
||||
if (err)
|
||||
goto leave;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!last_chunk_done)
|
||||
{
|
||||
if (dfx->eof_seen)
|
||||
{
|
||||
/* This is the last block of the last chunk. Its length may
|
||||
@ -739,17 +763,25 @@ aead_underflow (decode_filter_ctx_t dfx, iobuf_t a, byte *buf, size_t *ret_len)
|
||||
err = gcry_cipher_decrypt (dfx->cipher_hd, buf + off, len, NULL, 0);
|
||||
if (err)
|
||||
{
|
||||
log_error ("gcry_cipher_decrypt failed (2): %s\n", gpg_strerror (err));
|
||||
log_error ("gcry_cipher_decrypt failed (2): %s\n",
|
||||
gpg_strerror (err));
|
||||
goto leave;
|
||||
}
|
||||
totallen += len;
|
||||
dfx->chunklen += len;
|
||||
dfx->total += len;
|
||||
if (DBG_FILTER)
|
||||
log_debug ("ndecrypted: %zu (nchunk=%zu)\n", totallen, dfx->chunklen);
|
||||
}
|
||||
|
||||
if (dfx->eof_seen)
|
||||
{
|
||||
if (DBG_FILTER)
|
||||
log_debug ("eof seen: holdback buffer has the tags.\n");
|
||||
log_debug ("eof seen: holdback buffer has the %s.\n",
|
||||
last_chunk_done? "final tag":"last and final tag");
|
||||
|
||||
if (!last_chunk_done)
|
||||
{
|
||||
log_assert (dfx->holdbacklen >= 32);
|
||||
|
||||
if (DBG_FILTER)
|
||||
@ -761,8 +793,12 @@ aead_underflow (decode_filter_ctx_t dfx, iobuf_t a, byte *buf, size_t *ret_len)
|
||||
gpg_strerror (err));
|
||||
goto leave;
|
||||
}
|
||||
if (DBG_FILTER)
|
||||
log_debug ("tag is valid\n");
|
||||
}
|
||||
|
||||
/* Check the final chunk. */
|
||||
if (dfx->chunklen)
|
||||
dfx->chunkindex++;
|
||||
err = aead_set_nonce (dfx);
|
||||
if (err)
|
||||
@ -771,7 +807,7 @@ aead_underflow (decode_filter_ctx_t dfx, iobuf_t a, byte *buf, size_t *ret_len)
|
||||
if (err)
|
||||
goto leave;
|
||||
gcry_cipher_final (dfx->cipher_hd);
|
||||
/* decrypt an empty string. */
|
||||
/* Decrypt an empty string. */
|
||||
err = gcry_cipher_decrypt (dfx->cipher_hd, dfx->holdback, 0, NULL, 0);
|
||||
if (err)
|
||||
{
|
||||
@ -779,8 +815,10 @@ aead_underflow (decode_filter_ctx_t dfx, iobuf_t a, byte *buf, size_t *ret_len)
|
||||
gpg_strerror (err));
|
||||
goto leave;
|
||||
}
|
||||
/* log_printhex (dfx->holdback+16, 16, "tag:"); */
|
||||
err = gcry_cipher_checktag (dfx->cipher_hd, dfx->holdback+16, 16);
|
||||
if (DBG_CRYPTO)
|
||||
log_printhex (dfx->holdback+(last_chunk_done?0:16), 16, "tag:");
|
||||
err = gcry_cipher_checktag (dfx->cipher_hd,
|
||||
dfx->holdback+(last_chunk_done?0:16), 16);
|
||||
if (err)
|
||||
{
|
||||
if (DBG_FILTER)
|
||||
@ -788,6 +826,8 @@ aead_underflow (decode_filter_ctx_t dfx, iobuf_t a, byte *buf, size_t *ret_len)
|
||||
gpg_strerror (err));
|
||||
goto leave;
|
||||
}
|
||||
if (DBG_FILTER)
|
||||
log_debug ("final tag is valid\n");
|
||||
err = gpg_error (GPG_ERR_EOF);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user