Skip to content

Commit 9fe785d

Browse files
authored
Merge pull request #1686 from val-ms/CLAM-2957-generate-aes-key-optimization
Performance improvement generating key to decrypt read-only office docs
2 parents 2c29eff + e5f5464 commit 9fe785d

2 files changed

Lines changed: 139 additions & 26 deletions

File tree

cmake/FindRust.cmake

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,33 @@ if(EXISTS "${CMAKE_SOURCE_DIR}/.cargo/vendor")
484484
list(APPEND CARGO_ARGS "--offline")
485485
endif()
486486

487+
string(STRIP "${RUSTFLAGS} $ENV{RUSTFLAGS}" RUSTFLAGS)
488+
489+
if(RUSTFLAGS MATCHES "(^| )-Zsanitizer=[^ ]+($| )")
490+
if(NOT rustc_VERSION MATCHES "-nightly")
491+
message(FATAL_ERROR
492+
"Rust sanitizer support requires a nightly Rust toolchain because "
493+
"`-Zsanitizer` is unstable."
494+
)
495+
endif()
496+
endif()
497+
498+
if(RUSTFLAGS MATCHES "(^| )-Zsanitizer=memory($| )")
499+
if(NOT "${RUST_COMPILER_TARGET}" MATCHES "^(aarch64|x86_64)-unknown-linux-gnu$")
500+
message(FATAL_ERROR
501+
"Rust MemorySanitizer requires a target whose standard library can be "
502+
"rebuilt with `-Zbuild-std`, and `${RUST_COMPILER_TARGET}` is not a "
503+
"supported Rust MSan target. Use a supported Linux target or remove "
504+
"`-Zsanitizer=memory` from RUSTFLAGS."
505+
)
506+
endif()
507+
508+
list(APPEND CARGO_ARGS
509+
"-Zbuild-std=core,alloc,std,proc_macro,panic_abort"
510+
"-Zbuild-std-features=panic_immediate_abort"
511+
)
512+
endif()
513+
487514
if(NOT "${RUST_COMPILER_TARGET}" MATCHES "^universal-apple-darwin$")
488515
# Don't specify the target for macOS universal builds, we'll do that manually for each build.
489516
list(APPEND CARGO_ARGS "--target" ${RUST_COMPILER_TARGET})
@@ -501,7 +528,7 @@ elseif(${CMAKE_BUILD_TYPE} STREQUAL "RelWithDebInfo")
501528
else()
502529
set(CARGO_BUILD_TYPE "debug")
503530
endif()
504-
string(STRIP "${RUSTFLAGS} $ENV{RUSTFLAGS}" RUSTFLAGS)
531+
string(STRIP "${RUSTFLAGS}" RUSTFLAGS)
505532

