aboutsummaryrefslogtreecommitdiffstats
path: root/ccc.py
diff options
context:
space:
mode:
Diffstat (limited to 'ccc.py')
-rw-r--r--ccc.py193
1 files changed, 193 insertions, 0 deletions
diff --git a/ccc.py b/ccc.py
new file mode 100644
index 0000000..09426b5
--- /dev/null
+++ b/ccc.py
@@ -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(),
+ )