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 {}