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 //GRID FOR REX DATA
152<
153    const W: usize = { options::DEFAULT_GRID_WIDTH },
154    const H: usize = { options::DEFAULT_GRID_HEIGHT },
155>([[i64; W]; H]);
156
157/// Represents structured errors that can occur during Grid operations.
158///
159/// This enum replaces generic string errors to provide zero-allocation error handling
160/// and programmatic access to failure details. It is primarily used during
161/// Grid initialization and serialization.
162#[derive(Debug, Clone, PartialEq)]
163pub enum GridError
164{
165    /// Indicates that the requested Grid dimensions are invalid for cryptographic operations.
166    ///
167    /// This error occurs when creating a new Grid if the dimensions do not allow
168    /// for sufficient diffusion (e.g., width is 1, or total area is too small).
169    ///
170    /// # Fields
171    /// - `width`: The width (columns) of the attempted Grid.
172    /// - `height`: The height (rows) of the attempted Grid.
173    InvalidDimensions
174    {
175        width: usize,
176        height: usize,
177    },
178
179    /// Indicates that the input byte sequence length does not align with [`Grid`] requirements.
180    ///
181    /// This error occurs during deserialization (e.g., `from_bytes`) when the provided
182    /// data length is not a multiple of the [`Grid`]'s total byte size ($W \times H \times 8$ bytes).
183    ///
184    /// # Fields
185    /// - `expected_mod`: The required modulus (block size in bytes).
186    /// - `actual_len`: The actual length of the provided byte vector.
187    InvalidByteLength
188    {
189        expected_mod: usize,
190        actual_len: usize,
191    },
192
193    /// Indicates that the provided raw key has an incorrect length.
194    ///
195    /// The WHY2 key scheduling algorithm requires the input key vector to be exactly
196    /// twice the size of the [`Grid`] area ($2 \times W \times H$). This allows for the initial
197    /// folding and mixing of key parts (low and high components).
198    ///
199    /// # Fields
200    /// - `expected_len`: The required key length (number of `i64` elements).
201    /// - `actual_len`: The length of the provided key vector.
202    InvalidKeyLength
203    {
204        expected_len: usize,
205        actual_len: usize,
206    },
207}
208
209//IMPLEMENTATIONS
210/// Implementation of core Grid operations for fixed-size grids.
211///
212/// This block defines methods for `Grid<W, H>`, where `W` and `H` are compile-time constants
213/// representing the grid's width and height. All transformations — such as ARX mixing, key application,
214/// and round-based encryption - operate on grids of this fixed shape.
215///
216/// # Type Parameters
217/// - `W`: Number of columns (width), must be a compile-time constant.
218/// - `H`: Number of rows (height), must be a compile-time constant.
219///
220/// # Notes
221/// - Grid dimensions must remain consistent across encryption and decryption.
222impl<const W: usize, const H: usize> Grid<W, H>
223{
224    /// Creates a new Grid initialized with zeroes.
225    ///
226    /// This constructor sets up an empty Grid where all cells are set to `0`.
227    ///
228    /// # Returns
229    /// - Ok(`Grid`) instance with all values set to zero and area is larger than 1.
230    /// - Err(`GridError`) if the area is 1
231    ///
232    /// # Notes
233    /// - This method does not perform any encryption or transformation.
234    #[inline]
235    pub fn new() -> result::Result<Self, GridError>
236    {
237        let area = W * H;
238        if area > 1 && W > 1
239        {
240            Ok(Self([[0i64; W]; H]))
241        } else
242        {
243            Err(GridError::InvalidDimensions { width: W, height: H })
244        }
245    }
246
247    /// Initializes a key Grid from a vector of signed 64-bit integers.
248    ///
249    /// Each cell is built from two key parts using nonlinear mixing.
250    /// addition, XOR, and rotation. This improves diffusion and avoids
251    /// simple linear patterns in the key.
252    ///
253    /// # Algorithm
254    /// For each cell index $i$, the key parts $A$ and $B$ are derived from the input vector $V$:
255    ///
256    /// $$ A = (V_i + V_{i + \text{Area}}) \lll (i \bmod 64) $$
257    ///
258    /// $$ B = (V_i \oplus V_{i + \text{Area}}) \ggg (i \bmod 64) $$
259    ///
260    /// where $\lll$ and $\ggg$ denote left and right rotation respectively.
261    ///
262    /// The final grid value is computed as:
263    /// $$ Grid_{x,y} = A \oplus B \oplus i $$
264    ///
265    /// # Parameters
266    /// - `vec`: A slice of signed 64-bit integers representing the raw key.
267    ///
268    /// # Returns
269    /// - Ok(`Grid`) with mixed key values if dimensions are valid.
270    /// - Err(`GridError`) if the grid area is too small.
271    pub fn from_key(vec: &[i64]) -> result::Result<Self, GridError>
272    {
273        //GRID OPTIONS
274        let grid_area = W * H;
275
276        //SHAPE
277        let mut key_grid = Self::new()?;
278        for i in 0..grid_area
279        {
280            //APPLY NONLINEAR MIX TO KEY
281            let mut a = vec[i].wrapping_add(vec[i + grid_area]);
282            let mut b = vec[i] ^ vec[i + grid_area];
283            let rot = (i % 64) as u32;
284
285            //ROTATE
286            a = a.rotate_left(rot);
287            b = b.rotate_right(rot);
288
289            //APPLY
290            key_grid[i / W][i % W] = a ^ b ^ (i as i64);
291        }
292
293        Ok(key_grid)
294    }
295
296    /// Initializes [`Grid`] from vector of unsigned 8-bit integers.
297    ///
298    /// This function constructs [`Grid`] by chunking the input vector into `i64` cells. It expects
299    /// exactly $W \times H \times 8$ bytes and returns an error if the input length does not match.
300    ///
301    /// # Parameters
302    /// - `bytes`: A byte slice (`&[8u]`) containing the raw data.
303    ///
304    /// # Returns
305    /// - Ok(Vec<`Grid`>) if the byte length matches the expected grid size
306    /// - Err(`GridError`) if the input length is not divisible by matrix size.
307    ///
308    /// # Notes
309    /// - No transformation is applied
310    /// - Use this for raw Grid construction, not for secure key loading
311    pub fn from_bytes(bytes: &[u8]) -> result::Result<Vec<Self>, GridError>
312    {
313        let matrix_size = W * H * 8; //EACH i64 IS 8 BYTES
314
315        //CHECK FOR VALID GRID
316        if bytes.len() % matrix_size != 0
317        {
318            return Err(GridError::InvalidByteLength { expected_mod: matrix_size, actual_len: bytes.len() });
319        }
320
321        bytes.chunks(matrix_size).map(|chunk|
322        {
323            let mut grid = Grid::new()?;
324            for j in 0..H
325            {
326                for i in 0..W
327                {
328                    let start = (j * W + i) * 8;
329                    let slice = &chunk[start..start + 8];
330                    grid[j][i] = i64::from_be_bytes(slice.try_into().unwrap());
331                }
332            }
333
334            Ok(grid)
335        }).collect()
336    }
337
338    /// Returns an iterator over rows in the Grid
339    #[inline]
340    pub fn iter(&self) -> Iter<'_, [i64; W]>
341    {
342        self.0.iter()
343    }
344
345    /// Returns a mutable iterator over rows in the Grid
346    #[inline]
347    pub fn iter_mut(&mut self) -> IterMut<'_, [i64; W]>
348    {
349        self.0.iter_mut()
350    }
351
352    /// Returns width (number of columns) in the Grid
353    #[inline]
354    pub fn width(&self) -> usize
355    {
356        W
357    }
358
359    /// Returns height (number of rows) in the Grid
360    #[inline]
361    pub fn height(&self) -> usize
362    {
363        H
364    }
365
366    //ENCRYPTION
367    //PRIVATE HANDLERS
368    fn mix_matrix_handler
369    (
370        &mut self,
371        i_range: impl Iterator<Item = usize>,
372        j_range: impl Fn(usize) -> Range<usize>,
373        key_grid: &Grid<W, H>,
374    )
375    {
376        for i in i_range
377        {
378            for j in j_range(i)
379            {
380                let scalar = key_grid[i][j]; //USE KEY VALUE AS COEFFICIENT
381
382                //LWE NOISE
383                let noise = key_grid[j][i].wrapping_add(i as i64);
384
385                for col in 0..self.width()
386                {
387                    let val_j = self[j][col];
388                    let mixing = val_j.wrapping_mul(scalar).wrapping_add(noise); //ADD NOISE
389
390                    //Row[i] = Row[i] + (Row[j] * scalar + noise)
391                    self[i][col] = self[i][col].wrapping_add(mixing); //MIX
392                }
393            }
394        }
395    }
396
397    fn mix_diagonals_handler(&mut self, row: usize, col: usize) //MIX DIAGONALS IN GRID
398    {
399        //COPY PARAMETERS AS MUTABLE
400        let mut row = row;
401        let mut col = col;
402
403        //WALK ALONG THIS DIAGONAL
404        while row < self.height() - 1 && col < self.width() - 1
405        {
406            let next_row = row + 1;
407            let next_col = col + 1;
408
409            //XOR CURRENT CELL WITH NEXT DIAGONAL CELL
410            self[row][col] ^= self[next_row][next_col];
411
412            row = next_row;
413            col = next_col;
414        }
415    }
416
417    //PUBLIC
418    /// Computes the cell-wise XOR of two Grids.
419    ///
420    /// This function takes two [`Grid`]s of equal dimensions and modifies the [`Grid`] in-place:
421    /// $$ G_{x,y} = G_{x,y} \oplus K_{x,y} $$
422    /// It is used in WHY2 for mixing round keys, applying masks, or combining intermediate states.
423    ///
424    /// # Parameters
425    /// - `key_grid`: Input Grid for XOR
426    #[inline]
427    pub fn xor_grids(&mut self, key_grid: &Grid<W, H>)
428    {
429        for y in 0..(self.height()) //Y DIM
430        {
431            for x in 0..(self.width()) //X DIM
432            {
433                //XOR
434                self[y][x] ^= key_grid[y][x];
435            }
436        }
437    }
438
439    /// Applies nonlinear ARX-style mixing to each cell in the grid.
440    ///
441    /// This transformation introduces symmetric diffusion by modifying each `i64` cell
442    /// using a combination of addition, rotation, and XOR operations. The process is
443    /// round-dependent and designed to obscure bit patterns across the [`Grid`].
444    ///
445    /// # Parameters
446    /// - `round`: A round index used to tweak the transformation logic.
447    ///
448    /// # Behavior
449    /// Each 64-bit cell is split into two 32-bit halves $v_0, v_1$.
450    /// For [`SUBCELL_ROUNDS`](crate::options::SUBCELL_ROUNDS) iterations, the Feistel-like network applies:
451    ///
452    /// $$ v_0 \leftarrow v_0 + (((v_1 \ll 4) \oplus (v_1 \gg 5)) + v_1) \oplus \text{sum} $$
453    ///
454    /// $$ v_1 \leftarrow v_1 + (((v_0 \ll 4) \oplus (v_0 \gg 5)) + v_0) \oplus \text{sum} $$
455    ///
456    /// where $\text{sum}$ is incremented by a constant $\delta = $ [`SUBCELL_DELTA`](crate::options::SUBCELL_DELTA) in each round:
457    ///
458    /// $$ \text{sum} \leftarrow \text{sum} + \delta $$
459    ///
460    /// # Notes
461    /// - This method mutates the [`Grid`] in-place.
462    /// - It is inspired by TEA/XTEA but adapted for WHY2’s [`Grid`] architecture.
463    /// - The transformation is deterministic for a given round and [`Grid`] state.
464    pub fn subcell(&mut self, round: usize)
465    {
466        //APPLY ON EACH CELL
467        for col in self.iter_mut()
468        {
469            for cell in col
470            {
471                //SPLIT CELL TO HIGH32 AND LOW32
472                let x = *cell as u64;
473                let mut v0 = (x & 0xFFFF_FFFF) as u32; //LOW
474                let mut v1 = ((x >> 32) & 0xFFFF_FFFF) as u32; //HIGH
475
476                //XOR TWEAK -> MAKE ROUNDS DIFFERENT
477                v0 ^= round as u32;
478
479                //ARX-LIKE ROUNDS (INSPIRED BY XTEA/TEA)
480                let mut sum: u32 = 0;
481                for _ in 0..(options::SUBCELL_ROUNDS)
482                {
483                    sum = sum.wrapping_add(options::SUBCELL_DELTA);
484
485                    //MIX V1 INTO V0
486                    v0 = v0.wrapping_add(((v1 << 4) ^ (v1 >> 5)).wrapping_add(v1) ^ sum);
487
488                    //MIX V0 INTO V1
489                    v1 = v1.wrapping_add(((v0 << 4) ^ (v0 >> 5)).wrapping_add(v0) ^ sum);
490                }
491
492                //XOR TWEAK
493                v1 ^= round as u32;
494
495                //REBUILD AND APPLY
496                let out = ((v1 as u64) << 32) | (v0 as u64);
497                *cell = out as i64;
498            }
499        }
500    }
501
502    /// Applies row-wise shifting to the [`Grid`] based on a key [`Grid`].
503    ///
504    /// This transformation rotates each row of the [`Grid`] by a variable amount derived from
505    /// the corresponding row in `key_grid`. The shift amount $S_i$ for row $i$ is computed as:
506    ///
507    /// $$ S_i = \left( \bigoplus_{j=0}^{W-1} K_{i,j} \right) \bmod W $$
508    ///
509    /// # Behavior
510    /// - Each row is rotated left by $S_i$.
511    ///
512    /// # Notes
513    /// - This method mutates the grid in-place.
514    #[inline]
515    pub fn shift_rows(&mut self, key_grid: &Grid<W, H>)
516    {
517        #[cfg(not(feature = "constant-time"))]
518        let rows = self.width() as i64;
519
520        //SHIFT EACH ROW
521        for (i, row) in self.iter_mut().enumerate()
522        {
523            let hash_chunk = key_grid[i].iter().fold(0i64, |acc, &x| acc ^ x);
524            let shift: usize;
525
526            #[cfg(feature = "constant-time")]
527            {
528                shift = ((hash_chunk as u64 as u128 * W as u128) >> 64) as usize;
529            }
530
531            #[cfg(not(feature = "constant-time"))]
532            {
533                //SPLIT key_grid TO 8 PARTS & XOR EACH VALUE TO GET SHIFT
534                shift = hash_chunk.rem_euclid(rows) as usize;
535            }
536
537            //ROTATE THE ROW
538            #[cfg(feature = "constant-time")]
539            {
540                let mut new_row = [0i64; W]; //BUFFER
541
542                for i in 0..W
543                {
544                    let target_src_idx = (i + shift) % W;
545                    for src_idx in 0..W
546                    {
547                        new_row[i].conditional_assign(&row[src_idx], src_idx.ct_eq(&target_src_idx));
548                    }
549                }
550
551                //WRITE RESULT
552                *row = new_row;
553            }
554
555            #[cfg(not(feature = "constant-time"))]
556            {
557                row.rotate_left(shift);
558            }
559        }
560    }
561
562    /// Applies column-wise mixing to the grid using linear XOR diffusion.
563    ///
564    /// This transformation modifies each column by XORing it with its adjacent column,
565    /// introducing horizontal diffusion across the grid.
566    ///
567    /// # Behavior
568    /// For each column $c \in \{0, \dots, W-1\}$, compute:
569    /// $$ G_{r, c} \leftarrow G_{r, c} \oplus G_{r, (c + 1) \bmod W} $$
570    ///
571    /// # Notes
572    /// - This method mutates the grid in-place.
573    #[inline]
574    pub fn mix_columns(&mut self)
575    {
576        for col in 0..(self.width())
577        {
578            let next_col = (col + 1) % W;
579            for row in 0..self.height()
580            {
581                self[row][col] ^= self[row][next_col];
582            }
583        }
584    }
585
586    /// Applies a matrix-based affine transformation to mix rows.
587    ///
588    /// This function treats the [`Grid`] as a matrix and multiplies it by a key-dependent transformation
589    /// matrix, while adding a deterministic noise term. This converts the transformation from
590    /// purely linear ($Ax$) to affine:
591    ///
592    /// $$ G' = (L \cdot U) \cdot G + \text{noise} $$
593    ///
594    /// To ensure the operation is reversible (invertible) in modular arithmetic, the transformation
595    /// is constructed as a product of a **Lower triangular matrix ($L$)** and an **Upper triangular matrix ($U$)**.
596    ///
597    /// # Behavior
598    /// - **Lower Pass ($L$):** Each row adds a multiple of previous rows ($i > j$).
599    /// - **Upper Pass ($U$):** Each row adds a multiple of following rows ($i < j$).
600    ///
601    /// # Notes
602    /// - This method mutates the [`Grid`] in-place.
603    /// - All additions and multiplications are wrapping (modulo $2^{64}$).
604    pub fn mix_matrix(&mut self, key_grid: &Grid<W, H>)
605    {
606        //LOWER TRIANGULAR PASS (TOP -> DOWN)
607        self.mix_matrix_handler(1..H, |i| 0..i, key_grid);
608
609        //UPPER TRIANGULAR PASS (BOTTOM -> UP)
610        self.mix_matrix_handler((0..H - 1).rev(), |i| (i + 1)..H, key_grid);
611    }
612
613    /// Applies diagonal-wise mixing to the grid using XOR diffusion.
614    ///
615    /// This transformation modifies each diagonal line by XORing each element with
616    /// the next element along that diagonal.
617    ///
618    /// # Behavior
619    /// - Processes all diagonals parallel to the main diagonal.
620    /// - For each cell $(r, c)$, compute:
621    ///   $$ G_{r,c} \leftarrow G_{r,c} \oplus G_{r+1, c+1} $$
622    ///
623    /// # Notes
624    /// - This method mutates the grid in-place.
625    #[inline]
626    pub fn mix_diagonals(&mut self)
627    {
628        //PROCESS DIAGONALS BEGINNING IN THE FIRST COLUMN
629        for start_row in 0..self.height()
630        {
631            self.mix_diagonals_handler(start_row, 0);
632        }
633
634        //PROCESS DIAGONALS STARTING FROM THE FIRST ROW (EXCLUDING [0,0])
635        for start_col in 1..self.width()
636        {
637            self.mix_diagonals_handler(0, start_col);
638        }
639    }
640
641    //UTILS
642    /// Increments the [`Grid`] value by a specified amount, treating it as a large Little-Endian integer.
643    ///
644    /// This method performs modular addition of a 64-bit value to the multi-precision integer
645    /// represented by the grid:
646    ///
647    /// $$ G \leftarrow (G + \text{amount}) \bmod 2^{64 \times W \times H} $$
648    ///
649    /// # Parameters
650    /// - `amount`: The unsigned 64-bit value to add to the grid.
651    ///   - Pass `1` for standard sequential counter increment.
652    ///   - Pass a block index $i$ (offset) when initializing parallel CTR counters.
653    ///
654    /// # Behavior
655    /// - The [`Grid`] is treated as a single large integer in **Little-Endian** format
656    ///   (the cell at `[0][0]` is the least significant limb).
657    /// - The `amount` is added to the first cell, and any resulting carry is propagated
658    ///   sequentially through the remaining cells.
659    /// - If the entire grid overflows (wraps around), the value resets modulo the grid size.
660    ///
661    /// # Security
662    /// - When the **`constant-time`** feature is enabled, this function always iterates
663    ///   through the entire grid to prevent timing leaks via carry propagation analysis.
664    pub fn increment(&mut self, amount: &mut u64)
665    {
666        for row in self.iter_mut()
667        {
668            for cell in row.iter_mut()
669            {
670                let (result, overflow) = (*cell as u64).overflowing_add(*amount);
671                *cell = result as i64;
672
673                //NO CARRY (OVERFLOW), DONE
674                #[cfg(not(feature = "constant-time"))]
675                {
676                    if !overflow { return; }
677                    *amount = 1;
678                }
679
680                #[cfg(feature = "constant-time")]
681                {
682                    *amount = overflow as u64;
683                }
684            }
685        }
686    }
687}
688
689//INTO ITERATOR
690impl<const W: usize, const H: usize> IntoIterator for Grid<W, H>
691{
692    //TYPES
693    type Item = i64;
694    type IntoIter = Flatten<IntoIter<[i64; W], H>>;
695
696    //INTO ITERATOR
697    #[inline]
698    fn into_iter(self) -> Self::IntoIter
699    {
700        self.0.into_iter().flatten()
701    }
702}
703
704//INDEXING
705impl<const W: usize, const H: usize> Index<usize> for Grid<W, H>
706{
707    type Output = [i64; W];
708
709    #[inline]
710    fn index(&self, y: usize) -> &Self::Output
711    {
712        &self.0[y]
713    }
714}
715
716//MUTABLE INDEXING
717impl<const W: usize, const H: usize> IndexMut<usize> for Grid<W, H>
718{
719    #[inline]
720    fn index_mut(&mut self, y: usize) -> &mut Self::Output
721    {
722        &mut self.0[y]
723    }
724}
725
726//XOR ASSIGN
727impl<const W: usize, const H: usize> BitXorAssign<&Grid<W, H>> for Grid<W, H>
728{
729    #[inline]
730    fn bitxor_assign(&mut self, rhs: &Grid<W, H>)
731    {
732        self.xor_grids(rhs);
733    }
734}
735
736//CONSTANT-TIME EQ
737#[cfg(feature = "constant-time")]
738impl<const W: usize, const H: usize> ConstantTimeEq for Grid<W, H>
739{
740    fn ct_eq(&self, other: &Self) -> Choice
741    {
742        let mut result = Choice::from(1);
743
744        for (row_a, row_b) in self.iter().zip(other.iter())
745        {
746            for (cell_a, cell_b) in row_a.iter().zip(row_b.iter())
747            {
748                result &= cell_a.ct_eq(cell_b);
749            }
750        }
751
752        result
753    }
754}
755
756impl<const W: usize, const H: usize> PartialEq for Grid<W, H>
757{
758    fn eq(&self, other: &Self) -> bool
759    {
760        #[cfg(feature = "constant-time")]
761        {
762            self.ct_eq(other).into()
763        }
764
765        #[cfg(not(feature = "constant-time"))]
766        {
767            self.0 == other.0
768        }
769    }
770}
771
772//DISPLAY
773impl<const W: usize, const H: usize> Display for Grid<W, H>
774{
775    fn fmt(&self, f: &mut Formatter<'_>) -> Result
776    {
777        //CONVERT EACH VALUE TO 4 LINES
778        let cells: Vec<Vec<[String; 4]>> = self.iter().map(|row|
779        {
780            row.iter().map(|val|
781            {
782                let s = val.to_string();
783                let chunk_size = (s.len() + 3) / 4;
784                let mut lines = [String::new(), String::new(), String::new(), String::new()];
785
786                for (i, chunk) in s.chars().collect::<Vec<_>>().chunks(chunk_size).enumerate()
787                {
788                    lines[i] = chunk.iter().collect();
789                }
790
791                lines
792            }).collect()
793        }).collect();
794
795        //DETERMINE MAX WIDTH
796        let max_width = cells.iter()
797            .flat_map(|row| row.iter())
798            .flat_map(|lines| lines.iter())
799            .map(|line| line.len())
800            .max()
801            .unwrap_or(1);
802
803        //BUILD HORIZONTAL BORDER
804        let border = format!
805        (
806            "+{}+\n",
807            (0..self.width()).map(|_| "-".repeat(max_width + 2)).collect::<Vec<_>>().join("+")
808        );
809
810        //PRINT
811        for row in &cells
812        {
813            f.write_str(&border)?;
814            for line_idx in 0..4
815            {
816                for cell in row
817                {
818                    write!(f, "| {:>width$} ", cell[line_idx], width = max_width)?;
819                }
820
821                writeln!(f, "|")?;
822            }
823        }
824
825        f.write_str(&border)
826    }
827}
828
829impl<const W: usize, const H: usize> LowerHex for Grid<W, H>
830{
831    fn fmt(&self, f: &mut Formatter<'_>) -> Result
832    {
833        for row in self.iter()
834        {
835            for cell in row
836            {
837                write!(f, "{:016x}", cell)?;
838            }
839        }
840
841        Ok(())
842    }
843}
844
845impl Display for GridError
846{
847    fn fmt(&self, f: &mut Formatter<'_>) -> Result
848    {
849        match self
850        {
851            GridError::InvalidDimensions { width, height } =>
852            {
853                if *width <= 1
854                {
855                    write!(f, "Invalid dimensions: expected width larger than 1, got {width}")
856                } else
857                {
858                    write!(f, "Invalid dimensions: expected area larger than 1, got {width}x{height} ({})", width * height)
859                }
860            },
861
862            GridError::InvalidByteLength { expected_mod, actual_len } =>
863            {
864                write!(f, "Invalid byte length: expected multiple of {expected_mod} bytes for this Grid, got {actual_len}")
865            },
866
867            GridError::InvalidKeyLength { expected_len, actual_len } =>
868            {
869                write!(f, "Invalid key length: expected length {expected_len}, got {actual_len}")
870            },
871        }
872    }
873}
874
875impl Error for GridError {}