Package uk.co.nimp.scard

Source Code of uk.co.nimp.scard.ScriptPlayer$PowerOn

/**
*
* @author Sebastien Riou
*/
package uk.co.nimp.scard;

import com.atolsystems.atolutilities.AStringUtilities;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.smartcardio.CardNotPresentException;
import uk.co.nimp.smartcard.*;
import com.atolsystems.atolutilities.StopRequestFromUserException;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.TimerTask;
import javax.swing.JOptionPane;


public class ScriptPlayer implements StatusDisplayer{

    /*public interface StatusDisplayer{
        public void setText(String msg);
    }*/

    static String nl;
    static{
        nl=AStringUtilities.systemNewLine;
    }
    public abstract static class CallBack{
        final static public int SUCCESS=0;
        final static public int WARNING=1;
        final static public int ERROR=2;
        final static public int SYSTEM_ERROR=3;
        public abstract int execute(GenericTerminal terminal) throws ScardException;
    }
    static public class PowerOff{//to end a communication or prepare a cold activation
    }
    static public class PowerOn{//just activation (cold or warm depending of previous state)
        public final int activation;

        public PowerOn(int activation) {
            this.activation = activation;
        }
    }
    static public class PcscPowerOn{//activation + PPS: always try to set the fastest speed
        public final int protocol;
        public final int activation;

        public PcscPowerOn(int protocol, int activation) {
            this.protocol = protocol;
            this.activation = activation;
        }
    }
    static public class Frequency{
        public final int frequency;

        public Frequency(int frequency) {
            this.frequency = frequency;
        }
    }
    static public class PowerSupplyVoltage{
        public final int voltage;

        public PowerSupplyVoltage(int voltage) {
            this.voltage = voltage;
        }
    }
    static public class Pps{
        public final int fi;
        public final int di;
        public final int t;

        public Pps(int fi, int di, int t) {
            this.fi = fi;
            this.di = di;
            this.t = t;
        }
    }
    static public class DelayNs{
        public final long delayNs;

        public DelayNs(long delayNs) {
            this.delayNs = delayNs;
        }
    }
    static public class ApduTimeout{
        public final int timeoutMs;

        public ApduTimeout(int timeoutMs) {
            this.timeoutMs = timeoutMs;
        }
    }
    static public class ApduTimeoutPush extends ApduTimeout{
        public ApduTimeoutPush(int timeoutMs) {
            super(timeoutMs);
        }
    }
    static public class ApduTimeoutPop {
    }
    /*static public class ApduWithTimeout{
        public final int timeBudgetMs;
        public final Apdu apdu;

        public ApduWithTimeout(Apdu apdu, int timeBudgetMs) {
            this.apdu=apdu;
            this.timeBudgetMs = timeBudgetMs;
        }
    }*/

    static public class ExpectedApduResponse{
        public final int id;
        public final byte[] leData;
        public final int statusWord;

        public ExpectedApduResponse(int id, byte[] leData, short statusWord) {
            this.id = id;
            this.leData = leData;
            this.statusWord = 0xFFFF & statusWord;
        }

        private ExpectedApduResponse(Apdu apdu) {
            this.id=0;
            this.leData=apdu.getLeData();
            this.statusWord=apdu.getSw();
        }

        private ExpectedApduResponse(AnswerToReset answerToReset) {
            this.id=0;
            this.leData=new byte[answerToReset.getData().length-2];
            System.arraycopy(answerToReset.getData(), 0, this.leData, 0, this.leData.length);
            /*
             * for(int i=0;i<this.leData.length;i++)
                this.leData[i]=answerToReset.getData()[i];

             */
            int temp=0xFF & answerToReset.getData()[answerToReset.getData().length-2];
            temp=(temp<<8)+(0xFF & answerToReset.getData()[answerToReset.getData().length-1]);
            this.statusWord=temp;
        }
    }

