C# Implementation of Retail MAC Calculation (ISOIEC 9797-1 MAC algorithm 3)
Asked Answered
C

4

5

I am trying to calculate the MAC using

Cryptographic checksums are calculated using ISOIEC 9797-1 MAC algorithm 3 with block cipher DES, zero IV (8 bytes), and 1S09797-1 padding method 2. The MAC length MUST be 8 bytes

from Technical Report PM for Machine Readable Travel Documents offering ICC read-only access Release : 1.1 Date : October 01. 2004.

I am using the example values from the report:

Kenc: AB 94 FD EC F2 67 4F DF B9 B3 91 F8 5D 7F 76 F2

Kmac: 79 62 D9 EC E0 3D 1A CD 4C 76 08 9D CE 13 15 43

eIFD: 72 C2 9C 23 71 CC 9B DB 65 B7 79 B8 E8 D3 7B 29 EC C1 54 AA 56 A8 79 9F AE 2F 49 8F 76 ED 92 F2

However, I am not getting the same MAC and not sure how I need to go about it. My first attempt was:

                MACTripleDES mac = new System.Security.Cryptography.MACTripleDES(Kmac);
                mac.Initialize();
                mac.Padding = PaddingMode.None;
                mac.Key = Kmac;
                mIfd = mac.TransformFinalBlock(eIfd, 0, eIfd.Length);

Result:

mIFD:1C DE 09 70 4C 0D 9B 12

Expected:

mIFD:5F 14 48 EE A8 AD 90 A7

