blob: 09426b5db5fc53ff90f15150adab269fdf8d1878 [file] [log] [blame]
Sylvain Munaut93ec5f32010-12-26 00:11:19 +01001#!/usr/bin/env python
2
3#
4# CCC Event HLR management common stuff
5#
6#
7# Copyright (C) 2010 Sylvain Munaut <tnt@246tNt.com>
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 2 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.
21#
22
23import hashlib
24import os
25import random
26
27from collections import namedtuple
28
29try:
30 import json
31except Importerror:
32 # Python < 2.5
33 import simplejson as json
34
35#
36# Various helpers
37#
38
39def isnum(s, l=-1):
40 return s.isdigit() and ((l== -1) or (len(s) == l))
41
42
43#
44# Storage tuples
45#
46
47CardParameters = namedtuple("CardParameters", "num iccid imsi ki")
48NetworkParameters = namedtuple("NetworkParameters", "name cc mcc mnc smsp")
49
50
51#
52# State management
53#
54
55class StateManager(object):
56
57 def __init__(self, filename=None, options=None):
58 # Filename for state storage
59 self._filename = filename
60
61 # Params from options
62 self._net_name = options.name if options else None
63 self._net_cc = options.country if options else None
64 self._net_mcc = options.mcc if options else None
65 self._net_mnc = options.mnc if options else None
66 self._net_smsp = options.smsp if options else None
67
68 self._secret = options.secret if options else None
69
70 # Default
71 self._num_gen = 0
72 self._num_write = 0
73
74 def load(self):
75 # Skip if no state file
76 if self._filename is None:
77 return
78
79 # Skip if doesn't exist yet
80 if not os.path.isfile(self._filename):
81 return
82
83 # Read
84 fh = open(self._filename, 'r')
85 data = fh.read()
86 fh.close()
87
88 # Decode json and merge
89 dd = json.loads(data)
90
91 self._net_name = dd['name']
92 self._net_cc = dd['cc']
93 self._net_mcc = dd['mcc']
94 self._net_mnc = dd['mnc']
95 self._net_smsp = dd['smsp']
96 self._secret = dd['secret']
97 self._num_gen = dd['num_gen']
98 self._num_write = dd['num_write']
99
100 def save(self):
101 # Skip if no state file
102 if self._filename is None:
103 return
104
105 # Serialize
106 data = json.dumps({
107 'name': self._net_name,
108 'cc': self._net_cc,
109 'mcc': self._net_mcc,
110 'mnc': self._net_mnc,
111 'smsp': self._net_smsp,
112 'secret': self._secret,
113 'num_gen': self._num_gen,
114 'num_write': self._num_write,
115 })
116
117 # Save in json
118 fh = open(self._filename, 'w')
119 fh.write(data)
120 fh.close()
121
122 @property
123 def network(self):
124 return NetworkParameters(
125 self._net_name,
126 self._net_cc,
127 self._net_mcc,
128 self._net_mnc,
129 self._net_smsp,
130 )
131
132 def get_secret(self):
133 return self._secret
134
135 def next_gen_num(self):
136 n = self._num_gen
137 self._num_gen += 1
138 return n
139
140 def next_write_num(self):
141 n = self._num_write
142 self._num_write += 1
143 return n
144
145#
146# Card parameters generation
147#
148
149class CardParametersGenerator(object):
150
151 def __init__(self, cc, mcc, mnc, secret):
152 # Digitize country code (2 or 3 digits)
153 self._cc_digits = ('%03d' if cc > 100 else '%02d') % cc
154
155 # Digitize MCC/MNC (5 or 6 digits)
156 self._plmn_digits = ('%03d%03d' if mnc > 100 else '%03d%02d') % (mcc, mnc)
157
158 # Store secret
159 self._secret = secret
160
161 def _digits(self, usage, len_, num):
162 s = hashlib.sha1(self._secret + usage + '%d' % num)
163 d = ''.join(['%02d'%ord(x) for x in s.digest()])
164 return d[0:len_]
165
166 def _gen_iccid(self, num):
167 iccid = (
168 '89' + # Common prefix (telecom)
169 self._cc_digits + # Country Code on 2/3 digits
170 self._plmn_digits # MCC/MNC on 5/6 digits
171 )
172 ml = 20 - len(iccid)
173 iccid += self._digits('ccid', ml, num)
174 return iccid
175
176 def _gen_imsi(self, num):
177 ml = 15 - len(self._plmn_digits)
178 msin = self._digits('imsi', ml, num)
179 return (
180 self._plmn_digits + # MCC/MNC on 5/6 digits
181 msin # MSIN
182 )
183
184 def _gen_ki(self):
185 return ''.join(['%02x' % random.randrange(0,256) for i in range(16)])
186
187 def generate(self, num):
188 return CardParameters(
189 num,
190 self._gen_iccid(num),
191 self._gen_imsi(num),
192 self._gen_ki(),
193 )