Skip to content

Commit 8818eb4

Browse files
committed
8325506: Ensure randomness is only read from provided SecureRandom object
Reviewed-by: lucy Backport-of: b87d9cf2c9d905c15f4c957d42361b1a72974edf
1 parent d221709 commit 8818eb4

File tree

5 files changed

+554
-4
lines changed

5 files changed

+554
-4
lines changed
Lines changed: 297 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,297 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
/*
25+
* @test
26+
* @bug 8325506
27+
* @library /test/lib
28+
* @modules java.base/sun.security.util
29+
* @run main/othervm Deterministic
30+
* @summary confirm the output of random calculations are determined
31+
* by the SecureRandom parameters
32+
*/
33+
34+
import jdk.test.lib.Asserts;
35+
import jdk.test.lib.security.SeededSecureRandom;
36+
import sun.security.util.SignatureUtil;
37+
38+
import javax.crypto.Cipher;
39+
import javax.crypto.KEM;
40+
import javax.crypto.KeyAgreement;
41+
import javax.crypto.KeyGenerator;
42+
import javax.crypto.spec.ChaCha20ParameterSpec;
43+
import javax.crypto.spec.DHParameterSpec;
44+
import javax.crypto.spec.GCMParameterSpec;
45+
import javax.crypto.spec.IvParameterSpec;
46+
import javax.crypto.spec.PBEParameterSpec;
47+
import javax.crypto.spec.SecretKeySpec;
48+
import java.nio.charset.StandardCharsets;
49+
import java.security.*;
50+
import java.security.spec.AlgorithmParameterSpec;
51+
import java.security.spec.DSAParameterSpec;
52+
import java.security.spec.PSSParameterSpec;
53+
import java.util.Arrays;
54+
import java.util.Objects;
55+
56+
public class Deterministic {
57+
58+
private static final long SEED = SeededSecureRandom.seed();
59+
private static int hash = 0;
60+
61+
public static void main(String[] args) throws Exception {
62+
63+
for (var p : Security.getProviders()) {
64+
var name = p.getName();
65+
if (name.equals("SunMSCAPI") || name.startsWith("SunPKCS11")) {
66+
System.out.println("Skipped native provider " + name);
67+
continue;
68+
}
69+
for (var s : p.getServices()) {
70+
switch (s.getType()) {
71+
case "KeyPairGenerator" -> testKeyPairGenerator(s);
72+
case "KeyGenerator" -> testKeyGenerator(s);
73+
case "Signature" -> testSignature(s);
74+
case "KEM" -> testKEM(s);
75+
case "KeyAgreement" -> testKeyAgreement(s);
76+
case "Cipher" -> testCipher(s);
77+
case "AlgorithmParameterGenerator" -> testAlgorithmParameterGenerator(s);
78+
}
79+
}
80+
}
81+
// Run twice and this value should be the same for the same SEED
82+
System.out.println("Final hash: " + hash);
83+
}
84+
85+
static void testCipher(Provider.Service s) throws Exception {
86+
var alg = s.getAlgorithm();
87+
System.out.println(s.getProvider().getName()
88+
+ " " + s.getType() + "." + alg);
89+
if (alg.contains("Wrap") || alg.contains("KW")) {
90+
System.out.println(" Ignored");
91+
return;
92+
}
93+
Key key;
94+
AlgorithmParameterSpec spec;
95+
if (alg.startsWith("PBE")) {
96+
key = new SecretKeySpec("isthisakey".getBytes(StandardCharsets.UTF_8), "PBE");
97+
// Some cipher requires salt to be 8 byte long
98+
spec = new PBEParameterSpec("saltsalt".getBytes(StandardCharsets.UTF_8), 100);
99+
} else {
100+
key = generateKey(alg.split("/")[0], s.getProvider());
101+
if (!alg.contains("/") || alg.contains("/ECB/")) {
102+
spec = null;
103+
} else {
104+
if (alg.contains("/GCM/")) {
105+
spec = new GCMParameterSpec(128, new SeededSecureRandom(SEED + 1).generateSeed(16));
106+
} else if (alg.equals("ChaCha20")) {
107+
spec = new ChaCha20ParameterSpec(new SeededSecureRandom(SEED + 2).generateSeed(12), 128);
108+
} else if (alg.contains("ChaCha20")) {
109+
spec = new IvParameterSpec(new SeededSecureRandom(SEED + 3).generateSeed(12));
110+
} else {
111+
spec = new IvParameterSpec(new SeededSecureRandom(SEED + 4).generateSeed(16));
112+
}
113+
}
114+
}
115+
var c = Cipher.getInstance(alg, s.getProvider());
116+
c.init(Cipher.ENCRYPT_MODE, key, spec, new SeededSecureRandom(SEED));
117+
// Some cipher requires plaintext to be 16 byte long
118+
var ct1 = c.doFinal("asimpleplaintext".getBytes(StandardCharsets.UTF_8));
119+
// Some cipher requires IV to be different, so re-instantiate a cipher
120+
c = Cipher.getInstance(alg, s.getProvider());
121+
c.init(Cipher.ENCRYPT_MODE, key, spec, new SeededSecureRandom(SEED));
122+
var ct2 = c.doFinal("asimpleplaintext".getBytes(StandardCharsets.UTF_8));
123+
Asserts.assertEqualsByteArray(ct1, ct2);
124+
hash = Objects.hash(hash, Arrays.hashCode(ct1));
125+
System.out.println(" Passed");
126+
}
127+
128+
static void testAlgorithmParameterGenerator(Provider.Service s) throws Exception {
129+
System.out.println(s.getProvider().getName()
130+
+ " " + s.getType() + "." + s.getAlgorithm());
131+
var apg = AlgorithmParameterGenerator.getInstance(s.getAlgorithm(), s.getProvider());
132+
apg.init(1024, new SeededSecureRandom(SEED));
133+
var p1 = apg.generateParameters().getParameterSpec(AlgorithmParameterSpec.class);
134+
apg.init(1024, new SeededSecureRandom(SEED));
135+
var p2 = apg.generateParameters().getParameterSpec(AlgorithmParameterSpec.class);
136+
if (p1 instanceof DSAParameterSpec d1 && p2 instanceof DSAParameterSpec d2) {
137+
Asserts.assertEQ(d1.getG(), d2.getG());
138+
Asserts.assertEQ(d1.getP(), d2.getP());
139+
Asserts.assertEQ(d1.getQ(), d2.getQ());
140+
hash = Objects.hash(hash, d1.getG(), d1.getP(), d1.getQ());
141+
} else if (p1 instanceof DHParameterSpec d1 && p2 instanceof DHParameterSpec d2){
142+
Asserts.assertEQ(d1.getG(), d2.getG());
143+
Asserts.assertEQ(d1.getP(), d2.getP());
144+
Asserts.assertEQ(d1.getL(), d2.getL());
145+
hash = Objects.hash(hash, d1.getG(), d1.getP(), d1.getL());
146+
} else {
147+
Asserts.assertEQ(p1, p2);
148+
hash = Objects.hash(hash, p1);
149+
}
150+
System.out.println(" Passed");
151+
}
152+
153+
private static void testSignature(Provider.Service s) throws Exception {
154+
System.out.println(s.getProvider().getName()
155+
+ " " + s.getType() + "." + s.getAlgorithm());
156+
String keyAlg = SignatureUtil.extractKeyAlgFromDwithE(s.getAlgorithm());
157+
// Later versions implement this in sun/security/util/SignatureUtil.java
158+
if (keyAlg != null && keyAlg.endsWith("INP1363FORMAT")) {
159+
keyAlg = keyAlg.substring(0, keyAlg.length() - 13);
160+
if (keyAlg.equalsIgnoreCase("ECDSA")) {
161+
keyAlg = "EC";
162+
}
163+
}
164+
if (keyAlg == null) {
165+
if (s.getAlgorithm().equals("HSS/LMS")) {
166+
// We don't support HSS/LMS key generation and signing
167+
System.out.println(" Ignored: HSS/LMS");
168+
return;
169+
} else {
170+
keyAlg = s.getAlgorithm(); // EdDSA etc
171+
}
172+
}
173+
var sk = generateKeyPair(keyAlg, 0).getPrivate();
174+
var sig = Signature.getInstance(s.getAlgorithm(), s.getProvider());
175+
try {
176+
if (keyAlg.equals("RSASSA-PSS")) {
177+
sig.setParameter(PSSParameterSpec.DEFAULT);
178+
}
179+
sig.initSign(sk, new SeededSecureRandom(SEED));
180+
sig.update(new byte[20]);
181+
var s1 = sig.sign();
182+
sig.initSign(sk, new SeededSecureRandom(SEED));
183+
sig.update(new byte[20]);
184+
var s2 = sig.sign();
185+
Asserts.assertEqualsByteArray(s1, s2);
186+
hash = Objects.hash(hash, Arrays.hashCode(s1));
187+
System.out.println(" Passed");
188+
} catch (InvalidKeyException ike) {
189+
System.out.println(" Ignored: " + ike.getMessage());
190+
}
191+
}
192+
193+
static void testKeyPairGenerator(Provider.Service s) throws Exception {
194+
System.out.println(s.getProvider().getName()
195+
+ " " + s.getType() + "." + s.getAlgorithm());
196+
var kp1 = generateKeyPair(s.getAlgorithm(), 0);
197+
var kp2 = generateKeyPair(s.getAlgorithm(), 0);
198+
Asserts.assertEqualsByteArray(
199+
kp1.getPrivate().getEncoded(), kp2.getPrivate().getEncoded());
200+
Asserts.assertEqualsByteArray(
201+
kp1.getPublic().getEncoded(), kp2.getPublic().getEncoded());
202+
hash = Objects.hash(hash,
203+
Arrays.hashCode(kp1.getPrivate().getEncoded()),
204+
Arrays.hashCode(kp1.getPublic().getEncoded()));
205+
System.out.println(" Passed");
206+
}
207+
208+
static KeyPair generateKeyPair(String alg, int offset) throws Exception {
209+
var g = KeyPairGenerator.getInstance(alg);
210+
var size = switch (g.getAlgorithm()) {
211+
case "RSA", "RSASSA-PSS", "DSA", "DiffieHellman" -> 1024;
212+
case "EC" -> 256;
213+
case "EdDSA", "Ed25519", "XDH", "X25519" -> 255;
214+
case "Ed448", "X448" -> 448;
215+
default -> throw new UnsupportedOperationException(alg);
216+
};
217+
g.initialize(size, new SeededSecureRandom(SEED + offset));
218+
return g.generateKeyPair();
219+
}
220+
221+
static void testKeyGenerator(Provider.Service s) throws Exception {
222+
System.out.println(s.getProvider().getName()
223+
+ " " + s.getType() + "." + s.getAlgorithm());
224+
if (s.getAlgorithm().startsWith("SunTls")) {
225+
System.out.println(" Ignored");
226+
return;
227+
}
228+
var k1 = generateKey(s.getAlgorithm(), s.getProvider());
229+
var k2 = generateKey(s.getAlgorithm(), s.getProvider());
230+
Asserts.assertEqualsByteArray(k1.getEncoded(), k2.getEncoded());
231+
hash = Objects.hash(hash,
232+
Arrays.hashCode(k1.getEncoded()));
233+
System.out.println(" Passed");
234+
}
235+
236+
static Key generateKey(String s, Provider p) throws Exception {
237+
if (s.startsWith("AES_")) {
238+
var g = KeyGenerator.getInstance("AES", p);
239+
g.init(Integer.parseInt(s.substring(4)), new SeededSecureRandom(SEED + 1));
240+
return g.generateKey();
241+
} if (s.startsWith("ChaCha")) {
242+
var g = KeyGenerator.getInstance("ChaCha20", p);
243+
g.init(new SeededSecureRandom(SEED + 2));
244+
return g.generateKey();
245+
} if (s.equals("RSA")) {
246+
return generateKeyPair("RSA", 3).getPublic();
247+
} else {
248+
var g = KeyGenerator.getInstance(s, p);
249+
g.init(new SeededSecureRandom(SEED + 4));
250+
return g.generateKey();
251+
}
252+
}
253+
254+
static void testKEM(Provider.Service s) throws Exception {
255+
System.out.println(s.getProvider().getName()
256+
+ " " + s.getType() + "." + s.getAlgorithm());
257+
String keyAlg = getKeyAlgFromKEM(s.getAlgorithm());
258+
var kp = generateKeyPair(keyAlg, 10);
259+
var kem = KEM.getInstance(s.getAlgorithm(), s.getProvider());
260+
var e1 = kem.newEncapsulator(kp.getPublic(), null, new SeededSecureRandom(SEED));
261+
var enc1 = e1.encapsulate();
262+
var e2 = kem.newEncapsulator(kp.getPublic(), null, new SeededSecureRandom(SEED));
263+
var enc2 = e2.encapsulate();
264+
Asserts.assertEqualsByteArray(enc1.encapsulation(), enc2.encapsulation());
265+
Asserts.assertEqualsByteArray(enc1.key().getEncoded(), enc2.key().getEncoded());
266+
hash = Objects.hash(hash, Arrays.hashCode(enc1.encapsulation()),
267+
Arrays.hashCode(enc1.key().getEncoded()));
268+
System.out.println(" Passed");
269+
}
270+
271+
static void testKeyAgreement(Provider.Service s) throws Exception {
272+
System.out.println(s.getProvider().getName()
273+
+ " " + s.getType() + "." + s.getAlgorithm());
274+
String keyAlg = getKeyAlgFromKEM(s.getAlgorithm());
275+
var kpS = generateKeyPair(keyAlg, 11);
276+
var kpR = generateKeyPair(keyAlg, 12);
277+
var ka = KeyAgreement.getInstance(s.getAlgorithm(), s.getProvider());
278+
ka.init(kpS.getPrivate(), new SeededSecureRandom(SEED));
279+
ka.doPhase(kpR.getPublic(), true);
280+
var sc1 = ka.generateSecret();
281+
ka.init(kpS.getPrivate(), new SeededSecureRandom(SEED));
282+
ka.doPhase(kpR.getPublic(), true);
283+
var sc2 = ka.generateSecret();
284+
285+
Asserts.assertEqualsByteArray(sc1, sc2);
286+
hash = Objects.hash(hash, Arrays.hashCode(sc1));
287+
System.out.println(" Passed");
288+
}
289+
290+
static String getKeyAlgFromKEM(String algorithm) {
291+
return switch (algorithm) {
292+
case "DHKEM" -> "X25519";
293+
case "ECDH" -> "EC";
294+
default -> algorithm;
295+
};
296+
}
297+
}

0 commit comments

Comments
 (0)