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:
commit
0a580b313d
70422 changed files with 17213626 additions and 0 deletions
74
test/jdk/java/security/MessageDigest/ArgumentSanity.java
Normal file
74
test/jdk/java/security/MessageDigest/ArgumentSanity.java
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
97
test/jdk/java/security/MessageDigest/ByteBuffers.java
Normal file
97
test/jdk/java/security/MessageDigest/ByteBuffers.java
Normal 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();
|
||||
}
|
||||
}
|
||||
128
test/jdk/java/security/MessageDigest/TestCloneable.java
Normal file
128
test/jdk/java/security/MessageDigest/TestCloneable.java
Normal 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;
|
||||
}
|
||||
}
|
||||
344
test/jdk/java/security/MessageDigest/TestDigestIOStream.java
Normal file
344
test/jdk/java/security/MessageDigest/TestDigestIOStream.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
146
test/jdk/java/security/MessageDigest/TestSameLength.java
Normal file
146
test/jdk/java/security/MessageDigest/TestSameLength.java
Normal 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);
|
||||
}
|
||||
}
|
||||
305
test/jdk/java/security/MessageDigest/TestSameValue.java
Normal file
305
test/jdk/java/security/MessageDigest/TestSameValue.java
Normal 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;
|
||||
}
|
||||
}
|
||||
162
test/jdk/java/security/MessageDigest/ThreadSafetyTest.java
Normal file
162
test/jdk/java/security/MessageDigest/ThreadSafetyTest.java
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue