SM2 椭圆曲线公钥密码算法,完整c代码,前人栽树,后人乘凉
Posted 张志翔 ̮
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SM2 椭圆曲线公钥密码算法,完整c代码,前人栽树,后人乘凉相关的知识,希望对你有一定的参考价值。
某电信安信息安全数学基础实验要求实现SM2椭圆曲线公钥密码算法
这是基于mircal库实现的,没有mircal库的下载我以前的博客发的文件,根据教程在vs上搭建。
一共四个文件 SM2.c SM2.h SM3.c SM3.h
SM2.c
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <time.h>
#include<string.h>
#include "sm2.h"
void Buf_Out(unsigned char *buf, int buflen) //每32项为一行输出buf
int i;
printf("\\n");
for (i = 0; i < buflen; i++)
if (i % 32 != 31)
printf("%02x", buf[i]);
else
printf("%02x\\n", buf[i]);
printf("\\n");
return;
#define SEED_CONST 0x1BD8C95A
struct QXCS
char *p;//椭圆曲线的参数
char *a;
char *b;
char *n; //G的阶
char *x; //g=(x,y)
char *y;
;
struct QXCS pdf = "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF","FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC",
"28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93","FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123",
"32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7","BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0",
;
//接收方B的私钥和公钥产生
void sm2_keygen(unsigned char *wx, int *wxlen, unsigned char *wy, int *wylen,unsigned char *privkey, int *privkeylen)
struct QXCS *cfig = &pdf;
epoint *g,*pB;
big a,b,p,n,x,y,key1;
miracl *mip = mirsys(20,0); //初始化大数系统
mip->IOBASE = 16; //输入为16进制数改为大数
p = mirvar(0);
a = mirvar(0);
b = mirvar(0);
n = mirvar(0);
x = mirvar(0);
y = mirvar(0);
key1 = mirvar(0);
cinstr(p,cfig->p); //将大数字符串转换成大数,这里是16进制的字符串转换大数
cinstr(a,cfig->a);
cinstr(b,cfig->b);
cinstr(n,cfig->n);
cinstr(x,cfig->x);
cinstr(y,cfig->y);
ecurve_init(a,b,p,MR_PROJECTIVE); //初始化椭圆曲线
g = epoint_init();
pB = epoint_init();
epoint_set(x,y,0,g); //g=(x,y)为基点G
//产生私钥
irand(time(NULL)+SEED_CONST); //初始化种子
bigrand(n,key1); //生成随机数key1
ecurve_mult(key1,g,pB); //pB=key1*g
epoint_get(pB,x,y); //取pB上的点(x,y)x和y即为公钥
*wxlen = big_to_bytes(0, x, (char *)wx, FALSE); //公钥写入wx,长度wxlen
*wylen = big_to_bytes(0, y, (char *)wy, FALSE);
*privkeylen = big_to_bytes(0, key1, (char *)privkey, FALSE);
mirkill(key1);
mirkill(p);
mirkill(a);
mirkill(b);
mirkill(n);
mirkill(x);
mirkill(y);
epoint_free(g);
epoint_free(pB);
mirexit();
int kdf(unsigned char *zl, unsigned char *zr, int klen, unsigned char *kbuf) //zl,zr为(x2,y2)
unsigned char buf[70];
unsigned char digest[32];
unsigned int ct = 0x00000001;
int i, m, n;
unsigned char *p;
memcpy(buf, zl, 32); //把x2,y2传入buf
memcpy(buf+32, zr, 32);
m = klen / 32;
n = klen % 32;
p = kbuf;
for(i = 0; i < m; i++) //buf 64-70
buf[64] = (ct >> 24) & 0xFF; //ct前8位
buf[65] = (ct >> 16) & 0xFF;
buf[66] = (ct >> 8) & 0xFF;
buf[67] = ct & 0xFF;
sm3(buf, 68, p); //sm3后结果放在p中
p += 32;
ct++;
if(n != 0)
buf[64] = (ct >> 24) & 0xFF;
buf[65] = (ct >> 16) & 0xFF;
buf[66] = (ct >> 8) & 0xFF;
buf[67] = ct & 0xFF;
sm3(buf, 68, digest);
memcpy(p, digest, n);
for(i = 0; i < klen; i++)
if(kbuf[i] != 0) //kbuf中有i+1个0
break;
if(i < klen)
return 1; //kbuf(t)中的bit全是0, kdf判断通过,执行下一步C2=M异或t
else
return 0;
int A_encrypt(char *msg,int msglen, char *wx,int wxlen, char *wy,int wylen, char *outmsg)//wx,wy公钥的x,y的坐标
struct QXCS *cfig = &pdf;
big x2, y2, x1, y1, k;
big a,b,p,n,x,y;
epoint *g, *w, *pb, *c1,*kpb;
int ret = -1;
int i;
unsigned char zl[32], zr[32];
unsigned char *tmp;
miracl *mip;
tmp = malloc(msglen+64);
if(tmp == NULL)
return -1;
mip = mirsys(20, 0);
mip->IOBASE = 16; //读入16进制数
p=mirvar(0);
a=mirvar(0);
b=mirvar(0);
n=mirvar(0);
x=mirvar(0);
y=mirvar(0);
k=mirvar(0);
x2=mirvar(0);
y2=mirvar(0);
x1=mirvar(0);
y1=mirvar(0);
cinstr(p,cfig->p); //大数字符串变为大数
cinstr(a,cfig->a);
cinstr(b,cfig->b);
cinstr(n,cfig->n);
cinstr(x,cfig->x);
cinstr(y,cfig->y); //g=(x,y)
ecurve_init(a,b,p,MR_PROJECTIVE); //椭圆曲线方程初始化 y2 =x3 + Ax + B mod p
g=epoint_init(); //点坐标初始化
pb=epoint_init();
kpb = epoint_init();
c1= epoint_init();
w= epoint_init();
epoint_set(x,y,0,g); //点坐标设置 g=(x,y),现在无值
bytes_to_big(wxlen,(char *)wx,x); //把公钥wx和wy赋值给x,y
bytes_to_big(wylen,(char *)wy,y);
epoint_set(x,y,0,pb); //=(x1,y1)
//选择小于n的随机数k
irand(time(NULL)+SEED_CONST);
sm2_encrypt_again:
do
bigrand(n, k);
while(k->len == 0);
ecurve_mult(k, g, c1); // 点乘c1=k*g(第三个=第一个*第二个)
epoint_get(c1, x1, y1); //从c1里面得到x1,y1
big_to_bytes(32, x1, (char *)outmsg, TRUE);
big_to_bytes(32, y1, (char *)outmsg+32, TRUE);
if(point_at_infinity(pb)) //如果s是无穷点,返回1,报错退出
goto exit_sm2_encrypt;
ecurve_mult(k, pb, kpb); //kpb=K*pb
epoint_get(kpb, x2, y2); //从kpb得到x2,y2
big_to_bytes(32, x2, (char *)zl, TRUE); //把大数x2,y2变为字节放入zl,zr
big_to_bytes(32, y2, (char *)zr, TRUE);
//t=KDF(x2||y2,klen)
if (kdf(zl, zr, msglen, outmsg+64) == 0) //如果kdf返回的值为0,从头开始重新计算
goto sm2_encrypt_again;
for(i = 0; i < msglen; i++)
outmsg[64+i] ^= msg[i];
//tmp=x2 || M| |y2 相连
memcpy(tmp, zl, 32);
memcpy(tmp+32, msg, msglen);
memcpy(tmp+32+msglen, zr, 32);
//C3=outmsg=hash(SM3)(tmp)
sm3(tmp, 64+msglen, &outmsg[64+msglen]);
ret = msglen+64+32;
exit_sm2_encrypt: //退出释放内存
mirkill(x2);
mirkill(y2);
mirkill(x1);
mirkill(y1);
mirkill(k);
mirkill(a);
mirkill(b);
mirkill(p);
mirkill(n);
mirkill(x);
mirkill(y);
epoint_free(g); //释放点内存
epoint_free(w);
epoint_free(pb);
epoint_free(kpb);
mirexit();
free(tmp);
return ret;
//B收到密文后开始解密运算
int decrypt(char *msg,int msglen, char *privkey, int privkeylen,char *outmsg)
struct QXCS *cfig = &pdf;
big x2, y2, c, k;
big a,b,p,n,x,y,key1,dB;
epoint *g,*C1,*dBC1;
unsigned char c3[32];
unsigned char zl[32], zr[32];
int i, ret = -1;
unsigned char *tmp;
miracl *mip;
if(msglen < 96)
return 0;
msglen -= 96;
tmp = malloc(msglen+64);
if(tmp == NULL)
return 0;
mip = mirsys(20, 0);
mip->IOBASE = 16;
x2=mirvar(0);
y2=mirvar(0);
c=mirvar(0);
k = mirvar(0);
p = mirvar(0);
a = mirvar(0);
b = mirvar(0);
n = mirvar(0);
x = mirvar(0);
y = mirvar(0);
key1 = mirvar(0);
dB= mirvar(0);
bytes_to_big(privkeylen,(char *)privkey,dB);
cinstr(p,cfig->p);
cinstr(a,cfig->a);
cinstr(b,cfig->b);
cinstr(n,cfig->n);
cinstr(x,cfig->x);
cinstr(y,cfig->y);
ecurve_init(a,b,p,MR_PROJECTIVE); //初始化椭圆曲线 y2=x3+Ax+B (mod p)
g = epoint_init();
dBC1 = epoint_init();
C1 = epoint_init();
bytes_to_big(32, (char *)msg, x); //从msg中分别取出32位放入x和y
bytes_to_big(32, (char *)msg+32, y);
if(!epoint_set(x,y,0,C1)) //初始化点C1=(x,y)点C1=(x,y)是否在椭圆曲线 上
goto exit_sm2_decrypt;
if(point_at_infinity(C1)) //如果s(test)是无穷远点,报错并退出
goto exit_sm2_decrypt;
ecurve_mult(dB, C1, dBC1); //dBC1=dB*c1
epoint_get(dBC1, x2, y2); //从dBC1中读取x2,y2
big_to_bytes(32, x2, (char *)zl, TRUE);
big_to_bytes(32, y2, (char *)zr, TRUE);
if (kdf(zl, zr, msglen, outmsg) == 0) //判断:t=kdf不是全0,才继续
goto exit_sm2_decrypt;
for(i = 0; i < msglen; i++) //M'(outmsg)=C2 ^ t(outmsg)
outmsg[i] ^= msg[i+64];//密文从65位开始为c2
memcpy(tmp, zl, 32);
memcpy(tmp+32, outmsg, msglen);
memcpy(tmp+32+msglen, zr, 32);
sm3(tmp, 64+msglen, c3);
if(memcmp(c3, msg+64+msglen, 32) != 0)//判断u=c3则继续
goto exit_sm2_decrypt;
ret = msglen;
exit_sm2_decrypt:
mirkill(x2);
mirkill(y2);
mirkill(c);
mirkill(k);
mirkill(p);
mirkill(a);
mirkill(b);
mirkill(n);
mirkill(x);
mirkill(y);
mirkill(key1);
mirkill(dB);
epoint_free(g);
epoint_free(dBC1);
mirexit();
free(tmp);
return ret;
int main()
unsigned char dB[32]; //存放私钥
unsigned char xB[32]; //存放公钥pb(x,y)
unsigned char yB[32];
unsigned char tx[256]="0";
unsigned char etx[256] ;
unsigned char mtx[256] ="0";
int j;
struct QXCS *cfig = &pdf;
printf("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<SM2椭圆曲线公钥密码>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\\n" );
printf("由pdf给出的参数:\\n\\tp:%s\\n\\ta:%s\\n\\tb:%s\\n\\tn:%s\\n\\tG(x):%s\\n\\tG(y):%s\\n", cfig->p, cfig->a, cfig->b, cfig->n, cfig->x, cfig->y);
FILE *fp;
fopen_s(&fp,"7.txt", "r+");
fgets(tx, 200, fp);
fclose(fp);
printf("\\n文件中明文为:\\n\\t%s\\n", tx);
int msglen = strlen(tx);
int wxlen, wylen, privkeylen;
sm2_keygen(xB, &wxlen, yB, &wylen, dB, &privkeylen); //公钥(xB,yB)dB私钥 空值进入,产生公私钥
printf("\\n\\t公钥x坐标:");
for (j = 0; j< wxlen; j++)
printf("%02x", xB[j]);
printf("\\n\\t公钥y坐标:");
for (j = 0; j < wylen; j++)
printf("%02x", yB[j]);
printf("\\n\\t私钥:");
for (j = 0; j< privkeylen; j++)
printf("%02x", dB[j]);
printf("\\n");
A_encrypt(tx,msglen,xB,32,yB,32,etx); //传入明文和公钥xb,yb
printf("加密结果: ");
Buf_Out(etx, 64+msglen+32);
decrypt(etx,64+msglen+32,dB,32,mtx); //传入密文与私钥pb
if(decrypt(etx,64+msglen+32,dB,32,mtx) < 0)
printf("sm2_decrypt error!\\n");
else
printf("\\n解密结果: ");
Buf_Out(mtx, msglen);
printf("解密出的明文为:\\n\\t%s\\n", mtx);
return 0;
SM2.h
#ifndef __SM2_HEADER_2015_12_28__
#define __SM2_HEADER_2015_12_28__
#include "sm3.h"
#include "miracl.h"
#ifdef __cplusplus
extern "C"
#endif
int sm3_e(unsigned char *userid, int userid_len, unsigned char *xa, int xa_len, unsigned char *ya, int ya_len, unsigned char *msg, int msg_len, unsigned char *e);
/*
功能:根据用户ID及公钥,求用于签名或验签的消息HASH值
[输入] userid: 用户ID
[输入] userid_len: userid的字节数
[输入] xa: 公钥的X坐标
[输入] xa_len: xa的字节数
[输入] ya: 公钥的Y坐标
[输入] ya_len: ya的字节数
[输入] msg:要签名的消息
[输入] msg_len: msg的字节数
[输出] e:32字节,用于签名或验签
返回值:
-1:内存不足
0:成功
*/
void sm2_keygen(unsigned char *wx,int *wxlen, unsigned char *wy,int *wylen,unsigned char *privkey, int *privkeylen);
/*
功能:生成SM2公私钥对
[输出] wx: 公钥的X坐标,不足32字节在前面加0x00
[输出] wxlen: wx的字节数,32
[输出] wy: 公钥的Y坐标,不足32字节在前面加0x00
[输出] wylen: wy的字节数,32
[输出] privkey:私钥,不足32字节在前面加0x00
[输出] privkeylen: privkey的字节数,32
*/
void sm2_sign(unsigned char *hash,int hashlen,unsigned char *privkey,int privkeylen,unsigned char *cr,int *rlen,unsigned char *cs,int *slen);
/*
功能:SM2签名
[输入] hash: sm3_e()的结果
[输入] hashlen: hash的字节数,应为32
[输入] privkey: 私钥
[输入] privkeylen: privkeylen的字节数
[输出] cr: 签名结果的第一部分,不足32字节在前面加0x00。
[输出] rlen:cr的字节数,32
[输出] cs: 签名结果的第二部分,不足32字节在前面加0x00。
[输出] slen:cs的字节数,32
*/
int sm2_verify(unsigned char *hash,int hashlen,unsigned char *cr,int rlen,unsigned char *cs,int slen, unsigned char *wx,int wxlen, unsigned char *wy,int wylen);
/*
功能:验证SM2签名
[输入] hash: sm3_e()的结果
[输入] hashlen: hash的字节数,应为32
[输入] cr: 签名结果的第一部分
[输入] rlen:cr的字节数
[输入] cs: 签名结果的第二部分。
[输入] slen:cs的字节数
[输入] wx: 公钥的X坐标
[输入] wxlen: wx的字节数,不超过32字节
[输入] wy: 公钥的Y坐标
[输入] wylen: wy的字节数,不超过32字节
返回值:
0:验证失败
1:验证通过
*/
int A_encrypt(char *msg,int msglen, char *wx,int wxlen, char *wy,int wylen,char *outmsg);
/*
功能:用SM2公钥加密数据。加密结果比输入数据多96字节!
[输入] msg 要加密的数据
[输入] msglen:msg的字节数
[输入] wx: 公钥的X坐标
[输入] wxlen: wx的字节数,不超过32字节
[输入] wy: 公钥的Y坐标
[输入] wylen: wy的字节数,不超过32字节
[输出] outmsg: 加密结果,比输入数据多96字节!,C1(64字节)和C3(32字节)保留前导0x00
返回值:
-1: 加密失败
msglen+96: 加密成功
*/
int decrypt(char *msg,int msglen, char *privkey, int privkeylen, char *outmsg);
/*
功能:用SM2私钥解密数据。解密结果比输入数据少96字节!
[输入] msg 要解密的数据,是sm2_encrypt()加密的结果,不少于96字节。
[输入] msglen:msg的字节数
[输入] privkey: 私钥
[输入] privkeylen: privkeylen的字节数
[输出] outmsg: 解密结果,比输入数据少96字节!
返回值:
-1: 解密失败
msglen-96: 解密成功
*/
void sm2_keyagreement_a1_3(unsigned char *kx1, int *kx1len,
unsigned char *ky1, int *ky1len,
unsigned char *ra, int *ralen
);
/*
功能:密钥协商的发起方调用此函数产生一对临时公钥(kx1, ky1)和相应的随机数。公钥发送给对方,随机数ra自己保存。
[输出] kx1: 公钥的X坐标,不足32字节在前面加0x00
[输出] kx1len:kx1的字节数,32
[输出] ky1: 公钥的Y坐标,不足32字节在前面加0x00
[输出] ky1len:ky1的字节数,32
[输出] ra: 随机数,不足32字节在前面加0x00
[输出] ralen: ra的字节数,32
返回值:无
*/
int sm2_keyagreement_b1_9(
unsigned char *kx1, int kx1len,
unsigned char *ky1, int ky1len,
unsigned char *pax, int paxlen,
unsigned char *pay, int paylen,
unsigned char *private_b, int private_b_len,
unsigned char *pbx, int pbxlen,
unsigned char *pby, int pbylen,
unsigned char *ida, int idalen,
unsigned char *idb, int idblen,
unsigned int kblen,
unsigned char *kbbuf,
unsigned char *kx2, int *kx2len,
unsigned char *ky2, int *ky2len,
unsigned char *xv, int *xvlen,
unsigned char *yv, int *yvlen,
unsigned char *sb
);
/*
功能:密钥协商的接收方调用此函数协商出密钥kbbuf,同时产生一对临时公钥(kx2, ky2)以及(xv, yv)、sb。(kx2, ky2)和sb发送给对方,kbbuf和(xv, yv)自己保存。
说明:
[输入] (kx1, ky1)是发起方产生的临时公钥
[输入] (pax, pay)是发起方的公钥
[输入] private_b是接收方的私钥
[输入] (pbx, pby)是接收方的公钥
[输入] ida是发起方的用户标识
[输入] idb是接收方的用户标识
[输入] kblen是要约定的密钥字节数
[输出] kbbuf是协商密钥输出缓冲区
[输出] (kx2, ky2)是接收方产生的临时公钥,不足32字节在前面加0x00
[输出] (xv, yv)是接收方产生的中间结果,自己保存,用于验证协商的正确性。,不足32字节在前面加0x00。如果(xv, yv)=NULL,则不输出。
[输出] sb是接收方产生的32字节的HASH值,要传送给发起方,用于验证协商的正确性。如果为sb=NULL,则不输出。
返回值:0-失败 1-成功
*/
int sm2_keyagreement_a4_10(
unsigned char *kx1, int kx1len,
unsigned char *ky1, int ky1len,
unsigned char *pax, int paxlen,
unsigned char *pay, int paylen,
unsigned char *private_a, int private_a_len,
unsigned char *pbx, int pbxlen,
unsigned char *pby, int pbylen,
unsigned char *ida, int idalen,
unsigned char *idb, int idblen,
unsigned char *kx2, int kx2len,
unsigned char *ky2, int ky2len,
unsigned char *ra, int ralen,
unsigned int kalen,
unsigned char *kabuf,
unsigned char *s1,
unsigned char *sa
);
/*
功能:密钥协商的发起方调用此函数协商出密钥kabuf,同时产生s1和sa。s1和kabuf自己保存,sa发送给接收方,用于确认协商过程的正确性。
说明:
[输入] (kx1, ky1)是发起方产生的临时公钥
[输入] (pax, pay)是发起方的公钥
[输入] private_a是发起方的私钥
[输入] (pbx, pby)是接收方的公钥
[输入] ida是发起方的用户标识
[输入] idb是接收方的用户标识
[输入] (kx2, ky2)是接收方产生的临时公钥
[输入] ra是发起方调用sm2_keyagreement_a1_3产生的随机数
[输入] kalen是要约定的密钥字节数
[输出] kabuf是协商密钥输出缓冲区
[输出] s1和sa是发起方产生的32字节的HASH值,s1自己保存(应等于sb),sa要传送给接收方,用于验证协商的正确性。如果s1和sa为NULL,则不输出。
返回值:0-失败 1-成功
*/
void sm2_keyagreement_b10(
unsigned char *pax, int paxlen,
unsigned char *pay, int paylen,
unsigned char *pbx, int pbxlen,
unsigned char *pby, int pbylen,
unsigned char *kx1, int kx1len,
unsigned char *ky1, int ky1len,
unsigned char *kx2, int kx2len,
unsigned char *ky2, int ky2len,
unsigned char *xv, int xvlen,
unsigned char *yv, int yvlen,
unsigned char *ida, int idalen,
unsigned char *idb, int idblen,
unsigned char *s2
);
/*
功能:密钥协商的接收方调用此函数产生s2,用于验证协商过程的正确性。
说明:
[输入] (pax, pay)是发起方的公钥
[输入] (pbx, pby)是接收方的公钥
[输入] (kx1, ky1)是发起方产生的临时公钥
[输入] (kx2, ky2)是接收方产生的临时公钥
[输入] (xv, yv)是接收方产生的中间结果
[输入] ida是发起方的用户标识
[输入] idb是接收方的用户标识
[输出] s2是接收方产生的32字节的HASH值,应等于sa。
返回值:无
*/
#ifdef __cplusplus
#endif
#endif
SM3.c
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include "sm3.h"
void sm3_block(SM3_CTX *ctx)
int j, k;
unsigned long t;
unsigned long ss1, ss2, tt1, tt2;
unsigned long a, b, c, d, e, f, g, h;
unsigned long w[132];
for(j = 0; j < 16; j++)
w[j] = ctx->data[j];
for(j = 16; j < 68; j++)
t = w[j-16] ^ w[j-9] ^ ROTATE(w[j-3], 15);
w[j] = P1(t) ^ ROTATE(w[j-13], 7) ^ w[j-6];
for(j = 0, k = 68; j < 64; j++, k++)
w[k] = w[j] ^ w[j+4];
a = ctx->h[0];
b = ctx->h[1];
c = ctx->h[2];
d = ctx->h[3];
e = ctx->h[4];
f = ctx->h[5];
g = ctx->h[6];
h = ctx->h[7];
for(j = 0; j < 16; j++)
ss1 = ROTATE(ROTATE(a, 12) + e + ROTATE(TH, j), 7);
ss2 = ss1 ^ ROTATE(a, 12);
tt1 = FFH(a, b, c) + d + ss2 + w[68 + j];
tt2 = GGH(e, f, g) + h + ss1 + w[j];
d = c;
c = ROTATE(b, 9);
b = a;
a = tt1;
h = g;
g = ROTATE(f, 19);
f = e;
e = P0(tt2);
for(j = 16; j < 33; j++)
ss1 = ROTATE(ROTATE(a, 12) + e + ROTATE(TL, j), 7);
ss2 = ss1 ^ ROTATE(a, 12);
tt1 = FFL(a, b, c) + d + ss2 + w[68 + j];
tt2 = GGL(e, f, g) + h + ss1 + w[j];
d = c;
c = ROTATE(b, 9);
b = a;
a = tt1;
h = g;
g = ROTATE(f, 19);
f = e;
e = P0(tt2);
for(j = 33; j < 64; j++)
ss1 = ROTATE(ROTATE(a, 12) + e + ROTATE(TL, (j-32)), 7);
ss2 = ss1 ^ ROTATE(a, 12);
tt1 = FFL(a, b, c) + d + ss2 + w[68 + j];
tt2 = GGL(e, f, g) + h + ss1 + w[j];
d = c;
c = ROTATE(b, 9);
b = a;
a = tt1;
h = g;
g = ROTATE(f, 19);
f = e;
e = P0(tt2);
ctx->h[0] ^= a ;
ctx->h[1] ^= b ;
ctx->h[2] ^= c ;
ctx->h[3] ^= d ;
ctx->h[4] ^= e ;
ctx->h[5] ^= f ;
ctx->h[6] ^= g ;
ctx->h[7] ^= h ;
void SM3_Init (SM3_CTX *ctx)
ctx->h[0] = 0x7380166fUL;
ctx->h[1] = 0x4914b2b9UL;
ctx->h[2] = 0x172442d7UL;
ctx->h[3] = 0xda8a0600UL;
ctx->h[4] = 0xa96f30bcUL;
ctx->h[5] = 0x163138aaUL;
ctx->h[6] = 0xe38dee4dUL;
ctx->h[7] = 0xb0fb0e4eUL;
ctx->Nl = 0;
ctx->Nh = 0;
ctx->num = 0;
void SM3_Update(SM3_CTX *ctx, const void *data, unsigned int len)
unsigned char *d;
unsigned long l;
int i, sw, sc;
if (len == 0)
return;
l = (ctx->Nl + (len << 3)) & 0xffffffffL;
if (l < ctx->Nl) /* overflow */
ctx->Nh++;
ctx->Nh += (len >> 29);
ctx->Nl = l;
d = (unsigned char *)data;
while (len >= SM3_CBLOCK)
ctx->data[0] = c_2_nl(d);
d += 4;
ctx->data[1] = c_2_nl(d);
d += 4;
ctx->data[2] = c_2_nl(d);
d += 4;
ctx->data[3] = c_2_nl(d);
d += 4;
ctx->data[4] = c_2_nl(d);
d += 4;
ctx->data[5] = c_2_nl(d);
d += 4;
ctx->data[6] = c_2_nl(d);
d += 4;
ctx->data[7] = c_2_nl(d);
d += 4;
ctx->data[8] = c_2_nl(d);
d += 4;
ctx->data[9] = c_2_nl(d);
d += 4;
ctx->data[10] = c_2_nl(d);
d += 4;
ctx->data[11] = c_2_nl(d);
d += 4;
ctx->data[12] = c_2_nl(d);
d += 4;
ctx->data[13] = c_2_nl(d);
d += 4;
ctx->data[14] = c_2_nl(d);
d += 4;
ctx->data[15] = c_2_nl(d);
d += 4;
sm3_block(ctx);
len -= SM3_CBLOCK;
if(len > 0)
memset(ctx->data, 0, 64);
ctx->num = len + 1;
sw = len >> 2;
sc = len & 0x3;
for(i = 0; i < sw; i++)
ctx->data[i] = c_2_nl(d);
d += 4;
switch(sc)
case 0:
ctx->data[i] = 0x80000000;
break;
case 1:
ctx->data[i] = (d[0] << 24) | 0x800000;
break;
case 2:
ctx->data[i] = (d[0] << 24) | (d[1] << 16) | 0x8000;
break;
case 3:
ctx->data[i] = (d[0] << 24) | (d[1] << 16) | (d[2] << 8) | 0x80;
break;
void SM3_Final(unsigned char *md, SM3_CTX *ctx)
if(ctx->num == 0)
memset(ctx->data, 0, 64);
ctx->data[0] = 0x80000000;
ctx->data[14] = ctx->Nh;
ctx->data[15] = ctx->Nl;
else
if(ctx->num <= SM3_LAST_BLOCK)
ctx->data[14] = ctx->Nh;
ctx->data[15] = ctx->Nl;
else
sm3_block(ctx);
memset(ctx->data, 0, 56);
ctx->data[14] = ctx->Nh;
ctx->data[15] = ctx->Nl;
sm3_block(ctx);
nl2c(ctx->h[0], md);
nl2c(ctx->h[1], md);
nl2c(ctx->h[2], md);
nl2c(ctx->h[3], md);
nl2c(ctx->h[4], md);
nl2c(ctx->h[5], md);
nl2c(ctx->h[6], md);
nl2c(ctx->h[7], md);
unsigned char *sm3(const unsigned char *d, unsigned int n, unsigned char *md)
SM3_CTX ctx;
SM3_Init(&ctx);
SM3_Update(&ctx, d, n);
SM3_Final(md, &ctx);
memset(&ctx, 0, sizeof(ctx));
return(md);
#if 0
int main()
unsigned char data[] = "abc";
/*66c7f0f4 62eeedd9 d1f2d46b dc10e4e2 4167c487 5cf2f7a2 297da02b 8f4ba8e0*/
unsigned char data1[] = "abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd";
/*debe9ff9 2275b8a1 38604889 c18e5a4d 6fdb70e5 387e5765 293dcba3 9c0c5732*/
unsigned char md[SM3_DIGEST_LENGTH];
clock_t start,end;
double tt;
int j;
memset(md, 0, sizeof(md));
sm3(data, 3, md);
#if DEBUG_SM3
PrintBuf(md, 32);
#endif
memset(md, 0, sizeof(md));
sm3(data1, 64, md);
#if DEBUG_SM3
PrintBuf(md, 32);
#endif
start = clock();
for(j=0;j<1000000;j++)
sm3(data1, 55, md);
end = clock();
tt = (double)(end-start)/CLOCKS_PER_SEC;
printf("speed:%lfMbps\\n", (double)512/tt);
return 0;
#endif
SM3.h
#ifndef __SM3_HEADER__
#define __SM3_HEADER__
#ifdef __cplusplus
extern "C"
#endif
#define SM3_LBLOCK 16
#define SM3_CBLOCK 64
#define SM3_DIGEST_LENGTH 32
#define SM3_LAST_BLOCK 56
typedef struct SM3state_st
unsigned long h[8];
unsigned long Nl,Nh;
unsigned long data[SM3_LBLOCK];
unsigned int num;
SM3_CTX;
void SM3_Init (SM3_CTX *ctx);
void SM3_Update(SM3_CTX *ctx, const void *data, unsigned int len);
void SM3_Final(unsigned char *md, SM3_CTX *ctx);
unsigned char *sm3(const unsigned char *d, unsigned int n, unsigned char *md);
/*
d: data
n: byte length
md: 32 bytes digest
*/
#ifdef __cplusplus
#endif
#endif
#define nl2c(l,c) (*((c)++) = (unsigned char)(((l) >> 24) & 0xff), \\
*((c)++) = (unsigned char)(((l) >> 16) & 0xff), \\
*((c)++) = (unsigned char)(((l) >> 8) & 0xff), \\
*((c)++) = (unsigned char)(((l) ) & 0xff))
#define c_2_nl(c) ((*(c) << 24) | (*(c+1) << 16) | (*(c+2) << 8) | *(c+3))
#define ROTATE(X, C) (((X) << (C)) | ((X) >> (32 - (C))))
#define TH 0x79cc4519
#define TL 0x7a879d8a
#define FFH(X, Y, Z) ((X) ^ (Y) ^ (Z))
#define FFL(X, Y, Z) (((X) & (Y)) | ((X) & (Z)) | ((Y) & (Z)))
#define GGH(X, Y, Z) ((X) ^ (Y) ^ (Z))
#define GGL(X, Y, Z) (((X) & (Y)) | ((~X) & (Z)))
#define P0(X) ((X) ^ (((X) << 9) | ((X) >> 23)) ^ (((X) << 17) | ((X) >> 15)))
#define P1(X) ((X) ^ (((X) << 15) | ((X) >> 17)) ^ (((X) << 23) | ((X) >> 9)))
以上是关于SM2 椭圆曲线公钥密码算法,完整c代码,前人栽树,后人乘凉的主要内容,如果未能解决你的问题,请参考以下文章