【密码学】一文读懂CMAC

简介: 介于上一篇文章比较水,然后这个和上一篇也比较相似,CMAC是为了解决DAA当中安全性不足的问题而出现的,这个算法一共有三个密钥,K, K1, K2, 其中K1和K2可以由K导出,接下来就来一起看一下CMAC的具体过程吧,这一篇文章其实也不长。

一文读懂CMAC


_9X)L7(SVED837INQSR0(S8.jpgCMAC

介于上一篇文章比较水,然后这个和上一篇也比较相似,CMAC是为了解决DAA当中安全性不足的问题而出现的,这个算法一共有三个密钥,K, K1, K2, 其中K1和K2可以由K导出,接下来就来一起看一下CMAC的具体过程吧,这一篇文章其实也不长。


算法结构

对于CMAC来说,有两种情况,第一种是数据长度恰好就是分组长度的整数倍,对于这种情况,使用K1和最后一个分组异或之后加密得出结果,另一种情况是数据长度不是分组的整数倍,这就要先padding到分组的整数倍,Padding方法是先添加1bit的1, 其余bit填充0, 直到数据满足分组的整数倍,具体算法过程如下图所示:

IP(8BWA%(I{G53TRT%9{UDK.png

image.gif结构

具体过程如下:

image.png

上面是密码学与网络安全书里面的公式描述, 这个描述我感觉有一点点小问题, 就是只有一个分组的时候,实际是没有上一个明文的, 也就是是不存在的,在实现的时候需要特殊注意一下这一点, 别问我咋发现的,看着书的公式,我大意了, 没有闪,实现完成之后和RFC的结果对比的时候发现不对, 然后重新看了一遍RFC, 主要原因还是因为我太菜^.^

密钥导出算法

前文说到过, 这里的K1和K2都可以由K导出, 其中K是AES的密钥,按照如下的方式计算K1和K2

image.png

这个乘法实际上是GF()上面的乘法,具体乘法细节就不描述了,参考代码或者维基百科对于这个乘法的描述吧,有关有限域相关的知识,可以参考之前写过的有限域相关的文章。


代码实现

依然用老演员rust来实现这个算法,这里我直接用AES来实现了,偷个小懒。

use std::num::ParseIntError;
use aes::AES;
use std::{fmt::Write};
pub struct CMAC {}
fn block_xor(a: &[u8], b: &[u8]) -> [u8; 16] {
    let mut out = [0u8; 16];
    for i in 0..16 {
        out[i] = a[i] ^ b[i];
    }
    out
}
pub fn encode_hex(bytes: &[u8]) -> String {
    let mut s = String::with_capacity(bytes.len() * 2);
    for &b in bytes {
        write!(&mut s, "{:02x}", b).unwrap();
    }
    s
}
fn u8to128(message: &[u8]) -> u128 {
    ((message[0] as u128) << 120)
        | ((message[1] as u128) << 112)
        | ((message[2] as u128) << 104)
        | ((message[3] as u128) << 96)
        | ((message[4] as u128) << 88)
        | ((message[5] as u128) << 80)
        | ((message[6] as u128) << 72)
        | ((message[7] as u128) << 64)
        | ((message[8] as u128) << 56)
        | ((message[9] as u128) << 48)
        | ((message[10] as u128) << 40)
        | ((message[11] as u128) << 32)
        | ((message[12] as u128) << 24)
        | ((message[13] as u128) << 16)
        | ((message[14] as u128) << 8)
        | ((message[15] as u128) << 0)
}
fn gf128_add_one(num: &[u8]) -> Vec<u8> {
    let mut val = u8to128(num);
    if val & 0x80000000000000000000000000000000 != 0 {
        val <<= 1;
        val ^= 0x87;
    } else {
        val <<= 1;
    }
    val.to_be_bytes().to_vec()
}
impl CMAC {
    fn digest(key: &[u8], message: &[u8]) -> String {
        let mut aes = AES::new(key);
        let k0 = aes.encrypt_block(&[0; 16]);
        let k1 = gf128_add_one(k0.as_ref());
        let k2 = gf128_add_one(&k1);
        println!("k0 = {:x?}", k0);
        println!("k1 = {:x?}", k1);
        println!("k2 = {:x?}", k2);
        let mut padding_message = message.clone().to_vec();
        let mut remain_len = padding_message.len() % 16;
        if padding_message.len() == 0 {
            remain_len = 16;
        }
        if remain_len != 0 {
            padding_message.push(0x80);
            remain_len -= 1;
            while remain_len > 0 {
                padding_message.push(0x0);
                remain_len -= 1;
            }
        }
        println!("padding_message: {:?}", padding_message);
        let mut remain_len = padding_message.len() % 16;
        if message.len() == 0 {
            remain_len = 16;
        }
        let mut result = vec![0u8; padding_message.len()];
        let mut block = [0u8; 16];
        let mut buffer = [0u8; 16];
        if result.len() == 16 {
            for j in 0..16 {
                block[j] = padding_message[j];
            }
            if remain_len > 0 {
                buffer = aes.encrypt_block(&block_xor(&block, &k2));
            } else {
                buffer = aes.encrypt_block(&block_xor(&block, &k1));
            }
            return encode_hex(&buffer);
        }
        for i in 0..padding_message.len() / 16 {
            for j in 0..16 {
                block[j] = padding_message[i * 16 + j];
            }
            if i == 0 && i != padding_message.len() / 16 - 1 {
                buffer = aes.encrypt_block(&block);
            } else if i == padding_message.len() / 16 - 1 {
                if remain_len == 0 {
                    buffer = aes.encrypt_block(&block_xor(&block_xor(&buffer, &block), &k1));
                } else {
                    buffer = aes.encrypt_block(&block_xor(&block_xor(&buffer, &block), &k2));
                }
            } else {
                buffer = aes.encrypt_block(&block_xor(&buffer, &block));
            }
            for j in 0..16 {
                result[i * 16 + j] = buffer[j];
            }
        }
        return encode_hex(&buffer);
    }
}
pub fn decode_hex(s: &String) -> Result<Vec<u8>, ParseIntError> {
    (0..s.len())
        .step_by(2)
        .map(|i| u8::from_str_radix(&s[i..i + 2], 16))
        .collect()
}
#[cfg(test)]
mod tests {
    use crate::{CMAC, decode_hex};
    #[test]
    fn it_works() {
        let message = "".as_bytes();
        let key = [43u8, 126, 21, 22, 40, 174, 210, 166, 171, 247, 21, 136, 9, 207, 79, 60];
        let result = CMAC::digest(
            &key,
            &message,
        );
        println!("{}", result);
        let message = "6bc1bee22e409f96e93d7e117393172a";
        let message = decode_hex(&String::from(message)).unwrap();
        let result = CMAC::digest(
            &key,
            &message,
        );
        println!("{}", result);
    }
}


相关文章
|
存储 Rust 并行计算
【密码学】一文读懂XTS模式
这篇文章的灵感来源于我偶然翻到的一个某U盘有关磁盘加密的一个介绍(这一篇不是广告蛤), 然后发现这个模式我之前还真没遇到过,因此呢,就学习了一下,就出来了这一篇文章。
5119 0
【密码学】一文读懂XTS模式
|
Rust 算法 Go
【密码学】一文读懂MurMurHash3
本文应该是MurMurHash算法介绍的最后一篇,来一起看一下最新的MurMurHash算法的具体过程,对于最新的算法来说,整个流程和之前的其实也比较相似,这里从维基百科当中找到了伪代码,也就不贴出来Google官方给出的推荐代码了,先来看一下维基百科给出的伪代码,这里只有32位的伪代码。
2147 0
【密码学】一文读懂MurMurHash3
|
Web App开发 Rust 算法
【密码学】一文读懂ChaCha20
好久没写新的加密算法的原理了, 这次所选取的加密算法结构比较简单, 一起来看一下吧。
7591 0
【密码学】一文读懂ChaCha20
|
算法 数据安全/隐私保护
【密码学】一文读懂Whirlpool
首先呢,祝大家今晚节日快乐,Whirlpool是由Vincent Rijmen(高级加密标准的联合创始人)和Paulo S.L.M.Barreto设计的,后者于2000年首次提出了它。
1105 0
【密码学】一文读懂Whirlpool
|
Rust 算法 安全
【密码学】一文读懂MurMurHash2
上次我们聊过了一代的MurMurHash算法,是的,我又来水文章了,今天呢,接着来聊一下二代的MurMurHash算法,二代算法的整体结构实际上和一代算法差不太多,只是对于每一轮数据的处理过程当中的运算有一些差异,算法的来源依然是来自于Google官网给提供的源码,对着源码看的结构,对于这个算法呢,有两个版本,一个是32位的,一个是64位的,对于32位的算法和64位的算法,区别在于两个初始的魔数不同,整体运算过程还是十分相似的。
2189 0
【密码学】一文读懂MurMurHash2
|
Rust 算法 数据安全/隐私保护
【密码学】一文读懂XTEA加密
本篇文章,我们来看一下上一次讲过的TEA加密算法的一个升级版XTEA, 相比于TEA, XTEA的安全性显然是更高的,其中的过程要比TEA稍微复杂一点点。
1267 0
【密码学】一文读懂XTEA加密
|
算法 安全 Go
【密码学】一文读懂HKDF
我这又来水一篇文章,来聊一下HKDF(基于HMAC的密钥导出函数)。密钥派生函数是密钥管理的组成部分,他的目标是通过一些初始的数据派生出来密码学安全的随机密钥。
3076 1
【密码学】一文读懂HKDF
|
6月前
|
存储 安全 数据安全/隐私保护
加密的艺术:对称加密的奇妙之处(下)
加密的艺术:对称加密的奇妙之处(下)
加密的艺术:对称加密的奇妙之处(下)
|
存储 安全 算法
为什么人人都要懂点密码学
人类进入二十一世纪以来,随着计算机和移动设备的普及高速发展,我们的社会已经高度信息化,为了防止信息被窃取、修改,就需要对信息的存储、传递进行加密处理,而加密就需要使用到加密算法,解密需要使用密码才可以看到原文。
231 1
|
算法 搜索推荐 安全
【密码学】一文读懂CCM
本文简单介绍了CCM模式下的认证和加密机制,实际上这个是AES-CTR模式和CMAC的一个组合,如果理解了前面这两个,本文应该还是比较好理解的。
3382 0
【密码学】一文读懂CCM