一、概述
为实现用户个人数据在多个终端设备间的安全传输,设备认证模块提供将多个设备安全连接起来的能力,通过设备间信任关系建立和设备通信时信任关系验证保证安全性。主控设备和配件设备基于PAKE协议 完成认证会话密钥协商,并基于该会话密钥,安全的交换各自身份公钥。 当建立过信任关系的主控设备与配件设备间进行通信时,双方将相互交换身份公钥,并通过检查本地是否存储对端身份信息的方式确认对端与本设备的信任关系。进一步地,基于双方的身份公私钥对,通信对端设备可以基于STS协议进行密钥协商并建立安全通信通道,支撑设备间通信数据的端到端加密传输。 从本文开始,将介绍基于STS协议的通信过程,本文重点介绍sts客户端发起start请求的过程。
二、源码分析
这一模块的源码位于:/base/security/deviceauth。
1. authenticate_peer函数:与对端进行身份认证。
scss
/*
函数功能:与对端进行身份认证
函数参数:
handle:句柄,可用相应结构套取
params:操作参数
函数返回值:
成功:0
失败:error num
*/
DLL_API_PUBLIC int32_t authenticate_peer(hc_handle handle, struct operation_parameter *params)
{
LOGI("Begin authenticate peer");//开始身份验证
check_ptr_return_val(handle, HC_INPUT_ERROR);//检查参数有效性
check_ptr_return_val(params, HC_INPUT_ERROR);
struct hichain *hichain = (struct hichain *)handle;//hichain实例
int32_t ret = build_object(hichain, STS_MODULAR, true, params);//创建hichain子对象,此处应为创建sts客户端对象
if (ret != HC_OK) {
LOGE("Build sts client sub object failed, error code is %d", ret);
return ret;
}
ret = triggered_sts_client(hichain, AUTHENTICATE);//触发sts客户端发起AUTHENTICATE请求
LOGI("Triggered sts client error code is %d", ret);
LOGI("End authenticate peer");
return ret;
}
2. build_sts_client_object函数:创建sts客户端对象。
rust
//创建sts客户端对象
static void *build_sts_client_object(struct hichain *hichain, const void *params)
{
struct operation_parameter *para = (struct operation_parameter *)params;//接收操作参数
return build_sts_client(hichain, para->key_length, ¶->self_auth_id,
¶->peer_auth_id);//创建sts客户端对象
}
/*
函数功能:创建sts客户端对象
函数参数:
hichain_handle:hichain实例
key_length:密钥长度
client:客户端认证id
server:服务端认证id
函数返回值:
成功:返回sts_client对象
失败:NULL
*/
struct sts_client *build_sts_client(const hc_handle hichain_handle, uint32_t key_length,
const struct hc_auth_id *client, const struct hc_auth_id *server)
{
struct sts_client *sts_client = (struct sts_client *)MALLOC(sizeof(struct sts_client));//为该对象申请内存空间
if (sts_client == NULL) {
LOGE("Build sts client object failed");
return NULL;
}
(void)memset_s(sts_client, sizeof(*sts_client), 0, sizeof(*sts_client));//清空该空间
struct client_virtual_func_group funcs = { build_start_request_data, parse_start_response_data,
build_end_request_data, parse_end_response_data };//定义并初始化客户端虚函数组
init_client(&sts_client->client_info, &funcs);//初始化客户端,包括协议基础信息(如初始状态等)和打包函数
DBG_OUT("Build sts client object %u success", sts_client_sn(sts_client));
sts_client->self_id = *client;//赋值本端认证id
sts_client->peer_id = *server;//赋值对端认证id
sts_client->identity = &((struct hichain *)hichain_handle)->identity;//赋值会话标识
sts_client->key_length = key_length;//赋值密钥长度
sts_client->hichain_handle = hichain_handle;//保存hichain实例
return sts_client;//返回创建好的对象
}
3. 回到authenticate_peer函数中,继续执行triggered_sts_client函数:触发sts客户端发起AUTHENTICATE请求。
scss
/*
函数功能:触发sts客户端发起请求
函数参数:
hichain:hichain实例
operation_code:操作码
函数返回值:
成功:0
失败:error num
*/
static int32_t triggered_sts_client(struct hichain *hichain, int32_t operation_code)
#if !(defined(_CUT_STS_) || defined(_CUT_STS_CLIENT_))
{
hichain->operation_code = operation_code;//获取操作码赋值给hichain实例
hichain->sts_client->operation_code = operation_code;////获取操作码赋值给sts_client对象
struct message send = {//定义发送消息体并初始化
.msg_code = AUTH_START_REQUEST,//初始化消息码为AUTH_START_REQUEST
.rsv = 0,
.payload = NULL
};
int32_t ret = send_sts_start_request(hichain->sts_client, &send);//构造sts start请求数据,准备相关信息保存到send消息中
if (ret != HC_OK) {//构建失败
LOGE("Object %u build sts start request failed, error code is %d", sts_client_sn(hichain->sts_client), ret);
return HC_BUILD_SEND_DATA_FAILED;
}
void *send_data = NULL;//定义发送数据指针
uint32_t send_data_len = 0;//初始化待发送数据长度
ret = build_send_data_by_struct(&send, &send_data, &send_data_len);//根据相应的结构体构造发送数据
if (ret != HC_OK) {
LOGW("build send data failed, error code is %d", ret);
} else {
DBG_OUT("send_data:%s", (uint8_t *)send_data);
hichain->cb.transmit(&hichain->identity, send_data, send_data_len);//调用软总线模块的数据传输接口进行消息发送,消息起始地址为send_data,长度为send_data_len
FREE(send_data);
}
set_result(hichain, INVALID_MESSAGE, AUTH_START_REQUEST, ret);//根据接收消息码和发送消息码设置hichain结果和状态
destroy_send_data(&send);//释放send相关内存空间
return ret;
}
4. send_sts_start_request函数:构造sts协议的start请求,为该请求准备相关数据。
scss
/*
函数功能:构造sts协议的start请求,为该请求准备相关数据
函数参数:
sts_client:sts客户端对象
send:发送消息体
函数返回值:
成功:0
失败:error num
*/
int32_t send_sts_start_request(struct sts_client *sts_client, struct message *send)
{
check_ptr_return_val(sts_client, HC_INPUT_ERROR);//检查参数有效性
check_ptr_return_val(send, HC_INPUT_ERROR);
struct sts_start_request_data *send_data =
(struct sts_start_request_data *)MALLOC(sizeof(struct sts_start_request_data));//为sts_start_request_data结构的待发送请求数据申请空间
if (send_data == NULL) {//申请失败
LOGE("Malloc struct STS_START_REQUEST_DATA failed");
return HC_MALLOC_FAILED;
}
(void)memset_s(send_data, sizeof(*send_data), 0, sizeof(*send_data));//清空该空间
int32_t ret = send_start_request(sts_client, send_data);//构造start请求,此处应是sts协议的start请求
if (ret != HC_OK) {//执行失败
LOGE("Called send_start_request failed, error code is %d", ret);
FREE(send_data);
send->msg_code = INFORM_MESSAGE;//设置消息码为通知消息
} else {//执行成功
DBG_OUT("Called send_start_request success");
send->msg_code = AUTH_START_REQUEST;//设置消息码为AUTH_START_REQUEST
send->payload = send_data;//将准备好的请求数据赋给发送消息体的负载
}
return ret;
}
5. send_start_request函数:构造start请求数据。
scss
/*
函数功能:构造start请求数据
函数参数:
handle:句柄
send_data:待发送数据地址
函数返回值:
成功:0
失败:error
*/
int32_t send_start_request(void *handle, void *send_data)
{
check_ptr_return_val(handle, HC_INPUT_ERROR);//检查参数有效性
check_ptr_return_val(send_data, HC_INPUT_ERROR);
struct key_agreement_client *client = (struct key_agreement_client *)handle;//密钥协商客户端
struct key_agreement_protocol *base = &client->protocol_base_info;//基础密钥协议信息
DBG_OUT("Object %u begin send start request data", base->sn);
if (is_state_error(client, SEND_START_REQUEST)) {//判断协议动作和协议状态是否对应错误
LOGE("Object %u state error", base->sn);
return PROTOCOL_STATE_ERROR;
}
struct client_virtual_func_group *funcs = &client->package_funcs;//赋值虚拟函数群为客户端打包函数
int32_t ret = funcs->build_start_request_data(handle, send_data);//执行打包函数,构造发送请求数据
if (ret != HC_OK) {
set_state(base, PROTOCOL_ERROR);
LOGE("Object %u build start request data failed, error code is %d", base->sn, ret);
return ret;
}
set_state(base, START_REQUEST);//设置协议新状态为START_REQUEST
set_last_time_sec(base);//设置上一次的时间
DBG_OUT("Object %u send start request data success", base->sn);
return HC_OK;
}
DD一下: 欢迎大家关注公众号<程序猿百晓生>,可以了解到以下知识点。
erlang
`欢迎大家关注公众号<程序猿百晓生>,可以了解到以下知识点。`
1.OpenHarmony开发基础
2.OpenHarmony北向开发环境搭建
3.鸿蒙南向开发环境的搭建
4.鸿蒙生态应用开发白皮书V2.0 & V3.0
5.鸿蒙开发面试真题(含参考答案)
6.TypeScript入门学习手册
7.OpenHarmony 经典面试题(含参考答案)
8.OpenHarmony设备开发入门【最新版】
9.沉浸式剖析OpenHarmony源代码
10.系统定制指南
11.【OpenHarmony】Uboot 驱动加载流程
12.OpenHarmony构建系统--GN与子系统、部件、模块详解
13.ohos开机init启动流程
14.鸿蒙版性能优化指南
.......
6. 执行回调函数build_start_request_data:构造sts客户端start请求数据。
ini
/*
函数功能:构造sts客户端start请求数据
函数参数:
handle:sts客户端对象
data:待发送数据地址
函数返回值:
成功:0
失败:error num
*/
static int32_t build_start_request_data(void *handle, void *data)
{
struct sts_client *sts_client = (struct sts_client *)handle;//接收sts客户端对象
struct sts_start_request_data *send_data = (struct sts_start_request_data *)data;//用sts_start_request_data结构体接收待发送的数据
struct st_key_pair key_pair;//定义临时密钥对
int32_t ret = generate_st_key_pair(&key_pair);//利用X25519算法生成临时密钥对
if (ret != HC_OK) {//生成失败
LOGE("Object %u generate_st_key_pair failed, error code is %d", sts_client_sn(sts_client), ret);
return HC_INPUT_ERROR;
}
sts_client->self_private_key = key_pair.st_private_key;//赋值sts客户端本端私钥
sts_client->self_public_key = key_pair.st_public_key;//赋值sts客户端本端公钥
struct random_value random_value = generate_random(CHALLENGE_BUFF_LENGTH);//生成长度为CHALLENGE_BUFF_LENGTH的随机值
if (memcpy_s(sts_client->my_challenge.challenge, sizeof(sts_client->my_challenge.challenge),
random_value.random_value, random_value.length) != EOK) {//将生成的随机值作为客户端自身的challenge值
return memory_copy_error(__func__, __LINE__);
}
sts_client->my_challenge.length = random_value.length;//赋值challenge值长度
send_data->peer_version.first = 1;//对端密钥协商版本为(1.0.0)
send_data->peer_version.second = 0;
send_data->peer_version.third = 0;
send_data->peer_support_version.first = 1;//对端支持的密钥协商版本为(1.0.0)
send_data->peer_support_version.second = 0;
send_data->peer_support_version.third = 0;
send_data->operation_code = sts_client->operation_code;//赋值操作码
send_data->epk = sts_client->self_public_key;//将客户端自身公钥赋给epk临时公钥
send_data->challenge = sts_client->my_challenge;//赋值challenge
send_data->package_name = sts_client->identity->package_name;//赋值hc包名称
send_data->service_type = sts_client->identity->service_type;//赋值服务类型
send_data->self_auth_id = sts_client->self_id;//赋值本端认证id
send_data->peer_user_type = ((struct hichain *)sts_client->hichain_handle)->type == HC_CENTRE ?
HC_USER_TYPE_CONTROLLER : HC_USER_TYPE_ACCESSORY;//赋值对端用户类型,根据HC type判断用户类型:HC_CENTRE--HC_USER_TYPE_CONTROLLER,HC_ACCESSORY--HC_USER_TYPE_ACCESSORY
send_data->key_length = sts_client->key_length;//赋值密钥长度
return HC_OK;
}
7. 回到triggered_sts_client函数,继续执行build_send_data_by_struct函数:根据相应的结构体构造发送数据,调用make_auth_start_request函数构造json格式的认证start请求消息。
swift
/*
函数功能:构造json格式的认证start请求消息
函数参数:
data:待发送数据
函数返回值:
成功:json格式的字符串
失败:NULL
*/
char *make_auth_start_request(void *data)
{
struct sts_start_request_data *auth_start_request = data;//用sts_start_request_data结构体接收数据
struct sts_start_request_data_hex tmp_hex = {0, 0, 0, 0};//定义临时变量
/* challenge */
tmp_hex.tmp_cha_data_hex = raw_byte_to_hex_string(auth_start_request->challenge.challenge,
auth_start_request->challenge.length);//将原始的challenge字节数据转换为十六进制的字符串
if (tmp_hex.tmp_cha_data_hex == NULL) {//转换失败
return NULL;
}
/* epk */
tmp_hex.tmp_epk_data_hex = raw_byte_to_hex_string(auth_start_request->epk.stpk, auth_start_request->epk.length);//将原始的epk字节数据转换为十六进制的字符串
if (tmp_hex.tmp_epk_data_hex == NULL) {//转换失败
FREE(tmp_hex.tmp_cha_data_hex);//释放内存空间
return NULL;
}
/* service_type */
tmp_hex.tmp_type_data_hex = raw_byte_to_hex_string(auth_start_request->service_type.type,
auth_start_request->service_type.length);//将原始的service_type字节数据转换为十六进制的字符串
if (tmp_hex.tmp_type_data_hex == NULL) {//转换失败
FREE(tmp_hex.tmp_epk_data_hex);//释放内存空间
FREE(tmp_hex.tmp_cha_data_hex);
return NULL;
}
/* peerAuthId */
tmp_hex.tmp_auth_id_data_hex = raw_byte_to_hex_string(auth_start_request->self_auth_id.auth_id,
auth_start_request->self_auth_id.length);//将原始的peerAuthId字节数据转换为十六进制的字符串
if (tmp_hex.tmp_auth_id_data_hex == NULL) {//转换失败
FREE(tmp_hex.tmp_epk_data_hex);//释放内存空间
FREE(tmp_hex.tmp_type_data_hex);//释放内存空间
FREE(tmp_hex.tmp_cha_data_hex);//释放内存空间
return NULL;
}
char *ret_str = make_requst_json_str(auth_start_request, tmp_hex);//构造json字符串请求消息
FREE(tmp_hex.tmp_epk_data_hex);//释放内存空间
FREE(tmp_hex.tmp_cha_data_hex);//释放内存空间
FREE(tmp_hex.tmp_type_data_hex);//释放内存空间
FREE(tmp_hex.tmp_auth_id_data_hex);//释放内存空间
return ret_str;
}
/*
函数功能:构造json字符串请求消息
函数参数:
auth_start_request:请求消息数据
tmp_hex:临时变量
函数返回值:json格式的字符串
*/
static char *make_requst_json_str(struct sts_start_request_data *auth_start_request,
struct sts_start_request_data_hex tmp_hex)
{
char *tmp_str = (char *)MALLOC(RET_STR_LENGTH);//为json字符串申请空间
if (tmp_str == NULL) {
return NULL;
}
(void)memset_s(tmp_str, RET_STR_LENGTH, 0, RET_STR_LENGTH);//清空该空间
if (snprintf_s(tmp_str, RET_STR_LENGTH, RET_STR_LENGTH - 1,
"{\"%s\":%d,\"%s\":%d,\"%s\":{\"%s\":\"%s\",\"%s\":\"%s\",\"%s\":%d,"
"\"%s\":{\"%s\":\"%u.%u.%u\",\"%s\":\"%u.%u.%u\"},\"%s\":\"%s\","
"\"%s\":\"%s\",\"%s\":\"%s\",\"%s\":\"%d\",\"%s\":\"%u\"}}",
FIELD_AUTH_FORM, AUTH_FORM, FIELD_MESSAGE, AUTH_START_REQUEST, FIELD_PAYLOAD,
FIELD_CHALLENGE, tmp_hex.tmp_cha_data_hex, FIELD_EPK, tmp_hex.tmp_epk_data_hex,
FIELD_OPERATION_CODE, auth_start_request->operation_code,
FIELD_VERSION, FIELD_CURRENT_VERSION, auth_start_request->peer_version.first,
auth_start_request->peer_version.second,
auth_start_request->peer_version.third, FIELD_MIN_VERSION,
auth_start_request->peer_support_version.first,
auth_start_request->peer_support_version.second,
auth_start_request->peer_support_version.third,
FIELD_PKG_NAME, auth_start_request->package_name.name,
FIELD_SERVICE_TYPE, tmp_hex.tmp_type_data_hex,
FIELD_PEER_AUTH_ID, tmp_hex.tmp_auth_id_data_hex,
FIELD_PEER_USER_TYPE, auth_start_request->peer_user_type,
FIELD_KEY_LENGTH, auth_start_request->key_length) < 0) {
LOGE("String generate failed");
FREE(tmp_str);
tmp_str = NULL;
}//生成json格式的请求消息
return tmp_str;
}
三、小结
经过分析,得出客户端发起的请求消息格式如下:
json
{
"authForm":0, //AUTH_FORM
"message":0x0011, //消息码:AUTH_START_REQUEST
"payload":
{
"challenge":"十六进制格式的字符串", //基于"挑战/响应"方式的挑战值,是一个随机值
"epk":"十六进制格式的字符串", //临时公钥,X25519算法生成临时密钥对
"operationCode":2, //操作码:AUTHENTICATE
"version":
{
"currentVersion":"1.0.0",
"minVersion":"1.0.0"
},
"pkgName":"hc包名称",
"serviceType":"服务类型:十六进制格式的字符串",
"peerAuthId":"对端认证id:十六进制格式的字符串",
"peerUserType":"对端用户类型",
"keyLength":"密钥长度"
}
}