diff --git a/.jcheck/conf b/.jcheck/conf
index fd9a62ff0a58..d592833b176c 100644
--- a/.jcheck/conf
+++ b/.jcheck/conf
@@ -1,7 +1,7 @@
[general]
project=jdk-updates
jbs=JDK
-version=21.0.12
+version=21.0.13
[checks]
error=author,committer,reviewers,merge,issues,executable,symlink,message,hg-tag,whitespace,problemlists
diff --git a/doc/testing.html b/doc/testing.html
index b9720dd31a89..eeb67854a53c 100644
--- a/doc/testing.html
+++ b/doc/testing.html
@@ -509,6 +509,8 @@
REPEAT_COUNT
REPORT
Use this report style when reporting test results (sent to JTReg as
-report). Defaults to files.
+MANUAL
+Set to true to execute manual tests only.
Gtest keywords
REPEAT
The number of times to repeat the tests
diff --git a/doc/testing.md b/doc/testing.md
index 9faf3d9b0f45..6e605d0db9d0 100644
--- a/doc/testing.md
+++ b/doc/testing.md
@@ -498,6 +498,10 @@ Defaults to 0.
Use this report style when reporting test results (sent to JTReg as `-report`).
Defaults to `files`.
+#### MANUAL
+
+Set to `true` to execute manual tests only.
+
### Gtest keywords
#### REPEAT
diff --git a/make/RunTests.gmk b/make/RunTests.gmk
index f61125f53315..859eb68031ac 100644
--- a/make/RunTests.gmk
+++ b/make/RunTests.gmk
@@ -205,7 +205,8 @@ $(eval $(call SetTestOpt,REPORT,JTREG))
$(eval $(call ParseKeywordVariable, JTREG, \
SINGLE_KEYWORDS := JOBS TIMEOUT_FACTOR FAILURE_HANDLER_TIMEOUT \
TEST_MODE ASSERT VERBOSE RETAIN TEST_THREAD_FACTORY MAX_MEM RUN_PROBLEM_LISTS \
- RETRY_COUNT REPEAT_COUNT MAX_OUTPUT REPORT $(CUSTOM_JTREG_SINGLE_KEYWORDS), \
+ RETRY_COUNT REPEAT_COUNT MAX_OUTPUT REPORT MANUAL \
+ $(CUSTOM_JTREG_SINGLE_KEYWORDS), \
STRING_KEYWORDS := OPTIONS JAVA_OPTIONS VM_OPTIONS KEYWORDS \
EXTRA_PROBLEM_LISTS LAUNCHER_OPTIONS \
$(CUSTOM_JTREG_STRING_KEYWORDS), \
@@ -793,7 +794,13 @@ define SetupRunJtregTestBody
-vmoption:-Dtest.boot.jdk="$$(BOOT_JDK)" \
-vmoption:-Djava.io.tmpdir="$$($1_TEST_TMP_DIR)"
- $1_JTREG_BASIC_OPTIONS += -automatic -ignore:quiet
+ $1_JTREG_BASIC_OPTIONS += -ignore:quiet
+
+ ifeq ($$(JTREG_MANUAL), true)
+ $1_JTREG_BASIC_OPTIONS += -manual
+ else
+ $1_JTREG_BASIC_OPTIONS += -automatic
+ endif
# Make it possible to specify the JIB_DATA_DIR for tests using the
# JIB Artifact resolver
diff --git a/make/autoconf/flags-ldflags.m4 b/make/autoconf/flags-ldflags.m4
index e5c345fdef16..a1f50ffdaf3e 100644
--- a/make/autoconf/flags-ldflags.m4
+++ b/make/autoconf/flags-ldflags.m4
@@ -101,6 +101,9 @@ AC_DEFUN([FLAGS_SETUP_LDFLAGS_HELPER],
# Setup OS-dependent LDFLAGS
if test "x$OPENJDK_TARGET_OS" = xmacosx && test "x$TOOLCHAIN_TYPE" = xclang; then
+ if test x$DEBUG_LEVEL = xrelease; then
+ BASIC_LDFLAGS_JDK_ONLY="$BASIC_LDFLAGS_JDK_ONLY -Wl,-dead_strip"
+ fi
# FIXME: We should really generalize SET_SHARED_LIBRARY_ORIGIN instead.
OS_LDFLAGS_JVM_ONLY="-Wl,-rpath,@loader_path/. -Wl,-rpath,@loader_path/.."
OS_LDFLAGS="-mmacosx-version-min=$MACOSX_VERSION_MIN"
diff --git a/make/autoconf/lib-tests.m4 b/make/autoconf/lib-tests.m4
index 9c674721e39b..288f41b58a2c 100644
--- a/make/autoconf/lib-tests.m4
+++ b/make/autoconf/lib-tests.m4
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2018, 2026, 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
@@ -28,7 +28,7 @@
################################################################################
# Minimum supported versions
-JTREG_MINIMUM_VERSION=7.5.2
+JTREG_MINIMUM_VERSION=8.2.1
GTEST_MINIMUM_VERSION=1.14.0
###############################################################################
diff --git a/make/conf/github-actions.conf b/make/conf/github-actions.conf
index b35533eb1ce6..e1001dd6c4fe 100644
--- a/make/conf/github-actions.conf
+++ b/make/conf/github-actions.conf
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2020, 2026, 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
@@ -26,7 +26,7 @@
# Versions and download locations for dependencies used by GitHub Actions (GHA)
GTEST_VERSION=1.14.0
-JTREG_VERSION=7.5.2+1
+JTREG_VERSION=8.2.1+1
LINUX_X64_BOOT_JDK_EXT=tar.gz
LINUX_X64_BOOT_JDK_URL=https://github.com/adoptium/temurin21-binaries/releases/download/jdk-21.0.11%2B10/OpenJDK21U-jdk_x64_linux_hotspot_21.0.11_10.tar.gz
diff --git a/make/conf/jib-profiles.js b/make/conf/jib-profiles.js
index 1aa445d88e9b..eabfc859a202 100644
--- a/make/conf/jib-profiles.js
+++ b/make/conf/jib-profiles.js
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2026, 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
@@ -1200,9 +1200,9 @@ var getJibProfilesDependencies = function (input, common) {
jtreg: {
server: "jpg",
product: "jtreg",
- version: "7.5.2",
+ version: "8.2.1",
build_number: "1",
- file: "bundles/jtreg-7.5.2+1.zip",
+ file: "bundles/jtreg-8.2.1+1.zip",
environment_name: "JT_HOME",
environment_path: input.get("jtreg", "home_path") + "/bin",
configure_args: "--with-jtreg=" + input.get("jtreg", "home_path"),
@@ -1532,7 +1532,7 @@ var getVersionNumbers = function () {
var isWsl = function (input) {
return ( input.build_osenv == "wsl"
|| (input.build_os == "linux"
- && java.lang.System.getProperty("os.version").contains("Microsoft")));
+ && java.lang.System.getProperty("os.version").toLowerCase().contains("microsoft")));
}
var error = function (s) {
diff --git a/make/conf/version-numbers.conf b/make/conf/version-numbers.conf
index acb2393671cb..906c95217b49 100644
--- a/make/conf/version-numbers.conf
+++ b/make/conf/version-numbers.conf
@@ -28,15 +28,15 @@
DEFAULT_VERSION_FEATURE=21
DEFAULT_VERSION_INTERIM=0
-DEFAULT_VERSION_UPDATE=12
+DEFAULT_VERSION_UPDATE=13
DEFAULT_VERSION_PATCH=0
DEFAULT_VERSION_EXTRA1=0
DEFAULT_VERSION_EXTRA2=0
DEFAULT_VERSION_EXTRA3=0
-DEFAULT_VERSION_DATE=2026-07-21
+DEFAULT_VERSION_DATE=2026-10-20
DEFAULT_VERSION_CLASSFILE_MAJOR=65 # "`$EXPR $DEFAULT_VERSION_FEATURE + 44`"
DEFAULT_VERSION_CLASSFILE_MINOR=0
DEFAULT_VERSION_DOCS_API_SINCE=11
DEFAULT_ACCEPTABLE_BOOT_VERSIONS="20 21"
DEFAULT_JDK_SOURCE_TARGET_VERSION=21
-DEFAULT_PROMOTED_VERSION_PRE=
+DEFAULT_PROMOTED_VERSION_PRE=ea
diff --git a/make/data/hotspot-symbols/symbols-unix b/make/data/hotspot-symbols/symbols-unix
index 5cf6662cba84..be5528b12261 100644
--- a/make/data/hotspot-symbols/symbols-unix
+++ b/make/data/hotspot-symbols/symbols-unix
@@ -188,6 +188,7 @@ JVM_RawMonitorDestroy
JVM_RawMonitorEnter
JVM_RawMonitorExit
JVM_ReferenceClear
+JVM_ReferenceGet
JVM_ReferenceRefersTo
JVM_RegisterContinuationMethods
JVM_RegisterLambdaProxyClassForArchiving
diff --git a/make/hotspot/lib/JvmOverrideFiles.gmk b/make/hotspot/lib/JvmOverrideFiles.gmk
index b50d6f8bb36d..f64c538cd231 100644
--- a/make/hotspot/lib/JvmOverrideFiles.gmk
+++ b/make/hotspot/lib/JvmOverrideFiles.gmk
@@ -37,6 +37,10 @@ ifeq ($(TOOLCHAIN_TYPE), gcc)
# Need extra inlining to collapse shared marking code into the hot marking loop
BUILD_LIBJVM_shenandoahMark.cpp_CXXFLAGS := --param inline-unit-growth=1000
endif
+ # disable lto in g1ParScanThreadState because of special inlining/flattening used there
+ ifeq ($(call check-jvm-feature, link-time-opt), true)
+ BUILD_LIBJVM_g1ParScanThreadState.cpp_CXXFLAGS := -fno-lto
+ endif
endif
LIBJVM_FDLIBM_COPY_OPT_FLAG := $(CXX_O_FLAG_NONE)
diff --git a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp
index 5d3ca441b410..9ab80e91639f 100644
--- a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp
+++ b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2024, Red Hat Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -2571,6 +2571,11 @@ template
ldst_sstr(T, index, a, op1, op2, Vt, Vt2, Vt3, Vt4); \
}
+ INSN1(ld1, 0b001101010, 0b0000);
+ INSN2(ld2, 0b001101011, 0b0000);
+ INSN3(ld3, 0b001101010, 0b0010);
+ INSN4(ld4, 0b001101011, 0b0010);
+
INSN1(st1, 0b001101000, 0b0000);
INSN2(st2, 0b001101001, 0b0000);
INSN3(st3, 0b001101000, 0b0010);
@@ -2605,6 +2610,7 @@ template
void NAME(FloatRegister Vd, SIMD_Arrangement T, FloatRegister Vn, FloatRegister Vm) { \
guarantee(T != T1Q && T != T1D, "incorrect arrangement"); \
if (!acceptT2D) guarantee(T != T2D, "incorrect arrangement"); \
+ if (opc2 == 0b101101) guarantee(T != T8B && T != T16B, "incorrect arrangement"); \
starti; \
f(0, 31), f((int)T & 1, 30), f(opc, 29), f(0b01110, 28, 24); \
f((int)T >> 1, 23, 22), f(1, 21), rf(Vm, 16), f(opc2, 15, 10); \
@@ -2628,6 +2634,8 @@ template
INSN(minv, 0, 0b011011, false); // accepted arrangements: T8B, T16B, T4H, T8H, T2S, T4S
INSN(smaxp, 0, 0b101001, false); // accepted arrangements: T8B, T16B, T4H, T8H, T2S, T4S
INSN(sminp, 0, 0b101011, false); // accepted arrangements: T8B, T16B, T4H, T8H, T2S, T4S
+ INSN(sqdmulh,0, 0b101101, false); // accepted arrangements: T4H, T8H, T2S, T4S
+ INSN(shsubv, 0, 0b001001, false); // accepted arrangements: T8B, T16B, T4H, T8H, T2S, T4S
#undef INSN
diff --git a/src/hotspot/cpu/aarch64/register_aarch64.cpp b/src/hotspot/cpu/aarch64/register_aarch64.cpp
index 3a46e38a72a7..b61627f6b91e 100644
--- a/src/hotspot/cpu/aarch64/register_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/register_aarch64.cpp
@@ -59,3 +59,23 @@ const char* PRegister::PRegisterImpl::name() const {
};
return is_valid() ? names[encoding()] : "pnoreg";
}
+
+// convenience methods for splitting 8-way vector register sequences
+// in half -- needed because vector operations can normally only be
+// benefit from 4-way instruction parallelism
+
+VSeq<4> vs_front(const VSeq<8>& v) {
+ return VSeq<4>(v.base(), v.delta());
+}
+
+VSeq<4> vs_back(const VSeq<8>& v) {
+ return VSeq<4>(v.base() + 4 * v.delta(), v.delta());
+}
+
+VSeq<4> vs_even(const VSeq<8>& v) {
+ return VSeq<4>(v.base(), v.delta() * 2);
+}
+
+VSeq<4> vs_odd(const VSeq<8>& v) {
+ return VSeq<4>(v.base() + 1, v.delta() * 2);
+}
diff --git a/src/hotspot/cpu/aarch64/register_aarch64.hpp b/src/hotspot/cpu/aarch64/register_aarch64.hpp
index eef4182efe91..45351bb39451 100644
--- a/src/hotspot/cpu/aarch64/register_aarch64.hpp
+++ b/src/hotspot/cpu/aarch64/register_aarch64.hpp
@@ -408,4 +408,72 @@ inline Register as_Register(FloatRegister reg) {
return as_Register(reg->encoding());
}
+// AArch64 Vector Register Sequence management support
+//
+// VSeq implements an indexable (by operator[]) vector register
+// sequence starting from a fixed base register and with a fixed delta
+// (defaulted to 1, but sometimes 0 or 2) e.g. VSeq<4>(16) will return
+// registers v16, ... v19 for indices 0, ... 3.
+//
+// Generator methods may iterate across sets of VSeq<4> to schedule an
+// operation 4 times using distinct input and output registers,
+// profiting from 4-way instruction parallelism.
+//
+// A VSeq<2> can be used to specify registers loaded with special
+// constants e.g. --> .
+//
+// A VSeq with base n and delta 0 can be used to generate code that
+// combines values in another VSeq with the constant in register vn.
+//
+// A VSeq with base n and delta 2 can be used to select an odd or even
+// indexed set of registers.
+//
+// Methods which accept arguments of type VSeq<8>, may split their
+// inputs into front and back halves or odd and even halves (see
+// convenience methods below).
+
+template class VSeq {
+ static_assert(N >= 2, "vector sequence length must be greater than 1");
+ static_assert(N <= 8, "vector sequence length must not exceed 8");
+ static_assert((N & (N - 1)) == 0, "vector sequence length must be power of two");
+private:
+ int _base; // index of first register in sequence
+ int _delta; // increment to derive successive indices
+public:
+ VSeq(FloatRegister base_reg, int delta = 1) : VSeq(base_reg->encoding(), delta) { }
+ VSeq(int base, int delta = 1) : _base(base), _delta(delta) {
+ assert (_base >= 0, "invalid base register");
+ assert (_delta >= 0, "invalid register delta");
+ assert ((_base + (N - 1) * _delta) < 32, "range exceeded");
+ }
+ // indexed access to sequence
+ FloatRegister operator [](int i) const {
+ assert (0 <= i && i < N, "index out of bounds");
+ return as_FloatRegister(_base + i * _delta);
+ }
+ int mask() const {
+ int m = 0;
+ int bit = 1 << _base;
+ for (int i = 0; i < N; i++) {
+ m |= bit << (i * _delta);
+ }
+ return m;
+ }
+ int base() const { return _base; }
+ int delta() const { return _delta; }
+};
+
+// declare convenience methods for splitting vector register sequences
+
+VSeq<4> vs_front(const VSeq<8>& v);
+VSeq<4> vs_back(const VSeq<8>& v);
+VSeq<4> vs_even(const VSeq<8>& v);
+VSeq<4> vs_odd(const VSeq<8>& v);
+
+// methods for use in asserts to check VSeq inputs and oupts are
+// either disjoint or equal
+
+template bool vs_disjoint(const VSeq& n, const VSeq& m) { return (n.mask() & m.mask()) == 0; }
+template bool vs_same(const VSeq& n, const VSeq& m) { return n.mask() == m.mask(); }
+
#endif // CPU_AARCH64_REGISTER_AARCH64_HPP
diff --git a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp
index 2bfc49d05dd6..77cea7998832 100644
--- a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp
@@ -3983,6 +3983,95 @@ class StubGenerator: public StubCodeGenerator {
return start;
}
+ // Execute one round of keccak of two computations in parallel.
+ // One of the states should be loaded into the lower halves of
+ // the vector registers v0-v24, the other should be loaded into
+ // the upper halves of those registers. The ld1r instruction loads
+ // the round constant into both halves of register v31.
+ // Intermediate results c0...c5 and d0...d5 are computed
+ // in registers v25...v30.
+ // All vector instructions that are used operate on both register
+ // halves in parallel.
+ // If only a single computation is needed, one can only load the lower halves.
+ void keccak_round(Register rscratch1) {
+ __ eor3(v29, __ T16B, v4, v9, v14); // c4 = a4 ^ a9 ^ a14
+ __ eor3(v26, __ T16B, v1, v6, v11); // c1 = a1 ^ a16 ^ a11
+ __ eor3(v28, __ T16B, v3, v8, v13); // c3 = a3 ^ a8 ^a13
+ __ eor3(v25, __ T16B, v0, v5, v10); // c0 = a0 ^ a5 ^ a10
+ __ eor3(v27, __ T16B, v2, v7, v12); // c2 = a2 ^ a7 ^ a12
+ __ eor3(v29, __ T16B, v29, v19, v24); // c4 ^= a19 ^ a24
+ __ eor3(v26, __ T16B, v26, v16, v21); // c1 ^= a16 ^ a21
+ __ eor3(v28, __ T16B, v28, v18, v23); // c3 ^= a18 ^ a23
+ __ eor3(v25, __ T16B, v25, v15, v20); // c0 ^= a15 ^ a20
+ __ eor3(v27, __ T16B, v27, v17, v22); // c2 ^= a17 ^ a22
+
+ __ rax1(v30, __ T2D, v29, v26); // d0 = c4 ^ rol(c1, 1)
+ __ rax1(v26, __ T2D, v26, v28); // d2 = c1 ^ rol(c3, 1)
+ __ rax1(v28, __ T2D, v28, v25); // d4 = c3 ^ rol(c0, 1)
+ __ rax1(v25, __ T2D, v25, v27); // d1 = c0 ^ rol(c2, 1)
+ __ rax1(v27, __ T2D, v27, v29); // d3 = c2 ^ rol(c4, 1)
+
+ __ eor(v0, __ T16B, v0, v30); // a0 = a0 ^ d0
+ __ xar(v29, __ T2D, v1, v25, (64 - 1)); // a10' = rol((a1^d1), 1)
+ __ xar(v1, __ T2D, v6, v25, (64 - 44)); // a1 = rol(a6^d1), 44)
+ __ xar(v6, __ T2D, v9, v28, (64 - 20)); // a6 = rol((a9^d4), 20)
+ __ xar(v9, __ T2D, v22, v26, (64 - 61)); // a9 = rol((a22^d2), 61)
+ __ xar(v22, __ T2D, v14, v28, (64 - 39)); // a22 = rol((a14^d4), 39)
+ __ xar(v14, __ T2D, v20, v30, (64 - 18)); // a14 = rol((a20^d0), 18)
+ __ xar(v31, __ T2D, v2, v26, (64 - 62)); // a20' = rol((a2^d2), 62)
+ __ xar(v2, __ T2D, v12, v26, (64 - 43)); // a2 = rol((a12^d2), 43)
+ __ xar(v12, __ T2D, v13, v27, (64 - 25)); // a12 = rol((a13^d3), 25)
+ __ xar(v13, __ T2D, v19, v28, (64 - 8)); // a13 = rol((a19^d4), 8)
+ __ xar(v19, __ T2D, v23, v27, (64 - 56)); // a19 = rol((a23^d3), 56)
+ __ xar(v23, __ T2D, v15, v30, (64 - 41)); // a23 = rol((a15^d0), 41)
+ __ xar(v15, __ T2D, v4, v28, (64 - 27)); // a15 = rol((a4^d4), 27)
+ __ xar(v28, __ T2D, v24, v28, (64 - 14)); // a4' = rol((a24^d4), 14)
+ __ xar(v24, __ T2D, v21, v25, (64 - 2)); // a24 = rol((a21^d1), 2)
+ __ xar(v8, __ T2D, v8, v27, (64 - 55)); // a21' = rol((a8^d3), 55)
+ __ xar(v4, __ T2D, v16, v25, (64 - 45)); // a8' = rol((a16^d1), 45)
+ __ xar(v16, __ T2D, v5, v30, (64 - 36)); // a16 = rol((a5^d0), 36)
+ __ xar(v5, __ T2D, v3, v27, (64 - 28)); // a5 = rol((a3^d3), 28)
+ __ xar(v27, __ T2D, v18, v27, (64 - 21)); // a3' = rol((a18^d3), 21)
+ __ xar(v3, __ T2D, v17, v26, (64 - 15)); // a18' = rol((a17^d2), 15)
+ __ xar(v25, __ T2D, v11, v25, (64 - 10)); // a17' = rol((a11^d1), 10)
+ __ xar(v26, __ T2D, v7, v26, (64 - 6)); // a11' = rol((a7^d2), 6)
+ __ xar(v30, __ T2D, v10, v30, (64 - 3)); // a7' = rol((a10^d0), 3)
+
+ __ bcax(v20, __ T16B, v31, v22, v8); // a20 = a20' ^ (~a21 & a22')
+ __ bcax(v21, __ T16B, v8, v23, v22); // a21 = a21' ^ (~a22 & a23)
+ __ bcax(v22, __ T16B, v22, v24, v23); // a22 = a22 ^ (~a23 & a24)
+ __ bcax(v23, __ T16B, v23, v31, v24); // a23 = a23 ^ (~a24 & a20')
+ __ bcax(v24, __ T16B, v24, v8, v31); // a24 = a24 ^ (~a20' & a21')
+
+ __ ld1r(v31, __ T2D, __ post(rscratch1, 8)); // rc = round_constants[i]
+
+ __ bcax(v17, __ T16B, v25, v19, v3); // a17 = a17' ^ (~a18' & a19)
+ __ bcax(v18, __ T16B, v3, v15, v19); // a18 = a18' ^ (~a19 & a15')
+ __ bcax(v19, __ T16B, v19, v16, v15); // a19 = a19 ^ (~a15 & a16)
+ __ bcax(v15, __ T16B, v15, v25, v16); // a15 = a15 ^ (~a16 & a17')
+ __ bcax(v16, __ T16B, v16, v3, v25); // a16 = a16 ^ (~a17' & a18')
+
+ __ bcax(v10, __ T16B, v29, v12, v26); // a10 = a10' ^ (~a11' & a12)
+ __ bcax(v11, __ T16B, v26, v13, v12); // a11 = a11' ^ (~a12 & a13)
+ __ bcax(v12, __ T16B, v12, v14, v13); // a12 = a12 ^ (~a13 & a14)
+ __ bcax(v13, __ T16B, v13, v29, v14); // a13 = a13 ^ (~a14 & a10')
+ __ bcax(v14, __ T16B, v14, v26, v29); // a14 = a14 ^ (~a10' & a11')
+
+ __ bcax(v7, __ T16B, v30, v9, v4); // a7 = a7' ^ (~a8' & a9)
+ __ bcax(v8, __ T16B, v4, v5, v9); // a8 = a8' ^ (~a9 & a5)
+ __ bcax(v9, __ T16B, v9, v6, v5); // a9 = a9 ^ (~a5 & a6)
+ __ bcax(v5, __ T16B, v5, v30, v6); // a5 = a5 ^ (~a6 & a7)
+ __ bcax(v6, __ T16B, v6, v4, v30); // a6 = a6 ^ (~a7 & a8')
+
+ __ bcax(v3, __ T16B, v27, v0, v28); // a3 = a3' ^ (~a4' & a0)
+ __ bcax(v4, __ T16B, v28, v1, v0); // a4 = a4' ^ (~a0 & a1)
+ __ bcax(v0, __ T16B, v0, v2, v1); // a0 = a0 ^ (~a1 & a2)
+ __ bcax(v1, __ T16B, v1, v27, v2); // a1 = a1 ^ (~a2 & a3)
+ __ bcax(v2, __ T16B, v2, v28, v27); // a2 = a2 ^ (~a3 & a4')
+
+ __ eor(v0, __ T16B, v0, v31); // a0 = a0 ^ rc
+ }
+
// Arguments:
//
// Inputs:
@@ -4074,7 +4163,7 @@ class StubGenerator: public StubCodeGenerator {
__ cbzw(c_rarg5, rounds24_loop);
__ tbnz(block_size, 5, shake128);
- // block_size == 144, bit5 == 0, SHA3-244
+ // block_size == 144, bit5 == 0, SHA3-224
__ ldrd(v28, __ post(buf, 8));
__ eor(v17, __ T8B, v17, v28);
__ b(rounds24_loop);
@@ -4103,82 +4192,7 @@ class StubGenerator: public StubCodeGenerator {
__ BIND(rounds24_loop);
__ subw(rscratch2, rscratch2, 1);
- __ eor3(v29, __ T16B, v4, v9, v14);
- __ eor3(v26, __ T16B, v1, v6, v11);
- __ eor3(v28, __ T16B, v3, v8, v13);
- __ eor3(v25, __ T16B, v0, v5, v10);
- __ eor3(v27, __ T16B, v2, v7, v12);
- __ eor3(v29, __ T16B, v29, v19, v24);
- __ eor3(v26, __ T16B, v26, v16, v21);
- __ eor3(v28, __ T16B, v28, v18, v23);
- __ eor3(v25, __ T16B, v25, v15, v20);
- __ eor3(v27, __ T16B, v27, v17, v22);
-
- __ rax1(v30, __ T2D, v29, v26);
- __ rax1(v26, __ T2D, v26, v28);
- __ rax1(v28, __ T2D, v28, v25);
- __ rax1(v25, __ T2D, v25, v27);
- __ rax1(v27, __ T2D, v27, v29);
-
- __ eor(v0, __ T16B, v0, v30);
- __ xar(v29, __ T2D, v1, v25, (64 - 1));
- __ xar(v1, __ T2D, v6, v25, (64 - 44));
- __ xar(v6, __ T2D, v9, v28, (64 - 20));
- __ xar(v9, __ T2D, v22, v26, (64 - 61));
- __ xar(v22, __ T2D, v14, v28, (64 - 39));
- __ xar(v14, __ T2D, v20, v30, (64 - 18));
- __ xar(v31, __ T2D, v2, v26, (64 - 62));
- __ xar(v2, __ T2D, v12, v26, (64 - 43));
- __ xar(v12, __ T2D, v13, v27, (64 - 25));
- __ xar(v13, __ T2D, v19, v28, (64 - 8));
- __ xar(v19, __ T2D, v23, v27, (64 - 56));
- __ xar(v23, __ T2D, v15, v30, (64 - 41));
- __ xar(v15, __ T2D, v4, v28, (64 - 27));
- __ xar(v28, __ T2D, v24, v28, (64 - 14));
- __ xar(v24, __ T2D, v21, v25, (64 - 2));
- __ xar(v8, __ T2D, v8, v27, (64 - 55));
- __ xar(v4, __ T2D, v16, v25, (64 - 45));
- __ xar(v16, __ T2D, v5, v30, (64 - 36));
- __ xar(v5, __ T2D, v3, v27, (64 - 28));
- __ xar(v27, __ T2D, v18, v27, (64 - 21));
- __ xar(v3, __ T2D, v17, v26, (64 - 15));
- __ xar(v25, __ T2D, v11, v25, (64 - 10));
- __ xar(v26, __ T2D, v7, v26, (64 - 6));
- __ xar(v30, __ T2D, v10, v30, (64 - 3));
-
- __ bcax(v20, __ T16B, v31, v22, v8);
- __ bcax(v21, __ T16B, v8, v23, v22);
- __ bcax(v22, __ T16B, v22, v24, v23);
- __ bcax(v23, __ T16B, v23, v31, v24);
- __ bcax(v24, __ T16B, v24, v8, v31);
-
- __ ld1r(v31, __ T2D, __ post(rscratch1, 8));
-
- __ bcax(v17, __ T16B, v25, v19, v3);
- __ bcax(v18, __ T16B, v3, v15, v19);
- __ bcax(v19, __ T16B, v19, v16, v15);
- __ bcax(v15, __ T16B, v15, v25, v16);
- __ bcax(v16, __ T16B, v16, v3, v25);
-
- __ bcax(v10, __ T16B, v29, v12, v26);
- __ bcax(v11, __ T16B, v26, v13, v12);
- __ bcax(v12, __ T16B, v12, v14, v13);
- __ bcax(v13, __ T16B, v13, v29, v14);
- __ bcax(v14, __ T16B, v14, v26, v29);
-
- __ bcax(v7, __ T16B, v30, v9, v4);
- __ bcax(v8, __ T16B, v4, v5, v9);
- __ bcax(v9, __ T16B, v9, v6, v5);
- __ bcax(v5, __ T16B, v5, v30, v6);
- __ bcax(v6, __ T16B, v6, v4, v30);
-
- __ bcax(v3, __ T16B, v27, v0, v28);
- __ bcax(v4, __ T16B, v28, v1, v0);
- __ bcax(v0, __ T16B, v0, v2, v1);
- __ bcax(v1, __ T16B, v1, v27, v2);
- __ bcax(v2, __ T16B, v2, v28, v27);
-
- __ eor(v0, __ T16B, v0, v31);
+ keccak_round(rscratch1);
__ cbnzw(rscratch2, rounds24_loop);
@@ -4197,6 +4211,7 @@ class StubGenerator: public StubCodeGenerator {
__ st1(v20, v21, v22, v23, __ T1D, __ post(state, 32));
__ st1(v24, __ T1D, state);
+ // restore callee-saved registers
__ ldpd(v14, v15, Address(sp, 48));
__ ldpd(v12, v13, Address(sp, 32));
__ ldpd(v10, v11, Address(sp, 16));
@@ -4207,6 +4222,96 @@ class StubGenerator: public StubCodeGenerator {
return start;
}
+ // Inputs:
+ // c_rarg0 - long[] state0
+ // c_rarg1 - long[] state1
+ address generate_double_keccak() {
+ static const uint64_t round_consts[24] = {
+ 0x0000000000000001L, 0x0000000000008082L, 0x800000000000808AL,
+ 0x8000000080008000L, 0x000000000000808BL, 0x0000000080000001L,
+ 0x8000000080008081L, 0x8000000000008009L, 0x000000000000008AL,
+ 0x0000000000000088L, 0x0000000080008009L, 0x000000008000000AL,
+ 0x000000008000808BL, 0x800000000000008BL, 0x8000000000008089L,
+ 0x8000000000008003L, 0x8000000000008002L, 0x8000000000000080L,
+ 0x000000000000800AL, 0x800000008000000AL, 0x8000000080008081L,
+ 0x8000000000008080L, 0x0000000080000001L, 0x8000000080008008L
+ };
+
+ // Implements the double_keccak() method of the
+ // sun.secyrity.provider.SHA3Parallel class
+ __ align(CodeEntryAlignment);
+ StubCodeMark mark(this, "StubRoutines", "double_keccak");
+ address start = __ pc();
+ __ enter();
+
+ Register state0 = c_rarg0;
+ Register state1 = c_rarg1;
+
+ Label rounds24_loop;
+
+ // save callee-saved registers
+ __ stpd(v8, v9, __ pre(sp, -64));
+ __ stpd(v10, v11, Address(sp, 16));
+ __ stpd(v12, v13, Address(sp, 32));
+ __ stpd(v14, v15, Address(sp, 48));
+
+ // load states
+ __ add(rscratch1, state0, 32);
+ __ ld4(v0, v1, v2, v3, __ D, 0, state0);
+ __ ld4(v4, v5, v6, v7, __ D, 0, __ post(rscratch1, 32));
+ __ ld4(v8, v9, v10, v11, __ D, 0, __ post(rscratch1, 32));
+ __ ld4(v12, v13, v14, v15, __ D, 0, __ post(rscratch1, 32));
+ __ ld4(v16, v17, v18, v19, __ D, 0, __ post(rscratch1, 32));
+ __ ld4(v20, v21, v22, v23, __ D, 0, __ post(rscratch1, 32));
+ __ ld1(v24, __ D, 0, rscratch1);
+ __ add(rscratch1, state1, 32);
+ __ ld4(v0, v1, v2, v3, __ D, 1, state1);
+ __ ld4(v4, v5, v6, v7, __ D, 1, __ post(rscratch1, 32));
+ __ ld4(v8, v9, v10, v11, __ D, 1, __ post(rscratch1, 32));
+ __ ld4(v12, v13, v14, v15, __ D, 1, __ post(rscratch1, 32));
+ __ ld4(v16, v17, v18, v19, __ D, 1, __ post(rscratch1, 32));
+ __ ld4(v20, v21, v22, v23, __ D, 1, __ post(rscratch1, 32));
+ __ ld1(v24, __ D, 1, rscratch1);
+
+ // 24 keccak rounds
+ __ movw(rscratch2, 24);
+
+ // load round_constants base
+ __ lea(rscratch1, ExternalAddress((address) round_consts));
+
+ __ BIND(rounds24_loop);
+ __ subw(rscratch2, rscratch2, 1);
+ keccak_round(rscratch1);
+ __ cbnzw(rscratch2, rounds24_loop);
+
+ __ st4(v0, v1, v2, v3, __ D, 0, __ post(state0, 32));
+ __ st4(v4, v5, v6, v7, __ D, 0, __ post(state0, 32));
+ __ st4(v8, v9, v10, v11, __ D, 0, __ post(state0, 32));
+ __ st4(v12, v13, v14, v15, __ D, 0, __ post(state0, 32));
+ __ st4(v16, v17, v18, v19, __ D, 0, __ post(state0, 32));
+ __ st4(v20, v21, v22, v23, __ D, 0, __ post(state0, 32));
+ __ st1(v24, __ D, 0, state0);
+ __ st4(v0, v1, v2, v3, __ D, 1, __ post(state1, 32));
+ __ st4(v4, v5, v6, v7, __ D, 1, __ post(state1, 32));
+ __ st4(v8, v9, v10, v11, __ D, 1, __ post(state1, 32));
+ __ st4(v12, v13, v14, v15, __ D, 1, __ post(state1, 32));
+ __ st4(v16, v17, v18, v19, __ D, 1, __ post(state1, 32));
+ __ st4(v20, v21, v22, v23, __ D, 1, __ post(state1, 32));
+ __ st1(v24, __ D, 1, state1);
+
+ // restore callee-saved vector registers
+ __ ldpd(v14, v15, Address(sp, 48));
+ __ ldpd(v12, v13, Address(sp, 32));
+ __ ldpd(v10, v11, Address(sp, 16));
+ __ ldpd(v8, v9, __ post(sp, 64));
+
+ __ leave(); // required for proper stackwalking of RuntimeStub frame
+ __ mov(r0, zr); // return 0
+ __ ret(lr);
+
+ return start;
+ }
+
/**
* Arguments:
*
@@ -4373,6 +4478,963 @@ class StubGenerator: public StubCodeGenerator {
return start;
}
+ // Helpers to schedule parallel operation bundles across vector
+ // register sequences of size 2, 4 or 8.
+
+ // Implement various primitive computations across vector sequences
+
+ template
+ void vs_addv(const VSeq& v, Assembler::SIMD_Arrangement T,
+ const VSeq& v1, const VSeq& v2) {
+ for (int i = 0; i < N; i++) {
+ __ addv(v[i], T, v1[i], v2[i]);
+ }
+ }
+
+ template
+ void vs_subv(const VSeq& v, Assembler::SIMD_Arrangement T,
+ const VSeq& v1, const VSeq& v2) {
+ for (int i = 0; i < N; i++) {
+ __ subv(v[i], T, v1[i], v2[i]);
+ }
+ }
+
+ template
+ void vs_mulv(const VSeq& v, Assembler::SIMD_Arrangement T,
+ const VSeq& v1, const VSeq& v2) {
+ for (int i = 0; i < N; i++) {
+ __ mulv(v[i], T, v1[i], v2[i]);
+ }
+ }
+
+ template
+ void vs_negr(const VSeq& v, Assembler::SIMD_Arrangement T, const VSeq& v1) {
+ for (int i = 0; i < N; i++) {
+ __ negr(v[i], T, v1[i]);
+ }
+ }
+
+ template
+ void vs_sshr(const VSeq& v, Assembler::SIMD_Arrangement T,
+ const VSeq& v1, int shift) {
+ for (int i = 0; i < N; i++) {
+ __ sshr(v[i], T, v1[i], shift);
+ }
+ }
+
+ template
+ void vs_andr(const VSeq& v, const VSeq& v1, const VSeq& v2) {
+ for (int i = 0; i < N; i++) {
+ __ andr(v[i], __ T16B, v1[i], v2[i]);
+ }
+ }
+
+ template
+ void vs_orr(const VSeq& v, const VSeq& v1, const VSeq& v2) {
+ for (int i = 0; i < N; i++) {
+ __ orr(v[i], __ T16B, v1[i], v2[i]);
+ }
+ }
+
+ template
+ void vs_notr(const VSeq& v, const VSeq& v1) {
+ for (int i = 0; i < N; i++) {
+ __ notr(v[i], __ T16B, v1[i]);
+ }
+ }
+
+ // load N/2 successive pairs of quadword values from memory in order
+ // into N successive vector registers of the sequence via the
+ // address supplied in base.
+ template
+ void vs_ldpq(const VSeq& v, Register base) {
+ for (int i = 0; i < N; i += 2) {
+ __ ldpq(v[i], v[i+1], Address(base, 32 * i));
+ }
+ }
+
+ // load N/2 successive pairs of quadword values from memory in order
+ // into N vector registers of the sequence via the address supplied
+ // in base using post-increment addressing
+ template
+ void vs_ldpq_post(const VSeq& v, Register base) {
+ for (int i = 0; i < N; i += 2) {
+ __ ldpq(v[i], v[i+1], __ post(base, 32));
+ }
+ }
+
+ // store N successive vector registers of the sequence into N/2
+ // successive pairs of quadword memory locations via the address
+ // supplied in base using post-increment addressing
+ template
+ void vs_stpq_post(const VSeq& v, Register base) {
+ for (int i = 0; i < N; i += 2) {
+ __ stpq(v[i], v[i+1], __ post(base, 32));
+ }
+ }
+
+ // load N/2 pairs of quadword values from memory into N vector
+ // registers via the address supplied in base with each pair indexed
+ // using the the start offset plus the corresponding entry in the
+ // offsets array
+ template
+ void vs_ldpq_indexed(const VSeq& v, Register base, int start, int (&offsets)[N/2]) {
+ for (int i = 0; i < N/2; i++) {
+ __ ldpq(v[2*i], v[2*i+1], Address(base, start + offsets[i]));
+ }
+ }
+
+ // store N vector registers into N/2 pairs of quadword memory
+ // locations via the address supplied in base with each pair indexed
+ // using the the start offset plus the corresponding entry in the
+ // offsets array
+ template
+ void vs_stpq_indexed(const VSeq& v, Register base, int start, int offsets[N/2]) {
+ for (int i = 0; i < N/2; i++) {
+ __ stpq(v[2*i], v[2*i+1], Address(base, start + offsets[i]));
+ }
+ }
+
+ // load N single quadword values from memory into N vector registers
+ // via the address supplied in base with each value indexed using
+ // the the start offset plus the corresponding entry in the offsets
+ // array
+ template
+ void vs_ldr_indexed(const VSeq& v, Assembler::SIMD_RegVariant T, Register base,
+ int start, int (&offsets)[N]) {
+ for (int i = 0; i < N; i++) {
+ __ ldr(v[i], T, Address(base, start + offsets[i]));
+ }
+ }
+
+ // store N vector registers into N single quadword memory locations
+ // via the address supplied in base with each value indexed using
+ // the the start offset plus the corresponding entry in the offsets
+ // array
+ template
+ void vs_str_indexed(const VSeq& v, Assembler::SIMD_RegVariant T, Register base,
+ int start, int (&offsets)[N]) {
+ for (int i = 0; i < N; i++) {
+ __ str(v[i], T, Address(base, start + offsets[i]));
+ }
+ }
+
+ // load N/2 pairs of quadword values from memory de-interleaved into
+ // N vector registers 2 at a time via the address supplied in base
+ // with each pair indexed using the the start offset plus the
+ // corresponding entry in the offsets array
+ template
+ void vs_ld2_indexed(const VSeq& v, Assembler::SIMD_Arrangement T, Register base,
+ Register tmp, int start, int (&offsets)[N/2]) {
+ for (int i = 0; i < N/2; i++) {
+ __ add(tmp, base, start + offsets[i]);
+ __ ld2(v[2*i], v[2*i+1], T, tmp);
+ }
+ }
+
+ // store N vector registers 2 at a time interleaved into N/2 pairs
+ // of quadword memory locations via the address supplied in base
+ // with each pair indexed using the the start offset plus the
+ // corresponding entry in the offsets array
+ template
+ void vs_st2_indexed(const VSeq& v, Assembler::SIMD_Arrangement T, Register base,
+ Register tmp, int start, int (&offsets)[N/2]) {
+ for (int i = 0; i < N/2; i++) {
+ __ add(tmp, base, start + offsets[i]);
+ __ st2(v[2*i], v[2*i+1], T, tmp);
+ }
+ }
+
+ // Helper routines for various flavours of dilithium montgomery
+ // multiply
+
+ // Perform 16 32-bit Montgomery multiplications in parallel
+ // See the montMul() method of the sun.security.provider.ML_DSA class.
+ //
+ // Computes 4x4S results
+ // a = b * c * 2^-32 mod MONT_Q
+ // Inputs: vb, vc - 4x4S vector register sequences
+ // vq - 2x4S constants
+ // Temps: vtmp - 4x4S vector sequence trashed after call
+ // Outputs: va - 4x4S vector register sequences
+ // vb, vc, vtmp and vq must all be disjoint
+ // va must be disjoint from all other inputs/temps or must equal vc
+ // n.b. MONT_R_BITS is 32, so the right shift by it is implicit.
+ void dilithium_montmul16(const VSeq<4>& va, const VSeq<4>& vb, const VSeq<4>& vc,
+ const VSeq<4>& vtmp, const VSeq<2>& vq) {
+ assert(vs_disjoint(vb, vc), "vb and vc overlap");
+ assert(vs_disjoint(vb, vq), "vb and vq overlap");
+ assert(vs_disjoint(vb, vtmp), "vb and vtmp overlap");
+
+ assert(vs_disjoint(vc, vq), "vc and vq overlap");
+ assert(vs_disjoint(vc, vtmp), "vc and vtmp overlap");
+
+ assert(vs_disjoint(vq, vtmp), "vq and vtmp overlap");
+
+ assert(vs_disjoint(va, vc) || vs_same(va, vc), "va and vc neither disjoint nor equal");
+ assert(vs_disjoint(va, vb), "va and vb overlap");
+ assert(vs_disjoint(va, vq), "va and vq overlap");
+ assert(vs_disjoint(va, vtmp), "va and vtmp overlap");
+
+ // schedule 4 streams of instructions across the vector sequences
+ for (int i = 0; i < 4; i++) {
+ __ sqdmulh(vtmp[i], __ T4S, vb[i], vc[i]); // aHigh = hi32(2 * b * c)
+ __ mulv(va[i], __ T4S, vb[i], vc[i]); // aLow = lo32(b * c)
+ }
+
+ for (int i = 0; i < 4; i++) {
+ __ mulv(va[i], __ T4S, va[i], vq[0]); // m = aLow * qinv
+ }
+
+ for (int i = 0; i < 4; i++) {
+ __ sqdmulh(va[i], __ T4S, va[i], vq[1]); // n = hi32(2 * m * q)
+ }
+
+ for (int i = 0; i < 4; i++) {
+ __ shsubv(va[i], __ T4S, vtmp[i], va[i]); // a = (aHigh - n) / 2
+ }
+ }
+
+ // Perform 2x16 32-bit Montgomery multiplications in parallel
+ // See the montMul() method of the sun.security.provider.ML_DSA class.
+ //
+ // Computes 8x4S results
+ // a = b * c * 2^-32 mod MONT_Q
+ // Inputs: vb, vc - 8x4S vector register sequences
+ // vq - 2x4S constants
+ // Temps: vtmp - 4x4S vector sequence trashed after call
+ // Outputs: va - 8x4S vector register sequences
+ // vb, vc, vtmp and vq must all be disjoint
+ // va must be disjoint from all other inputs/temps or must equal vc
+ // n.b. MONT_R_BITS is 32, so the right shift by it is implicit.
+ void vs_montmul32(const VSeq<8>& va, const VSeq<8>& vb, const VSeq<8>& vc,
+ const VSeq<4>& vtmp, const VSeq<2>& vq) {
+ // vb, vc, vtmp and vq must be disjoint. va must either be
+ // disjoint from all other registers or equal vc
+
+ assert(vs_disjoint(vb, vc), "vb and vc overlap");
+ assert(vs_disjoint(vb, vq), "vb and vq overlap");
+ assert(vs_disjoint(vb, vtmp), "vb and vtmp overlap");
+
+ assert(vs_disjoint(vc, vq), "vc and vq overlap");
+ assert(vs_disjoint(vc, vtmp), "vc and vtmp overlap");
+
+ assert(vs_disjoint(vq, vtmp), "vq and vtmp overlap");
+
+ assert(vs_disjoint(va, vc) || vs_same(va, vc), "va and vc neither disjoint nor equal");
+ assert(vs_disjoint(va, vb), "va and vb overlap");
+ assert(vs_disjoint(va, vq), "va and vq overlap");
+ assert(vs_disjoint(va, vtmp), "va and vtmp overlap");
+
+ // we need to multiply the front and back halves of each sequence
+ // 4x4S at a time because
+ //
+ // 1) we are currently only able to get 4-way instruction
+ // parallelism at best
+ //
+ // 2) we need registers for the constants in vq and temporary
+ // scratch registers to hold intermediate results so vtmp can only
+ // be a VSeq<4> which means we only have 4 scratch slots
+
+ dilithium_montmul16(vs_front(va), vs_front(vb), vs_front(vc), vtmp, vq);
+ dilithium_montmul16(vs_back(va), vs_back(vb), vs_back(vc), vtmp, vq);
+ }
+
+ // perform combined montmul then add/sub on 4x4S vectors
+
+ void dilithium_montmul16_sub_add(const VSeq<4>& va0, const VSeq<4>& va1, const VSeq<4>& vc,
+ const VSeq<4>& vtmp, const VSeq<2>& vq) {
+ // compute a = montmul(a1, c)
+ dilithium_montmul16(vc, va1, vc, vtmp, vq);
+ // ouptut a1 = a0 - a
+ vs_subv(va1, __ T4S, va0, vc);
+ // and a0 = a0 + a
+ vs_addv(va0, __ T4S, va0, vc);
+ }
+
+ // perform combined add/sub then montul on 4x4S vectors
+
+ void dilithium_sub_add_montmul16(const VSeq<4>& va0, const VSeq<4>& va1, const VSeq<4>& vb,
+ const VSeq<4>& vtmp1, const VSeq<4>& vtmp2, const VSeq<2>& vq) {
+ // compute c = a0 - a1
+ vs_subv(vtmp1, __ T4S, va0, va1);
+ // output a0 = a0 + a1
+ vs_addv(va0, __ T4S, va0, va1);
+ // output a1 = b montmul c
+ dilithium_montmul16(va1, vtmp1, vb, vtmp2, vq);
+ }
+
+ // At these levels, the indices that correspond to the 'j's (and 'j+l's)
+ // in the Java implementation come in sequences of at least 8, so we
+ // can use ldpq to collect the corresponding data into pairs of vector
+ // registers.
+ // We collect the coefficients corresponding to the 'j+l' indexes into
+ // the vector registers v0-v7, the zetas into the vector registers v16-v23
+ // then we do the (Montgomery) multiplications by the zetas in parallel
+ // into v16-v23, load the coeffs corresponding to the 'j' indexes into
+ // v0-v7, then do the additions into v24-v31 and the subtractions into
+ // v0-v7 and finally save the results back to the coeffs array.
+ void dilithiumNttLevel0_4(const Register dilithiumConsts,
+ const Register coeffs, const Register zetas) {
+ int c1 = 0;
+ int c2 = 512;
+ int startIncr;
+ // don't use callee save registers v8 - v15
+ VSeq<8> vs1(0), vs2(16), vs3(24); // 3 sets of 8x4s inputs/outputs
+ VSeq<4> vtmp = vs_front(vs3); // n.b. tmp registers overlap vs3
+ VSeq<2> vq(30); // n.b. constants overlap vs3
+ int offsets[4] = { 0, 32, 64, 96 };
+
+ for (int level = 0; level < 5; level++) {
+ int c1Start = c1;
+ int c2Start = c2;
+ if (level == 3) {
+ offsets[1] = 32;
+ offsets[2] = 128;
+ offsets[3] = 160;
+ } else if (level == 4) {
+ offsets[1] = 64;
+ offsets[2] = 128;
+ offsets[3] = 192;
+ }
+
+ // for levels 1 - 4 we simply load 2 x 4 adjacent values at a
+ // time at 4 different offsets and multiply them in order by the
+ // next set of input values. So we employ indexed load and store
+ // pair instructions with arrangement 4S
+ for (int i = 0; i < 4; i++) {
+ // reload q and qinv
+ vs_ldpq(vq, dilithiumConsts); // qInv, q
+ // load 8x4S coefficients via second start pos == c2
+ vs_ldpq_indexed(vs1, coeffs, c2Start, offsets);
+ // load next 8x4S inputs == b
+ vs_ldpq_post(vs2, zetas);
+ // compute a == c2 * b mod MONT_Q
+ vs_montmul32(vs2, vs1, vs2, vtmp, vq);
+ // load 8x4s coefficients via first start pos == c1
+ vs_ldpq_indexed(vs1, coeffs, c1Start, offsets);
+ // compute a1 = c1 + a
+ vs_addv(vs3, __ T4S, vs1, vs2);
+ // compute a2 = c1 - a
+ vs_subv(vs1, __ T4S, vs1, vs2);
+ // output a1 and a2
+ vs_stpq_indexed(vs3, coeffs, c1Start, offsets);
+ vs_stpq_indexed(vs1, coeffs, c2Start, offsets);
+
+ int k = 4 * level + i;
+
+ if (k > 7) {
+ startIncr = 256;
+ } else if (k == 5) {
+ startIncr = 384;
+ } else {
+ startIncr = 128;
+ }
+
+ c1Start += startIncr;
+ c2Start += startIncr;
+ }
+
+ c2 /= 2;
+ }
+ }
+
+ // Dilithium NTT function except for the final "normalization" to |coeff| < Q.
+ // Implements the method
+ // static int implDilithiumAlmostNtt(int[] coeffs, int zetas[]) {}
+ // of the Java class sun.security.provider
+ //
+ // coeffs (int[256]) = c_rarg0
+ // zetas (int[256]) = c_rarg1
+ address generate_dilithiumAlmostNtt() {
+
+ __ align(CodeEntryAlignment);
+ StubCodeMark mark(this, "StubRoutines", "dilithiumAlmostNtt");
+ address start = __ pc();
+ __ enter();
+
+ const Register coeffs = c_rarg0;
+ const Register zetas = c_rarg1;
+
+ const Register tmpAddr = r9;
+ const Register dilithiumConsts = r10;
+ const Register result = r11;
+ // don't use callee save registers v8 - v15
+ VSeq<8> vs1(0), vs2(16), vs3(24); // 3 sets of 8x4s inputs/outputs
+ VSeq<4> vtmp = vs_front(vs3); // n.b. tmp registers overlap vs3
+ VSeq<2> vq(30); // n.b. constants overlap vs3
+ int offsets[4] = {0, 32, 64, 96};
+ int offsets1[8] = {16, 48, 80, 112, 144, 176, 208, 240 };
+ int offsets2[8] = { 0, 32, 64, 96, 128, 160, 192, 224 };
+ __ add(result, coeffs, 0);
+ __ lea(dilithiumConsts, ExternalAddress((address) StubRoutines::aarch64::_dilithiumConsts));
+
+ // Each level represents one iteration of the outer for loop of the Java version
+
+ // level 0-4
+ dilithiumNttLevel0_4(dilithiumConsts, coeffs, zetas);
+
+ // level 5
+
+ // at level 5 the coefficients we need to combine with the zetas
+ // are grouped in memory in blocks of size 4. So, for both sets of
+ // coefficients we load 4 adjacent values at 8 different offsets
+ // using an indexed ldr with register variant Q and multiply them
+ // in sequence order by the next set of inputs. Likewise we store
+ // the resuls using an indexed str with register variant Q.
+ for (int i = 0; i < 1024; i += 256) {
+ // reload constants q, qinv each iteration as they get clobbered later
+ vs_ldpq(vq, dilithiumConsts); // qInv, q
+ // load 32 (8x4S) coefficients via first offsets = c1
+ vs_ldr_indexed(vs1, __ Q, coeffs, i, offsets1);
+ // load next 32 (8x4S) inputs = b
+ vs_ldpq_post(vs2, zetas);
+ // a = b montul c1
+ vs_montmul32(vs2, vs1, vs2, vtmp, vq);
+ // load 32 (8x4S) coefficients via second offsets = c2
+ vs_ldr_indexed(vs1, __ Q, coeffs, i, offsets2);
+ // add/sub with result of multiply
+ vs_addv(vs3, __ T4S, vs1, vs2); // a1 = a - c2
+ vs_subv(vs1, __ T4S, vs1, vs2); // a0 = a + c1
+ // write back new coefficients using same offsets
+ vs_str_indexed(vs3, __ Q, coeffs, i, offsets2);
+ vs_str_indexed(vs1, __ Q, coeffs, i, offsets1);
+ }
+
+ // level 6
+ // at level 6 the coefficients we need to combine with the zetas
+ // are grouped in memory in pairs, the first two being montmul
+ // inputs and the second add/sub inputs. We can still implement
+ // the montmul+sub+add using 4-way parallelism but only if we
+ // combine the coefficients with the zetas 16 at a time. We load 8
+ // adjacent values at 4 different offsets using an ld2 load with
+ // arrangement 2D. That interleaves the lower and upper halves of
+ // each pair of quadwords into successive vector registers. We
+ // then need to montmul the 4 even elements of the coefficients
+ // register sequence by the zetas in order and then add/sub the 4
+ // odd elements of the coefficients register sequence. We use an
+ // equivalent st2 operation to store the results back into memory
+ // de-interleaved.
+ for (int i = 0; i < 1024; i += 128) {
+ // reload constants q, qinv each iteration as they get clobbered later
+ vs_ldpq(vq, dilithiumConsts); // qInv, q
+ // load interleaved 16 (4x2D) coefficients via offsets
+ vs_ld2_indexed(vs1, __ T2D, coeffs, tmpAddr, i, offsets);
+ // load next 16 (4x4S) inputs
+ vs_ldpq_post(vs_front(vs2), zetas);
+ // mont multiply odd elements of vs1 by vs2 and add/sub into odds/evens
+ dilithium_montmul16_sub_add(vs_even(vs1), vs_odd(vs1),
+ vs_front(vs2), vtmp, vq);
+ // store interleaved 16 (4x2D) coefficients via offsets
+ vs_st2_indexed(vs1, __ T2D, coeffs, tmpAddr, i, offsets);
+ }
+
+ // level 7
+ // at level 7 the coefficients we need to combine with the zetas
+ // occur singly with montmul inputs alterating with add/sub
+ // inputs. Once again we can use 4-way parallelism to combine 16
+ // zetas at a time. However, we have to load 8 adjacent values at
+ // 4 different offsets using an ld2 load with arrangement 4S. That
+ // interleaves the the odd words of each pair into one
+ // coefficients vector register and the even words of the pair
+ // into the next register. We then need to montmul the 4 even
+ // elements of the coefficients register sequence by the zetas in
+ // order and then add/sub the 4 odd elements of the coefficients
+ // register sequence. We use an equivalent st2 operation to store
+ // the results back into memory de-interleaved.
+
+ for (int i = 0; i < 1024; i += 128) {
+ // reload constants q, qinv each iteration as they get clobbered later
+ vs_ldpq(vq, dilithiumConsts); // qInv, q
+ // load interleaved 16 (4x4S) coefficients via offsets
+ vs_ld2_indexed(vs1, __ T4S, coeffs, tmpAddr, i, offsets);
+ // load next 16 (4x4S) inputs
+ vs_ldpq_post(vs_front(vs2), zetas);
+ // mont multiply odd elements of vs1 by vs2 and add/sub into odds/evens
+ dilithium_montmul16_sub_add(vs_even(vs1), vs_odd(vs1),
+ vs_front(vs2), vtmp, vq);
+ // store interleaved 16 (4x4S) coefficients via offsets
+ vs_st2_indexed(vs1, __ T4S, coeffs, tmpAddr, i, offsets);
+ }
+ __ leave(); // required for proper stackwalking of RuntimeStub frame
+ __ mov(r0, zr); // return 0
+ __ ret(lr);
+
+ return start;
+ }
+
+ // At these levels, the indices that correspond to the 'j's (and 'j+l's)
+ // in the Java implementation come in sequences of at least 8, so we
+ // can use ldpq to collect the corresponding data into pairs of vector
+ // registers
+ // We collect the coefficients that correspond to the 'j's into vs1
+ // the coefficiets that correspond to the 'j+l's into vs2 then
+ // do the additions into vs3 and the subtractions into vs1 then
+ // save the result of the additions, load the zetas into vs2
+ // do the (Montgomery) multiplications by zeta in parallel into vs2
+ // finally save the results back to the coeffs array
+ void dilithiumInverseNttLevel3_7(const Register dilithiumConsts,
+ const Register coeffs, const Register zetas) {
+ int c1 = 0;
+ int c2 = 32;
+ int startIncr;
+ int offsets[4];
+ VSeq<8> vs1(0), vs2(16), vs3(24); // 3 sets of 8x4s inputs/outputs
+ VSeq<4> vtmp = vs_front(vs3); // n.b. tmp registers overlap vs3
+ VSeq<2> vq(30); // n.b. constants overlap vs3
+
+ offsets[0] = 0;
+
+ for (int level = 3; level < 8; level++) {
+ int c1Start = c1;
+ int c2Start = c2;
+ if (level == 3) {
+ offsets[1] = 64;
+ offsets[2] = 128;
+ offsets[3] = 192;
+ } else if (level == 4) {
+ offsets[1] = 32;
+ offsets[2] = 128;
+ offsets[3] = 160;
+ } else {
+ offsets[1] = 32;
+ offsets[2] = 64;
+ offsets[3] = 96;
+ }
+
+ // for levels 3 - 7 we simply load 2 x 4 adjacent values at a
+ // time at 4 different offsets and multiply them in order by the
+ // next set of input values. So we employ indexed load and store
+ // pair instructions with arrangement 4S
+ for (int i = 0; i < 4; i++) {
+ // load v1 32 (8x4S) coefficients relative to first start index
+ vs_ldpq_indexed(vs1, coeffs, c1Start, offsets);
+ // load v2 32 (8x4S) coefficients relative to second start index
+ vs_ldpq_indexed(vs2, coeffs, c2Start, offsets);
+ // a0 = v1 + v2 -- n.b. clobbers vqs
+ vs_addv(vs3, __ T4S, vs1, vs2);
+ // a1 = v1 - v2
+ vs_subv(vs1, __ T4S, vs1, vs2);
+ // save a1 relative to first start index
+ vs_stpq_indexed(vs3, coeffs, c1Start, offsets);
+ // load constants q, qinv each iteration as they get clobbered above
+ vs_ldpq(vq, dilithiumConsts); // qInv, q
+ // load b next 32 (8x4S) inputs
+ vs_ldpq_post(vs2, zetas);
+ // a = a1 montmul b
+ vs_montmul32(vs2, vs1, vs2, vtmp, vq);
+ // save a relative to second start index
+ vs_stpq_indexed(vs2, coeffs, c2Start, offsets);
+
+ int k = 4 * level + i;
+
+ if (k < 24) {
+ startIncr = 256;
+ } else if (k == 25) {
+ startIncr = 384;
+ } else {
+ startIncr = 128;
+ }
+
+ c1Start += startIncr;
+ c2Start += startIncr;
+ }
+
+ c2 *= 2;
+ }
+ }
+
+ // Dilithium Inverse NTT function except the final mod Q division by 2^256.
+ // Implements the method
+ // static int implDilithiumAlmostInverseNtt(int[] coeffs, int[] zetas) {} of
+ // the sun.security.provider.ML_DSA class.
+ //
+ // coeffs (int[256]) = c_rarg0
+ // zetas (int[256]) = c_rarg1
+ address generate_dilithiumAlmostInverseNtt() {
+
+ __ align(CodeEntryAlignment);
+ StubCodeMark mark(this, "StubRoutines", "dilithiumAlmostInverseNtt");
+ address start = __ pc();
+ __ enter();
+
+ const Register coeffs = c_rarg0;
+ const Register zetas = c_rarg1;
+
+ const Register tmpAddr = r9;
+ const Register dilithiumConsts = r10;
+ const Register result = r11;
+ VSeq<8> vs1(0), vs2(16), vs3(24); // 3 sets of 8x4s inputs/outputs
+ VSeq<4> vtmp = vs_front(vs3); // n.b. tmp registers overlap vs3
+ VSeq<2> vq(30); // n.b. constants overlap vs3
+ int offsets[4] = { 0, 32, 64, 96 };
+ int offsets1[8] = { 0, 32, 64, 96, 128, 160, 192, 224 };
+ int offsets2[8] = { 16, 48, 80, 112, 144, 176, 208, 240 };
+
+ __ add(result, coeffs, 0);
+ __ lea(dilithiumConsts, ExternalAddress((address) StubRoutines::aarch64::_dilithiumConsts));
+
+ // Each level represents one iteration of the outer for loop of the Java version
+ // level0
+
+ // level 0
+ // At level 0 we need to interleave adjacent quartets of
+ // coefficients before we multiply and add/sub by the next 16
+ // zetas just as we did for level 7 in the multiply code. So we
+ // load and store the values using an ld2/st2 with arrangement 4S
+ for (int i = 0; i < 1024; i += 128) {
+ // load constants q, qinv
+ // n.b. this can be moved out of the loop as they do not get
+ // clobbered by first two loops
+ vs_ldpq(vq, dilithiumConsts); // qInv, q
+ // a0/a1 load interleaved 32 (8x4S) coefficients
+ vs_ld2_indexed(vs1, __ T4S, coeffs, tmpAddr, i, offsets);
+ // b load next 32 (8x4S) inputs
+ vs_ldpq_post(vs_front(vs2), zetas);
+ // compute in parallel (a0, a1) = (a0 + a1, (a0 - a1) montmul b)
+ // n.b. second half of vs2 provides temporary register storage
+ dilithium_sub_add_montmul16(vs_even(vs1), vs_odd(vs1),
+ vs_front(vs2), vs_back(vs2), vtmp, vq);
+ // a0/a1 store interleaved 32 (8x4S) coefficients
+ vs_st2_indexed(vs1, __ T4S, coeffs, tmpAddr, i, offsets);
+ }
+
+ // level 1
+ // At level 1 we need to interleave pairs of adjacent pairs of
+ // coefficients before we multiply by the next 16 zetas just as we
+ // did for level 6 in the multiply code. So we load and store the
+ // values an ld2/st2 with arrangement 2D
+ for (int i = 0; i < 1024; i += 128) {
+ // a0/a1 load interleaved 32 (8x2D) coefficients
+ vs_ld2_indexed(vs1, __ T2D, coeffs, tmpAddr, i, offsets);
+ // b load next 16 (4x4S) inputs
+ vs_ldpq_post(vs_front(vs2), zetas);
+ // compute in parallel (a0, a1) = (a0 + a1, (a0 - a1) montmul b)
+ // n.b. second half of vs2 provides temporary register storage
+ dilithium_sub_add_montmul16(vs_even(vs1), vs_odd(vs1),
+ vs_front(vs2), vs_back(vs2), vtmp, vq);
+ // a0/a1 store interleaved 32 (8x2D) coefficients
+ vs_st2_indexed(vs1, __ T2D, coeffs, tmpAddr, i, offsets);
+ }
+
+ // level 2
+ // At level 2 coefficients come in blocks of 4. So, we load 4
+ // adjacent coefficients at 8 distinct offsets for both the first
+ // and second coefficient sequences, using an ldr with register
+ // variant Q then combine them with next set of 32 zetas. Likewise
+ // we store the results using an str with register variant Q.
+ for (int i = 0; i < 1024; i += 256) {
+ // c0 load 32 (8x4S) coefficients via first offsets
+ vs_ldr_indexed(vs1, __ Q, coeffs, i, offsets1);
+ // c1 load 32 (8x4S) coefficients via second offsets
+ vs_ldr_indexed(vs2, __ Q,coeffs, i, offsets2);
+ // a0 = c0 + c1 n.b. clobbers vq which overlaps vs3
+ vs_addv(vs3, __ T4S, vs1, vs2);
+ // c = c0 - c1
+ vs_subv(vs1, __ T4S, vs1, vs2);
+ // store a0 32 (8x4S) coefficients via first offsets
+ vs_str_indexed(vs3, __ Q, coeffs, i, offsets1);
+ // b load 32 (8x4S) next inputs
+ vs_ldpq_post(vs2, zetas);
+ // reload constants q, qinv -- they were clobbered earlier
+ vs_ldpq(vq, dilithiumConsts); // qInv, q
+ // compute a1 = b montmul c
+ vs_montmul32(vs2, vs1, vs2, vtmp, vq);
+ // store a1 32 (8x4S) coefficients via second offsets
+ vs_str_indexed(vs2, __ Q, coeffs, i, offsets2);
+ }
+
+ // level 3-7
+ dilithiumInverseNttLevel3_7(dilithiumConsts, coeffs, zetas);
+
+ __ leave(); // required for proper stackwalking of RuntimeStub frame
+ __ mov(r0, zr); // return 0
+ __ ret(lr);
+
+ return start;
+
+ }
+
+ // Dilithium multiply polynomials in the NTT domain.
+ // Straightforward implementation of the method
+ // static int implDilithiumNttMult(
+ // int[] result, int[] ntta, int[] nttb {} of
+ // the sun.security.provider.ML_DSA class.
+ //
+ // result (int[256]) = c_rarg0
+ // poly1 (int[256]) = c_rarg1
+ // poly2 (int[256]) = c_rarg2
+ address generate_dilithiumNttMult() {
+
+ __ align(CodeEntryAlignment);
+ StubCodeMark mark(this, "StubRoutines", "dilithiumNttMult");
+ address start = __ pc();
+ __ enter();
+
+ Label L_loop;
+
+ const Register result = c_rarg0;
+ const Register poly1 = c_rarg1;
+ const Register poly2 = c_rarg2;
+
+ const Register dilithiumConsts = r10;
+ const Register len = r11;
+
+ VSeq<8> vs1(0), vs2(16), vs3(24); // 3 sets of 8x4s inputs/outputs
+ VSeq<4> vtmp = vs_front(vs3); // n.b. tmp registers overlap vs3
+ VSeq<2> vq(30); // n.b. constants overlap vs3
+ VSeq<8> vrsquare(29, 0); // for montmul by constant RSQUARE
+
+ __ lea(dilithiumConsts, ExternalAddress((address) StubRoutines::aarch64::_dilithiumConsts));
+
+ // load constants q, qinv
+ vs_ldpq(vq, dilithiumConsts); // qInv, q
+ // load constant rSquare into v29
+ __ ldr(v29, __ Q, Address(dilithiumConsts, 48)); // rSquare
+
+ __ mov(len, zr);
+ __ add(len, len, 1024);
+
+ __ BIND(L_loop);
+
+ // b load 32 (8x4S) next inputs from poly1
+ vs_ldpq_post(vs1, poly1);
+ // c load 32 (8x4S) next inputs from poly2
+ vs_ldpq_post(vs2, poly2);
+ // compute a = b montmul c
+ vs_montmul32(vs2, vs1, vs2, vtmp, vq);
+ // compute a = rsquare montmul a
+ vs_montmul32(vs2, vrsquare, vs2, vtmp, vq);
+ // save a 32 (8x4S) results
+ vs_stpq_post(vs2, result);
+
+ __ sub(len, len, 128);
+ __ cmp(len, (u1)128);
+ __ br(Assembler::GE, L_loop);
+
+ __ leave(); // required for proper stackwalking of RuntimeStub frame
+ __ mov(r0, zr); // return 0
+ __ ret(lr);
+
+ return start;
+
+ }
+
+ // Dilithium Motgomery multiply an array by a constant.
+ // A straightforward implementation of the method
+ // static int implDilithiumMontMulByConstant(int[] coeffs, int constant) {}
+ // of the sun.security.provider.MLDSA class
+ //
+ // coeffs (int[256]) = c_rarg0
+ // constant (int) = c_rarg1
+ address generate_dilithiumMontMulByConstant() {
+
+ __ align(CodeEntryAlignment);
+ StubCodeMark mark(this, "StubRoutines", "dilithiumMontMulByConstant");
+ address start = __ pc();
+ __ enter();
+
+ Label L_loop;
+
+ const Register coeffs = c_rarg0;
+ const Register constant = c_rarg1;
+
+ const Register dilithiumConsts = r10;
+ const Register result = r11;
+ const Register len = r12;
+
+ VSeq<8> vs1(0), vs2(16), vs3(24); // 3 sets of 8x4s inputs/outputs
+ VSeq<4> vtmp = vs_front(vs3); // n.b. tmp registers overlap vs3
+ VSeq<2> vq(30); // n.b. constants overlap vs3
+ VSeq<8> vconst(29, 0); // for montmul by constant
+
+ // results track inputs
+ __ add(result, coeffs, 0);
+ __ lea(dilithiumConsts, ExternalAddress((address) StubRoutines::aarch64::_dilithiumConsts));
+
+ // load constants q, qinv -- they do not get clobbered by first two loops
+ vs_ldpq(vq, dilithiumConsts); // qInv, q
+ // copy caller supplied constant across vconst
+ __ dup(vconst[0], __ T4S, constant);
+ __ mov(len, zr);
+ __ add(len, len, 1024);
+
+ __ BIND(L_loop);
+
+ // load next 32 inputs
+ vs_ldpq_post(vs2, coeffs);
+ // mont mul by constant
+ vs_montmul32(vs2, vconst, vs2, vtmp, vq);
+ // write next 32 results
+ vs_stpq_post(vs2, result);
+
+ __ sub(len, len, 128);
+ __ cmp(len, (u1)128);
+ __ br(Assembler::GE, L_loop);
+
+ __ leave(); // required for proper stackwalking of RuntimeStub frame
+ __ mov(r0, zr); // return 0
+ __ ret(lr);
+
+ return start;
+
+ }
+
+ // Dilithium decompose poly.
+ // Implements the method
+ // static int implDilithiumDecomposePoly(int[] coeffs, int constant) {}
+ // of the sun.security.provider.ML_DSA class
+ //
+ // input (int[256]) = c_rarg0
+ // lowPart (int[256]) = c_rarg1
+ // highPart (int[256]) = c_rarg2
+ // twoGamma2 (int) = c_rarg3
+ // multiplier (int) = c_rarg4
+ address generate_dilithiumDecomposePoly() {
+
+ __ align(CodeEntryAlignment);
+ StubCodeMark mark(this, "StubRoutines", "dilithiumDecomposePoly");
+ address start = __ pc();
+ Label L_loop;
+
+ const Register input = c_rarg0;
+ const Register lowPart = c_rarg1;
+ const Register highPart = c_rarg2;
+ const Register twoGamma2 = c_rarg3;
+ const Register multiplier = c_rarg4;
+
+ const Register len = r9;
+ const Register dilithiumConsts = r10;
+ const Register tmp = r11;
+
+ VSeq<4> vs1(0), vs2(4), vs3(8); // 6 independent sets of 4x4s values
+ VSeq<4> vs4(12), vs5(16), vtmp(20);
+ VSeq<4> one(25, 0); // 7 constants for cross-multiplying
+ VSeq<4> qminus1(26, 0);
+ VSeq<4> g2(27, 0);
+ VSeq<4> twog2(28, 0);
+ VSeq<4> mult(29, 0);
+ VSeq<4> q(30, 0);
+ VSeq<4> qadd(31, 0);
+
+ __ enter();
+
+ __ lea(dilithiumConsts, ExternalAddress((address) StubRoutines::aarch64::_dilithiumConsts));
+
+ // save callee-saved registers
+ __ stpd(v8, v9, __ pre(sp, -64));
+ __ stpd(v10, v11, Address(sp, 16));
+ __ stpd(v12, v13, Address(sp, 32));
+ __ stpd(v14, v15, Address(sp, 48));
+
+ // populate constant registers
+ __ mov(tmp, zr);
+ __ add(tmp, tmp, 1);
+ __ dup(one[0], __ T4S, tmp); // 1
+ __ ldr(q[0], __ Q, Address(dilithiumConsts, 16)); // q
+ __ ldr(qadd[0], __ Q, Address(dilithiumConsts, 64)); // addend for mod q reduce
+ __ dup(twog2[0], __ T4S, twoGamma2); // 2 * gamma2
+ __ dup(mult[0], __ T4S, multiplier); // multiplier for mod 2 * gamma reduce
+ __ subv(qminus1[0], __ T4S, v30, v25); // q - 1
+ __ sshr(g2[0], __ T4S, v28, 1); // gamma2
+
+ __ mov(len, zr);
+ __ add(len, len, 1024);
+
+ __ BIND(L_loop);
+
+ // load next 4x4S inputs interleaved: rplus --> vs1
+ __ ld4(vs1[0], vs1[1], vs1[2], vs1[3], __ T4S, __ post(input, 64));
+
+ // rplus = rplus - ((rplus + qadd) >> 23) * q
+ vs_addv(vtmp, __ T4S, vs1, qadd);
+ vs_sshr(vtmp, __ T4S, vtmp, 23);
+ vs_mulv(vtmp, __ T4S, vtmp, q);
+ vs_subv(vs1, __ T4S, vs1, vtmp);
+
+ // rplus = rplus + ((rplus >> 31) & dilithium_q);
+ vs_sshr(vtmp, __ T4S, vs1, 31);
+ vs_andr(vtmp, vtmp, q);
+ vs_addv(vs1, __ T4S, vs1, vtmp);
+
+ // quotient --> vs2
+ // int quotient = (rplus * multiplier) >> 22;
+ vs_mulv(vtmp, __ T4S, vs1, mult);
+ vs_sshr(vs2, __ T4S, vtmp, 22);
+
+ // r0 --> vs3
+ // int r0 = rplus - quotient * twoGamma2;
+ vs_mulv(vtmp, __ T4S, vs2, twog2);
+ vs_subv(vs3, __ T4S, vs1, vtmp);
+
+ // mask --> vs4
+ // int mask = (twoGamma2 - r0) >> 22;
+ vs_subv(vtmp, __ T4S, twog2, vs3);
+ vs_sshr(vs4, __ T4S, vtmp, 22);
+
+ // r0 -= (mask & twoGamma2);
+ vs_andr(vtmp, vs4, twog2);
+ vs_subv(vs3, __ T4S, vs3, vtmp);
+
+ // quotient += (mask & 1);
+ vs_andr(vtmp, vs4, one);
+ vs_addv(vs2, __ T4S, vs2, vtmp);
+
+ // mask = (twoGamma2 / 2 - r0) >> 31;
+ vs_subv(vtmp, __ T4S, g2, vs3);
+ vs_sshr(vs4, __ T4S, vtmp, 31);
+
+ // r0 -= (mask & twoGamma2);
+ vs_andr(vtmp, vs4, twog2);
+ vs_subv(vs3, __ T4S, vs3, vtmp);
+
+ // quotient += (mask & 1);
+ vs_andr(vtmp, vs4, one);
+ vs_addv(vs2, __ T4S, vs2, vtmp);
+
+ // r1 --> vs5
+ // int r1 = rplus - r0 - (dilithium_q - 1);
+ vs_subv(vtmp, __ T4S, vs1, vs3);
+ vs_subv(vs5, __ T4S, vtmp, qminus1);
+
+ // r1 --> vs1 (overwriting rplus)
+ // r1 = (r1 | (-r1)) >> 31; // 0 if rplus - r0 == (dilithium_q - 1), -1 otherwise
+ vs_negr(vtmp, __ T4S, vs5);
+ vs_orr(vtmp, vs5, vtmp);
+ vs_sshr(vs1, __ T4S, vtmp, 31);
+
+ // r0 += ~r1;
+ vs_notr(vtmp, vs1);
+ vs_addv(vs3, __ T4S, vs3, vtmp);
+
+ // r1 = r1 & quotient;
+ vs_andr(vs1, vs2, vs1);
+
+ // store results inteleaved
+ // lowPart[m] = r0;
+ // highPart[m] = r1;
+ __ st4(vs3[0], vs3[1], vs3[2], vs3[3], __ T4S, __ post(lowPart, 64));
+ __ st4(vs1[0], vs1[1], vs1[2], vs1[3], __ T4S, __ post(highPart, 64));
+
+
+ __ sub(len, len, 64);
+ __ cmp(len, (u1)64);
+ __ br(Assembler::GE, L_loop);
+
+ // restore callee-saved vector registers
+ __ ldpd(v14, v15, Address(sp, 48));
+ __ ldpd(v12, v13, Address(sp, 32));
+ __ ldpd(v10, v11, Address(sp, 16));
+ __ ldpd(v8, v9, __ post(sp, 64));
+
+ __ leave(); // required for proper stackwalking of RuntimeStub frame
+ __ mov(r0, zr); // return 0
+ __ ret(lr);
+
+ return start;
+
+ }
+
/**
* Arguments:
*
@@ -8822,6 +9884,14 @@ class StubGenerator: public StubCodeGenerator {
StubRoutines::_chacha20Block = generate_chacha20Block_blockpar();
}
+ if (UseDilithiumIntrinsics) {
+ StubRoutines::_dilithiumAlmostNtt = generate_dilithiumAlmostNtt();
+ StubRoutines::_dilithiumAlmostInverseNtt = generate_dilithiumAlmostInverseNtt();
+ StubRoutines::_dilithiumNttMult = generate_dilithiumNttMult();
+ StubRoutines::_dilithiumMontMulByConstant = generate_dilithiumMontMulByConstant();
+ StubRoutines::_dilithiumDecomposePoly = generate_dilithiumDecomposePoly();
+ }
+
if (UseBASE64Intrinsics) {
StubRoutines::_base64_encodeBlock = generate_base64_encodeBlock();
StubRoutines::_base64_decodeBlock = generate_base64_decodeBlock();
@@ -8864,6 +9934,7 @@ class StubGenerator: public StubCodeGenerator {
}
if (UseSHA3Intrinsics) {
StubRoutines::_sha3_implCompress = generate_sha3_implCompress(false, "sha3_implCompress");
+ StubRoutines::_double_keccak = generate_double_keccak();
StubRoutines::_sha3_implCompressMB = generate_sha3_implCompress(true, "sha3_implCompressMB");
}
diff --git a/src/hotspot/cpu/aarch64/stubRoutines_aarch64.cpp b/src/hotspot/cpu/aarch64/stubRoutines_aarch64.cpp
index c107e614b574..f7f836cab9f6 100644
--- a/src/hotspot/cpu/aarch64/stubRoutines_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/stubRoutines_aarch64.cpp
@@ -68,6 +68,15 @@ address StubRoutines::aarch64::_spin_wait = CAST_FROM_FN_PTR(address, empty_spin
bool StubRoutines::aarch64::_completed = false;
+ATTRIBUTE_ALIGNED(64) uint32_t StubRoutines::aarch64::_dilithiumConsts[] =
+{
+ 58728449, 58728449, 58728449, 58728449, // montQInvModR
+ 8380417, 8380417, 8380417, 8380417, // dilithium_q
+ 16382, 16382, 16382, 16382, // toMont((dilithium_n)^-1 (mod dilithium_q))
+ 2365951, 2365951, 2365951, 2365951, // montRSquareModQ
+ 5373807, 5373807, 5373807, 5373807 // addend for modular reduce
+};
+
/**
* crc_table[] from jdk/src/share/native/java/util/zip/zlib-1.2.5/crc32.h
*/
diff --git a/src/hotspot/cpu/aarch64/stubRoutines_aarch64.hpp b/src/hotspot/cpu/aarch64/stubRoutines_aarch64.hpp
index dcc2604ed214..d4e206d1b38d 100644
--- a/src/hotspot/cpu/aarch64/stubRoutines_aarch64.hpp
+++ b/src/hotspot/cpu/aarch64/stubRoutines_aarch64.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, Red Hat Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -38,7 +38,7 @@ enum platform_dependent_constants {
// simply increase sizes if too small (assembler will crash if too small)
_initial_stubs_code_size = 10000,
_continuation_stubs_code_size = 2000,
- _compiler_stubs_code_size = 30000 ZGC_ONLY(+10000),
+ _compiler_stubs_code_size = 55000 ZGC_ONLY(+10000),
_final_stubs_code_size = 20000 ZGC_ONLY(+100000)
};
@@ -220,6 +220,7 @@ class aarch64 {
}
private:
+ static uint32_t _dilithiumConsts[];
static juint _crc_table[];
static jubyte _adler_table[];
// begin trigonometric tables block. See comments in .cpp file
diff --git a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp
index 6c95dc9c8ff9..50ea4b3d2c47 100644
--- a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp
@@ -411,6 +411,17 @@ void VM_Version::initialize() {
FLAG_SET_DEFAULT(UseChaCha20Intrinsics, false);
}
+ if (_features & CPU_ASIMD) {
+ if (FLAG_IS_DEFAULT(UseDilithiumIntrinsics)) {
+ UseDilithiumIntrinsics = true;
+ }
+ } else if (UseDilithiumIntrinsics) {
+ if (!FLAG_IS_DEFAULT(UseDilithiumIntrinsics)) {
+ warning("Dilithium intrinsic requires ASIMD instructions");
+ }
+ FLAG_SET_DEFAULT(UseDilithiumIntrinsics, false);
+ }
+
if (FLAG_IS_DEFAULT(UseBASE64Intrinsics)) {
UseBASE64Intrinsics = true;
}
diff --git a/src/hotspot/cpu/x86/assembler_x86.cpp b/src/hotspot/cpu/x86/assembler_x86.cpp
index 7daf7589fb09..a018eaf7b576 100644
--- a/src/hotspot/cpu/x86/assembler_x86.cpp
+++ b/src/hotspot/cpu/x86/assembler_x86.cpp
@@ -3260,6 +3260,23 @@ void Assembler::evmovdqub(Address dst, KRegister mask, XMMRegister src, bool mer
emit_operand(src, dst, 0);
}
+void Assembler::evmovdquw(XMMRegister dst, XMMRegister src, int vector_len) {
+ // Unmasked instruction
+ evmovdquw(dst, k0, src, /*merge*/ false, vector_len);
+}
+
+void Assembler::evmovdquw(XMMRegister dst, KRegister mask, XMMRegister src, bool merge, int vector_len) {
+ assert(vector_len <= AVX_256bit ? VM_Version::supports_avx512vlbw() : VM_Version::supports_avx512bw(), "");
+ InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true);
+ attributes.set_embedded_opmask_register_specifier(mask);
+ attributes.set_is_evex_instruction();
+ if (merge) {
+ attributes.reset_is_clear_context();
+ }
+ int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F, &attributes);
+ emit_int16(0x6F, (0xC0 | encode));
+}
+
void Assembler::evmovdquw(XMMRegister dst, Address src, int vector_len) {
// Unmasked instruction
evmovdquw(dst, k0, src, /*merge*/ false, vector_len);
@@ -7483,6 +7500,15 @@ void Assembler::vpmuludq(XMMRegister dst, XMMRegister nds, XMMRegister src, int
emit_int16((unsigned char)0xF4, (0xC0 | encode));
}
+void Assembler::vpmuldq(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) {
+ assert(vector_len == AVX_128bit ? VM_Version::supports_avx() :
+ (vector_len == AVX_256bit ? VM_Version::supports_avx2() : VM_Version::supports_evex()), "");
+ InstructionAttr attributes(vector_len, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true);
+ attributes.set_rex_vex_w_reverted();
+ int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes);
+ emit_int16(0x28, (0xC0 | encode));
+}
+
void Assembler::vpmullw(XMMRegister dst, XMMRegister nds, Address src, int vector_len) {
assert(UseAVX > 0, "requires some form of AVX");
InstructionMark im(this);
@@ -9438,6 +9464,18 @@ void Assembler::evpmullq(XMMRegister dst, KRegister mask, XMMRegister nds, Addre
emit_operand(dst, src, 0);
}
+void Assembler::evpmulhw(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int vector_len) {
+ assert(VM_Version::supports_avx512bw() && (vector_len == AVX_512bit || VM_Version::supports_avx512vl()), "");
+ InstructionAttr attributes(vector_len, /* vex_w */ false,/* legacy_mode */ false, /* no_mask_reg */ false,/* uses_vl */ true);
+ attributes.set_is_evex_instruction();
+ attributes.set_embedded_opmask_register_specifier(mask);
+ if (merge) {
+ attributes.reset_is_clear_context();
+ }
+ int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes);
+ emit_int16((unsigned char)0xE5, (0xC0 | encode));
+}
+
void Assembler::evmulps(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int vector_len) {
assert(VM_Version::supports_evex(), "");
assert(vector_len == AVX_512bit || VM_Version::supports_avx512vl(), "");
@@ -14067,3 +14105,28 @@ void InstructionAttr::set_address_attributes(int tuple_type, int input_size_in_b
_input_size_in_bits = input_size_in_bits;
}
}
+
+void Assembler::evpermt2w(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) {
+ assert(vector_len <= AVX_256bit ? VM_Version::supports_avx512vlbw() : VM_Version::supports_avx512bw(), "");
+ InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true);
+ attributes.set_is_evex_instruction();
+ int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes);
+ emit_int16(0x7D, (0xC0 | encode));
+}
+
+void Assembler::evpermt2d(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) {
+ assert(VM_Version::supports_evex() && (vector_len == Assembler::AVX_512bit || VM_Version::supports_avx512vl()), "");
+ InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true);
+ attributes.set_is_evex_instruction();
+ int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes);
+ emit_int16(0x7E, (0xC0 | encode));
+}
+
+void Assembler::evpermt2q(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) {
+ assert(VM_Version::supports_evex() && (vector_len == Assembler::AVX_512bit || VM_Version::supports_avx512vl()), "");
+ InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true);
+ attributes.set_is_evex_instruction();
+ int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes);
+ emit_int16(0x7E, (0xC0 | encode));
+}
+
diff --git a/src/hotspot/cpu/x86/assembler_x86.hpp b/src/hotspot/cpu/x86/assembler_x86.hpp
index 8b17abd3fe6e..fe7e1b20e8be 100644
--- a/src/hotspot/cpu/x86/assembler_x86.hpp
+++ b/src/hotspot/cpu/x86/assembler_x86.hpp
@@ -1597,6 +1597,7 @@ class Assembler : public AbstractAssembler {
void evmovdqub(XMMRegister dst, KRegister mask, Address src, bool merge, int vector_len);
void evmovdqub(Address dst, KRegister mask, XMMRegister src, bool merge, int vector_len);
+ void evmovdquw(XMMRegister dst, XMMRegister src, int vector_len);
void evmovdquw(XMMRegister dst, Address src, int vector_len);
void evmovdquw(Address dst, XMMRegister src, int vector_len);
void evmovdquw(XMMRegister dst, KRegister mask, XMMRegister src, bool merge, int vector_len);
@@ -1777,6 +1778,9 @@ class Assembler : public AbstractAssembler {
void vpermpd(XMMRegister dst, XMMRegister src, int imm8, int vector_len);
void evpermi2q(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len);
void evpermt2b(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len);
+ void evpermt2w(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len);
+ void evpermt2d(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len);
+ void evpermt2q(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len);
void evpmultishiftqb(XMMRegister dst, XMMRegister ctl, XMMRegister src, int vector_len);
void pause();
@@ -2420,6 +2424,7 @@ class Assembler : public AbstractAssembler {
void evsubps(XMMRegister dst, KRegister mask, XMMRegister nds, Address src, bool merge, int vector_len);
void evsubpd(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int vector_len);
void evsubpd(XMMRegister dst, KRegister mask, XMMRegister nds, Address src, bool merge, int vector_len);
+ void evpmulhw(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int vector_len);
void evpmullw(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int vector_len);
void evpmullw(XMMRegister dst, KRegister mask, XMMRegister nds, Address src, bool merge, int vector_len);
void evpmulld(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int vector_len);
@@ -2556,6 +2561,7 @@ class Assembler : public AbstractAssembler {
void vpmulld(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len);
void evpmullq(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len);
void vpmuludq(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len);
+ void vpmuldq(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len);
void vpmullw(XMMRegister dst, XMMRegister nds, Address src, int vector_len);
void vpmulld(XMMRegister dst, XMMRegister nds, Address src, int vector_len);
void evpmullq(XMMRegister dst, XMMRegister nds, Address src, int vector_len);
diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.hpp b/src/hotspot/cpu/x86/macroAssembler_x86.hpp
index 7d07519c1c76..407e3bfd2cd8 100644
--- a/src/hotspot/cpu/x86/macroAssembler_x86.hpp
+++ b/src/hotspot/cpu/x86/macroAssembler_x86.hpp
@@ -1220,6 +1220,7 @@ class MacroAssembler: public Assembler {
void evmovdqub(XMMRegister dst, KRegister mask, Address src, bool merge, int vector_len) { Assembler::evmovdqub(dst, mask, src, merge, vector_len); }
void evmovdqub(XMMRegister dst, KRegister mask, AddressLiteral src, bool merge, int vector_len, Register rscratch = noreg);
+ void evmovdquw(XMMRegister dst, XMMRegister src, int vector_len) { Assembler::evmovdquw(dst, src, vector_len); }
void evmovdquw(Address dst, XMMRegister src, int vector_len) { Assembler::evmovdquw(dst, src, vector_len); }
void evmovdquw(XMMRegister dst, Address src, int vector_len) { Assembler::evmovdquw(dst, src, vector_len); }
@@ -1428,6 +1429,8 @@ class MacroAssembler: public Assembler {
void vpmulld(XMMRegister dst, XMMRegister nds, Address src, int vector_len) { Assembler::vpmulld(dst, nds, src, vector_len); }
void vpmulld(XMMRegister dst, XMMRegister nds, AddressLiteral src, int vector_len, Register rscratch = noreg);
+ void vpmuldq(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { Assembler::vpmuldq(dst, nds, src, vector_len); }
+
void vpsubb(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len);
void vpsubb(XMMRegister dst, XMMRegister nds, Address src, int vector_len);
@@ -1437,9 +1440,13 @@ class MacroAssembler: public Assembler {
void vpsraw(XMMRegister dst, XMMRegister nds, XMMRegister shift, int vector_len);
void vpsraw(XMMRegister dst, XMMRegister nds, int shift, int vector_len);
+ void evpsrad(XMMRegister dst, XMMRegister nds, XMMRegister shift, int vector_len);
+ void evpsrad(XMMRegister dst, XMMRegister nds, int shift, int vector_len);
+
void evpsraq(XMMRegister dst, XMMRegister nds, XMMRegister shift, int vector_len);
void evpsraq(XMMRegister dst, XMMRegister nds, int shift, int vector_len);
+ using Assembler::evpsllw;
void evpsllw(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int vector_len, bool is_varshift) {
if (!is_varshift) {
Assembler::evpsllw(dst, mask, nds, src, merge, vector_len);
@@ -1482,6 +1489,7 @@ class MacroAssembler: public Assembler {
Assembler::evpsrlvq(dst, mask, nds, src, merge, vector_len);
}
}
+ using Assembler::evpsraw;
void evpsraw(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int vector_len, bool is_varshift) {
if (!is_varshift) {
Assembler::evpsraw(dst, mask, nds, src, merge, vector_len);
@@ -1489,6 +1497,7 @@ class MacroAssembler: public Assembler {
Assembler::evpsravw(dst, mask, nds, src, merge, vector_len);
}
}
+ using Assembler::evpsrad;
void evpsrad(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int vector_len, bool is_varshift) {
if (!is_varshift) {
Assembler::evpsrad(dst, mask, nds, src, merge, vector_len);
diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp
index 15e9ba59c9ce..957ec4139aff 100644
--- a/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp
+++ b/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp
@@ -4206,6 +4206,8 @@ void StubGenerator::generate_compiler_stubs() {
StubRoutines::_base64_decodeBlock = generate_base64_decodeBlock();
}
+ generate_sha3_stubs();
+
#ifdef COMPILER2
if (UseMultiplyToLenIntrinsic) {
StubRoutines::_multiplyToLen = generate_multiplyToLen();
diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp b/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp
index 0e813681fcf2..5a3d6fe917cc 100644
--- a/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp
+++ b/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp
@@ -423,6 +423,10 @@ class StubGenerator: public StubCodeGenerator {
const XMMRegister L0, const XMMRegister L1, const XMMRegister L2, bool padMSG,
const XMMRegister TMP, const Register rscratch);
+ // SHA3 stubs
+ void generate_sha3_stubs();
+ address generate_sha3_implCompress(bool multiBlock, const char *name);
+
// BASE64 stubs
address base64_shuffle_addr();
diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_sha3.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_sha3.cpp
new file mode 100644
index 000000000000..49c39226708e
--- /dev/null
+++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_sha3.cpp
@@ -0,0 +1,326 @@
+/*
+ * Copyright (c) 2024, 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.
+ *
+ * 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.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "asm/assembler.hpp"
+#include "asm/assembler.inline.hpp"
+#include "runtime/stubRoutines.hpp"
+#include "macroAssembler_x86.hpp"
+#include "stubGenerator_x86_64.hpp"
+
+#define __ _masm->
+
+#ifdef PRODUCT
+#define BLOCK_COMMENT(str) /* nothing */
+#else
+#define BLOCK_COMMENT(str) __ block_comment(str)
+#endif // PRODUCT
+
+#define BIND(label) bind(label); BLOCK_COMMENT(#label ":")
+
+// Constants
+ATTRIBUTE_ALIGNED(64) static const uint64_t round_consts_arr[24] = {
+ 0x0000000000000001L, 0x0000000000008082L, 0x800000000000808AL,
+ 0x8000000080008000L, 0x000000000000808BL, 0x0000000080000001L,
+ 0x8000000080008081L, 0x8000000000008009L, 0x000000000000008AL,
+ 0x0000000000000088L, 0x0000000080008009L, 0x000000008000000AL,
+ 0x000000008000808BL, 0x800000000000008BL, 0x8000000000008089L,
+ 0x8000000000008003L, 0x8000000000008002L, 0x8000000000000080L,
+ 0x000000000000800AL, 0x800000008000000AL, 0x8000000080008081L,
+ 0x8000000000008080L, 0x0000000080000001L, 0x8000000080008008L
+ };
+
+ATTRIBUTE_ALIGNED(64) static const uint64_t permsAndRots[] = {
+ // permutation in combined rho and pi
+ 9, 2, 11, 0, 1, 2, 3, 4, // step 1 and 3
+ 8, 1, 9, 2, 11, 4, 12, 0, // step 2
+ 9, 2, 10, 3, 11, 4, 12, 0, // step 4
+ 8, 9, 2, 3, 4, 5, 6, 7, // step 5
+ 0, 8, 9, 10, 15, 0, 0, 0, // step 6
+ 4, 5, 8, 9, 6, 7, 10, 11, // step 7 and 8
+ 0, 1, 2, 3, 13, 0, 0, 0, // step 9
+ 2, 3, 0, 1, 11, 0, 0, 0, // step 10
+ 4, 5, 6, 7, 14, 0, 0, 0, // step 11
+ 14, 15, 12, 13, 4, 0, 0, 0, // step 12
+ // size of rotations (after step 5)
+ 1, 6, 62, 55, 28, 20, 27, 36,
+ 3, 45, 10, 15, 25, 8, 39, 41,
+ 44, 43, 21, 18, 2, 61, 56, 14,
+ // rotation of row elements
+ 12, 8, 9, 10, 11, 5, 6, 7,
+ 9, 10, 11, 12, 8, 5, 6, 7
+};
+
+static address round_constsAddr() {
+ return (address) round_consts_arr;
+}
+
+static address permsAndRotsAddr() {
+ return (address) permsAndRots;
+}
+
+void StubGenerator::generate_sha3_stubs() {
+ if (UseSHA3Intrinsics) {
+ StubRoutines::_sha3_implCompress = generate_sha3_implCompress(false,"sha3_implCompress");
+ StubRoutines::_sha3_implCompressMB = generate_sha3_implCompress(true, "sha3_implCompressMB");
+ }
+}
+
+// Arguments:
+//
+// Inputs:
+// c_rarg0 - byte[] source+offset
+// c_rarg1 - long[] SHA3.state
+// c_rarg2 - int block_size
+// c_rarg3 - int offset
+// c_rarg4 - int limit
+//
+address StubGenerator::generate_sha3_implCompress(bool multiBlock, const char *name) {
+ __ align(CodeEntryAlignment);
+ StubCodeMark mark(this, "StubRoutines", name);
+ address start = __ pc();
+
+ const Register buf = c_rarg0;
+ const Register state = c_rarg1;
+ const Register block_size = c_rarg2;
+ const Register ofs = c_rarg3;
+#ifndef _WIN64
+ const Register limit = c_rarg4;
+#else
+ const Address limit_mem(rbp, 6 * wordSize);
+ const Register limit = r12;
+#endif
+
+ const Register permsAndRots = r10;
+ const Register round_consts = r11;
+ const Register constant2use = r13;
+ const Register roundsLeft = r14;
+
+ Label sha3_loop;
+ Label rounds24_loop, block104, block136, block144, block168;
+
+ __ enter();
+
+ __ push(r12);
+ __ push(r13);
+ __ push(r14);
+
+#ifdef _WIN64
+ // on win64, fill limit from stack position
+ __ movptr(limit, limit_mem);
+#endif
+
+ __ lea(permsAndRots, ExternalAddress(permsAndRotsAddr()));
+ __ lea(round_consts, ExternalAddress(round_constsAddr()));
+
+ // set up the masks
+ __ movl(rax, 0x1F);
+ __ kmovwl(k5, rax);
+ __ kshiftrwl(k4, k5, 1);
+ __ kshiftrwl(k3, k5, 2);
+ __ kshiftrwl(k2, k5, 3);
+ __ kshiftrwl(k1, k5, 4);
+
+ // load the state
+ __ evmovdquq(xmm0, k5, Address(state, 0), false, Assembler::AVX_512bit);
+ __ evmovdquq(xmm1, k5, Address(state, 40), false, Assembler::AVX_512bit);
+ __ evmovdquq(xmm2, k5, Address(state, 80), false, Assembler::AVX_512bit);
+ __ evmovdquq(xmm3, k5, Address(state, 120), false, Assembler::AVX_512bit);
+ __ evmovdquq(xmm4, k5, Address(state, 160), false, Assembler::AVX_512bit);
+
+ // load the permutation and rotation constants
+ __ evmovdquq(xmm17, Address(permsAndRots, 0), Assembler::AVX_512bit);
+ __ evmovdquq(xmm18, Address(permsAndRots, 64), Assembler::AVX_512bit);
+ __ evmovdquq(xmm19, Address(permsAndRots, 128), Assembler::AVX_512bit);
+ __ evmovdquq(xmm20, Address(permsAndRots, 192), Assembler::AVX_512bit);
+ __ evmovdquq(xmm21, Address(permsAndRots, 256), Assembler::AVX_512bit);
+ __ evmovdquq(xmm22, Address(permsAndRots, 320), Assembler::AVX_512bit);
+ __ evmovdquq(xmm23, Address(permsAndRots, 384), Assembler::AVX_512bit);
+ __ evmovdquq(xmm24, Address(permsAndRots, 448), Assembler::AVX_512bit);
+ __ evmovdquq(xmm25, Address(permsAndRots, 512), Assembler::AVX_512bit);
+ __ evmovdquq(xmm26, Address(permsAndRots, 576), Assembler::AVX_512bit);
+ __ evmovdquq(xmm27, Address(permsAndRots, 640), Assembler::AVX_512bit);
+ __ evmovdquq(xmm28, Address(permsAndRots, 704), Assembler::AVX_512bit);
+ __ evmovdquq(xmm29, Address(permsAndRots, 768), Assembler::AVX_512bit);
+ __ evmovdquq(xmm30, Address(permsAndRots, 832), Assembler::AVX_512bit);
+ __ evmovdquq(xmm31, Address(permsAndRots, 896), Assembler::AVX_512bit);
+
+ __ BIND(sha3_loop);
+
+ // there will be 24 keccak rounds
+ __ movl(roundsLeft, 24);
+ // load round_constants base
+ __ movptr(constant2use, round_consts);
+
+ // load input: 72, 104, 136, 144 or 168 bytes
+ // i.e. 5+4, 2*5+3, 3*5+2, 3*5+3 or 4*5+1 longs
+ __ evpxorq(xmm0, k5, xmm0, Address(buf, 0), true, Assembler::AVX_512bit);
+
+ // if(blockSize == 72) SHA3-512
+ __ cmpl(block_size, 72);
+ __ jcc(Assembler::notEqual, block104);
+ __ evpxorq(xmm1, k4, xmm1, Address(buf, 40), true, Assembler::AVX_512bit);
+ __ jmp(rounds24_loop);
+
+ // if(blockSize == 104) SHA3-384
+ __ BIND(block104);
+ __ cmpl(block_size, 104);
+ __ jcc(Assembler::notEqual, block136);
+ __ evpxorq(xmm1, k5, xmm1, Address(buf, 40), true, Assembler::AVX_512bit);
+ __ evpxorq(xmm2, k3, xmm2, Address(buf, 80), true, Assembler::AVX_512bit);
+ __ jmp(rounds24_loop);
+
+ // if(blockSize == 136) SHA3-256 and SHAKE256
+ __ BIND(block136);
+ __ cmpl(block_size, 136);
+ __ jcc(Assembler::notEqual, block144);
+ __ evpxorq(xmm1, k5, xmm1, Address(buf, 40), true, Assembler::AVX_512bit);
+ __ evpxorq(xmm2, k5, xmm2, Address(buf, 80), true, Assembler::AVX_512bit);
+ __ evpxorq(xmm3, k2, xmm3, Address(buf, 120), true, Assembler::AVX_512bit);
+ __ jmp(rounds24_loop);
+
+ // if(blockSize == 144) SHA3-224
+ __ BIND(block144);
+ __ cmpl(block_size, 144);
+ __ jcc(Assembler::notEqual, block168);
+ __ evpxorq(xmm1, k5, xmm1, Address(buf, 40), true, Assembler::AVX_512bit);
+ __ evpxorq(xmm2, k5, xmm2, Address(buf, 80), true, Assembler::AVX_512bit);
+ __ evpxorq(xmm3, k3, xmm3, Address(buf, 120), true, Assembler::AVX_512bit);
+ __ jmp(rounds24_loop);
+
+ // if(blockSize == 168) SHAKE128
+ __ BIND(block168);
+ __ evpxorq(xmm1, k5, xmm1, Address(buf, 40), true, Assembler::AVX_512bit);
+ __ evpxorq(xmm2, k5, xmm2, Address(buf, 80), true, Assembler::AVX_512bit);
+ __ evpxorq(xmm3, k5, xmm3, Address(buf, 120), true, Assembler::AVX_512bit);
+ __ evpxorq(xmm4, k1, xmm4, Address(buf, 160), true, Assembler::AVX_512bit);
+
+ // The 24 rounds of the keccak transformation.
+ // The implementation closely follows the Java version, with the state
+ // array "rows" in the lowest 5 64-bit slots of zmm0 - zmm4, i.e.
+ // each row of the SHA3 specification is located in one zmm register.
+ __ BIND(rounds24_loop);
+ __ subl(roundsLeft, 1);
+
+ __ evmovdquw(xmm5, xmm0, Assembler::AVX_512bit);
+ // vpternlogq(x, 150, y, z) does x = x ^ y ^ z
+ __ vpternlogq(xmm5, 150, xmm1, xmm2, Assembler::AVX_512bit);
+ __ vpternlogq(xmm5, 150, xmm3, xmm4, Assembler::AVX_512bit);
+ // Now the "c row", i.e. c0-c4 are in zmm5.
+ // Rotate each element of the c row by one bit to zmm6, call the
+ // rotated version c'.
+ __ evprolq(xmm6, xmm5, 1, Assembler::AVX_512bit);
+ // Rotate elementwise the c row so that c4 becomes c0,
+ // c0 becomes c1, etc.
+ __ evpermt2q(xmm5, xmm30, xmm5, Assembler::AVX_512bit);
+ // rotate elementwise the c' row so that c'0 becomes c'4,
+ // c'1 becomes c'0, etc.
+ __ evpermt2q(xmm6, xmm31, xmm6, Assembler::AVX_512bit);
+ __ vpternlogq(xmm0, 150, xmm5, xmm6, Assembler::AVX_512bit);
+ __ vpternlogq(xmm1, 150, xmm5, xmm6, Assembler::AVX_512bit);
+ __ vpternlogq(xmm2, 150, xmm5, xmm6, Assembler::AVX_512bit);
+ __ vpternlogq(xmm3, 150, xmm5, xmm6, Assembler::AVX_512bit);
+ __ vpternlogq(xmm4, 150, xmm5, xmm6, Assembler::AVX_512bit);
+ // Now the theta mapping has been finished.
+
+ // Do the cyclical permutation of the 24 moving state elements
+ // and the required rotations within each element (the combined
+ // rho and sigma steps).
+ __ evpermt2q(xmm4, xmm17, xmm3, Assembler::AVX_512bit);
+ __ evpermt2q(xmm3, xmm18, xmm2, Assembler::AVX_512bit);
+ __ evpermt2q(xmm2, xmm17, xmm1, Assembler::AVX_512bit);
+ __ evpermt2q(xmm1, xmm19, xmm0, Assembler::AVX_512bit);
+ __ evpermt2q(xmm4, xmm20, xmm2, Assembler::AVX_512bit);
+ // The 24 moving elements are now in zmm1, zmm3 and zmm4,
+ // do the rotations now.
+ __ evprolvq(xmm1, xmm1, xmm27, Assembler::AVX_512bit);
+ __ evprolvq(xmm3, xmm3, xmm28, Assembler::AVX_512bit);
+ __ evprolvq(xmm4, xmm4, xmm29, Assembler::AVX_512bit);
+ __ evmovdquw(xmm2, xmm1, Assembler::AVX_512bit);
+ __ evmovdquw(xmm5, xmm3, Assembler::AVX_512bit);
+ __ evpermt2q(xmm0, xmm21, xmm4, Assembler::AVX_512bit);
+ __ evpermt2q(xmm1, xmm22, xmm3, Assembler::AVX_512bit);
+ __ evpermt2q(xmm5, xmm22, xmm2, Assembler::AVX_512bit);
+ __ evmovdquw(xmm3, xmm1, Assembler::AVX_512bit);
+ __ evmovdquw(xmm2, xmm5, Assembler::AVX_512bit);
+ __ evpermt2q(xmm1, xmm23, xmm4, Assembler::AVX_512bit);
+ __ evpermt2q(xmm2, xmm24, xmm4, Assembler::AVX_512bit);
+ __ evpermt2q(xmm3, xmm25, xmm4, Assembler::AVX_512bit);
+ __ evpermt2q(xmm4, xmm26, xmm5, Assembler::AVX_512bit);
+ // The combined rho and sigma steps are done.
+
+ // Do the chi step (the same operation on all 5 rows).
+ // vpternlogq(x, 180, y, z) does x = x ^ (y & ~z).
+ __ evpermt2q(xmm5, xmm31, xmm0, Assembler::AVX_512bit);
+ __ evpermt2q(xmm6, xmm31, xmm5, Assembler::AVX_512bit);
+ __ vpternlogq(xmm0, 180, xmm6, xmm5, Assembler::AVX_512bit);
+
+ __ evpermt2q(xmm5, xmm31, xmm1, Assembler::AVX_512bit);
+ __ evpermt2q(xmm6, xmm31, xmm5, Assembler::AVX_512bit);
+ __ vpternlogq(xmm1, 180, xmm6, xmm5, Assembler::AVX_512bit);
+
+ // xor the round constant into a0 (the lowest 64 bits of zmm0
+ __ evpxorq(xmm0, k1, xmm0, Address(constant2use, 0), true, Assembler::AVX_512bit);
+ __ addptr(constant2use, 8);
+
+ __ evpermt2q(xmm5, xmm31, xmm2, Assembler::AVX_512bit);
+ __ evpermt2q(xmm6, xmm31, xmm5, Assembler::AVX_512bit);
+ __ vpternlogq(xmm2, 180, xmm6, xmm5, Assembler::AVX_512bit);
+
+ __ evpermt2q(xmm5, xmm31, xmm3, Assembler::AVX_512bit);
+ __ evpermt2q(xmm6, xmm31, xmm5, Assembler::AVX_512bit);
+ __ vpternlogq(xmm3, 180, xmm6, xmm5, Assembler::AVX_512bit);
+
+ __ evpermt2q(xmm5, xmm31, xmm4, Assembler::AVX_512bit);
+ __ evpermt2q(xmm6, xmm31, xmm5, Assembler::AVX_512bit);
+ __ vpternlogq(xmm4, 180, xmm6, xmm5, Assembler::AVX_512bit);
+ __ cmpl(roundsLeft, 0);
+ __ jcc(Assembler::notEqual, rounds24_loop);
+
+ if (multiBlock) {
+ __ addptr(buf, block_size);
+ __ addl(ofs, block_size);
+ __ cmpl(ofs, limit);
+ __ jcc(Assembler::lessEqual, sha3_loop);
+ __ movq(rax, ofs); // return ofs
+ } else {
+ __ xorq(rax, rax); // return 0
+ }
+
+ // store the state
+ __ evmovdquq(Address(state, 0), k5, xmm0, true, Assembler::AVX_512bit);
+ __ evmovdquq(Address(state, 40), k5, xmm1, true, Assembler::AVX_512bit);
+ __ evmovdquq(Address(state, 80), k5, xmm2, true, Assembler::AVX_512bit);
+ __ evmovdquq(Address(state, 120), k5, xmm3, true, Assembler::AVX_512bit);
+ __ evmovdquq(Address(state, 160), k5, xmm4, true, Assembler::AVX_512bit);
+
+ __ pop(r14);
+ __ pop(r13);
+ __ pop(r12);
+
+ __ leave(); // required for proper stackwalking of RuntimeStub frame
+ __ ret(0);
+
+ return start;
+}
diff --git a/src/hotspot/cpu/x86/vm_version_x86.cpp b/src/hotspot/cpu/x86/vm_version_x86.cpp
index b7f5a2bc9127..c28afa1e44f5 100644
--- a/src/hotspot/cpu/x86/vm_version_x86.cpp
+++ b/src/hotspot/cpu/x86/vm_version_x86.cpp
@@ -1212,9 +1212,16 @@ void VM_Version::get_processor_features() {
FLAG_SET_DEFAULT(UseSHA512Intrinsics, false);
}
- if (UseSHA3Intrinsics) {
- warning("Intrinsics for SHA3-224, SHA3-256, SHA3-384 and SHA3-512 crypto hash functions not available on this CPU.");
- FLAG_SET_DEFAULT(UseSHA3Intrinsics, false);
+#ifdef _LP64
+ if (supports_evex() && supports_avx512bw()) {
+ if (FLAG_IS_DEFAULT(UseSHA3Intrinsics)) {
+ UseSHA3Intrinsics = true;
+ }
+ } else
+#endif
+ if (UseSHA3Intrinsics) {
+ warning("Intrinsics for SHA3-224, SHA3-256, SHA3-384 and SHA3-512 crypto hash functions not available on this CPU.");
+ FLAG_SET_DEFAULT(UseSHA3Intrinsics, false);
}
if (!(UseSHA1Intrinsics || UseSHA256Intrinsics || UseSHA512Intrinsics)) {
diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp
index a163b864808a..5ae41609b597 100644
--- a/src/hotspot/os/linux/os_linux.cpp
+++ b/src/hotspot/os/linux/os_linux.cpp
@@ -2058,6 +2058,10 @@ void os::print_os_info(outputStream* st) {
st->cr();
}
+ if (os::Linux::print_numa_info(st)) {
+ st->cr();
+ }
+
VM_Version::print_platform_virtualization_info(st);
os::Linux::print_steal_info(st);
@@ -2427,6 +2431,97 @@ bool os::Linux::print_container_info(outputStream* st) {
return true;
}
+#define SYS_DEVICES_NODE "/sys/devices/system/node"
+
+static size_t read_sysfs_file(const char* path, char* buf, size_t sz) {
+ FILE* f = os::fopen(path, "r");
+ if (f == nullptr) return 0;
+ size_t n = fread(buf, 1, sz - 1, f);
+ fclose(f);
+ buf[n] = '\0';
+ while (n > 0 && (buf[n-1] == '\n' || buf[n-1] == '\r')) buf[--n] = '\0';
+ return n;
+}
+
+static void print_numa_memory_info(outputStream* st, int node) {
+ char path[256];
+ char line[256];
+ long long mem_total = -1;
+ long long mem_free = -1;
+ os::snprintf_checked(path, sizeof(path), SYS_DEVICES_NODE "/node%d/meminfo", node);
+ FILE* f = os::fopen(path, "r");
+ if (f == nullptr) {
+ return;
+ }
+
+ while (fgets(line, sizeof(line), f) != nullptr) {
+ long long mval;
+ if (sscanf(line, "Node %*d MemTotal: %lld kB", &mval) == 1) mem_total = mval;
+ if (sscanf(line, "Node %*d MemFree: %lld kB", &mval) == 1) mem_free = mval;
+ }
+ fclose(f);
+
+ if (mem_total >= 0) { st->print_cr("mem size: %lld kB", mem_total); }
+ if (mem_free >= 0) { st->print_cr("mem free: %lld kB", mem_free); }
+}
+
+static void print_numa_cpu_list(outputStream* st, int node) {
+ char path[256];
+ char buf[1024];
+ os::snprintf_checked(path, sizeof(path), SYS_DEVICES_NODE "/node%d/cpulist", node);
+ if (read_sysfs_file(path, buf, sizeof(buf)) > 0) {
+ st->print_cr("cpus: %s", buf);
+ } else {
+ st->print_cr("cpus: (unavailable)");
+ }
+}
+
+bool os::Linux::print_numa_info(outputStream* st) {
+ if (!UseNUMA) {
+ // If NUMA optimizations are not enabled we don't print anything
+ return false;
+ }
+
+ char buf[1024];
+ if (read_sysfs_file("/sys/devices/system/node/online", buf, sizeof(buf)) > 0) {
+ st->print_cr("NUMA nodes online: %s", buf);
+ } else {
+ return false;
+ }
+
+ bool first = true;
+ int node_count = 0;
+
+ if (nindex_to_node() == nullptr) {
+ return false;
+ }
+
+ for (int node: *nindex_to_node()) {
+ char nodepath[256];
+ os::snprintf_checked(nodepath, sizeof(nodepath), SYS_DEVICES_NODE "/node%d", node);
+ DIR* currd = os::opendir(nodepath);
+ if (currd == nullptr) continue;
+ if (first) {
+ st->cr();
+ first = false;
+ }
+ os::closedir(currd);
+
+ st->print_cr("NUMA node %d", node);
+ streamIndentor si(st);
+ print_numa_cpu_list(st, node);
+ print_numa_memory_info(st, node);
+ node_count++;
+ }
+
+ if (node_count == 0) {
+ return false;
+ }
+
+ st->print_cr("Total NUMA node count: %d", node_count);
+ return true;
+}
+
void os::Linux::print_steal_info(outputStream* st) {
if (has_initial_tick_info) {
CPUPerfTicks pticks;
diff --git a/src/hotspot/os/linux/os_linux.hpp b/src/hotspot/os/linux/os_linux.hpp
index ed6c52aade44..799d98d9a155 100644
--- a/src/hotspot/os/linux/os_linux.hpp
+++ b/src/hotspot/os/linux/os_linux.hpp
@@ -96,6 +96,7 @@ class os::Linux {
static void print_proc_sys_info(outputStream* st);
static bool print_ld_preload_file(outputStream* st);
static void print_uptime_info(outputStream* st);
+ static bool print_numa_info(outputStream* st);
public:
struct CPUPerfTicks {
diff --git a/src/hotspot/os/windows/os_perf_windows.cpp b/src/hotspot/os/windows/os_perf_windows.cpp
index 57dcd2710c87..227564b066d0 100644
--- a/src/hotspot/os/windows/os_perf_windows.cpp
+++ b/src/hotspot/os/windows/os_perf_windows.cpp
@@ -780,6 +780,114 @@ static OSReturn allocate_pdh_constants() {
return OS_OK;
}
+// Look up the PDH index by reading the English (locale 009) counter name
+// registry. See KB Q287159: Using PDH APIs Correctly in a Localized Language
+// for details.
+static OSReturn lookup_perf_index_by_english_name(const char* english_name,
+ DWORD* result) {
+ ResourceMark rm;
+
+ DWORD type = 0;
+ DWORD size = 0;
+
+ // Determine the required buffer size
+ if (RegQueryValueEx(HKEY_PERFORMANCE_DATA, "Counter 009",
+ nullptr, &type, nullptr, &size) != ERROR_SUCCESS) {
+ return OS_ERR;
+ }
+
+ // Since registry entries in `HKEY_PERFORMANCE_DATA` are generated on the fly,
+ // they could change between calls, so we can't rely just on the size returned
+ // by the first call. Instead, Microsoft's documentation suggests running
+ // these calls in a loop until the return code is no longer `ERROR_MORE_DATA`.
+
+ char* buffer;
+ do {
+ if (size == 0) {
+ return OS_ERR;
+ }
+
+ // When `RegQueryValueEx()` returns `ERROR_MORE_DATA`, the value in the
+ // callback argument is undefined, so we need to create a new variable whose
+ // address is passed as the callback size argument.
+ buffer = NEW_RESOURCE_ARRAY(char, size);
+
+ DWORD cb_size = size;
+ LSTATUS status = RegQueryValueEx(HKEY_PERFORMANCE_DATA, "Counter 009",
+ nullptr, &type, (LPBYTE)buffer,
+ &cb_size);
+ if (status == ERROR_MORE_DATA) {
+ // We need to increase the buffer size. Since we don't know _how much_ to
+ // increase it by, we use an estimate (4096) for the increment.
+ DWORD increment = 4096;
+ if (size > MAXDWORD - increment) {
+ return OS_ERR;
+ }
+ size += increment;
+ } else if (status == ERROR_SUCCESS) {
+ break;
+ } else {
+ // If there was some other problem fetching this registry entry, tell the
+ // caller that we couldn't lookup the index.
+ return OS_ERR;
+ }
+ } while (true);
+
+ if (type != REG_MULTI_SZ) {
+ return OS_ERR;
+ }
+
+ // The buffer contains indices and names in the form (\0\0)*, so
+ // iterate character by character to parse the name and if it matches the
+ // English name, then we return the integer value of the index.
+ for (const char* p = buffer; *p != '\0'; ) {
+ const char* idx_str = p;
+ p += strlen(p) + 1;
+ if (*p == '\0') {
+ break;
+ }
+
+ const char* name = p;
+ p += strlen(p) + 1;
+ if (strcmp(name, english_name) == 0) {
+ errno = 0;
+ char* end = nullptr;
+ unsigned long value = strtoul(idx_str, &end, 10);
+ if (errno == 0 && end != idx_str && value <= MAXDWORD) {
+ *result = (DWORD)value;
+ return OS_OK;
+ }
+ }
+ }
+
+ return OS_ERR;
+}
+
+// Return the counter index of the 'Processor Information' counter, if
+// available, or else the 'Processor' counter. The former is aware of the
+// possibility of multiple processor groups and thus provides a more accurate
+// processor count whereas the latter serves as fallback.
+static DWORD get_proc_counter() {
+ static DWORD pdh_idx = 0;
+ if (pdh_idx != 0) {
+ return pdh_idx;
+ }
+
+ // Some APIs accept English counter names whereas others accept counter names
+ // in the specific user's locale. We determine the locale-specific name using
+ // the counter index, but to find the counter index, we use the English name
+ // of the counter and look for it in a specific registry key.
+ DWORD info_idx;
+ if (lookup_perf_index_by_english_name("Processor Information",
+ &info_idx) != OS_OK) {
+ info_idx = PDH_PROCESSOR_IDX;
+ }
+
+ // Assign to the static variable so that the value persists across calls.
+ pdh_idx = info_idx;
+ return pdh_idx;
+}
+
/*
* Enuerate the Processor PDH object and returns a buffer containing the enumerated instances.
* Caller needs ResourceMark;
@@ -787,8 +895,11 @@ static OSReturn allocate_pdh_constants() {
* @return buffer if successful, null on failure.
*/
static const char* enumerate_cpu_instances() {
- char* processor; //'Processor' == PDH_PROCESSOR_IDX
- if (lookup_name_by_index(PDH_PROCESSOR_IDX, &processor) != OS_OK) {
+ // The `PdhEnumObjectItems()` function accepts a localized name of the perf
+ // counter. To obtain the name that is specific to the user's locale, we
+ // perform a reverse lookup from counter index to counter name.
+ char* processor;
+ if (lookup_name_by_index(get_proc_counter(), &processor) != OS_OK) {
return nullptr;
}
DWORD c_size = 0;
@@ -822,13 +933,17 @@ static const char* enumerate_cpu_instances() {
static int count_logical_cpus(const char* instances) {
assert(instances != nullptr, "invariant");
- // count logical instances.
- DWORD count;
- char* tmp;
- for (count = 0, tmp = const_cast(instances); *tmp != '\0'; tmp = &tmp[strlen(tmp) + 1], count++);
- // PDH reports an instance for each logical processor plus an instance for the total (_Total)
- assert(count == os::processor_count() + 1, "invalid enumeration!");
- return count - 1;
+ DWORD count = 0;
+ for (const char* tmp = instances; *tmp != '\0'; tmp += strlen(tmp) + 1) {
+ // In both the 'Processor' counter and the 'Processor Information' counter,
+ // the output contains totals for the processor group(s). We filter those
+ // out by looking for the `_Total` substring.
+ if (strstr(tmp, "_Total") == nullptr) {
+ count++;
+ }
+ }
+ assert(count >= 1, "invalid enumeration!");
+ return count;
}
static int number_of_logical_cpus() {
@@ -848,7 +963,16 @@ static double cpu_factor() {
static double cpuFactor = .0;
if (numCpus == 0) {
numCpus = number_of_logical_cpus();
- assert(os::processor_count() <= (int)numCpus, "invariant");
+
+ // If we are using the legacy 'Processor' counter, which counts processors
+ // only in the first processor group, then `numCpus` can undercount, in
+ // which case, `numCpus` will be likely smaller than `os_processor_count`.
+ // However, when we use the 'Processor Information' counter, we expect both
+ // `numCpus` and `os::processorCount` to be identical. In both cases, we
+ // expect to see at least one CPU.
+ assert(numCpus >= 1 && numCpus <= (DWORD)os::processor_count(),
+ "unexpected cpu count");
+
cpuFactor = numCpus * 100;
}
return cpuFactor;
@@ -862,8 +986,8 @@ static void log_error_message_on_no_PDH_artifact(const char* counter_path) {
static int initialize_cpu_query_counters(MultiCounterQueryP query, DWORD pdh_counter_idx) {
assert(query != nullptr, "invariant");
assert(query->counters != nullptr, "invariant");
- char* processor; //'Processor' == PDH_PROCESSOR_IDX
- if (lookup_name_by_index(PDH_PROCESSOR_IDX, &processor) != OS_OK) {
+ char* processor;
+ if (lookup_name_by_index(get_proc_counter(), &processor) != OS_OK) {
return OS_ERR;
}
char* counter_name = nullptr;
@@ -881,7 +1005,11 @@ static int initialize_cpu_query_counters(MultiCounterQueryP query, DWORD pdh_cou
counter_len += OBJECT_WITH_INSTANCES_COUNTER_FMT_LEN; // "\\%s(%s)\\%s"
const char* instances = enumerate_cpu_instances();
DWORD index = 0;
- for (char* tmp = const_cast(instances); *tmp != '\0'; tmp = &tmp[strlen(tmp) + 1], index++) {
+ for (char* tmp = const_cast(instances); *tmp != '\0'; tmp = &tmp[strlen(tmp) + 1]) {
+ // Skip totals for each processor group.
+ if (strstr(tmp, ",_Total") != nullptr) {
+ continue;
+ }
const size_t tmp_len = strlen(tmp);
char* counter_path = NEW_RESOURCE_ARRAY(char, counter_len + tmp_len + 1);
const size_t jio_snprintf_result = jio_snprintf(counter_path,
@@ -897,6 +1025,7 @@ static int initialize_cpu_query_counters(MultiCounterQueryP query, DWORD pdh_cou
// return OS_OK to have the system continue to run without the missing counter
return OS_OK;
}
+ index++;
}
// Query once to initialize the counters which require at least two samples
// (like the % CPU usage) to calculate correctly.
diff --git a/src/hotspot/share/c1/c1_Compiler.cpp b/src/hotspot/share/c1/c1_Compiler.cpp
index 22299b4051b6..bac3a74e99e3 100644
--- a/src/hotspot/share/c1/c1_Compiler.cpp
+++ b/src/hotspot/share/c1/c1_Compiler.cpp
@@ -138,7 +138,7 @@ bool Compiler::is_intrinsic_supported(const methodHandle& method) {
case vmIntrinsics::_arraycopy:
case vmIntrinsics::_currentTimeMillis:
case vmIntrinsics::_nanoTime:
- case vmIntrinsics::_Reference_get:
+ case vmIntrinsics::_Reference_get0:
// Use the intrinsic version of Reference.get() so that the value in
// the referent field can be registered by the G1 pre-barrier code.
// Also to prevent commoning reads from this field across safepoint
diff --git a/src/hotspot/share/c1/c1_GraphBuilder.cpp b/src/hotspot/share/c1/c1_GraphBuilder.cpp
index f58e69d9cfa0..517fd07d64a6 100644
--- a/src/hotspot/share/c1/c1_GraphBuilder.cpp
+++ b/src/hotspot/share/c1/c1_GraphBuilder.cpp
@@ -3377,7 +3377,7 @@ GraphBuilder::GraphBuilder(Compilation* compilation, IRScope* scope)
break;
}
- case vmIntrinsics::_Reference_get:
+ case vmIntrinsics::_Reference_get0:
{
{
// With java.lang.ref.reference.get() we must go through the
diff --git a/src/hotspot/share/c1/c1_LIRGenerator.cpp b/src/hotspot/share/c1/c1_LIRGenerator.cpp
index dda69ef237c0..2699d840dde6 100644
--- a/src/hotspot/share/c1/c1_LIRGenerator.cpp
+++ b/src/hotspot/share/c1/c1_LIRGenerator.cpp
@@ -1203,7 +1203,7 @@ void LIRGenerator::do_Return(Return* x) {
// Example: ref.get()
// Combination of LoadField and g1 pre-write barrier
-void LIRGenerator::do_Reference_get(Intrinsic* x) {
+void LIRGenerator::do_Reference_get0(Intrinsic* x) {
const int referent_offset = java_lang_ref_Reference::referent_offset();
@@ -3016,8 +3016,8 @@ void LIRGenerator::do_Intrinsic(Intrinsic* x) {
case vmIntrinsics::_onSpinWait:
__ on_spin_wait();
break;
- case vmIntrinsics::_Reference_get:
- do_Reference_get(x);
+ case vmIntrinsics::_Reference_get0:
+ do_Reference_get0(x);
break;
case vmIntrinsics::_updateCRC32:
diff --git a/src/hotspot/share/c1/c1_LIRGenerator.hpp b/src/hotspot/share/c1/c1_LIRGenerator.hpp
index 20101fd02dd5..6ce708ce6e6b 100644
--- a/src/hotspot/share/c1/c1_LIRGenerator.hpp
+++ b/src/hotspot/share/c1/c1_LIRGenerator.hpp
@@ -267,7 +267,7 @@ class LIRGenerator: public InstructionVisitor, public BlockClosure {
void do_CompareAndSwap(Intrinsic* x, ValueType* type);
void do_PreconditionsCheckIndex(Intrinsic* x, BasicType type);
void do_FPIntrinsics(Intrinsic* x);
- void do_Reference_get(Intrinsic* x);
+ void do_Reference_get0(Intrinsic* x);
void do_update_CRC32(Intrinsic* x);
void do_update_CRC32C(Intrinsic* x);
void do_vectorizedMismatch(Intrinsic* x);
diff --git a/src/hotspot/share/classfile/classLoader.cpp b/src/hotspot/share/classfile/classLoader.cpp
index ec340d08e9a1..78445bc0f94e 100644
--- a/src/hotspot/share/classfile/classLoader.cpp
+++ b/src/hotspot/share/classfile/classLoader.cpp
@@ -1463,6 +1463,10 @@ char* ClassLoader::lookup_vm_options() {
jio_snprintf(modules_path, JVM_MAXPATHLEN, "%s%slib%smodules", Arguments::get_java_home(), fileSep, fileSep);
JImage_file =(*JImageOpen)(modules_path, &error);
if (JImage_file == nullptr) {
+ if (Arguments::has_jimage()) {
+ // The modules file exists but is unreadable or corrupt
+ vm_exit_during_initialization(err_msg("Unable to load %s", modules_path));
+ }
return nullptr;
}
diff --git a/src/hotspot/share/classfile/stringTable.cpp b/src/hotspot/share/classfile/stringTable.cpp
index 19ac5cc7709d..679edd396713 100644
--- a/src/hotspot/share/classfile/stringTable.cpp
+++ b/src/hotspot/share/classfile/stringTable.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2026, 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
@@ -455,6 +455,10 @@ struct StringTableDeleteCheck : StackObj {
};
void StringTable::clean_dead_entries(JavaThread* jt) {
+ // BulkDeleteTask::prepare() may take ConcurrentHashTableResize_lock (nosafepoint-2).
+ // When NativeHeapTrimmer is enabled, SuspendMark may take NativeHeapTrimmer::_lock (nosafepoint).
+ // Take SuspendMark first to keep lock order and avoid deadlock.
+ NativeHeapTrimmer::SuspendMark sm("stringtable");
StringTableHash::BulkDeleteTask bdt(_local_table);
if (!bdt.prepare(jt)) {
return;
@@ -462,7 +466,6 @@ void StringTable::clean_dead_entries(JavaThread* jt) {
StringTableDeleteCheck stdc;
StringTableDoDelete stdd;
- NativeHeapTrimmer::SuspendMark sm("stringtable");
{
TraceTime timer("Clean", TRACETIME_LOG(Debug, stringtable, perf));
while(bdt.do_task(jt, stdc, stdd)) {
diff --git a/src/hotspot/share/classfile/symbolTable.cpp b/src/hotspot/share/classfile/symbolTable.cpp
index 0b0377a2bb55..3e8ade0022b3 100644
--- a/src/hotspot/share/classfile/symbolTable.cpp
+++ b/src/hotspot/share/classfile/symbolTable.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2026, 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
@@ -755,6 +755,10 @@ struct SymbolTableDeleteCheck : StackObj {
};
void SymbolTable::clean_dead_entries(JavaThread* jt) {
+ // BulkDeleteTask::prepare() may take ConcurrentHashTableResize_lock (nosafepoint-2).
+ // When NativeHeapTrimmer is enabled, SuspendMark may take NativeHeapTrimmer::_lock (nosafepoint).
+ // Take SuspendMark first to keep lock order and avoid deadlock.
+ NativeHeapTrimmer::SuspendMark sm("symboltable");
SymbolTableHash::BulkDeleteTask bdt(_local_table);
if (!bdt.prepare(jt)) {
return;
@@ -762,7 +766,6 @@ void SymbolTable::clean_dead_entries(JavaThread* jt) {
SymbolTableDeleteCheck stdc;
SymbolTableDoDelete stdd;
- NativeHeapTrimmer::SuspendMark sm("symboltable");
{
TraceTime timer("Clean", TRACETIME_LOG(Debug, symboltable, perf));
while (bdt.do_task(jt, stdc, stdd)) {
diff --git a/src/hotspot/share/classfile/vmIntrinsics.cpp b/src/hotspot/share/classfile/vmIntrinsics.cpp
index 578b171a5528..63cc1fa8baf8 100644
--- a/src/hotspot/share/classfile/vmIntrinsics.cpp
+++ b/src/hotspot/share/classfile/vmIntrinsics.cpp
@@ -94,7 +94,7 @@ bool vmIntrinsics::preserves_state(vmIntrinsics::ID id) {
case vmIntrinsics::_dpow:
case vmIntrinsics::_Preconditions_checkIndex:
case vmIntrinsics::_Preconditions_checkLongIndex:
- case vmIntrinsics::_Reference_get:
+ case vmIntrinsics::_Reference_get0:
case vmIntrinsics::_Continuation_doYield:
case vmIntrinsics::_updateCRC32:
case vmIntrinsics::_updateBytesCRC32:
@@ -238,7 +238,7 @@ bool vmIntrinsics::disabled_by_jvm_flags(vmIntrinsics::ID id) {
case vmIntrinsics::_storeFence:
case vmIntrinsics::_fullFence:
case vmIntrinsics::_countPositives:
- case vmIntrinsics::_Reference_get:
+ case vmIntrinsics::_Reference_get0:
case vmIntrinsics::_Continuation_doYield:
case vmIntrinsics::_Continuation_enterSpecial:
break;
@@ -472,6 +472,7 @@ bool vmIntrinsics::disabled_by_jvm_flags(vmIntrinsics::ID id) {
case vmIntrinsics::_sha5_implCompress:
if (!UseSHA512Intrinsics) return true;
break;
+ case vmIntrinsics::_double_keccak:
case vmIntrinsics::_sha3_implCompress:
if (!UseSHA3Intrinsics) return true;
break;
@@ -484,6 +485,13 @@ bool vmIntrinsics::disabled_by_jvm_flags(vmIntrinsics::ID id) {
case vmIntrinsics::_chacha20Block:
if (!UseChaCha20Intrinsics) return true;
break;
+ case vmIntrinsics::_dilithiumAlmostNtt:
+ case vmIntrinsics::_dilithiumAlmostInverseNtt:
+ case vmIntrinsics::_dilithiumNttMult:
+ case vmIntrinsics::_dilithiumMontMulByConstant:
+ case vmIntrinsics::_dilithiumDecomposePoly:
+ if (!UseDilithiumIntrinsics) return true;
+ break;
case vmIntrinsics::_base64_encodeBlock:
case vmIntrinsics::_base64_decodeBlock:
if (!UseBASE64Intrinsics) return true;
diff --git a/src/hotspot/share/classfile/vmIntrinsics.hpp b/src/hotspot/share/classfile/vmIntrinsics.hpp
index 15ad554d8ec5..2c4b9da5bc2d 100644
--- a/src/hotspot/share/classfile/vmIntrinsics.hpp
+++ b/src/hotspot/share/classfile/vmIntrinsics.hpp
@@ -458,7 +458,7 @@ class methodHandle;
do_signature(vectorizedMismatch_signature, "(Ljava/lang/Object;JLjava/lang/Object;JII)I") \
\
/* java/lang/ref/Reference */ \
- do_intrinsic(_Reference_get, java_lang_ref_Reference, get_name, void_object_signature, F_R) \
+ do_intrinsic(_Reference_get0, java_lang_ref_Reference, get0_name, void_object_signature, F_RN) \
do_intrinsic(_Reference_refersTo0, java_lang_ref_Reference, refersTo0_name, object_boolean_signature, F_RN) \
do_intrinsic(_PhantomReference_refersTo0, java_lang_ref_PhantomReference, refersTo0_name, object_boolean_signature, F_RN) \
\
@@ -514,6 +514,12 @@ class methodHandle;
do_class(sun_security_provider_sha3, "sun/security/provider/SHA3") \
do_intrinsic(_sha3_implCompress, sun_security_provider_sha3, implCompress_name, implCompress_signature, F_R) \
\
+ /* support for sun.security.provider.SHAKE128Parallel */ \
+ do_class(sun_security_provider_sha3_parallel, "sun/security/provider/SHA3Parallel") \
+ do_intrinsic(_double_keccak, sun_security_provider_sha3_parallel, double_keccak_name, double_keccak_signature, F_S) \
+ do_name( double_keccak_name, "doubleKeccak") \
+ do_signature(double_keccak_signature, "([J[J)I") \
+ \
/* support for sun.security.provider.DigestBase */ \
do_class(sun_security_provider_digestbase, "sun/security/provider/DigestBase") \
do_intrinsic(_digestBase_implCompressMB, sun_security_provider_digestbase, implCompressMB_name, countPositives_signature, F_R) \
@@ -548,6 +554,26 @@ class methodHandle;
do_name(chacha20Block_name, "implChaCha20Block") \
do_signature(chacha20Block_signature, "([I[B)I") \
\
+ /* support for sun.security.provider.ML_DSA */ \
+ do_class(sun_security_provider_ML_DSA, "sun/security/provider/ML_DSA") \
+ do_signature(IaII_signature, "([II)I") \
+ do_signature(IaIaI_signature, "([I[I)I") \
+ do_signature(IaIaIaI_signature, "([I[I[I)I") \
+ do_signature(IaIaIaIII_signature, "([I[I[III)I") \
+ do_intrinsic(_dilithiumAlmostNtt, sun_security_provider_ML_DSA, dilithiumAlmostNtt_name, IaIaI_signature, F_S) \
+ do_name(dilithiumAlmostNtt_name, "implDilithiumAlmostNtt") \
+ do_intrinsic(_dilithiumAlmostInverseNtt, sun_security_provider_ML_DSA, \
+ dilithiumAlmostInverseNtt_name, IaIaI_signature, F_S) \
+ do_name(dilithiumAlmostInverseNtt_name, "implDilithiumAlmostInverseNtt") \
+ do_intrinsic(_dilithiumNttMult, sun_security_provider_ML_DSA, dilithiumNttMult_name, IaIaIaI_signature, F_S) \
+ do_name(dilithiumNttMult_name, "implDilithiumNttMult") \
+ do_intrinsic(_dilithiumMontMulByConstant, sun_security_provider_ML_DSA, \
+ dilithiumMontMulByConstant_name, IaII_signature, F_S) \
+ do_name(dilithiumMontMulByConstant_name, "implDilithiumMontMulByConstant") \
+ do_intrinsic(_dilithiumDecomposePoly, sun_security_provider_ML_DSA, \
+ dilithiumDecomposePoly_name, IaIaIaIII_signature, F_S) \
+ do_name(dilithiumDecomposePoly_name, "implDilithiumDecomposePoly") \
+ \
/* support for java.util.zip */ \
do_class(java_util_zip_CRC32, "java/util/zip/CRC32") \
do_intrinsic(_updateCRC32, java_util_zip_CRC32, update_name, int2_int_signature, F_SN) \
diff --git a/src/hotspot/share/classfile/vmSymbols.hpp b/src/hotspot/share/classfile/vmSymbols.hpp
index ba4dd72408be..82a79effb2c6 100644
--- a/src/hotspot/share/classfile/vmSymbols.hpp
+++ b/src/hotspot/share/classfile/vmSymbols.hpp
@@ -441,7 +441,7 @@
template(cs_name, "cs") \
template(refStack_name, "refStack") \
template(refSP_name, "refSP") \
- template(get_name, "get") \
+ template(get0_name, "get0") \
template(refersTo0_name, "refersTo0") \
template(put_name, "put") \
template(type_name, "type") \
diff --git a/src/hotspot/share/include/jvm.h b/src/hotspot/share/include/jvm.h
index 73e29bcf2006..ae554f2091c0 100644
--- a/src/hotspot/share/include/jvm.h
+++ b/src/hotspot/share/include/jvm.h
@@ -357,6 +357,9 @@ JVM_HasReferencePendingList(JNIEnv *env);
JNIEXPORT void JNICALL
JVM_WaitForReferencePendingList(JNIEnv *env);
+JNIEXPORT jobject JNICALL
+JVM_ReferenceGet(JNIEnv *env, jobject ref);
+
JNIEXPORT jboolean JNICALL
JVM_ReferenceRefersTo(JNIEnv *env, jobject ref, jobject o);
diff --git a/src/hotspot/share/interpreter/abstractInterpreter.cpp b/src/hotspot/share/interpreter/abstractInterpreter.cpp
index 2883630c37de..939e6afc0839 100644
--- a/src/hotspot/share/interpreter/abstractInterpreter.cpp
+++ b/src/hotspot/share/interpreter/abstractInterpreter.cpp
@@ -147,7 +147,7 @@ AbstractInterpreter::MethodKind AbstractInterpreter::method_kind(const methodHan
case vmIntrinsics::_fmaF: return java_lang_math_fmaF;
case vmIntrinsics::_dsqrt: return java_lang_math_sqrt;
case vmIntrinsics::_dsqrt_strict: return java_lang_math_sqrt_strict;
- case vmIntrinsics::_Reference_get: return java_lang_ref_reference_get;
+ case vmIntrinsics::_Reference_get0: return java_lang_ref_reference_get0;
case vmIntrinsics::_Object_init:
if (RegisterFinalizersAtInit && m->code_size() == 1) {
// We need to execute the special return bytecode to check for
@@ -207,7 +207,7 @@ vmIntrinsics::ID AbstractInterpreter::method_intrinsic(MethodKind kind) {
case java_lang_math_exp : return vmIntrinsics::_dexp;
case java_lang_math_fmaD : return vmIntrinsics::_fmaD;
case java_lang_math_fmaF : return vmIntrinsics::_fmaF;
- case java_lang_ref_reference_get: return vmIntrinsics::_Reference_get;
+ case java_lang_ref_reference_get0: return vmIntrinsics::_Reference_get0;
case java_util_zip_CRC32_update : return vmIntrinsics::_updateCRC32;
case java_util_zip_CRC32_updateBytes
: return vmIntrinsics::_updateBytesCRC32;
@@ -323,7 +323,7 @@ void AbstractInterpreter::print_method_kind(MethodKind kind) {
case java_util_zip_CRC32_updateByteBuffer : tty->print("java_util_zip_CRC32_updateByteBuffer"); break;
case java_util_zip_CRC32C_updateBytes : tty->print("java_util_zip_CRC32C_updateBytes"); break;
case java_util_zip_CRC32C_updateDirectByteBuffer: tty->print("java_util_zip_CRC32C_updateDirectByteByffer"); break;
- case java_lang_ref_reference_get : tty->print("java_lang_ref_reference_get"); break;
+ case java_lang_ref_reference_get0 : tty->print("java_lang_ref_reference_get0"); break;
case java_lang_Thread_currentThread : tty->print("java_lang_Thread_currentThread"); break;
case java_lang_Float_intBitsToFloat : tty->print("java_lang_Float_intBitsToFloat"); break;
case java_lang_Float_floatToRawIntBits : tty->print("java_lang_Float_floatToRawIntBits"); break;
diff --git a/src/hotspot/share/interpreter/abstractInterpreter.hpp b/src/hotspot/share/interpreter/abstractInterpreter.hpp
index e487b152b76e..6805bfe25f69 100644
--- a/src/hotspot/share/interpreter/abstractInterpreter.hpp
+++ b/src/hotspot/share/interpreter/abstractInterpreter.hpp
@@ -81,7 +81,7 @@ class AbstractInterpreter: AllStatic {
java_lang_math_exp, // implementation of java.lang.Math.exp (x)
java_lang_math_fmaF, // implementation of java.lang.Math.fma (x, y, z)
java_lang_math_fmaD, // implementation of java.lang.Math.fma (x, y, z)
- java_lang_ref_reference_get, // implementation of java.lang.ref.Reference.get()
+ java_lang_ref_reference_get0, // implementation of java.lang.ref.Reference.get()
java_util_zip_CRC32_update, // implementation of java.util.zip.CRC32.update()
java_util_zip_CRC32_updateBytes, // implementation of java.util.zip.CRC32.updateBytes()
java_util_zip_CRC32_updateByteBuffer, // implementation of java.util.zip.CRC32.updateByteBuffer()
diff --git a/src/hotspot/share/interpreter/templateInterpreterGenerator.cpp b/src/hotspot/share/interpreter/templateInterpreterGenerator.cpp
index 9cd6f5ceffbe..372e89ecb334 100644
--- a/src/hotspot/share/interpreter/templateInterpreterGenerator.cpp
+++ b/src/hotspot/share/interpreter/templateInterpreterGenerator.cpp
@@ -201,7 +201,7 @@ void TemplateInterpreterGenerator::generate_all() {
method_entry(java_lang_math_pow )
method_entry(java_lang_math_fmaF )
method_entry(java_lang_math_fmaD )
- method_entry(java_lang_ref_reference_get)
+ method_entry(java_lang_ref_reference_get0)
AbstractInterpreter::initialize_method_handle_entries();
method_entry(java_util_zip_CRC32C_updateBytes)
@@ -225,6 +225,7 @@ void TemplateInterpreterGenerator::generate_all() {
// entries for `native` methods to use the same address in case
// intrinsic is disabled.
native_method_entry(java_lang_Thread_currentThread)
+ native_method_entry(java_lang_ref_reference_get0)
native_method_entry(java_util_zip_CRC32_update)
native_method_entry(java_util_zip_CRC32_updateBytes)
@@ -467,7 +468,7 @@ address TemplateInterpreterGenerator::generate_intrinsic_entry(AbstractInterpret
case Interpreter::java_lang_math_fmaF : entry_point = generate_math_entry(kind); break;
case Interpreter::java_lang_math_sqrt_strict
: entry_point = generate_math_entry(Interpreter::java_lang_math_sqrt); break;
- case Interpreter::java_lang_ref_reference_get
+ case Interpreter::java_lang_ref_reference_get0
: entry_point = generate_Reference_get_entry(); break;
case Interpreter::java_util_zip_CRC32_update
: entry_point = generate_CRC32_update_entry(); break;
diff --git a/src/hotspot/share/interpreter/zero/zeroInterpreterGenerator.cpp b/src/hotspot/share/interpreter/zero/zeroInterpreterGenerator.cpp
index e08d9553c3e0..56cdf4505c8c 100644
--- a/src/hotspot/share/interpreter/zero/zeroInterpreterGenerator.cpp
+++ b/src/hotspot/share/interpreter/zero/zeroInterpreterGenerator.cpp
@@ -63,7 +63,7 @@ void ZeroInterpreterGenerator::generate_all() {
method_entry(java_lang_math_exp );
method_entry(java_lang_math_fmaD );
method_entry(java_lang_math_fmaF );
- method_entry(java_lang_ref_reference_get);
+ method_entry(java_lang_ref_reference_get0);
AbstractInterpreter::initialize_method_handle_entries();
@@ -104,7 +104,7 @@ address ZeroInterpreterGenerator::generate_method_entry(
case Interpreter::java_lang_math_exp : // fall thru
case Interpreter::java_lang_math_fmaD : // fall thru
case Interpreter::java_lang_math_fmaF : entry_point = generate_math_entry(kind); break;
- case Interpreter::java_lang_ref_reference_get
+ case Interpreter::java_lang_ref_reference_get0
: entry_point = generate_Reference_get_entry(); break;
default:
fatal("unexpected method kind: %d", kind);
diff --git a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp
index a4195a04f186..b058f31fec9b 100644
--- a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp
+++ b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp
@@ -350,7 +350,13 @@
static_field(StubRoutines, _sha512_implCompress, address) \
static_field(StubRoutines, _sha512_implCompressMB, address) \
static_field(StubRoutines, _sha3_implCompress, address) \
+ static_field(StubRoutines, _double_keccak, address) \
static_field(StubRoutines, _sha3_implCompressMB, address) \
+ static_field(StubRoutines, _dilithiumAlmostNtt, address) \
+ static_field(StubRoutines, _dilithiumAlmostInverseNtt, address) \
+ static_field(StubRoutines, _dilithiumNttMult, address) \
+ static_field(StubRoutines, _dilithiumMontMulByConstant, address) \
+ static_field(StubRoutines, _dilithiumDecomposePoly, address) \
static_field(StubRoutines, _updateBytesCRC32, address) \
static_field(StubRoutines, _crc_table_adr, address) \
static_field(StubRoutines, _crc32c_table_addr, address) \
diff --git a/src/hotspot/share/opto/c2compiler.cpp b/src/hotspot/share/opto/c2compiler.cpp
index 9e27ff17d9e1..eb9cd8e95402 100644
--- a/src/hotspot/share/opto/c2compiler.cpp
+++ b/src/hotspot/share/opto/c2compiler.cpp
@@ -706,7 +706,7 @@ bool C2Compiler::is_intrinsic_supported(const methodHandle& method) {
case vmIntrinsics::_doubleToRawLongBits:
case vmIntrinsics::_doubleToLongBits:
case vmIntrinsics::_longBitsToDouble:
- case vmIntrinsics::_Reference_get:
+ case vmIntrinsics::_Reference_get0:
case vmIntrinsics::_Reference_refersTo0:
case vmIntrinsics::_PhantomReference_refersTo0:
case vmIntrinsics::_Class_cast:
@@ -723,6 +723,7 @@ bool C2Compiler::is_intrinsic_supported(const methodHandle& method) {
case vmIntrinsics::_sha2_implCompress:
case vmIntrinsics::_sha5_implCompress:
case vmIntrinsics::_sha3_implCompress:
+ case vmIntrinsics::_double_keccak:
case vmIntrinsics::_digestBase_implCompressMB:
case vmIntrinsics::_multiplyToLen:
case vmIntrinsics::_squareToLen:
@@ -734,6 +735,11 @@ bool C2Compiler::is_intrinsic_supported(const methodHandle& method) {
case vmIntrinsics::_vectorizedMismatch:
case vmIntrinsics::_ghash_processBlocks:
case vmIntrinsics::_chacha20Block:
+ case vmIntrinsics::_dilithiumAlmostNtt:
+ case vmIntrinsics::_dilithiumAlmostInverseNtt:
+ case vmIntrinsics::_dilithiumNttMult:
+ case vmIntrinsics::_dilithiumMontMulByConstant:
+ case vmIntrinsics::_dilithiumDecomposePoly:
case vmIntrinsics::_base64_encodeBlock:
case vmIntrinsics::_base64_decodeBlock:
case vmIntrinsics::_poly1305_processBlocks:
diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp
index b83464783d12..e47a4daf833c 100644
--- a/src/hotspot/share/opto/compile.cpp
+++ b/src/hotspot/share/opto/compile.cpp
@@ -748,19 +748,9 @@ Compile::Compile( ciEnv* ci_env, ciMethod* target, int osr_bci,
StartNode* s = new StartNode(root(), tf()->domain());
initial_gvn()->set_type_bottom(s);
init_start(s);
- if (method()->intrinsic_id() == vmIntrinsics::_Reference_get) {
- // With java.lang.ref.reference.get() we must go through the
- // intrinsic - even when get() is the root
- // method of the compile - so that, if necessary, the value in
- // the referent field of the reference object gets recorded by
- // the pre-barrier code.
- cg = find_intrinsic(method(), false);
- }
- if (cg == nullptr) {
- float past_uses = method()->interpreter_invocation_count();
- float expected_uses = past_uses;
- cg = CallGenerator::for_inline(method(), expected_uses);
- }
+ float past_uses = method()->interpreter_invocation_count();
+ float expected_uses = past_uses;
+ cg = CallGenerator::for_inline(method(), expected_uses);
}
if (failing()) return;
if (cg == nullptr) {
diff --git a/src/hotspot/share/opto/escape.cpp b/src/hotspot/share/opto/escape.cpp
index 190b3ae75510..f71b43ed1525 100644
--- a/src/hotspot/share/opto/escape.cpp
+++ b/src/hotspot/share/opto/escape.cpp
@@ -1172,6 +1172,11 @@ void ConnectionGraph::process_call_arguments(CallNode *call) {
strcmp(call->as_CallLeaf()->_name, "poly1305_processBlocks") == 0 ||
strcmp(call->as_CallLeaf()->_name, "ghash_processBlocks") == 0 ||
strcmp(call->as_CallLeaf()->_name, "chacha20Block") == 0 ||
+ strcmp(call->as_CallLeaf()->_name, "dilithiumAlmostNtt") == 0 ||
+ strcmp(call->as_CallLeaf()->_name, "dilithiumAlmostInverseNtt") == 0 ||
+ strcmp(call->as_CallLeaf()->_name, "dilithiumNttMult") == 0 ||
+ strcmp(call->as_CallLeaf()->_name, "dilithiumMontMulByConstant") == 0 ||
+ strcmp(call->as_CallLeaf()->_name, "dilithiumDecomposePoly") == 0 ||
strcmp(call->as_CallLeaf()->_name, "encodeBlock") == 0 ||
strcmp(call->as_CallLeaf()->_name, "decodeBlock") == 0 ||
strcmp(call->as_CallLeaf()->_name, "md5_implCompress") == 0 ||
@@ -1183,6 +1188,7 @@ void ConnectionGraph::process_call_arguments(CallNode *call) {
strcmp(call->as_CallLeaf()->_name, "sha512_implCompress") == 0 ||
strcmp(call->as_CallLeaf()->_name, "sha512_implCompressMB") == 0 ||
strcmp(call->as_CallLeaf()->_name, "sha3_implCompress") == 0 ||
+ strcmp(call->as_CallLeaf()->_name, "double_keccak") == 0 ||
strcmp(call->as_CallLeaf()->_name, "sha3_implCompressMB") == 0 ||
strcmp(call->as_CallLeaf()->_name, "multiplyToLen") == 0 ||
strcmp(call->as_CallLeaf()->_name, "squareToLen") == 0 ||
diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp
index 8c892c3acb26..a2f341826ce9 100644
--- a/src/hotspot/share/opto/library_call.cpp
+++ b/src/hotspot/share/opto/library_call.cpp
@@ -568,7 +568,7 @@ bool LibraryCallKit::try_to_inline(int predicate) {
case vmIntrinsics::_getCallerClass: return inline_native_Reflection_getCallerClass();
- case vmIntrinsics::_Reference_get: return inline_reference_get();
+ case vmIntrinsics::_Reference_get0: return inline_reference_get0();
case vmIntrinsics::_Reference_refersTo0: return inline_reference_refersTo0(false);
case vmIntrinsics::_PhantomReference_refersTo0: return inline_reference_refersTo0(true);
@@ -597,6 +597,8 @@ bool LibraryCallKit::try_to_inline(int predicate) {
case vmIntrinsics::_sha5_implCompress:
case vmIntrinsics::_sha3_implCompress:
return inline_digestBase_implCompress(intrinsic_id());
+ case vmIntrinsics::_double_keccak:
+ return inline_double_keccak();
case vmIntrinsics::_digestBase_implCompressMB:
return inline_digestBase_implCompressMB(predicate);
@@ -627,6 +629,16 @@ bool LibraryCallKit::try_to_inline(int predicate) {
return inline_ghash_processBlocks();
case vmIntrinsics::_chacha20Block:
return inline_chacha20Block();
+ case vmIntrinsics::_dilithiumAlmostNtt:
+ return inline_dilithiumAlmostNtt();
+ case vmIntrinsics::_dilithiumAlmostInverseNtt:
+ return inline_dilithiumAlmostInverseNtt();
+ case vmIntrinsics::_dilithiumNttMult:
+ return inline_dilithiumNttMult();
+ case vmIntrinsics::_dilithiumMontMulByConstant:
+ return inline_dilithiumMontMulByConstant();
+ case vmIntrinsics::_dilithiumDecomposePoly:
+ return inline_dilithiumDecomposePoly();
case vmIntrinsics::_base64_encodeBlock:
return inline_base64_encodeBlock();
case vmIntrinsics::_base64_decodeBlock:
@@ -6554,9 +6566,9 @@ bool LibraryCallKit::inline_updateByteBufferAdler32() {
return true;
}
-//----------------------------inline_reference_get----------------------------
+//----------------------------inline_reference_get0----------------------------
// public T java.lang.ref.Reference.get();
-bool LibraryCallKit::inline_reference_get() {
+bool LibraryCallKit::inline_reference_get0() {
const int referent_offset = java_lang_ref_Reference::referent_offset();
// Get the argument:
@@ -7258,6 +7270,176 @@ bool LibraryCallKit::inline_chacha20Block() {
return true;
}
+//------------------------------inline_dilithiumAlmostNtt
+bool LibraryCallKit::inline_dilithiumAlmostNtt() {
+ address stubAddr;
+ const char *stubName;
+ assert(UseDilithiumIntrinsics, "need Dilithium intrinsics support");
+ assert(callee()->signature()->size() == 2, "dilithiumAlmostNtt has 2 parameters");
+
+ stubAddr = StubRoutines::dilithiumAlmostNtt();
+ stubName = "dilithiumAlmostNtt";
+ if (!stubAddr) return false;
+
+ Node* coeffs = argument(0);
+ Node* ntt_zetas = argument(1);
+
+ coeffs = must_be_not_null(coeffs, true);
+ ntt_zetas = must_be_not_null(ntt_zetas, true);
+
+ Node* coeffs_start = array_element_address(coeffs, intcon(0), T_INT);
+ assert(coeffs_start, "coeffs is null");
+ Node* ntt_zetas_start = array_element_address(ntt_zetas, intcon(0), T_INT);
+ assert(ntt_zetas_start, "ntt_zetas is null");
+ Node* dilithiumAlmostNtt = make_runtime_call(RC_LEAF|RC_NO_FP,
+ OptoRuntime::dilithiumAlmostNtt_Type(),
+ stubAddr, stubName, TypePtr::BOTTOM,
+ coeffs_start, ntt_zetas_start);
+ // return an int
+ Node* retvalue = _gvn.transform(new ProjNode(dilithiumAlmostNtt, TypeFunc::Parms));
+ set_result(retvalue);
+ return true;
+}
+
+//------------------------------inline_dilithiumAlmostInverseNtt
+bool LibraryCallKit::inline_dilithiumAlmostInverseNtt() {
+ address stubAddr;
+ const char *stubName;
+ assert(UseDilithiumIntrinsics, "need Dilithium intrinsics support");
+ assert(callee()->signature()->size() == 2, "dilithiumAlmostInverseNtt has 2 parameters");
+
+ stubAddr = StubRoutines::dilithiumAlmostInverseNtt();
+ stubName = "dilithiumAlmostInverseNtt";
+ if (!stubAddr) return false;
+
+ Node* coeffs = argument(0);
+ Node* zetas = argument(1);
+
+ coeffs = must_be_not_null(coeffs, true);
+ zetas = must_be_not_null(zetas, true);
+
+ Node* coeffs_start = array_element_address(coeffs, intcon(0), T_INT);
+ assert(coeffs_start, "coeffs is null");
+ Node* zetas_start = array_element_address(zetas, intcon(0), T_INT);
+ assert(zetas_start, "inverseNtt_zetas is null");
+ Node* dilithiumAlmostInverseNtt = make_runtime_call(RC_LEAF|RC_NO_FP,
+ OptoRuntime::dilithiumAlmostInverseNtt_Type(),
+ stubAddr, stubName, TypePtr::BOTTOM,
+ coeffs_start, zetas_start);
+
+ // return an int
+ Node* retvalue = _gvn.transform(new ProjNode(dilithiumAlmostInverseNtt, TypeFunc::Parms));
+ set_result(retvalue);
+ return true;
+}
+
+//------------------------------inline_dilithiumNttMult
+bool LibraryCallKit::inline_dilithiumNttMult() {
+ address stubAddr;
+ const char *stubName;
+ assert(UseDilithiumIntrinsics, "need Dilithium intrinsics support");
+ assert(callee()->signature()->size() == 3, "dilithiumNttMult has 3 parameters");
+
+ stubAddr = StubRoutines::dilithiumNttMult();
+ stubName = "dilithiumNttMult";
+ if (!stubAddr) return false;
+
+ Node* result = argument(0);
+ Node* ntta = argument(1);
+ Node* nttb = argument(2);
+
+ result = must_be_not_null(result, true);
+ ntta = must_be_not_null(ntta, true);
+ nttb = must_be_not_null(nttb, true);
+
+ Node* result_start = array_element_address(result, intcon(0), T_INT);
+ assert(result_start, "result is null");
+ Node* ntta_start = array_element_address(ntta, intcon(0), T_INT);
+ assert(ntta_start, "ntta is null");
+ Node* nttb_start = array_element_address(nttb, intcon(0), T_INT);
+ assert(nttb_start, "nttb is null");
+ Node* dilithiumNttMult = make_runtime_call(RC_LEAF|RC_NO_FP,
+ OptoRuntime::dilithiumNttMult_Type(),
+ stubAddr, stubName, TypePtr::BOTTOM,
+ result_start, ntta_start, nttb_start);
+
+ // return an int
+ Node* retvalue = _gvn.transform(new ProjNode(dilithiumNttMult, TypeFunc::Parms));
+ set_result(retvalue);
+
+ return true;
+}
+
+//------------------------------inline_dilithiumMontMulByConstant
+bool LibraryCallKit::inline_dilithiumMontMulByConstant() {
+ address stubAddr;
+ const char *stubName;
+ assert(UseDilithiumIntrinsics, "need Dilithium intrinsics support");
+ assert(callee()->signature()->size() == 2, "dilithiumMontMulByConstant has 2 parameters");
+
+ stubAddr = StubRoutines::dilithiumMontMulByConstant();
+ stubName = "dilithiumMontMulByConstant";
+ if (!stubAddr) return false;
+
+ Node* coeffs = argument(0);
+ Node* constant = argument(1);
+
+ coeffs = must_be_not_null(coeffs, true);
+
+ Node* coeffs_start = array_element_address(coeffs, intcon(0), T_INT);
+ assert(coeffs_start, "coeffs is null");
+ Node* dilithiumMontMulByConstant = make_runtime_call(RC_LEAF|RC_NO_FP,
+ OptoRuntime::dilithiumMontMulByConstant_Type(),
+ stubAddr, stubName, TypePtr::BOTTOM,
+ coeffs_start, constant);
+
+ // return an int
+ Node* retvalue = _gvn.transform(new ProjNode(dilithiumMontMulByConstant, TypeFunc::Parms));
+ set_result(retvalue);
+ return true;
+}
+
+
+//------------------------------inline_dilithiumDecomposePoly
+bool LibraryCallKit::inline_dilithiumDecomposePoly() {
+ address stubAddr;
+ const char *stubName;
+ assert(UseDilithiumIntrinsics, "need Dilithium intrinsics support");
+ assert(callee()->signature()->size() == 5, "dilithiumDecomposePoly has 5 parameters");
+
+ stubAddr = StubRoutines::dilithiumDecomposePoly();
+ stubName = "dilithiumDecomposePoly";
+ if (!stubAddr) return false;
+
+ Node* input = argument(0);
+ Node* lowPart = argument(1);
+ Node* highPart = argument(2);
+ Node* twoGamma2 = argument(3);
+ Node* multiplier = argument(4);
+
+ input = must_be_not_null(input, true);
+ lowPart = must_be_not_null(lowPart, true);
+ highPart = must_be_not_null(highPart, true);
+
+ Node* input_start = array_element_address(input, intcon(0), T_INT);
+ assert(input_start, "input is null");
+ Node* lowPart_start = array_element_address(lowPart, intcon(0), T_INT);
+ assert(lowPart_start, "lowPart is null");
+ Node* highPart_start = array_element_address(highPart, intcon(0), T_INT);
+ assert(highPart_start, "highPart is null");
+
+ Node* dilithiumDecomposePoly = make_runtime_call(RC_LEAF|RC_NO_FP,
+ OptoRuntime::dilithiumDecomposePoly_Type(),
+ stubAddr, stubName, TypePtr::BOTTOM,
+ input_start, lowPart_start, highPart_start,
+ twoGamma2, multiplier);
+
+ // return an int
+ Node* retvalue = _gvn.transform(new ProjNode(dilithiumDecomposePoly, TypeFunc::Parms));
+ set_result(retvalue);
+ return true;
+}
+
bool LibraryCallKit::inline_base64_encodeBlock() {
address stubAddr;
const char *stubName;
@@ -7430,7 +7612,7 @@ bool LibraryCallKit::inline_digestBase_implCompress(vmIntrinsics::ID id) {
break;
case vmIntrinsics::_sha3_implCompress:
assert(UseSHA3Intrinsics, "need SHA3 instruction support");
- state = get_state_from_digest_object(digestBase_obj, T_BYTE);
+ state = get_state_from_digest_object(digestBase_obj, T_LONG);
stubAddr = StubRoutines::sha3_implCompress();
stubName = "sha3_implCompress";
block_size = get_block_size_from_digest_object(digestBase_obj);
@@ -7460,6 +7642,38 @@ bool LibraryCallKit::inline_digestBase_implCompress(vmIntrinsics::ID id) {
return true;
}
+//------------------------------inline_double_keccak
+bool LibraryCallKit::inline_double_keccak() {
+ address stubAddr;
+ const char *stubName;
+ assert(UseSHA3Intrinsics, "need SHA3 intrinsics support");
+ assert(callee()->signature()->size() == 2, "double_keccak has 2 parameters");
+
+ stubAddr = StubRoutines::double_keccak();
+ stubName = "double_keccak";
+ if (!stubAddr) return false;
+
+ Node* status0 = argument(0);
+ Node* status1 = argument(1);
+
+ status0 = must_be_not_null(status0, true);
+ status1 = must_be_not_null(status1, true);
+
+ Node* status0_start = array_element_address(status0, intcon(0), T_LONG);
+ assert(status0_start, "status0 is null");
+ Node* status1_start = array_element_address(status1, intcon(0), T_LONG);
+ assert(status1_start, "status1 is null");
+ Node* double_keccak = make_runtime_call(RC_LEAF|RC_NO_FP,
+ OptoRuntime::double_keccak_Type(),
+ stubAddr, stubName, TypePtr::BOTTOM,
+ status0_start, status1_start);
+ // return an int
+ Node* retvalue = _gvn.transform(new ProjNode(double_keccak, TypeFunc::Parms));
+ set_result(retvalue);
+ return true;
+}
+
+
//------------------------------inline_digestBase_implCompressMB-----------------------
//
// Calculate MD5/SHA/SHA2/SHA5/SHA3 for multi-block byte[] array.
@@ -7530,7 +7744,7 @@ bool LibraryCallKit::inline_digestBase_implCompressMB(int predicate) {
klass_digestBase_name = "sun/security/provider/SHA3";
stub_name = "sha3_implCompressMB";
stub_addr = StubRoutines::sha3_implCompressMB();
- elem_type = T_BYTE;
+ elem_type = T_LONG;
}
break;
default:
diff --git a/src/hotspot/share/opto/library_call.hpp b/src/hotspot/share/opto/library_call.hpp
index f714625a4df4..5449d2f6d590 100644
--- a/src/hotspot/share/opto/library_call.hpp
+++ b/src/hotspot/share/opto/library_call.hpp
@@ -288,7 +288,7 @@ class LibraryCallKit : public GraphKit {
bool inline_bitshuffle_methods(vmIntrinsics::ID id);
bool inline_compare_unsigned(vmIntrinsics::ID id);
bool inline_divmod_methods(vmIntrinsics::ID id);
- bool inline_reference_get();
+ bool inline_reference_get0();
bool inline_reference_refersTo0(bool is_phantom);
bool inline_Class_cast();
bool inline_aescrypt_Block(vmIntrinsics::ID id);
@@ -301,10 +301,16 @@ class LibraryCallKit : public GraphKit {
Node* get_key_start_from_aescrypt_object(Node* aescrypt_object);
bool inline_ghash_processBlocks();
bool inline_chacha20Block();
+ bool inline_dilithiumAlmostNtt();
+ bool inline_dilithiumAlmostInverseNtt();
+ bool inline_dilithiumNttMult();
+ bool inline_dilithiumMontMulByConstant();
+ bool inline_dilithiumDecomposePoly();
bool inline_base64_encodeBlock();
bool inline_base64_decodeBlock();
bool inline_poly1305_processBlocks();
bool inline_digestBase_implCompress(vmIntrinsics::ID id);
+ bool inline_double_keccak();
bool inline_digestBase_implCompressMB(int predicate);
bool inline_digestBase_implCompressMB(Node* digestBaseObj, ciInstanceKlass* instklass,
BasicType elem_type, address stubAddr, const char *stubName,
diff --git a/src/hotspot/share/opto/runtime.cpp b/src/hotspot/share/opto/runtime.cpp
index bb79da326224..66e7e28db99c 100644
--- a/src/hotspot/share/opto/runtime.cpp
+++ b/src/hotspot/share/opto/runtime.cpp
@@ -1081,6 +1081,25 @@ const TypeFunc* OptoRuntime::digestBase_implCompressMB_Type(bool is_sha3) {
return TypeFunc::make(domain, range);
}
+// SHAKE128Parallel doubleKeccak function
+const TypeFunc* OptoRuntime::double_keccak_Type() {
+ int argcnt = 2;
+
+ const Type** fields = TypeTuple::fields(argcnt);
+ int argp = TypeFunc::Parms;
+ fields[argp++] = TypePtr::NOTNULL; // status0
+ fields[argp++] = TypePtr::NOTNULL; // status1
+
+ assert(argp == TypeFunc::Parms + argcnt, "correct decoding");
+ const TypeTuple* domain = TypeTuple::make(TypeFunc::Parms + argcnt, fields);
+
+ // result type needed
+ fields = TypeTuple::fields(1);
+ fields[TypeFunc::Parms + 0] = TypeInt::INT;
+ const TypeTuple* range = TypeTuple::make(TypeFunc::Parms + 1, fields);
+ return TypeFunc::make(domain, range);
+}
+
const TypeFunc* OptoRuntime::multiplyToLen_Type() {
// create input type (domain)
int num_args = 6;
@@ -1270,6 +1289,105 @@ const TypeFunc* OptoRuntime::chacha20Block_Type() {
return TypeFunc::make(domain, range);
}
+// Dilithium NTT function except for the final "normalization" to |coeff| < Q
+const TypeFunc* OptoRuntime::dilithiumAlmostNtt_Type() {
+ int argcnt = 2;
+
+ const Type** fields = TypeTuple::fields(argcnt);
+ int argp = TypeFunc::Parms;
+ fields[argp++] = TypePtr::NOTNULL; // coeffs
+ fields[argp++] = TypePtr::NOTNULL; // NTT zetas
+
+ assert(argp == TypeFunc::Parms + argcnt, "correct decoding");
+ const TypeTuple* domain = TypeTuple::make(TypeFunc::Parms + argcnt, fields);
+
+ // result type needed
+ fields = TypeTuple::fields(1);
+ fields[TypeFunc::Parms + 0] = TypeInt::INT;
+ const TypeTuple* range = TypeTuple::make(TypeFunc::Parms + 1, fields);
+ return TypeFunc::make(domain, range);
+}
+
+// Dilithium inverse NTT function except the final mod Q division by 2^256
+const TypeFunc* OptoRuntime::dilithiumAlmostInverseNtt_Type() {
+ int argcnt = 2;
+
+ const Type** fields = TypeTuple::fields(argcnt);
+ int argp = TypeFunc::Parms;
+ fields[argp++] = TypePtr::NOTNULL; // coeffs
+ fields[argp++] = TypePtr::NOTNULL; // inverse NTT zetas
+
+ assert(argp == TypeFunc::Parms + argcnt, "correct decoding");
+ const TypeTuple* domain = TypeTuple::make(TypeFunc::Parms + argcnt, fields);
+
+ // result type needed
+ fields = TypeTuple::fields(1);
+ fields[TypeFunc::Parms + 0] = TypeInt::INT;
+ const TypeTuple* range = TypeTuple::make(TypeFunc::Parms + 1, fields);
+ return TypeFunc::make(domain, range);
+}
+
+// Dilithium NTT multiply function
+const TypeFunc* OptoRuntime::dilithiumNttMult_Type() {
+ int argcnt = 3;
+
+ const Type** fields = TypeTuple::fields(argcnt);
+ int argp = TypeFunc::Parms;
+ fields[argp++] = TypePtr::NOTNULL; // result
+ fields[argp++] = TypePtr::NOTNULL; // ntta
+ fields[argp++] = TypePtr::NOTNULL; // nttb
+
+ assert(argp == TypeFunc::Parms + argcnt, "correct decoding");
+ const TypeTuple* domain = TypeTuple::make(TypeFunc::Parms + argcnt, fields);
+
+ // result type needed
+ fields = TypeTuple::fields(1);
+ fields[TypeFunc::Parms + 0] = TypeInt::INT;
+ const TypeTuple* range = TypeTuple::make(TypeFunc::Parms + 1, fields);
+ return TypeFunc::make(domain, range);
+}
+
+// Dilithium Montgomery multiply a polynome coefficient array by a constant
+const TypeFunc* OptoRuntime::dilithiumMontMulByConstant_Type() {
+ int argcnt = 2;
+
+ const Type** fields = TypeTuple::fields(argcnt);
+ int argp = TypeFunc::Parms;
+ fields[argp++] = TypePtr::NOTNULL; // coeffs
+ fields[argp++] = TypeInt::INT; // constant multiplier
+
+ assert(argp == TypeFunc::Parms + argcnt, "correct decoding");
+ const TypeTuple* domain = TypeTuple::make(TypeFunc::Parms + argcnt, fields);
+
+ // result type needed
+ fields = TypeTuple::fields(1);
+ fields[TypeFunc::Parms + 0] = TypeInt::INT;
+ const TypeTuple* range = TypeTuple::make(TypeFunc::Parms + 1, fields);
+ return TypeFunc::make(domain, range);
+}
+
+// Dilithium decompose polynomial
+const TypeFunc* OptoRuntime::dilithiumDecomposePoly_Type() {
+ int argcnt = 5;
+
+ const Type** fields = TypeTuple::fields(argcnt);
+ int argp = TypeFunc::Parms;
+ fields[argp++] = TypePtr::NOTNULL; // input
+ fields[argp++] = TypePtr::NOTNULL; // lowPart
+ fields[argp++] = TypePtr::NOTNULL; // highPart
+ fields[argp++] = TypeInt::INT; // 2 * gamma2
+ fields[argp++] = TypeInt::INT; // multiplier
+
+ assert(argp == TypeFunc::Parms + argcnt, "correct decoding");
+ const TypeTuple* domain = TypeTuple::make(TypeFunc::Parms + argcnt, fields);
+
+ // result type needed
+ fields = TypeTuple::fields(1);
+ fields[TypeFunc::Parms + 0] = TypeInt::INT;
+ const TypeTuple* range = TypeTuple::make(TypeFunc::Parms + 1, fields);
+ return TypeFunc::make(domain, range);
+}
+
// Base64 encode function
const TypeFunc* OptoRuntime::base64_encodeBlock_Type() {
int argcnt = 6;
@@ -1291,6 +1409,7 @@ const TypeFunc* OptoRuntime::base64_encodeBlock_Type() {
const TypeTuple* range = TypeTuple::make(TypeFunc::Parms, fields);
return TypeFunc::make(domain, range);
}
+
// Base64 decode function
const TypeFunc* OptoRuntime::base64_decodeBlock_Type() {
int argcnt = 7;
diff --git a/src/hotspot/share/opto/runtime.hpp b/src/hotspot/share/opto/runtime.hpp
index cd13c14148d7..c00f9228716b 100644
--- a/src/hotspot/share/opto/runtime.hpp
+++ b/src/hotspot/share/opto/runtime.hpp
@@ -275,6 +275,7 @@ class OptoRuntime : public AllStatic {
static const TypeFunc* galoisCounterMode_aescrypt_Type();
static const TypeFunc* digestBase_implCompress_Type(bool is_sha3);
+ static const TypeFunc* double_keccak_Type();
static const TypeFunc* digestBase_implCompressMB_Type(bool is_sha3);
static const TypeFunc* multiplyToLen_Type();
@@ -291,6 +292,11 @@ class OptoRuntime : public AllStatic {
static const TypeFunc* ghash_processBlocks_Type();
static const TypeFunc* chacha20Block_Type();
+ static const TypeFunc* dilithiumAlmostNtt_Type();
+ static const TypeFunc* dilithiumAlmostInverseNtt_Type();
+ static const TypeFunc* dilithiumNttMult_Type();
+ static const TypeFunc* dilithiumMontMulByConstant_Type();
+ static const TypeFunc* dilithiumDecomposePoly_Type();
static const TypeFunc* base64_encodeBlock_Type();
static const TypeFunc* base64_decodeBlock_Type();
static const TypeFunc* poly1305_processBlocks_Type();
diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp
index 7f0eddbda271..cb293051166d 100644
--- a/src/hotspot/share/prims/jvm.cpp
+++ b/src/hotspot/share/prims/jvm.cpp
@@ -3257,9 +3257,17 @@ JVM_ENTRY(void, JVM_WaitForReferencePendingList(JNIEnv* env))
}
JVM_END
+JVM_ENTRY(jobject, JVM_ReferenceGet(JNIEnv* env, jobject ref))
+ oop ref_oop = JNIHandles::resolve_non_null(ref);
+ // PhantomReference has its own implementation of get().
+ assert(!java_lang_ref_Reference::is_phantom(ref_oop), "precondition");
+ oop referent = java_lang_ref_Reference::weak_referent(ref_oop);
+ return JNIHandles::make_local(THREAD, referent);
+JVM_END
+
JVM_ENTRY(jboolean, JVM_ReferenceRefersTo(JNIEnv* env, jobject ref, jobject o))
oop ref_oop = JNIHandles::resolve_non_null(ref);
- // PhantomReference has it's own implementation of refersTo().
+ // PhantomReference has its own implementation of refersTo().
// See: JVM_PhantomReferenceRefersTo
assert(!java_lang_ref_Reference::is_phantom(ref_oop), "precondition");
oop referent = java_lang_ref_Reference::weak_referent_no_keepalive(ref_oop);
diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp
index bcd6d0291d54..a56d12b6c79b 100644
--- a/src/hotspot/share/runtime/globals.hpp
+++ b/src/hotspot/share/runtime/globals.hpp
@@ -332,6 +332,9 @@ const int ObjectAlignmentInBytes = 8;
product(bool, UseChaCha20Intrinsics, false, DIAGNOSTIC, \
"Use intrinsics for the vectorized version of ChaCha20") \
\
+ product(bool, UseDilithiumIntrinsics, false, DIAGNOSTIC, \
+ "Use intrinsics for the vectorized version of Dilithium") \
+ \
product(bool, UseMD5Intrinsics, false, DIAGNOSTIC, \
"Use intrinsics for MD5 crypto hash function") \
\
diff --git a/src/hotspot/share/runtime/os.cpp b/src/hotspot/share/runtime/os.cpp
index 77f6ef463bf3..2a64bc6fdd28 100644
--- a/src/hotspot/share/runtime/os.cpp
+++ b/src/hotspot/share/runtime/os.cpp
@@ -198,9 +198,9 @@ char* os::iso8601_time(jlong milliseconds_since_19700101, char* buffer, size_t b
abs_local_to_UTC = -(abs_local_to_UTC);
}
// Convert time zone offset seconds to hours and minutes.
- const time_t zone_hours = (abs_local_to_UTC / seconds_per_hour);
- const time_t zone_min =
- ((abs_local_to_UTC % seconds_per_hour) / seconds_per_minute);
+ const int zone_hours = static_cast(abs_local_to_UTC / seconds_per_hour);
+ const int zone_min =
+ static_cast((abs_local_to_UTC % seconds_per_hour) / seconds_per_minute);
// Print an ISO 8601 date and time stamp into the buffer
const int year = 1900 + time_struct.tm_year;
diff --git a/src/hotspot/share/runtime/stubRoutines.cpp b/src/hotspot/share/runtime/stubRoutines.cpp
index c5ef1c771949..4b80a34d3f4b 100644
--- a/src/hotspot/share/runtime/stubRoutines.cpp
+++ b/src/hotspot/share/runtime/stubRoutines.cpp
@@ -127,6 +127,11 @@ address StubRoutines::_counterMode_AESCrypt = nullptr;
address StubRoutines::_galoisCounterMode_AESCrypt = nullptr;
address StubRoutines::_ghash_processBlocks = nullptr;
address StubRoutines::_chacha20Block = nullptr;
+address StubRoutines::_dilithiumAlmostNtt = nullptr;
+address StubRoutines::_dilithiumAlmostInverseNtt = nullptr;
+address StubRoutines::_dilithiumNttMult = nullptr;
+address StubRoutines::_dilithiumMontMulByConstant = nullptr;
+address StubRoutines::_dilithiumDecomposePoly = nullptr;
address StubRoutines::_base64_encodeBlock = nullptr;
address StubRoutines::_base64_decodeBlock = nullptr;
address StubRoutines::_poly1305_processBlocks = nullptr;
@@ -140,6 +145,7 @@ address StubRoutines::_sha256_implCompressMB = nullptr;
address StubRoutines::_sha512_implCompress = nullptr;
address StubRoutines::_sha512_implCompressMB = nullptr;
address StubRoutines::_sha3_implCompress = nullptr;
+address StubRoutines::_double_keccak = nullptr;
address StubRoutines::_sha3_implCompressMB = nullptr;
address StubRoutines::_updateBytesCRC32 = nullptr;
diff --git a/src/hotspot/share/runtime/stubRoutines.hpp b/src/hotspot/share/runtime/stubRoutines.hpp
index 96946643c087..014aba74fbb4 100644
--- a/src/hotspot/share/runtime/stubRoutines.hpp
+++ b/src/hotspot/share/runtime/stubRoutines.hpp
@@ -207,6 +207,11 @@ class StubRoutines: AllStatic {
static address _galoisCounterMode_AESCrypt;
static address _ghash_processBlocks;
static address _chacha20Block;
+ static address _dilithiumAlmostNtt;
+ static address _dilithiumAlmostInverseNtt;
+ static address _dilithiumNttMult;
+ static address _dilithiumMontMulByConstant;
+ static address _dilithiumDecomposePoly;
static address _base64_encodeBlock;
static address _base64_decodeBlock;
static address _poly1305_processBlocks;
@@ -220,6 +225,7 @@ class StubRoutines: AllStatic {
static address _sha512_implCompress;
static address _sha512_implCompressMB;
static address _sha3_implCompress;
+ static address _double_keccak;
static address _sha3_implCompressMB;
static address _updateBytesCRC32;
@@ -396,6 +402,11 @@ class StubRoutines: AllStatic {
static address counterMode_AESCrypt() { return _counterMode_AESCrypt; }
static address ghash_processBlocks() { return _ghash_processBlocks; }
static address chacha20Block() { return _chacha20Block; }
+ static address dilithiumAlmostNtt() { return _dilithiumAlmostNtt; }
+ static address dilithiumAlmostInverseNtt() { return _dilithiumAlmostInverseNtt; }
+ static address dilithiumNttMult() { return _dilithiumNttMult; }
+ static address dilithiumMontMulByConstant() { return _dilithiumMontMulByConstant; }
+ static address dilithiumDecomposePoly() { return _dilithiumDecomposePoly; }
static address base64_encodeBlock() { return _base64_encodeBlock; }
static address base64_decodeBlock() { return _base64_decodeBlock; }
static address md5_implCompress() { return _md5_implCompress; }
@@ -407,6 +418,7 @@ class StubRoutines: AllStatic {
static address sha512_implCompress() { return _sha512_implCompress; }
static address sha512_implCompressMB() { return _sha512_implCompressMB; }
static address sha3_implCompress() { return _sha3_implCompress; }
+ static address double_keccak() { return _double_keccak; }
static address sha3_implCompressMB() { return _sha3_implCompressMB; }
static address updateBytesCRC32() { return _updateBytesCRC32; }
diff --git a/src/hotspot/share/runtime/threads.cpp b/src/hotspot/share/runtime/threads.cpp
index 90514b737bcc..1fbe6683f719 100644
--- a/src/hotspot/share/runtime/threads.cpp
+++ b/src/hotspot/share/runtime/threads.cpp
@@ -1306,10 +1306,24 @@ void Threads::print_on(outputStream* st, bool print_stacks,
char buf[32];
st->print_raw_cr(os::local_time_string(buf, sizeof(buf)));
- st->print_cr("Full thread dump %s (%s %s):",
+ st->print_cr("Full thread dump %s (%s %s)",
VM_Version::vm_name(),
VM_Version::vm_release(),
VM_Version::vm_info_string());
+ JDK_Version::current().to_string(buf, sizeof(buf));
+ const char* runtime_name = JDK_Version::runtime_name() != nullptr ?
+ JDK_Version::runtime_name() : "";
+ const char* runtime_version = JDK_Version::runtime_version() != nullptr ?
+ JDK_Version::runtime_version() : "";
+ const char* vendor_version = JDK_Version::runtime_vendor_version() != nullptr ?
+ JDK_Version::runtime_vendor_version() : "";
+ const char* jdk_debug_level = VM_Version::printable_jdk_debug_level() != nullptr ?
+ VM_Version::printable_jdk_debug_level() : "";
+
+ st->print_cr(" JDK version: %s%s%s (%s) (%sbuild %s)", runtime_name,
+ (*vendor_version != '\0') ? " " : "", vendor_version,
+ buf, jdk_debug_level, runtime_version);
+
st->cr();
#if INCLUDE_SERVICES
diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp
index 6c57c7998e7f..9d8034561647 100644
--- a/src/hotspot/share/runtime/vmStructs.cpp
+++ b/src/hotspot/share/runtime/vmStructs.cpp
@@ -540,6 +540,11 @@
static_field(StubRoutines, _galoisCounterMode_AESCrypt, address) \
static_field(StubRoutines, _ghash_processBlocks, address) \
static_field(StubRoutines, _chacha20Block, address) \
+ static_field(StubRoutines, _dilithiumAlmostNtt, address) \
+ static_field(StubRoutines, _dilithiumAlmostInverseNtt, address) \
+ static_field(StubRoutines, _dilithiumNttMult, address) \
+ static_field(StubRoutines, _dilithiumMontMulByConstant, address) \
+ static_field(StubRoutines, _dilithiumDecomposePoly, address) \
static_field(StubRoutines, _base64_encodeBlock, address) \
static_field(StubRoutines, _base64_decodeBlock, address) \
static_field(StubRoutines, _poly1305_processBlocks, address) \
@@ -548,6 +553,7 @@
static_field(StubRoutines, _crc32c_table_addr, address) \
static_field(StubRoutines, _updateBytesCRC32C, address) \
static_field(StubRoutines, _updateBytesAdler32, address) \
+ static_field(StubRoutines, _double_keccak, address) \
static_field(StubRoutines, _multiplyToLen, address) \
static_field(StubRoutines, _squareToLen, address) \
static_field(StubRoutines, _bigIntegerRightShiftWorker, address) \
diff --git a/src/java.base/linux/native/libnio/ch/FileDispatcherImpl.c b/src/java.base/linux/native/libnio/ch/FileDispatcherImpl.c
index 207e61431dc9..7c3761a613c3 100644
--- a/src/java.base/linux/native/libnio/ch/FileDispatcherImpl.c
+++ b/src/java.base/linux/native/libnio/ch/FileDispatcherImpl.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2026, 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
@@ -63,7 +63,7 @@ Java_sun_nio_ch_FileDispatcherImpl_transferFrom0(JNIEnv *env, jobject this,
if (n < 0) {
if (errno == EAGAIN)
return IOS_UNAVAILABLE;
- if (errno == ENOSYS)
+ if (errno == ENOSYS || errno == EOPNOTSUPP)
return IOS_UNSUPPORTED_CASE;
if ((errno == EBADF || errno == EINVAL || errno == EXDEV) &&
((ssize_t)count >= 0))
@@ -103,6 +103,7 @@ Java_sun_nio_ch_FileDispatcherImpl_transferTo0(JNIEnv *env, jobject this,
case EINVAL:
case ENOSYS:
case EXDEV:
+ case EOPNOTSUPP:
// ignore and try sendfile()
break;
default:
diff --git a/src/java.base/linux/native/libnio/fs/LinuxNativeDispatcher.c b/src/java.base/linux/native/libnio/fs/LinuxNativeDispatcher.c
index cf8592e1ced5..5f14896ad24c 100644
--- a/src/java.base/linux/native/libnio/fs/LinuxNativeDispatcher.c
+++ b/src/java.base/linux/native/libnio/fs/LinuxNativeDispatcher.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2026, 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
@@ -199,6 +199,7 @@ Java_sun_nio_fs_LinuxNativeDispatcher_directCopy0
case EINVAL:
case ENOSYS:
case EXDEV:
+ case EOPNOTSUPP:
// ignore and try sendfile()
break;
default:
diff --git a/src/java.base/share/classes/com/sun/crypto/provider/ML_KEM.java b/src/java.base/share/classes/com/sun/crypto/provider/ML_KEM.java
new file mode 100644
index 000000000000..9808a0133032
--- /dev/null
+++ b/src/java.base/share/classes/com/sun/crypto/provider/ML_KEM.java
@@ -0,0 +1,1183 @@
+/*
+ * Copyright (c) 2024, 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 com.sun.crypto.provider;
+
+import java.security.*;
+import java.util.Arrays;
+import javax.crypto.DecapsulateException;
+
+import sun.security.provider.SHA3.SHAKE256;
+import sun.security.provider.SHA3Parallel.Shake128Parallel;
+
+public final class ML_KEM {
+
+ public static final int SECRET_SIZE = 32;
+ private static final String HASH_H_NAME = "SHA3-256";
+ private static final String HASH_G_NAME = "SHA3-512";
+
+ private static final int ML_KEM_Q = 3329;
+ private static final int ML_KEM_N = 256;
+
+ private static final int XOF_BLOCK_LEN = 168; // the block length for SHAKE128
+ private static final int XOF_PAD = 24;
+ private static final int MONT_R_BITS = 20;
+ private static final int MONT_Q = 3329;
+ private static final int MONT_R_SQUARE_MOD_Q = 152;
+ private static final int MONT_Q_INV_MOD_R = 586497;
+
+ // toMont((ML_KEM_N / 2)^-1 mod ML_KEM_Q) using R = 2^MONT_R_BITS
+ private static final int MONT_DIM_HALF_INVERSE = 1534;
+ private static final int BARRETT_MULTIPLIER = 20159;
+ private static final int BARRETT_SHIFT = 26;
+ private static final int[] MONT_ZETAS_FOR_NTT = new int[]{
+ 1188, 914, -969, 585, -551, 1263, -97, 593,
+ -35, -1400, -417, -1253, 742, -281, 185, -819,
+ -1226, 895, -530, 52, 25, 1000, 1249, -909,
+ -373, -1604, -259, -1369, -82, 49, 1496, -406,
+ 445, 1155, -405, -714, 553, -1183, -1401, 1598,
+ -128, 1538, -669, 744, 1382, -1313, 201, -332,
+ -1440, -1007, -36, -1617, 567, -623, 1429, 290,
+ -1269, -825, -1613, 510, -395, 845, -426, -1003,
+ 222, -1107, 172, -42, 620, 1497, -1649, 94,
+ -595, -497, -431, -1327, -702, -1448, -184, -607,
+ -868, -1430, 977, 884, 425, 355, 1259, 1192,
+ 317, -636, -1074, 30, -1394, 833, -1200, -244,
+ 907, -339, -227, 1178, -586, -137, -514, 534,
+ 1153, -486, -1386, -668, 191, 982, 88, 1014,
+ -1177, -474, -612, -857, -348, -604, 990, 1601,
+ -1599, -709, -789, -1317, -57, 1049, -584
+ };
+
+ private static final int[] MONT_ZETAS_FOR_NTT_MULT = new int[]{
+ -1003, 1003, 222, -222, -1107, 1107, 172, -172,
+ -42, 42, 620, -620, 1497, -1497, -1649, 1649,
+ 94, -94, -595, 595, -497, 497, -431, 431,
+ -1327, 1327, -702, 702, -1448, 1448, -184, 184,
+ -607, 607, -868, 868, -1430, 1430, 977, -977,
+ 884, -884, 425, -425, 355, -355, 1259, -1259,
+ 1192, -1192, 317, -317, -636, 636, -1074, 1074,
+ 30, -30, -1394, 1394, 833, -833, -1200, 1200,
+ -244, 244, 907, -907, -339, 339, -227, 227,
+ 1178, -1178, -586, 586, -137, 137, -514, 514,
+ 534, -534, 1153, -1153, -486, 486, -1386, 1386,
+ -668, 668, 191, -191, 982, -982, 88, -88,
+ 1014, -1014, -1177, 1177, -474, 474, -612, 612,
+ -857, 857, -348, 348, -604, 604, 990, -990,
+ 1601, -1601, -1599, 1599, -709, 709, -789, 789,
+ -1317, 1317, -57, 57, 1049, -1049, -584, 584
+ };
+
+ private final int mlKem_k;
+ private final int mlKem_eta1;
+ private final int mlKem_eta2;
+
+ private final int mlKem_du;
+ private final int mlKem_dv;
+ private final int encapsulationSize;
+
+ public ML_KEM(String name) {
+ switch (name) {
+ case "ML-KEM-512" -> {
+ mlKem_k = 2;
+ mlKem_eta1 = 3;
+ mlKem_eta2 = 2;
+ mlKem_du = 10;
+ mlKem_dv = 4;
+ }
+ case "ML-KEM-768" -> {
+ mlKem_k = 3;
+ mlKem_eta1 = 2;
+ mlKem_eta2 = 2;
+ mlKem_du = 10;
+ mlKem_dv = 4;
+ }
+ case "ML-KEM-1024" -> {
+ mlKem_k = 4;
+ mlKem_eta1 = 2;
+ mlKem_eta2 = 2;
+ mlKem_du = 11;
+ mlKem_dv = 5;
+ }
+ default -> throw new IllegalArgumentException(
+ // This should never happen.
+ "Invalid algorithm name (" + name + ").");
+ }
+ encapsulationSize = (mlKem_k * mlKem_du + mlKem_dv) * 32;
+ }
+
+ /*
+ Classes for the internal K_PKE scheme
+ */
+ private record K_PKE_EncryptionKey(byte[] keyBytes) {}
+
+ private record K_PKE_DecryptionKey(byte[] keyBytes) {}
+
+ private record K_PKE_KeyPair(
+ K_PKE_EncryptionKey publicKey, K_PKE_DecryptionKey privateKey) {
+ }
+
+ protected record K_PKE_CipherText(byte[] encryptedBytes) {
+ }
+
+ private boolean isValidCipherText(K_PKE_CipherText cipherText) {
+ return (cipherText.encryptedBytes.length == encapsulationSize);
+ }
+
+ /*
+ Classes for internal KEM scheme
+ */
+ protected record ML_KEM_EncapsulationKey(byte[] keyBytes) {
+ }
+
+ protected record ML_KEM_DecapsulationKey(byte[] keyBytes) {
+ }
+
+ protected record ML_KEM_KeyPair(
+ ML_KEM_EncapsulationKey encapsulationKey,
+ ML_KEM_DecapsulationKey decapsulationKey) {
+ }
+
+ protected record ML_KEM_EncapsulateResult(
+ K_PKE_CipherText cipherText, byte[] sharedSecret) {
+ }
+
+ protected int getEncapsulationSize() {
+ return encapsulationSize;
+ }
+
+ // Encapsulation key checks from section 7.2 of spec
+ protected Object checkPublicKey(byte[] pk) throws InvalidKeyException {
+ //Encapsulation key type check
+ if (pk.length != mlKem_k * 384 + 32) {
+ throw new InvalidKeyException("Public key is not the correct size");
+ }
+
+ //Encapsulation key modulus check
+ int x, y, z, a, b;
+ for (int i = 0; i < mlKem_k * 384; i += 3) {
+ x = pk[i] & 0xFF;
+ y = pk[i + 1] & 0xFF;
+ z = pk[i + 2] & 0xFF;
+ a = x + ((y & 0xF) << 8);
+ b = (y >> 4) + (z << 4);
+ if ((a >= ML_KEM_Q) || (b >= ML_KEM_Q)) {
+ throw new InvalidKeyException(
+ "Coefficients in public key not in specified range");
+ }
+ }
+ return null;
+ }
+
+ // Decapsulation key checks from Section 7.3 of spec
+ protected Object checkPrivateKey(byte[] sk) throws InvalidKeyException {
+ MessageDigest mlKemH;
+ try {
+ mlKemH = MessageDigest.getInstance(HASH_H_NAME);
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException(e);
+ }
+
+ //Decapsulation key type check
+ if (sk.length != mlKem_k * 768 + 96) {
+ throw new InvalidKeyException("Private key is not the correct size");
+ }
+
+ //Decapsulation hash check
+ mlKemH.update(sk, mlKem_k * 384, mlKem_k * 384 + 32);
+ byte[] check = Arrays.copyOfRange(sk, mlKem_k * 768 + 32, mlKem_k * 768 + 64);
+ if (!MessageDigest.isEqual(mlKemH.digest(), check)) {
+ throw new InvalidKeyException("Private key hash check failed");
+ }
+ return null;
+ }
+
+ /*
+ Main internal algorithms from Section 6 of specification
+ */
+ protected ML_KEM_KeyPair generateKemKeyPair(byte[] kem_d, byte[] kem_z) {
+ MessageDigest mlKemH;
+ try {
+ mlKemH = MessageDigest.getInstance(HASH_H_NAME);
+ } catch (NoSuchAlgorithmException e) {
+ // This should never happen.
+ throw new RuntimeException(e);
+ }
+
+ //Generate K-PKE keys
+ var kPkeKeyPair = generateK_PkeKeyPair(kem_d);
+ //encaps key = kPke encryption key
+ byte[] encapsKey = kPkeKeyPair.publicKey.keyBytes;
+
+ //Derive decapsulation key = kPkePrivatKey || encapsKey || H(encapsKey) || kem_Z
+ byte[] kPkePrivateKey = kPkeKeyPair.privateKey.keyBytes;
+ byte[] decapsKey = new byte[encapsKey.length + kPkePrivateKey.length + 64];
+ System.arraycopy(kPkePrivateKey, 0, decapsKey, 0, kPkePrivateKey.length);
+ Arrays.fill(kPkePrivateKey, (byte)0);
+ System.arraycopy(encapsKey, 0, decapsKey,
+ kPkePrivateKey.length, encapsKey.length);
+
+ mlKemH.update(encapsKey);
+ try {
+ mlKemH.digest(decapsKey, kPkePrivateKey.length + encapsKey.length, 32);
+ } catch (DigestException e) {
+ // This should never happen.
+ throw new RuntimeException(e);
+ }
+ System.arraycopy(kem_z, 0, decapsKey,
+ kPkePrivateKey.length + encapsKey.length + 32, 32);
+
+ return new ML_KEM_KeyPair(
+ new ML_KEM_EncapsulationKey(encapsKey),
+ new ML_KEM_DecapsulationKey(decapsKey));
+ }
+
+ protected ML_KEM_EncapsulateResult encapsulate(
+ ML_KEM_EncapsulationKey encapsulationKey, byte[] randomMessage) {
+ MessageDigest mlKemH;
+ MessageDigest mlKemG;
+ try {
+ mlKemH = MessageDigest.getInstance(HASH_H_NAME);
+ mlKemG = MessageDigest.getInstance(HASH_G_NAME);
+ } catch (NoSuchAlgorithmException e){
+ // This should never happen.
+ throw new RuntimeException(e);
+ }
+
+ mlKemH.update(encapsulationKey.keyBytes);
+ mlKemG.update(randomMessage);
+ mlKemG.update(mlKemH.digest());
+ var kHatAndRandomCoins = mlKemG.digest();
+ var randomCoins = Arrays.copyOfRange(kHatAndRandomCoins, 32, 64);
+ var cipherText = kPkeEncrypt(new K_PKE_EncryptionKey(encapsulationKey.keyBytes),
+ randomMessage, randomCoins);
+ Arrays.fill(randomCoins, (byte) 0);
+ byte[] sharedSecret = Arrays.copyOfRange(kHatAndRandomCoins, 0, 32);
+ Arrays.fill(kHatAndRandomCoins, (byte) 0);
+
+ return new ML_KEM_EncapsulateResult(cipherText, sharedSecret);
+ }
+
+ protected byte[] decapsulate(ML_KEM_DecapsulationKey decapsulationKey,
+ K_PKE_CipherText cipherText)
+ throws DecapsulateException {
+
+ //Check ciphertext validity
+ if (!isValidCipherText(cipherText)) {
+ throw new DecapsulateException("Invalid ciphertext");
+ }
+
+ int encode12PolyLen = 12 * ML_KEM_N / 8;
+ var decapsKeyBytes = decapsulationKey.keyBytes;
+ MessageDigest mlKemG;
+ SHAKE256 mlKemJ;
+ try {
+ mlKemG = MessageDigest.getInstance(HASH_G_NAME);
+ mlKemJ = new SHAKE256(32);
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException(e);
+ }
+
+ byte[] kPkePrivateKeyBytes = new byte[mlKem_k * encode12PolyLen];
+ System.arraycopy(decapsKeyBytes, 0, kPkePrivateKeyBytes, 0,
+ kPkePrivateKeyBytes.length);
+
+ byte[] encapsKeyBytes = new byte[mlKem_k * encode12PolyLen + 32];
+ System.arraycopy(decapsKeyBytes, mlKem_k * encode12PolyLen,
+ encapsKeyBytes, 0, encapsKeyBytes.length);
+ var mCandidate = kPkeDecrypt(
+ new K_PKE_DecryptionKey(kPkePrivateKeyBytes), cipherText);
+ mlKemG.update(mCandidate);
+ mlKemG.update(decapsKeyBytes, decapsKeyBytes.length - 64, 32);
+ var kAndCoins = mlKemG.digest();
+ var realResult = Arrays.copyOfRange(kAndCoins, 0, 32);
+ var coins = Arrays.copyOfRange(kAndCoins, 32, 64);
+
+ // Zero out unused byte arrays containing sensitive data
+ Arrays.fill(kPkePrivateKeyBytes, (byte) 0);
+ Arrays.fill(kAndCoins, (byte) 0);
+
+ mlKemJ.update(decapsKeyBytes, decapsKeyBytes.length - 32, 32);
+ mlKemJ.update(cipherText.encryptedBytes);
+ var fakeResult = mlKemJ.digest();
+ var computedCipherText = kPkeEncrypt(
+ new K_PKE_EncryptionKey(encapsKeyBytes), mCandidate, coins);
+
+ // The rest of this method implements the following in constant time
+ //
+ // if (Arrays.equals(cipherText.encryptedBytes,
+ // computedCipherText.encryptedBytes)) {
+ // return realResult;
+ // } else {
+ // return fakeResult;
+ // }
+
+ int mask = 0;
+ byte[] origCiphertestBytes = cipherText.encryptedBytes;
+ byte[] compCipherTextBytes = computedCipherText.encryptedBytes;
+ for (int i = 0; i < cipherText.encryptedBytes.length; i++) {
+ mask |= (origCiphertestBytes[i] ^ compCipherTextBytes[i]);
+ }
+ mask = - (mask & 0xff); // sets mask to negative or 0
+ mask >>= 31; // sets mask to all 1-bits or all 0-bits
+ int notMask = ~mask;
+
+ byte[] result = realResult;
+ for (int i = 0; i < realResult.length; i ++) {
+ result[i] = (byte)((notMask & realResult[i]) | (mask & fakeResult[i]));
+ }
+
+ return result;
+ }
+
+ /*
+ K-PKE subroutines defined in Section 5 of spec
+ */
+ private K_PKE_KeyPair generateK_PkeKeyPair(byte[] seed) {
+
+ MessageDigest mlKemG;
+ SHAKE256 mlKemJ;
+ try {
+ mlKemG = MessageDigest.getInstance(HASH_G_NAME);
+ mlKemJ = new SHAKE256(64 * mlKem_eta1);
+ } catch (NoSuchAlgorithmException e) {
+ // This should never happen.
+ throw new RuntimeException(e);
+ }
+
+ mlKemG.update(seed);
+ mlKemG.update((byte)mlKem_k);
+
+ var rhoSigma = mlKemG.digest();
+ var rho = Arrays.copyOfRange(rhoSigma, 0, 32);
+ var sigma = Arrays.copyOfRange(rhoSigma, 32, 64);
+ Arrays.fill(rhoSigma, (byte)0);
+
+ var keyGenA = generateA(rho, false);
+
+ int keyGenN = 0;
+ byte[] prfSeed = new byte[sigma.length + 1];
+ System.arraycopy(sigma, 0, prfSeed, 0, sigma.length);
+ byte[] cbdInput;
+ short[][] keyGenS = new short[mlKem_k][];
+ short[][] keyGenE = new short[mlKem_k][];
+ for (int i = 0; i < mlKem_k; i++) {
+ prfSeed[sigma.length] = (byte) (keyGenN++);
+ mlKemJ.update(prfSeed);
+ cbdInput = mlKemJ.digest();
+ keyGenS[i] = centeredBinomialDistribution(mlKem_eta1, cbdInput);
+ }
+ for (int i = 0; i < mlKem_k; i++) {
+ prfSeed[sigma.length] = (byte) (keyGenN++);
+ mlKemJ.update(prfSeed);
+ cbdInput = mlKemJ.digest();
+ keyGenE[i] = centeredBinomialDistribution(mlKem_eta1, cbdInput);
+ }
+ Arrays.fill(sigma, (byte)0);
+
+ short[][] keyGenSHat = mlKemVectorNTT(keyGenS);
+ mlKemVectorReduce(keyGenSHat);
+ short[][] keyGenEHat = mlKemVectorNTT(keyGenE);
+
+ short[][] keyGenTHat =
+ mlKemMatrixVectorMuladd(keyGenA, keyGenSHat, keyGenEHat);
+
+ byte[] pkEncoded = new byte[(mlKem_k * ML_KEM_N * 12) / 8 + rho.length];
+ byte[] skEncoded = new byte[(mlKem_k * ML_KEM_N * 12) / 8];
+ for (int i = 0; i < mlKem_k; i++) {
+ encodePoly12(keyGenTHat[i], pkEncoded, i * ((ML_KEM_N * 12) / 8));
+ encodePoly12(keyGenSHat[i], skEncoded, i * ((ML_KEM_N * 12) / 8));
+ Arrays.fill(keyGenEHat[i], (short) 0);
+ Arrays.fill(keyGenSHat[i], (short) 0);
+ }
+ System.arraycopy(rho, 0,
+ pkEncoded, (mlKem_k * ML_KEM_N * 12) / 8, rho.length);
+
+ return new K_PKE_KeyPair(
+ new K_PKE_EncryptionKey(pkEncoded),
+ new K_PKE_DecryptionKey(skEncoded));
+ }
+
+ private K_PKE_CipherText kPkeEncrypt(
+ K_PKE_EncryptionKey publicKey, byte[] message, byte[] sigma) {
+ short[][] zeroes = new short[mlKem_k][ML_KEM_N];
+ byte[] pkBytes = publicKey.keyBytes;
+ byte[] rho = Arrays.copyOfRange(pkBytes,
+ pkBytes.length - 32, pkBytes.length);
+ byte[] tHatBytes = Arrays.copyOfRange(pkBytes,
+ 0, pkBytes.length - 32);
+ var encryptTHat = decodeVector(12, tHatBytes);
+ var encryptA = generateA(rho, true);
+ short[][] encryptR = new short[mlKem_k][];
+ short[][] encryptE1 = new short[mlKem_k][];
+ int encryptN = 0;
+ byte[] prfSeed = new byte[sigma.length + 1];
+ System.arraycopy(sigma, 0, prfSeed, 0, sigma.length);
+
+ var kPkePRFeta1 = new SHAKE256(64 * mlKem_eta1);
+ var kPkePRFeta2 = new SHAKE256(64 * mlKem_eta2);
+ for (int i = 0; i < mlKem_k; i++) {
+ prfSeed[sigma.length] = (byte) (encryptN++);
+ kPkePRFeta1.update(prfSeed);
+ byte[] cbdInput = kPkePRFeta1.digest();
+ encryptR[i] = centeredBinomialDistribution(mlKem_eta1, cbdInput);
+ }
+ for (int i = 0; i < mlKem_k; i++) {
+ prfSeed[sigma.length] = (byte) (encryptN++);
+ kPkePRFeta2.update(prfSeed);
+ byte[] cbdInput = kPkePRFeta2.digest();
+ encryptE1[i] = centeredBinomialDistribution(mlKem_eta2, cbdInput);
+ }
+ prfSeed[sigma.length] = (byte) encryptN;
+ kPkePRFeta2.reset();
+ kPkePRFeta2.update(prfSeed);
+ byte[] cbdInput = kPkePRFeta2.digest();
+ var encryptE2 = centeredBinomialDistribution(mlKem_eta2, cbdInput);
+
+ var encryptRHat = mlKemVectorNTT(encryptR);
+ var encryptUHat = mlKemMatrixVectorMuladd(encryptA, encryptRHat, zeroes);
+ var encryptU = mlKemVectorInverseNTT(encryptUHat);
+ encryptU = mlKemAddVec(encryptU, encryptE1);
+ var encryptVHat = mlKemVectorScalarMult(encryptTHat, encryptRHat);
+ var encryptV = mlKemInverseNTT(encryptVHat);
+ encryptV = mlKemAddPoly(encryptV, encryptE2, decompressDecode(message));
+ var encryptC1 = encodeVector(mlKem_du, compressVector10_11(encryptU, mlKem_du));
+ var encryptC2 = encodePoly(mlKem_dv, compressPoly4_5(encryptV, mlKem_dv));
+
+ byte[] result = new byte[encryptC1.length + encryptC2.length];
+ System.arraycopy(encryptC1, 0,
+ result, 0, encryptC1.length);
+ System.arraycopy(encryptC2, 0,
+ result, encryptC1.length, encryptC2.length);
+
+ return new K_PKE_CipherText(result);
+ }
+
+ private byte[] kPkeDecrypt(K_PKE_DecryptionKey privateKey,
+ K_PKE_CipherText cipherText) {
+ int uBytesLen = mlKem_k * mlKem_du * ML_KEM_N / 8;
+ byte[] uBytes = Arrays.copyOfRange(cipherText.encryptedBytes,
+ 0, uBytesLen);
+ byte[] vBytes = Arrays.copyOfRange(cipherText.encryptedBytes,
+ uBytesLen, cipherText.encryptedBytes.length);
+ var decryptU = decompressVector(decodeVector(mlKem_du, uBytes), mlKem_du);
+ var decryptV = decompressPoly(
+ decodePoly(mlKem_dv, vBytes, 0), mlKem_dv);
+ var decryptSHat = decodeVector(12, privateKey.keyBytes);
+ var decryptSU = mlKemInverseNTT(
+ mlKemVectorScalarMult(decryptSHat, mlKemVectorNTT(decryptU)));
+ for (int i = 0; i < mlKem_k; i++) {
+ Arrays.fill(decryptSHat[i], (short) 0);
+ }
+ decryptV = mlKemSubtractPoly(decryptV, decryptSU);
+ Arrays.fill(decryptSU, (short) 0);
+
+ return encodeCompress(decryptV);
+ }
+
+ /*
+ Sampling algorithms from Section 4.2.2 of the spec
+ */
+
+ //Combination of SampleNTT and KeyGen/Encrypt generation of A
+ private short[][][] generateA(byte[] rho, Boolean transposed) {
+ short[][][] a = new short[mlKem_k][mlKem_k][];
+
+ int nrPar = 2;
+ int rhoLen = rho.length;
+ byte[] seedBuf = new byte[XOF_BLOCK_LEN];
+ System.arraycopy(rho, 0, seedBuf, 0, rho.length);
+ seedBuf[rhoLen + 2] = 0x1F;
+ seedBuf[XOF_BLOCK_LEN - 1] = (byte)0x80;
+ byte[][] xofBufArr = new byte[nrPar][XOF_BLOCK_LEN + XOF_PAD];
+ int[] iIndex = new int[nrPar];
+ int[] jIndex = new int[nrPar];
+
+ short[] parsedBuf = new short[(xofBufArr[0].length / 3) * 2];
+
+ int parInd = 0;
+ boolean allDone;
+ int[] ofs = new int[nrPar];
+ Arrays.fill(ofs, 0);
+ short[][] aij = new short[nrPar][];
+ try {
+ Shake128Parallel parXof = new Shake128Parallel(xofBufArr);
+
+ for (int i = 0; i < mlKem_k; i++) {
+ for (int j = 0; j < mlKem_k; j++) {
+ xofBufArr[parInd] = seedBuf.clone();
+ if (transposed) {
+ xofBufArr[parInd][rhoLen] = (byte) i;
+ xofBufArr[parInd][rhoLen + 1] = (byte) j;
+ } else {
+ xofBufArr[parInd][rhoLen] = (byte) j;
+ xofBufArr[parInd][rhoLen + 1] = (byte) i;
+ }
+ iIndex[parInd] = i;
+ jIndex[parInd] = j;
+ ofs[parInd] = 0;
+ aij[parInd] = new short[ML_KEM_N];
+ parInd++;
+
+ if ((parInd == nrPar) ||
+ ((i == mlKem_k - 1) && (j == mlKem_k - 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_KEM_N) {
+ twelve2Sixteen(xofBufArr[k], 0,
+ parsedBuf, (XOF_BLOCK_LEN / 3) * 2);
+ }
+ while ((ofs[k] < ML_KEM_N) &&
+ (parsedOfs < (XOF_BLOCK_LEN / 3) * 2)) {
+ tmp = parsedBuf[parsedOfs++] & 0xFFFF;
+ if (tmp < ML_KEM_Q) {
+ aij[k][ofs[k]] = (short) tmp;
+ ofs[k]++;
+ }
+ tmp = parsedBuf[parsedOfs++] & 0xFFFF;
+ if ((ofs[k] < ML_KEM_N) && (tmp < ML_KEM_Q)) {
+ aij[k][ofs[k]] = (short) tmp;
+ ofs[k]++;
+ }
+ }
+ if (ofs[k] < ML_KEM_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 short[] centeredBinomialDistribution(int eta, byte[] input) {
+ if (eta == 2) return centeredBinomialDistribution2(input);
+ if (eta == 3) return centeredBinomialDistribution3(input);
+ // Below for arbitrary eta, not used in ML-KEM
+ short[] result = new short[ML_KEM_N];
+ int index = 0;
+ int shift = 8;
+ int currentByte = input[0];
+ for (int m = 0; m < ML_KEM_N; m++) {
+ int a = 0;
+ int b = 0;
+ for (int j = 0; j < eta; j++) {
+ if (shift == 8) {
+ currentByte = input[index++];
+ shift = 0;
+ }
+ a += (currentByte >> shift) & 1;
+ shift++;
+ }
+ for (int j = 0; j < eta; j++) {
+ if (shift == 8) {
+ currentByte = input[index++];
+ shift = 0;
+ }
+ b += (currentByte >> shift) & 1;
+ shift++;
+ }
+ result[m] = (short) (a - b);
+ }
+ return result;
+ }
+
+ private short[] centeredBinomialDistribution2(byte[] input) {
+ short[] result = new short[ML_KEM_N];
+ // A 64-bit number divided into 16 4-bits, representing all 4-bit
+ // patterns of input with values are CBD samples in [-2, 2].
+ long bits = 0x0112f001f001eff0L;
+ int j = 0;
+
+ for (int i = 0; i < input.length; i++) {
+ // One byte has 2 4-bit sequences, each producing a sample
+ int a = input[i];
+ int shift1 = (a << 2) & 0x3c;
+ int shift2 = (a >> 2) & 0x3c;
+ result[j++] = (short) ((bits << shift1) >> 60);
+ result[j++] = (short) ((bits << shift2) >> 60);
+ }
+
+ return result;
+ }
+
+ private short[] centeredBinomialDistribution3(byte[] input) {
+ short[] result = new short[ML_KEM_N];
+ // A 32-bit number divided into 8 4-bits, representing all 3-bits
+ // patterns (one half of a 6-bit input) with values in [0, 3].
+ int bits = 0x01121223;
+ int j = 0;
+
+ for (int i = 0; i < input.length; i += 3) {
+ // Every 3 bytes has 24 bits that produce 4 6-bit sequences.
+ // We calculate values for both halves of each sequence and
+ // do the subtraction to get the sample
+ int a1 = input[i];
+ int a2 = input[i + 1];
+ int a3 = input[i + 2];
+ int shift1 = (a1 << 2) & 0x1c;
+ int shift2 = (a1 >> 1) & 0x1c;
+ int shift3 = ((a1 >> 4) & 0x0c) | ((a2 << 4) & 0x10);
+ int shift4 = (a2 << 1) & 0x1c;
+ int shift5 = (a2 >> 2) & 0x1c;
+ int shift6 = ((a2 >> 5) & 0x04) | ((a3 << 3) & 0x18);
+ int shift7 = a3 & 0x1c;
+ int shift8 = (a3 >> 3) & 0x1c;
+ result[j++] = (short)
+ (((bits << shift1) >> 28) - ((bits << shift2) >> 28));
+ result[j++] = (short)
+ (((bits << shift3) >> 28) - ((bits << shift4) >> 28));
+ result[j++] = (short)
+ (((bits << shift5) >> 28) - ((bits << shift6) >> 28));
+ result[j++] = (short)
+ (((bits << shift7) >> 28) - ((bits << shift8) >> 28));
+ }
+
+ return result;
+ }
+
+ /*
+ NTT algorithms from Section 4.3 of the specification
+ */
+
+ // Works in place, it returns its (modified) input so that it can be used in
+ // expressions
+ private short[][] mlKemVectorNTT(short[][] vector) {
+ for (int i = 0; i < mlKem_k; i++) {
+ mlKemNTT(vector[i]);
+ }
+ return vector;
+ }
+
+ // Works in place, it returns its (modified) input so that it can be used in
+ // expressions
+ private short[][] mlKemVectorReduce(short[][] vector) {
+ for (int i = 0; i < mlKem_k; i++) {
+ mlKemBarrettReduce(vector[i]);
+ }
+ return vector;
+ }
+
+ // Works in place, it returns its (modified) input so that it can be used in
+ // expressions
+ private short[][] mlKemVectorInverseNTT(short[][] vector) {
+ for (int i = 0; i < mlKem_k; i++) {
+ vector[i] = mlKemInverseNTT(vector[i]);
+ }
+ return vector;
+ }
+
+ // The elements of poly should be in the range [-ML_KEM_Q, ML_KEM_Q]
+ // The elements of poly at return will be in the range of [0, ML_KEM_Q]
+ private void mlKemNTT(short[] poly) {
+ int[] coeffs = new int[ML_KEM_N];
+ for (int m = 0; m < ML_KEM_N; m++) {
+ coeffs[m] = poly[m];
+ }
+ seilerNTT(coeffs);
+ for (int m = 0; m < ML_KEM_N; m++) {
+ poly[m] = (short) coeffs[m];
+ }
+ mlKemBarrettReduce(poly);
+ }
+
+ // Works in place, but also returns its (modified) input so that it can
+ // be used in expressions
+ private short[] mlKemInverseNTT(short[] poly) {
+ int[] coeffs = new int[ML_KEM_N];
+ for (int m = 0; m < ML_KEM_N; m++) {
+ coeffs[m] = poly[m];
+ }
+ seilerInverseNTT(coeffs);
+ for (int m = 0; m < ML_KEM_N; m++) {
+ poly[m] = (short) coeffs[m];
+ }
+ return poly;
+ }
+
+ // Implements the ML_KEM NTT algorithm similarly to that described
+ // in https://eprint.iacr.org/2018/039.pdf .
+ // It works in place, replaces the elements of the input coeffs array
+ // by the transformed representation.
+ // The input elements should be in the range [-MONT_Q, MONT_Q].
+ // The result elements will fit into the range of short
+ // (i.e. [-32768, 32767]).
+ private static void seilerNTT(int[] coeffs) {
+ int dimension = ML_KEM_N;
+ int zetaIndex = 0;
+ for (int l = dimension / 2; l > 1; 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[zetaIndex], coeffs[j + l]);
+ coeffs[j + l] = coeffs[j] - tmp;
+ coeffs[j] = coeffs[j] + tmp;
+ }
+ zetaIndex++;
+ }
+ }
+ }
+
+ // Implements the ML_KEM inverse NTT algorithm similarly to that described
+ // in https://eprint.iacr.org/2018/039.pdf .
+ // It works in place, replaces the elements of the input coeffs array
+ // by the transformed representation.
+ // The input elements should be in the range [-MONT_Q, MONT_Q).
+ // The output elements will be in the range (-MONT_Q, MONT_Q).
+ private static void seilerInverseNTT(int[] coeffs) {
+ int dimension = ML_KEM_N;
+ int zetaIndex = MONT_ZETAS_FOR_NTT.length - 1;
+ for (int l = 2; 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[zetaIndex]);
+ }
+ zetaIndex--;
+ }
+ }
+
+ for (int i = 0; i < dimension; i++) {
+ int r = montMul(coeffs[i], MONT_DIM_HALF_INVERSE);
+ coeffs[i] = r;
+ }
+ }
+
+ // Performs A o b + c where
+ // A is a mlKem_k by mlKem_k matrix,
+ // b and c are mlKem_k long vectors of degree ML_KEM_N - 1
+ // polynomials in the NTT domain representation.
+ // The coefficients in the result are in the range [0, ML_KEM_Q).
+ private short[][] mlKemMatrixVectorMuladd(
+ short[][][] a, short[][] b, short[][] c) {
+ short[] product = new short[ML_KEM_N];
+
+ for (int i = 0; i < mlKem_k; i++) {
+ for (int j = 0; j < mlKem_k; j++) {
+ nttMult(product, a[i][j], b[j]);
+ mlKemAddPoly(c[i], product);
+ }
+ mlKemBarrettReduce(c[i]);
+ }
+ return c;
+ }
+
+ // Performs a^T o b where a and b are mlKem_k long vectors
+ // of degree ML_KEM_N - 1 polynomials in the NTT representation,
+ // with coefficients in the range [-ML_KEM_Q, ML_KEM_Q].
+ // The coefficients in the result are in the range [0, ML_KEM_Q).
+ private short[] mlKemVectorScalarMult(short[][] a, short[][] b) {
+ short[] result = new short[ML_KEM_N];
+ short[] product = new short[ML_KEM_N];
+
+ int j;
+ for (j = 0; j < mlKem_k; j++) {
+ nttMult(product, a[j], b[j]);
+ mlKemAddPoly(result, product);
+ }
+ mlKemBarrettReduce(result);
+
+ return result;
+ }
+
+ // Multiplies two polynomials represented in the NTT domain.
+ // The result is a representation of the product still in the NTT domain.
+ // The coefficients in the result are in the range (-ML_KEM_Q, ML_KEM_Q).
+ private void nttMult(short[] result, short[] ntta, short[] nttb) {
+ for (int m = 0; m < ML_KEM_N / 2; m++) {
+ int a0 = ntta[2 * m];
+ int a1 = ntta[2 * m + 1];
+ int b0 = nttb[2 * m];
+ int b1 = nttb[2 * m + 1];
+ int r = montMul(a0, b0) +
+ montMul(montMul(a1, b1), MONT_ZETAS_FOR_NTT_MULT[m]);
+ result[2 * m] = (short) montMul(r, MONT_R_SQUARE_MOD_Q);
+ result[2 * m + 1] = (short) montMul(
+ (montMul(a0, b1) + montMul(a1, b0)), MONT_R_SQUARE_MOD_Q);
+ }
+ }
+
+ // Adds the vector of polynomials b to a in place, i.e. a will hold
+ // the result. It also returns (the modified) a so that it can be used
+ // in an expression.
+ // The coefficients in all polynomials of both vectors are supposed to be
+ // greater than -ML_KEM_Q and less than ML_KEM_Q.
+ // The coefficients in the result are nonnegative and less than ML_KEM_Q.
+ private short[][] mlKemAddVec(short[][] a, short[][] b) {
+ for (int i = 0; i < mlKem_k; i++) {
+ mlKemAddPoly(a[i], b[i]);
+ mlKemBarrettReduce(a[i]);
+ }
+ return a;
+ }
+
+ // Adds the polynomial b to a in place, i.e. (the modified) a will hold
+ // the result.
+ // The coefficients are supposed be greater than -ML_KEM_Q in a and
+ // greater than -ML_KEM_Q and less than ML_KEM_Q in b.
+ // The coefficients in the result are greater than -ML_KEM_Q.
+ private void mlKemAddPoly(short[] a, short[] b) {
+ for (int m = 0; m < ML_KEM_N; m++) {
+ int r = a[m] + b[m] + ML_KEM_Q; // This makes r > -ML_KEM_Q
+ a[m] = (short) r;
+ }
+ }
+
+ // Adds the polynomials b and c to a in place, i.e. 'a' will hold the sum.
+ // 'a' is also returned so that this function can be used in an expression.
+ // The coefficients in all three polynomials are supposed to be
+ // greater than -ML_KEM_Q and less than ML_KEM_Q.
+ // The coefficients in the result are nonnegative and less than ML_KEM_Q.
+ private short[] mlKemAddPoly(short[] a, short[] b, short[] c) {
+ for (int m = 0; m < ML_KEM_N; m++) {
+ int r = a[m] + b[m] + c[m] + 2 * ML_KEM_Q; // This makes r > - ML_KEM_Q
+ a[m] = (short) r;
+ }
+ mlKemBarrettReduce(a);
+ return a;
+ }
+
+ // Subtracts the polynomial b from a in place, i.e. the result is
+ // stored in a. It also returns (the modified) a, so that it can be used
+ // in an expression.
+ // The coefficiens in both are assumed to be greater than -ML_KEM_Q
+ // and less than ML_KEM_Q.
+ // The coefficients in the result are nonnegative and less than ML_KEM_Q.
+ private short[] mlKemSubtractPoly(short[] a, short[] b) {
+ for (int m = 0; m < ML_KEM_N; m++) {
+ int r = a[m] - b[m] + ML_KEM_Q; // This makes r > -ML_KEM_Q
+ a[m] = (short) r;
+ }
+ mlKemBarrettReduce(a);
+ return a;
+ }
+
+ private byte[] encodeVector(int l, short[][] vector) {
+ return encodeVector(l, vector, mlKem_k);
+ }
+
+ private static byte[] encodeVector(int l, short[][] vector, int k) {
+ int encodedPolyLength = ML_KEM_N * l / 8;
+ byte[] result = new byte[k * encodedPolyLength];
+
+ for (int i = 0; i < k; i++) {
+ byte[] resultBytes = encodePoly(l, vector[i]);
+ System.arraycopy(resultBytes, 0,
+ result, i * encodedPolyLength, encodedPolyLength);
+ }
+ return result;
+ }
+
+ private static void encodePoly12(short[] poly, byte[] result, int resultOffs) {
+ int low;
+ int high;
+ for (int m = 0; m < ML_KEM_N / 2; m++) {
+ low = poly[2 * m];
+ low += ((low >> 31) & ML_KEM_Q);
+ low = low & 0xfff;
+ high = poly[2 * m + 1];
+ high += ((high >> 31) & ML_KEM_Q);
+ high = high & 0xfff;
+
+ result[resultOffs++] = (byte) low;
+ result[resultOffs++] = (byte) ((high << 4) + (low >> 8));
+ result[resultOffs++] = (byte) (high >> 4);
+ }
+ }
+
+ private static void encodePoly4(short[] poly, byte[] result) {
+ for (int m = 0; m < ML_KEM_N / 2; m++) {
+ result[m] = (byte) ((poly[2 * m] & 0xf) + (poly[2 * m + 1] << 4));
+ }
+ }
+
+ // Computes the byte array containing the packed l-bit representation
+ // of a polynomial. The coefficients in poly should be either nonnegative
+ // or elements of Z_(ML_KEM_Q) represented by a 16-bit value
+ // between -ML_KEM_Q and ML_KEM_Q.
+ private static byte[] encodePoly(int l, short[] poly) {
+ byte[] result = new byte[ML_KEM_N / 8 * l];
+ if (l == 4) {
+ encodePoly4(poly, result);
+ } else {
+ int mask = (1 << l) - 1;
+ int shift = 0;
+ int index = 0;
+ int current = 0;
+ for (int m = 0; m < ML_KEM_N; m++) {
+ int currentShort = poly[m];
+ currentShort += (currentShort >> 31) & ML_KEM_Q;
+ current += ((currentShort & mask) << shift);
+ shift += l;
+ while (shift >= 8) {
+ result[index++] = (byte) current;
+ current >>>= 8;
+ shift -= 8;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ private static byte[] encodeCompress(short[] poly) {
+ byte[] result = new byte[ML_KEM_N / 8];
+ int xx;
+ int currentByte;
+ for (int i = 0; i < ML_KEM_N / 8; i++) {
+ currentByte = 0;
+ xx = poly[i * 8];
+ currentByte |= (((832 - xx) & (xx - 2497)) >>> 31);
+ xx = poly[i * 8 + 1];
+ currentByte |= ((((832 - xx) & (xx - 2497)) >>> 30) & 2);
+ xx = poly[i * 8 + 2];
+ currentByte |= ((((832 - xx) & (xx - 2497)) >>> 29) & 4);
+ xx = poly[i * 8 + 3];
+ currentByte |= ((((832 - xx) & (xx - 2497)) >>> 28) & 8);
+ xx = poly[i * 8 + 4];
+ currentByte |= ((((832 - xx) & (xx - 2497)) >>> 27) & 16);
+ xx = poly[i * 8 + 5];
+ currentByte |= ((((832 - xx) & (xx - 2497)) >>> 26) & 32);
+ xx = poly[i * 8 + 6];
+ currentByte |= ((((832 - xx) & (xx - 2497)) >>> 25) & 64);
+ xx = poly[i * 8 + 7];
+ currentByte |= ((((832 - xx) & (xx - 2497)) >>> 24) & 128);
+ result[i] = (byte) currentByte;
+ }
+ return result;
+ }
+
+ private short[][] decodeVector(int l, byte[] encodedVector) {
+ short[][] result = new short[mlKem_k][];
+ for (int i = 0; i < mlKem_k; i++) {
+ result[i] = decodePoly(l, encodedVector, (i * ML_KEM_N * l) / 8);
+ }
+ return result;
+ }
+
+ // The intrinsic implementations assume that the input and output buffers
+ // are such that condensed can be read in 192-byte chunks and
+ // parsed can be written in 128 shorts chunks. In other words,
+ // if (i - 1) * 128 < parsedLengths <= i * 128 then
+ // parsed.size should be at least i * 128 and
+ // condensed.size should be at least index + i * 192
+ private void twelve2Sixteen(byte[] condensed, int index,
+ short[] parsed, int parsedLength) {
+
+ for (int i = 0; i < parsedLength * 3 / 2; i += 3) {
+ parsed[(i / 3) * 2] = (short) ((condensed[i + index] & 0xff) +
+ 256 * (condensed[i + index + 1] & 0xf));
+ parsed[(i / 3) * 2 + 1] = (short) (((condensed[i + index + 1] >>> 4) & 0xf) +
+ 16 * (condensed[i + index + 2] & 0xff));
+ }
+ }
+
+ private static void decodePoly5(byte[] condensed, int index, short[] parsed) {
+ int j = index;
+ for (int i = 0; i < ML_KEM_N; i += 8) {
+ parsed[i] = (short) (condensed[j] & 0x1f);
+ parsed[i + 1] = (short) ((((condensed[j] & 0xff) >>> 5) +
+ (condensed[j + 1] << 3) & 0x1f));
+ parsed[i + 2] = (short) ((condensed[j + 1] & 0x7f) >>> 2);
+ parsed[i + 3] = (short) ((((condensed[j + 1] & 0xff) >>> 7) +
+ (condensed[j + 2] << 1)) & 0x1f);
+ parsed[i + 4] = (short) ((((condensed[j + 2] & 0xff) >>> 4) +
+ (condensed[j + 3] << 4)) & 0x1f);
+ parsed[i + 5] = (short) ((condensed[j + 3] & 0x3f) >>> 1);
+ parsed[i + 6] = (short) ((((condensed[j + 3] & 0xff) >>> 6) +
+ (condensed[j + 4] << 2)) & 0x1f);
+ parsed[i + 7] = (short) ((condensed[j + 4] & 0xff) >>> 3);
+ j += 5;
+ }
+ }
+
+ private static void decodePoly4(byte[] condensed, int index, short[] parsed) {
+ for (int i = 0; i < ML_KEM_N / 2; i++) {
+ parsed[i * 2] = (short) (condensed[i + index] & 0xf);
+ parsed[i * 2 + 1] = (short) ((condensed[i + index] >>> 4) & 0xf);
+ }
+ }
+
+ // Recovers the 16-bit coefficients of a polynomial from a byte array
+ // containing a packed l-bit representation.
+ // The recovered coefficients will be in the range 0 <= coeff < 2^l .
+ private short[] decodePoly(int l, byte[] input, int index) {
+ short[] poly = new short[ML_KEM_N];
+ if (l == 12) {
+ twelve2Sixteen(input, index, poly, ML_KEM_N);
+ } else if (l == 4) {
+ decodePoly4(input, index, poly);
+ } else if (l == 5) {
+ decodePoly5(input, index, poly);
+ } else {
+ int mask = (1 << l) - 1;
+ int top = 0;
+ int shift = 0;
+ int acc = 0;
+ for (int m = 0; m < ML_KEM_N; m++) {
+ while (top - shift < l) {
+ acc += ((input[index++] & 0xff) << top);
+ top += 8;
+ }
+ poly[m] = (short) ((acc >> shift) & mask);
+ shift += l;
+ while (shift >= 8) {
+ top -= 8;
+ shift -= 8;
+ acc >>>= 8;
+ }
+ }
+ }
+
+ return poly;
+ }
+
+ // Prerequisite: d == 10 or d == 11
+ // Compresses a vector in place, i.e. it modifies the coefficients of the
+ // polynomials of its input vector. It returns its (modified) input so that
+ // the function can be used in an expression.
+ private short[][] compressVector10_11(short[][] vector, int d) {
+ for (int i = 0; i < mlKem_k; i++) {
+ vector[i] = compressPoly10_11(vector[i], d);
+ }
+ return vector;
+ }
+
+ // Prerequisite: for all m, 0 <= poly[m] < ML_KEM_Q, d == 4 or d == 5
+ // Replaces poly[m] with round(2^d * poly[m] / ML_KEM_Q) mod 2^d for all m,
+ // where round(z) is the integer closest to z, i.e.
+ // compresses a polynomial in place.
+ private static short[] compressPoly4_5(short[] poly, int d) {
+ int xx;
+ for (int m = 0; m < ML_KEM_N; m++) {
+ xx = (poly[m] << d) + ML_KEM_Q / 2;
+ poly[m] = (short)((xx * 315) >> 20);
+ }
+ return poly;
+ }
+
+ // Prerequisite: for all m, 0 <= poly[m] < ML_KEM_Q, d == 10 or d == 11
+ // Replaces poly[m] with round(2^d * poly[m] / ML_KEM_Q) mod 2^d for all m,
+ // where round(z) is the integer closest to z, i.e.
+ // compresses a polynomial in place.
+ private static short[] compressPoly10_11(short[] poly, int d) {
+ long xx;
+ for (int m = 0; m < ML_KEM_N; m++) {
+ xx = (poly[m] << d) + ML_KEM_Q / 2;
+ poly[m] = (short)((xx * 161271L) >> 29);
+ }
+ return poly;
+ }
+
+ // Decompresses a vector in place, i.e. it modifies the coefficients of the
+ // polynomials of its input vector. It returns its (modified) input so that
+ // the function can be used in an expression.
+ private short[][] decompressVector(short[][] vector, int d) {
+ for (int i = 0; i < mlKem_k; i++) {
+ vector[i] = decompressPoly(vector[i], d);
+ }
+ return vector;
+ }
+
+ // Decompresses a polynomial in place, i.e. it modifies the coefficients
+ // in its input. It returns its (modified) input so that the function can
+ // be used in an expression.
+ // Prerequisite: 0 <= x[i] < 2^d < ML_KEM_Q .
+ // Computes Round(ML_KEM_Q * x[i] / 2^d),
+ // where Round(z) is the integer closest to z,
+ // for each coefficient of a polynomial
+ private static short[] decompressPoly(short[] poly, int d) {
+ for (int m = 0; m < ML_KEM_N; m++) {
+ int qx = ML_KEM_Q * poly[m];
+ poly[m] = (short) ((qx >> d) + ((qx >> (d - 1)) & 1));
+ }
+ return poly;
+ }
+
+ private static short[] decompressDecode(byte[] input) {
+ short[] result = new short[256];
+ for (int i = 0; i < 32; i++) {
+ int currentByte = input[i] & 0xFF;
+ result [i * 8] = (short)(((currentByte << 31 ) >> 31) & 1665);
+ result [i * 8 + 1] = (short)(((currentByte << 30 ) >> 31) & 1665);
+ result [i * 8 + 2] = (short)(((currentByte << 29 ) >> 31) & 1665);
+ result [i * 8 + 3] = (short)(((currentByte << 28 ) >> 31) & 1665);
+ result [i * 8 + 4] = (short)(((currentByte << 27 ) >> 31) & 1665);
+ result [i * 8 + 5] = (short)(((currentByte << 26 ) >> 31) & 1665);
+ result [i * 8 + 6] = (short)(((currentByte << 25 ) >> 31) & 1665);
+ result [i * 8 + 7] = (short)(((currentByte << 24 ) >> 31) & 1665);
+ }
+ return result;
+ }
+
+ // The input elements can have any short value.
+ // Modifies poly such that upon return poly[i] will be
+ // in the range [0, ML_KEM_Q] and will be congruent with the original
+ // poly[i] modulo ML_KEM_Q, for all i in [0, ML_KEM_N).
+ // At return, poly[i] == ML_KEM_Q if and only if the original poly[i] was
+ // a negative integer multiple of ML_KEM_Q.
+ // That means that if the original poly[i] > -ML_KEM_Q then at return it
+ // will be in the range [0, ML_KEM_Q), i.e. it will be the canonical
+ // representative of its residue class.
+ private void mlKemBarrettReduce(short[] poly) {
+ for (int m = 0; m < ML_KEM_N; m++) {
+ int tmp = ((int) poly[m] * BARRETT_MULTIPLIER) >> BARRETT_SHIFT;
+ poly[m] = (short) (poly[m] - tmp * ML_KEM_Q);
+ }
+ }
+
+ // Precondition: -(2^MONT_R_BITS -1) * MONT_Q <= b * c < (2^MONT_R_BITS - 1) * MONT_Q
+ // Computes b * c * 2^-MONT_R_BITS mod MONT_Q
+ // The result is between -MONT_Q and MONT_Q
+ private static int montMul(int b, int c) {
+ int a = b * c;
+ int aHigh = a >> MONT_R_BITS;
+ int aLow = a & ((1 << MONT_R_BITS) - 1);
+ // signed low product
+ int m = ((MONT_Q_INV_MOD_R * aLow) << (32 - MONT_R_BITS)) >> (32 - MONT_R_BITS);
+
+ return (aHigh - ((m * MONT_Q) >> MONT_R_BITS)); // subtract signed high product
+ }
+}
diff --git a/src/java.base/share/classes/com/sun/crypto/provider/ML_KEM_Impls.java b/src/java.base/share/classes/com/sun/crypto/provider/ML_KEM_Impls.java
new file mode 100644
index 000000000000..2ce5b3324e76
--- /dev/null
+++ b/src/java.base/share/classes/com/sun/crypto/provider/ML_KEM_Impls.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (c) 2024, 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 com.sun.crypto.provider;
+
+import sun.security.jca.JCAUtil;
+import sun.security.provider.NamedKEM;
+import sun.security.provider.NamedKeyFactory;
+import sun.security.provider.NamedKeyPairGenerator;
+
+import java.security.*;
+import java.util.Arrays;
+
+import javax.crypto.DecapsulateException;
+
+public final class ML_KEM_Impls {
+
+ public sealed static class KPG
+ extends NamedKeyPairGenerator permits KPG2, KPG3, KPG5 {
+
+ public KPG() {
+ // ML-KEM-768 is the default
+ super("ML-KEM", "ML-KEM-768", "ML-KEM-512", "ML-KEM-1024");
+ }
+
+ protected KPG(String pname) {
+ super("ML-KEM", pname);
+ }
+
+ @Override
+ protected byte[][] implGenerateKeyPair(String name, SecureRandom random) {
+ byte[] seed = new byte[32];
+ var r = random != null ? random : JCAUtil.getDefSecureRandom();
+ r.nextBytes(seed);
+ byte[] z = new byte[32];
+ r.nextBytes(z);
+
+ ML_KEM mlKem = new ML_KEM(name);
+ ML_KEM.ML_KEM_KeyPair kp;
+ try {
+ kp = mlKem.generateKemKeyPair(seed, z);
+ } finally {
+ Arrays.fill(seed, (byte)0);
+ Arrays.fill(z, (byte)0);
+ }
+ return new byte[][] {
+ kp.encapsulationKey().keyBytes(),
+ kp.decapsulationKey().keyBytes()
+ };
+ }
+ }
+
+ public final static class KPG2 extends KPG {
+ public KPG2() {
+ super("ML-KEM-512");
+ }
+ }
+
+ public final static class KPG3 extends KPG {
+ public KPG3() {
+ super("ML-KEM-768");
+ }
+ }
+
+ public final static class KPG5 extends KPG {
+ public KPG5() {
+ super("ML-KEM-1024");
+ }
+ }
+
+ public sealed static class KF extends NamedKeyFactory permits KF2, KF3, KF5 {
+ public KF() {
+ super("ML-KEM", "ML-KEM-512", "ML-KEM-768", "ML-KEM-1024");
+ }
+ public KF(String name) {
+ super("ML-KEM", name);
+ }
+ }
+
+ public final static class KF2 extends KF {
+ public KF2() {
+ super("ML-KEM-512");
+ }
+ }
+
+ public final static class KF3 extends KF {
+ public KF3() {
+ super("ML-KEM-768");
+ }
+ }
+
+ public final static class KF5 extends KF {
+ public KF5() {
+ super("ML-KEM-1024");
+ }
+ }
+
+ public sealed static class K extends NamedKEM permits K2, K3, K5 {
+ private static final int SEED_SIZE = 32;
+
+ @Override
+ protected byte[][] implEncapsulate(String name, byte[] encapsulationKey,
+ Object ek, SecureRandom secureRandom) {
+
+ byte[] randomBytes = new byte[SEED_SIZE];
+ var r = secureRandom != null ? secureRandom : JCAUtil.getDefSecureRandom();
+ r.nextBytes(randomBytes);
+
+ ML_KEM mlKem = new ML_KEM(name);
+ ML_KEM.ML_KEM_EncapsulateResult mlKemEncapsulateResult = null;
+ try {
+ mlKemEncapsulateResult = mlKem.encapsulate(
+ new ML_KEM.ML_KEM_EncapsulationKey(
+ encapsulationKey), randomBytes);
+ } finally {
+ Arrays.fill(randomBytes, (byte) 0);
+ }
+
+ return new byte[][] {
+ mlKemEncapsulateResult.cipherText().encryptedBytes(),
+ mlKemEncapsulateResult.sharedSecret()
+ };
+ }
+
+ @Override
+ protected byte[] implDecapsulate(String name, byte[] decapsulationKey,
+ Object dk, byte[] cipherText)
+ throws DecapsulateException {
+
+ ML_KEM mlKem = new ML_KEM(name);
+ var kpkeCipherText = new ML_KEM.K_PKE_CipherText(cipherText);
+ return mlKem.decapsulate(new ML_KEM.ML_KEM_DecapsulationKey(
+ decapsulationKey), kpkeCipherText);
+ }
+
+ @Override
+ protected int implSecretSize(String name) {
+ return ML_KEM.SECRET_SIZE;
+ }
+
+ @Override
+ protected int implEncapsulationSize(String name) {
+ ML_KEM mlKem = new ML_KEM(name);
+ return mlKem.getEncapsulationSize();
+ }
+
+ @Override
+ protected Object implCheckPublicKey(String name, byte[] pk)
+ throws InvalidKeyException {
+
+ ML_KEM mlKem = new ML_KEM(name);
+ return mlKem.checkPublicKey(pk);
+ }
+
+ @Override
+ protected Object implCheckPrivateKey(String name, byte[] sk)
+ throws InvalidKeyException {
+
+ ML_KEM mlKem = new ML_KEM(name);
+ return mlKem.checkPrivateKey(sk);
+ }
+
+ public K() {
+ super("ML-KEM", "ML-KEM-512", "ML-KEM-768", "ML-KEM-1024");
+ }
+
+ public K(String name) {
+ super("ML-KEM", name);
+ }
+ }
+
+ public final static class K2 extends K {
+ public K2() {
+ super("ML-KEM-512");
+ }
+ }
+
+ public final static class K3 extends K {
+ public K3() {
+ super("ML-KEM-768");
+ }
+ }
+
+ public final static class K5 extends K {
+ public K5() {
+ super("ML-KEM-1024");
+ }
+ }
+}
diff --git a/src/java.base/share/classes/com/sun/crypto/provider/SunJCE.java b/src/java.base/share/classes/com/sun/crypto/provider/SunJCE.java
index 6a4e28372e50..0a5cb1abc4d9 100644
--- a/src/java.base/share/classes/com/sun/crypto/provider/SunJCE.java
+++ b/src/java.base/share/classes/com/sun/crypto/provider/SunJCE.java
@@ -78,6 +78,8 @@
*
* - DHKEM
*
+ * - ML-KEM
+ *
*/
public final class SunJCE extends Provider {
@@ -87,7 +89,7 @@ public final class SunJCE extends Provider {
private static final String info = "SunJCE Provider " +
"(implements RSA, DES, Triple DES, AES, Blowfish, ARCFOUR, RC2, PBE, "
- + "Diffie-Hellman, HMAC, ChaCha20)";
+ + "Diffie-Hellman, HMAC, ChaCha20, DHKEM, and ML-KEM)";
/* Are we debugging? -- for developers */
static final boolean debug = false;
@@ -759,6 +761,23 @@ void putEntries() {
"|java.security.interfaces.XECKey");
ps("KEM", "DHKEM", "com.sun.crypto.provider.DHKEM", null, attrs);
+ attrs.clear();
+ attrs.put("ImplementedIn", "Software");
+ ps("KEM", "ML-KEM", "com.sun.crypto.provider.ML_KEM_Impls$K", null, attrs);
+ psA("KEM", "ML-KEM-512", "com.sun.crypto.provider.ML_KEM_Impls$K2", attrs);
+ psA("KEM", "ML-KEM-768", "com.sun.crypto.provider.ML_KEM_Impls$K3", attrs);
+ psA("KEM", "ML-KEM-1024", "com.sun.crypto.provider.ML_KEM_Impls$K5",attrs);
+
+ ps("KeyPairGenerator", "ML-KEM", "com.sun.crypto.provider.ML_KEM_Impls$KPG", null, attrs);
+ psA("KeyPairGenerator", "ML-KEM-512", "com.sun.crypto.provider.ML_KEM_Impls$KPG2", attrs);
+ psA("KeyPairGenerator", "ML-KEM-768", "com.sun.crypto.provider.ML_KEM_Impls$KPG3", attrs);
+ psA("KeyPairGenerator", "ML-KEM-1024", "com.sun.crypto.provider.ML_KEM_Impls$KPG5", attrs);
+
+ ps("KeyFactory", "ML-KEM", "com.sun.crypto.provider.ML_KEM_Impls$KF", null, attrs);
+ psA("KeyFactory", "ML-KEM-512", "com.sun.crypto.provider.ML_KEM_Impls$KF2", attrs);
+ psA("KeyFactory", "ML-KEM-768", "com.sun.crypto.provider.ML_KEM_Impls$KF3", attrs);
+ psA("KeyFactory", "ML-KEM-1024", "com.sun.crypto.provider.ML_KEM_Impls$KF5", attrs);
+
/*
* SSL/TLS mechanisms
*
diff --git a/src/java.base/share/classes/java/lang/ref/Reference.java b/src/java.base/share/classes/java/lang/ref/Reference.java
index ef4fd1ef20fc..b78be26dee76 100644
--- a/src/java.base/share/classes/java/lang/ref/Reference.java
+++ b/src/java.base/share/classes/java/lang/ref/Reference.java
@@ -357,11 +357,18 @@ public ReferenceQueue newNativeReferenceQueue() {
* {@code null} if this reference object has been cleared
* @see #refersTo
*/
- @IntrinsicCandidate
public T get() {
- return this.referent;
+ return get0();
}
+ /* Implementation of get(). This method exists to avoid making get() all
+ * of virtual, native, and intrinsic candidate. That could have the
+ * undesirable effect of having the native method used instead of the
+ * intrinsic when devirtualization fails.
+ */
+ @IntrinsicCandidate
+ private native T get0();
+
/**
* Tests if the referent of this reference object is {@code obj}.
* Using a {@code null} {@code obj} returns {@code true} if the
diff --git a/src/java.base/share/classes/java/util/zip/ZipFile.java b/src/java.base/share/classes/java/util/zip/ZipFile.java
index cb9070fc885d..07a5fa0b8970 100644
--- a/src/java.base/share/classes/java/util/zip/ZipFile.java
+++ b/src/java.base/share/classes/java/util/zip/ZipFile.java
@@ -1544,7 +1544,7 @@ private final int readAt(byte[] buf, int off, int len, long pos)
private static class End {
- int centot; // 4 bytes
+ long centot; // 4 bytes
long cenlen; // 4 bytes
long cenoff; // 4 bytes
long endpos; // 4 bytes
@@ -1639,7 +1639,7 @@ private End findEND() throws IOException {
// to use the end64 values
end.cenlen = cenlen64;
end.cenoff = cenoff64;
- end.centot = (int)centot64; // assume total < 2g
+ end.centot = centot64;
end.endpos = end64pos;
} catch (IOException x) {} // no zip64 loc/end
return end;
@@ -1675,11 +1675,14 @@ private void initCEN(int knownTotal) throws IOException {
if (end.cenlen + ENDHDR >= Integer.MAX_VALUE) {
zerror("invalid END header (central directory size too large)");
}
+ if (end.centot < 0 || end.centot > end.cenlen / CENHDR) {
+ zerror("invalid END header (total entries count too large)");
+ }
cen = this.cen = new byte[(int)(end.cenlen + ENDHDR)];
if (readFullyAt(cen, 0, cen.length, cenpos) != end.cenlen + ENDHDR) {
zerror("read CEN tables failed");
}
- this.total = end.centot;
+ this.total = Math.toIntExact(end.centot);
} else {
cen = this.cen;
this.total = knownTotal;
diff --git a/src/java.base/share/classes/sun/security/pkcs/NamedPKCS8Key.java b/src/java.base/share/classes/sun/security/pkcs/NamedPKCS8Key.java
new file mode 100644
index 000000000000..b46234ba2882
--- /dev/null
+++ b/src/java.base/share/classes/sun/security/pkcs/NamedPKCS8Key.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2024, 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.pkcs;
+
+import sun.security.util.DerInputStream;
+import sun.security.util.DerValue;
+import sun.security.x509.AlgorithmId;
+
+import javax.security.auth.DestroyFailedException;
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
+import java.io.Serial;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.ProviderException;
+import java.security.spec.NamedParameterSpec;
+import java.util.Arrays;
+
+/// Represents a private key from an algorithm family that is specialized
+/// with a named parameter set.
+///
+/// This key is generated by either a [sun.security.provider.NamedKeyPairGenerator]
+/// or [sun.security.provider.NamedKeyFactory]. Its [#getAlgorithm] method
+/// returns the algorithm family name, while its [#getParams()] method returns
+/// the parameter set name as a [NamedParameterSpec] object. The algorithm
+/// identifier in the PKCS #8 encoding of the key is always a single OID derived
+/// from the parameter set name.
+///
+/// @see sun.security.provider.NamedKeyPairGenerator
+public final class NamedPKCS8Key extends PKCS8Key {
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ private final String fname;
+ private final transient NamedParameterSpec paramSpec;
+ private final byte[] rawBytes;
+
+ private transient boolean destroyed = false;
+
+ /// Ctor from family name, parameter set name, raw key bytes.
+ /// Key bytes won't be cloned, caller must relinquish ownership
+ public NamedPKCS8Key(String fname, String pname, byte[] rawBytes) {
+ this.fname = fname;
+ this.paramSpec = new NamedParameterSpec(pname);
+ try {
+ this.algid = AlgorithmId.get(pname);
+ } catch (NoSuchAlgorithmException e) {
+ throw new ProviderException(e);
+ }
+ this.rawBytes = rawBytes;
+
+ DerValue val = new DerValue(DerValue.tag_OctetString, rawBytes);
+ try {
+ this.key = val.toByteArray();
+ } finally {
+ val.clear();
+ }
+ }
+
+ /// Ctor from family name, and PKCS #8 bytes
+ public NamedPKCS8Key(String fname, byte[] encoded) throws InvalidKeyException {
+ super(encoded);
+ this.fname = fname;
+ try {
+ paramSpec = new NamedParameterSpec(algid.getName());
+ if (algid.getEncodedParams() != null) {
+ throw new InvalidKeyException("algorithm identifier has params");
+ }
+ rawBytes = new DerInputStream(key).getOctetString();
+ } catch (IOException e) {
+ throw new InvalidKeyException("Cannot parse input", e);
+ }
+ }
+
+ @Override
+ public String toString() {
+ // Do not modify: this can be used by earlier JDKs that
+ // do not have the getParams() method
+ return paramSpec.getName() + " private key";
+ }
+
+ /// Returns the reference to the internal key. Caller must not modify
+ /// the content or keep a reference.
+ public byte[] getRawBytes() {
+ return rawBytes;
+ }
+
+ public NamedParameterSpec getParams() {
+ return paramSpec;
+ }
+
+ @Override
+ public String getAlgorithm() {
+ return fname;
+ }
+
+ @java.io.Serial
+ private void readObject(ObjectInputStream stream)
+ throws IOException, ClassNotFoundException {
+ throw new InvalidObjectException(
+ "NamedPKCS8Key keys are not directly deserializable");
+ }
+
+ @Override
+ public void destroy() throws DestroyFailedException {
+ Arrays.fill(rawBytes, (byte)0);
+ Arrays.fill(key, (byte)0);
+ if (encodedKey != null) {
+ Arrays.fill(encodedKey, (byte)0);
+ }
+ destroyed = true;
+ }
+
+ @Override
+ public boolean isDestroyed() {
+ return destroyed;
+ }
+}
diff --git a/src/java.base/share/classes/sun/security/pkcs/PKCS7.java b/src/java.base/share/classes/sun/security/pkcs/PKCS7.java
index 302b5922bbc7..5f05af1c07e7 100644
--- a/src/java.base/share/classes/sun/security/pkcs/PKCS7.java
+++ b/src/java.base/share/classes/sun/security/pkcs/PKCS7.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2024, 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
@@ -38,7 +38,7 @@
import java.util.function.Function;
import sun.security.jca.JCAUtil;
-import sun.security.provider.SHAKE256;
+import sun.security.provider.SHA3.SHAKE256;
import sun.security.timestamp.*;
import sun.security.util.*;
import sun.security.x509.*;
diff --git a/src/java.base/share/classes/sun/security/pkcs/SignerInfo.java b/src/java.base/share/classes/sun/security/pkcs/SignerInfo.java
index 8f430f7b6b2b..6151069ea9a5 100644
--- a/src/java.base/share/classes/sun/security/pkcs/SignerInfo.java
+++ b/src/java.base/share/classes/sun/security/pkcs/SignerInfo.java
@@ -32,7 +32,7 @@
import java.security.spec.PSSParameterSpec;
import java.util.*;
-import sun.security.provider.SHAKE256;
+import sun.security.provider.SHA3.SHAKE256;
import sun.security.timestamp.TimestampToken;
import sun.security.util.*;
import sun.security.x509.AlgorithmId;
diff --git a/src/java.base/share/classes/sun/security/provider/DigestBase.java b/src/java.base/share/classes/sun/security/provider/DigestBase.java
index dbe59396ac0b..2aaf0a2fac6f 100644
--- a/src/java.base/share/classes/sun/security/provider/DigestBase.java
+++ b/src/java.base/share/classes/sun/security/provider/DigestBase.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2024, 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
@@ -61,7 +61,7 @@ abstract class DigestBase extends MessageDigestSpi implements Cloneable {
private final int digestLength;
// size of the input to the compression function in bytes
- private final int blockSize;
+ protected final int blockSize;
// buffer to store partial blocks, blockSize bytes large
// Subclasses should not access this array directly except possibly in their
// implDigest() method. See MD5.java as an example.
diff --git a/src/java.base/share/classes/sun/security/provider/ML_DSA.java b/src/java.base/share/classes/sun/security/provider/ML_DSA.java
new file mode 100644
index 000000000000..969b8fffa39f
--- /dev/null
+++ b/src/java.base/share/classes/sun/security/provider/ML_DSA.java
@@ -0,0 +1,1567 @@
+/*
+ * 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.SHAKE128;
+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 = new int[mlDsa_l][ML_DSA_N];
+ int[][] s2 = new int[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 = new int[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 = new int[mlDsa_k][ML_DSA_N];
+ int[][] t1 = new int[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 = new int[mlDsa_l][ML_DSA_N];
+ boolean[][] h = new boolean[mlDsa_k][ML_DSA_N];
+ byte[] commitmentHash = new byte[lambda/4];
+ int[][] y = new int[mlDsa_l][ML_DSA_N];
+ int[][] yy = new int[mlDsa_l][ML_DSA_N];
+ int[][] w = new int[mlDsa_k][ML_DSA_N];
+ int[][] w0 = new int[mlDsa_k][ML_DSA_N];
+ int[][] w1 = new int[mlDsa_k][ML_DSA_N];
+ int[][] w_ct0 = new int[mlDsa_k][ML_DSA_N];
+ int[] c = new int[ML_DSA_N];
+ int[][] cs1 = new int[mlDsa_l][ML_DSA_N];
+ int[][] cs2 = new int[mlDsa_k][ML_DSA_N];
+ int[][] ct0 = new int[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);
+ //mlDsaVectorInverseNtt(y);
+
+ //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 = new int[mlDsa_k][ML_DSA_N];
+ matrixVectorPointwiseMultiply(aHatZ, aHat, sig.response());
+
+ int[][] t1Hat = vectorConstMul(1 << ML_DSA_D, pk.t1());
+ mlDsaVectorNtt(t1Hat);
+
+ int[][] ct1 = new int[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 = new int[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 = new boolean[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 = new int[mlDsa_l][ML_DSA_N];
+ bitUnpack(s1, sk, start, mlDsa_l, eta, s1s2CoeffSize);
+
+ //Parse s2
+ start = end;
+ end += 32 * s1s2CoeffSize * mlDsa_k;
+ int[][] s2 = new int[mlDsa_k][ML_DSA_N];
+ bitUnpack(s2, sk, start, mlDsa_k, eta, s1s2CoeffSize);
+
+ //Parse t0
+ start = end;
+ int[][] t0 = new int[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 = new int[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) {
+ int[][][] a = new int[mlDsa_k][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 = new int[mlDsa_k][ML_DSA_N];
+ int[][] highPart = new int[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 = new int[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) {
+ 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) {
+ 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) {
+ 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) {
+ 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 = new int[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 = new int[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);
+ }
+}
diff --git a/src/java.base/share/classes/sun/security/provider/ML_DSA_Impls.java b/src/java.base/share/classes/sun/security/provider/ML_DSA_Impls.java
new file mode 100644
index 000000000000..dffe7c5cdb18
--- /dev/null
+++ b/src/java.base/share/classes/sun/security/provider/ML_DSA_Impls.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 2024, 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 sun.security.jca.JCAUtil;
+import java.security.*;
+import java.security.SecureRandom;
+import java.util.Arrays;
+
+public class ML_DSA_Impls {
+
+ public enum Version {
+ DRAFT, FINAL
+ }
+
+ // This implementation works in FIPS 204 final. If for some reason
+ // (for example, interop with an old version, or running an old test),
+ // set the version to an older one. The following VM option is required:
+ //
+ // --add-exports java.base/sun.security.provider=ALL-UNNAMED
+ public static Version version = Version.FINAL;
+
+ static int name2int(String name) {
+ if (name.endsWith("44")) {
+ return 2;
+ } else if (name.endsWith("65")) {
+ return 3;
+ } else if (name.endsWith("87")) {
+ return 5;
+ } else {
+ // should not happen
+ throw new ProviderException("Unknown name " + name);
+ }
+ }
+
+ public sealed static class KPG
+ extends NamedKeyPairGenerator permits KPG2, KPG3, KPG5 {
+
+ public KPG() {
+ // ML-DSA-65 is default
+ super("ML-DSA", "ML-DSA-65", "ML-DSA-44", "ML-DSA-87");
+ }
+
+ public KPG(String pname) {
+ super("ML-DSA", pname);
+ }
+
+ @Override
+ protected byte[][] implGenerateKeyPair(String name, SecureRandom sr) {
+ byte[] seed = new byte[32];
+ var r = sr != null ? sr : JCAUtil.getDefSecureRandom();
+ r.nextBytes(seed);
+ ML_DSA mlDsa = new ML_DSA(name2int(name));
+ ML_DSA.ML_DSA_KeyPair kp = mlDsa.generateKeyPairInternal(seed);
+ try {
+ return new byte[][]{
+ mlDsa.pkEncode(kp.publicKey()),
+ mlDsa.skEncode(kp.privateKey())
+ };
+ } finally {
+ kp.privateKey().destroy();
+ Arrays.fill(seed, (byte)0);
+ }
+ }
+ }
+
+ public final static class KPG2 extends KPG {
+ public KPG2() {
+ super("ML-DSA-44");
+ }
+ }
+
+ public final static class KPG3 extends KPG {
+ public KPG3() {
+ super("ML-DSA-65");
+ }
+ }
+
+ public final static class KPG5 extends KPG {
+ public KPG5() {
+ super("ML-DSA-87");
+ }
+ }
+
+ public sealed static class KF extends NamedKeyFactory permits KF2, KF3, KF5 {
+ public KF() {
+ super("ML-DSA", "ML-DSA-44", "ML-DSA-65", "ML-DSA-87");
+ }
+ public KF(String name) {
+ super("ML-DSA", name);
+ }
+ }
+
+ public final static class KF2 extends KF {
+ public KF2() {
+ super("ML-DSA-44");
+ }
+ }
+
+ public final static class KF3 extends KF {
+ public KF3() {
+ super("ML-DSA-65");
+ }
+ }
+
+ public final static class KF5 extends KF {
+ public KF5() {
+ super("ML-DSA-87");
+ }
+ }
+
+ public sealed static class SIG extends NamedSignature permits SIG2, SIG3, SIG5 {
+ public SIG() {
+ super("ML-DSA", "ML-DSA-44", "ML-DSA-65", "ML-DSA-87");
+ }
+ public SIG(String name) {
+ super("ML-DSA", name);
+ }
+
+ @Override
+ protected byte[] implSign(String name, byte[] skBytes,
+ Object sk2, byte[] msg, SecureRandom sr) {
+ var size = name2int(name);
+ var r = sr != null ? sr : JCAUtil.getDefSecureRandom();
+ byte[] rnd = new byte[32];
+ r.nextBytes(rnd);
+ var mlDsa = new ML_DSA(size);
+ if (version == Version.FINAL) {
+ // FIPS 204 Algorithm 2 ML-DSA.Sign prepend {0, len(ctx)}
+ // to message before passing it to Sign_internal.
+ var m = new byte[msg.length + 2];
+ System.arraycopy(msg, 0, m, 2, msg.length); // len(ctx) = 0
+ msg = m;
+ }
+ ML_DSA.ML_DSA_Signature sig = mlDsa.signInternal(msg, rnd, skBytes);
+ return mlDsa.sigEncode(sig);
+ }
+
+ @Override
+ protected boolean implVerify(String name, byte[] pkBytes,
+ Object pk2, byte[] msg, byte[] sigBytes)
+ throws SignatureException {
+ var size = name2int(name);
+ var mlDsa = new ML_DSA(size);
+ if (version == Version.FINAL) {
+ // FIPS 204 Algorithm 3 ML-DSA.Verify prepend {0, len(ctx)}
+ // to message before passing it to Verify_internal.
+ var m = new byte[msg.length + 2];
+ System.arraycopy(msg, 0, m, 2, msg.length); // len(ctx) = 0
+ msg = m;
+ }
+ return mlDsa.verifyInternal(pkBytes, msg, sigBytes);
+ }
+
+ @Override
+ protected Object implCheckPublicKey(String name, byte[] pk)
+ throws InvalidKeyException {
+
+ ML_DSA mlDsa = new ML_DSA(name2int(name));
+ return mlDsa.checkPublicKey(pk);
+ }
+
+ @Override
+ protected Object implCheckPrivateKey(String name, byte[] sk)
+ throws InvalidKeyException {
+
+ ML_DSA mlDsa = new ML_DSA(name2int(name));
+ return mlDsa.checkPrivateKey(sk);
+ }
+ }
+
+ public final static class SIG2 extends SIG {
+ public SIG2() {
+ super("ML-DSA-44");
+ }
+ }
+
+ public final static class SIG3 extends SIG {
+ public SIG3() {
+ super("ML-DSA-65");
+ }
+ }
+
+ public final static class SIG5 extends SIG {
+ public SIG5() {
+ super("ML-DSA-87");
+ }
+ }
+}
diff --git a/src/java.base/share/classes/sun/security/provider/NamedKEM.java b/src/java.base/share/classes/sun/security/provider/NamedKEM.java
new file mode 100644
index 000000000000..2731b3460af3
--- /dev/null
+++ b/src/java.base/share/classes/sun/security/provider/NamedKEM.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (c) 2024, 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 sun.security.pkcs.NamedPKCS8Key;
+import sun.security.x509.NamedX509Key;
+
+import javax.crypto.DecapsulateException;
+import javax.crypto.KEM;
+import javax.crypto.KEMSpi;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.PrivateKey;
+import java.security.ProviderException;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.NamedParameterSpec;
+import java.util.Arrays;
+import java.util.Objects;
+
+/// A base class for all `KEM` implementations that can be
+/// configured with a named parameter set. See [NamedKeyPairGenerator]
+/// for more details.
+public abstract class NamedKEM implements KEMSpi {
+
+ private final String fname; // family name
+ private final String[] pnames; // allowed parameter set name (at least one)
+
+ /// Creates a new `NamedKEM` object.
+ ///
+ /// @param fname the family name
+ /// @param pnames the standard parameter set names, at least one is needed.
+ protected NamedKEM(String fname, String... pnames) {
+ if (fname == null) {
+ throw new AssertionError("fname cannot be null");
+ }
+ if (pnames == null || pnames.length == 0) {
+ throw new AssertionError("pnames cannot be null or empty");
+ }
+ this.fname = fname;
+ this.pnames = pnames;
+ }
+
+ @Override
+ public EncapsulatorSpi engineNewEncapsulator(PublicKey publicKey,
+ AlgorithmParameterSpec spec, SecureRandom secureRandom)
+ throws InvalidAlgorithmParameterException, InvalidKeyException {
+ if (spec != null) {
+ throw new InvalidAlgorithmParameterException(
+ "The " + fname + " algorithm does not take any parameters");
+ }
+ // translate also check the key
+ var nk = (NamedX509Key) new NamedKeyFactory(fname, pnames)
+ .engineTranslateKey(publicKey);
+ var pk = nk.getRawBytes();
+ return getKeyConsumerImpl(this, nk.getParams(), pk,
+ implCheckPublicKey(nk.getParams().getName(), pk), secureRandom);
+ }
+
+ @Override
+ public DecapsulatorSpi engineNewDecapsulator(
+ PrivateKey privateKey, AlgorithmParameterSpec spec)
+ throws InvalidAlgorithmParameterException, InvalidKeyException {
+ if (spec != null) {
+ throw new InvalidAlgorithmParameterException(
+ "The " + fname + " algorithm does not take any parameters");
+ }
+ // translate also check the key
+ var nk = (NamedPKCS8Key) new NamedKeyFactory(fname, pnames)
+ .engineTranslateKey(privateKey);
+ var sk = nk.getRawBytes();
+ return getKeyConsumerImpl(this, nk.getParams(), sk,
+ implCheckPrivateKey(nk.getParams().getName(), sk), null);
+ }
+
+ // We don't have a flag on whether key is public key or private key.
+ // The correct method should always be called.
+ private record KeyConsumerImpl(NamedKEM kem, String name, int sslen,
+ int clen, byte[] key, Object k2, SecureRandom sr)
+ implements KEMSpi.EncapsulatorSpi, KEMSpi.DecapsulatorSpi {
+ @Override
+ public SecretKey engineDecapsulate(byte[] encapsulation, int from, int to,
+ String algorithm) throws DecapsulateException {
+ if (encapsulation.length != clen) {
+ throw new DecapsulateException("Invalid key encapsulation message length");
+ }
+ var ss = kem.implDecapsulate(name, key, k2, encapsulation);
+ try {
+ return new SecretKeySpec(ss,
+ from, to - from, algorithm);
+ } finally {
+ Arrays.fill(ss, (byte)0);
+ }
+ }
+
+ @Override
+ public KEM.Encapsulated engineEncapsulate(int from, int to, String algorithm) {
+ var enc = kem.implEncapsulate(name, key, k2, sr);
+ try {
+ return new KEM.Encapsulated(
+ new SecretKeySpec(enc[1],
+ from, to - from, algorithm),
+ enc[0],
+ null);
+ } finally {
+ Arrays.fill(enc[1], (byte)0);
+ }
+ }
+
+ @Override
+ public int engineSecretSize() {
+ return sslen;
+ }
+
+ @Override
+ public int engineEncapsulationSize() {
+ return clen;
+ }
+ }
+
+ private static KeyConsumerImpl getKeyConsumerImpl(NamedKEM kem,
+ NamedParameterSpec nps, byte[] key, Object k2, SecureRandom sr) {
+ String name = nps.getName();
+ return new KeyConsumerImpl(kem, name, kem.implSecretSize(name), kem.implEncapsulationSize(name),
+ key, k2, sr);
+ }
+
+ /// User-defined encap function.
+ ///
+ /// @param name parameter name
+ /// @param pk public key in raw bytes
+ /// @param pk2 parsed public key, `null` if none. See [#implCheckPublicKey].
+ /// @param sr SecureRandom object, `null` if not initialized
+ /// @return the key encapsulation message and the shared key (in this order)
+ /// @throws ProviderException if there is an internal error
+ protected abstract byte[][] implEncapsulate(String name, byte[] pk, Object pk2, SecureRandom sr);
+
+ /// User-defined decap function.
+ ///
+ /// @param name parameter name
+ /// @param sk private key in raw bytes
+ /// @param sk2 parsed private key, `null` if none. See [#implCheckPrivateKey].
+ /// @param encap the key encapsulation message
+ /// @return the shared key
+ /// @throws ProviderException if there is an internal error
+ /// @throws DecapsulateException if there is another error
+ protected abstract byte[] implDecapsulate(String name, byte[] sk, Object sk2, byte[] encap)
+ throws DecapsulateException;
+
+ /// User-defined function returning shared secret key length.
+ ///
+ /// @param name parameter name
+ /// @return shared secret key length
+ /// @throws ProviderException if there is an internal error
+ protected abstract int implSecretSize(String name);
+
+ /// User-defined function returning key encapsulation message length.
+ ///
+ /// @param name parameter name
+ /// @return key encapsulation message length
+ /// @throws ProviderException if there is an internal error
+ protected abstract int implEncapsulationSize(String name);
+
+ /// User-defined function to validate a public key.
+ ///
+ /// This method will be called in `newEncapsulator`. This gives the provider a chance to
+ /// reject the key so an `InvalidKeyException` can be thrown earlier.
+ /// An implementation can optionally return a "parsed key" as an `Object` value.
+ /// This object will be passed into the [#implEncapsulate] method along with the raw key.
+ ///
+ /// The default implementation returns `null`.
+ ///
+ /// @param name parameter name
+ /// @param pk public key in raw bytes
+ /// @return a parsed key, `null` if none.
+ /// @throws InvalidKeyException if the key is invalid
+ protected Object implCheckPublicKey(String name, byte[] pk) throws InvalidKeyException {
+ return null;
+ }
+
+ /// User-defined function to validate a private key.
+ ///
+ /// This method will be called in `newDecapsulator`. This gives the provider a chance to
+ /// reject the key so an `InvalidKeyException` can be thrown earlier.
+ /// An implementation can optionally return a "parsed key" as an `Object` value.
+ /// This object will be passed into the [#implDecapsulate] method along with the raw key.
+ ///
+ /// The default implementation returns `null`.
+ ///
+ /// @param name parameter name
+ /// @param sk private key in raw bytes
+ /// @return a parsed key, `null` if none.
+ /// @throws InvalidKeyException if the key is invalid
+ protected Object implCheckPrivateKey(String name, byte[] sk) throws InvalidKeyException {
+ return null;
+ }
+}
diff --git a/src/java.base/share/classes/sun/security/provider/NamedKeyFactory.java b/src/java.base/share/classes/sun/security/provider/NamedKeyFactory.java
new file mode 100644
index 000000000000..f7be5905a533
--- /dev/null
+++ b/src/java.base/share/classes/sun/security/provider/NamedKeyFactory.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright (c) 2024, 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 sun.security.pkcs.NamedPKCS8Key;
+import sun.security.util.KeyUtil;
+import sun.security.util.RawKeySpec;
+import sun.security.x509.NamedX509Key;
+
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.KeyFactorySpi;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.spec.EncodedKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
+import java.security.spec.NamedParameterSpec;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.Arrays;
+import java.util.Objects;
+
+/// A base class for all `KeyFactory` implementations that can be
+/// configured with a named parameter set. See [NamedKeyPairGenerator]
+/// for more details.
+///
+/// This factory supports reading and writing to RAW formats:
+///
+/// 1. It reads from a RAW key using `translateKey` if `key.getFormat` is "RAW".
+/// 2. It writes to a RAW [EncodedKeySpec] if `getKeySpec(key, EncodedKeySpec.class)`
+/// is called. The format of the output is "RAW" and the algorithm is
+/// intentionally left unspecified.
+/// 3. It reads from and writes to the internal type [RawKeySpec].
+///
+/// When reading from a RAW format, it needs enough info to derive the
+/// parameter set name.
+public class NamedKeyFactory extends KeyFactorySpi {
+
+ private final String fname; // family name
+ private final String[] pnames; // allowed parameter set name (at least one)
+
+ /// Creates a new `NamedKeyFactory` object.
+ ///
+ /// @param fname the family name
+ /// @param pnames the standard parameter set names, at least one is needed.
+ protected NamedKeyFactory(String fname, String... pnames) {
+ if (fname == null) {
+ throw new AssertionError("fname cannot be null");
+ }
+ if (pnames == null || pnames.length == 0) {
+ throw new AssertionError("pnames cannot be null or empty");
+ }
+ this.fname = fname;
+ this.pnames = pnames;
+ }
+
+ private String checkName(String name) throws InvalidKeyException {
+ for (var pname : pnames) {
+ if (pname.equalsIgnoreCase(name)) {
+ // return the stored standard name
+ return pname;
+ }
+ }
+ throw new InvalidKeyException("Unsupported parameter set name: " + name);
+ }
+
+ @Override
+ protected PublicKey engineGeneratePublic(KeySpec keySpec)
+ throws InvalidKeySpecException {
+ if (keySpec instanceof X509EncodedKeySpec xspec) {
+ try {
+ return fromX509(xspec.getEncoded());
+ } catch (InvalidKeyException e) {
+ throw new InvalidKeySpecException(e);
+ }
+ } else if (keySpec instanceof RawKeySpec rks) {
+ if (pnames.length == 1) {
+ return new NamedX509Key(fname, pnames[0], rks.getKeyArr());
+ } else {
+ throw new InvalidKeySpecException("Parameter set name unavailable");
+ }
+ } else if (keySpec instanceof EncodedKeySpec espec
+ && espec.getFormat().equalsIgnoreCase("RAW")) {
+ if (pnames.length == 1) {
+ return new NamedX509Key(fname, pnames[0], espec.getEncoded());
+ } else {
+ throw new InvalidKeySpecException("Parameter set name unavailable");
+ }
+ } else {
+ throw new InvalidKeySpecException("Unsupported keyspec: " + keySpec);
+ }
+ }
+
+ @Override
+ protected PrivateKey engineGeneratePrivate(KeySpec keySpec)
+ throws InvalidKeySpecException {
+ if (keySpec instanceof PKCS8EncodedKeySpec pspec) {
+ var bytes = pspec.getEncoded();
+ try {
+ return fromPKCS8(bytes);
+ } catch (InvalidKeyException e) {
+ throw new InvalidKeySpecException(e);
+ } finally {
+ Arrays.fill(bytes, (byte) 0);
+ }
+ } else if (keySpec instanceof RawKeySpec rks) {
+ if (pnames.length == 1) {
+ var bytes = rks.getKeyArr();
+ try {
+ return new NamedPKCS8Key(fname, pnames[0], bytes);
+ } finally {
+ Arrays.fill(bytes, (byte) 0);
+ }
+ } else {
+ throw new InvalidKeySpecException("Parameter set name unavailable");
+ }
+ } else if (keySpec instanceof EncodedKeySpec espec
+ && espec.getFormat().equalsIgnoreCase("RAW")) {
+ if (pnames.length == 1) {
+ var bytes = espec.getEncoded();
+ try {
+ return new NamedPKCS8Key(fname, pnames[0], bytes);
+ } finally {
+ Arrays.fill(bytes, (byte) 0);
+ }
+ } else {
+ throw new InvalidKeySpecException("Parameter set name unavailable");
+ }
+ } else {
+ throw new InvalidKeySpecException("Unsupported keyspec: " + keySpec);
+ }
+ }
+
+ private PrivateKey fromPKCS8(byte[] bytes)
+ throws InvalidKeyException, InvalidKeySpecException {
+ var k = new NamedPKCS8Key(fname, bytes);
+ checkName(k.getParams().getName());
+ return k;
+ }
+
+ private PublicKey fromX509(byte[] bytes)
+ throws InvalidKeyException, InvalidKeySpecException {
+ var k = new NamedX509Key(fname, bytes);
+ checkName(k.getParams().getName());
+ return k;
+ }
+
+ private static class RawEncodedKeySpec extends EncodedKeySpec {
+ public RawEncodedKeySpec(byte[] encodedKey) {
+ super(encodedKey);
+ }
+
+ @Override
+ public String getFormat() {
+ return "RAW";
+ }
+ }
+
+ @Override
+ protected T engineGetKeySpec(Key key, Class keySpec)
+ throws InvalidKeySpecException {
+ try {
+ key = engineTranslateKey(key);
+ } catch (InvalidKeyException e) {
+ throw new InvalidKeySpecException(e);
+ }
+ // key is now either NamedPKCS8Key or NamedX509Key of permitted param set
+ if (key instanceof NamedPKCS8Key nk) {
+ byte[] bytes = null;
+ try {
+ if (keySpec == PKCS8EncodedKeySpec.class) {
+ return keySpec.cast(
+ new PKCS8EncodedKeySpec(bytes = key.getEncoded()));
+ } else if (keySpec == RawKeySpec.class) {
+ return keySpec.cast(new RawKeySpec(nk.getRawBytes()));
+ } else if (keySpec.isAssignableFrom(EncodedKeySpec.class)) {
+ return keySpec.cast(
+ new RawEncodedKeySpec(nk.getRawBytes()));
+ } else {
+ throw new InvalidKeySpecException("Unsupported type: " + keySpec);
+ }
+ } finally {
+ if (bytes != null) {
+ Arrays.fill(bytes, (byte)0);
+ }
+ }
+ } else if (key instanceof NamedX509Key nk) {
+ if (keySpec == X509EncodedKeySpec.class
+ && key.getFormat().equalsIgnoreCase("X.509")) {
+ return keySpec.cast(new X509EncodedKeySpec(key.getEncoded()));
+ } else if (keySpec == RawKeySpec.class) {
+ return keySpec.cast(new RawKeySpec(nk.getRawBytes()));
+ } else if (keySpec.isAssignableFrom(EncodedKeySpec.class)) {
+ return keySpec.cast(new RawEncodedKeySpec(nk.getRawBytes()));
+ } else {
+ throw new InvalidKeySpecException("Unsupported type: " + keySpec);
+ }
+ }
+ throw new AssertionError("No " + keySpec.getName() + " for " + key.getClass());
+ }
+
+ @Override
+ protected Key engineTranslateKey(Key key) throws InvalidKeyException {
+ if (key == null) {
+ throw new InvalidKeyException("Key must not be null");
+ }
+ if (key instanceof NamedX509Key nk) {
+ checkName(nk.getParams().getName());
+ return key;
+ }
+ if (key instanceof NamedPKCS8Key nk) {
+ checkName(nk.getParams().getName());
+ return key;
+ }
+ var format = key.getFormat();
+ if (format == null) {
+ throw new InvalidKeyException("Unextractable key");
+ } else if (format.equalsIgnoreCase("RAW")) {
+ var kAlg = key.getAlgorithm();
+ if (key instanceof PrivateKey || key instanceof PublicKey) {
+ String name;
+ // Three cases that we can find the parameter set name from a RAW key:
+ // 1. getParams() returns one
+ // 2. getAlgorithm() returns param set name (some provider does this)
+ // 3. getAlgorithm() returns family name but this KF is for param set name
+ if (KeyUtil.getParams(key) instanceof NamedParameterSpec nps) {
+ name = checkName(nps.getName());
+ } else {
+ if (kAlg.equalsIgnoreCase(fname)) {
+ if (pnames.length == 1) {
+ name = pnames[0];
+ } else {
+ throw new InvalidKeyException("No parameter set info");
+ }
+ } else {
+ name = checkName(kAlg);
+ }
+ }
+ return key instanceof PrivateKey
+ ? new NamedPKCS8Key(fname, name, key.getEncoded())
+ : new NamedX509Key(fname, name, key.getEncoded());
+ } else {
+ throw new InvalidKeyException("Unsupported key type: " + key.getClass());
+ }
+ } else if (format.equalsIgnoreCase("PKCS#8") && key instanceof PrivateKey) {
+ var bytes = key.getEncoded();
+ try {
+ return fromPKCS8(bytes);
+ } catch (InvalidKeySpecException e) {
+ throw new InvalidKeyException("Invalid PKCS#8 key", e);
+ } finally {
+ Arrays.fill(bytes, (byte) 0);
+ }
+ } else if (format.equalsIgnoreCase("X.509") && key instanceof PublicKey) {
+ try {
+ return fromX509(key.getEncoded());
+ } catch (InvalidKeySpecException e) {
+ throw new InvalidKeyException("Invalid X.509 key", e);
+ }
+ } else {
+ throw new InvalidKeyException("Unsupported key format: " + key.getFormat());
+ }
+ }
+}
diff --git a/src/java.base/share/classes/sun/security/provider/NamedKeyPairGenerator.java b/src/java.base/share/classes/sun/security/provider/NamedKeyPairGenerator.java
new file mode 100644
index 000000000000..5be2b2b2a08b
--- /dev/null
+++ b/src/java.base/share/classes/sun/security/provider/NamedKeyPairGenerator.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2024, 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 sun.security.pkcs.NamedPKCS8Key;
+import sun.security.x509.NamedX509Key;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidParameterException;
+import java.security.KeyPair;
+import java.security.KeyPairGeneratorSpi;
+import java.security.ProviderException;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.NamedParameterSpec;
+import java.util.Objects;
+
+/// A base class for all `KeyPairGenerator` implementations that can be
+/// configured with a named parameter set.
+///
+/// Together with [NamedKeyFactory], [NamedKEM], and [NamedSignature], these
+/// classes form a compact framework designed to support any public key
+/// algorithm standardized with named parameter sets. In this scenario,
+/// the algorithm name is the "family name" and each standardized parameter
+/// set has a "parameter set name". Implementations of these classes are able
+/// to instantiate a `KeyPairGenerator`, `KeyFactory`, or `KEM` or `Signature`
+/// object using either the family name or a parameter set name. All keys used
+/// in this context will be of the type [NamedPKCS8Key] or [NamedX509Key],
+/// with `getAlgorithm` returning the family name, and `getParams` returning
+/// the parameter set name as a [NamedParameterSpec] object.
+///
+/// An implementation must include a zero-argument public constructor that
+/// calls `super(fname, pnames)`, where `fname` is the family name of the
+/// algorithm and `pnames` are its supported parameter set names. `pnames`
+/// must contain at least one element. For an implementation of
+/// `NamedKeyPairGenerator`, the first element becomes its default parameter
+/// set, i.e. the parameter set to be used in key pair generation unless
+/// [#initialize(AlgorithmParameterSpec, java.security.SecureRandom)]
+/// is called on a different parameter set.
+///
+/// An implementation must implement all abstract methods. For all these
+/// methods, the implementation must relinquish any "ownership" of any input
+/// and output array argument. Precisely, the implementation must not retain
+/// any reference to a returning array so that it won't be able to modify its
+/// content later. Similarly, the implementation must not modify any input
+/// array argument and must not retain any reference to an input array argument
+/// after the call.
+///
+/// Also, an implementation must not keep any extra copy of a private key.
+/// For key generation, the only copy is the one returned in the
+/// [#implGenerateKeyPair] call. For all other methods, it must not make
+/// a copy of the input private key. A `KEM` implementation also must not
+/// keep a copy of the shared secret key, no matter if it's an encapsulator
+/// or a decapsulator. Only the code that owns these sensitive data can
+/// choose to perform cleanup when it determines they are no longer needed.
+///
+/// The `NamedSignature` and `NamedKEM` classes provide `implCheckPublicKey`
+/// and `implCheckPrivateKey` methods that allow an implementation to validate
+/// a key before using it. An implementation may return a parsed key in
+/// a local type, and this parsed key will be passed to an operational method
+/// (For example, `implSign`) later. An implementation must not retain
+/// a reference of the parsed key.
+///
+/// When constructing a [NamedX509Key] or [NamedPKCS8Key] object from raw key
+/// bytes, the key bytes are directly referenced within the object, so the
+/// caller must not modify them afterward. Similarly, the key's `getRawBytes`
+/// method returns direct references to the underlying raw key bytes, meaning
+/// the caller must not alter the contents of the returned value.
+///
+/// Together, these measures ensure the classes are as efficient as possible,
+/// preventing unnecessary array cloning and potential data leaks. While these
+/// classes should not be considered immutable, strictly adhering to the rules
+/// above will ensure data integrity is maintained.
+///
+/// Note: A limitation of `NamedKeyPairGenerator` and `NamedKeyFactory` is
+/// that the keys generated by their implementations will always be of type
+/// `NamedX509Key` or `NamedPKCS8Key`. Existing implementations of algorithms
+/// like EdDSA and XDH have been generating keys implementing `EdECKey` or
+/// `XECKey` interfaces, and they are not rewritten with this framework.
+/// `NamedParameterSpec` fields not implemented with this framework include
+/// Ed25519, Ed448, X25519, and X448.
+public abstract class NamedKeyPairGenerator extends KeyPairGeneratorSpi {
+
+ private final String fname; // family name
+ private final String[] pnames; // allowed parameter set name (at least one)
+
+ protected String name; // init as
+ private SecureRandom secureRandom;
+
+ /// Creates a new `NamedKeyPairGenerator` object.
+ ///
+ /// @param fname the family name
+ /// @param pnames supported parameter set names, at least one is needed.
+ /// If multiple, the first one becomes the default parameter set name.
+ protected NamedKeyPairGenerator(String fname, String... pnames) {
+ if (fname == null) {
+ throw new AssertionError("fname cannot be null");
+ }
+ if (pnames == null || pnames.length == 0) {
+ throw new AssertionError("pnames cannot be null or empty");
+ }
+ this.fname = fname;
+ this.pnames = pnames;
+ }
+
+ private String checkName(String name) throws InvalidAlgorithmParameterException {
+ for (var pname : pnames) {
+ if (pname.equalsIgnoreCase(name)) {
+ // return the stored standard name
+ return pname;
+ }
+ }
+ throw new InvalidAlgorithmParameterException(
+ "Unsupported parameter set name: " + name);
+ }
+
+ @Override
+ public void initialize(AlgorithmParameterSpec params, SecureRandom random)
+ throws InvalidAlgorithmParameterException {
+ if (params instanceof NamedParameterSpec spec) {
+ name = checkName(spec.getName());
+ } else {
+ throw new InvalidAlgorithmParameterException(
+ "Unsupported AlgorithmParameterSpec: " + params);
+ }
+ this.secureRandom = random;
+ }
+
+ @Override
+ public void initialize(int keysize, SecureRandom random) {
+ if (keysize != -1) {
+ // User can call initialize(-1, sr) to provide a SecureRandom
+ // without touching the parameter set currently used
+ throw new InvalidParameterException("keysize not supported");
+ }
+ this.secureRandom = random;
+ }
+
+ @Override
+ public KeyPair generateKeyPair() {
+ String pname = name != null ? name : pnames[0];
+ var keys = implGenerateKeyPair(pname, secureRandom);
+ return new KeyPair(new NamedX509Key(fname, pname, keys[0]),
+ new NamedPKCS8Key(fname, pname, keys[1]));
+ }
+
+ /// User-defined key pair generator.
+ ///
+ /// @param pname parameter set name
+ /// @param sr `SecureRandom` object, `null` if not initialized
+ /// @return public key and private key (in this order) in raw bytes
+ /// @throws ProviderException if there is an internal error
+ protected abstract byte[][] implGenerateKeyPair(String pname, SecureRandom sr);
+}
diff --git a/src/java.base/share/classes/sun/security/provider/NamedSignature.java b/src/java.base/share/classes/sun/security/provider/NamedSignature.java
new file mode 100644
index 000000000000..921a39cfc926
--- /dev/null
+++ b/src/java.base/share/classes/sun/security/provider/NamedSignature.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 2024, 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 sun.security.pkcs.NamedPKCS8Key;
+import sun.security.x509.NamedX509Key;
+
+import java.io.ByteArrayOutputStream;
+import java.security.AlgorithmParameters;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.InvalidParameterException;
+import java.security.PrivateKey;
+import java.security.ProviderException;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.SignatureException;
+import java.security.SignatureSpi;
+import java.security.spec.AlgorithmParameterSpec;
+import java.util.Objects;
+
+/// A base class for all `Signature` implementations that can be
+/// configured with a named parameter set. See [NamedKeyPairGenerator]
+/// for more details.
+///
+/// This class does not work with preHash signatures.
+public abstract class NamedSignature extends SignatureSpi {
+
+ private final String fname; // family name
+ private final String[] pnames; // allowed parameter set name (at least one)
+
+ private final ByteArrayOutputStream bout = new ByteArrayOutputStream();
+
+ // init with...
+ private String name;
+ private byte[] secKey;
+ private byte[] pubKey;
+
+ private Object sk2;
+ private Object pk2;
+
+ /// Creates a new `NamedSignature` object.
+ ///
+ /// @param fname the family name
+ /// @param pnames the standard parameter set names, at least one is needed.
+ protected NamedSignature(String fname, String... pnames) {
+ if (fname == null) {
+ throw new AssertionError("fname cannot be null");
+ }
+ if (pnames == null || pnames.length == 0) {
+ throw new AssertionError("pnames cannot be null or empty");
+ }
+ this.fname = fname;
+ this.pnames = pnames;
+ }
+
+ @Override
+ protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException {
+ // translate also check the key
+ var nk = (NamedX509Key) new NamedKeyFactory(fname, pnames)
+ .engineTranslateKey(publicKey);
+ name = nk.getParams().getName();
+ pubKey = nk.getRawBytes();
+ pk2 = implCheckPublicKey(name, pubKey);
+ secKey = null;
+ bout.reset();
+ }
+
+ @Override
+ protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException {
+ // translate also check the key
+ var nk = (NamedPKCS8Key) new NamedKeyFactory(fname, pnames)
+ .engineTranslateKey(privateKey);
+ name = nk.getParams().getName();
+ secKey = nk.getRawBytes();
+ sk2 = implCheckPrivateKey(name, secKey);
+ pubKey = null;
+ bout.reset();
+ }
+
+ @Override
+ protected void engineUpdate(byte b) throws SignatureException {
+ bout.write(b);
+ }
+
+ @Override
+ protected void engineUpdate(byte[] b, int off, int len) throws SignatureException {
+ bout.write(b, off, len);
+ }
+
+ @Override
+ protected byte[] engineSign() throws SignatureException {
+ if (secKey != null) {
+ var msg = bout.toByteArray();
+ bout.reset();
+ return implSign(name, secKey, sk2, msg, appRandom);
+ } else {
+ throw new SignatureException("No private key");
+ }
+ }
+
+ @Override
+ protected boolean engineVerify(byte[] sig) throws SignatureException {
+ if (pubKey != null) {
+ var msg = bout.toByteArray();
+ bout.reset();
+ return implVerify(name, pubKey, pk2, msg, sig);
+ } else {
+ throw new SignatureException("No public key");
+ }
+ }
+
+ @Override
+ @SuppressWarnings("deprecation")
+ protected void engineSetParameter(String param, Object value)
+ throws InvalidParameterException {
+ throw new InvalidParameterException("setParameter() not supported");
+ }
+
+ @Override
+ @SuppressWarnings("deprecation")
+ protected Object engineGetParameter(String param) throws InvalidParameterException {
+ throw new InvalidParameterException("getParameter() not supported");
+ }
+
+ @Override
+ protected void engineSetParameter(AlgorithmParameterSpec params)
+ throws InvalidAlgorithmParameterException {
+ if (params != null) {
+ throw new InvalidAlgorithmParameterException(
+ "The " + fname + " algorithm does not take any parameters");
+ }
+ }
+
+ @Override
+ protected AlgorithmParameters engineGetParameters() {
+ return null;
+ }
+
+ /// User-defined sign function.
+ ///
+ /// @param name parameter name
+ /// @param sk private key in raw bytes
+ /// @param sk2 parsed private key, `null` if none. See [#implCheckPrivateKey].
+ /// @param msg the message
+ /// @param sr SecureRandom object, `null` if not initialized
+ /// @return the signature
+ /// @throws ProviderException if there is an internal error
+ /// @throws SignatureException if there is another error
+ protected abstract byte[] implSign(String name, byte[] sk, Object sk2,
+ byte[] msg, SecureRandom sr) throws SignatureException;
+
+ /// User-defined verify function.
+ ///
+ /// @param name parameter name
+ /// @param pk public key in raw bytes
+ /// @param pk2 parsed public key, `null` if none. See [#implCheckPublicKey].
+ /// @param msg the message
+ /// @param sig the signature
+ /// @return true if verified
+ /// @throws ProviderException if there is an internal error
+ /// @throws SignatureException if there is another error
+ protected abstract boolean implVerify(String name, byte[] pk, Object pk2,
+ byte[] msg, byte[] sig) throws SignatureException;
+
+ /// User-defined function to validate a public key.
+ ///
+ /// This method will be called in `initVerify`. This gives the provider a chance to
+ /// reject the key so an `InvalidKeyException` can be thrown earlier.
+ /// An implementation can optionally return a "parsed key" as an `Object` value.
+ /// This object will be passed into the [#implVerify] method along with the raw key.
+ ///
+ /// The default implementation returns `null`.
+ ///
+ /// @param name parameter name
+ /// @param pk public key in raw bytes
+ /// @return a parsed key, `null` if none.
+ /// @throws InvalidKeyException if the key is invalid
+ protected Object implCheckPublicKey(String name, byte[] pk) throws InvalidKeyException {
+ return null;
+ }
+
+ /// User-defined function to validate a private key.
+ ///
+ /// This method will be called in `initSign`. This gives the provider a chance to
+ /// reject the key so an `InvalidKeyException` can be thrown earlier.
+ /// An implementation can optionally return a "parsed key" as an `Object` value.
+ /// This object will be passed into the [#implSign] method along with the raw key.
+ ///
+ /// The default implementation returns `null`.
+ ///
+ /// @param name parameter name
+ /// @param sk private key in raw bytes
+ /// @return a parsed key, `null` if none.
+ /// @throws InvalidKeyException if the key is invalid
+ protected Object implCheckPrivateKey(String name, byte[] sk) throws InvalidKeyException {
+ return null;
+ }
+}
diff --git a/src/java.base/share/classes/sun/security/provider/SHA3.java b/src/java.base/share/classes/sun/security/provider/SHA3.java
index 2b8bf8afbeda..5f974bc6ea65 100644
--- a/src/java.base/share/classes/sun/security/provider/SHA3.java
+++ b/src/java.base/share/classes/sun/security/provider/SHA3.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2024, 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
@@ -25,13 +25,17 @@
package sun.security.provider;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+import java.nio.ByteOrder;
import java.security.ProviderException;
import java.util.Arrays;
import java.util.Objects;
+import jdk.internal.util.Preconditions;
import jdk.internal.vm.annotation.IntrinsicCandidate;
-import static sun.security.provider.ByteArrayAccess.b2lLittle;
-import static sun.security.provider.ByteArrayAccess.l2bLittle;
+
+import static java.lang.Math.min;
/**
* This class implements the Secure Hash Algorithm SHA-3 developed by
@@ -45,10 +49,10 @@
* @since 9
* @author Valerie Peng
*/
-abstract class SHA3 extends DigestBase {
+public abstract class SHA3 extends DigestBase {
private static final int WIDTH = 200; // in bytes, e.g. 1600 bits
- private static final int DM = 5; // dimension of lanes
+ private static final int DM = 5; // dimension of state matrix
private static final int NR = 24; // number of rounds
@@ -64,20 +68,39 @@ abstract class SHA3 extends DigestBase {
0x8000000000008080L, 0x80000001L, 0x8000000080008008L,
};
+ // The starting byte combining the 2 or 4-bit domain separator and
+ // leading bits of the 10*1 padding, see Table 6 in B.2 of FIPS PUB 202
+ // for examples
private final byte suffix;
- private byte[] state = new byte[WIDTH];
- private long[] lanes = new long[DM*DM];
+
+ // the state matrix flattened into an array
+ private long[] state = new long[DM*DM];
+
+ // The byte offset in the state where the next squeeze() will start.
+ // -1 indicates that either we are in the absorbing phase (only
+ // update() calls were made so far) in an extendable-output function (XOF)
+ // or the class was initialized as a hash.
+ // The first squeeze() call (after a possibly empty sequence of update()
+ // calls) will set it to 0 at its start.
+ // When a squeeze() call uses up all available bytes from this state
+ // and so a new keccak() call is made, squeezeOffset is reset to 0.
+ protected int squeezeOffset = -1;
+
+ static final VarHandle asLittleEndian
+ = MethodHandles.byteArrayViewVarHandle(long[].class,
+ ByteOrder.LITTLE_ENDIAN).withInvokeExactBehavior();
/**
* Creates a new SHA-3 object.
*/
- SHA3(String name, int digestLength, byte suffix, int c) {
+ private SHA3(String name, int digestLength, byte suffix, int c) {
super(name, digestLength, (WIDTH - c));
this.suffix = suffix;
}
private void implCompressCheck(byte[] b, int ofs) {
Objects.requireNonNull(b);
+ Preconditions.checkIndex(ofs + blockSize - 1, b.length, Preconditions.AIOOBE_FORMATTER);
}
/**
@@ -91,10 +114,21 @@ void implCompress(byte[] b, int ofs) {
@IntrinsicCandidate
private void implCompress0(byte[] b, int ofs) {
- for (int i = 0; i < buffer.length; i++) {
- state[i] ^= b[ofs++];
- }
- keccak();
+ for (int i = 0; i < blockSize / 8; i++) {
+ state[i] ^= (long) asLittleEndian.get(b, ofs);
+ ofs += 8;
+ }
+
+ keccak();
+ }
+
+ void finishAbsorb() {
+ int numOfPadding =
+ setPaddingBytes(suffix, buffer, (int)(bytesProcessed % blockSize));
+ if (numOfPadding < 1) {
+ throw new ProviderException("Incorrect pad size: " + numOfPadding);
+ }
+ implCompress(buffer, 0);
}
/**
@@ -102,35 +136,127 @@ private void implCompress0(byte[] b, int ofs) {
* DigestBase calls implReset() when necessary.
*/
void implDigest(byte[] out, int ofs) {
- int numOfPadding =
- setPaddingBytes(suffix, buffer, (int)(bytesProcessed % buffer.length));
- if (numOfPadding < 1) {
- throw new ProviderException("Incorrect pad size: " + numOfPadding);
+ // Moving this allocation to the block where it is used causes a little
+ // performance drop, that is why it is here.
+ byte[] byteState = new byte[8];
+ if (engineGetDigestLength() == 0) {
+ // This is an XOF, so the digest() call is illegal.
+ throw new ProviderException("Calling digest() is not allowed in an XOF");
}
- implCompress(buffer, 0);
- int availableBytes = buffer.length;
+
+ finishAbsorb();
+
+ int availableBytes = blockSize;
int numBytes = engineGetDigestLength();
+
while (numBytes > availableBytes) {
- System.arraycopy(state, 0, out, ofs, availableBytes);
+ for (int i = 0; i < availableBytes / 8; i++) {
+ asLittleEndian.set(out, ofs, state[i]);
+ ofs += 8;
+ }
numBytes -= availableBytes;
- ofs += availableBytes;
keccak();
}
- System.arraycopy(state, 0, out, ofs, numBytes);
+ int numLongs = numBytes / 8;
+
+ for (int i = 0; i < numLongs; i++) {
+ asLittleEndian.set(out, ofs, state[i]);
+ ofs += 8;
+ }
+ if (numBytes % 8 != 0) {
+ asLittleEndian.set(byteState, 0, state[numLongs]);
+ System.arraycopy(byteState, 0, out, ofs, numBytes % 8);
+ }
+ }
+
+ void implSqueeze(byte[] output, int offset, int numBytes) {
+ // Moving this allocation to the block where it is used causes a little
+ // performance drop, that is why it is here.
+ byte[] byteState = new byte[8];
+ if (engineGetDigestLength() != 0) {
+ // This is not an XOF, so the squeeze() call is illegal.
+ throw new ProviderException("Squeezing is only allowed in XOF mode.");
+ }
+
+ if (squeezeOffset == -1) {
+ finishAbsorb();
+ squeezeOffset = 0;
+ }
+
+ int availableBytes = blockSize - squeezeOffset;
+
+ while (numBytes > availableBytes) {
+ int longOffset = squeezeOffset / 8;
+ int bytesToCopy = 0;
+
+ if (longOffset * 8 < squeezeOffset) {
+ asLittleEndian.set(byteState, 0, state[longOffset]);
+ longOffset++;
+ bytesToCopy = longOffset * 8 - squeezeOffset;
+ System.arraycopy(byteState, 8 - bytesToCopy,
+ output, offset, bytesToCopy);
+ offset += bytesToCopy;
+ }
+ for (int i = longOffset; i < blockSize / 8; i++) {
+ asLittleEndian.set(output, offset, state[i]);
+ offset += 8;
+ }
+ keccak();
+ squeezeOffset = 0;
+ numBytes -= availableBytes;
+ availableBytes = blockSize;
+ }
+ // now numBytes <= availableBytes
+ int longOffset = squeezeOffset / 8;
+
+ if (longOffset * 8 < squeezeOffset) {
+ asLittleEndian.set(byteState, 0, state[longOffset]);
+ int bytesToCopy = min((longOffset + 1) * 8 - squeezeOffset, numBytes);
+ System.arraycopy(byteState, squeezeOffset - 8 * longOffset,
+ output, offset, bytesToCopy);
+ longOffset++;
+ numBytes -= bytesToCopy;
+ offset += bytesToCopy;
+ squeezeOffset += bytesToCopy;
+
+ if (numBytes == 0) return;
+ }
+
+ int numLongs = numBytes / 8;
+
+ for (int i = longOffset; i < longOffset + numLongs; i++) {
+ asLittleEndian.set(output, offset, state[i]);
+ offset += 8;
+ numBytes -= 8;
+ squeezeOffset += 8;
+ }
+
+ if (numBytes > 0) {
+ asLittleEndian.set(byteState, 0, state[squeezeOffset / 8]);
+ System.arraycopy(byteState, 0, output, offset, numBytes);
+ squeezeOffset += numBytes;
+ }
+ }
+
+ byte[] implSqueeze(int numBytes) {
+ byte[] result = new byte[numBytes];
+ implSqueeze(result, 0, numBytes);
+ return result;
}
/**
* Resets the internal state to start a new hash.
*/
void implReset() {
- Arrays.fill(state, (byte)0);
- Arrays.fill(lanes, 0L);
+ Arrays.fill(state, 0L);
+ squeezeOffset = -1;
}
/**
* Utility function for padding the specified data based on the
- * pad10*1 algorithm (section 5.1) and the 2-bit suffix "01" required
- * for SHA-3 hash (section 6.1).
+ * pad10*1 algorithm (section 5.1) and the 2-bit suffix "01" or 4-bit
+ * suffix "1111" required for SHA-3 hash functions (section 6.1) and
+ * extendable-output functions (section 6.1) respectively.
*/
private static int setPaddingBytes(byte suffix, byte[] in, int len) {
if (len != in.length) {
@@ -144,48 +270,25 @@ private static int setPaddingBytes(byte suffix, byte[] in, int len) {
return (in.length - len);
}
- /**
- * Utility function for transforming the specified byte array 's'
- * into array of lanes 'm' as defined in section 3.1.2.
- */
- private static void bytes2Lanes(byte[] s, long[] m) {
- int sOfs = 0;
- // Conversion traverses along x-axis before y-axis
- for (int y = 0; y < DM; y++, sOfs += 40) {
- b2lLittle(s, sOfs, m, DM*y, 40);
- }
- }
-
- /**
- * Utility function for transforming the specified array of
- * lanes 'm' into a byte array 's' as defined in section 3.1.3.
- */
- private static void lanes2Bytes(long[] m, byte[] s) {
- int sOfs = 0;
- // Conversion traverses along x-axis before y-axis
- for (int y = 0; y < DM; y++, sOfs += 40) {
- l2bLittle(m, DM*y, s, sOfs, 40);
- }
- }
-
/**
* The function Keccak as defined in section 5.2 with
* rate r = 1600 and capacity c.
*/
private void keccak() {
- // convert the 200-byte state into 25 lanes
- bytes2Lanes(state, lanes);
+ keccak(state);
+ }
+ public static void keccak(long[] stateArr) {
long a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12;
long a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24;
// move data into local variables
- a0 = lanes[0]; a1 = lanes[1]; a2 = lanes[2]; a3 = lanes[3]; a4 = lanes[4];
- a5 = lanes[5]; a6 = lanes[6]; a7 = lanes[7]; a8 = lanes[8]; a9 = lanes[9];
- a10 = lanes[10]; a11 = lanes[11]; a12 = lanes[12]; a13 = lanes[13]; a14 = lanes[14];
- a15 = lanes[15]; a16 = lanes[16]; a17 = lanes[17]; a18 = lanes[18]; a19 = lanes[19];
- a20 = lanes[20]; a21 = lanes[21]; a22 = lanes[22]; a23 = lanes[23]; a24 = lanes[24];
+ a0 = stateArr[0]; a1 = stateArr[1]; a2 = stateArr[2]; a3 = stateArr[3]; a4 = stateArr[4];
+ a5 = stateArr[5]; a6 = stateArr[6]; a7 = stateArr[7]; a8 = stateArr[8]; a9 = stateArr[9];
+ a10 = stateArr[10]; a11 = stateArr[11]; a12 = stateArr[12]; a13 = stateArr[13]; a14 = stateArr[14];
+ a15 = stateArr[15]; a16 = stateArr[16]; a17 = stateArr[17]; a18 = stateArr[18]; a19 = stateArr[19];
+ a20 = stateArr[20]; a21 = stateArr[21]; a22 = stateArr[22]; a23 = stateArr[23]; a24 = stateArr[24];
- // process the lanes through step mappings
+ // process the stateArr through step mappings
for (int ir = 0; ir < NR; ir++) {
// Step mapping Theta as defined in section 3.2.1.
long c0 = a0^a5^a10^a15^a20;
@@ -287,20 +390,16 @@ private void keccak() {
a0 ^= RC_CONSTANTS[ir];
}
- lanes[0] = a0; lanes[1] = a1; lanes[2] = a2; lanes[3] = a3; lanes[4] = a4;
- lanes[5] = a5; lanes[6] = a6; lanes[7] = a7; lanes[8] = a8; lanes[9] = a9;
- lanes[10] = a10; lanes[11] = a11; lanes[12] = a12; lanes[13] = a13; lanes[14] = a14;
- lanes[15] = a15; lanes[16] = a16; lanes[17] = a17; lanes[18] = a18; lanes[19] = a19;
- lanes[20] = a20; lanes[21] = a21; lanes[22] = a22; lanes[23] = a23; lanes[24] = a24;
-
- // convert the resulting 25 lanes back into 200-byte state
- lanes2Bytes(lanes, state);
+ stateArr[0] = a0; stateArr[1] = a1; stateArr[2] = a2; stateArr[3] = a3; stateArr[4] = a4;
+ stateArr[5] = a5; stateArr[6] = a6; stateArr[7] = a7; stateArr[8] = a8; stateArr[9] = a9;
+ stateArr[10] = a10; stateArr[11] = a11; stateArr[12] = a12; stateArr[13] = a13; stateArr[14] = a14;
+ stateArr[15] = a15; stateArr[16] = a16; stateArr[17] = a17; stateArr[18] = a18; stateArr[19] = a19;
+ stateArr[20] = a20; stateArr[21] = a21; stateArr[22] = a22; stateArr[23] = a23; stateArr[24] = a24;
}
public Object clone() throws CloneNotSupportedException {
SHA3 copy = (SHA3) super.clone();
copy.state = copy.state.clone();
- copy.lanes = new long[DM*DM];
return copy;
}
@@ -339,4 +438,85 @@ public SHA512() {
super("SHA3-512", 64, (byte)0x06, 128);
}
}
+
+ public abstract static class SHA3XOF extends SHA3 {
+ public SHA3XOF(String name, int digestLength, byte offset, int c) {
+ super(name, digestLength, offset, c);
+ }
+ public void update(byte in) {
+ if (squeezeOffset != -1) {
+ throw new ProviderException("update() after squeeze() is not allowed.");
+ }
+ engineUpdate(in);
+ }
+ public void update(byte[] in, int off, int len) {
+ if (squeezeOffset != -1) {
+ throw new ProviderException("update() after squeeze() is not allowed.");
+ }
+ engineUpdate(in, off, len);
+ }
+
+ public void update(byte[] in) {
+ if (squeezeOffset != -1) {
+ throw new ProviderException("update() after squeeze() is not allowed.");
+ }
+ engineUpdate(in, 0, in.length);
+ }
+
+ public byte[] digest() {
+ return engineDigest();
+ }
+
+ public void squeeze(byte[] output, int offset, int numBytes) {
+ implSqueeze(output, offset, numBytes);
+ }
+ public byte[] squeeze(int numBytes) {
+ return implSqueeze(numBytes);
+ }
+
+ public void reset() {
+ engineReset();
+ }
+ }
+
+ /*
+ * The SHAKE128 extendable output function.
+ */
+ public static final class SHAKE128 extends SHA3XOF {
+ // d is the required number of output bytes.
+ // If this constructor is used with d > 0, the squeezing methods
+ // will throw a ProviderException.
+ public SHAKE128(int d) {
+ super("SHAKE128", d, (byte) 0x1F, 32);
+ }
+
+ // If this constructor is used to get an instance of the class, then,
+ // after the last update, one can get the generated bytes using the
+ // squeezing methods.
+ // Calling digest method will throw a ProviderException.
+ public SHAKE128() {
+ super("SHAKE128", 0, (byte) 0x1F, 32);
+ }
+ }
+
+ /*
+ * The SHAKE256 extendable output function.
+ */
+ public static final class SHAKE256 extends SHA3XOF {
+ // d is the required number of output bytes.
+ // If this constructor is used with d > 0, the squeezing methods will
+ // throw a ProviderException.
+ public SHAKE256(int d) {
+ super("SHAKE256", d, (byte) 0x1F, 64);
+ }
+
+ // If this constructor is used to get an instance of the class, then,
+ // after the last update, one can get the generated bytes using the
+ // squeezing methods.
+ // Calling a digest method will throw a ProviderException.
+ public SHAKE256() {
+ super("SHAKE256", 0, (byte) 0x1F, 64);
+ }
+ }
+
}
diff --git a/src/java.base/share/classes/sun/security/provider/SHA3Parallel.java b/src/java.base/share/classes/sun/security/provider/SHA3Parallel.java
new file mode 100644
index 000000000000..0851d4a9216b
--- /dev/null
+++ b/src/java.base/share/classes/sun/security/provider/SHA3Parallel.java
@@ -0,0 +1,108 @@
+/*
+ * 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 java.security.InvalidAlgorithmParameterException;
+import java.util.Arrays;
+
+import static sun.security.provider.ByteArrayAccess.b2lLittle;
+import static sun.security.provider.ByteArrayAccess.l2bLittle;
+
+import static sun.security.provider.SHA3.keccak;
+
+/*
+ * This class is for making it possible that NRPAR (= 2) (rather restricted)
+ * SHAKE computations execute in parallel.
+ * The restrictions are:
+ * 1. The messages processed should be such that the absorb phase should
+ * execute a single keccak() call and the byte arrays passed to the constructor
+ * (or reset() method) of this class should be the message padded with the
+ * appropriate padding described in
+ * https://nvlpubs.nist.gov/nistpubs/fips/nist.fips.202.pdf.
+ * 2. The only available way for extracting data is the squeeze() method
+ * that extracts exactly 1 block of data of each computation, delivering it
+ * in the arrays that were passed to the class in the constructor (or the
+ * reset() call).
+ */
+public class SHA3Parallel {
+ private int blockSize = 0;
+ private static final int DM = 5; // dimension of lanesArr
+ private byte[][] buffers;
+ private long[][] lanesArr;
+ private static final int NRPAR = 2;
+
+ private SHA3Parallel(byte[][] buffers, int blockSize) throws InvalidAlgorithmParameterException {
+ if ((buffers.length != NRPAR) || (buffers[0].length < blockSize)) {
+ throw new InvalidAlgorithmParameterException("Bad buffersize.");
+ }
+ this.buffers = buffers;
+ this.blockSize = blockSize;
+ lanesArr = new long[NRPAR][];
+ for (int i = 0; i < NRPAR; i++) {
+ lanesArr[i] = new long[DM * DM];
+ b2lLittle(buffers[i], 0, lanesArr[i], 0, blockSize);
+ }
+ }
+
+ public void reset(byte[][] buffers) throws InvalidAlgorithmParameterException {
+ if ((buffers.length != NRPAR) || (buffers[0].length < blockSize)) {
+ throw new InvalidAlgorithmParameterException("Bad buffersize.");
+ }
+ this.buffers = buffers;
+ for (int i = 0; i < NRPAR; i++) {
+ Arrays.fill(lanesArr[i], 0L);
+ b2lLittle(buffers[i], 0, lanesArr[i], 0, blockSize);
+ }
+ }
+
+ public int squeezeBlock() {
+ int retVal = doubleKeccak(lanesArr[0], lanesArr[1]);
+ for (int i = 0; i < NRPAR; i++) {
+ l2bLittle(lanesArr[i], 0, buffers[i], 0, blockSize);
+ }
+ return retVal;
+ }
+
+ @IntrinsicCandidate
+ private static int doubleKeccak(long[] lanes0, long[] lanes1) {
+ doubleKeccakJava(lanes0, lanes1);
+ return 1;
+ }
+
+ private static int doubleKeccakJava(long[] lanes0, long[] lanes1) {
+ keccak(lanes0);
+ keccak(lanes1);
+ return 1;
+ }
+
+ public static final class Shake128Parallel extends SHA3Parallel {
+ public Shake128Parallel(byte[][] buf) throws InvalidAlgorithmParameterException {
+ super(buf, 168);
+ }
+ }
+}
diff --git a/src/java.base/share/classes/sun/security/provider/SHAKE256.java b/src/java.base/share/classes/sun/security/provider/SHAKE256.java
deleted file mode 100644
index 5dab92fdd783..000000000000
--- a/src/java.base/share/classes/sun/security/provider/SHAKE256.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (c) 2020, 2021, 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;
-
-/*
- * The SHAKE256 extendable output function.
- */
-public final class SHAKE256 extends SHA3 {
- public SHAKE256(int d) {
- super("SHAKE256", d, (byte) 0x1F, 64);
- }
-
- public void update(byte in) {
- engineUpdate(in);
- }
- public void update(byte[] in, int off, int len) {
- engineUpdate(in, off, len);
- }
-
- public byte[] digest() {
- return engineDigest();
- }
-
- public void reset() {
- engineReset();
- }
-}
diff --git a/src/java.base/share/classes/sun/security/provider/SunEntries.java b/src/java.base/share/classes/sun/security/provider/SunEntries.java
index f036a411f1de..1487e521c547 100644
--- a/src/java.base/share/classes/sun/security/provider/SunEntries.java
+++ b/src/java.base/share/classes/sun/security/provider/SunEntries.java
@@ -189,20 +189,33 @@ public final class SunEntries {
attrs.clear();
attrs.put("ImplementedIn", "Software");
addWithAlias(p, "Signature", "HSS/LMS", "sun.security.provider.HSS", attrs);
+
+ add(p, "Signature", "ML-DSA", "sun.security.provider.ML_DSA_Impls$SIG", attrs);
+ addWithAlias(p, "Signature", "ML-DSA-44", "sun.security.provider.ML_DSA_Impls$SIG2", attrs);
+ addWithAlias(p, "Signature", "ML-DSA-65", "sun.security.provider.ML_DSA_Impls$SIG3", attrs);
+ addWithAlias(p, "Signature", "ML-DSA-87", "sun.security.provider.ML_DSA_Impls$SIG5", attrs);
+
/*
* Key Pair Generator engines
*/
attrs.clear();
attrs.put("ImplementedIn", "Software");
- attrs.put("KeySize", "2048"); // for DSA KPG and APG only
String dsaKPGImplClass = "sun.security.provider.DSAKeyPairGenerator$";
dsaKPGImplClass += (useLegacyDSA? "Legacy" : "Current");
+ attrs.put("KeySize", "2048");
addWithAlias(p, "KeyPairGenerator", "DSA", dsaKPGImplClass, attrs);
+ attrs.remove("KeySize");
+
+ add(p, "KeyPairGenerator", "ML-DSA", "sun.security.provider.ML_DSA_Impls$KPG", attrs);
+ addWithAlias(p, "KeyPairGenerator", "ML-DSA-44", "sun.security.provider.ML_DSA_Impls$KPG2", attrs);
+ addWithAlias(p, "KeyPairGenerator", "ML-DSA-65", "sun.security.provider.ML_DSA_Impls$KPG3", attrs);
+ addWithAlias(p, "KeyPairGenerator", "ML-DSA-87", "sun.security.provider.ML_DSA_Impls$KPG5", attrs);
/*
* Algorithm Parameter Generator engines
*/
+ attrs.put("KeySize", "2048");
addWithAlias(p, "AlgorithmParameterGenerator", "DSA",
"sun.security.provider.DSAParameterGenerator", attrs);
attrs.remove("KeySize");
@@ -221,6 +234,11 @@ public final class SunEntries {
addWithAlias(p, "KeyFactory", "HSS/LMS",
"sun.security.provider.HSS$KeyFactoryImpl", attrs);
+ add(p, "KeyFactory", "ML-DSA", "sun.security.provider.ML_DSA_Impls$KF", attrs);
+ addWithAlias(p, "KeyFactory", "ML-DSA-44", "sun.security.provider.ML_DSA_Impls$KF2", attrs);
+ addWithAlias(p, "KeyFactory", "ML-DSA-65", "sun.security.provider.ML_DSA_Impls$KF3", attrs);
+ addWithAlias(p, "KeyFactory", "ML-DSA-87", "sun.security.provider.ML_DSA_Impls$KF5", attrs);
+
/*
* Digest engines
*/
diff --git a/src/java.base/share/classes/sun/security/util/KeyUtil.java b/src/java.base/share/classes/sun/security/util/KeyUtil.java
index f9e92c14cb96..33a4296e9ce9 100644
--- a/src/java.base/share/classes/sun/security/util/KeyUtil.java
+++ b/src/java.base/share/classes/sun/security/util/KeyUtil.java
@@ -26,9 +26,13 @@
package sun.security.util;
import java.math.BigInteger;
+import java.security.AccessController;
import java.security.AlgorithmParameters;
import java.security.InvalidKeyException;
import java.security.Key;
+import java.security.PrivilegedAction;
+import java.security.PrivateKey;
+import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.interfaces.*;
import java.security.spec.*;
@@ -186,13 +190,13 @@ public static final int getKeySize(AlgorithmParameters parameters) {
*/
public static final String fullDisplayAlgName(Key key) {
String result = key.getAlgorithm();
- if (key instanceof ECKey) {
- ECParameterSpec paramSpec = ((ECKey) key).getParams();
+ if (key instanceof PrivateKey || key instanceof PublicKey) {
+ AlgorithmParameterSpec paramSpec = getParams(key);
if (paramSpec instanceof NamedCurve nc) {
result += " (" + nc.getNameAndAliases()[0] + ")";
+ } else if (paramSpec instanceof NamedParameterSpec nps) {
+ result = nps.getName();
}
- } else if (key instanceof EdECKey) {
- result = ((EdECKey) key).getParams().getName();
}
return result;
}
@@ -426,5 +430,25 @@ public static byte[] trimZeroes(byte[] b) {
return t;
}
+ @SuppressWarnings({"deprecation", "removal"})
+ public static AlgorithmParameterSpec getParams(Key key) {
+ try {
+ var m = key.getClass().getMethod("getParams");
+ if (!m.isAccessible()) {
+ PrivilegedAction