Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Constant time RSA PKCS#1 v1.5 depadding #2948

Merged
merged 8 commits into from
Feb 5, 2024

Conversation

xhanulik
Copy link
Contributor

This PR implements constant-time PKCS#1 v1.5 depadding and return value handling after RSA decryption. The returned value and depadded message are handled in the same way, both for valid and invalid padding, not to disclose the result of the depadding operation in the time side-channel.

Checklist
  • New files have a LGPL 2.1 license statement
  • PKCS#11 module is tested
  • Windows minidriver is tested
  • macOS tokend is tested

Copy link
Member

@frankmorgner frankmorgner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can see that it is very hard to avoid time leakage throughout the whole software stack. It is not simply a matter of implementing sc_pkcs1_strip_02_padding_constant_time() and being done with it. Obviously, some files needed to be touched that seem unrelated at first glance. I have some doubts that we can keep up this effort without accidentally introducing new leakage.

That's why we should think again about the impact of deactivating RSA PKCS#1 v1.5 depadding in software. If the impact should be too heavy, then a legacy option could be introduced to enable this via opensc.conf. For now, this PR seems to solve the issue, so I think we can discuss this seperately without pressure.

src/common/constant-time.h Show resolved Hide resolved
src/pkcs11/misc.c Show resolved Hide resolved
@popovec
Copy link
Member

popovec commented Dec 13, 2023

I will try to draw my view on this PR. Let's assume that from the moment the token returns the APDU, OpenSC processes the APDU and returns the return value to the application in constant time. The attacker can find out from the timing attack on the application whether the decrypt operation was completed correctly or ended with an error.
For this reason, it is not necessary that all OpenSC operations take place in constant time. The only place where we have to prevent a side channel attack is how it is decided whether the decryption went correctly, i.e. Is the padding okay?
We only need to ensure that the removal (check) of the padding takes a constant time for arbitrary input data. For incorrect padding, the operation must take time T1, and for correct padding time T2, and T1 may or may not be the same as T2.
I believe that many changes in this PR are unnecessary. We don't need constant time for buffer size tests, and testing whether the user is logged in.. we don't need to adjust the logging of return codes. I would minimize this PR so that checking/removing the padding takes time T1 for correct padding and time T2 for incorrect padding.

@Jakuje Jakuje added this to In progress in OpenSC 0.25.0 via automation Dec 13, 2023
@frankmorgner
Copy link
Member

The matter is complicated as even a shortcut due to some error handling may lead to a side channel. If you want to dive into this, you may want to check out the marvin page and read the technical paper. I believe, the changes in this PR were guided by the toolkit for detecting time based side channels...

@popovec
Copy link
Member

popovec commented Dec 13, 2023

As I suggested, even if we make the OpenSC code so robust that it still maintains a constant time during decryption (for both correct and incorrect resulting plaintext), the resulting processing in the application that called the pkcs11 opensc service will still be vulnerable to a side channel timing attack. Even if the pkcs1 type 2 padding is removed directly in the token, we can still determine from a side channel attack on the application whether the plaintext met / did not meet the pkcs1 type 2 conditions (because it is possible to determine from the timing whether the application is processing the decrypted result, or is going the other way - an error path).

The only thing left is to consider how much we plan to complicate the OpenSC code, when a possible attacker calmly focuses on a timing attack that reveals the necessary information from the application calling opensc.

@xhanulik xhanulik marked this pull request as draft December 13, 2023 15:04
@frankmorgner
Copy link
Member

We should avoid leak of information that allows the attacker to distinguish a padding error from any other error, because this allows to gain information about the plaintext. This is also an API related problem, because the timing of handling buffers of different lengths are different. That's why changes in the PKCS#11/Minidriver/CTK code are needed. Above that, the attacker will only know that decryption failed without getting information about the plaintext.

It could be possible that I'm explaining this not well enough or even incorrectly. If you like, we can get you in touch with the author of the marvin paper.

@frankmorgner frankmorgner self-requested a review December 13, 2023 23:06
@popovec
Copy link
Member

