def pack(tlv_data, recalculate_length=False): result = b"" for data in tlv_data: tag, length, value = data[:3] if tag in (0xff, 0x00): result = result + inttostring(tag) continue if not isinstance(value, bytes): value = pack(value, recalculate_length) if recalculate_length: length = len(value) t = b"" while tag > 0: t = inttostring(tag & 0xff) + t tag = tag >> 8 if length < 0x7F: l = inttostring(length) else: l = b"" while length > 0: l = inttostring(length & 0xff) + l length = length >> 8 assert len(l) < 0x7f l = inttostring(0x80 | len(l)) + l result = result + t result = result + l result = result + value return b"".join(result)
def pack(tlv_data, recalculate_length=False): result = [] for data in tlv_data: tag, length, value = data[:3] if tag in (0xff, 0x00): result = result + inttostring(tag) continue if not isinstance(value, bytes): value = pack(value, recalculate_length) if recalculate_length: length = len(value) t = b"" while tag > 0: t = inttostring(tag & 0xff) + t tag = tag >> 8 if length < 0x7F: l = inttostring(length) else: l = b"" while length > 0: l = inttostring(length & 0xff) + l length = length >> 8 assert len(l) < 0x7f l = inttostring(0x80 | len(l)) + l result.append(t) result.append(l) result.append(value) return b"".join(result)
def __init__(self, mf, sam, ins2handler=None, extended_length=False): self.mf = mf self.SAM = sam if not ins2handler: self.ins2handler = { 0x0c: self.mf.eraseRecord, 0x0e: self.mf.eraseBinaryPlain, 0x0f: self.mf.eraseBinaryEncapsulated, 0x2a: self.SAM.perform_security_operation, 0x20: self.SAM.verify, 0x22: self.SAM.manage_security_environment, 0x24: self.SAM.change_reference_data, 0x46: self.SAM.generate_public_key_pair, 0x82: self.SAM.external_authenticate, 0x84: self.SAM.get_challenge, 0x88: self.SAM.internal_authenticate, 0xa0: self.mf.searchBinaryPlain, 0xa1: self.mf.searchBinaryEncapsulated, 0xa4: self.mf.selectFile, 0xb0: self.mf.readBinaryPlain, 0xb1: self.mf.readBinaryEncapsulated, 0xb2: self.mf.readRecordPlain, 0xb3: self.mf.readRecordEncapsulated, 0xc0: self.getResponse, 0xca: self.mf.getDataPlain, 0xcb: self.mf.getDataEncapsulated, 0xd0: self.mf.writeBinaryPlain, 0xd1: self.mf.writeBinaryEncapsulated, 0xd2: self.mf.writeRecord, 0xd6: self.mf.updateBinaryPlain, 0xd7: self.mf.updateBinaryEncapsulated, 0xda: self.mf.putDataPlain, 0xdb: self.mf.putDataEncapsulated, 0xdc: self.mf.updateRecordPlain, 0xdd: self.mf.updateRecordEncapsulated, 0xe0: self.mf.createFile, 0xe2: self.mf.appendRecord, 0xe4: self.mf.deleteFile, } else: self.ins2handler = ins2handler if extended_length: self.maxle = MAX_EXTENDED_LE else: self.maxle = MAX_SHORT_LE self.lastCommandOffcut = b"" self.lastCommandSW = SW["NORMAL"] el = extended_length # only needed to keep following line short tsft = Iso7816OS.makeThirdSoftwareFunctionTable(extendedLe=el) card_capabilities = self.mf.firstSFT + self.mf.secondSFT + tsft self.atr = Iso7816OS.makeATR( T=1, directConvention=True, TA1=0x13, histChars=inttostring(0x80) + inttostring(0x70 + len(card_capabilities)) + card_capabilities)
def generate_public_key_pair(self, p1, p2, data): """ In the Cryptoflex card this command only supports RSA keys. :param data: Contains the public exponent used for key generation :param p1: The key number. Can be used later to refer to the generated key :param p2: Used to specify the key length. The mapping is: 0x40 => 256 Bit, 0x60 => 512 Bit, 0x80 => 1024 """ from Crypto.PublicKey import RSA from Crypto.Util.randpool import RandomPool keynumber = p1 # TODO: Check if key exists keylength_dict = {0x40: 256, 0x60: 512, 0x80: 1024} if p2 not in keylength_dict: raise SwError(SW["ERR_INCORRECTP1P2"]) else: keylength = keylength_dict[p2] rnd = RandomPool() PublicKey = RSA.generate(keylength, rnd.get_bytes) self.dst.key = PublicKey e_in = struct.unpack("<i", data) if e_in[0] != 65537: logging.warning("Warning: Exponents different from 65537 are " + "ignored! The Exponent given is %i" % e_in[0]) # Encode Public key n = PublicKey.__getstate__()['n'] n_str = inttostring(n) n_str = n_str[::-1] e = PublicKey.__getstate__()['e'] e_str = inttostring(e, 4) e_str = e_str[::-1] pad = 187 * '\x30' # We don't have CRT components, so we need to pad pk_n = TLVutils.bertlv_pack(((0x81, len(n_str), n_str), (0x01, len(pad), pad), (0x82, len(e_str), e_str))) # Private key d = PublicKey.__getstate__()['d'] # Write result to FID 10 12 EF-PUB-KEY df = self.mf.currentDF() ef_pub_key = df.select("fid", 0x1012) ef_pub_key.writebinary([0], [pk_n]) data = ef_pub_key.getenc('data') # Write private key to FID 00 12 EF-PRI-KEY (not necessary?) # How to encode the private key? ef_priv_key = df.select("fid", 0x0012) ef_priv_key.writebinary([0], [inttostring(d)]) data = ef_priv_key.getenc('data') return PublicKey
def generate_public_key_pair(self, p1, p2, data): """ In the Cryptoflex card this command only supports RSA keys. :param data: Contains the public exponent used for key generation :param p1: The key number. Can be used later to refer to the generated key :param p2: Used to specify the key length. The mapping is: 0x40 => 256 Bit, 0x60 => 512 Bit, 0x80 => 1024 """ from Crypto.PublicKey import RSA from Crypto.Util.randpool import RandomPool keynumber = p1 # TODO: Check if key exists keylength_dict = {0x40: 256, 0x60: 512, 0x80: 1024} if p2 not in keylength_dict: raise SwError(SW["ERR_INCORRECTP1P2"]) else: keylength = keylength_dict[p2] rnd = RandomPool() PublicKey = RSA.generate(keylength, rnd.get_bytes) self.dst.key = PublicKey e_in = struct.unpack("<i", data) if e_in[0] != 65537: logging.warning("Warning: Exponents different from 65537 are " + "ignored! The Exponent given is %i" % e_in[0]) # Encode Public key n = PublicKey.__getstate__()['n'] n_str = inttostring(n) n_str = n_str[::-1] e = PublicKey.__getstate__()['e'] e_str = inttostring(e, 4) e_str = e_str[::-1] pad = 187 * '\x30' # We don't have CRT components, so we need to pad pk_n = TLVutils.bertlv_pack( ((0x81, len(n_str), n_str), (0x01, len(pad), pad), (0x82, len(e_str), e_str))) # Private key d = PublicKey.__getstate__()['d'] # Write result to FID 10 12 EF-PUB-KEY df = self.mf.currentDF() ef_pub_key = df.select("fid", 0x1012) ef_pub_key.writebinary([0], [pk_n]) data = ef_pub_key.getenc('data') # Write private key to FID 00 12 EF-PRI-KEY (not necessary?) # How to encode the private key? ef_priv_key = df.select("fid", 0x0012) ef_priv_key.writebinary([0], [inttostring(d)]) data = ef_priv_key.getenc('data') return PublicKey
def __init__(self, mf, sam, ins2handler=None, extended_length=False): self.mf = mf self.SAM = sam if not ins2handler: self.ins2handler = { 0x0c: self.mf.eraseRecord, 0x0e: self.mf.eraseBinaryPlain, 0x0f: self.mf.eraseBinaryEncapsulated, 0x2a: self.SAM.perform_security_operation, 0x20: self.SAM.verify, 0x22: self.SAM.manage_security_environment, 0x24: self.SAM.change_reference_data, 0x46: self.SAM.generate_public_key_pair, 0x82: self.SAM.external_authenticate, 0x84: self.SAM.get_challenge, 0x88: self.SAM.internal_authenticate, 0xa0: self.mf.searchBinaryPlain, 0xa1: self.mf.searchBinaryEncapsulated, 0xa4: self.mf.selectFile, 0xb0: self.mf.readBinaryPlain, 0xb1: self.mf.readBinaryEncapsulated, 0xb2: self.mf.readRecordPlain, 0xb3: self.mf.readRecordEncapsulated, 0xc0: self.getResponse, 0xca: self.mf.getDataPlain, 0xcb: self.mf.getDataEncapsulated, 0xd0: self.mf.writeBinaryPlain, 0xd1: self.mf.writeBinaryEncapsulated, 0xd2: self.mf.writeRecord, 0xd6: self.mf.updateBinaryPlain, 0xd7: self.mf.updateBinaryEncapsulated, 0xda: self.mf.putDataPlain, 0xdb: self.mf.putDataEncapsulated, 0xdc: self.mf.updateRecordPlain, 0xdd: self.mf.updateRecordEncapsulated, 0xe0: self.mf.createFile, 0xe2: self.mf.appendRecord, 0xe4: self.mf.deleteFile, } else: self.ins2handler = ins2handler if extended_length: self.maxle = MAX_EXTENDED_LE else: self.maxle = MAX_SHORT_LE self.lastCommandOffcut = b"" self.lastCommandSW = SW["NORMAL"] el = extended_length # only needed to keep following line short tsft = Iso7816OS.makeThirdSoftwareFunctionTable(extendedLe=el) card_capabilities = self.mf.firstSFT + self.mf.secondSFT + tsft self.atr = Iso7816OS.makeATR(T=1, directConvention=True, TA1=0x13, histChars=inttostring(0x80) + inttostring(0x70 + len(card_capabilities)) + card_capabilities)
def formatResult(self, seekable, le, data, sw, sm): if seekable: # when le = 0 then we want to have 0x9000. here we only have the # effective le, which is either MAX_EXTENDED_LE or MAX_SHORT_LE, # depending on the APDU. Note that the following distinguisher has # one false positive if le > len(data) and le != MAX_EXTENDED_LE and le != MAX_SHORT_LE: sw = SW["WARN_EOFBEFORENEREAD"] if le != None: result = data[:le] else: result = data[:0] if sm: try: sw, result = self.SAM.protect_result(sw, result) except SwError as e: logging.info(e.message) import traceback traceback.print_exception(*sys.exc_info()) sw = e.sw result = "" answer = self.formatResult(False, 0, result, sw, False) return R_APDU(result, inttostring(sw)).render()
def tlv_unpack(data): ber_class = (data[0] & 0xC0) >> 6 # 0 = primitive, 0x20 = constructed constructed = (data[0] & 0x20) != 0 tag = data[0] data = data[1:] if (tag & 0x1F) == 0x1F: tag = (tag << 8) | data[0] while data[0] & 0x80 == 0x80: data = data[1:] tag = (tag << 8) | data[0] data = data[1:] length = data[0] if length < 0x80: data = data[1:] elif length & 0x80 == 0x80: length_ = 0 data = data[1:] for i in range(0, length & 0x7F): length_ = length_ * 256 + data[0] data = data[1:] length = length_ value = b"".join(inttostring(i) for i in data[:length]) rest = data[length:] return ber_class, constructed, tag, length, value, rest
def formatResult(self, ins, le, data, sw): if le == 0 and len(data): # cryptoflex does not inpterpret le==0 as maxle self.lastCommandSW = sw self.lastCommandOffcut = data r = R_APDU(inttostring(SW["ERR_WRONGLENGTH"] + min(0xFF, len(data)))).render() else: if ins == 0xA4 and len(data): # get response should be followed by select file self.lastCommandSW = sw self.lastCommandOffcut = data r = R_APDU(inttostring(SW["NORMAL_REST"] + min(0xFF, len(data)))).render() else: r = Iso7816OS.formatResult(self, Iso7816OS.seekable(ins), le, data, sw, False) return r
def generate_public_key_pair(self, p1, p2, data): """ The GENERATE PUBLIC-KEY PAIR command either initiates the generation and storing of a key pair, i.e., a public key and a private key, in the card, or accesses a key pair previously generated in the card. :param p1: should be 0x00 (generate new key) :param p2: '00' (no information provided) or reference of the key to be generated :param data: One or more CRTs associated to the key generation if P1-P2 different from '0000' """ from Crypto.PublicKey import RSA, DSA cipher = self.ct.algorithm c_class = locals().get(cipher, None) if c_class is None: raise SwError(SW["ERR_CONDITIONNOTSATISFIED"]) if p1 & 0x01 == 0x00: # Generate key PublicKey = c_class.generate(self.dst.keylength) self.dst.key = PublicKey else: pass # Read key # Encode keys if cipher == "RSA": # Public key n = inttostring(PublicKey.n) e = inttostring(PublicKey.e) pk = ((0x81, len(n), n), (0x82, len(e), e)) result = bertlv_pack(pk) # Private key d = PublicKey.d elif cipher == "DSA": # DSAParams p = inttostring(PublicKey.p) q = inttostring(PublicKey.q) g = inttostring(PublicKey.g) # Public key y = inttostring(PublicKey.y) pk = ((0x81, len(p), p), (0x82, len(q), q), (0x83, len(g), g), (0x84, len(y), y)) # Private key x = inttostring(PublicKey.x) # Add more algorithms here # elif cipher = "ECDSA": else: raise SwError(SW["ERR_CONDITIONNOTSATISFIED"]) result = bertlv_pack([[0x7F49, len(pk), pk]]) # TODO: Internally store key pair if p1 & 0x02 == 0x02: # We do not support extended header lists yet raise SwError["ERR_NOTSUPPORTED"] else: return SW["NORMAL"], result
def formatResult(self, ins, le, data, sw): if le == 0 and len(data): # cryptoflex does not inpterpret le==0 as maxle self.lastCommandSW = sw self.lastCommandOffcut = data r = R_APDU(inttostring(SW["ERR_WRONGLENGTH"] +\ min(0xff, len(data)))).render() else: if ins == 0xa4 and len(data): # get response should be followed by select file self.lastCommandSW = sw self.lastCommandOffcut = data r = R_APDU(inttostring(SW["NORMAL_REST"] +\ min(0xff, len(data)))).render() else: r = Iso7816OS.formatResult(self, Iso7816OS.seekable(ins), le, data, sw, False) return r
def run(self): """ Main loop of the vpicc. Receives command APDUs via a socket from the vpcd, dispatches them to the emulated smartcard and sends the resulting respsonse APDU back to the vpcd. """ while True: try: (size, msg) = self.__recvFromVPICC() except socket.error as e: if not self.host: logging.info("Waiting for vpcd on port " + str(self.port)) (self.sock, address) = self.server_sock.accept() continue else: sys.exit() if not size: logging.warning("Error in communication protocol (missing \ size parameter)") elif size == VPCD_CTRL_LEN: if msg == inttostring(VPCD_CTRL_OFF): logging.info("Power Down") self.os.powerDown() elif msg == inttostring(VPCD_CTRL_ON): logging.info("Power Up") self.os.powerUp() elif msg == inttostring(VPCD_CTRL_RESET): logging.info("Reset") self.os.reset() elif msg == inttostring(VPCD_CTRL_ATR): self.__sendToVPICC(self.os.getATR()) else: logging.warning("unknown control command") else: if size != len(msg): logging.warning("Expected %u bytes, but received only %u", size, len(msg)) answer = self.os.execute(msg) logging.info("Response APDU (%d bytes):\n %s\n", len(answer), hexdump(answer, indent=2)) self.__sendToVPICC(answer)
def __replace_tag(self, tag, data): """ Adjust the config string using a given tag, value combination. If the config string already contains a tag, value pair for the given tag, replace it. Otherwise append tag, length and value to the config string. """ position = 0 while position < len(self.__config_string) and \ self.__config_string[position] != tag: length = stringtoint(self.__config_string[position+1]) position += length + 3 if position < len(self.__config_string): #Replace Tag length = stringtoint(self.__config_string[position+1]) self.__config_string = self.__config_string[:position] +\ chr(tag) + inttostring(len(data)) + data +\ self.__config_string[position+2+length:] else: #Add new tag self.__config_string += chr(tag) + inttostring(len(data)) + data
def __replace_tag(self, tag, data): """ Adjust the config string using a given tag, value combination. If the config string already contains a tag, value pair for the given tag, replace it. Otherwise append tag, length and value to the config string. """ position = 0 while position < len(self.__config_string) and \ self.__config_string[position] != tag: length = stringtoint(self.__config_string[position + 1]) position += length + 3 if position < len(self.__config_string): #Replace Tag length = stringtoint(self.__config_string[position + 1]) self.__config_string = self.__config_string[:position] +\ chr(tag) + inttostring(len(data)) + data +\ self.__config_string[position+2+length:] else: #Add new tag self.__config_string += chr(tag) + inttostring(len(data)) + data
def formatResult(self, seekable, le, data, sw, sm): if seekable: # when le = 0 then we want to have 0x9000. here we only have the # effective le, which is either MAX_EXTENDED_LE or MAX_SHORT_LE, # depending on the APDU. Note that the following distinguisher has # one false positive if le > len(data) and le != MAX_EXTENDED_LE and le != MAX_SHORT_LE: sw = SW["WARN_EOFBEFORENEREAD"] result = data[:le] if sm: sw, result = self.SAM.protect_result(sw, result) return R_APDU(result, inttostring(sw)).render()
def formatResult(self, seekable, le, data, sw, sm): if not seekable: self.lastCommandOffcut = data[le:] l = len(self.lastCommandOffcut) if l == 0: self.lastCommandSW = SW["NORMAL"] else: self.lastCommandSW = sw sw = SW["NORMAL_REST"] + min(0xff, l) else: if le > len(data): sw = SW["WARN_EOFBEFORENEREAD"] result = data[:le] if sm: sw, result = self.SAM.protect_result(sw, result) return R_APDU(result, inttostring(sw)).render()
def makeThirdSoftwareFunctionTable(commandChainging=False, extendedLe=False, assignLogicalChannel=0, maximumChannels=0): """ Returns a byte according to the third software function table from the historical bytes of the card capabilities. """ tsft = 0 if commandChainging: tsft |= 1 << 7 if extendedLe: tsft |= 1 << 6 if assignLogicalChannel: if not (0<=assignLogicalChannel and assignLogicalChannel<=3): raise ValueError tsft |= assignLogicalChannel << 3 if maximumChannels: if not (0<=maximumChannels and maximumChannels<=7): raise ValueError tsft |= maximumChannels return inttostring(tsft)
def internal_authenticate(self, p1, p2, data): """ Authenticate card to terminal. Encrypt the challenge of the terminal to prove key posession """ if p1 == 0x00: #No information given cipher = get_referenced_cipher(self.cipher) else: cipher = get_referenced_cipher(p1) if cipher == "RSA" or cipher == "DSA": crypted_challenge = self.asym_key.sign(data, "") crypted_challenge = crypted_challenge[0] crypted_challenge = inttostring(crypted_challenge) else: key = self._get_referenced_key(p1, p2) crypted_challenge = vsCrypto.encrypt(cipher, key, data) return SW["NORMAL"], crypted_challenge
def internal_authenticate(self, p1, p2, data): """ Authenticate card to terminal. Encrypt the challenge of the terminal to prove key posession """ if p1 == 0x00: #No information given cipher = get_referenced_cipher(self.cipher) else: cipher = get_referenced_cipher(p1) if cipher == "RSA" or cipher == "DSA": crypted_challenge = self.asym_key.sign(data,"") crypted_challenge = crypted_challenge[0] crypted_challenge = inttostring(crypted_challenge) else: key = self._get_referenced_key(p1, p2) crypted_challenge = vsCrypto.encrypt(cipher, key, data) return SW["NORMAL"], crypted_challenge
def protect_response(self, sw, result): """ This method protects a response APDU using secure messaging mechanisms :returns: the protected data and the SW bytes """ return_data = b"" if result: # Encrypt the data included in the RAPDU encrypted = self.encipher(0x82, 0x80, result) encrypted = b"\x01" + encrypted encrypted_tlv = bertlv_pack([ (SM_Class["CRYPTOGRAM_PADDING_INDICATOR_ODD"], len(encrypted), encrypted) ]) return_data += encrypted_tlv sw_str = inttostring(sw) length = len(sw_str) tag = SM_Class["PLAIN_PROCESSING_STATUS"] tlv_sw = bertlv_pack([(tag, length, sw_str)]) return_data += tlv_sw if self.cct.algorithm is None: raise SwError(SW["CONDITIONSNOTSATISFIED"]) elif self.cct.algorithm == "CC": tag = SM_Class["CHECKSUM"] padded = vsCrypto.append_padding(self.cct.blocklength, return_data) auth = self.compute_cryptographic_checksum(0x8E, 0x80, padded) length = len(auth) return_data += bertlv_pack([(tag, length, auth)]) elif self.cct.algorithm == "SIGNATURE": tag = SM_Class["DIGITAL_SIGNATURE"] hash = self.hash(0x90, 0x80, return_data) auth = self.compute_digital_signature(0x9E, 0x9A, hash) length = len(auth) return_data += bertlv_pack([(tag, length, auth)]) return sw, return_data
def protect_response(self, sw, result): """ This method protects a response APDU using secure messaging mechanisms :returns: the protected data and the SW bytes """ return_data = "" if result != "": # Encrypt the data included in the RAPDU encrypted = self.encipher(0x82, 0x80, result) encrypted = "\x01" + encrypted encrypted_tlv = bertlv_pack([( SM_Class["CRYPTOGRAM_PADDING_INDICATOR_ODD"], len(encrypted), encrypted)]) return_data += encrypted_tlv sw_str = inttostring(sw) length = len(sw_str) tag = SM_Class["PLAIN_PROCESSING_STATUS"] tlv_sw = bertlv_pack([(tag, length, sw_str)]) return_data += tlv_sw if self.cct.algorithm == None: raise SwError(SW["CONDITIONSNOTSATISFIED"]) elif self.cct.algorithm == "CC": tag = SM_Class["CHECKSUM"] padded = vsCrypto.append_padding(self.cct.blocklength, return_data) auth = self.compute_cryptographic_checksum(0x8E, 0x80, padded) length = len(auth) return_data += bertlv_pack([(tag, length, auth)]) elif self.cct.algorithm == "SIGNATURE": tag = SM_Class["DIGITAL_SIGNATURE"] hash = self.hash(0x90, 0x80, return_data) auth = self.compute_digital_signature(0x9E, 0x9A, hash) length = len(auth) return_data += bertlv_pack([(tag, length, auth)]) return sw, return_data
def crypto_checksum(algo, key, data, iv=None, ssc=None): """ Compute various types of cryptographic checksums. :param algo: A string specifying the algorithm to use. Currently supported algorithms are \"MAX\" \"HMAC\" and \"CC\" (Meaning a cryptographic checksum as used by the ICAO passports) :param key: They key used to computed the cryptographic checksum :param data: The data for which to calculate the checksum :param iv: Optional. An initialization vector. Only used by the \"MAC\" algorithm :param ssc: Optional. A send sequence counter to be prepended to the data. Only used by the \"CC\" algorithm """ if algo not in ("HMAC", "MAC", "CC"): raise ValueError("Unknown Algorithm %s" % algo) if algo == "MAC": checksum = calculate_MAC(key, data, iv) elif algo == "HMAC": hmac = HMAC.new(key, data) checksum = hmac.hexdigest() del hmac elif algo == "CC": if ssc != None: data = inttostring(ssc) + data a = cipher(True, "des-cbc", key[:8], data) b = cipher(False, "des-ecb", key[8:16], a[-8:]) c = cipher(True, "des-ecb", key[:8], b) checksum = c return checksum
def crypto_checksum(algo, key, data, iv=None, ssc=None): """ Compute various types of cryptographic checksums. :param algo: A string specifying the algorithm to use. Currently supported algorithms are \"MAX\" \"HMAC\" and \"CC\" (Meaning a cryptographic checksum as used by the ICAO passports) :param key: They key used to computed the cryptographic checksum :param data: The data for which to calculate the checksum :param iv: Optional. An initialization vector. Only used by the \"MAC\" algorithm :param ssc: Optional. A send sequence counter to be prepended to the data. Only used by the \"CC\" algorithm """ if algo not in ("HMAC", "MAC", "CC"): raise ValueError("Unknown Algorithm %s" % algo) if algo == "MAC": checksum = calculate_MAC(key, data, iv) elif algo == "HMAC": hmac = HMAC.new(key, data) checksum = hmac.hexdigest() del hmac elif algo == "CC": if ssc is not None: data = inttostring(ssc) + data a = cipher(True, "des-cbc", key[:8], data) b = cipher(False, "des-ecb", key[8:16], a[-8:]) c = cipher(True, "des-ecb", key[:8], b) checksum = c return checksum
def simpletlv_pack(tlv_data, recalculate_length=False): result = b"" for tag, length, value in tlv_data: if tag >= 0xff or tag <= 0x00: # invalid continue if recalculate_length: length = len(value) if length > 0xffff or length < 0: # invalid continue if length < 0xff: result += inttostring(tag) + inttostring(length) + value else: result += inttostring(tag) + inttostring(0xff) + inttostring(length >> 8) + \ inttostring(length & 0xff) + value return result
def __generate_nPA(self): from virtualsmartcard.cards.nPA import nPA_SAM mappings = [ (b'\x04\x00\x7f\x00\x07\x02\x02\x04', range(1, 5), range(1, 5), "PACE"), (b'\x04\x00\x7f\x00\x07\x02\x02\x02', range(1, 3), range(1, 6), "TA"), (b'\x04\x00\x7f\x00\x07\x02\x02\x02', [1], [6], "TA"), (b'\x04\x00\x7f\x00\x07\x02\x02\x03', range(1, 3), range(1, 5), "CA"), (b'\x04\x00\x7f\x00\x07\x02\x02\x05', range(1, 3), range(1, 6), "RI"), ] for oid_base, x_list, y_list, algo in mappings: for oid in [oid_base + inttostring(x) + inttostring(y) for x in x_list for y in y_list]: ALGO_MAPPING[oid] = algo ALGO_MAPPING[b"\x04\x00\x7f\x00\x07\x03\x01\x04\x03"] = "CommunityID" ALGO_MAPPING[b"\x04\x00\x7f\x00\x07\x03\x01\x04\x02"] = "DateOfExpiry" ALGO_MAPPING[b"\x04\x00\x7f\x00\x07\x03\x01\x04\x01"] = "DateOfBirth" self.mf = MF() card_access = b"\x31\x81\xb3\x30\x0d\x06\x08\x04\x00\x7f\x00\x07\x02"\ b"\x02\x02\x02\x01\x02\x30\x12\x06\x0a\x04\x00\x7f\x00"\ b"\x07\x02\x02\x03\x02\x02\x02\x01\x02\x02\x01\x41\x30"\ b"\x12\x06\x0a\x04\x00\x7f\x00\x07\x02\x02\x03\x02\x02"\ b"\x02\x01\x02\x02\x01\x45\x30\x12\x06\x0a\x04\x00\x7f"\ b"\x00\x07\x02\x02\x04\x02\x02\x02\x01\x02\x02\x01\x0d"\ b"\x30\x1c\x06\x09\x04\x00\x7f\x00\x07\x02\x02\x03\x02"\ b"\x30\x0c\x06\x07\x04\x00\x7f\x00\x07\x01\x02\x02\x01"\ b"\x0d\x02\x01\x41\x30\x1c\x06\x09\x04\x00\x7f\x00\x07"\ b"\x02\x02\x03\x02\x30\x0c\x06\x07\x04\x00\x7f\x00\x07"\ b"\x01\x02\x02\x01\x0d\x02\x01\x45\x30\x2a\x06\x08\x04"\ b"\x00\x7f\x00\x07\x02\x02\x06\x16\x1e\x68\x74\x74\x70"\ b"\x3a\x2f\x2f\x62\x73\x69\x2e\x62\x75\x6e\x64\x2e\x64"\ b"\x65\x2f\x63\x69\x66\x2f\x6e\x70\x61\x2e\x78\x6d\x6c" self.mf.append(TransparentStructureEF(parent=self.mf, fid=0x011c, shortfid=0x1c, data=card_access)) card_security = b"\x30\x82\x05\xA0\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01"\ b"\x07\x02\xA0\x82\x05\x91\x30\x82\x05\x8D\x02\x01\x03"\ b"\x31\x0F\x30\x0D\x06\x09\x60\x86\x48\x01\x65\x03\x04"\ b"\x02\x01\x05\x00\x30\x82\x01\x48\x06\x08\x04\x00\x7F"\ b"\x00\x07\x03\x02\x01\xA0\x82\x01\x3A\x04\x82\x01\x36"\ b"\x31\x82\x01\x32\x30\x0D\x06\x08\x04\x00\x7F\x00\x07"\ b"\x02\x02\x02\x02\x01\x02\x30\x12\x06\x0A\x04\x00\x7F"\ b"\x00\x07\x02\x02\x03\x02\x02\x02\x01\x02\x02\x01\x41"\ b"\x30\x12\x06\x0A\x04\x00\x7F\x00\x07\x02\x02\x04\x02"\ b"\x02\x02\x01\x02\x02\x01\x0D\x30\x17\x06\x0A\x04\x00"\ b"\x7F\x00\x07\x02\x02\x05\x02\x03\x30\x09\x02\x01\x01"\ b"\x02\x01\x43\x01\x01\xFF\x30\x17\x06\x0A\x04\x00\x7F"\ b"\x00\x07\x02\x02\x05\x02\x03\x30\x09\x02\x01\x01\x02"\ b"\x01\x44\x01\x01\x00\x30\x19\x06\x09\x04\x00\x7F\x00"\ b"\x07\x02\x02\x05\x02\x30\x0C\x06\x07\x04\x00\x7F\x00"\ b"\x07\x01\x02\x02\x01\x0D\x30\x1C\x06\x09\x04\x00\x7F"\ b"\x00\x07\x02\x02\x03\x02\x30\x0C\x06\x07\x04\x00\x7F"\ b"\x00\x07\x01\x02\x02\x01\x0D\x02\x01\x41\x30\x2A\x06"\ b"\x08\x04\x00\x7F\x00\x07\x02\x02\x06\x16\x1E\x68\x74"\ b"\x74\x70\x3A\x2F\x2F\x62\x73\x69\x2E\x62\x75\x6E\x64"\ b"\x2E\x64\x65\x2F\x63\x69\x66\x2F\x6E\x70\x61\x2E\x78"\ b"\x6D\x6C\x30\x62\x06\x09\x04\x00\x7F\x00\x07\x02\x02"\ b"\x01\x02\x30\x52\x30\x0C\x06\x07\x04\x00\x7F\x00\x07"\ b"\x01\x02\x02\x01\x0D\x03\x42\x00\x04\x92\x5D\xB4\xE1"\ b"\x7A\xDE\x58\x20\x9F\x96\xFA\xA0\x7F\x1F\x8A\x22\x3F"\ b"\x82\x3F\x96\xCC\x5D\x78\xCB\xEF\x5D\x17\x42\x20\x88"\ b"\xFD\xD5\x8E\x56\xBC\x42\x50\xDE\x33\x46\xB3\xC8\x32"\ b"\xCA\xE4\x86\x35\xFB\x6C\x43\x78\x9D\xE8\xB3\x10\x2F"\ b"\x43\x93\xB4\x18\xE2\x4A\x13\xD9\x02\x01\x41\xA0\x82"\ b"\x03\x1C\x30\x82\x03\x18\x30\x82\x02\xBC\xA0\x03\x02"\ b"\x01\x02\x02\x02\x01\x5C\x30\x0C\x06\x08\x2A\x86\x48"\ b"\xCE\x3D\x04\x03\x02\x05\x00\x30\x4F\x31\x0B\x30\x09"\ b"\x06\x03\x55\x04\x06\x13\x02\x44\x45\x31\x0D\x30\x0B"\ b"\x06\x03\x55\x04\x0A\x0C\x04\x62\x75\x6E\x64\x31\x0C"\ b"\x30\x0A\x06\x03\x55\x04\x0B\x0C\x03\x62\x73\x69\x31"\ b"\x0C\x30\x0A\x06\x03\x55\x04\x05\x13\x03\x30\x31\x33"\ b"\x31\x15\x30\x13\x06\x03\x55\x04\x03\x0C\x0C\x63\x73"\ b"\x63\x61\x2D\x67\x65\x72\x6D\x61\x6E\x79\x30\x1E\x17"\ b"\x0D\x31\x30\x31\x30\x30\x35\x31\x31\x30\x37\x35\x32"\ b"\x5A\x17\x0D\x32\x31\x30\x34\x30\x35\x30\x38\x33\x39"\ b"\x34\x37\x5A\x30\x47\x31\x0B\x30\x09\x06\x03\x55\x04"\ b"\x06\x13\x02\x44\x45\x31\x1D\x30\x1B\x06\x03\x55\x04"\ b"\x0A\x0C\x14\x42\x75\x6E\x64\x65\x73\x64\x72\x75\x63"\ b"\x6B\x65\x72\x65\x69\x20\x47\x6D\x62\x48\x31\x0C\x30"\ b"\x0A\x06\x03\x55\x04\x05\x13\x03\x30\x37\x34\x31\x0B"\ b"\x30\x09\x06\x03\x55\x04\x03\x0C\x02\x44\x53\x30\x82"\ b"\x01\x13\x30\x81\xD4\x06\x07\x2A\x86\x48\xCE\x3D\x02"\ b"\x01\x30\x81\xC8\x02\x01\x01\x30\x28\x06\x07\x2A\x86"\ b"\x48\xCE\x3D\x01\x01\x02\x1D\x00\xD7\xC1\x34\xAA\x26"\ b"\x43\x66\x86\x2A\x18\x30\x25\x75\xD1\xD7\x87\xB0\x9F"\ b"\x07\x57\x97\xDA\x89\xF5\x7E\xC8\xC0\xFF\x30\x3C\x04"\ b"\x1C\x68\xA5\xE6\x2C\xA9\xCE\x6C\x1C\x29\x98\x03\xA6"\ b"\xC1\x53\x0B\x51\x4E\x18\x2A\xD8\xB0\x04\x2A\x59\xCA"\ b"\xD2\x9F\x43\x04\x1C\x25\x80\xF6\x3C\xCF\xE4\x41\x38"\ b"\x87\x07\x13\xB1\xA9\x23\x69\xE3\x3E\x21\x35\xD2\x66"\ b"\xDB\xB3\x72\x38\x6C\x40\x0B\x04\x39\x04\x0D\x90\x29"\ b"\xAD\x2C\x7E\x5C\xF4\x34\x08\x23\xB2\xA8\x7D\xC6\x8C"\ b"\x9E\x4C\xE3\x17\x4C\x1E\x6E\xFD\xEE\x12\xC0\x7D\x58"\ b"\xAA\x56\xF7\x72\xC0\x72\x6F\x24\xC6\xB8\x9E\x4E\xCD"\ b"\xAC\x24\x35\x4B\x9E\x99\xCA\xA3\xF6\xD3\x76\x14\x02"\ b"\xCD\x02\x1D\x00\xD7\xC1\x34\xAA\x26\x43\x66\x86\x2A"\ b"\x18\x30\x25\x75\xD0\xFB\x98\xD1\x16\xBC\x4B\x6D\xDE"\ b"\xBC\xA3\xA5\xA7\x93\x9F\x02\x01\x01\x03\x3A\x00\x04"\ b"\x3D\x6A\x7C\x2A\x6F\x20\x5F\x83\x9B\x04\x14\xEC\x58"\ b"\xC6\xC7\x1B\x75\xF5\x15\xDE\xC3\xAE\x73\x3B\x5F\x47"\ b"\x88\xDD\xC8\x15\xF0\x5B\xC1\xF6\x53\x8F\xD9\x69\x54"\ b"\xE1\xF8\x40\xA2\xE2\x18\x99\x62\xCC\xAA\x14\x90\x08"\ b"\x24\xC7\xDD\xB9\xA3\x81\xD1\x30\x81\xCE\x30\x0E\x06"\ b"\x03\x55\x1D\x0F\x01\x01\xFF\x04\x04\x03\x02\x07\x80"\ b"\x30\x1F\x06\x03\x55\x1D\x23\x04\x18\x30\x16\x80\x14"\ b"\x60\x44\xF2\x45\xF2\xE0\x71\xD4\xD5\x64\xF4\xE5\x77"\ b"\xD6\x36\x69\xDB\xEB\x18\x59\x30\x41\x06\x03\x55\x1D"\ b"\x20\x04\x3A\x30\x38\x30\x36\x06\x09\x04\x00\x7F\x00"\ b"\x07\x03\x01\x01\x01\x30\x29\x30\x27\x06\x08\x2B\x06"\ b"\x01\x05\x05\x07\x02\x01\x16\x1B\x68\x74\x74\x70\x3A"\ b"\x2F\x2F\x77\x77\x77\x2E\x62\x73\x69\x2E\x62\x75\x6E"\ b"\x64\x2E\x64\x65\x2F\x63\x73\x63\x61\x30\x2B\x06\x09"\ b"\x2A\x86\x48\x86\xF7\x0D\x01\x09\x15\x04\x1E\x04\x1C"\ b"\x31\x2E\x32\x2E\x32\x37\x36\x2E\x30\x2E\x38\x30\x2E"\ b"\x31\x2E\x31\x32\x2E\x30\x2E\x32\x30\x2E\x35\x2E\x31"\ b"\x2E\x30\x30\x2B\x06\x03\x55\x1D\x10\x04\x24\x30\x22"\ b"\x80\x0F\x32\x30\x31\x30\x31\x30\x30\x35\x31\x31\x30"\ b"\x37\x35\x32\x5A\x81\x0F\x32\x30\x31\x31\x30\x32\x30"\ b"\x35\x30\x39\x33\x39\x34\x37\x5A\x30\x0C\x06\x08\x2A"\ b"\x86\x48\xCE\x3D\x04\x03\x02\x05\x00\x03\x48\x00\x30"\ b"\x45\x02\x20\x13\xE9\xE1\x7A\x9E\xFE\x8B\xD7\xD7\x27"\ b"\x62\x92\x30\x5B\xCC\xC3\x2B\x70\xC2\xB7\x60\x40\xF4"\ b"\x88\x30\x66\x62\x26\xCD\x6A\x4B\xF4\x02\x21\x00\x87"\ b"\xF4\x71\xE2\x44\x35\xB4\xC3\x4A\xF3\x57\x30\x94\xFB"\ b"\x1F\x1C\x2A\x48\xB1\x3E\xE5\xED\x67\xF1\x72\x6D\xCF"\ b"\x56\xE3\x84\xE3\x6F\x31\x82\x01\x09\x30\x82\x01\x05"\ b"\x02\x01\x01\x30\x55\x30\x4F\x31\x0B\x30\x09\x06\x03"\ b"\x55\x04\x06\x13\x02\x44\x45\x31\x0D\x30\x0B\x06\x03"\ b"\x55\x04\x0A\x0C\x04\x62\x75\x6E\x64\x31\x0C\x30\x0A"\ b"\x06\x03\x55\x04\x0B\x0C\x03\x62\x73\x69\x31\x0C\x30"\ b"\x0A\x06\x03\x55\x04\x05\x13\x03\x30\x31\x33\x31\x15"\ b"\x30\x13\x06\x03\x55\x04\x03\x0C\x0C\x63\x73\x63\x61"\ b"\x2D\x67\x65\x72\x6D\x61\x6E\x79\x02\x02\x01\x5C\x30"\ b"\x0D\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05"\ b"\x00\xA0\x4A\x30\x17\x06\x09\x2A\x86\x48\x86\xF7\x0D"\ b"\x01\x09\x03\x31\x0A\x06\x08\x04\x00\x7F\x00\x07\x03"\ b"\x02\x01\x30\x2F\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01"\ b"\x09\x04\x31\x22\x04\x20\xEF\x0F\xDA\x94\x2E\x5A\x0F"\ b"\x6F\xC9\xC5\x46\xEE\x01\xF9\x10\x31\x43\x64\x30\xF7"\ b"\x5E\x9D\x36\x54\xD3\x69\x30\x9E\x8B\xE7\x17\x48\x30"\ b"\x0C\x06\x08\x2A\x86\x48\xCE\x3D\x04\x03\x02\x05\x00"\ b"\x04\x40\x30\x3E\x02\x1D\x00\x89\x75\x92\x5B\xE1\x31"\ b"\xB7\x7C\x95\x8C\x3E\xCB\x2A\x5C\x67\xFC\x5C\xE3\x1C"\ b"\xBD\x01\x41\xE3\x4B\xC7\xF0\xA4\x47\x02\x1D\x00\xCC"\ b"\x65\xE6\x2D\xDC\xF2\x93\x96\x4B\x22\xD7\xB5\x10\xD7"\ b"\x81\x88\x07\xC8\x95\x96\xBD\x34\xD8\xF9\xBB\x4C\x05"\ b"\x27" self.mf.append(TransparentStructureEF(parent=self.mf, fid=0x011d, shortfid=0x1d, data=card_security)) ef_dir = b"\x61\x32\x4F\x0F\xE8\x28\xBD\x08\x0F\xA0\x00\x00\x01\x67"\ b"\x45\x53\x49\x47\x4E\x50\x0F\x43\x49\x41\x20\x7A\x75\x20"\ b"\x44\x46\x2E\x65\x53\x69\x67\x6E\x51\x00\x73\x0C\x4F\x0A"\ b"\xA0\x00\x00\x01\x67\x45\x53\x49\x47\x4E\x61\x09\x4F\x07"\ b"\xA0\x00\x00\x02\x47\x10\x01\x61\x0B\x4F\x09\xE8\x07\x04"\ b"\x00\x7F\x00\x07\x03\x02\x61\x0C\x4F\x0A\xA0\x00\x00\x01"\ b"\x67\x45\x53\x49\x47\x4E" self.mf.append(TransparentStructureEF(parent=self.mf, fid=0x2f00, shortfid=30, data=ef_dir)) eid = DF(parent=self.mf, fid=0xffff, dfname=b'\xe8\x07\x04\x00\x7f\x00\x07\x03\x02') eid.extra_fci_data = b"\xab\x75\x84\x01\xa4\xaf\x70\xa0\x10\xb4\x06"\ b"\x95\x01\x20\x83\x01\x04\xb4\x06\x95\x01\x20"\ b"\x83\x01\x06\xa0\x54\xa4\x14\x95\x01\x80\x7f"\ b"\x4c\x0e\x06\x09\x04\x00\x7f\x00\x07\x03\x01"\ b"\x02\x01\x53\x01\x00\xaf\x22\xa4\x18\x95\x01"\ b"\x80\x7f\x4c\x12\x06\x09\x04\x00\x7f\x00\x07"\ b"\x03\x01\x02\x02\x53\x05\x00\x00\x00\x00\x00"\ b"\xa4\x06\x95\x01\x80\x83\x01\x47\xa4\x18\x95"\ b"\x01\x80\x7f\x4c\x12\x06\x09\x04\x00\x7f\x00"\ b"\x07\x03\x01\x02\x02\x53\x05\x00\x00\x00\x00"\ b"\x10\x7a\x06\x8a\x01\x05\x9e\x01\x03" # FIXME access control for eid application DocumentType = self.datagroups["DocumentType"] if "DocumentType" in \ self.datagroups else 'ID' IssuingState = self.datagroups["IssuingState"] if "IssuingState" in \ self.datagroups else 'D' DateOfExpiry = self.datagroups["DateOfExpiry"] if "DateOfExpiry" in \ self.datagroups else '20201031' GivenNames = self.datagroups["GivenNames"] if "GivenNames" in \ self.datagroups else 'ERIKA' FamilyNames = self.datagroups["FamilyNames"] if "FamilyNames" in \ self.datagroups else 'MUSTERMANN' ReligiousArtisticName = self.datagroups["ReligiousArtisticName"] if \ "ReligiousArtisticName" in self.datagroups else '' AcademicTitle = self.datagroups["AcademicTitle"] if \ "AcademicTitle" in self.datagroups else '' DateOfBirth = self.datagroups["DateOfBirth"] if "DateOfBirth" in \ self.datagroups else '19640812' PlaceOfBirth = self.datagroups["PlaceOfBirth"] if "PlaceOfBirth" in \ self.datagroups else 'BERLIN' Nationality = self.datagroups["Nationality"] if "Nationality" in \ self.datagroups else 'DE' Sex = self.datagroups["Sex"] if "Sex" in self.datagroups else 'F' BirthName = self.datagroups["BirthName"] if "BirthName" in \ self.datagroups else 'Mein Geburtsname' # PlaceOfResidence variable is only a helper to get a switch for # <NotOnChip> PlaceOfResidence = self.datagroups["PlaceOfResidence"] if \ "PlaceOfResidence" in self.datagroups else '' Country = self.datagroups["Country"] if "Country" in self.datagroups \ else 'D' City = self.datagroups["City"] if "City" in self.datagroups else \ 'KOLN' ZIP = self.datagroups["ZIP"] if "ZIP" in self.datagroups else '51147' Street = self.datagroups["Street"] if "Street" in \ self.datagroups else 'HEIDESTRASSE 17' CommunityID = self.datagroups["CommunityID"] if "CommunityID" in \ self.datagroups else '02760378900276' if (CommunityID.rstrip() != "<NotOnChip>"): # the plain CommunityID integer value has to be translated into # its binary representation. '0276...' will be '\x02\x76\...' CommunityID_Binary = binascii.unhexlify(CommunityID) # ResidencePermit1 and ResidencePermit2 are part of eAT only ResidencePermit1 = self.datagroups["ResidencePermit1"] if \ "ResidencePermit1" in self.datagroups else \ 'ResidencePermit1 field up to 750 characters' ResidencePermit2 = self.datagroups["ResidencePermit2"] if \ "ResidencePermit2" in self.datagroups else \ 'ResidencePermit1 field up to 250 characters' # Currently, those data groups are for further usage: dg12_param = self.datagroups["dg12"] if "dg12" in \ self.datagroups else '' dg14_param = self.datagroups["dg14"] if "dg14" in \ self.datagroups else '' dg15_param = self.datagroups["dg15"] if "dg15" in \ self.datagroups else '' dg16_param = self.datagroups["dg16"] if "dg16" in \ self.datagroups else '' dg21_param = self.datagroups["dg21"] if "dg21" in \ self.datagroups else '' # "Attribute not on Chip" makes sence only for ReligiousArtisticName, # Nationality, BirthName, ResidencePermit1 and ResidencePermit2, refer # to BSI TR-03127 if (DocumentType.rstrip() != "<NotOnChip>"): dg1 = pack([(0x61, 0, [(0x13, 0, DocumentType)])], True) else: dg1 = None if (IssuingState.rstrip() != "<NotOnChip>"): dg2 = pack([(0x62, 0, [(0x13, 0, IssuingState)])], True) else: dg2 = None if (DateOfExpiry.rstrip() != "<NotOnChip>"): dg3 = pack([(0x63, 0, [(0x12, 0, DateOfExpiry)])], True) else: dg3 = None if (GivenNames.rstrip() != "<NotOnChip>"): dg4 = pack([(0x64, 0, [(0x0C, 0, GivenNames)])], True) else: dg4 = None if (FamilyNames.rstrip() != "<NotOnChip>"): dg5 = pack([(0x65, 0, [(0x0C, 0, FamilyNames)])], True) else: dg5 = None if (ReligiousArtisticName.rstrip() != "<NotOnChip>"): dg6 = pack([(0x66, 0, [(0x0C, 0, ReligiousArtisticName)])], True) else: dg6 = None if (AcademicTitle.rstrip() != "<NotOnChip>"): dg7 = pack([(0x67, 0, [(0x0C, 0, AcademicTitle)])], True) else: dg7 = None if (DateOfBirth.rstrip() != "<NotOnChip>"): dg8 = pack([(0x68, 0, [(0x12, 0, DateOfBirth)])], True) else: dg8 = None if (PlaceOfBirth.rstrip() != "<NotOnChip>"): dg9 = pack([(0x69, 0, [(0xA1, 0, [(0x0C, 0, PlaceOfBirth)])])], True) else: dg9 = None if (Nationality.rstrip() != "<NotOnChip>"): dg10 = pack([(0x6A, 0, [(0x13, 0, Nationality)])], True) else: dg10 = None if (Sex.rstrip() != "<NotOnChip>"): dg11 = pack([(0x6B, 0, [(0x13, 0, Sex)])], True) else: dg11 = None if (dg12_param.rstrip() != "<NotOnChip>"): dg12 = dg12_param else: dg12 = None if (BirthName.rstrip() != "<NotOnChip>"): dg13 = pack([(0x6D, 0, [(0x0C, 0, BirthName)])], True) else: dg13 = None if (dg14_param.rstrip() != "<NotOnChip>"): dg14 = dg14_param else: dg14 = None if (dg15_param.rstrip() != "<NotOnChip>"): dg15 = dg15_param else: dg15 = None if (dg16_param.rstrip() != "<NotOnChip>"): dg16 = dg16_param else: dg16 = None if (PlaceOfResidence.rstrip() != "<NotOnChip>"): dg17 = pack([(0x71, 0, [(0x30, 0, [ (0xAA, 0, [(0x0C, 0, Street)]), (0xAB, 0, [(0x0C, 0, City)]), (0xAD, 0, [(0x13, 0, Country)]), (0xAE, 0, [(0x13, 0, ZIP)]) ])])], True) else: dg17 = None if (CommunityID.rstrip() != "<NotOnChip>"): dg18 = pack([(0x72, 0, [(0x04, 0, CommunityID_Binary)])], True) else: dg18 = None if (ResidencePermit1.rstrip() != "<NotOnChip>"): dg19 = pack([(0x73, 0, [(0xA1, 0, [(0x0C, 0, ResidencePermit1)])])], True) else: dg19 = None if (ResidencePermit1.rstrip() != "<NotOnChip>"): dg20 = pack([(0x74, 0, [(0xA1, 0, [(0x0C, 0, ResidencePermit2)])])], True) else: dg20 = None if (dg21_param.rstrip() != "<NotOnChip>"): dg21 = dg21_param else: dg21 = None # If eid.append is not done for a DG, it results into required # SwError() with FileNotFound "6A82" APDU return code if dg1: eid.append(TransparentStructureEF(parent=eid, fid=0x0101, shortfid=0x01, data=dg1)) if dg2: eid.append(TransparentStructureEF(parent=eid, fid=0x0102, shortfid=0x02, data=dg2)) if dg3: eid.append(TransparentStructureEF(parent=eid, fid=0x0103, shortfid=0x03, data=dg3)) if dg4: eid.append(TransparentStructureEF(parent=eid, fid=0x0104, shortfid=0x04, data=dg4)) if dg5: eid.append(TransparentStructureEF(parent=eid, fid=0x0105, shortfid=0x05, data=dg5)) if dg6: eid.append(TransparentStructureEF(parent=eid, fid=0x0106, shortfid=0x06, data=dg6)) if dg7: eid.append(TransparentStructureEF(parent=eid, fid=0x0107, shortfid=0x07, data=dg7)) if dg8: eid.append(TransparentStructureEF(parent=eid, fid=0x0108, shortfid=0x08, data=dg8)) if dg9: eid.append(TransparentStructureEF(parent=eid, fid=0x0109, shortfid=0x09, data=dg9)) if dg10: eid.append(TransparentStructureEF(parent=eid, fid=0x010a, shortfid=0x0a, data=dg10)) if dg11: eid.append(TransparentStructureEF(parent=eid, fid=0x010b, shortfid=0x0b, data=dg11)) if dg12: eid.append(TransparentStructureEF(parent=eid, fid=0x010c, shortfid=0x0c, data=dg12)) if dg13: eid.append(TransparentStructureEF(parent=eid, fid=0x010d, shortfid=0x0d, data=dg13)) if dg14: eid.append(TransparentStructureEF(parent=eid, fid=0x010e, shortfid=0x0e, data=dg14)) if dg15: eid.append(TransparentStructureEF(parent=eid, fid=0x010f, shortfid=0x0f, data=dg15)) if dg16: eid.append(TransparentStructureEF(parent=eid, fid=0x0110, shortfid=0x10, data=dg16)) if dg17: eid.append(TransparentStructureEF(parent=eid, fid=0x0111, shortfid=0x11, data=dg17)) if dg18: eid.append(TransparentStructureEF(parent=eid, fid=0x0112, shortfid=0x12, data=dg18)) if dg19: eid.append(TransparentStructureEF(parent=eid, fid=0x0113, shortfid=0x13, data=dg19)) if dg20: eid.append(TransparentStructureEF(parent=eid, fid=0x0114, shortfid=0x14, data=dg20)) if dg21: eid.append(TransparentStructureEF(parent=eid, fid=0x0115, shortfid=0x15, data=dg21)) self.mf.append(eid) # DF.CIA cia = DF(parent=self.mf, fid=0xfffe, dfname=b'\xE8\x28\xBD\x08\x0F\xA0\x00\x00\x01\x67\x45\x53\x49' b'\x47\x4E') # EF.OD / EF.ODF cia.append(TransparentStructureEF(parent=cia, fid=0x5031, shortfid=0x11, data=b'\xa0\x060\x04\x04\x02D\x00\xa4' b'\x060\x04\x04\x02D\x04\xa8\x060\x04' b'\x04\x02D\x08')) # EF.CIAInfo / EF.TokenInfo cia.append(TransparentStructureEF(parent=cia, fid=0x5032, shortfid=0x12, data=b'06\x02\x01\x01\x80\x11eSign ' b'Application\x03\x02\x06\xc0\xa2' b'\x1a0\x18\x02\x01\x01\x02\x02\x10A' b'\x05\x00\x03\x02\x06@\x06\t\x04\x00' b'\x7f\x00\x07\x01\x01\x04\x01')) # EF.PrKD / EF.PrKDF cia.append(TransparentStructureEF(parent=cia, fid=0x4400, data=b'\xa080\x17\x0c\x0bPrK.ICC.QES' b'\x03\x02\x07\x80\x04\x01\x01\x02' b'\x01\x010\x15\x04\x01F\x03\x03\x06' b'\x00@\x03\x02\x03\xb8\x02\x02\x00' b'\x84\xa1\x03\x02\x01\x01\xa1\x060' b'\x040\x02\x04\x00')) # EF.PuKD / EF.PuKDF cia.append(TransparentStructureEF(parent=cia, fid=0x4404, data=b'050!\x0c\x1fZertifikat des ZDA' b' f\xc3\xbcr die QES0\x06\x04\x01E' b'\x01\x01\xff\xa1\x080\x060\x04\x04' b'\x02\xc0\x000:0&\x0c$Zertifikat des' b' Inhabers f\xc3\xbcr die QES0\x06' b'\x04\x01F\x01\x01\x00\xa1\x080\x060' b'\x04\x04\x02\xc0\x01')) # EF.AOD / EF.AODF cia.append(TransparentStructureEF(parent=cia, fid=0x4408, data=b'0/0\x0f\x0c\teSign-PIN\x03\x02' b'\x06@0\x03\x04\x01\x01\xa1\x170\x15' b'\x03\x03\x02H\x1c\n\x01\x01\x02\x01' b'\x06\x02\x01\x00\x80\x01\x810\x02' b'\x04\x00')) self.mf.append(cia) # DF.eSign esign = DF(parent=self.mf, fid=0xfffd, dfname=b'\xA0\x00\x00\x01\x67\x45\x53\x49\x47\x4E') # ZDA certificate esign.append(TransparentStructureEF(parent=esign, fid=0xC000, data=b'')) # User's certificate esign.append(TransparentStructureEF(parent=esign, fid=0xC001, data=b'')) self.mf.append(esign) self.sam = nPA_SAM(eid_pin="111111".encode('ascii'), can="222222".encode('ascii'), mrz="IDD<<T220001293<<<<<<<<<<<<<<<6408125<2010315D" "<<<<<<<<<<<<<<MUSTERMANN<<ERIKA<<<<<<<<<<<<<".encode('ascii'), puk="3333333333".encode('ascii'), qes_pin="444444".encode('ascii'), mf=self.mf) # FIXME: add CVCA for inspection systems and signature terminals. Here # we only add the eID CVCA. self.sam.current_SE.cvca = b"\x7f\x21\x82\x01\xb6\x7f\x4e\x82\x01\x6e"\ b"\x5f\x29\x01\x00\x42\x0e\x44\x45\x43\x56"\ b"\x43\x41\x65\x49\x44\x30\x30\x31\x30\x32"\ b"\x7f\x49\x82\x01\x1d\x06\x0a\x04\x00\x7f"\ b"\x00\x07\x02\x02\x02\x02\x03\x81\x20\xa9"\ b"\xfb\x57\xdb\xa1\xee\xa9\xbc\x3e\x66\x0a"\ b"\x90\x9d\x83\x8d\x72\x6e\x3b\xf6\x23\xd5"\ b"\x26\x20\x28\x20\x13\x48\x1d\x1f\x6e\x53"\ b"\x77\x82\x20\x7d\x5a\x09\x75\xfc\x2c\x30"\ b"\x57\xee\xf6\x75\x30\x41\x7a\xff\xe7\xfb"\ b"\x80\x55\xc1\x26\xdc\x5c\x6c\xe9\x4a\x4b"\ b"\x44\xf3\x30\xb5\xd9\x83\x20\x26\xdc\x5c"\ b"\x6c\xe9\x4a\x4b\x44\xf3\x30\xb5\xd9\xbb"\ b"\xd7\x7c\xbf\x95\x84\x16\x29\x5c\xf7\xe1"\ b"\xce\x6b\xcc\xdc\x18\xff\x8c\x07\xb6\x84"\ b"\x41\x04\x8b\xd2\xae\xb9\xcb\x7e\x57\xcb"\ b"\x2c\x4b\x48\x2f\xfc\x81\xb7\xaf\xb9\xde"\ b"\x27\xe1\xe3\xbd\x23\xc2\x3a\x44\x53\xbd"\ b"\x9a\xce\x32\x62\x54\x7e\xf8\x35\xc3\xda"\ b"\xc4\xfd\x97\xf8\x46\x1a\x14\x61\x1d\xc9"\ b"\xc2\x77\x45\x13\x2d\xed\x8e\x54\x5c\x1d"\ b"\x54\xc7\x2f\x04\x69\x97\x85\x20\xa9\xfb"\ b"\x57\xdb\xa1\xee\xa9\xbc\x3e\x66\x0a\x90"\ b"\x9d\x83\x8d\x71\x8c\x39\x7a\xa3\xb5\x61"\ b"\xa6\xf7\x90\x1e\x0e\x82\x97\x48\x56\xa7"\ b"\x86\x41\x04\x33\x47\xec\xf9\x6f\xfb\x4b"\ b"\xd9\xb8\x55\x4e\xfb\xcc\xfc\x7d\x0b\x24"\ b"\x2f\x10\x71\xe2\x9b\x4c\x9c\x62\x2c\x79"\ b"\xe3\x39\xd8\x40\xaf\x67\xbe\xb9\xb9\x12"\ b"\x69\x22\x65\xd9\xc1\x6c\x62\x57\x3f\x45"\ b"\x79\xff\xd4\xde\x2d\xe9\x2b\xab\x40\x9d"\ b"\xd5\xc5\xd4\x82\x44\xa9\xf7\x87\x01\x01"\ b"\x5f\x20\x0e\x44\x45\x43\x56\x43\x41\x65"\ b"\x49\x44\x30\x30\x31\x30\x32\x7f\x4c\x12"\ b"\x06\x09\x04\x00\x7f\x00\x07\x03\x01\x02"\ b"\x02\x53\x05\xfe\x0f\x01\xff\xff\x5f\x25"\ b"\x06\x01\x00\x01\x00\x01\x08\x5f\x24\x06"\ b"\x01\x03\x01\x00\x01\x08\x5f\x37\x40\x50"\ b"\x67\x14\x5c\x68\xca\xe9\x52\x0f\x5b\xb3"\ b"\x48\x17\xf1\xca\x9c\x43\x59\x3d\xb5\x64"\ b"\x06\xc6\xa3\xb0\x06\xcb\xf3\xf3\x14\xe7"\ b"\x34\x9a\xcf\x0c\xc6\xbf\xeb\xcb\xde\xfd"\ b"\x10\xb4\xdc\xf0\xf2\x31\xda\x56\x97\x7d"\ b"\x88\xf9\xf9\x01\x82\xd1\x99\x07\x6a\x56"\ b"\x50\x64\x51"
def get_card_number(self): return SW["NORMAL"], inttostring(self.cardNumber)
def generate_public_key_pair(self, p1, p2, data): """ The GENERATE PUBLIC-KEY PAIR command either initiates the generation and storing of a key pair, i.e., a public key and a private key, in the card, or accesses a key pair previously generated in the card. :param p1: should be 0x00 (generate new key) :param p2: '00' (no information provided) or reference of the key to be generated :param data: One or more CRTs associated to the key generation if P1-P2 different from '0000' """ from Crypto.PublicKey import RSA, DSA from Crypto.Util.randpool import RandomPool rnd = RandomPool() cipher = self.ct.algorithm c_class = locals().get(cipher, None) if c_class is None: raise SwError(SW["ERR_CONDITIONNOTSATISFIED"]) if p1 & 0x01 == 0x00: # Generate key PublicKey = c_class.generate(self.dst.keylength, rnd.get_bytes) self.dst.key = PublicKey else: pass # Read key # Encode keys if cipher == "RSA": # Public key n = inttostring(PublicKey.__getstate__()['n']) e = inttostring(PublicKey.__getstate__()['e']) pk = ((0x81, len(n), n), (0x82, len(e), e)) result = bertlv_pack(pk) # Private key d = PublicKey.__getstate__()['d'] elif cipher == "DSA": # DSAParams p = inttostring(PublicKey.__getstate__()['p']) q = inttostring(PublicKey.__getstate__()['q']) g = inttostring(PublicKey.__getstate__()['g']) # Public key y = inttostring(PublicKey.__getstate__()['y']) pk = ((0x81, len(p), p), (0x82, len(q), q), (0x83, len(g), g), (0x84, len(y), y)) # Private key x = inttostring(PublicKey.__getstate__()['x']) # Add more algorithms here # elif cipher = "ECDSA": else: raise SwError(SW["ERR_CONDITIONNOTSATISFIED"]) result = bertlv_pack([[0x7F49, len(pk), pk]]) # TODO: Internally store key pair if p1 & 0x02 == 0x02: # We do not support extended header lists yet raise SwError["ERR_NOTSUPPORTED"] else: return SW["NORMAL"], result
def __generate_ePass(self): """Generate the MF and SAM of an ICAO passport. This method is responsible for generating the filesystem and filling it with content. Therefore it must interact with the user by prompting for the MRZ and optionally for the path to a photo.""" from PIL import Image from virtualsmartcard.cards.ePass import PassportSAM # TODO: Sanity checks MRZ = raw_input("Please enter the MRZ as one string: ") readline.set_completer_delims("") readline.parse_and_bind("tab: complete") picturepath = raw_input("Please enter the path to an image: ") picturepath = picturepath.strip() # MRZ1 = "P<UTOERIKSSON<<ANNA<MARIX<<<<<<<<<<<<<<<<<<<" # MRZ2 = "L898902C<3UTO6908061F9406236ZE184226B<<<<<14" # MRZ = MRZ1 + MRZ2 try: im = Image.open(picturepath) pic_width, pic_height = im.size fd = open(picturepath, "rb") picture = fd.read() fd.close() except IOError: logging.warning("Failed to open file: " + picturepath) pic_width = 0 pic_height = 0 picture = None mf = MF() # We need a MF with Application DF \xa0\x00\x00\x02G\x10\x01 df = DF(parent=mf, fid=4, dfname='\xa0\x00\x00\x02G\x10\x01', bertlv_data=[]) # EF.COM COM = pack([(0x5F01, 4, "0107"), (0x5F36, 6, "040000"), (0x5C, 2, "6175")]) COM = pack(((0x60, len(COM), COM),)) df.append(TransparentStructureEF(parent=df, fid=0x011E, filedescriptor=0, data=COM)) # EF.DG1 DG1 = pack([(0x5F1F, len(MRZ), MRZ)]) DG1 = pack([(0x61, len(DG1), DG1)]) df.append(TransparentStructureEF(parent=df, fid=0x0101, filedescriptor=0, data=DG1)) # EF.DG2 if picture is not None: IIB = "\x00\x01" + inttostring(pic_width, 2) +\ inttostring(pic_height, 2) + 6 * "\x00" length = 32 + len(picture) # 32 is the length of IIB + FIB FIB = inttostring(length, 4) + 16 * "\x00" FRH = "FAC" + "\x00" + "010" + "\x00" +\ inttostring(14 + length, 4) + inttostring(1, 2) picture = FRH + FIB + IIB + picture DG2 = pack([(0xA1, 8, "\x87\x02\x01\x01\x88\x02\x05\x01"), (0x5F2E, len(picture), picture)]) DG2 = pack([(0x02, 1, "\x01"), (0x7F60, len(DG2), DG2)]) DG2 = pack([(0x7F61, len(DG2), DG2)]) else: DG2 = "" df.append(TransparentStructureEF(parent=df, fid=0x0102, filedescriptor=0, data=DG2)) # EF.SOD df.append(TransparentStructureEF(parent=df, fid=0x010D, filedescriptor=0, data="")) mf.append(df) self.mf = mf self.sam = PassportSAM(self.mf)
def selectFile(self, p1, p2, data): """ Function for instruction 0xa4. Takes the parameter bytes 'p1', 'p2' as integers and 'data' as binary string. Returns the status bytes as two byte long integer and the response data as binary string. """ P2_FCI = 0 P2_FCP = 1 << 2 P2_FMD = 2 << 2 P2_NONE = 3 << 2 file = self._selectFile(p1, p2, data) if isinstance(file, EF): # File size (body only) size = inttostring(min(0xffff, len(file.getenc('data'))), 2) extra = bytes(0) # RFU if (isinstance(file, RecordStructureEF) and file.hasFixedRecordSize() and not file.isCyclic()): # Length of records extra += bytes(0) + bytes(min(file.records, 0xff)) elif isinstance(file, DF): # Number of unused EEPROM bytes available in the DF size = inttostring(0xffff, 2) efcount = 0 dfcount = 0 chv1 = None chv2 = None chv1lifecycle = 0 chv2lifecycle = 0 for f in self.content: if f.fid == 0x0000: chv1 = f chv1lifecycle = f.lifecycle & 1 if f.fid == 0x0100: chv2 = f chv2lifecycle = f.lifecycle & 1 if isinstance(f, EF): efcount += 1 if isinstance(f, DF): dfcount += 1 if chv1: extra = bytes(1) # TODO LSB correct? else: extra = bytes(0) # TODO LSB correct? extra += bytes(efcount) extra += bytes(dfcount) extra += bytes(0) # TODO Number of PINs and unblock CHV PINs extra += bytes(0) # RFU if chv1: extra += bytes(0) # TODO remaining CHV1 attempts extra += bytes(0) # TODO remaining unblock CHV1 attempts if chv2: extra += bytes(0) # TODO CHV2 key status extra += bytes(0) # TODO CHV2 unblocking key status data = inttostring(0, 2) # RFU data += size data += inttostring(file.fid, 2) data += inttostring(file.filedescriptor) # File type data += inttostring(0, 4) # ACs TODO data += bytes(file.lifecycle & 1) # File status data += bytes(len(extra)) data += extra self.current = file return SW["NORMAL"], data
def __generate_nPA(self): from virtualsmartcard.cards.nPA import nPA_SAM mappings = [ (b'\x04\x00\x7f\x00\x07\x02\x02\x04', range(1, 5), range(1, 5), "PACE"), (b'\x04\x00\x7f\x00\x07\x02\x02\x02', range(1, 3), range(1, 6), "TA"), (b'\x04\x00\x7f\x00\x07\x02\x02\x02', [1], [6], "TA"), (b'\x04\x00\x7f\x00\x07\x02\x02\x03', range(1, 3), range(1, 5), "CA"), (b'\x04\x00\x7f\x00\x07\x02\x02\x05', range(1, 3), range(1, 6), "RI"), ] for oid_base, x_list, y_list, algo in mappings: for oid in [ oid_base + inttostring(x) + inttostring(y) for x in x_list for y in y_list ]: ALGO_MAPPING[oid] = algo ALGO_MAPPING[b"\x04\x00\x7f\x00\x07\x03\x01\x04\x03"] = "CommunityID" ALGO_MAPPING[b"\x04\x00\x7f\x00\x07\x03\x01\x04\x02"] = "DateOfExpiry" ALGO_MAPPING[b"\x04\x00\x7f\x00\x07\x03\x01\x04\x01"] = "DateOfBirth" self.mf = MF() card_access = b"\x31\x81\xb3\x30\x0d\x06\x08\x04\x00\x7f\x00\x07\x02"\ b"\x02\x02\x02\x01\x02\x30\x12\x06\x0a\x04\x00\x7f\x00"\ b"\x07\x02\x02\x03\x02\x02\x02\x01\x02\x02\x01\x41\x30"\ b"\x12\x06\x0a\x04\x00\x7f\x00\x07\x02\x02\x03\x02\x02"\ b"\x02\x01\x02\x02\x01\x45\x30\x12\x06\x0a\x04\x00\x7f"\ b"\x00\x07\x02\x02\x04\x02\x02\x02\x01\x02\x02\x01\x0d"\ b"\x30\x1c\x06\x09\x04\x00\x7f\x00\x07\x02\x02\x03\x02"\ b"\x30\x0c\x06\x07\x04\x00\x7f\x00\x07\x01\x02\x02\x01"\ b"\x0d\x02\x01\x41\x30\x1c\x06\x09\x04\x00\x7f\x00\x07"\ b"\x02\x02\x03\x02\x30\x0c\x06\x07\x04\x00\x7f\x00\x07"\ b"\x01\x02\x02\x01\x0d\x02\x01\x45\x30\x2a\x06\x08\x04"\ b"\x00\x7f\x00\x07\x02\x02\x06\x16\x1e\x68\x74\x74\x70"\ b"\x3a\x2f\x2f\x62\x73\x69\x2e\x62\x75\x6e\x64\x2e\x64"\ b"\x65\x2f\x63\x69\x66\x2f\x6e\x70\x61\x2e\x78\x6d\x6c" self.mf.append( TransparentStructureEF(parent=self.mf, fid=0x011c, shortfid=0x1c, data=card_access)) card_security = b"\x30\x82\x05\xA0\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01"\ b"\x07\x02\xA0\x82\x05\x91\x30\x82\x05\x8D\x02\x01\x03"\ b"\x31\x0F\x30\x0D\x06\x09\x60\x86\x48\x01\x65\x03\x04"\ b"\x02\x01\x05\x00\x30\x82\x01\x48\x06\x08\x04\x00\x7F"\ b"\x00\x07\x03\x02\x01\xA0\x82\x01\x3A\x04\x82\x01\x36"\ b"\x31\x82\x01\x32\x30\x0D\x06\x08\x04\x00\x7F\x00\x07"\ b"\x02\x02\x02\x02\x01\x02\x30\x12\x06\x0A\x04\x00\x7F"\ b"\x00\x07\x02\x02\x03\x02\x02\x02\x01\x02\x02\x01\x41"\ b"\x30\x12\x06\x0A\x04\x00\x7F\x00\x07\x02\x02\x04\x02"\ b"\x02\x02\x01\x02\x02\x01\x0D\x30\x17\x06\x0A\x04\x00"\ b"\x7F\x00\x07\x02\x02\x05\x02\x03\x30\x09\x02\x01\x01"\ b"\x02\x01\x43\x01\x01\xFF\x30\x17\x06\x0A\x04\x00\x7F"\ b"\x00\x07\x02\x02\x05\x02\x03\x30\x09\x02\x01\x01\x02"\ b"\x01\x44\x01\x01\x00\x30\x19\x06\x09\x04\x00\x7F\x00"\ b"\x07\x02\x02\x05\x02\x30\x0C\x06\x07\x04\x00\x7F\x00"\ b"\x07\x01\x02\x02\x01\x0D\x30\x1C\x06\x09\x04\x00\x7F"\ b"\x00\x07\x02\x02\x03\x02\x30\x0C\x06\x07\x04\x00\x7F"\ b"\x00\x07\x01\x02\x02\x01\x0D\x02\x01\x41\x30\x2A\x06"\ b"\x08\x04\x00\x7F\x00\x07\x02\x02\x06\x16\x1E\x68\x74"\ b"\x74\x70\x3A\x2F\x2F\x62\x73\x69\x2E\x62\x75\x6E\x64"\ b"\x2E\x64\x65\x2F\x63\x69\x66\x2F\x6E\x70\x61\x2E\x78"\ b"\x6D\x6C\x30\x62\x06\x09\x04\x00\x7F\x00\x07\x02\x02"\ b"\x01\x02\x30\x52\x30\x0C\x06\x07\x04\x00\x7F\x00\x07"\ b"\x01\x02\x02\x01\x0D\x03\x42\x00\x04\x92\x5D\xB4\xE1"\ b"\x7A\xDE\x58\x20\x9F\x96\xFA\xA0\x7F\x1F\x8A\x22\x3F"\ b"\x82\x3F\x96\xCC\x5D\x78\xCB\xEF\x5D\x17\x42\x20\x88"\ b"\xFD\xD5\x8E\x56\xBC\x42\x50\xDE\x33\x46\xB3\xC8\x32"\ b"\xCA\xE4\x86\x35\xFB\x6C\x43\x78\x9D\xE8\xB3\x10\x2F"\ b"\x43\x93\xB4\x18\xE2\x4A\x13\xD9\x02\x01\x41\xA0\x82"\ b"\x03\x1C\x30\x82\x03\x18\x30\x82\x02\xBC\xA0\x03\x02"\ b"\x01\x02\x02\x02\x01\x5C\x30\x0C\x06\x08\x2A\x86\x48"\ b"\xCE\x3D\x04\x03\x02\x05\x00\x30\x4F\x31\x0B\x30\x09"\ b"\x06\x03\x55\x04\x06\x13\x02\x44\x45\x31\x0D\x30\x0B"\ b"\x06\x03\x55\x04\x0A\x0C\x04\x62\x75\x6E\x64\x31\x0C"\ b"\x30\x0A\x06\x03\x55\x04\x0B\x0C\x03\x62\x73\x69\x31"\ b"\x0C\x30\x0A\x06\x03\x55\x04\x05\x13\x03\x30\x31\x33"\ b"\x31\x15\x30\x13\x06\x03\x55\x04\x03\x0C\x0C\x63\x73"\ b"\x63\x61\x2D\x67\x65\x72\x6D\x61\x6E\x79\x30\x1E\x17"\ b"\x0D\x31\x30\x31\x30\x30\x35\x31\x31\x30\x37\x35\x32"\ b"\x5A\x17\x0D\x32\x31\x30\x34\x30\x35\x30\x38\x33\x39"\ b"\x34\x37\x5A\x30\x47\x31\x0B\x30\x09\x06\x03\x55\x04"\ b"\x06\x13\x02\x44\x45\x31\x1D\x30\x1B\x06\x03\x55\x04"\ b"\x0A\x0C\x14\x42\x75\x6E\x64\x65\x73\x64\x72\x75\x63"\ b"\x6B\x65\x72\x65\x69\x20\x47\x6D\x62\x48\x31\x0C\x30"\ b"\x0A\x06\x03\x55\x04\x05\x13\x03\x30\x37\x34\x31\x0B"\ b"\x30\x09\x06\x03\x55\x04\x03\x0C\x02\x44\x53\x30\x82"\ b"\x01\x13\x30\x81\xD4\x06\x07\x2A\x86\x48\xCE\x3D\x02"\ b"\x01\x30\x81\xC8\x02\x01\x01\x30\x28\x06\x07\x2A\x86"\ b"\x48\xCE\x3D\x01\x01\x02\x1D\x00\xD7\xC1\x34\xAA\x26"\ b"\x43\x66\x86\x2A\x18\x30\x25\x75\xD1\xD7\x87\xB0\x9F"\ b"\x07\x57\x97\xDA\x89\xF5\x7E\xC8\xC0\xFF\x30\x3C\x04"\ b"\x1C\x68\xA5\xE6\x2C\xA9\xCE\x6C\x1C\x29\x98\x03\xA6"\ b"\xC1\x53\x0B\x51\x4E\x18\x2A\xD8\xB0\x04\x2A\x59\xCA"\ b"\xD2\x9F\x43\x04\x1C\x25\x80\xF6\x3C\xCF\xE4\x41\x38"\ b"\x87\x07\x13\xB1\xA9\x23\x69\xE3\x3E\x21\x35\xD2\x66"\ b"\xDB\xB3\x72\x38\x6C\x40\x0B\x04\x39\x04\x0D\x90\x29"\ b"\xAD\x2C\x7E\x5C\xF4\x34\x08\x23\xB2\xA8\x7D\xC6\x8C"\ b"\x9E\x4C\xE3\x17\x4C\x1E\x6E\xFD\xEE\x12\xC0\x7D\x58"\ b"\xAA\x56\xF7\x72\xC0\x72\x6F\x24\xC6\xB8\x9E\x4E\xCD"\ b"\xAC\x24\x35\x4B\x9E\x99\xCA\xA3\xF6\xD3\x76\x14\x02"\ b"\xCD\x02\x1D\x00\xD7\xC1\x34\xAA\x26\x43\x66\x86\x2A"\ b"\x18\x30\x25\x75\xD0\xFB\x98\xD1\x16\xBC\x4B\x6D\xDE"\ b"\xBC\xA3\xA5\xA7\x93\x9F\x02\x01\x01\x03\x3A\x00\x04"\ b"\x3D\x6A\x7C\x2A\x6F\x20\x5F\x83\x9B\x04\x14\xEC\x58"\ b"\xC6\xC7\x1B\x75\xF5\x15\xDE\xC3\xAE\x73\x3B\x5F\x47"\ b"\x88\xDD\xC8\x15\xF0\x5B\xC1\xF6\x53\x8F\xD9\x69\x54"\ b"\xE1\xF8\x40\xA2\xE2\x18\x99\x62\xCC\xAA\x14\x90\x08"\ b"\x24\xC7\xDD\xB9\xA3\x81\xD1\x30\x81\xCE\x30\x0E\x06"\ b"\x03\x55\x1D\x0F\x01\x01\xFF\x04\x04\x03\x02\x07\x80"\ b"\x30\x1F\x06\x03\x55\x1D\x23\x04\x18\x30\x16\x80\x14"\ b"\x60\x44\xF2\x45\xF2\xE0\x71\xD4\xD5\x64\xF4\xE5\x77"\ b"\xD6\x36\x69\xDB\xEB\x18\x59\x30\x41\x06\x03\x55\x1D"\ b"\x20\x04\x3A\x30\x38\x30\x36\x06\x09\x04\x00\x7F\x00"\ b"\x07\x03\x01\x01\x01\x30\x29\x30\x27\x06\x08\x2B\x06"\ b"\x01\x05\x05\x07\x02\x01\x16\x1B\x68\x74\x74\x70\x3A"\ b"\x2F\x2F\x77\x77\x77\x2E\x62\x73\x69\x2E\x62\x75\x6E"\ b"\x64\x2E\x64\x65\x2F\x63\x73\x63\x61\x30\x2B\x06\x09"\ b"\x2A\x86\x48\x86\xF7\x0D\x01\x09\x15\x04\x1E\x04\x1C"\ b"\x31\x2E\x32\x2E\x32\x37\x36\x2E\x30\x2E\x38\x30\x2E"\ b"\x31\x2E\x31\x32\x2E\x30\x2E\x32\x30\x2E\x35\x2E\x31"\ b"\x2E\x30\x30\x2B\x06\x03\x55\x1D\x10\x04\x24\x30\x22"\ b"\x80\x0F\x32\x30\x31\x30\x31\x30\x30\x35\x31\x31\x30"\ b"\x37\x35\x32\x5A\x81\x0F\x32\x30\x31\x31\x30\x32\x30"\ b"\x35\x30\x39\x33\x39\x34\x37\x5A\x30\x0C\x06\x08\x2A"\ b"\x86\x48\xCE\x3D\x04\x03\x02\x05\x00\x03\x48\x00\x30"\ b"\x45\x02\x20\x13\xE9\xE1\x7A\x9E\xFE\x8B\xD7\xD7\x27"\ b"\x62\x92\x30\x5B\xCC\xC3\x2B\x70\xC2\xB7\x60\x40\xF4"\ b"\x88\x30\x66\x62\x26\xCD\x6A\x4B\xF4\x02\x21\x00\x87"\ b"\xF4\x71\xE2\x44\x35\xB4\xC3\x4A\xF3\x57\x30\x94\xFB"\ b"\x1F\x1C\x2A\x48\xB1\x3E\xE5\xED\x67\xF1\x72\x6D\xCF"\ b"\x56\xE3\x84\xE3\x6F\x31\x82\x01\x09\x30\x82\x01\x05"\ b"\x02\x01\x01\x30\x55\x30\x4F\x31\x0B\x30\x09\x06\x03"\ b"\x55\x04\x06\x13\x02\x44\x45\x31\x0D\x30\x0B\x06\x03"\ b"\x55\x04\x0A\x0C\x04\x62\x75\x6E\x64\x31\x0C\x30\x0A"\ b"\x06\x03\x55\x04\x0B\x0C\x03\x62\x73\x69\x31\x0C\x30"\ b"\x0A\x06\x03\x55\x04\x05\x13\x03\x30\x31\x33\x31\x15"\ b"\x30\x13\x06\x03\x55\x04\x03\x0C\x0C\x63\x73\x63\x61"\ b"\x2D\x67\x65\x72\x6D\x61\x6E\x79\x02\x02\x01\x5C\x30"\ b"\x0D\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05"\ b"\x00\xA0\x4A\x30\x17\x06\x09\x2A\x86\x48\x86\xF7\x0D"\ b"\x01\x09\x03\x31\x0A\x06\x08\x04\x00\x7F\x00\x07\x03"\ b"\x02\x01\x30\x2F\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01"\ b"\x09\x04\x31\x22\x04\x20\xEF\x0F\xDA\x94\x2E\x5A\x0F"\ b"\x6F\xC9\xC5\x46\xEE\x01\xF9\x10\x31\x43\x64\x30\xF7"\ b"\x5E\x9D\x36\x54\xD3\x69\x30\x9E\x8B\xE7\x17\x48\x30"\ b"\x0C\x06\x08\x2A\x86\x48\xCE\x3D\x04\x03\x02\x05\x00"\ b"\x04\x40\x30\x3E\x02\x1D\x00\x89\x75\x92\x5B\xE1\x31"\ b"\xB7\x7C\x95\x8C\x3E\xCB\x2A\x5C\x67\xFC\x5C\xE3\x1C"\ b"\xBD\x01\x41\xE3\x4B\xC7\xF0\xA4\x47\x02\x1D\x00\xCC"\ b"\x65\xE6\x2D\xDC\xF2\x93\x96\x4B\x22\xD7\xB5\x10\xD7"\ b"\x81\x88\x07\xC8\x95\x96\xBD\x34\xD8\xF9\xBB\x4C\x05"\ b"\x27" self.mf.append( TransparentStructureEF(parent=self.mf, fid=0x011d, shortfid=0x1d, data=card_security)) ef_dir = b"\x61\x32\x4F\x0F\xE8\x28\xBD\x08\x0F\xA0\x00\x00\x01\x67"\ b"\x45\x53\x49\x47\x4E\x50\x0F\x43\x49\x41\x20\x7A\x75\x20"\ b"\x44\x46\x2E\x65\x53\x69\x67\x6E\x51\x00\x73\x0C\x4F\x0A"\ b"\xA0\x00\x00\x01\x67\x45\x53\x49\x47\x4E\x61\x09\x4F\x07"\ b"\xA0\x00\x00\x02\x47\x10\x01\x61\x0B\x4F\x09\xE8\x07\x04"\ b"\x00\x7F\x00\x07\x03\x02\x61\x0C\x4F\x0A\xA0\x00\x00\x01"\ b"\x67\x45\x53\x49\x47\x4E" self.mf.append( TransparentStructureEF(parent=self.mf, fid=0x2f00, shortfid=30, data=ef_dir)) eid = DF(parent=self.mf, fid=0xffff, dfname=b'\xe8\x07\x04\x00\x7f\x00\x07\x03\x02') eid.extra_fci_data = b"\xab\x75\x84\x01\xa4\xaf\x70\xa0\x10\xb4\x06"\ b"\x95\x01\x20\x83\x01\x04\xb4\x06\x95\x01\x20"\ b"\x83\x01\x06\xa0\x54\xa4\x14\x95\x01\x80\x7f"\ b"\x4c\x0e\x06\x09\x04\x00\x7f\x00\x07\x03\x01"\ b"\x02\x01\x53\x01\x00\xaf\x22\xa4\x18\x95\x01"\ b"\x80\x7f\x4c\x12\x06\x09\x04\x00\x7f\x00\x07"\ b"\x03\x01\x02\x02\x53\x05\x00\x00\x00\x00\x00"\ b"\xa4\x06\x95\x01\x80\x83\x01\x47\xa4\x18\x95"\ b"\x01\x80\x7f\x4c\x12\x06\x09\x04\x00\x7f\x00"\ b"\x07\x03\x01\x02\x02\x53\x05\x00\x00\x00\x00"\ b"\x10\x7a\x06\x8a\x01\x05\x9e\x01\x03" # FIXME access control for eid application DocumentType = self.datagroups["DocumentType"] if "DocumentType" in \ self.datagroups else 'ID' IssuingState = self.datagroups["IssuingState"] if "IssuingState" in \ self.datagroups else 'D' DateOfExpiry = self.datagroups["DateOfExpiry"] if "DateOfExpiry" in \ self.datagroups else '20201031' GivenNames = self.datagroups["GivenNames"] if "GivenNames" in \ self.datagroups else 'ERIKA' FamilyNames = self.datagroups["FamilyNames"] if "FamilyNames" in \ self.datagroups else 'MUSTERMANN' ReligiousArtisticName = self.datagroups["ReligiousArtisticName"] if \ "ReligiousArtisticName" in self.datagroups else '' AcademicTitle = self.datagroups["AcademicTitle"] if \ "AcademicTitle" in self.datagroups else '' DateOfBirth = self.datagroups["DateOfBirth"] if "DateOfBirth" in \ self.datagroups else '19640812' PlaceOfBirth = self.datagroups["PlaceOfBirth"] if "PlaceOfBirth" in \ self.datagroups else 'BERLIN' Nationality = self.datagroups["Nationality"] if "Nationality" in \ self.datagroups else 'DE' Sex = self.datagroups["Sex"] if "Sex" in self.datagroups else 'F' BirthName = self.datagroups["BirthName"] if "BirthName" in \ self.datagroups else 'Mein Geburtsname' # PlaceOfResidence variable is only a helper to get a switch for # <NotOnChip> PlaceOfResidence = self.datagroups["PlaceOfResidence"] if \ "PlaceOfResidence" in self.datagroups else '' Country = self.datagroups["Country"] if "Country" in self.datagroups \ else 'D' City = self.datagroups["City"] if "City" in self.datagroups else \ 'KOLN' ZIP = self.datagroups["ZIP"] if "ZIP" in self.datagroups else '51147' Street = self.datagroups["Street"] if "Street" in \ self.datagroups else 'HEIDESTRASSE 17' CommunityID = self.datagroups["CommunityID"] if "CommunityID" in \ self.datagroups else '02760378900276' if (CommunityID.rstrip() != "<NotOnChip>"): # the plain CommunityID integer value has to be translated into # its binary representation. '0276...' will be '\x02\x76\...' CommunityID_Binary = binascii.unhexlify(CommunityID) # ResidencePermit1 and ResidencePermit2 are part of eAT only ResidencePermit1 = self.datagroups["ResidencePermit1"] if \ "ResidencePermit1" in self.datagroups else \ 'ResidencePermit1 field up to 750 characters' ResidencePermit2 = self.datagroups["ResidencePermit2"] if \ "ResidencePermit2" in self.datagroups else \ 'ResidencePermit1 field up to 250 characters' # Currently, those data groups are for further usage: dg12_param = self.datagroups["dg12"] if "dg12" in \ self.datagroups else '' dg14_param = self.datagroups["dg14"] if "dg14" in \ self.datagroups else '' dg15_param = self.datagroups["dg15"] if "dg15" in \ self.datagroups else '' dg16_param = self.datagroups["dg16"] if "dg16" in \ self.datagroups else '' dg21_param = self.datagroups["dg21"] if "dg21" in \ self.datagroups else '' # "Attribute not on Chip" makes sence only for ReligiousArtisticName, # Nationality, BirthName, ResidencePermit1 and ResidencePermit2, refer # to BSI TR-03127 if (DocumentType.rstrip() != "<NotOnChip>"): dg1 = pack([(0x61, 0, [(0x13, 0, DocumentType)])], True) else: dg1 = None if (IssuingState.rstrip() != "<NotOnChip>"): dg2 = pack([(0x62, 0, [(0x13, 0, IssuingState)])], True) else: dg2 = None if (DateOfExpiry.rstrip() != "<NotOnChip>"): dg3 = pack([(0x63, 0, [(0x12, 0, DateOfExpiry)])], True) else: dg3 = None if (GivenNames.rstrip() != "<NotOnChip>"): dg4 = pack([(0x64, 0, [(0x0C, 0, GivenNames)])], True) else: dg4 = None if (FamilyNames.rstrip() != "<NotOnChip>"): dg5 = pack([(0x65, 0, [(0x0C, 0, FamilyNames)])], True) else: dg5 = None if (ReligiousArtisticName.rstrip() != "<NotOnChip>"): dg6 = pack([(0x66, 0, [(0x0C, 0, ReligiousArtisticName)])], True) else: dg6 = None if (AcademicTitle.rstrip() != "<NotOnChip>"): dg7 = pack([(0x67, 0, [(0x0C, 0, AcademicTitle)])], True) else: dg7 = None if (DateOfBirth.rstrip() != "<NotOnChip>"): dg8 = pack([(0x68, 0, [(0x12, 0, DateOfBirth)])], True) else: dg8 = None if (PlaceOfBirth.rstrip() != "<NotOnChip>"): dg9 = pack([(0x69, 0, [(0xA1, 0, [(0x0C, 0, PlaceOfBirth)])])], True) else: dg9 = None if (Nationality.rstrip() != "<NotOnChip>"): dg10 = pack([(0x6A, 0, [(0x13, 0, Nationality)])], True) else: dg10 = None if (Sex.rstrip() != "<NotOnChip>"): dg11 = pack([(0x6B, 0, [(0x13, 0, Sex)])], True) else: dg11 = None if (dg12_param.rstrip() != "<NotOnChip>"): dg12 = dg12_param else: dg12 = None if (BirthName.rstrip() != "<NotOnChip>"): dg13 = pack([(0x6D, 0, [(0x0C, 0, BirthName)])], True) else: dg13 = None if (dg14_param.rstrip() != "<NotOnChip>"): dg14 = dg14_param else: dg14 = None if (dg15_param.rstrip() != "<NotOnChip>"): dg15 = dg15_param else: dg15 = None if (dg16_param.rstrip() != "<NotOnChip>"): dg16 = dg16_param else: dg16 = None if (PlaceOfResidence.rstrip() != "<NotOnChip>"): dg17 = pack( [(0x71, 0, [(0x30, 0, [(0xAA, 0, [(0x0C, 0, Street)]), (0xAB, 0, [(0x0C, 0, City)]), (0xAD, 0, [(0x13, 0, Country)]), (0xAE, 0, [(0x13, 0, ZIP)])])])], True) else: dg17 = None if (CommunityID.rstrip() != "<NotOnChip>"): dg18 = pack([(0x72, 0, [(0x04, 0, CommunityID_Binary)])], True) else: dg18 = None if (ResidencePermit1.rstrip() != "<NotOnChip>"): dg19 = pack( [(0x73, 0, [(0xA1, 0, [(0x0C, 0, ResidencePermit1)])])], True) else: dg19 = None if (ResidencePermit1.rstrip() != "<NotOnChip>"): dg20 = pack( [(0x74, 0, [(0xA1, 0, [(0x0C, 0, ResidencePermit2)])])], True) else: dg20 = None if (dg21_param.rstrip() != "<NotOnChip>"): dg21 = dg21_param else: dg21 = None # If eid.append is not done for a DG, it results into required # SwError() with FileNotFound "6A82" APDU return code if dg1: eid.append( TransparentStructureEF(parent=eid, fid=0x0101, shortfid=0x01, data=dg1)) if dg2: eid.append( TransparentStructureEF(parent=eid, fid=0x0102, shortfid=0x02, data=dg2)) if dg3: eid.append( TransparentStructureEF(parent=eid, fid=0x0103, shortfid=0x03, data=dg3)) if dg4: eid.append( TransparentStructureEF(parent=eid, fid=0x0104, shortfid=0x04, data=dg4)) if dg5: eid.append( TransparentStructureEF(parent=eid, fid=0x0105, shortfid=0x05, data=dg5)) if dg6: eid.append( TransparentStructureEF(parent=eid, fid=0x0106, shortfid=0x06, data=dg6)) if dg7: eid.append( TransparentStructureEF(parent=eid, fid=0x0107, shortfid=0x07, data=dg7)) if dg8: eid.append( TransparentStructureEF(parent=eid, fid=0x0108, shortfid=0x08, data=dg8)) if dg9: eid.append( TransparentStructureEF(parent=eid, fid=0x0109, shortfid=0x09, data=dg9)) if dg10: eid.append( TransparentStructureEF(parent=eid, fid=0x010a, shortfid=0x0a, data=dg10)) if dg11: eid.append( TransparentStructureEF(parent=eid, fid=0x010b, shortfid=0x0b, data=dg11)) if dg12: eid.append( TransparentStructureEF(parent=eid, fid=0x010c, shortfid=0x0c, data=dg12)) if dg13: eid.append( TransparentStructureEF(parent=eid, fid=0x010d, shortfid=0x0d, data=dg13)) if dg14: eid.append( TransparentStructureEF(parent=eid, fid=0x010e, shortfid=0x0e, data=dg14)) if dg15: eid.append( TransparentStructureEF(parent=eid, fid=0x010f, shortfid=0x0f, data=dg15)) if dg16: eid.append( TransparentStructureEF(parent=eid, fid=0x0110, shortfid=0x10, data=dg16)) if dg17: eid.append( TransparentStructureEF(parent=eid, fid=0x0111, shortfid=0x11, data=dg17)) if dg18: eid.append( TransparentStructureEF(parent=eid, fid=0x0112, shortfid=0x12, data=dg18)) if dg19: eid.append( TransparentStructureEF(parent=eid, fid=0x0113, shortfid=0x13, data=dg19)) if dg20: eid.append( TransparentStructureEF(parent=eid, fid=0x0114, shortfid=0x14, data=dg20)) if dg21: eid.append( TransparentStructureEF(parent=eid, fid=0x0115, shortfid=0x15, data=dg21)) self.mf.append(eid) # DF.CIA cia = DF(parent=self.mf, fid=0xfffe, dfname=b'\xE8\x28\xBD\x08\x0F\xA0\x00\x00\x01\x67\x45\x53\x49' b'\x47\x4E') # EF.OD / EF.ODF cia.append( TransparentStructureEF(parent=cia, fid=0x5031, shortfid=0x11, data=b'\xa0\x060\x04\x04\x02D\x00\xa4' b'\x060\x04\x04\x02D\x04\xa8\x060\x04' b'\x04\x02D\x08')) # EF.CIAInfo / EF.TokenInfo cia.append( TransparentStructureEF(parent=cia, fid=0x5032, shortfid=0x12, data=b'06\x02\x01\x01\x80\x11eSign ' b'Application\x03\x02\x06\xc0\xa2' b'\x1a0\x18\x02\x01\x01\x02\x02\x10A' b'\x05\x00\x03\x02\x06@\x06\t\x04\x00' b'\x7f\x00\x07\x01\x01\x04\x01')) # EF.PrKD / EF.PrKDF cia.append( TransparentStructureEF(parent=cia, fid=0x4400, data=b'\xa080\x17\x0c\x0bPrK.ICC.QES' b'\x03\x02\x07\x80\x04\x01\x01\x02' b'\x01\x010\x15\x04\x01F\x03\x03\x06' b'\x00@\x03\x02\x03\xb8\x02\x02\x00' b'\x84\xa1\x03\x02\x01\x01\xa1\x060' b'\x040\x02\x04\x00')) # EF.PuKD / EF.PuKDF cia.append( TransparentStructureEF(parent=cia, fid=0x4404, data=b'050!\x0c\x1fZertifikat des ZDA' b' f\xc3\xbcr die QES0\x06\x04\x01E' b'\x01\x01\xff\xa1\x080\x060\x04\x04' b'\x02\xc0\x000:0&\x0c$Zertifikat des' b' Inhabers f\xc3\xbcr die QES0\x06' b'\x04\x01F\x01\x01\x00\xa1\x080\x060' b'\x04\x04\x02\xc0\x01')) # EF.AOD / EF.AODF cia.append( TransparentStructureEF(parent=cia, fid=0x4408, data=b'0/0\x0f\x0c\teSign-PIN\x03\x02' b'\x06@0\x03\x04\x01\x01\xa1\x170\x15' b'\x03\x03\x02H\x1c\n\x01\x01\x02\x01' b'\x06\x02\x01\x00\x80\x01\x810\x02' b'\x04\x00')) self.mf.append(cia) # DF.eSign esign = DF(parent=self.mf, fid=0xfffd, dfname=b'\xA0\x00\x00\x01\x67\x45\x53\x49\x47\x4E') # ZDA certificate esign.append(TransparentStructureEF(parent=esign, fid=0xC000, data=b'')) # User's certificate esign.append(TransparentStructureEF(parent=esign, fid=0xC001, data=b'')) self.mf.append(esign) self.sam = nPA_SAM( eid_pin="111111".encode('ascii'), can="222222".encode('ascii'), mrz="IDD<<T220001293<<<<<<<<<<<<<<<6408125<2010315D" "<<<<<<<<<<<<<<MUSTERMANN<<ERIKA<<<<<<<<<<<<<".encode('ascii'), puk="3333333333".encode('ascii'), qes_pin="444444".encode('ascii'), mf=self.mf) # FIXME: add CVCA for inspection systems and signature terminals. Here # we only add the eID CVCA. self.sam.current_SE.cvca = b"\x7f\x21\x82\x01\xb6\x7f\x4e\x82\x01\x6e"\ b"\x5f\x29\x01\x00\x42\x0e\x44\x45\x43\x56"\ b"\x43\x41\x65\x49\x44\x30\x30\x31\x30\x32"\ b"\x7f\x49\x82\x01\x1d\x06\x0a\x04\x00\x7f"\ b"\x00\x07\x02\x02\x02\x02\x03\x81\x20\xa9"\ b"\xfb\x57\xdb\xa1\xee\xa9\xbc\x3e\x66\x0a"\ b"\x90\x9d\x83\x8d\x72\x6e\x3b\xf6\x23\xd5"\ b"\x26\x20\x28\x20\x13\x48\x1d\x1f\x6e\x53"\ b"\x77\x82\x20\x7d\x5a\x09\x75\xfc\x2c\x30"\ b"\x57\xee\xf6\x75\x30\x41\x7a\xff\xe7\xfb"\ b"\x80\x55\xc1\x26\xdc\x5c\x6c\xe9\x4a\x4b"\ b"\x44\xf3\x30\xb5\xd9\x83\x20\x26\xdc\x5c"\ b"\x6c\xe9\x4a\x4b\x44\xf3\x30\xb5\xd9\xbb"\ b"\xd7\x7c\xbf\x95\x84\x16\x29\x5c\xf7\xe1"\ b"\xce\x6b\xcc\xdc\x18\xff\x8c\x07\xb6\x84"\ b"\x41\x04\x8b\xd2\xae\xb9\xcb\x7e\x57\xcb"\ b"\x2c\x4b\x48\x2f\xfc\x81\xb7\xaf\xb9\xde"\ b"\x27\xe1\xe3\xbd\x23\xc2\x3a\x44\x53\xbd"\ b"\x9a\xce\x32\x62\x54\x7e\xf8\x35\xc3\xda"\ b"\xc4\xfd\x97\xf8\x46\x1a\x14\x61\x1d\xc9"\ b"\xc2\x77\x45\x13\x2d\xed\x8e\x54\x5c\x1d"\ b"\x54\xc7\x2f\x04\x69\x97\x85\x20\xa9\xfb"\ b"\x57\xdb\xa1\xee\xa9\xbc\x3e\x66\x0a\x90"\ b"\x9d\x83\x8d\x71\x8c\x39\x7a\xa3\xb5\x61"\ b"\xa6\xf7\x90\x1e\x0e\x82\x97\x48\x56\xa7"\ b"\x86\x41\x04\x33\x47\xec\xf9\x6f\xfb\x4b"\ b"\xd9\xb8\x55\x4e\xfb\xcc\xfc\x7d\x0b\x24"\ b"\x2f\x10\x71\xe2\x9b\x4c\x9c\x62\x2c\x79"\ b"\xe3\x39\xd8\x40\xaf\x67\xbe\xb9\xb9\x12"\ b"\x69\x22\x65\xd9\xc1\x6c\x62\x57\x3f\x45"\ b"\x79\xff\xd4\xde\x2d\xe9\x2b\xab\x40\x9d"\ b"\xd5\xc5\xd4\x82\x44\xa9\xf7\x87\x01\x01"\ b"\x5f\x20\x0e\x44\x45\x43\x56\x43\x41\x65"\ b"\x49\x44\x30\x30\x31\x30\x32\x7f\x4c\x12"\ b"\x06\x09\x04\x00\x7f\x00\x07\x03\x01\x02"\ b"\x02\x53\x05\xfe\x0f\x01\xff\xff\x5f\x25"\ b"\x06\x01\x00\x01\x00\x01\x08\x5f\x24\x06"\ b"\x01\x03\x01\x00\x01\x08\x5f\x37\x40\x50"\ b"\x67\x14\x5c\x68\xca\xe9\x52\x0f\x5b\xb3"\ b"\x48\x17\xf1\xca\x9c\x43\x59\x3d\xb5\x64"\ b"\x06\xc6\xa3\xb0\x06\xcb\xf3\xf3\x14\xe7"\ b"\x34\x9a\xcf\x0c\xc6\xbf\xeb\xcb\xde\xfd"\ b"\x10\xb4\xdc\xf0\xf2\x31\xda\x56\x97\x7d"\ b"\x88\xf9\xf9\x01\x82\xd1\x99\x07\x6a\x56"\ b"\x50\x64\x51"
def parse_SM_CAPDU(self, CAPDU, authenticate_header): """ This methods parses a data field including Secure Messaging objects. SM_header indicates whether or not the header of the message shall be authenticated. It returns an unprotected command APDU :param CAPDU: The protected CAPDU to be parsed :param authenticate_header: Whether or not the header should be included in authentication mechanisms :returns: Unprotected command APDU """ structure = unpack(CAPDU.data) return_data = [ "", ] cla = None ins = None p1 = None p2 = None le = None if authenticate_header: to_authenticate = inttostring(CAPDU.cla) + inttostring(CAPDU.ins)+\ inttostring(CAPDU.p1) + inttostring(CAPDU.p2) to_authenticate = vsCrypto.append_padding(self.cct.blocklength, to_authenticate) else: to_authenticate = "" for tlv in structure: tag, length, value = tlv if tag % 2 == 1: #Include object in checksum calculation to_authenticate += bertlv_pack([[tag, length, value]]) #SM data objects for encapsulating plain values if tag in (SM_Class["PLAIN_VALUE_NO_TLV"], SM_Class["PLAIN_VALUE_NO_TLV_ODD"]): return_data.append(value) #FIXME: Need TLV coding? #Encapsulated SM objects. Parse them #FIXME: Need to pack value into a dummy CAPDU elif tag in (SM_Class["PLAIN_VALUE_TLV_INCULDING_SM"], SM_Class["PLAIN_VALUE_TLV_INCULDING_SM_ODD"]): return_data.append( self.parse_SM_CAPDU(value, authenticate_header)) #Encapsulated plaintext BER-TLV objects elif tag in (SM_Class["PLAIN_VALUE_TLV_NO_SM"], SM_Class["PLAIN_VALUE_TLV_NO_SM_ODD"]): return_data.append(value) elif tag in (SM_Class["Ne"], SM_Class["Ne_ODD"]): le = value elif tag == SM_Class["PLAIN_COMMAND_HEADER"]: if len(value) != 8: raise SwError(SW["ERR_SECMESSOBJECTSINCORRECT"]) else: cla = value[:2] ins = value[2:4] p1 = value[4:6] p2 = value[6:8] #SM data objects for confidentiality if tag in (SM_Class["CRYPTOGRAM_PLAIN_TLV_INCLUDING_SM"], SM_Class["CRYPTOGRAM_PLAIN_TLV_INCLUDING_SM_ODD"]): #The cryptogram includes SM objects. #We decrypt them and parse the objects. plain = self.decipher(tag, 0x80, value) #TODO: Need Le = length return_data.append( self.parse_SM_CAPDU(plain, authenticate_header)) elif tag in (SM_Class["CRYPTOGRAM_PLAIN_TLV_NO_SM"], SM_Class["CRYPTOGRAM_PLAIN_TLV_NO_SM_ODD"]): #The cryptogram includes BER-TLV encoded plaintext. #We decrypt them and return the objects. plain = self.decipher(tag, 0x80, value) return_data.append(plain) elif tag in (SM_Class["CRYPTOGRAM_PADDING_INDICATOR"], SM_Class["CRYPTOGRAM_PADDING_INDICATOR_ODD"]): #The first byte of the data field indicates the padding to use: """ Value Meaning '00' No further indication '01' Padding as specified in 6.2.3.1 '02' No padding '1X' One to four secret keys for enciphering information, not keys ('X' is a bitmap with any value from '0' to 'F') '11' indicates the first key (e.g., an "even" control word in a pay TV system) '12' indicates the second key (e.g., an "odd" control word in a pay TV system) '13' indicates the first key followed by the second key (e.g., a pair of control words in a pay TV system) '2X' Secret key for enciphering keys, not information ('X' is a reference with any value from '0' to 'F') (e.g., in a pay TV system, either an operational key for enciphering control words, or a management key for enciphering operational keys) '3X' Private key of an asymmetric key pair ('X' is a reference with any value from '0' to 'F') '4X' Password ('X' is a reference with any value from '0' to 'F') '80' to '8E' Proprietary """ padding_indicator = stringtoint(value[0]) plain = self.decipher(tag, 0x80, value[1:]) plain = vsCrypto.strip_padding(self.ct.blocklength, plain, padding_indicator) return_data.append(plain) #SM data objects for authentication if tag == SM_Class["CHECKSUM"]: auth = vsCrypto.append_padding(self.cct.blocklength, to_authenticate) checksum = self.compute_cryptographic_checksum( 0x8E, 0x80, auth) if checksum != value: raise SwError(SW["ERR_SECMESSOBJECTSINCORRECT"]) elif tag == SM_Class["DIGITAL_SIGNATURE"]: auth = to_authenticate #FIXME: Need padding? signature = self.compute_digital_signature(0x9E, 0x9A, auth) if signature != value: raise SwError(SW["ERR_SECMESSOBJECTSINCORRECT"]) elif tag in (SM_Class["HASH_CODE"], SM_Class["HASH_CODE_ODD"]): hash = self.hash(p1, p2, to_authenticate) if hash != value: raise SwError(SW["ERR_SECMESSOBJECTSINCORRECT"]) #Form unprotected CAPDU if cla == None: cla = CAPDU.cla if ins == None: ins = CAPDU.ins if p1 == None: p1 = CAPDU.p1 if p2 == None: p2 = CAPDU.p2 # FIXME #if expected != "": #raise SwError(SW["ERR_SECMESSOBJECTSMISSING"]) if isinstance(le, str): # FIXME C_APDU only handles le with strings of length 1. Better patch utils.py to support extended length apdus le_int = stringtoint(le) if le_int == 0 and len(le) > 1: le_int = MAX_EXTENDED_LE le = le_int c = C_APDU(cla=cla, ins=ins, p1=p1, p2=p2, le=le, data="".join(return_data)) return c
def selectFile(self, p1, p2, data): """ Function for instruction 0xa4. Takes the parameter bytes 'p1', 'p2' as integers and 'data' as binary string. Returns the status bytes as two byte long integer and the response data as binary string. """ P2_FCI = 0 P2_FCP = 1 << 2 P2_FMD = 2 << 2 P2_NONE = 3 << 2 file = self._selectFile(p1, p2, data) if isinstance(file, EF): # File size (body only) size = inttostring(min(0xffff, len(file.getenc('data'))), 2) extra = chr(0) # RFU if (isinstance(file, RecordStructureEF) and file.hasFixedRecordSize() and not file.isCyclic()): # Length of records extra += chr(0) + chr(min(file.records, 0xff)) elif isinstance(file, DF): # Number of unused EEPROM bytes available in the DF size = inttostring(0xffff, 2) efcount = 0 dfcount = 0 chv1 = None chv2 = None chv1lifecycle = 0 chv2lifecycle = 0 for f in self.content: if f.fid == 0x0000: chv1 = f chv1lifecycle = f.lifecycle & 1 if f.fid == 0x0100: chv2 = f chv2lifecycle = f.lifecycle & 1 if isinstance(f, EF): efcount += 1 if isinstance(f, DF): dfcount += 1 if chv1: extra = chr(1) # TODO LSB correct? else: extra = chr(0) # TODO LSB correct? extra += chr(efcount) extra += chr(dfcount) extra += chr(0) # TODO Number of PINs and unblock CHV PINs extra += chr(0) # RFU if chv1: extra += chr(0) # TODO remaining CHV1 attempts extra += chr(0) # TODO remaining unblock CHV1 attempts if chv2: extra += chr(0) # TODO CHV2 key status extra += chr(0) # TODO CHV2 unblocking key status data = inttostring(0, 2) # RFU data += size data += inttostring(file.fid, 2) data += inttostring(file.filedescriptor) # File type data += inttostring(0, 4) # ACs TODO data += chr(file.lifecycle & 1) # File status data += chr(len(extra)) data += extra self.current = file return SW["NORMAL"], data
def parse_SM_CAPDU(self, CAPDU, authenticate_header): """ This methods parses a data field including Secure Messaging objects. SM_header indicates whether or not the header of the message shall be authenticated. It returns an unprotected command APDU :param CAPDU: The protected CAPDU to be parsed :param authenticate_header: Whether or not the header should be included in authentication mechanisms :returns: Unprotected command APDU """ structure = unpack(CAPDU.data) return_data = ["",] cla = None ins = None p1 = None p2 = None le = None if authenticate_header: to_authenticate = inttostring(CAPDU.cla) + inttostring(CAPDU.ins)+\ inttostring(CAPDU.p1) + inttostring(CAPDU.p2) to_authenticate = vsCrypto.append_padding(self.cct.blocklength, to_authenticate) else: to_authenticate = "" for tlv in structure: tag, length, value = tlv if tag % 2 == 1: #Include object in checksum calculation to_authenticate += bertlv_pack([[tag, length, value]]) #SM data objects for encapsulating plain values if tag in (SM_Class["PLAIN_VALUE_NO_TLV"], SM_Class["PLAIN_VALUE_NO_TLV_ODD"]): return_data.append(value) #FIXME: Need TLV coding? #Encapsulated SM objects. Parse them #FIXME: Need to pack value into a dummy CAPDU elif tag in (SM_Class["PLAIN_VALUE_TLV_INCULDING_SM"], SM_Class["PLAIN_VALUE_TLV_INCULDING_SM_ODD"]): return_data.append(self.parse_SM_CAPDU(value, authenticate_header)) #Encapsulated plaintext BER-TLV objects elif tag in (SM_Class["PLAIN_VALUE_TLV_NO_SM"], SM_Class["PLAIN_VALUE_TLV_NO_SM_ODD"]): return_data.append(value) elif tag in (SM_Class["Ne"], SM_Class["Ne_ODD"]): le = value elif tag == SM_Class["PLAIN_COMMAND_HEADER"]: if len(value) != 8: raise SwError(SW["ERR_SECMESSOBJECTSINCORRECT"]) else: cla = value[:2] ins = value[2:4] p1 = value[4:6] p2 = value[6:8] #SM data objects for confidentiality if tag in (SM_Class["CRYPTOGRAM_PLAIN_TLV_INCLUDING_SM"], SM_Class["CRYPTOGRAM_PLAIN_TLV_INCLUDING_SM_ODD"]): #The cryptogram includes SM objects. #We decrypt them and parse the objects. plain = self.decipher(tag, 0x80, value) #TODO: Need Le = length return_data.append(self.parse_SM_CAPDU(plain, authenticate_header)) elif tag in (SM_Class["CRYPTOGRAM_PLAIN_TLV_NO_SM"], SM_Class["CRYPTOGRAM_PLAIN_TLV_NO_SM_ODD"]): #The cryptogram includes BER-TLV encoded plaintext. #We decrypt them and return the objects. plain = self.decipher(tag, 0x80, value) return_data.append(plain) elif tag in (SM_Class["CRYPTOGRAM_PADDING_INDICATOR"], SM_Class["CRYPTOGRAM_PADDING_INDICATOR_ODD"]): #The first byte of the data field indicates the padding to use: """ Value Meaning '00' No further indication '01' Padding as specified in 6.2.3.1 '02' No padding '1X' One to four secret keys for enciphering information, not keys ('X' is a bitmap with any value from '0' to 'F') '11' indicates the first key (e.g., an "even" control word in a pay TV system) '12' indicates the second key (e.g., an "odd" control word in a pay TV system) '13' indicates the first key followed by the second key (e.g., a pair of control words in a pay TV system) '2X' Secret key for enciphering keys, not information ('X' is a reference with any value from '0' to 'F') (e.g., in a pay TV system, either an operational key for enciphering control words, or a management key for enciphering operational keys) '3X' Private key of an asymmetric key pair ('X' is a reference with any value from '0' to 'F') '4X' Password ('X' is a reference with any value from '0' to 'F') '80' to '8E' Proprietary """ padding_indicator = stringtoint(value[0]) plain = self.decipher(tag, 0x80, value[1:]) plain = vsCrypto.strip_padding(self.ct.blocklength, plain, padding_indicator) return_data.append(plain) #SM data objects for authentication if tag == SM_Class["CHECKSUM"]: auth = vsCrypto.append_padding(self.cct.blocklength, to_authenticate) checksum = self.compute_cryptographic_checksum(0x8E, 0x80, auth) if checksum != value: raise SwError(SW["ERR_SECMESSOBJECTSINCORRECT"]) elif tag == SM_Class["DIGITAL_SIGNATURE"]: auth = to_authenticate #FIXME: Need padding? signature = self.compute_digital_signature(0x9E, 0x9A, auth) if signature != value: raise SwError(SW["ERR_SECMESSOBJECTSINCORRECT"]) elif tag in (SM_Class["HASH_CODE"], SM_Class["HASH_CODE_ODD"]): hash = self.hash(p1, p2, to_authenticate) if hash != value: raise SwError(SW["ERR_SECMESSOBJECTSINCORRECT"]) #Form unprotected CAPDU if cla == None: cla = CAPDU.cla if ins == None: ins = CAPDU.ins if p1 == None: p1 = CAPDU.p1 if p2 == None: p2 = CAPDU.p2 if le == None: le = CAPDU.le # FIXME #if expected != "": #raise SwError(SW["ERR_SECMESSOBJECTSMISSING"]) if isinstance(le, str): # FIXME C_APDU only handles le with strings of length 1. Better patch utils.py to support extended length apdus le_int = stringtoint(le) if le_int == 0 and len(le) > 1: le_int = MAX_EXTENDED_LE le = le_int c = C_APDU(cla=cla, ins=ins, p1=p1, p2=p2, le=le, data="".join(return_data)) return c