OpenHarmony解读之设备认证:pake协议-客户端发起start请求

一、概述

在设备认证过程中,pake协议用于认证会话密钥协商,基于该会话密钥,双方可以安全地交换各自的身份公钥。从本文开始,将对pake协议的详细过程进行介绍,本博客主要介绍客户端发起start请求的过程,协议状态从PROTOCOL_INIT转换为START_REQUEST。

二、源码分析

这一模块的源码位于:/base/security/deviceauth。

1. start_pake函数,启动pake模块。
scss 复制代码
/*
函数功能:启动pake模块
函数参数:
    handle:hichain实例
    params:操作参数
函数返回值:
    成功:0
    失败:error
*/
DLL_API_PUBLIC int32_t start_pake(hc_handle handle, const struct operation_parameter *params)
{
    LOGI("Begin start pake");
    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, PAKE_MODULAR, true, params);//构建PAKE_MODULAR client子对象
    if (ret != HC_OK) {
        LOGE("Build pake client sub object failed, error code is %d", ret);
        return ret;
    }
    ret = triggered_pake_client(hichain, BIND);//触发pake client子对象,发起设备绑定
    LOGI("Triggered pake client error code is %d", ret);
    LOGI("End start pake");
    return ret;
}
2. 首先调用build_object函数构建pake客户端对象。
c 复制代码
/*
函数功能:构建HiChain子对象
函数参数:
    hichain:HiChain实例
    modular:消息模块类型
    is_client:是否是client
    params:构建参数
函数返回值:
    成功:返回0 HC_OK
    失败:返回错误码
详细:
*/
int32_t build_object(struct hichain *hichain, int32_t modular, bool is_client, const void *params)
{
    //初始化HC对象表map
    const struct object_map map[] = { { PAKE_MODULAR, true, (void **)&hichain->pake_client },//pake客户端
                                      { PAKE_MODULAR, false, (void **)&hichain->pake_server },//pake服务端
                                      { STS_MODULAR, true, (void **)&hichain->sts_client },//sts客户端
                                      { STS_MODULAR, false, (void **)&hichain->sts_server },//sts服务端
                                      { ADD_MODULAR, true, (void **)&hichain->auth_info },//认证信息
                                      { REMOVE_MODULAR, true, (void **)&hichain->auth_info },//认证信息
                                      { SEC_CLONE_MODULAR, false, (void **)&hichain->sec_clone_server } };//安全克隆服务端
    void **object = get_object(map, sizeof(map) / sizeof(map[0]), modular, is_client);//根据消息模块类型获取HC对象
    if ((object == NULL) || (*object != NULL)) {//获取失败
        DBG_OUT("No sub-objects need to be applied for");
        return HC_OK;
    }
    if (check_mutex_object_is_null(map, sizeof(map) / sizeof(map[0]), modular, is_client) == false) {//检查互斥对象是否为空
        LOGE("The mutex sub-object have been created, create %d:%d sub-object failed", modular, is_client);
        return HC_REPEATED_REFERENCE;
    }
    if (check_depend_object_is_not_null(map, sizeof(map) / sizeof(map[0]), modular, is_client) == false) {//检查依赖对象是否为非空
        LOGE("The depend sub-object is not created, create %d:%d sub-object failed", modular, is_client);
        return HC_NEED_DEPEND;
    }
    *object = build_object_by_modular(hichain, modular, is_client, params);//根据消息模块协议类型构建子对象
    if (*object == NULL) {
        LOGE("Create %d:%d sub-object failed", modular, is_client);
        return HC_BUILD_OBJECT_FAILED;//构建失败
    }
    DBG_OUT("Create %d:%d sub-object success", modular, is_client);
    return HC_OK;
}
3. 实际上最终是执行build_pake_client_object函数构建客户端对象。
c 复制代码
/*
函数功能:构建pake协议客户端对象
函数参数:
    hichain:hichain实例
    params:参数
函数返回值:
    成功:子对象首地址
    失败:NULL
*/
static void *build_pake_client_object(struct hichain *hichain, const void *params)
{
    (void)params;//未使用参数
    struct hc_pin pin = { 0, {0} };
    struct operation_parameter para;//操作参数
    (void)memset_s(&para, sizeof(para), 0, sizeof(para));//清空操作参数空间
    hichain->cb.get_protocol_params(&hichain->identity, hichain->operation_code, &pin, &para);//获取协议参数
    if (check_param_is_valid(&para) == false) {//检查参数是否有效
        LOGE("Param invalid");
        return NULL;
    }
    if (pin.length > HC_PIN_BUFF_LEN) {//检查PIN是否有效
        LOGE("PIN invalid");
        return NULL;
    }
    return build_pake_client(&hichain->identity, &pin, para.key_length, &para.self_auth_id, &para.peer_auth_id);//构建pake客户端对象
}
/*
函数功能:构建pake客户端
函数参数:
    identity:hichain会话标识
    pin:PIN码
    key_length:密钥长度
    client:客户端认证id
    server:服务端认证id
函数返回值:
    成功:struct pake_client地址
    失败:NULL
*/
struct pake_client *build_pake_client(const struct session_identity *identity, const struct hc_pin *pin,
    uint32_t key_length, const struct hc_auth_id *client, const struct hc_auth_id *server)
{
    struct pake_client *pake_client = (struct pake_client *)MALLOC(sizeof(struct pake_client));//为pake客户端对象申请空间
    if (pake_client == NULL) {
        LOGE("Build pake client object failed");
        return NULL;
    }
    (void)memset_s(pake_client, sizeof(*pake_client), 0, sizeof(*pake_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(&pake_client->client_info, &funcs);//初始化协议基础信息和协议客户端打包函数为funcs
    pake_client->pin = *pin;//赋值PIN
    pake_client->key_length = key_length;//赋值密钥长度
    pake_client->self_id = *client;//赋值客户端认证id为本端id
    pake_client->peer_id = *server;//赋值服务端认证id为对端id
    pake_client->identity = identity;//赋值会话标识
    pake_client->prime_type = NUM_LEN_384;//最大质数类型
    LOGI("Build pake client object %u success", pake_client_sn(pake_client));
    return pake_client;
}
4. build_start_request_data函数,构造start请求数据。
ini 复制代码
//构造pake_client start请求数据,data参数:数据地址
static int32_t build_start_request_data(void *handle, void *data)
{
    struct pake_client *pake_client = (struct pake_client *)handle;//接收pake_client对象
    struct pake_start_request_data *send_data = (struct pake_start_request_data *)data;//接收数据地址
    send_data->peer_version.first = 1;//初始化对端版本号
    send_data->peer_version.second = 0;
    send_data->peer_version.third = 0;
    send_data->peer_support_version.first = 1;//初始化对端可支持的版本号
    send_data->peer_support_version.second = 0;
    send_data->peer_support_version.third = 0;
    send_data->operation_code = pake_client->operation_code;//赋值操作码
    send_data->epk_len = HC_BIG_PRIME_MAX_LEN;//企业级公钥长度为最大质数长度
    return HC_OK;
}
5. 回到start_pake 函数中,继续执行triggered_pake_client函数,触发pake client子对象,发起设备绑定。
ini 复制代码
/*
函数功能:触发pake_client子对象
函数参数:
    hichain:hichain实例
    operation_code:操作码
函数返回值:
    成功:0
    失败:error
*/
static int32_t triggered_pake_client(struct hichain *hichain, int32_t operation_code)
#if !(defined(_CUT_PAKE_) || defined(_CUT_PAKE_CLIENT_))
{
    hichain->operation_code = operation_code;//赋值操作码
    hichain->pake_client->operation_code = operation_code;
    struct message send = {//"发送消息"
        .msg_code = PAKE_REQUEST,
        .rsv = 0,
        .payload = NULL
    };
    int32_t ret = send_pake_start_request(hichain->pake_client, &send);//发送pake协议start请求
    if (ret != HC_OK) {
        LOGE("Object %u build sts start request failed, error code is %d", pake_client_sn(hichain->pake_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);//构造格式化的客户端请求数据,此处为json格式的字符串
    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, PAKE_REQUEST, ret);//根据接收消息码和发送消息码设置hichain结果和状态
    destroy_send_data(&send);//销毁发送缓冲区
    return ret;
}
6. 在triggered_pake_client函数中,首先执行send_pake_start_request函数初始化待发送消息。
scss 复制代码
/*
函数功能:发送"pake start request"消息
函数参数:
    pake_client:pake客户端对象
    send:待发送消息
函数返回值:
    成功:0
    失败:error
*/
int32_t send_pake_start_request(struct pake_client *pake_client, struct message *send)
{
    check_ptr_return_val(pake_client, HC_INPUT_ERROR);//检查参数有效性
    check_ptr_return_val(send, HC_INPUT_ERROR);
    struct pake_start_request_data *send_data =
        (struct pake_start_request_data *)MALLOC(sizeof(struct pake_start_request_data));//为待发送的请求消息申请内存空间
    if (send_data == NULL) {
        LOGE("Malloc struct PAKE_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(pake_client, send_data);//发送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 = PAKE_REQUEST;
        send->payload = send_data;
    }
    return ret;
}
/*
函数功能:发送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;
}
/*
函数功能:设置协议状态
函数参数:
    handle:密钥协商协议句柄
    new_state:新协议状态
函数返回值:无
*/
void set_state(struct key_agreement_protocol *handle, enum protocol_state new_state)
{
    check_ptr_return(handle);//检查参数有效性
    enum protocol_state ori_state = handle->state;//定义原始状态
    DBG_OUT("Object %u state is %u, new state is %u", handle->sn, ori_state, new_state);
    if ((ori_state == PROTOCOL_TIMEOUT) || (ori_state == PROTOCOL_ERROR) || (ori_state == PROTOCOL_FINISH)) {//如果原始状态为"超时"、"错误"、"结束",就直接返回,因为没有下一状态
        LOGE("Object %u state cannot change", handle->sn);
        return; /* not allowed to modify these end status */
    }
    if (new_state < ori_state) {//不允许协议状态回滚
        LOGE("Object %u state cannot rollback", handle->sn);
        return;
    }
    if (handle->state != new_state) {//如果新状态与当前状态不等
        handle->last_state = ori_state;//将原始状态赋给last_state(上一个状态)
        handle->state = new_state;//赋值新状态
    }
}
7. 然后执行build_send_data_by_struct函数,构造格式化的客户端请求数据,此处为json格式的字符串。
c 复制代码
/*
函数功能:构造结构化的发送消息
函数参数:
    message:输出参数,消息地址
    send_data:输出参数,消息有效载荷地址
    send_data_len:输出参数,消息有效载荷长度
函数返回值:
    成功:返回0
    失败:返回错误码
*/
static int32_t build_send_data_by_struct(const struct message *message, void **send_data, uint32_t *send_data_len)
{
    //消息构造表
    const struct make_message_map map[] = { { PAKE_REQUEST, make_pake_request },//构造pake请求消息
                                            { PAKE_RESPONSE, make_pake_response },//构造pake响应消息
                                            { PAKE_CLIENT_CONFIRM, make_pake_client_confirm },//构造pake协议客户端认证消息
                                            { PAKE_SERVER_CONFIRM_RESPONSE, make_pake_server_confirm },//构造pake协议服务端认证消息
                                            { AUTH_START_REQUEST, make_auth_start_request },//构造认证开始请求消息
                                            { AUTH_START_RESPONSE, make_auth_start_response },//构造认证开始响应消息
                                            { AUTH_ACK_REQUEST, make_auth_ack_request },//构造认证确认请求消息
                                            { AUTH_ACK_RESPONSE, make_auth_ack_response },//构造认证确认响应消息
                                            { ADD_AUTHINFO_REQUEST, make_add_auth_info_request },//构造添加认证信息请求消息
                                            { REMOVE_AUTHINFO_REQUEST, make_rmv_auth_info_request },//构造移除认证信息请求消息
                                            { ADD_AUTHINFO_RESPONSE, make_add_auth_info_response },//构造添加认证信息响应消息
                                            { REMOVE_AUTHINFO_RESPONSE, make_rmv_auth_info_response },//构造移除认证信息响应消息
                                            { EXCHANGE_REQUEST, make_exchange_request },//构造交换信息请求消息
                                            { EXCHANGE_RESPONSE, make_exchange_response },//构造交换信息响应消息
                                            { SEC_CLONE_START_RESPONSE, sec_clone_make_srv_proof },
                                            { SEC_CLONE_ACK_RESPONSE, sec_clone_make_clone_ret },
                                            { INFORM_MESSAGE, make_inform_message } };//构造通知消息
    if (message->msg_code == INVALID_MESSAGE) {//如果消息码为无效,则返回"无消息发送"
        return HC_NO_MESSAGE_TO_SEND;
    }
    if (message->payload == NULL) {//如果发送负载为空,则返回错误码
        LOGE("Message payload is null");
        return HC_BUILD_SEND_DATA_FAILED;
    }
    for (uint32_t i = 0; i < sizeof(map) / sizeof(struct make_message_map); i++) {//遍历消息构造表
        if (map[i].msg_code != message->msg_code) {
            continue;
        }
        *send_data = map[i].make_message(message->payload);//调用构造消息回调函数!!!
        if (*send_data == NULL) {//构造消息失败
            return HC_BUILD_SEND_DATA_FAILED;
        }
        *send_data_len = strlen(*send_data);//构造消息成功,获取长度
        return HC_OK;
    }
    LOGE("Unsupport encape 0x%04x message", message->msg_code);
    return HC_INNER_ERROR;
}

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.鸿蒙版性能优化指南
.......
8. 实际上是执行make_pake_request回调函数构造pake请求消息。
swift 复制代码
/*
函数功能:构造pake请求消息
函数参数:
    data:数据首地址
函数返回值:返回json格式的字符串
详细:
消息字符串实际为:{"message":1,"payload":{"version":{"currentVersion":"1.0.0","minVersion":"1.0.0"},"support256mod":true,"operationCode":1}}
*/
char *make_pake_request(void *data)
{
    struct pake_start_request_data *pake_request = data;//接收pake_start_request_data结构的数据
    char *ret_str = (char *)MALLOC(RET_STR_LENGTH);//为回复消息申请空间
    if (ret_str == NULL) {
        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\":true,\"%s\":%d}}",
        FIELD_MESSAGE, PAKE_REQUEST, FIELD_PAYLOAD, FIELD_VERSION, FIELD_CURRENT_VERSION,
        pake_request->peer_version.first, pake_request->peer_version.second,
        pake_request->peer_version.third, FIELD_MIN_VERSION,
        pake_request->peer_support_version.first, pake_request->peer_support_version.second,
        pake_request->peer_support_version.third, FIELD_SUPPORT_256_MOD,
        FIELD_OPERATION_CODE, pake_request->operation_code) < 0) {//生成json格式的字符串作为消息数据
        LOGE("String generate failed");
        FREE(ret_str);
        ret_str = NULL;
    }
    return ret_str;//返回json格式的字符串
}
9. 最后执行set_result函数设置hichain状态和结果。
c 复制代码
/*
函数功能:设置最终结果信息,即HiChain状态
函数参数:
    hichain:hichain实例对象
    rcv_msg_code:接收消息码
    snd_msg_code:发送消息码
    error_code:错误码
函数返回值:无
*/
static void set_result(struct hichain *hichain, uint16_t rcv_msg_code, uint16_t snd_msg_code, int32_t error_code)
{
    if (error_code != HC_OK) {//如果错误码不是0,则直接跳转到error,按对应错误码设置结果
        LOGE("Error code is not ok, and set end failed");
        goto error;
    }
    if (snd_msg_code == INFORM_MESSAGE) {//如果发送消息码为通知消息,则直接跳转到error
        LOGE("Send an inform message, and set end failed");
        goto error;
    }
    //消息结果表
    const struct msg_result_map map[] = { { PAKE_REQUEST, KEY_AGREEMENT_PROCESSING, KEY_AGREEMENT_STATE },//密钥协商过程中
                                          { PAKE_RESPONSE, KEY_AGREEMENT_PROCESSING, KEY_AGREEMENT_STATE },//密钥协商过程中
                                          { PAKE_CLIENT_CONFIRM, KEY_AGREEMENT_END, OPERATION_STATE },//密钥协商结束
                                          { PAKE_SERVER_CONFIRM_RESPONSE, KEY_AGREEMENT_END, OPERATION_STATE },//密钥协商结束
                                          { EXCHANGE_REQUEST, END_SUCCESS, OVER_STATE },
                                          { EXCHANGE_RESPONSE, END_SUCCESS, OVER_STATE },
                                          { AUTH_START_REQUEST, KEY_AGREEMENT_PROCESSING, KEY_AGREEMENT_STATE },
                                          { AUTH_START_RESPONSE, KEY_AGREEMENT_PROCESSING, KEY_AGREEMENT_STATE },
                                          { AUTH_ACK_REQUEST, KEY_AGREEMENT_END, OPERATION_STATE },
                                          { AUTH_ACK_RESPONSE, KEY_AGREEMENT_END, OPERATION_STATE },
                                          { ADD_AUTHINFO_REQUEST, END_SUCCESS, OVER_STATE },
                                          { ADD_AUTHINFO_RESPONSE, END_SUCCESS, OVER_STATE },
                                          { REMOVE_AUTHINFO_REQUEST, END_SUCCESS, OVER_STATE },
                                          { REMOVE_AUTHINFO_RESPONSE, END_SUCCESS, OVER_STATE },
                                          { SEC_CLONE_START_REQUEST, OPERATION_PROCESSING, OPERATION_STATE },
                                          { SEC_CLONE_ACK_REQUEST, END_SUCCESS, OVER_STATE },
                                          { INFORM_MESSAGE, END_FAILED, OVER_STATE },
                                          { INVALID_MESSAGE, KEY_AGREEMENT_PROCESSING, KEY_AGREEMENT_STATE } };
    const struct msg_result_map *map_ptr = select_result_map(rcv_msg_code, map,
        sizeof(map) / sizeof(struct msg_result_map));//根据接收消息码查找结果表
    set_result_by_map(hichain, map_ptr);//根据结果表内容设置最终结果
    return;
error:
    hichain->last_state = hichain->state;//将hc当前状态赋给last_state
    hichain->state = OVER_STATE;//设置hc当前状态为OVER_STATE
    hichain->cb.set_service_result(&hichain->identity, END_FAILED);//调用软总线模块回调函数设置服务结果为END_FAILED!!!
}

三、小结

经过分析,可以得到pake协议的start请求的消息格式为:

json 复制代码
{
    "message":1,//消息码为PAKE_REQUEST
    "payload":
    {
        "version":
        {
            "currentVersion":"1.0.0",//当前版本号
            "minVersion":"1.0.0"//最低版本号
        },
        "support256mod":true,//是否支持256mod
        "operationCode":1//操作码为BIND
    }
}

根据消息内容可以知道,客户端发起start请求的目的在于将本端的协议版本号、是否支持256mod、操作码等信息发送给服务端,向服务端发起认证会话密钥协商请求

相关推荐
李游Leo9 分钟前
HarmonyOS:ComposeTitleBar 组件自学指南
harmonyos
飞凌嵌入式15 分钟前
从DeepSeek到Qwen,AI大模型的移植与交互实战指南
人工智能·aigc·嵌入式
抓鱼猫L16 分钟前
鸿蒙Next(四)文字识别
harmonyos
IT乐手1 小时前
3.7、HarmonyOS Next 自定义弹窗(CustomDialog)
harmonyos
李游Leo1 小时前
HarmonyOS:ArkTS 多态样式自学指南
harmonyos
zacksleo1 小时前
鸿蒙Flutter实战:20. Flutter集成高德地图,同层渲染
flutter·harmonyos
李游Leo1 小时前
HarmonyOS:ArkTS RowSplit 组件自学指南
harmonyos
NodeMedia1 小时前
如何快速集成NodeMediaClient-Harmony
harmonyos
IT乐手1 小时前
3.8、HarmonyOS Next 气泡提示(Popup)
harmonyos
zacksleo1 小时前
鸿蒙Flutter实战:19-Flutter集成高德地图,跳转页面方式
flutter·harmonyos