一、概述
本文将继续介绍服务端响应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)
方式的,关于这种方式的原理可以参考相关在线资料。
版权声明:本文为weixin_46669761原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。