From 27031e5fac50d68dce07c2ececa5f9e3ee28bd6b Mon Sep 17 00:00:00 2001 From: eabdullin Date: Mon, 26 Feb 2024 06:54:47 +0000 Subject: [PATCH] import CS opensc-0.23.0-4.el9_3 --- .../opensc-0.23.0-constant-time-pkcs1.5.patch | 739 +++++++++++++----- SPECS/opensc.spec | 6 +- 2 files changed, 549 insertions(+), 196 deletions(-) diff --git a/SOURCES/opensc-0.23.0-constant-time-pkcs1.5.patch b/SOURCES/opensc-0.23.0-constant-time-pkcs1.5.patch index cd5393f..229d6fa 100644 --- a/SOURCES/opensc-0.23.0-constant-time-pkcs1.5.patch +++ b/SOURCES/opensc-0.23.0-constant-time-pkcs1.5.patch @@ -24,48 +24,64 @@ index 5153428dce..9ecbffe8fd 100644 if [ -x "$(CLANGTIDY)" ]; then clang-tidy -config='' --checks='$(TIDY_CHECKS)' -header-filter=.* $(addprefix $(srcdir)/,$(TIDY_FILES)) -- $(TIDY_FLAGS); fi diff --git a/src/common/constant-time.h b/src/common/constant-time.h new file mode 100644 -index 0000000000..f70251f5d6 +index 0000000000..40c3e500c2 --- /dev/null +++ b/src/common/constant-time.h -@@ -0,0 +1,108 @@ +@@ -0,0 +1,134 @@ +/* Original source: https://github.com/openssl/openssl/blob/9890cc42daff5e2d0cad01ac4bf78c391f599a6e/include/internal/constant_time.h */ + +#ifndef CONSTANT_TIME_H -+# define CONSTANT_TIME_H ++#define CONSTANT_TIME_H + -+# include -+# include ++#include ++#include + +#if !defined(inline) -+# if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L -+# define constant_inline inline -+# elif defined(__GNUC__) && __GNUC__>=2 -+# define constant_inline __inline__ -+# 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 */ ++#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 + -+static constant_inline unsigned int value_barrier(unsigned int a) ++/*- ++ * 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; ++ volatile unsigned int r = a; ++ return r; +} + -+static constant_inline size_t value_barrier_s(size_t a) ++static constant_inline size_t ++value_barrier_s(size_t a) +{ -+ volatile size_t r = a; -+ return r; ++ volatile size_t r = a; ++ return r; +} + +/* MSB */ -+static constant_inline size_t constant_time_msb_s(size_t a) ++static constant_inline size_t ++constant_time_msb_s(size_t a) +{ -+ return 0 - (a >> (sizeof(a) * 8 - 1)); ++ return 0 - (a >> (sizeof(a) * 8 - 1)); +} + +static constant_inline unsigned int @@ -87,9 +103,10 @@ index 0000000000..f70251f5d6 + 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) ++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); ++ return (value_barrier_s(mask) & a) | (value_barrier_s(~mask) & b); +} + +/* Zero */ @@ -99,15 +116,17 @@ index 0000000000..f70251f5d6 + return constant_time_msb(~a & (a - 1)); +} + -+static constant_inline size_t constant_time_is_zero_s(size_t a) ++static constant_inline size_t ++constant_time_is_zero_s(size_t a) +{ -+ return constant_time_msb_s(~a & (a - 1)); ++ return constant_time_msb_s(~a & (a - 1)); +} + +/* Comparison*/ -+static constant_inline size_t constant_time_lt_s(size_t a, size_t b) ++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))); ++ return constant_time_msb_s(a ^ ((a ^ b) | ((a - b) ^ b))); +} + +static constant_inline unsigned int @@ -130,34 +149,44 @@ index 0000000000..f70251f5d6 + return constant_time_is_zero(a ^ b); +} + -+static constant_inline size_t constant_time_eq_s(size_t a, size_t b) ++static constant_inline size_t ++constant_time_eq_s(size_t a, size_t b) +{ -+ return constant_time_is_zero_s(a ^ b); ++ return constant_time_is_zero_s(a ^ b); +} + -+#endif /* CONSTANT_TIME_H */ ++static constant_inline unsigned int ++constant_time_eq_i(int a, int b) ++{ ++ return constant_time_eq((unsigned int)a, (unsigned int)b); ++} ++ ++#endif /* CONSTANT_TIME_H */ diff --git a/src/libopensc/internal.h b/src/libopensc/internal.h -index 74014235ac..13eccfa1a4 100644 +index 7531260c14..2a98f0e966 100644 --- a/src/libopensc/internal.h +++ b/src/libopensc/internal.h -@@ -168,6 +168,8 @@ int sc_pkcs1_strip_01_padding(struct sc_context *ctx, const u8 *in_dat, size_t i - u8 *out_dat, size_t *out_len); - int sc_pkcs1_strip_02_padding(struct sc_context *ctx, const u8 *data, size_t len, +@@ -175,8 +175,8 @@ int _sc_card_add_xeddsa_alg(struct sc_card *card, size_t key_length, + + int sc_pkcs1_strip_01_padding(struct sc_context *ctx, const u8 *in_dat, size_t in_len, u8 *out_dat, size_t *out_len); +-int sc_pkcs1_strip_02_padding(struct sc_context *ctx, const u8 *data, size_t len, +- u8 *out_dat, size_t *out_len); +int sc_pkcs1_strip_02_padding_constant_time(sc_context_t *ctx, unsigned int n, const u8 *data, + unsigned int data_len, u8 *out, unsigned int *out_len); int sc_pkcs1_strip_digest_info_prefix(unsigned int *algorithm, const u8 *in_dat, size_t in_len, u8 *out_dat, size_t *out_len); #ifdef ENABLE_OPENSSL diff --git a/src/libopensc/padding.c b/src/libopensc/padding.c -index 2837466992..ceb2a1e219 100644 +index ca47733a4e..ddb3061134 100644 --- a/src/libopensc/padding.c +++ b/src/libopensc/padding.c -@@ -33,9 +33,12 @@ +@@ -32,10 +32,13 @@ + #include #include - #include "internal.h" +#include "common/constant-time.h" + #include "internal.h" #include "pkcs11/pkcs11.h" /* TODO doxygen comments */ @@ -166,24 +195,36 @@ index 2837466992..ceb2a1e219 100644 /* * Prefixes for pkcs-v1 signatures */ -@@ -184,6 +187,84 @@ sc_pkcs1_strip_02_padding(sc_context_t *ctx, const u8 *data, size_t len, u8 *out - LOG_FUNC_RETURN(ctx, len - n); +@@ -144,44 +147,82 @@ sc_pkcs1_strip_01_padding(struct sc_cont } -+/* Original source: https://github.com/openssl/openssl/blob/9890cc42daff5e2d0cad01ac4bf78c391f599a6e/crypto/rsa/rsa_pk1.c#L171 */ -+int + +-/* remove pkcs1 BT02 padding (adding BT02 padding is currently not +- * needed/implemented) */ ++/* Remove pkcs1 BT02 padding (adding BT02 padding is currently not ++ * needed/implemented) in constant-time. ++ * Original source: https://github.com/openssl/openssl/blob/9890cc42daff5e2d0cad01ac4bf78c391f599a6e/crypto/rsa/rsa_pk1.c#L171 */ + int +-sc_pkcs1_strip_02_padding(sc_context_t *ctx, const u8 *data, size_t len, u8 *out, size_t *out_len) +sc_pkcs1_strip_02_padding_constant_time(sc_context_t *ctx, unsigned int n, const u8 *data, unsigned int data_len, u8 *out, unsigned int *out_len) -+{ + { +- unsigned int n = 0; +- + unsigned int i = 0; -+ u8 *msg = NULL; ++ u8 *msg, *msg_orig = NULL; + unsigned int good, found_zero_byte, mask; + unsigned int zero_index = 0, msg_index, mlen = -1, len = 0; -+ LOG_FUNC_CALLED(ctx); + LOG_FUNC_CALLED(ctx); +- if (data == NULL || len < 3) + + if (data == NULL || data_len <= 0 || data_len > n || n < SC_PKCS1_PADDING_MIN_SIZE) -+ LOG_FUNC_RETURN(ctx, SC_ERROR_INTERNAL); -+ -+ msg = calloc(n, sizeof(u8)); + LOG_FUNC_RETURN(ctx, SC_ERROR_INTERNAL); + +- /* skip leading zero byte */ +- if (*data == 0) { +- data++; +- len--; ++ msg = msg_orig = calloc(n, sizeof(u8)); + if (msg == NULL) + LOG_FUNC_RETURN(ctx, SC_ERROR_INTERNAL); + @@ -211,8 +252,25 @@ index 2837466992..ceb2a1e219 100644 + unsigned int equals0 = constant_time_is_zero(msg[i]); + zero_index = constant_time_select(~found_zero_byte & equals0, i, zero_index); + found_zero_byte |= equals0; -+ } -+ + } +- if (data[0] != 0x02) +- LOG_FUNC_RETURN(ctx, SC_ERROR_WRONG_PADDING); +- /* skip over padding bytes */ +- for (n = 1; n < len && data[n]; n++) +- ; +- /* Must be at least 8 pad bytes */ +- if (n >= len || n < 9) +- LOG_FUNC_RETURN(ctx, SC_ERROR_WRONG_PADDING); +- n++; +- if (out == NULL) +- /* just check the padding */ +- LOG_FUNC_RETURN(ctx, SC_SUCCESS); + +- /* Now move decrypted contents to head of buffer */ +- if (*out_len < len - n) +- LOG_FUNC_RETURN(ctx, SC_ERROR_INTERNAL); +- *out_len = len - n; +- memmove(out, data + n, *out_len); + // zero_index stands for index of last found zero + good &= constant_time_ge(zero_index, 2 + 8); + @@ -222,14 +280,12 @@ index 2837466992..ceb2a1e219 100644 + // length of message + mlen = data_len - msg_index; + -+ // check that there is a message after padding -+ good &= constant_time_ge(mlen, 1); + // check that message fits into out buffer + good &= constant_time_ge(*out_len, mlen); + + // move the result in-place by |num|-SC_PKCS1_PADDING_MIN_SIZE-|mlen| bytes to the left. + *out_len = constant_time_select(constant_time_lt(n - SC_PKCS1_PADDING_MIN_SIZE, *out_len), -+ n - SC_PKCS1_PADDING_MIN_SIZE, *out_len); ++ n - SC_PKCS1_PADDING_MIN_SIZE, *out_len); + for (msg_index = 1; msg_index < n - SC_PKCS1_PADDING_MIN_SIZE; msg_index <<= 1) { + mask = ~constant_time_eq(msg_index & (n - SC_PKCS1_PADDING_MIN_SIZE - mlen), 0); + for (i = SC_PKCS1_PADDING_MIN_SIZE; i < n - msg_index; i++) @@ -243,19 +299,291 @@ index 2837466992..ceb2a1e219 100644 + msg_index = constant_time_select(mask, i + SC_PKCS1_PADDING_MIN_SIZE, 0); // to now overflow msg buffer + out[i] = constant_time_select_8(mask, msg[msg_index], out[i]); + } -+ -+ free(msg); + +- sc_log(ctx, "stripped output(%"SC_FORMAT_LEN_SIZE_T"u): %s", len - n, +- sc_dump_hex(out, len - n)); +- LOG_FUNC_RETURN(ctx, len - n); ++ free(msg_orig); + return constant_time_select(good, mlen, SC_ERROR_WRONG_PADDING); + } + + #ifdef ENABLE_OPENSSL +diff --git a/src/minidriver/minidriver.c b/src/minidriver/minidriver.c +index 3391994abe..5248d9758a 100644 +--- a/src/minidriver/minidriver.c ++++ b/src/minidriver/minidriver.c +@@ -4653,9 +4653,9 @@ DWORD WINAPI CardRSADecrypt(__in PCARD_DATA pCardData, + "sc_pkcs15_decipher: DECRYPT-INFO dwVersion=%lu\n", + (unsigned long)pInfo->dwVersion); + if (pInfo->dwPaddingType == CARD_PADDING_PKCS1) { +- size_t temp = pInfo->cbData; ++ unsigned int temp = pInfo->cbData; + logprintf(pCardData, 2, "sc_pkcs15_decipher: stripping PKCS1 padding\n"); +- r = sc_pkcs1_strip_02_padding(vs->ctx, pbuf2, pInfo->cbData, pbuf2, &temp); ++ r = sc_pkcs1_strip_02_padding_constant_time(vs->ctx, prkey_info->modulus_length / 8, pbuf2, pInfo->cbData, pbuf2, &temp); + pInfo->cbData = (DWORD) temp; + if (r < 0) { + logprintf(pCardData, 2, "Cannot strip PKCS1 padding: %i\n", r); + +diff --git a/src/tests/unittests/Makefile.am b/src/tests/unittests/Makefile.am +index 7019ca7ba8..4c73911e48 100644 +--- a/src/tests/unittests/Makefile.am ++++ b/src/tests/unittests/Makefile.am +@@ -6,8 +6,10 @@ include $(top_srcdir)/aminclude_static.a + clean-local: code-coverage-clean + distclean-local: code-coverage-dist-clean + +-noinst_PROGRAMS = asn1 simpletlv cachedir pkcs15filter openpgp-tool hextobin decode_ecdsa_signature +-TESTS = asn1 simpletlv cachedir pkcs15filter openpgp-tool hextobin decode_ecdsa_signature ++noinst_PROGRAMS = asn1 simpletlv cachedir pkcs15filter openpgp-tool hextobin \ ++ decode_ecdsa_signature strip_pkcs1_2_padding ++TESTS = asn1 simpletlv cachedir pkcs15filter openpgp-tool hextobin \ ++ decode_ecdsa_signature strip_pkcs1_2_padding + + noinst_HEADERS = torture.h + +@@ -28,6 +30,7 @@ pkcs15filter_SOURCES = pkcs15-emulator-f + openpgp_tool_SOURCES = openpgp-tool.c $(top_builddir)/src/tools/openpgp-tool-helpers.c + hextobin_SOURCES = hextobin.c + decode_ecdsa_signature_SOURCES = decode_ecdsa_signature.c ++strip_pkcs1_2_paddingSOURCES = strip_pkcs1_2_padding.c + + if ENABLE_ZLIB + noinst_PROGRAMS += compression +diff --git a/src/tests/unittests/Makefile.mak b/src/tests/unittests/Makefile.mak +index 2607546f57..6284b51af9 100644 +--- a/src/tests/unittests/Makefile.mak ++++ b/src/tests/unittests/Makefile.mak +@@ -1,10 +1,11 @@ + TOPDIR = ..\..\.. + +-TARGETS = asn1 compression pkcs15filter ++TARGETS = asn1 compression pkcs15filter strip_pkcs1_2_padding + + OBJECTS = asn1.obj \ + compression.obj \ +- pkcs15-emulator-filter.obj ++ pkcs15-emulator-filter.obj \ ++ strip_pkcs1_2_padding.obj \ + $(TOPDIR)\win32\versioninfo.res + + all: $(TARGETS) +diff --git a/src/tests/unittests/strip_pkcs1_2_padding.c b/src/tests/unittests/strip_pkcs1_2_padding.c +new file mode 100644 +index 0000000000..f9561b936d +--- /dev/null ++++ b/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 ++ ++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 = calloc(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); +} + - #ifdef ENABLE_OPENSSL - static int mgf1(u8 *mask, size_t len, u8 *seed, size_t seedLen, const EVP_MD *dgst) - { ++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 = calloc(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 = calloc(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 = calloc(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 = calloc(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 = calloc(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 = calloc(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 = calloc(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 = calloc(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 = calloc(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 = calloc(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); ++} + diff --git a/src/libopensc/pkcs15-sec.c b/src/libopensc/pkcs15-sec.c -index 7c3a39432f..b5e492fe25 100644 +index a019af460f..f7ee819d65 100644 --- a/src/libopensc/pkcs15-sec.c +++ b/src/libopensc/pkcs15-sec.c -@@ -308,9 +308,10 @@ int sc_pkcs15_decipher(struct sc_pkcs15_card *p15card, +@@ -308,9 +308,10 @@ int sc_pkcs15_decipher(struct sc_pkcs15_ /* Strip any padding */ if (pad_flags & SC_ALGORITHM_RSA_PAD_PKCS1) { @@ -263,39 +591,52 @@ index 7c3a39432f..b5e492fe25 100644 - r = sc_pkcs1_strip_02_padding(ctx, out, s, out, &s); - LOG_TEST_RET(ctx, r, "Invalid PKCS#1 padding"); + unsigned int s = r; -+ unsigned int key_size = alg_info->key_length; ++ unsigned int key_size = (unsigned int)alg_info->key_length; + r = sc_pkcs1_strip_02_padding_constant_time(ctx, key_size / 8, out, s, out, &s); + /* for keeping PKCS#1 v1.5 depadding constant-time, do not log error here */ } #ifdef ENABLE_OPENSSL if (pad_flags & SC_ALGORITHM_RSA_PAD_OAEP) -@@ -332,7 +333,7 @@ int sc_pkcs15_decipher(struct sc_pkcs15_card *p15card, +@@ -332,7 +333,8 @@ int sc_pkcs15_decipher(struct sc_pkcs15_ LOG_TEST_RET(ctx, r, "Invalid OAEP padding"); } #endif - LOG_FUNC_RETURN(ctx, r); ++ /* do not log error code to prevent side channel attack */ + return r; } /* derive one key from another. RSA can use decipher, so this is for only ECDH + diff --git a/src/pkcs11/framework-pkcs15.c b/src/pkcs11/framework-pkcs15.c -index 809cd72d93..9c75759a08 100644 +index f75a3dbaec..632681df63 100644 --- a/src/pkcs11/framework-pkcs15.c +++ b/src/pkcs11/framework-pkcs15.c -@@ -28,6 +28,7 @@ - #include "libopensc/cardctl.h" - #include "ui/notify.h" - #include "common/compat_strnlen.h" +@@ -18,6 +18,7 @@ + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "common/constant-time.h" - #ifdef ENABLE_OPENSSL - #include - #include -@@ -4603,15 +4604,51 @@ pkcs15_prkey_decrypt(struct sc_pkcs11_session *session, void *obj, + #include "config.h" + #include + #include +@@ -4395,7 +4396,8 @@ pkcs15_prkey_decrypt(struct sc_pkcs11_se + struct pkcs15_fw_data *fw_data = NULL; + struct pkcs15_prkey_object *prkey; + unsigned char decrypted[512]; /* FIXME: Will not work for keys above 4096 bits */ +- int buff_too_small, rv, flags = 0, prkey_has_path = 0; ++ int rv, flags = 0, prkey_has_path = 0; ++ CK_ULONG mask, good, rv_pkcs11; + + if (pulDataLen == NULL) { + /* This is call from the C_DecyptInit function */ +@@ -4484,27 +4486,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), pMechanism); - if (rv < 0 && !sc_pkcs11_conf.lock_login && !prkey_has_path) -+ if (!((flags & SC_ALGORITHM_RSA_PAD_PKCS1) && constant_time_eq_s(rv, SC_ERROR_WRONG_PADDING)) && ++ /* 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, @@ -304,102 +645,174 @@ index 809cd72d93..9c75759a08 100644 sc_unlock(p11card->card); - sc_log(context, "Decryption complete. Result %d.", rv); -+ /* Handle buffer after PKCS#1 v1.5 depadding constant-time */ -+ if (flags & SC_ALGORITHM_RSA_PAD_PKCS1) { -+ CK_ULONG mask, good, rv_pkcs11; -+ -+ sc_log(context, "Decryption complete."); -+ /* only padding error must be handled in constant-time way */ -+ 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"); -+ -+ /* check rv for error */ -+ good = ~constant_time_eq_s(rv, SC_ERROR_WRONG_PADDING); -+ rv_pkcs11 = constant_time_select_s(good, CKR_OK, SC_ERROR_WRONG_PADDING); -+ if (pData == NULL_PTR) { -+ /* set length only if rv good */ -+ *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 accordingly */ -+ mask = good & constant_time_lt_s(*pulDataLen, rv); -+ rv_pkcs11 = constant_time_select_s(mask, CKR_BUFFER_TOO_SMALL, rv_pkcs11); -+ good &= ~mask; ++ sc_log(context, "Decryption complete."); -+ /* move everything from decrypted into out buffer, 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]); -+ } +- 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_i(rv, SC_ERROR_WRONG_PADDING) & constant_time_lt_s(sizeof(decrypted), (size_t)rv))) ++ return sc_to_cryptoki_error(rv, "C_Decrypt"); + +- return CKR_OK; ++ /* check rv for padding error */ ++ good = ~constant_time_eq_i(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; + } + -+ sc_log(context, "Decryption complete. Result %d.", rv); - if (rv < 0) - return sc_to_cryptoki_error(rv, "C_Decrypt"); - -@@ -4622,7 +4659,6 @@ pkcs15_prkey_decrypt(struct sc_pkcs11_session *session, void *obj, - if (buff_too_small) - return CKR_BUFFER_TOO_SMALL; - memcpy(pData, decrypted, *pulDataLen); -- - return CKR_OK; ++ /* 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; } + + diff --git a/src/pkcs11/mechanism.c b/src/pkcs11/mechanism.c -index 03495265a4..95d80249bb 100644 +index 03495265a4..d3f0434231 100644 --- a/src/pkcs11/mechanism.c +++ b/src/pkcs11/mechanism.c -@@ -25,6 +25,7 @@ +@@ -23,6 +23,7 @@ + #include #include - #include "sc-pkcs11.h" +#include "common/constant-time.h" + #include "sc-pkcs11.h" /* Also used for verification data */ - struct hash_signature_info { -@@ -1093,7 +1094,8 @@ sc_pkcs11_decr(struct sc_pkcs11_session *session, +@@ -1089,7 +1090,9 @@ 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 */ ++ /* 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_BUFFER_TOO_SMALL) && pData != NULL) session_stop_operation(session, SC_PKCS11_OPERATION_DECRYPT); return rv; -@@ -1536,6 +1538,13 @@ sc_pkcs11_decrypt(sc_pkcs11_operation_t *operation, +@@ -1110,10 +1113,12 @@ sc_pkcs11_decr_update(struct sc_pkcs11_s + rv = op->type->decrypt_update(op, pEncryptedData, ulEncryptedDataLen, + pData, pulDataLen); + +- /* terminate session for any error except CKR_BUFFER_TOO_SMALL */ +- if (rv != CKR_OK && rv != CKR_BUFFER_TOO_SMALL) ++ /* 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); +- LOG_FUNC_RETURN(context, (int)rv); ++ /* do not log error code to prevent side channel attack */ ++ return rv; + } + + CK_RV +@@ -1530,6 +1535,10 @@ sc_pkcs11_decrypt(sc_pkcs11_operation_t if (pulDataLen) *pulDataLen = ulDataLen; -+ /* No need for DecryptFinalize */ -+ if (((CK_MECHANISM_PTR) &operation->mechanism)->mechanism == CKM_RSA_PKCS) { -+ if (pulDataLen) -+ *pulDataLen = ulDataLen; ++ /* Skip DecryptFinalize for PKCS#1 v1.5 padding to prevent time side-channel leakage */ ++ if (((CK_MECHANISM_PTR)&operation->mechanism)->mechanism == CKM_RSA_PKCS) + return rv; -+ } + if (rv != CKR_OK) return rv; + + +diff --git a/src/pkcs11/pkcs11-object.c b/src/pkcs11/pkcs11-object.c +index f04c0b4c56..b023911213 100644 +--- a/src/pkcs11/pkcs11-object.c ++++ b/src/pkcs11/pkcs11-object.c +@@ -1034,7 +1034,8 @@ C_Decrypt(CK_SESSION_HANDLE hSession, /* the session's handle */ + rv = reset_login_state(session->slot, rv); + } + +- SC_LOG_RV("C_Decrypt() = %s", rv); ++ /* do not log error code to prevent side channel attack */ ++ SC_LOG("C_Decrypt()"); + sc_pkcs11_unlock(); + return rv; + } +@@ -1058,7 +1059,8 @@ C_DecryptUpdate(CK_SESSION_HANDLE hSession, /* the session's handle */ + rv = sc_pkcs11_decr_update(session, pEncryptedPart, ulEncryptedPartLen, + pPart, pulPartLen); + +- SC_LOG_RV("C_DecryptUpdate() = %s", rv); ++ /* do not log error code to prevent side channel attack */ ++ SC_LOG("C_DecryptUpdate()"); + sc_pkcs11_unlock(); + return rv; + } +@@ -1086,7 +1088,8 @@ C_DecryptFinal(CK_SESSION_HANDLE hSession, /* the session's handle */ + rv = reset_login_state(session->slot, rv); + } + +- SC_LOG_RV("C_DecryptFinal() = %s", rv); ++ /* do not log error code to prevent side channel attack */ ++ SC_LOG("C_DecryptFinal()"); + sc_pkcs11_unlock(); + return rv; + } +diff --git a/src/pkcs11/sc-pkcs11.h b/src/pkcs11/sc-pkcs11.h +index 66dfcdde67..510017ed2a 100644 +--- a/src/pkcs11/sc-pkcs11.h ++++ b/src/pkcs11/sc-pkcs11.h +@@ -246,6 +246,11 @@ do {\ + }\ + } while(0) + ++#define SC_LOG(fmt) \ ++ do { \ ++ sc_log(context, (fmt)); \ ++ } while (0) ++ + /* Debug virtual slots. S is slot to be highlighted or NULL + * C is a comment format string and args It will be preceded by "VSS " */ + #define DEBUG_VSS(S, ...) do { sc_log(context,"VSS " __VA_ARGS__); _debug_virtual_slots(S); } while (0) + + diff --git a/src/pkcs11/misc.c b/src/pkcs11/misc.c -index 5ca1176b1d..2893b2bf3e 100644 +index 5ca1176b1d..1d893d6181 100644 --- a/src/pkcs11/misc.c +++ b/src/pkcs11/misc.c -@@ -24,6 +24,7 @@ +@@ -23,6 +23,7 @@ + #include #include - #include "sc-pkcs11.h" +#include "common/constant-time.h" + #include "sc-pkcs11.h" #define DUMP_TEMPLATE_MAX 32 - @@ -174,7 +175,7 @@ CK_RV reset_login_state(struct sc_pkcs11_slot *slot, CK_RV rv) slot->p11card->framework->logout(slot); } @@ -409,67 +822,3 @@ index 5ca1176b1d..2893b2bf3e 100644 slot->login_user = -1; pop_all_login_states(slot); } -diff --git a/src/pkcs11/pkcs11-object.c b/src/pkcs11/pkcs11-object.c -index f04c0b4c56..93cc319c24 100644 ---- a/src/pkcs11/pkcs11-object.c -+++ b/src/pkcs11/pkcs11-object.c -@@ -1034,7 +1034,7 @@ C_Decrypt(CK_SESSION_HANDLE hSession, /* the session's handle */ - rv = reset_login_state(session->slot, rv); - } - -- SC_LOG_RV("C_Decrypt() = %s", rv); -+ SC_LOG("C_Decrypt()"); - sc_pkcs11_unlock(); - return rv; - } -diff --git a/src/pkcs11/sc-pkcs11.h b/src/pkcs11/sc-pkcs11.h -index 66dfcdde67..7239b4c5b8 100644 ---- a/src/pkcs11/sc-pkcs11.h -+++ b/src/pkcs11/sc-pkcs11.h -@@ -246,6 +246,11 @@ do {\ - }\ - } while(0) - -+#define SC_LOG(fmt)\ -+do {\ -+ sc_log(context, (fmt));\ -+} while(0) -+ - /* Debug virtual slots. S is slot to be highlighted or NULL - * C is a comment format string and args It will be preceded by "VSS " */ - #define DEBUG_VSS(S, ...) do { sc_log(context,"VSS " __VA_ARGS__); _debug_virtual_slots(S); } while (0) - -diff --git a/src/libopensc/padding.c b/src/libopensc/padding.c -index ceb2a1e21..c2cc58d47 100644 ---- a/src/libopensc/padding.c -+++ b/src/libopensc/padding.c -@@ -192,7 +192,7 @@ int - sc_pkcs1_strip_02_padding_constant_time(sc_context_t *ctx, unsigned int n, const u8 *data, unsigned int data_len, u8 *out, unsigned int *out_len) - { - unsigned int i = 0; -- u8 *msg = NULL; -+ u8 *msg, *msg_orig = NULL; - unsigned int good, found_zero_byte, mask; - unsigned int zero_index = 0, msg_index, mlen = -1, len = 0; - LOG_FUNC_CALLED(ctx); -@@ -200,7 +200,7 @@ sc_pkcs1_strip_02_padding_constant_time(sc_context_t *ctx, unsigned int n, const - if (data == NULL || data_len <= 0 || data_len > n || n < SC_PKCS1_PADDING_MIN_SIZE) - LOG_FUNC_RETURN(ctx, SC_ERROR_INTERNAL); - -- msg = calloc(n, sizeof(u8)); -+ msg = msg_orig = calloc(n, sizeof(u8)); - if (msg == NULL) - LOG_FUNC_RETURN(ctx, SC_ERROR_INTERNAL); - -@@ -261,7 +261,7 @@ sc_pkcs1_strip_02_padding_constant_time(sc_context_t *ctx, unsigned int n, const - out[i] = constant_time_select_8(mask, msg[msg_index], out[i]); - } - -- free(msg); -+ free(msg_orig); - return constant_time_select(good, mlen, SC_ERROR_WRONG_PADDING); - } - --- -2.43.0 - diff --git a/SPECS/opensc.spec b/SPECS/opensc.spec index 05c4eef..0c3dfc3 100644 --- a/SPECS/opensc.spec +++ b/SPECS/opensc.spec @@ -3,7 +3,7 @@ Name: opensc Version: 0.23.0 -Release: 3%{?dist} +Release: 4%{?dist} Summary: Smart card library and applications License: LGPLv2+ @@ -54,6 +54,7 @@ Patch15: %{name}-0.23.0-pkcs15init.patch # https://github.com/OpenSC/OpenSC/commit/f1993dc4e0b33050b8f72a3558ee88b24c4063b2 Patch16: %{name}-0.23.0-myeid-sym.patch # https://github.com/OpenSC/OpenSC/pull/2948 +# https://github.com/OpenSC/OpenSC/pull/3016 Patch17: %{name}-0.23.0-constant-time-pkcs1.5.patch BuildRequires: make @@ -244,6 +245,9 @@ rm %{buildroot}%{_mandir}/man1/opensc-notify.1* %changelog +* Thu Feb 08 2024 Veronika Hanulikova - 0.23.0-4 +- Fix CVE-2023-5992: Side-channel leaks while stripping encryption PKCS#1.5 padding + * Thu Nov 30 2023 Jakub Jelen - 0.23.0-3 - Fix file caching with different offsets (RHEL-4079) - Fix CVE-2023-40660: Potential PIN bypass