Example #1
0
 def __init__(self, serial_number='000'):
     # prepare data to write into the card
     if not len(serial_number) == 3 or not serial_number.isdigit():
         print('must provided a 3 digits distinct serial number')
         raise ()
     self.ICCID = ICCID_pre + serial_number
     self.ICCID += str(compute_luhn(self.ICCID))
     self.IMSI = IMSI_pre + serial_number
     self.K = Ki_pre + serial_number
     self.Milenage = Milenage(OP)
     self.OPc = make_OPc(self.K, OP)
     # verify parameters
     if map(len, [self.K, self.OPc]) != [16, 16]:
         print('[-] bad length for K or OPc')
         raise ()
     # write on the card
     if self.program_card() != 0:
         return
     if self.test_identification() != 0:
         return
     self.auth_test = 0
     if self.test_authentication() != 0:
         return
     # finally add some files for infra (SMSP, HPLMN)
     u = UICC()
     program_files(u)
     u.disconnect()
     # and print results
     print(
         '[+] sysmoUSIM card personalization done and tested successfully:')
     print('%s;%s;0x%s;0x%s' % (self.ICCID, self.IMSI, \
                            hexlify(self.K), hexlify(self.OPc)))
Example #2
0
File: Hss.py Project: wnlUc3m/5G_CN
    def generate_authentication_info_answer_milenage(self, imsi, RAND=None):

        serving_network_id = PLMN(val=self.plmn).to_bytes()
        if len(serving_network_id) != 3:
            print "Invalid SN_ID %s" % hexlify(serving_network_id).decode(
                'ascii')

        # Get provided subscriber key (k), authentication management field (amf) and operator variant configuration field (op) if imsi is found
        K, AMF, OP, SQN, result = self.check_key_amf_op(imsi)

        # Pack SQN from integer to buffer
        SQNb = b'\0\0' + pack('>I', int(SQN))

        # Generate challenge
        if RAND is None or len(RAND) != 16:
            RAND = urandom(16)
        self.imsi_to_uecontext[imsi].set_rand(RAND)

        # Compute milenage functions
        milenage = Milenage(OP)
        XRES, CK, IK, AK = milenage.f2345(K, RAND, OP)
        MAC_A = milenage.f1(K, RAND, SQNb, AMF)
        SQN_X_AK = xor_buf(SQNb, AK)
        AUTN = SQN_X_AK + AMF + MAC_A
        K_ASME = conv_A2(CK, IK, serving_network_id, SQN_X_AK)
        # Store generated information
        self.imsi_to_uecontext[imsi].set_ik(IK)
        self.imsi_to_uecontext[imsi].set_ck(CK)
        self.imsi_to_uecontext[imsi].set_ak(AK)
        self.imsi_to_uecontext[imsi].set_xres(XRES)
        self.imsi_to_uecontext[imsi].set_autn(AUTN)

        print "\t [INFO] Generated Authentication vector for %s" % (imsi)
        return RAND, XRES, AUTN, K_ASME
Example #3
0
 def synchronize(self, IMSI, RAND=16*'\0', AMF='\0\0', AUTS=14*'\0'):
     '''
     synchronize the local counter SQN with AUTS provided by the USIM
     in response to a given 3G authentication challenge (RAND, AMF)
     '''
     # lookup AuC_db for authentication key and SQN from IMSI
     if IMSI not in self.db.keys():
         self._log('[ERR] IMSI is not present in AuC.db')
         return -1
     K, SQN = self.db[IMSI][0], self.db[IMSI][1]
     
     # 33.102, section 6.3.3, for resynch, AMF is always null (0x0000)
     AMF = '\0\0'
     
     # compute Milenage functions and unmask SQN
     Mil = Milenage( self.OP )
     AK = Mil.f5star( K, RAND )
     SQN_MS = xor_string( AUTS[0:6], AK )
     #self._log('USIM synchronization, unmasked SQN_MS: %s' % hexlify(SQN_MS))
     MAC_S = Mil.f1star( K, RAND, SQN_MS, AMF )
     #self._log('USIM synchronization, computed MAC_S: %s' % hexlify(MAC_S))
     
     # authenticate the USIM
     if MAC_S != AUTS[6:14]:
         self._log('USIM authentication failure during synchronization ' \
                   'for IMSI %s' % IMSI)
         return -1
     
     # re-synchronize local SQN value
     sqn = unpack('!I', SQN_MS[2:])[0] + 1
     self.db[IMSI][1] = sqn
     self._log('SQN resynchronized with value %i for IMSI %s' \
               % (sqn, IMSI))
     return 0
Example #4
0
    def synchronize(self, IMSI, RAND=16 * '\0', AMF='\0\0', AUTS=14 * '\0'):
        '''
        synchronize the local counter SQN with AUTS provided by the USIM
        in response to a given 3G authentication challenge (RAND, AMF)
        '''
        # lookup AuC_db for authentication key and SQN from IMSI
        if IMSI not in self.db.keys():
            self._log('[ERR] IMSI is not present in AuC.db')
            return -1
        K, SQN = self.db[IMSI][0], self.db[IMSI][1]

        # 33.102, section 6.3.3, for resynch, AMF is always null (0x0000)
        AMF = '\0\0'

        # compute Milenage functions and unmask SQN
        Mil = Milenage(self.OP)
        AK = Mil.f5star(K, RAND)
        SQN_MS = xor_string(AUTS[0:6], AK)
        #self._log('USIM synchronization, unmasked SQN_MS: %s' % hexlify(SQN_MS))
        MAC_S = Mil.f1star(K, RAND, SQN_MS, AMF)
        #self._log('USIM synchronization, computed MAC_S: %s' % hexlify(MAC_S))

        # authenticate the USIM
        if MAC_S != AUTS[6:14]:
            self._log('USIM authentication failure during synchronization ' \
                      'for IMSI %s' % IMSI)
            return -1

        # re-synchronize local SQN value
        sqn = unpack('!I', SQN_MS[2:])[0] + 1
        self.db[IMSI][1] = sqn
        self._log('SQN resynchronized with value %i for IMSI %s' \
                  % (sqn, IMSI))
        return 0
