Yubico / yubico-piv-tool

Command line tool for the YubiKey PIV application
https://developers.yubico.com/yubico-piv-tool
BSD 2-Clause "Simplified" License
290 stars 97 forks source link

YKCS11 RSA-OAEP seems broken #309

Closed mouse07410 closed 3 years ago

mouse07410 commented 3 years ago
$ openssl pkeyutl -encrypt -engine pkcs11 -keyform engine -pubin -inkey "pkcs11:model=PKCS%2315%20emulated;manufacturer=piv_II;id=%03;object=KEY%20MAN%20pubkey;type=public" -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_mgf1_md:sha384 -in /tmp/derive.5338.pass -out /tmp/derive.5338.key.enc 
engine "pkcs11" set.
$ pkcs11-tool  --module /usr/local/lib/libykcs11.dylib --decrypt -m RSA-PKCS-OAEP --mgf MGF1-SHA384 --hash-algorithm SHA384 --id 03 -y pubkey -i /tmp/derive.5338.key.enc -o /tmp/derive.5338.pass.dec
Using slot 0 with a present token (0x0)
Logging in to "YubiKey PIV #744xxxx".
Please enter User PIN: 
Using decrypt algorithm RSA-PKCS-OAEP
OAEP parameters: hashAlg=SHA384, mgf=MGF1-SHA384, source_type=0, source_ptr=0x0, source_len=0
error: PKCS11 function C_Decrypt failed: rv = CKR_FUNCTION_FAILED (0x6)
Aborting.
$ pkcs11-tool  --module /usr/local/lib/libykcs11.dylib --decrypt -m RSA-PKCS-OAEP --mgf MGF1-SHA384 --hash-algorithm SHA384 --salt-len -1 --id 03 -y pubkey -i /tmp/derive.5338.key.enc -o /tmp/derive.5338.pass.dec
Using slot 0 with a present token (0x0)
Logging in to "YubiKey PIV #744xxxx".
Please enter User PIN: 
Using decrypt algorithm RSA-PKCS-OAEP
OAEP parameters: hashAlg=SHA384, mgf=MGF1-SHA384, source_type=0, source_ptr=0x0, source_len=0
error: PKCS11 function C_Decrypt failed: rv = CKR_FUNCTION_FAILED (0x6)
Aborting.
$ openssl pkeyutl -decrypt -engine pkcs11 -keyform engine -inkey "pkcs11:model=PKCS%2315%20emulated;manufacturer=piv_II;id=%03;object=KEY%20MAN%20key;type=private" -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_mgf1_md:sha384 -out /tmp/derive.5338.pass.dec2 -in /tmp/derive.5338.key.enc 
engine "pkcs11" set.
Enter PKCS#11 token PIN for xxxxxxxx:
$ diff /tmp/derive.5338.pass.dec2 /tmp/derive.5338.pass
$ 

As you can see, RSA-OAEP decryption by YKCS11 library fails. OpenSSL using OpenSC and libp11 engine, decrypts that same file OK (and correctly, as the decrypted data matches the original plaintext).

SPY capture:

$ pkcs11-tool  --module /Library/OpenSC/lib/pkcs11-spy.so --decrypt -m RSA-PKCS-OAEP --mgf MGF1-SHA384 --hash-algorithm SHA384 --id 03 -y privkey -i /tmp/derive.5338.key.enc -o /tmp/derive.5338.pass.dec

*************** OpenSC PKCS#11 spy *****************
Loaded: "/usr/local/lib/libykcs11.dylib"

0: C_GetInterface
2021-07-02 12:35:44.759
[compat]
[in] pInterfaceName 0000000105d9bb1a / 7
    00000000  50 4B 43 53 20 31 31                             PKCS 11         
[in] pVersion = NULL
[in] flags = 
Returned:  0 CKR_OK

1: C_Initialize
2021-07-02 12:35:44.759
[in] pInitArgs = 0x0
Returned:  0 CKR_OK

2: C_GetSlotList
2021-07-02 12:35:44.759
[in] tokenPresent = 0x0
[out] pSlotList: 
Count is 1
[out] *pulCount = 0x1
Returned:  0 CKR_OK

3: C_GetSlotList
2021-07-02 12:35:44.780
[in] tokenPresent = 0x0
[out] pSlotList: 
Slot 0
[out] *pulCount = 0x1
Returned:  0 CKR_OK

4: C_GetSlotInfo
2021-07-02 12:35:44.780
[in] slotID = 0x0
[out] pInfo: 
      slotDescription:        'Yubico YubiKey OTP+FIDO+CCID    '
                              '                                '
      manufacturerID:         'Yubico (www.yubico.com)         '
      hardwareVersion:         1.0
      firmwareVersion:         1.0
      flags:                   7
        CKF_TOKEN_PRESENT                
        CKF_REMOVABLE_DEVICE             
        CKF_HW_SLOT                      
