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 17package libcore.javax.crypto; 18 19import static libcore.java.security.SignatureTest.hexToBytes; 20 21import junit.framework.AssertionFailedError; 22import junit.framework.TestCase; 23 24import java.lang.reflect.InvocationTargetException; 25import java.lang.reflect.Method; 26import java.security.GeneralSecurityException; 27import java.security.InvalidAlgorithmParameterException; 28import java.security.InvalidKeyException; 29import java.security.KeyFactory; 30import java.security.NoSuchAlgorithmException; 31import java.security.PrivateKey; 32import java.security.Provider; 33import java.security.PublicKey; 34import java.security.Security; 35import java.security.interfaces.ECPrivateKey; 36import java.security.interfaces.ECPublicKey; 37import java.security.spec.ECGenParameterSpec; 38import java.security.spec.PKCS8EncodedKeySpec; 39import java.security.spec.X509EncodedKeySpec; 40import java.util.Arrays; 41import java.util.Comparator; 42 43import javax.crypto.KeyAgreement; 44import javax.crypto.SecretKey; 45import javax.crypto.ShortBufferException; 46 47/** 48 * Tests for all registered Elliptic Curve Diffie-Hellman {@link KeyAgreement} providers. 49 */ 50public class ECDHKeyAgreementTest extends TestCase { 51 // Two key pairs and the resulting shared secret for the Known Answer Test 52 private static final byte[] KAT_PUBLIC_KEY1_X509 = hexToBytes( 53 "3059301306072a8648ce3d020106082a8648ce3d030107034200049fc2f71f85446b1371244491d83" 54 + "9cf97b5d27cedbb04d2c0058b59709df3a216e6b4ca1b2d622588c5a0e6968144a8965e816a600c" 55 + "05305a1da3df2bf02b41d1"); 56 private static final byte[] KAT_PRIVATE_KEY1_PKCS8 = hexToBytes( 57 "308193020100301306072a8648ce3d020106082a8648ce3d030107047930770201010420e1e683003" 58 + "c8b963a92742e5f955ce7fddc81d0c3ae9b149d6af86a0cacb2271ca00a06082a8648ce3d030107" 59 + "a144034200049fc2f71f85446b1371244491d839cf97b5d27cedbb04d2c0058b59709df3a216e6b" 60 + "4ca1b2d622588c5a0e6968144a8965e816a600c05305a1da3df2bf02b41d1"); 61 62 private static final byte[] KAT_PUBLIC_KEY2_X509 = hexToBytes( 63 "3059301306072a8648ce3d020106082a8648ce3d03010703420004358efb6d91e5bbcae21774af3f6" 64 + "d85d0848630e7e61dbeb5ac9e47036ed0f8d38c7a1d1bb249f92861c7c9153fff33f45ab5b171eb" 65 + "e8cad741125e6bb4fc6b07"); 66 private static final byte[] KAT_PRIVATE_KEY2_PKCS8 = hexToBytes( 67 "308193020100301306072a8648ce3d020106082a8648ce3d0301070479307702010104202b1810a69" 68 + "e12b74d50bf0343168f705f0104f76299855268aa526fdb31e6eec0a00a06082a8648ce3d030107" 69 + "a14403420004358efb6d91e5bbcae21774af3f6d85d0848630e7e61dbeb5ac9e47036ed0f8d38c7" 70 + "a1d1bb249f92861c7c9153fff33f45ab5b171ebe8cad741125e6bb4fc6b07"); 71 72 private static final byte[] KAT_SECRET = 73 hexToBytes("4faa0594c0e773eb26c8df2163af2443e88aab9578b9e1f324bc61e42d222783"); 74 75 private static final ECPublicKey KAT_PUBLIC_KEY1; 76 private static final ECPrivateKey KAT_PRIVATE_KEY1; 77 private static final ECPublicKey KAT_PUBLIC_KEY2; 78 private static final ECPrivateKey KAT_PRIVATE_KEY2; 79 static { 80 try { 81 KAT_PUBLIC_KEY1 = getPublicKey(KAT_PUBLIC_KEY1_X509); 82 KAT_PRIVATE_KEY1 = getPrivateKey(KAT_PRIVATE_KEY1_PKCS8); 83 KAT_PUBLIC_KEY2 = getPublicKey(KAT_PUBLIC_KEY2_X509); 84 KAT_PRIVATE_KEY2 = getPrivateKey(KAT_PRIVATE_KEY2_PKCS8); 85 } catch (Exception e) { 86 throw new RuntimeException("Failed to decode KAT key pairs using default provider", e); 87 } 88 } 89 90 /** 91 * Performs a known-answer test of the shared secret for all permutations of {@code Providers} 92 * of: first key pair, second key pair, and the {@code KeyAgreement}. This is to check that 93 * the {@code KeyAgreement} instances work with keys of all registered providers. 94 */ 95 public void testKnownAnswer() throws Exception { 96 for (Provider keyFactoryProvider1 : getKeyFactoryProviders()) { 97 ECPrivateKey privateKey1 = getPrivateKey(KAT_PRIVATE_KEY1_PKCS8, keyFactoryProvider1); 98 ECPublicKey publicKey1 = getPublicKey(KAT_PUBLIC_KEY1_X509, keyFactoryProvider1); 99 for (Provider keyFactoryProvider2 : getKeyFactoryProviders()) { 100 ECPrivateKey privateKey2 = 101 getPrivateKey(KAT_PRIVATE_KEY2_PKCS8, keyFactoryProvider2); 102 ECPublicKey publicKey2 = 103 getPublicKey(KAT_PUBLIC_KEY2_X509, keyFactoryProvider2); 104 for (Provider keyAgreementProvider : getKeyAgreementProviders()) { 105 try { 106 testKnownAnswer(publicKey1, privateKey1, publicKey2, privateKey2, 107 keyAgreementProvider); 108 } catch (Throwable e) { 109 throw new RuntimeException(getClass().getSimpleName() + ".testKnownAnswer(" 110 + keyFactoryProvider1.getName() 111 + ", " + keyFactoryProvider2.getName() 112 + ", " + keyAgreementProvider.getName() + ")", 113 e); 114 } 115 } 116 } 117 } 118 } 119 120 void testKnownAnswer( 121 ECPublicKey publicKey1, ECPrivateKey privateKey1, 122 ECPublicKey publicKey2, ECPrivateKey privateKey2, 123 Provider keyAgreementProvider) throws Exception { 124 assertTrue(Arrays.equals( 125 KAT_SECRET, generateSecret(keyAgreementProvider, privateKey1, publicKey2))); 126 assertTrue(Arrays.equals( 127 KAT_SECRET, generateSecret(keyAgreementProvider, privateKey2, publicKey1))); 128 } 129 130 public void testGetAlgorithm() throws Exception { 131 invokeCallingMethodForEachKeyAgreementProvider(); 132 } 133 134 void testGetAlgorithm(Provider provider) throws Exception { 135 assertEquals("ECDH", getKeyAgreement(provider).getAlgorithm()); 136 } 137 138 public void testGetProvider() throws Exception { 139 invokeCallingMethodForEachKeyAgreementProvider(); 140 } 141 142 void testGetProvider(Provider provider) throws Exception { 143 assertSame(provider, getKeyAgreement(provider).getProvider()); 144 } 145 146 public void testInit_withNullPrivateKey() throws Exception { 147 invokeCallingMethodForEachKeyAgreementProvider(); 148 } 149 150 void testInit_withNullPrivateKey(Provider provider) throws Exception { 151 KeyAgreement keyAgreement = getKeyAgreement(provider); 152 try { 153 keyAgreement.init(null); 154 fail(); 155 } catch (InvalidKeyException expected) {} 156 } 157 158 public void testInit_withUnsupportedPrivateKeyType() throws Exception { 159 invokeCallingMethodForEachKeyAgreementProvider(); 160 } 161 162 void testInit_withUnsupportedPrivateKeyType(Provider provider) throws Exception { 163 KeyAgreement keyAgreement = getKeyAgreement(provider); 164 try { 165 keyAgreement.init(KAT_PUBLIC_KEY1); 166 fail(); 167 } catch (InvalidKeyException expected) {} 168 } 169 170 public void testInit_withUnsupportedAlgorithmParameterSpec() throws Exception { 171 invokeCallingMethodForEachKeyAgreementProvider(); 172 } 173 174 void testInit_withUnsupportedAlgorithmParameterSpec(Provider provider) throws Exception { 175 try { 176 getKeyAgreement(provider).init(KAT_PRIVATE_KEY1, new ECGenParameterSpec("prime256v1")); 177 fail(); 178 } catch (InvalidAlgorithmParameterException expected) {} 179 } 180 181 public void testDoPhase_whenNotInitialized() throws Exception { 182 invokeCallingMethodForEachKeyAgreementProvider(); 183 } 184 185 void testDoPhase_whenNotInitialized(Provider provider) throws Exception { 186 try { 187 getKeyAgreement(provider).doPhase(KAT_PUBLIC_KEY1, true); 188 fail(); 189 } catch (IllegalStateException expected) {} 190 } 191 192 public void testDoPhaseReturnsNull() throws Exception { 193 invokeCallingMethodForEachKeyAgreementProvider(); 194 } 195 196 void testDoPhaseReturnsNull(Provider provider) throws Exception { 197 KeyAgreement keyAgreement = getKeyAgreement(provider); 198 keyAgreement.init(KAT_PRIVATE_KEY1); 199 assertNull(keyAgreement.doPhase(KAT_PUBLIC_KEY2, true)); 200 } 201 202 public void testDoPhase_withPhaseWhichIsNotLast() throws Exception { 203 invokeCallingMethodForEachKeyAgreementProvider(); 204 } 205 206 void testDoPhase_withPhaseWhichIsNotLast(Provider provider) throws Exception { 207 KeyAgreement keyAgreement = getKeyAgreement(provider); 208 keyAgreement.init(KAT_PRIVATE_KEY1); 209 try { 210 keyAgreement.doPhase(KAT_PUBLIC_KEY2, false); 211 fail(); 212 } catch (IllegalStateException expected) {} 213 } 214 215 public void testDoPhase_withNullKey() throws Exception { 216 invokeCallingMethodForEachKeyAgreementProvider(); 217 } 218 219 void testDoPhase_withNullKey(Provider provider) throws Exception { 220 KeyAgreement keyAgreement = getKeyAgreement(provider); 221 keyAgreement.init(KAT_PRIVATE_KEY1); 222 try { 223 keyAgreement.doPhase(null, true); 224 fail(); 225 } catch (InvalidKeyException expected) {} 226 } 227 228 public void testDoPhase_withInvalidKeyType() throws Exception { 229 invokeCallingMethodForEachKeyAgreementProvider(); 230 } 231 232 void testDoPhase_withInvalidKeyType(Provider provider) throws Exception { 233 KeyAgreement keyAgreement = getKeyAgreement(provider); 234 keyAgreement.init(KAT_PRIVATE_KEY1); 235 try { 236 keyAgreement.doPhase(KAT_PRIVATE_KEY1, true); 237 fail(); 238 } catch (InvalidKeyException expected) {} 239 } 240 241 public void testGenerateSecret_withNullOutputBuffer() throws Exception { 242 invokeCallingMethodForEachKeyAgreementProvider(); 243 } 244 245 void testGenerateSecret_withNullOutputBuffer(Provider provider) throws Exception { 246 KeyAgreement keyAgreement = getKeyAgreement(provider); 247 keyAgreement.init(KAT_PRIVATE_KEY1); 248 keyAgreement.doPhase(KAT_PUBLIC_KEY2, true); 249 try { 250 keyAgreement.generateSecret(null, 0); 251 fail(); 252 } catch (NullPointerException expected) {} 253 } 254 255 public void testGenerateSecret_withBufferOfTheRightSize() throws Exception { 256 invokeCallingMethodForEachKeyAgreementProvider(); 257 } 258 259 void testGenerateSecret_withBufferOfTheRightSize(Provider provider) throws Exception { 260 KeyAgreement keyAgreement = getKeyAgreement(provider); 261 keyAgreement.init(KAT_PRIVATE_KEY1); 262 keyAgreement.doPhase(KAT_PUBLIC_KEY2, true); 263 264 byte[] buffer = new byte[KAT_SECRET.length]; 265 int secretLengthBytes = keyAgreement.generateSecret(buffer, 0); 266 assertEquals(KAT_SECRET.length, secretLengthBytes); 267 assertTrue(Arrays.equals(KAT_SECRET, buffer)); 268 } 269 270 public void testGenerateSecret_withLargerThatNeededBuffer() throws Exception { 271 invokeCallingMethodForEachKeyAgreementProvider(); 272 } 273 274 void testGenerateSecret_withLargerThatNeededBuffer(Provider provider) throws Exception { 275 KeyAgreement keyAgreement = getKeyAgreement(provider); 276 keyAgreement.init(KAT_PRIVATE_KEY1); 277 keyAgreement.doPhase(KAT_PUBLIC_KEY2, true); 278 279 // Place the shared secret in the middle of the larger buffer and check that only that 280 // part of the buffer is affected. 281 byte[] buffer = new byte[KAT_SECRET.length + 2]; 282 buffer[0] = (byte) 0x85; // arbitrary canary value 283 buffer[buffer.length - 1] = (byte) 0x3b; // arbitrary canary value 284 int secretLengthBytes = keyAgreement.generateSecret(buffer, 1); 285 assertEquals(KAT_SECRET.length, secretLengthBytes); 286 assertEquals((byte) 0x85, buffer[0]); 287 assertEquals((byte) 0x3b, buffer[buffer.length - 1]); 288 byte[] secret = new byte[KAT_SECRET.length]; 289 System.arraycopy(buffer, 1, secret, 0, secret.length); 290 assertTrue(Arrays.equals(KAT_SECRET, secret)); 291 } 292 293 public void testGenerateSecret_withSmallerThanNeededBuffer() throws Exception { 294 invokeCallingMethodForEachKeyAgreementProvider(); 295 } 296 297 void testGenerateSecret_withSmallerThanNeededBuffer(Provider provider) throws Exception { 298 KeyAgreement keyAgreement = getKeyAgreement(provider); 299 keyAgreement.init(KAT_PRIVATE_KEY1); 300 keyAgreement.doPhase(KAT_PUBLIC_KEY2, true); 301 try { 302 // Although the buffer is big enough (1024 bytes) the shared secret should be placed 303 // at offset 1020 thus leaving only 4 bytes for the secret, which is not enough. 304 keyAgreement.generateSecret(new byte[1024], 1020); 305 fail(); 306 } catch (ShortBufferException expected) {} 307 } 308 309 public void testGenerateSecret_withoutBuffer() throws Exception { 310 invokeCallingMethodForEachKeyAgreementProvider(); 311 } 312 313 void testGenerateSecret_withoutBuffer(Provider provider) throws Exception { 314 KeyAgreement keyAgreement = getKeyAgreement(provider); 315 keyAgreement.init(KAT_PRIVATE_KEY2); 316 keyAgreement.doPhase(KAT_PUBLIC_KEY1, true); 317 318 byte[] secret = keyAgreement.generateSecret(); 319 assertTrue(Arrays.equals(KAT_SECRET, secret)); 320 } 321 322 public void testGenerateSecret_withAlgorithm() throws Exception { 323 invokeCallingMethodForEachKeyAgreementProvider(); 324 } 325 326 void testGenerateSecret_withAlgorithm(Provider provider) throws Exception { 327 KeyAgreement keyAgreement = getKeyAgreement(provider); 328 keyAgreement.init(KAT_PRIVATE_KEY2); 329 keyAgreement.doPhase(KAT_PUBLIC_KEY1, true); 330 331 SecretKey key = keyAgreement.generateSecret("AES"); 332 assertEquals("AES", key.getAlgorithm()); 333 // The check below will need to change if it's a hardware-backed key. 334 // We'll have to encrypt a known plaintext and check that the ciphertext is as 335 // expected. 336 assertTrue(Arrays.equals(KAT_SECRET, key.getEncoded())); 337 } 338 339 private void invokeCallingMethodForEachKeyAgreementProvider() throws Exception { 340 StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); 341 String callingMethodName = null; 342 for (int i = 0; i < stackTrace.length; i++) { 343 if ("invokeCallingMethodForEachKeyAgreementProvider".equals( 344 stackTrace[i].getMethodName())) { 345 callingMethodName = stackTrace[i + 1].getMethodName(); 346 } 347 } 348 if (callingMethodName == null) { 349 throw new RuntimeException("Failed to deduce calling method name from stack trace"); 350 } 351 352 String invokedMethodName = callingMethodName; 353 Method method; 354 try { 355 method = getClass().getDeclaredMethod(invokedMethodName, Provider.class); 356 } catch (NoSuchMethodError e) { 357 throw new AssertionFailedError("Failed to find per-Provider test method " 358 + getClass().getSimpleName() + "." + invokedMethodName + "(Provider)"); 359 } 360 361 for (Provider provider : getKeyAgreementProviders()) { 362 try { 363 method.invoke(this, provider); 364 } catch (InvocationTargetException e) { 365 throw new RuntimeException(getClass().getSimpleName() + "." + invokedMethodName 366 + "(provider: " + provider.getName() + ") failed", 367 e.getCause()); 368 } 369 } 370 } 371 372 private static Provider[] getKeyAgreementProviders() { 373 Provider[] providers = Security.getProviders("KeyAgreement.ECDH"); 374 if (providers == null) { 375 return new Provider[0]; 376 } 377 // Sort providers by name to guarantee non-determinism in the order in which providers are 378 // used in the tests. 379 return sortByName(providers); 380 } 381 382 private static Provider[] getKeyFactoryProviders() { 383 Provider[] providers = Security.getProviders("KeyFactory.EC"); 384 if (providers == null) { 385 return new Provider[0]; 386 } 387 // Sort providers by name to guarantee non-determinism in the order in which providers are 388 // used in the tests. 389 return sortByName(providers); 390 } 391 392 private static ECPrivateKey getPrivateKey(byte[] pkcs8EncodedKey, Provider provider) 393 throws GeneralSecurityException { 394 KeyFactory keyFactory = KeyFactory.getInstance("EC", provider); 395 return (ECPrivateKey) keyFactory.generatePrivate(new PKCS8EncodedKeySpec(pkcs8EncodedKey)); 396 } 397 398 private static ECPublicKey getPublicKey(byte[] x509EncodedKey, Provider provider) 399 throws GeneralSecurityException { 400 KeyFactory keyFactory = KeyFactory.getInstance("EC", provider); 401 return (ECPublicKey) keyFactory.generatePublic(new X509EncodedKeySpec(x509EncodedKey)); 402 } 403 404 private static ECPrivateKey getPrivateKey(byte[] pkcs8EncodedKey) 405 throws GeneralSecurityException { 406 KeyFactory keyFactory = KeyFactory.getInstance("EC"); 407 return (ECPrivateKey) keyFactory.generatePrivate(new PKCS8EncodedKeySpec(pkcs8EncodedKey)); 408 } 409 410 private static ECPublicKey getPublicKey(byte[] x509EncodedKey) 411 throws GeneralSecurityException { 412 KeyFactory keyFactory = KeyFactory.getInstance("EC"); 413 return (ECPublicKey) keyFactory.generatePublic(new X509EncodedKeySpec(x509EncodedKey)); 414 } 415 416 private static KeyAgreement getKeyAgreement(Provider provider) throws NoSuchAlgorithmException { 417 return KeyAgreement.getInstance("ECDH", provider); 418 } 419 420 private static byte[] generateSecret( 421 Provider keyAgreementProvider, PrivateKey privateKey, PublicKey publicKey) 422 throws GeneralSecurityException { 423 KeyAgreement keyAgreement = getKeyAgreement(keyAgreementProvider); 424 keyAgreement.init(privateKey); 425 keyAgreement.doPhase(publicKey, true); 426 return keyAgreement.generateSecret(); 427 } 428 429 private static Provider[] sortByName(Provider[] providers) { 430 Arrays.sort(providers, new Comparator<Provider>() { 431 @Override 432 public int compare(Provider lhs, Provider rhs) { 433 return lhs.getName().compareTo(rhs.getName()); 434 } 435 }); 436 return providers; 437 } 438} 439