Example #5
0
    def make_4g_vector(self, IMSI, SN_ID, AMF='\x80\x00'):
        '''
        produces a 4G authentication vector "quadruplet" for a given IMSI and
        network (MCC / MNC)
        
        requests self.db for the authentication key corresponding to the IMSI
        and returns RAND, XRES, AUTN, Kasme obtained from the Milenage 
        and key derivation function functions
        '''
        # lookup AuC_db for authentication key and SQN from IMSI
        if IMSI not in self.db.keys():
            self._log('[ERR] IMSI is not present in AuC.db')
            return -1
        if len(SN_ID) != 3:
            self._log('[ERR] incorrect Serving Network ID')
            return -1
        
        # WNG : there is an issue when retrieving SQN from 2 parallel threads
        #       (almost) at the same time
        # -> both threads can get the same SQN value
        # TODO: we would need a Queue / Lock mechanism so that MM & GMM stacks
        # never get the same SQN value
        
        # get Key and counter
        K, SQN = self.db[IMSI][0], self.db[IMSI][1]
        # increment counter
        self.db[IMSI][1] += 1
        
        # pack SQN from integer to buffer
        SQN = '\0\0' + pack('!I', SQN)
        
        # generate challenge
        RAND = urandom(16)
        
        # compute Milenage functions
        Mil = Milenage( self.OP )
        XRES, CK, IK, AK = Mil.f2345( K, RAND )
        MAC_A = Mil.f1( K, RAND, SQN, AMF ) # pack SQN
        AUTN = xor_string( SQN, AK ) + AMF + MAC_A # pack SQN
        
        # convert to LTE master key
        Kasme = conv_A2(CK=CK, IK=IK, sn_id=SN_ID, sqn_x_ak=AUTN[:6])
        
        # return auth vector
        self._log('Returning 4G authentication vector RAND, XRES, AUTN, KASME'\
                  ' for IMSI %s with SQN %s and SN ID %s' \
                  % (IMSI, hexlify(SQN), hexlify(SN_ID)))
        return RAND, XRES, AUTN, Kasme
#
Example #6
0
    def make_4g_vector(self, IMSI, SN_ID, AMF='\x80\x00'):
        '''
        produces a 4G authentication vector "quadruplet" for a given IMSI and
        network (MCC / MNC)
        
        requests self.db for the authentication key corresponding to the IMSI
        and returns RAND, XRES, AUTN, Kasme obtained from the Milenage 
        and key derivation function functions
        '''
        # lookup AuC_db for authentication key and SQN from IMSI
        if IMSI not in self.db.keys():
            self._log('[ERR] IMSI is not present in AuC.db')
            return -1
        if len(SN_ID) != 3:
            self._log('[ERR] incorrect Serving Network ID')
            return -1

        # WNG : there is an issue when retrieving SQN from 2 parallel threads
        #       (almost) at the same time
        # -> both threads can get the same SQN value
        # TODO: we would need a Queue / Lock mechanism so that MM & GMM stacks
        # never get the same SQN value

        # get Key and counter
        K, SQN = self.db[IMSI][0], self.db[IMSI][1]
        # increment counter
        self.db[IMSI][1] += 1

        # pack SQN from integer to buffer
        SQN = '\0\0' + pack('!I', SQN)

        # generate challenge
        RAND = urandom(16)

        # compute Milenage functions
        Mil = Milenage(self.OP)
        XRES, CK, IK, AK = Mil.f2345(K, RAND)
        MAC_A = Mil.f1(K, RAND, SQN, AMF)  # pack SQN
        AUTN = xor_string(SQN, AK) + AMF + MAC_A  # pack SQN

        # convert to LTE master key
        Kasme = conv_A2(CK=CK, IK=IK, sn_id=SN_ID, sqn_x_ak=AUTN[:6])

        # return auth vector
        self._log('Returning 4G authentication vector RAND, XRES, AUTN, KASME'\
                  ' for IMSI %s with SQN %s and SN ID %s' \
                  % (IMSI, hexlify(SQN), hexlify(SN_ID)))
        return RAND, XRES, AUTN, Kasme
Example #7
0
 def __init__(self, serial_number='000'):
     # prepare data to write into the card
     if not len(serial_number) == 3 or not serial_number.isdigit():
         print('must provided a 3 digits distinct serial number')
         raise()
     self.ICCID      = ICCID_pre + serial_number
     self.ICCID     += str(compute_luhn(self.ICCID))
     self.IMSI       = IMSI_pre + serial_number
     self.K          = K_pre + serial_number
     self.Milenage   = Milenage(OP)
     self.OPc        = make_OPc(self.K, OP)
     # verify parameters
     if map(len, [self.K, self.OPc]) != [16, 16]:
         print('[-] bad length for K or OPc')
         raise()
     # write on the card
     if self.program_card() != 0:
         return
     if self.test_identification() != 0:
         return
     self.auth_test = 0
     if self.test_authentication() != 0:
         return
     # finally add some files for infra (SMSP, HPLMN)
     u = UICC()
     program_files(u)
     u.disconnect()
     # and print results
     print('[+] sysmoUSIM card personalization done and tested successfully:')
     print('%s;%s;0x%s;0x%s' % (self.ICCID, self.IMSI, \
                            hexlify(self.K), hexlify(self.OPc)))
