1 /* 2 * hostapd / EAP-AKA (RFC 4187) and EAP-AKA' (draft-arkko-eap-aka-kdf) 3 * Copyright (c) 2005-2008, Jouni Malinen <j (at) w1.fi> 4 * 5 * This software may be distributed under the terms of the BSD license. 6 * See README for more details. 7 */ 8 9 #include "includes.h" 10 11 #include "common.h" 12 #include "crypto/sha256.h" 13 #include "crypto/crypto.h" 14 #include "crypto/random.h" 15 #include "eap_common/eap_sim_common.h" 16 #include "eap_server/eap_i.h" 17 #include "eap_server/eap_sim_db.h" 18 19 20 struct eap_aka_data { 21 u8 mk[EAP_SIM_MK_LEN]; 22 u8 nonce_s[EAP_SIM_NONCE_S_LEN]; 23 u8 k_aut[EAP_AKA_PRIME_K_AUT_LEN]; 24 u8 k_encr[EAP_SIM_K_ENCR_LEN]; 25 u8 k_re[EAP_AKA_PRIME_K_RE_LEN]; /* EAP-AKA' only */ 26 u8 msk[EAP_SIM_KEYING_DATA_LEN]; 27 u8 emsk[EAP_EMSK_LEN]; 28 u8 rand[EAP_AKA_RAND_LEN]; 29 u8 autn[EAP_AKA_AUTN_LEN]; 30 u8 ck[EAP_AKA_CK_LEN]; 31 u8 ik[EAP_AKA_IK_LEN]; 32 u8 res[EAP_AKA_RES_MAX_LEN]; 33 size_t res_len; 34 enum { 35 IDENTITY, CHALLENGE, REAUTH, NOTIFICATION, SUCCESS, FAILURE 36 } state; 37 char *next_pseudonym; 38 char *next_reauth_id; 39 u16 counter; 40 struct eap_sim_reauth *reauth; 41 int auts_reported; /* whether the current AUTS has been reported to the 42 * eap_sim_db */ 43 u16 notification; 44 int use_result_ind; 45 46 struct wpabuf *id_msgs; 47 int pending_id; 48 u8 eap_method; 49 u8 *network_name; 50 size_t network_name_len; 51 u16 kdf; 52 }; 53 54 55 static void eap_aka_determine_identity(struct eap_sm *sm, 56 struct eap_aka_data *data, 57 int before_identity, int after_reauth); 58 59 60 static const char * eap_aka_state_txt(int state) 61 { 62 switch (state) { 63 case IDENTITY: 64 return "IDENTITY"; 65 case CHALLENGE: 66 return "CHALLENGE"; 67 case REAUTH: 68 return "REAUTH"; 69 case SUCCESS: 70 return "SUCCESS"; 71 case FAILURE: 72 return "FAILURE"; 73 case NOTIFICATION: 74 return "NOTIFICATION"; 75 default: 76 return "Unknown?!"; 77 } 78 } 79 80 81 static void eap_aka_state(struct eap_aka_data *data, int state) 82 { 83 wpa_printf(MSG_DEBUG, "EAP-AKA: %s -> %s", 84 eap_aka_state_txt(data->state), 85 eap_aka_state_txt(state)); 86 data->state = state; 87 } 88 89 90 static void * eap_aka_init(struct eap_sm *sm) 91 { 92 struct eap_aka_data *data; 93 94 if (sm->eap_sim_db_priv == NULL) { 95 wpa_printf(MSG_WARNING, "EAP-AKA: eap_sim_db not configured"); 96 return NULL; 97 } 98 99 data = os_zalloc(sizeof(*data)); 100 if (data == NULL) 101 return NULL; 102 103 data->eap_method = EAP_TYPE_AKA; 104 105 data->state = IDENTITY; 106 eap_aka_determine_identity(sm, data, 1, 0); 107 data->pending_id = -1; 108 109 return data; 110 } 111 112 113 #ifdef EAP_SERVER_AKA_PRIME 114 static void * eap_aka_prime_init(struct eap_sm *sm) 115 { 116 struct eap_aka_data *data; 117 /* TODO: make ANID configurable; see 3GPP TS 24.302 */ 118 char *network_name = "WLAN"; 119 120 if (sm->eap_sim_db_priv == NULL) { 121 wpa_printf(MSG_WARNING, "EAP-AKA: eap_sim_db not configured"); 122 return NULL; 123 } 124 125 data = os_zalloc(sizeof(*data)); 126 if (data == NULL) 127 return NULL; 128 129 data->eap_method = EAP_TYPE_AKA_PRIME; 130 data->network_name = (u8 *) os_strdup(network_name); 131 if (data->network_name == NULL) { 132 os_free(data); 133 return NULL; 134 } 135 136 data->network_name_len = os_strlen(network_name); 137 138 data->state = IDENTITY; 139 eap_aka_determine_identity(sm, data, 1, 0); 140 data->pending_id = -1; 141 142 return data; 143 } 144 #endif /* EAP_SERVER_AKA_PRIME */ 145 146 147 static void eap_aka_reset(struct eap_sm *sm, void *priv) 148 { 149 struct eap_aka_data *data = priv; 150 os_free(data->next_pseudonym); 151 os_free(data->next_reauth_id); 152 wpabuf_free(data->id_msgs); 153 os_free(data->network_name); 154 os_free(data); 155 } 156 157 158 static int eap_aka_add_id_msg(struct eap_aka_data *data, 159 const struct wpabuf *msg) 160 { 161 if (msg == NULL) 162 return -1; 163 164 if (data->id_msgs == NULL) { 165 data->id_msgs = wpabuf_dup(msg); 166 return data->id_msgs == NULL ? -1 : 0; 167 } 168 169 if (wpabuf_resize(&data->id_msgs, wpabuf_len(msg)) < 0) 170 return -1; 171 wpabuf_put_buf(data->id_msgs, msg); 172 173 return 0; 174 } 175 176 177 static void eap_aka_add_checkcode(struct eap_aka_data *data, 178 struct eap_sim_msg *msg) 179 { 180 const u8 *addr; 181 size_t len; 182 u8 hash[SHA256_MAC_LEN]; 183 184 wpa_printf(MSG_DEBUG, " AT_CHECKCODE"); 185 186 if (data->id_msgs == NULL) { 187 /* 188 * No EAP-AKA/Identity packets were exchanged - send empty 189 * checkcode. 190 */ 191 eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, NULL, 0); 192 return; 193 } 194 195 /* Checkcode is SHA1 hash over all EAP-AKA/Identity packets. */ 196 addr = wpabuf_head(data->id_msgs); 197 len = wpabuf_len(data->id_msgs); 198 wpa_hexdump(MSG_MSGDUMP, "EAP-AKA: AT_CHECKCODE data", addr, len); 199 if (data->eap_method == EAP_TYPE_AKA_PRIME) 200 sha256_vector(1, &addr, &len, hash); 201 else 202 sha1_vector(1, &addr, &len, hash); 203 204 eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, hash, 205 data->eap_method == EAP_TYPE_AKA_PRIME ? 206 EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN); 207 } 208 209 210 static int eap_aka_verify_checkcode(struct eap_aka_data *data, 211 const u8 *checkcode, size_t checkcode_len) 212 { 213 const u8 *addr; 214 size_t len; 215 u8 hash[SHA256_MAC_LEN]; 216 size_t hash_len; 217 218 if (checkcode == NULL) 219 return -1; 220 221 if (data->id_msgs == NULL) { 222 if (checkcode_len != 0) { 223 wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from peer " 224 "indicates that AKA/Identity messages were " 225 "used, but they were not"); 226 return -1; 227 } 228 return 0; 229 } 230 231 hash_len = data->eap_method == EAP_TYPE_AKA_PRIME ? 232 EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN; 233 234 if (checkcode_len != hash_len) { 235 wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from peer indicates " 236 "that AKA/Identity message were not used, but they " 237 "were"); 238 return -1; 239 } 240 241 /* Checkcode is SHA1 hash over all EAP-AKA/Identity packets. */ 242 addr = wpabuf_head(data->id_msgs); 243 len = wpabuf_len(data->id_msgs); 244 if (data->eap_method == EAP_TYPE_AKA_PRIME) 245 sha256_vector(1, &addr, &len, hash); 246 else 247 sha1_vector(1, &addr, &len, hash); 248 249 if (os_memcmp(hash, checkcode, hash_len) != 0) { 250 wpa_printf(MSG_DEBUG, "EAP-AKA: Mismatch in AT_CHECKCODE"); 251 return -1; 252 } 253 254 return 0; 255 } 256 257 258 static struct wpabuf * eap_aka_build_identity(struct eap_sm *sm, 259 struct eap_aka_data *data, u8 id) 260 { 261 struct eap_sim_msg *msg; 262 struct wpabuf *buf; 263 264 wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Identity"); 265 msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method, 266 EAP_AKA_SUBTYPE_IDENTITY); 267 if (eap_sim_db_identity_known(sm->eap_sim_db_priv, sm->identity, 268 sm->identity_len)) { 269 wpa_printf(MSG_DEBUG, " AT_PERMANENT_ID_REQ"); 270 eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0, NULL, 0); 271 } else { 272 /* 273 * RFC 4187, Chap. 4.1.4 recommends that identity from EAP is 274 * ignored and the AKA/Identity is used to request the 275 * identity. 276 */ 277 wpa_printf(MSG_DEBUG, " AT_ANY_ID_REQ"); 278 eap_sim_msg_add(msg, EAP_SIM_AT_ANY_ID_REQ, 0, NULL, 0); 279 } 280 buf = eap_sim_msg_finish(msg, NULL, NULL, 0); 281 if (eap_aka_add_id_msg(data, buf) < 0) { 282 wpabuf_free(buf); 283 return NULL; 284 } 285 data->pending_id = id; 286 return buf; 287 } 288 289 290 static int eap_aka_build_encr(struct eap_sm *sm, struct eap_aka_data *data, 291 struct eap_sim_msg *msg, u16 counter, 292 const u8 *nonce_s) 293 { 294 os_free(data->next_pseudonym); 295 data->next_pseudonym = 296 eap_sim_db_get_next_pseudonym(sm->eap_sim_db_priv, 1); 297 os_free(data->next_reauth_id); 298 if (data->counter <= EAP_AKA_MAX_FAST_REAUTHS) { 299 data->next_reauth_id = 300 eap_sim_db_get_next_reauth_id(sm->eap_sim_db_priv, 1); 301 } else { 302 wpa_printf(MSG_DEBUG, "EAP-AKA: Max fast re-authentication " 303 "count exceeded - force full authentication"); 304 data->next_reauth_id = NULL; 305 } 306 307 if (data->next_pseudonym == NULL && data->next_reauth_id == NULL && 308 counter == 0 && nonce_s == NULL) 309 return 0; 310 311 wpa_printf(MSG_DEBUG, " AT_IV"); 312 wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); 313 eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA); 314 315 if (counter > 0) { 316 wpa_printf(MSG_DEBUG, " *AT_COUNTER (%u)", counter); 317 eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0); 318 } 319 320 if (nonce_s) { 321 wpa_printf(MSG_DEBUG, " *AT_NONCE_S"); 322 eap_sim_msg_add(msg, EAP_SIM_AT_NONCE_S, 0, nonce_s, 323 EAP_SIM_NONCE_S_LEN); 324 } 325 326 if (data->next_pseudonym) { 327 wpa_printf(MSG_DEBUG, " *AT_NEXT_PSEUDONYM (%s)", 328 data->next_pseudonym); 329 eap_sim_msg_add(msg, EAP_SIM_AT_NEXT_PSEUDONYM, 330 os_strlen(data->next_pseudonym), 331 (u8 *) data->next_pseudonym, 332 os_strlen(data->next_pseudonym)); 333 } 334 335 if (data->next_reauth_id) { 336 wpa_printf(MSG_DEBUG, " *AT_NEXT_REAUTH_ID (%s)", 337 data->next_reauth_id); 338 eap_sim_msg_add(msg, EAP_SIM_AT_NEXT_REAUTH_ID, 339 os_strlen(data->next_reauth_id), 340 (u8 *) data->next_reauth_id, 341 os_strlen(data->next_reauth_id)); 342 } 343 344 if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) { 345 wpa_printf(MSG_WARNING, "EAP-AKA: Failed to encrypt " 346 "AT_ENCR_DATA"); 347 return -1; 348 } 349 350 return 0; 351 } 352 353 354 static struct wpabuf * eap_aka_build_challenge(struct eap_sm *sm, 355 struct eap_aka_data *data, 356 u8 id) 357 { 358 struct eap_sim_msg *msg; 359 360 wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Challenge"); 361 msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method, 362 EAP_AKA_SUBTYPE_CHALLENGE); 363 wpa_printf(MSG_DEBUG, " AT_RAND"); 364 eap_sim_msg_add(msg, EAP_SIM_AT_RAND, 0, data->rand, EAP_AKA_RAND_LEN); 365 wpa_printf(MSG_DEBUG, " AT_AUTN"); 366 eap_sim_msg_add(msg, EAP_SIM_AT_AUTN, 0, data->autn, EAP_AKA_AUTN_LEN); 367 if (data->eap_method == EAP_TYPE_AKA_PRIME) { 368 if (data->kdf) { 369 /* Add the selected KDF into the beginning */ 370 wpa_printf(MSG_DEBUG, " AT_KDF"); 371 eap_sim_msg_add(msg, EAP_SIM_AT_KDF, data->kdf, 372 NULL, 0); 373 } 374 wpa_printf(MSG_DEBUG, " AT_KDF"); 375 eap_sim_msg_add(msg, EAP_SIM_AT_KDF, EAP_AKA_PRIME_KDF, 376 NULL, 0); 377 wpa_printf(MSG_DEBUG, " AT_KDF_INPUT"); 378 eap_sim_msg_add(msg, EAP_SIM_AT_KDF_INPUT, 379 data->network_name_len, 380 data->network_name, data->network_name_len); 381 } 382 383 if (eap_aka_build_encr(sm, data, msg, 0, NULL)) { 384 eap_sim_msg_free(msg); 385 return NULL; 386 } 387 388 eap_aka_add_checkcode(data, msg); 389 390 if (sm->eap_sim_aka_result_ind) { 391 wpa_printf(MSG_DEBUG, " AT_RESULT_IND"); 392 eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0); 393 } 394 395 #ifdef EAP_SERVER_AKA_PRIME 396 if (data->eap_method == EAP_TYPE_AKA) { 397 u16 flags = 0; 398 int i; 399 int aka_prime_preferred = 0; 400 401 i = 0; 402 while (sm->user && i < EAP_MAX_METHODS && 403 (sm->user->methods[i].vendor != EAP_VENDOR_IETF || 404 sm->user->methods[i].method != EAP_TYPE_NONE)) { 405 if (sm->user->methods[i].vendor == EAP_VENDOR_IETF) { 406 if (sm->user->methods[i].method == 407 EAP_TYPE_AKA) 408 break; 409 if (sm->user->methods[i].method == 410 EAP_TYPE_AKA_PRIME) { 411 aka_prime_preferred = 1; 412 break; 413 } 414 } 415 i++; 416 } 417 418 if (aka_prime_preferred) 419 flags |= EAP_AKA_BIDDING_FLAG_D; 420 eap_sim_msg_add(msg, EAP_SIM_AT_BIDDING, flags, NULL, 0); 421 } 422 #endif /* EAP_SERVER_AKA_PRIME */ 423 424 wpa_printf(MSG_DEBUG, " AT_MAC"); 425 eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); 426 return eap_sim_msg_finish(msg, data->k_aut, NULL, 0); 427 } 428 429 430 static struct wpabuf * eap_aka_build_reauth(struct eap_sm *sm, 431 struct eap_aka_data *data, u8 id) 432 { 433 struct eap_sim_msg *msg; 434 435 wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Re-authentication"); 436 437 if (random_get_bytes(data->nonce_s, EAP_SIM_NONCE_S_LEN)) 438 return NULL; 439 wpa_hexdump_key(MSG_MSGDUMP, "EAP-AKA: NONCE_S", 440 data->nonce_s, EAP_SIM_NONCE_S_LEN); 441 442 if (data->eap_method == EAP_TYPE_AKA_PRIME) { 443 eap_aka_prime_derive_keys_reauth(data->k_re, data->counter, 444 sm->identity, 445 sm->identity_len, 446 data->nonce_s, 447 data->msk, data->emsk); 448 } else { 449 eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, 450 data->msk, data->emsk); 451 eap_sim_derive_keys_reauth(data->counter, sm->identity, 452 sm->identity_len, data->nonce_s, 453 data->mk, data->msk, data->emsk); 454 } 455 456 msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method, 457 EAP_AKA_SUBTYPE_REAUTHENTICATION); 458 459 if (eap_aka_build_encr(sm, data, msg, data->counter, data->nonce_s)) { 460 eap_sim_msg_free(msg); 461 return NULL; 462 } 463 464 eap_aka_add_checkcode(data, msg); 465 466 if (sm->eap_sim_aka_result_ind) { 467 wpa_printf(MSG_DEBUG, " AT_RESULT_IND"); 468 eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0); 469 } 470 471 wpa_printf(MSG_DEBUG, " AT_MAC"); 472 eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); 473 return eap_sim_msg_finish(msg, data->k_aut, NULL, 0); 474 } 475 476 477 static struct wpabuf * eap_aka_build_notification(struct eap_sm *sm, 478 struct eap_aka_data *data, 479 u8 id) 480 { 481 struct eap_sim_msg *msg; 482 483 wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Notification"); 484 msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method, 485 EAP_AKA_SUBTYPE_NOTIFICATION); 486 wpa_printf(MSG_DEBUG, " AT_NOTIFICATION (%d)", data->notification); 487 eap_sim_msg_add(msg, EAP_SIM_AT_NOTIFICATION, data->notification, 488 NULL, 0); 489 if (data->use_result_ind) { 490 if (data->reauth) { 491 wpa_printf(MSG_DEBUG, " AT_IV"); 492 wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); 493 eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, 494 EAP_SIM_AT_ENCR_DATA); 495 wpa_printf(MSG_DEBUG, " *AT_COUNTER (%u)", 496 data->counter); 497 eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, data->counter, 498 NULL, 0); 499 500 if (eap_sim_msg_add_encr_end(msg, data->k_encr, 501 EAP_SIM_AT_PADDING)) { 502 wpa_printf(MSG_WARNING, "EAP-AKA: Failed to " 503 "encrypt AT_ENCR_DATA"); 504 eap_sim_msg_free(msg); 505 return NULL; 506 } 507 } 508 509 wpa_printf(MSG_DEBUG, " AT_MAC"); 510 eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); 511 } 512 return eap_sim_msg_finish(msg, data->k_aut, NULL, 0); 513 } 514 515 516 static struct wpabuf * eap_aka_buildReq(struct eap_sm *sm, void *priv, u8 id) 517 { 518 struct eap_aka_data *data = priv; 519 520 data->auts_reported = 0; 521 switch (data->state) { 522 case IDENTITY: 523 return eap_aka_build_identity(sm, data, id); 524 case CHALLENGE: 525 return eap_aka_build_challenge(sm, data, id); 526 case REAUTH: 527 return eap_aka_build_reauth(sm, data, id); 528 case NOTIFICATION: 529 return eap_aka_build_notification(sm, data, id); 530 default: 531 wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown state %d in " 532 "buildReq", data->state); 533 break; 534 } 535 return NULL; 536 } 537 538 539 static Boolean eap_aka_check(struct eap_sm *sm, void *priv, 540 struct wpabuf *respData) 541 { 542 struct eap_aka_data *data = priv; 543 const u8 *pos; 544 size_t len; 545 546 pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_method, respData, 547 &len); 548 if (pos == NULL || len < 3) { 549 wpa_printf(MSG_INFO, "EAP-AKA: Invalid frame"); 550 return TRUE; 551 } 552 553 return FALSE; 554 } 555 556 557 static Boolean eap_aka_subtype_ok(struct eap_aka_data *data, u8 subtype) 558 { 559 if (subtype == EAP_AKA_SUBTYPE_CLIENT_ERROR || 560 subtype == EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT) 561 return FALSE; 562 563 switch (data->state) { 564 case IDENTITY: 565 if (subtype != EAP_AKA_SUBTYPE_IDENTITY) { 566 wpa_printf(MSG_INFO, "EAP-AKA: Unexpected response " 567 "subtype %d", subtype); 568 return TRUE; 569 } 570 break; 571 case CHALLENGE: 572 if (subtype != EAP_AKA_SUBTYPE_CHALLENGE && 573 subtype != EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE) { 574 wpa_printf(MSG_INFO, "EAP-AKA: Unexpected response " 575 "subtype %d", subtype); 576 return TRUE; 577 } 578 break; 579 case REAUTH: 580 if (subtype != EAP_AKA_SUBTYPE_REAUTHENTICATION) { 581 wpa_printf(MSG_INFO, "EAP-AKA: Unexpected response " 582 "subtype %d", subtype); 583 return TRUE; 584 } 585 break; 586 case NOTIFICATION: 587 if (subtype != EAP_AKA_SUBTYPE_NOTIFICATION) { 588 wpa_printf(MSG_INFO, "EAP-AKA: Unexpected response " 589 "subtype %d", subtype); 590 return TRUE; 591 } 592 break; 593 default: 594 wpa_printf(MSG_INFO, "EAP-AKA: Unexpected state (%d) for " 595 "processing a response", data->state); 596 return TRUE; 597 } 598 599 return FALSE; 600 } 601 602 603 static void eap_aka_determine_identity(struct eap_sm *sm, 604 struct eap_aka_data *data, 605 int before_identity, int after_reauth) 606 { 607 const u8 *identity; 608 size_t identity_len; 609 int res; 610 611 identity = NULL; 612 identity_len = 0; 613 614 if (after_reauth && data->reauth) { 615 identity = data->reauth->identity; 616 identity_len = data->reauth->identity_len; 617 } else if (sm->identity && sm->identity_len > 0 && 618 sm->identity[0] == EAP_AKA_PERMANENT_PREFIX) { 619 identity = sm->identity; 620 identity_len = sm->identity_len; 621 } else { 622 identity = eap_sim_db_get_permanent(sm->eap_sim_db_priv, 623 sm->identity, 624 sm->identity_len, 625 &identity_len); 626 if (identity == NULL) { 627 data->reauth = eap_sim_db_get_reauth_entry( 628 sm->eap_sim_db_priv, sm->identity, 629 sm->identity_len); 630 if (data->reauth && 631 data->reauth->aka_prime != 632 (data->eap_method == EAP_TYPE_AKA_PRIME)) { 633 wpa_printf(MSG_DEBUG, "EAP-AKA: Reauth data " 634 "was for different AKA version"); 635 data->reauth = NULL; 636 } 637 if (data->reauth) { 638 wpa_printf(MSG_DEBUG, "EAP-AKA: Using fast " 639 "re-authentication"); 640 identity = data->reauth->identity; 641 identity_len = data->reauth->identity_len; 642 data->counter = data->reauth->counter; 643 if (data->eap_method == EAP_TYPE_AKA_PRIME) { 644 os_memcpy(data->k_encr, 645 data->reauth->k_encr, 646 EAP_SIM_K_ENCR_LEN); 647 os_memcpy(data->k_aut, 648 data->reauth->k_aut, 649 EAP_AKA_PRIME_K_AUT_LEN); 650 os_memcpy(data->k_re, 651 data->reauth->k_re, 652 EAP_AKA_PRIME_K_RE_LEN); 653 } else { 654 os_memcpy(data->mk, data->reauth->mk, 655 EAP_SIM_MK_LEN); 656 } 657 } 658 } 659 } 660 661 if (identity == NULL || 662 eap_sim_db_identity_known(sm->eap_sim_db_priv, sm->identity, 663 sm->identity_len) < 0) { 664 if (before_identity) { 665 wpa_printf(MSG_DEBUG, "EAP-AKA: Permanent user name " 666 "not known - send AKA-Identity request"); 667 eap_aka_state(data, IDENTITY); 668 return; 669 } else { 670 wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown whether the " 671 "permanent user name is known; try to use " 672 "it"); 673 /* eap_sim_db_get_aka_auth() will report failure, if 674 * this identity is not known. */ 675 } 676 } 677 678 wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Identity", 679 identity, identity_len); 680 681 if (!after_reauth && data->reauth) { 682 eap_aka_state(data, REAUTH); 683 return; 684 } 685 686 res = eap_sim_db_get_aka_auth(sm->eap_sim_db_priv, identity, 687 identity_len, data->rand, data->autn, 688 data->ik, data->ck, data->res, 689 &data->res_len, sm); 690 if (res == EAP_SIM_DB_PENDING) { 691 wpa_printf(MSG_DEBUG, "EAP-AKA: AKA authentication data " 692 "not yet available - pending request"); 693 sm->method_pending = METHOD_PENDING_WAIT; 694 return; 695 } 696 697 #ifdef EAP_SERVER_AKA_PRIME 698 if (data->eap_method == EAP_TYPE_AKA_PRIME) { 699 /* Note: AUTN = (SQN ^ AK) || AMF || MAC which gives us the 700 * needed 6-octet SQN ^AK for CK',IK' derivation */ 701 eap_aka_prime_derive_ck_ik_prime(data->ck, data->ik, 702 data->autn, 703 data->network_name, 704 data->network_name_len); 705 } 706 #endif /* EAP_SERVER_AKA_PRIME */ 707 708 data->reauth = NULL; 709 data->counter = 0; /* reset re-auth counter since this is full auth */ 710 711 if (res != 0) { 712 wpa_printf(MSG_INFO, "EAP-AKA: Failed to get AKA " 713 "authentication data for the peer"); 714 data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; 715 eap_aka_state(data, NOTIFICATION); 716 return; 717 } 718 if (sm->method_pending == METHOD_PENDING_WAIT) { 719 wpa_printf(MSG_DEBUG, "EAP-AKA: AKA authentication data " 720 "available - abort pending wait"); 721 sm->method_pending = METHOD_PENDING_NONE; 722 } 723 724 identity_len = sm->identity_len; 725 while (identity_len > 0 && sm->identity[identity_len - 1] == '\0') { 726 wpa_printf(MSG_DEBUG, "EAP-AKA: Workaround - drop last null " 727 "character from identity"); 728 identity_len--; 729 } 730 wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Identity for MK derivation", 731 sm->identity, identity_len); 732 733 if (data->eap_method == EAP_TYPE_AKA_PRIME) { 734 eap_aka_prime_derive_keys(identity, identity_len, data->ik, 735 data->ck, data->k_encr, data->k_aut, 736 data->k_re, data->msk, data->emsk); 737 } else { 738 eap_aka_derive_mk(sm->identity, identity_len, data->ik, 739 data->ck, data->mk); 740 eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, 741 data->msk, data->emsk); 742 } 743 744 eap_aka_state(data, CHALLENGE); 745 } 746 747 748 static void eap_aka_process_identity(struct eap_sm *sm, 749 struct eap_aka_data *data, 750 struct wpabuf *respData, 751 struct eap_sim_attrs *attr) 752 { 753 wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Identity"); 754 755 if (attr->mac || attr->iv || attr->encr_data) { 756 wpa_printf(MSG_WARNING, "EAP-AKA: Unexpected attribute " 757 "received in EAP-Response/AKA-Identity"); 758 data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; 759 eap_aka_state(data, NOTIFICATION); 760 return; 761 } 762 763 if (attr->identity) { 764 os_free(sm->identity); 765 sm->identity = os_malloc(attr->identity_len); 766 if (sm->identity) { 767 os_memcpy(sm->identity, attr->identity, 768 attr->identity_len); 769 sm->identity_len = attr->identity_len; 770 } 771 } 772 773 eap_aka_determine_identity(sm, data, 0, 0); 774 if (eap_get_id(respData) == data->pending_id) { 775 data->pending_id = -1; 776 eap_aka_add_id_msg(data, respData); 777 } 778 } 779 780 781 static int eap_aka_verify_mac(struct eap_aka_data *data, 782 const struct wpabuf *req, 783 const u8 *mac, const u8 *extra, 784 size_t extra_len) 785 { 786 if (data->eap_method == EAP_TYPE_AKA_PRIME) 787 return eap_sim_verify_mac_sha256(data->k_aut, req, mac, extra, 788 extra_len); 789 return eap_sim_verify_mac(data->k_aut, req, mac, extra, extra_len); 790 } 791 792 793 static void eap_aka_process_challenge(struct eap_sm *sm, 794 struct eap_aka_data *data, 795 struct wpabuf *respData, 796 struct eap_sim_attrs *attr) 797 { 798 const u8 *identity; 799 size_t identity_len; 800 801 wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Challenge"); 802 803 #ifdef EAP_SERVER_AKA_PRIME 804 #if 0 805 /* KDF negotiation; to be enabled only after more than one KDF is 806 * supported */ 807 if (data->eap_method == EAP_TYPE_AKA_PRIME && 808 attr->kdf_count == 1 && attr->mac == NULL) { 809 if (attr->kdf[0] != EAP_AKA_PRIME_KDF) { 810 wpa_printf(MSG_WARNING, "EAP-AKA': Peer selected " 811 "unknown KDF"); 812 data->notification = 813 EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; 814 eap_aka_state(data, NOTIFICATION); 815 return; 816 } 817 818 data->kdf = attr->kdf[0]; 819 820 /* Allow negotiation to continue with the selected KDF by 821 * sending another Challenge message */ 822 wpa_printf(MSG_DEBUG, "EAP-AKA': KDF %d selected", data->kdf); 823 return; 824 } 825 #endif 826 #endif /* EAP_SERVER_AKA_PRIME */ 827 828 if (attr->checkcode && 829 eap_aka_verify_checkcode(data, attr->checkcode, 830 attr->checkcode_len)) { 831 wpa_printf(MSG_WARNING, "EAP-AKA: Invalid AT_CHECKCODE in the " 832 "message"); 833 data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; 834 eap_aka_state(data, NOTIFICATION); 835 return; 836 } 837 if (attr->mac == NULL || 838 eap_aka_verify_mac(data, respData, attr->mac, NULL, 0)) { 839 wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message " 840 "did not include valid AT_MAC"); 841 data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; 842 eap_aka_state(data, NOTIFICATION); 843 return; 844 } 845 846 /* 847 * AT_RES is padded, so verify that there is enough room for RES and 848 * that the RES length in bits matches with the expected RES. 849 */ 850 if (attr->res == NULL || attr->res_len < data->res_len || 851 attr->res_len_bits != data->res_len * 8 || 852 os_memcmp(attr->res, data->res, data->res_len) != 0) { 853 wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message did not " 854 "include valid AT_RES (attr len=%lu, res len=%lu " 855 "bits, expected %lu bits)", 856 (unsigned long) attr->res_len, 857 (unsigned long) attr->res_len_bits, 858 (unsigned long) data->res_len * 8); 859 data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; 860 eap_aka_state(data, NOTIFICATION); 861 return; 862 } 863 864 wpa_printf(MSG_DEBUG, "EAP-AKA: Challenge response includes the " 865 "correct AT_MAC"); 866 if (sm->eap_sim_aka_result_ind && attr->result_ind) { 867 data->use_result_ind = 1; 868 data->notification = EAP_SIM_SUCCESS; 869 eap_aka_state(data, NOTIFICATION); 870 } else 871 eap_aka_state(data, SUCCESS); 872 873 identity = eap_sim_db_get_permanent(sm->eap_sim_db_priv, sm->identity, 874 sm->identity_len, &identity_len); 875 if (identity == NULL) { 876 identity = sm->identity; 877 identity_len = sm->identity_len; 878 } 879 880 if (data->next_pseudonym) { 881 eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, identity, 882 identity_len, 883 data->next_pseudonym); 884 data->next_pseudonym = NULL; 885 } 886 if (data->next_reauth_id) { 887 if (data->eap_method == EAP_TYPE_AKA_PRIME) { 888 #ifdef EAP_SERVER_AKA_PRIME 889 eap_sim_db_add_reauth_prime(sm->eap_sim_db_priv, 890 identity, 891 identity_len, 892 data->next_reauth_id, 893 data->counter + 1, 894 data->k_encr, data->k_aut, 895 data->k_re); 896 #endif /* EAP_SERVER_AKA_PRIME */ 897 } else { 898 eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity, 899 identity_len, 900 data->next_reauth_id, 901 data->counter + 1, 902 data->mk); 903 } 904 data->next_reauth_id = NULL; 905 } 906 } 907 908 909 static void eap_aka_process_sync_failure(struct eap_sm *sm, 910 struct eap_aka_data *data, 911 struct wpabuf *respData, 912 struct eap_sim_attrs *attr) 913 { 914 wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Synchronization-Failure"); 915 916 if (attr->auts == NULL) { 917 wpa_printf(MSG_WARNING, "EAP-AKA: Synchronization-Failure " 918 "message did not include valid AT_AUTS"); 919 data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; 920 eap_aka_state(data, NOTIFICATION); 921 return; 922 } 923 924 /* Avoid re-reporting AUTS when processing pending EAP packet by 925 * maintaining a local flag stating whether this AUTS has already been 926 * reported. */ 927 if (!data->auts_reported && 928 eap_sim_db_resynchronize(sm->eap_sim_db_priv, sm->identity, 929 sm->identity_len, attr->auts, 930 data->rand)) { 931 wpa_printf(MSG_WARNING, "EAP-AKA: Resynchronization failed"); 932 data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; 933 eap_aka_state(data, NOTIFICATION); 934 return; 935 } 936 data->auts_reported = 1; 937 938 /* Try again after resynchronization */ 939 eap_aka_determine_identity(sm, data, 0, 0); 940 } 941 942 943 static void eap_aka_process_reauth(struct eap_sm *sm, 944 struct eap_aka_data *data, 945 struct wpabuf *respData, 946 struct eap_sim_attrs *attr) 947 { 948 struct eap_sim_attrs eattr; 949 u8 *decrypted = NULL; 950 const u8 *identity, *id2; 951 size_t identity_len, id2_len; 952 953 wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Reauthentication"); 954 955 if (attr->mac == NULL || 956 eap_aka_verify_mac(data, respData, attr->mac, data->nonce_s, 957 EAP_SIM_NONCE_S_LEN)) { 958 wpa_printf(MSG_WARNING, "EAP-AKA: Re-authentication message " 959 "did not include valid AT_MAC"); 960 goto fail; 961 } 962 963 if (attr->encr_data == NULL || attr->iv == NULL) { 964 wpa_printf(MSG_WARNING, "EAP-AKA: Reauthentication " 965 "message did not include encrypted data"); 966 goto fail; 967 } 968 969 decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, 970 attr->encr_data_len, attr->iv, &eattr, 971 0); 972 if (decrypted == NULL) { 973 wpa_printf(MSG_WARNING, "EAP-AKA: Failed to parse encrypted " 974 "data from reauthentication message"); 975 goto fail; 976 } 977 978 if (eattr.counter != data->counter) { 979 wpa_printf(MSG_WARNING, "EAP-AKA: Re-authentication message " 980 "used incorrect counter %u, expected %u", 981 eattr.counter, data->counter); 982 goto fail; 983 } 984 os_free(decrypted); 985 decrypted = NULL; 986 987 wpa_printf(MSG_DEBUG, "EAP-AKA: Re-authentication response includes " 988 "the correct AT_MAC"); 989 990 if (eattr.counter_too_small) { 991 wpa_printf(MSG_DEBUG, "EAP-AKA: Re-authentication response " 992 "included AT_COUNTER_TOO_SMALL - starting full " 993 "authentication"); 994 eap_aka_determine_identity(sm, data, 0, 1); 995 return; 996 } 997 998 if (sm->eap_sim_aka_result_ind && attr->result_ind) { 999 data->use_result_ind = 1; 1000 data->notification = EAP_SIM_SUCCESS; 1001 eap_aka_state(data, NOTIFICATION); 1002 } else 1003 eap_aka_state(data, SUCCESS); 1004 1005 if (data->reauth) { 1006 identity = data->reauth->identity; 1007 identity_len = data->reauth->identity_len; 1008 } else { 1009 identity = sm->identity; 1010 identity_len = sm->identity_len; 1011 } 1012 1013 id2 = eap_sim_db_get_permanent(sm->eap_sim_db_priv, identity, 1014 identity_len, &id2_len); 1015 if (id2) { 1016 identity = id2; 1017 identity_len = id2_len; 1018 } 1019 1020 if (data->next_pseudonym) { 1021 eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, identity, 1022 identity_len, data->next_pseudonym); 1023 data->next_pseudonym = NULL; 1024 } 1025 if (data->next_reauth_id) { 1026 if (data->eap_method == EAP_TYPE_AKA_PRIME) { 1027 #ifdef EAP_SERVER_AKA_PRIME 1028 eap_sim_db_add_reauth_prime(sm->eap_sim_db_priv, 1029 identity, 1030 identity_len, 1031 data->next_reauth_id, 1032 data->counter + 1, 1033 data->k_encr, data->k_aut, 1034 data->k_re); 1035 #endif /* EAP_SERVER_AKA_PRIME */ 1036 } else { 1037 eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity, 1038 identity_len, 1039 data->next_reauth_id, 1040 data->counter + 1, 1041 data->mk); 1042 } 1043 data->next_reauth_id = NULL; 1044 } else { 1045 eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth); 1046 data->reauth = NULL; 1047 } 1048 1049 return; 1050 1051 fail: 1052 data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; 1053 eap_aka_state(data, NOTIFICATION); 1054 eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth); 1055 data->reauth = NULL; 1056 os_free(decrypted); 1057 } 1058 1059 1060 static void eap_aka_process_client_error(struct eap_sm *sm, 1061 struct eap_aka_data *data, 1062 struct wpabuf *respData, 1063 struct eap_sim_attrs *attr) 1064 { 1065 wpa_printf(MSG_DEBUG, "EAP-AKA: Client reported error %d", 1066 attr->client_error_code); 1067 if (data->notification == EAP_SIM_SUCCESS && data->use_result_ind) 1068 eap_aka_state(data, SUCCESS); 1069 else 1070 eap_aka_state(data, FAILURE); 1071 } 1072 1073 1074 static void eap_aka_process_authentication_reject( 1075 struct eap_sm *sm, struct eap_aka_data *data, 1076 struct wpabuf *respData, struct eap_sim_attrs *attr) 1077 { 1078 wpa_printf(MSG_DEBUG, "EAP-AKA: Client rejected authentication"); 1079 eap_aka_state(data, FAILURE); 1080 } 1081 1082 1083 static void eap_aka_process_notification(struct eap_sm *sm, 1084 struct eap_aka_data *data, 1085 struct wpabuf *respData, 1086 struct eap_sim_attrs *attr) 1087 { 1088 wpa_printf(MSG_DEBUG, "EAP-AKA: Client replied to notification"); 1089 if (data->notification == EAP_SIM_SUCCESS && data->use_result_ind) 1090 eap_aka_state(data, SUCCESS); 1091 else 1092 eap_aka_state(data, FAILURE); 1093 } 1094 1095 1096 static void eap_aka_process(struct eap_sm *sm, void *priv, 1097 struct wpabuf *respData) 1098 { 1099 struct eap_aka_data *data = priv; 1100 const u8 *pos, *end; 1101 u8 subtype; 1102 size_t len; 1103 struct eap_sim_attrs attr; 1104 1105 pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_method, respData, 1106 &len); 1107 if (pos == NULL || len < 3) 1108 return; 1109 1110 end = pos + len; 1111 subtype = *pos; 1112 pos += 3; 1113 1114 if (eap_aka_subtype_ok(data, subtype)) { 1115 wpa_printf(MSG_DEBUG, "EAP-AKA: Unrecognized or unexpected " 1116 "EAP-AKA Subtype in EAP Response"); 1117 data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; 1118 eap_aka_state(data, NOTIFICATION); 1119 return; 1120 } 1121 1122 if (eap_sim_parse_attr(pos, end, &attr, 1123 data->eap_method == EAP_TYPE_AKA_PRIME ? 2 : 1, 1124 0)) { 1125 wpa_printf(MSG_DEBUG, "EAP-AKA: Failed to parse attributes"); 1126 data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; 1127 eap_aka_state(data, NOTIFICATION); 1128 return; 1129 } 1130 1131 if (subtype == EAP_AKA_SUBTYPE_CLIENT_ERROR) { 1132 eap_aka_process_client_error(sm, data, respData, &attr); 1133 return; 1134 } 1135 1136 if (subtype == EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT) { 1137 eap_aka_process_authentication_reject(sm, data, respData, 1138 &attr); 1139 return; 1140 } 1141 1142 switch (data->state) { 1143 case IDENTITY: 1144 eap_aka_process_identity(sm, data, respData, &attr); 1145 break; 1146 case CHALLENGE: 1147 if (subtype == EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE) { 1148 eap_aka_process_sync_failure(sm, data, respData, 1149 &attr); 1150 } else { 1151 eap_aka_process_challenge(sm, data, respData, &attr); 1152 } 1153 break; 1154 case REAUTH: 1155 eap_aka_process_reauth(sm, data, respData, &attr); 1156 break; 1157 case NOTIFICATION: 1158 eap_aka_process_notification(sm, data, respData, &attr); 1159 break; 1160 default: 1161 wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown state %d in " 1162 "process", data->state); 1163 break; 1164 } 1165 } 1166 1167 1168 static Boolean eap_aka_isDone(struct eap_sm *sm, void *priv) 1169 { 1170 struct eap_aka_data *data = priv; 1171 return data->state == SUCCESS || data->state == FAILURE; 1172 } 1173 1174 1175 static u8 * eap_aka_getKey(struct eap_sm *sm, void *priv, size_t *len) 1176 { 1177 struct eap_aka_data *data = priv; 1178 u8 *key; 1179 1180 if (data->state != SUCCESS) 1181 return NULL; 1182 1183 key = os_malloc(EAP_SIM_KEYING_DATA_LEN); 1184 if (key == NULL) 1185 return NULL; 1186 os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN); 1187 *len = EAP_SIM_KEYING_DATA_LEN; 1188 return key; 1189 } 1190 1191 1192 static u8 * eap_aka_get_emsk(struct eap_sm *sm, void *priv, size_t *len) 1193 { 1194 struct eap_aka_data *data = priv; 1195 u8 *key; 1196 1197 if (data->state != SUCCESS) 1198 return NULL; 1199 1200 key = os_malloc(EAP_EMSK_LEN); 1201 if (key == NULL) 1202 return NULL; 1203 os_memcpy(key, data->emsk, EAP_EMSK_LEN); 1204 *len = EAP_EMSK_LEN; 1205 return key; 1206 } 1207 1208 1209 static Boolean eap_aka_isSuccess(struct eap_sm *sm, void *priv) 1210 { 1211 struct eap_aka_data *data = priv; 1212 return data->state == SUCCESS; 1213 } 1214 1215 1216 int eap_server_aka_register(void) 1217 { 1218 struct eap_method *eap; 1219 int ret; 1220 1221 eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, 1222 EAP_VENDOR_IETF, EAP_TYPE_AKA, "AKA"); 1223 if (eap == NULL) 1224 return -1; 1225 1226 eap->init = eap_aka_init; 1227 eap->reset = eap_aka_reset; 1228 eap->buildReq = eap_aka_buildReq; 1229 eap->check = eap_aka_check; 1230 eap->process = eap_aka_process; 1231 eap->isDone = eap_aka_isDone; 1232 eap->getKey = eap_aka_getKey; 1233 eap->isSuccess = eap_aka_isSuccess; 1234 eap->get_emsk = eap_aka_get_emsk; 1235 1236 ret = eap_server_method_register(eap); 1237 if (ret) 1238 eap_server_method_free(eap); 1239 return ret; 1240 } 1241 1242 1243 #ifdef EAP_SERVER_AKA_PRIME 1244 int eap_server_aka_prime_register(void) 1245 { 1246 struct eap_method *eap; 1247 int ret; 1248 1249 eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, 1250 EAP_VENDOR_IETF, EAP_TYPE_AKA_PRIME, 1251 "AKA'"); 1252 if (eap == NULL) 1253 return -1; 1254 1255 eap->init = eap_aka_prime_init; 1256 eap->reset = eap_aka_reset; 1257 eap->buildReq = eap_aka_buildReq; 1258 eap->check = eap_aka_check; 1259 eap->process = eap_aka_process; 1260 eap->isDone = eap_aka_isDone; 1261 eap->getKey = eap_aka_getKey; 1262 eap->isSuccess = eap_aka_isSuccess; 1263 eap->get_emsk = eap_aka_get_emsk; 1264 1265 ret = eap_server_method_register(eap); 1266 if (ret) 1267 eap_server_method_free(eap); 1268 1269 return ret; 1270 } 1271 #endif /* EAP_SERVER_AKA_PRIME */ 1272