/****************************************************************************
* 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 java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.openecard.common.util.ByteUtils;
import org.openecard.common.util.ShortUtils;
/**
* Resolves RAPDU status words to human readable messages.
* Cp. ISO-7816-4 sec. 5.1.3 Status Bytes
*
* @author Tobias Wich <tobias.wich@ecsec.de>
*/
public class CardCommandStatus {
private static final String defaultMsg = "Unknown status word (possibly proprietary).";
private static final String sw62 = "State of non-volatile memory unchanged.";
private static final String sw63 = "State of non-volatile memory changed.";
private static final String sw64 = "State of non-volatile memory unchanged.";
private static final String sw65 = "State of non-volatile memory changed.";
private static final String sw66 = "Security related issues.";
private static final String sw67 = "Wrong length without further indication.";
private static final String sw68 = "Functions in CLA not supported.";
private static final String sw69 = "Command not allowed.";
private static final String sw6A = "Wrong parameters P1-P2.";
private static final String sw6B = "Wrong parameters P1-P2.";
private static final String sw6C = "Wrong length field.";
private static final String sw6D = "Instruction code not supported or invalid.";
private static final String sw6E = "Class not supported.";
private static final String sw6F = "No precise diagnosis.";
public static String getMessage(byte[] status) {
String msg = defaultMsg;
switch (status[0]) {
case (byte) 0x90:
switch (status[1]) {
case 0x00: msg = "No error."; break;
}
break;
case 0x61: msg = "No error, but " + (0xFF & status[1]) + " bytes left to read."; break;
case 0x62:
switch (status[1]) {
case (byte) 0x00: msg = sw62 + " No further information given."; break;
case (byte) 0x81: msg = sw62 + " Part of returned data may be corrupted."; break;
case (byte) 0x82: msg = sw62 + " End of file reached before reading requested number of bytes."; break;
case (byte) 0x83: msg = sw62 + " Selected file deactivated."; break;
case (byte) 0x84: msg = sw62 + " File control information not formatted according to ISO/IEC 7816-4."; break;
case (byte) 0x85: msg = sw62 + " Selected file in termination state."; break;
case (byte) 0x86: msg = sw62 + " No input data available from a sensor on the card."; break;
default:
if (status[1] >= 0x02 && status[1] <= 0x80) {
msg = sw62 + " Triggering by the card (see ISO/IEC 7816-4 8.6.1).";
}
}
break;
case 0x63:
switch (status[1]) {
case (byte) 0x00: msg = sw63 + " No further information given."; break;
case (byte) 0x81: msg = sw63 + " File filled up by last write."; break;
default:
if (status[1] >= 0xC0 && status[1] <= 0xCF) {
msg = sw63 + " Counter is " + (0x0F & status[1]) + ".";
}
}
break;
case 0x64:
switch (status[1]) {
case (byte) 0x00: msg = sw64 + " Execution error."; break;
case (byte) 0x01: msg = sw64 + " Immediate response required by the card."; break;
default:
if (status[1] >= 0x02 && status[1] <= 0x80) {
msg = sw64 + " Triggered by the card (see ISO/IEC 7816-4 8.6.1).";
}
}
break;
case 0x65:
switch (status[1]) {
case (byte) 0x00: msg = sw65 + " No information given."; break;
case (byte) 0x81: msg = sw65 + " Memory failure."; break;
}
break;
case 0x66: msg = sw66; break;
case 0x67:
switch (status[1]) {
case (byte) 0x00: msg = sw67; break;
}
break;
case 0x68:
switch (status[1]) {
case (byte) 0x00: msg = sw68 + " No information given."; break;
case (byte) 0x81: msg = sw68 + " Logical channel not supported."; break;
case (byte) 0x82: msg = sw68 + " Secure messaging not supported."; break;
case (byte) 0x83: msg = sw68 + " Last command of the chain expected."; break;
case (byte) 0x84: msg = sw68 + " Command chaining not supported."; break;
}
break;
case 0x69:
switch (status[1]) {
case (byte) 0x00: msg = sw69 + " No information given."; break;
case (byte) 0x81: msg = sw69 + " Command incompatible with file structure."; break;
case (byte) 0x82: msg = sw69 + " Security status not satisfied."; break;
case (byte) 0x83: msg = sw69 + " Authentication method blocked."; break;
case (byte) 0x84: msg = sw69 + " Reference data not usable."; break;
case (byte) 0x85: msg = sw69 + " Conditions of use not satisfied."; break;
case (byte) 0x86: msg = sw69 + " Command not allowed (no current EF)."; break;
case (byte) 0x87: msg = sw69 + " Expected secure messaging data objects missing."; break;
case (byte) 0x88: msg = sw69 + " Incorrect secure messaging data objects."; break;
}
break;
case 0x6A:
switch (status[1]) {
case (byte) 0x00: msg = sw6A + " No information given."; break;
case (byte) 0x80: msg = sw6A + " Incorrect parameters in the command data field."; break;
case (byte) 0x81: msg = sw6A + " Function not supported."; break;
case (byte) 0x82: msg = sw6A + " File or application not found."; break;
case (byte) 0x83: msg = sw6A + " Record not found."; break;
case (byte) 0x84: msg = sw6A + " Not enough memory space in the file."; break;
case (byte) 0x85: msg = sw6A + " Command length inconsistent with TLV structure."; break;
case (byte) 0x86: msg = sw6A; break; // detailed message has no different spec than base message
case (byte) 0x87: msg = sw6A + " Command length inconsistent with parameters P1-P2."; break;
case (byte) 0x88: msg = sw6A + " Referenced data or reference data not found."; break;
case (byte) 0x89: msg = sw6A + " File already exists."; break;
case (byte) 0x8A: msg = sw6A + " DF name already exists."; break;
}
break;
case (byte) 0x6B:
switch (status[1]) {
case 0x00: msg = sw6B; break;
}
break;
case (byte) 0x6C: msg = sw6C + " " + (0xFF & status[1]) + " bytes available."; break;
case (byte)0x6D:
switch (status[1]) {
case 0x00: msg = sw6D; break;
}
break;
case (byte) 0x6E:
switch (status[1]) {
case 0x00: msg = sw6E; break;
}
break;
case (byte) 0x6F:
switch (status[1]) {
case 0x00: msg = sw6F; break;
}
break;
}
// append status code
msg += " (Code: " + ByteUtils.toHexString(status) + ")";
return msg;
}
public static byte[] ok() {
return new byte[] { (byte) 0x90, (byte) 0x00 };
}
public static List<byte[]> responseOk() {
return response(ok());
}
public static List<byte[]> response(byte[]... expected) {
ArrayList<byte[]> result = new ArrayList<byte[]>(expected.length);
result.addAll(Arrays.asList(expected));
return result;
}
public static List<byte[]> response(int... expected) {
byte[][] conv = new byte[expected.length][];
for (int i = 0; i < expected.length; i++) {
conv[i] = ShortUtils.toByteArray((short) expected[i], true);
}
return response(conv);
}
}