popovec commented Dec 14, 2023

Yes, I understand that we mask absolutely everything... the attacker finds out that the decrypt was wrong, but he doesn't know if it was a mistake in the padding or another mistake. Although in my opinion, if the attacker finds out from the side channel that there was a decryption error, he can almost certainly rely on the fact that it was an error when removing the padding. You're right, we can't do more. It is OK.

@tomato42
Copy link

That's why we should think again about the impact of deactivating RSA PKCS#1 v1.5 depadding in software.

even if it's done in hardware, the error must be returned to the calling application in constant time

As I suggested, even if we make the OpenSC code so robust that it still maintains a constant time during decryption (for both correct and incorrect resulting plaintext), the resulting processing in the application that called the pkcs11 opensc service will still be vulnerable to a side channel timing attack.

Correct, and applications like openssl should already be doing that; so, while it's hard, it's not impossible. For a library like opensc there are only two options: don't create a side-channel where there wasn't one, or don't support PKCS#1v1.5 decryption, anything else is a vulnerability in OpenSC. It won't matter if the calling code is processing the result in side-channel free way if the returned value already has the side-channel baked-in.

src/libopensc/internal.h Outdated Show resolved Hide resolved
@xhanulik xhanulik marked this pull request as ready for review December 21, 2023 11:27
@xhanulik
Copy link
Contributor Author

The above comments should by fixed by now.

@popovec
Copy link
Member

popovec commented Dec 21, 2023

There is error in this implementation for missing message - openssl correctly returns an empty file for an empty message, test script:

#!/bin/bash
set -e
openssl genrsa -out rsa1024-key.pem 1024
openssl rsa -in rsa1024-key.pem -pubout -out rsa1024-pub.pem

T0="\
000210ff101010101fffffffffffffffffffffffffffffffffffffffffffffff\
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\
ffffffffffffffffffffffffffffffffffffffffffffffffffffffd010101000\
"
echo -n $T0|xxd -p -r > rsa_decrypt_padding_testfile.txt
openssl pkeyutl -encrypt -pkeyopt rsa_padding_mode:none -pubin -inkey rsa1024-pub.pem  -in rsa_decrypt_padding_testfile.txt -out rsa_encrypted_testfile_check.txt
echo $?
openssl pkeyutl -decrypt -inkey rsa1024-key.pem -in rsa_encrypted_testfile_check.txt -out rsa_decrypted_testfile_check.txt
echo $?
hd rsa_decrypted_testfile_check.txt

MyEID card or OsEID simulator, padding removal turned off in opensc.conf:

app default {
        card_driver myeid {
                disable_hw_pkcs1_padding = 1;
        }
}

Using this PR ...

P:409665; T:0x140574651076096 15:46:05.315338 [opensc-pkcs11] reader-pcsc.c:244:pcsc_internal_transmit: called
P:409665; T:0x140574651076096 15:46:05.336470 [opensc-pkcs11] reader-pcsc.c:335:pcsc_transmit: 
Incoming APDU (130 bytes):
00 02 10 FF 10 10 10 10 1F FF FF FF FF FF FF FF ................
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ................
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ................
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ................
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ................
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ................
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ................
FF FF FF FF FF FF FF FF FF FF FF D0 10 10 10 00 ................
90 00                                           ..

