kaoh / globalplatform

C library + command-line for Open- / GlobalPlatform smart cards
https://kaoh.github.io/globalplatform/
Other
72 stars 30 forks source link

put key with parameter key type #73

Closed yp25 closed 1 year ago

yp25 commented 1 year ago

is there any available parameter options for passing key type (for example "DES" 0x80 or "AES" 0x88 ) on put_sc_key command ?

I have java card with default SCP02-i55, after enable secure channel protocol to support both SCP02 and SCP03, I need to add the new key for SCP03 (with new key version = 0x30 --> refer to the chip preperso administration document, 0x30 means AES key) . I open the SCP02 communication and try to use this command to add key with key version 48. put_sc_key -keyver 0 -newkeyver 48 -mac_key 404142434445464748494a4b4c4d4e4e -enc_key 404142434445464748494a4b4c4d4e4e -kek_key 404142434445464748494a4b4c4d4e4e and got error 6A80.

when I add DES key with different key version 0x20 --> 0x20 means DES key), the put_sc_key command works. So I believe the error occurred because the key type should be AES, not DES.

From globalplatform.c source code, function put_secure_channel_keys, the key type set to the default value like this :

`

if (cardInfo.specVersion == OP_201) { keyType = OP201_KEY_TYPE_DES_ECB; } else { keyType = GP211_KEY_TYPE_DES; } if (secInfo->secureChannelProtocol == GP211_SCP03) { keyType = GP211_KEY_TYPE_AES; }

`

is it possible to add new feature to enable passing key type parameter ? Thank you Mr. @kaoh

koh-osug commented 1 year ago

Right now like you analyzed it correctly, the key type is fixed. I will try to add this feature to the road map. Please try this in the meanwhile:

open_sc -scp 3

This sets SCP03 explicitly and should then use an AES key. Or is the key you are adding the initial SCP03 key which has to be stored with SCP02?

See gpshell.1.md

yp25 commented 1 year ago

Or is the key you are adding the initial SCP03 key which has to be stored with SCP02?

yes. open communication with SCP02, then add SCP03 key.

btw I try to modify the source code to test keytype AES 0x88 / 136 . but got response 6982. `put_sc_key -keytype 136 -keyver 0 -newkeyver 48 -mac_key B70E2A9F15335C0FE7A8B15A9F2C6266 -enc_key FB818CB44F35D5BEC3038234D0AC015C -kek_key 7B73940892323802F3A95601F797BEB5

Key Type 0x88 Command --> 80D80081433088100062F72B614E754E841D3B18C07D503303B90A6788102DAE2614F38C384EA1A932A960D3A6E40384A6068810DAD165FBACD4EF1FC56D9B5EE084D598032B243600 Wrapped command --> 80D80081433088100062F72B614E754E841D3B18C07D503303B90A6788102DAE2614F38C384EA1A932A960D3A6E40384A6068810DAD165FBACD4EF1FC56D9B5EE084D598032B243600 Response <-- 6982`

need to check what caused the problem. because I try to add key with JCShell and it's working `put-keyset -m add 48 =>80D80081463088111099B6E37FA3D8C65CFC23939D2D0358F3039473E7881110B61E2463D83F7FD803CE2109B854EB9703EFAF1A88111090262752FF9DE22CD27D4383B5727F7C030454DB00

<=309473E7EFAF1A0454DB9000

koh-osug commented 1 year ago

Have you patched gpshell and the globalplatform library to accept the key type as parameter? As simple test you could also hard code the key type in the function put_secure_channel_keys to see if it works. Has JCShell an option to see the send data in plain? I think the data is already encrypted. If you could see the plain data, we would see which identifier for the key type works.

koh-osug commented 1 year ago

I created a stub to support this:

https://github.com/kaoh/globalplatform/tree/put_sc_with_key_type

This is untested, but maybe it works directly. The new option is called "keyType" and the type is passed as hex string, e.g. 88 for AES.

yp25 commented 1 year ago

Have you patched gpshell and the globalplatform library to accept the key type as parameter? As simple test you could also hard code the key type in the function put_secure_channel_keys to see if it works.

Yes. already patched gpshell and globalplaform library to accept the key type as parameter. mine using '-keytype' with decimal number, and yours '-keyType' with hexadecimal number . from the log, the key type is correct. tried to hard code the key type and still got same error. I tried your latest code "put_sc_with_key_type" and also got same error when put_sc_key with keyType AES

this one is new DES key with newkeyversion 0x20 / 32 decimal  :

