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