2013-10-21 07:10:46 +00:00
|
|
|
|
/*! @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 <pcsc.hxx>
|
|
|
|
|
#include <cryptaux.hxx>
|
|
|
|
|
#include <openssl.hxx>
|
|
|
|
|
#include <mrw/string.hxx>
|
|
|
|
|
#include <stdexcept>
|
|
|
|
|
|
|
|
|
|
#ifndef CARDOS_LOG
|
|
|
|
|
#define CARDOS_LOG(X) // no logging by default
|
2013-11-06 12:24:52 +00:00
|
|
|
|
// use e.g. #define CARDOS_LOG(X) std::clog<<X<<std::endl
|
2013-10-21 07:10:46 +00:00
|
|
|
|
#endif
|
|
|
|
|
|
2014-03-07 15:53:22 +00:00
|
|
|
|
/** @defgroup gcardos C++ Access to Siemens CardOS 4.4
|
2014-01-31 13:32:31 +00:00
|
|
|
|
Implements APDUs for accessing Siemens CardOS V4.4 smartcards. */
|
|
|
|
|
//@{
|
|
|
|
|
/// @defgroup cardosexception CardOS Exceptions
|
|
|
|
|
/// @defgroup cardostypes CardOS Types
|
|
|
|
|
/// @defgroup cardoslib CardOS Library
|
|
|
|
|
//@}
|
|
|
|
|
|
|
|
|
|
/// @ref gcardos @copydoc gcardos
|
2013-10-21 07:10:46 +00:00
|
|
|
|
namespace cardos {
|
|
|
|
|
|
|
|
|
|
/// @addtogroup cardosexception CardOS Exceptions
|
|
|
|
|
//@{
|
|
|
|
|
//============================================================================
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
class exception: public pcsc::exception {
|
|
|
|
|
public:
|
|
|
|
|
exception(const std::string& reason) throw():
|
|
|
|
|
pcsc::exception("cardos: "+reason) {
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
class wrong_pin: public exception {
|
|
|
|
|
public:
|
2013-11-06 12:24:52 +00:00
|
|
|
|
wrong_pin(const std::string& s) throw(): exception("wrong pin "+s) {}
|
2013-10-21 07:10:46 +00:00
|
|
|
|
};
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
class user_pin_locked: public exception {
|
|
|
|
|
public:
|
|
|
|
|
user_pin_locked(const std::string& s) throw():
|
2013-11-06 12:24:52 +00:00
|
|
|
|
exception("user pin locked "+s) {}
|
2013-10-21 07:10:46 +00:00
|
|
|
|
};
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
class wrong_puk: public exception {
|
|
|
|
|
public:
|
2013-11-06 12:24:52 +00:00
|
|
|
|
wrong_puk(const std::string& s) throw(): exception("wrong puk "+s) {}
|
2013-10-21 07:10:46 +00:00
|
|
|
|
};
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
class wrong_result: public exception {
|
|
|
|
|
public:
|
|
|
|
|
wrong_result(const std::string& reason, const std::string& data) throw():
|
2013-11-06 12:24:52 +00:00
|
|
|
|
exception("wrong result, "+reason+": "+crypto::hex(data)) {}
|
2013-10-21 07:10:46 +00:00
|
|
|
|
wrong_result(const std::string& reason) throw():
|
2013-11-06 12:24:52 +00:00
|
|
|
|
exception("wrong result, "+reason) {}
|
2013-10-21 07:10:46 +00:00
|
|
|
|
};
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
class runtime_error: public exception {
|
|
|
|
|
public:
|
2013-11-06 12:24:52 +00:00
|
|
|
|
runtime_error(const std::string& reason) throw():
|
|
|
|
|
exception("runtime error, "+reason) {}
|
2013-10-21 07:10:46 +00:00
|
|
|
|
runtime_error(const std::string& reason, const std::string& data) throw():
|
2013-11-06 12:24:52 +00:00
|
|
|
|
exception("runtime error, "+reason+": "+crypto::hex(data)) {}
|
2013-10-21 07:10:46 +00:00
|
|
|
|
};
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
class unexpected_challenge_length: public exception {
|
|
|
|
|
public:
|
|
|
|
|
unexpected_challenge_length(const std::string& data) throw():
|
2013-11-06 12:24:52 +00:00
|
|
|
|
exception("challenge should be 8 bytes, challenge is: "
|
2013-10-21 07:10:46 +00:00
|
|
|
|
+crypto::hex(data)) {}
|
|
|
|
|
};
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
class card_transmission_failed: public exception {
|
|
|
|
|
public:
|
|
|
|
|
card_transmission_failed(const std::string& position,
|
|
|
|
|
const std::string& reason) throw():
|
2013-11-06 12:24:52 +00:00
|
|
|
|
exception("transmission to card failed: "+position
|
|
|
|
|
+" reason: "+reason) {
|
2013-10-21 07:10:46 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
class wrong_dataformat: public exception {
|
|
|
|
|
public:
|
|
|
|
|
wrong_dataformat(const std::string& data,
|
|
|
|
|
const std::string& reason) throw():
|
2013-11-06 12:24:52 +00:00
|
|
|
|
exception("wrong dataformat: "+crypto::hex(data)
|
|
|
|
|
+" reason: "+reason) {
|
2013-10-21 07:10:46 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
class array_range: public exception {
|
|
|
|
|
public:
|
|
|
|
|
array_range(unsigned int i, unsigned int j) throw():
|
|
|
|
|
exception("array index out of range: "+mrw::string(i)
|
|
|
|
|
+"; length: "+mrw::string(j)) {
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
class pin_locked: public exception {
|
|
|
|
|
public:
|
|
|
|
|
pin_locked() throw(): exception("pin is locked and cannot be changed") {}
|
|
|
|
|
};
|
|
|
|
|
//@}
|
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
|
|
|
|
|
|
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,
|
|
|
|
|
};
|
|
|
|
|
|
2014-03-07 15:53:22 +00:00
|
|
|
|
protected: // use BerValues instead
|
|
|
|
|
|
|
|
|
|
friend class BerValues;
|
|
|
|
|
|
|
|
|
|
BerValue(const std::vector<BerValue>& sequence):
|
2013-10-21 07:10:46 +00:00
|
|
|
|
_tag(PRIVATE|CONSTRUCTED|SEQUENCE),
|
|
|
|
|
_sequence(sequence) {
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-07 15:53:22 +00:00
|
|
|
|
BerValue(std::string& content) {
|
2013-10-21 07:10:46 +00:00
|
|
|
|
if (content.size()<2)
|
|
|
|
|
throw wrong_dataformat(content, "not a BER, header size too small: \""
|
|
|
|
|
+crypto::binToHex(content)+"\"");
|
|
|
|
|
_tag = content[0];
|
2014-03-07 15:53:22 +00:00
|
|
|
|
unsigned char length = content[1];
|
|
|
|
|
_value = content.substr(2, length);
|
|
|
|
|
if (content.size()<std::string::size_type(length)+2)
|
2013-10-21 07:10:46 +00:00
|
|
|
|
throw wrong_dataformat(content, "not a BER, content size too"
|
|
|
|
|
" small: \""+crypto::binToHex(_value)+"\"");
|
2014-03-07 15:53:22 +00:00
|
|
|
|
content.erase(0, 2+length);
|
2013-10-21 07:10:46 +00:00
|
|
|
|
if (tagType()==END_OF_CONTENT) return; // done
|
2014-03-07 15:53:22 +00:00
|
|
|
|
if (isContainer())
|
|
|
|
|
while (_value.size()) _sequence.push_back(BerValue(_value));
|
2013-10-21 07:10:46 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-03-07 15:53:22 +00:00
|
|
|
|
public:
|
|
|
|
|
|
|
|
|
|
BerValue(unsigned char tag, const std::string& value):
|
|
|
|
|
_tag(tag), _value(value) {
|
|
|
|
|
if (isContainer())
|
|
|
|
|
while (_value.size()) _sequence.push_back(BerValue(_value));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BerValue(unsigned char tag, const std::vector<BerValue>& values):
|
|
|
|
|
_tag(tag), _sequence(values) {
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-21 07:10:46 +00:00
|
|
|
|
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];
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-07 15:53:22 +00:00
|
|
|
|
operator std::string() {
|
|
|
|
|
std::string res;
|
|
|
|
|
res.push_back(_tag);
|
|
|
|
|
if (isContainer()) {
|
|
|
|
|
for (std::vector<BerValue>::iterator it(_sequence.begin());
|
|
|
|
|
it!=_sequence.end(); ++it) {
|
|
|
|
|
res += *it;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
(res += (char)_value.size()) += _value;
|
|
|
|
|
}
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string string() {
|
2013-10-21 07:10:46 +00:00
|
|
|
|
return _value;
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-07 15:53:22 +00:00
|
|
|
|
unsigned long ulong() {
|
|
|
|
|
return crypto::ulongFromBinary(_value);
|
2013-10-21 07:10:46 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string print(int indent=0, int indentStep = 4) {
|
|
|
|
|
std::stringstream ss;
|
2014-03-07 15:53:22 +00:00
|
|
|
|
ss<<std::string(indent*indentStep, ' ')<<'['<<crypto::binToHex(_tag)<<'=';
|
2013-10-21 07:10:46 +00:00
|
|
|
|
switch (tagClass()) {
|
|
|
|
|
case UNIVERSAL: ss<<"UNIVERSAL,"; break;
|
|
|
|
|
case APPLICATION: ss<<"APPLICATION,"; break;
|
|
|
|
|
case CONTEXT_SPECIFIC: ss<<"CONTEXT_SPECIFIC,"; break;
|
|
|
|
|
case PRIVATE: ss<<"PRIVATE,"; break;
|
|
|
|
|
default: ss<<(unsigned int)tagClass();
|
|
|
|
|
}
|
|
|
|
|
switch (tagPC()) {
|
|
|
|
|
case PRIMITIVE: ss<<"PRIMITIVE,"; break;
|
|
|
|
|
case CONSTRUCTED: ss<<"CONSTRUCTED,"; break;
|
|
|
|
|
default: ss<<(unsigned int)tagPC();
|
|
|
|
|
}
|
|
|
|
|
switch (tagType()) {
|
|
|
|
|
case END_OF_CONTENT: ss<<"END_OF_CONTENT"; break;
|
|
|
|
|
case BOOLEAN: ss<<"BOOLEAN"; break;
|
|
|
|
|
case INTEGER: ss<<"INTEGER"; break;
|
|
|
|
|
case BIT_STRING: ss<<"BIT_STRING"; break;
|
|
|
|
|
case OCTET_STRING: ss<<"OCTET_STRING"; break;
|
|
|
|
|
case EMPTY: ss<<"EMPTY"; break;
|
|
|
|
|
case OBJECT_IDENTIFIER: ss<<"OBJECT_IDENTIFIER"; break;
|
|
|
|
|
case OBJECT_DESCRIPTOR: ss<<"OBJECT_DESCRIPTOR"; break;
|
|
|
|
|
case EXTERNAL: ss<<"EXTERNAL"; break;
|
|
|
|
|
case REAL: ss<<"REAL"; break;
|
|
|
|
|
case ENUMERATED: ss<<"ENUMERATED"; break;
|
|
|
|
|
case EMBEDDED_PDV: ss<<"EMBEDDED_PDV"; break;
|
|
|
|
|
case UTF8_STRING: ss<<"UTF8_STRING"; break;
|
|
|
|
|
case RELATIVE_OID: ss<<"RELATIVE_OID"; break;
|
|
|
|
|
case SEQUENCE: ss<<"SEQUENCE"; break;
|
|
|
|
|
case SET: ss<<"SET"; break;
|
|
|
|
|
case NUMERIC_STRING: ss<<"NUMERIC_STRING"; break;
|
|
|
|
|
case PRINTABLE_STRING: ss<<"PRINTABLE_STRING"; break;
|
|
|
|
|
case T61_STRING: ss<<"T61_STRING"; break;
|
|
|
|
|
case VIDEOTEX_STRING: ss<<"VIDEOTEX_STRING"; break;
|
|
|
|
|
case IA5_STRING: ss<<"IA5_STRING"; break;
|
|
|
|
|
case UTC_TIME: ss<<"UTC_TIME"; break;
|
|
|
|
|
case GENERALIZED_TIME: ss<<"GENERALIZED_TIME"; break;
|
|
|
|
|
case GRAPHIC_STRING: ss<<"GRAPHIC_STRING"; break;
|
|
|
|
|
case VISIBLE_STRING: ss<<"VISIBLE_STRING"; break;
|
|
|
|
|
case GENERAL_STRING: ss<<"GENERAL_STRING"; break;
|
|
|
|
|
case UNIVERSAL_STRING: ss<<"UNIVERSAL_STRING"; break;
|
|
|
|
|
case CHARACTER_STRING: ss<<"CHARACTER_STRING"; break;
|
|
|
|
|
case BMP_STRING: ss<<"BMP_STRING"; break;
|
|
|
|
|
default: ss<<(unsigned int)tagType();
|
|
|
|
|
}
|
|
|
|
|
ss<<"] = ";
|
|
|
|
|
if (isContainer()) {
|
|
|
|
|
int i(0);
|
|
|
|
|
ss<<"{"<<std::endl;
|
|
|
|
|
for (std::vector<BerValue>::iterator it(_sequence.begin());
|
|
|
|
|
it!=_sequence.end(); ++it) {
|
|
|
|
|
ss<<std::string((indent+1)*indentStep, ' ')
|
2014-03-07 15:53:22 +00:00
|
|
|
|
<<"["<<i++<<"] {"
|
2013-10-21 07:10:46 +00:00
|
|
|
|
<<std::endl
|
|
|
|
|
<<it->print(indent+2, indentStep)
|
|
|
|
|
<<std::endl
|
|
|
|
|
<<std::string((indent+1)*indentStep, ' ')<<'}'
|
|
|
|
|
<<std::endl;
|
|
|
|
|
}
|
|
|
|
|
ss<<std::string(indent*indentStep, ' ')<<'}';
|
|
|
|
|
} else {
|
|
|
|
|
ss<<"0x"<<crypto::binToHex(_value)<<" = \"";
|
|
|
|
|
for (std::string::const_iterator it(_value.begin());
|
|
|
|
|
it!=_value.end(); ++it)
|
|
|
|
|
ss<<(isprint(*it)?*it:'.');
|
2014-03-07 15:53:22 +00:00
|
|
|
|
ss<<"\"";
|
2013-10-21 07:10:46 +00:00
|
|
|
|
}
|
|
|
|
|
return ss.str();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
|
|
|
|
unsigned char _tag;
|
|
|
|
|
std::string _value;
|
|
|
|
|
std::vector<BerValue> _sequence;
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/// Store a sequence of BerValue
|
|
|
|
|
class BerValues: public std::vector<BerValue> {
|
|
|
|
|
public:
|
2014-03-07 15:53:22 +00:00
|
|
|
|
BerValues() {}
|
|
|
|
|
BerValues(const std::string& content) {
|
|
|
|
|
std::string contentCopy(content);
|
|
|
|
|
while (contentCopy.size()) push_back(BerValue(contentCopy));
|
|
|
|
|
}
|
|
|
|
|
BerValues& operator=(std::string& content) {
|
2013-10-21 07:10:46 +00:00
|
|
|
|
clear();
|
2014-03-07 15:53:22 +00:00
|
|
|
|
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));
|
2013-10-21 07:10:46 +00:00
|
|
|
|
return *this;
|
|
|
|
|
}
|
2014-03-07 15:53:22 +00:00
|
|
|
|
BerValues& operator+=(const BerValues& values) {
|
|
|
|
|
insert(end(), values.begin(), values.end());
|
2013-10-21 07:10:46 +00:00
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
std::string print(int indent=0, int indentStep = 4) {
|
|
|
|
|
std::stringstream ss;
|
|
|
|
|
if (size()==1) {
|
|
|
|
|
ss<<std::string(indent*indentStep, ' ')<<at(0).print();
|
|
|
|
|
} else {
|
|
|
|
|
int i(0);
|
|
|
|
|
ss<<"{"<<std::endl;
|
|
|
|
|
for (iterator it(begin()); it!=end(); ++it) {
|
|
|
|
|
ss<<std::string((indent+1)*indentStep, ' ')
|
2014-03-07 15:53:22 +00:00
|
|
|
|
<<"["<<i++<<"] {"
|
2013-10-21 07:10:46 +00:00
|
|
|
|
<<std::endl
|
|
|
|
|
<<it->print(indent+2, indentStep)
|
|
|
|
|
<<std::endl
|
|
|
|
|
<<std::string((indent+1)*indentStep, ' ')<<'}'
|
|
|
|
|
<<std::endl;
|
|
|
|
|
}
|
|
|
|
|
ss<<std::string(indent*indentStep, ' ')<<'}';
|
|
|
|
|
}
|
|
|
|
|
return ss.str();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//@}
|
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
|
/// @addtogroup cardoslib
|
|
|
|
|
//@{
|
|
|
|
|
|
|
|
|
|
/// Implements CardOS V4.4 commands.
|
2013-11-06 12:24:52 +00:00
|
|
|
|
/** Directly sends CardOS V4.4 commands to a smart card using APDUs. */
|
2013-10-21 07:10:46 +00:00
|
|
|
|
class Commands {
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
|
/// @name Setup Smart Card Reader
|
|
|
|
|
//@{
|
|
|
|
|
|
2013-11-06 12:24:52 +00:00
|
|
|
|
/// Uninitialized class
|
|
|
|
|
/** Use @ref reader to setup assign smart card reader. */
|
|
|
|
|
Commands() {}
|
2013-10-21 07:10:46 +00:00
|
|
|
|
|
|
|
|
|
/// Initialize with given smart card reader.
|
|
|
|
|
Commands(mrw::Shared<pcsc::Connection::Reader> reader):
|
|
|
|
|
_reader(reader) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Set smart card reader.
|
|
|
|
|
void reader(mrw::Shared<pcsc::Connection::Reader> 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() {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
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.
|
|
|
|
|
|
|
|
|
|
<table>
|
|
|
|
|
<caption>Bytes P1-P2</caption>
|
|
|
|
|
<thead>
|
|
|
|
|
<tr><td colspan="2">P1 (MODE)</td><td>P2</td><td>Meaning</td></tr>
|
|
|
|
|
<tr><td>Bit 7</td><td>Bits 6 – 0</td><td/><td/></tr>
|
|
|
|
|
</thead>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>1</td><td>HI value</td><td>LO value</td>
|
|
|
|
|
<td>Allocate buffer with size HI-LO</td>
|
|
|
|
|
</tr><tr>
|
|
|
|
|
<td>0</td><td>rfu</td><td>ID</td>
|
|
|
|
|
<td>Free buffer with ID in P2</td>
|
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
|
|
|
|
|
|
|
|
|
@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) {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
|
|
|
|
if (size>0x7FFF) throw runtime_error("requested buffer too large");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
return check(send(0x80, 0x12, 8|size>>8, size&0xFF));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//! Free transaction buffer
|
|
|
|
|
/*! @see allocateTransactionBuffer */
|
|
|
|
|
void freeTransactionBuffer(unsigned char id) {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
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) {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
<table>
|
|
|
|
|
<caption>Format of the input for hash value calculation</caption>
|
|
|
|
|
<thead><tr>
|
|
|
|
|
<th>System_Challenge</th>
|
|
|
|
|
<th>Command Header<br/>CLA INS P1 P2</th>
|
|
|
|
|
<th>Global Life Cycle Phase</th>
|
|
|
|
|
<th>Internal Parameters</th>
|
|
|
|
|
<th>Number of loaded Packages</th>
|
|
|
|
|
<th>Chip Unique Identification Number (opt.)</th>
|
|
|
|
|
</tr></thead>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>xxh…xxh</td><td>80h 88h Mode 00h</td><td>xxh</td>
|
|
|
|
|
<td>22h…22h</td><td>xxh</td><td>xxh…xxh</td>
|
|
|
|
|
</tr><tr>
|
|
|
|
|
<td>n bytes</td><td>4 bytes</td><td>1 byte</td><td>9 bytes</td>
|
|
|
|
|
<td>1 byte</td><td>6 bytes/empty</td>
|
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
|
|
|
|
|
|
|
|
|
@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) {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
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() {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
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()) {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
|
|
|
|
check(send(0x00, 0x24, oldData.size()?0x00:0x01, 0x80|id,
|
|
|
|
|
oldData+newData));
|
2013-10-21 07:10:46 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//! Changes the data of a system key to the new key data
|
|
|
|
|
//! provided with the command.
|
|
|
|
|
void changeSystemKey() {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
assert(!"not implemented");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//! Creates a file (only EF or DF)
|
2014-03-07 15:53:22 +00:00
|
|
|
|
void createFile(std::string path="", const std::string data="") {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2014-03-07 15:53:22 +00:00
|
|
|
|
// pcsc::Connection::Reader::Transaction lock(_reader);
|
|
|
|
|
// if (path.size()) select(path);
|
|
|
|
|
// check(send(0x00, 0xE0, 0x00, 0x00,
|
2013-10-21 07:10:46 +00:00
|
|
|
|
// check(send(0x00, 0xE0, 0x00, 0x00, BerValue(0x62h,
|
|
|
|
|
// data).raw()));
|
|
|
|
|
assert(!"not implemented");
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-07 15:53:22 +00:00
|
|
|
|
//! 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)));
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-21 07:10:46 +00:00
|
|
|
|
//! Deactivates a file or a file tree
|
|
|
|
|
void deactivateFile() {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
assert(!"not implemented");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//! Decreases a record value
|
|
|
|
|
void decrease() {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
assert(!"not implemented");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//! Deletes a file (DF or EF)
|
|
|
|
|
void deleteFile() {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
assert(!"not implemented");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum FileTypes {DF=0x00, EF=0x01, DF_EF=0x02};
|
|
|
|
|
|
|
|
|
|
//! Reads file information of EFs and/or DFs in the current DF
|
2014-03-07 15:53:22 +00:00
|
|
|
|
BerValues directory(std::string path, FileTypes types=DF_EF,
|
|
|
|
|
unsigned char offset=0) {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2014-03-07 15:53:22 +00:00
|
|
|
|
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;
|
2013-10-21 07:10:46 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//! Enables an already loaded and activated but disabled license package.
|
|
|
|
|
void enablePackage() {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
assert(!"not implemented");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//! Erases the file system in the EEPROM.
|
|
|
|
|
void eraseFiles() {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
assert(!"not implemented");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//! Performs a challenge/response test
|
|
|
|
|
void externalAuthenticate() {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
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() {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
assert(!"not implemented");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//! Generates a key pair for the RSA/RSA2 algorithms within the card
|
|
|
|
|
void generateKeyPair() {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
assert(!"not implemented");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//! Generates an internal random number (i.e. SC_Challenge)
|
|
|
|
|
void getChallange() {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
assert(!"not implemented");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//! Reads system information
|
|
|
|
|
std::string getData(unsigned char mode) {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
return check(send(0x00, 0xCA, 0x01, mode));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//! Product name, version, release date, copyright string
|
|
|
|
|
std::string getDataProductName() {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
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() {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
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) {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
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) {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
std::string data;
|
|
|
|
|
data.resize(2);
|
|
|
|
|
data[0] = size>>8;
|
|
|
|
|
data[1] = size&0xff;
|
2014-03-05 14:23:12 +00:00
|
|
|
|
return check(send(0x80, 0x32, 0x00, efId, data)); // 0x08 or 0x80?
|
2013-10-21 07:10:46 +00:00
|
|
|
|
// old code was 0x80, manual says 0x08 (manual exactly says: "8xh")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//! Manufacturer use only
|
|
|
|
|
void initializeEeprom() {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
assert(!"not implemented");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//! Manufacturer use only
|
|
|
|
|
void initializeEnd() {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
assert(!"not implemented");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//! Performs cryptographic algorithms for authentication
|
|
|
|
|
void internalAuthenticate() {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
assert(!"not implemented");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//! Activates a package
|
|
|
|
|
void loadExecutable() {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
assert(!"not implemented");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//! Opens or closes a logical channel
|
|
|
|
|
std::string manageChannel(unsigned char mode, unsigned char channelId) {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
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() {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
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() {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
assert(!"not implemented");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//! Performs a cryptographic operation
|
|
|
|
|
void performSecurityOperation() {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
assert(!"not implemented");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//! Controls the command sequence transactions
|
|
|
|
|
void performTransactonOperation() {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
assert(!"not implemented");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//! Personalizer use only
|
|
|
|
|
void personalize() {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
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() {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
check(send(0x80, 0x10, 0x00, 0x00));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//! Installs / administrates / overwrites different objects
|
|
|
|
|
void putData() {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
assert(!"not implemented");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//! Read a BINARY file
|
2014-03-07 15:53:22 +00:00
|
|
|
|
std::string readBinary(std::string path="", unsigned short offset = 0) {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2014-03-07 15:53:22 +00:00
|
|
|
|
pcsc::Connection::Reader::Transaction lock(_reader);
|
|
|
|
|
if (path.size()) select(path);
|
2013-10-21 07:10:46 +00:00
|
|
|
|
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) {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
return check(send(0x00, 0xB2, record, (sfi&0xF8)|mode));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Read all records from a record oriented file
|
2014-03-07 15:53:22 +00:00
|
|
|
|
BerValues readBerFile(std::string path="") {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
BerValues content;
|
2013-11-06 12:24:52 +00:00
|
|
|
|
pcsc::Connection::Reader::Transaction lock(_reader);
|
2014-03-07 15:53:22 +00:00
|
|
|
|
if (path.size()) select(path);
|
2013-10-21 07:10:46 +00:00
|
|
|
|
while (true) {
|
|
|
|
|
std::string res(send(0x00, 0xB2, 0, NEXT_RECORD));
|
|
|
|
|
if (cardos::Commands::retCode(res)!=0x9000) break;
|
2014-03-07 15:53:22 +00:00
|
|
|
|
content += retData(res).substr(2);
|
2013-10-21 07:10:46 +00:00
|
|
|
|
}
|
|
|
|
|
return content;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//! Resets the error counter of a BS object to its maximum
|
|
|
|
|
void resetRetryCounter() {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
assert(!"not implemented");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//! Resets the security status of the current DF
|
|
|
|
|
void resetSecurityCounter() {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
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
|
2014-03-07 15:53:22 +00:00
|
|
|
|
BerValues selectFile(std::string file,
|
2013-10-21 07:10:46 +00:00
|
|
|
|
FileSelectionMode mode
|
|
|
|
|
= DF_OR_EF_USING_PATH_FROM_MF,
|
|
|
|
|
FileSelectionReturn ret
|
|
|
|
|
= RETURN_NOTHING) {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2014-03-07 15:53:22 +00:00
|
|
|
|
return BerValues(check(send(0x00, 0xA4, mode, ret, file)));
|
2013-10-21 07:10:46 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//! Sets the so-called Data_Field_Length parameter relevant for
|
|
|
|
|
//! the effective Command Data Field Length / Response Data
|
|
|
|
|
//! Field Length.
|
|
|
|
|
void setDataFieldLength() {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
assert(!"not implemented");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//! Enables or disables the usage of an existing transaction buffer
|
|
|
|
|
void setTransactionState() {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
assert(!"not implemented");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//! Signs a hashed input using a decryption key with the RSA_SIG
|
|
|
|
|
//! algorithm.
|
|
|
|
|
void signByDecryptionKey() {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
assert(!"not implemented");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//! Uninstalls a package of the smart card
|
|
|
|
|
void uninstallPackage() {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
assert(!"not implemented");
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-07 15:53:22 +00:00
|
|
|
|
// same as readBinary
|
|
|
|
|
// /// Read the previously file from smart card
|
|
|
|
|
// std::string readBinFile() {
|
|
|
|
|
// CRYPTOLOG("log");
|
|
|
|
|
// return check(send(0x00, 0xB0, 0x00, 0x00));
|
|
|
|
|
// }
|
2013-10-21 07:10:46 +00:00
|
|
|
|
|
|
|
|
|
//! Updates a BINARY file
|
|
|
|
|
void updateBinary(std::string data, unsigned short offset=0) {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
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) {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
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) {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
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) {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
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)
|
2013-11-06 12:24:52 +00:00
|
|
|
|
std::string sigG() {
|
2013-10-21 07:10:46 +00:00
|
|
|
|
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) {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
|
|
|
|
pcsc::Connection::Reader::Transaction lock(_reader);
|
2013-10-21 07:10:46 +00:00
|
|
|
|
selectSigG();
|
|
|
|
|
logon(transportPin(), pin);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Logon with SigG (Signaturgesetz) secure PIN
|
|
|
|
|
void logonSigG(std::string pin) {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
|
|
|
|
pcsc::Connection::Reader::Transaction lock(_reader);
|
2013-10-21 07:10:46 +00:00
|
|
|
|
selectSigG();
|
|
|
|
|
logon(sigGPin(), pin);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Logon with PKCS#15 user PUK to unlock user PIN
|
|
|
|
|
void logonPuk(std::string pin) {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
|
|
|
|
pcsc::Connection::Reader::Transaction lock(_reader);
|
2013-10-21 07:10:46 +00:00
|
|
|
|
selectMF();
|
|
|
|
|
logon(puk(), pin);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Logon with PKCS#15 user PIN
|
|
|
|
|
void logonPkcs15(std::string pin) {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
|
|
|
|
pcsc::Connection::Reader::Transaction lock(_reader);
|
2013-10-21 07:10:46 +00:00
|
|
|
|
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) {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
if (transportState()) { // first time use, reset transport state
|
2013-11-06 12:24:52 +00:00
|
|
|
|
pcsc::Connection::Reader::Transaction lock(_reader);
|
2013-10-21 07:10:46 +00:00
|
|
|
|
logonTransport(oldPin);
|
|
|
|
|
changeReferenceData(sigGPin(), newPin);
|
|
|
|
|
unsetTransportState();
|
|
|
|
|
} else { // ordinary PIN change
|
2013-11-06 12:24:52 +00:00
|
|
|
|
pcsc::Connection::Reader::Transaction lock(_reader);
|
|
|
|
|
logonSigG(oldPin);
|
|
|
|
|
selectSigG();
|
2013-10-21 07:10:46 +00:00
|
|
|
|
changeReferenceData(sigGPin(), newPin, oldPin);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Change PKCS#15 user PIN
|
|
|
|
|
void changePkcs15Pin(std::string newPin, std::string oldPin) {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
|
|
|
|
pcsc::Connection::Reader::Transaction lock(_reader);
|
2013-10-21 07:10:46 +00:00
|
|
|
|
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. */
|
2013-11-06 12:24:52 +00:00
|
|
|
|
void changePins(std::string newPin, std::string oldPin) {
|
|
|
|
|
CRYPTOLOG("log");
|
|
|
|
|
pcsc::Connection::Reader::Transaction lock(_reader);
|
|
|
|
|
if (transportState()) {
|
2014-03-04 12:11:51 +00:00
|
|
|
|
changeSigGPin(newPin, oldPin);
|
|
|
|
|
changePkcs15Pin(newPin, oldPin);
|
2013-11-06 12:24:52 +00:00
|
|
|
|
} else {
|
|
|
|
|
if (pkcs15PinRetries()!=-1) changePkcs15Pin(newPin, oldPin);
|
2013-10-21 07:10:46 +00:00
|
|
|
|
try {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
if (sigGPinRetries()!=-1) changeSigGPin(newPin, oldPin);
|
2013-10-21 07:10:46 +00:00
|
|
|
|
} catch (...) {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
// undo PKCS#15 PIN change
|
|
|
|
|
if (pkcs15PinRetries()!=-1) changePkcs15Pin(oldPin, newPin);
|
|
|
|
|
throw;
|
2013-10-21 07:10:46 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Select a file in the PKCS#15 part on the smart card
|
|
|
|
|
void selectPkcs15File(std::string file) {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
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) {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
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) {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
check(send(0x00, 0xA4, 0x08, 0x0C, crypto::hexToBin("3f00"+file)));
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-07 15:53:22 +00:00
|
|
|
|
/// Generic select file
|
|
|
|
|
void select(std::string path) {
|
|
|
|
|
CRYPTOLOG("log");
|
|
|
|
|
check(send(0x00, 0xA4, 0x08, 0x0C, crypto::hexToBin(path)));
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-21 07:10:46 +00:00
|
|
|
|
/// Select the PKCS#15 part on the smart card
|
|
|
|
|
void selectPkcs15() {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
check(send(0x00, 0xA4, 0x08, 0x0C, crypto::hexToBin("3f005015")));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Select the SigG (Signaturgesetz) part on the smart card
|
|
|
|
|
void selectSigG() {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
check(send(0x00, 0xA4, 0x08, 0x0C, crypto::hexToBin("3f001fff")));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Select the MFpart on the smart card
|
|
|
|
|
void selectMF() {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
check(send(0x00, 0xA4, 0x00, 0x0C));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// @return binary serial number
|
|
|
|
|
std::string serial() {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
return getDataChipProduction().serial;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// @return @c true if card is still in transport state
|
|
|
|
|
bool transportState() {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
|
|
|
|
pcsc::Connection::Reader::Transaction lock(_reader);
|
2013-10-21 07:10:46 +00:00
|
|
|
|
selectSigGFile("fe15");
|
2013-11-06 12:24:52 +00:00
|
|
|
|
return std::string(4, '\0')==readRecord();
|
2013-10-21 07:10:46 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Mark card as initiakized and no more in transport state
|
|
|
|
|
void unsetTransportState() {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
|
|
|
|
pcsc::Connection::Reader::Transaction lock(_reader);
|
2013-10-21 07:10:46 +00:00
|
|
|
|
selectSigGFile("fe15");
|
|
|
|
|
increase();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*! @return number of remaining transport PIN retries or -1 if locked */
|
|
|
|
|
int transportPinRetries() {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
|
|
|
|
pcsc::Connection::Reader::Transaction lock(_reader);
|
2013-10-21 07:10:46 +00:00
|
|
|
|
selectSigG();
|
|
|
|
|
return verify(transportPin());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*! @return number of remaining PKCS#15 PIN retries or -1 if locked */
|
|
|
|
|
int pkcs15PinRetries() {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
|
|
|
|
pcsc::Connection::Reader::Transaction lock(_reader);
|
2013-10-21 07:10:46 +00:00
|
|
|
|
selectMF();
|
|
|
|
|
return verify(pkcs15Pin());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*! @return number of remaining SigG PIN retries or -1 if locked */
|
|
|
|
|
int sigGPinRetries() {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
|
|
|
|
pcsc::Connection::Reader::Transaction lock(_reader);
|
2013-10-21 07:10:46 +00:00
|
|
|
|
selectSigG();
|
|
|
|
|
return verify(sigGPin());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*! @return number of remaining PUK retries or -1 if locked */
|
|
|
|
|
int pukRetries() {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
CRYPTOLOG("log");
|
|
|
|
|
pcsc::Connection::Reader::Transaction lock(_reader);
|
2013-10-21 07:10:46 +00:00
|
|
|
|
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
|
2013-11-06 12:24:52 +00:00
|
|
|
|
throw runtime_error("wrong APDU pass at least 4 bytes in hex");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//! @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
|
2013-11-06 12:24:52 +00:00
|
|
|
|
"Technical Error: "
|
|
|
|
|
" 1 Attempt to create more than 254 records in a file. "
|
2013-10-21 07:10:46 +00:00
|
|
|
|
" 2 Package uses SDK version which is not compatible to"
|
2013-11-06 12:24:52 +00:00
|
|
|
|
" API version "
|
2013-10-21 07:10:46 +00:00
|
|
|
|
" 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
|
2013-11-06 12:24:52 +00:00
|
|
|
|
"Internal assertion (invalid internal error) "
|
2013-10-21 07:10:46 +00:00
|
|
|
|
"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"<<std::hex<<ret;
|
|
|
|
|
return ss.str();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static unsigned int retCode(const std::string& res) {
|
|
|
|
|
if (res.size()>=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));
|
2013-11-06 12:24:52 +00:00
|
|
|
|
if (value==0x6300) throw wrong_pin(error(value));
|
|
|
|
|
if (value!=0x9000) throw runtime_error(error(value));
|
2013-10-21 07:10:46 +00:00
|
|
|
|
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)<<std::endl
|
|
|
|
|
<<"RC: "
|
|
|
|
|
<<crypto::binToHex(result.substr(result.size()-2, 2)));
|
|
|
|
|
if (result.size()>2) {
|
|
|
|
|
CARDOS_LOG("Data: "
|
|
|
|
|
<<crypto::binToHex(result.substr(0, result.size()-2)));
|
|
|
|
|
CARDOS_LOG("Text: "
|
|
|
|
|
<<txt.substr(0, result.size()-2));
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
CARDOS_LOG("RC: ?? Data: "<<crypto::binToHex(result)<<" Text: "<<txt);
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string send(char cla, char ins, char p1, char p2, std::string lc) {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
if (!_reader) throw runtime_error("no reader selected");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
CARDOS_LOG("APDU: cla="<<crypto::binToHex(cla)
|
|
|
|
|
<<" ins="<<crypto::binToHex(ins)
|
|
|
|
|
<<" p1="<<crypto::binToHex(p1)
|
|
|
|
|
<<" p2="<<crypto::binToHex(p2)
|
|
|
|
|
<<" lc="<<crypto::binToHex(lc));
|
|
|
|
|
return logResult(_reader->transmit(cla, ins, p1, p2, lc));
|
|
|
|
|
}
|
2013-11-06 12:24:52 +00:00
|
|
|
|
|
2013-10-21 07:10:46 +00:00
|
|
|
|
std::string send(char cla, char ins, char p1, char p2) {
|
2013-11-06 12:24:52 +00:00
|
|
|
|
if (!_reader) throw runtime_error("no reader selected");
|
2013-10-21 07:10:46 +00:00
|
|
|
|
CARDOS_LOG("APDU: cla="<<crypto::binToHex(cla)
|
|
|
|
|
<<" ins="<<crypto::binToHex(ins)
|
|
|
|
|
<<" p1="<<crypto::binToHex(p1)
|
|
|
|
|
<<" p2="<<crypto::binToHex(p2));
|
|
|
|
|
return logResult(_reader->transmit(cla, ins, p1, p2));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//@}
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
|
|
|
|
|
mrw::Shared<pcsc::Connection::Reader> _reader;
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
//@}
|
2013-11-06 12:24:52 +00:00
|
|
|
|
|
2013-10-21 07:10:46 +00:00
|
|
|
|
}
|
|
|
|
|
//@}
|
|
|
|
|
|
|
|
|
|
#endif
|