|
|
// CryptoCard.cpp: implementation of the CCryptoCard class.
//
// (c) Copyright Schlumberger Technology Corp., unpublished work, created
// 2000. This computer program includes Confidential, Proprietary
// Information and is a Trade Secret of Schlumberger Technology Corp. All
// use, disclosure, and/or reproduction is prohibited unless authorized
// in writing. All Rights Reserved.
//////////////////////////////////////////////////////////////////////
#include "NoWarning.h"
#include <scuArrayP.h>
#include "iopExc.h"
#include "CryptoCard.h"
#include "LockWrap.h"
using namespace std; using namespace iop;
namespace { BYTE AsPrivateAlgId(KeyType kt) { BYTE bAlgId = 0; switch (kt) { case ktRSA512: bAlgId = 0x40; break; case ktRSA768: bAlgId = 0x60; break; case ktRSA1024: bAlgId = 0x80; break; case ktDES: bAlgId = 0x08; break; default: throw Exception(ccInvalidParameter); break; }
return bAlgId; }
} // namespace
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CCryptoCard::CCryptoCard(const SCARDHANDLE hCardHandle, const char* szReaderName, const SCARDCONTEXT pContext, const DWORD dwMode) : CSmartCard(hCardHandle, szReaderName, pContext, dwMode) {
m_fSupportLogout = SupportLogout();
}
CCryptoCard::~CCryptoCard() {
}
void CCryptoCard::LogoutAll() { if(m_fSupportLogout) { CLockWrap wrap(&m_IOPLock); SendCardAPDU(0xF0, 0x22, 0x07, 0, 0, NULL, 0, NULL); } else ResetCard(); }
void CCryptoCard::DeleteFile(const WORD wFileID) { CLockWrap wrap(&m_IOPLock); RequireSelect(); ////////////////////////////////////////////////////////
// Ensure that a directory is empty before deletion //
////////////////////////////////////////////////////////
char cFilePathFormatter[2] = "/"; char cZero[2] = "0"; char sBuffer[4] = { 0, 0, 0, 0 }; char szFileToDelete[80]; FILE_HEADER FHeader; int iPad;
if (!(m_CurrentDirectory == m_CurrentFile)) { /////////////////////////////////////////////////////////////////////////////////////////////
// File's parent directory was not selected (Currently selected file is not a directory) //
/////////////////////////////////////////////////////////////////////////////////////////////
throw iop::Exception(iop::ccSelectedFileNotDirectory); }
strcpy(szFileToDelete, m_CurrentDirectory.GetStringPath().c_str()); strcat(szFileToDelete, cFilePathFormatter); _itoa(wFileID, sBuffer, 16);
/////////////////////////////////////////////////////////////////////////
// Padding file path with 0 if file ID does not contain 4 characters //
/////////////////////////////////////////////////////////////////////////
iPad = strlen(sBuffer); while (iPad < 4) { strcat(szFileToDelete, cZero); iPad++; } strcat(szFileToDelete, sBuffer); //
szFileToDelete[m_CurrentDirectory.NumComponents() * 5 + 5] = '\0'; // Select file to delete
Select(szFileToDelete, &FHeader);
if (FHeader.file_type == directory && (FHeader.nb_file + FHeader.nb_sub_dir) > 0) { ////////////////////////////////////////////////////////
// re-establish current file and directory pointers //
////////////////////////////////////////////////////////
SelectParent(); //////////////////////////////////////////////////////////////////////////////
// Directory was not empty, and will not be deleted. Cryptoflex does not //
// support this check internally -- this is the Cyberflex status code! //
//////////////////////////////////////////////////////////////////////////////
throw iop::Exception(iop::ccDirectoryNotEmpty); }
////////////////////////////////////////////////////////
// re-establish current file and directory pointers //
////////////////////////////////////////////////////////
szFileToDelete[strlen(szFileToDelete) - 5] = '\0'; Select(szFileToDelete);
///////////////////////////////////////////////////////////////////////////
// File was not a directory or directory was empty - proceed to delete //
///////////////////////////////////////////////////////////////////////////
BYTE bDataIn[2]; bDataIn[0] = (BYTE)(MSB(wFileID)); bDataIn[1] = (BYTE)(LSB(wFileID)); SendCardAPDU(0xF0, 0xE4, 0x00, 0x00, 0x02, bDataIn, 0, NULL); Dirty(true); }
void CCryptoCard::CreateFile(const FILE_HEADER* pMyFile) { CLockWrap wrap(&m_IOPLock);
switch(pMyFile->file_type) { case Binary_File: case Variable_Record_File: case Cyclic_File: case Fixed_Record_File: { BYTE bData[17]; BYTE bP2; BYTE bDataLength; if (pMyFile->file_type == Binary_File) bP2 = 0x00; // binary files have no records
else bP2 = pMyFile->nb_file; // number of records
if (pMyFile->file_type == Binary_File || pMyFile->file_type == Variable_Record_File) { bDataLength = 0x10; bData[12] = 0x03; } else { //////////////////////////////////////////////////////
// Cyclic and Fixed Record files contain an extra //
// byte that denotes the length of their records //
//////////////////////////////////////////////////////
bDataLength = 0x11; bData[12] = 0x04; }
/////////////////////////////////////////////////////////////////////////////////
// Note: cyclic files also have an added 4B header allocated for each record //
// in the file in addition to the space allocated by CreateFile(...) //
/////////////////////////////////////////////////////////////////////////////////
bData[0] = 0; // RFU
bData[1] = 0; // RFU
bData[2] = MSB(pMyFile->file_size); // File Size
bData[3] = LSB(pMyFile->file_size); // File Size
bData[4] = MSB(pMyFile->file_id); // File ID
bData[5] = LSB(pMyFile->file_id); // File ID
switch(pMyFile->file_type) // File type
{ case Binary_File: bData[6] = 0x01; break; case Variable_Record_File: bData[6] = 0x04; break; case Cyclic_File: bData[6] = 0x06; break; case Fixed_Record_File: bData[6] = 0x02; break; } bData[7] = 0xFF; bData[8] = 0; // File ACL, to be set
bData[9] = 0; // File ACL, to be set
bData[10] = 0; // File ACL, to be set
bData[11] = pMyFile->file_status & 1; // File Status
// bData[12] = 0x03; // Length of the following data, already set
bData[13] = 0; // AUT key numbers, to be set
bData[14] = 0; // AUT key numbers, to be set
bData[15] = 0; // AUT key numbers, to be set
bData[16] = pMyFile->nb_sub_dir; // Record length (irrelevant for
// binary and variable record files)
bool ReadACL[8]; bool WriteACL[8]; bool InvalidateACL[8]; bool RehabilitateACL[8];
CryptoACL Read = { 0, 0, 0, 0, 0 }; CryptoACL Write = { 0, 0, 0, 0, 0 }; CryptoACL Invalidate = { 0, 0, 0, 0, 0 }; CryptoACL Rehabilitate = { 0, 0, 0, 0, 0 }; ////////////////////////////////////////////////////////////////////////////
// Determination of the state of each action for each member of the ACL //
////////////////////////////////////////////////////////////////////////////
for(int i = 0; i < 8; i++) { ReadACL[i] = ((pMyFile->access_cond[i]) & 1) ? true : false; WriteACL[i] = ((pMyFile->access_cond[i]) & 2) ? true : false; InvalidateACL[i] = ((pMyFile->access_cond[i]) & 8) ? true : false; RehabilitateACL[i] = ((pMyFile->access_cond[i]) & 16) ? true : false; }
/////////////////////////////////////////////////
// Remapping Cyberflex ACL to Cryptoflex ACL //
/////////////////////////////////////////////////
AccessToCryptoACL(ReadACL, &Read); AccessToCryptoACL(WriteACL, &Write); AccessToCryptoACL(InvalidateACL, &Invalidate); AccessToCryptoACL(RehabilitateACL, &Rehabilitate); ////////////////////////////////////
// Assignment of security level //
////////////////////////////////////
bData[8] = Read.Level * 16 + Write.Level; bData[10] = Rehabilitate.Level * 16 + Invalidate.Level; bData[13] = Read.AUTnumber * 16 + Write.AUTnumber; bData[15] = Rehabilitate.AUTnumber * 16 + Invalidate.AUTnumber; // If all the cyberflex ACL are 0, but the Cryptoflex are not, use the Cryptoflex.
bool zero = true; for (int j = 0; j < 8; j++) if (pMyFile->access_cond[j] != 0x00) zero = false;
if (zero) { // Use cryptoflex ACL)
memcpy(&bData[7], pMyFile->CryptoflexACL, 4); memcpy(&bData[13], &(pMyFile->CryptoflexACL[4]),3); }
SendCardAPDU(0xF0, insCreateFile, 0x00, bP2, bDataLength, bData, 0, NULL); }
break; // end case non-Directory file
case directory: { BYTE bData[17];
bData[0] = 0; // RFU
bData[1] = 0; // RFU
bData[2] = MSB(pMyFile->file_size); // File Size
bData[3] = LSB(pMyFile->file_size); // File Size
bData[4] = MSB(pMyFile->file_id); // File ID
bData[5] = LSB(pMyFile->file_id); // File ID
bData[6] = 0x38; // File type
bData[7] = 0x00; // No Use for Dedicated files
bData[8] = 0; // File ACL, to be set
bData[9] = 0; // File ACL, to be set
bData[10] = 0x00; // RFU
bData[11] = pMyFile->file_status & 1; // File Status
bData[12] = 0x04; // Length of the following data
bData[13] = 0; // AUT key numbers, to be set
bData[14] = 0; // AUT key numbers, to be set
bData[15] = 0x00; // RFU
bData[16] = 0xFF; // RFU
bool DirNextACL[8]; bool DeleteACL[8]; bool CreateACL[8];
CryptoACL DirNext = { 0, 0, 0, 0, 0 }; CryptoACL Delete = { 0, 0, 0, 0, 0 }; CryptoACL Create = { 0, 0, 0, 0, 0 };
////////////////////////////////////////////////////////////////////////////
// Determination of the state of each action for each member of the ACL //
////////////////////////////////////////////////////////////////////////////
for(int i = 0; i < 8; i++) { DirNextACL[i] = ((pMyFile->access_cond[i]) & 1) ? true : false; DeleteACL[i] = ((pMyFile->access_cond[i]) & 2) ? true : false; CreateACL[i] = ((pMyFile->access_cond[i]) & 32) ? true : false; }
/////////////////////////////////////////////////
// Remapping Cyberflex ACL to Cryptoflex ACL //
/////////////////////////////////////////////////
AccessToCryptoACL(DirNextACL, &DirNext); AccessToCryptoACL(DeleteACL, &Delete); AccessToCryptoACL(CreateACL, &Create); ////////////////////////////////////
// Assignment of security level //
////////////////////////////////////
bData[8] = DirNext.Level * 16; bData[9] = Delete.Level * 16 + Create.Level; bData[13] = DirNext.AUTnumber * 16; bData[14] = Delete.AUTnumber * 16 + Create.AUTnumber;
bool zero = true; for (int j = 0; j < 8; j++) if (pMyFile->access_cond[j] != 0x00) zero = false;
if (zero) { for (int j = 0; j < 7; j++) if (pMyFile->CryptoflexACL[j] != 00) zero = false;
if (!zero) { // Use cryptoflex ACL)
memcpy(&bData[7], pMyFile->CryptoflexACL, 4); memcpy(&bData[13], &(pMyFile->CryptoflexACL[4]),3); } }
SendCardAPDU(0xF0, 0xE0, 0x00, 0x00, 0x11, bData, 0, NULL); }
break; // end case Directory file
default: throw iop::Exception(iop::ccFileTypeInvalid); break; } Dirty(true); }
void CCryptoCard::Directory(BYTE bFile_Nb, FILE_HEADER* pMyFile) { CLockWrap wrap(&m_IOPLock); RequireSelect(); BYTE bDataOut[18]; for (BYTE index = 0; index < bFile_Nb; index++) SendCardAPDU(0xF0, 0xA8, 0x00, 0x00, 0, NULL, 0x10, bDataOut);
switch(bDataOut[4]) { case 0x38: // Directory file
{ pMyFile->file_id = (WORD)(bDataOut[2] * 256 + bDataOut[3]); pMyFile->file_type = directory; pMyFile->nb_file = bDataOut[15]; pMyFile->nb_sub_dir = bDataOut[14]; pMyFile->file_status = bDataOut[9]; memcpy(pMyFile->CryptoflexACL, &bDataOut[6], 3); memcpy(&(pMyFile->CryptoflexACL[3]), &bDataOut[11],3); ///////////////////////////////////////////////////////////////////////
// Build ACL //
///////////////////////////////////////////////////////////////////////
BYTE bACL[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; BYTE bACLNibble; BYTE bKeyNibble;
///////////////////
// Dir Next AC //
///////////////////
bACLNibble = bDataOut[6] / 16; bKeyNibble = bDataOut[11] / 16;
CryptoToAccessACL(bACL, bACLNibble, bKeyNibble, 0); //////////////////////
// Delete File AC //
//////////////////////
bACLNibble = bDataOut[7] / 16; bKeyNibble = bDataOut[12] / 16;
CryptoToAccessACL(bACL, bACLNibble, bKeyNibble, 1);
//////////////////////
// Create File AC //
//////////////////////
bACLNibble = bDataOut[7] % 16; bKeyNibble = bDataOut[12] % 16;
CryptoToAccessACL(bACL, bACLNibble, bKeyNibble, 5);
////////////////////////////////////////////////
// done remapping; assigning to file header //
////////////////////////////////////////////////
memcpy((void*)(pMyFile->access_cond), (void*)(bACL), 8); memset((void*)(pMyFile->applicationID), 0x00, 16);
break;
} // end case Directory
case 0x01: // Binary_File
case 0x02: // Fixed_Record_File
case 0x04: // Variable_Record_File
case 0x06: // Cyclic_File
{ pMyFile->file_id = (WORD)(bDataOut[2] * 256 + bDataOut[3]); pMyFile->file_status = bDataOut[9]; pMyFile->nb_sub_dir = bDataOut[14]; pMyFile->nb_file = bDataOut[15]; memcpy(pMyFile->CryptoflexACL, &bDataOut[6], 3); memcpy(&(pMyFile->CryptoflexACL[3]), &bDataOut[11],3);
////////////////////////////////////////////////////////////////////////
// Cryptoflex includes the file header in the file size -- removing //
////////////////////////////////////////////////////////////////////////
pMyFile->file_size = (WORD)(bDataOut[0] * 256 + bDataOut[1] - 16);
//////////////////////////////////////////
// Remove flag for file size rounding //
//////////////////////////////////////////
if (pMyFile->file_size >= 0x3FFF) pMyFile->file_size &= 0x3FFF;
switch(bDataOut[4]) { case 0x01: pMyFile->file_type = Binary_File; break; case 0x02: pMyFile->file_type = Fixed_Record_File; break; case 0x04: pMyFile->file_type = Variable_Record_File; break; case 0x06: pMyFile->file_type = Cyclic_File; break; } ////////////////////////////////////////////////////////////////////////
// Also includes 4 bytes record headers in cyclic files -- removing //
////////////////////////////////////////////////////////////////////////
if (pMyFile->file_type == Cyclic_File) pMyFile->file_size -= pMyFile->nb_file * 4;
///////////////////////////////////////////////////////////////////////
// Build ACL //
///////////////////////////////////////////////////////////////////////
BYTE bACL[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; BYTE bACLNibble; BYTE bKeyNibble;
////////////////////
// Read file AC //
////////////////////
bACLNibble = bDataOut[6] / 16; bKeyNibble = bDataOut[11] / 16;
CryptoToAccessACL(bACL, bACLNibble, bKeyNibble, 0); ////////////////////////
// Write to file AC //
////////////////////////
bACLNibble = bDataOut[6] % 16; bKeyNibble = bDataOut[11] % 16;
CryptoToAccessACL(bACL, bACLNibble, bKeyNibble, 1); ///////////////////////
// Rehabilitate AC //
///////////////////////
bACLNibble = bDataOut[8] / 16; bKeyNibble = bDataOut[13] / 16; CryptoToAccessACL(bACL, bACLNibble, bKeyNibble, 4);
/////////////////////
// Invalidate AC //
/////////////////////
bACLNibble = bDataOut[8] % 16; bKeyNibble = bDataOut[13] % 16;
CryptoToAccessACL(bACL, bACLNibble, bKeyNibble, 3);
////////////////////////
// Create Record AC //
////////////////////////
if (bDataOut[4] != 0x01) // omit create record file AC for binary file
{ bACLNibble = bDataOut[7] % 16; bKeyNibble = bDataOut[12] % 16;
CryptoToAccessACL(bACL, bACLNibble, bKeyNibble, 2); } ////////////////////////////////////////////////////
// done remapping ACL; assigning to file header //
////////////////////////////////////////////////////
memcpy((void*)(pMyFile->access_cond), (void*)(bACL), 8); memset((void*)(pMyFile->applicationID), 0x00, 16); break; } // end case non-Directory file
default: break; }
/////////////////////////////
// reset DirNext pointer //
/////////////////////////////
char szCurrentFile[80]; strcpy(szCurrentFile, m_CurrentFile.GetStringPath().c_str());
Select(m_CurrentDirectory.GetStringPath().c_str(), NULL, true); Select(szCurrentFile, NULL);
}
void CCryptoCard::Select(const WORD wFileID) { CLockWrap wrap(&m_IOPLock); BYTE bDataIn[2]; bDataIn[0] = (BYTE)(MSB(wFileID)); bDataIn[1] = (BYTE)(LSB(wFileID));
SendCardAPDU(0xC0, 0xA4, 0x00, 0x00, 0x02, bDataIn, 0, NULL); }
void CCryptoCard::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 //
////////////////////////////////////////////////////////
else if (m_CurrentFile == *apfp) { if (pMyFile) // force Select so file info is retrieved
{ 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; } //////////////////////////////////////////
// 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; } if (fSelectFailed) // assert(!fSelectAll)
{ Select(szFormattedPath, pMyFile, true); fFileSelected = true; }
BYTE bResponseLength = 0; if (fFileSelected) bResponseLength = ResponseLengthAvailable(); /////////////////////////////////////////
// Get response and fill file header //
/////////////////////////////////////////
switch(bResponseLength) { case 0x17: //
case 0x16: //
case 0x15: // Directory file
case 0x14: //
case 0x13: //
case 0x12: //
{ //////////////////////////////////////////
// Update file and directory pointers //
//////////////////////////////////////////
m_CurrentDirectory = *apfp; m_CurrentFile = *apfp;
if (pMyFile) { BYTE bDataOut[0x19]; GetResponse(0xC0, bResponseLength, bDataOut); pMyFile->file_id = (unsigned short)(bDataOut[4] * 256 + bDataOut[5]); pMyFile->file_size = (unsigned short)(bDataOut[2] * 256 + bDataOut[3]); pMyFile->file_type = directory; pMyFile->nb_file = bDataOut[15]; pMyFile->nb_sub_dir = bDataOut[14]; pMyFile->file_status = bDataOut[11]; memcpy(m_bLastACL, &bDataOut[7],4); //////////////////////////////////////////////////////////////
// Build ACL
//////////////////////////////////////////////////////////////
BYTE bACL[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; BYTE bKeyNibble = 0xFF; // flag to ignore AUT keys
// -- useless for
// Select(...)
BYTE bACLNibble;
//////////////////
// Dir Next AC //
//////////////////
bACLNibble = bDataOut[8] / 16;
CryptoToAccessACL(bACL, bACLNibble, bKeyNibble, 0); //////////////////////
// Delete File AC //
//////////////////////
bACLNibble = bDataOut[9] / 16;
CryptoToAccessACL(bACL, bACLNibble, bKeyNibble, 1);
/////////////////////
// CreateFile AC //
/////////////////////
bACLNibble = bDataOut[9] % 16;
CryptoToAccessACL(bACL, bACLNibble, bKeyNibble, 5);
////////////////////////////////////////////////////
// done remapping ACL; assigning to file header //
////////////////////////////////////////////////////
memcpy((void*)(pMyFile->access_cond), (void*)(bACL), 8); memset((void*)(pMyFile->applicationID), 0x00, 16); } } // end case Directory file
break; case 0x0F: // non-Directory file types
case 0x0E: //
{ //////////////////////////////////////////
// Update file and directory pointers //
//////////////////////////////////////////
m_CurrentFile = *apfp; apfp->ChopTail(); m_CurrentDirectory = *apfp;
if (pMyFile) { BYTE bDataOut[0x11];
GetResponse(0xC0, bResponseLength, bDataOut); pMyFile->file_size = (WORD)(bDataOut[2]*256+bDataOut[3]); pMyFile->file_id = (WORD)(bDataOut[4]*256+bDataOut[5]); pMyFile->file_status = bDataOut[11]; memcpy(m_bLastACL, &bDataOut[7],4); switch(bDataOut[6]) { case 0x01: pMyFile->file_type = Binary_File; break; case 0x02: pMyFile->file_type = Fixed_Record_File; break; case 0x04: pMyFile->file_type = Variable_Record_File; break; case 0x06: pMyFile->file_type = Cyclic_File; break; } 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; } else { ///////////////////////////////////////////////////////////
// number of records inaccessable except by file //
// size calculation above //
///////////////////////////////////////////////////////////
pMyFile->nb_file = 0x00; pMyFile->nb_sub_dir = 0x00; } //////////////////////////////////////////////////////////////
// Build ACL //
//////////////////////////////////////////////////////////////
BYTE bACL[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; BYTE bKeyNibble = 0xFF; // flag to ignore AUT keys
// -- useless for
// Select(...)
BYTE bACLNibble; ////////////////////
// Read file AC //
////////////////////
bACLNibble = bDataOut[8] / 16;
CryptoToAccessACL(bACL, bACLNibble, bKeyNibble, 0);
////////////////////////
// Write to file AC //
////////////////////////
bACLNibble = bDataOut[8] % 16;
CryptoToAccessACL(bACL, bACLNibble, bKeyNibble, 1);
///////////////////////
// Rehabilitate AC //
///////////////////////
bACLNibble = bDataOut[10] / 16;
CryptoToAccessACL(bACL, bACLNibble, bKeyNibble, 4); /////////////////////
// Invalidate AC //
/////////////////////
bACLNibble = bDataOut[10] % 16;
CryptoToAccessACL(bACL, bACLNibble, bKeyNibble, 3); ////////////////////////
// Create Record AC //
////////////////////////
if (bDataOut[6] != 0x01) // omit create record file
// AC for binary file
{ bACLNibble = bDataOut[9] % 16; CryptoToAccessACL(bACL, bACLNibble, bKeyNibble, 2); }
////////////////////////////////////////////////////
// done remapping ACL; assigning to file header //
////////////////////////////////////////////////////
memcpy((void*)(pMyFile->access_cond), (void*)(bACL), 8); memset((void*)(pMyFile->applicationID), 0x00, 16); } } // end case non-Directory file
break;
default: break; } }
void CCryptoCard::SelectParent() { CLockWrap wrap(&m_IOPLock); RequireSelect();
///////////////////////////////////////////////////
// If current directory is root, reselect root //
///////////////////////////////////////////////////
if (m_CurrentDirectory.NumComponents() == 1) { Select(0x3F00); m_CurrentFile = m_CurrentDirectory; } else { if (m_CurrentDirectory == m_CurrentFile) { m_CurrentDirectory.ChopTail(); Select(m_CurrentDirectory.Tail().GetShortID()); m_CurrentFile = m_CurrentDirectory; } else { Select(m_CurrentDirectory.Tail().GetShortID()); m_CurrentFile = m_CurrentDirectory; } } }
void CCryptoCard::VerifyKey(const BYTE bKeyNumber, const BYTE bKeyLength, const BYTE* bKey) { CLockWrap wrap(&m_IOPLock);
SendCardAPDU(0xF0, 0x2A, 0x00, bKeyNumber, bKeyLength, bKey, 0, NULL); }
void CCryptoCard::VerifyCHV(const BYTE bCHVNumber, const BYTE* bCHV) { CLockWrap wrap(&m_IOPLock); SendCardAPDU(0xC0, insVerifyChv, 0x00, bCHVNumber, 0x08, bCHV, 0, NULL); }
void CCryptoCard::VerifyTransportKey(const BYTE *bKey) { CLockWrap wrap(&m_IOPLock); VerifyKey(1, 8, bKey); }
void CCryptoCard::GetChallenge(const DWORD dwNumberLength, BYTE* bRandomNumber) { CLockWrap wrap(&m_IOPLock);
const DWORD dwMaxLen = 64;
DWORD dwRamainingBytes = dwNumberLength; BYTE *bpBuf = bRandomNumber;
while(dwRamainingBytes) { BYTE bNumGet = (dwRamainingBytes > dwMaxLen) ? dwMaxLen : dwRamainingBytes;
SendCardAPDU(0xC0, 0x84, 0x00, 0x00, 0, NULL, bNumGet, bpBuf);
bpBuf += bNumGet; dwRamainingBytes -= bNumGet; } }
void CCryptoCard::ExternalAuth(const KeyType kt, const BYTE bKeyNb, const BYTE bDataLength, const BYTE* bData) { CLockWrap wrap(&m_IOPLock);
//BYTE bAlgo_ID = AsPrivateAlgId(kt);
SendCardAPDU(0xC0, 0x82, 0, bKeyNb, bDataLength, bData, 0, NULL); }
void CCryptoCard::InternalAuth(const KeyType kt, const BYTE bKeyNb, const BYTE bDataLength, const BYTE* bDataIn, BYTE* bDataOut) { CLockWrap wrap(&m_IOPLock);
if ((bDataLength < 0x40) || (bDataLength > 0x80)) throw iop::Exception(iop::ccAlgorithmIdNotSupported);
SendCardAPDU(0xC0, insInternalAuth, 0, bKeyNb, bDataLength, bDataIn, 0, NULL);
GetResponse(0xC0, ResponseLengthAvailable(), bDataOut); }
void CCryptoCard::WritePublicKey(const CPublicKeyBlob aKey, const BYTE bKeyNum) { CLockWrap wrap(&m_IOPLock);
WORD wOffset;
Select(0x1012); WORD wKeyBlockLen = 7 + 5 * aKey.bModulusLength / 2;
scu::AutoArrayPtr<BYTE> aabKeyBlob(new BYTE[wKeyBlockLen]);
aabKeyBlob[0] = HIBYTE(wKeyBlockLen); aabKeyBlob[1] = LOBYTE(wKeyBlockLen); aabKeyBlob[2] = bKeyNum + 1; // Cryptoflex key numbers are offset by one on the file...
memcpy((void*) &aabKeyBlob[3], (void*)&aKey.bModulus, aKey.bModulusLength); // Would need to set Montgomery constants here, but since nobody seems
// to know what they are...
// Montgomery constants take 3 * modulus_length / 2 bytes
memcpy((void*) &aabKeyBlob[3 + aKey.bModulusLength + (3 * aKey.bModulusLength / 2)], aKey.bExponent,4);
wOffset = bKeyNum * wKeyBlockLen;
WriteBinary(wOffset, wKeyBlockLen, aabKeyBlob.Get()); }
void CCryptoCard::GetSerial(BYTE* bSerial, size_t &SerialLength) { CLockWrap wrap(&m_IOPLock);
try { FILE_HEADER fh; Select("/3f00/0002", &fh);
if (SerialLength < fh.file_size) { SerialLength = fh.file_size; return; }
ReadBinary(0, fh.file_size, bSerial); } catch(Exception &rExc) { if(rExc.Cause()==ccFileNotFound || rExc.Cause()==ccFileNotFoundOrNoMoreFilesInDf) SerialLength = 0; else throw; } }
void CCryptoCard::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 = wKeyBlockLength * bKeyNum; scu::AutoArrayPtr<BYTE> aabBuffer(new BYTE[wKeyBlockLength]);
ReadBinary(wOffset, wKeyBlockLength, aabBuffer.Get());
aKey->bModulusLength = ((wKeyBlockLength - 7) * 2) / 5;
memcpy((void*)aKey->bModulus, (void*)&aabBuffer[3], aKey->bModulusLength); memcpy((void*)aKey->bExponent, (void*)&aabBuffer[wKeyBlockLength - 4], 4); }
void CCryptoCard::WritePrivateKey(const CPrivateKeyBlob aKey, const BYTE bKeyNum) { CLockWrap wrap(&m_IOPLock);
Select(0x0012);
WORD wHalfModulus = aKey.bPLen; // Check that the lengths are all equal?
WORD wKeyBlockLength = wHalfModulus * 5 + 3; WORD wOffset = bKeyNum * wKeyBlockLength; scu::SecureArray<BYTE> aabKeyBlob(wKeyBlockLength);
aabKeyBlob[0] = HIBYTE(wKeyBlockLength); aabKeyBlob[1] = LOBYTE(wKeyBlockLength); aabKeyBlob[2] = bKeyNum + 1; // Cryptoflex key numbers are offset by one on the file...
memcpy(&aabKeyBlob[3 ], aKey.bP.data(), wHalfModulus); memcpy(&aabKeyBlob[3 + wHalfModulus], aKey.bQ.data(), wHalfModulus); memcpy(&aabKeyBlob[3 + 2 * wHalfModulus], aKey.bInvQ.data(), wHalfModulus); memcpy(&aabKeyBlob[3 + 3 * wHalfModulus], aKey.bKsecModP.data(), wHalfModulus); memcpy(&aabKeyBlob[3 + 4 * wHalfModulus], aKey.bKsecModQ.data(), wHalfModulus);
WriteBinary(wOffset, wKeyBlockLength, aabKeyBlob.data()); }
CPublicKeyBlob CCryptoCard::GenerateKeyPair(const BYTE *bpPublExp, const WORD wPublExpLen, const BYTE bKeyNum, const KeyType kt) {
// This function generates a key-pair, using the public exponent as specified in
// in CPublicKeyBlob parameter. The private key is stored in the private
// key file at position specified by bKeyNum. The public key components are
// returned through the CPublicKeyBlob parameter. Prior to call, the correct
// DF containing the key file must be selected.
// Implementation:
// The offset of the key in the private key file is proportional to the key number
// and it is assumed that all keys in a private key file have the same length. It
// assumes that there is a public key file available with space for at least one
// public key. The public key will always be written to the first position in the
// public key file.
BYTE bModulusLength;
switch(kt) { case ktRSA512: bModulusLength = 0x40; break;
case ktRSA768: bModulusLength = 0x60; break;
case ktRSA1024: bModulusLength = 0x80; break;
default: throw iop::Exception(iop::ccAlgorithmIdNotSupported);
}
// Check public exponent size and copy to 4 byte buffer
if(wPublExpLen < 1 || wPublExpLen > 4) throw iop::Exception(iop::ccInvalidParameter);
BYTE bPublExponent[4]; memset(bPublExponent,0,4); memcpy(bPublExponent,bpPublExp,wPublExpLen);
// Pre-define public key
CPublicKeyBlob PublKey;
PublKey.bModulusLength = bModulusLength; memset(PublKey.bModulus,0,bModulusLength); memset(PublKey.bExponent,0,4);
WritePublicKey(PublKey, 0); // Write in first position.
// Specify the correct key number in this position
BYTE bKeyNumPlus1 = bKeyNum + 1; // Cryptoflex key numbers are offset by one on the file...
Select(0x1012); WriteBinary(2, 1, &bKeyNumPlus1);
// Pre-define private key
CPrivateKeyBlob PrivKey; PrivKey.bPLen = bModulusLength/2; memset(PrivKey.bP.data(),0,PrivKey.bPLen);
PrivKey.bQLen = bModulusLength/2; memset(PrivKey.bQ.data(),0,PrivKey.bQLen);
PrivKey.bInvQLen = bModulusLength/2; memset(PrivKey.bInvQ.data(),0,PrivKey.bInvQLen);
PrivKey.bKsecModQLen = bModulusLength/2; memset(PrivKey.bKsecModQ.data(),0,PrivKey.bKsecModQLen);
PrivKey.bKsecModPLen = bModulusLength/2; memset(PrivKey.bKsecModP.data(),0,PrivKey.bKsecModPLen);
WritePrivateKey(PrivKey, bKeyNum); // Write in actual position.
// Generate the key pair
SendCardAPDU(0xF0, insKeyGeneration, bKeyNum, bModulusLength, 4, bPublExponent, 0, NULL); ReadPublicKey(&PublKey,0);
return PublKey;
}
void CCryptoCard::ChangeCHV(const BYTE bKey_nb, 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(0xF0, insChangeChv, 0x00, bKey_nb, 0x10, bDataIn.data(), 0, NULL);
Dirty(true); }
void CCryptoCard::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 = 1; WriteBinary(12, 1, &bRemaingAttempts); Dirty(true); VerifyCHV(bKey_nb,bNewCHV);
}
void CCryptoCard::UnblockCHV(const BYTE bKey_nb, 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(0xF0, insUnblockChv, 0x00, bKey_nb, 0x10, bDataIn.data(), 0, NULL);
Dirty(true); }
void CCryptoCard::ChangeUnblockKey(const BYTE bKey_nb, const BYTE *bNewPIN) { 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(13, 8, bNewPIN); }
void CCryptoCard::ChangeTransportKey(const BYTE *bNewKey) {
CLockWrap wrap(&m_IOPLock); Select("/3f00/0011");
//////////////////////////////////////////
// Build byte string to write to card //
//////////////////////////////////////////
BYTE bKeyString[10] = { 0x08, // length of key
0x00, // tag to identify key as a DES key
0, 0, 0, 0, 0, 0, 0, 0 // 8 bytes for key
}; // Copy the template into secure arry for storing the key
const WORD wKeySize = 10; scu::SecureArray<BYTE> newbKeyStr(bKeyString,wKeySize); //////////////////////////////////////////////////////
// insert new key into key string to pass to card //
//////////////////////////////////////////////////////
memcpy((void*)(newbKeyStr.data() + 2), (void*)bNewKey, 8);
WriteBinary(13, wKeySize, newbKeyStr.data());
BYTE bRemainingAttempt = 1; // Minumum # of verification attempts remaining before card is blocked
WriteBinary(24, 1, &bRemainingAttempt);
// Make a (hopefully) successfull verification to re-set attempt counter
VerifyTransportKey(bNewKey); }
void CCryptoCard::ChangeACL(const BYTE *bACL) { throw iop::Exception(iop::ccUnsupportedCommand); }
void CCryptoCard::AccessToCryptoACL(bool* fAccessACL, CryptoACL* pCryptoACL) { if (fAccessACL[0] == true)
pCryptoACL->Level = 0;
else { pCryptoACL->Level = 0x0F; for(BYTE i = 1; i < 3; i++) { if (fAccessACL[i] == true) { pCryptoACL->CHVcounter++; pCryptoACL->CHVnumber = i; } if (pCryptoACL->CHVcounter > 1 ) { // More than one CHV for a single action
// is not supported by Cryptoflex
throw iop::Exception(iop::ccAclNotSupported); } }
for(i = 3; i < 8; i++) { if (fAccessACL[i] == true) { pCryptoACL->AUTcounter++; pCryptoACL->AUTnumber = i - 3; // AUT0 starts with an index of 3
} if (pCryptoACL->AUTcounter > 1) { // More than one AUT for a single action
// is not supported by Cryptoflex
throw iop::Exception(iop::ccAclNotSupported); } } } if (pCryptoACL->CHVcounter == 1 && pCryptoACL->AUTcounter == 1) { if(pCryptoACL->CHVnumber == 1) pCryptoACL->Level = 8; else pCryptoACL->Level = 9; }
if (pCryptoACL->CHVcounter == 1 && pCryptoACL->AUTcounter == 0) { if(pCryptoACL->CHVnumber == 1) pCryptoACL->Level = 1; else pCryptoACL->Level = 2; }
if (pCryptoACL->CHVcounter == 0 && pCryptoACL->AUTcounter == 1) pCryptoACL->Level = 4; }
void CCryptoCard::CryptoToAccessACL(BYTE* bAccessACL, const BYTE bACLNibble, const BYTE bKeyNibble, const BYTE bShift) { switch (bACLNibble) { case 0x00: bAccessACL[0] = (1 << bShift) | bAccessACL[0]; break; case 0x01: case 0x06: case 0x08: bAccessACL[1] = (1 << bShift) | bAccessACL[1]; break; case 0x02: case 0x07: case 0x09: bAccessACL[2] = (1 << bShift) | bAccessACL[2]; break; default: // bAccessACL already initialized to 0x00
break; }
if (bACLNibble == 0x04 || bACLNibble == 0x08 || bACLNibble == 0x09) { ////////////////////////////////////////////////////////////////////////////
// Cyberflex only supports 5 AUT keys, and AUT0 starts at bAccessACL[3] //
////////////////////////////////////////////////////////////////////////////
if (bKeyNibble < 0x05) bAccessACL[3 + bKeyNibble] = (1 << bShift) | bAccessACL[3 + bKeyNibble]; } }
void CCryptoCard::DefaultDispatchError(ClassByte cb, Instruction ins, StatusWord sw) const { CauseCode cc; bool fDoThrow = true; switch (sw) { case 0x6281: cc = ccDataPossiblyCorrupted; break; case 0x6300: cc = ccAuthenticationFailed; break; case 0x6982: cc = ccAccessConditionsNotMet; break; case 0x6981: cc = ccNoEfExistsOrNoChvKeyDefined; break; case 0x6985: cc = ccNoGetChallengeBefore; break; case 0x6986: cc = ccNoEfSelected; break; case 0x6A83: cc = ccOutOfRangeOrRecordNotFound; break;
case 0x6A84: cc = ccInsufficientSpace; break; case 0x6A82: cc = ccFileNotFoundOrNoMoreFilesInDf; break;
default: fDoThrow = false; break; }
if (fDoThrow) throw Exception(cc, cb, ins, sw);
CSmartCard::DefaultDispatchError(cb, ins, sw); }
void CCryptoCard::DispatchError(ClassByte cb, Instruction ins, StatusWord sw) const { CauseCode cc; bool fDoThrow = true;
switch (ins) { case insChangeChv: // fall-through intentional
case insUnblockChv: switch (sw) { case 0x6300: cc = ccChvVerificationFailedMoreAttempts; break; case 0x6581: cc = ccUpdateImpossible; break;
default: fDoThrow = false; break; } break;
case insCreateFile: switch (sw) { case 0x6A80: cc = ccFileIdExistsOrTypeInconsistentOrRecordTooLong; break;
default: fDoThrow = false; break; } break;
case insGetResponse: switch (sw) { case 0x6500: cc = ccTooMuchDataForProMode; break;
default: fDoThrow = false; break; } break;
case insReadBinary: switch (sw) { case 0x6B00: cc = ccCannotReadOutsideFileBoundaries; break;
default: fDoThrow = false; break; } break;
case insVerifyChv: switch (sw) { case 0x6300: cc = ccChvVerificationFailedMoreAttempts; break;
default: fDoThrow = false; break; } break; default: fDoThrow = false; break; }
if (fDoThrow) throw Exception(cc, cb, ins, sw);
DefaultDispatchError(cb, ins, sw); }
void CCryptoCard::DoReadBlock(WORD wOffset, BYTE *pbBuffer, BYTE bLength) { SendCardAPDU(0xC0, insReadBinary, HIBYTE(wOffset), LOBYTE(wOffset), 0, 0, bLength, pbBuffer); } void CCryptoCard::DoWriteBlock(WORD wOffset, BYTE const *pbBuffer, BYTE cLength) { SendCardAPDU(0xC0, insUpdateBinary, HIBYTE(wOffset), LOBYTE(wOffset), cLength, pbBuffer, 0, 0); } bool CCryptoCard::SupportLogout() { bool fSuccess = true; try { CLockWrap wrap(&m_IOPLock); SendCardAPDU(0xF0, 0x22, 0x07, 0, 0, NULL, 0, NULL); } catch(...) { fSuccess = false; }
return fSuccess; }
void CCryptoCard::GetACL(BYTE *bACL) { CLockWrap wrap(&m_IOPLock);
memcpy(bACL,m_bLastACL,4);
BYTE bTemp[5];
SendCardAPDU(0xF0, 0xC4, 0x00, 0x00, 0x00, NULL, 0x03, bTemp);
memcpy(&bACL[4], bTemp, 3);
}
|