Table of Contents
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
SM2加密性能分析
按SM2加密算法流程来看,
- 第1、2、3、4步属于SM2椭圆曲线计算;
- 第5步是KDF操作,其主要也是SM3哈希计算;
- 第6步是异或操作;
- 第7步是SM3哈希计算;
SM2加密的性能主要是由上述7步计算共同决定的,关于SM2椭圆曲线计算,和待加密数据无关,这里可以看作常量,不作讨论。第6步异或操作,相对最简单、耗时也最少。第7步SM3哈希计算,其耗时随待加密数据长度增加而增加。我们来看看性能数据:
SM2加密(明文长度不超过32字节,使第5-7步影响最小):
goos: windows
goarch: amd64
pkg: github.com/emmansun/gmsm/sm2
cpu: Intel(R) Core(TM) i5-9500 CPU @ 3.00GHz
BenchmarkLessThan32_SM2
BenchmarkLessThan32_SM2-6
17731 67668 ns/op 712 B/op 12 allocs/op
第5步KDF:
goos: windows
goarch: amd64
pkg: github.com/emmansun/gmsm/sm3
cpu: Intel(R) Core(TM) i5-9500 CPU @ 3.00GHz
BenchmarkKdfWithSM3
BenchmarkKdfWithSM3/zLen=32-kLen=32
BenchmarkKdfWithSM3/zLen=32-kLen=32-6
5110834 232.9 ns/op 32 B/op 1 allocs/op
BenchmarkKdfWithSM3/zLen=32-kLen=64
BenchmarkKdfWithSM3/zLen=32-kLen=64-6
2580963 463.4 ns/op 96 B/op 2 allocs/op
BenchmarkKdfWithSM3/zLen=32-kLen=128
BenchmarkKdfWithSM3/zLen=32-kLen=128-6
1305332 897.0 ns/op 224 B/op 3 allocs/op
BenchmarkKdfWithSM3/zLen=64-kLen=32
BenchmarkKdfWithSM3/zLen=64-kLen=32-6
2992752 399.6 ns/op 32 B/op 1 allocs/op
BenchmarkKdfWithSM3/zLen=64-kLen=64
BenchmarkKdfWithSM3/zLen=64-kLen=64-6
1893337 638.8 ns/op 96 B/op 2 allocs/op
BenchmarkKdfWithSM3/zLen=64-kLen=128
BenchmarkKdfWithSM3/zLen=64-kLen=128-6
1000000 1102 ns/op 224 B/op 3 allocs/op
BenchmarkKdfWithSM3/zLen=64-kLen=256
BenchmarkKdfWithSM3/zLen=64-kLen=256-6
574406 1982 ns/op 480 B/op 4 allocs/op
BenchmarkKdfWithSM3/zLen=64-kLen=512
BenchmarkKdfWithSM3/zLen=64-kLen=512-6
302526 3704 ns/op 992 B/op 5 allocs/op
BenchmarkKdfWithSM3/zLen=64-kLen=1024
BenchmarkKdfWithSM3/zLen=64-kLen=1024-6
155256 7910 ns/op 3296 B/op 7 allocs/op
BenchmarkKdfWithSM3/zLen=64-kLen=8192
BenchmarkKdfWithSM3/zLen=64-kLen=8192-6
19880 60780 ns/op 34272 B/op 13 allocs/op
可以看到当加密数据长度达到8K时,其耗时和完整的加密不超过32字节的耗时几乎相同。
第7步SM3哈希计算:
goos: windows
goarch: amd64
pkg: github.com/emmansun/gmsm/sm3
cpu: Intel(R) Core(TM) i5-9500 CPU @ 3.00GHz
BenchmarkHash1K
BenchmarkHash1K-6
418222 2805 ns/op 365.01 MB/s 0 B/op 0 allocs/op
BenchmarkHash8K
BenchmarkHash8K-6
57502 20781 ns/op 394.21 MB/s 0 B/op 0 allocs/op
可以看到当加密数据长度达到8K时,其耗时几乎和达到了加密不超过32字节的耗时的1/3。但是,目前SM3的软件实现的优化空间已经不大。
KDF软件实现优化分析
针对SM2的KDF,有以下特点:
- 输入的比特串Z的长度固定,为64字节,正好为SM3的一个处理块长度。第二个处理块,也是尾块,由ct开头,后续由填充和长度68构成。
- klen决定了要调用哈希运算的次数:(klen + v - 1) / v,每次哈希运算无依赖。
从上面两个特点可以看出,可以有下面优化方向:
- 每次哈希运算的第一个块是相同的,这个可以只计算一次,然后共享,避免重复计算。这个优化比较简单,尤其是自己实现的SM3。
- 每次哈希运算相互无依赖,可以并行计算。这个优化比较复杂,代码量大。
关于SM3基于SIMD的多路并行
目前已经有好多基于SIMD的哈希算法实现:MD5,SHA256,也包括SM3。通用SIMD多路并行设计实现的难点在于输入、输出协调处理,象SM2-KDF这种应用场景是最简单的:处理的数据块数相同,数据源单一。预测当待加密数据足够长的情况下,SM2加密性能能赶上(甚至超过?)无SM4-NI的SM4-CBC的性能。接下来会做一些实验性实现,观察一下效果。
关于密文格式C1C2C3和C1C3C2
C1C2C3更适合加密流式处理:
- 用公钥生成Encrypter。
- 生成随机数和点C1,随之生成Z,初始化KDF(COUNTER及HASH(Z)),输出C1点。
- XOR Stream,输出部分密文C2;同时计算C3,也就是认证码。
- Finalize,输出最终C3值。
解密过程:
- 用私钥生成Decrypter。
- 读入至少96字节,生成C1,随之生成Z,初始化KDF(COUNTER及HASH(Z))。
- XOR Stream, 输出部分明文;同时计算C3'。这一步要特殊处理,缓冲区中至少保留后32字节。
- Finalize,如果缓冲区数据长度超过32字节,则先对多出部分(前n字节)继续进行XOR stream动作,计算最终C3',和缓冲区中的最后32字节进行比较,相等则返回成功,否则失败。
由此可见,解密时C1C2C3比C1C3C2复杂一点,但C1C3C2做不到流式加密。当然,如果我们严格按照SM2非对称加密设计的初衷,只对少量数据进行加解密,则各种格式都没什么问题。
结论
经过KDF共享Z状态优化后:
goos: windows
goarch: amd64
pkg: github.com/emmansun/gmsm/sm2
cpu: Intel(R) Core(TM) i5-9500 CPU @ 3.00GHz
BenchmarkEncrypt1K_SM2
BenchmarkEncrypt1K_SM2-6
15978 74357 ns/op 13.77 MB/s 3880 B/op 14 allocs/op
BenchmarkEncrypt8K_SM2
BenchmarkEncrypt8K_SM2-6
7197 140847 ns/op 58.16 MB/s 18344 B/op 13 allocs/op
BenchmarkSM4CBCEncrypt1K
BenchmarkSM4CBCEncrypt1K-6
142844 8071 ns/op 126.88 MB/s 0 B/op 0 allocs/op
BenchmarkSM4CBCEncrypt8K
BenchmarkSM4CBCEncrypt8K-6
18459 65322 ns/op 125.41 MB/s 0 B/op 0 allocs/op
BenchmarkSM4GCMSeal1K
BenchmarkSM4GCMSeal1K-6
514671 2036 ns/op 502.89 MB/s 0 B/op 0 allocs/op
BenchmarkSM4GCMSeal8K
BenchmarkSM4GCMSeal8K-6
76536 15293 ns/op 535.67 MB/s 0 B/op 0 allocs/op
KDF AVX2 8路并行后:
goos: windows
goarch: amd64
pkg: github.com/emmansun/gmsm/sm3
cpu: Intel(R) Core(TM) i5-9500 CPU @ 3.00GHz
BenchmarkKdfWithSM3
BenchmarkKdfWithSM3/zLen=32-kLen=32
BenchmarkKdfWithSM3/zLen=32-kLen=32-6
5110020 229.9 ns/op 32 B/op 1 allocs/op
BenchmarkKdfWithSM3/zLen=32-kLen=64
BenchmarkKdfWithSM3/zLen=32-kLen=64-6
2790901 423.9 ns/op 64 B/op 1 allocs/op
BenchmarkKdfWithSM3/zLen=32-kLen=128
BenchmarkKdfWithSM3/zLen=32-kLen=128-6
2514219 467.8 ns/op 128 B/op 1 allocs/op
BenchmarkKdfWithSM3/zLen=64-kLen=32
BenchmarkKdfWithSM3/zLen=64-kLen=32-6
3024373 399.9 ns/op 32 B/op 1 allocs/op
BenchmarkKdfWithSM3/zLen=64-kLen=64
BenchmarkKdfWithSM3/zLen=64-kLen=64-6
2027554 596.3 ns/op 64 B/op 1 allocs/op
BenchmarkKdfWithSM3/zLen=64-kLen=128
BenchmarkKdfWithSM3/zLen=64-kLen=128-6
1744693 691.4 ns/op 128 B/op 1 allocs/op
BenchmarkKdfWithSM3/zLen=64-kLen=256
BenchmarkKdfWithSM3/zLen=64-kLen=256-6
1397571 811.7 ns/op 256 B/op 1 allocs/op
BenchmarkKdfWithSM3/zLen=64-kLen=512
BenchmarkKdfWithSM3/zLen=64-kLen=512-6
862700 1385 ns/op 512 B/op 1 allocs/op
BenchmarkKdfWithSM3/zLen=64-kLen=1024
BenchmarkKdfWithSM3/zLen=64-kLen=1024-6
507590 2364 ns/op 1024 B/op 1 allocs/op
BenchmarkKdfWithSM3/zLen=64-kLen=8192
BenchmarkKdfWithSM3/zLen=64-kLen=8192-6
70632 17524 ns/op 8192 B/op 1 allocs/op
goos: windows
goarch: amd64
pkg: github.com/emmansun/gmsm/sm2
cpu: Intel(R) Core(TM) i5-9500 CPU @ 3.00GHz
BenchmarkEncrypt1K_SM2
BenchmarkEncrypt1K_SM2-6
16662 72808 ns/op 14.06 MB/s 2856 B/op 13 allocs/op
BenchmarkEncrypt8K_SM2
BenchmarkEncrypt8K_SM2-6
10861 111046 ns/op 73.77 MB/s 18344 B/op 13 allocs/op
BenchmarkEncrypt1M_SM2
BenchmarkEncrypt1M_SM2-6
205 5856919 ns/op 179.03 MB/s 2106029 B/op 13 allocs/op
BenchmarkSM4CBCEncrypt1M
BenchmarkSM4CBCEncrypt1M-6
100 10017195 ns/op 104.68 MB/s 10489 B/op 0 allocs/op
当待加密数据足够长时,SM2加密性能比SM4-CBC要好,只是多用一些内存。