• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  * Contributed by: Giesecke & Devrient GmbH.
21  */
22 
23 package com.android.se;
24 
25 import android.os.Binder;
26 import android.os.IBinder;
27 import android.os.RemoteException;
28 import android.os.ServiceSpecificException;
29 import android.se.omapi.ISecureElementChannel;
30 import android.se.omapi.ISecureElementListener;
31 import android.se.omapi.SEService;
32 import android.util.Log;
33 
34 import com.android.se.SecureElementService.SecureElementSession;
35 import com.android.se.security.ChannelAccess;
36 
37 import java.io.IOException;
38 
39 /**
40  * Represents a Channel opened with the Secure Element
41  */
42 public class Channel implements IBinder.DeathRecipient {
43 
44     private final String mTag = "SecureElement-Channel";
45     private final int mChannelNumber;
46     private final Object mLock = new Object();
47     private IBinder mBinder = null;
48     private boolean mIsClosed;
49     private SecureElementSession mSession;
50     private Terminal mTerminal;
51     private byte[] mSelectResponse;
52     private ChannelAccess mChannelAccess = null;
53     private int mCallingPid = 0;
54     private byte[] mAid = null;
55 
Channel(SecureElementSession session, Terminal terminal, int channelNumber, byte[] selectResponse, byte[] aid, ISecureElementListener listener)56     Channel(SecureElementSession session, Terminal terminal, int channelNumber,
57             byte[] selectResponse, byte[] aid, ISecureElementListener listener) {
58         if (terminal == null) {
59             throw new IllegalArgumentException("Arguments can't be null");
60         }
61         mSession = session;
62         mTerminal = terminal;
63         mIsClosed = false;
64         mSelectResponse = selectResponse;
65         mChannelNumber = channelNumber;
66         mAid = aid;
67         if (listener != null) {
68             try {
69                 mBinder = listener.asBinder();
70                 mBinder.linkToDeath(this, 0);
71             } catch (RemoteException e) {
72                 Log.e(mTag, "Failed to register client listener");
73             }
74         }
75     }
76 
77     /**
78      * Close this channel if the client died.
79      */
binderDied()80     public void binderDied() {
81         try {
82             Log.e(mTag, Thread.currentThread().getName() + " Client "
83                     + mBinder.toString() + " died");
84             close();
85         } catch (Exception ignore) {
86         }
87     }
88 
89     /**
90      * Closes the channel.
91      */
close()92     public void close() {
93         synchronized (mLock) {
94             if (isClosed())
95                 return;
96             mIsClosed = true;
97         }
98         if (isBasicChannel()) {
99             Log.i(mTag, "Close basic channel - Select without AID ...");
100             mTerminal.selectDefaultApplication();
101         }
102 
103         mTerminal.closeChannel(this);
104         if (mBinder != null) {
105             mBinder.unlinkToDeath(this, 0);
106         }
107         if (mSession != null) {
108             mSession.removeChannel(this);
109         }
110     }
111 
112     /**
113      * Transmits the given byte and returns the response.
114      */
transmit(byte[] command)115     public byte[] transmit(byte[] command) throws IOException {
116         if (isClosed()) {
117             throw new IllegalStateException("Channel is closed");
118         }
119         if (command == null) {
120             throw new NullPointerException("Command must not be null");
121         }
122         if (mChannelAccess == null) {
123             throw new SecurityException("Channel access not set");
124         }
125         if (mChannelAccess.getCallingPid() != mCallingPid) {
126             throw new SecurityException("Wrong Caller PID.");
127         }
128 
129         // Validate the APDU command format and throw IllegalArgumentException, if necessary.
130         CommandApduValidator.execute(command);
131 
132         if (((command[0] & (byte) 0x80) == 0)
133                 && ((command[0] & (byte) 0x60) != (byte) 0x20)) {
134             // ISO command
135             if (command[1] == (byte) 0x70) {
136                 throw new SecurityException("MANAGE CHANNEL command not allowed");
137             }
138             if ((command[1] == (byte) 0xA4) && (command[2] == (byte) 0x04)) {
139                 // SELECT by DF name is only allowed for CarrierPrivilege applications
140                 // or system privilege applications
141                 if (ChannelAccess.ACCESS.ALLOWED != mChannelAccess.getPrivilegeAccess()) {
142                     throw new SecurityException("SELECT by DF name command not allowed");
143                 }
144             }
145         }
146 
147         checkCommand(command);
148         synchronized (mLock) {
149             // set channel number bits
150             command[0] = setChannelToClassByte(command[0], mChannelNumber);
151             return mTerminal.transmit(command);
152         }
153     }
154 
selectNext()155     private boolean selectNext() throws IOException {
156         if (isClosed()) {
157             throw new IllegalStateException("Channel is closed");
158         } else if (mChannelAccess == null) {
159             throw new IllegalStateException("Channel access not set.");
160         } else if (mChannelAccess.getCallingPid() != mCallingPid) {
161             throw new SecurityException("Wrong Caller PID.");
162         } else if (mAid == null || mAid.length == 0) {
163             throw new UnsupportedOperationException("No aid given");
164         }
165 
166         byte[] selectCommand = new byte[5 + mAid.length];
167         selectCommand[0] = 0x00;
168         selectCommand[1] = (byte) 0xA4;
169         selectCommand[2] = 0x04;
170         selectCommand[3] = 0x02; // next occurrence
171         selectCommand[4] = (byte) mAid.length;
172         System.arraycopy(mAid, 0, selectCommand, 5, mAid.length);
173 
174         // set channel number bits
175         selectCommand[0] = setChannelToClassByte(selectCommand[0], mChannelNumber);
176 
177         byte[] bufferSelectResponse = mTerminal.transmit(selectCommand);
178 
179         if (bufferSelectResponse.length < 2) {
180             throw new UnsupportedOperationException("Transmit failed");
181         }
182         int sw1 = bufferSelectResponse[bufferSelectResponse.length - 2] & 0xFF;
183         int sw2 = bufferSelectResponse[bufferSelectResponse.length - 1] & 0xFF;
184         int sw = (sw1 << 8) | sw2;
185 
186         if (((sw & 0xF000) == 0x9000) || ((sw & 0xFF00) == 0x6200)
187                 || ((sw & 0xFF00) == 0x6300)) {
188             mSelectResponse = bufferSelectResponse.clone();
189             return true;
190         } else if ((sw & 0xFF00) == 0x6A00) {
191             return false;
192         } else {
193             throw new UnsupportedOperationException("Unsupported operation");
194         }
195     }
196 
197     /**
198      * Returns a copy of the given CLA byte where the channel number bits are set
199      * as specified by the given channel number
200      *
201      * <p>See GlobalPlatform Card Specification 2.2.0.7: 11.1.4 Class Byte Coding
202      *
203      * @param cla           the CLA byte. Won't be modified
204      * @param channelNumber within [0..3] (for first inter-industry class byte
205      *                      coding) or [4..19] (for further inter-industry class byte coding)
206      * @return the CLA byte with set channel number bits. The seventh bit
207      * indicating the used coding
208      * (first/further interindustry class byte coding) might be modified
209      */
setChannelToClassByte(byte cla, int channelNumber)210     private byte setChannelToClassByte(byte cla, int channelNumber) {
211         if (channelNumber < 4) {
212             // b7 = 0 indicates the first interindustry class byte coding
213             cla = (byte) ((cla & 0xBC) | channelNumber);
214         } else if (channelNumber < 20) {
215             // b7 = 1 indicates the further interindustry class byte coding
216             boolean isSm = (cla & 0x0C) != 0;
217             cla = (byte) ((cla & 0xB0) | 0x40 | (channelNumber - 4));
218             if (isSm) {
219                 cla |= 0x20;
220             }
221         } else {
222             throw new IllegalArgumentException("Channel number must be within [0..19]");
223         }
224         return cla;
225     }
226 
getChannelAccess()227     public ChannelAccess getChannelAccess() {
228         return this.mChannelAccess;
229     }
230 
setChannelAccess(ChannelAccess channelAccess)231     public void setChannelAccess(ChannelAccess channelAccess) {
232         this.mChannelAccess = channelAccess;
233     }
234 
setCallingPid(int pid)235     private void setCallingPid(int pid) {
236         mCallingPid = pid;
237     }
238 
checkCommand(byte[] command)239     private void checkCommand(byte[] command) {
240         if (mTerminal.getAccessControlEnforcer() != null) {
241             // check command if it complies to the access rules.
242             // if not an exception is thrown
243             mTerminal.getAccessControlEnforcer().checkCommand(this, command);
244         } else {
245             // Allow access to Privileged App even if Access Control Enforcer is
246             // not initialized
247             if (ChannelAccess.ACCESS.ALLOWED != mChannelAccess.getPrivilegeAccess()) {
248                 throw new SecurityException("Access Controller not set for Terminal: "
249                         + mTerminal.getName());
250             }
251         }
252     }
253 
254     /**
255      * true if aid could be selected during opening the channel
256      * false if aid could not be or was not selected.
257      *
258      * @return boolean.
259      */
hasSelectedAid()260     public boolean hasSelectedAid() {
261         return (mAid != null);
262     }
263 
getChannelNumber()264     public int getChannelNumber() {
265         return mChannelNumber;
266     }
267 
268     /**
269      * Returns the data as received from the application select command
270      * inclusively the status word.
271      *
272      * The returned byte array contains the data bytes in the following order:
273      * first data byte, ... , last data byte, sw1, sw2
274      *
275      * @return null if an application SELECT command has not been performed or
276      * the selection response can not be retrieved by the reader
277      * implementation.
278      */
getSelectResponse()279     public byte[] getSelectResponse() {
280         return (hasSelectedAid() ? mSelectResponse : null);
281     }
282 
isBasicChannel()283     public boolean isBasicChannel() {
284         return (mChannelNumber == 0) ? true : false;
285     }
286 
isClosed()287     public boolean isClosed() {
288         return mIsClosed;
289     }
290 
291     // Implementation of the SecureElement Channel interface according to OMAPI.
292     final class SecureElementChannel extends ISecureElementChannel.Stub {
293 
294         @Override
close()295         public void close() throws RemoteException {
296             Channel.this.close();
297         }
298 
299         @Override
isClosed()300         public boolean isClosed() throws RemoteException {
301             return Channel.this.isClosed();
302         }
303 
304         @Override
isBasicChannel()305         public boolean isBasicChannel() throws RemoteException {
306             return Channel.this.isBasicChannel();
307         }
308 
309         @Override
getSelectResponse()310         public byte[] getSelectResponse() throws RemoteException {
311             return Channel.this.getSelectResponse();
312         }
313 
314         @Override
transmit(byte[] command)315         public byte[] transmit(byte[] command) throws RemoteException {
316             Channel.this.setCallingPid(Binder.getCallingPid());
317             try {
318                 return Channel.this.transmit(command);
319             } catch (IOException e) {
320                 throw new ServiceSpecificException(SEService.IO_ERROR, e.getMessage());
321             }
322         }
323 
324         @Override
selectNext()325         public boolean selectNext() throws RemoteException {
326             Channel.this.setCallingPid(Binder.getCallingPid());
327             try {
328                 return Channel.this.selectNext();
329             } catch (IOException e) {
330                 throw new ServiceSpecificException(SEService.IO_ERROR, e.getMessage());
331             }
332         }
333     }
334 }
335