/* * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package org.poreid.pcscforjava; import java.io.PrintWriter; import java.io.StringWriter; import java.util.*; /** * A Smart Card's answer-to-reset bytes. A Card's ATR object can be obtained * by calling {@linkplain Card#getATR}.<br /> * This class analyzes the various elements of the ATR but does not indicate * if the ATR is valid or not (because of some smart card norms can impact this: * ISO, EMV ...). * * <p>Instances of this class are immutable. Where data is passed in or out * via byte arrays, defensive cloning is performed. * * @see Card#getATR * * @since 1.6 * @author Andreas Sterbenz * @author JSR 268 Expert Group * @author Matthieu Leromain */ public final class ATR implements java.io.Serializable { private static final long serialVersionUID = 6695383790847736493L; /** * Defines the F values defined by ISO 7816-3. * The RFU values are defined by 0. */ private static final int aiF[] = { 372, 372, 558, 744, 1116, 1488, 1860, 0, 0, 512, 768, 1024, 1536, 2048, 0, 0}; /** * Defines the max frequency defined by ISO 7816-3. * The RFU values are defined by 0. */ private static final double afFMax[] = { 4, 5, 6, 8, 12, 16, 20, 0, 0, 5, 7.5, 10, 15, 20, 0, 0}; /** * Defines the D values defined by ISO 7816-3. * The RFU values are defined by 0. */ private static final int aiD[] = { 0, 1, 2, 4, 8, 16, 32, 64, 12, 20, 0, 0, 0, 0, 0, 0}; /** * Contains the ATR bytes array. */ private byte[] atr; /** * Contains the list of TAx after the analyze of the ATR. */ private ArrayList<Byte> aBTai; /** * Contains the list of TBx after the analyze of the ATR. */ private ArrayList<Byte> aBTbi; /** * Contains the list of TCx after the analyze of the ATR. */ private ArrayList<Byte> aBTci; /** * Contains the list of TDx after the analyze of the ATR. */ private ArrayList<Byte> aBTdi; /** * Contains the list of supported protocols after the analyze of the ATR. */ private ArrayList<Byte> aBProtocol; /** * Contains the list of supported types of transmission defined by the * array below. */ private ArrayList<String> asTaType; /** - * Contains the list of types of transmission defined by ISO 7816-3. */ private static final String m_psT[] = {"Half-duplex transmission of characters.", "Half-duplex transmission of blocks.", "RFU of full-duplex operations.", "RFU of full-duplex operations.", "Enhanced half-duplex transmission " + "of characters.", "RFU by ISO/IEC JTC 1/SC 17.", "RFU by ISO/IEC JTC 1/SC 17.", "RFU by ISO/IEC JTC 1/SC 17.", "RFU by ISO/IEC JTC 1/SC 17.", "RFU by ISO/IEC JTC 1/SC 17.", "RFU by ISO/IEC JTC 1/SC 17.", "RFU by ISO/IEC JTC 1/SC 17.", "RFU by ISO/IEC JTC 1/SC 17.", "RFU by ISO/IEC JTC 1/SC 17.", "Transmission protocols not standardized by ISO/IEC JTC 1/SC 17.", "Does not refer to a transmission protocol, but only qualifies global" + "interface bytes."}; /** * Contains the convention.<br /> * It can be:<br /> * "Direct" for a direct convention (ATR starts with 0x3B)<br /> * "Inverse" for an inverse convention (ATR starts with 0x3F) */ private String sConvention; /** * CI is used to determine the voltage class of the ICC. */ private byte BCi; /** * Clock is used to indicate whether the card supports clock stop (!= 0x00) * or not (= 0x00) and, when supported, which state is preferred on the * electrical circuit CLK when the clock is stopped. */ private byte BClock; /** * Indicates the voltage class.<br /> * Class A is 5V => 0x01.<br /> * Class B is 3V => 0x02.<br /> * Class C is 1.8V => 0x04.<br /> * A mix of flags is also possible. */ private byte BVoltageClass; /** * The smart card clock frequency in Hz. */ private int iClockCardFrequency; /** * Indicates if the smart card is in negotiable mode or not. */ private boolean bIsNegotiable; /** * Indicates if the smart card is able to change from specific to * negotiable mode or not. */ private boolean bIsAbleToChangeSpecificNegotiableMode; /** * Indicates if the transmission parameters are implicitly known by the * terminal or not. */ private boolean bAreTransmissionParametersImplicitlyKnownByTerminal; /** * Index used to parse the ATR. */ private int iIndex; /** * Contains the Ts (first byte) of the ATR. */ private byte BTs; /** * Contains the T0 (second byte) of the ATR. */ private byte BT0; /** * TA1 * The elements below are determined by TA1. */ /** * Fi is used to determine the value of F, the clock rate conversion factor, * which may be used to modify the frequency of the clock provided by the * terminal subsequent to the ATR. */ private byte BFi; /** * Di is used to determine the value of D, the bit rate adjustment factor, * which may be used to adjust the bit duration used subsequent to the ATR. */ private byte BDi; /** * TB1<br /> * The elements below are determined by TB1. */ /** * PI1 is used to determine the value of the programming voltage required by * the ICC.<br /> * If PI1 = 0 then VPP is not connected in the ICC. */ private byte BPi1; /** * II is used to determine the maximum programming current, Ipp, required * by the ICC. Not used if PI1 = 0. */ private byte Bii; /** * TC1<br /> * The element below is determined by TC1. */ /** * N is used to indicate the extra guardtime that shall be aded to the * minimum duration between the leading edges of the start bits of two * consecutives characters for subsequent exchanges from the terminal to the * ICC. */ private byte BN; /** * TB2<br /> * The element below is determined by TB2. */ /** * PI2 is used to determine the value of programming * voltage P required by the ICC. When present it overrides the value * indicated by PI1 returned in TB1. */ private byte BPi2; /** * TC2<br /> * The elements below are determined by TC2. */ /** * WI is used to determine the maximum interval between the leading edge * of the start bit of any character sent by the ICC and the leading edge * of the start bit of the previous character sent either by the ICC or the * terminal (the work waiting time). */ private byte BWi = 10; /** * The work waiting time is given by 960 * D * WI. */ private long lWorkWaitingTime; /** * TA3<br /> * The element below is determined by TA3. */ /** * IFSI is used to determine the IFSC, and specifies the maximum length of * the information field (INF) of blocks that can be received by the card. */ private byte BIfsi; /** * TB3<br /> * The elements below are determined by TB3. */ /** * BWI is used to calculate the BWT */ private byte BBwi; /** * CWI is used to calculate the CWT */ private byte BCwi; /** * TC3 Indicates the block error detection for T = 1 protocol. */ private byte BBlockErrorDetection; /** * Contains the index of the first historical byte. */ private transient int startHistorical; /** * Contains the number of historical bytes. */ private transient int nHistorical; /** * TCK is used to check the integrity of the data sent in the ATR. * The byte is not mandatory. */ private byte BTck; /** * If the TCK is present or not. */ private boolean m_bTck = false; /** * Contains an analyze of the historical bytes.\n * Each row of the array gives two columns: the byte treated and the * result description of the byte. */ private Object[][] m_ppoHistoricalBytesAnalyze; /** * Initialize the default values of the ATR class. * @param iClockCard the smart card clock frequency. */ private void initDefault(int iClockCard) { aBTai = new ArrayList(); aBTbi = new ArrayList(); aBTci = new ArrayList(); aBTdi = new ArrayList(); aBProtocol = new ArrayList(); asTaType = new ArrayList(); bIsNegotiable = false; BVoltageClass = 0x01; iClockCardFrequency = iClockCard; iIndex = 2; // Start after TS and T0 } /** * Constructs an ATR from a byte array.<br /> * Analyzes the data of the ATR.<br /><br /> * This constructor is based on a default smart card frequency of 4MHz. * * @param atr the byte array containing the answer-to-reset bytes * @throws NullPointerException if <code>atr</code> is null * @deprecated */ public ATR(byte[] atr) { // If not specified the smart card clock frequency is 4MHz initDefault(4000000); this.atr = atr.clone(); parse(); } /** * Constructs an ATR from a byte array and a smart card clock frequency.<br /> * Analyzes the data of the ATR. * * @param atr the byte array containing the answer-to-reset bytes * @param iClockCard the smart card clock frequency * @throws NullPointerException if <code>atr</code> is null */ public ATR(byte[] atr, int iClockCard) { initDefault(iClockCard); this.atr = atr.clone(); parse(); } /** * Analyze the Ts and T0 to determine the convention and the number of * historical bytes. * @return false if the ATR must be rejected.<br /> * true if all information are determined. */ private boolean analyze_ts_t0() { switch(atr[0]) { case 0x3B: sConvention = "Direct"; break; case 0x3F: sConvention = "Inverse"; break; // Reject ICC returning ATR with TS different from 3F and 3B default: return false; } BTs = atr[0]; BT0 = atr[1]; nHistorical = BT0 & 0x0F; return true; } /** * Analyze the TX1 elements. * @return false if the analyze of the ATR is finalized.<br /> * true if the analyze must continue. */ private boolean analyze_tx1() { // TA1 if((BT0 & 0x10) != 0) { bIsNegotiable = true; BFi = (byte) ((atr[iIndex] & 0xF0) >> 4); BDi = (byte) (atr[iIndex] & 0x0F); aBTai.add(atr[iIndex]); iIndex++; } else { // If TA1 absent the FiDi couple is equal to 0x11 (default value) BFi = 0x01; BDi = 0x01; aBTai.add(null); } // TB1 if((BT0 & 0x20) != 0) { aBTbi.add(atr[iIndex]); BPi1 = (byte) (atr[iIndex] & 0x1F); Bii = (byte) (atr[iIndex] & 0x60); iIndex++; } else { BPi1 = 0x00; Bii = 0x00; aBTbi.add(null); } // TC1 if((BT0 & 0x40) != 0) { aBTci.add(atr[iIndex]); BN = atr[iIndex]; iIndex++; } else { BN = 0; // Indicates 12 etus if T = 0 and 11 etus if T = 1. aBTci.add(null); } // TD1 if((BT0 & 0x80) != 0) { aBTdi.add(atr[iIndex]); aBProtocol.add((byte)(atr[iIndex] & 0x0F)); iIndex++; } else { aBTdi.add(null); // If TD1 not present it is finished for the analyze. // It remains only historical bytes and TCK if present. startHistorical = iIndex; return false; } return true; } /** * Analyze the TX2 elements. * @return false if the analyze of the ATR is finalized.<br /> * true if the analyze must continue. */ private boolean analyze_tx2() { int _iIndexForTDi = aBTdi.size() - 1; // TA2 // The presence or absence of TA2 indicates whether the ICC will operate // in specific mode or negotiable mode respectively following the ATR. if((aBTdi.get(_iIndexForTDi) & 0x10) != 0) { bIsNegotiable = false; if((atr[iIndex] & 0x80) != 0x00) bIsAbleToChangeSpecificNegotiableMode = true; else bIsAbleToChangeSpecificNegotiableMode = false; // The transmission parameters are defined by the interface characters // if b5 is set to 0, or are implicitly known by the terminal if // b5 is set to 1. if((atr[iIndex] & 0x10) != 0x00) bAreTransmissionParametersImplicitlyKnownByTerminal = true; else bAreTransmissionParametersImplicitlyKnownByTerminal = false; aBTai.add(atr[iIndex]); asTaType.add(m_psT[atr[iIndex] & 0x0F]); iIndex++; } else { // If TA2 absent // Be sure that the negotiable mode is true bIsNegotiable = true; aBTai.add(null); } // TB2 if((aBTdi.get(_iIndexForTDi) & 0x20) != 0) { BPi2 = atr[iIndex]; aBTbi.add(atr[iIndex]); iIndex++; } else { BPi1 = 0x00; Bii = 0x00; aBTbi.add(null); } // TC2 // Specific to T = 0 if((aBTdi.get(_iIndexForTDi) & 0x40) != 0) { aBTci.add(atr[iIndex]); BWi = atr[iIndex]; iIndex++; } else { BWi = 0x0A; aBTci.add(null); } lWorkWaitingTime = 960 * BDi * BWi; // TD2 if((aBTdi.get(_iIndexForTDi) & 0x80) != 0) { aBTdi.add(atr[iIndex]); aBProtocol.add((byte)(atr[iIndex] & 0x0F)); iIndex++; } else { aBTdi.add(null); // If TD2 not present it is finished for the analyze. // It remains only historical bytes and TCK if present. startHistorical = iIndex; return false; } return true; } /** * Analyze the TX3 elements. * @return false if the analyze of the ATR is finalized.<br /> * true if the analyze must continue. */ private boolean analyze_tx3() { int _iIndexForTDi = aBTdi.size() - 1; // TA3 if((aBTdi.get(_iIndexForTDi) & 0x10) != 0) { // If TD2 returns T=1 TA3 returns the information field size integer // for the ICC (IFSI). if((aBTdi.get(_iIndexForTDi) & 0x0F) == 0x01) { BIfsi = atr[iIndex]; } else if((aBTdi.get(_iIndexForTDi) & 0x0F) == 0x0F) { BIfsi = atr[iIndex]; BCi = (byte) (BIfsi & 0x3F); BClock = (byte) (BIfsi & 0xC0); } BVoltageClass = (byte) (BIfsi & 0x3F); asTaType.add(m_psT[atr[iIndex] & 0x0F]); aBTai.add(atr[iIndex]); iIndex++; } else { // If TA3 absent aBTai.add(null); } // TB3 if((aBTdi.get(_iIndexForTDi) & 0x20) != 0) { // If TD2 returns T=1 TB3 indicates the values of the CWI and the WI // used to calucalte the CWT and BWT respectively. if((aBTdi.get(_iIndexForTDi) & 0x0F) == 0x01) { BBwi = (byte)((atr[iIndex] & 0xF0) >> 4); BCwi = (byte)(atr[iIndex] & 0x0F); } aBTbi.add(atr[iIndex]); iIndex++; } else { aBTbi.add(null); } // TC3 if((aBTdi.get(_iIndexForTDi) & 0x40) != 0) { // If TD2 returns T=1 TC3 indicates the type of block error // detection code to be used. if((aBTdi.get(_iIndexForTDi) & 0x0F) == 0x01) { BBlockErrorDetection = (byte)(atr[iIndex] & 0x01); } aBTci.add(atr[iIndex]); iIndex++; } else { BBlockErrorDetection = 0x00; aBTci.add(null); } // TD3 if((aBTdi.get(_iIndexForTDi) & 0x80) != 0) { aBTdi.add(atr[iIndex]); aBProtocol.add((byte)(atr[iIndex] & 0x0F)); iIndex++; } else { aBTdi.add(null); // If TD2 not present it is finished for the analyze. // It remains only historical bytes and TCK if present. startHistorical = iIndex; return false; } return true; } /** * Analyze the TXi elements. * @return false if the analyze of the ATR is finalized.<br /> * true if the analyze must continue. */ private boolean analyze_txi() { do { int _iIndexForTDi = aBTdi.size() - 1; // TAi if((aBTdi.get(_iIndexForTDi) & 0x10) != 0) { if((aBTdi.get(_iIndexForTDi) & 0x0F) == 0x0F) { BCi = (byte) (atr[iIndex] & 0x3F); BClock = (byte) (atr[iIndex] & 0xC0); } aBTai.add(atr[iIndex]); asTaType.add(m_psT[atr[iIndex] & 0x0F]); if(aBProtocol.get(aBProtocol.size()-1) == 0x0F) BVoltageClass = (byte) (atr[iIndex] & 0x3F); iIndex++; } else { aBTai.add(null); } // TBi if((aBTdi.get(_iIndexForTDi) & 0x20) != 0) { aBTbi.add(atr[iIndex]); iIndex++; } else { aBTbi.add(null); } // TCi if((aBTdi.get(_iIndexForTDi) & 0x40) != 0) { aBTci.add(atr[iIndex]); iIndex++; } else { aBTci.add(null); } // TDi if((aBTdi.get(_iIndexForTDi) & 0x80) != 0) { aBTdi.add(atr[iIndex]); aBProtocol.add((byte)(atr[iIndex] & 0x0F)); iIndex++; } else { aBTdi.add(null); // If TD2 not present it is finished for the analyze. // It remains only historical bytes and TCK if present. startHistorical = iIndex; return false; } } while(true); } /** * Parse the ATR to analyze it. */ private void parse() { // An ATR must contain at least TS and T0 which are mandatory bytes if(atr.length < 2) return; // TS must be 0x3B or 0x3F T0 indicates the values of TX1 if(analyze_ts_t0() == false) return; lWorkWaitingTime = 960 * BWi; // Analyze TA1 TB1 TC1 TD1 if(analyze_tx1() == false) { iIndex += nHistorical; // There is a TCK if(iIndex < atr.length) { BTck = atr[iIndex]; m_bTck = true; } return; } // Analyze TA2 TB2 TC2 TD2 if(analyze_tx2() == false) { iIndex += nHistorical; // There is a TCK if(iIndex < atr.length) { BTck = atr[iIndex]; m_bTck = true; } return; } // Analyze TA3 TB3 TC3 TD3 if(analyze_tx3() == false) { iIndex += nHistorical; // There is a TCK if(iIndex < atr.length) { BTck = atr[iIndex]; m_bTck = true; } return; } // Analyze TAi TBi TCi TDi if(analyze_txi() == false) { iIndex += nHistorical; // There is a TCK if(iIndex < atr.length) { BTck = atr[iIndex]; m_bTck = true; } return; } } /** * Returns a copy of the bytes in this ATR. * * @return a copy of the bytes in this ATR. */ public byte[] getBytes() { return atr.clone(); } /** * Returns the convention of the smart card. * * @return "Direct" if the smart card is in direct convention.<br /> * "Inverse" if the smart card is in inverse convention. */ public String getConvention() { return sConvention; } /** * Returns the work waiting time (WWT) of the smart card. * * @return the work waiting time (WWT) of the smart card. */ public long getWorkWaitingTime() { return lWorkWaitingTime; } /** * Returns if the smart card baudrate is negotiable or not. * * @return true if the smart card baudrate is negotiable.<br /> * false if the smart card baudrate is not negotiable. */ public boolean isNegotiableMode() { return bIsNegotiable; } /** * Returns if the smart card is able to change from specific mode to * negotiable mode. * * @return false if the smart card is not able.<br /> * true if the smart card is able or if the smart card is already * in negotiable mode. */ public boolean isAbleToChangeFromSpecificToNegociableMode() { if(bIsNegotiable == false) return bIsAbleToChangeSpecificNegotiableMode; // Not applicable already in negotiable mode. return true; } /** * Returns if the smart card transmission parameters are implicitly known by * the terminal or not. * * @return false if the smart card transmission parameters are not implicitly * knwon by the terminal.<br /> * true if the smart card transmission parameters are implicitly * knwon by the terminal. */ public boolean areTransmissionParametersImplicitlyKnownByTerminal() { return bAreTransmissionParametersImplicitlyKnownByTerminal; } /** * Returns the list of TA.<br /> * If a TA index is absent of the ATR then the element object will be null. * <br /><br /> * E.g: the ATR contains TA1 TA3 but not TA2 then the list is equals to: * TA1, null, TA3. * * @return the list of TA. */ public ArrayList<Byte> getListOfTA() { return aBTai; } /** * Returns the list of TB.<br /> * If a TB index is absent of the ATR then the element object will be null. * <br /><br /> * E.g: the ATR contains TB1 TB3 but not TB2 then the list is equals to: * TB1, null, TB3. * * @return the list of TB. */ public ArrayList<Byte> getListOfTB() { return aBTbi; } /** * Returns the list of TC.<br /> * If a TC index is absent of the ATR then the element object will be null. * <br /><br /> * E.g: the ATR contains TC1 TC3 but not TC2 then the list is equals to: * TC1, null, TC3. * * @return the list of TC. */ public ArrayList<Byte> getListOfTC() { return aBTci; } /** * Returns the list of TD.<br /> * If a TD index is absent of the ATR then the element object will be null. * <br /><br /> * E.g: the ATR contains TD1 TD3 but not TD2 then the list is equals to: * TD1, null, TD3. * * @return the list of TD. */ public ArrayList<Byte> getListOfTD() { return aBTdi; } /** * Returns the list of protocols.<br /> * A protocol can be 0 (T = 0), 1 (T = 1), 0x0F (specific protocol). * * @return the list of protocols. */ public ArrayList<Byte> getListOfProtocols() { return aBProtocol; } /** * Returns the list of transmission types. * * @return the list of transmission types. */ public ArrayList<String> getListOfTaTypes() { return asTaType; } /** * Returns the II.<br /> * II is used to determine the maximum programming current, Ipp, required * by the ICC. Not used if PI1 = 0. * * @return the II. */ public byte getII() { return Bii; } /** * Returns the WI.<br /> * WI is used to determine the maximum interval between the leading edge * of the start bit of any character sent by the ICC and the leading edge * of the start bit of the previous character sent either by the ICC or the * terminal (the work waiting time). * * @return the WI. */ public byte getWI() { return BWi; } /** * Returns the TS. * * @return the TS. */ public byte getTS() { return BTs; } /** * Returns the TCK.<br /> * TCK is used to check the integrity of the data sent in the ATR.<br /> * This byte is not mandatory in the ATR. * * @return the TCK. */ public byte getTCK() { return BTck; } /** * Returns the T0. * * @return the T0. */ public byte getT0() { return BT0; } /** * Returns the PI2.<br /> * PI2 is used to determine the value of programming * voltage P required by the ICC. When present it overrides the value * indicated by PI1 returned in TB1. * * @return the PI2. */ public byte getPI2() { return BPi2; } /** * Returns the PI1.<br /> * PI1 is used to determine the value of the programming voltage required by * the ICC.<br /> * If PI1 = 0 then VPP is not connected in the ICC. * * @return the PI1. */ public byte getPI1() { return BPi1; } /** * Returns the N.<br /> * N is used to indicate the extra guardtime that shall be aded to the * minimum duration between the leading edges of the start bits of two * consecutives characters for subsequent exchanges from the terminal to the * ICC. * * @return the N. */ public byte getN() { return BN; } /** * Returns the IFSI.<br /> * IFSI is used to determine the IFSC, and specifies the maximum length of * the information field (INF) of blocks that can be received by the card. * * @return the IFSI. */ public byte getIFSI() { return BIfsi; } /** * Returns the Fi.<br /> * Fi is used to determine the value of F, the clock rate conversion factor, * which may be used to modify the frequency of the clock provided by the * terminal subsequent to the ATR. * * @return the Fi. */ public byte getFI() { return BFi; } /** * Returns the Di.<br /> * Di is used to determine the value of D, the bit rate adjustment factor, * which may be used to adjust the bit duration used subsequent to the ATR. * * @return the Di. */ public byte getDI() { return BDi; } /** * Returns the CWI.<br /> * CWI is used to calculate the CWT. * * @return the CWI. */ public byte getCWI() { return BCwi; } /** * Returns the BWI.<br /> * BWI is used to calculate the BWT. * * @return the BWI. */ public byte getBWI() { return BBwi; } /** * Returns the block error detection for T = 1 protocol.<br /> * TC3 Indicates the block error detection for T = 1 protocol.<br /><br /> * <ul><li>0x00 => for Longitudinal Redundancy Code.</li><br /> * <li>0x01 => for Cyclic Redundancy Code.</li></ul> * * @return the block error detection for T = 1 protocol. */ public byte getBlockErrorDetection() { return BBlockErrorDetection; } /** * Returns the clock support.<br /> * Clock is used to indicate whether the card supports clock stop (!= 0x00) * or not (= 0x00) and, when supported, which state is preferred on the * electrical circuit CLK when the clock is stopped. * * @return the clock support. */ public byte getClock() { return BClock; } /** * Returns the current F in function of the TA1. * * @return the current F in function of the TA1. */ public int getF() { return aiF[BFi]; } /** * Returns the current D in function of the TA1. * * @return the current D in function of the TA1. */ public int getD() { return aiD[BDi]; } /** * Returns the max possible smart card frequency in function of the TA1. * * @return the max possible smart card frequency in function of the TA1. */ public double getClkFMax() { return afFMax[BFi]; } /** * Returns the smart card clock frequency. * * @return the smart card clock frequency. */ public double getClkF() { return iClockCardFrequency; } /** * Returns the default baudrate of the smart card. * * @return the default baudrate of the smart card. */ public double getDefaultBaudRate() { return Math.round(1 / (372 / 1 * 1.0 / getClkF())); } /** * Returns the current baudrate of the smart card. * * @return the current baudrate of the smart card. */ public double getBaudRate() { return Math.round((1 / (((double)getF() / (double)getD()) * ((1.0 / getClkF()))))); } /** * Returns the BWT.<br /> * The block waiting time is equal to: 11 etus + 2^BWI * 960 * Fi * Di / F etus. * * @return the BWT. */ public double getBlockWaitingTime() { int _iBWI = (getBWI() < 0) ? (256 + getBWI()) : getBWI(); return 11 + ((Math.pow(2, _iBWI) * 960 * getF() * getD()) / getClkF()); } /** * Returns the CWT.<br /> * The character waiting time is equal to: 11 etus + 2^CWI etus. * * @return the CWT. */ public double getCharacterWaitingTime() { int _iCWI = (getCWI() < 0) ? (256 + getCWI()) : getCWI(); return 11 + Math.pow(2, _iCWI); } private int byteToInt(byte aByte) { if(aByte < 0) return 256 + aByte; return aByte; } /** * Returns the EGT.<br /> * The extra guard time is equal to: 12 etus + Fi / Di * (N / F) etus.<br /> * The calcul is protocol depending. * @param iProtocol The protocol to analyze. * @return the EGT. */ public double getExtraGuardTime(int iProtocol) { double _dR; double _dN = byteToInt(getN()); if(_dN == 255) { if(isSupportedProtocol(iProtocol)) { if(iProtocol == 0) return 12; else return 11; } else return 0; } if(isSupportedProtocol(0x0F)) { _dR = getF() / getD(); } else { _dR = getF() / getD(); } return 12 + _dR * (_dN / getClkF()); } /** * Indicates if a protocol is supported by the smart card or not. * @param iProtocol the protocol.<br /> * It can be:<br /><br /> * <ul><li>0 for T = 0 protocol</li> * <li>1 for T = 1 protocol</li> * <li>0x0F for specific protocol</li></ul> * * @return if the protocol is supported by the smart card or not. */ public boolean isSupportedProtocol(int iProtocol) { int _i = 0; if((iProtocol != 0) && (iProtocol != 1) && (iProtocol != 0x0F)) return false; while(_i < aBProtocol.size()) { if(aBProtocol.get(_i) == iProtocol) return true; _i++; } if((aBProtocol.isEmpty()) && (iProtocol == 0)) return true; return false; } /** * Returns the voltage class.<br /> * Class A is 5V => 0x01.<br /> * Class B is 3V => 0x02.<br /> * Class C is 1.8V => 0x04.<br /> * A mix of flags is also possible. * * @return the voltage class. */ public byte getVoltageClass() { return BVoltageClass; } /** * Returns a copy of the historical bytes in this ATR. * If this ATR does not contain historical bytes, an array of length * zero is returned. * * @return a copy of the historical bytes in this ATR. */ public byte[] getHistoricalBytes() { byte[] b = new byte[nHistorical]; System.arraycopy(atr, startHistorical, b, 0, nHistorical); return b; } /** * Indicates if the TA with the index is present or not. * @param iIndex the index of the TA requested. 1 for TA1. TA0 does not exist. * * @return if the TA with the index is present or not. */ public boolean isTaPresent(int iIndex) { if((this.aBTai != null) && (this.aBTai.get(iIndex-1) != null)) return true; return false; } /** * Indicates if the TB with the index is present or not. * @param iIndex the index of the TB requested. 1 for TB1. TB0 does not exist. * * @return if the TB with the index is present or not. */ public boolean isTbPresent(int iIndex) { if((this.aBTbi != null) && (this.aBTbi.get(iIndex-1) != null)) return true; return false; } /** * Indicates if the TC with the index is present or not. * @param iIndex the index of the TC requested. 1 for TC1. TC0 does not exist. * * @return if the TC with the index is present or not. */ public boolean isTcPresent(int iIndex) { if((this.aBTci != null) && (this.aBTci.get(iIndex-1) != null)) return true; return false; } /** * Indicates if the TD with the index is present or not. * @param iIndex the index of the TD requested. 1 for TD1. TD0 does not exist. * * @return if the TD with the index is present or not. */ public boolean isTdPresent(int iIndex) { if((this.aBTdi != null) && (this.aBTdi.get(iIndex-1) != null)) return true; return false; } /** * Returns a string representation of this ATR. * * @return a String representation of this ATR. */ public String toString() { StringWriter _sw; PrintWriter _pw; _sw = new StringWriter(50); _pw = new PrintWriter(_sw); for(int _m = 0; _m< atr.length; _m++) _pw.printf("%02X", atr[_m]); return "ATR: " + atr.length + " bytes => " + _sw.toString(); } /** * Compares the specified object with this ATR for equality. * Returns true if the given object is also an ATR and its bytes are * identical to the bytes in this ATR. * * @param obj the object to be compared for equality with this ATR * @return true if the specified object is equal to this ATR */ public boolean equals(Object obj) { if (this == obj) { return true; } if (obj instanceof ATR == false) { return false; } ATR other = (ATR)obj; return Arrays.equals(this.atr, other.atr); } /** * Returns the hash code value for this ATR. * * @return the hash code value for this ATR. */ public int hashCode() { return Arrays.hashCode(atr); } /** * Returns if the ATR has a TCK or not. * * @return if the ATR has a TCK or not. */ public boolean hasTCK() { return this.m_bTck; } /** * Returns the analyze of the historical bytes. * @return the analyze of the historical bytes. */ public Object[][] getHistoricalBytesAnalyze() { analyzeHistoricalBytes(); return m_ppoHistoricalBytesAnalyze; } // Life cycle status byte, page 21 of ISO 7816-4 private String getLCS(byte BLcs) { if((BLcs & 0xF0) > 0) return "Proprietary"; switch(BLcs & 0x0F) { case 0x00: return "No information given"; case 0x01: return "Creation state"; case 0x03: return "Initialisation state"; case 0x04: case 0x06: return "Operational state (deactivated)"; case 0x05: case 0x07: return "Operation state (activated)"; case 0x0C: case 0x0D: case 0x0E: case 0x0F: return "Termination state"; default: return "Unknown"; } } private int splitStringToAnalyzeHistorical(String sResult, int j) { String [] _psArray = sResult.split("\n"); for(int _i = 0; _i < _psArray.length; _i++) { m_ppoHistoricalBytesAnalyze[j][1] = _psArray[_i]; j++; } return --j; } // Card service data byte, page 59 of ISO 7816-4 private int getCs(byte BCs, int j) { String _sResult = ""; int _iTmp = 0; if((BCs & ((byte)0x80)) != 0) _sResult += "Application selection: by full DF name\n"; if((BCs & ((byte)0x40)) != 0) _sResult += "Application selection: by partial DF name\n"; if((BCs & ((byte)0x20)) != 0) _sResult += "BER-TLV data objects available in EF.DIR\n"; if((BCs & ((byte)0x10)) != 0) _sResult += "BER-TLV data objects available in EF.ATR\n"; if((BCs & ((byte)0x08)) != 0) _iTmp += 4; if((BCs & ((byte)0x04)) != 0) _iTmp += 2; if((BCs & ((byte)0x02)) != 0) _iTmp += 1; _sResult += "EF.DIR and EF.ATR access services: "; switch(_iTmp) { case 4: _sResult += "by READ BINARY command\n"; break; case 0: _sResult += "by GET RECORD(s) command\n"; break; case 2: _sResult += "by GET DATA command\n"; break; default: _sResult += "reverved for future use\n"; break; } if((BCs & ((byte)0x01)) != 0) _sResult += "Card without MF\n"; else _sResult += "Card with MF\n"; return splitStringToAnalyzeHistorical(_sResult, j); } // First software function table (selection methods), // page 60 of ISO 7816-4 private int getSm(byte BSm, int j) { String _sResult = ""; if((BSm & ((byte)0x80)) != 0) _sResult += "DF selection by full DF name\n"; if((BSm & ((byte)0x40)) != 0) _sResult += "DF selection by partial DF name\n"; if((BSm & ((byte)0x20)) != 0) _sResult += "DF selection by path\n"; if((BSm & ((byte)0x10)) != 0) _sResult += "DF selection by file identifier\n"; if((BSm & ((byte)0x08)) != 0) _sResult += "Implicit DF selection\n"; if((BSm & ((byte)0x04)) != 0) _sResult += "Short EF identifier supported\n"; if((BSm & ((byte)0x02)) != 0) _sResult += "Record number supported\n"; if((BSm & ((byte)0x01)) != 0) _sResult += "Record identifier supported\n"; return splitStringToAnalyzeHistorical(_sResult, j); } // Second software function table (data coding byte), // page 60 of ISO 7816-4 private int getDc(byte BDc, int j) { String _sResult = ""; int _iTmp = 0; if((BDc & ((byte)0x80)) != 0) _sResult += "EF of TLV structure supported\n"; _sResult += "Behavoiur of write functions: "; if((BDc & ((byte)0x40)) != 0) _iTmp += 2; if((BDc & ((byte)0x20)) != 0) _iTmp += 1; switch(_iTmp) { case 0: _sResult += "one-time write\n"; break; case 1: _sResult += "proprietary\n"; break; case 2: _sResult += "write OR\n"; break; case 3: _sResult += "write AND\n"; break; default: break; } _sResult += "Value 'FF' for the first byte of BER-TLV tag fields: "; if((BDc & ((byte)0x10)) != 0) _sResult += "invalid\n"; else _sResult += "valid\n"; _sResult += "Data Unit in quartets: "; _iTmp = 0; if((BDc & ((byte)0x08)) != 0) _iTmp += 8; if((BDc & ((byte)0x04)) != 0) _iTmp += 4; if((BDc & ((byte)0x02)) != 0) _iTmp += 2; if((BDc & ((byte)0x01)) != 0) _iTmp += 1; _sResult += String.valueOf(Math.pow(2, _iTmp)) + "\n"; return splitStringToAnalyzeHistorical(_sResult, j); } // Third software function table (command chaining, // length fields and logical channels), page 61 of ISO 7816-4 private int getCc(byte BCc, int j) { String _sResult = ""; int _iTmp = 0; if((BCc & ((byte)0x80)) != 0) _sResult += "Command chaining\n"; if((BCc & ((byte)0x40)) != 0) _sResult += "Extended Lc and Le fields\n"; if((BCc & ((byte)0x20)) != 0) _sResult += "RFU (should not happen)\n"; _sResult += "Logical channel number assignment: "; if((BCc & ((byte)0x10)) != 0) _iTmp += 2; if((BCc & ((byte)0x08)) != 0) _iTmp += 1; switch(_iTmp) { case 0: _sResult += "No logical channel\n"; break; case 1: _sResult += "by the interface device\n"; break; case 2: _sResult += "by the card\n"; break; case 3: _sResult += "by the interface device and card\n"; break; default: break; } _iTmp = 0; if((BCc & ((byte)0x04)) != 0) _iTmp += 4; if((BCc & ((byte)0x02)) != 0) _iTmp += 2; if((BCc & ((byte)0x01)) != 0) _iTmp += 1; _sResult += "Maximum numbre of logical channels: " + _iTmp + "\n"; return splitStringToAnalyzeHistorical(_sResult, j); } private int analyzeTLV(int j, int iEnd) { int _i = 1; byte[] _tmppByte; while(_i + iEnd < nHistorical) { int _j = 0; byte _BTag = (byte) (((byte) (atr[startHistorical + _i] & 0xF0)) >> 4); int _iLength = (atr[startHistorical + _i] & 0x0F); m_ppoHistoricalBytesAnalyze[++j][0] = byteToString( atr[startHistorical + _i]); m_ppoHistoricalBytesAnalyze[j][1] = "Tag: " + byteToString(_BTag) + " - Length: " + _iLength + " - "; switch(_BTag) { case 0x01: m_ppoHistoricalBytesAnalyze[j][1] += " (Country Code, " + "ISO 3166-1)"; ++j; m_ppoHistoricalBytesAnalyze[j][0] = ""; for(_j = 0; _j < _iLength-_i; _j++) { m_ppoHistoricalBytesAnalyze[j][0] += byteToString(atr[startHistorical + _i + _j + 1]); } break; case 0x02: m_ppoHistoricalBytesAnalyze[j][1] += " (Issuer " + "Identification Number, " + "ISO 7812-1)"; ++j; m_ppoHistoricalBytesAnalyze[j][0] = ""; for(_j = 0; _j < _iLength-_i; _j++) { m_ppoHistoricalBytesAnalyze[j][0] += byteToString(atr[startHistorical + _i + _j + 1]); } break; case 0x03: m_ppoHistoricalBytesAnalyze[j][1] += " (Card Service Data " + "Byte)"; m_ppoHistoricalBytesAnalyze[++j][0] = byteToString(atr[startHistorical + _i + 1]); j = getCs(atr[startHistorical + _i + 1], j); break; case 0x04: m_ppoHistoricalBytesAnalyze[j][1] += " (Initial Access Data)"; ++j; m_ppoHistoricalBytesAnalyze[j][0] = ""; for(_j = 0; _j < _iLength-_i; _j++) { m_ppoHistoricalBytesAnalyze[j][0] += byteToString(atr[startHistorical + _i + _j + 1]); } break; case 0x05: m_ppoHistoricalBytesAnalyze[j][1] += " (Card Issuer's Data)"; ++j; m_ppoHistoricalBytesAnalyze[j][0] = ""; for(_j = 0; _j < _iLength-_i; _j++) { m_ppoHistoricalBytesAnalyze[j][0] += byteToString(atr[startHistorical + _i + _j + 1]); } break; case 0x06: m_ppoHistoricalBytesAnalyze[j][1] += " (Pre-Issuing Data)"; ++j; m_ppoHistoricalBytesAnalyze[j][0] = ""; for(_j = 0; _j < _iLength-_i; _j++) { m_ppoHistoricalBytesAnalyze[j][0] += byteToString(atr[startHistorical + _i + _j + 1]); } break; case 0x07: m_ppoHistoricalBytesAnalyze[j][1] += " (Card Capabilities)"; ++j; switch(_iLength) { case 0x01: m_ppoHistoricalBytesAnalyze[j][0] = byteToString( atr[startHistorical + _i + 1]); m_ppoHistoricalBytesAnalyze[j][1] = "Selection " + "Methods"; j++; j = getSm(atr[startHistorical + _i + 1], j); break; case 0x02: m_ppoHistoricalBytesAnalyze[j][0] = byteToString( atr[startHistorical + _i + 1]); m_ppoHistoricalBytesAnalyze[j][1] = "Selection " + "Methods"; j++; j = getSm(atr[startHistorical + _i + 1], j); j++; m_ppoHistoricalBytesAnalyze[j][0] = byteToString( atr[startHistorical + _i + 2]); m_ppoHistoricalBytesAnalyze[j][1] = "Data Coding " + "Byte"; j++; j = getDc(atr[startHistorical + _i + 2], j); break; case 0x03: m_ppoHistoricalBytesAnalyze[j][0] = byteToString( atr[startHistorical + _i + 1]); m_ppoHistoricalBytesAnalyze[j][1] = "Selection " + "Methods"; j++; j = getSm(atr[startHistorical + _i + 1], j); j++; m_ppoHistoricalBytesAnalyze[j][0] = byteToString( atr[startHistorical + _i + 2]); m_ppoHistoricalBytesAnalyze[j][1] = "Data Coding " + "Byte"; j++; j = getDc(atr[startHistorical + _i + 2], j); j++; m_ppoHistoricalBytesAnalyze[j][0] = byteToString( atr[startHistorical + _i + 3]); m_ppoHistoricalBytesAnalyze[j][1] = "Command " + "Chaining, Length Fields and Logical " + "Channels"; j++; j = getCc(atr[startHistorical + _i + 3], j); break; default: break; } break; case 0x08: m_ppoHistoricalBytesAnalyze[j][1] += " (Status Indicator)"; switch(_iLength) { case 0x01: m_ppoHistoricalBytesAnalyze[++j][0] = byteToString( atr[nHistorical + startHistorical - 3]); m_ppoHistoricalBytesAnalyze[j][1] = "LCS (Life " + "Card Cycle)"; break; case 0x02: _tmppByte = new byte[2]; _tmppByte[0] = atr[nHistorical + startHistorical - 2]; _tmppByte[1] = atr[nHistorical + startHistorical - 1]; m_ppoHistoricalBytesAnalyze[++j][0] = pByteToString(_tmppByte); m_ppoHistoricalBytesAnalyze[j][1] = "SW (Status " + "Word)"; break; case 0x03: m_ppoHistoricalBytesAnalyze[++j][0] = byteToString( atr[nHistorical + startHistorical - 3]); m_ppoHistoricalBytesAnalyze[j][1] = "LCS (Life " + "Card Cycle)"; m_ppoHistoricalBytesAnalyze[++j][0] = ""; m_ppoHistoricalBytesAnalyze[j][1] = getLCS( atr[nHistorical + startHistorical - 3]); _tmppByte = new byte[2]; _tmppByte[0] = atr[nHistorical + startHistorical - 2]; _tmppByte[1] = atr[nHistorical + startHistorical - 1]; m_ppoHistoricalBytesAnalyze[+j][0] = pByteToString(_tmppByte); m_ppoHistoricalBytesAnalyze[j][1] = "SW (Status " + "Word)"; break; default: break; } break; case 0x0F: m_ppoHistoricalBytesAnalyze[j][1] += " (Application " + "Identifier)\n"; ++j; m_ppoHistoricalBytesAnalyze[j][0] = ""; for(_j = 0; _j < _iLength-_i; _j++) { m_ppoHistoricalBytesAnalyze[j][0] += byteToString(atr[startHistorical + _i + _j + 1]); } break; default: m_ppoHistoricalBytesAnalyze[j][1] += " (Unknown)\n"; ++j; m_ppoHistoricalBytesAnalyze[j][0] = ""; for(_j = 0; _j < _iLength-_i; _j++) { m_ppoHistoricalBytesAnalyze[j][0] += byteToString(atr[startHistorical + _i + _j + 1]); } break; } _i += _iLength + 1; } return j; } private String pByteToString(byte[] command) { if(command == null) return ""; StringWriter _sw; PrintWriter _pw; _sw = new StringWriter(50); _pw = new PrintWriter(_sw); for(int _m=0; _m<command.length; _m++) _pw.printf("%02X", command[_m]); return (_sw.toString()); } private String byteToString(byte bByte) { byte[] _pByte = new byte[1]; _pByte[0] = bByte; return pByteToString(_pByte); } private void analyzeHistoricalBytes() { int _j = 0; byte[] _tmppByte; m_ppoHistoricalBytesAnalyze = new Object[200][2]; if(nHistorical == 0) return; m_ppoHistoricalBytesAnalyze[_j][0] = byteToString(atr[startHistorical]); m_ppoHistoricalBytesAnalyze[_j][1] = "Category indicator byte: "; switch(atr[startHistorical]) { case 0x00: m_ppoHistoricalBytesAnalyze[_j][1] += " (compact TLV data object)\n"; _j = analyzeTLV(_j, 3); _tmppByte = new byte[3]; _tmppByte[0] = atr[nHistorical + startHistorical - 3]; _tmppByte[1] = atr[nHistorical + startHistorical - 2]; _tmppByte[2] = atr[nHistorical + startHistorical - 1]; m_ppoHistoricalBytesAnalyze[++_j][0] = pByteToString(_tmppByte); m_ppoHistoricalBytesAnalyze[_j][1] = "Mandatory status " + "indicator (3 last bytes)"; m_ppoHistoricalBytesAnalyze[++_j][0] = byteToString( atr[nHistorical + startHistorical - 3]); m_ppoHistoricalBytesAnalyze[_j][1] = "LCS (Life Card Cycle)"; m_ppoHistoricalBytesAnalyze[++_j][0] = ""; m_ppoHistoricalBytesAnalyze[_j][1] = getLCS(atr[nHistorical + startHistorical - 3]); _tmppByte = new byte[2]; _tmppByte[0] = atr[nHistorical + startHistorical - 2]; _tmppByte[1] = atr[nHistorical + startHistorical - 1]; m_ppoHistoricalBytesAnalyze[++_j][0] = pByteToString( _tmppByte); m_ppoHistoricalBytesAnalyze[_j][1] = "SW (Status Word)"; break; case (byte)0x80: m_ppoHistoricalBytesAnalyze[_j][1] += " (compact TLV data object)\n"; _j = analyzeTLV(_j, 0); break; case 0x10: m_ppoHistoricalBytesAnalyze[++_j][0] = byteToString( atr[startHistorical + 1]); m_ppoHistoricalBytesAnalyze[_j][1] = "DIR data reference"; break; case (byte)0x81: case (byte)0x82: case (byte)0x83: case (byte)0x84: case (byte)0x85: case (byte)0x86: case (byte)0x87: case (byte)0x88: case (byte)0x89: case (byte)0x8A: case (byte)0x8B: case (byte)0x8C: case (byte)0x8D: case (byte)0x8E: case (byte)0x8F: m_ppoHistoricalBytesAnalyze[_j][1] += " (RFU)\n"; break; default: m_ppoHistoricalBytesAnalyze[_j][1] += " (proprietary format)\n"; break; } Object[][] _finalObj = new Object[_j+1][2]; int _i = 0; while(_i <= _j) { _finalObj[_i][0] = m_ppoHistoricalBytesAnalyze[_i][0]; _finalObj[_i][1] = m_ppoHistoricalBytesAnalyze[_i][1]; _i++; } m_ppoHistoricalBytesAnalyze = _finalObj; } }