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