P:409665; T:0x140574651076096 15:46:05.336557 [opensc-pkcs11] apdu.c:382:sc_single_transmit: returning with: 0 (Success)
P:409665; T:0x140574651076096 15:46:05.336605 [opensc-pkcs11] apdu.c:539:sc_transmit: returning with: 0 (Success)
P:409665; T:0x140574651076096 15:46:05.336649 [opensc-pkcs11] card.c:523:sc_unlock: called
P:409665; T:0x140574651076096 15:46:05.336694 [opensc-pkcs11] card-myeid.c:1404:myeid_transmit_decipher: returning with: 128
P:409665; T:0x140574651076096 15:46:05.336739 [opensc-pkcs11] card-myeid.c:1429:myeid_decipher: returning with: 128
P:409665; T:0x140574651076096 15:46:05.336783 [opensc-pkcs11] sec.c:50:sc_decipher: returning with: 128
P:409665; T:0x140574651076096 15:46:05.336827 [opensc-pkcs11] card.c:523:sc_unlock: called
P:409665; T:0x140574651076096 15:46:05.336871 [opensc-pkcs11] pkcs15-sec.c:169:use_key: returning with: 128
P:409665; T:0x140574651076096 15:46:05.336915 [opensc-pkcs11] padding.c:160:sc_pkcs1_strip_02_padding_constant_time: called
P:409665; T:0x140574651076096 15:46:05.336965 [opensc-pkcs11] card.c:523:sc_unlock: called
P:409665; T:0x140574651076096 15:46:05.337010 [opensc-pkcs11] reader-pcsc.c:742:pcsc_unlock: called
P:409665; T:0x140574651076096 15:46:05.337191 [opensc-pkcs11] framework-pkcs15.c:4620:pkcs15_prkey_decrypt: Decryption complete.
P:409665; T:0x140574651076096 15:46:05.337281 [opensc-pkcs11] mechanism.c:1123:sc_pkcs11_decr_update: returning with: -1412 (Wrong padding)

@xhanulik
Copy link
Contributor Author

Thanks for catching, there was a redundant check for a empty message in the depadding function. Added also the corresponding unit test.

@popovec
Copy link
Member

popovec commented Dec 21, 2023

Thank you, at the moment the tests are going without any problems.

Just for fun, I measured the approximate depadding time (average of 10 tests, i5-3470, Linux version 6.1.0-15-rt-amd64) - time from APDU response to C_finalize() , with PR/without this PR:

  • empty message .. depadding time 575uS/1267uS
  • correct padding about 100 bytes of message ... 1121uS/1331uS
  • short padding (00 inside 8 bytes after initial 00:02:) 458 uS/947uS
  • wrong init bytes (00:01:..) 826uS/564uS
  • wrong init bytes (01:02:...) 829uS/650uS

The truth is that the attacker has worse options for measuring this time, but thanks to other bugs in current processors, we have no way to prevent him from accessing this information.

Anyway, I agree with the fact that we try to disable the possible attack as much as possible, and this PR contributes to that.

@tomato42
Copy link

@popovec Those results don't look right to me... Veronika has been running tests to verify that there are no differences in behaviour on the nanosecond level, not µs level. If, in your testing, you're running the same ciphertext over and over, and just averaging out the time over such repeated measurements, then you're not measuring the differences in the behaviour of the code, you're measuring the average frequency of the CPU that was performing the test. Please read the Out of the Box Testing paper linked from the Marvin page for details, and the marvin-toolkit for a practical example of applying conclusion from that paper.

@popovec
Copy link
Member

popovec commented Dec 22, 2023

Let's describe once again the methodology I use:
We use an empty message (as I mentioned above) as the first test and the exact same message with the opening byte changed to 01. We measure the time from the arrival of the APDU to the return of C_Finalize. The measurement is performed only by editing OpenSC src/libopebsc/log.c, it prints the time in uS.

Here is an example for some measurements that I quickly made:

Ok wrong
0.000457 0.000377
0.000854 0.000425
0.000477 0.000727
0.000898 0.000654
0.000813 0.000666
0.000471 0.00066
0.000774 0.000616
0.000826 0.000367
0.000812 0.000334
0.00087 0.000666
0.00046 0.000363
0.000873 0.000382

Yes, sometimes it is reversed, i.e. incorrect padding takes longer than correct padding, but it seems to me that it makes it possible to identify quite precisely which message has faulty padding..

@xhanulik
Copy link
Contributor Author

I have measured the whole C_Decrypt function with the virtual card but without the APDU calls and actual decryption (to speed up measurements and eliminate noise) - with the input as padded plaintext, copied into the output buffer. The modified pkcs11-tool for the testing is here.

For generating the input plaintexts and performing the time analysis, I used marvin-toolkit (slightly adjusted to generate plaintexts instead of ciphertexts). The final report.txt looks ok. However, I can rerun the tests with the current state of PR to double-check.

