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
169 lines
6.7 KiB
Java
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;
|
|
}
|
|
|
|
|
|
}
|