undefect. CWE-407 — 63 sites patched across 27 ecosystems

Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com

Patches, unit tests, benchmarks, whitepaper, and outreach briefs.
Public domain — no copyright claimed. Use freely.
This commit is contained in:
russell@unturf.com 2026-03-26 17:11:57 -04:00
commit 0a580b313d
70422 changed files with 17213626 additions and 0 deletions

View file

@ -0,0 +1,74 @@
/*
* Copyright (c) 1997, 1998, 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.
*/
/*
* @test
* @bug 4100227
* @summary Do some sanity checks on the input arguments
*/
import java.security.*;
public class ArgumentSanity {
public static void main(String[]args) throws Exception {
byte[] data = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };
byte[] out = new byte[16];
MessageDigest dig = null;
try {
dig = MessageDigest.getInstance("md5");
try {
dig.update(null, 5, 20);
} catch (IllegalArgumentException e) {
System.err.println(e);
}
try {
dig.update(data, 5, 20);
} catch (IllegalArgumentException e) {
System.err.println(e);
}
try {
dig.digest(null, 5, 20);
} catch (IllegalArgumentException e) {
System.err.println(e);
}
try {
dig.digest(out, 5, 20);
} catch (IllegalArgumentException e) {
System.err.println(e);
}
System.out.println("Test succeeded");
} catch (Exception e) {
throw new Exception("Test failed: Wrong exception thrown");
}
}
}

View file

@ -0,0 +1,97 @@
/*
* 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
* 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.
*/
/**
* @test
* @bug 4844847
* @summary Test the MessageDigest.update(ByteBuffer) method
* @author Andreas Sterbenz
* @key randomness
* @run main ByteBuffers MD5
* @run main ByteBuffers SHA-1
*/
import java.util.*;
import java.nio.*;
import java.security.*;
public class ByteBuffers {
public static void main(String[] args) throws Exception {
Provider p = Security.getProvider(System.getProperty("test.provider.name", "SUN"));
Random random = new Random();
int n = 10 * 1024;
byte[] t = new byte[n];
random.nextBytes(t);
String digestAlgo = args[0];
MessageDigest md = MessageDigest.getInstance(digestAlgo, p);
byte[] d1 = md.digest(t);
// test 1: ByteBuffer with an accessible backing array
ByteBuffer b1 = ByteBuffer.allocate(n + 256);
b1.position(random.nextInt(256));
b1.limit(b1.position() + n);
ByteBuffer b2 = b1.slice();
b2.put(t);
b2.clear();
byte[] d2 = digest(md, b2, random);
if (Arrays.equals(d1, d2) == false) {
throw new Exception("Test 1 failed");
}
// test 2: direct ByteBuffer
ByteBuffer b3 = ByteBuffer.allocateDirect(t.length);
b3.put(t);
b3.clear();
byte[] d3 = digest(md, b3, random);
if (Arrays.equals(d1, d3) == false) {
throw new Exception("Test 2 failed");
}
// test 3: ByteBuffer without an accessible backing array
b2.clear();
ByteBuffer b4 = b2.asReadOnlyBuffer();
byte[] d4 = digest(md, b4, random);
if (Arrays.equals(d1, d4) == false) {
throw new Exception("Test 3 failed");
}
System.out.println("All tests passed");
}
private static byte[] digest(MessageDigest md, ByteBuffer b, Random random) throws Exception {
int lim = b.limit();
b.limit(random.nextInt(lim));
md.update(b);
if (b.hasRemaining()) {
throw new Exception("Buffer not consumed");
}
b.limit(lim);
md.update(b);
if (b.hasRemaining()) {
throw new Exception("Buffer not consumed");
}
return md.digest();
}
}

View file