@popovec
Copy link
Member

popovec commented Dec 22, 2023

Your testing method seems perfectly fine to me. I looked through the entire PR and it also seems fine. I still don't know the reason why the measurement I made gives such a good indication of the nature of the padding.

I will try to find out more precisely where the problem arises. The log shows which lines I use to measure the time difference:

Incoming APDU (130 bytes):
00 02 10 FF 10 10 10 10 1F FF FF FF FF FF FF FF ................
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ................
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ................
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ................
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ................
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ................
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ................
FF FF FF FF FF FF FF FF FF FF FF D0 10 10 10 00 ................
90 00                                           ..

P:541144; T:0x140353344187904 08:33:38.583479 [opensc-pkcs11] apdu.c:382:sc_single_transmit: returning with: 0 (Success)
P:541144; T:0x140353344187904 08:33:38.583500 [opensc-pkcs11] apdu.c:539:sc_transmit: returning with: 0 (Success)
P:541144; T:0x140353344187904 08:33:38.583520 [opensc-pkcs11] card.c:523:sc_unlock: called 
P:541144; T:0x140353344187904 08:33:38.583539 [opensc-pkcs11] card-myeid.c:1404:myeid_transmit_decipher: returning with: 128
>>>>>P:541144; T:0x140353344187904 08:33:38.583559 [opensc-pkcs11] card-myeid.c:1429:myeid_decipher: returning with: 128
P:541144; T:0x140353344187904 08:33:38.583578 [opensc-pkcs11] sec.c:50:sc_decipher: returning with: 128
P:541144; T:0x140353344187904 08:33:38.583597 [opensc-pkcs11] card.c:523:sc_unlock: called
P:541144; T:0x140353344187904 08:33:38.583616 [opensc-pkcs11] pkcs15-sec.c:169:use_key: returning with: 128
P:541144; T:0x140353344187904 08:33:38.583636 [opensc-pkcs11] padding.c:160:sc_pkcs1_strip_02_padding_constant_time: called
P:541144; T:0x140353344187904 08:33:38.583659 [opensc-pkcs11] card.c:523:sc_unlock: called
P:541144; T:0x140353344187904 08:33:38.583679 [opensc-pkcs11] reader-pcsc.c:742:pcsc_unlock: called
P:541144; T:0x140353344187904 08:33:38.583838 [opensc-pkcs11] framework-pkcs15.c:4620:pkcs15_prkey_decrypt: Decryption complete.
P:541144; T:0x140353344187904 08:33:38.583891 [opensc-pkcs11] pkcs11-object.c:1038:C_Decrypt: C_Decrypt()
P:541144; T:0x140353344187904 08:33:38.583920 [opensc-pkcs11] pkcs11-session.c:163:C_CloseSession: C_CloseSession(0x556b58289a50)
P:541144; T:0x140353344187904 08:33:38.583942 [opensc-pkcs11] pkcs11-session.c:109:sc_pkcs11_close_session: real C_CloseSession(0x556b58289a50)
P:541144; T:0x140353344187904 08:33:38.583966 [opensc-pkcs11] pkcs15-pin.c:851:sc_pkcs15_pincache_clear: called
P:541144; T:0x140353344187904 08:33:38.584003 [opensc-pkcs11] framework-pkcs15.c:1973:pkcs15_logout: Clearing PIN state without calling sc_logout()
>>>>>P:541144; T:0x140353344187904 08:33:38.584027 [opensc-pkcs11] pkcs11-global.c:415:C_Finalize: C_Finalize()

@popovec
Copy link
Member

popovec commented Dec 22, 2023

One more thing, pkcs11-tool tries to call C_Decrypt first, if it fails (it ends with an error) it tries to call C_DecryptUpdate (even if it is non-standard, it can also be used for asymmetric decryption), however, this ends in the case of incorrect padding as follows:

error: PKCS11 function C_DecryptUpdate failed: rv = unknown PKCS11 error (0xfffffa7c)

