def mutual_authenticate(self, p1, p2, mutual_challenge): """ Takes an encrypted challenge in the form 'Terminal Challenge | Card Challenge | Card number' and checks it for validity. If the challenge is successful the card encrypts 'Card Challenge | Terminal challenge' and returns this value """ key = self._get_referenced_key(p1, p2) card_number = self.get_card_number() if (key == None): raise SwError(SW["ERR_INCORRECTP1P2"]) if p1 == 0x00: #No information given cipher = get_referenced_cipher(self.cipher) else: cipher = get_referenced_cipher(p1) if (cipher == None): raise SwError(SW["ERR_INCORRECTP1P2"]) plain = vsCrypto.decrypt(cipher, key, mutual_challenge) last_challenge_len = len(self.last_challenge) terminal_challenge = plain[:last_challenge_len - 1] card_challenge = plain[last_challenge_len:-len(card_number) - 1] serial_number = plain[-len(card_number):] if terminal_challenge != self.last_challenge: raise SwError(SW["WARN_NOINFO63"]) elif serial_number != card_number: raise SwError(SW["WARN_NOINFO63"]) result = card_challenge + terminal_challenge return SW["NORMAL"], vsCrypto.encrypt(cipher, key, result)
def mutual_authenticate(self, p1, p2, mutual_challenge): """ Takes an encrypted challenge in the form 'Terminal Challenge | Card Challenge | Card number' and checks it for validity. If the challenge is successful the card encrypts 'Card Challenge | Terminal challenge' and returns this value """ key = self._get_referenced_key(p1, p2) card_number = self.get_card_number() if (key == None): raise SwError(SW["ERR_INCORRECTP1P2"]) if p1 == 0x00: #No information given cipher = get_referenced_cipher(self.cipher) else: cipher = get_referenced_cipher(p1) if (cipher == None): raise SwError(SW["ERR_INCORRECTP1P2"]) plain = vsCrypto.decrypt(cipher, key, mutual_challenge) last_challenge_len = len(self.last_challenge) terminal_challenge = plain[:last_challenge_len-1] card_challenge = plain[last_challenge_len:-len(card_number)-1] serial_number = plain[-len(card_number):] if terminal_challenge != self.last_challenge: raise SwError(SW["WARN_NOINFO63"]) elif serial_number != card_number: raise SwError(SW["WARN_NOINFO63"]) result = card_challenge + terminal_challenge return SW["NORMAL"], vsCrypto.encrypt(cipher, key, result)
def encipher(self, p1, p2, data): """ Encipher data using key, algorithm, IV and Padding specified by the current Security environment. :returns: raw data (no TLV coding). """ algo = self.ct.algorithm key = self.ct.key if key == None or algo == None: raise SwError(SW["ERR_CONDITIONNOTSATISFIED"]) else: padded = vsCrypto.append_padding(vsCrypto.get_cipher_blocklen(algo), data) crypted = vsCrypto.encrypt(algo, key, padded, self.ct.iv) return crypted
def verify_cryptographic_checksum(self, p1, p2, data): """ Verify the cryptographic checksum contained in the data field. Data field must contain a cryptographic checksum (tag 0x8E) and a plain value (tag 0x80) """ plain = "" cct = "" algo = self.cct.algorithm key = self.cct.key iv = self.cct.iv if algo == None or key == None: raise SwError(SW["ERR_CONDITIONNOTSATISFIED"]) structure = unpack(data) for tag, length, value in structure: if tag == 0x80: plain = value elif tag == 0x8E: cct = value if plain == "" or cct == "": raise SwError(SW["ERR_SECMESSOBJECTSMISSING"]) else: my_cct = vsCrypto.crypto_checksum(algo, key, plain, iv) if my_cct == cct: return "" else: raise SwError["ERR_SECMESSOBJECTSINCORRECT"]
def __init__(self, PIN, cardNumber, mf=None, cardSecret=None, default_se=Security_Environment): self.PIN = PIN self.mf = mf self.cardNumber = cardNumber self.last_challenge = None #Will contain non-readable binary string self.counter = 3 #Number of tries for PIN validation self.cipher = 0x01 self.asym_key = None keylen = vsCrypto.get_cipher_keylen(get_referenced_cipher(self.cipher)) if cardSecret is None: #Generate a random card secret self.cardSecret = urandom(keylen) else: if len(cardSecret) != keylen: raise ValueError("cardSecret has the wrong key length for: " +\ get_referenced_cipher(self.cipher)) else: self.cardSecret = cardSecret #Security Environments may be saved to/retrieved from this dictionary self.saved_SEs = {} self.default_se = default_se self.current_SE = default_se(self.mf, self)
def encipher(self, p1, p2, data): padded = vsCrypto.append_padding(self.cct.blocklength, data) cipher = eac.EAC_encrypt(self.eac_ctx, padded) if not cipher: eac.print_ossl_err() raise SwError(SW["ERR_NOINFO69"]) return cipher
def external_authenticate(self, p1, p2, resp_data): """Performs the basic access control protocol as defined in the ICAO MRTD standard""" rnd_icc = self.last_challenge # Receive Mutual Authenticate APDU from terminal # Decrypt data and check MAC Eifd = resp_data[:-8] padded_Eifd = vsCrypto.append_padding(self.current_SE.cct.blocklength, Eifd) Mifd = vsCrypto.crypto_checksum("CC", self.KMac, padded_Eifd) # Check the MAC if not Mifd == resp_data[-8:]: raise SwError(SW["ERR_SECMESSOBJECTSINCORRECT"]) # Decrypt the data plain = vsCrypto.decrypt("DES3-CBC", self.KEnc, resp_data[:-8]) if plain[8:16] != rnd_icc: raise SwError(SW["WARN_NOINFO63"]) # Extract keying material from IFD, generate ICC keying material Kifd = plain[16:] rnd_ifd = plain[:8] Kicc = urandom(16) # Generate Answer data = plain[8:16] + plain[:8] + Kicc Eicc = vsCrypto.encrypt("DES3-CBC", self.KEnc, data) padded_Eicc = vsCrypto.append_padding(self.current_SE.cct.blocklength, Eicc) Micc = vsCrypto.crypto_checksum("CC", self.KMac, padded_Eicc) # Derive the final keys and set the current SE KSseed = vsCrypto.operation_on_string(Kicc, Kifd, lambda a, b: a ^ b) self.current_SE.ct.key = self.derive_key(KSseed, 1) self.current_SE.cct.key = self.derive_key(KSseed, 2) self.current_SE.ssc = stringtoint(rnd_icc[-4:] + rnd_ifd[-4:]) return SW["NORMAL"], Eicc + Micc
def compute_cryptographic_checksum(self, p1, p2, data): """ Compute a cryptographic checksum (e.g. MAC) for the given data. Algorithm and key are specified in the current SE """ if p1 != 0x8E or p2 != 0x80: raise SwError(SW["ERR_INCORRECTP1P2"]) if self.cct.key is None: raise SwError(SW["ERR_CONDITIONNOTSATISFIED"]) checksum = vsCrypto.crypto_checksum(self.cct.algorithm, self.cct.key, data, self.cct.iv) return checksum
def external_authenticate(self, p1, p2, data): """ Authenticate the terminal to the card. Check whether Terminal correctly encrypted the given challenge or not """ if self.last_challenge is None: raise SwError(SW["ERR_CONDITIONNOTSATISFIED"]) key = self._get_referenced_key(p1, p2) if p1 == 0x00: #No information given cipher = get_referenced_cipher(self.cipher) else: cipher = get_referenced_cipher(p1) reference = vsCrypto.append_padding(cipher, self.last_challenge) reference = vsCrypto.encrypt(cipher, key, reference) if(reference == data): #Invalidate last challenge self.last_challenge = None return SW["NORMAL"], "" else: raise SwError(SW["WARN_NOINFO63"])
def compute_cryptographic_checksum(self, p1, p2, data): """ Compute a cryptographic checksum (e.g. MAC) for the given data. The ePass uses a Send Sequence Counter for MAC calculation """ if p1 != 0x8E or p2 != 0x80: raise SwError(SW["ERR_INCORRECTP1P2"]) self.ssc += 1 checksum = vsCrypto.crypto_checksum(self.cct.algorithm, self.cct.key, data, self.cct.iv, self.ssc) return checksum
def compute_cryptographic_checksum(self, p1, p2, data): """ Compute a cryptographic checksum (e.g. MAC) for the given data. Algorithm and key are specified in the current SE """ if p1 != 0x8E or p2 != 0x80: raise SwError(SW["ERR_INCORRECTP1P2"]) if self.cct.key == None: raise SwError(SW["ERR_CONDITIONNOTSATISFIED"]) checksum = vsCrypto.crypto_checksum(self.cct.algorithm, self.cct.key, data, self.cct.iv) return checksum
def decipher(self, p1, p2, data): """ Decipher data using key, algorithm, IV and Padding specified by the current Security environment. :returns: raw data (no TLV coding). Padding is not removed!!! """ algo = self.ct.algorithm key = self.ct.key if key == None or algo == None: raise SwError(SW["ERR_CONDITIONNOTSATISFIED"]) else: plain = vsCrypto.decrypt(algo, key, data, self.ct.iv) return plain
def hash(self, p1, p2, data): """ Hash the given data using the algorithm specified by the current Security environment. :return: raw data (no TLV coding). """ if p1 != 0x90 or not p2 in (0x80, 0xA0): raise SwError(SW["ERR_INCORRECTP1P2"]) algo = self.ht.algorithm if algo == None: raise SwError(SW["ERR_CONDITIONNOTSATISFIED"]) try: hash = vsCrypto.hash(algo, data) except ValueError: raise SwError(SW["ERR_EXECUTION"]) return hash
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 sw == SW["NORMAL"]: # sw = inttostring(sw) # length = len(sw) # tag = SM_Class["PLAIN_PROCESSING_STATUS"] # tlv_sw = pack([(tag,length,sw)]) # return_data += tlv_sw if result != "": # Encrypt the data included in the RAPDU encrypted = self.encipher(0x82, 0x80, result) encrypted = "\x01" + encrypted encrypted_tlv = pack([( SM_Class["CRYPTOGRAM_PADDING_INDICATOR_ODD"], len(encrypted), encrypted)]) return_data += encrypted_tlv if sw == SW["NORMAL"]: 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 += 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 += 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 sw == SW["NORMAL"]: # sw = inttostring(sw) # length = len(sw) # tag = SM_Class["PLAIN_PROCESSING_STATUS"] # tlv_sw = pack([(tag,length,sw)]) # return_data += tlv_sw if result != "": # Encrypt the data included in the RAPDU encrypted = self.encipher(0x82, 0x80, result) encrypted = "\x01" + encrypted encrypted_tlv = pack([ (SM_Class["CRYPTOGRAM_PADDING_INDICATOR_ODD"], len(encrypted), encrypted) ]) return_data += encrypted_tlv if sw == SW["NORMAL"]: 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 += 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 += pack([(tag, length, auth)]) return sw, return_data
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 _get_referenced_key(self, p1, p2): """ This method returns the key specified by the p2 parameter. The key may be stored on the cards filesystem. :param p1: Specifies the algorithm to use. Needed to know the keylength. :param p2: Specifies a reference to the key to be used for encryption == == == == == == == == ============================================= b8 b7 b6 b5 b4 b3 b2 b1 Meaning == == == == == == == == ============================================= 0 0 0 0 0 0 0 0 No information is given 0 - - - - - - - Global reference data(e.g. MF specific key) 1 - - - - - - - Specific reference data(e.g. DF specific key) - - - x x x x x Number of the secret == == == == == == == == ============================================= Any other value RFU """ key = None qualifier = p2 & 0x1F algo = get_referenced_cipher(p1) keylength = vsCrypto.get_cipher_keylen(algo) if (p2 == 0x00): #No information given, use the global card key key = self.cardSecret #We treat global and specific reference data alike #elif ((p2 >> 7) == 0x01 or (p2 >> 7) == 0x00): else: #Interpret qualifier as an short fid (try to read the key from FS) if self.mf == None: raise SwError(SW["ERR_REFNOTUSABLE"]) df = self.mf.currentDF() fid = df.select("fid", stringtoint(qualifier)) key = fid.readbinary(keylength) if key != None: return key else: raise SwError(SW["ERR_REFNOTUSABLE"])
if __name__ == "__main__": password = "******" MyCard = SAM("1234", "1234567890") try: print(MyCard.verify(0x00, 0x00, "5678")) except SwError as e: print(e.message) print("Counter = " + str(MyCard.counter)) print(MyCard.verify(0x00, 0x00, "1234")) print("Counter = " + str(MyCard.counter)) sw, challenge = MyCard.get_challenge(0x00, 0x00, "") print("Before encryption: " + challenge) padded = vsCrypto.append_padding("DES3-ECB", challenge) sw, result_data = MyCard.internal_authenticate(0x00, 0x00, padded) print("Internal Authenticate status code: %x" % sw) try: sw, res = MyCard.external_authenticate(0x00, 0x00, result_data) except SwError as e: print(e.message) sw = e.sw print("Decryption Status code: %x" % sw) #SE = Security_Environment(None) #testvektor = "foobar" #print "Testvektor = %s" % testvektor #sw, hash = SE.hash(0x90,0x80,testvektor) #print "SW after hashing = %s" % sw
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