put_sc_key -keyType 80 -keyver 0 -newkeyver 32 -mac_key 404142434445464748494A4B4C4D4E4F -enc_key 404142434445464748494A4B4C4D4E4F -kek_key 404142434445464748494A4B4C4D4E4F
Command --> 80D80081432080102FD3E7126616DBFDE95BB87AEAADAB18038BAF4780102FD3E7126616DBFDE95BB87AEAADAB18038BAF4780102FD3E7126616DBFDE95BB87AEAADAB18038BAF4700
Wrapped command --> 80D80081432080102FD3E7126616DBFDE95BB87AEAADAB18038BAF4780102FD3E7126616DBFDE95BB87AEAADAB18038BAF4780102FD3E7126616DBFDE95BB87AEAADAB18038BAF4700
Response <-- 208BAF478BAF478BAF479000
Unwrapped response <-- 208BAF478BAF478BAF479000
command time: 188 ms

and this one is new AES key with newkeyversion 0x30 / 48 decimal :

put_sc_key -keyType 88 -keyver 0 -newkeyver 48 -mac_key 404142434445464748494A4B4C4D4E4F -enc_key 404142434445464748494A4B4C4D4E4F -kek_key 404142434445464748494A4B4C4D4E4F
Command --> 80D80081433088102FD3E7126616DBFDE95BB87AEAADAB18038BAF4788102FD3E7126616DBFDE95BB87AEAADAB18038BAF4788102FD3E7126616DBFDE95BB87AEAADAB18038BAF4700
Wrapped command --> 80D80081433088102FD3E7126616DBFDE95BB87AEAADAB18038BAF4788102FD3E7126616DBFDE95BB87AEAADAB18038BAF4788102FD3E7126616DBFDE95BB87AEAADAB18038BAF4700
Response <-- 6982
Unwrapped response <-- 6982
put_secure_channel_keys() returns 0x80206982 (6982: Command not allowed - Security status not satisfied.)

Has JCShell an option to see the send data in plain? I think the data is already encrypted. If you could see the plain data, we would see which identifier for the key type works.

will try to find out. Thank you for your help Mr. @koh-osug

yp25 commented 1 year ago

from JCShell log, I try to figure it out :

  1. open communication (init update & ext auth) SCP02
  2. put sc key with key type AES (0x88), new ENC/MAC/DEK key = 404142434445464748494A4B4C4D4E4F , session DEK key = C61DF24D2C9A770E32E969984B5B7074, new key version = 0x30
  3. the command apdu = 80D800814630881110B23034FA4329DE9AD8E91583618813B303504A77881110B23034FA4329DE9AD8E91583618813B303504A77881110B23034FA4329DE9AD8E91583618813B303504A7700

CLA INS P1 P2 => 80D80081 LC => 46 Data => 30 --> key version 88 11 10 B23034FA4329DE9AD8E91583618813B303 504A77 88 11 10 B23034FA4329DE9AD8E91583618813B303 504A77 88 11 10 B23034FA4329DE9AD8E91583618813B303 504A77 LE = 00

