static void do_f2f3f5(uint8_t *key, uint8_t *rand, uint8_t *op, uint8_t *f2_exp, uint8_t *f5_exp, uint8_t *f3_exp) { uint8_t res_f2[8]; uint8_t res_f5[6]; uint8_t res_f3[16]; uint8_t res_f4[16]; SetOPc(op); f2345(key, rand, res_f2, res_f3, res_f4, res_f5); if (compare_buffer(res_f2, 8, f2_exp, 8) != 0) { fail("Fail: f2"); } if (compare_buffer(res_f5, 6, f5_exp, 6) != 0) { fail("Fail: f5"); } if (compare_buffer(res_f3, 16, f3_exp, 16) != 0) { fail("Fail: f3"); } }
int generate_vector(uint64_t imsi, uint8_t key[16], uint8_t plmn[3], uint8_t sqn[6], auc_vector_t *vector) { /* in E-UTRAN an authentication vector is composed of: * - RAND * - XRES * - AUTN * - KASME */ uint8_t amf[] = { 0x80, 0x00 }; uint8_t mac_a[8]; uint8_t ck[16]; uint8_t ik[16]; uint8_t ak[6]; int i; if (vector == NULL) { return EINVAL; } /* Compute MAC */ f1(key, vector->rand, sqn, amf, mac_a); print_buffer("MAC_A : ", mac_a, 8); print_buffer("SQN : ", sqn, 6); print_buffer("RAND : ", vector->rand, 16); /* Compute XRES, CK, IK, AK */ f2345(key, vector->rand, vector->xres, ck, ik, ak); print_buffer("AK : ", ak, 6); print_buffer("CK : ", ck, 16); print_buffer("IK : ", ik, 16); print_buffer("XRES : ", vector->xres, 8); /* AUTN = SQN ^ AK || AMF || MAC */ generate_autn(sqn, ak, amf, mac_a, vector->autn); print_buffer("AUTN : ", vector->autn, 16); derive_kasme(ck, ik, plmn, sqn, ak, vector->kasme); print_buffer("KASME : ", vector->kasme, 32); return 0; }
static void do_f4f5star(uint8_t *key, uint8_t *rand, uint8_t *op, uint8_t *f4_exp, uint8_t *f5star_exp) { uint8_t res_f2[8]; uint8_t res_f5[6]; uint8_t res_f3[16]; uint8_t res_f4[16]; uint8_t res_f5star[6]; SetOPc(op); f2345(key, rand, res_f2, res_f3, res_f4, res_f5); if (compare_buffer(res_f4, 16, f4_exp, 16) != 0) { fail("Fail: f4"); } f5star(key, rand, res_f5star); if (compare_buffer(res_f5star, 6, f5star_exp, 6) != 0) { fail("Fail: f5star"); } }
/* * Create MD5-AKA1 digest response. */ PJ_DEF(pj_status_t) pjsip_auth_create_aka_response( pj_pool_t *pool, const pjsip_digest_challenge*chal, const pjsip_cred_info *cred, const pj_str_t *method, pjsip_digest_credential *auth) { pj_str_t nonce_bin; int aka_version; const pj_str_t pjsip_AKAv1_MD5 = { "AKAv1-MD5", 9 }; const pj_str_t pjsip_AKAv2_MD5 = { "AKAv2-MD5", 9 }; pj_uint8_t *chal_rand, *chal_sqnxoraka, *chal_mac; pj_uint8_t k[PJSIP_AKA_KLEN]; pj_uint8_t op[PJSIP_AKA_OPLEN]; pj_uint8_t amf[PJSIP_AKA_AMFLEN]; pj_uint8_t res[PJSIP_AKA_RESLEN]; pj_uint8_t ck[PJSIP_AKA_CKLEN]; pj_uint8_t ik[PJSIP_AKA_IKLEN]; pj_uint8_t ak[PJSIP_AKA_AKLEN]; pj_uint8_t sqn[PJSIP_AKA_SQNLEN]; pj_uint8_t xmac[PJSIP_AKA_MACLEN]; pjsip_cred_info aka_cred; int i, len; pj_status_t status; /* Check the algorithm is supported. */ if (chal->algorithm.slen==0 || pj_stricmp2(&chal->algorithm, "md5") == 0) { /* * A normal MD5 authentication is requested. Fallbackt to the usual * MD5 digest creation. */ pjsip_auth_create_digest(&auth->response, &auth->nonce, &auth->nc, &auth->cnonce, &auth->qop, &auth->uri, &auth->realm, cred, method); return PJ_SUCCESS; } else if (pj_stricmp(&chal->algorithm, &pjsip_AKAv1_MD5) == 0) { /* * AKA version 1 is requested. */ aka_version = 1; } else if (pj_stricmp(&chal->algorithm, &pjsip_AKAv2_MD5) == 0) { /* * AKA version 2 is requested. */ aka_version = 2; } else { /* Unsupported algorithm */ return PJSIP_EINVALIDALGORITHM; } /* Decode nonce */ nonce_bin.slen = len = PJ_BASE64_TO_BASE256_LEN(chal->nonce.slen); nonce_bin.ptr = pj_pool_alloc(pool, nonce_bin.slen + 1); status = pj_base64_decode(&chal->nonce, (pj_uint8_t*)nonce_bin.ptr, &len); nonce_bin.slen = len; if (status != PJ_SUCCESS) return PJSIP_EAUTHINNONCE; if (nonce_bin.slen < PJSIP_AKA_RANDLEN + PJSIP_AKA_AUTNLEN) return PJSIP_EAUTHINNONCE; /* Get RAND, AUTN, and MAC */ chal_rand = (pj_uint8_t*)(nonce_bin.ptr + 0); chal_sqnxoraka = (pj_uint8_t*) (nonce_bin.ptr + PJSIP_AKA_RANDLEN); chal_mac = (pj_uint8_t*) (nonce_bin.ptr + PJSIP_AKA_RANDLEN + PJSIP_AKA_SQNLEN + PJSIP_AKA_AMFLEN); /* Copy k. op, and amf */ pj_bzero(k, sizeof(k)); pj_bzero(op, sizeof(op)); pj_bzero(amf, sizeof(amf)); if (cred->ext.aka.k.slen) pj_memcpy(k, cred->ext.aka.k.ptr, cred->ext.aka.k.slen); if (cred->ext.aka.op.slen) pj_memcpy(op, cred->ext.aka.op.ptr, cred->ext.aka.op.slen); if (cred->ext.aka.amf.slen) pj_memcpy(amf, cred->ext.aka.amf.ptr, cred->ext.aka.amf.slen); /* Given key K and random challenge RAND, compute response RES, * confidentiality key CK, integrity key IK and anonymity key AK. */ f2345(k, chal_rand, res, ck, ik, ak, op); /* Compute sequence number SQN */ for (i=0; i<PJSIP_AKA_SQNLEN; ++i) sqn[i] = (pj_uint8_t) (chal_sqnxoraka[i] ^ ak[i]); /* Verify MAC in the challenge */ /* Compute XMAC */ f1(k, chal_rand, sqn, amf, xmac, op); if (pj_memcmp(chal_mac, xmac, PJSIP_AKA_MACLEN) != 0) { return PJSIP_EAUTHINNONCE; } /* Build a temporary credential info to create MD5 digest, using * "res" as the password. */ pj_memcpy(&aka_cred, cred, sizeof(aka_cred)); aka_cred.data_type = PJSIP_CRED_DATA_PLAIN_PASSWD; /* Create a response */ if (aka_version == 1) { /* * For AKAv1, the password is RES */ aka_cred.data.ptr = (char*)res; aka_cred.data.slen = PJSIP_AKA_RESLEN; pjsip_auth_create_digest(&auth->response, &chal->nonce, &auth->nc, &auth->cnonce, &auth->qop, &auth->uri, &chal->realm, &aka_cred, method); } else if (aka_version == 2) { /* * For AKAv2, password is base64 encoded [1] parameters: * PRF(RES||IK||CK,"http-digest-akav2-password") * * The pseudo-random function (PRF) is HMAC-MD5 in this case. */ pj_str_t resikck; const pj_str_t AKAv2_Passwd = { "http-digest-akav2-password", 26 }; pj_uint8_t hmac_digest[16]; char tmp_buf[48]; int hmac64_len; resikck.slen = PJSIP_AKA_RESLEN + PJSIP_AKA_IKLEN + PJSIP_AKA_CKLEN; pj_assert(resikck.slen <= PJ_ARRAY_SIZE(tmp_buf)); resikck.ptr = tmp_buf; pj_memcpy(resikck.ptr + 0, res, PJSIP_AKA_RESLEN); pj_memcpy(resikck.ptr + PJSIP_AKA_RESLEN, ik, PJSIP_AKA_IKLEN); pj_memcpy(resikck.ptr + PJSIP_AKA_RESLEN + PJSIP_AKA_IKLEN, ck, PJSIP_AKA_CKLEN); pj_hmac_md5((const pj_uint8_t*)AKAv2_Passwd.ptr, AKAv2_Passwd.slen, (const pj_uint8_t*)resikck.ptr, resikck.slen, hmac_digest); aka_cred.data.slen = hmac64_len = PJ_BASE256_TO_BASE64_LEN(PJ_ARRAY_SIZE(hmac_digest)); pj_assert(aka_cred.data.slen+1 <= PJ_ARRAY_SIZE(tmp_buf)); aka_cred.data.ptr = tmp_buf; pj_base64_encode(hmac_digest, PJ_ARRAY_SIZE(hmac_digest), aka_cred.data.ptr, &len); aka_cred.data.slen = hmac64_len; pjsip_auth_create_digest(&auth->response, &chal->nonce, &auth->nc, &auth->cnonce, &auth->qop, &auth->uri, &chal->realm, &aka_cred, method); } else { pj_assert(!"Bug!"); return PJ_EBUG; } /* Done */ return PJ_SUCCESS; }
//3GPP TS 35.205/6/7/8/9 and RFC 3310 int tsip_challenge_get_akares(tsip_challenge_t *self, char const *password, char** result) { #define SQN_XOR_AK() (AUTN + 0) #define SERVER_DATA() (nonce + AKA_RAND_SIZE + AKA_AUTN_SIZE) // § ==> XOR // || ==> append AKA_RES_T akares; int ret = -1; tsk_size_t n; char *nonce = tsk_null; AKA_XXX_DECLARE(RAND); AKA_XXX_DECLARE(AK); AKA_XXX_DECLARE(AMF); AKA_XXX_DECLARE(CK); AKA_XXX_DECLARE(IK); AKA_XXX_DECLARE(K); AKA_XXX_DECLARE(SQN); AKA_XXX_DECLARE(MAC_A); AKA_XXX_DECLARE(AUTN); AKA_XXX_BZERO(RAND); AKA_XXX_BZERO(AK); AKA_XXX_BZERO(AMF); AKA_XXX_BZERO(CK); AKA_XXX_BZERO(IK); AKA_XXX_BZERO(K); AKA_XXX_BZERO(SQN); AKA_XXX_BZERO(MAC_A); AKA_XXX_BZERO(AUTN); /* RFC 3310 subclause 3.2: nonce = base64(RAND || AUTN || SERV_DATA) */ n = tsk_base64_decode((const uint8_t*)self->nonce, tsk_strlen(self->nonce), &nonce); if(n > TSK_MD5_STRING_SIZE){ TSK_DEBUG_ERROR("The IMS CORE returned an invalid nonce."); goto bail; } if(n < AKA_RAND_SIZE + AKA_AUTN_SIZE){ TSK_DEBUG_ERROR("The nonce returned by the IMS CORE is too short to contain both [RAND] and [AUTHN]"); goto bail; } else{ /* Get RAND and AUTN */ memcpy(RAND, nonce, AKA_RAND_SIZE); memcpy(AUTN, (nonce + AKA_RAND_SIZE), AKA_AUTN_SIZE); } /* Secret key */ memcpy(K, password, (tsk_strlen(password) > AKA_K_SIZE ? AKA_K_SIZE : tsk_strlen(password))); /* 3GPP TS 35.205: AUTN = SQN[§AK] || AMF || MAC-A */ memcpy(AMF, (AUTN + AKA_SQN_SIZE), AKA_AMF_SIZE); memcpy(MAC_A, (AUTN + AKA_SQN_SIZE + AKA_AMF_SIZE), AKA_MAC_A_SIZE); /* compute OP */ ComputeOP(TSIP_CHALLENGE_STACK(self)->security.operator_id); /* Checks that we hold the same AMF */ for(n=0; n<AKA_AMF_SIZE; n++){ if(AMF[n] != TSIP_CHALLENGE_STACK(self)->security.amf[n]){ TSK_DEBUG_ERROR("IMS-AKA error: AMF <> XAMF"); goto bail; } } /* Calculate CK, IK and AK */ f2345(K, RAND, akares, CK, IK, AK); /* Calculate SQN from SQN_XOR_AK */ for(n=0; n<AKA_SQN_SIZE; n++){ SQN[n] = (uint8_t) (SQN_XOR_AK()[n] ^ AK[n]); } /* Calculate XMAC_A */ { AKA_MAC_A_T XMAC_A; memset(XMAC_A, '\0', sizeof(XMAC_A)); f1(K, RAND, SQN, AMF, XMAC_A); if(!tsk_strnequals(MAC_A, XMAC_A, AKA_MAC_A_SIZE)){ TSK_DEBUG_ERROR("IMS-AKA error: XMAC_A [%s] <> MAC_A[%s]", XMAC_A, MAC_A); goto bail; } } /* RFC 4169 subclause 3 The HTTP Digest password is derived from base64 encoded PRF(RES || IK||CK, "http-digest-akav2-password") or PRF(XRES||IK||CK, "http-digest-akav2-password") instead of (RES) or (XRES) respectively. Where PRF ==> HMAC_MD5 function. */ if(TSIP_CHALLENGE_IS_AKAv2(self)){ uint8_t res_ik_ck[AKA_RES_SIZE + AKA_IK_SIZE + AKA_CK_SIZE]; tsk_md5digest_t md5_digest; memcpy(res_ik_ck, akares, AKA_RES_SIZE); memcpy((res_ik_ck + AKA_RES_SIZE), IK, AKA_IK_SIZE); memcpy((res_ik_ck + AKA_RES_SIZE + AKA_IK_SIZE), CK, AKA_CK_SIZE); if((ret = hmac_md5digest_compute((const uint8_t*)"http-digest-akav2-password", 26, (const char*)res_ik_ck, sizeof(res_ik_ck), md5_digest))){/* PRF(RES||IK||CK, ...) */ TSK_DEBUG_ERROR("hmac_md5digest_compute() failed. AKAv2 response will be invalid."); ret = -3; goto bail; } else{/* b64(PRF(...)) */ if(!tsk_base64_encode(md5_digest, sizeof(md5_digest), result)){ TSK_DEBUG_ERROR("tsk_base64_encode() failed. AKAv2 response will be invalid."); ret = -4; goto bail; } } } else{ *result = tsk_calloc(1, AKA_RES_SIZE + 1); memcpy(*result, akares, AKA_RES_SIZE); ret = 0; } /* Copy CK and IK */ memcpy(self->ck, CK, AKA_CK_SIZE); memcpy(self->ik, IK, AKA_IK_SIZE); bail: TSK_FREE(nonce); return ret; #undef SQN_XOR_AK #undef SERVER_DATA }
/**************************************************************************** ** ** ** Name: usim_api_authenticate() ** ** ** ** Description: Performs mutual authentication of the USIM to the network,** ** checking whether authentication token AUTN can be accep- ** ** ted. If so, returns an authentication response RES and ** ** the ciphering and integrity keys. ** ** In case of synch failure, returns a re-synchronization ** ** token AUTS. ** ** ** ** 3GPP TS 31.102, section 7.1.1.1 ** ** ** ** Authentication and key generating function algorithms are ** ** specified in 3GPP TS 35.206. ** ** ** ** Inputs: rand_pP: Random challenge number ** ** autn_pP: Authentication token ** ** AUTN = (SQN xor AK) || AMF || MAC ** ** 48 16 64 bits ** ** Others: Security key ** ** ** ** Outputs: auts_pP: Re-synchronization token ** ** res_pP: Authentication response ** ** ck_pP: Ciphering key ** ** ik_pP Integrity key ** ** ** ** Return: RETURNerror, RETURNok ** ** Others: None ** ** ** ***************************************************************************/ int usim_api_authenticate(const OctetString* rand_pP, const OctetString* autn_pP, OctetString* auts_pP, OctetString* res_pP, OctetString* ck_pP, OctetString* ik_pP) { LOG_FUNC_IN; int rc; int i; LOG_TRACE(DEBUG, "USIM-API - rand :%s",dump_octet_string(rand_pP)); LOG_TRACE(DEBUG, "USIM-API - autn :%s",dump_octet_string(autn_pP)); /* Compute the authentication response RES = f2K (RAND) */ /* Compute the cipher key CK = f3K (RAND) */ /* Compute the integrity key IK = f4K (RAND) */ /* Compute the anonymity key AK = f5K (RAND) */ #define USIM_API_AK_SIZE 6 u8 ak[USIM_API_AK_SIZE]; f2345(_usim_api_k, rand_pP->value, res_pP->value, ck_pP->value, ik_pP->value, ak); LOG_TRACE(DEBUG, "USIM-API - res(f2) :%s",dump_octet_string(res_pP)); LOG_TRACE(DEBUG, "USIM-API - ck(f3) :%s",dump_octet_string(ck_pP)); LOG_TRACE(DEBUG, "USIM-API - ik(f4) :%s",dump_octet_string(ik_pP)); LOG_TRACE(DEBUG, "USIM-API - ak(f5) : %02X%02X%02X%02X%02X%02X", ak[0],ak[1],ak[2],ak[3],ak[4],ak[5]); /* Retrieve the sequence number SQN = (SQN ⊕ AK) ⊕ AK */ #define USIM_API_SQN_SIZE USIM_API_AK_SIZE u8 sqn[USIM_API_SQN_SIZE]; for (i = 0; i < USIM_API_SQN_SIZE; i++) { sqn[i] = autn_pP->value[i] ^ ak[i]; } LOG_TRACE(DEBUG, "USIM-API - Retrieved SQN %02X%02X%02X%02X%02X%02X", sqn[0],sqn[1],sqn[2],sqn[3],sqn[4],sqn[5]); /* Compute XMAC = f1K (SQN || RAND || AMF) */ #define USIM_API_XMAC_SIZE 8 u8 xmac[USIM_API_XMAC_SIZE]; f1(_usim_api_k, rand_pP->value, sqn, &autn_pP->value[USIM_API_SQN_SIZE], xmac); LOG_TRACE(DEBUG, "USIM-API - Computed XMAC %02X%02X%02X%02X%02X%02X%02X%02X", xmac[0],xmac[1],xmac[2],xmac[3], xmac[4],xmac[5],xmac[6],xmac[7]); /* Compare the XMAC with the MAC included in AUTN */ #define USIM_API_AMF_SIZE 2 if ( memcmp(xmac, &autn_pP->value[USIM_API_SQN_SIZE + USIM_API_AMF_SIZE], USIM_API_XMAC_SIZE) != 0 ) { LOG_TRACE(INFO, "USIM-API - Comparing the XMAC with the MAC included in AUTN Failed"); //LOG_FUNC_RETURN (RETURNerror); } else { LOG_TRACE(INFO, "USIM-API - Comparing the XMAC with the MAC included in AUTN Succeeded"); } /* Verify that the received sequence number SQN is in the correct range */ rc = _usim_api_check_sqn(*(uint32_t*)(sqn), sqn[USIM_API_SQN_SIZE - 1]); if (rc != RETURNok) { /* Synchronisation failure; compute the AUTS parameter */ /* Concealed value of the counter SQNms in the USIM: * Conc(SQNMS) = SQNMS ⊕ f5*K(RAND) */ f5star(_usim_api_k, rand_pP->value, ak); #define USIM_API_SQNMS_SIZE USIM_API_SQN_SIZE u8 sqn_ms[USIM_API_SQNMS_SIZE]; memset(sqn_ms, 0, USIM_API_SQNMS_SIZE); #define USIM_API_SQN_MS_SIZE 3 for (i = 0; i < USIM_API_SQN_MS_SIZE; i++) { #warning "LG:BUG HERE TODO" sqn_ms[USIM_API_SQNMS_SIZE - i] = ((uint8_t*)(_usim_api_data.sqn_ms))[USIM_API_SQN_MS_SIZE - i]; } u8 sqnms[USIM_API_SQNMS_SIZE]; for (i = 0; i < USIM_API_SQNMS_SIZE; i++) { sqnms[i] = sqn_ms[i] ^ ak[i]; } LOG_TRACE(DEBUG, "USIM-API - SQNms %02X%02X%02X%02X%02X%02X", sqnms[0],sqnms[1],sqnms[2],sqnms[3],sqnms[4],sqnms[5]); /* Synchronisation message authentication code: * MACS = f1*K(SQNMS || RAND || AMF) */ #define USIM_API_MACS_SIZE USIM_API_XMAC_SIZE u8 macs[USIM_API_MACS_SIZE]; f1star(_usim_api_k, rand_pP->value, sqn_ms, &rand_pP->value[USIM_API_SQN_SIZE], macs); LOG_TRACE(DEBUG, "USIM-API - MACS %02X%02X%02X%02X%02X%02X%02X%02X", macs[0],macs[1],macs[2],macs[3], macs[4],macs[5],macs[6],macs[7]); /* Synchronisation authentication token: * AUTS = Conc(SQNMS) || MACS */ memcpy(&auts_pP->value[0], sqnms, USIM_API_SQNMS_SIZE); memcpy(&auts_pP->value[USIM_API_SQNMS_SIZE], macs, USIM_API_MACS_SIZE); } LOG_FUNC_RETURN (RETURNok); }