• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2017 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 android.security.keymaster.KeymasterDefs.KM_ALGORITHM_3DES;
20 import static android.security.keymaster.KeymasterDefs.KM_ALGORITHM_AES;
21 import static android.security.keymaster.KeymasterDefs.KM_ALGORITHM_EC;
22 import static android.security.keymaster.KeymasterDefs.KM_ALGORITHM_RSA;
23 import static android.security.keymaster.KeymasterDefs.KM_DIGEST_MD5;
24 import static android.security.keymaster.KeymasterDefs.KM_DIGEST_NONE;
25 import static android.security.keymaster.KeymasterDefs.KM_DIGEST_SHA1;
26 import static android.security.keymaster.KeymasterDefs.KM_DIGEST_SHA_2_224;
27 import static android.security.keymaster.KeymasterDefs.KM_DIGEST_SHA_2_256;
28 import static android.security.keymaster.KeymasterDefs.KM_DIGEST_SHA_2_384;
29 import static android.security.keymaster.KeymasterDefs.KM_DIGEST_SHA_2_512;
30 import static android.security.keymaster.KeymasterDefs.KM_KEY_FORMAT_PKCS8;
31 import static android.security.keymaster.KeymasterDefs.KM_KEY_FORMAT_RAW;
32 import static android.security.keymaster.KeymasterDefs.KM_MODE_CBC;
33 import static android.security.keymaster.KeymasterDefs.KM_MODE_ECB;
34 import static android.security.keymaster.KeymasterDefs.KM_PAD_NONE;
35 import static android.security.keymaster.KeymasterDefs.KM_PAD_PKCS7;
36 import static android.security.keymaster.KeymasterDefs.KM_PAD_RSA_OAEP;
37 import static android.security.keymaster.KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT;
38 import static android.security.keymaster.KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN;
39 import static android.security.keymaster.KeymasterDefs.KM_PAD_RSA_PSS;
40 import static android.security.keymaster.KeymasterDefs.KM_PURPOSE_DECRYPT;
41 import static android.security.keymaster.KeymasterDefs.KM_PURPOSE_ENCRYPT;
42 import static android.security.keymaster.KeymasterDefs.KM_PURPOSE_SIGN;
43 import static android.security.keymaster.KeymasterDefs.KM_PURPOSE_VERIFY;
44 import static android.security.keymaster.KeymasterDefs.KM_TAG_ALGORITHM;
45 import static android.security.keymaster.KeymasterDefs.KM_TAG_BLOCK_MODE;
46 import static android.security.keymaster.KeymasterDefs.KM_TAG_DIGEST;
47 import static android.security.keymaster.KeymasterDefs.KM_TAG_KEY_SIZE;
48 import static android.security.keymaster.KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED;
49 import static android.security.keymaster.KeymasterDefs.KM_TAG_PADDING;
50 import static android.security.keymaster.KeymasterDefs.KM_TAG_PURPOSE;
51 
52 import static com.google.common.truth.Truth.assertThat;
53 import static com.google.common.truth.Truth.assertWithMessage;
54 
55 import static org.junit.Assert.assertEquals;
56 import static org.junit.Assert.assertFalse;
57 import static org.junit.Assert.assertTrue;
58 import static org.junit.Assume.assumeNoException;
59 import static org.junit.Assume.assumeTrue;
60 
61 import android.content.Context;
62 import android.keystore.cts.util.TestUtils;
63 import android.security.keystore.KeyGenParameterSpec;
64 import android.security.keystore.KeyProperties;
65 import android.security.keystore.SecureKeyImportUnavailableException;
66 import android.security.keystore.WrappedKeyEntry;
67 
68 import androidx.test.InstrumentationRegistry;
69 import androidx.test.runner.AndroidJUnit4;
70 
71 import org.bouncycastle.asn1.ASN1Encoding;
72 import org.bouncycastle.asn1.ASN1EncodableVector;
73 import org.bouncycastle.asn1.ASN1Integer;
74 import org.bouncycastle.asn1.DERNull;
75 import org.bouncycastle.asn1.DEROctetString;
76 import org.bouncycastle.asn1.DERSequence;
77 import org.bouncycastle.asn1.DERSet;
78 import org.bouncycastle.asn1.DERTaggedObject;
79 import org.junit.Test;
80 import org.junit.runner.RunWith;
81 
82 import java.security.Key;
83 import java.security.KeyPair;
84 import java.security.KeyPairGenerator;
85 import java.security.KeyStore;
86 import java.security.KeyStore.Entry;
87 import java.security.KeyStoreException;
88 import java.security.PrivateKey;
89 import java.security.PublicKey;
90 import java.security.SecureRandom;
91 import java.security.Signature;
92 import java.security.spec.AlgorithmParameterSpec;
93 import java.security.spec.MGF1ParameterSpec;
94 import java.util.Arrays;
95 
96 import javax.crypto.Cipher;
97 import javax.crypto.KeyGenerator;
98 import javax.crypto.spec.GCMParameterSpec;
99 import javax.crypto.spec.IvParameterSpec;
100 import javax.crypto.spec.OAEPParameterSpec;
101 import javax.crypto.spec.PSource;
102 import javax.crypto.spec.SecretKeySpec;
103 
104 @RunWith(AndroidJUnit4.class)
105 public class ImportWrappedKeyTest {
106     private static final String TAG = "ImportWrappedKeyTest";
107 
108     private static final String ALIAS = "my key";
109     private static final String WRAPPING_KEY_ALIAS = "my_favorite_wrapping_key";
110 
111     private static final int WRAPPED_FORMAT_VERSION = 0;
112     private static final int GCM_TAG_SIZE = 128;
113 
114     SecureRandom random = new SecureRandom();
115 
getContext()116     private Context getContext() {
117         return InstrumentationRegistry.getInstrumentation().getTargetContext();
118     }
119 
removeTagType(int tag)120     private int removeTagType(int tag) {
121         int kmTagTypeMask = 0x0FFFFFFF;
122         return tag & kmTagTypeMask;
123     }
124 
125     @Test
testKeyStore_ImportWrappedKey_AES()126     public void testKeyStore_ImportWrappedKey_AES() throws Exception {
127         testKeyStore_ImportWrappedKey_AES(false);
128     }
129 
130     @Test
testKeyStore_ImportWrappedKey_AES_StrongBox()131     public void testKeyStore_ImportWrappedKey_AES_StrongBox() throws Exception {
132         testKeyStore_ImportWrappedKey_AES(true);
133     }
134 
testKeyStore_ImportWrappedKey_AES(boolean isStrongBox)135     public void testKeyStore_ImportWrappedKey_AES(boolean isStrongBox) throws Exception {
136         if (isStrongBox) {
137             TestUtils.assumeStrongBox();
138         }
139 
140         KeyGenerator kg = KeyGenerator.getInstance("AES");
141         kg.init(256);
142         Key swKey = kg.generateKey();
143 
144         byte[] keyMaterial = swKey.getEncoded();
145         byte[] mask = new byte[32]; // Zero mask
146 
147         try {
148             importWrappedKey(wrapKey(
149                     genKeyPair(WRAPPING_KEY_ALIAS, isStrongBox).getPublic(),
150                     keyMaterial,
151                     mask,
152                     KM_KEY_FORMAT_RAW,
153                     makeAesAuthList(keyMaterial.length * 8)));
154         } catch (SecureKeyImportUnavailableException e) {
155             assumeNoException("Can only test if secure key import is available", e);
156         }
157 
158         // Use Key
159         KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
160         keyStore.load(null, null);
161 
162         assertTrue("Failed to load key after wrapped import", keyStore.containsAlias(ALIAS));
163 
164         Key importedKey = keyStore.getKey(ALIAS, null);
165         String plaintext = "hello, world";
166 
167         Cipher c = Cipher.getInstance("AES/ECB/PKCS7Padding");
168         c.init(Cipher.ENCRYPT_MODE, importedKey);
169         byte[] encrypted = c.doFinal(plaintext.getBytes());
170 
171         // Decrypt using key imported into keystore.
172         c = Cipher.getInstance("AES/ECB/PKCS7Padding");
173         c.init(Cipher.DECRYPT_MODE, importedKey);
174         assertEquals(new String(c.doFinal(encrypted)), plaintext);
175 
176         // Decrypt using local software copy of the key.
177         c = Cipher.getInstance("AES/ECB/PKCS7Padding");
178         c.init(Cipher.DECRYPT_MODE, swKey);
179         assertEquals(new String(c.doFinal(encrypted)), plaintext);
180     }
181 
182     @Test
testKeyStore_ImportIncorrectWrappedKey()183     public void testKeyStore_ImportIncorrectWrappedKey() throws Exception {
184         testKeyStore_ImportIncorrectWrappedKey(false);
185     }
186 
187     @Test
testKeyStore_ImportIncorrectWrappedKey_StrongBox()188     public void testKeyStore_ImportIncorrectWrappedKey_StrongBox() throws Exception {
189         testKeyStore_ImportIncorrectWrappedKey(true);
190     }
191 
testKeyStore_ImportIncorrectWrappedKey(boolean isStrongBox)192     private void testKeyStore_ImportIncorrectWrappedKey(boolean isStrongBox) throws Exception {
193         if (isStrongBox) {
194             TestUtils.assumeStrongBox();
195         }
196         random.setSeed(0);
197 
198         byte[] keyMaterial = new byte[32];
199         random.nextBytes(keyMaterial);
200         byte[] mask = new byte[32]; // Zero mask
201 
202         KeyStoreException exception = null;
203         try {
204             importWrappedKey(
205                     wrapKey(
206                             genKeyPair(WRAPPING_KEY_ALIAS, isStrongBox).getPublic(),
207                             keyMaterial,
208                             mask,
209                             KM_KEY_FORMAT_RAW,
210                             makeAesAuthList(keyMaterial.length * 8),
211                             /* correctWrappingRequired= */ false));
212         } catch (SecureKeyImportUnavailableException e) {
213             assumeNoException("Can only test if secure key import is available", e);
214         } catch (KeyStoreException e) {
215             exception = e;
216         }
217         assertWithMessage("Did not hit a failure but expected one").that(exception).isNotNull();
218         assertThat(exception.getCause()).isInstanceOf(android.security.KeyStoreException.class);
219         android.security.KeyStoreException ksException =
220                 (android.security.KeyStoreException) exception.getCause();
221         assertFalse("Importing incorrectly wrapped key should not cause transient failure in"
222                     + " Key{Mint/Master}. That means performing same operation will fail always.",
223                         ksException.isTransientFailure());
224     }
225 
226     @Test
testKeyStore_ImportWrappedKeyWrappingKeyMissing()227     public void testKeyStore_ImportWrappedKeyWrappingKeyMissing() throws Exception {
228         final String EXPECTED_FAILURE = "Failed to import wrapped key. Keystore error code: 7";
229         KeyStoreException exception = null;
230 
231         try {
232             byte [] fakeWrappedKey = new byte[1];
233             importWrappedKey(fakeWrappedKey, WRAPPING_KEY_ALIAS + "_Missing");
234         } catch (KeyStoreException e) {
235             exception = e;
236         }
237 
238         assertWithMessage("Did not hit a failure but expected one").that(exception).isNotNull();
239 
240         assertThat(exception.getMessage()).isEqualTo(EXPECTED_FAILURE);
241         assertThat(exception.getCause()).isInstanceOf(android.security.KeyStoreException.class);
242         android.security.KeyStoreException ksException =
243                 (android.security.KeyStoreException) exception.getCause();
244         assertThat(ksException.getNumericErrorCode()).isEqualTo(
245                 android.security.KeyStoreException.ERROR_KEY_DOES_NOT_EXIST);
246     }
247 
248     @Test
testKeyStore_ImportWrappedKey_3DES()249     public void testKeyStore_ImportWrappedKey_3DES() throws Exception {
250         testKeyStore_ImportWrappedKey_3DES(false);
251     }
252 
253     @Test
testKeyStore_ImportWrappedKey_3DES_StrongBox()254     public void testKeyStore_ImportWrappedKey_3DES_StrongBox() throws Exception {
255         testKeyStore_ImportWrappedKey_3DES(true);
256     }
257 
testKeyStore_ImportWrappedKey_3DES(boolean isStrongBox)258     public void testKeyStore_ImportWrappedKey_3DES(boolean isStrongBox) throws Exception {
259         if (isStrongBox) {
260             TestUtils.assumeStrongBox();
261         }
262 
263         assumeTrue("Can only test if device supports 3DES", TestUtils.supports3DES());
264 
265         KeyGenerator kg = KeyGenerator.getInstance("DESEDE");
266         kg.init(168);
267         Key swKey = kg.generateKey();
268 
269         byte[] keyMaterial = swKey.getEncoded();
270         byte[] mask = new byte[24]; // Zero mask
271 
272         try {
273             importWrappedKey(wrapKey(
274                     genKeyPair(WRAPPING_KEY_ALIAS, isStrongBox).getPublic(),
275                     keyMaterial,
276                     mask,
277                     KM_KEY_FORMAT_RAW,
278                     make3desAuthList(168)));
279         } catch (SecureKeyImportUnavailableException e) {
280             assumeNoException("Can only test if secure key import is available", e);
281         }
282 
283         // Use Key
284         KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
285         keyStore.load(null, null);
286 
287         assertTrue("Failed to load key after wrapped import", keyStore.containsAlias(ALIAS));
288 
289         Key importedKey = keyStore.getKey(ALIAS, null);
290         String plaintext = "hello, world";
291 
292         Cipher c = Cipher.getInstance("DESede/CBC/PKCS7Padding");
293         c.init(Cipher.ENCRYPT_MODE, importedKey);
294         IvParameterSpec paramSpec = new IvParameterSpec(c.getIV());
295         byte[] encrypted = c.doFinal(plaintext.getBytes());
296 
297         // Decrypt using key imported into keystore.
298         c = Cipher.getInstance("DESede/CBC/PKCS7Padding");
299         c.init(Cipher.DECRYPT_MODE, importedKey, paramSpec);
300         assertEquals(new String(c.doFinal(encrypted)), plaintext);
301 
302         // Decrypt using local software copy of the key.
303         c = Cipher.getInstance("DESede/CBC/PKCS7Padding");
304         c.init(Cipher.DECRYPT_MODE, swKey, paramSpec);
305         assertEquals(new String(c.doFinal(encrypted)), plaintext);
306     }
307 
308     @Test
testKeyStore_ImportWrappedKey_RSA()309     public void testKeyStore_ImportWrappedKey_RSA() throws Exception {
310         testKeyStore_ImportWrappedKey_RSA(false);
311     }
312 
313     @Test
testKeyStore_ImportWrappedKey_RSA_StrongBox()314     public void testKeyStore_ImportWrappedKey_RSA_StrongBox() throws Exception {
315         testKeyStore_ImportWrappedKey_RSA(true);
316     }
317 
testKeyStore_ImportWrappedKey_RSA(boolean isStrongBox)318     public void testKeyStore_ImportWrappedKey_RSA(boolean isStrongBox) throws Exception {
319         assumeTrue("Only VSR V+ KeyMint implementations are expected to pass.",
320                 TestUtils.getVendorApiLevel() >= 35);
321 
322         if (isStrongBox) {
323             TestUtils.assumeStrongBox();
324         }
325 
326         KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
327         // Both TEE and Strongbox must support 2048-bit keys.
328         int keySize = 2048;
329         kpg.initialize(keySize);
330         KeyPair kp = kpg.generateKeyPair();
331         PublicKey publicKey = kp.getPublic();
332         PrivateKey privateKey = kp.getPrivate();
333 
334         assertEquals(privateKey.getFormat(), "PKCS#8");
335 
336         byte[] keyMaterial = privateKey.getEncoded();
337         byte[] mask = new byte[32]; // Zero mask
338 
339         try {
340             importWrappedKey(wrapKey(
341                     genKeyPair(WRAPPING_KEY_ALIAS, isStrongBox).getPublic(),
342                     keyMaterial,
343                     mask,
344                     KM_KEY_FORMAT_PKCS8,
345                     makeRsaAuthList(keySize)));
346         } catch (SecureKeyImportUnavailableException e) {
347             assumeNoException("Can only test if secure key import is available", e);
348         }
349 
350         // Use Key
351         KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
352         keyStore.load(null, null);
353 
354         assertTrue("Failed to load key after wrapped import", keyStore.containsAlias(ALIAS));
355 
356         String plaintext = "hello, world";
357 
358         Key importedKey = keyStore.getKey(ALIAS, null);
359         assertTrue(importedKey instanceof PrivateKey);
360 
361         // Encrypt with KS private key, then decrypt with local public key.
362         Cipher c = Cipher.getInstance("RSA/ECB/PKCS1Padding");
363         c.init(Cipher.ENCRYPT_MODE, importedKey);
364         byte[] encrypted = c.doFinal(plaintext.getBytes());
365 
366         c.init(Cipher.DECRYPT_MODE, publicKey);
367         assertEquals(new String(c.doFinal(encrypted)), plaintext);
368 
369         // Encrypt with local public key, then decrypt with KS private key.
370         c.init(Cipher.ENCRYPT_MODE, publicKey);
371         encrypted = c.doFinal(plaintext.getBytes());
372 
373         c.init(Cipher.DECRYPT_MODE, importedKey);
374         assertEquals(new String(c.doFinal(encrypted)), plaintext);
375 
376         // Sign with KS private key, then verify with local public key.
377         Signature s = Signature.getInstance("SHA256withRSA");
378         s.initSign((PrivateKey) importedKey);
379         s.update(plaintext.getBytes());
380         byte[] signature = s.sign();
381 
382         s.initVerify(publicKey);
383         s.update(plaintext.getBytes());
384         assertTrue(s.verify(signature));
385     }
386 
387     @Test
testKeyStore_ImportWrappedKey_EC()388     public void testKeyStore_ImportWrappedKey_EC() throws Exception {
389         testKeyStore_ImportWrappedKey_EC(false);
390     }
391 
392     @Test
testKeyStore_ImportWrappedKey_EC_StrongBox()393     public void testKeyStore_ImportWrappedKey_EC_StrongBox() throws Exception {
394         testKeyStore_ImportWrappedKey_EC(true);
395     }
396 
testKeyStore_ImportWrappedKey_EC(boolean isStrongBox)397     public void testKeyStore_ImportWrappedKey_EC(boolean isStrongBox) throws Exception {
398         assumeTrue("Only VSR V+ KeyMint implementations are expected to pass.",
399                 TestUtils.getVendorApiLevel() >= 35);
400 
401         if (isStrongBox) {
402             TestUtils.assumeStrongBox();
403         }
404 
405         KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC");
406         // Both TEE and Strongbox must support P256 curve.
407         int keySize = 256;
408         kpg.initialize(keySize);
409         KeyPair kp = kpg.generateKeyPair();
410         PublicKey publicKey = kp.getPublic();
411         PrivateKey privateKey = kp.getPrivate();
412 
413         assertEquals(privateKey.getFormat(), "PKCS#8");
414 
415         byte[] keyMaterial = privateKey.getEncoded();
416         byte[] mask = new byte[32]; // Zero mask
417 
418         try {
419             importWrappedKey(wrapKey(
420                     genKeyPair(WRAPPING_KEY_ALIAS, isStrongBox).getPublic(),
421                     keyMaterial,
422                     mask,
423                     KM_KEY_FORMAT_PKCS8,
424                     makeEcAuthList(keySize)));
425         } catch (SecureKeyImportUnavailableException e) {
426             assumeNoException("Can only test if secure key import is available", e);
427         }
428 
429         // Use Key
430         KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
431         keyStore.load(null, null);
432 
433         assertTrue("Failed to load key after wrapped import", keyStore.containsAlias(ALIAS));
434 
435         String plaintext = "hello, world";
436 
437         Key importedKey = keyStore.getKey(ALIAS, null);
438         assertTrue(importedKey instanceof PrivateKey);
439 
440         // Sign with KS private key, then verify with local public key.
441         Signature s = Signature.getInstance("SHA256withECDSA");
442         s.initSign((PrivateKey) importedKey);
443         s.update(plaintext.getBytes());
444         byte[] signature = s.sign();
445 
446         s.initVerify(publicKey);
447         s.update(plaintext.getBytes());
448         assertTrue(s.verify(signature));
449     }
450 
importWrappedKey(byte[] wrappedKey, String wrappingKeyAlias)451     public void importWrappedKey(byte[] wrappedKey, String wrappingKeyAlias) throws Exception {
452         KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
453         keyStore.load(null, null);
454 
455         AlgorithmParameterSpec spec = new KeyGenParameterSpec.Builder(wrappingKeyAlias,
456                 KeyProperties.PURPOSE_WRAP_KEY)
457                 .setDigests(KeyProperties.DIGEST_SHA256)
458                 .build();
459         Entry wrappedKeyEntry = new WrappedKeyEntry(wrappedKey, wrappingKeyAlias,
460                   "RSA/ECB/OAEPPadding", spec);
461         keyStore.setEntry(ALIAS, wrappedKeyEntry, null);
462     }
463 
importWrappedKey(byte[] wrappedKey)464     public void importWrappedKey(byte[] wrappedKey) throws Exception {
465         importWrappedKey(wrappedKey, WRAPPING_KEY_ALIAS);
466     }
467 
wrapKey(PublicKey publicKey, byte[] keyMaterial, byte[] mask, int keyFormat, DERSequence authorizationList)468     public byte[] wrapKey(PublicKey publicKey, byte[] keyMaterial, byte[] mask,
469                           int keyFormat, DERSequence authorizationList) throws Exception {
470         return wrapKey(publicKey, keyMaterial, mask, keyFormat, authorizationList, true);
471     }
472 
wrapKey(PublicKey publicKey, byte[] keyMaterial, byte[] mask, int keyFormat, DERSequence authorizationList, boolean correctWrappingRequired)473     public byte[] wrapKey(PublicKey publicKey, byte[] keyMaterial, byte[] mask,
474             int keyFormat, DERSequence authorizationList, boolean correctWrappingRequired)
475             throws Exception {
476         // Build description
477         ASN1EncodableVector descriptionItems = new ASN1EncodableVector();
478         descriptionItems.add(new ASN1Integer(keyFormat));
479         descriptionItems.add(authorizationList);
480         DERSequence wrappedKeyDescription = new DERSequence(descriptionItems);
481 
482         // Generate 12 byte initialization vector
483         byte[] iv = new byte[12];
484         random.nextBytes(iv);
485 
486         // Generate 256 bit AES key. This is the ephemeral key used to encrypt the secure key.
487         byte[] aesKeyBytes = new byte[32];
488         random.nextBytes(aesKeyBytes);
489 
490         // Encrypt ephemeral keys
491         OAEPParameterSpec spec =
492                 new OAEPParameterSpec(
493                         "SHA-256", "MGF1", MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT);
494         Cipher pkCipher = Cipher.getInstance("RSA/ECB/OAEPPadding");
495         if (correctWrappingRequired) {
496             pkCipher.init(Cipher.ENCRYPT_MODE, publicKey, spec);
497         } else {
498             // Use incorrect OAEPParameters while initializing cipher. By default, main digest and
499             // MGF1 digest are SHA-1 here.
500             pkCipher.init(Cipher.ENCRYPT_MODE, publicKey);
501         }
502         byte[] encryptedEphemeralKeys = pkCipher.doFinal(aesKeyBytes);
503 
504         // Encrypt secure key
505         Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
506         SecretKeySpec secretKeySpec = new SecretKeySpec(aesKeyBytes, "AES");
507         GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_SIZE, iv);
508         cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, gcmParameterSpec);
509         byte[] aad = wrappedKeyDescription.getEncoded();
510 
511         cipher.updateAAD(aad);
512         byte[] encryptedSecureKey = cipher.doFinal(keyMaterial);
513         // Get GCM tag. Java puts the tag at the end of the ciphertext data :(
514         int len = encryptedSecureKey.length;
515         int tagSize = (GCM_TAG_SIZE / 8);
516         byte[] tag = Arrays.copyOfRange(encryptedSecureKey, len - tagSize, len);
517 
518         // Remove GCM tag from end of output
519         encryptedSecureKey = Arrays.copyOfRange(encryptedSecureKey, 0, len - tagSize);
520 
521         // Build ASN.1 encoded sequence WrappedKeyWrapper
522         ASN1EncodableVector items = new ASN1EncodableVector();
523         items.add(new ASN1Integer(WRAPPED_FORMAT_VERSION));
524         items.add(new DEROctetString(encryptedEphemeralKeys));
525         items.add(new DEROctetString(iv));
526         items.add(wrappedKeyDescription);
527         items.add(new DEROctetString(encryptedSecureKey));
528         items.add(new DEROctetString(tag));
529         return new DERSequence(items).getEncoded(ASN1Encoding.DER);
530     }
531 
makeSymKeyAuthList(int size, int algo)532     private DERSequence makeSymKeyAuthList(int size, int algo) {
533         ASN1EncodableVector allPurposes = new ASN1EncodableVector();
534         allPurposes.add(new ASN1Integer(KM_PURPOSE_ENCRYPT));
535         allPurposes.add(new ASN1Integer(KM_PURPOSE_DECRYPT));
536         DERSet purposeSet = new DERSet(allPurposes);
537         DERTaggedObject purpose =
538                 new DERTaggedObject(true, removeTagType(KM_TAG_PURPOSE), purposeSet);
539         DERTaggedObject algorithm =
540                 new DERTaggedObject(true, removeTagType(KM_TAG_ALGORITHM), new ASN1Integer(algo));
541         DERTaggedObject keySize =
542                 new DERTaggedObject(true, removeTagType(KM_TAG_KEY_SIZE), new ASN1Integer(size));
543 
544         ASN1EncodableVector allBlockModes = new ASN1EncodableVector();
545         allBlockModes.add(new ASN1Integer(KM_MODE_ECB));
546         allBlockModes.add(new ASN1Integer(KM_MODE_CBC));
547         DERSet blockModeSet = new DERSet(allBlockModes);
548         DERTaggedObject blockMode =
549                 new DERTaggedObject(true, removeTagType(KM_TAG_BLOCK_MODE), blockModeSet);
550 
551         ASN1EncodableVector allPaddings = new ASN1EncodableVector();
552         allPaddings.add(new ASN1Integer(KM_PAD_PKCS7));
553         allPaddings.add(new ASN1Integer(KM_PAD_NONE));
554         DERSet paddingSet = new DERSet(allPaddings);
555         DERTaggedObject padding =
556                 new DERTaggedObject(true, removeTagType(KM_TAG_PADDING), paddingSet);
557 
558         DERTaggedObject noAuthRequired =
559                 new DERTaggedObject(true, removeTagType(KM_TAG_NO_AUTH_REQUIRED), DERNull.INSTANCE);
560 
561         // Build sequence
562         ASN1EncodableVector allItems = new ASN1EncodableVector();
563         allItems.add(purpose);
564         allItems.add(algorithm);
565         allItems.add(keySize);
566         allItems.add(blockMode);
567         allItems.add(padding);
568         allItems.add(noAuthRequired);
569 
570         return new DERSequence(allItems);
571     }
572 
make3desAuthList(int size)573     private DERSequence make3desAuthList(int size) {
574         return makeSymKeyAuthList(size, KM_ALGORITHM_3DES);
575     }
576 
makeAesAuthList(int size)577     private DERSequence makeAesAuthList(int size) {
578         return makeSymKeyAuthList(size, KM_ALGORITHM_AES);
579     }
580 
makeRsaAuthList(int size)581     private DERSequence makeRsaAuthList(int size) {
582         ASN1EncodableVector allPurposes = new ASN1EncodableVector();
583         allPurposes.add(new ASN1Integer(KM_PURPOSE_ENCRYPT));
584         allPurposes.add(new ASN1Integer(KM_PURPOSE_DECRYPT));
585         allPurposes.add(new ASN1Integer(KM_PURPOSE_SIGN));
586         allPurposes.add(new ASN1Integer(KM_PURPOSE_VERIFY));
587         DERSet purposeSet = new DERSet(allPurposes);
588         DERTaggedObject purpose =
589                 new DERTaggedObject(true, removeTagType(KM_TAG_PURPOSE), purposeSet);
590 
591         DERTaggedObject algorithm =
592                 new DERTaggedObject(true, removeTagType(KM_TAG_ALGORITHM),
593                                     new ASN1Integer(KM_ALGORITHM_RSA));
594         DERTaggedObject keySize =
595                 new DERTaggedObject(true, removeTagType(KM_TAG_KEY_SIZE), new ASN1Integer(size));
596 
597         ASN1EncodableVector allDigests = new ASN1EncodableVector();
598         allDigests.add(new ASN1Integer(KM_DIGEST_NONE));
599         allDigests.add(new ASN1Integer(KM_DIGEST_MD5));
600         allDigests.add(new ASN1Integer(KM_DIGEST_SHA1));
601         allDigests.add(new ASN1Integer(KM_DIGEST_SHA_2_224));
602         allDigests.add(new ASN1Integer(KM_DIGEST_SHA_2_256));
603         allDigests.add(new ASN1Integer(KM_DIGEST_SHA_2_384));
604         allDigests.add(new ASN1Integer(KM_DIGEST_SHA_2_512));
605         DERSet digestSet = new DERSet(allDigests);
606         DERTaggedObject digest =
607                 new DERTaggedObject(true, removeTagType(KM_TAG_DIGEST), digestSet);
608 
609         ASN1EncodableVector allPaddings = new ASN1EncodableVector();
610         allPaddings.add(new ASN1Integer(KM_PAD_PKCS7));
611         allPaddings.add(new ASN1Integer(KM_PAD_NONE));
612         allPaddings.add(new ASN1Integer(KM_PAD_RSA_OAEP));
613         allPaddings.add(new ASN1Integer(KM_PAD_RSA_PSS));
614         allPaddings.add(new ASN1Integer(KM_PAD_RSA_PKCS1_1_5_ENCRYPT));
615         allPaddings.add(new ASN1Integer(KM_PAD_RSA_PKCS1_1_5_SIGN));
616         DERSet paddingSet = new DERSet(allPaddings);
617         DERTaggedObject padding =
618                 new DERTaggedObject(true, removeTagType(KM_TAG_PADDING), paddingSet);
619 
620         DERTaggedObject noAuthRequired =
621                 new DERTaggedObject(true, removeTagType(KM_TAG_NO_AUTH_REQUIRED), DERNull.INSTANCE);
622 
623         // Build sequence
624         ASN1EncodableVector allItems = new ASN1EncodableVector();
625         allItems.add(purpose);
626         allItems.add(algorithm);
627         allItems.add(keySize);
628         allItems.add(digest);
629         allItems.add(padding);
630         allItems.add(noAuthRequired);
631 
632         return new DERSequence(allItems);
633     }
634 
makeEcAuthList(int size)635     private DERSequence makeEcAuthList(int size) {
636         ASN1EncodableVector allPurposes = new ASN1EncodableVector();
637         allPurposes.add(new ASN1Integer(KM_PURPOSE_SIGN));
638         allPurposes.add(new ASN1Integer(KM_PURPOSE_VERIFY));
639         DERSet purposeSet = new DERSet(allPurposes);
640         DERTaggedObject purpose =
641                 new DERTaggedObject(true, removeTagType(KM_TAG_PURPOSE), purposeSet);
642 
643         DERTaggedObject algorithm =
644                 new DERTaggedObject(true, removeTagType(KM_TAG_ALGORITHM),
645                                     new ASN1Integer(KM_ALGORITHM_EC));
646         DERTaggedObject keySize =
647                 new DERTaggedObject(true, removeTagType(KM_TAG_KEY_SIZE), new ASN1Integer(size));
648 
649         ASN1EncodableVector allDigests = new ASN1EncodableVector();
650         allDigests.add(new ASN1Integer(KM_DIGEST_SHA_2_224));
651         allDigests.add(new ASN1Integer(KM_DIGEST_SHA_2_256));
652         allDigests.add(new ASN1Integer(KM_DIGEST_SHA_2_384));
653         allDigests.add(new ASN1Integer(KM_DIGEST_SHA_2_512));
654         DERSet digestSet = new DERSet(allDigests);
655         DERTaggedObject digest =
656                 new DERTaggedObject(true, removeTagType(KM_TAG_DIGEST), digestSet);
657 
658         DERTaggedObject noAuthRequired =
659                 new DERTaggedObject(true, removeTagType(KM_TAG_NO_AUTH_REQUIRED), DERNull.INSTANCE);
660 
661         // Build sequence
662         ASN1EncodableVector allItems = new ASN1EncodableVector();
663         allItems.add(purpose);
664         allItems.add(algorithm);
665         allItems.add(keySize);
666         allItems.add(digest);
667         allItems.add(noAuthRequired);
668 
669         return new DERSequence(allItems);
670     }
671 
genKeyPair(String alias, boolean isStrongBoxBacked)672     private KeyPair genKeyPair(String alias, boolean isStrongBoxBacked) throws Exception {
673         KeyPairGenerator kpg =
674                 KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore");
675         kpg.initialize(
676                 new KeyGenParameterSpec.Builder(alias, KeyProperties.PURPOSE_WRAP_KEY)
677                         .setDigests(KeyProperties.DIGEST_SHA256)
678                         .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP)
679                         .setBlockModes(KeyProperties.BLOCK_MODE_ECB)
680                         .setIsStrongBoxBacked(isStrongBoxBacked)
681                         .build());
682         return kpg.generateKeyPair();
683     }
684 }
685