鸿蒙设备开发OpenHarmony解读之设备认证:pake协议-服务端响应pake start请求(2)
📌往期推文全新看点(文中附带最新·鸿蒙全栈学习笔记)
📃 鸿蒙(HarmonyOS)北向开发知识点记录~
📃 鸿蒙(OpenHarmony)南向开发保姆级知识点汇总~
📃 鸿蒙应用开发与鸿蒙系统开发哪个更有前景?
📃 嵌入式开发适不适合做鸿蒙南向开发?看完这篇你就了解了~
📃 对于大前端开发来说,转鸿蒙开发究竟是福还是祸?
📃 鸿蒙岗位需求突增!移动端、PC端、IoT到底该怎么选?
📃 记录一场鸿蒙开发岗位面试经历~
📃 持续更新中……
一、概述
本文将继续介绍服务端响应pake协议的start请求的过程,承接上文: OpenHarmony解读之设备认证:pake协议-服务端响应pake start请求(1) 。
二、源码分析
这一模块的源码位于:/base/security/deviceauth。
1. 在上文中创建完pake服务端对象之后,开始进入消息处理阶段,根据协议模块字段在全局分布式消息表中查询对应的消息处理函数,此处应该是proc_pake_request_message函数。
/*函数功能:处理pake请求消息函数参数: handle:hichain实例 nav:消息头分析导航 receive:接收的消息 send:待发送消息地址函数返回值: 成功:0 失败:error*/static int32_t proc_pake_request_message(struct hichain *handle, struct header_analysis *nav, struct message *receive, struct message *send)#if !(defined(_CUT_PAKE_) || defined(_CUT_PAKE_SERVER_)){ DBG_OUT(\"Object %u proc pake %d request message\", pake_server_sn(handle->pake_server), nav->msg_type); int32_t ret; if (nav->msg_type == PAKE_START_MSG) {//如果是start请求 ret = send_pake_start_response(handle->pake_server, receive, send);//针对start请求构造发送响应消息 } else if (nav->msg_type == PAKE_END_MSG) {//如果是end请求 ret = send_pake_end_response(handle->pake_server, receive, send);//针对end请求发送响应消息 if (ret == HC_OK) { handle->cb.set_session_key(&handle->identity, &handle->pake_server->service_key);//设置会话密钥为service_key (void)memset_s(handle->pake_server->service_key.session_key, HC_SESSION_KEY_LEN, 0, HC_SESSION_KEY_LEN);//清空会话密钥空间 } } else { return HC_UNKNOW_MESSAGE; } return ret;}
2. send_pake_start_response函数,构造针对start请求发送响应消息。
/*函数功能:构造针对start请求发送响应消息函数参数: pake_server:pake服务端对象 receive:接收消息地址 send:待发送消息缓冲区函数返回值: 成功:0 失败:error*/int32_t send_pake_start_response(struct pake_server *pake_server, struct message *receive, struct message *send){ check_ptr_return_val(pake_server, HC_INPUT_ERROR);//检查参数有效性 check_ptr_return_val(receive, HC_INPUT_ERROR); check_ptr_return_val(send, HC_INPUT_ERROR); DBG_OUT(\"Receive pake start request message object %u success\", pake_server_sn(pake_server)); struct pake_start_request_data *receive_data = receive->payload;//获取start请求数据 struct pake_start_response_data *send_data = (struct pake_start_response_data *)MALLOC(sizeof(struct pake_start_response_data));//申请发送缓冲区空间 if (send_data == NULL) { LOGE(\"Malloc struct pake_start_response_data failed\"); send->msg_code = INFORM_MESSAGE; return HC_MALLOC_FAILED; } (void)memset_s(send_data, sizeof(*send_data), 0, sizeof(*send_data));//清空该空间 int32_t ret = send_start_response(pake_server, receive_data, send_data);//构造start请求响应消息 if (ret != HC_OK) { LOGE(\"Object %u called send_start_response failed, error code is %d\", pake_server_sn(pake_server), ret); FREE(send_data); send_data = NULL; send->msg_code = INFORM_MESSAGE; } else { LOGI(\"Object %u called send_start_response success\", pake_server_sn(pake_server)); send->msg_code = PAKE_RESPONSE;//赋值消息码为PAKE_RESPONSE send->payload = send_data;//赋值消息负载部分 } return ret;}
3. send_start_response函数,构造start响应消息。
/*函数功能:构造start响应消息函数参数: handle:用于接收hichain对象 receive_data:接收的数据 send_data:待发送数据地址函数返回值: 成功:0 失败:error*/int32_t send_start_response(void *handle, void *receive_data, void *send_data){ check_ptr_return_val(handle, HC_INPUT_ERROR);//检查参数有效性 check_ptr_return_val(receive_data, HC_INPUT_ERROR); check_ptr_return_val(send_data, HC_INPUT_ERROR); struct key_agreement_server *server = handle; struct key_agreement_protocol *base = &server->protocol_base_info;//赋值协议基础信息 DBG_OUT(\"Object %u begin receive start request data and send start response data\", base->sn); if (is_state_error(server, SEND_START_RESPONSE)) {//判断协议状态是否错误 LOGE(\"Object %u state error\", base->sn); return PROTOCOL_STATE_ERROR; } const struct server_virtual_func_group *funcs = &server->package_funcs;//赋值服务端打包函数 int32_t ret = funcs->parse_start_request_data(handle, receive_data);//执行解析函数解析接收数据 if (ret != HC_OK) { set_state(base, PROTOCOL_ERROR); LOGE(\"Object %u parse start request data failed, error code is %d\", base->sn, ret); return ret; } ret = funcs->build_start_response_data(handle, send_data);//调用回调函数构造start响应消息 if (ret != HC_OK) { set_state(base, PROTOCOL_ERROR);//设置协议状态为PROTOCOL_ERROR LOGE(\"Object %u build start response data failed, error code is %d\", base->sn, ret); return ret; } set_state(base, START_RESPONSE);//设置协议状态为START_RESPONSE set_last_time_sec(base); DBG_OUT(\"Object %u receive start request data send start response data succcess\", base->sn); return HC_OK;}
4. parse_start_request_data虚函数,解析start请求数据,实际上就是为了获取大素数长度。
/*函数功能:解析start请求数据,实际上就是为了获取大素数长度函数参数: handle:pake服务端对象 receive_data:接收的数据函数返回值: 成功:0 失败:error*/static int32_t parse_start_request_data(void *handle, void *receive_data){ struct pake_server *pake_server = handle;//获取服务端对象 struct pake_start_request_data *data = receive_data;//获取接收数据 DBG_OUT(\"Object %u begin parse PAKE start request data\", pake_server_sn(pake_server)); if (!is_peer_support_current_version(&data->peer_version, &data->peer_support_version)) {//检查对端是否支持当前版本 LOGE(\"Object %u peer is not support current version\", pake_server_sn(pake_server)); return HC_VERSION_UNSUPPORT; } if (data->epk_len == HC_BIG_PRIME_MAX_LEN_384) { pake_server->prime_type = NUM_LEN_384;//赋值大素数长度为384 } else if (data->epk_len == HC_BIG_PRIME_MAX_LEN_256) { pake_server->prime_type = NUM_LEN_256;//赋值大素数长度为256 } else { LOGE(\"Object %u peer is not support big number len %d\", pake_server_sn(pake_server), data->epk_len); return HC_LARGE_PRIME_NUMBER_LEN_UNSUPPORT; } return HC_OK;}
5. build_start_response_data函数,构造响应消息。
/*函数功能:构造响应start请求的数据函数参数: handle:pake服务端对象 send_data:待发送的数据缓冲区函数返回值: 成功:0 失败:error*/static int32_t build_start_response_data(void *handle, void *send_data){ struct pake_server *pake_server = handle;//获取服务端对象 struct pake_start_response_data *data = send_data;//响应start请求的数据结构 DBG_OUT(\"Object %u begin build PAKE start response data\", pake_server_sn(pake_server)); struct epk self_epk = { 0, {0} };//初始化本端epk if (generate_pake_params(pake_server, &self_epk) != HC_OK) {//生成pake协议参数 LOGE(\"Object %u internal calculation error\", pake_server_sn(pake_server)); return HC_INTERNAL_ERROR; } data->salt = pake_server->salt;//赋值salt值 data->challenge = pake_server->self_challenge;//赋挑战值 data->epk = self_epk;//赋值epk data->self_version = get_current_version();//获取本端当前协议版本为(1,0,0) data->self_support_version = get_current_version();//获取本端可支持的协议版本为(1,0,0) return HC_OK;}
6. generate_pake_params函数,生成pake协议参数。
/*函数功能:生成pake协议参数函数参数: pake_server:pake服务端对象 self_epk:本端epk函数返回值: 成功:0 失败:error number*/static int32_t generate_pake_params(struct pake_server *pake_server, struct epk *self_epk){ if (pake_server->server_info.protocol_base_info.state == PROTOCOL_INIT) {//如果协议状态为PROTOCOL_INIT pake_server->salt = get_salt();//获取随机salt值 pake_server->self_challenge = get_challenge();//获取随机挑战值 } uint32_t esk_len = (pake_server->prime_type == NUM_LEN_384) ? PAKE_ESK_LENGTH : PAKE_ESK_SHORT_LENGTH;//获取esk密钥长度 uint32_t prime_len = (pake_server->prime_type == NUM_LEN_384) ? HC_BIG_PRIME_MAX_LEN_384 : HC_BIG_PRIME_MAX_LEN_256;//获取质数长度 if (pake_server->server_info.protocol_base_info.state == PROTOCOL_INIT) {//如果协议状态为PROTOCOL_INIT struct random_value rand = generate_random(esk_len);//生成随机值 if (rand.length == 0) { LOGE(\"Generate random value failed\"); return HC_GEN_RANDOM_FAILED; } pake_server->self_esk = *(struct esk *)&rand;//将生成的随机值赋给本端esk } struct hkdf secret = { 0, {0} };//hkdf派生密钥 int32_t ret = compute_hkdf((struct var_buffer *)&pake_server->pin, &pake_server->salt, HICHAIN_SPEKE_BASE_INFO, HC_HKDF_SECRET_LEN, (struct var_buffer *)&secret);//基于HKDF算法的密钥派生函数,根据密钥派生种子和salt值计算出派生密钥保存在secret中 if (ret != HC_OK) { LOGE(\"Object %u generate hkdf failed, error code is %d\", pake_server_sn(pake_server), ret); return ret; } struct exponent exp = {.length = 1};//初始化指数exp exp.exp[0] = 2; /* square */ struct epk base = { 0, {0} }; ret = cal_bignum_exp((struct var_buffer *)&secret, (struct var_buffer *)&exp, prime_len, (struct big_num *)&base);//计算大素数指数输出到base中 if (ret != HC_OK) { return HC_CAL_BIGNUM_EXP_FAILED; } ret = cal_bignum_exp((struct var_buffer *)&base, (struct var_buffer *)&pake_server->self_esk, prime_len, (struct big_num *)self_epk);//再次计算大素数指数作为本端临时公钥self_epk if (ret != HC_OK) { return HC_CAL_BIGNUM_EXP_FAILED; } return HC_OK;}/*函数功能:获取salt值函数参数:无函数返回值:返回salt值*/static struct hc_salt get_salt(void){ struct hc_salt salt = { 0, {0} };//初始化salt值 struct random_value rand = generate_random(HC_SALT_BUFF_LEN);//获取随机值 if (rand.length == HC_SALT_BUFF_LEN) { DBG_OUT(\"Generate salt success\"); salt.length = rand.length; (void)memcpy_s(salt.salt, sizeof(salt.salt), rand.random_value, HC_SALT_BUFF_LEN);//将生成的随机值拷贝到salt值中 } else { LOGE(\"Generate salt failed\"); } return salt;//返回随机的salt值}/*函数功能:获取挑战值函数参数:无函数返回值:返回生成的随机挑战值*/static struct challenge get_challenge(void){ struct challenge challenge = { 0, {0} };//初始化挑战值 struct random_value rand = generate_random(CHALLENGE_BUFF_LENGTH);//获取随机值 if (rand.length == CHALLENGE_BUFF_LENGTH) { DBG_OUT(\"Generate challenge success\"); challenge.length = rand.length; (void)memcpy_s(challenge.challenge, sizeof(challenge.challenge), rand.random_value, CHALLENGE_BUFF_LENGTH);//拷贝随机值到挑战值中 } else { LOGE(\"Generate challenge failed\"); } return challenge;}
7. 处理解析完请求数据并准备好待发送的数据之后,回到receive_data函数中,调用build_send_data_by_struct构造结构化的发送消息。然后在build_send_data_by_struct函数中选择相应的回调函数进行消息构造,此处应该是make_pake_response函数。然后调用软总线模块的数据传输接口发送给对端。
/*函数功能:构造pake响应消息函数参数: data:待发送的数据函数返回值:json格式的字符串*/char *make_pake_response(void *data){ struct pake_start_response_data *pake_response = data;//用pake_start_response_data结构接收待发送数据 /* challenge */ uint8_t *tmp_cha_data_hex = raw_byte_to_hex_string(pake_response->challenge.challenge, pake_response->challenge.length);//将challenge值转换为十六进制格式的字符串 if (tmp_cha_data_hex == NULL) { return NULL; } /* salt */ uint8_t *tmp_salt_data_hex = raw_byte_to_hex_string(pake_response->salt.salt, pake_response->salt.length);//将salt值转换为十六进制格式的字符串 if (tmp_salt_data_hex == NULL) { FREE(tmp_cha_data_hex); return NULL; } /* epk */ uint8_t *tmp_epk_data_hex = raw_byte_to_hex_string(pake_response->epk.epk, pake_response->epk.length);//将epk值转换为十六进制格式的字符串 if (tmp_epk_data_hex == NULL) { FREE(tmp_cha_data_hex); FREE(tmp_salt_data_hex); return NULL; } char *ret_str = (char *)MALLOC(RET_STR_LENGTH);//申请2048字节的空间以存放待发送消息 if (ret_str == NULL) { FREE(tmp_cha_data_hex); FREE(tmp_salt_data_hex); FREE(tmp_epk_data_hex); return NULL; } (void)memset_s(ret_str, RET_STR_LENGTH, 0, RET_STR_LENGTH);//清空该地址空间 if (snprintf_s(ret_str, RET_STR_LENGTH, RET_STR_LENGTH - 1, \"{\\\"%s\\\":%d,\\\"%s\\\":{\\\"%s\\\":{\\\"%s\\\":\\\"%u.%u.%u\\\",\\\"%s\\\":\\\"%u.%u.%u\\\"},\" \"\\\"%s\\\":\\\"%s\\\",\\\"%s\\\":\\\"%s\\\",\\\"%s\\\":\\\"%s\\\"}}\", FIELD_MESSAGE, PAKE_RESPONSE, FIELD_PAYLOAD, FIELD_VERSION, FIELD_CURRENT_VERSION, pake_response->self_version.first, pake_response->self_version.second, pake_response->self_version.third, FIELD_MIN_VERSION, pake_response->self_support_version.first, pake_response->self_support_version.second, pake_response->self_support_version.third, FIELD_CHALLENGE, (char *)tmp_cha_data_hex, FIELD_SALT, (char *)tmp_salt_data_hex, FIELD_EPK, (char *)tmp_epk_data_hex) < 0) { LOGE(\"String generate failed\"); FREE(ret_str); ret_str = NULL; }//生成json格式的消息字符串 FREE(tmp_cha_data_hex); FREE(tmp_salt_data_hex); FREE(tmp_epk_data_hex); return ret_str;}
三、小结
至此,服务端响应pake的start请求的过程结束,经过分析得到响应的消息格式如下:
{ \"message\":0x8001, //消息码为PAKE_RESPONSE \"payload\": { \"version\": { \"currentVersion\":\"1.0.0\", \"minVersion\":\"1.0.0\" }, \"challenge\":\"十六进制格式的字符串\" //基于“挑战/响应”方式的挑战值,是一个随机值 \"salt\":\"十六进制格式的字符串\" //用于身份认证的随机salt值 \"epk\":\"十六进制格式的字符串\" //临时公钥,也是一个随机值 }}
在服务端收到客户端发起的start请求之后,服务端生成本端的challenge值、salt值、和临时公钥epk以及自己的版本号,将这些内容封装成响应消息回复给客户端,这个过程表示服务端支持该客户端发起的认证会话密钥协商请求,密钥协商过程是基于**“挑战/响应”(Challenge/Response)** 方式。



