Skip to main content

MaskedPoly

Struct MaskedPoly 

Source
pub struct MaskedPoly {
    pub share0: [i32; 256],
    pub share1: [i32; 256],
}
Expand description

A polynomial split into two additive shares modulo q.

Maintains the invariant unmask()[i] = (share0[i] + share1[i]) mod q for all i in 0..N. Both shares are stored with coefficients in [0, q-1]. Neither share alone reveals any information about the underlying polynomial.

Fields§

§share0: [i32; 256]

First additive share.

§share1: [i32; 256]

Second additive share.

Implementations§

Source§

impl MaskedPoly

Source

pub const fn zero() -> Self

Build an all-zero MaskedPoly. Both shares are zero, so unmask() returns the zero polynomial. Useful as a stack initializer for fixed-size arrays of masked polynomials.

Source

pub fn sample_expand_mask( rho_double_prime: &[u8; 64], nonce: u16, gamma1: i32, bitlen_gamma1_minus1: usize, ) -> Self

Masked sampling of a masking vector polynomial from a SHAKE256 stream — the DPA-safe replacement for sample::expand_mask.

Implements ExpandMask (FIPS 204 Algorithm 34) but produces a two-share arithmetic representation (share0, share1) directly, without ever materializing the unmasked y coefficient in a stack or heap slot.

§Threat model

Boolean-masked y is attackable with ~300 traces per the Hermelink-Ning-Petri result (ePrint 2025/276). Arithmetic masking is more robust but still requires careful implementation: the key invariant is that the unmasked coefficient value must only exist transiently in a CPU register, never be written to RAM.

§Implementation

For each coefficient:

  1. Decode the unmasked y_i from SHAKE256 output bytes into a stack-local let y_i: i32 = ... (register-scoped).
  2. Draw a fresh random mask r_i from the same SHAKE256 stream (a separate squeeze block).
  3. Compute share1_i = r_i mod q, share0_i = (y_i - r_i) mod q.
  4. Write both shares to the output MaskedPoly.
  5. y_i and r_i go out of scope immediately.

The two SHAKE256 streams (y bits and mask bits) are drawn from the same state: we first squeeze the packed-y bytes, then squeeze additional bytes for the mask. This keeps the function deterministic for a given rho'' || nonce, so the signature remains reproducible (ACVP-compatible).

§Arguments
  • rho_double_prime — 64-byte seed (FIPS 204).
  • nonce — the per-polynomial nonce (kappa + r).
  • gamma1 — the Γ₁ parameter for the current ML-DSA level.
  • bitlen_gamma1_minus1 — bit length used by ExpandMask (17 or 19).
Source

pub fn mask( poly: &[i32; 256], rng: &mut dyn CryptoRng, ) -> Result<Self, MlDsaError>

Split a plaintext polynomial into two random additive shares.

Generates a uniformly distributed share1 ∈ [0, q-1]^N from the RNG, then sets share0 = poly - share1 mod q. The intermediate random bytes are zeroized after use.

§Errors

Returns MlDsaError::RngFailure if the RNG fails.

Source

pub fn unmask(&self) -> [i32; 256]

Reconstruct the plaintext polynomial from the two shares.

Result coefficients are in [0, q-1]. The returned polynomial is unmasked secret data and should be zeroized after use.

Source

pub fn zeroize(&mut self)

Securely erase both shares via volatile writes.

Source

pub fn refresh(&mut self, rng: &mut dyn CryptoRng) -> Result<(), MlDsaError>

Re-randomize the shares without changing the unmasked value.

Draws a fresh random polynomial r and updates the shares as share0' = share0 - r mod q, share1' = share1 + r mod q. The sum is preserved: share0' + share1' ≡ share0 + share1. Refreshing prevents higher-order correlation buildup when the same masked polynomial is reused across multiple operations.

Auto Trait Implementations§

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