相关文章:

  • SHA1哈希算法原理及实现(附源码)
  • MD5哈希算法原理及实现(附源码)
  • MD4哈希算法原理及实现(附源码)
  • MD2哈希算法原理及实现(附源码)
  • MD2中用于随机置换的S盒是如何生成的?

最近陆续造了一批哈希算法的轮子,包括MD家族(包括MD2/MD4/MD5), SHA1, SHA2家族(SHA256/SHA384/SHA512),SHA3家族以及国密SM3算法。
原来打算将每一个算法都详细分析并实现,现在看来,这个工作短时间可能无法完成,所以先将源码发上来。

这部分实现的源码完全参考官方文档的算法描述,连变量名也尽可能和官方文档中的变量保持一致,方便学习。

另外, 代码封装的MD4哈希调用接口参考了openssl官方的接口,完全兼容,无缝对接。会使用这里的接口,就会使用openssl的库函数接口,甚至连代码都不需要修改。

除了实现的源码外,还另外附带了一个测试例子,这个测试例子不仅仅是用于测试哈希算法的实现是否正确,还可以提供了"-f"/"-s"等选项用于对任意文件和字符串进行哈希,因此作为一个工具使用,类似系统内置的md5sum/sha1sum。

MD4实现源码

1. 头文件md4.c

/** @        file: md4.h* @ description: header file for md4.c* @      author: Gu Yongqiang* @        blog: https://blog.csdn.net/guyongqiangx*/
#ifndef __ROCKY_MD4__H
#define __ROCKY_MD4__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 md4_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;}hash;/* last block */struct {uint32_t used;     /* used bytes */uint8_t  buf[64];  /* block data buffer */}last;
}MD4_CTX;/* https://www.openssl.org/docs/man1.1.0/man3/MD5_Init.html */
int MD4_Init(MD4_CTX *c);
int MD4_Update(MD4_CTX *c, const void *data, unsigned long len);
int MD4_Final(unsigned char *md, MD4_CTX *c);
unsigned char *MD4(const unsigned char *d, unsigned long n, unsigned char *md);
#endif

2. 代码文件md4.c