@ -0,0 +1,128 @@
/*
* Copyright (c) 2020, 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.
*/
/*
* @test
* @bug 8246077 8300416
* @summary Make sure that digest spi and the resulting digest impl are
* consistent in the impl of Cloneable interface, and that clones do not
* share memory.
* @run testng TestCloneable
*/
import java.nio.ByteBuffer;
import java.security.*;
import java.util.Arrays;
import java.util.Random;
import java.util.Objects;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import org.testng.Assert;
public class TestCloneable {
private static final Class<CloneNotSupportedException> CNSE =
CloneNotSupportedException.class;
private static String providerName = System.getProperty("test.provider.name", "SUN");
@DataProvider
public Object[][] testData() {
return new Object[][] {
{ "MD2", providerName }, { "MD5", providerName }, { "SHA-1", providerName },
{ "SHA-224", providerName }, { "SHA-256", providerName },
{ "SHA-384", providerName }, { "SHA-512", providerName },
{ "SHA3-224", providerName }, { "SHA3-256", providerName },
{ "SHA3-384", providerName }, { "SHA3-512", providerName }
};
}
@Test(dataProvider = "testData")
public void test(String algo, String provName)
throws NoSuchProviderException, NoSuchAlgorithmException,
CloneNotSupportedException, InterruptedException {
System.out.print("Testing " + algo + " impl from " + provName);
Provider p = Security.getProvider(provName);
Provider.Service s = p.getService("MessageDigest", algo);
Objects.requireNonNull(s);
MessageDigestSpi spi = (MessageDigestSpi) s.newInstance(null);
MessageDigest md = MessageDigest.getInstance(algo, provName);
if (spi instanceof Cloneable) {
System.out.println(": Cloneable");
Assert.assertTrue(md instanceof Cloneable);
MessageDigest md2 = (MessageDigest) md.clone();
Assert.assertEquals(md2.getAlgorithm(), algo);
Assert.assertEquals(md2.getProvider().getName(), provName);
Assert.assertTrue(md2 instanceof Cloneable);
} else {
System.out.println(": NOT Cloneable");
Assert.assertThrows(CNSE, ()->md.clone());
}
System.out.print("Testing " + algo + " impl from " + provName);
final var d1 = MessageDigest.getInstance(algo, provName);
final var buffer = ByteBuffer.allocateDirect(1024);
final var r = new Random(1024);
fillBuffer(r, buffer);
d1.update(buffer); // this statement triggers tempArray allocation
final var d2 = (MessageDigest) d1.clone();
assert Arrays.equals(d1.digest(), d2.digest());
final var t1 = updateThread(d1);
final var t2 = updateThread(d2);
t1.join();
t2.join();
System.out.println(": Shared data check");
// Random is producing the same sequence of bytes for each thread,
// and thus each MessageDigest should be equal. When the memory is
// shared, they inevitably overwrite each other's tempArray and
// you get different results.
if (!Arrays.equals(d1.digest(), d2.digest())) {
throw new AssertionError("digests differ");
}
System.out.println("Test Passed");
}
private static void fillBuffer(final Random r, final ByteBuffer buffer) {
final byte[] bytes = new byte[buffer.capacity()];
r.nextBytes(bytes);
buffer.clear();
buffer.put(bytes);
buffer.flip();
}
public static Thread updateThread(final MessageDigest d) {
final var t = new Thread(() -> {
final var r = new Random(1024);
final ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
for (int i = 0; i < 1024; i++) {
fillBuffer(r, buffer);
d.update(buffer);
}
});
t.start();
return t;
}
}

View file

