From 890c3c0d321c3b59ec1224d4630b3f2401ef94e7 Mon Sep 17 00:00:00 2001 From: tkellner Date: Tue, 7 Jan 2014 17:28:54 +0000 Subject: Add custom PC/SC library for MacOS X from OpeneCard project git-svn-id: https://joinup.ec.europa.eu/svn/mocca/trunk@1276 8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4 --- .../java/org/openecard/scio/osx/ChannelImpl.java | 298 +++++++++++++++++++++ 1 file changed, 298 insertions(+) create mode 100644 smcc/src/main/java/org/openecard/scio/osx/ChannelImpl.java (limited to 'smcc/src/main/java/org/openecard/scio/osx/ChannelImpl.java') diff --git a/smcc/src/main/java/org/openecard/scio/osx/ChannelImpl.java b/smcc/src/main/java/org/openecard/scio/osx/ChannelImpl.java new file mode 100644 index 00000000..1094fb9d --- /dev/null +++ b/smcc/src/main/java/org/openecard/scio/osx/ChannelImpl.java @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package org.openecard.scio.osx; + +import static org.openecard.scio.osx.PCSC.SCardTransmit; +import static org.openecard.scio.osx.PlatformPCSC.SCARD_PROTOCOL_T0; +import static org.openecard.scio.osx.PlatformPCSC.SCARD_PROTOCOL_T1; + +import java.nio.ByteBuffer; +import java.nio.ReadOnlyBufferException; +import java.security.AccessController; +import javax.smartcardio.Card; +import javax.smartcardio.CardChannel; +import javax.smartcardio.CardException; +import javax.smartcardio.CommandAPDU; +import javax.smartcardio.ResponseAPDU; + +import sun.security.action.GetPropertyAction; + +/** + * CardChannel implementation. + * + * @since 1.6 + * @author Andreas Sterbenz + */ +final class ChannelImpl extends CardChannel { + + // the card this channel is associated with + private final CardImpl card; + + // the channel number, 0 for the basic logical channel + private final int channel; + + // whether this channel has been closed. only logical channels can be closed + private volatile boolean isClosed; + + ChannelImpl(CardImpl card, int channel) { + this.card = card; + this.channel = channel; + } + + void checkClosed() { + card.checkState(); + if (isClosed) { + throw new IllegalStateException("Logical channel has been closed"); + } + } + + public Card getCard() { + return card; + } + + public int getChannelNumber() { + checkClosed(); + return channel; + } + + private static void checkManageChannel(byte[] b) { + if (b.length < 4) { + throw new IllegalArgumentException( + "Command APDU must be at least 4 bytes long"); + } + if ((b[0] >= 0) && (b[1] == 0x70)) { + throw new IllegalArgumentException( + "Manage channel command not allowed, use openLogicalChannel()"); + } + } + + public ResponseAPDU transmit(CommandAPDU command) throws CardException { + checkClosed(); + card.checkExclusive(); + byte[] commandBytes = command.getBytes(); + byte[] responseBytes = doTransmit(commandBytes); + return new ResponseAPDU(responseBytes); + } + + public int transmit(ByteBuffer command, ByteBuffer response) + throws CardException { + checkClosed(); + card.checkExclusive(); + if ((command == null) || (response == null)) { + throw new NullPointerException(); + } + if (response.isReadOnly()) { + throw new ReadOnlyBufferException(); + } + if (command == response) { + throw new IllegalArgumentException( + "command and response must not be the same object"); + } + if (response.remaining() < 258) { + throw new IllegalArgumentException( + "Insufficient space in response buffer"); + } + byte[] commandBytes = new byte[command.remaining()]; + command.get(commandBytes); + byte[] responseBytes = doTransmit(commandBytes); + response.put(responseBytes); + return responseBytes.length; + } + + private final static boolean t0GetResponse = getBooleanProperty( + "sun.security.smartcardio.t0GetResponse", true); + + private final static boolean t1GetResponse = getBooleanProperty( + "sun.security.smartcardio.t1GetResponse", true); + + private final static boolean t1StripLe = getBooleanProperty( + "sun.security.smartcardio.t1StripLe", false); + + private static boolean getBooleanProperty(String name, boolean def) { + String val = AccessController.doPrivileged(new GetPropertyAction(name)); + if (val == null) { + return def; + } + if (val.equalsIgnoreCase("true")) { + return true; + } else if (val.equalsIgnoreCase("false")) { + return false; + } else { + throw new IllegalArgumentException(name + + " must be either 'true' or 'false'"); + } + } + + private byte[] concat(byte[] b1, byte[] b2, int n2) { + int n1 = b1.length; + if ((n1 == 0) && (n2 == b2.length)) { + return b2; + } + byte[] res = new byte[n1 + n2]; + System.arraycopy(b1, 0, res, 0, n1); + System.arraycopy(b2, 0, res, n1, n2); + return res; + } + + private final static byte[] B0 = new byte[0]; + + private byte[] doTransmit(byte[] command) throws CardException { + // note that we modify the 'command' array in some cases, so it must + // be a copy of the application provided data. + try { + checkManageChannel(command); + setChannel(command); + int n = command.length; + boolean t0 = card.protocol == SCARD_PROTOCOL_T0; + boolean t1 = card.protocol == SCARD_PROTOCOL_T1; + if (t0 && (n >= 7) && (command[4] == 0)) { + throw new CardException("Extended length forms not supported for T=0"); + } + if ((t0 || (t1 && t1StripLe)) && (n >= 7)) { + int lc = command[4] & 0xff; + if (lc != 0) { + if (n == lc + 6) { + n--; + } + } else { + lc = ((command[5] & 0xff) << 8) | (command[6] & 0xff); + if (n == lc + 9) { + n -= 2; + } + } + } + boolean getresponse = (t0 && t0GetResponse) || (t1 && t1GetResponse); + int k = 0; + byte[] result = B0; + while (true) { + if (++k >= 32) { + throw new CardException("Could not obtain response"); + } + byte[] response = SCardTransmit(card.cardId, card.protocol, command, 0, + n); + int rn = response.length; + if (getresponse && (rn >= 2)) { + // see ISO 7816/2005, 5.1.3 + if ((rn == 2) && (response[0] == 0x6c)) { + // Resend command using SW2 as short Le field + command[n - 1] = response[1]; + continue; + } + if (response[rn - 2] == 0x61) { + // Issue a GET RESPONSE command with the same CLA + // using SW2 as short Le field + if (rn > 2) { + result = concat(result, response, rn - 2); + } + command[1] = (byte) 0xC0; + command[2] = 0; + command[3] = 0; + command[4] = response[rn - 1]; + n = 5; + continue; + } + + } + result = concat(result, response, rn); + break; + } + return result; + } catch (PCSCException e) { + card.handleError(e); + throw new CardException(e); + } + } + + private static int getSW(byte[] res) throws CardException { + if (res.length < 2) { + throw new CardException("Invalid response length: " + res.length); + } + int sw1 = res[res.length - 2] & 0xff; + int sw2 = res[res.length - 1] & 0xff; + return (sw1 << 8) | sw2; + } + + private static boolean isOK(byte[] res) throws CardException { + return (res.length == 2) && (getSW(res) == 0x9000); + } + + private void setChannel(byte[] com) { + int cla = com[0]; + if (cla < 0) { + // proprietary class format, cannot set or check logical channel + // for now, just return + return; + } + // classes 001x xxxx is reserved for future use in ISO, ignore + if ((cla & 0xe0) == 0x20) { + return; + } + // see ISO 7816/2005, table 2 and 3 + if (channel <= 3) { + // mask of bits 7, 1, 0 (channel number) + // 0xbc == 1011 1100 + com[0] &= 0xbc; + com[0] |= channel; + } else if (channel <= 19) { + // mask of bits 7, 3, 2, 1, 0 (channel number) + // 0xbc == 1011 0000 + com[0] &= 0xb0; + com[0] |= 0x40; + com[0] |= (channel - 4); + } else { + throw new RuntimeException("Unsupported channel number: " + channel); + } + } + + public void close() throws CardException { + if (getChannelNumber() == 0) { + throw new IllegalStateException("Cannot close basic logical channel"); + } + if (isClosed) { + return; + } + card.checkExclusive(); + try { + byte[] com = new byte[] { 0x00, 0x70, (byte) 0x80, 0 }; + com[3] = (byte) getChannelNumber(); + setChannel(com); + byte[] res = SCardTransmit(card.cardId, card.protocol, com, 0, com.length); + if (isOK(res) == false) { + throw new CardException("close() failed: " + PCSC.toString(res)); + } + } catch (PCSCException e) { + card.handleError(e); + throw new CardException("Could not close channel", e); + } finally { + isClosed = true; + } + } + + public String toString() { + return "PC/SC channel " + channel; + } + +} -- cgit v1.2.3