├── README
├── VERSION
├── input
├── milenage.py
└── sample
/README:
--------------------------------------------------------------------------------
1 | #
2 | # milenage.py
3 | # Program to generate GSM Authentication Triplets
4 | # Author: mmehra@juniper.net
5 | #
6 | # https://github.com/mmehra/milenage
7 | #
8 |
9 | This program can be used to generate GSM authentication triplets
10 | using milenage algorithm specified in 3GPP TS 55.205 v9.0.0. These
11 | authentication triplets can be used to test EAP-SIM with real UE
12 | and freeradius server.
13 |
14 | Softwares like gemalto, etc are available to provision (U)SIM with
15 | IMSI, Ki, Op values. However very few softwares are available to
16 | generate GSM auth triplets. AGSM (http://agsm.sourceforge.net) is
17 | available but it does not work with all card readers. This small
18 | utility does the job, generates SRES, Kc given Ki, Op and RAND.
19 |
20 |
21 | Directory Structure:
22 | milenage/
23 | |--> milenage.py Milenage algorithm implementation
24 | |--> input Sample input file
25 | |--> sample Sample test-sets from 55.205
26 | |--> VERSION Version
27 |
28 |
29 | Usage:
30 | The program expects an input file with Ki, Op and rand values.
31 | Please refer to sample input file for the format
32 | ./milenage
33 |
--------------------------------------------------------------------------------
/VERSION:
--------------------------------------------------------------------------------
1 | 0.1
2 |
--------------------------------------------------------------------------------
/input:
--------------------------------------------------------------------------------
1 | #
2 | # Input file for milenage.py. This program understands
3 | # input file in following format. Multiple keysets
4 | # can be specified
5 | #
6 | # ki=128 bit encryption key
7 | # op=128 bit operator specific constant
8 | # rand=128 bit random number
9 | #
10 | #
11 |
12 | ki=90dca4eda45b53cf0f12d7c9c3bc6a89
13 | op=3ffcfe5b7b1111589920d3528e84e655
14 | rand=9fddc72092c6ad036b6e464789315b78
15 |
16 | ki=5122250214c33e723a5dd523fc145fc0
17 | op=c9e8763286b5b9ffbdf56e1297d0887b
18 | rand=81e92b6c0ee0e12ebceba8d92a99dfa5
19 |
20 | ki=b73a90cbcf3afb622dba83c58a8415df
21 | op=b672047e003bb952dca6cb8af0e5b779
22 | rand=b120f1c1a0102a2f507dd543de68281f
23 |
24 |
--------------------------------------------------------------------------------
/milenage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | #
3 | # $Id$
4 | #
5 | # This program implements GSM milenage algorithm, specified
6 | # in 3GPP TS 55.205, for generating GSM auth triplets. This
7 | # function runs on HLR for providing auth info to SGSN/MME/
8 | # Radius server for UE/MS authentication.
9 | #
10 | # Author: mmehra@juniper.net
11 | #
12 |
13 | import sys
14 | import binascii
15 | from Crypto.Cipher import AES
16 | from itertools import izip
17 |
18 |
19 | #Our macro
20 | __XOR__ = lambda x, y: chr(ord(x) ^ ord(y))
21 |
22 |
23 | def LogicalXOR(str1, str2):
24 | '''Function to XOR two strings'''
25 | return ''.join(__XOR__(x, y) for (x,y) in izip(str1, str2))
26 |
27 |
28 | def AESEncrypt(key, buf):
29 | '''Encrypt buffer using AES-SHA1 128 algorithm
30 | @key: Key to be used for encryption
31 | @buf: Buffer to be encrypted'''
32 | encryptor = AES.new(key, AES.MODE_CBC)
33 | return encryptor.encrypt(buf)
34 |
35 |
36 | def GsmMilenageGenOpc(ki, op):
37 | '''Generate Opc using Ki and Op
38 | @ki: 128-bit subscriber key
39 | @op: 128-bit operator variant'''
40 | opc = AESEncrypt(ki, op)
41 | return LogicalXOR(opc, op)
42 |
43 |
44 | def GsmMilenageF2345(ki, opc, rand):
45 | '''Milenage f2, f3, f4, f5, f5* algorithms'''
46 | i = 0
47 | tmp1 = LogicalXOR(rand, opc)
48 | tmp2 = AESEncrypt(ki, tmp1)
49 | tmp1 = LogicalXOR(tmp2, opc)
50 | tmp1 = tmp1[:15] + chr(ord(tmp1[15]) ^ 1)
51 | tmp3 = AESEncrypt(ki, tmp1)
52 | tmp3 = LogicalXOR(tmp3, opc)
53 | res = tmp3[8:]
54 |
55 | #F3 - to calculate ck
56 | ck_map = {}
57 | for i in range(16):
58 | ck_map[(i+12)%16] = __XOR__(tmp2[i], opc[i])
59 | ck_map[15] = __XOR__(ck_map[15], chr(2))
60 | tmp1 = ''.join(val for val in ck_map.values())
61 | ck = AESEncrypt(ki, tmp1)
62 | ck = LogicalXOR(ck, opc)
63 |
64 | #F4 - to calculate ik
65 | ik_map = {}
66 | for i in range(16):
67 | ik_map[(i+8)%16] = __XOR__(tmp2[i], opc[i])
68 | ik_map[15] = __XOR__(ik_map[15], chr(4))
69 | tmp1 = ''.join(val for val in ik_map.values())
70 | ik = AESEncrypt(ki, tmp1)
71 | ik = LogicalXOR(ik, opc)
72 |
73 | return res, ck, ik
74 |
75 |
76 | def GsmMilenage(ki, opc, rand):
77 | '''Generate GSM-Milenage (3GPP TS 55.205) auth triplet
78 | @ki : 128-bit subscriber key
79 | @opc : 128-bit operator variant algorithm configuration
80 | @rand: 128-bit random challenge'''
81 |
82 | res, ck, ik = GsmMilenageF2345(ki, opc, rand)
83 |
84 | #Calculate sres
85 | sres_map = {}
86 | for idx in range(4):
87 | sres_map[idx] = __XOR__(res[idx], res[idx+4])
88 | sres = ''.join(val for val in sres_map.values())
89 |
90 | #Calculate kc
91 | kc_map = {}
92 | for idx in range(8):
93 | kc_map[idx] = __XOR__(__XOR__(ck[idx], ck[idx+8]),
94 | __XOR__(ik[idx], ik[idx+8]))
95 | kc = ''.join(val for val in kc_map.values())
96 |
97 | return sres, kc
98 |
99 |
100 | def GenerateAuthTriplets(keyset):
101 | ki = binascii.unhexlify(keyset['ki'])
102 | op = binascii.unhexlify(keyset['op'])
103 | rand = binascii.unhexlify(keyset['rand'])
104 |
105 | #Generate opc from ki and op
106 | opc = GsmMilenageGenOpc(ki, op)
107 |
108 | #Get sres, kc
109 | sres, kc = GsmMilenage(ki, opc, rand)
110 |
111 | #Store values now
112 | keyset['opc'] = binascii.hexlify(opc)
113 | keyset['kc'] = binascii.hexlify(kc)
114 | keyset['sres'] = binascii.hexlify(sres)
115 | return
116 |
117 |
118 | def ReadMilenageInput(filename):
119 | attribs = []
120 | keyset = {}
121 | try:
122 | fp = open(filename)
123 | except:
124 | print 'Error opening file %s'%(filename)
125 | sys.exit()
126 |
127 | for line in fp.readlines():
128 | if line.startswith('#'):
129 | continue
130 |
131 | if line.startswith('\n'):
132 | if len(keyset):
133 | attribs.append(keyset)
134 | keyset = {}
135 | continue
136 |
137 | key, value = line.split('=')
138 | keyset[key] = value.split('\n')[0]
139 |
140 | #Validate input
141 | if len(attribs) == 0:
142 | print 'Milenage: Please provide KI/OP/RAND in input file'
143 | sys.exit()
144 |
145 | for keyset in attribs:
146 | if not keyset.has_key('ki') or \
147 | not keyset.has_key('op') or \
148 | not keyset.has_key('rand'):
149 | print 'Milenage: KI or OP missing in keyset'
150 | sys.exit()
151 |
152 | return attribs
153 |
154 |
155 | def PrintMilenageOutput(attribs):
156 | '''Prints input read'''
157 | idx = 1
158 | for keyset in attribs:
159 | print 'Keyset # %d'%(idx)
160 | print ' %2s: %s'%('ki', keyset['ki'])
161 | print ' %2s: %s'%('op', keyset['op'])
162 | print ' Auth Triplets: '
163 | print ' %4s: %s'%('rand', keyset['rand'])
164 | print ' %4s: %s'%('sres', keyset['sres'])
165 | print ' %4s: %s'%('kc', keyset['kc'])
166 | print ''
167 | idx += 1
168 | return
169 |
170 |
171 | def main():
172 | '''The main function'''
173 | if len(sys.argv) < 2:
174 | print 'Milenage: Please provide input file'
175 | return
176 |
177 | #Read input
178 | attribs = ReadMilenageInput(sys.argv[1])
179 |
180 | #Generate auth triplets now
181 | for keyset in attribs:
182 | GenerateAuthTriplets(keyset)
183 |
184 | #Print output
185 | PrintMilenageOutput(attribs)
186 | return
187 |
188 |
189 | if __name__ == '__main__':
190 | main()
191 |
--------------------------------------------------------------------------------
/sample:
--------------------------------------------------------------------------------
1 | #
2 | # Sample test sets for testing milenage.py
3 | # These test sets are copied from 3GPP TS 55.205 v9.0
4 | #
5 | # The specification can be found @
6 | # http://www.3gpp.org/ftp/Specs/html-info/55205.htm
7 | #
8 |
9 | TEST SET # 11
10 | -------------
11 | Ki 77b45843c88e58c10d202684515ed430
12 | RAND 4c47eb3076dc55fe5106cb2034b8cd78
13 | OP bf3286c7a51409ce95724d503bfe6e70
14 | OPc d483afae562409a326b5bb0b20c4d762
15 | MIL3G-RES aefa357beac2a87a
16 | SRES#1 44389d01
17 | SRES#2 aefa357b
18 | MIL3G-CK 908c43f0569cb8f74bc971e706c36c5f
19 | MIL3G-IK c251df0d888dd9329bcf46655b226e40
20 | Kc 82dbab7f83f063da
21 |
22 |
23 | TEST SET # 12
24 | -------------
25 | Ki 729b17729270dd87ccdf1bfe29b4e9bb
26 | RAND 311c4c929744d675b720f3b7e9b1cbd0
27 | OP d04c9c35bd2262fa810d2924d036fd13
28 | OPc 228c2f2f06ac3268a9e616ee16db4ba1
29 | MIL3G-RES 98dbbd099b3b408d
30 | SRES#1 03e0fd84
31 | SRES#2 98dbbd09
32 | MIL3G-CK 44c0f23c5493cfd241e48f197e1d1012
33 | MIL3G-IK 0c9fb81613884c2535dd0eabf3b440d8
34 | Kc 3c66cb98cab2d33d
35 |
36 |
37 | TEST SET # 13
38 | -------------
39 | Ki d32dd23e89dc662354ca12eb79dd32fa
40 | RAND cf7d0ab1d94306950bf12018fbd46887
41 | OP fe75905b9da47d356236d0314e09c32e
42 | OPc d22a4b4180a5325708a5ff70d9f67ec7
43 | MIL3G-RES af4a411e1139f2c2
44 | SRES#1 be73b3dc
45 | SRES#2 af4a411e
46 | MIL3G-CK 5af86b80edb70df5292cc1121cbad50c
47 | MIL3G-IK 7f4d6ae7440e18789a8b75ad3f42f03a
48 | Kc 9612b5d88a4130bb
49 |
50 |
51 | TEST SET # 14
52 | -------------
53 | Ki af7c65e1927221de591187a2c5987a53
54 | RAND 1f0f8578464fd59b64bed2d09436b57a
55 | OP 0c7acb8d95b7d4a31c5aca6d26345a88
56 | OPc a4cf5c8155c08a7eff418e5443b98e55
57 | MIL3G-RES 7bffa5c2f41fbc05
58 | SRES#1 8fe019c7
59 | SRES#2 7bffa5c2
60 | MIL3G-CK 3f8c3f3ccf7625bf77fc94bcfd22fd26
61 | MIL3G-IK abcbae8fd46115e9961a55d0da5f2078
62 | Kc 75a150df3c6aed08
63 |
64 |
65 | TEST SET # 15
66 | -------------
67 | Ki 5bd7ecd3d3127a41d12539bed4e7cf71
68 | RAND 59b75f14251c75031d0bcbac1c2c04c7
69 | OP f967f76038b920a9cd25e10c08b49924
70 | OPc 76089d3c0ff3efdc6e36721d4fceb747
71 | MIL3G-RES 7e3f44c7591f6f45
72 | SRES#1 27202b82
73 | SRES#2 7e3f44c7
74 | MIL3G-CK d42b2d615e49a03ac275a5aef97af892
75 | MIL3G-IK 0b3f8d024fe6bfafaa982b8f82e319c2
76 | Kc b7f92e426a36fec5
77 |
78 |
79 | TEST SET # 16
80 | -------------
81 | Ki 6cd1c6ceb1e01e14f1b82316a90b7f3d
82 | RAND f69b78f300a0568bce9f0cb93c4be4c9
83 | OP 078bfca9564659ecd8851e84e6c59b48
84 | OPc a219dc37f1dc7d66738b5843c799f206
85 | MIL3G-RES 70f6bdb9ad21525f
86 | SRES#1 ddd7efe6
87 | SRES#2 70f6bdb9
88 | MIL3G-CK 6edaf99e5bd9f85d5f36d91c1272fb4b
89 | MIL3G-IK d61c853c280dd9c46f297baec386de17
90 | Kc 88d9de10a22004c5
91 |
92 |
93 | TEST SET # 17
94 | -------------
95 | Ki 5122250214c33e723a5dd523fc145fc0
96 | RAND 81e92b6c0ee0e12ebceba8d92a99dfa5
97 | OP c9e8763286b5b9ffbdf56e1297d0887b
98 | OPc 981d464c7c52eb6e5036234984ad0bcf
99 | MIL3G-RES 28d7b0f2a2ec3de5
100 | SRES#1 8a3b8d17
101 | SRES#2 28d7b0f2
102 | MIL3G-CK 5349fbe098649f948f5d2e973a81c00f
103 | MIL3G-IK 9744871ad32bf9bbd1dd5ce54e3e2e5a
104 | Kc 9a8d0e883ff0887a
105 |
106 |
107 | TEST SET # 18
108 | -------------
109 | Ki b73a90cbcf3afb622dba83c58a8415df
110 | RAND b120f1c1a0102a2f507dd543de68281f
111 | OP b672047e003bb952dca6cb8af0e5b779
112 | OPc df0c67868fa25f748b7044c6e7c245b8
113 | MIL3G-RES 479dd25c20792d63
114 | SRES#1 67e4ff3f
115 | SRES#2 479dd25c
116 | MIL3G-CK 66195dbed0313274c5ca7766615fa25e
117 | MIL3G-IK 66bec707eb2afc476d7408a8f2927b36
118 | Kc a819e577a8d6175b
119 |
120 |
121 | TEST-SET # 19
122 | -------------
123 | Ki 90dca4eda45b53cf0f12d7c9c3bc6a89
124 | RAND 9fddc72092c6ad036b6e464789315b78
125 | OP 3ffcfe5b7b1111589920d3528e84e655
126 | OPc cb9cccc4b9258e6dca4760379fb82581
127 | MIL3G-RES a95100e2760952cd
128 | SRES#1 df58522f
129 | SRES#2 a95100e2
130 | MIL3G-CK b5f2da03883b69f96bf52e029ed9ac45
131 | MIL3G-IK b4721368bc16ea67875c5598688bb0ef
132 | Kc ed29b2f1c27f9f34
133 |
--------------------------------------------------------------------------------