尧图精选

国产安全芯片国密算法性能适配:从SM2到SM4的优化实战

🕒 发布时间:2026/10/2 14:37:17 📁 来源:尧图网络
去年年底我在评估一款国产安全芯片时有个客户比较直接原来用的是国际算法方案换到国密体系后每秒签名次数不能掉太多SM3摘要吞吐不能成为业务瓶颈SM4加解密还得让硬件引擎跑得动。最开始我把问题想简单了觉得国密不就是换几个算法接口嘛真正动手才发现“国产安全芯片国密体系性能适配”三个词放到一起完全变成另一种玩法没有现成的OpenSSL高性能后端可以无缝替换很多底层优化得自己造轮子芯片的异构计算资源CPU、密码引擎、DMA、安全存储要一起拉通才能交出满意答卷。这篇文章我就把这次实践经验整理出来围绕国产安全芯片在密码算法国密体系下的性能适配策略展开从算法瓶颈、芯片边界、优化手段到实测记录一次讲透适合正在做国密改造、嵌入式平台适配、安全模块性能调优的同行参考。1. 先看清国密体系的“摩擦力”在哪做性能适配之前建议大家先别急着写代码而是把国密四个主力算法——SM2、SM3、SM4、ZUC——各自的计算特征拉个清单。每个算法的瓶颈位置完全不同有的卡在数学运算上有的卡在访存模式上还有的卡在数据搬运方式上。搞错重点后面优化全是白费劲。1.1 SM2椭圆曲线点乘才是性能命门SM2是基于椭圆曲线的公钥密码算法核心运算包括签名、验签、密钥交换和加密解密。这些操作拆到最底层几乎全部落在有限域上的模乘、模加、模逆以及椭圆曲线上的标量乘法k倍点上。比如签名算法里的 k·G验签算法里的 u·G v·P这两个操作往往占据整个SM2流程80%以上的时间。点乘怎么算直接决定性能天花板。朴素的自左向右二进制扫描法256位标量平均需要256次倍点加128次点加如果换成窗口法比如4-bit窗口预计算15个点后平均只需要256次倍点加64次点加点加次数直接减半。此外坐标系的选取也很关键仿射坐标下每次点加都要做模逆模逆在软件里通常是用扩展欧几里得或费马小定理实现的一次模逆的代价可能是模乘的好几十倍而Jacobian投影坐标可以把求逆延后到最终结果转换时只做一次中间过程全部用模乘和模平方完成性能差距非常明显。我见过不少国产芯片只有SM4硬件加速SM2完全靠CPU裸算。这种情况下如果不做算法级优化一个签名跑出十几毫秒甚至几十毫秒都很正常。所以SM2适配的第一原则就是宁可多做预计算表也要减少点加次数和求逆次数。1.2 SM3杂凑算法的扩散效应与性能边界SM3是国密体系里的密码杂凑算法输出256位摘要。它的结构是经典的Merkle-Damgard消息分组512位压缩函数迭代64轮。SM3的性能特征比较特殊它不像SM2那样有大量高开销数学运算但计算密度高、循环体大软件实现受编译器和CPU流水线影响非常明显。压缩函数每轮要更新7个32位寄存器字还要进行消息扩展。消息扩展部分每轮生成新的W和W如果编译器把中间变量溢出到内存Cache命中率会很难看。这块的优化重点其实不在“数学”而在“寄存器分配”能不能把常用的常量、状态字全部留在寄存器里避免每轮都从内存装入。这里顺便回答一个网上讨论比较多的问题SM3的P置换如果输入差分只有1比特输出差分有多少比特SM3中的置换P定义是P(X0, X1, X2, X3) (X0 xor X1, X1 xor X2, X2 xor X3, X3 xor X0)。假设输入差分只在X0的最低位有1比特那么经过异或扩散Y0 X0 xor X1会有一个比特差分Y3 X3 xor X0也会有一个比特差分Y1和Y2则没有差分。所以单比特输入差分经过P置换后输出总差分比特数是2比特。注意这里说的是P置换本身它是一层线性扩散不会把1变成几十比特真正的“雪崩效应”来自前面的S盒和异或链叠加经过多轮迭代才会扩散到全部分组。这个细节在做SM3的差分分析和硬件故障注入检测时很重要性能适配时也要意识到P置换的扩散能力偏弱轮数必须做够不能为了提速而削减轮逻辑。SM3还有个特点是数据依赖较浅相邻几轮之间的寄存器更新有一定并行度所以在支持SIMD指令的CPU上可以尝试打包处理或者至少通过循环展开让编译器有更多调度空间。我在Cortex-M系列上做过测试单纯把编译器优化等级从-O2调到-O3再配合手工循环展开SM3吞吐率能提升20%到40%这是性价比很高的一步。1.3 SM4/ZUC对称算法相对温和但别忽视软硬协同SM4是国密体系的分组密码算法分组长度128位密钥长度128位32轮非线性迭代结构。它的轮函数里有一个8位S盒和一个32位线性变换L。SM4的软件优化有一个经典思路把S盒和线性变换合并成多个大查找表用查表加异或的方式一次完成多轮的S盒替换和线性扩散。最常见的是合并出4张4KB的表但缺点是总大小可能超出一些低端芯片的Cache容量。更保守的做法是只保留S盒本体配合循环移位和异或来实现线性变换以时间换空间。ZUC祖冲之算法是国密体系里的流密码算法用于加密和完整性保护。ZUC由线性反馈移位寄存器LFSR、比特重组BR和非线性函数F组成每个时钟周期可以产出32位密钥字。从性能角度看ZUC非常适合硬件实现LFSR更新是线性递推F函数是S盒加线性变换流水线可以做得又深又密。在软件上做ZUC适配最需要注意的是32位字端序和LFSR里的模(2^31-1)运算这块用无符号整型加判断分支去处理编译器优化会友好很多。ZUC初始化阶段有几个周期的空转迭代对单包加密延迟有影响如果业务是高频小包场景建议一次性多申请密钥流摊薄初始化开销。SM4和ZUC的性能适配共同点在于数据量大、流程规整最适合交给专用密码引擎纯软件跑也能跑但会长时间霸占CPU影响系统中其他任务响应。2. 芯片硬件资源决定适配的边界条件听完算法层面的分析有人可能会问那我把每个算法都优化到极致不就行了吗现实没这么简单。芯片的硬件拓扑决定了你能用的优化手段上限。不看清芯片手里有什么牌盲目上优化方案可能会南辕北辙。2.1 安全芯片的典型硬件拓扑国产安全芯片虽然型号千差万别但内部结构大多可以抽象成几个关键部件主CPU核心常见的是ARM Cortex-M系列、Cortex-A系列也有RISC-V核心。主频从几十MHz到GHz不等决定了软件密码实现的基线速度。密码引擎/协处理器有些芯片内置SM2/SM3/SM4硬件加速模块有些只支持其中一类比如只做SM4和SM3摘要引擎公钥运算完全靠CPU软件。密码引擎通常有自己的寄存器接口、状态位和中断数据可以通过DMA喂进去。安全存储包括eFuse、安全Flash、安全SRAM用于保存根密钥、密钥密文和运行时敏感数据。密钥从安全存储装载到密码引擎的路径是否畅通直接影响握手性能。总线与DMA如果密码引擎和CPU共享内存总线并发访问会产生争用有独立DMA通道时大块数据可以不经过CPU直接搬运。这套拓扑带来的第一个启示芯片厂商为什么往往把SM4做成硬件、SM2却留给软件因为SM4这类对称算法处理的数据量大、模式固定硬件流式处理效率极高而SM2是离散的数学运算如果不专门做公钥引擎通用CPU配合良好软件反而更灵活。所以拿到芯片先看数据手册确认哪些算法有硬件加速哪些需要软件实现适配策略从此出发。2.2 性能适配的目标不是跑分而是端到端做芯片适配很容易陷入“单算法跑分崇拜”SM3测出100MB/sSM4硬件引擎标称500Mbps就觉得万事大吉。但真实业务往往是一整条链路比如车联网V2X消息签名从应用层构造数据到安全模块做哈希、签名、格式封装再通过通信接口发出去又比如安全启动固件镜像要逐块做SM3校验、做SM2验签中间还有Flash读取、DMA传输、日志记录。端到端性能瓶颈常常不在算法本身而在数据拷贝、上下文切换、密钥装载、中断响应这些“看不见的角落”。举个真实例子某芯片SM4硬件引擎标称吞吐率很高但业务侧发现加密一段64KB文件耗时不短。排查后发现问题不在引擎而是软件每加密16字节就发起一次寄存器轮询等待引擎根本没机会跑满。改成DMA多块连续传输后吞吐率直接翻倍。所以性能适配的第一步应该是画业务时序图把每一段耗时标出来找出真正的大头。不是所有场景都需要把算法优化到极限很多时候把流程理顺收益来得更快。3. 性能适配的整体策略与拆解前面铺了大量背景这节进入正题完整的性能适配策略应该怎么设计。我会按照“软硬件分工、存储优化、算法级流程优化、功耗调优”四个层次来拆。这四个层次是递进关系先定好分工再做局部优化最后调整调度策略。3.1 软硬件分工把每个算法放到最适合的引擎上拿到一款具体芯片第一步不是写代码而是做一张“算法×硬件资源”分工表。以我接触比较多的一类芯片为例192MHz Cortex-M33内核 内置SM4引擎 无SM2硬件加速 有TRNG“分工”大概是这样的算法推荐形态决策依据SM2软件实现 预计算表芯片无公钥引擎只能靠CPU预计算表可以把点加次数压下来SM3软件优化或摘要引擎如果有SM3硬件引擎则优先硬件否则用循环展开和Cache优化软件跑SM4硬件引擎 DMA引擎吞吐率高DMA搬运可以把CPU释放出来ZUC硬件引擎 DMA流密码适合硬件流水线软件方案仅作回退这种分工的核心思想是让CPU少做轮函数运算把大块数据搬运交给DMA把重复性计算交给专用引擎CPU只负责控制流和调度。听起来像废话但很多项目连这一步都没做好原因在于他们下意识把所有算法都拿到一个引擎上去跑互相排队等资源。举个例子假设芯片的SM3引擎和SM4引擎共用一条总线同时开启两条流水线总线可能成为瓶颈。这时就要考虑“时分复用”SM3算完再启动SM4或者给两个引擎的任务设不同优先级。这个决策不能靠拍脑袋最好直接用性能计数器统计总线占用率。3.2 存储与Cache友好的查表优化安全芯片的SRAM往往不大Cache可能只有16KB甚至8KB这对依赖大查找表的密码算法很不友好。以SM4为例理想化的4张4KB大表总共16KB几乎占满整个Cache如果系统里还有其他模块的频繁访问Cache会被频繁刷掉查表效果大打折扣。我的建议是分级处理如果芯片Cache足够大用4张合并表如果Cache紧张采用“S盒本体 循环移位异或”方案虽然每轮多几次移位运算但存储占用只有256字节Cache命中率极高。中间的折中方案是两张8KB表把S盒的两次变换合并其中一部分。具体选哪种建议用芯片厂商提供的性能基准工程实测对比不要凭空猜。SM2的预计算表也有类似取舍。窗口法窗口越大预计算表越大点加次数越少。但表太大装不进紧密SRAM读取顺序又不规律Cache miss带来的惩罚可能超过点加次数减少的收益。我用下来Cortex-M系列窗口取4是比较稳的平衡点如果RAM充足且CPU有指令预取可以试试5但收益通常只有几个百分点。SM3相对友好主要查表是常量数组比如64个轮常量总共几百字节可以安全地放进RAM。但要注意如果常量数组定义在Flash上每次循环从Flash读取会比从SRAM读慢不少尤其Flash带等待周期时更明显。很多新手在这里掉坑明明算法没问题性能就是上不去最后发现是常量表放在外部Flash。3.3 算法级流程优化批量、异步、流水线单次操作优化到一定程度后就要从流程层面“挖潜”。密码业务很少是“一次签完就结束”更多是连续不断的会话、证书链验证、批量数据加解密。这时候三种玩法很实用。第一是批量验签。SM2验签的核心是计算u·G v·P如果你有多个签名要验可以把多个点的运算合并调度或者至少共享一些固定预计算表。比如证书链有3层根证书的验签结果可以做缓存下次只要验中间证书和叶子证书省掉重复计算。第二是异步DMA流水线。硬件密码引擎算完一批数据时CPU不需要干等可以先去准备下一批数据。典型做法是CPU把数据地址通过DMA描述符交给引擎引擎完成后触发中断中断服务程序里只做状态标记主循环再去取结果。这样就形成“引擎在算第N批CPU在准备第N1批”的流水线整体吞吐率能明显提升。实测里流水线化之后SM4加密耗时比“每次等中断”减少30%到50%非常可观。第三是任务级合并。有些安全操作可以合并成复合指令比如先SM3再SM4解密部分芯片支持“摘要引擎→加解密引擎”的链式级联数据不用回到CPU内存再搬一次省掉两轮拷贝。没有这种硬件级链式支持时也要尽量在软件层减少中间缓冲区的复制。3.4 功耗与频率调优安全芯片大量用在电池供电或需要低功耗设计的场景性能适配不能只盯着跑分还要看每瓦性能。同一个SM2签名如果主频固定192MHz那么算法优化目标是减少周期数如果允许动态调频还可以考虑“快速算完、快速休眠”的策略——集中算力把工作做完然后马上进入低功耗状态比一直低频慢跑更省电。有个细节容易被忽略密码引擎本身的时钟可以独立配置。某些芯片的密码引擎支持分频运行性能要求不高时可以降频节能。反过来如果密码引擎频率高于总线频率也可能造成数据搬运瓶颈这时反而要适当降低引擎频率使DMA能稳定喂足数据。功耗调优需要实测数据支撑最好在开发板上接上功耗分析仪对比不同频率组合下的功耗和吞吐曲线选一个工作点。4. 实操记录一个安全启动场景的适配全过程前面讲策略这节开始手把手过一遍实操。我拿一个典型的工业安全启动场景举例设备上电后安全模块要对固件镜像做SM3摘要然后用SM2验签验签通过后才允许启动。业务设计要求整个验签流程在1秒内完成同时设备平时还会跑SM4加解密用于通信保护。为了把问题说透我设计了一组“芯片画像”主控Cortex-M33主频192MHz安全特性内置SM4硬件引擎、TRNG、安全FlashSM3/SM2无硬件加速需要软件实现内存SRAM 512KB其中安全RAM 128KBCache 16KBDMA2个通道支持内存到密码引擎数据传输4.1 性能基线怎么打拿到开发板后我没有马上写优化代码而是先打了一版“原生基线”用O2编译不开任何优化技巧API怎么方便怎么调。我习惯用芯片内部的DWT Cycle Counter或者定时器做前方统计记录每个算法的周期数再转成毫秒。注意要关闭调试器因为调试器连接状态会影响Flash读取延时和时钟配置测出来的数据会偏高而且不稳定。基线结果是SM3处理256KB固件耗时约18ms吞吐率约14.5MB/s离业务“1秒内完成验签”的要求似乎很远——但等一下验签不止SM3还有SM2。SM2验签一次平均耗时约9.2ms那总耗时应该是18ms9.2ms看似不到30ms为什么业务还喊慢因为实际启动流程里还包含固件从Flash逐块读入、安全完整性校验、日志记录、密钥装载和随机数生成等步骤端到端跑下来将近300ms。虽然离1秒还有余量但考虑到后续固件会继续变大、加功能这个余量并不安全值得优化。基线的意义是找到优化的“分母”。如果你不知道原来有多慢就不知道改了哪里真正产生了收益。我建议每个优化动作前后都用同一套标准用例测试比如SM3固定处理256KB随机数据、SM2固定对同一摘要做验签100次、SM4固定加密64KB数据记录周期数和耗时。4.2 优化动作的优先级排序根据基线结果我按“成本低收益高”的原则把优化动作排成了P0到P3四个优先级P0必做把SM3的轮常量和SM2的预计算表从Flash搬到SRAM确保所有热数据都在快速存储器里。P1必做SM3循环展开加手工寄存器分配SM2切换窗口法窗口设为4并将点加模块改成Jacobian坐标。P2推荐SM2模逆改成常数时间实现费马小定理消除验签耗时抖动SM3考虑双缓冲配合DMA读Flash让摘要计算和数据搬运重叠。P3可选SM4引擎配合DMA异步流水线把CPU等待时间压到最低。执行P0和P1后结果如下SM3处理256KB耗时降到了11ms左右吞吐率约23MB/s。SM2验签从9.2ms降到4.8ms提升接近一半。主要收益来自两个地方一是SRAM访问速度比Flash高很多常量表从Flash搬RAM后SM3每轮少等好几个Flash等待周期二是SM2窗口法把点加次数从128次降到64次Jacobian坐标又省掉了大量求逆。接下来做P2和P3SM2验签进一步降到4.1msSM4加密64KB从原来的1.2ms降到0.7ms。到这个阶段整个验签流程端到端已经压到140ms左右业务压力小了很多。4.3 性能测试与结果分析优化不能只看一两次结果还要看稳定性和极端情况。我做了三组测试单次最优测试所有Cache为理想状态测极限吞吐。持续运行测试连续跑1000次验签观察性能是否衰减、有没有温度导致降频。混合负载测试验签过程中同时启动SM4加密任务模拟真实业务中的任务争用。比较有参考价值的发现是混合负载下SM2验签耗时比单独测试高出约15%原因不是CPU频率下降而是SM4任务占用了DMA控制器和总线带宽SM2软件需要访问的预计算表所在SRAM受到总线仲裁影响。解决思路是将SM4的DMA优先级调低一点或者把SM2的预计算表放到私有SRAM区域避免与DMA冲突。从最终结果看整个适配策略的效果是指标优化前优化后提升幅度SM3吞吐率14.5MB/s23MB/s58%SM2验签9.2ms/次4.1ms/次55%SM4加密64KB1.2ms0.7ms41%端到端验签流程约300ms约140ms53%这些数据不是理论值是在真实芯片上跑出来的。我列这个表是想说明性能适配是一个系统性工程单纯优化一个算法端到端效果可能很有限必须把算法、存储、DMA、任务调度全部纳入考量。5. 常见问题与排查技巧实录做国密性能适配半年多踩过的坑不少挑几个典型问题列出来给大家做参考。5.1 SM3跑不到理论值的一半问题出在哪有次我在某款Cortex-M4芯片上做SM3理论估计至少能跑到30MB/s实际只有13MB/s百思不得其解。后来用编译器生成汇编看了一眼发现核心循环里的变量被编译器放到栈上了每轮都要做内存加载和存储周期数翻了好几倍。解决办法是手动把循环变量声明成register变量虽然现在编译器不一定听更可靠的方式是直接循环展开让编译器有足够寄存器可以分配。展开4轮以后性能立刻恢复到预期水平。这提示SM3的性能瓶颈往往不是算法结构而是编译器的寄存器分配和访存调度。5.2 SM2验签耗时抖动很大一发不可控验签操作理论上应该是固定流程但实测每次耗时差别很大从3ms到6ms都有。排查后发现模逆实现用的是扩展欧几里得算法执行路径与输入数据相关偶尔会多跑很多轮。换成费马小定理计算模逆之后耗时变的非常平稳虽然平均耗时略高一点点但最坏情况大幅改善。对于安全启动、密钥协商这类对时间敏感的操作稳定的最坏耗时比平均耗时更重要这也是为什么常数时间实现不能省。5.3 硬件SM4引擎吞吐率上不去硬件引擎标称吞吐很高但实际业务达不到。最典型的原因是软件驱动把数据一块一块地喂给引擎每块之间还轮询等待。正确做法是使用DMA描述符链一次提交多块缓冲区让引擎自己连续处理等全部完成后统一发中断。另一个容易被忽略的点是引擎寄存器访问最好使用内存映射MMIO的写组合方式不要用普通的volatile指针逐次读写后者会在总线上产生大量小事务降低吞吐。我实测过驱动改写之后SM4引擎吞吐率从标称值的40%提升到接近90%。5.4 ZUC密码流解不出来端序是罪魁祸首ZUC算法规定32位字的装载顺序是大端很多嵌入式平台是小端。直接把字节数组按小端方式解释成字数组就会得到完全不同的密钥流密文自然解不出来。适配时一定要按规范做字节序转换或者用REV指令高效交换字节序不要偷懒直接把指针强制转换。这个问题属于“看起来能跑但结果全错”的典型排查时非常容易怀疑算法实现有问题结果只是端序没转。5.5 开启调试器之后性能和不开不一样安全芯片通常有调试保护机制调试器连接状态下会引入额外的总线监听、Flash读取延迟和时钟变化导致性能测试数据失真。我的习惯是正式性能数据一定在完全脱离调试器、设备独立运行的情况下通过串口或日志输出测量结果。另外注意芯片的安全状态位如果芯片正处于安全模式而非用户模式有些模块访问路径不同性能也会不一样。下面把几个高频问题整理成速查表方便大家现场排查问题现象可能原因排查手段解决办法SM3吞吐率远低于预期编译器寄存器分配差、常量表在Flash看汇编、检查常量存储位置循环展开、常量表搬SRAMSM2验签耗时抖动大非常数时间模逆、随机中断干扰多次计时统计最坏值改费马小定理、屏蔽无关中断硬件SM4引擎吞吐率低驱动轮询等待、单块传输检查总线占用、DMA描述符启用DMA链、批量传输两端ZUC加解密结果不一致字节序未按大端处理对比中间密钥流加字节序转换调试器连接后性能下降调试监视导致额外访存脱离调试器测试独立运行测量6. 适配过程中的几个实操心得最后聊几点我自己的经验不保证所有芯片都适用但在多款国产安全芯片上调过之后我认为这些原则是通用的。第一“先看清楚硬件的屁股再决定优化方向”。这句话有点糙但道理实在同一套SM2优化代码在某款RISC-V芯片上窗口法效果很好换到另一款带硬件除法器的芯片上可能优化空间就小很多。一定要先看芯片手册里有没有硬件加速、有没有DMA、Cache有多大、Flash等待周期是多少这些决定你该投入时间到哪个方向。第二“性能数据别测一次就下结论”。嵌入式环境下分支预测、Cache状态、总线仲裁、温度降频都会影响单次测量。我通常每个测试项跑100次以上取中位数和最坏值并且区分“裸奔性能”和“端到端性能”。裸奔性能用于对比算法实现优劣端到端性能用于评估业务是否满足。第三“预计算表不是越大越好”。很多人以为SM2窗口越大越快SM4表合并越多性能越高实际上安全芯片的Cache资源非常敏感表太大导致Cache频繁刷新反而变慢。我习惯的做法是把性能做参数化比如窗口大小做成宏分别编译测试用数据说话。第四“安全属性不能被优化掉”。性能适配做得再好也不能以牺牲安全性为前提。比如不能因为常数时间模逆慢就换回时间可变的实现不能为了流水线效率跳过对签名结果的验证不能为了减少IO就把密钥装载过程简化过度。密码模块的优化一定要在安全规范和原理解释的边界内进行这个底线不能突破。如果你也在做国产安全芯片的国密适配建议从上面的策略入手先画一张算法和硬件资源的映射表再打一轮性能基线按优先级逐步优化时刻记录中间数据。适配的过程是重复的但每轮优化后的数据都会让你对芯片和算法的理解更深一层。希望这篇实操记录能帮你少走一些弯路把国密体系的性能真正释放出来。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →