/*! @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 #define CARDOS_LOG(X) // no logging by default // use e.g. #define CARDOS_LOG(X) std::clog<& sequence): _tag(PRIVATE|CONSTRUCTED|SEQUENCE), _sequence(sequence) { } BerValue(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()& values): _tag(tag), _sequence(values) { } unsigned char tagClass() { return _tag&0xC0; } unsigned char tagPC() { return _tag&0x20; } unsigned char tagType() { return _tag&0x1F; } bool isContainer() { return tagPC()==CONSTRUCTED; } unsigned int size() { return _sequence.size(); } unsigned char tag() { return _tag; } BerValue operator[](unsigned int i) { if (i>=_sequence.size()) throw array_range(i, _sequence.size()); return _sequence[i]; } operator std::string() { std::string res; res.push_back(_tag); if (isContainer()) { for (std::vector::iterator it(_sequence.begin()); it!=_sequence.end(); ++it) { res += *it; } } else { (res += (char)_value.size()) += _value; } return res; } std::string string() { return _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 BerValues& values) { insert(end(), values.begin(), values.end()); return *this; } std::string print(int indent=0, int indentStep = 4) { std::stringstream ss; if (size()==1) { ss<print(indent+2, indentStep) < reader): _reader(reader) { } /// Set smart card reader. void reader(mrw::Shared reader) { _reader = reader; } //@} //------------------------------------------------------------------------ /// @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. @prereq 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
@prereq 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, 8|size>>8, 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. @prereq 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, @refs 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…xxh80h 88h Mode 00hxxh 22h…22hxxhxxh…xxh
n bytes4 bytes1 byte9 bytes 1 byte6 bytes/empty
@prereq 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())); std::string idbin(crypto::hexToBin(id)); if (idbin.size()!=2) throw runtime_error("file id must be two bytes"); c += BerValue(0x83, idbin); check(send(0x00, 0xE0, 0x00, 0x00, BerValue(82, c))); } //! 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] = size>>8; data[1] = 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 theglobal 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. @prereq 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. @prereq 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, offset>>8, 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(unsigned char record = 0, unsigned char sfi = 0, ReadRecordMode mode = ABSOLUTE_RECORD) { CRYPTOLOG("log"); return check(send(0x00, 0xB2, record, (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, offset>>8, 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, (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"); check(send(0x00, 0x20, 0x00, id|mode, pin)); } //! Performs a PIN test (CHV test) /*! @return number of remaining PIN retries or -1 if PIN is locked */ int verify(unsigned char id, VerifyMode mode = SEARCH_FROM_DF) { CRYPTOLOG("log"); std::string res(send(0x00, 0x20, 0x00, id|mode)); unsigned int value((((unsigned int)(unsigned char)res[0])*256) +((unsigned int)(unsigned char)res[1])); if ((value&0x63C0)==0x63C0) return value&0x0F; return -1; } //@} //------------------------------------------------------------------------ /// @name Files and IDs on a Smart Card //@{ /// Path to MF std::string mf() { return crypto::hexToBin("3f00"); } /// Path to PKCS#15 std::string pkcs15() { return crypto::hexToBin("5015"); } /// Path to SigG (Signaturgesetz) std::string sigG() { return crypto::hexToBin("1fff"); } /// ID of transport PIN unsigned char transportPin() { return 0x71; } /// ID of PKCS#15 user PIN unsigned char pkcs15Pin() { return 0x01; } /// ID of SigG (Signaturgesetz) secure PIN unsigned char sigGPin() { return 0x01; } /// ID of PUK to unlock PKCS#15 user PIN unsigned char puk() { return 0x02; } //@} //------------------------------------------------------------------------ /// @name High Level Smart Card Access //@{ /// Logon with transport PIN void logonTransport(std::string pin) { CRYPTOLOG("log"); pcsc::Connection::Reader::Transaction lock(_reader); selectSigG(); logon(transportPin(), pin); } /// Logon with SigG (Signaturgesetz) secure PIN void logonSigG(std::string pin) { CRYPTOLOG("log"); pcsc::Connection::Reader::Transaction lock(_reader); selectSigG(); logon(sigGPin(), pin); } /// Logon with PKCS#15 user PUK to unlock user PIN void logonPuk(std::string pin) { CRYPTOLOG("log"); pcsc::Connection::Reader::Transaction lock(_reader); selectMF(); logon(puk(), pin); } /// Logon with PKCS#15 user PIN void logonPkcs15(std::string pin) { CRYPTOLOG("log"); pcsc::Connection::Reader::Transaction lock(_reader); selectMF(); logon(pkcs15Pin(), pin); } /// Change SigG (Signaturgesetz) secure PIN /** If smart card is in transport state, @p oldPin must be transport PIN and then the card is unlocked and the transport state is unset. */ void changeSigGPin(std::string newPin, std::string oldPin) { CRYPTOLOG("log"); if (transportState()) { // first time use, reset transport state pcsc::Connection::Reader::Transaction lock(_reader); logonTransport(oldPin); changeReferenceData(sigGPin(), newPin); unsetTransportState(); } else { // ordinary PIN change pcsc::Connection::Reader::Transaction lock(_reader); logonSigG(oldPin); selectSigG(); changeReferenceData(sigGPin(), newPin, oldPin); } } /// Change PKCS#15 user PIN void changePkcs15Pin(std::string newPin, std::string oldPin) { CRYPTOLOG("log"); pcsc::Connection::Reader::Transaction lock(_reader); selectMF(); changeReferenceData(pkcs15Pin(), newPin, oldPin); } /// Change PKCS#15 user PIN /** To keep all PINs synchronized: Detect if it is in transport state, if so, old PIN must be transport PIN. in any case, change PIN on PKCS#15 and SigG from the same old PIN to the same new PIN. */ void changePins(std::string newPin, std::string oldPin) { CRYPTOLOG("log"); pcsc::Connection::Reader::Transaction lock(_reader); if (transportState()) { changeSigGPin(newPin, oldPin); changePkcs15Pin(newPin, oldPin); } else { if (pkcs15PinRetries()!=-1) changePkcs15Pin(newPin, oldPin); try { if (sigGPinRetries()!=-1) changeSigGPin(newPin, oldPin); } catch (...) { // undo PKCS#15 PIN change if (pkcs15PinRetries()!=-1) changePkcs15Pin(oldPin, newPin); throw; } } } /// Select a file in the PKCS#15 part on the smart card void selectPkcs15File(std::string file) { CRYPTOLOG("log"); check(send(0x00, 0xA4, 0x08, 0x0C, crypto::hexToBin("3f005015"+file))); } /// Select a file in the SigG (Signaturgesetz) part on the smart card void selectSigGFile(std::string file) { CRYPTOLOG("log"); check(send(0x00, 0xA4, 0x08, 0x0C, crypto::hexToBin("3f001fff"+file))); } /// Select a file in the MF part on the smart card void selectMfFile(std::string file) { CRYPTOLOG("log"); check(send(0x00, 0xA4, 0x08, 0x0C, crypto::hexToBin("3f00"+file))); } /// Generic select file void select(std::string path) { CRYPTOLOG("log"); check(send(0x00, 0xA4, 0x08, 0x0C, crypto::hexToBin(path))); } /// Select the PKCS#15 part on the smart card void selectPkcs15() { CRYPTOLOG("log"); check(send(0x00, 0xA4, 0x08, 0x0C, crypto::hexToBin("3f005015"))); } /// Select the SigG (Signaturgesetz) part on the smart card void selectSigG() { CRYPTOLOG("log"); check(send(0x00, 0xA4, 0x08, 0x0C, crypto::hexToBin("3f001fff"))); } /// Select the MFpart on the smart card void selectMF() { CRYPTOLOG("log"); check(send(0x00, 0xA4, 0x00, 0x0C)); } /// @return binary serial number std::string serial() { CRYPTOLOG("log"); return getDataChipProduction().serial; } /// @return @c true if card is still in transport state bool transportState() { CRYPTOLOG("log"); pcsc::Connection::Reader::Transaction lock(_reader); selectSigGFile("fe15"); return std::string(4, '\0')==readRecord(); } /// Mark card as initiakized and no more in transport state void unsetTransportState() { CRYPTOLOG("log"); pcsc::Connection::Reader::Transaction lock(_reader); selectSigGFile("fe15"); increase(); } /*! @return number of remaining transport PIN retries or -1 if locked */ int transportPinRetries() { CRYPTOLOG("log"); pcsc::Connection::Reader::Transaction lock(_reader); selectSigG(); return verify(transportPin()); } /*! @return number of remaining PKCS#15 PIN retries or -1 if locked */ int pkcs15PinRetries() { CRYPTOLOG("log"); pcsc::Connection::Reader::Transaction lock(_reader); selectMF(); return verify(pkcs15Pin()); } /*! @return number of remaining SigG PIN retries or -1 if locked */ int sigGPinRetries() { CRYPTOLOG("log"); pcsc::Connection::Reader::Transaction lock(_reader); selectSigG(); return verify(sigGPin()); } /*! @return number of remaining PUK retries or -1 if locked */ int pukRetries() { CRYPTOLOG("log"); pcsc::Connection::Reader::Transaction lock(_reader); selectMF(); return verify(puk()); } //@} protected: /// @name More Generic Internal Auxiliary Methods //@{ void logon(unsigned char id, std::string pin) { verify(pin, id); } int pinStatus(unsigned char id) { std::string res(send(0x00, 0x20, 0x00, id|0x80)); unsigned int value((((unsigned int)(unsigned char)res[0])*256) +((unsigned int)(unsigned char)res[1])); switch (value) { case 0x6983: CARDOS_LOG("LOCKED"); return -1; case 0x63Ca: CARDOS_LOG("TEN"); return 10; case 0x63C9: CARDOS_LOG("NINE"); return 9; case 0x63C8: CARDOS_LOG("EIGHT"); return 8; case 0x63C7: CARDOS_LOG("SEVEN"); return 7; case 0x63C6: CARDOS_LOG("SIX"); return 6; case 0x63C5: CARDOS_LOG("FIVE"); return 5; case 0x63C4: CARDOS_LOG("FOUR"); return 4; case 0x63C3: CARDOS_LOG("THREE"); return 3; case 0x63C2: CARDOS_LOG("TWO"); return 2; case 0x63C1: CARDOS_LOG("ONE"); return 1; case 0x63C0: CARDOS_LOG("ZERO"); return 0; case 0x6300: CARDOS_LOG("LOCKED"); return -1; default: CARDOS_LOG("UNKNOWN"); return -1; } } std::string sendAPDU(std::string data) { if (data.size()==4) return send(data[0], data[1], data[2], data[3]); else if (data.size()>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: " <