OpenHarmony解读之设备认证:sts协议-服务端响应sts end请求
一、概述
在上文中说到,客户端根据服务端的start响应消息发起了end请求,本文将重点介绍服务端是如何处理和响应end请求的。
二、源码分析
这一模块的源码位于:/base/security/deviceauth。
1. 首先解析认证ack请求消息,parse_auth_ack_request函数,获取该消息中的关键字段,保存在相应结构体中。
/*函数功能:解析认证ack请求消息函数参数: payload:消息负载 data_type:数据类型函数返回值: 成功:返回解析完成的消息负载地址 失败:返回NULL*/void *parse_auth_ack_request(const char *payload, enum json_object_data_type data_type){ struct sts_end_request_data *auth_ack_request = (struct sts_end_request_data *)MALLOC(sizeof(struct sts_end_request_data));//申请sts_end_request_data结构体空间保存解析的数据 if (auth_ack_request == NULL) { return NULL; } (void)memset_s(auth_ack_request, sizeof(*auth_ack_request), 0, sizeof(*auth_ack_request));//清空该空间 json_handle obj = parse_payload(payload, data_type);//如果消息负载为json格式的字符串,则将json格式的数据解析成cjson结构体对象 if (obj == NULL) { LOGE("Parse Auth ACK Request parse payload failed"); goto error; } /* authData */ int32_t result = byte_convert(obj, FIELD_AUTH_DATA, auth_ack_request->auth_data.auth_data, (uint32_t *)&auth_ack_request->auth_data.length, HC_AUTH_DATA_BUFF_LEN);//获取authData,将原来的十六进制字符串格式转换为byte数组格式 if (result != HC_OK) { LOGE("Parse Auth ACK Request Data failed, field is null in addId"); goto error; } free_payload(obj, data_type); return (void *)auth_ack_request;//返回解析完成的结构体error: free_payload(obj, data_type); FREE(auth_ack_request); return NULL;}
2. 然后调用proc_sts_request_message函数处理请求消息,再这个函数里面,根据消息类型选择对应的处理方式,此处应是send_sts_end_response函数。
/*函数功能:构造sts协议end响应数据函数参数: handle:sts服务端对象 receive:接收到的消息 send:待发送的数据缓冲区地址函数返回值: 成功:0 失败:error num*/int32_t send_sts_end_response(struct sts_server *handle, struct message *receive, struct message *send){ DBG_OUT("Called send sts end response"); check_ptr_return_val(handle, HC_INPUT_ERROR);//检查参数有效性 check_ptr_return_val(receive, HC_INPUT_ERROR); check_ptr_return_val(send, HC_INPUT_ERROR); int32_t ret = HC_OK; struct sts_end_response_data *send_data = (struct sts_end_response_data *)MALLOC(sizeof(struct sts_end_response_data));//申请sts_end_response_data结构体空间send_data if (send_data == NULL) { LOGE("Malloc struct sts_end_response_data failed"); send->msg_code = INFORM_MESSAGE; return HC_MALLOC_FAILED; } (void)memset_s(send_data, sizeof(*send_data), 0, sizeof(*send_data));//清空该空间 if (handle->ack_request == 0) { struct sts_end_request_data *receive_data = receive->payload;//用sts_end_request_data结构接收消息负载 ret = send_end_response(handle, receive_data, send_data);//构造end响应消息 if (ret != HC_OK) {//执行失败 LOGE("Called send_end_response failed, error code is %d", ret); FREE(send_data); send_data = NULL; send->msg_code = INFORM_MESSAGE; } else {//执行成功 DBG_OUT("Called send_end_response success"); send->msg_code = AUTH_ACK_RESPONSE;//设置消息码为AUTH_ACK_RESPONSE send->payload = send_data;//赋值待发送的数据 handle->ack_request = 1;//将start_request标志置1 (void)memcpy_s(&handle->end_response_data, sizeof(struct sts_end_response_data), send_data, sizeof(struct sts_end_response_data)); } } else {//如果start_request不为0,则再次调用send_start_response DBG_OUT("Called send sts end response again"); send->msg_code = AUTH_ACK_RESPONSE;//置响应消息码为AUTH_ACK_RESPONSE (void)memcpy_s(send_data, sizeof(struct sts_end_response_data), &handle->end_response_data, sizeof(struct sts_end_response_data)); send->payload = send_data;//赋值消息负载 } return ret;}/*函数功能:发送end响应消息,首先解析end请求数据,然后构造end响应消息函数参数: handle:句柄,可用相关结构体获取 receive_data:接收的数据 send_data:待发送数据地址函数返回值: 成功:0 失败:error*/int32_t send_end_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 end request data and send end response data", base->sn); if (is_state_error(server, SEND_END_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_end_request_data(handle, receive_data);//执行解析函数解析end请求数据 if (ret != HC_OK) {//解析失败 set_state(base, PROTOCOL_ERROR); LOGE("Object %u parse end request data failed, error code is %d", base->sn, ret); return ret; } ret = funcs->build_end_response_data(handle, send_data);//调用回调函数构造end响应消息 if (ret != HC_OK) { set_state(base, PROTOCOL_ERROR); LOGE("Object %u build end response data failed, error code is %d", base->sn, ret); return ret; } set_state(base, PROTOCOL_FINISH);//设置协议状态为:PROTOCOL_FINISH set_last_time_sec(base);//设置上一次的时间 DBG_OUT("Object %u receive end request data send end response data succcess", base->sn); return HC_OK;}
3. 紧接着在上述函数中执行回调函数parse_end_request_data,解析end请求数据。这个函数的功能主要对客户端发来的认证数据进行解密,然后再根据ED25519算法进行签名验证。
/*函数功能:解析end请求数据函数参数: handle:sts服务端对象 receive_data:接收数的消息数据函数返回值: 成功:0 失败:error num*/static int32_t parse_end_request_data(void *handle, void *receive_data){ DBG_OUT("Called parse end request data"); struct sts_server *sts_server = handle;//接收sts服务端对象 struct sts_end_request_data *auth_ack_request = receive_data;//用sts_end_request_data结构体接收ack消息 struct uint8_buff out_plain;//定义输出明文缓冲区 out_plain.val = (uint8_t *)MALLOC(HC_STS_REQUEST_LEN);//为明文缓冲区申请空间 if (out_plain.val == NULL) { LOGE("Malloc failed"); return HC_MALLOC_FAILED; } (void)memset_s(out_plain.val, HC_STS_REQUEST_LEN, 0, HC_STS_REQUEST_LEN);//清空该空间 out_plain.size = HC_STS_REQUEST_LEN;//赋值缓冲区大小为HC_STS_REQUEST_LEN out_plain.length = 0;//缓冲区实际数据大小 int32_t ret = decrypt_request_message(handle, auth_ack_request, &out_plain);//解密请求消息,即解密对端发来的认证数据 if (ret != HC_OK) {//解密失败 FREE(out_plain.val); LOGE("Object %u aes_gcm_decrypt failed, error code is %d", sts_server_sn(sts_server), ret); return HC_DECRYPT_FAILED; } struct signature signature;//定义签名 //将解密后的签名消息拷贝到signature中作为对比签名 if (memcpy_s(signature.signature, sizeof(signature.signature), out_plain.val, out_plain.length) != EOK) { FREE(out_plain.val); return memory_copy_error(__func__, __LINE__); } signature.length = out_plain.length;//获取长度 FREE(out_plain.val); struct uint8_buff message;//定义消息缓冲区 (void)memset_s(&message, sizeof(message), 0, sizeof(message));//清空该缓冲区 ret = generate_sign_message(handle, &message);//生成本端的签名消息:{客户端公钥、客户端认证id、服务端公钥、服务端认证id} if (ret != HC_OK) { return HC_MALLOC_FAILED; } ret = verify_request_data(handle, &message, &signature);//验证请求数据是否正确,即验证对端身份 FREE(message.val); message.val = NULL; if (ret != HC_OK) { LOGE("Object %u verify failed, error code is %d", sts_server_sn(sts_server), ret); return HC_VERIFY_PROOF_FAILED; } LOGI("Called parse end request data success!"); (void)sts_server; return HC_OK;}/*函数功能:解密请求消息函数参数: handle:sts_server对象 request_data:待解密请求数据函数返回值: 成功:0 失败:error num*/static int32_t decrypt_request_message(void *handle, struct sts_end_request_data *request_data, struct uint8_buff *out_plain){ DBG_OUT("Called decrypt request message"); check_ptr_return_val(handle, HC_INPUT_ERROR); check_ptr_return_val(request_data, HC_INPUT_ERROR); struct sts_server *sts_server = handle;//接收sts服务端对象 struct aes_aad aes_aad;//aes GCM附加验证数据 if (memcpy_s(aes_aad.aad, sizeof(aes_aad.aad), sts_server->my_challenge.challenge, sts_server->my_challenge.length) != EOK) {//将本端challenge值拷贝到aes_aad return memory_copy_error(__func__, __LINE__); } aes_aad.length = sts_server->my_challenge.length;//获取长度 struct uint8_buff auth_data = {//定义认证数据缓冲区 .val = request_data->auth_data.auth_data, .length = request_data->auth_data.length, .size = request_data->auth_data.length }; //利用aes gcm算法基于会话密钥和aes_aad对认证数据进行解密,解密内容保存在out_plain中 int32_t ret = aes_gcm_decrypt((struct var_buffer *)&sts_server->session_key, &auth_data, &aes_aad, out_plain); return ret;}/*函数功能:生成签名消息:{客户端公钥、客户端认证id、服务端公钥、服务端认证id}函数参数: handle:sts_server对象 message:输出参数,签名消息缓冲区函数返回值: 成功:0 失败:error num*/static int32_t generate_sign_message(void *handle, struct uint8_buff *message){ DBG_OUT("Called generate sign message"); check_ptr_return_val(handle, HC_INPUT_ERROR);//检查参数有效性 check_ptr_return_val(message, HC_INPUT_ERROR); struct sts_server *sts_server = handle;//接收sts服务端对象 //客户端公钥、客户端认证id、服务端公钥、服务端认证id长度之和 int32_t len = sts_server->peer_public_key.length + sts_server->peer_id.length + sts_server->self_public_key.length + sts_server->self_id.length; uint8_t *info1 = (uint8_t *)MALLOC(len);//申请len长度的缓冲区info1 if (info1 == NULL) { LOGE("Malloc failed"); return HC_MALLOC_FAILED; } int32_t pos = 0; //将客户端公钥、客户端认证id、服务端公钥、服务端认证id按顺序拷贝到info1缓冲区中 (void)memcpy_s(info1 + pos, len - pos, sts_server->peer_public_key.stpk, sts_server->peer_public_key.length); pos += sts_server->peer_public_key.length; (void)memcpy_s(info1 + pos, len - pos, sts_server->peer_id.auth_id, sts_server->peer_id.length); pos += sts_server->peer_id.length; (void)memcpy_s(info1 + pos, len - pos, sts_server->self_public_key.stpk, sts_server->self_public_key.length); pos += sts_server->self_public_key.length; (void)memcpy_s(info1 + pos, len - pos, sts_server->self_id.auth_id, sts_server->self_id.length); //即 info1 = {客户端公钥、客户端认证id、服务端公钥、服务端认证id} message->val = info1;//将上述内容赋给签名消息缓冲区 message->length = len; message->size = len; return HC_OK;}/*函数功能:验证请求数据函数参数: handle:sts_server对象 message:待验证的消息 signature:对比签名函数返回值: ok:0 not ok:error num*/static int32_t verify_request_data(void *handle, const struct uint8_buff *message, struct signature *signature){ DBG_OUT("Called verify request data"); check_ptr_return_val(handle, HC_INPUT_ERROR);//检查参数有效性 check_ptr_return_val(message, HC_INPUT_ERROR); check_ptr_return_val(signature, HC_INPUT_ERROR); struct sts_server *sts_server = handle;//接收sts服务端对象 //利用会话标识符的HC包名称和HC服务类型通过sha256哈希算法计算出哈希值,作为服务id struct service_id service_id = generate_service_id(sts_server->identity); if (service_id.length == 0) { LOGE("Generate service id failed"); return HC_GEN_SERVICE_ID_FAILED; } struct hichain *hichain_handle = sts_server->hichain_handle;//获取hichain实例 enum huks_key_alias_type alias_type;//密钥别名类型 if (hichain_handle->type == HC_CENTRE) {//根据hc类型决定密钥别名(密钥对)类型 struct hc_pin pin = { 0, {0} };//PIN码 struct operation_parameter para;//协议操作参数 (void)memset_s(¶, sizeof(para), 0, sizeof(para));//清空参数空间 //获取协议参数 hichain_handle->cb.get_protocol_params(&hichain_handle->identity, GET_PEER_USER_TYPE, &pin, ¶); int32_t flag = sts_server->peer_user_type;//对端用户类型 if (flag == PEER_USER_TYPE_EMPTY) { sts_server->peer_user_type = para.key_length; } if (para.key_length == PEER_USER_TYPE_ACCESSORY) { sts_server->peer_user_type = HC_USER_TYPE_ACCESSORY; } if (sts_server->peer_user_type == HC_USER_TYPE_CONTROLLER) {//如果对端类型为HC_USER_TYPE_CONTROLLER if (memcmp(sts_server->peer_id.auth_id, sts_server->self_id.auth_id, sts_server->peer_id.length) == 0) { alias_type = KEY_ALIAS_LT_KEY_PAIR; /* center(as phone identity) -> phone */ } else { alias_type = KEY_ALIAS_CONTROLLER_PK; /* center -> phone (other identity) */ } } else { alias_type = KEY_ALIAS_ACCESSOR_PK; /* center -> accessory */ } } else { alias_type = KEY_ALIAS_CONTROLLER_PK; /* accessory -> center/phone */ } //根据服务id和对端认证id生成密钥别名 struct hc_key_alias key_alias = generate_key_alias(&service_id, &sts_server->peer_id, alias_type); if (key_alias.length == 0) { LOGE("Generate key alias failed"); return HC_GEN_ALIAS_FAILED; } //通过ED25519长期公钥验证签名是否正确(用对端公钥解密) int32_t ret = verify(&key_alias, sts_server->peer_user_type, message, signature); if (ret != HC_OK) { LOGE("Object %u verify failed, error code is %d", sts_server_sn(sts_server), ret); return HC_VERIFY_PROOF_FAILED; } return HC_OK;}
4. 然后继续执行build_end_response_data回调函数,构造end响应数据。
/*函数功能:构造end响应数据函数参数: handle:sts_client对象 data:待发送数据函数返回值: 成功:0 失败:error num*/static int32_t build_end_response_data(void *handle, void *send_data){ DBG_OUT("Called build end response_data"); struct sts_server *sts_server = handle;//接收sts服务端对象 struct sts_end_response_data *auth_start_response = send_data;//sts_end_response_data数据结构接收该数据 struct uint8_buff auth_ret;//定义认证返回数据缓冲区 auth_ret.size = HC_STS_RESPONSE_LEN;//获取长度 auth_ret.val = (uint8_t *)MALLOC(auth_ret.size);//为该缓冲区申请空间 if (auth_ret.val == NULL) { LOGE("Malloc failed"); return HC_MALLOC_FAILED; } (void)memset_s(auth_ret.val, auth_ret.size, 0, auth_ret.size);//清空该空间 auth_ret.length = 0; struct aes_aad aes_aad;//aes GCM附加验证数据 if (memcpy_s(aes_aad.aad, sizeof(aes_aad.aad), sts_server->peer_challenge.challenge, sts_server->peer_challenge.length) != EOK) {//将对端challenge值拷贝到aes_aad FREE(auth_ret.val); return memory_copy_error(__func__, __LINE__); } aes_aad.length = sts_server->peer_challenge.length;//获取长度 struct uint8_buff ret_code;//定义返回码缓冲区 ret_code.val = (uint8_t *)MALLOC(sizeof(uint8_t));//为返回码缓冲区申请空间 if (ret_code.val == NULL) { FREE(auth_ret.val); LOGE("Malloc failed"); return HC_MALLOC_FAILED; } ret_code.length = 1;//初始化返回码 ret_code.size = 1; ret_code.val[0] = 0; //对返回码进行aes gcm加密,加密结果保存在auth_ret中 int32_t ret = aes_gcm_encrypt((struct var_buffer *)&sts_server->session_key, &ret_code, &aes_aad, &auth_ret); FREE(ret_code.val); if (ret != HC_OK) {//加密失败 FREE(auth_ret.val); LOGE("Object %u aes_gcm_encrypt failed, err code is %d", sts_server_sn(sts_server), ret); return HC_ENCRYPT_FAILED; } //利用session_key、salt、"hichain_return_key"等信息基于hkdf算法生成派生密钥service_key generate_output_key(sts_server); if (memcpy_s(auth_start_response->auth_return.auth_return, sizeof(auth_start_response->auth_return.auth_return), auth_ret.val, auth_ret.length) != EOK) {//将加密后的auth_data拷贝到待发送的响应消息体中 FREE(auth_ret.val); return memory_copy_error(__func__, __LINE__); } auth_start_response->auth_return.length = auth_ret.length;//赋值加密认证数据长度 FREE(auth_ret.val); LOGI("Called build end response data success!"); return HC_OK;}//生成output_keystatic void generate_output_key(struct sts_server *sts_server){ DBG_OUT("sts_server generate output key"); //利用session_key、salt、"hichain_return_key"等信息基于hkdf算法生成派生密钥service_key int32_t ret = compute_hkdf((struct var_buffer *)&sts_server->session_key, &sts_server->salt, HICHAIN_RETURN_KEY, sts_server->key_length, (struct var_buffer *)&sts_server->service_key); if (ret != HC_OK) { LOGE("Object %u generate output key failed, error code is %d", sts_server_sn(sts_server), ret); }}
5. 最后执行make_auth_ack_response函数,构造json格式的认证ack响应消息。
/*函数功能:构造json格式的认证ack响应消息函数参数: data:待发送数据函数返回值: 成功:json格式的字符串 失败:NULL*/char *make_auth_ack_response(void *data){ struct sts_end_response_data *auth_ack_response = data;//用sts_end_response_data结构接收数据 /* authdata */ uint8_t *tmp_data_hex = raw_byte_to_hex_string(auth_ack_response->auth_return.auth_return, auth_ack_response->auth_return.length);//将原始的authdata字节数据转换为十六进制的字符串 if (tmp_data_hex == NULL) { return NULL; } char *ret_str = (char *)MALLOC(RET_STR_LENGTH);//为json字符串申请空间 if (ret_str == NULL) { FREE(tmp_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\":%d,\"%s\":{\"%s\":\"%s\"}}", FIELD_AUTH_FORM, AUTH_FORM, FIELD_MESSAGE, AUTH_ACK_RESPONSE, FIELD_PAYLOAD, FIELD_AUTH_RETURN, tmp_data_hex) < 0) { LOGE("String generate failed"); FREE(ret_str); ret_str = NULL; }//生成json格式的请求消息 FREE(tmp_data_hex); return ret_str;}
三、小结
经过分析,得到服务端的响应消息格式为:
{ "authForm":0, //AUTH_FORM "message":0x8012, //消息码:AUTH_ACK_RESPONSE "payload": { "authReturn":"十六进制格式的字符串" //认证返回值 }}