The matrix module provides functions for generating and manipulating sparse parity check matrices and permutations used in PVAC-HFHE.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/octra-labs/pvac_hfhe_cpp/llms.txt
Use this file to discover all available pages before exploring further.
Matrix generation
gen_H()
Generates the sparse parity check matrix H for the public key.Public key containing parameters and
canon_tag. The matrix H is written to pk.H and its digest to pk.H_digest.Algorithm
For each columnc = 0 to n_bits - 1:
- Generate deterministic seed from
(m_bits, n_bits, h_col_wt, c, canon_tag) - Use
prg_choose_k()to selecth_col_wtunique row indices in[0, m_bits) - Set those positions to 1 in the column bit vector
n × m with each column having exactly h_col_wt ones.
Digest computation
After generation, a SHA-256 digest is computed over:- Domain separator
"H|v2" - Matrix dimensions:
m_bits,n_bits,h_col_wt - All column bit vectors in row-major order
pk.H_digest for verification.
The matrix H is deterministic from
canon_tag, allowing efficient verification without transmitting the full matrix.gen_ubk_public()
Generates the public permutation (UBK) from a canonical tag.Random tag identifying the key (from
pk.canon_tag)Dimension of the permutation (typically 8192)
Structure containing both the permutation and its inverse
Algorithm
Uses the Fisher-Yates shuffle:- Initialize
perm = [0, 1, 2, ..., m_bits-1] - Derive pseudorandom stream from
canon_tagusing SHA-256 with domain separator"UBK" - For
i = m_bits-1down to1:- Generate uniform random
jin[0, i] - Swap
perm[i]andperm[j]
- Generate uniform random
- Compute inverse permutation:
inv[perm[i]] = i
Ubk structure contains:
perm: forward permutationinv: inverse permutation for decryption
prg_choose_k()
Selects k unique indices uniformly at random from[0, N).
Number of indices to select
Size of the range
[0, N)Domain separator string (e.g.,
Dom::H_GEN, Dom::X_SEED)Seed words for deterministic randomness
Vector of k unique indices in
[0, N)Sampling algorithm
- Hash initialization: Creates PRG from SHA-256 of
(label, words, counter) - Bounded sampling: Uses rejection sampling to avoid modulo bias
- Uniqueness: Maintains hash set to ensure no duplicates
- Uniform distribution: Each valid subset has equal probability
- All indices are unique
- All indices are in range
[0, N) - Distribution is cryptographically uniform
Permutation operations
apply_perm_sigma()
Applies an inverse permutation to a bit vector.Input bit vector
Inverse permutation (from
Ubk::inv)Permuted bit vector where
output[inv[i]] = input[i]ubk_apply()
Applies the UBK inverse permutation to all edges in a ciphertext.Public key containing the UBK permutation
Ciphertext whose edge syndrome vectors will be permuted
pk.ubk.inv to the s vector of each edge.
Syndrome generation
sigma_from_H()
Generates a syndrome vector for an encryption edge.Public key containing matrix H and parameters
Layer tag (from
RSeed::ztag)128-bit nonce (from
RSeed::nonce)Edge index
Channel/sign (0 for positive, 1 for negative)
Additional entropy (typically 0)
Syndrome vector of length
m_bitsAlgorithm
- Select columns: Use
prg_choose_k()to selectx_col_wtcolumns from H - XOR columns: Compute
s = H[c1] ⊕ H[c2] ⊕ ... ⊕ H[c_{x_col_wt}] - Add noise: Use
prg_choose_k()to fliperr_wtrandom bits in s
prg_layer_ztag()
Computes the layer ztag fromcanon_tag and nonce.
Public key canonical tag
128-bit layer nonce
64-bit layer tag derived via SHA-256
Dom::ZTAG with canon_tag and the nonce to produce a unique layer identifier.
Matrix properties
Sparse parity check matrix H
- Dimensions:
n_bits × m_bits(default: 16384 × 8192) - Column weight: Each column has exactly
h_col_wtones (default: 192) - Row weight: Variable, approximately
n * h_col_wt / m ≈ 384per row - Storage: Each column stored as a
BitVec(compressed)
Syndrome properties
Each syndrome vector fromsigma_from_H():
- Results from XORing
x_col_wtcolumns (default: 128) - Has approximately
x_col_wt * h_col_wt / 2ones (ignoring cancellations) - Includes
err_wtadditional noise bits (default: 128) - Is computationally hard to decode without the secret key
The syndrome decoding problem is related to the syndrome decoding problem for LDPC codes, which is NP-hard.
Example usage
Performance notes
- gen_H(): Generates 16384 columns, takes ~10-50ms depending on hardware
- gen_ubk_public(): Generates 8192-element permutation, takes less than 1ms
- sigma_from_H(): Generates one syndrome, takes ~0.1ms
- apply_perm_sigma(): Applies permutation to sparse vector, takes ~0.01ms
Related functions
keygen()- Callsgen_H()andgen_ubk_public()during key generationprg_choose_k()- Used internally for sparse sampling