一.简介

设备之间互联是基于系统的IoT设备(如AI音箱、智能家居、智能穿戴等设备)与IoT主控设备(手机、平板等)间建立点对点的信任关系,并在具备信任关系的设备间,搭建安全的连接通道,实现用户数据端到端加密传输。

IoT主控设备和IoT设备建立点对点信任关系的过程,实际上是相互交换IoT设备的身份标识的过程。

authmanager是openharmony为设备提供认证机制的模块,模块内的处理流程为

二.HiChan(设备互联安全)

为了实现用户数据在设备互联场景下,在各个设备之间的安全流转,实现用户数据的安全传输。设备之间的信任关系指的是本文档中涉及IoT主控设备和IoT设备之间建立的可信关系。

三.加密框架mbedtls

AES-GCM加密算法:AES是一种对称加密算法,GCM是对该对称加密采用Counter模式,并带有GMAC消息认证码。AES-GCM算法是带认证和加密的算法,同时可以对给定的原文,生成加密数据和认证码。

首先调用intmbedtls_gcm_setkey(mbedtls_gcm_context*ctx,mbedtls_cipher_id_tcipher,constunsignedchar*key,unsignedintkeybits),该函数将GCM上下文与密码算法和密钥相关联。

加密调用:intmbedtls_gcm_crypt_and_tag(mbedtls_gcm_context*ctx,intmode,size_tlength,constunsignedchar*iv,size_tiv_len,constunsignedchar*add,size_tadd_len,constunsignedchar*input,unsignedchar*output,size_ttag_len,unsignedchar*tag),加密后的数据以出参形式传出。

解密调用:intmbedtls_gcm_auth_decrypt(mbedtls_gcm_context*ctx,size_tlength,constunsignedchar*iv,size_tiv_len,constunsignedchar*add,size_tadd_len,constunsignedchar*tag,size_ttag_len,constunsignedchar*input,unsignedchar*output),解密后的数据以出参形式传出。

四.流程详解与关键代码数据结构:

1初始化代码流程如下图所示:

该部分主要由discover模块调用BusManager函数开始,若flag=1,则进行StartBus函数的执行,它的实现主要是调用了StartListener函数及StartSession函数,实现对设备进行认证及加解密的过程。

关键数据结构

注册回调函数的全局变量g_baseLister

typedefstruct{

OnConnectEventProconConnectEvent;

OnDataEventProconDataEvent;

}BaseListener;

关键接口StartListener

StartSession

StartSession该函数只有一个参数,即constchar*ip,也就是一个IP,和StartListener函数中的IP是一样的。该函数是为全局变量g_sessionMgr申请空间及初始化,然后根据所给的参数创建socket文件描述符并监听,之后通过调用StartSelectLoop函数创建SelectSessionLoop的线程,该线程将socket文件描述符加入集合,并调用select函数进行监控,若函数的返回值大于0,则调用ProcessData函数,该函数有两个分支,若socket未创建session则为其创建session;若已创建session,则处理其数据部分。

2.工作流程(消息处理流程)

2.1工作流程图如下

该部分主要有两个对外接口,一个是连接管理,另一个是数据处理。

数据处理流程图如下

该部分为收到数据后的整体处理流程,包括数据接收、包头解析和包数据处理。通过调用FindAuthConnByFd接口获取到连接结构AuthConn后,用AuthConnRecv来接收数据,通过ProcessPackets对收到的包处理,在ParsePacketHead中对包头解析,OnDataReceived中对数据处理,MODULE_AUTH_SDK类型包经过hichain处理流程,处理过程中调用回调函数AuthOnTransmit,AuthGetProtocolParams,AuthSetSessionKey,AuthSetServiceResult,AuthConfirmReceiveRequest,其他包都会通过cJSON_Parse函数对数据解析,最后都会为MODULE_TRUST_ENGINE包和MODULE_CONNECTION包构造一个reply包发送给对端验证设备信息,对于验证IP的请求包在回包之后会将设备置为ONLINE状态,在所有数据处理结束之后关闭该连接。

2.2关键数据结构

2.2.1双向链表

typedefstructList{

structList*prev;

structList*next;

}List;

2.2.2保存所有连接的链表头

staticList*g_fdMap=NULL;

2.2.3包类型定义

defineMODULE_TRUST_ENGINE1

defineMODULE_AUTH_SDK3

defineMODULE_CONNECTION5

defineMODULE_SMART_COMM7

defineMODULE_AUTH_MSG9

2.2.4数据缓冲区定义

