SM3算法的C++实现(代码)_清梦长安的博客-程序员宅基地_sm3算法实现

技术标签: 算法  c++  哈希算法  


一、SM3算法原理

  1. SM3密码杂凑算法文档
  2. 文档中对于SM3密码杂凑算法描述得十分清楚。整个算法主要涉及了两个主要的函数:填充函数、迭代压缩函数。
  3. 迭代压缩函数包括两部分的内容:消息扩展函数、压缩函数。
  4. 消息扩展函数涉及到的主要函数为:循环左移函数、异或操作函数、置换函数P1。
  5. 压缩函数涉及到的主要函数为:循环左移函数、模加运算函数、异或操作函数、布尔函数FFj、布尔函数GGj、置换函数P0。
  6. 输入的消息首先转换成为ASCII码;然后进行消息填充,使得其长度为512比特的倍数;其次经过迭代压缩最后输出哈希值结果。
    粗糙版流程图

二、SM3算法的C++代码实现

#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;
	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";
	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);
		extensionB = extension(B);
		compressB = compress(extensionB, V);
		V = XOR(V, compressB);
	}
	return V;
}

int main() {
    //主函数
	string str[2];
	str[0] = "abc";
	str[1] = "abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd";
	for (int num = 0; num < 2; num++) {
    
		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++) {
    
				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;
	}
}

三、SM3算法的实现结果(给了文档里两个示例的运行结果)

1.当输入数据值“abc”时候的实验结果:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.当输入数据值为“abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd”时候的实验结果:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

总结

  • 实现的C++代码还有很多值得改进的地方,但是因为本人是菜鸟一只,代码写的略长,哈哈哈。
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/nicai_hualuo/article/details/121555000

智能推荐

CentOS7 安装postgreSQL11.5_bitterliquor的博客-程序员宅基地

新建用户$sudo useradd -d /data/postgres -m postgres$sudo passwd postgres$su - postgres$ wget http://ftp.postgresql.org/pub/source/v11.5/postgresql-11.5.tar.bz2$tar jxf postgresql-11.5.tar.bz2$...

Win10系统添加英文键盘_苹果@橙的博客-程序员宅基地_win10添加英文键盘

添加输入法--&gt;左键点击输入法--&gt;选择语言首选项要删除输入法点击该输入法

【ROS交叉编译】——brisk_农夫山泉2号的博客-程序员宅基地_ros2 交叉编译

ROS,交叉编译,这是一个交叉编译问题的集合,记录相关的问题的,所以可能比较乱。1. 错误: #include_next 是一个 GCC 扩展 [-Werror]报错:limits.h:124:3: 错误: #include_next 是一个 GCC 扩展 [-Werror] # include_next &lt;limits.h&gt; ^~~~~~~~~~~~cc1plus:所有的警告都被当作是错误解决:这是因为gcc将警告当成了错误,在CMakeLists.txt中注释掉这.

线代笔记 #04# 线性变换_bangtuo9862的博客-程序员宅基地

源:线性代数的本质1、如何用数字描述向量每当我们用数字描述向量,都依赖于我们正在使用的基有了基之后,如何用数字刻画一个向量呢?例如说,我们用 i 帽和 j 帽线性组合得得一个向量 v , v = x * i + y * j只需要取出 i 帽和 j 帽前面的参数 x 和 y,就可以在该基下唯一确定一个向量 v换句话话说, v 是该基下 x 和 ...

Struts笔记_Jinggg_的博客-程序员宅基地

Struts笔记第一个struts name="domain":包名;namespace="/test":该包的名称空间,一般是以"/"开头;extends="struts-default" :继承的父包的名称;method="showHello" :JavaBean中对应处理的方法。(无参的);name="success" :动作方法返回的字符串;name

FLASH位宽为8、16、32时,CPU与外设之间地址线的连接方法_liuqiang_mail的博客-程序员宅基地

FLASH连接CPU时,根据不同的数据宽度,比如16位的NOR FLASH (A0-A19),处理器的地址线要(A1-A20)左移偏1位。为什么要偏1位?从软件和CPU的角度而言,一个地址对应一个字节,就是8位数据。这是肯定的,不要怀疑这点。对于具体器件而言,它的位宽是一定的,所谓位宽,指的是\"读/写操作时,最小的数据单元\" --别说最小单元是\"位\",一般设备上没有单独

随便推点

玩转树莓派 一(准备前期配置工作1)_放小孩的羊的博客-程序员宅基地

1.刷机树莓派树莓派(raspberry pi)是一块集成度很高的板子,专门为学习arm架构的开发板,支持Raspbian, Ubuntu 等多种Linux操作系统。接下来就简单的讲解下如何烧录树莓派系统。1.1准备一张至少8g以上的内存卡(太少的话 不够后续操作)读卡器 电脑 树莓派4b1.2大家可以去进入Raspberry Pi官网 https://www.raspberrypi.org/进行下载镜像我把我本人用的镜像提供给大家,大家可以自由选择提取https://pan.baidu.c

如何在输入时弹出建议项_iteye_13411的博客-程序员宅基地

我们可以通过AutoCompleteTextView来让用户在输入时弹出建议项。importandroid.app.Activity;importandroid.os.Bundle;importandroid.widget.ArrayAdapter;importandroid.widget.AutoCompleteTextView;importcom.sec.android.touchwiz.sa...

java 定义析构函数_java 它 引用(基本类型的包装,构造函数和析构函数c++不同)..._波兰蜜蜡世家的博客-程序员宅基地

一个:java 和c++参考控制他提到引用,我们会想到java它不喜欢c++里面的指针。当然java内引用和c++里面的引用是不同的。比如:比方C++中,我对某一个函数的声明。int a(int &amp;b),b即为引用类型,函数内b的改动能够影响到函数外部。C++中,int a(int &amp;b){},b即为引用类型,若没有&amp;。b即为值类型。可是在java里面没有显示的符号说明是引...

windows oracle升级 11.2.0.3,windows平台升级ORACLE11.2.0.1到11.2.0.4_weixin_39738774的博客-程序员宅基地

一备份升级数据库是一个十分危险的事情,在生产库上,升级之前最好做一个全库的备份,以便在升级失败时可以还原数据库。停掉所有与这个数据库相关的业务系统,如中间件。确定没有业务在运行。在数据库正常关闭后,还需要备份Oracle主目录,目的还是为了升级失败时,能够还原出数据库软件到升级前的版本。1把system,sysaux中有业务相关的表移动到其他表空间通过createpfilefromspf...

nagios介绍_xuejinliang的博客-程序员宅基地

nagiosnagios是一种监控软件,可以实现对Linux主机,Windows主机,路由器,交换机等等的监控。和cacti监控所呈现的效果不同,nagios所展示出来的是所监控的设备的状态,比如:ok,warning,critical,unknown。而不是像cacti一样的绘制出某个设备的某个参数的图形。①nagios实现监控的过程中主要涉及到以下的几个对象:主机/主机组:(交换机

exit()、_exit()、return()的区别_小猪快点跑的博客-程序员宅基地

exit()函数名: exit()所在头文件:stdlib.h(如果是”VC6.0“的话头文件为:windows.h)功 能: 关闭所有文件,终止正在执行的进程。exit(0)表示正常退出,exit(x)(x不为0)都表示异常退出,这个x是返回给操作系统(包括UNIX,Linux,和MS DOS)的,以供其他程序使用。stdlib.h: void exit(int stat...

推荐文章

热门文章

相关标签