diff --git a/goopenssl.h b/goopenssl.h index c9f4c2a4..98c29087 100644 --- a/goopenssl.h +++ b/goopenssl.h @@ -144,12 +144,40 @@ go_openssl_EVP_CIPHER_CTX_open_wrapper(const GO_EVP_CIPHER_CTX_PTR ctx, const unsigned char *aad, int aad_len, const unsigned char *tag) { - if (in_len == 0) in = ""; + if (in_len == 0) { + in = ""; + // When the caller only wants to verify authenticated additional data + // (AAD), no output buffer is technically needed as there is no data to + // decrypt. Unfortunately, AAD is specified by calling EVP_DecryptUpdate + // with out set to NULL, so decrypting a zero-length input with a NULL + // output buffer looks to OpenSSL like specifying an additional + // (zero-length) chunk of AAD. This is effectively a no-op and + // verification will proceed as expected, except for OpenSSL 1.0.2 in + // FIPS mode. It will fail to verify unless EVP_DecryptUpdate is called + // at least once with a non-NULL output buffer. OpenSSL will not + // dereference the output buffer when the input length is zero, so set + // it to an arbitrary non-NULL pointer to satisfy OpenSSL. + // + // While a stack-allocated buffer could be used, that would risk a + // buffer overflow which would then smash the stack if OpenSSL + // unexpectedly dereferenced it. Instead pass a value which would + // trigger a segfault if dereferenced on any modern platform where a + // NULL-pointer dereference would also segfault. + if (out == NULL) out = (unsigned char *)1; + } if (aad_len == 0) aad = ""; if (go_openssl_EVP_CipherInit_ex(ctx, NULL, NULL, NULL, nonce, GO_AES_DECRYPT) != 1) return 0; + // OpenSSL 1.0.x FIPS Object Module 2.0 versions below 2.0.5 require that + // the tag be set before the ciphertext, otherwise EVP_DecryptUpdate returns + // an error. At least one extant commercially-supported, FIPS validated + // build of OpenSSL 1.0.2 uses FIPS module version 2.0.1. Set the tag first + // to maximize compatibility with all OpenSSL version combinations. + if (go_openssl_EVP_CIPHER_CTX_ctrl(ctx, GO_EVP_CTRL_GCM_SET_TAG, 16, (unsigned char *)(tag)) != 1) + return 0; + int discard_len, out_len; if (go_openssl_EVP_DecryptUpdate(ctx, NULL, &discard_len, aad, aad_len) != 1 || go_openssl_EVP_DecryptUpdate(ctx, out, &out_len, in, in_len) != 1) @@ -157,9 +185,6 @@ go_openssl_EVP_CIPHER_CTX_open_wrapper(const GO_EVP_CIPHER_CTX_PTR ctx, return 0; } - if (go_openssl_EVP_CIPHER_CTX_ctrl(ctx, GO_EVP_CTRL_GCM_SET_TAG, 16, (unsigned char *)(tag)) != 1) - return 0; - if (go_openssl_EVP_DecryptFinal_ex(ctx, out + out_len, &discard_len) != 1) return 0;