oarust 0.8.6

GPU-first semantic computing engine written in Rust
// ML-DSA-44/65/87 pure/prehashed verification, FIPS 204 Algorithms 3, 5 and 8.
// New standards implementation: the historical OA verifier was a status stub.
// Public-data helper shared by the quarantined dispatch and test laboratory.
// This formula does not itself admit a GPU capability or secret operations.
import shake;
import mldsa_prehash_layout;
import mldsa_prehash_digest;
import mldsa_field;
import mldsa_ntt;
import mldsa_packing;
import mldsa_sampling;

static const uint MLDSA65_SIG_BYTES = 3309u;
static const uint MLDSA65_PK_BYTES = 1952u;
static const uint MLDSA65_BETA = 196u;

// algorithm=0 selects pure framing; nonzero selects the checked prehash identity.
// hash_message hashes the original message; otherwise the input is its exact digest.
// This internal composition preserves the proven decoding and polynomial body.
bool mldsa_verify_framed(
	uint parameter_set,
	RWByteAddressBuffer public_keys, uint pk_base, uint pk_length,
	RWByteAddressBuffer signatures, uint sig_base, uint sig_length,
	RWByteAddressBuffer messages, uint message_base, uint message_length,
	RWByteAddressBuffer contexts, uint context_base, uint context_length,
	uint algorithm, bool hash_message
) {
	// FIPS 204 final Table 1. Reject unknown sets before deriving any size or indexing.
	if (parameter_set != 44u && parameter_set != 65u && parameter_set != 87u) return false;
	uint rows = parameter_set == 44u ? 4u : (parameter_set == 65u ? 6u : 8u);
	uint cols = parameter_set == 44u ? 4u : (parameter_set == 65u ? 5u : 7u);
	uint challenge_words = parameter_set == 44u ? 8u : (parameter_set == 65u ? 12u : 16u);
	uint gamma1 = parameter_set == 44u ? 131072u : 524288u;
	uint gamma2 = parameter_set == 44u ? 95232u : 261888u;
	uint z_bits = parameter_set == 44u ? 18u : 20u;
	uint beta = parameter_set == 44u ? 78u : (parameter_set == 65u ? 196u : 120u);
	uint omega = parameter_set == 44u ? 80u : (parameter_set == 65u ? 55u : 75u);
	uint tau = parameter_set == 44u ? 39u : (parameter_set == 65u ? 49u : 60u);
	uint pk_bytes = 32u + rows * 320u;
	uint sig_bytes = challenge_words * 4u + cols * z_bits * 32u + omega + rows;
	if (pk_length != pk_bytes || sig_length != sig_bytes || context_length > 255u) return false;
	if (algorithm != 0u && (mldsa_prehash_bytes(algorithm) == 0u
		|| (!hash_message && message_length != mldsa_prehash_bytes(algorithm)))) return false;
	if (!pqc_buffer_range(public_keys, pk_base, pk_length)
		|| !pqc_buffer_range(signatures, sig_base, sig_length)
		|| !pqc_buffer_range(messages, message_base, message_length)
		|| !pqc_buffer_range(contexts, context_base, context_length)) return false;

	uint rho[8], t1[8][256], c_tilde[16], z[7][256], h[8][256];
	for (uint w = 0u; w < 8u; ++w) rho[w] = read_le_word(public_keys, pk_base + w * 4u);
	for (uint row = 0u; row < rows; ++row)
		mldsa_unpack_poly_unsigned(public_keys, pk_base + 32u + row * 320u, 10u, t1[row]);
	for (uint w = 0u; w < 16u; ++w)
		c_tilde[w] = w < challenge_words ? read_le_word(signatures, sig_base + w * 4u) : 0u;
	for (uint col = 0u; col < cols; ++col) {
		mldsa_unpack_poly_signed(signatures, sig_base + challenge_words * 4u + col * z_bits * 32u,
			z_bits, gamma1, z[col]);
		if (!mldsa_check_norm(z[col], gamma1 - beta)) return false;
	}
	uint h_base = sig_base + challenge_words * 4u + cols * z_bits * 32u;
	uint idx = 0u;
	for (uint row = 0u; row < rows; ++row)
		if (!mldsa_unpack_hint_row(signatures, h_base, omega, row, idx, h[row])) return false;
	if (!mldsa_hint_tail_valid(signatures, h_base, idx, omega)) return false;

	PqcShake256 sponge;
	pqc_shake256_init(sponge);
	pqc_shake256_absorb_buffer(sponge, public_keys, pk_base, pk_length);
	uint2 tr[8];
	pqc_shake256_finish(sponge, tr);

	pqc_shake256_init(sponge);
	pqc_shake256_absorb_digest(sponge, tr);
	if (algorithm == 0u) {
		// External pure M' = 0x00 || len(ctx) || ctx || message.
		pqc_shake256_absorb_byte(sponge, 0u);
		pqc_shake256_absorb_byte(sponge, context_length);
		pqc_shake256_absorb_buffer(sponge, contexts, context_base, context_length);
		pqc_shake256_absorb_buffer(sponge, messages, message_base, message_length);
	} else if (hash_message) {
		uint digest[16];
		if (!mldsa_prehash_digest(messages, message_base, message_length, algorithm, digest)) return false;
		mldsa_absorb_prehash_header(sponge, contexts, context_base, context_length, algorithm);
		for (uint i = 0u; i < mldsa_prehash_bytes(algorithm); ++i)
			pqc_shake256_absorb_byte(sponge, (digest[i / 4u] >> ((i & 3u) * 8u)) & 255u);
	} else {
		mldsa_absorb_prehash_framing(sponge, messages, message_base, message_length,
			contexts, context_base, context_length, algorithm);
	}
	uint2 mu[8];
	pqc_shake256_finish(sponge, mu);

	uint c[256];
	mldsa_sample_in_ball(c_tilde, challenge_words, tau, c);
	mldsa_ntt(c);
	for (uint l = 0u; l < cols; ++l) mldsa_ntt(z[l]);
	for (uint k = 0u; k < rows; ++k) {
		for (uint i = 0u; i < 256u; ++i) t1[k][i] = mldsa_mul(t1[k][i], MLDSA_D_FACTOR);
		mldsa_ntt(t1[k]);
	}

	pqc_shake256_init(sponge);
	pqc_shake256_absorb_digest(sponge, mu);
	for (uint k = 0u; k < rows; ++k) {
		uint w[256];
		for (uint i = 0u; i < 256u; ++i) w[i] = 0u;
		for (uint l = 0u; l < cols; ++l) {
			uint a[256];
			// ExpandA already returns NTT-domain coefficients.
			mldsa_expand_a_poly(rho, k, l, a);
			for (uint i = 0u; i < 256u; ++i) w[i] = mldsa_add(w[i], mldsa_mul(a[i], z[l][i]));
		}
		for (uint i = 0u; i < 256u; ++i) w[i] = mldsa_sub(w[i], mldsa_mul(c[i], t1[k][i]));
		mldsa_intt(w);
		// FIPS w1Encode: six-bit coefficients for 44, four-bit for 65/87.
		// Stream packed bytes directly into the hash; do not retain an encoded row.
		uint bits = parameter_set == 44u ? 6u : 4u;
		uint reservoir = 0u, occupied = 0u;
		for (uint i = 0u; i < 256u; ++i) {
			uint high = mldsa_use_hint(h[k][i], w[i], gamma2);
			reservoir |= high << occupied;
			occupied += bits;
			if (occupied >= 8u) {
				pqc_shake256_absorb_byte(sponge, reservoir & 255u);
				reservoir >>= 8u;
				occupied -= 8u;
			}
		}

	}
	uint2 expected[8];
	pqc_shake256_finish(sponge, expected);
	uint difference = 0u;
	for (uint i = 0u; i < challenge_words; ++i) {
		uint word = ((i & 1u) == 0u) ? expected[i / 2u].x : expected[i / 2u].y;
		difference |= c_tilde[i] ^ word;
	}
	return difference == 0u;
}