@ -0,0 +1,344 @@
/*
* Copyright (c) 2003, 2020, 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.
*/
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.security.DigestInputStream;
import java.security.DigestOutputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.Security;
import java.util.Arrays;
import java.util.Random;
import jdk.test.lib.RandomFactory;
import static java.lang.System.out;
/**
* @test
* @bug 8050370 8156059
* @summary MessageDigest tests with DigestIOStream
* @author Kevin Liu
* @key randomness
* @library /test/lib
* @build jdk.test.lib.RandomFactory
* @run main/timeout=180 TestDigestIOStream
*/
enum ReadModel {
READ, BUFFER_READ, MIX_READ
}
public class TestDigestIOStream {
private static final int[] DATA_LEN_ARRAY = { 1, 50, 2500, 125000,
6250000 };
private static final String[] ALGORITHM_ARRAY = { "MD2", "MD5", "SHA1",
"SHA-224", "SHA-256", "SHA-384", "SHA-512", "SHA3-224", "SHA3-256",
"SHA3-384", "SHA3-512" };
private static byte[] data;
private static MessageDigest md = null;
public static void main(String argv[]) throws Exception {
TestDigestIOStream test = new TestDigestIOStream();
test.run();
}
public void run() throws Exception {
for (String algorithm : ALGORITHM_ARRAY) {
md = MessageDigest.getInstance(algorithm);
for (int length : DATA_LEN_ARRAY) {
Random rdm = RandomFactory.getRandom();
data = new byte[length];
rdm.nextBytes(data);
if (!testMDChange(algorithm, length)) {
throw new RuntimeException("testMDChange failed at:"
+ algorithm + "/" + length);
}
if (!testMDShare(algorithm, length)) {
throw new RuntimeException("testMDShare failed at:"
+ algorithm + "/" + length);
}
for (ReadModel readModel : ReadModel.values()) {
// test Digest function when digest switch on
if (!testDigestOnOff(algorithm, readModel, true,
length)) {
throw new RuntimeException(
"testDigestOn failed at:" + algorithm + "/"
+ length + "/" + readModel);
}
// test Digest function when digest switch off
if (!testDigestOnOff(algorithm, readModel, false,
length)) {
throw new RuntimeException(
"testDigestOff failed at:" + algorithm + "/"
+ length + "/" + readModel);
}
}
}
}
int testNumber = ALGORITHM_ARRAY.length * ReadModel.values().length
* DATA_LEN_ARRAY.length * 2
+ ALGORITHM_ARRAY.length * DATA_LEN_ARRAY.length * 2;
out.println("All " + testNumber + " Tests Passed");
}
/**
* Test DigestInputStream and DigestOutputStream digest function when digest
* set on and off
*
* @param algo
* Message Digest algorithm
* @param readModel
* which read method used(READ, BUFFER_READ, MIX_READ)
* @param on
* digest switch(on and off)
* @param dataLength
* plain test data length.
* @exception Exception
* throw unexpected exception
*/
public boolean testDigestOnOff(String algo, ReadModel readModel, boolean on,
int dataLength) throws Exception {
// Generate the DigestInputStream/DigestOutputStream object
try (ByteArrayInputStream bais = new ByteArrayInputStream(data);
DigestInputStream dis = new DigestInputStream(bais,
MessageDigest.getInstance(algo));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DigestOutputStream dos = new DigestOutputStream(baos,
MessageDigest.getInstance(algo));
ByteArrayOutputStream baOut = new ByteArrayOutputStream();) {
// Perform the update using all available/possible update methods
int k = 0;
byte[] buffer = new byte[5];
boolean enDigest = true;
// Make sure the digest function is on (default)
dis.on(enDigest);
dos.on(enDigest);
switch (readModel) {
case READ: // use only read()
while ((k = dis.read()) != -1) {
if (on) {
dos.write(k);
} else {
dos.write(k);
if (enDigest) {
baOut.write(k);
}
enDigest = !enDigest;
dos.on(enDigest);
dis.on(enDigest);
}
}
break;
case BUFFER_READ: // use only read(byte[], int, int)
while ((k = dis.read(buffer, 0, buffer.length)) != -1) {
if (on) {
dos.write(buffer, 0, k);
} else {
dos.write(buffer, 0, k);
if (enDigest) {
baOut.write(buffer, 0, k);
}
enDigest = !enDigest;
dis.on(enDigest);
dos.on(enDigest);
}
}
break;
case MIX_READ: // use both read() and read(byte[], int, int)
while ((k = dis.read()) != -1) {
if (on) {
dos.write(k);
if ((k = dis.read(buffer, 0, buffer.length)) != -1) {
dos.write(buffer, 0, k);
}
} else {
dos.write(k);
if (enDigest) {
baOut.write(k);
}
enDigest = !enDigest;
dis.on(enDigest);
dos.on(enDigest);
if ((k = dis.read(buffer, 0, buffer.length)) != -1) {
dos.write(buffer, 0, k);
if (enDigest) {
baOut.write(buffer, 0, k);
}
enDigest = !enDigest;
dis.on(enDigest);
dos.on(enDigest);
}
}
}
break;
default:
out.println("ERROR: Invalid read/write combination choice!");
return false;
}
// Get the output and the "correct" digest values
byte[] output1 = dis.getMessageDigest().digest();
byte[] output2 = dos.getMessageDigest().digest();
byte[] standard;
if (on) {
standard = md.digest(data);
} else {
byte[] dataDigested = baOut.toByteArray();
standard = md.digest(dataDigested);
}
// Compare the output byte array value to the input data
if (!MessageDigest.isEqual(data, baos.toByteArray())) {
out.println("ERROR of " + readModel
+ ": output and input data unexpectedly changed");
return false;
}
// Compare generated digest values
if (!MessageDigest.isEqual(output1, standard)
|| !MessageDigest.isEqual(output2, standard)) {
out.println("ERROR" + readModel
+ ": generated digest data unexpectedly changed");
return false;
}
return true;
} catch (Exception ex) {
out.println("testDigestOnOff failed at:" + algo + "/" + readModel
+ "/" + dataLength + " with unexpected exception");
throw ex;
}
}
/**
* Test DigestInputStream and DigestOutputStream digest function when Swap
* the message digest engines between DigestIn/OutputStream
*
* @param algo
* Message Digest algorithm
* @param dataLength
* plain test data length.
* @exception Exception
* throw unexpected exception
*/
public boolean testMDChange(String algo, int dataLength) throws Exception {
// Generate the DigestInputStream/DigestOutputStream object
MessageDigest mdIn = MessageDigest.getInstance(algo);
MessageDigest mdOut = MessageDigest.getInstance(algo);
try (ByteArrayInputStream bais = new ByteArrayInputStream(data);
DigestInputStream dis = new DigestInputStream(bais, mdIn);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DigestOutputStream dos = new DigestOutputStream(baos, mdOut);) {
// Perform the update using all available/possible update methods
int k = 0;
byte[] buffer = new byte[10];
// use both read() and read(byte[], int, int)
while ((k = dis.read()) != -1) {
dos.write(k);
if ((k = dis.read(buffer, 0, buffer.length)) != -1) {
dos.write(buffer, 0, k);
}
// Swap the message digest engines between
// DigestIn/OutputStream objects
dis.setMessageDigest(mdOut);
dos.setMessageDigest(mdIn);
mdIn = dis.getMessageDigest();
mdOut = dos.getMessageDigest();
}
// Get the output and the "correct" digest values
byte[] output1 = mdIn.digest();
byte[] output2 = mdOut.digest();
byte[] standard = md.digest(data);
// Compare generated digest values
return MessageDigest.isEqual(output1, standard)
&& MessageDigest.isEqual(output2, standard);
} catch (Exception ex) {
out.println("testMDChange failed at:" + algo + "/" + dataLength
+ " with unexpected exception");
throw ex;
}
}
/**
* Test DigestInputStream and DigestOutputStream digest function when use
* same message digest object.
*
* @param algo
* Message Digest algorithm
* @param dataLength
* plain test data length.
* @exception Exception
* throw unexpected exception
*/
public boolean testMDShare(String algo, int dataLength) throws Exception {
MessageDigest mdCommon = MessageDigest.getInstance(algo);
// Generate the DigestInputStream/DigestOutputStream object
try (ByteArrayInputStream bais = new ByteArrayInputStream(data);
DigestInputStream dis = new DigestInputStream(bais, mdCommon);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DigestOutputStream dos = new DigestOutputStream(baos,
mdCommon);) {
// Perform the update using all available/possible update methods
int k = 0;
byte[] buffer = new byte[10];
// use both read() and read(byte[], int, int)
while (k < data.length) {
int len = dis.read(buffer, 0, buffer.length);
if (len != -1) {
k += len;
if (k < data.length) {
dos.write(data[k]);
k++;
dis.skip(1);
}
}
}
// Get the output and the "correct" digest values
byte[] output = mdCommon.digest();
byte[] standard = md.digest(data);
// Compare generated digest values
return MessageDigest.isEqual(output, standard);
} catch (Exception ex) {
out.println("TestMDShare failed at:" + algo + "/" + dataLength
+ " with unexpected exception");
throw ex;
}
}
}

