From 3da4655d011dfc2f04f9e4ac28b38aee42d01bc0 Mon Sep 17 00:00:00 2001 From: clemenso Date: Tue, 5 Jan 2010 10:06:47 +0000 Subject: Features [#437] Handle pinpad [64:03] response apdu correctly [#445] pin entry feedback for VERIFY_PIN_START/FINISH [#471] Provide SecureViewer Link before Pinpad PinEntry timeout starts Bugs [#479] PIN Managment Applet allows unmatching new pin and pin confirmation [#480] PIN Management displays blocked PINs as ACTIVE [#486] Not possible to select 3 times in series the same item from signedReferencesList for display in secureViewer [#506] change pin dialog (gui) issues [#508] e-card G3 PIN activation (with TransportPIN) not supported [#509] closing secure viewer window (WINDOW_CLOSING) leaves "signature data is displayed in viewer" dialog in applet git-svn-id: https://joinup.ec.europa.eu/svn/mocca/trunk@565 8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4 --- smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java | 44 +- .../at/gv/egiz/smcc/AbstractSignatureCard.java | 14 +- smcc/src/main/java/at/gv/egiz/smcc/BELPICCard.java | 11 +- .../java/at/gv/egiz/smcc/ChangePINProvider.java | 36 -- smcc/src/main/java/at/gv/egiz/smcc/ITCard.java | 11 +- .../at/gv/egiz/smcc/PINConfirmationException.java | 19 - .../java/at/gv/egiz/smcc/PINFormatException.java | 19 - .../java/at/gv/egiz/smcc/PINMgmtSignatureCard.java | 11 +- .../src/main/java/at/gv/egiz/smcc/PINProvider.java | 45 -- .../at/gv/egiz/smcc/ResetRetryCounterAPDUSpec.java | 38 ++ .../src/main/java/at/gv/egiz/smcc/STARCOSCard.java | 208 +++--- smcc/src/main/java/at/gv/egiz/smcc/SWCard.java | 106 +--- .../main/java/at/gv/egiz/smcc/SignatureCard.java | 9 +- .../java/at/gv/egiz/smcc/pin/gui/ModifyPINGUI.java | 36 ++ .../at/gv/egiz/smcc/pin/gui/ModifyPINProvider.java | 48 ++ .../main/java/at/gv/egiz/smcc/pin/gui/PINGUI.java | 42 ++ .../java/at/gv/egiz/smcc/pin/gui/PINProvider.java | 49 ++ .../java/at/gv/egiz/smcc/reader/CardReader.java | 92 +++ .../at/gv/egiz/smcc/reader/DefaultCardReader.java | 106 ++++ .../at/gv/egiz/smcc/reader/PinpadCardReader.java | 703 +++++++++++++++++++++ .../java/at/gv/egiz/smcc/reader/ReaderFactory.java | 128 ++++ 21 files changed, 1419 insertions(+), 356 deletions(-) delete mode 100644 smcc/src/main/java/at/gv/egiz/smcc/ChangePINProvider.java delete mode 100644 smcc/src/main/java/at/gv/egiz/smcc/PINProvider.java create mode 100644 smcc/src/main/java/at/gv/egiz/smcc/ResetRetryCounterAPDUSpec.java create mode 100644 smcc/src/main/java/at/gv/egiz/smcc/pin/gui/ModifyPINGUI.java create mode 100644 smcc/src/main/java/at/gv/egiz/smcc/pin/gui/ModifyPINProvider.java create mode 100644 smcc/src/main/java/at/gv/egiz/smcc/pin/gui/PINGUI.java create mode 100644 smcc/src/main/java/at/gv/egiz/smcc/pin/gui/PINProvider.java create mode 100644 smcc/src/main/java/at/gv/egiz/smcc/reader/CardReader.java create mode 100644 smcc/src/main/java/at/gv/egiz/smcc/reader/DefaultCardReader.java create mode 100644 smcc/src/main/java/at/gv/egiz/smcc/reader/PinpadCardReader.java create mode 100644 smcc/src/main/java/at/gv/egiz/smcc/reader/ReaderFactory.java (limited to 'smcc/src/main') diff --git a/smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java b/smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java index 1ed5a177..b8cdb208 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java @@ -16,6 +16,8 @@ */ package at.gv.egiz.smcc; +import at.gv.egiz.smcc.pin.gui.ModifyPINGUI; +import at.gv.egiz.smcc.pin.gui.PINGUI; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; @@ -79,10 +81,16 @@ public class ACOSCard extends AbstractSignatureCard implements PINMgmtSignatureC public static final byte KID_PIN_SIG = (byte) 0x81; + public static final byte KID_PUK_SIG = (byte) 0x83; + public static final byte KID_PIN_DEC = (byte) 0x81; + public static final byte KID_PUK_DEC = (byte) 0x82; + public static final byte KID_PIN_INF = (byte) 0x83; + public static final byte KID_PUK_INF = (byte) 0x84; + public static final byte[] DST_SIG = new byte[] { (byte) 0x84, (byte) 0x01, // tag // , // length @@ -217,7 +225,7 @@ public class ACOSCard extends AbstractSignatureCard implements PINMgmtSignatureC @Override @Exclusive - public byte[] getInfobox(String infobox, PINProvider provider, String domainId) + public byte[] getInfobox(String infobox, PINGUI provider, String domainId) throws SignatureCardException, InterruptedException { if ("IdentityLink".equals(infobox)) { @@ -233,7 +241,7 @@ public class ACOSCard extends AbstractSignatureCard implements PINMgmtSignatureC } - protected byte[] getIdentityLinkV1(PINProvider provider, String domainId) + protected byte[] getIdentityLinkV1(PINGUI provider, String domainId) throws SignatureCardException, InterruptedException { try { @@ -262,7 +270,7 @@ public class ACOSCard extends AbstractSignatureCard implements PINMgmtSignatureC } - protected byte[] getIdentityLinkV2(PINProvider provider, String domainId) + protected byte[] getIdentityLinkV2(PINGUI provider, String domainId) throws SignatureCardException, InterruptedException { try { @@ -388,7 +396,7 @@ public class ACOSCard extends AbstractSignatureCard implements PINMgmtSignatureC @Override @Exclusive public byte[] createSignature(InputStream input, KeyboxName keyboxName, - PINProvider provider, String alg) throws SignatureCardException, InterruptedException, IOException { + PINGUI provider, String alg) throws SignatureCardException, InterruptedException, IOException { ByteArrayOutputStream dst = new ByteArrayOutputStream(); // key ID @@ -487,7 +495,7 @@ public class ACOSCard extends AbstractSignatureCard implements PINMgmtSignatureC * @see at.gv.egiz.smcc.AbstractSignatureCard#verifyPIN(at.gv.egiz.smcc.PINSpec, at.gv.egiz.smcc.PINProvider) */ @Override - public void verifyPIN(PINSpec pinSpec, PINProvider pinProvider) + public void verifyPIN(PINSpec pinSpec, PINGUI pinProvider) throws LockedException, NotActivatedException, CancelledException, TimeoutException, SignatureCardException, InterruptedException { @@ -509,7 +517,7 @@ public class ACOSCard extends AbstractSignatureCard implements PINMgmtSignatureC * @see at.gv.egiz.smcc.AbstractSignatureCard#changePIN(at.gv.egiz.smcc.PINSpec, at.gv.egiz.smcc.ChangePINProvider) */ @Override - public void changePIN(PINSpec pinSpec, ChangePINProvider pinProvider) + public void changePIN(PINSpec pinSpec, ModifyPINGUI pinProvider) throws LockedException, NotActivatedException, CancelledException, TimeoutException, SignatureCardException, InterruptedException { @@ -528,7 +536,7 @@ public class ACOSCard extends AbstractSignatureCard implements PINMgmtSignatureC } @Override - public void activatePIN(PINSpec pinSpec, PINProvider pinProvider) + public void activatePIN(PINSpec pinSpec, ModifyPINGUI pinGUI) throws CancelledException, SignatureCardException, CancelledException, TimeoutException, InterruptedException { log.error("ACTIVATE PIN not supported by ACOS"); @@ -536,7 +544,7 @@ public class ACOSCard extends AbstractSignatureCard implements PINMgmtSignatureC } @Override - public void unblockPIN(PINSpec pinSpec, PINProvider pinProvider) + public void unblockPIN(PINSpec pinSpec, ModifyPINGUI pinGUI) throws CancelledException, SignatureCardException, InterruptedException { throw new SignatureCardException("Unblock PIN not supported."); } @@ -570,10 +578,8 @@ public class ACOSCard extends AbstractSignatureCard implements PINMgmtSignatureC // PROTECTED METHODS (assume exclusive card access) //////////////////////////////////////////////////////////////////////// - protected void verifyPINLoop(CardChannel channel, PINSpec spec, PINProvider provider) - throws InterruptedException, LockedException, NotActivatedException, - TimeoutException, PINFormatException, PINOperationAbortedException, - SignatureCardException, CardException { + protected void verifyPINLoop(CardChannel channel, PINSpec spec, PINGUI provider) + throws InterruptedException, CardException, SignatureCardException { int retries = -1; do { @@ -581,10 +587,8 @@ public class ACOSCard extends AbstractSignatureCard implements PINMgmtSignatureC } while (retries > 0); } - protected void changePINLoop(CardChannel channel, PINSpec spec, ChangePINProvider provider) - throws InterruptedException, LockedException, NotActivatedException, - TimeoutException, PINFormatException, PINOperationAbortedException, - SignatureCardException, CardException { + protected void changePINLoop(CardChannel channel, PINSpec spec, ModifyPINGUI provider) + throws InterruptedException, CardException, SignatureCardException { int retries = -1; do { @@ -593,7 +597,7 @@ public class ACOSCard extends AbstractSignatureCard implements PINMgmtSignatureC } protected int verifyPIN(CardChannel channel, PINSpec pinSpec, - PINProvider provider, int retries) throws InterruptedException, CardException, SignatureCardException { + PINGUI provider, int retries) throws InterruptedException, CardException, SignatureCardException { VerifyAPDUSpec apduSpec = new VerifyAPDUSpec( new byte[] { @@ -602,7 +606,7 @@ public class ACOSCard extends AbstractSignatureCard implements PINMgmtSignatureC (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 }, 0, VerifyAPDUSpec.PIN_FORMAT_ASCII, 8); - ResponseAPDU resp = reader.verify(channel, apduSpec, pinSpec, provider, retries); + ResponseAPDU resp = reader.verify(channel, apduSpec, provider, pinSpec, retries); if (resp.getSW() == 0x9000) { return -1; @@ -625,7 +629,7 @@ public class ACOSCard extends AbstractSignatureCard implements PINMgmtSignatureC } protected int changePIN(CardChannel channel, PINSpec pinSpec, - ChangePINProvider pinProvider, int retries) throws CancelledException, InterruptedException, CardException, SignatureCardException { + ModifyPINGUI pinProvider, int retries) throws CancelledException, InterruptedException, CardException, SignatureCardException { ChangeReferenceDataAPDUSpec apduSpec = new ChangeReferenceDataAPDUSpec( new byte[] { @@ -639,7 +643,7 @@ public class ACOSCard extends AbstractSignatureCard implements PINMgmtSignatureC - ResponseAPDU resp = reader.modify(channel, apduSpec, pinSpec, pinProvider, retries); + ResponseAPDU resp = reader.modify(channel, apduSpec, pinProvider, pinSpec, retries); if (resp.getSW() == 0x9000) { return -1; diff --git a/smcc/src/main/java/at/gv/egiz/smcc/AbstractSignatureCard.java b/smcc/src/main/java/at/gv/egiz/smcc/AbstractSignatureCard.java index 54b4c7fe..fcb94fc6 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/AbstractSignatureCard.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/AbstractSignatureCard.java @@ -16,6 +16,8 @@ */ package at.gv.egiz.smcc; +import at.gv.egiz.smcc.reader.CardReader; +import at.gv.egiz.smcc.reader.ReaderFactory; import java.util.ArrayList; import java.util.List; import java.util.Locale; @@ -29,9 +31,6 @@ import javax.smartcardio.CardTerminal; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import at.gv.egiz.smcc.ccid.CCID; -import at.gv.egiz.smcc.ccid.ReaderFactory; - public abstract class AbstractSignatureCard implements SignatureCard { private static Log log = LogFactory.getLog(AbstractSignatureCard.class); @@ -45,7 +44,7 @@ public abstract class AbstractSignatureCard implements SignatureCard { private Card card_; - protected CCID reader; + protected CardReader reader; protected AbstractSignatureCard(String resourceBundleName) { this.resourceBundleName = resourceBundleName; @@ -68,7 +67,7 @@ public abstract class AbstractSignatureCard implements SignatureCard { @Override public void init(Card card, CardTerminal cardTerminal) { this.card_ = card; - this.reader = ReaderFactory.getInstance().getReader(card, cardTerminal); + this.reader = ReaderFactory.getReader(card, cardTerminal); } @Override @@ -80,11 +79,6 @@ public abstract class AbstractSignatureCard implements SignatureCard { return new LogCardChannel(card_.getBasicChannel()); } - @Override - public CCID getReader() { - return reader; - } - @Override public void setLocale(Locale locale) { if (locale == null) { diff --git a/smcc/src/main/java/at/gv/egiz/smcc/BELPICCard.java b/smcc/src/main/java/at/gv/egiz/smcc/BELPICCard.java index e02a55d4..41358bb5 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/BELPICCard.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/BELPICCard.java @@ -18,6 +18,7 @@ package at.gv.egiz.smcc; +import at.gv.egiz.smcc.pin.gui.PINGUI; import java.io.IOException; import java.io.InputStream; import java.security.MessageDigest; @@ -110,7 +111,7 @@ public class BELPICCard extends AbstractSignatureCard implements SignatureCard { @Override @Exclusive - public byte[] getInfobox(String infobox, PINProvider provider, String domainId) + public byte[] getInfobox(String infobox, PINGUI provider, String domainId) throws SignatureCardException, InterruptedException { throw new IllegalArgumentException("Infobox '" + infobox @@ -120,7 +121,7 @@ public class BELPICCard extends AbstractSignatureCard implements SignatureCard { @Override @Exclusive public byte[] createSignature(InputStream input, KeyboxName keyboxName, - PINProvider provider, String alg) throws SignatureCardException, InterruptedException, IOException { + PINGUI provider, String alg) throws SignatureCardException, InterruptedException, IOException { if (KeyboxName.SECURE_SIGNATURE_KEYPAIR != keyboxName) { throw new SignatureCardException("Card does not support key " + keyboxName + "."); @@ -176,7 +177,7 @@ public class BELPICCard extends AbstractSignatureCard implements SignatureCard { } protected void verifyPINLoop(CardChannel channel, PINSpec spec, - PINProvider provider) throws LockedException, NotActivatedException, + PINGUI provider) throws LockedException, NotActivatedException, SignatureCardException, InterruptedException, CardException { int retries = -1; //verifyPIN(channel, spec, null, -1); @@ -186,7 +187,7 @@ public class BELPICCard extends AbstractSignatureCard implements SignatureCard { } protected int verifyPIN(CardChannel channel, PINSpec pinSpec, - PINProvider provider, int retries) throws SignatureCardException, + PINGUI provider, int retries) throws SignatureCardException, LockedException, NotActivatedException, InterruptedException, CardException { @@ -197,7 +198,7 @@ public class BELPICCard extends AbstractSignatureCard implements SignatureCard { (byte) 0xff, (byte) 0xff, (byte) 0xff }, 1, VerifyAPDUSpec.PIN_FORMAT_BCD, 7, 4, 4); - ResponseAPDU resp = reader.verify(channel, apduSpec, pinSpec, provider, retries); + ResponseAPDU resp = reader.verify(channel, apduSpec, provider, pinSpec, retries); if (resp.getSW() == 0x9000) { return -1; diff --git a/smcc/src/main/java/at/gv/egiz/smcc/ChangePINProvider.java b/smcc/src/main/java/at/gv/egiz/smcc/ChangePINProvider.java deleted file mode 100644 index 41010551..00000000 --- a/smcc/src/main/java/at/gv/egiz/smcc/ChangePINProvider.java +++ /dev/null @@ -1,36 +0,0 @@ -/* -* Copyright 2008 Federal Chancellery Austria and -* Graz University of Technology -* -* 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 at.gv.egiz.smcc; - -/** - * - * @author Clemens Orthacker - */ -public interface ChangePINProvider extends PINProvider { - - /** - * - * @param spec - * @param retries - * @return null if no old value for this pin - * @throws at.gv.egiz.smcc.CancelledException if cancelled by user - * @throws java.lang.InterruptedException - */ - public char[] provideOldPIN(PINSpec spec, int retries) - throws CancelledException, InterruptedException; - -} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/ITCard.java b/smcc/src/main/java/at/gv/egiz/smcc/ITCard.java index 831a1f9b..64389190 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/ITCard.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/ITCard.java @@ -17,6 +17,7 @@ package at.gv.egiz.smcc; +import at.gv.egiz.smcc.pin.gui.PINGUI; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; @@ -96,7 +97,7 @@ public class ITCard extends AbstractSignatureCard { @Override @Exclusive - public byte[] getInfobox(String infobox, PINProvider provider, String domainId) + public byte[] getInfobox(String infobox, PINGUI provider, String domainId) throws SignatureCardException, InterruptedException { throw new IllegalArgumentException("Infobox '" + infobox @@ -106,7 +107,7 @@ public class ITCard extends AbstractSignatureCard { @Override @Exclusive public byte[] createSignature(InputStream input, KeyboxName keyboxName, - PINProvider provider, String alg) throws SignatureCardException, + PINGUI provider, String alg) throws SignatureCardException, InterruptedException, IOException { if (KeyboxName.SECURE_SIGNATURE_KEYPAIR != keyboxName) { @@ -159,7 +160,7 @@ public class ITCard extends AbstractSignatureCard { } protected void verifyPINLoop(CardChannel channel, PINSpec spec, - PINProvider provider) throws LockedException, NotActivatedException, + PINGUI provider) throws LockedException, NotActivatedException, SignatureCardException, InterruptedException, CardException { int retries = -1; @@ -169,7 +170,7 @@ public class ITCard extends AbstractSignatureCard { } protected int verifyPIN(CardChannel channel, PINSpec pinSpec, - PINProvider provider, int retries) throws SignatureCardException, + PINGUI provider, int retries) throws SignatureCardException, LockedException, NotActivatedException, InterruptedException, CardException { @@ -180,7 +181,7 @@ public class ITCard extends AbstractSignatureCard { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff }, 0, VerifyAPDUSpec.PIN_FORMAT_ASCII, 8); - ResponseAPDU resp = reader.verify(channel, apduSpec, pinSpec, provider, retries); + ResponseAPDU resp = reader.verify(channel, apduSpec, provider, pinSpec, retries); if (resp.getSW() == 0x9000) { return -2; diff --git a/smcc/src/main/java/at/gv/egiz/smcc/PINConfirmationException.java b/smcc/src/main/java/at/gv/egiz/smcc/PINConfirmationException.java index eaf38435..24dfa53c 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/PINConfirmationException.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/PINConfirmationException.java @@ -23,23 +23,4 @@ package at.gv.egiz.smcc; * @author Clemens Orthacker */ public class PINConfirmationException extends SignatureCardException { - - private static final long serialVersionUID = 1L; - - public PINConfirmationException() { - super(); - } - - public PINConfirmationException(String message, Throwable cause) { - super(message, cause); - } - - public PINConfirmationException(String message) { - super(message); - } - - public PINConfirmationException(Throwable cause) { - super(cause); - } - } diff --git a/smcc/src/main/java/at/gv/egiz/smcc/PINFormatException.java b/smcc/src/main/java/at/gv/egiz/smcc/PINFormatException.java index 774fcdf5..721c63e2 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/PINFormatException.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/PINFormatException.java @@ -23,23 +23,4 @@ package at.gv.egiz.smcc; * @author Clemens Orthacker */ public class PINFormatException extends SignatureCardException { - - private static final long serialVersionUID = 1L; - - public PINFormatException() { - super(); - } - - public PINFormatException(String message, Throwable cause) { - super(message, cause); - } - - public PINFormatException(String message) { - super(message); - } - - public PINFormatException(Throwable cause) { - super(cause); - } - } diff --git a/smcc/src/main/java/at/gv/egiz/smcc/PINMgmtSignatureCard.java b/smcc/src/main/java/at/gv/egiz/smcc/PINMgmtSignatureCard.java index 53738612..5091c10f 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/PINMgmtSignatureCard.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/PINMgmtSignatureCard.java @@ -16,6 +16,9 @@ */ package at.gv.egiz.smcc; +import at.gv.egiz.smcc.pin.gui.ModifyPINGUI; + +import at.gv.egiz.smcc.pin.gui.PINGUI; import java.util.List; public interface PINMgmtSignatureCard extends SignatureCard { @@ -26,16 +29,16 @@ public interface PINMgmtSignatureCard extends SignatureCard { public PIN_STATE getPINState(PINSpec pinSpec) throws SignatureCardException; - public void verifyPIN(PINSpec pinSpec, PINProvider pinProvider) + public void verifyPIN(PINSpec pinSpec, PINGUI pinGUI) throws LockedException, NotActivatedException, CancelledException, SignatureCardException, InterruptedException; - public void changePIN(PINSpec pinSpec, ChangePINProvider pinProvider) + public void changePIN(PINSpec pinSpec, ModifyPINGUI changePINGUI) throws LockedException, NotActivatedException, CancelledException, PINFormatException, SignatureCardException, InterruptedException; - public void activatePIN(PINSpec pinSpec, PINProvider pinProvider) + public void activatePIN(PINSpec pinSpec, ModifyPINGUI activatePINGUI) throws CancelledException, SignatureCardException, InterruptedException; - public void unblockPIN(PINSpec pinSpec, PINProvider pukProvider) + public void unblockPIN(PINSpec pinSpec, ModifyPINGUI pukGUI) throws CancelledException, SignatureCardException, InterruptedException; } diff --git a/smcc/src/main/java/at/gv/egiz/smcc/PINProvider.java b/smcc/src/main/java/at/gv/egiz/smcc/PINProvider.java deleted file mode 100644 index 5c294b5b..00000000 --- a/smcc/src/main/java/at/gv/egiz/smcc/PINProvider.java +++ /dev/null @@ -1,45 +0,0 @@ -/* -* Copyright 2008 Federal Chancellery Austria and -* Graz University of Technology -* -* 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 at.gv.egiz.smcc; - -/** - * The number of retries is not fixed and there is no way (?) to obtain this value. - * A PINProvider should therefore maintain an internal retry counter or flag - * to decide whether or not to warn the user (num retries passed in providePIN). - * - * Therefore PINProvider objects should not be reused. - * - * (ACOS: reload counter: between 0 and 15, where 15 meens deactivated) - * - * @author Clemens Orthacker - */ -public interface PINProvider { - - /** - * - * @param spec - * @param retries num of remaining retries or -1 if unknown - * (a positive value does not necessarily signify that there was - * already an unsuccessful PIN verification) - * @return pin != null - * @throws at.gv.egiz.smcc.CancelledException - * @throws java.lang.InterruptedException - */ - public char[] providePIN(PINSpec spec, int retries) - throws CancelledException, InterruptedException; - -} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/ResetRetryCounterAPDUSpec.java b/smcc/src/main/java/at/gv/egiz/smcc/ResetRetryCounterAPDUSpec.java new file mode 100644 index 00000000..7e71eb7e --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/ResetRetryCounterAPDUSpec.java @@ -0,0 +1,38 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.smcc; + +public class ResetRetryCounterAPDUSpec extends ChangeReferenceDataAPDUSpec { + + /** + * @param apdu + * @param pukPosition + * @param pukFormat + * @param pukLength + * @param pukLengthSize + * @param pukLengthPos + * @param pinInsertionOffsetNew + */ + public ResetRetryCounterAPDUSpec(byte[] apdu, int pukPosition, + int pukFormat, int pukLength, int pukLengthSize, int pukLengthPos, + int pinInsertionOffsetNew) { + super(apdu, pukPosition, pukFormat, pukLength, pukLengthSize, pukLengthPos); + this.pinInsertionOffsetNew = pinInsertionOffsetNew; + } + + +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java b/smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java index 79a4cc69..ad05352f 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java @@ -17,6 +17,8 @@ package at.gv.egiz.smcc; +import at.gv.egiz.smcc.pin.gui.ModifyPINGUI; +import at.gv.egiz.smcc.pin.gui.PINGUI; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; @@ -225,7 +227,7 @@ public class STARCOSCard extends AbstractSignatureCard implements PINMgmtSignatu @Override @Exclusive - public byte[] getInfobox(String infobox, PINProvider provider, String domainId) + public byte[] getInfobox(String infobox, PINGUI pinGUI, String domainId) throws SignatureCardException, InterruptedException { try { @@ -243,7 +245,7 @@ public class STARCOSCard extends AbstractSignatureCard implements PINMgmtSignatu try { return ISO7816Utils.readTransparentFileTLV(channel, -1, (byte) 0x30); } catch (SecurityStatusNotSatisfiedException e) { - verifyPINLoop(channel, spec, provider); + verifyPINLoop(channel, spec, pinGUI); } } @@ -301,7 +303,7 @@ public class STARCOSCard extends AbstractSignatureCard implements PINMgmtSignatu @Override @Exclusive public byte[] createSignature(InputStream input, KeyboxName keyboxName, - PINProvider provider, String alg) throws SignatureCardException, InterruptedException, IOException { + PINGUI provider, String alg) throws SignatureCardException, InterruptedException, IOException { ByteArrayOutputStream dst = new ByteArrayOutputStream(); byte[] ht = null; @@ -431,7 +433,7 @@ public class STARCOSCard extends AbstractSignatureCard implements PINMgmtSignatu */ @Override @Exclusive - public void verifyPIN(PINSpec pinSpec, PINProvider pinProvider) + public void verifyPIN(PINSpec pinSpec, PINGUI pinProvider) throws LockedException, NotActivatedException, CancelledException, TimeoutException, SignatureCardException, InterruptedException { @@ -442,12 +444,7 @@ public class STARCOSCard extends AbstractSignatureCard implements PINMgmtSignatu // SELECT application execSELECT_AID(channel, pinSpec.getContextAID()); } - log.debug("*** verifyPIN loop"); verifyPINLoop(channel, pinSpec, pinProvider); -// log.debug("*** verifyPIN 0"); -// int retries = verifyPIN(channel, pinSpec, null, 0); -// log.debug("*** verifyPIN " + retries + " tries"); -// verifyPIN(channel, pinSpec, pinProvider, retries); } catch (CardException e) { log.info("Failed to verify PIN.", e); throw new SignatureCardException("Failed to verify PIN.", e); @@ -460,7 +457,7 @@ public class STARCOSCard extends AbstractSignatureCard implements PINMgmtSignatu */ @Override @Exclusive - public void changePIN(PINSpec pinSpec, ChangePINProvider pinProvider) + public void changePIN(PINSpec pinSpec, ModifyPINGUI pinGUI) throws LockedException, NotActivatedException, CancelledException, TimeoutException, SignatureCardException, InterruptedException { @@ -471,9 +468,7 @@ public class STARCOSCard extends AbstractSignatureCard implements PINMgmtSignatu // SELECT application execSELECT_AID(channel, pinSpec.getContextAID()); } - changePINLoop(channel, pinSpec, pinProvider); -// int retries = verifyPIN(channel, pinSpec, null, 0); -// changePIN(channel, pinSpec, pinProvider, retries); + changePINLoop(channel, pinSpec, pinGUI); } catch (CardException e) { log.info("Failed to change PIN.", e); throw new SignatureCardException("Failed to change PIN.", e); @@ -486,7 +481,7 @@ public class STARCOSCard extends AbstractSignatureCard implements PINMgmtSignatu */ @Override @Exclusive - public void activatePIN(PINSpec pinSpec, PINProvider pinProvider) + public void activatePIN(PINSpec pinSpec, ModifyPINGUI activatePINGUI) throws CancelledException, SignatureCardException, CancelledException, TimeoutException, InterruptedException { @@ -497,7 +492,7 @@ public class STARCOSCard extends AbstractSignatureCard implements PINMgmtSignatu // SELECT application execSELECT_AID(channel, pinSpec.getContextAID()); } - activatePIN(channel, pinSpec, pinProvider); + activatePIN(channel, pinSpec, activatePINGUI); } catch (CardException e) { log.info("Failed to activate PIN.", e); throw new SignatureCardException("Failed to activate PIN.", e); @@ -509,9 +504,16 @@ public class STARCOSCard extends AbstractSignatureCard implements PINMgmtSignatu * @see at.gv.egiz.smcc.PINMgmtSignatureCard#unblockPIN(at.gv.egiz.smcc.PINSpec, at.gv.egiz.smcc.PINProvider) */ @Override - public void unblockPIN(PINSpec pinSpec, PINProvider pukProvider) + public void unblockPIN(PINSpec pinSpec, ModifyPINGUI pukProvider) throws CancelledException, SignatureCardException, InterruptedException { - throw new SignatureCardException("Unblock PIN is not supported."); + CardChannel channel = getCardChannel(); + + try { + unblockPINLoop(channel, pinSpec, pukProvider); + } catch (CardException e) { + log.info("Failed to activate PIN.", e); + throw new SignatureCardException("Failed to activate PIN.", e); + } } @Override @@ -574,7 +576,7 @@ public class STARCOSCard extends AbstractSignatureCard implements PINMgmtSignatu // PROTECTED METHODS (assume exclusive card access) //////////////////////////////////////////////////////////////////////// - protected void verifyPINLoop(CardChannel channel, PINSpec spec, PINProvider provider) + protected void verifyPINLoop(CardChannel channel, PINSpec spec, PINGUI provider) throws LockedException, NotActivatedException, SignatureCardException, InterruptedException, CardException { @@ -584,7 +586,7 @@ public class STARCOSCard extends AbstractSignatureCard implements PINMgmtSignatu } while (retries > 0); } - protected void changePINLoop(CardChannel channel, PINSpec spec, ChangePINProvider provider) + protected void changePINLoop(CardChannel channel, PINSpec spec, ModifyPINGUI provider) throws LockedException, NotActivatedException, SignatureCardException, InterruptedException, CardException { @@ -594,8 +596,19 @@ public class STARCOSCard extends AbstractSignatureCard implements PINMgmtSignatu } while (retries > 0); } + protected void unblockPINLoop(CardChannel channel, PINSpec spec, ModifyPINGUI provider) + throws LockedException, NotActivatedException, SignatureCardException, + InterruptedException, CardException { + + //TODO get PUK retry counter from EF FID 0036 in MF + int retries = -1; + do { + retries = unblockPIN(channel, spec, provider, retries); + } while (retries > 0); + } + protected int verifyPIN(CardChannel channel, PINSpec pinSpec, - PINProvider provider, int retries) throws SignatureCardException, + PINGUI provider, int retries) throws SignatureCardException, LockedException, NotActivatedException, InterruptedException, CardException { @@ -608,108 +621,135 @@ public class STARCOSCard extends AbstractSignatureCard implements PINMgmtSignatu ResponseAPDU resp; if (provider != null) { - resp = reader.verify(channel, apduSpec, pinSpec, provider, retries); + resp = reader.verify(channel, apduSpec, provider, pinSpec, retries); } else { resp = channel.transmit(new CommandAPDU(0x00, 0x20, 0x00, pinSpec.getKID())); } - + + if (resp.getSW() == 0x9000) { return -1; - } - if (resp.getSW() == 0x63c0) { - // returned by the 'short' VERIFY + } else if (resp.getSW() == 0x6983 || resp.getSW() == 0x63c0) { + // authentication method blocked (0x63c0 returned by 'short' VERIFY) throw new LockedException(); - } - if (resp.getSW() >> 4 == 0x63c) { - return 0x0f & resp.getSW(); - } - - switch (resp.getSW()) { - case 0x6983: - // authentication method blocked - // returned by the 'long' VERIFY - throw new LockedException(); - case 0x6984: - // reference data not usable + } else if (resp.getSW() == 0x6984 || resp.getSW() == 0x6985) { + // reference data not usable; conditions of use not satisfied throw new NotActivatedException(); - case 0x6985: - // conditions of use not satisfied + } else if (resp.getSW() >> 4 == 0x63c) { + return 0x0f & resp.getSW(); + } else if (version > 1.2 && resp.getSW() == 0x6400) { + log.warn("cannot query pin status prior to card activation"); throw new NotActivatedException(); - - default: + } else { String msg = "VERIFY failed. SW=" + Integer.toHexString(resp.getSW()); log.info(msg); throw new SignatureCardException(msg); } - } protected int changePIN(CardChannel channel, PINSpec pinSpec, - ChangePINProvider pinProvider, int retries) throws CancelledException, + ModifyPINGUI pinProvider, int retries) throws CancelledException, InterruptedException, CardException, SignatureCardException { - - ChangeReferenceDataAPDUSpec apduSpec = new ChangeReferenceDataAPDUSpec( - new byte[] { - (byte) 0x00, (byte) 0x24, (byte) 0x00, pinSpec.getKID(), (byte) 0x10, - (byte) 0x20, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, - (byte) 0xff, (byte) 0xff, (byte) 0xff, - (byte) 0x20, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, - (byte) 0xff, (byte) 0xff, (byte) 0xff }, + + ChangeReferenceDataAPDUSpec apduSpec = new ChangeReferenceDataAPDUSpec( + new byte[] { + (byte) 0x00, (byte) 0x24, (byte) 0x00, pinSpec.getKID(), (byte) 0x10, + (byte) 0x20, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0x20, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff }, 1, VerifyAPDUSpec.PIN_FORMAT_BCD, 7, 4, 4, 8); - - ResponseAPDU resp = reader.modify(channel, apduSpec, pinSpec, pinProvider, retries); - + + ResponseAPDU resp = reader.modify(channel, apduSpec, pinProvider, pinSpec, retries); + if (resp.getSW() == 0x9000) { return -1; - } - if (resp.getSW() >> 4 == 0x63c) { - return 0x0f & resp.getSW(); - } - - switch (resp.getSW()) { - case 0x6983: + } else if (resp.getSW() == 0x6983) { // authentication method blocked throw new LockedException(); - - default: + } else if (resp.getSW() == 0x6984) { + throw new NotActivatedException(); + } else if (resp.getSW() >> 4 == 0x63c) { + return 0x0f & resp.getSW(); + } else { String msg = "CHANGE REFERENCE DATA failed. SW=" + Integer.toHexString(resp.getSW()); log.info(msg); throw new SignatureCardException(msg); } - - } protected int activatePIN(CardChannel channel, PINSpec pinSpec, - PINProvider provider) throws SignatureCardException, + ModifyPINGUI provider) throws SignatureCardException, InterruptedException, CardException { - NewReferenceDataAPDUSpec apduSpec = new NewReferenceDataAPDUSpec( + ResponseAPDU resp; + if (version < 1.2) { + NewReferenceDataAPDUSpec apduSpec = new NewReferenceDataAPDUSpec( + new byte[] { + (byte) 0x00, (byte) 0x24, (byte) 0x01, pinSpec.getKID(), (byte) 0x08, + (byte) 0x20, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff }, + 1, VerifyAPDUSpec.PIN_FORMAT_BCD, 7, 4, 4); + + resp = reader.modify(channel, apduSpec, provider, pinSpec); + } else { + NewReferenceDataAPDUSpec apduSpec = new NewReferenceDataAPDUSpec( + new byte[] { + (byte) 0x00, (byte) 0x24, (byte) 0x00, pinSpec.getKID(), (byte) 0x10, + (byte) 0x26, (byte) 0x12, (byte) 0x34, (byte) 0x56, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0x20, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff }, + 1, VerifyAPDUSpec.PIN_FORMAT_BCD, 7, 4, 4); + apduSpec.setPinInsertionOffsetNew(8); + resp = reader.modify(channel, apduSpec, provider, pinSpec); + } + + if (resp.getSW() == 0x9000) { + return -1; + } else { + String msg = "CHANGE REFERENCE DATA failed. SW=" + Integer.toHexString(resp.getSW()); + log.info(msg); + throw new SignatureCardException(msg); + } + } + + protected int unblockPIN(CardChannel channel, PINSpec pinSpec, + ModifyPINGUI provider, int retries) throws SignatureCardException, + InterruptedException, CardException { + + if (version < 1.2) { + // would return 0x6982 (Security status not satisfied) + throw new SignatureCardException("RESET RETRY COUNTER is not supported by this card."); + } + + ResetRetryCounterAPDUSpec apduSpec = new ResetRetryCounterAPDUSpec( new byte[] { - (byte) 0x00, (byte) 0x24, (byte) 0x01, pinSpec.getKID(), (byte) 0x08, + (byte) 0x00, (byte) 0x2c, (byte) 0x00, pinSpec.getKID(), (byte) 0x10, (byte) 0x20, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, - (byte) 0xff, (byte) 0xff, (byte) 0xff }, - 1, VerifyAPDUSpec.PIN_FORMAT_BCD, 7, 4, 4); - - ResponseAPDU resp = reader.activate(channel, apduSpec, pinSpec, provider); - - switch (resp.getSW()) { - - case 0x9000: - return -1; + (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0x20, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff }, + 1, VerifyAPDUSpec.PIN_FORMAT_BCD, 7, 4, 4, 8); - case 0x6983: - // authentication method blocked - throw new LockedException(); + ResponseAPDU resp = reader.modify(channel, apduSpec, provider, pinSpec, retries); - default: - String msg = "CHANGE REFERENCE DATA failed. SW=" + Integer.toHexString(resp.getSW()); + if (resp.getSW() == 0x9000) { + return -1; + } else if (resp.getSW() == 0x6983) { + // PUK blocked + throw new LockedException(); + } else if (resp.getSW() == 0x6984) { + throw new NotActivatedException(); + } else if (resp.getSW() >> 4 == 0x63c) { + return 0x0f & resp.getSW(); + } else { + String msg = "RESET RETRY COUNTER failed. SW=" + Integer.toHexString(resp.getSW()); log.info(msg); throw new SignatureCardException(msg); } - } - + protected void execSELECT_MF(CardChannel channel) throws CardException, SignatureCardException { ResponseAPDU resp = channel.transmit( new CommandAPDU(0x00, 0xA4, 0x00, 0x0C)); diff --git a/smcc/src/main/java/at/gv/egiz/smcc/SWCard.java b/smcc/src/main/java/at/gv/egiz/smcc/SWCard.java index 670704d5..73c7faa8 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/SWCard.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/SWCard.java @@ -40,15 +40,12 @@ import java.util.Enumeration; import java.util.Locale; import javax.smartcardio.Card; -import javax.smartcardio.CardChannel; -import javax.smartcardio.CardException; import javax.smartcardio.CardTerminal; -import javax.smartcardio.ResponseAPDU; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import at.gv.egiz.smcc.ccid.CCID; +import at.gv.egiz.smcc.pin.gui.PINGUI; /** * @@ -280,7 +277,7 @@ public class SWCard implements SignatureCard { } - public byte[] getInfobox(String infobox, PINProvider provider, String domainId) throws SignatureCardException { + public byte[] getInfobox(String infobox, PINGUI provider, String domainId) throws SignatureCardException { String fileName = getFileName(infobox + ".ibx"); FileInputStream file; @@ -309,7 +306,7 @@ public class SWCard implements SignatureCard { } @Override - public byte[] createSignature(InputStream input, KeyboxName keyboxName, PINProvider provider, String alg) throws SignatureCardException, InterruptedException, IOException { + public byte[] createSignature(InputStream input, KeyboxName keyboxName, PINGUI provider, String alg) throws SignatureCardException, InterruptedException, IOException { // KeyStore password char[] password = getPassword(keyboxName); @@ -396,101 +393,4 @@ public class SWCard implements SignatureCard { @Override public void reset() throws SignatureCardException { } - - @Override - public CCID getReader() { - return new CCID() { - - @Override - public boolean hasFeature(Byte feature) { - return false; - } - - @Override - public byte getbTimeOut() { - return 0; - } - - @Override - public byte getbTimeOut2() { - return 0; - } - - @Override - public byte getwPINMaxExtraDigitL() { - return 0x12; - } - - @Override - public byte getwPINMaxExtraDigitH() { - return 0x00; - } - - @Override - public byte getbEntryValidationCondition() { - return 0x02; - } - - @Override - public Card connect() { - return null; - } - - @Override - public String getName() { - return "Software CardReader"; - } - - @Override - public byte[] verifyPin(byte[] PIN_VERIFY) throws CardException { - throw new UnsupportedOperationException("Not supported yet."); - } - - @Override - public byte[] verifyPinDirect(byte[] PIN_VERIFY) throws CardException { - throw new UnsupportedOperationException("Not supported yet."); - } - - @Override - public byte[] modifyPin(byte[] PIN_MODIFY) throws CardException { - throw new UnsupportedOperationException("Not supported yet."); - } - - @Override - public byte[] modifyPinDirect(byte[] PIN_MODIFY) throws CardException { - throw new UnsupportedOperationException("Not supported yet."); - } - - @Override - public void setDisablePinpad(boolean disable) { - throw new UnsupportedOperationException("Not supported yet."); - } - - @Override - public ResponseAPDU verify(CardChannel channel, VerifyAPDUSpec apduSpec, - PINSpec pinSpec, PINProvider provider, int retries) - throws CancelledException, InterruptedException, CardException, - SignatureCardException { - throw new UnsupportedOperationException("Not supported yet."); - } - - @Override - public ResponseAPDU activate(CardChannel channel, - NewReferenceDataAPDUSpec apduSpec, PINSpec pinSpec, - PINProvider provider) throws CancelledException, - InterruptedException, CardException, SignatureCardException { - // TODO Auto-generated method stub - return null; - } - - @Override - public ResponseAPDU modify(CardChannel channel, - ChangeReferenceDataAPDUSpec apduSpec, PINSpec pinSpec, - ChangePINProvider provider, int retries) throws CancelledException, - InterruptedException, CardException, SignatureCardException { - // TODO Auto-generated method stub - return null; - } - }; - } } diff --git a/smcc/src/main/java/at/gv/egiz/smcc/SignatureCard.java b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCard.java index 3d56f97b..fa589b84 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/SignatureCard.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCard.java @@ -17,8 +17,7 @@ package at.gv.egiz.smcc; -import at.gv.egiz.smcc.ccid.CCID; - +import at.gv.egiz.smcc.pin.gui.PINGUI; import java.io.IOException; import java.io.InputStream; import java.util.Locale; @@ -99,7 +98,7 @@ public interface SignatureCard { * @throws SignatureCardException * @throws InterruptedException if applet is destroyed while in pin dialog */ - public byte[] getInfobox(String infobox, PINProvider provider, String domainId) + public byte[] getInfobox(String infobox, PINGUI pinGUI, String domainId) throws SignatureCardException, InterruptedException; /** @@ -114,9 +113,7 @@ public interface SignatureCard { * @throws IOException */ public byte[] createSignature(InputStream input, KeyboxName keyboxName, - PINProvider provider, String alg) throws SignatureCardException, InterruptedException, IOException; - - public CCID getReader(); + PINGUI pinGUI, String alg) throws SignatureCardException, InterruptedException, IOException; /** * Sets the local for evtl. required callbacks (e.g. PINSpec) diff --git a/smcc/src/main/java/at/gv/egiz/smcc/pin/gui/ModifyPINGUI.java b/smcc/src/main/java/at/gv/egiz/smcc/pin/gui/ModifyPINGUI.java new file mode 100644 index 00000000..00dc2d0e --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/pin/gui/ModifyPINGUI.java @@ -0,0 +1,36 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.smcc.pin.gui; + +import at.gv.egiz.smcc.CancelledException; +import at.gv.egiz.smcc.PINSpec; + + +public interface ModifyPINGUI extends ModifyPINProvider { + + void modifyPINDirect(PINSpec spec, int retries) throws CancelledException, InterruptedException; + void finishDirect(); + + void enterCurrentPIN(PINSpec spec, int retries); + void enterNewPIN(PINSpec spec); + void confirmNewPIN(PINSpec spec); + void validKeyPressed(); + void correctionButtonPressed(); + void allKeysCleared(); + /** called prior to MODIFY_PIN_FINISH control command transmission (clear display or display wait message) */ + void finish(); +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/pin/gui/ModifyPINProvider.java b/smcc/src/main/java/at/gv/egiz/smcc/pin/gui/ModifyPINProvider.java new file mode 100644 index 00000000..36f0097d --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/pin/gui/ModifyPINProvider.java @@ -0,0 +1,48 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.smcc.pin.gui; + +import at.gv.egiz.smcc.CancelledException; +import at.gv.egiz.smcc.PINSpec; + + +/** + * user interface for "software pin-entry" of + *
    + *
  • current pin and new pin (change pin) + *
  • new pin (pin activation, no current pin) + *
  • puk and new pin (probably verify only?) + *
+ * @author Clemens Orthacker + */ +public interface ModifyPINProvider { + + /** + * + * @param spec + * @param retries + * @return null if no old value for this pin + * @throws at.gv.egiz.smcc.CancelledException if cancelled by user + * @throws java.lang.InterruptedException + */ + public char[] provideCurrentPIN(PINSpec spec, int retries) + throws CancelledException, InterruptedException; + + public char[] provideNewPIN(PINSpec spec) + throws CancelledException, InterruptedException; + +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/pin/gui/PINGUI.java b/smcc/src/main/java/at/gv/egiz/smcc/pin/gui/PINGUI.java new file mode 100644 index 00000000..5199977b --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/pin/gui/PINGUI.java @@ -0,0 +1,42 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.smcc.pin.gui; + +import at.gv.egiz.smcc.CancelledException; +import at.gv.egiz.smcc.PINSpec; + + +/** + * Display messages for pinpad pin-entry. + * Provides an interface for two types of pinpad pin-entry: pinpad-direct and pinpad-start/finish + * @author clemens.orthacker@iaik.tugraz.at + */ +public interface PINGUI extends PINProvider { + + void enterPINDirect(PINSpec spec, int retries) + throws CancelledException, InterruptedException; + + /** + * @throws CancelledException, InterruptedException if signature-data dialog is interrupted or cancelled + */ + void enterPIN(PINSpec spec, int retries) + throws CancelledException, InterruptedException; + void validKeyPressed(); + void correctionButtonPressed(); + void allKeysCleared(); + +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/pin/gui/PINProvider.java b/smcc/src/main/java/at/gv/egiz/smcc/pin/gui/PINProvider.java new file mode 100644 index 00000000..3bf49888 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/pin/gui/PINProvider.java @@ -0,0 +1,49 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.smcc.pin.gui; + +import at.gv.egiz.smcc.CancelledException; +import at.gv.egiz.smcc.PINSpec; + + +/** + * The number of retries is not fixed and there is no way (?) to obtain this value. + * A PINProvider should therefore maintain an internal retry counter or flag + * to decide whether or not to warn the user (num retries passed in providePIN). + * + * Therefore PINProvider objects should not be reused. + * + * (ACOS: reload counter: between 0 and 15, where 15 meens deactivated) + * + * @author Clemens Orthacker + */ +public interface PINProvider { + + /** + * + * @param spec + * @param retries num of remaining retries or -1 if unknown + * (a positive value does not necessarily signify that there was + * already an unsuccessful PIN verification) + * @return pin != null + * @throws at.gv.egiz.smcc.CancelledException + * @throws java.lang.InterruptedException + */ + char[] providePIN(PINSpec pinSpec, int retries) + throws CancelledException, InterruptedException; + +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/reader/CardReader.java b/smcc/src/main/java/at/gv/egiz/smcc/reader/CardReader.java new file mode 100644 index 00000000..a1246dd6 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/reader/CardReader.java @@ -0,0 +1,92 @@ +/* + * Copyright 2008 Federal Chancellery Austria and + * Graz University of Technology + * + * 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 at.gv.egiz.smcc.reader; + +import javax.smartcardio.CardChannel; +import javax.smartcardio.CardException; +import javax.smartcardio.ResponseAPDU; + +import at.gv.egiz.smcc.CancelledException; +import at.gv.egiz.smcc.ChangeReferenceDataAPDUSpec; +import at.gv.egiz.smcc.NewReferenceDataAPDUSpec; +import at.gv.egiz.smcc.PINSpec; +import at.gv.egiz.smcc.ResetRetryCounterAPDUSpec; +import at.gv.egiz.smcc.SignatureCardException; +import at.gv.egiz.smcc.VerifyAPDUSpec; +import at.gv.egiz.smcc.pin.gui.ModifyPINGUI; +import at.gv.egiz.smcc.pin.gui.PINGUI; +import javax.smartcardio.Card; + +/** + * + * @author Clemens Orthacker + */ +public interface CardReader { + + + String[] FEATURES = new String[]{"NO_FEATURE", + "FEATURE_VERIFY_PIN_START", + "FEATURE_VERIFY_PIN_FINISH", + "FEATURE_MODIFY_PIN_START", + "FEATURE_MODIFY_PIN_FINISH", + "FEATURE_GET_KEY_PRESSED", + "FEATURE_VERIFY_PIN_DIRECT", + "FEATURE_MODIFY_PIN_DIRECT", + "FEATURE_MCT_READER_DIRECT", + "FEATURE_MCT_UNIVERSAL", + "FEATURE_IFD_PIN_PROPERTIES", + "FEATURE_ABORT", + "FEATURE_SET_SPE_MESSAGE", + "FEATURE_VERIFY_PIN_DIRECT_APP_ID", + "FEATURE_MODIFY_PIN_DIRECT_APP_ID", + "FEATURE_WRITE_DISPLAY", + "FEATURE_GET_KEY", + "FEATURE_IFD_DISPLAY_PROPERTIES"}; + + Byte FEATURE_VERIFY_PIN_START = new Byte((byte) 0x01); + Byte FEATURE_VERIFY_PIN_FINISH = new Byte((byte) 0x02); + Byte FEATURE_MODIFY_PIN_START = new Byte((byte) 0x03); + Byte FEATURE_MODIFY_PIN_FINISH = new Byte((byte) 0x04); + Byte FEATURE_GET_KEY_PRESSED = new Byte((byte) 0x05); + Byte FEATURE_VERIFY_PIN_DIRECT = new Byte((byte) 0x06); + Byte FEATURE_MODIFY_PIN_DIRECT = new Byte((byte) 0x07); + Byte FEATURE_MCT_READER_DIRECT = new Byte((byte) 0x08); + Byte FEATURE_MCT_UNIVERSAL = new Byte((byte) 0x09); + Byte FEATURE_IFD_PIN_PROPERTIES = new Byte((byte) 0x0a); + //TODO continue list + + + Card connect() throws CardException; + + boolean hasFeature(Byte feature); + + ResponseAPDU verify(CardChannel channel, VerifyAPDUSpec apduSpec, + PINGUI pinGUI, PINSpec pinSpec, int retries) + throws CancelledException, InterruptedException, CardException, SignatureCardException; + + ResponseAPDU modify(CardChannel channel, ChangeReferenceDataAPDUSpec apduSpec, + ModifyPINGUI pinGUI, PINSpec pinSpec, int retries) + throws CancelledException, InterruptedException, CardException, SignatureCardException; + + ResponseAPDU modify(CardChannel channel, NewReferenceDataAPDUSpec apduSpec, + ModifyPINGUI pinGUI, PINSpec pinSpec) + throws CancelledException, InterruptedException, CardException, SignatureCardException; + + ResponseAPDU modify(CardChannel channel, ResetRetryCounterAPDUSpec apduSpec, + ModifyPINGUI pinGUI, PINSpec pinSpec, int retries) + throws CancelledException, InterruptedException, CardException, SignatureCardException; +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/reader/DefaultCardReader.java b/smcc/src/main/java/at/gv/egiz/smcc/reader/DefaultCardReader.java new file mode 100644 index 00000000..45ea7a5a --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/reader/DefaultCardReader.java @@ -0,0 +1,106 @@ +/* + * Copyright 2008 Federal Chancellery Austria and + * Graz University of Technology + * + * 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 at.gv.egiz.smcc.reader; + + +import javax.smartcardio.Card; +import javax.smartcardio.CardChannel; +import javax.smartcardio.CardException; +import javax.smartcardio.CardTerminal; +import javax.smartcardio.ResponseAPDU; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import at.gv.egiz.smcc.ChangeReferenceDataAPDUSpec; +import at.gv.egiz.smcc.NewReferenceDataAPDUSpec; +import at.gv.egiz.smcc.PINSpec; +import at.gv.egiz.smcc.ResetRetryCounterAPDUSpec; +import at.gv.egiz.smcc.SignatureCardException; +import at.gv.egiz.smcc.VerifyAPDUSpec; +import at.gv.egiz.smcc.pin.gui.ModifyPINGUI; +import at.gv.egiz.smcc.pin.gui.PINGUI; +import at.gv.egiz.smcc.util.ISO7816Utils; + +/** + * + * @author Clemens Orthacker + */ +public class DefaultCardReader implements CardReader { + + protected final static Log log = LogFactory.getLog(DefaultCardReader.class); + + protected CardTerminal ct; + protected String name; + + public DefaultCardReader(CardTerminal ct) { + if (ct == null) { + throw new NullPointerException("no card or card terminal provided"); + } + this.ct = ct; + this.name = ct.getName(); + } + + @Override + public ResponseAPDU verify(CardChannel channel, VerifyAPDUSpec apduSpec, + PINGUI pinGUI, PINSpec pinSpec, int retries) + throws SignatureCardException, CardException, InterruptedException { + + log.debug("VERIFY"); + return channel.transmit(ISO7816Utils.createVerifyAPDU(apduSpec, pinGUI.providePIN(pinSpec, retries))); + } + + @Override + public ResponseAPDU modify(CardChannel channel, ChangeReferenceDataAPDUSpec apduSpec, + ModifyPINGUI pinGUI, PINSpec pinSpec, int retries) + throws SignatureCardException, CardException, InterruptedException { + log.debug("MODIFY (CHANGE_REFERENCE_DATA)"); + char[] oldPin = pinGUI.provideCurrentPIN(pinSpec, retries); + char[] newPin = pinGUI.provideNewPIN(pinSpec); + return channel.transmit(ISO7816Utils.createChangeReferenceDataAPDU(apduSpec, oldPin, newPin)); + } + + @Override + public ResponseAPDU modify(CardChannel channel, NewReferenceDataAPDUSpec apduSpec, + ModifyPINGUI pinGUI, PINSpec pinSpec) + throws SignatureCardException, CardException, InterruptedException { + log.debug("MODIFY (NEW_REFERENCE_DATA)"); + char[] newPIN = pinGUI.provideNewPIN(pinSpec); + return channel.transmit(ISO7816Utils.createNewReferenceDataAPDU(apduSpec, newPIN)); + } + + @Override + public ResponseAPDU modify(CardChannel channel, ResetRetryCounterAPDUSpec apduSpec, + ModifyPINGUI pinGUI, PINSpec pinSpec, int retries) + throws InterruptedException, CardException, SignatureCardException { + log.debug("MODIFY (RESET_RETRY_COUNTER)"); + //TODO + return modify(channel, (ChangeReferenceDataAPDUSpec) apduSpec, pinGUI, pinSpec, retries); + } + + @Override + public Card connect() throws CardException { + log.debug("connect icc"); + return ct.connect("*"); + } + + @Override + public boolean hasFeature(Byte feature) { + return false; + } + +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/reader/PinpadCardReader.java b/smcc/src/main/java/at/gv/egiz/smcc/reader/PinpadCardReader.java new file mode 100644 index 00000000..c2537af8 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/reader/PinpadCardReader.java @@ -0,0 +1,703 @@ +/* + * Copyright 2008 Federal Chancellery Austria and + * Graz University of Technology + * + * 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 at.gv.egiz.smcc.reader; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Map; + +import javax.smartcardio.Card; +import javax.smartcardio.CardChannel; +import javax.smartcardio.CardException; +import javax.smartcardio.CardTerminal; +import javax.smartcardio.ResponseAPDU; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import at.gv.egiz.smcc.CancelledException; +import at.gv.egiz.smcc.ChangeReferenceDataAPDUSpec; +import at.gv.egiz.smcc.NewReferenceDataAPDUSpec; +import at.gv.egiz.smcc.PINConfirmationException; +import at.gv.egiz.smcc.PINFormatException; +import at.gv.egiz.smcc.PINOperationAbortedException; +import at.gv.egiz.smcc.PINSpec; +import at.gv.egiz.smcc.ResetRetryCounterAPDUSpec; +import at.gv.egiz.smcc.SignatureCardException; +import at.gv.egiz.smcc.TimeoutException; +import at.gv.egiz.smcc.VerifyAPDUSpec; +import at.gv.egiz.smcc.pin.gui.ModifyPINGUI; +import at.gv.egiz.smcc.pin.gui.PINGUI; +import at.gv.egiz.smcc.util.SMCCHelper; + +/** + * + * @author Clemens Orthacker + */ +public class PinpadCardReader extends DefaultCardReader { + + public static final int PIN_ENTRY_POLLING_INTERVAL = 10; + + protected final static Log log = LogFactory.getLog(PinpadCardReader.class); + + protected byte bEntryValidationCondition = 0x02; // validation key pressed + protected byte bTimeOut = 0x3c; // 60sec (= max on ReinerSCT) + protected byte bTimeOut2 = 0x00; // default (attention with SCM) + protected byte wPINMaxExtraDigitH = 0x00; // min pin length zero digits + protected byte wPINMaxExtraDigitL = 0x0c; // max pin length 12 digits + + /** + * supported features and respective control codes + */ + protected Map features; + protected boolean VERIFY, MODIFY, VERIFY_DIRECT, MODIFY_DIRECT; + + public PinpadCardReader(CardTerminal ct, Map features) { + super(ct); + if (features == null) { + throw new NullPointerException("Pinpad card reader does not support any features"); + } + this.features = features; + + if (features.containsKey(FEATURE_VERIFY_PIN_START) && + features.containsKey(FEATURE_GET_KEY_PRESSED) && + features.containsKey(FEATURE_VERIFY_PIN_FINISH)) { + VERIFY = true; + } + if (features.containsKey(FEATURE_MODIFY_PIN_START) && + features.containsKey(FEATURE_GET_KEY_PRESSED) && + features.containsKey(FEATURE_MODIFY_PIN_FINISH)) { + MODIFY = true; + } + if (features.containsKey(FEATURE_VERIFY_PIN_DIRECT)) { + VERIFY_DIRECT = true; + } + if (features.containsKey(FEATURE_MODIFY_PIN_DIRECT)) { + MODIFY_DIRECT = true; + } + + if (name != null) { + name = name.toLowerCase(); + //ReinerSCT: http://support.reiner-sct.de/downloads/LINUX + // http://www.linux-club.de/viewtopic.php?f=61&t=101287&start=0 + //old: REINER SCT CyberJack 00 00 + //new (CCID): 0C4B/0300 Reiner-SCT cyberJack pinpad(a) 00 00 + //Snow Leopard: Reiner-SCT cyberJack pinpad(a) 00 00 + //display: REINER SCT CyberJack 00 00 + if(name.startsWith("gemplus gempc pinpad") || name.startsWith("gemalto gempc pinpad")) { + log.debug("setting custom wPINMaxExtraDigitH (0x04) for " + name); + wPINMaxExtraDigitH = 0x04; + log.debug("setting custom wPINMaxExtraDigitL (0x08) for " + name); + wPINMaxExtraDigitL = 0x08; + } else if (name.startsWith("omnikey cardman 3621")) { + log.debug("setting custom wPINMaxExtraDigitH (0x01) for " + name); + wPINMaxExtraDigitH = 0x01; + } else if (name.startsWith("scm spr 532") || name.startsWith("scm microsystems inc. sprx32 usb smart card reader")) { + log.debug("setting custom bTimeOut (0x3c) for " + name); + bTimeOut = 0x3c; + log.debug("setting custom bTimeOut2 (0x0f) for " + name); + bTimeOut2 = 0x0f; + } else if (name.startsWith("cherry smartboard xx44")) { + log.debug("setting custom wPINMaxExtraDigitH (0x01) for " + name); + wPINMaxExtraDigitH = 0x01; + } + } + + } + + @Override + public boolean hasFeature(Byte feature) { + return features.containsKey(feature); + } + + private void VERIFY_PIN_START(Card icc, byte[] PIN_VERIFY) throws CardException { + int ioctl = features.get(FEATURE_VERIFY_PIN_START); + if (log.isTraceEnabled()) { + log.trace("VERIFY_PIN_START (" + Integer.toHexString(ioctl) + + ") " + SMCCHelper.toString(PIN_VERIFY)); + } + byte[] resp = icc.transmitControlCommand(ioctl, PIN_VERIFY); + if (resp != null && resp.length > 0) { + if (resp[0] == (byte) 0x57) { + log.error("Invalid parameter in PIN_VERIFY structure"); + throw new CardException("ERROR_INVALID_PARAMETER"); + } else { + log.error("unexpected response to VERIFY_PIN_START: " + + SMCCHelper.toString(resp)); + throw new CardException("unexpected response to VERIFY_PIN_START: " + + SMCCHelper.toString(resp)); + } + } + } + + private byte GET_KEY_PRESSED(Card icc) throws CardException { + int ioctl = features.get(FEATURE_GET_KEY_PRESSED); + byte[] resp = icc.transmitControlCommand(ioctl, new byte[0]); + if (resp != null && resp.length == 1) { +// if (log.isTraceEnabled()) { +// log.trace("response " + SMCCHelper.toString(resp)); +// } + return resp[0]; + } + log.error("unexpected response to GET_KEY_PRESSED: " + + SMCCHelper.toString(resp)); + throw new CardException("unexpected response to GET_KEY_PRESSED: " + + SMCCHelper.toString(resp)); + } + + private byte[] VERIFY_PIN_FINISH(Card icc) throws CardException { + int ioctl = features.get(FEATURE_VERIFY_PIN_FINISH); + if (log.isTraceEnabled()) { + log.trace("VERIFY_PIN_FINISH (" + Integer.toHexString(ioctl) + ")"); + } + byte[] resp = icc.transmitControlCommand(ioctl, new byte[0]); + if (resp != null && resp.length == 2) { + if (log.isTraceEnabled()) { + log.trace("response " + SMCCHelper.toString(resp)); + } + return resp; + } + log.error("unexpected response to VERIFY_PIN_FINISH: " + + SMCCHelper.toString(resp)); + throw new CardException("unexpected response to VERIFY_PIN_FINISH: " + + SMCCHelper.toString(resp)); + } + + private void MODIFY_PIN_START(Card icc, byte[] PIN_MODIFY) throws CardException { + int ioctl = features.get(FEATURE_MODIFY_PIN_START); + if (log.isTraceEnabled()) { + log.trace("MODFIY_PIN_START (" + Integer.toHexString(ioctl) + + ") " + SMCCHelper.toString(PIN_MODIFY)); + } + byte[] resp = icc.transmitControlCommand(ioctl, PIN_MODIFY); + if (resp != null && resp.length > 0) { + if (resp[0] == (byte) 0x57) { + log.error("Invalid parameter in PIN_MODIFY structure"); + throw new CardException("ERROR_INVALID_PARAMETER"); + } else { + log.error("unexpected response to MODIFY_PIN_START: " + + SMCCHelper.toString(resp)); + throw new CardException("unexpected response to MODIFY_PIN_START: " + + SMCCHelper.toString(resp)); + } + } + } + + private byte[] MODIFY_PIN_FINISH(Card icc) throws CardException { + int ioctl = features.get(FEATURE_MODIFY_PIN_FINISH); + if (log.isTraceEnabled()) { + log.trace("MODIFY_PIN_FINISH (" + Integer.toHexString(ioctl) + ")"); + } + byte[] resp = icc.transmitControlCommand(ioctl, new byte[0]); + if (resp != null && resp.length == 2) { + if (log.isTraceEnabled()) { + log.trace("response " + SMCCHelper.toString(resp)); + } + return resp; + } + log.error("unexpected response to MODIFY_PIN_FINISH: " + + SMCCHelper.toString(resp)); + throw new CardException("unexpected response to MODIFY_PIN_FINISH: " + + SMCCHelper.toString(resp)); + } + + private byte[] VERIFY_PIN_DIRECT(Card icc, byte[] PIN_VERIFY) throws CardException { + int ioctl = features.get(FEATURE_VERIFY_PIN_DIRECT); + if (log.isTraceEnabled()) { + log.trace("VERIFY_PIN_DIRECT (" + Integer.toHexString(ioctl) + + ") " + SMCCHelper.toString(PIN_VERIFY)); + } + byte[] resp = icc.transmitControlCommand(ioctl, PIN_VERIFY); + if (log.isTraceEnabled()) { + log.trace("response " + SMCCHelper.toString(resp)); + } + return resp; + } + + private byte[] verifyPin(Card icc, byte[] PIN_VERIFY, PINGUI pinGUI) + throws SignatureCardException, CardException, InterruptedException { + +// pinGUI.enterPIN(pinSpec, retries); + + log.debug("VERIFY_PIN_START [" + FEATURES[FEATURE_VERIFY_PIN_START] + "]"); + VERIFY_PIN_START(icc, PIN_VERIFY); + + byte resp; + do { + resp = GET_KEY_PRESSED(icc); + if (resp == (byte) 0x00) { + synchronized(this) { + try { + wait(PIN_ENTRY_POLLING_INTERVAL); + } catch (InterruptedException ex) { + log.error("interrupted in VERIFY_PIN"); + } + } + } else if (resp == (byte) 0x0d) { + log.debug("GET_KEY_PRESSED: 0x0d (user confirmed)"); + break; + } else if (resp == (byte) 0x2b) { + log.trace("GET_KEY_PRESSED: 0x2b (user entered valid key 0-9)"); + pinGUI.validKeyPressed(); + } else if (resp == (byte) 0x1b) { + log.debug("GET_KEY_PRESSED: 0x1b (user cancelled VERIFY_PIN via cancel button)"); + break; // returns 0x6401 + } else if (resp == (byte) 0x08) { + log.debug("GET_KEY_PRESSED: 0x08 (user pressed correction/backspace button)"); + pinGUI.correctionButtonPressed(); + } else if (resp == (byte) 0x0e) { + log.debug("GET_KEY_PRESSED: 0x0e (timeout occured)"); + break; // return 0x6400 + } else if (resp == (byte) 0x40) { + log.debug("GET_KEY_PRESSED: 0x40 (PIN_Operation_Aborted)"); + throw new PINOperationAbortedException("PIN_Operation_Aborted (0x40)"); + } else if (resp == (byte) 0x0a) { + log.debug("GET_KEY_PRESSED: 0x0a (all keys cleared"); + pinGUI.allKeysCleared(); + } else { + log.error("unexpected response to GET_KEY_PRESSED: " + + Integer.toHexString(resp)); + throw new CardException("unexpected response to GET_KEY_PRESSED: " + + Integer.toHexString(resp)); + } + } while (true); + + return VERIFY_PIN_FINISH(icc); + } + + /** + * does not display the first pin dialog (enterCurrentPIN or enterNewPIN, depends on bConfirmPIN), + * since this is easier to do in calling modify() + */ + private byte[] modifyPin(Card icc, byte[] PIN_MODIFY, ModifyPINGUI pinGUI, PINSpec pINSpec) + throws PINOperationAbortedException, CardException { + + byte pinConfirmations = (byte) 0x00; //b0: new pin not entered (0) / entered (1) + //b1: current pin not entered (0) / entered (1) + byte bConfirmPIN = PIN_MODIFY[9]; + +// if ((bConfirmPIN & (byte) 0x02) == 0) { +// log.debug("no current PIN entry requested"); +// pinGUI.enterNewPIN(pINSpec); +// } else { +// log.debug("current PIN entry requested"); +// pinGUI.enterCurrentPIN(pINSpec, retries); +// } + + log.debug("MODIFY_PIN_START [" + FEATURES[FEATURE_MODIFY_PIN_START] + "]"); + MODIFY_PIN_START(icc, PIN_MODIFY); + + byte resp; + while (true) { + resp = GET_KEY_PRESSED(icc); + if (resp == (byte) 0x00) { + synchronized(this) { + try { + wait(PIN_ENTRY_POLLING_INTERVAL); + } catch (InterruptedException ex) { + log.error("interrupted in MODIFY_PIN"); + } + } + } else if (resp == (byte) 0x0d) { + if (log.isTraceEnabled()) { + log.trace("requested pin confirmations: 0b" + Integer.toBinaryString(bConfirmPIN & 0xff)); + log.trace("performed pin confirmations: 0b" + Integer.toBinaryString(pinConfirmations & 0xff)); + } + log.debug("GET_KEY_PRESSED: 0x0d (user confirmed)"); + if (pinConfirmations == bConfirmPIN) { + break; + } else if ((bConfirmPIN & (byte) 0x02) == 0 || + (pinConfirmations & (byte) 0x02) == (byte) 0x02) { + // no current pin entry or current pin entry already performed + if ((pinConfirmations & (byte) 0x01) == 0) { + // new pin + pinConfirmations |= (byte) 0x01; + pinGUI.confirmNewPIN(pINSpec); + } // else: new pin confirmed + } else { + // current pin entry + pinConfirmations |= (byte) 0x02; + pinGUI.enterNewPIN(pINSpec); + } + } else if (resp == (byte) 0x2b) { + log.trace("GET_KEY_PRESSED: 0x2b (user entered valid key 0-9)"); + pinGUI.validKeyPressed(); + } else if (resp == (byte) 0x1b) { + log.debug("GET_KEY_PRESSED: 0x1b (user cancelled VERIFY_PIN via cancel button)"); + break; // returns 0x6401 + } else if (resp == (byte) 0x08) { + log.debug("GET_KEY_PRESSED: 0x08 (user pressed correction/backspace button)"); + pinGUI.correctionButtonPressed(); + } else if (resp == (byte) 0x0e) { + log.debug("GET_KEY_PRESSED: 0x0e (timeout occured)"); + break; // return 0x6400 + } else if (resp == (byte) 0x40) { + log.debug("GET_KEY_PRESSED: 0x40 (PIN_Operation_Aborted)"); + throw new PINOperationAbortedException("PIN_Operation_Aborted (0x40)"); + } else if (resp == (byte) 0x0a) { + log.debug("GET_KEY_PRESSED: 0x0a (all keys cleared"); + pinGUI.allKeysCleared(); + } else { + log.error("unexpected response to GET_KEY_PRESSED: " + + Integer.toHexString(resp)); + throw new CardException("unexpected response to GET_KEY_PRESSED: " + + Integer.toHexString(resp)); + } + + } + + pinGUI.finish(); + return MODIFY_PIN_FINISH(icc); + } + + private byte[] MODIFY_PIN_DIRECT(Card icc, byte[] PIN_MODIFY) throws CardException { + int ioctl = features.get(FEATURE_MODIFY_PIN_DIRECT); + if (log.isTraceEnabled()) { + log.trace("MODIFY_PIN_DIRECT (" + Integer.toHexString(ioctl) + + ") " + SMCCHelper.toString(PIN_MODIFY)); + } + byte[] resp = icc.transmitControlCommand(ioctl, PIN_MODIFY); + if (log.isTraceEnabled()) { + log.trace("response " + SMCCHelper.toString(resp)); + } + return resp; + } + + protected byte[] createPINModifyStructure(NewReferenceDataAPDUSpec apduSpec, PINSpec pinSpec) { + + ByteArrayOutputStream s = new ByteArrayOutputStream(); + // bTimeOut + s.write(bTimeOut); + // bTimeOut2 + s.write(bTimeOut2); + // bmFormatString + s.write(1 << 7 // system unit = byte + | (0xF & apduSpec.getPinPosition()) << 3 + | (0x1 & apduSpec.getPinJustification() << 2) + | (0x3 & apduSpec.getPinFormat())); + // bmPINBlockString + s.write((0xF & apduSpec.getPinLengthSize()) << 4 + | (0xF & apduSpec.getPinLength())); + // bmPINLengthFormat + s.write(// system unit = bit + (0xF & apduSpec.getPinLengthPos())); + // bInsertionOffsetOld + s.write(0x00); + // bInsertionOffsetNew + s.write(apduSpec.getPinInsertionOffsetNew()); + // wPINMaxExtraDigit + s.write(Math.min(pinSpec.getMaxLength(), wPINMaxExtraDigitL)); + s.write(Math.max(pinSpec.getMinLength(), wPINMaxExtraDigitH)); + // bConfirmPIN + s.write(0x01); + // bEntryValidationCondition + s.write(bEntryValidationCondition); + // bNumberMessage + s.write(0x02); + // wLangId English (United States), see http://www.usb.org/developers/docs/USB_LANGIDs.pdf + s.write(0x09); + s.write(0x04); + // bMsgIndex1 + s.write(0x01); + // bMsgIndex2 + s.write(0x02); + // bMsgIndex3 + s.write(0x00); + + // bTeoPrologue + s.write(0x00); + s.write(0x00); + s.write(0x00); + // ulDataLength + s.write(apduSpec.getApdu().length); + s.write(0x00); + s.write(0x00); + s.write(0x00); + // abData + try { + s.write(apduSpec.getApdu()); + } catch (IOException e) { + // As we are dealing with ByteArrayOutputStreams no exception is to be + // expected. + throw new RuntimeException(e); + } + + return s.toByteArray(); + + } + + protected byte[] createPINModifyStructure(ChangeReferenceDataAPDUSpec apduSpec, PINSpec pinSpec) { + //TODO bInsertionOffsetOld (0x00), bConfirmPIN (0x01), bNumberMessage (0x02), bMsgIndex1/2/3 + + ByteArrayOutputStream s = new ByteArrayOutputStream(); + // bTimeOut + s.write(bTimeOut); + // bTimeOut2 + s.write(bTimeOut2); + // bmFormatString + s.write(1 << 7 // system unit = byte + | (0xF & apduSpec.getPinPosition()) << 3 + | (0x1 & apduSpec.getPinJustification() << 2) + | (0x3 & apduSpec.getPinFormat())); + // bmPINBlockString + s.write((0xF & apduSpec.getPinLengthSize()) << 4 + | (0xF & apduSpec.getPinLength())); + // bmPINLengthFormat + s.write(// system unit = bit + (0xF & apduSpec.getPinLengthPos())); + // bInsertionOffsetOld (0x00 for no old pin?) + s.write(apduSpec.getPinInsertionOffsetOld()); + // bInsertionOffsetNew + s.write(apduSpec.getPinInsertionOffsetNew()); + // wPINMaxExtraDigit + s.write(Math.min(pinSpec.getMaxLength(), wPINMaxExtraDigitL)); + s.write(Math.max(pinSpec.getMinLength(), wPINMaxExtraDigitH)); + // bConfirmPIN + s.write(0x03); + // bEntryValidationCondition + s.write(bEntryValidationCondition); + // bNumberMessage + s.write(0x03); + // wLangId English (United States), see http://www.usb.org/developers/docs/USB_LANGIDs.pdf + s.write(0x09); + s.write(0x04); + // bMsgIndex1 + s.write(0x00); + // bMsgIndex2 + s.write(0x01); + // bMsgIndex3 + s.write(0x02); + + // bTeoPrologue + s.write(0x00); + s.write(0x00); + s.write(0x00); + // ulDataLength + s.write(apduSpec.getApdu().length); + s.write(0x00); + s.write(0x00); + s.write(0x00); + // abData + try { + s.write(apduSpec.getApdu()); + } catch (IOException e) { + // As we are dealing with ByteArrayOutputStreams no exception is to be + // expected. + throw new RuntimeException(e); + } + + return s.toByteArray(); + + } + + protected byte[] createPINVerifyStructure(VerifyAPDUSpec apduSpec, PINSpec pinSpec) { + + ByteArrayOutputStream s = new ByteArrayOutputStream(); + // bTimeOut + s.write(bTimeOut); + // bTimeOut2 + s.write(bTimeOut2); + // bmFormatString + s.write(1 << 7 // system unit = byte + | (0xF & apduSpec.getPinPosition()) << 3 + | (0x1 & apduSpec.getPinJustification() << 2) + | (0x3 & apduSpec.getPinFormat())); + // bmPINBlockString + s.write((0xF & apduSpec.getPinLengthSize()) << 4 + | (0xF & apduSpec.getPinLength())); + // bmPINLengthFormat + s.write(// system unit = bit + (0xF & apduSpec.getPinLengthPos())); + // wPINMaxExtraDigit + s.write(Math.min(pinSpec.getMaxLength(), wPINMaxExtraDigitL)); // max PIN length + s.write(Math.max(pinSpec.getMinLength(), wPINMaxExtraDigitH)); // min PIN length + // bEntryValidationCondition + s.write(bEntryValidationCondition); + // bNumberMessage + s.write(0x01); + // wLangId + s.write(0x09); + s.write(0x04); + // bMsgIndex + s.write(0x00); + // bTeoPrologue + s.write(0x00); + s.write(0x00); + s.write(0x00); + // ulDataLength + s.write(apduSpec.getApdu().length); + s.write(0x00); + s.write(0x00); + s.write(0x00); + // abData + try { + s.write(apduSpec.getApdu()); + } catch (IOException e) { + // As we are dealing with ByteArrayOutputStreams no exception is to be + // expected. + throw new RuntimeException(e); + } + + return s.toByteArray(); + + } + + @Override + public ResponseAPDU verify(CardChannel channel, VerifyAPDUSpec apduSpec, + PINGUI pinGUI, PINSpec pinSpec, int retries) + throws SignatureCardException, CardException, InterruptedException { + + ResponseAPDU resp = null; + + byte[] s = createPINVerifyStructure(apduSpec, pinSpec); + Card icc = channel.getCard(); + + if (VERIFY) { + pinGUI.enterPIN(pinSpec, retries); + resp = new ResponseAPDU(verifyPin(icc, s, pinGUI)); + } else if (VERIFY_DIRECT) { + pinGUI.enterPINDirect(pinSpec, retries); + log.debug("VERIFY_PIN_DIRECT [" + FEATURES[FEATURE_VERIFY_PIN_DIRECT] + "]"); + resp = new ResponseAPDU(VERIFY_PIN_DIRECT(icc, s)); + } else { + log.warn("falling back to default pin-entry"); + return super.verify(channel, apduSpec, pinGUI, pinSpec, retries); + } + + switch (resp.getSW()) { + case 0x6400: + log.debug("SPE operation timed out."); + throw new TimeoutException(); + case 0x6401: + log.debug("SPE operation was cancelled by the 'Cancel' button."); + throw new CancelledException(); + case 0x6403: + log.debug("User entered too short or too long PIN " + + "regarding MIN/MAX PIN length."); + throw new PINFormatException(); + case 0x6480: + log.debug("SPE operation was aborted by the 'Cancel' operation " + + "at the host system."); + case 0x6b80: + log.info("Invalid parameter in passed structure."); + default: + return resp; + } + } + + @Override + public ResponseAPDU modify(CardChannel channel, ChangeReferenceDataAPDUSpec apduSpec, + ModifyPINGUI pinGUI, PINSpec pinSpec, int retries) + throws SignatureCardException, CardException, InterruptedException { + + ResponseAPDU resp = null; + + byte[] s = createPINModifyStructure(apduSpec, pinSpec); + Card icc = channel.getCard(); + + if (MODIFY) { + pinGUI.enterCurrentPIN(pinSpec, retries); + resp = new ResponseAPDU(modifyPin(icc, s, pinGUI, pinSpec)); + } else if (MODIFY_DIRECT) { + pinGUI.modifyPINDirect(pinSpec, retries); + log.debug("MODIFY_PIN_DIRECT [" + FEATURES[FEATURE_MODIFY_PIN_DIRECT] + "]"); + resp = new ResponseAPDU(MODIFY_PIN_DIRECT(icc, s)); + } else { + log.warn("falling back to default pin-entry"); + return super.modify(channel, apduSpec, pinGUI, pinSpec, retries); + } + + switch (resp.getSW()) { + case 0x6400: + log.debug("SPE operation timed out."); + throw new TimeoutException(); + case 0x6401: + log.debug("SPE operation was cancelled by the 'Cancel' button."); + throw new CancelledException(); + case 0x6402: + log.debug("Modify PIN operation failed because two 'new PIN' " + + "entries do not match"); + throw new PINConfirmationException(); + case 0x6403: + log.debug("User entered too short or too long PIN " + + "regarding MIN/MAX PIN length."); + throw new PINFormatException(); + case 0x6480: + log.debug("SPE operation was aborted by the 'Cancel' operation " + + "at the host system."); + case 0x6b80: + log.info("Invalid parameter in passed structure."); + default: + return resp; + } + } + + @Override + public ResponseAPDU modify(CardChannel channel, NewReferenceDataAPDUSpec apduSpec, + ModifyPINGUI pinGUI, PINSpec pinSpec) + throws SignatureCardException, CardException, InterruptedException { + + ResponseAPDU resp = null; + + byte[] s = createPINModifyStructure(apduSpec, pinSpec); + Card icc = channel.getCard(); + + if (MODIFY) { + pinGUI.enterNewPIN(pinSpec); + resp = new ResponseAPDU(modifyPin(icc, s, pinGUI, pinSpec)); + } else if (MODIFY_DIRECT) { + pinGUI.modifyPINDirect(pinSpec, -1); + log.debug("MODIFY_PIN_DIRECT [" + FEATURES[FEATURE_MODIFY_PIN_DIRECT] + "]"); + resp = new ResponseAPDU(MODIFY_PIN_DIRECT(icc, s)); + } else { + log.warn("falling back to default pin-entry"); + return super.modify(channel, apduSpec, pinGUI, pinSpec); + } + + switch (resp.getSW()) { + case 0x6400: + log.debug("SPE operation timed out."); + throw new TimeoutException(); + case 0x6401: + log.debug("SPE operation was cancelled by the 'Cancel' button."); + throw new CancelledException(); + case 0x6402: + log.debug("Modify PIN operation failed because two 'new PIN' " + + "entries do not match"); + throw new PINConfirmationException(); + case 0x6403: + log.debug("User entered too short or too long PIN " + + "regarding MIN/MAX PIN length."); + throw new PINFormatException(); + case 0x6480: + log.debug("SPE operation was aborted by the 'Cancel' operation " + + "at the host system."); + case 0x6b80: + log.info("Invalid parameter in passed structure."); + default: + return resp; + } + } + + @Override + public ResponseAPDU modify(CardChannel channel, ResetRetryCounterAPDUSpec apduSpec, + ModifyPINGUI pinGUI, PINSpec pinSpec, int retries) + throws InterruptedException, CardException, SignatureCardException { + //TODO + return modify(channel, (ChangeReferenceDataAPDUSpec) apduSpec, pinGUI, pinSpec, retries); + } +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/reader/ReaderFactory.java b/smcc/src/main/java/at/gv/egiz/smcc/reader/ReaderFactory.java new file mode 100644 index 00000000..eb197d9f --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/reader/ReaderFactory.java @@ -0,0 +1,128 @@ +/* + * Copyright 2008 Federal Chancellery Austria and + * Graz University of Technology + * + * 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 at.gv.egiz.smcc.reader; + +import at.gv.egiz.smcc.conf.SMCCConfiguration; +import at.gv.egiz.smcc.util.SMCCHelper; +import java.util.HashMap; +import java.util.Map; +import javax.smartcardio.Card; +import javax.smartcardio.CardException; +import javax.smartcardio.CardTerminal; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * + * @author Clemens Orthacker + */ +public class ReaderFactory { + + protected final static Log log = LogFactory.getLog(ReaderFactory.class); + + protected SMCCConfiguration configuration; + + private ReaderFactory() { + } + + /** + * @param configuration the configuration to set + */ + public void setConfiguration(SMCCConfiguration configuration) { + this.configuration = configuration; + } + + public static CardReader getReader(Card icc, CardTerminal ct) { + + String name = ct.getName(); + log.info("creating reader " + name); + + Map features = queryFeatures(icc); + boolean disablePinpad = false; + CardReader reader; + + //TODO query application context for reader config +// if (configuration != null) { +// String disablePinpad = configuration.getProperty(SMCCConfiguration.DISABLE_PINPAD_P); +// log.debug("setting disablePinpad to " + Boolean.parseBoolean(disablePinpad)); +// reader.setDisablePinpad(Boolean.parseBoolean(disablePinpad)); +// } + log.warn("card reader configuration is not considered"); + + if (features.isEmpty() || disablePinpad) { + reader = new DefaultCardReader(ct); + } else { + reader = new PinpadCardReader(ct, features); + } + + return reader; + } + + private static int CTL_CODE(int code) { + String os_name = System.getProperty("os.name").toLowerCase(); + if (os_name.indexOf("windows") > -1) { + // cf. WinIOCTL.h + return (0x31 << 16 | (code) << 2); + } + // cf. reader.h + return 0x42000000 + (code); + } + + static int IOCTL_GET_FEATURE_REQUEST = CTL_CODE(3400); + + private static Map queryFeatures(Card icc) { + Map features = new HashMap(); + + if (icc == null) { + log.warn("invalid card handle, cannot query ifd features"); + } else { + try { + if (log.isTraceEnabled()) { + log.trace("GET_FEATURE_REQUEST " + Integer.toHexString(IOCTL_GET_FEATURE_REQUEST)); + } + byte[] resp = icc.transmitControlCommand(IOCTL_GET_FEATURE_REQUEST, + new byte[0]); + + if (log.isTraceEnabled()) { + log.trace("Response TLV " + SMCCHelper.toString(resp)); + } + // tag + // length in bytes (always 4) + // control code value for supported feature (in big endian) + for (int i = 0; i < resp.length; i += 6) { + Byte feature = new Byte(resp[i]); + Integer ioctl = new Integer((0xff & resp[i + 2]) << 24) | + ((0xff & resp[i + 3]) << 16) | + ((0xff & resp[i + 4]) << 8) | + (0xff & resp[i + 5]); + if (log.isInfoEnabled()) { + log.info("IFD supports " + CardReader.FEATURES[feature.intValue()] + + ": " + Integer.toHexString(ioctl.intValue())); + } + features.put(feature, ioctl); + } + } catch (CardException ex) { + log.debug("Failed to query IFD features: " + ex.getMessage()); + log.trace(ex); + log.info("IFD does not support secure pin entry"); + } + } + return features; + } + +} -- cgit v1.2.3