OpenHarmony解读之设备认证:pake协议-服务端响应pake start请求(2)

  • Post author:
  • Post category:其他




一、概述

本文将继续介绍服务端响应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 版权协议,转载请附上原文出处链接和本声明。