view g23m/condat/com/src/comlib/sec_drv_prim.c @ 349:ed3e588600c1 default tip

README update for the successful completion of the L1 reconstruction project
author Mychaela Falconia <falcon@freecalypso.org>
date Mon, 30 Oct 2017 18:03:24 +0000
parents 509db1a7b7b8
children
line wrap: on
line source

#include "general.h"
#include "typedefs.h"
#include "vsi.h"
#include <stdlib.h>
#include <string.h>
#include "sec_drv.h"
#include "sec_drv_prim.h"
#include "ffs/ffs.h"

#ifndef NON_ENCRYPTED_MODE
#include "cl_des.h"
#include "cl_imei.h"
#endif


/*
Layout of flash:
	T_SEC_DRV_GLOBAL_CONF
	T_SEC_DRV_KEY (failure count)
	T_SEC_DRV_CONFIGURATION
	{
		T_SEC_DRV_KEY
		T_SEC_DRV_CATEGORY (T_SEC_DRV_CAT_HDR + SEC_DRV_CAT_MAX_SIZE*UINT8)
	}[configuration -> number of categories]
*/


/*******************************************
			Global variables
*******************************************/

#ifndef FFS_MODE_ENABLED
/* SIM lock data area in flash */
#pragma DATA_SECTION(d_mepd_data, ".mepd")
volatile const UINT8 d_mepd_data;  // Adress of first byte in 8 kb buffer
#endif /* FFS_MODE_ENABLED */

static T_SEC_DRV_GLOBAL_CONF gGlobalConf;
static INT8 gGlobalConfRead = FALSE;
INT8 sec_drv_fast_write = FALSE;
static INT16 gMaxCategories = 0;

#if (defined(RSA_HEAP_IS_INTERNAL) && defined(FIRSTBOOT_RSA_ENABLED)) || (defined(USE_RSAHEAP_FOR_FLASH) && !defined(FFS_MODE_ENABLED))
#ifdef USE_RSAHEAP_FOR_FLASH
char sec_drv_buffer[SEC_FLASH_SIZE];
#else /* USE_RSAHEAP_FOR_FLASH */
char sec_drv_buffer[RSA_HEAP_SIZE];
#endif /* USE_RSAHEAP_FOR_FLASH */
#endif


/*******************************************
			Trace functionality
*******************************************/
#ifdef SEC_TRACES_ENABLED
static void _trace(const char *str)
{
	T_FFS_FD test_file;
	test_file = ffs_open("/testfile.txt", FFS_O_CREATE | FFS_O_WRONLY | FFS_O_APPEND);
    if (test_file > 0)
    {
        ffs_write(test_file, (char *)str, strlen((char *)str));
        ffs_close(test_file);
    }
}
void test_trace(const char *str)
{
	char *s = (char *)str;
	T_FFS_FD test_file;
	test_file = ffs_open("/testfile.txt", FFS_O_CREATE | FFS_O_WRONLY | FFS_O_APPEND);
    if (test_file > 0)
    {
		ffs_write(test_file, s, strlen(s));
        s = "\n";
        ffs_write(test_file, s, strlen(s));
		ffs_close(test_file);
	}
}
void hex_trace(const char *str, const void *in, unsigned long int size)
{
	char buffer[4];
	unsigned long int i;
	_trace(str);
	for (i=0; i<size; i++)
	{
		sprintf(buffer, "%02x ", ((volatile unsigned char *)in)[i]);
		_trace(buffer);
	}
	_trace("\n");
}
#endif /* SEC_TRACES_ENABLED */


#ifdef FFS_MODE_ENABLED
/*******************************************
				FFS functions
*******************************************/
void sec_drv_flash_backup(void)
{ }


void sec_drv_remove_backup(void)
{ }


void sec_drv_erase_flash(void)
{ }