    static public class BufferOperation{
        public static final int OP_FULL_COMPARISON=1;
        public static final int OP_AND=2;
        public static final int OP_OR=3;
        public static final int OP_XOR=4;
        public static final int OP_INVALID=5;

        public final int id1;
        public final int id2;
        public final int operationId;

        public BufferOperation(int id1, int id2, int operationId) {
            this.id1 = id1;
            this.id2 = id2;
            this.operationId = operationId;
            if((operationId<=0) || (operationId>=OP_INVALID))
                throw new RuntimeException("Invalid operation ID: "+ operationId);
        }

        public void perform(ScriptPlayer player) throws ScardException, StopRequestFromUserException, UnexpectedCardResponseException, CardNotPresentException{
            Object o1=player.buffers.get(id1);
            Object o2=player.buffers.get(id2);
            if(null==o1)
                throw new RuntimeException("Buffer "+id1+" is not defined.");
            if(null==o1)
                throw new RuntimeException("Buffer "+id2+" is not defined.");

            ExpectedApduResponse response1;
            if(o1.getClass().equals(ExpectedApduResponse.class)){
                response1=(ExpectedApduResponse)o1;
            } else if(o1.getClass().equals(Apdu.class)){
                response1=new ExpectedApduResponse((Apdu)o1);
            } else if(o1.getClass().equals(AnswerToReset.class)){
                response1=new ExpectedApduResponse((AnswerToReset)o1);
            } else
                throw new RuntimeException("Cannot perform operation on class "+o1.getClass());
            ExpectedApduResponse response2;
            if(o2.getClass().equals(ExpectedApduResponse.class)){
                response2=(ExpectedApduResponse)o2;
            } else if(o2.getClass().equals(Apdu.class)){
                response2=new ExpectedApduResponse((Apdu)o2);
            } else if(o2.getClass().equals(AnswerToReset.class)){
                response2=new ExpectedApduResponse((AnswerToReset)o2);
            } else
                throw new RuntimeException("Cannot perform operation on class "+o2.getClass());

            switch(operationId){
                case OP_AND:
                case OP_OR:
                case OP_XOR:
                {            
                    int compareLen=response1.leData.length;
                    byte out[]=new byte[response1.leData.length];
                    int outStatusWord;
                    if(response1.leData.length!=response2.leData.length)
                        throw new RuntimeException("Length of buffers do not match: response1.leData.length="+response1.leData.length+", response2.leData.length="+response2.leData.length);
                    else{
                        switch(operationId){
                            case OP_AND:
                                for(int i=0;i<compareLen;i++)
                                    out[i] = (byte)(response1.leData[i] & response2.leData[i]);
                                outStatusWord=response1.statusWord & response2.statusWord;
                                break;
                            case OP_OR:
                                for(int i=0;i<compareLen;i++)
                                    out[i] = (byte)(response1.leData[i] | response2.leData[i]);
                                outStatusWord=response1.statusWord | response2.statusWord;
                                break;
                            case OP_XOR:
                                for(int i=0;i<compareLen;i++)
                                    out[i] = (byte)(response1.leData[i] ^ response2.leData[i]);
                                outStatusWord=response1.statusWord ^ response2.statusWord;
                                break;
                            default:
                                throw new RuntimeException("Invalid operation code: "+ operationId);
                        }
                    }
                    response1=new ExpectedApduResponse(id1, out, (short) outStatusWord);
                    player.buffers.put(response1.id, response1);
                    break;
                }
                case OP_FULL_COMPARISON:
                {
                    if(false==player.checkOperation)
                        return;
                    //Object o1=player.buffers.get(id1);
                    //Object o2=player.buffers.get(id2);

                    /*ExpectedApduResponse response1;
                    if(o1.getClass().equals(ExpectedApduResponse.class)){
                        response1=(ExpectedApduResponse)o1;
                    } else if(o1.getClass().equals(Apdu.class)){
                        response1=new ExpectedApduResponse((Apdu)o1);
                    } else if(o1.getClass().equals(AnswerToReset.class)){
                        response1=new ExpectedApduResponse((AnswerToReset)o1);
                    } else
                        throw new RuntimeException("Cannot compare class "+o1.getClass());
                    ExpectedApduResponse response2;
                    if(o2.getClass().equals(ExpectedApduResponse.class)){
                        response2=(ExpectedApduResponse)o2;
                    } else if(o2.getClass().equals(Apdu.class)){
                        response2=new ExpectedApduResponse((Apdu)o2);
                    } else if(o2.getClass().equals(AnswerToReset.class)){
                        response2=new ExpectedApduResponse((AnswerToReset)o2);
                    } else
                        throw new RuntimeException("Cannot compare class "+o2.getClass());*/
                   
                    //int compareLen=Math.min(response1.leData.length, response2.leData.length);
                    int compareLen=response1.leData.length;
                    boolean lengthError=false;
                    byte leDataError=0;
                    if(response1.leData.length!=response2.leData.length)
                        lengthError=true;
                    else{
                        int first;
                        int second;
                        /*if(null!=player.compareMask){
                            first=Math.min(compareLen, player.compareMask.length);
                            second=Math.max(compareLen-player.compareMask.length, 0);
                            for(int i=0;i<first;i++){
                                leDataError|=player.compareMask[i] & (response1.leData[i] ^ response2.leData[i]);
                            }
                        }else*/{
                            first=0;
                            second=compareLen;
                        }

                        for(int i=first;i<second;i++){
                            leDataError|=(response1.leData[i] ^ response2.leData[i]);
                        }
                    }
                    int swError=response1.statusWord ^ response2.statusWord;
                    if(lengthError || ((leDataError|swError)!=0)){
                        player.errorOccured=true;
                        String temp="Buffer comparison failed:"+nl;
                        String msg = temp+nl;
                        String logMsg=temp;
                        if(lengthError){
                            temp = "Le data size don't match:"+nl;
                            temp += nl+"First buffer length is  "+response1.leData.length+" (0x"+Integer.toHexString(response1.leData.length)+")"+nl;
                            temp += nl+"Second buffer length is "+response2.leData.length+" (0x"+Integer.toHexString(response2.leData.length)+")"+nl;
                            msg+=temp;
                            logMsg+=temp;
                        }
                        if(leDataError!=0){
                            temp = "Error indexe(s) in \"Le data\": ";
                            int errCnt=0;
                            for(int i=0;i<compareLen;i++){
                                if(0!=(response1.leData[i] ^ response2.leData[i])){
                                    if((i!=0) && (0!=errCnt))
                                        temp+=", ";
                                    temp+=i;
                                    errCnt++;
                                }
                            }
                            msg+=AStringUtilities.limitSize(temp, 64, "...");
                            logMsg+=temp;
                        }
                        temp = nl+"First buffer";
                        msg+=temp;
                        logMsg+=temp;
                        if(response1.leData.length>0){
                            temp=nl+AStringUtilities.bytesToHex(response1.leData)+nl;
                            msg += AStringUtilities.limitSize(temp, 64, "...");
                            logMsg+=temp;
                            temp = "Status word: ";
                        }else
                            temp = "'s status word: ";
                        temp+= AStringUtilities.shortToHex(response1.statusWord)+nl;
                        temp+= nl+"Second buffer";
                        msg+=temp;
                        logMsg+=temp;
                        if(response2.leData.length>0){
                            temp=nl+AStringUtilities.bytesToHex(response2.leData)+nl;
                            msg += AStringUtilities.limitSize(temp, 64, "...");
                            logMsg+=temp;
                            temp = "Status word: ";
                        }else
                            temp = "'s status word: ";
                        temp += AStringUtilities.shortToHex(response2.statusWord)+nl+nl;
                        msg+=temp;
                        logMsg+=temp;
                        msg += "Do you want to continue to check card responses ? (press Cancel to stop sequence)";
                       
                        UnexpectedCardResponseException ex=new UnexpectedCardResponseException(logMsg);
                        player.playTerminal.log(ex);
                        long start=System.nanoTime();
                        int choice = JOptionPane.showConfirmDialog(null, msg, "Buffer comparison failed", JOptionPane.YES_NO_CANCEL_OPTION);
                        player.deadTime+=System.nanoTime()-start;
                        player.playTerminal.playUnexpectedCardResponseScript();
                       
                        if (JOptionPane.NO_OPTION == choice) {
                            player.checkOperation = false;
                        }
                        if (JOptionPane.CANCEL_OPTION == choice) {
                            player.userCancelled = true;
                            throw new StopRequestFromUserException("Operation cancelled by user.");
                        }
                    }
                    break;
                }
                default:
                    throw new RuntimeException("Invalid operation code: "+ operationId);
            }
        }
    }

