/* * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.security.provider; import jdk.internal.vm.annotation.IntrinsicCandidate; import sun.security.provider.SHA3.SHAKE256; import sun.security.provider.SHA3Parallel.Shake128Parallel; import java.security.InvalidAlgorithmParameterException; import java.security.MessageDigest; import java.security.InvalidKeyException; import java.security.SignatureException; import java.util.Arrays; public class ML_DSA { // Security level constants static final int ML_DSA_44 = 2; static final int ML_DSA_65 = 3; static final int ML_DSA_87 = 5; // Constants from FIPS 204 that do not depend on security level private static final int ML_DSA_D = 13; private static final int ML_DSA_Q = 8380417; private static final int ML_DSA_N = 256; private static final int SHAKE256_BLOCK_SIZE = 136; // the block length for SHAKE256 private static final int SHAKE128_BLOCK_SIZE = 168; // the block length for SHAKE128 private final int A_SEED_LEN = 32; private final int S1S2_SEED_LEN = 64; private final int K_LEN = 32; private final int TR_LEN = 64; private final int MU_LEN = 64; private final int MASK_SEED_LEN = 64; private static final int D_MASK = (1 << ML_DSA_D) - 1; private final int T0_COEFF_SIZE = 13; private static final int MONT_R_BITS = 32; private static final long MONT_R = 4294967296L; // 1 << MONT_R_BITS private static final int MONT_Q = 8380417; private static final int MONT_R_SQUARE_MOD_Q = 2365951; private static final int MONT_Q_INV_MOD_R = 58728449; private static final int MONT_R_MOD_Q = 4193792; // toMont((ML_DSA_N)^-1 (mod ML_DSA_Q)) private static final int MONT_DIM_INVERSE = 16382; // Zeta values for NTT with montgomery factor precomputed private static final int[] MONT_ZETAS_FOR_NTT = new int[]{ 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 }; private static final int[] MONT_ZETAS_FOR_VECTOR_NTT = new int[]{ 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909, 237124, 237124, 237124, 237124, 237124, 237124, 237124, 237124, 237124, 237124, 237124, 237124, 237124, 237124, 237124, 237124, 237124, 237124, 237124, 237124, 237124, 237124, 237124, 237124, 237124, 237124, 237124, 237124, 237124, 237124, 237124, 237124, -777960, -777960, -777960, -777960, -777960, -777960, -777960, -777960, -777960, -777960, -777960, -777960, -777960, -777960, -777960, -777960, -777960, -777960, -777960, -777960, -777960, -777960, -777960, -777960, -777960, -777960, -777960, -777960, -777960, -777960, -777960, -777960, -876248, -876248, -876248, -876248, -876248, -876248, -876248, -876248, -876248, -876248, -876248, -876248, -876248, -876248, -876248, -876248, -876248, -876248, -876248, -876248, -876248, -876248, -876248, -876248, -876248, -876248, -876248, -876248, -876248, -876248, -876248, -876248, 466468, 466468, 466468, 466468, 466468, 466468, 466468, 466468, 466468, 466468, 466468, 466468, 466468, 466468, 466468, 466468, 466468, 466468, 466468, 466468, 466468, 466468, 466468, 466468, 466468, 466468, 466468, 466468, 466468, 466468, 466468, 466468, 1826347, 1826347, 1826347, 1826347, 1826347, 1826347, 1826347, 1826347, 1826347, 1826347, 1826347, 1826347, 1826347, 1826347, 1826347, 1826347, 2353451, 2353451, 2353451, 2353451, 2353451, 2353451, 2353451, 2353451, 2353451, 2353451, 2353451, 2353451, 2353451, 2353451, 2353451, 2353451, -359251, -359251, -359251, -359251, -359251, -359251, -359251, -359251, -359251, -359251, -359251, -359251, -359251, -359251, -359251, -359251, -2091905, -2091905, -2091905, -2091905, -2091905, -2091905, -2091905, -2091905, -2091905, -2091905, -2091905, -2091905, -2091905, -2091905, -2091905, -2091905, 3119733, 3119733, 3119733, 3119733, 3119733, 3119733, 3119733, 3119733, 3119733, 3119733, 3119733, 3119733, 3119733, 3119733, 3119733, 3119733, -2884855, -2884855, -2884855, -2884855, -2884855, -2884855, -2884855, -2884855, -2884855, -2884855, -2884855, -2884855, -2884855, -2884855, -2884855, -2884855, 3111497, 3111497, 3111497, 3111497, 3111497, 3111497, 3111497, 3111497, 3111497, 3111497, 3111497, 3111497, 3111497, 3111497, 3111497, 3111497, 2680103, 2680103, 2680103, 2680103, 2680103, 2680103, 2680103, 2680103, 2680103, 2680103, 2680103, 2680103, 2680103, 2680103, 2680103, 2680103, 2725464, 2725464, 2725464, 2725464, 2725464, 2725464, 2725464, 2725464, 1024112, 1024112, 1024112, 1024112, 1024112, 1024112, 1024112, 1024112, -1079900, -1079900, -1079900, -1079900, -1079900, -1079900, -1079900, -1079900, 3585928, 3585928, 3585928, 3585928, 3585928, 3585928, 3585928, 3585928, -549488, -549488, -549488, -549488, -549488, -549488, -549488, -549488, -1119584, -1119584, -1119584, -1119584, -1119584, -1119584, -1119584, -1119584, 2619752, 2619752, 2619752, 2619752, 2619752, 2619752, 2619752, 2619752, -2108549, -2108549, -2108549, -2108549, -2108549, -2108549, -2108549, -2108549, -2118186, -2118186, -2118186, -2118186, -2118186, -2118186, -2118186, -2118186, -3859737, -3859737, -3859737, -3859737, -3859737, -3859737, -3859737, -3859737, -1399561, -1399561, -1399561, -1399561, -1399561, -1399561, -1399561, -1399561, -3277672, -3277672, -3277672, -3277672, -3277672, -3277672, -3277672, -3277672, 1757237, 1757237, 1757237, 1757237, 1757237, 1757237, 1757237, 1757237, -19422, -19422, -19422, -19422, -19422, -19422, -19422, -19422, 4010497, 4010497, 4010497, 4010497, 4010497, 4010497, 4010497, 4010497, 280005, 280005, 280005, 280005, 280005, 280005, 280005, 280005, 2706023, 2706023, 2706023, 2706023, 95776, 95776, 95776, 95776, 3077325, 3077325, 3077325, 3077325, 3530437, 3530437, 3530437, 3530437, -1661693, -1661693, -1661693, -1661693, -3592148, -3592148, -3592148, -3592148, -2537516, -2537516, -2537516, -2537516, 3915439, 3915439, 3915439, 3915439, -3861115, -3861115, -3861115, -3861115, -3043716, -3043716, -3043716, -3043716, 3574422, 3574422, 3574422, 3574422, -2867647, -2867647, -2867647, -2867647, 3539968, 3539968, 3539968, 3539968, -300467, -300467, -300467, -300467, 2348700, 2348700, 2348700, 2348700, -539299, -539299, -539299, -539299, -1699267, -1699267, -1699267, -1699267, -1643818, -1643818, -1643818, -1643818, 3505694, 3505694, 3505694, 3505694, -3821735, -3821735, -3821735, -3821735, 3507263, 3507263, 3507263, 3507263, -2140649, -2140649, -2140649, -2140649, -1600420, -1600420, -1600420, -1600420, 3699596, 3699596, 3699596, 3699596, 811944, 811944, 811944, 811944, 531354, 531354, 531354, 531354, 954230, 954230, 954230, 954230, 3881043, 3881043, 3881043, 3881043, 3900724, 3900724, 3900724, 3900724, -2556880, -2556880, -2556880, -2556880, 2071892, 2071892, 2071892, 2071892, -2797779, -2797779, -2797779, -2797779, -3930395, -3930395, -1528703, -1528703, -3677745, -3677745, -3041255, -3041255, -1452451, -1452451, 3475950, 3475950, 2176455, 2176455, -1585221, -1585221, -1257611, -1257611, 1939314, 1939314, -4083598, -4083598, -1000202, -1000202, -3190144, -3190144, -3157330, -3157330, -3632928, -3632928, 126922, 126922, 3412210, 3412210, -983419, -983419, 2147896, 2147896, 2715295, 2715295, -2967645, -2967645, -3693493, -3693493, -411027, -411027, -2477047, -2477047, -671102, -671102, -1228525, -1228525, -22981, -22981, -1308169, -1308169, -381987, -381987, 1349076, 1349076, 1852771, 1852771, -1430430, -1430430, -3343383, -3343383, 264944, 264944, 508951, 508951, 3097992, 3097992, 44288, 44288, -1100098, -1100098, 904516, 904516, 3958618, 3958618, -3724342, -3724342, -8578, -8578, 1653064, 1653064, -3249728, -3249728, 2389356, 2389356, -210977, -210977, 759969, 759969, -1316856, -1316856, 189548, 189548, -3553272, -3553272, 3159746, 3159746, -1851402, -1851402, -2409325, -2409325, -177440, -177440, 1315589, 1315589, 1341330, 1341330, 1285669, 1285669, -1584928, -1584928, -812732, -812732, -1439742, -1439742, -3019102, -3019102, -3881060, -3881060, -3628969, -3628969, 3839961, 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 }; private static final int[] MONT_ZETAS_FOR_VECTOR_INVERSE_NTT = new int[]{ -1976782, 846154, -1400424, -3937738, 1362209, 48306, -3919660, 554416, 3545687, -1612842, 976891, -183443, 2286327, 420899, 2235985, 2939036, 3833893, 260646, 1104333, 1667432, -1910376, 1803090, -1723600, 426683, -472078, -1717735, 975884, -2213111, -269760, -3866901, -3523897, 3038916, 1799107, 3694233, -1652634, -810149, -3014001, -1616392, -162844, 3183426, 1207385, -185531, -3369112, -1957272, 164721, -2454455, -2432395, 2013608, 3776993, -594136, 3724270, 2584293, 1846953, 1671176, 2831860, 542412, -3406031, -2235880, -777191, -1500165, 1374803, 2546312, -1917081, 1279661, 1962642, -3306115, -1312455, 451100, 1430225, 3318210, -1237275, 1333058, 1050970, -1903435, -1869119, 2994039, 3548272, -2635921, -1250494, 3767016, -1595974, -2486353, -1247620, -4055324, -1265009, 2590150, -2691481, -2842341, -203044, -1735879, 3342277, -3437287, -4108315, 2437823, -286988, -342297, 3595838, 768622, 525098, 3556995, -3207046, -2031748, 3122442, 655327, 522500, 43260, 1613174, -495491, -819034, -909542, -1859098, -900702, 3193378, 1197226, 3759364, 3520352, -3513181, 1235728, -2434439, -266997, 3562462, 2446433, -2244091, 3342478, -3817976, -2316500, -3407706, -2091667, -3839961, -3839961, 3628969, 3628969, 3881060, 3881060, 3019102, 3019102, 1439742, 1439742, 812732, 812732, 1584928, 1584928, -1285669, -1285669, -1341330, - 1341330, -1315589, -1315589, 177440, 177440, 2409325, 2409325, 1851402, 1851402, -3159746, -3159746, 3553272, 3553272, -189548, -189548, 1316856, 1316856, -759969, -759969, 210977, 210977, -2389356, -2389356, 3249728, 3249728, -1653064, -1653064, 8578, 8578, 3724342, 3724342, -3958618, -3958618, -904516, -904516, 1100098, 1100098, -44288, -44288, -3097992, -3097992, -508951, -508951, -264944, -264944, 3343383, 3343383, 1430430, 1430430, -1852771, -1852771, -1349076, -1349076, 381987, 381987, 1308169, 1308169, 22981, 22981, 1228525, 1228525, 671102, 671102, 2477047, 2477047, 411027, 411027, 3693493, 3693493, 2967645, 2967645, -2715295, -2715295, -2147896, -2147896, 983419, 983419, -3412210, -3412210, -126922, -126922, 3632928, 3632928, 3157330, 3157330, 3190144, 3190144, 1000202, 1000202, 4083598, 4083598, -1939314, -1939314, 1257611, 1257611, 1585221, 1585221, -2176455, -2176455, -3475950, -3475950, 1452451, 1452451, 3041255, 3041255, 3677745, 3677745, 1528703, 1528703, 3930395, 3930395, 2797779, 2797779, 2797779, 2797779, -2071892, -2071892, -2071892, -2071892, 2556880, 2556880, 2556880, 2556880, -3900724, -3900724, -3900724, -3900724, -3881043, -3881043, -3881043, -3881043, -954230, -954230, -954230, -954230, -531354, -531354, -531354, -531354, -811944, -811944, -811944, -811944, -3699596, -3699596, -3699596, -3699596, 1600420, 1600420, 1600420, 1600420, 2140649, 2140649, 2140649, 2140649, -3507263, -3507263, -3507263, -3507263, 3821735, 3821735, 3821735, 3821735, -3505694, -3505694, -3505694, -3505694, 1643818, 1643818, 1643818, 1643818, 1699267, 1699267, 1699267, 1699267, 539299, 539299, 539299, 539299, -2348700, -2348700, -2348700, -2348700, 300467, 300467, 300467, 300467, -3539968, -3539968, -3539968, -3539968, 2867647, 2867647, 2867647, 2867647, -3574422, -3574422, -3574422, -3574422, 3043716, 3043716, 3043716, 3043716, 3861115, 3861115, 3861115, 3861115, -3915439, -3915439, -3915439, -3915439, 2537516, 2537516, 2537516, 2537516, 3592148, 3592148, 3592148, 3592148, 1661693, 1661693, 1661693, 1661693, -3530437, -3530437, -3530437, -3530437, -3077325, -3077325, -3077325, -3077325, -95776, -95776, -95776, -95776, -2706023, -2706023, -2706023, -2706023, -280005, -280005, -280005, -280005, -280005, -280005, -280005, -280005, -4010497, -4010497, -4010497, -4010497, -4010497, -4010497, -4010497, -4010497, 19422, 19422, 19422, 19422, 19422, 19422, 19422, 19422, -1757237, -1757237, -1757237, -1757237, -1757237, -1757237, -1757237, -1757237, 3277672, 3277672, 3277672, 3277672, 3277672, 3277672, 3277672, 3277672, 1399561, 1399561, 1399561, 1399561, 1399561, 1399561, 1399561, 1399561, 3859737, 3859737, 3859737, 3859737, 3859737, 3859737, 3859737, 3859737, 2118186, 2118186, 2118186, 2118186, 2118186, 2118186, 2118186, 2118186, 2108549, 2108549, 2108549, 2108549, 2108549, 2108549, 2108549, 2108549, -2619752, -2619752, -2619752, -2619752, -2619752, -2619752, -2619752, -2619752, 1119584, 1119584, 1119584, 1119584, 1119584, 1119584, 1119584, 1119584, 549488, 549488, 549488, 549488, 549488, 549488, 549488, 549488, -3585928, -3585928, -3585928, -3585928, -3585928, -3585928, -3585928, -3585928, 1079900, 1079900, 1079900, 1079900, 1079900, 1079900, 1079900, 1079900, -1024112, -1024112, -1024112, -1024112, -1024112, -1024112, -1024112, -1024112, -2725464, -2725464, -2725464, -2725464, -2725464, -2725464, -2725464, -2725464, -2680103, -2680103, -2680103, -2680103, -2680103, -2680103, -2680103, -2680103, -2680103, -2680103, -2680103, -2680103, -2680103, -2680103, -2680103, -2680103, -3111497, -3111497, -3111497, -3111497, -3111497, -3111497, -3111497, -3111497, -3111497, -3111497, -3111497, -3111497, -3111497, -3111497, -3111497, -3111497, 2884855, 2884855, 2884855, 2884855, 2884855, 2884855, 2884855, 2884855, 2884855, 2884855, 2884855, 2884855, 2884855, 2884855, 2884855, 2884855, -3119733, -3119733, -3119733, -3119733, -3119733, -3119733, -3119733, -3119733, -3119733, -3119733, -3119733, -3119733, -3119733, -3119733, -3119733, -3119733, 2091905, 2091905, 2091905, 2091905, 2091905, 2091905, 2091905, 2091905, 2091905, 2091905, 2091905, 2091905, 2091905, 2091905, 2091905, 2091905, 359251, 359251, 359251, 359251, 359251, 359251, 359251, 359251, 359251, 359251, 359251, 359251, 359251, 359251, 359251, 359251, -2353451, -2353451, -2353451, -2353451, -2353451, -2353451, -2353451, -2353451, -2353451, -2353451, -2353451, -2353451, -2353451, -2353451, -2353451, -2353451, -1826347, -1826347, -1826347, -1826347, -1826347, -1826347, -1826347, -1826347, -1826347, -1826347, -1826347, -1826347, -1826347, -1826347, -1826347, -1826347, -466468, -466468, -466468, -466468, -466468, -466468, -466468, -466468, -466468, -466468, -466468, -466468, -466468, -466468, -466468, -466468, -466468, -466468, -466468, -466468, -466468, -466468, -466468, -466468, -466468, -466468, -466468, -466468, -466468, -466468, -466468, -466468, 876248, 876248, 876248, 876248, 876248, 876248, 876248, 876248, 876248, 876248, 876248, 876248, 876248, 876248, 876248, 876248, 876248, 876248, 876248, 876248, 876248, 876248, 876248, 876248, 876248, 876248, 876248, 876248, 876248, 876248, 876248, 876248, 777960, 777960, 777960, 777960, 777960, 777960, 777960, 777960, 777960, 777960, 777960, 777960, 777960, 777960, 777960, 777960, 777960, 777960, 777960, 777960, 777960, 777960, 777960, 777960, 777960, 777960, 777960, 777960, 777960, 777960, 777960, 777960, -237124, -237124, -237124, -237124, -237124, -237124, -237124, -237124, -237124, -237124, -237124, -237124, -237124, -237124, -237124, -237124, -237124, -237124, -237124, -237124, -237124, -237124, -237124, -237124, -237124, -237124, -237124, -237124, -237124, -237124, -237124, -237124, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847 }; // Constants defined for each security level private final int level; private final int tau; private final int lambda; private final int gamma1; private final int gamma2; private final int mlDsa_k; private final int mlDsa_l; private final int eta; private final int beta; private final int omega; // Second-class constants derived from above values // log_2(gamma1) private final int gamma1Bits; // mlDsa_l * (eta + 1) * 256 / 8 private final int s1PackedLength; // mlDsa_k * (eta + 1) * 256 / 8 private final int s2PackedLength; // mlDsa_k * ML_DSA_D * 256 / 8 private final int t0PackedLength; // log_2(eta) + 1 private final int s1s2CoeffSize; // rho_size + t1_size private final int publicKeyLength; // c_tilde_size + z_size + h_size private final int signatureLength; // mlDsa_k * log_2((q-1)/(2*gamma2) - 1) * 256 / 8 private final int wCoeffSize; public ML_DSA(int security_level) { switch (security_level) { case ML_DSA_44: level = 2; tau = 39; lambda = 128; gamma1 = 1 << 17; gamma1Bits = 17; gamma2 = (ML_DSA_Q - 1) / 88; mlDsa_k = 4; mlDsa_l = 4; eta = 2; beta = 78; omega = 80; publicKeyLength = 1312; signatureLength = 2420; s1PackedLength = 384; s2PackedLength = 384; t0PackedLength = 1664; s1s2CoeffSize = 3; wCoeffSize = 6; break; case ML_DSA_65: level = 3; tau = 49; lambda = 192; gamma1 = 1 << 19; gamma2 = (ML_DSA_Q - 1) / 32; mlDsa_k = 6; mlDsa_l = 5; eta = 4; beta = 196; omega = 55; publicKeyLength = 1952; signatureLength = 3293; s1PackedLength = 640; s2PackedLength = 768; t0PackedLength = 2496; s1s2CoeffSize = 4; wCoeffSize = 4; gamma1Bits = 19; break; case ML_DSA_87: level = 4; tau = 60; lambda = 256; gamma1 = 1 << 19; gamma1Bits = 19; gamma2 = (ML_DSA_Q - 1) / 32; mlDsa_k = 8; mlDsa_l = 7; eta = 2; beta = 120; omega = 75; publicKeyLength = 2592; signatureLength = 4595; s1PackedLength = 672; s2PackedLength = 768; t0PackedLength = 3328; s1s2CoeffSize = 3; wCoeffSize = 4; break; default: throw new IllegalArgumentException("Wrong security level"); } } public record ML_DSA_PrivateKey(byte[] rho, byte[] k, byte[] tr, int[][] s1, int[][] s2, int[][] t0) { void destroy() { Arrays.fill(k, (byte)0); for (var b : s1) { Arrays.fill(b, (byte) 0); } for (var b : s2) { Arrays.fill(b, (byte) 0); } for (var b : t0) { Arrays.fill(b, (byte) 0); } } } public record ML_DSA_PublicKey(byte[] rho, int[][] t1) { } public record ML_DSA_KeyPair(ML_DSA_PrivateKey privateKey, ML_DSA_PublicKey publicKey) { } public record ML_DSA_Signature(byte[] commitmentHash, int[][] response, boolean[][] hint) { } /* Key validity checks */ public Object checkPublicKey(byte[] pk) throws InvalidKeyException { int pk_size = 32 + (mlDsa_k * 32 * (23 - ML_DSA_D)); if (pk.length != pk_size) { throw new InvalidKeyException("Incorrect public key size"); } return null; } public Object checkPrivateKey(byte[] sk) throws InvalidKeyException { int eta_bits = eta == 4 ? 4 : 3; //SK size is 128 + 32 * ((l + k) * bitlen(2*eta) + d*k) int sk_size = 128 + 32 * ((mlDsa_l + mlDsa_k) * eta_bits + ML_DSA_D * mlDsa_k); if (sk.length != sk_size) { throw new InvalidKeyException("Incorrect private key size"); } return null; } //Internal functions in Section 6 of specification public ML_DSA_KeyPair generateKeyPairInternal(byte[] randomBytes) { //Initialize hash functions var hash = new SHAKE256(0); var crHash = new SHAKE256(TR_LEN); //Expand seed hash.update(randomBytes); hash.update((byte)mlDsa_k); hash.update((byte)mlDsa_l); byte[] rho = hash.squeeze(A_SEED_LEN); byte[] rhoPrime = hash.squeeze(S1S2_SEED_LEN); byte[] k = hash.squeeze(K_LEN); hash.reset(); //Sample A int[][][] keygenA = generateA(rho); //A is in NTT domain //Sample S1 and S2 int[][] s1 = integerMatrixAlloc(mlDsa_l, ML_DSA_N); int[][] s2 = integerMatrixAlloc(mlDsa_k, ML_DSA_N); //hash is reset before being used in sampleS1S2 sampleS1S2(s1, s2, hash, rhoPrime); //Compute t and tr mlDsaVectorNtt(s1); //s1 now in NTT domain int[][] As1 = integerMatrixAlloc(mlDsa_k, ML_DSA_N); matrixVectorPointwiseMultiply(As1, keygenA, s1); mlDsaVectorInverseNtt(s1); //take s1 out of NTT domain mlDsaVectorInverseNtt(As1); int[][] t = vectorAddPos(As1, s2); int[][] t0 = integerMatrixAlloc(mlDsa_k, ML_DSA_N); int[][] t1 = integerMatrixAlloc(mlDsa_k, ML_DSA_N); power2Round(t, t0, t1); //Encode PK and SK ML_DSA_PublicKey pk = new ML_DSA_PublicKey(rho, t1); byte[] publicKeyBytes = pkEncode(pk); crHash.update(publicKeyBytes); byte[] tr = crHash.digest(); ML_DSA_PrivateKey sk = new ML_DSA_PrivateKey(rho, k, tr, s1, s2, t0); return new ML_DSA_KeyPair(sk, pk); } public ML_DSA_Signature signInternal(byte[] message, byte[] rnd, byte[] skBytes) { //Decode private key and initialize hash function ML_DSA_PrivateKey sk = skDecode(skBytes); var hash = new SHAKE256(0); //Do some NTTs mlDsaVectorNtt(sk.s1()); mlDsaVectorNtt(sk.s2()); mlDsaVectorNtt(sk.t0()); int[][][] aHat = generateA(sk.rho()); //Compute mu hash.update(sk.tr()); hash.update(message); byte[] mu = hash.squeeze(MU_LEN); hash.reset(); //Compute rho' hash.update(sk.k()); hash.update(rnd); hash.update(mu); byte[] rhoDoublePrime = hash.squeeze(MASK_SEED_LEN); hash.reset(); //Initialize vectors used in loop int[][] z = integerMatrixAlloc(mlDsa_l, ML_DSA_N); boolean[][] h = booleanMatrixAlloc(mlDsa_k, ML_DSA_N); byte[] commitmentHash = new byte[lambda/4]; int[][] y = integerMatrixAlloc(mlDsa_l, ML_DSA_N); int[][] yy = integerMatrixAlloc(mlDsa_l, ML_DSA_N); int[][] w = integerMatrixAlloc(mlDsa_k, ML_DSA_N); int[][] w0 = integerMatrixAlloc(mlDsa_k, ML_DSA_N); int[][] w1 = integerMatrixAlloc(mlDsa_k, ML_DSA_N); int[][] w_ct0 = integerMatrixAlloc(mlDsa_k, ML_DSA_N); int[] c = new int[ML_DSA_N]; int[][] cs1 = integerMatrixAlloc(mlDsa_l, ML_DSA_N); int[][] cs2 = integerMatrixAlloc(mlDsa_k, ML_DSA_N); int[][] ct0 = integerMatrixAlloc(mlDsa_k, ML_DSA_N); int kappa = 0; while (true) { expandMask(y, rhoDoublePrime, kappa); //Save non-ntt version of y for later use for (int i = 0; i < y.length; i++) { System.arraycopy(y[i], 0, yy[i], 0, ML_DSA_N); } //Compute w and w1 mlDsaVectorNtt(y); //y is now in NTT domain matrixVectorPointwiseMultiply(w, aHat, y); mlDsaVectorInverseNtt(w); //w is now in normal domain decompose(w, w0, w1); //Get commitment hash hash.update(mu); hash.update(simpleBitPack(wCoeffSize, w1)); commitmentHash = hash.squeeze(lambda/4); hash.reset(); //Get z and r0 sampleInBall(c, commitmentHash); mlDsaNtt(c); //c is now in NTT domain nttConstMultiply(cs1, c, sk.s1()); nttConstMultiply(cs2, c, sk.s2()); mlDsaVectorInverseNtt(cs1); mlDsaVectorInverseNtt(cs2); z = vectorAdd(z, yy, cs1); //w0 = w0 - cs2 (this is r0 in the spec) vectorSub(w0, cs2, false); //Update z and h kappa += mlDsa_l; if (vectorNormBound(z, gamma1 - beta) || vectorNormBound(w0, gamma2 - beta)) { continue; } else { nttConstMultiply(ct0, c, sk.t0()); mlDsaVectorInverseNtt(ct0); w = vectorSub(w, cs2, false); int hint_weight = makeHint(h, w, vectorAdd(w_ct0, w, ct0)); if (vectorNormBound(ct0, gamma2) || (hint_weight > omega)) { continue; } } sk.destroy(); return new ML_DSA_Signature(commitmentHash, z, h); } } public boolean verifyInternal(byte[] pkBytes, byte[] message, byte[] sigBytes) throws SignatureException { //Decode sig and initialize hash ML_DSA_Signature sig = sigDecode(sigBytes); var hash = new SHAKE256(0); //Decode pk ML_DSA_PublicKey pk = pkDecode(pkBytes); //Expand A int[][][] aHat = generateA(pk.rho()); //Generate tr hash.update(pkBytes); byte[] tr = hash.squeeze(TR_LEN); hash.reset(); //Generate mu hash.update(tr); hash.update(message); byte[] mu = hash.squeeze(MU_LEN); hash.reset(); //Get verifiers challenge int[] cHat = new int[ML_DSA_N]; sampleInBall(cHat, sig.commitmentHash()); mlDsaNtt(cHat); //Compute response norm and put it in NTT domain boolean zNorm = vectorNormBound(sig.response(), gamma1 - beta); mlDsaVectorNtt(sig.response()); //Reconstruct signer's commitment int[][] aHatZ = integerMatrixAlloc(mlDsa_k, ML_DSA_N); matrixVectorPointwiseMultiply(aHatZ, aHat, sig.response()); int[][] t1Hat = vectorConstMul(1 << ML_DSA_D, pk.t1()); mlDsaVectorNtt(t1Hat); int[][] ct1 = integerMatrixAlloc(mlDsa_k, ML_DSA_N); nttConstMultiply(ct1, cHat, t1Hat); int[][] wApprox = vectorSub(aHatZ, ct1, true); mlDsaVectorInverseNtt(wApprox); int[][] w1Prime = useHint(sig.hint(), wApprox); //Hash signer's commitment hash.update(mu); hash.update(simpleBitPack(wCoeffSize, w1Prime)); byte[] cTildePrime = hash.squeeze(lambda/4); //Check verify conditions boolean hashEq = MessageDigest.isEqual(sig.commitmentHash(), cTildePrime); return !zNorm && hashEq; } /* Data conversion functions in Section 7.1 of specification */ // Bit-pack the t1 and w1 vector into a byte array. // The coefficients of the polynomials in the vector should be // nonnegative and less than 2^bitsPerCoeff . public byte[] simpleBitPack(int bitsPerCoeff, int[][] vector) { byte[] result = new byte[(mlDsa_k * ML_DSA_N * bitsPerCoeff) / 8]; int acc = 0; int shift = 0; int i = 0; for (int[] poly : vector) { for (int m = 0; m < ML_DSA_N; m++) { acc += (poly[m] << shift); shift += bitsPerCoeff; while (shift >= 8) { result[i++] = (byte) acc; acc >>= 8; shift -= 8; } } } // Shift must now be 0 so we have all output bits return result; } public void bitPack(int[][] vector, int bitsPerCoeff, int maxValue, byte[] output, int offset) { int vecLen = vector.length; int acc = 0; int shift = 0; for (int[] poly : vector) { for (int m = 0; m < ML_DSA_N; m++) { acc += (maxValue - poly[m]) << shift; shift += bitsPerCoeff; while (shift >= 8) { output[offset++] = (byte) acc; acc >>= 8; shift -= 8; } } } } //This is simpleBitUnpack from FIPS 204. Since it is only called on the //vector t1 we can optimize for that case public int[][] t1Unpack(byte[] v) { int[][] t1 = integerMatrixAlloc(mlDsa_k, ML_DSA_N); for (int i = 0; i < mlDsa_k; i++) { for (int j = 0; j < ML_DSA_N / 4; j++) { int tOffset = j*4; int vOffset = (i*320) + (j*5); t1[i][tOffset] = (v[vOffset] & 0xFF) + ((v[vOffset+1] << 8) & 0x3FF); t1[i][tOffset+1] = ((v[vOffset+1] >> 2) & 0x3F) + ((v[vOffset+2] << 6) & 0x3FF); t1[i][tOffset+2] = ((v[vOffset+2] >> 4) & 0xF) + ((v[vOffset+3] << 4) & 0x3FF); t1[i][tOffset+3] = ((v[vOffset+3] >> 6) & 0x3) + ((v[vOffset+4] << 2) & 0x3FF); } } return t1; } public int[][] bitUnpack(int[][] result, byte[] v, int offset, int dim, int maxValue, int bitsPerCoeff) { switch (bitsPerCoeff) { case 3 -> { bitUnpackGeneral(result, v, offset, dim, maxValue, 3); } case 4 -> { bitUnpackGeneral(result, v, offset, dim, maxValue, 4); } case 13 -> { bitUnpackGeneral(result, v, offset, dim, maxValue, 13); } case 18 -> { bitUnpack18(result, v, offset, dim, maxValue); } case 20 -> { bitUnpack20(result, v, offset, dim, maxValue); } default -> throw new RuntimeException( "Wrong bitsPerCoeff value in bitUnpack (" + bitsPerCoeff + ")."); } return result; } public void bitUnpackGeneral(int[][] result, byte[] v, int offset, int dim, int maxValue, int bitsPerCoeff) { int mask = (1 << bitsPerCoeff) - 1; int top = 0; int shift = 0; int acc = 0; for (int i = 0; i < dim; i++) { for (int j = 0; j < ML_DSA_N; j++) { while (top - shift < bitsPerCoeff) { acc += ((v[offset++] & 0xff) << top); top += 8; } result[i][j] = maxValue - ((acc >> shift) & mask); shift += bitsPerCoeff; while (shift >= 8) { top -= 8; shift -= 8; acc >>>= 8; } } } } public void bitUnpack18(int [][] result, byte[] v, int offset, int dim, int maxValue) { int vIndex = offset; for (int i = 0; i < dim; i++) { for (int j = 0; j < ML_DSA_N; j += 4) { result[i][j] = maxValue - ((v[vIndex] & 0xff) + ((v[vIndex + 1] & 0xff) << 8) + ((v[vIndex + 2] & 0x3) << 16)); result[i][j + 1] = maxValue - (((v[vIndex + 2] >> 2) & 0x3f) + ((v[vIndex + 3] & 0xff) << 6) + ((v[vIndex + 4] & 0xf) << 14)); result[i][j + 2] = maxValue - (((v[vIndex + 4] >> 4) & 0xf) + ((v[vIndex + 5] & 0xff) << 4) + ((v[vIndex + 6] & 0x3f) << 12)); result[i][j + 3] = maxValue - (((v[vIndex + 6] >> 6) & 0x3) + ((v[vIndex + 7] & 0xff) << 2) + ((v[vIndex + 8] & 0xff) << 10)); vIndex += 9; } } } public void bitUnpack20(int[][] result, byte[] v, int offset, int dim, int maxValue) { int vIndex = offset; for (int i = 0; i < dim; i++) { for (int j = 0; j < ML_DSA_N; j += 2) { result[i][j] = maxValue - ((v[vIndex] & 0xff) + ((v[vIndex + 1] & 0xff) << 8) + ((v[vIndex + 2] & 0xf) << 16)); result[i][j + 1] = maxValue - (((v[vIndex + 2] >> 4) & 0xf) + ((v[vIndex + 3] & 0xff) << 4) + ((v[vIndex + 4] & 0xff) << 12)); vIndex += 5; } } } private void hintBitPack(boolean[][] h, byte[] buffer, int offset) { int idx = 0; for (int i = 0; i < mlDsa_k; i++) { for (int j = 0; j < ML_DSA_N; j++) { if (h[i][j]) { buffer[offset + idx] = (byte)j; idx++; } } buffer[offset + omega + i] = (byte)idx; } } private boolean[][] hintBitUnpack(byte[] y, int offset) { boolean[][] h = booleanMatrixAlloc(mlDsa_k, ML_DSA_N); int idx = 0; for (int i = 0; i < mlDsa_k; i++) { int j = y[offset + omega + i]; if (j < idx || j > omega) { return null; } int first = idx; while (idx < j) { if (idx > first) { if ((y[offset + idx - 1] & 0xff) >= (y[offset + idx] & 0xff)) { return null; } } int hintIndex = y[offset + idx] & 0xff; h[i][hintIndex] = true; idx++; } } while (idx < omega) { if (y[offset + idx] != 0) { return null; } idx++; } return h; } /* Encoding functions as specified in Section 7.2 of the specification */ public byte[] pkEncode(ML_DSA_PublicKey key) { byte[] t1Packed = simpleBitPack(10, key.t1); byte[] publicKeyBytes = new byte[A_SEED_LEN + t1Packed.length]; System.arraycopy(key.rho, 0, publicKeyBytes, 0, A_SEED_LEN); System.arraycopy(t1Packed, 0, publicKeyBytes, A_SEED_LEN, t1Packed.length); return publicKeyBytes; } public ML_DSA_PublicKey pkDecode(byte[] pk) { byte[] rho = Arrays.copyOfRange(pk, 0, A_SEED_LEN); byte[] v = Arrays.copyOfRange(pk, A_SEED_LEN, pk.length); int[][] t1 = t1Unpack(v); return new ML_DSA_PublicKey(rho, t1); } public byte[] skEncode(ML_DSA_PrivateKey key) { byte[] skBytes = new byte[A_SEED_LEN + K_LEN + key.tr.length + s1PackedLength + s2PackedLength + t0PackedLength]; int pos = 0; System.arraycopy(key.rho, 0, skBytes, pos, A_SEED_LEN); pos += A_SEED_LEN; System.arraycopy(key.k, 0, skBytes, pos, K_LEN); pos += K_LEN; System.arraycopy(key.tr, 0, skBytes, pos, TR_LEN); pos += TR_LEN; bitPack(key.s1, s1s2CoeffSize, eta, skBytes, pos); pos += s1PackedLength; bitPack(key.s2, s1s2CoeffSize, eta, skBytes, pos); pos += s2PackedLength; bitPack(key.t0, T0_COEFF_SIZE, 1 << 12, skBytes, pos); return skBytes; } public ML_DSA_PrivateKey skDecode(byte[] sk) { byte[] rho = new byte[A_SEED_LEN]; System.arraycopy(sk, 0, rho, 0, A_SEED_LEN); byte[] k = new byte[K_LEN]; System.arraycopy(sk, A_SEED_LEN, k, 0, K_LEN); byte[] tr = new byte[TR_LEN]; System.arraycopy(sk, A_SEED_LEN + K_LEN, tr, 0, TR_LEN); //Parse s1 int start = A_SEED_LEN + K_LEN + TR_LEN; int end = start + (32 * mlDsa_l * s1s2CoeffSize); int[][] s1 = integerMatrixAlloc(mlDsa_l, ML_DSA_N); bitUnpack(s1, sk, start, mlDsa_l, eta, s1s2CoeffSize); //Parse s2 start = end; end += 32 * s1s2CoeffSize * mlDsa_k; int[][] s2 = integerMatrixAlloc(mlDsa_k, ML_DSA_N); bitUnpack(s2, sk, start, mlDsa_k, eta, s1s2CoeffSize); //Parse t0 start = end; int[][] t0 = integerMatrixAlloc(mlDsa_k, ML_DSA_N); bitUnpack(t0, sk, start, mlDsa_k, 1 << 12, T0_COEFF_SIZE); return new ML_DSA_PrivateKey(rho, k, tr, s1, s2, t0); } public byte[] sigEncode(ML_DSA_Signature sig) { int cSize = lambda / 4; int zSize = mlDsa_l * 32 * (1 + gamma1Bits); byte[] sigBytes = new byte[cSize + zSize + omega + mlDsa_k]; System.arraycopy(sig.commitmentHash, 0, sigBytes, 0, cSize); bitPack(sig.response, gamma1Bits + 1, gamma1, sigBytes, cSize); hintBitPack(sig.hint, sigBytes, cSize + zSize); return sigBytes; } public ML_DSA_Signature sigDecode(byte[] sig) throws SignatureException { int cSize = lambda / 4; int zSize = mlDsa_l * 32 * (1 + gamma1Bits); int sigLen = cSize + zSize + omega + mlDsa_k; if (sig.length != sigLen) { throw new SignatureException("Incorrect signature length"); } //Decode cTilde byte[] cTilde = Arrays.copyOfRange(sig, 0, lambda/4); //Decode z int start = cSize; int end = start + zSize; int[][] z = integerMatrixAlloc(mlDsa_l, ML_DSA_N); bitUnpack(z, sig, start, mlDsa_l, gamma1, gamma1Bits + 1); //Decode h start = end; boolean[][] h = hintBitUnpack(sig, start); if (h == null) { throw new SignatureException("Invalid hints encoding"); } return new ML_DSA_Signature(cTilde, z, h); } /* Auxiliary functions defined in Section 7.3 of specification */ private class Shake256Slicer { SHAKE256 xof; byte[] block; int byteOffset; int current; int bitsInCurrent; int bitsPerCall; int bitMask; Shake256Slicer(SHAKE256 xof, int bitsPerCall) { this.xof = xof; //BitsPerCall can only be 4 (when called from sampleS1S2), //or 8 (when called from sampleInBall) this.bitsPerCall = bitsPerCall; bitMask = (1 << bitsPerCall) - 1; current = 0; byteOffset = SHAKE256_BLOCK_SIZE; bitsInCurrent = 0; block = new byte[SHAKE256_BLOCK_SIZE]; } void reset() { xof.reset(); current = 0; byteOffset = SHAKE256_BLOCK_SIZE; bitsInCurrent = 0; } int squeezeBits() { while (bitsInCurrent < bitsPerCall) { if (byteOffset == SHAKE256_BLOCK_SIZE) { xof.squeeze(block, 0, SHAKE256_BLOCK_SIZE); byteOffset = 0; } current += ((block[byteOffset++] & 0xff) << bitsInCurrent); bitsInCurrent += 8; } int result = current & bitMask; current >>= bitsPerCall; bitsInCurrent -= bitsPerCall; return result; } } private void sampleInBall(int[] c, byte[] rho) { var xof = new SHAKE256(0); Shake256Slicer slicer = new Shake256Slicer(xof, 8); xof.update(rho); long parity = 0; for (int i = 0; i < 8; i++) { long sample = slicer.squeezeBits(); parity |= sample << 8 * i; } Arrays.fill(c, 0); int k = 8; for (int i = 256 - tau; i < 256; i++) { //Get random index < i int j = slicer.squeezeBits(); while (j > i) { j = slicer.squeezeBits(); } //Swap c[i] and c[j], set c[j] based on parity c[i] = c[j]; c[j] = (int) (1 - 2 * (parity & 1)); parity >>= 1; } } private int[][][] generateA(byte[] seed) { // Manually do multidimensional array initialization for performance int[][][] a = new int[mlDsa_k][][]; for (int i = 0; i < mlDsa_k; i++) { a[i] = new int[mlDsa_l][]; } int nrPar = 2; int rhoLen = seed.length; byte[] seedBuf = new byte[SHAKE128_BLOCK_SIZE]; System.arraycopy(seed, 0, seedBuf, 0, seed.length); seedBuf[rhoLen + 2] = 0x1F; seedBuf[SHAKE128_BLOCK_SIZE - 1] = (byte)0x80; byte[][] xofBufArr = new byte[nrPar][SHAKE128_BLOCK_SIZE]; int[] iIndex = new int[nrPar]; int[] jIndex = new int[nrPar]; int[] parsedBuf = new int[SHAKE128_BLOCK_SIZE / 3]; int parInd = 0; boolean allDone; int[] ofs = new int[nrPar]; Arrays.fill(ofs, 0); int[][] aij = new int[nrPar][]; try { Shake128Parallel parXof = new Shake128Parallel(xofBufArr); for (int i = 0; i < mlDsa_k; i++) { for (int j = 0; j < mlDsa_l; j++) { xofBufArr[parInd] = seedBuf.clone(); xofBufArr[parInd][rhoLen] = (byte) j; xofBufArr[parInd][rhoLen + 1] = (byte) i; iIndex[parInd] = i; jIndex[parInd] = j; ofs[parInd] = 0; aij[parInd] = new int[ML_DSA_N]; parInd++; if ((parInd == nrPar) || ((i == mlDsa_k - 1) && (j == mlDsa_l - 1))) { parXof.reset(xofBufArr); allDone = false; while (!allDone) { allDone = true; parXof.squeezeBlock(); for (int k = 0; k < parInd; k++) { int parsedOfs = 0; int tmp; if (ofs[k] < ML_DSA_N) { for (int l = 0; l < SHAKE128_BLOCK_SIZE; l += 3) { byte[] rawBuf = xofBufArr[k]; parsedBuf[l / 3] = (rawBuf[l] & 0xFF) + ((rawBuf[l + 1] & 0xFF) << 8) + ((rawBuf[l + 2] & 0x7F) << 16); } } while ((ofs[k] < ML_DSA_N) && (parsedOfs < SHAKE128_BLOCK_SIZE / 3)) { tmp = parsedBuf[parsedOfs++]; if (tmp < ML_DSA_Q) { aij[k][ofs[k]] = tmp; ofs[k]++; } } if (ofs[k] < ML_DSA_N) { allDone = false; } } } for (int k = 0; k < parInd; k++) { a[iIndex[k]][jIndex[k]] = aij[k]; } parInd = 0; } } } } catch (InvalidAlgorithmParameterException e) { // This should never happen since xofBufArr is of the correct size throw new RuntimeException("Internal error."); } return a; } private void sampleS1S2(int[][] s1, int[][] s2, SHAKE256 xof, byte[] rhoPrime) { byte[] seed = new byte[S1S2_SEED_LEN + 2]; System.arraycopy(rhoPrime, 0, seed, 0, S1S2_SEED_LEN); Shake256Slicer slicer = new Shake256Slicer(xof, 4); for (int i = 0; i < mlDsa_l; i++) { seed[S1S2_SEED_LEN] = (byte) i; seed[S1S2_SEED_LEN + 1] = 0; slicer.reset(); xof.update(seed); if (eta == 2) { for (int j = 0; j < ML_DSA_N; j++) { int sample; do { sample = slicer.squeezeBits(); } while (sample > 14); // 2 - sample mod 5 s1[i][j] = eta - sample + (205 * sample >> 10) * 5; } } else { // eta == 4 for (int j = 0; j < ML_DSA_N; j++) { int sample; do { sample = slicer.squeezeBits(); } while (sample > 2 * eta); s1[i][j] = eta - sample; } } } for (int i = 0; i < mlDsa_k; i++) { seed[S1S2_SEED_LEN] = (byte) (mlDsa_l + i); seed[S1S2_SEED_LEN + 1] = 0; slicer.reset(); xof.update(seed); if (eta == 2) { for (int j = 0; j < ML_DSA_N; j++) { int sample; do { sample = slicer.squeezeBits(); } while (sample > 14); s2[i][j] = eta - sample + (205 * sample >> 10) * 5; } } else { for (int j = 0; j < ML_DSA_N; j++) { int sample; do { sample = slicer.squeezeBits(); } while (sample > 2 * eta); s2[i][j] = eta - sample; } } } } private void expandMask(int[][] result, byte[] rho, int mu) { var xof = new SHAKE256(0); int c = 1 + gamma1Bits; byte[] v = new byte[mlDsa_l * 32 * c]; for (int r = 0; r < mlDsa_l; r++) { int a = mu + r; byte[] n = {(byte) a, (byte) (a >> 8)}; xof.update(rho); xof.update(n); xof.squeeze(v, r * 32 * c, 32 * c); xof.reset(); } bitUnpack(result, v, 0, mlDsa_l, gamma1, c); } /* Auxiliary functions defined in section 7.4 of specification */ private void power2Round(int[][] input, int[][] lowPart, int[][] highPart) { for (int i = 0; i < mlDsa_k; i++) { for (int m = 0; m < ML_DSA_N; m++) { int rplus = input[i][m]; int r0 = input[i][m] & D_MASK; int r00 = (1 << (ML_DSA_D - 1)) - r0 ; // 2^d/2 - r+ r0 -= (r00 >> 31) & (1 << ML_DSA_D); //0 if r+ < 2^d/2 lowPart[i][m] = r0; highPart[i][m] = (rplus - r0) >> ML_DSA_D; } } } private void decompose(int[][] input, int[][] lowPart, int[][] highPart) { int multiplier = (gamma2 == 95232 ? 22 : 8); for (int i = 0; i < mlDsa_k; i++) { mlDsaDecomposePoly(input[i], lowPart[i], highPart[i], gamma2 * 2, multiplier); } } private int[][] highBits(int[][] input) { int[][] lowPart = integerMatrixAlloc(mlDsa_k, ML_DSA_N); int[][] highPart = integerMatrixAlloc(mlDsa_k, ML_DSA_N); decompose(input, lowPart, highPart); return highPart; } //Creates the hint polynomial and returns its hamming weight private int makeHint(boolean[][] res, int[][] z, int[][] r) { int hammingWeight = 0; int[][] r1 = highBits(r); int[][] v1 = highBits(z); for (int i = 0; i < mlDsa_k; i++) { for (int j = 0; j < ML_DSA_N; j++) { if (r1[i][j] != v1[i][j]) { res[i][j] = true; hammingWeight++; } else { res[i][j] = false; } } } return hammingWeight; } private int[][] useHint(boolean[][] h, int[][] r) { int m = (ML_DSA_Q - 1) / (2*gamma2); int[][] lowPart = r; int[][] highPart = integerMatrixAlloc(mlDsa_k, ML_DSA_N); decompose(r, lowPart, highPart); for (int i = 0; i < mlDsa_k; i++) { for (int j = 0; j < ML_DSA_N; j++) { if (h[i][j]) { highPart[i][j] += lowPart[i][j] > 0 ? 1 : -1; } highPart[i][j] = ((highPart[i][j] % m) + m) % m; } } return highPart; } /* NTT functions as specified in Section 7.5 of specification */ public static void mlDsaNtt(int[] coeffs) { assert coeffs.length == ML_DSA_N; implDilithiumAlmostNtt(coeffs, MONT_ZETAS_FOR_VECTOR_NTT); implDilithiumMontMulByConstant(coeffs, MONT_R_MOD_Q); } @IntrinsicCandidate static int implDilithiumAlmostNtt(int[] coeffs, int[] zetas) { implDilithiumAlmostNttJava(coeffs); return 1; } static void implDilithiumAlmostNttJava(int[] coeffs) { int dimension = ML_DSA_N; int m = 0; for (int l = dimension / 2; l > 0; l /= 2) { for (int s = 0; s < dimension; s += 2 * l) { for (int j = s; j < s + l; j++) { int tmp = montMul(MONT_ZETAS_FOR_NTT[m], coeffs[j + l]); coeffs[j + l] = coeffs[j] - tmp; coeffs[j] = coeffs[j] + tmp; } m++; } } } public static void mlDsaInverseNtt(int[] coeffs) { assert coeffs.length == ML_DSA_N; implDilithiumAlmostInverseNtt(coeffs, MONT_ZETAS_FOR_VECTOR_INVERSE_NTT); implDilithiumMontMulByConstant(coeffs, MONT_DIM_INVERSE); } @IntrinsicCandidate static int implDilithiumAlmostInverseNtt(int[] coeffs, int[] zetas) { implDilithiumAlmostInverseNttJava(coeffs); return 1; } static void implDilithiumAlmostInverseNttJava(int[] coeffs) { int dimension = ML_DSA_N; int m = MONT_ZETAS_FOR_NTT.length - 1; for (int l = 1; l < dimension; l *= 2) { for (int s = 0; s < dimension; s += 2 * l) { for (int j = s; j < s + l; j++) { int tmp = coeffs[j]; coeffs[j] = (tmp + coeffs[j + l]); coeffs[j + l] = montMul(tmp - coeffs[j + l], -MONT_ZETAS_FOR_NTT[m]); } m--; } } } void mlDsaVectorNtt(int[][] vector) { for (int[] ints : vector) { mlDsaNtt(ints); } } void mlDsaVectorInverseNtt(int[][] vector) { for (int[] ints : vector) { mlDsaInverseNtt(ints); } } public static void mlDsaNttMultiply(int[] product, int[] coeffs1, int[] coeffs2) { assert (coeffs1.length == ML_DSA_N) && (coeffs2.length == ML_DSA_N); implDilithiumNttMult(product, coeffs1, coeffs2); } @IntrinsicCandidate static int implDilithiumNttMult(int[] product, int[] coeffs1, int[] coeffs2) { implDilithiumNttMultJava(product, coeffs1, coeffs2); return 1; } static void implDilithiumNttMultJava(int[] product, int[] coeffs1, int[] coeffs2) { for (int i = 0; i < ML_DSA_N; i++) { product[i] = montMul(coeffs1[i], toMont(coeffs2[i])); } } @IntrinsicCandidate static int implDilithiumMontMulByConstant(int[] coeffs, int constant) { implDilithiumMontMulByConstantJava(coeffs, constant); return 1; } static void implDilithiumMontMulByConstantJava(int[] coeffs, int constant) { for (int i = 0; i < ML_DSA_N; i++) { coeffs[i] = montMul((coeffs[i]), constant); } } public static void mlDsaDecomposePoly(int[] input, int[] lowPart, int[] highPart, int twoGamma2, int multiplier) { assert (input.length == ML_DSA_N) && (lowPart.length == ML_DSA_N) && (highPart.length == ML_DSA_N); implDilithiumDecomposePoly(input, lowPart, highPart,twoGamma2, multiplier); } @IntrinsicCandidate static int implDilithiumDecomposePoly(int[] input, int[] lowPart, int[] highPart, int twoGamma2, int multiplier) { decomposePolyJava(input, lowPart, highPart, twoGamma2, multiplier); return 1; } static void decomposePolyJava(int[] input, int[] lowPart, int[] highPart, int twoGamma2, int multiplier) { int dilithiumBarrettAddend = 5373807; for (int m = 0; m < ML_DSA_N; m++) { int rplus = input[m]; rplus -= ((rplus + dilithiumBarrettAddend) >> 23) * ML_DSA_Q; rplus += ((rplus >> 31) & ML_DSA_Q); int quotient = (rplus * multiplier) >> 22; int r0 = rplus - quotient * twoGamma2; int mask = (twoGamma2 - r0) >> 22; r0 -= (mask & twoGamma2); quotient += (mask & 1); mask = (twoGamma2 / 2 - r0) >> 31; r0 -= (mask & twoGamma2); quotient += (mask & 1); int r1 = rplus - r0 - (ML_DSA_Q - 1); r1 = (r1 | (-r1)) >> 31; // 0 if rplus - r0 == (dilithium_q - 1), -1 otherwise r0 += ~r1; // quotient = (rplus - r0) / twoGamma2; r1 = r1 & quotient; lowPart[m] = r0; highPart[m] = r1; } } private void matrixVectorPointwiseMultiply(int[][] res, int[][][] matrix, int[][] vector) { int resulti[] = new int[ML_DSA_N]; int[] product = new int[ML_DSA_N]; for (int i = 0; i < mlDsa_k; i++) { for (int m = 0; m < ML_DSA_N; m++) { resulti[m] = 0; } for (int j = 0; j < mlDsa_l; j++) { mlDsaNttMultiply(product, matrix[i][j], vector[j]); for (int m = 0; m < ML_DSA_N; m++) { resulti[m] += product[m]; } } for (int m = 0; m < ML_DSA_N; m++) { res[i][m] = montMul(resulti[m], MONT_R_MOD_Q); } } } private void nttConstMultiply(int[][] res, int[] a, int[][] b) { for (int i = 0; i < b.length; i++) { mlDsaNttMultiply(res[i], a, b[i]); } } private int[][] vectorConstMul(int c, int[][] vec) { int[][] res = integerMatrixAlloc(vec.length, vec[0].length); for (int i = 0; i < vec.length; i++) { for (int j = 0; j < vec[0].length; j++) { res[i][j] = montMul(c, toMont(vec[i][j])); } } return res; // -q < res[i][j] < q } // Adds two vectors of polynomials // The coefficients in the input should be between -MONT_Q and MONT_Q . // The coefficients in the output will be nonnegative and less than MONT_Q int[][] vectorAddPos(int[][] vec1, int[][] vec2) { int dim = vec1.length; int[][] result = integerMatrixAlloc(dim, ML_DSA_N); for (int i = 0; i < dim; i++) { for (int m = 0; m < ML_DSA_N; m++) { int r = vec1[i][m] + vec2[i][m]; // -2 * MONT_Q < r < 2 * MONT_Q r += (((r >> 31) & (2 * MONT_Q)) - MONT_Q); // -MONT_Q < r < MONT_Q r += ((r >> 31) & MONT_Q); // 0 <= r < MONT_Q result[i][m] = r; } } return result; } int[][] vectorAdd(int[][] result, int[][] vec1, int[][] vec2) { for (int i = 0; i < result.length; i++) { for (int j = 0; j < ML_DSA_N; j++) { int tmp = vec1[i][j] + vec2[i][j]; result[i][j] = tmp; } } return result; } int[][] vectorSub(int[][] vec1, int[][] vec2, boolean needsAdjustment) { int dim = vec1.length; for (int i = 0; i < dim; i++) { for (int j = 0; j < ML_DSA_N; j++) { int tmp = vec1[i][j] - vec2[i][j]; if (needsAdjustment) { if (tmp <= -ML_DSA_Q) { tmp += ML_DSA_Q; } else if (tmp >= ML_DSA_Q) { tmp -= ML_DSA_Q; } } vec1[i][j] = tmp; } } return vec1; } //Precondition: 2^-31 <= r1 <= 2^31 - 5 * 2^20, and bound < q - 5234431 //Computes whether the infinity norm of a vector is >= bound boolean vectorNormBound(int[][] vec, int bound) { boolean res = false; for (int i = 0; i < vec.length; i++) { for (int j = 0; j < ML_DSA_N; j++) { int r1 = vec[i][j]; r1 = r1 - ((r1 + (5 << 20)) >> 23) * ML_DSA_Q; r1 = r1 - ((r1 >> 31) & r1) * 2; res |= (r1 >= bound); } } return res; } // precondition: -2^31 * MONT_Q <= a, b < 2^31, -2^31 < a * b < 2^31 * MONT_Q // computes a * b * 2^-32 mod MONT_Q // the result is greater than -MONT_Q and less than MONT_Q // See e.g. Algorithm 3 in https://eprint.iacr.org/2018/039.pdf private static int montMul(int b, int c) { long a = (long) b * (long) c; int aHigh = (int) (a >> MONT_R_BITS); int aLow = (int) a; int m = MONT_Q_INV_MOD_R * aLow; // signed low product // subtract signed high product return (aHigh - (int) (((long)m * MONT_Q) >> MONT_R_BITS)); } static int toMont(int a) { return montMul(a, MONT_R_SQUARE_MOD_Q); } // For multidimensional array initialization, manually allocating each entry is // faster than doing the entire initialization in one go static boolean[][] booleanMatrixAlloc(int first, int second) { boolean[][] res = new boolean[first][]; for (int i = 0; i < first; i++) { res[i] = new boolean[second]; } return res; } static int[][] integerMatrixAlloc(int first, int second) { int[][] res = new int[first][]; for (int i = 0; i < first; i++) { res[i] = new int[second]; } return res; } }