#if HAVE_CRT #define _CRTDBG_MAP_ALLOC #include <stdlib.h> #include <crtdbg.h> #endif //HAVE_CRT /* * Copyright (C) 2020, University of the Basque Country (UPV/EHU) * Contact for licensing options: <licensing-mcpttclient(at)mcopenplatform(dot)com> * * The original file was part of Open Source Doubango Framework * Copyright (C) 2010-2011 Mamadou Diop. * Copyright (C) 2012 Doubango Telecom <http://doubango.org> * * This file is part of Open Source Doubango Framework. * * DOUBANGO is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * DOUBANGO is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with DOUBANGO. * */ /**@file tsip_challenge.c * @brief SIP authentication challenge. * * @author Mamadou Diop <diopmamadou(at)doubango[dot]org> * */ #include "tinysip/authentication/tsip_challenge.h" #include "tinysip/headers/tsip_header_Authorization.h" #include "tinysip/headers/tsip_header_Proxy_Authorization.h" #include "tsk_string.h" #include "tsk_debug.h" #include "tsk_memory.h" #include "tsk_base64.h" #include "tsk_hmac.h" #include <string.h> #define TSIP_CHALLENGE_IS_DIGEST(self) ((self) ? tsk_striequals((self)->scheme, "Digest") : 0) #define TSIP_CHALLENGE_IS_AKAv1(self) ((self) ? tsk_striequals((self)->algorithm, "AKAv1-MD5") : 0) #define TSIP_CHALLENGE_IS_AKAv2(self) ((self) ? tsk_striequals((self)->algorithm, "AKAv2-MD5") : 0) #define TSIP_CHALLENGE_STACK(self) (TSIP_STACK((self)->stack)) #define TSIP_CHALLENGE_USERNAME(self) (self)->username #define TSIP_CHALLENGE_PASSWORD(self) TSIP_CHALLENGE_STACK(self)->identity.password /** Creates new challenge object. */ tsip_challenge_t* tsip_challenge_create(tsip_stack_t* stack, tsk_bool_t isproxy, const char* scheme, const char* realm, const char* nonce, const char* opaque, const char* algorithm,const char* auts,const char* auts_param,const char* auts_value, const char* qop) { return tsk_object_new(tsip_challenge_def_t, stack, isproxy,scheme, realm, nonce, opaque, algorithm,auts,auts_param,auts_value, qop); } /** Creates new challenge object (with default values). */ tsip_challenge_t* tsip_challenge_create_null(tsip_stack_t* stack) { return tsip_challenge_create(stack, tsk_false, tsk_null, tsk_null, tsk_null, tsk_null, tsk_null,tsk_null,tsk_null,tsk_null, tsk_null); } int tsip_challenge_reset_cnonce(tsip_challenge_t *self) { if(self){ if(self->qop) /* client nonce is only used if qop=auth, auth-int or both */ { #if 0 memcpy(self->cnonce, "ecb1d3f6931803ce7ae68099cb946594", 32); #else tsk_istr_t istr; tsk_strrandom(&istr); tsk_md5compute(istr, tsk_strlen(istr), &self->cnonce); #endif self->nc = 1; } } return -1; } //3GPP TS 35.205/6/7/8/9 and RFC 3310 int tsip_challenge_get_akares(tsip_challenge_t *self, char const *password,uint8_t** auts_bin , char** result) { char *nonce = tsk_null; int ret = -1; uint8_t* hex_key=tsk_null; if(tsk_register_get_cb()){ TSK_DEBUG_INFO("Use authentication SIM"); tsk_size_t n; AKA_RAND_T rand; AKA_AUTN_T autn; tsk_buffer_t* resultAka_base64; char *commandAuthBase64=tsk_null; uint8_t *commandAuth=tsk_null; uint8_t *resultAka=tsk_null; uint8_t *res=tsk_null; uint8_t *ck=tsk_null; uint8_t *ik=tsk_null; uint8_t *kc=tsk_null; int size_ck=-1; int size_ik=-1; int size_kc=-1; int size_res=-1; int size_auts=-1; #if HAVE_CRT //Debug memory commandAuth = (uint8_t*)calloc((AKA_RAND_SIZE+AKA_AUTN_SIZE+2), sizeof(uint8_t)); #else commandAuth = (uint8_t*)tsk_calloc((AKA_RAND_SIZE+AKA_AUTN_SIZE+2), sizeof(uint8_t)); #endif //HAVE_CRT /*Calcule CK, IK and RES with GSM*/ /* 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); } //Byte(s) Description Length //1 Length of RAND (L1) 1 //2 to (L1+1) RAND L1 //(L1+2) Length of AUTN (L2) 1 //(L1+3) (L1+L2+2) AUTN L2 //Note: Parameter present if and only if in 3G security context.*/ //Copy parameters commandAuth[0]=AKA_RAND_SIZE; memcpy(&commandAuth[1],rand,AKA_RAND_SIZE*sizeof(uint8_t)); commandAuth[AKA_RAND_SIZE+1]=AKA_AUTN_SIZE; memcpy(&commandAuth[AKA_RAND_SIZE+1+1],autn,AKA_RAND_SIZE*sizeof(uint8_t)); TSK_DEBUG_INFO("Data authentication command Auth: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", commandAuth[0] & 0xff, commandAuth[1] & 0xff, commandAuth[2] & 0xff, commandAuth[3] & 0xff, commandAuth[4] & 0xff, commandAuth[5] & 0xff, commandAuth[6] & 0xff, commandAuth[7] & 0xff, commandAuth[8] & 0xff, commandAuth[9] & 0xff, commandAuth[10] & 0xff, commandAuth[11] & 0xff, commandAuth[12] & 0xff, commandAuth[13] & 0xff, commandAuth[14] & 0xff, commandAuth[15] & 0xff, commandAuth[16] & 0xff, commandAuth[17] & 0xff, commandAuth[18] & 0xff, commandAuth[19] & 0xff, commandAuth[20] & 0xff, commandAuth[21] & 0xff, commandAuth[22] & 0xff, commandAuth[23] & 0xff, commandAuth[24] & 0xff, commandAuth[25] & 0xff, commandAuth[26] & 0xff, commandAuth[27] & 0xff, commandAuth[28] & 0xff, commandAuth[29] & 0xff, commandAuth[30] & 0xff, commandAuth[31] & 0xff, commandAuth[32] & 0xff, commandAuth[33] & 0xff); if(!tsk_base64_encode(commandAuth, AKA_RAND_SIZE+AKA_AUTN_SIZE+2, &commandAuthBase64)){ TSK_DEBUG_ERROR("tsk_base64_encode() failed. AKAv1 response will be invalid."); ret = -4; goto bail; } resultAka_base64=tsk_register_get_cb()(tsk_register_get_arg_data(), "" "%s" "\n", commandAuthBase64); //Byte(s) Description Length //1 "Successful 3G authentication" tag = 'DB' 1 //2 Length of RES (L3) 1 //3 to (L3+2) RES L3 //(L3+3) Length of CK (L4) 1 //(L3+4) to (L3+L4+3) CK L4 //(L3+L4+4) Length of IK (L5) 1 //(L3+L4+5) to (L3+L4+L5+4) IK L5 //(L3+L4+L5+5) Length of KC (= 8) (see note) 1 //(L3+L4+L5+6to(L3+L4+L5+13) KC (see note) 8 //Byte(s) Description Length //1 "Synchronisation failure" tag = 'DC' 1 //2 Length of AUTS (L1) 1 //3 to (L1+2) AUTS L1 if(resultAka_base64 && resultAka_base64!=tsk_null && resultAka_base64->data && resultAka_base64->data!=tsk_null){ AKA_RES_T akares; AKA_XXX_DECLARE(AK); AKA_XXX_DECLARE(CK); AKA_XXX_DECLARE(IK); AKA_XXX_DECLARE(K); AKA_XXX_BZERO(AK); AKA_XXX_BZERO(CK); AKA_XXX_BZERO(IK); AKA_XXX_BZERO(K); TSK_DEBUG_INFO("Receive response for authentication SIM"); //Processing response message n=-1; n = tsk_base64_decode((const uint8_t*)resultAka_base64->data,tsk_strlen(resultAka_base64->data), &resultAka); if(n <= 0){ TSK_DEBUG_ERROR("Failed to receive response by AKA"); }else if(resultAka && resultAka!=tsk_null) switch (resultAka[0]) { case 0xdb: //"Successful 3G authentication" TSK_DEBUG_INFO("Successful 3G authentication"); size_res=resultAka[1]; //RES #if HAVE_CRT //Debug memory res = (uint8_t*)calloc((size_res), sizeof(uint8_t)); #else res = (uint8_t*)tsk_calloc((size_res), sizeof(uint8_t)); #endif //HAVE_CRT memcpy(res,&resultAka[2],size_res*sizeof(uint8_t)); //check authentication SIM with Secret key /* Secret key */ if(tsk_strlen(password)==(AKA_K_SIZE*2)){ //It´s hex #if HAVE_CRT //Debug memory hex_key = malloc(tsk_strlen(password)*sizeof(uint8_t)); #else hex_key = tsk_malloc(tsk_strlen(password)*sizeof(uint8_t)); #endif //HAVE_CRT tsk_str_to_hex(password,tsk_strlen(password),hex_key); memcpy(K, hex_key,AKA_K_SIZE); }else{ //It´s string memcpy(K, password, (tsk_strlen(password) > AKA_K_SIZE ? AKA_K_SIZE : tsk_strlen(password))); } /* Calculate CK, IK and AK */ f2345(K,rand, akares, CK, IK, AK); TSK_DEBUG_INFO("Data authentication result: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", akares[0] & 0xff, akares[1] & 0xff, akares[2] & 0xff, akares[3] & 0xff, akares[4] & 0xff, akares[5] & 0xff, akares[6] & 0xff, akares[7] & 0xff); TSK_DEBUG_INFO("Data authentication result from sim: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", res[0] & 0xff, res[1] & 0xff, res[2] & 0xff, res[3] & 0xff, res[4] & 0xff, res[5] & 0xff, res[6] & 0xff, res[7] & 0xff); //CK size_ck=resultAka[2+size_res]; #if HAVE_CRT //Debug memory ck = (uint8_t*)calloc((size_ck), sizeof(uint8_t)); #else ck = (uint8_t*)tsk_calloc((size_ck), sizeof(uint8_t)); #endif //HAVE_CRT memcpy(ck,&resultAka[size_res+3],size_ck*sizeof(uint8_t)); //IK size_ik=resultAka[3+size_res+size_ck]; #if HAVE_CRT //Debug memory ik = (uint8_t*)calloc((size_ik), sizeof(uint8_t)); #else ik = (uint8_t*)tsk_calloc((size_ik), sizeof(uint8_t)); #endif //HAVE_CRT memcpy(ik,&resultAka[size_res+size_ck+4],size_ik*sizeof(uint8_t)); //KC size_kc=resultAka[4+size_res+size_ck+size_ik]; #if HAVE_CRT //Debug memory kc = (uint8_t*)calloc((size_kc), sizeof(uint8_t)); #else kc = (uint8_t*)tsk_calloc((size_kc), sizeof(uint8_t)); #endif //HAVE_CRT memcpy(kc,&resultAka[size_res+size_ck+size_ik+5],size_kc*sizeof(uint8_t)); /* 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, res, 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{ #if HAVE_CRT //Debug memory *result = calloc(1, AKA_RES_SIZE + 1); #else *result = tsk_calloc(1, AKA_RES_SIZE + 1); #endif //HAVE_CRT memcpy(*result, res, AKA_RES_SIZE); TSK_DEBUG_INFO("Data authentication result: %s\n",*result); ret = 0; } /* Copy CK and IK */ if(ck && ck!=tsk_null) memcpy(self->ck, ck, ( size_ck>= AKA_CK_SIZE ? AKA_CK_SIZE : size_ck)); if(ik && ik!=tsk_null) memcpy(self->ik, ik, ( size_ik>= AKA_IK_SIZE ? AKA_IK_SIZE : size_ik)); /* Secret key */ if(kc && kc!=tsk_null) memcpy(self->k, kc, (size_kc > AKA_K_SIZE ? AKA_K_SIZE : size_kc)); TSK_FREE(ck); TSK_FREE(ik); break; case 0xdc: //"Synchronisation failure" TSK_DEBUG_WARN("Synchronisation failure"); size_auts=resultAka[1]; //AUTS #if HAVE_CRT //Debug memory *auts_bin = (uint8_t*)calloc((size_auts), sizeof(uint8_t)); #else *auts_bin = (uint8_t*)tsk_calloc((size_auts), sizeof(uint8_t)); #endif //HAVE_CRT memcpy(*auts_bin,&resultAka[2],(size_auts > AKA_AUTS_SIZE ? AKA_AUTS_SIZE : size_auts)*sizeof(uint8_t)); break; default: TSK_DEBUG_ERROR("Response isn´t logic in authentication: %02x",resultAka[0] & 0xff); ret=-4; goto bail; break; } }else{ TSK_DEBUG_ERROR("it�s no ok the data"); } }else{ TSK_DEBUG_INFO("Use authentication NO SIM"); #define SQN_XOR_AK() (AUTN + 0) #define SERVER_DATA() (nonce + AKA_RAND_SIZE + AKA_AUTN_SIZE) // � ==> XOR // || ==> append AKA_RES_T akares; int i=0; tsk_size_t n; //uint8_t sqn_ms[AKA_SQN_SIZE]; uint8_t sqn_he[AKA_SQN_SIZE]= {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; 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); #if HAVE_CRT //Debug memory *auts_bin = (uint8_t*)calloc((AKA_AUTS_SIZE), sizeof(uint8_t)); #else *auts_bin = (uint8_t*)tsk_calloc((AKA_AUTS_SIZE), sizeof(uint8_t)); #endif //HAVE_CRT /* 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 */ /* Secret key */ if(tsk_strlen(password)==(AKA_K_SIZE*2)){ //It´s hex #if HAVE_CRT //Debug memory hex_key = malloc(tsk_strlen(password)*sizeof(uint8_t)); #else hex_key = tsk_malloc(tsk_strlen(password)*sizeof(uint8_t)); #endif //HAVE_CRT tsk_str_to_hex(password,tsk_strlen(password),hex_key); memcpy(K, hex_key,AKA_K_SIZE); }else{ //It´s string 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 */ /* SQN is optenido of the nonce and must be checked to be equal to the stored for the client. Where it is not what is should be general AUTS parameter to resynchronize the SQN between the client and the HSS */ for(n=0; n<AKA_SQN_SIZE; n++){ SQN[n] = (uint8_t) (SQN_XOR_AK()[n] ^ AK[n]); } TSK_DEBUG_INFO("Data authentication SQN: %02x:%02x:%02x:%02x:%02x:%02x\n", SQN[0] & 0xff, SQN[1] & 0xff, SQN[2] & 0xff, SQN[3] & 0xff, SQN[4] & 0xff, SQN[5] & 0xff); TSK_DEBUG_INFO("Data authentication SQN XOR AK: %02x:%02x:%02x:%02x:%02x:%02x\n", SQN_XOR_AK()[0] & 0xff, SQN_XOR_AK()[1] & 0xff, SQN_XOR_AK()[2] & 0xff, SQN_XOR_AK()[3] & 0xff, SQN_XOR_AK()[4] & 0xff, SQN_XOR_AK()[5] & 0xff); TSK_DEBUG_INFO("Data authentication AK: %02x:%02x:%02x:%02x:%02x:%02x\n", AK[0] & 0xff, AK[1] & 0xff, AK[2] & 0xff, AK[3] & 0xff, AK[4] & 0xff, AK[5] & 0xff); /* 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; }else{ TSK_DEBUG_INFO("Data:\n xmac=[%s]\nmax=[%s]", XMAC_A, MAC_A); TSK_DEBUG_INFO("Data authentication XMAC_A: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", XMAC_A[0] & 0xff, XMAC_A[1] & 0xff, XMAC_A[2] & 0xff, XMAC_A[3] & 0xff, XMAC_A[4] & 0xff, XMAC_A[5] & 0xff, XMAC_A[6] & 0xff, XMAC_A[7] & 0xff) TSK_DEBUG_INFO("Data authentication MAC_A: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", MAC_A[0] & 0xff, MAC_A[1] & 0xff, MAC_A[2] & 0xff, MAC_A[3] & 0xff, MAC_A[4] & 0xff, MAC_A[5] & 0xff, MAC_A[6] & 0xff, XMAC_A[7] & 0xff) } } /* //Test //Check SQN han generate AUTS sqn_ms[5] = sqn_he[5] + 1; f5star(K, RAND, AK); for(i=0; i<AKA_SQN_SIZE; i++) (*auts_bin)[i]=sqn_ms[i]^AK[i]; f1star(K, RAND, sqn_ms, AMF, (uint8_t * ) ((*auts_bin)+AKA_SQN_SIZE)); TSK_DEBUG_INFO("Data authentication AUTS BIN: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", (*auts_bin)[0] & 0xff, (*auts_bin)[1] & 0xff, (*auts_bin)[2] & 0xff, (*auts_bin)[3] & 0xff, (*auts_bin)[4] & 0xff, (*auts_bin)[5] & 0xff, (*auts_bin)[6] & 0xff, (*auts_bin)[7] & 0xff, (*auts_bin)[8] & 0xff, (*auts_bin)[9] & 0xff, (*auts_bin)[10] & 0xff, (*auts_bin)[11] & 0xff, (*auts_bin)[12] & 0xff, (*auts_bin)[13] & 0xff); */ /* 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{ #if HAVE_CRT //Debug memory *result = calloc(1, AKA_RES_SIZE + 1); #else *result = tsk_calloc(1, AKA_RES_SIZE + 1); #endif //HAVE_CRT memcpy(*result, akares, AKA_RES_SIZE); TSK_DEBUG_INFO("Data authentication result: %s\n",*result); TSK_DEBUG_INFO("Data authentication result: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", akares[0] & 0xff, akares[1] & 0xff, akares[2] & 0xff, akares[3] & 0xff, akares[4] & 0xff, akares[5] & 0xff, akares[6] & 0xff, akares[7] & 0xff) 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 } int tsip_challenge_get_response(tsip_challenge_t *self, const char* method, const char* uristring, const tsk_buffer_t* entity_body,char** auts_base64, tsk_md5string_t* response) { if(TSIP_CHALLENGE_IS_DIGEST(self) && self->stack){ tsk_md5string_t ha1, ha2; nonce_count_t nc; /* === Calculate HA1 = MD5(A1) = M5(username:realm:secret) In case of AKAv1-MD5 and AKAv2-MD5 the secret must be computed as per RFC 3310 + 3GPP TS 206/7/8/9. The resulting AKA RES parameter is treated as a "password"/"secret" when calculating the response directive of RFC 2617. */ if(TSIP_CHALLENGE_IS_AKAv1(self) || TSIP_CHALLENGE_IS_AKAv2(self)){ char* akaresult = tsk_null; uint8_t* auts_bin=tsk_null; tsip_challenge_get_akares(self, TSIP_CHALLENGE_STACK(self)->identity.password,&auts_bin, &akaresult); if(akaresult && akaresult!=tsk_null && thttp_auth_digest_HA1(TSIP_CHALLENGE_USERNAME(self), self->realm, akaresult, &ha1)){ // return -1; } if(auts_bin && auts_bin!=tsk_null){ //Encode auts base64 char* autsBase64_temp=tsk_null; tsk_size_t size=0; size=tsk_base64_encode((auts_bin),AKA_AUTS_SIZE*sizeof(uint8_t), &autsBase64_temp); if(((size>0 && autsBase64_temp) && autsBase64_temp!=tsk_null)){ TSK_DEBUG_INFO("Generate AUTS: %s",autsBase64_temp); *auts_base64=strdup (autsBase64_temp); } //if(auts_base64->data) //TSK_DEBUG_INFO("Data authentication AUTS Base64: %s",(char*)auts_base64->data); } TSK_DEBUG_INFO("Response in authentication HA1: %s",ha1); TSK_FREE(akaresult); TSK_FREE(auts_bin); } else{ if(!tsk_strnullORempty(self->ha1_hexstr)){ // use HA1 provide be the user (e.g. webrtc2sip server will need this to authenticate INVITEs when acting as b2bua) memset(ha1, 0, sizeof(tsk_md5string_t)); memcpy(ha1, self->ha1_hexstr, (TSK_MD5_DIGEST_SIZE << 1)); } else{ thttp_auth_digest_HA1(TSIP_CHALLENGE_USERNAME(self), self->realm, TSIP_CHALLENGE_STACK(self)->identity.password, &ha1); } } /* === HA2 */ thttp_auth_digest_HA2(method, uristring, entity_body, self->qop, &ha2); TSK_DEBUG_INFO("Response in authentication HA2: %s",ha2); /* RESPONSE */ if(self->nc){ THTTP_NCOUNT_2_STRING(self->nc, nc); } thttp_auth_digest_response((const tsk_md5string_t *)&ha1, self->nonce, nc, self->cnonce, self->qop, (const tsk_md5string_t *)&ha2, response); if(self->qop){ self->nc++; } return 0; } return -1; } int tsip_challenge_update(tsip_challenge_t *self, const char* scheme, const char* realm, const char* nonce, const char* opaque, const char* algorithm,const char* auts,const char* auts_param,const char* auts_value, const char* qop) { if(self){ int noncechanged = !tsk_striequals(self->nonce, nonce); tsk_strupdate(&self->scheme, scheme); tsk_strupdate(&self->realm, realm); tsk_strupdate(&self->nonce, nonce); tsk_strupdate(&self->opaque, opaque); tsk_strupdate(&self->algorithm, algorithm); tsk_strupdate(&self->auts, auts); tsk_strupdate(&self->auts_param, auts_param); tsk_strupdate(&self->auts_value, auts_value); if(qop){ self->qop = tsk_strcontains(qop, tsk_strlen(qop), "auth-int") ? "auth-int" : (tsk_strcontains(qop, tsk_strlen(qop), "auth") ? "auth" : tsk_null); } if(noncechanged && self->qop){ tsip_challenge_reset_cnonce(self); } return 0; } return -1; } int tsip_challenge_set_cred(tsip_challenge_t *self, const char* username, const char* ha1_hexstr) { if(!self || tsk_strlen(ha1_hexstr) != (TSK_MD5_DIGEST_SIZE << 1)){ TSK_DEBUG_ERROR("Invalid parameter"); return -1; } tsk_strupdate(&self->username, username); tsk_strupdate(&self->ha1_hexstr, ha1_hexstr); return 0; } tsip_header_t *tsip_challenge_create_header_authorization(tsip_challenge_t *self, const tsip_request_t *request) { tsk_md5string_t response; char* auts=tsk_null; nonce_count_t nc; char *uristring = tsk_null; tsip_header_t *header = tsk_null; if(!self || !self->stack || !request){ goto bail; } if(!(uristring = tsip_uri_tostring(request->line.request.uri, tsk_true, tsk_false))){ TSK_DEBUG_ERROR("Failed to parse URI: %s", uristring); goto bail; } /* We compute the nc here because @ref tsip_challenge_get_response function will increment it's value. */ if(self->nc){ THTTP_NCOUNT_2_STRING(self->nc, nc); } /* entity_body ==> request-content */ if(tsip_challenge_get_response(self, request->line.request.method, uristring, request->Content,&auts, &response)){ goto bail; }else if(auts && auts!=tsk_null){ TSK_DEBUG_INFO("Nonce in authentication auts base64: %s",auts); } TSK_DEBUG_INFO("Nonce in authentication: %s",self->nonce); TSK_DEBUG_INFO("Response in authentication: %s",response); #define TSIP_AUTH_COPY_VALUES(hdr) \ hdr->username = tsk_strdup(TSIP_CHALLENGE_USERNAME(self)); \ hdr->scheme = tsk_strdup(self->scheme); \ hdr->realm = tsk_strdup(self->realm); \ hdr->nonce = tsk_strdup(self->nonce); \ hdr->qop = tsk_strdup(self->qop); \ hdr->opaque = tsk_strdup(self->opaque); \ hdr->algorithm = self->algorithm ? tsk_strdup(self->algorithm) : tsk_strdup("AKAv1-MD5"); \ hdr->cnonce = self->nc? tsk_strdup(self->cnonce) : tsk_null; \ hdr->uri = tsk_strdup(uristring); \ hdr->nc = self->nc? tsk_strdup(nc) : 0; \ hdr->response = tsk_strdup(response); \ hdr->auts = tsk_strdup(auts); \ hdr->auts_param = tsk_strdup(self->auts_param); \ hdr->auts_value = tsk_strdup(self->auts_value); \ if(self->isproxy){ tsip_header_Proxy_Authorization_t *proxy_auth = tsip_header_Proxy_Authorization_create(); TSIP_AUTH_COPY_VALUES(proxy_auth); header = TSIP_HEADER(proxy_auth); } else{ tsip_header_Authorization_t *auth = tsip_header_Authorization_create(); TSIP_AUTH_COPY_VALUES(auth); header = TSIP_HEADER(auth); } bail: TSK_FREE(uristring); return header; #undef TSIP_AUTH_COPY_VALUES } tsip_header_t *tsip_challenge_create_empty_header_authorization(const char* username, const char* realm, const char* uristring) { tsip_header_Authorization_t *header = tsip_header_Authorization_create(); if(header){ header->scheme = tsk_strdup("Digest"); header->username = tsk_strdup(username); header->realm = tsk_strdup(realm); header->nonce = tsk_strdup(""); header->response = tsk_strdup(""); header->uri = tsk_strdup(uristring); //header->algorithm = tsk_strdup(""); //header->algorithm = tsk_strdup("AKAv1-MD5"); } return TSIP_HEADER(header); } //======================================================== // SIP challenge object definition // /**@ingroup tsip_challenge_group */ static tsk_object_t* tsip_challenge_ctor(tsk_object_t *self, va_list * app) { tsip_challenge_t *challenge = self; if(challenge){ const char* qop; challenge->stack = va_arg(*app, const tsip_stack_handle_t *); challenge->isproxy = va_arg(*app, tsk_bool_t); challenge->username = tsk_strdup(((const struct tsip_stack_s*)challenge->stack)->identity.impi); challenge->scheme = tsk_strdup(va_arg(*app, const char*)); challenge->realm = tsk_strdup(va_arg(*app, const char*)); challenge->nonce = tsk_strdup(va_arg(*app, const char*)); challenge->opaque = tsk_strdup(va_arg(*app, const char*)); challenge->algorithm = tsk_strdup(va_arg(*app, const char*)); // //This is for SQN with SIM challenge->auts = tsk_strdup(va_arg(*app, const char*)); challenge->auts_param = tsk_strdup(va_arg(*app, const char*)); challenge->auts_value = tsk_strdup(va_arg(*app, const char*)); qop = va_arg(*app, const char*); if(qop){ challenge->qop = tsk_strcontains(qop, tsk_strlen(qop), "auth-int") ? "auth-int" : (tsk_strcontains(qop, tsk_strlen(qop), "auth") ? "auth" : tsk_null); } if(challenge->qop){ tsip_challenge_reset_cnonce(challenge); } } else TSK_DEBUG_ERROR("Failed to create new sip challenge object."); return self; } /**@ingroup tsip_challenge_group */ static tsk_object_t* tsip_challenge_dtor(tsk_object_t *self) { tsip_challenge_t *challenge = self; if(challenge){ TSK_FREE(challenge->username); TSK_FREE(challenge->scheme); TSK_FREE(challenge->realm); TSK_FREE(challenge->nonce); TSK_FREE(challenge->opaque); TSK_FREE(challenge->algorithm); // //This is for SQN with SIM TSK_FREE(challenge->auts); TSK_FREE(challenge->auts_param); TSK_FREE(challenge->auts_value); TSK_FREE(challenge->ha1_hexstr); } else{ TSK_DEBUG_ERROR("Null SIP challenge object."); } return self; } static const tsk_object_def_t tsip_challenge_def_s = { sizeof(tsip_challenge_t), tsip_challenge_ctor, tsip_challenge_dtor, tsk_null }; const tsk_object_def_t *tsip_challenge_def_t = &tsip_challenge_def_s;