From 1303674a0a25cea099b8894dd26456e6759b58ba Mon Sep 17 00:00:00 2001 From: Alon Shalev Housfater Date: Thu, 28 Apr 2022 15:34:53 -0400 Subject: [PATCH] Add CRIU security provider Signed-off-by: Alon Shalev Housfater --- closed/GensrcJ9JCL.gmk | 15 +- .../ibm/security/criu/CRIUSECProvider.java | 63 ++++ .../com/ibm/security/criu/DigestBase.java | 284 ++++++++++++++++++ .../com/ibm/security/criu/NativePRNG.java | 249 +++++++++++++++ .../classes/com/ibm/security/criu/SHA.java | 240 +++++++++++++++ .../java/security/CRIUConfigurator.java | 103 +++++++ .../share/classes/java/security/Security.java | 17 ++ 7 files changed, 965 insertions(+), 6 deletions(-) create mode 100644 closed/src/java.base/share/classes/com/ibm/security/criu/CRIUSECProvider.java create mode 100644 closed/src/java.base/share/classes/com/ibm/security/criu/DigestBase.java create mode 100644 closed/src/java.base/share/classes/com/ibm/security/criu/NativePRNG.java create mode 100644 closed/src/java.base/share/classes/com/ibm/security/criu/SHA.java create mode 100644 closed/src/java.base/share/classes/java/security/CRIUConfigurator.java diff --git a/closed/GensrcJ9JCL.gmk b/closed/GensrcJ9JCL.gmk index 151736b07fe..058e408519c 100644 --- a/closed/GensrcJ9JCL.gmk +++ b/closed/GensrcJ9JCL.gmk @@ -46,12 +46,15 @@ ifeq (true,$(OPENJ9_ENABLE_OPENJDK_METHODHANDLES)) JPP_TAGS += OPENJDK_METHODHANDLES endif # OPENJ9_ENABLE_OPENJDK_METHODHANDLES -# OpenJ9 CRIU only supports Linux, so we only need to consider the unix sub-directory. -OPENJDK_SOURCE_PROCESS_ENVIRONMENT := $(TOPDIR)/src/java.base/unix/classes/java/lang/ProcessEnvironment.java -OPENJDK_STAGED_PROCESS_ENVIRONMENT := $(patsubst $(TOPDIR)/%,$(SUPPORT_OUTPUTDIR)/overlay/%,$(OPENJDK_SOURCE_PROCESS_ENVIRONMENT)) -$(OPENJDK_STAGED_PROCESS_ENVIRONMENT) : $(OPENJDK_SOURCE_PROCESS_ENVIRONMENT) - $(call install-file) +# OpenJ9 CRIU only supports Linux, so we only need to consider the share and unix sub-directories. +$(eval $(call SetupCopyFiles,COPY_OVERLAY_FILES, \ + SRC := $(TOPDIR), \ + DEST := $(SUPPORT_OUTPUTDIR)/overlay, \ + FILES := \ + src/java.base/share/classes/java/security/Security.java \ + src/java.base/unix/classes/java/lang/ProcessEnvironment.java \ + )) # invoke JPP to preprocess java source files # $1 - configuration @@ -76,7 +79,7 @@ IncludeIfUnsure := -includeIfUnsure -noWarnIncludeIf $(J9JCL_SOURCES_DONEFILE) : \ $(foreach dir, $(JppSourceDirs), $(call RecursiveWildcard,$(dir),*.java)) \ - $(OPENJDK_STAGED_PROCESS_ENVIRONMENT) + $(COPY_OVERLAY_FILES) @$(ECHO) Building OpenJ9 Java Preprocessor @$(MKDIR) -p $(J9TOOLS_DIR) $(MAKE) $(MAKE_ARGS) -C $(OPENJ9_TOPDIR)/sourcetools -f buildj9tools.mk \ diff --git a/closed/src/java.base/share/classes/com/ibm/security/criu/CRIUSECProvider.java b/closed/src/java.base/share/classes/com/ibm/security/criu/CRIUSECProvider.java new file mode 100644 index 00000000000..e292821045a --- /dev/null +++ b/closed/src/java.base/share/classes/com/ibm/security/criu/CRIUSECProvider.java @@ -0,0 +1,63 @@ +/*[INCLUDE-IF CRIU_SUPPORT]*/ +/* + * =========================================================================== + * (c) Copyright IBM Corp. 2022, 2022 All Rights Reserved + * =========================================================================== + * + * 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. + * + * IBM designates this particular file as subject to the "Classpath" exception + * as provided by IBM 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, see . + * + * =========================================================================== + */ + +package com.ibm.security.criu; + +import java.security.Provider; + +/** + * The CRIUSECProvider is a security provider that is used as follows when CRIU + * is enabled. During the checkpoint phase, all other security providers are + * removed, except CRIUSECProvider, and the digests are cleared, to ensure that + * no state is saved during checkpoint that is then restored during the restore + * phase. During the resore phase, CRIUSECProvider is removed and the other + * security providers are added back. + */ +public final class CRIUSECProvider extends Provider { + + private static final long serialVersionUID = -3240458633432287743L; + + public CRIUSECProvider() { + super("CRIUSEC", "1", "CRIUSEC Provider for IBM Semeru"); + + 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)); + } + + /** + * Resets the security digests. + */ + public static void resetDigests() { + DigestBase.resetDigests(); + } +} diff --git a/closed/src/java.base/share/classes/com/ibm/security/criu/DigestBase.java b/closed/src/java.base/share/classes/com/ibm/security/criu/DigestBase.java new file mode 100644 index 00000000000..8def36aefdf --- /dev/null +++ b/closed/src/java.base/share/classes/com/ibm/security/criu/DigestBase.java @@ -0,0 +1,284 @@ +/*[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 com.ibm.security.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/com/ibm/security/criu/NativePRNG.java b/closed/src/java.base/share/classes/com/ibm/security/criu/NativePRNG.java new file mode 100644 index 00000000000..6c85fc6b422 --- /dev/null +++ b/closed/src/java.base/share/classes/com/ibm/security/criu/NativePRNG.java @@ -0,0 +1,249 @@ +/*[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 com.ibm.security.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"; + + // 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_RANDOM); + + 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) { + int len = bytes.length; + byte[] b = INSTANCE.implGenerateSeed(len); + System.arraycopy(b, 0, bytes, 0, len); + } + + // get true random bytes + @Override + protected byte[] engineGenerateSeed(int numBytes) { + return INSTANCE.implGenerateSeed(numBytes); + } + + /** + * 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; + + // mutex lock for generateSeed() + private final Object LOCK_GET_SEED = new Object(); + + // mutex lock for setSeed() + private final Object LOCK_SET_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; + } + + // 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; + int ofs = 0; + while (len > 0) { + int k = in.read(data, ofs, len); + if (k <= 0) { + throw new EOFException("File(s) closed?"); + } + ofs += k; + len -= k; + } + if (len > 0) { + 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 void implSetSeed(byte[] seed) { + synchronized (LOCK_SET_SEED) { + if (seedOutInitialized == false) { + seedOutInitialized = true; + seedOut = AccessController.doPrivileged( + new PrivilegedAction<>() { + @Override + public OutputStream run() { + try { + return new FileOutputStream(seedFile, true); + } catch (Exception 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. + } + } + } + } + } +} diff --git a/closed/src/java.base/share/classes/com/ibm/security/criu/SHA.java b/closed/src/java.base/share/classes/com/ibm/security/criu/SHA.java new file mode 100644 index 00000000000..4690abcc511 --- /dev/null +++ b/closed/src/java.base/share/classes/com/ibm/security/criu/SHA.java @@ -0,0 +1,240 @@ +/*[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 com.ibm.security.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/java/security/CRIUConfigurator.java b/closed/src/java.base/share/classes/java/security/CRIUConfigurator.java new file mode 100644 index 00000000000..3f584f4f761 --- /dev/null +++ b/closed/src/java.base/share/classes/java/security/CRIUConfigurator.java @@ -0,0 +1,103 @@ +/*[INCLUDE-IF CRIU_SUPPORT]*/ +/* + * =========================================================================== + * (c) Copyright IBM Corp. 2022, 2022 All Rights Reserved + * =========================================================================== + * + * 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. + * + * IBM designates this particular file as subject to the "Classpath" exception + * as provided by IBM 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, see . + * + * =========================================================================== + */ + +package java.security; + +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +import sun.security.action.GetPropertyAction; +import sun.security.jca.ProviderList; +import sun.security.jca.Providers; + +/** + * Configures the security providers when in CRIU mode. + */ +public final class CRIUConfigurator { + private static Properties systemProps; + /** Stores the old security providers (position, name). */ + private static final HashMap oldProviders = new HashMap<>(); + /** Tracing for CRIUSEC. */ + private static final boolean debug = Boolean.parseBoolean(GetPropertyAction.privilegedGetProperty + ("enable.j9internal.checkpoint.security.api.debug", "false")); + + /** + * Removes the usual security providers and adds the CRIU security provider. + * + * @param props the system properties + */ + public static void setCRIUSecMode(Properties props) { + systemProps = props; + + for (Map.Entry entry : props.entrySet()) { + String key = (String) entry.getKey(); + if (key.startsWith("security.provider.")) { + oldProviders.put(key, (String) entry.getValue()); + } + } + for (String provider : oldProviders.keySet()) { + props.remove(provider); + } + props.put("security.provider.1", "com.ibm.security.criu.CRIUSECProvider"); + + if (debug) { + System.out.println("CRIUSEC added and all other security providers removed."); + } + } + + /** + * Removes the CRIU security provider and adds the usual security providers back. + */ + public static void setCRIURestoreMode() { + Security.removeProvider("CRIUSEC"); + // Note that CRIUSEC was set as security.provider.1 in the method setCRIUSecMode, + // which is called before this method. + systemProps.remove("security.provider.1"); + if (debug) { + System.out.println("CRIUSEC removed."); + } + + for (Map.Entry entry : oldProviders.entrySet()) { + systemProps.put(entry.getKey(), entry.getValue()); + } + try { + // Invoke the fromSecurityProperties method from the ProviderList class. + Class runnable = Class.forName("sun.security.jca.ProviderList", true, ClassLoader.getSystemClassLoader()); + Method readProperties = runnable.getDeclaredMethod("fromSecurityProperties"); + readProperties.setAccessible(true); + ProviderList providerList = (ProviderList) readProperties.invoke(null); + Providers.setProviderList(providerList); + } catch (Exception e) { + System.out.println(e.toString()); + } + if (debug) { + for (String provider : oldProviders.values()) { + System.out.println(provider + " restored."); + } + } + } +} diff --git a/src/java.base/share/classes/java/security/Security.java b/src/java.base/share/classes/java/security/Security.java index b36510a376b..79c5a11caa5 100644 --- a/src/java.base/share/classes/java/security/Security.java +++ b/src/java.base/share/classes/java/security/Security.java @@ -23,6 +23,12 @@ * questions. */ +/* + * =========================================================================== + * (c) Copyright IBM Corp. 2022, 2022 All Rights Reserved + * =========================================================================== + */ + package java.security; import java.util.*; @@ -39,6 +45,10 @@ import sun.security.jca.*; +/*[IF CRIU_SUPPORT]*/ +import openj9.internal.criu.InternalCRIUSupport; +/*[ENDIF] CRIU_SUPPORT*/ + /** *

This class centralizes all security properties and common security * methods. One of its primary uses is to manage providers. @@ -192,6 +202,13 @@ private static void initialize() { } } +/*[IF CRIU_SUPPORT]*/ + // Check if CRIU checkpoint mode is enabled, if it is then reconfigure the security providers. + if (InternalCRIUSupport.isCheckpointAllowed()) { + CRIUConfigurator.setCRIUSecMode(props); + } +/*[ENDIF] CRIU_SUPPORT*/ + } /*