0b83fc0150
Reviewed-by: xuelei, erikj
248 lines
9.0 KiB
Java
248 lines
9.0 KiB
Java
/*
|
|
* Copyright (c) 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 com.sun.jdi.Bootstrap;
|
|
import com.sun.jdi.VMDisconnectedException;
|
|
import com.sun.jdi.VirtualMachine;
|
|
import com.sun.jdi.connect.Connector;
|
|
import com.sun.jdi.connect.LaunchingConnector;
|
|
import com.sun.jdi.event.Event;
|
|
import com.sun.jdi.event.EventSet;
|
|
import com.sun.jdi.event.MethodEntryEvent;
|
|
import com.sun.jdi.request.MethodEntryRequest;
|
|
|
|
import java.security.AlgorithmParameters;
|
|
import java.security.KeyPair;
|
|
import java.security.KeyPairGenerator;
|
|
import java.security.SecureRandom;
|
|
import java.security.Signature;
|
|
import java.security.SignatureException;
|
|
import java.security.interfaces.ECPrivateKey;
|
|
import java.security.interfaces.ECPublicKey;
|
|
import java.security.spec.ECGenParameterSpec;
|
|
import java.security.spec.ECParameterSpec;
|
|
import java.util.Arrays;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Random;
|
|
|
|
/*
|
|
* @test
|
|
* @bug 8237218 8239928
|
|
* @modules jdk.crypto.ec
|
|
* jdk.jdi
|
|
* @requires os.family != "windows"
|
|
* @run main ECDSAJavaVerify debug
|
|
* @summary Support NIST Curves verification in java implementation.
|
|
* This test does not run stable on Windows. VMDisconnectedException
|
|
* might not be thrown at all.
|
|
*/
|
|
|
|
// ATTENTION: This test depends on method names inside the non-exported
|
|
// class sun.security.ec.ECDSASignature.
|
|
public class ECDSAJavaVerify {
|
|
|
|
static final String[] ALL_ALGS = new String[] {
|
|
"SHA1withECDSA", "SHA256withECDSA", "SHA384withECDSA", "SHA512withECDSA"};
|
|
|
|
static final String[] ALL_CURVES = new String[] {
|
|
"secp256r1", "secp384r1", "secp521r1"};
|
|
|
|
public static void main(String[] args) throws Exception {
|
|
if (args.length == 1) {
|
|
// Debugging a new process with no arg
|
|
debug();
|
|
} else if (args.length == 3) {
|
|
// If one test case fail, re-run it with first 3 columns
|
|
new Test().run(Integer.parseInt(args[0]), args[1], args[2]);
|
|
} else {
|
|
// Run all test cases
|
|
Test t = new Test();
|
|
Random r = new Random();
|
|
|
|
for (String sigAlg : ALL_ALGS) {
|
|
for (String curve : ALL_CURVES) {
|
|
t.run(r.nextInt(1000000), sigAlg, curve);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void debug() throws Exception {
|
|
|
|
LaunchingConnector launchingConnector = Bootstrap
|
|
.virtualMachineManager().defaultConnector();
|
|
|
|
Map<String, Connector.Argument> arguments
|
|
= launchingConnector.defaultArguments();
|
|
arguments.get("main").setValue(ECDSAJavaVerify.class.getName());
|
|
arguments.get("options").setValue(
|
|
"-cp " + System.getProperty("test.classes"));
|
|
VirtualMachine vm = launchingConnector.launch(arguments);
|
|
|
|
MethodEntryRequest req = vm.eventRequestManager()
|
|
.createMethodEntryRequest();
|
|
req.addClassFilter("sun.security.ec.ECDSASignature");
|
|
req.enable();
|
|
|
|
int numberOfTests = ALL_ALGS.length * ALL_CURVES.length * 2;
|
|
|
|
// Expected methods to call. 'J' for java impl, 'N' for native impl
|
|
char[] expected = new char[numberOfTests];
|
|
|
|
int pos = 0;
|
|
for (String dummy : ALL_ALGS) {
|
|
for (String curve : ALL_CURVES) {
|
|
char caller = 'J';
|
|
// For each case, Signature::verify is called twice
|
|
expected[pos++] = caller;
|
|
expected[pos++] = caller;
|
|
}
|
|
}
|
|
|
|
// Test result
|
|
// '.': not run yet
|
|
// '-': enter engineVerify
|
|
// 'v': expected impl called
|
|
// 'x': unexpected impl called
|
|
// Note: some error cases fail before any impl called. Ex: if there
|
|
// is a DER encoding error.
|
|
char[] result = new char[numberOfTests];
|
|
Arrays.fill(result, '.');
|
|
|
|
String stdout, stderr;
|
|
|
|
try {
|
|
EventSet eventSet;
|
|
pos = -1; // will become 0 when entering 'engineVerify'
|
|
while ((eventSet = vm.eventQueue().remove()) != null) {
|
|
for (Event event : eventSet) {
|
|
if (event instanceof MethodEntryEvent) {
|
|
MethodEntryEvent e = (MethodEntryEvent)event;
|
|
switch (e.method().name()) {
|
|
case "engineVerify":
|
|
result[++pos] = '-';
|
|
break;
|
|
case "verifySignedDigestImpl": // the java impl
|
|
result[pos] = expected[pos] != 'J' ? 'x' : 'v';
|
|
break;
|
|
}
|
|
}
|
|
vm.resume();
|
|
}
|
|
}
|
|
} catch (VMDisconnectedException e) {
|
|
System.out.println("Virtual Machine is disconnected.");
|
|
} finally {
|
|
stderr = new String(vm.process().getErrorStream().readAllBytes());
|
|
stdout = new String(vm.process().getInputStream().readAllBytes());
|
|
}
|
|
|
|
int exitCode = vm.process().waitFor();
|
|
System.out.println(" exit: " + exitCode);
|
|
System.out.println("stderr:\n" + stderr);
|
|
System.out.println("stdout:\n" + stdout);
|
|
|
|
String sResult = new String(result);
|
|
|
|
System.out.println(" Cases: " + new String(expected));
|
|
System.out.println("Result: " + sResult);
|
|
|
|
if (pos != numberOfTests - 1 || sResult.contains("x")
|
|
|| sResult.contains(".")) {
|
|
throw new Exception("Unexpected result");
|
|
}
|
|
|
|
if (stdout.contains("fail") || exitCode != 0) {
|
|
throw new Exception("Test failed");
|
|
}
|
|
}
|
|
|
|
static class Test {
|
|
|
|
public boolean run(int seed, String sigAlg, String curve)
|
|
throws Exception {
|
|
|
|
// A determined SecureRandom based on seed. If there is anything
|
|
// wrong, we can reproduce the problem using the seed.
|
|
Random r = new Random(seed);
|
|
SecureRandom rand = new SecureRandom() {
|
|
@Override
|
|
public void nextBytes(byte[] bytes) {
|
|
r.nextBytes(bytes);
|
|
}
|
|
};
|
|
|
|
AlgorithmParameters ap = AlgorithmParameters.getInstance("EC", "SunEC");
|
|
ap.init(new ECGenParameterSpec(curve));
|
|
ECParameterSpec spec = ap.getParameterSpec(ECParameterSpec.class);
|
|
|
|
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", "SunEC");
|
|
kpg.initialize(spec, rand);
|
|
KeyPair kp = kpg.generateKeyPair();
|
|
ECPrivateKey ecPrivateKey = (ECPrivateKey) kp.getPrivate();
|
|
ECPublicKey ecPublicKey = (ECPublicKey) kp.getPublic();
|
|
|
|
Signature s1 = Signature.getInstance(sigAlg, "SunEC");
|
|
s1.initSign(ecPrivateKey, rand);
|
|
byte[] msg = new byte[1234];
|
|
rand.nextBytes(msg);
|
|
s1.update(msg);
|
|
byte[] sig = s1.sign();
|
|
|
|
Signature s2 = Signature.getInstance(sigAlg, "SunEC");
|
|
s2.initVerify(ecPublicKey);
|
|
s2.update(msg);
|
|
|
|
boolean result1 = s2.verify(sig);
|
|
|
|
s2.initVerify(ecPublicKey);
|
|
// modify the signature in some random manner
|
|
if (rand.nextInt(10) < 8) {
|
|
sig[rand.nextInt(10000) % sig.length]
|
|
= (byte) rand.nextInt(10000);
|
|
} else {
|
|
int newLength = rand.nextInt(100);
|
|
if (newLength == sig.length) {
|
|
newLength += 1 + rand.nextInt(2);
|
|
}
|
|
sig = Arrays.copyOf(sig, newLength);
|
|
}
|
|
|
|
boolean result2;
|
|
try {
|
|
result2 = s2.verify(sig);
|
|
} catch (SignatureException se) {
|
|
result2 = false;
|
|
}
|
|
|
|
boolean finalResult = result1 && !result2;
|
|
System.out.printf("%10d %20s %20s -- %5s %5s -- %s\n",
|
|
seed, sigAlg, curve, result1, result2,
|
|
finalResult ? "succeed" : "fail");
|
|
|
|
return finalResult;
|
|
}
|
|
}
|
|
}
|