这部分实现的源码完全参考官方文档的算法描述,连变量名也尽可能和官方文档中的变量保持一致,方便学习。
另外, 代码封装的SM3哈希调用接口参考了openssl官方的接口,完全兼容,无缝对接。会使用这里的接口,就会使用openssl的库函数接口,甚至连代码都不需要修改。
除了实现的源码外,还另外附带了一个测试例子,这个测试例子不仅仅是用于测试哈希算法的实现是否正确,还可以提供了”-f”/”-s”等选项用于对任意文件和字符串进行哈希,因此作为一个工具使用,类似系统内置的md5sum/sha1sum。
头文件sm3.h
/* * @ 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
代码文件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 */ } } #endif return 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); #endif for (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); #else DBG(" %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); #endif return 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; }
本文出自:https://blog.csdn.net/guyongqiangx/article/details/118061218