• 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 synchronized void close() {
93         synchronized (mLock) {
94             if (isBasicChannel()) {
95                 Log.i(mTag, "Close basic channel - Select without AID ...");
96                 mTerminal.selectDefaultApplication();
97             }
98 
99             mTerminal.closeChannel(this);
100             mIsClosed = true;
101             if (mBinder != null) {
102                 mBinder.unlinkToDeath(this, 0);
103             }
104             if (mSession != null) {
105                 mSession.removeChannel(this);
106             }
107         }
108     }
109 
110     /**
111      * Transmits the given byte and returns the response.
112      */
transmit(byte[] command)113     public byte[] transmit(byte[] command) throws IOException {
114         if (isClosed()) {
115             throw new IllegalStateException("Channel is closed");
116         }
117         if (command == null) {
118             throw new NullPointerException("Command must not be null");
119         }
120         if (mChannelAccess == null) {
121             throw new SecurityException("Channel access not set");
122         }
123         if (mChannelAccess.getCallingPid() != mCallingPid) {
124             throw new SecurityException("Wrong Caller PID.");
125         }
126 
127         // Validate the APDU command format and throw IllegalArgumentException, if necessary.
128         CommandApduValidator.execute(command);
129 
130         if (((command[0] & (byte) 0x80) == 0)
131                 && ((command[0] & (byte) 0x60) != (byte) 0x20)) {
132             // ISO command
133             if (command[1] == (byte) 0x70) {
134                 throw new SecurityException("MANAGE CHANNEL command not allowed");
135             }
136             if ((command[1] == (byte) 0xA4) && (command[2] == (byte) 0x04)) {
137                 throw new SecurityException("SELECT by DF name command not allowed");
138             }
139         }
140 
141         checkCommand(command);
142         synchronized (mLock) {
143             // set channel number bits
144             command[0] = setChannelToClassByte(command[0], mChannelNumber);
145             return mTerminal.transmit(command);
146         }
147     }
148 
selectNext()149     private boolean selectNext() throws IOException {
150         if (isClosed()) {
151             throw new IllegalStateException("Channel is closed");
152         } else if (mChannelAccess == null) {
153             throw new IllegalStateException("Channel access not set.");
154         } else if (mChannelAccess.getCallingPid() != mCallingPid) {
155             throw new SecurityException("Wrong Caller PID.");
156         } else if (mAid == null || mAid.length == 0) {
157             throw new UnsupportedOperationException("No aid given");
158         }
159 
160         byte[] selectCommand = new byte[5 + mAid.length];
161         selectCommand[0] = 0x00;
162         selectCommand[1] = (byte) 0xA4;
163         selectCommand[2] = 0x04;
164         selectCommand[3] = 0x02; // next occurrence
165         selectCommand[4] = (byte) mAid.length;
166         System.arraycopy(mAid, 0, selectCommand, 5, mAid.length);
167 
168         // set channel number bits
169         selectCommand[0] = setChannelToClassByte(selectCommand[0], mChannelNumber);
170 
171         byte[] bufferSelectResponse = mTerminal.transmit(selectCommand);
172 
173         if (bufferSelectResponse.length < 2) {
174             throw new UnsupportedOperationException("Transmit failed");
175         }
176         int sw1 = bufferSelectResponse[bufferSelectResponse.length - 2] & 0xFF;
177         int sw2 = bufferSelectResponse[bufferSelectResponse.length - 1] & 0xFF;
178         int sw = (sw1 << 8) | sw2;
179 
180         if (((sw & 0xF000) == 0x9000) || ((sw & 0xFF00) == 0x6200)
181                 || ((sw & 0xFF00) == 0x6300)) {
182             mSelectResponse = bufferSelectResponse.clone();
183             return true;
184         } else if ((sw & 0xFF00) == 0x6A00) {
185             return false;
186         } else {
187             throw new UnsupportedOperationException("Unsupported operation");
188         }
189     }
190 
191     /**
192      * Returns a copy of the given CLA byte where the channel number bits are set
193      * as specified by the given channel number
194      *
195      * <p>See GlobalPlatform Card Specification 2.2.0.7: 11.1.4 Class Byte Coding
196      *
197      * @param cla           the CLA byte. Won't be modified
198      * @param channelNumber within [0..3] (for first inter-industry class byte
199      *                      coding) or [4..19] (for further inter-industry class byte coding)
200      * @return the CLA byte with set channel number bits. The seventh bit
201      * indicating the used coding
202      * (first/further interindustry class byte coding) might be modified
203      */
setChannelToClassByte(byte cla, int channelNumber)204     private byte setChannelToClassByte(byte cla, int channelNumber) {
205         if (channelNumber < 4) {
206             // b7 = 0 indicates the first interindustry class byte coding
207             cla = (byte) ((cla & 0xBC) | channelNumber);
208         } else if (channelNumber < 20) {
209             // b7 = 1 indicates the further interindustry class byte coding
210             boolean isSm = (cla & 0x0C) != 0;
211             cla = (byte) ((cla & 0xB0) | 0x40 | (channelNumber - 4));
212             if (isSm) {
213                 cla |= 0x20;
214             }
215         } else {
216             throw new IllegalArgumentException("Channel number must be within [0..19]");
217         }
218         return cla;
219     }
220 
getChannelAccess()221     public ChannelAccess getChannelAccess() {
222         return this.mChannelAccess;
223     }
224 
setChannelAccess(ChannelAccess channelAccess)225     public void setChannelAccess(ChannelAccess channelAccess) {
226         this.mChannelAccess = channelAccess;
227     }
228 
setCallingPid(int pid)229     private void setCallingPid(int pid) {
230         mCallingPid = pid;
231     }
232 
checkCommand(byte[] command)233     private void checkCommand(byte[] command) {
234         if (mTerminal.getAccessControlEnforcer() != null) {
235             // check command if it complies to the access rules.
236             // if not an exception is thrown
237             mTerminal.getAccessControlEnforcer().checkCommand(this, command);
238         } else {
239             throw new SecurityException("Access Controller not set for Terminal: "
240                     + mTerminal.getName());
241         }
242     }
243 
244     /**
245      * true if aid could be selected during opening the channel
246      * false if aid could not be or was not selected.
247      *
248      * @return boolean.
249      */
hasSelectedAid()250     public boolean hasSelectedAid() {
251         return (mAid != null);
252     }
253 
getChannelNumber()254     public int getChannelNumber() {
255         return mChannelNumber;
256     }
257 
258     /**
259      * Returns the data as received from the application select command
260      * inclusively the status word.
261      *
262      * The returned byte array contains the data bytes in the following order:
263      * first data byte, ... , last data byte, sw1, sw2
264      *
265      * @return null if an application SELECT command has not been performed or
266      * the selection response can not be retrieved by the reader
267      * implementation.
268      */
getSelectResponse()269     public byte[] getSelectResponse() {
270         return (hasSelectedAid() ? mSelectResponse : null);
271     }
272 
isBasicChannel()273     public boolean isBasicChannel() {
274         return (mChannelNumber == 0) ? true : false;
275     }
276 
isClosed()277     public boolean isClosed() {
278         return mIsClosed;
279     }
280 
281     // Implementation of the SecureElement Channel interface according to OMAPI.
282     final class SecureElementChannel extends ISecureElementChannel.Stub {
283 
284         @Override
close()285         public void close() throws RemoteException {
286             Channel.this.close();
287         }
288 
289         @Override
isClosed()290         public boolean isClosed() throws RemoteException {
291             return Channel.this.isClosed();
292         }
293 
294         @Override
isBasicChannel()295         public boolean isBasicChannel() throws RemoteException {
296             return Channel.this.isBasicChannel();
297         }
298 
299         @Override
getSelectResponse()300         public byte[] getSelectResponse() throws RemoteException {
301             return Channel.this.getSelectResponse();
302         }
303 
304         @Override
transmit(byte[] command)305         public byte[] transmit(byte[] command) throws RemoteException {
306             Channel.this.setCallingPid(Binder.getCallingPid());
307             try {
308                 return Channel.this.transmit(command);
309             } catch (IOException e) {
310                 throw new ServiceSpecificException(SEService.IO_ERROR, e.getMessage());
311             }
312         }
313 
314         @Override
selectNext()315         public boolean selectNext() throws RemoteException {
316             Channel.this.setCallingPid(Binder.getCallingPid());
317             try {
318                 return Channel.this.selectNext();
319             } catch (IOException e) {
320                 throw new ServiceSpecificException(SEService.IO_ERROR, e.getMessage());
321             }
322         }
323     }
324 }
325