pub struct Grid<const W: usize = { consts::DEFAULT_GRID_WIDTH }, const H: usize = { consts::DEFAULT_GRID_HEIGHT }>(/* private fields */);Expand description
A 2D matrix of 64-bit signed integers used as the core data structure in WHY2 encryption.
The Grid represents either input data or a key, formatted into rows and columns of i64 cells.
All transformations—round mixing, key scheduling, and nonlinear diffusion—operate directly on this structure.
Grids are flexible and can be transformed in-place. This abstraction allows WHY2 to generalize encryption over variable-sized blocks of dimension $W \times H$.
§Grid Size Consistency
WHY2 requires that the same grid dimensions ($W \times H$) be used consistently throughout encryption and decryption. Mixing grid sizes within a single session or across rounds is unsupported and may lead to incorrect results or undefined behavior.
Implementations§
Source§impl<const W: usize, const H: usize> Grid<W, H>
Implementation of core Grid operations for fixed-size grids.
impl<const W: usize, const H: usize> Grid<W, H>
Implementation of core Grid operations for fixed-size grids.
This block defines methods for Grid<W, H>, where W and H are compile-time constants
representing the grid’s width and height. All transformations — such as ARX mixing, key application,
and round-based encryption - operate on grids of this fixed shape.
§Type Parameters
W: Number of columns (width), must be a compile-time constant.H: Number of rows (height), must be a compile-time constant.
§Notes
- Grid dimensions must remain consistent across encryption and decryption.
Sourcepub fn new() -> Result<Self, GridError>
pub fn new() -> Result<Self, GridError>
Creates a new Grid initialized with zeroes.
This constructor sets up an empty Grid where all cells are set to 0.
§Returns
- Ok(
Grid) instance with all values set to zero. - Err(
GridError) if the area is invalid.
§Notes
- This method does not perform any encryption or transformation.
- Valid area is defined by $W > 0$ and $H \in \lbrace4, 8, 16\rbrace$
Sourcepub fn from_key(vec: &[i64]) -> Result<Self, GridError>
pub fn from_key(vec: &[i64]) -> Result<Self, GridError>
Initializes a key Grid from a vector of signed 64-bit integers.
Each cell is built from two key parts using nonlinear mixing: addition, XOR, and key-dependent rotation. This improves diffusion and ensures that both the values and the mixing angles depend on the key material.
§Algorithm
For each cell index $i$, two intermediate values are derived from the input vector $V$:
$$ A = V_i + V_{i + \text{Area}} $$
$$ B = V_i \oplus V_{i + \text{Area}} $$
Rotation amounts are derived via cross-dependence — each value is rotated by an angle derived from the other:
$$ A’ = A \lll (B \bmod 64) $$
$$ B’ = B \ggg (A \bmod 64) $$
where $\lll$ and $\ggg$ denote left and right rotation respectively.
The final grid value is computed as:
$$ \text{Grid}_{x,y} = A’ \oplus B’ \oplus i $$
where $i$ acts as domain separation, ensuring distinct positions produce distinct output even for identical key values.
§Parameters
vec: A slice of signed 64-bit integers representing the raw key. Must contain at least $2 \times W \times H$ elements.
§Returns
- Ok(
Grid) with mixed key values if input is valid. - Err(
GridError::InvalidKeyLength) ifvec.len() < 2 × W × H. - Err(
GridError::InvalidDimensions) if grid dimensions are invalid.
Sourcepub fn from_bytes(bytes: &[u8]) -> Result<Vec<Self>, GridError>
pub fn from_bytes(bytes: &[u8]) -> Result<Vec<Self>, GridError>
Initializes Grid from vector of unsigned 8-bit integers.
This function constructs Grid by chunking the input vector into i64 cells. It expects
exactly $W \times H \times 8$ bytes and returns an error if the input length does not match.
§Parameters
bytes: A byte slice (&[8u]) containing the raw data.
§Returns
- Ok(Vec<
Grid>) if the byte length matches the expected grid size - Err(
GridError) if the input length is not divisible by matrix size.
§Notes
- No transformation is applied
- Use this for raw Grid construction, not for secure key loading
Sourcepub fn from_flat(vec: &[i64]) -> Result<Self, GridError>
pub fn from_flat(vec: &[i64]) -> Result<Self, GridError>
Initializes Grid from a flat slice of 64-bit integers.
This function constructs a Grid by sequentially taking up to $W \times H$ elements
from the provided slice and placing them into the 2D grid structure, row by row.
§Parameters
vec: A slice of signed 64-bit integers (&[i64]). Elements beyond the grid’s capacity are ignored. If the slice is shorter than the grid capacity, the remaining cells retain their initial (zeroed) state.
§Returns
- Ok(
Grid) populated with the values from the slice. - Err(
GridError::InvalidDimensions) if grid dimensions are invalid.
Sourcepub fn iter_mut(&mut self) -> IterMut<'_, [i64; W]>
pub fn iter_mut(&mut self) -> IterMut<'_, [i64; W]>
Returns a mutable iterator over rows in the Grid
Sourcepub fn xor_grids(&mut self, key_grid: &Grid<W, H>)
pub fn xor_grids(&mut self, key_grid: &Grid<W, H>)
Computes the cell-wise XOR of two Grids.
This function takes two Grids of equal dimensions and modifies the Grid in-place:
$$ G_{x,y} = G_{x,y} \oplus K_{x,y} $$
It is used in WHY2 for mixing round keys, applying masks, or combining intermediate states.
§Parameters
key_grid: Input Grid for XOR
§Implementation
Uses SIMD acceleration to process 4 cells simultaneously when possible.
Sourcepub fn subcell(&mut self, round: usize)
pub fn subcell(&mut self, round: usize)
Applies nonlinear ARX-style mixing to each cell in the grid.
This transformation introduces symmetric diffusion by modifying each i64 cell
using a combination of addition, rotation, and XOR operations. The process is
round-dependent and designed to obscure bit patterns across the Grid.
§Parameters
round: A round index used to tweak the transformation logic.
§Behavior
Each 64-bit cell is split into two 32-bit halves $v_0, v_1$.
For SUBCELL_ROUNDS iterations, the Feistel-like network applies:
$$ v_0 \leftarrow v_0 + (((v_1 \ll 4) \oplus (v_1 \gg 5)) + v_1) \oplus \text{sum} $$
$$ v_1 \leftarrow v_1 + (((v_0 \ll 4) \oplus (v_0 \gg 5)) + v_0) \oplus \text{sum} $$
where $\text{sum}$ is incremented by a constant $\delta_{32} = $ DELTA_32 in each round:
$$ \text{sum} \leftarrow \text{sum} + \delta_{32} $$
§Implementation
The function uses SIMD acceleration via 256-bit AVX2 (or 2×128-bit NEON) vector operations to process 4 cells simultaneously. Remaining cells are handled with a scalar fallback.
§Notes
Sourcepub fn precalculate_shifts(&self) -> [usize; H]
pub fn precalculate_shifts(&self) -> [usize; H]
Precomputes row shift amounts from the current Grid state.
This function derives a deterministic shift value for each row by XOR-folding all elements in that row and applying a modulo operation. The resulting array can be reused across multiple rounds or operations without redundant computation.
§Algorithm
For each row $i$, the shift amount $S_i$ is computed as:
$$ H_i = \bigoplus_{j=0}^{W-1} G_{i,j} $$
Constant-time variant: $$ S_i = \left\lfloor \frac{H_i \cdot W}{2^{64}} \right\rfloor $$
Non-constant-time variant: $$ S_i = H_i \bmod W $$
where $G_{i,j}$ represents the cell at row $i$, column $j$.
§Returns
An array of length $H$ containing shift amounts in the range $[0, W)$ for each row.
§Security Notes
- The constant-time variant uses Barrett reduction to prevent timing attacks.
- The XOR-fold ensures each row’s shift is influenced by all cells in that row.
- Output shifts are deterministic for a given Grid state.
§Performance
This function should be called once per round key, not per grid, to avoid redundant computation. The precomputed shifts can be reused for all grids in a single encryption/decryption round.
Sourcepub fn shift_rows(&mut self, shifts: &[usize; H])
pub fn shift_rows(&mut self, shifts: &[usize; H])
Applies precomputed row-wise shifting to the Grid.
This transformation rotates each row of the Grid left by a precalculated amount, providing horizontal diffusion.
§Algorithm
For each row $i$, apply a left rotation by shift amount $S_i$:
$$ R’_i = \text{RotateLeft}(R_i, S_i) $$
where $R_i$ is the original row and $R’_i$ is the transformed row.
§Parameters
shifts: A precomputed array of shift amounts for each row, typically obtained fromprecalculate_shiftscalled on a round key Grid.
§Security Notes
- The constant-time implementation prevents side-channel attacks via memory access patterns.
- Shift amounts must come from a cryptographically secure source (e.g., round keys).
- This operation is reversible if shift amounts are known.
§Notes
- This method mutates the Grid in-place.
- The shifts array must have exactly $H$ elements.
- All shift values must be in the range $[0, W)$.
Sourcepub fn mix_columns(&mut self)
pub fn mix_columns(&mut self)
Applies column-wise mixing using a fixed Cauchy MDS matrix over $\mathbb{F}_{2^{64}}$.
This transformation provides vertical diffusion by treating each column as a vector of elements in $\mathbb{F}_{2^{64}}$ and multiplying it by a precomputed Cauchy MDS matrix. Unlike the previous implementation, the matrix is fixed and independent of the round key, enabling formal cryptographic analysis.
§Algorithm
For each column $c$, the output vector is computed as:
$$ \text{out}[r] = \sum_{k=0}^{H-1} M[r][k] \cdot \text{col}[k] $$
Multiplication uses carry-less multiplication modulo the irreducible polynomial $p(x) = x^{64} + x^4 + x^3 + x + 1$ in $\mathbb{F}_{2^{64}}$. Addition corresponds to XOR.
The matrix $M$ is a Cauchy MDS matrix constructed from disjoint sets $x = {0, \ldots, H-1}$ and $y = {H, \ldots, 2H-1}$ over $\mathbb{F}_{2^{64}}$:
$$ M_{ij} = (x_i \oplus y_j)^{-1} $$
§Security Properties
- True MDS: Branch number is provably $H + 1$ — the theoretical maximum. Any nonzero input with $k$ nonzero elements produces an output with at least $H+1-k$ nonzero elements.
- Formally analyzable: Fixed matrix enables standard differential/linear cryptanalysis bounds.
- Nothing-up-my-sleeve: Matrix derived from smallest possible disjoint sets, demonstrating no hidden weaknesses.
§Implementation
Uses hardware-accelerated carry-less multiplication:
- x86_64:
PCLMULQDQinstruction, processing 2 multiplications per instruction. - AArch64:
PMULL/PMULL2instructions, processing 2 multiplications per instruction. - Fallback: Software implementation for other architectures.
§Notes
- This method mutates the grid in-place.
- Grid heights outside the supported set will panic.
Sourcepub fn increment(&mut self, amount: u64)
pub fn increment(&mut self, amount: u64)
Increments the Grid value by a specified amount, treating it as a large Little-Endian integer.
This method performs modular addition of a 64-bit value to the multi-precision integer represented by the grid:
$$ G \leftarrow (G + \text{amount}) \bmod 2^{64 \times W \times H} $$
§Parameters
amount: The unsigned 64-bit value to add to the grid.- Pass
1for standard sequential counter increment. - Pass a block index $i$ (offset) when initializing parallel CTR counters.
- Pass
§Behavior
- The
Gridis treated as a single large integer in Little-Endian format (the cell at[0][0]is the least significant limb). - The
amountis added to the first cell, and any resulting carry is propagated sequentially through the remaining cells. - If the entire grid overflows (wraps around), the value resets modulo the grid size.
§Security
- When the
constant-timefeature is enabled, this function always iterates through the entire grid to prevent timing leaks via carry propagation analysis.
Trait Implementations§
Source§impl<const W: usize, const H: usize> BitXorAssign<&Grid<W, H>> for Grid<W, H>
impl<const W: usize, const H: usize> BitXorAssign<&Grid<W, H>> for Grid<W, H>
Source§fn bitxor_assign(&mut self, rhs: &Grid<W, H>)
fn bitxor_assign(&mut self, rhs: &Grid<W, H>)
^= operation. Read moreSource§impl<const W: usize, const H: usize> ConstantTimeEq for Grid<W, H>
Available on crate feature constant-time only.
impl<const W: usize, const H: usize> ConstantTimeEq for Grid<W, H>
constant-time only.Auto Trait Implementations§
impl<const W: usize, const H: usize> Freeze for Grid<W, H>
impl<const W: usize, const H: usize> RefUnwindSafe for Grid<W, H>
impl<const W: usize, const H: usize> Send for Grid<W, H>
impl<const W: usize, const H: usize> Sync for Grid<W, H>
impl<const W: usize, const H: usize> Unpin for Grid<W, H>
impl<const W: usize, const H: usize> UnsafeUnpin for Grid<W, H>
impl<const W: usize, const H: usize> UnwindSafe for Grid<W, H>
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more