國密複習

2022-11-14 12:00:10

國密複習

其中SM1和SM7是不公開的。

ZUC

ZUC(ZU Chong zhi)演演算法一個串流加密法(序列密碼),主要功能是產生金鑰流,可以用機密性和完整性驗證

初始金鑰 初始向量 輸出(每次) 輪數
128bit 128bit 32bit 32輪

金鑰流生成

金鑰流生成演演算法是核心,主要分為兩個階段:初始化階段和工作階段

演演算法從流程上分為上下三層:16級的線性反饋移位暫存器(LFSR)、位元重組(BR)和非線性函數(F)

  • LFSR

分為兩個模式:初始化模式和工作模式

(1)初始化模式

LSFR接收一個31bit的\(u\),其中\(u\)是非線性函數\(F\)的32bit的輸出\(W\)通過捨棄最低位得到的,即\(u=W>>1\)

\(LFSRWithInitialisation(u)\)

{

}

(2)工作模式

該模式下,LSFR沒有輸入:

$LFSRWithWorkMode() $

{

}

  • 位元重組BR

從LSFR中的16個暫存器中抽取128bit組成的4個32bit的\(X_0,X_1,X_2,X_3\),其中\(X_0,X_1,X_2\)用於下面的非線性函數\(F\)\(X_3\)用於金鑰流生成。

\(BitReconstruction()\)

{

}

其中,對於每個\(i\)\(s_i\)是31bit,所以\(s_{iH}\)是取\(s_i\)的第30bit到第15位元

  • F函數

\(F\)中包含兩個32bit的儲存單元\(R_1\)\(R_2\),輸入的是來自位元重組的3個32bit的\(X_0,X_1,X_2\),輸出的是一個32bit的\(W\),實際上\(F\)函數就是一個把96bit壓縮為32bit的一個非線性壓縮函數:

\(F(X_0,X_1,X_2)\)

{

}

其中\(S()\)是非線性S盒變換【混淆】,與AES和SM4一樣,都是取前4bit為行,後4bit為列,且這裡輸入的32bit,需要經過4個S盒,即\(S=(S_0,S_1,S_0,S_1)\);另外\(L()\)是迴圈左移【擴散】:

初始化階段

  • 金鑰裝載

輸入的128bit的初始金鑰\(k\)128bit的初始向量\(IV\)擴充套件為16個31bit長的整數,作為LFSR暫存器單元\(s_0,...,s_{15}\)的初始值

\(k\)\(IV\)表示為\(k=k_{0}|| k_{1}|| \cdots|| k_{15}\)\(\mathrm{IV}=\mathrm{iv}_{0}|| \mathrm{iv}_{1}|| \cdots \cdots|| \mathrm{iv}_{15}\),其中\(k_i\)\(iv_i\)均是8bit

對於常數\(D=d_{0}\left\|d_{1}\right\| \cdots \| d_{15}\)(240bit),即分為16個15bit的子串:

下面構造擴充套件出16個31bit的整數\(s_{i}=k_{i}\left\|d_{i}\right\| i v_{i}\)

  • 經過金鑰過載後,LFSR中的16個暫存器中都有初始值,作為LFSR的初始狀態,並將非線性函數\(F\)中的32bit儲存單元\(R_1\)\(R_2\)全部初始化為0。
  • 然後重複執行一下過程32次:
(1)BitReconstruction()  //位元重組
(2)W=F(X_0,X_1,X_2)	//F函數
(3)LFSRWithInitialisation(u) //LFSR中的初始化模式,更新16個暫存器的值

工作階段

初始化後,首先執行以下過程,並將\(F\)的輸出丟棄:

(1)BitReconstruction()  //位元重組
(2)F(X_0,X_1,X_2)	//F函數
(3)LFSRWithWorkMode() //LFSR中的工作模式,更新16個暫存器的值

然後進行金鑰輸出階段,以下過程每執行一次就輸出一個32bit的金鑰\(Z\)

(1)BitReconstruction()  //位元重組
(2)Z=F(X_0,X_1,X_2) ^ X_3	//F函數
(3)LFSRWithWorkMode() //LFSR中的工作模式,更新16個暫存器的值

程式

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

typedef unsigned char uint8;
typedef unsigned int uint32;

uint8 S0[256] = {
        0x3e, 0x72, 0x5b, 0x47, 0xca, 0xe0, 0x00, 0x33, 0x04, 0xd1, 0x54, 0x98, 0x09, 0xb9, 0x6d, 0xcb,
        0x7b, 0x1b, 0xf9, 0x32, 0xaf, 0x9d, 0x6a, 0xa5, 0xb8, 0x2d, 0xfc, 0x1d, 0x08, 0x53, 0x03, 0x90,
        0x4d, 0x4e, 0x84, 0x99, 0xe4, 0xce, 0xd9, 0x91, 0xdd, 0xb6, 0x85, 0x48, 0x8b, 0x29, 0x6e, 0xac,
        0xcd, 0xc1, 0xf8, 0x1e, 0x73, 0x43, 0x69, 0xc6, 0xb5, 0xbd, 0xfd, 0x39, 0x63, 0x20, 0xd4, 0x38,
        0x76, 0x7d, 0xb2, 0xa7, 0xcf, 0xed, 0x57, 0xc5, 0xf3, 0x2c, 0xbb, 0x14, 0x21, 0x06, 0x55, 0x9b,
        0xe3, 0xef, 0x5e, 0x31, 0x4f, 0x7f, 0x5a, 0xa4, 0x0d, 0x82, 0x51, 0x49, 0x5f, 0xba, 0x58, 0x1c,
        0x4a, 0x16, 0xd5, 0x17, 0xa8, 0x92, 0x24, 0x1f, 0x8c, 0xff, 0xd8, 0xae, 0x2e, 0x01, 0xd3, 0xad,
        0x3b, 0x4b, 0xda, 0x46, 0xeb, 0xc9, 0xde, 0x9a, 0x8f, 0x87, 0xd7, 0x3a, 0x80, 0x6f, 0x2f, 0xc8,
        0xb1, 0xb4, 0x37, 0xf7, 0x0a, 0x22, 0x13, 0x28, 0x7c, 0xcc, 0x3c, 0x89, 0xc7, 0xc3, 0x96, 0x56,
        0x07, 0xbf, 0x7e, 0xf0, 0x0b, 0x2b, 0x97, 0x52, 0x35, 0x41, 0x79, 0x61, 0xa6, 0x4c, 0x10, 0xfe,
        0xbc, 0x26, 0x95, 0x88, 0x8a, 0xb0, 0xa3, 0xfb, 0xc0, 0x18, 0x94, 0xf2, 0xe1, 0xe5, 0xe9, 0x5d,
        0xd0, 0xdc, 0x11, 0x66, 0x64, 0x5c, 0xec, 0x59, 0x42, 0x75, 0x12, 0xf5, 0x74, 0x9c, 0xaa, 0x23,
        0x0e, 0x86, 0xab, 0xbe, 0x2a, 0x02, 0xe7, 0x67, 0xe6, 0x44, 0xa2, 0x6c, 0xc2, 0x93, 0x9f, 0xf1,
        0xf6, 0xfa, 0x36, 0xd2, 0x50, 0x68, 0x9e, 0x62, 0x71, 0x15, 0x3d, 0xd6, 0x40, 0xc4, 0xe2, 0x0f,
        0x8e, 0x83, 0x77, 0x6b, 0x25, 0x05, 0x3f, 0x0c, 0x30, 0xea, 0x70, 0xb7, 0xa1, 0xe8, 0xa9, 0x65,
        0x8d, 0x27, 0x1a, 0xdb, 0x81, 0xb3, 0xa0, 0xf4, 0x45, 0x7a, 0x19, 0xdf, 0xee, 0x78, 0x34, 0x60 };

uint8 S1[256] = {
        0x55, 0xc2, 0x63, 0x71, 0x3b, 0xc8, 0x47, 0x86, 0x9f, 0x3c, 0xda, 0x5b, 0x29, 0xaa, 0xfd, 0x77,
        0x8c, 0xc5, 0x94, 0x0c, 0xa6, 0x1a, 0x13, 0x00, 0xe3, 0xa8, 0x16, 0x72, 0x40, 0xf9, 0xf8, 0x42,
        0x44, 0x26, 0x68, 0x96, 0x81, 0xd9, 0x45, 0x3e, 0x10, 0x76, 0xc6, 0xa7, 0x8b, 0x39, 0x43, 0xe1,
        0x3a, 0xb5, 0x56, 0x2a, 0xc0, 0x6d, 0xb3, 0x05, 0x22, 0x66, 0xbf, 0xdc, 0x0b, 0xfa, 0x62, 0x48,
        0xdd, 0x20, 0x11, 0x06, 0x36, 0xc9, 0xc1, 0xcf, 0xf6, 0x27, 0x52, 0xbb, 0x69, 0xf5, 0xd4, 0x87,
        0x7f, 0x84, 0x4c, 0xd2, 0x9c, 0x57, 0xa4, 0xbc, 0x4f, 0x9a, 0xdf, 0xfe, 0xd6, 0x8d, 0x7a, 0xeb,
        0x2b, 0x53, 0xd8, 0x5c, 0xa1, 0x14, 0x17, 0xfb, 0x23, 0xd5, 0x7d, 0x30, 0x67, 0x73, 0x08, 0x09,
        0xee, 0xb7, 0x70, 0x3f, 0x61, 0xb2, 0x19, 0x8e, 0x4e, 0xe5, 0x4b, 0x93, 0x8f, 0x5d, 0xdb, 0xa9,
        0xad, 0xf1, 0xae, 0x2e, 0xcb, 0x0d, 0xfc, 0xf4, 0x2d, 0x46, 0x6e, 0x1d, 0x97, 0xe8, 0xd1, 0xe9,
        0x4d, 0x37, 0xa5, 0x75, 0x5e, 0x83, 0x9e, 0xab, 0x82, 0x9d, 0xb9, 0x1c, 0xe0, 0xcd, 0x49, 0x89,
        0x01, 0xb6, 0xbd, 0x58, 0x24, 0xa2, 0x5f, 0x38, 0x78, 0x99, 0x15, 0x90, 0x50, 0xb8, 0x95, 0xe4,
        0xd0, 0x91, 0xc7, 0xce, 0xed, 0x0f, 0xb4, 0x6f, 0xa0, 0xcc, 0xf0, 0x02, 0x4a, 0x79, 0xc3, 0xde,
        0xa3, 0xef, 0xea, 0x51, 0xe6, 0x6b, 0x18, 0xec, 0x1b, 0x2c, 0x80, 0xf7, 0x74, 0xe7, 0xff, 0x21,
        0x5a, 0x6a, 0x54, 0x1e, 0x41, 0x31, 0x92, 0x35, 0xc4, 0x33, 0x07, 0x0a, 0xba, 0x7e, 0x0e, 0x34,
        0x88, 0xb1, 0x98, 0x7c, 0xf3, 0x3d, 0x60, 0x6c, 0x7b, 0xca, 0xd3, 0x1f, 0x32, 0x65, 0x04, 0x28,
        0x64, 0xbe, 0x85, 0x9b, 0x2f, 0x59, 0x8a, 0xd7, 0xb0, 0x25, 0xac, 0xaf, 0x12, 0x03, 0xe2, 0xf2 };

uint32 D[16] = {
        0x44d7, 0x26bc, 0x626b, 0x135e, 0x5789, 0x35e2, 0x7135, 0x09af,
        0x4d78, 0x2f13, 0x6bc4, 0x1af1, 0x5e26, 0x3c4d, 0x789a, 0x47ac };

uint32 LFSR[16] = { 0 };
uint32 X[4] = { 0 };
uint32 R1 = 0, R2 = 0;
uint32 W = 0;

uint32 mod_add(uint32 a, uint32 b);
uint32 mod_2exp_mul(uint32 x, int exp);
void LFSRWithInitMode(uint32 u);
void LFSRWithWorkMode();
void BitReconstruction();

uint32 mod_add(uint32 a, uint32 b)
{
    uint32 c = a + b;
    c = (c & 0x7fffffff) + (c >> 31);
    return c;
}

uint32 mod_2exp_mul(uint32 x, int exp)
{
    return ((x << exp) | (x >> (31 - exp))) & 0x7fffffff;
}

uint32 Rot(uint32 x, int move)
{
    return ((x << move) | (x >> (32 - move)));
}

//LFSR初始化模式
void LFSRWithInitMode(uint32 u)
{
    uint32 v = 0, tmp = 0, i = 0;

    v = LFSR[0];
    tmp = mod_2exp_mul(LFSR[0], 8);
    v = mod_add(v, tmp);

    tmp = mod_2exp_mul(LFSR[4], 20);
    v = mod_add(v, tmp);

    tmp = mod_2exp_mul(LFSR[10], 21);
    v = mod_add(v, tmp);

    tmp = mod_2exp_mul(LFSR[13], 17);
    v = mod_add(v, tmp);

    tmp = mod_2exp_mul(LFSR[15], 15);
    v = mod_add(v, tmp);

    v = mod_add(v, u);
    if (v == 0)
        v = 0x7fffffff;

    for (i = 0; i < 15; i++)
        LFSR[i] = LFSR[i + 1];
    LFSR[15] = v;
}

//LFSR工作模式
void LFSRWithWorkMode()
{
    uint32 v = 0, tmp = 0, i = 0;

    v = LFSR[0];
    tmp = mod_2exp_mul(LFSR[0], 8);
    v = mod_add(v, tmp);

    tmp = mod_2exp_mul(LFSR[4], 20);
    v = mod_add(v, tmp);

    tmp = mod_2exp_mul(LFSR[10], 21);
    v = mod_add(v, tmp);

    tmp = mod_2exp_mul(LFSR[13], 17);
    v = mod_add(v, tmp);

    tmp = mod_2exp_mul(LFSR[15], 15);
    v = mod_add(v, tmp);

    if (v == 0)
        v = 0x7fffffff;

    for (i = 0; i < 15; i++)
        LFSR[i] = LFSR[i + 1];
    LFSR[15] = v;
}

void BitReconstruction()
{
    X[0] = ((LFSR[15] & 0x7fff8000) << 1) | (LFSR[14] & 0xffff);
    X[1] = (LFSR[11] << 16) | (LFSR[9] >> 15);
    X[2] = (LFSR[7] << 16) | (LFSR[5] >> 15);
    X[3] = (LFSR[2] << 16) | (LFSR[0] >> 15);
}

uint32 L1(uint32 x)
{
    return (x ^ Rot(x, 2) ^ Rot(x, 10) ^ Rot(x, 18) ^ Rot(x, 24));
}

uint32 L2(uint32 x)
{
    return (x ^ Rot(x, 8) ^ Rot(x, 14) ^ Rot(x, 22) ^ Rot(x, 30));
}

uint32 S(uint32 a)
{
    uint8 x[4] = { 0 }, y[4] = { 0 };
    uint32 b = 0;
    int i = 0, row = 0, line = 0;
    x[0] = a >> 24;
    x[1] = (a >> 16) & 0xff;
    x[2] = (a >> 8) & 0xff;
    x[3] = a & 0xff;
    for (i = 0; i < 4; i++)
    {
        //row = x[i] >> 4;
        //line = x[i] & 0xf;
        if (i == 0 || i == 2)
            y[i] = S0[x[i]];
        else
            y[i] = S1[x[i]];
    }
    b = (y[0] << 24) | (y[1] << 16) | (y[2] << 8) | y[3];
    return b;
}

void F()
{
    uint32 W1 = 0, W2 = 0;
    uint32 tmp1 = 0, tmp2 = 0;
    W = (X[0] ^ R1) + R2;
    W1 = R1 + X[1];
    W2 = R2 ^ X[2];
    R1 = S(L1((W1 << 16) | (W2 >> 16)));
    R2 = S(L2((W2 << 16) | (W1 >> 16)));
}

//金鑰裝載
void Key_IV_Insert(uint8* k, uint8* iv)
{
    int i = 0;
    printf("\ninitial state of LFSR: S[0]-S[15]\n");
    for (i = 0; i < 16; i++)
    {
        LFSR[i] = (k[i] << 23) | (D[i] << 8) | iv[i]; //s_i=k_i || d_i || iv_i
        printf("%08x  ", LFSR[i]);
    }
}

