• 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.Collection;
29 import java.util.LinkedList;
30 
31 class 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 
CredstoreWritableIdentityCredential(Context context, @NonNull String credentialName, @NonNull String docType, IWritableCredential binder)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
getCredentialKeyCertificateChain(@onNull byte[] challenge)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
personalize(@onNull PersonalizationData personalizationData)78     public byte[] personalize(@NonNull PersonalizationData personalizationData) {
79         return personalize(mBinder, personalizationData);
80     }
81 
82     // Used by both personalize() and CredstoreIdentityCredential.update().
83     //
84     @NonNull
personalize(IWritableCredential binder, @NonNull PersonalizationData personalizationData)85     static byte[] personalize(IWritableCredential binder,
86             @NonNull PersonalizationData personalizationData) {
87         Collection<AccessControlProfile> accessControlProfiles =
88                 personalizationData.getAccessControlProfiles();
89 
90         AccessControlProfileParcel[] acpParcels =
91                 new AccessControlProfileParcel[accessControlProfiles.size()];
92         boolean usingUserAuthentication = false;
93         int n = 0;
94         for (AccessControlProfile profile : accessControlProfiles) {
95             acpParcels[n] = new AccessControlProfileParcel();
96             acpParcels[n].id = profile.getAccessControlProfileId().getId();
97             X509Certificate cert = profile.getReaderCertificate();
98             if (cert != null) {
99                 try {
100                     acpParcels[n].readerCertificate = cert.getEncoded();
101                 } catch (CertificateException e) {
102                     throw new RuntimeException("Error encoding reader certificate", e);
103                 }
104             } else {
105                 acpParcels[n].readerCertificate = new byte[0];
106             }
107             acpParcels[n].userAuthenticationRequired = profile.isUserAuthenticationRequired();
108             acpParcels[n].userAuthenticationTimeoutMillis = profile.getUserAuthenticationTimeout();
109             if (profile.isUserAuthenticationRequired()) {
110                 usingUserAuthentication = true;
111             }
112             n++;
113         }
114 
115         Collection<String> namespaces = personalizationData.getNamespaces();
116 
117         EntryNamespaceParcel[] ensParcels  = new EntryNamespaceParcel[namespaces.size()];
118         n = 0;
119         for (String namespaceName : namespaces) {
120             PersonalizationData.NamespaceData nsd =
121                     personalizationData.getNamespaceData(namespaceName);
122 
123             ensParcels[n] = new EntryNamespaceParcel();
124             ensParcels[n].namespaceName = namespaceName;
125 
126             Collection<String> entryNames = nsd.getEntryNames();
127             EntryParcel[] eParcels = new EntryParcel[entryNames.size()];
128             int m = 0;
129             for (String entryName : entryNames) {
130                 eParcels[m] = new EntryParcel();
131                 eParcels[m].name = entryName;
132                 eParcels[m].value = nsd.getEntryValue(entryName);
133                 Collection<AccessControlProfileId> acpIds =
134                         nsd.getAccessControlProfileIds(entryName);
135                 eParcels[m].accessControlProfileIds = new int[acpIds.size()];
136                 int o = 0;
137                 for (AccessControlProfileId acpId : acpIds) {
138                     eParcels[m].accessControlProfileIds[o++] = acpId.getId();
139                 }
140                 m++;
141             }
142             ensParcels[n].entries = eParcels;
143             n++;
144         }
145 
146         // Note: The value 0 is used to convey that no user-authentication is needed for this
147         // credential. This is to allow creating credentials w/o user authentication on devices
148         // where Secure lock screen is not enabled.
149         long secureUserId = 0;
150         if (usingUserAuthentication) {
151             secureUserId = getRootSid();
152         }
153         try {
154             byte[] personalizationReceipt = binder.personalize(acpParcels, ensParcels,
155                     secureUserId);
156             return personalizationReceipt;
157         } catch (android.os.RemoteException e) {
158             throw new RuntimeException("Unexpected RemoteException ", e);
159         } catch (android.os.ServiceSpecificException e) {
160             throw new RuntimeException("Unexpected ServiceSpecificException with code "
161                     + e.errorCode, e);
162         }
163     }
164 
getRootSid()165     private static long getRootSid() {
166         long rootSid = GateKeeper.getSecureUserId();
167         if (rootSid == 0) {
168             throw new IllegalStateException("Secure lock screen must be enabled"
169                     + " to create credentials requiring user authentication");
170         }
171         return rootSid;
172     }
173 
174 }
175