P:542859; T:0x140565198802432 11:44:26.632504 [opensc-pkcs11] padding.c:160:sc_pkcs1_strip_02_padding_constant_time: called
P:542859; T:0x140565198802432 11:44:26.632546 [opensc-pkcs11] card.c:523:sc_unlock: called
P:542859; T:0x140565198802432 11:44:26.632574 [opensc-pkcs11] reader-pcsc.c:742:pcsc_unlock: called
P:542859; T:0x140565198802432 11:44:26.632739 [opensc-pkcs11] framework-pkcs15.c:4620:pkcs15_prkey_decrypt: Decryption complete.
P:542859; T:0x140565198802432 11:44:26.632793 [opensc-pkcs11] mechanism.c:1123:sc_pkcs11_decr_update: returning with: -1412 (Wrong padding)
P:542859; T:0x140565198802432 11:44:26.632826 [opensc-pkcs11] pkcs11-object.c:1062:C_DecryptUpdate: C_DecryptUpdate() = 0xFFFFFFFFFFFFFA7C
P:542859; T:0x140565198802432 11:44:26.632858 [opensc-pkcs11] pkcs11-global.c:415:C_Finalize: C_Finalize()

When I conducted my measurements, I was actually measuring part of C_DecryptUpdate and not C_Decrypt itself.
I measured it again on a completely different processor - Quad-Core AMD Opteron(tm) Processor 2352, the log shows the part that was measured:

P:544673; T:0x140544698334720 12:17:30.697226 [opensc-pkcs11] card-myeid.c:1429:myeid_decipher: returning with: 128
P:544673; T:0x140544698334720 12:17:30.697245 [opensc-pkcs11] sec.c:50:sc_decipher: returning with: 128
P:544673; T:0x140544698334720 12:17:30.697265 [opensc-pkcs11] card.c:523:sc_unlock: called
P:544673; T:0x140544698334720 12:17:30.697284 [opensc-pkcs11] pkcs15-sec.c:169:use_key: returning with: 128
P:544673; T:0x140544698334720 12:17:30.697303 [opensc-pkcs11] padding.c:160:sc_pkcs1_strip_02_padding_constant_time: called
P:544673; T:0x140544698334720 12:17:30.697327 [opensc-pkcs11] card.c:523:sc_unlock: called
P:544673; T:0x140544698334720 12:17:30.697346 [opensc-pkcs11] reader-pcsc.c:742:pcsc_unlock: called
P:544673; T:0x140544698334720 12:17:30.697501 [opensc-pkcs11] framework-pkcs15.c:4620:pkcs15_prkey_decrypt: Decryption complete.
correct fail(Decrypt) fail(decrypt update)
0.000436 0.000465 0.000467
0.000476 0.000466 0.000465
0.000454 0.000491 0.000507
0.000458 0.000418 0.000495
0.000276 0.000255 0.000284
0.000465 0.000279 0.000273
0.000456 0.000473 0.000472
0.000467 0.000469 0.000445
0.000277 0.00047 0.000533
0.000458 0.000499 0.000482

I don't dare to judge whether there is any correlation between padding and time, rather no than yes.
Unless someone has a different opinion, I consider this PR suitable for acceptance.

@tomato42
Copy link

hmm, isn't it actually incorrect to call C_DecryptUpdate on a context returned by C_DecryptInit when C_Decrypt was already called?

Or the behaviour you're talking about is for handling a buggy PKCS#11 driver, that doesn't implement C_Decrypt but does C_DecryptInit? That's probably fine for a command line tool, but will create a side-channel when done on library level.

The measurements you've quoted are too variable to be able to say anything about presence or absence of a side-channel:

Yes, sometimes it is reversed, i.e. incorrect padding takes longer than correct padding, but it seems to me that it makes it possible to identify quite precisely which message has faulty padding

unless there's a consistent difference, then no, it can't be used like that

@popovec
Copy link
Member

popovec commented Dec 23, 2023

hmm, isn't it actually incorrect to call C_DecryptUpdate on a context returned by C_DecryptInit when C_Decrypt was already called?

