1 /*
2  * Copyright (C) 2017 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  * Copyright (c) 2014-2017, The Linux Foundation.
18  */
19 
20 /*
21  * Copyright (C) 2011 Deutsche Telekom, A.G.
22  *
23  * Licensed under the Apache License, Version 2.0 (the "License");
24  * you may not use this file except in compliance with the License.
25  * You may obtain a copy of the License at
26  *
27  *      http://www.apache.org/licenses/LICENSE-2.0
28  *
29  * Unless required by applicable law or agreed to in writing, software
30  * distributed under the License is distributed on an "AS IS" BASIS,
31  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
32  * See the License for the specific language governing permissions and
33  * limitations under the License.
34  */
35 
36 /*
37  * Contributed by: Giesecke & Devrient GmbH.
38  */
39 
40 package com.android.se.security.arf.pkcs15;
41 
42 import android.util.Log;
43 
44 import com.android.se.Channel;
45 import com.android.se.security.arf.SecureElement;
46 import com.android.se.security.arf.SecureElementException;
47 
48 import java.io.IOException;
49 import java.security.AccessControlException;
50 import java.security.cert.CertificateException;
51 import java.util.MissingResourceException;
52 import java.util.NoSuchElementException;
53 
54 /** Handles PKCS#15 topology */
55 public class PKCS15Handler {
56 
57     // AID of the GPAC Applet/ADF
58     public static final byte[] GPAC_ARF_AID = {
59             (byte) 0xA0, 0x00, 0x00, 0x00, 0x18, 0x47, 0x50, 0x41, 0x43, 0x2D, 0x31, 0x35
60     };
61     // AID of the PKCS#15 ADF
62     public static final byte[] PKCS15_AID = {
63             (byte) 0xA0, 0x00, 0x00, 0x00, 0x63, 0x50, 0x4B, 0x43, 0x53, 0x2D, 0x31, 0x35
64     };
65     // AIDs of "Access Control Rules" containers
66     public static final byte[][] CONTAINER_AIDS = {PKCS15_AID, GPAC_ARF_AID, null};
67     public final String mTag = "SecureElement-PKCS15Handler";
68     // Handle to "Secure Element"
69     private SecureElement mSEHandle;
70     // "Secure Element" label
71     private String mSELabel = null;
72     // Handle to "Logical Channel" allocated by the SE
73     private Channel mArfChannel = null;
74     // "EF Access Control Main" object
75     private EFACMain mACMainObject = null;
76     // EF AC Rules object
77     private EFACRules mACRulesObject = null;
78     private byte[] mPkcs15Path = null;
79     private byte[] mACMainPath = null;
80     private boolean mACMFfound = true;
81 
82     /**
83      * Constructor
84      *
85      * @param handle Handle to "Secure Element"
86      */
PKCS15Handler(SecureElement handle)87     public PKCS15Handler(SecureElement handle) {
88         mSEHandle = handle;
89     }
90 
91     /** Updates "Access Control Rules" */
updateACRules()92     private boolean updateACRules() throws CertificateException, IOException,
93             MissingResourceException, NoSuchElementException, PKCS15Exception,
94             SecureElementException {
95         byte[] ACRulesPath = null;
96         if (!mACMFfound) {
97             mSEHandle.resetAccessRules();
98             mACMainPath = null;
99             if (mArfChannel != null) mSEHandle.closeArfChannel();
100             this.initACEntryPoint();
101         }
102         try {
103             ACRulesPath = mACMainObject.analyseFile();
104             mACMFfound = true;
105         } catch (IOException e) {
106             // IOException must be propagated to the access control enforcer.
107             throw e;
108         } catch (Exception e) {
109             Log.i(mTag, "ACMF Not found !");
110             mACMainObject = null;
111             mSEHandle.resetAccessRules();
112             mACMFfound = false;
113             throw e;
114         }
115         // Check if rules must be updated
116         if (ACRulesPath != null) {
117             Log.i(mTag, "Access Rules needs to be updated...");
118             if (mACRulesObject == null) {
119                 mACRulesObject = new EFACRules(mSEHandle);
120             }
121             mSEHandle.clearAccessRuleCache();
122             mACMainPath = null;
123             if (mArfChannel != null) mSEHandle.closeArfChannel();
124 
125             this.initACEntryPoint();
126 
127             try {
128                 mACRulesObject.analyseFile(ACRulesPath);
129             } catch (IOException e) {
130                 // IOException must be propagated to the access control enforcer.
131                 throw e;
132             } catch (Exception e) {
133                 Log.i(mTag, "Exception: clear access rule cache and refresh tag");
134                 mSEHandle.resetAccessRules();
135                 throw e;
136             }
137             return true;
138         } else {
139             Log.i(mTag, "Refresh Tag has not been changed...");
140             return false;
141         }
142     }
143 
144     /** Initializes "Access Control" entry point [ACMain] */
initACEntryPoint()145     private void initACEntryPoint() throws CertificateException, IOException,
146             MissingResourceException, NoSuchElementException, PKCS15Exception,
147             SecureElementException {
148 
149         byte[] DODFPath = null;
150         boolean absent = true;
151 
152         for (int ind = 0; ind < CONTAINER_AIDS.length; ind++) {
153             try {
154                 boolean result = selectACRulesContainer(CONTAINER_AIDS[ind]);
155                 // NoSuchElementException was not thrown by the terminal.
156                 // The terminal confirmed that the specified applet or PKCS#15 ADF exists
157                 // or could not determine that it does not exists on the secure element.
158                 absent = false;
159                 if (!result) {
160                     continue;
161                 }
162 
163                 byte[] acMainPath = null;
164                 if (mACMainPath == null) {
165                     EFODF ODFObject = new EFODF(mSEHandle);
166                     DODFPath = ODFObject.analyseFile(mPkcs15Path);
167                     EFDODF DODFObject = new EFDODF(mSEHandle);
168                     acMainPath = DODFObject.analyseFile(DODFPath);
169 
170                     mACMainPath = acMainPath;
171                 } else {
172                     if (mPkcs15Path != null) {
173                         acMainPath = new byte[mPkcs15Path.length + mACMainPath.length];
174                         System.arraycopy(mPkcs15Path, 0, acMainPath, 0, mPkcs15Path.length);
175                         System.arraycopy(mACMainPath, 0, acMainPath, mPkcs15Path.length,
176                                 mACMainPath.length);
177 
178                     } else {
179                         acMainPath = mACMainPath;
180                     }
181                 }
182                 mACMainObject = new EFACMain(mSEHandle, acMainPath);
183                 break;
184             } catch (NoSuchElementException e) {
185                 // The specified applet or PKCS#15 ADF does not exist.
186                 // Let us check the next candidate.
187             }
188         }
189 
190         if (absent) {
191             // All the candidate applet and/or PKCS#15 ADF cannot be found on the secure element.
192             throw new NoSuchElementException("No ARF exists");
193         }
194     }
195 
196     /**
197      * Selects "Access Control Rules" container
198      *
199      * @param AID Identification of the GPAC Applet/PKCS#15 ADF; <code>null</code> for EF_DIR file
200      * @return <code>true</code> when container is active; <code>false</code> otherwise
201      */
selectACRulesContainer(byte[] aid)202     private boolean selectACRulesContainer(byte[] aid) throws IOException, MissingResourceException,
203             NoSuchElementException, PKCS15Exception, SecureElementException {
204         if (aid == null) {
205             mArfChannel = mSEHandle.openLogicalArfChannel(new byte[]{});
206             if (mArfChannel != null) {
207                 Log.i(mTag, "Logical channels are used to access to PKC15");
208             } else {
209                 return false;
210             }
211             // estimate PKCS15 path only if it is not known already.
212             if (mPkcs15Path == null) {
213                 mACMainPath = null;
214                 // EF_DIR parsing
215                 EFDIR DIRObject = new EFDIR(mSEHandle);
216                 mPkcs15Path = DIRObject.lookupAID(PKCS15_AID);
217                 if (mPkcs15Path == null) {
218                     Log.i(mTag, "Cannot use ARF: cannot select PKCS#15 directory via EF Dir");
219                     // TODO: Here it might be possible to set a default path
220                     // so that SIMs without EF-Dir could be supported.
221                     throw new NoSuchElementException("Cannot select PKCS#15 directory via EF Dir");
222                 }
223             }
224         } else {
225             // if an AID is given use logical channel.
226             // Selection of Applet/ADF via AID is done via SCAPI and logical Channels
227             mArfChannel = mSEHandle.openLogicalArfChannel(aid);
228             if (mArfChannel == null) {
229                 Log.w(mTag, "GPAC/PKCS#15 ADF not found!!");
230                 return false;
231             }
232             // ARF is selected via AID.
233             // if there is a change from path selection to AID
234             // selection, then reset AC Main path.
235             if (mPkcs15Path != null) {
236                 mACMainPath = null;
237             }
238             mPkcs15Path = null; // selection is done via AID
239         }
240         return true;
241     }
242 
243     /**
244      * Loads "Access Control Rules" from container
245      *
246      * @return false if access rules where not read due to constant refresh tag.
247      */
loadAccessControlRules(String secureElement)248     public synchronized boolean loadAccessControlRules(String secureElement) throws IOException,
249             MissingResourceException, NoSuchElementException {
250         mSELabel = secureElement;
251         Log.i(mTag, "- Loading " + mSELabel + " rules...");
252         try {
253             initACEntryPoint();
254             return updateACRules();
255         } catch (IOException | MissingResourceException | NoSuchElementException e) {
256             throw e;
257         } catch (Exception e) {
258             Log.e(mTag, mSELabel + " rules not correctly initialized! " + e.getLocalizedMessage());
259             throw new AccessControlException(e.getLocalizedMessage());
260         } finally {
261             // Close previously opened channel
262             if (mArfChannel != null) mSEHandle.closeArfChannel();
263         }
264     }
265 }
266