hc_256/
lib.rs

1//! Implementation of the [HC-256] stream cipher.
2//!
3//! Cipher functionality is accessed using traits from re-exported [`cipher`] crate.
4//!
5//! # ⚠️ Security Warning: Hazmat!
6//!
7//! This crate does not ensure ciphertexts are authentic! Thus ciphertext integrity
8//! is not verified, which can lead to serious vulnerabilities!
9//!
10//! USE AT YOUR OWN RISK!
11//!
12//! # Example
13//! ```
14//! use hc_256::Hc256;
15//! // Import relevant traits
16//! use hc_256::cipher::{KeyIvInit, StreamCipher};
17//! use hex_literal::hex;
18//!
19//! let key = [0x42; 32];
20//! let nonce = [0x24; 32];
21//! let plaintext = hex!("00010203 04050607 08090A0B 0C0D0E0F");
22//! let ciphertext = hex!("ca982177 325cd40e bc208045 066c420f");
23//!
24//! // Key and IV must be references to the `GenericArray` type.
25//! // Here we use the `Into` trait to convert arrays into it.
26//! let mut cipher = Hc256::new(&key.into(), &nonce.into());
27//!
28//! let mut buffer = plaintext.clone();
29//!
30//! // apply keystream (encrypt)
31//! cipher.apply_keystream(&mut buffer);
32//! assert_eq!(buffer, ciphertext);
33//!
34//! let ciphertext = buffer.clone();
35//!
36//! // decrypt ciphertext by applying keystream again
37//! let mut cipher = Hc256::new(&key.into(), &nonce.into());
38//! cipher.apply_keystream(&mut buffer);
39//! assert_eq!(buffer, plaintext);
40//!
41//! // stream ciphers can be used with streaming messages
42//! let mut cipher = Hc256::new(&key.into(), &nonce.into());
43//! for chunk in buffer.chunks_mut(3) {
44//!     cipher.apply_keystream(chunk);
45//! }
46//! assert_eq!(buffer, ciphertext);
47//! ```
48//!
49//! [HC-256]: https://en.wikipedia.org/wiki/HC-256
50
51#![no_std]
52#![cfg_attr(docsrs, feature(doc_cfg))]
53#![doc(
54    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg",
55    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg",
56    html_root_url = "https://docs.rs/hc-256/0.5.0"
57)]
58#![forbid(unsafe_code)]
59#![warn(missing_docs, rust_2018_idioms)]
60
61pub use cipher;
62
63use cipher::{
64    consts::{U1, U32, U4},
65    AlgorithmName, Block, BlockSizeUser, Iv, IvSizeUser, Key, KeyIvInit, KeySizeUser,
66    ParBlocksSizeUser, StreamBackend, StreamCipherCore, StreamCipherCoreWrapper, StreamClosure,
67};
68use core::fmt;
69
70#[cfg(feature = "zeroize")]
71use cipher::zeroize::{Zeroize, ZeroizeOnDrop};
72
73const TABLE_SIZE: usize = 1024;
74const TABLE_MASK: usize = TABLE_SIZE - 1;
75const INIT_SIZE: usize = 2660;
76const KEY_BITS: usize = 256;
77const KEY_WORDS: usize = KEY_BITS / 32;
78const IV_BITS: usize = 256;
79const IV_WORDS: usize = IV_BITS / 32;
80
81/// The HC-256 stream cipher core
82pub type Hc256 = StreamCipherCoreWrapper<Hc256Core>;
83
84/// The HC-256 stream cipher core
85pub struct Hc256Core {
86    ptable: [u32; TABLE_SIZE],
87    qtable: [u32; TABLE_SIZE],
88    idx: u32,
89}
90
91impl BlockSizeUser for Hc256Core {
92    type BlockSize = U4;
93}
94
95impl KeySizeUser for Hc256Core {
96    type KeySize = U32;
97}
98
99impl IvSizeUser for Hc256Core {
100    type IvSize = U32;
101}
102
103impl KeyIvInit for Hc256Core {
104    fn new(key: &Key<Self>, iv: &Iv<Self>) -> Self {
105        fn f1(x: u32) -> u32 {
106            x.rotate_right(7) ^ x.rotate_right(18) ^ (x >> 3)
107        }
108
109        fn f2(x: u32) -> u32 {
110            x.rotate_right(17) ^ x.rotate_right(19) ^ (x >> 10)
111        }
112
113        let mut out = Self {
114            ptable: [0; TABLE_SIZE],
115            qtable: [0; TABLE_SIZE],
116            idx: 0,
117        };
118        let mut data = [0; INIT_SIZE];
119
120        for i in 0..KEY_WORDS {
121            data[i] = key[4 * i] as u32 & 0xff
122                | (key[(4 * i) + 1] as u32 & 0xff) << 8
123                | (key[(4 * i) + 2] as u32 & 0xff) << 16
124                | (key[(4 * i) + 3] as u32 & 0xff) << 24;
125        }
126
127        for i in 0..IV_WORDS {
128            data[i + KEY_WORDS] = iv[4 * i] as u32 & 0xff
129                | (iv[(4 * i) + 1] as u32 & 0xff) << 8
130                | (iv[(4 * i) + 2] as u32 & 0xff) << 16
131                | (iv[(4 * i) + 3] as u32 & 0xff) << 24;
132        }
133
134        for i in IV_WORDS + KEY_WORDS..INIT_SIZE {
135            data[i] = f2(data[i - 2])
136                .wrapping_add(data[i - 7])
137                .wrapping_add(f1(data[i - 15]))
138                .wrapping_add(data[i - 16])
139                .wrapping_add(i as u32);
140        }
141
142        out.ptable[..TABLE_SIZE].clone_from_slice(&data[512..(TABLE_SIZE + 512)]);
143        out.qtable[..TABLE_SIZE].clone_from_slice(&data[1536..(TABLE_SIZE + 1536)]);
144
145        out.idx = 0;
146
147        for _ in 0..4096 {
148            out.gen_word();
149        }
150
151        out
152    }
153}
154
155impl StreamCipherCore for Hc256Core {
156    #[inline(always)]
157    fn remaining_blocks(&self) -> Option<usize> {
158        None
159    }
160
161    fn process_with_backend(&mut self, f: impl StreamClosure<BlockSize = Self::BlockSize>) {
162        f.call(&mut Backend(self));
163    }
164}
165
166impl AlgorithmName for Hc256Core {
167    fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
168        f.write_str("Hc256")
169    }
170}
171
172impl fmt::Debug for Hc256Core {
173    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
174        f.write_str("Hc256Core { ... }")
175    }
176}
177
178impl Hc256Core {
179    #[inline]
180    fn g1(&self, x: u32, y: u32) -> u32 {
181        (x.rotate_right(10) ^ y.rotate_right(23))
182            .wrapping_add(self.qtable[(x ^ y) as usize & TABLE_MASK])
183    }
184
185    #[inline]
186    fn g2(&self, x: u32, y: u32) -> u32 {
187        (x.rotate_right(10) ^ y.rotate_right(23))
188            .wrapping_add(self.ptable[(x ^ y) as usize & TABLE_MASK])
189    }
190
191    #[inline]
192    fn h1(&self, x: u32) -> u32 {
193        self.qtable[(x & 0xff) as usize]
194            .wrapping_add(self.qtable[(256 + ((x >> 8) & 0xff)) as usize])
195            .wrapping_add(self.qtable[(512 + ((x >> 16) & 0xff)) as usize])
196            .wrapping_add(self.qtable[(768 + ((x >> 24) & 0xff)) as usize])
197    }
198
199    #[inline]
200    fn h2(&self, x: u32) -> u32 {
201        self.qtable[(x & 0xff) as usize]
202            .wrapping_add(self.qtable[(256 + ((x >> 8) & 0xff)) as usize])
203            .wrapping_add(self.qtable[(512 + ((x >> 16) & 0xff)) as usize])
204            .wrapping_add(self.qtable[(768 + ((x >> 24) & 0xff)) as usize])
205    }
206
207    fn gen_word(&mut self) -> u32 {
208        let i = self.idx as usize;
209        let j = self.idx as usize & TABLE_MASK;
210
211        self.idx = (self.idx + 1) & (2048 - 1);
212
213        if i < 1024 {
214            self.ptable[j] = self.ptable[j]
215                .wrapping_add(self.ptable[j.wrapping_sub(10) & TABLE_MASK])
216                .wrapping_add(self.g1(
217                    self.ptable[j.wrapping_sub(3) & TABLE_MASK],
218                    self.ptable[j.wrapping_sub(1023) & TABLE_MASK],
219                ));
220
221            self.h1(self.ptable[j.wrapping_sub(12) & TABLE_MASK]) ^ self.ptable[j]
222        } else {
223            self.qtable[j] = self.qtable[j]
224                .wrapping_add(self.qtable[j.wrapping_sub(10) & TABLE_MASK])
225                .wrapping_add(self.g2(
226                    self.qtable[j.wrapping_sub(3) & TABLE_MASK],
227                    self.qtable[j.wrapping_sub(1023) & TABLE_MASK],
228                ));
229
230            self.h2(self.qtable[j.wrapping_sub(12) & TABLE_MASK]) ^ self.qtable[j]
231        }
232    }
233}
234
235#[cfg(feature = "zeroize")]
236#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))]
237impl Drop for Hc256Core {
238    fn drop(&mut self) {
239        self.ptable.zeroize();
240        self.qtable.zeroize();
241        self.idx.zeroize();
242    }
243}
244
245#[cfg(feature = "zeroize")]
246#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))]
247impl ZeroizeOnDrop for Hc256Core {}
248
249struct Backend<'a>(&'a mut Hc256Core);
250
251impl<'a> BlockSizeUser for Backend<'a> {
252    type BlockSize = <Hc256Core as BlockSizeUser>::BlockSize;
253}
254
255impl<'a> ParBlocksSizeUser for Backend<'a> {
256    type ParBlocksSize = U1;
257}
258
259impl<'a> StreamBackend for Backend<'a> {
260    #[inline(always)]
261    fn gen_ks_block(&mut self, block: &mut Block<Self>) {
262        block.copy_from_slice(&self.0.gen_word().to_le_bytes());
263    }
264}