Skip to main content

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.

Overview

This module provides cryptographic hash functions (SHA-256) and extendable-output functions (SHAKE256) for key derivation, commitments, and deterministic randomness expansion in PVAC-HFHE.

SHA-256

Sha256

SHA-256 hash state for incremental hashing.
struct Sha256 {
    uint32_t h[8];
    uint64_t len;
    uint8_t buf[64];
    size_t ptr;
};
h
uint32_t[8]
Hash state (eight 32-bit words)
len
uint64_t
Total number of bytes processed
buf
uint8_t[64]
Input buffer for the current block
ptr
size_t
Current position in buffer

Methods

init

Initializes the hash state.
void init();
Example:
Sha256 hasher;
hasher.init();

update

Processes data incrementally.
void update(const void* data, size_t n);
data
const void*
Pointer to data to hash
n
size_t
Number of bytes to process
Example:
Sha256 hasher;
hasher.init();
hasher.update("hello", 5);
hasher.update("world", 5);

finish

Finalizes the hash and produces the digest.
void finish(uint8_t out[32]);
out
uint8_t[32]
Output buffer for 256-bit (32-byte) digest
Example:
Sha256 hasher;
hasher.init();
hasher.update(data, len);

uint8_t digest[32];
hasher.finish(digest);

Convenience functions

sha256_bytes

Computes SHA-256 hash of a single buffer.
void sha256_bytes(const void* data, size_t n, uint8_t out[32]);
data
const void*
Input data
n
size_t
Input length in bytes
out
uint8_t[32]
Output digest
Example:
uint8_t digest[32];
sha256_bytes("hello world", 11, digest);

sha256_acc_u64

Accumulates a 64-bit integer into the hash in little-endian format.
void sha256_acc_u64(Sha256& s, uint64_t x);
s
Sha256&
Hash state
x
uint64_t
Value to hash
Example:
Sha256 s;
s.init();
sha256_acc_u64(s, 0x123456789ABCDEFULL);
sha256_acc_u64(s, timestamp);

uint8_t digest[32];
s.finish(digest);

SHAKE256

Shake256

SHAKE256 XOF (extendable-output function) state.
struct Shake256 {
    uint64_t st[25];
    size_t rate;
    size_t pos;
    bool squeezing;
};
st
uint64_t[25]
Keccak state (1600 bits)
rate
size_t
Rate parameter (136 bytes for SHAKE256)
pos
size_t
Current position in rate bytes
squeezing
bool
True if in squeezing mode, false if absorbing

Methods

init

Initializes SHAKE256 state.
void init();
Example:
Shake256 xof;
xof.init();

absorb

Absorbs input data into the sponge.
void absorb(const uint8_t* data, size_t len);
data
const uint8_t*
Input data
len
size_t
Input length in bytes
Example:
Shake256 xof;
xof.init();
xof.absorb((const uint8_t*)"seed", 4);
xof.absorb((const uint8_t*)&counter, sizeof(counter));
Do not call absorb() after calling squeeze(). The function will abort if called in squeezing mode.

pad

Finalizes absorption and switches to squeezing mode.
void pad();
Example:
xof.absorb(seed, seed_len);
xof.pad();
// Now ready to squeeze output

squeeze

Extracts output bytes from the XOF.
void squeeze(uint8_t* out, size_t len);
out
uint8_t*
Output buffer
len
size_t
Number of bytes to extract
Example:
uint8_t output[1024];
xof.squeeze(output, 1024);
You can call squeeze() multiple times to extract as much output as needed. The function automatically calls pad() if not already in squeezing mode.

next_u64

Extracts the next 64-bit integer from the XOF.
uint64_t next_u64();
return
uint64_t
Next 64 bits of output
Example:
uint64_t r1 = xof.next_u64();
uint64_t r2 = xof.next_u64();

XofShake

XofShake

High-level wrapper for SHAKE256 with domain-separated seeding.
struct XofShake {
    Shake256 sh;
};

Methods

init

Initializes XOF with a label and seed.
void init(const std::string& label, const std::vector<uint64_t>& seed);
label
const std::string&
Domain separation label (e.g., from Dom namespace)
seed
const std::vector<uint64_t>&
Seed values in little-endian format
Example:
XofShake xof;
std::vector<uint64_t> seed = {tag, nonce.lo, nonce.hi};
xof.init(Dom::NOISE, seed);

take_u64

Extracts the next 64-bit value.
uint64_t take_u64();
return
uint64_t
Next 64-bit value
Example:
uint64_t random = xof.take_u64();

bounded

Generates a uniformly random integer in the range [0, M).
uint64_t bounded(uint64_t M);
M
uint64_t
Upper bound (exclusive)
return
uint64_t
Uniformly random value in [0, M)
Example:
// Random value in [0, 100)
uint64_t dice = xof.bounded(100);

// Random index for array of size n
size_t idx = xof.bounded(n);
This function uses rejection sampling to ensure uniform distribution, avoiding modulo bias.

Utility functions

hex8

Converts binary data to hexadecimal string.
std::string hex8(const uint8_t* d, size_t n);
d
const uint8_t*
Input data
n
size_t
Number of bytes
return
std::string
Hexadecimal string (lowercase)
Example:
uint8_t digest[32];
sha256_bytes(data, len, digest);
std::string hex = hex8(digest, 32);
std::cout << "Hash: " << hex << std::endl;

Usage patterns

Computing public key digest

Sha256 s;
s.init();
for (const auto& h_row : pk.H) {
    s.update(h_row.w.data(), h_row.w.size() * sizeof(uint64_t));
}
s.finish(pk.H_digest.data());

Deterministic random stream

XofShake xof;
std::vector<uint64_t> seed = {prf_key[0], prf_key[1], prf_key[2], prf_key[3]};
xof.init(Dom::PRF_LPN, seed);

// Generate random matrix
for (int i = 0; i < rows; i++) {
    for (int j = 0; j < cols; j++) {
        matrix[i][j] = xof.take_u64();
    }
}

Sampling without replacement

XofShake xof;
xof.init(Dom::X_SEED, seed);

std::set<size_t> selected;
while (selected.size() < target_count) {
    size_t idx = xof.bounded(total_count);
    selected.insert(idx);
}

Commitment scheme

// Commit
uint8_t commitment[32];
Sha256 s;
s.init();
s.update(message, message_len);
sha256_acc_u64(s, randomness);
s.finish(commitment);

// Verify
uint8_t recomputed[32];
s.init();
s.update(revealed_message, message_len);
sha256_acc_u64(s, revealed_randomness);
s.finish(recomputed);

bool valid = std::memcmp(commitment, recomputed, 32) == 0;

Security properties

SHA-256

  • Collision resistance: ~128-bit security
  • Preimage resistance: 256-bit security
  • Second preimage resistance: 256-bit security

SHAKE256

  • Security level: 256-bit (for 512-bit output)
  • Collision resistance: 128-bit
  • Uniformity: Output is computationally indistinguishable from random

Performance

  • SHA-256: ~300-500 MB/s on modern CPUs
  • SHAKE256: ~150-250 MB/s on modern CPUs
  • Hardware acceleration (AES-NI, SHA extensions) not currently used

Build docs developers (and LLMs) love