def _check_rand(self, rand): """Check provided rand and calculate temp. Use stored values if None""" if self.rand is None or rand is not None: self.rand = s2int(randomBytes(16)) if rand is None else \ self._check_int(rand, "RAND", 128) temp = self.Ek.encrypt(int2s(self.rand ^ self.OPc, 128)) self.temp = s2int(temp)
def calculate(self, ki, rand, op, sqn, amf, opc, mac_a, mac_s, res, ck, ik, ak_a, ak_s): m = MilenageAlgo(ki, op=op) self.assertEqual(m.OPc, s2int(opc), "Wrong OPc") mac = m.f1(rand, sqn, amf) self.assertEqual(mac[0], mac_a, "Wrong MAC_A") self.assertEqual(mac[1], mac_s, "Wrong MAC_A") self.assertEqual(m.f2(), res, "Wrong RES") self.assertEqual(m.f3(), ck, "Wrong CK") self.assertEqual(m.f4(), ik, "Wrong IK") self.assertEqual(m.f5(), s2int(ak_a), "Wrong AK auth") self.assertEqual(m.f5s(), s2int(ak_s), "Wrong AK resync")
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 _check_int(self, param, label, bitlen): """Convert param to int and check its value""" if isinstance(param, str): param = s2int(param) assert isinstance(param, int) or isinstance(param, long), \ "Wrong type of " + label assert 0 <= param < (1 << bitlen), "Wrong value of " + label return param
def f5s(self, rand=None): """Calculate anonimity keys for resync. rand - str or int, if None, use the stored value return AK as 48b integer""" self._check_rand(rand) arg = rot(self.temp ^ self.OPc, self.R[5]) ^ self.C[5] out5 = s2int(self.Ek.encrypt(int2s(arg, 128))) ^ self.OPc return out5 >> 80
def f5(self, rand=None): """Calculate anonimity keys for authentication. rand - str or int, if None, use the stored value return AK as 48b integer""" self._check_rand(rand) arg = rot(self.temp ^ self.OPc, self.R[2]) ^ self.C[2] out2 = s2int(self.Ek.encrypt(int2s(arg, 128))) ^ self.OPc return out2 >> 80
def f4(self, rand=None): """Calculate integrity key. rand - str or int, if None, use the stored value return IK as 16B str""" self._check_rand(rand) arg = rot(self.temp ^ self.OPc, self.R[4]) ^ self.C[4] out4 = s2int(self.Ek.encrypt(int2s(arg, 128))) ^ self.OPc return int2s(out4, 128)
def f3(self, rand=None): """Calculate confidentiality key. rand - str or int, if None, use the stored value return CK as 16B str""" self._check_rand(rand) arg = rot(self.temp ^ self.OPc, self.R[3]) ^ self.C[3] out3 = s2int(self.Ek.encrypt(int2s(arg, 128))) ^ self.OPc return int2s(out3, 128)
def __init__(self, ki, opc=None, op=None, c=(0, 1, 2, 4, 8), r=(64, 0, 32, 64, 96)): """Initialization with key, OP(C) and Milenage parameters ki, opc, op - strings of len 16B or int/long c, r - 5-tuple of ints""" ki = self._check_str(ki, "KI") # encryption function self.Ek = AES.new(ki, AES.MODE_ECB) assert opc is not None or op is not None, \ "Either OP or OPC must be specified" if opc is not None: self.OPc = self._check_int(opc, "OPC", 128) if op is not None: op = self._check_str(op, "OP") op_int = s2int(op) topc = s2int(self.Ek.encrypt(op)) ^ op_int if opc is None: self.OPc = topc else: assert topc == opc, "OPC %s does not match to KI, OPC: %s" # sanity check of c & r assert isinstance(c, tuple) or isinstance(c, list), "Wrong type of C" assert isinstance(r, tuple) or isinstance(r, list), "Wrong type of R" assert len(c) == 5, "Wrong number of parameters C" assert len(r) == 5, "Wrong number of parameters R" assert all([isinstance(ci, int) or isinstance(ci, long) for ci in c]), "Wrong type of Ci" assert all([isinstance(ri, int) or isinstance(ri, long) for ri in r]), "Wrong type of Ri" assert all([0 <= ci <= MASK128 for ci in c]), "Wrong value of Ci" assert all([0 <= ri < 128 for ri in r]), "Wrong value of Ri" self.C = tuple([None] + list(c)) # parameters are numbered from 1 to 5 self.R = tuple([None] + list(r)) self.rand = None self.temp = None self.sqn = (1 << 5) | 1 self.amf = 0x0000
def f2(self, rand=None): """Calculate authentication response. rand - str or int, if None, use the stored value return RES as 8B str""" self._check_rand(rand) arg = rot(self.temp ^ self.OPc, self.R[2]) ^ self.C[2] out2 = s2int(self.Ek.encrypt(int2s(arg, 128))) ^ self.OPc out2s = int2s(out2, 128) return out2s[8:]
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 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 f1(self, rand=None, sqn=None, amf=None): """Calculate network & resync authentication code. rand, sqn, amf - str or int, if None, use the stored value return (MAC-A, MAC-S) as two 8B str""" self._check_rand(rand) if sqn is None: assert self.sqn is not None, "SQN not stored" else: self.sqn = self._check_int(sqn, "SQN", 48) if amf is None: assert self.amf is not None, "AMF not stored" else: self.amf = self._check_int(amf, "AMF", 16) in1 = (self.sqn << 16) | self.amf in1 |= in1 << 64 arg = self.temp ^ rot(in1 ^ self.OPc, self.R[1]) ^ self.C[1] out1 = s2int(self.Ek.encrypt(int2s(arg, 128))) ^ self.OPc out1s = int2s(out1, 128) return out1s[:8], out1s[8:]
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