View file

@ -0,0 +1,93 @@
/*
* 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.
*
* 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.
*/
/**
* @test
* @bug 8244336
* @summary Test JCE layer algorithm restriction
* @library /test/lib
* @run main/othervm TestDisabledAlgorithms MESSAGEdigest.Sha-512 true
* @run main/othervm TestDisabledAlgorithms messageDIGest.what false
* @run main/othervm TestDisabledAlgorithms meSSagedIgest.sHA-512/224 false
*/
import java.util.List;
import java.security.NoSuchAlgorithmException;
import java.security.MessageDigest;
import java.security.Provider;
import java.security.Security;
import jdk.test.lib.Utils;
public class TestDisabledAlgorithms {
private static final String PROP_NAME = "jdk.crypto.disabledAlgorithms";
private static void test(List<String> algos, Provider p,
boolean shouldThrow) throws Exception {
for (String a : algos) {
System.out.println("Testing " + (p != null ? p.getName() : "") +
": " + a + ", shouldThrow=" + shouldThrow);
if (shouldThrow) {
if (p == null) {
Utils.runAndCheckException(() -> MessageDigest.getInstance(a),
NoSuchAlgorithmException.class);
} else {
Utils.runAndCheckException(() -> MessageDigest.getInstance(a, p),
NoSuchAlgorithmException.class);
Utils.runAndCheckException(() -> MessageDigest.getInstance(a,
p.getName()), NoSuchAlgorithmException.class);
}
} else {
MessageDigest m;
if (p == null) {
m = MessageDigest.getInstance(a);
} else {
m = MessageDigest.getInstance(a, p);
m = MessageDigest.getInstance(a, p.getName());
}
System.out.println("Got MessageDigest w/ algo " +
m.getAlgorithm());
}
}
}
public static void main(String[] args) throws Exception {
String propValue = args[0];
System.out.println("Setting Security Prop " + PROP_NAME + " = " +
propValue);
Security.setProperty(PROP_NAME, propValue);
boolean shouldThrow = Boolean.valueOf(args[1]);
List<String> algos = List.of("sHA-512", "shA-512",
"2.16.840.1.101.3.4.2.3");
// test w/o provider
test(algos, null, shouldThrow);
// test w/ provider
Provider[] providers = Security.getProviders("MessageDigest.SHA-512");
for (Provider p : providers) {
test(algos, p, shouldThrow);
}
}
}