/** @        file: md4.c* @ description: implementation for the MD4 Message-Digest Algorithm* @      author: Gu Yongqiang* @        blog: https://blog.csdn.net/guyongqiangx*/
#include <stdio.h>
#include <string.h>#include "utils.h"
#include "md4.h"//#define DEBUG#ifdef DEBUG
#define DBG(...) printf(__VA_ARGS__)
#define DUMP_BLOCK_DATA 1
#define DUMP_BLOCK_HASH 1
#define DUMP_ROUND_DATA 1
#else
#define DBG(...)
#define DUMP_BLOCK_DATA 0
#define DUMP_BLOCK_HASH 0
#define DUMP_ROUND_DATA 0
#endif#define MD4_BLOCK_SIZE          64  /* 512 bits = 64 bytes */
#define MD4_LEN_SIZE            8   /*  64 bits =  8 bytes */
#define MD4_LEN_OFFSET          (MD4_BLOCK_SIZE - MD4_LEN_SIZE)
#define MD4_DIGEST_SIZE 16  /* 128 bits = 16 bytes */#define MD4_PADDING_PATTERN     0x80
#define MD4_ROUND_NUM           64#define HASH_BLOCK_SIZE         MD4_BLOCK_SIZE
#define HASH_LEN_SIZE           MD4_LEN_SIZE
#define HASH_LEN_OFFSET         MD4_LEN_OFFSET
#define HASH_DIGEST_SIZE        MD4_DIGEST_SIZE#define HASH_PADDING_PATTERN    MD4_PADDING_PATTERN
#define HASH_ROUND_NUM          MD4_ROUND_NUMtypedef uint32_t (*md4_func)(uint32_t x, uint32_t y, uint32_t z);/* MD4 Round Constants, refer rfc1320, section 3.4 */
static uint32_t T[3] =
{0x00000000, /* Round 1( 0 ~ 15), placeholder of T[idx/16] in MD4_OP */0x5A827999, /* Round 2(16 ~ 31), square root of 2 */0x6ED9EBA1, /* Round 3(32 ~ 47), square root of 3 */
};/* ROTate Left (circular left shift) */
static uint32_t ROTL(uint32_t x, uint8_t shift)
{return (x << shift) | (x >> (32 - shift));
}/** F/G/H definition, refer rfc1320, section 3.4*//** Condition* In each bit position, F acts as a conditional:*   if X then Y else Z.*/
static uint32_t F(uint32_t x, uint32_t y, uint32_t z)
{return (x & y) | ((~x) & z);
}/** Majority* In each bit position, G acts as a majority function:*   if at least two of X, Y, Z are on, then G has a "1" bit in that bit position, else G has a "0" bit.*/
static uint32_t G(uint32_t x, uint32_t y, uint32_t z)
{return (x & y) | (x & z) | (y & z);
}/** Parity* H is the bit-wise XOR or "parity" function*/
static uint32_t H(uint32_t x, uint32_t y, uint32_t z)
{return x ^ y ^ z;
}/* MD4 Functions */
static md4_func g[3] =
{F,  /*  0 ~ 15 operations */G,  /* 16 ~ 31 operations */H   /* 32 ~ 47 operations */
};int MD4_Init(MD4_CTX *c)
{if (NULL == c){return ERR_INV_PARAM;}memset(c, 0, sizeof(MD4_CTX));/* MD4 Initial Value, refer rfc1320, section 3.3 */c->hash.a = 0x67452301; /* little endian */c->hash.b = 0xEFCDAB89;c->hash.c = 0x98BADCFE;c->hash.d = 0x10325476;c->total = 0;c->last.used = 0;return ERR_OK;
}static int MD4_PrepareScheduleWord(const unsigned char *block, uint32_t *X)
{uint32_t i;uint32_t *temp;if ((NULL == block) || (NULL == X)){return ERR_INV_PARAM;}temp = (uint32_t *)block;for (i=0; i<HASH_BLOCK_SIZE/4; i++){X[i] = le32toh(temp[i]);}return ERR_OK;
}#if (DUMP_ROUND_DATA == 1)
#define MD4_OP(a,b,c,d,k,s) \a = ROTL(a + (g[idx/16])(b, c, d) + X[k] + T[idx/16], s); \DBG("      %02d: a=0x%08x, b=0x%08x, c=0x%08x, d=0x%08x, X=0x%08x, T=0x%08x\n", idx, a, b, c, d, X[k], T[idx/16]); \idx ++;
#else
#define MD4_OP(a,b,c,d,k,s) \a = ROTL(a + (g[idx/16])(b, c, d) + X[k] + T[idx/16], s); \idx ++;
#endif/* Process Message in 16-Word Blocks, refer rfc1320, section 3.4 */
static int MD4_ProcessBlock(MD4_CTX *ctx, const void *block)
{uint32_t X[HASH_BLOCK_SIZE/4];uint32_t A, B, C, D;uint32_t idx;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#if (DUMP_BLOCK_HASH == 1)DBG("  (LE)IV: %08x %08x %08x %08x\n",ctx->hash.a, ctx->hash.b, ctx->hash.c, ctx->hash.d);
#endif/* Copy block into X */MD4_PrepareScheduleWord(block, X);A = ctx->hash.a;B = ctx->hash.b;C = ctx->hash.c;D = ctx->hash.d;idx = 0;/* Round 1 */MD4_OP(A, B, C, D,  0,  3); MD4_OP(D, A, B, C,  1,  7); MD4_OP(C, D, A, B,  2, 11); MD4_OP(B, C, D, A,  3, 19);MD4_OP(A, B, C, D,  4,  3); MD4_OP(D, A, B, C,  5,  7); MD4_OP(C, D, A, B,  6, 11); MD4_OP(B, C, D, A,  7, 19);MD4_OP(A, B, C, D,  8,  3); MD4_OP(D, A, B, C,  9,  7); MD4_OP(C, D, A, B, 10, 11); MD4_OP(B, C, D, A, 11, 19);MD4_OP(A, B, C, D, 12,  3); MD4_OP(D, A, B, C, 13,  7); MD4_OP(C, D, A, B, 14, 11); MD4_OP(B, C, D, A, 15, 19);/* Round 2 */MD4_OP(A, B, C, D,  0,  3); MD4_OP(D, A, B, C,  4,  5); MD4_OP(C, D, A, B,  8,  9); MD4_OP(B, C, D, A, 12, 13);MD4_OP(A, B, C, D,  1,  3); MD4_OP(D, A, B, C,  5,  5); MD4_OP(C, D, A, B,  9,  9); MD4_OP(B, C, D, A, 13, 13);MD4_OP(A, B, C, D,  2,  3); MD4_OP(D, A, B, C,  6,  5); MD4_OP(C, D, A, B, 10,  9); MD4_OP(B, C, D, A, 14, 13);MD4_OP(A, B, C, D,  3,  3); MD4_OP(D, A, B, C,  7,  5); MD4_OP(C, D, A, B, 11,  9); MD4_OP(B, C, D, A, 15, 13);/* Round 3 */MD4_OP(A, B, C, D,  0,  3); MD4_OP(D, A, B, C,  8,  9); MD4_OP(C, D, A, B,  4, 11); MD4_OP(B, C, D, A, 12, 15);MD4_OP(A, B, C, D,  2,  3); MD4_OP(D, A, B, C, 10,  9); MD4_OP(C, D, A, B,  6, 11); MD4_OP(B, C, D, A, 14, 15);MD4_OP(A, B, C, D,  1,  3); MD4_OP(D, A, B, C,  9,  9); MD4_OP(C, D, A, B,  5, 11); MD4_OP(B, C, D, A, 13, 15);MD4_OP(A, B, C, D,  3,  3); MD4_OP(D, A, B, C, 11,  9); MD4_OP(C, D, A, B,  7, 11); MD4_OP(B, C, D, A, 15, 15);ctx->hash.a += A;ctx->hash.b += B;ctx->hash.c += C;ctx->hash.d += D;#if (DUMP_BLOCK_HASH == 1)DBG(" (LE)OUT: %08x %08x %08x %08x\n",ctx->hash.a, ctx->hash.b, ctx->hash.c, ctx->hash.d);
#endifreturn ERR_OK;
}int MD4_Update(MD4_CTX *c, const void *data, unsigned long 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);MD4_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){MD4_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 MD4_Final(unsigned char *md, MD4_CTX *c)
{uint32_t *temp;if ((NULL == c) || (NULL == md)){return ERR_INV_PARAM;}/* Last block should be less than 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);MD4_ProcessBlock(c, &c->last.buf);memset(&c->last.buf[0], 0, HASH_BLOCK_SIZE - HASH_LEN_SIZE);c->last.used = 0;/* save length */temp = (uint32_t *)&(c->last.buf[HASH_LEN_OFFSET]);temp[0] = htole32((c->total << 3) & 0xFFFFFFFF);temp[1] = htole32(((c->total << 3) >> 32) & 0xFFFFFFFF);MD4_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 */temp = (uint32_t *)&(c->last.buf[HASH_LEN_OFFSET]);temp[0] = htole32((c->total << 3) & 0xFFFFFFFF);temp[1] = htole32(((c->total << 3) >> 32) & 0xFFFFFFFF);MD4_ProcessBlock(c, &c->last.buf);}/* LE for MD4/MD5, different from SHA family(Big Endian) */temp = (uint32_t *)md;temp[0] = htole32(c->hash.a);temp[1] = htole32(c->hash.b);temp[2] = htole32(c->hash.c);temp[3] = htole32(c->hash.d);return ERR_OK;
}unsigned char *MD4(const unsigned char *d, unsigned long n, unsigned char *md)
{MD4_CTX c;if ((NULL == d) || (NULL == md)){return NULL;}MD4_Init(&c);MD4_Update(&c, d, n);MD4_Final(md, &c);return md;
}

