TrevorM
TrevorM

Reputation: 11

GlobalPlatform UICC SCP03 AES Card Challenge

I am implementing SCP03 in a test tool for GlobalPlatform UICC (2.2.1) cards. The code for the tool is written in C# and I am attempting to use the dotNet AES class.

The problem I have is that my code cannot reproduce the card challenge sent by the card. I am not sure if this is due to the data I am providing to the AES encryptor, or whether I am not using the AES encryptor correctly.

I have based the code on the GP Spec 2.2 Amendment-D which is a little vague, at least in the process of generating the card challenge and doesn't specify the Initial Vector.

The derivation data I create is as follows:-

00 00 00 00 00 00 00 00 00 00 00 - 11 bytes all zero

02 - card challenge identifier

00 - separator

00 40 - length

01 - counter

00 00 02 - key sequence counter (received from the card)

A0 00 00 01 51 00 00 00 - appID of the currently selected application (the ISD)

80 00 00 00 00 - padding to 32 bytes.

The Initial Vector (IV) I have set to be 16 bytes of zeroes.

For creation of the card challenge I am using the static key K-ENC which has the value:-

40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F

The card challenge (verified as correct) returned by the card is:-

83 FA 04 2C 5C 10 F7 78

The code I have written to do reproduce the card challenge is:-

public static Byte []
GenerateCardChallengeScp03
    (   UInt32      seqCounter,
        Byte []     selectedAid,
        Byte []     baseKeyEnc)
{
    Byte []     finalDerivationData = null;
    Byte []     sequenceCounter     = new Byte [3];
    Byte []     derivationData      = new Byte [16];

    for (int i = 0; i < derivationData.Length; i++)
        derivationData [i] = 0x00; 

    sequenceCounter [0] = (Byte) (seqCounter / 0X10000);
    sequenceCounter [1] = (Byte) (seqCounter / 0X100);
    sequenceCounter [2] = (Byte) (seqCounter);

    derivationData [11] = 0x02; // Card challenge
    derivationData [12] = 0x00; // Separator
    derivationData [13] = 0x00; // MSB length
    derivationData [14] = 0x40; // LSB length
    derivationData [15] = 0x01; // Counter

    finalDerivationData = GP_Utils.ConcatenateArrays (derivationData, sequenceCounter);
    finalDerivationData = GP_Utils.ConcatenateArrays (finalDerivationData, selectedAid);

    Byte []                     icv         = new Byte [] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                                                            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
    Byte []                     inputData   = PadForAesEncryption_0x80 (finalDerivationData);
    AesCryptoServiceProvider    aes         = new AesCryptoServiceProvider ();

    aes.BlockSize       = 128;
    aes.FeedbackSize    = 128;
    aes.KeySize         = baseKeyEnc.Length * 8;
    aes.Mode            = CipherMode.CBC;
    aes.Key             = baseKeyEnc;
    aes.IV              = icv;
    aes.Padding         = PaddingMode.None;

    ICryptoTransform    encryptor   = aes.CreateEncryptor ();
    Byte []             outputData  = encryptor.TransformFinalBlock (inputData, 0, inputData.Length);
    Byte []             cardChallenge = new Byte [8];

    for (int i = 0; i < cardChallenge.Length; i++)
        cardChallenge [i] = outputData [i]; 

    return (cardChallenge);
}

So, what am I not doing, or doing incorrectly?

Upvotes: 1

Views: 3619

Answers (2)

k_o_
k_o_

Reputation: 6298

I could verify your card challenge. Please take a look at crypto.c. There is a function called calculate_card_challenge_SCP03 which is using calculate_CMAC_aes. It is C code but you should be able convert it into C# code. I'm using crypto functions from OpenSSL, so there should be something similar for C#.

 static void test_card_challenge_SCP03()
 {
    BYTE sequenceCounter[3] = {0x00, 0x00, 0x02};
    BYTE invokingAID[8] = {0xA0, 0x00, 0x00, 0x01, 0x51, 0x00, 0x00, 0x00};
    BYTE calculatedCardChallenge[8];
    BYTE cardChallenge[8] = {0x83,0xFA,0x04,0x2C,0x5C,0x10,0xF7,0x78};
    OPGP_ERROR_STATUS status;

    status = calculate_card_challenge_SCP03((PBYTE)OPGP_VISA_DEFAULT_KEY, (PBYTE)sequenceCounter, invokingAID, sizeof(invokingAID), calculatedCardChallenge);
    sput_fail_unless(OPGP_ERROR_CHECK(status) == 0, NULL);
    OPGP_LOG_HEX(_T("Calculated card challenge: "), calculatedCardChallenge, 8);
    OPGP_LOG_HEX(_T("Given card challenge: "), cardChallenge, 8);
    sput_fail_unless(memcmp(calculatedCardChallenge, cardChallenge, 8) == 0, "Card Challenge Comparison");
 }

What card are you using for testing? I'm searching for a public available test card with SCP03 support with known keys to implement SCP03. I'm using a Gemalto IDPrime PIV Card 2.0 card from the Gemalto store but this seems to use some secret undocumented keys, undocumented key derivation or is not compatible with the standard.

Upvotes: 0

user2860360
user2860360

Reputation: 33

Not sure if you found an answer yet. If I am not mistaken, you should be calculating calculating card challenge by computing CMAC as per NIST 800-38B and not just plain AES encryption.

Upvotes: 1

Related Questions