OpenHarmony-HUKS模块梳理(修正)

本文研究了openHarmony的security_huks模块的结构,尝试做一个较清楚的整体架构梳理,如有错误敬请指正!

该模块分为三个大文件夹framework,interface,service,整体上可以分为四个独立的子模块:lite版的Client-Service模型,crypto_lite封装的CipherModule ,标准版的IPC_Client-Service模型,以及可在本地调用的API接口。同时本文还新增了关于OpenSSL & Mbedtls与常见的密钥集合的总结。


首先先放一张整体的文件夹导图。
请添加图片描述

我们依次来看这四个子模块。

1.lite版的Client-Service模型

该模型实现在frameworks/huks_lite/hw_keystore_sdk部分:
Alt

①common:该文件夹涉及安全密钥服务的基础数据结构、加解密计算、日志、内存申请与释放、密钥格式转换、hardware_udid的获取等内容,提供一些基本函数与加密函数。
②soft_service:

  • a.hks_storage.h:该文件管理密钥数据在缓冲区数据与文件之间的来往,包括密钥数据的封装存储、同步文件的写入、密钥数据的销毁等。

  • b.hks_file.h:该文件给hks_storage提供基本的文件操作函数。

  • c.hks_rkc.h:rkc即root key component,负责管理keystore files(KSF)的生成、读写、销毁等,还可通过此获取rawKey,值得注意的是,rawKey包含了 udid 的数据。KSF文件用于存储加密后的mainKey,且每个KSF中的数据都必须一样。mainKey用于密钥的派生,mac计算。密钥服务的实现需要有rkc系统的支持,是整个服务启动不可缺少的重要部分。

  • d.hks_service.h:即lite版密钥服务端的接口函数,各类加密算法也在此实现。

③hks_access.c:管理client与service之间的来往,client端的请求发送给access模块,它将该请求交给相应的service端处理函数,再将处理结果返回给client。具体地,这里client端的请求被放置到sec_mod_msg 消息盒子中,同时还有一个全局的处理函数指针集合,盒子中的消息传递给 __hks_handle_secure_call() 函数,该函数调用对应的函数即完成消息处理,然后将处理结果写回消息盒子,client端从盒子中读走结果。
④hks_client.h:lite版密钥服务客户端接口函数。

huks_lite的大致逻辑框架如下:Alt

2.crypto_lite

涉及的文件夹有frameworks/crypto_lite
在这里插入图片描述

这个部分构造了 CipherModule 类,用于AES与RSA加解密处理。本人查了对于该部分的头文件、函数接口的引用,发现这部分似乎与其他子模块孤立,并没有引用 CipherModule 类,且有一个 jsi.h 的头文件,猜测可能是用于前端的开发。

