Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
SUSE:SLE-12-SP1:GA
opensc.33796
opensc-CVE-2023-5992.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File opensc-CVE-2023-5992.patch of Package opensc.33796
From b9e1d344df1f850a9b15bce6294f72c1620d0b45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Veronika=20Hanul=C3=ADkov=C3=A1?= <vhanulik@redhat.com> Date: Mon, 13 Nov 2023 13:54:54 +0100 Subject: [PATCH 1/8] Reimplement removing of PKCS#1 v1.5 padding to be time constant --- src/common/Makefile.am | 6 +- src/common/constant-time.h | 128 ++++++++++++++++++++++++++++++++++++ src/libopensc/internal.h | 4 +- src/libopensc/padding.c | 102 +++++++++++++++++++--------- src/libopensc/pkcs15-sec.c | 5 +- src/minidriver/minidriver.c | 4 +- 6 files changed, 210 insertions(+), 39 deletions(-) create mode 100644 src/common/constant-time.h Index: opensc-0.13.0/src/common/constant-time.h =================================================================== --- /dev/null +++ opensc-0.13.0/src/common/constant-time.h @@ -0,0 +1,128 @@ +/* Original source: https://github.com/openssl/openssl/blob/9890cc42daff5e2d0cad01ac4bf78c391f599a6e/include/internal/constant_time.h */ + +#ifndef CONSTANT_TIME_H +#define CONSTANT_TIME_H + +#include <stdlib.h> +#include <string.h> + +#if !defined(inline) +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#define constant_inline inline +#elif defined(__GNUC__) && __GNUC__ >= 2 +#elif defined(__GNUC__) && __GNUC__ >= 2 +#elif defined(_MSC_VER) +#define constant_inline __inline +#else +#define constant_inline +#endif +#else /* use what caller wants as inline may be from config.h */ +#define constant_inline inline /* inline */ +#endif + +/*- + * The boolean methods return a bitmask of all ones (0xff...f) for true + * and 0 for false. For example, + * if (a < b) { + * c = a; + * } else { + * c = b; + * } + * can be written as + * unsigned int lt = constant_time_lt(a, b); + * c = constant_time_select(lt, a, b); + */ + +static constant_inline unsigned int +value_barrier(unsigned int a) +{ + volatile unsigned int r = a; + return r; +} + +static constant_inline size_t +value_barrier_s(size_t a) +{ + volatile size_t r = a; + return r; +} + +/* MSB */ +static constant_inline size_t +constant_time_msb_s(size_t a) +{ + return 0 - (a >> (sizeof(a) * 8 - 1)); +} + +static constant_inline unsigned int +constant_time_msb(unsigned int a) +{ + return 0 - (a >> (sizeof(a) * 8 - 1)); +} + +/* Select */ +static constant_inline unsigned int +constant_time_select(unsigned int mask, unsigned int a, unsigned int b) +{ + return (value_barrier(mask) & a) | (value_barrier(~mask) & b); +} + +static constant_inline unsigned char +constant_time_select_8(unsigned char mask, unsigned char a, unsigned char b) +{ + return (unsigned char)constant_time_select(mask, a, b); +} + +static constant_inline size_t +constant_time_select_s(size_t mask, size_t a, size_t b) +{ + return (value_barrier_s(mask) & a) | (value_barrier_s(~mask) & b); +} + +/* Zero */ +static constant_inline unsigned int +constant_time_is_zero(unsigned int a) +{ + return constant_time_msb(~a & (a - 1)); +} + +static constant_inline size_t +constant_time_is_zero_s(size_t a) +{ + return constant_time_msb_s(~a & (a - 1)); +} + +/* Comparison*/ +static constant_inline size_t +constant_time_lt_s(size_t a, size_t b) +{ + return constant_time_msb_s(a ^ ((a ^ b) | ((a - b) ^ b))); +} + +static constant_inline unsigned int +constant_time_lt(unsigned int a, unsigned int b) +{ + return constant_time_msb(a ^ ((a ^ b) | ((a - b) ^ b))); +} + +static constant_inline unsigned int +constant_time_ge(unsigned int a, unsigned int b) +{ + return ~constant_time_lt(a, b); +} + +/* Equality*/ + +static constant_inline unsigned int +constant_time_eq(unsigned int a, unsigned int b) +{ + return constant_time_is_zero(a ^ b); +} + +static constant_inline size_t +constant_time_eq_s(size_t a, size_t b) +{ + return constant_time_is_zero_s(a ^ b); +} + +#endif /* CONSTANT_TIME_H */ Index: opensc-0.13.0/src/libopensc/padding.c =================================================================== --- opensc-0.13.0.orig/src/libopensc/padding.c +++ opensc-0.13.0/src/libopensc/padding.c @@ -24,10 +24,13 @@ #include <string.h> #include <stdlib.h> +#include "common/constant-time.h" #include "internal.h" /* TODO doxygen comments */ +#define SC_PKCS1_PADDING_MIN_SIZE 11 + /* * Prefixes for pkcs-v1 signatures */ Index: opensc-0.13.0/src/tests/unittests/strip_pkcs1_2_padding.c =================================================================== --- /dev/null +++ opensc-0.13.0/src/tests/unittests/strip_pkcs1_2_padding.c @@ -0,0 +1,204 @@ +#include "common/compat_strlcpy.c" +#include "libopensc/log.c" +#include "libopensc/padding.c" +#include "torture.h" +#include <cmocka.h> + +static void +torture_long_output_buffer(void **state) +{ + unsigned int n = 14; + unsigned int in_len = 14; + unsigned char in[] = {0x00, 0x02, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x00, + 'm', 's', 'g'}; + unsigned int out_len = 3; + unsigned char *out = malloc(out_len * sizeof(unsigned char)); + unsigned char result_msg[] = {'m', 's', 'g'}; + int r = sc_pkcs1_strip_02_padding_constant_time(NULL, n, in, in_len, out, &out_len); + assert_int_equal(r, 3); + assert_memory_equal(out, result_msg, r); + free(out); +} + +static void +torture_short_output_buffer(void **state) +{ + unsigned int n = 14; + unsigned int in_len = 14; + unsigned char in[] = {0x00, 0x02, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x00, + 'm', 's', 'g'}; + unsigned int out_len = 1; + unsigned char *out = malloc(out_len * sizeof(unsigned char)); + int r = sc_pkcs1_strip_02_padding_constant_time(NULL, n, in, in_len, out, &out_len); + assert_int_equal(r, SC_ERROR_WRONG_PADDING); + free(out); +} + +static void +torture_short_message_correct_padding(void **state) +{ + unsigned int n = 14; + unsigned int in_len = 14; + unsigned char in[] = {0x00, 0x02, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x00, + 'm', 's', 'g'}; + unsigned int out_len = 3; + unsigned char *out = malloc(out_len * sizeof(unsigned char)); + unsigned char result_msg[] = {'m', 's', 'g'}; + int r = sc_pkcs1_strip_02_padding_constant_time(NULL, n, in, in_len, out, &out_len); + assert_int_equal(r, 3); + assert_memory_equal(out, result_msg, r); + free(out); +} + +static void +torture_missing_first_zero(void **state) +{ + unsigned int n = 13; + unsigned int in_len = 13; + unsigned char in[] = {0x02, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x00, + 'm', 's', 'g'}; + unsigned int out_len = 10; + unsigned char *out = malloc(out_len * sizeof(unsigned char)); + int r = sc_pkcs1_strip_02_padding_constant_time(NULL, n, in, in_len, out, &out_len); + assert_int_equal(r, SC_ERROR_WRONG_PADDING); + free(out); +} + +static void +torture_missing_two(void **state) +{ + unsigned int n = 13; + unsigned int in_len = 13; + unsigned char in[] = {0x00, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x00, + 'm', 's', 'g'}; + unsigned int out_len = 10; + unsigned char *out = malloc(out_len * sizeof(unsigned char)); + int r = sc_pkcs1_strip_02_padding_constant_time(NULL, n, in, in_len, out, &out_len); + assert_int_equal(r, SC_ERROR_WRONG_PADDING); + free(out); +} + +static void +torture_short_padding(void **state) +{ + unsigned int n = 13; + unsigned int in_len = 13; + unsigned char in[] = {0x00, 0x02, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x00, + 'm', 's', 'g'}; + unsigned int out_len = 10; + unsigned char *out = malloc(out_len * sizeof(unsigned char)); + int r = sc_pkcs1_strip_02_padding_constant_time(NULL, n, in, in_len, out, &out_len); + assert_int_equal(r, SC_ERROR_WRONG_PADDING); + free(out); +} + +static void +torture_missing_second_zero(void **state) +{ + unsigned int n = 13; + unsigned int in_len = 13; + unsigned char in[] = {0x00, 0x02, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 'm', 's', 'g'}; + unsigned int out_len = 10; + unsigned char *out = malloc(out_len * sizeof(unsigned char)); + int r = sc_pkcs1_strip_02_padding_constant_time(NULL, n, in, in_len, out, &out_len); + assert_int_equal(r, SC_ERROR_WRONG_PADDING); + free(out); +} + +static void +torture_missing_message(void **state) +{ + unsigned int n = 20; + unsigned int in_len = 11; + unsigned char in[] = {0x00, 0x02, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x00}; + unsigned int out_len = 11; + unsigned char *out = malloc(out_len * sizeof(unsigned char)); + int r = sc_pkcs1_strip_02_padding_constant_time(NULL, n, in, in_len, out, &out_len); + assert_int_equal(r, SC_ERROR_WRONG_PADDING); + free(out); +} + +static void +torture_one_byte_message(void **state) +{ + unsigned int n = 12; + unsigned int in_len = 12; + unsigned char in[] = {0x00, 0x02, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x00, + 'm'}; + unsigned int out_len = 1; + unsigned char *out = malloc(out_len * sizeof(unsigned char)); + unsigned char result_msg[] = {'m'}; + int r = sc_pkcs1_strip_02_padding_constant_time(NULL, n, in, in_len, out, &out_len); + assert_int_equal(r, 1); + assert_memory_equal(out, result_msg, r); + free(out); +} + +static void +torture_longer_padding(void **state) +{ + unsigned int n = 26; + unsigned int in_len = 26; + unsigned char in[] = {0x00, 0x02, + 0x0e, 0x38, 0x97, 0x18, 0x16, 0x57, 0x9e, 0x30, 0xb6, 0xa5, 0x78, 0x13, 0x20, 0xca, 0x11, + 0x00, + 0x9d, 0x98, 0x3d, 0xca, 0xa9, 0xa7, 0x11, 0x0a}; + unsigned int out_len = 8; + unsigned char *out = malloc(out_len * sizeof(unsigned char)); + unsigned char result_msg[] = {0x9d, 0x98, 0x3d, 0xca, 0xa9, 0xa7, 0x11, 0x0a}; + int r = sc_pkcs1_strip_02_padding_constant_time(NULL, n, in, in_len, out, &out_len); + assert_int_equal(r, 8); + assert_memory_equal(out, result_msg, r); + free(out); +} + +static void +torture_empty_message(void **state) +{ + unsigned int n = 18; + unsigned int in_len = 18; + unsigned char in[] = {0x00, 0x02, + 0x0e, 0x38, 0x97, 0x18, 0x16, 0x57, 0x9e, 0x30, 0xb6, 0xa5, 0x78, 0x13, 0x20, 0xca, 0x11, + 0x00}; + unsigned int out_len = 8; + unsigned char *out = malloc(out_len * sizeof(unsigned char)); + int r = sc_pkcs1_strip_02_padding_constant_time(NULL, n, in, in_len, out, &out_len); + assert_int_equal(r, 0); + free(out); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(torture_long_output_buffer), + cmocka_unit_test(torture_short_output_buffer), + cmocka_unit_test(torture_short_message_correct_padding), + cmocka_unit_test(torture_missing_first_zero), + cmocka_unit_test(torture_missing_two), + cmocka_unit_test(torture_short_padding), + cmocka_unit_test(torture_missing_second_zero), + cmocka_unit_test(torture_missing_message), + cmocka_unit_test(torture_one_byte_message), + cmocka_unit_test(torture_longer_padding), + cmocka_unit_test(torture_empty_message)}; + return cmocka_run_group_tests(tests, NULL, NULL); +} Index: opensc-0.13.0/src/pkcs11/framework-pkcs15.c =================================================================== --- opensc-0.13.0.orig/src/pkcs11/framework-pkcs15.c +++ opensc-0.13.0/src/pkcs11/framework-pkcs15.c @@ -18,6 +18,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "common/constant-time.h" #include "config.h" #include "libopensc/log.h" #include "libopensc/asn1.h" @@ -3449,6 +3450,7 @@ pkcs15_prkey_decrypt(struct sc_pkcs11_se struct pkcs15_prkey_object *prkey; unsigned char decrypted[256]; /* FIXME: Will not work for keys above 2048 bits */ int buff_too_small, rv, flags = 0, prkey_has_path = 0; + CK_ULONG mask, good, rv_pkcs11; sc_log(context, "Initiating decryption."); @@ -3485,27 +3487,53 @@ pkcs15_prkey_decrypt(struct sc_pkcs11_se rv = sc_pkcs15_decipher(fw_data->p15_card, prkey->prv_p15obj, flags, pEncryptedData, ulEncryptedDataLen, decrypted, sizeof(decrypted)); - if (rv < 0 && !sc_pkcs11_conf.lock_login && !prkey_has_path) + /* skip for PKCS#1 v1.5 padding prevent side channel attack */ + if (!(flags & SC_ALGORITHM_RSA_PAD_PKCS1) && + rv < 0 && !sc_pkcs11_conf.lock_login && !prkey_has_path) if (reselect_app_df(fw_data->p15_card) == SC_SUCCESS) rv = sc_pkcs15_decipher(fw_data->p15_card, prkey->prv_p15obj, flags, pEncryptedData, ulEncryptedDataLen, decrypted, sizeof(decrypted)); sc_unlock(p11card->card); - sc_log(context, "Decryption complete. Result %d.", rv); + sc_log(context, "Decryption complete."); - if (rv < 0) - return sc_to_cryptoki_error(rv, "C_Decrypt"); + /* Handle following code in constant-time + * to prevent Marvin attack for PKCS#1 v1.5 padding. */ - buff_too_small = (*pulDataLen < (CK_ULONG)rv); - *pulDataLen = rv; - if (pData == NULL_PTR) - return CKR_OK; - if (buff_too_small) - return CKR_BUFFER_TOO_SMALL; - memcpy(pData, decrypted, *pulDataLen); + /* only padding error must be handled in constant-time way, + * other error can be returned straight away */ + if ((~constant_time_eq_s(rv, SC_ERROR_WRONG_PADDING) & constant_time_lt_s(sizeof(decrypted), rv))) + return sc_to_cryptoki_error(rv, "C_Decrypt"); - return CKR_OK; + /* check rv for padding error */ + good = ~constant_time_eq_s(rv, SC_ERROR_WRONG_PADDING); + rv_pkcs11 = sc_to_cryptoki_error(SC_ERROR_WRONG_PADDING, "C_Decrypt"); + rv_pkcs11 = constant_time_select_s(good, CKR_OK, rv_pkcs11); + + if (pData == NULL_PTR) { + /* set length only if no error */ + *pulDataLen = constant_time_select_s(good, rv, *pulDataLen); + /* return error only if original rv < 0 */ + return rv_pkcs11; + } + + /* check whether *pulDataLen < rv and set return value for small output buffer */ + mask = good & constant_time_lt_s(*pulDataLen, rv); + rv_pkcs11 = constant_time_select_s(mask, CKR_BUFFER_TOO_SMALL, rv_pkcs11); + good &= ~mask; + + /* move everything from decrypted into out buffer constant-time, if rv is ok */ + for (CK_ULONG i = 0; i < *pulDataLen; i++) { /* iterate over whole pData to not disclose real depadded length */ + CK_ULONG msg_index; + mask = good & constant_time_lt_s(i, sizeof(decrypted)); /* i should be in the bounds of decrypted */ + mask &= constant_time_lt_s(i, constant_time_select_s(good, rv, 0)); /* check that is in bounds of depadded message */ + msg_index = constant_time_select_s(mask, i, 0); + pData[i] = constant_time_select_8(mask, decrypted[msg_index], pData[i]); + } + *pulDataLen = constant_time_select_s(good, rv, *pulDataLen); + /* do not log error code to prevent side channel attack */ + return rv_pkcs11; } Index: opensc-0.13.0/src/pkcs11/mechanism.c =================================================================== --- opensc-0.13.0.orig/src/pkcs11/mechanism.c +++ opensc-0.13.0/src/pkcs11/mechanism.c @@ -9,6 +9,7 @@ #include <stdlib.h> #include <string.h> +#include "common/constant-time.h" #include "sc-pkcs11.h" /* Also used for verification data */ @@ -754,9 +755,12 @@ sc_pkcs11_decr(struct sc_pkcs11_session rv = op->type->decrypt(op, pEncryptedData, ulEncryptedDataLen, pData, pulDataLen); - if (rv != CKR_BUFFER_TOO_SMALL && pData != NULL) + /* terminate session for any return value except CKR_BUFFER_TOO_SMALL, + * perform check in time side-channel free way to prevent Marvin attack */ + if (~constant_time_eq_s(rv, CKR_OK) & ~constant_time_eq_s(rv, CKR_BUFFER_TOO_SMALL)) session_stop_operation(session, SC_PKCS11_OPERATION_DECRYPT); + /* do not log error code to prevent side channel attack */ return rv; } Index: opensc-0.13.0/src/pkcs11/pkcs11-object.c =================================================================== --- opensc-0.13.0.orig/src/pkcs11/pkcs11-object.c +++ opensc-0.13.0/src/pkcs11/pkcs11-object.c @@ -923,7 +923,8 @@ CK_RV C_Decrypt(CK_SESSION_HANDLE hSessi rv = sc_pkcs11_decr(session, pEncryptedData, ulEncryptedDataLen, pData, pulDataLen); - sc_log(context, "C_Decrypt() = %s", lookup_enum ( RV_T, rv )); + /* do not log error code to prevent side channel attack */ + SC_LOG("C_Decrypt()"); sc_pkcs11_unlock(); return rv; } @@ -1281,7 +1282,8 @@ CK_RV C_VerifyUpdate(CK_SESSION_HANDLE h if (rv == CKR_OK) rv = sc_pkcs11_verif_update(session, pPart, ulPartLen); - sc_log(context, "C_VerifyUpdate() = %s", lookup_enum ( RV_T, rv )); + /* do not log error code to prevent side channel attack */ + SC_LOG("C_DecryptUpdate()"); sc_pkcs11_unlock(); return rv; #endif @@ -1305,7 +1307,8 @@ CK_RV C_VerifyFinal(CK_SESSION_HANDLE hS if (rv == CKR_OK) rv = sc_pkcs11_verif_final(session, pSignature, ulSignatureLen); - sc_log(context, "C_VerifyFinal() = %s", lookup_enum ( RV_T, rv )); + /* do not log error code to prevent side channel attack */ + SC_LOG("C_DecryptFinal()"); sc_pkcs11_unlock(); return rv; #endif Index: opensc-0.13.0/src/pkcs11/misc.c =================================================================== --- opensc-0.13.0.orig/src/pkcs11/misc.c +++ opensc-0.13.0/src/pkcs11/misc.c @@ -23,6 +23,7 @@ #include <stdlib.h> #include <string.h> +#include "common/constant-time.h" #include "sc-pkcs11.h" #define DUMP_TEMPLATE_MAX 32
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor