You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1567 lines
46 KiB
1567 lines
46 KiB
// 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);
|
|
|
|
}
|