diff --git a/build.gradle b/build.gradle index 19dc729..b7da5db 100644 --- a/build.gradle +++ b/build.gradle @@ -26,7 +26,7 @@ dependencies { compile gradleApi() compile localGroovy() compile 'com.fidesmo:gradle-javacard:0.2.4' - compile 'com.fidesmo:sec-client-core:0.0.5' + compile 'com.fidesmo:sec-client-core:0.1.26' compile 'io.github.jnasmartcardio:jnasmartcardio:0.2.4' testCompile 'junit:junit:4.11' testCompile 'org.hamcrest:hamcrest-all:1.3' diff --git a/src/main/groovy/com/fidesmo/gradle/plugin/JnasmartcardioTransceiver.groovy b/src/main/groovy/com/fidesmo/gradle/plugin/JnasmartcardioTransceiver.groovy deleted file mode 100644 index f5d6fe8..0000000 --- a/src/main/groovy/com/fidesmo/gradle/plugin/JnasmartcardioTransceiver.groovy +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2014 Fidesmo AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package com.fidesmo.gradle.plugin - -import com.fidesmo.sec.transceivers.AbstractTransceiver - -import javax.smartcardio.* -import jnasmartcardio.Smartcardio - -import org.slf4j.Logger -import org.slf4j.LoggerFactory - -class JnasmartcardioTransceiver extends AbstractTransceiver { - - Logger logger = LoggerFactory.getLogger(this.getClass()) - - Card card - Boolean open - - public byte[] open() { - TerminalFactory factory = TerminalFactory.getInstance("PC/SC", null, new Smartcardio()) - List terminalsWithCard = factory.terminals().list(CardTerminals.State.CARD_PRESENT) - if (terminalsWithCard.size() == 0) { - if (factory.terminals().list().size() == 0) { - throw new Exception('No terminals found') - } else { - throw new Exception('No terminal with card found') - } - } - CardTerminal terminal = terminalsWithCard.first() - logger.info("Using terminal '${terminal.name}' to connect to fidesmo card") - open = true - card = terminal.connect('*') - card.ATR.bytes - } - - public void close() { - open = false - card.disconnect(false) - } - - public byte[] transceive(byte[] command) { - CardChannel cardChannel = card.basicChannel - ResponseAPDU responseApdu = cardChannel.transmit(new CommandAPDU(command)) - responseApdu.bytes - } -} diff --git a/src/main/groovy/com/fidesmo/gradle/plugin/OperationTask.groovy b/src/main/groovy/com/fidesmo/gradle/plugin/OperationTask.groovy index 57b7724..f87d7cc 100644 --- a/src/main/groovy/com/fidesmo/gradle/plugin/OperationTask.groovy +++ b/src/main/groovy/com/fidesmo/gradle/plugin/OperationTask.groovy @@ -21,15 +21,13 @@ import org.gradle.api.Project import org.gradle.api.DefaultTask import org.gradle.api.GradleException -import java.util.concurrent.CountDownLatch -import java.util.concurrent.TimeUnit - import retrofit.* import retrofit.RequestInterceptor.RequestFacade import com.fidesmo.sec.client.RetrofitSecClient -import com.fidesmo.sec.client.OperationClient +import com.fidesmo.sec.client.OperationClientImpl import com.fidesmo.sec.client.ClientCallback +import com.fidesmo.sec.transceivers.Transceiver class OperationTask extends FidesmoBaseTask { @@ -38,28 +36,16 @@ class OperationTask extends FidesmoBaseTask { } def executeOperation(UUID operationId) { - logger.info("Starting fidesmo sec-client to execute operation '${operationId}'") - def latch = new CountDownLatch(1) - def client = OperationClient.getInstance( + + // execute operation by implementing sec-client flow + def client = (new OperationClientImpl()).get( operationId, - new JnasmartcardioTransceiver(), - new ClientCallback() { - void success() { - latch.countDown() - } - void failure(String message) { - throw new GradleException("Writing to fidesmo card failed with: '${message}'") - } - }, - RetrofitSecClient.getClient() + (Transceiver) new SmartcardioTransceiver(), + RetrofitSecClient.client ) + client.transceive().toBlocking().last() - client.transceive() - - if (! latch.await(operationTimeout, TimeUnit.SECONDS)) { - throw new GradleException('Time out while writing to fidesmo card') - } - + // check operation result by querying the status service int maxRetries = 9 for(int i = 0; i <= maxRetries; i ++) { // try ten times try { diff --git a/src/main/java/com/fidesmo/gradle/plugin/LoggingCard.java b/src/main/java/com/fidesmo/gradle/plugin/LoggingCard.java new file mode 100644 index 0000000..cd91758 --- /dev/null +++ b/src/main/java/com/fidesmo/gradle/plugin/LoggingCard.java @@ -0,0 +1,89 @@ +/* + * Copyright 2015 Fidesmo AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.fidesmo.gradle.plugin; + +import java.io.IOException; +import java.util.List; +import java.util.ArrayList; + +import com.fidesmo.sec.utils.Hex; +import nordpol.IsoCard; +import nordpol.OnCardErrorListener; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** Wrap an IsoCard so that all transmitted APDUs are logged */ + +public class LoggingCard implements IsoCard { + + IsoCard underlying; + Logger logger; + + public LoggingCard(IsoCard underlying) { + this.underlying = underlying; + this.logger = LoggerFactory.getLogger("transaction-trace"); + } + + public void addOnCardErrorListener(OnCardErrorListener listener) { + underlying.addOnCardErrorListener(listener); + } + + public void removeOnCardErrorListener(OnCardErrorListener listener) { + underlying.removeOnCardErrorListener(listener); + } + + public boolean isConnected() { + return underlying.isConnected(); + } + + public void connect() throws IOException { + underlying.connect(); + } + + public void close() throws IOException { + underlying.close(); + } + + public int getTimeout() { + return underlying.getTimeout(); + } + + public void setTimeout(int t) { + underlying.setTimeout(t); + } + + public int getMaxTransceiveLength() throws IOException { + return underlying.getMaxTransceiveLength(); + } + + public byte[] transceive(byte[] command) throws IOException { + logger.info("Send to card: " + Hex.encodeHex(command)); + byte[] response = underlying.transceive(command); + logger.info("Received from card: " + Hex.encodeHex(response)); + return response; + } + + public List transceive(List commands) throws IOException { + List responses = new ArrayList(commands.size()); + for (byte[] command: commands) { + responses.add(transceive(command)); + } + return responses; + } +} diff --git a/src/main/java/com/fidesmo/gradle/plugin/SmartcardioCard.java b/src/main/java/com/fidesmo/gradle/plugin/SmartcardioCard.java new file mode 100644 index 0000000..e090a82 --- /dev/null +++ b/src/main/java/com/fidesmo/gradle/plugin/SmartcardioCard.java @@ -0,0 +1,98 @@ +/* + * Copyright 2015 Fidesmo AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.fidesmo.gradle.plugin; + +import java.io.IOException; +import java.util.List; +import java.util.ArrayList; + +import javax.smartcardio.Card; +import javax.smartcardio.CardException; +import javax.smartcardio.CommandAPDU; + +import nordpol.IsoCard; +import nordpol.OnCardErrorListener; + +public class SmartcardioCard implements IsoCard { + + private Card card; + private boolean openFlag = false; + private int timeout = 15; + private List listeners = new ArrayList(); + + public SmartcardioCard(Card card) { + this.card = card; + } + + public void addOnCardErrorListener(OnCardErrorListener listener) { + listeners.add(listener); + } + + public void removeOnCardErrorListener(OnCardErrorListener listener) { + listeners.remove(listener); + } + + public boolean isConnected() { + return openFlag; + } + + public void connect() { + openFlag = true; + } + + public void close() throws IOException { + try { + card.disconnect(false); + openFlag = false; + } catch (CardException e) { + throw new IOException("Couldn't close card", e); + } + } + + public int getTimeout() { + return timeout; + } + + public void setTimeout(int t) { + timeout = t; + } + + public int getMaxTransceiveLength() { + return 261; + } + + public byte[] transceive(byte[] command) throws IOException { + try { + return card + .getBasicChannel() + .transmit(new CommandAPDU(command)) + .getBytes(); + } catch (CardException e) { + throw new IOException("Couldn't close card", e); + } + } + + public List transceive(List commands) throws IOException { + List responses = new ArrayList(commands.size()); + for (byte[] command: commands) { + responses.add(transceive(command)); + } + return responses; + } + +} diff --git a/src/main/java/com/fidesmo/gradle/plugin/SmartcardioTransceiver.java b/src/main/java/com/fidesmo/gradle/plugin/SmartcardioTransceiver.java new file mode 100644 index 0000000..deb8dda --- /dev/null +++ b/src/main/java/com/fidesmo/gradle/plugin/SmartcardioTransceiver.java @@ -0,0 +1,67 @@ +/* + * Copyright 2015 Fidesmo AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.fidesmo.gradle.plugin; + +import java.io.IOException; +import java.util.List; +import java.security.NoSuchAlgorithmException; +import javax.smartcardio.TerminalFactory; +import javax.smartcardio.CardTerminal; +import javax.smartcardio.CardTerminals; +import javax.smartcardio.CardException; + +import jnasmartcardio.Smartcardio; +import rx.Observable; + +import com.fidesmo.sec.transceivers.Transceiver; +import nordpol.IsoCard; + + +/** Transceiver implementation using the javax.smartcardio package. + * + * This transceiver uses the jnasmartcardio package instead of the + * default implementation. + */ +public class SmartcardioTransceiver implements Transceiver { + + public Observable getCard() { + try { + TerminalFactory factory = + TerminalFactory.getInstance("PC/SC",null, new Smartcardio()); + List terminalsWithCard = + factory.terminals().list(CardTerminals.State.CARD_PRESENT); + + if (terminalsWithCard.size() == 0) { + if (factory.terminals().list().size() == 0) { + return Observable.error(new IOException("No terminals found")); + } else { + return Observable.error(new IOException("No card found")); + } + } + + return Observable + .just((IsoCard) new LoggingCard(new SmartcardioCard(terminalsWithCard + .get(0) + .connect("*")))); + } catch (NoSuchAlgorithmException e) { + return Observable.error(new IOException("Error with jnassmartcardio", e)); + } catch (CardException e) { + return Observable.error(new IOException("Unable to get Smartcard", e)); + } + } +}