def test_mac_iso9797_3() -> None: # SK < 16 bytes with pytest.raises( ValueError, match="pecify valid padding method: 1, 2 or 3.", ): mac.mac_iso9797_3( bytes.fromhex("AAAAAAAAAAAAAAAA"), bytes.fromhex("BBBBBBBBBBBBBBBB"), b"hello world", 4, ) _mac = ( mac.mac_iso9797_3( bytes.fromhex("AAAAAAAAAAAAAAAA"), bytes.fromhex("BBBBBBBBBBBBBBBB"), b"hello world", 1, ) .hex() .upper() ) assert _mac == "78DA3D3DEB48FD4D" _mac = ( mac.mac_iso9797_3( bytes.fromhex("AAAAAAAAAAAAAAAA"), bytes.fromhex("BBBBBBBBBBBBBBBB"), b"hello world", 2, ) .hex() .upper() ) assert _mac == "2BC2D9EDE0CF31F6" _mac = ( mac.mac_iso9797_3( bytes.fromhex("AAAAAAAAAAAAAAAA"), bytes.fromhex("BBBBBBBBBBBBBBBB"), b"hello world", 3, ) .hex() .upper() ) assert _mac == "1789FED38FC05D1B"
def generate_cvc3(icc_cvc3: bytes, track_template: bytes, atc: bytes, un: bytes) -> str: r"""Generate MasterCard Dynamic Card Verification Code (CVC3). Parameters ---------- icc_cvc3 : bytes Binary 16-byte ICC CVC3 key encoded on the card. This key is derived from Issuer Master CVC3 key using either `kd.derive_icc_mk_a` or `kd.derive_icc_mk_b`. Has to be a valid Triple DES key. track_template : bytes Binary track template data. Track2 template consists of: PAN || "D" || EXPIRY DATE || SERVICE CODE || DD || "F"? DD is Discretionary Data up to 13 digits. DD must have value before it's encoded with ATC, UN and CVC3. If the resulting track2 template is odd length then it must be padded with an "F". Track1 template consists of: "B" || PAN || ^LAST/FIRST^ || EXPIRY DATE || SERVICE CODE || DD Track1 template must be encoded as an ASCII byte string, where, for example, character "B" is encoded as "\x42". atc : bytes Binary data from tag 9F36 (Application Transaction Counter). un : bytes Binary data from tag 9F37 (Unpredictable Number). Returns ------- cvc3 : str 5-digit Dynamic Card Verification Code. Compare generated CVC3 against number of available least significant digits of CVC3 received on track1/2. Raises ------ ValueError ICC CVC3 key must be a double length DES key ATC value must be 2 bytes long Unpredictable number must be 4 bytes long Examples -------- >>> from pyemv.cvv import generate_cvc3 >>> from pyemv.kd import derive_icc_mk_a >>> iss_cvc3 = bytes.fromhex("01234567899876543210012345678998") >>> pan = "5123456789012345" >>> psn = "00" >>> icc_cvc3 = derive_icc_mk_a(iss_cvc3, pan, psn) >>> track2 = bytes.fromhex("5123456789012345D35121010000000000000F") >>> atc = bytes.fromhex("005E") >>> un = bytes.fromhex("00000899") >>> generate_cvc3(icc_cvc3, track2, atc, un) '29488' """ if len(icc_cvc3) != 16: raise ValueError("ICC CVC3 key must be a double length DES key") if len(atc) != 2: raise ValueError("ATC value must be 2 bytes long") if len(un) != 4: raise ValueError("Unpredictable number must be 4 bytes long") # IV CVC3 is formed from 2 least significant bytes of CBC-MAC # computed over track template iv_cvc3 = _mac.mac_iso9797_3(icc_cvc3[:8], icc_cvc3[-8:], track_template, 2)[-2:] # CVC3 is formed from 2 least significant bytes of TDES encrypted # ciphertext block block = iv_cvc3 + un + atc cvc3 = _tools.encrypt_tdes_ecb(icc_cvc3, block)[-2:] return str(int(cvc3.hex(), 16))
def generate_command_mac( sk_smi: bytes, command: bytes, length: Optional[int] = None ) -> bytes: r"""Message Authentication Code (MAC) for Issuer Script Integrity Parameters ---------- sk_smi : bytes Binary ICC Session Key for script integrity (MAC). command : bytes Binary command to be MACed; may or may not include command data, e.g: Command Header || { Command Data if present } Some issuers choose to append transaction data, such as ATC and ARQC, to the command between header and data. The transaction data is present for the MAC calculation but not transmitted. Command Header || ATC || ARQC || { Command Data if present } length : int, optional Desired length of AC [4 <= `length` <= 8] (default 8 bytes). Returns ------- mac : bytes Binary command MAC. Raises ------ ValueError Session Key must be a double length DES key Notes ----- During a transaction the host may send an Issuer Script. These Issuer Scripts allow the issuer to block or unblock an application, or unblock or change PIN on the card among other things. These issuer scripts are extracted from the host response and sent to the card as individual commands. These commands contain a MAC value that the card authenticates prior to actioning the script. The MAC can be 4-8 bytes in length. For further details see also: - EMV 4.3 Book 2 Section 9.2 Secure Messaging for Integrity and Authentication - EMV 4.3 Book 2 Section 9.2.1.2 Format 2 - EMV 4.3 Book 2 Section 9.2.3 MAC Computation - EMV 4.3 Book 2 Annex A 1.2 Examples -------- >>> from pyemv import sm >>> sk_smi = bytes.fromhex("0123456789ABCDEFFEDCBA9876543210") >>> command = bytes.fromhex("8424000008") >>> atc = bytes.fromhex("FFFF") >>> arqc = bytes.fromhex("1234567890123456") >>> mac = sm.generate_command_mac(sk_smi, command + atc + arqc) >>> mac.hex().upper() 'E07B8DF1B4184282' """ if len(sk_smi) != 16: raise ValueError("Session Key must be a double length DES key") return _mac.mac_iso9797_3(sk_smi[:8], sk_smi[-8:], command, 2, length)