aboutsummaryrefslogtreecommitdiffstats
path: root/utils/osmo-aka-verify.c
blob: bbc65ef7fdba85193628f848a5100df975d082b8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <getopt.h>

#include <osmocom/core/utils.h>
#include <osmocom/core/bit64gen.h>

/* Utility program for implementing the SIM-side procedures of 3GPP Authentication and Key Agreement
 * as specified by 3GPP TS 33.102 Section 6.3.3
 *
 * (C) 2021 by Harald Welte <laforge@gnumonks.org>
 * Milenage library code used from libosmocore, which inherited it from wpa_supplicant
 */

/* FIXME: libosmogsm implements those, but doesn't declare them */
int milenage_f1(const uint8_t *opc, const uint8_t *k, const uint8_t *_rand,
		const uint8_t *sqn, const uint8_t *amf, uint8_t *mac_a, uint8_t *mac_s);
int milenage_f2345(const uint8_t *opc, const uint8_t *k, const uint8_t *_rand,
		   uint8_t *res, uint8_t *ck, uint8_t *ik, uint8_t *ak, uint8_t *akstar);
int milenage_opc_gen(uint8_t *opc, const uint8_t *k, const uint8_t *op);

static int milenage_check(const uint8_t *opc, const uint8_t *k, const uint8_t *sqn, const uint8_t *_rand,
			  const uint8_t *autn, uint8_t *ck, uint8_t *ik, uint8_t *res, size_t *res_len,
			  uint8_t *auts)
{
	int i;
	uint8_t xmac[8], ak[6], rx_sqn_bin[6];
	unsigned long long rx_sqn;
	const uint8_t *amf;

	printf("=== Static SIM parameters:\n");
	printf("Milenage SIM K: %s\n", osmo_hexdump_nospc(k, 16));
	printf("Milenage SIM OPc: %s\n", osmo_hexdump_nospc(opc, 16));
	printf("Milenage SIM SQN: %s\n", osmo_hexdump_nospc(sqn, 6));
	printf("\n");

	printf("=== Authentication Tuple as received from Network:\n");
	printf("Milenage Input RAND: %s\n", osmo_hexdump_nospc(_rand, 16));
	printf("Milenage Input AUTN: %s\n", osmo_hexdump_nospc(autn, 16));
	printf("\tAUTN(+)AK: %s\n", osmo_hexdump_nospc(autn, 6));
	printf("\tAMF: %s\n", osmo_hexdump_nospc(autn+6, 2));
	printf("\tMAC: %s\n", osmo_hexdump_nospc(autn+8, 8));
	printf("\n");

	if (milenage_f2345(opc, k, _rand, res, ck, ik, ak, NULL))
		return -1;

	*res_len = 8;
	printf("Milenage f2-Computed RES: %s\n", osmo_hexdump_nospc(res, *res_len));
	printf("Milenage f3-Computed CK: %s\n", osmo_hexdump_nospc(ck, 16));
	printf("Milenage f4-Computed IK: %s\n", osmo_hexdump_nospc(ik, 16));
	printf("Milenage f5-Computed AK: %s\n", osmo_hexdump_nospc(ak, 6));

	/* AUTN = (SQN ^ AK) || AMF || MAC */
	for (i = 0; i < 6; i++)
		rx_sqn_bin[i] = autn[i] ^ ak[i];
	rx_sqn = osmo_load64be_ext(rx_sqn_bin, 6);
	printf("Milenage Computed SQN: %s (%llu)\n", osmo_hexdump_nospc(rx_sqn_bin, 6), rx_sqn);

	if (memcmp(rx_sqn_bin, sqn, 6) <= 0) {
		printf("Milenage: RX-SQN differs from SIM SQN: Re-Sync!\n");
		uint8_t auts_amf[2] = { 0x00, 0x00 }; /* TS 33.102 v7.0.0, 6.3.3 */
		if (milenage_f2345(opc, k, _rand, NULL, NULL, NULL, NULL, ak))
			return -1;
		printf("Milenage Computed AK*: %s", osmo_hexdump_nospc(ak, 6));
		for (i = 0; i < 6; i++)
			auts[i] = sqn[i] ^ ak[i];
		if (milenage_f1(opc, k, _rand, sqn, auts_amf, NULL, auts + 6))
			return -1;
		printf("Milenage AUTS: %s\n", osmo_hexdump_nospc(auts, 14));
		return -2;
	}

	amf = autn + 6;
	if (milenage_f1(opc, k, _rand, rx_sqn_bin, amf, xmac, NULL))
		return -1;

	printf("Milenage f1-Computed XMAC: %s\n", osmo_hexdump_nospc(xmac, 8));

	if (memcmp(xmac, autn + 8, 8) != 0) {
		fprintf(stderr, "Milenage: MAC mismatch!\n");
		return -1;
	}

	return 0;
}


