当前位置: 首页 > 工具软件 > AuthManager > 使用案例 >

OpenHarmony深度解读之分布式软总线:authmanager模块(3)/设备身份认证过程

夏侯弘量
2023-12-01

一、概述

设备之间互联是基于系统的IoT设备(如AI音箱、智能家居、智能穿戴等设备)与IoT主控设备(手机、平板等)间建立点对点的信任关系,并在具备信任关系的设备间,搭建安全的连接通道,实现用户数据端到端加密传输。IoT主控设备和IoT设备建立点对点信任关系的过程,实际上是相互交换IoT设备的身份标识的过程。authmanager是openharmony为设备提供认证机制的模块。
上一篇文章OpenHarmony源码分析之分布式软总线:authmanager模块(2)/设备认证通信管理已经对设备间的身份认证过程进行了部分介绍,如认证连接的管理以及认证协议数据包头部的解析等,本文将继续分析认证协议数据包负载部分的处理过程。

二、源码分析

本模块大部分处理代码位于 wifi_auth_manager.c 中。下面将进行详细分析:

  1. 在函数ProcessPackets中,循环解析认证数据包,首先解析数据包头部,然后再解析数据包负载。
/*
函数功能:处理身份认证协议数据包
函数参数:conn——认证连接信息结构体;
		buf——数据缓冲区地址;
		size——数据缓冲区总大小;
		used——数据缓冲区已使用量,这里指的是已接收数据总量
函数返回值:返回已成功处理完的数据量
详细:循环解析收到的每一个数据包,先解析其头部,再解析其数据负载部分。
*/
static int ProcessPackets(AuthConn *conn, const char *buf, int size, int used)
{
    int processed = 0;//已处理数据量
    while (processed + PACKET_HEAD_SIZE < used) {//循环解析数据包头部
    	//解析包头部,返回认证协议数据包头部字段信息
        Packet *pkt = ParsePacketHead(buf, processed, used - processed, size);
        if (pkt == NULL) {
            SOFTBUS_PRINT("[AUTH] ProcessPackets ParsePacketHead fail\n");
            return -1;
        }

        int len = pkt->dataLen;//获取数据负载部分的长度
        //如果产生越界,跳出循环
        if ((len > PACKET_DATA_SIZE) || (processed + PACKET_HEAD_SIZE + len > used)) {
            free(pkt);
            pkt = NULL;
            break;
        }
        processed += PACKET_HEAD_SIZE;

        OnDataReceived(conn, pkt, buf + processed);//解析数据负载部分
        processed += len;
        free(pkt);
        pkt = NULL;
    }

    return processed;
}
  1. OnDataReceived 函数,处理数据包负载部分。
/*
函数功能:处理接收到的认证协议数据包数据负载部分
函数参数:
    conn:认证设备连接信息结构体
    pkt:认证协议数据包头部结构体的地址
    data:数据负载部分的起始地址
函数返回值:无
详细:
*/
static void OnDataReceived(AuthConn *conn, const Packet *pkt, const char *data)
{
    SOFTBUS_PRINT("[AUTH] OnDataReceived\n");
    //如果module字段为MODULE_AUTH_SDK,则调用AuthInterfaceOnDataReceived继续进行处理。这里没有直接用==,是为了可扩展性
    if ((pkt->module > MODULE_HICHAIN) && (pkt->module <= MODULE_AUTH_SDK)) {
    	//若数据包类型为MODULE_AUTH_SDK,表示对端请求创建设备身份认证环境(暂定)
        AuthInterfaceOnDataReceived(conn, pkt->module, pkt->seq, data, pkt->dataLen);
        return;
    }
	//解密消息,返回cJSON结构体格式的数据
    cJSON *msg = DecryptMessage(pkt->module, data, pkt->dataLen);
    if (msg == NULL) {
        SOFTBUS_PRINT("[AUTH] OnDataReceived DecryptMessage fail\n");
        return;
    }
	//根据数据包类型字段module,对接收到的cJSON数据选择不同的处理方式
    OnModuleMessageReceived(conn, pkt->module, pkt->flags, pkt->seq, msg);
    cJSON_Delete(msg);
    msg = NULL;
}
  1. 函数 AuthInterfaceOnDataReceived() ,若数据包类型为 MODULE_AUTH_SDK ,则调用本函数进行处理,此函数为认证接口。