Example #8
0
    def make_4g_vector(self, IMSI, SN_ID, AMF='\x80\x00', RAND=None):
        '''
        produces a 4G authentication vector "quadruplet" for a given IMSI and
        network (MCC / MNC)
        
        requests self.db for the authentication key corresponding to the IMSI
        and returns RAND, XRES, AUTN, Kasme obtained from the Milenage 
        and key derivation function functions
        '''
        # lookup AuC_db for authentication key and SQN from IMSI
        if IMSI not in self.db.keys():
            self._log('WNG', '[make_4g_vector] IMSI {0} not present in '\
                      'AuC.db'.format(IMSI))
            return -1
        if len(SN_ID) != 3:
            self._log('ERR', '[make_4g_vector] incorrect Serving Network ID:'\
                      ' {0}'.format(hexlify(SN_ID)))
            return -1
        
        # get Key and counter
        K, SQN = self.db[IMSI][0], self.db[IMSI][1]
        # increment counter
        self.db[IMSI][1] += 1
        
        # pack SQN from integer to buffer
        SQN = '\0\0' + pack('!I', SQN)
        
        # generate challenge
        if not isinstance(RAND, bytes) or len(RAND) != 16:
            RAND = urandom(16)
        
        # compute Milenage functions
        Mil = Milenage( self.OP )
        XRES, CK, IK, AK = Mil.f2345( K, RAND )
        MAC_A = Mil.f1( K, RAND, SQN, AMF ) # pack SQN
        AUTN = xor_string( SQN, AK ) + AMF + MAC_A # pack SQN
        
        # convert to LTE master key
        Kasme = conv_A2(CK=CK, IK=IK, sn_id=SN_ID, sqn_x_ak=AUTN[:6])
        
        # return auth vector
        self._log('DBG', '[make_4g_vector] returning 4G authentication vector:'\
                  ' RAND, XRES, AUTN, KASME for IMSI {0} with SQN {1} and SN '\
                  'ID {2}'.format(IMSI, hexlify(SQN), hexlify(SN_ID)))
        return RAND, XRES, AUTN, Kasme
#
Example #9
0
    def make_4g_vector(self, IMSI, SN_ID, AMF='\x80\x00', RAND=None):
        '''
        produces a 4G authentication vector "quadruplet" for a given IMSI and
        network (MCC / MNC)
        
        requests self.db for the authentication key corresponding to the IMSI
        and returns RAND, XRES, AUTN, Kasme obtained from the Milenage 
        and key derivation function functions
        '''
        # lookup AuC_db for authentication key and SQN from IMSI
        if IMSI not in self.db.keys():
            self._log('WNG', '[make_4g_vector] IMSI {0} not present in '\
                      'AuC.db'.format(IMSI))
            return -1
        if len(SN_ID) != 3:
            self._log('ERR', '[make_4g_vector] incorrect Serving Network ID:'\
                      ' {0}'.format(hexlify(SN_ID)))
            return -1

        # get Key and counter
        K, SQN = self.db[IMSI][0], self.db[IMSI][1]
        # increment counter
        self.db[IMSI][1] += 1

        # pack SQN from integer to buffer
        SQN = '\0\0' + pack('!I', SQN)

        # generate challenge
        if not isinstance(RAND, bytes) or len(RAND) != 16:
            RAND = urandom(16)

        # compute Milenage functions
        Mil = Milenage(self.OP)
        XRES, CK, IK, AK = Mil.f2345(K, RAND)
        MAC_A = Mil.f1(K, RAND, SQN, AMF)  # pack SQN
        AUTN = xor_string(SQN, AK) + AMF + MAC_A  # pack SQN

        # convert to LTE master key
        Kasme = conv_A2(CK=CK, IK=IK, sn_id=SN_ID, sqn_x_ak=AUTN[:6])

        # return auth vector
        self._log('DBG', '[make_4g_vector] returning 4G authentication vector:'\
                  ' RAND, XRES, AUTN, KASME for IMSI {0} with SQN {1} and SN '\
                  'ID {2}'.format(IMSI, hexlify(SQN), hexlify(SN_ID)))
        return RAND, XRES, AUTN, Kasme
