OpenHarmony解读之设备认证:sts协议-服务端响应sts end请求

50 阅读13分钟

一、概述

在上文中说到,客户端根据服务端的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;
}

DD一下:欢迎大家关注公众号<程序猿百晓生>,可以了解到以下知识点。

`欢迎大家关注公众号<程序猿百晓生>,可以了解到以下知识点。`
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.鸿蒙版性能优化指南
.......
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(&para, sizeof(para), 0, sizeof(para));//清空参数空间
        //获取协议参数
        hichain_handle->cb.get_protocol_params(&hichain_handle->identity, GET_PEER_USER_TYPE, &pin, &para);
        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_key
static 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":"十六进制格式的字符串"     //认证返回值
    }
}