-
Notifications
You must be signed in to change notification settings - Fork 279
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
HMAC errors not reported when reading compressed and encrypted files (AE-2) #478
Comments
Here's the sequence of events I observe in the debugger:
|
My diagnostic is that it's a mistake for
Changing the behavior of The alternative would be to change |
Confirming that either of the two following patches fix the problem, and also pass all regression tests (though I have only tested the OpenSSL crypto implementation and the zlib compression algorithm; also the Fix diff --git a/lib/zip_source_read.c b/lib/zip_source_read.c
index 910d4c3e..13e88f15 100644
--- a/lib/zip_source_read.c
+++ b/lib/zip_source_read.c
@@ -64,12 +64,7 @@ zip_source_read(zip_source_t *src, void *data, zip_uint64_t len) {
while (bytes_read < len) {
if ((n = _zip_source_call(src, (zip_uint8_t *)data + bytes_read, len - bytes_read, ZIP_SOURCE_READ)) < 0) {
src->had_read_error = true;
- if (bytes_read == 0) {
- return -1;
- }
- else {
- return (zip_int64_t)bytes_read;
- }
+ return -1;
}
if (n == 0) { Or fix diff --git a/lib/zip_source_compress.c b/lib/zip_source_compress.c
index 54387eca..7f10d91d 100644
--- a/lib/zip_source_compress.c
+++ b/lib/zip_source_compress.c
@@ -228,7 +228,20 @@ compress_read(zip_source_t *src, struct context *ctx, void *data, zip_uint64_t l
ctx->end_of_stream = true;
if (!ctx->end_of_input) {
- /* TODO: garbage after stream, or compression ended before all data read */
+ if ((n = zip_source_read(src, ctx->buffer, sizeof(ctx->buffer))) < 0) {
+ zip_error_set_from_source(&ctx->error, src);
+ end = true;
+ break;
+ }
+ else if (n != 0) {
+ /* garbage after stream, or compression ended before all data read */
+ zip_error_set(&ctx->error, ZIP_ER_INTERNAL, 0);
+ end = true;
+ break;
+ }
+
+ ctx->end_of_input = true;
+ ctx->algorithm->end_of_input(ctx->ud);
}
if (ctx->first_read < 0) { |
Also, catch garbage after compressed data if checking for consistency. Adresses issue #478
Thank you for the detailed analysis, we've reworked your patch slightly and applied it. Can you please provide us with a short example file with an HMAC error, so we can include a test case? |
Describe the Bug
It appears that libzip is not fully checking for checksums / CRCs when reading data from compressed (DEFLATE) and encrypted (AES-256) files.
I wrote a test that creates a zip buffer in memory, then loops over each byte of that buffer, manually corrupting the byte, and trying to extract back the data. I expect either:
This works as expected for:
But when enabling both compression and encryption, some corrupted bytes go through undetected (no reported error), leaving me with a seemingly good file with content that is actually corrupted.
Expected Behavior
Observed Behavior
To Reproduce
See source code below.
zip_test.c
1.11.1
With OpenSSL 3.3.2 and zlib 1.3.1
Operating System
Ubuntu 22.04
Test Files
None
Additional context
Digging through the source code and debugger as best I could, I first identified the root cause as this line:
libzip/lib/zip_dirent.c
Line 741 in b57ed83
This marks CRCs as invalid, meaning they are not checked when reading the file content. Reading the ZIP spec, I understand this is expected for the WinZip AE-2 encryption, for which indeed the standard zip CRCs are not meant to be used and should be set to zero (NB: they are not -- I see valid non-zero CRCs in the zip).
The expectation in this situation is to check the authentication code (HMAC). As best I can tell, libzip is indeed reading and checking the HMAC, so I am not sure why this isn't able to catch all corrupted bytes.
Digging further with the debugger, I finally realize that the error is indeed caught, but not actually propagated because of this:
libzip/lib/zip_source_read.c
Lines 67 to 72 in b57ed83
Bytes were actually read, so
zip_source_read()
does not return-1
which is normally used to indicate a read error. Thereforezip_fread()
does not copy the source error into the file error structure, and the actual error goes undetected.Edit: it's more complicated than that; the error reporting works well when compression is disabled, I assume because of the check for
src->had_read_error
. The issue is probably an interaction between the encryption and compression sources.The text was updated successfully, but these errors were encountered: