def __eac_pace_step1(self, data): tlv_data = nPA_SE.__unpack_general_authenticate(data) if tlv_data != []: raise SwError(SW["WARN_NOINFO63"]) if self.at.keyref_is_mrz(): self.PACE_SEC = PACE_SEC(self.sam.mrz, eac.PACE_MRZ) elif self.at.keyref_is_can(): self.PACE_SEC = PACE_SEC(self.sam.can, eac.PACE_CAN) elif self.at.keyref_is_pin(): if self.sam.counter <= 0: print "Must use PUK to unblock" return 0x63c0, "" if self.sam.counter == 1 and not self.sam.active: print "Must use CAN to activate" return 0x63c1, "" self.PACE_SEC = PACE_SEC(self.sam.PIN, eac.PACE_PIN) self.sam.counter -= 1 if self.sam.counter <= 1: self.sam.active = False elif self.at.keyref_is_puk(): if self.sam.counter_puk <= 0: raise SwError(SW["WARN_NOINFO63"]) self.PACE_SEC = PACE_SEC(self.sam.puk, eac.PACE_PUK) self.sam.counter_puk -= 1 else: raise SwError(SW["ERR_INCORRECTPARAMETERS"]) self.sec = self.PACE_SEC.sec if not self.eac_ctx: eac.EAC_init() self.EAC_CTX = EAC_CTX() self.eac_ctx = self.EAC_CTX.ctx eac.CA_disable_passive_authentication(self.eac_ctx) ef_card_security = self.mf.select('fid', 0x011d) ef_card_security_data = ef_card_security.data eac.EAC_CTX_init_ef_cardsecurity(ef_card_security_data, self.eac_ctx) if self.ca_key: ca_pubkey = eac.CA_get_pubkey(self.eac_ctx, ef_card_security_data) if 1 != eac.CA_set_key(self.eac_ctx, self.ca_key, ca_pubkey): eac.print_ossl_err() raise SwError(SW["WARN_NOINFO63"]) else: # we don't have a good CA key, so we simply generate an ephemeral one comp_pubkey = eac.TA_STEP3_generate_ephemeral_key(self.eac_ctx) pubkey = eac.CA_STEP2_get_eph_pubkey(self.eac_ctx) if not comp_pubkey or not pubkey: eac.print_ossl_err() raise SwError(SW["WARN_NOINFO63"]) # save public key in EF.CardSecurity (and invalidate the signature) # FIXME this only works for the default EF.CardSecurity. # Better use an ASN.1 parser to do this manipulation ef_card_security = self.mf.select('fid', 0x011d) ef_card_security_data = ef_card_security.data ef_card_security_data = ef_card_security_data[:61 + 4 + 239 + 2 + 1] + pubkey + ef_card_security_data[ 61 + 4 + 239 + 2 + 1 + len(pubkey):] ef_card_security.data = ef_card_security_data nonce = eac.PACE_STEP1_enc_nonce(self.eac_ctx, self.sec) if not nonce: eac.print_ossl_err() raise SwError(SW["WARN_NOINFO63"]) resp = nPA_SE.__pack_general_authenticate([[0x80, len(nonce), nonce]]) self.eac_step += 1 return 0x9000, resp
def getResponse(self, p1, p2, data): if not (p1 == 0 and p2 == 0): raise SwError(SW["ERR_INCORRECTP1P2"]) return self.lastCommandSW, self.lastCommandOffcut
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 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 is None: cla = CAPDU.cla if ins is None: ins = CAPDU.ins if p1 is None: p1 = CAPDU.p1 if p2 is None: p2 = CAPDU.p2 # FIXME: # if expected != "": # raise SwError(SW["ERR_SECMESSOBJECTSMISSING"]) if isinstance(le, bytes): # 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 verify(self, p1, p2, data): if p1 == 0x80 and p2 == 0x00: if self.current_SE.eac_step == 6: # data should only contain exactly OID [(tag, _, value)] = structure = unpack(data) if tag == 6: mapped_algo = ALGO_MAPPING[value] eid = self.mf.select( 'dfname', '\xe8\x07\x04\x00\x7f\x00' '\x07\x03\x02') if mapped_algo == "DateOfExpiry": [(_, _, [(_, _, mine)])] = \ unpack(eid.select('fid', 0x0103).data) logging.info("DateOfExpiry: " + str(mine) + "; reference: " + str(self.current_SE.at.DateOfExpiry)) if self.current_SE.at.DateOfExpiry < mine: print("Date of expiry verified") return SW["NORMAL"], "" else: print("Date of expiry not verified (expired)") return SW["WARN_NOINFO63"], "" elif mapped_algo == "DateOfBirth": [(_, _, [(_, _, mine)])] = \ unpack(eid.select('fid', 0x0108).data) # case1: YYYYMMDD -> good # case2: YYYYMM -> mapped to last day of given month, # i.e. YYYYMM31 ;-) # case3: YYYY -> mapped to YYYY-12-31 if len(str(mine)) == 6: mine = int(str(mine) + "31") elif len(str(mine)) == 4: mine = int(str(mine) + "1231") logging.info("DateOfBirth: " + str(mine) + "; reference: " + str(self.current_SE.at.DateOfExpiry)) if self.current_SE.at.DateOfBirth < mine: print("Date of birth verified (old enough)") return SW["NORMAL"], "" else: print("Date of birth not verified (too young)") return SW["WARN_NOINFO63"], "" elif mapped_algo == "CommunityID": [(_, _, [(_, _, mine)])] = \ unpack(eid.select('fid', 0x0112).data) mine = binascii.hexlify(mine) logging.info("CommunityID: " + str(mine) + "; reference: " + str(self.current_SE.at.CommunityID)) if mine.startswith(self.current_SE.at.CommunityID): print("Community ID verified (living there)") return SW["NORMAL"], "" else: print( "Community ID not verified (not living" "there)") return SW["WARN_NOINFO63"], "" else: return SwError(SW["ERR_DATANOTFOUND"]) else: return SwError(SW["ERR_DATANOTFOUND"]) else: return SAM.verify(self, p1, p2, data)
def notImplemented(*argz, **args): raise SwError(SW["ERR_INSNOTSUPPORTED"])