Returned:  0 CKR_OK
Using slot 0 with a present token (0x0)

5: C_GetTokenInfo
2021-07-02 12:35:44.780
[in] slotID = 0x0
[out] pInfo: 
      label:                  'YubiKey PIV #744xxxx            '
      manufacturerID:         'Yubico (www.yubico.com)         '
      model:                  'YubiKey YK4     '
      serialNumber:           '744xxxx         '
      ulMaxSessionCount:       16
      ulSessionCount:          0
      ulMaxRwSessionCount:     16
      ulRwSessionCount:        0
      ulMaxPinLen:             48
      ulMinPinLen:             6
      ulTotalPublicMemory:     -1
      ulFreePublicMemory:      -1
      ulTotalPrivateMemory:    -1
      ulFreePrivateMemory:     -1
      hardwareVersion:         1.0
      firmwareVersion:         4.37
      time:                   '                '
      flags:                   40d
        CKF_RNG                          
        CKF_LOGIN_REQUIRED               
        CKF_USER_PIN_INITIALIZED         
        CKF_TOKEN_INITIALIZED            
Returned:  0 CKR_OK

6: C_OpenSession
2021-07-02 12:35:44.780
[in] slotID = 0x0
[in] flags = 0x6
pApplication=0x0
Notify=0x0
[out] *phSession = 0x1
Returned:  0 CKR_OK

7: C_GetTokenInfo
2021-07-02 12:35:44.997
[in] slotID = 0x0
[out] pInfo: 
      label:                  'YubiKey PIV #744xxxx            '
      manufacturerID:         'Yubico (www.yubico.com)         '
      model:                  'YubiKey YK4     '
      serialNumber:           '744xxxx         '
      ulMaxSessionCount:       16
      ulSessionCount:          0
      ulMaxRwSessionCount:     16
      ulRwSessionCount:        1
      ulMaxPinLen:             48
      ulMinPinLen:             6
      ulTotalPublicMemory:     -1
      ulFreePublicMemory:      -1
      ulTotalPrivateMemory:    -1
      ulFreePrivateMemory:     -1
      hardwareVersion:         1.0
      firmwareVersion:         4.37
      time:                   '                '
      flags:                   40d
        CKF_RNG                          
        CKF_LOGIN_REQUIRED               
        CKF_USER_PIN_INITIALIZED         
        CKF_TOKEN_INITIALIZED            
Returned:  0 CKR_OK
Logging in to "YubiKey PIV #744xxxx".
Please enter User PIN: 

8: C_Login
2021-07-02 12:35:49.075
[in] hSession = 0x1
[in] userType = CKU_USER
[in] pPin[ulPinLen] 00007fa3455059d0 / <whatever>
    00000000  <whatever>       
Returned:  0 CKR_OK

9: C_FindObjectsInit
2021-07-02 12:35:49.085
[in] hSession = 0x1
[in] pTemplate[2]: 
    CKA_CLASS             CKO_PRIVATE_KEY      
    CKA_ID                0000000105de21d0 / 1
    00000000  03                                               .               
Returned:  0 CKR_OK

10: C_FindObjects
2021-07-02 12:35:49.085
[in] hSession = 0x1
[in] ulMaxObjectCount = 0x1
[out] ulObjectCount = 0x1
Object 0x58 matches
Returned:  0 CKR_OK

11: C_FindObjectsFinal
2021-07-02 12:35:49.085
[in] hSession = 0x1
Returned:  0 CKR_OK
Using decrypt algorithm RSA-PKCS-OAEP
OAEP parameters: hashAlg=SHA384, mgf=MGF1-SHA384, source_type=0, source_ptr=0x0, source_len=0

12: C_DecryptInit
2021-07-02 12:35:49.086
[in] hSession = 0x1
pMechanism->type=CKM_RSA_PKCS_OAEP            
pMechanism->pParameter->hashAlg=CKM_SHA384                   
pMechanism->pParameter->mgf=CKG_MGF1_SHA384
pMechanism->pParameter->source=0
[out] pSourceData[ulSourceDalaLen] NULL [size : 0x0 (0)]
[in] hKey = 0x58
Returned:  0 CKR_OK

13: C_GetAttributeValue
2021-07-02 12:35:49.086
[in] hSession = 0x1
[in] hObject = 0x58
[in] pTemplate[1]: 
    CKA_ALWAYS_AUTHENTICATE  00007ffeea00f060 / 1
[out] pTemplate[1]: 
    CKA_ALWAYS_AUTHENTICATE  False
Returned:  0 CKR_OK