Example #10
0
    def make_3g_vector(self, IMSI, AMF='\0\0', RAND=None):
        '''
        produces a 3G authentication vector "quintuplet" for a given IMSI
        
        requests self.db for the authentication key corresponding to the IMSI
        and returns RAND, XRES, CK, IK, AUTN obtained from the Milenage functions
        '''
        # lookup AuC_db for authentication key and SQN from IMSI
        if IMSI not in self.db.keys():
            self._log('WNG', '[make_3g_vector] IMSI {0} not present in '\
                      'AuC.db'.format(IMSI))
            return -1

        # WNG : there is an issue when retrieving SQN from 2 parallel threads
        #       (almost) at the same time
        # -> both threads can get the same SQN value
        # TODO: we would need a Queue / Lock mechanism so that MM & GMM stacks
        # never get the same SQN value

        # get Key and counter
        K, SQN = self.db[IMSI][0], self.db[IMSI][1]
        # increment counter
        self.db[IMSI][1] += 1

        # pack SQN from integer to buffer
        SQN = '\0\0' + pack('!I', SQN)

        # generate challenge
        if not isinstance(RAND, bytes) or len(RAND) != 16:
            RAND = urandom(16)

        # compute Milenage functions
        Mil = Milenage(self.OP)
        XRES, CK, IK, AK = Mil.f2345(K, RAND)
        MAC_A = Mil.f1(K, RAND, SQN, AMF)  # pack SQN
        AUTN = xor_string(SQN, AK) + AMF + MAC_A  # pack SQN

        # return auth vector
        self._log('DBG', '[make_3g_vector] returning 3G authentication vector:'\
                  ' RAND, XRES, CK, IK, AUTN for IMSI {0} with SQN {1}'.format(
                  IMSI, hexlify(SQN)))
        return RAND, XRES, CK, IK, AUTN
Example #11
0
 def __init__(self):
     """start the AuC
     
     open AuC.db file
     parse it into self.db (dict), containing IMSI: (K, SQN [, OP])
         IMSI: string of digits
         K   : 16 bytes buffer
         ALG2: integer (0, 1, 2 or 3, identifies the 2G auth algorithm)
         SQN : unsigned integer
         OP  : subscriber specific OP, distinct from self.OP, optional field
     """
     self.db = {}
     try:
         # get 3G authentication database AuC.db
         db_fd = open('%sAuC.db' % self.AUC_DB_PATH, 'r')
         # parse it into a dict object with IMSI as key
         for line in db_fd.readlines():
             if line[0] != '#' and line.count(';') >= 3:
                 fields = line[:-1].split(';')
                 IMSI = str(fields[0])
                 K = unhexlify(fields[1].encode('ascii'))
                 ALG2 = int(fields[2])
                 SQN = int(fields[3])
                 if len(fields) > 4 and len(fields) == 32:
                     OP = unhexlify(fields[4].encode('ascii'))
                 else:
                     OP = None
                 self.db[IMSI] = [K, ALG2, SQN, OP]
         self._log('INF',
                   'AuC.db file opened: %i record(s) found' % len(self.db))
         # close the file
         db_fd.close()
     except Exception as err:
         self._log('ERR',
                   'unable to read AuC.db, path: %s' % self.AUC_DB_PATH)
         raise (err)
     self._save_required = False
     #
     # initiatlize the Milenage algo with the AuC-defined OP
     self.Milenage = Milenage(self.OP)
     #
     self._log('DBG', 'AuC started')
Example #12
0
 def make_3g_vector(self, IMSI, AMF='\0\0', RAND=None):
     '''
     produces a 3G authentication vector "quintuplet" for a given IMSI
     
     requests self.db for the authentication key corresponding to the IMSI
     and returns RAND, XRES, CK, IK, AUTN obtained from the Milenage functions
     '''
     # lookup AuC_db for authentication key and SQN from IMSI
     if IMSI not in self.db.keys():
         self._log('WNG', '[make_3g_vector] IMSI {0} not present in '\
                   'AuC.db'.format(IMSI))
         return -1
     
     # WNG : there is an issue when retrieving SQN from 2 parallel threads
     #       (almost) at the same time
     # -> both threads can get the same SQN value
     # TODO: we would need a Queue / Lock mechanism so that MM & GMM stacks
     # never get the same SQN value
     
     # get Key and counter
     K, SQN = self.db[IMSI][0], self.db[IMSI][1]
     # increment counter
     self.db[IMSI][1] += 1
     
     # pack SQN from integer to buffer
     SQN = '\0\0' + pack('!I', SQN)
     
     # generate challenge
     if not isinstance(RAND, bytes) or len(RAND) != 16:
         RAND = urandom(16)
     
     # compute Milenage functions
     Mil = Milenage( self.OP )
     XRES, CK, IK, AK = Mil.f2345( K, RAND )
     MAC_A = Mil.f1( K, RAND, SQN, AMF ) # pack SQN
     AUTN = xor_string( SQN, AK ) + AMF + MAC_A # pack SQN
     
     # return auth vector
     self._log('DBG', '[make_3g_vector] returning 3G authentication vector:'\
               ' RAND, XRES, CK, IK, AUTN for IMSI {0} with SQN {1}'.format(
               IMSI, hexlify(SQN)))
     return RAND, XRES, CK, IK, AUTN
