Home | History | Annotate | Download | only in crypto
      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