Cryptoki PKCS11 C_Decrypt returns shorter key (decrypted messaged) than expected

First off let me say I’m fully aware this question can only be answered by the token vendor but I have already contacted them and with the whole COVID situation my hopes of hearing from them soon are not high (nor that I need to, this is just to satisfy my own curiosity). What I’m hoping to get as an answer is maybe somebody with a similar experience.

I have several tokens (smart cards with crypto capabilities based on an MCU from ST Micro., ST19WLxx to be more precise) where I have stored certificates, mostly for authentication and digital signature purposes. But they can also be used for decryption so I decided to give that a try. The original idea was to generate a symmetric key for disk encryption purposes. Then I would use the public key on the card to encrypt it and the private key to decrypt it to unlock access to a partition on my disk.

This is all standard practice and should be pretty straight forward but for completeness, I will guide you through the steps (on Linux; although I also tried with Windows with the same end result):

1) I generated a random symmetric key, 245 bytes long to account for the fact that I will be using RSA-PKCS padding, the only one supported by the card and considering the RSA keys are 2048 bit long:

$   dd if=/dev/urandom of=./symmetric_key bs=1 count=245 

2) I extract the public key from the card, once I got its ID:

$   pkcs11-tool -r --login --id $  KEY_ID --type pubkey --module token_driver.so -o public_key.der 

3) I convert the public key format to pem:

$   openssl rsa -pubin -in pub_key.der -inform DER -outform PEM -out pub_key.pem 

4) I encrypt the symmetric key from step one using my public key:

$   openssl rsautl -inkey ./pub_key.pem -pubin -encrypt -pkcs -in ./symmetryc_key -out ./encrypted_key.pkcs1 

5) And finally, I decrypt with the private key on my card:

$   pkcs11-tool --id $  KEY_ID --decrypt --login -m RSA-PKCS --module token_driver.so --input-file encrypted_key.pkcs1 --output-file decrypted_key 

With that, I should recover the original symmetric_key again on decrypted_key. Unfortunately, this is not what happens. Instead, my decrypted_key is only 102 bytes long.

If I examine the key I can see I’m getting only the last 102 bytes of the original key, the remaining 143 are lost.

This is an example of symmetric_key (sample output from step 1):

00000000  77 1a e4 f3 71 c1 23 c8  0a 47 17 87 d3 c6 ad 31  |w...q.#..G.....1| 00000010  2b 43 94 f9 1f 41 a0 c7  4f 80 5c 00 51 bb 6b b6  |+C...A..O.\.Q.k.| 00000020  a4 4c 87 5b 5c 5c 28 ef  d3 b7 d1 85 a2 3a c0 87  |.L.[\(......:..| 00000030  f1 25 38 b7 b9 28 d7 5f  e4 a1 da 4d 0a 71 f2 85  |.%8..(._...M.q..| 00000040  89 0e bb a4 2b 58 3e 18  90 c6 be 75 22 78 27 d7  |....+X>....u"x'.| 00000050  36 4a 95 74 aa fe e3 c1  d1 f6 02 a0 26 18 28 e2  |6J.t........&.(.| 00000060  14 9c 46 58 ea d1 b6 b6  1f d6 86 f6 9f f7 29 c7  |..FX..........).| 00000070  0e bd 50 8e dd ce 34 65  3f 7a 32 e3 3a 28 4c 3a  |..P...4e?z2.:(L:| 00000080  8d 47 36 9c ab af d0 db  bf d0 db f1 ca 32 be 97  |.G6..........2..| 00000090  62 4e c4 6a 79 b3 1a 3a  2b 2c 11 69 84 9b d5 65  |bN.jy..:+,.i...e| 000000a0  d6 75 b5 00 05 42 c5 8f  cd 82 6a 09 9a 50 07 2b  |.u...B....j..P.+| 000000b0  04 86 0d 15 92 e3 8b cf  fb 97 1c 9e f7 6f 22 51  |.............o"Q| 000000c0  e1 45 00 64 45 3d 4b 38  a6 7f f0 aa 7e 12 bb 26  |.E.dE=K8....~..&| 000000d0  85 91 a4 5c 9e dd 59 6a  f6 85 c2 2b 38 4d 2b c2  |...\..Yj...+8M+.| 000000e0  f1 2f 71 d0 21 46 1b d2  fd 57 03 66 2f b1 c1 0f  |./q.!F...W.f/...| 000000f0  51 53 9d 22 4e                                    |QS."N| 000000f5 

And the corresponding output from decrypting on step 5:

00000000  97 62 4e c4 6a 79 b3 1a  3a 2b 2c 11 69 84 9b d5  |.bN.jy..:+,.i...| 00000010  65 d6 75 b5 00 05 42 c5  8f cd 82 6a 09 9a 50 07  |e.u...B....j..P.| 00000020  2b 04 86 0d 15 92 e3 8b  cf fb 97 1c 9e f7 6f 22  |+.............o"| 00000030  51 e1 45 00 64 45 3d 4b  38 a6 7f f0 aa 7e 12 bb  |Q.E.dE=K8....~..| 00000040  26 85 91 a4 5c 9e dd 59  6a f6 85 c2 2b 38 4d 2b  |&...\..Yj...+8M+| 00000050  c2 f1 2f 71 d0 21 46 1b  d2 fd 57 03 66 2f b1 c1  |../q.!F...W.f/..| 00000060  0f 51 53 9d 22 4e                                 |.QS."N| 00000066 

First thing I thought was: “huh? software/driver issue. But I have access to the driver code and after staring at it and messing with it for quite a long while I am almost completely sure there is nothing wrong with it.

The major clue that makes me think this is a firmware issue (I don’t have access to the code inside the card’s MCU) comes from a very careful examination of the APDU frames that the card exchanges with the host: there are no errors anywhere, I always get the magic 0x9000 everything is fine message from the card and the frame where I receive the decrypted data is short (it’s actually 20 or so bytes longer than 102, but there are headers and a secure channel involved so part of the message is encrypted) and comes announced with the correct number of bytes (SW=0x6179).

I did many more things, like: testing on Windows, trying keys and text messages with different lengths (the decryption works fine up to messages of 102 bytes, longer than that and they get truncated), using different cards with the same hardware and firmware version, using different cards with different hardware and firmware versions (not that dissimilar after all because I got the same problem), getting all debug info from the driver to see if I was getting any hidden errors…

Considering RSA-OAEP is not supported by this card (or at least not documented) and the problems associated with RSA-PKCS I guess it’s best to let this old dog sleep and not try to teach it new tricks.

But as I said: I’m curious, have you ever encounter something like this? Is there something else I can do to be sure this is a firmware issue? I guess in part I refuse to believe something so fundamental has been lurking undetected for so long (this hardware has been in use for many years by a significant amount of people). Maybe there is something wrong with my setup or understanding of the problem after all.