|
|
@ -19,7 +19,7 @@ |
|
|
|
// use e.g. #define CARDOS_LOG(X) std::clog<<X<<std::endl
|
|
|
|
// use e.g. #define CARDOS_LOG(X) std::clog<<X<<std::endl
|
|
|
|
#endif |
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
/** @defgroup gcardos C++ Access to Siemens CardOS V4.4
|
|
|
|
/** @defgroup gcardos C++ Access to Siemens CardOS 4.4
|
|
|
|
Implements APDUs for accessing Siemens CardOS V4.4 smartcards. */ |
|
|
|
Implements APDUs for accessing Siemens CardOS V4.4 smartcards. */ |
|
|
|
//@{
|
|
|
|
//@{
|
|
|
|
/// @defgroup cardosexception CardOS Exceptions
|
|
|
|
/// @defgroup cardosexception CardOS Exceptions
|
|
|
@ -157,33 +157,44 @@ namespace cardos { |
|
|
|
CHARACTER_STRING = 0x1D, |
|
|
|
CHARACTER_STRING = 0x1D, |
|
|
|
BMP_STRING = 0x1E, |
|
|
|
BMP_STRING = 0x1E, |
|
|
|
}; |
|
|
|
}; |
|
|
|
public: |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
BerValue(std::vector<BerValue> sequence): |
|
|
|
protected: // use BerValues instead
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
friend class BerValues; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
BerValue(const std::vector<BerValue>& sequence): |
|
|
|
_tag(PRIVATE|CONSTRUCTED|SEQUENCE), |
|
|
|
_tag(PRIVATE|CONSTRUCTED|SEQUENCE), |
|
|
|
_length(0), |
|
|
|
|
|
|
|
_sequence(sequence) { |
|
|
|
_sequence(sequence) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
BerValue(const std::string& content) { |
|
|
|
BerValue(std::string& content) { |
|
|
|
if (content.size()<2) |
|
|
|
if (content.size()<2) |
|
|
|
throw wrong_dataformat(content, "not a BER, header size too small: \"" |
|
|
|
throw wrong_dataformat(content, "not a BER, header size too small: \"" |
|
|
|
+crypto::binToHex(content)+"\""); |
|
|
|
+crypto::binToHex(content)+"\""); |
|
|
|
_tag = content[0]; |
|
|
|
_tag = content[0]; |
|
|
|
_length = content[1]; |
|
|
|
unsigned char length = content[1]; |
|
|
|
_value = content.substr(2, _length); |
|
|
|
_value = content.substr(2, length); |
|
|
|
if (std::string::size_type(_length)+2>content.size()) |
|
|
|
if (content.size()<std::string::size_type(length)+2) |
|
|
|
throw wrong_dataformat(content, "not a BER, content size too" |
|
|
|
throw wrong_dataformat(content, "not a BER, content size too" |
|
|
|
" small: \""+crypto::binToHex(_value)+"\""); |
|
|
|
" small: \""+crypto::binToHex(_value)+"\""); |
|
|
|
|
|
|
|
content.erase(0, 2+length); |
|
|
|
if (tagType()==END_OF_CONTENT) return; // done
|
|
|
|
if (tagType()==END_OF_CONTENT) return; // done
|
|
|
|
if (isContainer()) { |
|
|
|
if (isContainer()) |
|
|
|
for (std::string::size_type pos(0); pos+1<_value.size(); |
|
|
|
while (_value.size()) _sequence.push_back(BerValue(_value)); |
|
|
|
pos+=2+_value[pos+1]) { // recursively extract value
|
|
|
|
|
|
|
|
_sequence.push_back(BerValue(_value.substr(pos, 2+_value[pos+1]))); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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) { |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
unsigned char tagClass() { |
|
|
|
unsigned char tagClass() { |
|
|
|
return _tag&0xC0; |
|
|
|
return _tag&0xC0; |
|
|
|
} |
|
|
|
} |
|
|
@ -213,21 +224,31 @@ namespace cardos { |
|
|
|
return _sequence[i]; |
|
|
|
return _sequence[i]; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
std::string value() { |
|
|
|
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() { |
|
|
|
return _value; |
|
|
|
return _value; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
unsigned long toULong() { |
|
|
|
unsigned long ulong() { |
|
|
|
unsigned long res(0); |
|
|
|
return crypto::ulongFromBinary(_value); |
|
|
|
for (std::string::reverse_iterator it(_value.rbegin()); |
|
|
|
|
|
|
|
it!=_value.rend(); ++it) |
|
|
|
|
|
|
|
(res<<=8)+=(unsigned)*it; |
|
|
|
|
|
|
|
return res; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
std::string print(int indent=0, int indentStep = 4) { |
|
|
|
std::string print(int indent=0, int indentStep = 4) { |
|
|
|
std::stringstream ss; |
|
|
|
std::stringstream ss; |
|
|
|
ss<<std::string(indent*indentStep, ' ')<<'['; |
|
|
|
ss<<std::string(indent*indentStep, ' ')<<'['<<crypto::binToHex(_tag)<<'='; |
|
|
|
switch (tagClass()) { |
|
|
|
switch (tagClass()) { |
|
|
|
case UNIVERSAL: ss<<"UNIVERSAL,"; break; |
|
|
|
case UNIVERSAL: ss<<"UNIVERSAL,"; break; |
|
|
|
case APPLICATION: ss<<"APPLICATION,"; break; |
|
|
|
case APPLICATION: ss<<"APPLICATION,"; break; |
|
|
@ -279,7 +300,7 @@ namespace cardos { |
|
|
|
for (std::vector<BerValue>::iterator it(_sequence.begin()); |
|
|
|
for (std::vector<BerValue>::iterator it(_sequence.begin()); |
|
|
|
it!=_sequence.end(); ++it) { |
|
|
|
it!=_sequence.end(); ++it) { |
|
|
|
ss<<std::string((indent+1)*indentStep, ' ') |
|
|
|
ss<<std::string((indent+1)*indentStep, ' ') |
|
|
|
<<" ["<<i++<<"]{" |
|
|
|
<<"["<<i++<<"] {" |
|
|
|
<<std::endl |
|
|
|
<<std::endl |
|
|
|
<<it->print(indent+2, indentStep) |
|
|
|
<<it->print(indent+2, indentStep) |
|
|
|
<<std::endl |
|
|
|
<<std::endl |
|
|
@ -292,15 +313,14 @@ namespace cardos { |
|
|
|
for (std::string::const_iterator it(_value.begin()); |
|
|
|
for (std::string::const_iterator it(_value.begin()); |
|
|
|
it!=_value.end(); ++it) |
|
|
|
it!=_value.end(); ++it) |
|
|
|
ss<<(isprint(*it)?*it:'.'); |
|
|
|
ss<<(isprint(*it)?*it:'.'); |
|
|
|
|
|
|
|
ss<<"\""; |
|
|
|
} |
|
|
|
} |
|
|
|
ss<<"\""; |
|
|
|
|
|
|
|
return ss.str(); |
|
|
|
return ss.str(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private: |
|
|
|
private: |
|
|
|
|
|
|
|
|
|
|
|
unsigned char _tag; |
|
|
|
unsigned char _tag; |
|
|
|
unsigned char _length; |
|
|
|
|
|
|
|
std::string _value; |
|
|
|
std::string _value; |
|
|
|
std::vector<BerValue> _sequence; |
|
|
|
std::vector<BerValue> _sequence; |
|
|
|
|
|
|
|
|
|
|
@ -309,13 +329,24 @@ namespace cardos { |
|
|
|
/// Store a sequence of BerValue
|
|
|
|
/// Store a sequence of BerValue
|
|
|
|
class BerValues: public std::vector<BerValue> { |
|
|
|
class BerValues: public std::vector<BerValue> { |
|
|
|
public: |
|
|
|
public: |
|
|
|
BerValues& operator=(const std::string& content) { |
|
|
|
BerValues() {} |
|
|
|
|
|
|
|
BerValues(const std::string& content) { |
|
|
|
|
|
|
|
std::string contentCopy(content); |
|
|
|
|
|
|
|
while (contentCopy.size()) push_back(BerValue(contentCopy)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
BerValues& operator=(std::string& content) { |
|
|
|
clear(); |
|
|
|
clear(); |
|
|
|
push_back(BerValue(content)); |
|
|
|
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; |
|
|
|
return *this; |
|
|
|
} |
|
|
|
} |
|
|
|
BerValues& operator+=(const BerValue& value) { |
|
|
|
BerValues& operator+=(const BerValues& values) { |
|
|
|
push_back(value); |
|
|
|
insert(end(), values.begin(), values.end()); |
|
|
|
return *this; |
|
|
|
return *this; |
|
|
|
} |
|
|
|
} |
|
|
|
std::string print(int indent=0, int indentStep = 4) { |
|
|
|
std::string print(int indent=0, int indentStep = 4) { |
|
|
@ -327,7 +358,7 @@ namespace cardos { |
|
|
|
ss<<"{"<<std::endl; |
|
|
|
ss<<"{"<<std::endl; |
|
|
|
for (iterator it(begin()); it!=end(); ++it) { |
|
|
|
for (iterator it(begin()); it!=end(); ++it) { |
|
|
|
ss<<std::string((indent+1)*indentStep, ' ') |
|
|
|
ss<<std::string((indent+1)*indentStep, ' ') |
|
|
|
<<" ["<<i++<<"]{" |
|
|
|
<<"["<<i++<<"] {" |
|
|
|
<<std::endl |
|
|
|
<<std::endl |
|
|
|
<<it->print(indent+2, indentStep) |
|
|
|
<<it->print(indent+2, indentStep) |
|
|
|
<<std::endl |
|
|
|
<<std::endl |
|
|
@ -590,13 +621,30 @@ namespace cardos { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//! Creates a file (only EF or DF)
|
|
|
|
//! Creates a file (only EF or DF)
|
|
|
|
void createFile(BerValue) { |
|
|
|
void createFile(std::string path="", const std::string data="") { |
|
|
|
CRYPTOLOG("log"); |
|
|
|
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,
|
|
|
|
// check(send(0x00, 0xE0, 0x00, 0x00, BerValue(0x62h,
|
|
|
|
// data).raw()));
|
|
|
|
// data).raw()));
|
|
|
|
assert(!"not implemented"); |
|
|
|
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
|
|
|
|
//! Deactivates a file or a file tree
|
|
|
|
void deactivateFile() { |
|
|
|
void deactivateFile() { |
|
|
|
CRYPTOLOG("log"); |
|
|
|
CRYPTOLOG("log"); |
|
|
@ -618,9 +666,19 @@ namespace cardos { |
|
|
|
enum FileTypes {DF=0x00, EF=0x01, DF_EF=0x02}; |
|
|
|
enum FileTypes {DF=0x00, EF=0x01, DF_EF=0x02}; |
|
|
|
|
|
|
|
|
|
|
|
//! Reads file information of EFs and/or DFs in the current DF
|
|
|
|
//! Reads file information of EFs and/or DFs in the current DF
|
|
|
|
BerValue directory(FileTypes types, unsigned char offset=0) { |
|
|
|
BerValues directory(std::string path, FileTypes types=DF_EF, |
|
|
|
|
|
|
|
unsigned char offset=0) { |
|
|
|
CRYPTOLOG("log"); |
|
|
|
CRYPTOLOG("log"); |
|
|
|
return BerValue(check(send(0x80, 0x16, types, offset))); |
|
|
|
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.
|
|
|
|
//! Enables an already loaded and activated but disabled license package.
|
|
|
@ -832,8 +890,10 @@ namespace cardos { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//! Read a BINARY file
|
|
|
|
//! Read a BINARY file
|
|
|
|
std::string readBinary(unsigned short offset = 0) { |
|
|
|
std::string readBinary(std::string path="", unsigned short offset = 0) { |
|
|
|
CRYPTOLOG("log"); |
|
|
|
CRYPTOLOG("log"); |
|
|
|
|
|
|
|
pcsc::Connection::Reader::Transaction lock(_reader); |
|
|
|
|
|
|
|
if (path.size()) select(path); |
|
|
|
return check(send(0x00, 0xB0, offset>>8, offset&0xFF)); |
|
|
|
return check(send(0x00, 0xB0, offset>>8, offset&0xFF)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -855,14 +915,15 @@ namespace cardos { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Read all records from a record oriented file
|
|
|
|
/// Read all records from a record oriented file
|
|
|
|
BerValues readBerFile() { |
|
|
|
BerValues readBerFile(std::string path="") { |
|
|
|
CRYPTOLOG("log"); |
|
|
|
CRYPTOLOG("log"); |
|
|
|
BerValues content; |
|
|
|
BerValues content; |
|
|
|
pcsc::Connection::Reader::Transaction lock(_reader); |
|
|
|
pcsc::Connection::Reader::Transaction lock(_reader); |
|
|
|
|
|
|
|
if (path.size()) select(path); |
|
|
|
while (true) { |
|
|
|
while (true) { |
|
|
|
std::string res(send(0x00, 0xB2, 0, NEXT_RECORD)); |
|
|
|
std::string res(send(0x00, 0xB2, 0, NEXT_RECORD)); |
|
|
|
if (cardos::Commands::retCode(res)!=0x9000) break; |
|
|
|
if (cardos::Commands::retCode(res)!=0x9000) break; |
|
|
|
content += BerValue(retData(res).substr(2)); |
|
|
|
content += retData(res).substr(2); |
|
|
|
} |
|
|
|
} |
|
|
|
return content; |
|
|
|
return content; |
|
|
|
} |
|
|
|
} |
|
|
@ -896,13 +957,13 @@ namespace cardos { |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
//! Selects a file
|
|
|
|
//! Selects a file
|
|
|
|
BerValue selectFile(std::string file, |
|
|
|
BerValues selectFile(std::string file, |
|
|
|
FileSelectionMode mode |
|
|
|
FileSelectionMode mode |
|
|
|
= DF_OR_EF_USING_PATH_FROM_MF, |
|
|
|
= DF_OR_EF_USING_PATH_FROM_MF, |
|
|
|
FileSelectionReturn ret |
|
|
|
FileSelectionReturn ret |
|
|
|
= RETURN_NOTHING) { |
|
|
|
= RETURN_NOTHING) { |
|
|
|
CRYPTOLOG("log"); |
|
|
|
CRYPTOLOG("log"); |
|
|
|
return BerValue(check(send(0x00, 0xA4, mode, ret, file))); |
|
|
|
return BerValues(check(send(0x00, 0xA4, mode, ret, file))); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//! Sets the so-called Data_Field_Length parameter relevant for
|
|
|
|
//! Sets the so-called Data_Field_Length parameter relevant for
|
|
|
@ -932,11 +993,12 @@ namespace cardos { |
|
|
|
assert(!"not implemented"); |
|
|
|
assert(!"not implemented"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Read the previously file from smart card
|
|
|
|
// same as readBinary
|
|
|
|
std::string readBinFile() { |
|
|
|
// /// Read the previously file from smart card
|
|
|
|
CRYPTOLOG("log"); |
|
|
|
// std::string readBinFile() {
|
|
|
|
return check(send(0x00, 0xB0, 0x00, 0x00)); |
|
|
|
// CRYPTOLOG("log");
|
|
|
|
} |
|
|
|
// return check(send(0x00, 0xB0, 0x00, 0x00));
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
//! Updates a BINARY file
|
|
|
|
//! Updates a BINARY file
|
|
|
|
void updateBinary(std::string data, unsigned short offset=0) { |
|
|
|
void updateBinary(std::string data, unsigned short offset=0) { |
|
|
@ -1123,6 +1185,12 @@ namespace cardos { |
|
|
|
check(send(0x00, 0xA4, 0x08, 0x0C, crypto::hexToBin("3f00"+file))); |
|
|
|
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
|
|
|
|
/// Select the PKCS#15 part on the smart card
|
|
|
|
void selectPkcs15() { |
|
|
|
void selectPkcs15() { |
|
|
|
CRYPTOLOG("log"); |
|
|
|
CRYPTOLOG("log"); |
|
|
|