1 /*
2  * Copyright (C) 2012 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.security.keystore2;
18 
19 import static android.security.keystore2.AndroidKeyStoreCipherSpiBase.DEFAULT_MGF1_DIGEST;
20 
21 import android.annotation.NonNull;
22 import android.hardware.biometrics.BiometricManager;
23 import android.hardware.security.keymint.EcCurve;
24 import android.hardware.security.keymint.HardwareAuthenticatorType;
25 import android.hardware.security.keymint.KeyParameter;
26 import android.hardware.security.keymint.SecurityLevel;
27 import android.os.StrictMode;
28 import android.security.Flags;
29 import android.security.GateKeeper;
30 import android.security.KeyStore2;
31 import android.security.KeyStoreParameter;
32 import android.security.KeyStoreSecurityLevel;
33 import android.security.keymaster.KeymasterDefs;
34 import android.security.keystore.KeyGenParameterSpec;
35 import android.security.keystore.KeyPermanentlyInvalidatedException;
36 import android.security.keystore.KeyProperties;
37 import android.security.keystore.KeyProtection;
38 import android.security.keystore.SecureKeyImportUnavailableException;
39 import android.security.keystore.WrappedKeyEntry;
40 import android.system.keystore2.AuthenticatorSpec;
41 import android.system.keystore2.Authorization;
42 import android.system.keystore2.Domain;
43 import android.system.keystore2.IKeystoreSecurityLevel;
44 import android.system.keystore2.KeyDescriptor;
45 import android.system.keystore2.KeyEntryResponse;
46 import android.system.keystore2.KeyMetadata;
47 import android.system.keystore2.ResponseCode;
48 import android.util.Log;
49 
50 import com.android.internal.annotations.VisibleForTesting;
51 
52 import java.io.ByteArrayInputStream;
53 import java.io.IOException;
54 import java.io.InputStream;
55 import java.io.OutputStream;
56 import java.security.Key;
57 import java.security.KeyStore.Entry;
58 import java.security.KeyStore.LoadStoreParameter;
59 import java.security.KeyStore.PrivateKeyEntry;
60 import java.security.KeyStore.ProtectionParameter;
61 import java.security.KeyStore.SecretKeyEntry;
62 import java.security.KeyStoreException;
63 import java.security.KeyStoreSpi;
64 import java.security.NoSuchAlgorithmException;
65 import java.security.PrivateKey;
66 import java.security.ProviderException;
67 import java.security.UnrecoverableKeyException;
68 import java.security.cert.Certificate;
69 import java.security.cert.CertificateEncodingException;
70 import java.security.cert.CertificateException;
71 import java.security.cert.CertificateFactory;
72 import java.security.cert.X509Certificate;
73 import java.security.interfaces.ECKey;
74 import java.security.interfaces.ECPrivateKey;
75 import java.security.interfaces.EdECKey;
76 import java.security.interfaces.EdECPrivateKey;
77 import java.security.interfaces.XECKey;
78 import java.security.interfaces.XECPrivateKey;
79 import java.security.spec.AlgorithmParameterSpec;
80 import java.security.spec.ECParameterSpec;
81 import java.security.spec.NamedParameterSpec;
82 import java.util.ArrayList;
83 import java.util.Arrays;
84 import java.util.Collection;
85 import java.util.Date;
86 import java.util.Enumeration;
87 import java.util.Iterator;
88 import java.util.List;
89 import java.util.NoSuchElementException;
90 
91 import javax.crypto.SecretKey;
92 
93 /**
94  * A java.security.KeyStore interface for the Android KeyStore. An instance of
95  * it can be created via the {@link java.security.KeyStore#getInstance(String)
96  * KeyStore.getInstance("AndroidKeyStore")} interface. This returns a
97  * java.security.KeyStore backed by this "AndroidKeyStore" implementation.
98  * <p>
99  * This is built on top of Android's keystore daemon. The convention of alias
100  * use is:
101  * <p>
102  * PrivateKeyEntry will have a Credentials.USER_PRIVATE_KEY as the private key,
103  * Credentials.USER_CERTIFICATE as the first certificate in the chain (the one
104  * that corresponds to the private key), and then a Credentials.CA_CERTIFICATE
105  * entry which will have the rest of the chain concatenated in BER format.
106  * <p>
107  * TrustedCertificateEntry will just have a Credentials.CA_CERTIFICATE entry
108  * with a single certificate.
109  *
110  * @hide
111  */
112 public class AndroidKeyStoreSpi extends KeyStoreSpi {
113     public static final String TAG = "AndroidKeyStoreSpi";
114     public static final String NAME = "AndroidKeyStore";
115 
116     private KeyStore2 mKeyStore;
117     private @KeyProperties.Namespace int mNamespace = KeyProperties.NAMESPACE_APPLICATION;
118 
119     @Override
engineGetKey(String alias, char[] password)120     public Key engineGetKey(String alias, char[] password) throws NoSuchAlgorithmException,
121             UnrecoverableKeyException {
122         try {
123             return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore(mKeyStore,
124                                                                               alias,
125                                                                               mNamespace);
126         } catch (KeyPermanentlyInvalidatedException e) {
127             throw new UnrecoverableKeyException(e.getMessage());
128         } catch (UnrecoverableKeyException e) {
129             Throwable cause = e.getCause();
130             if (cause instanceof android.security.KeyStoreException) {
131                 if (((android.security.KeyStoreException) cause).getErrorCode()
132                         == ResponseCode.KEY_NOT_FOUND) {
133                     return null;
134                 }
135             }
136             throw e;
137         }
138     }
139 
140     /**
141      * Make a key descriptor from the given alias and the mNamespace member.
142      * If mNamespace is -1 it sets the domain field to {@link Domain#APP} and {@link Domain#SELINUX}
143      * otherwise. The blob field is always set to null and the alias field to {@code alias}
144      * @param alias The alias of the new key descriptor.
145      * @return A new key descriptor.
146      */
makeKeyDescriptor(@onNull String alias)147     private KeyDescriptor makeKeyDescriptor(@NonNull String alias) {
148         KeyDescriptor descriptor = new KeyDescriptor();
149         descriptor.domain = getTargetDomain();
150         descriptor.nspace = mNamespace; // ignored if Domain.App;
151         descriptor.alias = alias;
152         descriptor.blob = null;
153         return descriptor;
154     }
155 
getTargetDomain()156     private @Domain int getTargetDomain() {
157         return mNamespace == KeyProperties.NAMESPACE_APPLICATION
158                 ? Domain.APP
159                 : Domain.SELINUX;
160     }
getKeyMetadata(String alias)161     private KeyEntryResponse getKeyMetadata(String alias) {
162         if (alias == null) {
163             throw new NullPointerException("alias == null");
164         }
165 
166         KeyDescriptor descriptor = makeKeyDescriptor(alias);
167 
168         try {
169             StrictMode.noteDiskRead();
170             return mKeyStore.getKeyEntry(descriptor);
171         } catch (android.security.KeyStoreException e) {
172             if (e.getErrorCode() != ResponseCode.KEY_NOT_FOUND) {
173                 Log.w(TAG, "Could not get key metadata from Keystore.", e);
174             }
175             return null;
176         }
177     }
178 
179     @Override
engineGetCertificateChain(String alias)180     public Certificate[] engineGetCertificateChain(String alias) {
181         KeyEntryResponse response = getKeyMetadata(alias);
182 
183         if (response == null || response.metadata.certificate == null) {
184             return null;
185         }
186 
187         final X509Certificate leaf = (X509Certificate) toCertificate(response.metadata.certificate);
188         if (leaf == null) {
189             return null;
190         }
191 
192         final Certificate[] caList;
193 
194         final byte[] caBytes = response.metadata.certificateChain;
195 
196         if (caBytes != null) {
197             final Collection<X509Certificate> caChain = toCertificates(caBytes);
198 
199             caList = new Certificate[caChain.size() + 1];
200 
201             final Iterator<X509Certificate> it = caChain.iterator();
202             int i = 1;
203             while (it.hasNext()) {
204                 caList[i++] = it.next();
205             }
206         } else {
207             caList = new Certificate[1];
208         }
209 
210         caList[0] = leaf;
211 
212         return caList;
213     }
214 
215     @Override
engineGetCertificate(String alias)216     public Certificate engineGetCertificate(String alias) {
217         KeyEntryResponse response = getKeyMetadata(alias);
218 
219         if (response == null) {
220             return null;
221         }
222 
223         byte[] encodedCert = response.metadata.certificate;
224         if (encodedCert != null) {
225             return toCertificate(encodedCert);
226         }
227 
228         encodedCert = response.metadata.certificateChain;
229         if (encodedCert != null) {
230             return toCertificate(encodedCert);
231         }
232 
233         // This entry/alias does not contain a certificate.
234         return null;
235     }
236 
toCertificate(byte[] bytes)237     static X509Certificate toCertificate(byte[] bytes) {
238         try {
239             final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
240             return (X509Certificate) certFactory.generateCertificate(
241                     new ByteArrayInputStream(bytes));
242         } catch (CertificateException e) {
243             Log.w(NAME, "Couldn't parse certificate in keystore", e);
244             return null;
245         }
246     }
247 
248     @SuppressWarnings("unchecked")
toCertificates(byte[] bytes)249     private static Collection<X509Certificate> toCertificates(byte[] bytes) {
250         try {
251             final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
252             return (Collection<X509Certificate>) certFactory.generateCertificates(
253                             new ByteArrayInputStream(bytes));
254         } catch (CertificateException e) {
255             Log.w(NAME, "Couldn't parse certificates in keystore", e);
256             return new ArrayList<X509Certificate>();
257         }
258     }
259 
getMgf1DigestSetterFlag()260     private static boolean getMgf1DigestSetterFlag() {
261         try {
262             return Flags.mgf1DigestSetterV2();
263         } catch (SecurityException e) {
264             Log.w(NAME, "Cannot read MGF1 Digest setter flag value", e);
265             return false;
266         }
267     }
268 
269     @Override
engineGetCreationDate(String alias)270     public Date engineGetCreationDate(String alias) {
271         KeyEntryResponse response = getKeyMetadata(alias);
272 
273         if (response == null) {
274             return null;
275         }
276 
277         if (response.metadata.modificationTimeMs == -1) {
278             return null;
279         }
280         return new Date(response.metadata.modificationTimeMs);
281     }
282 
283     @Override
engineSetKeyEntry(String alias, Key key, char[] password, Certificate[] chain)284     public void engineSetKeyEntry(String alias, Key key, char[] password, Certificate[] chain)
285             throws KeyStoreException {
286         if ((password != null) && (password.length > 0)) {
287             throw new KeyStoreException("entries cannot be protected with passwords");
288         }
289 
290         if (key instanceof PrivateKey) {
291             setPrivateKeyEntry(alias, (PrivateKey) key, chain, null);
292         } else if (key instanceof SecretKey) {
293             setSecretKeyEntry(alias, (SecretKey) key, null);
294         } else {
295             throw new KeyStoreException("Only PrivateKey and SecretKey are supported");
296         }
297     }
298 
getLegacyKeyProtectionParameter(PrivateKey key)299     private static KeyProtection getLegacyKeyProtectionParameter(PrivateKey key)
300             throws KeyStoreException {
301         String keyAlgorithm = key.getAlgorithm();
302         KeyProtection.Builder specBuilder;
303         if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) {
304             specBuilder =
305                     new KeyProtection.Builder(
306                             KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY);
307             // Authorized to be used with any digest (including no digest).
308             // MD5 was never offered for Android Keystore for ECDSA.
309             specBuilder.setDigests(
310                     KeyProperties.DIGEST_NONE,
311                     KeyProperties.DIGEST_SHA1,
312                     KeyProperties.DIGEST_SHA224,
313                     KeyProperties.DIGEST_SHA256,
314                     KeyProperties.DIGEST_SHA384,
315                     KeyProperties.DIGEST_SHA512);
316         } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) {
317             specBuilder =
318                     new KeyProtection.Builder(
319                             KeyProperties.PURPOSE_ENCRYPT
320                             | KeyProperties.PURPOSE_DECRYPT
321                             | KeyProperties.PURPOSE_SIGN
322                             | KeyProperties.PURPOSE_VERIFY);
323             // Authorized to be used with any digest (including no digest).
324             specBuilder.setDigests(
325                     KeyProperties.DIGEST_NONE,
326                     KeyProperties.DIGEST_MD5,
327                     KeyProperties.DIGEST_SHA1,
328                     KeyProperties.DIGEST_SHA224,
329                     KeyProperties.DIGEST_SHA256,
330                     KeyProperties.DIGEST_SHA384,
331                     KeyProperties.DIGEST_SHA512);
332             // Authorized to be used with any encryption and signature padding
333             // schemes (including no padding).
334             specBuilder.setEncryptionPaddings(
335                     KeyProperties.ENCRYPTION_PADDING_NONE,
336                     KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1,
337                     KeyProperties.ENCRYPTION_PADDING_RSA_OAEP);
338             specBuilder.setSignaturePaddings(
339                     KeyProperties.SIGNATURE_PADDING_RSA_PKCS1,
340                     KeyProperties.SIGNATURE_PADDING_RSA_PSS);
341             // Disable randomized encryption requirement to support encryption
342             // padding NONE above.
343             specBuilder.setRandomizedEncryptionRequired(false);
344         } else {
345             throw new KeyStoreException("Unsupported key algorithm: " + keyAlgorithm);
346         }
347         specBuilder.setUserAuthenticationRequired(false);
348 
349         return specBuilder.build();
350     }
351 
setPrivateKeyEntry(String alias, PrivateKey key, Certificate[] chain, java.security.KeyStore.ProtectionParameter param)352     private void setPrivateKeyEntry(String alias, PrivateKey key, Certificate[] chain,
353             java.security.KeyStore.ProtectionParameter param) throws KeyStoreException {
354         @SecurityLevel int securitylevel = SecurityLevel.TRUSTED_ENVIRONMENT;
355         int flags = 0;
356         KeyProtection spec;
357         if (param == null) {
358             spec = getLegacyKeyProtectionParameter(key);
359         } else if (param instanceof KeyStoreParameter) {
360             spec = getLegacyKeyProtectionParameter(key);
361             KeyStoreParameter legacySpec = (KeyStoreParameter) param;
362         } else if (param instanceof KeyProtection) {
363             spec = (KeyProtection) param;
364             if (spec.isCriticalToDeviceEncryption()) {
365                 // This key is should not be bound to the LSKF even if it is auth bound.
366                 // This indicates that this key is used in the derivation for of the
367                 // master key, that is used for the LSKF binding of other auth bound
368                 // keys. This breaks up a circular dependency while retaining logical
369                 // authentication binding of the key.
370                 flags |= IKeystoreSecurityLevel
371                         .KEY_FLAG_AUTH_BOUND_WITHOUT_CRYPTOGRAPHIC_LSKF_BINDING;
372             }
373 
374             if (spec.isStrongBoxBacked()) {
375                 securitylevel = SecurityLevel.STRONGBOX;
376             }
377         } else {
378             throw new KeyStoreException(
379                     "Unsupported protection parameter class:" + param.getClass().getName()
380                     + ". Supported: " + KeyProtection.class.getName() + ", "
381                     + KeyStoreParameter.class.getName());
382         }
383 
384         // Make sure the chain exists since this is a PrivateKey
385         if ((chain == null) || (chain.length == 0)) {
386             throw new KeyStoreException("Must supply at least one Certificate with PrivateKey");
387         }
388 
389         // Do chain type checking.
390         X509Certificate[] x509chain = new X509Certificate[chain.length];
391         for (int i = 0; i < chain.length; i++) {
392             if (!"X.509".equals(chain[i].getType())) {
393                 throw new KeyStoreException("Certificates must be in X.509 format: invalid cert #"
394                         + i);
395             }
396 
397             if (!(chain[i] instanceof X509Certificate)) {
398                 throw new KeyStoreException("Certificates must be in X.509 format: invalid cert #"
399                         + i);
400             }
401 
402             x509chain[i] = (X509Certificate) chain[i];
403         }
404 
405         final byte[] userCertBytes;
406         try {
407             userCertBytes = x509chain[0].getEncoded();
408         } catch (CertificateEncodingException e) {
409             throw new KeyStoreException("Failed to encode certificate #0", e);
410         }
411 
412         /*
413          * If we have a chain, store it in the CA certificate slot for this
414          * alias as concatenated DER-encoded certificates. These can be
415          * deserialized by {@link CertificateFactory#generateCertificates}.
416          */
417         final byte[] chainBytes;
418         if (chain.length > 1) {
419             /*
420              * The chain is passed in as {user_cert, ca_cert_1, ca_cert_2, ...}
421              * so we only need the certificates starting at index 1.
422              */
423             final byte[][] certsBytes = new byte[x509chain.length - 1][];
424             int totalCertLength = 0;
425             for (int i = 0; i < certsBytes.length; i++) {
426                 try {
427                     certsBytes[i] = x509chain[i + 1].getEncoded();
428                     totalCertLength += certsBytes[i].length;
429                 } catch (CertificateEncodingException e) {
430                     throw new KeyStoreException("Failed to encode certificate #" + i, e);
431                 }
432             }
433 
434             /*
435              * Serialize this into one byte array so we can later call
436              * CertificateFactory#generateCertificates to recover them.
437              */
438             chainBytes = new byte[totalCertLength];
439             int outputOffset = 0;
440             for (int i = 0; i < certsBytes.length; i++) {
441                 final int certLength = certsBytes[i].length;
442                 System.arraycopy(certsBytes[i], 0, chainBytes, outputOffset, certLength);
443                 outputOffset += certLength;
444                 certsBytes[i] = null;
445             }
446         } else {
447             chainBytes = null;
448         }
449 
450         @Domain int targetDomain = getTargetDomain();
451 
452         // If the given key is an AndroidKeyStorePrivateKey, we attempt to update
453         // its subcomponents with the given certificate and certificate chain.
454         if (key instanceof AndroidKeyStorePrivateKey) {
455             AndroidKeyStoreKey ksKey = (AndroidKeyStoreKey) key;
456             KeyDescriptor descriptor = ksKey.getUserKeyDescriptor();
457 
458             // This throws if the request cannot replace the entry.
459             assertCanReplace(alias, targetDomain, mNamespace, descriptor);
460 
461             try {
462                 StrictMode.noteDiskWrite();
463                 mKeyStore.updateSubcomponents(
464                         ((AndroidKeyStorePrivateKey) key).getKeyIdDescriptor(),
465                         userCertBytes, chainBytes);
466             } catch (android.security.KeyStoreException e) {
467                 throw new KeyStoreException("Failed to store certificate and certificate chain", e);
468             }
469             return;
470         }
471 
472         // Make sure the PrivateKey format is the one we support.
473         final String keyFormat = key.getFormat();
474         if ((keyFormat == null) || (!"PKCS#8".equals(keyFormat))) {
475             throw new KeyStoreException(
476                     "Unsupported private key export format: " + keyFormat
477                     + ". Only private keys which export their key material in PKCS#8 format are"
478                     + " supported.");
479         }
480 
481         // Make sure we can actually encode the key.
482         byte[] pkcs8EncodedPrivateKeyBytes = key.getEncoded();
483         if (pkcs8EncodedPrivateKeyBytes == null) {
484             throw new KeyStoreException("Private key did not export any key material");
485         }
486 
487         final List<KeyParameter> importArgs = new ArrayList<>();
488 
489         try {
490             importArgs.add(KeyStore2ParameterUtils.makeEnum(
491                     KeymasterDefs.KM_TAG_ALGORITHM,
492                     KeyProperties.KeyAlgorithm.toKeymasterAsymmetricKeyAlgorithm(
493                             key.getAlgorithm()))
494             );
495             KeyStore2ParameterUtils.forEachSetFlag(spec.getPurposes(), (purpose) -> {
496                 importArgs.add(KeyStore2ParameterUtils.makeEnum(
497                         KeymasterDefs.KM_TAG_PURPOSE,
498                         KeyProperties.Purpose.toKeymaster(purpose)
499                 ));
500             });
501             if (spec.isDigestsSpecified()) {
502                 for (String digest : spec.getDigests()) {
503                     importArgs.add(KeyStore2ParameterUtils.makeEnum(
504                             KeymasterDefs.KM_TAG_DIGEST,
505                             KeyProperties.Digest.toKeymaster(digest)
506                     ));
507                 }
508             }
509             for (String blockMode : spec.getBlockModes()) {
510                 importArgs.add(KeyStore2ParameterUtils.makeEnum(
511                         KeymasterDefs.KM_TAG_BLOCK_MODE,
512                         KeyProperties.BlockMode.toKeymaster(blockMode)
513                 ));
514             }
515             int[] keymasterEncryptionPaddings =
516                     KeyProperties.EncryptionPadding.allToKeymaster(
517                             spec.getEncryptionPaddings());
518             if (((spec.getPurposes() & KeyProperties.PURPOSE_ENCRYPT) != 0)
519                     && (spec.isRandomizedEncryptionRequired())) {
520                 for (int keymasterPadding : keymasterEncryptionPaddings) {
521                     if (!KeymasterUtils
522                             .isKeymasterPaddingSchemeIndCpaCompatibleWithAsymmetricCrypto(
523                                     keymasterPadding)) {
524                         throw new KeyStoreException(
525                                 "Randomized encryption (IND-CPA) required but is violated by"
526                                         + " encryption padding mode: "
527                                         + KeyProperties.EncryptionPadding.fromKeymaster(
528                                         keymasterPadding)
529                                         + ". See KeyProtection documentation.");
530                     }
531                 }
532             }
533             for (int padding : keymasterEncryptionPaddings) {
534                 importArgs.add(KeyStore2ParameterUtils.makeEnum(
535                         KeymasterDefs.KM_TAG_PADDING,
536                         padding
537                 ));
538                 if (padding == KeymasterDefs.KM_PAD_RSA_OAEP) {
539                     if (spec.isMgf1DigestsSpecified()) {
540                         for (String mgf1Digest : spec.getMgf1Digests()) {
541                             importArgs.add(KeyStore2ParameterUtils.makeEnum(
542                                     KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST,
543                                     KeyProperties.Digest.toKeymaster(mgf1Digest)
544                             ));
545                         }
546                     } else {
547                         /* Because of default MGF1 digest is SHA-1. It has to be added in Key
548                          * characteristics. Otherwise, crypto operations will fail with Incompatible
549                          * MGF1 digest.
550                          * If the MGF1 Digest setter flag isn't set, then the condition in the
551                          * if clause above must be false (cannot have MGF1 digests specified if the
552                          * flag was off). In that case, in addition to adding the default MGF1
553                          * digest, we have to add all the other digests as MGF1 Digests.
554                          *
555                          */
556                         importArgs.add(KeyStore2ParameterUtils.makeEnum(
557                                 KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST,
558                                 KeyProperties.Digest.toKeymaster(DEFAULT_MGF1_DIGEST)
559                         ));
560                         if (!getMgf1DigestSetterFlag()) {
561                             final int defaultMgf1Digest = KeyProperties.Digest.toKeymaster(
562                                     DEFAULT_MGF1_DIGEST);
563                             for (String digest : spec.getDigests()) {
564                                 int digestToAddAsMgf1Digest = KeyProperties.Digest.toKeymaster(
565                                         digest);
566                                 // Do not add the default MGF1 digest as it has been added above.
567                                 if (digestToAddAsMgf1Digest != defaultMgf1Digest) {
568                                     importArgs.add(KeyStore2ParameterUtils.makeEnum(
569                                             KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST,
570                                             digestToAddAsMgf1Digest
571                                     ));
572                                 }
573                             }
574                         }
575                     }
576                 }
577             }
578             for (String padding : spec.getSignaturePaddings()) {
579                 importArgs.add(KeyStore2ParameterUtils.makeEnum(
580                         KeymasterDefs.KM_TAG_PADDING,
581                         KeyProperties.SignaturePadding.toKeymaster(padding)
582                 ));
583             }
584             KeyStore2ParameterUtils.addUserAuthArgs(importArgs, spec);
585             if (spec.getKeyValidityStart() != null) {
586                 importArgs.add(KeyStore2ParameterUtils.makeDate(
587                         KeymasterDefs.KM_TAG_ACTIVE_DATETIME, spec.getKeyValidityStart()
588                 ));
589             }
590             if (spec.getKeyValidityForOriginationEnd() != null) {
591                 importArgs.add(KeyStore2ParameterUtils.makeDate(
592                         KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
593                         spec.getKeyValidityForOriginationEnd()
594                 ));
595             }
596             if (spec.getKeyValidityForConsumptionEnd() != null) {
597                 importArgs.add(KeyStore2ParameterUtils.makeDate(
598                         KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME,
599                         spec.getKeyValidityForConsumptionEnd()
600                 ));
601             }
602             if (spec.getMaxUsageCount() != KeyProperties.UNRESTRICTED_USAGE_COUNT) {
603                 importArgs.add(KeyStore2ParameterUtils.makeInt(
604                         KeymasterDefs.KM_TAG_USAGE_COUNT_LIMIT,
605                         spec.getMaxUsageCount()
606                 ));
607             }
608             if (KeymasterDefs.KM_ALGORITHM_EC
609                     == KeyProperties.KeyAlgorithm.toKeymasterAsymmetricKeyAlgorithm(
610                             key.getAlgorithm())) {
611                 importArgs.add(KeyStore2ParameterUtils.makeEnum(
612                         KeymasterDefs.KM_TAG_EC_CURVE,
613                         getKeymasterEcCurve(key)
614                 ));
615             }
616         } catch (IllegalArgumentException | IllegalStateException e) {
617             throw new KeyStoreException(e);
618         }
619 
620         try {
621             KeyStoreSecurityLevel securityLevelInterface = mKeyStore.getSecurityLevel(
622                     securitylevel);
623 
624             KeyDescriptor descriptor = makeKeyDescriptor(alias);
625 
626             KeyMetadata metadata = securityLevelInterface.importKey(descriptor, null,
627                     importArgs, flags, pkcs8EncodedPrivateKeyBytes);
628 
629             try {
630                 StrictMode.noteDiskWrite();
631                 mKeyStore.updateSubcomponents(metadata.key, userCertBytes, chainBytes);
632             } catch (android.security.KeyStoreException e) {
633                 mKeyStore.deleteKey(metadata.key);
634                 throw new KeyStoreException("Failed to store certificate and certificate chain", e);
635             }
636 
637         } catch (android.security.KeyStoreException e) {
638             throw new KeyStoreException("Failed to store private key", e);
639         }
640     }
641 
getKeymasterEcCurve(PrivateKey key)642     private int getKeymasterEcCurve(PrivateKey key) {
643         if (key instanceof ECKey) {
644             ECParameterSpec param = ((ECPrivateKey) key).getParams();
645             int kmECCurve = KeymasterUtils.getKeymasterEcCurve(KeymasterUtils.getCurveName(param));
646             if (kmECCurve >= 0) {
647                 return kmECCurve;
648             }
649         } else if (key instanceof XECKey) {
650             AlgorithmParameterSpec param = ((XECPrivateKey) key).getParams();
651             if (param.equals(NamedParameterSpec.X25519)) {
652                 return EcCurve.CURVE_25519;
653             }
654         } else if (key.getAlgorithm().equals("XDH")) {
655             // TODO com.android.org.conscrypt.OpenSSLX25519PrivateKey does not implement XECKey,
656             //  this case is not required once it implements XECKey interface(b/214203951).
657             return EcCurve.CURVE_25519;
658         } else if (key instanceof EdECKey) {
659             AlgorithmParameterSpec param = ((EdECPrivateKey) key).getParams();
660             if (param.equals(NamedParameterSpec.ED25519)) {
661                 return EcCurve.CURVE_25519;
662             }
663         }
664         throw new IllegalArgumentException("Unexpected Key " + key.getClass().getName());
665     }
666 
assertCanReplace(String alias, @Domain int targetDomain, int targetNamespace, KeyDescriptor descriptor)667     private static void assertCanReplace(String alias, @Domain int targetDomain,
668             int targetNamespace, KeyDescriptor descriptor)
669             throws KeyStoreException {
670         // If
671         //  * the alias does not match, or
672         //  * the domain does not match, or
673         //  * the domain is Domain.SELINUX and the namespaces don not match,
674         // then the designated key location is not equivalent to the location of the
675         // given key parameter and cannot be updated.
676         //
677         // Note: mNamespace == KeyProperties.NAMESPACE_APPLICATION implies that the target domain
678         // is Domain.APP and Domain.SELINUX is the target domain otherwise.
679         if (!alias.equals(descriptor.alias)
680                 || descriptor.domain != targetDomain
681                 || (descriptor.domain == Domain.SELINUX && descriptor.nspace != targetNamespace)) {
682             throw new KeyStoreException("Can only replace keys with same alias: " + alias
683                     + " != " + descriptor.alias + " in the same target domain: " + targetDomain
684                     + " != " + descriptor.domain
685                     + (targetDomain == Domain.SELINUX ? " in the same target namespace: "
686                     + targetNamespace + " != " + descriptor.nspace : "")
687             );
688         }
689     }
690 
setSecretKeyEntry(String alias, SecretKey key, java.security.KeyStore.ProtectionParameter param)691     private void setSecretKeyEntry(String alias, SecretKey key,
692             java.security.KeyStore.ProtectionParameter param)
693             throws KeyStoreException {
694         if ((param != null) && (!(param instanceof KeyProtection))) {
695             throw new KeyStoreException(
696                     "Unsupported protection parameter class: " + param.getClass().getName()
697                     + ". Supported: " + KeyProtection.class.getName());
698         }
699         KeyProtection params = (KeyProtection) param;
700 
701         @Domain int targetDomain = (getTargetDomain());
702 
703         if (key instanceof AndroidKeyStoreSecretKey) {
704             String keyAliasInKeystore =
705                     ((AndroidKeyStoreSecretKey) key).getUserKeyDescriptor().alias;
706 
707             KeyDescriptor descriptor = ((AndroidKeyStoreSecretKey) key).getUserKeyDescriptor();
708 
709             // This throws if the request cannot replace the existing key.
710             assertCanReplace(alias, targetDomain, mNamespace, descriptor);
711 
712             // This is the entry where this key is already stored. No need to do anything.
713             if (params != null) {
714                 throw new KeyStoreException("Modifying KeyStore-backed key using protection"
715                         + " parameters not supported");
716             }
717             return;
718         }
719 
720         if (params == null) {
721             throw new KeyStoreException(
722                     "Protection parameters must be specified when importing a symmetric key");
723         }
724 
725         // Not a KeyStore-backed secret key -- import its key material into keystore.
726         String keyExportFormat = key.getFormat();
727         if (keyExportFormat == null) {
728             throw new KeyStoreException(
729                     "Only secret keys that export their key material are supported");
730         } else if (!"RAW".equals(keyExportFormat)) {
731             throw new KeyStoreException(
732                     "Unsupported secret key material export format: " + keyExportFormat);
733         }
734         byte[] keyMaterial = key.getEncoded();
735         if (keyMaterial == null) {
736             throw new KeyStoreException("Key did not export its key material despite supporting"
737                     + " RAW format export");
738         }
739 
740         final List<KeyParameter> importArgs = new ArrayList<>();
741 
742         try {
743             int keymasterAlgorithm =
744                     KeyProperties.KeyAlgorithm.toKeymasterSecretKeyAlgorithm(
745                             key.getAlgorithm());
746 
747             importArgs.add(KeyStore2ParameterUtils.makeEnum(
748                     KeymasterDefs.KM_TAG_ALGORITHM,
749                     keymasterAlgorithm
750             ));
751 
752             if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) {
753                 // JCA HMAC key algorithm implies a digest (e.g., HmacSHA256 key algorithm
754                 // implies SHA-256 digest). Because keymaster HMAC key is authorized only for one
755                 // digest, we don't let import parameters override the digest implied by the key.
756                 // If the parameters specify digests at all, they must specify only one digest, the
757                 // only implied by key algorithm.
758                 int keymasterImpliedDigest =
759                         KeyProperties.KeyAlgorithm.toKeymasterDigest(key.getAlgorithm());
760                 if (keymasterImpliedDigest == -1) {
761                     throw new ProviderException(
762                             "HMAC key algorithm digest unknown for key algorithm "
763                                     + key.getAlgorithm());
764                 }
765 
766                 if (params.isDigestsSpecified()) {
767                     // Digest(s) explicitly specified in params -- check that the list consists of
768                     // exactly one digest, the one implied by key algorithm.
769                     int[] keymasterDigestsFromParams =
770                             KeyProperties.Digest.allToKeymaster(params.getDigests());
771                     if ((keymasterDigestsFromParams.length != 1)
772                             || (keymasterDigestsFromParams[0] != keymasterImpliedDigest)) {
773                         throw new KeyStoreException(
774                                 "Unsupported digests specification: "
775                                         + Arrays.asList(params.getDigests()) + ". Only "
776                                         + KeyProperties.Digest.fromKeymaster(keymasterImpliedDigest)
777                                         + " supported for HMAC key algorithm "
778                                         + key.getAlgorithm());
779                     }
780                 }
781                 int outputBits = KeymasterUtils.getDigestOutputSizeBits(keymasterImpliedDigest);
782                 if (outputBits == -1) {
783                     throw new ProviderException(
784                             "HMAC key authorized for unsupported digest: "
785                                     + KeyProperties.Digest.fromKeymaster(keymasterImpliedDigest));
786                 }
787                 importArgs.add(KeyStore2ParameterUtils.makeEnum(
788                         KeymasterDefs.KM_TAG_DIGEST, keymasterImpliedDigest
789                 ));
790                 importArgs.add(KeyStore2ParameterUtils.makeInt(
791                         KeymasterDefs.KM_TAG_MIN_MAC_LENGTH, outputBits
792                 ));
793             } else {
794                 if (params.isDigestsSpecified()) {
795                     for (String digest : params.getDigests()) {
796                         importArgs.add(KeyStore2ParameterUtils.makeEnum(
797                                 KeymasterDefs.KM_TAG_DIGEST,
798                                 KeyProperties.Digest.toKeymaster(digest)
799                         ));
800                     }
801                 }
802             }
803 
804             KeyStore2ParameterUtils.forEachSetFlag(params.getPurposes(), (purpose) -> {
805                 importArgs.add(KeyStore2ParameterUtils.makeEnum(
806                         KeymasterDefs.KM_TAG_PURPOSE,
807                         KeyProperties.Purpose.toKeymaster(purpose)
808                 ));
809             });
810 
811             boolean indCpa = false;
812             if ((params.getPurposes() & KeyProperties.PURPOSE_ENCRYPT) != 0) {
813                 if (((KeyProtection) param).isRandomizedEncryptionRequired()) {
814                     indCpa = true;
815                 } else {
816                     importArgs.add(KeyStore2ParameterUtils.makeBool(
817                             KeymasterDefs.KM_TAG_CALLER_NONCE
818                     ));
819                 }
820             }
821 
822             for (String blockMode : params.getBlockModes()) {
823                 int keymasterBlockMode = KeyProperties.BlockMode.toKeymaster(blockMode);
824                 if (indCpa
825                         && !KeymasterUtils.isKeymasterBlockModeIndCpaCompatibleWithSymmetricCrypto(
826                                 keymasterBlockMode)) {
827                     throw new KeyStoreException(
828                             "Randomized encryption (IND-CPA) required but may be violated by"
829                                     + " block mode: " + blockMode
830                                     + ". See KeyProtection documentation.");
831 
832                 }
833                 if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_AES
834                         && keymasterBlockMode == KeymasterDefs.KM_MODE_GCM) {
835                     importArgs.add(KeyStore2ParameterUtils.makeInt(
836                             KeymasterDefs.KM_TAG_MIN_MAC_LENGTH,
837                             AndroidKeyStoreAuthenticatedAESCipherSpi.GCM
838                                     .MIN_SUPPORTED_TAG_LENGTH_BITS
839                     ));
840                 }
841                 importArgs.add(KeyStore2ParameterUtils.makeEnum(
842                         KeymasterDefs.KM_TAG_BLOCK_MODE,
843                         keymasterBlockMode
844                 ));
845             }
846 
847             if (params.getSignaturePaddings().length > 0) {
848                 throw new KeyStoreException("Signature paddings not supported for symmetric keys");
849             }
850 
851             for (String padding : params.getEncryptionPaddings()) {
852                 importArgs.add(KeyStore2ParameterUtils.makeEnum(
853                         KeymasterDefs.KM_TAG_PADDING,
854                         KeyProperties.EncryptionPadding.toKeymaster(padding)
855                 ));
856             }
857 
858             KeyStore2ParameterUtils.addUserAuthArgs(importArgs, params);
859 
860             if (params.getKeyValidityStart() != null) {
861                 importArgs.add(KeyStore2ParameterUtils.makeDate(
862                         KeymasterDefs.KM_TAG_ACTIVE_DATETIME, params.getKeyValidityStart()
863                 ));
864             }
865             if (params.getKeyValidityForOriginationEnd() != null) {
866                 importArgs.add(KeyStore2ParameterUtils.makeDate(
867                         KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
868                         params.getKeyValidityForOriginationEnd()
869                 ));
870             }
871             if (params.getKeyValidityForConsumptionEnd() != null) {
872                 importArgs.add(KeyStore2ParameterUtils.makeDate(
873                         KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME,
874                         params.getKeyValidityForConsumptionEnd()
875                 ));
876             }
877             if (params.getMaxUsageCount() != KeyProperties.UNRESTRICTED_USAGE_COUNT) {
878                 importArgs.add(KeyStore2ParameterUtils.makeInt(
879                         KeymasterDefs.KM_TAG_USAGE_COUNT_LIMIT,
880                         params.getMaxUsageCount()
881                 ));
882             }
883 
884             if (params.isRollbackResistant()) {
885                 importArgs.add(KeyStore2ParameterUtils.makeBool(
886                         KeymasterDefs.KM_TAG_ROLLBACK_RESISTANT
887                 ));
888             }
889         } catch (IllegalArgumentException | IllegalStateException e) {
890             throw new KeyStoreException(e);
891         }
892 
893         int flags = 0;
894         if (params.isCriticalToDeviceEncryption()) {
895             flags |= IKeystoreSecurityLevel.KEY_FLAG_AUTH_BOUND_WITHOUT_CRYPTOGRAPHIC_LSKF_BINDING;
896         }
897 
898         @SecurityLevel int securityLevel = params.isStrongBoxBacked() ? SecurityLevel.STRONGBOX :
899                 SecurityLevel.TRUSTED_ENVIRONMENT;
900 
901         try {
902             KeyStoreSecurityLevel securityLevelInterface = mKeyStore.getSecurityLevel(
903                     securityLevel);
904 
905             KeyDescriptor descriptor = makeKeyDescriptor(alias);
906 
907             securityLevelInterface.importKey(descriptor, null /* TODO attestationKey */,
908                     importArgs, flags, keyMaterial);
909         } catch (android.security.KeyStoreException e) {
910             throw new KeyStoreException("Failed to import secret key.", e);
911         }
912     }
913 
setWrappedKeyEntry(String alias, WrappedKeyEntry entry, java.security.KeyStore.ProtectionParameter param)914     private void setWrappedKeyEntry(String alias, WrappedKeyEntry entry,
915             java.security.KeyStore.ProtectionParameter param) throws KeyStoreException {
916         if (param != null) {
917             throw new KeyStoreException("Protection parameters are specified inside wrapped keys");
918         }
919 
920         byte[] maskingKey = new byte[32];
921 
922         String[] parts = entry.getTransformation().split("/");
923 
924         List<KeyParameter> args = new ArrayList<>();
925 
926         String algorithm = parts[0];
927         if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(algorithm)) {
928             args.add(KeyStore2ParameterUtils.makeEnum(
929                     KeymasterDefs.KM_TAG_ALGORITHM,
930                     KeymasterDefs.KM_ALGORITHM_RSA
931             ));
932         } else {
933             throw new KeyStoreException("Algorithm \"" + algorithm + "\" not supported for "
934                     + "wrapping. Only RSA wrapping keys are supported.");
935         }
936 
937         if (parts.length > 1) {
938             String mode = parts[1];
939             args.add(KeyStore2ParameterUtils.makeEnum(
940                     KeymasterDefs.KM_TAG_BLOCK_MODE,
941                     KeyProperties.BlockMode.toKeymaster(mode)
942             ));
943         }
944 
945         if (parts.length > 2) {
946             @KeyProperties.EncryptionPaddingEnum int padding =
947                     KeyProperties.EncryptionPadding.toKeymaster(parts[2]);
948             if (padding != KeymasterDefs.KM_PAD_NONE) {
949                 args.add(KeyStore2ParameterUtils.makeEnum(
950                         KeymasterDefs.KM_TAG_PADDING,
951                         padding
952                 ));
953             }
954         }
955 
956         KeyGenParameterSpec spec = (KeyGenParameterSpec) entry.getAlgorithmParameterSpec();
957         if (spec.isDigestsSpecified()) {
958             @KeyProperties.DigestEnum int digest =
959                     KeyProperties.Digest.toKeymaster(spec.getDigests()[0]);
960             if (digest != KeymasterDefs.KM_DIGEST_NONE) {
961                 args.add(KeyStore2ParameterUtils.makeEnum(
962                         KeymasterDefs.KM_TAG_DIGEST,
963                         digest
964                 ));
965             }
966         }
967 
968         KeyDescriptor wrappingkey = makeKeyDescriptor(entry.getWrappingKeyAlias());
969 
970         KeyEntryResponse response = null;
971         try {
972             StrictMode.noteDiskRead();
973             response = mKeyStore.getKeyEntry(wrappingkey);
974         } catch (android.security.KeyStoreException e) {
975             throw new KeyStoreException("Failed to import wrapped key. Keystore error code: "
976                     + e.getErrorCode(), e);
977         }
978 
979         KeyDescriptor wrappedKey = makeKeyDescriptor(alias);
980 
981         KeyStoreSecurityLevel securityLevel = new KeyStoreSecurityLevel(response.iSecurityLevel);
982 
983         final BiometricManager bm = android.app.AppGlobals.getInitialApplication()
984                 .getSystemService(BiometricManager.class);
985 
986         long[] biometricSids = bm.getAuthenticatorIds();
987 
988         List<AuthenticatorSpec> authenticatorSpecs = new ArrayList<>();
989 
990         AuthenticatorSpec authenticatorSpec = new AuthenticatorSpec();
991         authenticatorSpec.authenticatorType = HardwareAuthenticatorType.PASSWORD;
992         authenticatorSpec.authenticatorId = GateKeeper.getSecureUserId();
993         authenticatorSpecs.add(authenticatorSpec);
994 
995         for (long sid : biometricSids) {
996             AuthenticatorSpec authSpec = new AuthenticatorSpec();
997             authSpec.authenticatorType = HardwareAuthenticatorType.FINGERPRINT;
998             authSpec.authenticatorId = sid;
999             authenticatorSpecs.add(authSpec);
1000         }
1001 
1002         if (parts.length > 2) {
1003             @KeyProperties.EncryptionPaddingEnum int padding =
1004                     KeyProperties.EncryptionPadding.toKeymaster(parts[2]);
1005             if (padding == KeymasterDefs.KM_PAD_RSA_OAEP
1006                     && response.metadata != null
1007                     && response.metadata.authorizations != null) {
1008                 Authorization[] keyCharacteristics = response.metadata.authorizations;
1009 
1010                 for (Authorization authorization : keyCharacteristics) {
1011                     // Add default MGF1 digest SHA-1
1012                     // when wrapping key has KM_TAG_RSA_OAEP_MGF_DIGEST tag
1013                     if (authorization.keyParameter.tag
1014                             == KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST) {
1015                         // Default MGF1 digest is SHA-1
1016                         // and KeyMint only supports default MGF1 digest crypto operations
1017                         // for importWrappedKey.
1018                         args.add(KeyStore2ParameterUtils.makeEnum(
1019                                 KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST,
1020                                 KeyProperties.Digest.toKeymaster(DEFAULT_MGF1_DIGEST)
1021                         ));
1022                         break;
1023                     }
1024                 }
1025             }
1026         }
1027 
1028         try {
1029             StrictMode.noteDiskWrite();
1030             securityLevel.importWrappedKey(
1031                     wrappedKey, wrappingkey,
1032                     entry.getWrappedKeyBytes(),
1033                     null /* masking key is set to 32 bytes if null is given here */,
1034                     args,
1035                     authenticatorSpecs.toArray(new AuthenticatorSpec[0]));
1036         } catch (android.security.KeyStoreException e) {
1037             switch (e.getErrorCode()) {
1038                 case KeymasterDefs.KM_ERROR_UNIMPLEMENTED: {
1039                     throw new SecureKeyImportUnavailableException("Could not import wrapped key");
1040                 }
1041                 default:
1042                     throw new KeyStoreException("Failed to import wrapped key. Keystore error "
1043                             + "code: " + e.getErrorCode(), e);
1044             }
1045         }
1046     }
1047 
1048     @Override
engineSetKeyEntry(String alias, byte[] userKey, Certificate[] chain)1049     public void engineSetKeyEntry(String alias, byte[] userKey, Certificate[] chain)
1050             throws KeyStoreException {
1051         throw new KeyStoreException("Operation not supported because key encoding is unknown");
1052     }
1053 
1054     /**
1055      * This function sets a trusted certificate entry. It fails if the given
1056      * alias is already taken by an actual key entry. However, if the entry is a
1057      * trusted certificate it will get silently replaced.
1058      * @param alias the alias name
1059      * @param cert the certificate
1060      *
1061      * @throws KeyStoreException if the alias is already taken by a secret or private
1062      *         key entry.
1063      * @throws KeyStoreException with a nested {@link CertificateEncodingException}
1064      *         if the {@code cert.getEncoded()} throws.
1065      * @throws KeyStoreException with a nested {@link android.security.KeyStoreException} if
1066      *         something went wrong while inserting the certificate into keystore.
1067      * @throws NullPointerException if cert or alias is null.
1068      *
1069      * @hide
1070      */
1071     @Override
engineSetCertificateEntry(String alias, Certificate cert)1072     public void engineSetCertificateEntry(String alias, Certificate cert) throws KeyStoreException {
1073         if (isKeyEntry(alias)) {
1074             throw new KeyStoreException("Entry exists and is not a trusted certificate");
1075         }
1076 
1077         // We can't set something to null.
1078         if (cert == null) {
1079             throw new NullPointerException("cert == null");
1080         }
1081 
1082         final byte[] encoded;
1083         try {
1084             encoded = cert.getEncoded();
1085         } catch (CertificateEncodingException e) {
1086             throw new KeyStoreException(e);
1087         }
1088 
1089         try {
1090             StrictMode.noteDiskWrite();
1091             mKeyStore.updateSubcomponents(makeKeyDescriptor(alias),
1092                     null /* publicCert - unused when used as pure certificate store. */,
1093                     encoded);
1094         } catch (android.security.KeyStoreException e) {
1095             throw new KeyStoreException("Couldn't insert certificate.", e);
1096         }
1097     }
1098 
1099     @Override
engineDeleteEntry(String alias)1100     public void engineDeleteEntry(String alias) throws KeyStoreException {
1101         KeyDescriptor descriptor = makeKeyDescriptor(alias);
1102         try {
1103             StrictMode.noteDiskWrite();
1104             mKeyStore.deleteKey(descriptor);
1105         } catch (android.security.KeyStoreException e) {
1106             if (e.getErrorCode() != ResponseCode.KEY_NOT_FOUND) {
1107                 throw new KeyStoreException("Failed to delete entry: " + alias, e);
1108             }
1109         }
1110     }
1111 
getAliasesBatch(String startPastAlias)1112     private KeyDescriptor[] getAliasesBatch(String startPastAlias) {
1113         try {
1114             StrictMode.noteDiskRead();
1115             return mKeyStore.listBatch(
1116                     getTargetDomain(),
1117                     mNamespace,
1118                     startPastAlias
1119             );
1120         } catch (android.security.KeyStoreException e) {
1121             Log.e(TAG, "Failed to list keystore entries.", e);
1122             return new KeyDescriptor[0];
1123         }
1124     }
1125 
1126     @Override
engineAliases()1127     public Enumeration<String> engineAliases() {
1128         return new KeyEntriesEnumerator();
1129     }
1130 
1131     @Override
engineContainsAlias(String alias)1132     public boolean engineContainsAlias(String alias) {
1133         if (alias == null) {
1134             throw new NullPointerException("alias == null");
1135         }
1136 
1137         return getKeyMetadata(alias) != null;
1138     }
1139     @Override
engineSize()1140     public int engineSize() {
1141         try {
1142             StrictMode.noteDiskRead();
1143             return mKeyStore.getNumberOfEntries(
1144                     getTargetDomain(),
1145                     mNamespace
1146             );
1147         } catch (android.security.KeyStoreException e) {
1148             Log.e(TAG, "Failed to get the number of keystore entries.", e);
1149             return 0;
1150         }
1151     }
1152     @Override
engineIsKeyEntry(String alias)1153     public boolean engineIsKeyEntry(String alias) {
1154         return isKeyEntry(alias);
1155     }
1156 
isKeyEntry(String alias)1157     private boolean isKeyEntry(String alias) {
1158         if (alias == null) {
1159             throw new NullPointerException("alias == null");
1160         }
1161 
1162         KeyEntryResponse response = getKeyMetadata(alias);
1163         // If response is null, there is no such entry.
1164         // If response.iSecurityLevel is null, there is no private or secret key material stored.
1165         return response != null && response.iSecurityLevel != null;
1166     }
1167 
1168 
1169     @Override
engineIsCertificateEntry(String alias)1170     public boolean engineIsCertificateEntry(String alias) {
1171         if (alias == null) {
1172             throw new NullPointerException("alias == null");
1173         }
1174         KeyEntryResponse response = getKeyMetadata(alias);
1175         // If response == null there is no such entry.
1176         // If there is no certificateChain, then this is not a certificate entry.
1177         // If there is a private key entry, this is the certificate chain for that
1178         // key entry and not a CA certificate entry.
1179         return response != null
1180                 && response.metadata.certificateChain != null
1181                 && response.iSecurityLevel == null;
1182     }
1183 
1184     @Override
engineGetCertificateAlias(Certificate cert)1185     public String engineGetCertificateAlias(Certificate cert) {
1186         if (cert == null) {
1187             return null;
1188         }
1189         if (!"X.509".equalsIgnoreCase(cert.getType())) {
1190             Log.e(TAG, "In engineGetCertificateAlias: only X.509 certificates are supported.");
1191             return null;
1192         }
1193         byte[] targetCertBytes;
1194         try {
1195             targetCertBytes = cert.getEncoded();
1196         } catch (CertificateEncodingException e) {
1197             Log.e(TAG, "While trying to get the alias for a certificate.", e);
1198             return null;
1199         }
1200         if (targetCertBytes == null) {
1201             return null;
1202         }
1203 
1204         KeyDescriptor[] keyDescriptors = null;
1205         try {
1206             StrictMode.noteDiskRead();
1207             keyDescriptors = mKeyStore.list(
1208                     getTargetDomain(),
1209                     mNamespace
1210             );
1211         } catch (android.security.KeyStoreException e) {
1212             Log.w(TAG, "Failed to get list of keystore entries.", e);
1213         }
1214 
1215         String caAlias = null;
1216         for (KeyDescriptor d : keyDescriptors) {
1217             KeyEntryResponse response = getKeyMetadata(d.alias);
1218             if (response == null) {
1219                 continue;
1220             }
1221             /*
1222              * The KeyStoreSpi documentation says to only compare the first certificate in the
1223              * chain which is equivalent to the {@code response.metadata.certificate} field.
1224              * So we look for a hit in this field first. For pure CA certificate entries,
1225              * we check the {@code response.metadata.certificateChain} field. But we only
1226              * return a CA alias if there was no hit in the certificate field of any other
1227              * entry.
1228              */
1229             if (response.metadata.certificate != null) {
1230                 if (Arrays.equals(response.metadata.certificate, targetCertBytes)) {
1231                     return d.alias;
1232                 }
1233             } else if (response.metadata.certificateChain != null && caAlias == null) {
1234                 if (Arrays.equals(response.metadata.certificateChain, targetCertBytes)) {
1235                     caAlias =  d.alias;
1236                 }
1237             }
1238         }
1239         return caAlias;
1240     }
1241 
1242     /**
1243      * Used by Tests to initialize with a fake KeyStore2.
1244      * @hide
1245      * @param keystore
1246      */
1247     @VisibleForTesting
initForTesting(KeyStore2 keystore)1248     public void initForTesting(KeyStore2 keystore) {
1249         mKeyStore = keystore;
1250         mNamespace = KeyProperties.NAMESPACE_APPLICATION;
1251     }
1252 
1253     @Override
engineStore(OutputStream stream, char[] password)1254     public void engineStore(OutputStream stream, char[] password) throws IOException,
1255             NoSuchAlgorithmException, CertificateException {
1256         throw new UnsupportedOperationException("Can not serialize AndroidKeyStore to OutputStream");
1257     }
1258 
1259     @Override
engineLoad(InputStream stream, char[] password)1260     public void engineLoad(InputStream stream, char[] password) throws IOException,
1261             NoSuchAlgorithmException, CertificateException {
1262         if (stream != null) {
1263             throw new IllegalArgumentException("InputStream not supported");
1264         }
1265 
1266         if (password != null) {
1267             throw new IllegalArgumentException("password not supported");
1268         }
1269 
1270         // Unfortunate name collision.
1271         mKeyStore = KeyStore2.getInstance();
1272         mNamespace = KeyProperties.NAMESPACE_APPLICATION;
1273     }
1274 
1275     @Override
engineLoad(LoadStoreParameter param)1276     public void engineLoad(LoadStoreParameter param) throws IOException,
1277             NoSuchAlgorithmException, CertificateException {
1278         @KeyProperties.Namespace int namespace = KeyProperties.NAMESPACE_APPLICATION;
1279         if (param != null) {
1280             if (param instanceof AndroidKeyStoreLoadStoreParameter) {
1281                 namespace = ((AndroidKeyStoreLoadStoreParameter) param).getNamespace();
1282             } else {
1283                 throw new IllegalArgumentException(
1284                         "Unsupported param type: " + param.getClass());
1285             }
1286         }
1287         mKeyStore = KeyStore2.getInstance();
1288         mNamespace = namespace;
1289     }
1290 
1291     @Override
engineSetEntry(String alias, Entry entry, ProtectionParameter param)1292     public void engineSetEntry(String alias, Entry entry, ProtectionParameter param)
1293             throws KeyStoreException {
1294         if (entry == null) {
1295             throw new KeyStoreException("entry == null");
1296         }
1297 
1298         if (entry instanceof java.security.KeyStore.TrustedCertificateEntry) {
1299             java.security.KeyStore.TrustedCertificateEntry trE =
1300                     (java.security.KeyStore.TrustedCertificateEntry) entry;
1301             // engineSetCertificateEntry does not overwrite if the existing entry
1302             // is a key entry, but the semantic of engineSetEntry is such that it
1303             // overwrites any existing entry. Thus we delete any possible existing
1304             // entry by this alias.
1305             engineDeleteEntry(alias);
1306             engineSetCertificateEntry(alias, trE.getTrustedCertificate());
1307             return;
1308         }
1309 
1310         if (entry instanceof PrivateKeyEntry) {
1311             PrivateKeyEntry prE = (PrivateKeyEntry) entry;
1312             setPrivateKeyEntry(alias, prE.getPrivateKey(), prE.getCertificateChain(), param);
1313         } else if (entry instanceof SecretKeyEntry) {
1314             SecretKeyEntry secE = (SecretKeyEntry) entry;
1315             setSecretKeyEntry(alias, secE.getSecretKey(), param);
1316         } else if (entry instanceof WrappedKeyEntry) {
1317             WrappedKeyEntry wke = (WrappedKeyEntry) entry;
1318             setWrappedKeyEntry(alias, wke, param);
1319         } else {
1320             throw new KeyStoreException(
1321                     "Entry must be a PrivateKeyEntry, SecretKeyEntry, WrappedKeyEntry "
1322                             + "or TrustedCertificateEntry; was " + entry);
1323         }
1324     }
1325 
1326     private class KeyEntriesEnumerator implements Enumeration<String> {
1327         private KeyDescriptor[] mCurrentBatch;
1328         private int mCurrentEntry = 0;
1329         private String mLastAlias = null;
KeyEntriesEnumerator()1330         private KeyEntriesEnumerator() {
1331             getAndValidateNextBatch();
1332         }
1333 
getAndValidateNextBatch()1334         private void getAndValidateNextBatch() {
1335             mCurrentBatch = getAliasesBatch(mLastAlias);
1336             mCurrentEntry = 0;
1337         }
1338 
hasMoreElements()1339         public boolean hasMoreElements() {
1340             return (mCurrentBatch != null) && (mCurrentBatch.length > 0);
1341         }
1342 
nextElement()1343         public String nextElement() {
1344             if ((mCurrentBatch == null) || (mCurrentBatch.length == 0)) {
1345                 throw new NoSuchElementException("Error while fetching entries.");
1346             }
1347             final KeyDescriptor currentEntry = mCurrentBatch[mCurrentEntry];
1348             mLastAlias = currentEntry.alias;
1349 
1350             mCurrentEntry++;
1351             // This was the last entry in the batch.
1352             if (mCurrentEntry >= mCurrentBatch.length) {
1353                 getAndValidateNextBatch();
1354             }
1355 
1356             return mLastAlias;
1357         }
1358     }
1359 }
1360