pkcs11-tool is also used for symmetric decryption. In this tool, we cannot say in advance whether the pkcs11 module to be used has support for C_Decrypt for symmetric ciphers. Therefore, a decryption attempt is first made via C_Decrypt, and in case of failure, the attempt is repeated via C_DecryptUpdate (at the same time, a new initialization will take place using C_DecrytpInit). Yes, we can modify the pkcs11 tool to no longer decrypt with C_DecryptUpdate for certain return codes from C-Decrypt. Feel free to submit a PR.

unless there's a consistent difference, then no, it can't be used like that

When I took the measurements on the i5, the correlation of time and type of padding was strangely very accurate... I don't know why. It was probably a coincidence. However, the measurements on the Opteron are far less convincing.

@xhanulik: please check if we don't have a condition in C_DecryptUpdate that should also be executed in constant time.
Thank you.

@tomato42
Copy link

Let me ask it the other way: pkcs11-tool is a debugging and management tool, it's not the intended interface for any automation around encryption and decryption?

When I took the measurements on the i5, the correlation of time and type of padding was strangely very accurate

@xhanulik please correct me if you have the data, but based on the necessary changes I wouldn't expect the side-channel to be larger than 10 µs. With a processing time in the order of 500 µs, st.dev of 90µs, with just 10 measurements even Student t test is unlikely to have a statistically significant result with normally distributed data. So yes, very likely a coincidence.

@popovec
Copy link
Member

popovec commented Dec 24, 2023

Let me ask it the other way: pkcs11-tool is a debugging and management tool, it's not the intended interface for any automation around encryption and decryption?

We can safely expect that in programming languages that do not have pkcs11 support in their libraries, anyone can call pkcs11-tool via execve() or a similar call. An example can be a bash script where someone uses the pkcs11-tool to decrypt some data. Even if the relevant programming language has pkcs11 support in it, someone can call pkcs11-tool as a decryption tool...

Much more dangerous things happen on the internet, people download an unverified docker file and run it…

@popovec
Copy link
Member

popovec commented Dec 24, 2023

src/libopensc/pkcs15-sec.c

-       LOG_FUNC_RETURN(ctx, r);
+       /* do not log error code to prevent side channel attack */
+       return r;

I was particularly interested in this place in this PR, why not record the return value to opensc if a few places later in the user application there is a change in the behavior of the program flow, and here one way or another the error type will show up.

But I accept it, in that case I have to modify similarly:

diff --git a/src/pkcs11/pkcs11-object.c b/src/pkcs11/pkcs11-object.c
index 99219eeb3..b02391121 100644
--- a/src/pkcs11/pkcs11-object.c
+++ b/src/pkcs11/pkcs11-object.c
@@ -1059,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;
 }
@@ -1087,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;
 }

@popovec
Copy link
Member

popovec commented Dec 24, 2023

sc_pkcs11_decrypt():

???        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) {
???                if (pulDataLen)
                        *pulDataLen = ulDataLen;
                return rv;
        }

Isn't it a duplicate condition?

@popovec
Copy link
Member

popovec commented Jan 2, 2024

I'm afraid that here we would have to push these things through ISO7816-8.

I think that from OpenSC's point of view it will be best to prepare (after accepting this PR) patches for each of the supported cards, which will make it possible to turn off hardware depadding on the cards (if possible).

MyEID card already has support for turning off depadding in the card, but depadding is turned on by default. This will need to be changed.

@tomato42
Copy link

tomato42 commented Jan 2, 2024

Yes, I don't think it needs to be in a single PR.

Speaking of wider efforts, I've been talking with Robert Relyea (co-chair of the OASIS PKCS#11 committee) about recommending Marvin workaround/implicit rejection on PKCS#11 level. I'm also working on an update to the PKCS#1 standard (through CFRG) here: https://datatracker.ietf.org/doc/draft-kario-rsa-guidance/ . I don't think I have the necessary contacts, or time, to influence ISO standards directly.

@xhanulik
Copy link
Contributor Author

