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

c#, cryptography, hash, tripledes

Solution

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));

Problem

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.

Original source

Related problems