diff --git a/src/common/Makefile.am b/src/common/Makefile.am
index 5153428dce..9ecbffe8fd 100644
--- a/src/common/Makefile.am
+++ b/src/common/Makefile.am
@@ -8,7 +8,8 @@ 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
 
@@ -43,7 +44,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 0000000000..40c3e500c2
--- /dev/null
+++ b/src/common/constant-time.h
@@ -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
+
+#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);
+}
+
+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 7531260c14..2a98f0e966 100644
--- a/src/libopensc/internal.h
+++ b/src/libopensc/internal.h
@@ -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 ca47733a4e..ddb3061134 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"
 #include "pkcs11/pkcs11.h"
 /* TODO doxygen comments */
 
+#define SC_PKCS1_PADDING_MIN_SIZE 11
+
 /*
  * Prefixes for pkcs-v1 signatures
  */
@@ -144,44 +147,82 @@ sc_pkcs1_strip_01_padding(struct sc_cont
 }
 
 
-/* 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);
 }
 
 #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 <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 = 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_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 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_
 
 	/* 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);
-		LOG_TEST_RET(ctx, r, "Invalid PKCS#1 padding");
+		unsigned int s = r;
+		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,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 f75a3dbaec..632681df63 100644
--- a/src/pkcs11/framework-pkcs15.c
+++ b/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 <stdlib.h>
 #include <string.h>
@@ -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)
+	/* 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), pMechanism);
 
 	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_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;
+	}
+
+	/* 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..d3f0434231 100644
--- a/src/pkcs11/mechanism.c
+++ b/src/pkcs11/mechanism.c
@@ -23,6 +23,7 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include "common/constant-time.h"
 #include "sc-pkcs11.h"
 
 /* Also used for verification data */
@@ -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,
+	 * 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;
@@ -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;
 
+	/* 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..1d893d6181 100644
--- a/src/pkcs11/misc.c
+++ b/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
@@ -174,7 +175,7 @@ CK_RV reset_login_state(struct sc_pkcs11_slot *slot, CK_RV rv)
 			slot->p11card->framework->logout(slot);
 		}
 
-		if (rv == CKR_USER_NOT_LOGGED_IN) {
+		if (constant_time_eq_s(rv, CKR_USER_NOT_LOGGED_IN)) {
 			slot->login_user = -1;
 			pop_all_login_states(slot);
 		}
