diff options
Diffstat (limited to 'ccc.py')
-rw-r--r-- | ccc.py | 193 |
1 files changed, 193 insertions, 0 deletions
@@ -0,0 +1,193 @@ +#!/usr/bin/env python + +# +# CCC Event HLR management common stuff +# +# +# Copyright (C) 2010 Sylvain Munaut <tnt@246tNt.com> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +import hashlib +import os +import random + +from collections import namedtuple + +try: + import json +except Importerror: + # Python < 2.5 + import simplejson as json + +# +# Various helpers +# + +def isnum(s, l=-1): + return s.isdigit() and ((l== -1) or (len(s) == l)) + + +# +# Storage tuples +# + +CardParameters = namedtuple("CardParameters", "num iccid imsi ki") +NetworkParameters = namedtuple("NetworkParameters", "name cc mcc mnc smsp") + + +# +# State management +# + +class StateManager(object): + + def __init__(self, filename=None, options=None): + # Filename for state storage + self._filename = filename + + # Params from options + self._net_name = options.name if options else None + self._net_cc = options.country if options else None + self._net_mcc = options.mcc if options else None + self._net_mnc = options.mnc if options else None + self._net_smsp = options.smsp if options else None + + self._secret = options.secret if options else None + + # Default + self._num_gen = 0 + self._num_write = 0 + + def load(self): + # Skip if no state file + if self._filename is None: + return + + # Skip if doesn't exist yet + if not os.path.isfile(self._filename): + return + + # Read + fh = open(self._filename, 'r') + data = fh.read() + fh.close() + + # Decode json and merge + dd = json.loads(data) + + self._net_name = dd['name'] + self._net_cc = dd['cc'] + self._net_mcc = dd['mcc'] + self._net_mnc = dd['mnc'] + self._net_smsp = dd['smsp'] + self._secret = dd['secret'] + self._num_gen = dd['num_gen'] + self._num_write = dd['num_write'] + + def save(self): + # Skip if no state file + if self._filename is None: + return + + # Serialize + data = json.dumps({ + 'name': self._net_name, + 'cc': self._net_cc, + 'mcc': self._net_mcc, + 'mnc': self._net_mnc, + 'smsp': self._net_smsp, + 'secret': self._secret, + 'num_gen': self._num_gen, + 'num_write': self._num_write, + }) + + # Save in json + fh = open(self._filename, 'w') + fh.write(data) + fh.close() + + @property + def network(self): + return NetworkParameters( + self._net_name, + self._net_cc, + self._net_mcc, + self._net_mnc, + self._net_smsp, + ) + + def get_secret(self): + return self._secret + + def next_gen_num(self): + n = self._num_gen + self._num_gen += 1 + return n + + def next_write_num(self): + n = self._num_write + self._num_write += 1 + return n + +# +# Card parameters generation +# + +class CardParametersGenerator(object): + + def __init__(self, cc, mcc, mnc, secret): + # Digitize country code (2 or 3 digits) + self._cc_digits = ('%03d' if cc > 100 else '%02d') % cc + + # Digitize MCC/MNC (5 or 6 digits) + self._plmn_digits = ('%03d%03d' if mnc > 100 else '%03d%02d') % (mcc, mnc) + + # Store secret + self._secret = secret + + def _digits(self, usage, len_, num): + s = hashlib.sha1(self._secret + usage + '%d' % num) + d = ''.join(['%02d'%ord(x) for x in s.digest()]) + return d[0:len_] + + def _gen_iccid(self, num): + iccid = ( + '89' + # Common prefix (telecom) + self._cc_digits + # Country Code on 2/3 digits + self._plmn_digits # MCC/MNC on 5/6 digits + ) + ml = 20 - len(iccid) + iccid += self._digits('ccid', ml, num) + return iccid + + def _gen_imsi(self, num): + ml = 15 - len(self._plmn_digits) + msin = self._digits('imsi', ml, num) + return ( + self._plmn_digits + # MCC/MNC on 5/6 digits + msin # MSIN + ) + + def _gen_ki(self): + return ''.join(['%02x' % random.randrange(0,256) for i in range(16)]) + + def generate(self, num): + return CardParameters( + num, + self._gen_iccid(num), + self._gen_imsi(num), + self._gen_ki(), + ) |