/*========================================================================= * SnaFile.c : Manage files with checksums and basic encryption. * * The checksum is a simple xor check. * To use files w/ checksums you must open, close, read and write to a file * using SNA_fn_bFOpen, SNA_fn_bFClose, etc. * * The functions automatically compute the checksum and/or crypt the datas upon reading or writing. * * How to create a file : * Open it with SNA_fn_bFOpen (specify if you want to use checksum and/or encryption) * Write data with SNA_fn_ulFWrite (you may call for it several times) * When ALL datas are written, call SNA_fn_bWriteChecksum, * then SNA_fn_bFClose * * * How to read a file : * Open it with SNA_fn_bFOpen * Read data with SNA_fn_ulFRead (you may call for it several times) * When ALL datas are read, call SNA_fn_bReadChecksum, * then SNA_fn_bFClose * Now, you can check if checksum is good with SNA_fn_bTestChecksum. * * You can put several checksums in one file. * Simply call SNA_fn_bWriteChecksum when you want to add a checksum * for datas allready written. * Checksums only control datas written BETWEEN two consecutive SNA_fn_bWriteChecksum. * Be sure to read checksums at the same place you wrote them. * * Do NOT use fseek when writing or reading, it would make the checksum useless. * You can use other file functions like ftell like this: ftell( stSNAFile->p_xFile ); * * Version 1.0 * Creation date 25/09/98 * Revision date * * (c) Ubi R&D 1998 *========================================================================= */ #define min(a, b) (((a) < (b)) ? (a) : (b)) #include #include #include //#include "acp_opfi.h" #include "snafile.h" #include "TMP.h" #include "mmg.h" #include "snammg.h" #include "snarlt.h" // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /** * SNA_fn_vSetXorCode * Sets the xor key for encryption of a file. * To call after SNA_fn_bFOpen(..). */ void SNA_fn_vSetCryptKey( unsigned char _ulCryptKey, struct SNA_tdstFile_ *_p_stFile ) { _p_stFile->ulCryptKey = _ulCryptKey; } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /** * SNA_fn_vResetChecksum * Sets the checksum of a file to 0. * To use after reading and testing it's value, before further reading. */ void SNA_fn_vResetChecksum( struct SNA_tdstFile_ *_p_stFile ) { _p_stFile->ulChecksum = 0; _p_stFile->ucBytesInChecksumBuffer = 0; } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /** * SNA_fn_bTestChecksum * Tests if checksum is ok (==0). * Tu use JUST AFTER SNA_bReadChecksum */ ACP_tdxBool SNA_fn_bTestChecksum( struct SNA_tdstFile_ *_p_stFile ) { return (_p_stFile->ulChecksum == 0); } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /** * SNA_fn_bWriteChecksum * Write the computed checksum in the file. * The written checksum checks datas between the current position and * the beginning of the file or the position of the last call to SNA_fn_bWriteChecksum */ ACP_tdxBool SNA_fn_bWriteChecksum( struct SNA_tdstFile_ *_p_stFile ) { if( SNA_fn_bFileUseChecksum( _p_stFile ) ) { unsigned long ulTempChecksum = _p_stFile->ulChecksum; DWORD dwlNumberOfBytesWriten; if( _p_stFile->ucBytesInChecksumBuffer ) { memset( &_p_stFile->a4_cChecksumBuffer[_p_stFile->ucBytesInChecksumBuffer], 4-_p_stFile->ucBytesInChecksumBuffer, 0 ); ulTempChecksum ^= *(unsigned long *)_p_stFile->a4_cChecksumBuffer; _p_stFile->ucBytesInChecksumBuffer = 0; } _p_stFile->ulChecksum = 0; SNA_M_WRITEFILE(_p_stFile->hFile , &ulTempChecksum , sizeof(ulTempChecksum) , &dwlNumberOfBytesWriten , NULL); return (dwlNumberOfBytesWriten != 0); } return TRUE; } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /** * Read file checksum (if file uses checksums) * Return TRUE if checksum was read ok or FALSE if there was a read error. * To test the checksum value, use SNA_fn_bTestChecksum */ ACP_tdxBool SNA_fn_bReadChecksum( struct SNA_tdstFile_ *_p_stFile ) { if( SNA_fn_bFileUseChecksum( _p_stFile ) ) { DWORD dwNbRead; unsigned long ulTempChecksum = 0; if( _p_stFile->ucBytesInChecksumBuffer ) { memset( &_p_stFile->a4_cChecksumBuffer[_p_stFile->ucBytesInChecksumBuffer], 4-_p_stFile->ucBytesInChecksumBuffer, 0 ); _p_stFile->ulChecksum ^= *(unsigned long *)_p_stFile->a4_cChecksumBuffer; _p_stFile->ucBytesInChecksumBuffer = 0; } if (!SNA_M_READFILE(_p_stFile->hFile , &ulTempChecksum , sizeof(ulTempChecksum) , &dwNbRead , NULL)) { _p_stFile->ulChecksum = -1; _p_stFile -> bReadSuccess = FALSE; return FALSE; } _p_stFile -> bReadSuccess = TRUE; _p_stFile->ulChecksum ^= ulTempChecksum; } return TRUE; } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /** * Opens a file. * ucOptions are a bitwise combo of SNA_C_ucUseChecksum and/or SNA_C_ucUseEncryption and/or SNA_C_ucUseMemBuffer */ void SNA_fn_vInitFile(unsigned char ucOptions, struct SNA_tdstFile_ *_p_stFile ) { _p_stFile->ulChecksum = 0; _p_stFile->ucBytesInChecksumBuffer = 0; _p_stFile->ucOptions = ucOptions; _p_stFile->p_fn_bWriteWithoutOptions = SNA_fn_bWriteToFile; _p_stFile->p_fn_bReadWithoutOptions = SNA_fn_bReadFromFile; if( SNA_fn_bFileUseEncryption( _p_stFile ) ) { _p_stFile->ulCryptKey = 0x6AB5CC79; // Set default encryption key. _p_stFile->p_fn_bDefaultWrite = SNA_fn_bCryptAndWriteToFile; _p_stFile->p_fn_bDefaultRead = SNA_fn_bReadAndDecryptFromFile; } else { _p_stFile->ulCryptKey = 0; _p_stFile->p_fn_bDefaultWrite = SNA_fn_bWriteToFile; _p_stFile->p_fn_bDefaultRead = SNA_fn_bReadFromFile; } } ACP_tdxBool SNA_fn_bFOpen( char *_p_szfilename, unsigned char ucMode, unsigned char ucOptions, struct SNA_tdstFile_ *_p_stFile ) { DWORD dwCreate,dwAccess; /* _p_stFile->ulChecksum = 0; _p_stFile->ucBytesInChecksumBuffer = 0; _p_stFile->ucOptions = ucOptions; _p_stFile->p_fn_bWriteWithoutOptions = SNA_fn_bWriteToFile; _p_stFile->p_fn_bReadWithoutOptions = SNA_fn_bReadFromFile; if( SNA_fn_bFileUseEncryption( _p_stFile ) ) { _p_stFile->ulCryptKey = 0x6AB5CC79; // Set default encryption key. _p_stFile->p_fn_bDefaultWrite = SNA_fn_bCryptAndWriteToFile; _p_stFile->p_fn_bDefaultRead = SNA_fn_bReadAndDecryptFromFile; } else { _p_stFile->ulCryptKey = 0; _p_stFile->p_fn_bDefaultWrite = SNA_fn_bWriteToFile; _p_stFile->p_fn_bDefaultRead = SNA_fn_bReadFromFile; } */ SNA_fn_vInitFile (ucOptions , _p_stFile); switch(ucMode) { case SNA_C_ucWrite: dwCreate = CREATE_ALWAYS; dwAccess = GENERIC_WRITE; { DWORD dwAttrib = GetFileAttributes(_p_szfilename); if(dwAttrib & FILE_ATTRIBUTE_READONLY) SetFileAttributes(_p_szfilename , dwAttrib & ~FILE_ATTRIBUTE_READONLY); } break; case SNA_C_ucRead: dwCreate = OPEN_EXISTING; dwAccess = GENERIC_READ; break; default: _asm {int 3h}; break; } _p_stFile -> hFile = SNA_M_CREATEFILE(_p_szfilename , dwAccess , 0 , NULL , dwCreate , FILE_ATTRIBUTE_NORMAL , NULL); _p_stFile -> bReadSuccess = (_p_stFile -> hFile != INVALID_HANDLE_VALUE); return (_p_stFile -> bReadSuccess); } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /** * Closes a file * Be sure to call SNA_fn_bTestChecksum before Close to check the value of the file checksum. */ ACP_tdxBool SNA_fn_bFClose( struct SNA_tdstFile_ *_p_stFile ) { int iRes; // _p_stFile->ulChecksum = 0; _p_stFile->ucBytesInChecksumBuffer = 0; iRes = (int) SNA_M_CLOSEHANDLE(_p_stFile->hFile); _p_stFile->hFile = INVALID_HANDLE_VALUE; return (iRes == 0); } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /** * SNA_fn_ulFWrite * Write datas into a file with or without encryption and checksum. * (it checks the option field in SNA_tdstFile) * Returns the number of items written. */ size_t SNA_fn_ulFWrite( void *_p_vBuffer, size_t _ulSize, size_t _ulCount, struct SNA_tdstFile_ *_p_stFile ) { unsigned long ulTotalSize = _ulSize*_ulCount; // If nothing to write, then nothing to do... if( ! ulTotalSize ) return 0; // Check if this file uses checksums. if( SNA_fn_bFileUseChecksum( _p_stFile ) ) { /* It uses checksums. */ unsigned char ucRest; char *p_cBuffer = _p_vBuffer; // If there was some bytes remaining in the checksum buffer, take them into account. if( _p_stFile->ucBytesInChecksumBuffer ) { // First, fill the buffer. unsigned char ucBytesToCopy = (unsigned char)min( ulTotalSize, (unsigned long)(4-_p_stFile->ucBytesInChecksumBuffer) ); memcpy( &_p_stFile->a4_cChecksumBuffer[_p_stFile->ucBytesInChecksumBuffer], p_cBuffer, ulTotalSize ); // Update buffer info _p_stFile->ucBytesInChecksumBuffer += ucBytesToCopy; p_cBuffer += ucBytesToCopy; ulTotalSize -= ucBytesToCopy; // If buffer is full, compute checksum. if( _p_stFile->ucBytesInChecksumBuffer == 4 ) { _p_stFile->ulChecksum ^= *(unsigned long *)_p_stFile->a4_cChecksumBuffer; _p_stFile->ucBytesInChecksumBuffer = 0; } } // Compute number of long integers in the datas. ucRest = (unsigned char)(ulTotalSize & 0x03); // =Total Size % 4 ulTotalSize >>= 2; // For each long integer, update the checksum. while( ulTotalSize ) { _p_stFile->ulChecksum ^= *(unsigned long *)p_cBuffer; p_cBuffer += 4; ulTotalSize--; } // Trailing bytes (less than 4 bytes) are put in the checksum buffer. if( ucRest ) { memcpy( &_p_stFile->a4_cChecksumBuffer[_p_stFile->ucBytesInChecksumBuffer], p_cBuffer, ucRest ); _p_stFile->ucBytesInChecksumBuffer += ucRest; } } // end of checksum calculation // Then, write the data to the file. if( ! _p_stFile->p_fn_bDefaultWrite( _p_vBuffer, ulTotalSize, _p_stFile ) ) return 0; return _ulCount; } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /** * SNA_fn_ulFRead * Read datas from a file with or without encryption and checksum. * (it checks the option field in SNA_tdstFile) * Returns the number of items read. */ size_t SNA_fn_ulFRead( void *_p_vBuffer, size_t _ulSize, size_t _ulCount, struct SNA_tdstFile_ *_p_stFile ) { unsigned long ulTotalSize = _ulSize*_ulCount; // If nothing to read, then nothing to do... if( ! ulTotalSize ) return 0; // First, read the datas. if( ! _p_stFile->p_fn_bDefaultRead( _p_vBuffer, ulTotalSize, _p_stFile ) ) return 0; // Check if this file uses checksums. if( SNA_fn_bFileUseChecksum( _p_stFile ) ) { unsigned long ulTotalSize = _ulSize*_ulCount; unsigned char ucRest; char *p_cBuffer = _p_vBuffer; if( _p_stFile->ucBytesInChecksumBuffer ) { unsigned char ucBytesToCopy = (unsigned char)min( ulTotalSize, (unsigned long)(4-_p_stFile->ucBytesInChecksumBuffer) ); memcpy( &_p_stFile->a4_cChecksumBuffer[_p_stFile->ucBytesInChecksumBuffer], p_cBuffer, ucBytesToCopy ); _p_stFile->ucBytesInChecksumBuffer += ucBytesToCopy; p_cBuffer += ucBytesToCopy; ulTotalSize -= ucBytesToCopy; if( _p_stFile->ucBytesInChecksumBuffer == 4 ) { _p_stFile->ulChecksum ^= *(unsigned long *)_p_stFile->a4_cChecksumBuffer; _p_stFile->ucBytesInChecksumBuffer = 0; } } ucRest = (unsigned char)(ulTotalSize & 0x03); // =Total Size % 4 ulTotalSize >>= 2; while( ulTotalSize ) { _p_stFile->ulChecksum ^= *(unsigned long *)p_cBuffer; p_cBuffer += 4; ulTotalSize--; } if( ucRest ) { memcpy( &_p_stFile->a4_cChecksumBuffer[_p_stFile->ucBytesInChecksumBuffer], p_cBuffer, ucRest ); _p_stFile->ucBytesInChecksumBuffer += ucRest; } } // End of checksum calculation return _ulCount; } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // For writing datas without encryption nor checksum calculation, // even if the file has been opened with such options size_t SNA_fn_ulFWriteWithoutOptions( void *_p_vBuffer, size_t _ulSize, size_t _ulCount, struct SNA_tdstFile_ *_p_stFile ) { unsigned long ulTotalSize = _ulSize*_ulCount; // If nothing to write, then nothing to do... if( ! ulTotalSize ) return 0; // write the data to the file without encryption nor checksum calculation if( ! _p_stFile->p_fn_bWriteWithoutOptions( _p_vBuffer, ulTotalSize, _p_stFile ) ) return 0; return _ulCount; } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // For reading datas without encryption nor checksum calculation, // even if the file has been opened with such options // Use this function ONLY if you have written the datas you want to read // with SNA_fn_ulFWriteWithoutOptions size_t SNA_fn_ulFReadWithoutOptions( void *_p_vBuffer, size_t _ulSize, size_t _ulCount, struct SNA_tdstFile_ *_p_stFile ) { unsigned long ulTotalSize = _ulSize*_ulCount; // If nothing to read, then nothing to do... if( ! ulTotalSize ) return 0; // read the datas without encryption nor checksum calculation. if( ! _p_stFile->p_fn_bReadWithoutOptions( _p_vBuffer, ulTotalSize, _p_stFile ) ) return 0; return _ulCount; } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ACP_tdxBool SNA_fn_bFileUseChecksum( struct SNA_tdstFile_ *_p_stFile ) { return (_p_stFile->ucOptions & SNA_C_ucUseChecksum) != 0; } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ACP_tdxBool SNA_fn_bFileUseEncryption( struct SNA_tdstFile_ *_p_stFile ) { return (_p_stFile->ucOptions & SNA_C_ucUseEncryption) != 0; } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /////// // Handle seek for sna file. // WARNING : you'd better not use seek with file with checksum and encryption. // Encryption is handled by seek only if the origin is the current position. // Checksum calculation is NOT handled by seek. // This function does not check the validity of the offset. // A bad offset (seek goes out of file) can cause problems in key calculation for files with encryption... int SNA_fn_bFseek( struct SNA_tdstFile_ *_p_stFile, long _lOffset, int _lOrigin ) { // fflush( _p_stFile->p_xFile ); DWORD dwMoveMethod; if( _lOrigin == SEEK_CUR && SNA_fn_bFileUseEncryption( _p_stFile ) ) { unsigned char *p_a_ucCryptKeys = (unsigned char *)&_p_stFile->ulCryptKey; unsigned long ulOffset = abs( _lOffset ); ACP_tdxBool ucForward = (_lOffset>0); if( _lOffset>0 ) while( ulOffset-- ) { p_a_ucCryptKeys[0] += p_a_ucCryptKeys[2]; p_a_ucCryptKeys[1] += p_a_ucCryptKeys[3]; } else while( ulOffset-- ) { p_a_ucCryptKeys[0] -= p_a_ucCryptKeys[2]; p_a_ucCryptKeys[1] -= p_a_ucCryptKeys[3]; } } if (_lOrigin == SEEK_SET) dwMoveMethod = FILE_BEGIN; else if (_lOrigin == SEEK_END) dwMoveMethod = FILE_END; else dwMoveMethod = FILE_CURRENT; return SNA_M_SETFILEPOINTER(_p_stFile->hFile , _lOffset , NULL , dwMoveMethod); } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * int SNA_fn_iFFlush( struct SNA_tdstFile_ *_p_stFile ) { return (int) SNA_M_FLUSHFILEBUFFERS (_p_stFile->hFile); } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Crypt the input data into the file. // Return FALSE if an IO error occured // To perform this, the datas to be written are copied into a buffer with encryption. // Then the buffer is written to the file. // Datas are cut into 2048 bytes blocs. ACP_tdxBool SNA_fn_bCryptAndWriteToFile( const void *_p_vData, unsigned long _ulSize, struct SNA_tdstFile_ *_p_stFile ) { unsigned char a_ucBuffer[2048]; unsigned long ulBytesInBuffer; const unsigned char *p_ucInput, *p_ucEndInput, *p_ucEndStep; unsigned char *p_ucOutput; unsigned char *p_a_ucCryptKeys = (unsigned char *)&_p_stFile->ulCryptKey; unsigned long ulBytesWritten; // Compute the begining and the end of the input buffer (=datas to be written). p_ucInput = _p_vData; p_ucEndInput = (char *)_p_vData + _ulSize; // For each 2048 bytes block. do { // Compute amount of data to copy to the buffer (max 2048) ulBytesInBuffer = p_ucEndInput - p_ucInput; if( ulBytesInBuffer > 2048 ) ulBytesInBuffer = 2048; // Compute the pointer to the end of this bloc. p_ucEndStep = p_ucInput + ulBytesInBuffer; // Destination = buffer. p_ucOutput = a_ucBuffer; // Perform the copy with encryption. while( p_ucInput < p_ucEndStep ) { /* *p_ucOutput++ = (*p_ucInput++ ^ p_a_ucCryptKeys[1]) + p_a_ucCryptKeys[0]; p_a_ucCryptKeys[0] += p_a_ucCryptKeys[2]; p_a_ucCryptKeys[1] += p_a_ucCryptKeys[3]; */ *p_ucOutput++ = (*p_ucInput++ ^ p_a_ucCryptKeys[1]); *((DWORD*)p_a_ucCryptKeys) = SNA_fn_dwGetCryptKey(*((DWORD*)p_a_ucCryptKeys)); } // Writes the buffer to disk. SNA_M_WRITEFILE(_p_stFile->hFile , a_ucBuffer , ulBytesInBuffer , &ulBytesWritten, NULL); // If not all datas were written (because of an error), we return FALSE. if( ulBytesWritten != ulBytesInBuffer ) { return FALSE; } // If not end of input, go for the next bloc. } while( p_ucInput < p_ucEndInput ); // all the datas were written ok, return TRUE. return TRUE; } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Write the input data into file without encryption. // Return FALSE if an IO error occured ACP_tdxBool SNA_fn_bWriteToFile( const void *_p_vData, unsigned long _ulSize, struct SNA_tdstFile_ *_p_stFile ) { DWORD dwBytesWritten; SNA_M_WRITEFILE(_p_stFile->hFile , _p_vData , _ulSize , &dwBytesWritten, NULL); return (dwBytesWritten == _ulSize); } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Read the data from the IO buffer and decrypt it. // Buffer is filled if needed // Return FALSE if an IO error occured ACP_tdxBool SNA_fn_bReadAndDecryptFromFile( void *_p_vData, unsigned long _ulSize, struct SNA_tdstFile_ *_p_stFile ) { unsigned char *p_ucBuffer = _p_vData; unsigned char *p_ucEndBuffer = p_ucBuffer + _ulSize; unsigned char *p_a_ucCryptKeys = (unsigned char *)&_p_stFile->ulCryptKey; // First, read the datas. DWORD dwBytesRead; _p_stFile -> bReadSuccess = SNA_M_READFILE(_p_stFile->hFile , p_ucBuffer , _ulSize , &dwBytesRead , NULL); if( dwBytesRead != _ulSize ) return FALSE; // Then, decrypt the datas. for( ; p_ucBuffer bReadSuccess = SNA_M_READFILE(_p_stFile->hFile , _p_vData , _ulSize , &dwBytesRead , NULL); return (dwBytesRead == _ulSize); }