View file

@ -0,0 +1,146 @@
/*
* Copyright (c) 2015, 2020, 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.
*/
import static java.lang.System.out;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.Security;
import jdk.test.lib.RandomFactory;
/**
* @test
* @bug 8050371 8156059
* @summary Check md.getDigestLength() equal digest output length with various
* algorithm/dataLen/(update,digest methods).
* @author Kevin Liu
* @key randomness
* @library /test/lib
* @build jdk.test.lib.RandomFactory
* @run main TestSameLength
*/
public class TestSameLength {
public static void main(String[] args) throws Exception {
TestSameLength test = new TestSameLength();
test.run();
}
private void run() throws Exception {
String[] algorithmArr = { "SHA", "Sha", "SHA-1", "sha-1", "SHA1",
"sha1", "MD5", "md5", "SHA-224", "SHA-256", "SHA-384",
"SHA-512", "SHA3-224", "SHA3-256", "SHA3-384", "SHA3-512" };
int[] nUpdatesArr = { 0, 1, 2, 3 };
int[] dataLenArr = { 1, 50, 2500, 125000, 6250000 };
for (String algorithm : algorithmArr) {
for (UpdateMethod update : UpdateMethod.values()) {
for (int dataLen : dataLenArr) {
if (!runTest(algorithm, dataLen, update)) {
throw new RuntimeException(
"Test failed at algorithm/dataLen/numUpdate:"
+ algorithm + "/" + dataLen + "/"
+ update.toString());
}
}
}
}
out.println("All "
+ algorithmArr.length * nUpdatesArr.length * dataLenArr.length
+ " tests Passed");
}
private boolean runTest(String algo, long dataLen, UpdateMethod whichUpdate)
throws Exception {
try {
// Do initialization
byte[] data = new byte[(int) dataLen];
RandomFactory.getRandom().nextBytes(data);
MessageDigest md = MessageDigest.getInstance(algo);
int outputLen = md.getDigestLength();
// Perform the update using all available/possible update methods
whichUpdate.updateDigest(data, md, dataLen);
// Get the output
byte[] output = md.digest();
// Compare input and output
return outputLen == output.length;
} catch (NoSuchAlgorithmException nae) {
throw nae;
} catch (Exception ex) {
System.err.println("Testing: " + algo + "/" + dataLen + "/"
+ whichUpdate.toString()
+ " failed with unexpected exception");
ex.printStackTrace();
throw ex;
}
}
private static enum UpdateMethod {
UPDATE_BYTE {
@Override
public void updateDigest(byte[] data, MessageDigest md,
long dataLen) {
for (int i = 0; i < dataLen; i++) {
md.update(data[i]);
}
}
},
UPDATE_BUFFER {
@Override
public void updateDigest(byte[] data, MessageDigest md,
long dataLen) {
md.update(data);
}
},
UPDATE_BUFFER_LEN {
@Override
public void updateDigest(byte[] data, MessageDigest md,
long dataLen) {
for (int i = 0; i < dataLen; i++) {
md.update(data, i, 1);
}
}
},
UPDATE_BYTE_BUFFER {
@Override
public void updateDigest(byte[] data, MessageDigest md,
long dataLen) {
md.update(ByteBuffer.wrap(data));
}
};
public abstract void updateDigest(byte[] data, MessageDigest md,
long dataLen);
}
}

