← 首页 1对称算法

1对称算法

用于加密、认证、加密认证的加密算法

DES

DES是分组加密算法,以64位为分组对数据进行加解密,密钥64位(实际参与运算56位)

特点:分组短、密钥短、运算速度慢

IP置换

将输入的64位数据块按位重新组合,并把输出分为L0、R0两部分,各32位。
IP置换遵循下表规则(8*8)

密钥置换

不考虑每个字节的第8位,DES密钥由64位减至56位,每个字节的第8位为奇偶校验位。
产生56位密钥,遵循下表规则(4*14),注意没有(8/16/24/32/40/48/56/64)

E拓展置换

S盒代替

P盒置换

IP^-1末置换

DES与TDES

算法 分组长度(字节) 密钥 加密 解密 速度(相对)
DES 8 64 E D 1
TDES_128 8 128 EDE DED 1/3
TDES_192 8 192 EDE DED
TDES_EEE_128 8 128 EEE DDD
TDES_EEE_192 8 192 EEE DDD

注:
1、TDES_EEE表示的是非标准3DES,即其加密是连续三次加密,其解密实连续三次解密
2、TDES_192的加解密密钥使用顺序:k1、k2、k3
3、TDES_128的加解密密钥使用顺序:k1、k2、k1

TDES128计算CFB流程示例

//TDES128-CFB_block-ENC
//plain
81709944E0CB2E1DB5B0A477D1A8539B
//key
E07099F1BFAFFD7F
240CD790CA4FE134
//iv
C72B6591A0D7DE8F
//cipher
DE658657CEC1D13A4C416ED7E83CAB85


//step
//C1, key=E07099F1BFAFFD7F,crypto=enc
DD5F07CEFABC934E52D7417C07F8EF30
//C2,key=240CD790CA4FE134,cipher=DD5F07CEFABC934E52D7417C07F8EF30,crypto=dec
56D029BFEE32C886DB98EBFDFC35476D
//C3,key=E07099F1BFAFFD7F,plain=56D029BFEE32C886DB98EBFDFC35476D,crypto=enc
00E7549663AB000094840B2FE6190DCC



//TDES128-CFB-ENC
//plain
81709944E0CB2E1D
//key
E07099F1BFAFFD7F
240CD790CA4FE134
//iv
C72B6591A0D7DE8F
//cipher
B3F76EB9504F5B5D

//step
//C1, key=E07099F1BFAFFD7F,crypto=enc
DD5F07CEFABC934E
//C2,key=240CD790CA4FE134,cipher=DD5F07CEFABC934E,crypto=dec
56D029BFEE32C886
//C3,key=E07099F1BFAFFD7F,plain=56D029BFEE32C886,crypto=enc
00E7549663AB0000




//TDES128-CFB-ENC
//plain
81709944E0CB2E1D
//key
E07099F1BFAFFD7F
240CD790CA4FE134
//cipher
C00727761EC27282

//step
//C1, key=E07099F1BFAFFD7F,crypto=enc
60DFC6E3581378FB
//C2,key=240CD790CA4FE134,cipher=60DFC6E3581378FB,crypto=dec
6380CA29F4F4BAB0
//C3,key=E07099F1BFAFFD7F,plain=6380CA29F4F4BAB0,crypto=enc
C00727761EC27282

AES

高级加密标准,AES区块长度固定为128bit,密钥可以是128、192、256bits。
《RFC3394》定义密钥封装算法

AES128 AES192 AES256
key_byte 16 24 32
block_byte 16 16 16
Number of round 10 12 14
round key size (words/bytes) 4/16 4/16 4/16
expanded key size (words/bytes) 44/176 52/208 60/240

特点:运算速度快、安全性高、资源消耗小

SM4

2006年发布,同年8月成为国家标准,该算法公开。
key长度16字节。
原名SMS4

《GB/T 36624-2018》定义密钥封装算法

ZUC

序列密码,密钥长度128bit,由128bit种子密钥和128bit IV共同生成32bit密钥流

GCM

参考文档:《NIST-SP800-38D》

GCM计算流程

1)加密流程:

Input:aad、payload、iv、mac_byte(不影响,取最高位Nbytes)
output:cipher、mac(长度为mac_bytes)

image-20240718143758752

Step:
1.Let H = CIPHk(128位全0)
2.define a block J0 :
if byte_len(IV) == 12 then J0= IV||00..00||1
else J0 = GHASHh(IV || 0->(s+64)bit || len(IV)->64bit )
其中s=128 * upper_integer(len(IV)/128) - len(IV) //upper_integer向上取整,不小于实数X的整数
3.Let C = GCTRk(inc32(J0), P)
4.Let u = 128 * upper_integer(len(C)/128) - len(C)
5.Let v = 128 * upper_integer(len(A)/128) - len(A)
6.define a block S = GHASHh(A || 0->vbit || C || 0->ubit || len(A)->64bit || len(C)->64bit)
7.let T = MSB(GCTRk(J0, S))
8.return (C, T)

注:注意第3步,密文的计算还是CTR模式,不过参与计算的IV起始是00...002(对于iV长度为96bit来说)

//---------------------Input---------------------//
//key-16byte
FEFFE9928665731C6D6A8F9467308308
//plain-64byte
d9313225f88406e5a55909c5aff5269a
86a7a9531534f7da2e4c303d8a318a72
1c3c0c95956809532fcf0e2449a6b525
b16aedf5aa0de657ba637b391aafd255
//iv-12byte
cafebabefacedbaddecaf888
//aad-20bytes
feedfacedeadbeeffeedfacedeadbeef
abaddad2000000000000000000000000

//---------------------Output---------------------//
//cipher-64byte
//iv长度12byte,J0=CAFEBABEFACEDBADDECAF88800000001
//ctr-iv(即J1):CAFEBABEFACEDBADDECAF88800000002
42831ec2217774244b7221b784d0d49c
e3aa212f2c02a4e035c17e2329aca12e
21d514b25466931c7d8f6a5aac84aa05
1ba30b396a0aac973d58e091473f5985
//mac
DA80CE830CFDA02DA2A218A1744F4C76

//构造IV-16byte
cafebabefacedbaddecaf88800000000

构造blocks

blocks=aad||padding(aad)||payload||padding(payload)
注:如果aad或payload实际长度已经分组整数倍,则不需要padding

GHASH计算实现

prerequisites:
block H
hash subkey

Input:
比特串X,比特长度128对齐

Output:
block GHASHh(X)

Steps:
1.Let X1, X2, ..., Xm-1, Xm denote the unique sequence of blocks such that X = X1 || X2 || ... || Xm-1 || Xm
2.Let Y0 be the "zero block", 0128
3.For i=1,...,m, let Yi = (Yi-1 xor Xi) * H
4.return Ym

Output:


chacha20_poly1305

标准文档《RFC8349》

chacha20_poly1305,已完成两版本,一个是自写实现,调用openssl大数计算,另外一版本是从openssl抽出chacha20、poly1305源码,其余自写,已发布在公众号。

chacha理论上可以做到任意输入,同时任意输出,这取决于算法的特性。首先硬件update以分组为单位,从算法层面分析,不同于AES/DES/SM4对原始消息操作(分组内的不同字节值变化影响整体加解密结果),CHACHA是先生成密钥流,最后与原始消息异或得到密文,不受原始消息字节值排布影响,只跟IV和分组counter有关。设计需要明确知道当前输入消息处于的counter,以及偏移即可支持任意输入输出,改造CPU模式支持该方式

对加密全0的原始消息,会不会存在泄露密钥风险

在chacha20轮函数中,a += b存在回绕
Qround:
  a += b; d ^= a; d <<<= 16;
  c += d; b ^= c; b <<<= 12;
  a += b; d ^= a; d <<<= 8;
  c += d; b ^= c; b <<<= 7;
the constant prime "P" be 2^130-5=0x3fffffffffffffffffffffffffffffffb

参考自写公众号链接:https://mp.weixin.qq.com/s/ojxEvq9sPkBKbLgqbNWrzQ?token=1386669807&lang=zh_CN

工作模式

PIPS:联邦信息处理标准

Counter Mode(CTR)
用密钥对输入的计数器加密,然后跟明文异或得到密文。优点是可以并行,避免误差传递问题。缺点是可以针对单个数据块进行攻击。

Counter with CBC-MAC Mode(CCM)
CCM是在CBC和CTR的基础上增加CMAC算法校验,主要是通过MAC校验来解决CTR容易被攻击的问题。

Galois/Counter Mode(GCM)
GCM是在CCM的基础上改用GMAC算法代替CMAC算法,增加了并行化设计,可以提高加密性能,降低时延。

CBC-MAC:密码块链接-身份消息认证
CCM: 计数器与密码块-身份消息认证

CCM*

标准链接:https://mentor.ieee.org/802.15/dcn/04/15-04-0537-02-004b-formal-specification-ccm-star-mode-operation.pdf

该标准指定了L和M的范围:
L:2,3,4,5,6,7,8
M:0,4,6,8,10,12,14,16
ByteLen(msg):[0, 28L)
ByteLen(aad):[0, 264)

CCM*是标准CCM的一些特例,外加新增一个扩展(即只加密,无认证)。

可以不修改算法库,分三种情况。

  1. 同时要加密和认证,当前已经满足。

  2. 只要认证,可以把消息作为AAD,而明文参数设置为NULL,做CCM加密就可以。

  3. 只要加密,当前库的CCM接口不满足,但此时CCM退化为CTR模式,用户只要丢弃AAD,自己构造下初始IV(即初始counter),调用CTR接口就可以。

初始IV构造很简单,即 0x01 || 13字节的nonce || 0x00 || 0x01。

CCM(Counter with CBC-MAC)

Application: 802.11协议族
在CBC和CTR基础上增加CMAC,主要通过MAC校验来解决CTR容易被攻击的问题。

《nist sp800-38c》为对称密钥块密码算法定义了一种操作模式,称为带密码块链接消息验证码(CCM)的计数器。CCM 可用于通过结合计数器 (CTR) 模式和密码块链接-消息验证码 (CBC-MAC) 算法的技术来保证计算机数据的机密性和真实性。

CCM=CBC-MAC认证 + CTR加密
CBC-MAC认证算法输出:长度为M的认证字段T
CTR加密算法针对两种数据进行加密:
(1)对T进行CFB/OFB加密,输出加密后的认证字段U
(2)对消息字段M(划分为一个或者多个blocks,不足一个block部分补0)进行CTR 加密

CCM参数

//认证域的字节长度,即认证tag(如下用U表示)的字节长度,有效值是在{4,6,8,10,12,14,16}中选取。M越大,则攻击者篡改后仍有效的可能越小,也即越安全。
M
//长度域的长度,即消息字节长度占用的字节数,有效值在2到8个字节(1个字节是保留值)
L
//一次性的认证信息,字节长度是15-L
Nonce
//额外的认证消息,其字节长度表示为len(a),要求0≤len(a)<2^64
AAD:

GCM(The Galois/Counter Mode of Operation)

见上述GCM计算流程

认证加密

目前先加密,再认证,是三者中最安全的。

//同时加密和MAC
C = Enc(K,M)
T = Mac(M)
//先加密、再MAC
C = Enc(K,M)
T = Enc(C)
//先MAC、再加密  //选择密文攻击
C = Enc(K,(M||Mac(M))

CFB

由NIST SP800-38A所定义的密码反馈模式(Cipher Feedback),特指CFB128

image-20230711163559969

全称:Cipher Feedback Mode
参考链接:https://www.zhihu.com/column/c_1380896366827388928

加密参数:明文P、加密密钥Key、初始向量IV
加密步骤
1.将P分为P0、P1、...、Pn,len(Pi) <= 128
2.用Key加密IV得到IV0,将IV0、P0做异或运算得到C0
3.用Key加密C0得到IV1,将IV1、P1做异或运算得到C1
...
4.用Key加密Cn-2得到IVn-1,将IVn-1、Pn-1做异或运算得到Cn-1
5.用Key加密Cn-1得到IVn,将IVn、Pn做异或运算得到Cn
6.拼接C0、C1、...、Cn得到密文,加密结束

解密参数:密文C、加密密钥Key、初始向量IV
解密步骤
1.将P分为C0、C1、...、Cn,len(Ci) <= 128
2.用Key加密IV得到IV0,将IV0、C0做异或运算得到P0
3.用Key加密C0得到IV1,将IV1、C1做异或运算得到P1
...
4.用Key加密Cn-2得到IVn-1,将IVn-1、Cn-1做异或运算得到Pn-1
5.用Key加密Cn-1得到IVn,将IVn、Cn做异或运算得到Pn
6.拼接P0、P1、...、Pn得到明文,解密结束

特点
1.CFB采用分组算法实现流算法(分组不大于16字节),明文数据不需要按分组大小对齐
2.加密不可并行,解密可以并行(当前分组解密需要上一分组密文经过Key加密后的值,与当前分组的密文异或)

OFB

由NIST SP800-38A所定义的输出反馈模式(Output Feedback)

image-20230711163658494

全称:Output Feedback Mode
参考链接:https://www.zhihu.com/column/c_1380896366827388928

加密参数:明文P、加密密钥Key、初始向量IV
加密步骤
1.将P分为P0、P1、...、Pn,len(Pi) <= 128
2.用Key加密IV得到IV0,将IV0、P0做异或运算得到C0
3.用Key加密IV0得到IV1,将IV1、P1做异或运算得到C1
...
4.用Key加密IVn-1得到IVn-1,将IVn-1、Pn-1做异或运算得到Cn-1
5.用Key加密IVn-1得到IVn,将IVn、Pn做异或运算得到Cn
6.拼接C0、C1、...、Cn得到密文,加密结束

解密参数:密文C、加密密钥Key、初始向量IV
解密步骤
1.将C分为C0、C1、...、Cn,len(Ci) <= 128
2.用Key加密IV得到IV0,将IV0、C0做异或运算得到P0
3.用Key加密IV0得到IV1,将IV1、C1做异或运算得到P1
...
4.用Key加密IVn-1得到IVn-1,将IVn-1、Cn-1做异或运算得到Pn-1
5.用Key加密IVn-1得到IVn,将IVn、Cn做异或运算得到Pn
6.拼接P0、P1、...、Pn得到明文,解密结束

特点
1.与CFB相同:分组、参数、加密模式类似:加密IV-与原文异或;
OFB的分组密文是异或出来的,可以理解为先加密"IV",再与分组原文异或
2.与CFB不同:OFB的当前分组输入IV是上一分组的已加密的IV(输出反馈);
CFB的当前分组输入IV是上一分组的密文(密文反馈);

3.OFB加解密理论上可以并行:先把所有分组对应的IVi计算出来,IV1=Enc(Key,IV),IV2=Enc(Key,IV1),然后每个分组各自加密(Key、IV、P)或解密(Key、IV、C),注意加解密的IV值是一样的。

4.克服CBC、CFB的错误传播问题(1bit错误只导致明文1bit的错误)

CTR

由NIST SP800-38A所定义的计数器模式(Counter),特指CTR32,参考RFC 3686标准

image-20230711163814847

全称:Counter Mode
参考链接:https://www.zhihu.com/column/c_1380896366827388928

加密参数:明文P、加密密钥Key、初始向量IV、随机数Nonce
加密步骤
1.将P分为P0、P1、...、Pn,len(Pi) <= 128
2.生成计数器Ctr0、Ctr1、...、Ctrn
3.用Key加密Nonce||Ctr0得到IV0,将IV0与P0异或得到C0
4.用Key加密Nonce||Ctr1得到IV1,将IV1与P1异或得到C1
...
5.用Key加密Nonce||Ctrn-1得到IVn-1,将IVn-1与Pn-1异或得到Cn-1
6.用Key加密Nonce||Ctrn得到IVn,将IVn、Pn做异或运算得到Cn
7.拼接C0、C1、...、Cn得到密文,加密结束

解密参数:密文C、加密密钥Key、初始向量IV、随机数Nonce
解密步骤
1.将P分为C0、C1、...、Cn,len(Ci) <= 128
2.生成计数器Ctr0、Ctr1、...、Ctrn
3.用Key加密Nonce||Ctr0得到IV0,将IV0与C0异或得到P0
4.用Key加密Nonce||Ctr1得到IV1,将IV1与C1异或得到P1
...
5.用Key加密Nonce||Ctrn-1得到IVn-1,将IVn-1与Cn-1异或得到Pn-1
6.用Key加密Nonce||Ctrn得到IVn,将IVn、Cn做异或运算得到Pn
7.拼接P0、P1、...、Pn得到明文,解密结束

特点:
1.加解密可并行,先把所有分组的IV值计算出来,各分组单独运行(C/P、Nonce||Ctr、Key)

//SM4 example
//key
ffffffffffffffffffffffffffffffff
//iv
ffffffffffffffffffffffffffffffff
//plain
ffffffffffffffffffffffffffffffff
//ECB(key, iv)
EDB119F8455FD2327AD6FBEDCB5B4F5D
//ECB(key, iv) ^ plain
EDB119F8455FD2327AD6FBEDCB5B4F5D ^ ffffffffffffffffffffffffffffffff
=124EE607BAA02DCD8529041234A4B0A2

CBC

image-20230711163445872

由NIST SP800-38A所定义的密码分组链接模式(Cipher Block Chaining)

Cipher Block Chaning密码块链接
https://blog.csdn.net/u013073067/article/details/87086562

加密参数:明文P、加密密钥Key、初始向量IV0
加密步骤
1.将P分组P0、P1、...、Pn,Pi=密钥模长
2.P0与初始向量IV0异或,再经过Key加密,得到C0
3.P1与C0异或,再经过Key加密,得到C1
4.P2与C1异或,再经过Key加密,得到C2
...
5.Pn与Cn-1异或,再经过Key加密,得到Cn
6.拼接C0、C1、...、Cn得到密文,加密结束

解密参数:密文C、加密密钥Key、初始向量IV0
解密步骤
1.将C分组C0、C1、...、Cn,Ci=密钥模长
2.C0经过Key解密,再与初始向量IV0异或,得到P0
3.C1经过Key解密,再与C0异或,得到P1
4.C2经过Key解密,再与C1异或,得到P2
...
5.Cn经过Key解密,再与Cn-1异或,得到Pn
6.拼接P0、P1、...、Pn得到明文,解密结束

特点:
加密:速度慢、对比ECB无法看出明文的规律
解密:速度快(可并行解密,只需要Key、C参与,每一分组的解密需要前一分组的密文)

PCBC

Propagating Cipher Block Chaining

加密参数:明文P、加密密钥Key、初始向量IV,数据填充模式M
加密步骤
1.将P分组P0、P1、...、Pn,Pi=密钥模长,长度不足需要填充
2.P0、IV异或,再经过Key加密,得到C0,下一分组IV1 = P0与C0异或
3.P1、IV1异或,再经过Key加密,得到C1,下一分组IV2 = P1与C1异或
4.P2、IV2异或,再经过Key加密,得到C2,下一分组IV3 = P2与C2异或
...
5.Pn、IVn异或,再经过Key加密,得到Cn
6.拼接C0、C1、...、Cn得到密文,加密结束

XTS

XEX(Xor–Encrypt–Xor) encryption mode with tweak and ciphertext stealing或XEX-based tweaked-codebook mode with ciphertext stealing (XTS), 由《IEEE p1619-2007》定义的对称分组密码算法的一种加密模式,使用了密文窃取技术,主要用于存储数据加密(如磁盘)

【128-bit-block-Enc:C = XTS-AES-block_Enc(Key, P, i, j)】
Key:256或者512bit,长度为普通AES密钥长度的2倍,等分为两个AES密钥,Key=key1||key2
P:128bit明文
i:128-bit tweak value
j:当前128-bit block在DataUnit内的序号
C:128bit密文

【DataUnit-Enc:C = XTS-AES-block_Enc(Key, P, i)】

XTS_ENC-for Plen>=128bit or more

//《IEEE_1619_2007_standard_for_cryptographic_protection_of_data_on_block_oriented_storage_devices.pdf》
//Input
Key //is the 256 or 512 bit XTS key
P //is the plaintext, bitlen(p)>=128
i //is the value of the 128-bit tweak
//Output
C

//calc step
P = P0||P1||...||Pm
1.for q <- 0 to m-2 do
    Cq = XTS_blockEnc(key, Pj, i, q)
2.b=bit_len(Pm)
3.if b == 0
    Cm-1 = XTS_blockEnc(key,Pm-1, i, m-1)
    Cm = empty
  else
    CC=XTS_blockEnc(key,pm-1,i,m-1)
    Cm=first b bits of CC
    CP=last (128-b) bits of CC
    PP=Pm || CP
    Cm-1=XTS_blockEnc(key,PP,i,m)
4.return C=C1||...||Cm-1||Cm

//XTS_blockEnc calc step
key=key1||key2
T=Enc(key2, i) ⊗ (alpha^j)
PP = P ⊕ T
CC=Enc(key1, PP)
C=CC ⊕ T

//T ⊗ (alpha)// multiply T by alpha
//input:T
//output:T
Cin = 0;
for (j=0;j<16;j++)
{
    Cout=(T[j]>>7)&1
    T[j]=((T[j]<<1)+Cin) & 0xFF
    Cin=Cout
}
if Cout
    T[0]^=0x87//GF_128_FDBK
//XTS AES128_Enc Demo
//Input
//key-256bit-32byte
27F86757EE3A22CF321E2C04931DD95F//key1
9DAD1FD72D56817C4E3E0A49AA462378//key2
//i
11223344556677881122334455667788//must be a block
//plain-33byte
27C066D4F46C716C9C7F7ED01F4B6812
CAE8E5549F276F703D7AEBFA397A74D3
11

//output
6654931E11319CAECDA1801149E740E2
18713E0EC80926DF795E3613E6570E21
01

//block-1
//T = AES128_ECB(key2,i)
D1184F906DB0B8368CC8639E3F337011
//PP = P ⊕ T
27C066D4F46C716C9C7F7ED01F4B6812^
D1184F906DB0B8368CC8639E3F337011
=F6D8294499DCC95A10B71D4E20781803
//CC=Enc(key1, PP)
=Enc(27F86757EE3A22CF321E2C04931DD95F,F6D8294499DCC95A10B71D4E20781803)
=B74CDC8E7C8124984169E38F76D430F3
//C=CC ⊕ T
B74CDC8E7C8124984169E38F76D430F3^
D1184F906DB0B8368CC8639E3F337011
=6654931E11319CAECDA1801149E740E2

//block-2 and block-3 (last)
// T ⊗ (alpha)// multiply T by alpha
a2319e20db60716d1891c73c7f66e022

//calc CC
//CC = Pm-1 ⊕ T
CAE8E5549F276F703D7AEBFA397A74D3^
a2319e20db60716d1891c73c7f66e022
=68D97B7444471E1D25EB2CC6461C94F1
//CC=Enc(key1, CC)
=Enc(27F86757EE3A22CF321E2C04931DD95F,68D97B7444471E1D25EB2CC6461C94F1)
=A33A4CEE7D5D5BCFB8868304988D182B
//CC=CC ⊕ T
A33A4CEE7D5D5BCFB8868304988D182B^
a2319e20db60716d1891c73c7f66e022
=010BD2CEA63D2AA2A0174438E7EBF809

//Cm=first b bits of CC
Cm=01

//PP = Pm||CP=last(128-b) bits of CC
11||0BD2CEA63D2AA2A0174438E7EBF809

//clac Cm-1
// T ⊗ (alpha)// multiply T by alpha
44633c41b6c1e2da30228f79feccc045
//Cm-1 = PP ⊕ T
110BD2CEA63D2AA2A0174438E7EBF809^
44633c41b6c1e2da30228f79feccc045
=5568EE8F10FCC8789035CB411927384C
//Cm-1=Enc(key1, CC)
=Enc(27F86757EE3A22CF321E2C04931DD95F,5568EE8F10FCC8789035CB411927384C)
=5C12024F7EC8C405497CB96A189BCE64
//Cm-1=Cm-1 ⊕ T
5C12024F7EC8C405497CB96A189BCE64^
44633c41b6c1e2da30228f79feccc045
=18713E0EC80926DF795E3613E6570E21

C=C1||Cm-1||Cm
=6654931E11319CAECDA1801149E740E2||18713E0EC80926DF795E3613E6570E21||01

最后两个分组处理流程(非分组对齐)

image-20250528104652524

ECB

由NIST SP800-38A所定义的密码分组链接模式(Cipher Block Chaining)

过于简单,略。

HCTR2

标准链接:https://csrc.nist.gov/csrc/media/Events/2023/third-workshop-on-block-cipher-modes-of-operation/documents/accepted-papers/Length%20Preserving%20Encryption.pdf

各模式对比

操作对象

模式 KEY个数 IV 加密对象 并行处理
ECB 1 N 明文分组 Enc&Dec
CBC 1 Y 明文分组与前一分组的异或后的值 Dec
CFB 1 Y 前一分组输出的密文 Dec
OFB 1 Y 前一分组中对IV加密后的值 NA
CTR 1 N 计数器CRT的值 Enc&Dec
XTS 2 N 明文和tweak值 Enc&Dec

优缺点

模式 优 缺
ECB 1.简单
2.快速
3.支持并行计算
1.明文中的重复排列会反馈在密文中
2.通过删除、替换密文分组可以对明文进行操作
3.不能抵御重放攻击
CBC 1.明文的重复排列不会反馈在密文中
2.支持并行计算(仅解密)
3.能够解密任意密文分组
1.加密不支持并行计算
2.对包含某些错误比特的密文进行解密时,第一个分组的全部比特以及后一个分组的相应比特会出错
CFB 1.不需要填充
2.支持并行计算(仅解密)
3.能解密任意密文分组
1.加密不支持并行计算
2.对包含某些错误比特的密文进行解密时,第一个分组的全部比特以及后一个分组的相应比特会出错
3.不能抵御重放攻击
OFB 1.不需要填充
2.可事先进行加密解密的准备
3.加解密使用相同结构
4.对包含某些错误bit的密文进行解密时,只有明文中相应的bit会出错
5.支持并行计算(加解密)
1.不支持并行计算
2.主动攻击者翻转密文分组中的某些bit,明文分组中相应的bit也会被翻转
CTR 1.不需要填充
2.可事先进行加密解密的准备
3.加解密使用相同结构
4.对包含某些错误bit的密文进行解密时,只有明文中相应的bit会出错
5.支持并行计算(加解密)
1.主动攻击者翻转密文分组中的某些bit,明文分组中相应的bit也会被翻转
XTS 1.支持并行计算(加解密)
2.可事先进行加密解密的准备
1.不能抵御重放攻击
2.对包含某些错误bit的密文,第一个分组的全部bit以及后一分组的相应bit会出错

填充模式

NoPadding

不填充,在此模式下原始数据必须是分组长度的整数倍

PKCS5_PKCS7

PKCS5:块大小固定为8
PKCS7:块大小1-255
以AES为例:分组长度16字节

填充方式为:满分组需要增加1block,不满分组需要填充至满分组,每字节填充值为(blocklen-bytes%blocklen)

原始:11223344556677881122334455667788
填充:11223344556677881122334455667788 10101010101010101010101010101010

原始:112233445566778811
填充:11223344556677881107070707070707

ISO/IEC7816-4

填充方式为:满分组需要增加1block,不满分组需要填充至满分组,先在数据右侧填充80,其他填充00至满分组

以AES为例:分组长度16字节

原始:11223344556677881122334455667788
填充:11223344556677881122334455667788,80000000000000000000000000000000

原始:112233445566778811
填充:11223344556677881180000000000000

ZeroPadding

填充00至满分组,若原始数据为分组整数倍,不需要填充

原始:11223344556677881122334455667788
填充:11223344556677881122334455667788

原始:112233445566778811
填充:11223344556677881100000000000000

PKCS1Padding

该填充模式是 RSA 加密中使用的,详见 RFC 2313。RSA 加密时,需要将原文填充至密钥大小,填充的格式为:00 + BT + PS + 00 + D。

  • 00 为固定字节

  • BT 为处理模式。公钥操作时为 02,私钥操作为 00 或 01

  • PS 为填充字节,填充数量为 k - 3 - D,k 表示密钥长度,D 表示原文长度。PS 的最小长度为 8 个字节。填充的值根据 BT 值不同而不同:

  • BT = 00 时,填充全 00

  • BT = 01 时,填充全 FF

  • BT = 02 时,随机填充,但不能为 00

ANSI X.923

填充的字节长度=块长度-(数据长度%块长度)

当不满分组长度时,填充“0”字节。最后一个字节填充padding的长度

1122334455667788 || 1122334455000003

当满分组长度时,

1122334455667788 || 0000000000000008

ISO 10126

填充若干随机字节,最后一个字节填充padding长度

ISO IEC 9797-1-2011

Padding Method 1
消息末尾填充尽可能少的0000直到满分组
注:如果消息为空,填充满分组0;如果满分组,不填充

Padding Method 2
消息末尾写1bit值‘1’,填充0000直到满分组
注:如果消息为空,填充1000..00直满分组;如果满分组,填充1000..00直满分组

Padding Method 3
输入到 MAC(消息认证码)算法的数据字符串 D应该右填充尽可能少的(可能为零个)‘0’ 位,以获得一个长度(以位为单位)为正整数倍的 的数据字符串。结果字符串随后将左填充一个块 L。块 L 由原始(未填充)数据字符串 D 的长度(以位为单位)LD 的二进制表示构成,并根据需要左填充尽可能少的(可能为零个)‘0’ 位,以形成一个 n 位的块。块 L 的最右边位对应于 LD 的二进制表示的最低有效位。

Padding Method 4
如果输入到 MAC(消息认证码)算法的数据字符串 D 的长度(以位为单位)是正整数倍的 n,则不应进行填充。否则,数据字符串 D 应该右填充一个单独的‘1’位。然后,结果字符串应继续右填充尽可能少的(可能为零个)‘0’位,以获得一个长度(以位为单位)为正整数倍的 n 的数据字符串。
注:跟【Padding Method 2】非常类似,区别在于满分组下,该padding不需要填充

填充示例,取自标准(标准只提供了Method1/2/3)

//原始数据-DES
4E6F77206973207468652074696D6520666F7220616C6C20  //3block=24byte=192bit
//Padding Method 1
4E6F77206973207468652074696D6520666F7220616C6C20
//Padding Method 2
4E6F77206973207468652074696D6520666F7220616C6C208000000000000000
//Padding Method 3
00000000000000C04E6F77206973207468652074696D6520666F7220616C6C20
//Padding Method 4
4E6F77206973207468652074696D6520666F7220616C6C20

9、Padding Oracles

如果输入的密文不合法,类库则会抛出异常,这便是一种提示。攻击者可以不断地提供密文,让解密程序给出提示,不断修正,最终得到的所需要的结果。这里的一个关键在于,攻击者所需要的提示仅仅是“解密成功与否”这样一个二元信息

【示例1】
前提
1.假设明文“TEST”(攻击者未知)经过加密成密文:6D367076036E2239F851D6CC68FC9537
2.服务器对解密只返回解密成功(不对比原文)或者填充位错误

攻击
手段:破解得到中间值(服务器对密文加密后的值),然后与IV异或得到明文
1.由于IV和密文分组等长,得IV=6D367076036E2239,C=F851D6CC68FC9537
2.IV置零,假设解密后的明文最后一字节为01(含填充),改变IV最后一个字节,IV=00000000000000XX,将C和IV上送服务器解密,直到不报填充位错误,最多256次遍历
3.将XX与01异或,得到中间值最后一字节TT
4.继续,IV=000000000000XXTT,将C和IV上送服务器解密,直到不报填充位错误,最多256次遍历
5.将XX与02异或,得到中间值TT
......
6.以此类推,直到中间值全部找出来,与密文异或,得到明文

攻击防范
1)先加密再认证
2)Nonce机制:Nonce不重用

密钥协商算法

Diffie-Hellman

国密算法

SM4(SMS4):对称算法,算法公开,分块长度16字节
SM1(SCB/SCB2):对称算法,算法不公开,分块长度16字节
SSF33:对称算法,算法不公开,分块长度16字节
SM6:对称算法,算法不公开,分块长度16字节
SM7:对称算法,算法不公开,分块长度16字节

10、密钥和分组长度

算法 key长度bit 分组长度bit
DES 64 64
TDES-128(2key) 128 64
TDES-192(3key) 192 64
AES-128 128 128
AES-192 192 128
AES-256 256 128
SM4 128 128

消息认证码

CBC-MAC流程

特点:
1)不需要IV(IV = 0)

IV = 00...00
for i = 1 to n //n分组
{
    temp = M_i XOR IV
    temp = Enc(K,temp)
    IV = temp;
}
最后一个密文block作为MAC

另外有标准,提到MAC的一些填充方式, 如《ISO/IEC 9797-1》

CMAC

由NIST SP800-38B所定义的用于认证的CMAC模式(Cipher-based Message Authentication Code)

标准:《RFC3610》《NIST-SP800-38B》

//《NIST-SP800-38B》
//Input,两个子密钥生成
Key
block cipher CIPH with block size b
//Output
subKeys K1、K2

//Step
1)当原文长度不是分组长度的整数倍时(10填充),最后一组明文与k2异或后进行CBC加密
2)当原文长度为分组长度的整数倍时,最后一组明文(不需padding)与k1异或后进行CBC加密
3)T长度至少64bit

当消息为空,block数量为1,即10填充后,与k2异或后进行CBC加密

子密钥生成流程

Prerequisites:
block cipher CIPH with block size b
key K

Output:
subkeys k1,k2

Suggested Notation:
SUBK(K)

Steps:
1.Let L = CIPH_k(0_b)//使用密钥对全0的分组做ECB加密
2.if MSB_1(L) = 0, then k1 = L<<1;
  else k1 = (L<<1) xor B_b //see follow
3.if MSB_1(k1) = 0, then K2 = k1<<1
  else k2 = (k1<<1) xor R_b
4.return k1,k2

R_128 = 0_120||10000111 = (0_15||87)hex
R_64 = 0_59||11011 = (0_7||1B)hex

CMAC计算示例

//AES128-CMAC
//key
e07099f1bfaffd7f240cd790ca4fe134
//k1
7051501d5233e9d2e1f43857dc30888c
//k2
e0a2a03aa467d3a5c3e870afb8611118

L=DES_ECB(key,00.00)=3828A80EA919F4E970FA1C2BEE184446
MSB_1(L)=0,k1=L<<1=7051501d5233e9d2e1f43857dc30888c
MSB_1(k1)=0,k2=(k1<<1) =7051501d5233e9d2e1f43857dc30888c<<1=e0a2a03aa467d3a5c3e870afb8611118
//DES CMAC
 k1: 9cce53a2d96b4d28
 k2: 399ca745b2d69a4b
 key: e07099f1bfaffd7f
//calc subkey
L=DES_ECB(key,00.00)=4E6729D16CB5A694
MSB_1(L)=0,k1=L<<1=9cce53a2d96b4d28
MSB_1(k1)=1,k2=(k1<<1) xor R_b=399CA745B2D69A50 xor 000000000000001B=399CA745B2D69A4B

//msg
616263
//pad
6162638000000000
//pad(msg) XOR k2
58FEC4C5B2D69A4B
//res=Enc(key, (pad(msg) XOR k2))
EE3F707F2C11D297

399CA745B2D69A50
000000000000001B

//msg
248F9D
//msg||padding
248F9D8000000000
//res=Enc(key, (pad(msg) XOR k2))
Enc(1D133AC5B2D69A4B)
=B8F9226E6B34BAE4
//DES CMAC
 k1: 9cce53a2d96b4d28
 k2: 399ca745b2d69a4b
 key: e07099f1bfaffd7f
//calc subkey
L=DES_ECB(key,00.00)=4E6729D16CB5A694
MSB_1(L)=0,k1=L<<1=9cce53a2d96b4d28
MSB_1(k1)=1,k2=(k1<<1) xor R_b=399CA745B2D69A50 xor 000000000000001B=399CA745B2D69A4B

//iv
0000000000000000
//msg
0001020304050607
08090a0b0c0d0e0f
//pad
0001020304050607
08090a0b0c0d0e0f

//cbc加密第一个分组
58BD1B8CEBD10955

//第二分组与k1异或
08090a0b0c0d0e0f xor 9cce53a2d96b4d28
=94C759A9D5664327
//msg
0001020304050607
94C759A9D5664327

//整个msg做cbc
58BD1B8CEBD10955
87B39963013D547F
//SM4-CMAC
//key
E07099F1BFAFFD7F240CD790CA4FE134
k1: 1d016f274a3a18888c48f7e5401a17d2
k2: 3a02de4e947431111891efca80342fa4
//p
000102030405060708090a0b0c0d0e0f
101112131415161718191a1b1c1d1e1f
202122232425262728292a2b2c2d2e2f
303132333435363738393a3b3c3d3e3f
40

//cmac
09D7F12496D4E84C39C05F298FD39DC5

1)IV=0..0
Enc(IV XOR 000102030405060708090a0b0c0d0e0f)=20B93A0C8A401D018464875F408136DE

2)IV=20B93A0C8A401D018464875F408136DE
Enc(IV XOR 101112131415161718191a1b1c1d1e1f)
=Enc(30A8281F9E550B169C7D9D445C9C28C1)
=F1295C1E1FFABBDADC0E909E1E2E2DC2

3)IV=F1295C1E1FFABBDADC0E909E1E2E2DC2
Enc(IV XOR 202122232425262728292a2b2c2d2e2f)
=Enc(D1087E3D3BDF9DFDF427BAB5320303ED)
=DA3AEEDBDBAD4F411C2EA7F7D352138B

3)IV=DA3AEEDBDBAD4F411C2EA7F7D352138B
Enc(IV XOR 303132333435363738393a3b3c3d3e3f)
=Enc(EA0BDCE8EF98797624179DCCEF6F2DB4)
=E557ECCBA0181C95D0EDF6D566064081

//最后block非block整数倍,k2 xor 后加密
3) IV=K2=3a02de4e947431111891efca80342fa4
(K2 XOR 40800000000000000000000000000000)
=(7A82DE4E947431111891EFCA80342FA4)

Enc(E557ECCBA0181C95D0EDF6D566064081 XOR 7A82DE4E947431111891EFCA80342FA4)
=Enc(9FD53285346C2D84C87C191FE6326F25)
=09D7F12496D4E84C39C05F298FD39DC5
//SM4-CMAC
//key
e07099f1bfaffd7fe07099f1bfaffd7f
//plain
e07099f1bfaffd7fe07099f1bfaffd7f
e07099f1bfaffd7fe07099f1bfaffd7f
e07099f1bfaffd7fe07099f1bfaffd7f

//mac
458AB613526E09AB50C7A7B8EC6CD753

//流程
 k1: 22b4c22b9e8e8b9c23c9da876039a8dc
 k2: 456984573d1d17384793b50ec07351b8

1)IV=0..0
Enc(IV XOR block1)=1B2273875F25284EA8823A473CD88E5C

2)IV=1B2273875F25284EA8823A473CD88E5C
Enc(IV XOR block2)
=Enc(1B2273875F25284EA8823A473CD88E5C XOR e07099f1bfaffd7fe07099f1bfaffd7f)
=Enc(fb52ea76e08ad53148f2a3b683777323)
=F76BFA239CC75759CB83F8D78E7B30DF

3) IV=K1=22b4c22b9e8e8b9c23c9da876039a8dc
(K1 XOR block3)
=(22b4c22b9e8e8b9c23c9da876039a8dc XOR e07099f1bfaffd7fe07099f1bfaffd7f)
=(c2c45bda212176e3c3b94376df9655a3)

Enc(c2c45bda212176e3c3b94376df9655a3 XOR F76BFA239CC75759CB83F8D78E7B30DF)
=Enc(35afa1f9bde621ba083abba151ed657c)
=458AB613526E09AB50C7A7B8EC6CD753
//TDES128
//key
E07099F1BFAFFD7F240CD790CA4FE134
k1: f72b4ee8a67f18ae
k2: ee569dd14cfe3147
//p
0001020304050607
08090a0b0c0d0e0f

//std_mac
E4C3593AD6F86AA9

1)IV=0..0
Enc(IV XOR block1)=C0D92C81A7AE8D03

2)IV=C0D92C81A7AE8D03
(k1 xor block2)
=f72b4ee8a67f18ae xor 08090a0b0c0d0e0f
=FF2244E3AA7216A1

CBC(IV, (FF2244E3AA7216A1))
=E4C3593AD6F86AA9

GMAC的计算

https://zhuanlan.zhihu.com/p/376692295

HMAC

《FIPS198-1》

HMAC是一种基于hash函数和密钥进行消息认证的方法
流程图:https://zhuanlan.zhihu.com/p/336054453

//输入
    K:秘密密钥
    message:消息
    w:压缩分组大小(哈希函数分组大小)
    Tlen:要求的MAC标记长度
//输出
    Tag:MAC标记

1. if length(K) > w then
    K = hash(K)
   else
    对K进行0填充至w字长
2. hmac = hash((opad XOR K) || hash(ipad XOR K || message))
3. 通过只保留前Tlen字节作为Tag

注:
1)ipad为00110110(0x36)的循环,直到达到单项散列函数的分组长度。
2)opad为01011100(0x5C)的循环,直到达到单项散列函数的分组长度。
//手动计算与在线计算验证HSHA256(分组512bit,输出256bit)
M:ABC(hex格式:616263)
K:313233343536(hex格式)
alg:SHA256
//在线计算
res:f6ced6f4883ffc0981a6b9945819f680102b43097ad8ef7a0df9bde06fb3d2e4

//手动计算,运算过程均为hex,验证成功
//K = K(pad)
31323334353600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
//ipadKey = ipad XOR K:
36363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636^
31323334353600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
=07040502030036363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636
//opadKey = opad XOR K
5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C^
31323334353600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
=6D6E6F68696A5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C
//SHA256(ipadKey || meassage)
=SHA256(07040502030036363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636||616263)
=29B4C2EE6B7392016EEB0E5729790C1F299B551F54902E78AD049BC33D8EF29C
//Tag=SHA256(opadKey||SHA256(ipadKey || meassage))
SHA256(6D6E6F68696A5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C||29B4C2EE6B7392016EEB0E5729790C1F299B551F54902E78AD049BC33D8EF29C)
=F6CED6F4883FFC0981A6B9945819F680102B43097AD8EF7A0DF9BDE06FB3D2E4

CRC校验码

https://blog.csdn.net/GMingZhou/article/details/111386451

计算示例:
1.选择CRC生成多项式G(X)=X4+X3+1
2.原数据二进制:10110011
3.CRC位数4(总位数(最高幂次+1,即4+1=5)-1)

计算过程:
1.把多项式转成二进制:11001
2.原数据后补CRC位数个零:10110011 || 0000
3.将2对1进行”模2除法“得到余数(不足4位前面补0)
4.执行步骤1-3,得到余数0100,即CRC校验码

“模2除法”:我们知道模2加减是不进位、不借位的加减法,比如1+1=0,1+0=1,101+011=110,等于每一位的异或运算,相同bit加减为0,不同bit加减则为1。模2除法同理,即当部分余数首位是1时商取1,反之商取0。然后每一位的减法运算是按位减,不产生借位。余数的位数一定要是比除数位数只能少一位,哪怕前面位是0,甚至是全为0(附带好整除时)也都不能省略。

AES_XCBC_MAC_96

标准:《RFC3566》

//Input
Key //128bit
Plaintext //could be NULL
//preprocess:calc subkey
t1 = 0x010101010101010101010101010101
t2 = 02020202020202020202020202020202
t3 = 03030303030303030303030303030303
K1 = AES128_ECB_ENC(Key, t1)
K2 = AES128_ECB_ENC(Key, t2)
k3 = AES128_ECB_ENC(Key, t3)
//Output
Mac //occupy 12 Bytes

//Step
1)初始iv全0
2)中间block进行CBC运算
1)当原文长度不是分组长度的整数倍时(10填充),最后一组明文与k3异或后进行CBC加密
2)当原文长度为分组长度的整数倍时,最后一组明文(padding)与k2异或后进行CBC加密
//《NIST-SP800-38B》

非对称算法

公钥密码仅限于密钥管理和签名等应用,因为性能与对称算法差距三个数量级。

SM9

1、概述:2016年3月公布,基于双线性对的标识密码算法,非对称密码
2、公钥:由用户的标识(邮箱地址、手机号码、QQ号码等)和一组公开的数学参数计算出公钥,省略交换数字证书和公钥的过程
3、私钥:由用户标识、一组公开的数学参数、一个域范围内的秘密值(系统私钥参数)计算出
4、目的:为了降低公开密钥系统中密钥和证书管理的复杂性,让通信双方在不需要交换公私钥信息、不需要保存密钥的目录服务等基础设施、不需要使用第三方提供认证服务的情况下,保证信息交换的安全性并可以验证相互之间的签名。实现用户身份与密钥对直接绑定的密码技术。

《密码标识算法》
相关链接:
http://www.gmbz.org.cn/main/postDetail.html?id=20250123174859

https://std.samr.gov.cn/search/std?q=SM9 国家标准>行业标准

以下参考《GMT 0044-2016》,从第五部分参数定义:
签名私钥生成函数识别符 hid:0x01(数字签名)
加密私钥生成函数识别符 hid:0x02(密钥交换)
加密私钥生成函数识别符 hid:0x03(密钥封装)
注:hid用一个字节表示标识符,由KGC选择并公开

SM9椭圆曲线参数(参考SM9表示密码算法第五部分:参数定义):

image-20250109102913952

双线性对eid标识符参考《SM9标识密码算法 第1部分:总则》,0x04代表R-ate对

//SM9椭圆曲线参数(y^2 = x^3 +ax +b)
//p
B640000002A3A6F1D603AB4FF58EC74521F2934B1A7AEEDBE56F9B27E351457D
//a
0000000000000000000000000000000000000000000000000000000000000000
//b
0000000000000000000000000000000000000000000000000000000000000005
//Gx
93DE051D62BF718FF5ED0704487D01D6E1E4086909DC3280E8C4E4817C66DDDD
//Gy
21FE8DDA4F21E607631065125C395BBC1C1C00CBFA6024350C464CD70A3EA616
//n
B640000002A3A6F1D603AB4FF58EC74449F2934B18EA8BEEE56EE19CD69ECF25
//h
1

SM9_辅助函数

辅助函数H1()

image-20250109111032492

辅助函数H2()

image-20250109111054723

image-20250109111112958

由上图可知H<sub>2</sub>和H<sub>1</sub>的区别在于计算H<sub>a</sub>的第一个字节值,对于SM9的椭圆曲线参数,log2 N=256,hlen=8 * 5 * 256 / 32 = 320bit,占用40字节。因此计数器ct只需要[1,2],第2次计算hash值只截取前8字节。

这里似乎可以使用预计算加速h的计算,待研究。

SM9_数字签名验签算法

系统签名主密钥产生

1.get rand ks∈[1,N-1] //主私钥
2.Ppub-s = [ks]P2 // P2为G2生成元
3.return (ks, Ppub-s) //ks-32byte, 主公钥128byte--二次扩域上的点,有x、y坐标构成,每个坐标有2个分量

keypair(32B, 128B)

用户签名密钥产生

1.t1 = H1(IDA||hid, N) + ks //hid=1
2.if t1 == 0 重新生成签名主私钥ks,goto step1, 计算t1,公开主公钥,更新已有用户的签名私钥
3.t2 = ks * t1(-1) mod N
4.dsA = [t2]P1 //

dsA (64B)

数字签名生成

image-20230719152255920

1.产生群GT中元素 g = e(P1,Ppub-s)
2.产生随机数r∈[1,N-1]
3.计算群GT中元素 w = gr, 将w的数据类型转换为bit串(32*12byte)
4.计算整数h = h2(M||w, N)
1) t1 = SM3(0x02||M||w||00000002), 取前8byte, 因为hlen = 8 * 5 * ⌈log2N / 32⌉ = 40 * 8 =320bit = 40byte
2) t2 = SM3(0x02||M||w||00000001)
3) h = (t2||t1) mod (N-1) + 1
5.计算整数l = (r-h) mod N,if l==0,goto 2.
6.计算群GT中元素S = [l]dsA //占用65byte,第一个字节是标识04,代表非压缩点
7.return (h,S)

数字签名验证

image-20230719154012328

1.if h’ ∈ [1, N-1]是否成立,若不成立则验证不通过
2.检验S' ∈G1 是否成立,若不成立则验证不通过
3.计算群GT中的元素g=e(P1, Ppub-s)
4.计算群GT中的元素t=gh'
5.计算整数h1 = H1(IDA||hid, N)
6.计算群G2中的元素 P = [h1]P2 + Ppub-s
7.计算群GT中的元素u = e(S', P)
8.计算群GT中的元素w' = u - t,将w'数据类型转换为比特串
9.计算整数h2 = H2(M'||w', N),检验h2 = h'是否成立,若成立则验证通过,否则验证不通过

签名验签的预计算值计算

//g=(P1, Ppub-s),其中P1为系统参数群1生成元,xy各占32byte,Ppub-s为签名主公钥,是二次扩域上点,xy各有两个分量,各占32bytes
//计算数值Demo
//Ppub-s(128bytes)
9f64080b3084f733e48aff4b41b565011ce0711c5e392cfb0ab1b6791b94c408//x_re
29dba116152d1f786ce843ed24a3b573414d2177386a92dd8f14d65696ea5e32//x_rm
69850938abea0112b57329f447e3a0cbad3e2fdb1a77f335e89e1408d0ef1c25//y_re
41e00a53dda532da1a7ce027b7a46f741006e85f5cdff0730e75c05fb4e3216d//y_rm
//P1(64bytes)
93DE051D62BF718FF5ED0704487D01D6E1E4086909DC3280E8C4E4817C66DDDD
21FE8DDA4F21E607631065125C395BBC1C1C00CBFA6024350C464CD70A3EA616
//t=e(P1,Pupub-s)[32*2*3*2=32*12bytes]
4E378FB5561CD0668F906B731AC58FEE25738EDF09CADC7A29C0ABC0177AEA6D28B3404A61908F5D6198815C99AF1990C8AF38655930058C28C21BB539CE000067E0E0C2EED7A6993DCE28FE9AA2EF56834307860839677F96685F2B44D0911F5A1AE172102EFD95DF7338DBC577C66D8D6C15E0A0158C7507228EFB078F42A6B3129A75D31D17194675A1BC56947920898FBF390A5BF5D931CE6CBB3340F66D4C744E69C4A2E1C8ED72F796D151A17CE2325B943260FC460B9F73CB57C9014B38BFFE40A22D529A0C66124B2C308DAC9229912656F62B4FACFCED408E02380FA01F2C8BEE81769609462C69C96AA923FD863E209D3CE26DD889B55E2E3873DB1604A3FCFA9783E667CE9FCB1062C2A5C6685C316DDA62DE0548BAA6BA30038B93634F44FA13AF76169F3CC8FBEA880ADAFF8475D5FD28A75DEB83C44362B43984B87422330D7936EABA1109FA5A7A7181EE16F2438B0AEB2F38FD5F7554E57AAAB9F06A4EEBA4323A7833DB202E4E35639D93FA3305AF73F0F071D7D284FCFB

3)SM9_密钥交换协议算法

系统加密主密钥产生

1.get rand ke∈[1,N-1] //加密主私钥
2.Ppub-e = [ke]P1 //P1为N阶循环子群G1生成元
3.return (ke, Ppub-e) //ke-32byte, 加密主公钥64byte--普通点乘

keypair(32B, 64B)

用户加密私钥密钥产生

1.t1 = H1(IDA||hid, N) + ke //hid--1字节
2.if t1 == 0 重新生成加密主私钥,goto step1, 计算t1,公开主公钥,更新已有用户的加密私钥
3.t2 = ke * t1(-1) mod N
4.deA = [t2]P2 //128byte

deA (128Byte)

密钥交换协议

image-20230720145818501

设用户A、用户B协商klen比特密钥,A为发起方,B为响应方
用户A
1.计算群G1中的元素QB = [H1(IDB || hid, N)]P1 + Ppub-e //这里是生成临时密钥对(rA, RA),注意是使用对方ID计算
2.产生随机数rA ∈ [1, N-1]
3.计算群G1中的元素 RA = [rA]QB
4.将RA发送给用户B

用户B
1.计算群G1中的元素QA = [H1(IDA || hid, N)]P1 + Ppub-e
2.产生随机数rB ∈ [1, N-1]
3.计算群G1中的元素 RB = [rB]QA
4.if RA not ∈ G1 return fail
else
计算群GT中元素g1 = e(RA, deB), g2 = e(Ppub-e,P2)rB , g3 = g1rB , 将g1 g2 g3转化为bit串
5.计算SKB = KDF(IDA || IDB || RA || RB || g1 || g2 || g3, klen)
6.(optional)计算 SB = Hash(0x82 || g1 || Hash(g2 || g3 || IDA || IDB || RA || RB))
7.send RB (optional)SB to 用户A

用户A
5.if RB not ∈ G1 return fail
else
计算群GT中元素g1‘ = e(Ppub-e, ,P2)rA, g2' = e(RB, deA) , g3 = (g2')rA , 将g1 g2 g3转化为bit串
6.(optional)S1 = Hash(0x82 || g1' || Hash(g2' || g3' || IDA || IDB || RA || RB)), if S1 != SB return fail
7.计算SKA = KDF(IDA || IDB || RA || RB || g1' || g2' || g3', klen)
8.(optional)计算 SA = Hash(0x83 || g1’ || Hash(g2‘ || g3’ || IDA || IDB || RA || RB)),send to 用户B

用户B
8.(optional)计算S2 = Hash(0x83 || g1 || Hash(g2 || g3 || IDA || IDB || RA || RB)),if S2 != SA return fail

由上图可知在标准中S1/SA和SB/S2是可选项,在实际运算中,S1/SA和SB/S2的作用是为了辅助密钥对比验证,如果协商的密钥长度很低,比如1字节,假设双方因毛刺发生的1比特翻转,对方翻转概率为1/64,共同协商出错误的密钥。

SM9_密钥封装解封装

封装算法

为了封装klen长度的密钥给用户B,作为封装者的用户A需要执行以下运算步骤:
1.计算群G1中的元素QB=[H1(IDB||hid,N)]P1+Ppub-e
2.产生随机数r∈[1,N-1]
3.计算群G1中的元素C=[r]QB,将C的数据类型转换为比特串
4.计算群GT中的元素g=e(Ppub-e,P2)
5.计算群GT中的元素w=gr,将w的数据类型转换为比特串
6.计算K=KDF(C||w||IDB,klen),若K全为0比特串,返回2
7.输出(K,C),其中K是被封装的密文,C是封装密文 //只有C给到用户B

image-20230912160558945

解封装算法

用户B收到封装密文C后,为了对比特长度为klen的密钥解封装,需要执行以下运算步骤

1.验证C∈G1是否成立,若不成立则报错并退出
2.计算群GT中的元素w'=e(C,deB),将w'的数据类型转换为比特串
3.将C的数据类型转换为比特串,计算封装的密文K'=KDF(C||w'||IDB,klen),若k'为全0比特串,则报错并退出
4.输出密钥K’

image-20230912161316213

SM9_加解密

注:密钥加解密和密钥封装的主公钥是同一套系统

加密

设需要发送的消息为比特串M,mlen为M的比特长度,k1_len为分组密码算法中密钥K1的比特长度,k2_len为函数MAC(k2,Z)中密钥K2的比特长度

为了加密明文M给用户B,作为加密者的用户A应实现以下运算步骤:
1.计算群G1中的元素QB=[H1(IDB||hid,N)]P1+Ppub-e
2.产生随机数r∈[1,N-1]
3.计算群G1中的元素C=[r]QB,将C的数据类型转换为比特串
4.计算群GT中的元素g=e(Ppub-e,P2)
5.计算群GT中的元素w=gr,将w的数据类型转换为比特串
6.按加密明文的方法进行计算:
a)如果加密明文的方法是基于密钥派生函数的序列密码算法,则
1)计算整数klen=mlen+k2,然后计算K=KDF(C1||w||IDB,klen),令k1为K最左边的mlen比特,k2为剩下的k2_len比特,若k1为全0比特串,则返回2
2)计算C2=M⊕k1
b)如果加密明文的方法是结合密钥派生函数的分组密码算法,则
1)计算整数klen=mlen+k2,然后计算K=KDF(C1||w||IDB,klen),令k1为K最左边的mlen比特,k2为剩下的k2_len比特,若k1为全0比特串,则返回2
2)计算C2=Enc(K2,M)
7.计算C3=MAC(k2, C2)
8.输出密文C=C1||C3||C2

image-20230912163006406

解密

设mlen为密文C=C1||C3||C2中C2的比特长度,K1_len为分组密码算法中密钥K1的比特长度,K2_len为函数MAC(K2,Z)中密钥K2的比特长度

为了对C进行解密,作为解密者的用户B应实现以下运算步骤:
1.从C中取出C1,将C1的数据类型转换为椭圆曲线上的点,验证C1∈G1是否成立,若不成立则报错并退出
2.计算群GT中的元素w'=e(C1,deB),将w'的数据类型转换为比特串
3.按加密明文的方法进行计算:
a)如果加密明文的方法是基于密钥派生函数的序列密码算法,则
1)计算整数klen=mlen+k2,然后计算K=KDF(C1||w||IDB,klen),令k1为K最左边的mlen比特,k2为剩下的k2_len比特,若k1为全0比特串,则返回2
2)计算M'=C2⊕k1
b)如果加密明文的方法是结合密钥派生函数的分组密码算法,则
1)计算整数klen=mlen+k2,然后计算K=KDF(C1||w||IDB,klen),令k1为K最左边的mlen比特,k2为剩下的k2_len比特,若k1为全0比特串,则返回2
2)计算C2=Dec(K2,M)
4.计算u=MAC(k2',C2),对比C中C3,若不相等,则报错并退出
5.输出明文M'

image-20230912164722534

SM9二次扩域

//二次扩域下的点乘
//k
2
//Gx
85AEF3D078640C98597B6027B441A01FF1DD2C190F5E93C454806C11D8806141 //Gx_1
3722755292130B08D2AAB97FD34EC120EE265948D19C17ABF9B7213BAF82D65B //Gx_2
//Gy
17509B092E845C1266BA0D262CBEE6ED0736A96FA347C8BD856DC76B84EBEB96 //Gy_1
A7CF28D519BE3DA65F3170153D278FF247EFBA98A71A08116215BBA5C999A7C7 //Gy_2

//[k]G-x
513F149AB53E94BB3A0367C61FF87670E025DB30C57F84594E4BA4D7B3C656CF
2A74F8561B91993205EB512576AD56221EA5963F3DA078240D55594FB051EA86
//[k]G-y
776DE41DB0511B8976D69C982DD4757D641487C68D13CBEE7069396C20CD3459
8E3D9EC4E63D5B9F83081FB97B715430C8BFC6F1A1321A89627B9A4E8961C7BD

//以上在OSR_driver里,Gx和Gy,整体U8小端配置到寄存器

//[n-1]G
//sm9-p
B640000002A3A6F1D603AB4FF58EC74521F2934B1A7AEEDBE56F9B27E351457D
//y_1 = sm9-p - Gx_1
9EEF64F6D41F4ADF6F499E29C8CFE0581ABBE9DB7733261E6001D3BC5E6559E7
//y_2 = sm9-p - Gx_2
7F1D8AAD70909BE90358F1D02240062433CC3A0248DED72FEBB879EC33CE6F22

SM9双线性对计算

//g = e(P1, P2),P1群的生成元P1,
//主公钥128byte--二次扩域上的点,有x、y坐标构成,每个坐标有2个分量

image-20240721180217830

安全实现方案

双线性对运算

将对运算e(C, deA)转化为等价实现e(C, deA1)*e(C, deA2)

//计算g = e(C, deA), C为素数基域上的点,deA为二次扩域上的点,g为12次扩域上的点 
1.r = rand[1, N-1]
2.Q = [r]P2 //Q为二次扩域上的点(Qx0||Qx1, Qy0||Qy1),128B  //循环群G2的点是二次扩域Fp2的点
3.R = (Qx, (p-Qy0)||(p-Qy1)) = (Qx, -Qy)
4.e(C,deA + R)*e(C, Q)
  =e(C,deA + R + Q) //R和Q互为负点
  =e(C, deA)

SM9加速(预计算)

可以发现,如果所有sm9计算都围绕在一个长期不变、稳定的系统主密钥对情况下,对于签名/验签,加解密/密钥封装/密钥交换,可以提前计算e(Ppube, P2)和e(P1, Ppubs)。

RSA

理论上加解密性能不确定,在实际在在电子签名场景中,签名性能远远低于验签,因为一般E=65537,因此模幂运算签名会很慢。

RSA-CRT(Chinese Remainder Theorem)
目的:为了加速私钥运算。一般接近4倍,如模幂可并行,则接近8倍
公钥:(e,n)
私钥:(d,n)
私钥CRT:(p、q、dp、dq、u),其中,dp=e^(-1) mod (p-1);dq=e^(-1) mod (q-1);u=q^(-1) mod p;e-1=d (u=qInv=q^(-1) mod p),此外,pinv=p^(-1) mod q
RSA计算cd mod n:
m1 = c(dp) mod p
m2 = c(dq) mod q
h = qInv * (m1 - m2) mod p
c = m2+h*q;

注:qinv、h、c的计算使用到Garner's formula

已知:p,q,e,非CRT私钥d可以直接计算:d=e^-1 mod ((p-1)(q-1))
已知:p,dp,公钥e可以直接计算:e=dp^-1 mod (p-1)
已知:q,dq,公钥e可以直接计算:e=dq^-1 mod (q-1)

中国剩余定理(Chinses Remainder theorem)
定义:设两两互素的模数n1,n2,...,nm ∈ N,以及任意整数a1,a2,...,am∈Z,并满足N=n1n2...*nm,以下方程组必有解x:

x ≡ a1 (mod n1)
x ≡ a2 (mod n2)
...
x ≡ am (mod nm)

x ≡ a1n’1d1 + a2n’2d2 + ... + amn’mdm (mod N)
where n’i = N/ni and di ≡ ni-1 (mod ni)

已知(cd mod p, cd mod q),可求cd mod N, N=p*q

//RSA2048
//e
10001
//d
58D83FB05C894A8A7CCD708B9E6DA8515E06009AC2D736A00DD2D40C552A037BECA9D30EBA86A6985D003AED2D1D233D540BBD19A13730317C4FCC18EAF171B4765E3E0809BBCAF2936EB1DD3168160174169935CB4EEBBC77B78FFA79A83467F34C2E6C93ECA3FFE528DA4E91D850BC0333D432E93682EBE7528918F920DFA0CA65B8DAC5E216A297AB4C82A35B040E95E4E95F3DC1FF71FBCE0338F8020C2DD03F0DC423FFDB4F6CD0C116E4D53248E91ED36189921D2D20F996B3D06CCF0634F1A0D68E8CC18B98E348B11EB8BEAF5F49F4DD4FE917EC2682298924B29FC5ABF1E2C751286399F2B3318980C7408AE2E73969248A9953D28F405F946D3F01
//n
F6A32EDA0C98C1D74E984DFFFD26AD5101BA6952B5D995E580AE8FD2A48C1BD310A1B30EFB497D57EAC0ABE92F6406E1485C7CFA84375CA81587294D06191F08AD0263D4B585E80A0BA3687D4D8674DE897FED6575C56EE75049A5CD44D26F63960936D59AC6920CC3CDADD75BF174349D042853408EBFBE8723D518BF7A537ABDD7F82952EAAF6226856F8C28783A2CBE85D6275C6EE089E22EC11F771F0488B04780F5B263454DB13C77188A641D5C2FA8A9248910129EEC71A2E03C05FF5BE3E83D244ABD8B89BB2274BA71D5850471F7285CF743DE7328CC74CCA7918195812153623A587B57A0AE25C5A4B440AE9181ABAA7C5B09901B47F461C09986F3

//p
FEA137FD595620504DE3CCEC7F99CB6542EB70000EF68AEE4A877321CC504B2073BF16E2FC867F95A0772126BE073BB93A08EB36E90973892B51DF96BD8739B179E3CCB1D479BA1A64CD266B5C7940410F978DD9403C122F9505FB2E101989650525F2E70607DCCD6F438476DE4ABEDFC84FAD8F253F8CCB6343620DA5578E91
//q
F7F6F437708C87628AD6DAC60134C16F52CAE67FB5E767B2068538EBEF64EE27203312DABCC2522CEAAF7F5405A90EC4687BF3B59E13E865ABE258DF687431CF800DBEDE09F6F4D33F451103F6CDDA51C36EBD656329066AA1C2A8578FF79E0D7BD0306898DC95253BF1BDB5FB94F461B41792E1272E3FA06A419CAA1C554743
//dp=eInv mod p-1
3B06744C2E6160AC3CE209A6E90D65B0DB8A68341B43DB80D1960482593263B7EA1CE9D944608942CE27CF6F83AD7992BE7942CD3FC809184B0A77A221DD2EEA8D2ED94C7FCC97EF765E78017B364046D4AE751B5F608D76AA3CF0B504D6669B1C4E67B856158C81B2C11BFBD21BD1935E83D9DFCCEAA6609F6A0758D2733181
//dq=eInv mod q-1
968C7B6E05896CD1699655318E9DE72B9DCACEF38F0FB627A249D46B7AE77EA886A63BB64E9074B00CB50CCB88BC41D901CEF002BEBD3B862F3BB7D5BC677FAE8A02CE75A4FB295DF0B61044A5DE09C546B0BF9105096D79E4487948E081E8962B2C15980BE7312E701CAFA003E54612E3F2C41CAB5B2B0A4C3915D33A6C945D
//u=qInv mod p
896EA479B6B4CD371FF8ED70EE18D2986489D31D310E1F0CC3C10139105538C811F5C355B69C2B3D74AF9F21FAD9C999D6F4B80872476D96557429C1A7C2EF31E5471BBD2AFF3E1480A5239CB1815F61719A3CAB4A1057FE4E934E7C72C4DB4129E124F5BE600F22318963BAFA7EF625C1A5E2B491310391F7774670346D8B73


//m=11223344
//m^d mod n
69717BD50F3E04B30A68FAD9439FCB023631511A3AAD9B472D30D480B79ABF89D5498882E0E0EF05F6CAB3ACDF3B0E41FC91A3C23C5BE7109EB58CF26D618BC9657454FF2CEEEC902C84399FECAD66E33A69571A5472964D52494E5F165A87C64E5B82D3DAC36E59008BF3BA6D52FAEB54FDE27B51A03CADC6D197825BF60502F21CD8C2FC35DF134ABE3D44F460F5565D00831AF21DC939C5926A05A8AC03CFB8702673F2B1819BDF44A4B4100FEA9D214D5E29267E9D5DC9775CE876B0DC608057DD1F70D16AF0E7ED1E12A2A7F9537423605CF9043637917B515EE2144CA9AA1D651EA55632F1D470235A237CFB97597E202F2504D7CCA2840B8969D36F87

//m1=m^dp mod p
E726FD1B53A390F6A482292A2C0D82A9672C8D8D175C67F9E910C1379EDACE7F81FFD9EE6CCC13DF6C46C9B85749301A4991FA059A966F89A66ADB460770EE2C9E8CB92AFA4EB4D56C6534BB0EF83DAF959656B7AB5F47734381BDB31B02892DDED0523049485E59C6BCB139448CAD3E3A39A1DFAB9D73FC2FE661661F56F199
//m2=m^dq mod q
7C98F4FFCEB47BE7A484543FD8AFA96FA872F0C7DF06FE24C445F3FDEA932F31E4DA4994F5112C65A4467BAF645A75ACABBFB9718EB9DEB3E785F25829235D3F64BCC98DED351D9FCA1D2C7540ED3CA5508FC1A7EB8E2469D498AF5091FFA66960D0795D0C368459904D6806975EBACD15B834E5CBB861CD57E91DD694D699A5

//(m1-m2) mod p
6A8E081B84EF150EFFFDD4EA535DD939BEB99CC5385569D524CACD39B4479F4D9D25905977BAE779C8004E08F2EEBA6D9DD240940BDC90D5BEE4E8EDDE4D90ED39CFEF9D0D199735A2480845CE0B010A4506950FBFD123096EE90E628902E2C47DFFD8D33D11DA00366F4932AD2DF27124816CF9DFE5122ED7FD438F8A8057F4
//h=qInv * (m1-m2) mod p
6CDC3647F6DC3050558B99A6F9129DC823102832BE44475E0832C09787A52119B855BBB66C4E64EC2DE9D949B5F62875C1436D4B889BF868F51E9EACD10EE4C0D3D2B7D1E1B26D561D66B257989D76E933818A1CB84C2392C12FB697A053F7191D16D56EF2356DB65575EF90247C3A42510F7D24F0C6BC1FD296127224C1BF76

//h*q
69717BD50F3E04B30A68FAD9439FCB023631511A3AAD9B472D30D480B79ABF89D5498882E0E0EF05F6CAB3ACDF3B0E41FC91A3C23C5BE7109EB58CF26D618BC9657454FF2CEEEC902C84399FECAD66E33A69571A5472964D52494E5F165A87C64E5B82D3DAC36E59008BF3BA6D52FAEB54FDE27B51A03CADC6D197825BF605027583E3C32D81632BA639E9051BB14BE6B48D92531316CB15014C7607BE18D49DD395DCDEFDA055363AFE2904ABB574F0758DA4B797C4BEA9E1F16A904D8D7F211B9B1391839C4D511DCFF19D61BABCAE23939EB50D7611CDBCE2A20E5014A640494CEBC1991FAE984422BB538C1E40CA43C5EB49594C75FF4A9AEDB2D4FCD5E2
//C=m2+h*q
69717BD50F3E04B30A68FAD9439FCB023631511A3AAD9B472D30D480B79ABF89D5498882E0E0EF05F6CAB3ACDF3B0E41FC91A3C23C5BE7109EB58CF26D618BC9657454FF2CEEEC902C84399FECAD66E33A69571A5472964D52494E5F165A87C64E5B82D3DAC36E59008BF3BA6D52FAEB54FDE27B51A03CADC6D197825BF60502F21CD8C2FC35DF134ABE3D44F460F5565D00831AF21DC939C5926A05A8AC03CFB8702673F2B1819BDF44A4B4100FEA9D214D5E29267E9D5DC9775CE876B0DC608057DD1F70D16AF0E7ED1E12A2A7F9537423605CF9043637917B515EE2144CA9AA1D651EA55632F1D470235A237CFB97597E202F2504D7CCA2840B8969D36F87

欧拉定理
对于正整数n,a是任意整数,gcd(a,n)=1,那么a(Φ(N)) ≡ 1 (mod n)

rsa生成流程:

1.挑选两个不同的大素数p、q,令N=p*q
2.利用Euler函数计算Φ(N)=(p-1)(q-1)
3.挑选一个整数e,满足:1<e<Φ(N),gcd(e,Φ(N))=1
4.计算d,满足:d*e≡1 (mod Φ(N))
5.(e,N)作为公钥,(d,N)作为私钥

RSAES-OAEP

参考文档《RFC8017》中《PKCS#1 RSA Cryptography Specifications Version 2.2》

结合RSAEP、RSADP、EME-OAEP编码
消息的最大长度:k-2hlen-2 Byte,其中hlen是hash摘要Byte长度,k是接收者的RSA模数Byte
Lable L:标签输入,在这个版本的PKCS#1,L是空字符串NULL
OAEP参数化hash函数和MGF,建议的hash和MFG见附录B
RSAEP:public-key(RSA) encryption primetive
攻击方式:CCA1、CCA2(OAEP能有效抵御)

RSAES-OAEP-ENCRYPT

【Options】
Hash:哈希算法,hlen取决于hash算法输出长度
MGF:掩码生成函数
【Input】
(n, e):接收者的公钥
M:待加密的原始消息,其长度要求:mLen<=k - 2hlen -2
L:可选标签,关联原始消息,默认值取决于L,如果L不提供,默认为NULL
【Output】
C:密文

算法流程:
1.检查参数有效性
1.1 检查M输入长度结合hash函数,判断是否合法,if mLen > k-2hLen-2 then return "message too long"
1.2 检查关联数据标签L,是否超过hash算法要求的总长度,不满足返回“label too long” (比如sha-1字节长度不超过264 - 1)
2.EME-OAEP编码
2.1 计算lhash=hash(L),L可为NULL
2.2 生成值为全0的字节串PS,长度为k-mLen-2hLen-2,可为0(当mLen= k-2hLen-2)
2.3 将lhash,PS,0x01,M构造数据块
DB = lhash || PS || 0x01 || M (if mLen=k-2hLen-2, blocks size = k - (hLen+1))
2.4 get rand seed,randLen = hLen
2.5 dbMask = MGF(seed, k-hLen-1)
2.6 maskedDB = DB xor dbMask
2.7 seedMask = MGF(maskedDB, hLen)
2.8 maskedSeed = seed xor seedMask
2.9 EM = 0x00 || maskedSeed || maskedDB
3.RSA加密
3.1 m=OS2IP(EM),Octet String to Integer primitive
3.2 c=RSAEP((n,e), m),计算me mod n
3.3 I2OSP(c, k),Integer to Octet String primitive

EME-OAEP Encoding

下图为编码操作

image-20240729173121643

RSAES-OAEP-DECRYPT

【Options】
Hash:哈希算法,hlen取决于hash算法输出长度
MGF:掩码生成函数
【Input】
C:Cipher
K:接受者的私钥,字节长度k>=2hLen+2
L:可选标签,关联原始消息,默认值取决于L,如果L不支持,默认为NULL,解密用于校验
【Output】
M:Plaintext明文

如果校验失败,输出“decryption error”

算法流程:
1.有效性检查
1.1. 检查关联数据标签L,是否超过hash算法要求的总长度,不满足返回“label too long” (比如sha-1字节长度不超过264 - 1)
1.2. 密文字节长度不等于k,输出"decryption error",这里应该是指模数要跟n对齐,并非指最高有效位
1.3 if k < 2hLen+2 return "decryption error"
1.4 if C > n, return "decryption error"
2.RSA解密
2.1 C = OS2IP(C)
2.2 m = RSA(k, C
2.3 EM = I2OSP()
3.EME-OAEP解码
3.1 计算lhash=hash(L),L可为NULL
3.2 对EM拆分:EM = Y || maskedSeed || maskedDB,其中长度分别占比1 || hLen || k-hLen-1
3.2.1 if Y != 0 then return "decryption error"
3.3 seedMask = MFG(maskedDB, hLen)
3.4 seed = maskedSeed xor seedMask
3.5 dbMask = MGF(seed, k-hLen-1)
3.6 DB = maskedDB xor dbMask
3.7 对DB拆分,DB = lhash’ || PS || 0x01 || M
3.7.1 if lhash‘ != lhash then return error
3.7.2 if 单字节 != 0x01 then return error
4.return M

RSAES-PKCS1-v1.5

参考文档《PKCS#1 v1.5 rfc2313》

RSAES-PKCS1-v1.5-ENCRYPT

结合了RSAEP、RSADP、EME-PKCS1-v1.5编码
原始待加密消息:可操作最多k-11字节,k是模数的字节数
填充字节串PS最少8个字节

【Input】
(n, e):接收者的公钥
M:待加密的原始消息,其长度要求:mLen<=k - 11(11字节长度包括0x00、0x02、0x00、M占用的总长度mLen,PS)
【Output】
C:密文,占用k字节

算法流程:
1.有效性检查
1.1 if mLen > k-11 return "error message too long"
2.EME-PKCS1-v1.5编码
2.1 生成随机数PS(每个元素非0),其占用长度为k-mLen-3(即减去0x00、0x02、0x00、M占用的总长度),最少占用8个字节
2.2 拼接PS、M、填充字节0x00,0x02,形成长k字节的数据块
EM = 0x00 || 0x02 || PS || 0x00 || M
3.RSA加密
3.1 m = OS2IP(EM)
3.2 C = me mod N
3.3 C = I2OSP(c, k)
4.return C

RSAES-PKCS1-v1.5-DECRYPT

【Input】
(n, d):接收者的私钥
C:待解密的密文
【Output】
M:明文,其占用长度最多k-11字节(11字节长度包括0x00、0x02、0x00、M占用的总长度mLen,PS)

算法流程:
1.有效性检查:if Bytelen(C) != k and k<11 then return "decryption error"
2.RSA解密
2.1 C=OS2IP
2.2 m=Cd mod N
2.3 EM = I2OSP(m,k)
3.EME-PKCS1-v1.5解码
EM = 0x00 || 0x02 || PS || 0x00 || M
3.1 if EM[0] != 0x00 return "decryption error"
3.2 if EM[1] != 0x02 return "decryption error"
3.3 if Bytelen(PS)<8 return "decryption error"
3.4 根据PS字串每个字节为非0值特性,拆分出M,即遍历出第2个0x00值后面即为M
4.return M

RSASSA-PSS

参考文档《RFC8017》中《PKCS#1 RSA Cryptography Specifications Version 2.2》

结合了RSASP1、RSAVP1、EMSA-PSS编码

RSASSA-PSS-SIGN

【Input】
(n, d):签名者的私钥
M:待签名的原始消息,字节串
【Output】
S:签名值,占用k字节(模数bit长度)

算法流程:
1.EMSA-PSS编码
1.1 对消息M处理为一个编码消息EM(长度为ceil(modBit - 1)/8字节,理解为小于等于模数的字节长度),ceil返回大于或等于表达式的整数,EM最多占用modBits-1比特(理解为最高有效位)
EM = EMSA-PSS-ENCODE (M, modBits - 1)
1.2 如果编码操作返回"message too long", return “message too long”
1.3 如果编码操作返回"encoding error", return "encoding error"
2.RSA签名
2.1 m = OS2IP(EM)
2.2 s = RSASP1(K, m)
2.3 S = I2OSP(s, k)
3.return S

EMSA-PSS Encoding

下图为编码操作

image-20240912140440683

EMSA-PSS编码流程

EMSA-PSS-ENCODE(M, emBits)
Options:
Hash:hash函数,hLen为摘要字节长度
MGF:掩码生成函数
sLen:salt,盐值字节长度
Input:
M:待编码的消息,字节串
emBit:整数OS2IP(EM)最大的比特长度,最少8hLen+8sLen+9
Output:
EM:编码后的消息,字节串,占用emLen = ceil(emBits/8)
Error:"Encoding error" "message too long"

步骤:
1.if message byte len > hash function require(2^61 -1byte for sha-1)
2.Let mHash = hash(M),字节串,占用hLen字节
3.if emLen < hLen + sLen + 2 return "encoding error", sLen should be in [0, emLen -hLen -2]
4.generate salt, of sLen, if sLen =0, salt is empty
5.Let M' = (0x)00 00 00 00 00|| 00 00 00 00 || mHash || salt //M字节串,占用8+hLen+sLen,前面8字节初始化为全0,
这里(0x)00 00 00 00 00|| 00 00 00 00对应上图Padding1
6.Let H = hash(M'),占用hLen长度
7.生成PS,值为全0,长度占用为emLen - sLen - hLen - 2,PS的长度可以为0
8.Let DB = PS || 0x01 || salt,DB长度占用emLen - hLen - 1,这里PS || 0x01对应上图Padding2
9.Let dbMask = MFG(H, emLen - hLen -1)
10.Let maskedDB = DB xor dbMask
11.将maskedDB的高位比特(占用8 * emLen-emBits)设置为0
12.EM = maskedDB ||H||0xbc
13.return EM

RSASSA-PSS-VERIFY

【Input】
(n, e):签名者的公钥
M:待验签的签名值,占用k字节(模数bit长度)
【Output】
S:签名值,占用k字节(模数bit长度)

算法流程:
1.长度检查:
1.1 if ByteLen(S) != k, return "invalid signature"
2.RSA校验
2.1 s = OS2IP(S)
2.2 m = RSAVP1 = sd mod N
2.3 EM = I2OSP(m ,emLen),注意如果modBits-1能被8整除,emLen=k-1,否则emLen=k
3.EMSA-PSS校验
3.1 Result = EMSA-PSS-VERIFY(M, EM, modBits - 1)
4.如果result是一致的,return "valid signature"

Data Conversion Primitives

RSASSA-PKCS1-v1.5

参考文档《PKCS#1 v1.5 rfc2313》

结合RSASP1、RSAVP1、EMSA-PKCS1-v1.5编码
验签单元IFSP-RSA1、IFVP-RSA1
RSASSA-PKCS1-v1.5可操作不限制的大数,取决于底层EMSA-PKCS1-v1.5方法的hash函数
如今更推荐使用RSASSA-PSS

RSASSA-PKCS1-v1.5-SIGN

【Input】
(n, d):签名者的私钥
M:待签名的原始消息,字节串
【Output】
S:签名值,占用k字节(模数bit长度)

流程:
1.EMSA-PKCS1-v1.5编码:操作消息M处理程长度为k字节的EM
EM = EMSA-PKCS1-v1.5-ENCODE(M, k)
1.1 如果编码操作输出"message too long", return "message too long"
1.2 如果编码操作输出"intended encoded message length too short", return "RSA modulus too short"
2.return EMd mod n

EMSA-PKCS1-v1_5 Encoding

EMSA-PKCS1-v1_5编码流程

EMSA-PKCS1-v1_5-ENCODE(M, emLen)
Options:
Hash:hash函数,hLen为摘要字节长度
Input:
M:待编码的消息,字节串
emLen:编码的字节长度,最少为tLen+11,tLen为DER编码的确定字节串(可认为签名可对空消息进行)
Output:
EM:编码后的消息,字节串,占用emLen = ceil(emBits/8)
Error:"Encoding error" "intended encoded message length too short"

步骤:
1.11如果原始消息长度大于Hash所要求长度,return"message too long",否则,H=Hash(M)
2.根据hash类型,构造T字节串,T=hashAlgID||hash(M),长度为tLen
3.if em<tLen + 11, return "intended message length too short"
4.创建PS,字节长度为emLen-tLen-3,PS的每个字节值为0xFF,PS占用最少8个字节
5.EM = 0x00 || 0x01 || PS || 0x00 || T,其中T = hashAlgID || hash(M)
6.return EM

image-20240925140217569

RSASSA-PKCS1-v1.5-VERIFY

【Input】
(n, e):签名者的公钥
M:待验签的原始消息,字节串
S:待验签的签名值,占用字节为模数n字节长度
【Output】
“valid signature” “invalid signature” “message too long” “modulus too short”

流程:
1.如果签名值的长度不为k字节,return "invalid signature"
2.检查Signature是否满足1 < Signature < n
3.EM1 = Signaturee mod n
3.对输入的原始消息M进行EM编码
4.对比公钥运算出来的EM1和对原始消息进行编码的EM2

签名流程

image-20240603140922711

验签流程

image-20240603141038607

image-20240603141056309

image-20240603141114918

MGF1

Mask Generation Functions

依赖于hash函数,未来的版本会在IEEE 1363 和 ANSI X9.44定义其他的掩码生成函数

MFG1(mgfSeed, maskLen)
Options:
Hash:hash函数,hLen标记对应的hash输出字节长度,比如SM3为32字节摘要
Input:
mgfSeed:种子,字节串
maskLen:掩码字节长度,最多2^32 * hLen
Output:
mask:掩码,长度为maskLen字节
Error:"mask too long"

流程:
1.if maskLen > 2^32 * hLen, return "mask too long"
2.let T 为空字符串
3.for counter=0 to ceil((maskLen/hLen))-1:
C = I2OSP(counter, 4) //转换counter为4字节的字节串
T = T || hash(mgfSeed || C)
4.mask,对T进行截断,取maskLen长度

f)RSA带公钥的安全模幂

安全模幂运算如下:

z=x(d1(1+ek)+d2(1+ek)-k) mop p

d1(1+e * k)+d2(1+e * k) - k
=(d1+d2) + (d1 * e * k) + (d2 * e * k) - k
=d + e * k * (d1 + d2) - k
=d + e * k * d - k
=d + (e * d)k - k

Md + (e * d)k - k
=Md[M(ed)]kM-k
=Md

x:模幂运算的底数
z:模幂运算的结果
d1:私钥d的一分量(d=d1+d2)
d2:私钥d的另一分量
e:公钥
k:随机数
p:模数

1024bit示例:
//x
11223344
//e
10001
//d
23075234F1770E2356480F0D91ED1F51D823B8C1F23547D40F1871FD9A7193EF0EA1D22508078E76753971597BDB016D2E71CC80F565B5D530B412ACE635AE3EF2ADD0B624071F4CEA9F609E0F65B0289DD173C169729CFE36E7EF4B2033358DE63CF6E4CF9AE5078D43C428BFC38F38F4672F71BC1616EE4711A03BE3E767C1
//d1
23075234F1770E2356480F0D91ED1F51D823B8C1F23547D40F1871FD9A7193EF0EA1D22508078E76753971597BDB016D2E71CC80F565B5D530B412ACE635AE3EF2ADD0B624071F4CEA9F609E0F65B0289DD173C169729CFE36E7EF4B2033358DE63CF6E4CF9AE5078D43C428BFC38F38F4672F71BC1616EE4711A03BE3E76700
//d2
C1

//p即n
B0EAA37021B25702ED99D4DD9CC734B1DD1253722B6350AD6F0B11CBD886C1323D922A6EFF4D2627D88A637D93D13A8EFDC53008E03FFC1C89CF747F62DA3571BEA9A16DE4C65612CE49BDF9A7E5A588BA8F9B4517BEA84DE5A45CF27B53008BB66E08BE903FC9DF71E8D76F4E174A50A8B6648E74EFC45FF4612EB35E960631

//k
FF

----------calc----------
//1+e*k
1+FF00FF
=FF0100

//d1*(1+e*k)
22E46DEA0EBA888C41151D469368C41FA59D6D2CE90504C182DD68A40ED4BCCCB3823EF4B5248EEF8D3AAD2193B8A246C2B0892640F1458511588F4E4BFC5EC661FA15933E993C34BD01ABDCD1F459DE255C401F1BCA93D3D5AF3E43C45E228B8DE4A02AE1B019BD6ABE0DA85B2C8B6D4AABBCA979CBBCED6FB8D5AD483F6380670000

//d2*(1+e*k)
C03FC100

//d1*(1+e*k)+d2*(1+e*k)
22E46DEA0EBA888C41151D469368C41FA59D6D2CE90504C182DD68A40ED4BCCCB3823EF4B5248EEF8D3AAD2193B8A246C2B0892640F1458511588F4E4BFC5EC661FA15933E993C34BD01ABDCD1F459DE255C401F1BCA93D3D5AF3E43C45E228B8DE4A02AE1B019BD6ABE0DA85B2C8B6D4AABBCA979CBBCED6FB8D5AD483F6440A6C100

//t=d1*(1+e*k)+d2*(1+e*k)-k
22E46DEA0EBA888C41151D469368C41FA59D6D2CE90504C182DD68A40ED4BCCCB3823EF4B5248EEF8D3AAD2193B8A246C2B0892640F1458511588F4E4BFC5EC661FA15933E993C34BD01ABDCD1F459DE255C401F1BCA93D3D5AF3E43C45E228B8DE4A02AE1B019BD6ABE0DA85B2C8B6D4AABBCA979CBBCED6FB8D5AD483F6440A6C001

//c=x^t mod n
000FB212D1E54C45D537ADBB5CFAB00E4DA49D791BB3FCF80B6215C7EEE45D8B7B5EAFD126B4F233D2FBF2CD874F5AD275FD8B371A827636E610AF80B3D3A42781CC19B9CF6226CAA904AA379FD88001A3114FC2AFB2161DC27D93CBC8024CAEA9B540B316DDBD8E7A40DD14F17BE0575E1289F1E2101169891D655C69E87BD8

//c=x^d mod n
同上

g)费马素性检测

费马小定理:如果p是素数,a∈[1,p-1],那么

a(p-1) ≡ 1 mod p

//判断n是否为素数
1. rand get a, 2<=a<=n-2
2.if gcd(a,n) == 1, goto step3
3.if a^(n-1)=1 mod n, n maybe is prime
4.repeat m times tesp1-step3

n's probability is prime 1-1/2^(m) 

软件安全方案

RSA-私钥运算
1.随机延时--抵御计时攻击
2.故障注入--重复比较
3.反向计算--私钥运算后,再对结果公钥运算比较原文正确性

1.对于安全版本的模幂,参考RSA带公钥的安全模幂

RSA-私钥运算非CRT

Cd
=[ (r-1)e * C]d * r
= [(r-1)e]d * Cd * r
=[(r-1)e]d * r *Cd
=r-1r * Cd

其中,d=d1(1+e * k)+d2(1+e * k) - k,涉及到对底数C的变换,引入随机数(r-1)e

RSA-私钥运算CRT

费马小定理(Fermat’s little theorem):
对于任意素数p和整数a∈Zp,都有
ap ≡ a (mod p)
两边乘a-1: ap-1 ≡ 1 (mod p)

欧拉函数:φ(p)=p-1, p为素数

cd mod p
= c^(d mod φ(p))^ mod p
= c^d mod (p-1)^ mod p
= c^k (p-1)+r^ mod p
= 1k
cr mod p
= cr mod p

基于上述特性,软件安全方案在模幂运算中,引入随机数对指数进行加扰

预计算基本流程:
dp =e-1 mod (p-1)
dq =e-1 mod (q-1)
qInv = (1/q) mod p

私钥运算流程:
m1 = c(dp) mod p ①
m2 = c(dq) mod q ②
h = qInv * (m1 - m2) mod p ③
M = m2+h*q; ④

软件安全方案流程:
流程:
1.引入随机数r,对底数变换cx = c * (r-1)e mod N
2.引入随机数k1, h1, p1, y1对①进行变换:
m1 = c(dp+k1h1+k1(p-1-h1)) mod (y1(p-p1)+y1(p1))
m1 = m1 mod p
3.引入随机数k2, h2, q1, y2对②进行变换:
m2 = c(dq+k2
h2+k1(q-1-h2)) mod (y1(q-q1)+y1(q1))
m2 = m2 mod q
4.引入随机数u1对③进行变换:
Δ = (m1 - m2) mod (y1(p-p1)+y1(p1))
h = [Δ
u+Δ(qInv-u1) mod (y1(p-p1)+y1(p1))] mod p
5.计算M:M=r * (m2+h*q) mod N

DH密钥交换协议

依赖离散对数问题
用户A:(Xa,Ya),Xa私钥、Ya公钥
用户B:(Xb,Yb),Xb私钥、Yb公钥
双方分别把公钥发送给对方
A方计算k 同余 (Yb)^Xa 同余 (g)^(XaXb) mod p
B方计算k 同余 (Ya)^Xb 同余 (g)^(XaXb) mod p

衍生密钥协商协议:ECDH、X25519

参考标准《ANSI X9.42 2003》

域元素(p,q,g)
p:素数
q:p-1的素因子
g:乘法群GF(p)* (包含所有非零元素)的生成元,阶是q

生成公钥pub:pub=g^(priv) mod p
有效性校验:
priv∈[2, q-2]
pub∈[2, p-2],pub^q = 1 mod p,g^q = 1 mod p

openssl:没有考虑q的情况下,将q设置为p-1

SM2

国密局2010年公开公钥密码算法,属于ECC类公钥算法。
2017年11月,SM2/3/9称为ISO/IEC国际标准

Weierstrass曲线:y^2 = x^3 + ax + b
点加公式:R = P +Q
Rx = m^2-Px-Qx, Ry = m(Px-Rx)-Py
注:m = (y2-y1)/(x2-x1) => m=(x1^2 + x2^2 + x1 * x2 + a)/(y1 + y2) //斜率
涉及公式:(x-y)(x+y) = x^2 - y^2; x3-y3 = (x-y)(x2+y2+x
y)

例如求[k]G,最多计算2^r >= k (r次)

设计公开椭圆曲线参数:p、a、b、n、Gx、Gy
私钥32字节,公钥64字节(65字节,前面04代表非压缩格式,压缩格式为33字节(只留x/y坐标),02代表x,03代表

数学介绍:
https://www.jianshu.com/p/090f605f1842/
密钥对生成:Q=d*G

预计算
a)Z = SM3(bitLenofID|| ID|| a || b || Gx || Gy || Xa || Xy),ID缺省1234567812345678 (16bytes)
b)E = SM3(Z || M)
注:bitLenofID(16bit)只取高位8bit带入计算,即((uint8_t)(byteLenofID>>8U)) & 0xFFu
标准提到:“作为签名者的用户A具有长度为entlenA比特的可辨别标识IDA,记ENTLA是由整数entlenA转换 而成的两个字节”,即ID字节长度取值为[0,8191]

椭圆曲线参数
p = FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF
a = FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC
b = 28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93
n = FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123
Gx = 32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7
Gy = BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0

椭圆曲线的阶(计数),包括无穷远点0
阶(基数)计算:Schoof算法,由曲线方程和P值确定

密钥生成

//通过预计算快速点乘
令k=k1||k2,其中k1和k2分别代表k的高128bit和低128比特。
输入k1,预计算点P=2128[G]
调用硬件Shamir点乘方法计算k1[2128[G]]+k2[G]
该运算等价于2128k1[G]+k2[G] = (2128k1+k2)[G] = k[G]

签名

image-20240605165227277

a)选择随机大数k->[1, n-1]
b)计算[k]G=(x1, y1)
c)将32字节的E值转为大数e,计算r = (e+x1) mod n
d)若r=0

//sign
//e=SM3(Z || M),私钥d
while(1)
{
    k = rand(1,n-1);
    [k]G = (x1,y1);
    r = (e+x1) mod n;
    if(r == 0 || r+k == n) 
        continue;
    s = (1+d)^-1(k-r*d) mod n;//这里d不能为N-1
    if(s == 0) continue;
    return (r,s);
}

//sm2签名防护方案--数据盲化
//Δ和rg为随机值
s=((dA - Δ + 1)*rg + Δ*rg)^(-1) * (k+r)*rg - r mod N
=((dA - Δ + 1 + Δ)*rg)^(-1) * (k+r)*rg - r mod N
=[(dA+1)*rg]^(-1) * (k+r)*rg - r mod N
=(dA+1)^(-1) * rg^(-1) * (k+r)*rg - r mod N
=(dA+1)^(-1) * (k+r) -r mod N
=(dA+1)^(-1) * (k+r) -r*(dA+1)^(-1) * (dA+1) mod N
=(dA+1)^(-1) * (k+r - r*(dA+1)) mod N
=(dA+1)^(-1) * (k+r - r*dA - r) mod N
=(dA+1)^(-1) * (k- r*dA) mod N

验签

image-20240605165345363

//verify
if(publickey is not on ECC and not be 0(加法群的单位元)) return fail;
if(r and s are not in [1,n-1]) return fail;
t = (r+s) mod n;
if(t == 0) return fail;
X(x1,y1) = [S]G + [t]P; //这里,[s]G + [t*dA]G = [k]G, 即s+t*dA = k
R = (e+x1) mod n;
if(R == r) return succeed;
    else return fail;

//验签安全方案
e mod n //加扰 引入假动作(不管e是否满足小于n)
x mod n //加扰 引入假动作(不管x是否满足小于n)

加密

KDF(S)是由hash函数hash(m)构造的密钥导出函数。若要生成l比特的密钥 ,则将KDF(S)定义为hash值hash(S||i)的级联,i是计数器,初值为1,每计算一次hash值后递增,直至取得l比特的hash值的级联后为止。

image-20240605165629011

//Enc
//已知明文M,密文接收者公钥Pa
k = get_rand_from(1,n-1)
C1 = [k]G //64字节
Z = [h]Pa //h=1,该步骤省略
[k]Pa = (x2,y2) //[k]Pa = [k * dA]G
t = KDF(x2 || y2)
C2 = M XOR t
C3 = SM3(x2 || M || y2) //32字节,认证作用
return C1 || C3 || C2  //比原文多97字节

//安全方案
使用硬件版本安全点乘

SM2密文结构,一般输出C1C3C2,C1代表曲线的点,占64字节,有些协议会多一个字节0x04,代表未压缩的点,C3是密文的SM3哈希值占32字节,用于完整性校验,C2是密文,字节长度同明文。

SM2_KDF = KDF(Z||ct), ct是32bit的计数器,初始值0x00000001

解密

image-20240605165713629

//dec
//已知密文C1C2C3,解密者私钥da
check_C1_On_ECC(C1)
Z = [h]C1 //h==1
if Z == 无穷远点 return fail //略
[da]C1 = (x2,y2) //[dA]C1 = [dA * k]G
t = KDF(x2 || y2)
m = C2 XOR t
u = SM3(x2 || m || y2)
if (u != C3) return fail
return m

密钥协商

依赖椭圆曲线离散对数难题

SM2密钥协商相比ECDH,增加了一些额外要求:
1.双方需生成各自的临时密钥对(一次性使用),并交换各自临时公钥?
2.双方提供各自ID并交换(用来生成Z值,参与认证值的生成)
3.协商完成后可进行认证,验真协商是否成功(可选)

协商计算前,双方都应先准备好如下材料或动作
1.己方(永久或持久)公私钥对(dA,PA),并将公钥PA发送给对方,同时获得对方公钥PB
2.己方临时公私钥对(rA。RA),并将临时公钥RA发送给对方,同时获得对方临时公钥RB
3.己方ID,并将其发送给对方,同时获得对方的ID。双方协商密钥长度klen

注:ID用来生成各自的Z值ZA和ZB,Z值生成同SM2签名的预计算1

双方生成临时密钥对,交换各自公钥、交换各自ID

image-20240605165554724

//协商流程,用户A为发起方,用户B为响应方
//A方:公私钥对(dA,PA)、临时公私钥对(rA,RA),【B方ID、B方公钥Pb,对方临时公钥Rb】,ZA,ZB
//B方:公私钥(dB,PA)、临时公私钥对(rB,RB),【A方ID、A方公钥Pa,对方临时公钥Rb】,ZA,AB

//用户A
x1' = 2^w+(RA_x & (2^w - 1)) //w=127,取低位127bit,高位1byte异或0x80
tA = (dA + x1' * rA) mod n
if RB not on sm2_curve 
    return fail
else
    x2' = 2^w + (RB_x & (2^w - 1))
U = [h*tA](PB + [x2']RB) = (xU, yU)
if U is 0 
    return fail
else
    KA = KDF(xU||yU||ZA||ZB, klen)
(optional)
{
S1 = Hash(0x02 || yU || Hash(xU||ZA||ZB||RA_x||RA_y||RB_x||RB_y))
if S1 == SB return exchange_key success else fail
SA = Hash(0x03 || yU || Hash(xU||ZA||ZB||RA_x||RA_y||RB_x||RB_y))
}

//用户B
x2' = 2^w + (RB_x & (2^w - 1))
tB = (dB + x2' * rB) mod n
if RA not on sm2_curve 
      return fail
else
      x1' = 2^w + (RA_x & (2^w -1))
V = [h*tB](PA + [x1']RA) = (xV,yV)
if V is 0 
      return fail
else
      KB = KDF(xV||yV||ZA||ZB, klen)
SB = Hash(0x02 || yV || Hash(xV||ZA||ZB||RA_x||RA_y||RB_x||RB_y))
return RB (optional)SB to 用户A
(optional)
{
S2 = Hash(0x03 || yV || Hash(xV||ZA||ZB||RA_x||RA_y||RB_x||RB_y))
if S2 == SA return exchange_key success else return fail
}

签名速度:ECC > RSA
验签:RSA > ECC (因为RSA验签的e值很小)

Demo

//密钥交换
//IDa
31323334353637383132333435363738
//Z=SM3(bitLenofID|| ID|| a || b || Gx || Gy || Xa || Xy)
=SM3(0080||31323334353637383132333435363738||FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC||28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93||32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7||BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0||160E12897DF4EDB61DD812FEB96748FBD3CCF4FFE26AA6F6DB9540AF49C94232||4A7DAD08BB9A459531694BEB20AA489D6649975E1BFCF8C4741B78B4B223007F)
=SM3(008031323334353637383132333435363738FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E9332C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0160E12897DF4EDB61DD812FEB96748FBD3CCF4FFE26AA6F6DB9540AF49C942324A7DAD08BB9A459531694BEB20AA489D6649975E1BFCF8C4741B78B4B223007F)
=3B85A57179E11E7E513AA622991F2CA74D1807A0BD4D4B38F90987A17AC245B1

//Sponsor
//dA
81EB26E941BB5AF16DF116495F90695272AE2CD63D6C4AE1678418BE48230029
//ZA
3B85A57179E11E7E513AA622991F2CA74D1807A0BD4D4B38F90987A17AC245B1
//rA
D4DE15474DB74D06491C440D305E012400990F3E390C7E87153C12DB2EA60BB3
//RA
64CED1BDBC99D590049B434D0FD73428CF608A5DB8FE5CE07F15026940BAE40E//x1
376629C7AB21E7DB260922499DDB118F07CE8EAAE3E7720AFEF6A5CC062070C0//t1
//[S1]
D3A0FE15DEE185CEAE907A6B595CC32A266ED7B3367E9983A896DC32FA20F8EB
//[SA]
18C7894B3816DF16CF07B05C5EC0BEF5D655D58F779CC1B400A4F3884644DB88

//dB
785129917D45A9EA5437A59356B82338EAADDA6CEB199088F14AE10DEFA229B5
//PB
6AE848C57C53C7B1B5FA99EB2286AF078BA64C64591B8B566F7357D576F16DFB
EE489D771621A27B36C5C7992062E9CD09A9264386F3FBEA54DFF69305621C4D
//ZB
79C988D63229D97EF19FE02CA1056E01E6A7411ED24694AA8F834F4A4AB022F7
//rB
7E07124814B309489125EAED101113164EBF0F3458C5BD88335C1F9D596243D6
//RB
ACC27688A6F7B706098BC91FF3AD1BFF7DC2802CDB14CCCCDB0A90471F9BD707
2FEDAC0494B2FFC4D6853876C79B8F301C6573AD0AA50F39FC87181E1A1B46FE
//[S2]
18C7894B3816DF16CF07B05C5EC0BEF5D655D58F779CC1B400A4F3884644DB88
//[SB]
D3A0FE15DEE185CEAE907A6B595CC32A266ED7B3367E9983A896DC32FA20F8EB

//k
6C89347354DE2484C60B4AB1FDE4C6E5

//取x1'=2^127+(x1&(2^127 - 1)) //x1为RA的x坐标,保留低127bit
x1'=2^127 + CF608A5DB8FE5CE07F15026940BAE40E=CF608A5DB8FE5CE07F15026940BAE40E
//tA=(dA+x1'*rA) mod n
tA=(dA+CF608A5DB8FE5CE07F15026940BAE40E*D4DE15474DB74D06491C440D305E012400990F3E390C7E87153C12DB2EA60BB3) mod FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123
=(81EB26E941BB5AF16DF116495F90695272AE2CD63D6C4AE1678418BE48230029+BB7D99D62C08B4264BECE7B73443579195F483A72E7239F2993AD3692DC40DE0) mod n
=3D68C0C06DC40F17B9DDFE0093D3C0E4969ED1124A187FA8AD02F81E3C11CCE6

//取x2'=2^127+(x2&(2^127 - 1)) //x2为RB的x坐标,保留低127bit
x2=2^127 + 7DC2802CDB14CCCCDB0A90471F9BD707=FDC2802CDB14CCCCDB0A90471F9BD707
//U=[h*tA](PB + x2'*RB) //h=1
=tA*PB + [tA*x2' mod n]RB
=tA*PB + [3D68C0C06DC40F17B9DDFE0093D3C0E4969ED1124A187FA8AD02F81E3C11CCE6*FDC2802CDB14CCCCDB0A90471F9BD707 mod n]RB
=tA*PB + [20B34E8F94D2B962B9CC15ADA9F3E7C83601A2C02E4BACF0D6610B668A9AAE3F]RB
=[3D68C0C06DC40F17B9DDFE0093D3C0E4969ED1124A187FA8AD02F81E3C11CCE6]PB +[20B34E8F94D2B962B9CC15ADA9F3E7C83601A2C02E4BACF0D6610B668A9AAE3F]RB
=C558B44BEE5301D9F52B44D939BB59584D75B9034DD6A9FC826872109A65739F||3252B35B191D8AE01CD122C025204334C5EACF68A0CB4854C6A7D367ECAD4DE7

//KA=KDF(Ux||Uy||ZA||ZB, klen)
//KA_16byte_0
=SM3(C558B44BEE5301D9F52B44D939BB59584D75B9034DD6A9FC826872109A65739F||3252B35B191D8AE01CD122C025204334C5EACF68A0CB4854C6A7D367ECAD4DE7||3B85A57179E11E7E513AA622991F2CA74D1807A0BD4D4B38F90987A17AC245B1||79C988D63229D97EF19FE02CA1056E01E6A7411ED24694AA8F834F4A4AB022F7||0x00000001)
=6C89347354DE2484C60B4AB1FDE4C6E579391A21FA6CB72AE8754EC21AD8B703
=6C89347354DE2484C60B4AB1FDE4C6E5//取前面16byte


//下面获取KB
...


总结:
1.己方:已方临时公钥x坐标低127bit,高bit异或上1,结合己方私钥和乙方临时私钥,计算t
2.己方:对方临时公钥x坐标低127bit,高bit异或上1,结合对方公钥和对方临时公钥,计算U
3.通过U和双方Z值计算KDF

基于SM2算法的无证书及隐式证书公钥机制

文档:《GM/T 0130-2023》

image-20250205150434038

image-20250205144211292

注:HA计算的公钥参数为系统主公钥坐标

隐式证书:无需CA签名,使用通过声明公钥验证(即公钥还原数据)

ks:KGC秘密,用于派生伪随机数
ms:系统主私钥(隐式证书系统中,为CA的签名私钥)
PPub:系统主公钥(隐式证书系统中,为CA的签名公钥)
PA:用户A的实际公钥
WA:用户A的声明公钥(隐式证书系统中,为隐式证书中的公钥还原数据)==无证书身份公钥==,使用主公钥进行验证合法性
UA:用户A生成的部分公钥
tA:KGC为用户A生成的部分私钥
dA:用户私钥==无证书身份私钥==

image-20250205151847892

image-20250205151924272

流程:
1.KGC:初始化,ms,Ppub,并公开Ppub
2.用户:生临时公私钥dA‘ = rand(),UA = [dA']G
3.用户:将身份信息IDA和临时公钥UA给到KGC
4.KGC:生成对应用户的声明公钥WA = UA + [w]G (w = rand())
5.KGC:基于声明公钥,系统参数、KGC主公钥生成私钥分片tA = w + z * ms mod N,其中z = H256(xwA||ywA||HA) mod N
6.用户:基于实际私钥dA,生成真正私钥dA = tA + dA mod N和公钥PA
7.用户:基于声明公钥WA,主公钥Ppub,生成真正公钥PA = WA + [z]Ppub,其中,z=H256(xWA || yWA || H256(xwA||ywA||HA) ) mod N

签名

image-20250205144412663

验签

image-20250205144424481

加密

image-20250205144506152

PA是引入了主公钥计算的公钥,理解为在KGC下变换另一个公钥进行SM2加密

解密

image-20250205144517739

ECDSA

参考标准:Public Key Cryptography for the Financial Services Industry. The Elliptic Curve Digital Signature Algorithm(ECDSA). 2005. s.l. : ANS X9.62–2005
参考标准:《sec1-v2 2009.pdf》https://www.secg.org/sec1-v2.pdf
上述两个标准是一致的

签名方案,是DSA的椭圆曲线版本

曲线参数可参考密码小工具或者PKE_UHP工程

1、计算Z(according to SEC 1-V2 2009)

//以spec160k1为例,SHA1("abc")=023097D223405D8228642A477BDA255B32AADBCE4BDA0B3F7E36C9DA7
//曲线nbit=161位
//若hash长度比nbit小,实际运算大数就是hash值
//若hash长度比nbit大,实际运算大数:从左边开始截取nbit,注意2是0010开始,非10开始
//Z
0x4612fa44680bb0450c8548ef7b44ab66555b79c9 //数值从右往左4bit转换   《FIPS 186-5》


/* 以下忽略 历史描述
//(0x023097D223405D8228642A477BDA255B32AADBCE4BDA0B3F7E36C9DA7)转2进制,注意实际大数(4bit一个块)
000000100011000010010111110100100010001101000000010111011000001000101000011001000010101001000111011110111101101000100101010110110011001010101010110110111100111001001011110110100000101100111111011111100011011011001001110110100111
//从左往右截取161bit
00100011000010010111110100100010001101000000010111011000001000101000011001000010101001000111011110111101101000100101010110110011001010101010110110111100111001001
//转成U8,161比特需要高位补3bit0
00000100011000010010111110100100010001101000000010111011000001000101000011001000010101001000111011110111101101000100101010110110011001010101010110110111100111001001
//转U8
0x04612fa44680bb0450c8548ef7b44ab66555b79c9
0x04612fa44680bb0450c8548ef7b44ab66555b79c
  以上忽略 历史描述 */

//(0x023097D223405D8228642A477BDA255B32AADBCE4BDA0B3F7E36C9DA7)转2进制,注意实际大数(8bit一个块,字节对齐)
000000100011000010010111110100100010001101000000010111011000001000101000011001000010101001000111011110111101101000100101010110110011001010101010110110111100111001001011110110100000101100111111011111100011011011001001110110100111
//从左往右截取161bit
00000010001100001001011111010010001000110100000001011101100000100010100001100100001010100100011101111011110110100010010101011011001100101010101011011011110011100
//转成U8,161比特需要高位补7bit0(相当于右移7bit)
0000000||00000010001100001001011111010010001000110100000001011101100000100010100001100100001010100100011101111011110110100010010101011011001100101010101011011011110011100
//转U8
0x00||04612fa44680bb0450c8548ef7b44ab66555b79c(161bit取21Byte)

2、签名流程

//e=Hash(m)
//z=e从左边截断的Ln个bit构成大数,Ln是曲线阶n的bit长度
while(1)
{
    k = rand(1,n-1);
    [k]G = (x1,y1);  //[k-r]G + [r]G   //[k+1]G
    r = x1 mod n;
    if(r == 0) 
        continue;
    s = k^(-1)*(z+r*dA) mod n; //r=0,r*dA无意义
    if(s == 0) continue;
    return (r,s);//(rf,sf)
}

1.calc e=hash(m)
2.let z be the left most of e
3.get the k randomly chosen from [1,n-1]
4.calc the curve point(x,y)=[k]G,r=x, if r=0 return back to step 3
5.calc s=(z+r*dA)/k(mod n) if s=0 return back to step 3
6.return (r,s)

安全方案:将r和d进行拆分
k-1(e+r1d1+r2d2) + k-1(r1d2+r2d1)
=k-1(e+(r1+r2)(d1+d2))
=k-1(e+r
d) mod n

//硬件算子
Input:消息hash值e, 私钥dA, 曲线参数
Output:签名值rs 
while(1)
{
    e = pre_process(e) //参考SEC1-V2—2009
    k = rand(1,n-1);
    [k]G = (x1,y1);  //1次点乘
    r = x1 mod n; //1次模运算
    if(r == 0) 
        continue;
    s = k^(-1)*(e+r*dA) mod n; //1次模逆,2次模乘,1次模加
    if(s == 0) continue;
    return (r,s);
}

3、验证流程

//使用公钥和消息m,对签名进行验证
//verify
if(publickey is not on ECC and not be 0(无穷远点,加法群的单位元)) return fail;
if(r and s are not in [1,n-1]) return fail;
e = Hash(m);
z = get(z) //e从左边截断的Ln个bit构成大数,Ln是曲线阶n的bit长度
u1 = z * s^(-1) mod n
u2 = r * s^(-1) mod n
[u1]G + [u2]Pa = (x1, y1)
if(X == 无穷远点) return fail;
v = x1 mod n
if(v == r) return succeed;
    else return fail;

z * s^(-1) * G + r * s^(-1) * Q
=z * s^(-1) + r * s^(-1) * dA * G
=[z * s^(-1) + r * s^(-1) * dA]G
=[s^(-1)](z + r * dA) * G
即要[s^(-1)](z + r * dA)等价于[k]

由s = k^(-1)*(z+r*dA) mod n,两边同乘k,除s,得到k=[s^(-1)](z + r * dA) mod n

ECDH

ECDH密钥协商是DH的椭圆曲线版本

基于EC点乘原理:(a * G ) * b = a * (G * b)

原始的ECDH协议里,密钥协商双方分别使用各自的私钥和对方的公钥,这样做有一些不便和危险。例如,若双方都用自己的固定密钥对,那么每次协商出来的密钥都一样,攻击者可以截获很多用相同的协商密钥加密的密文,这增大了破解协商密钥的可能,一旦攻击者得手,那么之前的加密会话密文都会被破解。即便其中一方使用固定的密钥对,另一方每次都实时生成密钥对,也仍然不够安全,因为一旦固定密钥被破解,则协商密钥就被破解了(用固定的私钥和公开的对方公钥即得),之前的密文也就被解密了。

因此推荐的做法是每次密钥协商前,双方各自生成自己的临时密钥对,双方均用临时密钥对进行密钥协商,这样可以最大程度避免上述的缺点。这种修正的方法即ECDHE,其中E是ephemeral的缩写,表示每次都用临时的,且只用一次的临时密钥对。

ECIES

ECIES(Elliptic Curve Integrated Encryption Scheme),即椭圆曲线集成加密方案,它是一种混合的公钥加密方案,内部用到椭圆曲线密钥交换(ECDH)、密钥派生函数(KDF)、对称加密和消息验证码(MAC)。

规定ECIES流程主要的加密标准有SECG SEC-1、ISO/IEC 18033-2、IEEE 1363a和ANSI X9.63。现在描述的是标准ANSI X9.63。在该标准中定义了基于hash算法的KDF函数(即ANSI-X9.63-KDF),规定了采用的对称算法为异或加密,MAC算法为HMAC

ECIES输出的密文由发送方的临时公钥C1、被加密的消息C2、消息验证码C3三部分组成

这里的ECIES使用的椭圆曲线方程为y^2 = x^3 +ax+b,支持这类的曲线如secp曲线、brainpoolp曲线

key pair generation

Standard Diffe-Hellman

Input
1.an EC private key d owned by A
2.an EC private key Q owned by B

Actions
1.Compute the point P=dQ
2.Check P,if P==0 stop
3.Set z=Px, Px is the x-coordinate of P

Output
z∈Fq as the shared secret value

Modified Diffe-Hellman

//同上,唯一不同是
1.Compute the point P=hdQ

hash function

1.使用摘要长度大于等于224bit(28byte)

kdf function

见KDF章节

Encryption Transformation

Input
1.a bit string EncData
2.An EC public key Q owned by receiver
3.(optional)sharedData1, sharedData2, which are shared by the sender and the receiver

Action
1.Generate an ephemeral key pair(de,Qe), corresponding to the EC domain parameters D
2.conver Qe to a bit string QE
3.use DH to derive a shared secret value z∈Fq from de and Q, if error,stop
4.conver z∈Fq to a bit string Z
5.use KDF generate keydata of bit length encdatalen+mackeylen from Z and SharedData1
6.parse keyData: KeyData = EncKey||MacKey//k1||k2
7.compute MaskedEncData=EncData xor EncKey
8.compute the tag MacTag on the bit string: MacData=MaskedEncData||[SharedData2]

Output
QE||MaskedEncData||MacTag

d)Decryption Transformation

Input
1.QE||MaskedEncData||MacTag
2.An EC private key d own by the recipient
3.(optional)SharedData1,SharedData2

Action
1.conver QE to an elliptic Qe'
2.Check public key Qe'
3.use DH z=get_x_coordinate([d]Qe)
4.convert z to a bit string Z
5.use KDF generate keydata of bit length encdatalen+mackeylen from Z and SharedData1
6.parse keyData: KeyData = EncKey||MacKey//k1(encdatalen)||k2(mackeylen)
7.Compute EncData=MaskedEncData xor EncKey
8.hmac(MacKey, MaskedEncData||SharedData2)

Output
if verify pass
    return EncData
else
    return error
//Demo
ECIES

========secp256k1==========
//receiver_pri_key
23CDCED066095AA72DE0192509A1E540DE341C1D4DC3B1DEB8A258A6BA26199C

//receiver_pub_key=[receiver_pri_key]G
E3B2EBEC783BB230F20F336E30FBA3BADD0A3453B4F6DCB77C0462B5166F795A
171C91D560427708ED15DCFF24E858233881AD2478CBA05EC9145836B9C787D9

//sender_tmp_pri_key
998943C47E19596D126BB33AC7C656F764D509F2F39AA9C0A872117B9CBA3D8C

//plain (43byte)
33333320717569636B2062726F776E20666F78206A756D7073206F76657220746865206C617A7944444444


//shared_info for KDF
//shared_info1_2  (51bytes)
A7F359B46855C48A1B672F1C65FB57A8F5581AD4A96AA0637C4D36F44BCDFBA5D0101E5ECBAF6F4FEA3CD2706B459650D9089F

//shared_info for MAC
//shared_info2_1 (5bytes)
6162636465

//hmac_key_byte
0x20

Output=Q||EncData||Hmac

//Q=[sender_tmp_pri_key]G
382A1AABFBD5B501917145D25035027DE977F65E432F0C89B7AD58A25FFF9103EB73E26421182E2F508C5C0A0D65BBDCBB0CD07D62DC628A93F7EE2F0F2B9E8D


==========without s1 s2==========
//KDF

//[sender_tmp_pri_key]receiver_pub_key
=[998943C47E19596D126BB33AC7C656F764D509F2F39AA9C0A872117B9CBA3D8C]*E3B2EBEC783BB230F20F336E30FBA3BADD0A3453B4F6DCB77C0462B5166F795A171C91D560427708ED15DCFF24E858233881AD2478CBA05EC9145836B9C787D9
=DAC75A25D3FCA8F8905FB5079C08FE8B470BD32BE22D50424660DE311DE83598||D37BA38C5D1D390CC5238DBDD404C7ED56BFE24FA1CA242BCDBD2C91EA6B151B

//get Z=coordiate x
DAC75A25D3FCA8F8905FB5079C08FE8B470BD32BE22D50424660DE311DE83598


//Compute Hash_i = H(Z||counter||Shareinfo), counter=00000001, Hash_alg=sha512
//KDF
//H(DAC75A25D3FCA8F8905FB5079C08FE8B470BD32BE22D50424660DE311DE83598||00000001)
AFC610393EDC3CE518BF8B13565B87B6D06A6787781C29F57DBC3EB8EFCFC5E963438D303F3D379BA04DA263E29F9828243D23EB65D652A5C44BB6D82D469574
//H(DAC75A25D3FCA8F8905FB5079C08FE8B470BD32BE22D50424660DE311DE83598||00000002)
AEF21C42354B0C638DCCF5CA1B5E18627EA9674103B65DA1932D78163B6339C75429A245BA82006173E92F25BEC8764404A864623363CF4BD142DC29FE117EBE

//Enc_key = MBS(KDF,sizeof(plain)=43)
AFC610393EDC3CE518BF8B13565B87B6D06A6787781C29F57DBC3EB8EFCFC5E963438D303F3D379BA04DA2

//Cipher = Plain XOR (MBS(KDF,sizeof(plain)=43))
AFC610393EDC3CE518BF8B13565B87B6D06A6787781C29F57DBC3EB8EFCFC5E963438D303F3D379BA04DA2
33333320717569636B2062726F776E20666F78206A756D7073206F76657220746865206C617A7944444444
=9CF523194FA95586739FE961392CE996B6051FA7126944850E9C51CE8ABDE59D0B26AD5C5E474EDFE409E6


//mac_key = MBS(KDF+sizeof(plain)=43, sizepf(mac_key_byte)=32)
63E29F9828243D23EB65D652A5C44BB6D82D469574AEF21C42354B0C638DCCF5
//Mac=Hmac(mac_key,Cipher||ShareInfo2=NULL),hash_alg=sha1
=(63E29F9828243D23EB65D652A5C44BB6D82D469574AEF21C42354B0C638DCCF5, 9CF523194FA95586739FE961392CE996B6051FA7126944850E9C51CE8ABDE59D0B26AD5C5E474EDFE409E6)
=E48FC0BBFAAF26897CE60A541BAFC536640CED52





================================secp256k1 SHA256==================================
//receiver_pri_key
23CDCED066095AA72DE0192509A1E540DE341C1D4DC3B1DEB8A258A6BA26199C

//receiver_pub_key=[receiver_pri_key]G
E3B2EBEC783BB230F20F336E30FBA3BADD0A3453B4F6DCB77C0462B5166F795A
171C91D560427708ED15DCFF24E858233881AD2478CBA05EC9145836B9C787D9

//sender_tmp_pri_key
998943C47E19596D126BB33AC7C656F764D509F2F39AA9C0A872117B9CBA3D8C

//plain (43byte)
33333320717569636B2062726F776E20666F78206A756D7073206F76657220746865206C617A7944444444


//shared_info for KDF
//shared_info1_2  (51bytes)
A7F359B46855C48A1B672F1C65FB57A8F5581AD4A96AA0637C4D36F44BCDFBA5D0101E5ECBAF6F4FEA3CD2706B459650D9089F

//shared_info for MAC
//shared_info2_1 (5bytes)
6162636465

//hmac_key_byte
0x20

Output=Q||EncData||Hmac

//Q=[sender_tmp_pri_key]G
382A1AABFBD5B501917145D25035027DE977F65E432F0C89B7AD58A25FFF9103EB73E26421182E2F508C5C0A0D65BBDCBB0CD07D62DC628A93F7EE2F0F2B9E8D


==========without s1 s2==========
//KDF

//[sender_tmp_pri_key]receiver_pub_key
=[998943C47E19596D126BB33AC7C656F764D509F2F39AA9C0A872117B9CBA3D8C]*E3B2EBEC783BB230F20F336E30FBA3BADD0A3453B4F6DCB77C0462B5166F795A171C91D560427708ED15DCFF24E858233881AD2478CBA05EC9145836B9C787D9
=DAC75A25D3FCA8F8905FB5079C08FE8B470BD32BE22D50424660DE311DE83598||D37BA38C5D1D390CC5238DBDD404C7ED56BFE24FA1CA242BCDBD2C91EA6B151B

//get Z=coordiate x
DAC75A25D3FCA8F8905FB5079C08FE8B470BD32BE22D50424660DE311DE83598


//Compute Hash_i = H(Z||counter||Shareinfo), counter=00000001, Hash_alg=sha256
//KDF
//H(DAC75A25D3FCA8F8905FB5079C08FE8B470BD32BE22D50424660DE311DE83598||00000001)
88ED4CCAF84134FC66360397B35B2CA9081CADE5071B8AC95A767441C8B5CA24
//H(DAC75A25D3FCA8F8905FB5079C08FE8B470BD32BE22D50424660DE311DE83598||00000002)
B8B34FFFF293CAF036AE901E590B4CD39ACCA91C7563247450185F1264FDC2F0
//H(DAC75A25D3FCA8F8905FB5079C08FE8B470BD32BE22D50424660DE311DE83598||00000003)
6B2762EB34DCA0DEA26BA6D29BEE0E1803084CAFD51B21B1C35843B2FA9FFF62

//Enc_key = MBS(KDF,sizeof(plain)=43)
88ED4CCAF84134FC66360397B35B2CA9081CADE5071B8AC95A767441C8B5CA24B8B34FFFF293CAF036AE90

//Cipher = (MBS(KDF,sizeof(plain)=43)) XOR Plain
88ED4CCAF84134FC66360397B35B2CA9081CADE5071B8AC95A767441C8B5CA24B8B34FFFF293CAF036AE90 ^
33333320717569636B2062726F776E20666F78206A756D7073206F76657220746865206C617A7944444444
=BBDE7FEA89345D9F0D1661E5DC2C42896E73D5C56D6EE7B929561B37ADC7EA50D0D66F9393E9B3B472EAD4


//mac_key = MBS(KDF+sizeof(plain)=43, sizepf(mac_key_byte)=32)
1E590B4CD39ACCA91C7563247450185F1264FDC2F06B2762EB34DCA0DEA26BA6
//Mac=Hmac(mac_key,Cipher||ShareInfo2=NULL),hash_alg=sha256
=(1E590B4CD39ACCA91C7563247450185F1264FDC2F06B2762EB34DCA0DEA26BA6, BBDE7FEA89345D9F0D1661E5DC2C42896E73D5C56D6EE7B929561B37ADC7EA50D0D66F9393E9B3B472EAD4)
=BAC23020CBB5A5DA57A9C6B98E739794C8D8023BB0DB2B9C65BBD205C93DA173



==========only with s1==========
//Compute Hash_i = H(Z||counter||Shareinfo1), counter=00000001, Hash_alg=sha256
//KDF
//H(DAC75A25D3FCA8F8905FB5079C08FE8B470BD32BE22D50424660DE311DE83598||00000001||A7F359B46855C48A1B672F1C65FB57A8F5581AD4A96AA0637C4D36F44BCDFBA5D0101E5ECBAF6F4FEA3CD2706B459650D9089F)
AD5ECFBEF03BE969B84FB07A9E0337C93D9493B07C6533BCF82A910D9F97D61F
//H(DAC75A25D3FCA8F8905FB5079C08FE8B470BD32BE22D50424660DE311DE83598||00000002||A7F359B46855C48A1B672F1C65FB57A8F5581AD4A96AA0637C4D36F44BCDFBA5D0101E5ECBAF6F4FEA3CD2706B459650D9089F)
82869BDA29490DAF6A0650F581CD6592D3EA5D5AE14B160B275F420AAF277B5F
//H(DAC75A25D3FCA8F8905FB5079C08FE8B470BD32BE22D50424660DE311DE83598||00000003||A7F359B46855C48A1B672F1C65FB57A8F5581AD4A96AA0637C4D36F44BCDFBA5D0101E5ECBAF6F4FEA3CD2706B459650D9089F)
0651C3D52DED2E9B8FFDC5D0AFF2178AF462BB46C70A63651934B3D4FFBCAAC6

AD5ECFBEF03BE969B84FB07A9E0337C93D9493B07C6533BCF82A910D9F97D61F82869BDA29490DAF6A0650 

//Enc_key = MBS(KDF,sizeof(plain)=43)
AD5ECFBEF03BE969B84FB07A9E0337C93D9493B07C6533BCF82A910D9F97D61F82869BDA29490DAF6A0650

//Cipher = (MBS(KDF,sizeof(plain)=43)) XOR Plain
AD5ECFBEF03BE969B84FB07A9E0337C93D9493B07C6533BCF82A910D9F97D61F82869BDA29490DAF6A0650
33333320717569636B2062726F776E20666F78206A756D7073206F76657220746865206C617A7944444444
=9E6DFC9E814E800AD36FD208F17459E95BFBEB9016105ECC8B0AFE7BFAE5F66BEAE3BBB6483374EB2E4214


//mac_key = MBS(KDF+sizeof(plain)=43, sizepf(mac_key_byte)=32)
F581CD6592D3EA5D5AE14B160B275F420AAF277B5F0651C3D52DED2E9B8FFDC5
//Mac=Hmac(mac_key,Cipher||ShareInfo2=NULL),hash_alg=sha256
=(F581CD6592D3EA5D5AE14B160B275F420AAF277B5F0651C3D52DED2E9B8FFDC5, 9E6DFC9E814E800AD36FD208F17459E95BFBEB9016105ECC8B0AFE7BFAE5F66BEAE3BBB6483374EB2E4214)
=8BB6E853122377B14F9E3F91A8F325ABF1D8D077A757BDB9630DCA97AAE17AF3


==========only with s2==========
//KDF

//[sender_tmp_pri_key]receiver_pub_key
=[998943C47E19596D126BB33AC7C656F764D509F2F39AA9C0A872117B9CBA3D8C]*E3B2EBEC783BB230F20F336E30FBA3BADD0A3453B4F6DCB77C0462B5166F795A171C91D560427708ED15DCFF24E858233881AD2478CBA05EC9145836B9C787D9
=DAC75A25D3FCA8F8905FB5079C08FE8B470BD32BE22D50424660DE311DE83598||D37BA38C5D1D390CC5238DBDD404C7ED56BFE24FA1CA242BCDBD2C91EA6B151B

//get Z=coordiate x
DAC75A25D3FCA8F8905FB5079C08FE8B470BD32BE22D50424660DE311DE83598


//Compute Hash_i = H(Z||counter||Shareinfo), counter=00000001, Hash_alg=sha256
//KDF
//H(DAC75A25D3FCA8F8905FB5079C08FE8B470BD32BE22D50424660DE311DE83598||00000001)
88ED4CCAF84134FC66360397B35B2CA9081CADE5071B8AC95A767441C8B5CA24
//H(DAC75A25D3FCA8F8905FB5079C08FE8B470BD32BE22D50424660DE311DE83598||00000002)
B8B34FFFF293CAF036AE901E590B4CD39ACCA91C7563247450185F1264FDC2F0
//H(DAC75A25D3FCA8F8905FB5079C08FE8B470BD32BE22D50424660DE311DE83598||00000003)
6B2762EB34DCA0DEA26BA6D29BEE0E1803084CAFD51B21B1C35843B2FA9FFF62

//Enc_key = MBS(KDF,sizeof(plain)=43)
88ED4CCAF84134FC66360397B35B2CA9081CADE5071B8AC95A767441C8B5CA24B8B34FFFF293CAF036AE90

//Cipher = (MBS(KDF,sizeof(plain)=43)) XOR Plain
88ED4CCAF84134FC66360397B35B2CA9081CADE5071B8AC95A767441C8B5CA24B8B34FFFF293CAF036AE90 ^
33333320717569636B2062726F776E20666F78206A756D7073206F76657220746865206C617A7944444444
=BBDE7FEA89345D9F0D1661E5DC2C42896E73D5C56D6EE7B929561B37ADC7EA50D0D66F9393E9B3B472EAD4


//mac_key = MBS(KDF+sizeof(plain)=43, sizepf(mac_key_byte)=32)
1E590B4CD39ACCA91C7563247450185F1264FDC2F06B2762EB34DCA0DEA26BA6
//Mac=Hmac(mac_key,Cipher||ShareInfo2=NULL),hash_alg=sha256
=(1E590B4CD39ACCA91C7563247450185F1264FDC2F06B2762EB34DCA0DEA26BA6, BBDE7FEA89345D9F0D1661E5DC2C42896E73D5C56D6EE7B929561B37ADC7EA50D0D66F9393E9B3B472EAD4||6162636465)
=490B384E5C4FF3300DDE70A3663BBE3EBBA306A523402ED595F4F10B4B63B261




==========only with s1 and s2==========
//Compute Hash_i = H(Z||counter||Shareinfo1), counter=00000001, Hash_alg=sha256
//KDF
//H(DAC75A25D3FCA8F8905FB5079C08FE8B470BD32BE22D50424660DE311DE83598||00000001||A7F359B46855C48A1B672F1C65FB57A8F5581AD4A96AA0637C4D36F44BCDFBA5D0101E5ECBAF6F4FEA3CD2706B459650D9089F)
AD5ECFBEF03BE969B84FB07A9E0337C93D9493B07C6533BCF82A910D9F97D61F
//H(DAC75A25D3FCA8F8905FB5079C08FE8B470BD32BE22D50424660DE311DE83598||00000002||A7F359B46855C48A1B672F1C65FB57A8F5581AD4A96AA0637C4D36F44BCDFBA5D0101E5ECBAF6F4FEA3CD2706B459650D9089F)
82869BDA29490DAF6A0650F581CD6592D3EA5D5AE14B160B275F420AAF277B5F
//H(DAC75A25D3FCA8F8905FB5079C08FE8B470BD32BE22D50424660DE311DE83598||00000003||A7F359B46855C48A1B672F1C65FB57A8F5581AD4A96AA0637C4D36F44BCDFBA5D0101E5ECBAF6F4FEA3CD2706B459650D9089F)
0651C3D52DED2E9B8FFDC5D0AFF2178AF462BB46C70A63651934B3D4FFBCAAC6

AD5ECFBEF03BE969B84FB07A9E0337C93D9493B07C6533BCF82A910D9F97D61F82869BDA29490DAF6A0650 

//Enc_key = MBS(KDF,sizeof(plain)=43)
AD5ECFBEF03BE969B84FB07A9E0337C93D9493B07C6533BCF82A910D9F97D61F82869BDA29490DAF6A0650

//Cipher = (MBS(KDF,sizeof(plain)=43)) XOR Plain
AD5ECFBEF03BE969B84FB07A9E0337C93D9493B07C6533BCF82A910D9F97D61F82869BDA29490DAF6A0650
33333320717569636B2062726F776E20666F78206A756D7073206F76657220746865206C617A7944444444
=9E6DFC9E814E800AD36FD208F17459E95BFBEB9016105ECC8B0AFE7BFAE5F66BEAE3BBB6483374EB2E4214


//mac_key = MBS(KDF+sizeof(plain)=43, sizepf(mac_key_byte)=32)
F581CD6592D3EA5D5AE14B160B275F420AAF277B5F0651C3D52DED2E9B8FFDC5
//Mac=Hmac(mac_key,Cipher||ShareInfo2=NULL),hash_alg=sha256
=(F581CD6592D3EA5D5AE14B160B275F420AAF277B5F0651C3D52DED2E9B8FFDC5, 9E6DFC9E814E800AD36FD208F17459E95BFBEB9016105ECC8B0AFE7BFAE5F66BEAE3BBB6483374EB2E4214||6162636465)
=686C508582CD785519A76A59E9379083F3CA4DFB6A0BF1D2E4CB8EC7427ACCBB




================================secp256k1 SM3==================================
//receiver_pri_key
23CDCED066095AA72DE0192509A1E540DE341C1D4DC3B1DEB8A258A6BA26199C

//receiver_pub_key=[receiver_pri_key]G
E3B2EBEC783BB230F20F336E30FBA3BADD0A3453B4F6DCB77C0462B5166F795A
171C91D560427708ED15DCFF24E858233881AD2478CBA05EC9145836B9C787D9

//sender_tmp_pri_key
998943C47E19596D126BB33AC7C656F764D509F2F39AA9C0A872117B9CBA3D8C

//plain (43byte)
33333320717569636B2062726F776E20666F78206A756D7073206F76657220746865206C617A7944444444


//shared_info for KDF
//shared_info1_2  (51bytes)
A7F359B46855C48A1B672F1C65FB57A8F5581AD4A96AA0637C4D36F44BCDFBA5D0101E5ECBAF6F4FEA3CD2706B459650D9089F

//shared_info for MAC
//shared_info2_1 (5bytes)
6162636465

//hmac_key_byte
0x20

Output=Q||EncData||Hmac

//Q=[sender_tmp_pri_key]G
382A1AABFBD5B501917145D25035027DE977F65E432F0C89B7AD58A25FFF9103EB73E26421182E2F508C5C0A0D65BBDCBB0CD07D62DC628A93F7EE2F0F2B9E8D


==========without s1 s2==========
//KDF

//[sender_tmp_pri_key]receiver_pub_key
=[998943C47E19596D126BB33AC7C656F764D509F2F39AA9C0A872117B9CBA3D8C]*E3B2EBEC783BB230F20F336E30FBA3BADD0A3453B4F6DCB77C0462B5166F795A171C91D560427708ED15DCFF24E858233881AD2478CBA05EC9145836B9C787D9
=DAC75A25D3FCA8F8905FB5079C08FE8B470BD32BE22D50424660DE311DE83598||D37BA38C5D1D390CC5238DBDD404C7ED56BFE24FA1CA242BCDBD2C91EA6B151B

//get Z=coordiate x
DAC75A25D3FCA8F8905FB5079C08FE8B470BD32BE22D50424660DE311DE83598


//Compute Hash_i = H(Z||counter||Shareinfo), counter=00000001, Hash_alg=SM3
//KDF
//H(DAC75A25D3FCA8F8905FB5079C08FE8B470BD32BE22D50424660DE311DE83598||00000001)
443DC3B378B234CB4FA82731B21313513FC663B2F8FAD076D2CA6551EA97CF3C
//H(DAC75A25D3FCA8F8905FB5079C08FE8B470BD32BE22D50424660DE311DE83598||00000002)
76A7FC64D6351048F8CCEB32790BB50A65404560C0DA6131D1DAFCB572BE8B33
//H(DAC75A25D3FCA8F8905FB5079C08FE8B470BD32BE22D50424660DE311DE83598||00000003)
3AEB1DD3739DCA104822DB08E9BAF59560ED71D10B2FFF1609C973DDBF8A9E9A

443DC3B378B234CB4FA82731B21313513FC663B2F8FAD076D2CA6551EA97CF3C76A7FC64D6351048F8CCEB 32790BB50A65404560C0DA6131D1DAFCB572BE8B333AEB1DD3739DCA104822DB08E9BAF59560ED71D10B2FFF1609C973DDBF8A9E9A

//Enc_key = MBS(KDF,sizeof(plain)=43)
443DC3B378B234CB4FA82731B21313513FC663B2F8FAD076D2CA6551EA97CF3C76A7FC64D6351048F8CCEB

//Cipher = (MBS(KDF,sizeof(plain)=43)) XOR Plain
443DC3B378B234CB4FA82731B21313513FC663B2F8FAD076D2CA6551EA97CF3C76A7FC64D6351048F8CCEB ^
33333320717569636B2062726F776E20666F78206A756D7073206F76657220746865206C617A7944444444
=770EF09309C75DA824884543DD647D7159A91B92928FBD06A1EA0A278FE5EF481EC2DC08B74F690CBC88AF


//mac_key = MBS(KDF+sizeof(plain)=43, sizepf(mac_key_byte)=32)
32790BB50A65404560C0DA6131D1DAFCB572BE8B333AEB1DD3739DCA104822DB
//Mac=Hmac(mac_key,Cipher||ShareInfo2=NULL),hash_alg=SM3
=(32790BB50A65404560C0DA6131D1DAFCB572BE8B333AEB1DD3739DCA104822DB, 770EF09309C75DA824884543DD647D7159A91B92928FBD06A1EA0A278FE5EF481EC2DC08B74F690CBC88AF)
=C17A7AECCA3FFA80B5DBC1E2B755022988592D642D68B9CDC18A1FFE7805F835



==========only with s1==========

//Compute Hash_i = H(Z||counter||Shareinfo), counter=00000001, Hash_alg=SM3
//KDF
//H(DAC75A25D3FCA8F8905FB5079C08FE8B470BD32BE22D50424660DE311DE83598||00000001||A7F359B46855C48A1B672F1C65FB57A8F5581AD4A96AA0637C4D36F44BCDFBA5D0101E5ECBAF6F4FEA3CD2706B459650D9089F)
B56AD8D7ECA977DA795245B84FC00ACB17914160DFDD0D2E75A2FDB6835458F4
//H(DAC75A25D3FCA8F8905FB5079C08FE8B470BD32BE22D50424660DE311DE83598||00000002||A7F359B46855C48A1B672F1C65FB57A8F5581AD4A96AA0637C4D36F44BCDFBA5D0101E5ECBAF6F4FEA3CD2706B459650D9089F)
BBA6C4C6CF1611C8717A59ABE5002646647C2356CD1D7F0814204BC646CFA882
//H(DAC75A25D3FCA8F8905FB5079C08FE8B470BD32BE22D50424660DE311DE83598||00000003||A7F359B46855C48A1B672F1C65FB57A8F5581AD4A96AA0637C4D36F44BCDFBA5D0101E5ECBAF6F4FEA3CD2706B459650D9089F)
85D971DFDFEE9A627E34AF27BE75B947B7C1C0EABB024CB2AD2F5A4F8050776D

B56AD8D7ECA977DA795245B84FC00ACB17914160DFDD0D2E75A2FDB6835458F4BBA6C4C6CF1611C8717A59 ABE5002646647C2356CD1D7F0814204BC646CFA88285D971DFDFEE9A627E34AF27BE75B947B7C1C0EABB024CB2AD2F5A4F8050776D

//Enc_key = MBS(KDF,sizeof(plain)=43)
B56AD8D7ECA977DA795245B84FC00ACB17914160DFDD0D2E75A2FDB6835458F4BBA6C4C6CF1611C8717A59

//Cipher = (MBS(KDF,sizeof(plain)=43)) XOR Plain
B56AD8D7ECA977DA795245B84FC00ACB17914160DFDD0D2E75A2FDB6835458F4BBA6C4C6CF1611C8717A59
33333320717569636B2062726F776E20666F78206A756D7073206F76657220746865206C617A7944444444
=8659EBF79DDC1EB9127227CA20B764EB71FE3940B5A8605E068292C0E6267880D3C3E4AAAE6C688C353E1D


//mac_key = MBS(KDF+sizeof(plain)=43, sizepf(mac_key_byte)=32)
ABE5002646647C2356CD1D7F0814204BC646CFA88285D971DFDFEE9A627E34AF
//Mac=Hmac(mac_key,Cipher||ShareInfo2=NULL),hash_alg=SM3
=(ABE5002646647C2356CD1D7F0814204BC646CFA88285D971DFDFEE9A627E34AF, 8659EBF79DDC1EB9127227CA20B764EB71FE3940B5A8605E068292C0E6267880D3C3E4AAAE6C688C353E1D)
=CDF90807DCB66D2E881EB76321239DC2DA45A031988EFCCD188E2EB440D44E4E



==========only with s2==========
//Compute Hash_i = H(Z||counter||Shareinfo1), counter=00000001, Hash_alg=SM3
//KDF
//H(DAC75A25D3FCA8F8905FB5079C08FE8B470BD32BE22D50424660DE311DE83598||00000001)
443DC3B378B234CB4FA82731B21313513FC663B2F8FAD076D2CA6551EA97CF3C
//H(DAC75A25D3FCA8F8905FB5079C08FE8B470BD32BE22D50424660DE311DE83598||00000002)
76A7FC64D6351048F8CCEB32790BB50A65404560C0DA6131D1DAFCB572BE8B33
//H(DAC75A25D3FCA8F8905FB5079C08FE8B470BD32BE22D50424660DE311DE83598||00000003)
3AEB1DD3739DCA104822DB08E9BAF59560ED71D10B2FFF1609C973DDBF8A9E9A

443DC3B378B234CB4FA82731B21313513FC663B2F8FAD076D2CA6551EA97CF3C76A7FC64D6351048F8CCEB 32790BB50A65404560C0DA6131D1DAFCB572BE8B333AEB1DD3739DCA104822DB08E9BAF59560ED71D10B2FFF1609C973DDBF8A9E9A

//Enc_key = MBS(KDF,sizeof(plain)=43)
443DC3B378B234CB4FA82731B21313513FC663B2F8FAD076D2CA6551EA97CF3C76A7FC64D6351048F8CCEB

//Cipher = (MBS(KDF,sizeof(plain)=43)) XOR Plain
443DC3B378B234CB4FA82731B21313513FC663B2F8FAD076D2CA6551EA97CF3C76A7FC64D6351048F8CCEB ^
33333320717569636B2062726F776E20666F78206A756D7073206F76657220746865206C617A7944444444
=770EF09309C75DA824884543DD647D7159A91B92928FBD06A1EA0A278FE5EF481EC2DC08B74F690CBC88AF


//mac_key = MBS(KDF+sizeof(plain)=43, sizepf(mac_key_byte)=32)
32790BB50A65404560C0DA6131D1DAFCB572BE8B333AEB1DD3739DCA104822DB
//Mac=Hmac(mac_key,Cipher||ShareInfo2),hash_alg=SM3
=(32790BB50A65404560C0DA6131D1DAFCB572BE8B333AEB1DD3739DCA104822DB, 770EF09309C75DA824884543DD647D7159A91B92928FBD06A1EA0A278FE5EF481EC2DC08B74F690CBC88AF||6162636465)
=65A9D394BC99BBF6FCDFA0A95EBA059497967EB653A7549625A039714CFD7BED


==========with s1 and s2==========

//Compute Hash_i = H(Z||counter||Shareinfo), counter=00000001, Hash_alg=SM3
//KDF
//H(DAC75A25D3FCA8F8905FB5079C08FE8B470BD32BE22D50424660DE311DE83598||00000001||A7F359B46855C48A1B672F1C65FB57A8F5581AD4A96AA0637C4D36F44BCDFBA5D0101E5ECBAF6F4FEA3CD2706B459650D9089F)
B56AD8D7ECA977DA795245B84FC00ACB17914160DFDD0D2E75A2FDB6835458F4
//H(DAC75A25D3FCA8F8905FB5079C08FE8B470BD32BE22D50424660DE311DE83598||00000002||A7F359B46855C48A1B672F1C65FB57A8F5581AD4A96AA0637C4D36F44BCDFBA5D0101E5ECBAF6F4FEA3CD2706B459650D9089F)
BBA6C4C6CF1611C8717A59ABE5002646647C2356CD1D7F0814204BC646CFA882
//H(DAC75A25D3FCA8F8905FB5079C08FE8B470BD32BE22D50424660DE311DE83598||00000003||A7F359B46855C48A1B672F1C65FB57A8F5581AD4A96AA0637C4D36F44BCDFBA5D0101E5ECBAF6F4FEA3CD2706B459650D9089F)
85D971DFDFEE9A627E34AF27BE75B947B7C1C0EABB024CB2AD2F5A4F8050776D

B56AD8D7ECA977DA795245B84FC00ACB17914160DFDD0D2E75A2FDB6835458F4BBA6C4C6CF1611C8717A59 ABE5002646647C2356CD1D7F0814204BC646CFA88285D971DFDFEE9A627E34AF27BE75B947B7C1C0EABB024CB2AD2F5A4F8050776D

//Enc_key = MBS(KDF,sizeof(plain)=43)
B56AD8D7ECA977DA795245B84FC00ACB17914160DFDD0D2E75A2FDB6835458F4BBA6C4C6CF1611C8717A59

//Cipher = (MBS(KDF,sizeof(plain)=43)) XOR Plain
B56AD8D7ECA977DA795245B84FC00ACB17914160DFDD0D2E75A2FDB6835458F4BBA6C4C6CF1611C8717A59
33333320717569636B2062726F776E20666F78206A756D7073206F76657220746865206C617A7944444444
=8659EBF79DDC1EB9127227CA20B764EB71FE3940B5A8605E068292C0E6267880D3C3E4AAAE6C688C353E1D


//mac_key = MBS(KDF+sizeof(plain)=43, sizepf(mac_key_byte)=32)
ABE5002646647C2356CD1D7F0814204BC646CFA88285D971DFDFEE9A627E34AF
//Mac=Hmac(mac_key,Cipher||ShareInfo2=NULL),hash_alg=SM3
=(ABE5002646647C2356CD1D7F0814204BC646CFA88285D971DFDFEE9A627E34AF, 8659EBF79DDC1EB9127227CA20B764EB71FE3940B5A8605E068292C0E6267880D3C3E4AAAE6C688C353E1D||6162636465)
=909E14F4170F8E6BAF56590EF25E9435A926A7C6FEC6C414B3CBB59174C6B243

Ed25519

参考文档《RFC 8032》

Ed25519属于EdDSA签名算法一种,后者使用的椭圆曲线是Edwards curve,即它是DSA签名算法的Edwards curve版本,这种类型的椭圆曲线方程如下:

ax^2 + y^2 = 1 + d * x^2 * y^2 --twisted Edwards曲线,当a=1时,退化为普通Edwards曲线

Curve25519曲线(Ed25519椭圆曲线):y2 = x3 + 486662x2+x,由素数2255-19定义素数域

这里Ed25519使用的曲线各参数如下:

p=0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED  //p=2^255-19
a=-1
d=0x52036CEE2B6FFE738CC740797779E89800700A4D4141D8AB75EB4DCA135978A3
Gx=0x216936D3CD6E53FEC0A4E231FDD6DC5C692CC7609525A7B2C9562D608F25D51A
Gy=0x6666666666666666666666666666666666666666666666666666666666666658
n=0x1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED
点(Gx, Gy)是曲线的基点,其阶是素数n。

《RFC 8032》
Ed25519签名系统有11个参数
1)一个奇素数p,椭圆曲线建立在有限域GF(p)上,如Ed25519,p=2^255-19  //得p是256bit
2)一个正整数b,满足2^(b-1) > p  //公钥是b比特,对于Ed25519,b=256
3)有限域GF(p)中的元素用(b-1)比特表示,对Ed25519,元素用255个比特表示
4)一个密码学hash函数H,具有2b比特输出,对Ed25519,H=sha512
5)一个整数c,取值2或3,秘密的EdDSA标量应该是2c倍数,c是余因子的底数为2的对数,对Ed25519,c是3
6)一个整数n,满足c<=n<=b,秘密的EdDSA应该有n+1个bit,且其最高比特总是1,最低c比特总是0,如Ed25519,n即254
7)有限域GF(p)中的一个非平方元素d,通常建议取离0最近的,且能确定一个可接受的曲线的值,如对Ed25519,d=-121665/12666
8)有限域GF(p)中的一个非0元素a,考虑到性能要求,通常建议a=-1 if p mod 4=1,and a=1 if p mod 4 = 3,如Ed25519,a=-1
9)曲线上一点B != (0,1)
10)点B的阶L,是个奇素数,且曲线的阶#E = L * 2^c
11)一个预计算hash函数PH

编码
1.一个整数0<S<L-1,被编码作一个小端的b比特的串ENC(S),从左到右,s0s1...sn,s0是低位,sn是高位,用字节串表示时,和x25519中一致
2.一个群元素,即曲线上一个点(x,y),被编码作一个b比特的串ENC(x,y),其中b-1比特是y的编码,之后的1比特表示x,具体的,该bit为1,则表示x是负的,若为0,则表示x是非负的
3.GF(p)中的负元素定义如下:x和-x都用这里的编码表示时,按从左到右比较大小,若x>-x mod p,则x是负的。等价说,若x数值是奇数,则为负数,反之为非负

b=256
encoding of GF(p) 255-bit little endian encoding of {0,1,...,p-1}
H(x)=SHA512(dom2(phflag, context) || x)

Ed25519密钥生成

从小端方式看,如果最低位1bit不为0,则公钥最高bit写1

1.私钥是一个b比特的串k
2.H(k)=h0h1..h62h63(字节串)
3.s为截取H(k)的前n个bit,然后将前c个bit改为0,再在最后添加一个比特1构成的一个比特大数
4.A=[s]B
5.公钥为Encode(A) //The public key A is the encoding of the point [s]B

Ed25519签名

Input:privkey(32byte),M(任意字节,包括null),Ed25519ctx--0 / Ed25519ph--1
Output:(R,S)--64byte

pureEd25519

1) dom2(flag, ctx)=NULL
2) PH(M) = M

Ed25519_ph

1) dom2(flag=1, ctx)="SigEd25519 no Ed25519 collisions"||1||octet(OLEN(ctx))--1byte||ctx
2) PH(M) = sha512(M)

Ed25519_ctx

1) dom2(flag=0,ctx)="SigEd25519 no Ed25519 collisions"||0||octet(OLEN(ctx))--1byte||ctx
2PH(M) = M

以上三种模式,M可以为空,对于ctx模式,ctx长度为[1,255],对于ph模式,ctx长度为[0,255]

1.h=sha512(privkey)=h0h1,...h63,字节串
2.prefix = h[32]||...||h[63],s=h[0]||...||h[31]
3.r =sha512(dom2(flag, ctx) || prefix || PH(M)), r理解为小端
4.R = [r mod n]G
5.k = sha512(dom2(flag, ctx) || R || A ||PH(M)),结果理解为小端
6.S = (r + (k mod n) * (s mod n)) mod L
7.return (R,S) //S最后一字节的最高有效bit永远是0

驱动优化的地方:
1.启动R = [r mod n]G计算后,直接计算sha512(dom2(flag, ctx)
2. 

1.dom2(x,y) = "SigEd25519 no Ed25519 collisions" || octet(x) || octet(OLEN(y)) || y
其中,x∈[0,255],OLEN(y)<=255,"SigEd25519 no Ed25519 collisions" occupy 32 bytes
2.PH是身份函数,例如PH(M)=SHA512(M)

//Demo ed25519-pure-sign
//privkey
F5E5767CF153319517630F226876B86C8160CC583BC013744C6BF255F5CC0EE5
//message
08B8B2B733424243760FE426A4B54908632110A66C2F6591EABD3345E3E4EB98FA6E264BF09EFE12EE50F8F54E9F77B1E355F6C50544E23FB1433DDF73BE84D879DE7C0046DC4996D9E773F4BC9EFE5738829ADB26C81B37C93A1B270B20329D658675FC6EA534E0810A4432826BF58C941EFB65D57A338BBD2E26640F89FFBC1A858EFCB8550EE3A5E1998BD177E93A7363C344FE6B199EE5D02E82D522C4FEBA15452F80288A821A579116EC6DAD2B3B310DA903401AA62100AB5D1A36553E06203B33890CC9B832F79EF80560CCB9A39CE767967ED628C6AD573CB116DBEFEFD75499DA96BD68A8A97B928A8BBC103B6621FCDE2BECA1231D206BE6CD9EC7AFF6F6C94FCD7204ED3455C68C83F4A41DA4AF2B74EF5C53F1D8AC70BDCB7ED185CE81BD84359D44254D95629E9855A94A7C1958D1F8ADA5D0532ED8A5AA3FB2D17BA70EB6248E594E1A2297ACBBB39D502F1A8C6EB6F1CE22B3DE1A1F40CC24554119A831A9AAD6079CAD88425DE6BDE1A9187EBB6092CF67BF2B13FD65F27088D78B7E883C8759D2C4F5C65ADB7553878AD575F9FAD878E80A0C9BA63BCBCC2732E69485BBC9C90BFBD62481D9089BECCF80CFE2DF16A2CF65BD92DD597B0707E0917AF48BBB75FED413D238F5555A7A569D80C3414A8D0859DC65A46128BAB27AF87A71314F318C782B23EBFE808B82B0CE26401D2E22F04D83D1255DC51ADDD3B75A2B1AE0784504DF543AF8969BE3EA7082FF7FC9888C144DA2AF58429EC96031DBCAD3DAD9AF0DCBAAAF268CB8FCFFEAD94F3C7CA495E056A9B47ACDB751FB73E666C6C655ADE8297297D07AD1BA5E43F1BCA32301651339E22904CC8C42F58C30C04AAFDB038DDA0847DD988DCDA6F3BFD15C4B4C4525004AA06EEFF8CA61783AACEC57FB3D1F92B0FE2FD1A85F6724517B65E614AD6808D6F6EE34DFF7310FDC82AEBFD904B01E1DC54B2927094B2DB68D6F903B68401ADEBF5A7E08D78FF4EF5D63653A65040CF9BFD4ACA7984A74D37145986780FC0B16AC451649DE6188A7DBDF191F64B5FC5E2AB47B57F7F7276CD419C17A3CA8E1B939AE49E488ACBA6B965610B5480109C8B17B80E1B7B750DFC7598D5D5011FD2DCC5600A32EF5B52A1ECC820E308AA342721AAC0943BF6686B64B2579376504CCC493D97E6AED3FB0F9CD71A43DD497F01F17C0E2CB3797AA2A2F256656168E6C496AFC5FB93246F6B1116398A346F1A641F3B041E989F7914F90CC2C7FFF357876E506B50D334BA77C225BC307BA537152F3F1610E4EAFE595F6D9D90D11FAA933A15EF1369546868A7F3A45A96768D40FD9D03412C091C6315CF4FDE7CB68606937380DB2EAAA707B4C4185C32EDDCDD306705E4DC1FFC872EEEE475A64DFAC86ABA41C0618983F8741C5EF68D3A101E8A3B8CAC60C905C15FC910840B94C00A0B9D0
//h
h=sha512(F5E5767CF153319517630F226876B86C8160CC583BC013744C6BF255F5CC0EE5)
=609C888D0EF886D34B6EDEF27FB244AA04B3A49C82260E6AB86E784FF488CCC8||
6990F0F6BEFEB2651705E7F5F6CF2F13C42005C7CA8419B1FD050616F902D258
//对h前面32B进行解码,注意s被更新
out[0] &= 0xF8;            //clear lowest 3 bits
out[bytes - 1] &= 0x7F;    //clear highest 1 bit
out[bytes - 1] |= 0x40;    //set second highest bit as 1
h=sha512(F5E5767CF153319517630F226876B86C8160CC583BC013744C6BF255F5CC0EE5)
=609C888D0EF886D34B6EDEF27FB244AA04B3A49C82260E6AB86E784FF488CC48||
6990F0F6BEFEB2651705E7F5F6CF2F13C42005C7CA8419B1FD050616F902D258
//prefix取低32Byte
prefix =6990F0F6BEFEB2651705E7F5F6CF2F13C42005C7CA8419B1FD050616F902D258
//r =sha512(dom2(flag, ctx) || prefix || PH(M))
r=sha512(NULL||NULL||6990F0F6BEFEB2651705E7F5F6CF2F13C42005C7CA8419B1FD050616F902D258||08B8B2B733424243760FE426A4B54908632110A66C2F6591EABD3345E3E4EB98FA6E264BF09EFE12EE50F8F54E9F77B1E355F6C50544E23FB1433DDF73BE84D879DE7C0046DC4996D9E773F4BC9EFE5738829ADB26C81B37C93A1B270B20329D658675FC6EA534E0810A4432826BF58C941EFB65D57A338BBD2E26640F89FFBC1A858EFCB8550EE3A5E1998BD177E93A7363C344FE6B199EE5D02E82D522C4FEBA15452F80288A821A579116EC6DAD2B3B310DA903401AA62100AB5D1A36553E06203B33890CC9B832F79EF80560CCB9A39CE767967ED628C6AD573CB116DBEFEFD75499DA96BD68A8A97B928A8BBC103B6621FCDE2BECA1231D206BE6CD9EC7AFF6F6C94FCD7204ED3455C68C83F4A41DA4AF2B74EF5C53F1D8AC70BDCB7ED185CE81BD84359D44254D95629E9855A94A7C1958D1F8ADA5D0532ED8A5AA3FB2D17BA70EB6248E594E1A2297ACBBB39D502F1A8C6EB6F1CE22B3DE1A1F40CC24554119A831A9AAD6079CAD88425DE6BDE1A9187EBB6092CF67BF2B13FD65F27088D78B7E883C8759D2C4F5C65ADB7553878AD575F9FAD878E80A0C9BA63BCBCC2732E69485BBC9C90BFBD62481D9089BECCF80CFE2DF16A2CF65BD92DD597B0707E0917AF48BBB75FED413D238F5555A7A569D80C3414A8D0859DC65A46128BAB27AF87A71314F318C782B23EBFE808B82B0CE26401D2E22F04D83D1255DC51ADDD3B75A2B1AE0784504DF543AF8969BE3EA7082FF7FC9888C144DA2AF58429EC96031DBCAD3DAD9AF0DCBAAAF268CB8FCFFEAD94F3C7CA495E056A9B47ACDB751FB73E666C6C655ADE8297297D07AD1BA5E43F1BCA32301651339E22904CC8C42F58C30C04AAFDB038DDA0847DD988DCDA6F3BFD15C4B4C4525004AA06EEFF8CA61783AACEC57FB3D1F92B0FE2FD1A85F6724517B65E614AD6808D6F6EE34DFF7310FDC82AEBFD904B01E1DC54B2927094B2DB68D6F903B68401ADEBF5A7E08D78FF4EF5D63653A65040CF9BFD4ACA7984A74D37145986780FC0B16AC451649DE6188A7DBDF191F64B5FC5E2AB47B57F7F7276CD419C17A3CA8E1B939AE49E488ACBA6B965610B5480109C8B17B80E1B7B750DFC7598D5D5011FD2DCC5600A32EF5B52A1ECC820E308AA342721AAC0943BF6686B64B2579376504CCC493D97E6AED3FB0F9CD71A43DD497F01F17C0E2CB3797AA2A2F256656168E6C496AFC5FB93246F6B1116398A346F1A641F3B041E989F7914F90CC2C7FFF357876E506B50D334BA77C225BC307BA537152F3F1610E4EAFE595F6D9D90D11FAA933A15EF1369546868A7F3A45A96768D40FD9D03412C091C6315CF4FDE7CB68606937380DB2EAAA707B4C4185C32EDDCDD306705E4DC1FFC872EEEE475A64DFAC86ABA41C0618983F8741C5EF68D3A101E8A3B8CAC60C905C15FC910840B94C00A0B9D0)
=C191ED996744E0E280359D6BF0C762179F17EBD84EC4D4DFD287A33AAA58FB2D||
9F16171155E4C109E70579E15790E6EFCDEFB8BA66E21D659140D09356E0D609
//R = [r mod n]G 注意此处模运算hash已认为是小端
r mod n
=9D6E05693D04091651DE266BAB8EFCDEFE69057E17905E709C1E4551117169F2DFB58AA3AA387D2DFD4C44ED8EB179F1762C7F06B9D3580E2E0446799ED91C1 mod 1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED
=D5B57538A9A43A6CDC255B8C040ABB4E174635A018C973D7B9DF09B1F613F57 //注意此处r值默认小端输出
R = [r mod n]G
=[39B6CFFBFC21B42CABAA8A646D56465B3C2C7360E7E266FF080D151055D7EA5]G
=0aab4c900501b3e24d7cdf4663326a3a87df5e4843b2cbdb67cbf6e460fec350//目前小工具没有点乘计算,公钥非直接点乘,最终输出的r
//k = sha512(dom2(flag, ctx) || R || A ||PH(M)), A是公钥
k=sha512(NULL||0aab4c900501b3e24d7cdf4663326a3a87df5e4843b2cbdb67cbf6e460fec350||278117FC144C72340F67D0F2316E8386CEFFBF2B2428C9C51FEF7C597F1D426E||08B8B2B733424243760FE426A4B54908632110A66C2F6591EABD3345E3E4EB98FA6E264BF09EFE12EE50F8F54E9F77B1E355F6C50544E23FB1433DDF73BE84D879DE7C0046DC4996D9E773F4BC9EFE5738829ADB26C81B37C93A1B270B20329D658675FC6EA534E0810A4432826BF58C941EFB65D57A338BBD2E26640F89FFBC1A858EFCB8550EE3A5E1998BD177E93A7363C344FE6B199EE5D02E82D522C4FEBA15452F80288A821A579116EC6DAD2B3B310DA903401AA62100AB5D1A36553E06203B33890CC9B832F79EF80560CCB9A39CE767967ED628C6AD573CB116DBEFEFD75499DA96BD68A8A97B928A8BBC103B6621FCDE2BECA1231D206BE6CD9EC7AFF6F6C94FCD7204ED3455C68C83F4A41DA4AF2B74EF5C53F1D8AC70BDCB7ED185CE81BD84359D44254D95629E9855A94A7C1958D1F8ADA5D0532ED8A5AA3FB2D17BA70EB6248E594E1A2297ACBBB39D502F1A8C6EB6F1CE22B3DE1A1F40CC24554119A831A9AAD6079CAD88425DE6BDE1A9187EBB6092CF67BF2B13FD65F27088D78B7E883C8759D2C4F5C65ADB7553878AD575F9FAD878E80A0C9BA63BCBCC2732E69485BBC9C90BFBD62481D9089BECCF80CFE2DF16A2CF65BD92DD597B0707E0917AF48BBB75FED413D238F5555A7A569D80C3414A8D0859DC65A46128BAB27AF87A71314F318C782B23EBFE808B82B0CE26401D2E22F04D83D1255DC51ADDD3B75A2B1AE0784504DF543AF8969BE3EA7082FF7FC9888C144DA2AF58429EC96031DBCAD3DAD9AF0DCBAAAF268CB8FCFFEAD94F3C7CA495E056A9B47ACDB751FB73E666C6C655ADE8297297D07AD1BA5E43F1BCA32301651339E22904CC8C42F58C30C04AAFDB038DDA0847DD988DCDA6F3BFD15C4B4C4525004AA06EEFF8CA61783AACEC57FB3D1F92B0FE2FD1A85F6724517B65E614AD6808D6F6EE34DFF7310FDC82AEBFD904B01E1DC54B2927094B2DB68D6F903B68401ADEBF5A7E08D78FF4EF5D63653A65040CF9BFD4ACA7984A74D37145986780FC0B16AC451649DE6188A7DBDF191F64B5FC5E2AB47B57F7F7276CD419C17A3CA8E1B939AE49E488ACBA6B965610B5480109C8B17B80E1B7B750DFC7598D5D5011FD2DCC5600A32EF5B52A1ECC820E308AA342721AAC0943BF6686B64B2579376504CCC493D97E6AED3FB0F9CD71A43DD497F01F17C0E2CB3797AA2A2F256656168E6C496AFC5FB93246F6B1116398A346F1A641F3B041E989F7914F90CC2C7FFF357876E506B50D334BA77C225BC307BA537152F3F1610E4EAFE595F6D9D90D11FAA933A15EF1369546868A7F3A45A96768D40FD9D03412C091C6315CF4FDE7CB68606937380DB2EAAA707B4C4185C32EDDCDD306705E4DC1FFC872EEEE475A64DFAC86ABA41C0618983F8741C5EF68D3A101E8A3B8CAC60C905C15FC910840B94C00A0B9D0)
=60F83D247242CB144548DDA50BC649CAB8CD2AD24CF46D7D25F13118E4A96F5C49AF3FD726BA59B012B2D2410214FD60B3924F84AC85312514F08F43EC3BD3D1
//k mod n
=D1D33BEC438FF014253185AC844F92B360FD140241D2B212B059BA26D73FAF495C6FA9E41831F1257D6DF44CD22ACDB8CA49C60BA5DD484514CB4272243DF860 mod 1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED
=3A96A76606CEE099A04A61B43317A9FE8A3F37EBA34209DD4C647D06E08A069
//s mod n
=48CC88F44F786EB86A0E26829CA4B304AA44B27FF2DE6E4BD386F80E8D889C60 mod 1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED
=8CC88F44F786EB86A0E26829CA4B30456C8CB0566FFFAF2733D6BA519B14CAC
//k*s mod n
=3A96A76606CEE099A04A61B43317A9FE8A3F37EBA34209DD4C647D06E08A069 *8CC88F44F786EB86A0E26829CA4B30456C8CB0566FFFAF2733D6BA519B14CAC mod n
=62EC0F6E60EBD05D86DC8AF8F8CE3A97F43CD48C555F1C1221401CFEF05E840
//r+k*s mod n
=D5B57538A9A43A6CDC255B8C040ABB4E174635A018C973D7B9DF09B1F613F57 + 62EC0F6E60EBD05D86DC8AF8F8CE3A97F43CD48C555F1C1221401CFEF05E840 mod n
=38A184A70A900ACA6301E684FCD8F5E4BD936C423EAEC28459F8F50B17153AA,最终输出s值,注意直接小端输出
//RS
=0AAB4C900501B3E24D7CDF4663326A3A87DF5E4843B2CBDB67CBF6E460FEC350||
AA5371B1508F9F4528ECEA23C436D94B5E8FCD4F681E30A6AC00A9704A188A03

C25519与X25519

C25519即Curve 25519,它是Montgomery曲线,曲线方程是v^2 = u3+Au2+u,具体的,各个参数是:

   p =  2^255 – 19
   A =  486662
   B =  1
   Gx = 9 //u
   Gy = 14781619447589544791020593568409986887264606134616475288964881837755586237401
   order(G) = 2^252 + 0x14def9dea2f79cd65812631a5cf5d3ed     //素数
   h = 8   //余因子

C25519可以转换为twisted Edwards curve y2-x2=1+dx2y2:

(x, y) = (sqrt(-486664)*u/v, (u-1)/(u+1))

C25519用于构建快速密钥协商算法X25519

X25519其实是ECDH的C25519版本,二者非常相似,区别在于后者的点乘发生了变化,输入点和输出点都只有x坐标,于是公钥只有x坐标,协商使用的点乘也只有输出x坐标。因为去除了y坐标及其计算,于是X25519需要的存储空间更少,性能更高。

X25519的安全级别是O(2128),2017年和X448一起被NIST批准作为美国联邦政府标准算法。

X25519中,充分考虑了当前主流CPU是大端的事实,其大数表示采用字节小端方式,例如,若私钥用字节序列表示是
D8375C6C3E8A4611ED1487D9F2E044277B98A2AD3C44A39D859F92A67096E750
那么其对应的大数是:
0x50E79670A6929F859DA3443CADA2987B2744E0F2D98714ED11468A3E6C5C37D8
这么做的好处是可直接转换为U32类型之间小端表示的大数形式,这是大数处理最常采用的方式,从而省去字节串到大数之间的转换,有利于提升性能。以上面的私钥字节序列表示为例,按U32数组方式去访问它,得到的是:
0x6C5C37D8,0x11468A3E,0xD98714ED,……,0x9DA3443C,0xA6929F85,0x50E79670

椭圆曲线

维尔斯特拉斯曲线(Weierstrass Curve)

y2 = x3 + ax + b

蒙哥马利曲线

By2 = x3 + Ax2 + x,其中A,B∈K且B(A2 - 4)

基于蒙哥马利曲线的Curve25519密钥协商(DH)算法
Curve25519曲线(Ed25519椭圆曲线):y2 = x3 + 486662x2+x,由素数2255-19定义素数域,用于现代加密协议(如TLS1.3和SSH)的椭圆曲线。

爱德华曲线

基于爱德华曲线的Ed25519数字签名算法

爱德华曲线:x2 + y2 = 1 + dx2y2
Ed25519曲线:-x2 + y2 = 1 - 121665/121666x2y2

Curve25519/Ed25519/X25519

Daniel J. Bernstein在2006年独立设计的椭圆曲线加密/签名/密钥交换算法,Ed25519用于签名,X25519密钥交换

优势:
a.快速的签名验签
b.高安全性,等同于NIST P-256,RSA3000,128-block-cipher
c.少的签名值,64bytes
d.少的key,32bytes,私钥长度和公钥长度为32bytes

安全方案参考文档:
《SCA-secure ECC in software-mission.pdf》
《rfc7748.pdf》
《rfc8032.pdf》
《20240903_eucleak.pdf》

1.考虑蒙哥马利阶梯过程中地址泄露
2.考虑单条曲线横向分析

椭圆曲线点加

//计算点(x1,y2)+(x2,y2) = (x3, y3)

x3 = s2 - x1 - x2 mod p
y3 = s(x1 - x3) - y1 mod p

//斜率s
s = (y2 - y1)/(x2 - x1) mod p 当P≠Q (不同点相加)
s = (3x12 + a)/(2y1) mod p 当P=Q(相同点相加)

推导
1.P、Q为不同点的斜率
k = (y2 - y1)/(x2 - x1) ————(2)

2.P、Q为相同点的斜率
对曲线表达式两边求导,得
2yy‘ = 3x2 + a,得
y' = (3x2 + a)/(2y) ————(3)
y'就是曲线上任意一点的切线的斜率

通过点(x1,y1)的直线L(x)可以表示为:
y = k(x - x1) + y1 ————(4)

求(x3,y3)坐标-横坐标x3的计算
将(4)带入(1),消去y,得
(k(x-x1)+y1)2 = x3 + ax + b
k2(x-x1)2 + 2k(x-x1)y1 + y12 = x3 + ax + b
k2x2 - 2k2xx1 + k2x12 + 2kxy - 2kx1y1 + y12 = x3 + ax + b
k2x2 - 2
k2xx1 + k2x12 + 2kxy - 2kx1y1 + y12 - x3 - ax - b = 0 //将等式右边移到左边
-k2x2 + 2*k2xx1 - k2x12 - 2kxy + 2kx1y1 - y12+ x3 + ax + b = 0 //取反
x3 - k2x2 + (a + 2k2x1 - 2ky1)x + b - (k2x12 - 2kx1y1 + y12) = 0 //合并x
————(5)

根据韦达定理(Vieta's Formulas),三次方程ax3 + bx + cx + d = 0的三个根x1,x2,x3有以下关系

x1 + x2 + x3 = -b/a
x1x2 + x1x3 + x2x3 = -c/a
x1x2x3 = -d/a

对应(5),x1 + x2 + x3 = -b/a = k2/1
因此,x3 = k2 - x1 - x2

求(x3,y3)坐标-横坐标y3的计算
方法:通过两点斜率直接计算得到,通过所求R(x3, y3)的镜像点R‘(x3, -y3)

k = (y1 - (-y3))/(x1 - x3),得
y3 = k(x1 - x3) - y1

高效点乘

快速对点P的任意乘法,使用一种称为“double-and-add”方法
1)循环倍点P得到(2P, 4P, 8P, 16P)
2)通过上述组合,得到所需要的任意倍数的P

NIST曲线

ECC NIST P256 = secp256r1
ECC NIST P384 = secp384r1
ECC NIST P521 = secp521r1

ECDLP问题

DLP(离散对数问题):给定基数g和元素x = g^y mod p,求解y
ECDLP椭圆曲线密码学(椭圆曲线离散对数问题):给定G,Q=[k]G,求解k

区别:ECDLP允许使用更小的数

破解:c1 * P + d1 * Q = c2 * P + d2 * Q (Q=[k]P)

大数运算

模运算

/* function: c = a mod b
 * parameters:
 *     a -------------------------- input, integer a
 *     aWordLen ------------------- input, word length of integer
 *     b -------------------------- input, integer b, modulus
 *     b_h ------------------------ input, H parameter of b
 *     b_n0 ----------------------- input, - modulus ^(-1) mod 2^w, here w is 32 actually
 *     bWordLen ------------------- input, word length of integer b and b_h
 *     c -------------------------- output, c = a mod b
 * return: PKE_SUCCESS(success), other(error)
 * caution:
 *     1. b must be odd, and please make sure bWordLen is real word length of b
 *     2. real bit length of a can not be bigger than 2*(real bit length of b), so aWordLen can
 *        not be bigger than 2*bWordLen
 *     3. pleae make sure aWordLen <= 2*OPERAND_MAX_WORD_LEN, bWordLen <= OPERAND_MAX_WORD_LEN
 */
uint32_t pke_mod(const uint32_t *a, uint32_t aWordLen, const uint32_t *b, const uint32_t *b_h, const uint32_t *b_n0, uint32_t bWordLen, uint32_t *c)
//a
11223344||FF4DA38170E229215E01ADF8D4CF5207808C037878FCBB6AFE76B8E3CA92C3B5
//b(a的有效bit长度不能超过b的2倍)
ED17045630B452D89F6A4D4B8873220B4127A63D94DE7179C1F584E1E3095D4F
//a mod b
C8D957C5A8CB5D78409E680B456F77A403961DB4A9FC3D87EC893603367D5C5A

//t2 = a high part mod b
11223344
//t1 = 1000...000 mod b
10000000000000000000000000000000000000000000000000000000000000000-0ED17045630B452D89F6A4D4B8873220B4127A63D94DE7179C1F584E1E3095D4F
=12E8FBA9CF4BAD276095B2B4778CDDF4BED859C26B218E863E0A7B1E1CF6A2B1
//t2 = a_high * t1 mod b = a_high * (1000...000 mod b) mod b
11223344 * 12E8FBA9CF4BAD276095B2B4778CDDF4BED859C26B218E863E0A7B1E1CF6A2B1 mod ED17045630B452D89F6A4D4B8873220B4127A63D94DE7179C1F584E1E3095D4F
=B6A2B89A689D872F8207075DF91347A7C431C079C5DDF396B00802014EF3F5F4
//t1 = a low part mod b
FF4DA38170E229215E01ADF8D4CF5207808C037878FCBB6AFE76B8E3CA92C3B5 mod b
=12369F2B402DD648BE9760AD4C5C2FFC3F645D3AE41E49F13C813401E7896666
//a mod b=t1+t2 mod b
=C8D957C5A8CB5D78409E680B456F77A403961DB4A9FC3D87EC893603367D5C5A

相关文章讲解

https://zhuanlan.zhihu.com/p/36326221【ECC椭圆曲线加密算法:介绍】

https://zhuanlan.zhihu.com/p/44743146【ECC椭圆曲线加密算法:有限域和离散对数】

https://zhuanlan.zhihu.com/p/66794410/【ECC椭圆曲线加密算法:ECDH 和 ECDSA】

PS3 ECDSA破解

破解的原因:PS2000使用固定k值签名
破解方式:两组hash值(z1,z2)和两组签名值(r1,s1)(r2,s2)

1.其中r1==r2,因为k固定
2.(s1-s2)=k^(-1)*(z1+r*dA) - k^(-1)*(z2+r*dA)=k^(-1) * (r1-r2) mod n
3.从2推导出k=(z1-z2) * (s1-s2)^(-1)//这里计算得到k
4.由公式s=k^(-1)*(z+r*dA) 计算出dA //k z r已知
dA=s*k-z) * r^(-1)

椭圆曲线

定义:
{(x,y)∈R^2 | y^2 = x^3 + a*x + b, 4 * a^3 + 27 * b^2 ≠ 0} ∪ {0},此处0是指无穷远点
注:
4 * a^3 + 27 * b^2 ≠ 0限定是为了保证曲线不包含奇点,y^2 = x^3 + a * x + b这个方程称为椭圆曲线的维尔斯特拉斯标准形式(Weierstrass normal form)

在Fp上的椭圆曲线

{(x,y)∈R^2 | y^2 ≡ x^3 + a*x + b, 4 * a^3 + 27 * b^2 ≠ 0(mod p)} ∪ {0}
0仍然是无穷远点,a和b是Fp上的两个整数

计算椭圆曲线的阶

School算法(在整个椭圆曲线上生效,在子群上无效)
椭圆曲线的阶:群的点的数量,如果一个椭圆曲线包含N个点,它的一个子群包含n个点,那么n是N的因子

椭圆曲线算法

1.计算椭圆曲线的阶N
2.选择一个阶为n的子群,n必须是素数且必须是N的因子
3.计算辅因子h=N/n
4.在曲线上选择一个随机的点P
5.计算G=hP
6.如果G是0,那么回到步骤4,否则我们已经找到阶为n和辅因子是h的子群的生成器/基点

蒙哥马利计算

蒙哥马利约减:REDC(x,y)=x * y * R-1

求x * y mod N
1)REDC(x, R2) = xR
2)REDC(y, R2) = yR
3)REDC(xR, yR) = xyR
4)REDC(xyR, 1) = xy

计算约简:TR-1 mod N ≡ T/R mod N
变形:TR-1 mod N ≡ T/R mod N ≡ (T+mN)/R mod N
同时,要使T+mN ≡ 0 mod R,求m
获取:
令N0 ≡ -N-1 mod R
=> -N0 ≡ N-1 mod R
=> -N0N ≡ 1 mod R ----①

令R‘ ≡ R-1 mod N
=>R'R ≡ 1 mod N -----②

由①得:
-N0N + R'R ≡ 1 mod R

T+mN ≡ 0 mod R
=> T(-N0N + R'R)+mN ≡ 0 mod R
=> -TN0N + TR'R + mN ≡ 0 mod R
=> mN ≡ TN0N - TR'R mod R
=> mN ≡TN0N mod R
=> m ≡ TN0 mod R

简单乘法:32560/1000
=((((32 * 0/10) +32 * 6)/10)+32
5)/10

计算ABR-1 mod N = AB/R mod N
//A和B为超大数,也可以直接(AB+ABN0N)/R mod N,本身大数计算,没有计算优势
从低位分组i=0,1,...,ceil(n_bitlen/mgmr_bitlen),开始,原分组:res = res + A * Bi
改成蒙哥马利形式:令T = res + A * Bi => TR-1 = (res + A * Bi)/R
①计算m=TN0 mod R = (res + A * Bi) N0 mod R //取低位
②计算TR-1 = (T + mN)/R = [(res+AB1) + (res+ABi)NN0]/R //右移

椭圆曲线多倍点运算实现

1)二进制展开法:
计算Q=[k]P,将k=knkn-1...k0,kn=1 //最高位是有效位
1)Q=P
2)for j = n-1 to 0:
2.1)Q=[2]Q
b.2)if kj=1 then Q=Q+P
c)return Q

霍纳法则:多项式求值运算中的应用

哈希算法

Hash

算法 分组长度Byte 摘要长度Byte 中间迭代值byte 支持消息最大长度比特 填充方式 参考标准
SM3 64 8*4=32 32 64/32*32 GB/T 32905-2016
MD5 64 4*4=16 16 64/32*32 RFC 1321
SHA1 64 5*4=20 20 64/32*32 NIST SP 800-57
SHA256 64 8*4=32 32 64/32*32 先添加1比特1,再填充k个比特0,使得消息比特长度对512去摸后的余数是448,再填充64位数据代表原始消息的比特长度 FIPS PUB 180-4
FIPS PUB 202
SHA224 64 7*4=28 32 64/32*32 同上 同上
SHA384 32*4=128 12*4=48 64 64/32*32 先添加1比特1,再填充k个比特0,使得消息比特长度对1024去摸后的余数是896,再填充128位数据代表原始消息的比特长度 同上
SHA512 32*4=128 16*4=64 64 128/32*32 同上 同上
SHA512_224 32*4=128 7*4=28 64 128/32*32 同上 同上
SHA512_256 32*4=128 8*4=32 64 128/32*32 同上 同上
SHA3_224 36*4=144 7*4=28 200 64 FIPS PUB 202
NIST SP 800-185
SHA3_256 34*4=136 8*4=32 200 64 同上
SHA3_384 26*4=104 12*4=48 200 64 同上
SHA3_512 18*4=72 16*4=64 200 64 同上
SHAKE128 168 任意长度 200 b=r+c=2000=1744+256 FIPS 202
SHAKE256 同SHA3_256 任意长度 200 b=r+c=2000=1488+512 FIPS 202

注:

  1. 以上hash都有起始IV(迭代值),其中sha3 iv是1600bit的0,摘要结果取最后迭代值最高有效位长度byte
  2. SHAKE(Secure HASH Algorithm and KECCAK),是Keccak哈希函数家族成员,属于可拓展输出函数(XOF,Extendable Output Function),源自NIST SHA-3标准,FIPS 202

在 SHA-3 中,c 表示容量(capacity),b 表示中间状态位宽。具体介绍如下:

  • c(容量):它是 SHA-3 算法海绵结构中的一个重要参数,其长度通常为输出长度的两倍。例如对于 SHA3-256,输出长度是 256 位,那么 c 通常为 512 位。c 的主要作用是增强算法的安全性,防止碰撞等攻击,容量越大,安全性理论上越高。

  • b(中间状态位宽):b=r+c,其中 r 是消息分组长度,也称为比特率。b 代表了轮函数 f 的输入输出位宽,在 SHA-3 标准中,b 的长度固定为 1600 位。无论具体的 SHA-3 变体(如 SHA3-224、SHA3-256 等)或可扩展输出函数(如 SHAKE128、SHAKE256)如何,其内部运算时的状态大小均为 1600 位,即 b 的值是固定的,通过调整 r 和 c 的值来适应不同的安全需求和输出要求。

第一原像攻击(First-Preimage Attack):给定一个哈希值 h,找到任意一个消息 m,使得哈希函数 H(m)=h。
第二原像攻击(Second-Preimage Attack):给定一个消息 m1,找到另一个不同的消息 m2(即 m2 != m1),使得 H(m1)=H(m2)。
碰撞攻击:找任意两个不同消息 m1 != m2 使 H(m1)=H(m2)(无前提的碰撞寻找)

PBKDF

《RFC7914》
《RFC 2898》——已废除
cryptolib pbkdf2:由RFC8018定义的一种基于口令的密钥派生算法

Password-Base Key Derivation Function 基于密码的密钥派生函数

原理:通过一个伪随机函数(例如HMAC函数),把明文和一个盐值作为输入参数,然后重复运算,并最终产生密钥。

//函数定义
DK = PBKDF(PRF, Password, Password_Len, Salt, Salt_Len, iter_num, DKLen)

DK //PBKDF算法产生的密钥
PRF //伪随机数,例如HASH_HMAC,输出长度为hLen结果
Password //原文密码
Salt //用于生成密钥加盐的盐值
iter_num //迭代运算次数
DKLen //期望得到的密钥的长度

具体流程:https://blog.csdn.net/HORHEART/article/details/119968850

公式:
PRF取自hmac
2.DK=T1||T2||...||Tdklen/hlen
3.Ti=F(Password,salt,c,i)
4.F(Password,salt,c,i)=U1⊕U2⊕...⊕Uc
5.Ui=PRF(Password, Salt||INT_32_BE(i))

KDF(ANSI X9-63-2011(R2017))

Key Derivation Function(kdf)

Input:
1.secret value, of bit length at most maxlength-32
2.keydatalen, bit len shall be less than(2^32-1)hashlen
3.(optional) A bit string ShareInfo that consists of some data shared by two entities

the total bit length of Z and ShareInfo must be at most maxhashlen-32

Actions:
1.Initiate a 32-bit, big endian bit string counter as 0x00000001
2.for i=1 to j=integer_up(keydatalen/hashlen):
    2.1 Compute Hash_i = H(Z||counter||Shareinfo)
    2.2 Increment counter
    2.3 Increment i
3. Let HHash_j denote Hash_j if keydatalen/hashlen is an integer, and let it denote the (keydatalen-(hashlen)_j) leftmost bits of Hash_j otherwise
4.Set KeyData = Hash_1||Hash_2||Hash_3||...||Hash_j

Output: The bit string KeyData of length keydatalen bits

X9.63 KDF-SHA256

计算流程示例

[SHA-256]
[shared secret length = 192]
[SharedInfo length = 128]
[key data length = 1024]

COUNT = 0
Z = 22518b10e70f2a3f243810ae3254139efbee04aa57c7af7d
SharedInfo = 75eef81aa3041e33b80971203d2c0c52
    Counter = 00000001
    Hash input 1 = 22518b10e70f2a3f243810ae3254139efbee04aa57c7af7d0000000175eef81aa3041e33b80971203d2c0c52 //Z||Counter||ShareInfo
    K1 = c498af77161cc59f2962b9a713e2b215152d139766ce34a776df11866a69bf2e
    Counter = 00000002
    Hash input 2 = 22518b10e70f2a3f243810ae3254139efbee04aa57c7af7d0000000275eef81aa3041e33b80971203d2c0c52
    K2 = 52a13d9c7c6fc878c50c5ea0bc7b00e0da2447cfd874f6cf92f30d0097111485
    Counter = 00000003
    Hash input 3 = 22518b10e70f2a3f243810ae3254139efbee04aa57c7af7d0000000375eef81aa3041e33b80971203d2c0c52
    K3 = 500c90c3af8b487872d04685d14c8d1dc8d7fa08beb0ce0ababc11f0bd496269
    Counter = 00000004
    Hash input 4 = 22518b10e70f2a3f243810ae3254139efbee04aa57c7af7d0000000475eef81aa3041e33b80971203d2c0c52
    K4 = 142d43525a78e5bc79a17f59676a5706dc54d54d4d1f0bd7e386128ec26afc21
key_data = c498af77161cc59f2962b9a713e2b215152d139766ce34a776df11866a69bf2e52a13d9c7c6fc878c50c5ea0bc7b00e0da2447cfd874f6cf92f30d0097111485500c90c3af8b487872d04685d14c8d1dc8d7fa08beb0ce0ababc11f0bd496269142d43525a78e5bc79a17f59676a5706dc54d54d4d1f0bd7e386128ec26afc21

PBKDF2

由RFC8018定义的一种基于口令的密钥派生算法.

公式:
PRF取自hmac
2.DK=T1||T2||...||Tdklen/hlen
3.Ti=F(Password,salt,c,i) //c为迭代次数,i为块索引号,F为密钥派生使用的方式,在此为hmac
4.F(Password,salt,c,i)=U1⊕U2⊕...⊕Uc //每轮迭代值进行异或,异或c轮
5.其中,U1=PRF(Password, Salt||INT_32_BE(i)) //INT_32_BE占用4字节,即最多2^32-1个分组
6.其中,U2=PRF(Password, U1) ... Uc=PRF(Password, Uc-1)

//demo pbkdf2-sha256-hmac
//key-password-16Byte
2C66FFF7B11A80DEA2F6B40C195D5004
//salt-4Byte
73616C74
//iter-4Byte
1
//keybytes-Byte
32
//pbkdf2-sha256-hmac
2221967a15e14c16e45f6334fcd5058f4698eed1b137a30029b92428c21301e4

//result
=hmac(key,  Salt||INT_32_BE(i))
=hmac(2C66FFF7B11A80DEA2F6B40C195D5004,73616C7400000001)
=2221967A15E14C16E45F6334FCD5058F4698EED1B137A30029B92428C21301E4


//demo pbkdf2-sha256-hmac
//key-password-16Byte
2C66FFF7B11A80DEA2F6B40C195D5004
//salt-4Byte
73616C74
//iter-4Byte
2
//keybytes-Byte
32
//pbkdf2-sha256-hmac
2221967a15e14c16e45f6334fcd5058f4698eed1b137a30029b92428c21301e4

//result
U1=hmac(key,  Salt||INT_32_BE(i))
=hmac(2C66FFF7B11A80DEA2F6B40C195D5004,73616C7400000001)
=2221967A15E14C16E45F6334FCD5058F4698EED1B137A30029B92428C21301E4
U2=hmac(key,  U1)
=hmac(2C66FFF7B11A80DEA2F6B40C195D5004, 2221967A15E14C16E45F6334FCD5058F4698EED1B137A30029B92428C21301E4)
=FF3F55577345ABEC90C5D7B9A151AC0B3E7363CC89FE893E533F172F0C36E4B2

result=U1 xor U2
=2221967A15E14C16E45F6334FCD5058F4698EED1B137A30029B92428C21301E4 xor FF3F55577345ABEC90C5D7B9A151AC0B3E7363CC89FE893E533F172F0C36E4B2
=DD1EC32D66A4E7FA749AB48D5D84A98478EB8D1D38C92A3E7A863307CE25E556

PQC后量子密码

实现安全性的技术

​ IND-CCA-1攻击:攻击者只能在收到挑战密文之前提交选择的密文进行解密查询
​ IND-CCA-2攻击:攻击者在整个攻击过程中都可以提交选择的密文进行解密查询
​ IND-CPA攻击:攻击者可以在任何时间提交选择的明文并获得密文
​ FO变换(Fujisaki-Okamoto):用哈希函数绑定,解密时重新加密验证(ML-KEM、其他后量子KEM)

Kyber

FIPS 203 是美国国家标准与技术研究院(NIST)在2024年8月正式发布的 基于模块格的密钥封装机制标准,是NIST后量子密码标准化项目选出的首批算法之一。其技术基础是 CRYSTALS-Kyber

参数集

可变参数:𝑘、𝜂1、𝜂2、𝑑𝑢和𝑑𝑣
固定参数:𝑛= 256,𝑞= 3329<212

image-20260127192006010

image-20260127192059818

关键参数解释:

  1. n=256: 所有参数集使用相同的环维度,即多项式有256个系数。
  2. q=3329(12比特): 所有参数集使用相同的模数,是一个便于计算的素数(接近2^12)。
  3. k: 模块维度。这是区分不同安全级别的核心参数。它决定了MLWE问题中“模块”的大小。
  4. k=2:公钥、密文由2个“向量”(多项式)组成。
  5. k=3:公钥、密文由3个“向量”(多项式)组成。
  6. k=4:公钥、密文由4个“向量”(多项式)组成。
  7. k 越大,MLWE问题的维度越高,安全性越强,但密钥和密文尺寸也越大。
  8. η (Eta):中心二项分布(CBD)的参数,控制着误差(噪声)的大小。FIPS 203统一使用η=2

核心数学对象与运算

  1. 环:R_q = ℤ_q[X] / (X^n + 1),其中 n=256, q=3329。所有多项式运算在此环上进行(系数模q,多项式模X^256+1)。
  2. 模块:由k个R_q中的多项式组成的向量(或k x k矩阵)。例如,ML-KEM-512的私钥s是一个2维向量(s0, s1),公钥矩阵A是一个2x2的矩阵。
  3. 中心二项分布 (CBD):用于生成保密的私钥向量和误差向量的概率分布。通过计算两个长度为η的随机字节串的汉明重量差来采样。
  4. 位打包与压缩:
  5. 公钥压缩:使用模数q到更少比特(如dq=12比特降到dq=12比特?实际不压缩公钥系数,但压缩密文)的“压缩”和“解压缩”函数,以减少通信开销。
  6. 密文压缩:对于向量u和v,将其系数从模q(12比特)压缩到du=10或dv=4比特等,通过取模和四舍五入实现。解压时引入量化误差,这误差在解密时被设计为可以消除(通过四舍五入到最近倍数)。

辅助函数

​

​ PRF:哈希函数,PRFn(s,b) = SHAKE256(s||b),输出8 * 64 * n1比特,即64*n1字节,其中s占用32字节,n∈{2,3}
​ H:sha3-256 //输出32Byte
​ J:shake256 //输出32Byte
​ G:sha3-512 //输出32+32Byte

​ ByteEncode:pack,将多项式打包成字节流
​ ByteDecode:unpack,将字节流解包成多项式
​ SampleNTT:输入一个 32 字节的种子和两个索引,并输出一个属于集合 𝑇 的伪随机元素
​ Input: byte array 𝐵 ∈ 𝔹34 // a 32-byte seed along with two indices
​ Output: array a^ ∈Z256q //the coefficients of the NTT of a polynomial
​ SamplePolyCBD:输入一个种子,并输出来自分布 D𝜂(𝑅𝑞) 的一个伪随机样本
​ Input: byte array 𝐵 ∈ 𝔹64𝜂
​ Output: array 𝑓 ∈ ℤ256 //the coefficients of the sampled polynomial
​ NTT
​ Input: array 𝑓 ∈ ℤ256 𝑞 //the coefficients of the input polynomial
​ Output: array 𝑓^ ∈ ℤ256 q //the coefficients of the NTT of the input polynomial
​ INTT
​ Input: array 𝑓^ ∈ ℤ256 𝑞 //the coefficients of input NTT representation
​ Output: array 𝑓 ∈ ℤ256 𝑞 //the coefficients of the inverse NTT of the input
​ MultiplyNTTs
​ Input: Two arrays 𝑓^ ∈ ℤ256 𝑞 and g^ ∈ ℤ256 𝑞 //the coefficients of two NTT representations
​ Output: An array ℎ^ ∈ ℤ256 q //the coefficients of the product of the inputs
​ BaseCaseMultiply
​ Input: a0, a1, b0, b1 ∈ Zq // the coefficients of a0 + a1X and b0 + b1X
​ Input: y ∈ Zq // the modulus is X2-y
​ Output: c0,c1 ∈ Zq //the coefficients of the product of the two polynomials

密钥生成

K-PKE.KeyGen

​ 输入:
​ d (32字节随机数)
​ 输出
​ ekpke (384 * k + 32字节)
​ dkpke(384 * k字节)

​ 流程:

image-20260127174747477

​ 矩阵A:从ρ确定性地生成一个k x k的矩阵A,其元素属于环R_q上的多项式(多项式系数是模q的整数,范围[0,3328])。这是一个“伪随机”的公共矩阵

​ 生成私钥和误差:
​ 采样秘密向量 s ← CBD(σ),其中s是一个k维向量(k * 384字节),每个分量是一个多项式,每个多项式由 256 个系数组成,每个系数是 模 q 的整数(但 s 的系数来源于 CBD 分布,范围较小,仍用 12 比特格式编码)
​ 采样误差向量 e ← CBD(σ),与s同理。

ML-KEM.KeyGen_internal

​ 输入:
​ d:32字节随机数
​ z:32字节随机数
​ 输出:
​ ek:加密密钥,384 * k+32字节
​ dk:解密密钥,768 * k+96字节
​ 流程:

image-20260128110736602

​ dk = dkpke||ek||H(ek)||z,其中dkpke解密密钥(s^向量,384 * k字节,k个打包多项式),ek是加密密钥,H(ek)是对ek的sha3-256,z是外部的32字节随机数,一共占用(384 * k)+(384 * k + 32)+32+32=768 * k+96字节

​ ek=ekpke=t^||p, 其中t^是长度为k的向量,每个分量是R_q中的一个打包多项式(256 个系数,每个系数模 q=3329,需要 12 比特,实际采用压缩的紧凑编码),每个系数是 0 ≤ 系数 < 3329(因为 3329 < 2¹²,所以需要 12 比特),占用k*384字节,p是G函数sha3-512对32字节随机数d级联1字节k派生出来的32字节。一共占用384 * k+32字节。

ML-KEM.KeyGen

​ 输入:无
​ 输出:
​ ek:加密密钥,384 * k+32字节
​ dk:解密密钥,768 * k+96字节
​ 流程:

image-20260128114302378

ek=ekpke=t^||p

参数集 k t^ 的字节数 (k×384) ρ 的字节数 总 ek 长度 (字节)
ML-KEM-512 2 2 × 384 = 768 32 800
ML-KEM-768 3 3 × 384 = 1152 32 1184
ML-KEM-1024 4 4 × 384 = 1536 32 1568

dk = dkpke||ek||H(ek)||z,可以看到,解密密钥包含加密密钥ek,用于重新加密验证

参数集 k dkpke 长度 ek长度 H(ek)+z长度 总 sk 长度 (字节)
ML-KEM-512 2 768 800 64 768 + 800 + 64 = 1632
ML-KEM-768 3 1152 1184 64 1152 + 1184 + 64 = 2400
ML-KEM-1024 4 1536 1568 64 1536 + 1568 + 64 = 3168

计算流程优化

​ 在上述K-PKE.KeyGen流程中,可以调整步骤用于节约存储:
​ 1)计算ρ,σ
​ 2)先计算秘密向量s并进行NTT变换得到s^,再进行矩阵A计算,每次计算一个A[i,j]即与s进行计算,无需存储全部的A
​ 3)计算向量误差e并进行NTT运算得到e^
​ 4)计算t = As + e,并完善计算ek dk

封装

K-PKE.Encrypt

​ 输入:
​ pk:加密公钥(384 *k + 32字节)
​ m:明文(32字节)
​ r:随机值(32字节)
​ 输出:
​ c:密文(32 * (du * k + dv)字节) //k+1个多项式,du是压缩向量u保留bit精度,dv是压缩向量v保留bit精度

​ 流程:

image-20260127195115108

​ 生成矩阵A‘:直接从ek加密密钥获取p,生成同样的A(或取其转置,取决于实现,这里其实可以考虑预计算了)
​ 采样加密噪声:
​ 采样误差向量 r ← CBD(r作为种子)
​ 采样误差 e1 ← CBD(r作为种子)(一个k维向量)
​ 采样误差 e2 ← CBD(r作为种子)(一个k维向量)
​ 计算密文:
​ u = Aᵀ ◦ r + e1 (一个k维向量)
​ v = tᵀ ◦ r + e2 + Decompress(Decode(m), 1)。这里m被编码并解压缩到R_q的一个元素,然后加到结果中。tᵀ ◦ r是向量点积
​ 压缩与输出:
​ 将u和v的系数进行压缩(例如,u压缩到10比特,v压缩到4比特),得到密文c = (Compress(u, du), Compress(v, dv))
​ 输出(c, K),其中K = KDF(K̄)是最终的共享密钥

ML-KEM.Encaps_internal

​ 输入:
​ ek:加密密钥,384 * k+32字节
​ m:明文(32字节随机数)
​ 输出:
​ K:共享密钥,32字节
​ c:密文,32 * (du * k+dv),,共32 * (k+1)*384字节
​ 流程:

image-20260128112907647

ML-KEM.Encapsulation

​ 输入:
​ ek:加密密钥,384 * k+32字节
​ 输出:
​ K:共享密钥32字节
​ c:加密密文,32 * (du * k+dv),共32 * (k+1)*384字节
​ 流程:

image-20260128143700708

​ 在执行上述之前,需要对加密密钥进行检查:
​ 1)检查加密密钥长度是否满足384*k+32字节
​ 2)对密钥前384 * k进行unpack再进行pack,查看是否一致

解封装

K-PKE.Decrypt

​ 输入:
​ dkpke :解封装密钥,占用384*k字节
​ c:密文,占用32 * (du * k+dv)字节 //k+1个多项式
​ 输出:
​ m:明文,32字节
​ 流程:

image-20260128105544474

​ 1)解压缩密文:从c恢复u' = Decompress(u, du)和v' = Decompress(v, dv)

ML-KEM.Decaps_internal

​ 输入:
​ dk:解密密钥,768 * k + 96字节
​ c:密文,占用32 * (du * k+dv)字节 //k+1个多项式,每个多项式按d比特压缩
​ 输出:
​ K:共享密钥,32字节
​ 流程:

image-20260128113539933

​ 流程:

​ 1)重新加密验证 (CCA安全的关键):
​ 使用恢复的m'和r‘,和存储的ekpke,完全重新执行一遍封装流程,得到一个新的密文c*和封装的秘密K̄*
​ 比较计算出的c*与接收到的c是否完全一致

ML-KEM.Decapsulation

​ 输入:
​ dk:解封密钥,768*k+96字节
​ c:密文,占用32 * (du * k+dv)字节 //k+1个多项式,每个多项式按d比特压缩
​ 输出:
​ K:协商密钥,32字节
​ 流程:

image-20260128153323118

​ 在解封装之前,需要判断:
​ 1)密文长度,如果c长度不满足32 * (du * k+dv),报错
​ 2)解封密钥,如果长度不满足768*k+96,报错
​ 3)对dk = dkpke||ek||H(ek)||z,计算H(dk[384 * k:784 * k+32])与dk[768 * k+32:768 * k+64]是否等价

​ 输入:密文 c,私钥 sk(包含s, pk, z, h等)
​ 输出:共享密钥 K

​

安全版本运算

密钥生成

ek = ek_PKE
dk = (dk0||dk1||ek||H(ek)||z) //host lib要求客户输入coins 64字节,d||z
Return (ek, dk)

封装

输入: ek,m0, m1 // //host lib要求客户输入coins 32字节,推测是m
输出:K, c

解封装

Input: dk0, dk1, c
Output: K

Dilithium

CRYSTALS-Dilithium 是NIST在2022年选定的后量子数字签名标准之一(与Falcon、SPHINCS+并列),基于格密码学(Lattice-Based Cryptography)。

数学基础

困难问题:模块格上的短整数问题(Module - SIS)
安全参数:
    安全性基于格上最坏情况下的复杂性假设
    3个安全级别(NIST Level 1/3/5对应128/192/256比特经典安全)

密钥结构

​

组件 大小 描述
私钥(sk) - K * L多项式矩阵 ~2.5KB 短向量矩阵+随机种子
公钥(pk) - L * 1 短向量矩阵 ~1.5KB 模块格上的矩阵
签名 ~2.7KB 响应向量+掩码

注:相比RSA-3072(签名=384B),密钥较大但运算更快

性能表现

运算效率(x64 CPU基准)

操作 周期数 对比RSA-3072
签名生成 ~500K 快5x
签名验证 ~200K 快20x
密钥生成 ~100K 快100x

资源占用优势

无大数运算:仅需多项式乘法(适合嵌入式设备)
并行化友好:矩阵运算可SIMD加速(AVX2指令集)

安全性分析

抗量子攻击能力

已知最优攻击需>2128次格规约运算(即使使用量子算法)

侧信道防御

内置对抗措施:

  1. 签名时添加高斯噪声(防定时攻击)
  2. 拒绝采样机制(放信息泄露)

需注意实现:

  1. 多项式乘法需恒定时间实现
  2. 随机数生成必须符合NIST SP 800-90B

Hints 用于标识签名 zz 中需要模约减的系数位置,以确保验证时能正确恢复 w=A⋅z−c⋅t

在 CRYSTALS-Dilithium 算法中,Hint Make(生成提示)和 Use Hint(使用提示)是两个关键步骤,用于优化签名的验证过程

Hint Packing(提示打包)和 Hint Unpacking(提示解包)是为了优化签名大小而设计的关键步骤,主要用于压缩签名中的辅助信息(hints)

LMS

参考标准《RFC8554》

LM-OTS签名

签名用于通过将秘密私钥与共享公钥关联来验证消息的真实性。这些是一次性签名;每个私钥最多只能用于一次对任何给定消息进行签名。

作为签名过程的一部分,使用加密哈希函数H(参见第4.1节)计算原始消息的摘要,并对生成的摘要进行签名。

参数

n:哈希函数输出的字节数
w:Winternitz coefficients的位宽度,即单个 Winternitz 链中使用的哈希值或校验和的位数。它是集合 { 1, 2, 4, 8 } 中的一个成员。参数w确定作为OTS签名一部分计算的Winternitz链的长度(涉及2^w-1次哈希函数调用)
p:构成 LM-OTS 签名的 n 字节字符串元素的数量。它是 n 和 w 的函数;定义参数集的值列于表 1;也可以通过附录 B 中给出的算法计算。
ls:校验和函数Cksm总使用的左移位位数(定义见4.4节)。
H:抗第二原像攻击加密哈希函数,接受任意长度字节,输出固定长度字符串

w的取值对安全几乎没有影响。增加w将缩短签名(切分块=m/w,w增大分块减少),但生成(总哈希调用次数增加,m_bit/w * (2w-1)可以看出)和验证签名的计算量会增加。
p和ls的值取决于参数n和w的选择,表1说明了n、w、p和ls的各种组合以及产生的签名长度

Parameter Set Name H n w p ls u=消息分段组数(推算) v=cks组数(推算) sig_len
LMOTS_SHA256_N32_W1 SHA256 32 1 265 7 256 9 8516
LMOTS_SHA256_N32_W2 SHA256 32 2 133 6 128 5 4292
LMOTS_SHA256_N32_W4 SHA256 32 4 67 4 64 3 2180
LMOTS_SHA256_N32_W8 SHA256 32 8 34 0 32 2 1124
表1

注意:上表v代表checksum的分组数量,每个分组w位宽,从上表可知,无论何种参数,都需要使用2个字节可以表示全部type的Cks

参数ls和p的计算如下:

 u = ceil(8*n/w) //ceil向上取整
 v = ceil((floor(lg((2^w - 1) * u)) + 1) / w) //floor向下取整,lg底数为2
 ls = 16 - (v * w)
 p = u + v
//u:消息所需的w位字段数
//v:校验和字节字符串所需的w位字段数

//以LMOTS_SHA256_N32_W8举例
u = ceil(8*32/8) = 32 //消息被分成32个块,每个块8bit
v = ceil((floor(lg((2^w - 1) * u)) + 1) / w) //v可以理解为checksum数量
  = ceil((floor(lg((2^8 - 1) * 32)) + 1) / 8)
  = ceil((floor(X) + 1) / 8) //X=lg((2^8 - 1) * 32)=lg(255 * 32)=lg(8160) ,12<lg(8160)<13,底数是2,log2(8160) ≈ 12.994
  = ceil((12 + 1) / 8)
  = ceil(13/8)
  = 2
ls = 16 - 2*8 = 0
p = 32+2=34

//sign_len
|sig| = typecode||C||y[1]||...||y[32]
1124 = 4+n+p*n=4+32+p*32=36+34*32=36+1088=1124
私钥

LM-OTS私钥格式是实现的内部问题,本标准不对其进行定义。
==一种可能性是私钥由表示特定LM-OTS算法的类型码、包含p个n字节字符串、16字节字符串l,和4字节字符串q。==
私钥最多对一个消息进行签名

算法0:

  1. 从LM-OTS私钥将与之一起使用的LMS树中检索q和l值(LMS公钥/私钥对的16字节标识符)
  2. 将type设置为算法的typecode
  3. 根据typecode和表1设置n和p
  4. 按如下方式设置数组x:
  5. for(int i=0;i<p;i++) x[i] = rand() //该部分可以使用伪随机计算,但会影响密码强度
  6. 返回u32str(type) || I || u32str(q) || x[0] || x[1] || ... || x[p-1]
//type取值:
enum lmots_algorithm_type {
 lmots_reserved       = 0,
 lmots_sha256_n32_w1  = 1,
 lmots_sha256_n32_w2  = 2,
 lmots_sha256_n32_w4  = 3,
 lmots_sha256_n32_w8  = 4
};

从官方标准cmodel看,LM-OTS私钥生成遵循了附录A规定了LMS私钥的生成

//l为16字节,SEED为m字节,m为H输出字节数,在开始时统一生成
x_q[i] = H(l || u32str(q) || u16str(i) || u8str(0xff) || SEED). //私钥长度固定,为
公钥

LM-OTS公钥通过将函数H迭代应用于x的每个单独元素,进行2^w-1次迭代,然后散列所有结果值,从私钥生成。

算法1:

  1. 将type设置为算法的typecode

  2. 根据类型代码和表1设置整数n、p、w

  3. 根据私钥确定x、l和q

  4. 按如下方式计算字符串k:

C for(i=0;i<p;i++) { tmp = x[i] for(j=0;j<2^w-1;j++) { tmp = H(l || u32str(q) || u16str(i) || u8str(j) || tmp) } y[i] = tmp } K = H(l || u32str(q) || u16str(D_PBLC) || y[0] || ... || y[p-1])

其中,D_PBLC是固定的两字节值0x8080,用于区分此系统中的最后一个散列和其他所有散列

  1. return u32str(type) || I || u32str(q) || K

从公钥值长度看,是个固定值,长度为4+16+4+N=24+N,N为hash输出字节长度

注:公钥是算法1返回的值

校验和

校验和用于确保能够检测到任何操纵现有签名元素的伪造企图。之所以需要此校验和,是因为攻击者可以自由推进任何Winternitz链。也就是说,如果该校验和不存在,则攻击者可以找到每一位都大于有效哈希值的哈希值来替换它(并调整Winternitz链条)。

算法2:

//byte(S,0)是S最左边字节,byte(S,1)是左边开始的第二个字节,byte(S,n-1)是S最右边字节
coef(S, i, w) = (2^w - 1) & (byte(S, floor(i * w / 8)) >> (8 - (w * (i % (8 / w)) + w)) )

sum = 0
for (i=0; i< n*8/w; i++) //n是哈希摘要输出字节数
{
    sum += sum + (2^w -1) - cofe(S, i, w)//S表示输入到该函数的n字节字符串
}
return (sum << 1s) //ls值见表1,sum值总和为16位无符号整数

由于左移位操作,Cksm结果的最右边的位通常为零。由于p的值,这些位在签名生成或验证期间不会使用。

校验和(见第 4.4 节)使用非负整数“sum”计算,其宽度选定为 w 位字段的整数个数,使其能够容纳函数 H(定义见第 4.5 节的签名算法)的总可能应用次数与实际应用次数之差。如果 H 的执行次数为 0,则 sum 为 (2^w - 1) * (8*n/w)。因此,在本文档中,描述了基于 H = SHA256(n = 32 字节)和 w = { 1, 2, 4, 8 } 的签名方法,==sum 变量是一个 16 位非负整数==,适用于 n 和 w 的所有组合。计算使用第 4.1 节中定义并在附录 B 中计算的参数 ls,该参数表示左移运算中使用的位数。

签名生成
通过依次执行以下操作生成消息的LM-OTS签名:将LMS密钥标识符I、LMS叶标识符q、值D_MESG(0x8181)和随机化器C前置到消息(==这里的消息是指原始待签名的消息,任意长度==);计算散列;将哈希的校验和连接到哈希本身;将结果值视为w位值序列;以及使用每个w位值来确定将函数H应用于私钥的对应元素的次数。函数H的输出连接在一起并作为签名返回。此过程的伪代码如下所示。

算法3:

  1. 将type设置为算法的typecode,占用4字节

  2. 根据类型代码和表1设置n、p、w

  3. 根据私钥确定x、l、q

  4. 将C设置为统一随机的n字节字符串

  5. 按如下方式计算数组y:

C //C占用n字节,D_MESG占用2字节 //l是16字节字符串,用于指示此LM-OTS与哪个Merkle树一起使用。当LM-OTS签名系统在N次签名系统之外使用时,值I可用于区分此一次性签名与其他签名;但是,值q必须设置为全零值。 //q占用4字节,表示OTS公钥出现的Merkle树的叶子序号 Q = H(l || u32str(q) || u16str(D_MESG) || C || message) //D_MESG (0x8181) for ( i = 0; i < p; i = i + 1 ) { a = coef(Q || Cksm(Q), i, w) tmp = x[i] = H(l || u32str(q) || u16str(i) || u8str(0xff) || SEED) for ( j = 0; j < a; j = j + 1 ) { tmp = H(l || u32str(q) || u16str(i) || u8str(j) || tmp) } y[i] = tmp }

  1. Return u32str(type) || C || y[0] || ... || y[p-1]

注意,该算法产生的签名的元素是第4.3节中公钥算法计算的元素中间值

签名是算法3返回的字符串。第3.3节将字符串的结构正式定义为lmots_signature联合。

签名验证

为了验证带有签名的消息(n字节字符串的数组,表示为y),接收方必须使用消息散列及其校验和串联产生的字符串的w位系数“完成”H的迭代链。此计算应产生与提供的公钥匹配的值。

算法4a:解析公钥

  1. 如果公钥长度<4个字节,返回INVALID
  2. 从公钥解析pubtype,l、q和k,如下:
  3. )pubtype = strTou32(pubkey前4个字节)
  4. )l = 下一个16字节的公钥
  5. )q = strTou32(公钥的下4字节)
  6. )k = 公钥的下n个字节 // 由此可知,pubkey=pubtype || l || q || k,一共4+16+4+N个字节
  7. 使用算法4b,从签名、消息、pubtype以及从公钥获得的标识符I和q计算公钥候选Kc。如果算法4b返回INVALID,则返回INVALID。
  8. 如果Kc等于k,则返回有效值,否则,返回无效

算法4b:根据签名、消息、签名类型码pubtype和标识符I、q计算公钥候选Kc

  1. 如果签名长度<4字节,则返回INVALID

  2. 从签名中解析sigtype、C和y,如下所示

  3. )sigtype = strTou32(签名的前4个字节)

  4. )如果sigtype不等于pubtype,则返回INVALID
  5. )根据pubtype和表1设置n和p,如签名长度不完全为4+n*(p+1)字节,则返回INVALID
  6. )C=签名的下n个字节
  7. )y[0]=签名的下一个n字节,y[1]=签名的下一个n字节...y[p-1]=签名的下一个n字节 //由此C||y长度=n*(p+1)

  8. 计算Kc

C Q = H(l || u32str(q) || u16str(D_MESG) || C || message) for ( i = 0; i < p; i = i + 1 ) { a = coef(Q || Cksm(Q), i, w) tmp = y[i] for ( j = a; j < 2^w - 1; j = j + 1 ) { tmp = H(l || u32str(q) || u16str(i) || u8str(j) || tmp) } z[i] = tmp } Kc = H(l || u32str(q) || u16str(D_PBLC) || z[0] || z[1] || ... || z[p-1])

LMS性能对比
Parameter Set Name w p h sig_len
(4+4+32+p 32+4+h 32)
Verify hash
(Avg)
Speed
(10Mhz LP)
LMOTS_SHA256_N32_W1 &LMS_SHA256_M32_HX 1 265 [5,10,15,20,25] 8.6K~9.3K 390ms/次
LMOTS_SHA256_N32_W2 & LMS_SHA256_M32_HX 2 133 [5,10,15,20,25] 4.5K~5.1K
LMOTS_SHA256_N32_W4 & LMS_SHA256_M32_HX 4 67 [5,10,15,20,25] 2.3K~3.0K
LMOTS_SHA256_N32_W8 & LMS_SHA256_M32_HX 8 34 [5,10,15,20,25] 1.2K~1.9K

统计LMS计算过程中一次性hash次数

密钥(只计算一次) 签名 签名(平均,预存储) 验签(平均) M树所有节点hash存储 M树叶节点hash存储
W8_H5 2^5 * (34*(1+256))+2^5-1=279647 2^5 * (34*(1+255))+5=278533 1*(34 * 128)=4,352 34*128+5=4357 32 * 32+31 * 32=2016=2KB 32 * 32=1KB
W8_H10 2^10 * (34*(1+255)) + 2^10-1=8913919 2^10 * (34*(1+255))+10=8912906 1*(34 * 128)=4,352 34*128+10=4362 1024 * 32+1023 * 32=65504B=64KB 1024 * 32=32768B=32KB
W8_H15 2^15 * (34*(1+256))+2^15-1=285245439 2^15 * (34*(1+255))+15=285212687 1*(34 * 128)=4,352 34*128+15=4367 32768 * 32+32767 * 32=2097120B=2M 32768 * 32=2097120B=1M
密钥(只计算一次) 签名 签名(平均,预存储) 验签(平均) M树所有节点hash存储 M树叶节点hash存储
W1_H5 2^5 * (265*(1+2))+2^5-1=25471 同左 1*(265 * 0.5)=133 265*0.5+5=138 同W8_H5 同W8_H5
W1_H10 2^10 * (265*(1+2))+2^10-1=815103 同左 1*(265 * 0.5)=133 265*0.5+10=143 同W8_H10 同W8_H10
W1_H15 2^15 * (265*(1+2))+2^15-1=26083327 同左 1*(265 * 0.5)=133 265*0.5+15=148 同W8_H15 同W8_H15
密钥(只计算一次) 签名 签名(平均,预存储) 验签(平均) M树所有节点hash存储 M树叶节点hash存储
W2_H5 2^5 * (133*(1+4))+2^5-1=21311 同左 1*(133 * 1.5)=200 133*1.5+5=215 同W8_H5 同W8_H5
W2_H10 2^10 * (133*(1+4))+2^10-1=681983 同左 1*(133 * 1.5)=200 133*1.5+10=210 同W8_H10 同W8_H10
W2_H15 2^15 * (133*(1+4))+2^15-1=21823487 同左 1*(133 * 1.5)=200 133*1.5+15=215 同W8_H15 同W8_H15
密钥(只计算一次) 签名 签名(平均,预存储) 验签(平均) M树所有节点hash存储 M树叶节点hash存储
W4_H5 2^5 * (67*(1+16))+2^5-1=36479 同左 1*(67 * 7.5)=503 67 * 7.5+5=508 同W8_H5 同W8_H5
W4_H10 2^10 * (67*(1+16))+2^10-1=1167359 同左 1*(67 * 7.5)=503 67 * 7.5+10=513 同W8_H10 同W8_H10
W4_H15 2^15 * (67*(1+16))+2^15-1=37355519 同左 1*(67 * 7.5)=503 67 * 7.5+15=518 同W8_H15 同W8_H15
总结
//签名值表示
sig=type||C||y[0]||..||y[p-1]
//对消息hash预处理,得到Q
Q = H(I||q||8181||C||message)
for ( i = 0; i < p; i = i + 1 )
{
     a = coef(Q || Cksm(Q), i, w) 
     tmp = x[i] //这里x[i]参考私钥表示:x[i]=H(I||u32str(q)||u16str(i)||u8str(0xff)||SEED)
     for ( j = 0; j < a; j = j + 1 ) 
     {
         tmp = H(I || u32str(q) || u16str(i) || u8str(j) || tmp)
     }
     y[i] = tmp
}

//私钥表示(标准没有规定格式)
priv=type||I||q||x[0]||...||x[p-1]
//附录A指定求私钥方式
x[i]=H(I||u32str(q)||u16str(i)||u8str(0xff)||SEED)

//公钥表示
pubkey=type||I||q||K
K=H(I||q||8080||y[0]||...||y[p-1])
for(i=0;i<p;i++)
{
    tmp = x[i]//这里x[i]参考私钥表示:x[i]=H(I||u32str(q)||u16str(i)||u8str(0xff)||SEED)
    for(j=0;j<2^w-1;j++)
    {
        tmp = H(I || u32str(q) || u16str(i) || u8str(j) || tmp)
    }
    y[i] = tmp  
}
K = H(l || u32str(q) || u16str(D_PBLC) || y[0] || ... || y[p-1])

Leighton-Micali Signatures

Leighton-Michali签名(LMS)方法可以对大量但固定数量的消息进行签名。LMS系统使用两个加密组件:一次性签名方法和哈希函数。每个LMS公钥/私钥对与一个完美二叉树相关联,其中每个节点包含一个m字节值,其中m是哈希函数的输出长度。树的每个叶包含LM-OTS公钥/私钥对的公钥值。树的根包含的值是LMS公钥。通过将哈希函数应用于其子节点的值的串联来计算每个内部节点。

树的每个节点都与一个节点编号相关联,这是一个无符号整数,在下面的算法中表示为node_num,计算如下。根节点具有节点编号1;对于节点号N<2<sup>h(其中h是树的高度)</sup>的每个节点,其左子节点号为2*N,而其右子节点号为2*N+1。这样做的结果是树中的每个节点都有一个唯一的节点号,叶子的节点号为2<sup>h</sup>,(2<sup>h</sup>)+1,(2<sup>h</sup>)+2,…(2<sup>h</sup>)+(2<sup>h</sup>)-1。通常,第i级的第j个节点的节点号为2<sup>i</sup>+j。当LMS算法中需要节点数时,可以方便地计算节点数,如这些算法中所述。
由上可知,根节点的编号是1,节点号不包含叶子节点,假设一个满二叉树前序遍历为:ABDECFG
      A
      |
    |   |
    B   C
    |   |
   | | | |
   D E F G
A的编号为1,ABCDEFG的编号依次为1234567,G的编号为7,树高度h为2(从0开始编号)
节点号(不含叶子节点)N<2^2 => 3<4
叶子节点DEFG=4,5,6,7 => 2^2, 2^2+1,2^2+2,2^2+3
比方说第2级(从0级开始)第0个节点号为:2^2+0=4 (D)
LMS系统参数

h:树的高度
m:与每个节点关联的字节数,等于hash摘要长度
H:第二个抗二次原像攻击的加密哈希函数,接受任意长度返回m字节字符串
树总共有2h叶子节点,2h-1非叶节点,一共2h+1-1节点

LMS签名的总体强度由==LM-OTS中使用的哈希函数和LMS系统中使用的哈希函数==中较弱的一个决定。为了将风险降至最低,这两个哈希函数应该相同(以便攻击者无法利用较弱的哈希函数选择)。

Name H m h 非叶节点数 叶节点数
LMS_SHA256_M32_H5 SHA256 32 5 31 32
LMS_SHA256_M32_H10 SHA256 32 10 1023 1024
LMS_SHA256_M32_H15 SHA256 32 15 32767 32768
LMS_SHA256_M32_H20 SHA256 32 20 1048575 1048576
LMS_SHA256_M32_H25 SHA256 32 25 33554431 33554432
表2
LMS私钥
LMS私钥的格式是实现的内部问题,本文档不尝试对其进行定义。一种可能性是,它可能由2<sup>h</sup> LM-OTS私钥的OTS_PRIV[]数组和尚未使用的下一个LM-OTS私钥的叶编号q组成。OTS_PRIV[]的第q个元素是使用带有标识符I,q的算法0生成的。创建LMS私钥时,叶编号q初始化为零(==即q取值范围[0,2<sup>h</sup>-1]==)。程序如下:
结构体
//标准文档没有对其进行定义
//自定义:整个LMS系统的私钥(一次性计算所有私钥)
typedef struct {
    uint32_t lms_type; //lms算法类型
    uint32_t ots_type; //ots算法类型
    //2^h为叶节点数,LM-OTS规定了私钥=ots_type||I||q||x[0]||...||x[p],其中x[i]=H(I||q||u16(i)||ff||seed)
    uint8_t  lm_ots_key[2^h][32];//32为sha256输出字节长度,已经是计算完成好的私钥(q和seed不需要保存,q是从0到2^h-1的遍历) 
    uint32_t q; //尚未使用的叶编号(下一个签名要计算的密钥编号)
    uint8_t I[16]; //LMS私钥的16字节标识符
} lms_sk_t;

//自定义:考虑lm_ots_key存储太大,私钥可以实时计算,根据I, q和seed
//对于整个LMS系统,OTS私钥共2^h个
typedef struct {
    uint32_t lms_type; //lms算法类型
    uint32_t ots_type; //ots算法类型
    uint8_t  seed[16]; //随机化种子,用于实时生成私钥,LM-OTS规定了私钥=ots_type||I||q||x[0]||...||x[p],其中x[i]=H(I||q||u16(i)||ff||seed)
    uint32_t q; //尚未使用的叶编号(下一个签名要计算的密钥编号),q的取值范围[0,2^h-1]
    uint8_t I[16]; //LMS私钥的16字节标识符
} lms_sk_t;
流程

算法5:计算LMS私钥

  1. 根据typecode和表2确定h和m (附录A提到m是hash输出字节数,m字节随机值)

  2. 将l设置为均匀随机的16字节字符串

  3. 计算数组OTS_PRIV如下

```C
for ( q = 0; q < 2^h; q = q + 1) //q代表叶编号
{
OTS_PRIV[q] = LM-OTS private key with identifiers I, q
}

//附录A规定了LMS私钥的生成
//l为16字节,SEED为m字节,m为H输出字节数,在开始时统一生成
x_q[i] = H(I || u32str(q) || u16str(i) || u8str(0xff) || SEED). //私钥长度固定,为32字节
```

LMS私钥可以从秘密值伪随机生成;在这种情况下,秘密值必须至少为m字节长且均匀随机,并且不得用于除生成LMS私钥之外的任何其他目的。如何完成该过程的细节不会影响互操作性;也就是说,公钥验证操作独立于这些细节。附录A提供了用于计算LMS私钥的伪随机方法的示例。

LMS公钥

LMS公钥定义如下,其中我们将与第i个LM-OTS私钥相关联的公钥最终散列值(即在算法1中计算的K值)表示为OTS_PUB_hash[i],i的范围为0到(2h)-1,即非叶子节点数量。LMS公钥/私钥对的每个实例都与一个平衡二叉树相关联,该树的节点索引范围为1到2(h+1)-1,即所有节点包含叶子和非叶子节点的数量。每个节点都与一个m字节字符串相关联。第r个节点的字符串表示为T[r]

结构体
typedef struct {
uint32_t lms_type;   //lms算法类型
uint32_t lm_ots_type;//lm_ots算法类型
uint8_t pk_root[32]; //sha256哈希值
uint8_t I[16];       //LMS公私钥16字节唯一标识符
} scl_lms_pk_t
流程

输入:
p,w:用户指定LM-OTS参数集,取自公钥结构体
h:用户指定LMS参数集,取自公钥结构体
q:LMS中OTS私钥编号,对应Merkle树叶节点,取值范围[0, 2^h-1]
I:LMS公私钥16字节唯一标识符,取自公钥结构体
输出:
pk_root:LMS公钥的hash部分

//即OTS_PUB_hash[i]=K = H(I || u32str(q) || u16str(D_PBLC) || y[0] || ... || y[p-1]) // D_PBLC=0x8080 
for(i=0;i<p;i++)//LMS叶编号
{
    //计算LM-OTS私钥
    tmp[i] = H(I || u32str(q) || u16str(i) || u8str(0xff) || SEED)
    //计算LM-OTS公钥
    for(j=0;j<2^w-1;j++)
    {
        tmp[i] = H(l || u32str(q) || u16str(i) || u8str(j) || tmp[i])
    }
}
K = H(l || u32str(q) || u16str(D_PBLC) || tmp[0] || ... || tmp[p-1])//D_PBLC=0x8080


for(r=2^(h+1)-1;r>0;r--)//r代表所有节点包含叶子节点
{
    if (r >= 2^h)
        //散列LM-OTS公钥
        T[r] = H(I||u32str(r)||u16str(D_LEAF)||ost_pub_hash[r-2^h])//D_LEAF值0x8282
    else
        //合并散列左右节点, 处理的是一个内部节点——也就是说,一个节点有两个子节点,需要合并
        T[r] = H(I||u32str(r)||u16str(D_INTR)||T[2*r]||T[2*r+1])//D_INTR值0x8383
}

//公钥标准格式:u32str(lmstype) || u32str(otstype) || I || T[1]
pk_root = T[1]

LMS公钥可以表示为字节字符串:u32str(type) || u32str(otstype) || I || T[1],一共是4+4+16+32=56字节

流程(存储优化1)

上述存储资源太大,需要保存所有的节点,需要优化,空间复杂度O(2^h)降至O(h)

top=0
uint8_t T_h_stack[25+1][32];
uint32_t T_idx_stack[25+1];

//计算LMS所有叶节点LM-OTS公钥,最原始计算,不考虑并行hash,不考虑资源优化
for(q=0;q<2^h;q++) //LMS叶编号
{
    H_init(ctx0)
    H_update(ctx0, I, 16)
    H_update(ctx0, q, 4)
    H_update(ctx0, D_PBLC, 2)//D_PBLC=0x8080

    for(i=0;i<p;i++)
    {
        //计算LM-OTS私钥
        H_init(ctx1)
        H_update(ctx1, I, 16)
        H_update(ctx1, q, 4)
        H_update(ctx1, i, 2)
        H_update(ctx1, 0xff, 1)
        H_update(ctx1, SEED, 32)
        tmp1 = H_final(ctx1)

        //计算LM-OTS公钥
        for(j=0;j<2^w-1;j++)
        {
            H_init()
            H_update(ctx1, I, 16)
            H_update(ctx1, q, 4)
            H_update(ctx1, i, 2)
            H_update(ctx1, j, 1)
            H_update(ctx1, tmp, 32)
            tmp1 = H_final(ctx1)
        }
        H_update(ctx0, tmp1, 32)
    }

    //LM-OTS公钥
    tmp0 = H_final(ctx0)
    //散列LM-OTS公钥
    r = i + (1<< h) //计算叶节点在Merkel树编号
    tmp0 = H(I||u32str(r)||u16str(D_LEAF)||tmp0)//D_LEAF值0x8282

    //计算LMS根公钥
    if (i != 0)
        top++;

    // 压栈
    T_h_stack[top] = tmp0
    T_idx_stack[top] = i + (1<< h)

    while (top >= 1) //至少存在两个节点,判断是否需要合并
    {
        // 判断top两个节点是否是相邻节点
        if (T_idx_stack[top] == (T_idx_stack[top - 1] + 1))
        {
            //更新父节点编号
            T_idx_stack[top-1] = T_idx_stack[top-1] / 2

            //合并计算hash更新到父节点: H(I||u32str(r)||u16str(D_INTR)||T[2*r]||T[2*r+1])
            r = T_idx_stack[top-1]
            r = ((r << 24) | ((r & 0x0000FF00) << 8) | ((r & 0x00FF0000) >> 8) | (r >> 24));
            H_Init(ctx)
            H_update(ctx, I_buf, 16)
            H_update(ctx, &r, 4)
            H_update(ctx, intr_buf, 2)
            H_update(ctx, T_h_stack[top-1], 32)
            H_update(ctx, T_h_stack[top], 32)
            H_final(ctx, T_h_stack[top - 1])

            //更新top
            top--;
        }
        else
        {
            break;
        }
    }
}

return T_h_stack[0]
流程(存储性能优化)
//pqc ram一些存储
uint32_t  top=0xffffffff;
uint8_t h_stack[25][32]
uint8_t h_tmp0[32]
uint32_t tmp_r

//pqc ram一些存储
hash_ip_num = m+1         //假设可以同时使用m+1个hash IP,chain链并行m条
uint32_t tmp_m            //临时存储
uint8_t sk[m][16+4+2+1+32] //z[i]=I||u32str(q)||u16str(i)||u8str(j)||x

//预处理待hash的m条链数据,I和q,seed存储
for(k=0; k<m; k++)
{
    memcpy(sk, I, 16)
    memcpy(sk+16, q, 4)
    memcpy(sk+23, SEED, 32)
}

//计算LMS所有叶节点LM-OTS公钥
for(q=0;q<2^h;q++) //LMS叶编号
{
    H_init(hw_idx_0)
    H_update(hw_idx_0, I, 16)
    H_update(hw_idx_0, q, 4)
    H_update(hw_idx_0, D_PBLC, 2)//D_PBLC=0x8080

    for(i=0;i<p;;)
    {
        //获取本次并行的chain数量
        if ((p-i) >= m)
        {
            tmp_m = m
            i += m 
        }
        else
        {
            tmp_m = p-i
            i = p
        }

        //并行计算m个LM-OTS私钥
        for(k=0; k<tmp_m; k++)
        {
            sk[k].i = i //在sk中更新i值
            sk[k].j = 0xff //在sk中更新j值
            H_start(hw_idx_k+1, sk[k], 16+4+2+1+32) //启动hash计算
        }

        //等待m个LM-OTS私钥计算完成
        for(k=0; k<tmp_m; k++)
        {
            wait H(hw_idx_k+1)
            sk[k].x = H_get(hw_idx_k)
        }

        //并行计算m个LM-OTS公钥
        for(j=0;j<2^w-1;j++)
        {
            for(k=0; k<tmp_m; k++)
            {
                sk[k].j = j //在sk中更新j值
                H_start(hw_idx_k+1, sk[k], 16+4+2+1+32) //启动hash计算
            }

            //等待m个链hash计算完成
            for(k=0; k<tmp_m; k++)
            {
                wait H(hw_idx_k+1)
                sk[k].x = H_get(hw_idx_k)
            }
        }
        //将m个pk合并计算
        for(k=0; k<tmp_m; k++)
            H_update(hw_idx_0, sk[k].x, 32)

        if ((p-i) >= m)
            i += m 
        else
            i = p
    }

    //获取LM-OTS公钥
    h_tmp0 = H_final(hw_idx_0)
    //散列LM-OTS公钥
    r = i + (1<< h) //计算叶节点在Merkel树编号
    h_tmp0 = H(hw_idx_0, I||u32str(r)||u16str(D_LEAF)||h_tmp0)//D_LEAF值0x8282

    j = i;
    while ((j % 2) == 1) //至少存在两个节点 
    {
        r = (r - 1) / 2;
        j = (j - 1) / 2;
        tmp_r  = ((r << 24) | ((r & 0x0000FF00) << 8) | ((r & 0x00FF0000) >> 8) | (r >> 24));
        H_Init(hw_idx_0);
        H_update(hw_idx_0, I_buf, 16);
        H_update(hw_idx_0, &tmp_r, 4);
        H_update(hw_idx_0, 0x8383, 2);
        H_update(hw_idx_0, h_stack[top], 32);
        H_update(hw_idx_0, node, 32);
        h_tmp0 = H_final(hw_idx_0);
        top--;
    }
    top++;
    memcpy(h_stack[top], node, 32);
}

return h_stack[top]

结构体定义

enum lmots_algorithm_type {
 lmots_reserved       = 0,
 lmots_sha256_n32_w1  = 1,
 lmots_sha256_n32_w2  = 2,
 lmots_sha256_n32_w4  = 3,
 lmots_sha256_n32_w8  = 4
};
struct lms_key_n32 {
    lmots_algorithm_type ots_alg_type;
    opaque I[16];
    opaque K[32];
};

//自定义:这里我理解为私钥和公钥的密钥对结构体定义
struct lms_pubkey {
    uint32_t lms_type; //lms算法类型
    uint32_t ots_type; //ots算法类型
    uint8_t K[32]; //K=H(u32str(type)||u32str(otstype)||I||T[1]),这里的type指lms的type,标准里面指定的type暂时理解为不保存在结构体中,由外部保存
};

结合LM-OTS,合并逻辑

//lms公钥结构体
typedef struct {
uint32_t lms_type;   //lms算法类型
uint32_t lm_ots_type;//lm_ots算法类型
uint8_t pk_root[32]; //sha256哈希值
uint8_t I[16];       //LMS私钥的16字节标识符
} scl_lms_pk_t
//计算LMS所有叶节点LM-OTS公钥,最原始计算,不考虑并行hash,不考虑资源优化
for(q=0;q<2^h;q++) //LMS叶编号
{
    for(i=0;i<p;i++)
    {
        //计算LM-OTS私钥
        tmp[i] = H(I || u32str(q) || u16str(i) || u8str(0xff) || SEED)
        //计算LM-OTS公钥
        for(j=0;j<2^w-1;j++)
        {
            tmp[i] = H(I || u32str(q) || u16str(i) || u8str(j) || tmp[i])
        }
    }
    OTS_PUB_HASH[q] = H(l || u32str(q) || u16str(D_PBLC) || tmp[0] || ... || tmp[p-1])//D_PBLC=0x8080
}

//计算LMS根公钥,最原始计算,不考虑并行hash,不考虑资源优化
for(r=2^h-1;r>0;r--)
{
    if (r >= 2^h)
        //处理一个叶节点(因此仅散列一个LM-OTS公钥),可以看出D_LEAF和D_INTR区别
        T[r] = H(I||u32str(r)||u16str(D_LEAF)||OTS_PUB_HASH[r-2^h])//D_LEAF值0x8282
    else
        //处理的是一个内部节点——也就是说,一个节点有两个子节点,需要合并
        T[r] = H(I||u32str(r)||u16str(D_INTR)||T[2*r]||T[2*r+1])//D_INTR值0x8383
}

//公钥标准格式:u32str(lmstype) || u32str(otstype) || I || T[1]
pk_root = T[1]
LMS签名

与LM-OTS签名关联的叶的编号q,作为网络字节顺序的四字节无符号整数,一个LM-OTS签名,typecode表示特定LMS算法类型代码,一个数组包含h、m字节值,与从LM-OTS签名关联的叶到根的树路径关联

从象征意义上讲,签名可以表示为u32str(q) || lmots_signature || u32str(type) || path[0] || path[1] || path[2] || ... || path[h-1],==注意,path[0]是叶子节点层==
其中:lmots_signature = u32str(type) || C || y[0] || ... || y[p-1] (实际应用中可能不需要type,需要在官方c model确认)

由上可知,签名值的长度为:4 +(32+32 * p)+ 4 + (32 * h)

结构体

签名流程使用scl_lms_sig_t和消息

typedef struct {
uint8_t sig[LMS_SIG_MAX_LEN]; //签名值
uint32_t siglen; //签名值长度,取决于参数集lms_ots的p和lms的h
} scl_lms_sig_t

//合并私钥结构体
//自定义:考虑lm_ots_key存储太大,私钥可以实时计算,根据I, q和seed
//对于整个LMS系统,OTS私钥共2^h个
struct lms_privkey {
    uint32_t lms_type; //lms算法类型
    uint32_t ots_type; //ots算法类型
    uint8_t  seed[16]; //随机化种子,用于实时生成私钥,LM-OTS规定了私钥=ots_type||I||q||x[0]||...||x[p],其中x[i]=H(I||q||u16(i)||ff||seed)
    uint32_t q; //尚未使用的叶编号(下一个签名要计算的密钥编号),q的取值范围[0,2^h-1]
    uint8_t  I[16]; //LMS私钥的16字节标识符
    uint8_t  *msg;
    uint8_t  *msg_len;
};

//在标准3.3章节中,正式将lmots_signature做了定义,占用的范围参考定义对应的lmots_signature_nXX_pXX
   enum lmots_algorithm_type {
     lmots_reserved       = 0,
     lmots_sha256_n32_w1  = 1,
     lmots_sha256_n32_w2  = 2,
     lmots_sha256_n32_w4  = 3,
     lmots_sha256_n32_w8  = 4
   };

   struct lmots_signature_n32_p265 {
     bytestring32 C;
     bytestring32 y[265]; //y的范围即p的值,p的值由lmots_algorithm_type决定参数值
   };

   struct lmots_signature_n32_p133 {
     bytestring32 C;
     bytestring32 y[133];
   };

   struct lmots_signature_n32_p133 {
     bytestring32 C;
     bytestring32 y[133];
   };

   struct lmots_signature_n32_p67 {
     bytestring32 C;
     bytestring32 y[67];
   };

   struct lmots_signature_n32_p67 {
     bytestring32 C;
     bytestring32 y[67];
   };

   struct lmots_signature_n32_p34 {
     bytestring32 C;
     bytestring32 y[34];
   };

   struct lmots_signature_n32_p34 {
     bytestring32 C;
     bytestring32 y[34];
   };

   union lmots_signature switch (lmots_algorithm_type type) {
    case lmots_sha256_n32_w1:
      lmots_signature_n32_p265 sig_n32_p265;
    case lmots_sha256_n32_w2:
      lmots_signature_n32_p133 sig_n32_p133;
    case lmots_sha256_n32_w4:
      lmots_signature_n32_p67  sig_n32_p67;
    case lmots_sha256_n32_w8:
      lmots_signature_n32_p34  sig_n32_p34;
    default:
      void;   /* error condition */
   };
流程

要使用LMS私钥计算消息的LMS签名,签名者首先使用下一个未使用的LM-OTS私钥的叶编号计算消息的LM-OTS签名。签名中的叶编号q设置为签名中使用的LMS私钥的叶编号。在释放签名之前,必须增加LMS私钥中的叶编号q,以防止再次使用LM-OTS私钥

从象征意义上讲,签名可以表示为u32str(q) || lmots_signature || u32str(type) || path[0] || path[1] || path[2] || ... || path[h-1]
其中:lmots_signature = u32str(type) || C || y[0] || ... || y[p-1] (实际应用中可能不需要type,需要在官方c model确认)

由上可知,签名值的长度为:4 +(32+32 * p)+ 4 + (32 * h)

                                Root
                                 |
                 ---------------------------------
                 |                               |
               T[2]*                          T[3]**
                 |                               |
          ------------------            -----------------
          |                |            |               |
       T[4]**           T[5]*         T[6]            T[7]
          |                |            |               |
      ---------       ----------     --------       ---------
      |       |       |        |     |      |       |       |
     leaf    leaf    OTS  leaf**   leaf   leaf    leaf    leaf

上面是h=3,q=2的身份验证路径的简单示例。

  1. 标记为OTS的叶子节点是用于对实际消息的一次性签名
  2. path[0]是与OTS公钥(叶节点q)相邻的叶节点(路径起点),如图leaf**
  3. path[1]是路径上第二个节点T[5]的同级节点T[4]*节点,即与叶节点q的父节点相邻
  4. path[2]是路径上第三个节点T[2]的同级节点T[3]*节点

验证思想:。验证器所做的是重新计算路径上的散列;首先,它散列给定的OTS和路径[0]值,给出一个暂定的T[5]值。然后,它散列其路径[1]和暂定T[5]'值,以获得暂定T[2]'值。然后,它将其与路径[2]值散列,以获得一个暂定的根值。如果该值是Merkle树的已知公钥,那么我们可以假设它得到的值T[2]是原始树中正确的T[2]值,因此它得到的值T[5]是原始树中正确的T[5]值,因此OTS公钥与原始树中相同,因此是正确的。

生成LMS签名流程:

  1. 将type设置为LMS算法的typecode

  2. 根据表2,从类型代码中提取h

  3. 为消息创建LM-OTS签名 //lmots_signature = u32str(type) || C || y[0] || ... || y[p-1]

  4. 按如下方式计算数组路径

```C
i = 0
r = 2^h + q //获取叶节点在整个M树的编号[1, 2^(h+1)-1]
while (i < h)
{
temp = (r / 2^i) xor 1 //节点编号/2,第0比特取反,就是该节点的父节点的相邻节点
path[i] = T[temp]
i = i + 1
}

//优化一下r
i = 1
r = 2^h + q //获取叶节点在整个M树的编号[1, 2^(h+1)-1]
path[0] = T[r xor 1]
while (i < h)
{
r = (r / 2) xor 1 //节点编号/2,第0比特取反,就是该节点的父节点的相邻节点
path[i] = T[r]
i = i + 1
}
```

  1. S = u32str(q) || ots_signature || u32str(type) || path[0] || path[1] || ... || path[h-1],其中ots_signature为u32str(type) || C || y[0] || ... || y[p-1]

  2. q=q+1

  3. return S

LMS签名验证
结构体

使用公钥和签名值验证

流程

输入:
q:LMS中OTS私钥编号,取值范围[0, 2^h-1],取自签名值
p, w:LM-OTS参数集指定,取自公钥结构体
I:LMS公私钥16字节唯一标识符,取自公钥结构体
C:32字节随机值,取自签名值lmots_signature部分
T[1]:LMS公钥,32字节hash值,取自公钥结构体
message:外部用户指定的待验签的原始消息
输出:
验签结果,成功或失败

首先使用LM-OTS签名验证算法(算法4b)验证LMS签名,以根据LM-OTS签名和消息计算LM-OTS公钥。然后将该公钥的值分配给LMS树的关联叶,然后根据叶值和数组路径[]计算树的根,如下面的算法6所述。如果根值与公钥匹配,则签名有效;否则,签名验证将失败。

算法6:LMS签名验证

  1. 如果公钥长度不超过8个字节,则返回INVALID
  2. 从公钥解析pubtype、I和T[1]
  3. )pubtype=strTou32(公钥的前4个字节)
  4. )ots_typecode=strTou32(公钥的下4个字节)
  5. )Set m according to pubtype, based on Table 2 //m恒定32
  6. )如果公钥的长度不是24+m字节,则返回INVALID
  7. )I=公钥的下一个16字节
  8. )T[1]=公钥的下一个m字节
  9. 使用算法6a从签名、消息、标识符、pubtype和ots_类型码计算LMS公钥候选Tc
  10. 如果Tc等于T[1],则返回有效;否则,返回无效

算法6a:根据签名、消息、标识符和算法类型码计算LMS公钥候选

  1. 如果签名长度不超过8字节,则返回INVALID

  2. 按如下方式解析签名的sigtype、q、lmots_签名和路径

  3. )q=strTou32(签名的前4个字节)

  4. )otssigtype=strTou32(签名的下4个字节)
  5. )如果otssigtype不是公钥中的OTS类型代码,则返回INVALID
  6. )根据otssigtype和表1设置n、p;如果签名长度不超过12+n*(p+1)字节,则返回INVALID
  7. )lmots_signature = bytes 4 through 7 + n * (p + 1) of signature
  8. )sigtype = strTou32(bytes 8 + n * (p + 1)) through 11 + n * (p + 1) of signature)

  9. )如果sigtype不是公钥中的LM typecode,则返回INVALID

  10. )根据sigtype和表2设置m、h
  11. )如果q>=2^h或签名的长度不完全是12+n*(p+1)+m * h字节,则返回INVALID
  12. )按如下方式设置路径:

C path[0] = next m bytes of signature path[1] = next m bytes of signature ... path[h-1] = next m bytes of signature

  1. Kc=通过将算法4b应用于签名lmots_signature、消息和标识符I、q而计算的候选公钥

  2. 计算候选LMS根值Tc如下

//获取OTS公钥,Kc是计算指定叶子节点的OTS公钥,占用32字节
Q = H(l || u32str(q) || u16str(D_MESG) || C || message)//D_MESG=0x8181
for ( i = 0; i < p; i = i + 1 ) 
{
    a = coef(Q || Cksm(Q), i, w) 
    tmp = y[i] 
    for ( j = a; j < ((1<<w) - 1); j = j + 1 ) 
    {
        tmp = H(l || u32str(q) || u16str(i) || u8str(j) || tmp) 
    } 
    z[i] = tmp 
} 
Kc = H(l || u32str(q) || u16str(D_PBLC) || z[0] || z[1] || ... || z[p-1])
//对指定叶子节点OTS公钥做hash 
node_num = (1<<h) + q //根据指定叶编号q获取整颗树节点编号(包含叶子节点和非叶子节点)
tmp = H(I || u32str(node_num) || u16str(D_LEAF) || Kc) //D_LEAF为0x8282
i = 0 
while (node_num > 1) //一共执行h次
{ 
    if (0!= (node_num&1))
        //当前节点是右子节点
        tmp = H(I||u32str(node_num/2)||u16str(D_INTR)||path[i]||tmp) //D_INTR为0x8383
    else
        // 当前节点是左子节点
        tmp = H(I||u32str(node_num/2)||u16str(D_INTR)||tmp||path[i]) 
    //获取上一层父节点
    node_num = node_num/2 
    i = i + 1 
} 
Tc = tmp

return 
项目上的TASK结构体定义
typedef struct {
uint32_t cfg;         //keygen, sign, verify, []:init/update/final, []:决定是否吐出hash中间值
volatile uint32_t sw; //result
} scl_base_task_t

typedef struct {
uint32_t lms_type;   //lms算法类型
uint32_t lm_ots_type;//lm_ots算法类型
uint8_t pk_root[32]; //sha256哈希值,参与计算的I值存储在私钥,验签不需要计算该
uint8_t I[16];       //LMS私钥的16字节标识符
} scl_lms_pk_t

typedef struct {
uint32_t lms_type;    //lms算法类型
uint32_t lm_ots_type; //lm_ots算法类型
uint8_t  sk_seed[16]; //种子, 用于ots私钥生成
uint32_t q;          //尚未使用的叶编号(用于下一次签名),lms_type决定q的取值范围[0,2^h-1]
uint8_t I[16];       //LMS私钥的16字节标识符
} scl_lms_sk_t

typedef struct {
//sig = u32str(q)||lmots_signature||u32str(type)||path[0]||path[1]||path[2]||...||path[h-1]
//lmots_signature = u32str(type)||C||y[0]||...||y[p-1]
uint32_t lms_type;    //lms算法类型
uint32_t lm_ots_type; //lm_ots算法类型
uint8_t sig[LMS_SIG_MAX_LEN]; //4+(32+32*p)+4+(32*h), p取最大值265,h取最大值25,取决于参数集选择p和h
uint32_t siglen; //签名长度取决于参数集lms_ots的p和lms的h
} scl_lms_sig_t


typedef struct {
scl_base_task_t base;
scl_lms_pk_t *lms_pk;
scl_lms_sk_t *lms_sk;
scl_lms_sig_t *lms_sig;
uint8_t *msg;
uint32_t msglen;
uint8_t *l_tmp;
uint32_t *l_tmplen;
} scl_lms_task_t
安全字符串
为了提高对攻击的安全性,这些攻击会分散他们对哈希函数多次调用的精力,Leighton和Micali引入了一个“安全字符串”,该字符串对于该函数的每次调用都是不同的。每当此进程计算散列时,被散列的字符串将以以下字段形成的字符串开始。这些字段将出现在我们计算散列值的固定位置,因此我们列出这些字段在散列中的位置。组成此字符串的字段如下所示:

I :LMS公钥/私钥对的16字节标识符。在生成密钥对时,必须随机或通过伪随机过程均匀地选择该密钥,以最小化I的任何特定值用于大量不同LMS私钥的概率。这始终是散列值的0-15字节

r:在LMS N时间签名方案中,与散列树的特定节点相关联的节点号r被用作用于计算该节点的散列的输入。此值表示为网络字节顺序的32位(四字节)无符号整数。r或q(取决于域分隔参数)将是散列值的字节16-19。

q:在LMS N时间签名方案中,每个LM-OTS签名都与哈希树的叶子相关联,并且q被设置为叶子编号。这可确保每个不同的LM-OTS公钥/私钥对使用不同的q值。此值表示为网络字节顺序的32位(四字节)无符号整数。r或q(取决于域分隔参数)将是散列值的字节16-19。

D:一个域分离参数,它是一个双字节标识符,在调用哈希函数的不同上下文中具有不同的值。D出现在被散列的值的字节20和21中,并采用以下值:

计算LM-OTS算法中所有迭代的哈希时,D_PBLC=0x8080

在LM-OTS算法中计算消息哈希时,D_MESG=0x8181

计算LMS树的叶散列时,D_LEAF=0x8282

计算LMS树内部节点的哈希时,D_INTR=0x8383

i:值介于0和264之间;当计算Winternitz链的迭代次数或使用建议的LM-OTS私钥生成过程时,这在LM-OTS方案中使用。它表示为网络字节顺序的16位(两字节)无符号整数。如果存在,它出现在被散列的值的字节20和21处。

j:在LM-OTS方案中,j是迭代散列私钥元素时使用的迭代数。它表示为一个8位(一字节)无符号整数,如果i是一个介于0和264之间的值,则存在该整数。如果存在,它出现在被散列的值的字节22到21+n处。

C:一个n字节的随机化器,每当对消息进行哈希处理以提高安全性时,它都包含在消息中。C必须随机或通过另一个不可预测的过程统一选择。如果D=D_MESG,则存在该值,它出现在被散列值的字节22到21+n处。

官方C model分析

官方c model链接:https://github.com/cisco/hash-sigs

该模型会输出如下文件:
【keyname.aux】:目前还没有仔细分析里面的数据组成,结合代码分析是预计算数据
【keyname.prv】:
数据结构组成:

//demo w8 h5
  keyname.prv(私钥文件)- 总长度56字节

  实际数据分析:
  00000000: 0000 0000 0000 0001 64ff ffff ffff ffff  ........d....... //counter||set_depress
  00000010: 0000 0000 0000 0000 0000 0000 0000 0000  ................ //seed
  00000020: 0000 0000 0000 0000 0000 0000 0000 0000  ................
  00000030: 1000 0000 0000 0000 0000 0000 0000 0000  ................ //I

  keyname.prv = str8(q)||str8(set)||str32(seed)||str16(I)

  1. 字节0-7 (PRIVATE_KEY_INDEX): 00 00 00 00 00 00 00 01    //q - 4byte
    - 当前签名计数器,值为1(大端字节序)
    - 表示已经使用了1次签名
  2. 字节8-15: 64 ff ff ff ff ff ff ff
    - 参数集压缩数据的一部分
    - 64 = 十进制100,可能是w参数或其他参数配置
  3. 字节16-47: 全0  //seed
  4. 字节48-63: 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 //I

【keyname.pub】:
数据结构组成:

//demo w8 h5
  keyname.pub(公钥文件)- 总长度60字节

  实际数据分析:
  ⎿  00000000: 0000 0001 0000 0005 0000 0004 1000 0000  ................
     00000010: 0000 0000 0000 0000 0000 0000 ee75 d868  .............u.h
     00000020: cced dfdd 57d1 0a9c 889a 6df4 1e03 efde  ....W.....m.....
     00000030: 2af2 c6e0 5831 d594 6e75 3b0b            *...X1..nu;.

  keyname.pub = str4(HSS high)||str4(lms_type)||str4(lm ots type)||str16(I)||str32(root)

  1. 前4字节 - HSS层级数
    - 格式:4字节大端字节序整数
    - 含义:HSS签名方案的层级数量
  2. 第4-7字节 - LM类型参数
    - 格式:4字节大端字节序整数
    - 含义:Merkle树的参数类型
  3. 第8-11字节 - LM-OTS类型参数
    - 格式:4字节大端字节序整数
    - 含义:一次性签名方案的参数类型
  4. 第12-27字节 - 标识符I
    - 长度:16字节 (I_LEN = 16)
    - 含义:密钥对的唯一标识符
  5. 第28-59字节 - 根哈希值
    - 长度:32字节 (MAX_HASH = 32,对于SHA-256)
    - 含义:Merkle树的根哈希值,用于验证签名

【msg.txt】:待签名的原始消息

//demo w8 h5 16进制
//80字节
3132333435363738313233343536373831323334353637383132333435363738313233343536373831323334353637383132333435363738313233343536373831323334353637383132333435363738

【msg.txt.sig】
数据结构组成:

//demo w8 h5

//HSS层级数减1(L-1)
00000000
// q (4 bytes)
00000002
// LMOTS_SHA256_N32_W8 (4 bytes)
00000004
// C (32 bytes)
df5f5fdeddd39038432ea36f9afd5510075fd03609106d81a457ef70003a019f
// y[0]||...||y[p-1] (34*32 = 1088 bytes)
c371ecb5b5d5bbda59bc1098ce8a1e7ae62676a30d14707e305c8b3b36213d3a
19ce9cd221a5586125f9865d9ad443d1743573607006d0b3fe40d4707aa35325
300c80d76ad23c6f0a8c116d4c3d47809994706058bd64b6d78e4ca57a77f9c9
b4fbc38a4d040e0eca7fd0060e5a616249076a99faf4b9ec2c440070c9de52f2
f43973c513b1b38419218debd34104991219dbbf3e6969e71cd5911a6d352657
6fcb890518d2a360fad59e3ecf43b349481ca7b2498f6f4c160aceeaa357e74e
64d152489d354ae1e34d01bcb0da7736d5e4abca3a6a4f5485b63af76aad651a
197a74fa1233002bc0cd87608d4bb4154a489e0f500a22d31b04c79c2303d621
8332f8cd1910586bdc621f5841fd3a624033ba9255b6a855e25efc0fbcec806d
9bd982146f6fcbc52d25df802809f4d194edcd41aad83a2554851a14440ede1d
607cad6eecae5e693fb37334d65675866b898b469ff11bb204f6f7ad86e9a163
005d8bf485488d6a8c6773006ebc571e87487e5892e73c2abb6dc00d7f5bbadd
c34347a46517e410f70625c084cd3d85c7c17dab9fd2b5ad47c2fa80dc745b8d
07b6c5a8145604bf1047ae539b32599549ee7dc1e1a20237c90ae437e0742845
174256d2ea15e9e8575594e1823af0c2fa43af99fb57065f1752c2d0751087a5
a9f3818bef618978d80b7d8fcf215fca116b49b859b94092ed232153caaed6d6
113c50b07c12e2173b29eb9ebf44c03111867c5c6e19eb0c1155ea231c76ebda
f4d17e2d12376f9f1722fc0a3789874d0b268d12db65c6510df41af8e7b91b0e
5211831f7954085fc2d29cc5664b3c7531e889d123638be117adfa1db6b4d12e
ec4195a79a1159e6dd0b9dd2e81fed24ce33527d2b6cc6e30c6f7743481e6621
bcf335ccb43dcc6cf2437d62b530dae37b07e0f04bfaf91c92f31d3ba00fdeef
4fc0c52bc2aa796a1b1765e66999fb9f93ae6c33689dc802e49fb316e95e8119
66065a3b3bcd2ad12ac01cafaec628f2a842d47b6d020c58f986d67177804658
85b7a29044a85b069ffcdb5ecddec5ba7122ba4f3888a32a47ebe4c44702e89f
ebab940f7c5fd9ba92aeef8363e7da92acb79681f50c58b85ff0ba3e2f5731e2
e509260844b808d0023a46b15c133243a1e4d753c939d99e9d0d5a7b5e74a475
2a8d654a69b99c41fc445ff843dba809b97682a0197066682973fc9e14c97378
a52dd3b575b55d8a7f75b7963f676836321c0e07d19367d8cb41b813fbbc9a00
cdc2a66116c889752fb88fcffa09b7153d7d67c167800271d47f6041c7577a19
8882e6d263a619f05e1a067bd4eadf90866f5a5282daf6d786cad134b25c118a
e1328c4db11934de8ad031f4da53051c2d3a3ec6f21af748e90cb7a1026b910e
c3092eb023f4cd74745e99c5e38f725a296b47d9d7ce1d9471bcdd7d75369e76
2ce89c663aefe2ac80355bb9ae4d7f6dad1f7a30eea7a4ed376088a8909c7468
11078e27e7a40ab32bd58a8082490b515f164f2f84559e49b1ab142c3f90d05c
// LMS_SHA256_M32_H5 (4 bytes)
00000005
// path[0]||...||path[h-1] (5*32 = 160 bytes)
abc80a13a7910f4af6efca10be4575edb5c336bc5c1a6d991d4edd898bbbb50a
ffd75ef07a4c1e2c1f51dc10a9acf54fbe8ab93b79fcd820ca6c8ec7f249609c
1efde58c816132cc74beed6b5790615faf2d499cf06eca583466a1fde205d2a3
d348a557e94b9981f7e0609aa3d82691aa45f87ab95b493a42fdd0188f337286
4c446423d7cc93b9ed731be8b1bf40c6a97f83f325be7c2c440ac2a5fe59d087
其他

这是基于hash的签名PQC方案

对于一颗高度为h的二叉Merkle树(LMS中通常使用二叉树):
1.叶子节点的数量为2h,每个叶子节点对应一个OTS密钥对,因此一颗高度h的树可管理2h个OTS密钥对

One Time Signatures

one bit

1979,Leslie Lamport:One Time Signatures(OTS)

1. If need ti sign 0, publish x = x0
2. If need to sign 1,pushlish x=x1
3. Verify by H(x) = y0 or y1

上述是对于1比特的签名和验签demo

Pk :  y0     y1
     h()↑   h()↑
Sk:    x0     x1

multi bit

Pk :  y1,0     y1,1    y2,0    y2,1   y3,0   y3,1
       h()↑     h()↑     h()↑   h()↑   h()↑   h()↑
Sk:    x1,0     x1,1    x2,0    x2,1   x3,0   x3,1

举例:假设m=010,那么输出x1,0||x2,1||x3,0。(每比特有两个状态,根据m比特状态值,对应选择)

对n比特消息(原始消息已被hash处理完毕)进行签名,那么公钥长度|pk|=私钥长度|sk|=2 * n * c,签名值长度|sig| = n * c,c是hash的输入/输出长度

WOTS

1979,Robert Winternitz:Winternitz OTS(WOTS)

Pk:    H3(x1)   H3(x2)   H3(x3)   H3(x4)  H3(x5)  H3(x6)
        h()↑     h()↑     h()↑     h()↑    h()↑    h()↑
Sk2:    H2(x1)   H2(x2)   H2(x3)   H2(x4)  H2(x5)  H2(x6)
        h()↑     h()↑     h()↑     h()↑    h()↑    h()↑
Sk1:    H(x1)    H(x2)    H(x3)    H(x4)   H(x5)   H(x6)
        h()↑     h()↑     h()↑     h()↑    h()↑    h()↑
Sk0:     x1       x2       x3       x4      x5      x6

举例,假设m=011100,表示为(01||11||00),那么输出签名值=H(x1)||H3(x1)||x3...)
每一个分组包含log2(w)比特,分组的数量=n/log2(w)

在WOTS,对手可以伪造签名对于一个大数m,因此需要添加额外的检查(利用抗第一原像攻击)

Pk:    H3(x1)   H3(x2)   H3(x3)   H3(x4)  H3(x5)  H3(x6)  ... H3(xcs1)  H3(xcs2) 
        h()↑     h()↑     h()↑     h()↑    h()↑    h()↑   ...  h()↑       h()↑
Sk2:    H2(x1)   H2(x2)   H2(x3)   H2(x4)  H2(x5)  H2(x6) ... H2(xcs1)  H2(xcs2) 
        h()↑     h()↑     h()↑     h()↑    h()↑    h()↑   ...  h()↑       h()↑
Sk1:    H(x1)    H(x2)    H(x3)    H(x4)   H(x5)   H(x6)  ... H(xcs1)    H(xcs2) 
        h()↑     h()↑     h()↑     h()↑    h()↑    h()↑   ...  h()↑       h()↑
Sk0:     x1       x2       x3       x4      x5      x6    ...  xcs1        xcs2

cs = sumi(w-1-mi),larger m leads to smaller cs(m越大,cs越小)
假设对n比特消息进行签名,公钥长度|pk| = 私钥长度|sk| = 签名值长度|sig| = (L+log2(w-1)L)/log2(w) * c,其中c是hash的输入输出长度
如上,L为分组的数量,L=n/log2(w),其中log2(w)为一个分组包含的比特数

假设w为4,i为4,cs=(4-1-m0) + (4-1-m1) + (4-1-m2) + (4-1-m3) = 4*(4-1)- sum(m0+m1+m2+m3) ,这里mi是哈希链上的值,这里是把每个

WOTS+

2013,Andreas Hulsing:WOTS+

取代hash碰撞攻击,F可以是一个PRF或抗二次原像攻击的hash函数
更小的签名具有相同的安全性
Pk:     C3k=Fk(C2k ⊕ r3)  C3k=Fk(C2k ⊕ r3)  ... C3k=Fk(C2k ⊕ r3)   C3k=Fk(C2k ⊕ r3) 
           r3()↑                r3()↑                  r3()↑             r3()↑ 
Sk2:    C2k=Fk(C1k ⊕ r2)  C2k=Fk(C1k ⊕ r2)  ... C2k=Fk(C1k ⊕ r2)   C2k=Fk(C1k ⊕ r2) 
           r2()↑                r2()↑                  r2()↑             r2()↑ 
Sk1:    C1k=Fk(C0k ⊕ r1)  C1k=Fk(C0k ⊕ r1)  ... C1k=Fk(C0k ⊕ r1)   C1k=Fk(C0k ⊕ r1) 
           r1()↑                r1()↑                  r1()↑             r1()↑ 
Sk0:      C0k=x1               C0k=x2         ...       C0k=xcs1          C0k=xcs2 

r1/r2/r3是随机值

Many Time Signature

1979,Merkel: Merkle Signature Scheme(MSS)

PK         H(H(PK1)||H(PK2)||H(PK3)||H(PK4))
                |                    |
        H(H(PK1)||H(PK2))      H(H(PK3)||H(PK4))
                |                    |
           |————————|           |——————————|
       H(PK1)      H(PK2)      H(PK3)     H(Pk4)
          |           |           |          | 
       OTS-PK1     OTS-PK2     OTS-PK3    OTS-PK4
SK     OTS-SK1     OTS-SK2     OTS-SK3    OTS-SK4

每次使用一对密钥OTS,匹配对应的认证路径。举例,使用第一对密钥,认证路径为:H(PK2),H(H(PK3)||H(PK4))

安全性:不会重复使用一个OTS

OpenSSL

Windows cmd命令

//生成rsa2048私钥文件,数据是大端
openssl genpkey -algorithm RSA -out priv.pem -pkeyopt rsa_keygen_bits:2048

//根据私钥文件生成公钥文件并打印,数据是大端
openssl rsa -pubout -in priv.pem -out pub.pem -text

标准文档

《NIST-SP800-38C》

CCM

//CCM
//5.1 底层块密码算法和密钥的选择
the forward cipher function前向密码函数是对固定比特长度的比特串的函数,长度为块,128位
//5.2 两个加密原语的操作讨论要求
向前密码功能、CTR模式
//5.3 CCM元素
A:附加认证数据,可以为空,CCM没有为A提供保密性
P:CCM保护的数据消息组成
N:用一次的位串,不要求随机
T:CCM-MAC值
//5.4 有效数据元素讨论-输入格式化
1.第一块B0,唯一确定的Nonce
2.第一块B0,独特于其他技术块(整个调用过程中)
3....
4.格式化函数对N、P、A、Tlen有长度限制,例如Tlen<32的无效
//5.5 操作和function的例子
LBS3(111011010) = 010
MBS4(111011010) = 1110
lg(2^10) = 10

//CCM规格
1.计数器可以被使用前任何时间生成
//生成-加密过程
1.先行条件
    块加密算法、Key、计数器生成函数、格式化函数、MAC长度TLen
2.输入
    N(Nonce)、P(有效负载明文)、A(附加认证数据)
3.输出
    C(密文)
4.Steps
    1) 应用格式化函数从(N、A、P)生成B0,...,Br
    2) Set Y0 = CIPHK(B0)
    3) for i=1 to r do {Yi = CIPHK(Bi XOR Yi-1)}
    4) Set T = MSB_TLen(Yr)
    5) 利用计数器生成函数生成Ctr0,...,Ctrm,其中m = PLen/128
    6) for j=1 to m do {Sj = CIPHK(Ctrj)}
    7) Set S = S1||S2||...||Sm
    //以下看是密文+Mac
    8) Return C = (P XOR MSB_PLen(S))||(T XOR MSB_TLen(S0))

//解密-认证
1.先行条件
    块加密算法、Key、计数器生成函数、格式化函数、MAC长度TLen
2.输入
    N(Nonce)、C(密文)、A(附加认证数据)
3.输出
    P(明文)或者错误信息
4.Steps
    1) if CLen < T,return INVALID
    2) 利用计数器生成函数生成Ctr0,...,Ctrm,其中m = PLen/128
    3) for j=1 to m do {Sj = CIPHK(Ctrj)}
    4) Set S = S1||S2||...||Sm
    5) Set P = MSB_Clen-Tlen(C) XOR MSB_Clen-Tlen(S) //把T去掉,异或恢复明文
    6) Set T = LSB_Clen-Tlen(C) XOR MSB_Clen-Tlen(C)
    7) 应用格式化函数从(N、A、P)生成B0,...,Br
    8) Set Y0 = CIPHK(B0)
    9) for i=1 to r do {Yi = CIPHK(Bi XOR Yi-1)}
    10) if T != MSB_Tlen(Yr) return INVALID
        else return P

《RFC4309》-AES_CCM

AES-CCM
https://rfc2cn.com/rfc4309.html

//AES CCM有两个参数:
//M表示完整性检查值(ICV)的大小。CCM定义了4、6、8、10、12、14和16个八位字节的值;但是,为了保持对齐并提供足够的安全性,只允许使用四的倍数且至少为八的值。实现必须支持8个八位字节和16个八位字节的M值,并且实现可以支持12个八位字节的M值。
M  

//L表示长度字段的大小(以八位字节为单位)。CCM定义2个八位字节和8个八位字节之间的L值。本规范仅支持L=4。实现必须支持4个八位字节的L值,这可以容纳一个完整的巨型程序[JUMBO];但是,长度包括所有加密数据,其中还包括ESP Padding、Pad length和Next Header字段。
L

//CCM发起人处理有4个输入
//密钥单个密钥用于使用CBC-MAC计算ICV,并使用计数器模式执行有效负载加密。AES支持128位、192位和256位的密钥大小。默认键大小为128位,实现必须支持此密钥大小。实现还可以支持192位和256位的密钥大小。
Key

//nonce nonce的大小取决于为参数L选择的值。它是15-L八位字节。实现必须支持11个八位字节的nonce。第4节描述了nonce的构造
Nonce

//有效载荷ESP数据包的有效载荷。有效载荷不得超过4294967295个八位字节,这是一个巨型程序[JUMBO]的最大大小;但是,ESP Padding、Pad Length和Next Header字段也是有效负载的一部分。
ESP //ESP = IV(8 octets) || Encrypted Payload || Authentication

//AAD CCM为有效负载之外的某些数据提供数据完整性和数据源身份验证。CCM不允许附加认证数据(AAD)的长度超过18446744073709551615个八位字节。ICV根据ESP标题、有效载荷和ESP拖车字段计算,该字段明显小于CCM施加的限制。第5节中描述的AAD施工
AAD

注意:

1)同一IV和钥匙组合不得使用多次
2)KEYMAT的大小等于AES密钥长度+3字节
3)《RFC3610》提供测试向量

《RFC3610》//CCM

Counter with CBC-MAC(CCM)
CCM定义用于128位分组密码,如AES

CBC-MAC(CCM)计数器是一种通用的认证加密分组密码模式。CCM定义用于128位分组密码,如高级加密标准(AES)。

//CCM Mode Specification
//身份验证字段的大小。M值的选择涉及消息扩展和攻击者无法检测到修改消息的可能性之间的权衡。有效值为4、6、8、10、12、14和16个八位字节
M

//长度字段的大小。此值需要在最大消息大小和Nonce大小之间进行权衡。不同的应用程序需要不同的权衡,因此L是一个参数。L的有效值范围在2个八位字节和8个八位字节之间(保留值L=1)
L

//Input
//分组密码的加密密钥K
Key

//15-L个八位组的一个非正数N,唯一值,对使用相同密钥加密的两条不同消息使用相同Nonce会破坏此模式的安全属性
//有效长度:7-13字节(15-L)
N

//消息M
M

//附加认证数据,此附加数据经过身份验证但未加密,不包含在此模式的输出中,可用于验证明文数据包头,或影响消息解释的上下文消息。不希望验证其他数据的用户可以提供长度为0的字符串
aad
//计算认证字段T步骤
//1.定义B_0...B_n序列
字节序号:   0     || 1..15-L || 16-L...15
内容    : Flags       Nonce       Len(M)
//Flags of B_0
位序号:    7           ||  6   ||5..3  ||2..0
内容  : Reserved(通常为0)  Adata     M'    L'
if len(aad)>0 Adata = 1 else Adata = 0
M' = [1,7]
L' = L-1,保留0值  //Len(M)

//2.使用CTR模式,定义密钥流块
S_i = E(K, A_i) for i=0 to ... //S_0不用于加密消息
//值A_i的格式
位序号:0   ||  1..15-L ||  16-L..15
内容 :Flags    Nonce       Counter_i
//CBC-MAC计算流程
X_1 = E(K, B_0)
X_i+1 = E(K, X_i XOR B_i) for i=1 to n
T = first_M_bytes(X_n+1)

AES128-CCM计算流程1

B0构造(16byte)

Octet Number Contents
0 Flags
1 ... 15-L Nonce N
16-L ... 15 ByteLength(message)

Flags格式(1byte)

Octet Number Contents
7 Reserved (always is 0)
6 Adata(if len(aad) > 0, value is 1,else value is 0)
5 ... 3 M' = (M-2)/2, so M' = [1,7]
2 ... 0 L' = L -1, so L' = [0,7]

B1起始构造(2byte)

first two octets followed by Comment
0x0000 nothing Reserved
0x0001 ... 0xFEFF nothing 0 < ByteLen(aad) < (2^16 - 2^8)
0xFF00 ... 0xFFFD nothing Reserved
0xFFFE 4 octets of ByteLen(aad) (2^16 - 2^8) <= ByteLen(aad) < (2^32)
0xFFFF 8 octets of ByteLen(aad) (2^32) <=ByteLen(aad) < (2^64)

Si构造(i=0, 1, 2...)(16byte)

Octet Number Contents
0 Flags(对于Si, bit[0:3]为L', 其余bit为0),即Flags=L’
1 ... 15-L Nonce N
16-L ... 15 Counter i

注:S0经CTR计算得到最终MAC

总流程:
1.根据Nonce长度,Mac长度,AAD长度,消息长度,来构造B0
2.根据AAD构造B1起始,如果AAD=NULL,不占一个block
3.根据Message构造剩余block,0填充,如果Message=NULL,不占一个block

//-------------------------Input5---------------------------
//key-AES128-16bytes
27F86757EE3A22CF321E2C04931DD95F
//Nonce-7Bytes //有效位7-13bytes //so L=15-7=8
11223344556677
//AAD-15bytes 
D3691BC11966725BAD706DD7FAEF81
//plaintext-0bytes
NULL
//M=length(Mac)
6
//-------------------------Output5---------------------------
//cipher
NULL
//Tag
CA0EE26E9FE4


//B0-hex
57
11223344556677
0000000000000000
//Flag-bin,对应hex(‭00010111‬)=0x17
0//Reserved (always is 0)
1//Adata(if len(aad) > 0, value is 1,else value is 0)
010//M' = (M-2)/2, so M' = [1,7]
111//L' = L -1, so L' = [0,7]

//S0 - CTR IV
07//0Flags(对于Si, bit[0:3]为L', 其余bit为0),即Flags=L’
11223344556677//Nonce N
0000000000000000//Counter i

//blocks
57112233445566770000000000000000
000FD3691BC11966725BAD706DD7FAEF
81000000000000000000000000000000

//将blocks带入CBC计算得到最后一组block
D4A26C759CAF5A1330991E8563756A8C
//中间tag值,最后一组block,截取前M字节,并0填充(其实没必要做,因为ctr字节流加密,不影响前M字节)
D4A26C759CAF00000000000000000000
//IV为S0=07112233445566770000000000000000,中间tag值进行CTR加密计算,得
CA0EE26E9FE43C255FFD2386AE216B85
//Mac
CA0EE26E9FE4
//-------------------------Input4---------------------------
//key-AES128-16bytes
27F86757EE3A22CF321E2C04931DD95F
//Nonce-7Bytes //有效位7-13bytes //so L=15-7=8
11223344556677
//AAD-0bytes 
NULL
//plaintext-16bytes
NULL
//M=length(Mac)
6
//-------------------------Output4---------------------------
//cipher
NULL
//Tag
C246B98F34CC


//B0-hex
17
11223344556677
0000000000000000
//Flag-bin,对应hex(‭00010111‬)=0x17
0//Reserved (always is 0)
0//Adata(if len(aad) > 0, value is 1,else value is 0)
010//M' = (M-2)/2, so M' = [1,7]
111//L' = L -1, so L' = [0,7]

//S0 - CTR IV
07//0Flags(对于Si, bit[0:3]为L', 其余bit为0),即Flags=L’
11223344556677//Nonce N
0000000000000000//Counter i

//blocks
17112233445566770000000000000000

//将blocks带入CBC计算得到最后一组block
DCEA379437877AB8CCD74C12F01F31BE
//中间tag值,最后一组block,截取前M字节,并0填充(其实没必要做,因为ctr字节流加密,不影响前M字节)
DCEA3794378700000000000000000000
//IV为S0=07112233445566770000000000000000,中间tag值进行CTR加密计算,得
C246B98F34CC66366F643D03CD540109
//Mac
C246B98F34CC
//-------------------------Input3---------------------------
//key-AES128-16bytes
27F86757EE3A22CF321E2C04931DD95F
//Nonce-7Bytes //有效位7-13bytes //so L=15-7=8
11223344556677
//AAD-0bytes 
NULL
//plaintext-16bytes
3A1A07582FEECD2F6C42D18A04189082
//M=length(Mac)
6
//-------------------------Output3---------------------------
//cipher
B82F54D9D7AE4D0E739BA821416A4C46
//Tag
F6049BE2C785


//B0-hex
17
11223344556677
0000000000000010
//Flag-bin
//0x17
0
0
010
111

//S0
07
11223344556677
0000000000000000

//blocks
17112233445566770000000000000010
3A1A07582FEECD2F6C42D18A04189082

//将blocks带入CBC计算(IV为全0)得到最后一组block 
E8A815F9C4CEF6326EA31BA1E12232A3
//中间tag值,最后一组block,截取前M字节,并0填充
E8A815F9C4CE00000000000000000000
//IV为S0=07112233445566770000000000000000,中间tag值进行CTR加密计算,得
F6049BE2C78566366F643D03CD540109
//Mac
F6049BE2C785
//计算密文,IV++,CTR加密
B82F54D9D7AE4D0E739BA821416A4C46
//-------------------------Input1---------------------------
//key-AES128-16bytes
27F86757EE3A22CF321E2C04931DD95F
//Nonce-7Bytes //有效位7-13bytes //so L=15-7=8
11223344556677
//AAD-15bytes 
D3691BC11966725BAD706DD7FAEF81
//plaintext-16bytes
3A1A07582FEECD2F6C42D18A04189082
//M=length(Mac)
6
//-------------------------Output1---------------------------
//cipher
B82F54D9D7AE4D0E739BA821416A4C46
//Tag
198455B4E0AE


//B0-hex
57
11223344556677//Nonce
0000000000000010 //8byte, length(msg)
//Flag-bin
//0x57
0
1
010
111

//S0
07//注意非57,flag=L'
11223344556677
0000000000000000

//blocks
//B0||B1(2byte)||pad(AAD)||pad(msg)
57112233445566770000000000000010
000FD3691BC11966725BAD706DD7FAEF //注意此处前2byte,代表aad实际长度
81000000000000000000000000000000
3A1A07582FEECD2F6C42D18A04189082


//将blocks带入CBC计算得到最后一组block //CBC IV为0
0728DBAFE3E5D9E6A61FA10CE9DA281F
//中间tag值,最后一组block,截取前M字节,并0填充
0728DBAFE3E500000000000000000000
//IV为S0(即A0)=07112233445566770000000000000000,中间tag值进行CTR加密计算,得
198455B4E0AE66366F643D03CD540109
//Mac
198455B4E0AE
//计算密文,IV++,CTR加密
B82F54D9D7AE4D0E739BA821416A4C46//加密可以并行,因为S0已知


//-------------------------Input2---------------------------
//key-AES128-16bytes
27F86757EE3A22CF321E2C04931DD95F
//Nonce-7Bytes //有效位7-13bytes //so L=15-7=8
11223344556677
//AAD-15bytes 
D3691BC11966725BAD706DD7FAEF81
//plaintext-16bytes
3A1A07582FEECD2F6C42D18A04189082
//M=length(Mac)
16
//-------------------------Output2---------------------------
//cipher
B82F54D9D7AE4D0E739BA821416A4C46
//Tag
BD9DE8763952F5AFD0E112B056A084A3


//B0-hex
7F
11223344556677
0000000000000010
//Flag-bin
//0x7F
0
1
111
111

//block
7F112233445566770000000000000010
000FD3691BC11966725BAD706DD7FAEF
81000000000000000000000000000000
3A1A07582FEECD2F6C42D18A04189082

//blocks带入CBC_MAC,得
A331666D3A199399BF852FB39BF485AA
//中间Tag值,截取前M字节,0填充
A331666D3A199399BF852FB39BF485AA
//IV=S0
07112233445566770000000000000000
//中间Tag值进行CTR加密
BD9DE8763952F5AFD0E112B056A084A3
//计算cipher,iv++
B82F54D9D7AE4D0E739BA821416A4C46

2)计算B0

void ccm_get_B0(uint8_t *nonce, uint8_t M, uint8_t L, uint32_t aad_bytes, uint32_t payload_byte, uint8_t out[16])
{
    uint8_t tmp[4];

    //B0 Flag
    out[0] = 0;
    out[0] |= (M - 2) / 2;
    out[0] <<= 3;
    out[0] |= L - 1;

    if (aad_bytes)
    {
        out[0] |= 0x40;
    }

    //B0 nonce
    if (nonce != out + 1)
    {
        memcpy_(out + 1, nonce, 15 - L);
        memset_(out + 1 + 15 - L, 0, L);
    }

    reverse_byte_array((uint8_t *)&payload_byte, tmp, 4);

    if (L <= 4)
    {
        memcpy_(out + 16 - L, tmp + 4 - L, L);
    }
    else
    {
        memcpy_(out + 16 - 4, tmp, 4);
    }
}

3)计算B1

void ccm_get_B1(uint8_t *aad, uint32_t aad_bytes, uint32_t *aad_offset, uint8_t out[16])
{
    uint8_t tmp[4];
    uint32_t current_bytes, left_bytes;

    reverse_byte_array((uint8_t *)&aad_bytes, tmp, 4);

    if (aad_bytes < ((1 << 16) - (1 << 8)))
    {
        memcpy_(out, tmp + 2, 2);
        current_bytes = 2;
        left_bytes = 16 - 2;
    }
    else
    {
        out[0] = 0xFF;
        out[1] = 0xFE;
        memcpy_(out + 2, tmp, 4);
        current_bytes = 6;
        left_bytes = 16 - 6;
    }

    if (aad_bytes <= left_bytes)
    {
        memcpy_(out+current_bytes, aad, aad_bytes);
        memset_(out + current_bytes + aad_bytes, 0, left_bytes - aad_bytes);
        *aad_offset = aad_bytes;
    }
    else
    {
        memcpy_(out + current_bytes, aad, aad_bytes);
        *aad_offset = left_bytes;
    }
}

CCM计算流程2(可能有问题)

文档里的测试数据验证,已成功。

//《RFC3610规范》测试数据验证
//部分流程跟网上一些论文有出入
//https://www.doc88.com/p-3793128718693.html?r=1
//Input
T_L             = 8 //输出Mac的长度,字节
AES Key = C0 C1 C2 C3  C4 C5 C6 C7  C8 C9 CA CB  CC CD CE CF
Nonce   = 00 00 00 03  02 01 00 A0  A1 A2 A3 A4  A5
Total packet length = 31. [Input with 8 cleartext header octets]
          00 01 02 03  04 05 06 07  08 09 0A 0B  0C 0D 0E 0F
          10 11 12 13  14 15 16 17  18 19 1A 1B  1C 1D 1E


//Output
Total packet length = 39. [Authenticated and Encrypted Output]
          00 01 02 03  04 05 06 07  58 8C 97 9A  61 C6 63 D2
          F0 66 D0 C2  C0 F9 89 80  6D 5F 6B 61  DA C3 84 17
          E8 D1 2C FD  F9 26 E0


//1.提取信息
//K
C0C1C2C3C4C5C6C7C8C9CACBCCCDCECF

//IV
5900000003020100A0A1A2A3A4A50017 //IV构造
//IV_Enc = Enc(K,IV)
=EB9D5547730955AB231E0A2DFE4B90D6

//M(带有8个明文标题八位字节的输入)
000102030405060708090A0B0C0D0E0F
101112131415161718191A1B1C1D1E

//Nonce,长度13字节
00000003020100A0A1A2A3A4A5
//T,长度8字节




//2.对报文进行分组
//M1构造:len(aad)用2字节表示 || packet header || zero padding
M1=00080001020304050607000000000000 
M2=08090A0B0C0D0E0F1011121314151617 //开始从原文划分
M3=18191A1B1C1D1E000000000000000000 //00填充




//3.计算T_CBC-MAC
//Block_1 = E(K,IV_Enc^M1)
=E(K,EB9D5547730955AB231E0A2DFE4B90D6^00080001020304050607000000000000)
=E(K,EB955546710A51AE25190A2DFE4B90D6)
=CDB6411E3CDC9B4F5D9258B69EE7F091

//Block_2 = E(K,Block_1^M2)
=E(K,CDB6411E3CDC9B4F5D9258B69EE7F091^08090A0B0C0D0E0F1011121314151617)
=(K,C5BF4B1530D195404D834AA58AF2E686)
=9C38405EA03C1BC904B58B40C76CA2EB

//Block_3 = E(K,Block_2^M3)
=E(K,9C38405EA03C1BC904B58B40C76CA2EB^18191A1B1C1D1E000000000000000000)
=(K,84215A45BC2105C904B58B40C76CA2EB)
=2DC697E411CA83A860C2C406CCAA542F

//T_CBC-MAC = Block_3[0:T_L-1]
=2DC697E411CA8 //取最后一分组密文前T_L字节




//4.计数器CTR加密计算
//A_0
=Enc(K,01||Nonce||0000)
=Enc(K,0100000003020100A0A1A2A3A4A50000)
=3A2E46C8EC33A5485620542C022CC07D
//A_0_first_T_M //截取结果前T_M长度数据
=3A2E46C8EC33A54

//计数器CTR加密计算
//CTR[0000] = 01||Nonce||0001
0100000003020100A0A1A2A3A4A50001

//CTR[0001]
E(K,CTR[0000])
=E(K,0100000003020100A0A1A2A3A4A50001)
=50859D916DCB6DDDE077C2D1D4EC9F97

//CTR[0002]
=E(K,CTR[0001])
=E(K,0100000003020100A0A1A2A3A4A50002)
=7546717AC6DE9AFF640C9C06DE6D0D8F

//CTR[MAC],未知,结果好像没用到
=E(K,CTR[0002])
=E(K,0100000003020100A0A1A2A3A4A50003)
=1E22952AEC3F6B8BB2AE125FF7E3986C //测试答案是3A2E46C8EC33A548




//5.CBC计算分组密文C
//M(已去掉前面带有8个明文标题八位字节的输入)
M1 = 08090A0B0C0D0E0F1011121314151617
M2 = 18191A1B1C1D1E000000000000000000

//C
C1 = M1^CTR[0001]
=08090A0B0C0D0E0F1011121314151617^50859D916DCB6DDDE077C2D1D4EC9F97
=588C979A61C663D2F066D0C2C0F98980
C1 = M2^CTR[0002]
=18191A1B1C1D1E000000000000000000^7546717AC6DE9AFF640C9C06DE6D0D8F
=6D5F6B61DAC384//截取有效长度6D5F6B61DAC384FF640C9C06DE6D0D8F



//6.计算T-CCM 
T-CCM = T_CBC-MAC^A_0_first_T_M
2DC697E411CA83A8^3A2E46C8EC33A54
=17E8D12CFDF926E0


//合并结果hdr||C||T-CCM
0001020304050607||
588C979A61C663D2F066D0C2C0F98980||
6D5F6B61DAC384||
17E8D12CFDF926E0

4、《RFC6655》

本备忘录描述了在传输层安全性(TLS)和数据报TLS(DTLS)内操作的密码块链-消息认证码(CBC-MAC)模式(CCM)计数器中使用高级加密标准(AES),以提供机密性和数据源认证。AES-CCM算法易于紧凑实现,因此适合于受限环境。

RFC3566》//XCBC

AES-XCBC-MAC-96

核心算法思想:
https://www.rfc-editor.org/rfc/rfc3566.html第4点

Input:k(AES128)、msg

Output:mac(96bit=12byte)

Step:
1.k1=Enc(k, 0x01010101010101010101010101010101);
2.k2=Enc(k, 0x02020202020202020202020202020202);
3.k3=Enc(k, 0x03030303030303030303030303030303);
4.C1=AES_CBC(k1,msg_exclude_last_block)
5.if last_block_byte == 16, C2=AES_CBC(k1, last_block⊕k2)
else last_block_byte == 16, C2=AES_CBC(k1, ISO/IEC7816-4_padding(last_block)⊕k3)
6.return C1||C2

明文少于128bits=16Bytes

//根据规范上的测试数据,手动计算了解流程
测试用例 #2:AES-XCBC-MAC-96 与 3 字节输入
密钥(K):000102030405060708090a0b0c0d0e0f
明文 (M) : 000102
AES-XCBC-MAC:5b376580ae2f19afe7219ceef172756f
AES-XCBC-MAC-96:5b376580ae2f19afe7219cee

//第一步,初始化
//Key,16字节128bit
000102030405060708090a0b0c0d0e0f
//M(填充)至分组长度,10*方式,M[1]
00010280000000000000000000000000
//IV,初始0000*
00000000000000000000000000000000

//**********参数配置**********
//从128位密钥中导出3个128bit密钥
//K1 
0x01010101010101010101010101010101
//AES(Key,K1,ECB)
C352805754237F311AC0FFF4E3E03E78

//K2
0x02020202020202020202020202020202
//AES(Key,K2,ECB)
BD862FFB97AD2FB8F8B891F6032F36CB

//K3
0x03030303030303030303030303030303
//AES(Key,K3,ECB)
C1A7ABA1A23A94065807A08CC8EED06E

//E[0]
00000000000000000000000000000000

//验证成功
//当最后一分组不满128bit时,bit位10*补足,下面已补足
Enc(K1,M[n]^E[n-1]^K3)
=Enc(K1,00010280000000000000000000000000^00000000000000000000000000000000^C1A7ABA1A23A94065807A08CC8EED06E)
=Enc(K1,00010280000000000000000000000000^C1A7ABA1A23A94065807A08CC8EED06E)
=Enc(C352805754237F311AC0FFF4E3E03E78,C1A6A921A23A94065807A08CC8EED06E)
=5B376580AE2F19AFE7219CEEF172756F
//从左往右取96bit=12字节数据
5B376580AE2F19AFE7219CEE

明文等于128bits=16Bytes

测试用例 #3:AES-XCBC-MAC-96 与 16 字节输入
密钥(K):000102030405060708090a0b0c0d0e0f
消息(M):000102030405060708090a0b0c0d0e0f
AES-XCBC-MAC:d2a246fa349b68a79998a4394ff7a263
AES-XCBC-MAC-96:d2a246fa349b68a79998a439

//Key
000102030405060708090a0b0c0d0e0f
//M
000102030405060708090a0b0c0d0e0f
//K1 
Enc(key,K1)
=Enc(Key,01010101010101010101010101010101)
=C352805754237F311AC0FFF4E3E03E78

//K2
Enc(key,K2)
=Enc(Key,02020202020202020202020202020202)
=BD862FFB97AD2FB8F8B891F6032F36CB

//K3
Enc(key,K3)
=Enc(Key,03030303030303030303030303030303)
=C1A7ABA1A23A94065807A08CC8EED06E

//E[0]
00000000000000000000000000000000

Enc(K1,M[n]^E[n-1]^K1)
=Enc(K1,000102030405060708090a0b0c0d0e0f^00000000000000000000000000000000^BD862FFB97AD2FB8F8B891F6032F36CB)
=Enc(K1,000102030405060708090a0b0c0d0e0f^BD862FFB97AD2FB8F8B891F6032F36CB)
=Enc(K1,BD872DF893A829BFF0B19BFD0F2238C4)
=Enc(C352805754237F311AC0FFF4E3E03E78,BD872DF893A829BFF0B19BFD0F2238C4)
=D2A246FA349B68A79998A4394FF7A263

明文为NULL

测试用例 #1:AES-XCBC-MAC-96 与 0 字节输入
密钥(K):000102030405060708090a0b0c0d0e0f
消息(M):<空字符串>
AES-XCBC-MAC:75f0??251d528ac01c4573dfd584d79f29
AES-XCBC-MAC-96:75f0??251d528ac01c4573dfd5

//Key
000102030405060708090a0b0c0d0e0f
//M
NULL
//M[n],注意比特位是10*填充,转到字节为80开头
80000000000000000000000000000000

//K1
Enc(key,K1)
=Enc(Key,01010101010101010101010101010101)
=C352805754237F311AC0FFF4E3E03E78

//K3
Enc(key,K3)
=Enc(Key,03030303030303030303030303030303)
=C1A7ABA1A23A94065807A08CC8EED06E

Enc(K1,M[n]^E[n-1]^K3)
=Enc(K1,80000000000000000000000000000000^00000000000000000000000000000000^C1A7ABA1A23A94065807A08CC8EED06E)
=Enc(K1,80000000000000000000000000000000^C1A7ABA1A23A94065807A08CC8EED06E)
=Enc(K1,41A7ABA1A23A94065807A08CC8EED06E)
=Enc(C352805754237F311AC0FFF4E3E03E78,D1A7ABA1A23A94065807A08CC8EED06E)
=75F0251D528AC01C4573DFD584D79F29

明文长度大于128bit时

测试用例 #4:AES-XCBC-MAC-96 与 20 字节输入
密钥(K):000102030405060708090a0b0c0d0e0f
消息(M):000102030405060708090a0b0c0d0e0f10111213
AES-XCBC-MAC:47f51b4564966215b8985c63055ed308
AES-XCBC-MAC-96:47f51b4564966215b8985c63

//Key
000102030405060708090a0b0c0d0e0f

//M[1]
000102030405060708090a0b0c0d0e0f
//M[2]
10111213800000000000000000000000

//E[0]
00000000000000000000000000000000

//K1
Enc(key,K1)
=Enc(Key,01010101010101010101010101010101)
=C352805754237F311AC0FFF4E3E03E78

//K3
Enc(key,K3)
=Enc(Key,03030303030303030303030303030303)
=C1A7ABA1A23A94065807A08CC8EED06E

CBC-MAC:先XOR,再Enc,C作为下一组的IV

//计算E[i](需要参数:明文,K1密钥,不需要密文参与)
//只需要计算到分组个数
E[0] = 00000000000000000000000000000000
E[1]
=Enc(K1,M[1]^E[0])
=Enc(K1,000102030405060708090A0B0C0D0E0F^00000000000000000000000000000000)
=Enc(K1,000102030405060708090A0B0C0D0E0F)
=1D0448FACF4D9C6F55B993DA09803DB3


//第一分组
Enc(Key,M[1]^E[0])
=Enc(Key,000102030405060708090a0b0c0d0e0f^00000000000000000000000000000000)
=Enc(000102030405060708090a0b0c0d0e0f,000102030405060708090a0b0c0d0e0f)
=0A940BB5416EF045F1C39458C653EA5A

//最后一分组,原文分组不满128bits
Enc(K1,M[2]^E[1]^K3)
=Enc(K1,10111213800000000000000000000000^1D0448FACF4D9C6F55B993DA09803DB3^C1A7ABA1A23A94065807A08CC8EED06E)
=Enc(K1,0D155AE94F4D9C6F55B993DA09803DB3^C1A7ABA1A23A94065807A08CC8EED06E)
=Enc(K1,CCB2F148ED7708690DBE3356C16EEDDD)
=Enc(C352805754237F311AC0FFF4E3E03E78,CCB2F148ED7708690DBE3356C16EEDDD)
=47F51B4564966215B8985C63055ED308
测试用例 #6:AES-XCBC-MAC-96 与 34 字节输入
密钥(K):000102030405060708090a0b0c0d0e0f
消息(M):000102030405060708090a0b0c0d0e0f10111213141516171819
                    1a1b1c1d1e1f2021
AES-XCBC-MAC:becbb3bccdb518a30677d5481fb6b4d8
AES-XCBC-MAC-96:becbb3bccdb518a30677d548


//Key
000102030405060708090a0b0c0d0e0f
//M[1]
000102030405060708090a0b0c0d0e0f
//M[2]
101112131415161718191a1b1c1d1e1f
//M[3]
20218000000000000000000000000000

//K1
Enc(key,K1)
=Enc(Key,01010101010101010101010101010101)
=C352805754237F311AC0FFF4E3E03E78
//K3
Enc(key,K3)
=Enc(Key,03030303030303030303030303030303)
=C1A7ABA1A23A94065807A08CC8EED06E

//E[0]
00000000000000000000000000000000
//E[1]
=Enc(K1,M[1]^E[0])
=Enc(C352805754237F311AC0FFF4E3E03E78,000102030405060708090a0b0c0d0e0f)
=1D0448FACF4D9C6F55B993DA09803DB3
//E[2]
=Enc(K1,M[2]^E[1])
=Enc(K1,101112131415161718191a1b1c1d1e1f^1D0448FACF4D9C6F55B993DA09803DB3)
=Enc(K1,101112131415161718191a1b1c1d1e1f^1D0448FACF4D9C6F55B993DA09803DB3)
=Enc(C352805754237F311AC0FFF4E3E03E78,0D155AE9DB588A784DA089C1159D23AC)
=09025E5A6725207244145C6B80668579

//第1分组运算C1
Enc(Key,M[1]^E[0])
=Enc(000102030405060708090a0b0c0d0e0f,000102030405060708090a0b0c0d0e0f)
=0A940BB5416EF045F1C39458C653EA5A
//第2分组运算C2
Enc(Key,101112131415161718191A1B1C1D1E1F^1D0448FACF4D9C6F55B993DA09803DB3)
=Enc(000102030405060708090a0b0c0d0e0f,0D155AE9DB588A784DA089C1159D23AC)
=9D35AF36E5755C77C7B9814EB90F5E8A

//不涉及上面两分组加密运算
//最后一分组C3,原文分组不满128bits
Enc(K1,M[3]^E[2]^K3)
=Enc(K1,20218000000000000000000000000000^09025E5A6725207244145C6B80668579^C1A7ABA1A23A94065807A08CC8EED06E)
=Enc(K1,2923DE5A6725207244145C6B80668579^C1A7ABA1A23A94065807A08CC8EED06E)
=Enc(C352805754237F311AC0FFF4E3E03E78,E88475FBC51FB4741C13FCE748885517)
=BECBB3BCCDB518A30677D5481FB6B4D8

总结

已知Key、M外部给定,求Mac

算法约定参数值:
E[0] = 00..00(16字节)

if 原文为16字节倍数
K1 = Enc(Key,01...01)
K2 = Enc(Key,02...02)
Calculate -> E[1]...E[N-1]:
E[x] = Enc(K1, M[x]^E[x-1])-> N-1轮计算
Mac = Enc(K1,M[n]E[n-1]K2)
else
K1 = Enc(Key,01...01)
K3 = Enc(Key,03...03)
Calculate -> E[1]...E[N-1]:
E[x] = Enc(K1, M[x]^E[x-1])-> N-1轮计算
Mac = Enc(K1, M[N]E[N-1]K3)

《rfc5288.AES_Galois_Counter_Mode_(GCM)_Cipher_Suites_for_TLS》

也可参考《RFC4106》

《NIST-SP800-38D》

chacha20-《RFC7539》

chacha20:高速密码,在纯软比AES快得多。在缺乏专用AES硬件平台上,速度大约是AES的三倍。(20轮)

ploy1305:验证器:高速消息身份验证代码,实现简单,容易正确。

CHACHA20-POLY1305认证加密与相关数据(AEAD)构造。

chacha20_poly1305

标准文档《RFC8349》

chacha20_poly1305,已完成两版本,一个是自写实现,调用openssl大数计算,另外一版本是从openssl抽出chacha20、poly1305源码,其余自写,已发布在公众号。

《The Galois/Counter Mode of Operation (GCM)》

gcm-revised-spec

//Input
Key
IV //any number of bits between 1 and 2^64
Plaintext //any number of bits between 0 and 2^39 - 256
AAD // any number of bits between 0 and 2^64

//Output
Ciphertext //as same as Plaintext length
Tag //whose bits length can be value between 64 and 128

椭圆曲线参数标准文档《RFC5639》

Elliptic Curve Cryptography (ECC) Brainpool Standard Curves and Curve Generation

//工具缺少brainpoop224r1
3.3.  Domain Parameters for 224-Bit Curves
   Curve-ID: brainpoolP224r1
      p = D7C134AA264366862A18302575D1D787B09F075797DA89F57EC8C0FF
      A = 68A5E62CA9CE6C1C299803A6C1530B514E182AD8B0042A59CAD29F43
      B = 2580F63CCFE44138870713B1A92369E33E2135D266DBB372386C400B
      x = 0D9029AD2C7E5CF4340823B2A87DC68C9E4CE3174C1E6EFDEE12C07D
      y = 58AA56F772C0726F24C6B89E4ECDAC24354B9E99CAA3F6D3761402CD
      q = D7C134AA264366862A18302575D0FB98D116BC4B6DDEBCA3A5A7939F
      h = 1

数学理论

有限域(伽罗瓦域)

域是一个可以在其上进行加法、减法、乘法和除法运算而结果不会超出域的集合。如有理数集合、实数集合、复数集合都是域,但整数集合不是(很明显,使用除法得到的分数或小数已超出整数集合)。

如果域F只包含有限个元素,则称其为有限域。有限域中元素的个数称为有限域的阶。尽管存在有无限个元素的无限域,但只有有限域在密码编码学中得到了广泛的应用。每个有限域的阶必为素数的幂,即有限域的阶可表示为pⁿ(p是素数、n是正整数),该有限域通常称为Galois域(Galois Fields),记为GF(pⁿ)。

注:为什么P一定要是个素数?这是因为当P为素数时,才能保证集合中所有元素加法和乘法都有逆元,假如p等于10,尽管所有元素都有加法逆元,但乘法不行,如元素2,因为找不到一个数a,使得2*a mod 10等于1;若p为素数,那么它就能保证域中的所有元素都有逆元。利用反证法和余数的定义即可证明。

定理1 一个有限域E有pⁿ个元素,这里p是E的特征,而n是E在它的素域△上的次数。 [1]
定理2 令有限域E的特征是素数p,E所含的素域是△,而E有q=pⁿ个元素,那么E就是多项式在△上的分裂域。任何两个这样的域都同构。
定理3 令△是特征为p的素域,而q=pⁿ(n≥1),那么多项式在△上的分裂域E就是一个有q个元的有限域。定理4 一个有限域E是它的素域△的一个单扩域。 [1]

【四则运算】
若任意a、b、c∈GF(q),则必有:
1.封闭性:a+b∈GF(q),a·b∈GF(q);
2.结合律:(a+b)+c=a+(b+c),(a·b)c=a(b·c);
3.交换律:a+b=b+a,a·b=b·a;
4.乘对加分配律:a(b+c)=a·b+a·c;
5.必有加法恒等元0和乘法恒等元e,使:a+0=a,a·e=a,e可理解为1;
6.必有加法负元-a和乘法逆元a-1,使:a+(-a)=0,a.a-1=e,但0-1无定义。

【有限域GF(2^8)多项式运算】
这是GF(pn)的特例,p=2,n=8。例如多项式:f(x)=x6+x4+x2+x+1,特点:

特点1:多项式的系数只能是0或1(即【0,p-1】)。若p=3,那么系数是可以取0,1,2的;
特点2:合并同类项“加法”即“⊕”,对系数进行异或操作,如x4+x4=0;
特点3:减法就等于加法(即无所谓的减法),或者负系数。所以,x4–x4就等于x4+x4,-x3就是x3
例如,设f(x)=x6+x4+x2+x+1,g(x)=x7+x+1,则:
f(x)+g(x)=x7+x6+x4+x2+(1+1)x+(1+1)1=x7+x6+x4+x2;
f(x)–g(x)=f(x)+g(x);
f(x)*g(x)=(x13+x11+x9+x8+x7)+(x7+x5+x3+x2+x)+(x6+x4+x2+x+1)=x13+x11+x9+x8+x6+x5+x4+x3+1;
f(x)/g(x)=x6+x4+x3+x2+1

伽罗华域的元素可以通过该域上的本原多项式生成。通过本原多项式得到的域,其加法单位元都是0,乘法单位元是1。本原多项式是一个素多项式(素多项式不能表示为其他两个多项式相乘的乘积,类似于素数)。以GF(23)为例,指数小于3的多项式共8个:0,1,x,x+1,x2,x2+1,x2+x,x^2+x+1。其系数刚好就是000,001,010,011,100,101,110,111,是0到7这8个数的二进制形式。多项式对应一个值,我们可以称这个值为多项式值(有时看成一个向量)。GF(23)上有不只一个本原多项式,选一个本原多项式x3+x+1,这8个多项式进行四则运算后 mod (x^3+x+1)的结果都是8个之中的某一个。

weierstrass椭圆曲线

https://blog.csdn.net/weixin_44830789/article/details/106671946

采用weierstrass方程,当特征域不为2、3,方程:

y^2 = x^3 + ax + b (mod p)

p:大素数,构成有限域GF(p) ,椭圆曲线点的坐标,曲线方程系数,以及点群上的运算均是在该域上的。
a,b:曲线方程系数。
G:曲线生成元,即基点G (Gx,Gy),其生成椭圆曲线上所有点,构成一个群。
n:点G的阶,也即曲线的阶,要求为与p规模接近的大素数。即要求曲线的余因子为1。

ECDSA/ECDH/ECIES/SM2

secp192r1, secp224r1, secp256r1, secp384r1, secp521r1
brainpoolP160r1, brainpoolP192r1, brainpoolP256r1, brainpoolP320r1, brainpoolP384r1, brainpoolP512r1
sm2p256v1

P ⊕ Q = R ,椭圆曲线两点PQ,L是过P、Q两点(如果P==Q,L是椭圆曲线的切线),与椭圆曲线相交于R‘,过R’做平行于y轴的直线,与椭圆曲线E相交于点R。

求乘法逆元

a)拓展的欧几里得算法

要求a与p互素

b)基于费马小定理的计算

a<sup>p-1</sup> = 1 mod p  等价于a的逆元为 a<sup>-1</sup> = a<sup>p-2</sup> mod p,要求a与p素数

c)

素数域

Fp :对于模数p的乘法集合{0,1,2, ..., p-1},p为素数

模p加法:a,b ∈ Fp,则a+b = (a+b) mod p
模p乘法:a,b ∈ Fp,则a · b = (a · b) mod p
加法单位元:e=0
乘法单位元:e=1
存在逆元:对于集合中的非0元素x,都存在集合内对应元素x-1,使得x-1x = 1 (mod p),x-1为x模p下的逆元

攻击与防护

故障攻击:在合适的时间点进行故障注入,单bit、多bit、如X射线、电磁场、光学诱发设备

CPA/DPA(相关性/差分功耗分析):根据输入明文与侧信道信息(能量、功耗)的相关性分析私钥指数如:模幂、模乘
SPA(简单功耗分析):当运算时由于操作数据的不同而导致功耗曲线存在可观察的差异如:椭圆曲线点乘
措施:伪操作、掩码、指数加扰/拆分

中间值故障注入:在运算过程中,向运算中间值注入故障,通过错误结果和数学分析反推私钥指数如:ECDSA
RSA公钥e被改成1:验签阶段公钥被改成 1,构造签名值使其与消息值相同,则验签通过
措施:重复计算,校验公钥,反向计算

模乘的模板攻击:根据模乘输入的两个已知操作数建立模板,利用模板分析实际运算中的模乘操作数
具体例子比如模乘:采集一组功耗曲线,所有密钥已知,选定特定中间值,根据不同的中间值建立模板。 采集一组新的曲线,密钥未知,根据模板进行匹配,恢复密钥。这里的密钥指的是模数p

采集模乘计算a*b mod p计算过程中的功耗。在CRT的mod p实现中,调用硬件实现了一个64bit随机数和一个固定数计算以p为模数的蒙哥马利模乘操作。所以采集的是64bit随机数乘以512bit固定数的蒙哥马利模乘功耗。
A组:a为64bit数,全随机;b为512bit数,2^512 % p;p为512bit模数,最高比特和最低比特置1,其余比特全随机。采样率2.5GS/s,采样点数50000,采集100万条曲线。
B组:a为64bit数,全随机;b为固定的512bit数;p为固定的512bit模数。采样率2.5GS/s,采样点数50000,采集100万条曲线。

safe-error 攻击:向特定运算时刻或特定地址数据注入故障,根据运算结果是否正确推测密钥信息。
模加 DPA/CPA:根据模乘输入的两个已知操作数建立模板,利用模板分析实际运算中的模乘操作数
选择明文攻击:选择特殊值作为明文输入,使运算过程出现 1 值、0 值、两次相同值等,这类数值产生的侧信道信息易于区分,从而分析出私钥
措施:明文掩码、指数加扰/指数拆分,蒙哥马利阶梯无伪运算

辅助工具

素数检测

在线网站:https://username1565.github.io/Javascript-Primality-Tester/
缺点:只支持10进制输入

密码小工具

myblockcipher-对称算法
myPAETool-非对称算法
chacha

计算蒙哥马利模乘参数

C = R^2 mod N
D = -N^(-1) mod 2^w
其中,R=1<<N_bit ,w为位宽

以brainpoolp512r1计算与计算值为例
n_h = R^2 mod n
n_n0 = -N^(-1) mod 2^w //这里根据位宽改变,默认32bit

随机数发生器

好的随机数发生器要满足以下特性:
1、统计测试(good statistical poperties)
2、不可预测性(unpredictable)

PRNG:Algorithm

TRNG:physical
Source:
1、时钟抖动(Clock Jitter)
2、Meta Stability(”亚平衡“,元稳定性)
3、Chaos(混沌)
4、Thermal noise(热噪声)

HRNG:PRNG+TRNG(增熵)

相关文档:NIST SP800-22

嵌入式

FLASH:闪存
SRAM:静态随机存取存储器,成本高,存取速度快,一般用于高速缓存中
DRAM:动态随机存取存储器,一般用于内存条
SDRAM:同步动态随机存取存储器
RAM:随机存取存储器,易失性,掉电丢数据
ROM:只读存储器
BootLoad:操作系统内核运行前执行的一段小程序,可以对CPU、SDRAM、串口等进行初始化

时钟周期和时钟频率

工作主频2.0GHz,说明每秒产生20亿个时钟脉冲信号,每个时钟信号周期0.5ns,如果CPU有4条流水线运算单元,意味着每秒执行80亿条二进制加法运算。

单片机

单片机:将CPU、ROM、RAM、输入输出接口电路、定时器/计数器等主要部件集成在一块集成电路芯片上,称为单片微型计算机。

RISC(Reduced Instruction Set Computer):精简指令集计算机
CISC(Complex Instruction Set Computer):复杂指令集计算机

UART

通用异步收发器,异步传输接口,不需要时钟线,通过起始位和停止位及波特率进行数据识别。
工作原理是将数据的字节一位接一位地传输。

SPI

Serial Peripheral Interface 串行外设接口,是Motorola提出的一种同步串行数据传输标准
https://blog.csdn.net/u010183728/article/details/81984433?spm=1001.2101.3001.6661.1&utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1-81984433-blog-82982824.pc_relevant_aa2&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1-81984433-blog-82982824.pc_relevant_aa2&utm_relevant_index=1

I2C

包括时钟线SCL和数据线SDA

6、代码禁止优化

//对某个函数禁止代码优化
void __attribute__((optimize("O0")))  func(){}

6、代码段、堆栈

以Keil5为例:

Code:通常指编译器生成的机器指令,即程序的执行代码,会被存储到ROM(flash)区域
RO-data:只读数据,数据存储在ROM(flash)区域,例如const关键字定义的变量,全局常量,全局字符串
RW-data:可读写数据区域,一定是初始化为非0值的可读写数据,且应用程序可以修改内容,存储在RAM
ZI-data:可读写区域,一定是初始化为0值或未初始化的可读写数据,存储在RAM

Min(ROM) > Code+RO_data+RW_data
Min(RAM) > RW_data + RO_data

专利

1.一种基于椭圆曲线签名,应用于抗错误故障注入实现安全签名的方法

本发明技术关键点
    ECDSA(Elliptic curve digital signature algorithm)是基于椭圆曲线的数字签名算法,它是一种采用公私钥体系实现数字签名的算法,其安全性基于椭圆曲线离散对数问题(ECC)。与传统的数字签名算法(如DSA和RSA)相比,ECDSA使用较短的密钥却能达到相同的安全级别,且计算速度更快,签名更加短小,在密码学和网络安全领域得到广泛应用。
    很多已有的设备如通用MCU上以集成硬件ECC点乘加速器,通过软硬件结合方式,实现ECDSA签名算法。在实际应用中,如果教科书式按步骤实现ECDSA,存在安全风险,攻击者可通过错误注入ECC点乘加速器,导致私钥泄露,本发明公开一种安全的签名方法,所述方法包括,在执行点乘结束后,引入随机标量执行一次点加,结合一次点校验,结果对比校验来判断是否受到攻击,解决ECC点乘硬件加速器被错误故障注入导致私钥泄露的问题,提高签名的安全性.

现有技术方案
    现有技术问题在于只进行点校验,实际是依旧有风险,点校验只保证点乘结果在曲线上,但没有保证点乘结果与签名随机生成的标量k之间的绑定关系,存在安全缺陷。本发明则利用已有的ECC硬件加速器,增加安全防护,引入随机标量进行一次点加校验,再结合一次点校验,最终保证硬件加速器中参与点乘计算的参数k和椭圆曲线参数G、以及计算流程均正确执行,防止私钥泄露,提高ECDSA签名的安全性。

错误故障注入导致私钥泄露的原理:d=h(s-sf)[(sf* r - s * rf)-1] mod p
参考文档:https://asecuritysite.com/ecdsa/ecd7

首先对于签名有两步:
1.r = [k]G
2.s = k-1(h+r * d)

给定两组签名,一组正确,一组错误(使r值发生错误)
sf = k-1 (h + rf * d) ①
s = k-1(h + r * d) ②

②-①,得到:
s - sf = k-1(h + d * r) - k-1(h+d * rf)
s - sf = k-1(d * r - d * rf)
k(s - sf) = d * r - d * rf
k = (d * r - d * rf)(s - sf)-1 ③

把③代入签名公式:
s = k-1(h+r * d)
s = (d * r - d * rf)-1(s - sf)(h+r * d)
s(d * r - d * rf) = (s - sf)(h+r * d)
sdr - sdrf = sh + sdr - hsf - sfdr
-sdrf = sh - hsf - sfdr
sfdr - sdrf = sh - hsf
d(sfr - srf) = h(s-sf)
d = h(s-sf)(sfr - srf)-1 (mod p)

//测试Demo
//secp256k1
//privkey
FF890E39083268830B89118290750B5B2FD511C5CB3D501E7F21AD79095E4B69
//pubkey
0401FF01346CEC0529FCF4F18C355F4E9B2F02DCC05E4141BEC0F910468D13A4F9A6202628700E19E305C22C9E05A6D1515813DECDC31405F4E0E54F47B8E571D3

//sign-true
//e
1122
//k
5566
//r
DAFA79E07CA3928CE4AA1D65984F335F08454EDA1819A10D978D1CB79806434A 
//s
FD7E0AF3DDDDD592918452501D3EA4EEB1D889C70E0A6E1BC7F9A3F6A9EA99B5


============================ 假设故障导致计算结果错误(非曲线点) ================================
//sign-false//change r,keep k
//e
1122
//k
5566
//r=[k]G-x
DAFA79E07CA3928CE4AA1D65984F335F08454EDA1819A10D978D1CB79806434A
//change rf=[k]G-x
DAFA79E07CA3928CE4AA1D65984F335F08454EDA1819A10D978D1CB798064340 //k-A
//s=k^-1 * (e+rf * dA) mod n
//k^-1 mod n
A4AFD5A845227F4324E2CBB436CD4BE65B91D11C6C2BD119A06D4A268C44888
//rf*dA mod n
DE80C79ABC54AD9876B2B687CF26F1FF865ED5E239031D4AEA46207E413A191D
//(e+rf*dA) mod n
DE80C79ABC54AD9876B2B687CF26F1FF865ED5E239031D4AEA46207E413A2A3F
//sf=k^-1 * (e+rf * dA) mod n
537D4D094AFF8F1416DCECF1DC5A63CCEDCE2F67FB6D4813A3AD22E28F3170A4


//r
DAFA79E07CA3928CE4AA1D65984F335F08454EDA1819A10D978D1CB79806434A
//s
FD7E0AF3DDDDD592918452501D3EA4EEB1D889C70E0A6E1BC7F9A3F6A9EA99B5
//rf
DAFA79E07CA3928CE4AA1D65984F335F08454EDA1819A10D978D1CB798064340
//sf
537D4D094AFF8F1416DCECF1DC5A63CCEDCE2F67FB6D4813A3AD22E28F3170A4



//d=e*(s-sf)*[(sf*r - s*rf)^-1] mod p
//sf*r
1DA41944C4EA3AC2C8BB3ED77C09A8E75BBB5156F94DD4B83FA8D6389359612
//s*rf
8A03A97E00A969F17303F071A5EF9560CB7D710BF3D42D582F47CE68C2349556
//sf*r - s*rf
77D698164BA539BAB987C37BD1D1052C64ED20F02B09502F14851D87973741FD
//(sf*r - s*rf)^-1
2BDB7C3ABDB7C3ABDB7C3ABDB7C3ABDB447F328B89FC5E21419B4EE87EF50D90


//s-sf
AA00BDEA92DE467E7AA7655E40E44121C40A5A5F129D2608244C81141AB92911
//e*(s-sf)
A0B5CCE84433C2F167F6B8D3C6A3F2F5344F90D30E46EFD7EDC7F8B16F2B51E2
//d=e*(s-sf)*[(sf*r - s*rf)^-1] mod p
FF890E39083268830B89118290750B5B2FD511C5CB3D501E7F21AD79095E4B69





============================ 假设故障导致计算结果错误(结果不在曲线点上) ================================
//sign-false//change r,keep k
//e
1122
//k
5566
//r=[k]G-x
DAFA79E07CA3928CE4AA1D65984F335F08454EDA1819A10D978D1CB79806434A
//change rf=[k]G-x
DAFA79E07CA3928CE4AA1D65984F335F08454EDA1819A10D978D1CB798064340 //之前的安全方案只考虑点校验,但没有校验点与k的关系
//s=k^-1 * (e+rf * dA) mod n
//k^-1 mod n
A4AFD5A845227F4324E2CBB436CD4BE65B91D11C6C2BD119A06D4A268C44888
//rf*dA mod n
DE80C79ABC54AD9876B2B687CF26F1FF865ED5E239031D4AEA46207E413A191D
//(e+rf*dA) mod n
DE80C79ABC54AD9876B2B687CF26F1FF865ED5E239031D4AEA46207E413A2A3F
//sf=k^-1 * (e+rf * dA) mod n
537D4D094AFF8F1416DCECF1DC5A63CCEDCE2F67FB6D4813A3AD22E28F3170A4


//r
DAFA79E07CA3928CE4AA1D65984F335F08454EDA1819A10D978D1CB79806434A
//s
FD7E0AF3DDDDD592918452501D3EA4EEB1D889C70E0A6E1BC7F9A3F6A9EA99B5
//rf
DAFA79E07CA3928CE4AA1D65984F335F08454EDA1819A10D978D1CB798064340
//sf
537D4D094AFF8F1416DCECF1DC5A63CCEDCE2F67FB6D4813A3AD22E28F3170A4



//d=e*(s-sf)*[(sf*r - s*rf)^-1] mod p
//sf*r
1DA41944C4EA3AC2C8BB3ED77C09A8E75BBB5156F94DD4B83FA8D6389359612
//s*rf
8A03A97E00A969F17303F071A5EF9560CB7D710BF3D42D582F47CE68C2349556
//sf*r - s*rf
77D698164BA539BAB987C37BD1D1052C64ED20F02B09502F14851D87973741FD
//(sf*r - s*rf)^-1
2BDB7C3ABDB7C3ABDB7C3ABDB7C3ABDB447F328B89FC5E21419B4EE87EF50D90


//s-sf
AA00BDEA92DE467E7AA7655E40E44121C40A5A5F129D2608244C81141AB92911
//e*(s-sf)
A0B5CCE84433C2F167F6B8D3C6A3F2F5344F90D30E46EFD7EDC7F8B16F2B51E2
//d=e*(s-sf)*[(sf*r - s*rf)^-1] mod p
FF890E39083268830B89118290750B5B2FD511C5CB3D501E7F21AD79095E4B69



============================ 假设故障导致计算结果错误(结果在曲线上,但非对应k) ================================
//sign-false//change r,keep k
//e
1122
//k
5566
//r=[k]G-x
DAFA79E07CA3928CE4AA1D65984F335F08454EDA1819A10D978D1CB79806434A
//change rf=[k]G-x //
DAFA79E07CA3928CE4AA1D65984F335F08454EDA1819A10D978D1CB798064340
//s=k^-1 * (e+rf * dA) mod n
//k^-1 mod n
A4AFD5A845227F4324E2CBB436CD4BE65B91D11C6C2BD119A06D4A268C44888
//rf*dA mod n
DE80C79ABC54AD9876B2B687CF26F1FF865ED5E239031D4AEA46207E413A191D
//(e+rf*dA) mod n
DE80C79ABC54AD9876B2B687CF26F1FF865ED5E239031D4AEA46207E413A2A3F
//sf=k^-1 * (e+rf * dA) mod n
537D4D094AFF8F1416DCECF1DC5A63CCEDCE2F67FB6D4813A3AD22E28F3170A4



//r
DAFA79E07CA3928CE4AA1D65984F335F08454EDA1819A10D978D1CB79806434A
//s
FD7E0AF3DDDDD592918452501D3EA4EEB1D889C70E0A6E1BC7F9A3F6A9EA99B5
//rf
DAFA79E07CA3928CE4AA1D65984F335F08454EDA1819A10D978D1CB798064340
//sf
537D4D094AFF8F1416DCECF1DC5A63CCEDCE2F67FB6D4813A3AD22E28F3170A4



//d=e*(s-sf)*[(sf*r - s*rf)^-1] mod p
//sf*r

//s*rf
8A03A97E00A969F17303F071A5EF9560CB7D710BF3D42D582F47CE68C2349556
//sf*r - s*rf
77D698164BA539BAB987C37BD1D1052C64ED20F02B09502F14851D87973741FD
//(sf*r - s*rf)^-1
2BDB7C3ABDB7C3ABDB7C3ABDB7C3ABDB447F328B89FC5E21419B4EE87EF50D90


//s-sf
AA00BDEA92DE467E7AA7655E40E44121C40A5A5F129D2608244C81141AB92911
//e*(s-sf)
A0B5CCE84433C2F167F6B8D3C6A3F2F5344F90D30E46EFD7EDC7F8B16F2B51E2
//d=e*(s-sf)*[(sf*r - s*rf)^-1] mod p
FF890E39083268830B89118290750B5B2FD511C5CB3D501E7F21AD79095E4B69



安全方案:
1.抵抗中间值故障注入(增加安全性),硬件点乘算子 //外部传入k值
    点乘运算结束后,在计算签名值s部分时,对计算结果[k]G做一次曲线上点校验
    a)点校验
    b)随机标量点乘校验
    c)[k+N]G = [k]G

2、一种利用已有模加模乘运算加速器实现大数取模的方法

    很多已有的设备如通用MCU上已集成了硬件模运算加速器,包括模加、模乘、模逆、点乘、点加、模幂,用于参与实现经典的密码运算如Ed25519签名等。对于Ed25519签名,集成的硬件加速器算子操作数位宽,往往只支持Ed25519标准曲线最大数据位宽256bit。
本发明提出了一种方法,将大数从低位到高位,按模数的实际有效位宽为单位,切分为若干分组,利用已有的支持模数位宽的模加、模乘硬件加速器,实现大数求余计算,,减少硬件电路面积成本,充分发挥已有硬件加速器的计算潜力。

3、一种利用有限位宽硬件加速器实现更大位宽RSA CRT运算的方法

在支持1024位宽硬件加速器(模乘、模加、模幂)基础上实现更高位宽RSA1026-RSA2048 CRT运算
在支持2048位宽硬件加速器(模乘、模加、模幂)基础上实现更高位宽RSA2048-RSA4096 CRT运算
在支持4096位宽硬件加速器(模乘、模加、模幂)基础上实现更高位宽RSA4096-RSA8192 CRT运算
本发明提出了一种方法,将大数从低位到高位,按模数的实际有效位宽为单位,切分为若干分组,利用已有的支持模数位宽的模加、模乘硬件加速器,实现大数求余计算,同时结合幂运算性质,减少硬件电路面积成本,充分发挥已有硬件加速器的计算潜力。
p、q、dp、dq、qinv位宽:nbit
c位宽:2*nbit

m1 = (c mod p)^dp
//Demo
//p
E885BA6181AD24D7B8AA1F1A92FD2F3841EEC04120CD06142E96E99D8370725AA32C10D0134FB2B6C20771E27F8CBDBD858073DA3A0E13C6F801AAF2D2AE8588BF293727A80209C18275CE82745CF8D04BC43C1A12880AB73AD7BD0A7DDB8042634CAE44AE2635B40F52AED1A0293A158769F2F0BFDD2B0C8A6B460522914561
//q CB63B7462B37640A4940C39CA60F6E843B9EBC26EEFB4E3B62C40A74AB157E25594B7781C5AA885ED509480144E6841BC2DF87B8444940BEE7EF842ECC4952158D43D8AE46E9FF06198A35DA4D882882BEEA80D59CC7DC2D29803E503FCAAAC4058BBF4A9CBF4732E2F66DF918CCA8A50E372ACEAA6526E50CFD21C1053410AB
//dp
AAD75691BDCD917C4E59F0707DFF386A8546FE4ADBE6C7C89C529562FDE7E89D5FB648C418EC3DCAB9D4EE8014673C21BBAD1D7201A5C6323296471C125FD80D1927173733E20B78FA79C4396575C9FA8130B55812B591E9FE79F49378D585EF41365DF4685FFAA5497CDEA5769FD3E56D9112C680B1E7A7572C6A4DEB900101
//dq
3801F293290D6CC8045A0843D7DAC20539E1C1503B590AB701C0985C7E4C41DB891666075F66517D43CEF57C1BFC1A7F068FC570F1A4F6665BBA323D2CD637FB65563C91E9474089EE7887D1E0ADD0F2081ECD718565E7F4ABCBFBDCDF6430D81FEAD6B776289A33A21A58385322F081DB4347CF774EB98233AC7522110BC057
//u
205850F94496A6F922FF11B140DF253A09AB49661BC9EF4BF5C4CC2D8D15CD1A679652D9E8D707014680401E447EFEBA9E8097C43816A36EDAB26B37D7FD800B6F7EE8488F26CCC4AF5E713FC7591A19C9D3FAF1CFB58F355D2B7216E576FFCE18B9011F7C14F75C08950110101AAD59AAD889B591441A1A7E699C8FB9B333FE

//msg-2048
11BC9CF9B996E26F89D4DD8C99C07017365EF703C5AC3C0B8B16BF37A1A8E53047CE5246E9309880A4E788A9B814A87DAB577618176E7897BBA7964C687D0E4558320467EE8753AEE0B0DF0503AF014FFD197EE36A9EE474CE495616C39969ACFEA661F6745023689E752ADB9CE4ACE52C00EE11C352127E29DAF2CEB18DD6956D2EDF3BAD13DD595B107E7D5529AB407B50679AF51DBE4908B350115BBC1DB16DB0BF225C3937AB672DF6654A99883A437EA9F00DA08D35368C420DD9DB34A10B0892C9BDC5B628F0373D8930177C74D6F87FF2A2E1BACDE146F7CE25E5A4BD5882B99A94E8ACA0570EF14AF9A909DA0D3AC254DDC9A21A6313C295281367CB

4、一种基于现有硬件加速器应用于RSA-CRT来实现安全运算的方法

本发明提供了一种RSA-CRT运算方法和装置,应用现有的模运算硬件加速器,真随机数发生器将运算所参与的底数、模数、指数进行加扰,用于提高数据运算的安全性,抵御侧信道攻击。所述方法包括:获取随机数r、随机数k1、随机数h1、随机数y1、随机数k2、随机数h2、随机数q1、随机数y2,底数c、公钥e,私钥五元组(p、q、dp、dq、qInv),模数N。计算第一值,所述第一值为;计算第二值,所述第二值为 mod (y1*(p-p1)+y1*p1);计算第三值,所述第三值为;计算第四值,所述第四值为 mod (y2*(q-q1)+y2*q1);计算第五值,所述第五值为;计算第六值,所述第六值为Δ = (m1-m2) mod (y1*(p-p1)+y1*p1);计算第七值,所述第七值为;计算第八值,所述第八值为。第八值为最终的RSA-CRT运算的结果。所述的数据防攻击方法,包括运算所参与所有的底数、模数、指数随机化和扩大了模数和指数的实际有效比特长度,增加分析破解难度,最大程度降低攻击者通过侧信道分析获得RSA私钥的关键信息的可能,提高应用RSA-CRT运算安全性。

4、一种SM9预计算来加速签名运算的方法

提前预计消息、双线性对

一种在资源受限设备上利用已有hash加速器实现高速LMS算法的方法


模块

RSA-CRT格式

//CR五元组,https://www.bilibili.com/read/cv6250533
p //素数
q //素数
dp // D mod (p-1),D为私钥
dq // D mod (p-1),D为公钥
qinv //q*qinv 与 p互质

注:e //固定65537

密码原语

哈希函数 hash function
单向函数 one-way function
消息认证码 message authentication code (MAC)
数字签名 digital signature承诺 commitment
伪随机数生成器 pesudorandom generator (PRG)
伪随机函数 pesudorandom function (PRF)
抗碰撞哈希 collision-resistant hash (CRH)
通用单向哈希函数 universal one-way hash  function (UOWHF)
全同态加密 fully homomorphic encryption (FHE)
多方安全计算 multiparty computation (MPC)
基于身份的加密 identity-based encryption (IBE)
基于属性的加密 attribute-based encryption (ABE)
混淆 obfuscation

MSB、LSB

MSB:最高有效位,位于二进制数最左侧
LSB:最低有效位,位于二进制数最右侧

XOR-C语言

#include <stdio.h>
#include <string.h>

int main(void) {
  char str1[33] = {"CDB6411E3CDC9B4F5D9258B69EE7F091"};
  char str2[33] = {"08090A0B0C0D0E0F1011121314151617"};
  int  a,b;

  for(int i = 0; i<strlen(str1); i++)
  {
    a = str1[i];
    b = str2[i];
    if(a >= 'A')
      a = a - 'A'+10;
    else
      a = a - '0';
    if(b >= 'A')
      b = b - 'A'+10;
    else
      b = b - '0';
    if((a^b) >= 10)
    printf("%c",(a^b)+55);
    else
    printf("%0x",a^b);
  }

  return 0;
}

同态加密

1、如果满足f(A)+F(B) = f(A+B),加法同态
2、如果满足f(A) * F(B) = f(A * B),乘法同态
3、如果同时满足加法同态、乘法同态,称为全同态加密

软件测试

代码覆盖率:
(1)行覆盖率又称为语句覆盖率,指已经被执行到的语句占总可执行语句(不包含类似 C++ 的头文件声明、代码注释、空行等等)的百分比。这是最常用也是要求最低的覆盖率指标。实际项目中通常会结合判定覆盖率或者条件覆盖率一起使用。

(2)判定覆盖又称分支覆盖,用以度量程序中每一个判定的分支是否都被测试到了,即代码中每个判断的取真分支和取假分支是否各被覆盖至少各一次。比如,对于 if(a>0 && b>0),就要求覆盖“a>0 && b>0”为 TURE 和 FALSE 各一次。

(3)条件覆盖是指,判定中的每个条件的可能取值至少满足一次,度量判定中的每个条件的结果 TRUE 和 FALSE 是否都被测试到了。比如,对于 if(a>0 && b>0),就要求“a>0”取 TRUE 和 FALSE 各一次,同时要求“b>0”取 TRUE 和 FALSE 各一次。

位运算

特定位置0(&):构造一个特定位为0,其他位为1的数,再与被操作数进行&运算
特定位置1(|):构造一个特定位为1,其他位为0的数,再与被操作数进行|运算
特定位置取反():构造一个特定位为1,其他位为0的数,再与被操作数进行运算

//乘2的倍数值,左移
x=x*2 => x=x<<1
x=x*64 => x=x<<6
//除2的倍数值,右移
x=x/2 => x=x>>1
x=x/64 => x=x>>6
//数值转整
x=int(1.232) => x=1.232>>0
//取余数
x=131%4 => x=131&(4-1)
//判断是否偶数
isEven=(i%2) == 0 => isEven=(i&1) == 0
//正负号转换
i=-i => i=~i+1
//交换两个数
a=a^b;b=a^b;a=a^b;

PUF

physical unclonable function,物理不可克隆函数,是一种数字指纹,电路生成一个唯一输出值(私钥,不同时间、温度、工作电压等条件下不变),任何探测或者观测PUF操作的尝试都将改变底层电路特性,这可以防范侵入物理攻击侦测芯片加密功能所使用的唯一值。

特点:唯一、隐匿、稳定、随机。

openssl

openssl一次性安装
1、安装包:https://slproweb.com/products/Win32OpenSSL.html
2、添加环境变量:bin路径
3、cmd 命令教程:https://www.bilibili.com/read/cv14924390/

CMD指令

//生成2048长度的RSA密钥对(公钥在私钥里,所以只会有一个私钥文件,生成非对称密钥对的示例都一样)
openssl genrsa -out test.key 2048

//生成RSA密钥对(使用保护口令:123456保护)
openssl genrsa -aes256 -passout pass:123456  -out protectedByPwd.key 2048

//RSA查看私钥信息(如果私钥有被口令保护则会要求输入口令)
openssl rsa -in test.key -text

//RSA由私钥文件获取公钥文件
openssl rsa -in prvTest.key -pubout -out pubTest.key

//查看公钥信息
openssl rsa -pubin -in pubTest.key -text

//由pkcs1的RSA私钥获得无保护的PKCS8格式私钥
openssl pkcs8 -topk8 -inform PEM -in test.key -outform PEM -nocrypt -out pkcs8Test.key

//生成SM2私钥
openssl ecparam -genkey -name SM2 -out priv_sm2.key

//生成对应SM2公钥
openssl ec -in priv_sm2.key -pubout -out pub_sm2.key

//查看公私钥信息
openssl ec -in priv_sm2.key -text

//

VS调用OpenSSL

接口文档(旧版本openssl):https://www.openssl.net.cn/
接口文档(3.0):https://www.openssl.org/docs/

//VS2019配置
//VC++目录->包含目录
C:\Program Files (x86)\OpenSSL-Win32\include
//VC++目录->库目录
C:\Program Files (x86)\OpenSSL-Win32\lib
//连接器->输入
libssl.lib
libcrypto.lib
//libcrypto-3.dll & libssl-3.dll
放在执行文件同目录下//【目前还没了解如何配置在VS工程里】
//openssl 3.0
#pragma warning(disable : 4996)
//代码配置
#pragma warning(disable : 4996) //解决C4996报错

extern "C"
{
#include <openssl/applink.c> //解决OPENSSL_Uplink(XX,XX): no OPENSSL_Applink报错
}

详情请看OpenSSL_Demo.project

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<openssl/md5.h>

#pragma warning(disable : 4996)

#define _CRT_SECURE_NO_WARNINGS

void getMD5(const char* str, char* result)
{
    MD5_CTX ctx;

    MD5_Init(&ctx);

    MD5_Update(&ctx, str, strlen(str));

    unsigned char md[MD5_DIGEST_LENGTH] = { 0 };
    MD5_Final(md, &ctx);
    //转变为16进制
    for (int i = 0; i < MD5_DIGEST_LENGTH; ++i)
    {
        sprintf(&result[i * 2], "%02x", md[i]);
    }
}

int main()
{
    char result[33] = { 0 };
    getMD5("hello, md5", result);
    printf("md5 value: %s\n", result);
    system("pause");

    return 0;
}

VS快捷键设置

//后退
工具-键盘-视图 向后导航 alt+左箭头

unifdef

https://dotat.at/prog/unifdef/

https://www.ibm.com/docs/es/aix/7.2?topic=u-unifdef-command
句法
unifdef [ -t ] [ -l ] [ -c ] [ -D符号 ] [ -U符号] [ -id符号 ] [ -iu符号 ] [ 文件 ]

物品 描述
-C 补充unifdef命令的操作。也就是说,将保留已删除的行,反之亦然。
-D符号 指定要定义的符号。
文件 指定输入源。
-id符号 unifdef命令不会尝试识别指定 ifdefs 中的注释、单引号或双引号, 但这些行将被复制出来。
-iu符号 unifdef命令不会尝试识别指定 ifdefs 中的注释、单引号或双 引号。这些行不会被复制出来。
-l 导致被删除的行替换为空行而不是被删除。
-t 允许将unifdef命令用于纯文本(而不是 C 代码):unifdef命令不会尝试识别注释、单引号和双引号。
-U符号 指定未定义的符号。
//testA.c
#include<stdio.h>

#define A

int main()
{
#ifdef A
    printf("A \n");
#else
    printf("ndef  A \n");
#endif

#ifdef A
    #ifndef B
        printf("ndef B \n");
    #else
        printf("B \n");
#endif
#else
    #ifndef B
        printf("ndef B \n");
    #else
        printf("B \n");
    #endif
#endif
}

//unifdef -DA testA.c > resA.c
#include<stdio.h>

#define A

int main()
{
    printf("A \n");

    #ifndef B
        printf("ndef B \n");
    #else
        printf("B \n");
#endif
}

//unifdef -DA testA.c > resA.c //删除testA.c中的#define A
//以下输出不符合预期
#include<stdio.h>

int main()
{
    printf("A \n");

    #ifndef B
        printf("ndef B \n");
    #else
        printf("B \n");
#endif
}

//unifdef -DA -UA testA.c > resA.c
#include<stdio.h>

#define A

int main()
{
    printf("ndef  A \n");

    #ifndef B
        printf("ndef B \n");
    #else
        printf("B \n");
    #endif
}
set workDir = %~dp0

::crypto_common
cd  %workDir%
cd .\osr_crypto
unifdef -DA -UA -DB -UB -m testA.c testB.c

Git

//Git bash
//对当前目录下所有文件中的tab替换为空格
sed -i 's/\t/    /g'  *
//对当前目录以及子目录中所有txt文件tab替换为空格
find . -name "*.txt" | xargs.exe sed -i 's/\t/    /g'
//对当前目录以及子目录中所有c文件tab替换为空格
find . -name "*" | xargs.exe sed -i 's/\t/    /g'

大小端

//大小端判断
//大端模式,是指数据的高字节保存在内存的低地址中
#  define DECLARE_IS_ENDIAN \
    const union { \
        long one; \
        char little; \
    } ossl_is_endian = { 1 }

#  define IS_LITTLE_ENDIAN (ossl_is_endian.little != 0)
#  define IS_BIG_ENDIAN    (ossl_is_endian.little == 0)

汉明重量

统计二进制串中1的个数

//统计汉明重量的demo,C快速方法,比遍历判断高效
uint32_t a = 0xfffffff1;
a = (a & 0x55555555u) + ((a >> 1U) & 0x55555555u);
a = (a & 0x33333333u) + ((a >> 2U) & 0x33333333u);
a = (a & 0x0F0F0F0Fu) + ((a >> 4U) & 0x0F0F0F0Fu);

#if 0
a = ((a * (0x01010101u)) >> 24U);
#else
a = (a + (a << 8) + (a << 16) + (a << 24)) >> 24;
#endif

SysTick

//keil5
uint32_t startP(void)
{
    g_irq_timer_counter = 0;
    SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;  //disable SysTick, and this could be ignored. please don't use SysTick->CTRL = 0;
    g_starl = TIMER_MAX_COUNTER-1;
    SysTick_Config(TIMER_MAX_COUNTER);

    //g_starl = SysTick->VAL; //在开启优化后,g_starl有可能获取到0(SysTick_Config中SysTick->VAL=0),非SysTick重载数值的递减值
    return 0;
}

uint32_t endP(uint8_t mode, uint32_t once_bytes, uint32_t round)
{
    g_endl = SysTick->VAL;
    SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;  //disable SysTick, and this could be ignored. please don't use SysTick->CTRL = 0;
    calcP(mode,once_bytes,round);

    return 0;
}

ARMCC和ARM Clang

//ramcc  编译选项添加
--no-multibyte-chars

//arm clang
__clang__  //实际未使用

从armcc切换到clang,需要修改为以下代码(retarget.c)


RISCV

汇编指令

lui - Load Upper Immediate
addi - Add Immediate
jal - Jump And Link
beq - Branch if EQual
sw - Store Word
lw - Load Word
li - Load Immediate 
la - Load Address

bne - Branch if Not Equal

bne rs1, rs2, offset
如果寄存器 rs1 的值不等于寄存器 rs2 的值,则跳转到 PC + offset 处执行

other

商用密码产品认证

1)终端类密码产品
智能IC卡、智能密码钥匙、动态口令系统

2)系统类密码产品
密码机、IPSec VPN、SSL VPN、安全认证

VPN(Virtual Private Network)
IPSecVPN
【特点】网络层、站到站、保护内网整体、颗粒度粗
SSL VPN
【特点】应用层和TCP层之间,端到站、仅开放一主机或端口,颗粒度细

IPSec协议
IKE协议:Internet Key Exchange因特网密钥交换协议
【特点】通讯双方身份鉴别,互发双证书,2个对称的工作密钥,用于加密和完整性校验
AH:Authentication Header鉴别头协议
【特点】只做认证不加密,需要ESP嵌套使用
ESP:Encapsulating Security Payload封装安全载荷
【特点】加密+认证

3)服务类密码产品
数字证书认证系统、电子签章系统
【特点】PKI体系

密评:
密码产品的使用(重点检查要素)、理解信息系统密码应用(重要抓手)

非法素数

表示一些禁止拥有或散布资料的质数,以下素数可以理解为一段代码

//16进制
1F8B0808196C9A3A00036373732D6465736372616D626C652E6300ED56CD8E9B3010BEF314F412E140241B304B95984B1F618F164859205D2B4BB64AA65B56BB79F70EE12736F1A1552F6DB51C6C3CF3CD8F3DDF185E9E55E57EB9BFAFEA5379DC360F4FB5F7FD70525F0F75E5968FDBA3BB3CD5653013EDEB57E2BC39EEF04C5A75001758006100510071003C80647D8BEBBDD4874AA0739FB629A56B678201131840D2BC40ADA42D8FF377DAB20E3341C20B844D109E6BCA48784BCF33B25A120F0DFC901052D8947D1E3C41BDE62816102DEE0CCF102D433F5D417C9576B6A2DB8406E4425BFD785478AC08FB2470CF64925F4F508B88B580ED432821CC8BFE3D92C0B4ED6967002CCB9845C384E7015B30B2D9A4A4D073BD09C425C436E709BAB83C106559844E2282B186391D664E16B4DDED2CE6580288BAE8EF3A0134EF7DF45842628BCE7D01896F4BBCA3A3EF0FE64C76CBBCF0805F122136575926D2ABFCEC9C1DE765203D2878AA91176CCE7994CD39AF9A3FA7FC5EEA3CED4C95C97BD520ED2D64473933196EE1B06A3A7EFF367D4DAEEE9E8F9E4281DAF0B5F2FDBF8BACF13F46D6BD54B918B8F92BD41CDFFA227C5EABACABC46A456E3DE3EDD76745A5F2599E8BBDB92EC60EB941E68521CA714B9696083F5AC2ACC6474B18DEA7AFC7FFDF12555D1E5FBFC1D819F3C6004B6754B33FA481D7E3D244AB8649FD14E7DA10B5E20DFF4E5840DBE46E1C4A8E434D71A0F47CB5B6D03631693B258571B1185DAE386B5D677C191115600E5A1319B7C465F78841FDF92783B1BF304C0A00000823
/*
    memcpy_(msg2 , plain, 32);
    uint32_clear(iv,4);
    ret = ske_hp_dma_init(SKE_ALG_AES_128, SKE_MODE_CBC, SKE_CRYPTO_ENCRYPT, (uint8_t *)k1, 0, (uint8_t *)iv);
    ret |= ske_hp_dma_operate(ctx->ske_xcbc_mac_ctx, msg2, NULL, 0, 0, callback);
    ret |= ske_hp_dma_operate(ctx->ske_xcbc_mac_ctx, msg2, msg2, ctx->ske_xcbc_mac_ctx->block_words, 4, callback);
    print_buf_U8((uint8_t *)msg2, 16, "msg_cbc_res");//5b376580ae2f19afe7219ceef172756f

    memcpy_(msg2 , plain, 16);
    ret |= ske_hp_dma_operate(ctx->ske_xcbc_mac_ctx, msg2, NULL, ctx->ske_xcbc_mac_ctx->block_words, 0, callback);
    printf("ret=%d\n",ret);
    print_buf_U8((uint8_t *)msg2, 16, "msg2");//第二分组密文不输出

    memcpy_(msg2 , plain, 16);
    ret |= ske_hp_dma_operate(ctx->ske_xcbc_mac_ctx, msg2, msg2, ctx->ske_xcbc_mac_ctx->block_words, 4, callback);
    print_buf_U8((uint8_t *)msg2, 32, "msg_cbc_res");//获取的是第2分组的密文,bdd3589af928ac0fca575ba9ee865651 || c1a6a921a23a94065807a08cc8eed06e

    ske_hp_dma_final();//密文没有获取完,还有第三分组密文没获取,影响下面的加密,坑?
*/