MD4源码的编译和测试

我直接在Makefile中内置了一个test伪目标,编译时除了编译生成名为md4的哈希工具外,还会直接调用内置的哈希测试。

编译和运行如下:

$ make
gcc -Wall -g -O2 -c utils.c -o utils.o
gcc -Wall -g -O2 -c md4.c -o md4.o
gcc -Wall -g -O2 -c md4test.c -o md4test.o
gcc -Wall -g -O2 utils.o md4.o md4test.o -o md4Run Test...
./md4 -x
Internal hash tests for ./md4:
./md4("")Expect: 31d6cfe0d16ae931b73c59d7e0c089c0Result: 31d6cfe0d16ae931b73c59d7e0c089c0./md4("a")Expect: bde52cb31de33e46245e05fbdbd6fb24Result: bde52cb31de33e46245e05fbdbd6fb24./md4("abc")Expect: a448017aaf21d8525fc10ae87aa6729dResult: a448017aaf21d8525fc10ae87aa6729d./md4("message digest")Expect: d9130a8164549fe818874806e1c7014bResult: d9130a8164549fe818874806e1c7014b./md4("abcdefghijklmnopqrstuvwxyz")Expect: d79e1c308aa5bbcdeea8ed63df412da9Result: d79e1c308aa5bbcdeea8ed63df412da9./md4("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")Expect: 043f8582f241db351ce627e153e7f0e4Result: 043f8582f241db351ce627e153e7f0e4./md4("12345678901234567890123456789012345678901234567890123456789012345678901234567890")Expect: e33b4ddc9c38f2199c3e7b164fcc0536Result: e33b4ddc9c38f2199c3e7b164fcc0536

