PJ_DEF(void) pj_hmac_md5_final(pj_hmac_md5_context *hctx, pj_uint8_t digest[16]) { pj_md5_final(&hctx->context, digest); /* * perform outer MD5 */ pj_md5_init(&hctx->context); pj_md5_update(&hctx->context, hctx->k_opad, 64); pj_md5_update(&hctx->context, digest, 16); pj_md5_final(&hctx->context, digest); }
PJ_DEF(pj_str_t) pjsip_calculate_branch_id( pjsip_rx_data *rdata ) { pj_md5_context ctx; pj_uint8_t digest[16]; pj_str_t branch; pj_str_t rfc3261_branch = {PJSIP_RFC3261_BRANCH_ID, PJSIP_RFC3261_BRANCH_LEN}; /* If incoming request does not have RFC 3261 branch value, create * a branch value from GUID . */ if (pj_strncmp(&rdata->msg_info.via->branch_param, &rfc3261_branch, PJSIP_RFC3261_BRANCH_LEN) != 0 ) { pj_str_t tmp; branch.ptr = (char*) pj_pool_alloc(rdata->tp_info.pool, PJSIP_MAX_BRANCH_LEN); branch.slen = PJSIP_RFC3261_BRANCH_LEN; pj_memcpy(branch.ptr, PJSIP_RFC3261_BRANCH_ID, PJSIP_RFC3261_BRANCH_LEN); tmp.ptr = branch.ptr + PJSIP_RFC3261_BRANCH_LEN + 2; *(tmp.ptr-2) = (pj_int8_t)(branch.slen+73); *(tmp.ptr-1) = (pj_int8_t)(branch.slen+99); pj_generate_unique_string( &tmp ); branch.slen = PJSIP_MAX_BRANCH_LEN; return branch; } /* Create branch ID for new request by calculating MD5 hash * of the branch parameter in top-most Via header. */ pj_md5_init(&ctx); pj_md5_update(&ctx, (pj_uint8_t*)rdata->msg_info.via->branch_param.ptr, rdata->msg_info.via->branch_param.slen); pj_md5_final(&ctx, digest); branch.ptr = (char*) pj_pool_alloc(rdata->tp_info.pool, 34 + PJSIP_RFC3261_BRANCH_LEN); pj_memcpy(branch.ptr, PJSIP_RFC3261_BRANCH_ID, PJSIP_RFC3261_BRANCH_LEN); branch.slen = PJSIP_RFC3261_BRANCH_LEN; *(branch.ptr+PJSIP_RFC3261_BRANCH_LEN) = (pj_int8_t)(branch.slen+73); *(branch.ptr+PJSIP_RFC3261_BRANCH_LEN+1) = (pj_int8_t)(branch.slen+99); digest2str(digest, branch.ptr+PJSIP_RFC3261_BRANCH_LEN+2); branch.slen = 34 + PJSIP_RFC3261_BRANCH_LEN; return branch; }
/* Calculate HMAC-SHA1 key for long term credential, by getting * MD5 digest of username, realm, and password. */ static void calc_md5_key(pj_uint8_t digest[16], const pj_str_t *realm, const pj_str_t *username, const pj_str_t *passwd) { /* The 16-byte key for MESSAGE-INTEGRITY HMAC is formed by taking * the MD5 hash of the result of concatenating the following five * fields: (1) The username, with any quotes and trailing nulls * removed, (2) A single colon, (3) The realm, with any quotes and * trailing nulls removed, (4) A single colon, and (5) The * password, with any trailing nulls removed. */ pj_md5_context ctx; pj_str_t s; pj_md5_init(&ctx); #define REMOVE_QUOTE(s) if (s.slen && *s.ptr=='"') \ s.ptr++, s.slen--; \ if (s.slen && s.ptr[s.slen-1]=='"') \ s.slen--; /* Add username */ s = *username; REMOVE_QUOTE(s); pj_md5_update(&ctx, (pj_uint8_t*)s.ptr, (unsigned)s.slen); /* Add single colon */ pj_md5_update(&ctx, (pj_uint8_t*)":", 1); /* Add realm */ s = *realm; REMOVE_QUOTE(s); pj_md5_update(&ctx, (pj_uint8_t*)s.ptr, (unsigned)s.slen); #undef REMOVE_QUOTE /* Another colon */ pj_md5_update(&ctx, (pj_uint8_t*)":", 1); /* Add password */ pj_md5_update(&ctx, (pj_uint8_t*)passwd->ptr, (unsigned)passwd->slen); /* Done */ pj_md5_final(&ctx, digest); }
PJ_DEF(void) pj_hmac_md5_init(pj_hmac_md5_context *hctx, const pj_uint8_t *key, unsigned key_len) { pj_uint8_t k_ipad[64]; pj_uint8_t tk[16]; int i; /* if key is longer than 64 bytes reset it to key=MD5(key) */ if (key_len > 64) { pj_md5_context tctx; pj_md5_init(&tctx); pj_md5_update(&tctx, key, key_len); pj_md5_final(&tctx, tk); key = tk; key_len = 16; } /* * HMAC = H(K XOR opad, H(K XOR ipad, text)) */ /* start out by storing key in pads */ pj_bzero( k_ipad, sizeof(k_ipad)); pj_bzero( hctx->k_opad, sizeof(hctx->k_opad)); pj_memcpy( k_ipad, key, key_len); pj_memcpy( hctx->k_opad, key, key_len); /* XOR key with ipad and opad values */ for (i=0; i<64; i++) { k_ipad[i] ^= 0x36; hctx->k_opad[i] ^= 0x5c; } /* * perform inner MD5 */ pj_md5_init(&hctx->context); pj_md5_update(&hctx->context, k_ipad, 64); }
/* * Create response digest based on the parameters and store the * digest ASCII in 'result'. */ PJ_DEF(void) pjsip_auth_create_digest( pj_str_t *result, const pj_str_t *nonce, const pj_str_t *nc, const pj_str_t *cnonce, const pj_str_t *qop, const pj_str_t *uri, const pj_str_t *realm, const pjsip_cred_info *cred_info, const pj_str_t *method) { char ha1[PJSIP_MD5STRLEN]; char ha2[PJSIP_MD5STRLEN]; unsigned char digest[16]; pj_md5_context pms; pj_assert(result->slen >= PJSIP_MD5STRLEN); AUTH_TRACE_((THIS_FILE, "Begin creating digest")); if ((cred_info->data_type & PASSWD_MASK) == PJSIP_CRED_DATA_PLAIN_PASSWD) { /*** *** ha1 = MD5(username ":"******":" password) ***/ pj_md5_init(&pms); MD5_APPEND( &pms, cred_info->username.ptr, cred_info->username.slen); MD5_APPEND( &pms, ":", 1); MD5_APPEND( &pms, realm->ptr, realm->slen); MD5_APPEND( &pms, ":", 1); MD5_APPEND( &pms, cred_info->data.ptr, cred_info->data.slen); pj_md5_final(&pms, digest); digest2str(digest, ha1); } else if ((cred_info->data_type & PASSWD_MASK) == PJSIP_CRED_DATA_DIGEST) { pj_assert(cred_info->data.slen == 32); pj_memcpy( ha1, cred_info->data.ptr, cred_info->data.slen ); } else { pj_assert(!"Invalid data_type"); } AUTH_TRACE_((THIS_FILE, " ha1=%.32s", ha1)); /*** *** ha2 = MD5(method ":" req_uri) ***/ pj_md5_init(&pms); MD5_APPEND( &pms, method->ptr, method->slen); MD5_APPEND( &pms, ":", 1); MD5_APPEND( &pms, uri->ptr, uri->slen); pj_md5_final(&pms, digest); digest2str(digest, ha2); AUTH_TRACE_((THIS_FILE, " ha2=%.32s", ha2)); /*** *** When qop is not used: *** response = MD5(ha1 ":" nonce ":" ha2) *** *** When qop=auth is used: *** response = MD5(ha1 ":" nonce ":" nc ":" cnonce ":" qop ":" ha2) ***/ pj_md5_init(&pms); MD5_APPEND( &pms, ha1, PJSIP_MD5STRLEN); MD5_APPEND( &pms, ":", 1); MD5_APPEND( &pms, nonce->ptr, nonce->slen); if (qop && qop->slen != 0) { MD5_APPEND( &pms, ":", 1); MD5_APPEND( &pms, nc->ptr, nc->slen); MD5_APPEND( &pms, ":", 1); MD5_APPEND( &pms, cnonce->ptr, cnonce->slen); MD5_APPEND( &pms, ":", 1); MD5_APPEND( &pms, qop->ptr, qop->slen); } MD5_APPEND( &pms, ":", 1); MD5_APPEND( &pms, ha2, PJSIP_MD5STRLEN); /* This is the final response digest. */ pj_md5_final(&pms, digest); /* Convert digest to string and store in chal->response. */ result->slen = PJSIP_MD5STRLEN; digest2str(digest, result->ptr); AUTH_TRACE_((THIS_FILE, " digest=%.32s", result->ptr)); AUTH_TRACE_((THIS_FILE, "Digest created")); }