void Init(uint8* k, uint8* iv)
{
    Key_IV_Insert(k, iv); //金鑰裝載,初始化LFSR
    R1 = R2 = 0; //初始化儲存單元R_1,R_2
    uint32 i = 0;
    //初始化
    for (i = 0; i < 32; i++)
    {
        BitReconstruction();
        F(X[0], X[1], X[2]);
        LFSRWithInitMode(W >> 1);
    }
    printf("\n------------------------------------------------------------\nstate of LFSR after executing initialization: S[0]-S[15]\n");
    for (i = 0; i < 16; i++)
    {
        printf("%08x  ", LFSR[i]);
    }
    printf("\n------------------------------------------------------------\ninternal state of Finite State Machine:\n");
    printf("R1=%08x\n", R1);
    printf("R2=%08x\n", R2);
}

//工作階段
uint32* KeyStream_Generator(int keylen)
{
    uint32 Z = 0, i = 0;
    uint32* keystream = (uint32*)malloc(keylen * sizeof(uint32));
    BitReconstruction();
    F(X[0], X[1], X[2]);  //將F的輸出W丟棄
    LFSRWithWorkMode();
    //金鑰輸出階段
    for (i = 0; i < keylen; i++)
    {
        BitReconstruction();
        F(X[0], X[1], X[2]);
        keystream[i] = W ^ X[3]; //一次金鑰流生成
        LFSRWithWorkMode();
    }
    return keystream;
}

int main()
{
    int i = 0, keylen = 0;
    uint8 key[16] = { 0 };
    uint8 iv[16] = { 0 };

    char k[50] = { 0 };
    char v[50] = { 0 };
    char tmp[2] = { 0 };
    printf("key: ");
    scanf("%s", k);
    printf("iv: ");
    scanf("%s", v);
    printf("keylen : ");
    scanf("%d", &keylen);
    for (i = 0; i < 16; i++)
    {
        key[i] = (((k[2 * i] <= '9') ? (k[2 * i] - '0') : (k[2 * i] - 'a' + 10)) << 4) +
                 ((k[2 * i + 1] <= '9') ? (k[2 * i + 1] - '0') : (k[2 * i + 1] - 'a' + 10));
        iv[i] = (((v[2 * i] <= '9') ? (v[2 * i] - '0') : (v[2 * i] - 'a' + 10)) << 4) +
                ((v[2 * i + 1] <= '9') ? (v[2 * i + 1] - '0') : (v[2 * i + 1] - 'a' + 10));
    }

    Init(key, iv);
    uint32* keylist = KeyStream_Generator(keylen);
    printf("\n輸出金鑰流:\n");
    for (i = 0; i < keylen; i++)
    {
        printf("KeyStream[%d]=%08x\n",i,keylist[i]);
    }
    printf("\n");
    return 0;
}

機密性

ZUC-128加密演演算法,主要用於新一代寬頻無線行動通訊系統的國際標準(4G標準)中的加解密。

  • 輸入

  • 輸出

演演算法

加密演演算法分為三步:初始化、金鑰流生成和加解密:

  • 初始化

根據金鑰\(CK\)和其他輸入引數構造初始金鑰\(k\)和初始向量\(IV\)

  • 金鑰流生成

  • 加解密

完整性

完整性驗證類似於Hash,對訊息生成一個唯一的MAC碼。

  • 輸入

  • 輸出

演演算法

演演算法核心就是為訊息生成一個唯一的認證碼,主要分為:初始化、金鑰流生成和生成訊息鑑別碼的過程:

  • 初始化

  • 金鑰流生成

  • 生成訊息鑑別碼

參考

1、https://www.jiamisoft.com/blog/28206-zuc-jmsf.html

2、https://www.jiamisoft.com/blog/21611-zuc.html

3、https://blog.csdn.net/anonymous_qsh/article/details/120794011

SM2

SM2是一個基於橢圓曲線上的密碼演演算法,其中包含公鑰加密演演算法、數位簽章演演算法和金鑰交換演演算法

SM2演演算法和ECC演演算法相比:

  • ECC採用的通常是國際機構推薦的曲線和引數;而SM2的引數是需要演演算法生成的,且演演算法中加入了使用者特異性的引數曲線,基點和使用者公鑰資訊等,使得SM2演演算法的安全性提升。
  • ECC中的雜湊演演算法使用的是MD5活著SHA-1,而SM2使用的是SM3,其安全性與SHA-256相當。

SM2演演算法分為基於素域和基於二元擴域的計算,下面介紹的是基於素域(有限域)計算的。

橢圓曲線公鑰密碼基於橢圓曲線的性質:

  • 有限域上的橢圓曲線在點加運算下(B=A+C)構成有限交換群,其階與有限域規模相近
  • 類似於有限域乘法群中的乘冪(\(a=y^b(mod p)\)),橢圓曲線上多倍點運算(B=[k]A)構成一個單項函數

在多倍點運算中,已知多倍點\((B)\)和基點\((A)\),求解倍數\((k)\)問題稱為橢圓曲線上的離散對數問題,對於一般橢圓曲線的離散對數問題,目前只存在指數級計算複雜度的求解方法,與大數分解問題及有限域上的離散對數問題相比,橢圓曲線上的離散對數問題的求解難度大的多,因此,在相同安全程度下,橢圓曲線密碼較其他公鑰密碼所需的金鑰規模要小得多

SM2演演算法用到了三個輔助演演算法:雜湊演演算法(SM3)、金鑰派生函數(KDF)和亂數發生器,這三類輔助演演算法的強弱直接影響到演演算法的安全性。

  • 雜湊演演算法

使用的SM3,生成256bit的雜湊值。

  • 金鑰派生函數

就是從一個共用的祕密位元串中派生出金鑰,金鑰派生函數需要使用雜湊函數:

  • 亂數發生器

使用國家釋出的亂數發生器。

公鑰加密

假設存在兩方:A(加密)和B(解密)方。

引數

素域\(F_p\)下SM2演演算法的引數:

其中位元串\(SEED\)和引數\((a,b)\)的生成如下:

金鑰生成

B方生成使用者金鑰對:私鑰\(d_B\)和公鑰\(P_B\)

  • \((1,...,n-1)\)中任取一個亂數\(d_B\)作為私鑰
  • 計算公鑰:\(P_B=d_BG\),其中\(G=(x_G,y_G)\)是基點。

加密

A方要加密的是長度為\(klen\)長的位元串\(M\),這裡使用的是公鑰\(P_B\)加密的:

其中訊息\(M\)不是編碼到橢圓曲線上,這裡是直接對字串加密,產生的密文也是字串,加密流程圖如下:

解密

B方收到密文\(C\)後,利用私鑰\(d_B\)解密:

加密和解密都需要使用KDF生成金鑰\(t\),具體解密流程圖如下:

分析

  • 安全性

根據生成的使用者公鑰\(P_B=d_BG\)得不到私鑰\(d_B\),滿足橢圓曲線上的離散對數問題

  • 正確性

因為\((x_2,y_2)=d_BC_1\)產生\(t\)\(u\),所以只需證明\((x_2,y_2)=d_BC_1\)

由於\(P_B=d_BG,C_1=kG=(x_1,y_1)\),所以\(d_BC_1=d_BkG=k(d_BG)=kP_B=(x_2,y_2)\)

程式實現

/****************************************************************
  Function:       SM2_ENC_SelfTest
  Description:    test whether the SM2 calculation is correct by comparing the result with the
standard data
  Calls:          SM2_init,SM2_ENC,SM2_DEC
  Called By:
  Input:          NULL
  Output:         NULL
  Return:         0: sucess
                  1: S is a point at finity
                  2: X or Y coordinate is beyond Fq
                  3: not a valid point on curve
                  4: the given point G is not a point of order n
                  5: KDF output is all zero
                  6: C3 does not match
                  8: public key generation error
                  9: SM2 encryption error
                  a: SM2 decryption error

  Others:
****************************************************************/
int SM2_ENC_SelfTest()
{
    int tmp = 0, i = 0;
    unsigned char Cipher[115] = { 0 };
    unsigned char M[19] = { 0 };
    unsigned char kGxy[SM2_NUMWORD * 2] = { 0 };
    big ks, x, y;
    epoint* kG;

    //standard data 
    unsigned char
            std_priKey[32] = { 0x39,0x45,0x20,0x8F,0x7B,0x21,0x44,0xB1,0x3F,0x36,0xE3,0x8A,0xC6,0xD3,0x9F,0x95,
                               0x88,0x93,0x93,0x69,0x28,0x60,0xB5,0x1A,0x42,0xFB,0x81,0xEF,0x4D,0xF7,0xC5,0xB8 };
    unsigned char
            std_pubKey[64] = { 0x09,0xF9,0xDF,0x31,0x1E,0x54,0x21,0xA1,0x50,0xDD,0x7D,0x16,0x1E,0x4B,0xC5,0xC6,
                               0x72,0x17,0x9F,0xAD,0x18,0x33,0xFC,0x07,0x6B,0xB0,0x8F,0xF3,0x56,0xF3,0x50,0x20,
                               0xCC,0xEA,0x49,0x0C,0xE2,0x67,0x75,0xA5,0x2D,0xC6,0xEA,0x71,0x8C,0xC1,0xAA,0x60,
                               0x0A,0xED,0x05,0xFB,0xF3,0x5E,0x08,0x4A,0x66,0x32,0xF6,0x07,0x2D,0xA9,0xAD,0x13 };
    unsigned char
            std_rand[32] = { 0x59,0x27,0x6E,0x27,0xD5,0x06,0x86,0x1A,0x16,0x68,0x0F,0x3A,0xD9,0xC0,0x2D,0xCC,
                             0xEF,0x3C,0xC1,0xFA,0x3C,0xDB,0xE4,0xCE,0x6D,0x54,0xB8,0x0D,0xEA,0xC1,0xBC,0x21 };
    unsigned char
            std_Message[19] = { 0x65,0x6E,0x63,0x72,0x79,0x70,0x74,0x69,0x6F,0x6E,0x20,0x73,0x74,0x61,0x6E,
                                0x64,0x61,0x72,0x64 };
    unsigned char
            std_Cipher[115] = { 0x04,0xEB,0xFC,0x71,0x8E,0x8D,0x17,0x98,0x62,0x04,0x32,0x26,0x8E,0x77,0xFE,
                                0xB6,0x41,0x5E,0x2E,0xDE,0x0E,0x07,0x3C,0x0F,0x4F,0x64,0x0E,0xCD,0x2E,0x14,0x9A,0x73,
                                0xE8,0x58,0xF9,0xD8,0x1E,0x54,0x30,0xA5,0x7B,0x36,0xDA,0xAB,0x8F,0x95,0x0A,0x3C,
                                0x64,0xE6,0xEE,0x6A,0x63,0x09,0x4D,0x99,0x28,0x3A,0xFF,0x76,0x7E,0x12,0x4D,0xF0,
                                0x59,0x98,0x3C,0x18,0xF8,0x09,0xE2,0x62,0x92,0x3C,0x53,0xAE,0xC2,0x95,0xD3,0x03,
                                0x83,0xB5,0x4E,0x39,0xD6,0x09,0xD1,0x60,0xAF,0xCB,0x19,0x08,0xD0,0xBD,0x87,0x66,
                                0x21,0x88,0x6C,0xA9,0x89,0xCA,0x9C,0x7D,0x58,0x08,0x73,0x07,0xCA,0x93,0x09,0x2D,0x65,0x1E,0xFA };

    mip = mirsys(1000, 16);
    mip->IOBASE = 16;
    x = mirvar(0);
    y = mirvar(0);
    ks = mirvar(0);
    kG = epoint_init();
    bytes_to_big(32, std_priKey, ks);  //ks is the standard private key 

    printf(" 待加密的訊息M:");
    for (i = 0; i < 19; i++)
    {
        printf("%c", std_Message[i]);
    }
    printf("\n 待加密的訊息[十六進位制]M:");
    for (i = 0; i < 19; i++)
    {
        printf("%X", std_Message[i]);
    }
    //initiate SM2 curve 
    SM2_Init();

    printf("\n 私鑰Db:");
    for (i = 0; i < 32; i++)
    {
        printf("%X", std_priKey[i]);
    }
    printf("\n 公鑰Pb=(x,y):");
    printf("\n 座標X:");
    for (i = 0; i < 32; i++)
    {
        printf("%X", std_pubKey[i]);
    }
    printf("\n 座標Y:");
    for (i = 32; i < 64; i++)
    {
        printf("%X", std_pubKey[i]);
    }
    printf("\n\n ************************開始加密************************");
    //generate key pair 
    tmp = SM2_KeyGeneration(ks, kG);
    if (tmp != 0)
        return tmp;
    epoint_get(kG, x, y);
    big_to_bytes(SM2_NUMWORD, x, kGxy, 1);
    big_to_bytes(SM2_NUMWORD, y, kGxy + SM2_NUMWORD, 1);
    if (memcmp(kGxy, std_pubKey, SM2_NUMWORD * 2) != 0)
        return ERR_SELFTEST_KG;

    //encrypt data and compare the result with the standard data 
    tmp = SM2_Encrypt(std_rand, kG, std_Message, 19, Cipher);
    if (tmp != 0)
        return tmp;
    if (memcmp(Cipher, std_Cipher, 19 + SM2_NUMWORD * 3) != 0)
        return ERR_SELFTEST_ENC;

    printf("\n\n ************************開始解密************************");
    //decrypt cipher and compare the result with the standard data 
    tmp = SM2_Decrypt(ks, Cipher, 115, M);
    if (tmp != 0)
        return tmp;
    printf("\n\n 明文M':");
    for (i = 0; i < 19; i++)
    {
        printf("%X", M[i]);
    }
    printf("\n 即為:");
    for (i = 0; i < 19; i++)
    {
        printf("%c", M[i]);
    }
    if (memcmp(M, std_Message, 19) != 0)
        return ERR_SELFTEST_DEC;

    return 0;
}

數位簽章

引數

基本引數設定與加解密相同,假設簽名方為A,驗籤方為B:

金鑰生成

簽名

驗籤

分析

  • 正確性

程式實現