    protected boolean errorOccured;
    protected boolean checkOperation;
    protected boolean userCancelled;
    protected boolean reportUnsupportedError;
    protected Map<Integer,Object> buffers;
    protected GenericTerminal playTerminal;
    protected boolean checkConnection;
    protected boolean checkConnectionDone;
    protected long nRun;
    protected long runCnt;
    protected StatusDisplayer statusDiplayer;
    protected long deadTime;
    //protected byte[] compareMask;

    public void setText(String msg){
        playTerminal.logLine(ScardLogHandler.LOG_INFO, "SCRIPT STATUS: " + msg);
    }

    private void init(StatusDisplayer statusDiplayer){
        checkOperation = true;
        errorOccured=false;
        userCancelled=false;
        reportUnsupportedError = true;
        checkConnection = true;
        checkConnectionDone=false;
        nRun=1;
        this.statusDiplayer=statusDiplayer;
        deadTime=0;
    }

    public ScriptPlayer() {
        init(this);
    }

    public ScriptPlayer(StatusDisplayer statusDiplayer) {
        init(statusDiplayer);
    }

    public long getDeadTime() {
        return deadTime;
    }

    public long getnRun() {
        return nRun;
    }

    public void setnRun(long nRun) {
        if(-1>nRun)
            throw new IllegalArgumentException("nRun must be >= -1 and <= "+Long.MAX_VALUE+", but argument="+nRun);
        this.nRun = nRun;
    }