xhanulik commented Jan 3, 2024

I have one more proposal, currently we have created a branch in pkcs15_prkey_decrypt(), which processes the error code/buffer in constant time for SC_ALGORITHM_RSA_PAD_PKCS1. The question is, wouldn't it be easier to manage everything in constant time, i.e. also the output of the RSA raw operation or of other depadding (OAEP). The time impact is insignificant here (the longest operation takes a token and data transfer). The code would be simplified to one branch, which would always execute in constant time.

This should be fixed by now.

@popovec
Copy link
Member

popovec commented Jan 8, 2024

The final code is fine, but the readability of this PR (17 commits) is rather unclear. I would like it if the PR could be simplified somehow, after years it will be very difficult to read what was done and why. Don't fix your own code with new commits, use rebase and fix the commit where the error in your PR originated. For example: commit pkcs15_prkey_decrypt: Fix error code mismatch should be removed and error should be fixed in commit framework-pkcs15.c: Handle PKCS# 1 v1.5 depadding constant time.

And one whitespace error:

$ git rebase master
$ git diff master --check
src/tests/unittests/strip_pkcs1_2_padding.c:152: trailing whitespace.
+                        0x00, 

coding style:

+# define CONSTANT_TIME_H

It will be better if we write it down without that space.. Try using clang-format - more in #2017

@xhanulik
Copy link
Contributor Author

xhanulik commented Jan 8, 2024

Sure, will do, I was just waiting to see if there were any other comments, thanks.

Copy link
Member

@Jakuje Jakuje left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just a minor nits.
I think for the pkcs11 we have test coverage. Could somebody test the minidriver works too? Or even measure the timing difference if possible?

src/pkcs11/mechanism.c Outdated Show resolved Hide resolved
src/minidriver/minidriver.c Outdated Show resolved Hide resolved
src/tests/unittests/Makefile.mak Outdated Show resolved Hide resolved
@xhanulik xhanulik force-pushed the constant-pkcs1 branch 2 times, most recently from 10f4ddd to 9f0ade0 Compare January 30, 2024 10:50
Copy link
Member

@frankmorgner frankmorgner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good now

To prevent Marvin attack on RSA PKCS#1 v1.5 padding
when logging the return value, signaling the padding error.
In order to not disclose time side-channel when the depadding
fails, do the same operations as for case when depadding ends
with success.
To prevent Marvin attack on RSA PKCS#1 v1.5 padding
when logging the return value, signaling the padding error.
@xhanulik
Copy link
Contributor Author

Added minor fixes for includes order.

@xhanulik
Copy link
Contributor Author

I have also checked how the returned decrypted buffer is handled in the card drivers (supporting RSA_RAW operation) before the depadding is done. In most drivers, I haven't found any code dependent on the particular bytes in the buffer, so that should be ok.

There is only card-tcos.c where tcos_decipher() attempts to remove padding from the received buffer

if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) {
size_t len = (apdu.resplen > outlen) ? outlen : apdu.resplen;
unsigned int offset = 0;
if (tcos3 && (data->pad_flags & SC_ALGORITHM_RSA_PAD_PKCS1)
&& len > 2 && apdu.resp[0] == 0 && apdu.resp[1] == 2) {
offset = 2;
while (offset < len && apdu.resp[offset] != 0)
++offset;
offset = (offset < len - 1) ? offset + 1 : 0;
}
if (offset < len)
memcpy(out, apdu.resp + offset, len - offset);
SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, (int)(len - offset));
}

I don't know whether the card checks the padding, and this stripping is done only when the padding is correct, so I'm not sure whether this needs more care or not.

@Jakuje
Copy link
Member

Jakuje commented Feb 5, 2024

Thank you! Merging to move along with the release.

@Jakuje Jakuje merged commit b31f82b into OpenSC:master Feb 5, 2024
39 of 43 checks passed
OpenSC 0.25.0 automation moved this from In progress to Done Feb 5, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
No open projects
Development

Successfully merging this pull request may close these issues.

None yet

5 participants