【密码学】一文读懂SM3

简介: SM3是中华人民共和国政府采用的一种密码散列函数标准,前身为SCH4杂凑算法,由国家密码管理局于2010年12月17日发布,相关标准为"GM/T 0004-2012 《SM3密码杂凑算法》"。

一文读懂SM3


({02X~QR~71OPWM76MZDSYX.jpg一文读懂SM3


算法简介

SM3是中华人民共和国政府采用的一种密码散列函数标准,前身为SCH4杂凑算法,由国家密码管理局于2010年12月17日发布,相关标准为"GM/T 0004-2012 《SM3密码杂凑算法》"。2016年,成为中国国家密码标准(GB/T 32905-2016)。【维基百科】

这个算法是公开的算法,其他公开的算法还有SM2和SM4, 再给自己挖个坑吧,有空的时候顺道研究研究剩下的两个。


算法描述

这个算法的结构和MD系列以及SHA1/SHA2相似。

消息填充

这一步和SHA256的填充方案一致,规则如下:

步骤一: 数据填充(Append Padding Bits)

SM3是按照分块进行处理的,分块长度为512bit, 大多数情况下,数据的长度不会恰好满足是512的整数倍,因此需要进行「padding」到给定的长度。

「填充规则」: 原始明文消息的b位之后补100..., 直到满足b + paddingLength % 512 = 448, 那如果b % 512[448, 512(0)]之间呢,则在增加一个分块,按照前面的规则填充即可。

步骤二: 长度填充

之前说了,需要满足b + paddingLength % 512 = 448, 那么对于最后一个分块,就还剩512 - 448 = 64 bit 这剩下的64bit存放的是原始消息的长度,也就是b「SM3」最多可以处理明文长度小于等于2^64 bit的数据。

经过上面两个步骤的处理,最终得到的处理后的数据如下图所示:

4PMZ`W$T1RN%@1KD~~{X)PB.png

image.gifSM3-Padding

消息扩展

这里对于消息的处理与SHA系列有一些差别,这里并没有对原始的消息分组直接参与运算,而是通过某种规则,将消息扩展成为以及, 规则如下:

  1. 对于~, 内容为原始的消息字
  2. 而对于~, 通过如下的公式进行计算
W_{j} = P_{1}(W_{j - 16} \oplus W_{j-9} \oplus (W_{j-3} <<< 15)) \oplus (W_{j-13} <<< 7) \oplus W_{j-6}
  1. 对于~, 按照如下方式进行处理
W_{j}^{'} = W_j \oplus W_{j+4}

BR33UVZ1~2BCG{4QOOB8~SN.pngSM3消息扩展

上图简单描述了一下消息扩展的过程,有部分细节并没有完全体现在图中,注意一下(全画出来图有点乱,所以省略了一下部分细节)。

迭代压缩

这块结构和SHA系列算法也比较相似,由初始化向量然后对于每一个扩展后的消息进行处理,直到处理完最后一个分块。

){]OBSBZ@})[VC]GJR1M64S.png压缩迭代过程

整个算法的核心,也就是压缩函数,具体流程如下图所示:

}Q0H0}TEG[X9WLEAKK%NACI.png

image.gif压缩函数

对于详细压缩函数的计算过程,这里我就不贴出来了,可以参考本文给出的代码,也可以参考文末的资料。

输出

到此,最终迭代完成的变量,按照大端输出即为最终的杂凑值。


代码实现

rust作为老演员了,依然用它写吧, 感觉这些代码实现起来大同小异,语言特性我也没用多少,哈哈哈。

pub struct SM3 {}
fn p_0(x: u32) -> u32 {
    x ^ x.rotate_left(9) ^ x.rotate_left(17)
}
fn p_1(x: u32) -> u32 {
    x ^ x.rotate_left(15) ^ x.rotate_left(23)
}
fn ff(j: u32, x: u32, y: u32, z: u32) -> u32 {
    match j {
        n if n < 16 => x ^ y ^ z,
        n if 16 <= n && n < 64 => (x & y) | (x & z) | (y & z),
        _ => 0,
    }
}
fn gg(j: u32, x: u32, y: u32, z: u32) -> u32 {
    match j {
        n if n < 16 => x ^ y ^ z,
        n if n >= 16 => (x & y) | ((!x) & z),
        _ => 0,
    }
}
fn t(j: u32) -> u32 {
    return if j < 16 {
        0x79cc4519
    } else {
        0x7a879d8a
    }
}
impl SM3 {
    fn padding(message: &[u8]) -> Vec<u8> {
        let message_length = message.len() as u64 * 8;
        let mut result = message.to_owned();
        // padding 1
        result.push(0x80);
        // padding 0
        while ((result.len() * 8) + 64) % 512 != 0 {
            result.push(0b00000000);
        }
        for b in 0..8 {
            result.push((message_length >> ((7 - b) * 8)) as u8);
        }
        return result;
    }
    pub fn hash(message: &[u8]) -> String {
        let padding_message = SM3::padding(message);
        let mut iv: [u32; 8] = [
            0x7380166f, 0x4914b2b9, 0x172442d7, 0xda8a0600,
            0xa96f30bc, 0x163138aa, 0xe38dee4d, 0xb0fb0e4e,
        ];
        let mut v: [u32; 8] = [0; 8];
        let mut w = [0u32; 68];
        let mut w_p = [0u32; 64];
        for chunk in padding_message.chunks(64) {
            let m: Vec<u32> = chunk.chunks(4).map(|i| {
                ((i[0] as u32) << 24) | ((i[1] as u32) << 16) | ((i[2] as u32) << 8) | ((i[3] as u32) << 0)
            }).collect();
            for j in 0..16 {
                w[j] = m[j];
            }
            for j in 16..68 {
                w[j] = p_1(w[j - 16] ^ w[j - 9] ^ w[j - 3].rotate_left(15))
                    ^ w[j - 13].rotate_left(7)
                    ^ w[j - 6];
            }
            for j in 0..64 {
                w_p[j] = w[j] ^ w[j + 4];
            }
            v = iv;
            // ABCDEFGH
            // 01234567
            for j in 0..64 {
                // SS1 ← ((A ≪ 12) + E + (Tj ≪ j)) ≪ 7
                let mut ss1 = v[0]
                    .rotate_left(12)
                    .wrapping_add(v[4])
                    .wrapping_add(t(j).rotate_left(j as u32))
                    .rotate_left(7);
                let mut ss2 = ss1 ^ v[0].rotate_left(12);
                let mut tt1 = ff(j, v[0], v[1], v[2]).
                    wrapping_add(v[3])
                    .wrapping_add(ss2)
                    .wrapping_add(w_p[j as usize]);
                let mut tt2 = gg(j, v[4], v[5], v[6]).
                    wrapping_add(v[7])
                    .wrapping_add(ss1)
                    .wrapping_add(w[j as usize]);
                v[3] = v[2];
                v[2] = v[1].rotate_left(9);
                v[1] = v[0];
                v[0] = tt1;
                v[7] = v[6];
                v[6] = v[5].rotate_left(19);
                v[5] = v[4];
                v[4] = p_0(tt2);
            }
            iv[0] ^= v[0];
            iv[1] ^= v[1];
            iv[2] ^= v[2];
            iv[3] ^= v[3];
            iv[4] ^= v[4];
            iv[5] ^= v[5];
            iv[6] ^= v[6];
            iv[7] ^= v[7];
        }
        return String::from(format!(
            "{:08x}{:08x}{:08x}{:08x}{:08x}{:08x}{:08x}{:08x}",
            iv[0], iv[1], iv[2], iv[3], iv[4], iv[5], iv[6], iv[7]
        ));
    }
}
#[cfg(test)]
mod test {
    use crate::sm3::SM3;
    #[test]
    fn test() {
        println!("SM3([empty string]) = {}", SM3::hash("".as_bytes()));
        println!("SM3([The quick brown fox jumps over the lazy dog]) = {}", SM3::hash("The quick brown fox jumps over the lazy dog".as_bytes()));
    }
}


相关文章
|
机器学习/深度学习 算法 安全
密码学系列之五:MD5、SHA1——一文搞懂哈希函数
密码学系列之五:MD5、SHA1——一文搞懂哈希函数
11064 113
|
Rust 算法 安全
【密码学】一文读懂HMAC
本文将来聊一聊基于哈希函数的消息认证码,在此之前,先来科普一下什么是 「消息认证码」 (MAC), 先来看一个简单的栗子
2527 0
【密码学】一文读懂HMAC
|
11月前
|
存储 Web App开发 缓存
清理C盘空间的6种方法,附详细操作步骤
释放C盘空间并不难。只要掌握合适的方法,哪怕你是电脑小白,也能轻松清理出几十GB空间。下面就为大家介绍6种实用、安全、细致的清理方法,并附上操作步骤。
|
11月前
|
JSON 算法 安全
harmony-utils之SM3,SM3工具类
harmony-utils是一款HarmonyOS工具库,提供丰富的SM3加密工具类,支持摘要、分段处理及消息认证码计算,助力开发者高效构建安全应用。
491 0
|
算法 安全 物联网
关于SM2、SM3、SM4、SM9这四种国密算法
本文介绍了四种国密算法——SM2、SM3、SM4和SM9。SM2是一种基于椭圆曲线的非对称加密算法,用于数据加密和数字签名;SM3是哈希算法,用于数字签名和消息完整性验证;SM4是对称加密算法,用于数据加密和解密;SM9是基于标识的非对称密码算法,适用于物联网环境中的数据安全和隐私保护。
11125 121
|
存储 算法 安全
一文带你学习“国密算法”
一文带你学习“国密算法”
3103 3
一文带你学习“国密算法”
|
Rust 搜索推荐 算法
【密码学】一文读懂GCM(Golais计数器模式)
GCM(Galois计数器模式)同样的也是NIST提出来的,这个模式基于并行化设计,下面来一起看一下这个模式具体是如何工作的吧。
3855 0
【密码学】一文读懂GCM(Golais计数器模式)
|
算法 安全 Serverless
超级好用的C++实用库之国密sm3算法
超级好用的C++实用库之国密sm3算法
1035 0
|
Java 应用服务中间件 Linux
解决Tomcat启动闪退问题的详细指南
解决Tomcat启动闪退问题的详细指南
|
算法 安全 网络安全
信息安全: MAC(消息认证码)算法,保护数据完整性和真实性的利器
MAC 算法在保证数据完整性和真实性方面扮演着重要角色。HMAC 和 CMAC 作为两种主要的 MAC 算法,因其高安全性和广泛应用,已经成为现代通信和数据保护中不可或缺的一部分。通过本文的介绍,希望读者能够更好地理解和使用 MAC 算法,保障信息的安全性。