why2/lib.rs
1/*
2This is part of WHY2
3Copyright (C) 2022-2026 Václav Šmejkal
4
5This program is free software: you can redistribute it and/or modify
6it under the terms of the GNU General Public License as published by
7the Free Software Foundation, either version 3 of the License, or
8(at your option) any later version.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program. If not, see <https://www.gnu.org/licenses/>.
17*/
18
19//! # WHY2
20//!
21//! WHY2 is a modern, fast, and secure encryption crate designed for privacy-first applications.
22//!
23//! ## Design Overview
24//! The WHY2 encryption algorithm is loosely inspired by AES, but with a twist. Instead of relying on S-boxes,
25//! WHY2 uses a nonlinear ARX-style transformation (Addition, Rotation, XOR) for symmetric diffusion.
26//!
27//! Key mechanics include:
28//! - **Grid-based State**: Input and key data are formatted into 2D grids of 64-bit cells.
29//! - **Key Expansion**: The key grid is shuffled and seeded to generate round keys.
30//! - **Nonlinear Mixing**: Each round applies a transformation to the input grids using round tweaks to ensure variability.
31//!
32//! WHY2 also powers a minimalist text and voice chat application built for maximal privacy, designed for self-hosting
33//! by individuals or small groups.
34//!
35//! ## Features
36//! - Grid-based encryption with customizable layout
37//! - ARX-style nonlinear mixing instead of S-boxes
38//! - Round-key generation from seeded, shuffled keys
39//! - Lightweight encrypted text and voice chat backend for private deployments
40//! - Maximal customization
41//!
42//! ## Cargo Features
43//!
44//! This crate allows selective enabling of components to keep the build lightweight.
45//!
46//! - **`constant-time`** (default):
47//! Enables constant-time comparison for cryptographic operations using the [`subtle`] crate.
48//! Disabling this may improve performance on non-sensitive data but opens the system to timing attacks.
49//!
50//! - **`client`**:
51//! Enables the terminal-based client application with interactive interface and real-time voice chat support.
52//!
53//! - **`server`**:
54//! Enables the relay server logic for routing encrypted messages between clients.
55//! *Use this if you are building a custom node or hosting a relay.*
56//!
57//! - **`legacy`**:
58//! Enables the deprecated `legacy` module containing older, insecure versions of the encryption routines.
59//! This feature should only be used for migration or compatibility testing.
60//!
61//! ## Philosophy
62//! - **Privacy is a right**, not a subscription feature.
63//! - **No government insight**: no telemetry, no backdoors, no metadata leakage.
64//! - **No payment required**: encryption should be free as in freedom.
65//!
66//! ## Terminology
67//!
68//! The codebase is organized to distinguish between the current implementation and deprecated versions:
69//!
70//! * **REX**: Refers to the modern, secure implementation of the WHY2 algorithm.
71//! These are the modules exposed directly at the crate root (e.g., [`encrypter`], [`decrypter`]).
72//! * **Legacy**: Refers to older, deprecated encryption routines found in the [`legacy`] module.
73//! These are retained for compatibility but are considered insecure.
74//!
75//! ## Security Disclaimer
76//!
77//! WHY2 is an experimental encryption algorithm. While it draws inspiration from established designs like AES,
78//! **it has not undergone formal cryptographic review or extensive academic analysis**.
79//!
80//! As such, it should **not be considered suitable for high-assurance or production-grade cryptographic applications** where
81//! proven security guarantees are required. Use at your own discretion, and always evaluate your threat model carefully.
82//!
83//! ## License
84//! WHY2 is licensed under the GNU GPLv3. You are free to use, modify, and redistribute it
85//! under the terms of the license. See <https://www.gnu.org/licenses/> for details.
86
87//MODULES
88#[cfg(feature = "auth")]
89pub mod auth;
90pub mod crypto;
91pub mod decrypter;
92pub mod encrypter;
93pub mod options;
94
95#[cfg(feature = "legacy")]
96#[deprecated(since = "0.2.0-rex", note = "Legacy encryption is unsecure. Use REX module (why2::core) instead.")]
97pub mod legacy;
98
99/*************************************************************************************************/
100
101use std::
102{
103 result,
104 ops::Range,
105 error::Error,
106 iter::Flatten,
107 array::IntoIter,
108 slice::{ Iter, IterMut },
109 ops::
110 {
111 Index,
112 IndexMut,
113 BitXorAssign,
114 },
115
116 fmt::
117 {
118 Display,
119 Formatter,
120 Result,
121 LowerHex,
122 },
123};
124
125use zeroize::Zeroize;
126
127#[cfg(feature = "constant-time")]
128use subtle::
129{
130 Choice,
131 ConstantTimeEq,
132 ConditionallySelectable,
133};
134
135//TYPES
136/// A 2D matrix of 64-bit signed integers used as the core data structure in WHY2 encryption.
137///
138/// The [`Grid`] represents either input data or a key, formatted into rows and columns of `i64` cells.
139/// All transformations—round mixing, key scheduling, and nonlinear diffusion—operate directly on this structure.
140///
141/// Grids are flexible and can be transformed in-place.
142/// This abstraction allows WHY2 to generalize encryption over variable-sized blocks of dimension $W \times H$.
143///
144/// # Grid Size Consistency
145///
146/// WHY2 requires that the same grid dimensions ($W \times H$) be used consistently
147/// throughout encryption and decryption. Mixing grid sizes within a single session or
148/// across rounds is unsupported and may lead to incorrect results or undefined behavior.
149#[derive(Clone, Debug, Zeroize)]
150#[zeroize(drop)]
151pub struct Grid<const W: usize, const H: usize>([[i64; W]; H]); //GRID FOR REX DATA
152
153/// Represents structured errors that can occur during Grid operations.
154///
155/// This enum replaces generic string errors to provide zero-allocation error handling
156/// and programmatic access to failure details. It is primarily used during
157/// Grid initialization and serialization.
158#[derive(Debug, Clone, PartialEq)]
159pub enum GridError
160{
161 /// Indicates that the requested Grid dimensions are invalid for cryptographic operations.
162 ///
163 /// This error occurs when creating a new Grid if the dimensions do not allow
164 /// for sufficient diffusion (e.g., width is 1, or total area is too small).
165 ///
166 /// # Fields
167 /// - `width`: The width (columns) of the attempted Grid.
168 /// - `height`: The height (rows) of the attempted Grid.
169 InvalidDimensions
170 {
171 width: usize,
172 height: usize,
173 },
174
175 /// Indicates that the input byte sequence length does not align with [`Grid`] requirements.
176 ///
177 /// This error occurs during deserialization (e.g., `from_bytes`) when the provided
178 /// data length is not a multiple of the [`Grid`]'s total byte size ($W \times H \times 8$ bytes).
179 ///
180 /// # Fields
181 /// - `expected_mod`: The required modulus (block size in bytes).
182 /// - `actual_len`: The actual length of the provided byte vector.
183 InvalidByteLength
184 {
185 expected_mod: usize,
186 actual_len: usize,
187 },
188
189 /// Indicates that the provided raw key has an incorrect length.
190 ///
191 /// The WHY2 key scheduling algorithm requires the input key vector to be exactly
192 /// twice the size of the [`Grid`] area ($2 \times W \times H$). This allows for the initial
193 /// folding and mixing of key parts (low and high components).
194 ///
195 /// # Fields
196 /// - `expected_len`: The required key length (number of `i64` elements).
197 /// - `actual_len`: The length of the provided key vector.
198 InvalidKeyLength
199 {
200 expected_len: usize,
201 actual_len: usize,
202 },
203}
204
205//IMPLEMENTATIONS
206/// Implementation of core Grid operations for fixed-size grids.
207///
208/// This block defines methods for `Grid<W, H>`, where `W` and `H` are compile-time constants
209/// representing the grid's width and height. All transformations — such as ARX mixing, key application,
210/// and round-based encryption - operate on grids of this fixed shape.
211///
212/// # Type Parameters
213/// - `W`: Number of columns (width), must be a compile-time constant.
214/// - `H`: Number of rows (height), must be a compile-time constant.
215///
216/// # Notes
217/// - Grid dimensions must remain consistent across encryption and decryption.
218impl<const W: usize, const H: usize> Grid<W, H>
219{
220 /// Creates a new Grid initialized with zeroes.
221 ///
222 /// This constructor sets up an empty Grid where all cells are set to `0`.
223 ///
224 /// # Returns
225 /// - Ok(`Grid`) instance with all values set to zero and area is larger than 1.
226 /// - Err(`GridError`) if the area is 1
227 ///
228 /// # Notes
229 /// - This method does not perform any encryption or transformation.
230 #[inline]
231 pub fn new() -> result::Result<Self, GridError>
232 {
233 let area = W * H;
234 if area > 1 && W > 1
235 {
236 Ok(Self([[0i64; W]; H]))
237 } else
238 {
239 Err(GridError::InvalidDimensions { width: W, height: H })
240 }
241 }
242
243 /// Initializes a key Grid from a vector of signed 64-bit integers.
244 ///
245 /// Each cell is built from two key parts using nonlinear mixing.
246 /// addition, XOR, and rotation. This improves diffusion and avoids
247 /// simple linear patterns in the key.
248 ///
249 /// # Algorithm
250 /// For each cell index $i$, the key parts $A$ and $B$ are derived from the input vector $V$:
251 ///
252 /// $$ A = (V_i + V_{i + \text{Area}}) \lll (i \bmod 64) $$
253 ///
254 /// $$ B = (V_i \oplus V_{i + \text{Area}}) \ggg (i \bmod 64) $$
255 ///
256 /// where $\lll$ and $\ggg$ denote left and right rotation respectively.
257 ///
258 /// The final grid value is computed as:
259 /// $$ Grid_{x,y} = A \oplus B \oplus i $$
260 ///
261 /// # Parameters
262 /// - `vec`: A slice of signed 64-bit integers representing the raw key.
263 ///
264 /// # Returns
265 /// - Ok(`Grid`) with mixed key values if dimensions are valid.
266 /// - Err(`GridError`) if the grid area is too small.
267 pub fn from_key(vec: &[i64]) -> result::Result<Self, GridError>
268 {
269 //GRID OPTIONS
270 let grid_area = W * H;
271
272 //SHAPE
273 let mut key_grid = Self::new()?;
274 for i in 0..grid_area
275 {
276 //APPLY NONLINEAR MIX TO KEY
277 let mut a = vec[i].wrapping_add(vec[i + grid_area]);
278 let mut b = vec[i] ^ vec[i + grid_area];
279 let rot = (i % 64) as u32;
280
281 //ROTATE
282 a = a.rotate_left(rot);
283 b = b.rotate_right(rot);
284
285 //APPLY
286 key_grid[i / W][i % W] = a ^ b ^ (i as i64);
287 }
288
289 Ok(key_grid)
290 }
291
292 /// Initializes [`Grid`] from vector of unsigned 8-bit integers.
293 ///
294 /// This function constructs [`Grid`] by chunking the input vector into `i64` cells. It expects
295 /// exactly $W \times H \times 8$ bytes and returns an error if the input length does not match.
296 ///
297 /// # Parameters
298 /// - `bytes`: A byte slice (`&[8u]`) containing the raw data.
299 ///
300 /// # Returns
301 /// - Ok(Vec<`Grid`>) if the byte length matches the expected grid size
302 /// - Err(`GridError`) if the input length is not divisible by matrix size.
303 ///
304 /// # Notes
305 /// - No transformation is applied
306 /// - Use this for raw Grid construction, not for secure key loading
307 pub fn from_bytes(bytes: &[u8]) -> result::Result<Vec<Self>, GridError>
308 {
309 let matrix_size = W * H * 8; //EACH i64 IS 8 BYTES
310
311 //CHECK FOR VALID GRID
312 if bytes.len() % matrix_size != 0
313 {
314 return Err(GridError::InvalidByteLength { expected_mod: matrix_size, actual_len: bytes.len() });
315 }
316
317 bytes.chunks(matrix_size).map(|chunk|
318 {
319 let mut grid = Grid::new()?;
320 for j in 0..H
321 {
322 for i in 0..W
323 {
324 let start = (j * W + i) * 8;
325 let slice = &chunk[start..start + 8];
326 grid[j][i] = i64::from_be_bytes(slice.try_into().unwrap());
327 }
328 }
329
330 Ok(grid)
331 }).collect()
332 }
333
334 /// Returns an iterator over rows in the Grid
335 #[inline]
336 pub fn iter(&self) -> Iter<'_, [i64; W]>
337 {
338 self.0.iter()
339 }
340
341 /// Returns a mutable iterator over rows in the Grid
342 #[inline]
343 pub fn iter_mut(&mut self) -> IterMut<'_, [i64; W]>
344 {
345 self.0.iter_mut()
346 }
347
348 /// Returns width (number of columns) in the Grid
349 #[inline]
350 pub fn width(&self) -> usize
351 {
352 W
353 }
354
355 /// Returns height (number of rows) in the Grid
356 #[inline]
357 pub fn height(&self) -> usize
358 {
359 H
360 }
361
362 //ENCRYPTION
363 //PRIVATE HANDLERS
364 fn mix_matrix_handler
365 (
366 &mut self,
367 i_range: impl Iterator<Item = usize>,
368 j_range: impl Fn(usize) -> Range<usize>,
369 key_grid: &Grid<W, H>,
370 )
371 {
372 for i in i_range
373 {
374 for j in j_range(i)
375 {
376 let scalar = key_grid[i][j]; //USE KEY VALUE AS COEFFICIENT
377
378 //LWE NOISE
379 let noise = key_grid[j][i].wrapping_add(i as i64);
380
381 for col in 0..self.width()
382 {
383 let val_j = self[j][col];
384 let mixing = val_j.wrapping_mul(scalar).wrapping_add(noise); //ADD NOISE
385
386 //Row[i] = Row[i] + (Row[j] * scalar + noise)
387 self[i][col] = self[i][col].wrapping_add(mixing); //MIX
388 }
389 }
390 }
391 }
392
393 fn mix_diagonals_handler(&mut self, row: usize, col: usize) //MIX DIAGONALS IN GRID
394 {
395 //COPY PARAMETERS AS MUTABLE
396 let mut row = row;
397 let mut col = col;
398
399 //WALK ALONG THIS DIAGONAL
400 while row < self.height() - 1 && col < self.width() - 1
401 {
402 let next_row = row + 1;
403 let next_col = col + 1;
404
405 //XOR CURRENT CELL WITH NEXT DIAGONAL CELL
406 self[row][col] ^= self[next_row][next_col];
407
408 row = next_row;
409 col = next_col;
410 }
411 }
412
413 //PUBLIC
414 /// Computes the cell-wise XOR of two Grids.
415 ///
416 /// This function takes two [`Grid`]s of equal dimensions and modifies the [`Grid`] in-place:
417 /// $$ G_{x,y} = G_{x,y} \oplus K_{x,y} $$
418 /// It is used in WHY2 for mixing round keys, applying masks, or combining intermediate states.
419 ///
420 /// # Parameters
421 /// - `key_grid`: Input Grid for XOR
422 #[inline]
423 pub fn xor_grids(&mut self, key_grid: &Grid<W, H>)
424 {
425 for y in 0..(self.height()) //Y DIM
426 {
427 for x in 0..(self.width()) //X DIM
428 {
429 //XOR
430 self[y][x] ^= key_grid[y][x];
431 }
432 }
433 }
434
435 /// Applies nonlinear ARX-style mixing to each cell in the grid.
436 ///
437 /// This transformation introduces symmetric diffusion by modifying each `i64` cell
438 /// using a combination of addition, rotation, and XOR operations. The process is
439 /// round-dependent and designed to obscure bit patterns across the [`Grid`].
440 ///
441 /// # Parameters
442 /// - `round`: A round index used to tweak the transformation logic.
443 ///
444 /// # Behavior
445 /// Each 64-bit cell is split into two 32-bit halves $v_0, v_1$.
446 /// For [`SUBCELL_ROUNDS`](crate::options::SUBCELL_ROUNDS) iterations, the Feistel-like network applies:
447 ///
448 /// $$ v_0 \leftarrow v_0 + (((v_1 \ll 4) \oplus (v_1 \gg 5)) + v_1) \oplus \text{sum} $$
449 ///
450 /// $$ v_1 \leftarrow v_1 + (((v_0 \ll 4) \oplus (v_0 \gg 5)) + v_0) \oplus \text{sum} $$
451 ///
452 /// where $\text{sum}$ is incremented by a constant $\delta = $ [`SUBCELL_DELTA`](crate::options::SUBCELL_DELTA) in each round:
453 ///
454 /// $$ \text{sum} \leftarrow \text{sum} + \delta $$
455 ///
456 /// # Notes
457 /// - This method mutates the [`Grid`] in-place.
458 /// - It is inspired by TEA/XTEA but adapted for WHY2’s [`Grid`] architecture.
459 /// - The transformation is deterministic for a given round and [`Grid`] state.
460 pub fn subcell(&mut self, round: usize)
461 {
462 //APPLY ON EACH CELL
463 for col in self.iter_mut()
464 {
465 for cell in col
466 {
467 //SPLIT CELL TO HIGH32 AND LOW32
468 let x = *cell as u64;
469 let mut v0 = (x & 0xFFFF_FFFF) as u32; //LOW
470 let mut v1 = ((x >> 32) & 0xFFFF_FFFF) as u32; //HIGH
471
472 //XOR TWEAK -> MAKE ROUNDS DIFFERENT
473 v0 ^= round as u32;
474
475 //ARX-LIKE ROUNDS (INSPIRED BY XTEA/TEA)
476 let mut sum: u32 = 0;
477 for _ in 0..(options::SUBCELL_ROUNDS)
478 {
479 sum = sum.wrapping_add(options::SUBCELL_DELTA);
480
481 //MIX V1 INTO V0
482 v0 = v0.wrapping_add(((v1 << 4) ^ (v1 >> 5)).wrapping_add(v1) ^ sum);
483
484 //MIX V0 INTO V1
485 v1 = v1.wrapping_add(((v0 << 4) ^ (v0 >> 5)).wrapping_add(v0) ^ sum);
486 }
487
488 //XOR TWEAK
489 v1 ^= round as u32;
490
491 //REBUILD AND APPLY
492 let out = ((v1 as u64) << 32) | (v0 as u64);
493 *cell = out as i64;
494 }
495 }
496 }
497
498 /// Applies row-wise shifting to the [`Grid`] based on a key [`Grid`].
499 ///
500 /// This transformation rotates each row of the [`Grid`] by a variable amount derived from
501 /// the corresponding row in `key_grid`. The shift amount $S_i$ for row $i$ is computed as:
502 ///
503 /// $$ S_i = \left( \bigoplus_{j=0}^{W-1} K_{i,j} \right) \bmod W $$
504 ///
505 /// # Behavior
506 /// - Each row is rotated left by $S_i$.
507 ///
508 /// # Notes
509 /// - This method mutates the grid in-place.
510 #[inline]
511 pub fn shift_rows(&mut self, key_grid: &Grid<W, H>)
512 {
513 #[cfg(not(feature = "constant-time"))]
514 let rows = self.width() as i64;
515
516 //SHIFT EACH ROW
517 for (i, row) in self.iter_mut().enumerate()
518 {
519 let hash_chunk = key_grid[i].iter().fold(0i64, |acc, &x| acc ^ x);
520 let shift: usize;
521
522 #[cfg(feature = "constant-time")]
523 {
524 shift = ((hash_chunk as u64 as u128 * W as u128) >> 64) as usize;
525 }
526
527 #[cfg(not(feature = "constant-time"))]
528 {
529 //SPLIT key_grid TO 8 PARTS & XOR EACH VALUE TO GET SHIFT
530 shift = hash_chunk.rem_euclid(rows) as usize;
531 }
532
533 //ROTATE THE ROW
534 #[cfg(feature = "constant-time")]
535 {
536 let mut new_row = [0i64; W]; //BUFFER
537
538 for i in 0..W
539 {
540 let target_src_idx = (i + shift) % W;
541 for src_idx in 0..W
542 {
543 new_row[i].conditional_assign(&row[src_idx], src_idx.ct_eq(&target_src_idx));
544 }
545 }
546
547 //WRITE RESULT
548 *row = new_row;
549 }
550
551 #[cfg(not(feature = "constant-time"))]
552 {
553 row.rotate_left(shift);
554 }
555 }
556 }
557
558 /// Applies column-wise mixing to the grid using linear XOR diffusion.
559 ///
560 /// This transformation modifies each column by XORing it with its adjacent column,
561 /// introducing horizontal diffusion across the grid.
562 ///
563 /// # Behavior
564 /// For each column $c \in \{0, \dots, W-1\}$, compute:
565 /// $$ G_{r, c} \leftarrow G_{r, c} \oplus G_{r, (c + 1) \bmod W} $$
566 ///
567 /// # Notes
568 /// - This method mutates the grid in-place.
569 #[inline]
570 pub fn mix_columns(&mut self)
571 {
572 for col in 0..(self.width())
573 {
574 let next_col = (col + 1) % W;
575 for row in 0..self.height()
576 {
577 self[row][col] ^= self[row][next_col];
578 }
579 }
580 }
581
582 /// Applies a matrix-based affine transformation to mix rows.
583 ///
584 /// This function treats the [`Grid`] as a matrix and multiplies it by a key-dependent transformation
585 /// matrix, while adding a deterministic noise term. This converts the transformation from
586 /// purely linear ($Ax$) to affine:
587 ///
588 /// $$ G' = (L \cdot U) \cdot G + \text{noise} $$
589 ///
590 /// To ensure the operation is reversible (invertible) in modular arithmetic, the transformation
591 /// is constructed as a product of a **Lower triangular matrix ($L$)** and an **Upper triangular matrix ($U$)**.
592 ///
593 /// # Behavior
594 /// - **Lower Pass ($L$):** Each row adds a multiple of previous rows ($i > j$).
595 /// - **Upper Pass ($U$):** Each row adds a multiple of following rows ($i < j$).
596 ///
597 /// # Notes
598 /// - This method mutates the [`Grid`] in-place.
599 /// - All additions and multiplications are wrapping (modulo $2^{64}$).
600 pub fn mix_matrix(&mut self, key_grid: &Grid<W, H>)
601 {
602 //LOWER TRIANGULAR PASS (TOP -> DOWN)
603 self.mix_matrix_handler(1..H, |i| 0..i, key_grid);
604
605 //UPPER TRIANGULAR PASS (BOTTOM -> UP)
606 self.mix_matrix_handler((0..H - 1).rev(), |i| (i + 1)..H, key_grid);
607 }
608
609 /// Applies diagonal-wise mixing to the grid using XOR diffusion.
610 ///
611 /// This transformation modifies each diagonal line by XORing each element with
612 /// the next element along that diagonal.
613 ///
614 /// # Behavior
615 /// - Processes all diagonals parallel to the main diagonal.
616 /// - For each cell $(r, c)$, compute:
617 /// $$ G_{r,c} \leftarrow G_{r,c} \oplus G_{r+1, c+1} $$
618 ///
619 /// # Notes
620 /// - This method mutates the grid in-place.
621 #[inline]
622 pub fn mix_diagonals(&mut self)
623 {
624 //PROCESS DIAGONALS BEGINNING IN THE FIRST COLUMN
625 for start_row in 0..self.height()
626 {
627 self.mix_diagonals_handler(start_row, 0);
628 }
629
630 //PROCESS DIAGONALS STARTING FROM THE FIRST ROW (EXCLUDING [0,0])
631 for start_col in 1..self.width()
632 {
633 self.mix_diagonals_handler(0, start_col);
634 }
635 }
636
637 //UTILS
638 /// Increments the [`Grid`] value by a specified amount, treating it as a large Little-Endian integer.
639 ///
640 /// This method performs modular addition of a 64-bit value to the multi-precision integer
641 /// represented by the grid:
642 ///
643 /// $$ G \leftarrow (G + \text{amount}) \bmod 2^{64 \times W \times H} $$
644 ///
645 /// # Parameters
646 /// - `amount`: The unsigned 64-bit value to add to the grid.
647 /// - Pass `1` for standard sequential counter increment.
648 /// - Pass a block index $i$ (offset) when initializing parallel CTR counters.
649 ///
650 /// # Behavior
651 /// - The [`Grid`] is treated as a single large integer in **Little-Endian** format
652 /// (the cell at `[0][0]` is the least significant limb).
653 /// - The `amount` is added to the first cell, and any resulting carry is propagated
654 /// sequentially through the remaining cells.
655 /// - If the entire grid overflows (wraps around), the value resets modulo the grid size.
656 ///
657 /// # Security
658 /// - When the **`constant-time`** feature is enabled, this function always iterates
659 /// through the entire grid to prevent timing leaks via carry propagation analysis.
660 pub fn increment(&mut self, amount: &mut u64)
661 {
662 for row in self.iter_mut()
663 {
664 for cell in row.iter_mut()
665 {
666 let (result, overflow) = (*cell as u64).overflowing_add(*amount);
667 *cell = result as i64;
668
669 //NO CARRY (OVERFLOW), DONE
670 #[cfg(not(feature = "constant-time"))]
671 {
672 if !overflow { return; }
673 *amount = 1;
674 }
675
676 #[cfg(feature = "constant-time")]
677 {
678 *amount = overflow as u64;
679 }
680 }
681 }
682 }
683}
684
685//INTO ITERATOR
686impl<const W: usize, const H: usize> IntoIterator for Grid<W, H>
687{
688 //TYPES
689 type Item = i64;
690 type IntoIter = Flatten<IntoIter<[i64; W], H>>;
691
692 //INTO ITERATOR
693 #[inline]
694 fn into_iter(self) -> Self::IntoIter
695 {
696 self.0.into_iter().flatten()
697 }
698}
699
700//INDEXING
701impl<const W: usize, const H: usize> Index<usize> for Grid<W, H>
702{
703 type Output = [i64; W];
704
705 #[inline]
706 fn index(&self, y: usize) -> &Self::Output
707 {
708 &self.0[y]
709 }
710}
711
712//MUTABLE INDEXING
713impl<const W: usize, const H: usize> IndexMut<usize> for Grid<W, H>
714{
715 #[inline]
716 fn index_mut(&mut self, y: usize) -> &mut Self::Output
717 {
718 &mut self.0[y]
719 }
720}
721
722//XOR ASSIGN
723impl<const W: usize, const H: usize> BitXorAssign<&Grid<W, H>> for Grid<W, H>
724{
725 #[inline]
726 fn bitxor_assign(&mut self, rhs: &Grid<W, H>)
727 {
728 self.xor_grids(rhs);
729 }
730}
731
732//CONSTANT-TIME EQ
733#[cfg(feature = "constant-time")]
734impl<const W: usize, const H: usize> ConstantTimeEq for Grid<W, H>
735{
736 fn ct_eq(&self, other: &Self) -> Choice
737 {
738 let mut result = Choice::from(1);
739
740 for (row_a, row_b) in self.iter().zip(other.iter())
741 {
742 for (cell_a, cell_b) in row_a.iter().zip(row_b.iter())
743 {
744 result &= cell_a.ct_eq(cell_b);
745 }
746 }
747
748 result
749 }
750}
751
752impl<const W: usize, const H: usize> PartialEq for Grid<W, H>
753{
754 fn eq(&self, other: &Self) -> bool
755 {
756 #[cfg(feature = "constant-time")]
757 {
758 self.ct_eq(other).into()
759 }
760
761 #[cfg(not(feature = "constant-time"))]
762 {
763 self.0 == other.0
764 }
765 }
766}
767
768//DISPLAY
769impl<const W: usize, const H: usize> Display for Grid<W, H>
770{
771 fn fmt(&self, f: &mut Formatter<'_>) -> Result
772 {
773 //CONVERT EACH VALUE TO 4 LINES
774 let cells: Vec<Vec<[String; 4]>> = self.iter().map(|row|
775 {
776 row.iter().map(|val|
777 {
778 let s = val.to_string();
779 let chunk_size = (s.len() + 3) / 4;
780 let mut lines = [String::new(), String::new(), String::new(), String::new()];
781
782 for (i, chunk) in s.chars().collect::<Vec<_>>().chunks(chunk_size).enumerate()
783 {
784 lines[i] = chunk.iter().collect();
785 }
786
787 lines
788 }).collect()
789 }).collect();
790
791 //DETERMINE MAX WIDTH
792 let max_width = cells.iter()
793 .flat_map(|row| row.iter())
794 .flat_map(|lines| lines.iter())
795 .map(|line| line.len())
796 .max()
797 .unwrap_or(1);
798
799 //BUILD HORIZONTAL BORDER
800 let border = format!
801 (
802 "+{}+\n",
803 (0..self.width()).map(|_| "-".repeat(max_width + 2)).collect::<Vec<_>>().join("+")
804 );
805
806 //PRINT
807 for row in &cells
808 {
809 f.write_str(&border)?;
810 for line_idx in 0..4
811 {
812 for cell in row
813 {
814 write!(f, "| {:>width$} ", cell[line_idx], width = max_width)?;
815 }
816
817 writeln!(f, "|")?;
818 }
819 }
820
821 f.write_str(&border)
822 }
823}
824
825impl<const W: usize, const H: usize> LowerHex for Grid<W, H>
826{
827 fn fmt(&self, f: &mut Formatter<'_>) -> Result
828 {
829 for row in self.iter()
830 {
831 for cell in row
832 {
833 write!(f, "{:016x}", cell)?;
834 }
835 }
836
837 Ok(())
838 }
839}
840
841impl Display for GridError
842{
843 fn fmt(&self, f: &mut Formatter<'_>) -> Result
844 {
845 match self
846 {
847 GridError::InvalidDimensions { width, height } =>
848 {
849 if *width <= 1
850 {
851 write!(f, "Invalid dimensions: expected width larger than 1, got {width}")
852 } else
853 {
854 write!(f, "Invalid dimensions: expected area larger than 1, got {width}x{height} ({})", width * height)
855 }
856 },
857
858 GridError::InvalidByteLength { expected_mod, actual_len } =>
859 {
860 write!(f, "Invalid byte length: expected multiple of {expected_mod} bytes for this Grid, got {actual_len}")
861 },
862
863 GridError::InvalidKeyLength { expected_len, actual_len } =>
864 {
865 write!(f, "Invalid key length: expected length {expected_len}, got {actual_len}")
866 },
867 }
868 }
869}
870
871impl Error for GridError {}