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