def derive(x: bytes, y: bytes, j: int) -> bytes: """Map two 16-byte numbers X and Y and an integer j onto a 16-byte number.""" j_mod_b = int.to_bytes(j % branch_factor, 8, "big") # (DES3(X)[YL XOR (j mod b)] l_data = _tools.xor(y[:8], j_mod_b) l_data = _tools.encrypt_tdes_ecb(x, l_data) # DES3(X)[YR XOR (j mod b) XOR 'F0'] r_data = _tools.xor(y[8:], j_mod_b) r_data = _tools.xor(r_data, b"\x00" * 7 + b"\xF0") r_data = _tools.encrypt_tdes_ecb(x, r_data) return l_data + r_data
def derive_visa_sm_sk(icc_mk: bytes, atc: bytes) -> bytes: r"""Visa Secure Messaging Session Key Derivation. Parameters ---------- icc_mk : bytes Binary ICC Master Key to derive session key from. Has to be a valid DES key. atc : bytes Binary data from tag 9F36 (Application Transaction Counter). Returns ------- sk : bytes Binary 16-byte Session Key. Raises ------ ValueError ICC Master Key must be a double length DES key ValueError ATC value must be 2 bytes long Examples -------- >>> from pyemv import kd >>> mk = bytes.fromhex("0123456789ABCDEFFEDCBA9876543210") >>> atc = bytes.fromhex("001C") >>> sk = kd.derive_visa_sm_sk(mk, atc) >>> sk.hex().upper() '0123456789ABCDF2FEDCBA987654CDF2' """ if len(icc_mk) != 16: raise ValueError("ICC Master Key must be a double length DES key") if len(atc) != 2: raise ValueError("ATC value must be 2 bytes long") # SK Key A (i.e. first 8 bytes) = r xor MK Key A r = b"\x00" * 6 + atc sk_a = _tools.xor(r, icc_mk[:8]) # SK Key B (i.e. second 8 bytes) = r xor MK Key B r = b"\x00" * 6 + _tools.xor(atc, b"\xff\xff") sk_b = _tools.xor(r, icc_mk[8:]) return _tools.adjust_key_parity(sk_a + sk_b)
def derive_icc_mk_b(iss_mk: bytes, pan: Union[bytes, str], psn: Optional[Union[bytes, str]] = None) -> bytes: r"""ICC Master Key Derivation. EMV Option B. Uses PAN, PAN Sequence Number, MK ISS, Triple DES, SHA-1 and decimalisation of hex digits. Parameters ---------- iss_mk : bytes Binary Issuer Master Key to derive ICC Master Key from. Has to be a valid DES key. pan : bytes or str ASCII Application Primary Account Number. psn : bytes or str, optional ASCII 2-digit PAN Sequence Number (default 00). Returns ------- icc_mk : bytes Binary 16-byte ICC Master Key Notes ----- Derived from Issuer Master Key (iss_mk). Uses EMV Option B - Master Key Derivation method which uses the PAN and PAN sequence number, as defined in EMV Book 2, Annex A. When a card is personalised the issuer will take the 3 iss_mk keys and calculate the 3 icc_mk keys to be stored on the card. - icc_mk_ac - used for the transaction cryptograms (ARQC, TC or AAC) - icc_mk_smi - used for Issuer Script Integrity - icc_mk_smc - used for Issuer Script Confidentiality For further details see also: - EMV 4.3 Book 2 Annex A 1.4 Master Key Derivation - EMV 4.3 Book 2 Annex A 1.4.2 Option B Examples -------- >>> from pyemv import kd >>> iss_mk = bytes.fromhex("0123456789ABCDEFFEDCBA9876543210") >>> icc_mk = kd.derive_icc_mk_b(iss_mk, pan="12345678901234567", psn="01") >>> icc_mk.hex().upper() 'AD406D7F6D7570916D75E5DCAB8CF737' """ # For PANs with length of 16 or less method B works as method A if len(pan) <= 16: return derive_icc_mk_a(iss_mk, pan, psn) if psn is None: psn = "00" if isinstance(psn, bytes): psn = psn.decode("ascii") if isinstance(pan, bytes): pan = pan.decode("ascii") # Data A must be an even number of digits, # right-justified, zero-padded from the left. if len(pan) % 2: pan_psn = _binascii.a2b_hex("0" + pan + psn) else: pan_psn = _binascii.a2b_hex(pan + psn) # Hash PAN || PAN sequence digest = _hashlib.sha1(pan_psn).hexdigest() # Get first 16 digits out the hash value. result = "".join(filter(str.isdigit, digest))[:16] # If there are not enough digits, substitute # letters using the following decimalization table: # Input a b c d e f # Table 0 1 2 3 4 5 if len(result) < 16: digest = "".join(filter((lambda x: x in ("abcdef")), digest)) digest = digest.translate({ 97: 48, 98: 49, 99: 50, 100: 51, 101: 52, 102: 53 }) result = result + digest[:16 - len(result)] data_a = _binascii.a2b_hex(result) # Data B is inverted data A data_b = _tools.xor(data_a, b"\xFF" * len(data_a)) icc_mk = _tools.encrypt_tdes_ecb(iss_mk, data_a + data_b) return _tools.adjust_key_parity(icc_mk)
def derive_icc_mk_a(iss_mk: bytes, pan: Union[bytes, str], psn: Optional[Union[bytes, str]] = None) -> bytes: r"""ICC Master Key Derivation. EMV Option A. Uses PAN, PAN Sequence Number, MK ISS, Triple DES. Parameters ---------- iss_mk : bytes Binary Issuer Master Key to derive ICC Master Key from. Has to be a valid DES key. pan : bytes or str ASCII Application Primary Account Number. psn : bytes or str, optional ASCII 2-digit PAN Sequence Number (default 00). Returns ------- icc_mk : bytes Binary 16-byte ICC Master Key. Notes ----- Derived from Issuer Master Key (iss_mk). Uses EMV Option A - Master Key Derivation method which uses the PAN and PAN sequence number, as defined in EMV Book 2, Annex A. When a card is personalised the issuer will take the 3 iss_mk keys and calculate the 3 icc_mk keys to be stored on the card. - icc_mk_ac - used for the transaction cryptograms (ARQC, TC or AAC) - icc_mk_smi - used for Issuer Script Integrity - icc_mk_smc - used for Issuer Script Confidentiality For further details see also: - EMV 4.3 Book 2 Annex A 1.4 Master Key Derivation - EMV 4.3 Book 2 Annex A 1.4.1 Option A Examples -------- >>> from pyemv import kd >>> iss_mk = bytes.fromhex("0123456789ABCDEFFEDCBA9876543210") >>> icc_mk = kd.derive_icc_mk_a(iss_mk, pan="12345678901234567", psn="01") >>> icc_mk.hex().upper() '73AD54688CEF2934B0979857E3C719F1' """ if psn is None: psn = "00" if isinstance(psn, bytes): psn = psn.decode("ascii") if isinstance(pan, bytes): pan = pan.decode("ascii") # Data A must be at most 16 digits, right-justified, # zero-padded from the left. data_a = _binascii.a2b_hex((pan + psn)[-16:].zfill(16)) # Data B is inverted data A data_b = _tools.xor(data_a, b"\xFF" * len(data_a)) icc_mk = _tools.encrypt_tdes_ecb(iss_mk, data_a + data_b) return _tools.adjust_key_parity(icc_mk)
def format_vis_pin_block( icc_mk_ac: bytes, pin: Union[bytes, str], current_pin: Optional[Union[bytes, str]] = None, ) -> bytes: r"""Format VIS PIN block with or without the current PIN. Parameters ---------- icc_mk_ac : bytes Binary 16-byte ICC Master Key for Application Cryptogram Has to be a valid DES key. pin : bytes or str New ASCII Personal Identification Number. current_pin : bytes or str, optional Current ASCII Personal Identification Number (optional). If present VIS PIN block is generated using current PIN. Returns ------- pin_block : bytes Binary 16-byte VIS PIN block Raises ------ ValueError PIN must be between 4 and 12 digits long Current PIN must be between 4 and 12 digits long ICC Master Key for AC must be a double length DES key Examples -------- >>> from pyemv import sm >>> icc_mk_ac = bytes.fromhex("0123456789ABCDEFFEDCBA9876543210") >>> sm.format_vis_pin_block(icc_mk_ac, "1234").hex().upper() '041234FF76543210' """ if len(pin) < 4 or len(pin) > 12: raise ValueError("PIN must be between 4 and 12 digits long") if len(icc_mk_ac) != 16: raise ValueError("ICC Master Key for AC must be a double length DES key") if isinstance(pin, bytes): pin = pin.decode("ascii") if isinstance(current_pin, bytes): current_pin = current_pin.decode("ascii") # 4 right-most bytes of ICC MK AC Key A padded with 0x00 to form an 8-byte block block_a = b"\x00" * 4 + icc_mk_ac[4:8] # PIN length as 1 byte concatenated with PIN then F-padded to form an 8 byte block block_b = len(pin).to_bytes(1, _sys.byteorder) + _binascii.a2b_hex( pin + "F" * (14 - len(pin)) ) pin_block = _tools.xor(block_a, block_b) # Generate VIS PIN block using current PIN if current_pin is not None: if len(current_pin) < 4 or len(current_pin) > 12: raise ValueError("Current PIN must be between 4 and 12 digits long") pin_block = _tools.xor( pin_block, _binascii.a2b_hex(current_pin + "0" * (16 - len(current_pin))) ) return pin_block
def derive_emv2000_tree_sk( icc_mk: bytes, atc: bytes, height: int = 8, branch_factor: int = 4, iv: bytes = b"\x00" * 16, ) -> bytes: r"""EMV2000-Tree Session Key Derivation. Parameters ---------- icc_mk : bytes Binary ICC Master Key to derive session key from. Has to be a valid DES key. atc : bytes Binary data from tag 9F36 (Application Transaction Counter). height : int Height value used for EMV-Tree derivation. Height controls the number of levels of intermediate keys in the tree excluding the base level. Set to either 8 or 16. The specification recommends value 8. Defaults to 8. branch_factor : int Branch factor value used for EMV-Tree derivation. Branch factor controls number of "child" keys a "parent" key derives. The specification recommends value 4. Defaults to 4. iv : bytes 16-byte binary initialization vector used for EMV-Tree derivation. The specification recommends IV value of zeros. Defaults to 0s. Returns ------- sk : bytes Binary 16-byte Session Key. Raises ------ ValueError ICC Master Key must be a double length DES key ValueError ATC value must be 2 bytes long ValueError Initialization vector value must be 16 bytes long ValueError Number of possible session keys must exceed maximum ATC value Notes ----- For more information see: - EMV 4.1 Book 2 Annex A 1.3 Session Key Derivation - EMV 4.1 Book 2 Annex A 1.3.1 Description - EMV 4.1 Book 2 Annex A 1.3.2 Implementation This method was replaced by common session key derivation in 2005 and should not be used for new development. See EMVCo specification update bulletin 46 (SU-46). Recommended branch factor and tree height combinations are as follow. Both combinations produce enough session keys for every possible ATC value. - Branch factor 2 and tree height 16 - Branch factor 4 and tree height 8 Examples -------- >>> from pyemv import kd >>> mk = bytes.fromhex("0123456789ABCDEFFEDCBA9876543210") >>> atc = bytes.fromhex("001C") >>> sk = kd.derive_emv2000_tree_sk(mk, atc, 8, 4) >>> sk.hex().upper() 'E5BF6D1067F194B0A89B7F5D83BC64A2' """ if len(icc_mk) != 16: raise ValueError("ICC Master Key must be a double length DES key") if len(atc) != 2: raise ValueError("ATC value must be 2 bytes long") if len(iv) != 16: raise ValueError("Initialization vector value must be 16 bytes long") # The number of possible session keys (branch_factor ** height) # must exceed the maximum value of the ATC which is 2 ** 16 - 1. if branch_factor**height < 65535: raise ValueError( "Number of possible session keys must exceed maximum ATC value") # F(X,Y,j) := (DES3(X)[YL XOR (j mod b)] || DES3(X)[YR XOR (j mod b) XOR 'F0']) def derive(x: bytes, y: bytes, j: int) -> bytes: """Map two 16-byte numbers X and Y and an integer j onto a 16-byte number.""" j_mod_b = int.to_bytes(j % branch_factor, 8, "big") # (DES3(X)[YL XOR (j mod b)] l_data = _tools.xor(y[:8], j_mod_b) l_data = _tools.encrypt_tdes_ecb(x, l_data) # DES3(X)[YR XOR (j mod b) XOR 'F0'] r_data = _tools.xor(y[8:], j_mod_b) r_data = _tools.xor(r_data, b"\x00" * 7 + b"\xF0") r_data = _tools.encrypt_tdes_ecb(x, r_data) return l_data + r_data # GP = Grandparent Key # IK = Intermediate Key # P = Parent Key # H = Height of the tree def walk(j: int, h: int) -> _typing.Tuple[bytes, bytes]: """Returns P and GP""" # Base case: P = ICC MK, GP = IV if h == 0: return icc_mk, iv p, gp = walk(j // branch_factor, h - 1) # Derives an IK from P and GP # IK becomes the new parent and current P becomes the new GP return derive(p, gp, j), p atc_num = int.from_bytes(atc, "big") # Derive IKs from the bottom of the tree to the second to last level # because GP from that level is required for SK. p, gp = walk(atc_num // branch_factor, height - 1) # Derive SK from a new IK at the tree height XOR'd by GP. sk = _tools.xor(derive(p, gp, atc_num), gp) return _tools.adjust_key_parity(sk)