blob: 725e3d8e429a1a2b3446c47c2298e431e9e048c1 [file] [log] [blame]
David Zeuthen045b6de2019-10-29 15:15:18 -04001/*
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
17package android.security.identity;
18
19import android.annotation.NonNull;
20import android.content.Context;
21import android.security.GateKeeper;
22
23import java.io.ByteArrayInputStream;
24import java.security.cert.Certificate;
25import java.security.cert.CertificateException;
26import java.security.cert.CertificateFactory;
27import java.security.cert.X509Certificate;
28import java.util.Collection;
29import java.util.LinkedList;
30
31class CredstoreWritableIdentityCredential extends WritableIdentityCredential {
32
33 private static final String TAG = "CredstoreWritableIdentityCredential";
34
35 private String mDocType;
36 private String mCredentialName;
37 private Context mContext;
38 private IWritableCredential mBinder;
39
40 CredstoreWritableIdentityCredential(Context context,
41 @NonNull String credentialName,
42 @NonNull String docType,
43 IWritableCredential binder) {
44 mContext = context;
45 mDocType = docType;
46 mCredentialName = credentialName;
47 mBinder = binder;
48 }
49
50 @NonNull @Override
51 public Collection<X509Certificate> getCredentialKeyCertificateChain(@NonNull byte[] challenge) {
52 try {
53 byte[] certsBlob = mBinder.getCredentialKeyCertificateChain(challenge);
54 ByteArrayInputStream bais = new ByteArrayInputStream(certsBlob);
55
56 Collection<? extends Certificate> certs = null;
57 try {
58 CertificateFactory factory = CertificateFactory.getInstance("X.509");
59 certs = factory.generateCertificates(bais);
60 } catch (CertificateException e) {
61 throw new RuntimeException("Error decoding certificates", e);
62 }
63
64 LinkedList<X509Certificate> x509Certs = new LinkedList<>();
65 for (Certificate cert : certs) {
66 x509Certs.add((X509Certificate) cert);
67 }
68 return x509Certs;
69 } catch (android.os.RemoteException e) {
70 throw new RuntimeException("Unexpected RemoteException ", e);
71 } catch (android.os.ServiceSpecificException e) {
72 throw new RuntimeException("Unexpected ServiceSpecificException with code "
73 + e.errorCode, e);
74 }
75 }
76
77 @NonNull @Override
78 public byte[] personalize(@NonNull PersonalizationData personalizationData) {
79
80 Collection<AccessControlProfile> accessControlProfiles =
81 personalizationData.getAccessControlProfiles();
82
83 AccessControlProfileParcel[] acpParcels =
84 new AccessControlProfileParcel[accessControlProfiles.size()];
85 boolean usingUserAuthentication = false;
86 int n = 0;
87 for (AccessControlProfile profile : accessControlProfiles) {
88 acpParcels[n] = new AccessControlProfileParcel();
89 acpParcels[n].id = profile.getAccessControlProfileId().getId();
90 X509Certificate cert = profile.getReaderCertificate();
91 if (cert != null) {
92 try {
93 acpParcels[n].readerCertificate = cert.getEncoded();
94 } catch (CertificateException e) {
95 throw new RuntimeException("Error encoding reader certificate", e);
96 }
97 } else {
98 acpParcels[n].readerCertificate = new byte[0];
99 }
100 acpParcels[n].userAuthenticationRequired = profile.isUserAuthenticationRequired();
101 acpParcels[n].userAuthenticationTimeoutMillis = profile.getUserAuthenticationTimeout();
102 if (profile.isUserAuthenticationRequired()) {
103 usingUserAuthentication = true;
104 }
105 n++;
106 }
107
David Zeuthend50b2812020-03-05 10:10:27 -0500108 Collection<String> namespaces = personalizationData.getNamespaces();
David Zeuthen045b6de2019-10-29 15:15:18 -0400109
David Zeuthend50b2812020-03-05 10:10:27 -0500110 EntryNamespaceParcel[] ensParcels = new EntryNamespaceParcel[namespaces.size()];
David Zeuthen045b6de2019-10-29 15:15:18 -0400111 n = 0;
David Zeuthend50b2812020-03-05 10:10:27 -0500112 for (String namespaceName : namespaces) {
David Zeuthen045b6de2019-10-29 15:15:18 -0400113 PersonalizationData.NamespaceData nsd =
114 personalizationData.getNamespaceData(namespaceName);
115
116 ensParcels[n] = new EntryNamespaceParcel();
117 ensParcels[n].namespaceName = namespaceName;
118
119 Collection<String> entryNames = nsd.getEntryNames();
120 EntryParcel[] eParcels = new EntryParcel[entryNames.size()];
121 int m = 0;
122 for (String entryName : entryNames) {
123 eParcels[m] = new EntryParcel();
124 eParcels[m].name = entryName;
125 eParcels[m].value = nsd.getEntryValue(entryName);
126 Collection<AccessControlProfileId> acpIds =
127 nsd.getAccessControlProfileIds(entryName);
128 eParcels[m].accessControlProfileIds = new int[acpIds.size()];
129 int o = 0;
130 for (AccessControlProfileId acpId : acpIds) {
131 eParcels[m].accessControlProfileIds[o++] = acpId.getId();
132 }
133 m++;
134 }
135 ensParcels[n].entries = eParcels;
136 n++;
137 }
138
139 // Note: The value 0 is used to convey that no user-authentication is needed for this
140 // credential. This is to allow creating credentials w/o user authentication on devices
141 // where Secure lock screen is not enabled.
142 long secureUserId = 0;
143 if (usingUserAuthentication) {
144 secureUserId = getRootSid();
145 }
146 try {
147 byte[] personalizationReceipt = mBinder.personalize(acpParcels, ensParcels,
148 secureUserId);
149 return personalizationReceipt;
150 } catch (android.os.RemoteException e) {
151 throw new RuntimeException("Unexpected RemoteException ", e);
152 } catch (android.os.ServiceSpecificException e) {
153 throw new RuntimeException("Unexpected ServiceSpecificException with code "
154 + e.errorCode, e);
155 }
156 }
157
158 private static long getRootSid() {
159 long rootSid = GateKeeper.getSecureUserId();
160 if (rootSid == 0) {
161 throw new IllegalStateException("Secure lock screen must be enabled"
162 + " to create credentials requiring user authentication");
163 }
164 return rootSid;
165 }
166
167
168}