/**************************************************************************** * Copyright (C) 2012 ecsec GmbH. * 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.common.apdu.common; import iso.std.iso_iec._24727.tech.schema.TransmitResponse; import java.io.ByteArrayOutputStream; import java.util.Arrays; import java.util.List; import org.openecard.common.util.ByteUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Implements a response APDU. * See ISO/IEC 7816-4 Section 5.1. * * @author Moritz Horsch <horsch@cdc.informatik.tu-darmstadt.de> */ public final class CardResponseAPDU extends CardAPDU { private static final Logger logger = LoggerFactory.getLogger(CardResponseAPDU.class); private byte[] trailer; /** * Creates a new response APDU. */ public CardResponseAPDU() { trailer = new byte[2]; } /** * Creates a new response APDU. * * @param responseAPDU Response APDU */ public CardResponseAPDU(byte[] responseAPDU) { trailer = new byte[2]; data = new byte[responseAPDU.length - 2]; System.arraycopy(responseAPDU, 0, data, 0, responseAPDU.length - 2); System.arraycopy(responseAPDU, responseAPDU.length - 2, trailer, 0, 2); } /** * Creates a new response APDU. * * @param data Data field * @param trailer Trailer (SW1, SW2) */ public CardResponseAPDU(byte[] data, byte[] trailer) { setData(data); setTrailer(trailer); } /** * Creates a new response APDU. * * @param transmitResponse TransmitResponse */ public CardResponseAPDU(TransmitResponse transmitResponse) { this(transmitResponse.getOutputAPDU().get(0)); } /** * Returns the data field of the APDU. * * @param responseAPDU Response APDU * @return Data field of the APDU */ public static byte[] getData(byte[] responseAPDU) { if (responseAPDU.length < 2) { throw new IllegalArgumentException("Malformed APDU"); } return ByteUtils.copy(responseAPDU, 0, responseAPDU.length - 2); } /** * Sets the trailer (status bytes) of the APDU. * * @param trailer Trailer (SW1, SW2) */ public void setTrailer(byte[] trailer) { setTrailer(trailer); } /** * Returns the trailer (status bytes) of the APDU. * * @return Trailer (SW1, SW2) */ public byte[] getTrailer() { return trailer; } /** * Returns the trailer (status bytes) of the APDU. * * @param responseAPDU Response APDU * @return Trailer of the APDU */ public static byte[] getTrailer(byte[] responseAPDU) { if (responseAPDU.length < 2) { throw new IllegalArgumentException("Malformed APDU"); } return ByteUtils.copy(responseAPDU, responseAPDU.length - 2, 2); } /** * Returns the status byte SW1. * * @return SW1 */ public byte getSW1() { return (byte) (trailer[0] & 0xFF); } /** * Sets the status byte SW1. * * @param sw1 SW1 */ protected void setSW1(byte sw1) { trailer[0] = sw1; } /** * Returns the status byte SW1. * * @return SW2 */ public byte getSW2() { return (byte) (trailer[1] & 0xFF); } /** * Sets the status byte SW2. * * @param sw2 SW2 */ protected void setSW2(byte sw2) { trailer[1] = sw2; } /** * Returns the status bytes of the APDU. * * @return Status bytes */ public short getSW() { return (short) (((trailer[0] & 0xFF) << 8) | (trailer[1] & 0xFF)); } /** * Returns the status bytes of the APDU. * * @return Status bytes */ public byte[] getStatusBytes() { return getTrailer(); } /** * Returns the status message of the APDU. * * @return Status bytes */ public String getStatusMessage() { return CardCommandStatus.getMessage(getTrailer()); } /** * Sets the status bytes of the APDU. * * @param statusbytes Status bytes */ protected void setStatusBytes(byte[] statusbytes) { setTrailer(statusbytes); } /** * Checks if the status bytes indicates an normal processing. * See ISO/IEC 7816-4 Section 5.1.3 * * @return True if SW = 0x9000, otherwise false */ public boolean isNormalProcessed() { if (Arrays.equals(trailer, new byte[]{(byte) 0x90, (byte) 0x00})) { return true; } else { return false; } } /** * Checks if the status bytes indicates a warning processing. * See ISO/IEC 7816-4 Section 5.1.3 * * @return True if SW = 0x62XX or 0x63XX, otherwise false */ public boolean isWarningProcessed() { if (trailer[0] == (byte) 0x62 || trailer[0] == (byte) 0x63) { return true; } else { return false; } } /** * Checks if the status bytes indicates an execution error. * See ISO/IEC 7816-4 Section 5.1.3 * * @return True if SW = 0x64XX to 0x66XX, otherwise false */ public boolean isExecutionError() { if (trailer[0] == (byte) 0x64 || trailer[0] == (byte) 0x65 || trailer[0] == (byte) 0x66) { return true; } else { return false; } } /** * Checks if the status bytes indicates an checking error. * See ISO/IEC 7816-4 Section 5.1.3 * * @return True if SW = 0x67XX to 0x6FXX, otherwise false */ public boolean isCheckingError() { if ((trailer[0] & 0xF0) == (byte) 0x60) { for (byte b = 0x07; b < 0x0F; b++) { if ((trailer[0] & 0x0F) == b) { return true; } } } return false; } /** * Checks if the status bytes equals to a element of the list of positive responses. * * @param responses Positive responses * @return True if the status bytes equals to a element of the list of positive responses, otherwise false */ public boolean isPositiveResponse(List<byte[]> responses) { for (int i = 0; i < responses.size(); i++) { if (Arrays.equals(responses.get(i), trailer)) { return true; } } return false; } /** * Returns the byte encoded APDU: TRAILER | DATA * * @return Encoded APDU */ public byte[] toByteArray() { ByteArrayOutputStream baos = new ByteArrayOutputStream(data.length + 2); try { baos.write(trailer); baos.write(data); } catch (Exception e) { logger.error("Exception", e); } return baos.toByteArray(); } /** * Returns the bytes of the APDU as a hex encoded string. * * @return Hex encoded string of the APDU */ public String toHexString() { return ByteUtils.toHexString(toByteArray()); } /** * Returns the bytes of the APDU as a hex encoded string. * * @return Hex encoded string of the APDU */ @Override public String toString() { return ByteUtils.toHexString(toByteArray(), true); } }