Package org.nfctools.spi.acs

Source Code of org.nfctools.spi.acs.AcsReaderWriter

/**
* Copyright 2011-2012 Adrian Stabiszewski, as@nfctools.org
*
* 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 org.nfctools.spi.acs;

import java.io.IOException;

import javax.smartcardio.CardChannel;
import javax.smartcardio.CardException;
import javax.smartcardio.CardTerminal;
import javax.smartcardio.CommandAPDU;
import javax.smartcardio.ResponseAPDU;

import org.nfctools.io.NfcDevice;
import org.nfctools.mf.MfAccess;
import org.nfctools.mf.MfCardListener;
import org.nfctools.mf.MfException;
import org.nfctools.mf.MfLoginException;
import org.nfctools.mf.MfReaderWriter;
import org.nfctools.mf.block.Block;
import org.nfctools.mf.block.BlockResolver;
import org.nfctools.mf.block.MfBlock;
import org.nfctools.mf.block.TrailerBlock;
import org.nfctools.mf.card.MfCard;
import org.nfctools.mf.classic.Key;
import org.nfctools.scio.CardTerminalToken;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public abstract class AcsReaderWriter implements MfReaderWriter {

  protected Logger log = LoggerFactory.getLogger(getClass());
  protected BlockResolver blockResolver = new BlockResolver();

  protected CardTerminal cardTerminal;
  private Thread pollingThread = null;

  protected AcsReaderWriter(NfcDevice nfcDevice) {
    if (nfcDevice.getConnectionToken() instanceof CardTerminalToken)
      this.cardTerminal = ((CardTerminalToken)nfcDevice.getConnectionToken()).getCardTerminal();
    else
      throw new IllegalArgumentException("unsupported connection token");
  }

  @Override
  public void setCardIntoHalt(MfCard card) throws IOException {
    try {
      AcsConnectionToken connectionToken = (AcsConnectionToken)card.getConnectionToken();
      connectionToken.getCard().disconnect(true);
    }
    catch (CardException e) {
      throw new IOException(e);
    }
  }

  @Override
  public void reselectCard(MfCard card) throws IOException {
  }

  @Override
  public MfBlock[] readBlock(MfAccess mfAccess) throws IOException {

    AcsConnectionToken connectionToken = retrieveConnectionToken(mfAccess);
    CardChannel cardChannel = connectionToken.getCard().getBasicChannel();

    byte blockNumber;
    loginIntoSector(mfAccess, cardChannel);

    MfBlock[] returnBlocks = new Block[mfAccess.getBlocksToRead()];

    for (int currentBlock = 0; currentBlock < mfAccess.getBlocksToRead(); currentBlock++) {

      blockNumber = (byte)mfAccess.getCard().getBlockNumber(mfAccess.getSector(),
          mfAccess.getBlock() + currentBlock);

      CommandAPDU readBlock = new CommandAPDU(Apdu.CLS_PTS, Apdu.INS_READ_BINARY, 0x00, blockNumber, 16);
      ResponseAPDU readBlockResponse;
      try {
        readBlockResponse = cardChannel.transmit(readBlock);
        if (!ApduUtils.isSuccess(readBlockResponse)) {
          throw new MfException("Reading block failed. Sector: " + mfAccess.getSector() + ", Block: "
              + mfAccess.getBlock() + " Key: " + mfAccess.getKey().name() + ", Response: "
              + readBlockResponse);
        }

      }
      catch (CardException e) {
        throw new IOException(e);
      }

      returnBlocks[currentBlock] = blockResolver.resolveBlock(mfAccess.getCard(), mfAccess.getSector(),
          currentBlock + mfAccess.getBlock(), readBlockResponse.getData());

    }
    return returnBlocks;
  }

  protected abstract void loginIntoSector(MfAccess mfAccess, CardChannel cardChannel) throws IOException;

  protected void loginIntoSector(MfAccess mfAccess, CardChannel cardChannel, byte memoryKeyId) throws IOException {
    try {
      CommandAPDU loadKey = new CommandAPDU(Apdu.CLS_PTS, Apdu.INS_EXTERNAL_AUTHENTICATE,
          Acs.P1_LOAD_KEY_INTO_VOLATILE_MEM, memoryKeyId, mfAccess.getKeyValue());
      ResponseAPDU loadKeyResponse = cardChannel.transmit(loadKey);
      if (!ApduUtils.isSuccess(loadKeyResponse)) {
        throw new MfLoginException("Loading key failed. Sector: " + mfAccess.getSector() + ", Block: "
            + mfAccess.getBlock() + " Key: " + mfAccess.getKey().name() + ", Response: " + loadKeyResponse);
      }

      byte blockNumber = (byte)mfAccess.getCard().getBlockNumber(mfAccess.getSector(), mfAccess.getBlock());

      byte keyTypeToUse = mfAccess.getKey() == Key.A ? Acs.KEY_A : Acs.KEY_B;

      CommandAPDU auth = new CommandAPDU(Apdu.CLS_PTS, Apdu.INS_INTERNAL_AUTHENTICATE_ACS, 0, 0, new byte[] {
          0x01, 0x00, blockNumber, keyTypeToUse, memoryKeyId });
      ResponseAPDU authResponse = cardChannel.transmit(auth);
      if (!ApduUtils.isSuccess(authResponse)) {
        throw new MfLoginException("Login failed. Sector: " + mfAccess.getSector() + ", Block: "
            + mfAccess.getBlock() + " Key: " + mfAccess.getKey().name() + ", Response: " + authResponse);
      }
    }
    catch (CardException e) {
      throw new IOException(e);
    }
  }

  private AcsConnectionToken retrieveConnectionToken(MfAccess mfAccess) {
    return (AcsConnectionToken)mfAccess.getCard().getConnectionToken();
  }

  @Override
  public void writeBlock(MfAccess mfAccess, MfBlock... mfBlock) throws IOException {
    AcsConnectionToken connectionToken = retrieveConnectionToken(mfAccess);
    CardChannel cardChannel = connectionToken.getCard().getBasicChannel();

    loginIntoSector(mfAccess, cardChannel);

    for (int currentBlock = 0; currentBlock < mfBlock.length; currentBlock++) {
      int blockNumber = mfAccess.getCard().getBlockNumber(mfAccess.getSector(), mfAccess.getBlock())
          + currentBlock;

      if (mfAccess.getCard().isTrailerBlock(mfAccess.getSector(), mfAccess.getBlock() + currentBlock)) {
        if (!(mfBlock[currentBlock] instanceof TrailerBlock))
          throw new MfException("invalid block for trailer");
      }

      CommandAPDU writeBlock = new CommandAPDU(Apdu.CLS_PTS, Apdu.INS_UPDATE_BINARY, 0x00, blockNumber,
          mfBlock[currentBlock].getData());
      ResponseAPDU writeBlockResponse;
      try {
        writeBlockResponse = cardChannel.transmit(writeBlock);
        if (!ApduUtils.isSuccess(writeBlockResponse)) {
          throw new MfException("Writing block failed. Sector: " + mfAccess.getSector() + ", Block: "
              + mfAccess.getBlock() + " Key: " + mfAccess.getKey().name() + ", Response: "
              + writeBlockResponse);
        }

      }
      catch (CardException e) {
        throw new IOException(e);
      }
    }
  }

  @Override
  public void setCardListener(MfCardListener cardListener) throws IOException {
    pollingThread = new Thread(new PollingCardScanner(cardTerminal, cardListener, this));
    pollingThread.setDaemon(false);
    log.debug("Starting new thread " + pollingThread.getName());
    pollingThread.start();
  }

  @Override
  public boolean waitForCard(MfCardListener mfCardListener, int timeout) throws IOException {
    removeCardListener();
    try {
      return new PollingCardScanner(cardTerminal, mfCardListener, this).waitForCard(timeout);
    }
    catch (CardException e) {
      throw new IOException(e);
    }
    catch (InterruptedException e) {
      throw new IOException(e);
    }
  }

  @Override
  public void removeCardListener() {
    if (pollingThread != null && pollingThread.isAlive()) {
      pollingThread.interrupt();
    }
  }
}
TOP

Related Classes of org.nfctools.spi.acs.AcsReaderWriter

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.