【密码学】一文读懂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()));
    }
}


相关文章
|
Rust 算法 安全
【密码学】一文读懂HMAC
本文将来聊一聊基于哈希函数的消息认证码,在此之前,先来科普一下什么是 「消息认证码」 (MAC), 先来看一个简单的栗子
1763 0
【密码学】一文读懂HMAC
|
Rust 算法 数据安全/隐私保护
【密码学】一文读懂XTEA加密
本篇文章,我们来看一下上一次讲过的TEA加密算法的一个升级版XTEA, 相比于TEA, XTEA的安全性显然是更高的,其中的过程要比TEA稍微复杂一点点。
1296 0
【密码学】一文读懂XTEA加密
|
算法 网络安全 数据安全/隐私保护
【密码学】手摸手带你手算AES
本文带着大家手动计算了一下完整的简化版AES的整个流程,其实主要都参考了密码学与网络安全这本书,大部分的公式都是从这本书上来的,我是真的喜欢这个例子,麻雀虽小,五脏俱全,用来学习AES的入门感觉非常的合适,如果能够完整的自己手算下来这个例子,然后再去看完整版的AES算法,会简单非常多,主要就是规模扩大了一下,核心的流程还是上面这一套。最后,感谢能看到这里的读者,如果本文对大佬们理解AES有一点点的帮助,也不枉我手动敲了这么多的公式和矩阵了。
【密码学】手摸手带你手算AES
|
8月前
|
机器学习/深度学习 安全 算法
【现代密码学】笔记9-10.3-- 公钥(非对称加密)、混合加密理论《introduction to modern cryphtography》
【现代密码学】笔记9-10.3-- 公钥(非对称加密)、混合加密理论《introduction to modern cryphtography》
125 0
|
8月前
|
人工智能 分布式计算 安全
【现代密码学】笔记1.2 -- 对称密钥加密、现代密码学的基本原则《introduction to modern cryphtography》现代密码学原理与协议
【现代密码学】笔记1.2 -- 对称密钥加密、现代密码学的基本原则《introduction to modern cryphtography》现代密码学原理与协议
473 0
|
算法 数据安全/隐私保护
【密码学】一文读懂SHA-1
SHA-1(Secure Hash Algorithm 1)是一种密码散列函数,美国国家安全局设计,并由美国国家标准技术研究所(NIST)发布为联邦资料处理标准(FIPS)。SHA-1可以生成一个被称为消息摘要的160位(20字节)散列值,散列值通常的呈现形式为40个十六进制数。
1257 1
【密码学】一文读懂SHA-1
|
算法 安全 数据安全/隐私保护
【密码学】一文读懂SM4
SM4(原名SMS4)是中华人民共和国政府采用的一种分组密码标准,由国家密码管理局于2012年3月21日发布,相关标准为“GM/T 0002-2012《SM4分组密码算法》(原SMS4分组密码算法)”。
2840 0
【密码学】一文读懂SM4
|
Rust 算法 数据安全/隐私保护
【密码学】一文读懂AES
AES加密算法 本文将不会讲述过多有关AES的数学知识, 如果有兴趣的可以自行了解。
1789 0
【密码学】一文读懂AES
|
Rust 算法 数据安全/隐私保护
【密码学】一文读懂MD5
曾经沧海难为水,除却巫山不是云。-- 元稹
【密码学】一文读懂MD5
|
算法 安全 数据安全/隐私保护
【密码学】一文读懂MD4
MD4是麻省理工学院教授Ronald Rivest于1990年设计的一种信息摘要算法。它是一种用来测试信息完整性的密码散列函数的实行。其摘要长度为128位。这个算法影响了后来的算法如MD5、SHA家族和RIPEMD等。
1504 0
【密码学】一文读懂MD4