smartcardx
Version:
Backend library for communication with smartcards using system native PCSC interface. Plain Iso7816 + EMV + GlobalPlatform functionality.
305 lines (304 loc) • 10.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.statusDecode = statusDecode;
const utils_1 = require("./utils");
const NORM_PROC = 'Normal processing';
const WARN_PROC = 'Warning processing';
const EXEC_ERR = 'Execution error';
const CHCK_ERR = 'Checking error';
const NVM_U = 'State of non-volatile memory is unchanged';
const NVM_C = 'State of non-volatile memory may have changed';
function unkSw2(sw2) {
return `Unknown SW2: [${(0, utils_1.hexEncode)([sw2])}]`;
}
const meanings = {
// NORMAL PROCESSING
'^9000$': (_) => {
return NORM_PROC;
},
'^61(.{2})$': (sw2) => {
return `${NORM_PROC}; ${sw2} response bytes still available`;
},
// WARNING PROCESSING
'^62(.{2})$': (sw2) => {
let msg = `${WARN_PROC}; ${NVM_U}`;
switch (true) {
case sw2 == 0x00:
msg += '; No further info';
break;
case sw2 >= 0x02 && sw2 <= 0x80:
msg += `; Card wants to send at least one more query of ${sw2} bytes`;
break;
case sw2 == 0x81:
msg += '; Part of returned data may be corrupted';
break;
case sw2 == 0x82:
msg +=
'; End of file or record reached before reading Ne bytes, or unsuccessful search';
break;
case sw2 == 0x83:
msg += '; Selected file deactivated';
break;
case sw2 == 0x84:
msg +=
'; File or data control information not formatted properly';
break;
case sw2 == 0x85:
msg += '; Selected file in termination state';
break;
case sw2 == 0x86:
msg += '; No input data available from a sensor on the card';
break;
case sw2 == 0x87:
msg +=
'; At least one of the referenced records is deactivated';
break;
default:
msg += `; ${unkSw2(sw2)}`;
break;
}
return msg;
},
'^63(.{2})$': (sw2) => {
let msg = `${WARN_PROC}; ${NVM_C}`;
switch (true) {
case sw2 == 0x00:
msg += '; No further info';
break;
case sw2 == 0x40:
msg +=
'; Unsuccessful comparison (exact meaning depends on the command)';
break;
case sw2 == 0x81:
msg += '; File filled up by the last write';
break;
case sw2 >= 0xc0 && sw2 <= 0xcf:
msg += `; Counter: ${sw2 & 0x0f} (exact meaning depends on the command)`;
break;
default:
msg += `; ${unkSw2(sw2)}`;
break;
}
return msg;
},
// EXECUTION ERROR
'^64(.{2})$': (sw2) => {
let msg = `${EXEC_ERR}; ${NVM_U}`;
switch (true) {
case sw2 == 0x00:
msg += '; No further info';
break;
case sw2 == 0x01:
msg += '; Immediate response required by the card';
break;
case sw2 >= 0x02 && sw2 <= 0x80:
msg += `; Card wants to send at least one more query of ${sw2} bytes`;
break;
case sw2 == 0x81:
msg += '; Logical channel shared access denied';
break;
case sw2 == 0x82:
msg += `; Logical channel opening denied`;
break;
default:
msg += `; ${unkSw2(sw2)}`;
break;
}
return msg;
},
'^65(.{2})$': (sw2) => {
let msg = `${EXEC_ERR}; ${NVM_C}`;
switch (true) {
case sw2 == 0x00:
msg += '; No further info';
break;
case sw2 == 0x81:
msg += '; Memory failure';
break;
default:
msg += `; ${unkSw2(sw2)}`;
break;
}
return msg;
},
'^66(.{2})$': (sw2) => {
let msg = `${EXEC_ERR}; Security-related issues`;
switch (true) {
case sw2 == 0x00:
msg += '; No further info';
break;
default:
msg += `; ${unkSw2(sw2)}`;
break;
}
return msg;
},
// CHECKING ERROR
'^67(.{2})$': (sw2) => {
let msg = `${CHCK_ERR}; Wrong length`;
switch (true) {
case sw2 == 0x00:
msg += '; No further info';
break;
case sw2 == 0x01:
msg += '; Command APDU format not compliant with standard';
break;
case sw2 == 0x02:
msg += '; The value of Lc is not the expected one';
break;
default:
msg += `; ${unkSw2(sw2)}`;
break;
}
return msg;
},
'^68(.{2})$': (sw2) => {
let msg = `${CHCK_ERR}; Functions in CLA not supported`;
switch (true) {
case sw2 == 0x00:
msg += '; No further info';
break;
case sw2 == 0x81:
msg += '; Logical channel not supported';
break;
case sw2 == 0x82:
msg += '; Secure messaging not supported';
break;
case sw2 == 0x83:
msg += '; Last command of the chain expected';
break;
case sw2 == 0x84:
msg += '; Command chaining not supported';
break;
default:
msg += `; ${unkSw2(sw2)}`;
break;
}
return msg;
},
'^69(.{2})$': (sw2) => {
let msg = `${CHCK_ERR}; Command not allowed`;
switch (true) {
case sw2 == 0x00:
msg += '; No further info';
break;
case sw2 == 0x81:
msg += '; Command incompatible with file structure';
break;
case sw2 == 0x82:
msg += '; Security status not satisfied';
break;
case sw2 == 0x83:
msg += '; Authentication method blocked';
break;
case sw2 == 0x84:
msg += '; Reference data not usable';
break;
case sw2 == 0x85:
msg += '; Conditions of use not satisfied';
break;
case sw2 == 0x86:
msg += '; Command not allowed (no current EF)';
break;
case sw2 == 0x87:
msg += '; Expected secure messaging DOs missing';
break;
case sw2 == 0x88:
msg += '; Incorrect secure messaging DOs';
break;
default:
msg += `; ${unkSw2(sw2)}`;
break;
}
return msg;
},
'^6[A,a](.{2})$': (sw2) => {
let msg = `${CHCK_ERR}; Wrong parameters P1-P2`;
switch (true) {
case sw2 == 0x00:
msg += '; No further info';
break;
case sw2 == 0x80:
msg += '; Incorrect parameters in the command data field';
break;
case sw2 == 0x81:
msg += '; Function not supported';
break;
case sw2 == 0x82:
msg += '; File or application not found';
break;
case sw2 == 0x83:
msg += '; Record not found';
break;
case sw2 == 0x84:
msg += '; Not enough memory space in the file';
break;
case sw2 == 0x85:
msg += '; Nc inconsistent with TLV structure';
break;
case sw2 == 0x86:
msg += '; Incorrect parameters P1-P2';
break;
case sw2 == 0x87:
msg += '; Nc inconsistent with parameters P1-P2';
break;
case sw2 == 0x88:
msg +=
'; Referenced data or reference data not found (exact meaning depends on the command)';
break;
case sw2 == 0x89:
msg += '; File already exists';
break;
case sw2 == 0x8a:
msg += '; DF name already exists';
break;
default:
msg += `; ${unkSw2(sw2)}`;
break;
}
return msg;
},
'^6[B,b]00$': (_) => {
return `${CHCK_ERR}; Wrong parameters P1-P2`;
},
'^6[C,c](.{2})$': (sw2) => {
return `${CHCK_ERR}; Wrong Le field; ${sw2} data bytes available`;
},
'^6[D,d]00$': (_) => {
return `${CHCK_ERR}; Instruction code not supported or invalid`;
},
'^6[E,e]00$': (_) => {
return `${CHCK_ERR}; Class not supported`;
},
'^6[F,f]00$': (_) => {
return `${CHCK_ERR}; No precise diagnosis`;
},
};
const regexStrList = Object.keys(meanings);
const compiledRegexList = new Array(regexStrList.length);
regexStrList.reduce((_, regexStr, idx) => {
compiledRegexList[idx] = new RegExp(regexStr);
return null;
}, null);
/** Tries to decode 2-byte status and returns it's meaning. */
function statusDecode(status) {
let statusByteArray;
try {
statusByteArray = (0, utils_1.importBinData)(status);
}
catch (error) {
return `Could not decode status: ${error.message}`;
}
const statusHex = (0, utils_1.hexEncode)(statusByteArray);
let meaning = `Unknown status: [${statusHex}]`;
if (statusByteArray.byteLength === 2) {
for (let i = 0; i < compiledRegexList.length; i++) {
if (compiledRegexList[i].exec(statusHex)) {
meaning = meanings[regexStrList[i]](statusByteArray[1]);
break;
}
}
}
return meaning;
}
exports.default = statusDecode;