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代码,前人栽树,后人乘凉的主要内容,如果未能解决你的问题,请参考以下文章

SM2椭圆曲线公钥密码算法

国密算法实现

国家密码标准-商密SM2官方文档整理

2017-2018-2 20179204《网络攻防实践》第十三周学习总结 python实现国密算法

密码学系列 - 国密SM2为什么不支持恢复公钥

密码学系列 - 国密SM2为什么不支持恢复公钥