Example #13
0
class personalize(object):
    '''
    Class to program sysmo-USIM card
    takes a 3 digit serial number as argument to personalize the USIM card.
    
    Makes use of the fixed parameters in this file header:
    ICCID_pre, IMSI_pre, Ki_pre, OP,
    SMSP, HPLMN, PLMNsel, SST, SPN
    '''
    #
    # current auth counter for the USIM to personalize:
    # if you do not know it, just comment the following attribute
    # it seems sysmoUSIM are shipped with a counter value around 31
    SQN = 1
    
    def __init__(self, serial_number='000'):
        # prepare data to write into the card
        if not len(serial_number) == 3 or not serial_number.isdigit():
            print('must provided a 3 digits distinct serial number')
            raise()
        self.ICCID      = ICCID_pre + serial_number
        self.ICCID     += str(compute_luhn(self.ICCID))
        self.IMSI       = IMSI_pre + serial_number
        self.K          = K_pre + serial_number
        self.Milenage   = Milenage(OP)
        self.OPc        = make_OPc(self.K, OP)
        # verify parameters
        if map(len, [self.K, self.OPc]) != [16, 16]:
            print('[-] bad length for K or OPc')
            raise()
        # write on the card
        if self.program_card() != 0:
            return
        if self.test_identification() != 0:
            return
        self.auth_test = 0
        if self.test_authentication() != 0:
            return
        # finally add some files for infra (SMSP, HPLMN)
        u = UICC()
        program_files(u)
        u.disconnect()
        # and print results
        print('[+] sysmoUSIM card personalization done and tested successfully:')
        print('%s;%s;0x%s;0x%s' % (self.ICCID, self.IMSI, \
                               hexlify(self.K), hexlify(self.OPc)))
    
    def program_card(self):
        return program_vec(K = stringToByte(self.K), \
                           OPc = stringToByte(self.OPc), \
                           ICCID = encode_ICCID(self.ICCID), \
                           IMSI = encode_IMSI(self.IMSI))
        
    def test_identification(self):
        u = UICC()
        self.ICCID = u.get_ICCID()
        u.disconnect()
        u = USIM()
        self.IMSI = u.get_imsi()
        print('[+] USIM identification:\nICCID: %s\nIMSI: %s'  \
              % (self.ICCID, self.IMSI))
        u.disconnect()
        if not self.ICCID or not self.IMSI:
            print('[-] identification error')
            return 1
        return 0
    
    def test_authentication(self):
        if self.auth_test >= 2:
            return 1
        u = USIM()
        # prepare auth challenge
        self.RAND = urand(16) # challenge is 128 bits
        if not hasattr(self, 'SQN'):
            self.SQN = 0 # default SQN is 0, coded on 48 bits
        AMF = 2*'\0' # management field, unneeded, left blank
        # compute Milenage functions
        XRES, CK, IK, AK = self.Milenage.f2345( self.K, self.RAND )
        MAC_A = self.Milenage.f1(self.K, self.RAND, sqn_to_str(self.SQN), AMF)
        AUTN = xor_string(sqn_to_str(self.SQN), AK) + AMF + MAC_A
        # run auth data on the USIM
        ret = u.authenticate(stringToByte(self.RAND), stringToByte(AUTN), '3G')
        # check results (and pray)
        if ret == None:
            print('[-] authenticate() failed; something wrong happened, '\
                  'maybe during card programmation ?')
        elif len(ret) == 1:
            print('[-] sync failure during authenticate(); unmasking counter')
            auts = byteToString(ret[0])
            ak = self.Milenage.f5star(self.K, self.RAND)
            self.SQN = str_to_sqn(xor_string(auts, ak)[:6])
            print('[+] auth counter value in USIM: %i' % self.SQN)
            self.SQN += 1
            print('[+] retrying authenticate() with SQN: %i' % self.SQN)
            u.disconnect()
            self.test_authentication()
        elif len(ret) in (3, 4):
            # RES, CK, IK(, Kc)
            if ret[0:3] == map(stringToByte, [XRES, CK, IK]):
                print('[+] 3G auth successful with SQN: %i\n' \
                      'increment it from now' % self.SQN)
                print('[+] USIM secrets:\nOPc: %s\nK: %s' \
                      % (hexlify(self.OPc), hexlify(self.K)))
            else:
                print('[-] 3G auth accepted on the USIM, ' \
                      'but not matching auth vector generated: strange!')
                print('card returned:\n%s' % ret)
        u.disconnect()
        return 0
