国密SM3密码杂凑算法原理及实现(附源码)
相关文章:
- 国密SM3哈希算法原理及实现(附源码)
- SHA1哈希算法原理及实现(附源码)
- MD5哈希算法原理及实现(附源码)
- MD4哈希算法原理及实现(附源码)
- MD2哈希算法原理及实现(附源码)
- MD2中用于随机置换的S盒是如何生成的?
最近陆续造了一批哈希算法的轮子,包括MD家族(包括MD2/MD4/MD5), SHA1, SHA2家族(SHA256/SHA384/SHA512),SHA3家族以及国密SM3算法。
原来打算将每一个算法都详细分析并实现,现在看来,这个工作短时间可能无法完成,所以先将源码发上来。
这部分实现的源码完全参考官方文档的算法描述,连变量名也尽可能和官方文档中的变量保持一致,方便学习。
另外, 代码封装的SM3哈希调用接口参考了openssl官方的接口,完全兼容,无缝对接。会使用这里的接口,就会使用openssl的库函数接口,甚至连代码都不需要修改。
除了实现的源码外,还另外附带了一个测试例子,这个测试例子不仅仅是用于测试哈希算法的实现是否正确,还可以提供了"-f"/"-s"等选项用于对任意文件和字符串进行哈希,因此作为一个工具使用,类似系统内置的md5sum/sha1sum。
SM3实现源码
1. 头文件sm3.c
/** @ file: sm3.h* @ description: header file for sm3.c* @ author: Gu Yongqiang* @ blog: https://blog.csdn.net/guyongqiangx*/
#ifndef __ROCKY_SM3__H
#define __ROCKY_SM3__H#define ERR_OK 0
#define ERR_ERR -1 /* generic error */
#define ERR_INV_PARAM -2 /* invalid parameter */
#define ERR_TOO_LONG -3 /* too long */
#define ERR_STATE_ERR -4 /* state error */typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef unsigned long long uint64_t;typedef struct sm3_context {/* message total length in bytes */uint64_t total;/* intermedia hash value for each block */struct {uint32_t a;uint32_t b;uint32_t c;uint32_t d;uint32_t e;uint32_t f;uint32_t g;uint32_t h;}hash;/* last block */struct {uint32_t used; /* used bytes */uint8_t buf[64]; /* block data buffer */}last;
}SM3_CTX;/* https://www.openssl.org/docs/man1.1.1/man3/SHA256_Final.html */int SM3_Init(SM3_CTX *c);
int SM3_Update(SM3_CTX *c, const void *data, size_t len);
int SM3_Final(unsigned char *md, SM3_CTX *c);
unsigned char *SM3(const unsigned char *d, size_t n, unsigned char *md);
#endif
2. 代码文件sm3.c
/** @ file: sm3.c* @ description: implementation for the SM3 Cryptographic Hash Algorithm* @ author: Gu Yongqiang* @ blog: https://blog.csdn.net/guyongqiangx*/
#include <stdio.h>
#include <string.h>#include "utils.h"
#include "sm3.h"// #define DEBUG#ifdef DEBUG
#define DBG(...) printf(__VA_ARGS__)
#define DUMP_SCHED_DATA 1
#define DUMP_BLOCK_DATA 1
#define DUMP_BLOCK_HASH 1
#define DUMP_ROUND_DATA 1
#else
#define DBG(...)
#define DUMP_SCHED_DATA 0
#define DUMP_BLOCK_DATA 0
#define DUMP_BLOCK_HASH 0
#define DUMP_ROUND_DATA 0
#endif#define HASH_BLOCK_SIZE 64 /* 512 bits = 64 Bytes */
#define HASH_LEN_SIZE 8 /* 64 bits = 8 bytes */
#define HASH_LEN_OFFSET (HASH_BLOCK_SIZE - HASH_LEN_SIZE)#define HASH_DIGEST_SIZE 32 /* 256 bits = 32 bytes */#define HASH_PADDING_PATTERN 0x80
#define HASH_ROUND_NUM 64/* SM3 Constants */
static uint32_t T[2] =
{0x79CC4519, 0x7A879D8A
};/* ROTate Left (circular left shift) */
static uint32_t ROTL(uint32_t x, uint8_t shift)
{shift %= 32;return (x << shift) | (x >> (32 - shift));
}static uint32_t FF(uint32_t x, uint32_t y, uint32_t z, uint32_t j)
{if (j<16) /* 0 <= j <= 15 */{return x ^ y ^ z;}else /* 16 <= j <= 63 */{return (x & y) | (x & z) | (y & z);}
}static uint32_t GG(uint32_t x, uint32_t y, uint32_t z, uint32_t j)
{if (j<16) /* 0 <= j <= 15 */{return x ^ y ^ z;}else /* 16 <= j <= 63 */{return (x & y) | (~x & z);}
}/* P0, Permutation 0 */
static uint32_t P0(uint32_t x)
{return x ^ ROTL(x, 9) ^ ROTL(x, 17);
}/* P1, Permutation 1 */
static uint32_t P1(uint32_t x)
{return x ^ ROTL(x, 15) ^ ROTL(x, 23);
}int SM3_Init(SM3_CTX *c)
{if (NULL == c){return ERR_INV_PARAM;}memset(c, 0, sizeof(SM3_CTX));/* Initial Value for SM3 */c->hash.a = 0x7380166f;c->hash.b = 0x4914b2b9;c->hash.c = 0x172442d7;c->hash.d = 0xda8a0600;c->hash.e = 0xa96f30bc;c->hash.f = 0x163138aa;c->hash.g = 0xe38dee4d;c->hash.h = 0xb0fb0e4e;return ERR_OK;
}static int SM3_PrepareScheduleWord(const uint32_t *block, uint32_t *W, uint32_t *Wp)
{uint32_t j;if ((NULL == block) || (NULL == W) || (NULL == Wp)){return ERR_INV_PARAM;}/* Array W */for (j=0; j<(HASH_ROUND_NUM+4); j++){if (j<=15) /* 0 <= j <= 15 */W[j] = be32toh(block[j]);else /* 16 <= j <= 67 */W[j] = P1(W[j-16] ^ W[j-9] ^ ROTL(W[j-3],15)) ^ ROTL(W[j-13],7) ^ W[j-6];}/* Array W Prime */for (j=0; j<HASH_ROUND_NUM; j++){Wp[j] = W[j] ^ W[j+4];}#if (DUMP_SCHED_DATA == 1)printf(" W1...W67:\n");for (j=0; j<(HASH_ROUND_NUM+4); j++){if (j%8 == 0) /* line indent */{printf(" ");}printf("%08x ", W[j]);if (j%8 == 7){printf("\n");}else if (j == (HASH_ROUND_NUM+4-1)){printf("\n"); /* last one */}}printf(" W'1...W'63:\n");for (j=0; j<HASH_ROUND_NUM; j++){if (j%8 == 0) /* line indent */{printf(" ");}printf("%08x ", Wp[j]);if (j%8 == 7){printf("\n");}else if (j == HASH_ROUND_NUM-1){printf("\n"); /* last one */}}
#endifreturn ERR_OK;
}static int SM3_ProcessBlock(SM3_CTX *ctx, const void *block)
{uint32_t j;uint32_t W[HASH_ROUND_NUM+4], Wp[HASH_ROUND_NUM];uint32_t SS1, SS2;uint32_t TT1, TT2;uint32_t A, B, C, D, E, F, G, H;if ((NULL == ctx) || (NULL == block)){return ERR_INV_PARAM;}#if (DUMP_BLOCK_DATA == 1)DBG("---------------------------------------------------------\n");DBG(" BLOCK: %llu\n", ctx->total/HASH_BLOCK_SIZE);DBG(" DATA:\n");print_buffer(block, HASH_BLOCK_SIZE, " ");
#endif/* prepare schedule word */SM3_PrepareScheduleWord(block, W, Wp);A = ctx->hash.a;B = ctx->hash.b;C = ctx->hash.c;D = ctx->hash.d;E = ctx->hash.e;F = ctx->hash.f;G = ctx->hash.g;H = ctx->hash.h;#if (DUMP_BLOCK_HASH == 1)DBG(" IV: %08x %08x %08x %08x %08x %08x %08x %08x\n",ctx->hash.a, ctx->hash.b, ctx->hash.c, ctx->hash.d, ctx->hash.e, ctx->hash.f, ctx->hash.g, ctx->hash.h);
#endiffor (j=0; j<HASH_ROUND_NUM; j++){SS1 = ROTL(ROTL(A, 12) + E + ROTL(T[j<16?0:1], j), 7);SS2 = SS1 ^ ROTL(A, 12);TT1 = FF(A, B, C, j) + D + SS2 + Wp[j];TT2 = GG(E, F, G, j) + H + SS1 + W[j];D = C;C = ROTL(B, 9);B = A;A = TT1;H = G;G = ROTL(F, 19);F = E;E = P0(TT2);#if (DUMP_ROUND_DATA == 1)
#if 1 /* Don't show temp variables: SS1/SS2/TT1/TT2/W/W' */DBG(" %02d: A=0x%08x, B=0x%08x, C=0x%08x, D=0x%08x, E=0x%08x, F=0x%08x, G=0x%08x, H=0x%08x\n", \j, A, B, C, D, E, F, G, H);
#elseDBG(" %02d: SS1=0x%08x, SS2=0x%08x, TT1=0x%08x, TT2=0x%08x, W=0x%08x, Wp=0x%08x\n"\" A=0x%08x, B=0x%08x, C=0x%08x, D=0x%08x, E=0x%08x, F=0x%08x, G=0x%08x, H=0x%08x\n", \j, SS1, SS2, TT1, TT2, W[j], Wp[j], A, B, C, D, E, F, G, H);
#endif
#endif}ctx->hash.a ^= A;ctx->hash.b ^= B;ctx->hash.c ^= C;ctx->hash.d ^= D;ctx->hash.e ^= E;ctx->hash.f ^= F;ctx->hash.g ^= G;ctx->hash.h ^= H;#if (DUMP_BLOCK_HASH == 1)DBG(" HASH: %08x %08x %08x %08x %08x %08x %08x %08x\n",ctx->hash.a, ctx->hash.b, ctx->hash.c, ctx->hash.d, ctx->hash.e, ctx->hash.f, ctx->hash.g, ctx->hash.h);
#endifreturn ERR_OK;
}int SM3_Update(SM3_CTX *c, const void *data, size_t len)
{uint32_t copy_len = 0;if ((NULL == c) || (NULL == data)){return ERR_INV_PARAM;}/* has used data */if (c->last.used != 0){/* less than 1 block in total, combine data */if (c->last.used + len < HASH_BLOCK_SIZE){memcpy(&c->last.buf[c->last.used], data, len);c->last.used += len;return ERR_OK;}else /* more than 1 block */{/* process the block in context buffer */copy_len = HASH_BLOCK_SIZE - c->last.used;memcpy(&c->last.buf[c->last.used], data, copy_len);SM3_ProcessBlock(c, &c->last.buf);c->total += HASH_BLOCK_SIZE;data = (uint8_t *)data + copy_len;len -= copy_len;/* reset context buffer */memset(&c->last.buf[0], 0, HASH_BLOCK_SIZE);c->last.used = 0;}}/* less than 1 block, copy to context buffer */if (len < HASH_BLOCK_SIZE){memcpy(&c->last.buf[c->last.used], data, len);c->last.used += len;return ERR_OK;}else{/* process data blocks */while (len >= HASH_BLOCK_SIZE){SM3_ProcessBlock(c, data);c->total += HASH_BLOCK_SIZE;data = (uint8_t *)data + HASH_BLOCK_SIZE;len -= HASH_BLOCK_SIZE;}/* copy rest data to context buffer */memcpy(&c->last.buf[0], data, len);c->last.used = len;}return ERR_OK;
}int SM3_Final(unsigned char *md, SM3_CTX *c)
{uint32_t *temp;//uint64_t *buf;if ((NULL == c) || (NULL == md)){return ERR_INV_PARAM;}/* Last block should be less thant HASH_BLOCK_SIZE - HASH_LEN_SIZE */if (c->last.used >= (HASH_BLOCK_SIZE - HASH_LEN_SIZE)){c->total += c->last.used;/* one more block */c->last.buf[c->last.used] = HASH_PADDING_PATTERN;c->last.used++;memset(&c->last.buf[c->last.used], 0, HASH_BLOCK_SIZE - c->last.used);SM3_ProcessBlock(c, &c->last.buf);memset(&c->last.buf[0], 0, HASH_BLOCK_SIZE - HASH_LEN_SIZE);c->last.used = 0;/* save length *///buf = (uint64_t *)&(c->last.buf[HASH_LEN_OFFSET]);//*buf = htobe64(c->total << 3);temp = (uint32_t *)&(c->last.buf[HASH_LEN_OFFSET]);temp[0] = htobe32((c->total << 3) >> 32 & 0xFFFFFFFF);temp[1] = htobe32((c->total << 3) & 0xFFFFFFFF);SM3_ProcessBlock(c, &c->last.buf);}else /* 0 <= last.used < HASH_BLOCK_SIZE - HASH_LEN_SIZE */{c->total += c->last.used;/* one more block */c->last.buf[c->last.used] = HASH_PADDING_PATTERN;c->last.used++;/* padding 0s */memset(&c->last.buf[c->last.used], 0, HASH_BLOCK_SIZE - HASH_LEN_SIZE - c->last.used);/* save length *///buf = (uint64_t *)&(c->last.buf[HASH_LEN_OFFSET]);//*buf = htobe64(c->total << 3);temp = (uint32_t *)&(c->last.buf[HASH_LEN_OFFSET]);temp[0] = htobe32((c->total << 3) >> 32 & 0xFFFFFFFF);temp[1] = htobe32((c->total << 3) & 0xFFFFFFFF);SM3_ProcessBlock(c, &c->last.buf);}temp = (uint32_t *)md;temp[0] = htobe32(c->hash.a);temp[1] = htobe32(c->hash.b);temp[2] = htobe32(c->hash.c);temp[3] = htobe32(c->hash.d);temp[4] = htobe32(c->hash.e);temp[5] = htobe32(c->hash.f);temp[6] = htobe32(c->hash.g);temp[7] = htobe32(c->hash.h);return ERR_OK;
}unsigned char *SM3(const unsigned char *d, size_t n, unsigned char *md)
{SM3_CTX c;if ((NULL == d) || (NULL == md)){return NULL;}SM3_Init(&c);SM3_Update(&c, d, n);SM3_Final(md, &c);return md;
}
SM3源码的编译和测试
我直接在Makefile中内置了一个test伪目标,编译时除了编译生成名为sm3的哈希工具外,还会直接调用内置的哈希测试。
编译和运行如下:
$ make
gcc -Wall -g -O2 -c utils.c -o utils.o
gcc -Wall -g -O2 -c sm3.c -o sm3.o
gcc -Wall -g -O2 -c sm3test.c -o sm3test.o
gcc -Wall -g -O2 utils.o sm3.o sm3test.o -o sm3Run Test...
./sm3 -x
Internal hash tests for ./sm3:
./sm3("")Expect: 1ab21d8355cfa17f8e61194831e81a8f22bec8c728fefb747ed035eb5082aa2bResult: 1ab21d8355cfa17f8e61194831e81a8f22bec8c728fefb747ed035eb5082aa2b./sm3("a")Expect: 623476ac18f65a2909e43c7fec61b49c7e764a91a18ccb82f1917a29c86c5e88Result: 623476ac18f65a2909e43c7fec61b49c7e764a91a18ccb82f1917a29c86c5e88./sm3("abc")Expect: 66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0Result: 66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0./sm3("message digest")Expect: c522a942e89bd80d97dd666e7a5531b36188c9817149e9b258dfe51ece98ed77Result: c522a942e89bd80d97dd666e7a5531b36188c9817149e9b258dfe51ece98ed77./sm3("abcdefghijklmnopqrstuvwxyz")Expect: b80fe97a4da24afc277564f66a359ef440462ad28dcc6d63adb24d5c20a61595Result: b80fe97a4da24afc277564f66a359ef440462ad28dcc6d63adb24d5c20a61595./sm3("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")Expect: 2971d10c8842b70c979e55063480c50bacffd90e98e2e60d2512ab8abfdfcec5Result: 2971d10c8842b70c979e55063480c50bacffd90e98e2e60d2512ab8abfdfcec5./sm3("12345678901234567890123456789012345678901234567890123456789012345678901234567890")Expect: ad81805321f3e69d251235bf886a564844873b56dd7dde400f055b7dde39307aResult: ad81805321f3e69d251235bf886a564844873b56dd7dde400f055b7dde39307a./sm3("abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd")Expect: debe9ff92275b8a138604889c18e5a4d6fdb70e5387e5765293dcba39c0c5732Result: debe9ff92275b8a138604889c18e5a4d6fdb70e5387e5765293dcba39c0c5732
最新版本的openssl工具已经支持sm3哈希算法,因此可以将sm3工具和openssl执行dgst计算的结果进行比较:
$ sm3 -h
Usage:
Common options: [-x|-f file|-s string|-h]
Hash a string:sm3 -s string
Hash a file:sm3 -f file [-k key]
-x Internal string hash test
-h Display this message# 使用"-f"和"-s"选项分别对文件和字符串计算sm3哈希值
$ sm3 -f sm3.o
sm3(sm3.o) = 347d091d1269da1e00166b3dec34885b55b011ddd06d989f092f73c9488fcac7
$ sm3 -s "I Love China!"
sm3("I Love China!") = c661494fb7e8f3c7fe9c1926d69961fb1a3ccdc2a1c8cdd817fe0b7f777cea5a# 使用开源的openssl工具计算相应的哈希进行对比
$ openssl dgst -sm3 sm3.o
SHA1(sha1.o)= 347d091d1269da1e00166b3dec34885b55b011ddd06d989f092f73c9488fcac7
$ echo -n "I Love China!" | openssl dgst -sm3
(stdin)= c661494fb7e8f3c7fe9c1926d69961fb1a3ccdc2a1c8cdd817fe0b7f777cea5a
完整代码
完整的代码文件列表如下:
sm3$ ls -lh
total 140K
-rwxr--r-- 1 rocky rocky 592 Jun 19 23:14 Makefile
-rwxrwxr-x 1 rocky rocky 11K Jun 20 00:04 sm3.c
-rwxrwxr-x 1 rocky rocky 1.4K Jun 19 23:19 sm3.h
-rwxr--r-- 1 rocky rocky 6.0K Jun 19 23:35 sm3test.c
-rwxr--r-- 1 rocky rocky 578 Jun 17 21:46 utils.c
-rwxr--r-- 1 rocky rocky 1.7K Jun 17 21:46 utils.h
-rwxr--r-- 1 rocky rocky 102K Jun 19 23:30 国密-SM3密码杂凑算法.pdf
需要代码请访问:
- https://github.com/guyongqiangx/cryptography/
其它
洛奇工作中常常会遇到自己不熟悉的问题,这些问题可能并不难,但因为不了解,找不到人帮忙而瞎折腾,往往导致浪费几天甚至更久的时间。
所以我组建了几个微信讨论群(记得微信我说加哪个群,如何加微信见后面),欢迎一起讨论:
- 一个密码编码学讨论组,主要讨论各种加解密,签名校验等算法,请说明加密码学讨论群。
- 一个Android OTA的讨论组,请说明加Android OTA群。
- 一个git和repo的讨论组,请说明加git和repo群。
在工作之余,洛奇尽量写一些对大家有用的东西,如果洛奇的这篇文章让您有所收获,解决了您一直以来未能解决的问题,不妨赞赏一下洛奇,这也是对洛奇付出的最大鼓励。扫下面的二维码赞赏洛奇,金额随意:
洛奇自己维护了一个公众号“洛奇看世界”,一个很佛系的公众号,不定期瞎逼逼。公号也提供个人联系方式,一些资源,说不定会有意外的收获,详细内容见公号提示。扫下方二维码关注公众号:
国密SM3密码杂凑算法原理及实现(附源码)相关推荐
- 原味的SM3密码杂凑算法
根据国家密码管理局官网发布的规范文档里的算法描述,对SM3密码杂凑算法进行了原汁原味的实现.代码里的函数.变量名称都尽量使用算法描述中的名称,尽量遵循算法描述的原始步骤,不使用算法技巧进行处理. 算法 ...
- SHA224和SHA256哈希算法原理及实现(附源码)
相关文章: SHA224和SHA256哈希算法原理及实现(附源码) 国密SM3哈希算法原理及实现(附源码) SHA1哈希算法原理及实现(附源码) MD5哈希算法原理及实现(附源码) MD4哈希算法原理 ...
- SHA3系列(KECCAK)哈希算法原理及实现(附源码)
相关文章: (本文持续更新中) SHA3系列(KECCAK)哈希算法原理及实现(附源码) SHA512系列哈希算法原理及实现(附源码) SHA224和SHA256哈希算法原理及实现(附源码) 国密SM ...
- SHA512系列哈希算法原理及实现(附源码)
相关文章: SHA512系列哈希算法原理及实现(附源码) SHA224和SHA256哈希算法原理及实现(附源码) 国密SM3哈希算法原理及实现(附源码) SHA1哈希算法原理及实现(附源码) MD5哈 ...
- SM3密码杂凑算法源码解析
1.在SM3算法源文件中主要有以下几个函数: void sm3_starts( sm3_context *ctx ); void sm3_update( sm3_context *ctx, unsig ...
- android杂凑算法,SM3密码杂凑算法分析
SM3密码杂凑算法分析 杂凑函数在密码学中具有重要的地位,被广泛应用在数字签名,消息认证,数据完整性检测等领域.杂凑函数通常被认为需要满足三个基本特性:碰撞稳固性,原根稳固性和第二原根稳固性.2005 ...
- linux直流电机测试,带霍尔传感器编码器的直流减速电机测速原理讲解(附源码)...
查看: 14294|回复: 83 带霍尔传感器编码器的直流减速电机测速原理讲解(附源码) 高级会员, 积分 891, 距离下一级还需 109 积分 积分金钱891 注册时间2019-4-22 在线时间 ...
- 超详讲解图像拼接/全景图原理和应用 | 附源码
点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 概述 图像拼接是计算机视觉中最成功的应用之一.如今,很难找到不包含 ...
- python全景图像拼接_超详讲解图像拼接/全景图原理和应用 | 附源码
研究好玩又有用的技术第 008 期 在学习中发现快乐,在应用找到价值.这是我第八期分享图像技术应用的文章. 前七期欢迎阅读和分享: 概述 作者:Thalles Silva 编译:AI算法与图像处理 图 ...
最新文章
- Ubuntu 系统 在终端中过滤log 特殊的信息
- 设计模式之_Iterator_03
- ASP.NET MVC中同步与异步
- 【Python】PyCharm中Matplotlib绘图不能显示UI效果的问题解决
- linux设置windows共享为yum源
- 优秀的云存储解决方案Dropbox,现在注册就有2G
- “编程能力差,90%会输在这点上!”谷歌开发:方法不对,努力也白费
- Luogu3387【模板】缩点(Kosaraju)
- Hadoop权威指南
- ColorUI开发手册(适用于后端同学使用)
- R语言 软件安装 源码安装R
- 天堂2单机版进去显示服务器维护,天堂2五章单机版设置完成后却玩不了 高手解决一下...
- python numpy.linalg.norm函数的用法
- springmvc GET POST 接受参数方式
- 可重入锁 ReentrantLock
- 今天开通个人博客,值得祝贺!
- 黑客利用0day,从General Bytes比特币ATM盗走150万美元
- Docker Swarm学习教程【转载】
- 按首字母升序输出国家名称
- 安天每日安全简讯20160712
热门文章
- C# WinForm UI 触摸屏按下和抬起事件处理方法
- 基于51单片机的蓝牙控制小车的简单实现(有源代码,无图) (上篇)
- 光影mod_效果不俗!《雷神之锤2》贴图/光影重制MOD新截图公布
- 远程连接工具putty和winscp
- 自动登录Github官网,cookie验证简单模拟登录
- Unity中EditorWindow的创建和停靠
- SQL中纵表、横表互相转换
- Altium Designer入门实战教程-从原理图到印制电路板
- vue报错Error in render: TypeError: Cannot read property 'name' of undefined
- STM32 TIM定时器的使用(2)——输入捕获