一文读懂ChaCha20
一文读懂ChaCha20
好久没写新的加密算法的原理了, 这次所选取的加密算法结构比较简单, 一起来看一下吧。
算法简介
❝Google选择了伯恩斯坦设计的,带Poly1305消息认证码的ChaCha20(即ChaCha20-Poly1305),作为OpenSSL中RC4的替代品,用以完成互联网的安全通信。Google最初实现了HTTPS (TLS/SSL)流量在Chrome浏览器(Android手机版)与Google网站之间的通信。【维基百科】
❞
本文只是介绍ChaCha20, 暂时将不会介绍带有Poly1305验证码的ChaCha20, 至于为啥叫ChaCha20可能是因为它有20轮的迭代吧。
算法原理
还是老样子, 直接来看这个加密的具体原理吧。
1/4轮操作
在ChaCha20算法当中, 一个基础的操作即为1/4轮运算, 它主要操作4个32位的无符号整数,具体操作如下:
a += b; d ^= a; d <<<= 16; c += d; b ^= c; b <<<= 12; a += b; d ^= a; d <<<= 8; c += d; b ^= c; b <<<= 7;
初始化矩阵
矩阵的输入为一个256位的密钥、32位随机数、96位计数器值以及4×32位的常数,它们均填充在32位整型数组中作为初始矩阵,如下图所示:
ChaCha20初始化矩阵
块函数(ChaCha20 Block Function)
这个块函数输入是之前所生成的状态矩阵, 最终输出64bit的"随机化"的字节, 具体操作如下所示:
initial_state = state for i in 10: QUARTERROUND(0, 4, 8, 12) QUARTERROUND(1, 5, 9, 13) QUARTERROUND(2, 6, 10, 14) QUARTERROUND(3, 7, 11, 15) QUARTERROUND(0, 5, 10, 15) QUARTERROUND(1, 6, 11, 12) QUARTERROUND(2, 7, 8, 13) QUARTERROUND(3, 4, 9, 14) state += initial_state output state
到这里, ChaCha20的基本原理就结束了, 整个密码结构并不是很复杂, 整体思路也比较清晰。
代码实现
依然采用rust来实现这个代码
// https://datatracker.ietf.org/doc/html/rfc8439 pub struct ChaCha20 { state: [u32; 16], } fn quarter_round(state: &mut [u32; 16], a: usize, b: usize, c: usize, d: usize) { state[a] = state[a].wrapping_add(state[b]); state[d] = (state[d] ^ state[a]).rotate_left(16); state[c] = state[c].wrapping_add(state[d]); state[b] = (state[b] ^ state[c]).rotate_left(12); state[a] = state[a].wrapping_add(state[b]); state[d] = (state[d] ^ state[a]).rotate_left(8); state[c] = state[c].wrapping_add(state[d]); state[b] = (state[b] ^ state[c]).rotate_left(7); } impl ChaCha20 { pub fn new(key: [u32; 8], counter: u32, nonce: [u32; 3]) -> ChaCha20 { return ChaCha20 { state: [ 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574, key[0], key[1], key[2], key[3], key[4], key[5], key[6], key[7], counter, nonce[0], nonce[1], nonce[2], ] }; } fn chacha20_block(&mut self) -> Vec<u8> { let mut key_stream = vec![]; let mut initial_state = self.state; for _ in 1..=10 { quarter_round(&mut initial_state, 0, 4, 8, 12); quarter_round(&mut initial_state, 1, 5, 9, 13); quarter_round(&mut initial_state, 2, 6, 10, 14); quarter_round(&mut initial_state, 3, 7, 11, 15); quarter_round(&mut initial_state, 0, 5, 10, 15); quarter_round(&mut initial_state, 1, 6, 11, 12); quarter_round(&mut initial_state, 2, 7, 8, 13); quarter_round(&mut initial_state, 3, 4, 9, 14); } for (index, value) in initial_state.iter().enumerate() { let new_value = self.state[index].wrapping_add(*value); for &x in new_value.to_le_bytes().iter() { key_stream.push(x); } } key_stream } pub fn encrypt(&mut self, message: &[u8]) -> Vec<u8> { let mut result = vec![]; for chunk in message.chunks(64) { for (&key, value) in chunk.iter().zip(self.chacha20_block()) { result.push(key ^ value); } self.state[12] += 1; } return result; } } #[cfg(test)] mod test { use crate::chacha20::ChaCha20; #[test] fn test() { let key = [0u32; 8]; let nonce = [0x0u32; 3]; let mut cc20 = ChaCha20::new(key, 1, nonce); let result = cc20.encrypt("1234".as_bytes()); println!("{:?}", result); } }