当前位置: 首页 > news >正文

从零手搓SM3国密算法:用C++一步步实现哈希函数(附完整可运行代码)

从零手搓SM3国密算法:用C++一步步实现哈希函数(附完整可运行代码)

密码学算法的魅力在于,它用数学的确定性构建了数字世界的安全基石。当我们谈论哈希函数时,开发者往往满足于调用现成的库函数,却错过了理解算法精妙之处的机会。本文将带你从零开始,用C++实现SM3国密哈希算法,这个过程不仅是代码编写,更是一次密码学思维的深度训练。

1. 环境准备与基础工具函数

1.1 开发环境配置

实现SM3算法需要基本的C++开发环境:

  • 支持C++11及以上标准的编译器(GCC/Clang/MSVC均可)
  • 文本编辑器或IDE(VS Code/CLion等)
  • 调试工具(gdb/lldb或IDE内置调试器)

建议在Linux/macOS下开发,可以更方便地使用命令行工具进行编译和测试。创建项目目录结构:

sm3_implement/ ├── include/ │ └── sm3.h # 算法声明 ├── src/ │ ├── sm3.cpp # 算法实现 │ └── main.cpp # 测试代码 └── Makefile # 构建配置

1.2 基础数据类型转换

SM3算法处理的是二进制位操作,我们需要实现各种进制间的转换函数:

// 十六进制字符到4位二进制的映射 const std::unordered_map<char, std::string> hexToBinMap = { {'0', "0000"}, {'1', "0001"}, {'2', "0010"}, {'3', "0011"}, {'4', "0100"}, {'5', "0101"}, {'6', "0110"}, {'7', "0111"}, {'8', "1000"}, {'9', "1001"}, {'a', "1010"}, {'b', "1011"}, {'c', "1100"}, {'d', "1101"}, {'e', "1110"}, {'f', "1111"} }; std::string hex_to_binary(const std::string& hex) { std::string binary; for (char c : hex) { auto it = hexToBinMap.find(tolower(c)); if (it != hexToBinMap.end()) { binary += it->second; } } return binary; }

注意:在实际实现中,位操作比字符串操作效率更高。这里使用字符串是为了代码可读性,生产环境应考虑优化。

2. SM3算法核心组件实现

2.1 消息填充机制

SM3的消息填充遵循以下规则:

  1. 在消息末尾添加一个"1"位
  2. 添加若干个"0"位直到长度满足 (长度 % 512) = 448
  3. 最后64位表示原始消息的位长度
