【加解密与C】HASH系列(三)SM3

SM3算法简介

SM3是中国国家密码管理局发布的密码杂凑算法标准,属于商用密码体系中的哈希算法。其输出为256位(32字节)固定长度的哈希值,安全性对标国际通用的SHA-256,但设计更注重抗碰撞性和效率,适用于数字签名、消息认证等场景。

SM3算法特点

  • 输出长度:256位。
  • 分组处理:输入消息按512位分组处理。
  • 填充规则:采用Merkle-Damgård结构,填充方式与SHA-256类似(附加比特"1"、消息长度及填充"0")。
  • 运算步骤:包含消息扩展、压缩函数迭代等步骤,使用非线性逻辑函数和移位操作。

SM3算法流程

  1. 消息填充
    对输入消息补足至512位的倍数,填充规则为:

    • 添加一个"1"比特。
    • 补"0"至长度模512余448。
    • 最后64位写入消息的原始长度(二进制)。

    示例公式

    padded_message = original_message || 0x80 || zeros || length_in_bits
    

  2. 消息扩展
    将每个512位分组扩展为132个字(每字32位):

    • 前16个字为原始分组。
    • 后续字通过异或、循环移位等操作生成。

    扩展公式

    W_j = P1(W_{j-16} ⊕ W_{j-9} ⊕ (W_{j-3} <<< 15)) ⊕ (W_{j-13} <<< 7) ⊕ W_{j-6}
    

  3. 压缩函数
    使用8个初始变量(IV)迭代处理扩展后的消息,涉及以下操作:

    • 逻辑函数:FFj、GGj(随轮次变化的布尔函数)。
    • 置换函数:P0、P1(字级混淆)。
    • 循环移位:左移12位或7位等。

    压缩核心公式

    SS1 = (A <<< 12) + E + (T_j <<< j)
    

  4. 最终哈希值
    将所有分组的压缩结果与初始IV异或,拼接后生成最终哈希值。

应用场景

  • 数字签名:与SM2公钥算法配合使用。
  • 数据完整性验证:如区块链、文件校验。
  • 密码派生:基于哈希的密钥生成(KDF)。

SM3.h

#ifndef _SM3_H_
#define _SM3_H_ 
#include <stdint.h>
// 定义常量
#define SM3_DIGEST_SIZE 32    // 摘要长度(字节)

// SM3上下文结构
typedef struct {
    uint32_t state[8];      // 中间状态
    uint64_t count;         // 已处理消息长度(位)
    uint8_t buffer[64];     // 当前分组
} SM3_CTX;

void sm3_init(SM3_CTX* ctx);
void sm3_update(SM3_CTX* ctx, const uint8_t* data, size_t len);
void sm3_final(SM3_CTX* ctx, uint8_t digest[SM3_DIGEST_SIZE]);

#endif

SM3.cpp

#include <stdio.h>
#include <string.h>
#include "SM3.h"
#define SM3_BLOCK_SIZE 64     // 分组长度(字节)

// 初始向量IV (大端序)
static const uint32_t IV[8] = {
    0x7380166F, 0x4914B2B9, 0x172442D7, 0xDA8A0600,
    0xA96F30BC, 0x163138AA, 0xE38DEE4D, 0xB0FB0E4E
};

// 循环左移
static uint32_t ROTL(uint32_t X, int n) {
    return (X << n) | (X >> (32 - n));
}

// 布尔函数FFj
static uint32_t FFj(uint32_t X, uint32_t Y, uint32_t Z, int j) {
    return (j < 16) ? (X ^ Y ^ Z) : ((X & Y) | (X & Z) | (Y & Z));
}

// 布尔函数GGj
static uint32_t GGj(uint32_t X, uint32_t Y, uint32_t Z, int j) {
    return (j < 16) ? (X ^ Y ^ Z) : ((X & Y) | (~X & Z));
}

// 置换函数P0
static uint32_t P0(uint32_t X) {
    return X ^ ROTL(X, 9) ^ ROTL(X, 17);
}

// 置换函数P1
static uint32_t P1(uint32_t X) {
    return X ^ ROTL(X, 15) ^ ROTL(X, 23);
}

// 常量Tj
static uint32_t Tj(int j) {
    return (j < 16) ? 0x79CC4519 : 0x7A879D8A;
}

