开源之夏05:命令行应用与SDF接口集成
进入九月,核心工作转向命令行应用的开发、69 个 SDF 接口的全面对齐,以及将新开发的 TSAPI_SDF_* 接口集成到 Tongsuo 中。
命令行应用开发
Tongsuo 的命令行框架
Tongsuo/OpenSSL 的命令行工具采用选项表驱动 + 枚举令牌 + 自动 help 生成的模式,遵循 POSIX/GNU 约定。curl、ffmpeg 等经典工具都使用类似理念,各语言生态也有对应实现(Python 的 argparse/click、Go 的 cobra、Rust 的 clap)。
核心数据结构:
1 | |
主函数骨架:
1 | |
对比手写 strcmp(argv[i], "-n") 的方式,这种框架的好处是:选项与帮助文档一处维护(不用改两处)、自动类型校验、分节/续行统一排版、声明式语义清晰。
为什么是 xxx_main?
apps/progs.pl 脚本通过正则 int ([a-z_][a-z0-9_]*)_main(int argc, ...) 自动扫描所有命令源文件,捕获到的前缀即为子命令名。生成的 progs.c 中包含全局注册表:
1 | |
openssl.c 启动时构建命令哈希表,do_cmd() 以 O(1) 查表后调用对应函数指针。这就是为什么命名必须是 xxx_main + xxx_options 的形式。
为什么不做成 20 个独立可执行文件?四个硬指标:
- 兼容性:全世界脚本里都是
openssl req ...,不能突然变成openssl-req - 体积:静态链接 1 份 libcrypto 比 20 份省一个数量级
- 性能:哈希表+函数指针 O(1) 分发,比 dlopen 启动快得多
- 维护:约定优于配置,零手工列表
命令行功能设计
命令行的功能实现依赖三层调用链:
1 | |
在开发过程中发现了一个遗留问题:TSAPI 层”写穿”了。按照工程设计,SDF 层的 sdf_lib.c 使用函数映射表构建抽象,让 TSAPI 层屏蔽厂商细节。但前人在 TSAPI 中直接使用了厂商的 sdfe_api.h 符号(如 SDFE_LoginUsr),导致没有硬件就编译报错,TSAPI 和 SDF 层耦合在一起。
破案后发现,之前的蚂蚁密码卡适配根本没怎么遵循 SDF 标准——身份认证通过 login 实现而不是 GetPrivateKeyAccessRight。需要做的是在 SDF 层为所有 TSAPI 需要的功能提供标准化通用接口,把厂商扩展类型限制在 SDF 层内部,TSAPI 层不再 include sdfe_api.h。
exec 函数族的巧用
实现命令行应用过程中,可以直接使用 exec(3) 函数族套壳调用:
1 | |
Unix 提供 execl/execv/execle/execve/execlp/execvp 六种变体,按”参数形式(l/v)× 是否指定环境(- /e)× 是否搜 PATH(- /p)”形成 2×2 矩阵,外加 GNU 扩展 execvpe 补全。
接口测试与集成
测试框架
Tongsuo 自带集成测试框架。util/libcrypto.num 中已经列出了所有 TSAPI_SDF_* 符号及其编号,确保导出接口在 ABI 层面可追踪。测试文件(如 test/sm4_internal_test.c)通过 ADD_TEST(test_xxx) 注册测试函数,make test 或 ninja test 自动批量运行。
为 SDF 模块添加测试的步骤:
- 在
test/下新建sdf_internal_test.c - 编写测试函数并用
ADD_TEST注册 - 在
test/build.info中包含该文件 - 编译后
make test自动执行
69 个接口的全面对齐
对照 GM/T 0018-2023 标准,整理和清点了所有 SDF 接口,包括:
- 设备管理类(OpenDevice, CloseDevice, GetDeviceInfo)
- 会话管理类(OpenSession, CloseSession)
- 对称加密类(Encrypt, Decrypt, CalculateMAC)
- 哈希类(HashInit, HashUpdate, HashFinal)
- 密钥管理类(GenerateKey, ImportKey, DestroyKey, 各种封装/解封)
- 非对称运算类(签名、验签、加解密、密钥协商)
- 随机数类(GenerateRandom)
- 文件管理类(ReadFile, WriteFile, CreateFile, DeleteFile)
- 访问控制类(Get/ReleasePrivateKeyAccessRight)
- 验证测试类(最后 12 个接口)
后 12 个是验证测试类接口,功能由厂商设备实现,但为了测试其他标准 SDF 接口需要在软实现中提供几个。
符号导出验证
按照 2012 标准,编译完成后应导出 45 个标准接口。通过 nm 工具检查编译产物的符号表,确认所有 TSAPI_SDF_* 接口都正确导出且可见。
接口集成的成功节点
9 月 24 日,在导师帮助下,新增的 TSAPI_SDF_* 接口成功集成到了 Tongsuo 中。之后只需 #include <openssl/sdf.h> 就可以直接使用。
集成过程中使用了调试技巧:添加 --debug 编译选项后可以用 gdb 单步跟踪整个调用链:
1 | |
代码格式化规范
导师给了几个实用的提醒:
- 括号风格:Google 风格
int function {vs Tongsuo 源码风格int function \n {,两种都行但保持一致。 - 宏定义中
#后不要带空格(#define而非# define),与工程保持一致。 - 源文件中的意外空格、空行——不影响功能,但在开源项目中要重视。现代工具(VSCode 自带格式化、pre-commit hook 自动检查)可以解决大部分问题。