506533
find_package_handle_standard_args(Rust
507534
REQUIRED_VARS cargo_EXECUTABLE

libclamav/ole2_extract.c

Lines changed: 111 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
#include <errno.h>
3838
#include <conv.h>
3939
#include <zlib.h>
40+
#include <openssl/evp.h>
4041
#ifdef HAVE_UNISTD_H
4142
#include <unistd.h>
4243
#endif
@@ -2350,15 +2351,43 @@ static cl_error_t generate_key_aes(const char *const password, encryption_key_t
23502351
encryption_verifier_t *verifier)
23512352
{
23522353
uint8_t *buffer = NULL;
2354+
uint8_t *heapBuffer = NULL;
2355+
uint8_t stackBuffer[128] = {0};
23532356
size_t bufLen = 0;
2357+
size_t passwordLen = 0;
23542358
cl_error_t ret = CL_ERROR;
23552359
uint32_t i = 0;
2360+
size_t j = 0;
23562361
uint8_t sha1[sizeof(uint32_t) + SHA1_HASH_SIZE + sizeof(uint32_t)] = {0};
23572362
uint8_t *sha1Dst = &(sha1[sizeof(uint32_t)]);
23582363
uint8_t buf1[64];
23592364
uint8_t buf2[64];
23602365
uint8_t doubleSha[SHA1_HASH_SIZE * 2];
2361-
uint32_t tmp = 0;
2366+
uint32_t tmp = 0;
2367+
EVP_MD_CTX *sha1_ctx = NULL;
2368+
2369+
#if OPENSSL_VERSION_MAJOR >= 3
2370+
OSSL_LIB_CTX *ossl_ctx = NULL;
2371+
EVP_MD *sha1_md = NULL;
2372+
#else
2373+
const EVP_MD *sha1_md = NULL;
2374+
#endif
2375+
2376+
#define SHA1_HASH_WITH_CTX(data, data_len, out) \
2377+
do { \
2378+
if (!EVP_DigestInit_ex(sha1_ctx, sha1_md, NULL) || \
2379+
!EVP_DigestUpdate(sha1_ctx, (data), (data_len)) || \
2380+
!EVP_DigestFinal_ex(sha1_ctx, (out), NULL)) { \
2381+
cli_errmsg("ole2: SHA1 hash computation failed in generate_key_aes.\n"); \
2382+
goto done; \
2383+
} \
2384+
} while (0)
2385+
2386+
if (NULL == password || NULL == key || NULL == verifier) {
2387+
cli_errmsg("ole2: Invalid arguments to generate_key_aes\n");
2388+
ret = CL_ENULLARG;
2389+
goto done;
2390+
}
23622391

23632392
if (!key_length_valid_aes_bits(key->key_length_bits)) {
23642393
cli_errmsg("ole2: Invalid key length '0x%x'\n", key->key_length_bits / 8);
@@ -2367,15 +2396,56 @@ static cl_error_t generate_key_aes(const char *const password, encryption_key_t
23672396

23682397
memset(key->key, 0, key->key_length_bits / 8);
23692398

2370-
bufLen = verifier->salt_size + (strlen(password) * 2);
2399+
passwordLen = strlen(password);
2400+
if (passwordLen > ((SIZE_MAX - verifier->salt_size) / 2)) {
2401+
cli_errmsg("ole2: Password length overflow\n");
2402+
ret = CL_EARG;
2403+
goto done;
2404+
}
2405+
bufLen = verifier->salt_size + (passwordLen * 2);
2406+
2407+
if (bufLen <= sizeof(stackBuffer)) {
2408+
buffer = stackBuffer;
2409+
} else {
2410+
heapBuffer = calloc(bufLen, 1);
2411+
if (NULL == heapBuffer) {
2412+
cli_errmsg("ole2: calloc failed\n");
2413+
ret = CL_EMEM;
2414+
goto done;
2415+
}
2416+
buffer = heapBuffer;
2417+
}
2418+
2419+
#if OPENSSL_VERSION_MAJOR >= 3
2420+
ossl_ctx = OSSL_LIB_CTX_new();
2421+
if (NULL == ossl_ctx) {
2422+
cli_errmsg("ole2: Failed to create OpenSSL library context\n");
2423+
ret = CL_EMEM;
2424+
goto done;
2425+
}
2426+
sha1_md = EVP_MD_fetch(ossl_ctx, "SHA1", "-fips");
2427+
#else
2428+
sha1_md = EVP_get_digestbyname("sha1");
2429+
#endif
2430+
if (NULL == sha1_md) {
2431+
cli_errmsg("ole2: Failed to initialize SHA1 digest\n");
2432+
ret = CL_EARG;
2433+
goto done;
2434+
}
23712435

2372-
buffer = calloc(bufLen, 1);
2373-
if (NULL == buffer) {
2374-
cli_errmsg("ole2: calloc failed\n");
2436+
sha1_ctx = EVP_MD_CTX_new();
2437+
if (NULL == sha1_ctx) {
2438+
cli_errmsg("ole2: Failed to allocate SHA1 context\n");
23752439
ret = CL_EMEM;
23762440
goto done;
23772441
}
23782442

2443+
#ifdef EVP_MD_CTX_FLAG_NON_FIPS_ALLOW
2444+
if (OPENSSL_VERSION_NUMBER < 0x30000000L) {
2445+
EVP_MD_CTX_set_flags(sha1_ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
2446+
}
2447+
#endif
2448+
23792449
tmp = verifier->salt_size;
23802450
if (verifier->salt_size > sizeof(verifier->salt)) {
23812451
cli_dbgmsg("ole2: Invalid salt length '0x%x'\n", verifier->salt_size);
@@ -2384,37 +2454,37 @@ static cl_error_t generate_key_aes(const char *const password, encryption_key_t
23842454
memcpy(buffer, verifier->salt, tmp);
23852455

23862456
/*Convert to UTF16-LE*/
2387-
for (i = 0; i < (uint32_t)strlen(password); i++) {
2388-
buffer[verifier->salt_size + (i * 2)] = password[i];
2457+
for (j = 0; j < passwordLen; j++) {
2458+
buffer[verifier->salt_size + (j * 2)] = (uint8_t)password[j];
23892459
}
23902460

2391-
(void)cl_sha1(buffer, bufLen, sha1Dst, NULL);
2461+
SHA1_HASH_WITH_CTX(buffer, bufLen, sha1Dst);
23922462

23932463
for (i = 0; i < GENERATE_KEY_AES_ITERATIONS; i++) {
23942464
uint32_t eye = ole2_endian_convert_32(i);
23952465

23962466
memcpy(sha1, &eye, sizeof(eye));
2397-
(void)cl_sha1(sha1, SHA1_HASH_SIZE + sizeof(uint32_t), sha1Dst, NULL);
2467+
SHA1_HASH_WITH_CTX(sha1, SHA1_HASH_SIZE + sizeof(uint32_t), sha1Dst);
23982468
}
23992469

24002470
memset(&(sha1Dst[SHA1_HASH_SIZE]), 0, sizeof(uint32_t));
24012471

2402-
(void)cl_sha1(sha1Dst, SHA1_HASH_SIZE + sizeof(uint32_t), sha1Dst, NULL);
2472+
SHA1_HASH_WITH_CTX(sha1Dst, SHA1_HASH_SIZE + sizeof(uint32_t), sha1Dst);
24032473

24042474
memset(buf1, 0x36, sizeof(buf1));
24052475
for (i = 0; i < SHA1_HASH_SIZE; i++) {
24062476
buf1[i] = buf1[i] ^ sha1Dst[i];
24072477
}
24082478

24092479
// now sha1 buf1
2410-
(void)cl_sha1(buf1, sizeof(buf1), doubleSha, NULL);
2480+
SHA1_HASH_WITH_CTX(buf1, sizeof(buf1), doubleSha);
24112481

24122482
memset(buf2, 0x5c, sizeof(buf2));
24132483
for (i = 0; i < SHA1_HASH_SIZE; i++) {
24142484
buf2[i] = buf2[i] ^ sha1Dst[i];
24152485
}
24162486

2417-
(void)cl_sha1(buf2, sizeof(buf2), &(doubleSha[SHA1_HASH_SIZE]), NULL);
2487+
SHA1_HASH_WITH_CTX(buf2, sizeof(buf2), &(doubleSha[SHA1_HASH_SIZE]));
24182488

24192489
tmp = key->key_length_bits / 8;
24202490
if (tmp > sizeof(key->key)) {
@@ -2424,8 +2494,22 @@ static cl_error_t generate_key_aes(const char *const password, encryption_key_t
24242494

24252495
memcpy(key->key, doubleSha, tmp);
24262496
ret = CL_SUCCESS;
2497+
24272498
done:
2428-
CLI_FREE_AND_SET_NULL(buffer);
2499+
#undef SHA1_HASH_WITH_CTX
2500+
2501+
CLI_FREE_AND_SET_NULL(heapBuffer);
2502+
if (NULL != sha1_ctx) {
2503+
EVP_MD_CTX_free(sha1_ctx);
2504+
}
2505+
#if OPENSSL_VERSION_MAJOR >= 3
2506+
if (NULL != sha1_md) {
2507+
EVP_MD_free(sha1_md);
2508+
}
2509+
if (NULL != ossl_ctx) {
2510+
OSSL_LIB_CTX_free(ossl_ctx);
2511+
}
2512+
#endif
24292513

24302514
return ret;
24312515
}
@@ -2556,8 +2640,9 @@ static bool initialize_encryption_key(
25562640
encryption_key_t *encryptionKey,
25572641
encryption_status_t *pEncryptionStatus)
25582642
{
2559-
bool bRet = false;
2560-
size_t idx = 0;
2643+
bool bRet = false;
2644+
size_t idx = 0;
2645+
size_t csp_name_max_u16 = 0;
25612646
encryption_key_t key;
25622647
bool bAES = false;
25632648

@@ -2697,21 +2782,20 @@ static bool initialize_encryption_key(
26972782
goto done;
26982783
}
26992784

2700-
while (true) {
2701-
// Check if we've gone past the end of the buffer without finding the end of the CSPName string.
2702-
if ((idx + 1) * sizeof(uint16_t) > remainingBytes) {
2703-
cli_dbgmsg("ole2: CSPName is missing null terminator before end of buffer.\n");
2704-
goto done;
2705-
}
2706-
// Check if we've found the end of the CSPName string.
2785+
csp_name_max_u16 = remainingBytes / sizeof(*encryptionInfo_CSPName);
2786+
2787+
for (idx = 0; idx < csp_name_max_u16; idx++) {
27072788
if (encryptionInfo_CSPName[idx] == 0) {
27082789
break;
27092790
}
2710-
// Found another character in the CSPName string, keep going.
2711-
idx++;
27122791
}
27132792

2714-
CSPName_length = (idx + 1) * sizeof(uint16_t);
2793+
if (idx == csp_name_max_u16) {
2794+
cli_dbgmsg("ole2: CSPName is missing null terminator before end of buffer.\n");
2795+
goto done;
2796+
}
2797+
2798+
CSPName_length = (idx + 1) * sizeof(*encryptionInfo_CSPName);
27152799

27162800
encryptionVerifierPtr = (uint8_t *)encryptionInfo_CSPName + CSPName_length;
27172801
remainingBytes -= CSPName_length;
@@ -2720,6 +2804,7 @@ static bool initialize_encryption_key(
27202804
cli_dbgmsg("ole2: No encryption_verifier_t\n");
27212805
goto done;
27222806
}
2807+
27232808
copy_encryption_verifier(&encryptionVerifier, encryptionVerifierPtr);
27242809

27252810
key.key_length_bits = encryptionInfo.encryptionInfo.keySize;
@@ -2741,6 +2826,7 @@ static bool initialize_encryption_key(
27412826
memcpy(encryptionKey, &key, sizeof(encryption_key_t));
27422827
bRet = true;
27432828
pEncryptionStatus->encryption_type = VELVET_SWEATSHOP_ENCRYPTION;
2829+
27442830
done:
27452831

27462832
if (pEncryptionStatus->encryption_type) {

0 commit comments

Comments
 (0)