Build Better Software. Faster.
Visit our company site at: www.techsoft3d.com
 

White Papers Index

Protecting Custom HSF-based Files
Travis

Using HSF as a basis for a custom file format

It is becoming common for developers to use the HSF format as a basis for a proprietary file format. Such a custom format is not officially an HSF file, but it does contain HSF information which other HSF-reading applications might attempt to read. If it desirable to prevent other applications from reading the HSF information, some security measures should be taken.

A brute force approach would be to modify some (or all) of the HSF opcodes to that they write out data in a different format from the HSF specification. However, this poses a maintenance challenge since a significant benefit of basing a file on HSF is to be able to use the HOOPS/Stream toolkit as-is, and drop in future versions of it. This means that the HSF data should be left intact and formatted according to the HSF specification, and a different protection measure should be taken.

A better way to prevent other HSF-reading applications from reading the HSF data is for developers to register a custom opcode which has a known amount of data associated with it, and place that opcode at the beginning of the file. The length of the data associated with the opcode is unknown to vanilla HSF-reading applications, and thus they will not be able to correctly parse or skip over the data. A basic implementation is to simply export the HSF TKE_Start_User_Data_Opcode, but not specify any length. The following sample code defines a new opcode handler called TK_My_User_Data, and registers it to handle the TKE_Start_User_Data opcode. The opcode handler is essentially a copy of the TK_User_Data opcode handler, but with modification such that it reads and writes a predefined length of data, rather than getting the length from the file. The length would be known to the reading and writing application, but not to any third parties.

A) Registration

#include "BStream.h"
#include "BOpcodeHandler.h" /* this header defines the
TKE_Start_User_Data opcode value */
#include “CustomHeader.h"

FileExportClass:: FileExportClass()
{
         …
         SetOpcodeHandler (TKE_Start_User_Data, new TK_My_User_Data);
         …
}


B) TK_My_User_Data.h

class BBINFILETK_API2 TK_My_User_Data :public BBaseOpcodeHandler 
{ 
     protected:
         int m_size; /* Number of bytes of user data */
         char m_data[50]; /* User data */
         // internal use
         void set_data (int size, char const * bytes = 0); // for
internal use only
     public:
         /* constructor */
         TK_My_User_Data (): BBaseOpcodeHandler 
(TKE_Start_User_Data), m_size (50), m_data (0) {} ~ TK_My_User_Data ();
                       TK_Status    Read (BStreamFileToolkit & tk);
                       TK_Status    Write (BStreamFileToolkit & tk);
                       TK_Status    Execute (BStreamFileToolkit & tk);
                       void         Reset (void) alter;
           /* Sets the data buffer. Allocates a buffer which can hold 
'size' bytes and, if specified, copies 'bytes' into the buffer */ void SetUserData (int size, char const * bytes = 0) { set_data (size, bytes); }
           /* Returns the address of the data buffer */
                       char const * GetUserData (void) const 
{ return m_data; }
           /* Returns the address of the data buffer, which may be modified directly */
                       char alter * GetUserData (void) alter 
{ return m_data; }
           /* Returns the size of the data buffer */
                       int GetSize (void) const 
{ return m_size; } };

C) TK_My_User_Data.cpp

TK_Status TK_My_User_Data::Write (BStreamFileToolkit & tk) 
{
    TK_Status          status = TK_Normal;
                       
    switch (m_stage) {
       case 0: {
           if ((status = PutOpcode (tk, 0)) != TK_Normal)
               return status;
           m_stage++;
       }   nobreak;
       case 1: {
           //write m_size*sizeof(integer) of uninitialized garbage
           if ((status = PutData (tk, m_data, m_size)) != TK_Normal)
               return status;
           m_stage++;
       }   break;
       default:
           return tk.Error();
    }
    return status;
}
TK_Status TK_My_User_Data::Read (BStreamFileToolkit & tk) 
{
     TK_Status          status = TK_Normal;
     switch (m_stage) {
        case 0: {
            if ((status = GetData (tk, m_data, m_size)) != TK_Normal)
                return status;
            m_stage++;
        }   break;


        default:
           return tk.Error();
        }
        return status;
}
void TK_My_User_Data::Reset (void) 
{
      m_size = 0;
      BBaseOpcodeHandler::Reset();
}

The above approach is likely to be sufficient enough to prevent other applications from extracting HSF data from the file. If further security is desired, the next step would be to simply encrypt the entire file after the toolkit is finished with it.

Embedding custom data inside an HSF File

Developers can embed custom data inside a regular HSF file using the TKE_Start_User_Data opcode. Since other products could export HSF files which also contain TKE_Start_User_Data opcodes, there should be some mechanism to identify the custom data. This is usually achieved by having the first few bytes of each TKE_Start_User_Data chunk be a unique identifier. Usage of this opcode is discussed in Section 4 of the HOOPS/Stream Programming Guide.

Using this approach, any HSF-reading application will be able to reliably read the vanilla HSF opcodes, and skip over the custom data. The custom data is generally secure, since vanilla reading applications are not aware of what the custom data is or how it’s formatted. If a tighter, more formal security mechanism is desired, the custom data could be encoded or even encrypted. The only requirement is that the custom data be exported using the TKE_Start_User_Data opcode.

 
 

 

 

 

 

©2004-06 Tech Soft 3D All Rights Reserved. Privacy | Legal