View file

@ -0,0 +1,305 @@
/*
* Copyright (c) 2015, 2020, 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.
*/
import static java.lang.System.out;
import java.nio.ByteBuffer;
import java.security.DigestException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.Security;
import jdk.test.lib.RandomFactory;
/**
* @test
* @bug 8050371 8156059
* @summary Check md.digest(data) value whether same with digest output value
* with various update/digest methods.
* @author Kevin Liu
* @key randomness
* @library /test/lib
* @build jdk.test.lib.RandomFactory
* @run main TestSameValue
*/
public class TestSameValue {
public static void main(String[] args) throws Exception {
TestSameValue test1 = new TestSameValue();
test1.run();
}
private void run() throws Exception {
byte[] data = new byte[6706];
MessageDigest md = null;
// Initialize input data
RandomFactory.getRandom().nextBytes(data);
String[] algorithmArr = { "SHA", "Sha", "MD5", "md5", "SHA-224",
"SHA-256", "SHA-384", "SHA-512", "SHA3-224", "SHA3-256",
"SHA3-384", "SHA3-512" };
for (String algorithm : algorithmArr) {
md = MessageDigest.getInstance(algorithm);
for (UpdateDigestMethod updateMethod : UpdateDigestMethod
.values()) {
byte[] output = updateMethod.updateDigest(data, md);
// Get the output and the "correct" one
byte[] standard = md.digest(data);
// Compare input and output
if (!MessageDigest.isEqual(output, standard)) {
throw new RuntimeException(
"Test failed at algorithm/provider/numUpdate:"
+ algorithm + "/" + md.getProvider()
+ "/" + updateMethod);
}
}
}
out.println("All "
+ algorithmArr.length * UpdateDigestMethod.values().length
+ " tests Passed");
}
private static enum UpdateDigestMethod {
/*
* update the data one by one using method update(byte input) then do
* digest (giving the output buffer, offset, and the number of bytes to
* put in the output buffer)
*/
UPDATE_DIGEST_BUFFER {
@Override
public byte[] updateDigest(byte[] data, MessageDigest md)
throws DigestException {
for (byte element : data) {
md.update(element);
}
byte[] output = new byte[md.getDigestLength()];
int len = md.digest(output, 0, output.length);
if (len != output.length) {
throw new RuntimeException(
"ERROR" + ": digest length differs!");
}
return output;
}
},
/*
* update the data one by one using method update(byte input) then do
* digest
*/
UPDATE_DIGEST {
@Override
public byte[] updateDigest(byte[] data, MessageDigest md) {
for (byte element : data) {
md.update(element);
}
return md.digest();
}
},
/*
* update all the data at once as a block, then do digest ( giving the
* output buffer, offset, and the number of bytes to put in the output
* buffer)
*/
UPDATE_BLOCK_DIGEST_BUFFER {
@Override
public byte[] updateDigest(byte[] data, MessageDigest md)
throws DigestException {
md.update(data);
byte[] output = new byte[md.getDigestLength()];
int len = md.digest(output, 0, output.length);
if (len != output.length) {
throw new RuntimeException(
"ERROR" + ": digest length differs!");
}
return output;
}
},
// update all the data at once as a block, then do digest
UPDATE_BLOCK_DIGEST {
@Override
public byte[] updateDigest(byte[] data, MessageDigest md) {
md.update(data);
return md.digest();
}
},
/*
* update the leading bytes (length is "data.length-LASTNBYTES") at once
* as a block, then do digest (do a final update using the left
* LASTNBYTES bytes which is passed as a parameter for the digest
* method, then complete the digest)
*/
UPDATE_LEADING_BLOCK_DIGEST_REMAIN {
@Override
public byte[] updateDigest(byte[] data, MessageDigest md) {
byte[] mainPart = new byte[data.length - LASTNBYTES];
for (int i = 0; i < mainPart.length; i++) {
mainPart[i] = data[i];
}
for (int j = 0; j < LASTNBYTES; j++) {
REMAIN[j] = data[data.length - LASTNBYTES + j];
}
md.update(mainPart);
return md.digest(REMAIN);
}
},
/*
* update the data 2 bytes each time, after finishing updating, do
* digest (giving the output buffer, offset, and the number of bytes to
* put in the output buffer)
*/
UPDATE_BYTES_DIGEST_BUFFER {
@Override
public byte[] updateDigest(byte[] data, MessageDigest md)
throws DigestException {
for (int i = 0; i < data.length / 2; i++) {
md.update(data, i * 2, 2);
}
byte[] output = new byte[md.getDigestLength()];
int len = md.digest(output, 0, output.length);
if (len != output.length) {
throw new RuntimeException(
"ERROR" + ": digest length differs!");
}
return output;
}
},
/*
* update the data 2 bytes each time, after finishing updating, do
* digest
*/
UPDATE_BYTES_DIGEST {
@Override
public byte[] updateDigest(byte[] data, MessageDigest md) {
for (int i = 0; i < data.length / 2; i++) {
md.update(data, i * 2, 2);
}
return md.digest();
}
},
/*
* update the data one by one using method update(byte[] input, int
* offset, int len) for the leading bytes (length is
* "data.length-LASTNBYTES"), then do digest (do a final update using
* the left LASTNBYTES bytes which is passed as a parameter for digest
* method then complete the digest)
*/
UPDATE_BUFFER_LEADING_DIGEST_REMAIN {
@Override
public byte[] updateDigest(byte[] data, MessageDigest md) {
for (int i = 0; i < data.length - LASTNBYTES; i++) {
md.update(data, i, 1);
}
for (int j = 0; j < LASTNBYTES; j++) {
REMAIN[j] = data[data.length - LASTNBYTES + j];
}
return md.digest(REMAIN);
}
},
/*
* update the data one by one using method update(byte input) for the
* leading bytes (length is "data.length-LASTNBYTES"), then do digest
* (do a final update using the left LASTNBYTES bytes which is passed as
* a parameter for digest method, then complete the digest)
*/
UPDATE_LEADING_DIGEST_REMAIN {
@Override
public byte[] updateDigest(byte[] data, MessageDigest md) {
for (int i = 0; i < data.length - LASTNBYTES; i++) {
md.update(data[i]);
}
for (int j = 0; j < LASTNBYTES; j++) {
REMAIN[j] = data[data.length - LASTNBYTES + j];
}
return md.digest(REMAIN);
}
},
/*
* update all the data at once as a ByteBuffer, then do digest (giving
* the output buffer, offset, and the number of bytes to put in the
* output buffer)
*/
UPDATE_BYTE_BUFFER_DIGEST_BUFFER {
@Override
public byte[] updateDigest(byte[] data, MessageDigest md)
throws DigestException {
md.update(ByteBuffer.wrap(data));
byte[] output = new byte[md.getDigestLength()];
int len = md.digest(output, 0, output.length);
if (len != output.length) {
throw new RuntimeException(
"ERROR" + ": digest length differs!");
}
return output;
}
},
// update all the data at once as a ByteBuffer, then do digest
UPDATE_BYTE_BUFFER_DIGEST {
@Override
public byte[] updateDigest(byte[] data, MessageDigest md) {
md.update(ByteBuffer.wrap(data));
return md.digest();
}
},
/*
* update the leading bytes (length is "data.length-LASTNBYTES") at once
* as a ByteBuffer, then do digest (do a final update using the left
* LASTNBYTES bytes which is passed as a parameter for the digest
* method, then complete the digest)
*/
UPDATE_BYTE_BUFFER_LEADING_DIGEST_REMAIN {
@Override
public byte[] updateDigest(byte[] data, MessageDigest md) {
byte[] mainPart = new byte[data.length - LASTNBYTES];
for (int i = 0; i < mainPart.length; i++) {
mainPart[i] = data[i];
}
for (int j = 0; j < LASTNBYTES; j++) {
REMAIN[j] = data[data.length - LASTNBYTES + j];
}
md.update(ByteBuffer.wrap(mainPart));
return md.digest(REMAIN);
}
};
private static final int LASTNBYTES = 5;
private static final byte[] REMAIN = new byte[LASTNBYTES];
public abstract byte[] updateDigest(byte[] data, MessageDigest md)
throws DigestException;
}
}