void sec_drv_read_flash(UINT32 offset, void *pDest, UINT32 size)
{
	T_FFS_FD file;
	file = ffs_open(FFS_MEPD_FILENAME, FFS_O_RDONLY);
	if (file <= 0)
	{
		memset(pDest, 0, size);
		TRACE("sec_drv_read_flash - FAILed opening file!");
	}
	else
	{
		ffs_seek(file, offset, FFS_SEEK_SET);
		ffs_read(file, pDest, size);
		ffs_close(file);
	}
}


void sec_drv_write_flash(UINT32 offset, const void *pSrc, UINT32 size)
{
	T_FFS_FD file;
	file = ffs_open(FFS_MEPD_FILENAME, FFS_O_WRONLY );
	if (!(file <= 0))
	{
		ffs_seek(file, offset, FFS_SEEK_SET);
		ffs_write(file, (void *)pSrc, size);
		ffs_close(file);
	}
	else
	{
		TRACE("sec_drv_write_flash - FAILed opening file!");
	}
}

#else /* FFS_MODE_ENABLED */

/*******************************************
			Memory functions
*******************************************/
UINT32 gBackupOffset;

void sec_drv_flash_backup(void)
{
	if (!sec_drv_fast_write)
	{
		TRACE("sec_drv_flash_backup");
#ifdef USE_RSAHEAP_FOR_FLASH
		sec_drv_read_flash(0, (void *)SEC_DRV_RSA_HEAP, SEC_FLASH_SIZE);
#else /* USE_RSAHEAP_FOR_FLASH */
#error "QUESTION: what if we don't have the space required?"
		T_FFS_FD file = ffs_open(FLASH_BACKUP_NAME, FFS_O_CREATE | FFS_O_WRONLY | FFS_O_TRUNC);
		ffs_write(file, (void *)SEC_BASE, SEC_FLASH_SIZE);
		ffs_close(file);
#endif /* USE_RSAHEAP_FOR_FLASH */
		gBackupOffset = 0;
	}
}


void sec_drv_remove_backup(void)
{
	if (!sec_drv_fast_write)
	{
#ifdef USE_RSAHEAP_FOR_FLASH
		copy_to_flash(0, 0, 0, gBackupOffset, SEC_FLASH_SIZE);
		TRACE("sec_drv_remove_backup");
		memset(SEC_DRV_RSA_HEAP, 0xFF, SEC_FLASH_SIZE);
#else /* USE_RSAHEAP_FOR_FLASH */
#error "QUESTION: what if we don't have the space required?"
		T_FFS_FD file;
		void *buf;
		
		TRACE("sec_drv_remove_backup");
		buf = M_ALLOC(BACKUP_BUF_SIZE);
		file = ffs_open(FLASH_BACKUP_NAME, FFS_O_RDONLY);
		copy_to_flash(file, buf, BACKUP_BUF_SIZE, gBackupOffset, SEC_FLASH_SIZE);
		ffs_close(file);
		M_FREE(buf);
		
		ffs_remove(FLASH_BACKUP_NAME);
#endif /* USE_RSAHEAP_FOR_FLASH */
	}
}


void sec_drv_erase_flash(void)
{
	if (!sec_drv_fast_write)
	{
		TRACE("sec_drv_erase_flash");
		sec_drv_amd_erase();
	}
}


void sec_drv_read_flash(UINT32 offset, void *pDest, UINT32 size)
{
	void *pSrc = (void *)((UINT32)SEC_BASE + offset);
	assert(pSrc != 0L);
	assert(pDest != 0L);
	assert(size > 0L);
	assert(offset >= 0L && (offset+size) <= SEC_FLASH_SIZE);
	
	memcpy(pDest, pSrc, size);
}


