/****************************************************************************
* Copyright (C) 2012 ecsec GmbH.
* All rights reserved.
* Contact: ecsec GmbH (info@ecsec.de)
*
* This file is part of the Open eCard App.
*
* GNU General Public License Usage
* This file may be used under the terms of the GNU General Public
* License version 3.0 as published by the Free Software Foundation
* and appearing in the file LICENSE.GPL included in the packaging of
* this file. Please review the following information to ensure the
* GNU General Public License version 3.0 requirements will be met:
* http://www.gnu.org/copyleft/gpl.html.
*
* Other Usage
* Alternatively, this file may be used in accordance with the terms
* and conditions contained in a signed written agreement between
* you and ecsec GmbH.
*
***************************************************************************/
package org.openecard.ifd.scio;
import iso.std.iso_iec._24727.tech.schema.BeginTransaction;
import iso.std.iso_iec._24727.tech.schema.BeginTransactionResponse;
import iso.std.iso_iec._24727.tech.schema.Cancel;
import iso.std.iso_iec._24727.tech.schema.CancelResponse;
import iso.std.iso_iec._24727.tech.schema.ChannelHandleType;
import iso.std.iso_iec._24727.tech.schema.Connect;
import iso.std.iso_iec._24727.tech.schema.ConnectResponse;
import iso.std.iso_iec._24727.tech.schema.ControlIFD;
import iso.std.iso_iec._24727.tech.schema.ControlIFDResponse;
import iso.std.iso_iec._24727.tech.schema.DIDAuthenticationDataType;
import iso.std.iso_iec._24727.tech.schema.DestroyChannel;
import iso.std.iso_iec._24727.tech.schema.DestroyChannelResponse;
import iso.std.iso_iec._24727.tech.schema.Disconnect;
import iso.std.iso_iec._24727.tech.schema.DisconnectResponse;
import iso.std.iso_iec._24727.tech.schema.DisplayCapabilityType;
import iso.std.iso_iec._24727.tech.schema.EndTransaction;
import iso.std.iso_iec._24727.tech.schema.EndTransactionResponse;
import iso.std.iso_iec._24727.tech.schema.EstablishChannel;
import iso.std.iso_iec._24727.tech.schema.EstablishChannelResponse;
import iso.std.iso_iec._24727.tech.schema.EstablishContext;
import iso.std.iso_iec._24727.tech.schema.EstablishContextResponse;
import iso.std.iso_iec._24727.tech.schema.GetIFDCapabilities;
import iso.std.iso_iec._24727.tech.schema.GetIFDCapabilitiesResponse;
import iso.std.iso_iec._24727.tech.schema.GetStatus;
import iso.std.iso_iec._24727.tech.schema.GetStatusResponse;
import iso.std.iso_iec._24727.tech.schema.IFDCapabilitiesType;
import iso.std.iso_iec._24727.tech.schema.IFDStatusType;
import iso.std.iso_iec._24727.tech.schema.InputAPDUInfoType;
import iso.std.iso_iec._24727.tech.schema.KeyPadCapabilityType;
import iso.std.iso_iec._24727.tech.schema.ListIFDs;
import iso.std.iso_iec._24727.tech.schema.ListIFDsResponse;
import iso.std.iso_iec._24727.tech.schema.ModifyVerificationData;
import iso.std.iso_iec._24727.tech.schema.ModifyVerificationDataResponse;
import iso.std.iso_iec._24727.tech.schema.Output;
import iso.std.iso_iec._24727.tech.schema.OutputInfoType;
import iso.std.iso_iec._24727.tech.schema.OutputResponse;
import iso.std.iso_iec._24727.tech.schema.ReleaseContext;
import iso.std.iso_iec._24727.tech.schema.ReleaseContextResponse;
import iso.std.iso_iec._24727.tech.schema.SlotCapabilityType;
import iso.std.iso_iec._24727.tech.schema.Transmit;
import iso.std.iso_iec._24727.tech.schema.TransmitResponse;
import iso.std.iso_iec._24727.tech.schema.VerifyUser;
import iso.std.iso_iec._24727.tech.schema.VerifyUserResponse;
import iso.std.iso_iec._24727.tech.schema.Wait;
import iso.std.iso_iec._24727.tech.schema.WaitResponse;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.atomic.AtomicInteger;
import javax.jws.WebService;
import javax.smartcardio.CardException;
import oasis.names.tc.dss._1_0.core.schema.Result;
import org.openecard.common.ECardConstants;
import org.openecard.common.WSHelper;
import org.openecard.common.ifd.PACECapabilities;
import org.openecard.common.ifd.Protocol;
import org.openecard.common.ifd.ProtocolFactory;
import org.openecard.common.ifd.anytype.PACEInputType;
import org.openecard.common.ifd.anytype.PACEOutputType;
import org.openecard.common.interfaces.Dispatcher;
import org.openecard.common.util.ByteUtils;
import org.openecard.common.util.ValueGenerators;
import org.openecard.gui.UserConsent;
import org.openecard.ifd.scio.reader.EstablishPACERequest;
import org.openecard.ifd.scio.reader.EstablishPACEResponse;
import org.openecard.ifd.scio.reader.ExecutePACERequest;
import org.openecard.ifd.scio.reader.ExecutePACEResponse;
import org.openecard.ifd.scio.reader.PCSCFeatures;
import org.openecard.ifd.scio.wrapper.SCCard;
import org.openecard.ifd.scio.wrapper.SCChannel;
import org.openecard.ifd.scio.wrapper.SCTerminal;
import org.openecard.ifd.scio.wrapper.SCWrapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* @author Tobias Wich <tobias.wich@ecsec.de>
*/
@WebService(endpointInterface = "org.openecard.ws.IFD")
public class IFD implements org.openecard.ws.IFD {
private static final Logger _logger = LoggerFactory.getLogger(IFD.class);
private byte[] ctxHandle = null;
private SCWrapper scwrapper;
private Dispatcher dispatcher;
private UserConsent gui = null;
private ProtocolFactories protocolFactories = new ProtocolFactories();
private AtomicInteger numClients;
private ExecutorService threadPool;
private ConcurrentSkipListMap<String,Future<List<IFDStatusType>>> asyncWaitThreads;
private Future<List<IFDStatusType>> syncWaitThread;
protected synchronized void removeAsnycTerminal(String session) {
if (asyncWaitThreads != null) { // be sure the list still exists
asyncWaitThreads.remove(session);
}
}
private boolean hasContext() {
Boolean hasContext = ctxHandle != null;
return hasContext.booleanValue();
}
public void setGUI(UserConsent gui) {
this.gui = gui;
}
public void setDispatcher(Dispatcher dispatcher) {
this.dispatcher = dispatcher;
}
public boolean addProtocol(String proto, ProtocolFactory factory) {
return protocolFactories.add(proto, factory);
}
@Override
public synchronized EstablishContextResponse establishContext(EstablishContext parameters) {
try {
// on first call, create a new unique handle
if (ctxHandle == null) {
scwrapper = new SCWrapper();
ctxHandle = scwrapper.createHandle(ECardConstants.CONTEXT_HANDLE_DEFAULT_SIZE);
numClients = new AtomicInteger(1);
threadPool = Executors.newCachedThreadPool();
asyncWaitThreads = new ConcurrentSkipListMap<String, Future<List<IFDStatusType>>>();
} else {
// on second or further calls, increment usage counter
numClients.incrementAndGet();
}
// prepare response
EstablishContextResponse response = WSHelper.makeResponse(EstablishContextResponse.class, WSHelper.makeResultOK());
response.setContextHandle(ctxHandle);
return response;
} catch (Exception ex) {
if (ex instanceof RuntimeException) {
throw (RuntimeException)ex;
}
_logger.warn(ex.getMessage(), ex);
return WSHelper.makeResponse(EstablishContextResponse.class, WSHelper.makeResult(ex));
}
}
@Override
public synchronized ReleaseContextResponse releaseContext(ReleaseContext parameters) {
try {
ReleaseContextResponse response;
if (ByteUtils.compare(ctxHandle, parameters.getContextHandle())) {
if (numClients.decrementAndGet() == 0) { // last client detaches
ctxHandle = null;
numClients = null;
// terminate thread pool
threadPool.shutdownNow(); // wait for threads to die and block new requests
// just assume it worked ... and don't wait
threadPool = null;
asyncWaitThreads = null;
}
response = WSHelper.makeResponse(ReleaseContextResponse.class, WSHelper.makeResultOK());
return response;
} else {
String msg = "Invalid context handle specified.";
Result r = WSHelper.makeResultError(ECardConstants.Minor.IFD.INVALID_CONTEXT_HANDLE, msg);
response = WSHelper.makeResponse(ReleaseContextResponse.class, r);
return response;
}
} catch (Exception ex) {
if (ex instanceof RuntimeException) {
throw (RuntimeException)ex;
}
_logger.warn(ex.getMessage(), ex);
return WSHelper.makeResponse(ReleaseContextResponse.class, WSHelper.makeResult(ex));
}
}
@Override
public ListIFDsResponse listIFDs(ListIFDs parameters) {
try {
ListIFDsResponse response;
if (! ByteUtils.compare(ctxHandle, parameters.getContextHandle())) {
String msg = "Invalid context handle specified.";
Result r = WSHelper.makeResultError(ECardConstants.Minor.IFD.INVALID_CONTEXT_HANDLE, msg);
response = WSHelper.makeResponse(ListIFDsResponse.class, r);
return response;
} else {
try {
List<String> ifds = scwrapper.getTerminalNames(true);
response = WSHelper.makeResponse(ListIFDsResponse.class, WSHelper.makeResultOK());
response.getIFDName().addAll(ifds);
return response;
} catch (IFDException ex) {
response = WSHelper.makeResponse(ListIFDsResponse.class, ex.getResult());
_logger.warn(ex.getMessage(), ex);
return response;
}
}
} catch (Exception ex) {
if (ex instanceof RuntimeException) {
throw (RuntimeException)ex;
}
_logger.warn(ex.getMessage(), ex);
return WSHelper.makeResponse(ListIFDsResponse.class, WSHelper.makeResult(ex));
}
}
private List<String> buildPACEProtocolList(List<PACECapabilities.PACECapability> paceCapabilities) {
List<String> supportedProtos = new LinkedList<String>();
for (PACECapabilities.PACECapability next : paceCapabilities) {
supportedProtos.add(next.getProtocol());
}
return supportedProtos;
}
@Override
public GetIFDCapabilitiesResponse getIFDCapabilities(GetIFDCapabilities parameters) {
try {
GetIFDCapabilitiesResponse response;
if (! ByteUtils.compare(ctxHandle, parameters.getContextHandle())) {
String msg = "Invalid context handle specified.";
Result r = WSHelper.makeResultError(ECardConstants.Minor.IFD.INVALID_CONTEXT_HANDLE, msg);
response = WSHelper.makeResponse(GetIFDCapabilitiesResponse.class, r);
return response;
} else {
try {
// get reader
SCTerminal t = scwrapper.getTerminal(parameters.getIFDName(), true);
// fetch general reader capabilities
IFDCapabilitiesType cap = new IFDCapabilitiesType();
cap.setAcousticSignalUnit(t.isAcousticSignal());
cap.setOpticalSignalUnit(t.isOpticalSignal());
DisplayCapabilityType dispCap = t.getDisplayCapability();
if (dispCap != null) {
cap.getDisplayCapability().add(dispCap);
}
KeyPadCapabilityType keyCap = t.getKeypadCapability();
if (keyCap != null) {
cap.getKeyPadCapability().add(keyCap);
}
// fetch capabilities specific to the slot (where is the difference?!?)
SlotCapabilityType slotCap = new SlotCapabilityType();
slotCap.setIndex(BigInteger.ZERO);
cap.getSlotCapability().add(slotCap);
// detect PACE capability, start by virtual terminal which is needed for that task
// then ask whether the reader can do it or a software solution exists
if (gui != null) {
if (t.supportsPace()) {
List<PACECapabilities.PACECapability> capabilities = t.getPACECapabilities();
List<String> protos = buildPACEProtocolList(capabilities);
slotCap.getProtocol().addAll(protos);
}
}
// detect PinCompare capabilities
if (gui != null) {
slotCap.getProtocol().add(ECardConstants.Protocol.PIN_COMPARE);
} else if (t.supportsPinCompare()) {
slotCap.getProtocol().add(ECardConstants.Protocol.PIN_COMPARE);
}
// ask protocol factory which types it supports
for (String proto : this.protocolFactories.protocols()) {
if (! slotCap.getProtocol().contains(proto)) {
slotCap.getProtocol().add(proto);
}
}
// send response
response = WSHelper.makeResponse(GetIFDCapabilitiesResponse.class, WSHelper.makeResultOK());
response.setIFDCapabilities(cap);
return response;
} catch (IFDException ex) {
response = WSHelper.makeResponse(GetIFDCapabilitiesResponse.class, ex.getResult());
_logger.warn(ex.getMessage(), ex);
return response;
}
}
} catch (Exception ex) {
if (ex instanceof RuntimeException) {
throw (RuntimeException) ex;
}
_logger.warn(ex.getMessage(), ex);
return WSHelper.makeResponse(GetIFDCapabilitiesResponse.class, WSHelper.makeResult(ex));
}
}
@Override
public GetStatusResponse getStatus(GetStatus parameters) {
try {
GetStatusResponse response;
//FIXME
if (! ByteUtils.compare(ctxHandle, parameters.getContextHandle())) {
String msg = "Invalid context handle specified.";
Result r = WSHelper.makeResultError(ECardConstants.Minor.IFD.INVALID_CONTEXT_HANDLE, msg);
response = WSHelper.makeResponse(GetStatusResponse.class, r);
return response;
} else {
// get ifd name from request or directly from the sc-io
List<SCTerminal> ifds = new LinkedList<SCTerminal>();
// get ifd names which should be investigated
try {
if (parameters.getIFDName() != null) {
SCTerminal t = scwrapper.getTerminal(parameters.getIFDName(), true);
if (t == null) {
Result r = WSHelper.makeResult(ECardConstants.Major.ERROR, ECardConstants.Minor.IFD.UNKNOWN_IFD, "Unknown IFD.");
response = WSHelper.makeResponse(GetStatusResponse.class, r);
return response;
} else {
ifds.add(t);
}
} else {
ifds.addAll(scwrapper.getTerminals(true));
}
} catch (IFDException ex) {
response = WSHelper.makeResponse(GetStatusResponse.class, ex.getResult());
_logger.warn(ex.getMessage(), ex);
return response;
}
// request status for each ifd
ArrayList<IFDStatusType> statuss = new ArrayList<IFDStatusType>(ifds.size());
for (SCTerminal ifd : ifds) {
try {
IFDStatusType s = ifd.getStatus();
statuss.add(s);
} catch (IFDException ex) {
response = WSHelper.makeResponse(GetStatusResponse.class, ex.getResult());
_logger.warn(ex.getMessage(), ex);
return response;
}
}
// everything worked out well
response = WSHelper.makeResponse(GetStatusResponse.class, WSHelper.makeResultOK());
response.getIFDStatus().addAll(statuss);
return response;
}
} catch (Exception ex) {
if (ex instanceof RuntimeException) {
throw (RuntimeException)ex;
}
_logger.warn(ex.getMessage(), ex);
return WSHelper.makeResponse(GetStatusResponse.class, WSHelper.makeResult(ex));
}
}
@Override
public WaitResponse wait(Wait parameters) {
try {
WaitResponse response;
// check for context handle
if (! ByteUtils.compare(ctxHandle, parameters.getContextHandle())) {
String msg = "Invalid context handle specified.";
Result r = WSHelper.makeResultError(ECardConstants.Minor.IFD.INVALID_CONTEXT_HANDLE, msg);
response = WSHelper.makeResponse(WaitResponse.class, r);
return response;
} else {
BigInteger timeout = parameters.getTimeOut();
if (timeout == null) {
timeout = BigInteger.valueOf(Long.MAX_VALUE);
}
ChannelHandleType callback = parameters.getCallback();
// callback is only useful with a protocol termination point
if (callback != null && callback.getProtocolTerminationPoint() == null) {
callback = null;
}
// get expected status or current status for all if none specified
List<IFDStatusType> expectedStatuses = parameters.getIFDStatus();
boolean withNew = false;
if (expectedStatuses.isEmpty()) {
withNew = true;
GetStatus statusReq = new GetStatus();
statusReq.setContextHandle(ctxHandle);
GetStatusResponse status = getStatus(statusReq);
if (status.getResult().getResultMajor().equals(ECardConstants.Major.ERROR)) {
response = WSHelper.makeResponse(WaitResponse.class, status.getResult());
return response;
}
expectedStatuses = status.getIFDStatus();
} else {
// check that ifdname is present, needed for comparison
for (IFDStatusType s : expectedStatuses) {
if (s.getIFDName() == null) {
String msg = "IFD in a request IFDStatus not known.";
Result r = WSHelper.makeResultError(ECardConstants.Minor.IFD.UNKNOWN_IFD, msg);
response = WSHelper.makeResponse(WaitResponse.class, r);
return response;
}
}
}
// if callback, generate session id
if (callback != null) {
ChannelHandleType newCallback = new ChannelHandleType();
newCallback.setBinding(callback.getBinding());
newCallback.setPathSecurity(callback.getPathSecurity());
newCallback.setProtocolTerminationPoint(callback.getProtocolTerminationPoint());
newCallback.setSessionIdentifier(ValueGenerators.generateSessionID());
callback = newCallback;
}
// create the event and fire
EventListener l = new EventListener(this, scwrapper, threadPool, ctxHandle, timeout.longValue(), callback, expectedStatuses, withNew);
FutureTask<List<IFDStatusType>> future = new FutureTask<List<IFDStatusType>>(l);
if (l.isAsync()) {
// add future to async wait list
asyncWaitThreads.put(callback.getSessionIdentifier(), future);
threadPool.execute(future); // finally run this darn thingy
// prepare result with session id in it
response = WSHelper.makeResponse(WaitResponse.class, WSHelper.makeResultOK());
if (callback.getSessionIdentifier() != null) {
response.setSessionIdentifier(callback.getSessionIdentifier());
}
return response;
} else {
// run wait in a future so it can be easily interrupted
syncWaitThread = future;
threadPool.execute(future);
// get results from the future
List<IFDStatusType> events = future.get();
// prepare response
response = WSHelper.makeResponse(WaitResponse.class, WSHelper.makeResultOK());
response.getIFDEvent().addAll(events);
return response;
}
}
} catch (ExecutionException ex) { // this is the exception from within the future
_logger.warn(ex.getMessage(), ex);
return WSHelper.makeResponse(WaitResponse.class, WSHelper.makeResult(ex.getCause()));
} catch (Exception ex) {
if (ex instanceof RuntimeException) {
throw (RuntimeException)ex;
}
_logger.warn(ex.getMessage(), ex);
return WSHelper.makeResponse(WaitResponse.class, WSHelper.makeResult(ex));
}
}
@Override
public CancelResponse cancel(Cancel parameters) {
try {
CancelResponse response = null;
// check for context handle
if (! ByteUtils.compare(ctxHandle, parameters.getContextHandle())) {
String msg = "Invalid context handle specified.";
Result r = WSHelper.makeResultError(ECardConstants.Minor.IFD.INVALID_CONTEXT_HANDLE, msg);
response = WSHelper.makeResponse(CancelResponse.class, r);
} else {
if (parameters.getSessionIdentifier() != null) {
// async wait
String session = parameters.getSessionIdentifier();
Future<List<IFDStatusType>> f = this.asyncWaitThreads.get(session);
if (f != null) {
f.cancel(true);
response = WSHelper.makeResponse(CancelResponse.class, WSHelper.makeResultOK());
}
} else {
// sync wait
synchronized (this) {
if (syncWaitThread != null) {
syncWaitThread.cancel(true);
syncWaitThread = null; // not really needed but seems cleaner
response = WSHelper.makeResponse(CancelResponse.class, WSHelper.makeResultOK());
} else {
String msg = "No synchronous Wait to cancel.";
Result r = WSHelper.makeResultError(ECardConstants.Minor.IFD.IO.CANCEL_NOT_POSSIBLE, msg);
response = WSHelper.makeResponse(CancelResponse.class, r);
}
}
}
// TODO: other cancel cases
}
if (response == null) {
// nothing to cancel
String msg = "No cancelable command matches the given parameters.";
response = WSHelper.makeResponse(CancelResponse.class, WSHelper.makeResultUnknownError(msg));
}
return response;
} catch (Exception ex) {
if (ex instanceof RuntimeException) {
throw (RuntimeException)ex;
}
_logger.warn(ex.getMessage(), ex);
return WSHelper.makeResponse(CancelResponse.class, WSHelper.makeResult(ex));
}
}
/**
* Note: the first byte of the command data is the control code.
*/
@Override
public ControlIFDResponse controlIFD(ControlIFD parameters) {
try {
ControlIFDResponse response;
if (! ByteUtils.compare(ctxHandle, parameters.getContextHandle())) {
String msg = "Invalid context handle specified.";
Result r = WSHelper.makeResultError(ECardConstants.Minor.IFD.INVALID_CONTEXT_HANDLE, msg);
response = WSHelper.makeResponse(ControlIFDResponse.class, r);
return response;
} else {
try {
SCTerminal t = scwrapper.getTerminal(parameters.getIFDName());
byte[] command = parameters.getCommand();
byte ctrlCode = command[0];
command = Arrays.copyOfRange(command, 1, command.length);
// check if the code is present
byte[] resultCommand = t.executeCtrlCode(ctrlCode, command);
// TODO: evaluate result
response = WSHelper.makeResponse(ControlIFDResponse.class, WSHelper.makeResultOK());
response.setResponse(resultCommand);
return response;
} catch (IFDException ex) {
response = WSHelper.makeResponse(ControlIFDResponse.class, WSHelper.makeResult(ex));
return response;
}
}
} catch (Throwable ex) {
if (ex instanceof RuntimeException) {
throw (RuntimeException)ex;
}
_logger.warn(ex.getMessage(), ex);
return WSHelper.makeResponse(ControlIFDResponse.class, WSHelper.makeResult(ex));
}
}
@Override
public ConnectResponse connect(Connect parameters) {
try {
ConnectResponse response;
if (! ByteUtils.compare(ctxHandle, parameters.getContextHandle())) {
String msg = "Invalid context handle specified.";
Result r = WSHelper.makeResultError(ECardConstants.Minor.IFD.INVALID_CONTEXT_HANDLE, msg);
response = WSHelper.makeResponse(ConnectResponse.class, r);
return response;
} else {
try {
//FIXME
if (! IFDUtils.getSlotIndex(parameters.getIFDName()).equals(parameters.getSlot())) {
String msg = "Invalid slot handle.";
Result r = WSHelper.makeResultError(ECardConstants.Minor.IFD.INVALID_SLOT_HANDLE, msg);
response = WSHelper.makeResponse(ConnectResponse.class, r);
return response;
} else {
SCTerminal t = scwrapper.getTerminal(parameters.getIFDName());
SCChannel channel = t.connect();
// make connection exclusive
Boolean exclusive = parameters.isExclusive();
if (exclusive != null && exclusive.booleanValue() == true) {
BeginTransaction transact = new BeginTransaction();
transact.setSlotHandle(channel.getHandle());
BeginTransactionResponse resp = beginTransaction(transact);
if (resp.getResult().getResultMajor().equals(ECardConstants.Major.ERROR)) {
response = WSHelper.makeResponse(ConnectResponse.class, resp.getResult());
return response;
}
}
// connection established, return result
response = WSHelper.makeResponse(ConnectResponse.class, WSHelper.makeResultOK());
response.setSlotHandle(channel.getHandle());
return response;
}
} catch (IFDException ex) {
response = WSHelper.makeResponse(ConnectResponse.class, ex.getResult());
_logger.warn(ex.getMessage(), ex);
return response;
}
}
} catch (Exception ex) {
if (ex instanceof RuntimeException) {
throw (RuntimeException)ex;
}
_logger.warn(ex.getMessage(), ex);
return WSHelper.makeResponse(ConnectResponse.class, WSHelper.makeResult(ex));
}
}
@Override
public synchronized DisconnectResponse disconnect(Disconnect parameters) {
try {
DisconnectResponse response;
if (! hasContext()) {
String msg = "Context not initialized.";
Result r = WSHelper.makeResultError(ECardConstants.Minor.IFD.INVALID_SLOT_HANDLE, msg);
response = WSHelper.makeResponse(DisconnectResponse.class, r);
return response;
}
byte[] handle = parameters.getSlotHandle();
try {
SCCard c = scwrapper.getCard(handle);
// TODO: add support for actions
c.closeChannel(handle, false);
response = WSHelper.makeResponse(DisconnectResponse.class, WSHelper.makeResultOK());
return response;
} catch (IFDException ex) {
response = WSHelper.makeResponse(DisconnectResponse.class, ex.getResult());
_logger.warn(ex.getMessage(), ex);
return response;
}
} catch (Exception ex) {
if (ex instanceof RuntimeException) {
throw (RuntimeException)ex;
}
_logger.warn(ex.getMessage(), ex);
return WSHelper.makeResponse(DisconnectResponse.class, WSHelper.makeResult(ex));
}
}
@Override
public BeginTransactionResponse beginTransaction(BeginTransaction beginTransaction) {
try {
BeginTransactionResponse response;
if (! hasContext()) {
String msg = "Context not initialized.";
Result r = WSHelper.makeResultError(ECardConstants.Minor.IFD.INVALID_SLOT_HANDLE, msg);
response = WSHelper.makeResponse(BeginTransactionResponse.class, r);
return response;
}
byte[] handle = beginTransaction.getSlotHandle();
try {
SCCard c = scwrapper.getCard(handle);
// TODO: create thread, associate it with the current card instance, and begin exclusive card access
c.beginExclusive();
response = WSHelper.makeResponse(BeginTransactionResponse.class, WSHelper.makeResultOK());
return response;
} catch (CardException ex) {
response = WSHelper.makeResponse(BeginTransactionResponse.class, WSHelper.makeResult(ex));
_logger.warn(ex.getMessage(), ex);
return response;
} catch (IFDException ex) {
response = WSHelper.makeResponse(BeginTransactionResponse.class, ex.getResult());
_logger.warn(ex.getMessage(), ex);
return response;
}
} catch (Exception ex) {
if (ex instanceof RuntimeException) {
throw (RuntimeException)ex;
}
_logger.warn(ex.getMessage(), ex);
return WSHelper.makeResponse(BeginTransactionResponse.class, WSHelper.makeResult(ex));
}
}
@Override
public EndTransactionResponse endTransaction(EndTransaction parameters) {
try {
EndTransactionResponse response;
if (! hasContext()) {
String msg = "Context not initialized.";
Result r = WSHelper.makeResultError(ECardConstants.Minor.IFD.INVALID_SLOT_HANDLE, msg);
response = WSHelper.makeResponse(EndTransactionResponse.class, r);
return response;
}
byte[] handle = parameters.getSlotHandle();
try {
SCCard c = scwrapper.getCard(handle);
// TODO: retrieve thread associated with the current card instance and end exclusive card access
c.endExclusive();
response = WSHelper.makeResponse(EndTransactionResponse.class, WSHelper.makeResultOK());
return response;
} catch (CardException ex) {
response = WSHelper.makeResponse(EndTransactionResponse.class, WSHelper.makeResult(ex));
_logger.warn(ex.getMessage(), ex);
return response;
} catch (IFDException ex) {
response = WSHelper.makeResponse(EndTransactionResponse.class, ex.getResult());
_logger.warn(ex.getMessage(), ex);
return response;
}
} catch (Exception ex) {
if (ex instanceof RuntimeException) {
throw (RuntimeException)ex;
}
_logger.warn(ex.getMessage(), ex);
return WSHelper.makeResponse(EndTransactionResponse.class, WSHelper.makeResult(ex));
}
}
@Override
public TransmitResponse transmit(Transmit parameters) {
try {
TransmitResponse response;
if (! hasContext()) {
String msg = "Context not initialized.";
Result r = WSHelper.makeResultError(ECardConstants.Minor.IFD.INVALID_SLOT_HANDLE, msg);
response = WSHelper.makeResponse(TransmitResponse.class, r);
return response;
}
byte[] handle = parameters.getSlotHandle();
List<InputAPDUInfoType> apdus = parameters.getInputAPDUInfo();
response = WSHelper.makeResponse(TransmitResponse.class, null);
Result result;
List<byte[]> rapdus = response.getOutputAPDU();
try {
SCChannel ch = scwrapper.getChannel(handle);
for (InputAPDUInfoType capdu : apdus) {
byte[] rapdu = ch.transmit(capdu.getInputAPDU(), capdu.getAcceptableStatusCode());
rapdus.add(rapdu);
}
result = WSHelper.makeResultOK();
} catch (TransmitException ex) {
rapdus.add(ex.getResponseAPDU());
result = ex.getResult();
} catch (IFDException ex) {
result = ex.getResult();
}
response.setResult(result);
return response;
} catch (Exception ex) {
if (ex instanceof RuntimeException) {
throw (RuntimeException)ex;
}
_logger.warn(ex.getMessage(), ex);
return WSHelper.makeResponse(TransmitResponse.class, WSHelper.makeResult(ex));
}
}
@Override
public VerifyUserResponse verifyUser(VerifyUser parameters) {
try {
VerifyUserResponse response;
if (! hasContext()) {
String msg = "Context not initialized.";
Result r = WSHelper.makeResultError(ECardConstants.Minor.IFD.INVALID_SLOT_HANDLE, msg);
response = WSHelper.makeResponse(VerifyUserResponse.class, r);
return response;
}
AbstractTerminal aTerm = new AbstractTerminal(this, scwrapper, gui, ctxHandle, parameters.getDisplayIndex());
try {
response = aTerm.verifyUser(parameters);
return response;
} catch (IFDException ex) {
response = WSHelper.makeResponse(VerifyUserResponse.class, ex.getResult());
return response;
}
} catch (Exception ex) {
if (ex instanceof RuntimeException) {
throw (RuntimeException)ex;
}
_logger.warn(ex.getMessage(), ex);
return WSHelper.makeResponse(VerifyUserResponse.class, WSHelper.makeResult(ex));
}
}
@Override
public ModifyVerificationDataResponse modifyVerificationData(ModifyVerificationData parameters) {
try {
if (! hasContext()) {
String msg = "Context not initialized.";
Result r = WSHelper.makeResultError(ECardConstants.Minor.IFD.INVALID_SLOT_HANDLE, msg);
return WSHelper.makeResponse(ModifyVerificationDataResponse.class, r);
}
throw new UnsupportedOperationException("Not supported yet.");
} catch (Exception ex) {
if (ex instanceof RuntimeException) {
throw (RuntimeException)ex;
}
_logger.warn(ex.getMessage(), ex);
return WSHelper.makeResponse(ModifyVerificationDataResponse.class, WSHelper.makeResult(ex));
}
}
@Override
public OutputResponse output(Output parameters) {
try {
OutputResponse response;
if (! ByteUtils.compare(ctxHandle, parameters.getContextHandle())) {
String msg = "Invalid context handle specified.";
Result r = WSHelper.makeResultError(ECardConstants.Minor.IFD.INVALID_CONTEXT_HANDLE, msg);
response = WSHelper.makeResponse(OutputResponse.class, r);
return response;
} else {
String ifdName = parameters.getIFDName();
OutputInfoType outInfo = parameters.getOutputInfo();
AbstractTerminal aTerm = new AbstractTerminal(this, scwrapper, gui, ctxHandle, outInfo.getDisplayIndex());
try {
aTerm.output(ifdName, outInfo);
} catch (IFDException ex) {
response = WSHelper.makeResponse(OutputResponse.class, ex.getResult());
return response;
}
response = WSHelper.makeResponse(OutputResponse.class, WSHelper.makeResultOK());
return response;
}
} catch (Exception ex) {
if (ex instanceof RuntimeException) {
throw (RuntimeException)ex;
}
_logger.warn(ex.getMessage(), ex);
return WSHelper.makeResponse(OutputResponse.class, WSHelper.makeResult(ex));
}
}
@Override
public EstablishChannelResponse establishChannel(EstablishChannel parameters) {
byte[] slotHandle = parameters.getSlotHandle();
try {
SCTerminal term = this.scwrapper.getTerminal(slotHandle);
SCCard card = this.scwrapper.getCard(slotHandle);
SCChannel channel = card.getChannel(slotHandle);
DIDAuthenticationDataType protoParam = parameters.getAuthenticationProtocolData();
String protocol = protoParam.getProtocol();
// check if it is PACE and try to perform native implementation
// get pace capabilities
List<PACECapabilities.PACECapability> paceCapabilities = term.getPACECapabilities();
List<String> supportedProtos = buildPACEProtocolList(paceCapabilities);
// check out if this actually a PACE request
// FIXME: check type of protocol
// i don't care which type is supported, i try it anyways
if (! supportedProtos.isEmpty() && supportedProtos.get(0).startsWith(protocol)) {
// yeah, PACE seems to be supported by the reader, big win
PACEInputType paceParam = new PACEInputType(protoParam);
// extract variables needed for pace
byte pinID = paceParam.getPINID();
// optional elements
byte[] chat = paceParam.getCHAT();
String pin = paceParam.getPIN();
byte[] certDesc = paceParam.getCertificateDescription();
// prepare pace data structures
EstablishPACERequest estPaceReq = new EstablishPACERequest(pinID, chat, null, certDesc); // TODO: add supplied PIN
ExecutePACERequest execPaceReq = new ExecutePACERequest(ExecutePACERequest.Function.EstablishPACEChannel, estPaceReq.toBytes());
// see if PACE type demanded for this input value combination is supported
// TODO: check if this additional check is really necessary
if (estPaceReq.isSupportedType(paceCapabilities)) {
byte[] reqData = execPaceReq.toBytes();
_logger.debug("executeCtrlCode request: {}", ByteUtils.toHexString(reqData));
// execute pace
byte[] resData = term.executeCtrlCode(PCSCFeatures.EXECUTE_PACE, reqData);
_logger.debug("Response of executeCtrlCode: {}", ByteUtils.toHexString(resData));
// evaluate response
ExecutePACEResponse execPaceRes = new ExecutePACEResponse(resData);
if (execPaceRes.isError()) {
return WSHelper.makeResponse(EstablishChannelResponse.class, execPaceRes.getResult());
}
EstablishPACEResponse estPaceRes = new EstablishPACEResponse(execPaceRes.getData());
// get values and prepare response
PACEOutputType authDataResponse = paceParam.getOutputType();
// mandatory fields
authDataResponse.setRetryCounter(estPaceRes.getRetryCounter());
authDataResponse.setEFCardAccess(estPaceRes.getEFCardAccess());
// optional fields
if (estPaceRes.hasCurrentCAR()) {
authDataResponse.setCurrentCAR(estPaceRes.getCurrentCAR());
}
if (estPaceRes.hasPreviousCAR()) {
authDataResponse.setPreviousCAR(estPaceRes.getPreviousCAR());
}
if (estPaceRes.hasIDICC()) {
authDataResponse.setIDPICC(estPaceRes.getIDICC());
}
// create response type and return
EstablishChannelResponse response = WSHelper.makeResponse(EstablishChannelResponse.class, WSHelper.makeResultOK());
response.setAuthenticationProtocolData(authDataResponse.getAuthDataType());
return response;
}
} // end native pace support
// check out available software protocols
if (this.protocolFactories.contains(protocol)) {
ProtocolFactory factory = this.protocolFactories.get(protocol);
Protocol protoImpl = factory.createInstance();
EstablishChannelResponse response = protoImpl.establish(parameters, dispatcher, this.gui);
// register protocol instance for secure messaging when protocol was processed successful
if (response.getResult().getResultMajor().equals(ECardConstants.Major.OK)) {
channel.addSecureMessaging(protoImpl);
}
return response;
}
// if this point is reached a native implementation is not present, try registered protocols
Result r = WSHelper.makeResultUnknownError("No such protocol available in this IFD.");
return WSHelper.makeResponse(EstablishChannelResponse.class, r);
} catch (Throwable t) {
return WSHelper.makeResponse(EstablishChannelResponse.class, WSHelper.makeResult(t));
}
}
@Override
public DestroyChannelResponse destroyChannel(DestroyChannel parameters) {
try {
byte[] slotHandle = parameters.getSlotHandle();
SCCard card = this.scwrapper.getCard(slotHandle);
SCChannel channel = card.getChannel(slotHandle);
channel.removeSecureMessaging();
DestroyChannelResponse destroyChannelResponse = new DestroyChannelResponse();
Result r = new Result();
r.setResultMajor(ECardConstants.Major.OK);
destroyChannelResponse.setResult(r);
return destroyChannelResponse;
} catch (Throwable t) {
return WSHelper.makeResponse(DestroyChannelResponse.class, WSHelper.makeResult(t));
}
}
}