(88= AES; 11 = 10+1; 10 = new encrypted key length; key data = new ENC key encrypted using 3DES ECB with key = session DEK key; key check value using AES

B23034FA4329DE9AD8E91583618813B303 = output from encrypt 3DES ECB, key = C61DF24D2C9A770E32E969984B5B7074, data input = 404142434445464748494A4B4C4D4E4F

key check value AES = 504A77 = first 3 byte output from encrypt AES algorithm with key = new ENC key = 404142434445464748494A4B4C4D4E4F, data input = 01010101010101010101010101010101. output = 504A77E6E457DD861E066F7804281A5F

try to find other reference from GPSession.java - encodeKey and GPCrypto.java - kcv_aes and GPCrypto.java - one_bytes_16

seems that if key type = AES, need to add '11' after key type and calculate key check value using AES. so I think need to check and modify the function of get_key_data_field and calculate_key_check_value

koh-osug commented 1 year ago

I have already added keys successfully. The 0x11 should be already be set with encrypted_key_length + 1:

    keyDataField[i++] = keyType;
    if (isSensitive) {
        status = encrypt_sensitive_data(secInfo, keyData, keyDataLength, encrypted_key, &encrypted_key_length);
        if (OPGP_ERROR_CHECK(status)) {
            goto end;
        }
        if (encrypted_key_length != keyDataLength || secInfo->secureChannelProtocol == GP211_SCP03) {
            // + 1 byte key component length field
            keyDataField[i++] = encrypted_key_length + 1;
            keyDataField[i++] = keyDataLength;

Can you please check if the JCShell output differs from gpshell (expect the different encrypted data because of the session key)?

yp25 commented 1 year ago

I have already added keys successfully

how you did it ? Can I request a copy of the complete logs starting from open_sc ?

I did a little modification in get_key_data_field function --> add || keyType == GP211_KEY_TYPE_AES, because the current SCP is SCP02, not SCP03 (secInfo->secureChannelProtocol => GP211_SCP02), so with your original code need to modified like this :

if (secInfo->secureChannelProtocol == GP211_SCP03  || keyType == GP211_KEY_TYPE_AES ) {
    // the length of the key component is always included
    sizeNeeded++;
}

Can you please check if the JCShell output differs from gpshell (expect the different encrypted data because of the session key)?

I'm currently at home and forgot to bring the java card. I will reply to you next Monday. But I remember after I did a little modification as above, the key check value still different. the JCShell calculated 504A77, but the gpshell resulting in different value calculations. I will check again on Monday.

koh-osug commented 1 year ago

I have used the putKeysSCP03.txt with an Infineon and NXP card. I have to find time to rerun it next week. I have added your fix.

yp25 commented 1 year ago

Hi Mr. @koh-osug ,

I have added AES keys successfully (with open_sc SCP02). What I did :

  1. Function calculate_key_check_value (crypto.c) needs to be modified to accept keyType parameter == GP211_KEY_TYPE_AES
    OPGP_ERROR_STATUS calculate_key_check_value(GP211_SECURITY_INFO *secInfo,
    BYTE keyType,
    PBYTE keyData,
    DWORD keyDataLength,
    BYTE keyCheckValue[3]) {
    OPGP_ERROR_STATUS status;
    BYTE dummy[16];
    DWORD dummyLength;
    BYTE keyCheckTest[16];
    OPGP_LOG_START(_T("calculate_key_check_value"));
    memset(keyCheckTest, 0, 16);
    if (secInfo->secureChannelProtocol == GP211_SCP03 || keyType == GP211_KEY_TYPE_AES) {
        memset(keyCheckTest, 0x01, sizeof(keyCheckTest));
        status = calculate_enc_ecb_SCP03(keyData, keyDataLength, keyCheckTest, 16, dummy, &dummyLength);
    }
    else {
        status = calculate_enc_ecb_two_key_triple_des(keyData, keyCheckTest, 8, dummy, &dummyLength);
    }
    if (OPGP_ERROR_CHECK(status)) {
        goto end;
    }
    memcpy(keyCheckValue, dummy, 3);
    { OPGP_ERROR_CREATE_NO_ERROR(status); goto end; }
    end:
    OPGP_LOG_END(_T("calculate_key_check_value"), status);
    return status;
    }
  2. Function get_key_data_field (crypto.c) also needs to be modified || keyType == GP211_KEY_TYPE_AES + calling calculate_key_check_value with keyType parameter

    OPGP_ERROR_STATUS get_key_data_field(GP211_SECURITY_INFO *secInfo,
                             PBYTE keyData,
                             DWORD keyDataLength,
                             BYTE keyType,
                             BYTE isSensitive,
                             PBYTE keyDataField,
                             PDWORD keyDataFieldLength,
                             BYTE keyCheckValue[3]) {
    OPGP_ERROR_STATUS status;
    DWORD sizeNeeded=0, i=0;
    BYTE encrypted_key[32];
    DWORD encrypted_key_length;
    BYTE keyCheckTest[16];
    OPGP_LOG_START(_T("get_key_data_field"));
    memset(keyCheckTest, 0, 16);
    // key type + length + key data + length + key check value
    sizeNeeded = 1 + 1 + keyDataLength + 1;
    if (secInfo->secureChannelProtocol == GP211_SCP03 || keyType == GP211_KEY_TYPE_AES) {
        // the length of the key component is always included
        sizeNeeded++;
    }
    if (isSensitive) {
        // 3 byte key check value
        sizeNeeded+=3;
    }   
    if (sizeNeeded > *keyDataFieldLength) {
        OPGP_ERROR_CREATE_ERROR(status, OPGP_ERROR_INSUFFICIENT_BUFFER, OPGP_stringify_error(OPGP_ERROR_INSUFFICIENT_BUFFER));
        goto end;
    }
    // set key type
    keyDataField[i++] = keyType;
    if (isSensitive) {
        status = encrypt_sensitive_data(secInfo, keyData, keyDataLength, encrypted_key, &encrypted_key_length);
        if (OPGP_ERROR_CHECK(status)) {
            goto end;
        }
        if (encrypted_key_length != keyDataLength || secInfo->secureChannelProtocol == GP211_SCP03 || keyType == GP211_KEY_TYPE_AES) {
            // + 1 byte key component length field
            keyDataField[i++] = encrypted_key_length + 1;
            keyDataField[i++] = keyDataLength;
        }
        else {
            // not padded and no length must be specified
            keyDataField[i++] = encrypted_key_length;
        }
        memcpy(keyDataField+i, encrypted_key, encrypted_key_length);
        i+= encrypted_key_length;
    }
    else {
        keyDataField[i++] = (BYTE)keyDataLength;
        // not sensitive - copy directly, no key component length is needed
        memcpy(keyDataField+i, keyData, keyDataLength);
        i+=keyDataLength;
    }
    // we always use key check values
    keyDataField[i++] = 0x03; // length of key check value
    status = calculate_key_check_value(secInfo, keyType, keyData, keyDataLength, keyCheckValue);
    if (OPGP_ERROR_CHECK(status)) {
        goto end;
    }
    memcpy(keyDataField+i, keyCheckValue, 3);
    i+=3;
    *keyDataFieldLength = i;
    { OPGP_ERROR_CREATE_NO_ERROR(status); goto end; }
    end:
    
    OPGP_LOG_END(_T("get_key_data_field"), status);
    return status;
    }

with this modification, now put_key_sc works well

mode_211
enable_trace
enable_timer
establish_context
command time: 79 ms
card_connect -reader "OMNIKEY CardMan 5x21-CL 1"
reader name OMNIKEY CardMan 5x21-CL 1
command time: 0 ms
select -AID A000000151000000
Command --> 00A4040008A000000151000000
Wrapped command --> 00A4040008A000000151000000
Response <-- 9000
Unwrapped response <-- 9000
command time: 46 ms
open_sc -scp 2 -scpimpl 0x55 -security 0 -keyind 0 -keyver 0 -mac_key ... -enc_key ... -kek_key ..
Command --> 8050000008C6465FD8791A537600
Wrapped command --> 8050000008C6465FD8791A537600
Response <-- 000083456E6E6E505243FF020004E40C8EC0A2EECF44EE385F587F7A9000
Unwrapped response <-- 000083456E6E6E505243FF020004E40C8EC0A2EECF44EE385F587F7A9000
Command --> 8482000010B079F21DDF034D01E936464341FDDC60
Wrapped command --> 8482000010B079F21DDF034D01E936464341FDDC60
Response <-- 9000
Unwrapped response <-- 9000
command time: 297 ms
put_sc_key -keyType 88 -keyver 0 -newkeyver 48 -mac_key 404142434445464748494A4B4C4D4E4F -enc_key 404142434445464748494A4B4C4D4E4F -kek_key 404142434445464748494A4B4C4D4E4F
Command --> 80D800814630881110B23034FA4329DE9AD8E91583618813B303504A77881110B23034FA4329DE9AD8E91583618813B303504A77881110B23034FA4329DE9AD8E91583618813B303504A7700
Wrapped command --> 80D800814630881110B23034FA4329DE9AD8E91583618813B303504A77881110B23034FA4329DE9AD8E91583618813B303504A77881110B23034FA4329DE9AD8E91583618813B303504A7700
Response <-- 30504A77504A77504A779000
Unwrapped response <-- 30504A77504A77504A779000
koh-osug commented 1 year ago

Awesome! I have to find out, when my SCP03 card were working without this. When I publish the next release so you want to be attributed? What name / email should I use then?

koh-osug commented 1 year ago

I have integrated your changes. Please check. I found the explanation also why it works in my case: I was using SCP03 as protocol, not SCP02 for putting a SCP03 key.

yp25 commented 1 year ago

Awesome! I have to find out, when my SCP03 card were working without this. When I publish the next release so you want to be attributed? What name / email should I use then?

Thank you for considering attribution in the next release. Please use yp25 / yudhipra25@gmail.com.

I have integrated your changes. Please check. I found the explanation also why it works in my case: I was using SCP03 as protocol, not SCP02 for putting a SCP03 key.

I have checked the latest commit - put_sc_with_key_type branch. please modify line 1427 crypto.c with || keyType == GP211_KEY_TYPE_AES)

if (encrypted_key_length != keyDataLength || secInfo->secureChannelProtocol == GP211_SCP03 || keyType == GP211_KEY_TYPE_AES)

without || keyType == GP211_KEY_TYPE_AES, will result error like this : put_secure_channel_keys() returns 0x80304000 (A used buffer is too small.)

Thank you for your effort & hard work Mr. @koh-osug

koh-osug commented 1 year ago

Updated. Please also see the AUTHORS file in gpshell / globalplatform for the attribution.

yp25 commented 1 year ago

Thank you Mr. @koh-osug