static void copy_to_flash(T_FFS_FD file, void *pBuf, UINT32 bufSize, UINT32 offset, UINT32 maxOffset)
{
	UINT32 size = maxOffset-offset;
	TRACE("copy_to_flash");
#ifdef USE_RSAHEAP_FOR_FLASH
	sec_drv_amd_write((void *)((UINT32)SEC_BASE+offset), (const void *)((UINT32)SEC_DRV_RSA_HEAP+offset), size);
#else /* USE_RSAHEAP_FOR_FLASH */
	ffs_seek(file, offset, FFS_SEEK_SET);
	while(offset < maxOffset)
	{
		size = (maxOffset-offset > bufSize)? bufSize : maxOffset-offset;
		ffs_read(file, pBuf, size);
		sec_drv_amd_write((void *)(SEC_BASE + offset), pBuf, size);
		offset += size;
	}
#endif /* USE_RSAHEAP_FOR_FLASH */
}


void sec_drv_write_flash(UINT32 offset, const void *pSrc, UINT32 size)
{
	assert(pSrc != 0L);
	assert(size > 0L);
	assert(offset >= 0L && (offset+size) <= SEC_FLASH_SIZE);
	
	if (sec_drv_fast_write)
	{
		TRACE("sec_drv_write_flash");
		sec_drv_amd_write((void *)((UINT32)SEC_BASE+offset), pSrc, size);
	}
	else
	{
#ifdef USE_RSAHEAP_FOR_FLASH
		copy_to_flash(0, 0, 0, gBackupOffset, offset);
		HEXTRACE("sec_drv_write_flash - offset: ", &offset, sizeof(UINT32));
		sec_drv_amd_write((void *)((UINT32)SEC_BASE+offset), pSrc, size);
		gBackupOffset = offset+size;
#else /* USE_RSAHEAP_FOR_FLASH */
		T_FFS_FD file;
		void *buf;
		TRACE("Using FFS");
		buf = M_ALLOC(BACKUP_BUF_SIZE);
		file = ffs_open(FLASH_BACKUP_NAME, FFS_O_RDONLY);
		copy_to_flash(file, buf, BACKUP_BUF_SIZE, gBackupOffset, offset);
		sec_drv_amd_write((void *)((UINT32)SEC_BASE + offset), pSrc, size);
		gBackupOffset += size;
		ffs_close(file);
		M_FREE(buf);
#endif /* USE_RSAHEAP_FOR_FLASH */
	}
}

#endif /* FFS_MODE_ENABLED */


/*******************************************
			DES de-/encryption
*******************************************/

/**
 * Returns the DES key from the DSP
 *
 * @return The DES key for use by de-encryption
 */
static const UINT8 *GetDESKey(void)
{
#ifdef DSP_DES_KEY_ENABLED
#error "Not yet implemented"
#else /* DSP_DES_KEY_ENABLED */
static const UINT8 desKey[8] = {0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78};
#ifndef NON_ENCRYPTED_MODE
#ifdef CL_IMEI_CALYPSO_PLATFORM
#ifdef FF_PROTECTED_IMEI
    volatile USHORT *reg_p = (USHORT *) CL_IMEI_DIE_ID_REG;
    USHORT *outBuf16 = (USHORT*)&desKey[0];
    INT8 i;
    for (i = 0; i < CL_IMEI_DIE_ID_SIZE; i++)
    {
        /* Die ID is 4 BYTE long, extract it to 8 BYTE. */
        outBuf16[i] = (USHORT)(*(UINT8*)(reg_p)++);
    }
#endif
#endif
#endif
	return desKey;
#endif /* DSP_DES_KEY_ENABLED */
}


/**
 * Read a crypted block of the secure_area.
 *
 * @param offset Offset into the secure area from which to read the block.
 * @param pDest  Pointer to the receiving buffer.
 * @param size   Size of the block to read. Minimum 1.
 */
