mirror of
git://git.openembedded.org/meta-openembedded
synced 2026-10-01 07:47:05 +00:00
CVE-2023-5992: A vulnerability was found in OpenSC where PKCS#1 encryption padding removal is not implemented as side-channel resistant. This issue may result in the potential leak of private data. Reference: [https://nvd.nist.gov/vuln/detail/CVE-2023-5992] [https://github.com/OpenSC/OpenSC/wiki/CVE-2023-5992] Upstream patches: [https://github.com/OpenSC/OpenSC/pull/2948] [https://github.com/OpenSC/OpenSC/pull/3016] Signed-off-by: Zhang Peng <peng.zhang1.cn@windriver.com> Signed-off-by: Gyorgy Sarvari <skandigraun@gmail.com>
360 lines
12 KiB
Diff
360 lines
12 KiB
Diff
From 9c14d6d996e526ebfda75de7b577255acf7ad86d 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 01/10] Reimplement removing of PKCS#1 v1.5 padding to be time
|
|
constant
|
|
|
|
CVE: CVE-2023-5992
|
|
Upstream-Status: Backport [https://github.com/OpenSC/OpenSC/pull/2948]
|
|
|
|
Signed-off-by: Zhang Peng <peng.zhang1.cn@windriver.com>
|
|
---
|
|
src/common/Makefile.am | 5 +-
|
|
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, 209 insertions(+), 39 deletions(-)
|
|
create mode 100644 src/common/constant-time.h
|
|
|
|
diff --git a/src/common/Makefile.am b/src/common/Makefile.am
|
|
index 83a40e1c2..c4cfff185 100644
|
|
--- a/src/common/Makefile.am
|
|
+++ b/src/common/Makefile.am
|
|
@@ -8,7 +8,7 @@ dist_noinst_DATA = \
|
|
LICENSE.compat_getopt compat_getopt.txt \
|
|
compat_getopt_main.c \
|
|
README.compat_strlcpy compat_strlcpy.3
|
|
-noinst_HEADERS = compat_strlcat.h compat_strlcpy.h compat_strnlen.h compat_getpass.h compat_getopt.h simclist.h libpkcs11.h libscdl.h
|
|
+noinst_HEADERS = compat_strlcat.h compat_strlcpy.h compat_strnlen.h compat_getpass.h compat_getopt.h simclist.h libpkcs11.h libscdl.h constant-time.h
|
|
|
|
AM_CPPFLAGS = -I$(top_srcdir)/src
|
|
|
|
@@ -40,7 +40,8 @@ TIDY_FILES = \
|
|
compat_report_rangecheckfailure.c \
|
|
compat___iob_func.c \
|
|
simclist.c simclist.h \
|
|
- libpkcs11.c libscdl.c
|
|
+ libpkcs11.c libscdl.c \
|
|
+ constant-time.h
|
|
|
|
check-local:
|
|
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 000000000..40c3e500c
|
|
--- /dev/null
|
|
+++ b/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 */
|
|
diff --git a/src/libopensc/internal.h b/src/libopensc/internal.h
|
|
index e7ac63ccf..57568d311 100644
|
|
--- a/src/libopensc/internal.h
|
|
+++ b/src/libopensc/internal.h
|
|
@@ -166,8 +166,8 @@ int _sc_card_add_xeddsa_alg(struct sc_card *card, unsigned int 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);
|
|
|
|
diff --git a/src/libopensc/padding.c b/src/libopensc/padding.c
|
|
index e4940ea2b..3a8b81c3f 100644
|
|
--- a/src/libopensc/padding.c
|
|
+++ b/src/libopensc/padding.c
|
|
@@ -32,10 +32,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
|
|
*/
|
|
@@ -143,45 +146,82 @@ sc_pkcs1_strip_01_padding(struct sc_context *ctx, const u8 *in_dat, size_t in_le
|
|
return SC_SUCCESS;
|
|
}
|
|
|
|
-
|
|
-/* 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, *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);
|
|
- 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);
|
|
|
|
- /* 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);
|
|
+
|
|
+ /*
|
|
+ * We can not check length of input data straight away and still we need to read
|
|
+ * from input even when the input is not as long as needed to keep the time constant.
|
|
+ * If data has wrong size, it is padded by zeroes from left and the following checks
|
|
+ * do not pass.
|
|
+ */
|
|
+ len = data_len;
|
|
+ for (data += len, msg += n, i = 0; i < n; i++) {
|
|
+ mask = ~constant_time_is_zero(len);
|
|
+ len -= 1 & mask;
|
|
+ data -= 1 & mask;
|
|
+ *--msg = *data & mask;
|
|
+ }
|
|
+ // check first byte to be 0x00
|
|
+ good = constant_time_is_zero(msg[0]);
|
|
+ // check second byte to be 0x02
|
|
+ good &= constant_time_eq(msg[1], 2);
|
|
+
|
|
+ // find zero byte after random data in padding
|
|
+ found_zero_byte = 0;
|
|
+ for (i = 2; i < n; i++) {
|
|
+ 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);
|
|
+
|
|
+ // start of the actual message in data
|
|
+ msg_index = zero_index + 1;
|
|
+
|
|
+ // length of message
|
|
+ mlen = data_len - msg_index;
|
|
+
|
|
+ // 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);
|
|
+ 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++)
|
|
+ msg[i] = constant_time_select_8(mask, msg[i + msg_index], msg[i]);
|
|
+ }
|
|
+ // move message into out buffer, if good
|
|
+ for (i = 0; i < *out_len; i++) {
|
|
+ unsigned int msg_index;
|
|
+ // when out is longer than message in data, use some bogus index in msg
|
|
+ mask = good & constant_time_lt(i, mlen);
|
|
+ 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]);
|
|
+ }
|
|
|
|
- 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);
|
|
}
|
|
|
|
/* add/remove DigestInfo prefix */
|
|
diff --git a/src/libopensc/pkcs15-sec.c b/src/libopensc/pkcs15-sec.c
|
|
index b86cb77c3..cea46798a 100644
|
|
--- a/src/libopensc/pkcs15-sec.c
|
|
+++ b/src/libopensc/pkcs15-sec.c
|
|
@@ -308,8 +308,9 @@ int sc_pkcs15_decipher(struct sc_pkcs15_card *p15card,
|
|
|
|
/* Strip any padding */
|
|
if (pad_flags & SC_ALGORITHM_RSA_PAD_PKCS1) {
|
|
- size_t s = r;
|
|
- r = sc_pkcs1_strip_02_padding(ctx, out, s, out, &s);
|
|
+ int s = r;
|
|
+ int key_size = alg_info->key_length;
|
|
+ r = sc_pkcs1_strip_02_padding_constant_time(ctx, key_size / 8, out, s, out, &s);
|
|
LOG_TEST_RET(ctx, r, "Invalid PKCS#1 padding");
|
|
}
|
|
|
|
diff --git a/src/minidriver/minidriver.c b/src/minidriver/minidriver.c
|
|
index 0c089feab..e4d693a09 100644
|
|
--- a/src/minidriver/minidriver.c
|
|
+++ b/src/minidriver/minidriver.c
|
|
@@ -4582,9 +4582,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);
|
|
--
|
|
2.50.0
|
|
|