/*
函数功能:若数据包类型为MODULE_AUTH_SDK,则调用本函数进行处理,此函数为认证接口
函数参数:
    conn:当前连接信息
    module:数据包类型
    seqId:数据包序列号
    data:数据负载
    dataLen:负载长度
函数返回值:无
*/
void AuthInterfaceOnDataReceived(const AuthConn *conn, int module, long long seqId, const char *data, int dataLen)
{
    SOFTBUS_PRINT("[AUTH] AuthInterfaceOnDataReceived begin\n");
    if (conn == NULL || data == NULL || dataLen > PACKET_DATA_SIZE) {//若产生越界,则返回
        return;
    }
    if (AuthSessionMapInit() != 0) {//初始化认证会话表
        return;
    }

    AuthSession *auth = AuthGetAuthSessionBySeqId(seqId);//根据SeqId查找认证会话
    if (auth == NULL) {//如果不存在
    	//构造新的认证会话,g_authSessionId初值为1
        auth = AuthGetNewAuthSession(conn, seqId, g_authSessionId);
        if (auth == NULL) {
            return;
        }
        ++g_authSessionId;//全局认证会话id加1
    }

    switch (module) {
        case MODULE_AUTH_SDK://如果数据包类型是MODULE_AUTH_SDK:
        	//处理身份认证过程中接收到的数据
            AuthProcessReceivedData(auth->sessionId, data, dataLen);
            break;
        default:
            break;
    }

    return;
}
  1. 首先初始化认证会话表,然后根据 SeqId 查找认证会话,若不存在就构造新的认证会话。
/*
函数功能:认证会话表初始化
函数参数:无
函数返回值:
    成功:返回0
    失败:返回-1
详细:具体就是为全局会话表指针g_authSessionMap申请空间并初始化为0
*/
static int AuthSessionMapInit(void)
{
    if (g_authSessionMap != NULL) {
        return 0;
    }

    int len = sizeof(AuthSession) * AUTH_SESSION_MAX_NUM;
    g_authSessionMap = (AuthSession *)malloc(len);//申请空间
    if (g_authSessionMap == NULL) {
        return -1;
    }
    (void)memset_s(g_authSessionMap, len, 0, len);//清空地址空间
    return 0;
}

/*
函数功能:根据SeqId在全局会话表中查找认证会话
函数参数:
    SeqId:数据包序列号
函数返回值:
    成功:返回认证会话首地址
    失败:返回NULL
详细:
*/
static AuthSession *AuthGetAuthSessionBySeqId(long long seqId)
{
    if (g_authSessionMap == NULL) {
        return NULL;
    }

    for (int i = 0; i < AUTH_SESSION_MAX_NUM; i++) {
        if (g_authSessionMap[i].isUsed == 0) {//如果该会话未被使用,则continue
            continue;
        }
		//查找到seqId对应的认证会话,返回该认证会话地址
        if (g_authSessionMap[i].seqId == seqId) {
            return &g_authSessionMap[i];
        }
    }

    return NULL;
}

/*
函数功能:构造新的认证会话
函数参数:
    conn:连接信息
    seqId:数据包序列号
    sessionId:会话id
函数返回值:
    成功:返回认证会话结构体地址
    失败:返回NULL
详细:
*/
static AuthSession *AuthGetNewAuthSession(const AuthConn *conn, long long seqId, uint32_t sessionId)
{
    if (conn == NULL || g_authSessionMap == NULL) {//边界检查
        return NULL;
    }

    for (int i = 0; i < AUTH_SESSION_MAX_NUM; i++) {
        if (g_authSessionMap[i].isUsed == 0) {//将未被使用的认证会话初始化
            g_authSessionMap[i].isUsed = 1;
            g_authSessionMap[i].seqId = seqId;
            g_authSessionMap[i].sessionId = sessionId;
            g_authSessionMap[i].conn = (AuthConn *)conn;

            return &g_authSessionMap[i];//返回认证会话表中首次被使用的认证会话结构体地址
        }
    }

    return NULL;
}
  1. 如果数据包类型是 MODULE_AUTH_SDK ,则调用 AuthProcessReceivedData 函数进行处理。
/*
函数功能:处理身份认证过程中接收到的数据
函数参数:
    sessionId:会话id
    data:数据负载部分首地址
    dataLen:数据长度
函数返回值:无
详细:
*/
static void AuthProcessReceivedData(uint32_t sessionId, const char *data, int dataLen)
{
    if (g_hcHandle == NULL) {
        if (AuthInitHiChain(sessionId) != 0) {//初始化HiChain
            AuthDelAuthSessionBySessionId(sessionId);//根据sessionId删除该认证会话
            return;
        }
    }

    struct uint8_buff request = {(uint8_t *)data, dataLen, dataLen};//封装request数据
    if (receive_data(g_hcHandle, &request) != HC_OK) {//其他模块继续处理该数据包
        return;
    }
}

至此,本文分析了数据包类型为 MODULE_AUTH_SDK 的数据包负载的处理过程,对于其他类别的数据包,将在接下来的文章中进行分析。

 类似资料: