1 /* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package org.conscrypt.javax.crypto; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertFalse; 21 import static org.junit.Assert.assertNull; 22 import static org.junit.Assert.assertSame; 23 import static org.junit.Assert.assertTrue; 24 import static org.junit.Assert.fail; 25 26 import java.lang.reflect.InvocationTargetException; 27 import java.lang.reflect.Method; 28 import java.security.GeneralSecurityException; 29 import java.security.InvalidAlgorithmParameterException; 30 import java.security.InvalidKeyException; 31 import java.security.KeyFactory; 32 import java.security.NoSuchAlgorithmException; 33 import java.security.PrivateKey; 34 import java.security.Provider; 35 import java.security.PublicKey; 36 import java.security.Security; 37 import java.security.interfaces.ECPrivateKey; 38 import java.security.interfaces.ECPublicKey; 39 import java.security.spec.ECGenParameterSpec; 40 import java.security.spec.PKCS8EncodedKeySpec; 41 import java.security.spec.X509EncodedKeySpec; 42 import java.util.ArrayList; 43 import java.util.Arrays; 44 import java.util.Comparator; 45 import java.util.List; 46 import javax.crypto.KeyAgreement; 47 import javax.crypto.SecretKey; 48 import javax.crypto.ShortBufferException; 49 import junit.framework.AssertionFailedError; 50 import org.conscrypt.Conscrypt; 51 import org.conscrypt.TestUtils; 52 import org.junit.AfterClass; 53 import org.junit.BeforeClass; 54 import org.junit.Test; 55 import org.junit.runner.RunWith; 56 import org.junit.runners.JUnit4; 57 import dalvik.system.VMRuntime; 58 import sun.security.jca.Providers; 59 60 /** 61 * Tests for all registered Elliptic Curve Diffie-Hellman {@link KeyAgreement} providers. 62 */ 63 @RunWith(JUnit4.class) 64 public class ECDHKeyAgreementTest { 65 66 // BEGIN Android-Added: Allow access to deprecated BC algorithms. 67 // Allow access to deprecated BC algorithms in this test, so we can ensure they 68 // continue to work 69 @BeforeClass 70 public static void enableDeprecatedAlgorithms() { 71 Providers.setMaximumAllowableApiLevelForBcDeprecation( 72 VMRuntime.getRuntime().getTargetSdkVersion()); 73 } 74 75 @AfterClass 76 public static void restoreDeprecatedAlgorithms() { 77 Providers.setMaximumAllowableApiLevelForBcDeprecation( 78 Providers.DEFAULT_MAXIMUM_ALLOWABLE_TARGET_API_LEVEL_FOR_BC_DEPRECATION); 79 } 80 // END Android-Added: Allow access to deprecated BC algorithms. 81 82 // Two key pairs and the resulting shared secret for the Known Answer Test 83 private static final byte[] KAT_PUBLIC_KEY1_X509 = TestUtils.decodeHex( 84 "3059301306072a8648ce3d020106082a8648ce3d030107034200049fc2f71f85446b1371244491d83" 85 + "9cf97b5d27cedbb04d2c0058b59709df3a216e6b4ca1b2d622588c5a0e6968144a8965e816a600c" 86 + "05305a1da3df2bf02b41d1"); 87 private static final byte[] KAT_PRIVATE_KEY1_PKCS8 = TestUtils.decodeHex( 88 "308193020100301306072a8648ce3d020106082a8648ce3d030107047930770201010420e1e683003" 89 + "c8b963a92742e5f955ce7fddc81d0c3ae9b149d6af86a0cacb2271ca00a06082a8648ce3d030107" 90 + "a144034200049fc2f71f85446b1371244491d839cf97b5d27cedbb04d2c0058b59709df3a216e6b" 91 + "4ca1b2d622588c5a0e6968144a8965e816a600c05305a1da3df2bf02b41d1"); 92 93 private static final byte[] KAT_PUBLIC_KEY2_X509 = TestUtils.decodeHex( 94 "3059301306072a8648ce3d020106082a8648ce3d03010703420004358efb6d91e5bbcae21774af3f6" 95 + "d85d0848630e7e61dbeb5ac9e47036ed0f8d38c7a1d1bb249f92861c7c9153fff33f45ab5b171eb" 96 + "e8cad741125e6bb4fc6b07"); 97 private static final byte[] KAT_PRIVATE_KEY2_PKCS8 = TestUtils.decodeHex( 98 "308193020100301306072a8648ce3d020106082a8648ce3d0301070479307702010104202b1810a69" 99 + "e12b74d50bf0343168f705f0104f76299855268aa526fdb31e6eec0a00a06082a8648ce3d030107" 100 + "a14403420004358efb6d91e5bbcae21774af3f6d85d0848630e7e61dbeb5ac9e47036ed0f8d38c7" 101 + "a1d1bb249f92861c7c9153fff33f45ab5b171ebe8cad741125e6bb4fc6b07"); 102 103 private static final byte[] KAT_SECRET = 104 TestUtils.decodeHex("4faa0594c0e773eb26c8df2163af2443e88aab9578b9e1f324bc61e42d222783"); 105 106 private static final ECPublicKey KAT_PUBLIC_KEY1; 107 private static final ECPrivateKey KAT_PRIVATE_KEY1; 108 private static final ECPublicKey KAT_PUBLIC_KEY2; 109 private static final ECPrivateKey KAT_PRIVATE_KEY2; 110 static { 111 try { 112 KAT_PUBLIC_KEY1 = getPublicKey(KAT_PUBLIC_KEY1_X509); 113 KAT_PRIVATE_KEY1 = getPrivateKey(KAT_PRIVATE_KEY1_PKCS8); 114 KAT_PUBLIC_KEY2 = getPublicKey(KAT_PUBLIC_KEY2_X509); 115 KAT_PRIVATE_KEY2 = getPrivateKey(KAT_PRIVATE_KEY2_PKCS8); 116 } catch (Exception e) { 117 throw new RuntimeException("Failed to decode KAT key pairs using default provider", e); 118 } 119 } 120 121 @BeforeClass 122 public static void setUp() { 123 TestUtils.assumeAllowsUnsignedCrypto(); 124 } 125 126 /** 127 * Performs a known-answer test of the shared secret for all permutations of {@code Providers} 128 * of: first key pair, second key pair, and the {@code KeyAgreement}. This is to check that 129 * the {@code KeyAgreement} instances work with keys of all registered providers. 130 */ 131 @Test 132 public void testKnownAnswer() throws Exception { 133 for (Provider keyFactoryProvider1 : getKeyFactoryProviders()) { 134 ECPrivateKey privateKey1 = getPrivateKey(KAT_PRIVATE_KEY1_PKCS8, keyFactoryProvider1); 135 ECPublicKey publicKey1 = getPublicKey(KAT_PUBLIC_KEY1_X509, keyFactoryProvider1); 136 for (Provider keyFactoryProvider2 : getKeyFactoryProviders()) { 137 ECPrivateKey privateKey2 = 138 getPrivateKey(KAT_PRIVATE_KEY2_PKCS8, keyFactoryProvider2); 139 ECPublicKey publicKey2 = 140 getPublicKey(KAT_PUBLIC_KEY2_X509, keyFactoryProvider2); 141 for (Provider keyAgreementProvider : getKeyAgreementProviders()) { 142 try { 143 testKnownAnswer(publicKey1, privateKey1, publicKey2, privateKey2, 144 keyAgreementProvider); 145 } catch (Throwable e) { 146 throw new RuntimeException(getClass().getSimpleName() + ".testKnownAnswer(" 147 + keyFactoryProvider1.getName() 148 + ", " + keyFactoryProvider2.getName() 149 + ", " + keyAgreementProvider.getName() + ")", 150 e); 151 } 152 } 153 } 154 } 155 } 156 157 void testKnownAnswer( 158 ECPublicKey publicKey1, ECPrivateKey privateKey1, 159 ECPublicKey publicKey2, ECPrivateKey privateKey2, 160 Provider keyAgreementProvider) throws Exception { 161 assertTrue(Arrays.equals( 162 KAT_SECRET, generateSecret(keyAgreementProvider, privateKey1, publicKey2))); 163 assertTrue(Arrays.equals( 164 KAT_SECRET, generateSecret(keyAgreementProvider, privateKey2, publicKey1))); 165 } 166 167 @Test 168 public void testGetAlgorithm() throws Exception { 169 invokeCallingMethodForEachKeyAgreementProvider(); 170 } 171 172 void testGetAlgorithm(Provider provider) throws Exception { 173 assertEquals("ECDH", getKeyAgreement(provider).getAlgorithm()); 174 } 175 176 @Test 177 public void testGetProvider() throws Exception { 178 invokeCallingMethodForEachKeyAgreementProvider(); 179 } 180 181 void testGetProvider(Provider provider) throws Exception { 182 assertSame(provider, getKeyAgreement(provider).getProvider()); 183 } 184 185 @Test 186 public void testInit_withNullPrivateKey() throws Exception { 187 invokeCallingMethodForEachKeyAgreementProvider(); 188 } 189 190 void testInit_withNullPrivateKey(Provider provider) throws Exception { 191 KeyAgreement keyAgreement = getKeyAgreement(provider); 192 try { 193 keyAgreement.init(null); 194 fail(); 195 } catch (InvalidKeyException expected) {} 196 } 197 198 @Test 199 public void testInit_withUnsupportedPrivateKeyType() throws Exception { 200 invokeCallingMethodForEachKeyAgreementProvider(); 201 } 202 203 void testInit_withUnsupportedPrivateKeyType(Provider provider) throws Exception { 204 KeyAgreement keyAgreement = getKeyAgreement(provider); 205 try { 206 keyAgreement.init(KAT_PUBLIC_KEY1); 207 fail(); 208 } catch (InvalidKeyException expected) {} 209 } 210 211 @Test 212 public void testInit_withUnsupportedAlgorithmParameterSpec() throws Exception { 213 invokeCallingMethodForEachKeyAgreementProvider(); 214 } 215 216 void testInit_withUnsupportedAlgorithmParameterSpec(Provider provider) throws Exception { 217 try { 218 getKeyAgreement(provider).init(KAT_PRIVATE_KEY1, new ECGenParameterSpec("prime256v1")); 219 fail(); 220 } catch (InvalidAlgorithmParameterException expected) {} 221 } 222 223 @Test 224 public void testDoPhase_whenNotInitialized() throws Exception { 225 invokeCallingMethodForEachKeyAgreementProvider(); 226 } 227 228 void testDoPhase_whenNotInitialized(Provider provider) throws Exception { 229 try { 230 getKeyAgreement(provider).doPhase(KAT_PUBLIC_KEY1, true); 231 fail(); 232 } catch (IllegalStateException expected) {} 233 } 234 235 @Test 236 public void testDoPhaseReturnsNull() throws Exception { 237 invokeCallingMethodForEachKeyAgreementProvider(); 238 } 239 240 void testDoPhaseReturnsNull(Provider provider) throws Exception { 241 KeyAgreement keyAgreement = getKeyAgreement(provider); 242 keyAgreement.init(KAT_PRIVATE_KEY1); 243 assertNull(keyAgreement.doPhase(KAT_PUBLIC_KEY2, true)); 244 } 245 246 @Test 247 public void testDoPhase_withPhaseWhichIsNotLast() throws Exception { 248 invokeCallingMethodForEachKeyAgreementProvider(); 249 } 250 251 void testDoPhase_withPhaseWhichIsNotLast(Provider provider) throws Exception { 252 KeyAgreement keyAgreement = getKeyAgreement(provider); 253 keyAgreement.init(KAT_PRIVATE_KEY1); 254 try { 255 keyAgreement.doPhase(KAT_PUBLIC_KEY2, false); 256 fail(); 257 } catch (IllegalStateException expected) {} 258 } 259 260 @Test 261 public void testDoPhase_withNullKey() throws Exception { 262 invokeCallingMethodForEachKeyAgreementProvider(); 263 } 264 265 void testDoPhase_withNullKey(Provider provider) throws Exception { 266 KeyAgreement keyAgreement = getKeyAgreement(provider); 267 keyAgreement.init(KAT_PRIVATE_KEY1); 268 try { 269 keyAgreement.doPhase(null, true); 270 fail(); 271 } catch (InvalidKeyException expected) {} 272 } 273 274 @Test 275 public void testDoPhase_withInvalidKeyType() throws Exception { 276 invokeCallingMethodForEachKeyAgreementProvider(); 277 } 278 279 void testDoPhase_withInvalidKeyType(Provider provider) throws Exception { 280 KeyAgreement keyAgreement = getKeyAgreement(provider); 281 keyAgreement.init(KAT_PRIVATE_KEY1); 282 try { 283 keyAgreement.doPhase(KAT_PRIVATE_KEY1, true); 284 fail(); 285 } catch (InvalidKeyException expected) {} 286 } 287 288 @Test 289 public void testGenerateSecret_withNullOutputBuffer() throws Exception { 290 invokeCallingMethodForEachKeyAgreementProvider(); 291 } 292 293 void testGenerateSecret_withNullOutputBuffer(Provider provider) throws Exception { 294 KeyAgreement keyAgreement = getKeyAgreement(provider); 295 keyAgreement.init(KAT_PRIVATE_KEY1); 296 keyAgreement.doPhase(KAT_PUBLIC_KEY2, true); 297 try { 298 keyAgreement.generateSecret(null, 0); 299 fail(); 300 } catch (NullPointerException expected) {} 301 } 302 303 @Test 304 public void testGenerateSecret_withBufferOfTheRightSize() throws Exception { 305 invokeCallingMethodForEachKeyAgreementProvider(); 306 } 307 308 void testGenerateSecret_withBufferOfTheRightSize(Provider provider) throws Exception { 309 KeyAgreement keyAgreement = getKeyAgreement(provider); 310 keyAgreement.init(KAT_PRIVATE_KEY1); 311 keyAgreement.doPhase(KAT_PUBLIC_KEY2, true); 312 313 byte[] buffer = new byte[KAT_SECRET.length]; 314 int secretLengthBytes = keyAgreement.generateSecret(buffer, 0); 315 assertEquals(KAT_SECRET.length, secretLengthBytes); 316 assertTrue(Arrays.equals(KAT_SECRET, buffer)); 317 } 318 319 @Test 320 public void testGenerateSecret_withLargerThatNeededBuffer() throws Exception { 321 invokeCallingMethodForEachKeyAgreementProvider(); 322 } 323 324 void testGenerateSecret_withLargerThatNeededBuffer(Provider provider) throws Exception { 325 KeyAgreement keyAgreement = getKeyAgreement(provider); 326 keyAgreement.init(KAT_PRIVATE_KEY1); 327 keyAgreement.doPhase(KAT_PUBLIC_KEY2, true); 328 329 // Place the shared secret in the middle of the larger buffer and check that only that 330 // part of the buffer is affected. 331 byte[] buffer = new byte[KAT_SECRET.length + 2]; 332 buffer[0] = (byte) 0x85; // arbitrary canary value 333 buffer[buffer.length - 1] = (byte) 0x3b; // arbitrary canary value 334 int secretLengthBytes = keyAgreement.generateSecret(buffer, 1); 335 assertEquals(KAT_SECRET.length, secretLengthBytes); 336 assertEquals((byte) 0x85, buffer[0]); 337 assertEquals((byte) 0x3b, buffer[buffer.length - 1]); 338 byte[] secret = new byte[KAT_SECRET.length]; 339 System.arraycopy(buffer, 1, secret, 0, secret.length); 340 assertTrue(Arrays.equals(KAT_SECRET, secret)); 341 } 342 343 @Test 344 public void testGenerateSecret_withSmallerThanNeededBuffer() throws Exception { 345 invokeCallingMethodForEachKeyAgreementProvider(); 346 } 347 348 void testGenerateSecret_withSmallerThanNeededBuffer(Provider provider) throws Exception { 349 KeyAgreement keyAgreement = getKeyAgreement(provider); 350 keyAgreement.init(KAT_PRIVATE_KEY1); 351 keyAgreement.doPhase(KAT_PUBLIC_KEY2, true); 352 try { 353 // Although the buffer is big enough (1024 bytes) the shared secret should be placed 354 // at offset 1020 thus leaving only 4 bytes for the secret, which is not enough. 355 keyAgreement.generateSecret(new byte[1024], 1020); 356 fail(); 357 } catch (ShortBufferException expected) {} 358 } 359 360 @Test 361 public void testGenerateSecret_withoutBuffer() throws Exception { 362 invokeCallingMethodForEachKeyAgreementProvider(); 363 } 364 365 void testGenerateSecret_withoutBuffer(Provider provider) throws Exception { 366 KeyAgreement keyAgreement = getKeyAgreement(provider); 367 keyAgreement.init(KAT_PRIVATE_KEY2); 368 keyAgreement.doPhase(KAT_PUBLIC_KEY1, true); 369 370 byte[] secret = keyAgreement.generateSecret(); 371 assertTrue(Arrays.equals(KAT_SECRET, secret)); 372 } 373 374 @Test 375 public void testGenerateSecret_withAlgorithm() throws Exception { 376 invokeCallingMethodForEachKeyAgreementProvider(); 377 } 378 379 void testGenerateSecret_withAlgorithm(Provider provider) throws Exception { 380 KeyAgreement keyAgreement = getKeyAgreement(provider); 381 keyAgreement.init(KAT_PRIVATE_KEY2); 382 keyAgreement.doPhase(KAT_PUBLIC_KEY1, true); 383 384 try { 385 SecretKey key = keyAgreement.generateSecret("AES"); 386 assertEquals("AES", key.getAlgorithm()); 387 // The check below will need to change if it's a hardware-backed key. 388 // We'll have to encrypt a known plaintext and check that the ciphertext is as 389 // expected. 390 assertTrue(Arrays.equals(KAT_SECRET, key.getEncoded())); 391 } catch (NoSuchAlgorithmException e) { 392 // This provider doesn't support AES, that's fine as long as it's not Conscrypt 393 assertFalse(Conscrypt.isConscrypt(provider)); 394 } 395 } 396 397 private void invokeCallingMethodForEachKeyAgreementProvider() throws Exception { 398 StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); 399 String callingMethodName = null; 400 for (int i = 0; i < stackTrace.length; i++) { 401 if ("invokeCallingMethodForEachKeyAgreementProvider".equals( 402 stackTrace[i].getMethodName())) { 403 callingMethodName = stackTrace[i + 1].getMethodName(); 404 } 405 } 406 if (callingMethodName == null) { 407 throw new RuntimeException("Failed to deduce calling method name from stack trace"); 408 } 409 410 String invokedMethodName = callingMethodName; 411 Method method; 412 try { 413 method = getClass().getDeclaredMethod(invokedMethodName, Provider.class); 414 } catch (NoSuchMethodError e) { 415 throw new AssertionFailedError("Failed to find per-Provider test method " 416 + getClass().getSimpleName() + "." + invokedMethodName + "(Provider)"); 417 } 418 419 for (Provider provider : getKeyAgreementProviders()) { 420 try { 421 method.invoke(this, provider); 422 } catch (InvocationTargetException e) { 423 throw new RuntimeException(getClass().getSimpleName() + "." + invokedMethodName 424 + "(provider: " + provider.getName() + ") failed", 425 e.getCause()); 426 } 427 } 428 } 429 430 private static Provider[] getKeyAgreementProviders() { 431 Provider[] providers = Security.getProviders("KeyAgreement.ECDH"); 432 if (providers == null) { 433 return new Provider[0]; 434 } 435 // Sort providers by name to guarantee deterministic order in which providers are used in 436 // the tests. 437 return sortByName(providers); 438 } 439 440 private static Provider[] getKeyFactoryProviders() { 441 Provider[] providers = Security.getProviders("KeyFactory.EC"); 442 if (providers == null) { 443 return new Provider[0]; 444 } 445 446 // Do not test AndroidKeyStore's KeyFactory. It only handles Android Keystore-backed keys. 447 // It's OKish not to test AndroidKeyStore's KeyFactory here because it's tested by 448 // cts/tests/test/keystore. 449 List<Provider> filteredProvidersList = new ArrayList<Provider>(providers.length); 450 for (Provider provider : providers) { 451 if ("AndroidKeyStore".equals(provider.getName())) { 452 continue; 453 } 454 filteredProvidersList.add(provider); 455 } 456 providers = filteredProvidersList.toArray(new Provider[filteredProvidersList.size()]); 457 458 // Sort providers by name to guarantee deterministic order in which providers are used in 459 // the tests. 460 return sortByName(providers); 461 } 462 463 private static ECPrivateKey getPrivateKey(byte[] pkcs8EncodedKey, Provider provider) 464 throws GeneralSecurityException { 465 KeyFactory keyFactory = KeyFactory.getInstance("EC", provider); 466 return (ECPrivateKey) keyFactory.generatePrivate(new PKCS8EncodedKeySpec(pkcs8EncodedKey)); 467 } 468 469 private static ECPublicKey getPublicKey(byte[] x509EncodedKey, Provider provider) 470 throws GeneralSecurityException { 471 KeyFactory keyFactory = KeyFactory.getInstance("EC", provider); 472 return (ECPublicKey) keyFactory.generatePublic(new X509EncodedKeySpec(x509EncodedKey)); 473 } 474 475 private static ECPrivateKey getPrivateKey(byte[] pkcs8EncodedKey) 476 throws GeneralSecurityException { 477 KeyFactory keyFactory = KeyFactory.getInstance("EC"); 478 return (ECPrivateKey) keyFactory.generatePrivate(new PKCS8EncodedKeySpec(pkcs8EncodedKey)); 479 } 480 481 private static ECPublicKey getPublicKey(byte[] x509EncodedKey) 482 throws GeneralSecurityException { 483 KeyFactory keyFactory = KeyFactory.getInstance("EC"); 484 return (ECPublicKey) keyFactory.generatePublic(new X509EncodedKeySpec(x509EncodedKey)); 485 } 486 487 private static KeyAgreement getKeyAgreement(Provider provider) throws NoSuchAlgorithmException { 488 return KeyAgreement.getInstance("ECDH", provider); 489 } 490 491 private static byte[] generateSecret( 492 Provider keyAgreementProvider, PrivateKey privateKey, PublicKey publicKey) 493 throws GeneralSecurityException { 494 KeyAgreement keyAgreement = getKeyAgreement(keyAgreementProvider); 495 keyAgreement.init(privateKey); 496 keyAgreement.doPhase(publicKey, true); 497 return keyAgreement.generateSecret(); 498 } 499 500 private static Provider[] sortByName(Provider[] providers) { 501 Arrays.sort(providers, new Comparator<Provider>() { 502 @Override 503 public int compare(Provider lhs, Provider rhs) { 504 return lhs.getName().compareTo(rhs.getName()); 505 } 506 }); 507 return providers; 508 } 509 } 510