注:本文不讨论算法具体实现细节,只介绍如何编译一个现有的程序实现。
在商用密码检测中心( 链接:http://www.scctc.org.cn/index.aspx )的网站上,“下载中心” -> “算法源代码”网页( 链接:http://www.scctc.org.cn/templates/Download/index.aspx?nodeid=71 )上发布了国密 SM9 算法的 C 程序源代码。该程序是基于开源的 MIRACL 密码库( 在 github 网站上的链接: https://github.com/miracl/MIRACL )实现的,MIRACL 是一款商业软件,在商业应用中使用它必须先获取许可证。国密 SM9 程序的免责声明链接是:http://www.scctc.org.cn/Upload/accessory/20175/201755105494041082.pdf 。
通过下载链接 http://www.scctc.org.cn/Upload/accessory/20175/201755103016253196.rar 可以下载到 SM9 算法源码,解压压缩文件后,发现源码放在 PDF 格式文件中,放在四个目录下,分别包含签名验签、封装、密钥协商、加解密这四部分。在本文中,只对签名及验签部分感兴趣,于是手动将相关的那一部分代码复制到一些文本文件格式的源程序中,抽取出这些源码后,尝试在 Windows 下编译,结果遇到了很多问题。对这些问题的简单描述及解决思路介绍如下:
1)从 PDF 文件中粘贴文字到文本文件时,部分代码格式会在复制过程中出错,需要手动修改调整;
2)会遇到不能找到某些宏定义、函数声明、函数实现的错误。原因是这些代码段并未包含在 SM9 签名验签相关的 PDF 文件中,但是在同一网站上发布的 SM2 或 SM3 算法源代码中,或是在 SM9 代码的封装子目录中 PDF 文件里可以找到有关的代码段,需要手工复制粘贴提取出来;
3)对函数的声明和实现写在同一个文件里,有时导致编译器查找不到某些函数,从而报错。为了便于阅读和分析,最好手工将它们分离一下,把函数声明放到对应的头文件中,把函数的实现放到对应的 c 程序文件中;
4)缺少 main( ) 主函数,需要自己手写一个。
经过对 SM9 签名及验签部分代码的整理,并在 Visual Studio Code 中使用 Alt + Shift + f 组合键对代码进行格式化后,得到了以下 4 个头文件:
KDF.h, R-ate.h, zzn12_operatoin.h, SM9_sv.h
还得到了以下 5 个 c 程序文件:
KDF.c, R-ate.c, zzn12_operation.c, SM9_sv.c, test.c
以上文件的内容如下:
KDF.h
/************************************************************
Note:
Codes here were downloaded from:
http://www.scctc.org.cn/templates/Download/index.aspx?nodeid=71.
The disclaimer was published on the link:
http://www.scctc.org.cn/Upload/accessory/20175/201755105494041082.pdf .
The codes were slightly modified to pass the check of C complier.
*************************************************************/
/************************************************************************
FileName:
KDF.h
Version:
KDF_V1.1
Date:
Sep 24,2016
Description:
This headfile provides KDF function needed in SM2 algorithm
Function List:
1.SM3_256 //calls SM3_init, SM3_process and SM3_done to calculate hash value
2.SM3_init //init the SM3 state
3.SM3_process //compress the the first len/64 blocks of the message
4.SM3_done //compress the rest message and output the hash value
5.SM3_compress //called by SM3_process and SM3_done, compress a single block of message
6.BiToW //called by SM3_compress,to calculate W from Bi
7.WToW1 //called by SM3_compress, calculate W' from W
8.CF //called by SM3_compress, to calculate CF function.
9.BigEndian //called by SM3_compress and SM3_done.GM/T 0004-2012 requires to use
big-endian.
//if CPU uses little-endian, BigEndian function is a necessary call to
change the
//little-endian format into big-endian format.
10.SM3_KDF //calls SM3_init, SM3_process and SM3_done to generate key stream
History:
1. Date: Sep 18,2016
Modification: Adding notes to all the functions
************************************************************************/
#ifndef HEADER_KDF_H
#define HEADER_KDF_H
#include
#define SM2_WORDSIZE 8
#define SM2_NUMBITS 256
#define SM2_NUMWORD (SM2_NUMBITS / SM2_WORDSIZE) //32
#define SM3_len 256
#define SM3_T1 0x79CC4519
#define SM3_T2 0x7A879D8A
#define SM3_IVA 0x7380166f
#define SM3_IVB 0x4914b2b9
#define SM3_IVC 0x172442d7
#define SM3_IVD 0xda8a0600
#define SM3_IVE 0xa96f30bc
#define SM3_IVF 0x163138aa
#define SM3_IVG 0xe38dee4d
#define SM3_IVH 0xb0fb0e4e
/* Various logical functions */
#define SM3_p1(x) (x ^ SM3_rotl32(x, 15) ^ SM3_rotl32(x, 23))
#define SM3_p0(x) (x ^ SM3_rotl32(x, 9) ^ SM3_rotl32(x, 17))
#define SM3_ff0(a, b, c) (a ^ b ^ c)
#define SM3_ff1(a, b, c) ((a & b) | (a & c) | (b & c))
#define SM3_gg0(e, f, g) (e ^ f ^ g)
#define SM3_gg1(e, f, g) ((e & f) | ((~e) & g))
#define SM3_rotl32(x, n) (((x) << n) | ((x) >> (32 - n)))
#define SM3_rotr32(x, n) (((x) >> n) | ((x) << (32 - n)))
typedef struct
{
unsigned long state[8];
unsigned long length;
unsigned long curlen;
unsigned char buf[64];
} SM3_STATE;
void CF(unsigned long Wj[], unsigned long Wj1[], unsigned long V[]);
void BigEndian(unsigned char src[], unsigned int bytelen, unsigned char des[]);
void SM3_init(SM3_STATE *md);
void SM3_compress(SM3_STATE *md);
void SM3_process(SM3_STATE *md, unsigned char *buf, int len);
void SM3_done(SM3_STATE *md, unsigned char hash[]);
void SM3_256(unsigned char buf[], int len, unsigned char hash[]);
void SM3_KDF(unsigned char Z[], unsigned short zlen, unsigned short klen, unsigned char K[]);
#endif
KDF.c
/************************************************************
Note:
Codes here were downloaded from:
http://www.scctc.org.cn/templates/Download/index.aspx?nodeid=71.
The disclaimer was published on the link:
http://www.scctc.org.cn/Upload/accessory/20175/201755105494041082.pdf .
The codes were slightly modified to pass the check of C complier.
*************************************************************/
#include
#include "KDF.h"
/****************************************************************
Function: BiToW
Description: calculate W from Bi
Calls:
Called By: SM3_compress
Input: Bi[16] //a block of a message
Output: W[68]
Return: null
Others:
****************************************************************/
void BiToW(unsigned long Bi[], unsigned long W[])
{
int i;
unsigned long tmp;
for (i = 0; i <= 15; i++)
{
W[i] = Bi[i];
}
for (i = 16; i <= 67; i++)
{
tmp = W[i - 16] ^ W[i - 9] ^ SM3_rotl32(W[i - 3], 15);
W[i] = SM3_p1(tmp) ^ (SM3_rotl32(W[i - 13], 7)) ^ W[i - 6];
}
}
/*****************************************************************
Function: WToW1
Description: calculate W1 from W
Calls:
Called By: SM3_compress
Input: W[68]
Output: W1[64]
Return: null
Others:
*****************************************************************/
void WToW1(unsigned long W[], unsigned long W1[])
{
int i;
for (i = 0; i <= 63; i++)
{
W1[i] = W[i] ^ W[i + 4];
}
}
/******************************************************************
Function: CF
Description: calculate the CF compress function and update V
Calls:
Called By: SM3_compress
Input: W[68]
W1[64]
V[8]
Output: V[8]
Return: null
Others:
********************************************************************/
void CF(unsigned long W[], unsigned long W1[], unsigned long V[])
{
unsigned long SS1;
unsigned long SS2;
unsigned long TT1;
unsigned long TT2;
unsigned long A, B, C, D, E, F, G, H;
unsigned long T = SM3_T1;
unsigned long FF;
unsigned long GG;
int j;
//reg init,set ABCDEFGH=V0
A = V[0];
B = V[1];
C = V[2];
D = V[3];
E = V[4];
F = V[5];
G = V[6];
H = V[7];
for (j = 0; j <= 63; j++)
{
//SS1
if (j == 0)
{
T = SM3_T1;
}
else if (j == 16)
{
T = SM3_rotl32(SM3_T2, 16);
}
else
{
T = SM3_rotl32(T, 1);
}
SS1 = SM3_rotl32((SM3_rotl32(A, 12) + E + T), 7);
//SS2
SS2 = SS1 ^ SM3_rotl32(A, 12);
//TT1
if (j <= 15)
{
FF = SM3_ff0(A, B, C);
}
else
{
FF = SM3_ff1(A, B, C);
}
TT1 = FF + D + SS2 + *W1;
W1++;
//TT2
if (j <= 15)
{
GG = SM3_gg0(E, F, G);
}
else
{
GG = SM3_gg1(E, F, G);
}
TT2 = GG + H + SS1 + *W;
W++;
//D
D = C;
//C
C = SM3_rotl32(B, 9);
//B
B = A;
//A
A = TT1;
//H
H = G;
//G
G = SM3_rotl32(F, 19);
//F
F = E;
//E
E = SM3_p0(TT2);
}
//update V
V[0] = A ^ V[0];
V[1] = B ^ V[1];
V[2] = C ^ V[2];
V[3] = D ^ V[3];
V[4] = E ^ V[4];
V[5] = F ^ V[5];
V[6] = G ^ V[6];
V[7] = H ^ V[7];
}
/******************************************************************************
Function: BigEndian
Description: unsigned int endian converse.GM/T 0004-2012 requires to use big-endian.
if CPU uses little-endian, BigEndian function is a necessary
call to change the little-endian format into big-endian format.
Calls:
Called By: SM3_compress, SM3_done
Input: src[bytelen]
bytelen
Output: des[bytelen]
Return: null
Others: src and des could implies the same address
*******************************************************************************/
void BigEndian(unsigned char src[], unsigned int bytelen, unsigned char des[])
{
unsigned char tmp = 0;
unsigned long i = 0;
for (i = 0; i < bytelen / 4; i++)
{
tmp = des[4 * i];
des[4 * i] = src[4 * i + 3];
src[4 * i + 3] = tmp;
tmp = des[4 * i + 1];
des[4 * i + 1] = src[4 * i + 2];
des[4 * i + 2] = tmp;
}
}
/******************************************************************************
Function: SM3_init
Description: initiate SM3 state
Calls:
Called By: SM3_256
Input: SM3_STATE *md
Output: SM3_STATE *md
Return: null
Others:
*******************************************************************************/
void SM3_init(SM3_STATE *md)
{
md->curlen = md->length = 0;
md->state[0] = SM3_IVA;
md->state[1] = SM3_IVB;
md->state[2] = SM3_IVC;
md->state[3] = SM3_IVD;
md->state[4] = SM3_IVE;
md->state[5] = SM3_IVF;
md->state[6] = SM3_IVG;
md->state[7] = SM3_IVH;
}
/******************************************************************************
Function: SM3_compress
Description: compress a single block of message
Calls: BigEndian
BiToW
WToW1
CF
Called By: SM3_256
Input: SM3_STATE *md
Output: SM3_STATE *md
Return: null
Others:
*******************************************************************************/
void SM3_compress(SM3_STATE *md)
{
unsigned long W[68];
unsigned long W1[64];
//if CPU uses little-endian, BigEndian function is a necessary call
BigEndian(md->buf, 64, md->buf);
BiToW((unsigned long *)md->buf, W);
WToW1(W, W1);
CF(W, W1, md->state);
}
/******************************************************************************
Function: SM3_process
Description: compress the first (len/64) blocks of message
Calls: SM3_compress
Called By: SM3_256
Input: SM3_STATE *md
unsigned char buf[len] //the input message
int len //bytelen of message
Output: SM3_STATE *md
Return: null
Others:
*******************************************************************************/
void SM3_process(SM3_STATE *md, unsigned char *buf, int len)
{
while (len--)
{
/* copy byte */
md->buf[md->curlen] = *buf++;
md->curlen++;
/* is 64 bytes full? */
if (md->curlen == 64)
{
SM3_compress(md);
md->length += 512;
md->curlen = 0;
}
}
}
/******************************************************************************
Function: SM3_done
Description: compress the rest message that the SM3_process has left behind
Calls: SM3_compress
Called By: SM3_256
Input: SM3_STATE *md
Output: unsigned char *hash
Return: null
Others:
*******************************************************************************/
void SM3_done(SM3_STATE *md, unsigned char hash[])
{
int i;
unsigned char tmp = 0;
/* increase the bit length of the message */
md->length += md->curlen << 3;
/* append the '1' bit */
md->buf[md->curlen] = 0x80;
md->curlen++;
/* if the length is currently above 56 bytes, appends zeros till
it reaches 64 bytes, compress the current block, creat a new
block by appending zeros and length,and then compress it
*/
if (md->curlen > 56)
{
for (; md->curlen < 64;)
{
md->buf[md->curlen] = 0;
md->curlen++;
}
SM3_compress(md);
md->curlen = 0;
}
/* if the length is less than 56 bytes, pad upto 56 bytes of zeroes */
for (; md->curlen < 56;)
{
md->buf[md->curlen] = 0;
md->curlen++;
}
/* since all messages are under 2^32 bits we mark the top bits zero */
for (i = 56; i < 60; i++)
{
md->buf[i] = 0;
}
/* append length */
md->buf[63] = md->length & 0xff;
md->buf[62] = (md->length >> 8) & 0xff;
md->buf[61] = (md->length >> 16) & 0xff;
md->buf[60] = (md->length >> 24) & 0xff;
SM3_compress(md);
/* copy output */
memcpy(hash, md->state, SM3_len / 8);
BigEndian(hash, SM3_len / 8, hash); //if CPU uses little-endian, BigEndian function is a necessary call
}
/******************************************************************************
Function: SM3_256
Description: calculate a hash value from a given message
Calls: SM3_init
SM3_process
SM3_done
Called By:
Input: unsigned char buf[len] //the input message
int len //bytelen of the message
Output: unsigned char hash[32]
Return: null
Others:
*******************************************************************************/
void SM3_256(unsigned char buf[], int len, unsigned char hash[])
{
SM3_STATE md;
SM3_init(&md);
SM3_process(&md, buf, len);
SM3_done(&md, hash);
}
/******************************************************************************
Function: SM3_KDF
Description: key derivation function
Calls: SM3_init
SM3_process
SM3_done
Called By:
Input: unsigned char Z[zlen]
unsigned short zlen //bytelen of Z
unsigned short klen //bytelen of K
Output: unsigned char K[klen] //shared secret key
Return: null
Others:
*******************************************************************************/
void SM3_KDF(unsigned char Z[], unsigned short zlen, unsigned short klen, unsigned char K[])
{
unsigned short i, j, t;
unsigned int bitklen;
SM3_STATE md;
unsigned char Ha[SM2_NUMWORD];
unsigned char ct[4] = {0, 0, 0, 1};
bitklen = klen * 8;
if (bitklen % SM2_NUMBITS)
t = bitklen / SM2_NUMBITS + 1;
else
t = bitklen / SM2_NUMBITS;
//s4: K=Ha1||Ha2||...
for (i = 1; i < t; i++)
{
//s2: Hai=Hv(Z||ct)
SM3_init(&md);
SM3_process(&md, Z, zlen);
SM3_process(&md, ct, 4);
SM3_done(&md, Ha);
memcpy((K + SM2_NUMWORD * (i - 1)), Ha, SM2_NUMWORD);
if (ct[3] == 0xff)
{
ct[3] = 0;
if (ct[2] == 0xff)
{
ct[2] = 0;
if (ct[1] == 0xff)
{
ct[1] = 0;
ct[0]++;
}
else
ct[1]++;
}
else
ct[2]++;
}
else
ct[3]++;
}
//s3: klen/v�������Ĵ���
SM3_init(&md);
SM3_process(&md, Z, zlen);
SM3_process(&md, ct, 4);
SM3_done(&md, Ha);
if (bitklen % SM2_NUMBITS)
{
i = (SM2_NUMBITS - bitklen + SM2_NUMBITS * (bitklen / SM2_NUMBITS)) / 8;
j = (bitklen - SM2_NUMBITS * (bitklen / SM2_NUMBITS)) / 8;
memcpy((K + SM2_NUMWORD * (t - 1)), Ha, j);
}
else
{
memcpy((K + SM2_NUMWORD * (t - 1)), Ha, SM2_NUMWORD);
}
}
R-ate.h
/************************************************************
Note:
Codes here were downloaded from:
http://www.scctc.org.cn/templates/Download/index.aspx?nodeid=71.
The disclaimer was published on the link:
http://www.scctc.org.cn/Upload/accessory/20175/201755105494041082.pdf .
The codes were slightly modified to pass the check of C complier.
*************************************************************/
/************************************************************************
File name: R-ate.h
Version:
Date: Dec 15,2016
Description: this code is achieved according to ake12bnx.cpp in MIRCAL C++ source file.
see ake12bnx.cpp for details.
this code gives calculation of R-ate pairing
Function List:
1.zzn2_pow //regular zzn2 powering
2.set_frobenius_constant //calculate frobenius_constant X
3.q_power_frobenius
4.line
5.g
6.fast_pairing
7.ecap
Notes:
**************************************************************************/
#ifndef HEADER_R_ATE_H
#define HEADER_R_ATE_H
#include "miracl.h"
#include "zzn12_operation.h"
void q_power_frobenius(ecn2 A, zzn2 F);
zzn2 zzn2_pow(zzn2 x, big k);
BOOL fast_pairing(ecn2 P, big Qx, big Qy, big x, zzn2 X, zzn12 *r);
zzn12 line(ecn2 A, ecn2 *C, ecn2 *B, zzn2 slope, zzn2 extra, BOOL Doubling, big Qx, big Qy);
zzn12 g(ecn2 *A, ecn2 *B, big Qx, big Qy);
void set_frobenius_constant(zzn2 *X);
BOOL ecap(ecn2 P, epoint *Q, big x, zzn2 X, zzn12 *r);
BOOL member(zzn12 r, big x, zzn2 F);
#endif
R-ate.c
/************************************************************
Note:
Codes here were downloaded from:
http://www.scctc.org.cn/templates/Download/index.aspx?nodeid=71.
The disclaimer was published on the link:
http://www.scctc.org.cn/Upload/accessory/20175/201755105494041082.pdf .
The codes were slightly modified to pass the check of C complier.
*************************************************************/
#include "zzn12_operation.h"
#include "miracl.h"
#include "R-ate.h"
miracl *mip;
/****************************************************************
Function: q_power_frobenius
Description: F is frobenius_constant X
see ake12bnx.cpp for details in MIRACL c++ source file
Calls: MIRACL functions
Called By: fast_pairing
Input: ecn2 A,zzn2 F
Output: zzn2 A
Return: NULL
Others:
****************************************************************/
void q_power_frobenius(ecn2 A, zzn2 F)
{
// Fast multiplication of A by q (for Trace-Zero group members only)
zzn2 x, y, z, w, r;
x.a = mirvar(0);
x.b = mirvar(0);
y.a = mirvar(0);
y.b = mirvar(0);
z.a = mirvar(0);
z.b = mirvar(0);
w.a = mirvar(0);
w.b = mirvar(0);
r.a = mirvar(0);
r.b = mirvar(0);
ecn2_get(&A, &x, &y, &z);
zzn2_copy(&F, &r); //r=F
if (get_mip()->TWIST == MR_SEXTIC_M)
zzn2_inv(&r); // could be precalculated
zzn2_mul(&r, &r, &w); //w=r*r
zzn2_conj(&x, &x);
zzn2_mul(&w, &x, &x);
zzn2_conj(&y, &y);
zzn2_mul(&w, &r, &w);
zzn2_mul(&w, &y, &y);
zzn2_conj(&z, &z);
ecn2_setxyz(&x, &y, &z, &A);
}
/****************************************************************
Function: zzn2_pow
Description: regular zzn2 powering
see zzn2.cpp for details in MIRACL c++ source file
Calls: MIRACL functions
Called By: set_frobenius_constant
Input: zzn2 x,big k
Output: null
Return: zzn2
Others:
****************************************************************/
zzn2 zzn2_pow(zzn2 x, big k)
{
int i, j, nb, n, nbw, nzs;
big zero;
zzn2 res, u2, t[16];
zero = mirvar(0);
res.a = mirvar(0);
res.b = mirvar(0);
u2.a = mirvar(0);
u2.b = mirvar(0);
if (zzn2_iszero(&x))
{
zzn2_zero(&res);
return res;
}
if (size(k) == 0)
{
zzn2_from_int(1, &res);
return res;
}
if (size(k) == 1)
return x;
// Prepare table for windowing
zzn2_mul(&x, &x, &u2);
t[0].a = mirvar(0);
t[0].b = mirvar(0);
zzn2_copy(&x, &t[0]);
for (i = 1; i < 16; i++)
{
t[i].a = mirvar(0);
t[i].b = mirvar(0);
zzn2_mul(&t[i - 1], &u2, &t[i]);
}
// Left to right method - with windows
zzn2_copy(&x, &res);
nb = logb2(k);
if (nb > 1)
for (i = nb - 2; i >= 0;)
{
//Note new parameter of window_size=5. Default to 5, but reduce to 4 (or even 3) to save RAM
n = mr_window(k, i, &nbw, &nzs, 5);
for (j = 0; j < nbw; j++)
zzn2_mul(&res, &res, &res);
if (n > 0)
zzn2_mul(&res, &t[n / 2], &res);
i -= nbw;
if (nzs)
{
for (j = 0; j < nzs; j++)
zzn2_mul(&res, &res, &res);
i -= nzs;
}
}
return res;
}
/****************************************************************
Function: fast_pairing
Description: R-ate Pairing G2 x G1 -> GT
P is a point of order q in G1. Q(x,y) is a point of order q in G2.
Note that P is a point on the sextic twist of the curve over Fp^2,
Q(x,y) is a point on the curve over the base field Fp
see ake12bnx.cpp for details in MIRACL c++ source file
Calls: MIRACL functions,zzn12_init,g,q_power_frobenius
zzn12_copy,zzn12_conj,zzn12_div,zzn12_powq,zzn12_inverse
Called By: ecap
Input: ecn2 P,big Qx,big Qy,big x,zzn2 X
Output: zzn12 *r
Return: FALSE: r=0
TRUE: correct calculation
Others:
****************************************************************/
BOOL fast_pairing(ecn2 P, big Qx, big Qy, big x, zzn2 X, zzn12 *r)
{
int i, nb;
big n, zero, negify_x;
ecn2 A, KA;
zzn12 t0, x0, x1, x2, x3, x4, x5, res;
zero = mirvar(0);
n = mirvar(0);
negify_x = mirvar(0);
A.x.a = mirvar(0);
A.x.b = mirvar(0);
A.y.a = mirvar(0);
A.y.b = mirvar(0);
A.z.a = mirvar(0);
A.z.b = mirvar(0);
A.marker = MR_EPOINT_INFINITY;
KA.x.a = mirvar(0);
KA.x.b = mirvar(0);
KA.y.a = mirvar(0);
KA.y.b = mirvar(0);
KA.z.a = mirvar(0);
KA.z.b = mirvar(0);
KA.marker = MR_EPOINT_INFINITY;
zzn12_init(&t0);
zzn12_init(&x0);
zzn12_init(&x1);
zzn12_init(&x2);
zzn12_init(&x3);
zzn12_init(&x4);
zzn12_init(&x5);
zzn12_init(&res);
premult(x, 6, n);
incr(n, 2, n); //n=(6*x+2);
if (mr_compare(x, zero) < 0) //x<0
negify(n, n); //n=-(6*x+2);
ecn2_copy(&P, &A);
nb = logb2(n);
zzn4_from_int(1, &res.a);
res.unitary = TRUE; //res=1
// Short Miller loop
res.miller = TRUE;
for (i = nb - 2; i >= 0; i--)
{
zzn12_mul(res, res, &res);
zzn12_mul(res, g(&A, &A, Qx, Qy), &res);
if (mr_testbit(n, i))
zzn12_mul(res, g(&A, &P, Qx, Qy), &res);
}
// Combining ideas due to Longa, Aranha et al. and Naehrig
ecn2_copy(&P, &KA);
q_power_frobenius(KA, X);
if (mr_compare(x, zero) < 0)
{
ecn2_negate(&A, &A);
zzn12_conj(&res, &res);
}
zzn12_mul(res, g(&A, &KA, Qx, Qy), &res);
q_power_frobenius(KA, X);
ecn2_negate(&KA, &KA);
zzn12_mul(res, g(&A, &KA, Qx, Qy), &res);
if (zzn4_iszero(&res.a) && zzn4_iszero(&res.b) && zzn4_iszero(&res.c))
return FALSE;
// The final exponentiation
zzn12_copy(&res, &t0); //t0=r;
zzn12_conj(&res, &res);
zzn12_div(res, t0, &res);
res.miller = FALSE;
res.unitary = FALSE;
zzn12_copy(&res, &t0); //t0=r;
zzn12_powq(X, &res);
zzn12_powq(X, &res);
zzn12_mul(res, t0, &res); // r^[(p^6-1)*(p^2+1)]
res.miller = FALSE;
res.unitary = TRUE;
// Newer new idea...
// See "On the final exponentiation for calculating pairings on ordinary elliptic curves"
// Michael Scott and Naomi Benger and Manuel Charlemagne and Luis J. Dominguez Perez and Ezekiel J. Kachisa
zzn12_copy(&res, &t0);
zzn12_powq(X, &t0);
zzn12_copy(&t0, &x0);
zzn12_powq(X, &x0); //x0=t0
zzn12_mul(res, t0, &x1);
zzn12_mul(x0, x1, &x0); // x0*=(res*t0);
zzn12_powq(X, &x0);
x1 = zzn12_inverse(res); // just a conjugation!
negify(x, negify_x);
x4 = zzn12_pow(res, negify_x); //negify_x=-x x is sparse.
zzn12_copy(&x4, &x3);
zzn12_powq(X, &x3);
x2 = zzn12_pow(x4, negify_x);
x5 = zzn12_inverse(x2);
t0 = zzn12_pow(x2, negify_x);
zzn12_powq(X, &x2);
zzn12_div(x4, x2, &x4);
zzn12_powq(X, &x2);
zzn12_copy(&t0, &res); // res=t0
zzn12_powq(X, &res);
zzn12_mul(t0, res, &t0);
zzn12_mul(t0, t0, &t0);
zzn12_mul(t0, x4, &t0);
zzn12_mul(t0, x5, &t0); //t0*=t0;t0*=x4;t0*=x5;
zzn12_mul(x3, x5, &res);
zzn12_mul(res, t0, &res); //res=x3*x5;res*=t0;
zzn12_mul(t0, x2, &t0); //t0*=x2;
zzn12_mul(res, res, &res);
zzn12_mul(res, t0, &res);
zzn12_mul(res, res, &res); //res*=res; res*=t0;res*=res;
zzn12_mul(res, x1, &t0); // t0=res*x1;
zzn12_mul(res, x0, &res); //res*=x0;
zzn12_mul(t0, t0, &t0);
zzn12_mul(t0, res, &t0); //t0*=t0;t0*=res;
zzn12_copy(&t0, r); //r= t0;
return TRUE;
}
/****************************************************************
Function: line
Description: Line from A to destination C. Let A=(x,y)
Line Y-slope.X-c=0, through A, so intercept c=y-slope.x
Line Y-slope.X-y+slope.x = (Y-y)-slope.(X-x) = 0
Now evaluate at Q -> return (Qy-y)-slope.(Qx-x)
see ake12bnx.cpp for details in MIRACL c++ source file
Calls: MIRACL functions,zzn12_init
Called By: g
Input: ecn2 A,ecn2 *C,ecn2 *B,zzn2 slope,zzn2 extra,BOOL Doubling,big Qx,big Qy
Output:
Return: zzn12
Others:
****************************************************************/
zzn12 line(ecn2 A, ecn2 *C, ecn2 *B, zzn2 slope, zzn2 extra, BOOL Doubling, big Qx, big Qy)
{
zzn12 res;
zzn2 X, Y, Z, Z2, U, QY, CZ;
big QX;
QX = mirvar(0);
X.a = mirvar(0);
X.b = mirvar(0);
Y.a = mirvar(0);
Y.b = mirvar(0);
Z.a = mirvar(0);
Z.b = mirvar(0);
Z2.a = mirvar(0);
Z2.b = mirvar(0);
U.a = mirvar(0);
U.b = mirvar(0);
QY.a = mirvar(0);
QY.b = mirvar(0);
CZ.a = mirvar(0);
CZ.b = mirvar(0);
zzn12_init(&res);
ecn2_getz(C, &CZ);
// Thanks to A. Menezes for pointing out this optimization...
if (Doubling)
{
ecn2_get(&A, &X, &Y, &Z);
zzn2_mul(&Z, &Z, &Z2); //Z2=Z*Z
//X=slope*X-extra
zzn2_mul(&slope, &X, &X);
zzn2_sub(&X, &extra, &X);
zzn2_mul(&CZ, &Z2, &U);
//(-(Z*Z*slope)*Qx);
nres(Qx, QX);
zzn2_mul(&Z2, &slope, &Y);
zzn2_smul(&Y, QX, &Y);
zzn2_negate(&Y, &Y);
if (get_mip()->TWIST == MR_SEXTIC_M)
{ // "multiplied across" by i to simplify
zzn2_from_big(Qy, &QY);
zzn2_txx(&QY);
zzn2_mul(&U, &QY, &QY);
zzn4_from_zzn2s(&QY, &X, &res.a);
zzn2_copy(&Y, &(res.c.b));
}
if (get_mip()->TWIST == MR_SEXTIC_D)
{
zzn2_smul(&U, Qy, &QY);
zzn4_from_zzn2s(&QY, &X, &res.a);
zzn2_copy(&Y, &(res.b.b));
}
}
else
{ //slope*X-Y*Z
ecn2_getxy(B, &X, &Y);
zzn2_mul(&slope, &X, &X);
zzn2_mul(&Y, &CZ, &Y);
zzn2_sub(&X, &Y, &X);
//(-slope*Qx)
nres(Qx, QX);
zzn2_smul(&slope, QX, &Z);
zzn2_negate(&Z, &Z);
if (get_mip()->TWIST == MR_SEXTIC_M)
{
zzn2_from_big(Qy, &QY);
zzn2_txx(&QY);
zzn2_mul(&CZ, &QY, &QY);
zzn4_from_zzn2s(&QY, &X, &res.a);
zzn2_copy(&Z, &(res.c.b));
}
if (get_mip()->TWIST == MR_SEXTIC_D)
{
zzn2_smul(&CZ, Qy, &QY);
zzn4_from_zzn2s(&QY, &X, &res.a);
zzn2_copy(&Z, &(res.b.b));
}
}
return res;
}
/****************************************************************
Function: g
Description: Add A=A+B (or A=A+A),Return line function value
see ake12bnx.cpp for details in MIRACL c++ source file
Calls: MIRACL functions,zzn12_init,line
Called By:
Input: ecn2 *A,ecn2 *B,big Qx,big Qy
Output:
Return: zzn12
Others:
****************************************************************/
zzn12 g(ecn2 *A, ecn2 *B, big Qx, big Qy)
{
zzn2 lam, extra;
BOOL Doubling;
ecn2 P;
zzn12 res;
lam.a = mirvar(0);
lam.b = mirvar(0);
extra.a = mirvar(0);
extra.b = mirvar(0);
P.x.a = mirvar(0);
P.x.b = mirvar(0);
P.y.a = mirvar(0);
P.y.b = mirvar(0);
P.z.a = mirvar(0);
P.z.b = mirvar(0);
P.marker = MR_EPOINT_INFINITY;
zzn12_init(&res);
ecn2_copy(A, &P);
Doubling = ecn2_add2(B, A, &lam, &extra);
if (A->marker == MR_EPOINT_INFINITY)
{
zzn4_from_int(1, &res.a);
res.miller = FALSE;
res.unitary = TRUE;
}
else
res = line(P, A, B, lam, extra, Doubling, Qx, Qy);
return res;
}
/****************************************************************
Function: set_frobenius_constant
Description: calculate frobenius_constant X
see ake12bnx.cpp for details in MIRACL c++ source file
Calls: MIRACL functions,zzn2_pow
Called By: SM9_init
Input: NULL
Output: zzn2 *X
Return: NULL
Others:
****************************************************************/
void set_frobenius_constant(zzn2 *X)
{
big p, zero, one, two;
p = mirvar(0);
zero = mirvar(0);
one = mirvar(0);
two = mirvar(0);
convert(0, zero);
convert(1, one);
convert(2, two);
mip = get_mip();
copy(mip->modulus, p);
switch (get_mip()->pmod8)
{
case 5:
zzn2_from_bigs(zero, one, X); // = (sqrt(-2)^(p-1)/2
break;
case 3:
zzn2_from_bigs(one, one, X); // = (1+sqrt(-1))^(p-1)/2
break;
case 7:
zzn2_from_bigs(two, one, X); // = (2+sqrt(-1))^(p-1)/2
default:
break;
}
decr(p, 1, p);
subdiv(p, 6, p);
*X = zzn2_pow(*X, p);
}
/****************************************************************
Function: ecap
Description: caculate Rate pairing
see ake12bnx.cpp for details in MIRACL c++ source file
Calls: MIRACL functions,fast_pairing
Called By: SM9_Sign,SM9_Verify
Input: ecn2 P,epoint *Q,big x,zzn2 X
Output: zzn12 *r
Return: FALSE: calculation error
TRUE: correct calculation
Others:
****************************************************************/
BOOL ecap(ecn2 P, epoint *Q, big x, zzn2 X, zzn12 *r)
{
BOOL Ok;
big Qx, Qy;
Qx = mirvar(0);
Qy = mirvar(0);
ecn2_norm(&P);
epoint_get(Q, Qx, Qy);
Ok = fast_pairing(P, Qx, Qy, x, X, r);
if (Ok)
return TRUE;
return FALSE;
}
/****************************************************************
Function: member
Description: ctest if a zzn12 element is of order q
test r^q = r^(p+1-t) =1, so test r^p=r^(t-1)
see ake12bnx.cpp for details in MIRACL c++ source file
Calls: MIRACL functions,zzn12_init,zzn12_copy,zzn12_powq
Called By: SM9_Sign,SM9_Verify
Input: zzn12 r,big x,zzn2 F
Output: NULL
Return: FALSE: zzn12 element is not of order q
TRUE: zzn12 element is of order q
Others:
****************************************************************/
BOOL member(zzn12 r, big x, zzn2 F)
{
zzn12 w;
big six;
six = mirvar(0);
zzn12_init(&w);
convert(6, six);
zzn12_copy(&r, &w); //w=r
zzn12_powq(F, &w);
r = zzn12_pow(r, x);
r = zzn12_pow(r, x);
r = zzn12_pow(r, six); // t-1=6x^2
if (zzn4_compare(&w.a, &r.a) && zzn4_compare(&w.a, &r.a) && zzn4_compare(&w.a, &r.a))
return TRUE;
return FALSE;
}
zzn12_operatoin.h
/************************************************************
Note:
Codes here were downloaded from:
http://www.scctc.org.cn/templates/Download/index.aspx?nodeid=71.
The disclaimer was published on the link:
http://www.scctc.org.cn/Upload/accessory/20175/201755105494041082.pdf .
The codes were slightly modified to pass the check of C complier.
*************************************************************/
/************************************************************************
File name: zzn12_operation.h
Version:
Date: Dec 15,2016
Description: this code is achieved according to zzn12a.h and zzn12a.cpp in MIRCAL C++ source
file writen by M. Scott.
so,see zzn12a.h and zzn12a.cpp for details.
this code define one struct zzn12,and based on it give many fuctions.
Function List:
1.zzn12_init //Initiate struct zzn12
2.zzn12_copy //copy one zzn12 to another
3.zzn12_mul //z=x*y,achieve multiplication with two zzn12
4.zzn12_conj //achieve conjugate complex
5.zzn12_inverse //element inversion
6.zzn12_powq //
7.zzn12_div //division operation
8.zzn12_pow //regular zzn12 powering
Notes:
**************************************************************************/
#ifndef HEADER_ZZN12_OPERATION_H
#define HEADER_ZZN12_OPERATION_H
#include "miracl.h"
typedef struct
{
zzn4 a, b, c;
BOOL unitary; // "unitary property means that fast squaring can be used, and inversions are just conjugates
BOOL miller; // "miller" property means that arithmetic on this instance can ignore multiplications
// or divisions by constants - as instance will eventually be raised to (p-1).
} zzn12;
void zzn12_init(zzn12 *x);
void zzn12_copy(zzn12 *x, zzn12 *y);
zzn12_mul(zzn12 x, zzn12 y, zzn12 *z);
void zzn12_conj(zzn12 *x, zzn12 *y);
zzn12 zzn12_inverse(zzn12 w);
void zzn12_powq(zzn2 F, zzn12 *y);
void zzn12_div(zzn12 x, zzn12 y, zzn12 *z);
zzn12 zzn12_pow(zzn12 x, big k);
#endif
zzn12_operation.c
/************************************************************
Note:
Codes here were downloaded from:
http://www.scctc.org.cn/templates/Download/index.aspx?nodeid=71.
The disclaimer was published on the link:
http://www.scctc.org.cn/Upload/accessory/20175/201755105494041082.pdf .
The codes were slightly modified to pass the check of C complier.
*************************************************************/
#include "zzn12_operation.h"
zzn2 X; //Frobniues constant
/****************************************************************
Function: zzn12_init
Description: Initiate struct zzn12
Calls: MIRACL functions
Called By:
Input: zzn12 *x
Output: null
Return: null
Others:
****************************************************************/
void zzn12_init(zzn12 *x)
{
x->a.a.a = mirvar(0);
x->a.a.b = mirvar(0);
x->a.b.a = mirvar(0);
x->a.b.b = mirvar(0);
x->a.unitary = FALSE;
x->b.a.a = mirvar(0);
x->b.a.b = mirvar(0);
x->b.b.a = mirvar(0);
x->b.b.b = mirvar(0);
x->b.unitary = FALSE;
x->c.a.a = mirvar(0);
x->c.a.b = mirvar(0);
x->c.b.a = mirvar(0);
x->c.b.b = mirvar(0);
x->c.unitary = FALSE;
x->miller = FALSE;
x->unitary = FALSE;
}
/****************************************************************
Function: zzn12_copy
Description: copy y=x
Calls: MIRACL functions
Called By:
Input: zzn12 *x
Output: zzn12 *y
Return: null
Others:
****************************************************************/
void zzn12_copy(zzn12 *x, zzn12 *y)
{
zzn4_copy(&x->a, &y->a);
zzn4_copy(&x->b, &y->b);
zzn4_copy(&x->c, &y->c);
y->miller = x->miller;
y->unitary = x->unitary;
}
/****************************************************************
Function: zzn12_mul
Description: z=x*y,see zzn12a.h and zzn12a.cpp for details in MIRACL c++ source file
Calls: MIRACL functions
Called By:
Input: zzn12 x,y
Output: zzn12 *z
Return: null
Others:
****************************************************************/
zzn12_mul(zzn12 x, zzn12 y, zzn12 *z)
{
// Karatsuba
zzn4 Z0, Z1, Z2, Z3, T0, T1;
BOOL zero_c, zero_b;
Z0.a.a = mirvar(0);
Z0.a.b = mirvar(0);
Z0.b.a = mirvar(0);
Z0.b.b = mirvar(0);
Z0.unitary = FALSE;
Z1.a.a = mirvar(0);
Z1.a.b = mirvar(0);
Z1.b.a = mirvar(0);
Z1.b.b = mirvar(0);
Z1.unitary = FALSE;
Z2.a.a = mirvar(0);
Z2.a.b = mirvar(0);
Z2.b.a = mirvar(0);
Z2.b.b = mirvar(0);
Z2.unitary = FALSE;
Z3.a.a = mirvar(0);
Z3.a.b = mirvar(0);
Z3.b.a = mirvar(0);
Z3.b.b = mirvar(0);
Z3.unitary = FALSE;
T0.a.a = mirvar(0);
T0.a.b = mirvar(0);
T0.b.a = mirvar(0);
T0.b.b = mirvar(0);
T0.unitary = FALSE;
T1.a.a = mirvar(0);
T1.a.b = mirvar(0);
T1.b.a = mirvar(0);
T1.b.b = mirvar(0);
T1.unitary = FALSE;
zzn12_copy(&x, z);
if (zzn4_compare(&x.a, &y.a) && zzn4_compare(&x.a, &y.a) && zzn4_compare(&x.a, &y.a))
{
if (x.unitary == TRUE)
{
zzn4_copy(&x.a, &Z0);
zzn4_mul(&x.a, &x.a, &z->a);
zzn4_copy(&z->a, &Z3);
zzn4_add(&z->a, &z->a, &z->a);
zzn4_add(&z->a, &Z3, &z->a);
zzn4_conj(&Z0, &Z0);
zzn4_add(&Z0, &Z0, &Z0);
zzn4_sub(&z->a, &Z0, &z->a);
zzn4_copy(&x.c, &Z1);
zzn4_mul(&Z1, &Z1, &Z1);
zzn4_tx(&Z1);
zzn4_copy(&Z1, &Z3);
zzn4_add(&Z1, &Z1, &Z1);
zzn4_add(&Z1, &Z3, &Z1);
zzn4_copy(&x.b, &Z2);
zzn4_mul(&Z2, &Z2, &Z2);
zzn4_copy(&Z2, &Z3);
zzn4_add(&Z2, &Z2, &Z2);
zzn4_add(&Z2, &Z3, &Z2);
zzn4_conj(&x.b, &z->b);
zzn4_add(&z->b, &z->b, &z->b);
zzn4_conj(&x.c, &z->c);
zzn4_add(&z->c, &z->c, &z->c);
zzn4_negate(&z->c, &z->c);
zzn4_add(&z->b, &Z1, &z->b);
zzn4_add(&z->c, &Z2, &z->c);
}
else
{
if (!x.miller)
{ // Chung-Hasan SQR2
zzn4_copy(&x.a, &Z0);
zzn4_mul(&Z0, &Z0, &Z0);
zzn4_mul(&x.b, &x.c, &Z1);
zzn4_add(&Z1, &Z1, &Z1);
zzn4_copy(&x.c, &Z2);
zzn4_mul(&Z2, &Z2, &Z2);
zzn4_mul(&x.a, &x.b, &Z3);
zzn4_add(&Z3, &Z3, &Z3);
zzn4_add(&x.a, &x.b, &z->c);
zzn4_add(&z->c, &x.c, &z->c);
zzn4_mul(&z->c, &z->c, &z->c);
zzn4_tx(&Z1);
zzn4_add(&Z0, &Z1, &z->a);
zzn4_tx(&Z2);
zzn4_add(&Z3, &Z2, &z->b);
zzn4_add(&Z0, &Z1, &T0);
zzn4_add(&T0, &Z2, &T0);
zzn4_add(&T0, &Z3, &T0);
zzn4_sub(&z->c, &T0, &z->c);
}
else
{ // Chung-Hasan SQR3 - actually calculate 2x^2 !
// Slightly dangerous - but works as will be raised to p^{k/2}-1
// which wipes out the 2.
zzn4_copy(&x.a, &Z0);
zzn4_mul(&Z0, &Z0, &Z0); // a0^2 = S0
zzn4_copy(&x.c, &Z2);
zzn4_mul(&Z2, &x.b, &Z2);
zzn4_add(&Z2, &Z2, &Z2); // 2a1.a2 = S3
zzn4_copy(&x.c, &Z3);
zzn4_mul(&Z3, &Z3, &Z3);
; // a2^2 = S4
zzn4_add(&x.c, &x.a, &z->c); // a0+a2
zzn4_copy(&x.b, &Z1);
zzn4_add(&Z1, &z->c, &Z1);
zzn4_mul(&Z1, &Z1, &Z1); // (a0+a1+a2)^2 =S1
zzn4_sub(&z->c, &x.b, &z->c);
zzn4_mul(&z->c, &z->c, &z->c); // (a0-a1+a2)^2 =S2
zzn4_add(&Z2, &Z2, &Z2);
zzn4_add(&Z0, &Z0, &Z0);
zzn4_add(&Z3, &Z3, &Z3);
zzn4_sub(&Z1, &z->c, &T0);
zzn4_sub(&T0, &Z2, &T0);
zzn4_sub(&Z1, &Z0, &T1);
zzn4_sub(&T1, &Z3, &T1);
zzn4_add(&z->c, &T1, &z->c);
zzn4_tx(&Z3);
zzn4_add(&T0, &Z3, &z->b);
zzn4_tx(&Z2);
zzn4_add(&Z0, &Z2, &z->a);
}
}
}
else
{
// Karatsuba
zero_b = zzn4_iszero(&y.b);
zero_c = zzn4_iszero(&y.c);
zzn4_mul(&x.a, &y.a, &Z0); //9
if (!zero_b)
zzn4_mul(&x.b, &y.b, &Z2); //+6
zzn4_add(&x.a, &x.b, &T0);
zzn4_add(&y.a, &y.b, &T1);
zzn4_mul(&T0, &T1, &Z1); //+9
zzn4_sub(&Z1, &Z0, &Z1);
if (!zero_b)
zzn4_sub(&Z1, &Z2, &Z1);
zzn4_add(&x.b, &x.c, &T0);
zzn4_add(&y.b, &y.c, &T1);
zzn4_mul(&T0, &T1, &Z3); //+6
if (!zero_b)
zzn4_sub(&Z3, &Z2, &Z3);
zzn4_add(&x.a, &x.c, &T0);
zzn4_add(&y.a, &y.c, &T1);
zzn4_mul(&T0, &T1, &T0); //+9=39 for "special case"
if (!zero_b)
zzn4_add(&Z2, &T0, &Z2);
else
zzn4_copy(&T0, &Z2);
zzn4_sub(&Z2, &Z0, &Z2);
zzn4_copy(&Z1, &z->b);
if (!zero_c)
{ // exploit special form of BN curve line function
zzn4_mul(&x.c, &y.c, &T0);
zzn4_sub(&Z2, &T0, &Z2);
zzn4_sub(&Z3, &T0, &Z3);
zzn4_tx(&T0);
zzn4_add(&z->b, &T0, &z->b);
}
zzn4_tx(&Z3);
zzn4_add(&Z0, &Z3, &z->a);
zzn4_copy(&Z2, &z->c);
if (!y.unitary)
z->unitary = FALSE;
}
}
/****************************************************************
Function: zzn12_conj
Description: achieve conjugate complex
see zzn12a.h and zzn1212.cpp for details in MIRACL c++ source file
Calls: MIRACL functions
Called By:
Input: zzn12 x,y
Output: zzn12 *z
Return: null
Others:
****************************************************************/
void zzn12_conj(zzn12 *x, zzn12 *y)
{
zzn4_conj(&x->a, &y->a);
zzn4_conj(&x->b, &y->b);
zzn4_negate(&y->b, &y->b);
zzn4_conj(&x->c, &y->c);
y->miller = x->miller;
y->unitary = x->unitary;
}
/****************************************************************
Function: zzn12_inverse
Description: element inversion,
see zzn12a.h and zzn1212.cpp for details in MIRACL c++ source file
Calls: MIRACL functions,zzn12_init,zzn12_conj
Called By:
Input: zzn12 w
Output:
Return: zzn12
Others:
****************************************************************/
zzn12 zzn12_inverse(zzn12 w)
{
zzn4 tmp1, tmp2;
zzn12 res;
tmp1.a.a = mirvar(0);
tmp1.a.b = mirvar(0);
tmp1.b.a = mirvar(0);
tmp1.b.b = mirvar(0);
tmp1.unitary = FALSE;
tmp2.a.a = mirvar(0);
tmp2.a.b = mirvar(0);
tmp2.b.a = mirvar(0);
tmp2.b.b = mirvar(0);
tmp2.unitary = FALSE;
zzn12_init(&res);
if (w.unitary)
{
zzn12_conj(&w, &res);
return res;
}
//res.a=w.a*w.a-tx(w.b*w.c);
zzn4_mul(&w.a, &w.a, &res.a);
zzn4_mul(&w.b, &w.c, &res.b);
zzn4_tx(&res.b);
zzn4_sub(&res.a, &res.b, &res.a);
//res.b=tx(w.c*w.c)-w.a*w.b;
zzn4_mul(&w.c, &w.c, &res.c);
zzn4_tx(&res.c);
zzn4_mul(&w.a, &w.b, &res.b);
zzn4_sub(&res.c, &res.b, &res.b);
//res.c=w.b*w.b-w.a*w.c;
zzn4_mul(&w.b, &w.b, &res.c);
zzn4_mul(&w.a, &w.c, &tmp1);
zzn4_sub(&res.c, &tmp1, &res.c);
//tmp1=tx(w.b*res.c)+w.a*res.a+tx(w.c*res.b);
zzn4_mul(&w.b, &res.c, &tmp1);
zzn4_tx(&tmp1);
zzn4_mul(&w.a, &res.a, &tmp2);
zzn4_add(&tmp1, &tmp2, &tmp1);
zzn4_mul(&w.c, &res.b, &tmp2);
zzn4_tx(&tmp2);
zzn4_add(&tmp1, &tmp2, &tmp1);
zzn4_inv(&tmp1);
zzn4_mul(&res.a, &tmp1, &res.a);
zzn4_mul(&res.b, &tmp1, &res.b);
zzn4_mul(&res.c, &tmp1, &res.c);
return res;
}
/****************************************************************
Function: zzn12_powq
Description: Frobenius F=x^p. Assumes p=1 mod 6
see zzn12a.h and zzn1212.cpp for details in MIRACL c++ source file
Calls: MIRACL functions
Called By:
Input: zzn2 F
Output: zzn12 *y
Return: NULL
Others:
****************************************************************/
void zzn12_powq(zzn2 F, zzn12 *y)
{
zzn2 X2, X3;
X2.a = mirvar(0);
X2.b = mirvar(0);
X3.a = mirvar(0);
X3.b = mirvar(0);
zzn2_mul(&F, &F, &X2);
zzn2_mul(&X2, &F, &X3);
zzn4_powq(&X3, &y->a);
zzn4_powq(&X3, &y->b);
zzn4_powq(&X3, &y->c);
zzn4_smul(&y->b, &X, &y->b);
zzn4_smul(&y->c, &X2, &y->c);
}
/****************************************************************
Function: zzn12_div
Description: z=x/y
see zzn12a.h and zzn1212.cpp for details in MIRACL c++ source file
Calls: MIRACL functions,zzn12_inverse,zzn12_mul
Called By:
Input: zzn12 x,y
Output: zzn12 *z
Return: NULL
Others:
****************************************************************/
void zzn12_div(zzn12 x, zzn12 y, zzn12 *z)
{
y = zzn12_inverse(y);
zzn12_mul(x, y, z);
}
/****************************************************************
Function: zzn12_pow
Description: regular zzn12 powering,If k is low Hamming weight this will be just as good.
see zzn12a.h and zzn1212.cpp for details in MIRACL c++ source file
Calls: MIRACL functions,zzn12_inverse,zzn12_mul,zzn12_copy,zzn12_init
Called By:
Input: zzn12 x,big k
Output:
Return: zzn12
Others:
****************************************************************/
zzn12 zzn12_pow(zzn12 x, big k)
{
big zero, tmp, tmp1;
int nb, i;
BOOL invert_it;
zzn12 res;
zero = mirvar(0);
tmp = mirvar(0);
tmp1 = mirvar(0);
zzn12_init(&res);
copy(k, tmp1);
invert_it = FALSE;
if (mr_compare(tmp1, zero) == 0)
{
tmp = get_mip()->one;
zzn4_from_big(tmp, &res.a);
return res;
}
if (mr_compare(tmp1, zero) < 0)
{
negify(tmp1, tmp1);
invert_it = TRUE;
}
nb = logb2(k);
zzn12_copy(&x, &res);
if (nb > 1)
for (i = nb - 2; i >= 0; i--)
{
zzn12_mul(res, res, &res);
if (mr_testbit(k, i))
zzn12_mul(res, x, &res);
}
if (invert_it)
res = zzn12_inverse(res);
return res;
}
SM9_sv.h
/************************************************************
Note:
Codes here were downloaded from:
http://www.scctc.org.cn/templates/Download/index.aspx?nodeid=71.
The disclaimer was published on the link:
http://www.scctc.org.cn/Upload/accessory/20175/201755105494041082.pdf .
The codes were slightly modified to pass the check of C complier.
*************************************************************/
#ifndef HEADER_SM9_SV_H
#define HEADER_SM9_SV_H
///************************************************************************
// File name: SM9_sv.h
// Version: SM9_sv_V1.0
// Date: Dec 15,2016
// Description: implementation of SM9 signature algorithm and verification algorithm
// all operations based on BN curve line function
// Function List:
// 1.bytes128_to_ecn2 //convert 128 bytes into ecn2
// 2.zzn12_ElementPrint //print all element of struct zzn12
// 3.ecn2_Bytes128_Print //print 128 bytes of ecn2
// 4.LinkCharZzn12 //link two different types(unsigned char and zzn12)to one(unsigned char)
// 5.Test_Point //test if the given point is on SM9 curve
// 6.Test_Range //test if the big x belong to the range[1,N-1]
// 7.SM9_Init //initiate SM9 curve
// 8.SM9_H1 //function H1 in SM9 standard 5.4.2.2
// 9.SM9_H2 //function H2 in SM9 standard 5.4.2.3
// 10.SM9_GenerateSignKey //generate signed private and public key
// 11.SM9_Sign //SM9 signature algorithm
// 12.SM9_Verify //SM9 verification
// 13.SM9_SelfCheck() //SM9 slef-check
//
// Notes:
// This SM9 implementation source code can be used for academic, non-profit making or non-commercial use only.
// This SM9 implementation is created on MIRACL. SM9 implementation source code provider does not provide MIRACL library, MIRACL license or any permission to use MIRACL library. Any commercial use of MIRACL requires a license which may be obtained from Shamus Software Ltd.
//**************************************************************************/
#include
#include
#include "miracl.h"
#include "R-ate.h"
#define BNLEN 32 //BN curve with 256bit is used in SM9 algorithm
#define SM9_ASK_MEMORY_ERR 0x00000001 //申请内存失败
#define SM9_H_OUTRANGE 0x00000002 //签名H不属于[1,N-1]
#define SM9_DATA_MEMCMP_ERR 0x00000003 //数据对比不一致
#define SM9_MEMBER_ERR 0x00000004 //群的阶错误
#define SM9_MY_ECAP_12A_ERR 0x00000005 //R-ate对计算出现错误
#define SM9_S_NOT_VALID_G1 0x00000006 //S不属于群G1
#define SM9_G1BASEPOINT_SET_ERR 0x00000007 //G1基点设置错误
#define SM9_G2BASEPOINT_SET_ERR 0x00000008 //G2基点设置错误
#define SM9_L_error 0x00000009 //参数L错误
#define SM9_GEPUB_ERR 0x0000000A //生成公钥错误
#define SM9_GEPRI_ERR 0x0000000B //生成私钥错误
#define SM9_SIGN_ERR 0x0000000C //签名错误
BOOL bytes128_to_ecn2(unsigned char Ppubs[], ecn2 *res);
void zzn12_ElementPrint(zzn12 x);
void ecn2_Bytes128_Print(ecn2 x);
void LinkCharZzn12(unsigned char *message, int len, zzn12 w, unsigned char *Z, int Zlen);
int Test_Point(epoint *point);
int Test_Range(big x);
int SM9_Init();
int SM9_H1(unsigned char Z[], int Zlen, big n, big h1);
int SM9_H2(unsigned char Z[], int Zlen, big n, big h2);
int SM9_GenerateSignKey(unsigned char hid[], unsigned char *ID, int IDlen,
big ks, unsigned char Ppubs[], unsigned char dsa[]);
int SM9_Sign(unsigned char hid[], unsigned char *IDA, unsigned char *message, int len, unsigned char rand[],
unsigned char dsa[], unsigned char Ppub[], unsigned char H[], unsigned char S[]);
int SM9_Verify(unsigned char H[], unsigned char S[], unsigned char hid[],
unsigned char *IDA, unsigned char *message, int len, unsigned char Ppub[]);
int SM9_SelfCheck();
#endif
SM9_sv.c
/************************************************************
Note:
Codes here were downloaded from:
http://www.scctc.org.cn/templates/Download/index.aspx?nodeid=71.
The disclaimer was published on the link:
http://www.scctc.org.cn/Upload/accessory/20175/201755105494041082.pdf .
The codes were slightly modified to pass the check of C complier.
*************************************************************/
///************************************************************************
// File name: SM9_sv.c
// Version: SM9_sv_V1.0
// Date: Dec 15,2016
// Description: implementation of SM9 signature algorithm and verification algorithm
// all operations based on BN curve line function
// Function List:
// 1.bytes128_to_ecn2 //convert 128 bytes into ecn2
// 2.zzn12_ElementPrint //print all element of struct zzn12
// 3.ecn2_Bytes128_Print //print 128 bytes of ecn2
// 4.LinkCharZzn12 //link two different types(unsigned char and zzn12)to one(unsigned char)
// 5.Test_Point //test if the given point is on SM9 curve
// 6.Test_Range //test if the big x belong to the range[1,N-1]
// 7.SM9_Init //initiate SM9 curve
// 8.SM9_H1 //function H1 in SM9 standard 5.4.2.2
// 9.SM9_H2 //function H2 in SM9 standard 5.4.2.3
// 10.SM9_GenerateSignKey //generate signed private and public key
// 11.SM9_Sign //SM9 signature algorithm
// 12.SM9_Verify //SM9 verification
// 13.SM9_SelfCheck() //SM9 slef-check
//
// Notes:
// This SM9 implementation source code can be used for academic, non-profit making or non-commercial use only.
// This SM9 implementation is created on MIRACL. SM9 implementation source code provider does not provide MIRACL library, MIRACL license or any permission to use MIRACL library. Any commercial use of MIRACL requires a license which may be obtained from Shamus Software Ltd.
//**************************************************************************/
#include "SM9_sv.h"
#include "kdf.h"
extern miracl *mip;
extern zzn2 X; //Frobniues constant
unsigned char SM9_q[32] = {0xB6, 0x40, 0x00, 0x00, 0x02, 0xA3, 0xA6, 0xF1, 0xD6, 0x03, 0xAB, 0x4F, 0xF5, 0x8E, 0xC7, 0x45,
0x21, 0xF2, 0x93, 0x4B, 0x1A, 0x7A, 0xEE, 0xDB, 0xE5, 0x6F, 0x9B, 0x27, 0xE3, 0x51, 0x45, 0x7D};
unsigned char SM9_N[32] = {0xB6, 0x40, 0x00, 0x00, 0x02, 0xA3, 0xA6, 0xF1, 0xD6, 0x03, 0xAB, 0x4F, 0xF5, 0x8E, 0xC7, 0x44,
0x49, 0xF2, 0x93, 0x4B, 0x18, 0xEA, 0x8B, 0xEE, 0xE5, 0x6E, 0xE1, 0x9C, 0xD6, 0x9E, 0xCF, 0x25};
unsigned char SM9_P1x[32] = {0x93, 0xDE, 0x05, 0x1D, 0x62, 0xBF, 0x71, 0x8F, 0xF5, 0xED, 0x07, 0x04, 0x48, 0x7D, 0x01, 0xD6,
0xE1, 0xE4, 0x08, 0x69, 0x09, 0xDC, 0x32, 0x80, 0xE8, 0xC4, 0xE4, 0x81, 0x7C, 0x66, 0xDD, 0xDD};
unsigned char SM9_P1y[32] = {0x21, 0xFE, 0x8D, 0xDA, 0x4F, 0x21, 0xE6, 0x07, 0x63, 0x10, 0x65, 0x12, 0x5C, 0x39, 0x5B, 0xBC,
0x1C, 0x1C, 0x00, 0xCB, 0xFA, 0x60, 0x24, 0x35, 0x0C, 0x46, 0x4C, 0xD7, 0x0A, 0x3E, 0xA6, 0x16};
unsigned char SM9_P2[128] = {0x85, 0xAE, 0xF3, 0xD0, 0x78, 0x64, 0x0C, 0x98, 0x59, 0x7B, 0x60, 0x27, 0xB4, 0x41, 0xA0, 0x1F,
0xF1, 0xDD, 0x2C, 0x19, 0x0F, 0x5E, 0x93, 0xC4, 0x54, 0x80, 0x6C, 0x11, 0xD8, 0x80, 0x61, 0x41,
0x37, 0x22, 0x75, 0x52, 0x92, 0x13, 0x0B, 0x08, 0xD2, 0xAA, 0xB9, 0x7F, 0xD3, 0x4E, 0xC1, 0x20,
0xEE, 0x26, 0x59, 0x48, 0xD1, 0x9C, 0x17, 0xAB, 0xF9, 0xB7, 0x21, 0x3B, 0xAF, 0x82, 0xD6, 0x5B,
0x17, 0x50, 0x9B, 0x09, 0x2E, 0x84, 0x5C, 0x12, 0x66, 0xBA, 0x0D, 0x26, 0x2C, 0xBE, 0xE6, 0xED,
0x07, 0x36, 0xA9, 0x6F, 0xA3, 0x47, 0xC8, 0xBD, 0x85, 0x6D, 0xC7, 0x6B, 0x84, 0xEB, 0xEB, 0x96,
0xA7, 0xCF, 0x28, 0xD5, 0x19, 0xBE, 0x3D, 0xA6, 0x5F, 0x31, 0x70, 0x15, 0x3D, 0x27, 0x8F, 0xF2,
0x47, 0xEF, 0xBA, 0x98, 0xA7, 0x1A, 0x08, 0x11, 0x62, 0x15, 0xBB, 0xA5, 0xC9, 0x99, 0xA7, 0xC7};
unsigned char SM9_t[32] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x58, 0xF9, 0x8A};
unsigned char SM9_a[32] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
unsigned char SM9_b[32] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05};
epoint *P1;
ecn2 P2;
big N; //order of group, N(t)
big para_a, para_b, para_t, para_q;
/****************************************************************
Function: bytes128_to_ecn2
Description: convert 128 bytes into ecn2
Calls: MIRACL functions
Called By: SM9_Init
Input: Ppubs[]
Output: ecn2 *res
Return: FALSE: execution error
TRUE: execute correctly
Others:
****************************************************************/
BOOL bytes128_to_ecn2(unsigned char Ppubs[], ecn2 *res)
{
zzn2 x, y;
big a, b;
ecn2 r;
r.x.a = mirvar(0);
r.x.b = mirvar(0);
r.y.a = mirvar(0);
r.y.b = mirvar(0);
r.z.a = mirvar(0);
r.z.b = mirvar(0);
r.marker = MR_EPOINT_INFINITY;
x.a = mirvar(0);
x.b = mirvar(0);
y.a = mirvar(0);
y.b = mirvar(0);
a = mirvar(0);
b = mirvar(0);
bytes_to_big(BNLEN, Ppubs, b);
bytes_to_big(BNLEN, Ppubs + BNLEN, a);
zzn2_from_bigs(a, b, &x);
bytes_to_big(BNLEN, Ppubs + BNLEN * 2, b);
bytes_to_big(BNLEN, Ppubs + BNLEN * 3, a);
zzn2_from_bigs(a, b, &y);
return ecn2_set(&x, &y, res);
}
/****************************************************************
Function: zzn12_ElementPrint
Description: print all element of struct zzn12
Calls: MIRACL functions
Called By: SM9_Sign,SM9_Verify
Input: zzn12 x
Output: NULL
Return: NULL
Others:
****************************************************************/
void zzn12_ElementPrint(zzn12 x)
{
big tmp;
tmp = mirvar(0);
redc(x.c.b.b, tmp);
cotnum(tmp, stdout);
redc(x.c.b.a, tmp);
cotnum(tmp, stdout);
redc(x.c.a.b, tmp);
cotnum(tmp, stdout);
redc(x.c.a.a, tmp);
cotnum(tmp, stdout);
redc(x.b.b.b, tmp);
cotnum(tmp, stdout);
redc(x.b.b.a, tmp);
cotnum(tmp, stdout);
redc(x.b.a.b, tmp);
cotnum(tmp, stdout);
redc(x.b.a.a, tmp);
cotnum(tmp, stdout);
redc(x.a.b.b, tmp);
cotnum(tmp, stdout);
redc(x.a.b.a, tmp);
cotnum(tmp, stdout);
redc(x.a.a.b, tmp);
cotnum(tmp, stdout);
redc(x.a.a.a, tmp);
cotnum(tmp, stdout);
}
/****************************************************************
Function: ecn2_Bytes128_Print
Description: print 128 bytes of ecn2
Calls: MIRACL functions
Called By: SM9_Sign,SM9_Verify
Input: ecn2 x
Output: NULL
Return: NULL
Others:
****************************************************************/
void ecn2_Bytes128_Print(ecn2 x)
{
big tmp;
tmp = mirvar(0);
redc(x.x.b, tmp);
cotnum(tmp, stdout);
redc(x.x.a, tmp);
cotnum(tmp, stdout);
redc(x.y.b, tmp);
cotnum(tmp, stdout);
redc(x.y.a, tmp);
cotnum(tmp, stdout);
}
/****************************************************************
Function: LinkCharZzn12
Description: link two different types(unsigned char and zzn12)to one(unsigned char)
Calls: MIRACL functions
Called By: SM9_Sign,SM9_Verify
Input: message:
len: length of message
w: zzn12 element
Output: Z: the characters array stored message and w
Zlen: length of Z
Return: NULL
Others:
****************************************************************/
void LinkCharZzn12(unsigned char *message, int len, zzn12 w, unsigned char *Z, int Zlen)
{
big tmp;
tmp = mirvar(0);
memcpy(Z, message, len);
redc(w.c.b.b, tmp);
big_to_bytes(BNLEN, tmp, Z + len, 1);
redc(w.c.b.a, tmp);
big_to_bytes(BNLEN, tmp, Z + len + BNLEN, 1);
redc(w.c.a.b, tmp);
big_to_bytes(BNLEN, tmp, Z + len + BNLEN * 2, 1);
redc(w.c.a.a, tmp);
big_to_bytes(BNLEN, tmp, Z + len + BNLEN * 3, 1);
redc(w.b.b.b, tmp);
big_to_bytes(BNLEN, tmp, Z + len + BNLEN * 4, 1);
redc(w.b.b.a, tmp);
big_to_bytes(BNLEN, tmp, Z + len + BNLEN * 5, 1);
redc(w.b.a.b, tmp);
big_to_bytes(BNLEN, tmp, Z + len + BNLEN * 6, 1);
redc(w.b.a.a, tmp);
big_to_bytes(BNLEN, tmp, Z + len + BNLEN * 7, 1);
redc(w.a.b.b, tmp);
big_to_bytes(BNLEN, tmp, Z + len + BNLEN * 8, 1);
redc(w.a.b.a, tmp);
big_to_bytes(BNLEN, tmp, Z + len + BNLEN * 9, 1);
redc(w.a.a.b, tmp);
big_to_bytes(BNLEN, tmp, Z + len + BNLEN * 10, 1);
redc(w.a.a.a, tmp);
big_to_bytes(BNLEN, tmp, Z + len + BNLEN * 11, 1);
}
/****************************************************************
Function: Test_Point
Description: test if the given point is on SM9 curve
Calls:
Called By: SM9_Verify
Input: point
Output: null
Return: 0: success
1: not a valid point on curve
Others:
****************************************************************/
int Test_Point(epoint *point)
{
big x, y, x_3, tmp;
epoint *buf;
x = mirvar(0);
y = mirvar(0);
x_3 = mirvar(0);
tmp = mirvar(0);
buf = epoint_init();
//test if y^2=x^3+b
epoint_get(point, x, y);
power(x, 3, para_q, x_3); //x_3=x^3 mod p
multiply(x, para_a, x);
divide(x, para_q, tmp);
add(x_3, x, x); //x=x^3+ax+b
add(x, para_b, x);
divide(x, para_q, tmp); //x=x^3+ax+b mod p
power(y, 2, para_q, y); //y=y^2 mod p
if (mr_compare(x, y) != 0)
return 1;
//test infinity
ecurve_mult(N, point, buf);
if (point_at_infinity(buf) == FALSE)
return 1;
return 0;
}
/****************************************************************
Function: Test_Range
Description: test if the big x belong to the range[1,n-1]
Calls:
Called By: SM9_Verify
Input: big x ///a miracl data type
Output: null
Return: 0: success
1: x==n,fail
Others:
****************************************************************/
int Test_Range(big x)
{
big one, decr_n;
one = mirvar(0);
decr_n = mirvar(0);
convert(1, one);
decr(N, 1, decr_n);
if ((mr_compare(x, one) < 0) | (mr_compare(x, decr_n) > 0))
return 1;
return 0;
}
/****************************************************************
Function: SM9_Init
Description: Initiate SM9 curve
Calls: MIRACL functions
Called By: SM9_SelfCheck
Input: null
Output: null
Return: 0: success;
7: base point P1 error
8: base point P2 error
Others:
****************************************************************/
int SM9_Init()
{
big P1_x, P1_y;
mip = mirsys(1000, 16);
;
mip->IOBASE = 16;
para_q = mirvar(0);
N = mirvar(0);
P1_x = mirvar(0);
P1_y = mirvar(0);
para_a = mirvar(0);
para_b = mirvar(0);
para_t = mirvar(0);
X.a = mirvar(0);
X.b = mirvar(0);
P2.x.a = mirvar(0);
P2.x.b = mirvar(0);
P2.y.a = mirvar(0);
P2.y.b = mirvar(0);
P2.z.a = mirvar(0);
P2.z.b = mirvar(0);
P2.marker = MR_EPOINT_INFINITY;
P1 = epoint_init();
bytes_to_big(BNLEN, SM9_q, para_q);
bytes_to_big(BNLEN, SM9_P1x, P1_x);
bytes_to_big(BNLEN, SM9_P1y, P1_y);
bytes_to_big(BNLEN, SM9_a, para_a);
bytes_to_big(BNLEN, SM9_b, para_b);
bytes_to_big(BNLEN, SM9_N, N);
bytes_to_big(BNLEN, SM9_t, para_t);
mip->TWIST = MR_SEXTIC_M;
ecurve_init(para_a, para_b, para_q, MR_PROJECTIVE); //Initialises GF(q) elliptic curve
//MR_PROJECTIVE specifying projective coordinates
if (!epoint_set(P1_x, P1_y, 0, P1))
return SM9_G1BASEPOINT_SET_ERR;
if (!(bytes128_to_ecn2(SM9_P2, &P2)))
return SM9_G2BASEPOINT_SET_ERR;
set_frobenius_constant(&X);
return 0;
}
/****************************************************************
Function: SM9_H1
Description: function H1 in SM9 standard 5.4.2.2
Calls: MIRACL functions,SM3_KDF
Called By: SM9_Verify
Input: Z:
Zlen:the length of Z
n:Frobniues constant X
Output: h1=H1(Z,Zlen)
Return: 0: success;
1: asking for memory error
Others:
****************************************************************/
int SM9_H1(unsigned char Z[], int Zlen, big n, big h1)
{
int hlen, i, ZHlen;
big hh, i256, tmp, n1;
unsigned char *ZH = NULL, *ha = NULL;
hh = mirvar(0);
i256 = mirvar(0);
tmp = mirvar(0);
n1 = mirvar(0);
convert(1, i256);
ZHlen = Zlen + 1;
hlen = (int)ceil((5.0 * logb2(n)) / 32.0);
decr(n, 1, n1);
ZH = (char *)malloc(sizeof(char) * (ZHlen + 1));
if (ZH == NULL)
return SM9_ASK_MEMORY_ERR;
memcpy(ZH + 1, Z, Zlen);
ZH[0] = 0x01;
ha = (char *)malloc(sizeof(char) * (hlen + 1));
if (ha == NULL)
return SM9_ASK_MEMORY_ERR;
SM3_KDF(ZH, ZHlen, hlen, ha);
for (i = hlen - 1; i >= 0; i--) //key[�Ӵ�С]
{
premult(i256, ha[i], tmp);
add(hh, tmp, hh);
premult(i256, 256, i256);
divide(i256, n1, tmp);
divide(hh, n1, tmp);
}
incr(hh, 1, h1);
free(ZH);
free(ha);
return 0;
}
/****************************************************************
Function: SM9_H2
Description: function H2 in SM9 standard 5.4.2.3
Calls: MIRACL functions,SM3_KDF
Called By: SM9_Sign,SM9_Verify
Input: Z:
Zlen:the length of Z
n:Frobniues constant X
Output: h2=H2(Z,Zlen)
Return: 0: success;
1: asking for memory error
Others:
****************************************************************/
int SM9_H2(unsigned char Z[], int Zlen, big n, big h2)
{
int hlen, ZHlen, i;
big hh, i256, tmp, n1;
unsigned char *ZH = NULL, *ha = NULL;
hh = mirvar(0);
i256 = mirvar(0);
tmp = mirvar(0);
n1 = mirvar(0);
convert(1, i256);
ZHlen = Zlen + 1;
hlen = (int)ceil((5.0 * logb2(n)) / 32.0);
decr(n, 1, n1);
ZH = (char *)malloc(sizeof(char) * (ZHlen + 1));
if (ZH == NULL)
return SM9_ASK_MEMORY_ERR;
memcpy(ZH + 1, Z, Zlen);
ZH[0] = 0x02;
ha = (char *)malloc(sizeof(char) * (hlen + 1));
if (ha == NULL)
return SM9_ASK_MEMORY_ERR;
SM3_KDF(ZH, ZHlen, hlen, ha);
for (i = hlen - 1; i >= 0; i--) //key[�Ӵ�С]
{
premult(i256, ha[i], tmp);
add(hh, tmp, hh);
premult(i256, 256, i256);
divide(i256, n1, tmp);
divide(hh, n1, tmp);
}
incr(hh, 1, h2);
free(ZH);
free(ha);
return 0;
}
/****************************************************************
Function: SM9_GenerateSignKey
Description: Generate Signed key
Calls: MIRACL functions,SM9_H1,xgcd,ecn2_Bytes128_Print
Called By: SM9_SelfCheck
Input: hid:0x01
ID:identification
IDlen:the length of ID
ks:master private key used to generate signature public key and private key
Output: Ppub:signature public key
dSA: signature private key
Return: 0: success;
1: asking for memory error
Others:
****************************************************************/
int SM9_GenerateSignKey(unsigned char hid[], unsigned char *ID, int IDlen, big ks, unsigned char Ppubs[], unsigned char dsa[])
{
big h1, t1, t2, rem, xdSA, ydSA, tmp;
unsigned char *Z = NULL;
int Zlen = IDlen + 1, buf;
ecn2 Ppub;
epoint *dSA;
h1 = mirvar(0);
t1 = mirvar(0);
t2 = mirvar(0);
rem = mirvar(0);
tmp = mirvar(0);
xdSA = mirvar(0);
ydSA = mirvar(0);
dSA = epoint_init();
Ppub.x.a = mirvar(0);
Ppub.x.b = mirvar(0);
Ppub.y.a = mirvar(0);
Ppub.y.b = mirvar(0);
Ppub.z.a = mirvar(0);
Ppub.z.b = mirvar(0);
Ppub.marker = MR_EPOINT_INFINITY;
Z = (char *)malloc(sizeof(char) * (Zlen + 1));
if (!(Z))
return 1;
memcpy(Z, ID, IDlen);
memcpy(Z + IDlen, hid, 1);
buf = SM9_H1(Z, Zlen, N, h1);
if (buf != 0)
return buf;
add(h1, ks, t1); //t1=H1(IDA||hid,N)+ks
xgcd(t1, N, t1, t1, t1); //t1=t1(-1)
multiply(ks, t1, t2);
divide(t2, N, rem); //t2=ks*t1(-1)
//dSA=[t2]P1
ecurve_mult(t2, P1, dSA);
//Ppub=[ks]P2
ecn2_copy(&P2, &Ppub);
ecn2_mul(ks, &Ppub);
printf("\n*********************The signed key = (xdA, ydA): *********************\n");
epoint_get(dSA, xdSA, ydSA);
cotnum(xdSA, stdout);
cotnum(ydSA, stdout);
printf("\n**********************PublicKey Ppubs=[ks]P2: *************************\n");
ecn2_Bytes128_Print(Ppub);
epoint_get(dSA, xdSA, ydSA);
big_to_bytes(BNLEN, xdSA, dsa, 1);
big_to_bytes(BNLEN, ydSA, dsa + BNLEN, 1);
redc(Ppub.x.b, tmp);
big_to_bytes(BNLEN, tmp, Ppubs, 1);
redc(Ppub.x.a, tmp);
big_to_bytes(BNLEN, tmp, Ppubs + BNLEN, 1);
redc(Ppub.y.b, tmp);
big_to_bytes(BNLEN, tmp, Ppubs + BNLEN * 2, 1);
redc(Ppub.y.a, tmp);
big_to_bytes(BNLEN, tmp, Ppubs + BNLEN * 3, 1);
free(Z);
return 0;
}
/****************************************************************
Function: SM9_Sign
Description: SM9 signature algorithm
Calls: MIRACL functions,zzn12_init(),ecap(),member(),zzn12_ElementPrint(),
zzn12_pow(),LinkCharZzn12(),SM9_H2()
Called By: SM9_SelfCheck()
Input:
hid:0x01
IDA //identification of userA
message //the message to be signed
len //the length of message
rand //a random number K lies in [1,N-1]
dSA //signature private key
Ppubs //signature public key
Output: H,S //signature result
Return: 0: success
1: asking for memory error
4: element is out of order q
5: R-ate calculation error
9: parameter L error
Others:
****************************************************************/
int SM9_Sign(unsigned char hid[], unsigned char *IDA, unsigned char *message, int len, unsigned char rand[],
unsigned char dsa[], unsigned char Ppub[], unsigned char H[], unsigned char S[])
{
big h1, r, h, l, xdSA, ydSA;
big xS, yS, tmp, zero;
zzn12 g, w;
epoint *s, *dSA;
ecn2 Ppubs;
int Zlen, buf;
unsigned char *Z = NULL;
//initiate
h1 = mirvar(0);
r = mirvar(0);
h = mirvar(0);
l = mirvar(0);
tmp = mirvar(0);
zero = mirvar(0);
xS = mirvar(0);
yS = mirvar(0);
xdSA = mirvar(0);
ydSA = mirvar(0);
s = epoint_init();
dSA = epoint_init();
Ppubs.x.a = mirvar(0);
Ppubs.x.b = mirvar(0);
Ppubs.y.a = mirvar(0);
Ppubs.y.b = mirvar(0);
Ppubs.z.a = mirvar(0);
Ppubs.z.b = mirvar(0);
Ppubs.marker = MR_EPOINT_INFINITY;
zzn12_init(&g);
zzn12_init(&w);
bytes_to_big(BNLEN, rand, r);
bytes_to_big(BNLEN, dsa, xdSA);
bytes_to_big(BNLEN, dsa + BNLEN, ydSA);
epoint_set(xdSA, ydSA, 0, dSA);
bytes128_to_ecn2(Ppub, &Ppubs);
//Step1:g = e(P1, Ppub-s)
if (!ecap(Ppubs, P1, para_t, X, &g))
return SM9_MY_ECAP_12A_ERR;
//test if a ZZn12 element is of order q
if (!member(g, para_t, X))
return SM9_MEMBER_ERR;
printf("\n***********************g=e(P1,Ppubs):****************************\n");
zzn12_ElementPrint(g);
//Step2:calculate w=g(r)
printf("\n***********************randnum r:********************************\n");
cotnum(r, stdout);
w = zzn12_pow(g, r);
printf("\n***************************w=gr:**********************************\n");
zzn12_ElementPrint(w);
//Step3:calculate h=H2(M||w,N)
Zlen = len + 32 * 12;
Z = (char *)malloc(sizeof(char) * (Zlen + 1));
if (Z == NULL)
return SM9_ASK_MEMORY_ERR;
LinkCharZzn12(message, len, w, Z, Zlen);
buf = SM9_H2(Z, Zlen, N, h);
if (buf != 0)
return buf;
printf("\n****************************h:*************************************\n");
cotnum(h, stdout);
//Step4:l=(r-h)mod N
subtract(r, h, l);
divide(l, N, tmp);
while (mr_compare(l, zero) < 0)
add(l, N, l);
if (mr_compare(l, zero) == 0)
return SM9_L_error;
printf("\n**************************l=(r-h)mod N:****************************\n");
cotnum(l, stdout);
//Step5:S=[l]dSA=(xS,yS)
ecurve_mult(l, dSA, s);
epoint_get(s, xS, yS);
printf("\n**************************S=[l]dSA=(xS,yS):*************************\n");
cotnum(xS, stdout);
cotnum(yS, stdout);
big_to_bytes(32, h, H, 1);
big_to_bytes(32, xS, S, 1);
big_to_bytes(32, yS, S + 32, 1);
free(Z);
return 0;
}
/****************************************************************
Function: SM9_Verify
Description: SM9 signature verification algorithm
Calls: MIRACL functions,zzn12_init(),Test_Range(),Test_Point(),
ecap(),member(),zzn12_ElementPrint(),SM9_H1(),SM9_H2()
Called By: SM9_SelfCheck()
Input:
H,S //signature result used to be verified
hid //identification
IDA //identification of userA
message //the message to be signed
len //the length of message
Ppubs //signature public key
Output: NULL
Return: 0: success
1: asking for memory error
2: H is not in the range[1,N-1]
6: S is not on the SM9 curve
4: element is out of order q
5: R-ate calculation error
3: h2!=h,comparison error
Others:
****************************************************************/
int SM9_Verify(unsigned char H[], unsigned char S[], unsigned char hid[], unsigned char *IDA, unsigned char *message, int len,
unsigned char Ppub[])
{
big h, xS, yS, h1, h2;
epoint *S1;
zzn12 g, t, u, w;
ecn2 P, Ppubs;
int Zlen1, Zlen2, buf;
unsigned char *Z1 = NULL, *Z2 = NULL;
h = mirvar(0);
h1 = mirvar(0);
h2 = mirvar(0);
xS = mirvar(0);
yS = mirvar(0);
P.x.a = mirvar(0);
P.x.b = mirvar(0);
P.y.a = mirvar(0);
P.y.b = mirvar(0);
P.z.a = mirvar(0);
P.z.b = mirvar(0);
P.marker = MR_EPOINT_INFINITY;
Ppubs.x.a = mirvar(0);
Ppubs.x.b = mirvar(0);
Ppubs.y.a = mirvar(0);
Ppubs.y.b = mirvar(0);
Ppubs.z.a = mirvar(0);
Ppubs.z.b = mirvar(0);
Ppubs.marker = MR_EPOINT_INFINITY;
S1 = epoint_init();
zzn12_init(&g), zzn12_init(&t);
zzn12_init(&u);
zzn12_init(&w);
bytes_to_big(BNLEN, H, h);
bytes_to_big(BNLEN, S, xS);
bytes_to_big(BNLEN, S + BNLEN, yS);
bytes128_to_ecn2(Ppub, &Ppubs);
//Step 1:test if h in the rangge [1,N-1]
if (Test_Range(h))
return SM9_H_OUTRANGE;
//Step 2:test if S is on G1
epoint_set(xS, yS, 0, S1);
if (Test_Point(S1))
return SM9_S_NOT_VALID_G1;
//Step3:g = e(P1, Ppub-s)
if (!ecap(Ppubs, P1, para_t, X, &g))
return SM9_MY_ECAP_12A_ERR;
//test if a ZZn12 element is of order q
if (!member(g, para_t, X))
return SM9_MEMBER_ERR;
printf("\n***********************g=e(P1,Ppubs): ****************************\n");
zzn12_ElementPrint(g);
//Step4:calculate t=g(h)
t = zzn12_pow(g, h);
printf("\n***************************w=gh: **********************************\n");
zzn12_ElementPrint(t);
//Step5:calculate h1=H1(IDA||hid,N)
Zlen1 = strlen(IDA) + 1;
Z1 = (char *)malloc(sizeof(char) * (Zlen1 + 1));
if (Z1 == NULL)
return SM9_ASK_MEMORY_ERR;
memcpy(Z1, IDA, strlen(IDA));
memcpy(Z1 + strlen(IDA), hid, 1);
buf = SM9_H1(Z1, Zlen1, N, h1);
if (buf != 0)
return buf;
printf("\n****************************h1: **********************************\n");
cotnum(h1, stdout);
//Step6:P=[h1]P2+Ppubs
ecn2_copy(&P2, &P);
ecn2_mul(h1, &P);
ecn2_add(&Ppubs, &P);
//Step7:u=e(S1,P)
if (!ecap(P, S1, para_t, X, &u))
return SM9_MY_ECAP_12A_ERR;
//test if a ZZn12 element is of order q
if (!member(u, para_t, X))
return SM9_MEMBER_ERR;
printf("\n************************** u=e(S1,P): *****************************\n");
zzn12_ElementPrint(u);
//Step8:w=u*t
zzn12_mul(u, t, &w);
printf("\n************************* w=u*t: **********************************\n");
zzn12_ElementPrint(w);
//Step9:h2=H2(M||w,N)
Zlen2 = len + 32 * 12;
Z2 = (char *)malloc(sizeof(char) * (Zlen2 + 1));
if (Z2 == NULL)
return SM9_ASK_MEMORY_ERR;
LinkCharZzn12(message, len, w, Z2, Zlen2);
buf = SM9_H2(Z2, Zlen2, N, h2);
if (buf != 0)
return buf;
printf("\n**************************** h2: ***********************************\n");
cotnum(h2, stdout);
free(Z1);
free(Z2);
if (mr_compare(h2, h) != 0)
return SM9_DATA_MEMCMP_ERR;
return 0;
}
/****************************************************************
Function: SM9_SelfCheck
Description: SM9 self check
Calls: MIRACL functions,SM9_Init(),SM9_GenerateSignKey(),
SM9_Sign,SM9_Verify
Called By:
Input:
Output:
Return: 0: self-check success
1: asking for memory error
2: H is not in the range[1,N-1]
3: h2!=h,comparison error
4: element is out of order q
5: R-ate calculation error
6: S is not on the SM9 curve
7: base point P1 error
8: base point P2 error
9: parameter L error
A: public key generated error
B: private key generated error
C: signature result error
Others:
****************************************************************/
int SM9_SelfCheck()
{
//the master private key
unsigned char dA[32] = {0x00, 0x01, 0x30, 0xE7, 0x84, 0x59, 0xD7, 0x85, 0x45, 0xCB, 0x54, 0xC5, 0x87, 0xE0, 0x2C, 0xF4,
0x80, 0xCE, 0x0B, 0x66, 0x34, 0x0F, 0x31, 0x9F, 0x34, 0x8A, 0x1D, 0x5B, 0x1F, 0x2D, 0xC5, 0xF4};
unsigned char rand[32] = {0x00, 0x03, 0x3C, 0x86, 0x16, 0xB0, 0x67, 0x04, 0x81, 0x32, 0x03, 0xDF, 0xD0, 0x09, 0x65, 0x02,
0x2E, 0xD1, 0x59, 0x75, 0xC6, 0x62, 0x33, 0x7A, 0xED, 0x64, 0x88, 0x35, 0xDC, 0x4B, 0x1C, 0xBE};
unsigned char h[32], S[64]; // Signature
unsigned char Ppub[128], dSA[64];
unsigned char std_h[32] = {0x82, 0x3C, 0x4B, 0x21, 0xE4, 0xBD, 0x2D, 0xFE, 0x1E, 0xD9, 0x2C, 0x60, 0x66, 0x53, 0xE9, 0x96,
0x66, 0x85, 0x63, 0x15, 0x2F, 0xC3, 0x3F, 0x55, 0xD7, 0xBF, 0xBB, 0x9B, 0xD9, 0x70, 0x5A, 0xDB};
unsigned char std_S[64] = {0x73, 0xBF, 0x96, 0x92, 0x3C, 0xE5, 0x8B, 0x6A, 0xD0, 0xE1, 0x3E, 0x96, 0x43, 0xA4, 0x06, 0xD8,
0xEB, 0x98, 0x41, 0x7C, 0x50, 0xEF, 0x1B, 0x29, 0xCE, 0xF9, 0xAD, 0xB4, 0x8B, 0x6D, 0x59, 0x8C,
0x85, 0x67, 0x12, 0xF1, 0xC2, 0xE0, 0x96, 0x8A, 0xB7, 0x76, 0x9F, 0x42, 0xA9, 0x95, 0x86, 0xAE,
0xD1, 0x39, 0xD5, 0xB8, 0xB3, 0xE1, 0x58, 0x91, 0x82, 0x7C, 0xC2, 0xAC, 0xED, 0x9B, 0xAA, 0x05};
unsigned char std_Ppub[128] = {0x9F, 0x64, 0x08, 0x0B, 0x30, 0x84, 0xF7, 0x33, 0xE4, 0x8A, 0xFF, 0x4B, 0x41, 0xB5, 0x65, 0x01,
0x1C, 0xE0, 0x71, 0x1C, 0x5E, 0x39, 0x2C, 0xFB, 0x0A, 0xB1, 0xB6, 0x79, 0x1B, 0x94, 0xC4, 0x08,
0x29, 0xDB, 0xA1, 0x16, 0x15, 0x2D, 0x1F, 0x78, 0x6C, 0xE8, 0x43, 0xED, 0x24, 0xA3, 0xB5, 0x73,
0x41, 0x4D, 0x21, 0x77, 0x38, 0x6A, 0x92, 0xDD, 0x8F, 0x14, 0xD6, 0x56, 0x96, 0xEA, 0x5E, 0x32,
0x69, 0x85, 0x09, 0x38, 0xAB, 0xEA, 0x01, 0x12, 0xB5, 0x73, 0x29, 0xF4, 0x47, 0xE3, 0xA0, 0xCB,
0xAD, 0x3E, 0x2F, 0xDB, 0x1A, 0x77, 0xF3, 0x35, 0xE8, 0x9E, 0x14, 0x08, 0xD0, 0xEF, 0x1C, 0x25,
0x41, 0xE0, 0x0A, 0x53, 0xDD, 0xA5, 0x32, 0xDA, 0x1A, 0x7C, 0xE0, 0x27, 0xB7, 0xA4, 0x6F, 0x74,
0x10, 0x06, 0xE8, 0x5F, 0x5C, 0xDF, 0xF0, 0x73, 0x0E, 0x75, 0xC0, 0x5F, 0xB4, 0xE3, 0x21, 0x6D};
unsigned char std_dSA[64] = {0xA5, 0x70, 0x2F, 0x05, 0xCF, 0x13, 0x15, 0x30, 0x5E, 0x2D, 0x6E, 0xB6, 0x4B, 0x0D, 0xEB, 0x92,
0x3D, 0xB1, 0xA0, 0xBC, 0xF0, 0xCA, 0xFF, 0x90, 0x52, 0x3A, 0xC8, 0x75, 0x4A, 0xA6, 0x98, 0x20,
0x78, 0x55, 0x9A, 0x84, 0x44, 0x11, 0xF9, 0x82, 0x5C, 0x10, 0x9F, 0x5E, 0xE3, 0xF5, 0x2D, 0x72,
0x0D, 0xD0, 0x17, 0x85, 0x39, 0x2A, 0x72, 0x7B, 0xB1, 0x55, 0x69, 0x52, 0xB2, 0xB0, 0x13, 0xD3};
unsigned char hid[] = {0x01};
unsigned char *IDA = "Alice";
unsigned char *message = "Chinese IBS standard"; //the message to be signed
int mlen = strlen(message), tmp; //the length of message
big ks;
tmp = SM9_Init();
if (tmp != 0)
return tmp;
ks = mirvar(0);
bytes_to_big(32, dA, ks);
printf("\n*********************** SM9 key Generation ***************************\n");
tmp = SM9_GenerateSignKey(hid, IDA, strlen(IDA), ks, Ppub, dSA);
if (tmp != 0)
return tmp;
if (memcmp(Ppub, std_Ppub, 128) != 0)
return SM9_GEPUB_ERR;
if (memcmp(dSA, std_dSA, 64) != 0)
return SM9_GEPRI_ERR;
printf("\n********************** SM9 signature algorithm***************************\n");
tmp = SM9_Sign(hid, IDA, message, mlen, rand, dSA, Ppub, h, S);
if (tmp != 0)
return tmp;
if (memcmp(h, std_h, 32) != 0)
return SM9_SIGN_ERR;
if (memcmp(S, std_S, 64) != 0)
return SM9_SIGN_ERR;
printf("\n******************* SM9 verification algorithm *************************\n");
tmp = SM9_Verify(h, S, hid, IDA, message, mlen, Ppub);
if (tmp != 0)
return tmp;
return 0;
}
test.c
#include
#include "SM9_sv.h"
int main(void)
{
int error_code;
if ( error_code = SM9_SelfCheck() )
{
printf("\nSM9 sign and verify self test falied!.\n");
printf("\nError code: 0x%x\n", error_code);
return error_code;
}
else
{
printf("\nSM9 sign and verify self test succeeded.\n");
return 0;
}
}
这里使用 Visual Studio Community 2019 创建一个空的控制台项目,将上面的源文件拷贝到该项目所在的目录下,并在“解决方案资源管理器”子窗口中,添加相应的头文件和源文件。编译程序需要用到开源库 MIRACL 相关的头文件和库文件。对于如何在 Windows下 编译 MIRACL,在本博客前面的系列文章(链接: https://blog.csdn.net/henter/article/details/103606333 )中有介绍。需要将 miracl.h 和 mirdef.h 头文件及其所在目录添加到项目属性的“附加包含目录”设置项中,在链接器相关的“附加依赖项”设置中,要设置 MIRACL 相关的库文件路径及文件名,例如 miracl_x86.lib ,然后按组合键 Ctrl + F5 ,就可以编译并运行 SM9 签名验签程序了,运行结果的部分截图如下: