def unwrapResp(self, resp, sw1, sw2): """ Unwrap response (decipher and check MAC).""" sw = (sw1 << 8) + sw2 if not(sw == 0x9000 or sw1 in (0x62, 0x63)): assert len(resp) == 0, "No response data expected" return [], sw1, sw2 dresp = l2s(resp) if (self.SL | self.rmacSL) & SL_RMAC: assert len(resp) >= 8, "Resp data shorter than 8: %d" % len(resp) data2sign = self.MACchain + dresp[:-8] + chr(sw1) + chr(sw2) rmac = CMAC(self.SRMAC, data2sign)[:8] assert rmac == dresp[-8:], "Wrong R-MAC: %s vs expected: %s" % \ (hexlify(dresp[-8:]).upper(), hexlify(rmac).upper()) dresp = dresp[:-8] if (self.SL | self.rmacSL) & SL_RENC and len(dresp) > 0: assert len(dresp) % 16 == 0, \ "Length of encrypted data not multiple of 16: %d" % len(dresp) k = AES.new(self.SENC, AES.MODE_ECB) ICV = k.encrypt(pack(">QQ", 0x8000000000000000L | self.cmdCount / 0x10000000000000000L, self.cmdCount % 0x10000000000000000L)) k = AES.new(self.SENC, AES.MODE_CBC, IV=ICV) ddata = k.decrypt(dresp) data = unpad80(ddata, 16) assert len(data) > 0, "Empty data encrypted" else: data = dresp return s2l(data), sw1, sw2
def wrapAPDU(self, apdu): """ Wrap APDU for SCP02, i.e. calculate MAC and encrypt. Input APDU and output APDU are list of uint8. """ lc = len(apdu) - 5 assert len(apdu) >= 5, "Wrong APDU length: %d" % len(apdu) assert len(apdu) == 5 or apdu[4] == lc, \ "Lc differs from length of data: %d vs %d" % (apdu[4], lc) cla = apdu[0] b8 = cla & 0x80 if cla & 0x03 > 0 or cla & 0x40 != 0: # nonzero logical channel in APDU, check that are the same assert cla == self.CLA(False, b8), "CLA mismatch" sapdu = l2s(apdu) # CLA without log. channel can be 80 or 00 only if self.isCMAC: if self.i & M_CMAC_MODIF: # CMAC on unmodified APDU mlc = lc clac = chr(b8) else: # CMAC on modified APDU mlc = lc + 8 clac = chr(b8 + 0x04) mac = self.calcMAC_1d(clac + sapdu[1:4] + chr(mlc) + sapdu[5:]) mac = [ord(x) for x in mac] if self.isENC: k = DES3.new(self.ses_ENC, DES.MODE_CBC, ZERO8) data = s2l(k.encrypt(pad80(sapdu[5:], 8))) lc = len(data) else: data = apdu[5:] lc += 8 apdu = [self.CLA(True, b8)] + apdu[1:4] + [lc] + data + mac return apdu
def _doDelete(self, aid, P2, token): """ Build delete APDU (either package or instance) token - instance of Token, calculates token for Delegated management if present""" data = '\x4F' + derLV(aid) if token: data += token.getCRT() data2sign = pack("BBB", 0, P2, len(data)) + data data += '\x9E' + derLV(token.calc(data2sign)) apdu = [0x80, INS_DELETE, 0, P2, len(data)] + s2l(data) return apdu
def test_Cdecrypt(self): host_challenge = unhexlify('0807060504030201') scp = SCP03(**self.scp_par) apdu = scp.initUpdate(host_challenge) apdu = scp.extAuth(SL=3) apdu_i4lh = '80E60200150A45786572636973655236000006EF04C602068200' apdu_i4l = s2l(unhexlify(apdu_i4lh)) wapdu = scp.wrapAPDU(apdu_i4l) encdata = l2s(wapdu[5:-8]) self.assertEqual(encdata, unhexlify('DF31907FC027482D5DCB7DC028245F7C108CA4D2AFF12275079768E1EFE9429E'))
def install_inst(self, token=None): """ Build Install for install APDU. token - instance of Token, calculates token for Delegated management if present""" privileges = getattr(self, 'privileges', '\0') sys_spec_par = ''.join( getattr(self, 'par_sys', [unhexlify("C7020000"), unhexlify("C8020000")])) if 'par_sys_sim' in self.__dict__: sys_spec_par += '\xCA' + derLV(self.par_sys_sim) sys_spec_par = '\xEF' + derLV(sys_spec_par) app_spec_par = 'par_applet' in self.__dict__ \ and self.par_applet or '' app_spec_par = '\xC9' + derLV(app_spec_par) uicc_spec = [] if 'par_UICC_toolkit' in self.__dict__: uicc_spec.append('\x80' + derLV(self.par_UICC_toolkit)) if 'par_UICC_DAP' in self.__dict__: uicc_spec.append('\xC3' + derLV(self.par_UICC_DAP)) if 'par_UICC_access' in self.__dict__: uicc_spec.append('\x81' + derLV(self.par_UICC_access)) if 'par_UICC_admin_access' in self.__dict__: uicc_spec.append('\x82' + derLV(self.par_UICC_admin_access)) if uicc_spec: uicc_sys_par = '\xEA' + derLV(''.join(uicc_spec)) else: uicc_sys_par = '' AID_inst = getattr(self, 'AID_instance', self.AID_module) params = app_spec_par + sys_spec_par + uicc_sys_par if token: params += token.getCRT() data = derLV(self.AID_package) + \ derLV(self.AID_module) + \ derLV(AID_inst) + \ derLV(privileges) + \ derLV(params) if token: data2sign = pack("BBB", P1_INST_INSMSEL, 0, len(data)) + data data += derLV(token.calc(data2sign)) else: data += '\0' assert len(data) < 0x100, "Data longer than 0xFF: 0x%X '%s'" % \ (len(data), hexlify(data).upper()) apdu = [0x80, INS_INSTALL, P1_INST_INSMSEL, 0, len(data)] + s2l(data) return apdu
def send(self, templ, **kw): objects = self.objects.copy() objects.update(kw) sapdu = GAF(templ).eval(**objects) apdu = s2l(sapdu) assert 5 <= len(apdu) and len(apdu) <= 260, "Wrong APDU length %d, '%s'" % (len(apdu), hexlify(sapdu)) resp, sw1, sw2 = CardConnectionDecorator.transmit(self, apdu) if sw1 == 0x6C and len(apdu) == 5: apdu[4] = sw2 resp, sw1, sw2 = CardConnectionDecorator.transmit(self, apdu) elif sw1 == 0x61: apdu = [0, 0xC0, 0, 0, sw2] resp, sw1, sw2 = CardConnectionDecorator.transmit(self, apdu) sw = (sw1 << 8) + sw2 return l2s(resp), sw
def send( self, templ, **kw ): objects = self.objects.copy() objects.update( kw ) sapdu = GAF( templ ).eval( **objects ) apdu = s2l( sapdu ) assert 5 <= len( apdu ) and len( apdu ) <= 260,\ "Wrong APDU length %d, '%s'" % ( len(apdu), hexlify( sapdu )) resp, sw1, sw2 = CardConnectionDecorator.transmit( self, apdu ) if sw1 == 0x6C and len( apdu ) == 5: apdu[4] = sw2 resp, sw1, sw2 = CardConnectionDecorator.transmit( self, apdu ) elif sw1 == 0x61: apdu = [ 0, 0xC0, 0, 0, sw2 ] resp, sw1, sw2 = CardConnectionDecorator.transmit( self, apdu ) sw = ( sw1 << 8 ) + sw2 return l2s( resp ), sw
def unwrapAPDU(self, SL): host_challenge = unhexlify('0807060504030201') scp = SCP03(**self.scp_par) apdu = scp.initUpdate(host_challenge) apdu = scp.extAuth(SL) macchain = scp.MACchain cmdCount = scp.cmdCount apdu_i4lh = '80E60200150A45786572636973655236000006EF04C602068200' apdu_i4l = s2l(unhexlify(apdu_i4lh)) wapdu = scp.wrapAPDU(apdu_i4l) scp.MACchain = macchain scp.cmdCount = cmdCount papdu = scp.unwrapAPDU(wapdu) self.assertEqual(papdu, apdu_i4l)
def mut_auth(self, SL, **kw): """ Perform mutual authentication. Optional paramters in kw: logCh - logical channel (u8, default 0) host_challenge - string 8B long aid - AID to select (string, default self.scp) (SD_AID for calculation is self.scp.SD_AID) """ # select SD logCh = kw.get('logCh', 0) assert 0 <= logCh and logCh < 20 self.scp.logCh = logCh aid = kw.get('aid', self.scp.SD_AID) assert 5 <= len(aid) and len(aid) <= 16 cla = self.scp.CLA(False, b8=0) apdu = [cla, 0xA4, 0x04, 0, len(aid)] + s2l(aid) resp, sw1, sw2 = CardConnectionDecorator.transmit(self, apdu) if sw1 == 0x61: apdu = [cla, 0xC0, 0, 0, sw2] resp, sw1, sw2 = CardConnectionDecorator.transmit(self, apdu) sw = (sw1 << 8) + sw2 if sw != 0x9000: raise ISOerror("NOK: SW = %04X", sw) # Initial update host_challenge = kw.get('host_challenge', '\0'*8) assert len(host_challenge) == 8 apdu = self.scp.initUpdate(host_challenge, logCh) resp, sw1, sw2 = CardConnectionDecorator.transmit(self, apdu) # Case 4 command, expects 61xx assert sw1 == 0x61 resp, sw1, sw2 = self.getResponse(sw2) sw = (sw1 << 8) + sw2 if sw != 0x9000: raise ISOerror("NOK: SW = %04X", sw) # check response to initial update, may raise exception self.scp.parseInitUpdateResp(resp) # External authenticate apdu = self.scp.extAuth(SL) resp, sw1, sw2 = CardConnectionDecorator.transmit(self, apdu) sw = (sw1 << 8) + sw2 if sw != 0x9000: raise ISOerror("NOK: SW = %04X", sw)
def send(self, templ, **kw): """ Evaluate GAF and transmit as APDU templ - a GAF template to evaluate kw - GAF dictionary (updates dictionary from SCP02Connection.__init__) Return (resp, SW) as (str, int)""" objects = self.objects.copy() objects.update(kw) papdu = s2l(GAF(templ).eval(**objects)) apdu = self.scp.wrapAPDU(papdu) resp, sw1, sw2 = CardConnectionDecorator.transmit(self, apdu) if sw1 == 0x6C and len(papdu) == 5: papdu[4] = sw2 apdu = self.scp.wrapAPDU(papdu) resp, sw1, sw2 = CardConnectionDecorator.transmit(self, apdu) elif sw1 == 0x61: resp, sw1, sw2 = self.getResponse(sw2) resp, sw1, sw2 = self.scp.unwrapResp(resp, sw1, sw2) sw = (sw1 << 8) + sw2 return l2s(resp), sw
def send(self, templ, **kw): """ Evaluate GAF and transmit as APDU templ - a GAF template to evaluate kw - GAF dictionary (updates dictionary from GAFConnection.__init__) Return ( resp, SW ) as ( str, int )""" objects = self.objects.copy() objects.update(kw) sapdu = GAF(templ).eval(**objects) apdu = s2l(sapdu) assert 5 <= len( apdu ) and len( apdu ) <= 260,\ "Wrong APDU length %d, '%s'" % ( len(apdu), hexlify( sapdu )) resp, sw1, sw2 = CardConnectionDecorator.transmit(self, apdu) if sw1 == 0x6C and len(apdu) == 5: apdu[4] = sw2 resp, sw1, sw2 = CardConnectionDecorator.transmit(self, apdu) elif sw1 == 0x61: apdu = [0, 0xC0, 0, 0, sw2] resp, sw1, sw2 = CardConnectionDecorator.transmit(self, apdu) sw = (sw1 << 8) + sw2 return l2s(resp), sw
def unwrapAPDU(self, apdu): """ Parse MACed/encrypted APDU, decipher and check MAC. """ lc = self.checkAPDU(apdu) if apdu[1] == 0xC0: # Get Response TPDU return apdu if 'beginRmaSL' in self.__dict__: self.rmacSL = self.beginRmacSL del self.beginRmacSL self.cmdCount += 1 cla = apdu[0] b8 = cla & 0x80 assert cla & 0x04, "Secure messaging missing" if (cla & 0x40 == 0 and cla & 0x03 > 0) or cla & 0x40 != 0: # check logical channels assert cla == self.CLA(True, b8), "CLA mismatch" scla = b8 | 0x04 # CLA without log. ch. but with secure messaging data = l2s(apdu[5:]) if self.SL & SL_CMAC: # C-MAC assert lc >= 8, "Missing/ too short CMAC" sdata = data[:-8] data2sign = self.MACchain + chr(scla) + l2s(apdu[1:4])\ + chr(lc) + sdata self.MACchain = CMAC(self.SMAC, data2sign) assert data[-8:] == self.MACchain[:8], "Wrong CMAC" data = sdata lc -= 8 if self.SL & SL_CENC and lc > 0: # C-ENC assert lc % 16 == 0, "Encoded data length not multiple of BS" k = AES.new(self.SENC, AES.MODE_ECB) ICV = k.encrypt(pack(">QQ", self.cmdCount / 0x10000000000000000L, self.cmdCount % 0x10000000000000000L)) k = AES.new(self.SENC, AES.MODE_CBC, IV=ICV) pdata = k.decrypt(data) data = unpad80(pdata, 16) assert len(data) > 0, "Empty data encrypted" lc = len(data) apdu = [self.CLA(False, b8)] + apdu[1:4] + [lc] + s2l(data) return apdu
def wrapResp(self, resp, sw1, sw2): """ Wrap expected response as card would do.""" sw = (sw1 << 8) + sw2 if not(sw == 0x9000 or sw1 in (0x62, 0x63)): assert len(resp) == 0, "No response data expected" return [], sw1, sw2 dresp = l2s(resp) if (self.SL | self.rmacSL) & SL_RENC and len(dresp) > 0: assert len(dresp) <= 0xEF, "Data too long for RENC+RMAC" k = AES.new(self.SENC, AES.MODE_ECB) ICV = k.encrypt(pack(">QQ", 0x8000000000000000L | self.cmdCount / 0x10000000000000000L, self.cmdCount % 0x10000000000000000L)) k = AES.new(self.SENC, AES.MODE_CBC, IV=ICV) dresp = k.encrypt(pad80(dresp, 16)) if (self.SL | self.rmacSL) & SL_RMAC: assert len(dresp) <= 0xF0, "Data too long for RMAC" data2sign = self.MACchain + dresp + chr(sw1) + chr(sw2) rmac = CMAC(self.SRMAC, data2sign)[:8] dresp += rmac return s2l(dresp), sw1, sw2
def install_extradict(self, aid_sd, token=None): """ Build install for extradiction APDU aid_sd - AID of SD to extradite to/from token - instance of Token, calculates token for Delegated management if present """ assert 5 <= len(aid_sd) and len(aid_sd) <= 16 AID_inst = getattr(self, 'AID_instance', self.AID_module) params = token and token.getCRT() or '' data = derLV(aid_sd) + '\0' + derLV(AID_inst) + '\0' + derLV(params) if token: data2sign = pack("BBB", P1_INST_EXTRA, 0, len(data)) + data data += derLV(token.calc(data2sign)) else: data += '\0' assert len(data) < 0x100, "Data longer than 0xFF: 0x%X '%s'" % \ (len(data), hexlify(data).upper()) apdu = [0x80, INS_INSTALL, P1_INST_EXTRA, 0, len(data)] + s2l(data) return apdu
def wrapAPDU(self, apdu): """ Wrap APDU for SCP03, i.e. calculate MAC and encrypt. Input APDU and output APDU are list of u8. """ lc = self.checkAPDU(apdu) if apdu[1] == 0xC0: # Get Response TPDU return apdu if 'beginRmaSL' in self.__dict__: self.rmacSL = self.beginRmacSL del self.beginRmacSL self.cmdCount += 1 cla = apdu[0] b8 = cla & 0x80 if (cla & 0x40 == 0 and cla & 0x03 > 0) or cla & 0x40 != 0: # check logical channels assert cla == self.CLA(False, b8), "CLA mismatch" scla = b8 | 0x04 # CLA without log. ch. but with secure messaging cdata = l2s(apdu[5:]) if self.SL & SL_CENC and lc > 0: # C-ENC k = AES.new(self.SENC, AES.MODE_ECB) ICV = k.encrypt( pack(">QQ", self.cmdCount / 0x10000000000000000L, self.cmdCount % 0x10000000000000000L)) k = AES.new(self.SENC, AES.MODE_CBC, IV=ICV) data2enc = pad80(cdata, 16) cdata = k.encrypt(data2enc) lc = len(cdata) if self.SL & SL_CMAC: # C-MAC lc += 8 data2sign = self.MACchain + chr(scla) + l2s(apdu[1:4]) if (lc > 0xff): data2sign += b'\x00' + chr((lc >> 8) & 0xff) + chr(lc & 0xff) else: data2sign += chr(lc) data2sign += cdata self.MACchain = CMAC(self.SMAC, data2sign) cdata += self.MACchain[:8] apdu = [self.CLA(True, b8)] + apdu[1:4] + [lc] + s2l(cdata) return apdu
def load(self, datalen=239, token=None, DAP=[], cipher=None, AID_SD=''): """ Build list of APDUs for InstallForLoad and Load. datalen - data length in Load APDUs token - instance of Token, calculates token for Delegated management if present DAP - list of (aid, signature_function). For each item, E2 TLV is inserted (each signature_function called on LFDB hash). Example of signature_function: DESsign.calc or RSAsign.calc cipher - cipher function for Ciphere Load File Data Block if present, D4#(cipher(<ijc content>)) sent instead of C4 AID_SD - AID of SD to load package to """ f = open(self.file_ijc, "rb") ijc_data = f.read() ijc_len = len(ijc_data) h = hashlib.new('sha1') h.update(ijc_data) ijc_hash = h.digest() print "Loading '%s', len = %d, SHA1 = %s" % \ (self.file_ijc, ijc_len, hexlify(ijc_hash).upper()) # build Install for load APDU sysparams = ''.join( getattr(self, 'par_sysLoad', [ unhexlify("C6020000"), unhexlify("C7020000"), unhexlify("C8020000") ])) params = '\xEF' + derLV(sysparams) if token: params += token.getCRT() # put ijc hash only if token or DAP is present ins_hash = (token is not None or len(DAP) > 0) and ijc_hash or '' data = derLV(self.AID_package) + \ derLV(AID_SD) + \ derLV(ins_hash) + \ derLV(params) if token: data2sign = pack("BBB", P1_INST_LOAD, 0, len(data)) + data data += derLV(token.calc(data2sign)) else: data += '\0' assert len(data) < 0x100, "Data longer than 0xFF: 0x%X '%s'" % \ (len(data), hexlify(data).upper()) apdus = [[0x80, INS_INSTALL, 2, 0, len(data)] + s2l(data)] # build Load APDUs GP CS 2.2.1 11.6.2.3 load_data = '' E2template = GAF("E2 #(4F #($aid) C3 #($sig))") for d in DAP: sig = d[1](ijc_hash) # calculate signature load_data += E2template.eval(aid=d[0], sig=sig) if cipher: load_data += '\xD4' + derLV(cipher(ijc_data)) else: load_data += '\xC4' + derLV(ijc_data) napdu = (len(load_data) + datalen - 1) / datalen P1 = 0 for i in xrange(napdu): if i == napdu - 1: # the last block P1 = 0x80 datalen = len(load_data) apdus.append([0x80, INS_LOAD, P1, i, datalen] + s2l(load_data[:datalen])) load_data = load_data[datalen:] return apdus
def install_perso(self): """ Build install for perso APDU """ AID_inst = getattr(self, 'AID_instance', self.AID_module) data = '\0\0' + derLV(AID_inst) + '\0\0\0' apdu = [0x80, INS_INSTALL, P1_INST_PERSO, 0, len(data)] + s2l(data) return apdu