/****************************************************************
  Function:       SM2_SelfCheck
  Description:    SM2 self check
  Calls:          SM2_Init(), SM2_KeyGeneration,SM2_Sign, SM2_Verify,SM3_256()
  Called By:
  Input:
  Output:
  Return:         0: sucess
                  1: paremeter initialization error
                  2: a point at infinity
                  5: X or Y coordinate is beyond Fq
                  3: not a valid point on curve
                  4: not a point of order n
                  B: public key error
                  8: the signed R out of range [1,n-1]
                  9: the signed S out of range [1,n-1]
                  A: the intermediate data t equals 0
                  C: verification fail
  Others:
****************************************************************/
int SM2_SelfCheck()
{
    //the private key
    unsigned char
            dA[32] = { 0x39,0x45,0x20,0x8f,0x7b,0x21,0x44,0xb1,0x3f,0x36,0xe3,0x8a,0xc6,0xd3,0x9f,
                       0x95,0x88,0x93,0x93,0x69,0x28,0x60,0xb5,0x1a,0x42,0xfb,0x81,0xef,0x4d,0xf7,0xc5,0xb8 };

    unsigned char
            rand[32] = { 0x59,0x27,0x6E,0x27,0xD5,0x06,0x86,0x1A,0x16,0x68,0x0F,0x3A,0xD9,0xC0,0x2D,
                         0xCC,0xEF,0x3C,0xC1,0xFA,0x3C,0xDB,0xE4,0xCE,0x6D,0x54,0xB8,0x0D,0xEA,0xC1,0xBC,0x21 };

    //the public key
    /* unsigned char
         xA[32]={0x09,0xf9,0xdf,0x31,0x1e,0x54,0x21,0xa1,0x50,0xdd,0x7d,0x16,0x1e,0x4b,0xc5,
         0xc6,0x72,0x17,0x9f,0xad,0x18,0x33,0xfc,0x07,0x6b,0xb0,0x8f,0xf3,0x56,0xf3,0x50,0x20};
             unsigned char
         yA[32]={0xcc,0xea,0x49,0x0c,0xe2,0x67,0x75,0xa5,0x2d,0xc6,0xea,0x71,0x8c,0xc1,0xaa,
         0x60,0x0a,0xed,0x05,0xfb,0xf3,0x5e,0x08,0x4a,0x66,0x32,0xf6,0x07,0x2d,0xa9,0xad,0x13};*/

    unsigned char xA[32], yA[32];
    unsigned char r[32], s[32];// Signature

    unsigned char IDA[16] = { 0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x31,0x32,0x33,
                              0x34,0x35,0x36,0x37,0x38 };//ASCII code of userA's identification
    int IDA_len = 16;
    unsigned char ENTLA[2] = { 0x00,0x80 };
    //the length of userA's identification,presentation in  ASCII code

    unsigned char* message = "message digest";//the message to be signed
    int len = strlen(message);//the length of message

    unsigned char ZA[SM3_len / 8];//ZA=Hash(ENTLA|| IDA|| a|| b|| Gx || Gy || xA|| yA)
    unsigned char Msg[210]; //210=IDA_len+2+SM2_NUMWORD*6

    int temp;

    miracl* mip = mirsys(10000, 16);
    mip->IOBASE = 16;

    temp = SM2_KeyGeneration(dA, xA, yA); // 金鑰生成
    if (temp)
        return temp;

    printf(" 待簽名的訊息M:%s\n", message);

    //生成簽名前,使用HASH函數針對訊息進行壓縮生成 ZA=HASH256(ENTLA|| IDA|| a|| b|| Gx || Gy || xA|| yA)
    // ENTLA|| IDA|| a|| b|| Gx || Gy || xA|| yA
    memcpy(Msg, ENTLA, 2);
    memcpy(Msg + 2, IDA, IDA_len);
    memcpy(Msg + 2 + IDA_len, SM2_a, SM2_NUMWORD);
    memcpy(Msg + 2 + IDA_len + SM2_NUMWORD, SM2_b, SM2_NUMWORD);
    memcpy(Msg + 2 + IDA_len + SM2_NUMWORD * 2, SM2_Gx, SM2_NUMWORD);
    memcpy(Msg + 2 + IDA_len + SM2_NUMWORD * 3, SM2_Gy, SM2_NUMWORD);
    memcpy(Msg + 2 + IDA_len + SM2_NUMWORD * 4, xA, SM2_NUMWORD);
    memcpy(Msg + 2 + IDA_len + SM2_NUMWORD * 5, yA, SM2_NUMWORD);
    SM3_256(Msg, 210, ZA);
    printf(" 雜湊值ZA:");
    for (int i = 0; i < SM3_len / 8; i++)
    {
        printf("%X", ZA[i]);
    }

    printf("\n\n******************************開始生成簽名******************************");
    //生成簽名
    temp = SM2_Sign(message, len, ZA, rand, dA, r, s);
    if (temp)
        return temp;

    printf("\n******************************開始驗證簽名******************************");
    //驗證簽名
    temp = SM2_Verify(message, len, ZA, xA, yA, r, s);
    if (temp)
        return temp;

    return 0;
}

金鑰交換

金鑰交換又叫做金鑰協商,就是兩個使用者A和B通過互動的訊息傳遞,用各自的私鑰和對方的公鑰來商定一個只有他們知道的祕密金鑰,這個共用的祕密金鑰通常用於對稱加密中,金鑰交換協定能用於金鑰管理和協商

引數

  • 金鑰

  • 標識資訊

協定

使用者A為發起方,使用者B為響應方,協商獲得的祕密金鑰長度為\(klen\)bit,

  • A

  • A

  • B

分析

  • 這裡使用的密碼雜湊演演算法(KDF)與加密和簽名演演算法所用相同。
  • 通訊次數:3次

流程圖

程式實現

/****************************************************************
  Function:       SM2_KeyEX_SelfTest
  Description:    self check of SM2 key exchange
  Calls:          SM2_Init, SM3_Z, SM2_KeyEx_Init_I, SM2_KeyEx_Re_I, SM2_KeyEx_Init_II,
SM2_KeyEx_Re_II
  Called By:
  Input:
  Output:
  Return:         0: sucess
                  1:  a point at infinity
                  2: X or Y coordinate is beyond Fq
                  3: not a valid point on curve
                  4: not a point of order n
                  6: RA is not valid
                  7:  RB is not valid
                  8:  key validation failed,form B to A,S1!=SB
                  A: the hash value Z error,Z=hash(ELAN||ID||a ||b||Gx||Gy||Px||Py)
                  B: initialization I failed
                  C; the shared key KA error,self check failed
                  D; the shared key KB error,self check failed
                  9: key validation failed,form A to B,S2!=SA
  Others:
****************************************************************/
int SM2_KeyEX_SelfTest()
{
    //standard data
    unsigned char
            std_priKeyA[SM2_NUMWORD] = { 0x81,0xEB,0x26,0xE9,0x41,0xBB,0x5A,0xF1,0x6D,0xF1,0x16,0x49,0x5F,0x90,
                                         0x69,0x52,0x72,0xAE,0x2C,0xD6,0x3D,0x6C,0x4A,0xE1,0x67,0x84,0x18,0xBE,0x48,0x23,0x00,0x29 };
    unsigned char
            std_pubKeyA[SM2_NUMWORD * 2] = { 0x16,0x0E,0x12,0x89,0x7D,0xF4,0xED,0xB6,0x1D,0xD8,0x12,0xFE,0xB9
            ,0x67,0x48,0xFB,
                                             0xD3,0xCC,0xF4,0xFF,0xE2,0x6A,0xA6,0xF6,0xDB,0x95,0x40,0xAF,0x49,0xC9,0x42,0x32,
                                             0x4A,0x7D,0xAD,0x08,0xBB,0x9A,0x45,0x95,0x31,0x69,0x4B,0xEB,0x20,0xAA,0x48,0x9D,
                                             0x66,0x49,0x97,0x5E,0x1B,0xFC,0xF8,0xC4,0x74,0x1B,0x78,0xB4,0xB2,0x23,0x00,0x7F };
    unsigned char std_randA[SM2_NUMWORD] =
            { 0xD4,0xDE,0x15,0x47,0x4D,0xB7,0x4D,0x06,0x49,0x1C,0x44,0x0D,0x30,0x5E,0x01,0x24,
              0x00,0x99,0x0F,0x3E,0x39,0x0C,0x7E,0x87,0x15,0x3C,0x12,0xDB,0x2E,0xA6,0x0B,0xB3 };
    unsigned char
            std_priKeyB[SM2_NUMWORD] = { 0x78,0x51,0x29,0x91,0x7D,0x45,0xA9,0xEA,0x54,0x37,0xA5,0x93,0x56,0xB8,0x23,0x38,
                                         0xEA,0xAD,0xDA,0x6C,0xEB,0x19,0x90,0x88,0xF1,0x4A,0xE1,0x0D,0xEF,0xA2,0x29,0xB5 };
    unsigned char
            std_pubKeyB[SM2_NUMWORD * 2] = { 0x6A,0xE8,0x48,0xC5,0x7C,0x53,0xC7,0xB1,0xB5,0xFA,0x99,0xEB,0x22
            ,0x86,0xAF,0x07,
                                             0x8B,0xA6,0x4C,0x64,0x59,0x1B,0x8B,0x56,0x6F,0x73,0x57,0xD5,0x76,0xF1,0x6D,0xFB,
                                             0xEE,0x48,0x9D,0x77,0x16,0x21,0xA2,0x7B,0x36,0xC5,0xC7,0x99,0x20,0x62,0xE9,0xCD,
                                             0x09,0xA9,0x26,0x43,0x86,0xF3,0xFB,0xEA,0x54,0xDF,0xF6,0x93,0x05,0x62,0x1C,0x4D };
    unsigned char std_randB[SM2_NUMWORD] =
            { 0x7E,0x07,0x12,0x48,0x14,0xB3,0x09,0x48,0x91,0x25,0xEA,0xED,0x10,0x11,0x13,0x16,
              0x4E,0xBF,0x0F,0x34,0x58,0xC5,0xBD,0x88,0x33,0x5C,0x1F,0x9D,0x59,0x62,0x43,0xD6 };
    unsigned char
            std_IDA[16] = { 0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38
    };
    unsigned char
            std_IDB[16] = { 0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38
    };
    unsigned short int std_ENTLA = 0x0080;
    unsigned short int std_ENTLB = 0x0080;
    unsigned char
            std_ZA[SM3_len] = { 0x3B,0x85,0xA5,0x71,0x79,0xE1,0x1E,0x7E,0x51,0x3A,0xA6,0x22,0x99,0x1F,0x2C,
                                0xA7,0x4D,0x18,0x07,0xA0,0xBD,0x4D,0x4B,0x38,0xF9,0x09,0x87,0xA1,0x7A,0xC2,0x45,0xB1 };
    unsigned char
            std_ZB[SM3_len] = { 0x79,0xC9,0x88,0xD6,0x32,0x29,0xD9,0x7E,0xF1,0x9F,0xE0,0x2C,0xA1,0x05,0x6E,
                                0x01,0xE6,0xA7,0x41,0x1E,0xD2,0x46,0x94,0xAA,0x8F,0x83,0x4F,0x4A,0x4A,0xB0,0x22,0xF7 };
    unsigned char
            std_RA[SM2_NUMWORD * 2] = { 0x64,0xCE,0xD1,0xBD,0xBC,0x99,0xD5,0x90,0x04,0x9B,0x43,0x4D,0x0F,0xD7
            ,0x34,0x28,0xCF,0x60,0x8A,0x5D,0xB8,0xFE,0x5C,0xE0,0x7F,0x15,0x02,0x69,0x40,0xBA,0xE4,0x0E,

                                        0x37,0x66,0x29,0xC7,0xAB,0x21,0xE7,0xDB,0x26,0x09,0x22,0x49,0x9D,0xDB,0x11,0x8F,0x07,0xCE,0x8E,0xAA,0xE3,0xE7,0x72,0x0A,0xFE,0xF6,0xA5,0xCC,0x06,0x20,0x70,0xC0 };
    unsigned char
            std_K[16] = { 0x6C,0x89,0x34,0x73,0x54,0xDE,0x24,0x84,0xC6,0x0B,0x4A,0xB1,0xFD,0xE4,0xC6,0xE5 };
    unsigned char std_RB[SM2_NUMWORD * 2] =
            { 0xAC,0xC2,0x76,0x88,0xA6,0xF7,0xB7,0x06,0x09,0x8B,0xC9,0x1F,0xF3,0xAD,0x1B,0xFF,
              0x7D,0xC2,0x80,0x2C,0xDB,0x14,0xCC,0xCC,0xDB,0x0A,0x90,0x47,0x1F,0x9B,0xD7,0x07,
              0x2F,0xED,0xAC,0x04,0x94,0xB2,0xFF,0xC4,0xD6,0x85,0x38,0x76,0xC7,0x9B,0x8F,0x30,
              0x1C,0x65,0x73,0xAD,0x0A,0xA5,0x0F,0x39,0xFC,0x87,0x18,0x1E,0x1A,0x1B,0x46,0xFE };
    unsigned char
            std_SB[SM3_len] = { 0xD3,0xA0,0xFE,0x15,0xDE,0xE1,0x85,0xCE,0xAE,0x90,0x7A,0x6B,0x59,0x5C,0xC3,
                                0x2A,0x26,0x6E,0xD7,0xB3,0x36,0x7E,0x99,0x83,0xA8,0x96,0xDC,0x32,0xFA,0x20,0xF8,0xEB };
    int std_Klen = 128;//bit len
    int temp;

    big x, y, dA, dB, rA, rB;
    epoint* pubKeyA, * pubKeyB, * RA, * RB, * V;

    unsigned char hash[SM3_len / 8] = { 0 };
    unsigned char ZA[SM3_len / 8] = { 0 };
    unsigned char ZB[SM3_len / 8] = { 0 };
    unsigned char xy[SM2_NUMWORD * 2] = { 0 };
    unsigned char* KA, * KB;
    unsigned char SA[SM3_len / 8];


    KA = malloc(std_Klen / 8);
    KB = malloc(std_Klen / 8);

    mip = mirsys(1000, 16);
    mip->IOBASE = 16;

    x = mirvar(0);
    y = mirvar(0);
    dA = mirvar(0);
    dB = mirvar(0);
    rA = mirvar(0);
    rB = mirvar(0);
    pubKeyA = epoint_init();
    pubKeyB = epoint_init();
    RA = epoint_init();
    RB = epoint_init();
    V = epoint_init();
    SM2_Init();

    bytes_to_big(SM2_NUMWORD, std_priKeyA, dA);
    bytes_to_big(SM2_NUMWORD, std_priKeyB, dB);
    bytes_to_big(SM2_NUMWORD, std_randA, rA);
    bytes_to_big(SM2_NUMWORD, std_randB, rB);
    bytes_to_big(SM2_NUMWORD, std_pubKeyA, x);
    bytes_to_big(SM2_NUMWORD, std_pubKeyA + SM2_NUMWORD, y);
    epoint_set(x, y, 0, pubKeyA);
    bytes_to_big(SM2_NUMWORD, std_pubKeyB, x);
    bytes_to_big(SM2_NUMWORD, std_pubKeyB + SM2_NUMWORD, y);
    epoint_set(x, y, 0, pubKeyB);

    printf("\n 使用者A的私鑰Da:");
    cotnum(dA, stdout);
    printf(" 使用者A的公鑰Pa=(x,y):");
    printf("\n 座標x:");
    cotnum(pubKeyA->X, stdout);
    printf(" 座標y:");
    cotnum(pubKeyA->Y, stdout);

    printf(" 使用者B的私鑰Db:");
    cotnum(dB, stdout);
    printf(" 使用者B的公鑰Pb=(x,y):");
    printf("\n 座標x:");
    cotnum(pubKeyB->X, stdout);
    printf(" 座標y:");
    cotnum(pubKeyB->Y, stdout);

    SM3_Z(std_IDA, std_ENTLA, pubKeyA, ZA);
    if (memcmp(ZA, std_ZA, SM3_len / 8) != 0)
        return ERR_SELFTEST_Z;
    SM3_Z(std_IDB, std_ENTLB, pubKeyB, ZB);
    if (memcmp(ZB, std_ZB, SM3_len / 8) != 0)
        return ERR_SELFTEST_Z;
    printf("\n 雜湊值ZA:");
    for (int i = 0; i < SM3_len / 8; i++)
    {
        printf("%X", ZA[i]);
    }
    printf("\n 雜湊值ZB:");
    for (int i = 0; i < SM3_len / 8; i++)
    {
        printf("%X", ZB[i]);
    }
    printf("\n\n ************************金鑰交換A1-A3************************");

    temp = SM2_KeyEx_Init_I(rA, RA);
    if (temp) return temp;
    printf("\n 產生亂數rA:");
    cotnum(rA, stdout);
    printf(" 計算橢圓曲線RA:");
    printf("\n 座標x:");
    cotnum(RA->X, stdout);
    printf(" 座標y:");
    cotnum(RA->Y, stdout);

    epoint_get(RA, x, y);
    big_to_bytes(SM2_NUMWORD, x, xy, 1);
    big_to_bytes(SM2_NUMWORD, y, xy + SM2_NUMWORD, 1);
    if (memcmp(xy, std_RA, SM2_NUMWORD * 2) != 0)
        return ERR_SELFTEST_INI_I;
    printf("\n ************************金鑰交換B1-B9************************");
    temp = SM2_KeyEx_Re_I(rB, dB, RA, pubKeyA, ZA, ZB, KA, std_Klen, RB, V, hash);

    if (temp) return temp;
    if (memcmp(KA, std_K, std_Klen / 8) != 0)
        return ERR_SELFTEST_RES_I;
    printf("\n ************************金鑰交換A4-A10************************\n");
    temp = SM2_KeyEx_Init_II(rA, dA, RA, RB, pubKeyB, ZA, ZB, hash, KB, std_Klen, SA);
    if (temp) return temp;
    if (memcmp(KB, std_K, std_Klen / 8) != 0)
        return ERR_SELFTEST_INI_II;
    printf("\n ************************金鑰交換B10************************");
    if (SM2_KeyEx_Re_II(V, RA, RB, ZA, ZB, SA) != 0)
        return ERR_EQUAL_S2SA;

    free(KA); free(KB);

    return 0;
}

SM3

SM3是國家2010年公佈的一種密碼雜湊演演算法,該演演算法能對\(l\)bit的訊息hash,生成256bit的雜湊值,演演算法主要分為兩步:訊息填充和迭代壓縮,其中迭代壓縮有包含訊息擴充套件、壓縮函數和輸出雜湊值。

  • 用於簽名和認證
  • 在SHA-256演演算法上改進的,安全性與SHA-256相當
  • 迭代過程和訊息填充與MD5相似,也採用Merkle-Damgard結構。
