]>
Commit | Line | Data |
---|---|---|
6fc6879b JM |
1 | /* |
2 | * 3GPP AKA - Milenage algorithm (3GPP TS 35.205, .206, .207, .208) | |
3 | * Copyright (c) 2006-2007 <j@w1.fi> | |
4 | * | |
0f3d578e JM |
5 | * This software may be distributed under the terms of the BSD license. |
6 | * See README for more details. | |
6fc6879b JM |
7 | * |
8 | * This file implements an example authentication algorithm defined for 3GPP | |
9 | * AKA. This can be used to implement a simple HLR/AuC into hlr_auc_gw to allow | |
10 | * EAP-AKA to be tested properly with real USIM cards. | |
11 | * | |
12 | * This implementations assumes that the r1..r5 and c1..c5 constants defined in | |
13 | * TS 35.206 are used, i.e., r1=64, r2=0, r3=32, r4=64, r5=96, c1=00..00, | |
14 | * c2=00..01, c3=00..02, c4=00..04, c5=00..08. The block cipher is assumed to | |
15 | * be AES (Rijndael). | |
16 | */ | |
17 | ||
18 | #include "includes.h" | |
19 | ||
20 | #include "common.h" | |
03da66bd | 21 | #include "crypto/aes_wrap.h" |
6fc6879b | 22 | #include "milenage.h" |
6fc6879b JM |
23 | |
24 | ||
25 | /** | |
26 | * milenage_f1 - Milenage f1 and f1* algorithms | |
27 | * @opc: OPc = 128-bit value derived from OP and K | |
28 | * @k: K = 128-bit subscriber key | |
29 | * @_rand: RAND = 128-bit random challenge | |
30 | * @sqn: SQN = 48-bit sequence number | |
31 | * @amf: AMF = 16-bit authentication management field | |
32 | * @mac_a: Buffer for MAC-A = 64-bit network authentication code, or %NULL | |
33 | * @mac_s: Buffer for MAC-S = 64-bit resync authentication code, or %NULL | |
34 | * Returns: 0 on success, -1 on failure | |
35 | */ | |
953f8343 JM |
36 | int milenage_f1(const u8 *opc, const u8 *k, const u8 *_rand, |
37 | const u8 *sqn, const u8 *amf, u8 *mac_a, u8 *mac_s) | |
6fc6879b JM |
38 | { |
39 | u8 tmp1[16], tmp2[16], tmp3[16]; | |
40 | int i; | |
41 | ||
42 | /* tmp1 = TEMP = E_K(RAND XOR OP_C) */ | |
43 | for (i = 0; i < 16; i++) | |
44 | tmp1[i] = _rand[i] ^ opc[i]; | |
45 | if (aes_128_encrypt_block(k, tmp1, tmp1)) | |
46 | return -1; | |
47 | ||
48 | /* tmp2 = IN1 = SQN || AMF || SQN || AMF */ | |
2a24bb31 JM |
49 | os_memcpy(tmp2, sqn, 6); |
50 | os_memcpy(tmp2 + 6, amf, 2); | |
51 | os_memcpy(tmp2 + 8, tmp2, 8); | |
6fc6879b JM |
52 | |
53 | /* OUT1 = E_K(TEMP XOR rot(IN1 XOR OP_C, r1) XOR c1) XOR OP_C */ | |
54 | ||
55 | /* rotate (tmp2 XOR OP_C) by r1 (= 0x40 = 8 bytes) */ | |
56 | for (i = 0; i < 16; i++) | |
57 | tmp3[(i + 8) % 16] = tmp2[i] ^ opc[i]; | |
58 | /* XOR with TEMP = E_K(RAND XOR OP_C) */ | |
59 | for (i = 0; i < 16; i++) | |
60 | tmp3[i] ^= tmp1[i]; | |
61 | /* XOR with c1 (= ..00, i.e., NOP) */ | |
62 | ||
63 | /* f1 || f1* = E_K(tmp3) XOR OP_c */ | |
64 | if (aes_128_encrypt_block(k, tmp3, tmp1)) | |
65 | return -1; | |
66 | for (i = 0; i < 16; i++) | |
67 | tmp1[i] ^= opc[i]; | |
68 | if (mac_a) | |
2a24bb31 | 69 | os_memcpy(mac_a, tmp1, 8); /* f1 */ |
6fc6879b | 70 | if (mac_s) |
2a24bb31 | 71 | os_memcpy(mac_s, tmp1 + 8, 8); /* f1* */ |
6fc6879b JM |
72 | return 0; |
73 | } | |
74 | ||
75 | ||
76 | /** | |
77 | * milenage_f2345 - Milenage f2, f3, f4, f5, f5* algorithms | |
78 | * @opc: OPc = 128-bit value derived from OP and K | |
79 | * @k: K = 128-bit subscriber key | |
80 | * @_rand: RAND = 128-bit random challenge | |
81 | * @res: Buffer for RES = 64-bit signed response (f2), or %NULL | |
82 | * @ck: Buffer for CK = 128-bit confidentiality key (f3), or %NULL | |
83 | * @ik: Buffer for IK = 128-bit integrity key (f4), or %NULL | |
84 | * @ak: Buffer for AK = 48-bit anonymity key (f5), or %NULL | |
85 | * @akstar: Buffer for AK = 48-bit anonymity key (f5*), or %NULL | |
86 | * Returns: 0 on success, -1 on failure | |
87 | */ | |
953f8343 JM |
88 | int milenage_f2345(const u8 *opc, const u8 *k, const u8 *_rand, |
89 | u8 *res, u8 *ck, u8 *ik, u8 *ak, u8 *akstar) | |
6fc6879b JM |
90 | { |
91 | u8 tmp1[16], tmp2[16], tmp3[16]; | |
92 | int i; | |
93 | ||
94 | /* tmp2 = TEMP = E_K(RAND XOR OP_C) */ | |
95 | for (i = 0; i < 16; i++) | |
96 | tmp1[i] = _rand[i] ^ opc[i]; | |
97 | if (aes_128_encrypt_block(k, tmp1, tmp2)) | |
98 | return -1; | |
99 | ||
100 | /* OUT2 = E_K(rot(TEMP XOR OP_C, r2) XOR c2) XOR OP_C */ | |
101 | /* OUT3 = E_K(rot(TEMP XOR OP_C, r3) XOR c3) XOR OP_C */ | |
102 | /* OUT4 = E_K(rot(TEMP XOR OP_C, r4) XOR c4) XOR OP_C */ | |
103 | /* OUT5 = E_K(rot(TEMP XOR OP_C, r5) XOR c5) XOR OP_C */ | |
104 | ||
105 | /* f2 and f5 */ | |
106 | /* rotate by r2 (= 0, i.e., NOP) */ | |
107 | for (i = 0; i < 16; i++) | |
108 | tmp1[i] = tmp2[i] ^ opc[i]; | |
109 | tmp1[15] ^= 1; /* XOR c2 (= ..01) */ | |
110 | /* f5 || f2 = E_K(tmp1) XOR OP_c */ | |
111 | if (aes_128_encrypt_block(k, tmp1, tmp3)) | |
112 | return -1; | |
113 | for (i = 0; i < 16; i++) | |
114 | tmp3[i] ^= opc[i]; | |
115 | if (res) | |
2a24bb31 | 116 | os_memcpy(res, tmp3 + 8, 8); /* f2 */ |
6fc6879b | 117 | if (ak) |
2a24bb31 | 118 | os_memcpy(ak, tmp3, 6); /* f5 */ |
6fc6879b JM |
119 | |
120 | /* f3 */ | |
121 | if (ck) { | |
122 | /* rotate by r3 = 0x20 = 4 bytes */ | |
123 | for (i = 0; i < 16; i++) | |
124 | tmp1[(i + 12) % 16] = tmp2[i] ^ opc[i]; | |
125 | tmp1[15] ^= 2; /* XOR c3 (= ..02) */ | |
126 | if (aes_128_encrypt_block(k, tmp1, ck)) | |
127 | return -1; | |
128 | for (i = 0; i < 16; i++) | |
129 | ck[i] ^= opc[i]; | |
130 | } | |
131 | ||
132 | /* f4 */ | |
133 | if (ik) { | |
134 | /* rotate by r4 = 0x40 = 8 bytes */ | |
135 | for (i = 0; i < 16; i++) | |
136 | tmp1[(i + 8) % 16] = tmp2[i] ^ opc[i]; | |
137 | tmp1[15] ^= 4; /* XOR c4 (= ..04) */ | |
138 | if (aes_128_encrypt_block(k, tmp1, ik)) | |
139 | return -1; | |
140 | for (i = 0; i < 16; i++) | |
141 | ik[i] ^= opc[i]; | |
142 | } | |
143 | ||
144 | /* f5* */ | |
145 | if (akstar) { | |
146 | /* rotate by r5 = 0x60 = 12 bytes */ | |
147 | for (i = 0; i < 16; i++) | |
148 | tmp1[(i + 4) % 16] = tmp2[i] ^ opc[i]; | |
149 | tmp1[15] ^= 8; /* XOR c5 (= ..08) */ | |
150 | if (aes_128_encrypt_block(k, tmp1, tmp1)) | |
151 | return -1; | |
152 | for (i = 0; i < 6; i++) | |
153 | akstar[i] = tmp1[i] ^ opc[i]; | |
154 | } | |
155 | ||
156 | return 0; | |
157 | } | |
158 | ||
159 | ||
160 | /** | |
161 | * milenage_generate - Generate AKA AUTN,IK,CK,RES | |
162 | * @opc: OPc = 128-bit operator variant algorithm configuration field (encr.) | |
163 | * @amf: AMF = 16-bit authentication management field | |
164 | * @k: K = 128-bit subscriber key | |
165 | * @sqn: SQN = 48-bit sequence number | |
166 | * @_rand: RAND = 128-bit random challenge | |
167 | * @autn: Buffer for AUTN = 128-bit authentication token | |
168 | * @ik: Buffer for IK = 128-bit integrity key (f4), or %NULL | |
169 | * @ck: Buffer for CK = 128-bit confidentiality key (f3), or %NULL | |
170 | * @res: Buffer for RES = 64-bit signed response (f2), or %NULL | |
171 | * @res_len: Max length for res; set to used length or 0 on failure | |
172 | */ | |
173 | void milenage_generate(const u8 *opc, const u8 *amf, const u8 *k, | |
174 | const u8 *sqn, const u8 *_rand, u8 *autn, u8 *ik, | |
175 | u8 *ck, u8 *res, size_t *res_len) | |
176 | { | |
177 | int i; | |
2a24bb31 | 178 | u8 mac_a[8], ak[6]; |
6fc6879b JM |
179 | |
180 | if (*res_len < 8) { | |
181 | *res_len = 0; | |
182 | return; | |
183 | } | |
184 | if (milenage_f1(opc, k, _rand, sqn, amf, mac_a, NULL) || | |
185 | milenage_f2345(opc, k, _rand, res, ck, ik, ak, NULL)) { | |
186 | *res_len = 0; | |
187 | return; | |
188 | } | |
189 | *res_len = 8; | |
190 | ||
191 | /* AUTN = (SQN ^ AK) || AMF || MAC */ | |
192 | for (i = 0; i < 6; i++) | |
193 | autn[i] = sqn[i] ^ ak[i]; | |
2a24bb31 JM |
194 | os_memcpy(autn + 6, amf, 2); |
195 | os_memcpy(autn + 8, mac_a, 8); | |
6fc6879b JM |
196 | } |
197 | ||
198 | ||
199 | /** | |
200 | * milenage_auts - Milenage AUTS validation | |
201 | * @opc: OPc = 128-bit operator variant algorithm configuration field (encr.) | |
202 | * @k: K = 128-bit subscriber key | |
203 | * @_rand: RAND = 128-bit random challenge | |
204 | * @auts: AUTS = 112-bit authentication token from client | |
205 | * @sqn: Buffer for SQN = 48-bit sequence number | |
206 | * Returns: 0 = success (sqn filled), -1 on failure | |
207 | */ | |
208 | int milenage_auts(const u8 *opc, const u8 *k, const u8 *_rand, const u8 *auts, | |
209 | u8 *sqn) | |
210 | { | |
211 | u8 amf[2] = { 0x00, 0x00 }; /* TS 33.102 v7.0.0, 6.3.3 */ | |
212 | u8 ak[6], mac_s[8]; | |
213 | int i; | |
214 | ||
215 | if (milenage_f2345(opc, k, _rand, NULL, NULL, NULL, NULL, ak)) | |
216 | return -1; | |
217 | for (i = 0; i < 6; i++) | |
218 | sqn[i] = auts[i] ^ ak[i]; | |
219 | if (milenage_f1(opc, k, _rand, sqn, amf, NULL, mac_s) || | |
a79aea53 | 220 | os_memcmp_const(mac_s, auts + 6, 8) != 0) |
6fc6879b JM |
221 | return -1; |
222 | return 0; | |
223 | } | |
224 | ||
225 | ||
226 | /** | |
227 | * gsm_milenage - Generate GSM-Milenage (3GPP TS 55.205) authentication triplet | |
228 | * @opc: OPc = 128-bit operator variant algorithm configuration field (encr.) | |
229 | * @k: K = 128-bit subscriber key | |
230 | * @_rand: RAND = 128-bit random challenge | |
231 | * @sres: Buffer for SRES = 32-bit SRES | |
232 | * @kc: Buffer for Kc = 64-bit Kc | |
233 | * Returns: 0 on success, -1 on failure | |
234 | */ | |
235 | int gsm_milenage(const u8 *opc, const u8 *k, const u8 *_rand, u8 *sres, u8 *kc) | |
236 | { | |
237 | u8 res[8], ck[16], ik[16]; | |
238 | int i; | |
239 | ||
240 | if (milenage_f2345(opc, k, _rand, res, ck, ik, NULL, NULL)) | |
241 | return -1; | |
242 | ||
243 | for (i = 0; i < 8; i++) | |
244 | kc[i] = ck[i] ^ ck[i + 8] ^ ik[i] ^ ik[i + 8]; | |
245 | ||
246 | #ifdef GSM_MILENAGE_ALT_SRES | |
2a24bb31 | 247 | os_memcpy(sres, res, 4); |
6fc6879b JM |
248 | #else /* GSM_MILENAGE_ALT_SRES */ |
249 | for (i = 0; i < 4; i++) | |
250 | sres[i] = res[i] ^ res[i + 4]; | |
251 | #endif /* GSM_MILENAGE_ALT_SRES */ | |
252 | return 0; | |
253 | } | |
254 | ||
255 | ||
2a24bb31 JM |
256 | /** |
257 | * milenage_generate - Generate AKA AUTN,IK,CK,RES | |
258 | * @opc: OPc = 128-bit operator variant algorithm configuration field (encr.) | |
259 | * @k: K = 128-bit subscriber key | |
260 | * @sqn: SQN = 48-bit sequence number | |
261 | * @_rand: RAND = 128-bit random challenge | |
262 | * @autn: AUTN = 128-bit authentication token | |
263 | * @ik: Buffer for IK = 128-bit integrity key (f4), or %NULL | |
264 | * @ck: Buffer for CK = 128-bit confidentiality key (f3), or %NULL | |
265 | * @res: Buffer for RES = 64-bit signed response (f2), or %NULL | |
266 | * @res_len: Variable that will be set to RES length | |
267 | * @auts: 112-bit buffer for AUTS | |
268 | * Returns: 0 on success, -1 on failure, or -2 on synchronization failure | |
269 | */ | |
270 | int milenage_check(const u8 *opc, const u8 *k, const u8 *sqn, const u8 *_rand, | |
271 | const u8 *autn, u8 *ik, u8 *ck, u8 *res, size_t *res_len, | |
272 | u8 *auts) | |
273 | { | |
274 | int i; | |
275 | u8 mac_a[8], ak[6], rx_sqn[6]; | |
276 | const u8 *amf; | |
277 | ||
278 | wpa_hexdump(MSG_DEBUG, "Milenage: AUTN", autn, 16); | |
279 | wpa_hexdump(MSG_DEBUG, "Milenage: RAND", _rand, 16); | |
280 | ||
281 | if (milenage_f2345(opc, k, _rand, res, ck, ik, ak, NULL)) | |
282 | return -1; | |
283 | ||
284 | *res_len = 8; | |
285 | wpa_hexdump_key(MSG_DEBUG, "Milenage: RES", res, *res_len); | |
f32fe71a JM |
286 | wpa_hexdump_key(MSG_DEBUG, "Milenage: CK", ck, 16); |
287 | wpa_hexdump_key(MSG_DEBUG, "Milenage: IK", ik, 16); | |
2a24bb31 JM |
288 | wpa_hexdump_key(MSG_DEBUG, "Milenage: AK", ak, 6); |
289 | ||
290 | /* AUTN = (SQN ^ AK) || AMF || MAC */ | |
291 | for (i = 0; i < 6; i++) | |
292 | rx_sqn[i] = autn[i] ^ ak[i]; | |
293 | wpa_hexdump(MSG_DEBUG, "Milenage: SQN", rx_sqn, 6); | |
294 | ||
295 | if (os_memcmp(rx_sqn, sqn, 6) <= 0) { | |
296 | u8 auts_amf[2] = { 0x00, 0x00 }; /* TS 33.102 v7.0.0, 6.3.3 */ | |
297 | if (milenage_f2345(opc, k, _rand, NULL, NULL, NULL, NULL, ak)) | |
298 | return -1; | |
299 | wpa_hexdump_key(MSG_DEBUG, "Milenage: AK*", ak, 6); | |
300 | for (i = 0; i < 6; i++) | |
301 | auts[i] = sqn[i] ^ ak[i]; | |
302 | if (milenage_f1(opc, k, _rand, sqn, auts_amf, NULL, auts + 6)) | |
303 | return -1; | |
304 | wpa_hexdump(MSG_DEBUG, "Milenage: AUTS", auts, 14); | |
305 | return -2; | |
306 | } | |
307 | ||
308 | amf = autn + 6; | |
309 | wpa_hexdump(MSG_DEBUG, "Milenage: AMF", amf, 2); | |
310 | if (milenage_f1(opc, k, _rand, rx_sqn, amf, mac_a, NULL)) | |
311 | return -1; | |
312 | ||
313 | wpa_hexdump(MSG_DEBUG, "Milenage: MAC_A", mac_a, 8); | |
314 | ||
a79aea53 | 315 | if (os_memcmp_const(mac_a, autn + 8, 8) != 0) { |
2a24bb31 JM |
316 | wpa_printf(MSG_DEBUG, "Milenage: MAC mismatch"); |
317 | wpa_hexdump(MSG_DEBUG, "Milenage: Received MAC_A", | |
318 | autn + 8, 8); | |
319 | return -1; | |
320 | } | |
321 | ||
322 | return 0; | |
323 | } |