1 /*
2  * Copyright (c) 2018, 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 8200219
27  * @summary Negative tests for Key related Test with DiffieHellman, ECDH, XDH.
28  *          It Tests,
29  *          Use modified encoding while generating Public/Private Keys
30  *          Short, long, unsupported keysize
31  *          Invalid Algo names including Null
32  *          Invalid provider names including Null
33  *          Invalid curve names
34  *          Invalid spec usage
35  *  Arguments order <KeyExchangeAlgorithm> <Provider> <KeyGenAlgorithm>
36  *                  <keySize> <Curve*>
37  * @library /test/lib
38  * @run main NegativeTest DiffieHellman SunJCE DiffieHellman 1024
39  * @run main NegativeTest ECDH SunEC EC 256
40  * @run main NegativeTest XDH SunEC XDH 255 X25519
41  * @run main NegativeTest XDH SunEC XDH 448 X448
42  */
43 package test.java.security.KeyAgreement;
44 
45 import java.math.BigInteger;
46 import java.security.InvalidAlgorithmParameterException;
47 import java.security.InvalidParameterException;
48 import java.security.KeyFactory;
49 import java.security.KeyPair;
50 import java.security.KeyPairGenerator;
51 import java.security.NoSuchAlgorithmException;
52 import java.security.NoSuchProviderException;
53 import java.security.Security;
54 import java.security.spec.InvalidKeySpecException;
55 import java.security.spec.NamedParameterSpec;
56 import java.security.spec.KeySpec;
57 import java.security.spec.PKCS8EncodedKeySpec;
58 import java.security.spec.X509EncodedKeySpec;
59 import java.security.spec.XECPrivateKeySpec;
60 import java.security.spec.XECPublicKeySpec;
61 import java.util.Arrays;
62 import java.util.List;
63 
64 import javax.crypto.KeyAgreement;
65 import org.testng.annotations.Test;
66 import static org.junit.Assert.fail;
67 
68 public class NegativeTest {
69 
70     @Test
testDHNegative()71     public void testDHNegative() throws Exception {
72         String kaAlgo = "DiffieHellman";
73         String provider = "BC";
74         String kpgAlgo = "DiffieHellman";
75         int keySize = 1024;
76         String kpgInit = "DiffieHellman";
77         testModifiedKeyEncodingTest(provider, kpgAlgo, kpgInit);
78         testInvalidKeyLen(provider, kaAlgo, kpgAlgo, kpgInit);
79         testInvalidKpgAlgo(provider, kaAlgo, keySize);
80         testInvalidKaAlgo(provider, kpgAlgo, keySize);
81         testInvalidProvider(kaAlgo, kpgAlgo, keySize);
82     }
83 
84     @Test
testECDHNegative()85     public void testECDHNegative() throws Exception {
86         String kaAlgo = "ECDH";
87         String provider = "AndroidOpenSSL";
88         String kpgAlgo = "EC";
89         int keySize = 256;
90         String kpgInit = "EC";
91         testModifiedKeyEncodingTest(provider, kpgAlgo, kpgInit);
92         testInvalidKeyLen(provider, kaAlgo, kpgAlgo, kpgInit);
93         testInvalidKpgAlgo(provider, kaAlgo, keySize);
94         testInvalidKaAlgo(provider, kpgAlgo, keySize);
95         testInvalidProvider(kaAlgo, kpgAlgo, keySize);
96         testNamedParameter(provider, kpgAlgo);
97     }
98 
99     // BEGIN Android-removed: XDH is not yet supported
100     /*
101     @Test
102     public void testXDHNegative() throws Exception {
103         String kaAlgo = "XDH";
104         String provider = "AndroidOpenSSL";
105         String kpgAlgo = "XDH";
106         List<Integer> keySizes = Arrays.asList(256, 448);
107         List<String> kpgInits = Arrays.asList("X25519", "X448");
108         for (int i = 0; i < keySizes.size(); i++){
109             testModifiedKeyEncodingTest(provider, kpgAlgo, kpgInits.get(i));
110             testInvalidKeyLen(provider, kaAlgo, kpgAlgo, kpgInits.get(i));
111             testInvalidKpgAlgo(provider, kaAlgo, keySizes.get(i));
112             testInvalidKaAlgo(provider, kpgAlgo, keySizes.get(i));
113             testInvalidProvider(kaAlgo, kpgAlgo, keySizes.get(i));
114             testInvalidSpec(provider, kpgAlgo, kpgInits.get(i));
115             testInCompatibleSpec(provider, kpgAlgo, kpgInits.get(i));
116         }
117         testNamedParameter(provider, kpgAlgo);
118     }
119      */
120     // END Android-removed: XDH is not yet supported
121 
122     /**
123      * Generate keyPair based on KeyPairGenerator algorithm.
124      */
genKeyPair(String provider, String kpgAlgo, String kpgInit)125     private static KeyPair genKeyPair(String provider, String kpgAlgo,
126             String kpgInit) throws Exception {
127 
128         KeyPairGenerator kpg = KeyPairGenerator.getInstance(kpgAlgo,
129                 Security.getProvider(provider));
130         switch (kpgInit) {
131             case "DiffieHellman":
132                 kpg.initialize(512);
133                 break;
134             case "EC":
135                 kpg.initialize(256);
136                 break;
137             case "X25519":
138                 kpg.initialize(255);
139                 break;
140             case "X448":
141                 kpg.initialize(448);
142                 break;
143             default:
144                 fail("Invalid Algo name " + kpgInit);
145         }
146         return kpg.generateKeyPair();
147     }
148 
testModifiedKeyEncodingTest(String provider, String kpgAlgo, String kpgInit)149     private static void testModifiedKeyEncodingTest(String provider,
150             String kpgAlgo, String kpgInit) throws Exception {
151 
152         KeyFactory kf = KeyFactory.getInstance(kpgAlgo, provider);
153         KeyPair kp = genKeyPair(provider, kpgAlgo, kpgInit);
154         // Test modified PrivateKey encoding
155         byte[] encoded = kp.getPrivate().getEncoded();
156         byte[] modified = modifyEncoded(encoded);
157         PKCS8EncodedKeySpec priSpec = new PKCS8EncodedKeySpec(modified);
158         try {
159             // Generate PrivateKey with modified encoding
160             kf.generatePrivate(priSpec);
161             fail("testModifiedKeyTest should fail but passed.");
162         } catch (InvalidKeySpecException e) {
163         }
164         // Test modified PublicKey encoding
165         encoded = kp.getPublic().getEncoded();
166         modified = modifyEncoded(encoded);
167         X509EncodedKeySpec pubSpec = new X509EncodedKeySpec(modified);
168         try {
169             // Generate PublicKey with modified encoding
170             kf.generatePublic(pubSpec);
171             fail("testModifiedKeyTest should fail but passed.");
172         } catch (InvalidKeySpecException e) {
173         }
174     }
175 
176     /**
177      * Test with all Invalid key length.
178      */
testInvalidKeyLen(String provider, String kaAlgo, String kpgAlgo, String kpgInit)179     private static void testInvalidKeyLen(String provider, String kaAlgo,
180             String kpgAlgo, String kpgInit) throws Exception {
181 
182         for (int keySize : selectInvalidKeylength(kpgInit)) {
183             try {
184                 startKeyAgreement(provider, kaAlgo, kpgAlgo, keySize);
185                 fail("testInvalidKeyLen should fail but passed: " + kaAlgo + ", " + Integer.toString(keySize));
186             } catch (InvalidParameterException e) {
187             } catch (IllegalArgumentException e) {
188             }
189         }
190     }
191 
192     /**
193      * Test with all Invalid KeyPairGenerator algorithms.
194      */
testInvalidKpgAlgo(String provider, String algo, int keySize)195     private static void testInvalidKpgAlgo(String provider, String algo,
196             int keySize) throws Exception {
197 
198         for (String kpgAlgo : new String[]{null, " ", "", "NoSuchAlgorithm"}) {
199             try {
200                 startKeyAgreement(provider, algo, kpgAlgo, keySize);
201                 fail("testInvalidKpgAlgo should fail but passed.");
202             } catch (NoSuchAlgorithmException e) {
203             } catch (NullPointerException e) {
204                 if (kpgAlgo == null) {
205                     continue;
206                 }
207                 fail("Unknown failure in testInvalidKpgAlgo.");
208             }
209         }
210     }
211 
212     /**
213      * Test with all Invalid KeyAgreement algorithms.
214      */
testInvalidKaAlgo(String provider, String kpgAlgo, int keySize)215     private static void testInvalidKaAlgo(String provider, String kpgAlgo,
216             int keySize) throws Exception {
217 
218         for (String algo : new String[]{null, " ", "", "NoSuchAlgorithm"}) {
219             try {
220                 startKeyAgreement(provider, algo, kpgAlgo, keySize);
221                 fail("testInvalidKaAlgo should fail but passed.");
222             } catch (NoSuchAlgorithmException e) {
223             } catch (NullPointerException e) {
224                 if (algo == null) {
225                     continue;
226                 }
227                 fail("Unknown failure in testInvalidKaAlgo.");
228             }
229         }
230     }
231 
232     /**
233      * Test with all Invalid Provider names.
234      */
testInvalidProvider(String kaAlgo, String kpgAlgo, int keySize)235     private static void testInvalidProvider(String kaAlgo, String kpgAlgo,
236             int keySize) throws Exception {
237 
238         for (String provider : new String[]{null, " ", "", "NoSuchProvider"}) {
239             try {
240                 startKeyAgreement(provider, kaAlgo, kpgAlgo, keySize);
241                 fail("testInvalidProvider should fail but passed.");
242             } catch (NoSuchProviderException e) {
243             } catch (IllegalArgumentException e) {
244             }
245         }
246     }
247 
248     /**
249      * Test for (in)valid curve names as argument to NamedParameterSpec
250      */
testNamedParameter(String provider, String kpgAlgo)251     private static void testNamedParameter(String provider, String kpgAlgo)
252             throws Exception {
253 
254         for (String name : new String[]{null, " ", "", "NoSuchCurve"}) {
255             try {
256                 NamedParameterSpec spec = new NamedParameterSpec(name);
257                 KeyPairGenerator kpg
258                         = KeyPairGenerator.getInstance(kpgAlgo, provider);
259                 kpg.initialize(spec);
260                 kpg.generateKeyPair();
261                 fail("testNamedParameter should fail but passed.");
262             } catch (NullPointerException e) {
263                 if (name == null) {
264                     continue;
265                 }
266                 fail("Unknown failure in testNamedParameter.");
267             } catch (InvalidAlgorithmParameterException e) {
268             }
269         }
270     }
271 
272     /**
273      * Test to generate Public/Private keys using (in)valid coordinate/scalar.
274      */
testInvalidSpec(String provider, String kpgAlgo, String curve)275     private static void testInvalidSpec(String provider,
276             String kpgAlgo, String curve) throws Exception {
277 
278         NamedParameterSpec spec = new NamedParameterSpec(curve);
279         KeyFactory kf = KeyFactory.getInstance(kpgAlgo, provider);
280         int validLen = curve.equalsIgnoreCase("X448") ? 56 : 32;
281         for (byte[] scalarBytes : new byte[][]{null, new byte[]{},
282                 new byte[32], new byte[56], new byte[65535]}) {
283             try {
284                 KeySpec privateSpec = new XECPrivateKeySpec(spec, scalarBytes);
285                 kf.generatePrivate(privateSpec);
286                 if (scalarBytes.length != validLen) {
287                     fail(String.format("testInvalidSpec "
288                             + "should fail but passed when Scalar bytes length "
289                             + "!= %s for curve %s", validLen, curve));
290                 }
291             } catch (NullPointerException e) {
292                 if (scalarBytes == null) {
293                     continue;
294                 }
295                 fail(e.getMessage());
296             } catch (InvalidKeySpecException e) {
297                 if (scalarBytes.length != validLen) {
298                     continue;
299                 }
300                 fail(e.getMessage());
301             }
302         }
303         for (BigInteger coordinate : new BigInteger[]{null, BigInteger.ZERO,
304                 BigInteger.ONE, new BigInteger("2").pow(255),
305                 new BigInteger("2").pow(448)}) {
306             try {
307                 KeySpec publicSpec = new XECPublicKeySpec(spec, coordinate);
308                 kf.generatePublic(publicSpec);
309             } catch (NullPointerException e) {
310                 if (coordinate == null) {
311                     continue;
312                 }
313                 fail(e.getMessage());
314             }
315         }
316     }
317 
testInCompatibleSpec(String provider, String kpgAlgo, String curve)318     private static void testInCompatibleSpec(String provider,
319             String kpgAlgo, String curve) throws Exception {
320 
321         int validLen = curve.equalsIgnoreCase("X448") ? 56 : 32;
322         NamedParameterSpec spec = new NamedParameterSpec(curve);
323         KeyFactory kf = KeyFactory.getInstance(kpgAlgo, provider);
324         KeySpec privateSpec = new XECPrivateKeySpec(spec, new byte[validLen]);
325         KeySpec publicSpec = new XECPublicKeySpec(spec, BigInteger.ONE);
326         try {
327             kf.generatePrivate(publicSpec);
328             fail("testInCompatibleSpec should fail but passed.");
329         } catch (InvalidKeySpecException e) {
330         }
331         try {
332             kf.generatePublic(privateSpec);
333             fail("testInCompatibleSpec should fail but passed.");
334         } catch (InvalidKeySpecException e) {
335         }
336     }
337 
338     /**
339      * Perform KeyAgreement operation.
340      */
startKeyAgreement(String provider, String kaAlgo, String kpgAlgo, int keySize)341     private static void startKeyAgreement(String provider, String kaAlgo,
342             String kpgAlgo, int keySize) throws Exception {
343 
344         KeyPairGenerator kpg = KeyPairGenerator.getInstance(kpgAlgo, provider);
345         kpg.initialize(keySize);
346         KeyPair kp = kpg.generateKeyPair();
347         KeyAgreement ka = KeyAgreement.getInstance(kaAlgo, provider);
348         ka.init(kp.getPrivate());
349         ka.doPhase(kp.getPublic(), true);
350         ka.generateSecret();
351     }
352 
353     /**
354      * Return manipulated encoded bytes.
355      */
modifyEncoded(byte[] encoded)356     private static byte[] modifyEncoded(byte[] encoded) {
357 
358         byte[] copy = Arrays.copyOf(encoded, encoded.length);
359         for (int i = 0; i < copy.length; i++) {
360             copy[i] = (byte) ~copy[i];
361         }
362         return copy;
363     }
364 
365     /**
366      * Select invalid key sizes for different Key generation algorithms.
367      */
selectInvalidKeylength(String kpgInit)368     private static int[] selectInvalidKeylength(String kpgInit) {
369 
370         int[] keySize = new int[]{};
371         switch (kpgInit) {
372             case "DiffieHellman":
373                 keySize = new int[]{100};
374                 break;
375             case "EC":
376             case "X25519":
377                 keySize = new int[]{100, 300};
378                 break;
379             case "X448":
380                 keySize = new int[]{100, 500};
381                 break;
382             default:
383                 fail("Invalid Algo name " + kpgInit);
384         }
385         return keySize;
386     }
387 }