|
|
// AccessCard.cpp: implementation of the CAccessCard class.
//
//////////////////////////////////////////////////////////////////////
#include "NoWarning.h"
#include <scuArrayP.h>
#include <scuSecureArray.h>
#include "AccessCard.h"
#include <stdio.h>
#include "iopExc.h"
#include "LockWrap.h"
#include "FilePath.h"
using namespace std; using namespace iop;
namespace { BYTE AsPrivateAlgId(KeyType kt) { BYTE bAlgId = 0; switch (kt) { case ktRSA512: bAlgId = 0xC4; break; case ktRSA768: bAlgId = 0xC6; break; case ktRSA1024: bAlgId = 0xC8; break; case ktDES: bAlgId = 0x00; break; default: throw Exception(ccInvalidParameter); break; }
return bAlgId; }
} // namespace
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CAccessCard::CAccessCard(const SCARDHANDLE hCardHandle, const char* szReaderName, const SCARDCONTEXT pContext, const DWORD dwMode) : CSmartCard(hCardHandle, szReaderName, pContext, dwMode) { if (ValidClassByte(0xF0)) m_bClassByte = 0xF0; else if (ValidClassByte(0x00)) m_bClassByte = 0x00; else // Not an access card!
throw iop::Exception(iop::ccUnknownCard); m_fSupportLogout = SupportLogout();
}
CAccessCard::~CAccessCard() {
}
void CAccessCard::GetChallenge(const DWORD dwNumberLength, BYTE* bRandomNumber) { CLockWrap wrap(&m_IOPLock);
const BYTE bMinLen = 4, bMaxLen = 64; // Max 64 due to bug in old cards.
if(dwNumberLength < bMinLen) { BYTE bBuf[bMinLen]; SendCardAPDU(m_bClassByte, 0x84, 0x00, 0x00, 0, NULL, bMinLen, bBuf); memcpy(bRandomNumber,bBuf,dwNumberLength); } else {
DWORD dwRamainingBytes = dwNumberLength; BYTE *bpBuf = bRandomNumber;
while(dwRamainingBytes) { BYTE bNumGet = (dwRamainingBytes > bMaxLen) ? bMaxLen : dwRamainingBytes;
SendCardAPDU(m_bClassByte, 0x84, 0x00, 0x00, 0, NULL, bNumGet, bpBuf);
bpBuf += bNumGet; dwRamainingBytes -= bNumGet; } } }
void CAccessCard::DeleteFile(const WORD wFileID) { CLockWrap wrap(&m_IOPLock); BYTE bDataIn[2]; bDataIn[0] = (BYTE)(MSB(wFileID)); bDataIn[1] = (BYTE)(LSB(wFileID)); SendCardAPDU(m_bClassByte, insDeleteFile, 0x00, 0x00, 0x02, bDataIn, 0, NULL);
}
void CAccessCard::Directory(const BYTE bFile_Nb, FILE_HEADER* pMyfile) { CLockWrap wrap(&m_IOPLock);
RequireSelect();
BYTE bDataOut[40]; memset(bDataOut, 0, 40); SendCardAPDU(m_bClassByte, 0xA8, 0x00, bFile_Nb, 0, NULL, 40, bDataOut);
switch(bDataOut[6]) { case 0x01: // Root directory file
case 0x02: // Other directory file or Instance file
{ pMyfile->file_id = (WORD)(bDataOut[4] * 256 + bDataOut[5]); pMyfile->file_size = (WORD)(bDataOut[2] * 256 + bDataOut[3]); pMyfile->nb_file = bDataOut[15]; pMyfile->nb_sub_dir = bDataOut[14]; pMyfile->file_status = 0x01;
if (bDataOut[9] == 0) { memset((void*)(pMyfile->applicationID), 0x00, 16); pMyfile->file_type = directory; } else if (bDataOut[9] == 0x01 || bDataOut[9] == 0x02 || bDataOut[9] == 0x03) { /////////////////////////////////////////////////////////////////
// Instance files contain one "hidden" file for program data //
// that should not be shown to the users //
/////////////////////////////////////////////////////////////////
pMyfile->nb_file--;
pMyfile->file_type = Instance; pMyfile->AIDLength = bDataOut[23]; memcpy((void*)(pMyfile->applicationID), (void*)(&bDataOut[24]), pMyfile->AIDLength); /////////////////////////////////////////////////////////////////////
// Set flags in file status to discriminate applets/applications //
/////////////////////////////////////////////////////////////////////
switch(bDataOut[9]) { case 0x01: pMyfile->file_status |= (1 << 5); // Applet
break; case 0x02: pMyfile->file_status |= (1 << 4); // Application
break; case 0x03: pMyfile->file_status |= (3 << 4); // Both
break; }
////////////////////////////////////////////////
// Set flags in file status to discriminate //
// created/installed/registered instances //
////////////////////////////////////////////////
switch(bDataOut[10]) { case 0x01: pMyfile->file_status |= (1 << 1); // Created
break; case 0x02: pMyfile->file_status |= (1 << 2); // Installed
break; case 0x03: pMyfile->file_status |= (1 << 3); // Registered
break; } } ///////////////////////////////////////////////
// guard against a bad Instance file //
// created without an AID for file control //
///////////////////////////////////////////////
else { pMyfile->AIDLength = 0x00; memset((void*)(pMyfile->applicationID), 0x00, 16);
throw iop::Exception(iop::ccBadInstanceFile); }
///////////////////////////////
// Select file and get ACL //
///////////////////////////////
Select(pMyfile->file_id);
SendCardAPDU(m_bClassByte, insGetACL, 0x00, 0x00, 0, NULL, 0x08, bDataOut); memcpy((void*)(pMyfile->access_cond), (void*)(bDataOut),8); /////////////////////////////////
// Reselect parent directory //
/////////////////////////////////
Select(m_CurrentDirectory.Tail().GetShortID());
break;
} // end case directory or Instance file
case 0x04: // all other file types
{ pMyfile->file_id = (WORD)(bDataOut[4] * 256 + bDataOut[5]); pMyfile->file_size = (WORD)(bDataOut[2] * 256 + bDataOut[3]); pMyfile->nb_file = 0x00; pMyfile->nb_sub_dir = 0x00; pMyfile->file_status = bDataOut[11];
switch(bDataOut[13]) { case 0x00: pMyfile->file_type = Binary_File; break; case 0x01: pMyfile->file_type = Fixed_Record_File; break; case 0x02: pMyfile->file_type = Variable_Record_File; break; case 0x03: pMyfile->file_type = Cyclic_File; break; case 0x04: pMyfile->file_type = Program_File; break; ///////////////////////////////////////////////////////////////////////
// if GetResponse(...) is successful but bad file type is returned //
///////////////////////////////////////////////////////////////////////
default: pMyfile->file_type = Unknown; throw iop::Exception(iop::ccFileTypeUnknown); }
if (pMyfile->file_type == Cyclic_File || pMyfile->file_type == Fixed_Record_File) { pMyfile->nb_sub_dir = bDataOut[14]; pMyfile->nb_file = (pMyfile->nb_sub_dir) ? pMyfile->file_size / pMyfile->nb_sub_dir : 0; }
///////////////////////////////
// Select file and get ACL //
///////////////////////////////
if (pMyfile->file_id != 0xFFFF) { Select(pMyfile->file_id);
SendCardAPDU(m_bClassByte, insGetACL, 0x00, 0x00, 0, NULL, 0x08, bDataOut);
memcpy((void*)(pMyfile->access_cond), (void*)(bDataOut), 8); memset((void*)(pMyfile->applicationID), 0x00, 16); /////////////////////////////////
// Reselect parent directory //
/////////////////////////////////
Select(m_CurrentDirectory.Tail().GetShortID()); } break;
} // end case non-Directory/Instance files
default: ///////////////////////////////////////////////////////////////////////////
// if GetResponse(...) is successful but bad file category is returned //
///////////////////////////////////////////////////////////////////////////
throw iop::Exception(iop::ccBadFileCategory); } // end switch
}
void CAccessCard::ExternalAuth(const KeyType kt, const BYTE bKeyNb, const BYTE bDataLength, const BYTE* bData) { CLockWrap wrap(&m_IOPLock);
BYTE bAlgo_ID = AsPrivateAlgId(kt); SendCardAPDU(m_bClassByte, insExternalAuth, bAlgo_ID, bKeyNb, bDataLength, bData, 0, NULL); }
void CAccessCard::InternalAuth(const KeyType kt, const BYTE bKeyNb, const BYTE bDataLength, const BYTE* bDataIn, BYTE* bDataOut) { CLockWrap wrap(&m_IOPLock); ////////////////////////////////////////////////////////////////////////////
// The following checks to make sure the internal Auth is not being //
// used for DES, or for an RSA key operation of greater than 128 bytes. //
////////////////////////////////////////////////////////////////////////////
BYTE bAlgo_ID = AsPrivateAlgId(kt); if (((bAlgo_ID != 0xC4) && (bAlgo_ID != 0xC6) && (bAlgo_ID != 0xC8)) || (bDataLength > 0x80)) throw iop::Exception(iop::ccAlgorithmIdNotSupported);
/////////////////////////////////////////////////////////////////////////
// Need to reverse the byte order of the input data (big endian card) //
/////////////////////////////////////////////////////////////////////////
if (cMaxApduLength < bDataLength) throw iop::Exception(iop::ccInvalidParameter); BYTE bReversedData[cMaxApduLength];
for (BYTE bIndex = 0; bIndex < bDataLength; bIndex++) bReversedData[bIndex] = bDataIn[bDataLength - 1 - bIndex];
SendCardAPDU(m_bClassByte, insInternalAuth, bAlgo_ID, bKeyNb, bDataLength, bReversedData, 0, NULL); GetResponse(m_bClassByte, bDataLength, bReversedData);
//////////////////////////////////////////////////////
// Need to reverse the byte order of output data. //
//////////////////////////////////////////////////////
for (bIndex = 0; bIndex < bDataLength; bIndex++) bDataOut[bIndex] = bReversedData[bDataLength - 1 - bIndex]; }
void CAccessCard::ReadRecord(const BYTE bRecNum, const BYTE bMode, const BYTE bDataLen, BYTE *bData) { CLockWrap wrap(&m_IOPLock);
SendCardAPDU(m_bClassByte, 0xB2, bRecNum, bMode, 0, NULL, bDataLen, bData); }
void CAccessCard::UpdateRecord(const BYTE bRecNum, const BYTE bMode, const BYTE bDataLen, BYTE *bData) { CLockWrap wrap(&m_IOPLock);
SendCardAPDU(m_bClassByte, 0xDC, bRecNum, bMode, bDataLen, bData, 0, NULL); }
void CAccessCard::VerifyCHV(const BYTE bCHVNumber, const BYTE* bCHV) { CLockWrap wrap(&m_IOPLock); SendCardAPDU(m_bClassByte, insVerifyChv, 0x00, bCHVNumber, 0x08, bCHV, 0, NULL); }
void CAccessCard::VerifyKey(const BYTE bKeyNumber, const BYTE bKeyLength, const BYTE* bKey) { CLockWrap wrap(&m_IOPLock); SendCardAPDU(m_bClassByte, 0x2A, 0x00, bKeyNumber, bKeyLength, bKey, 0, NULL); }
void CAccessCard::SelectCardlet(const BYTE *bAID, const BYTE bAIDLen) { CLockWrap wrap(&m_IOPLock); SendCardAPDU(m_bClassByte, 0xA4, 0x04, 0x00, bAIDLen, bAID, 0, NULL); }
void CAccessCard::SelectLoader() { CLockWrap wrap(&m_IOPLock); SendCardAPDU(m_bClassByte, 0xA4, 0x04, 0x00, 0x00, NULL, 0, NULL); }
void CAccessCard::DeleteApplet() { CLockWrap wrap(&m_IOPLock); SendCardAPDU(m_bClassByte, 0x08, 0x02, 0x00, 0x00, NULL, 0, NULL); }
void CAccessCard::ResetInstance() { CLockWrap wrap(&m_IOPLock); SendCardAPDU(m_bClassByte, 0x08, 0x03, 0x00, 0x00, NULL, 0, NULL); }
void CAccessCard::SetCurrentAsLoader() { CLockWrap wrap(&m_IOPLock); SendCardAPDU(m_bClassByte, 0x08, 0x04, 0x00, 0x00, NULL, 0, NULL); }
void CAccessCard::SetDefaultAsLoader() { CLockWrap wrap(&m_IOPLock); SendCardAPDU(m_bClassByte, 0x08, 0x05, 0x00, 0x00, NULL, 0, NULL); }
void CAccessCard::BlockApplet() { CLockWrap wrap(&m_IOPLock); SendCardAPDU(m_bClassByte, 0x08, 0x07, 0x00, 0x00, NULL, 0, NULL); }
void CAccessCard::ValidateProgram(const BYTE *bSig, const BYTE bSigLength) { CLockWrap wrap(&m_IOPLock); SendCardAPDU(m_bClassByte, 0x0A, 0x01, 0x00, bSigLength, bSig, 0, NULL); }
void CAccessCard::ResetProgram() { CLockWrap wrap(&m_IOPLock); SendCardAPDU(m_bClassByte, 0x0A, 0x02, 0x00, 0x00, NULL, 0, NULL); }
void CAccessCard::VerifyTransportKey(const BYTE *bKey) { VerifyKey(0, 8, bKey); }
void CAccessCard::ExecuteMain() { CLockWrap wrap(&m_IOPLock); SendCardAPDU(m_bClassByte, insExecuteMethod, 0x02, 0x00, 0x00, NULL, 0, NULL); }
void CAccessCard::ExecuteInstall(const BYTE *bBlock, const BYTE bLen) { CLockWrap wrap(&m_IOPLock); SendCardAPDU(m_bClassByte, insExecuteMethod, 0x13, 0x00, bLen, bBlock, 0, NULL); }
void CAccessCard::SelectParent() { CLockWrap wrap(&m_IOPLock); RequireSelect();
///////////////////////////////////////////////////
// If current directory is root, reselect root //
///////////////////////////////////////////////////
if (m_CurrentDirectory.NumComponents() == 1) Select(0x3F00); else { SendCardAPDU(m_bClassByte, 0xA4, 0x03, 0x00, 0, NULL, 0, NULL); m_CurrentDirectory.ChopTail(); m_CurrentFile = m_CurrentDirectory; } }
void CAccessCard::Select(const WORD wFileID) { CLockWrap wrap(&m_IOPLock); BYTE bDataIn[2]; bDataIn[0] = (BYTE)(MSB(wFileID)); bDataIn[1] = (BYTE)(LSB(wFileID));
SendCardAPDU(m_bClassByte, 0xA4, 0x00, 0x00, 0x02, bDataIn, 0, NULL); }
void CAccessCard::LogoutAll() { if(m_fSupportLogout) { CLockWrap wrap(&m_IOPLock); SendCardAPDU(m_bClassByte, 0x22, 0x07, 0x00, 0x00, NULL, 0, NULL); } else ResetCard(); }
void CAccessCard::Select(const char* szFileFullPath, FILE_HEADER* pMyfile, const bool fSelectAll) { CLockWrap wrap(&m_IOPLock); BYTE bIndex = 0; char szFormattedPath[cMaxPathLength]; BYTE bFileCount = FormatPath(szFormattedPath, szFileFullPath); BYTE bPathLength = strlen(szFormattedPath);
auto_ptr<FilePath> apfp(new FilePath(string(szFormattedPath)));
///////////////////////////////////////////////////////////
// Select all files in path regardless of current path. //
// Do this on request, or if cache is empty //
///////////////////////////////////////////////////////////
if (fSelectAll || (m_CurrentFile.IsEmpty()) || (m_CurrentDirectory.IsEmpty())) { bIndex = 0; } ////////////////////////////////////////////////////////
// if path names match, do nothing except get file info //
////////////////////////////////////////////////////////
else if (m_CurrentFile == *apfp) { bIndex = 0; if (pMyfile) // force Select to get file info
{ if (1 < bFileCount) { if (m_CurrentFile == m_CurrentDirectory) bIndex = bFileCount - 1; // just reselect dir
else bIndex = bFileCount - 2; // select dir & file
SelectParent(); } } else bIndex = bFileCount; } ////////////////////////////////////////////////////////////////////
// if current directory is in path, only select remaining files //
////////////////////////////////////////////////////////////////////
else if(m_CurrentDirectory.NumComponents() < apfp->NumComponents()) { if (apfp->GreatestCommonPrefix(m_CurrentDirectory) == m_CurrentDirectory) bIndex = m_CurrentDirectory.NumComponents(); else bIndex = 0; } ////////////////////////////////////////////////////////////////////
// if new path share part of current directory, step upwards //
////////////////////////////////////////////////////////////////////
else if(m_CurrentDirectory.NumComponents() > apfp->NumComponents()) { BYTE bSharedPathLen;
bSharedPathLen = apfp->GreatestCommonPrefix(m_CurrentDirectory).NumComponents();
if(bSharedPathLen>1) // Not worth while to step up to 3F00.
{ BYTE bLevelsUp = m_CurrentDirectory.NumComponents() - bSharedPathLen; bool fSelectFailed = false; try { for(int i=0; i < bLevelsUp; i++) { SelectParent(); } } catch (Exception const &) { // TODO: Not sure if this is handling correctly!
fSelectFailed = true; }
if (fSelectFailed) bIndex = 0; else bIndex = bSharedPathLen; } else bIndex = 0; } //////////////////////////////////////////
// Select the necessary files in path //
//////////////////////////////////////////
char sFileToSelect[5] = { 0, 0, 0, 0, 0 }; bool fFileSelected = false; bool fSelectFailed = false; try { while (bIndex < bFileCount) { WORD wFileHexID = (*apfp)[bIndex].GetShortID(); Select(wFileHexID); fFileSelected = true; bIndex++; } }
catch (Exception const &) { fSelectFailed = true; if (fSelectAll) throw; // TODO: throw something useful, eh?
}
if (fSelectFailed) // assert(!fSelectAll)
{ Select(szFormattedPath,pMyfile,true); fFileSelected = true; } BYTE bResponseLength = 0; if (fFileSelected) bResponseLength = ResponseLengthAvailable();
////////////////////////////////////////////////////
// GetResponse and fill file header information //
////////////////////////////////////////////////////
switch(bResponseLength) { case 0x17: case 0x28: { ////////////////////////////////////////////////
// File selected is a directory or Instance //
////////////////////////////////////////////////
m_CurrentDirectory = *apfp; m_CurrentFile = *apfp;
if (pMyfile) { BYTE bDataOut[0x28]; GetResponse(m_bClassByte, bResponseLength, bDataOut);
pMyfile->file_id = (WORD)(bDataOut[4] * 256 + bDataOut[5]); pMyfile->file_size = (WORD)(bDataOut[2] * 256 + bDataOut[3]); pMyfile->nb_file = bDataOut[15]; pMyfile->nb_sub_dir = bDataOut[14]; pMyfile->file_status = 0x01; if (bResponseLength == 0x17) { ////////////////////////////////////
// File selected is a directory //
////////////////////////////////////
pMyfile->file_type = directory; pMyfile->AIDLength = 0;
memset((void*)(pMyfile->applicationID), 0x00, 16); } else { ////////////////////////////////////
// File selected is an Instance //
////////////////////////////////////
/////////////////////////////////////////////////////
// Instance files contain one "hidden" file for //
// program data that should not be shown to the //
// users //
/////////////////////////////////////////////////////
pMyfile->nb_file--;
pMyfile->file_type = Instance; pMyfile->AIDLength = bDataOut[23];
memcpy((void*)(pMyfile->applicationID), (void*)(&bDataOut[24]), pMyfile->AIDLength);
////////////////////////////////////////////////////
// Set flags in file status to discriminate //
// applets/applications //
///////////////////////////////////////////////////
switch(bDataOut[9]) { case 0x01: pMyfile->file_status |= (1 << 5); // Applet
break; case 0x02: pMyfile->file_status |= (1 << 4); // Application
break; case 0x03: pMyfile->file_status |= (3 << 4); // Both
break; }
////////////////////////////////////////////////
// Set flags in file status to discriminate //
// created/installed/registered instances //
////////////////////////////////////////////////
switch(bDataOut[10]) { case 0x01: pMyfile->file_status |= (1 << 1); // Created
break; case 0x02: pMyfile->file_status |= (1 << 2); // Installed
break; case 0x03: pMyfile->file_status |= (1 << 3); // Registered
break; } }
////////////////////
// Get file ACL //
////////////////////
SendCardAPDU(m_bClassByte, 0xFE, 0x00, 0x00, 0, NULL,0x08, bDataOut); memcpy((void*)(pMyfile->access_cond), (void*)(bDataOut), 8); } break;
} // end case directory or Instance file
case 0x0F: { ////////////////////////////////////////////////////////
// File selected is an elementary file of some type //
////////////////////////////////////////////////////////
m_CurrentFile = *apfp; apfp->ChopTail(); m_CurrentDirectory = *apfp;
if (pMyfile) { BYTE bDataOut[0x0F];
GetResponse(m_bClassByte, bResponseLength, bDataOut);
pMyfile->file_id = (WORD)(bDataOut[4] * 256 + bDataOut[5]); pMyfile->file_size = (WORD)(bDataOut[2] * 256 + bDataOut[3]); pMyfile->nb_file = 0x00; pMyfile->nb_sub_dir = 0x00; pMyfile->AIDLength = 0x00; pMyfile->file_status = bDataOut[11];
memset((void*)pMyfile->applicationID, 0, 16);
switch(bDataOut[13]) { case 0x00: pMyfile->file_type = Binary_File; break;
case 0x01: pMyfile->file_type = Fixed_Record_File; pMyfile->nb_sub_dir = bDataOut[14]; pMyfile->nb_file = pMyfile->file_size / pMyfile->nb_sub_dir; break;
case 0x02: pMyfile->file_type = Variable_Record_File; break;
case 0x03: pMyfile->file_type = Cyclic_File; pMyfile->nb_sub_dir = bDataOut[14]; pMyfile->nb_file = pMyfile->file_size / pMyfile->nb_sub_dir; break;
case 0x04: pMyfile->file_type = Program_File; break; //////////////////////////////////////////////////////////
// if GetResponse(...) is successful but bad file //
// type is returned //
//////////////////////////////////////////////////////////
default: pMyfile->file_type = Unknown; throw iop::Exception(iop::ccFileTypeUnknown); }
////////////////////
// Get file ACL //
////////////////////
SendCardAPDU(m_bClassByte, 0xFE, 0x00, 0x00, 0, NULL, 0x08, bDataOut);
memcpy((void*)(pMyfile->access_cond), (void*)(bDataOut), 8); } break;
} // end case elementary file
default: //////////////////////////////////////////////////////////////////
// GetResponse was successful but returned an uninterpretable //
//////////////////////////////////////////////////////////////////
if (fFileSelected) throw iop::Exception(iop::ccCannotInterpretGetResponse); } // end of SWITCH
}
void CAccessCard::CreateFile(const FILE_HEADER* pMyfile) { CLockWrap wrap(&m_IOPLock);
BYTE bDataIn[16]; /////////////////////////////////////////////////////
// Cyberflex cards don't allocate space for file //
// headers implicity, so it's done here manually //
/////////////////////////////////////////////////////
WORD wFileSize; if (pMyfile->file_type == directory) wFileSize = pMyfile->file_size + 24; else wFileSize = pMyfile->file_size + 16; ////////////////////////////////////////////////////
// Cyclic files need a 4 byte header per record //
////////////////////////////////////////////////////
if (pMyfile->file_type == Cyclic_File) { /////////////////////////////////////////
// prevent overflow error in card OS! //
/////////////////////////////////////////
if (pMyfile->nb_sub_dir > 251) throw iop::Exception(iop::ccCyclicRecordSizeTooLarge); bDataIn[6] = pMyfile->nb_sub_dir + 4; wFileSize += pMyfile->nb_file * 4; } else bDataIn[6] = pMyfile->nb_sub_dir;
bDataIn[0] = MSB(wFileSize); bDataIn[1] = LSB(wFileSize); bDataIn[2] = MSB(pMyfile->file_id); bDataIn[3] = LSB(pMyfile->file_id); bDataIn[5] = pMyfile->file_status & 1; bDataIn[7] = pMyfile->nb_file;
switch(pMyfile->file_type) { case Binary_File: bDataIn[4] = 0x02; break; case directory: bDataIn[4] = 0x20; break; case Cyclic_File: bDataIn[4] = 0x1D; break; case Variable_Record_File: bDataIn[4] = 0x19; break; case Fixed_Record_File: bDataIn[4] = 0x0C; break; case Instance: bDataIn[4] = 0x21; break; case Program_File: bDataIn[4] = 0x03; break;
//////////////////////////////////
// Requested file type is bad //
//////////////////////////////////
default: // TO DO: Why not let the card return the
// error?
throw iop::Exception(iop::ccFileTypeInvalid); }
memcpy((void*)&bDataIn[8], (void*)(pMyfile->access_cond), 8);
SendCardAPDU(m_bClassByte, insCreateFile, 0x00, 0x00, 0x10, bDataIn, 0, NULL);
Dirty(true); }
void CAccessCard::WritePublicKey(const CPublicKeyBlob aKey, const BYTE bKeyNum) {
CLockWrap wrap(&m_IOPLock);
BYTE bAlgoID; switch(aKey.bModulusLength) { case 0x40: bAlgoID = 0xC5; break;
case 0x60: bAlgoID = 0xC7; break;
case 0x80: bAlgoID = 0xC9; break;
default: throw iop::Exception(iop::ccAlgorithmIdNotSupported); }
Select(0x1012);
DWORD dwKeyBlockLen = 4 + 9 + aKey.bModulusLength + 4;
BYTE bKeyBlob[256];
bKeyBlob[0] = (BYTE) ((dwKeyBlockLen >> 8) & 0xFF); bKeyBlob[1] = (BYTE) (dwKeyBlockLen & 0xFF); bKeyBlob[2] = bKeyNum; bKeyBlob[3] = bAlgoID; bKeyBlob[4] = 0xC1; bKeyBlob[5] = 0x01; bKeyBlob[6] = 0x02; bKeyBlob[7] = 0xC0; bKeyBlob[8] = (BYTE)aKey.bModulusLength + 1; bKeyBlob[9] = 0x00; //////////////////////////////////////////////////
// Convert modulus to big endian for the card //
//////////////////////////////////////////////////
for (int i = 0; i < aKey.bModulusLength; i++) bKeyBlob[10 + i] = aKey.bModulus[aKey.bModulusLength - 1 - i];
bKeyBlob[10 + aKey.bModulusLength] = 0xC0; bKeyBlob[11 + aKey.bModulusLength] = 0x04;
///////////////////////////////////////////////////
// Convert exponent to big endian for the card //
///////////////////////////////////////////////////
for (i = 0; i < 4; i++) bKeyBlob[12 + aKey.bModulusLength + i] = aKey.bExponent[3 - i];
WORD wOffset = (WORD) (dwKeyBlockLen * bKeyNum);
WriteBinary(wOffset, (WORD) dwKeyBlockLen, bKeyBlob); }
void CAccessCard::GetSerial(BYTE* bSerial, size_t &SerialLength) { if (bSerial == NULL) { SerialLength = 22; return; }
if (SerialLength < 22) { throw iop::Exception(ccBufferTooSmall); }
SerialLength = 22;
SendCardAPDU(m_bClassByte, 0xCA, 0x00, 0x01, 0, NULL, 22, bSerial); }
void CAccessCard::ReadPublicKey(CPublicKeyBlob *aKey, const BYTE bKeyNum) { CLockWrap wrap(&m_IOPLock); BYTE bKeyLength[2];
Select(0x1012); ReadBinary(0,2,bKeyLength);
WORD wKeyBlockLength = bKeyLength[0] * 256 + bKeyLength[1]; WORD wOffset = (WORD)(wKeyBlockLength * bKeyNum); BYTE bBuffer[512];
ReadBinary(wOffset, wKeyBlockLength, bBuffer);
aKey->bModulusLength = bBuffer[8] - 1;
scu::AutoArrayPtr<BYTE> aabModule(new BYTE[aKey->bModulusLength]); BYTE aExponent[4];
memcpy((void*)aabModule.Get(), (void*)&bBuffer[10], aKey->bModulusLength); memcpy((void*)aExponent, (void*)&bBuffer[10 + aKey->bModulusLength + 2], 4);
/////////////////////////////////////////////////////////////////////////
// Change from big endian on the card to little endian in the struct //
/////////////////////////////////////////////////////////////////////////
for (WORD i = 0; i < aKey->bModulusLength; i++) aKey->bModulus[i] = aabModule[aKey->bModulusLength - 1 - i]; for (i = 0; i < 4; i++) aKey->bExponent[i] = aExponent[3 - i]; }
// There are some slightly arbitrary constants used here...tighten down later.
void CAccessCard::WritePrivateKey(const CPrivateKeyBlob aKey, const BYTE bKeyNum) {
CLockWrap wrap(&m_IOPLock);
BYTE bAlgoID; switch(aKey.bPLen) { case 0x20: bAlgoID = 0xC4; break;
case 0x30: bAlgoID = 0xC6; break;
case 0x40: bAlgoID = 0xC8; break;
default: throw iop::Exception(iop::ccAlgorithmIdNotSupported); }
Select(0x0012);
CPrivateKeyBlob anotherKey;
WORD wKeyBlockLength = 22 + 5 * aKey.bPLen; WORD wOffset = (WORD)(bKeyNum * wKeyBlockLength);
scu::SecureArray<BYTE> bKeyBlob(1024); bKeyBlob[0] = (BYTE)((wKeyBlockLength >> 8) & 0xFF); bKeyBlob[1] = (BYTE)(wKeyBlockLength & 0xFF); bKeyBlob[2] = bKeyNum; bKeyBlob[3] = bAlgoID; bKeyBlob[4] = 0xC2; bKeyBlob[5] = 0x01; bKeyBlob[6] = 0x05;
////////////////////////////////////////////////////////////////////////
// Need to convert from little endian (struct) to big endian (card) //
////////////////////////////////////////////////////////////////////////
scu::SecureArray<BYTE> bP(256); scu::SecureArray<BYTE> bQ(256); scu::SecureArray<BYTE> bQInv(256); scu::SecureArray<BYTE> bKmodP(256); scu::SecureArray<BYTE> bKmodQ(256);
for (WORD i = 0; i < aKey.bPLen; i++) { bP[i] = aKey.bP[aKey.bQLen - 1 - i]; bQ[i] = aKey.bQ[aKey.bPLen - 1 - i]; bQInv[i] = aKey.bInvQ[aKey.bInvQLen - 1 - i]; bKmodP[i] = aKey.bKsecModP[aKey.bKsecModQLen - 1 - i]; bKmodQ[i] = aKey.bKsecModQ[aKey.bKsecModPLen - 1 - i]; }
//////////////////////////////////////////////////////
// Now we need to left align the bytes of P and Q //
//////////////////////////////////////////////////////
BYTE pShift = 0, qShift = 0, qIShift = 0; /* Punting on bad keys!
if ((bP[0] < 8) || (bQ[0] < 8)) { // Bad key?
delete bP; delete bQ; delete bQInv; delete bKmodP; delete bKmodQ; delete bKeyBlob; return FALSE; } */
bKeyBlob[7] = 0xC2; bKeyBlob[8] = (BYTE) aKey.bPLen + 1; bKeyBlob[9] = 0x00;
memcpy((void*) &bKeyBlob[10], (void*)bQ.data(), aKey.bPLen); bKeyBlob[10 + aKey.bPLen] = 0xC2; bKeyBlob[11 + aKey.bPLen] = (BYTE) aKey.bQLen + 1; bKeyBlob[12 + aKey.bPLen] = 0x00;
memcpy((void*) &bKeyBlob[13 + aKey.bPLen], (void*)bP.data(), aKey.bPLen); bKeyBlob[13 + 2* aKey.bPLen] = 0xC2; bKeyBlob[14 + 2* aKey.bPLen] = (BYTE) aKey.bQLen + 1; bKeyBlob[15 + 2* aKey.bPLen] = 0x00;
memcpy((void*) &bKeyBlob[16 + 2* aKey.bPLen], (void*)bQInv.data(), aKey.bPLen); bKeyBlob[16 + 3* aKey.bPLen] = 0xC2; bKeyBlob[17 + 3* aKey.bPLen] = (BYTE) aKey.bQLen + 1; bKeyBlob[18 + 3* aKey.bPLen] = 0x00;
memcpy((void*) &bKeyBlob[19 + 3* aKey.bPLen], (void*)bKmodQ.data(), aKey.bPLen); bKeyBlob[19 + 4* aKey.bPLen] = 0xC2; bKeyBlob[20 + 4* aKey.bPLen] = (BYTE) aKey.bQLen + 1; bKeyBlob[21 + 4* aKey.bPLen] = 0x00;
memcpy((void*) &bKeyBlob[22 + 4 * aKey.bPLen], (void*)bKmodP.data(), aKey.bPLen);
WriteBinary(wOffset, wKeyBlockLength, bKeyBlob.data()); }
void CAccessCard::ChangeCHV(const BYTE bKeyNumber, const BYTE *bOldCHV, const BYTE *bNewCHV) { CLockWrap wrap(&m_IOPLock); scu::SecureArray<BYTE> bDataIn(16); memcpy((void*)bDataIn.data(), (void*)bOldCHV, 8); memcpy((void*)(bDataIn.data() + 8), (void*)bNewCHV, 8); SendCardAPDU(m_bClassByte, 0x24, 0x00, bKeyNumber, 16, bDataIn.data(), 0, NULL); Dirty(true); }
void CAccessCard::ChangeCHV(const BYTE bKey_nb, const BYTE *bNewCHV) {
CLockWrap wrap(&m_IOPLock);
switch (bKey_nb) { case 1: Select("/3f00/0000"); // CHV1 and CHV2 are the only CHV's supported
break; case 2: Select("/3f00/0100"); break;
default: throw iop::Exception(iop::ccInvalidChv); break; }
WriteBinary(3, 8, bNewCHV);
BYTE bRemaingAttempts = 3; // Minumum number + 2
WriteBinary(12, 1, &bRemaingAttempts); Dirty(true); VerifyCHV(bKey_nb,bNewCHV);
}
void CAccessCard::UnblockCHV(const BYTE bKeyNumber, const BYTE *bUnblockPIN, const BYTE *bNewPin) { CLockWrap wrap(&m_IOPLock);
scu::SecureArray<BYTE> bDataIn(16); memcpy((void*)bDataIn.data(), (void*)bUnblockPIN, 8); memcpy((void*)(bDataIn.data() + 8), (void*)bNewPin, 8);
SendCardAPDU(m_bClassByte, 0x2C, 0x00, bKeyNumber, 16, bDataIn.data(), 0, NULL);
Dirty(true); }
void CAccessCard::ChangeUnblockKey(const BYTE bKeyNumber, const BYTE *bNewPIN) { CLockWrap wrap(&m_IOPLock);
switch (bKeyNumber) { case 1: Select("/3f00/0000"); // CHV1 and CHV2 are the only CHV's supported
break; case 2: Select("/3f00/0100"); break;
default: throw iop::Exception(iop::ccInvalidChv); break; }
WriteBinary(13, 8, bNewPIN); }
void CAccessCard::ChangeTransportKey(const BYTE *bNewKey) { CLockWrap wrap(&m_IOPLock); Select("/3f00/0011");
//////////////////////////////////////////
// Build byte string to write to card //
//////////////////////////////////////////
BYTE bKeyString[13] = { 0x00, // length of key
0x0E, // length of key
0x00, // Key number
0x01, // tag to identify key as an ID PIN
0, 0, 0, 0, 0, 0, 0, 0, // 8 bytes for key
0x03 // # of verification attempts remaining before card is blocked + 2
}; // copy the template into a secure array for storing the sensitive
// part
const WORD wKeySize = 13; scu::SecureArray<BYTE> newbKeyStr(bKeyString,wKeySize); //////////////////////////////////////////////////////
// insert new key into key string to pass to card //
//////////////////////////////////////////////////////
memcpy((void*)(newbKeyStr.data() + 4), (void*)bNewKey, 8);
WriteBinary(0, wKeySize, newbKeyStr.data());
// Make a (hopefully) successfull verification to re-set attempt counter
VerifyTransportKey(bNewKey); }
void CAccessCard::ChangeACL(const BYTE *bACL) { CLockWrap wrap(&m_IOPLock);
SendCardAPDU(m_bClassByte, 0xFC, 0x00, 0x00, 0x08, bACL, 0, NULL);
Dirty(true); }
void CAccessCard::DefaultDispatchError(ClassByte cb, Instruction ins, StatusWord sw) const { CauseCode cc; bool fDoThrow = true; switch (sw) { case 0x6283: cc = ccContradictionWithInvalidationStatus; break; case 0x6300: cc = ccChvVerificationFailedMoreAttempts; break; case 0x6981: cc = ccChvNotInitialized; break;
case 0x6985: cc = ccNoFileSelected; break;
case 0x6A00: cc = ccFileExists; break;
case 0x6A84: cc = ccCannotReadOutsideFileBoundaries; break;
case 0x6A86: cc = ccLimitReached; break;
case 0x6F11: cc = ccDirectoryNotEmpty; break;
case 0x6F12: cc = ccInvalidSignature; break;
case 0x6F14: cc = ccBadState; break; default: fDoThrow = false; break; }
if (fDoThrow) throw Exception(cc, cb, ins, sw);
CSmartCard::DefaultDispatchError(cb, ins, sw); }
void CAccessCard::DispatchError(ClassByte cb, Instruction ins, StatusWord sw) const { CauseCode cc; bool fDoThrow = true; switch (ins) { case insCreateFile: switch (sw) { case 0x6A00: cc = ccFileExists; break;
case 0x6A84: cc = ccOutOfSpaceToCreateFile; break; case 0x6B00: cc = ccRecordInfoIncompatible; break;
default: fDoThrow = false; break; } break;
case insDeleteFile: switch (sw) { case 0x6A00: cc = ccRootDirectoryNotErasable; break;
default: fDoThrow = false; break; } break;
case insDirectory: switch (sw) { case 0x6985: cc = ccCurrentDirectoryIsNotSelected; break;
case 0x6A83: cc = ccFileIndexDoesNotExist; break;
default: fDoThrow = false; break; } break;
case insExecuteMethod: switch (sw) { case 0x6283: cc = ccProgramFileInvalidated; break;
case 0x6A00: cc = ccInstanceIdInUse; break;
case 0x6F15: cc = ccCardletNotInRegisteredState; break;
case 0x6F19: cc = ccInstallCannotRun; break;
default: if ((0x6230 <= sw) && (0x6260 <= sw)) cc = ccJava; else fDoThrow = false; } break; case insExternalAuth: switch (sw) { case 0x6300: cc = ccVerificationFailed; break;
case 0x6981: cc = ccInvalidKey; break;
case 0x6985: cc = ccAskRandomNotLastApdu; break;
case 0x6B00: cc = ccAlgorithmIdNotSupported; break;
case 0x6F15: cc = ccOperationNotActivatedForApdu; break; default: fDoThrow = false; break; } break; case insInternalAuth: switch (sw) { case 0x6300: cc = ccRequestedAlgIdMayNotMatchKeyUse; break; case 0x6981: cc = ccInvalidKey; break;
case 0x6B00: cc = ccAlgorithmIdNotSupported; break;
case 0x6F15: cc = ccOperationNotActivatedForApdu; break; default: fDoThrow = false; break; } break; case insReadBinary: // fall-through intentional
case insUpdateBinary: switch (sw) { case 0x6A84: cc = ccCannotReadOutsideFileBoundaries; break;
default: fDoThrow = false; break; } break;
default: fDoThrow = false; break; }
if (fDoThrow) throw Exception(cc, cb, ins, sw);
CSmartCard::DispatchError(cb, ins, sw); }
void CAccessCard::DoReadBlock(WORD wOffset, BYTE *pbBuffer, BYTE bLength) { SendCardAPDU(m_bClassByte, insReadBinary, HIBYTE(wOffset), LOBYTE(wOffset), 0, 0, bLength, pbBuffer); } void CAccessCard::DoWriteBlock(WORD wOffset, BYTE const *pbBuffer, BYTE cLength) { SendCardAPDU(m_bClassByte, insUpdateBinary, HIBYTE(wOffset), LOBYTE(wOffset), cLength, pbBuffer, 0, 0); } bool CAccessCard::ValidClassByte(BYTE bClassByte) { ////////////////////////////////////////////////////////////////
// Calling Directory on root to check for proper class byte //
////////////////////////////////////////////////////////////////
BYTE bOutput[cMaxDirInfo];
bool fValidClassByte = true; try { SendCardAPDU(bClassByte, insDirectory, 0x00, 0x00, 0, NULL, sizeof bOutput / sizeof *bOutput, bOutput); }
catch (Exception &) { fValidClassByte = false; }
return fValidClassByte; }
bool CAccessCard::SupportLogout() { bool fSuccess = true; try { CLockWrap wrap(&m_IOPLock); SendCardAPDU(m_bClassByte, 0x22, 0x07, 0x00, 0x00, NULL, 0, NULL);
} catch(...) { fSuccess = false; }
return fSuccess; }
|