Search code examples
javacardglobalplatform

How to calculate Retail-MAC(Single DES Plus Final Triple DES MAC) in SCP02 (Secure channel Protocol 02) with ICV encryption?


I have seen multiple people asking for a help on C-MAC generation(Retail MAC). This question contains the answer as well. This will help your enough time. I have tested this function with real card and it worked fine.


Solution

  • Note:

    1. One can improve the efficiency of function if like.
    2. If you find any improvement please suggest.
    3. Before you start work on SCP 02 with communication in Ext_Atuh as CMAC please check SCP i value.
      1. This function supports ICV encryption for next command.

    public static byte[] generateCmac(byte []apdu,byte[]sMacSessionKey,byte[]icv) throws Exception {

        if(sMacSessionKey.length == 16) {
        byte []temp  = sMacSessionKey.clone();
        sMacSessionKey = new byte[24];
        System.arraycopy(temp,0,sMacSessionKey,0,temp.length);
        System.arraycopy(temp,0,sMacSessionKey,16,8);
        }
    
        byte []cMac = new byte[8];
        byte []padding = {(byte)0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,};
        int paddingRequired  = 8 - (apdu.length) %8;
        byte[] data = new byte[apdu.length + paddingRequired];
        System.arraycopy(apdu, 0, data, 0, apdu.length);
        System.arraycopy(padding, 0, data, apdu.length,paddingRequired);
    
                Cipher cipher = Cipher.getInstance("DESede/CBC/NoPadding");
    
                Cipher singleDesCipher = Cipher.getInstance("DES/CBC/NoPadding",
                    "SunJCE");
                SecretKeySpec desSingleKey = new SecretKeySpec(sMacSessionKey, 0, 8,
                    "DES");
                SecretKey secretKey = new SecretKeySpec(sMacSessionKey, "DESede");
                // Calculate the first n - 1 block. For this case, n = 1
                IvParameterSpec ivSpec = new IvParameterSpec(icv);
                singleDesCipher.init(Cipher.ENCRYPT_MODE, desSingleKey, ivSpec);
                // byte ivForLastBlock[] = singleDesCipher.doFinal(data, 0, 8);
    
                int blocks = data.length / 8;
    
                for (int i = 0; i < blocks - 1; i++) {
                    singleDesCipher.init(Cipher.ENCRYPT_MODE, desSingleKey, ivSpec);
                    byte[] block = singleDesCipher.doFinal(data, i * 8, 8);
                    ivSpec = new IvParameterSpec(block);
                }
    
                int offset = (blocks - 1) * 8;
    
                cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
                cMac = cipher.doFinal(data, offset, 8);
    
                ivSpec = new IvParameterSpec(new byte[8]);
    
                singleDesCipher.init(Cipher.ENCRYPT_MODE, desSingleKey, ivSpec);
                icvNextCommand = singleDesCipher.doFinal(cMac);
                System.out.println("icvNextCommand"+Utility.bytesToHex(icvNextCommand, icvNextCommand.length));
    
                return cMac;
    
        }