Skip to main content

Grid

Struct Grid 

Source
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.

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.
Source

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$
Source

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
Source

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
Source

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
Source

pub fn to_flat(&self) -> Vec<i64>

Converts the Grid into a flat vector of 64-bit integers.

This method flattens the 2D grid structure by sequentially iterating over its rows and collecting all cells into a single, continuous Vec<i64>.

§Returns
  • A Vec<i64> containing exactly $W \times H$ elements extracted from the grid.
Source

pub fn iter(&self) -> Iter<'_, [i64; W]>

Returns an iterator over rows in the Grid

Source

pub fn iter_mut(&mut self) -> IterMut<'_, [i64; W]>

Returns a mutable iterator over rows in the Grid

Source

pub fn width(&self) -> usize

Returns width (number of columns) in the Grid

Source

pub fn height(&self) -> usize

Returns height (number of rows) in the Grid

Source

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.

Source

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
  • This method mutates the Grid in-place.
  • It is inspired by TEA/XTEA but adapted for WHY2’s Grid architecture.
  • The transformation is deterministic for a given round and Grid state.
  • SIMD implementation provides 2.5-4× speedup on modern CPUs compared to scalar code.
Source

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.

Source

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 from precalculate_shifts called 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)$.
Source

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: PCLMULQDQ instruction, processing 2 multiplications per instruction.
  • AArch64: PMULL/PMULL2 instructions, 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.
Source

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 1 for standard sequential counter increment.
    • Pass a block index $i$ (offset) when initializing parallel CTR counters.
§Behavior
  • The Grid is treated as a single large integer in Little-Endian format (the cell at [0][0] is the least significant limb).
  • The amount is 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-time feature 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>

Source§

fn bitxor_assign(&mut self, rhs: &Grid<W, H>)

Performs the ^= operation. Read more
Source§

impl<const W: usize, const H: usize> Clone for Grid<W, H>

Source§

fn clone(&self) -> Grid<W, H>

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl<const W: usize, const H: usize> ConstantTimeEq for Grid<W, H>

Available on crate feature constant-time only.
Source§

fn ct_eq(&self, other: &Self) -> Choice

Determine if two items are equal. Read more
Source§

fn ct_ne(&self, other: &Self) -> Choice

Determine if two items are NOT equal. Read more
Source§

impl<const W: usize, const H: usize> Debug for Grid<W, H>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<const W: usize, const H: usize> Display for Grid<W, H>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<const W: usize, const H: usize> Index<usize> for Grid<W, H>

Source§

type Output = [i64; W]

The returned type after indexing.
Source§

fn index(&self, y: usize) -> &Self::Output

Performs the indexing (container[index]) operation. Read more
Source§

impl<const W: usize, const H: usize> IndexMut<usize> for Grid<W, H>

Source§

fn index_mut(&mut self, y: usize) -> &mut Self::Output

Performs the mutable indexing (container[index]) operation. Read more
Source§

impl<const W: usize, const H: usize> IntoIterator for Grid<W, H>

Source§

type Item = i64

The type of the elements being iterated over.
Source§

type IntoIter = Flatten<IntoIter<[i64; W], H>>

Which kind of iterator are we turning this into?
Source§

fn into_iter(self) -> Self::IntoIter

Creates an iterator from a value. Read more
Source§

impl<const W: usize, const H: usize> LowerHex for Grid<W, H>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<const W: usize, const H: usize> PartialEq for Grid<W, H>

Source§

fn eq(&self, other: &Self) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl<const W: usize, const H: usize> Zeroize for Grid<W, H>

Source§

fn zeroize(&mut self)

Zero out this object from memory using Rust intrinsics which ensure the zeroization operation is not “optimized away” by the compiler.

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> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts 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
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V