From 2ea0dd12b588bd2b4d5d6a958379ed4fe200c3f6 Mon Sep 17 00:00:00 2001 From: wk333 <13474090681@163.com> Date: Thu, 1 Feb 2024 14:18:06 +0800 Subject: [PATCH] Fix CVE-2024-0914 --- CVE-2024-0914-1.patch | 156 +++++++++ CVE-2024-0914-2.patch | 392 ++++++++++++++++++++++ CVE-2024-0914-3.patch | 743 ++++++++++++++++++++++++++++++++++++++++++ opencryptoki.spec | 8 +- 4 files changed, 1298 insertions(+), 1 deletion(-) create mode 100644 CVE-2024-0914-1.patch create mode 100644 CVE-2024-0914-2.patch create mode 100644 CVE-2024-0914-3.patch diff --git a/CVE-2024-0914-1.patch b/CVE-2024-0914-1.patch new file mode 100644 index 0000000..eeabcf5 --- /dev/null +++ b/CVE-2024-0914-1.patch @@ -0,0 +1,156 @@ +From 2ea019ee2b09f15724d808382d53baca03403288 Mon Sep 17 00:00:00 2001 +From: Ingo Franzki +Date: Tue, 12 Dec 2023 17:16:56 +0100 +Subject: [PATCH] COMMON: Update rsa_parse_block_type_2() to not leak the + message length + +Take the implementation of OpenSSL function RSA_padding_check_PKCS1_type_2() +in crypto/rsa/rsa_pk1.c instead of ossl_rsa_padding_check_PKCS1_type_2(), since +the latter leaks the message size. + +Signed-off-by: Ingo Franzki +--- + usr/lib/common/mech_rsa.c | 85 +++++++++++++++++++++++++++------------ + 1 file changed, 60 insertions(+), 25 deletions(-) + +diff --git a/usr/lib/common/mech_rsa.c b/usr/lib/common/mech_rsa.c +index 326c5795c..7bab1a84f 100644 +--- a/usr/lib/common/mech_rsa.c ++++ b/usr/lib/common/mech_rsa.c +@@ -29,6 +29,7 @@ + #include "constant_time.h" + + #include ++#include + + CK_BBOOL is_rsa_mechanism(CK_MECHANISM_TYPE mech) + { +@@ -293,13 +294,16 @@ static CK_RV rsa_parse_block_type_2(CK_BYTE *in_data, + CK_BYTE *out_data, + CK_ULONG *out_data_len) + { +- unsigned int ok = 0, found, zero; +- size_t zero_index = 0, msg_index, mlen; +- size_t i, j; ++ int i; ++ unsigned char *em = NULL; ++ unsigned int good, found_zero_byte, mask, equals0; ++ int zero_index = 0, msg_index, mlen = -1; ++ int out_len = *out_data_len; ++ int rsa_size = in_data_len; + + /* + * The implementation of this function is copied from OpenSSL's function +- * ossl_rsa_padding_check_PKCS1_type_2() in crypto/rsa/rsa_pk1.c ++ * RSA_padding_check_PKCS1_type_2() in crypto/rsa/rsa_pk1.c + * and is slightly modified to fit to the OpenCryptoki environment. + * + * The OpenSSL code is licensed under the Apache License 2.0. +@@ -324,55 +328,86 @@ static CK_RV rsa_parse_block_type_2(CK_BYTE *in_data, + * PKCS#1 v1.5 decryption. See "PKCS #1 v2.2: RSA Cryptography Standard", + * section 7.2.2. + */ +- if (in_data_len < 11) { ++ if (rsa_size < RSA_PKCS1_PADDING_SIZE) { + TRACE_DEVEL("%s\n", ock_err(ERR_FUNCTION_FAILED)); + return CKR_FUNCTION_FAILED; + } + +- ok = constant_time_is_zero(in_data[0]); +- ok &= constant_time_eq(in_data[1], 2); ++ em = malloc(rsa_size); ++ if (em == NULL) { ++ TRACE_DEVEL("%s\n", ock_err(ERR_HOST_MEMORY)); ++ return CKR_HOST_MEMORY; ++ } ++ ++ /* in_data_len is always equal to rsa_size */ ++ memcpy(em, in_data, rsa_size); ++ ++ good = constant_time_is_zero(em[0]); ++ good &= constant_time_eq(em[1], 2); + + /* scan over padding data */ +- found = 0; +- for (i = 2; i < in_data_len; i++) { +- zero = constant_time_is_zero(in_data[i]); ++ found_zero_byte = 0; ++ for (i = 2; i < rsa_size; i++) { ++ equals0 = constant_time_is_zero(em[i]); + +- zero_index = constant_time_select_int(~found & zero, i, zero_index); +- found |= zero; ++ zero_index = constant_time_select_int(~found_zero_byte & equals0, ++ i, zero_index); ++ found_zero_byte |= equals0; + } + + /* +- * PS must be at least 8 bytes long, and it starts two bytes into |enc_msg|. ++ * PS must be at least 8 bytes long, and it starts two bytes into |em|. + * If we never found a 0-byte, then |zero_index| is 0 and the check + * also fails. + */ +- ok &= constant_time_ge(zero_index, 2 + 8); ++ good &= constant_time_ge(zero_index, 2 + 8); + + /* + * Skip the zero byte. This is incorrect if we never found a zero-byte + * but in this case we also do not copy the message out. + */ + msg_index = zero_index + 1; +- mlen = in_data_len - msg_index; ++ mlen = rsa_size - msg_index; + + /* + * For good measure, do this check in constant time as well. + */ +- ok &= constant_time_ge(*out_data_len, mlen); ++ good &= constant_time_ge(out_len, mlen); + + /* +- * since at this point the |msg_index| does not provide the signal +- * indicating if the padding check failed or not, we don't have to worry +- * about leaking the length of returned message, we still need to ensure +- * that we read contents of both buffers so that cache accesses don't leak +- * the value of |good| ++ * Move the result in-place by |rsa_size|-RSA_PKCS1_PADDING_SIZE-|mlen| ++ * bytes to the left. ++ * Then if |good| move |mlen| bytes from |em|+RSA_PKCS1_PADDING_SIZE to ++ * |out_data|. Otherwise leave |out_data| unchanged. ++ * Copy the memory back in a way that does not reveal the size of ++ * the data being copied via a timing side channel. This requires copying ++ * parts of the buffer multiple times based on the bits set in the real ++ * length. Clear bits do a non-copy with identical access pattern. ++ * The loop below has overall complexity of O(N*log(N)). + */ +- for (i = msg_index, j = 0; i < in_data_len && j < *out_data_len; i++, j++) +- out_data[j] = constant_time_select_8(ok, in_data[i], out_data[j]); ++ out_len = constant_time_select_int( ++ constant_time_lt(rsa_size - RSA_PKCS1_PADDING_SIZE, out_len), ++ rsa_size - RSA_PKCS1_PADDING_SIZE, ++ out_len); ++ for (msg_index = 1; msg_index < rsa_size - RSA_PKCS1_PADDING_SIZE; ++ msg_index <<= 1) { ++ mask = ~constant_time_eq( ++ msg_index & (rsa_size - RSA_PKCS1_PADDING_SIZE - mlen), 0); ++ for (i = RSA_PKCS1_PADDING_SIZE; i < rsa_size - msg_index; i++) ++ em[i] = constant_time_select_8(mask, em[i + msg_index], em[i]); ++ } ++ for (i = 0; i < out_len; i++) { ++ mask = good & constant_time_lt(i, mlen); ++ out_data[i] = constant_time_select_8( ++ mask, em[i + RSA_PKCS1_PADDING_SIZE], out_data[i]); ++ } ++ ++ OPENSSL_cleanse(em, rsa_size); ++ free(em); + +- *out_data_len = j; ++ *out_data_len = constant_time_select_int(good, mlen, 0); + +- return constant_time_select_int(ok, CKR_OK, CKR_ENCRYPTED_DATA_INVALID); ++ return constant_time_select_int(good, CKR_OK, CKR_ENCRYPTED_DATA_INVALID); + } + + CK_RV rsa_parse_block(CK_BYTE *in_data, diff --git a/CVE-2024-0914-2.patch b/CVE-2024-0914-2.patch new file mode 100644 index 0000000..e30b527 --- /dev/null +++ b/CVE-2024-0914-2.patch @@ -0,0 +1,392 @@ +From 7ffc0e135b4d923d686be536aa7bf69405a360a1 Mon Sep 17 00:00:00 2001 +From: Ingo Franzki +Date: Fri, 12 Jan 2024 09:36:27 +0100 +Subject: [PATCH] Constant time fixes for C_Decrypt return code handling + +Return code handling of C_Decrypt, C_DecryptUpdate, and C_DecryptFinal must +be performed in a constant time manner for RSA mechanisms. Otherwise it +may cause a timing side channel that may be used to perform a Bleichenbacher +style attack. + +Handling of error situations with CKR_BUFFER_TOO_SMALL or size-query calls, +where the output buffer is NULL and the required size of the output buffer +is to be returned, do not need to be performed in constant time, since +these cases are shortcut anyway, and the result is only dependent on the +modulus size of the RSA key (which is public information anyway). + +Signed-off-by: Ingo Franzki +--- + usr/lib/common/new_host.c | 39 ++++++++++++++++++++++---- + usr/lib/ep11_stdll/ep11_specific.c | 30 ++++++++++++-------- + usr/lib/ep11_stdll/new_host.c | 45 +++++++++++++++++++++++++----- + usr/lib/icsf_stdll/new_host.c | 40 ++++++++++++++++++++++---- + 4 files changed, 123 insertions(+), 31 deletions(-) + +diff --git a/usr/lib/common/new_host.c b/usr/lib/common/new_host.c +index 8a1e8723b..bbb0f6010 100644 +--- a/usr/lib/common/new_host.c ++++ b/usr/lib/common/new_host.c +@@ -47,6 +47,7 @@ + #include "trace.h" + #include "slotmgr.h" + #include "attributes.h" ++#include "constant_time.h" + + #include "../api/apiproto.h" + #include "../api/policy.h" +@@ -2345,6 +2346,7 @@ CK_RV SC_Decrypt(STDLL_TokData_t *tokdata, ST_SESSION_HANDLE *sSession, + SESSION *sess = NULL; + CK_BBOOL length_only = FALSE; + CK_RV rc = CKR_OK; ++ unsigned int mask; + + if (tokdata->initialized == FALSE) { + TRACE_ERROR("%s\n", ock_err(ERR_CRYPTOKI_NOT_INITIALIZED)); +@@ -2377,11 +2379,19 @@ CK_RV SC_Decrypt(STDLL_TokData_t *tokdata, ST_SESSION_HANDLE *sSession, + rc = decr_mgr_decrypt(tokdata, sess, length_only, &sess->decr_ctx, + pEncryptedData, ulEncryptedDataLen, pData, + pulDataLen); +- if (!is_rsa_mechanism(sess->decr_ctx.mech.mechanism) && rc != CKR_OK) ++ /* (!is_rsa_mechanism(sess->decr_ctx.mech.mechanism) && rc != CKR_OK) */ ++ mask = ~constant_time_is_zero( ++ is_rsa_mechanism(sess->decr_ctx.mech.mechanism)); ++ mask &= ~constant_time_eq(rc, CKR_OK); ++ if (mask) + TRACE_DEVEL("decr_mgr_decrypt() failed.\n"); + + done: +- if (rc != CKR_BUFFER_TOO_SMALL && (rc != CKR_OK || length_only != TRUE)) { ++ /* (rc != CKR_BUFFER_TOO_SMALL && (rc != CKR_OK || length_only != TRUE)) */ ++ mask = ~constant_time_eq(rc, CKR_OK); ++ mask |= constant_time_is_zero(length_only); ++ mask &= ~constant_time_eq(rc, CKR_BUFFER_TOO_SMALL); ++ if (mask) { + if (sess) + decr_mgr_cleanup(tokdata, sess, &sess->decr_ctx); + } +@@ -2404,6 +2414,7 @@ CK_RV SC_DecryptUpdate(STDLL_TokData_t *tokdata, ST_SESSION_HANDLE *sSession, + SESSION *sess = NULL; + CK_BBOOL length_only = FALSE; + CK_RV rc = CKR_OK; ++ unsigned int mask; + + if (tokdata->initialized == FALSE) { + TRACE_ERROR("%s\n", ock_err(ERR_CRYPTOKI_NOT_INITIALIZED)); +@@ -2436,11 +2447,18 @@ CK_RV SC_DecryptUpdate(STDLL_TokData_t *tokdata, ST_SESSION_HANDLE *sSession, + rc = decr_mgr_decrypt_update(tokdata, sess, length_only, + &sess->decr_ctx, pEncryptedPart, + ulEncryptedPartLen, pPart, pulPartLen); +- if (!is_rsa_mechanism(sess->decr_ctx.mech.mechanism) && rc != CKR_OK) ++ /* (!is_rsa_mechanism(sess->decr_ctx.mech.mechanism) && rc != CKR_OK) */ ++ mask = ~constant_time_is_zero( ++ is_rsa_mechanism(sess->decr_ctx.mech.mechanism)); ++ mask &= ~constant_time_eq(rc, CKR_OK); ++ if (mask) + TRACE_DEVEL("decr_mgr_decrypt_update() failed.\n"); + + done: +- if (rc != CKR_OK && rc != CKR_BUFFER_TOO_SMALL && sess != NULL) { ++ /* (rc != CKR_OK && rc != CKR_BUFFER_TOO_SMALL */ ++ mask = ~constant_time_eq(rc, CKR_OK); ++ mask &= ~constant_time_eq(rc, CKR_BUFFER_TOO_SMALL); ++ if (mask) { + if (sess) + decr_mgr_cleanup(tokdata, sess, &sess->decr_ctx); + } +@@ -2462,6 +2480,7 @@ CK_RV SC_DecryptFinal(STDLL_TokData_t *tokdata, ST_SESSION_HANDLE *sSession, + SESSION *sess = NULL; + CK_BBOOL length_only = FALSE; + CK_RV rc = CKR_OK; ++ unsigned int mask; + + if (tokdata->initialized == FALSE) { + TRACE_ERROR("%s\n", ock_err(ERR_CRYPTOKI_NOT_INITIALIZED)); +@@ -2493,11 +2512,19 @@ CK_RV SC_DecryptFinal(STDLL_TokData_t *tokdata, ST_SESSION_HANDLE *sSession, + + rc = decr_mgr_decrypt_final(tokdata, sess, length_only, &sess->decr_ctx, + pLastPart, pulLastPartLen); +- if (!is_rsa_mechanism(sess->decr_ctx.mech.mechanism) && rc != CKR_OK) ++ /* (!is_rsa_mechanism(sess->decr_ctx.mech.mechanism) && rc != CKR_OK) */ ++ mask = ~constant_time_is_zero( ++ is_rsa_mechanism(sess->decr_ctx.mech.mechanism)); ++ mask &= ~constant_time_eq(rc, CKR_OK); ++ if (mask) + TRACE_DEVEL("decr_mgr_decrypt_final() failed.\n"); + + done: +- if (rc != CKR_BUFFER_TOO_SMALL && (rc != CKR_OK || length_only != TRUE)) { ++ /* (rc != CKR_BUFFER_TOO_SMALL && (rc != CKR_OK || length_only != TRUE)) */ ++ mask = ~constant_time_eq(rc, CKR_OK); ++ mask |= constant_time_is_zero(length_only); ++ mask &= ~constant_time_eq(rc, CKR_BUFFER_TOO_SMALL); ++ if (mask) { + if (sess) + decr_mgr_cleanup(tokdata, sess, &sess->decr_ctx); + } +diff --git a/usr/lib/ep11_stdll/ep11_specific.c b/usr/lib/ep11_stdll/ep11_specific.c +index 723eb3f63..e8543300a 100644 +--- a/usr/lib/ep11_stdll/ep11_specific.c ++++ b/usr/lib/ep11_stdll/ep11_specific.c +@@ -9596,10 +9596,12 @@ CK_RV ep11tok_decrypt_final(STDLL_TokData_t * tokdata, SESSION * session, + rc = constant_time_select(constant_time_eq(rc, CKR_OK), + ep11_error_to_pkcs11_error(rc, session), + rc); +- if (rc != CKR_OK) { +- TRACE_ERROR("%s rc=0x%lx\n", __func__, rc); +- } else { +- TRACE_INFO("%s rc=0x%lx\n", __func__, rc); ++ if (!is_rsa_mechanism(ctx->mech.mechanism)) { ++ if (rc != CKR_OK) { ++ TRACE_ERROR("%s rc=0x%lx\n", __func__, rc); ++ } else { ++ TRACE_INFO("%s rc=0x%lx\n", __func__, rc); ++ } + } + + done: +@@ -9655,10 +9657,12 @@ CK_RV ep11tok_decrypt(STDLL_TokData_t * tokdata, SESSION * session, + rc = constant_time_select(constant_time_eq(rc, CKR_OK), + ep11_error_to_pkcs11_error(rc, session), + rc); +- if (rc != CKR_OK) { +- TRACE_ERROR("%s rc=0x%lx\n", __func__, rc); +- } else { +- TRACE_INFO("%s rc=0x%lx\n", __func__, rc); ++ if (!is_rsa_mechanism(ctx->mech.mechanism)) { ++ if (rc != CKR_OK) { ++ TRACE_ERROR("%s rc=0x%lx\n", __func__, rc); ++ } else { ++ TRACE_INFO("%s rc=0x%lx\n", __func__, rc); ++ } + } + + done: +@@ -9720,10 +9724,12 @@ CK_RV ep11tok_decrypt_update(STDLL_TokData_t * tokdata, SESSION * session, + rc = constant_time_select(constant_time_eq(rc, CKR_OK), + ep11_error_to_pkcs11_error(rc, session), + rc); +- if (rc != CKR_OK) { +- TRACE_ERROR("%s rc=0x%lx\n", __func__, rc); +- } else { +- TRACE_INFO("%s rc=0x%lx\n", __func__, rc); ++ if (!is_rsa_mechanism(ctx->mech.mechanism)) { ++ if (rc != CKR_OK) { ++ TRACE_ERROR("%s rc=0x%lx\n", __func__, rc); ++ } else { ++ TRACE_INFO("%s rc=0x%lx\n", __func__, rc); ++ } + } + + done: +diff --git a/usr/lib/ep11_stdll/new_host.c b/usr/lib/ep11_stdll/new_host.c +index 55e34c18f..299a1d3c3 100644 +--- a/usr/lib/ep11_stdll/new_host.c ++++ b/usr/lib/ep11_stdll/new_host.c +@@ -38,6 +38,7 @@ + #include "slotmgr.h" + #include "attributes.h" + #include "ep11_specific.h" ++#include "constant_time.h" + + #include "../api/apiproto.h" + #include "../api/policy.h" +@@ -2466,6 +2467,7 @@ CK_RV SC_Decrypt(STDLL_TokData_t *tokdata, ST_SESSION_HANDLE *sSession, + SESSION *sess = NULL; + CK_BBOOL length_only = FALSE; + CK_RV rc = CKR_OK; ++ unsigned int mask; + + if (tokdata->initialized == FALSE) { + TRACE_ERROR("%s\n", ock_err(ERR_CRYPTOKI_NOT_INITIALIZED)); +@@ -2513,17 +2515,29 @@ CK_RV SC_Decrypt(STDLL_TokData_t *tokdata, ST_SESSION_HANDLE *sSession, + length_only, sess->decr_ctx.key, + pEncryptedData, ulEncryptedDataLen, + pData, pulDataLen); +- if (!is_rsa_mechanism(sess->decr_ctx.mech.mechanism) && rc != CKR_OK) ++ /* (!is_rsa_mechanism(sess->decr_ctx.mech.mechanism) && rc != CKR_OK) */ ++ mask = ~constant_time_is_zero( ++ is_rsa_mechanism(sess->decr_ctx.mech.mechanism)); ++ mask &= ~constant_time_eq(rc, CKR_OK); ++ if (mask) + TRACE_DEVEL("ep11tok_decrypt_single() failed.\n"); + } else { + rc = ep11tok_decrypt(tokdata, sess, pEncryptedData, ulEncryptedDataLen, + pData, pulDataLen); +- if (!is_rsa_mechanism(sess->decr_ctx.mech.mechanism) && rc != CKR_OK) ++ /* (!is_rsa_mechanism(sess->decr_ctx.mech.mechanism) && rc != CKR_OK) */ ++ mask = ~constant_time_is_zero( ++ is_rsa_mechanism(sess->decr_ctx.mech.mechanism)); ++ mask &= ~constant_time_eq(rc, CKR_OK); ++ if (mask) + TRACE_DEVEL("ep11tok_decrypt() failed.\n"); + } + + done: +- if (rc != CKR_BUFFER_TOO_SMALL && (rc != CKR_OK || length_only != TRUE)) { ++ /* (rc != CKR_BUFFER_TOO_SMALL && (rc != CKR_OK || length_only != TRUE)) */ ++ mask = ~constant_time_eq(rc, CKR_OK); ++ mask |= constant_time_is_zero(length_only); ++ mask &= ~constant_time_eq(rc, CKR_BUFFER_TOO_SMALL); ++ if (mask) { + if (sess) + decr_mgr_cleanup(tokdata, sess, &sess->decr_ctx); + } +@@ -2545,6 +2559,7 @@ CK_RV SC_DecryptUpdate(STDLL_TokData_t *tokdata, ST_SESSION_HANDLE *sSession, + { + SESSION *sess = NULL; + CK_RV rc = CKR_OK; ++ unsigned int mask; + + if (tokdata->initialized == FALSE) { + TRACE_ERROR("%s\n", ock_err(ERR_CRYPTOKI_NOT_INITIALIZED)); +@@ -2596,11 +2611,18 @@ CK_RV SC_DecryptUpdate(STDLL_TokData_t *tokdata, ST_SESSION_HANDLE *sSession, + + rc = ep11tok_decrypt_update(tokdata, sess, pEncryptedPart, + ulEncryptedPartLen, pPart, pulPartLen); +- if (!is_rsa_mechanism(sess->decr_ctx.mech.mechanism) && rc != CKR_OK) ++ /* (!is_rsa_mechanism(sess->decr_ctx.mech.mechanism) && rc != CKR_OK) */ ++ mask = ~constant_time_is_zero( ++ is_rsa_mechanism(sess->decr_ctx.mech.mechanism)); ++ mask &= ~constant_time_eq(rc, CKR_OK); ++ if (mask) + TRACE_DEVEL("ep11tok_decrypt_update() failed.\n"); + + done: +- if (rc != CKR_OK && rc != CKR_BUFFER_TOO_SMALL && sess != NULL) { ++ /* (rc != CKR_OK && rc != CKR_BUFFER_TOO_SMALL */ ++ mask = ~constant_time_eq(rc, CKR_OK); ++ mask &= ~constant_time_eq(rc, CKR_BUFFER_TOO_SMALL); ++ if (mask) { + if (sess) + decr_mgr_cleanup(tokdata, sess, &sess->decr_ctx); + } +@@ -2622,6 +2644,7 @@ CK_RV SC_DecryptFinal(STDLL_TokData_t *tokdata, ST_SESSION_HANDLE *sSession, + SESSION *sess = NULL; + CK_BBOOL length_only = FALSE; + CK_RV rc = CKR_OK; ++ unsigned int mask; + + if (tokdata->initialized == FALSE) { + TRACE_ERROR("%s\n", ock_err(ERR_CRYPTOKI_NOT_INITIALIZED)); +@@ -2670,10 +2693,18 @@ CK_RV SC_DecryptFinal(STDLL_TokData_t *tokdata, ST_SESSION_HANDLE *sSession, + } + + rc = ep11tok_decrypt_final(tokdata, sess, pLastPart, pulLastPartLen); +- if (!is_rsa_mechanism(sess->decr_ctx.mech.mechanism) && rc != CKR_OK) ++ /* (!is_rsa_mechanism(sess->decr_ctx.mech.mechanism) && rc != CKR_OK) */ ++ mask = ~constant_time_is_zero( ++ is_rsa_mechanism(sess->decr_ctx.mech.mechanism)); ++ mask &= ~constant_time_eq(rc, CKR_OK); ++ if (mask) + TRACE_DEVEL("ep11tok_decrypt_final() failed.\n"); + done: +- if (rc != CKR_BUFFER_TOO_SMALL && (rc != CKR_OK || length_only != TRUE)) { ++ /* (rc != CKR_BUFFER_TOO_SMALL && (rc != CKR_OK || length_only != TRUE)) */ ++ mask = ~constant_time_eq(rc, CKR_OK); ++ mask |= constant_time_is_zero(length_only); ++ mask &= ~constant_time_eq(rc, CKR_BUFFER_TOO_SMALL); ++ if (mask) { + if (sess) + decr_mgr_cleanup(tokdata, sess, &sess->decr_ctx); + } +diff --git a/usr/lib/icsf_stdll/new_host.c b/usr/lib/icsf_stdll/new_host.c +index 6c419750d..d8064559d 100644 +--- a/usr/lib/icsf_stdll/new_host.c ++++ b/usr/lib/icsf_stdll/new_host.c +@@ -35,6 +35,8 @@ + #include "slotmgr.h" + #include "attributes.h" + #include "icsf_specific.h" ++#include "constant_time.h" ++ + #include "../api/apiproto.h" + #include "../api/policy.h" + +@@ -1768,6 +1770,7 @@ CK_RV SC_Decrypt(STDLL_TokData_t *tokdata, ST_SESSION_HANDLE *sSession, + SESSION *sess = NULL; + CK_BBOOL length_only = FALSE; + CK_RV rc = CKR_OK; ++ unsigned int mask; + + if (tokdata->initialized == FALSE) { + TRACE_ERROR("%s\n", ock_err(ERR_CRYPTOKI_NOT_INITIALIZED)); +@@ -1801,11 +1804,19 @@ CK_RV SC_Decrypt(STDLL_TokData_t *tokdata, ST_SESSION_HANDLE *sSession, + + rc = icsftok_decrypt(tokdata, sess, pEncryptedData, ulEncryptedDataLen, + pData, pulDataLen); +- if (!is_rsa_mechanism(sess->decr_ctx.mech.mechanism) && rc != CKR_OK) ++ /* (!is_rsa_mechanism(sess->decr_ctx.mech.mechanism) && rc != CKR_OK) */ ++ mask = ~constant_time_is_zero( ++ is_rsa_mechanism(sess->decr_ctx.mech.mechanism)); ++ mask &= ~constant_time_eq(rc, CKR_OK); ++ if (mask) + TRACE_DEVEL("icsftok_decrypt() failed.\n"); + + done: +- if (rc != CKR_BUFFER_TOO_SMALL && (rc != CKR_OK || length_only != TRUE)) { ++ /* (rc != CKR_BUFFER_TOO_SMALL && (rc != CKR_OK || length_only != TRUE)) */ ++ mask = ~constant_time_eq(rc, CKR_OK); ++ mask |= constant_time_is_zero(length_only); ++ mask &= ~constant_time_eq(rc, CKR_BUFFER_TOO_SMALL); ++ if (mask) { + if (sess) + decr_mgr_cleanup(tokdata, sess, &sess->decr_ctx); + } +@@ -1827,6 +1838,7 @@ CK_RV SC_DecryptUpdate(STDLL_TokData_t *tokdata, ST_SESSION_HANDLE *sSession, + { + SESSION *sess = NULL; + CK_RV rc = CKR_OK; ++ unsigned int mask; + + if (tokdata->initialized == FALSE) { + TRACE_ERROR("%s\n", ock_err(ERR_CRYPTOKI_NOT_INITIALIZED)); +@@ -1857,11 +1869,18 @@ CK_RV SC_DecryptUpdate(STDLL_TokData_t *tokdata, ST_SESSION_HANDLE *sSession, + + rc = icsftok_decrypt_update(tokdata, sess, pEncryptedPart, + ulEncryptedPartLen, pPart, pulPartLen); +- if (!is_rsa_mechanism(sess->decr_ctx.mech.mechanism) && rc != CKR_OK) ++ /* (!is_rsa_mechanism(sess->decr_ctx.mech.mechanism) && rc != CKR_OK) */ ++ mask = ~constant_time_is_zero( ++ is_rsa_mechanism(sess->decr_ctx.mech.mechanism)); ++ mask &= ~constant_time_eq(rc, CKR_OK); ++ if (mask) + TRACE_DEVEL("icsftok_decrypt_update() failed.\n"); + + done: +- if (rc != CKR_OK && rc != CKR_BUFFER_TOO_SMALL && sess != NULL) { ++ /* (rc != CKR_OK && rc != CKR_BUFFER_TOO_SMALL */ ++ mask = ~constant_time_eq(rc, CKR_OK); ++ mask &= ~constant_time_eq(rc, CKR_BUFFER_TOO_SMALL); ++ if (mask) { + if (sess) + decr_mgr_cleanup(tokdata, sess, &sess->decr_ctx); + } +@@ -1883,6 +1902,7 @@ CK_RV SC_DecryptFinal(STDLL_TokData_t *tokdata, ST_SESSION_HANDLE *sSession, + SESSION *sess = NULL; + CK_BBOOL length_only = FALSE; + CK_RV rc = CKR_OK; ++ unsigned int mask; + + if (tokdata->initialized == FALSE) { + TRACE_ERROR("%s\n", ock_err(ERR_CRYPTOKI_NOT_INITIALIZED)); +@@ -1915,10 +1935,18 @@ CK_RV SC_DecryptFinal(STDLL_TokData_t *tokdata, ST_SESSION_HANDLE *sSession, + length_only = TRUE; + + rc = icsftok_decrypt_final(tokdata, sess, pLastPart, pulLastPartLen); +- if (!is_rsa_mechanism(sess->decr_ctx.mech.mechanism) && rc != CKR_OK) ++ /* (!is_rsa_mechanism(sess->decr_ctx.mech.mechanism) && rc != CKR_OK) */ ++ mask = ~constant_time_is_zero( ++ is_rsa_mechanism(sess->decr_ctx.mech.mechanism)); ++ mask &= ~constant_time_eq(rc, CKR_OK); ++ if (mask) + TRACE_DEVEL("icsftok_decrypt_final() failed.\n"); + done: +- if (rc != CKR_BUFFER_TOO_SMALL && (rc != CKR_OK || length_only != TRUE)) { ++ /* (rc != CKR_BUFFER_TOO_SMALL && (rc != CKR_OK || length_only != TRUE)) */ ++ mask = ~constant_time_eq(rc, CKR_OK); ++ mask |= constant_time_is_zero(length_only); ++ mask &= ~constant_time_eq(rc, CKR_BUFFER_TOO_SMALL); ++ if (mask) { + if (sess) + decr_mgr_cleanup(tokdata, sess, &sess->decr_ctx); + } diff --git a/CVE-2024-0914-3.patch b/CVE-2024-0914-3.patch new file mode 100644 index 0000000..eeb2b1c --- /dev/null +++ b/CVE-2024-0914-3.patch @@ -0,0 +1,743 @@ +From c26e049bf40d656bc51429bad190b82fbf63f0c7 Mon Sep 17 00:00:00 2001 +From: Ingo Franzki +Date: Mon, 15 Jan 2024 12:53:37 +0100 +Subject: [PATCH] common: Add support for implicit rejection for RSA PKCS#1 + v1.5 de-padding + +Implicit rejection returns a pseudo random message in case the RSA PKCS#1 v1.5 +padding is incorrect, but returns no error. The pseudo random message is based +on static secret data (the private exponent) and the provided ciphertext, so +that the attacker cannot determine that the returned value is randomly generated +instead of the result of decryption and de-padding. + +The implicit rejection algorithm is the same as used by OpenSSL. + +Signed-off-by: Ingo Franzki +--- + COPYRIGHTS | 14 +- + usr/lib/common/h_extern.h | 11 +- + usr/lib/common/mech_openssl.c | 402 +++++++++++++++++++++++++++++++++- + usr/lib/common/mech_rsa.c | 147 ++++++++----- + 4 files changed, 515 insertions(+), 59 deletions(-) + +diff --git a/COPYRIGHTS b/COPYRIGHTS +index 2bb3dffe7..21b6b702e 100644 +--- a/COPYRIGHTS ++++ b/COPYRIGHTS +@@ -12,19 +12,29 @@ For code originating from OpenSSL: + * Note that in OpenSSL the file crypto/bn/rsa_sup_mul.c does no longer + * exist, it was removed with commit https://github.com/openssl/openssl/commit/4209ce68d8fe8b1506494efa03d378d05baf9ff8 + * - usr/lib/common/constant_time.h: Copied unchanged from OpenSSL from +- include/internal/constant_time.h ++ * include/internal/constant_time.h + * - The implementation of function rsa_parse_block_type_2() in + * usr/lib/common/mech_rsa.c is copied from OpenSSL's function + * ossl_rsa_padding_check_PKCS1_type_2() in crypto/rsa/rsa_pk1.c + * and is slightly modified to fit to the OpenCryptoki environment. + * See comment in function rsa_parse_block_type_2() for a list of changes. ++ * - The implementation of function openssl_specific_rsa_derive_kdk() in ++ * usr/lib/common/mech_openssl.c is copied from OpenSSL's function ++ * derive_kdk() in crypto/rsa/rsa_ossl.c and is slightly modified to fit to ++ * the OpenCryptoki environment. See comment in function ++ * openssl_specific_rsa_derive_kdk() for a list of changes. ++ * - The implementation of function openssl_specific_rsa_prf() in ++ * usr/lib/common/mech_openssl.c is copied from OpenSSL's function ++ * ossl_rsa_prf() in crypto/rsa/rsapk1.c and is slightly modified to fit to ++ * the OpenCryptoki environment. See comment in function ++ * openssl_specific_rsa_prf() for a list of changes. + * - The implementation of function decode_eme_oaep() in + * usr/lib/common/mech_rsa.c is copied from OpenSSL's function + * RSA_padding_check_PKCS1_OAEP_mgf1() in crypto/rsa/rsa_oaep.c and is + * slightly modified to fit to the OpenCryptoki environment. See comment in + * function decode_eme_oaep() for a list of changes. + * +- * Copyright 1999-2023 The OpenSSL Project Authors. All Rights Reserved. ++ * Copyright 1999-2024 The OpenSSL Project Authors. All Rights Reserved. + * + * The OpenSSL code is licensed under the Apache License 2.0 (the "License"). + * You can obtain a copy in the file LICENSE in the OpenSSL source distribution +diff --git a/usr/lib/common/h_extern.h b/usr/lib/common/h_extern.h +index 7400c6dba..1d79a4f71 100644 +--- a/usr/lib/common/h_extern.h ++++ b/usr/lib/common/h_extern.h +@@ -731,7 +731,8 @@ CK_RV rsa_format_block(STDLL_TokData_t *tokdata, + CK_RV rsa_parse_block(CK_BYTE *in_data, + CK_ULONG in_data_len, + CK_BYTE *out_data, +- CK_ULONG *out_data_len, CK_ULONG type); ++ CK_ULONG *out_data_len, CK_ULONG type, ++ CK_BYTE *kdk, CK_ULONG kdklen); + + CK_RV get_mgf_mech(CK_RSA_PKCS_MGF_TYPE mgf, CK_MECHANISM_TYPE *mech); + +@@ -3182,6 +3183,14 @@ CK_RV openssl_specific_hmac_update(SIGN_VERIFY_CONTEXT *ctx, CK_BYTE *in_data, + CK_RV openssl_specific_hmac_final(SIGN_VERIFY_CONTEXT *ctx, CK_BYTE *signature, + CK_ULONG *sig_len, CK_BBOOL sign); + ++CK_RV openssl_specific_rsa_derive_kdk(STDLL_TokData_t *tokdata, OBJECT *key_obj, ++ const CK_BYTE *in, CK_ULONG inlen, ++ CK_BYTE *kdk, CK_ULONG kdklen); ++CK_RV openssl_specific_rsa_prf(CK_BYTE *out, CK_ULONG outlen, ++ const char *label, CK_ULONG labellen, ++ const CK_BYTE *kdk, CK_ULONG kdklen, ++ uint16_t bitlen); ++ + #include "tok_spec_struct.h" + extern token_spec_t token_specific; + +diff --git a/usr/lib/common/mech_openssl.c b/usr/lib/common/mech_openssl.c +index 9983fcb3b..da5152896 100644 +--- a/usr/lib/common/mech_openssl.c ++++ b/usr/lib/common/mech_openssl.c +@@ -1154,6 +1154,7 @@ CK_RV openssl_specific_rsa_pkcs_decrypt(STDLL_TokData_t *tokdata, + CK_RV rc; + CK_BYTE out[MAX_RSA_KEYLEN]; + CK_ULONG modulus_bytes; ++ unsigned char kdk[SHA256_HASH_SIZE] = { 0 }; + + modulus_bytes = in_data_len; + +@@ -1163,7 +1164,16 @@ CK_RV openssl_specific_rsa_pkcs_decrypt(STDLL_TokData_t *tokdata, + goto done; + } + +- rc = rsa_parse_block(out, modulus_bytes, out_data, out_data_len, PKCS_BT_2); ++ rc = openssl_specific_rsa_derive_kdk(tokdata, key_obj, ++ in_data, in_data_len, ++ kdk, sizeof(kdk)); ++ if (rc != CKR_OK) { ++ TRACE_DEVEL("openssl_specific_rsa_derive_kdk failed\n"); ++ goto done; ++ } ++ ++ rc = rsa_parse_block(out, modulus_bytes, out_data, out_data_len, PKCS_BT_2, ++ kdk, sizeof(kdk)); + + done: + OPENSSL_cleanse(out, sizeof(out)); +@@ -1254,7 +1264,7 @@ CK_RV openssl_specific_rsa_pkcs_verify(STDLL_TokData_t *tokdata, SESSION *sess, + } + + rc = rsa_parse_block(out, modulus_bytes, out_data, &out_data_len, +- PKCS_BT_1); ++ PKCS_BT_1, NULL, 0); + if (rc == CKR_ENCRYPTED_DATA_INVALID) { + TRACE_ERROR("%s\n", ock_err(ERR_SIGNATURE_INVALID)); + return CKR_SIGNATURE_INVALID; +@@ -1318,7 +1328,8 @@ CK_RV openssl_specific_rsa_pkcs_verify_recover(STDLL_TokData_t *tokdata, + return rc; + } + +- rc = rsa_parse_block(out, modulus_bytes, out_data, out_data_len, PKCS_BT_1); ++ rc = rsa_parse_block(out, modulus_bytes, out_data, out_data_len, PKCS_BT_1, ++ NULL, 0); + if (rc == CKR_ENCRYPTED_DATA_INVALID) { + TRACE_ERROR("%s\n", ock_err(ERR_SIGNATURE_INVALID)); + return CKR_SIGNATURE_INVALID; +@@ -4983,3 +4994,388 @@ CK_RV openssl_specific_hmac_final(SIGN_VERIFY_CONTEXT *ctx, CK_BYTE *signature, + ctx->context = NULL; + return rv; + } ++ ++static CK_RV calc_rsa_priv_exp(STDLL_TokData_t *tokdata, OBJECT *key_obj, ++ CK_BYTE *priv_exp, CK_ULONG priv_exp_len) ++{ ++ CK_ATTRIBUTE *modulus = NULL, *pub_exp = NULL; ++ CK_ATTRIBUTE *prime1 = NULL, *prime2 = NULL; ++ BN_CTX *bn_ctx; ++ BIGNUM *n, *e, *p, *q, *d; ++ CK_RV rc; ++ ++ UNUSED(tokdata); ++ ++ bn_ctx = BN_CTX_secure_new(); ++ if (bn_ctx == NULL) { ++ TRACE_ERROR("BN_CTX_secure_new failed\n"); ++ return CKR_FUNCTION_FAILED; ++ } ++ ++ /* Get modulus a BIGNUM */ ++ rc = template_attribute_get_non_empty(key_obj->template, CKA_MODULUS, ++ &modulus); ++ if (rc != CKR_OK) { ++ TRACE_ERROR("Failed to get CKA_MODULUS\n"); ++ goto done; ++ } ++ ++ n = BN_CTX_get(bn_ctx); ++ if (n == NULL || ++ BN_bin2bn(modulus->pValue, modulus->ulValueLen, n) == NULL) { ++ TRACE_ERROR("BN_CTX_get/BN_bin2bn failed for modulus\n"); ++ rc = CKR_FUNCTION_FAILED; ++ goto done; ++ } ++ BN_set_flags(n, BN_FLG_CONSTTIME); ++ ++ /* Get public exponent a BIGNUM */ ++ rc = template_attribute_get_non_empty(key_obj->template, ++ CKA_PUBLIC_EXPONENT, &pub_exp); ++ if (rc != CKR_OK) { ++ TRACE_ERROR("Failed to get CKA_PUBLIC_EXPONENT\n"); ++ goto done; ++ } ++ ++ e = BN_CTX_get(bn_ctx); ++ if (e == NULL || ++ BN_bin2bn(pub_exp->pValue, pub_exp->ulValueLen, e) == NULL) { ++ TRACE_ERROR("BN_CTX_get/BN_bin2bn failed for public exponent\n"); ++ rc = CKR_FUNCTION_FAILED; ++ goto done; ++ } ++ BN_set_flags(e, BN_FLG_CONSTTIME); ++ ++ /* Get prime1 a BIGNUM */ ++ rc = template_attribute_get_non_empty(key_obj->template, CKA_PRIME_1, ++ &prime1); ++ if (rc != CKR_OK) { ++ TRACE_ERROR("Failed to get CKA_PRIME_1\n"); ++ goto done; ++ } ++ ++ p = BN_CTX_get(bn_ctx); ++ if (p == NULL || ++ BN_bin2bn(prime1->pValue, prime1->ulValueLen, p) == NULL) { ++ TRACE_ERROR("BN_CTX_get/BN_bin2bn failed for prime1\n"); ++ rc = CKR_FUNCTION_FAILED; ++ goto done; ++ } ++ BN_set_flags(p, BN_FLG_CONSTTIME); ++ ++ /* Get prime2 a BIGNUM */ ++ rc = template_attribute_get_non_empty(key_obj->template, CKA_PRIME_2, ++ &prime2); ++ if (rc != CKR_OK) { ++ TRACE_ERROR("Failed to get CKA_PRIME_2\n"); ++ goto done; ++ } ++ ++ q = BN_CTX_get(bn_ctx); ++ if (q == NULL || ++ BN_bin2bn(prime2->pValue, prime2->ulValueLen, q) == NULL) { ++ TRACE_ERROR("BN_CTX_get/BN_bin2bn failed for prime2\n"); ++ rc = CKR_FUNCTION_FAILED; ++ goto done; ++ } ++ BN_set_flags(q, BN_FLG_CONSTTIME); ++ ++ d = BN_CTX_get(bn_ctx); ++ if (d == NULL) { ++ TRACE_ERROR("BN_CTX_get failed to get d\n"); ++ rc = CKR_FUNCTION_FAILED; ++ goto done; ++ } ++ BN_set_flags(d, BN_FLG_CONSTTIME); ++ ++ /* ++ * phi(n) = (p - 1 )(q - 1) = n - p - q + 1 ++ * d = e ^{-1} mod phi(n). ++ */ ++ if (BN_copy(d, n) == NULL || ++ BN_sub(d, d, p) == 0 || ++ BN_sub(d, d, q) == 0 || ++ BN_add_word(d, 1) == 0 || ++ BN_mod_inverse(d, e, d, bn_ctx) == NULL) { ++ TRACE_ERROR("Failed to calculate private key part d\n"); ++ rc = CKR_FUNCTION_FAILED; ++ goto done; ++ } ++ ++ if (BN_bn2binpad(d, priv_exp, priv_exp_len) <= 0) { ++ TRACE_ERROR("BN_bn2binpad failed\n"); ++ rc = CKR_FUNCTION_FAILED; ++ goto done; ++ } ++ ++done: ++ BN_CTX_free(bn_ctx); ++ ++ return rc; ++} ++ ++CK_RV openssl_specific_rsa_derive_kdk(STDLL_TokData_t *tokdata, OBJECT *key_obj, ++ const CK_BYTE *in, CK_ULONG inlen, ++ CK_BYTE *kdk, CK_ULONG kdklen) ++{ ++ CK_ATTRIBUTE *priv_exp_attr = NULL, *modulus = NULL; ++ CK_BYTE *priv_exp = NULL, *buf = NULL; ++ EVP_PKEY *pkey = NULL; ++ EVP_MD_CTX *mdctx = NULL; ++ const EVP_MD *md = NULL; ++ size_t md_len; ++ unsigned char d_hash[SHA256_HASH_SIZE] = { 0 }; ++ CK_RV rc; ++ ++ /* ++ * The implementation of this function is copied from OpenSSL's function ++ * derive_kdk() in crypto/rsa/rsa_ossl.c and is slightly modified to fit to ++ * the OpenCryptoki environment. ++ * Changes include: ++ * - Different variable and define names. ++ * - Usage of TRACE_ERROR to report errors and issue debug messages. ++ * - Different return codes. ++ * - Different code to get the private key component 'd'. ++ * - Use of the EVP APIs instead of the internal APIs for Digest and HMAC ++ * operations. ++ */ ++ ++ if (kdklen != SHA256_HASH_SIZE) { ++ TRACE_ERROR("KDK length is wrong\n"); ++ return CKR_ARGUMENTS_BAD; ++ } ++ ++ rc = template_attribute_get_non_empty(key_obj->template, CKA_MODULUS, ++ &modulus); ++ if (rc != CKR_OK) { ++ TRACE_ERROR("Failed to get CKA_MODULUS\n"); ++ return rc; ++ } ++ ++ buf = calloc(1, modulus->ulValueLen); ++ if (buf == NULL) { ++ TRACE_ERROR("Failed to allocate a buffer for private exponent\n"); ++ return CKR_HOST_MEMORY; ++ } ++ ++ rc = template_attribute_get_non_empty(key_obj->template, ++ CKA_PRIVATE_EXPONENT, &priv_exp_attr); ++ if (rc != CKR_OK && rc != CKR_TEMPLATE_INCOMPLETE) { ++ TRACE_ERROR("Failed to get CKA_PRIVATE_EXPONENT\n"); ++ goto out; ++ } ++ ++ if (priv_exp_attr == NULL) { ++ rc = calc_rsa_priv_exp(tokdata, key_obj, buf, modulus->ulValueLen); ++ if (rc != CKR_OK) { ++ TRACE_ERROR("calc_rsa_priv_exp failed\n"); ++ goto out; ++ } ++ priv_exp = buf; ++ } else { ++ if (priv_exp_attr->ulValueLen < modulus->ulValueLen) { ++ memcpy(buf + modulus->ulValueLen - priv_exp_attr->ulValueLen, ++ priv_exp_attr->pValue, priv_exp_attr->ulValueLen); ++ priv_exp = buf; ++ } else { ++ priv_exp = (CK_BYTE *)priv_exp_attr->pValue + ++ priv_exp_attr->ulValueLen - modulus->ulValueLen; ++ } ++ } ++ ++ /* ++ * we use hardcoded hash so that migrating between versions that use ++ * different hash doesn't provide a Bleichenbacher oracle: ++ * if the attacker can see that different versions return different ++ * messages for the same ciphertext, they'll know that the message is ++ * synthetically generated, which means that the padding check failed ++ */ ++ md = EVP_sha256(); ++ if (md == NULL) { ++ TRACE_ERROR("EVP_sha256 failed\n"); ++ rc = CKR_FUNCTION_FAILED; ++ goto out; ++ } ++ ++ if (EVP_Digest(priv_exp, modulus->ulValueLen, d_hash, NULL, ++ md, NULL) <= 0) { ++ TRACE_ERROR("EVP_Digest failed\n"); ++ rc = CKR_FUNCTION_FAILED; ++ goto out; ++ } ++ ++ pkey = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL, d_hash, sizeof(d_hash)); ++ if (pkey == NULL) { ++ TRACE_ERROR("EVP_PKEY_new_mac_key() failed.\n"); ++ rc = CKR_FUNCTION_FAILED; ++ goto out; ++ } ++ ++ mdctx = EVP_MD_CTX_create(); ++ if (mdctx == NULL) { ++ TRACE_ERROR("EVP_MD_CTX_create() failed.\n"); ++ rc = CKR_FUNCTION_FAILED; ++ goto out; ++ } ++ ++ if (EVP_DigestSignInit(mdctx, NULL, md, NULL, pkey) != 1) { ++ TRACE_ERROR("EVP_DigestSignInit failed\n"); ++ rc = CKR_FUNCTION_FAILED; ++ goto out; ++ } ++ ++ if (inlen < modulus->ulValueLen) { ++ memset(buf, 0, modulus->ulValueLen - inlen); ++ if (EVP_DigestSignUpdate(mdctx, buf, modulus->ulValueLen - inlen)!= 1) { ++ TRACE_ERROR("EVP_DigestSignUpdate failed\n"); ++ rc = CKR_FUNCTION_FAILED; ++ goto out; ++ } ++ } ++ if (EVP_DigestSignUpdate(mdctx, in, inlen) != 1) { ++ TRACE_ERROR("EVP_DigestSignUpdate failed\n"); ++ rc = CKR_FUNCTION_FAILED; ++ goto out; ++ } ++ ++ md_len = kdklen; ++ if (EVP_DigestSignFinal(mdctx, kdk, &md_len) != 1 || ++ md_len != kdklen) { ++ TRACE_ERROR("EVP_DigestSignFinal failed\n"); ++ rc = CKR_FUNCTION_FAILED; ++ goto out; ++ } ++ ++ rc = CKR_OK; ++ ++out: ++ if (buf != NULL) ++ free(buf); ++ if (pkey != NULL) ++ EVP_PKEY_free(pkey); ++ if (mdctx != NULL) ++ EVP_MD_CTX_free(mdctx); ++ ++ return rc; ++} ++ ++CK_RV openssl_specific_rsa_prf(CK_BYTE *out, CK_ULONG outlen, ++ const char *label, CK_ULONG labellen, ++ const CK_BYTE *kdk, CK_ULONG kdklen, ++ uint16_t bitlen) ++{ ++ CK_RV rc; ++ CK_ULONG pos; ++ uint16_t iter = 0; ++ unsigned char be_iter[sizeof(iter)]; ++ unsigned char be_bitlen[sizeof(bitlen)]; ++ EVP_PKEY *pkey = NULL; ++ EVP_MD_CTX *mdctx = NULL; ++ unsigned char hmac_out[SHA256_HASH_SIZE]; ++ size_t md_len; ++ ++ /* ++ * The implementation of this function is copied from OpenSSL's function ++ * ossl_rsa_prf() in crypto/rsa/rsapk1.c and is slightly modified to fit to ++ * the providers environment. ++ * Changes include: ++ * - Different variable and define names. ++ * - Usage of TRACE_ERROR report errors and issue debug messages. ++ * - Different return codes. ++ * - Use of the EVP API instead of the internal APIs for HMAC operations. ++ */ ++ ++ if (kdklen != SHA256_HASH_SIZE) { ++ TRACE_ERROR("invalid kdklen\n"); ++ return CKR_ARGUMENTS_BAD; ++ } ++ if (outlen * 8 != bitlen) { ++ TRACE_ERROR("invalid outlen\n"); ++ return CKR_ARGUMENTS_BAD; ++ } ++ ++ be_bitlen[0] = (bitlen >> 8) & 0xff; ++ be_bitlen[1] = bitlen & 0xff; ++ ++ pkey = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL, kdk, kdklen); ++ if (pkey == NULL) { ++ TRACE_ERROR("EVP_PKEY_new_mac_key() failed.\n"); ++ rc = CKR_FUNCTION_FAILED; ++ goto out; ++ } ++ ++ mdctx = EVP_MD_CTX_create(); ++ if (mdctx == NULL) { ++ TRACE_ERROR("EVP_MD_CTX_create() failed.\n"); ++ rc = CKR_FUNCTION_FAILED; ++ goto out; ++ } ++ ++ /* ++ * we use hardcoded hash so that migrating between versions that use ++ * different hash doesn't provide a Bleichenbacher oracle: ++ * if the attacker can see that different versions return different ++ * messages for the same ciphertext, they'll know that the message is ++ * synthetically generated, which means that the padding check failed ++ */ ++ for (pos = 0; pos < outlen; pos += SHA256_HASH_SIZE, iter++) { ++ if (EVP_DigestSignInit(mdctx, NULL, EVP_sha256(), NULL, pkey) != 1) { ++ TRACE_ERROR("EVP_DigestSignInit failed\n"); ++ rc = CKR_FUNCTION_FAILED; ++ goto out; ++ } ++ ++ be_iter[0] = (iter >> 8) & 0xff; ++ be_iter[1] = iter & 0xff; ++ ++ if (EVP_DigestSignUpdate(mdctx, be_iter, sizeof(be_iter)) != 1) { ++ TRACE_ERROR("EVP_DigestSignUpdate failed\n"); ++ rc = CKR_FUNCTION_FAILED; ++ goto out; ++ } ++ if (EVP_DigestSignUpdate(mdctx, (unsigned char *)label, labellen) != 1) { ++ TRACE_ERROR("EVP_DigestSignUpdate failed\n"); ++ rc = CKR_FUNCTION_FAILED; ++ goto out; ++ } ++ if (EVP_DigestSignUpdate(mdctx, be_bitlen, sizeof(be_bitlen)) != 1) { ++ TRACE_ERROR("EVP_DigestSignUpdate failed\n"); ++ rc = CKR_FUNCTION_FAILED; ++ goto out; ++ } ++ ++ /* ++ * HMAC_Final requires the output buffer to fit the whole MAC ++ * value, so we need to use the intermediate buffer for the last ++ * unaligned block ++ */ ++ md_len = SHA256_HASH_SIZE; ++ if (pos + SHA256_HASH_SIZE > outlen) { ++ md_len = sizeof(hmac_out); ++ if (EVP_DigestSignFinal(mdctx, hmac_out, &md_len) != 1) { ++ TRACE_ERROR("EVP_DigestSignFinal failed\n"); ++ rc = CKR_FUNCTION_FAILED; ++ goto out; ++ } ++ memcpy(out + pos, hmac_out, outlen - pos); ++ } else { ++ md_len = outlen - pos; ++ if (EVP_DigestSignFinal(mdctx, out + pos, &md_len) != 1) { ++ TRACE_ERROR("EVP_DigestSignFinal failed\n"); ++ rc = CKR_FUNCTION_FAILED; ++ goto out; ++ } ++ } ++ } ++ ++ rc = CKR_OK; ++ ++out: ++ if (pkey != NULL) ++ EVP_PKEY_free(pkey); ++ if (mdctx != NULL) ++ EVP_MD_CTX_free(mdctx); ++ ++ return rc; ++} ++ +diff --git a/usr/lib/common/mech_rsa.c b/usr/lib/common/mech_rsa.c +index 7bab1a84f..7dc9589a2 100644 +--- a/usr/lib/common/mech_rsa.c ++++ b/usr/lib/common/mech_rsa.c +@@ -289,21 +289,34 @@ static CK_RV rsa_parse_block_type_1(CK_BYTE *in_data, + return rc; + } + ++#define MAX_LEN_GEN_TRIES 128 ++ + static CK_RV rsa_parse_block_type_2(CK_BYTE *in_data, + CK_ULONG in_data_len, + CK_BYTE *out_data, +- CK_ULONG *out_data_len) ++ CK_ULONG *out_data_len, ++ CK_BYTE *kdk, CK_ULONG kdklen) + { +- int i; +- unsigned char *em = NULL; +- unsigned int good, found_zero_byte, mask, equals0; +- int zero_index = 0, msg_index, mlen = -1; +- int out_len = *out_data_len; +- int rsa_size = in_data_len; ++ unsigned int good = 0, found_zero_byte, equals0; ++ size_t zero_index = 0, msg_index; ++ unsigned char *synthetic = NULL; ++ int synthetic_length; ++ uint16_t len_candidate; ++ unsigned char candidate_lengths[MAX_LEN_GEN_TRIES * sizeof(len_candidate)]; ++ uint16_t len_mask; ++ uint16_t max_sep_offset; ++ int synth_msg_index = 0; ++ size_t i, j; ++ CK_RV rc; ++ ++ if (kdk == NULL || kdklen == 0) { ++ TRACE_DEVEL("%s\n", ock_err(ERR_ARGUMENTS_BAD)); ++ return CKR_ARGUMENTS_BAD; ++ } + + /* + * The implementation of this function is copied from OpenSSL's function +- * RSA_padding_check_PKCS1_type_2() in crypto/rsa/rsa_pk1.c ++ * ossl_rsa_padding_check_PKCS1_type_2() in crypto/rsa/rsa_pk1.c + * and is slightly modified to fit to the OpenCryptoki environment. + * + * The OpenSSL code is licensed under the Apache License 2.0. +@@ -328,27 +341,67 @@ static CK_RV rsa_parse_block_type_2(CK_BYTE *in_data, + * PKCS#1 v1.5 decryption. See "PKCS #1 v2.2: RSA Cryptography Standard", + * section 7.2.2. + */ +- if (rsa_size < RSA_PKCS1_PADDING_SIZE) { ++ if (in_data_len < RSA_PKCS1_PADDING_SIZE) { + TRACE_DEVEL("%s\n", ock_err(ERR_FUNCTION_FAILED)); + return CKR_FUNCTION_FAILED; + } + +- em = malloc(rsa_size); +- if (em == NULL) { +- TRACE_DEVEL("%s\n", ock_err(ERR_HOST_MEMORY)); ++ /* Generate a random message to return in case the padding checks fail. */ ++ synthetic = calloc(1, in_data_len); ++ if (synthetic == NULL) { ++ TRACE_ERROR("Failed to allocate synthetic buffer"); + return CKR_HOST_MEMORY; + } + +- /* in_data_len is always equal to rsa_size */ +- memcpy(em, in_data, rsa_size); ++ rc = openssl_specific_rsa_prf(synthetic, in_data_len, "message", 7, ++ kdk, kdklen, in_data_len * 8); ++ if (rc != CKR_OK) ++ goto out; ++ ++ /* decide how long the random message should be */ ++ rc = openssl_specific_rsa_prf(candidate_lengths, ++ sizeof(candidate_lengths), ++ "length", 6, kdk, kdklen, ++ MAX_LEN_GEN_TRIES * ++ sizeof(len_candidate) * 8); ++ if (rc != CKR_OK) ++ goto out; + +- good = constant_time_is_zero(em[0]); +- good &= constant_time_eq(em[1], 2); ++ /* ++ * max message size is the size of the modulus size minus 2 bytes for ++ * version and padding type and a minimum of 8 bytes padding ++ */ ++ len_mask = max_sep_offset = in_data_len - 2 - 8; ++ /* ++ * we want a mask so let's propagate the high bit to all positions less ++ * significant than it ++ */ ++ len_mask |= len_mask >> 1; ++ len_mask |= len_mask >> 2; ++ len_mask |= len_mask >> 4; ++ len_mask |= len_mask >> 8; ++ ++ synthetic_length = 0; ++ for (i = 0; i < MAX_LEN_GEN_TRIES * (int)sizeof(len_candidate); ++ i += sizeof(len_candidate)) { ++ len_candidate = (candidate_lengths[i] << 8) | ++ candidate_lengths[i + 1]; ++ len_candidate &= len_mask; ++ ++ synthetic_length = constant_time_select_int( ++ constant_time_lt(len_candidate, max_sep_offset), ++ len_candidate, synthetic_length); ++ } ++ ++ synth_msg_index = in_data_len - synthetic_length; ++ ++ good = constant_time_is_zero(in_data[0]); ++ good &= constant_time_eq(in_data[1], 2); + + /* scan over padding data */ + found_zero_byte = 0; +- for (i = 2; i < rsa_size; i++) { +- equals0 = constant_time_is_zero(em[i]); ++ for (i = 2; i < in_data_len; i++) { ++ equals0 = constant_time_is_zero(in_data[i]); + + zero_index = constant_time_select_int(~found_zero_byte & equals0, + i, zero_index); +@@ -356,7 +409,7 @@ static CK_RV rsa_parse_block_type_2(CK_BYTE *in_data, + } + + /* +- * PS must be at least 8 bytes long, and it starts two bytes into |em|. ++ * PS must be at least 8 bytes long, and it starts two bytes into |in_data|. + * If we never found a 0-byte, then |zero_index| is 0 and the check + * also fails. + */ +@@ -367,53 +420,41 @@ static CK_RV rsa_parse_block_type_2(CK_BYTE *in_data, + * but in this case we also do not copy the message out. + */ + msg_index = zero_index + 1; +- mlen = rsa_size - msg_index; + + /* +- * For good measure, do this check in constant time as well. ++ * old code returned an error in case the decrypted message wouldn't fit ++ * into the |out_data|, since that would leak information, return the ++ * synthetic message instead + */ +- good &= constant_time_ge(out_len, mlen); ++ good &= constant_time_ge(*out_data_len, in_data_len - msg_index); ++ ++ msg_index = constant_time_select_int(good, msg_index, synth_msg_index); + + /* +- * Move the result in-place by |rsa_size|-RSA_PKCS1_PADDING_SIZE-|mlen| +- * bytes to the left. +- * Then if |good| move |mlen| bytes from |em|+RSA_PKCS1_PADDING_SIZE to +- * |out_data|. Otherwise leave |out_data| unchanged. +- * Copy the memory back in a way that does not reveal the size of +- * the data being copied via a timing side channel. This requires copying +- * parts of the buffer multiple times based on the bits set in the real +- * length. Clear bits do a non-copy with identical access pattern. +- * The loop below has overall complexity of O(N*log(N)). ++ * since at this point the |msg_index| does not provide the signal ++ * indicating if the padding check failed or not, we don't have to worry ++ * about leaking the length of returned message, we still need to ensure ++ * that we read contents of both buffers so that cache accesses don't leak ++ * the value of |good| + */ +- out_len = constant_time_select_int( +- constant_time_lt(rsa_size - RSA_PKCS1_PADDING_SIZE, out_len), +- rsa_size - RSA_PKCS1_PADDING_SIZE, +- out_len); +- for (msg_index = 1; msg_index < rsa_size - RSA_PKCS1_PADDING_SIZE; +- msg_index <<= 1) { +- mask = ~constant_time_eq( +- msg_index & (rsa_size - RSA_PKCS1_PADDING_SIZE - mlen), 0); +- for (i = RSA_PKCS1_PADDING_SIZE; i < rsa_size - msg_index; i++) +- em[i] = constant_time_select_8(mask, em[i + msg_index], em[i]); +- } +- for (i = 0; i < out_len; i++) { +- mask = good & constant_time_lt(i, mlen); +- out_data[i] = constant_time_select_8( +- mask, em[i + RSA_PKCS1_PADDING_SIZE], out_data[i]); +- } ++ for (i = msg_index, j = 0; i < in_data_len && j < *out_data_len; ++ i++, j++) ++ out_data[j] = constant_time_select_8(good, in_data[i], synthetic[i]); + +- OPENSSL_cleanse(em, rsa_size); +- free(em); ++ *out_data_len = j; + +- *out_data_len = constant_time_select_int(good, mlen, 0); ++out: ++ if (synthetic != NULL) ++ free(synthetic); + +- return constant_time_select_int(good, CKR_OK, CKR_ENCRYPTED_DATA_INVALID); ++ return rc; + } + + CK_RV rsa_parse_block(CK_BYTE *in_data, + CK_ULONG in_data_len, + CK_BYTE *out_data, +- CK_ULONG *out_data_len, CK_ULONG type) ++ CK_ULONG *out_data_len, CK_ULONG type, ++ CK_BYTE *kdk, CK_ULONG kdklen) + { + switch (type) { + case PKCS_BT_1: +@@ -421,7 +462,7 @@ CK_RV rsa_parse_block(CK_BYTE *in_data, + out_data, out_data_len); + case PKCS_BT_2: + return rsa_parse_block_type_2(in_data, in_data_len, +- out_data, out_data_len); ++ out_data, out_data_len, kdk, kdklen); + } + + return CKR_ARGUMENTS_BAD; diff --git a/opencryptoki.spec b/opencryptoki.spec index 275d3df..e9917e8 100644 --- a/opencryptoki.spec +++ b/opencryptoki.spec @@ -1,6 +1,6 @@ Name: opencryptoki Version: 3.22.0 -Release: 1 +Release: 2 Summary: PKCS#11 library and tools for Linux License: CPL-1.0 URL: https://github.com/opencryptoki/opencryptoki @@ -8,6 +8,9 @@ Source0: https://github.com/opencryptoki/opencryptoki/archive/v%{vers Patch0001: opencryptoki-3.11.0-lockdir.patch Patch0002: opencryptoki-3.21.0-p11sak.patch +Patch0003: CVE-2024-0914-1.patch +Patch0004: CVE-2024-0914-2.patch +Patch0005: CVE-2024-0914-3.patch BuildRequires: openssl-devel trousers-devel openldap-devel autoconf automake libtool BuildRequires: bison flex systemd libcap-devel expect gcc-c++ @@ -140,6 +143,9 @@ fi %changelog +* Thu Feb 01 2024 wangkai <13474090681@163.com> - 3.22.0-2 +- Fix CVE-2024-0914 + * Fri Sep 22 2023 liyanan - 3.22.0-1 - update to 3.22.0