arcium-primitives 0.6.0

Arcium primitives
Documentation
use std::{
    fmt::Debug,
    hint::black_box,
    ops::{Mul, Sub},
};

use typenum::{Logarithm2, NonZero, PartialDiv, PowerOfTwo, Unsigned, U1};

use crate::{
    algebra::{
        field::FieldExtension,
        multivariate_ring::{Evaluation, MultivariateRing},
    },
    types::{HeapArray, Positive},
};

/// The table of public QA-SD expansion elements: `C` `M`-wide evaluation-form rings.
pub type QasdPublicElements<F, M, C> = HeapArray<MultivariateRing<F, M, Evaluation>, C>;

/// Domain-separated seed for deterministic derivation of the public QA-SD elements.
/// First 32 bytes of π (hex), reused as a nothing-up-my-sleeve value.
pub const QASD_PUBLIC_ELEMENT_SEED: [u8; 32] = [
    0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44,
    0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89,
];

/// Trait grouping core parameters and helpers for the Quasi-Abelian Syndrome Decoding (QA-SD)
/// assumption with regular SPFSS noise.
/// Protocol parameters extend this trait with protocol-specific constants.
pub trait QasdParams: Send + Unpin + Debug + Clone {
    /// Prime field
    type F: FieldExtension<Subfield = Self::F>;

    /// Output size (= BlockSize * T). `PartialDiv<BlockSize>` records that `BlockSize` divides `M`
    /// (always true since `M = BlockSize · T`), which lets `from_blockwise_walsh::<BlockSize>` be
    /// called without a runtime bound check.
    type M: Positive + PowerOfTwo + PartialDiv<Self::BlockSize>;

    /// Block size (= 2^N). SPFSS output size.
    type BlockSize: Positive + PowerOfTwo + Logarithm2<Output = Self::N>;

    /// log2 of the block size (lowercase `n` in [LLXY+26]).
    type N: Positive;

    /// Hamming weight of the error vector (lowercase `t` in the [LLXY+26]).
    /// The `Mul<U9/U3/U2/U4>` bounds mirror `round_based_op::dpf::params::BatchSize`
    /// (T singlet/full-triple/stationary-triple/Input* pools), which `primitives`
    /// cannot name directly.
    type T: Positive
        + Mul<typenum::U9, Output: Positive>
        + Mul<typenum::U3, Output: Positive>
        + Mul<typenum::U2, Output: Positive>
        + Mul<typenum::U4, Output: Positive>
        + Mul<Self::T, Output = Self::TSquared>;

    /// Compression factor (lowercase `c` in the [LLXY+26]).
    type C: Positive + Mul<Self::C, Output = Self::CSquared>;

    /// T^2.
    type TSquared: Positive + Mul<Self::CSquared, Output: Positive>;

    /// C^2 · T^2.
    type K: Positive;

    /// C^2.
    type CSquared: Positive;

    /// C^2 · T.
    type TCSquared: Positive;

    /// Public QA-SD expansion elements: `(1, a_1, ..., a_{c-1})`, where `1` is the
    /// multiplicative identity and each `a_i` is a deterministically derived random
    /// ring element. Cached for the process lifetime and shared by reference.
    fn qasd_public_elements() -> &'static QasdPublicElements<Self::F, Self::M, Self::C>;

    /// Warm up the cached QA-SD public elements so the first protocol run doesn't
    /// pay for the one-time derivation.
    fn warm_up() {
        black_box(Self::qasd_public_elements());
    }
}

/// QA-SD parameters augmented with the stationary-syndrome-decoding (SSD) reuse count.
pub trait StationaryQasdParams: QasdParams {
    /// Number of stationary iterations performed against one set of noise vectors sharing the same
    /// non-zero indices (one set of alphas) before the expensive MPC must be re-run to refresh
    /// them. Larger `D`
    /// amortizes the MPC further but reuses the same noise under more public matrices. D degrades
    /// the security of a classic QA-SD parameter set by `log(D)` bits.
    ///
    /// Asserts `D ≥ 2`: a `D = 1` epoch never
    /// feeds its refill channel, starving the second refresh.
    type D: Unsigned + Sub<U1, Output: NonZero>;
}