Example #14
0
class AuC:
    """3GPP Authentication Centre
    
    use the AuC.db file with (IMSI, K, SQN[, OP]) records to produce 2G, 3G or
    4G auth vectors, and resynchronize SQN
    """

    # verbosity level: list of log types to be displayed
    DEBUG = ('ERR', 'WNG', 'INF', 'DBG')

    AUC_DB_PATH = os.path.dirname(os.path.abspath(__file__)) + os.sep
    #AUC_DB_PATH = 'C:\Python27\Lib\sitepackages\pycrate_corenet\'

    # when rewriting the AuC.db, do a back-up of the last version of the file
    DO_BACKUP = True

    # MNO OP diversification parameter
    # The AuC supports also a per-subscriber OP, to be set optionally in the AuC.db database
    OP = b'ffffffffffffffff'

    # SQN incrementation when a resynch is required by a USIM card
    SQN_SYNCH_STEP = 2

    # PLMN restriction for returning 4G vectors
    # provide a list of allowed PLMN, or None for disabling the filter
    #PLMN_FILTER = ['20869']
    PLMN_FILTER = None

    def __init__(self):
        """start the AuC
        
        open AuC.db file
        parse it into self.db (dict), containing IMSI: (K, SQN [, OP])
            IMSI: string of digits
            K   : 16 bytes buffer
            ALG2: integer (0, 1, 2 or 3, identifies the 2G auth algorithm)
            SQN : unsigned integer
            OP  : subscriber specific OP, distinct from self.OP, optional field
        """
        self.db = {}
        try:
            # get 3G authentication database AuC.db
            db_fd = open('%sAuC.db' % self.AUC_DB_PATH, 'r')
            # parse it into a dict object with IMSI as key
            for line in db_fd.readlines():
                if line[0] != '#' and line.count(';') >= 3:
                    fields = line[:-1].split(';')
                    IMSI = str(fields[0])
                    K = unhexlify(fields[1].encode('ascii'))
                    ALG2 = int(fields[2])
                    SQN = int(fields[3])
                    if len(fields) > 4 and len(fields) == 32:
                        OP = unhexlify(fields[4].encode('ascii'))
                    else:
                        OP = None
                    self.db[IMSI] = [K, ALG2, SQN, OP]
            self._log('INF',
                      'AuC.db file opened: %i record(s) found' % len(self.db))
            # close the file
            db_fd.close()
        except Exception as err:
            self._log('ERR',
                      'unable to read AuC.db, path: %s' % self.AUC_DB_PATH)
            raise (err)
        self._save_required = False
        #
        # initiatlize the Milenage algo with the AuC-defined OP
        self.Milenage = Milenage(self.OP)
        #
        self._log('DBG', 'AuC started')

    def _log(self, logtype='DBG', msg=''):
        if logtype in self.DEBUG:
            log('[%s] [AuC] %s' % (logtype, msg))

    def save(self):
        """
        optionally save old AuC.db with timestamp suffix (if self.DO_BACKUP is set)
        write the current content of self.db dict into AuC.db, with updated SQN
        values
        """
        if not self._save_required:
            return

        T = timemod.strftime('20%y%m%d_%H%M', timemod.gmtime())

        # get header from original file AuC.db
        header = []
        file_db = open('%sAuC.db' % self.AUC_DB_PATH)
        for line in file_db:
            if line[0] == '#':
                header.append(line)
            else:
                break
        header = ''.join(header) + '\n'
        file_db.close()

        if self.DO_BACKUP:
            # save the last current version of AuC.db
            os.rename('%sAuC.db' % self.AUC_DB_PATH,
                      '%sAuC.%s.db' % (self.AUC_DB_PATH, T))
            self._log('DBG', 'old AuC.db saved with timestamp')

        # save the current self.db into a new AuC.db file
        file_db = open('%s/AuC.db' % self.AUC_DB_PATH, 'w')
        file_db.write(header)
        indexes = list(self.db.keys())
        indexes.sort()
        for IMSI in indexes:
            K, ALG2, SQN, OP = self.db[IMSI]
            if OP is not None:
                # OP additional parameter
                file_db.write('%s;%s;%i;%i;%s;\n'\
                    % (IMSI, hexlify(K).decode('ascii'), ALG2, SQN, hexlify(OP).decode('ascii')))
            else:
                file_db.write('%s;%s;%i;%i;\n'\
                    % (IMSI, hexlify(K).decode('ascii'), ALG2, SQN))
        file_db.close()
        self._log('INF', 'current db saved to AuC.db file')

    stop = save

    def make_2g_vector(self, IMSI, RAND=None):
        """
        return a 2G authentication vector "triplet":
        RAND [16 bytes], RES [4 bytes], Kc [8 bytes]
        or None if the IMSI is not defined in the db or ALG2 is invalid
        
        RAND can be passed as argument
        """
        # lookup db for authentication Key and algorithm id for IMSI
        try:
            K, ALG2, SQN, OP = self.db[IMSI]
        except KeyError:
            self._log('WNG',
                      '[make_2g_vector] IMSI %s not present in AuC.db' % IMSI)
            return None
        #
        if not RAND:
            RAND = genrand(16)
        #
        if ALG2 == 0:
            if OP is not None:
                XRES, CK, IK, AK = self.Milenage.f2345(RAND, K, OP)
            else:
                XRES, CK, IK, AK = self.Milenage.f2345(RAND, K)
            RES, Ck = conv_C2(XRES), conv_C3(Ck, Ik)
        else:
            if ALG2 == 1:
                RES, Ck = comp128v1(K, RAND)
            elif ALG2 == 2:
                RES, Ck = comp128v2(K, RAND)
            elif ALG2 == 3:
                RES, Ck = comp128v3(K, RAND)
            else:
                return None
        #
        # return auth vector
        self._log('DBG', '[make_2g_vector] IMSI %s: RAND %s, RES %s, Kc %s'\
                  % (IMSI, hexlify(RAND).decode('ascii'), hexlify(RES).decode('ascii'),
                     hexlify(Kc).decode('ascii')))
        return RAND, RES, Kc

    def make_3g_vector(self, IMSI, AMF=b'\0\0', RAND=None):
        '''
        return a 3G authentication vector "quintuplet":
        RAND [16 bytes], XRES [8 bytes], AUTN [16 bytes], CK [16 bytes], IK [16 bytes]
        or None if the IMSI is not defined in the db or does not support Milenage
        
        RAND can be passed as argument
        '''
        # lookup db for authentication Key and counter for IMSI
        try:
            K_ALG2_SQN_OP = self.db[IMSI]
        except:
            self._log('WNG',
                      '[make_3g_vector] IMSI %s not present in AuC.db' % IMSI)
            return None
        #
        K, ALG2, SQN, OP = K_ALG2_SQN_OP
        #
        if SQN == -1:
            # Milenage not supported
            self._log(
                'WNG',
                '[make_3g_vector] IMSI %s does not support Milenage' % IMSI)
            return None
        #
        # increment SQN counter in the db
        K_ALG2_SQN_OP[2] += 1
        self._save_required = True

        # pack SQN from integer to a 48-bit buffer
        SQNb = b'\0\0' + pack('>I', SQN)

        # generate challenge if necessary
        if RAND is None:
            RAND = genrand(16)

        # compute Milenage functions
        if OP is not None:
            XRES, CK, IK, AK = self.Milenage.f2345(K, RAND, OP)
            MAC_A = self.Milenage.f1(K, RAND, SQNb, AMF, OP)
            AUTN = xor_buf(SQNb, AK) + AMF + MAC_A
        else:
            XRES, CK, IK, AK = self.Milenage.f2345(K, RAND)
            MAC_A = self.Milenage.f1(K, RAND, SQNb, AMF)
            AUTN = xor_buf(SQNb, AK) + AMF + MAC_A

        # return auth vector
        self._log('DBG', '[make_3g_vector] IMSI %s, SQN %i: RAND %s, XRES %s, AUTN %s, CK %s, IK %s'\
                  % (IMSI, SQN, hexlify(RAND).decode('ascii'), hexlify(XRES).decode('ascii'),
                     hexlify(AUTN).decode('ascii'), hexlify(CK).decode('ascii'),
                     hexlify(IK).decode('ascii')))
        return RAND, XRES, AUTN, CK, IK

    def make_4g_vector(self, IMSI, SN_ID, AMF=b'\x80\x00', RAND=None):
        """
        return a 4G authentication vector "quadruplet":
        RAND [16 bytes], XRES [8 bytes], AUTN [16 bytes], KASME [32 bytes]
        or None if the IMSI is not defined in the db or does not support Milenage
        or SN_ID is invalid or not allowed
        
        SN_ID is the serving network identity, bcd-encoded buffer
        RAND can be passed as argument
        """
        if not isinstance(SN_ID, bytes_types) or len(SN_ID) != 3:
            self._log(
                'WNG', '[make_4g_vector] SN_ID invalid, %s' %
                hexlify(SN_ID).decode('ascii'))
            return None
        elif self.PLMN_FILTER is not None and SN_ID not in self.PLMN_FILTER:
            self._log(
                'WNG', '[make_4g_vector] SN_ID not allowed, %s' %
                hexlify(SN_ID).decode('ascii'))
            return None
        #
        # lookup db for authentication Key and counter for IMSI
        try:
            K_ALG2_SQN_OP = self.db[IMSI]
        except:
            self._log('WNG',
                      '[make_4g_vector] IMSI %s not present in AuC.db' % IMSI)
            return None
        #
        K, ALG2, SQN, OP = K_ALG2_SQN_OP
        #
        if SQN == -1:
            # Milenage not supported
            self._log(
                'WNG',
                '[make_4g_vector] IMSI %s does not support Milenage' % IMSI)
            return None
        #
        # increment SQN counter in the db
        K_ALG2_SQN_OP[2] += 1
        self._save_required = True

        # pack SQN from integer to a 48-bit buffer
        SQNb = b'\0\0' + pack('>I', SQN)
        #
        # generate challenge
        if RAND is None:
            RAND = genrand(16)

        # compute Milenage functions
        if OP is not None:
            XRES, CK, IK, AK = self.Milenage.f2345(K, RAND, OP)
            MAC_A = self.Milenage.f1(K, RAND, SQNb, AMF, OP)
            SQN_X_AK = xor_buf(SQNb, AK)
            AUTN = SQN_X_AK + AMF + MAC_A
        else:
            XRES, CK, IK, AK = self.Milenage.f2345(K, RAND)
            MAC_A = self.Milenage.f1(K, RAND, SQNb, AMF)
            SQN_X_AK = xor_buf(SQNb, AK)
            AUTN = SQN_X_AK + AMF + MAC_A

        # convert to LTE master key
        KASME = conv_A2(CK, IK, SN_ID, SQN_X_AK)

        # return auth vector
        self._log('DBG', '[make_4g_vector] IMSI %s, SQN %i, SN_ID %s: RAND %s, XRES %s, AUTN %s, KASME %s'\
                  % (IMSI, SQN, hexlify(SN_ID).decode('ascii'), hexlify(RAND).decode('ascii'),
                     hexlify(XRES).decode('ascii'), hexlify(AUTN).decode('ascii'),
                     hexlify(KASME).decode('ascii')))
        return RAND, XRES, AUTN, KASME

    def synch_sqn(self, IMSI, RAND, AUTS):
        """
        synchronize the local counter SQN with AUTS provided by the USIM
        in response to a given 3G or 4G authentication challenge (RAND, AMF)
        
        return 0 on successful synch, 1 on unsuccessful synch due to invalid AUTS
        or None if the IMSI is not defined in the db
        """
        # lookup db for authentication Key and counter for IMSI
        try:
            K_ALG2_SQN_OP = self.db[IMSI]
        except:
            self._log('WNG',
                      '[synch_sqn] IMSI %s not present in AuC.db' % IMSI)
            return None
        #
        K, ALG2, SQN, OP = K_ALG2_SQN_OP
        #
        if K_ALG2_SQN_OP[2] == -1:
            # Milenage not supported
            self._log(
                'WNG',
                '[make_3g_vector] IMSI %s does not support Milenage' % IMSI)
            return None
        #
        # 33.102, section 6.3.3, for resynch, AMF is always null (0x0000)
        AMF = b'\0\0'
        #
        # compute Milenage functions and unmask SQN
        if OP is not None:
            AK = self.Milenage.f5star(K, RAND, OP)
            SQN_MS = xor_buf(AUTS[0:6], AK)
            MAC_S = self.Milenage.f1star(K, RAND, SQN_MS, AMF, OP)
            SQN_MSi = unpack('>Q', b'\0\0' + SQN_MS)[0]
        else:
            AK = self.Milenage.f5star(K, RAND)
            SQN_MS = xor_buf(AUTS[0:6], AK)
            MAC_S = self.Milenage.f1star(K, RAND, SQN_MS, AMF)
            SQN_MSi = unpack('>Q', b'\0\0' + SQN_MS)[0]

        self._log('DBG', '[synch_sqn] USIM resynchronization, SQN_MS %i, MAC_S %s'\
                  % (SQN_MSi, hexlify(MAC_S).decode('ascii')))

        # authenticate the USIM
        if MAC_S != AUTS[6:14]:
            self._log(
                'WNG',
                '[synch_sqn] IMSI %s, USIM authentication failure' % IMSI)
            return 1

        # resynchronize local SQN value
        K_ALG2_SQN_OP[2] = SQN_MSi + self.SQN_SYNCH_STEP
        self._save_required = True
        self._log(
            'DBG', '[synch_sqn] IMSI %s, SQN resynchronized to %i' %
            (IMSI, K_ALG2_SQN_OP[2]))
        return 0
