blob: bbc65ef7fdba85193628f848a5100df975d082b8 [file] [log] [blame]
Harald Welte86021062021-03-19 13:19:18 +01001#include <stdint.h>
2#include <stdio.h>
3#include <stdlib.h>
4#include <stdbool.h>
5#include <getopt.h>
6
7#include <osmocom/core/utils.h>
8#include <osmocom/core/bit64gen.h>
9
10/* Utility program for implementing the SIM-side procedures of 3GPP Authentication and Key Agreement
11 * as specified by 3GPP TS 33.102 Section 6.3.3
12 *
13 * (C) 2021 by Harald Welte <laforge@gnumonks.org>
14 * Milenage library code used from libosmocore, which inherited it from wpa_supplicant
15 */
16
17/* FIXME: libosmogsm implements those, but doesn't declare them */
18int milenage_f1(const uint8_t *opc, const uint8_t *k, const uint8_t *_rand,
19 const uint8_t *sqn, const uint8_t *amf, uint8_t *mac_a, uint8_t *mac_s);
20int milenage_f2345(const uint8_t *opc, const uint8_t *k, const uint8_t *_rand,
21 uint8_t *res, uint8_t *ck, uint8_t *ik, uint8_t *ak, uint8_t *akstar);
Harald Welte9fb5d802021-03-22 11:53:00 +010022int milenage_opc_gen(uint8_t *opc, const uint8_t *k, const uint8_t *op);
Harald Welte86021062021-03-19 13:19:18 +010023
24static int milenage_check(const uint8_t *opc, const uint8_t *k, const uint8_t *sqn, const uint8_t *_rand,
Vadim Yanitskiyc3c914a2021-03-21 22:36:39 +010025 const uint8_t *autn, uint8_t *ck, uint8_t *ik, uint8_t *res, size_t *res_len,
Harald Welte86021062021-03-19 13:19:18 +010026 uint8_t *auts)
27{
28 int i;
29 uint8_t xmac[8], ak[6], rx_sqn_bin[6];
30 unsigned long long rx_sqn;
31 const uint8_t *amf;
32
33 printf("=== Static SIM parameters:\n");
34 printf("Milenage SIM K: %s\n", osmo_hexdump_nospc(k, 16));
35 printf("Milenage SIM OPc: %s\n", osmo_hexdump_nospc(opc, 16));
36 printf("Milenage SIM SQN: %s\n", osmo_hexdump_nospc(sqn, 6));
37 printf("\n");
38
39 printf("=== Authentication Tuple as received from Network:\n");
40 printf("Milenage Input RAND: %s\n", osmo_hexdump_nospc(_rand, 16));
41 printf("Milenage Input AUTN: %s\n", osmo_hexdump_nospc(autn, 16));
42 printf("\tAUTN(+)AK: %s\n", osmo_hexdump_nospc(autn, 6));
43 printf("\tAMF: %s\n", osmo_hexdump_nospc(autn+6, 2));
44 printf("\tMAC: %s\n", osmo_hexdump_nospc(autn+8, 8));
45 printf("\n");
46
47 if (milenage_f2345(opc, k, _rand, res, ck, ik, ak, NULL))
48 return -1;
49
50 *res_len = 8;
51 printf("Milenage f2-Computed RES: %s\n", osmo_hexdump_nospc(res, *res_len));
52 printf("Milenage f3-Computed CK: %s\n", osmo_hexdump_nospc(ck, 16));
53 printf("Milenage f4-Computed IK: %s\n", osmo_hexdump_nospc(ik, 16));
54 printf("Milenage f5-Computed AK: %s\n", osmo_hexdump_nospc(ak, 6));
55
56 /* AUTN = (SQN ^ AK) || AMF || MAC */
57 for (i = 0; i < 6; i++)
58 rx_sqn_bin[i] = autn[i] ^ ak[i];
59 rx_sqn = osmo_load64be_ext(rx_sqn_bin, 6);
60 printf("Milenage Computed SQN: %s (%llu)\n", osmo_hexdump_nospc(rx_sqn_bin, 6), rx_sqn);
61
62 if (memcmp(rx_sqn_bin, sqn, 6) <= 0) {
63 printf("Milenage: RX-SQN differs from SIM SQN: Re-Sync!\n");
64 uint8_t auts_amf[2] = { 0x00, 0x00 }; /* TS 33.102 v7.0.0, 6.3.3 */
65 if (milenage_f2345(opc, k, _rand, NULL, NULL, NULL, NULL, ak))
66 return -1;
67 printf("Milenage Computed AK*: %s", osmo_hexdump_nospc(ak, 6));
68 for (i = 0; i < 6; i++)
69 auts[i] = sqn[i] ^ ak[i];
70 if (milenage_f1(opc, k, _rand, sqn, auts_amf, NULL, auts + 6))
71 return -1;
72 printf("Milenage AUTS: %s\n", osmo_hexdump_nospc(auts, 14));
73 return -2;
74 }
75
76 amf = autn + 6;
77 if (milenage_f1(opc, k, _rand, rx_sqn_bin, amf, xmac, NULL))
78 return -1;
79
80 printf("Milenage f1-Computed XMAC: %s\n", osmo_hexdump_nospc(xmac, 8));
81
82 if (memcmp(xmac, autn + 8, 8) != 0) {
83 fprintf(stderr, "Milenage: MAC mismatch!\n");
84 return -1;
85 }
86
87 return 0;
88}
89
90
91static void help()
92{
93 printf( "Static SIM card parameters:\n"
94 "-k --key\tSpecify Ki / K\n"
95 "-o --opc\tSpecify OPC\n"
96 "-O --op\tSpecify OP\n"
97 "-f --amf\tSpecify AMF\n"
98 "-s --sqn\tSpecify SQN\n"
99 "\n"
100 "Authentication Tuple by network:\n"
101 //"-i --ind\tSpecify IND slot for new SQN after AUTS\n"
102 //"-l --ind-len\tSpecify IND bit length (default=5)\n"
103 "-r --rand\tSpecify RAND random value\n"
104 "-A --autn\tSpecify AUTN authentication nonce\n"
105 );
106}
107
108static uint8_t g_k[16];
109static uint8_t g_opc[16];
110static uint8_t g_rand[16];
111static uint8_t g_autn[16];
112static uint8_t g_amf[16];
113static unsigned long long g_sqn;
114
115
116static int handle_options(int argc, char **argv)
117{
118 int rc, option_index;
119 bool rand_is_set = false;
120 bool autn_is_set = false;
121 bool sqn_is_set = false;
122 bool k_is_set = false;
123 bool opc_is_set = false;
124 bool amf_is_set = false;
125 bool opc_is_op = false;
Neels Hofmeyr34907fe2021-09-05 19:50:34 +0200126 int64_t val64;
Harald Welte86021062021-03-19 13:19:18 +0100127
128 while (1) {
129 int c;
130 static struct option long_options[] = {
131 { "key", 1, 0, 'k' },
132 { "opc", 1, 0, 'o' },
133 { "op", 1, 0, 'O' },
134 { "amf", 1, 0, 'f' },
135 { "sqn", 1, 0, 's' },
136 { "rand", 1, 0, 'r' },
137 { "autn", 1, 0, 'A' },
138 { "help", 0, 0, 'h' },
139 { 0, 0, 0, 0 }
140 };
141
142 rc = 0;
143
144 c = getopt_long(argc, argv, "k:o:O:f:s:r:A:h", long_options, &option_index);
145
146 if (c == -1)
147 break;
148
149 switch (c) {
150 case 'k':
151 rc = osmo_hexparse(optarg, g_k, sizeof(g_k));
152 k_is_set = true;
153 break;
154 case 'o':
155 rc = osmo_hexparse(optarg, g_opc, sizeof(g_opc));
156 opc_is_op = false;
157 opc_is_set = true;
158 break;
159 case 'O':
160 rc = osmo_hexparse(optarg, g_opc, sizeof(g_opc));
161 opc_is_op = true;
162 opc_is_set = true;
163 break;
164 case 'A':
165 rc = osmo_hexparse(optarg, g_autn, sizeof(g_autn));
166 autn_is_set = true;
167 break;
168 case 'f':
169 rc = osmo_hexparse(optarg, g_amf, sizeof(g_amf));
170 amf_is_set = true;
171 break;
172 case 's':
Neels Hofmeyr34907fe2021-09-05 19:50:34 +0200173 rc = osmo_str_to_int64(&val64, optarg, 10, 0, INT64_MAX);
174 g_sqn = (unsigned long long)val64;
Harald Welte86021062021-03-19 13:19:18 +0100175 sqn_is_set = true;
176 break;
177 case 'r':
178 rc = osmo_hexparse(optarg, g_rand, sizeof(g_rand));
179 rand_is_set = true;
180 break;
181 case 'h':
182 help();
183 exit(0);
184 default:
185 help();
186 exit(1);
187 }
188
189 if (rc < 0) {
190 help();
191 fprintf(stderr, "\nError parsing argument of option `%c'\n", c);
192 exit(2);
193 }
194 }
195
196 if (!k_is_set || !opc_is_set || !autn_is_set || !rand_is_set) {
197 fprintf(stderr, "Error: K, OP[c], AUTN and RAND are mandatory arguments\n");
198 fprintf(stderr, "\n");
199 help();
200 exit(2);
201 }
202
203 if (!sqn_is_set)
204 printf("Warning: You may want to specify SQN\n");
205
206 if (!amf_is_set)
207 printf("Warning: You may want to specify AMF\n");
208
209 if (opc_is_op) {
Harald Welte9fb5d802021-03-22 11:53:00 +0100210 uint8_t op[16];
211 memcpy(op, g_opc, 16);
212 rc = milenage_opc_gen(g_opc, g_k, op);
213 OSMO_ASSERT(rc == 0);
Harald Welte86021062021-03-19 13:19:18 +0100214 }
215
216 return 0;
217}
218
219
220
221int main(int argc, char **argv)
222{
223 printf("osmo-aka-check (C) 2021 by Harald Welte\n");
224 printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n");
225
226 handle_options(argc, argv);
227
228 printf("\n");
229
230 uint8_t ck[16];
231 uint8_t ik[16];
232 uint8_t res[16];
233 size_t res_len;
234 uint8_t auts[14];
235 uint8_t sqn_bin[6];
236 int rc;
237
238 osmo_store64be_ext(g_sqn, sqn_bin, 6);
239
240 rc = milenage_check(g_opc, g_k, sqn_bin, g_rand, g_autn, ck, ik, res, &res_len, auts);
241
242 if (rc < 0) {
243 fprintf(stderr, "Authentication FAILED!\n");
244 exit(1);
245 } else {
246 printf("Authentication SUCCEEDED\n");
247 exit(0);
248 }
249}