def __init__(self, respdataPM, respdataApp): ind = 0 self.packages = [] while len(respdataPM) > ind: length = respdataPM[ind] pack_aid = l2s(respdataPM[ind + 1:ind + 1 + length]) ind += length + 1 lcs = respdataPM[ind] priv = respdataPM[ind + 1] nmod = respdataPM[ind + 2] ind += 3 mods = [] for i in xrange(nmod): length = respdataPM[ind] mods.append(l2s(respdataPM[ind + 1:ind + 1 + length])) ind += length + 1 self.packages.append({ 'pack_aid': pack_aid, 'lcs': lcs, 'priv': priv, 'modules': mods }) ind = 0 self.insts = [] while len(respdataApp) > ind: length = respdataApp[ind] app_aid = l2s(respdataApp[ind + 1:ind + 1 + length]) ind += length + 1 lcs = respdataApp[ind] priv = respdataApp[ind + 1] ind += 2 self.insts.append({'app_aid': app_aid, 'lcs': lcs, 'priv': priv})
def test_Mutauth( self ): host_challenge = unhexlify( '0807060504030201' ) scp = SCP03( **self.scp_par ) apdu = scp.initUpdate( host_challenge ) self.assertEqual( l2s( apdu ), unhexlify( '80503000080807060504030201' )) # invoke key derivation resp = scp.initUpdateResp() self.assertEqual( scp.card_challenge, unhexlify( 'A3F5F144D19BE66E' )) self.assertEqual( scp.SENC, unhexlify( '852D207B7CC8C880231EDFD5C644CFB1' )) self.assertEqual( scp.SMAC, unhexlify( '7131B9369F3D19850E6919CD3321523E' )) self.assertEqual( scp.SRMAC, unhexlify( 'B570AA1FDE18F9179B5CBD42D8939D05' )) self.assertEqual( scp.card_cryptogram, unhexlify( '72BFCBDF4A14515F' )) self.assertEqual( scp.host_cryptogram, unhexlify( 'AEB8DAD1865B85E2' )) self.assertEqual( resp, unhexlify( '000050C7606A8CF64800300370' + 'A3F5F144D19BE66E72BFCBDF4A14515F00002A' )) # external authenticate apdu = scp.extAuth( SL = 1 ) self.assertEqual( l2s( apdu ), unhexlify( '8482010010AEB8DAD1865B85E2' ) + unhexlify( '49FC4CF184E61DCD' )) self.assertEqual( scp.MACchain, unhexlify( '49FC4CF184E61DCD4C3928E4C617FBA3' ))
def __init__( self, respdataPM, respdataApp ): ind = 0 self.packages = [] while len( respdataPM ) > ind: length = respdataPM[ind] pack_aid = l2s( respdataPM[ ind+1: ind+1+length]) ind += length + 1 lcs = respdataPM[ ind ] priv = respdataPM[ ind+1 ] nmod = respdataPM[ ind+2 ] ind += 3 mods = [] for i in xrange( nmod ): length = respdataPM[ind] mods.append( l2s( respdataPM[ ind+1: ind+1+length])) ind += length + 1 self.packages.append( { 'pack_aid': pack_aid, 'lcs': lcs, 'priv': priv, 'modules': mods } ) ind = 0 self.insts = [] while len( respdataApp ) > ind: length = respdataApp[ind] app_aid = l2s( respdataApp[ ind+1: ind+1+length]) ind += length + 1 lcs = respdataApp[ ind ] priv = respdataApp[ ind+1 ] ind += 2 self.insts.append( { 'app_aid': app_aid, 'lcs': lcs, 'priv': priv } )
def test_Mutauth(self): host_challenge = unhexlify('0807060504030201') scp = SCP03(**self.scp_par) apdu = scp.initUpdate(host_challenge) self.assertEqual(l2s(apdu), unhexlify('80503000080807060504030201')) # invoke key derivation resp = scp.initUpdateResp() self.assertEqual(scp.card_challenge, unhexlify('A3F5F144D19BE66E')) self.assertEqual(scp.SENC, unhexlify('852D207B7CC8C880231EDFD5C644CFB1')) self.assertEqual(scp.SMAC, unhexlify('7131B9369F3D19850E6919CD3321523E')) self.assertEqual(scp.SRMAC, unhexlify('B570AA1FDE18F9179B5CBD42D8939D05')) self.assertEqual(scp.card_cryptogram, unhexlify('72BFCBDF4A14515F')) self.assertEqual(scp.host_cryptogram, unhexlify('AEB8DAD1865B85E2')) self.assertEqual(resp, unhexlify('000050C7606A8CF64800300370' + 'A3F5F144D19BE66E72BFCBDF4A14515F00002A')) # external authenticate apdu = scp.extAuth(SL=1) self.assertEqual(l2s(apdu), unhexlify('8482010010AEB8DAD1865B85E2') + unhexlify('49FC4CF184E61DCD')) self.assertEqual(scp.MACchain, unhexlify('49FC4CF184E61DCD4C3928E4C617FBA3'))
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" scla = b8 | 0x04 # CLA without log. ch. but with secure messaging 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 = [ ord(x) for x in k.encrypt( pad80( sapdu[5:] ))] lc = len( data ) else: data = apdu[5:] lc += 8 apdu = [ self.CLA( True, b8 )] + apdu[1:4] + [ lc ] + data + mac return apdu
def unwrapResp( self, resp, sw1, sw2 ): """ Unwrap response (decipher and check MAC).""" 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 ) m = re_pad80.match( ddata ) assert m is not None, "Wrong padding" data = m.groups()[0] assert len( data ) > 0, "Empty data encrypted" else: data = dresp return [ord(x) for x in data ], sw1, sw2
def parseInitUpdateResp( self, resp ): """ Parse response to Init Update and if correct set diverData, seqCounter, and card_challenge from it. resp - response (list[u8]) Raise exception in case of wrong response. """ assert len( resp ) in ( 29, 32 ), \ "Wrong length of response data to Init Update: %d" % len(resp) diverData, keyInfo, card_chal, card_cryptogram, seqCounter =\ partition( l2s( resp ), ( 10, 13, 21, 29 )) kv, i = ord( keyInfo[0]), ord( keyInfo[2] ) assert 0x30 <= kv and kv <= 0x3F, \ "Wrong key version in resp. to Init Update %02X" % kv assert keyInfo[1] == chr( 0x03 ), \ "Wrong SCP number in resp. to Init Update %02X" % ord(keyInfo[0]) assert i & ~( M_PSEUDO | M_RMACENC ) == 0 \ and i != M_RMACENC ^ M_RMAC, "Wrong SCP03 parameter %02X" % i self.i, self.keyVer = i, kv if self.i & M_PSEUDO: assert len( seqCounter ) == 3, "Missing seq. counter" self.seqCounter = seqCounter else: assert len( seqCounter ) == 0, "Seq. counter shall not be present" self.deriveKeys( card_chal ) assert card_cryptogram == self.card_cryptogram, \ "Recieved and calculated card cryptogram difer: %s vs. %s" % \ ( hexlify( card_cryptogram ), hexlify( self.card_cryptogram ))
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 parseProactiveCmd(self, resp): """ Parse Proactive command from fetched byte array. """ try: res = split2TLV(l2s(resp)) assert len(res) == 1 assert res[0][0] == T_PROCMD except AssertionError: raise ProactiveException("Wrong ProCmd tag / length") try: tlvs = split2TLV(res[0][1]) except AssertionError: raise ProactiveException("Wrong TLVs in proactive command") # parse Command details and Devices TLVs tlv = tlvs.pop(0) if 0x7F & tlv[0] != T_CMD_DETAILS or len(tlv[1]) != 3 or tlv[1][0] != "\x01": raise ProactiveException("ProCmd wrong Command details") cmd, qualif = unpack("BB", tlv[1][1:]) # parse Command details and Devices TLVs tlv = tlvs.pop(0) if 0x7F & tlv[0] != T_DEVICES or len(tlv[1]) != 2 or tlv[1][0] != chr(DEV_UICC): raise ProactiveException("ProCmd wrong Devices") dev = ord(tlv[1][1]) proCmd = ProactiveCommand(cmd, qualif, dev) # parse rest of TLVs for tlv in tlvs: proCmd.TLVs.append(TLV(*tlv)) return proCmd
def parseInitUpdateResp(self, resp): """ Parse response to Init Update and if correct set diverData, seqCounter, and card_challenge from it. resp - response (list[u8]) Raise exception in case of wrong response. """ assert len(resp) in (29, 32), \ "Wrong length of response data to Init Update: %d" % len(resp) diverData, keyInfo, card_chal, card_cryptogram, seqCounter =\ partition(l2s(resp), (10, 13, 21, 29)) kv, i = ord(keyInfo[0]), ord(keyInfo[2]) assert 0x30 <= kv and kv <= 0x3F, \ "Wrong key version in resp. to Init Update %02X" % kv assert keyInfo[1] == chr(0x03), \ "Wrong SCP number in resp. to Init Update %02X" % ord(keyInfo[0]) assert i & ~(M_PSEUDO | M_RMACENC) == 0 \ and i != M_RMACENC ^ M_RMAC, "Wrong SCP03 parameter %02X" % i self.i, self.keyVer, self.diverData = i, kv, diverData if self.i & M_PSEUDO: assert len(seqCounter) == 3, "Missing seq. counter" self.seqCounter = s2int(seqCounter) else: assert len(seqCounter) == 0, "Seq. counter shall not be present" self.deriveKeys(card_chal) assert card_cryptogram == self.card_cryptogram, \ "Recieved and calculated card cryptogram difer: %s vs. %s" % \ (hexlify(card_cryptogram), hexlify(self.card_cryptogram))
def parseProactiveCmd(self, resp): """ Parse Proactive command from fetched byte array. """ try: res = split2TLV(l2s(resp)) assert len(res) == 1 assert res[0][0] == T_PROCMD except AssertionError: raise ProactiveException("Wrong ProCmd tag / length") try: tlvs = split2TLV(res[0][1]) except AssertionError: raise ProactiveException("Wrong TLVs in proactive command") # parse Command details and Devices TLVs tlv = tlvs.pop(0) if 0x7F & tlv[0] != T_CMD_DETAILS or len( tlv[1] ) != 3 \ or tlv[1][0] != '\x01': raise ProactiveException("ProCmd wrong Command details") cmd, qualif = unpack("BB", tlv[1][1:]) # parse Command details and Devices TLVs tlv = tlvs.pop(0) if 0x7F & tlv[0] != T_DEVICES or len( tlv[1] ) != 2 \ or tlv[1][0] != chr( DEV_UICC ): raise ProactiveException("ProCmd wrong Devices") dev = ord(tlv[1][1]) proCmd = ProactiveCommand(cmd, qualif, dev) # parse rest of TLVs for tlv in tlvs: proCmd.TLVs.append(TLV(*tlv)) return proCmd
def readBinary(c, le, logCh=0, offset=0): """Read Binary on currently selected EF""" P1 = (offset >> 8) & 0x7F P2 = offset & 0xFF apdu = [logCh, INS_READBIN, P1, P2, le] resp, sw1, sw2 = c.transmit(apdu) sw = (sw1 << 8) + sw2 if sw != 0x9000: raise ISOException(sw) return l2s(resp)
def readBinary( c, le, logCh = 0, offset = 0 ): """Read Binary on currently selected EF""" P1 = ( offset >> 8 ) & 0x7F P2 = offset & 0xFF apdu = [ logCh, INS_READBIN, P1, P2, le ] resp, sw1, sw2 = c.transmit( apdu ) sw = ( sw1 << 8 ) + sw2 if sw != 0x9000: raise ISOException( sw ) return l2s( resp )
def readRecord( c, recNum, logCh = 0 ): """ Read record from currently selected EF""" apdu = [ logCh, INS_READREC, recNum, 4, 0 ] resp, sw1, sw2 = c.transmit( apdu ) if sw1 == 0x6C: apdu[4] = sw2 resp, sw1, sw2 = c.transmit( apdu ) sw = ( sw1 << 8 ) + sw2 if sw != 0x9000: raise ISOException( sw ) return l2s( resp )
def getData(c, tag): P1 = tag >> 8 P2 = tag & 0xFF apdu = [0x80, INS_GETDATA, P1, P2, 0] resp, sw1, sw2 = c.transmit(apdu) if sw1 == 0x6C: apdu[4] = sw2 resp, sw1, sw2 = c.transmit(apdu) sw = (sw1 << 8) + sw2 if sw != 0x9000: raise ISOException(sw) return l2s(resp)
def getData( c, tag ): P1 = tag >> 8 P2 = tag & 0xFF apdu = [ 0x80, INS_GETDATA, P1, P2, 0 ] resp, sw1, sw2 = c.transmit( apdu ) if sw1 == 0x6C: apdu[4] = sw2 resp, sw1, sw2 = c.transmit( apdu ) sw = ( sw1 << 8 ) + sw2 if sw != 0x9000: raise ISOException( sw ) return l2s( resp )
def readRecord(c, recNum, logCh=0): """ Read record from currently selected EF""" apdu = [logCh, INS_READREC, recNum, 4, 0] resp, sw1, sw2 = c.transmit(apdu) if sw1 == 0x6C: apdu[4] = sw2 resp, sw1, sw2 = c.transmit(apdu) sw = (sw1 << 8) + sw2 if sw != 0x9000: raise ISOException(sw) return l2s(resp)
def parseInitUpdate(self, apdu): """ Parse Init Update APDU (list[u8]) and if correct, set log. channel and host challenge from it. """ cla = apdu[0] assert 0x80 <= cla <= 0x83 or 0xC0 <= cla < 0xCF, "Wrong CLA" assert apdu[1] == INS_INIT_UPDATE, "Wrong INS" assert apdu[2] == self.keyVer, "Key version changed" # ignore P2? assert apdu[4] == len(apdu) - 5 == 8, "Wrong Lc/data length" self.logCh = logCh(cla) self.host_challenge = l2s(apdu[5:])
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 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 selectFile( c, path, logCh = 0 ): """ Select file by path from MF or MF for empty path """ if len( path ) > 0: apdu = [ logCh, INS_SELECT, 8, 4, len( path )] + [ ord(x) for x in path ] else: apdu = [ logCh, INS_SELECT, 0, 4, 2, 0x3F, 0x00 ] resp, sw1, sw2 = c.transmit( apdu ) if sw1 == 0x61: resp, sw1, sw2 = c.transmit([0, 0xC0, 0, 0, sw2]) sw = ( sw1 << 8 ) + sw2 if sw != 0x9000: raise ISOException( sw ) return l2s( resp )
def incCounter( self, value = 1 ): """ Increment counter by given values (as int, default = 1). """ vals = [ ord( x ) for x in self.counter ] vals[4] += value for i in xrange( 4, 0, -1 ): if 0 <= vals[i] and vals[i] < 0x100: break vals[i-1] += vals[i] / 0x100 vals[i] %= 0x100 self.counter = l2s( vals ) if 'counter_file' in self.__dict__: f = open( self.counter_file, 'w' ) f.write( hexlify( self.counter )) f.close()
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 selectFile(c, path, logCh=0): """ Select file by path from MF or MF for empty path """ if len(path) > 0: apdu = [logCh, INS_SELECT, 8, 4, len(path)] + [ord(x) for x in path] else: apdu = [logCh, INS_SELECT, 0, 4, 2, 0x3F, 0x00] resp, sw1, sw2 = c.transmit(apdu) if sw1 == 0x61: resp, sw1, sw2 = c.transmit([0, 0xC0, 0, 0, sw2]) sw = (sw1 << 8) + sw2 if sw != 0x9000: raise ISOException(sw) return l2s(resp)
def incCounter(self, value=1): """ Increment counter by given values (as int, default = 1). """ vals = [ord(x) for x in self.counter] vals[4] += value for i in xrange(4, 0, -1): if 0 <= vals[i] < 0x100: break vals[i - 1] += vals[i] / 0x100 vals[i] %= 0x100 self.counter = l2s(vals) if 'counter_file' in self.__dict__: f = open(self.counter_file, 'w') f.write(hexlify(self.counter)) f.close()
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 getExtCardRes( c ): """ Issue GET DATA with tag FF21 in order to receive Extended Card Resources (GP 2.2.1, 11.3 & ETSI TS 102.226, 8.2.1.7). Returns [ num. of install applets, free NVM, free RAM ]""" # CLA = 0x00: return only value # CLA = 0x80: return TLV, i.e. 0xFF21 #( value ) apdu = [ 0x80, INS_GETDATA, 0xFF, 0x21, 0 ] resp, sw1, sw2 = c.transmit( apdu ) if sw1 == 0x6C: apdu[4] = sw2 resp, sw1, sw2 = c.transmit( apdu ) sw = ( sw1 << 8 ) + sw2 if sw != 0x9000: raise ISOException( sw ) payload = l2s( resp ) result = [ s2int( findTLValue( payload, ( 0xFF21, tag ))) for tag in ( 0x81, 0x82, 0x83 )] return result
def selectApplet( c, AID, logCh = 0 ): """ Select applet on a given logical channel or open new log. channel if logCh is None. """ if logCh is None: logCh = openLogCh( c ) # select the Applet on the given logical channel apdu = [ logCh, INS_SELECT, 4, 0, len( AID ) ] + [ ord( x ) for x in AID ] resp, sw1, sw2 = c.transmit( apdu ) if sw1 == 0x6C and len( AID ) == 0: apdu = [ logCh, INS_SELECT, 4, 0, sw2 ] resp, sw1, sw2 = c.transmit( apdu ) if( sw1 == 0x61 ): apdu = [ logCh, 0xC0, 0, 0, sw2 ] resp, sw1, sw2 = c.transmit( apdu ) sw = ( sw1 << 8 ) + sw2 if sw != 0x9000 : raise ISOException( sw ) respdata = l2s( resp ) # close channel return ( respdata, logCh )
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 selectApplet(c, AID, logCh=0): """ Select applet on a given logical channel or open new log. channel if logCh is None. """ if logCh is None: logCh = openLogCh(c) # select the Applet on the given logical channel apdu = [logCh, INS_SELECT, 4, 0, len(AID)] + [ord(x) for x in AID] resp, sw1, sw2 = c.transmit(apdu) if sw1 == 0x6C and len(AID) == 0: apdu = [logCh, INS_SELECT, 4, 0, sw2] resp, sw1, sw2 = c.transmit(apdu) if (sw1 == 0x61): apdu = [logCh, 0xC0, 0, 0, sw2] resp, sw1, sw2 = c.transmit(apdu) sw = (sw1 << 8) + sw2 if sw != 0x9000: raise ISOException(sw) respdata = l2s(resp) # close channel return (respdata, logCh)
def getExtCardRes(c): """ Issue GET DATA with tag FF21 in order to receive Extended Card Resources (GP 2.2.1, 11.3 & ETSI TS 102.226, 8.2.1.7). Returns [ num. of install applets, free NVM, free RAM ]""" # CLA = 0x00: return only value # CLA = 0x80: return TLV, i.e. 0xFF21 #( value ) apdu = [0x80, INS_GETDATA, 0xFF, 0x21, 0] resp, sw1, sw2 = c.transmit(apdu) if sw1 == 0x6C: apdu[4] = sw2 resp, sw1, sw2 = c.transmit(apdu) sw = (sw1 << 8) + sw2 if sw != 0x9000: raise ISOException(sw) payload = l2s(resp) result = [ s2int(findTLValue(payload, (0xFF21, tag))) for tag in (0x81, 0x82, 0x83) ] return result
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 parseInitUpdateResp(self, resp): """ Parse response to Init Update and if correct set diverData, seqCounter, and card_challenge from it. resp - response (list[u8]) Return received seqCounter if different from the expected one, otherwise None Raise exception in case of wrong response. """ assert len(resp) == 28, \ "Wrong response length to Init Update: %d" % len(resp) diverData, keyVer, const02, seqCounter, card_chal, card_cryptogram =\ partition(l2s(resp), (10, 11, 12, 14, 20)) assert keyVer == chr(self.keyVer), \ "Different key version in Init Update response" assert const02 == '\x02', "Wrong protocol number" self.seqCounter = seqCounter self.deriveKeys(card_chal) if self.i & M_PSEUDO: assert card_chal == self.card_challenge,\ "Different card challenge" assert card_cryptogram == self.card_cryptogram, \ "Different card cryptogram"
def parseInitUpdateResp( self, resp ): """ Parse response to Init Update and if correct set diverData, seqCounter, and card_challenge from it. resp - response (list[u8]) Return received seqCounter if different from the expected one, otherwise None Raise exception in case of wrong response. """ assert len( resp ) == 28, \ "Wrong response length to Init Update: %d" % len( resp ) diverData, keyVer, const02, seqCounter, card_chal, card_cryptogram =\ partition( l2s( resp ), ( 10, 11, 12, 14, 20 )) assert keyVer == chr( self.keyVer ), \ "Different key version in Init Update response" assert const02 == '\x02', "Wrong protocol number" self.seqCounter = seqCounter self.deriveKeys( card_chal ) if self.i & M_PSEUDO: assert card_chal == self.card_challenge,\ "Different card challenge" assert card_cryptogram == self.card_cryptogram, \ "Different card cryptogram"
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 cardInfo(c): """Deselect, read EF_DIR, EF_ICCID""" resetCard(c) histBytes = l2s(ATR(c.getATR()).getHistoricalBytes()) infoMF = selectFile(c, '') # read EF_ICCID infoICCID = selectFile(c, unhexlify('2FE2')) fileSize = s2int(findTLValue(infoICCID, (0x62, 0x80))) assert fileSize == 10, "Wrong size of EF_ICCID" iccid = swapNibbles(readBinary(c, fileSize)) # read EF_DIR infoDIR = selectFile(c, unhexlify('2F00')) # see ETSI 102.221 11.1.1.4.3 for coding fileDesc = findTLValue(infoDIR, (0x62, 0x82)) assert len( fileDesc ) == 5 and \ fileDesc[:2] == '\x42\x21' # linear EF recLen, nRec = unpack(">HB", fileDesc[2:5]) dirDO = [] for recNum in xrange(1, nRec + 1): try: r = readRecord(c, recNum) if r == '\xFF' * len(r): continue aid = findTLValue(r, (0x61, 0x4F)) label = findTLValue(r, (0x61, 0x50)) dirDO.append({'AID': aid, 'label': label}) except ISOException: break # select USIM and try to read IMSI if len(dirDO) == 1: aid_usim = dirDO[0]['AID'] else: aids = [DO['AID'] for DO in dirDO if re.match(DO['label'], 'USIM')] if len(aids) == 1: aid_usim = aids[0] else: aid_usim = None if aid_usim: infoUSIM = selectApplet(c, aid_usim) infoIMSI = selectFile(c, unhexlify('7FFF6F07')) try: bimsi = readBinary(c, 9) digits = reduce( lambda d, n: d + [ord(n) & 0x0F, ord(n) >> 4], bimsi[1:1 + ord(bimsi[0])], []) digits.pop(0) # remove first nibble 8 or 9 while digits[-1] == 0x0F: digits.pop() # remove trailing F imsi = ''.join([chr(ord('0') + i) for i in digits]) except ISOException: imsi = None else: imsi = None # select default applet and get tags 45 and 42 selectApplet(c, '') try: cin = findTLValue(getData(c, 0x42), (0x42, )) except ISOException: cin = None try: iin = findTLValue(getData(c, 0x45), (0x45, )) except ISOException: iin = None return histBytes, iccid, dirDO, imsi, iin, cin
def cardInfo( c ): """Deselect, read EF_DIR, EF_ICCID""" resetCard( c ) histBytes = l2s( ATR( c.getATR()).getHistoricalBytes()) infoMF = selectFile( c, '' ) # read EF_ICCID infoICCID = selectFile( c, unhexlify( '2FE2' )) fileSize = s2int( findTLValue( infoICCID, ( 0x62, 0x80 ))) assert fileSize == 10, "Wrong size of EF_ICCID" iccid = swapNibbles( readBinary( c, fileSize )) # read EF_DIR infoDIR = selectFile( c, unhexlify( '2F00' )) # see ETSI 102.221 11.1.1.4.3 for coding fileDesc = findTLValue( infoDIR, ( 0x62, 0x82 )) assert len( fileDesc ) == 5 and \ fileDesc[:2] == '\x42\x21' # linear EF recLen, nRec = unpack( ">HB", fileDesc[2:5] ) dirDO = [] for recNum in xrange( 1, nRec+1 ): try: r = readRecord( c, recNum ) if r == '\xFF'* len( r ): continue aid = findTLValue( r, ( 0x61, 0x4F )) label = findTLValue( r, ( 0x61, 0x50 )) dirDO.append( { 'AID': aid, 'label': label } ) except ISOException: break # select USIM and try to read IMSI if len( dirDO ) == 1: aid_usim = dirDO[0]['AID'] else: aids = [ DO['AID'] for DO in dirDO if re.match( DO['label'], 'USIM' )] if len( aids ) == 1: aid_usim = aids[0] else: aid_usim = None if aid_usim: infoUSIM = selectApplet( c, aid_usim ) infoIMSI = selectFile( c, unhexlify( '7FFF6F07' )) try: bimsi = readBinary( c, 9 ) digits = reduce( lambda d, n: d + [ ord(n) & 0x0F, ord(n) >> 4 ], bimsi[ 1:1+ord(bimsi[0])], [] ) digits.pop(0) # remove first nibble 8 or 9 while digits[-1] == 0x0F: digits.pop() # remove trailing F imsi = ''.join( [ chr(ord('0')+i) for i in digits ]) except ISOException: imsi = None else: imsi = None # select default applet and get tags 45 and 42 selectApplet( c, '' ) try: cin = findTLValue( getData( c, 0x42 ), ( 0x42, )) except ISOException: cin = None try: iin = findTLValue( getData( c, 0x45 ), ( 0x45, )) except ISOException: iin = None return histBytes, iccid, dirDO, imsi, iin, cin