    public boolean isCheckConnection() {
        return checkConnection;
    }

    public void setCheckConnection(boolean checkConnection) {
        this.checkConnection = checkConnection;
    }

    public boolean isUserCancelled() {
        return userCancelled;
    }

    public boolean isCheckOperation() {
        return checkOperation;
    }

    public void setCheckOperation(boolean checkOperation) {
        this.checkOperation = checkOperation;
    }

    public boolean isErrorOccured() {
        return errorOccured;
    }

    public void setErrorOccured(boolean errorOccured) {
        this.errorOccured = errorOccured;
    }

    Long lastApduTotalTime;
    public long getLastApduTotalTime(){
        return lastApduTotalTime;
    }

    public Apdu playApdu(GenericTerminal terminal, Apdu apdu) throws ScardException, StopRequestFromUserException, IOException, UnexpectedCardResponseException, CardNotPresentException{
        playTerminal=terminal;
        playApdu(apdu);
        return apdu;
    }
    public Apdu playApdu(Apdu apdu) throws ScardException, StopRequestFromUserException, IOException, UnexpectedCardResponseException, CardNotPresentException{
    /*    return playApdu(apdu,0);
    }
    public Apdu playApdu(Apdu apdu, int timeoutMs) throws ScardException, StopRequestFromUserException, IOException, UnexpectedCardResponseException, CardNotPresentException{
    */
        if(false==checkConnectionDone){
            checkConnectionDone=true;
            playTerminal.checkConnection();
        }
        long end=-1;
        long start=System.nanoTime();
        try {
            /*if(timeoutMs>0)
                playTerminal.sendApdu(apdu,timeoutMs);
            else*/
                playTerminal.sendApdu(apdu);
            end=System.nanoTime();
        } catch (UnexpectedCardResponseException e) {
            end=System.nanoTime();
            errorOccured=true;
            if (checkOperation) {
                playTerminal.logLine(ScardLogHandler.LOG_ERROR, apdu.getResponseApduComparisonErrorMessage(Integer.MAX_VALUE));
                String msg = "The card sent an unexpected response:"+nl+nl;
                msg += "Error message: " + e.getMessage() + nl+nl;
                msg += apdu.toString(64)+nl+nl;
                msg += "Do you want to continue to check card responses ? (press Cancel to stop sequence)";
                long startDead=System.nanoTime();
                int choice = JOptionPane.showConfirmDialog(null, msg, "Unexpected response from card", JOptionPane.YES_NO_CANCEL_OPTION);
                deadTime+=System.nanoTime()-startDead;
                if (JOptionPane.NO_OPTION == choice) {
                    checkOperation = false;
                }
                if (JOptionPane.CANCEL_OPTION == choice) {
                    userCancelled=true;
                    throw new StopRequestFromUserException("Operation cancelled by user.");
                }
            }
        }finally{
            if(-1==end)
                end=System.nanoTime();
            lastApduTotalTime=new Long(end-start);
        }
        return apdu;
    }