static void read_crypted_flash(UINT32 offset, void *pDest, UINT32 size)
{
#ifndef NON_ENCRYPTED_MODE
	UINT8 inbuffer[CRYPT_BLOCK_SIZE];
	UINT8 outbuffer[CRYPT_BLOCK_SIZE];
#endif
	UINT32 blocksize;
	assert(offset != 0L);
	assert(pDest != 0L);
	assert(size > 0L);
	assert(offset >= 0L && (offset+size) <= SEC_FLASH_SIZE);
	do
	{
		blocksize = (size>CRYPT_BLOCK_SIZE)? CRYPT_BLOCK_SIZE : size;
#ifdef NON_ENCRYPTED_MODE
		sec_drv_read_flash(offset, pDest, blocksize);
#else
		sec_drv_read_flash(offset, inbuffer, CRYPT_BLOCK_SIZE);
		cl_des((UBYTE *)inbuffer, (UBYTE *)GetDESKey(), (UBYTE *)outbuffer, (UBYTE)CL_DES_DECRYPTION);
		memcpy(pDest, outbuffer, blocksize);
#endif
		offset += blocksize;
		pDest = (void *)((UINT32)pDest + blocksize);
		size -= blocksize;
	} while(size);
}


/**
 * Write a crypted block of the secure_area.
 *
 * @param offset Offset into the secure area from which to write the block.
 * @param pDest  Pointer to the input buffer.
 * @param size   Size of the block to write. Minimum 1.
 */
static void write_crypted_flash(UINT32 offset, const void *pSrc, UINT32 size)
{
	UINT8 inbuffer[CRYPT_BLOCK_SIZE];
	UINT8 outbuffer[CRYPT_BLOCK_SIZE];
	UINT32 blocksize;
	assert(offset != 0L);
	assert(pSrc != 0L);
	assert(size > 0L);
	assert(offset >= 0L && (offset+size) <= SEC_FLASH_SIZE);

	sec_drv_flash_backup();
	sec_drv_erase_flash();
	do
	{
		blocksize = (size>CRYPT_BLOCK_SIZE)? CRYPT_BLOCK_SIZE : size;
		memset(inbuffer, 0, CRYPT_BLOCK_SIZE);
		memcpy(inbuffer, pSrc, blocksize);
#ifdef NON_ENCRYPTED_MODE
		memcpy(outbuffer, inbuffer, CRYPT_BLOCK_SIZE);
#else
		cl_des((UBYTE *)inbuffer, (UBYTE *)GetDESKey(), (UBYTE *)outbuffer, (UBYTE)CL_DES_ENCRYPTION);
#endif
		/* write crypted datablock to flash */
		sec_drv_write_flash(offset, outbuffer, CRYPT_BLOCK_SIZE);
        offset += blocksize;
        pSrc = (void *)((UINT32)pSrc + blocksize);
		size -= blocksize;
	} while(size);
	sec_drv_remove_backup();
}


/*******************************************
			Offset calculations
*******************************************/

static UINT32 offset_global_conf(void)
{
	return 0;
}


static UINT32 offset_fc_key(void)
{
	return	ALIGN_CRYPT_BLOCK(sizeof(T_SEC_DRV_GLOBAL_CONF));
}


static UINT32 offset_configuration(void)
{
	return	ALIGN_CRYPT_BLOCK(sizeof(T_SEC_DRV_GLOBAL_CONF)) +
			ALIGN_CRYPT_BLOCK(sizeof(T_SEC_DRV_KEY));
}


static UINT32 offset_category_key(int rec_num)
{
	return	ALIGN_CRYPT_BLOCK(sizeof(T_SEC_DRV_GLOBAL_CONF)) +
			ALIGN_CRYPT_BLOCK(sizeof(T_SEC_DRV_KEY)) +
			ALIGN_CRYPT_BLOCK(sizeof(T_SEC_DRV_CONFIGURATION)) +
			rec_num * (ALIGN_CRYPT_BLOCK(sizeof(T_SEC_DRV_KEY)) + 
					   ALIGN_CRYPT_BLOCK(sizeof(T_SEC_DRV_CAT_HDR)) +
					   ALIGN_CRYPT_BLOCK(SEC_DRV_CAT_MAX_SIZE));
}