分組長度 雜湊長度
512bit 256bit

下面是演演算法用到的常數和函數:

訊息填充

\(l\)bit的訊息經過訊息\(m\)填充後,得到\(m'\)訊息的位元數是512的倍數。

迭代壓縮

訊息擴充套件

對迭代壓縮前訊息分組\(B^{i}\)(一個字,512bit)進行擴充套件得到132個字\(W_0,...,W_{63},W_0',...,W_{63}'\)

壓縮函數

\(ABCDEFGH\)是字暫存器(32bit),壓縮函數輸入的\(V^{(0)}\)是256bit的初始IV,\(B^{(0)}\)是訊息擴充套件後的第一個分組(5125bit),輸出的是256bit的\(V^{(1)}\),依次計算直到計算出\(V^{(n)}\),並作為最後的雜湊值:

輸出雜湊值

程式

#include <iostream>
#include <string>
#include <cmath>
using namespace std;

//二進位制轉換為十六進位制函數實現
string BinToHex(string str) {
    string hex = "";//用來儲存最後生成的十六進位制數
    int temp = 0;//用來儲存每次四位二進位制數的十進位制值
    while (str.size() % 4 != 0) {//因為每四位二進位制數就能夠成為一個十六進位制數,所以將二進位制數長度轉換為4的倍數
        str = "0" + str;//最高位添0直到長度為4的倍數即可
    }
    for (int i = 0; i < str.size(); i += 4) {
        temp = (str[i] - '0') * 8 + (str[i + 1] - '0') * 4 + (str[i + 2] - '0') * 2 + (str[i + 3] - '0') * 1;//判斷出4位元二進位制數的十進位制大小為多少
        if (temp < 10) {//當得到的值小於10時,可以直接用0-9來代替
            hex += to_string(temp);
        }
        else {//當得到的值大於10時,需要進行A-F的轉換
            hex += 'A' + (temp - 10);
        }
    }
    return hex;
}

//十六進位制轉換為二進位制函數實現
string HexToBin(string str) {
    string bin = "";
    string table[16] = { "0000","0001","0010","0011","0100","0101","0110","0111","1000","1001","1010","1011","1100","1101","1110","1111" };
    for (int i = 0; i < str.size(); i++) {
        if (str[i] >= 'A'&&str[i] <= 'F') {
            bin += table[str[i] - 'A' + 10];
        }
        else {
            bin += table[str[i] - '0'];
        }
    }
    return bin;
}

//二進位制轉換為十進位制的函數實現
int BinToDec(string str) {
    int dec = 0;
    for (int i = 0; i < str.size(); i++) {
        dec += (str[i] - '0')*pow(2, str.size() - i - 1);
    }
    return dec;
}

//十進位制轉換為二進位制的函數實現
string DecToBin(int str) {
    string bin = "";
    while (str >= 1) {
        bin = to_string(str % 2) + bin;
        str = str / 2;
    }
    return bin;
}

//十六進位制轉換為十進位制的函數實現
int HexToDec(string str) {
    int dec = 0;
    for (int i = 0; i < str.size(); i++) {
        if (str[i] >= 'A'&&str[i] <= 'F') {
            dec += (str[i] - 'A' + 10)*pow(16, str.size() - i - 1);
        }
        else {
            dec += (str[i] - '0')*pow(16, str.size() - i - 1);
        }
    }
    return dec;
}

//十進位制轉換為十六進位制的函數實現
string DecToHex(int str) {
    string hex = "";
    int temp = 0;
    while (str >= 1) {
        temp = str % 16;
        if (temp < 10 && temp >= 0) {
            hex = to_string(temp) + hex;
        }
        else {
            hex += ('A' + (temp - 10));
        }
        str = str / 16;
    }
    return hex;
}

string padding(string str) {//對資料進行填充
    string res = "";
    for (int i = 0; i < str.size(); i++) {//首先將輸入值轉換為16進位制字串
        res += DecToHex((int)str[i]);
    }
    cout << "輸入字串的ASCII碼錶示為:" << endl;
    for (int i = 0; i < res.size(); i++) {
        cout << res[i];
        if ((i + 1) % 8 == 0) {
            cout << "  ";
        }
        if ((i + 1) % 64 == 0 || (i + 1) == res.size()) {
            cout << endl;
        }
    }
    cout << endl;
    //填充方式:先補加1,然後填充0至112位元,最後16bit填充0+原來長度(二進位制)
    int res_length = res.size() * 4;//記錄的長度為2進位制下的長度
    res += "8";//在獲得的資料後面添1,在16進位制下相當於是新增8
    while (res.size() % 128 != 112) {
        res += "0";//「0」資料填充
    }
    string res_len = DecToHex(res_length);//用於記錄資料長度的字串
    while (res_len.size() != 16) {
        res_len = "0" + res_len;
    }
    res += res_len;
    return res;
}

string LeftShift(string str, int len) {//實現迴圈左移len位功能
    string res = HexToBin(str);
    res = res.substr(len) + res.substr(0, len);
    return BinToHex(res);
}

string XOR(string str1, string str2) {//實現互斥或操作
    string res1 = HexToBin(str1);
    string res2 = HexToBin(str2);
    string res = "";
    for (int i = 0; i < res1.size(); i++) {
        if (res1[i] == res2[i]) {
            res += "0";
        }
        else {
            res += "1";
        }
    }
    return BinToHex(res);
}

string AND(string str1, string str2) {//實現與操作
    string res1 = HexToBin(str1);
    string res2 = HexToBin(str2);
    string res = "";
    for (int i = 0; i < res1.size(); i++) {
        if (res1[i] == '1' && res2[i] == '1') {
            res += "1";
        }
        else {
            res += "0";
        }
    }
    return BinToHex(res);
}

string OR(string str1, string str2) {//實現或操作
    string res1 = HexToBin(str1);
    string res2 = HexToBin(str2);
    string res = "";
    for (int i = 0; i < res1.size(); i++) {
        if (res1[i] == '0' && res2[i] == '0') {
            res += "0";
        }
        else {
            res += "1";
        }
    }
    return BinToHex(res);
}

string NOT(string str) {//實現非操作
    string res1 = HexToBin(str);
    string res = "";
    for (int i = 0; i < res1.size(); i++) {
        if (res1[i] == '0') {
            res += "1";
        }
        else {
            res += "0";
        }
    }
    return BinToHex(res);
}

char binXor (char str1, char str2) {//實現單位元的互斥或操作
    return str1 == str2 ? '0' : '1';
}

char binAnd(char str1, char str2) {//實現單位元的與操作
    return (str1 == '1'&&str2 == '1') ? '1' : '0';
}

string ModAdd(string str1, string str2) {//mod 2^32運算的函數實現
    string res1 = HexToBin(str1);
    string res2 = HexToBin(str2);
    char temp = '0';
    string res = "";
    for (int i = res1.size() - 1; i >= 0; i--) {
        res = binXor(binXor(res1[i], res2[i]), temp) + res;
        if (binAnd(res1[i], res2[i]) == '1') {
            temp = '1';
        }
        else {
            if (binXor(res1[i], res2[i]) == '1') {
                temp = binAnd('1', temp);
            }
            else {
                temp = '0';
            }
        }
    }
    return BinToHex(res);
}

string P1(string str) {//實現置換功能P1(X)
    return XOR(XOR(str, LeftShift(str, 15)), LeftShift(str, 23));
}

string P0(string str) {//實現置換功能P0(X)
    return XOR(XOR(str, LeftShift(str, 9)), LeftShift(str, 17));
}

string T(int j) {//返回Tj常數值的函數實現
    if (0 <= j && j <= 15) {
        return "79CC4519";
    }
    else {
        return "7A879D8A";
    }
}

string FF(string str1, string str2, string str3, int j) {//實現布林函數FF功能
    if (0 <= j && j <= 15) {
        return XOR(XOR(str1, str2), str3);
    }
    else {
        return OR(OR(AND(str1, str2), AND(str1, str3)), AND(str2, str3));
    }
}

string GG(string str1, string str2, string str3, int j) {//實現布林函數GG功能
    if (0 <= j && j <= 15) {
        return XOR(XOR(str1, str2), str3);
    }
    else {
        return OR(AND(str1, str2), AND(NOT(str1), str3));
    }
}
string extension(string str) {//訊息擴充套件函數
    string res = str;//字串型別儲存前68位元儲存擴充套件字W值
    for (int i = 16; i < 68; i++) {//根據公式生成第17位到第68位元的W值
        res += XOR(
                XOR(
                        P1(
                                XOR(
                                        XOR(
                                                res.substr((i-16)*8,8), res.substr((i - 9) * 8, 8)),
                                                LeftShift(res.substr((i - 3) * 8, 8), 15)
                                        )
                                ),
                    LeftShift(res.substr((i - 13) * 8, 8), 7)
                        ),
            res.substr((i - 6) * 8, 8));
    }
    cout << "擴充套件後的訊息:" << endl;
    cout << "W0,W1,……,W67的訊息:" << endl;
    for (int i = 0; i < 8; i++) {
        for (int j = 0; j < 8; j++) {
            cout << res.substr(i * 64 + j * 8, 8) << "  ";
        }
        cout << endl;
    }
    cout << res.substr(512, 8) << "  " << res.substr(520, 8) << "  " << res.substr(528, 8) << "  " << res.substr(536, 8) << endl;
    cout << endl;
    for (int i = 0; i < 64; i++) {//根據公式生成64位元W'值
        res += XOR(res.substr(i * 8, 8), res.substr((i + 4) * 8, 8));
    }
    cout << "W0',W1',……,W63'的訊息:" << endl;
    for (int i = 0; i < 8; i++) {
        for (int j = 0; j < 8; j++) {
            cout << res.substr(544+i * 64 + j * 8, 8) << "  ";
        }
        cout << endl;
    }
    cout << endl;
    return res;
}

string compress(string str1, string str2) {//訊息壓縮函數
    string IV = str2;
    string A = IV.substr(0, 8), B = IV.substr(8, 8), C = IV.substr(16, 8), D = IV.substr(24, 8), E = IV.substr(32, 8), F = IV.substr(40, 8), G = IV.substr(48, 8), H = IV.substr(56, 8);
    string SS1 = "", SS2 = "", TT1 = "", TT2 = "";
    cout << "迭代壓縮中間值: " << endl;
    cout << "    A         B         C         D         E         F        G         H " << endl;
    cout << A << "  " << B << "  " << C << "  " << D << "  " << E << "  " << F << "  " << G << "  " << H << endl;
    for (int j = 0; j < 64; j++) {
        SS1 = LeftShift(ModAdd(ModAdd(LeftShift(A, 12), E), LeftShift(T(j), (j%32))), 7);
        SS2 = XOR(SS1, LeftShift(A, 12));
        TT1 = ModAdd(ModAdd(ModAdd(FF(A, B, C, j), D), SS2), str1.substr((j + 68) * 8, 8));
        TT2 = ModAdd(ModAdd(ModAdd(GG(E, F, G, j), H), SS1), str1.substr(j * 8, 8));
        D = C;
        C = LeftShift(B, 9);
        B = A;
        A = TT1;
        H = G;
        G = LeftShift(F, 19);
        F = E;
        E = P0(TT2);
        cout << A << "  " << B << "  " << C << "  " << D << "  " << E << "  " << F << "  " << G << "  " << H << endl;
    }
    string res = (A + B + C + D + E + F + G + H);
    cout << endl;
    return res;
}

string iteration(string str) {//迭代壓縮函數實現
    int num = str.size() / 128; //求有多少分組
    cout << "訊息經過填充之後共有 " + to_string(num) + " 個訊息分組。" << endl;
    cout << endl;
    string V = "7380166F4914B2B9172442D7DA8A0600A96F30BC163138AAE38DEE4DB0FB0E4E"; //初始IV(256bit)
    string B = "", extensionB = "", compressB = "";
    for (int i = 0; i < num; i++) {
        cout << "第 " << to_string(i+1) << " 個訊息分組:" << endl;
        cout << endl;
        B = str.substr(i * 128, 128);//取一個分組(512bit)
        extensionB = extension(B); //金鑰擴充套件,得到132個字
        compressB = compress(extensionB, V); //壓縮函數,返回的是ABCDEFGH
        V = XOR(V, compressB); //V^{i+1}=V^{i} ^ V ^{i}
    }
    return V;
}

int main() {//主函數
    string str[2];
    str[0] = "abc";
    str[1] = "abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd";
    for (int num = 0; num < 2; num++) {
        //to_string():將數位常數轉換為字串,返回值為轉換後的字串
        cout << "範例 " + to_string(num + 1) + " :輸入訊息為字串: " + str[num] << endl;
        cout << endl;
        string paddingValue = padding(str[num]);
        cout << "填充後的訊息為:" << endl;
        for (int i = 0; i < paddingValue.size() / 64; i++) {
            for (int j = 0; j < 8; j++) {
                //substr(pos,len):從初始位置pos開始讀取長度為len個字元
                cout << paddingValue.substr(i * 64 + j * 8, 8) << "  ";
            }
            cout << endl;
        }
        cout << endl;
        string result = iteration(paddingValue);
        cout << "雜湊值:" << endl;
        for (int i = 0; i < 8; i++) {
            cout << result.substr(i * 8, 8) << "  ";
        }
        cout << endl;
        cout << endl;
    }
}

參考

1、SM3密碼雜湊演演算法

2、https://blog.csdn.net/qq_40662424/article/details/121637732

SM4

SM4用於WAPI(無線區域網鑑別和保密基礎協定)的分組密碼演演算法,2006年公佈的第一個商密

分組長度 金鑰長度 輪數
128bit 128bit 32

  • 金鑰擴充套件演演算法:將初始金鑰擴充套件為輪金鑰。
  • 資料解密和資料加密的演演算法結構相同,只是輪金鑰的使用順序相反解密輪金鑰是加密輪金鑰的逆序
  • 優勢:
    • 金鑰擴充套件演演算法與加密演演算法結構類似,在設計上做到資源重用
    • SM4採用的是非平衡Feistel網路
    • SM4中使用了唯一的非線性變換(S盒),提高了演演算法的非線性,隱藏代數結構,提高混淆作用
    • SM4中使用了線性變換(\(L()\)),增加了各輸出值的相關性,使S盒的輸出值得到擴散,使得密碼演演算法能夠抵抗差分攻擊和線性分析

加密

輸入128bit的明文分組,輸出128bit的密文,下面定義「1個字=32bit=4個位元組」。

輪函數

輪函數\(F()\)輸入4個字(128bit)\((X_0,X_1,X_2,X_3)\)和一個字(32bit)的輪金鑰\(rk\),變換為:

\[F(X_0,X_1,X_2,X_3,rk)=X_0\oplus T(X_1\oplus X_2\oplus X_3\oplus rk)= \\ X_0\oplus L(\tau (X_1\oplus X_2\oplus X_3\oplus rk)) \]

其中\(T()\)是合成變換,即\(T()=L(\tau())\)\(\tau ()\)是非線性變換(S盒),\(L()\)是線性變換。

\(B=(X_1\oplus X_2\oplus X_3\oplus rk)\),則\(F(X_0,X_1,X_2,X_3,rk)\begin{aligned}=& X_{0} \oplus[S(B)]\oplus[S(B)<<<2]\oplus[S(B)<<<10]& \oplus[S(B)<<<18] \oplus[S(B)<<<24]\end{aligned}\)

  • 非線性變換

就是4個S盒並行,其作用是混淆,輸入的是\(A=(a_0,a_1,a_2,a_3)\)(4個位元組,32bit),輸出的是\(B=(b_0,b_1,b_2,b_3)\)(32位元)即:

\[B=(b_0,b_1,b_2,b_3)=\tau (A)=(S(a_0),S(a_1),S(s_2),S(a_3)) \]

其中\(S(a)\)是一個S盒變換,輸入\(a\)是一個位元組(8bit),輸出的\(S(a)\)也是8bit。

其中S盒變換,與AES相似:行(前4bit),列(後4bit)

  • 線性變換

線性變換\(L(B)\)的作用是擴散,其功能就是迴圈移位和互斥或,輸入的\(B\)是一個字(32bit),輸出的\(C=L(B)\)也是一個字(32bit),具體如下:

\[C=L(B)=B \oplus(B<<<2) \oplus(B<<<10) \oplus(B<<<18) \oplus(B<<<24) \]

反序變換

反序變換\(R\)就是在最後一輪後生成的\((x_{32},X_{33},X_{34},X_{35})\)進行反序輸出,即

\[\left(Y_{0}, Y_{1}, Y_{2}, Y_{3}\right)=\left(X_{35}, X_{34}, X_{33}, X_{32}\right)=R\left(X_{32}, X_{33}, X_{34}, X_{35}\right) \]

金鑰擴充套件

輸入的初始金鑰為\(MK=(MK_0,MK_1,MK_2,MK_3)\)(128bit),採用32輪迭代,每輪輸出一個輪金鑰\(rk_i,(i=0,...,31)\)(32bit)

所需引數:

  • 常數FK

\[F K_{0}=(A3B1BAC6),FK_{1}=(56AA3350),FK_{2}=(677D9197),FK_{3}=(B27022DC) \]

  • 固定引數CK

引數生成方式:

\(ck_{i,j}\)\(CK_i\)的第\(j\)個位元組\((i=0,1, \cdots, 31 ; j=0,1,2,3)\),即\(C K_{i}=\left(c k_{i, 0}, c k_{i, 1}, c k_{i, 2}, c k_{i, 3}\right)\),其中\(c k_{i, j}=(4 i+j) \times 7(\bmod 256)\)

以下是生成好的32個固定引數:

  • 32輪迭代

其中\(K_i(i=0,1,...,35)\)為中間資料:

\(\text { (1) }\left(K_{0}, K_{1}, K_{2}, K_{3}\right)=\left(\mathrm{MK}_{0} \oplus \mathrm{FK}_{0}, \mathrm{MK}_{1} \oplus \mathrm{FK}_{1}, \mathrm{MK}_{2} \oplus \mathrm{FK}_{2}, \mathrm{MK}_{3} \oplus \mathrm{FK}_{3}\right)\)

\(\text { (2) }\mathrm{rk}_{i}=K_{i+4}=K_{i} \oplus T^{\prime}\left(K_{i+1} \oplus K_{i+2} \oplus K_{i+3} \oplus CK_{i}\right)\),對於\(i=0,...,31\)

其中的\(T'()\)與加密演演算法中的輪函數相似,只是其中的線性變換\(L'()\)有所不同:

\[L^{\prime}(B)=B \oplus(B<<<13) \oplus(B<<<23) \]

解密

解密與加密相似,不同的是輪金鑰使用的順序,即解密時使用的輪金鑰為\(rk_{31},...,rk_0\)

程式

  • 簡單實現加解密
#include <iostream>
#include <string>
using namespace std;

string BinToHex(string str) {//二進位制轉換為十六進位制的函數實現
    string hex = "";
    int temp = 0;
    while (str.size() % 4 != 0) {
        str = "0" + str;
    }
    for (int i = 0; i < str.size(); i += 4) {
        temp = (str[i] - '0') * 8 + (str[i + 1] - '0') * 4 + (str[i + 2] - '0') * 2 + (str[i + 3] - '0') * 1;
        if (temp < 10) {
            hex += to_string(temp);
        }
        else {
            hex += 'A' + (temp - 10);
        }
    }
    return hex;
}

string HexToBin(string str) {//十六進位制轉換為二進位制的函數實現
    string bin = "";
    string table[16] = { "0000","0001","0010","0011","0100","0101","0110","0111","1000","1001","1010","1011","1100","1101","1110","1111" };
    for (int i = 0; i < str.size(); i++) {
        if (str[i] >= 'A'&&str[i] <= 'F') {
            bin += table[str[i] - 'A' + 10];
        }
        else {
            bin += table[str[i] - '0'];
        }
    }
    return bin;
}


int HexToDec(char str) {//十六進位制轉換為十進位制的函數實現
    int dec = 0;
    if (str >= 'A' && str <= 'F') {
        dec += (str - 'A' + 10);
    }
    else {
        dec += (str - '0');
    }
    return dec;
}

string LeftShift(string str, int len) {//迴圈左移len位函數實現
    string res = HexToBin(str);
    res = res.substr(len) + res.substr(0, len);
    return BinToHex(res);
}

string XOR(string str1, string str2) {//互斥或函數實現
    string res1 = HexToBin(str1);
    string res2 = HexToBin(str2);
    string res = "";
    for (int i = 0; i < res1.size(); i++) {
        if (res1[i] == res2[i]) {
            res += "0";
        }
        else {
            res += "1";
        }
    }
    return BinToHex(res);
}

string NLTransform(string str) {//非線性變換t函數實現
    string Sbox[16][16] = { {"D6","90","E9","FE","CC","E1","3D","B7","16","B6","14","C2","28","FB","2C","05"},
                            {"2B","67","9A","76","2A","BE","04","C3","AA","44","13","26","49","86","06","99"},
                            {"9C","42","50","F4","91","EF","98","7A","33","54","0B","43","ED","CF","AC","62"},
                            {"E4","B3","1C","A9","C9","08","E8","95","80","DF","94","FA","75","8F","3F","A6"},
                            {"47","07","A7","FC","F3","73","17","BA","83","59","3C","19","E6","85","4F","A8"},
                            {"68","6B","81","B2","71","64","DA","8B","F8","EB","0F","4B","70","56","9D","35"},
                            {"1E","24","0E","5E","63","58","D1","A2","25","22","7C","3B","01","21","78","87"},
                            {"D4","00","46","57","9F","D3","27","52","4C","36","02","E7","A0","C4","C8","9E"},
                            {"EA","BF","8A","D2","40","C7","38","B5","A3","F7","F2","CE","F9","61","15","A1"},
                            {"E0","AE","5D","A4","9B","34","1A","55","AD","93","32","30","F5","8C","B1","E3"},
                            {"1D","F6","E2","2E","82","66","CA","60","C0","29","23","AB","0D","53","4E","6F"},
                            {"D5","DB","37","45","DE","FD","8E","2F","03","FF","6A","72","6D","6C","5B","51"},
                            {"8D","1B","AF","92","BB","DD","BC","7F","11","D9","5C","41","1F","10","5A","D8"},
                            {"0A","C1","31","88","A5","CD","7B","BD","2D","74","D0","12","B8","E5","B4","B0"},
                            {"89","69","97","4A","0C","96","77","7E","65","B9","F1","09","C5","6E","C6","84"},
                            {"18","F0","7D","EC","3A","DC","4D","20","79","EE","5F","3E","D7","CB","39","48"} };
    string res = "";
    for (int i = 0; i < 4; i++) {
        res = res + Sbox[HexToDec(str[2 * i])][HexToDec(str[2 * i + 1])];
    }
    return res;
}

string LTransform(string str) {//線性變換L函數實現
    return XOR(XOR(XOR(XOR(str, LeftShift(str, 2)), LeftShift(str, 10)), LeftShift(str, 18)), LeftShift(str, 24));
}

string L2Transform(string str) {//線性變換L'函數實現
    return XOR(XOR(str, LeftShift(str, 13)), LeftShift(str, 23));
}

string T(string str) {//用於加解密演演算法中的合成置換T函數實現
    return LTransform(NLTransform(str));//NLTransform()是S盒置換,LTransform()是線性L()變換
}

string T2(string str) {//用於金鑰擴充套件演演算法中的合成置換T函數實現
    return L2Transform(NLTransform(str));//NLTransform()是S盒置換,L2Transform()是線性L'()變換
}

string KeyExtension(string MK) {//金鑰擴充套件函數實現
    string FK[4] = { "A3B1BAC6", "56AA3350", "677D9197", "B27022DC" };
    string CK[32] = { "00070E15", "1C232A31", "383F464D", "545B6269",
                      "70777E85", "8C939AA1", "A8AFB6BD", "C4CBD2D9",
                      "E0E7EEF5", "FC030A11", "181F262D", "343B4249",
                      "50575E65", "6C737A81", "888F969D", "A4ABB2B9",
                      "C0C7CED5", "DCE3EAF1", "F8FF060D", "141B2229",
                      "30373E45", "4C535A61", "686F767D", "848B9299",
                      "A0A7AEB5", "BCC3CAD1", "D8DFE6ED", "F4FB0209",
                      "10171E25", "2C333A41", "484F565D", "646B7279" };
    string K[36] = { XOR(MK.substr(0,8),FK[0]),XOR(MK.substr(8,8),FK[1]),XOR(MK.substr(16,8),FK[2]),XOR(MK.substr(24),FK[3]) };
    string rks = "";
    for (int i = 0; i < 32; i++) {
        K[i + 4] = XOR(K[i], T2(XOR(XOR(XOR(K[i + 1], K[i + 2]), K[i + 3]), CK[i])));//T2()是金鑰擴充套件的變換
        rks += K[i + 4];
    }
    return rks;
}

string encode(string plain, string key) {//加密函數實現
    cout << "輪金鑰與每輪輸出狀態:" << endl;
    cout << endl;
    string cipher[36] = { plain.substr(0,8),plain.substr(8,8),plain.substr(16,8),plain.substr(24) };
    string rks = KeyExtension(key); //金鑰擴充套件
    for (int i = 0; i < 32; i++) {
        cipher[i + 4] = XOR(cipher[i], T(XOR(XOR(XOR(cipher[i + 1], cipher[i + 2]), cipher[i + 3]), rks.substr(8 * i, 8))));
        cout << "rk[" + to_string(i) + "] = " + rks.substr(8 * i, 8) + "    X[" + to_string(i) + "] = " + cipher[i + 4] << endl;
    }
    cout << endl;
    return cipher[35] + cipher[34] + cipher[33] + cipher[32]; //反序變換R
}

string decode(string cipher, string key) {//解密函數實現
    cout << "輪金鑰與每輪輸出狀態:" << endl;
    cout << endl;
    string plain[36] = { cipher.substr(0,8),cipher.substr(8,8), cipher.substr(16,8), cipher.substr(24,8) };
    string rks = KeyExtension(key); //金鑰擴充套件
    for (int i = 0; i < 32; i++) {
        plain[i + 4] = XOR(plain[i], T(XOR(XOR(XOR(plain[i + 1], plain[i + 2]), plain[i + 3]), rks.substr(8 * (31 - i), 8))));
        cout << "rk[" + to_string(i) + "] = " + rks.substr(8 * (31 - i), 8) + "    X[" + to_string(i) + "] = " + plain[i + 4] << endl;
    }
    cout << endl;
    return plain[35] + plain[34] + plain[33] + plain[32]; //反序變換R
}

int main() {//主函數
    string str = "0123456789ABCDEFFEDCBA9876543210";
    cout << "明    文:" << str.substr(0, 8) << "  " << str.substr(8, 8) << "  " << str.substr(16, 8) << "  " << str.substr(24, 8) << endl;
    cout << endl;

    string key = "0123456789ABCDEFFEDCBA9876543210";
    cout << "加密金鑰:" << key.substr(0, 8) << "  " << key.substr(8, 8) << "  " << key.substr(16, 8) << "  " << key.substr(24, 8) << endl;
    cout << endl;

    string cipher = encode(str, key);
    cout << "密    文:" << cipher.substr(0, 8) << "  " << cipher.substr(8, 8) << "  " << cipher.substr(16, 8) << "  " << cipher.substr(24, 8) << endl;
    cout << endl;

    cout << "解密金鑰:" << key.substr(0, 8) << "  " << key.substr(8, 8) << "  " << key.substr(16, 8) << "  " << key.substr(24, 8) << endl;
    cout << endl;

    string plain = decode(cipher, key);
    cout << "明    文:" << plain.substr(0, 8) << "  " << plain.substr(8, 8) << "  " << plain.substr(16, 8) << "  " << plain.substr(24, 8) << endl;
}

  • openSSL中提取
/*
 * Copyright 2017-2021 The OpenSSL Project Authors. All Rights Reserved.
 * Copyright 2017 Ribose Inc. All Rights Reserved.
 * Ported from Ribose contributions from Botan.
 *
 * Licensed under the Apache License 2.0 (the "License").  You may not use
 * this file except in compliance with the License.  You can obtain a copy
 * in the file LICENSE in the source distribution or at
 * https://www.openssl.org/source/license.html
 */

#include "sm4.h"
#define ossl_inline inline
static const uint8_t SM4_S[256] = {
        0xD6, 0x90, 0xE9, 0xFE, 0xCC, 0xE1, 0x3D, 0xB7, 0x16, 0xB6, 0x14, 0xC2,
        0x28, 0xFB, 0x2C, 0x05, 0x2B, 0x67, 0x9A, 0x76, 0x2A, 0xBE, 0x04, 0xC3,
        0xAA, 0x44, 0x13, 0x26, 0x49, 0x86, 0x06, 0x99, 0x9C, 0x42, 0x50, 0xF4,
        0x91, 0xEF, 0x98, 0x7A, 0x33, 0x54, 0x0B, 0x43, 0xED, 0xCF, 0xAC, 0x62,
        0xE4, 0xB3, 0x1C, 0xA9, 0xC9, 0x08, 0xE8, 0x95, 0x80, 0xDF, 0x94, 0xFA,
        0x75, 0x8F, 0x3F, 0xA6, 0x47, 0x07, 0xA7, 0xFC, 0xF3, 0x73, 0x17, 0xBA,
        0x83, 0x59, 0x3C, 0x19, 0xE6, 0x85, 0x4F, 0xA8, 0x68, 0x6B, 0x81, 0xB2,
        0x71, 0x64, 0xDA, 0x8B, 0xF8, 0xEB, 0x0F, 0x4B, 0x70, 0x56, 0x9D, 0x35,
        0x1E, 0x24, 0x0E, 0x5E, 0x63, 0x58, 0xD1, 0xA2, 0x25, 0x22, 0x7C, 0x3B,
        0x01, 0x21, 0x78, 0x87, 0xD4, 0x00, 0x46, 0x57, 0x9F, 0xD3, 0x27, 0x52,
        0x4C, 0x36, 0x02, 0xE7, 0xA0, 0xC4, 0xC8, 0x9E, 0xEA, 0xBF, 0x8A, 0xD2,
        0x40, 0xC7, 0x38, 0xB5, 0xA3, 0xF7, 0xF2, 0xCE, 0xF9, 0x61, 0x15, 0xA1,
        0xE0, 0xAE, 0x5D, 0xA4, 0x9B, 0x34, 0x1A, 0x55, 0xAD, 0x93, 0x32, 0x30,
        0xF5, 0x8C, 0xB1, 0xE3, 0x1D, 0xF6, 0xE2, 0x2E, 0x82, 0x66, 0xCA, 0x60,
        0xC0, 0x29, 0x23, 0xAB, 0x0D, 0x53, 0x4E, 0x6F, 0xD5, 0xDB, 0x37, 0x45,
        0xDE, 0xFD, 0x8E, 0x2F, 0x03, 0xFF, 0x6A, 0x72, 0x6D, 0x6C, 0x5B, 0x51,
        0x8D, 0x1B, 0xAF, 0x92, 0xBB, 0xDD, 0xBC, 0x7F, 0x11, 0xD9, 0x5C, 0x41,
        0x1F, 0x10, 0x5A, 0xD8, 0x0A, 0xC1, 0x31, 0x88, 0xA5, 0xCD, 0x7B, 0xBD,
        0x2D, 0x74, 0xD0, 0x12, 0xB8, 0xE5, 0xB4, 0xB0, 0x89, 0x69, 0x97, 0x4A,
        0x0C, 0x96, 0x77, 0x7E, 0x65, 0xB9, 0xF1, 0x09, 0xC5, 0x6E, 0xC6, 0x84,
        0x18, 0xF0, 0x7D, 0xEC, 0x3A, 0xDC, 0x4D, 0x20, 0x79, 0xEE, 0x5F, 0x3E,
        0xD7, 0xCB, 0x39, 0x48
};

/*
 * SM4_SBOX_T[j] == L(SM4_SBOX[j]).
 */
static const uint32_t SM4_SBOX_T[256] = {
        0x8ED55B5B, 0xD0924242, 0x4DEAA7A7, 0x06FDFBFB, 0xFCCF3333, 0x65E28787,
        0xC93DF4F4, 0x6BB5DEDE, 0x4E165858, 0x6EB4DADA, 0x44145050, 0xCAC10B0B,
        0x8828A0A0, 0x17F8EFEF, 0x9C2CB0B0, 0x11051414, 0x872BACAC, 0xFB669D9D,
        0xF2986A6A, 0xAE77D9D9, 0x822AA8A8, 0x46BCFAFA, 0x14041010, 0xCFC00F0F,
        0x02A8AAAA, 0x54451111, 0x5F134C4C, 0xBE269898, 0x6D482525, 0x9E841A1A,
        0x1E061818, 0xFD9B6666, 0xEC9E7272, 0x4A430909, 0x10514141, 0x24F7D3D3,
        0xD5934646, 0x53ECBFBF, 0xF89A6262, 0x927BE9E9, 0xFF33CCCC, 0x04555151,
        0x270B2C2C, 0x4F420D0D, 0x59EEB7B7, 0xF3CC3F3F, 0x1CAEB2B2, 0xEA638989,
        0x74E79393, 0x7FB1CECE, 0x6C1C7070, 0x0DABA6A6, 0xEDCA2727, 0x28082020,
        0x48EBA3A3, 0xC1975656, 0x80820202, 0xA3DC7F7F, 0xC4965252, 0x12F9EBEB,
        0xA174D5D5, 0xB38D3E3E, 0xC33FFCFC, 0x3EA49A9A, 0x5B461D1D, 0x1B071C1C,
        0x3BA59E9E, 0x0CFFF3F3, 0x3FF0CFCF, 0xBF72CDCD, 0x4B175C5C, 0x52B8EAEA,
        0x8F810E0E, 0x3D586565, 0xCC3CF0F0, 0x7D196464, 0x7EE59B9B, 0x91871616,
        0x734E3D3D, 0x08AAA2A2, 0xC869A1A1, 0xC76AADAD, 0x85830606, 0x7AB0CACA,
        0xB570C5C5, 0xF4659191, 0xB2D96B6B, 0xA7892E2E, 0x18FBE3E3, 0x47E8AFAF,
        0x330F3C3C, 0x674A2D2D, 0xB071C1C1, 0x0E575959, 0xE99F7676, 0xE135D4D4,
        0x661E7878, 0xB4249090, 0x360E3838, 0x265F7979, 0xEF628D8D, 0x38596161,
        0x95D24747, 0x2AA08A8A, 0xB1259494, 0xAA228888, 0x8C7DF1F1, 0xD73BECEC,
        0x05010404, 0xA5218484, 0x9879E1E1, 0x9B851E1E, 0x84D75353, 0x00000000,
        0x5E471919, 0x0B565D5D, 0xE39D7E7E, 0x9FD04F4F, 0xBB279C9C, 0x1A534949,
        0x7C4D3131, 0xEE36D8D8, 0x0A020808, 0x7BE49F9F, 0x20A28282, 0xD4C71313,
        0xE8CB2323, 0xE69C7A7A, 0x42E9ABAB, 0x43BDFEFE, 0xA2882A2A, 0x9AD14B4B,
        0x40410101, 0xDBC41F1F, 0xD838E0E0, 0x61B7D6D6, 0x2FA18E8E, 0x2BF4DFDF,
        0x3AF1CBCB, 0xF6CD3B3B, 0x1DFAE7E7, 0xE5608585, 0x41155454, 0x25A38686,
        0x60E38383, 0x16ACBABA, 0x295C7575, 0x34A69292, 0xF7996E6E, 0xE434D0D0,
        0x721A6868, 0x01545555, 0x19AFB6B6, 0xDF914E4E, 0xFA32C8C8, 0xF030C0C0,
        0x21F6D7D7, 0xBC8E3232, 0x75B3C6C6, 0x6FE08F8F, 0x691D7474, 0x2EF5DBDB,
        0x6AE18B8B, 0x962EB8B8, 0x8A800A0A, 0xFE679999, 0xE2C92B2B, 0xE0618181,
        0xC0C30303, 0x8D29A4A4, 0xAF238C8C, 0x07A9AEAE, 0x390D3434, 0x1F524D4D,
        0x764F3939, 0xD36EBDBD, 0x81D65757, 0xB7D86F6F, 0xEB37DCDC, 0x51441515,
        0xA6DD7B7B, 0x09FEF7F7, 0xB68C3A3A, 0x932FBCBC, 0x0F030C0C, 0x03FCFFFF,
        0xC26BA9A9, 0xBA73C9C9, 0xD96CB5B5, 0xDC6DB1B1, 0x375A6D6D, 0x15504545,
        0xB98F3636, 0x771B6C6C, 0x13ADBEBE, 0xDA904A4A, 0x57B9EEEE, 0xA9DE7777,
        0x4CBEF2F2, 0x837EFDFD, 0x55114444, 0xBDDA6767, 0x2C5D7171, 0x45400505,
        0x631F7C7C, 0x50104040, 0x325B6969, 0xB8DB6363, 0x220A2828, 0xC5C20707,
        0xF531C4C4, 0xA88A2222, 0x31A79696, 0xF9CE3737, 0x977AEDED, 0x49BFF6F6,
        0x992DB4B4, 0xA475D1D1, 0x90D34343, 0x5A124848, 0x58BAE2E2, 0x71E69797,
        0x64B6D2D2, 0x70B2C2C2, 0xAD8B2626, 0xCD68A5A5, 0xCB955E5E, 0x624B2929,
        0x3C0C3030, 0xCE945A5A, 0xAB76DDDD, 0x867FF9F9, 0xF1649595, 0x5DBBE6E6,
        0x35F2C7C7, 0x2D092424, 0xD1C61717, 0xD66FB9B9, 0xDEC51B1B, 0x94861212,
        0x78186060, 0x30F3C3C3, 0x897CF5F5, 0x5CEFB3B3, 0xD23AE8E8, 0xACDF7373,
        0x794C3535, 0xA0208080, 0x9D78E5E5, 0x56EDBBBB, 0x235E7D7D, 0xC63EF8F8,
        0x8BD45F5F, 0xE7C82F2F, 0xDD39E4E4, 0x68492121 };

static ossl_inline uint32_t rotl(uint32_t a, uint8_t n)
{
return (a << n) | (a >> (32 - n));
}

static ossl_inline uint32_t load_u32_be(const uint8_t *b, uint32_t n)
{
    return ((uint32_t)b[4 * n] << 24) |
    ((uint32_t)b[4 * n + 1] << 16) |
    ((uint32_t)b[4 * n + 2] << 8) |
    ((uint32_t)b[4 * n + 3]);
}

static ossl_inline void store_u32_be(uint32_t v, uint8_t *b)
{
    b[0] = (uint8_t)(v >> 24);
    b[1] = (uint8_t)(v >> 16);
    b[2] = (uint8_t)(v >> 8);
    b[3] = (uint8_t)(v);
}
//合成變換T()=L(S())
static ossl_inline uint32_t SM4_T_slow(uint32_t X)
{
    uint32_t t = 0;

    //S盒變換(非線性變換)
    t |= ((uint32_t)SM4_S[(uint8_t)(X >> 24)]) << 24;
    t |= ((uint32_t)SM4_S[(uint8_t)(X >> 16)]) << 16;
    t |= ((uint32_t)SM4_S[(uint8_t)(X >> 8)]) << 8;
    t |= SM4_S[(uint8_t)X];

    /*
     * L linear transform L()變換(左迴圈移位)-線性變換
     */
    return t ^ rotl(t, 2) ^ rotl(t, 10) ^ rotl(t, 18) ^ rotl(t, 24);
}

//
static ossl_inline uint32_t SM4_T(uint32_t X)
{
    return SM4_SBOX_T[(uint8_t)(X >> 24)] ^
    rotl(SM4_SBOX_T[(uint8_t)(X >> 16)], 24) ^
    rotl(SM4_SBOX_T[(uint8_t)(X >> 8)], 16) ^
    rotl(SM4_SBOX_T[(uint8_t)X], 8);

    //沒有迴圈移位(線性變換)
}

int ossl_sm4_set_key(const uint8_t *key, SM4_KEY *ks)
{
    /*
     * Family Key
     */
    static const uint32_t FK[4] =
            { 0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc };

    /*
     * Constant Key
     */
    static const uint32_t CK[32] = {
            0x00070E15, 0x1C232A31, 0x383F464D, 0x545B6269,
            0x70777E85, 0x8C939AA1, 0xA8AFB6BD, 0xC4CBD2D9,
            0xE0E7EEF5, 0xFC030A11, 0x181F262D, 0x343B4249,
            0x50575E65, 0x6C737A81, 0x888F969D, 0xA4ABB2B9,
            0xC0C7CED5, 0xDCE3EAF1, 0xF8FF060D, 0x141B2229,
            0x30373E45, 0x4C535A61, 0x686F767D, 0x848B9299,
            0xA0A7AEB5, 0xBCC3CAD1, 0xD8DFE6ED, 0xF4FB0209,
            0x10171E25, 0x2C333A41, 0x484F565D, 0x646B7279
    };

    uint32_t K[4];
    int i;

    K[0] = load_u32_be(key, 0) ^ FK[0];
    K[1] = load_u32_be(key, 1) ^ FK[1];
    K[2] = load_u32_be(key, 2) ^ FK[2];
    K[3] = load_u32_be(key, 3) ^ FK[3];

    for (i = 0; i != SM4_KEY_SCHEDULE; ++i) {
        uint32_t X = K[(i + 1) % 4] ^ K[(i + 2) % 4] ^ K[(i + 3) % 4] ^ CK[i]; //B'
        uint32_t t = 0;

        //4個S盒變換
        t |= ((uint32_t)SM4_S[(uint8_t)(X >> 24)]) << 24;
        t |= ((uint32_t)SM4_S[(uint8_t)(X >> 16)]) << 16;
        t |= ((uint32_t)SM4_S[(uint8_t)(X >> 8)]) << 8;
        t |= SM4_S[(uint8_t)X];

        t = t ^ rotl(t, 13) ^ rotl(t, 23);//L'()函數
        K[i % 4] ^= t; //K_{i+4}
        ks->rk[i] = K[i % 4]; //rk_i
    }

    return 1;
}

#define SM4_RNDS(k0, k1, k2, k3, F)          \
      do {                                   \
         B0 ^= F(B1 ^ B2 ^ B3 ^ ks->rk[k0]); \
         B1 ^= F(B0 ^ B2 ^ B3 ^ ks->rk[k1]); \
         B2 ^= F(B0 ^ B1 ^ B3 ^ ks->rk[k2]); \
         B3 ^= F(B0 ^ B1 ^ B2 ^ ks->rk[k3]); \
      } while(0)

void ossl_sm4_encrypt(const uint8_t *in, uint8_t *out, const SM4_KEY *ks)
{
    //8bit擴充套件為32bit,最終形成128bit=4*32bit
    uint32_t B0 = load_u32_be(in, 0);
    uint32_t B1 = load_u32_be(in, 1);
    uint32_t B2 = load_u32_be(in, 2);
    uint32_t B3 = load_u32_be(in, 3);

    /*
     * Uses byte-wise sbox in the first and last rounds to provide some
     * protection from cache based side channels.
     * 32輪迭代(4個為一個組)
     */
    SM4_RNDS( 0,  1,  2,  3, SM4_T_slow);//前4輪迭代
    SM4_RNDS( 4,  5,  6,  7, SM4_T);
    SM4_RNDS( 8,  9, 10, 11, SM4_T);
    SM4_RNDS(12, 13, 14, 15, SM4_T);
    SM4_RNDS(16, 17, 18, 19, SM4_T);
    SM4_RNDS(20, 21, 22, 23, SM4_T);
    SM4_RNDS(24, 25, 26, 27, SM4_T);
    SM4_RNDS(28, 29, 30, 31, SM4_T_slow);//後4輪迭代

    store_u32_be(B3, out);
    store_u32_be(B2, out + 4);
    store_u32_be(B1, out + 8);
    store_u32_be(B0, out + 12);
}

void ossl_sm4_decrypt(const uint8_t *in, uint8_t *out, const SM4_KEY *ks)
{
    uint32_t B0 = load_u32_be(in, 0);
    uint32_t B1 = load_u32_be(in, 1);
    uint32_t B2 = load_u32_be(in, 2);
    uint32_t B3 = load_u32_be(in, 3);

    SM4_RNDS(31, 30, 29, 28, SM4_T_slow);
    SM4_RNDS(27, 26, 25, 24, SM4_T);
    SM4_RNDS(23, 22, 21, 20, SM4_T);
    SM4_RNDS(19, 18, 17, 16, SM4_T);
    SM4_RNDS(15, 14, 13, 12, SM4_T);
    SM4_RNDS(11, 10,  9,  8, SM4_T);
    SM4_RNDS( 7,  6,  5,  4, SM4_T);
    SM4_RNDS( 3,  2,  1,  0, SM4_T_slow);

    store_u32_be(B3, out);
    store_u32_be(B2, out + 4);
    store_u32_be(B1, out + 8);
    store_u32_be(B0, out + 12);
}
int main()
{
    const char* Sm4Key = "12345678";
    SM4_KEY key;
    ossl_sm4_set_key((const unsigned char* )Sm4Key, & key);

    const char* indata = "0123456789ABCDEF";
    unsigned char Encrydata[256] = { 0 };

    printf("明文訊息:%s\n",indata);
    printf("金鑰:%s\n",Sm4Key);

    //該函數每次加密128bit分組,即16位元組,需自行補齊、分組
    ossl_sm4_encrypt((unsigned char*)indata, Encrydata, &key);
    printf("密文:%sX\n",Encrydata);

    unsigned char Decrydata[256] = { 0 };
    SM4_KEY key2;
    ossl_sm4_set_key((const unsigned char*)Sm4Key, &key2);
    ossl_sm4_decrypt((unsigned char*)Encrydata, Decrydata, &key2);
    printf("解密後明文:%s\n",Decrydata);
    return  0;
}

SM9

SM9是一種標識密碼(Identity-Based Cryptography),其使用者的私鑰由金鑰生成中心(KGC)根據主金鑰和使用者標識計算出,使用者的公鑰由使用者標識唯一確定。

下面解釋一些名詞:

  • 使用者標識:指唯一可以確定使用者身份的資訊,即使用者無法否認的資訊,例如:身份證,電話號碼等

  • 主金鑰:分為主私鑰和主公鑰,其中主私鑰由KGC通過亂數發生器產生,並由KGC祕密儲存,主公鑰由主私鑰結合系統引數產生

  • KGC:負責選擇系統引數,生成主金鑰併產生使用者私鑰

  • 有限域、素域和擴域(更多參考:https://www.cnblogs.com/pam-sh/p/16540779.html)

  • 有限域上的離散對數問題(DLP)和橢圓曲線上的離散對數問題(ECDLP)(更多參考:https://www.cnblogs.com/pam-sh/p/16778293.html)

    • 解以上兩個問題的方法(攻擊方式):

    加密

    • A需要給B傳送長度為\(mlen\)的明文位元串\(M\),其中\(K_1-len\)是金鑰\(K_1\)的位元長度,\(K_2-len\)是金鑰\(K_2\)的位元長度

    • 金鑰封裝

    ![image-20221114112344182](/Users/hangshao/Library/Application Support/typora-user-images/image-20221114112344182.png)

    • 訊息封裝

    • 加密流程:

    解密

    • B得到密文\(C=C_1||C_2||C_3\)後,使用私鑰\(de_B\)解密

    • 最後的訊息認證函數,就是將金鑰和密文進行雜湊

    • 解密流程:

    程式實現

    /****************************************************************
    Function:       SM9_SelfCheck
    Description:    SM9 self check
    Calls:          MIRACL functions,SM9_Init(),SM9_GenerateEncryptKey(),
    SM9_Encrypt,SM9_Decrypt
    Called By:
    Input:
    Output:
    Return:         0: self-check success
    1: asking for memory error
    2: element is out of order q
    3: R-ate calculation error
    4: test if C1 is on G1
    5: base point P1 error
    6: base point P2 error
    7: Encryption public key generated error
    8: Encryption private key generated error
    9: encryption error
    A: K1 equals 0
    B: compare error of C3
    C: decryption error
    Others:
    ****************************************************************/
    int SM9_SelfCheck()
    {
        int MAX_LEN=1024;
        //時間定義
        time_t begin, end;
    
        //the master private key
        unsigned char KE[32] = { 0x00,0x01,0xED,0xEE,0x37,0x78,0xF4,0x41,0xF8,0xDE,0xA3,0xD9,0xFA,0x0A,0xCC,0x4E,
                                 0x07,0xEE,0x36,0xC9,0x3F,0x9A,0x08,0x61,0x8A,0xF4,0xAD,0x85,0xCE,0xDE,0x1C,0x22 };
    
        unsigned char rand[32] = {0x00,0x00,0xAA,0xC0,0x54,0x17,0x79,0xC8,0xFC,0x45,0xE3,0xE2,0xCB,0x25,0xC1,0x2B,
                                  0x5D,0x25,0x76,0xB2,0x12,0x9A,0xE8,0xBB,0x5E,0xE2,0xCB,0xE5,0xEC,0x9E,0x78,0x5C };
        //standard datas
        unsigned char std_Ppub[64] = { 0x78,0x7E,0xD7,0xB8,0xA5,0x1F,0x3A,0xB8,0x4E,0x0A,0x66,0x00,0x3F,0x32,0xDA,0x5C,
                                       0x72,0x0B,0x17,0xEC,0xA7,0x13,0x7D,0x39,0xAB,0xC6,0x6E,0x3C,0x80,0xA8,0x92,0xFF,
                                       0x76,0x9D,0xE6,0x17,0x91,0xE5,0xAD,0xC4,0xB9,0xFF,0x85,0xA3,0x13,0x54,0x90,0x0B,
                                       0x20,0x28,0x71,0x27,0x9A,0x8C,0x49,0xDC,0x3F,0x22,0x0F,0x64,0x4C,0x57,0xA7,0xB1 };
        unsigned char std_deB[128] = { 0x94,0x73,0x6A,0xCD,0x2C,0x8C,0x87,0x96,0xCC,0x47,0x85,0xE9,0x38,0x30,0x1A,0x13,
                                       0x9A,0x05,0x9D,0x35,0x37,0xB6,0x41,0x41,0x40,0xB2,0xD3,0x1E,0xEC,0xF4,0x16,0x83,
                                       0x11,0x5B,0xAE,0x85,0xF5,0xD8,0xBC,0x6C,0x3D,0xBD,0x9E,0x53,0x42,0x97,0x9A,0xCC,
                                       0xCF,0x3C,0x2F,0x4F,0x28,0x42,0x0B,0x1C,0xB4,0xF8,0xC0,0xB5,0x9A,0x19,0xB1,0x58,
                                       0x7A,0xA5,0xE4,0x75,0x70,0xDA,0x76,0x00,0xCD,0x76,0x0A,0x0C,0xF7,0xBE,0xAF,0x71,
                                       0xC4,0x47,0xF3,0x84,0x47,0x53,0xFE,0x74,0xFA,0x7B,0xA9,0x2C,0xA7,0xD3,0xB5,0x5F,
                                       0x27,0x53,0x8A,0x62,0xE7,0xF7,0xBF,0xB5,0x1D,0xCE,0x08,0x70,0x47,0x96,0xD9,0x4C,
                                       0x9D,0x56,0x73,0x4F,0x11,0x9E,0xA4,0x47,0x32,0xB5,0x0E,0x31,0xCD,0xEB,0x75,0xC1 };
    
        unsigned char std_C_stream[116] = { 0x24,0x45,0x47,0x11,0x64,0x49,0x06,0x18,0xE1,0xEE,0x20,0x52,0x8F,0xF1,0xD5,0x45,
                                            0xB0,0xF1,0x4C,0x8B,0xCA,0xA4,0x45,0x44,0xF0,0x3D,0xAB,0x5D,0xAC,0x07,0xD8,0xFF,
                                            0x42,0xFF,0xCA,0x97,0xD5,0x7C,0xDD,0xC0,0x5E,0xA4,0x05,0xF2,0xE5,0x86,0xFE,0xB3,
                                            0xA6,0x93,0x07,0x15,0x53,0x2B,0x80,0x00,0x75,0x9F,0x13,0x05,0x9E,0xD5,0x9A,0xC0,
                                            0xBA,0x67,0x23,0x87,0xBC,0xD6,0xDE,0x50,0x16,0xA1,0x58,0xA5,0x2B,0xB2,0xE7,0xFC,
                                            0x42,0x91,0x97,0xBC,0xAB,0x70,0xB2,0x5A,0xFE,0xE3,0x7A,0x2B,0x9D,0xB9,0xF3,0x67,
                                            0x1B,0x5F,0x5B,0x0E,0x95,0x14,0x89,0x68,0x2F,0x3E,0x64,0xE1,0x37,0x8C,0xDD,0x5D,
                                            0xA9,0x51,0x3B,0x1C };
        unsigned char std_C_cipher[128] = { 0x24,0x45,0x47,0x11,0x64,0x49,0x06,0x18,0xE1,0xEE,0x20,0x52,0x8F,0xF1,0xD5,0x45,
                                            0xB0,0xF1,0x4C,0x8B,0xCA,0xA4,0x45,0x44,0xF0,0x3D,0xAB,0x5D,0xAC,0x07,0xD8,0xFF,
                                            0x42,0xFF,0xCA,0x97,0xD5,0x7C,0xDD,0xC0,0x5E,0xA4,0x05,0xF2,0xE5,0x86,0xFE,0xB3,
                                            0xA6,0x93,0x07,0x15,0x53,0x2B,0x80,0x00,0x75,0x9F,0x13,0x05,0x9E,0xD5,0x9A,0xC0,
                                            0xFD,0x3C,0x98,0xDD,0x92,0xC4,0x4C,0x68,0x33,0x26,0x75,0xA3,0x70,0xCC,0xEE,0xDE,
                                            0x31,0xE0,0xC5,0xCD,0x20,0x9C,0x25,0x76,0x01,0x14,0x9D,0x12,0xB3,0x94,0xA2,0xBE,
                                            0xE0,0x5B,0x6F,0xAC,0x6F,0x11,0xB9,0x65,0x26,0x8C,0x99,0x4F,0x00,0xDB,0xA7,0xA8,
                                            0xBB,0x00,0xFD,0x60,0x58,0x35,0x46,0xCB,0xDF,0x46,0x49,0x25,0x08,0x63,0xF1,0x0A };
    
        unsigned char *std_message = "Chinese IBE standard";
        unsigned char hid[] = { 0x03 };
        unsigned char *IDA = "Alice", *IDB = "Bob";
    
        unsigned char Ppub[64], deB[128];
        unsigned char message[1000], C[1000];
        int M_len, C_len;//M_len the length of message //C_len the length of C
        int k1_len = 16, k2_len = 32;
        int EncID = 0;//0,stream  //1 block
        int tmp, i;
        big ke;
    
        tmp = SM9_Init();//引數設定
        if (tmp != 0) return tmp;
    
        ke = mirvar(0);
        bytes_to_big(32, KE, ke);
    
        printf("\n***********************SM9 金鑰生成***************************\n");
        printf("使用者A的ID號為:%s\n使用者B的ID號為:%s\n",IDA,IDB);
        tmp = SM9_GenerateEncryptKey(hid, IDB, strlen(IDB), ke, Ppub, deB);
        if (tmp != 0)  return tmp;
        if (memcmp(Ppub, std_Ppub, 64) != 0)
            return SM9_GEPUB_ERR;
        if (memcmp(deB, std_deB, 128) != 0)
            return SM9_GEPRI_ERR;
    
        printf("\n***********************SM9 加密演演算法**************************\n");
        printf("要加密的明文訊息為:%s\n",std_message);
        begin = clock();
        tmp = SM9_Encrypt(hid, IDB, std_message, strlen(std_message), rand, EncID, k1_len, k2_len, Ppub, C, &C_len);
        end = clock();
        printf("\n\n\t\t\t加密時間: %f seconds\n", (double)(end - begin) / CLOCKS_PER_SEC);
    
        if (tmp != 0) return tmp;
        printf("\n******************************密文為:************************************\n");
        for (i = 0; i<C_len; i++) printf("%02x", C[i]);
        if (EncID == 0) tmp = memcmp(C, std_C_stream,C_len); else tmp=memcmp(C,std_C_cipher,C_len);
        if (tmp) return SM9_ENCRYPT_ERR;
    
        printf("\n\n");
        printf("\n**********************SM9 解密演演算法**************************\n");
        begin = clock();
        tmp = SM9_Decrypt(std_C_cipher, 128, deB, IDB, 2, k1_len, k2_len, message, &M_len);
        end = clock();
    
        printf("\n");
        message[M_len] = '\0';
        printf("明文:%s\n",message);
        if (tmp != 0)
            return tmp;
        if (memcmp(message, std_message, M_len) != 0)
            return SM9_DECRYPT_ERR;
        printf("\n\n\t\t\t解密時間: %f seconds\n", (double)(end - begin) / CLOCKS_PER_SEC);
        printf("\n");
        return 0;
    }
    

    數位簽章

    適用於接收者通過簽名者的標識驗證資料的完整性和資料傳送者的身份,也可以用於第三方確定簽名及所籤資料的真實性。

    (1)符號介紹:

    • \(cid\):曲線識別符號,表示使用曲線的類別
    • \(N\):曲線的素因子
    • \(k\):曲線\(E(F_q)\)相對於\(N\)的嵌入次數
    • \(P_1\)\(N\)階迴圈子群\(G_1\)的生成元
    • \(P_2\)\(N\)階迴圈子群\(G_2\)的生成元
    • \(eid\):雙線性對\(e()\)的識別符號,表示雙線性對類別
    • \(ID_A\):使用者標識
    • \(hid\):使用者私鑰生成函數識別符(一個位元組)

    (2)輔助函數

    • 雜湊函數:\(H_v()\),一般使用SM3,即生成256位的雜湊值
    • \(H1()\)函數:

    (3)演演算法流程:

    • 由KGC生成主私鑰和主公鑰,並且根據使用者標識生成使用者私鑰
    • 簽名者使用使用者私鑰對訊息簽名
    • 驗籤者使用使用者標識(相當於使用者公鑰)對簽名驗籤

    金鑰生成

    • 主私鑰\(ks\):KGC產生亂數\(ks\in [1,N=1]\),祕密保留主私鑰
    • 主公鑰\(P_{pub}\):KGC計算\(G_2\)中的元素\(P_{pub}=[ks]P_2\),公開主公鑰
    • 使用者私鑰\(d_{s_A}\):KGC選擇並公開\(hid\),首先在有限域\(F_N\)上計算\(t_1=H_1(ID_A || hid,N)+ks\),若\(t_1=0\)則需重新生成\(ks\),並重新計算其他金鑰,否則計算\(t_2=ks*t_1^{-1}\),最後計算出\(d_{s_A}=[t_2]P_1\)

    簽名

    下面是簽名流程圖:

    設訊息是位元串\(M\),簽名步驟如下:

    驗籤

    下面是驗籤流程圖:

    假設驗證者收到訊息和簽名\(\left (h',S' \right )\),簽名步驟如下:

    金鑰封裝

    • A使用者需要封裝長度為\(klen\)的金鑰給使用者B

    • 這裡的\(K\)是被封裝的金鑰,\(C\)是對\(K\)的加密,即祕密金鑰,目標使用者B需要從\(C\)中恢復出\(K\)
    • 封裝流程:

    • 其中雙線性對的計算結果\(g\)是一個數,不是橢圓曲線上的點
    • 有個疑問,為什麼需要傳送\(K\),目標使用者B最後就是為了得到\(K\),並且後面也沒有用到(對比)

    金鑰接封裝

    • 解封裝流程:

    • 其中\(w‘=e(C,de_B)\),可以看成用使用者私鑰\(de_B\)解密\(C\)

    程式實現

    /****************************************************************
      Function:       SM9_SelfCheck
      Description:    SM9 self check
      Calls:          MIRACL functions,SM9_Init(),SM9_GenerateEncryptKey(),SM9_Key_encap,
                      SM9_Key_decap
      Called By:
      Input:
      Output:
      Return:         0: self-check success
                      1: asking for memory error
                      2: element is out of order q
                      3: R-ate calculation error
                      4: test if C is on G1
                      5: base point P1 error
                      6: base point P2 error
                      7: Encryption public key generated error
                      8: Encryption private key generated error
                      9: K equals 0
                      A: cipher error in key encapsulation
                      B: key to be encapsulated
                      C: key generated by decapsulation
      Others:
    ****************************************************************/
    int SM9_SelfCheck()
    {
        //the master private key
        unsigned char KE[32] =
                {0x00,0x01,0xED,0xEE,0x37,0x78,0xF4,0x41,0xF8,0xDE,0xA3,0xD9,0xFA,0x0A,0xCC,0x4E,
                 0x07,0xEE,0x36,0xC9,0x3F,0x9A,0x08,0x61,0x8A,0xF4,0xAD,0x85,0xCE,0xDE,0x1C,0x22};
    
        unsigned char rand[32]={0x00,0x00,0x74,0x01,0x5F,0x84,0x89,0xC0,0x1E,0xF4,0x27,0x04,0x56,0xF9,0xE6,0x47,
                                0x5B,0xFB,0x60,0x2B,0xDE,0x7F,0x33,0xFD,0x48,0x2A,0xB4,0xE3,0x68,0x4A,0x67,0x22};
        //standard datas
        unsigned char std_Ppub[64]=
                {0x78,0x7E,0xD7,0xB8,0xA5,0x1F,0x3A,0xB8,0x4E,0x0A,0x66,0x00,0x3F,0x32,0xDA,0x5C,
                 0x72,0x0B,0x17,0xEC,0xA7,0x13,0x7D,0x39,0xAB,0xC6,0x6E,0x3C,0x80,0xA8,0x92,0xFF,
                 0x76,0x9D,0xE6,0x17,0x91,0xE5,0xAD,0xC4,0xB9,0xFF,0x85,0xA3,0x13,0x54,0x90,0x0B,
                 0x20,0x28,0x71,0x27,0x9A,0x8C,0x49,0xDC,0x3F,0x22,0x0F,0x64,0x4C,0x57,0xA7,0xB1};
        unsigned char std_deB[128]=
                {0x94,0x73,0x6A,0xCD,0x2C,0x8C,0x87,0x96,0xCC,0x47,0x85,0xE9,0x38,0x30,0x1A,0x13,
                 0x9A,0x05,0x9D,0x35,0x37,0xB6,0x41,0x41,0x40,0xB2,0xD3,0x1E,0xEC,0xF4,0x16,0x83,
                 0x11,0x5B,0xAE,0x85,0xF5,0xD8,0xBC,0x6C,0x3D,0xBD,0x9E,0x53,0x42,0x97,0x9A,0xCC,
                 0xCF,0x3C,0x2F,0x4F,0x28,0x42,0x0B,0x1C,0xB4,0xF8,0xC0,0xB5,0x9A,0x19,0xB1,0x58,
                 0x7A,0xA5,0xE4,0x75,0x70,0xDA,0x76,0x00,0xCD,0x76,0x0A,0x0C,0xF7,0xBE,0xAF,0x71,
                 0xC4,0x47,0xF3,0x84,0x47,0x53,0xFE,0x74,0xFA,0x7B,0xA9,0x2C,0xA7,0xD3,0xB5,0x5F,
                 0x27,0x53,0x8A,0x62,0xE7,0xF7,0xBF,0xB5,0x1D,0xCE,0x08,0x70,0x47,0x96,0xD9,0x4C,
                 0x9D,0x56,0x73,0x4F,0x11,0x9E,0xA4,0x47,0x32,0xB5,0x0E,0x31,0xCD,0xEB,0x75,0xC1};
        unsigned char std_K[64] =
                {0x4F,0xF5,0xCF,0x86,0xD2,0xAD,0x40,0xC8,0xF4,0xBA,0xC9,0x8D,0x76,0xAB,0xDB,0xDE,
                 0x0C,0x0E,0x2F,0x0A,0x82,0x9D,0x3F,0x91,0x1E,0xF5,0xB2,0xBC,0xE0,0x69,0x54,0x80};
        unsigned char std_C[64] =
                {0x1E,0xDE,0xE2,0xC3,0xF4,0x65,0x91,0x44,0x91,0xDE,0x44,0xCE,0xFB,0x2C,0xB4,0x34,
                 0xAB,0x02,0xC3,0x08,0xD9,0xDC,0x5E,0x20,0x67,0xB4,0xFE,0xD5,0xAA,0xAC,0x8A,0x0F,
                 0x1C,0x9B,0x4C,0x43,0x5E,0xCA,0x35,0xAB,0x83,0xBB,0x73,0x41,0x74,0xC0,0xF7,0x8F,
                 0xDE,0x81,0xA5,0x33,0x74,0xAF,0xF3,0xB3,0x60,0x2B,0xBC,0x5E,0x37,0xBE,0x9A,0x4C};
    
        unsigned char hid[]={0x03},*IDB="Bob";
        unsigned char Ppub[64],deB[128],C[64],K[32],K_decap[32];
        big ke;
        int tmp,i;
        int Klen=32;
    
        mip=mirsys(1000, 16);
        mip->IOBASE=16;
        ke=mirvar(0);
        bytes_to_big(32,KE,ke);
    
        tmp=SM9_Init();
        if(tmp!=0) return tmp;
    
        printf("\n***********************  SM9 金鑰產生    ***************************");
        tmp=SM9_GenerateEncryptKey(hid,IDB,strlen(IDB),ke,Ppub,deB);
        if(tmp!=0)  return tmp;
        if(memcmp(Ppub,std_Ppub,64)!=0)
            return SM9_GEPUB_ERR;
        if(memcmp(deB,std_deB,128)!=0)
            return SM9_GEPRI_ERR;
    
        printf("\n***********************  主加密私鑰 KE:    ***************************\n");
        for(i=0;i<32;i++){printf("%02x",KE[i]);}
        printf("\n***********************  主加密公鑰 Ppubs=[ke]P1:    ***************************\n");
        for(i=0;i<64;i++){if(i==32) printf("\n");printf("%02x",Ppub[i]);}
        printf("\n\t******使用者私鑰 deB = (xdeB, ydeB):*************\n");
        for(i=0;i<128;i++)
        {   if(i==64) printf("\n");
            printf("%02x",deB[i]);}
    
        printf("\n\n///////////////////金鑰封裝//////////////////////");
        tmp=SM9_Key_encap( hid,IDB,rand, Ppub, C, K,Klen);
        if(tmp!=0) return tmp;
    
        if(memcmp(C,std_C,64)!=0)
            return SM9_ERR_Encap_C;
        if(memcmp(K,std_K,Klen)!=0)
            return SM9_ERR_Encap_K;
    
        printf("\n\n///////////////////金鑰解封裝//////////////////////");
        tmp=SM9_Key_decap(IDB, deB, C,Klen,K_decap);
        if(tmp!=0) return tmp;
    
        if(memcmp(K_decap,std_K,32)!=0)
            return SM9_ERR_Decap_K;
        return 0;
    } 
    

    金鑰交換

    協定可以通過雙方的標識和私鑰經過兩次貨可選三次資訊傳遞,計算獲得一個由雙方共同決定的共用金鑰,該金鑰可作為對稱加密的繪畫金鑰。

    (1)基本概念

    • 這裡認為金鑰交換就是金鑰協商
    • 金鑰交換和簽名的不同:
      • 金鑰交換需要互動多次,且需要兩方的使用者標識
      • 金鑰交換需要KDF,即金鑰派生函數
    • KDF:作用於共用金鑰和雙方都知道的其他引數,產生一個或多個共用金鑰的金鑰,可以看成對(共用金鑰和共用引數)的雜湊
    • 雙方均能通過可選項實現金鑰確認

    (2)輔助函數

    ⚠️:\(H1(),H2()\)函數和簽名方案相同,多了一個KDF

    • \(H1()\)

    程式實現

    /****************************************************************
    Function:       SM9_SelfCheck
    Description:    SM9 self check
    Calls:          MIRACL functions,SM9_Init(),SM9_GenerateEncryptKey(),SM9_KeyEx_InitA_I,
    SM9_KeyEx_InitA_II,SM9_KeyEx_ReB_I,SM9_KeyEx_ReB_II
    Called By:
    Input:
    Output:
    Return:         0: self-check success
    1: asking for memory error
    2: element is out of order q
    3: R-ate calculation error
    4: test if C1 is on G1
    5: base point P1 error
    6: base point P2 error
    7: Encryption public key generated error
    8: Encryption private key generated error
    9: key exchange failed,form B to A,S1!=SB
    A: key exchange failed,form A to B,S2!=SA
    B: RA generated error
    C: RB generated error
    D: SA generated error
    E: SB generated error
    Others:
    ****************************************************************/
    int ARS_SM9_SelfCheck()
    {
        //the master private key
        unsigned char KE[32] = { 0x00,0x02,0xE6,0x5B,0x07,0x62,0xD0,0x42,0xF5,0x1F,0x0D,0x23,0x54,0x2B,0x13,0xED,
                                 0x8C,0xFA,0x2E,0x9A,0x0E,0x72,0x06,0x36,0x1E,0x01,0x3A,0x28,0x39,0x05,0xE3,0x1F };
    
        unsigned char randA[32] = { 0x00,0x00,0x58,0x79,0xDD,0x1D,0x51,0xE1,0x75,0x94,0x6F,0x23,0xB1,0xB4,0x1E,0x93,
                                    0xBA,0x31,0xC5,0x84,0xAE,0x59,0xA4,0x26,0xEC,0x10,0x46,0xA4,0xD0,0x3B,0x06,0xC8 };
        unsigned char randB[32] = { 0x00,0x01,0x8B,0x98,0xC4,0x4B,0xEF,0x9F,0x85,0x37,0xFB,0x7D,0x07,0x1B,0x2C,0x92,
                                    0x8B,0x3B,0xC6,0x5B,0xD3,0xD6,0x9E,0x1E,0xEE,0x21,0x35,0x64,0x90,0x56,0x34,0xFE };
        //standard datas
        unsigned char std_Ppub[64] = { 0x91,0x74,0x54,0x26,0x68,0xE8,0xF1,0x4A,0xB2,0x73,0xC0,0x94,0x5C,0x36,0x90,0xC6,
                                       0x6E,0x5D,0xD0,0x96,0x78,0xB8,0x6F,0x73,0x4C,0x43,0x50,0x56,0x7E,0xD0,0x62,0x83,
                                       0x54,0xE5,0x98,0xC6,0xBF,0x74,0x9A,0x3D,0xAC,0xC9,0xFF,0xFE,0xDD,0x9D,0xB6,0x86,
                                       0x6C,0x50,0x45,0x7C,0xFC,0x7A,0xA2,0xA4,0xAD,0x65,0xC3,0x16,0x8F,0xF7,0x42,0x10 };
        unsigned char std_deA[128] = { 0x0F,0xE8,0xEA,0xB3,0x95,0x19,0x9B,0x56,0xBF,0x1D,0x75,0xBD,0x2C,0xD6,0x10,0xB6,
                                       0x42,0x4F,0x08,0xD1,0x09,0x29,0x22,0xC5,0x88,0x2B,0x52,0xDC,0xD6,0xCA,0x83,0x2A,
                                       0x7D,0xA5,0x7B,0xC5,0x02,0x41,0xF9,0xE5,0xBF,0xDD,0xC0,0x75,0xDD,0x9D,0x32,0xC7,
                                       0x77,0x71,0x00,0xD7,0x36,0x91,0x6C,0xFC,0x16,0x5D,0x8D,0x36,0xE0,0x63,0x4C,0xD7,
                                       0x83,0xA4,0x57,0xDA,0xF5,0x2C,0xAD,0x46,0x4C,0x90,0x3B,0x26,0x06,0x2C,0xAF,0x93,
                                       0x7B,0xB4,0x0E,0x37,0xDA,0xDE,0xD9,0xED,0xA4,0x01,0x05,0x0E,0x49,0xC8,0xAD,0x0C,
                                       0x69,0x70,0x87,0x6B,0x9A,0xAD,0x1B,0x7A,0x50,0xBB,0x48,0x63,0xA1,0x1E,0x57,0x4A,
                                       0xF1,0xFE,0x3C,0x59,0x75,0x16,0x1D,0x73,0xDE,0x4C,0x3A,0xF6,0x21,0xFB,0x1E,0xFB };
        unsigned char std_deB[128] = { 0x74,0xCC,0xC3,0xAC,0x9C,0x38,0x3C,0x60,0xAF,0x08,0x39,0x72,0xB9,0x6D,0x05,0xC7,
                                       0x5F,0x12,0xC8,0x90,0x7D,0x12,0x8A,0x17,0xAD,0xAF,0xBA,0xB8,0xC5,0xA4,0xAC,0xF7,
                                       0x01,0x09,0x2F,0xF4,0xDE,0x89,0x36,0x26,0x70,0xC2,0x17,0x11,0xB6,0xDB,0xE5,0x2D,
                                       0xCD,0x5F,0x8E,0x40,0xC6,0x65,0x4B,0x3D,0xEC,0xE5,0x73,0xC2,0xAB,0x3D,0x29,0xB2,
                                       0x44,0xB0,0x29,0x4A,0xA0,0x42,0x90,0xE1,0x52,0x4F,0xF3,0xE3,0xDA,0x8C,0xFD,0x43,
                                       0x2B,0xB6,0x4D,0xE3,0xA8,0x04,0x0B,0x5B,0x88,0xD1,0xB5,0xFC,0x86,0xA4,0xEB,0xC1,
                                       0x8C,0xFC,0x48,0xFB,0x4F,0xF3,0x7F,0x1E,0x27,0x72,0x74,0x64,0xF3,0xC3,0x4E,0x21,
                                       0x53,0x86,0x1A,0xD0,0x8E,0x97,0x2D,0x16,0x25,0xFC,0x1A,0x7B,0xD1,0x8D,0x55,0x39 };
        unsigned char std_RA[64] = { 0x7C,0xBA,0x5B,0x19,0x06,0x9E,0xE6,0x6A,0xA7,0x9D,0x49,0x04,0x13,0xD1,0x18,0x46,
                                     0xB9,0xBA,0x76,0xDD,0x22,0x56,0x7F,0x80,0x9C,0xF2,0x3B,0x6D,0x96,0x4B,0xB2,0x65,
                                     0xA9,0x76,0x0C,0x99,0xCB,0x6F,0x70,0x63,0x43,0xFE,0xD0,0x56,0x37,0x08,0x58,0x64,
                                     0x95,0x8D,0x6C,0x90,0x90,0x2A,0xBA,0x7D,0x40,0x5F,0xBE,0xDF,0x7B,0x78,0x15,0x99 };
        unsigned char std_RB[64] = { 0x86,0x1E,0x91,0x48,0x5F,0xB7,0x62,0x3D,0x27,0x94,0xF4,0x95,0x03,0x1A,0x35,0x59,
                                     0x8B,0x49,0x3B,0xD4,0x5B,0xE3,0x78,0x13,0xAB,0xC7,0x10,0xFC,0xC1,0xF3,0x44,0x82,
                                     0x32,0xD9,0x06,0xA4,0x69,0xEB,0xC1,0x21,0x6A,0x80,0x2A,0x70,0x52,0xD5,0x61,0x7C,
                                     0xD4,0x30,0xFB,0x56,0xFB,0xA7,0x29,0xD4,0x1D,0x9B,0xD6,0x68,0xE9,0xEB,0x96,0x00 };
        unsigned char std_SA[32] = { 0x19,0x5D,0x1B,0x72,0x56,0xBA,0x7E,0x0E,0x67,0xC7,0x12,0x02,0xA2,0x5F,0x8C,0x94,
                                     0xFF,0x82,0x41,0x70,0x2C,0x2F,0x55,0xD6,0x13,0xAE,0x1C,0x6B,0x98,0x21,0x51,0x72 };
        unsigned char std_SB[32] = { 0x3B,0xB4,0xBC,0xEE,0x81,0x39,0xC9,0x60,0xB4,0xD6,0x56,0x6D,0xB1,0xE0,0xD5,0xF0,
                                     0xB2,0x76,0x76,0x80,0xE5,0xE1,0xBF,0x93,0x41,0x03,0xE6,0xC6,0x6E,0x40,0xFF,0xEE };
    
        unsigned char hid[] = { 0x02 }, *IDA = "Alice", *IDB = "Bob";
        unsigned char Ppub[64], deA[128], deB[128];
        unsigned char xy[64], SA[SM3_len / 8], SB[SM3_len / 8];
        epoint *RA, *RB;
        big ke, x, y;
        zzn12 g1, g2, g3;
        int tmp, i;
    
        mip = mirsys(1000, 16);
        mip->IOBASE = 16;
    
        x = mirvar(0); y = mirvar(0); ke = mirvar(0);
        bytes_to_big(32, KE, ke);//主私鑰,即亂數,這裡事先定義好
        RA = epoint_init(); RB = epoint_init();
        zzn12_init(&g1); zzn12_init(&g2); zzn12_init(&g3);
    
        //初始化,設定引數和曲線
        tmp = ARS_SM9_Init();
        if (tmp != 0) return tmp;
        printf("\n使用者A的ID號為:%s\n使用者B的ID號為:%s\n",IDA,IDB);
    
        printf("\n*********************** SM9 金鑰生成 ***************************");
        tmp = ARS_SM9_GenerateEncryptKey(hid, IDA, strlen(IDA), ke, Ppub, deA);//Alice的使用者私鑰deA生成
        if (tmp != 0)  return tmp;
    
        tmp = ARS_SM9_GenerateEncryptKey(hid, IDB, strlen(IDB), ke, Ppub, deB);//Bob的使用者私鑰deB生成
        if (tmp != 0)  return tmp;
    
        if (memcmp(Ppub, std_Ppub, 64) != 0)
            return SM9_GEPUB_ERR;
    
        if (memcmp(deA, std_deA, 128) != 0)
            return SM9_GEPRI_ERR;
        if (memcmp(deB, std_deB, 128) != 0)
            return SM9_GEPRI_ERR;
    
        printf("\n\n**********************主私鑰 KE:*************************\n");
        for (i = 0; i<32; i++) printf("%02x", KE[i]);
        printf("\n\n**********************主公鑰 Ppubs=[ke]P1:*************************\n");
        for (i = 0; i<64; i++) printf("%02x", Ppub[i]);
        printf("\n\n**************使用者A私鑰 deA = (xdeA, ydeA):*********************\n");
        for (i = 0; i<128; i++) printf("%02x", deA[i]);
        printf("\n\n**************使用者B私鑰 deB = (xdeB, ydeB):*********************\n");
        for (i = 0; i<128; i++) printf("%02x", deB[i]);
    
        printf("\n");
        printf("\n*********************** SM9 金鑰交換 ***************************\n");
    
        printf("\n//////////////////// SM9 金鑰交換 A1-A4://////////////////////////\n");
        tmp = ARS_SM9_KeyEx_InitA_I(hid, IDB, randA, Ppub, deA, RA);//計算RA
        if (tmp != 0) return tmp;
    
        printf("\n//////////////////////////// RA=[r]QB //////////////////////////////\n");
        epoint_get(RA, x, y);
        cotnum(x, stdout); cotnum(y, stdout);
        big_to_bytes(BNLEN, x, xy, 1); big_to_bytes(BNLEN, y, xy + BNLEN, 1);
        if (memcmp(xy, std_RA, BNLEN * 2) != 0)
            return SM9_ERR_RA;
    
        printf("\n//////////////////////// SM9 金鑰交換 B1-B7:///////////////////////\n");
        tmp = ARS_SM9_KeyEx_ReB_I(hid, IDA, IDB, randB, Ppub, deB, RA, RB, SB, &g1, &g2, &g3);
        if (tmp != 0) return tmp;
        epoint_get(RB, x, y);
        big_to_bytes(BNLEN, x, xy, 1); big_to_bytes(BNLEN, y, xy + BNLEN, 1);
        if (memcmp(xy, std_RB, BNLEN * 2) != 0)
            return SM9_ERR_RB;
        if (memcmp(SB, std_SB, SM3_len / 8) != 0)
            return SM9_ERR_SB;
    
        printf("\n");
        printf("\n//////////////////////// SM9 金鑰交換 A5-A8:///////////////////////");
        tmp = ARS_SM9_KeyEx_InitA_II(IDA, IDB, randA, Ppub, deA, RA, RB, SB, SA);
        if (tmp != 0) return tmp;
        if (memcmp(SA, std_SA, SM3_len / 8) != 0)
            return SM9_ERR_SA;
    
        printf("\n");
        printf("\n//////////////////////// SM9 金鑰交換 B8:///////////////////////");
        tmp = ARS_SM9_KeyEx_ReB_II(IDA, IDB, g1, g2, g3, RA, RB, SA);
        if (tmp != 0) return tmp;
    
        printf("\n");
        return 0;
    }