    class ApduTimeoutTask extends TimerTask{
        //final GenericTerminal terminal;
        public boolean expired=false;
        /*ApduTimeoutTask(GenericTerminal terminal){
            this.terminal=terminal;
        }*/
        @Override
        public void run() {
            errorOccured=true;
            expired=true;
            try {
                playTerminal.forceDisconnection();
            } catch (ScardException ex) {
                Logger.getLogger(ScriptPlayer.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    public void play(GenericTerminal terminal, List<? extends Object> cmds) throws ScardException, StopRequestFromUserException, IOException, UnexpectedCardResponseException, CardNotPresentException {
        if(null==cmds)
            return;
        playTerminal=terminal;
        userCancelled=false;
        buffers=new HashMap<Integer,Object>();
        Apdu lastApdu=null;
        AnswerToReset lastAtr=null;
        checkConnectionDone=!checkConnection;
        Stack<Integer> terminalTimeoutStack=new Stack<Integer>();

        runCnt=0;
        while((-1==nRun) || (runCnt<nRun)){
            StringBuilder sb=new StringBuilder("iteration ");
            sb.append(runCnt+1);
            statusDiplayer.setText(sb.toString());
            for (int i = 0; i < cmds.size(); i++) {

                if(Thread.interrupted())
                    throw new StopRequestFromUserException("Operation cancelled by user.");

                if (cmds.get(i).getClass().equals(Apdu.class)) {
                    lastApdu=playApdu((Apdu) cmds.get(i));
                    lastAtr=null;
                }else if (cmds.get(i).getClass().equals(Integer.class)) {
                    Integer bufferId = (Integer) cmds.get(i);
                    if(null!=lastApdu)
                        buffers.put(bufferId, lastApdu);
                    else if(null!=lastAtr)
                        buffers.put(bufferId, lastAtr);
                    else
                        throw new ScardException("'Save to buffer' invoked but no ATR nor APDU to save");
                } else if (cmds.get(i).getClass().equals(BufferOperation.class)) {
                    BufferOperation bufferOperation = (BufferOperation) cmds.get(i);
                    bufferOperation.perform(this);
                } else if (cmds.get(i).getClass().equals(Frequency.class)) {
                    Frequency frequency = (Frequency) cmds.get(i);
                    if(playTerminal instanceof FrequencySetter){
                        FrequencySetter fs=(FrequencySetter) playTerminal;
                        fs.setFrequency(frequency.frequency);
                    }else
                        terminal.logLine(ScardLogHandler.LOG_WARNING,"Frequency specified in script but frequency setting is not supported with this terminal");
                } else if (cmds.get(i).getClass().equals(PowerSupplyVoltage.class)) {
                    PowerSupplyVoltage power = (PowerSupplyVoltage) cmds.get(i);
                    if(playTerminal instanceof PowerSupplyVoltageSetter){
                        PowerSupplyVoltageSetter pss=(PowerSupplyVoltageSetter) playTerminal;
                        pss.setVoltage(power.voltage);
                    }else
                        terminal.logLine(ScardLogHandler.LOG_WARNING,"Voltage specified in script but voltage setting is not supported with this terminal");
                } else if (cmds.get(i).getClass().equals(PcscPowerOn.class)) {
                    PcscPowerOn pcscPowerOn = (PcscPowerOn) cmds.get(i);
                    terminal.setNegociateComSpeed(true);
                    terminal.connect(pcscPowerOn.protocol, pcscPowerOn.activation);
                    lastAtr=terminal.getAtr();
                    lastApdu=null;
                } else if (cmds.get(i).getClass().equals(PowerOn.class)) {
                    PowerOn powerOn = (PowerOn) cmds.get(i);
                    terminal.setNegociateComSpeed(false);
                    terminal.connect(powerOn.activation);
                    lastAtr=terminal.getAtr();
                    lastApdu=null;
                } else if (cmds.get(i).getClass().equals(PowerOff.class)) {
                    terminal.disconnect();
                } else if (cmds.get(i) instanceof CallBack) {
                    CallBack callBack = (CallBack) cmds.get(i);
                    callBack.execute(terminal);
                    lastAtr=terminal.getAtr();
                    lastApdu=null;
                } else if (cmds.get(i).getClass().equals(DelayNs.class)) {
                    DelayNs delayNs=(DelayNs)cmds.get(i);
                    terminal.delay(delayNs.delayNs);
                } else if (cmds.get(i).getClass().equals(ApduTimeoutPush.class)) {
                    ApduTimeoutPush timeout=(ApduTimeoutPush)cmds.get(i);
                    terminalTimeoutStack.push(terminal.getApduTimeout());
                    terminal.setApduTimeout(timeout.timeoutMs);
                } else if (cmds.get(i).getClass().equals(ApduTimeout.class)) {
                    ApduTimeout timeout=(ApduTimeout)cmds.get(i);
                    terminal.setApduTimeout(timeout.timeoutMs);
                } else if (cmds.get(i).getClass().equals(ApduTimeoutPop.class)) {
                    terminal.setApduTimeout(terminalTimeoutStack.pop());
                } else if (cmds.get(i).getClass().equals(String.class)) {
                    String comment = (String) cmds.get(i);
                    terminal.logLine(ScardLogHandler.LOG_COMMENT,comment);
                } else if (cmds.get(i).getClass().equals(ExpectedApduResponse.class)) {
                    ExpectedApduResponse expectedApduResponse = (ExpectedApduResponse) cmds.get(i);
                    buffers.put(expectedApduResponse.id, expectedApduResponse);
                } else {
                    if (reportUnsupportedError) {
                        errorOccured=true;
                        String msg = "Runtime error: object ";
                        msg += cmds.get(i).getClass().getCanonicalName();
                        msg += " not supported. Action not performed."+nl+nl;
                        msg += "Ignore that kind of error ? (press Cancel to stop sequence)";
                        long start=System.nanoTime();
                        int choice = JOptionPane.showConfirmDialog(null, msg, "Runtime error", JOptionPane.YES_NO_CANCEL_OPTION);
                        deadTime+=System.nanoTime()-start;
                        if (JOptionPane.YES_OPTION == choice) {
                            reportUnsupportedError = false;
                        }
                        if (JOptionPane.CANCEL_OPTION == choice) {
                            userCancelled=false;
                            throw new StopRequestFromUserException("Operation cancelled by user.");
                        }
                    }
                }
            }
            runCnt++;
        }
    }
}
TOP

Related Classes of uk.co.nimp.scard.ScriptPlayer$PowerOn

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.