static UINT32 offset_category_header(int rec_num)
{
	return	ALIGN_CRYPT_BLOCK(sizeof(T_SEC_DRV_GLOBAL_CONF)) +
			ALIGN_CRYPT_BLOCK(sizeof(T_SEC_DRV_KEY)) +
			ALIGN_CRYPT_BLOCK(sizeof(T_SEC_DRV_CONFIGURATION)) +
			rec_num * (ALIGN_CRYPT_BLOCK(sizeof(T_SEC_DRV_KEY)) + 
					   ALIGN_CRYPT_BLOCK(sizeof(T_SEC_DRV_CAT_HDR)) +
					   ALIGN_CRYPT_BLOCK(SEC_DRV_CAT_MAX_SIZE)) +
			ALIGN_CRYPT_BLOCK(sizeof(T_SEC_DRV_KEY));
}


static UINT32 offset_category_body(int rec_num)
{
	return	ALIGN_CRYPT_BLOCK(sizeof(T_SEC_DRV_GLOBAL_CONF)) +
			ALIGN_CRYPT_BLOCK(sizeof(T_SEC_DRV_KEY)) +
			ALIGN_CRYPT_BLOCK(sizeof(T_SEC_DRV_CONFIGURATION)) +
			rec_num * (ALIGN_CRYPT_BLOCK(sizeof(T_SEC_DRV_KEY)) + 
					   ALIGN_CRYPT_BLOCK(sizeof(T_SEC_DRV_CAT_HDR)) +
					   ALIGN_CRYPT_BLOCK(SEC_DRV_CAT_MAX_SIZE)) +
			ALIGN_CRYPT_BLOCK(sizeof(T_SEC_DRV_KEY)) +
			ALIGN_CRYPT_BLOCK(sizeof(T_SEC_DRV_CAT_HDR));
}


/*******************************************
			Helper functions
*******************************************/

/**
 * Check whether or not the secure area has been initialized.
 * 
 * @return TRUE if the secure area has been initialized.
 */
static int check_firstboot_pattern_ok(void)
{
	sec_prim_get_global_conf(0);
	if (gGlobalConf.firstboot_pattern != SEC_PATTERN_INITIALIZED)
	{
		TRACE("check_firstboot_pattern_ok - FAIL");
		return FALSE;
	}
	return TRUE;
}


/**
 * Check whether or not the given number of categories is within bounds.
 * 
 * @return TRUE if the given number of categories is within bounds.
 */
static int check_number_categories(int catNum)
{
	BOOL result;
	if (!gMaxCategories)
	{
		T_SEC_DRV_CONFIGURATION  conf;
		if (sec_prim_get_configuration(&conf))
		{
			gMaxCategories = conf.NumCategories;
			HEXTRACE("check_number_categories - max: ", &gMaxCategories, sizeof(int));
		}
	}
	return (catNum >= 0) && (catNum < gMaxCategories);
}


/*******************************************
				PRIMITIVES
*******************************************/

/**
 * Read the global configuration for the secure flash area used to store
 * SIM lock data.
 *
 * @param pGlobalConf Pointer where to store the global configuration.
 * @return TRUE if the global configuration could be read.
 */
BOOL sec_prim_get_global_conf(T_SEC_DRV_GLOBAL_CONF *pGlobalConf)
{
	if (!gGlobalConfRead)
	{
		sec_drv_read_flash(offset_global_conf(), &gGlobalConf, sizeof(T_SEC_DRV_GLOBAL_CONF));
		HEXTRACE("sec_prim_get_global_conf: ", &gGlobalConf, sizeof(T_SEC_DRV_GLOBAL_CONF));
		gGlobalConfRead = TRUE;
	}
	if (pGlobalConf != 0L)
	{
		memcpy(pGlobalConf, &gGlobalConf, sizeof(T_SEC_DRV_GLOBAL_CONF));
		return TRUE;
	}
	return FALSE;
}

/**
 * Store the global configuration for the secure flash area used to store
 * SIM lock data.
 * 
 * @param pGlobalConf Pointer to a global configuration.
 * @return TRUE if the global configuration could be written.
 */