typedefstructDataBuffer{

char*buf;

intsize;

intused;

}DataBuffer;

2.2.5连接定义,一个AuthConn表示一个真实连接

typedefstructAuthConn{

intfd;

charauthId[MAX_AUTH_ID_LEN];

chardeviceId[MAX_DEV_ID_LEN];

chardeviceIp[MAX_DEV_IP_LEN];

intbusVersion;

intauthPort;

intsessionPort;

intauthState;

intonlineState;

DataBufferdb;

}AuthConn;

2.2.6连接管理节点,用双向链表的方式保存所有连接

typedefstructAuthConnNode{

Listhead;

AuthConn*aconn;

}AuthConnNode;

2.2.7数据包头结构

typedefstruct{

intmodule;

intflags;

longlongseq;

intdataLen;

}Packet;

2.3关键接口

2.3.1ProcessConnectEvent

负责连接建立和管理

连接建立:

在链表g_fdMap中根据fd查找AuthConn,找不到则说明该fd没有被使用,然后为AuthConn分配内存,并且填充fd和ip数据到新建连接的AuthConn中,为AuthConnNode分配空间后将AuthConnNode和AuthConn绑定,最后将AuthConnNode追加在g_fdMap链表尾部后表示连接完成。

连接管理:

用一个g_fdMap为头指针的双向链表来保存所有的连接节点AuthConnNode,每一个AuthConnNode结构体都有自己的AuthConn,表示一个真正的连接,g_fdMap中最多可以保存32个连接,超过32个时建立连接会失败。

2.3.2ProcessDataEvent

负责从fd对应的连接中接收数据包,并且根据包的module不同来分别处理

首先根据fd找到对应的连接AuthConn,如果该连接还没有分配缓冲区,则为其创建一个1536字节大小的接收buf并清空,然后通过AuthConnRecv函数接收数据,其底层也是调用大家所熟悉的socket编程接口recv去接收数据,将接收的数据保存在刚才为AuthConn分配的buf中,并且更新表示当前缓冲区使用属性的变量used。然后调用ProcessPackets函数对接收的数据包解析并处理,该函数中对包头解析,然后根据包头中获取的数据长度来对数据部分进行处理。

2.3.3ParsePacketHead

解析包头

对收到的数据包头进行校验,magicnumber正确后,获取包的module、seq、flags、datalen信息并保存在Packet里面,后续对该包处理。

2.3.4OnDataReceived

对收到的数据进行处理

数据处理主要依赖包头中pkt-module这个值来区分不同类型的包,包的类型共有10种,除了MODULE_AUTH_SDK包通过AuthInterfaceOnDataReceived处理外,其余都会通过DecryptMessage函数进行处理,包处理结束之后会调用cJSON_Parse来获取数据,最后会对MODULE_TRUST_ENGINE包和MODULE_CONNECTION包构造一个新的reply包,将本地的设备信息封装成cJSON格式的msg后发回对端。

3.最终调用封装加密发送流程

3.1工作流程图如下

3.2关键数据结构

3.2.1SessionKey

在hichain处理流程中获取到的key被保存在SessionKey中,在最后发送之前获取sessionkey打包到要发送的buf中。

typedefstructSessionKey{

charkey[AUTH_SESSION_KEY_LEN];

intindex;

intfd;

}SessionKey;

3.2.2AuthSession

每当收到新的连接且类型是MODULE_AUTH_SDK时,创建一个AuthSession,并保存到全局数组g_authSessionMap中,下次再进行连接会话时从全局数据中找AuthSession

typedefstructAuthSession{

intisUsed;

longlongseqId;

uint32_tsessionId;

AuthConn*conn;

}AuthSession;

3.3关键接口

3.3.1AuthConnPackBytes

打包与加密接口,首先打包关键信息identifier,module等到传输buf中,判断isCipherText,是否需要加密,判断标准为module是否为MODULE_CONNECTION,MODULE_SESSIONMODULE_SMART_COMM

3.3.2AuthConnPackBytes

获取sessionkey,打包入要传输的buf中

3.3.3EncryptTransData

实际调用mbedtls加密框架,进行加密的接口

3.3.4AuthConnS

调用s函数,把打包好的buf通过套接字fd发送出去。

五.结束

当初步建立信任关系的IoT主控设备与IoT设备间在进行通信时,双方首先完成信任关系绑定,然后基于存储在本地的对端身份公钥相互进行认证;在每次通信时完成双向身份认证以及会话密钥协商,之后设备使用此会话密钥来解密双方设备间的传输通道。