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