rivide 1.1.3

Official Rust idiomatic and zero-allocation bindings for the Rivide Post-Quantum Cryptography (NIST FIPS 203 ML-KEM & NIST FIPS 204 ML-DSA) C99 Library.
Documentation
/*
 * SPDX-License-Identifier: MIT
 *
 * Rivide Post-Quantum Cryptography Library
 * Copyright (C) 2026 Moh. Ananda Firmansyah Putra
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 */

/**
 * @file dsa_ntt.c
 * @brief ML-DSA NTT, inverse NTT, and pointwise multiplication.
 *
 * Contains the precomputed zeta table and the iterative butterfly NTT/INTT
 * transforms over Z_q[X]/(X^256 + 1) with q = 8380417.
 */

#include "rivide/internal/dsa_ntt.h"

#include "rivide/internal/dsa_reduce.h"

/**
 * @brief NTT zeta table for q = 8380417.
 *
 * Precomputed roots of unity in Montgomery form, in bit-reversed order
 * for the iterative NTT butterfly. The primitive 512th root of unity
 * modulo q is zeta = 1753.
 */
const int32_t dsa_zetas[DSA_N] = {
    0,        25847,    -2608894, -518909,  237124,   -777960,  -876248,  466468,   1826347,
    2353451,  -359251,  -2091905, 3119733,  -2884855, 3111497,  2680103,  2725464,  1024112,
    -1079900, 3585928,  -549488,  -1119584, 2619752,  -2108549, -2118186, -3859737, -1399561,
    -3277672, 1757237,  -19422,   4010497,  280005,   2706023,  95776,    3077325,  3530437,
    -1661693, -3592148, -2537516, 3915439,  -3861115, -3043716, 3574422,  -2867647, 3539968,
    -300467,  2348700,  -539299,  -1699267, -1643818, 3505694,  -3821735, 3507263,  -2140649,
    -1600420, 3699596,  811944,   531354,   954230,   3881043,  3900724,  -2556880, 2071892,
    -2797779, -3930395, -1528703, -3677745, -3041255, -1452451, 3475950,  2176455,  -1585221,
    -1257611, 1939314,  -4083598, -1000202, -3190144, -3157330, -3632928, 126922,   3412210,
    -983419,  2147896,  2715295,  -2967645, -3693493, -411027,  -2477047, -671102,  -1228525,
    -22981,   -1308169, -381987,  1349076,  1852771,  -1430430, -3343383, 264944,   508951,
    3097992,  44288,    -1100098, 904516,   3958618,  -3724342, -8578,    1653064,  -3249728,
    2389356,  -210977,  759969,   -1316856, 189548,   -3553272, 3159746,  -1851402, -2409325,
    -177440,  1315589,  1341330,  1285669,  -1584928, -812732,  -1439742, -3019102, -3881060,
    -3628969, 3839961,  2091667,  3407706,  2316500,  3817976,  -3342478, 2244091,  -2446433,
    -3562462, 266997,   2434439,  -1235728, 3513181,  -3520352, -3759364, -1197226, -3193378,
    900702,   1859098,  909542,   819034,   495491,   -1613174, -43260,   -522500,  -655327,
    -3122442, 2031748,  3207046,  -3556995, -525098,  -768622,  -3595838, 342297,   286988,
    -2437823, 4108315,  3437287,  -3342277, 1735879,  203044,   2842341,  2691481,  -2590150,
    1265009,  4055324,  1247620,  2486353,  1595974,  -3767016, 1250494,  2635921,  -3548272,
    -2994039, 1869119,  1903435,  -1050970, -1333058, 1237275,  -3318210, -1430225, -451100,
    1312455,  3306115,  -1962642, -1279661, 1917081,  -2546312, -1374803, 1500165,  777191,
    2235880,  3406031,  -542412,  -2831860, -1671176, -1846953, -2584293, -3724270, 594136,
    -3776993, -2013608, 2432395,  2454455,  -164721,  1957272,  3369112,  185531,   -1207385,
    -3183426, 162844,   1616392,  3014001,  810149,   1652634,  -3694233, -1799107, -3038916,
    3523897,  3866901,  269760,   2213111,  -975884,  1717735,  472078,   -426683,  1723600,
    -1803090, 1910376,  -1667432, -1104333, -260646,  -3833893, -2939036, -2235985, -420899,
    -2286327, 183443,   -976891,  1612842,  -3545687, -554416,  3919660,  -48306,   -1362209,
    3937738,  1400424,  -846154,  1976782};

/**
 * @brief In-place forward NTT for Dilithium polynomials.
 *
 * @param[in,out] p Polynomial structure to transform in-place.
 */
void dsa_poly_ntt(dsa_poly_t *p) {
    unsigned int len, start, j, k;
    int32_t zeta, t;

    k = 0;
    for (len = 128; len >= 1; len >>= 1) {
        for (start = 0; start < DSA_N; start += 2 * len) {
            zeta = dsa_zetas[++k];
            for (j = start; j < start + len; j++) {
                t = dsa_montgomery_reduce((int64_t)zeta * p->coeffs[j + len]);
                p->coeffs[j + len] = p->coeffs[j] - t;
                p->coeffs[j] = p->coeffs[j] + t;
            }
        }
    }
}

/**
 * @brief In-place inverse NTT for Dilithium polynomials.
 *
 * @param[in,out] p Polynomial structure to transform in-place.
 */
void dsa_poly_invntt(dsa_poly_t *p) {
    unsigned int len, start, j, k;
    int32_t zeta, t;
    const int32_t f = 41978; /* 256^{-1} mod q in Montgomery form. */

    k = 256;
    for (len = 1; len < DSA_N; len <<= 1) {
        for (start = 0; start < DSA_N; start += 2 * len) {
            zeta = dsa_zetas[--k];
            for (j = start; j < start + len; j++) {
                t = p->coeffs[j];
                p->coeffs[j] = t + p->coeffs[j + len];
                int32_t diff = p->coeffs[j + len] - t;
                p->coeffs[j + len] = dsa_montgomery_reduce((int64_t)zeta * diff);
            }
        }
    }

    for (j = 0; j < DSA_N; j++) {
        p->coeffs[j] = dsa_montgomery_reduce((int64_t)f * p->coeffs[j]);
    }
}

/**
 * @brief Pointwise multiplication of two polynomials in the NTT domain.
 *
 * @param[out] r Output polynomial buffer.
 * @param[in]  a First input polynomial in NTT domain.
 * @param[in]  b Second input polynomial in NTT domain.
 */
void dsa_poly_pointwise(dsa_poly_t *r, const dsa_poly_t *a, const dsa_poly_t *b) {
    unsigned int i;
    for (i = 0; i < DSA_N; i++) {
        r->coeffs[i] = dsa_montgomery_reduce((int64_t)a->coeffs[i] * b->coeffs[i]);
    }
}

void dsa_poly_tomont(dsa_poly_t *p) {
    static const int32_t r2 = 4193792;
    unsigned int i;
    for (i = 0; i < DSA_N; i++) {
        p->coeffs[i] = dsa_montgomery_reduce((int64_t)p->coeffs[i] * r2);
    }
}