mdgboi
mdgboi

Reputation: 23

How to calculate C-MAC from EXTERNAL AUTHENTICATE command apdu?

Thanks to these informations I'm able to calculate EXTERNAL_AUTHENTICATE_data but how exactly can I calculate MAC ?

I know that my default C-MAC key is [40 41 ... 4F]. I have tried to encrypt [84 82 00 00 10]+EXTERNAL_AUTHENTIFICATE apdu with it using 3DES in CBC mode but it doesn't give the MAC value I expect.


With the help of this tutorial, these are the steps I followed to "Calculate C-MAC" :

1 - I take the apdu with EXTERNAL_AUTHENTICATE_data : 8482000010448126B770B27702

2 - I pad this apdu : 8482000010448126B770B27702800000

3 - I encrypt the data with the first 8 bytes of S-MAC key : single DES in CBC mode (key : D1C28C601652A477 / IV : 00 00 00 00 00 00 00 00) result => 25F7DC3B1FEE1B9018CCD8E66A69B560

4 - I encrypt this with the last 8 bytes of S-MAC key : 3DES in EBC mode (key : 0D67AD82D2D2E1C4) result => 11E1B058F0EB6910196A68BF1FBA97AA

Or the result I except is D770D0A0001B05AA

Did I do the retail MAC wrong ?

Upvotes: 2

Views: 704

Answers (2)

Ebrahim Ghasemi
Ebrahim Ghasemi

Reputation: 6126

I've developed a Python class for the SCP02. The MAC-related secions may answer your question:

from Crypto.Cipher import DES3,DES

ZERO_IV_8 = b"\x00\x00\x00\x00\x00\x00\x00\x00"
last_mac = None


def pad_80(data_list):
    reminder = len(data_list) % 8 
    if reminder != 0:
        return data_list + [0x80,] + [0x00 for i in range(8 - reminder - 1)] 
    else:
        return data_list + [0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]


def calc_mac(command):
    padded_command = self.pad_80(command)
    mac_in = bytes.fromhex(toHexString(padded_command).replace(" ", ""))

    iv = ZERO_IV_8
    if last_mac != None:
        cipher = DES.new(session_mac[:8], DES3.MODE_ECB)
        iv = cipher.encrypt(last_mac)

    cipher = DES.new(session_mac[:8], DES3.MODE_CBC, iv) 
    step1 = cipher.encrypt(mac_in)

    cipher = DES.new(session_mac[8:16], DES3.MODE_ECB)
    step2 = cipher.decrypt(step1[-8:])

    cipher = DES.new(session_mac[:8], DES3.MODE_ECB)
    mac = list(bytes(cipher.encrypt(step2[-8:])))

    last_mac = bytes.fromhex(toHexString(mac).replace(" ", ""))

    return mac

The variable session_mac is a 16-bytes byte strings which contains session MAC key. You also need to keep the calculated MAC in a variable (in my case last_mac) to use it as IV for next command MAC calculation.

The calc_mac function input (command) is a list of numbers as the APDU Command. You need to fix CLA and LC values before MAC calculation.

Upvotes: 2

Maarten Bodewes
Maarten Bodewes

Reputation: 94038

I see two mistakes:

  1. the last block needs to be encrypted three times instead of 4 (single DES + 3DES is one first round too many);
  2. you are not using both keys for your 3DES, in which case it probably reverts to single DES encryption.

As for the last point, it performs DES encrypt with single key given, decrypt with single key given and a final encrypt again. So two of these operations are cancelling each other out. This is deliberate in the design of triple 3DES to make a single HW implementation be able to do both.

I think this is mentioned in the earlier question, but beware, MAC generally uses bit padding: a byte with 0x80 always padded, with as many 0x00 bytes to reach the next block boundary.

Note that Bouncy Castle contains Retail MAC.

Upvotes: 1

Related Questions