Files
frameworks_base/identity/java/android/security/identity/CredstoreWritableIdentityCredential.java
David Zeuthen 045b6de975 Add Framework APIs for Identity Credential.
The Identity Credential APIs provides an interface to a secure store
for user identity documents.  These APIs are deliberately fairly
general and abstract.  To the extent possible, specification of the
message formats and semantics of communication with credential
verification devices and Issuing Authorities (IAs) is out of scope for
these APIs.

The Identity Credential APIs rely on user authentication to protect
data elements in credentials which is implemented through
auth-tokens. This CL contains changes to CryptoObject to allow this.

Bug: 111446262
Test: CtsIdentityTestCases
Change-Id: I48f21a561b762d86c9ca8d229962782572412f47
2020-01-20 15:56:57 -05:00

169 lines
6.7 KiB
Java

/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.security.identity;
import android.annotation.NonNull;
import android.content.Context;
import android.security.GateKeeper;
import java.io.ByteArrayInputStream;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.LinkedList;
class CredstoreWritableIdentityCredential extends WritableIdentityCredential {
private static final String TAG = "CredstoreWritableIdentityCredential";
private String mDocType;
private String mCredentialName;
private Context mContext;
private IWritableCredential mBinder;
CredstoreWritableIdentityCredential(Context context,
@NonNull String credentialName,
@NonNull String docType,
IWritableCredential binder) {
mContext = context;
mDocType = docType;
mCredentialName = credentialName;
mBinder = binder;
}
@NonNull @Override
public Collection<X509Certificate> getCredentialKeyCertificateChain(@NonNull byte[] challenge) {
try {
byte[] certsBlob = mBinder.getCredentialKeyCertificateChain(challenge);
ByteArrayInputStream bais = new ByteArrayInputStream(certsBlob);
Collection<? extends Certificate> certs = null;
try {
CertificateFactory factory = CertificateFactory.getInstance("X.509");
certs = factory.generateCertificates(bais);
} catch (CertificateException e) {
throw new RuntimeException("Error decoding certificates", e);
}
LinkedList<X509Certificate> x509Certs = new LinkedList<>();
for (Certificate cert : certs) {
x509Certs.add((X509Certificate) cert);
}
return x509Certs;
} catch (android.os.RemoteException e) {
throw new RuntimeException("Unexpected RemoteException ", e);
} catch (android.os.ServiceSpecificException e) {
throw new RuntimeException("Unexpected ServiceSpecificException with code "
+ e.errorCode, e);
}
}
@NonNull @Override
public byte[] personalize(@NonNull PersonalizationData personalizationData) {
Collection<AccessControlProfile> accessControlProfiles =
personalizationData.getAccessControlProfiles();
AccessControlProfileParcel[] acpParcels =
new AccessControlProfileParcel[accessControlProfiles.size()];
boolean usingUserAuthentication = false;
int n = 0;
for (AccessControlProfile profile : accessControlProfiles) {
acpParcels[n] = new AccessControlProfileParcel();
acpParcels[n].id = profile.getAccessControlProfileId().getId();
X509Certificate cert = profile.getReaderCertificate();
if (cert != null) {
try {
acpParcels[n].readerCertificate = cert.getEncoded();
} catch (CertificateException e) {
throw new RuntimeException("Error encoding reader certificate", e);
}
} else {
acpParcels[n].readerCertificate = new byte[0];
}
acpParcels[n].userAuthenticationRequired = profile.isUserAuthenticationRequired();
acpParcels[n].userAuthenticationTimeoutMillis = profile.getUserAuthenticationTimeout();
if (profile.isUserAuthenticationRequired()) {
usingUserAuthentication = true;
}
n++;
}
Collection<String> namespaceNames = personalizationData.getNamespaceNames();
EntryNamespaceParcel[] ensParcels = new EntryNamespaceParcel[namespaceNames.size()];
n = 0;
for (String namespaceName : namespaceNames) {
PersonalizationData.NamespaceData nsd =
personalizationData.getNamespaceData(namespaceName);
ensParcels[n] = new EntryNamespaceParcel();
ensParcels[n].namespaceName = namespaceName;
Collection<String> entryNames = nsd.getEntryNames();
EntryParcel[] eParcels = new EntryParcel[entryNames.size()];
int m = 0;
for (String entryName : entryNames) {
eParcels[m] = new EntryParcel();
eParcels[m].name = entryName;
eParcels[m].value = nsd.getEntryValue(entryName);
Collection<AccessControlProfileId> acpIds =
nsd.getAccessControlProfileIds(entryName);
eParcels[m].accessControlProfileIds = new int[acpIds.size()];
int o = 0;
for (AccessControlProfileId acpId : acpIds) {
eParcels[m].accessControlProfileIds[o++] = acpId.getId();
}
m++;
}
ensParcels[n].entries = eParcels;
n++;
}
// Note: The value 0 is used to convey that no user-authentication is needed for this
// credential. This is to allow creating credentials w/o user authentication on devices
// where Secure lock screen is not enabled.
long secureUserId = 0;
if (usingUserAuthentication) {
secureUserId = getRootSid();
}
try {
byte[] personalizationReceipt = mBinder.personalize(acpParcels, ensParcels,
secureUserId);
return personalizationReceipt;
} catch (android.os.RemoteException e) {
throw new RuntimeException("Unexpected RemoteException ", e);
} catch (android.os.ServiceSpecificException e) {
throw new RuntimeException("Unexpected ServiceSpecificException with code "
+ e.errorCode, e);
}
}
private static long getRootSid() {
long rootSid = GateKeeper.getSecureUserId();
if (rootSid == 0) {
throw new IllegalStateException("Secure lock screen must be enabled"
+ " to create credentials requiring user authentication");
}
return rootSid;
}
}