14: C_Decrypt
2021-07-02 12:35:49.086
[in] hSession = 0x1
[in] pEncryptedData[ulEncryptedDataLen] 00007ffeea00f290 / 256
    00000000  A3 1B 93 C9 64 15 BC DA 93 23 CA 70 8B 82 47 BE  ....d....#.p..G.
    00000010  E9 40 60 EC F3 28 F3 42 24 4B 21 0C 35 C3 F5 B9  .@`..(.B$K!.5...
    00000020  E9 DE B1 67 CA 18 AF 82 5E 62 5E C1 64 19 18 A6  ...g....^b^.d...
    00000030  7F 51 B1 08 7B 28 C4 08 6F BE 9D FE C7 2F 7E C2  Q..{(..o..../~.
    00000040  11 1F F6 DF A0 98 20 BD 70 D6 BA B3 54 86 D9 43  ...... .p...T..C
    00000050  21 BE 3C 22 DF D0 48 76 27 F0 37 9A 9C 16 E0 68  !.<"..Hv'.7....h
    00000060  3C EC B0 B4 B7 2D A6 82 D9 35 5A 55 AE EE C1 23  <....-...5ZU...#
    00000070  51 8D 29 02 AD D2 7B 91 49 1E D8 94 1E DD CF 1A  Q.)...{.I.......
    00000080  8B 70 11 F5 28 74 78 76 BE 05 49 F2 95 F5 C5 7E  .p..(txv..I....~
    00000090  8C B0 DF FD A1 8C B8 AE 7C 76 5F 8F 20 0F D3 58  ........|v_. ..X
    000000A0  ED CE BB 97 AD F7 EC 43 B5 AC 75 E6 A9 88 24 77  .......C..u...$w
    000000B0  B9 53 70 B9 11 24 9D 3E 93 FD 06 7D 11 67 07 DA  .Sp..$.>...}.g..
    000000C0  E9 56 8D 52 BC BA A0 F0 FB 45 32 E3 94 30 3E F5  .V.R.....E2..0>.
    000000D0  48 D1 87 6B 3A 39 02 14 B7 4B D2 C9 48 8A CA 58  H..k:9...K..H..X
    000000E0  7F 76 73 3A A4 C1 33 06 62 4C C2 84 44 D0 36 95  vs:..3.bL..D.6.
    000000F0  1C C1 1D FF B5 76 8A B5 8A 1C 2D 2F 33 59 4A B1  .....v....-/3YJ.
Returned:  6 CKR_FUNCTION_FAILED

15: C_Finalize
2021-07-02 12:35:49.225
Returned:  0 CKR_OK
error: PKCS11 function C_Decrypt failed: rv = CKR_FUNCTION_FAILED (0x6)
Aborting.

Update

Here's your own test failing:

$ pkcs15-tool --read-public-key 02 > /tmp/pub_03.pem
Using reader with a card: Yubico YubiKey OTP+FIDO+CCID
$ openssl rsautl -encrypt -oaep -keyform PEM -pubin -inkey /tmp/pub_03.pem -in /tmp/derive.5338.pass -out /tmp/derive.5338.key.enc 
$ pkcs11-tool  --module /usr/local/lib/libykcs11.dylib --decrypt -m RSA-PKCS-OAEP --id 3 -i /tmp/derive.5338.key.enc -o /tmp/derive.5338.pass.dec
Using slot 0 with a present token (0x0)
Logging in to "YubiKey PIV #744xxxx".
Please enter User PIN: 
Using decrypt algorithm RSA-PKCS-OAEP
OAEP parameters: hashAlg=SHA256, mgf=MGF1-SHA256, source_type=0, source_ptr=0x0, source_len=0
error: PKCS11 function C_Decrypt failed: rv = CKR_FUNCTION_FAILED (0x6)
Aborting.
$
$ yubico-piv-tool -a status
Version:    4.3.7
Serial Number:  744xxxx
CHUID:  3019d4e739da739ced..........................
CCC:    f015a000000116f.................................
Slot 9a:    
    Algorithm:  RSA2048
    Subject DN: xxx
    Issuer DN:  xxx
    Fingerprint:    499634ceffefb163eee3ecec52557065d2xxxxxxxxxxxxx
    Not Before: Aug 20 18:03:10 2018 GMT
    Not After:  Aug 19 18:03:10 2021 GMT
Slot 9c:    
    Algorithm:  RSA2048
    Subject DN: xxx
    Issuer DN:  xxx
    Fingerprint:    1a3e3ef454a65xxxxxxxxxxxxxxxxxxxxxxx
    Not Before: Aug 20 18:15:15 2019 GMT
    Not After:  Aug 20 18:15:15 2022 GMT
Slot 9d:    
    Algorithm:  RSA2048
    Subject DN: CN=xxx
    Issuer DN:  CN=xxx
    Fingerprint:    d9fe861c6c2e25d513xxxxxxxx
    Not Before: Aug 20 18:24:46 2019 GMT
    Not After:  Aug 20 18:24:46 2022 GMT

PIN tries left: 3
a-dma commented 3 years ago

I believe you are not correctly matching the hashes between encryption and decryption. Keep in mind that there are two hash functions at play, one for the MGF1 function and one for OAEP.

Here are some examples:

First some data

$ cat data.txt
hello world

Using SHA256 for both:

$ openssl pkeyutl -in data.txt -encrypt -pubin -inkey pub.pem -keyform PEM -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 -pkeyopt rsa_mgf1_md:sha256 -out data.txt.enc256

$ pkcs11-tool --module ./libykcs11.so --decrypt -m RSA-PKCS-OAEP --id 1 -i /tmp/data.txt.enc256 --mgf MGF1-SHA256 --hash-algorithm SHA256
Using slot 0 with a present token (0x0)
Logging in to "YubiKey PIV #123456".
Please enter User PIN:
Using decrypt algorithm RSA-PKCS-OAEP
OAEP parameters: hashAlg=SHA256, mgf=MGF1-SHA256, source_type=0, source_ptr=(nil), source_len=0
hello world

Using SHA384 for both:

$ openssl pkeyutl -in data.txt -encrypt -pubin -inkey pub.pem -keyform PEM -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha384 -pkeyopt rsa_mgf1_md:sha384 -out data.txt.enc384

$ pkcs11-tool --module ./libykcs11.so --decrypt -m RSA-PKCS-OAEP --id 1 -i /tmp/data.txt.enc384 --mgf MGF1-SHA384 --hash-algorithm SHA384
Using slot 0 with a present token (0x0)
Logging in to "YubiKey PIV #123456".
Please enter User PIN:
Using decrypt algorithm RSA-PKCS-OAEP
OAEP parameters: hashAlg=SHA384, mgf=MGF1-SHA384, source_type=0, source_ptr=(nil), source_len=0
hello world

Using SHA256 for the OAEP hash and SHA384 for the MGF1 hash:

$ openssl pkeyutl -in data.txt -encrypt -pubin -inkey pub.pem -keyform PEM -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 -pkeyopt rsa_mgf1_md:sha384 -out data.txt.enc256-mgf384

$ pkcs11-tool --module ./libykcs11.so --decrypt -m RSA-PKCS-OAEP --id 1 -i /tmp/data.txt.enc256-mgf384 --mgf MGF1-SHA384 --hash-algorithm SHA256
Using slot 0 with a present token (0x0)
Logging in to "YubiKey PIV #123456".
Please enter User PIN:
Using decrypt algorithm RSA-PKCS-OAEP
OAEP parameters: hashAlg=SHA256, mgf=MGF1-SHA384, source_type=0, source_ptr=(nil), source_len=0
hello world

And finally your last example, in the "update". You're using openssl rsautl which defaults to using SHA1 for both hashes but you are using SHA256 for both hashes during decryption. Correctly setting the algorithms produces the expected result:

$ openssl rsautl -encrypt -oaep -keyform PEM -pubin -inkey pub.pem -in data.txt -out data.txt.enc

$ pkcs11-tool --module ./ykcs11/libykcs11.so --decrypt -m RSA-PKCS-OAEP --id 1 -i /tmp/data.txt.enc --mgf MGF1-SHA1 --hash-a SHA-1
Using slot 0 with a present token (0x0)
Logging in to "YubiKey PIV #123456".
Please enter User PIN:
Using decrypt algorithm RSA-PKCS-OAEP
OAEP parameters: hashAlg=SHA-1, mgf=MGF1-SHA1, source_type=0, source_ptr=(nil), source_len=0
hello world
mouse07410 commented 3 years ago

Thank you!!

The problem turned out to be the undocumented by OpenSSL -pkeyopt parameter rsa_oaep_md:. I wonder where you found it!

After adding -pkeyopt rsa_oaep_md:sha384 to my SHA384-using tests, everything began working correctly.

a-dma commented 3 years ago

Glad to hear it is solved and that everything works as expected.

It is indeed undocumented, I hadn't realized that. There's this issue but it is still open. I'm not entirely sure what the "correct" way to invoke this would be.

Probably good to have the option name spelled out in here for the future.

mouse07410 commented 3 years ago

I'm not entirely sure what the "correct" way to invoke this would be.

The only correct way to invoke it (when you want to use hash other than SHA-1) is exactly what you and I did. ;-)

mouse07410 commented 3 years ago

I also want to compliment you on achieving a milestone - at this point YKCS11 capabilities exceed those of OpenSC.

a-dma commented 3 years ago

Thanks, appreciate it. It's considerably easier to handle things when you have to support only one product, and the folks at OpenSC do an admirable job, but thank you!