std::string sm3_padding(const std::string& message) { // 转换为二进制字符串 std::string binary; for (char c : message) { binary += std::bitset<8>(c).to_string(); } uint64_t original_length = binary.size(); binary += "1"; // 添加终止位 // 填充0直到长度 ≡ 448 mod 512 while (binary.size() % 512 != 448) { binary += "0"; } // 添加原始长度(64位大端序) binary += std::bitset<64>(original_length).to_string(); return binary; }

2.2 消息扩展函数

消息扩展将512位的消息分组扩展为132个字(W0-W67和W'0-W'63):

void message_expansion(const std::string& block, std::array<uint32_t, 68>& W, std::array<uint32_t, 64>& W_prime) { // 初始化前16个字 for (int i = 0; i < 16; ++i) { W[i] = std::stoul(block.substr(i * 32, 32), nullptr, 2); } // 计算W16-W67 for (int j = 16; j < 68; ++j) { uint32_t temp = W[j-16] ^ W[j-9] ^ (rotate_left(W[j-3], 15)); W[j] = P1(temp) ^ (rotate_left(W[j-13], 7)) ^ W[j-6]; } // 计算W'0-W'63 for (int j = 0; j < 64; ++j) { W_prime[j] = W[j] ^ W[j+4]; } }

其中rotate_leftP1函数实现如下:

constexpr uint32_t rotate_left(uint32_t x, uint32_t n) { return (x << n) | (x >> (32 - n)); } constexpr uint32_t P0(uint32_t x) { return x ^ rotate_left(x, 9) ^ rotate_left(x, 17); } constexpr uint32_t P1(uint32_t x) { return x ^ rotate_left(x, 15) ^ rotate_left(x, 23); }

3. 压缩函数与迭代处理

3.1 压缩函数实现

压缩函数是SM3的核心,处理扩展后的消息和当前哈希值:

void compression_function(const std::array<uint32_t, 68>& W, const std::array<uint32_t, 64>& W_prime, std::array<uint32_t, 8>& V) { uint32_t A = V[0], B = V[1], C = V[2], D = V[3]; uint32_t E = V[4], F = V[5], G = V[6], H = V[7]; for (int j = 0; j < 64; ++j) { uint32_t SS1 = rotate_left(rotate_left(A, 12) + E + rotate_left(T(j), j), 7); uint32_t SS2 = SS1 ^ rotate_left(A, 12); uint32_t TT1 = FF(A, B, C, j) + D + SS2 + W_prime[j]; uint32_t TT2 = GG(E, F, G, j) + H + SS1 + W[j]; D = C; C = rotate_left(B, 9); B = A; A = TT1; H = G; G = rotate_left(F, 19); F = E; E = P0(TT2); } V[0] ^= A; V[1] ^= B; V[2] ^= C; V[3] ^= D; V[4] ^= E; V[5] ^= F; V[6] ^= G; V[7] ^= H; }

辅助函数实现:

constexpr uint32_t T(int j) { return (j < 16) ? 0x79CC4519 : 0x7A879D8A; } constexpr uint32_t FF(uint32_t X, uint32_t Y, uint32_t Z, int j) { return (j < 16) ? (X ^ Y ^ Z) : ((X & Y) | (X & Z) | (Y & Z)); } constexpr uint32_t GG(uint32_t X, uint32_t Y, uint32_t Z, int j) { return (j < 16) ? (X ^ Y ^ Z) : ((X & Y) | ((~X) & Z)); }

3.2 迭代压缩过程

完整的SM3算法处理流程:

std::string sm3_hash(const std::string& message) { // 初始值IV std::array<uint32_t, 8> V = { 0x7380166F, 0x4914B2B9, 0x172442D7, 0xDA8A0600, 0xA96F30BC, 0x163138AA, 0xE38DEE4D, 0xB0FB0E4E }; // 消息填充 std::string padded = sm3_padding(message); size_t blocks = padded.size() / 512; // 处理每个512位块 for (size_t i = 0; i < blocks; ++i) { std::string block = padded.substr(i * 512, 512); std::array<uint32_t, 68> W = {0}; std::array<uint32_t, 64> W_prime = {0}; message_expansion(block, W, W_prime); compression_function(W, W_prime, V); } // 生成最终哈希值 std::stringstream ss; for (uint32_t word : V) { ss << std::hex << std::setw(8) << std::setfill('0') << word; } return ss.str(); }

4. 测试验证与性能优化

4.1 标准测试向量验证

使用官方测试用例验证实现正确性:

void test_sm3() { struct TestCase { std::string input; std::string expected; }; std::vector<TestCase> tests = { {"abc", "66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0"}, {"abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd", "debe9ff92275b8a138604889c18e5a4d6fdb70e5387e5765293dcba39c0c5732"} }; for (const auto& test : tests) { std::string result = sm3_hash(test.input); std::cout << "Input: " << test.input << "\n"; std::cout << "Expected: " << test.expected << "\n"; std::cout << "Actual: " << result << "\n"; std::cout << (result == test.expected ? "PASS" : "FAIL") << "\n\n"; } }

4.2 性能优化技巧

原始实现可以通过以下方式优化:

  1. 减少内存分配:预分配缓冲区,避免频繁的字符串操作
  2. 使用位操作替代算术运算:如模加运算可以用掩码实现
  3. 循环展开:手动展开关键循环减少分支预测失败
  4. SIMD指令:利用现代CPU的并行计算能力

优化后的模加运算实现:

inline uint32_t mod_add(uint32_t a, uint32_t b) { uint32_t sum = a + b; return sum; // 在32位系统中,自然溢出等同于模2^32 }

4.3 错误处理与边界条件

健壮的实现需要考虑各种边界情况:

std::string sm3_hash(const std::string& message) { if (message.empty()) { // 处理空输入的特殊情况 return "1ab21d8355cfa17f8e61194831e81a8f22bec8c728fefb747ed035eb5082aa2b"; } try { // ...正常处理流程... } catch (const std::exception& e) { std::cerr << "SM3 hash error: " << e.what() << std::endl; return ""; } }
http://www.jsqmd.com/news/663511/

相关文章:

  • 解锁MATLAB算力:GPU并行计算实战指南
  • 如何用 filter 过滤数组中不符合业务条件的冗余数据
  • 从CH344Q出发:打造高性能USB转4路TTL串口模块的设计实践
  • 软件测试核心概念实战解析:从理论到习题的深度贯通
  • 别再让VAE学废了!手把手教你诊断和修复‘后验坍塌’(附PyTorch代码)
  • 从滤波到优化:手把手拆解VIO算法核心,看懂OpenVINS的MSCKF和ORB-SLAM3的BA到底差在哪
  • AI代码配额=新型IT预算?2026奇点大会披露:头部企业已将配额消耗纳入DevOps成本中心KPI(含真实财务映射表)
  • 最新 AI 论文盘点(2026-04-12):5 篇新作看长时记忆、推理微调、可审计医疗抽取、端侧个性化与分层 RAG
  • 从IoU到EIoU:目标检测边界框回归损失函数的演进与实战解析
  • 用周立功CAN分析仪抓包解析电动汽车充电握手(附真实报文数据)
  • 从原理到代码:手把手教你用C语言和OpenSSL实现RSA分段加密与验签(附完整项目)
  • ABR 会将自身所在区域内的路由(包括直连网段)通过 Type 3 LSA 通告到其他区域,但不会通告回本区域
  • Multi-Agent产品策略:从功能堆砌到智能工作流的重构
  • MT7916芯片深度解析:从拆机中兴E1630看MTK首款AX3000方案
  • Zotero-OCR插件:3步实现PDF文献智能识别与可搜索文本层添加
  • 【雷达成像】基于二维ADMM的稀度驱动ISAR成像附Matlab复现含文献
  • X.509数字证书实战解析:从结构到应用
  • 别再只读SOC了!MAX17048电量计的高级玩法:休眠管理、报警阈值设置与电量跳变修复
  • MATLAB条形图进阶:从基础bar函数到数据可视化实战
  • RobotStudio导入外部工具模型避坑指南:从‘无坐标’模型到可用的工具坐标系
  • Databricks 自定义容器配置指南
  • 从PID调参到根轨迹:一个电机控制工程师的实战避坑笔记
  • STM32 HAL库SPI驱动ST7789中景园屏实战:从CubeMX配置到显示优化
  • d2s-editor:暗黑破坏神2存档编辑实战指南与深度解析
  • 信息学奥赛一本通 1248:Dungeon Master | 三维迷宫搜索算法精讲
  • 别再手动算面积和距离了!用Shapely处理GeoJSON数据,效率提升10倍
  • 基于西门子PLCS7-1200的程序仿真立体车库设计报告(含硬件原理图和CAD)
  • AI大模型对内容创作的颠覆:机遇、版权争议与行业新规则
  • MIPI-DSI协议解析:从物理层到应用层的LCD驱动实践
  • 深度学习---注意力机制(Attention Mechanism)