|
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.
|