diff --git a/closed/GensrcJ9JCL.gmk b/closed/GensrcJ9JCL.gmk index eaded1a21df..a175cd4cd4b 100644 --- a/closed/GensrcJ9JCL.gmk +++ b/closed/GensrcJ9JCL.gmk @@ -39,6 +39,7 @@ $(eval $(call SetupCopyFiles,COPY_OVERLAY_FILES, \ SRC := $(TOPDIR), \ DEST := $(SUPPORT_OUTPUTDIR)/overlay, \ FILES := \ + src/java.base/share/classes/com/sun/crypto/provider/HmacCore.java \ src/java.base/share/classes/java/lang/ClassValue.java \ src/java.base/share/classes/java/net/InetAddress.java \ src/java.base/share/classes/java/security/Security.java \ @@ -48,7 +49,10 @@ $(eval $(call SetupCopyFiles,COPY_OVERLAY_FILES, \ src/java.base/share/classes/jdk/internal/misc/JavaNetInetAddressAccess.java \ src/java.base/share/classes/sun/security/jca/ProviderConfig.java \ src/java.base/share/classes/sun/security/jca/ProviderList.java \ + src/java.base/share/classes/sun/security/provider/DigestBase.java \ + src/java.base/share/classes/sun/security/provider/SecureRandom.java \ src/java.base/unix/classes/java/lang/ProcessEnvironment.java \ + src/java.base/unix/classes/sun/security/provider/NativePRNG.java \ )) IncludeIfUnsure := -includeIfUnsure -noWarnIncludeIf diff --git a/closed/src/java.base/share/classes/openj9/internal/criu/CRIUSECProvider.java b/closed/src/java.base/share/classes/openj9/internal/criu/CRIUSECProvider.java index 52c9ba4ac7c..c8544357355 100644 --- a/closed/src/java.base/share/classes/openj9/internal/criu/CRIUSECProvider.java +++ b/closed/src/java.base/share/classes/openj9/internal/criu/CRIUSECProvider.java @@ -1,7 +1,7 @@ /*[INCLUDE-IF CRIU_SUPPORT]*/ /* * =========================================================================== - * (c) Copyright IBM Corp. 2022, 2022 All Rights Reserved + * (c) Copyright IBM Corp. 2022, 2023 All Rights Reserved * =========================================================================== * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,9 @@ package openj9.internal.criu; import java.security.Provider; +import java.util.Map; +import java.util.WeakHashMap; +import java.util.function.Consumer; /** * The CRIUSECProvider is a security provider that is used as follows when CRIU @@ -39,26 +42,47 @@ public final class CRIUSECProvider extends Provider { private static final long serialVersionUID = -3240458633432287743L; + private static final Map> actions = new WeakHashMap<>(); + + @SuppressWarnings("unchecked") + public static void doOnRestart(T object, Consumer action) { + if (InternalCRIUSupport.isCheckpointAllowed()) { + synchronized (actions) { + // This unchecked cast is safe because the action + // is only applied the supplied object. + actions.put(object, (Consumer) action); + } + } + } + public CRIUSECProvider() { super("CRIUSEC", "1", "CRIUSEC Provider"); - String packageName = CRIUSECProvider.class.getPackage().getName() + "."; - String[] aliases = new String[] { "SHA", "SHA1", "OID.1.3.14.3.2.26", "1.3.14.3.2.26" }; // SHA1PRNG is the default name needed by the jdk, but SHA1 is not used, rather it reads directly from /dev/random. - putService(new Service(this, "MessageDigest", "SHA-1", packageName + "SHA", java.util.Arrays.asList(aliases), null)); - putService(new Service(this, "SecureRandom", "SHA1PRNG", packageName + "NativePRNG", null, null)); + putService(new Service(this, "MessageDigest", "SHA-1", "sun.security.provider.SHA", java.util.Arrays.asList(aliases), null)); + putService(new Service(this, "MessageDigest", "SHA-256", "sun.security.provider.SHA2$SHA256", null, null)); + putService(new Service(this, "MessageDigest", "MD5", "sun.security.provider.MD5", null, null)); + putService(new Service(this, "Mac", "HmacSHA256", "com.sun.crypto.provider.HmacCore$HmacSHA256", null, null)); + putService(new Service(this, "SecureRandom", "SHA1PRNG", "sun.security.provider.NativePRNG$CRIUNativePRNG", null, null)); } /** - * Resets the security digests. + * Reset security algorithms. */ public static void resetCRIUSEC() { - NativePRNG.clearRNGState(); - DigestBase.resetDigests(); + synchronized (actions) { + for (Map.Entry> entry : actions.entrySet()) { + Object object = entry.getKey(); + + if (object != null) { + entry.getValue().accept(object); + } + } + } } } diff --git a/closed/src/java.base/share/classes/openj9/internal/criu/DigestBase.java b/closed/src/java.base/share/classes/openj9/internal/criu/DigestBase.java deleted file mode 100644 index 4d293171b80..00000000000 --- a/closed/src/java.base/share/classes/openj9/internal/criu/DigestBase.java +++ /dev/null @@ -1,284 +0,0 @@ -/*[INCLUDE-IF CRIU_SUPPORT]*/ -/* - * Copyright (c) 2003, 2014, 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. - */ - -/* - * =========================================================================== - * (c) Copyright IBM Corp. 2022, 2022 All Rights Reserved - * =========================================================================== - */ - -package openj9.internal.criu; - -import java.security.MessageDigestSpi; -import java.security.DigestException; -import java.security.ProviderException; -import java.util.Arrays; -import java.util.Objects; -import java.util.ArrayList; - -import sun.security.action.GetPropertyAction; - -/** - * Common base message digest implementation for the Sun provider. - * It implements all the JCA methods as suitable for a Java message digest - * implementation of an algorithm based on a compression function (as all - * commonly used algorithms are). The individual digest subclasses only need to - * implement the following methods: - * - * . abstract void implCompress(byte[] b, int ofs); - * . abstract void implDigest(byte[] out, int ofs); - * . abstract void implReset(); - * - * See the inline documentation for details. - * - * @since 1.5 - * @author Andreas Sterbenz - */ -abstract class DigestBase extends MessageDigestSpi implements Cloneable { - - // one element byte array, temporary storage for update(byte) - private byte[] oneByte; - - // algorithm name to use in the exception message - private final String algorithm; - // length of the message digest in bytes - private final int digestLength; - - // size of the input to the compression function in bytes - private 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. - byte[] buffer; - // offset into buffer - private int bufferOffset; - - // number of bytes processed so far - // subclasses should not modify this value - // also used as a flag to indicate reset status - // -1: need to call engineReset() before next call to update() - // 0: is already reset - long bytesProcessed; - - private static final ArrayList digestList = new ArrayList<>(); - - // tracing for CRIUSEC - private static final boolean debug = Boolean.parseBoolean(GetPropertyAction.privilegedGetProperty - ("enable.j9internal.checkpoint.security.api.debug", "false")); - - /** - * This method is required to ensure that no state is maintained when checkpointing. - */ - static void resetDigests() { - if (debug) { - System.out.println("Clearing all digests ..."); - } - synchronized (digestList) { - for (DigestBase digest : digestList) { - digest.engineReset(); - } - digestList.clear(); - } - } - - /** - * Main constructor. - */ - DigestBase(String algorithm, int digestLength, int blockSize) { - super(); - this.algorithm = algorithm; - this.digestLength = digestLength; - this.blockSize = blockSize; - this.buffer = new byte[blockSize]; - - // adding digest object to digest list to be reset - // during post-checkpoint hook - synchronized (digestList) { - digestList.add(this); - } - } - - // return digest length. See JCA doc. - protected final int engineGetDigestLength() { - return digestLength; - } - - // single byte update. See JCA doc. - protected final void engineUpdate(byte b) { - if (oneByte == null) { - oneByte = new byte[1]; - } - oneByte[0] = b; - engineUpdate(oneByte, 0, 1); - } - - // array update. See JCA doc. - protected final void engineUpdate(byte[] b, int ofs, int len) { - if (len == 0) { - return; - } - if ((ofs < 0) || (len < 0) || (ofs > b.length - len)) { - throw new ArrayIndexOutOfBoundsException(); - } - if (bytesProcessed < 0) { - engineReset(); - } - bytesProcessed += len; - // if buffer is not empty, we need to fill it before proceeding - if (bufferOffset != 0) { - int n = Math.min(len, blockSize - bufferOffset); - System.arraycopy(b, ofs, buffer, bufferOffset, n); - bufferOffset += n; - ofs += n; - len -= n; - if (bufferOffset >= blockSize) { - // compress completed block now - implCompress(buffer, 0); - bufferOffset = 0; - } - } - // compress complete blocks - if (len >= blockSize) { - int limit = ofs + len; - ofs = implCompressMultiBlock(b, ofs, limit - blockSize); - len = limit - ofs; - } - // copy remainder to buffer - if (len > 0) { - System.arraycopy(b, ofs, buffer, 0, len); - bufferOffset = len; - } - } - - // compress complete blocks - private int implCompressMultiBlock(byte[] b, int ofs, int limit) { - implCompressMultiBlockCheck(b, ofs, limit); - return implCompressMultiBlock0(b, ofs, limit); - } - - private int implCompressMultiBlock0(byte[] b, int ofs, int limit) { - for (; ofs <= limit; ofs += blockSize) { - implCompress(b, ofs); - } - return ofs; - } - - private void implCompressMultiBlockCheck(byte[] b, int ofs, int limit) { - if (limit < 0) { - return; // not an error because implCompressMultiBlockImpl won't execute if limit < 0 - // and an exception is thrown if ofs < 0. - } - - Objects.requireNonNull(b); - - if ((ofs < 0) || (ofs >= b.length)) { - throw new ArrayIndexOutOfBoundsException(ofs); - } - - int endIndex = (limit / blockSize) * blockSize + blockSize - 1; - if (endIndex >= b.length) { - throw new ArrayIndexOutOfBoundsException(endIndex); - } - } - - // reset this object. See JCA doc. - protected final void engineReset() { - if (bytesProcessed == 0) { - // already reset, ignore - return; - } - implReset(); - bufferOffset = 0; - bytesProcessed = 0; - Arrays.fill(buffer, (byte) 0x00); - } - - // return the digest. See JCA doc. - protected final byte[] engineDigest() { - byte[] b = new byte[digestLength]; - try { - engineDigest(b, 0, b.length); - } catch (DigestException e) { - throw (ProviderException) - new ProviderException("Internal error").initCause(e); - } - return b; - } - - // return the digest in the specified array. See JCA doc. - protected final int engineDigest(byte[] out, int ofs, int len) - throws DigestException { - if (len < digestLength) { - throw new DigestException("Length must be at least " - + digestLength + " for " + algorithm + "digests"); - } - if ((ofs < 0) || (len < 0) || (ofs > (out.length - len))) { - throw new DigestException("Buffer too short to store digest"); - } - if (bytesProcessed < 0) { - engineReset(); - } - implDigest(out, ofs); - bytesProcessed = -1; - return digestLength; - } - - /** - * Core compression function. Processes blockSize bytes at a time - * and updates the state of this object. - */ - abstract void implCompress(byte[] b, int ofs); - - /** - * Return the digest. Subclasses do not need to reset() themselves, - * DigestBase calls implReset() when necessary. - */ - abstract void implDigest(byte[] out, int ofs); - - /** - * Reset subclass specific state to their initial values. DigestBase - * calls this method when necessary. - */ - abstract void implReset(); - - @Override - public Object clone() throws CloneNotSupportedException { - DigestBase copy = (DigestBase) super.clone(); - copy.buffer = copy.buffer.clone(); - return copy; - } - - // padding used for the MD5, and SHA-* message digests - static final byte[] padding; - - static { - // we need 128 byte padding for SHA-384/512 - // and an additional 8 bytes for the high 8 bytes of the 16 - // byte bit counter in SHA-384/512 - padding = new byte[136]; - padding[0] = (byte) 0x80; - } -} diff --git a/closed/src/java.base/share/classes/openj9/internal/criu/NativePRNG.java b/closed/src/java.base/share/classes/openj9/internal/criu/NativePRNG.java deleted file mode 100644 index 4d86db2ca8c..00000000000 --- a/closed/src/java.base/share/classes/openj9/internal/criu/NativePRNG.java +++ /dev/null @@ -1,284 +0,0 @@ -/*[INCLUDE-IF CRIU_SUPPORT]*/ -/* - * Copyright (c) 2003, 2016, 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. - */ - -/* - * =========================================================================== - * (c) Copyright IBM Corp. 2022, 2022 All Rights Reserved - * =========================================================================== - */ - -package openj9.internal.criu; - -import java.io.*; -import java.lang.reflect.Method; -import java.net.*; -import java.security.*; -import java.util.Arrays; - -import sun.security.util.Debug; - -/** - * Native PRNG implementation for Solaris/Linux/MacOS. - * This class was modified from the OpenJDK implementation. Code was removed - * to eliminate any buffering, so we trade off performance and allow only - * for reading directly from /dev/random when the app needs random bytes. - *

- * It obtains seed and random numbers by reading /dev/random and /dev/urandom. - *

- * On some Unix platforms, /dev/random may block until enough entropy is - * available, but that may negatively impact the perceived startup - * time. By selecting these sources, this implementation tries to - * strike a balance between performance and security. - *

- * generateSeed()/nextBytes() and setSeed() attempt to directly read/write to the seed - * source. However, this file may only be writable by root in many - * configurations. - *

- * Finally, note that we use a singleton for the actual work (RandomIO) - * to avoid having to open and close /dev/random constantly. However, - * there may be many NativePRNG instances created by the JCA framework. - * - * @since 1.5 - * @author Andreas Sterbenz - */ -public final class NativePRNG extends SecureRandomSpi { - - private static final long serialVersionUID = -6599091113397072932L; - - private static final Debug debug = Debug.getInstance("provider"); - - // name of the pure random file (also used for setSeed()) - private static final String NAME_RANDOM = "/dev/random"; - - // name of the pseudo random file - private static final String NAME_URANDOM = "/dev/urandom"; - - // singleton instance or null if not available - private static final RandomIO INSTANCE = initIO(); - - /** - * Create a RandomIO object for all I/O of this Variant type. - */ - private static RandomIO initIO() { - return AccessController.doPrivileged( - new PrivilegedAction<>() { - @Override - public RandomIO run() { - File seedFile = new File(NAME_RANDOM); - File nextFile = new File(NAME_URANDOM); - - if (debug != null) { - debug.println("NativePRNG." + - " seedFile: " + seedFile + - " nextFile: " + nextFile); - } - - if (!seedFile.canRead() || !nextFile.canRead()) { - if (debug != null) { - debug.println("NativePRNG." + - " Couldn't read Files."); - } - return null; - } - - try { - return new RandomIO(seedFile, nextFile); - } catch (Exception e) { - return null; - } - } - }); - } - - // return whether the NativePRNG is available - static boolean isAvailable() { - return INSTANCE != null; - } - - // constructor, called by the JCA framework - public NativePRNG() { - super(); - if (INSTANCE == null) { - throw new AssertionError("NativePRNG not available"); - } - } - - // set the seed - @Override - protected void engineSetSeed(byte[] seed) { - INSTANCE.implSetSeed(seed); - } - - // get pseudo random bytes - @Override - protected void engineNextBytes(byte[] bytes) { - INSTANCE.implNextBytes(bytes); - } - - // get true random bytes - @Override - protected byte[] engineGenerateSeed(int numBytes) { - return INSTANCE.implGenerateSeed(numBytes); - } - - static void clearRNGState() { - INSTANCE.clearRNGState(); - } - - /** - * Nested class doing the actual work. Singleton, see INSTANCE above. - */ - private static final class RandomIO { - - // holder for the seedFile, used if we ever add seed material - private File seedFile; - - // in/OutputStream for "seed" and "next" - private final InputStream seedIn, nextIn; - private OutputStream seedOut; - - // flag indicating if we have tried to open seedOut yet - private boolean seedOutInitialized; - - // SHA1PRNG instance for mixing - // initialized lazily on demand to avoid problems during startup - private volatile SHA1PRNG mixRandom; - - // mutex lock for accessing the seed file stream - private final Object LOCK_GET_SEED = new Object(); - - // constructor, called only once from initIO() - private RandomIO(File seedFile, File nextFile) throws IOException { - this.seedFile = seedFile; - InputStream seedStream = null, nextStream = null; - try { - // invoke the getInputStream method from the FileInputStreamPool class - Class runnable = Class.forName("sun.security.provider.FileInputStreamPool", - true, ClassLoader.getSystemClassLoader()); - Method getStream = runnable.getDeclaredMethod("getInputStream", File.class); - getStream.setAccessible(true); - seedStream = (InputStream) getStream.invoke(null, seedFile); - nextStream = (InputStream) getStream.invoke(null, nextFile); - } catch (Exception e) { - System.out.println(e.toString()); - } - this.seedIn = seedStream; - this.nextIn = nextStream; - } - - // get the SHA1PRNG for mixing - // initialize if not yet created - private SHA1PRNG getMixRandom() { - SHA1PRNG prngObj = mixRandom; - if (prngObj == null) { - synchronized (LOCK_GET_SEED) { - prngObj = mixRandom; - if (prngObj == null) { - try { - prngObj = SHA1PRNG.seedFrom(seedIn); - } catch (IOException e) { - throw new ProviderException("init failed", e); - } - mixRandom = prngObj; - } - } - } - return prngObj; - } - // Read data.length bytes from in. - // These are not normal files, so we need to loop the read. - // Just keep trying as long as we are making progress. - private static void readFully(InputStream in, byte[] data) - throws IOException { - int len = data.length; - if (in.readNBytes(data, 0, len) < len) { - throw new IOException("Could not read from file(s)"); - } - } - - // get true random bytes, just read from "seed" - private byte[] implGenerateSeed(int numBytes) { - synchronized (LOCK_GET_SEED) { - try { - byte[] b = new byte[numBytes]; - readFully(seedIn, b); - return b; - } catch (IOException e) { - throw new ProviderException("generateSeed() failed", e); - } - } - } - - // supply random bytes to the OS - // write to "seed" if possible - // always add the seed to our mixing random - private synchronized void implSetSeed(byte[] seed) { - if (seedOutInitialized == false) { - seedOutInitialized = true; - seedOut = AccessController.doPrivileged( - new PrivilegedAction<>() { - @Override - public OutputStream run() { - try { - return new FileOutputStream(seedFile, true); - } catch (IOException e) { - return null; - } - } - }); - } - if (seedOut != null) { - try { - seedOut.write(seed); - } catch (IOException e) { - // ignored, on Mac OS X, /dev/urandom can be opened - // for write, but actual write is not permitted - } - } - getMixRandom().engineSetSeed(seed); - } - - private synchronized void implNextBytes(byte[] data) { - getMixRandom().engineNextBytes(data); - try { - // read random data from non blocking source - byte[] rawData = new byte[data.length]; - readFully(nextIn, rawData); - for (int i = 0; i < data.length; i++) { - data[i] ^= rawData[i]; - } - } catch (IOException e) { - throw new ProviderException("nextBytes() failed", e); - } - } - - private void clearRNGState() { - if (mixRandom != null) { - mixRandom.clearState(); - } - } - } -} diff --git a/closed/src/java.base/share/classes/openj9/internal/criu/SHA.java b/closed/src/java.base/share/classes/openj9/internal/criu/SHA.java deleted file mode 100644 index 1163e427ac7..00000000000 --- a/closed/src/java.base/share/classes/openj9/internal/criu/SHA.java +++ /dev/null @@ -1,240 +0,0 @@ -/*[INCLUDE-IF CRIU_SUPPORT]*/ -/* - * Copyright (c) 1996, 2012, 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. - */ - -/* - * =========================================================================== - * (c) Copyright IBM Corp. 2022, 2022 All Rights Reserved - * =========================================================================== - */ - -package openj9.internal.criu; - -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.Objects; - -/** - * This class implements the Secure Hash Algorithm (SHA) developed by - * the National Institute of Standards and Technology along with the - * National Security Agency. This is the updated version of SHA - * fip-180 as superseded by fip-180-1. - * - *

It implement JavaSecurity MessageDigest, and can be used by in - * the Java Security framework, as a pluggable implementation, as a - * filter for the digest stream classes. - * - * @author Roger Riggs - * @author Benjamin Renaud - * @author Andreas Sterbenz - */ -public final class SHA extends DigestBase { - - // Buffer of int's and count of characters accumulated - // 64 bytes are included in each hash block so the low order - // bits of count are used to know how to pack the bytes into ints - // and to know when to compute the block and start the next one. - private int[] W; - - // state of this - private int[] state; - - /** - * Creates a new SHA object. - */ - public SHA() { - super("SHA-1", 20, 64); - state = new int[5]; - W = new int[80]; - resetHashes(); - } - - /* - * Clones this object. - */ - public Object clone() throws CloneNotSupportedException { - SHA copy = (SHA) super.clone(); - copy.state = copy.state.clone(); - copy.W = new int[80]; - return copy; - } - - /** - * Resets the buffers and hash value to start a new hash. - */ - void implReset() { - // Load magic initialization constants. - resetHashes(); - // clear out old data - Arrays.fill(W, 0); - } - - private void resetHashes() { - state[0] = 0x67452301; - state[1] = 0xefcdab89; - state[2] = 0x98badcfe; - state[3] = 0x10325476; - state[4] = 0xc3d2e1f0; - } - - /** - * Computes the final hash and copies the 20 bytes to the output array. - */ - void implDigest(byte[] out, int ofs) { - long bitsProcessed = bytesProcessed << 3; - - int index = (int) bytesProcessed & 0x3f; - int padLen = (index < 56) ? (56 - index) : (120 - index); - engineUpdate(padding, 0, padLen); - - try { - // Invoke the i2bBig4 method from the ByteArrayAccess class. - Class runnable = Class.forName("sun.security.provider.ByteArrayAccess", - true, ClassLoader.getSystemClassLoader()); - Method i2bBig4 = runnable.getDeclaredMethod("i2bBig4", - int.class, byte[].class, int.class); - i2bBig4.setAccessible(true); - i2bBig4.invoke(null, (int) (bitsProcessed >>> 32), buffer, 56); - i2bBig4.invoke(null, (int) bitsProcessed, buffer, 60); - implCompress(buffer, 0); - - // Invoke the i2bBig method from the ByteArrayAccess class. - Method i2bBig = runnable.getDeclaredMethod("i2bBig", - int[].class, int.class, byte[].class, int.class, int.class); - i2bBig.setAccessible(true); - i2bBig.invoke(null, state, 0, out, ofs, 20); - } catch (Exception e) { - System.out.println(e.toString()); - } - } - - // constants for each round - private static final int round1_kt = 0x5a827999; - private static final int round2_kt = 0x6ed9eba1; - private static final int round3_kt = 0x8f1bbcdc; - private static final int round4_kt = 0xca62c1d6; - - /** - * Compute a the hash for the current block. - * - * This is in the same vein as Peter Gutmann's algorithm listed in - * the back of Applied Cryptography, Compact implementation of - * "old" NIST Secure Hash Algorithm. - */ - void implCompress(byte[] buf, int ofs) { - implCompressCheck(buf, ofs); - implCompress0(buf, ofs); - } - - private void implCompressCheck(byte[] buf, int ofs) { - Objects.requireNonNull(buf); - - // The checks performed by the method 'b2iBig64' - // are sufficient for the case when the method - // 'implCompressImpl' is replaced with a compiler - // intrinsic. - try { - // Invoke the b2iBig64 method from the ByteArrayAccess class. - Class runnable = Class.forName("sun.security.provider.ByteArrayAccess", - true, ClassLoader.getSystemClassLoader()); - Method b2iBig64 = runnable.getDeclaredMethod("b2iBig64", - byte[].class, int.class, int[].class); - b2iBig64.setAccessible(true); - b2iBig64.invoke(null, buf, ofs, W); - } catch (Exception e) { - System.out.println(e.toString()); - } - } - - // The method 'implCompressImpl seems not to use its parameters. - // The method can, however, be replaced with a compiler intrinsic - // that operates directly on the array 'buf' (starting from - // offset 'ofs') and not on array 'W', therefore 'buf' and 'ofs' - // must be passed as parameter to the method. - private void implCompress0(byte[] buf, int ofs) { - // The first 16 ints have the byte stream, compute the rest of - // the buffer. - for (int t = 16; t <= 79; t++) { - int temp = W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]; - W[t] = (temp << 1) | (temp >>> 31); - } - - int a = state[0]; - int b = state[1]; - int c = state[2]; - int d = state[3]; - int e = state[4]; - - // Round 1 - for (int i = 0; i < 20; i++) { - int temp = ((a<<5) | (a>>>(32-5))) + - ((b&c)|((~b)&d))+ e + W[i] + round1_kt; - e = d; - d = c; - c = ((b<<30) | (b>>>(32-30))); - b = a; - a = temp; - } - - // Round 2 - for (int i = 20; i < 40; i++) { - int temp = ((a<<5) | (a>>>(32-5))) + - (b ^ c ^ d) + e + W[i] + round2_kt; - e = d; - d = c; - c = ((b<<30) | (b>>>(32-30))); - b = a; - a = temp; - } - - // Round 3 - for (int i = 40; i < 60; i++) { - int temp = ((a<<5) | (a>>>(32-5))) + - ((b&c)|(b&d)|(c&d)) + e + W[i] + round3_kt; - e = d; - d = c; - c = ((b<<30) | (b>>>(32-30))); - b = a; - a = temp; - } - - // Round 4 - for (int i = 60; i < 80; i++) { - int temp = ((a<<5) | (a>>>(32-5))) + - (b ^ c ^ d) + e + W[i] + round4_kt; - e = d; - d = c; - c = ((b<<30) | (b>>>(32-30))); - b = a; - a = temp; - } - state[0] += a; - state[1] += b; - state[2] += c; - state[3] += d; - state[4] += e; - } - -} diff --git a/closed/src/java.base/share/classes/openj9/internal/criu/SHA1PRNG.java b/closed/src/java.base/share/classes/openj9/internal/criu/SHA1PRNG.java deleted file mode 100644 index 676b5212432..00000000000 --- a/closed/src/java.base/share/classes/openj9/internal/criu/SHA1PRNG.java +++ /dev/null @@ -1,200 +0,0 @@ -/*[INCLUDE-IF CRIU_SUPPORT]*/ -/* - * Copyright (c) 1998, 2014, 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. - */ - -/* - * =========================================================================== - * (c) Copyright IBM Corp. 2022, 2022 All Rights Reserved - * =========================================================================== - */ - -package openj9.internal.criu; - -import java.io.InputStream; -import java.io.IOException; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; -import java.util.Arrays; - -/** - *

This class provides a crytpographically strong pseudo-random number - * generator based on the SHA-1 hash algorithm. - * - *

Seed must be provided externally. - * - *

Also note that when a random object is deserialized, - * engineNextBytes invoked on the - * restored random object will yield the exact same (random) bytes as the - * original object. If this behaviour is not desired, the restored random - * object should be seeded, using - * engineSetSeed. - * - * @author Benjamin Renaud - * @author Josh Bloch - * @author Gadi Guy - */ - -public final class SHA1PRNG implements java.io.Serializable { - - private static final long serialVersionUID = 3581829991155417889L; - - // SHA-1 Digest yields 160-bit hashes which require 20 bytes of space. - private static final int DIGEST_SIZE = 20; - private transient MessageDigest digest; - private byte[] state; - private byte[] remainder; - private int remCount; - - // This class is a modified version of the SHA1PRNG SecureRandom implementation - // that is found at sun.security.provider.SecureRandom. - // It was modified to be used by CRIUSEC NativePRNG as a mixing data source. - // Auto-seeding was removed, it is always seeded by NativePRNG from a - // blocking entropy source. - - private SHA1PRNG(byte[] seed) { - init(seed); - } - - static SHA1PRNG seedFrom(InputStream in) throws IOException { - byte[] seed = new byte[DIGEST_SIZE]; - if (in.readNBytes(seed, 0, DIGEST_SIZE) != DIGEST_SIZE) { - throw new IOException("Could not read seed"); - } - return new SHA1PRNG(seed); - } - - /** - * This call, used by the constructor, instantiates the SHA digest - * and sets the seed. - */ - private void init(byte[] seed) { - if (seed == null) { - throw new InternalError("internal error: no seed available."); - } - - try { - digest = MessageDigest.getInstance("SHA-1", "CRIUSEC"); - } catch (NoSuchProviderException | NoSuchAlgorithmException e) { - throw new InternalError("internal error: SHA-1 not available.", e); - } - - engineSetSeed(seed); - } - - - /** - * Reseeds this random object. The given seed supplements, rather than - * replaces, the existing seed. Thus, repeated calls are guaranteed - * never to reduce randomness. - * - * @param seed the seed. - */ - public synchronized void engineSetSeed(byte[] seed) { - if (state != null) { - digest.update(state); - for (int i = 0; i < state.length; i++) { - state[i] = 0; - } - } - state = digest.digest(seed); - remCount = 0; - } - - private static void updateState(byte[] state, byte[] output) { - int carry = 1; - boolean collision = true; - - // state(n + 1) = (state(n) + output(n) + 1) % 2^160; - for (int i = 0; i < state.length; i++) { - // Add two bytes. - int stateCalc = (state[i] & 0xFF) + (output[i] & 0xFF) + carry; - // Result is lower 8 bits. - byte newState = (byte)stateCalc; - // Store result. Check for state collision. - collision &= (state[i] == newState); - state[i] = newState; - // High 8 bits are carry. Store for next iteration. - carry = stateCalc >>> 8; - } - - // Make sure at least one bit changes. - if (collision) { - state[0]++; - } - } - - - /** - * Generates a user-specified number of random bytes. - * - * @param result the array to be filled in with random bytes. - */ - public synchronized void engineNextBytes(byte[] result) { - int index = 0; - byte[] output = remainder; - - // Use remainder from last time. - int r = remCount; - if (r > 0) { - // Compute how many bytes to be copied. - int todo = Math.min(result.length - index, DIGEST_SIZE - r); - // Copy the bytes, zero the buffer. - for (int i = 0; i < todo; i++) { - result[i] = output[r]; - output[r++] = 0; - } - remCount += todo; - index += todo; - } - - // If we need more bytes, make them. - while (index < result.length) { - // Step the state. - digest.update(state); - output = digest.digest(); - updateState(state, output); - - // Compute how many bytes to be copied. - int todo = Math.min(result.length - index, DIGEST_SIZE); - // Copy the bytes, zero the buffer. - for (int i = 0; i < todo; i++) { - result[index++] = output[i]; - output[i] = 0; - } - remCount += todo; - } - - // Store remainder for next time. - remainder = output; - remCount %= DIGEST_SIZE; - } - - void clearState() { - Arrays.fill(state, (byte) 0x00); - Arrays.fill(remainder, (byte) 0x00); - remCount = 0; - } -} diff --git a/src/java.base/share/classes/com/sun/crypto/provider/HmacCore.java b/src/java.base/share/classes/com/sun/crypto/provider/HmacCore.java index 2abc7fa7a9a..d17ee3d71de 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/HmacCore.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/HmacCore.java @@ -23,6 +23,12 @@ * questions. */ +/* + * =========================================================================== + * (c) Copyright IBM Corp. 2022, 2023 All Rights Reserved + * =========================================================================== + */ + package com.sun.crypto.provider; import java.util.Arrays; @@ -34,6 +40,11 @@ import java.security.*; import java.security.spec.*; +/*[IF CRIU_SUPPORT]*/ +import openj9.internal.criu.CRIUSECProvider; +import openj9.internal.criu.InternalCRIUSupport; +/*[ENDIF] CRIU_SUPPORT */ + /** * This class constitutes the core of HMAC- algorithms, where * can be SHA1 or MD5, etc. See RFC 2104 for spec. @@ -94,6 +105,12 @@ abstract class HmacCore extends MacSpi implements Cloneable { this.k_ipad = new byte[blockLen]; this.k_opad = new byte[blockLen]; first = true; + + /*[IF CRIU_SUPPORT]*/ + if (InternalCRIUSupport.enableCRIUSecProvider()) { + CRIUSECProvider.doOnRestart(this, hmac -> hmac.engineReset()); + } + /*[ENDIF] CRIU_SUPPORT */ } /** 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 d4596eb64be..35d419db1c1 100644 --- a/src/java.base/share/classes/sun/security/provider/DigestBase.java +++ b/src/java.base/share/classes/sun/security/provider/DigestBase.java @@ -23,6 +23,12 @@ * questions. */ +/* + * =========================================================================== + * (c) Copyright IBM Corp. 2022, 2023 All Rights Reserved + * =========================================================================== + */ + package sun.security.provider; import java.security.MessageDigestSpi; @@ -33,6 +39,11 @@ import jdk.internal.HotSpotIntrinsicCandidate; +/*[IF CRIU_SUPPORT]*/ +import openj9.internal.criu.CRIUSECProvider; +import openj9.internal.criu.InternalCRIUSupport; +/*[ENDIF] CRIU_SUPPORT */ + /** * Common base message digest implementation for the Sun provider. * It implements all the JCA methods as suitable for a Java message digest @@ -84,6 +95,12 @@ abstract class DigestBase extends MessageDigestSpi implements Cloneable { this.digestLength = digestLength; this.blockSize = blockSize; buffer = new byte[blockSize]; + + /*[IF CRIU_SUPPORT]*/ + if (InternalCRIUSupport.enableCRIUSecProvider()) { + CRIUSECProvider.doOnRestart(this, digest -> digest.engineReset()); + } + /*[ENDIF] CRIU_SUPPORT */ } // return digest length. See JCA doc. diff --git a/src/java.base/share/classes/sun/security/provider/SecureRandom.java b/src/java.base/share/classes/sun/security/provider/SecureRandom.java index c55dba0ed0a..77cfab3e8c4 100644 --- a/src/java.base/share/classes/sun/security/provider/SecureRandom.java +++ b/src/java.base/share/classes/sun/security/provider/SecureRandom.java @@ -23,6 +23,12 @@ * questions. */ +/* + * =========================================================================== + * (c) Copyright IBM Corp. 2022, 2023 All Rights Reserved + * =========================================================================== + */ + package sun.security.provider; import java.io.IOException; @@ -31,6 +37,7 @@ import java.security.SecureRandomSpi; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; +import java.util.Arrays; /** *

This class provides a crytpographically strong pseudo-random number @@ -262,6 +269,14 @@ public synchronized void engineNextBytes(byte[] result) { remCount %= DIGEST_SIZE; } + /*[IF CRIU_SUPPORT]*/ + void clearState() { + Arrays.fill(state, (byte) 0x00); + Arrays.fill(remainder, (byte) 0x00); + remCount = 0; + } + /*[ENDIF] CRIU_SUPPORT */ + /* * This method is called to restore the state of the random object from * a stream. diff --git a/src/java.base/unix/classes/sun/security/provider/NativePRNG.java b/src/java.base/unix/classes/sun/security/provider/NativePRNG.java index 46e6764a411..1fda1373281 100644 --- a/src/java.base/unix/classes/sun/security/provider/NativePRNG.java +++ b/src/java.base/unix/classes/sun/security/provider/NativePRNG.java @@ -23,6 +23,12 @@ * questions. */ +/* + * =========================================================================== + * (c) Copyright IBM Corp. 2022, 2023 All Rights Reserved + * =========================================================================== + */ + package sun.security.provider; import java.io.*; @@ -30,6 +36,10 @@ import java.security.*; import java.util.Arrays; +/*[IF CRIU_SUPPORT]*/ +import openj9.internal.criu.CRIUSECProvider; +/*[ENDIF] CRIU_SUPPORT */ + import sun.security.util.Debug; /** @@ -86,6 +96,9 @@ public final class NativePRNG extends SecureRandomSpi { // which kind of RandomIO object are we creating? private enum Variant { + /*[IF CRIU_SUPPORT]*/ + CRIU, + /*[ENDIF] CRIU_SUPPORT */ MIXED, BLOCKING, NONBLOCKING } @@ -135,6 +148,13 @@ public RandomIO run() { File nextFile; switch(v) { + /*[IF CRIU_SUPPORT]*/ + case CRIU: + seedFile = new File(NAME_RANDOM); + nextFile = new File(NAME_URANDOM); + break; + /*[ENDIF] CRIU_SUPPORT */ + case MIXED: URL egdUrl; File egdFile = null; @@ -187,7 +207,18 @@ public RandomIO run() { } try { - return new RandomIO(seedFile, nextFile); + RandomIO instance = new RandomIO(seedFile, nextFile, v); + + /*[IF CRIU_SUPPORT]*/ + /* CRIU is only used if isCheckpointAllowed(), + * so there's no need to check that again here. + */ + if (v == Variant.CRIU) { + CRIUSECProvider.doOnRestart(instance, random -> random.clearRNGState()); + } + /*[ENDIF] CRIU_SUPPORT */ + + return instance; } catch (Exception e) { return null; } @@ -325,6 +356,53 @@ protected byte[] engineGenerateSeed(int numBytes) { } } + /*[IF CRIU_SUPPORT]*/ + /** + * A NativePRNG-like class that uses /dev/random for seed and + * /dev/urandom for random material. + * + * Note that it does not respect the egd properties, since we have + * no way of knowing what those qualities are. + * + * This is very similar to the outer NativePRNG class, minimizing any + * breakage to the serialization of the existing implementation. + * + * @since 1.8 + */ + public static final class CRIUNativePRNG extends SecureRandomSpi { + private static final long serialVersionUID = -6599091113397072932L; + + private static final RandomIO INSTANCE = initIO(Variant.CRIU); + + // constructor, called by the JCA framework + public CRIUNativePRNG() { + super(); + if (INSTANCE == null) { + throw new AssertionError( + "NativePRNG$CRIUNativePRNG not available"); + } + } + + // set the seed + @Override + protected void engineSetSeed(byte[] seed) { + INSTANCE.implSetSeed(seed); + } + + // get pseudo random bytes + @Override + protected void engineNextBytes(byte[] bytes) { + INSTANCE.implNextBytes(bytes); + } + + // get true random bytes + @Override + protected byte[] engineGenerateSeed(int numBytes) { + return INSTANCE.implGenerateSeed(numBytes); + } + } + /*[ENDIF] CRIU_SUPPORT */ + /** * Nested class doing the actual work. Singleton, see INSTANCE above. */ @@ -357,6 +435,8 @@ private static class RandomIO { // buffer for next bits private byte[] nextBuffer; + private final Variant variant; + // number of bytes left in nextBuffer private int buffered; @@ -383,11 +463,12 @@ private static class RandomIO { private final Object LOCK_SET_SEED = new Object(); // constructor, called only once from initIO() - private RandomIO(File seedFile, File nextFile) throws IOException { + private RandomIO(File seedFile, File nextFile, Variant variant) throws IOException { this.seedFile = seedFile; seedIn = FileInputStreamPool.getInputStream(seedFile); nextIn = FileInputStreamPool.getInputStream(nextFile); nextBuffer = new byte[bufferSize]; + this.variant = variant; } // get the SHA1PRNG for mixing @@ -401,7 +482,7 @@ private sun.security.provider.SecureRandom getMixRandom() { r = new sun.security.provider.SecureRandom(); try { byte[] b = new byte[20]; - readFully(nextIn, b); + readFully(nextIn, b, variant); r.engineSetSeed(b); } catch (IOException e) { throw new ProviderException("init failed", e); @@ -416,9 +497,17 @@ private sun.security.provider.SecureRandom getMixRandom() { // read data.length bytes from in // These are not normal files, so we need to loop the read. // just keep trying as long as we are making progress - private static void readFully(InputStream in, byte[] data) + private static void readFully(InputStream in, byte[] data, Variant variant) throws IOException { int len = data.length; + /*[IF CRIU_SUPPORT]*/ + if (variant == Variant.CRIU) { + // read bytes from non blocking source + if (in.readNBytes(data, 0, len) < len) { + throw new IOException("Could not read from file(s)"); + } + } + /*[ELSE] CRIU_SUPPORT */ int ofs = 0; while (len > 0) { int k = in.read(data, ofs, len); @@ -431,6 +520,7 @@ private static void readFully(InputStream in, byte[] data) if (len > 0) { throw new IOException("Could not read from file(s)"); } + /*[ENDIF] CRIU_SUPPORT */ } // get true random bytes, just read from "seed" @@ -438,7 +528,7 @@ private byte[] implGenerateSeed(int numBytes) { synchronized (LOCK_GET_SEED) { try { byte[] b = new byte[numBytes]; - readFully(seedIn, b); + readFully(seedIn, b, variant); return b; } catch (IOException e) { throw new ProviderException("generateSeed() failed", e); @@ -523,7 +613,7 @@ private void ensureBufferValid() throws IOException { // Load fresh random bytes into nextBuffer lastRead = time; - readFully(nextIn, nextBuffer); + readFully(nextIn, nextBuffer, variant); buffered = nextBuffer.length; } @@ -533,6 +623,16 @@ private void ensureBufferValid() throws IOException { private void implNextBytes(byte[] data) { try { getMixRandom().engineNextBytes(data); + /*[IF CRIU_SUPPORT]*/ + if (variant == Variant.CRIU) { + // read random data from non blocking source + byte[] rawData = new byte[data.length]; + readFully(nextIn, rawData, variant); + for (int i = 0; i < data.length; i++) { + data[i] ^= rawData[i]; + } + } + /*[ELSE] CRIU_SUPPORT */ int data_len = data.length; int ofs = 0; int len; @@ -562,9 +662,19 @@ private void implNextBytes(byte[] data) { } data_len -= len; } + /*[ENDIF] CRIU_SUPPORT */ } catch (IOException e){ throw new ProviderException("nextBytes() failed", e); } } + + /*[IF CRIU_SUPPORT]*/ + private void clearRNGState() { + if (mixRandom != null) { + mixRandom.clearState(); + } + } + /*[ENDIF] CRIU_SUPPORT */ + } }