目前版本的openssl工具还支持md4哈希算法,因此可以将md4工具和openssl执行dgst计算的结果进行比较:

# 使用"-f"和"-s"选项分别对文件和字符串计算md4哈希值
$ md4 -f md4.o
md4(md4.o) = 801a3587c9d30d276bd07a7a4a1fa616
$ md4 -s "I Love China!"
md4("I Love China!") = 24cb6e3ed5ba92ab304dee9982d7f317# 使用开源的openssl工具计算相应的哈希进行对比
$ openssl dgst -md4 md4.o
MD4(md4.o)= 801a3587c9d30d276bd07a7a4a1fa616
$ echo -n "I Love China!" | openssl dgst -md4
(stdin)= 24cb6e3ed5ba92ab304dee9982d7f317

完整代码

完整的代码包括Makefile和相应的测试文件md4test.c,需要代码请访问:

  • https://github.com/guyongqiangx/cryptography/

其它

洛奇工作中常常会遇到自己不熟悉的问题,这些问题可能并不难,但因为不了解,找不到人帮忙而瞎折腾,往往导致浪费几天甚至更久的时间。

所以我组建了几个微信讨论群(记得微信我说加哪个群,如何加微信见后面),欢迎一起讨论:

  • 一个密码编码学讨论组,主要讨论各种加解密,签名校验等算法,请说明加密码学讨论群。
  • 一个Android OTA的讨论组,请说明加Android OTA群。
  • 一个git和repo的讨论组,请说明加git和repo群。

在工作之余,洛奇尽量写一些对大家有用的东西,如果洛奇的这篇文章让您有所收获,解决了您一直以来未能解决的问题,不妨赞赏一下洛奇,这也是对洛奇付出的最大鼓励。扫下面的二维码赞赏洛奇,金额随意:

洛奇自己维护了一个公众号“洛奇看世界”,一个很佛系的公众号,不定期瞎逼逼。公号也提供个人联系方式,一些资源,说不定会有意外的收获,详细内容见公号提示。扫下方二维码关注公众号:

