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 = str(PublicKey.__getstate__()['n']) e = str(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 = str(PublicKey.__getstate__()['p']) q = str(PublicKey.__getstate__()['q']) g = str(PublicKey.__getstate__()['g']) #Public key y = str(PublicKey.__getstate__()['y']) pk = ((0x81, len(p), p), (0x82, len(q), q), (0x83, len(g), g), (0x84, len(y), y)) #Private key x = str(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 parse_SE_config(self, config): r = 0x9000 try: ControlReferenceTemplate.parse_SE_config(self, config) except SwError as e: structure = unpack(config) for tlv in structure: tag, length, value = tlv if tag == 0x7f4c: self.chat = CHAT(bertlv_pack([[tag, length, value]])) print(self.chat) elif tag == 0x67: self.auxiliary_data = bertlv_pack([[tag, length, value]]) elif tag == 0x80 or tag == 0x84 or tag == 0x83 or tag == 0x91: # handled by ControlReferenceTemplate.parse_SE_config pass else: raise SwError(SW["ERR_REFNOTUSABLE"]) structure = unpack(config) pin_ref_str = '%c'% self.PACE_PIN for tlv in structure: if [0x83, len(pin_ref_str), pin_ref_str] == tlv: if self.sam.counter <= 0: r = 0x63c0 elif self.sam.counter == 1: r = 0x63c1 elif self.sam.counter == 2: r = 0x63c2 return r, ""
def parse_SE_config(self, config): r = 0x9000 try: ControlReferenceTemplate.parse_SE_config(self, config) except SwError as e: structure = unpack(config) for tlv in structure: tag, length, value = tlv if tag == 0x7f4c: self.chat = CHAT(bertlv_pack([[tag, length, value]])) print(self.chat) elif tag == 0x67: self.auxiliary_data = bertlv_pack([[tag, length, value]]) elif tag == 0x80 or tag == 0x84 or tag == 0x83 or tag == 0x91: # handled by ControlReferenceTemplate.parse_SE_config pass else: raise SwError(SW["ERR_REFNOTUSABLE"]) return r, ""
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 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 verify_certificate(self, p1, p2, data): if (p1, p2) != (0x00, 0xbe): raise SwError(SW["ERR_INCORRECTPARAMETERS"]) cert = bertlv_pack([[0x7f21, len(data), data]]) if 1 != eac.TA_STEP2_import_certificate(self.eac_ctx, cert): eac.print_ossl_err() raise SwError(SW["ERR_NOINFO69"]) print "Imported Certificate" return ""
def verify_certificate(self, p1, p2, data): if (p1, p2) != (0x00, 0xbe): raise SwError(SW["ERR_INCORRECTPARAMETERS"]) cert = bertlv_pack([[0x7f21, len(data), data]]) if 1 != eac.TA_STEP2_import_certificate(self.eac_ctx, cert): eac.print_ossl_err() raise SwError(SW["ERR_NOINFO69"]) print("Imported Certificate") return b""
def parse_SE_config(self, config): r = 0x9000 try: ControlReferenceTemplate.parse_SE_config(self, config) except SwError as e: structure = unpack(config) for tlv in structure: tag, length, value = tlv if tag == 0x7f4c: self.chat = CHAT(bertlv_pack([[tag, length, value]])) elif tag == 0x67: self.auxiliary_data = bertlv_pack([[tag, length, value]]) # extract reference values for verifying # DateOfBirth, DateOfExpiry and CommunityID for ddo in decodeDiscretionaryDataObjects(value): try: oidvalue = ddo[0][2] reference = ddo[1][2] mapped_algo = ALGO_MAPPING[oidvalue] if mapped_algo == "DateOfBirth": self.DateOfBirth = int(reference) logging.info("Found reference DateOfBirth: " + str(self.DateOfBirth)) elif mapped_algo == "DateOfExpiry": self.DateOfExpiry = int(reference) logging.info("Found reference DateOfExpiry: " + str(self.DateOfExpiry)) elif mapped_algo == "CommunityID": self.CommunityID = binascii.hexlify(reference) logging.info("Found reference CommunityID: " + str(self.CommunityID)) except: pass elif tag == 0x80 or tag == 0x84 or tag == 0x83 or tag == 0x91: # handled by ControlReferenceTemplate.parse_SE_config pass else: raise SwError(SW["ERR_REFNOTUSABLE"]) return r, b""
def parse_SE_config(self, config): r = 0x9000 try: ControlReferenceTemplate.parse_SE_config(self, config) except SwError as e: structure = unpack(config) for tlv in structure: tag, length, value = tlv if tag == 0x7f4c: self.chat = CHAT(bertlv_pack([[tag, length, value]])) elif tag == 0x67: self.auxiliary_data = bertlv_pack([[tag, length, value]]) # extract reference values for verifying # DateOfBirth, DateOfExpiry and CommunityID for ddo in decodeDiscretionaryDataObjects(value): try: oidvalue = ddo[0][2] reference = ddo[1][2] mapped_algo = ALGO_MAPPING[oidvalue] if mapped_algo == "DateOfBirth": self.DateOfBirth = int(reference) logging.info("Found reference DateOfBirth: " + str(self.DateOfBirth)) elif mapped_algo == "DateOfExpiry": self.DateOfExpiry = int(reference) logging.info("Found reference DateOfExpiry: " + str(self.DateOfExpiry)) elif mapped_algo == "CommunityID": self.CommunityID = binascii.hexlify(reference) logging.info("Found reference CommunityID: " + str(self.CommunityID)) except: pass elif tag == 0x80 or tag == 0x84 or tag == 0x83 or tag == 0x91: # handled by ControlReferenceTemplate.parse_SE_config pass else: raise SwError(SW["ERR_REFNOTUSABLE"]) return r, ""
def __pack_general_authenticate(data): tlv_data = bertlv_pack(data) return bertlv_pack([[0x7c, len(tlv_data), tlv_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 # 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 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