Example #15
0
class personalize(object):
    '''
    Class to program sysmo-USIM card
    takes a 3 digit serial number as argument to personalize the USIM card.
    
    Makes use of the fixed parameters in this file header:
    ICCID_pre, IMSI_pre, Ki_pre, OP,
    SMSP, HPLMN, PLMNsel, SST, SPN
    '''
    #
    # current auth counter for the USIM to personalize:
    # if you do not know it, just comment the following attribute
    # it seems sysmoUSIM are shipped with a counter value around 31
    SQN = 1

    def __init__(self, serial_number='000'):
        # prepare data to write into the card
        if not len(serial_number) == 3 or not serial_number.isdigit():
            print('must provided a 3 digits distinct serial number')
            raise ()
        self.ICCID = ICCID_pre + serial_number
        self.ICCID += str(compute_luhn(self.ICCID))
        self.IMSI = IMSI_pre + serial_number
        self.K = Ki_pre + serial_number
        self.Milenage = Milenage(OP)
        self.OPc = make_OPc(self.K, OP)
        # verify parameters
        if map(len, [self.K, self.OPc]) != [16, 16]:
            print('[-] bad length for K or OPc')
            raise ()
        # write on the card
        if self.program_card() != 0:
            return
        if self.test_identification() != 0:
            return
        self.auth_test = 0
        if self.test_authentication() != 0:
            return
        # finally add some files for infra (SMSP, HPLMN)
        u = UICC()
        program_files(u)
        u.disconnect()
        # and print results
        print(
            '[+] sysmoUSIM card personalization done and tested successfully:')
        print('%s;%s;0x%s;0x%s' % (self.ICCID, self.IMSI, \
                               hexlify(self.K), hexlify(self.OPc)))

    def program_card(self):
        return program_vec(K = stringToByte(self.K), \
                           OPc = stringToByte(self.OPc), \
                           ICCID = encode_ICCID(self.ICCID), \
                           IMSI = encode_IMSI(self.IMSI))

    def test_identification(self):
        u = UICC()
        self.ICCID = u.get_ICCID()
        u.disconnect()
        u = USIM()
        self.IMSI = u.get_imsi()
        print('[+] USIM identification:\nICCID: %s\nIMSI: %s'  \
              % (self.ICCID, self.IMSI))
        u.disconnect()
        if not self.ICCID or not self.IMSI:
            print('[-] identification error')
            return 1
        return 0

    def test_authentication(self):
        if self.auth_test >= 2:
            return 1
        u = USIM()
        # prepare auth challenge
        self.RAND = urand(16)  # challenge is 128 bits
        if not hasattr(self, 'SQN'):
            self.SQN = 0  # default SQN is 0, coded on 48 bits
        AMF = 2 * '\0'  # management field, unneeded, left blank
        # compute Milenage functions
        XRES, CK, IK, AK = self.Milenage.f2345(self.K, self.RAND)
        MAC_A = self.Milenage.f1(self.K, self.RAND, sqn_to_str(self.SQN), AMF)
        AUTN = xor_string(sqn_to_str(self.SQN), AK) + AMF + MAC_A
        # run auth data on the USIM
        ret = u.authenticate(stringToByte(self.RAND), stringToByte(AUTN), '3G')
        # check results (and pray)
        if ret == None:
            print('[-] authenticate() failed; something wrong happened, '\
                  'maybe during card programmation ?')
        elif len(ret) == 1:
            print('[-] sync failure during authenticate(); unmasking counter')
            auts = byteToString(ret[0])
            ak = self.Milenage.f5star(self.K, self.RAND)
            self.SQN = str_to_sqn(xor_string(auts, ak)[:6])
            print('[+] auth counter value in USIM: %i' % self.SQN)
            self.SQN += 1
            print('[+] retrying authenticate() with SQN: %i' % self.SQN)
            u.disconnect()
            self.test_authentication()
        elif len(ret) in (3, 4):
            # RES, CK, IK(, Kc)
            if ret[0:3] == map(stringToByte, [XRES, CK, IK]):
                print('[+] 3G auth successful with SQN: %i\n' \
                      'increment it from now' % self.SQN)
                print('[+] USIM secrets:\nOPc: %s\nK: %s' \
                      % (hexlify(self.OPc), hexlify(self.K)))
            else:
                print('[-] 3G auth accepted on the USIM, ' \
                      'but not matching auth vector generated: strange!')
                print('card returned:\n%s' % ret)
        u.disconnect()
        return 0