From ad989373f1a46139ed0fbc4d4a91069b78617ad9 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 27 Feb 2018 19:50:54 +0100 Subject: [PATCH] gpg: Factor common code out of the AEAD decryption function. * g10/decrypt-data.c (aead_underflow): Factor reading and checking code code out to ... (fill_buffer, aead_checktag): new functions. -- Here is a simple test script to check against a set of encrypted files with naming convention like "symenc-aead-eax-c6-56.asc" # ------------------------ >8 ------------------------ set -e GPG=../g10/gpg for file in "$@"; do echo "${file##*/}" | ( IFS=- read dummy1 dummy2 mode cbyte len rest len="${len%.*}" cbyte="${cbyte#c}" [ "$dummy1" != "symenc" -o "$dummy2" != "aead" ] && continue echo "checking mode=$mode chunkbyte=$cbyte length=$len" if ! $GPG --no-options --rfc4880bis --batch --passphrase "abc" \ -d < $file >tmp.plain 2>/dev/null; then echo "Decryption failed for $file" >&2 exit 2 fi plainlen=$(wc -c &2 exit 2 fi ) done echo "all files are okay" >&2 # ------------------------ 8< ------------------------ Signed-off-by: Werner Koch --- g10/decrypt-data.c | 173 ++++++++++++++++++++------------------------- 1 file changed, 77 insertions(+), 96 deletions(-) diff --git a/g10/decrypt-data.c b/g10/decrypt-data.c index 0b0051af7..b00f2bce1 100644 --- a/g10/decrypt-data.c +++ b/g10/decrypt-data.c @@ -192,6 +192,28 @@ aead_set_ad (decode_filter_ctx_t dfx, int final) } +/* Helper to check the 16 byte tag in TAGBUF. The FINAL flag is only + * for debug messages. */ +static gpg_error_t +aead_checktag (decode_filter_ctx_t dfx, int final, const void *tagbuf) +{ + gpg_error_t err; + + if (DBG_FILTER) + log_printhex (tagbuf, 16, "tag:"); + err = gcry_cipher_checktag (dfx->cipher_hd, tagbuf, 16); + if (err) + { + log_error ("gcry_cipher_checktag%s failed: %s\n", + final? " (final)":"", gpg_strerror (err)); + return err; + } + if (DBG_FILTER) + log_debug ("%stag is valid\n", final?"final ":""); + return 0; +} + + /**************** * Decrypt the data, specified by ED with the key DEK. */ @@ -531,6 +553,49 @@ decrypt_data (ctrl_t ctrl, void *procctx, PKT_encrypted *ed, DEK *dek) } +/* Fill BUFFER with up to NBYTES-OFFSET from STREAM utilizing + * information from the context DFX. Returns the new offset which is + * the number of bytes read plus the original offset. On EOF the + * respective flag in DFX is set. */ +static size_t +fill_buffer (decode_filter_ctx_t dfx, iobuf_t stream, + byte *buffer, size_t nbytes, size_t offset) +{ + size_t nread = offset; + int c; + + if (dfx->partial) + { + for (; nread < nbytes; nread++ ) + { + if ((c = iobuf_get (stream)) == -1) + { + dfx->eof_seen = 1; /* Normal EOF. */ + break; + } + buffer[nread] = c; + } + } + else + { + for (; nread < nbytes && dfx->length; nread++, dfx->length--) + { + c = iobuf_get (stream); + if (c == -1) + { + dfx->eof_seen = 3; /* Premature EOF. */ + break; + } + buffer[nread] = c; + } + if (!dfx->length) + dfx->eof_seen = 1; /* Normal EOF. */ + } + + return nread; +} + + /* The core of the AEAD decryption. This is the underflow function of * the aead_decode_filter. */ static gpg_error_t @@ -542,7 +607,6 @@ aead_underflow (decode_filter_ctx_t dfx, iobuf_t a, byte *buf, size_t *ret_len) 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. */ @@ -560,39 +624,12 @@ aead_underflow (decode_filter_ctx_t dfx, iobuf_t a, byte *buf, size_t *ret_len) * chunksize. After the last data byte from the last chunk 32 more * bytes are expected for the last chunk's tag and the following * final chunk's tag. To detect the EOF we need to try reading at least - * one further byte; however we try to ready 16 extra bytes to avoid + * one further byte; however we try to read 16 extra bytes to avoid * single byte reads in some lower layers. The outcome is that we * have up to 48 extra extra octets which we will later put into the * holdback buffer for the next invocation (which handles the EOF * case). */ - if (dfx->partial) - { - for (; len < size; len++ ) - { - if ((c = iobuf_get (a)) == -1) - { - dfx->eof_seen = 1; /* Normal EOF. */ - break; - } - buf[len] = c; - } - } - else - { - for (; len < size && dfx->length; len++, dfx->length--) - { - c = iobuf_get (a); - if (c == -1) - { - dfx->eof_seen = 3; /* Premature EOF. */ - break; - } - buf[len] = c; - } - if (!dfx->length) - dfx->eof_seen = 1; /* Normal EOF. */ - } - + len = fill_buffer (dfx, a, buf, size, len); if (len < 32) { /* Not enough data for the last two tags. */ @@ -673,47 +710,19 @@ aead_underflow (decode_filter_ctx_t dfx, iobuf_t a, byte *buf, size_t *ret_len) err = gpg_error (GPG_ERR_TRUNCATED); goto leave; } - len = 0; last_chunk_done = 1; } else { - len = dfx->holdbacklen; - if (dfx->partial) - { - for (; len < 48; len++ ) - { - if ((c = iobuf_get (a)) == -1) - { - dfx->eof_seen = 1; /* Normal EOF. */ - break; - } - dfx->holdback[len] = c; - } - } - else - { - for (; len < 48 && dfx->length; len++, dfx->length--) - { - c = iobuf_get (a); - if (c == -1) - { - dfx->eof_seen = 3; /* Premature EOF. */ - break; - } - dfx->holdback[len] = c; - } - if (!dfx->length) - dfx->eof_seen = 1; /* Normal EOF. */ - } - if (len < 32) + len = 0; + dfx->holdbacklen = fill_buffer (dfx, a, dfx->holdback, 48, + dfx->holdbacklen); + if (dfx->holdbacklen < 32) { /* Not enough data for the last two tags. */ err = gpg_error (GPG_ERR_TRUNCATED); goto leave; } - dfx->holdbacklen = len; - len = 0; } } else /* We already have the full tag. */ @@ -723,18 +732,9 @@ aead_underflow (decode_filter_ctx_t dfx, iobuf_t a, byte *buf, size_t *ret_len) memmove (buf + off, buf + off + 16, len - 16); len -= 16; } - if (DBG_CRYPTO) - log_printhex (tagbuf, 16, "tag:"); - err = gcry_cipher_checktag (dfx->cipher_hd, tagbuf, 16); + err = aead_checktag (dfx, 0, tagbuf); if (err) - { - if (DBG_FILTER) - log_debug ("gcry_cipher_checktag failed (1): %s\n", - gpg_strerror (err)); - goto leave; - } - if (DBG_FILTER) - log_debug ("tag is valid\n"); + goto leave; /* Prepare a new chunk. */ if (!last_chunk_done) @@ -783,18 +783,9 @@ aead_underflow (decode_filter_ctx_t dfx, iobuf_t a, byte *buf, size_t *ret_len) if (!last_chunk_done) { log_assert (dfx->holdbacklen >= 32); - - if (DBG_FILTER) - log_printhex (dfx->holdback, 16, "tag:"); - err = gcry_cipher_checktag (dfx->cipher_hd, dfx->holdback, 16); + err = aead_checktag (dfx, 0, dfx->holdback); if (err) - { - log_error ("gcry_cipher_checktag failed (2): %s\n", - gpg_strerror (err)); - goto leave; - } - if (DBG_FILTER) - log_debug ("tag is valid\n"); + goto leave; } /* Check the final chunk. */ @@ -815,19 +806,9 @@ aead_underflow (decode_filter_ctx_t dfx, iobuf_t a, byte *buf, size_t *ret_len) gpg_strerror (err)); goto leave; } - 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); + err = aead_checktag (dfx, 1, dfx->holdback+(last_chunk_done?0:16)); if (err) - { - if (DBG_FILTER) - log_debug ("gcry_cipher_checktag failed (final): %s\n", - gpg_strerror (err)); - goto leave; - } - if (DBG_FILTER) - log_debug ("final tag is valid\n"); + goto leave; err = gpg_error (GPG_ERR_EOF); }