BOOL sec_prim_set_global_conf(const T_SEC_DRV_GLOBAL_CONF *pGlobalConf)
{
	/* Calc total size needed for this configuration */
	if (pGlobalConf == 0L) return FALSE;
	
	HEXTRACE("sec_prim_set_global_conf: ", pGlobalConf, sizeof(T_SEC_DRV_GLOBAL_CONF));
	sec_drv_flash_backup();
	sec_drv_erase_flash();
	sec_drv_write_flash(offset_global_conf(), pGlobalConf, sizeof(T_SEC_DRV_GLOBAL_CONF));
	sec_drv_remove_backup();
	gGlobalConfRead = FALSE;
	return TRUE;
}


/**
 * Read the configuration for the MEPD structure.
 *
 * @param pConf Pointer to buffer where the result will be placed
 * @Return 0 if flash area hasn't been configured.
 */
BOOL sec_prim_get_configuration(T_SEC_DRV_CONFIGURATION *pConf)
{
	if (! check_firstboot_pattern_ok()) return FALSE;
	
	read_crypted_flash(offset_configuration(), pConf, sizeof(T_SEC_DRV_CONFIGURATION));
	HEXTRACE("sec_prim_get_configuration: ", pConf, sizeof(T_SEC_DRV_CONFIGURATION));
	return TRUE;
}


/**
 * Store the configuration for the MEPD structure.
 *
 * @param pConf Pointer to configuration to set
 * @Return TRUE if ok.
 */
BOOL sec_prim_set_configuration(const T_SEC_DRV_CONFIGURATION *pConf)
{
	if (! check_firstboot_pattern_ok()) return FALSE;
	if (pConf == 0L) return FALSE;
	
	HEXTRACE("sec_prim_set_configuration: ", pConf, sizeof(T_SEC_DRV_CONFIGURATION));
	write_crypted_flash(offset_configuration(), pConf, sizeof(T_SEC_DRV_CONFIGURATION));
	gMaxCategories = 0;
	return TRUE;
}


/**
 * Read the key for either the failure counter (category -1) or a 
 * lock-category (category 0..n).
 *
 * @param rec_num Category number to operate on
 * @param pKey Pointer to keybuffer
 * @Return TRUE if ok.
 */
BOOL sec_prim_get_key(int rec_num, T_SEC_DRV_KEY *pKey)
{
	if (pKey!=0L)
	{
		if ((rec_num == -1) && (check_firstboot_pattern_ok()))
		{
			/* failure counter key */
			read_crypted_flash(offset_fc_key(), pKey, sizeof(T_SEC_DRV_KEY));
			HEXTRACE("sec_prim_get_key: ", pKey, sizeof(T_SEC_DRV_KEY));
			return TRUE;
		}
		else if (check_number_categories(rec_num))
		{
			/* category key */
			read_crypted_flash(offset_category_key(rec_num), pKey, sizeof(T_SEC_DRV_KEY));
			HEXTRACE("sec_prim_get_key: ", &rec_num, sizeof(int));
			HEXTRACE("sec_prim_get_key: ", pKey, sizeof(T_SEC_DRV_KEY));
			return TRUE;
		}
	}
	return FALSE;
}


/**
 * Store the key for either the failure counter (category -1) or a 
 * lock-category (category 0..n).
 *
 * @param rec_num Category number to operate on
 * @param pKey Pointer to keybuffer
 * @Return TRUE if ok.
 */
