diff options
author | clemenso <clemenso@8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4> | 2009-04-02 19:13:48 +0000 |
---|---|---|
committer | clemenso <clemenso@8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4> | 2009-04-02 19:13:48 +0000 |
commit | 2dbf2347bc78fd835c857ad438514fb6251f6f7a (patch) | |
tree | ec8eb7876dfdca86eeeec1d08ebbeb31eb464892 /smcc/src/main/java/at/gv/egiz/smcc/ccid | |
parent | 1ad095128a98137e2b4c904814722be4ec43eebd (diff) | |
download | mocca-2dbf2347bc78fd835c857ad438514fb6251f6f7a.tar.gz mocca-2dbf2347bc78fd835c857ad438514fb6251f6f7a.tar.bz2 mocca-2dbf2347bc78fd835c857ad438514fb6251f6f7a.zip |
1.1-RC7 (pinpad revisited)
git-svn-id: https://joinup.ec.europa.eu/svn/mocca/trunk@325 8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4
Diffstat (limited to 'smcc/src/main/java/at/gv/egiz/smcc/ccid')
4 files changed, 384 insertions, 47 deletions
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,17 +45,48 @@ 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 * @param ctrlCommand @@ -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<Byte, Integer> 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 <clemens.orthacker@iaik.tugraz.at> + */ +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); } |