1 /* 2 * Copyright 2019 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.identity; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.content.Context; 22 import android.content.pm.FeatureInfo; 23 import android.content.pm.PackageManager; 24 import android.os.ServiceManager; 25 26 class CredstoreIdentityCredentialStore extends IdentityCredentialStore { 27 28 private static final String TAG = "CredstoreIdentityCredentialStore"; 29 30 private Context mContext = null; 31 private ICredentialStore mStore = null; 32 private int mFeatureVersion; 33 getFeatureVersion(@onNull Context context)34 static int getFeatureVersion(@NonNull Context context) { 35 PackageManager pm = context.getPackageManager(); 36 if (pm.hasSystemFeature(PackageManager.FEATURE_IDENTITY_CREDENTIAL_HARDWARE)) { 37 FeatureInfo[] infos = pm.getSystemAvailableFeatures(); 38 for (int n = 0; n < infos.length; n++) { 39 FeatureInfo info = infos[n]; 40 if (info.name.equals(PackageManager.FEATURE_IDENTITY_CREDENTIAL_HARDWARE)) { 41 return info.version; 42 } 43 } 44 } 45 // Use of the system feature is not required since Android 12. So for Android 11 46 // return 202009 which is the feature version shipped with Android 11. 47 return 202009; 48 } 49 CredstoreIdentityCredentialStore(@onNull Context context, ICredentialStore store)50 private CredstoreIdentityCredentialStore(@NonNull Context context, ICredentialStore store) { 51 mContext = context; 52 mStore = store; 53 mFeatureVersion = getFeatureVersion(mContext); 54 } 55 getInstanceForType(@onNull Context context, int credentialStoreType)56 static CredstoreIdentityCredentialStore getInstanceForType(@NonNull Context context, 57 int credentialStoreType) { 58 ICredentialStoreFactory storeFactory = 59 ICredentialStoreFactory.Stub.asInterface( 60 ServiceManager.getService("android.security.identity")); 61 if (storeFactory == null) { 62 // This can happen if credstore is not running or not installed. 63 return null; 64 } 65 66 ICredentialStore credStore = null; 67 try { 68 credStore = storeFactory.getCredentialStore(credentialStoreType); 69 } catch (android.os.RemoteException e) { 70 throw new RuntimeException("Unexpected RemoteException ", e); 71 } catch (android.os.ServiceSpecificException e) { 72 if (e.errorCode == ICredentialStore.ERROR_GENERIC) { 73 return null; 74 } else { 75 throw new RuntimeException("Unexpected ServiceSpecificException with code " 76 + e.errorCode, e); 77 } 78 } 79 if (credStore == null) { 80 return null; 81 } 82 83 return new CredstoreIdentityCredentialStore(context, credStore); 84 } 85 86 private static CredstoreIdentityCredentialStore sInstanceDefault = null; 87 private static CredstoreIdentityCredentialStore sInstanceDirectAccess = null; 88 getInstance(@onNull Context context)89 public static @Nullable IdentityCredentialStore getInstance(@NonNull Context context) { 90 if (sInstanceDefault == null) { 91 sInstanceDefault = getInstanceForType(context, 92 ICredentialStoreFactory.CREDENTIAL_STORE_TYPE_DEFAULT); 93 } 94 return sInstanceDefault; 95 } 96 getDirectAccessInstance(@onNull Context context)97 public static @Nullable IdentityCredentialStore getDirectAccessInstance(@NonNull 98 Context context) { 99 if (sInstanceDirectAccess == null) { 100 sInstanceDirectAccess = getInstanceForType(context, 101 ICredentialStoreFactory.CREDENTIAL_STORE_TYPE_DIRECT_ACCESS); 102 } 103 return sInstanceDirectAccess; 104 } 105 106 @Override getSupportedDocTypes()107 public @NonNull String[] getSupportedDocTypes() { 108 try { 109 SecurityHardwareInfoParcel info; 110 info = mStore.getSecurityHardwareInfo(); 111 return info.supportedDocTypes; 112 } catch (android.os.RemoteException e) { 113 throw new RuntimeException("Unexpected RemoteException ", e); 114 } catch (android.os.ServiceSpecificException e) { 115 throw new RuntimeException("Unexpected ServiceSpecificException with code " 116 + e.errorCode, e); 117 } 118 } 119 createCredential( @onNull String credentialName, @NonNull String docType)120 @Override public @NonNull WritableIdentityCredential createCredential( 121 @NonNull String credentialName, 122 @NonNull String docType) throws AlreadyPersonalizedException, 123 DocTypeNotSupportedException { 124 try { 125 IWritableCredential wc = mStore.createCredential(credentialName, docType); 126 return new CredstoreWritableIdentityCredential(mContext, credentialName, docType, wc); 127 } catch (android.os.RemoteException e) { 128 throw new RuntimeException("Unexpected RemoteException ", e); 129 } catch (android.os.ServiceSpecificException e) { 130 if (e.errorCode == ICredentialStore.ERROR_ALREADY_PERSONALIZED) { 131 throw new AlreadyPersonalizedException(e.getMessage(), e); 132 } else if (e.errorCode == ICredentialStore.ERROR_DOCUMENT_TYPE_NOT_SUPPORTED) { 133 throw new DocTypeNotSupportedException(e.getMessage(), e); 134 } else { 135 throw new RuntimeException("Unexpected ServiceSpecificException with code " 136 + e.errorCode, e); 137 } 138 } 139 } 140 getCredentialByName( @onNull String credentialName, @Ciphersuite int cipherSuite)141 @Override public @Nullable IdentityCredential getCredentialByName( 142 @NonNull String credentialName, 143 @Ciphersuite int cipherSuite) throws CipherSuiteNotSupportedException { 144 try { 145 ICredential credstoreCredential; 146 credstoreCredential = mStore.getCredentialByName(credentialName, cipherSuite); 147 return new CredstoreIdentityCredential(mContext, credentialName, cipherSuite, 148 credstoreCredential, null, mFeatureVersion); 149 } catch (android.os.RemoteException e) { 150 throw new RuntimeException("Unexpected RemoteException ", e); 151 } catch (android.os.ServiceSpecificException e) { 152 if (e.errorCode == ICredentialStore.ERROR_NO_SUCH_CREDENTIAL) { 153 return null; 154 } else if (e.errorCode == ICredentialStore.ERROR_CIPHER_SUITE_NOT_SUPPORTED) { 155 throw new CipherSuiteNotSupportedException(e.getMessage(), e); 156 } else { 157 throw new RuntimeException("Unexpected ServiceSpecificException with code " 158 + e.errorCode, e); 159 } 160 } 161 } 162 163 @Override deleteCredentialByName(@onNull String credentialName)164 public @Nullable byte[] deleteCredentialByName(@NonNull String credentialName) { 165 ICredential credstoreCredential = null; 166 try { 167 try { 168 credstoreCredential = mStore.getCredentialByName(credentialName, 169 CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256); 170 } catch (android.os.ServiceSpecificException e) { 171 if (e.errorCode == ICredentialStore.ERROR_NO_SUCH_CREDENTIAL) { 172 return null; 173 } 174 } 175 byte[] proofOfDeletion = credstoreCredential.deleteCredential(); 176 return proofOfDeletion; 177 } catch (android.os.RemoteException e) { 178 throw new RuntimeException("Unexpected RemoteException ", e); 179 } catch (android.os.ServiceSpecificException e) { 180 throw new RuntimeException("Unexpected ServiceSpecificException with code " 181 + e.errorCode, e); 182 } 183 } 184 185 @Override createPresentationSession(@iphersuite int cipherSuite)186 public @NonNull PresentationSession createPresentationSession(@Ciphersuite int cipherSuite) 187 throws CipherSuiteNotSupportedException { 188 try { 189 ISession credstoreSession = mStore.createPresentationSession(cipherSuite); 190 return new CredstorePresentationSession(mContext, cipherSuite, this, credstoreSession, 191 mFeatureVersion); 192 } catch (android.os.RemoteException e) { 193 throw new RuntimeException("Unexpected RemoteException ", e); 194 } catch (android.os.ServiceSpecificException e) { 195 if (e.errorCode == ICredentialStore.ERROR_CIPHER_SUITE_NOT_SUPPORTED) { 196 throw new CipherSuiteNotSupportedException(e.getMessage(), e); 197 } else { 198 throw new RuntimeException("Unexpected ServiceSpecificException with code " 199 + e.errorCode, e); 200 } 201 } 202 } 203 204 } 205