// 初始化上下文
void sm3_init(SM3_CTX* ctx) {
    memcpy(ctx->state, IV, sizeof(IV));
    ctx->count = 0;
    memset(ctx->buffer, 0, SM3_BLOCK_SIZE);
}

// 处理单个512位分组
static void sm3_compress(SM3_CTX* ctx, const uint8_t block[SM3_BLOCK_SIZE]) {
    uint32_t W[68];  // 消息扩展后的字
    uint32_t W1[64]; // 用于压缩的字
    uint32_t A, B, C, D, E, F, G, H;
    uint32_t SS1, SS2, TT1, TT2;

    // 1. 消息扩展 (手动解析大端序)
    for (int i = 0; i < 16; i++) {
        W[i] = ((uint32_t)block[i * 4] << 24) |
            ((uint32_t)block[i * 4 + 1] << 16) |
            ((uint32_t)block[i * 4 + 2] << 8) |
            (uint32_t)block[i * 4 + 3];
    }
    for (int j = 16; j < 68; j++) {
        W[j] = P1(W[j - 16] ^ W[j - 9] ^ ROTL(W[j - 3], 15))
            ^ ROTL(W[j - 13], 7) ^ W[j - 6];
    }
    for (int j = 0; j < 64; j++) {
        W1[j] = W[j] ^ W[j + 4];
    }

    // 2. 压缩函数
    A = ctx->state[0]; B = ctx->state[1]; C = ctx->state[2]; D = ctx->state[3];
    E = ctx->state[4]; F = ctx->state[5]; G = ctx->state[6]; H = ctx->state[7];

    for (int j = 0; j < 64; j++) {
        SS1 = ROTL((ROTL(A, 12) + E + ROTL(Tj(j), j)), 7);
        SS2 = SS1 ^ ROTL(A, 12);
        TT1 = FFj(A, B, C, j) + D + SS2 + W1[j];
        TT2 = GGj(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);
    }

    // 3. 更新状态
    ctx->state[0] ^= A; ctx->state[1] ^= B;
    ctx->state[2] ^= C; ctx->state[3] ^= D;
    ctx->state[4] ^= E; ctx->state[5] ^= F;
    ctx->state[6] ^= G; ctx->state[7] ^= H;
}

// 更新消息数据
void sm3_update(SM3_CTX* ctx, const uint8_t* data, size_t len) {
    size_t left = ctx->count / 8 % SM3_BLOCK_SIZE;
    size_t fill = SM3_BLOCK_SIZE - left;

    ctx->count += len * 8; // 更新位计数

    // 处理不完整缓冲区
    if (left && len >= fill) {
        memcpy(ctx->buffer + left, data, fill);
        sm3_compress(ctx, ctx->buffer);
        data += fill;
        len -= fill;
        left = 0;
    }

    // 处理完整分组
    while (len >= SM3_BLOCK_SIZE) {
        sm3_compress(ctx, data);
        data += SM3_BLOCK_SIZE;
        len -= SM3_BLOCK_SIZE;
    }

    // 存储剩余数据
    if (len) {
        memcpy(ctx->buffer + left, data, len);
    }
}

// 生成最终摘要
void sm3_final(SM3_CTX* ctx, uint8_t digest[SM3_DIGEST_SIZE]) {
    size_t last = (ctx->count / 8) % SM3_BLOCK_SIZE;
    size_t padn = (last < 56) ? (56 - last) : (120 - last);

    // 填充数据: 0x80 + 0x00... + 消息长度(64位大端序)
    uint8_t footer[64] = { 0x80 };
    uint64_t bit_count = ctx->count;
    uint8_t len_bytes[8];

    // 手动构造大端序长度
    for (int i = 0; i < 8; i++) {
        len_bytes[i] = (bit_count >> (56 - 8 * i)) & 0xFF;
    }

    sm3_update(ctx, footer, padn);
    sm3_update(ctx, len_bytes, 8);

    // 输出大端序摘要
    for (int i = 0; i < 8; i++) {
        digest[4 * i] = (ctx->state[i] >> 24) & 0xFF;
        digest[4 * i + 1] = (ctx->state[i] >> 16) & 0xFF;
        digest[4 * i + 2] = (ctx->state[i] >> 8) & 0xFF;
        digest[4 * i + 3] = ctx->state[i] & 0xFF;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿捏利

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值