static void help()
{
	printf( "Static SIM card parameters:\n"
		"-k  --key\tSpecify Ki / K\n"
		"-o  --opc\tSpecify OPC\n"
		"-O  --op\tSpecify OP\n"
		"-f  --amf\tSpecify AMF\n"
		"-s  --sqn\tSpecify SQN\n"
	        "\n"
	        "Authentication Tuple by network:\n"
		//"-i  --ind\tSpecify IND slot for new SQN after AUTS\n"
		//"-l  --ind-len\tSpecify IND bit length (default=5)\n"
		"-r  --rand\tSpecify RAND random value\n"
		"-A  --autn\tSpecify AUTN authentication nonce\n"
	      );
}

static uint8_t g_k[16];
static uint8_t g_opc[16];
static uint8_t g_rand[16];
static uint8_t g_autn[16];
static uint8_t g_amf[16];
static unsigned long long g_sqn;


static int handle_options(int argc, char **argv)
{
	int rc, option_index;
	bool rand_is_set = false;
	bool autn_is_set = false;
	bool sqn_is_set = false;
	bool k_is_set = false;
	bool opc_is_set = false;
	bool amf_is_set = false;
	bool opc_is_op = false;
	int64_t val64;

	while (1) {
		int c;
		static struct option long_options[] = {
			{ "key", 1, 0, 'k' },
			{ "opc", 1, 0, 'o' },
			{ "op", 1, 0, 'O' },
			{ "amf", 1, 0, 'f' },
			{ "sqn", 1, 0, 's' },
			{ "rand", 1, 0, 'r' },
			{ "autn", 1, 0, 'A' },
			{ "help", 0, 0, 'h' },
			{ 0, 0, 0, 0 }
		};

		rc = 0;

		c = getopt_long(argc, argv, "k:o:O:f:s:r:A:h", long_options, &option_index);

		if (c == -1)
			break;

		switch (c) {
		case 'k':
			rc = osmo_hexparse(optarg, g_k, sizeof(g_k));
			k_is_set = true;
			break;
		case 'o':
			rc = osmo_hexparse(optarg, g_opc, sizeof(g_opc));
			opc_is_op = false;
			opc_is_set = true;
			break;
		case 'O':
			rc = osmo_hexparse(optarg, g_opc, sizeof(g_opc));
			opc_is_op = true;
			opc_is_set = true;
			break;
		case 'A':
			rc = osmo_hexparse(optarg, g_autn, sizeof(g_autn));
			autn_is_set = true;
			break;
		case 'f':
			rc = osmo_hexparse(optarg, g_amf, sizeof(g_amf));
			amf_is_set = true;
			break;
		case 's':
			rc = osmo_str_to_int64(&val64, optarg, 10, 0, INT64_MAX);
			g_sqn = (unsigned long long)val64;
			sqn_is_set = true;
			break;
		case 'r':
			rc = osmo_hexparse(optarg, g_rand, sizeof(g_rand));
			rand_is_set = true;
			break;
		case 'h':
			help();
			exit(0);
		default:
			help();
			exit(1);
		}

		if (rc < 0) {
			help();
			fprintf(stderr, "\nError parsing argument of option `%c'\n", c);
			exit(2);
		}
	}

	if (!k_is_set || !opc_is_set || !autn_is_set || !rand_is_set) {
		fprintf(stderr, "Error: K, OP[c], AUTN and RAND are mandatory arguments\n");
		fprintf(stderr, "\n");
		help();
		exit(2);
	}

	if (!sqn_is_set)
		printf("Warning: You may want to specify SQN\n");

	if (!amf_is_set)
		printf("Warning: You may want to specify AMF\n");

	if (opc_is_op) {
		uint8_t op[16];
		memcpy(op, g_opc, 16);
		rc = milenage_opc_gen(g_opc, g_k, op);
		OSMO_ASSERT(rc == 0);
	}

	return 0;
}



int main(int argc, char **argv)
{
	printf("osmo-aka-check (C) 2021 by Harald Welte\n");
	printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n");

	handle_options(argc, argv);

	printf("\n");

	uint8_t ck[16];
	uint8_t ik[16];
	uint8_t res[16];
	size_t res_len;
	uint8_t auts[14];
	uint8_t sqn_bin[6];
	int rc;

	osmo_store64be_ext(g_sqn, sqn_bin, 6);

	rc = milenage_check(g_opc, g_k, sqn_bin, g_rand, g_autn, ck, ik, res, &res_len, auts);

	if (rc < 0) {
		fprintf(stderr, "Authentication FAILED!\n");
		exit(1);
	} else {
		printf("Authentication SUCCEEDED\n");
		exit(0);
	}
}