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