/*! @file
@ id $ Id : cardos . hxx 3826 2012 - 05 - 21 12 : 25 : 29 Z 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
# ifdef DEBUG_SECRETS
# define CARDOS_LOG(X) std::clog<<X<<std::endl
# else
# define CARDOS_LOG(X) // no logging by default
// use e.g. #define CARDOS_LOG(X) std::clog<<X<<std::endl
# endif
# endif
/** @defgroup gcardos C++ Access to Siemens CardOS 4.4
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
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 :
wrong_pin ( const std : : string & s ) throw ( ) : exception ( " wrong pin " + s ) { }
} ;
//----------------------------------------------------------------------------
class pin_locked : public exception {
public :
pin_locked ( ) throw ( ) : exception ( " pin is locked and cannot be changed " ) { }
} ;
//----------------------------------------------------------------------------
class wrong_puk : public wrong_pin {
public :
wrong_puk ( const std : : string & s ) throw ( ) : wrong_pin ( " wrong puk " + s ) { }
} ;
//----------------------------------------------------------------------------
class wrong_result : public exception {
public :
wrong_result ( const std : : string & reason , const std : : string & data ) throw ( ) :
exception ( " wrong result, " + reason + " : " + crypto : : hex ( data ) ) { }
wrong_result ( const std : : string & reason ) throw ( ) :
exception ( " wrong result, " + reason ) { }
} ;
//----------------------------------------------------------------------------
class runtime_error : public exception {
public :
runtime_error ( const std : : string & reason ) throw ( ) :
exception ( " runtime error, " + reason ) { }
runtime_error ( const std : : string & reason , const std : : string & data ) throw ( ) :
exception ( " runtime error, " + reason + " : " + crypto : : hex ( data ) ) { }
} ;
//----------------------------------------------------------------------------
class unexpected_challenge_length : public exception {
public :
unexpected_challenge_length ( const std : : string & data ) throw ( ) :
exception ( " challenge should be 8 bytes, challenge is: "
+ crypto : : hex ( data ) ) { }
} ;
//----------------------------------------------------------------------------
class card_transmission_failed : public exception {
public :
card_transmission_failed ( const std : : string & position ,
const std : : string & reason ) throw ( ) :
exception ( " transmission to card failed: " + position
+ " reason: " + reason ) {
}
} ;
//----------------------------------------------------------------------------
class wrong_dataformat : public exception {
public :
wrong_dataformat ( const std : : string & data ,
const std : : string & reason ) throw ( ) :
exception ( " wrong dataformat: " + crypto : : hex ( data )
+ " reason: " + reason ) {
}
} ;
//----------------------------------------------------------------------------
class too_large_for_tlv : public wrong_dataformat {
public :
too_large_for_tlv ( const std : : string & data ) throw ( ) :
wrong_dataformat ( data , " data size too long for TLV " ) {
}
} ;
//----------------------------------------------------------------------------
class array_range : public exception {
public :
array_range ( unsigned long i , unsigned long j ) throw ( ) :
exception ( " array index out of range: " + mrw : : string ( i )
+ " ; length: " + mrw : : string ( j ) ) {
}
} ;
//@}
//============================================================================
/// @addtogroup cardoslib
//@{
class BerValue {
public :
enum Class {
UNIVERSAL = 0x00 ,
APPLICATION = 0x40 ,
CONTEXT_SPECIFIC = 0x80 ,
PRIVATE = 0xC0
} ;
enum PC {
PRIMITIVE = 0x00 ,
CONSTRUCTED = 0x20
} ;
enum Type {
END_OF_CONTENT = 0x00 ,
BOOLEAN = 0x01 ,
INTEGER = 0x02 ,
BIT_STRING = 0x03 ,
OCTET_STRING = 0x04 ,
EMPTY = 0x05 ,
OBJECT_IDENTIFIER = 0x06 ,
OBJECT_DESCRIPTOR = 0x07 ,
EXTERNAL = 0x08 ,
REAL = 0x09 ,
ENUMERATED = 0x0A ,
EMBEDDED_PDV = 0x0B ,
UTF8_STRING = 0x0C ,
RELATIVE_OID = 0x0D ,
SEQUENCE = 0x10 ,
SET = 0x11 ,
NUMERIC_STRING = 0x12 ,
PRINTABLE_STRING = 0x13 ,
T61_STRING = 0x14 ,
VIDEOTEX_STRING = 0x15 ,
IA5_STRING = 0x16 ,
UTC_TIME = 0x17 ,
GENERALIZED_TIME = 0x18 ,
GRAPHIC_STRING = 0x19 ,
VISIBLE_STRING = 0x1A ,
GENERAL_STRING = 0x1B ,
UNIVERSAL_STRING = 0x1C ,
CHARACTER_STRING = 0x1D ,
BMP_STRING = 0x1E ,
} ;
protected : // use BerValues instead
friend class BerValues ;
BerValue ( const std : : vector < BerValue > & sequence ) :
_tag ( PRIVATE | CONSTRUCTED | SEQUENCE ) ,
_sequence ( sequence ) {
}
BerValue ( std : : string & content ) {
set ( content ) ;
}
void set ( std : : string & content ) {
if ( content . size ( ) < 2 )
throw wrong_dataformat ( content , " not a BER, header size too small: \" "
+ crypto : : binToHex ( content ) + " \" " ) ;
_tag = content [ 0 ] ;
unsigned char length = content [ 1 ] ;
_value = content . substr ( 2 , length ) ;
if ( content . size ( ) < std : : string : : size_type ( length ) + 2 )
throw wrong_dataformat ( content , " not a BER, content size too "
" small: \" " + crypto : : binToHex ( _value ) + " \" " ) ;
content . erase ( 0 , 2 + length ) ;
if ( tagType ( ) = = END_OF_CONTENT ) return ; // done
if ( isContainer ( ) )
while ( _value . size ( ) ) _sequence . push_back ( BerValue ( _value ) ) ;
}
public :
BerValue ( const std : : string & content ) {
std : : string contentCopy ( content ) ;
set ( contentCopy ) ;
}
BerValue ( unsigned char t , const std : : string & v ) :
_tag ( t ) , _value ( v ) {
if ( isContainer ( ) )
while ( _value . size ( ) ) _sequence . push_back ( BerValue ( _value ) ) ;
}
BerValue ( unsigned char t , const std : : vector < BerValue > & vs ) :
_tag ( t ) , _sequence ( vs ) {
if ( ! isContainer ( ) )
throw runtime_error ( " BER tag 0x " + crypto : : binToHex ( _tag )
+ " is not a container " ) ;
}
unsigned char tagClass ( ) {
return _tag & 0xC0 ;
}
unsigned char tagPC ( ) {
return _tag & 0x20 ;
}
unsigned char tagType ( ) {
return _tag & 0x1F ;
}
bool isContainer ( ) {
return tagPC ( ) = = CONSTRUCTED ;
}
std : : vector < BerValue > : : size_type size ( ) {
return _sequence . size ( ) ;
}
unsigned char tag ( ) {
return _tag ;
}
BerValue operator [ ] ( std : : vector < BerValue > : : size_type i ) {
if ( i > = _sequence . size ( ) ) throw array_range ( i , _sequence . size ( ) ) ;
return _sequence [ i ] ;
}
std : : string binary ( ) {
std : : string res ;
res . push_back ( _tag ) ;
if ( isContainer ( ) ) {
std : : string seq ;
for ( std : : vector < BerValue > : : iterator it ( _sequence . begin ( ) ) ;
it ! = _sequence . end ( ) ; + + it ) {
seq + = it - > binary ( ) ;
}
if ( seq . size ( ) > 255 ) throw too_large_for_tlv ( seq ) ;
res + = ( char ) seq . size ( ) ;
res + = seq ;
} else {
if ( _value . size ( ) > 255 ) throw too_large_for_tlv ( _value ) ;
res + = ( char ) _value . size ( ) ;
res + = _value ;
}
return res ;
}
std : : string string ( ) {
return _value ;
}
std : : string hex ( ) {
return crypto : : hex ( _value ) ;
}
unsigned long ulong ( ) {
return crypto : : ulongFromBinary ( _value ) ;
}
std : : string print ( int indent = 0 , int indentStep = 4 ) {
std : : stringstream ss ;
ss < < std : : string ( indent * indentStep , ' ' ) < < ' [ ' < < crypto : : binToHex ( _tag )
< < ' = ' ;
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 , ' ' )
< < " [ " < < i + + < < " ] { "
< < 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 : ' . ' ) ;
ss < < " \" " ;
}
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 :
BerValues ( ) { }
BerValues ( const std : : string & content ) {
std : : string contentCopy ( content ) ;
while ( contentCopy . size ( ) ) push_back ( BerValue ( contentCopy ) ) ;
}
BerValues & operator = ( std : : string & content ) {
clear ( ) ;
std : : string contentCopy ( content ) ;
while ( contentCopy . size ( ) ) push_back ( BerValue ( contentCopy ) ) ;
return * this ;
}
BerValues & operator + = ( const std : : string & content ) {
std : : string contentCopy ( content ) ;
while ( contentCopy . size ( ) ) push_back ( BerValue ( contentCopy ) ) ;
return * this ;
}
BerValues & operator + = ( const BerValue & value ) {
push_back ( value ) ;
return * this ;
}
BerValues & operator + = ( const BerValues & values ) {
insert ( end ( ) , values . begin ( ) , values . end ( ) ) ;
return * this ;
}
std : : string binary ( ) {
std : : string res ;
for ( BerValues : : iterator it ( begin ( ) ) ; it ! = end ( ) ; + + it )
res + = it - > binary ( ) ;
return res ;
}
std : : string print ( int indent = 0 , int indentStep = 4 ) {
std : : stringstream ss ;
if ( size ( ) = = 1 ) {
ss < < 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 , ' ' )
< < " [ " < < i + + < < " ] { "
< < std : : endl
< < it - > print ( indent + 2 , indentStep )
< < std : : endl
< < std : : string ( ( indent + 1 ) * indentStep , ' ' ) < < ' } '
< < std : : endl ;
}
ss < < std : : string ( indent * indentStep , ' ' ) < < ' } ' ;
}
return ss . str ( ) ;
}
} ;
//============================================================================
/// Implements CardOS V4.4 commands.
/** Directly sends CardOS V4.4 commands to a smart card using APDUs. */
class Commands {
public :
//------------------------------------------------------------------------
/// @name Setup Smart Card Reader
//@{
/// Uninitialized class
/** Use @ref reader to setup assign smart card reader. */
Commands ( ) { }
/// Initialize with given smart card reader.
Commands ( std : : shared_ptr < pcsc : : Connection : : Reader > r ) :
_reader ( r ) {
}
/// Set smart card reader.
void reader ( std : : shared_ptr < pcsc : : Connection : : Reader > r ) {
_reader = r ;
}
//@}
//------------------------------------------------------------------------
/// @name Basic CardOS Commands
//@{
//! Activates a deactivated file or file tree.
/*! This command reactivates the current file in the file
system . If there were any child files ( file tree ) , they can
subsequently be selected and thus used again . The command
will not return an error , if the current file is already
active .
@ pre The command can only be executed , if the right
referenced by the file ’ s AC ACTIVATE is granted in the
security status of the current DF . The command cannot
be applied to CODE files . */
void activateFile ( ) {
CRYPTOLOG ( " log " ) ;
check ( send ( 0x00 , 0x44 , 0x00 , 0x00 ) ) ;
}
//! Manages the transaction buffer for command transactions
//! and/or command sequence transactions
/*! The command allocates or frees a transaction buffer
In case of mode Allocate a possibly existing old buffer
block is marked as unused and a new block with size HI - LO is
initialized . The ID of the new buffer is returned as
response . After the command has been executed successfully ,
all the EEPROM contents , which will be affected by a command
with the setting AutoTR = ON , will be stored in the allocated
EEPROM buffer , so that in case of an unforeseen interruption
the former EEPROM state can be restored after the next reset
( see also SET TRANSACTION STATE command ) .
In case of mode Free , the buffer with the ID in P2 will not
be used anymore .
The recommended buffer size is 300 bytes . A small buffer
will increase the EEPROM stress .
Buffers should be allocated and freed again by the
application to reduce the EEPROM stress .
< table >
< caption > Bytes P1 - P2 < / caption >
< 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 > < td > < / td > < / tr >
< 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 >
@ pre The command can only be executed , if the right
referenced by the MF ’ s AC ALLOCATE is granted .
@ returns 1 byte : ID of allocated buffer
@ see freeTransactionBuffer */
std : : string allocateTransactionBuffer ( unsigned short size ) {
CRYPTOLOG ( " log " ) ;
if ( size > 0x7FFF ) throw runtime_error ( " requested buffer too large " ) ;
return check ( send ( 0x80 , 0x12 , ( unsigned char ) ( 8 | size > > 8 ) ,
( unsigned char ) ( size & 0xFF ) ) ) ;
}
//! Free transaction buffer
/*! @see allocateTransactionBuffer */
void freeTransactionBuffer ( unsigned char id ) {
CRYPTOLOG ( " log " ) ;
check ( send ( 0x80 , 0x12 , 0x00 , id ) ) ;
}
//! Creates a new record in a record oriented file
/*! This command creates a new record in the currently selected
file , or , if P2 is not 00 h , in the file , which is referenced
by the Short File Identifier ( SFI ) . The command can only be
used on LINEAR FIXED , CYCLIC FIXED and LINEAR TLV files .
In the parameter EF - ID a file ID can be specified with an
SFI . See table 3.4 - 1 for its format . The thus referenced
file will then be selected .
EF - ID Byte P2 :
- 0x00 → use current EF
- bits 7 - 3 = ppppp , bits 2 - 0 = 0 → use SFI ppppp ( 11111 not allowed )
- other value → rfu
If an SFI is present the actual FID to be used is built by
adding the constant value FE00h to the specified SFI .
The Record_Data contain the contents of the new record . The
length of the Record_Data must not be more than 254 bytes .
For LINEAR FIXED or CYCLIC FIXED files the length of the
Record_Data must match the length , which was specified by
the belonging CREATE FILE command .
For LINEAR TLV files the command data field must have a
valid TLV format .
If there is not sufficient file memory to store the new
record , an error message is generated for all files , except
for a CYCLIC FIXED file , where the previous record will be
overwritten ( same behavior as UPDATE RECORD in PREV mode ) .
For all of file types the newly written record becomes the
current record . For CYCLIC FIXED files the newly written
record becomes the logical first record .
For LINEAR TLV files it is assumed that the length field of
the record consists of one byte . The tag byte is not
interpreted by APPEND RECORD .
@ pre The command can only be executed , if the right
referenced by the file ’ s AC APPEND is granted in the
security status of the current DF . */
void appendRecord ( unsigned char efId , std : : string data ) {
CRYPTOLOG ( " log " ) ;
check ( send ( 0x00 , 0xE2 , 0x00 , efId , data ) ) ;
}
//! Computes a digital signature of internally hashed data
//! provided by the application and the card itself in order to
//! authenticate the card to the application.
/*! The CARD AUTHENTICATE command allows to check a card’s
authenticity before personalization takes place . It returns
a digital signature , which is computed over an internal hash
value .
Inputs for the hash value calculation are :
- The System_Challenge ( s . Command Data Field )
- the command header of the CARD AUTHENTICATE command
( CLA , INS , P1 , P2 )
- the global life cycle phase ( s . GET DATA command , mode 83 h )
- the number of loaded Packages ( s . GET DATA command , mode 88 h )
- the system internal parameters ( 9 bytes consisting 22 h ) and
- optionally , the Chip Unique Identification Number
( as indicated in mode byte P1 , @ ref getData , mode 81 h ,
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 01 h 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 >
< 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 >
< tr >
< td > xxh … xxh < / td >
< td > 80 h 88 h Mode 00 h < / td >
< td > xxh < / td >
< td > 22 h … 22 h < / 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 >
@ pre The System_Challenge provided by the application must
be greater or equal to 16 bytes . Since the command
does not support chaining , the length of the
System_Challenge is limited by the IO buffer size .
@ note The application must use the corresponding RSA2 public
key to verify the Digital Signature received from the
card and thus establishes the card ’ s authenticity . The
RSA2 public key is contained in the CardOS V4 .4 PRNs .
@ return Digital Signature */
std : : string cardAuthenticate ( bool hashInclIdentNum ,
std : : string systemChallange ) {
CRYPTOLOG ( " log " ) ;
return check ( send ( 0x80 , 0x88 , hashInclIdentNum ? 0x01 : 0x02 , 0x00 ,
systemChallange ) ) ;
}
//! Changes the object data of any BS object except for PIN TEST objects
void changeKeyData ( ) {
CRYPTOLOG ( " log " ) ;
assert ( ! " not implemented " ) ;
}
//! Changes the object data of a PIN TEST object
/** Changes a PIN. */
void changeReferenceData ( unsigned char id , std : : string newData ,
std : : string oldData = std : : string ( ) ) {
CRYPTOLOG ( " log " ) ;
check ( send ( 0x00 , 0x24 , oldData . size ( ) ? 0x00 : 0x01 , 0x80 | id ,
oldData + newData ) ) ;
}
//! Changes the data of a system key to the new key data
//! provided with the command.
void changeSystemKey ( ) {
CRYPTOLOG ( " log " ) ;
assert ( ! " not implemented " ) ;
}
//! Creates a file (only EF or DF)
void createFile ( std : : string /*path*/ = " " , const std : : string & /*data*/ = " " ) {
CRYPTOLOG ( " log " ) ;
// pcsc::Connection::Reader::Transaction lock(_reader);
// if (path.size()) select(path);
// check(send(0x00, 0xE0, 0x00, 0x00,
// check(send(0x00, 0xE0, 0x00, 0x00, BerValue(0x62h,
// data).raw()));
assert ( ! " not implemented " ) ;
}
//! Creates a EF file
void createBinary ( std : : string path = " " , std : : string id = " " ,
const std : : string data = " " ) {
CRYPTOLOG ( " log " ) ;
pcsc : : Connection : : Reader : : Transaction lock ( _reader ) ;
if ( path . size ( ) ) select ( path ) ;
BerValues c ;
c + = BerValue ( 0x80 , crypto : : toBinary ( data . size ( ) ) ) ;
c + = BerValue ( 0x82 , crypto : : hexToBin ( " 01 " ) ) ;
std : : string idbin ( crypto : : hexToBin ( id ) ) ;
if ( idbin . size ( ) ! = 2 ) throw runtime_error ( " file id must be two bytes " ) ;
c + = BerValue ( 0x83 , idbin ) ;
c + = BerValue ( 0x85 , std : : string ( 1 , ( char ) ( 1 < < 7 ) ) ) ;
c + = BerValue ( 0x86 , crypto : : hexToBin ( " 00000000000000 " ) ) ;
check ( send ( 0x00 , 0xE0 , 0x00 , 0x00 , BerValue ( 0x62 , c ) . binary ( ) ) ) ;
updateBinary ( data ) ;
}
//! Deactivates a file or a file tree
void deactivateFile ( ) {
CRYPTOLOG ( " log " ) ;
assert ( ! " not implemented " ) ;
}
//! Decreases a record value
void decrease ( ) {
CRYPTOLOG ( " log " ) ;
assert ( ! " not implemented " ) ;
}
//! Deletes a file (DF or EF)
void deleteFile ( ) {
CRYPTOLOG ( " log " ) ;
assert ( ! " not implemented " ) ;
}
enum FileTypes { DF = 0x00 , EF = 0x01 , DF_EF = 0x02 } ;
//! Reads file information of EFs and/or DFs in the current DF
BerValues directory ( std : : string path , FileTypes types = DF_EF ,
unsigned char offset = 0 ) {
CRYPTOLOG ( " log " ) ;
unsigned char o ( offset ) ;
BerValues content ;
pcsc : : Connection : : Reader : : Transaction lock ( _reader ) ;
if ( path . size ( ) ) select ( path ) ;
/*while (o<(unsigned char)-1)*/ { /// @todo read until done
std : : string res ( check ( send ( 0x80 , 0x16 , types , o + + ) ) ) ;
//if (cardos::Commands::retCode(res)!=0x9000) break;
content + = res ;
}
return content ;
}
//! Enables an already loaded and activated but disabled license package.
void enablePackage ( ) {
CRYPTOLOG ( " log " ) ;
assert ( ! " not implemented " ) ;
}
//! Erases the file system in the EEPROM.
void eraseFiles ( ) {
CRYPTOLOG ( " log " ) ;
assert ( ! " not implemented " ) ;
}
//! Performs a challenge/response test
void externalAuthenticate ( ) {
CRYPTOLOG ( " log " ) ;
assert ( ! " not implemented " ) ;
}
//! Changes the life cycle phase from MANUFACTURING to
//! ADMINISTRATION after creation of the MF or changes only from
//! MANUFACTURING to INITIALIZATION.
void format ( ) {
CRYPTOLOG ( " log " ) ;
assert ( ! " not implemented " ) ;
}
//! Generates a key pair for the RSA/RSA2 algorithms within the card
void generateKeyPair ( ) {
CRYPTOLOG ( " log " ) ;
assert ( ! " not implemented " ) ;
}
//! Generates an internal random number (i.e. SC_Challenge)
void getChallange ( ) {
CRYPTOLOG ( " log " ) ;
assert ( ! " not implemented " ) ;
}
//! Reads system information
std : : string getData ( unsigned char mode ) {
CRYPTOLOG ( " log " ) ;
return check ( send ( 0x00 , 0xCA , 0x01 , mode ) ) ;
}
//! Product name, version, release date, copyright string
std : : string getDataProductName ( ) {
CRYPTOLOG ( " log " ) ;
return getData ( 0x80 ) ;
}
struct ChipProductionData {
std : : string serial ;
unsigned char type ;
std : : string id ;
} ;
//! Chip production data as supplied by Infineon, PROM area
ChipProductionData getDataChipProduction ( ) {
CRYPTOLOG ( " log " ) ;
std : : string code ( getData ( 0x81 ) ) ;
ChipProductionData res = {
code . substr ( 8 , 8 ) ,
( unsigned char ) code [ 9 ] ,
code . substr ( 11 , 6 )
} ;
return res ;
}
//! Transmits an external random number (i.e. System_Random) to
//! the smart card
/*! The command transmits an external random number of n bytes
the so - called System_Random to the smart card . System_Random
will be used for the next response - SM ( SIG or ENC_SIG )
calculation . Valid values for n in short APDU mode are in
the range 1 – 256. In extended APDU mode nmax is dependent
on the preset data field length ( nmax = data field length ) .
The external random number System_Random can be used only
once . System_Random is valid during the same smart card
session until it has been used by response - SM or until it is
overwritten by a new GIVE RANDOM command .
The external random number is stored in the XRAM of the
smart card . */
void giveRandom ( std : : string random ) {
CRYPTOLOG ( " log " ) ;
check ( send ( 0x80 , 0x86 , 0x00 , 0x00 , random ) ) ;
}
//! Increases a record value
/** This command increases the value of the current record of a
cyclic fixed file by the @ p size .
EF - ID Byte P2 :
- 0x00 → use current EF
- bits 7 - 3 = ppppp , bits 2 - 0 = 0 → use SFI ppppp ( 11111 not allowed )
- other value → rfu */
std : : string increase ( unsigned char efId = 0 , unsigned short size = 1 ) {
CRYPTOLOG ( " log " ) ;
std : : string data ;
data . resize ( 2 ) ;
data [ 0 ] = char ( ( unsigned char ) ( size > > 8 ) ) ;
data [ 1 ] = char ( ( unsigned char ) ( size & 0xff ) ) ;
return check ( send ( 0x80 , 0x32 , 0x00 , efId , data ) ) ; // 0x08 or 0x80?
// old code was 0x80, manual says 0x08 (manual exactly says: "8xh")
}
//! Manufacturer use only
void initializeEeprom ( ) {
CRYPTOLOG ( " log " ) ;
assert ( ! " not implemented " ) ;
}
//! Manufacturer use only
void initializeEnd ( ) {
CRYPTOLOG ( " log " ) ;
assert ( ! " not implemented " ) ;
}
//! Performs cryptographic algorithms for authentication
void internalAuthenticate ( ) {
CRYPTOLOG ( " log " ) ;
assert ( ! " not implemented " ) ;
}
//! Activates a package
void loadExecutable ( ) {
CRYPTOLOG ( " log " ) ;
assert ( ! " not implemented " ) ;
}
//! Opens or closes a logical channel
std : : string manageChannel ( unsigned char mode , unsigned char channelId ) {
CRYPTOLOG ( " log " ) ;
return check ( send ( 0x00 , 0x70 , mode , channelId ) ) ;
}
//! Loads a CSE (RESTORE) or sets a component of the CSE (SET)
//! and/or sets an extended headerlist
void manageSecureEnvironment ( ) {
CRYPTOLOG ( " log " ) ;
assert ( ! " not implemented " ) ;
}
//! Performs a challenge/response test and a subsequent
//! MAC/Signature calculation and, depending on the input, a
//! session key derivation.
void mutualAuthenticate ( ) {
CRYPTOLOG ( " log " ) ;
assert ( ! " not implemented " ) ;
}
//! Performs a cryptographic operation
void performSecurityOperation ( ) {
CRYPTOLOG ( " log " ) ;
assert ( ! " not implemented " ) ;
}
//! Controls the command sequence transactions
void performTransactonOperation ( ) {
CRYPTOLOG ( " log " ) ;
assert ( ! " not implemented " ) ;
}
//! Personalizer use only
void personalize ( ) {
CRYPTOLOG ( " log " ) ;
assert ( ! " not implemented " ) ;
}
//! Changes from life cycle phase ADMINISTRATION to OPERATIONAL
//! and vice versa.
/*! The command changes the global life cycle phase of the smart
card from ADMINISTRATION to OPERATIONAL . This change is
permanently valid for all DFs and will be stored in EEPROM ,
i . e . this life cycle phase is still valid after a reset of
the smart card .
The change from life cycle phase OPERATIONAL to
ADMINISTRATION , however , is only temporary and valid only
for the current DF ( current life cycle phase ) The life cycle
phase is set back to OPERATIONAL when a context change
( selection of a different DF ) occurs or after a reset of the
smart card . If the current life cycle phase shall be changed
to ADMINISTRATION in several DFs , a PHASE CONTROL command
has to be issued each time after the selection of one of
those DFs . The temporary change to ADMINISTRATION in one DF
can also be undone ( without reset ) with another PHASE
CONTROL command .
@ pre Changing from ADMINISTRATION to OPERATIONAL is not
protected . Changing from OPERATIONAL to ADMINISTRATION
is controlled by the right referenced by the current
DF ’ s AC LCYCLE .
@ pre The command can be executed in the life cycle phases
ADMINISTRATION and OPERATIONAL . */
void phaseControl ( ) {
CRYPTOLOG ( " log " ) ;
check ( send ( 0x80 , 0x10 , 0x00 , 0x00 ) ) ;
}
//! Installs / administrates / overwrites different objects
void putData ( ) {
CRYPTOLOG ( " log " ) ;
assert ( ! " not implemented " ) ;
}
//! Read a BINARY file
std : : string readBinary ( std : : string path = " " , unsigned short offset = 0 ) {
CRYPTOLOG ( " log " ) ;
pcsc : : Connection : : Reader : : Transaction lock ( _reader ) ;
if ( path . size ( ) ) select ( path ) ;
return check ( send ( 0x00 , 0xB0 , ( unsigned char ) ( offset > > 8 ) ,
( unsigned char ) ( offset & 0xFF ) ) ) ;
}
enum ReadRecordMode {
FIRST_RECORD = 0 ,
LAST_RECORD = 1 ,
NEXT_RECORD = 2 ,
PREVIOUS_RECORD = 3 ,
CURRENT_RECORD = 4 ,
ABSOLUTE_RECORD = 4
} ;
//! Read a record oriented file
std : : string readRecord ( const std : : string & path = std : : string ( ) ,
unsigned char record = 0 ,
unsigned char sfi = 0 ,
ReadRecordMode mode = ABSOLUTE_RECORD ) {
CRYPTOLOG ( " log " ) ;
pcsc : : Connection : : Reader : : Transaction lock ( _reader ) ;
if ( path . size ( ) ) select ( path ) ;
return check ( send ( 0x00 , 0xB2 , record ,
( unsigned char ) ( ( sfi & 0xF8 ) | mode ) ) ) ;
}
/// Read all records from a record oriented file
BerValues readBerFile ( std : : string path = " " ) {
CRYPTOLOG ( " log " ) ;
BerValues content ;
pcsc : : Connection : : Reader : : Transaction lock ( _reader ) ;
if ( path . size ( ) ) select ( path ) ;
while ( true ) {
std : : string res ( send ( 0x00 , 0xB2 , 0 , NEXT_RECORD ) ) ;
if ( cardos : : Commands : : retCode ( res ) ! = 0x9000 ) break ;
content + = retData ( res ) . substr ( 2 ) ;
}
return content ;
}
//! Resets the error counter of a BS object to its maximum
void resetRetryCounter ( ) {
CRYPTOLOG ( " log " ) ;
assert ( ! " not implemented " ) ;
}
//! Resets the security status of the current DF
void resetSecurityCounter ( ) {
CRYPTOLOG ( " log " ) ;
assert ( ! " not implemented " ) ;
}
enum FileSelectionMode {
DF_OR_EF_DIRECTLY_BELOW_CURRENT_DF_USING_FILE_ID = 0x00 ,
DF_DIRECTLY_BELOW_CURRENT_DF_USING_FILE_ID = 0x01 ,
EF_DIRECTLY_BELOW_CURRENT_DF_USING_FILE_ID = 0x02 ,
PARENT_DF_OF_CURRENT_DF = 0x03 ,
DF_BY_NAME = 0x04 ,
DF_OR_EF_USING_PATH_FROM_MF = 0x08 ,
DF_OR_EF_USING_PATH_FROM_CURRENT_DF = 0x09
} ;
enum FileSelectionReturn {
RETURN_FCI_DATA = 0x00 ,
RETURN_FCP_DATA = 0x04 ,
RETURN_NOTHING = 0x0C
} ;
//! Selects a file
BerValues selectFile ( std : : string file ,
FileSelectionMode mode
= DF_OR_EF_USING_PATH_FROM_MF ,
FileSelectionReturn ret
= RETURN_NOTHING ) {
CRYPTOLOG ( " log " ) ;
return BerValues ( check ( send ( 0x00 , 0xA4 , mode , ret , file ) ) ) ;
}
//! Sets the so-called Data_Field_Length parameter relevant for
//! the effective Command Data Field Length / Response Data
//! Field Length.
void setDataFieldLength ( ) {
CRYPTOLOG ( " log " ) ;
assert ( ! " not implemented " ) ;
}
//! Enables or disables the usage of an existing transaction buffer
void setTransactionState ( ) {
CRYPTOLOG ( " log " ) ;
assert ( ! " not implemented " ) ;
}
//! Signs a hashed input using a decryption key with the RSA_SIG
//! algorithm.
void signByDecryptionKey ( ) {
CRYPTOLOG ( " log " ) ;
assert ( ! " not implemented " ) ;
}
//! Uninstalls a package of the smart card
void uninstallPackage ( ) {
CRYPTOLOG ( " log " ) ;
assert ( ! " not implemented " ) ;
}
// same as readBinary
// /// Read the previously file from smart card
// std::string readBinFile() {
// CRYPTOLOG("log");
// return check(send(0x00, 0xB0, 0x00, 0x00));
// }
//! Updates a BINARY file
void updateBinary ( std : : string data , unsigned short offset = 0 ) {
CRYPTOLOG ( " log " ) ;
check ( send ( 0x00 , 0xD6 , ( unsigned char ) ( offset > > 8 ) ,
( unsigned char ) ( offset & 0xFF ) , data ) ) ;
}
//! Overwrites an existing record.
std : : string updateRecord ( std : : string data ,
unsigned char record = 0 ,
unsigned char sfi = 0 ,
ReadRecordMode mode = ABSOLUTE_RECORD ) {
CRYPTOLOG ( " log " ) ;
return check ( send ( 0x00 , 0xDC , record ,
( unsigned char ) ( ( sfi & 0xF8 ) | mode ) , data ) ) ;
}
enum VerifyMode {
SEARCH_IN_MF = 0 ,
SEARCH_FROM_DF = 0x80
} ;
//! Performs a PIN test (CHV test)
void verify ( std : : string pin , unsigned char id ,
VerifyMode mode = SEARCH_FROM_DF ) {
CRYPTOLOG ( " log pin= \" " < < pin < < " \" id= \" " < < std : : hex < < ( int ) id < < " \" " ) ;
check ( send ( 0x00 , 0x20 , 0x00 , ( unsigned char ) ( 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 , ( unsigned char ) ( 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
static std : : string mf ( ) {
return " 3f00 " ;
}
/// Path to PKCS#15
static std : : string pkcs15 ( ) {
return mf ( ) + " 5015 " ;
}
/// Path to SigG (Signaturgesetz)
static std : : string sigG ( ) {
return mf ( ) + " 1fff " ;
}
/// ID of transport PIN
static unsigned char transportPin ( ) {
return 0x71 ;
}
/// ID of PKCS#15 user PIN
static unsigned char pkcs15Pin ( ) {
return 0x01 ;
}
/// ID of SigG (Signaturgesetz) secure PIN
static unsigned char sigGPin ( ) {
return 0x01 ;
}
/// ID of PUK to unlock PKCS#15 user PIN
static unsigned char puk ( ) {
return 0x02 ;
}
//@}
//------------------------------------------------------------------------
/// @name High Level Smart Card Access
//@{
/// Logon with transport PIN
void logonTransport ( std : : string pin ) {
CRYPTOLOG ( " log pin= \" " < < pin < < " \" " ) ;
pcsc : : Connection : : Reader : : Transaction lock ( _reader ) ;
selectSigG ( ) ;
try {
logon ( transportPin ( ) , pin ) ;
} catch ( std : : exception & x ) {
throw wrong_pin ( x . what ( ) ) ;
}
}
/// Logon with SigG (Signaturgesetz) secure PIN
void logonSigG ( std : : string pin ) {
CRYPTOLOG ( " log " ) ;
pcsc : : Connection : : Reader : : Transaction lock ( _reader ) ;
selectSigG ( ) ;
try {
logon ( sigGPin ( ) , pin ) ;
} catch ( std : : exception & x ) {
throw wrong_pin ( x . what ( ) ) ;
}
}
/// Logon with PKCS#15 user PUK to unlock user PIN
void logonPuk ( std : : string pin ) {
CRYPTOLOG ( " log " ) ;
pcsc : : Connection : : Reader : : Transaction lock ( _reader ) ;
selectMF ( ) ;
try {
logon ( puk ( ) , pin ) ;
} catch ( std : : exception & x ) {
throw wrong_puk ( x . what ( ) ) ;
}
}
/// Logon with PKCS#15 user PIN
void logonPkcs15 ( std : : string pin ) {
CRYPTOLOG ( " log " ) ;
pcsc : : Connection : : Reader : : Transaction lock ( _reader ) ;
selectMF ( ) ;
try {
logon ( pkcs15Pin ( ) , pin ) ;
} catch ( std : : exception & x ) {
throw wrong_pin ( x . what ( ) ) ;
}
}
/// 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 newPin= \" " < < newPin < < " \" oldPin= \" " < < oldPin < < " \" " ) ;
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 newPin= \" " < < newPin < < " \" oldPin= \" " < < oldPin < < " \" " ) ;
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 newPin= \" " < < newPin < < " \" oldPin= \" " < < oldPin < < " \" " ) ;
pcsc : : Connection : : Reader : : Transaction lock ( _reader ) ;
if ( transportState ( ) ) { // transport state
changeSigGPin ( newPin , oldPin ) ;
changePkcs15Pin ( newPin , oldPin ) ;
} else {
if ( pkcs15PinRetries ( ) ! = - 1 ) { // normal pin change
changePkcs15Pin ( newPin , oldPin ) ;
try {
if ( sigGPinRetries ( ) ! = - 1 ) changeSigGPin ( newPin , oldPin ) ;
} catch ( . . . ) {
// undo PKCS#15 PIN change
if ( pkcs15PinRetries ( ) ! = - 1 ) changePkcs15Pin ( oldPin , newPin ) ;
throw ;
}
} else if ( pukRetries ( ) = = - 1 & & sigGPinRetries ( ) ! = - 1 ) {
changeSigGPin ( newPin , oldPin ) ; // only valid sigg left
} else throw pin_locked ( ) ;
}
}
/// Unlock PKCS#15 PIN using PUK
/** If the PIN is still the same as the old PIN was, SigG PIN
can be saved . If you have forgotten your SigG PIN , just use
@ c force to completely break it . Otherwise PIN will first be
applied to SigG , if that works , PKCS # 15 will be changed
accordingly . */
void unlock ( std : : string pin , std : : string puk , bool force = false ) {
CRYPTOLOG ( " log " ) ;
if ( pukRetries ( ) = = - 1 ) throw pin_locked ( ) ; // too late, puk broken
logonPuk ( puk ) ;
CRYPTOLOG ( " PUK accepted " ) ;
while ( sigGPinRetries ( ) ! = - 1 ) try {
logonSigG ( pin ) ; // check SigG
CRYPTOLOG ( " SigG successfully checked " ) ;
break ; // SigG still works
} catch ( . . . ) {
CRYPTOLOG ( " SigG PIN failed " ) ;
if ( ! force ) throw ; // don't enforce, abort after one try
// otherwise break the SigG PUK now
// continue until SigG is broken
CRYPTOLOG ( " Force kill SigG " ) ;
}
selectMF ( ) ;
changeReferenceData ( pkcs15Pin ( ) , pin ) ;
CRYPTOLOG ( " Successfully restored PKCS#15 PIN " ) ;
}
/// 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: select " < < path ) ;
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 " < < 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 ) ) ;
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 ) < < 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 ( unsigned char cla , unsigned char ins ,
unsigned char p1 , unsigned char p2 ,
std : : string lc ) {
if ( ! _reader ) throw runtime_error ( " no reader selected " ) ;
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 ) ) ;
}
std : : string send ( unsigned char cla , unsigned char ins ,
unsigned char p1 , unsigned char p2 ) {
if ( ! _reader ) throw runtime_error ( " no reader selected " ) ;
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 :
std : : shared_ptr < pcsc : : Connection : : Reader > _reader ;
} ;
//============================================================================
/// Represents a CardOS Filesystem Object
class Object {
public :
typedef std : : shared_ptr < Object > Child ;
typedef std : : vector < Child > Children ;
public :
Object ( const std : : string & p ) : _path ( p ) { }
virtual ~ Object ( ) { }
virtual const std : : string & path ( ) const {
return _path ;
}
virtual std : : string name ( ) const {
return _path ;
}
virtual std : : string logicalName ( ) const {
switch ( crypto : : ulongFromBinary ( crypto : : hexToBin ( name ( ) ) ) ) {
case 0x3f00 : return " Masterfile " ;
case 0x5649 : return " Version Info " ;
case 0x2F02 : return " Global Definition Object " ;
case 0x5015 : return " PKCS#15 " ;
case 0x5032 : return " Token Info " ;
case 0x5600 : return " File System Version " ;
case 0x4404 : return " Certificate Definition " ;
case 0x4304 : return " Certificates " ;
case 0x4401 : return " Public Key Definition " ;
case 0x5075 : return " Public Keys " ;
case 0x4408 : return " Authentication Object Definition " ;
case 0x5031 : return " Object Definition " ;
case 0x4407 : return " Data Object Definition " ;
case 0x4444 : return " Data Objects " ;
case 0x4400 : return " Private Key Definition " ;
case 0x5072 : return " Private Keys " ;
case 0x1fff : return " SigG " ;
case 0xfe15 : return " Transport Protection State " ;
case 0xc100 : return " Public Key " ;
default :
switch ( crypto : : ulongFromBinary ( crypto : : hexToBin ( name ( ) ) ) & 0xff00 ) {
case 0x4b00 : return " Private Key " + name ( ) . substr ( 2 ) ;
case 0x5500 : return " Public Key " + name ( ) . substr ( 2 ) ;
default : return type ( ) ;
}
}
}
virtual const std : : string & content ( ) const {
const static std : : string c ;
return c ;
}
virtual const std : : string & contentInfo ( ) const {
const static std : : string c ;
return c ;
}
virtual std : : string print ( int indent = 0 , int = 4 ) const {
return std : : string ( indent , ' ' ) + type ( ) + " : " + name ( ) ;
}
virtual bool isFile ( ) const {
return false ;
}
virtual bool isDir ( ) const {
return false ;
}
virtual bool hasChildren ( ) const {
return _children . size ( ) ;
}
virtual Children : : size_type size ( ) const {
return _children . size ( ) ;
}
virtual const Children & children ( ) const {
return _children ;
}
virtual std : : string type ( ) const = 0 ;
protected :
std : : string _path ;
Children _children ;
} ;
//============================================================================
/// Represents a File on a Smart Card
class File : public Object {
public :
File ( Commands & cmd , const std : : string & p , const std : : string & file ) :
Object ( p ) , _file ( file ) {
CRYPTOLOG ( " new file " < < file < < " at " < < p ) ;
try {
_content = cmd . readBinary ( p + file ) ;
try {
_contentInfo = cardos : : BerValues ( _content ) . print ( ) ;
} catch ( . . . ) {
}
} catch ( std : : exception & x ) {
_contentInfo = x . what ( ) ;
}
}
File ( const std : : string & p , const std : : string & file ) :
Object ( p ) , _file ( file ) {
CRYPTOLOG ( " new file " < < file < < " at " < < p ) ;
}
std : : string name ( ) const {
return _file ;
}
const std : : string & content ( ) const {
return _content ;
}
const std : : string & contentInfo ( ) const {
return _contentInfo ;
}
bool isFile ( ) const {
return true ;
}
std : : string type ( ) const {
return " File " ;
}
protected :
std : : string _file ;
std : : string _content ;
std : : string _contentInfo ;
} ;
//============================================================================
/// Represents a Link on a Smart Card
class Link : public File {
public :
Link ( Commands & cmd , const std : : string & p , const std : : string & file ) :
File ( cmd , p , file ) {
CRYPTOLOG ( " new link " < < file < < " at " < < p ) ;
}
std : : string type ( ) const {
return " Link " ;
}
} ;
//============================================================================
/// Represents a Counter on a Smart Card
class Counter : public File {
public :
Counter ( Commands & cmd , const std : : string & p , const std : : string & file ) :
File ( p , file ) {
CRYPTOLOG ( " new counter " < < file < < " at " < < p ) ;
try {
_content = cmd . readRecord ( p + file ) ;
std : : stringstream s ;
s < < crypto : : ulongFromBinary ( _content ) ;
_contentInfo = s . str ( ) ;
} catch ( std : : exception & x ) {
_contentInfo = x . what ( ) ;
}
}
std : : string type ( ) const {
return " Counter " ;
}
} ;
//============================================================================
/// Represents a Directory on a Smart Card
class Dir : public Object {
public :
Dir ( Commands & cmd , const std : : string & p ) : Object ( p ) {
CRYPTOLOG ( " new directory path= " < < p ) ;
setup ( cmd ) ;
}
std : : string name ( ) const {
return _path . substr ( _path . size ( ) - 4 ) ;
}
bool isDir ( ) const {
return true ;
}
std : : string print ( int indent = 0 , int step = 4 ) const {
std : : string res ( std : : string ( indent , ' ' ) + type ( ) + " : " + name ( ) ) ;
for ( Children : : const_iterator it ( _children . begin ( ) ) ;
it ! = _children . end ( ) ; + + it )
res + = ' \n ' + ( * it ) - > print ( indent + step , step ) ;
return res ;
}
std : : string type ( ) const {
return " Directory " ;
}
protected :
void setup ( Commands & cmd ) {
_children . clear ( ) ;
BerValues i ( cmd . directory ( _path ) ) ;
CRYPTOLOG ( " Found Structure: " < < i . print ( ) ) ;
for ( BerValues : : iterator it ( i . begin ( ) ) ; it ! = i . end ( ) ; + + it ) {
CRYPTOLOG ( " -> Value: " < < it - > print ( ) ) ;
if ( it - > size ( ) < 2 ) continue ; // empty dir
switch ( ( * it ) [ 0 ] . ulong ( ) ) {
case 0x38 :
_children . push_back ( Child ( new Dir ( cmd , _path + ( * it ) [ 1 ] . hex ( ) ) ) ) ;
break ;
case 0x01 :
_children . push_back ( Child ( new File ( cmd , _path , ( * it ) [ 1 ] . hex ( ) ) ) ) ;
break ;
case 0x05 :
_children . push_back ( Child ( new Link ( cmd , _path , ( * it ) [ 1 ] . hex ( ) ) ) ) ;
break ;
case 0x06 :
_children . push_back ( Child ( new Counter ( cmd , _path , ( * it ) [ 1 ] . hex ( ) ) ) ) ;
break ;
default : // unknown
throw runtime_error ( " unknown object: " + it - > print ( ) ) ;
}
}
}
} ;
//@}
}
//@}
# endif