parent
b8bf95e7a8
commit
4a83bb5c1a
@ -0,0 +1,153 @@
|
|||||||
|
commit 47c55113f81794408a0afda2e19e1a5aa40d2212
|
||||||
|
Author: Ingo Franzki <ifranzki@linux.ibm.com>
|
||||||
|
Date: Tue Dec 12 17:16:56 2023 +0100
|
||||||
|
|
||||||
|
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 <ifranzki@linux.ibm.com>
|
||||||
|
|
||||||
|
diff --git a/usr/lib/common/mech_rsa.c b/usr/lib/common/mech_rsa.c
|
||||||
|
index 326c5795..7bab1a84 100644
|
||||||
|
--- a/usr/lib/common/mech_rsa.c
|
||||||
|
+++ b/usr/lib/common/mech_rsa.c
|
||||||
|
@@ -29,6 +29,7 @@
|
||||||
|
#include "constant_time.h"
|
||||||
|
|
||||||
|
#include <openssl/crypto.h>
|
||||||
|
+#include <openssl/rsa.h>
|
||||||
|
|
||||||
|
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,
|
@ -0,0 +1,387 @@
|
|||||||
|
commit 2fb51b9e4d390f889c109e1765c3284b5d6f5fb8
|
||||||
|
Author: Ingo Franzki <ifranzki@linux.ibm.com>
|
||||||
|
Date: Fri Jan 12 09:36:27 2024 +0100
|
||||||
|
|
||||||
|
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 <ifranzki@linux.ibm.com>
|
||||||
|
|
||||||
|
diff --git a/usr/lib/common/new_host.c b/usr/lib/common/new_host.c
|
||||||
|
index 8a1e8723..bbb0f601 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 df1f68f9..42793955 100644
|
||||||
|
--- a/usr/lib/ep11_stdll/ep11_specific.c
|
||||||
|
+++ b/usr/lib/ep11_stdll/ep11_specific.c
|
||||||
|
@@ -10777,10 +10777,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:
|
||||||
|
@@ -10836,10 +10838,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:
|
||||||
|
@@ -10901,10 +10905,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 ce18f729..f7ee0546 100644
|
||||||
|
--- a/usr/lib/ep11_stdll/new_host.c
|
||||||
|
+++ b/usr/lib/ep11_stdll/new_host.c
|
||||||
|
@@ -37,6 +37,7 @@
|
||||||
|
#include "slotmgr.h"
|
||||||
|
#include "attributes.h"
|
||||||
|
#include "ep11_specific.h"
|
||||||
|
+#include "constant_time.h"
|
||||||
|
|
||||||
|
#include "../api/apiproto.h"
|
||||||
|
#include "../api/policy.h"
|
||||||
|
@@ -2465,6 +2466,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));
|
||||||
|
@@ -2512,17 +2514,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);
|
||||||
|
}
|
||||||
|
@@ -2544,6 +2558,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));
|
||||||
|
@@ -2595,11 +2610,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);
|
||||||
|
}
|
||||||
|
@@ -2621,6 +2643,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));
|
||||||
|
@@ -2669,10 +2692,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 115fd40b..192fe128 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);
|
||||||
|
}
|
@ -0,0 +1,737 @@
|
|||||||
|
commit 034d70ec4cfde81ea71cf8acbe9097fa15e49a02
|
||||||
|
Author: Ingo Franzki <ifranzki@linux.ibm.com>
|
||||||
|
Date: Mon Jan 15 12:53:37 2024 +0100
|
||||||
|
|
||||||
|
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 <ifranzki@linux.ibm.com>
|
||||||
|
|
||||||
|
diff --git a/COPYRIGHTS b/COPYRIGHTS
|
||||||
|
index 2bb3dffe..21b6b702 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 a14542fa..a6ee4c33 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);
|
||||||
|
|
||||||
|
@@ -3179,6 +3180,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 9983fcb3..da515289 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 @@ done:
|
||||||
|
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 7bab1a84..7dc9589a 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;
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,31 @@
|
|||||||
|
commit c859ed40828bf808e83a3f437c2e34c9c843a4c3
|
||||||
|
Author: Ingo Franzki <ifranzki@linux.ibm.com>
|
||||||
|
Date: Fri Feb 9 14:07:34 2024 +0100
|
||||||
|
|
||||||
|
COMMON: Fix implicit rejection with RSA keys with empty CKA_PRIVATE_EXPONENT
|
||||||
|
|
||||||
|
An RSA key object that has no CKA_PRIVATE_EXPONENT may either don't have that
|
||||||
|
attribute at all, or may have an empty CKA_PRIVATE_EXPONENT attribute.
|
||||||
|
Both situations should be handed the same, and the private exponent of the
|
||||||
|
key needs to be calculated from the other key components.
|
||||||
|
|
||||||
|
Note that RSA key objects generated with a current soft or ICA token will
|
||||||
|
always have a valid CKA_PRIVATE_EXPONENT attribute, since this is provided
|
||||||
|
during key generation.
|
||||||
|
|
||||||
|
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
|
||||||
|
|
||||||
|
diff --git a/usr/lib/common/mech_openssl.c b/usr/lib/common/mech_openssl.c
|
||||||
|
index da515289..14c82e2d 100644
|
||||||
|
--- a/usr/lib/common/mech_openssl.c
|
||||||
|
+++ b/usr/lib/common/mech_openssl.c
|
||||||
|
@@ -5160,7 +5160,8 @@ CK_RV openssl_specific_rsa_derive_kdk(STDLL_TokData_t *tokdata, OBJECT *key_obj,
|
||||||
|
|
||||||
|
rc = template_attribute_get_non_empty(key_obj->template,
|
||||||
|
CKA_PRIVATE_EXPONENT, &priv_exp_attr);
|
||||||
|
- if (rc != CKR_OK && rc != CKR_TEMPLATE_INCOMPLETE) {
|
||||||
|
+ if (rc != CKR_OK && rc != CKR_TEMPLATE_INCOMPLETE &&
|
||||||
|
+ rc != CKR_ATTRIBUTE_VALUE_INVALID) {
|
||||||
|
TRACE_ERROR("Failed to get CKA_PRIVATE_EXPONENT\n");
|
||||||
|
goto out;
|
||||||
|
}
|
Loading…
Reference in new issue