// Existing external pure contract and descriptor ABI remain unchanged.
bool mldsa_verify_external(
	uint parameter_set,
	RWByteAddressBuffer public_keys, uint pk_base, uint pk_length,
	RWByteAddressBuffer signatures, uint sig_base, uint sig_length,
	RWByteAddressBuffer messages, uint message_base, uint message_length,
	RWByteAddressBuffer contexts, uint context_base, uint context_length
) {
	return mldsa_verify_framed(parameter_set, public_keys, pk_base, pk_length,
		signatures, sig_base, sig_length, messages, message_base, message_length,
		contexts, context_base, context_length, 0u, false);
}

// Precomputed public digest only. Unknown selectors, including zero, are false;
// no fallback to pure mode or arbitrary caller-supplied DER identifier exists.
bool mldsa_verify_prehashed(
	uint parameter_set,
	RWByteAddressBuffer public_keys, uint pk_base, uint pk_length,
	RWByteAddressBuffer signatures, uint sig_base, uint sig_length,
	RWByteAddressBuffer digests, uint digest_base, uint digest_length,
	RWByteAddressBuffer contexts, uint context_base, uint context_length,
	uint algorithm
) {
	if (mldsa_prehash_bytes(algorithm) == 0u) return false;
	return mldsa_verify_framed(parameter_set, public_keys, pk_base, pk_length,
		signatures, sig_base, sig_length, digests, digest_base, digest_length,
		contexts, context_base, context_length, algorithm, false);
}

// Original-message HashML-DSA: hash locally, preserving the shared verifier body.
// The digest is never materialized in an intermediate device allocation.
bool mldsa_verify_hash_message(
	uint parameter_set,
	RWByteAddressBuffer public_keys, uint pk_base, uint pk_length,
	RWByteAddressBuffer signatures, uint sig_base, uint sig_length,
	RWByteAddressBuffer messages, uint message_base, uint message_length,
	RWByteAddressBuffer contexts, uint context_base, uint context_length,
	uint algorithm
) {
	if (mldsa_prehash_bytes(algorithm) == 0u) return false;
	return mldsa_verify_framed(parameter_set, public_keys, pk_base, pk_length,
		signatures, sig_base, sig_length, messages, message_base, message_length,
		contexts, context_base, context_length, algorithm, true);
}

// Preserve the existing private 65 call sites and schema ABI.
bool mldsa65_verify_external(
	RWByteAddressBuffer public_keys, uint pk_base, uint pk_length,
	RWByteAddressBuffer signatures, uint sig_base, uint sig_length,
	RWByteAddressBuffer messages, uint message_base, uint message_length,
	RWByteAddressBuffer contexts, uint context_base, uint context_length
) {
	return mldsa_verify_external(65u, public_keys, pk_base, pk_length,
		signatures, sig_base, sig_length, messages, message_base, message_length,
		contexts, context_base, context_length);
}