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) 2015-2017, The Linux Foundation.
18  */
19 
20 /*
21  * Copyright 2012 Giesecke & Devrient GmbH.
22  *
23  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
24  * use this file except in compliance with the License. You may obtain a copy of
25  * 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, WITHOUT
31  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
32  * License for the specific language governing permissions and limitations under
33  * the License.
34  */
35 
36 package com.android.se.security.ara;
37 
38 import android.content.Context;
39 import android.text.TextUtils;
40 import android.util.Log;
41 
42 import com.android.se.Channel;
43 import com.android.se.R;
44 import com.android.se.SecureElementService;
45 import com.android.se.Terminal;
46 import com.android.se.internal.ByteArrayConverter;
47 import com.android.se.security.AccessRuleCache;
48 import com.android.se.security.ChannelAccess;
49 import com.android.se.security.gpac.BerTlv;
50 import com.android.se.security.gpac.ParserException;
51 import com.android.se.security.gpac.REF_AR_DO;
52 import com.android.se.security.gpac.Response_ALL_AR_DO;
53 import com.android.se.security.gpac.Response_DO_Factory;
54 
55 import java.io.IOException;
56 import java.security.AccessControlException;
57 import java.util.ArrayList;
58 import java.util.Iterator;
59 import java.util.MissingResourceException;
60 import java.util.NoSuchElementException;
61 
62 /** Reads and Maintains the ARA access for the Secure Element */
63 public class AraController {
64 
65     public static final byte[] ARA_M_AID =
66             new byte[]{
67                     (byte) 0xA0,
68                     (byte) 0x00,
69                     (byte) 0x00,
70                     (byte) 0x01,
71                     (byte) 0x51,
72                     (byte) 0x41,
73                     (byte) 0x43,
74                     (byte) 0x4C,
75                     (byte) 0x00
76             };
77     private final String mTag = "SecureElement-AraController";
78     private AccessRuleCache mAccessRuleCache = null;
79     private Terminal mTerminal = null;
80     private AccessRuleApplet mApplet = null;
81     private Context mContext = null;
82     private String[] mAids = new String[0];
83     private byte[] mAccessControlAid = ARA_M_AID;
84 
AraController(AccessRuleCache cache, Terminal terminal)85     public AraController(AccessRuleCache cache, Terminal terminal) {
86         mAccessRuleCache = cache;
87         mTerminal = terminal;
88         mContext = mTerminal.getContext();
89         if (mTerminal.getName().startsWith(SecureElementService.ESE_TERMINAL)) {
90             mAids = mContext.getResources().getStringArray(
91                     R.array.config_ara_aid_candidate_list_ese);
92         }
93     }
94 
getAraMAid()95     public static byte[] getAraMAid() {
96         return ARA_M_AID;
97     }
98 
getAccessControlAid()99     public byte[] getAccessControlAid() {
100         return mAccessControlAid;
101     }
102 
103     /**
104      * Initialize the AraController, reads the refresh tag.
105      * and fetch the access rules
106      */
initialize()107     public synchronized void initialize() throws IOException, NoSuchElementException {
108         Channel channel = null;
109         byte[] aid = null;
110 
111         // try to fetch access rules from ARA Aid list
112         for (String araAid : mAids) {
113             if (!TextUtils.isEmpty(araAid)) {
114                 aid = ByteArrayConverter.hexStringToByteArray(araAid);
115             } else {
116                 aid = null;
117             }
118             try {
119                 channel = mTerminal.openLogicalChannelWithoutChannelAccess(aid);
120                 if (channel == null) {
121                     throw new MissingResourceException("could not open channel", "", "");
122                 }
123                 mAccessControlAid = aid;
124                 break;
125             } catch (NoSuchElementException e) {
126                 Log.i(mTag, "applet:" + araAid + " is not accessible");
127                 continue;
128             }
129         }
130 
131         // try to fetch access rules from ARA-M if all ARA in Aid list is not accessible
132         if (channel == null) {
133             channel = mTerminal.openLogicalChannelWithoutChannelAccess(getAraMAid());
134             if (channel == null) {
135                 throw new MissingResourceException("could not open channel", "", "");
136             }
137         }
138 
139         // set access conditions to access ARA.
140         ChannelAccess araChannelAccess = new ChannelAccess();
141         araChannelAccess.setAccess(ChannelAccess.ACCESS.ALLOWED, "");
142         araChannelAccess.setApduAccess(ChannelAccess.ACCESS.ALLOWED);
143         channel.setChannelAccess(araChannelAccess);
144 
145         try {
146             // set new applet handler since a new channel is used.
147             mApplet = new AccessRuleApplet(channel);
148             byte[] tag = mApplet.readRefreshTag();
149             // if refresh tag is equal to the previous one it is not
150             // necessary to read all rules again.
151             if (mAccessRuleCache.isRefreshTagEqual(tag)) {
152                 Log.i(mTag, "Refresh tag unchanged. Using access rules from cache.");
153                 return;
154             }
155             Log.i(mTag, "Refresh tag has changed.");
156             // set new refresh tag and empty cache.
157             mAccessRuleCache.setRefreshTag(tag);
158             mAccessRuleCache.clearCache();
159 
160             Log.i(mTag, "Read ARs from ARA");
161             readAllAccessRules();
162         } catch (IOException e) {
163             // Some kind of communication problem happened while transmit() was executed.
164             // IOError shall be notified to the client application in this case.
165             throw e;
166         } catch (Exception e) {
167             Log.i(mTag, "ARA error: " + e.getLocalizedMessage());
168             throw new AccessControlException(e.getLocalizedMessage());
169         } finally {
170             if (channel != null) {
171                 channel.close();
172             }
173         }
174     }
175 
readAllAccessRules()176     private void readAllAccessRules() throws AccessControlException, IOException {
177         try {
178             byte[] data = mApplet.readAllAccessRules();
179             // no data returned, but no exception
180             // -> no rule.
181             if (data == null) {
182                 return;
183             }
184 
185             BerTlv tlv = Response_DO_Factory.createDO(data);
186             if (tlv == null || !(tlv instanceof Response_ALL_AR_DO)) {
187                 throw new AccessControlException("No valid data object found");
188             }
189             ArrayList<REF_AR_DO> array = ((Response_ALL_AR_DO) tlv).getRefArDos();
190 
191             if (array != null && array.size() != 0) {
192                 Iterator<REF_AR_DO> iter = array.iterator();
193                 while (iter.hasNext()) {
194                     REF_AR_DO refArDo = iter.next();
195                     mAccessRuleCache.putWithMerge(refArDo.getRefDo(), refArDo.getArDo());
196                 }
197             }
198         } catch (ParserException e) {
199             throw new AccessControlException("Parsing Data Object Exception: "
200                     + e.getMessage());
201         }
202     }
203 }
204