View file

@ -0,0 +1,162 @@
/*
* Copyright (c) 2020, 2021, Azul Systems, Inc. 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.
*/
/*
* @test
* @bug 8241960 8277353
* @summary Confirm that java.security.MessageDigest is thread-safe after clone.
* @run main ThreadSafetyTest 4 2
*/
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.security.Security;
import java.util.Arrays;
import java.util.Random;
// Usage: java ThreadSafetyTest [threadsFactor [duration]]
public class ThreadSafetyTest {
static volatile boolean runrun = true;
static volatile boolean error = false;
private static final String[] ALGORITHM_ARRAY = { "MD2", "MD5",
"SHA1", "SHA-224", "SHA-256", "SHA-384",
"SHA-512", "SHA-512/224", "SHA-512/256",
"SHA3-224", "SHA3-256", "SHA3-384", "SHA3-512" };
public static void main(String[] args) throws Exception {
int threadsFactor = 5;
if (args.length > 0) {
threadsFactor = Integer.parseInt(args[0]);
}
int duration = 4;
if (args.length > 1) {
duration = Integer.parseInt(args[1]);
}
int nProcessors = Runtime.getRuntime().availableProcessors();
int nTasks = Math.min(nProcessors, 4) * threadsFactor;
System.out.println("Testing with " + nTasks + " threads on " +
nProcessors + " processors for " + duration +
" seconds.");
// Initialize input data
byte input[] = new byte[1024];
(new Random()).nextBytes(input);
for (Provider p : Security.getProviders()) {
for (String alg : ALGORITHM_ARRAY) {
try {
MessageDigest md = MessageDigest.getInstance(alg, p);
testThreadSafety(md, input, nTasks, duration, false);
if (isClonable(md)) {
md.reset();
testThreadSafety(md, input, nTasks, duration, true);
}
} catch (NoSuchAlgorithmException nae) {
// algorithm not supported, just skip
}
}
}
}
static private void testThreadSafety(final MessageDigest originalMD,
final byte[] input, final int nTasks,
final int duration, final boolean useClone) {
Thread[] tasks = new Thread[nTasks];
byte[] expectedOut = getHash(originalMD, input, useClone);
originalMD.reset();
runrun = true;
for (int i = 0; i < nTasks; i++) {
tasks[i] = new Thread(new Runnable() {
public void run() {
MessageDigest md = getMessageDigest(originalMD, useClone);
while (runrun) {
byte newOut[] = getHash(md, input, useClone);
if (!Arrays.equals(expectedOut, newOut)) {
runrun = false;
error = true;
}
}
}
});
}
for (int i = 0; i < nTasks; i++) {
tasks[i].start();
}
try {
for (int i = 0; runrun && i < duration; i++) {
Thread.sleep(1000); // 1 second
}
runrun = false;
for (int i = 0; i < nTasks; i++) {
tasks[i].join();
}
} catch (InterruptedException e) {
}
if (error) {
throw new RuntimeException("MessageDigest " + originalMD.getAlgorithm() +
" in the provider " + originalMD.getProvider().getName() +
" is not thread-safe" + (useClone?" after clone.":"." ));
}
}
static private byte[] getHash(final MessageDigest messageDigest,
final byte[] input, final boolean useClone) {
for (int i = 0; i < 100; i++)
messageDigest.update(input);
return messageDigest.digest();
}
static private MessageDigest getMessageDigest(final MessageDigest prototype,
final boolean useClone) {
try {
if (useClone) {
return (MessageDigest)prototype.clone();
}
return MessageDigest.getInstance(
prototype.getAlgorithm(), prototype.getProvider());
} catch (final CloneNotSupportedException | NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
// The impls from libucrypto does not support clone but ones
// from libmd do.
static private boolean isClonable(final MessageDigest messageDigest) {
try {
messageDigest.clone();
return true;
} catch (final CloneNotSupportedException e) {
return false;
}
}
}