在看标准版的 IPC 模型之前先看看 OpenSSL 与 Mbedtls,这两大框架在密钥加解密中起到重要的支撑作用。(下面复制一段队长博客中的内容原文链接

3.OpenSSL VS Mbedtls

在安全模块中我们看到了两个安全算法实现框架:OpenSSL和Mbedtls,那么在实际的应用场景中我们该如何选择呢?

其实鸿蒙已经给出了答案:lite版本中主要调用了mbedtls框架,standard版本中主要调用了OpenSSL框架,那么我们看看它们两个各自的特点吧!

OpenSSL

OpenSSL主要实现了三类算法:

  • 对称算法比如经典的AES,然后实现了四种主要的加密模式:

    • 电子密码本模式(ECB):将加密数据分成若干组,每组的大小跟加密密钥长度相同,然后每组使用相同的密钥进行加密
    • 加密块链模块(CBC):同样时将明文分成固定长度的块,然后将前面一个加密快输出和下一个要加密的明文进行异或操作,以此类推,由于第一明文加密时没有前面加密的密文,所以需要一个初始化向量,也就是我们在代码中可以看到的参数 IV(init vector)
    • 加密反馈模式(CFB):面向字符的应用程序要使用流加密法则可以使用CFB模式
    • 输出反馈模式(OFB):与CFB类似
  • 摘要算法

    • 能够产生特殊输出格式的算法——也就是固定长度的输出,无论输入字串的长度如何。根据一定的运算规则对原始数据进行某种形式的提取得到摘要,原数据变化则摘要必然变化,理论上信息摘要算法不可逆,所以一般用来做数据完整性的验证
    • SHA族算法:SHA1输出结果为20字节
    • MD族算法:MD5输出为8字节
  • 公钥算法——也称非对称算法比如著名的RSA算法,既能够应用于数据加密也能够应用于数字签名

    • RSA:基于数论的非对称密码体制,是一种分组密码体制
    • DSA:数字签名算法,其安全性基于解离散对数的困难性
    • DH:密钥交换算法,其安全性基于有限域上计算离散对数的困难性
    • ECC:椭圆曲线密码体制,依据是定义在椭圆曲线点群上的离散对数问题的难解性

OpenSSL的另一特点就是回调函数——一般定义在数据结构中,是一个函数指针。通过回调函数——让openSSL的函数来调用它,即用户调用库中函数,而库中函数又调用用户提供的函数,方便用户对OpenSSL函数的操作

其实现的功能:

  • SSL协议的实现
  • 对称、非对称和摘要算法的实现
  • 大数运算的实现
  • 非对称密钥的生成
  • 证书请求(PKCS10)编解码
  • 数字证书编解码
  • 数字证书验证
  • PKCS7标准实现
  • PKCS12个人证书格式实现等

从代码目录中我们也可以看到在鸿蒙的安全模块中,实现了那些功能:

			1. aes
    		2. curve25519
    		3. ecc
    		4. ed25519tox25519
    		5. hash
    		6. hmac
    		7. kdf

Mbedtls
主要面向嵌入式产品,加入加密和SSL/TLS功能

从功能的角度来看,大致分为三个部分:

  • SSL/TLS协议实施
  • 加密库
  • X509证书处理库

为什么会广泛应用于轻量级领域呢?——因为它足够小但是全而且提供了强大的API,即开即用
Alt
同样我们可以在安全模块的代码中看到实现了的算法和功能:

			1. ed25519
    		2. aes
    		3. bn
    		4. ecc
    		5. ecdh
    		6. ecdsa
    		7. hash
    		8. hmac
    		9. kdf
    		10. rsa
    		11. x25519

这里两个框架有个共同点——都应用了X509证书标准,为统一进行数据处理提供了方便

注:
SSL:Secure socket layer 由SSL记录协议和握手协议组成
TLS:Transport layer security

TLS与SSL在传输层与应用层之间对网络连接进行加密,保证数据的安全

当然OpenSSL和Mbedtls都实现了它们

4.标准版的IPC_Client-Service模型

首先贴一张IPC_Client-Service的图:
请添加图片描述
涉及的文件夹有frameworks/huks_standard/main,service/huks_standard

4.1 standard版IPC通信client模型

涉及到的文件夹有frameworks/huks_standard/os_dependency,frameworks/huks_standard/main/common/src
Alt

  • a.main/common/src:该文件夹下的函数负责client端的基础参数检查与构造,为加解密准备必要的参数等,为其他子部分,如client端的接口,各类加解密的算法实现(service端要用到的mbedtls/openssl框架)等提供必需的基础支持。

  • b.hks_ipc_check.h:检查密钥、参数集等数据的有效性,并保证其大小的合法性。

  • -c.hks_ipc_serialization.h:负责将client端的请求数据打包(pack),便于向service端发送。

  • d.hks_ipc_slice.h:在面对密钥的签名验签、加解密等服务时,往往遇到传输数据过大的情况,需要将数据进行切分传输,该子模块就实现了这样的功能。

  • e.hks_request.h:给client提供了发送请求的接口,并从该接口获得服务处理的结果反馈。

  • f.hks_client_service_ipc.c:集合了前面几个子模块提供的支持,提供了client端的服务请求接口。

client_service_ipc的大致逻辑结构:
Alt
除了client端的接口,这部分中还包括了日志输出、内存分配管理以及进程名与硬件标识符的获取,进程名唯一对应KSF,用于对KSF文件的标识;硬件标识符需要添加到rawKey中,用于生成rawKey

4.2 standard版IPC通信service模型

service/huks_standard下有两大子文件夹:
Alt

4.2.1 huks_engine/core

Alt

a.hks_rkc_rw:与前面frameworks/huks_lite/hw_keystore_sdk中的rkc类型,管理密钥存储文件(KSF)数据与缓冲区数据之间的来往,包括rootKey的各类信息通过缓冲区写入到KSF文件,以及读取KSF文件数据到缓冲区中。值得注意的是,每次在文件中写入rootKey信息会在最后加入哈希值,从文件中读取密钥信息时在最后会根据计算一次哈希值,然后与其比对用于验证数据的完整性。

b.hks_rkc.c: rkc系统的建立,包括创建或者载入rkc的keystore files(KSF)(由hks_rkc_rw.c提供相应的文件读写支持),该文件用于存储加密后的mainKey,且每个KSF中的数据都必须一样。mainKey用于密钥的派生,mac计算,keyblob通过派生密钥完成对其他密钥的加解密、rawKey的获取等;另外轻量存储端(STORAGE_LITE)直接使用mainKey实现MAC的计算。此外hks_rkc还负责对hksService密钥服务的初始化,是很重要的子模块。

c.hks_keyblob.c: 在签名验签、消息的加解密等服务中传递的密钥往往是加密过的,通过KeyNode的构造获取得到原始密钥rawKey才能继续后续的操作,对应地,该子模块还负责密钥的加密,即keyblob的构造;另外通过它提供的接口还可以获取rawKey。

d.hks_auth.h:提供加解密、签名验签的认证功能。

e.hks_crypto_hal.h:其中的函数链接到 mbedtls 和 openssl 中实现(crypto_engine),调用的其实是这两个里面的函数。

f.hks_core_service.h: services函数的底层实现,由前面的几块提供相应的支持。

g. crypto_engine:使用mbedtls与openssl两大框架,前面已经提到,它为各种密钥服务提供具体的函数实现。

4.2.2 huks_service
  • a.core
    Alt

    • I. hks_access.c:链接到hks_passthrough_access.c,实则调用hks_core_service的函数
    • II. hks_client_check.c:检查各个密钥服务函数的参数
    • III. hks_client_service_adapter.c:实现客户端和服务端数据格式的转换——X509格式与其他格式的适配
    • IV. hks_operation.c:在具体的密钥服务中管理加解密、签名验签、mac计算等服务进程的三个阶段:initial,update,final
    • V. hks_storage.c:管理密钥文件系统,包括密钥存储、读写等
    • VI. hks_client_service.c:由参数检查、密钥格式适配、密钥文件系统等子模块支持,
      各项密钥服务由huks_engine/hks_core_service.c提供,它为后面的ipc_service端提供服务处理的函数接口
  • b.os_dependency
    Alt

    I. ipc:hks_ipc_service作为IPC服务端的函数接口,由前面的几个子模块作为底层支持,通过response函数将处理结果写入MessageParcel缓冲区,供client端取走。值得注意的是,hks_response.cpp提供了获取进程名的函数HksGetProcessNameForIPC(),该进程名与进程的UID直接绑定,每个进程名与KSF一一对应,有了processName就知道密钥该存到哪里了。此外进程的UID

    II. sa:SA即系统能力(SystemAbility),作为hks服务的管理者,管理该服务的初始化、启动、停止,以及对各类消息的处理,保证该服务的正常运行。值得一提的是,与前面的frameworks/huks_lite/hw_keystore_sdk类似,sa有一个处理各类服务的全局变量g_hksIpcMessageHandler,它有一个存放了针对各类消息请求的函数指针的集合,根据消息类型即可调用对应的hks_ipc_service提供的接口函数,只不过这里使用的是MessageParcel缓冲区,前者使用消息盒子通信。

    III. posix:提供基本文件操作的标准版与lite版函数,供hks_storage,huks_engine中的hks_keyblob调用。

5.各类Key集合

看过了大的模型,接下来看看小的细节,即在代码中遇到的各类Key:
a. rootKey:更全面地说它应该叫root main Key,在文件 services\huks_standard\huks_engine\main\core\src\hks_rkc.c 中可以看到其中的信息:

/* the data of main key */
struct HksRkcMk g_hksRkcMk = { false, { 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0 }, {0} };

/* the additional data of main key. 'H', 'K', 'S', 'R', 'K', 'C', 'M', 'K' */
const uint8_t g_hksRkcMkAddData[HKS_RKC_MK_ADD_DATA_LEN] = { 0x48, 0x4B, 0x53, 0x52, 0x4B, 0x43, 0x4D, 0x4B };

整个密钥服务的一开始,会将 rkc 加密(它被派生过)过的 mainKey 存入各个 keystore files 中,hks_keyblob.c 会从中获取到 mainKey,经过派生得到 derivedKey,而进一步 derivedKey 用于对keyblob的加解密。所以看来,rkc 中的 mainKey 用于对keyblob的加解密。此外在 STORAGE_LITE 端(轻量级存储) rkc的 mainKey 被用于计算 MAC。

b. genKey:不是单独的什么密钥类型,应该是 generate key 的缩写
c. PublicKey,PrivateKey与 agreeKey:生成密钥有两种方式,默认与agree方式,当通过 agree 方式生成密钥时,需要有 PublicKey 与 PrivateKey,开始时 PublicKey 与 PrivateKey 都存放在文件中,从文件中获取后将二者结合为密钥对,称为 DeriveMainKey,把它传递给生成函数后,该函数需要将它解密为两个 rawKey,再对着两个 rawKey 再结合再加密,得到 agreeKey,最后 agreeKey 再封装(即 keyblob 的加密)为 keyBlob 存到文件中去。
d. WrapKey与UnWrapKey:目前的源码中它的处理函数直接 return 0。推测可能是为了更复杂的密钥加密存储。
e. RawKey:从密钥存储文件中拿到的 key 都是加密过的,当要进行例如签名、验签、消息的加解密、密钥派生等操作时需要将 key 解密为 keyNode,其中就包含了 rawKey 数据,有了它才能继续后面的操作。
f. MainKey(或者说MasterKey)与DeriveKey:mainKey是被派生的密钥,从文件中获得后需要解为 rawKey,然后用 rawKey 去派生,得到 DeriveKey。

g.ImportKey:客户端会发送导入密钥的请求,在请求中包含了要导入的密钥数据,服务端经过参数检查后,将该密钥封装为 keyblob,然后存入文件中即可。
h. ExportKey:具体指的是 ExportPublicKey 服务,客户端发送要导出的密钥的 keyAlias,服务端根据此从文件中获取对应的密钥,解密获得 rawKey,从中获取 publicKey 发送给客户端。

贴一张关系图:
Alt

6.本地API接口

涉及的文件夹有interface,frameworks/huks_standard/core下的hks_local_engine,它可以分为3个子部分:
Alt

6.1 hks_client_ipc

这里的client_ipc不同于前面提到的standard的client模型,它链接到hks_client_service_passthrough.c
依据:由宏定义*ifndef CUT_AUTHENTICATE*指明,在hks_api.c的源码中可以看到:

HKS_API_EXPORT int32_t HksRefreshKeyInfo(void)
{
#ifndef _CUT_AUTHENTICATE_
    return HksClientRefreshKeyInfo();
#else
    return HKS_ERROR_NOT_SUPPORTED;
#endif
}

且在hks_client_service_passthrough.c中有:

#include "hks_client_ipc.h"
#include "hks_client_service.h"
#include "hks_get_process_info.h"
#include "hks_log.h"

#ifndef _CUT_AUTHENTICATE_
...//此处省略其他函数
int32_t HksClientRefreshKeyInfo(void)
{
    char *processName = NULL;
    if (HksGetProcessName(&processName) != HKS_SUCCESS) {
        HKS_LOG_E("get process name failed");
        return HKS_ERROR_INTERNAL_ERROR;
    }
    struct HksBlob processNameBlob = { strlen(processName), (uint8_t *)processName };
    /*这里直接调用service的处理函数*/
    return HksServiceRefreshKeyInfo(&processNameBlob);
}

证明这里的client_ipc是链接到hks_client_service_passthrough.c的。

在上面的代码中可以看到,函数最后直接调用service端的函数,没有sa的介入,故中间不经过request和response,而且根据一些参数特征(paramSet中的isKeyAlias),某些服务可直接在本地完成,即调用hks_local_engine提供的接口,而不需要调用service端的接口

6.2 hks_local_engine

作为本地的密钥服务实现函数,提供了哈希算法、MAC计算、密钥生成、明文加密、签名验签等函数,供hks_api调用。

6.3 hks_api

由上面两部分作支持,提供本地可调用的密钥服务的API接口。

interface中表现出的逻辑图如下:
Alt

7.小结

该模块不可谓不复杂,有很多细节值得去研究学习,比如宏的使用、进程间的通信、消息盒子等,同时可以发现lite和standard两种模式下有很多相似的地方,比如进程间的共享、全局的服务维护者等。感谢阅读,如有错误敬请指正!

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值