• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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 android.keystore.cts;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import static org.junit.Assert.assertThrows;
22 import static org.junit.Assert.fail;
23 
24 import android.content.Context;
25 import android.keystore.cts.util.ImportedKey;
26 import android.keystore.cts.util.TestUtils;
27 import android.security.keystore.KeyGenParameterSpec;
28 import android.security.keystore.KeyProperties;
29 import android.security.keystore.KeyProtection;
30 
31 import androidx.test.InstrumentationRegistry;
32 import androidx.test.runner.AndroidJUnit4;
33 
34 import com.android.compatibility.common.util.ApiTest;
35 
36 import org.junit.Test;
37 import org.junit.runner.RunWith;
38 
39 import java.security.InvalidAlgorithmParameterException;
40 import java.security.InvalidKeyException;
41 import java.security.KeyPair;
42 import java.security.KeyPairGenerator;
43 import java.security.KeyStore;
44 import java.security.KeyStoreException;
45 import java.security.NoSuchAlgorithmException;
46 import java.security.NoSuchProviderException;
47 import java.security.Signature;
48 import java.security.SignatureException;
49 import java.security.interfaces.EdECPublicKey;
50 import java.security.spec.ECGenParameterSpec;
51 import java.security.spec.InvalidKeySpecException;
52 import java.security.spec.NamedParameterSpec;
53 import java.util.Arrays;
54 import java.util.Base64;
55 
56 import javax.crypto.KeyAgreement;
57 
58 @RunWith(AndroidJUnit4.class)
59 public class Curve25519Test {
deleteEntry(String entry)60     private void deleteEntry(String entry) {
61         try {
62             KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
63             keyStore.deleteEntry(entry);
64         } catch (KeyStoreException e) {
65             // Skipped
66         }
67     }
68 
getContext()69     private Context getContext() {
70         return InstrumentationRegistry.getInstrumentation().getTargetContext();
71     }
72 
73     @Test
x25519KeyAgreementTest()74     public void x25519KeyAgreementTest() throws NoSuchAlgorithmException, NoSuchProviderException,
75             InvalidAlgorithmParameterException, InvalidKeySpecException, InvalidKeyException {
76         KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", "AndroidKeyStore");
77         // Aliases for both keys.
78         final String firstKeyAlias = "x25519-alias";
79         deleteEntry(firstKeyAlias);
80         final String secondKeyAlias = "x25519-alias-second";
81         deleteEntry(secondKeyAlias);
82 
83         // Generate first x25519 key pair.
84         KeyGenParameterSpec firstKeySpec = new KeyGenParameterSpec.Builder(firstKeyAlias,
85                 KeyProperties.PURPOSE_AGREE_KEY)
86                 .setAlgorithmParameterSpec(new ECGenParameterSpec("x25519")).build();
87         kpg.initialize(firstKeySpec);
88         KeyPair firstKeyPair = kpg.generateKeyPair();
89 
90         // Generate second x25519 key pair.
91         KeyGenParameterSpec secondKeySpec = new KeyGenParameterSpec.Builder(secondKeyAlias,
92                 KeyProperties.PURPOSE_AGREE_KEY)
93                 .setAlgorithmParameterSpec(new ECGenParameterSpec("x25519")).build();
94         kpg.initialize(secondKeySpec);
95         KeyPair secondKeyPair = kpg.generateKeyPair();
96 
97         // Attempt a key agreement with the private key from the first key pair and the public
98         // key from the second key pair.
99         KeyAgreement secondKa = KeyAgreement.getInstance("XDH");
100         secondKa.init(firstKeyPair.getPrivate());
101         secondKa.doPhase(secondKeyPair.getPublic(), true);
102         byte[] secondSecret = secondKa.generateSecret();
103 
104         // Attempt a key agreement "the other way around": using the private key from the second
105         // key pair and the public key from the first key pair.
106         KeyAgreement firstKa = KeyAgreement.getInstance("XDH");
107         firstKa.init(secondKeyPair.getPrivate());
108         firstKa.doPhase(firstKeyPair.getPublic(), true);
109         byte[] firstSecret = firstKa.generateSecret();
110 
111         // Both secrets being equal means the key agreement was successful.
112         assertThat(Arrays.compare(firstSecret, secondSecret)).isEqualTo(0);
113     }
114 
115     @Test
116     @ApiTest(apis = {"java.security.KeyStore#setEntry", "javax.crypto.KeyAgreement#doPhase"})
x25519KeyImportAndAgreementTest()117     public void x25519KeyImportAndAgreementTest() throws Exception {
118         final String alias = "import-x25519";
119         deleteEntry(alias);
120 
121         KeyProtection importParams = new KeyProtection.Builder(KeyProperties.PURPOSE_AGREE_KEY)
122                 .build();
123         ImportedKey importedKey = TestUtils.importIntoAndroidKeyStore(
124                     alias,
125                     getContext(),
126                     R.raw.ec_key8_x25519,
127                     R.raw.ec_key8_x25519_cert,
128                     importParams);
129 
130         assertThat(importedKey).isNotNull();
131         //Second key generate from AndroidOpenSSL
132         KeyPairGenerator kpg = KeyPairGenerator.getInstance("XDH");
133         KeyPair secondKeyPair = kpg.generateKeyPair();
134 
135         // Attempt a key agreement with the private key from the first key pair and the public
136         // key from the second key pair.
137         KeyAgreement secondKa = KeyAgreement.getInstance("XDH");
138         secondKa.init(importedKey.getKeystoreBackedKeyPair().getPrivate());
139         secondKa.doPhase(secondKeyPair.getPublic(), true);
140         byte[] secondSecret = secondKa.generateSecret();
141 
142         // Attempt a key agreement "the other way around": using the private key from the second
143         // key pair and the public key from the first key pair.
144         KeyAgreement firstKa = KeyAgreement.getInstance("XDH");
145         firstKa.init(secondKeyPair.getPrivate());
146         firstKa.doPhase(importedKey.getKeystoreBackedKeyPair().getPublic(), true);
147         byte[] firstSecret = firstKa.generateSecret();
148 
149         // Both secrets being equal means the key agreement was successful.
150         assertThat(Arrays.compare(firstSecret, secondSecret)).isEqualTo(0);
151     }
152 
153     @Test
ed25519KeyGenerationAndSigningTest()154     public void ed25519KeyGenerationAndSigningTest()
155             throws NoSuchAlgorithmException, NoSuchProviderException,
156             InvalidAlgorithmParameterException, InvalidKeyException, SignatureException {
157         KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", "AndroidKeyStore");
158         final String alias = "ed25519-alias";
159         deleteEntry(alias);
160 
161         KeyGenParameterSpec keySpec = new KeyGenParameterSpec.Builder(alias,
162                 KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
163                 .setAlgorithmParameterSpec(new ECGenParameterSpec("ed25519"))
164                 .setDigests(KeyProperties.DIGEST_NONE).build();
165         kpg.initialize(keySpec);
166 
167         KeyPair kp = kpg.generateKeyPair();
168         assertThat(kp.getPublic()).isInstanceOf(EdECPublicKey.class);
169 
170         byte[] data = "helloxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx".getBytes();
171         Signature signer = Signature.getInstance("Ed25519");
172         signer.initSign(kp.getPrivate());
173         signer.update(data);
174         byte[] sigBytes = signer.sign();
175         assertThat(sigBytes.length).isEqualTo(64);
176         EdECPublicKey publicKey = (EdECPublicKey) kp.getPublic();
177         android.util.Log.i("Curve25519Test", "Manually validate: Payload "
178                 + Base64.getEncoder().encodeToString(data) + " encoded key: "
179                 + Base64.getEncoder().encodeToString(kp.getPublic().getEncoded())
180                 + " signature: " + Base64.getEncoder().encodeToString(sigBytes));
181 
182         //TODO: Verify signature over the data when Conscrypt supports validating Ed25519
183         // signatures.
184     }
185 
186     @Test
testX25519CannotBeUsedForSigning()187     public void testX25519CannotBeUsedForSigning()
188             throws NoSuchAlgorithmException, NoSuchProviderException {
189         KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", "AndroidKeyStore");
190         final String alias = "x25519-baduse-alias";
191         deleteEntry(alias);
192 
193         KeyGenParameterSpec keySpec = new KeyGenParameterSpec.Builder(alias,
194                 KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
195                 .setAlgorithmParameterSpec(new ECGenParameterSpec("x25519")).build();
196 
197         assertThrows(InvalidAlgorithmParameterException.class, () -> kpg.initialize(keySpec));
198     }
199 
200     @Test
testEd25519CannotBeUsedForKeyExchange()201     public void testEd25519CannotBeUsedForKeyExchange() throws NoSuchAlgorithmException,
202             NoSuchProviderException {
203         KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", "AndroidKeyStore");
204         final String alias = "ed25519-baduse-alias";
205         deleteEntry(alias);
206 
207         KeyGenParameterSpec keySpec = new KeyGenParameterSpec.Builder(alias,
208                 KeyProperties.PURPOSE_AGREE_KEY)
209                 .setAlgorithmParameterSpec(new ECGenParameterSpec("ed25519")).build();
210 
211         assertThrows(InvalidAlgorithmParameterException.class, () -> kpg.initialize(keySpec));
212     }
213 
214     @Test
x25519CannotCreateKeyUsingKPGWithNamedParameterSpec()215     public void x25519CannotCreateKeyUsingKPGWithNamedParameterSpec()
216             throws NoSuchAlgorithmException, NoSuchProviderException,
217             InvalidAlgorithmParameterException {
218         KeyPairGenerator kpg = KeyPairGenerator.getInstance("XDH", "AndroidKeyStore");
219 
220         NamedParameterSpec paramSpec = new NamedParameterSpec("X25519");
221         try {
222             kpg.initialize(paramSpec);
223             fail("Should not be able to generate keys using NamedParameterSpec");
224         } catch (IllegalArgumentException e) {
225             assertThat(e.getMessage()).contains("cannot be initialized using NamedParameterSpec");
226         }
227     }
228 
229     @Test
ed25519CannotCreateKeyUsingKPGWithNamedParameterSpec()230     public void ed25519CannotCreateKeyUsingKPGWithNamedParameterSpec()
231             throws NoSuchAlgorithmException, NoSuchProviderException,
232             InvalidAlgorithmParameterException {
233         KeyPairGenerator kpg = KeyPairGenerator.getInstance("XDH", "AndroidKeyStore");
234 
235         NamedParameterSpec paramSpec = new NamedParameterSpec("Ed25519");
236         try {
237             kpg.initialize(paramSpec);
238             fail("Should not be able to generate keys using NamedParameterSpec");
239         } catch (IllegalArgumentException e) {
240             assertThat(e.getMessage()).contains("cannot be initialized using NamedParameterSpec");
241         }
242     }
243 }
244