From 2dbf2347bc78fd835c857ad438514fb6251f6f7a Mon Sep 17 00:00:00 2001 From: clemenso Date: Thu, 2 Apr 2009 19:13:48 +0000 Subject: 1.1-RC7 (pinpad revisited) git-svn-id: https://joinup.ec.europa.eu/svn/mocca/trunk@325 8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4 --- smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java | 34 +- .../at/gv/egiz/smcc/AbstractSignatureCard.java | 16 +- .../at/gv/egiz/smcc/PINConfirmationException.java | 43 +++ .../java/at/gv/egiz/smcc/PINFormatException.java | 43 +++ .../gv/egiz/smcc/PINOperationAbortedException.java | 43 +++ .../src/main/java/at/gv/egiz/smcc/STARCOSCard.java | 59 ++-- smcc/src/main/java/at/gv/egiz/smcc/SWCard.java | 32 +- .../main/java/at/gv/egiz/smcc/SignatureCard.java | 2 +- smcc/src/main/java/at/gv/egiz/smcc/ccid/CCID.java | 45 ++- .../java/at/gv/egiz/smcc/ccid/DefaultReader.java | 345 ++++++++++++++++++--- .../at/gv/egiz/smcc/ccid/OMNIKEYCardMan3621.java | 39 +++ .../java/at/gv/egiz/smcc/ccid/ReaderFactory.java | 2 + 12 files changed, 605 insertions(+), 98 deletions(-) create mode 100644 smcc/src/main/java/at/gv/egiz/smcc/PINConfirmationException.java create mode 100644 smcc/src/main/java/at/gv/egiz/smcc/PINFormatException.java create mode 100644 smcc/src/main/java/at/gv/egiz/smcc/PINOperationAbortedException.java create mode 100644 smcc/src/main/java/at/gv/egiz/smcc/ccid/OMNIKEYCardMan3621.java (limited to 'smcc/src') 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 06e4a018..d064b821 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java @@ -401,15 +401,16 @@ public class ACOSCard extends AbstractSignatureCard { @Override protected int verifyPIN(byte kid, char[] pin) - throws LockedException, NotActivatedException, CancelledException, TimeoutException, SignatureCardException { + throws LockedException, NotActivatedException, CancelledException, TimeoutException, PINFormatException, PINOperationAbortedException, SignatureCardException { try { byte[] sw; if (reader.hasFeature(CCID.FEATURE_VERIFY_PIN_DIRECT)) { - log.debug("verify PIN on IFD"); - sw = reader.transmitControlCommand( - CCID.FEATURE_VERIFY_PIN_DIRECT, - getPINVerifyStructure(kid)); + log.debug("verify pin on cardreader"); + sw = reader.verifyPinDirect(getPINVerifyStructure(kid)); // int sw = (resp[resp.length-2] & 0xff) << 8 | resp[resp.length-1] & 0xff; + } else if (reader.hasFeature(CCID.FEATURE_VERIFY_PIN_START)) { + log.debug("verify pin on cardreader"); + sw = reader.verifyPin(getPINVerifyStructure(kid)); } else { byte[] pinBlock = encodePINBlock(pin); CardChannel channel = getCardChannel(); @@ -442,6 +443,8 @@ public class ACOSCard extends AbstractSignatureCard { throw new TimeoutException("[64:00]"); } else if (sw[0] == (byte) 0x64 && sw[1] == (byte) 0x01) { throw new CancelledException("[64:01]"); + } else if (sw[0] == (byte) 0x64 && sw[1] == (byte) 0x03) { + throw new PINFormatException("[64:03]"); } log.error("Failed to verify pin: SW=" + SMCCHelper.toString(sw)); @@ -465,15 +468,15 @@ public class ACOSCard extends AbstractSignatureCard { */ @Override protected int changePIN(byte kid, char[] oldPin, char[] newPin) - throws LockedException, NotActivatedException, CancelledException, TimeoutException, SignatureCardException { + throws LockedException, NotActivatedException, CancelledException, PINFormatException, PINConfirmationException, TimeoutException, PINOperationAbortedException, SignatureCardException { try { byte[] sw; if (reader.hasFeature(CCID.FEATURE_MODIFY_PIN_DIRECT)) { - log.debug("modify PIN on IFD"); - sw = reader.transmitControlCommand( - CCID.FEATURE_MODIFY_PIN_DIRECT, - getPINModifyStructure(kid)); -// int sw = (resp[resp.length-2] & 0xff) << 8 | resp[resp.length-1] & 0xff; + log.debug("modify pin on cardreader"); + sw = reader.modifyPinDirect(getPINModifyStructure(kid)); + } else if (reader.hasFeature(CCID.FEATURE_MODIFY_PIN_START)) { + log.debug("modify pin on cardreader"); + sw = reader.modifyPin(getPINModifyStructure(kid)); } else { byte[] cmd = new byte[16]; System.arraycopy(encodePINBlock(oldPin), 0, cmd, 0, 8); @@ -504,6 +507,13 @@ public class ACOSCard extends AbstractSignatureCard { throw new TimeoutException("[64:00]"); } else if (sw[0] == (byte) 0x64 && sw[1] == (byte) 0x01) { throw new CancelledException("[64:01]"); + } else if (sw[0] == (byte) 0x64 && sw[1] == (byte) 0x02) { + throw new PINConfirmationException("[64:02]"); + } else if (sw[0] == (byte) 0x64 && sw[1] == (byte) 0x03) { + throw new PINFormatException("[64:03]"); + } else if (sw[0] == (byte) 0x6a && sw[1] == (byte) 0x80) { + log.info("invalid parameter, assume wrong pin size"); + throw new PINFormatException("[6a:80]"); } log.error("Failed to change pin: SW=" + SMCCHelper.toString(sw)); @@ -559,7 +569,7 @@ public class ACOSCard extends AbstractSignatureCard { byte bmPINLengthFormat = (byte) 0x00; // 000 0 0000 // ^-------- System bit units is bit // ^^^^--- no PIN length - byte wPINMaxExtraDigitL = + byte wPINMaxExtraDigitL = //TODO compare ints, not bytes (reader.getwPINMaxExtraDigitL() < (byte) 0x08) ? reader.getwPINMaxExtraDigitL() : (byte) 0x08; byte wPINMaxExtraDigitH = 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 47c27369..7dd3ee78 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/AbstractSignatureCard.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/AbstractSignatureCard.java @@ -29,21 +29,16 @@ package at.gv.egiz.smcc; import at.gv.egiz.smcc.ccid.CCID; -import at.gv.egiz.smcc.ccid.DefaultReader; import at.gv.egiz.smcc.ccid.ReaderFactory; import at.gv.egiz.smcc.util.SMCCHelper; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Locale; -import java.util.Map; import java.util.ResourceBundle; -import java.util.logging.Level; -import java.util.logging.Logger; import javax.smartcardio.ATR; import javax.smartcardio.Card; import javax.smartcardio.CardChannel; @@ -152,8 +147,8 @@ public abstract class AbstractSignatureCard implements SignatureCard { * @throws at.gv.egiz.smcc.SignatureCardException */ protected abstract int verifyPIN(byte kid, char[] pin) - throws LockedException, NotActivatedException, CancelledException, TimeoutException, SignatureCardException; - + throws LockedException, NotActivatedException, CancelledException, PINFormatException, TimeoutException, PINOperationAbortedException, SignatureCardException; + /** * CHANGE(?) APDU * If IFD supports VERIFY_PIN on pinpad, parameter pin may be empty. @@ -162,7 +157,7 @@ public abstract class AbstractSignatureCard implements SignatureCard { * @throws at.gv.egiz.smcc.SignatureCardException if activation fails */ protected abstract void activatePIN(byte kid, char[] pin) - throws CancelledException, TimeoutException, SignatureCardException; + throws CancelledException, PINFormatException, PINConfirmationException, TimeoutException, PINOperationAbortedException, SignatureCardException; /** * CHANGE(?) APDU @@ -173,7 +168,7 @@ public abstract class AbstractSignatureCard implements SignatureCard { * @throws at.gv.egiz.smcc.SignatureCardException if change fails */ protected abstract int changePIN(byte kid, char[] oldPin, char[] newPin) - throws CancelledException, TimeoutException, SignatureCardException; + throws LockedException, NotActivatedException, CancelledException, PINFormatException, PINConfirmationException, TimeoutException, PINOperationAbortedException, SignatureCardException; /** * encode the pin as needed in VERIFY/CHANGE APDUs @@ -595,7 +590,8 @@ public abstract class AbstractSignatureCard implements SignatureCard { */ @Override public void changePIN(PINSpec pinSpec, ChangePINProvider pinProvider) - throws LockedException, NotActivatedException, CancelledException, TimeoutException, SignatureCardException, InterruptedException { + throws LockedException, NotActivatedException, CancelledException, + TimeoutException, SignatureCardException, InterruptedException { try { getCard().beginExclusive(); diff --git a/smcc/src/main/java/at/gv/egiz/smcc/PINConfirmationException.java b/smcc/src/main/java/at/gv/egiz/smcc/PINConfirmationException.java new file mode 100644 index 00000000..115e6d5f --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/PINConfirmationException.java @@ -0,0 +1,43 @@ +/* +* 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; + +/** + * TODO check whether card readers distinguish specific reason (pin too short?) + * and add getters/setters + * + * @author Clemens Orthacker + */ +public class PINConfirmationException extends SignatureCardException { + + 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 new file mode 100644 index 00000000..28a13fdb --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/PINFormatException.java @@ -0,0 +1,43 @@ +/* +* 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; + +/** + * TODO check whether card readers distinguish specific reason (pin too short?) + * and add getters/setters + * + * @author Clemens Orthacker + */ +public class PINFormatException extends SignatureCardException { + + 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/PINOperationAbortedException.java b/smcc/src/main/java/at/gv/egiz/smcc/PINOperationAbortedException.java new file mode 100644 index 00000000..7337f59e --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/PINOperationAbortedException.java @@ -0,0 +1,43 @@ +/* +* 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; + +/** + * TODO check whether card readers distinguish specific reason (pin too short?) + * and add getters/setters + * + * @author Clemens Orthacker + */ +public class PINOperationAbortedException extends SignatureCardException { + + public PINOperationAbortedException() { + super(); + } + + public PINOperationAbortedException(String message, Throwable cause) { + super(message, cause); + } + + public PINOperationAbortedException(String message) { + super(message); + } + + public PINOperationAbortedException(Throwable cause) { + super(cause); + } + +} 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 bc6a2316..b9c68b06 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java @@ -28,7 +28,7 @@ // package at.gv.egiz.smcc; -import at.gv.egiz.smcc.ccid.DefaultReader; +import at.gv.egiz.smcc.ccid.CCID; import at.gv.egiz.smcc.util.SMCCHelper; import java.util.Arrays; import javax.smartcardio.CardChannel; @@ -319,6 +319,8 @@ public class STARCOSCard extends AbstractSignatureCard { return createSignature(hash, AID_DF_SS, pin, KID_PIN_SS, DST_SS); } catch (VerificationFailedException e) { retries = e.getRetries(); + } catch (PINFormatException e) { + log.debug("wrong pin size entered, retry"); } finally { getCard().endExclusive(); } @@ -454,15 +456,16 @@ public class STARCOSCard extends AbstractSignatureCard { @Override protected int verifyPIN(byte kid, char[] pin) - throws LockedException, NotActivatedException, SignatureCardException { + throws LockedException, NotActivatedException, TimeoutException, CancelledException, PINFormatException, PINOperationAbortedException, SignatureCardException { try { byte[] sw; - if (reader.hasFeature(DefaultReader.FEATURE_VERIFY_PIN_DIRECT)) { - log.debug("verify PIN on IFD"); - sw = reader.transmitControlCommand( - DefaultReader.FEATURE_VERIFY_PIN_DIRECT, - getPINVerifyStructure(kid)); + if (reader.hasFeature(CCID.FEATURE_VERIFY_PIN_DIRECT)) { + log.debug("verify pin on cardreader"); + sw = reader.verifyPinDirect(getPINVerifyStructure(kid)); // int sw = (resp[resp.length-2] & 0xff) << 8 | resp[resp.length-1] & 0xff; + } else if (reader.hasFeature(CCID.FEATURE_VERIFY_PIN_START)) { + log.debug("verify pin on cardreader"); + sw = reader.verifyPin(getPINVerifyStructure(kid)); } else { byte[] pinBlock = encodePINBlock(pin); CardChannel channel = getCardChannel(); @@ -492,6 +495,8 @@ public class STARCOSCard extends AbstractSignatureCard { throw new TimeoutException("[64:00]"); } else if (sw[0] == (byte) 0x64 && sw[1] == (byte) 0x01) { throw new CancelledException("[64:01]"); + } else if (sw[0] == (byte) 0x64 && sw[1] == (byte) 0x03) { + throw new PINFormatException("[64:03]"); } log.error("Failed to verify pin: SW=" + SMCCHelper.toString(sw)); @@ -539,15 +544,15 @@ public class STARCOSCard extends AbstractSignatureCard { @Override protected int changePIN(byte kid, char[] oldPin, char[] newPin) - throws LockedException, NotActivatedException, CancelledException, TimeoutException, SignatureCardException { + throws LockedException, CancelledException, PINFormatException, PINConfirmationException, TimeoutException, PINOperationAbortedException, SignatureCardException { try { byte[] sw; - if (reader.hasFeature(DefaultReader.FEATURE_MODIFY_PIN_DIRECT)) { - log.debug("modify PIN on IFD"); - sw = reader.transmitControlCommand( - DefaultReader.FEATURE_MODIFY_PIN_DIRECT, - getPINModifyStructure(kid)); -// int sw = (resp[resp.length-2] & 0xff) << 8 | resp[resp.length-1] & 0xff; + if (reader.hasFeature(CCID.FEATURE_MODIFY_PIN_DIRECT)) { + log.debug("modify pin on cardreader"); + sw = reader.modifyPinDirect(getPINModifyStructure(kid)); + } else if (reader.hasFeature(CCID.FEATURE_MODIFY_PIN_START)) { + log.debug("modify pin on cardreader"); + sw = reader.modifyPin(getPINModifyStructure(kid)); } else { byte[] cmd = new byte[16]; System.arraycopy(encodePINBlock(oldPin), 0, cmd, 0, 8); @@ -583,6 +588,13 @@ public class STARCOSCard extends AbstractSignatureCard { throw new TimeoutException("[64:00]"); } else if (sw[0] == (byte) 0x64 && sw[1] == (byte) 0x01) { throw new CancelledException("[64:01]"); + } else if (sw[0] == (byte) 0x64 && sw[1] == (byte) 0x02) { + throw new PINConfirmationException("[64:02]"); + } else if (sw[0] == (byte) 0x64 && sw[1] == (byte) 0x03) { + throw new PINFormatException("[64:03]"); + } else if (sw[0] == (byte) 0x6a && sw[1] == (byte) 0x80) { + log.info("invalid parameter, assume wrong pin size"); + throw new PINFormatException("[6a:80]"); } log.error("Failed to change pin: SW=" + SMCCHelper.toString(sw)); @@ -595,15 +607,15 @@ public class STARCOSCard extends AbstractSignatureCard { @Override protected void activatePIN(byte kid, char[] pin) - throws CancelledException, TimeoutException, SignatureCardException { + throws CancelledException, PINFormatException, PINConfirmationException, TimeoutException, PINOperationAbortedException, SignatureCardException { try { byte[] sw; - if (reader.hasFeature(DefaultReader.FEATURE_MODIFY_PIN_DIRECT)) { - log.debug("activate PIN on IFD"); - sw = reader.transmitControlCommand( - DefaultReader.FEATURE_MODIFY_PIN_DIRECT, - getActivatePINModifyStructure(kid)); -// int sw = (resp[resp.length-2] & 0xff) << 8 | resp[resp.length-1] & 0xff; + if (reader.hasFeature(CCID.FEATURE_MODIFY_PIN_DIRECT)) { + log.debug("activate pin on cardreader"); + sw = reader.modifyPinDirect(getActivatePINModifyStructure(kid)); + } else if (reader.hasFeature(CCID.FEATURE_MODIFY_PIN_START)) { + log.debug("activate pin on cardreader"); + sw = reader.modifyPin(getActivatePINModifyStructure(kid)); } else { CardChannel channel = getCardChannel(); ResponseAPDU resp = transmit(channel, @@ -630,6 +642,10 @@ public class STARCOSCard extends AbstractSignatureCard { throw new TimeoutException("[64:00]"); } else if (sw[0] == (byte) 0x64 && sw[1] == (byte) 0x01) { throw new CancelledException("[64:01]"); + } else if (sw[0] == (byte) 0x64 && sw[1] == (byte) 0x02) { + throw new PINConfirmationException("[64:02]"); + } else if (sw[0] == (byte) 0x64 && sw[1] == (byte) 0x03) { + throw new PINFormatException("[64:03]"); } log.error("Failed to activate pin: SW=" + SMCCHelper.toString(sw)); @@ -683,6 +699,7 @@ public class STARCOSCard extends AbstractSignatureCard { byte bmPINLengthFormat = (byte) 0x04; // 000 0 0100 // ^-------- System bit units is bit // ^^^^--- PIN length is at the 4th position bit + //TODO compare ints, not bytes byte wPINMaxExtraDigitL = // Max=12 digits (Gemplus support max 8) (reader.getwPINMaxExtraDigitL() < (byte) 0x12) ? reader.getwPINMaxExtraDigitL() : (byte) 0x12; 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 253ac7a0..2ed1fe64 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/SWCard.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/SWCard.java @@ -429,13 +429,6 @@ public class SWCard implements SignatureCard { return false; } - @Override - public byte[] transmitControlCommand(Byte feature, byte[] ctrlCommand) - throws SignatureCardException { - throw new SignatureCardException(CCID.FEATURES[feature.intValue()] + - " not supported"); - } - @Override public byte getbTimeOut() { return 0; @@ -465,6 +458,31 @@ public class SWCard implements SignatureCard { 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."); + } }; } } 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 ad530ad5..c06074f2 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/SignatureCard.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCard.java @@ -128,7 +128,7 @@ public interface SignatureCard { throws LockedException, NotActivatedException, CancelledException, SignatureCardException, InterruptedException; public void changePIN(PINSpec pinSpec, ChangePINProvider pinProvider) - throws LockedException, NotActivatedException, CancelledException, SignatureCardException, InterruptedException; + throws LockedException, NotActivatedException, CancelledException, PINFormatException, SignatureCardException, InterruptedException; public void activatePIN(PINSpec pinSpec, PINProvider pinProvider) throws CancelledException, SignatureCardException, InterruptedException; diff --git a/smcc/src/main/java/at/gv/egiz/smcc/ccid/CCID.java b/smcc/src/main/java/at/gv/egiz/smcc/ccid/CCID.java index 2c56ce98..56ebaffe 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/ccid/CCID.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/ccid/CCID.java @@ -45,16 +45,47 @@ public interface CCID { "FEATURE_WRITE_DISPLAY", "FEATURE_GET_KEY", "FEATURE_IFD_DISPLAY_PROPERTIES"}; - - Byte FEATURE_IFD_PIN_PROPERTIES = new Byte((byte) 10); - Byte FEATURE_MCT_READER_DIRECT = new Byte((byte) 8); - Byte FEATURE_MODIFY_PIN_DIRECT = new Byte((byte) 7); - Byte FEATURE_VERIFY_PIN_DIRECT = new Byte((byte) 6); - + + 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 + + String getName(); + Card connect() throws CardException; boolean hasFeature(Byte feature); + /** + * not supported by OMNIKEY CardMan 3621 with ACOS card + * @param PIN_VERIFY + * @return + * @throws at.gv.egiz.smcc.PINOperationAbortedException + * @throws javax.smartcardio.CardException + */ + byte[] verifyPin(byte[] PIN_VERIFY) throws PINOperationAbortedException, CardException; + + byte[] verifyPinDirect(byte[] PIN_VERIFY) throws CardException; + + /** + * not supported by OMNIKEY CardMan 3621 with ACOS card + * @param PIN_MODIFY + * @return + * @throws at.gv.egiz.smcc.PINOperationAbortedException + * @throws javax.smartcardio.CardException + */ + byte[] modifyPin(byte[] PIN_MODIFY) throws PINOperationAbortedException, CardException; + + byte[] modifyPinDirect(byte[] PIN_MODIFY) throws CardException; + /** * * @param feature the corresponding control code will be transmitted @@ -63,7 +94,7 @@ public interface CCID { * @throws at.gv.egiz.smcc.SignatureCardException if feature is not supported * or card communication fails */ - byte[] transmitControlCommand(Byte feature, byte[] ctrlCommand) throws SignatureCardException; +// byte[] transmitControlCommand(Byte feature, byte[] ctrlCommand) throws SignatureCardException; /** * allow subclasses to override default (deal with reader bugs) diff --git a/smcc/src/main/java/at/gv/egiz/smcc/ccid/DefaultReader.java b/smcc/src/main/java/at/gv/egiz/smcc/ccid/DefaultReader.java index 2cc77dc9..9b1787a0 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/ccid/DefaultReader.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/ccid/DefaultReader.java @@ -20,6 +20,8 @@ import at.gv.egiz.smcc.*; import at.gv.egiz.smcc.util.SMCCHelper; import java.util.HashMap; import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; import javax.smartcardio.Card; import javax.smartcardio.CardException; import javax.smartcardio.CardTerminal; @@ -35,12 +37,17 @@ public class DefaultReader implements CCID { protected final static Log log = LogFactory.getLog(DefaultReader.class); private static int CTL_CODE(int code) { - return 0x42000000 + 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); } int IOCTL_GET_FEATURE_REQUEST = CTL_CODE(3400); - protected Card icc; protected CardTerminal ct; @@ -58,14 +65,19 @@ public class DefaultReader implements CCID { features = queryFeatures(); } - public Card connect() throws CardException { //SignatureCardException { -// try { + /** + * + * @return the card terminals name + */ + @Override + public String getName() { + return ct.getName(); + } + + @Override + public Card connect() throws CardException { icc = ct.connect("*"); return icc; -// } catch (CardException ex) { -// log.error(ex.getMessage(), ex); -// throw new SignatureCardException("Failed to connect to card: " + ex.getMessage()); -// } } Map queryFeatures() { @@ -81,7 +93,7 @@ public class DefaultReader implements CCID { " on " + ct.getName()); } byte[] resp = icc.transmitControlCommand(IOCTL_GET_FEATURE_REQUEST, - new byte[]{}); + new byte[0]); if (log.isTraceEnabled()) { log.trace("Response TLV " + SMCCHelper.toString(resp)); @@ -91,8 +103,10 @@ public class DefaultReader implements CCID { // control code value for supported feature (in big endian) for (int i = 0; i < resp.length; i += 6) { Byte feature = new Byte(resp[i]); - int ioctlBigEndian = (resp[i + 2] << 24) | - (resp[i + 3] << 16) | (resp[i + 4] << 8) | resp[i + 5]; + int ioctlBigEndian = ((0xff & resp[i + 2]) << 24) | + ((0xff & resp[i + 3]) << 16) | + ((0xff & resp[i + 4]) << 8) | + (0xff & resp[i + 5]); Integer ioctl = new Integer(ioctlBigEndian); if (log.isInfoEnabled()) { log.info("CCID supports " + FEATURES[feature.intValue()] + @@ -117,37 +131,29 @@ public class DefaultReader implements CCID { return false; } -// public Integer getIOCTL(Byte feature) { -// if (features != null) { -// return features.get(feature); +// protected byte[] transmitControlCommand(Byte feature, byte[] ctrlCommand) +// throws CardException { +// try { +// if (!features.containsKey(feature)) { +// throw new CardException(FEATURES[feature.intValue()] + " not supported"); +// } +// int ioctl = features.get(feature); +// if (log.isTraceEnabled()) { +// log.trace("CtrlCommand (" + Integer.toHexString(ioctl) + +// ") " + SMCCHelper.toString(ctrlCommand)); +// } +// byte[] resp = icc.transmitControlCommand(ioctl, ctrlCommand); +// if (log.isTraceEnabled()) { +// log.trace("CtrlCommand Response " + SMCCHelper.toString(resp)); +// } +// return resp; +// } catch (CardException ex) { +// log.error(ex.getMessage()); +// throw new SignatureCardException("Failed to transmit CtrlCommand for " + +// FEATURES[feature.intValue()]); // } -// return null; // } - @Override - public byte[] transmitControlCommand(Byte feature, byte[] ctrlCommand) - throws SignatureCardException { - try { - if (!features.containsKey(feature)) { - throw new SignatureCardException(FEATURES[feature.intValue()] + " not supported"); - } - int ioctl = features.get(feature); - if (log.isTraceEnabled()) { - log.trace("CtrlCommand (" + Integer.toHexString(ioctl) + - ") " + SMCCHelper.toString(ctrlCommand)); - } - byte[] resp = icc.transmitControlCommand(ioctl, ctrlCommand); - if (log.isTraceEnabled()) { - log.trace("CtrlCommand Response " + SMCCHelper.toString(resp)); - } - return resp; - } catch (CardException ex) { - log.error(ex.getMessage()); - throw new SignatureCardException("Failed to transmit CtrlCommand for " + - FEATURES[feature.intValue()]); - } - } - @Override public byte getbTimeOut() { @@ -162,7 +168,7 @@ public class DefaultReader implements CCID { @Override public byte getwPINMaxExtraDigitL() { - return (byte) 0x12; + return (byte) 0x12; // signed int } @Override @@ -175,6 +181,265 @@ public class DefaultReader implements CCID { return (byte) 0x02; // validation key pressed } + void verifyPinStart(byte[] PIN_VERIFY) throws CardException { + if (!features.containsKey(FEATURE_VERIFY_PIN_START)) { + throw new CardException("FEATURE_VERIFY_PIN_START not supported"); + } + 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)); + } + } + } + + byte[] verifyPinFinish() throws CardException { + if (!features.containsKey(FEATURE_VERIFY_PIN_FINISH)) { + throw new CardException("FEATURE_VERIFY_FINISH_FINISH not supported"); + } + 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)); + } + + void modifyPinStart(byte[] PIN_MODIFY) throws CardException { + if (!features.containsKey(FEATURE_MODIFY_PIN_START)) { + throw new CardException("FEATURE_MODIFY_PIN_START not supported"); + } + 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)); + } + } + } + + byte[] modifyPinFinish() throws CardException { + if (!features.containsKey(FEATURE_MODIFY_PIN_FINISH)) { + throw new CardException("FEATURE_MODIFY_FINISH_FINISH not supported"); + } + 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)); + } + + + byte getKeyPressed() throws CardException { + if (!features.containsKey(FEATURE_GET_KEY_PRESSED)) { + throw new CardException("FEATURE_GET_KEY_PRESSED not supported"); + } + int ioctl = features.get(FEATURE_GET_KEY_PRESSED); +// if (log.isTraceEnabled()) { +// log.trace("GET_KEY_PRESSED (" + Integer.toHexString(ioctl) + ")"); +// } + 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)); + } + + + + @Override + public byte[] verifyPinDirect(byte[] PIN_VERIFY) throws CardException { + if (!features.containsKey(FEATURE_VERIFY_PIN_DIRECT)) { + throw new CardException("FEATURE_VERIFY_PIN_DIRECT not supported"); + } + 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; + } + + @Override + public byte[] verifyPin(byte[] PIN_VERIFY) throws PINOperationAbortedException, CardException { + verifyPinStart(PIN_VERIFY); + byte resp; + do { + resp = getKeyPressed(); + if (resp == (byte) 0x00) { + synchronized(this) { + try { + wait(200); + } catch (InterruptedException ex) { + log.error("interrupted in VERIFY_PIN"); + } + } + } else if (resp == (byte) 0x0d) { + log.trace("user confirmed"); + break; + } else if (resp == (byte) 0x2b) { + log.trace("user entered valid key (0-9)"); + } else if (resp == (byte) 0x1b) { + log.info("user cancelled VERIFY_PIN via cancel button"); + return verifyPinFinish(); +// return new byte[] { (byte) 0x64, (byte) 0x01 }; + } else if (resp == (byte) 0x08) { + log.trace("user pressed correction/backspace button"); + } else if (resp == (byte) 0x0e) { + log.trace("timeout occured"); + return verifyPinFinish(); // return 0x64 0x00 + } else if (resp == (byte) 0x40) { + log.trace("PIN_Operation_Aborted"); + throw new PINOperationAbortedException("PIN_Operation_Aborted"); + } else if (resp == (byte) 0x0a) { + log.trace("all keys cleared"); + } 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); //resp != (byte) 0x0d); + + return verifyPinFinish(); + } + + @Override + public byte[] modifyPin(byte[] PIN_MODIFY) throws PINOperationAbortedException, CardException { + modifyPinStart(PIN_MODIFY); + log.debug(PIN_MODIFY[9] + " pin confirmations expected"); + + byte resp; + short pinConfirmations = 0; + do { + resp = getKeyPressed(); + if (resp == (byte) 0x00) { + synchronized(this) { + try { + wait(200); + } catch (InterruptedException ex) { + log.error("interrupted in MODIFY_PIN"); + } + } + } else if (resp == (byte) 0x0d) { + log.trace("user confirmed"); + pinConfirmations++; + continue; + } else if (resp == (byte) 0x2b) { + log.trace("user entered valid key (0-9)"); + } else if (resp == (byte) 0x1b) { + log.info("user cancelled MODIFY_PIN via cancel button"); +// return verifyPinFinish(); + return new byte[] { (byte) 0x64, (byte) 0x01 }; + } else if (resp == (byte) 0x08) { + log.trace("user pressed correction/backspace button"); + } else if (resp == (byte) 0x0e) { + log.trace("timeout occured"); + return new byte[] { (byte) 0x64, (byte) 0x00 }; +// return verifyPinFinish(); // return 0x64 0x00 + } else if (resp == (byte) 0x40) { + log.trace("PIN_Operation_Aborted"); + throw new PINOperationAbortedException("PIN_Operation_Aborted"); + } else if (resp == (byte) 0x0a) { + log.trace("all keys cleared"); + } 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 (pinConfirmations < PIN_MODIFY[9]); + + return modifyPinFinish(); + } + + /** + * NOT SUPPORTED FOR ACOS ON OMNIKEY CardMan 3621 + * + * @param PIN_MODIFY + * @return + * @throws javax.smartcardio.CardException + */ + @Override + public byte[] modifyPinDirect(byte[] PIN_MODIFY) throws CardException { + if (!features.containsKey(FEATURE_MODIFY_PIN_DIRECT)) { + throw new CardException("FEATURE_MODIFY_PIN_DIRECT not supported"); + } + 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; + } + + +//[TRACE] SmartCardIO - terminal 'PC/SC terminal OMNIKEY CardMan 3621 0' card inserted : PC/SC card in OMNIKEY CardMan 3621 0, protocol T=1, state OK +//[TRACE] DefaultReader - GET_FEATURE_REQUEST 313520 on OMNIKEY CardMan 3621 0 +//[TRACE] DefaultReader - Response TLV [01:04:00:31:30:00:02:04:00:31:2f:d4:03:04:00:31:30:04:04:04:00:31:2f:dc:05:04:00:31:2f:e0:0a:04 +//00:31:30:08] +//[INFO] DefaultReader - CCID supports FEATURE_VERIFY_PIN_START: 313000 +//[INFO] DefaultReader - CCID supports FEATURE_VERIFY_PIN_FINISH: ffffffd4 +//[INFO] DefaultReader - CCID supports FEATURE_MODIFY_PIN_START: 313004 +//[INFO] DefaultReader - CCID supports FEATURE_MODIFY_PIN_FINISH: ffffffdc +//[INFO] DefaultReader - CCID supports FEATURE_GET_KEY_PRESSED: ffffffe0 +//[INFO] DefaultReader - CCID supports FEATURE_IFD_PIN_PROPERTIES: 313008 +//[TRACE] AbstractSignatureCard - Setting IFS (information field size) to 254 + // protected byte ifdGetKeyPressed() throws CardException { // if (ifdSupportsFeature(FEATURE_VERIFY_PIN_DIRECT)) { diff --git a/smcc/src/main/java/at/gv/egiz/smcc/ccid/OMNIKEYCardMan3621.java b/smcc/src/main/java/at/gv/egiz/smcc/ccid/OMNIKEYCardMan3621.java new file mode 100644 index 00000000..35dd4f99 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/ccid/OMNIKEYCardMan3621.java @@ -0,0 +1,39 @@ +/* + * 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.ccid; + +import javax.smartcardio.Card; +import javax.smartcardio.CardTerminal; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Fails with ACOS cards (Problem might be 'short' VERIFY which is not supported by ACOS) + * TODO + * + * @author Clemens Orthacker + */ +public class OMNIKEYCardMan3621 extends DefaultReader { + + protected static final Log log = LogFactory.getLog(OMNIKEYCardMan3621.class); + + public OMNIKEYCardMan3621(Card icc, CardTerminal ct) { + super(icc, ct); + log.warn("This card reader does not support ACOS cards."); + log.debug("TODO: fall back to software pin entry"); + } +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/ccid/ReaderFactory.java b/smcc/src/main/java/at/gv/egiz/smcc/ccid/ReaderFactory.java index 2cfcef19..07c16c3e 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/ccid/ReaderFactory.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/ccid/ReaderFactory.java @@ -29,6 +29,8 @@ public class ReaderFactory { public static CCID getReader(Card icc, CardTerminal ct) { if ("Gemplus GemPC Pinpad 00 00".equals(ct.getName())) { return new GemplusGemPCPinpad(icc, ct); + } else if ("OmniKey CardMan 3621 00 00".equals(ct.getName())) { + return new OMNIKEYCardMan3621(icc, ct); } return new DefaultReader(icc, ct); } -- cgit v1.2.3