/*! @file @id $Id: cardos.hxx 3826 2012-05-21 12:25:29Z marc $ */ // 1 2 3 4 5 6 7 8 // 45678901234567890123456789012345678901234567890123456789012345678901234567890 #ifndef __CARDOS_HXX__ #define __CARDOS_HXX__ #include #include #include #include #include #ifndef CARDOS_LOG # ifdef DEBUG_SECRETS # define CARDOS_LOG(X) std::clog<ISO 7816-4 */ //@{ /// @defgroup cardosexception CardOS Exceptions /// @defgroup cardostypes CardOS Types /// @defgroup cardoslib CardOS Library //@} /// @ref gcardos @copydoc gcardos namespace cardos { /// @addtogroup cardosexception CardOS Exceptions //@{ //============================================================================ //---------------------------------------------------------------------------- class exception: public pcsc::exception { public: exception(const std::string& reason) noexcept: pcsc::exception("cardos: "+reason) { } }; //---------------------------------------------------------------------------- class wrong_pin: public exception { public: wrong_pin(const std::string& s) noexcept: exception("wrong pin "+s) {} }; //---------------------------------------------------------------------------- class pin_locked: public exception { public: pin_locked() noexcept: exception("pin is locked and cannot be changed") {} }; //---------------------------------------------------------------------------- class wrong_puk: public wrong_pin { public: wrong_puk(const std::string& s) noexcept: wrong_pin("wrong puk "+s) {} }; //---------------------------------------------------------------------------- class wrong_result: public exception { public: wrong_result(const std::string& reason, const std::string& data) noexcept: exception("wrong result, "+reason+": "+crypto::hex(data)) {} wrong_result(const std::string& reason) noexcept: exception("wrong result, "+reason) {} }; //---------------------------------------------------------------------------- class runtime_error: public exception { public: runtime_error(const std::string& reason) noexcept: exception("runtime error, "+reason) {} runtime_error(const std::string& reason, const std::string& data) noexcept: exception("runtime error, "+reason+": "+crypto::hex(data)) {} }; //---------------------------------------------------------------------------- class unexpected_challenge_length: public exception { public: unexpected_challenge_length(const std::string& data) noexcept: exception("challenge should be 8 bytes, challenge is: " +crypto::hex(data)) {} }; //---------------------------------------------------------------------------- class card_transmission_failed: public exception { public: card_transmission_failed(const std::string& position, const std::string& reason) noexcept: exception("transmission to card failed: "+position +" reason: "+reason) { } }; //---------------------------------------------------------------------------- class wrong_dataformat: public exception { public: wrong_dataformat(const std::string& data, const std::string& reason) noexcept: exception("wrong dataformat: "+crypto::hex(data) +" reason: "+reason) { } }; //---------------------------------------------------------------------------- class too_large_for_tlv: public wrong_dataformat { public: too_large_for_tlv(const std::string& data) noexcept: wrong_dataformat(data, "data size too long for TLV") { } }; //---------------------------------------------------------------------------- class array_range: public exception { public: array_range(unsigned long i, unsigned long j) noexcept: exception("array index out of range: "+mrw::string(i) +"; length: "+mrw::string(j)) { } }; //@} //============================================================================ /// @addtogroup cardoslib //@{ class BerValue { public: enum Class { UNIVERSAL = 0x00, APPLICATION = 0x40, CONTEXT_SPECIFIC = 0x80, PRIVATE = 0xC0 }; enum PC { PRIMITIVE = 0x00, CONSTRUCTED = 0x20 }; enum Type { END_OF_CONTENT = 0x00, BOOLEAN = 0x01, INTEGER = 0x02, BIT_STRING = 0x03, OCTET_STRING = 0x04, EMPTY = 0x05, OBJECT_IDENTIFIER = 0x06, OBJECT_DESCRIPTOR = 0x07, EXTERNAL = 0x08, REAL = 0x09, ENUMERATED = 0x0A, EMBEDDED_PDV = 0x0B, UTF8_STRING = 0x0C, RELATIVE_OID = 0x0D, SEQUENCE = 0x10, SET = 0x11, NUMERIC_STRING = 0x12, PRINTABLE_STRING = 0x13, T61_STRING = 0x14, VIDEOTEX_STRING = 0x15, IA5_STRING = 0x16, UTC_TIME = 0x17, GENERALIZED_TIME = 0x18, GRAPHIC_STRING = 0x19, VISIBLE_STRING = 0x1A, GENERAL_STRING = 0x1B, UNIVERSAL_STRING = 0x1C, CHARACTER_STRING = 0x1D, BMP_STRING = 0x1E, }; protected: // use BerValues instead friend class BerValues; BerValue(const std::vector& sequence): _tag(PRIVATE|CONSTRUCTED|SEQUENCE), _sequence(sequence) { } BerValue(std::string& content) { set(content); } void set(std::string& content) { if (content.size()<2) throw wrong_dataformat(content, "not a BER, header size too small: \"" +crypto::binToHex(content)+"\""); _tag = content[0]; unsigned char length = content[1]; _value = content.substr(2, length); if (content.size()& vs): _tag(t), _sequence(vs) { if (!isContainer()) throw runtime_error("BER tag 0x"+crypto::binToHex(_tag) +" is not a container"); } unsigned char tagClass() { return _tag&0xC0; } unsigned char tagPC() { return _tag&0x20; } unsigned char tagType() { return _tag&0x1F; } bool isContainer() { return tagPC()==CONSTRUCTED; } std::vector::size_type size() { return _sequence.size(); } unsigned char tag() { return _tag; } BerValue operator[](std::vector::size_type i) { if (i>=_sequence.size()) throw array_range(i, _sequence.size()); return _sequence[i]; } std::string binary() { std::string res; res.push_back(_tag); if (isContainer()) { std::string seq; for (std::vector::iterator it(_sequence.begin()); it!=_sequence.end(); ++it) { seq += it->binary(); } if (seq.size()>255) throw too_large_for_tlv(seq); res += (char)seq.size(); res += seq; } else { if (_value.size()>255) throw too_large_for_tlv(_value); res += (char)_value.size(); res += _value; } return res; } std::string string() { return _value; } std::string hex() { return crypto::hex(_value); } unsigned long ulong() { return crypto::ulongFromBinary(_value); } std::string print(int indent=0, int indentStep = 4) { std::stringstream ss; ss<print(indent+2, indentStep) < _sequence; }; //============================================================================ /// Store a sequence of BerValue class BerValues: public std::vector { public: BerValues() {} BerValues(const std::string& content) { std::string contentCopy(content); while (contentCopy.size()) push_back(BerValue(contentCopy)); } BerValues& operator=(std::string& content) { clear(); std::string contentCopy(content); while (contentCopy.size()) push_back(BerValue(contentCopy)); return *this; } BerValues& operator+=(const std::string& content) { std::string contentCopy(content); while (contentCopy.size()) push_back(BerValue(contentCopy)); return *this; } BerValues& operator+=(const BerValue& value) { push_back(value); return *this; } BerValues& operator+=(const BerValues& values) { insert(end(), values.begin(), values.end()); return *this; } std::string binary() { std::string res; for (BerValues::iterator it(begin()); it!=end(); ++it) res += it->binary(); return res; } std::string print(int indent=0, int indentStep = 4) { std::stringstream ss; if (size()==1) { ss<print(indent+2, indentStep) < r): _reader(r) { } /// Set smart card reader. void reader(std::shared_ptr r) { _reader = r; } //@} //------------------------------------------------------------------------ /// @name Basic CardOS Commands //@{ //! Activates a deactivated file or file tree. /*! This command reactivates the current file in the file system. If there were any child files (file tree), they can subsequently be selected and thus used again. The command will not return an error, if the current file is already active. @pre The command can only be executed, if the right referenced by the file’s AC ACTIVATE is granted in the security status of the current DF. The command cannot be applied to CODE files. */ void activateFile() { CRYPTOLOG("log"); check(send(0x00, 0x44, 0x00, 0x00)); } //! Manages the transaction buffer for command transactions //! and/or command sequence transactions /*! The command allocates or frees a transaction buffer In case of mode Allocate a possibly existing old buffer block is marked as unused and a new block with size HI-LO is initialized. The ID of the new buffer is returned as response. After the command has been executed successfully, all the EEPROM contents, which will be affected by a command with the setting AutoTR=ON, will be stored in the allocated EEPROM buffer, so that in case of an unforeseen interruption the former EEPROM state can be restored after the next reset (see also SET TRANSACTION STATE command). In case of mode Free, the buffer with the ID in P2 will not be used anymore. The recommended buffer size is 300 bytes. A small buffer will increase the EEPROM stress. Buffers should be allocated and freed again by the application to reduce the EEPROM stress.
Bytes P1-P2
P1 (MODE)P2Meaning
Bit 7Bits 6 – 0
1HI valueLO value Allocate buffer with size HI-LO
0rfuID Free buffer with ID in P2
@pre The command can only be executed, if the right referenced by the MF’s AC ALLOCATE is granted. @returns 1 byte: ID of allocated buffer @see freeTransactionBuffer */ std::string allocateTransactionBuffer(unsigned short size) { CRYPTOLOG("log"); if (size>0x7FFF) throw runtime_error("requested buffer too large"); return check(send(0x80, 0x12, (unsigned char)(8|size>>8), (unsigned char)(size&0xFF))); } //! Free transaction buffer /*! @see allocateTransactionBuffer */ void freeTransactionBuffer(unsigned char id) { CRYPTOLOG("log"); check(send(0x80, 0x12, 0x00, id)); } //! Creates a new record in a record oriented file /*! This command creates a new record in the currently selected file, or, if P2 is not 00h, in the file, which is referenced by the Short File Identifier (SFI). The command can only be used on LINEAR FIXED, CYCLIC FIXED and LINEAR TLV files. In the parameter EF-ID a file ID can be specified with an SFI. See table 3.4-1 for its format. The thus referenced file will then be selected. EF-ID Byte P2: - 0x00 → use current EF - bits 7-3 = ppppp, bits 2-0=0 → use SFI ppppp (11111 not allowed) - other value → rfu If an SFI is present the actual FID to be used is built by adding the constant value FE00h to the specified SFI. The Record_Data contain the contents of the new record. The length of the Record_Data must not be more than 254 bytes. For LINEAR FIXED or CYCLIC FIXED files the length of the Record_Data must match the length, which was specified by the belonging CREATE FILE command. For LINEAR TLV files the command data field must have a valid TLV format. If there is not sufficient file memory to store the new record, an error message is generated for all files, except for a CYCLIC FIXED file, where the previous record will be overwritten (same behavior as UPDATE RECORD in PREV mode). For all of file types the newly written record becomes the current record. For CYCLIC FIXED files the newly written record becomes the logical first record. For LINEAR TLV files it is assumed that the length field of the record consists of one byte. The tag byte is not interpreted by APPEND RECORD. @pre The command can only be executed, if the right referenced by the file’s AC APPEND is granted in the security status of the current DF. */ void appendRecord(unsigned char efId, std::string data) { CRYPTOLOG("log"); check(send(0x00, 0xE2, 0x00, efId, data)); } //! Computes a digital signature of internally hashed data //! provided by the application and the card itself in order to //! authenticate the card to the application. /*! The CARD AUTHENTICATE command allows to check a card’s authenticity before personalization takes place. It returns a digital signature, which is computed over an internal hash value. Inputs for the hash value calculation are: - The System_Challenge (s. Command Data Field) - the command header of the CARD AUTHENTICATE command (CLA, INS, P1, P2) - the global life cycle phase (s. GET DATA command, mode 83h) - the number of loaded Packages (s. GET DATA command, mode 88h) - the system internal parameters (9 bytes consisting 22h) and - optionally, the Chip Unique Identification Number (as indicated in mode byte P1, @ref getData, mode 81h, Bytes 11-16). The commands supports 2 modes indicated by the parameter P1: - 0x01: The Chip Unique Identification Number is not used for the calculation of the hash value - 0x02: Include the Chip Unique Identification Number for the calculation of hash value The mode 01h can be used to compute a constant signature for different cards of the same CardOS version, depending on their global life cycle phase and loaded system packages that use the system internal data The command uses a 1536-bit private RSA2_SIG_SHA-1 key stored in ROM, see chapter 2.4.2.4
Format of the input for hash value calculation
System_Challenge Command Header
CLA INS P1 P2
Global Life Cycle Phase Internal Parameters Number of loaded Packages Chip Unique Identification Number (opt.)
xxh…xxh 80h 88h Mode 00h xxh 22h…22h xxh xxh…xxh
n bytes 4 bytes 1 byte 9 bytes 1 byte 6 bytes/empty
@pre The System_Challenge provided by the application must be greater or equal to 16 bytes. Since the command does not support chaining, the length of the System_Challenge is limited by the IO buffer size. @note The application must use the corresponding RSA2 public key to verify the Digital Signature received from the card and thus establishes the card’s authenticity. The RSA2 public key is contained in the CardOS V4.4 PRNs. @return Digital Signature */ std::string cardAuthenticate(bool hashInclIdentNum, std::string systemChallange) { CRYPTOLOG("log"); return check(send(0x80, 0x88, hashInclIdentNum?0x01:0x02, 0x00, systemChallange)); } //! Changes the object data of any BS object except for PIN TEST objects void changeKeyData() { CRYPTOLOG("log"); assert(!"not implemented"); } //! Changes the object data of a PIN TEST object /** Changes a PIN. */ void changeReferenceData(unsigned char id, std::string newData, std::string oldData=std::string()) { CRYPTOLOG("log"); check(send(0x00, 0x24, oldData.size()?0x00:0x01, 0x80|id, oldData+newData)); } //! Changes the data of a system key to the new key data //! provided with the command. void changeSystemKey() { CRYPTOLOG("log"); assert(!"not implemented"); } //! Creates a file (only EF or DF) void createFile(std::string /*path*/="", const std::string& /*data*/="") { CRYPTOLOG("log"); // pcsc::Connection::Reader::Transaction lock(_reader); // if (path.size()) select(path); // check(send(0x00, 0xE0, 0x00, 0x00, // check(send(0x00, 0xE0, 0x00, 0x00, BerValue(0x62h, // data).raw())); assert(!"not implemented"); } //! Creates a EF file void createBinary(std::string path="", std::string id="", const std::string data="") { CRYPTOLOG("log"); pcsc::Connection::Reader::Transaction lock(_reader); if (path.size()) select(path); BerValues c; c += BerValue(0x80, crypto::toBinary(data.size())); c += BerValue(0x82, crypto::hexToBin("01")); std::string idbin(crypto::hexToBin(id)); if (idbin.size()!=2) throw runtime_error("file id must be two bytes"); c += BerValue(0x83, idbin); c += BerValue(0x85, std::string(1, (char)(1<<7))); c += BerValue(0x86, crypto::hexToBin("00000000000000")); check(send(0x00, 0xE0, 0x00, 0x00, BerValue(0x62, c).binary())); updateBinary(data); } //! Deactivates a file or a file tree void deactivateFile() { CRYPTOLOG("log"); assert(!"not implemented"); } //! Decreases a record value void decrease() { CRYPTOLOG("log"); assert(!"not implemented"); } //! Deletes a file (DF or EF) void deleteFile() { CRYPTOLOG("log"); assert(!"not implemented"); } enum FileTypes {DF=0x00, EF=0x01, DF_EF=0x02}; //! Reads file information of EFs and/or DFs in the current DF BerValues directory(std::string path, FileTypes types=DF_EF, unsigned char offset=0) { CRYPTOLOG("log"); unsigned char o(offset); BerValues content; pcsc::Connection::Reader::Transaction lock(_reader); if (path.size()) select(path); /*while (o<(unsigned char)-1)*/ { /// @todo read until done std::string res(check(send(0x80, 0x16, types, o++))); //if (cardos::Commands::retCode(res)!=0x9000) break; content += res; } return content; } //! Enables an already loaded and activated but disabled license package. void enablePackage() { CRYPTOLOG("log"); assert(!"not implemented"); } //! Erases the file system in the EEPROM. void eraseFiles() { CRYPTOLOG("log"); assert(!"not implemented"); } //! Performs a challenge/response test void externalAuthenticate() { CRYPTOLOG("log"); assert(!"not implemented"); } //! Changes the life cycle phase from MANUFACTURING to //! ADMINISTRATION after creation of the MF or changes only from //! MANUFACTURING to INITIALIZATION. void format() { CRYPTOLOG("log"); assert(!"not implemented"); } //! Generates a key pair for the RSA/RSA2 algorithms within the card void generateKeyPair() { CRYPTOLOG("log"); assert(!"not implemented"); } //! Generates an internal random number (i.e. SC_Challenge) void getChallange() { CRYPTOLOG("log"); assert(!"not implemented"); } //! Reads system information std::string getData(unsigned char mode) { CRYPTOLOG("log"); return check(send(0x00, 0xCA, 0x01, mode)); } //! Product name, version, release date, copyright string std::string getDataProductName() { CRYPTOLOG("log"); return getData(0x80); } struct ChipProductionData { std::string serial; unsigned char type; std::string id; }; //! Chip production data as supplied by Infineon, PROM area ChipProductionData getDataChipProduction() { CRYPTOLOG("log"); std::string code(getData(0x81)); ChipProductionData res = { code.substr(8, 8), (unsigned char)code[9], code.substr(11, 6) }; return res; } //! Transmits an external random number (i.e. System_Random) to //! the smart card /*! The command transmits an external random number of n bytes the so-called System_Random to the smart card. System_Random will be used for the next response- SM (SIG or ENC_SIG) calculation. Valid values for n in short APDU mode are in the range 1 – 256. In extended APDU mode nmax is dependent on the preset data field length (nmax = data field length). The external random number System_Random can be used only once. System_Random is valid during the same smart card session until it has been used by response-SM or until it is overwritten by a new GIVE RANDOM command. The external random number is stored in the XRAM of the smart card. */ void giveRandom(std::string random) { CRYPTOLOG("log"); check(send(0x80, 0x86, 0x00, 0x00, random)); } //! Increases a record value /** This command increases the value of the current record of a cyclic fixed file by the @p size. EF-ID Byte P2: - 0x00 → use current EF - bits 7-3 = ppppp, bits 2-0=0 → use SFI ppppp (11111 not allowed) - other value → rfu */ std::string increase(unsigned char efId=0, unsigned short size=1) { CRYPTOLOG("log"); std::string data; data.resize(2); data[0] = char((unsigned char)(size>>8)); data[1] = char((unsigned char)(size&0xff)); return check(send(0x80, 0x32, 0x00, efId, data)); // 0x08 or 0x80? // old code was 0x80, manual says 0x08 (manual exactly says: "8xh") } //! Manufacturer use only void initializeEeprom() { CRYPTOLOG("log"); assert(!"not implemented"); } //! Manufacturer use only void initializeEnd() { CRYPTOLOG("log"); assert(!"not implemented"); } //! Performs cryptographic algorithms for authentication void internalAuthenticate() { CRYPTOLOG("log"); assert(!"not implemented"); } //! Activates a package void loadExecutable() { CRYPTOLOG("log"); assert(!"not implemented"); } //! Opens or closes a logical channel std::string manageChannel(unsigned char mode, unsigned char channelId) { CRYPTOLOG("log"); return check(send(0x00, 0x70, mode, channelId)); } //! Loads a CSE (RESTORE) or sets a component of the CSE (SET) //! and/or sets an extended headerlist void manageSecureEnvironment() { CRYPTOLOG("log"); assert(!"not implemented"); } //! Performs a challenge/response test and a subsequent //! MAC/Signature calculation and, depending on the input, a //! session key derivation. void mutualAuthenticate() { CRYPTOLOG("log"); assert(!"not implemented"); } //! Performs a cryptographic operation void performSecurityOperation() { CRYPTOLOG("log"); assert(!"not implemented"); } //! Controls the command sequence transactions void performTransactonOperation() { CRYPTOLOG("log"); assert(!"not implemented"); } //! Personalizer use only void personalize() { CRYPTOLOG("log"); assert(!"not implemented"); } //! Changes from life cycle phase ADMINISTRATION to OPERATIONAL //! and vice versa. /*! The command changes the global life cycle phase of the smart card from ADMINISTRATION to OPERATIONAL. This change is permanently valid for all DFs and will be stored in EEPROM, i.e. this life cycle phase is still valid after a reset of the smart card. The change from life cycle phase OPERATIONAL to ADMINISTRATION, however, is only temporary and valid only for the current DF (current life cycle phase) The life cycle phase is set back to OPERATIONAL when a context change (selection of a different DF) occurs or after a reset of the smart card. If the current life cycle phase shall be changed to ADMINISTRATION in several DFs, a PHASE CONTROL command has to be issued each time after the selection of one of those DFs. The temporary change to ADMINISTRATION in one DF can also be undone (without reset) with another PHASE CONTROL command. @pre Changing from ADMINISTRATION to OPERATIONAL is not protected. Changing from OPERATIONAL to ADMINISTRATION is controlled by the right referenced by the current DF’s AC LCYCLE. @pre The command can be executed in the life cycle phases ADMINISTRATION and OPERATIONAL. */ void phaseControl() { CRYPTOLOG("log"); check(send(0x80, 0x10, 0x00, 0x00)); } //! Installs / administrates / overwrites different objects void putData() { CRYPTOLOG("log"); assert(!"not implemented"); } //! Read a BINARY file std::string readBinary(std::string path="", unsigned short offset = 0) { CRYPTOLOG("log"); pcsc::Connection::Reader::Transaction lock(_reader); if (path.size()) select(path); return check(send(0x00, 0xB0, (unsigned char)(offset>>8), (unsigned char)(offset&0xFF))); } enum ReadRecordMode { FIRST_RECORD =0, LAST_RECORD = 1, NEXT_RECORD = 2, PREVIOUS_RECORD = 3, CURRENT_RECORD = 4, ABSOLUTE_RECORD = 4 }; //! Read a record oriented file std::string readRecord(const std::string& path = std::string(), unsigned char record = 0, unsigned char sfi = 0, ReadRecordMode mode = ABSOLUTE_RECORD) { CRYPTOLOG("log"); pcsc::Connection::Reader::Transaction lock(_reader); if (path.size()) select(path); return check(send(0x00, 0xB2, record, (unsigned char)((sfi&0xF8)|mode))); } /// Read all records from a record oriented file BerValues readBerFile(std::string path="") { CRYPTOLOG("log"); BerValues content; pcsc::Connection::Reader::Transaction lock(_reader); if (path.size()) select(path); while (true) { std::string res(send(0x00, 0xB2, 0, NEXT_RECORD)); if (cardos::Commands::retCode(res)!=0x9000) break; content += retData(res).substr(2); } return content; } //! Resets the error counter of a BS object to its maximum void resetRetryCounter() { CRYPTOLOG("log"); assert(!"not implemented"); } //! Resets the security status of the current DF void resetSecurityCounter() { CRYPTOLOG("log"); assert(!"not implemented"); } enum FileSelectionMode { DF_OR_EF_DIRECTLY_BELOW_CURRENT_DF_USING_FILE_ID = 0x00, DF_DIRECTLY_BELOW_CURRENT_DF_USING_FILE_ID = 0x01, EF_DIRECTLY_BELOW_CURRENT_DF_USING_FILE_ID = 0x02, PARENT_DF_OF_CURRENT_DF = 0x03, DF_BY_NAME = 0x04, DF_OR_EF_USING_PATH_FROM_MF = 0x08, DF_OR_EF_USING_PATH_FROM_CURRENT_DF = 0x09 }; enum FileSelectionReturn { RETURN_FCI_DATA = 0x00, RETURN_FCP_DATA = 0x04, RETURN_NOTHING = 0x0C }; //! Selects a file BerValues selectFile(std::string file, FileSelectionMode mode = DF_OR_EF_USING_PATH_FROM_MF, FileSelectionReturn ret = RETURN_NOTHING) { CRYPTOLOG("log"); return BerValues(check(send(0x00, 0xA4, mode, ret, file))); } //! Sets the so-called Data_Field_Length parameter relevant for //! the effective Command Data Field Length / Response Data //! Field Length. void setDataFieldLength() { CRYPTOLOG("log"); assert(!"not implemented"); } //! Enables or disables the usage of an existing transaction buffer void setTransactionState() { CRYPTOLOG("log"); assert(!"not implemented"); } //! Signs a hashed input using a decryption key with the RSA_SIG //! algorithm. void signByDecryptionKey() { CRYPTOLOG("log"); assert(!"not implemented"); } //! Uninstalls a package of the smart card void uninstallPackage() { CRYPTOLOG("log"); assert(!"not implemented"); } // same as readBinary // /// Read the previously file from smart card // std::string readBinFile() { // CRYPTOLOG("log"); // return check(send(0x00, 0xB0, 0x00, 0x00)); // } //! Updates a BINARY file void updateBinary(std::string data, unsigned short offset=0) { CRYPTOLOG("log"); check(send(0x00, 0xD6, (unsigned char)(offset>>8), (unsigned char)(offset&0xFF), data)); } //! Overwrites an existing record. std::string updateRecord(std::string data, unsigned char record = 0, unsigned char sfi = 0, ReadRecordMode mode = ABSOLUTE_RECORD) { CRYPTOLOG("log"); return check(send(0x00, 0xDC, record, (unsigned char)((sfi&0xF8)|mode), data)); } enum VerifyMode { SEARCH_IN_MF = 0, SEARCH_FROM_DF = 0x80 }; //! Performs a PIN test (CHV test) void verify(std::string pin, unsigned char id, VerifyMode mode = SEARCH_FROM_DF) { CRYPTOLOG("log pin=\""<4) return send(data[0], data[1], data[2], data[3], data.substr(4)); else throw runtime_error("wrong APDU pass at least 4 bytes in hex"); } //! @return error text of APDU result string static std::string error(std::string res) { return error(retCode(res)); } //! @return error text of return code evaluated by @ref retCode static std::string error(int ret) { switch (ret) { case 0x6283: return "File is deactivated (see DEACTIVATE FILE command)"; case 0x6285: return "File is terminated"; case 0x6300: return "Authentication failed"; case 0x6581: return "EEPROM error; command aborted"; case 0x6700: return "LC invalid"; case 0x6881: return "Logical channel not supported"; case 0x6882: return "SM mode not supported"; case 0x6884: return "Chaining Error"; case 0x6981: return "Command cannot be used for file structure"; case 0x6982: return "Required access right not granted"; case 0x6983: return "BS object blocked"; case 0x6984: return "BS object has invalid format"; case 0x6985: return "Conditions of use not satisfied; no random number available"; case 0x6986: return "no current EF selected"; case 0x6987: return "Key object for SM not found"; case 0x6988: return "Key object used for SM has invalid format"; case 0x6a80: return "Invalid parameters in data field"; case 0x6a81: return "Function / mode not supported"; case 0x6a82: return "File not found"; case 0x6a83: return "Record / object not found"; case 0x6a84: return "Not enough memory in file / in file system (e.g. CREATE FILE)" " available"; case 0x6a85: return "LC does not fit the TLV structure of the data field"; case 0x6a86: return "P1 / P2 invalid"; case 0x6a87: return "LC does not fit P1 /P2"; case 0x6a88: return "Object not found (GET DATA)"; case 0x6a89: return "File already exists"; case 0x6a8a: return "DF name already exists"; case 0x6c00: return "LE does not fit the data to be sent (e.g. SM)"; case 0x6d00: return "INS invalid"; case 0x6e00: return "CLA invalid (Hi nibble)"; case 0x6f00: return "Technical Error: " " 1 Attempt to create more than 254 records in a file. " " 2 Package uses SDK version which is not compatible to" " API version " " 3 Package contains invalid statements (LOAD EXECUTABLE)"; case 0x6f81: return "File is invalidated because of checksum error (prop.)"; case 0x6f82: return "Not enough memory available in XRAM"; case 0x6f83: return "Transaction error (i.e. command must not be used in a" " transaction)"; case 0x6f84: return "General protection fault (prop.)"; case 0x6f85: return "Internal failure of PK-API (e.g. wrong CCMS format)"; case 0x6f86: return "Key Object not found"; case 0x6f87: return "Internal hardware attack detected, change to life cycle death"; case 0x6f88: return "Transaction buffer too small"; case 0x6fff: return "Internal assertion (invalid internal error) " "This error is no runtime error but an internal error which can" " occur because of a programming error only."; case 0x9000: return "Command executed correctly"; case 0x9001: return "Command executed correctly; EEPROM weakness detected" " (EEPROM written with second trial; the EEPROM area" " overwritten has a limited life time only.)"; case 0x9850: return "Overflow through INCREASE / underflow through DECREASE"; case -1: return "No return code received"; default: std::stringstream ss; if ((ret&0xff00)==0x6100) ss<<(ret&0xff)<<" bytes of response data can be received" <<" with GET RESPONSE (only T=0 transmission protocol)"; else ss<<"Unknown CardOS error code: 0x"<=2) return ((((unsigned int)(unsigned char)res[res.size()-2])*256) +((unsigned int)(unsigned char)res[res.size()-1])); else return -1; } static std::string retData(const std::string& res) { if (res.size()>=2) return res.substr(0, res.size()-2); else return std::string(); } std::string check(std::string res) { unsigned int value(retCode(res)); if (value==0x6300) throw wrong_pin(error(value)); if (value!=0x9000) throw runtime_error(error(value)); return retData(res); } std::string logResult(const std::string& result) { std::string txt; for (std::string::const_iterator it(result.begin()); it!=result.end(); ++it) txt += isprint(*it)?*it:'.'; if (result.size()>=2) { CARDOS_LOG(error(result)<2) { CARDOS_LOG("Data: " <print(indent+step, step); return res; } std::string type() const { return "Directory"; } protected: void setup(Commands& cmd) { _children.clear(); BerValues i(cmd.directory(_path)); CRYPTOLOG("Found Structure: "< Value: "<print()); if (it->size()<2) continue; // empty dir switch ((*it)[0].ulong()) { case 0x38: _children.push_back(Child(new Dir(cmd, _path+(*it)[1].hex()))); break; case 0x01: _children.push_back(Child(new File(cmd, _path, (*it)[1].hex()))); break; case 0x05: _children.push_back(Child(new Link(cmd, _path, (*it)[1].hex()))); break; case 0x06: _children.push_back(Child(new Counter(cmd, _path, (*it)[1].hex()))); break; default: // unknown throw runtime_error("unknown object: "+it->print()); } } } }; //@} } //@} #endif