MD4哈希算法原理及实现(附源码)相关推荐

  1. SHA224和SHA256哈希算法原理及实现(附源码)

    相关文章: SHA224和SHA256哈希算法原理及实现(附源码) 国密SM3哈希算法原理及实现(附源码) SHA1哈希算法原理及实现(附源码) MD5哈希算法原理及实现(附源码) MD4哈希算法原理 ...

  2. SHA3系列(KECCAK)哈希算法原理及实现(附源码)

    相关文章: (本文持续更新中) SHA3系列(KECCAK)哈希算法原理及实现(附源码) SHA512系列哈希算法原理及实现(附源码) SHA224和SHA256哈希算法原理及实现(附源码) 国密SM ...

  3. SHA512系列哈希算法原理及实现(附源码)

    相关文章: SHA512系列哈希算法原理及实现(附源码) SHA224和SHA256哈希算法原理及实现(附源码) 国密SM3哈希算法原理及实现(附源码) SHA1哈希算法原理及实现(附源码) MD5哈 ...

  4. 国密SM3密码杂凑算法原理及实现(附源码)

    相关文章: 国密SM3哈希算法原理及实现(附源码) SHA1哈希算法原理及实现(附源码) MD5哈希算法原理及实现(附源码) MD4哈希算法原理及实现(附源码) MD2哈希算法原理及实现(附源码) M ...

  5. linux直流电机测试,带霍尔传感器编码器的直流减速电机测速原理讲解(附源码)...

    查看: 14294|回复: 83 带霍尔传感器编码器的直流减速电机测速原理讲解(附源码) 高级会员, 积分 891, 距离下一级还需 109 积分 积分金钱891 注册时间2019-4-22 在线时间 ...

  6. 超详讲解图像拼接/全景图原理和应用 | 附源码

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 概述 图像拼接是计算机视觉中最成功的应用之一.如今,很难找到不包含 ...

  7. python全景图像拼接_超详讲解图像拼接/全景图原理和应用 | 附源码

    研究好玩又有用的技术第 008 期 在学习中发现快乐,在应用找到价值.这是我第八期分享图像技术应用的文章. 前七期欢迎阅读和分享: 概述 作者:Thalles Silva 编译:AI算法与图像处理 图 ...

  8. 基于Vision Transformer的图像去雾算法研究与实现(附源码)

    基于Vision Transformer的图像去雾算法研究与实现 0. 服务器性能简单监控 \LOG_USE_CPU_MEMORY\文件夹下的use_memory.py文件可以实时输出CPU使用率以及 ...

  9. 人工智能 - A*算法解决迷宫问题 附源码和可视化显示

    写在最前,先附上可视化后的效果: 一.问题描述 迷宫问题可以表述为:一个二维的网格,0 表示点可走,1 表示点 不可以走,点用(x,y)表示,寻找从某一个给定的起始单元格出发, 经由行相邻或列相邻的单 ...

  10. 中文分词:隐马尔可夫-维特比算法(HMM-Viterbi)附源码

    目录 0.先验知识 1.什么是中文分词 2.数据集的构造 3.训练及预测过程简述 4.训练阶段:统计隐马尔可夫模型的参数 5.预测阶段:应用 Viterbi 算法 6.完整的 Python 实现代码 ...

最新文章

  1. Aspose.Words导出图片 表格 Interop.Word
  2. 让人头痛的大事务问题到底要如何解决?
  3. React鼠标右单击事件
  4. WEB开发中的页面跳转方法总结
  5. 广工android嵌入式系统试卷_嵌入式系统考试试题A及答案
  6. Spring Boot中使用MongoDB的连接池配置
  7. 爱普生690k打印针测试软件_办公室打印机什么牌子好 办公室打印机怎么选购【详解】...
  8. java 内存分配参数_浅谈JAVA内存分配与参数传递
  9. 思必驰AI芯片发布:内置完整语音交互方案,支持离线模式,All in One
  10. Microsoft caffe(caffe-windows) cifar实例编译之model的使用
  11. 个人收集的资源(0th) IDM绿化版(免费免积分)
  12. RecycleView多布局的实现
  13. 什么是android原生系统版本,定制安卓和原生Android到底有哪些不同之处?彻底真相了...
  14. 达叔机器学习笔记1_逻辑回归建立一般流程
  15. xinxin -QQ登录界面
  16. 数学题库python_GitHub Python项目推荐|一个拍照做题程序|数学计算题识题|opencv...
  17. 爬取偶像/私房小姐姐图片--爬虫基础篇
  18. 阿里云APP备案操作流程 新手看过来
  19. 学ajax要学php吗,PHP学习
  20. 原创教程PS修图技巧-如何用portraiture滤镜来进行人像磨皮

热门文章

  1. debian 11安装微信
  2. 微信取消支付再二次/多次支付及201商户订单号重复解决思路
  3. 【已解决】NC65收款合同查询数据最多只显示5000条
  4. 婴幼儿体重在线计算机,宝宝身高体重标准计算器
  5. stdafx.h简介
  6. 分析一块某宝上的WiFi摄像头模块
  7. PHP数组按字符串长度排序
  8. c#使用webbrowser时,设定IE版本
  9. 细胞自动机 java_中国MOOC_面向对象程序设计——Java语言_期末考试编程题_1细胞自动机...
  10. VMware-workstation 密钥