/**************************************************************************** * Copyright (C) 2012 HS Coburg. * All rights reserved. * Contact: ecsec GmbH (info@ecsec.de) * * This file is part of the Open eCard App. * * GNU General Public License Usage * This file may be used under the terms of the GNU General Public * License version 3.0 as published by the Free Software Foundation * and appearing in the file LICENSE.GPL included in the packaging of * this file. Please review the following information to ensure the * GNU General Public License version 3.0 requirements will be met: * http://www.gnu.org/copyleft/gpl.html. * * Other Usage * Alternatively, this file may be used in accordance with the terms * and conditions contained in a signed written agreement between * you and ecsec GmbH. * ***************************************************************************/ package org.openecard.ifd.scio.reader; import iso.std.iso_iec._24727.tech.schema.PasswordAttributesType; import iso.std.iso_iec._24727.tech.schema.PasswordTypeType; import java.io.ByteArrayOutputStream; import org.openecard.common.USBLangID; import org.openecard.common.util.ByteUtils; import org.openecard.common.util.IntegerUtils; import org.openecard.common.util.PINUtils; import org.openecard.common.util.UtilException; import org.openecard.ifd.scio.IFDException; /** * @author Tobias Wich <tobias.wich@ecsec.de> * @author Dirk Petrautzki <petrautzki@hs-coburg.de> */ public class PCSCPinModify { private final PasswordTypeType pwdType; private final int minLen; private final int storedLen; private final int maxLen; public PCSCPinModify(PasswordAttributesType attributes, byte[] cmdTemplate) throws IFDException { this.pwdType = attributes.getPwdType(); this.minLen = attributes.getMinLength().intValue(); this.storedLen = attributes.getStoredLength().intValue(); if (attributes.getMaxLength() != null) { this.maxLen = attributes.getMaxLength().intValue(); } else { if (pwdType == PasswordTypeType.ISO_9564_1) { this.maxLen = (storedLen * 2) - 2; } else if (pwdType == PasswordTypeType.BCD) { this.maxLen = storedLen * 2; } else { this.maxLen = this.storedLen; } } // initialise content needed for serialisation prepareStructure(attributes, cmdTemplate); } private void prepareStructure(PasswordAttributesType attributes, byte[] cmdTemplate) throws IFDException { // get apdu and pin template byte[] pinTemplate; try { pinTemplate = PINUtils.createPinMask(attributes); } catch (UtilException e) { IFDException ex = new IFDException(e); throw ex; } byte[] template = cmdTemplate; if (pinTemplate.length > 0) { template = ByteUtils.concatenate(cmdTemplate, (byte) pinTemplate.length); template = ByteUtils.concatenate(template, pinTemplate); } setData(template); boolean nibbleHandling = pwdType == PasswordTypeType.BCD || pwdType == PasswordTypeType.ISO_9564_1; boolean isoPin = pwdType == PasswordTypeType.ISO_9564_1; int pinLenIdx = template.length; // pointer to byte containing pin length in iso encoding int pinPos = isoPin ? pinLenIdx + 1 : pinLenIdx; // prepare bmFormatString byte bmSysUnits = 1; // bytes byte bmPinPos = (byte) (isoPin ? 1 : 0); byte bmJustify = 0; // left byte bmPinType = 0; // binary if (nibbleHandling) { bmPinType = 1; } else if (pwdType == PasswordTypeType.ASCII_NUMERIC) { bmPinType = 2; } this.bmFormatString = (byte) ((bmSysUnits << 7) | (bmPinPos << 3) | (bmJustify << 2) | bmPinType); // prepare pin block string byte bmPinManagement = (byte) (isoPin ? 4 : 0); // number of bits of the length field byte pinSize = (byte) (isoPin ? storedLen - 1 : storedLen); this.bmPINBlockString = (byte) ((bmPinManagement << 4) | pinSize); // pin length format byte bmPinLengthUnit = 0; // bits byte bmPinBytePos = (byte) (isoPin ? 4 : 0); bmPINLengthFormat = (byte) ((bmPinLengthUnit << 4) | bmPinBytePos); setMinPINSize((byte) minLen); setMaxPINSize((byte) maxLen); } /** timeout in seconds, 0 means default */ public byte bTimeOut = 0x15; /** timeout in seconds after first keystroke */ public byte bTimeOut2 = 0x05; /** formatting options, USB_CCID_PIN_FORMAT */ public byte bmFormatString = 0; /** bits 7-4 bit size of PIN length in APDU, bits 3-0 PIN block size in bytes after justification and formatting */ public byte bmPINBlockString = 0; /** bits 7-5 RFU, bit 4 set if system units are bytes clear if system units are bits, bits 3-0 PIN length position in system units */ public byte bmPINLengthFormat = 0; /** Insertion position offset in bytes for the current PIN */ private byte bInsertionOffsetOld = 0x00; /** Insertion position offset in bytes for the new PIN */ private byte bInsertionOffsetNew = 0x00; /** XXYY, where XX is minimum PIN size in digits, YY is maximum */ private short wPINMaxExtraDigit = 0; /** Flags governing need for confirmation of new PIN */ private byte bConfirmPIN = 0x01; /** Conditions under which PIN entry should be considered complete. * <p>The value is a bit wise OR operation: * <ul><li>0x1 Max size reached</li> * <li>0x2 Validation key pressed</li> * <li>0x4 Timeout occurred</li></ul></p> */ private byte bEntryValidationCondition = 0x2; /** Number of messages to display for PIN verification management. * <p>The value is one of: * <ul><li>0x0 no string</li> * <li>0x1 Message indicated by msg idx</li> * <li>0xFF default CCID message</li></ul></p> */ private byte bNumberMessage = (byte) 0x02; /** Language for messages */ private short wLangId = USBLangID.German_Standard.getCode(); // this software is international, so use german of couse ;-) /** Message index (should be 00). * <p>The first three messages should be as follows in the reader: * <ul><li>0x0 PIN insertion prompt: "ENTER PIN"</li> * <li>0x1 PIN modification prompt: "ENTER NEW PIN"</li> * <li>0x2 New PIN confirmation prompt: "CONFIRM NEW PIN"</li></ul></p> */ /** Index of 1st prompting message */ private byte bMsgIndex1 = 0x00; /** Index of 2nd prompting message */ private byte bMsgIndex2 = 0x01; /** Index of 3rd prompting message */ private byte bMsgIndex3 = 0x02; /** T=1 I-block prologue field to use (fill with 00) */ private final byte[] bTeoPrologue = new byte[] {0,0,0}; /** length of Data to be sent to the ICC */ private int ulDataLength = 0; /** Data to send to the ICC */ private byte[] abData; public void setMinPINSize(byte minSize) { wPINMaxExtraDigit = (short) ((wPINMaxExtraDigit & 0x00FF) | (minSize << 8)); } public byte getMinPINSize() { return (byte) ((wPINMaxExtraDigit >> 8) & 0xFF); } public void setMaxPINSize(byte maxSize) { wPINMaxExtraDigit = (short) ((wPINMaxExtraDigit & 0xFF00) | maxSize); } public byte getMaxPINSize() { return (byte) (wPINMaxExtraDigit & 0xFF); } public void setData(byte[] data) { if (data != null) { ulDataLength = data.length; abData = data; } } public byte[] toBytes() { ByteArrayOutputStream o = new ByteArrayOutputStream(42); // just a random magic number ^^ // write all numbers to the stream o.write(bTimeOut); o.write(bTimeOut2); o.write(bmFormatString); o.write(bmPINBlockString); o.write(bmPINLengthFormat); o.write(bInsertionOffsetOld); o.write(bInsertionOffsetNew); o.write(getMaxPINSize()); o.write(getMinPINSize()); o.write(bConfirmPIN); o.write(bEntryValidationCondition); o.write(bNumberMessage); byte lang_low = (byte) (wLangId & 0xFF); byte lang_high = (byte) ((wLangId >> 8) & 0xFF); o.write(lang_high); o.write(lang_low); o.write(bMsgIndex1); o.write(bMsgIndex2); o.write(bMsgIndex3); o.write(bTeoPrologue, 0, bTeoPrologue.length); byte[] ulDataLength_bytes = IntegerUtils.toByteArray(ulDataLength); for (int i = ulDataLength_bytes.length - 1; i >= 0; i--) { o.write(ulDataLength_bytes[i]); } // write missing bytes to length field for (int i = ulDataLength_bytes.length; i < 4; i++) { o.write(0); } if (ulDataLength > 0) { o.write(abData, 0, abData.length); } byte[] result = o.toByteArray(); return result; } }