引言
SM2是我国自主研发的非对称加密算法(国密算法),于2010年由国密局发布,2017年成为ISO/IEC国际标准。其基于椭圆曲线密码学(ECC),具备安全性高(256位密钥强度相当于RSA-3072位)、运算速度快、密钥短(256位)等优势,广泛应用于数字签名、公钥加密、密钥交换等领域,是金融、政务、物联网等场景的核心安全技术。本文从算法原理到代码实现,系统解析SM2的核心技术。
一、SM2算法基础
1.1 算法概述
SM2属于非对称加密算法,包含三个核心功能:
- 数字签名:基于椭圆曲线离散对数难题(ECDLP),保障数据来源认证与完整性
- 公钥加密:使用接收方公钥加密数据,私钥解密
- 密钥交换:通过ECDH协议生成共享密钥,用于后续对称加密
1.2 核心数学原理
SM2的安全性依赖于椭圆曲线离散对数问题:已知椭圆曲线点P和Q=kP,求k在计算上不可行。其数学基础包括:
- 有限域运算:素域Fp上的加法、乘法、逆运算
- 椭圆曲线方程:标准方程为 ( y^2 = x^3 + ax + b \mod p ),推荐参数为256位素数域
- 点运算:点加(P+Q)、倍点(2P)和标量乘法(kP)构成核心运算
1.3 核心参数
国密推荐曲线参数如下(十六进制):
- 素数p:
FFFFFFFE FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF 00000000 FFFFFFFF FFFFFFFF
- 系数a:
FFFFFFFE ... FFFFFFFC
- 基点G:(x, y)坐标,阶n为
FFFFFFFE ... 39D54123
1.4 算法组件
SM2包含四大核心算法模块:
- 密钥生成:随机数d∈[1,n-1]作为私钥,公钥P=dG
注
:根据上述密钥生成的流程,是先生成私钥d
,然后计算公钥P
,因此在SM2的密钥中,可以根据私钥计算出公钥 - 数字签名:使用私钥对消息哈希值签名,生成(r,s)
- 加密/解密:基于KDF生成会话密钥,结合椭圆曲线点运算
- 密钥协商:通过两次握手协议生成共享密钥
二、算法原理详解
2.1 数字签名流程
签名生成:
- 计算消息哈希值e=HASH(Z_A || M),Z_A为用户身份标识
- 生成随机数k∈[1,n-1],计算点(x1,y1)=kG
- r=(e + x1) mod n,若r=0则重新选k
- s=((1+d_A)^-1 * (k - r*d_A)) mod n,输出签名(r,s)
验签流程:
- 验证r,s∈[1,n-1]
- 计算t=(r+s) mod n,点(x1,y1)=sG + tP_A
- 验证r ≡ (e + x1) mod n
2.2 加密与解密流程
加密:
- 生成随机数k,计算C1=kG(椭圆曲线点)
- 计算S=h*P_B(h为余因子),若S为无穷远点则报错
- 计算(x2,y2)=kP_B,通过KDF生成密钥t
- 密文C2=M⊕t,C3=HASH(x2||M||y2)
- 输出C=C1||C3||C2
解密:
- 验证C1在曲线上,计算S=h*C1
- 计算(x2,y2)=d_B*C1,生成t=KDF(x2||y2)
- 解密M’=C2⊕t,验证C3=HASH(x2||M’||y2)
2.3 密钥交换协议
基于改进的ECDH协议,两轮交互生成共享密钥:
- 双方生成临时密钥对(r_A, R_A)和(r_B, R_B)
- 计算共享点U=r_A(r_B G + P_B) 和 U’=r_B(r_A G + P_A)
- 通过KDF处理U的坐标生成会话密钥
2.4 椭圆曲线数学优化
- 坐标系选择:Jacobian射影坐标减少模逆运算
- 标量乘法优化:采用滑动窗口法、NAF编码降低计算量
- 预计算技术:固定基点G的预计算表加速kG运算
三、算法实现技术
3.1 硬件实现
- 专用芯片:如HS32U2-U,通过SPI接口实现低功耗加密
- FPGA优化:流水线设计SM2协处理器,时钟频率达50MHz,标量乘仅0.869ms
- 抗侧信道技术:随机化NAF窗口、盲化标量防御SPA/DPA攻击
3.2 软件实现
- Bouncy Castle库:Java示例代码展示密钥生成与加密
- OpenSSL引擎:集成SM2到EVP接口,支持标准API调用
- 性能优化技巧:
- 预计算常用点坐标(如16个预计算点加速kP)
- SIMD指令加速有限域乘法(如Intel AVX2)
四、安全性分析
4.1 算法设计安全性
- 数学安全性:基于ECDLP难题,256位密钥抗量子攻击优于RSA-3072
- 抗碰撞性:SM3哈希保障签名与加密数据完整性
- 标准化认证:通过国密GM/T 0003.5-2012、ISO/IEC 14888-3
4.2 抗攻击能力
- 侧信道防护:随机化标量乘顺序、加入噪声指令
- 故障注入防御:双点校验、错误感染技术
- 弱曲线防御:强制验证公共参数防止参数替换攻击
4.3 与RSA/AES对比
特性 | SM2 | RSA-3072 | AES-256 |
---|---|---|---|
密钥长度 | 256位 | 3072位 | 256位 |
安全强度 | 抗量子 | 不抗量子 | 抗量子 |
签名速度 | 0.98ms(FPGA) | 3.2ms | N/A |
适用场景 | 非对称加密 | 传统非对称加密 | 对称加密 |
五、C++跨平台自实现(Header-Only
)
5.1 源码封装
/*****************************************************************************
* Copyright (c) 2025, arbboter. All rights reserved.
* File : filename.cpp
* Desc : 本文件实现SM2模块的核心算法
* - SM2的签名和验签
* - SM2的加密和解密
* Version : 1.0.0.1
* Date : 2025-04-11
* Author : arbboter@hotmail.com
* License : SPDX-License-Identifier: Apache-2.0 (详见 LICENSE 文件)
*
* ------------------------- Revision History --------------------------------
* <Version> <Date> <Author> <Modification Description>
* 1.0.0.1 2025-01-01 arbboter@hotmail.com 初始版本创建
*****************************************************************************/
#ifndef __SM2_H__
#define __SM2_H__
#include <stdio.h>
#include <string.h>
#include <cstdint>
#ifdef _WIN32
#include <windows.h>
#include <bcrypt.h>
#pragma comment(lib, "bcrypt.lib")
#ifndef NT_SUCCESS
#define NT_SUCCESS(status) ((NTSTATUS)(status) >= 0)
#endif
#else
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#endif
#ifndef GET_UINT32_BE
#define GET_UINT32_BE(n,b,i) \
{
\
(n) = ((unsigned int)(b)[(i) ] << 24 ) \
| ((unsigned int)(b)[(i) + 1] << 16 ) \
| ((unsigned int)(b)[(i) + 2] << 8 ) \
| ((unsigned int)(b)[(i) + 3] );\
}
#endif
#ifndef PUT_UINT32_BE
#define PUT_UINT32_BE(n,b,i) \
{
\
(b)[(i) ] = (unsigned char) ( (n) >> 24 );\
(b)[(i) + 1] = (unsigned char) ( (n) >> 16 );\
(b)[(i) + 2] = (unsigned char) ( (n) >> 8 );\
(b)[(i) + 3] = (unsigned char) ( (n) );\
}
#endif
#define FF0(x,y,z) ( (x) ^ (y) ^ (z))
#define FF1(x,y,z) (((x) & (y)) | ( (x) & (z)) | ( (y) & (z)))
#define GG0(x,y,z) ( (x) ^ (y) ^ (z))
#define GG1(x,y,z) (((x) & (y)) | ( (~(x)) & (z)) )
#define SHL(x,n) (((x) & 0xFFFFFFFF) << n)
#define ROTL(x,n) (SHL((x),n) | ((x) >> (32 - n)))
#define P0(x) ((x) ^ ROTL((x),9) ^ ROTL((x),17))
#define P1(x) ((x) ^ ROTL((x),15) ^ ROTL((x),23))
#define GETU32(p) ((uint32_t)(p)[0] << 24 | (uint32_t)(p)[1] << 16 | (uint32_t)(p)[2] << 8 | (uint32_t)(p)[3])
#define PUTU32(p,V) ((p)[0] = (uint8_t)((V) >> 24), (p)[1] = (uint8_t)((V) >> 16), (p)[2] = (uint8_t)((V) >> 8), (p)[3] = (uint8_t)(V))
#define SM2_MIN_PLAINTEXT_SIZE 1
#define SM2_MAX_PLAINTEXT_SIZE 3072
#define SM2_DEFAULT_ID "1234567812345678"
#define RAND_MAX_BUF_SIZE 256
#ifndef INT_MAX
#define INT_MAX 2147483647
#endif
#define ASN1_TAG_INTEGER 2
#define ASN1_TAG_OCTET_STRING 4
#define ASN1_TAG_SEQUENCE 0x30
typedef uint64_t SM2_BN[8];
typedef SM2_BN SM2_Fp;
typedef SM2_BN SM2_Fn;
typedef struct
{
uint8_t x[32];
uint8_t y[32];
} SM2_POINT;
typedef struct
{
SM2_POINT public_key;
uint8_t private_key[32];
} SM2_KEY;
typedef struct
{
uint8_t r[32];
uint8_t s[32];
} SM2_SIGNATURE;
// 雅可比行列式
typedef struct
{
SM2_BN X;
SM2_BN Y;
SM2_BN Z;
} SM2_JACOBIAN_POINT;
typedef struct
{
SM2_POINT point;
uint8_t hash[32];
uint32_t ciphertext_size;
uint8_t ciphertext[SM2_MAX_PLAINTEXT_SIZE];
} SM2_CIPHERTEXT;
const SM2_BN SM2_P =
{
0xffffffff, 0xffffffff, 0x00000000, 0xffffffff,
0xffffffff, 0xffffffff, 0xffffffff, 0xfffffffe,
};
const SM2_BN SM2_B =
{
0x4d940e93, 0xddbcbd41, 0x15ab8f92, 0xf39789f5,
0xcf6509a7, 0x4d5a9e4b, 0x9d9f5e34, 0x28e9fa9e,
};
const SM2_JACOBIAN_POINT _SM2_G =
{
{
0x334c74c7, 0x715a4589, 0xf2660be1, 0x8fe30bbf,
0x6a39c994, 0x5f990446, 0x1f198119, 0x32c4ae2c,
},
{
0x2139f0a0, 0x02df32e5, 0xc62a4740, 0xd0a9877c,
0x6b692153, 0x59bdcee3, 0xf4f6779c, 0xbc3736a2,
},
{
1, 0, 0, 0, 0, 0, 0, 0,
},
};
const SM2_JACOBIAN_POINT* SM2_G = &_SM2_G;
const SM2_BN SM2_N =
{
0x39d54123, 0x53bbf409, 0x21c6052b, 0x7203df6b,
0xffffffff, 0xffffffff, 0xffffffff, 0xfffffffe,
};
const SM2_BN SM2_ONE = {
1,0,0,0,0,0,0,0 };
const SM2_BN SM2_TWO = {
2,0,0,0,0,0,0,0 };
typedef struct
{
unsigned int total[2]; // number of bytes processed
unsigned int state[8]; // intermediate digest state
unsigned char buffer[64]; // data block being processed
unsigned char ipad[64]; // HMAC: inner padding
unsigned char opad[64]; // HMAC: outer padding
} sm3_context;
class SM3
{
public:
static void Starts(sm3_context* ctx)
{
ctx->total[0] = 0;
ctx->total[1] = 0;
ctx->state[0] = 0x7380166F;
ctx->state[1] = 0x4914B2B9;
ctx->state[2] = 0x172442D7;
ctx->state[3] = 0xDA8A0600;
ctx->state[4] = 0xA96F30BC;
ctx->state[5] = 0x163138AA;
ctx->state[6] = 0xE38DEE4D;
ctx->state[7] = 0xB0FB0E4E;
}
static void Update(sm3_context* ctx, unsigned char* input, size_t ilen)
{
unsigned int fill = 0;
unsigned int left = 0;
if (ilen == 0) {
return; }
left = ctx->total[0] & 0x3F;
fill = 64 - left;
ctx->total[0] += (unsigned int)ilen;
ctx->total[0] &= 0xFFFFFFFF;
if (ctx->total[0] < ilen)
{
ctx->total[1]++;
}
if (left && ilen >= fill)
{
memcpy(ctx->buffer + left, input, fill);
Process(ctx, ctx->buffer);
input += fill;
ilen -= fill;
left = 0;
}
while (ilen >= 64)
{
Process(ctx, input);
input += 64;
ilen -= 64;
}
if (ilen > 0)
{
memcpy(ctx->buffer + left, input, ilen);
}
}
static void Finish(sm3_context* ctx, unsigned char output[32])
{
unsigned char sm3_padding[64] = {
0 };
unsigned int last, padn;
unsigned int high, low;
unsigned char msglen[8];
high = (ctx->total[0] >> 29)
| (ctx->total[1] << 3);
low = (ctx->total[0] << 3);
PUT_UINT32_BE(high, msglen, 0);
PUT_UINT32_BE(low, msglen, 4);
last = ctx->total[0] & 0x3F;
padn = (last < 56) ? (56 - last) : (120 - last);
sm3_padding[0] = '\x80';
Update(ctx, sm3_padding, padn);
Update(ctx, msglen, 8);
PUT_UINT32_BE(ctx->state[0], output, 0);
PUT_UINT32_BE(ctx->state[1], output, 4);
PUT_UINT32_BE(ctx->state[2], output, 8);
PUT_UINT32_BE(ctx->state[3], output, 12);
PUT_UINT32_BE(ctx->state[4], output, 16);
PUT_UINT32_BE(ctx->state[5], output, 20);
PUT_UINT32_BE(ctx->state[6], output, 24);
PUT_UINT32_BE(ctx->state[7], output, 28);
}
private:
static void Process(sm3_context* ctx, unsigned char data[64])
{
unsigned int SS1, SS2, TT1, TT2, W[68], W1[64];
unsigned int A, B, C, D, E, F, G, H;
unsigned int T[64];
unsigned int Temp1, Temp2, Temp3, Temp4, Temp5;
unsigned int n;
for (n = 0; n < 16; n++) {
T[n] = 0x79CC4519; }
for (n = 16; n < 64; n++) {
T[n] = 0x7A879D8A; }
GET_UINT32_BE(W[0], data, 0);
GET_UINT32_BE(W[1], data, 4);
GET_UINT32_BE(W[2], data, 8);
GET_UINT32_BE(W[3], data, 12);
GET_UINT32_BE(W[4], data, 16);
GET_UINT32_BE(W[5], data, 20);
GET_UINT32_BE(W[6], data, 24);
GET_UINT32_BE(W[7], data, 28);
GET_UINT32_BE(W[8], data, 32);
GET_UINT32_BE(W[9], data, 36);
GET_UINT32_BE(W[10], data, 40);
GET_UINT32_BE(W[11], data, 44);
GET_UINT32_BE(W[12], data, 48);
GET_UINT32_BE(W[13], data, 52);
GET_UINT32_BE(W[14], data, 56);
GET_UINT32_BE(W[15], data, 60);
for (n = 16; n < 68; n++)
{
Temp1 = W[n - 16] ^ W[n - 9];
Temp2 = ROTL(W[n - 3], 15);
Temp3 = Temp1 ^ Temp2;
Temp4 = P1(Temp3);
Temp5 = ROTL(W[n - 13], 7) ^ W[n - 6];
W[n] = Temp4 ^ Temp5;
}
for (n = 0; n < 64; n++)
{
W1[n] = W[n] ^ W[n + 4];
}
A = ctx->state[0];
B = ctx->state[1];
C = ctx->state[2];
D = ctx->state[3];
E = ctx->state[4];
F = ctx->state[5];
G = ctx->state[6];
H = ctx->state[7];
for (n = 0; n < 16; n++)
{
SS1 = ROTL((ROTL(A, 12) + E + ROTL(T[n], n)), 7);
SS2 = SS1 ^ ROTL(A, 12);
TT1 = FF0(A, B, C) + D + SS2 + W1[n];
TT2 = GG0(E, F, G) + H + SS1 + W[n];
D = C;
C = ROTL(B, 9);
B = A;
A = TT1;
H = G;
G = ROTL(F, 19);
F = E;
E = P0(TT2);
}
for (n = 16; n < 64; n++)
{
SS1 = ROTL((ROTL(A, 12) + E + ROTL(T[n], n)), 7);
SS2 = SS1 ^ ROTL(A, 12);
TT1 = FF1(A, B, C) + D + SS2 + W1[n];
TT2 = GG1(E, F, G) + H + SS1 + W[n];
D = C;
C = ROTL(B, 9);
B = A;
A = TT1;
H = G;
G = ROTL(F, 19);
F = E;
E = P0(TT2);
}
ctx->state[0] ^= A;
ctx->state[1] ^= B;
ctx->state[2] ^= C;
ctx->state[3] ^= D;
ctx->state[4] ^= E;
ctx->state[5] ^= F;
ctx->state[6] ^= G;
ctx->state[7] ^= H;
}
static void Process(unsigned char* input, size_t ilen, unsigned char output[32])
{
sm3_context ctx;
Starts(&ctx);
Update(&ctx, input, ilen);
Finish(&ctx, output);
memset(&ctx, 0, sizeof(sm3_context));
}
static void HmacStarts(sm3_context* ctx, unsigned char* key, size_t keylen)
{
unsigned int i;
unsigned char sum[32];
if (keylen > 64)
{
Process(key, keylen, sum);
keylen = 32;
key = sum;
}
memset(ctx->ipad, 0x36, 64);
memset(ctx->opad, 0x5C, 64);
for (i = 0; i < keylen; i++)
{
ctx->ipad[i] = (unsigned char)(ctx->ipad[i] ^ key[i]);
ctx->opad[i] = (unsigned char)(ctx->opad[i] ^ key[i]);
}
Starts(ctx);
Update(ctx, ctx->ipad, 64);
memset(sum, 0, sizeof(sum));
}
static void HmacUpdate(sm3_context* ctx, unsigned char* input, size_t ilen)
{
Update(ctx, input, ilen);
}
static void HmacFinish(sm3_context* ctx, unsigned char output[32])
{
unsigned int hlen;
unsigned char tmpbuf[32];
hlen = 32;
Finish(ctx, tmpbuf);
Starts(ctx);
Update(ctx, ctx->opad, 64);
Update(ctx, tmpbuf, hlen);
Finish(ctx, output);
memset(tmpbuf, 0, sizeof(tmpbuf));
}
void Hmac(unsigned char* key, size_t keylen, unsigned char* input, size_t ilen, unsigned char output[32])
{
sm3_context ctx;
HmacStarts(&ctx, key, keylen);
HmacUpdate(&ctx, input, ilen);
HmacFinish(&ctx, output);
memset(&ctx, 0, sizeof(sm3_context));
}
};
class SM2
{
public:
int SM2Encrypt(const unsigned char* src, size_t srcLen, unsigned char* dst, size_t* dstLen)
{
const SM2_KEY* pk = &_key;
if (encrypt(pk, src, srcLen, dst, dstLen) != 1)
{
return 0;
}
return 1;
}
int SM2Decrypt(const unsigned char* src, size_t srcLen, unsigned char* dst, size_t* dstLen)
{
const SM2_KEY* sk = &_key;
if (decrypt(sk, src, srcLen, dst, dstLen) != 1)
{
return 0;
}
return 1;
}
int SM2Sign(const unsigned char* src, size_t srcLen, unsigned char* sgn, size_t* sgnLen, bool rawRS = false)
{
const SM2_KEY* sk = &_key;
sm3_context ctx;
if (sign_init(&ctx, sk, SM2_DEFAULT_ID, strlen(SM2_DEFAULT_ID)) != 1)
{
return 0;
}
if (sign_update(&ctx, src, srcLen) != 1)
{
return 0;
}
if (sign_finish(&ctx, sk, sgn, sgnLen, rawRS) != 1)
{
return 0;
}
return 1;
}
int SM2Verify(const unsigned char* src, size_t srcLen, const unsigned char* sgn, size_t sgnLen, bool rawRS = false)
{
const SM2_KEY* sk = &_key;
sm3_context ctx;
if (verify_init(&ctx, sk, SM2_DEFAULT_ID, strlen(SM2_DEFAULT_ID)) != 1)
{
return 0;
}
if (verify_update(&ctx, src, srcLen) != 1)
{
return 0;
}
if (verify_finish(&ctx, sk, sgn, sgnLen, rawRS) != 1)
{
return 0;
}
return 1;
}
bool InitKey(const char* pri_key, const char* pub_key)
{
char pPubKey[256] = {
0};
char pPriKey[256] = {
0 };
if (pub_key && strlen(pub_key) == 130)
{
memcpy(pPubKey, pub_key + 2, 128);
}
if (pub_key && strlen(pub_key) == 128)
{
memcpy(pPubKey, pub_key, 128);
}
if (pri_key && strlen(pri_key) == 64)
{
memcpy(pPriKey, pri_key, 64);
}
if (pPubKey[0] == '\0' && pPriKey[0] == '\0')
{
return false;
}
memset(&_key, 0, sizeof(_key));
// 初始化
size_t nLen = 32;
bool bRet = false;
if (strlen(pPubKey) == 128)
{
Hex2Bin(pPubKey, 64, (char*)_key.public_key.x, nLen);
Hex2Bin(pPubKey + 64, 64, (char*)_key.public_key.y, nLen);
bRet = true;
}
if (strlen(pPriKey) == 64)
{
Hex2Bin(pPriKey, 64, (char*)_key.private_key, nLen);
bRet = true;
}
return bRet;
}
static bool Bin2Hex(const char* pIn, size_t nIn, char* pOut, size_t& nOut)
{
if (pIn == nullptr || nIn == 0 || pOut == nullptr || nOut < 2 * nIn)
{
return false;
}
auto ps = (const unsigned char*)pIn;
auto pe = ps + nIn;
const char* hex = "0123456789abcdef";
auto p = (unsigned char*)pOut;
for (; ps < pe; ++ps, p += 2)
{
*p = hex[*ps >> 4];
*(p + 1) = hex[*ps & 0xf];
}
nOut = 2 * nIn;
return true;
}
static bool Hex2Bin(const char* pIn, size_t nIn, char* pOut, size_t& nOut)
{
if (pIn == nullptr || nIn == 0 || (nIn&1) || pOut == nullptr || nOut < nIn/2)
{
return false;
}
const unsigned char* ps = (const unsigned char*)pIn;
const unsigned char* pe = ps + nIn;
unsigned char* p = (unsigned char*)pOut;
for (; ps + 1 < pe; ps += 2, ++p)
{
*p = (hex2int(*ps) << 4) | (hex2int(*(ps + 1)));
}
nOut = nIn / 2;
return true;
}
private:
SM2_KEY _key;
private:
static unsigned char hex2int(unsigned char c)
{
if (c >= 'a' && c <= 'f')
{
return (c - 'a' + 10);
}
else if (c >= 'A' && c <= 'F')
{
return (c - 'A' + 10);
}
else
{
return c - '0';
}
}
void bn_to_bytes(const SM2_BN a, uint8_t out[32])
{
int i = 0;
for (i = 7; i >= 0; i--)
{
uint32_t ai = (uint32_t)a[i];
PUTU32(out, ai);
out += sizeof(uint32_t);
}
}
void bn_from_bytes(SM2_BN r, const uint8_t in[32])
{
int i = 0;
for (i = 7; i >= 0; i--)
{
r[i] = GETU32(in);
in += sizeof(uint32_t);
}
}
void bn_set_word(SM2_BN r, uint32_t a)
{
int i = 0;
r[0] = a;
for (i = 1; i < 8; i++)
{
r[i] = 0;
}
}
void jacobian_point_from_bytes(SM2_JACOBIAN_POINT* P, const uint8_t in[64])
{
bn_from_bytes(P->X, in);
bn_from_bytes(P->Y, in + 32);
bn_set_word(P->Z, 1);
}
int rand_bytes(uint8_t* buf, size_t len)
{
if (!len)
{
return 0;
}
#ifdef _WIN32
/* Windows 实现 */
NTSTATUS status = BCryptGenRandom(
NULL,
buf,
(ULONG)len,
BCRYPT_USE_SYSTEM_PREFERRED_RNG
);
return NT_SUCCESS(status) ? 1 : -1;
#else
/* 类 Unix 实现 */
int fd = open("/dev/urandom", O_RDONLY);
if (fd == -1) return -1;
ssize_t bytes_read = 0;
while (bytes_read < (ssize_t)len)
{
ssize_t n = read(fd, buf + bytes_read, len - bytes_read);
if (n == -1)
{
// 被中断信号打断则重试
if (errno == EINTR) continue;
close(fd);
return -1;
}
// 永远不会触发(字符设备会持续提供数据)
if (n == 0)
{
close(fd);
return -1;
}
bytes_read += n;
}
close(fd);
return 1;
#endif
}
int bn_cmp(const SM2_BN a, const SM2_BN b)
{
int i;
for (i = 7; i >= 0; i--) {
if (a[i] > b[i])
return 1;
if (a[i] < b[i])
return -1;
}
return 0;
}
int bn_rand_range(SM2_BN r, const SM2_BN range)
{
uint8_t buf[32];
do
{
if (rand_bytes(buf, sizeof(buf)) != 1)
{
return -1;
}
bn_from_bytes(r, buf);
} while (bn_cmp(r, range) >= 0);
return 1;
}
int bn_is_zero(const SM2_BN a)
{
int i = 0;
for (i = 0; i < 8; i++)
{
if (a[i] != 0)
{
return 0;
}
}
return 1;
}
int bn_is_one(const SM2_BN a)
{
int i;
if (a[0] != 1)
return 0;
for (i = 1; i < 8; i++) {
if (a[i] != 0)
return 0;
}
return 1;
}
void bn_sub(SM2_BN ret, const SM2_BN a, const SM2_BN b)
{
int i;
SM2_BN r;
r[0] = ((uint64_t)1 << 32) + a[0] - b[0];
for (i = 1; i < 7; i++) {
r[i] = 0xffffffff + a[i] - b[i] + (r[i - 1] >> 32);
r[i - 1] &= 0xffffffff;
}
r[i] = a[i] - b[i] + (r[i - 1] >> 32) - 1;
r[i - 1] &= 0xffffffff;
memcpy(ret, r, sizeof(SM2_BN));
}
int fn_rand(SM2_BN r)
{
if (bn_rand_range(r, SM2_N) != 1)
{
return -1;
}
return 1;
}
void fp_mul(SM2_Fp r, const SM2_Fp a, const SM2_Fp b)
{
int i = 0, j = 0;
uint64_t s[16] = {
0 };
SM2_BN d = {
0 };
uint64_t u = 0;
// s = a * b
for (i = 0; i < 8; i++)
{
u = 0;
for (j = 0; j < 8; j++)
{
u = s[i + j] + a[i] * b[j] + u;
s[i + j] = u & 0xffffffff;
u >>= 32;
}
s[i + 8] = u;
}
r[0] = s[0] + s[8] + s[9] + s[10] + s[11] + s[12] + ((s[13] + s[14] + s[15]) << 1);
r[1] = s[1] + s[9] + s[10] + s[11] + s[12] + s[13] + ((s[14] + s[15]) << 1);
r[2] = s[2];
r[3] = s[3] + s[8] + s[11] + s[12] + s[14] + s[15] + (s[13] << 1);
r[4] = s[4] + s[9] + s[12] + s[13] + s[15] + (s[14] << 1);
r[5] = s[5] + s[10] + s[13] + s[14] + (s[15] << 1);
r[6] = s[6] + s[11] + s[14] + s[15];
r[7] = s[7] + s[8] + s[9] + s[10] + s[11] + s[15] + ((s[12] + s[13] + s[14] + s[15]) << 1);
for (i = 1; i < 8; i++)
{
r[i] += r[i - 1] >> 32;
r[i - 1] &= 0xffffffff;
}
d[2] = s[8] + s[9] + s[13] + s[14];
d[3] = d[2] >> 32;
d[2] &= 0xffffffff;
bn_sub(r, r, d);
while (bn_cmp(r, SM2_P) >= 0)
{
bn_sub(r, r, SM2_P);
}
}
void fp_sqr(SM2_Fp r, const SM2_Fp a)
{
fp_mul(r, a, a);
}
void fp_inv(SM2_Fp r, const SM2_Fp a)
{
SM2_BN a1;
SM2_BN a2;
SM2_BN a3;
SM2_BN a4;
SM2_BN a5;
int i;
fp_sqr(a1, a);
fp_mul(a2, a1, a);
fp_sqr(a3, a2);
fp_sqr(a3, a3);
fp_mul(a3, a3, a2);
fp_sqr(a4, a3);
fp_sqr(a4, a4);
fp_sqr(a4, a4);
fp_sqr(a4, a4);
fp_mul(a4, a4, a3);
fp_sqr(a5, a4);
for (i = 1; i < 8; i++) fp_sqr(a5, a5);
fp_mul(a5, a5, a4);
for (i = 0; i < 8; i++) fp_sqr(a5, a5);
fp_mul(a5, a5, a4);
for (i = 0; i < 4; i++) fp_sqr(a5, a5);
fp_mul(a5, a5, a3);
fp_sqr(a5, a5);
fp_sqr(a5, a5);
fp_mul(a5, a5, a2);
fp_sqr(a5, a5);
fp_mul(a5, a5, a);
fp_sqr(a4, a5);
fp_mul(a3, a4, a1);
fp_sqr(a5, a4);
for (i = 1; i < 31; i++) fp_sqr(a5, a5);
fp_mul(a4, a5, a4);
fp_sqr(a4, a4);
fp_mul(a4, a4, a);
fp_mul(a3, a4, a2);
for (i = 0; i < 33; i++) fp_sqr(a5, a5);
fp_mul(a2, a5, a3);
fp_mul(a3, a2, a3);
for (i = 0; i < 32; i++) fp_sqr(a5, a5);
fp_mul(a2, a5, a3);
fp_mul(a3, a2, a3);
fp_mul(a4, a2, a4);
for (i = 0; i < 32; i++) fp_sqr(a5, a5);
fp_mul(a2, a5, a3);
fp_mul(a3, a2, a3);
fp_mul(a4, a2, a4);
for (i = 0; i < 32; i++) fp_sqr(a5, a5);
fp_mul(a2, a5, a3);
fp_mul(a3, a2, a3);
fp_mul(a4, a2, a4);
for (i = 0; i < 32; i++) fp_sqr(a5, a5);
fp_mul(a2, a5, a3);
fp_mul(a3, a2, a3);
fp_mul(a4, a2, a4);
for (i = 0; i < 32; i++) fp_sqr(a5, a5);
fp_mul(r, a4, a5);
memset(a1, 0, sizeof(SM2_BN));
memset(a2, 0, sizeof(SM2_BN));
memset(a3, 0, sizeof(SM2_BN));
memset(a4, 0, sizeof(SM2_BN));
memset(a5, 0, sizeof(SM2_BN));
}
void bn_add(SM2_BN r, const SM2_BN a, const SM2_BN b)
{
int i = 0;
r[0] = a[0] + b[0];
for (i = 1; i < 8; i++)
{
r[i] = a[i] + b[i] + (r[i - 1] >> 32);
}
for (i = 0; i < 7; i++)
{
r[i] &= 0xffffffff;
}
}
void fp_add(SM2_Fp r, const SM2_Fp a, const SM2_Fp b)
{
bn_add(r, a, b);
if (bn_cmp(r, SM2_P) >= 0)
{
bn_sub(r, r, SM2_P);
}
}
void fp_sub(SM2_Fp r, const SM2_Fp a, const SM2_Fp b)
{
if (bn_cmp(a, b) >= 0)
{
bn_sub(r, a, b);
}
else
{
SM2_BN t;
bn_sub(t, SM2_P, b);
bn_add(r, t, a);
}
}
void fp_dbl(SM2_Fp r, const SM2_Fp a)
{
fp_add(r, a, a);
}
void fp_tri(SM2_Fp r, const SM2_Fp a)
{
SM2_BN t = {
0 };
fp_dbl(t, a);
fp_add(r, t, a);
}
void fp_div2(SM2_Fp r, const SM2_Fp a)
{
int i = 0;
memcpy(r, a, sizeof(SM2_BN));
if (r[0] & 0x01)
{
bn_add(r, r, SM2_P);
}
for (i = 0; i < 7; i++)
{
r[i] = (r[i] >> 1) | ((r[i + 1] & 0x01) << 31);
}
r[i] >>= 1;
}
void bn_to_bits(const SM2_BN a, char bits[256])
{
int i, j;
uint64_t w;
for (i = 7; i >= 0; i--)
{
w = a[i];
for (j = 0; j < 32; j++)
{
*bits++ = (w & 0x80000000) ? '1' : '0';
w <<= 1;
}
}
}
void fn_add(SM2_Fn r, const SM2_Fn a, const SM2_Fn b)
{
bn_add(r, a, b);
if (bn_cmp(r, SM2_N) >= 0)
{
bn_sub(r, r, SM2_N);
}
}
void fn_sub(SM2_Fn r, const SM2_Fn a, const SM2_Fn b)
{
if (bn_cmp(a, b) >= 0)
{
bn_sub(r, a, b);
}
else
{
SM2_BN t;
bn_add(t, a, SM2_N);
bn_sub(r, t, b);
}
}
/* bn288 only used in barrett reduction */
static int bn288_cmp(const uint64_t a[9], const uint64_t b[9])
{
int i = 0;
for (i = 8; i >= 0; i--)
{
if (a[i] > b[i]) return 1;
if (a[i] < b[i]) return -1;
}
return 0;
}
static void bn288_add(uint64_t r[9], const uint64_t a[9], const uint64_t b[9])
{
int i = 0;
r[0] = a[0] + b[0];
for (i = 1; i < 9; i++)
{
r[i] = a[i] + b[i] + (r[i - 1] >> 32);
}
for (i = 0; i < 8; i++)
{
r[i] &= 0xffffffff;
}
}
static void bn288_sub(uint64_t ret[9], const uint64_t a[9], const uint64_t b[9])
{
int i = 0;
uint64_t r[9] = {
0 };
r[0] = ((uint64_t)1 << 32) + a[0] - b[0];
for (i = 1; i < 8; i++)
{
r[i] = 0xffffffff + a[i] - b[i] + (r[i - 1] >> 32);
r[i - 1] &= 0xffffffff;
}
r[i] = a[i] - b[i] + (r[i - 1] >> 32) - 1;
r[i - 1] &= 0xffffffff;
for (i = 0; i < 9; i++)
{
ret[i] = r[i];
}
}
void fn_mul(SM2_BN ret, const SM2_BN a, const SM2_BN b)
{
SM2_BN r;
static const uint64_t mu[9] =
{
0xf15149a0, 0x12ac6361, 0xfa323c01, 0x8dfc2096, 1, 1, 1, 1, 1,
};
uint64_t s[18] = {
0 };
uint64_t zh[9] = {
0 };
uint64_t zl[9] = {
0 };
uint64_t q[9] = {
0 };
uint64_t w = 0;
int i = 0, j = 0;
/* z = a * b */
for (i = 0; i < 8; i++)
{
s[i] = 0;
}
for (i = 0; i < 8; i++)
{
w = 0;
for (j = 0; j < 8; j++)
{
w += s[i + j] + a[i] * b[j];
s[i + j] = w & 0xffffffff;
w >>= 32;
}
s[i + 8] = w;
}
/* zl = z mod (2^32)^9 = z[0..8]
* zh = z // (2^32)^7 = z[7..15] */
for (i = 0; i < 9; i++)
{
zl[i] = s[i];
zh[i] = s[7 + i];
}
/* q = zh * mu // (2^32)^9 */
for (i = 0; i < 9; i++)
{
s[i] = 0;
}
for (i = 0; i < 9; i++)
{
w = 0;
for (j = 0; j < 9; j++)
{
w += s[i + j] + zh[i] * mu[j];
s[i + j] = w & 0xffffffff;
w >>= 32;
}
s[i + 9] = w;
}
for (i = 0; i < 8; i++)
{
q[i] = s[9 + i];
}
/* q = q * n mod (2^32)^9 */
for (i = 0; i < 17; i++)
{
s[i] = 0;
}
for (i = 0; i < 8; i++)
{
w = 0;
for (j = 0; j < 8; j++)
{
w += s[i + j] + q[i] * SM2_N[j];
s[i + j] = w & 0xffffffff;
w >>= 32;
}
s[i + 8] = w;
}
for (i = 0; i < 9; i++)
{
q[i] = s[i];
}
/* r = zl - q (mod (2^32)^9) */
if (bn288_cmp(zl, q))
{
bn288_sub(zl, zl, q);
}
else
{
uint64_t c[9] = {
0,0,0,0,0,0,0,0,0x100000000 };
bn288_sub(q, c, q);
bn288_add(zl, q, zl);
}
for (i = 0; i < 8; i++)
{
r[i] = zl[i];
}
r[7] += zl[8] << 32;
/* while r >= p do: r = r - n */
while (bn_cmp(r, SM2_N) >= 0)
{
bn_sub(r, r, SM2_N);
}
memcpy(ret, r, sizeof(SM2_BN));
}
void fn_sqr(SM2_BN r, const SM2_BN a)
{
fn_mul(r, a, a);
}
void fn_exp(SM2_BN r, const SM2_BN a, const SM2_BN e)
{
SM2_BN t = {
0 };
uint32_t w = 0;
int i = 0, j = 0;
bn_set_word(t, 1);
for (i = 7; i >= 0; i--)
{
w = (uint32_t)e[i];
for (j = 0; j < 32; j++)
{
fn_sqr(t, t);
if (w & 0x80000000)
{
fn_mul(t, t, a);
}
w <<= 1;
}
}
memcpy(r, t, sizeof(SM2_BN));
}
void fn_inv(SM2_BN r, const SM2_BN a)
{
SM2_BN e = {
0 };
bn_sub(e, SM2_N, SM2_TWO);
fn_exp(r, a, e);
}
void jacobian_point_set_xy(SM2_JACOBIAN_POINT* R, const SM2_BN x, const SM2_BN y)
{
memcpy(R->X, x, sizeof(SM2_BN));
memcpy(R->Y, y, sizeof(SM2_BN));
bn_set_word(R->Z, 1);
}
void jacobian_point_get_xy(const SM2_JACOBIAN_POINT* P, SM2_BN x, SM2_BN y)
{
if (bn_is_one(P->Z))
{
memcpy(x, P->X, sizeof(SM2_BN));
if (y)
{
memcpy(y, P->Y, sizeof(SM2_BN));
}
}
else
{
SM2_BN z_inv;
fp_inv(z_inv, P->Z);
if (y)
{
fp_mul(y, P->Y, z_inv);
}
fp_sqr(z_inv, z_inv);
fp_mul(x, P->X, z_inv);
if (y)
{
fp_mul(y, y, z_inv);
}
}
}
void jacobian_point_init(SM2_JACOBIAN_POINT* R)
{
memset(R, 0, sizeof(SM2_JACOBIAN_POINT));
R->X[0] = 1;
R->Y[0] = 1;
}
int jacobian_point_is_on_curve(const SM2_JACOBIAN_POINT* P)
{
SM2_BN t0 = {
0 };
SM2_BN t1 = {
0 };
SM2_BN t2 = {
0 };
if (bn_is_one(P->Z))
{
fp_sqr(t0, P->Y);
fp_add(t0, t0, P->X);
fp_add(t0, t0, P->X);
fp_add(t0, t0, P->X);
fp_sqr(t1, P->X);
fp_mul(t1, t1, P->X);
fp_add(t1, t1, SM2_B);
}
else
{
fp_sqr(t0, P->Y);
fp_sqr(t1, P->Z);
fp_sqr(t2, t1);
fp_mul(t1, t1, t2);
fp_mul(t1, t1, SM2_B);
fp_mul(t2, t2, P->X);
fp_add(t0, t0, t2);
fp_add(t0, t0, t2);
fp_add(t0, t0, t2);
fp_sqr(t2, P->X);
fp_mul(t2, t2, P->X);
fp_add(t1, t1, t2);
}
if (bn_cmp(t0, t1) != 0)
{
return -1;
}
return 1;
}
void jacobian_point_dbl(SM2_JACOBIAN_POINT* R, const SM2_JACOBIAN_POINT* P)
{
const uint64_t* X1 = P->X;
const uint64_t* Y1 = P->Y;
const uint64_t* Z1 = P->Z;
SM2_BN T1 = {
0 };
SM2_BN T2 = {
0 };
SM2_BN T3 = {
0 };
SM2_BN X3 = {
0 };
SM2_BN Y3 = {
0 };
SM2_BN Z3 = {
0 };
if (bn_is_zero(P->Z))
{
memcpy(R, P, sizeof(SM2_JACOBIAN_POINT));
return;
}
fp_sqr(T1, Z1);
fp_sub(T2, X1, T1);
fp_add(T1, X1, T1);
fp_mul(T2, T2, T1);
fp_tri(T2, T2);
fp_dbl(Y3, Y1);
fp_mul(Z3, Y3, Z1);
fp_sqr(Y3, Y3);
fp_mul(T3, Y3, X1);
fp_sqr(Y3, Y3);
fp_div2(Y3, Y3);
fp_sqr(X3, T2);
fp_dbl(T1, T3);
fp_sub(X3, X3, T1);
fp_sub(T1, T3, X3);
fp_mul(T1, T1, T2);
fp_sub(Y3, T1, Y3);
memcpy(R->X, X3, sizeof(SM2_BN));
memcpy(R->Y, Y3, sizeof(SM2_BN));
memcpy(R->Z, Z3, sizeof(SM2_BN));
}
void jacobian_point_add(SM2_JACOBIAN_POINT* R, const SM2_JACOBIAN_POINT* P, const SM2_JACOBIAN_POINT* Q)
{
const uint64_t* X1 = P->X;
const uint64_t* Y1 = P->Y;
const uint64_t* Z1 = P->Z;
const uint64_t* x2 = Q->X;
const uint64_t* y2 = Q->Y;
SM2_BN T1 = {
0 };
SM2_BN T2 = {
0 };
SM2_BN T3 = {
0 };
SM2_BN T4 = {
0 };
SM2_BN X3 = {
0 };
SM2_BN Y3 = {
0 };
SM2_BN Z3 = {
0 };
if (bn_is_zero(Q->Z))
{
memcpy(R, P, sizeof(SM2_JACOBIAN_POINT));
return;
}
if (bn_is_zero(P->Z))
{
memcpy(R, Q, sizeof(SM2_JACOBIAN_POINT));
return;
}
if (bn_is_one(Q->Z) == 0)
{
return;
}
fp_sqr(T1, Z1);
fp_mul(T2, T1, Z1);
fp_mul(T1, T1, x2);
fp_mul(T2, T2, y2);
fp_sub(T1, T1, X1);
fp_sub(T2, T2, Y1);
if (bn_is_zero(T1))
{
if (bn_is_zero(T2))
{
SM2_JACOBIAN_POINT _Q, * Q = &_Q;
jacobian_point_set_xy(Q, x2, y2);
jacobian_point_dbl(R, Q);
return;
}
else
{
jacobian_point_init(R);
return;
}
}
fp_mul(Z3, Z1, T1);
fp_sqr(T3, T1);
fp_mul(T4, T3, T1);
fp_mul(T3, T3, X1);
fp_dbl(T1, T3);
fp_sqr(X3, T2);
fp_sub(X3, X3, T1);
fp_sub(X3, X3, T4);
fp_sub(T3, T3, X3);
fp_mul(T3, T3, T2);
fp_mul(T4, T4, Y1);
fp_sub(Y3, T3, T4);
memcpy(R->X, X3, sizeof(SM2_BN));
memcpy(R->Y, Y3, sizeof(SM2_BN));
memcpy(R->Z, Z3, sizeof(SM2_BN));
}
void jacobian_point_mul(SM2_JACOBIAN_POINT* R, const SM2_BN k, const SM2_JACOBIAN_POINT* P)
{
char bits[257] = {
0 };
SM2_JACOBIAN_POINT _Q, * Q = &_Q;
SM2_JACOBIAN_POINT _T, * T = &_T;
int i;
if (!bn_is_one(P->Z))
{
SM2_BN x = {
0 };
SM2_BN y = {
0 };
jacobian_point_get_xy(P, x, y);
jacobian_point_set_xy(T, x, y);
P = T;
}
jacobian_point_init(Q);
bn_to_bits(k, bits);
for (i = 0; i < 256; i++)
{
jacobian_point_dbl(Q, Q);
if (bits[i] == '1')
{
jacobian_point_add(Q, Q, P);
}
}
memcpy(R, Q, sizeof(SM2_JACOBIAN_POINT));
}
void jacobian_point_to_bytes(const SM2_JACOBIAN_POINT* P, uint8_t out[64])
{
SM2_BN x = {
0 };
SM2_BN y = {
0 };
jacobian_point_get_xy(P, x, y);
bn_to_bytes(x, out);
bn_to_bytes(y, out + 32);
}
void jacobian_point_mul_generator(SM2_JACOBIAN_POINT* R, const SM2_BN k)
{
jacobian_point_mul(R, k, SM2_G);
}
/* R = t * P + s * G */
void jacobian_point_mul_sum(SM2_JACOBIAN_POINT* R, const SM2_BN t, const SM2_JACOBIAN_POINT* P, const SM2_BN s)
{
SM2_JACOBIAN_POINT _sG, * sG = &_sG;
SM2_BN x = {
0 };
SM2_BN y = {
0 };
/* T = s * G */
jacobian_point_mul_generator(sG, s);
// R = t * P
jacobian_point_mul(R, t, P);
jacobian_point_get_xy(R, x, y);
jacobian_point_set_xy(R, x, y);
// R = R + T
jacobian_point_add(R, sG, R);
}
int sm2_point_is_on_curve(const SM2_POINT* P)
{
SM2_JACOBIAN_POINT T;
jacobian_point_from_bytes(&T, (const uint8_t*)P);
return jacobian_point_is_on_curve(&T);
}
int kdf(const uint8_t* in, size_t inlen, size_t outlen, uint8_t* out)
{
sm3_context ctx;
uint8_t counter_be[4];
uint8_t dgst[32];
uint32_t counter = 1;
size_t len;
while (outlen) {
PUTU32(counter_be, counter);
counter++;
SM3::Starts(&ctx);
SM3::Update(&ctx, (uint8_t*)in, (int)inlen);
SM3::Update(&ctx, counter_be, sizeof(counter_be));
SM3::Finish(&ctx, dgst);
len = outlen < 32 ? outlen : 32;
memcpy(out, dgst, len);
out += len;
outlen -= len;
}
memset(&ctx, 0, sizeof(sm3_context));
memset(dgst, 0, sizeof(dgst));
return 1;
}
static int all_zero(const uint8_t* buf, size_t len)
{
size_t i = 0;
for (i = 0; i < len; i++)
{
if (buf[i])
{
return 0;
}
}
return 1;
}
void gm_memxor(void* r, const void* a, const void* b, size_t len)
{
uint8_t* pr = (uint8_t*)r;
const uint8_t* pa = (const uint8_t*)a;
const uint8_t* pb = (const uint8_t*)b;
size_t i;
for (i = 0; i < len; i++)
{
pr[i] = pa[i] ^ pb[i];
}
}
int asn1_length_to_der(size_t len, uint8_t** out, size_t* outlen)
{
if (len > INT_MAX)
{
return -1;
}
if (!outlen)
{
return -1;
}
if (len < 128)
{
if (out && *out)
{
*(*out)++ = (uint8_t)len;
}
(*outlen)++;
}
else
{
uint8_t buf[4] = {
0 };
int nbytes = 0;
if (len < 256) nbytes = 1;
else if (len < 65536) nbytes = 2;
else if (len < (1 << 24)) nbytes = 3;
else nbytes = 4;
PUTU32(buf, (uint32_t)len);
if (out && *out)
{
*(*out)++ = 0x80 + nbytes;
memcpy(*out, buf + 4 - nbytes, nbytes);
(*out) += nbytes;
}
(*outlen) += 1 + nbytes;
}
return 1;
}
int asn1_length_from_der(size_t* len, const uint8_t** in, size_t* inlen)
{
if (!len || !in || !(*in) || !inlen)
{
return -1;
}
if (*inlen == 0)
{
return -1;
}
if (**in < 128)
{
*len = *(*in)++;
(*inlen)--;
}
else
{
uint8_t buf[4] = {
0 };
size_t nbytes = *(*in)++ & 0x7f;
(*inlen)--;
if (nbytes < 1 || nbytes > 4)
{
return -1;
}
if (*inlen < nbytes)
{
return -1;
}
memcpy(buf + 4 - nbytes, *in, nbytes);
*len = (size_t)GETU32(buf);
*in += nbytes;
*inlen -= nbytes;
}
// check if the left input is enough for reading
if (*inlen < *len)
{
return -2;
}
return 1;
}
int asn1_integer_to_der_ex(int tag, const uint8_t* a, size_t alen, uint8_t** out, size_t* outlen)
{
if (!outlen)
{
return -1;
}
if (!a)
{
return 0;
}
if (alen <= 0 || alen > INT_MAX)
{
return -1;
}
if (out && *out)
{
*(*out)++ = tag;
}
(*outlen)++;
while (*a == 0 && alen > 1)
{
a++;
alen--;
}
if (a[0] & 0x80)
{
asn1_length_to_der(alen + 1, out, outlen);
if (out && *out)
{
*(*out)++ = 0x00;
memcpy(*out, a, alen);
(*out) += alen;
}
(*outlen) += 1 + alen;
}
else
{
asn1_length_to_der(alen, out, outlen);
if (out && *out)
{
memcpy(*out, a, alen);
(*out) += alen;
}
(*outlen) += alen;
}
return 1;
}
//02200000FD0A697866AA1C79237AB65F740E24D9098C0846710EF692E187AA740BEF
//022000009286DCB4F8183AE098FB3817749F46FE15B9822AC20483D4BD037590BA6C
//022100917A0B39FBF089EC08D291A44E8171960F80BE6346A1AC97BCDD0642C69FE801
int asn1_integer_from_der_32(int tag, const uint8_t** a, size_t* alen, const uint8_t** in, size_t* inlen)
{
size_t len = 0;
if (!a || !alen || !in || !(*in) || !inlen)
{
return -1;
}
// tag
if (*inlen == 0 || **in != tag)
{
*a = NULL;
*alen = 0;
return 0;
}
(*in)++;
(*inlen)--;
// length (not zero)
if (asn1_length_from_der(&len, in, inlen) != 1)
{
return -1;
}
if (len == 0)
{
return -1;
}
// check if ASN1_INTEGER is negative
if (**in & 0x80)
{
return -1;
}
// remove leading zero
if (**in == 0 && len == 33)
{
(*in)++;
(*inlen)--;
len--;
}
// return integer bytes
*a = *in;
*alen = len;
*in += len;
*inlen -= len;
return 1;
}
int asn1_header_to_der(int tag, size_t dlen, uint8_t** out, size_t* outlen)
{
if (!outlen)
{
return -1;
}
if (out && *out)
{
*(*out)++ = (uint8_t)tag;
}
(*outlen)++;
(void)asn1_length_to_der(dlen, out, outlen);
return 1;
}
int asn1_type_to_der(int tag, const uint8_t* d, size_t dlen, uint8_t** out, size_t* outlen)
{
if (!outlen)
{
return -1;
}
if (!d)
{
if (dlen)
{
return -1;
}
return 0;
}
// tag
if (out && *out)
{
*(*out)++ = (uint8_t)tag;
}
(*outlen)++;
// length
(void)asn1_length_to_der(dlen, out, outlen);
// data
if (out && *out)
{
memcpy(*out, d, dlen);
*out += dlen;
}
*outlen += dlen;
return 1;
}
int asn1_type_from_der(int tag, const uint8_t** d, size_t* dlen, const uint8_t** in, size_t* inlen)
{
if (!d || !dlen || !in || !(*in) || !inlen)
{
return -1;
}
// tag
if (*inlen == 0 || **in != tag)
{
*d = NULL;
*dlen = 0;
return 0;
}
(*in)++;
(*inlen)--;
// length
if (asn1_length_from_der(dlen, in, inlen) != 1)
{
return -1;
}
// data
*d = *in;
*in += *dlen;
*inlen -= *dlen;
return 1;
}
int asn1_check(int expr)
{
if (expr)
{
return 1;
}
return -1;
}
int asn1_length_is_zero(size_t len)
{
if (len)
{
return -1;
}
return 1;
}
int asn1_length_le(size_t len1, size_t len2)
{
if (len1 > len2)
{
return -1;
}
return 1;
}
int ciphertext_to_der(const SM2_CIPHERTEXT* C, uint8_t** out, size_t* outlen)
{
size_t len = 0;
if (!C)
{
return 0;
}
if (asn1_integer_to_der_ex(ASN1_TAG_INTEGER, C->point.x, 32, NULL, &len) != 1
|| asn1_integer_to_der_ex(ASN1_TAG_INTEGER, C->point.y, 32, NULL, &len) != 1
|| asn1_type_to_der(ASN1_TAG_OCTET_STRING, C->hash, 32, NULL, &len) != 1
|| asn1_type_to_der(ASN1_TAG_OCTET_STRING, C->ciphertext, C->ciphertext_size, NULL, &len) != 1
|| asn1_header_to_der(ASN1_TAG_SEQUENCE, len, out, outlen) != 1
|| asn1_integer_to_der_ex(ASN1_TAG_INTEGER, C->point.x, 32, out, outlen) != 1
|| asn1_integer_to_der_ex(ASN1_TAG_INTEGER, C->point.y, 32, out, outlen) != 1
|| asn1_type_to_der(ASN1_TAG_OCTET_STRING, C->hash, 32, out, outlen) != 1
|| asn1_type_to_der(ASN1_TAG_OCTET_STRING, C->ciphertext, C->ciphertext_size, out, outlen) != 1)
{
return -1;
}
return 1;
}
int ciphertext_from_der(SM2_CIPHERTEXT* C, const uint8_t** in, size_t* inlen)
{
int ret = 0;
const uint8_t* d = nullptr;
size_t dlen = 0;
const uint8_t* x = nullptr;
const uint8_t* y = nullptr;
const uint8_t* hash = nullptr;
const uint8_t* c = nullptr;
size_t xlen = 0, ylen = 0, hashlen = 0, clen = 0;
if ((ret = asn1_type_from_der(ASN1_TAG_SEQUENCE, &d, &dlen, in, inlen)) != 1)
{
return ret;
}
if (asn1_integer_from_der_32(ASN1_TAG_INTEGER, &x, &xlen, &d, &dlen) != 1
|| asn1_length_le(xlen, 32) != 1)
{
return -1;
}
if (asn1_integer_from_der_32(ASN1_TAG_INTEGER, &y, &ylen, &d, &dlen) != 1
|| asn1_length_le(ylen, 32) != 1)
{
return -1;
}
if (asn1_type_from_der(ASN1_TAG_OCTET_STRING, &hash, &hashlen, &d, &dlen) != 1
|| asn1_check(hashlen == 32) != 1)
{
return -1;
}
if (asn1_type_from_der(ASN1_TAG_OCTET_STRING, &c, &clen, &d, &dlen) != 1
|| asn1_length_le(clen, SM2_MAX_PLAINTEXT_SIZE) != 1)
{
return -1;
}
if (asn1_length_is_zero(dlen) != 1)
{
return -1;
}
memset(C, 0, sizeof(SM2_CIPHERTEXT));
memcpy(C->point.x + 32 - xlen, x, xlen);
memcpy(C->point.y + 32 - ylen, y, ylen);
if (sm2_point_is_on_curve(&C->point) != 1)
{
return -1;
}
memcpy(C->hash, hash, hashlen);
memcpy(C->ciphertext, c, clen);
C->ciphertext_size = (uint32_t)clen;
return 1;
}
int signature_to_der(const SM2_SIGNATURE* sig, uint8_t** out, size_t* outlen)
{
size_t len = 0;
if (!sig)
{
return 0;
}
if (asn1_integer_to_der_ex(ASN1_TAG_INTEGER, sig->r, 32, NULL, &len) != 1
|| asn1_integer_to_der_ex(ASN1_TAG_INTEGER, sig->s, 32, NULL, &len) != 1
|| asn1_header_to_der(ASN1_TAG_SEQUENCE, len, out, outlen) != 1
|| asn1_integer_to_der_ex(ASN1_TAG_INTEGER, sig->r, 32, out, outlen) != 1
|| asn1_integer_to_der_ex(ASN1_TAG_INTEGER, sig->s, 32, out, outlen) != 1)
{
return -1;
}
return 1;
}
int signature_from_der(SM2_SIGNATURE* sig, const uint8_t** in, size_t* inlen)
{
int ret = 0;
const uint8_t* d = nullptr;
size_t dlen = 0;
const uint8_t* r = nullptr;
size_t rlen = 0;
const uint8_t* s = nullptr;
size_t slen = 0;
if ((ret = asn1_type_from_der(ASN1_TAG_SEQUENCE, &d, &dlen, in, inlen)) != 1)
{
return ret;
}
if (asn1_integer_from_der_32(ASN1_TAG_INTEGER, &r, &rlen, &d, &dlen) != 1
|| asn1_integer_from_der_32(ASN1_TAG_INTEGER, &s, &slen, &d, &dlen) != 1
|| asn1_length_le(rlen, 32) != 1
|| asn1_length_le(slen, 32) != 1
|| asn1_length_is_zero(dlen) != 1)
{
return -1;
}
memset(sig, 0, sizeof(*sig));
memcpy(sig->r + 32 - rlen, r, rlen);
memcpy(sig->s + 32 - slen, s, slen);
return 1;
}
int do_encrypt(const SM2_KEY* key, const uint8_t* in, size_t inlen, SM2_CIPHERTEXT* out)
{
SM2_BN k = {
0 };
SM2_JACOBIAN_POINT _P, * P = &_P;
SM2_JACOBIAN_POINT _C1, * C1 = &_C1;
SM2_JACOBIAN_POINT _kP, * kP = &_kP;
uint8_t x2y2[64] = {
0 };
sm3_context sm3_ctx;
if (!(SM2_MIN_PLAINTEXT_SIZE <= inlen && inlen <= SM2_MAX_PLAINTEXT_SIZE))
{
return -1;
}
jacobian_point_from_bytes(P, (uint8_t*)&key->public_key);
// S = h * P, check S != O
// for sm2 curve, h == 1 and S == P
// SM2_POINT can not present point at infinity, do do nothing here
retry:
// rand k in [1, n - 1]
do {
if (fn_rand(k) != 1)
{
return -1;
}
} while (bn_is_zero(k));
// output C1 = k * G = (x1, y1)
jacobian_point_mul_generator(C1, k);
jacobian_point_to_bytes(C1, (uint8_t*)&out->point);
// k * P = (x2, y2)
jacobian_point_mul(kP, k, P);
jacobian_point_to_bytes(kP, x2y2);
// t = KDF(x2 || y2, inlen)
kdf(x2y2, 64, inlen, out->ciphertext);
// if t is all zero, retry
if (all_zero(out->ciphertext, inlen))
{
goto retry;
}
// output C2 = M xor t
gm_memxor(out->ciphertext, out->ciphertext, in, inlen);
out->ciphertext_size = (uint32_t)inlen;
// output C3 = Hash(x2 || m || y2)
SM3::Starts(&sm3_ctx);
SM3::Update(&sm3_ctx, x2y2, 32);
SM3::Update(&sm3_ctx, (uint8_t*)in, inlen);
SM3::Update(&sm3_ctx, x2y2 + 32, 32);
SM3::Finish(&sm3_ctx, out->hash);
memset(k, 0, sizeof(k));
memset(kP, 0, sizeof(SM2_JACOBIAN_POINT));
memset(x2y2, 0, sizeof(x2y2));
return 1;
}
int do_decrypt(const SM2_KEY* key, const SM2_CIPHERTEXT* in, uint8_t* out, size_t* outlen)
{
int ret = -1;
SM2_BN d;
SM2_JACOBIAN_POINT _C1, * C1 = &_C1;
uint8_t x2y2[64];
sm3_context sm3_ctx;
uint8_t hash[32];
// check C1 is on sm2 curve
jacobian_point_from_bytes(C1, (uint8_t*)&in->point);
if (!jacobian_point_is_on_curve(C1))
{
return -1;
}
// check if S = h * C1 is point at infinity
// this will not happen, as SM2_POINT can not present point at infinity
// d * C1 = (x2, y2)
bn_from_bytes(d, key->private_key);
jacobian_point_mul(C1, d, C1);
// t = KDF(x2 || y2, klen) and check t is not all zeros
jacobian_point_to_bytes(C1, x2y2);
kdf(x2y2, 64, in->ciphertext_size, out);
if (all_zero(out, in->ciphertext_size))
{
goto end;
}
// M = C2 xor t
gm_memxor(out, out, in->ciphertext, in->ciphertext_size);
*outlen = in->ciphertext_size;
// u = Hash(x2 || M || y2)
SM3::Starts(&sm3_ctx);
SM3::Update(&sm3_ctx, x2y2, 32);
SM3::Update(&sm3_ctx, out, in->ciphertext_size);
SM3::Update(&sm3_ctx, x2y2 + 32, 32);
SM3::Finish(&sm3_ctx, hash);
// check if u == C3
if (memcmp(in->hash, hash, sizeof(hash)) != 0)
{
goto end;
}
ret = 1;
end:
memset(d, 0, sizeof(d));
memset(C1, 0, sizeof(SM2_JACOBIAN_POINT));
memset(x2y2, 0, sizeof(x2y2));
return ret;
}
int encrypt(const SM2_KEY* key, const uint8_t* in, size_t inlen, uint8_t* out, size_t* outlen)
{
SM2_CIPHERTEXT C;
if (!key || !in || !out || !outlen)
{
return -1;
}
if (!inlen)
{
return -1;
}
if (do_encrypt(key, in, inlen, &C) != 1)
{
return -1;
}
*outlen = 0;
if (ciphertext_to_der(&C, &out, outlen) != 1)
{
return -1;
}
return 1;
}
int decrypt(const SM2_KEY* key, const uint8_t* in, size_t inlen, uint8_t* out, size_t* outlen)
{
SM2_CIPHERTEXT C;
if (!key || !in || !out || !outlen)
{
return -1;
}
if (ciphertext_from_der(&C, &in, &inlen) != 1
|| asn1_length_is_zero(inlen) != 1)
{
return -1;
}
if (do_decrypt(key, &C, out, outlen) != 1)
{
return -1;
}
return 1;
}
int sm2_do_sign(const SM2_KEY* key, const uint8_t dgst[32], SM2_SIGNATURE* sig)
{
SM2_JACOBIAN_POINT _P, * P = &_P;
SM2_BN d;
SM2_BN d_inv;
SM2_BN e;
SM2_BN k;
SM2_BN x;
SM2_BN t;
SM2_BN r;
SM2_BN s;
bn_from_bytes(d, key->private_key);
// compute (d + 1)^-1 (mod n)
fn_add(d_inv, d, SM2_ONE);
if (bn_is_zero(d_inv))
{
return -1;
}
fn_inv(d_inv, d_inv);
// e = H(M)
bn_from_bytes(e, dgst);
retry:
// rand k in [1, n - 1]
do {
if (fn_rand(k) != 1)
{
return -1;
}
} while (bn_is_zero(k));
// (x, y) = kG
jacobian_point_mul_generator(P, k);
jacobian_point_get_xy(P, x, NULL);
// r = e + x (mod n)
if (bn_cmp(e, SM2_N) >= 0)
{
bn_sub(e, e, SM2_N);
}
if (bn_cmp(x, SM2_N) >= 0) {
bn_sub(x, x, SM2_N);
}
fn_add(r, e, x);
// if r == 0 or r + k == n re-generate k
bn_add(t, r, k);
if (bn_is_zero(r) || bn_cmp(t, SM2_N) == 0)
{
goto retry;
}
// s = ((1 + d)^-1 * (k - r * d)) mod n
fn_mul(t, r, d);
fn_sub(k, k, t);
fn_mul(s, d_inv, k);
// check s != 0
if (bn_is_zero(s))
{
goto retry;
}
bn_to_bytes(r, sig->r);
bn_to_bytes(s, sig->s);
memset(d, 0, sizeof(d));
memset(d_inv, 0, sizeof(d_inv));
memset(k, 0, sizeof(k));
memset(t, 0, sizeof(t));
return 1;
}
int do_verify(const SM2_KEY* key, const uint8_t dgst[32], const SM2_SIGNATURE* sig)
{
SM2_JACOBIAN_POINT _P, * P = &_P;
SM2_JACOBIAN_POINT _R, * R = &_R;
SM2_BN r;
SM2_BN s;
SM2_BN e;
SM2_BN x;
SM2_BN t;
// parse public key
jacobian_point_from_bytes(P, (const uint8_t*)&key->public_key);
// parse signature values
bn_from_bytes(r, sig->r);
bn_from_bytes(s, sig->s);
// check r, s in [1, n-1]
if (bn_is_zero(r) == 1
|| bn_cmp(r, SM2_N) >= 0
|| bn_is_zero(s) == 1
|| bn_cmp(s, SM2_N) >= 0)
{
return -1;
}
// e = H(M)
bn_from_bytes(e, dgst);
// t = r + s (mod n), check t != 0
fn_add(t, r, s);
if (bn_is_zero(t))
{
return -1;
}
// Q = s * G + t * P
jacobian_point_mul_sum(R, t, P, s);
jacobian_point_get_xy(R, x, NULL);
// r' = e + x (mod n)
if (bn_cmp(e, SM2_N) >= 0)
{
bn_sub(e, e, SM2_N);
}
if (bn_cmp(x, SM2_N) >= 0)
{
bn_sub(x, x, SM2_N);
}
fn_add(e, e, x);
// check if r == r'
if (bn_cmp(e, r) != 0)
{
return -1;
}
return 1;
}
int sign(const SM2_KEY* key, const uint8_t dgst[32], uint8_t* sigbuf, size_t* siglen, bool rawRS)
{
SM2_SIGNATURE sig;
if (!key || !dgst || !sigbuf || !siglen)
{
return -1;
}
if (sm2_do_sign(key, dgst, &sig) != 1)
{
return -1;
}
if (rawRS)
{
*siglen = sizeof(SM2_SIGNATURE);
memcpy(sigbuf, &sig, *siglen);
return 1;
}
*siglen = 0;
if (signature_to_der(&sig, &sigbuf, siglen) != 1)
{
return -1;
}
return 1;
}
int verify(const SM2_KEY* key, const uint8_t dgst[32], const uint8_t* sigbuf, size_t siglen, bool rawRS)
{
SM2_SIGNATURE sig;
if (!key || !dgst || !sigbuf || !siglen)
{
return -1;
}
if (rawRS)
{
if (siglen != sizeof(SM2_SIGNATURE))
{
return -1;
}
memcpy(&sig, sigbuf, siglen);
}
else
{
if (signature_from_der(&sig, &sigbuf, &siglen) != 1
|| asn1_length_is_zero(siglen) != 1)
{
return -1;
}
}
if (do_verify(key, dgst, &sig) != 1)
{
return -1;
}
return 1;
}
int compute_z(uint8_t z[32], const SM2_POINT* pub, const char* id, size_t idlen)
{
sm3_context ctx;
uint8_t zin[18 + 32 * 6] =
{
0x00, 0x80,
0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,
0xFF,0xFF,0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFC,
0x28,0xE9,0xFA,0x9E,0x9D,0x9F,0x5E,0x34,0x4D,0x5A,0x9E,0x4B,0xCF,0x65,0x09,0xA7,
0xF3,0x97,0x89,0xF5,0x15,0xAB,0x8F,0x92,0xDD,0xBC,0xBD,0x41,0x4D,0x94,0x0E,0x93,
0x32,0xC4,0xAE,0x2C,0x1F,0x19,0x81,0x19,0x5F,0x99,0x04,0x46,0x6A,0x39,0xC9,0x94,
0x8F,0xE3,0x0B,0xBF,0xF2,0x66,0x0B,0xE1,0x71,0x5A,0x45,0x89,0x33,0x4C,0x74,0xC7,
0xBC,0x37,0x36,0xA2,0xF4,0xF6,0x77,0x9C,0x59,0xBD,0xCE,0xE3,0x6B,0x69,0x21,0x53,
0xD0,0xA9,0x87,0x7C,0xC6,0x2A,0x47,0x40,0x02,0xDF,0x32,0xE5,0x21,0x39,0xF0,0xA0,
};
if (!z || !pub || !id)
{
return -1;
}
memcpy(&zin[18 + 32 * 4], pub->x, 32);
memcpy(&zin[18 + 32 * 5], pub->y, 32);
SM3::Starts(&ctx);
if (strcmp(id, SM2_DEFAULT_ID) == 0)
{
SM3::Update(&ctx, zin, sizeof(zin));
}
else
{
uint8_t idbits[2];
idbits[0] = (uint8_t)(idlen >> 5);
idbits[1] = (uint8_t)(idlen << 3);
SM3::Update(&ctx, idbits, 2);
SM3::Update(&ctx, (uint8_t*)id, idlen);
SM3::Update(&ctx, zin + 18, 32 * 6);
}
SM3::Finish(&ctx, z);
return 1;
}
int sign_init(sm3_context* ctx, const SM2_KEY* key, const char* id, size_t idlen)
{
if (!ctx || !key)
{
return -1;
}
SM3::Starts(ctx);
if (id)
{
uint8_t z[32];
if (idlen <= 0 || idlen > SM2_MAX_PLAINTEXT_SIZE)
{
return -1;
}
compute_z(z, &key->public_key, id, idlen);
SM3::Update(ctx, z, sizeof(z));
}
return 1;
}
int sign_update(sm3_context* ctx, const uint8_t* data, size_t datalen)
{
if (!ctx)
{
return -1;
}
if (data && datalen > 0)
{
SM3::Update(ctx, (uint8_t*)data, datalen);
}
return 1;
}
int sign_finish(sm3_context* ctx, const SM2_KEY* key, uint8_t* sig, size_t* siglen, bool rawRS)
{
uint8_t dgst[32];
if (!ctx || !key || !sig || !siglen)
{
return -1;
}
SM3::Finish(ctx, dgst);
if (sign(key, dgst, sig, siglen, rawRS) != 1)
{
return -1;
}
return 1;
}
int verify_init(sm3_context* ctx, const SM2_KEY* key, const char* id, size_t idlen)
{
if (!ctx || !key)
{
return -1;
}
SM3::Starts(ctx);
if (id)
{
uint8_t z[32];
if (idlen <= 0 || idlen > SM2_MAX_PLAINTEXT_SIZE)
{
return -1;
}
compute_z(z, &key->public_key, id, idlen);
SM3::Update(ctx, z, sizeof(z));
}
return 1;
}
int verify_update(sm3_context* ctx, const uint8_t* data, size_t datalen)
{
if (!ctx)
{
return -1;
}
if (data && datalen > 0)
{
SM3::Update(ctx, (uint8_t*)data, datalen);
}
return 1;
}
int verify_finish(sm3_context* ctx, const SM2_KEY* key, const uint8_t* sig, size_t siglen, bool rawRS)
{
uint8_t dgst[32];
if (!ctx || !sig)
{
return -1;
}
SM3::Finish(ctx, dgst);
if (verify(key, dgst, sig, siglen, rawRS) != 1)
{
return -1;
}
return 1;
}
int GetPubKeyOfCerStr(const char* cer, size_t cerLen, char* pubKey)
{
if (!cer || !cerLen || cer[0] != '0') {
return 0;
}
unsigned char subjectPublicKeyHead[25] = {
0 };
subjectPublicKeyHead[0] = '\x30';//表示一个Sequence(Subject Public Key Info)
subjectPublicKeyHead[1] = '\x59';//后续字节数
subjectPublicKeyHead[2] = '\x30';//表示一个Sequence(Public Key Algorithm)
subjectPublicKeyHead[3] = '\x13';//后续字节数
subjectPublicKeyHead[4] = '\x06';//表示一个ObjectID:1.2.840.10045.2.1 Elliptic curve public key cryptography
subjectPublicKeyHead[5] = '\x07';//后续字节数
subjectPublicKeyHead[6] = '\x2A';
subjectPublicKeyHead[7] = '\x86';
subjectPublicKeyHead[8] = '\x48';
subjectPublicKeyHead[9] = '\xCE';
subjectPublicKeyHead[10] = '\x3D';
subjectPublicKeyHead[11] = '\x02';
subjectPublicKeyHead[12] = '\x01';
subjectPublicKeyHead[13] = '\x06';//表示一个ObjectID:1.2.156.10197.1.301 "SM2" elliptic curve cryptography
subjectPublicKeyHead[14] = '\x08';//后续字节数
subjectPublicKeyHead[15] = '\x2A';
subjectPublicKeyHead[16] = '\x81';
subjectPublicKeyHead[17] = '\x1C';
subjectPublicKeyHead[18] = '\xCF';
subjectPublicKeyHead[19] = '\x55';
subjectPublicKeyHead[20] = '\x01';
subjectPublicKeyHead[21] = '\x82';
subjectPublicKeyHead[22] = '\x2D';
subjectPublicKeyHead[23] = '\x03';//表示一个BitString类型(Public-Key)
subjectPublicKeyHead[24] = '\x42';//后续字节数
size_t hdrLen = sizeof(subjectPublicKeyHead);
unsigned char ch;
for (size_t i = 0; i < cerLen - hdrLen; i++) {
if (memcmp(cer + i, subjectPublicKeyHead, hdrLen) == 0) {
for (size_t j = 2; j < 0x42; j++) {
ch = (cer[i + hdrLen + j] & 0xf0) >> 4;
pubKey[(j - 2) * 2] = (ch > 9) ? ch + 'A' - 10 : ch + '0';
ch = cer[i + hdrLen + j] & 0x0f;
pubKey[(j - 2) * 2 + 1] = (ch > 9) ? ch + 'A' - 10 : ch + '0';
}
return 1;
}
}
return 0;
}
};
#endif // !__SM2_H__
5.2 单元测试
void TestSM2()
{
const char* priKey = "ee9751a685f4f1ca1ef355b15b937475f90757c6ea930f04a3242901a13b94a2";
const char* pubKey = "04de16070290a70b3457fef7651c5e3be8902b5d589f3d0b80efc5f4e38a78d0e7d09b9d82acc2f31eb6314f831f0766cf644c1d5856794b9106b5f964b7b6fb5d";
const char* pMsg = "Hello, World,I love 中国.";
SM2 sm2;
// 初始化密钥
if (!sm2.InitKey(priKey, pubKey))
{
std::cout << "初始化SM2密钥失败" << std::endl;
return;
}
std::cout << "测试数据:" << strlen(pMsg) << "," << pMsg << std::endl;
// 签名
char hex[512] = {
0 };
char sign[128] = {
0 };
size_t len = sizeof(sign);
if (!sm2.SM2Sign((unsigned char*)pMsg, strlen(pMsg), (unsigned char*)sign, &len) || len == 0)
{
std::cout << "生成签名失败" << std::endl;
return;
}
size_t out_len = sizeof(hex);
memset(hex, 0, sizeof(hex));
SM2::Bin2Hex(sign, len, hex, out_len);
std::cout << "生成签名成功" << std::endl;
if (!sm2.SM2Verify((unsigned char*)pMsg, strlen(pMsg), (unsigned char*)sign, len))
{
std::cout << "签名校验失败" << std::endl;
return;
}
std::cout << "签名校验成功:" << out_len << "," << hex << std::endl;
// 加密
char ciphertext[512] = {
0 };
len = sizeof(ciphertext);
if (!sm2.SM2Encrypt((unsigned char*)pMsg, strlen(pMsg), (unsigned char*)ciphertext, &len))
{
std::cout << "加密失败" << std::endl;
return;
}
out_len = sizeof(hex);
memset(hex, 0, sizeof(hex));
SM2::Bin2Hex(ciphertext, len, hex, out_len);
std::cout << "加密成功:" << out_len << "," << hex << std::endl;
char text[512] = {
0 };
size_t text_len = sizeof(text);
if (!sm2.SM2Decrypt((unsigned char*)ciphertext, len, (unsigned char*)text, &text_len))
{
std::cout << "解密失败" << std::endl;
return;
}
std::cout << "解密成功:" << text_len << "," << text << std::endl;
}
5.3 输出结果
测试数据:26,Hello, World,I love 中国.
生成签名成功
签名校验成功:144,3046022100945d19ee176dae52533cf0a8af25030204020131d5ac232a0ed1bdb806b2d76d022100b3cdf47895c5e3c390442f7436b0f902b2eb5c8723a70ff8479fcec0b8adae63
加密成功:270,308184022100c1a0e063e981a544f974a9aacd1dd5e193f31378ec52d79e1d4522cd8f0200a0022100de6137f68384f86735c7af776525ab42b1177574305ddeec3f2547161da3ca6a042044e04f1bd288027c43eb99c6a6b1c2071a440f27f07194e0308dfa7530841c26041a926716f58ad2d5bd0c451df6ce1bdef976c608ed03aaf2f7f21f
解密成功:26,Hello, World,I love 中国.
六、结语
SM2作为我国密码技术的核心成果,已在政务、金融、物联网等领域大规模应用。其设计融合了ECC的高效性与抗量子特性,结合国密标准生态(如SM3/SM4),构建了自主可控的安全体系。未来随着抗量子算法的演进,SM2将在后量子密码时代持续发挥关键作用。