文件机密性保护功能的源代码
在某密码应用系统中,系统客户端的本地存储的重要档案文件、会议视频文件等数据通过智能密码钥匙进行加密,保障其机密性。该智能密码钥匙具有商用密码产品认证证书且密码模块安全等级为二级。
该系统在接受密码应用安全性评估时,密码应用安全性评估人员对该系统文件机密性保护功能的源代码实现进行审查,其内容如下:
#define SM4_IV_LEN 16
ULONG SKF_encrypt(BYTE *userPIN, BYTE *pbKey, ULONG pbkeyLen, BYTE *data, ULONG dataLen, BYTE *encData, ULONG *encDataLen)
{
BYTE devices[1024] = { 0 };
ULONG devices_len = 1024; //用于储存设备名称的内存长度
BYTE apps[1024] = { 0 }; //用于储存应用名称
ULONG apps_len = 1024; //用于储存应用名称的内存长度
BYTE containers[1024] = { 0 }; //用于储存容器名称
ULONG containers_len = 1024; //用于储存容器名称的内存长度
ULONG ret = 0; //接口调用返回值
DEVHANDLE hDevhandle = NULL; //定义一个设备句柄
HAPPLICATION hApplication = NULL; //定义一个应用句柄
do{
//首先遍历设备,目的是获取到当前插入设备的设备名称,如果已经知道了设备名称,则可以不用遍历,直接连接设备(相关异常处理略)
ret = SKF_EnumDev(TRUE, devices, &devices_len);
//根据遍历获取到的设备名称,来连接设备(假设此时仅适用第一个设备,其他相关异常处理略)
ret = SKF_ConnectDev(devices, &hDevhandle);
//枚举应用(相关异常处理略)
ret = SKF_EnumApplication(hDevhandle, apps, &apps_len);
//打开应用(假设这里打开第一个应用,相关异常处理略)
ret = SKF_OpenApplication(hDevhandle, apps, &hApplication);
//遍历容器(相关异常处理略)
ret = SKF_EnumContainer(hApplication, containers, &containers_len);
//打开容器(假设这里打开第一个容器,相关异常处理略)
ret = SKF_OpenContainer(hApplication, containers, &hContainer);
//加密初始化(相关异常处理略)
ret = SKF_EncryptInit(hkeyHandle, cipherParam);
//数据加密(相关异常处理略)
ret = SKF_Encrypt(hkeyHandle, data, dataLen, encData, encDataLen);
} while(0);
//10.释放相关资源,详细实现略
//......
return ret;
}
(1)请从智能密码钥匙标准接口使用安全性角度分析代码问题,并给出改进建议(改进建议应包括正确的函数名称和调用方法说明)。
(2)若 SKF_Encrypt 函数的输入参数 dataLen 值为 1000,请问该函数输出参数*encDataLen的值应为多少?
本题共 3 分,共 2 个问题,基本按照 2 分 + 1 分作答。
未验证设备合法性(直接使用第一个设备),存在中间人攻击风险
缺少设备状态检查(如物理篡改检测),违反 GM/T 0017-2023 标准中 6.2.3 设备认证要求
密钥(pbKey)直接通过参数传递,未使用 SKF_ImportKey 等标准接口进行安全导入
未显示密钥派生过程,不符合国密标准 SM2/SM9 密钥协商规范
未调用 SKF_BeginSession 建立安全会话通道
缺少应用容器绑定验证(应使用 SKF_OpenApplication 而非直接连接)
未指定 SM4 加密模式(如 CBC/ECB),IV 未按标准生成(应使用 SKF_GenRandom 生成随机 IV)
ret = SKF_GetDevState(devices, &devState); // GM/T 0017-2023 6.2.3
if(devState != SLOT_ACTIVE) { ... }
ret = SKF_DevAuth(devices, adminPIN, pinLen); // 标准设备认证
ret = SKF_ImportKey(hContainer, SGD_SM4_ECB, pbKey, pbkeyLen, &hKey);
ret = SKF_GenRandom(hDevhandle, iv, SM4_IV_LEN); // 生成随机IV
ret = SKF_EncryptInit(hKey, SGD_SM4_CBC, iv, 16); // 国密标准初始化
ret = SKF_EncryptUpdate(hKey, data, dataLen, encData, encDataLen);
ret = SKF_EncryptFinal(hKey, encData + *encDataLen, &finalLen); // 处理最后分组
根据 SM4 加密算法的特性,SM4 是一个分组密码算法,默认分组长度为 16 字节(128 位)。在加密过程中,如果没有特殊设置(如采用流加密模式),通常会对输入数据进行填充或以分组对齐的方式加密。
假设 SKF_Encrypt 使用的是分组加密且未明确指定其他模式(如 CBC 模式等),那么输出加密数据长度 *encDataLen 的计算方式如下:
加密数据长度 = 输入数据长度向上取整到 16 字节的倍数
当 dataLen = 1000 时,*encDataLen 的预期值应为:
SM4 作为分组密码(16 字节块大小),采用 PKCS#7 填充时:
填充长度 = 16 - (1000 % 16) = 8 字节
在智能密码钥匙标准接口中,dataLen 参数的单位是字节(Byte)。这一结论基于以下技术规范和实践依据:
国密标准(如 GM/T 0016-2023《智能密码钥匙应用接口规范》)中明确要求数据长度参数以字节为单位。
加密算法(如 SM4)的分组长度虽定义为 128 比特(16 字节),但接口实现时统一使用字节作为操作单位。
在您提供的代码片段中,SM4_IV_LEN定义为 16,对应 SM4 算法初始向量的 16 字节长度(而非 128 比特的数值形式)。
类似memcpy等底层函数操作均以字节为基本单位(如知识库中memcpy(&a[i-1], &a[i], dataLen*sizeof(int))的字节级操作)。
文件加密场景中,数据长度通常以字节计量(如 1000 字节的文件片段),而非比特单位。
智能密码钥匙的物理传输层协议(如 APDU 指令)也以字节为最小数据单元。
SM4-CBC 加密时,PKCS#7 填充规则要求补充16 - (1000 % 16) = 8字节
最终输出长度应为1000 + 8 = 1008字节(即*encDataLen = 1008)
原文始发于微信公众号(利刃信安):【商密测评】文件机密性保护功能的源代码
评论