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_PURPOSE;
45 import static android.security.keymaster.KeymasterDefs.KM_TAG_ALGORITHM;
46 import static android.security.keymaster.KeymasterDefs.KM_TAG_KEY_SIZE;
47 import static android.security.keymaster.KeymasterDefs.KM_TAG_BLOCK_MODE;
48 import static android.security.keymaster.KeymasterDefs.KM_TAG_DIGEST;
49 import static android.security.keymaster.KeymasterDefs.KM_TAG_PADDING;
50 import static android.security.keymaster.KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED;
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.DEREncodableVector;
73 import org.bouncycastle.asn1.DERInteger;
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(wrapKey(
205                     genKeyPair(WRAPPING_KEY_ALIAS, isStrongBox).getPublic(),
206                     keyMaterial,
207                     mask,
208                     KM_KEY_FORMAT_RAW,
209                     makeAesAuthList(keyMaterial.length * 8),
210                     false /* incorrect wrapping required*/));
211         } catch (SecureKeyImportUnavailableException e) {
212             assumeNoException("Can only test if secure key import is available", e);
213         } catch (KeyStoreException e) {
214             exception = e;
215         }
216         assertWithMessage("Did not hit a failure but expected one").that(exception).isNotNull();
217         assertThat(exception.getCause()).isInstanceOf(android.security.KeyStoreException.class);
218         android.security.KeyStoreException ksException =
219                 (android.security.KeyStoreException) exception.getCause();
220         assertFalse("Importing incorrectly wrapped key should not cause transient failure in"
221                     + " Key{Mint/Master}. That means performing same operation will fail always.",
222                         ksException.isTransientFailure());
223     }
224 
225     @Test
testKeyStore_ImportWrappedKeyWrappingKeyMissing()226     public void testKeyStore_ImportWrappedKeyWrappingKeyMissing() throws Exception {
227         final String EXPECTED_FAILURE = "Failed to import wrapped key. Keystore error code: 7";
228         KeyStoreException exception = null;
229 
230         try {
231             byte [] fakeWrappedKey = new byte[1];
232             importWrappedKey(fakeWrappedKey, WRAPPING_KEY_ALIAS + "_Missing");
233         } catch (KeyStoreException e) {
234             exception = e;
235         }
236 
237         assertWithMessage("Did not hit a failure but expected one").that(exception).isNotNull();
238 
239         assertThat(exception.getMessage()).isEqualTo(EXPECTED_FAILURE);
240         assertThat(exception.getCause()).isInstanceOf(android.security.KeyStoreException.class);
241         android.security.KeyStoreException ksException =
242                 (android.security.KeyStoreException) exception.getCause();
243         assertThat(ksException.getNumericErrorCode()).isEqualTo(
244                 android.security.KeyStoreException.ERROR_KEY_DOES_NOT_EXIST);
245     }
246 
247     @Test
testKeyStore_ImportWrappedKey_3DES()248     public void testKeyStore_ImportWrappedKey_3DES() throws Exception {
249         testKeyStore_ImportWrappedKey_3DES(false);
250     }
251 
252     @Test
testKeyStore_ImportWrappedKey_3DES_StrongBox()253     public void testKeyStore_ImportWrappedKey_3DES_StrongBox() throws Exception {
254         testKeyStore_ImportWrappedKey_3DES(true);
255     }
256 
testKeyStore_ImportWrappedKey_3DES(boolean isStrongBox)257     public void testKeyStore_ImportWrappedKey_3DES(boolean isStrongBox) throws Exception {
258         if (isStrongBox) {
259             TestUtils.assumeStrongBox();
260         }
261 
262         assumeTrue("Can only test if device supports 3DES", TestUtils.supports3DES());
263 
264         KeyGenerator kg = KeyGenerator.getInstance("DESEDE");
265         kg.init(168);
266         Key swKey = kg.generateKey();
267 
268         byte[] keyMaterial = swKey.getEncoded();
269         byte[] mask = new byte[24]; // Zero mask
270 
271         try {
272             importWrappedKey(wrapKey(
273                     genKeyPair(WRAPPING_KEY_ALIAS, isStrongBox).getPublic(),
274                     keyMaterial,
275                     mask,
276                     KM_KEY_FORMAT_RAW,
277                     make3desAuthList(168)));
278         } catch (SecureKeyImportUnavailableException e) {
279             assumeNoException("Can only test if secure key import is available", e);
280         }
281 
282         // Use Key
283         KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
284         keyStore.load(null, null);
285 
286         assertTrue("Failed to load key after wrapped import", keyStore.containsAlias(ALIAS));
287 
288         Key importedKey = keyStore.getKey(ALIAS, null);
289         String plaintext = "hello, world";
290 
291         Cipher c = Cipher.getInstance("DESede/CBC/PKCS7Padding");
292         c.init(Cipher.ENCRYPT_MODE, importedKey);
293         IvParameterSpec paramSpec = new IvParameterSpec(c.getIV());
294         byte[] encrypted = c.doFinal(plaintext.getBytes());
295 
296         // Decrypt using key imported into keystore.
297         c = Cipher.getInstance("DESede/CBC/PKCS7Padding");
298         c.init(Cipher.DECRYPT_MODE, importedKey, paramSpec);
299         assertEquals(new String(c.doFinal(encrypted)), plaintext);
300 
301         // Decrypt using local software copy of the key.
302         c = Cipher.getInstance("DESede/CBC/PKCS7Padding");
303         c.init(Cipher.DECRYPT_MODE, swKey, paramSpec);
304         assertEquals(new String(c.doFinal(encrypted)), plaintext);
305     }
306 
307     @Test
testKeyStore_ImportWrappedKey_RSA()308     public void testKeyStore_ImportWrappedKey_RSA() throws Exception {
309         testKeyStore_ImportWrappedKey_RSA(false);
310     }
311 
312     @Test
testKeyStore_ImportWrappedKey_RSA_StrongBox()313     public void testKeyStore_ImportWrappedKey_RSA_StrongBox() throws Exception {
314         testKeyStore_ImportWrappedKey_RSA(true);
315     }
316 
testKeyStore_ImportWrappedKey_RSA(boolean isStrongBox)317     public void testKeyStore_ImportWrappedKey_RSA(boolean isStrongBox) throws Exception {
318         assumeTrue("Only VSR V+ KeyMint implementations are expected to pass.",
319                 TestUtils.getVendorApiLevel() >= 35);
320 
321         if (isStrongBox) {
322             TestUtils.assumeStrongBox();
323         }
324 
325         KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
326         // Both TEE and Strongbox must support 2048-bit keys.
327         int keySize = 2048;
328         kpg.initialize(keySize);
329         KeyPair kp = kpg.generateKeyPair();
330         PublicKey publicKey = kp.getPublic();
331         PrivateKey privateKey = kp.getPrivate();
332 
333         assertEquals(privateKey.getFormat(), "PKCS#8");
334 
335         byte[] keyMaterial = privateKey.getEncoded();
336         byte[] mask = new byte[32]; // Zero mask
337 
338         try {
339             importWrappedKey(wrapKey(
340                     genKeyPair(WRAPPING_KEY_ALIAS, isStrongBox).getPublic(),
341                     keyMaterial,
342                     mask,
343                     KM_KEY_FORMAT_PKCS8,
344                     makeRsaAuthList(keySize)));
345         } catch (SecureKeyImportUnavailableException e) {
346             assumeNoException("Can only test if secure key import is available", e);
347         }
348 
349         // Use Key
350         KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
351         keyStore.load(null, null);
352 
353         assertTrue("Failed to load key after wrapped import", keyStore.containsAlias(ALIAS));
354 
355         String plaintext = "hello, world";
356 
357         Key importedKey = keyStore.getKey(ALIAS, null);
358         assertTrue(importedKey instanceof PrivateKey);
359 
360         // Encrypt with KS private key, then decrypt with local public key.
361         Cipher c = Cipher.getInstance("RSA/ECB/PKCS1Padding");
362         c.init(Cipher.ENCRYPT_MODE, importedKey);
363         byte[] encrypted = c.doFinal(plaintext.getBytes());
364 
365         c.init(Cipher.DECRYPT_MODE, publicKey);
366         assertEquals(new String(c.doFinal(encrypted)), plaintext);
367 
368         // Encrypt with local public key, then decrypt with KS private key.
369         c.init(Cipher.ENCRYPT_MODE, publicKey);
370         encrypted = c.doFinal(plaintext.getBytes());
371 
372         c.init(Cipher.DECRYPT_MODE, importedKey);
373         assertEquals(new String(c.doFinal(encrypted)), plaintext);
374 
375         // Sign with KS private key, then verify with local public key.
376         Signature s = Signature.getInstance("SHA256withRSA");
377         s.initSign((PrivateKey) importedKey);
378         s.update(plaintext.getBytes());
379         byte[] signature = s.sign();
380 
381         s.initVerify(publicKey);
382         s.update(plaintext.getBytes());
383         assertTrue(s.verify(signature));
384     }
385 
386     @Test
testKeyStore_ImportWrappedKey_EC()387     public void testKeyStore_ImportWrappedKey_EC() throws Exception {
388         testKeyStore_ImportWrappedKey_EC(false);
389     }
390 
391     @Test
testKeyStore_ImportWrappedKey_EC_StrongBox()392     public void testKeyStore_ImportWrappedKey_EC_StrongBox() throws Exception {
393         testKeyStore_ImportWrappedKey_EC(true);
394     }
395 
testKeyStore_ImportWrappedKey_EC(boolean isStrongBox)396     public void testKeyStore_ImportWrappedKey_EC(boolean isStrongBox) throws Exception {
397         assumeTrue("Only VSR V+ KeyMint implementations are expected to pass.",
398                 TestUtils.getVendorApiLevel() >= 35);
399 
400         if (isStrongBox) {
401             TestUtils.assumeStrongBox();
402         }
403 
404         KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC");
405         // Both TEE and Strongbox must support P256 curve.
406         int keySize = 256;
407         kpg.initialize(keySize);
408         KeyPair kp = kpg.generateKeyPair();
409         PublicKey publicKey = kp.getPublic();
410         PrivateKey privateKey = kp.getPrivate();
411 
412         assertEquals(privateKey.getFormat(), "PKCS#8");
413 
414         byte[] keyMaterial = privateKey.getEncoded();
415         byte[] mask = new byte[32]; // Zero mask
416 
417         try {
418             importWrappedKey(wrapKey(
419                     genKeyPair(WRAPPING_KEY_ALIAS, isStrongBox).getPublic(),
420                     keyMaterial,
421                     mask,
422                     KM_KEY_FORMAT_PKCS8,
423                     makeEcAuthList(keySize)));
424         } catch (SecureKeyImportUnavailableException e) {
425             assumeNoException("Can only test if secure key import is available", e);
426         }
427 
428         // Use Key
429         KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
430         keyStore.load(null, null);
431 
432         assertTrue("Failed to load key after wrapped import", keyStore.containsAlias(ALIAS));
433 
434         String plaintext = "hello, world";
435 
436         Key importedKey = keyStore.getKey(ALIAS, null);
437         assertTrue(importedKey instanceof PrivateKey);
438 
439         // Sign with KS private key, then verify with local public key.
440         Signature s = Signature.getInstance("SHA256withECDSA");
441         s.initSign((PrivateKey) importedKey);
442         s.update(plaintext.getBytes());
443         byte[] signature = s.sign();
444 
445         s.initVerify(publicKey);
446         s.update(plaintext.getBytes());
447         assertTrue(s.verify(signature));
448     }
449 
importWrappedKey(byte[] wrappedKey, String wrappingKeyAlias)450     public void importWrappedKey(byte[] wrappedKey, String wrappingKeyAlias) throws Exception {
451         KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
452         keyStore.load(null, null);
453 
454         AlgorithmParameterSpec spec = new KeyGenParameterSpec.Builder(wrappingKeyAlias,
455                 KeyProperties.PURPOSE_WRAP_KEY)
456                 .setDigests(KeyProperties.DIGEST_SHA256)
457                 .build();
458         Entry wrappedKeyEntry = new WrappedKeyEntry(wrappedKey, wrappingKeyAlias,
459                   "RSA/ECB/OAEPPadding", spec);
460         keyStore.setEntry(ALIAS, wrappedKeyEntry, null);
461     }
462 
importWrappedKey(byte[] wrappedKey)463     public void importWrappedKey(byte[] wrappedKey) throws Exception {
464         importWrappedKey(wrappedKey, WRAPPING_KEY_ALIAS);
465     }
466 
wrapKey(PublicKey publicKey, byte[] keyMaterial, byte[] mask, int keyFormat, DERSequence authorizationList)467     public byte[] wrapKey(PublicKey publicKey, byte[] keyMaterial, byte[] mask,
468                           int keyFormat, DERSequence authorizationList) throws Exception {
469         return wrapKey(publicKey, keyMaterial, mask, keyFormat, authorizationList, true);
470     }
471 
wrapKey(PublicKey publicKey, byte[] keyMaterial, byte[] mask, int keyFormat, DERSequence authorizationList, boolean correctWrappingRequired)472     public byte[] wrapKey(PublicKey publicKey, byte[] keyMaterial, byte[] mask,
473             int keyFormat, DERSequence authorizationList, boolean correctWrappingRequired)
474             throws Exception {
475         // Build description
476         DEREncodableVector descriptionItems = new DEREncodableVector();
477         descriptionItems.add(new DERInteger(keyFormat));
478         descriptionItems.add(authorizationList);
479         DERSequence wrappedKeyDescription = new DERSequence(descriptionItems);
480 
481         // Generate 12 byte initialization vector
482         byte[] iv = new byte[12];
483         random.nextBytes(iv);
484 
485         // Generate 256 bit AES key. This is the ephemeral key used to encrypt the secure key.
486         byte[] aesKeyBytes = new byte[32];
487         random.nextBytes(aesKeyBytes);
488 
489         // Encrypt ephemeral keys
490         OAEPParameterSpec spec = new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT);
491         Cipher pkCipher = Cipher.getInstance("RSA/ECB/OAEPPadding");
492         if (correctWrappingRequired) {
493             pkCipher.init(Cipher.ENCRYPT_MODE, publicKey, spec);
494         } else {
495             // Use incorrect OAEPParameters while initializing cipher. By default, main digest and
496             // MGF1 digest are SHA-1 here.
497             pkCipher.init(Cipher.ENCRYPT_MODE, publicKey);
498         }
499         byte[] encryptedEphemeralKeys = pkCipher.doFinal(aesKeyBytes);
500 
501         // Encrypt secure key
502         Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
503         SecretKeySpec secretKeySpec = new SecretKeySpec(aesKeyBytes, "AES");
504         GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_SIZE, iv);
505         cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, gcmParameterSpec);
506         byte[] aad = wrappedKeyDescription.getEncoded();
507 
508         cipher.updateAAD(aad);
509         byte[] encryptedSecureKey = cipher.doFinal(keyMaterial);
510         // Get GCM tag. Java puts the tag at the end of the ciphertext data :(
511         int len = encryptedSecureKey.length;
512         int tagSize = (GCM_TAG_SIZE / 8);
513         byte[] tag = Arrays.copyOfRange(encryptedSecureKey, len - tagSize, len);
514 
515         // Remove GCM tag from end of output
516         encryptedSecureKey = Arrays.copyOfRange(encryptedSecureKey, 0, len - tagSize);
517 
518         // Build ASN.1 DER encoded sequence WrappedKeyWrapper
519         DEREncodableVector items = new DEREncodableVector();
520         items.add(new DERInteger(WRAPPED_FORMAT_VERSION));
521         items.add(new DEROctetString(encryptedEphemeralKeys));
522         items.add(new DEROctetString(iv));
523         items.add(wrappedKeyDescription);
524         items.add(new DEROctetString(encryptedSecureKey));
525         items.add(new DEROctetString(tag));
526         return new DERSequence(items).getEncoded(ASN1Encoding.DER);
527     }
528 
makeSymKeyAuthList(int size, int algo)529     private DERSequence makeSymKeyAuthList(int size, int algo) {
530         DEREncodableVector allPurposes = new DEREncodableVector();
531         allPurposes.add(new DERInteger(KM_PURPOSE_ENCRYPT));
532         allPurposes.add(new DERInteger(KM_PURPOSE_DECRYPT));
533         DERSet purposeSet = new DERSet(allPurposes);
534         DERTaggedObject purpose =
535                 new DERTaggedObject(true, removeTagType(KM_TAG_PURPOSE), purposeSet);
536         DERTaggedObject algorithm =
537                 new DERTaggedObject(true, removeTagType(KM_TAG_ALGORITHM), new DERInteger(algo));
538         DERTaggedObject keySize =
539                 new DERTaggedObject(true, removeTagType(KM_TAG_KEY_SIZE), new DERInteger(size));
540 
541         DEREncodableVector allBlockModes = new DEREncodableVector();
542         allBlockModes.add(new DERInteger(KM_MODE_ECB));
543         allBlockModes.add(new DERInteger(KM_MODE_CBC));
544         DERSet blockModeSet = new DERSet(allBlockModes);
545         DERTaggedObject blockMode =
546                 new DERTaggedObject(true, removeTagType(KM_TAG_BLOCK_MODE), blockModeSet);
547 
548         DEREncodableVector allPaddings = new DEREncodableVector();
549         allPaddings.add(new DERInteger(KM_PAD_PKCS7));
550         allPaddings.add(new DERInteger(KM_PAD_NONE));
551         DERSet paddingSet = new DERSet(allPaddings);
552         DERTaggedObject padding =
553                 new DERTaggedObject(true, removeTagType(KM_TAG_PADDING), paddingSet);
554 
555         DERTaggedObject noAuthRequired =
556                 new DERTaggedObject(true, removeTagType(KM_TAG_NO_AUTH_REQUIRED), DERNull.INSTANCE);
557 
558         // Build sequence
559         DEREncodableVector allItems = new DEREncodableVector();
560         allItems.add(purpose);
561         allItems.add(algorithm);
562         allItems.add(keySize);
563         allItems.add(blockMode);
564         allItems.add(padding);
565         allItems.add(noAuthRequired);
566 
567         return new DERSequence(allItems);
568     }
569 
make3desAuthList(int size)570     private DERSequence make3desAuthList(int size) {
571         return makeSymKeyAuthList(size, KM_ALGORITHM_3DES);
572     }
573 
makeAesAuthList(int size)574     private DERSequence makeAesAuthList(int size) {
575         return makeSymKeyAuthList(size, KM_ALGORITHM_AES);
576     }
577 
makeRsaAuthList(int size)578     private DERSequence makeRsaAuthList(int size) {
579         DEREncodableVector allPurposes = new DEREncodableVector();
580         allPurposes.add(new DERInteger(KM_PURPOSE_ENCRYPT));
581         allPurposes.add(new DERInteger(KM_PURPOSE_DECRYPT));
582         allPurposes.add(new DERInteger(KM_PURPOSE_SIGN));
583         allPurposes.add(new DERInteger(KM_PURPOSE_VERIFY));
584         DERSet purposeSet = new DERSet(allPurposes);
585         DERTaggedObject purpose =
586                 new DERTaggedObject(true, removeTagType(KM_TAG_PURPOSE), purposeSet);
587 
588         DERTaggedObject algorithm =
589                 new DERTaggedObject(true, removeTagType(KM_TAG_ALGORITHM),
590                                     new DERInteger(KM_ALGORITHM_RSA));
591         DERTaggedObject keySize =
592                 new DERTaggedObject(true, removeTagType(KM_TAG_KEY_SIZE), new DERInteger(size));
593 
594         DEREncodableVector allDigests = new DEREncodableVector();
595         allDigests.add(new DERInteger(KM_DIGEST_NONE));
596         allDigests.add(new DERInteger(KM_DIGEST_MD5));
597         allDigests.add(new DERInteger(KM_DIGEST_SHA1));
598         allDigests.add(new DERInteger(KM_DIGEST_SHA_2_224));
599         allDigests.add(new DERInteger(KM_DIGEST_SHA_2_256));
600         allDigests.add(new DERInteger(KM_DIGEST_SHA_2_384));
601         allDigests.add(new DERInteger(KM_DIGEST_SHA_2_512));
602         DERSet digestSet = new DERSet(allDigests);
603         DERTaggedObject digest =
604                 new DERTaggedObject(true, removeTagType(KM_TAG_DIGEST), digestSet);
605 
606         DEREncodableVector allPaddings = new DEREncodableVector();
607         allPaddings.add(new DERInteger(KM_PAD_PKCS7));
608         allPaddings.add(new DERInteger(KM_PAD_NONE));
609         allPaddings.add(new DERInteger(KM_PAD_RSA_OAEP));
610         allPaddings.add(new DERInteger(KM_PAD_RSA_PSS));
611         allPaddings.add(new DERInteger(KM_PAD_RSA_PKCS1_1_5_ENCRYPT));
612         allPaddings.add(new DERInteger(KM_PAD_RSA_PKCS1_1_5_SIGN));
613         DERSet paddingSet = new DERSet(allPaddings);
614         DERTaggedObject padding =
615                 new DERTaggedObject(true, removeTagType(KM_TAG_PADDING), paddingSet);
616 
617         DERTaggedObject noAuthRequired =
618                 new DERTaggedObject(true, removeTagType(KM_TAG_NO_AUTH_REQUIRED), DERNull.INSTANCE);
619 
620         // Build sequence
621         DEREncodableVector allItems = new DEREncodableVector();
622         allItems.add(purpose);
623         allItems.add(algorithm);
624         allItems.add(keySize);
625         allItems.add(digest);
626         allItems.add(padding);
627         allItems.add(noAuthRequired);
628 
629         return new DERSequence(allItems);
630     }
631 
makeEcAuthList(int size)632     private DERSequence makeEcAuthList(int size) {
633         DEREncodableVector allPurposes = new DEREncodableVector();
634         allPurposes.add(new DERInteger(KM_PURPOSE_SIGN));
635         allPurposes.add(new DERInteger(KM_PURPOSE_VERIFY));
636         DERSet purposeSet = new DERSet(allPurposes);
637         DERTaggedObject purpose =
638                 new DERTaggedObject(true, removeTagType(KM_TAG_PURPOSE), purposeSet);
639 
640         DERTaggedObject algorithm =
641                 new DERTaggedObject(true, removeTagType(KM_TAG_ALGORITHM),
642                                     new DERInteger(KM_ALGORITHM_EC));
643         DERTaggedObject keySize =
644                 new DERTaggedObject(true, removeTagType(KM_TAG_KEY_SIZE), new DERInteger(size));
645 
646         DEREncodableVector allDigests = new DEREncodableVector();
647         allDigests.add(new DERInteger(KM_DIGEST_SHA_2_224));
648         allDigests.add(new DERInteger(KM_DIGEST_SHA_2_256));
649         allDigests.add(new DERInteger(KM_DIGEST_SHA_2_384));
650         allDigests.add(new DERInteger(KM_DIGEST_SHA_2_512));
651         DERSet digestSet = new DERSet(allDigests);
652         DERTaggedObject digest =
653                 new DERTaggedObject(true, removeTagType(KM_TAG_DIGEST), digestSet);
654 
655         DERTaggedObject noAuthRequired =
656                 new DERTaggedObject(true, removeTagType(KM_TAG_NO_AUTH_REQUIRED), DERNull.INSTANCE);
657 
658         // Build sequence
659         DEREncodableVector allItems = new DEREncodableVector();
660         allItems.add(purpose);
661         allItems.add(algorithm);
662         allItems.add(keySize);
663         allItems.add(digest);
664         allItems.add(noAuthRequired);
665 
666         return new DERSequence(allItems);
667     }
668 
genKeyPair(String alias, boolean isStrongBoxBacked)669     private KeyPair genKeyPair(String alias, boolean isStrongBoxBacked) throws Exception {
670         KeyPairGenerator kpg =
671                 KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore");
672         kpg.initialize(
673                 new KeyGenParameterSpec.Builder(alias, KeyProperties.PURPOSE_WRAP_KEY)
674                         .setDigests(KeyProperties.DIGEST_SHA256)
675                         .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP)
676                         .setBlockModes(KeyProperties.BLOCK_MODE_ECB)
677                         .setIsStrongBoxBacked(isStrongBoxBacked)
678                         .build());
679         return kpg.generateKeyPair();
680     }
681 }
682