BOOL sec_prim_set_key(int rec_num, const T_SEC_DRV_KEY *pKey)
{
	if (pKey!=0L)
	{
		if ((rec_num == -1) && (check_firstboot_pattern_ok()))
		{
			/* failure counter key */
			HEXTRACE("sec_prim_set_key - FC: ", pKey, sizeof(T_SEC_DRV_KEY));
			write_crypted_flash(offset_fc_key(), pKey, sizeof(T_SEC_DRV_KEY));
			return TRUE;
		}
		else if (check_number_categories(rec_num))
		{
			/* category key */
			HEXTRACE("sec_prim_set_key: ", &rec_num, sizeof(int));
			HEXTRACE("sec_prim_set_key: ", pKey, sizeof(T_SEC_DRV_KEY));
			write_crypted_flash(offset_category_key(rec_num), pKey, sizeof(T_SEC_DRV_KEY));
			return TRUE;
		}
	}
	return FALSE;
}


/**
 * Read a lock-category header.
 *
 * @param rec_num Category number to operate on
 * @param pHdr Where to place the read category header?
 * @Return TRUE if ok.
 */
BOOL sec_prim_get_cat_header(int rec_num, T_SEC_DRV_CAT_HDR *pHdr)
{
	if (!check_number_categories(rec_num)) return FALSE;
	if (pHdr == 0L) return FALSE;
	
	read_crypted_flash(offset_category_header(rec_num), pHdr, sizeof(T_SEC_DRV_CAT_HDR));
	HEXTRACE("sec_prim_get_cat_header: ", &rec_num, sizeof(int));
	HEXTRACE("sec_prim_get_cat_header: ", pHdr, sizeof(T_SEC_DRV_CAT_HDR));
	return TRUE;
}


/**
 * Store a lock-category header.
 *
 * @param rec_num Category number to operate on
 * @param pHdr Pointer to the category header
 * @Return TRUE if ok.
 */
BOOL sec_prim_set_cat_header(int rec_num, const T_SEC_DRV_CAT_HDR *pHdr)
{
	if (!check_number_categories(rec_num)) return FALSE;
	if (pHdr == 0L) return FALSE;
	
	HEXTRACE("sec_prim_set_cat_header: ", &rec_num, sizeof(int));
	HEXTRACE("sec_prim_set_cat_header: ", pHdr, sizeof(T_SEC_DRV_CAT_HDR));
	write_crypted_flash(offset_category_header(rec_num), pHdr, sizeof(T_SEC_DRV_CAT_HDR));
	return TRUE;
}


/**
 * Read a lock-category body (client data).
 *
 * @param rec_num Category number to operate on
 * @param pBody Pointer to buffer where the result will be placed
 * @param size Size of the buffer
 * @Return TRUE if ok.
 */
BOOL sec_prim_get_cat_body(int rec_num, void *pBody, UINT16 size)
{
	if (!check_number_categories(rec_num)) return FALSE;
	if (pBody == 0L) return FALSE;
	if (size==0 || size>SEC_DRV_CAT_MAX_SIZE) return FALSE;
	
	read_crypted_flash(offset_category_body(rec_num), pBody, size);
	HEXTRACE("sec_prim_get_cat_body: ", &rec_num, sizeof(int));
	HEXTRACE("sec_prim_get_cat_body: ", pBody, size);
	return TRUE;
}


/**
 * Store a lock-category body (client data).
 *
 * @param rec_num Category number to operate on
 * @param pBody Pointer to data-buffer
 * @param size Size of the buffer
 * @Return TRUE if ok.
 */
BOOL sec_prim_set_cat_body(int rec_num, const void *pBody, UINT16 size)
{
	T_SEC_DRV_CAT_HDR hdr;
	
	if (!check_number_categories(rec_num)) return FALSE;
	if (pBody == 0L) return FALSE;
	
	sec_prim_get_cat_header(rec_num, &hdr);
	if (hdr.DataLen != size)
	{
		hdr.DataLen = size;
		sec_prim_set_cat_header(rec_num, &hdr);
	}
	if (size)
	{
		HEXTRACE("sec_prim_set_cat_body: ", &rec_num, sizeof(int));
		HEXTRACE("sec_prim_set_cat_body: ", pBody, size);
		write_crypted_flash(offset_category_body(rec_num), pBody, size);
	}
	return TRUE;
}