blob: ba947028166e42780a116f2cbae45edfdc50776a [file] [log] [blame]
Sylvain Munaut76504e02010-12-07 00:24:32 +01001#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4""" pySim: various utilities
5"""
6
7#
8# Copyright (C) 2009-2010 Sylvain Munaut <tnt@246tNt.com>
9#
10# This program is free software: you can redistribute it and/or modify
11# it under the terms of the GNU General Public License as published by
12# the Free Software Foundation, either version 2 of the License, or
13# (at your option) any later version.
14#
15# This program is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18# GNU General Public License for more details.
19#
20# You should have received a copy of the GNU General Public License
21# along with this program. If not, see <http://www.gnu.org/licenses/>.
22#
23
24
25def h2b(s):
26 return ''.join([chr((int(x,16)<<4)+int(y,16)) for x,y in zip(s[0::2], s[1::2])])
27
28def b2h(s):
29 return ''.join(['%02x'%ord(x) for x in s])
30
31def h2i(s):
32 return [(int(x,16)<<4)+int(y,16) for x,y in zip(s[0::2], s[1::2])]
33
34def i2h(s):
35 return ''.join(['%02x'%(x) for x in s])
36
Alexander Chemerisa5f0ea62017-07-18 16:48:47 +030037def h2s(s):
38 return ''.join([chr((int(x,16)<<4)+int(y,16)) for x,y in zip(s[0::2], s[1::2]) if not (x == 'f' and y == 'f') ])
39
40def s2h(s):
41 return b2h(s)
42
Sylvain Munaut76504e02010-12-07 00:24:32 +010043def swap_nibbles(s):
44 return ''.join([x+y for x,y in zip(s[1::2], s[0::2])])
45
46def rpad(s, l, c='f'):
47 return s + c * (l - len(s))
48
49def lpad(s, l, c='f'):
50 return c * (l - len(s)) + s
Alexander Chemeris5e96c3d2013-07-04 17:33:33 +040051
Ben Fox-Moore0ec14752018-09-24 15:47:02 +020052def half_round_up(n):
53 return (n + 1)//2
54
55# IMSI encoded format:
56# For IMSI 0123456789ABCDE:
57#
58# | byte 1 | 2 upper | 2 lower | 3 upper | 3 lower | ... | 9 upper | 9 lower |
59# | length in bytes | 0 | odd/even | 2 | 1 | ... | E | D |
60#
61# If the IMSI is less than 15 characters, it should be padded with 'f' from the end.
62#
63# The length is the total number of bytes used to encoded the IMSI. This includes the odd/even
64# parity bit. E.g. an IMSI of length 14 is 8 bytes long, not 7, as it uses bytes 2 to 9 to
65# encode itself.
66#
67# Because of this, an odd length IMSI fits exactly into len(imsi) + 1 // 2 bytes, whereas an
68# even length IMSI only uses half of the last byte.
69
Alexander Chemeris7be92ff2013-07-10 11:18:06 +040070def enc_imsi(imsi):
71 """Converts a string imsi into the value of the EF"""
Ben Fox-Moore0ec14752018-09-24 15:47:02 +020072 l = half_round_up(len(imsi) + 1) # Required bytes - include space for odd/even indicator
Alexander Chemeris7be92ff2013-07-10 11:18:06 +040073 oe = len(imsi) & 1 # Odd (1) / Even (0)
Ben Fox-Moore0ec14752018-09-24 15:47:02 +020074 ei = '%02x' % l + swap_nibbles('%01x%s' % ((oe<<3)|1, rpad(imsi, 15)))
Alexander Chemeris7be92ff2013-07-10 11:18:06 +040075 return ei
76
Alexander Chemeris5e96c3d2013-07-04 17:33:33 +040077def dec_imsi(ef):
78 """Converts an EF value to the imsi string representation"""
79 if len(ef) < 4:
80 return None
Pau Espin Pedrol665bd222017-12-29 20:30:35 +010081 l = int(ef[0:2], 16) * 2 # Length of the IMSI string
Ben Fox-Moore0ec14752018-09-24 15:47:02 +020082 l = l - 1 # Encoded length byte includes oe nibble
83 swapped = swap_nibbles(ef[2:]).rstrip('f')
Alexander Chemeris5e96c3d2013-07-04 17:33:33 +040084 oe = (int(swapped[0])>>3) & 1 # Odd (1) / Even (0)
Ben Fox-Moore0ec14752018-09-24 15:47:02 +020085 if not oe:
86 # if even, only half of last byte was used
Alexander Chemeris5e96c3d2013-07-04 17:33:33 +040087 l = l-1
Ben Fox-Moore0ec14752018-09-24 15:47:02 +020088 if l != len(swapped) - 1:
Alexander Chemeris5e96c3d2013-07-04 17:33:33 +040089 return None
Ben Fox-Moore0ec14752018-09-24 15:47:02 +020090 imsi = swapped[1:]
Alexander Chemeris5e96c3d2013-07-04 17:33:33 +040091 return imsi
92
93def dec_iccid(ef):
94 return swap_nibbles(ef).strip('f')
Alexander Chemeris7be92ff2013-07-10 11:18:06 +040095
96def enc_iccid(iccid):
97 return swap_nibbles(rpad(iccid, 20))
98
99def enc_plmn(mcc, mnc):
Alexander Chemerisdddbf522017-07-18 16:49:59 +0300100 """Converts integer MCC/MNC into 3 bytes for EF"""
Alexander Chemeris7be92ff2013-07-10 11:18:06 +0400101 return swap_nibbles(lpad('%d' % mcc, 3) + lpad('%d' % mnc, 3))
Alexander Chemerisa5f0ea62017-07-18 16:48:47 +0300102
103def dec_spn(ef):
104 byte1 = int(ef[0:2])
105 hplmn_disp = (byte1&0x01 == 0x01)
106 oplmn_disp = (byte1&0x02 == 0x02)
107 name = h2s(ef[2:])
108 return (name, hplmn_disp, oplmn_disp)
109
110def enc_spn(name, hplmn_disp=False, oplmn_disp=False):
111 byte1 = 0x00
112 if hplmn_disp: byte1 = byte1|0x01
113 if oplmn_disp: byte1 = byte1|0x02
114 return i2h([byte1])+s2h(name)
Alexander Chemeris19fffa12018-01-11 13:06:43 +0900115
116def derive_milenage_opc(ki_hex, op_hex):
117 """
118 Run the milenage algorithm to calculate OPC from Ki and OP
119 """
120 from Crypto.Cipher import AES
121 from Crypto.Util.strxor import strxor
122 from pySim.utils import b2h
123
124 # We pass in hex string and now need to work on bytes
125 aes = AES.new(h2b(ki_hex))
126 opc_bytes = aes.encrypt(h2b(op_hex))
127 return b2h(strxor(opc_bytes, h2b(op_hex)))
128
129def calculate_luhn(cc):
130 """
131 Calculate Luhn checksum used in e.g. ICCID and IMEI
132 """
133 num = map(int, str(cc))
134 check_digit = 10 - sum(num[-2::-2] + [sum(divmod(d * 2, 10)) for d in num[::-2]]) % 10
135 return 0 if check_digit == 10 else check_digit