Then I tried to manually do every step as I understand "ISO/IEC 9797-1 MAC algorithm 3 with block cipher DES, zero IV (8 bytes), and 1S09797-1 padding method 2" with the following: (I based this on Rasmus Faber's answer, but splitting the data into 64bit blocks for Iteration steps)

                byte[] key1 = new byte[8];
                Array.Copy(kMAC, 0, key1, 0, 8);
                byte[] key2 = new byte[8];
                Array.Copy(kMAC, 8, key2, 0, 8);
                Console.WriteLine("key1:{0}", Hex.BytesToSpacedHexString(key1));
                Console.WriteLine("key2:{0}", Hex.BytesToSpacedHexString(key2));

                // Plit the blocks
                byte[] d1 = new byte[8];
                byte[] d2 = new byte[8];
                byte[] d3 = new byte[8];
                byte[] d4 = new byte[8];
                Array.Copy(eIfd, 0, d1, 0, 8);
                Array.Copy(eIfd, 8, d2, 0, 8);
                Array.Copy(eIfd, 16, d3, 0, 8);
                Array.Copy(eIfd, 24, d4, 0, 8);

                DES des1 = DES.Create();
                des1.BlockSize = 64;
                des1.Key = key1;
                des1.Mode = CipherMode.CBC;
                des1.Padding = PaddingMode.None;
                des1.IV = new byte[8];

                DES des2 = DES.Create();
                des2.BlockSize = 64;
                des2.Key = key2;
                des2.Mode = CipherMode.CBC;
                des2.Padding = PaddingMode.None;
                des2.IV = new byte[8];

                // MAC Algorithm 3
                // Initial Transformation 1
                byte[] h1 = des1.CreateEncryptor().TransformFinalBlock(d1, 0, 8);
                // Iteration on the rest of blocks
                // XOR
                byte[] int2 = new byte[8];
                for (int i = 0; i < 8; i++)
                    int2[i] = (byte)(h1[i] ^ d2[i]);
                // Encrypt
                byte[] h2 = des1.CreateEncryptor().TransformFinalBlock(int2, 0, 8);
                // XOR
                byte[] int3 = new byte[8];
                for (int i = 0; i < 8; i++)
                    int3[i] = (byte)(h2[i] ^ d3[i]);
                // Encrypt
                byte[] h3 = des1.CreateEncryptor().TransformFinalBlock(int3, 0, 8);
                // XOR
                byte[] int4 = new byte[8];
                for (int i = 0; i < 8; i++)
                    int4[i] = (byte)(h3[i] ^ d4[i]);
                // Encrypt
                byte[] h4 = des1.CreateEncryptor().TransformFinalBlock(int4, 0, 8);

                // Output Transformation 3
                byte[] h4decrypt = des2.CreateDecryptor().TransformFinalBlock(h4, 0, 8);
                mIfd = des1.CreateEncryptor().TransformFinalBlock(h4decrypt, 0, 8);
                Console.WriteLine("mIFD:{0}", Hex.BytesToSpacedHexString(mIfd));

The output was:

eIFD:72 C2 9C 23 71 CC 9B DB 65 B7 79 B8 E8 D3 7B 29 EC C1 54 AA 56 A8 79 9F AE 2F 49 8F 76 ED 92 F2

key1:79 62 D9 EC E0 3D 1A CD

key2:4C 76 08 9D CE 13 15 43

Result:

mIFD:AA E3 F3 51 32 ED 34 65

Expected:

mIFD:5F 14 48 EE A8 AD 90 A7

In both cases it was different as expected. What am I missing?

Thank you for your time.

Causation answered 1/12, 2013 at 13:31 Comment(1)
i wonder if this question is still active. I was thinking why was MACTripleDES not producing same output as the function. Can someone help me? Thanks in advance.Barragan
C
5

Thanks to owlstead, the trick was that one has to pad even though the data string was exactly 32 bytes. For the people who need the full code. The code to MAC hash for

eIFD:72 C2 9C 23 71 CC 9B DB 65 B7 79 B8 E8 D3 7B 29 EC C1 54 AA 56 A8 79 9F AE 2F 49 8F 76 ED 92 F2

data string looks as follows:

                // Split the 16 byte MAC key into two keys
            byte[] key1 = new byte[8];
            Array.Copy(kMAC, 0, key1, 0, 8);
            byte[] key2 = new byte[8];
            Array.Copy(kMAC, 8, key2, 0, 8);
            Console.WriteLine("key1:{0}", Hex.BytesToSpacedHexString(key1));
            Console.WriteLine("key2:{0}", Hex.BytesToSpacedHexString(key2));

            // Padd the data with Padding Method 2 (Bit Padding)
            System.IO.MemoryStream out_Renamed = new System.IO.MemoryStream();
            out_Renamed.Write(eIfd, 0, eIfd.Length);
            out_Renamed.WriteByte((byte)(0x80));
            while (out_Renamed.Length % 8 != 0)
            {
                out_Renamed.WriteByte((byte)0x00);
            }
            byte[] eIfd_padded = out_Renamed.ToArray();
            Console.WriteLine("eIfd_padded:{0}", Hex.BytesToSpacedHexString(eIfd_padded));

            // Split the blocks
            byte[] d1 = new byte[8];
            byte[] d2 = new byte[8];
            byte[] d3 = new byte[8];
            byte[] d4 = new byte[8];
            byte[] d5 = new byte[8];
            Array.Copy(eIfd_padded, 0, d1, 0, 8);
            Array.Copy(eIfd_padded, 8, d2, 0, 8);
            Array.Copy(eIfd_padded, 16, d3, 0, 8);
            Array.Copy(eIfd_padded, 24, d4, 0, 8);
            Array.Copy(eIfd_padded, 32, d5, 0, 8);

            DES des1 = DES.Create();
            des1.BlockSize = 64;
            des1.Key = key1;
            des1.Mode = CipherMode.CBC;
            des1.Padding = PaddingMode.None;
            des1.IV = new byte[8];

            DES des2 = DES.Create();
            des2.BlockSize = 64;
            des2.Key = key2;
            des2.Mode = CipherMode.CBC;
            des2.Padding = PaddingMode.None;
            des2.IV = new byte[8];

            // MAC Algorithm 3
            // Initial Transformation 1
            byte[] h1 = des1.CreateEncryptor().TransformFinalBlock(d1, 0, 8);
            // Iteration on the rest of blocks
            // XOR
            byte[] int2 = new byte[8];
            for (int i = 0; i < 8; i++)
                int2[i] = (byte)(h1[i] ^ d2[i]);
            // Encrypt
            byte[] h2 = des1.CreateEncryptor().TransformFinalBlock(int2, 0, 8);
            // XOR
            byte[] int3 = new byte[8];
            for (int i = 0; i < 8; i++)
                int3[i] = (byte)(h2[i] ^ d3[i]);
            // Encrypt
            byte[] h3 = des1.CreateEncryptor().TransformFinalBlock(int3, 0, 8);
            // XOR
            byte[] int4 = new byte[8];
            for (int i = 0; i < 8; i++)
                int4[i] = (byte)(h3[i] ^ d4[i]);
            // Encrypt
            byte[] h4 = des1.CreateEncryptor().TransformFinalBlock(int4, 0, 8);
            // XOR
            byte[] int5 = new byte[8];
            for (int i = 0; i < 8; i++)
                int5[i] = (byte)(h4[i] ^ d5[i]);
            // Encrypt
            byte[] h5 = des1.CreateEncryptor().TransformFinalBlock(int5, 0, 8);

            // Output Transformation 3
            byte[] h5decrypt = des2.CreateDecryptor().TransformFinalBlock(h5, 0, 8);
            byte[] mIfd = des1.CreateEncryptor().TransformFinalBlock(h5decrypt, 0, 8);
            Console.WriteLine("mIFD:{0}", Hex.BytesToSpacedHexString(mIfd));
Causation answered 2/12, 2013 at 16:31 Comment(1)
As this is basically a code example for my answer, you can edit it in instead - I'll accept the change.Doscher
D
4

You are missing at least the padding mode. The ICAO technical specification uses bit padding (at least one byte valued 80, then one to seven 00 valued bytes until you reach the end of the block.

Doscher answered 1/12, 2013 at 18:30 Comment(6)
Thank you @owlstead. The data length was exactly on the boundary, was not sure to have a new 64bit block with only one bit set in it. Let me try adding the padding.Causation
The normal method of saying thanks is an accept of the answer. Otherwise the question remains open.Doscher
How to add padding here?Tsushima
@LeonardBertone Eh, what is the part that you don't understand? You append one byte valued 0x80 and then as many zero valued bytes as needed to reach the block size. The total amount of padding should be 8 - pts % 8 where 8 is the block size (for triple DES).Doscher
I understand this, thank you. But the code in the tipic dosen't work in my case. Are there more errors in that code, except padding?Tsushima
I don't know, I haven't tested or validated it. But feel free to ask a new question.Doscher
F
4

Thanks for sharing the code. It is a great example. I have changed the code to address any size of block instead of 5 bytes only. Now in the following code, one can use from a single byte of block to any size of bytes in the block to get MAC.

    private static byte[] getCC_MACNbytes(string Key_MAC, byte[] eIFD, string Init_Vec)
    {
        byte[] Kmac = StringToByteArray(Key_MAC);

        // Split the 16 byte MAC key into two keys
        byte[] key1 = new byte[8];
        Array.Copy(Kmac, 0, key1, 0, 8);
        byte[] key2 = new byte[8];
        Array.Copy(Kmac, 8, key2, 0, 8);

        DES des1 = DES.Create();
        des1.BlockSize = 64;
        des1.Key = key1;
        des1.Mode = CipherMode.CBC;
        des1.Padding = PaddingMode.None;
        des1.IV = new byte[8];

        DES des2 = DES.Create();
        des2.BlockSize = 64;
        des2.Key = key2;
        des2.Mode = CipherMode.CBC;
        des2.Padding = PaddingMode.None;
        des2.IV = new byte[8];

        // Padd the data with Padding Method 2 (Bit Padding)
        System.IO.MemoryStream out_Renamed = new System.IO.MemoryStream();
        out_Renamed.Write(eIFD, 0, eIFD.Length);
        out_Renamed.WriteByte((byte)(0x80));
        while (out_Renamed.Length % 8 != 0)
        {
            out_Renamed.WriteByte((byte)0x00);
        }
        byte[] eIfd_padded = out_Renamed.ToArray();
        int N_bytes = eIfd_padded.Length/8;  // Number of Bytes 

        byte[] d1 = new byte[8];
        byte[] dN = new byte[8];
        byte[] hN = new byte[8];
        byte[] intN = new byte[8];

        // MAC Algorithm 3
        // Initial Transformation 1
        Array.Copy(eIfd_padded, 0, d1, 0, 8);
        hN = des1.CreateEncryptor().TransformFinalBlock(d1, 0, 8);

        // Split the blocks
        // Iteration on the rest of blocks
        for (int j = 1; j<N_bytes; j++)
        {
            Array.Copy(eIfd_padded, (8*j), dN, 0, 8);
            // XOR
            for (int i = 0; i < 8; i++)
                intN[i] = (byte)(hN[i] ^ dN[i]);

            // Encrypt
            hN = des1.CreateEncryptor().TransformFinalBlock(intN, 0, 8);
        }

        // Output Transformation 3
        byte[] hNdecrypt = des2.CreateDecryptor().TransformFinalBlock(hN, 0, 8);
        byte[] mIfd = des1.CreateEncryptor().TransformFinalBlock(hNdecrypt, 0, 8);

        //  Get check Sum CC
        return mIfd;
    }
Fennel answered 29/11, 2014 at 12:59 Comment(0)
A
0

Your problem is incorrect input data, as below.

eIFD:
72C29C2371CC9BDB65B779B8E8D37B29ECC154AA56A8799FAE2F498F76ED92F22

Correct is:

72C29C2371CC9BDB65B779B8E8D37B29ECC154AA56A8799FAE2F498F76ED92F2

And you need h5:

h5 - e
h5 - d
h5 - e2

Result = 5F1448EEA8AD90A7

Anisometric answered 30/9 at 7:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.