My goal is to extract data from a passport in which I already have the MRZ. I'm starting on a basis of a CardPeek lua script that works and trying to implement that in C++ with the CNG API. I 've gotten into a checkpoint that, given a K_MAC (derived from the MRZ) and the "E_IFD" (ENC(RND_IFD || RND_IC || K_IFD)) I have to implement the "retail MAC". At a point, the K_MAC is D6E358ECDA0015E273EEEBB5B865082F and the E_IFD is DE3737718D12260E21D6237319004D54AD7239F6F56EDD41725431B2EB4000FB. My code in Win32 API so far and the lua script produces the same results. At this point, the lua script is: local CBC_MAC = crypto.create_context(crypto.ALG_ISO9797_M3+crypto.PAD_ISO9797_P2,KM_MAC); m_ifd = crypto.mac(CBC_MAC,e_ifd) And this produces a value 0A2B038851996635 Now I 'm trying to implement this in C++ but something is wrong and I can't figure out why (error handling removed). bool ComputeRetailMAC(const BYTE* k_mac, const BYTE* data, size_t dataLen, BYTE* mac) { BCRYPT_ALG_HANDLE hAlg = NULL; BCRYPT_KEY_HANDLE hKey = NULL; NTSTATUS status; bool result = false; // 3DES key: K1 || K2 || K1 (24 bytes) BYTE k_mac_24[24] = {}; memcpy(k_mac_24, k_mac, 16); memcpy(k_mac_24 + 16, k_mac, 8); status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_3DES_ALGORITHM, NULL, 0); // Set chaining mode to CBC status = BCryptSetProperty(hAlg, BCRYPT_CHAINING_MODE, (PUCHAR)BCRYPT_CHAIN_MODE_CBC, wcslen(BCRYPT_CHAIN_MODE_CBC)*2, 0); // Generate symmetric key status = BCryptGenerateSymmetricKey(hAlg, &hKey, NULL, 0, (PUCHAR)k_mac_24, sizeof(k_mac_24), 0); // CBC IV = 0 BYTE iv[8] = { 0 }; // Pad data to 8-byte boundary with zeroes size_t paddedLen = (dataLen + 7) & ~7; if (dataLen % 8 == 0) paddedLen += 8; std::vector<BYTE> paddedData(paddedLen); memcpy(paddedData.data(), data, dataLen); paddedData[dataLen] = 0x80; if (paddedLen > dataLen + 1) memset(paddedData.data() + dataLen + 1, 0x00, paddedLen - dataLen - 1); // Encrypt DWORD cbResult = 0; std::vector<BYTE> encrypted(paddedLen); status = BCryptEncrypt(hKey, paddedData.data(), (ULONG)paddedLen, NULL, iv, sizeof(iv), encrypted.data(), (ULONG)paddedLen, &cbResult, 0); memcpy(mac, encrypted.data() + cbResult - 8, 8); // Last block is MAC ... cleanup here } I also found somewhere that the padding must add +8 bytes if already aligned. Either way I can't get the (correct) result of 0A2B038851996635. What am I doing wrong? Edit: I tried it with python and it returns the same as my Win32 version from Crypto.Cipher import DES3 def pad_iso7816(data, block_size): padding_len = block_size - len(data) % block_size if padding_len == 0: padding_len = block_size padding = bytes([0x80] + [0x00] * (padding_len - 1)) return data + padding def compute_retail_mac(k_mac_hex, e_ifd_hex): k_mac = bytes.fromhex(k_mac_hex) e_ifd = bytes.fromhex(e_ifd_hex) # Expand to 24-byte key: K1 || K2 || K1 k_mac_24 = k_mac + k_mac[:8] # Pad using ISO/IEC 7816-4 padded_data = pad_iso7816(e_ifd, 8) # Encrypt with 3DES CBC, IV = 0 cipher = DES3.new(k_mac_24, DES3.MODE_CBC, iv=bytes(8)) encrypted = cipher.encrypt(padded_data) # Retail MAC = last 8 bytes return encrypted[-8:].hex() # Test values k_mac = "D6E358ECDA0015E273EEEBB5B865082F" e_ifd = "DE3737718D12260E21D6237319004D54AD7239F6F56EDD41725431B2EB4000FB" mac = compute_retail_mac(k_mac, e_ifd) print("Retail MAC:", mac) So, it's the LUA implementation that does something weird, but what? And why it works with the passport? Here's the copy from cardseek function epass_create_session_keys() local sw, resp local rnd_icc local rnd_ifd local k_ifd = bytes.new(8,"00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00") local S local iv = bytes.new(8,"00 00 00 00 00 00 00 00") local TDES_CBC = crypto.create_context(crypto.ALG_DES2_EDE_CBC+crypto.PAD_ZERO,KM_ENC); local e_ifd local m_ifd rnd_ifd = bytes.new(8,"00 00 00 00 00 00 00 00") sw, rnd_icc = card.send(bytes.new(8,"00 84 00 00 08")) if (sw~=0x9000) then return false end S = rnd_ifd .. rnd_icc .. k_ifd log.print(log.INFO,"***XXX*** RND_IFD " .. tostring(rnd_ifd)) log.print(log.INFO,"***XXX*** RND_ICC " .. tostring(rnd_icc)) log.print(log.INFO,"***XXX*** K_IFD " .. tostring(k_ifd)) log.print(log.INFO,"***XXX*** S " .. tostring(S)) e_ifd = crypto.encrypt(TDES_CBC,S,iv) local CBC_MAC = crypto.create_context(crypto.ALG_ISO9797_M3+crypto.PAD_ISO9797_P2,KM_MAC); m_ifd = crypto.mac(CBC_MAC,e_ifd) log.print(log.INFO,"***MAC*** KM_MAC " .. tostring(KM_MAC)) log.print(log.INFO,"***MAC*** E_IFD " .. tostring(e_ifd)) log.print(log.INFO,"***MAC*** MAC " .. tostring(m_ifd)) cmd_data = e_ifd..m_ifd sw, resp = card.send(bytes.new(8,"00 82 00 00 28",cmd_data,0x28)) if (sw==0x6300) then log.print(log.WARNING,"Perhaps you didn't enter the right MRZ data for this passport") end if (sw~=0x9000) then return false end R = crypto.decrypt(TDES_CBC,bytes.sub(resp,0,31),iv) k_icc = bytes.sub(R,16,31) k_seed = bytes.new(8) for i=0,15 do k_seed = bytes.concat(k_seed,bit.XOR(k_ifd:get(i),k_icc:get(i))) end SSC = bytes.sub(rnd_icc,4,7)..bytes.sub(rnd_ifd,4,7) KS_ENC, KS_MAC = epass_key_derivation(k_seed) return true end Best, Continue reading...