14.2 VSS Keystore Module

API reference for the vss_keystore module providing read-only access to the PIC32CMSG00 VSS secure Flash pages in the TrustZone Secure application.

Overview

The vss_keystore module provides read-only access to the PIC32CMSG00 VSS secure Flash pages. It runs in the TrustZone Secure application and implements the memory layout defined by the Boot ROM specification.

Files:

  • vss_keystore.h: Data structures, enumerations, constants, and function prototypes
  • vss_keystore.c: Function implementations

Design principles:

  • All functions are read-only (no Flash writes)
  • All inputs are validated - invalid arguments return NULL or safe defaults
  • MISRA C:2012 compliant (documented deviations for hardware casts)
  • No key material crosses the TrustZone boundary

VSS Page Layout Diagram

Offset    Content                          Size
------------------------------------------------------
0x040     Page Header                      96 B
          +- pageSeqNumber (2B)
          +- lifecycleState (1B)
          +- secBootConfig (1B)
          +- Segment Descriptors (16B)
          +- fwVersion (4B)
          +- reserved (32B)
          +- headerHash (32B, SHA-256)
0x0A0     Unencrypted Segment              Variable
          +- SlotMetaSegHeader (4B)
          +- SlotMetaData[N] (N*16B)
          +- [padding to 32B boundary]
          +- SlotSegHeader (2B)
          +- Offset table (N*2B + padding)
          +- Slot data (concatenated)
          +- [0xFF padding to segment end]
          +- SHA-256 hash (32B)

------------------------------------------------------
          Total page size: 4096 bytes

Constants

MacroValueDescription
VSS_PAGE0_BASE_ADDR0x0A004000VSS Page 0 base address
VSS_PAGE1_BASE_ADDR0x0A005000VSS Page 1 base address
VSS_PAGE_SIZE4096Size of one VSS page (bytes)
VSS_FLASH_KEY_SIZE64Flash key field size (bytes)
VSS_PAGE_HEADER_SIZE96Page header size (bytes)
VSS_FLASH_KEY_OFFSET0x000Flash key offset from page start
VSS_PAGE_HEADER_OFFSET0x040Page header offset from page start
VSS_SHA256_HASH_SIZE32SHA-256 digest size (bytes)
VSS_MAX_VARIABLE_SLOTS255Maximum slots per segment
VSS_SLOT_META_ENTRY_SIZE16One slot metadata entry (bytes)
VSS_AES256_KEY_LEN32AES-256 key length (bytes)

Enumerations

Table 14-1. VSS_LifecycleState_t
ValueNameDescription
0xFFVSS_LIFECYCLE_ERASEDFlash erased / Open mode
0xAAVSS_LIFECYCLE_OPENOpen (any firmware accepted)
0x01VSS_LIFECYCLE_SECURESecure (signed firmware only)
Table 14-2. VSS_SecBootConfig_t
ValueNameDescription
0xAAVSS_SECBOOT_DISABLEDSecure boot disabled
0x02VSS_SECBOOT_SEQUENTIALSequential boot verification
0xFFVSS_SECBOOT_ERASEDErased state
Table 14-3. VSS_SlotType_t
ValueNameDescription
0VSS_SLOT_RAWRaw data / PUF key codes
1VSS_SLOT_SYMMETRICAL_KEYSymmetric key (AES, ChaCha, DES)
2VSS_SLOT_ASYMMETRICAL_KEYAsymmetric key (ECC, RSA, DH)
3VSS_SLOT_HASH_VALUEHash value (SHA, HMAC)
4VSS_SLOT_HASH_IVHash IV / ongoing context
5VSS_SLOT_VER_INFOVersion rollback protection
Table 14-4. VSS_StorageType_t
ValueNameDescription
0VSS_STORAGE_VMVolatile memory only
1VSS_STORAGE_NVM_UNENCRYPTEDNVM unencrypted
2VSS_STORAGE_NVM_FLASHKEYNVM encrypted with Flash Key
3VSS_STORAGE_NVM_TAMPERKEYNVM encrypted with Tamper Key
Table 14-5. VSS_AccessLevel_t
ValueNameDescription
0VSS_APL_NONENo special permissions
1VSS_APL_LEVEL1Create/modify NV slots
2VSS_APL_LEVEL2Authenticate debug access
3VSS_APL_LEVEL3Full admin access
Table 14-6. VSS_AesKeySize_t
ValueNameKey Size
0VSS_SK_AES_12816 bytes
1VSS_SK_AES_19224 bytes
2VSS_SK_AES_25632 bytes

Data Structures

VSS_Page_t

Complete VSS page overlay (first 160 fixed bytes).

typedef struct {
    VSS_NvmWrappedFlashKey_t  flashKey;      /* offset 0x000 (64 bytes) */
    VSS_NvmPageHeader_t       pageHeader;    /* offset 0x040 (96 bytes) */
} VSS_Page_t;

VSS_NvmPageHeader_t

96-byte page header at offset 0x040.

typedef struct {
    uint16_t                pageSeqNumber;
    uint8_t                 lifecycleState;
    uint8_t                 secBootConfig;
    VSS_SegmentDescriptor_t unencryptedSeg;
    VSS_SegmentDescriptor_t flashKeyEncSeg;
    VSS_SegmentDescriptor_t tamperKeyEncSeg;
    VSS_SegmentDescriptor_t tamperLogSeg;
    uint32_t                reservedA;
    uint32_t                fwRollbackMeta;
    uint32_t                fwVersion;
    uint8_t                 reservedB[32];
    uint8_t                 headerHash[32];
} VSS_NvmPageHeader_t;  /* 96 bytes at offset 0x040 */

VSS_SegmentDescriptor_t

4-byte segment locator within the page.

typedef struct {
    uint16_t offset;    /* Byte offset from page start */
    uint16_t size;      /* Segment size in bytes */
} VSS_SegmentDescriptor_t;  /* 4 bytes */

VSS_SlotMetaSegHeader_t

Header of the Variable Slot Meta Data Segment (at start of unencrypted segment).

typedef struct {
    uint16_t segmentSize;   /* Total size of this segment (including this field) */
    uint8_t  numSlots;      /* Number of slot metadata entries */
    uint8_t  reservedVSM;   /* Alignment padding */
} VSS_SlotMetaSegHeader_t;  /* 4 bytes */

VSS_SlotMetaData_t

16-byte metadata entry for one variable slot.

typedef struct {
    VSS_SlotHeader_t header;              /* Slot header (4 bytes) */
    uint32_t         validAfter;          /* Valid-after UNIX timestamp */
    uint32_t         validBefore;         /* Valid-before UNIX timestamp */
    uint32_t         dataSpecificMetaData; /* Type-specific metadata */
} VSS_SlotMetaData_t;  /* 16 bytes */

VSS_SlotHeader_t

4-byte packed slot header.

typedef union {
    struct {
        uint8_t variableSlotIndex;   /* Slot index (e.g. 2, 3, 0x82) */
        uint8_t storageIndex;        /* Position in offset table */
        VSS_StorageData_t storageData; /* Storage attributes (bitfield) */
        uint8_t vsType : 3;          /* VSS_SlotType_t value */
    };
    uint32_t v;
} VSS_SlotHeader_t;  /* 4 bytes */

VSS_EccWcPrimeKeyMeta_t

Interpretation of dataSpecificMetaData for ECC Weierstrass Prime keys.

typedef union {
    struct {
        uint32_t size        : 8;   /* Key size in bytes (32=P-256, 48=P-384) */
        uint32_t usage       : 1;   /* 0=key exchange, 1=signature */
        uint32_t algoUsed    : 1;   /* ECDSA algorithm flag */
        uint32_t paramA      : 2;   /* Curve parameter A type */
        uint32_t domainInc   : 1;   /* 1=domain parameters included */
        uint32_t publicKeyInc: 1;   /* 1=public key included */
        uint32_t privateKeyInc:1;   /* 1=private key included */
    };
    uint32_t v;
} VSS_EccWcPrimeKeyMeta_t;  /* 4 bytes (bitfield) */

Public Functions

VSS_GetPage

const VSS_Page_t * VSS_GetPage ( uint8_t pageIndex );

Returns a memory-mapped pointer to the full VSS page.

Table 14-7. Parameters
ParameterTypeDescription
pageIndexuint8_t0 or 1

Returns: const VSS_Page_t * or NULL if invalid.

Example:

const VSS_Page_t *pPage = VSS_GetPage(0);
if (pPage != NULL) {
    uint8_t lcs = pPage->pageHeader.lifecycleState;
    printf("Lifecycle: 0x%02X\n", lcs);
}

VSS_GetPageHeader

const VSS_NvmPageHeader_t * VSS_GetPageHeader ( uint8_t pageIndex );

Returns a pointer to the 96-byte page header.

Table 14-8. Parameters
ParameterTypeDescription
pageIndexuint8_t0 or 1

Returns: const VSS_NvmPageHeader_t * or NULL.

Example:

const VSS_NvmPageHeader_t *pHdr = VSS_GetPageHeader(0);
if (pHdr != NULL) {
    printf("Seq: %u, FW Ver: 0x%08X\n",
           pHdr->pageSeqNumber, pHdr->fwVersion);
}

VSS_GetActivePageIndex

uint8_t VSS_GetActivePageIndex ( void );

Determines which page (0 or 1) is the most recent by comparing sequence numbers with 16-bit wrap-around handling.

Returns: 0 or 1.

Example:

uint8_t active = VSS_GetActivePageIndex();
const VSS_NvmPageHeader_t *pHdr = VSS_GetPageHeader(active);

VSS_GetLifecycleState

uint8_t VSS_GetLifecycleState ( uint8_t pageIndex );

Returns the lifecycle state byte from the page header.

Table 14-9. Parameters
ParameterTypeDescription
pageIndexuint8_t0 or 1

Returns: VSS_LifecycleState_t value. Returns 0xFF on error.

Example:

uint8_t lcs = VSS_GetLifecycleState(VSS_GetActivePageIndex());
if (lcs == (uint8_t)VSS_LIFECYCLE_SECURE) {
    printf("Device is in SECURE lifecycle\n");
}

VSS_GetUnencryptedMetaSegment

const VSS_SlotMetaSegHeader_t * VSS_GetUnencryptedMetaSegment ( uint8_t pageIndex );

Returns a pointer to the Variable Slot Meta Data Segment header within the unencrypted segment. Uses the unencryptedSeg descriptor from the page header.

Table 14-10. Parameters
ParameterTypeDescription
pageIndexuint8_t0 or 1

Returns: const VSS_SlotMetaSegHeader_t * or NULL.

Example:

uint8_t active = VSS_GetActivePageIndex();
const VSS_SlotMetaSegHeader_t *pMeta = VSS_GetUnencryptedMetaSegment(active);
if (pMeta != NULL) {
    printf("Segment size: %u, Num slots: %u\n",
           pMeta->segmentSize, pMeta->numSlots);
}

VSS_GetSlotMetaData

const VSS_SlotMetaData_t * VSS_GetSlotMetaData (
    uint8_t pageIndex,
    uint8_t slotIndex );

Returns a pointer to a specific 16-byte slot metadata entry.

Table 14-11. Parameters
ParameterTypeDescription
pageIndexuint8_t0 or 1
slotIndexuint8_tIteration index (0-based, NOT the variableSlotIndex)

Returns: const VSS_SlotMetaData_t * or NULL.

Example:

uint8_t active = VSS_GetActivePageIndex();
uint8_t count = VSS_GetSlotCount(active);

for (uint8_t i = 0; i < count; i++) {
    const VSS_SlotMetaData_t *pSlot = VSS_GetSlotMetaData(active, i);
    if (pSlot != NULL) {
        printf("Slot[%u]: vsIndex=%u, type=%u\n",
               i, pSlot->header.variableSlotIndex,
               pSlot->header.slotType);
    }
}

VSS_GetSlotCount

uint8_t VSS_GetSlotCount ( uint8_t pageIndex );

Returns the number of variable slot entries in the unencrypted segment.

Table 14-12. Parameters
ParameterTypeDescription
pageIndexuint8_t0 or 1

Returns: Count (0..255), or 0 on error.

Example:

uint8_t n = VSS_GetSlotCount(VSS_GetActivePageIndex());
printf("Total slots in VSS: %u\n", n);

VSS_GetSlotType

VSS_SlotType_t VSS_GetSlotType ( uint8_t pageIndex, uint8_t slotIndex );

Returns the type of the specified slot.

Table 14-13. Parameters
ParameterTypeDescription
pageIndexuint8_t0 or 1
slotIndexuint8_tIteration index (0-based, NOT the variableSlotIndex)

Returns: VSS_SlotType_t value. Returns VSS_SLOT_RAW (0) on error.

Example:

VSS_SlotType_t st = VSS_GetSlotType(active, 0);
if (st == VSS_SLOT_ASYMMETRICAL_KEY) {
    /* Parse ECC metadata from dataSpecificMetaData */
}

VSS_GetFlashKey

const uint8_t * VSS_GetFlashKey ( uint8_t pageIndex );

Returns a pointer to the 32-byte AES-256 Flash key.

Table 14-14. Parameters
ParameterTypeDescription
pageIndexuint8_t0 or 1
Note: On devices with PUF hardware (PIC32CMSG00), this field is present but ignored. It is relevant for PIC32CZCA90/PIC32CKSG devices.

Returns: const uint8_t * (32 bytes) or NULL.

Usage Examples

Example 1: Read the Active VSS Page Status

#include "vss_keystore.h"
#include <stdio.h>

void print_vss_status(void)
{
    uint8_t active = VSS_GetActivePageIndex();
    const VSS_NvmPageHeader_t *pHdr = VSS_GetPageHeader(active);

    if (pHdr == NULL) {
        printf("ERROR: Cannot read VSS page %u\n", active);
        return;
    }

    printf("Active VSS page: %u\n", active);
    printf("Sequence number: 0x%04X\n", pHdr->pageSeqNumber);
    printf("Lifecycle state: 0x%02X\n", pHdr->lifecycleState);
    printf("Secure boot cfg: 0x%02X\n", pHdr->secBootConfig);
    printf("FW version:      0x%08X\n", pHdr->fwVersion);
    printf("Num slots:       %u\n", VSS_GetSlotCount(active));
}

Example 2: Iterate All Slots and Print Types

void enumerate_vss_slots(void)
{
    uint8_t active = VSS_GetActivePageIndex();
    uint8_t count = VSS_GetSlotCount(active);

    static const char *typeNames[] = {
        "RAW", "SYMMETRIC", "ASYMMETRIC", "HASH", "HASH_IV", "VER_INFO"
    };

    for (uint8_t i = 0; i < count; i++) {
        const VSS_SlotMetaData_t *pMeta = VSS_GetSlotMetaData(active, i);
        if (pMeta == NULL) continue;

        uint8_t vsIdx = pMeta->header.variableSlotIndex;
        uint8_t st = pMeta->header.slotType;

        printf("  Slot 0x%02X: %s\n", vsIdx,
               (st < 6) ? typeNames[st] : "UNKNOWN");
    }
}

Example 3: Extract ECC Public Key Coordinates

#include "vss_keystore.h"
#include <string.h>

/* Extract ECC public key coordinates (X, Y) from a VSS slot.
 * pXout and pYout must point to buffers of at least 48 bytes
 * (maximum for P-384). Actual size returned via *pKeySize. */
bool get_ecc_public_key(uint8_t pageIndex, uint8_t iterIdx,
                        uint8_t *pXout, uint8_t *pYout, uint16_t *pKeySize)
{
    if ((pXout == NULL) || (pYout == NULL) || (pKeySize == NULL))
    {
        return false;
    }

    const VSS_SlotMetaData_t *pMeta = VSS_GetSlotMetaData(pageIndex, iterIdx);
    if (pMeta == NULL)
    {
        return false;
    }
    
    if (pMeta->header.slotType != (uint8_t)VSS_SLOT_ASYMMETRICAL_KEY)
    {
        return false;
    }

    /* Parse key size and flags from dataSpecificMetaData */
    const VSS_EccWcPrimeKeyMeta_t *pEcc =
        (const VSS_EccWcPrimeKeyMeta_t *)&pMeta->dataSpecificMetaData;

    uint16_t keySize = (uint16_t)pEcc->size;  /* 32 for P-256, 48 for P-384 */
    *pKeySize = keySize;

    /* Check that public key is included in this slot */
    if (pEcc->publicKeyInc == 0U)
    {
        return false;  /* No public key in this slot */
    }

    /* Read the full slot data into a local buffer */
    static uint8_t slotBuf[256];
    uint16_t dataLen = VSS_ReadSlotData(iterIdx, slotBuf, (uint16_t)sizeof(slotBuf));
    if (dataLen == 0U)
    {
        return false;
    }

    /* Slot data layout for ECC Weierstrass Prime:
     *   [domain params if domainInc=1] (variable, typically not included)
     *   [public key X || Y]            (keySize * 2 bytes, if publicKeyInc=1)
     *   [private key]                  (keySize bytes, if privateKeyInc=1)
     */
    uint16_t offset = 0U;

    /* Skip domain parameters if present (rare) */
    if (pEcc->domainInc != 0U)
    {
        /* Domain params: p, a, b, Gx, Gy, n, h = 7 * keySize bytes */
        offset += (uint16_t)(7U * keySize);
    }

    /* Verify we have enough data for the public key */
    if ((offset + (2U * keySize)) > dataLen)
    {
        return false;
    }

    /* Copy X coordinate */
    memcpy(pXout, &slotBuf[offset], keySize);
    offset += keySize;

    /* Copy Y coordinate */
    memcpy(pYout, &slotBuf[offset], keySize);

    return true;
}

/* Usage example */
void print_slot2_public_key(void)
{
    uint8_t x[48], y[48];
    uint16_t keySize = 0;
    uint8_t active = VSS_GetActivePageIndex();

    if (get_ecc_public_key(active, 0, x, y, &keySize))
    {
        printf("Public Key (P-%u):\r\n", (unsigned)(keySize * 8U));
        printf("  X: ");
        for (uint16_t i = 0; i < keySize; i++) printf("%02X ", x[i]);
        printf("\r\n  Y: ");
        for (uint16_t i = 0; i < keySize; i++) printf("%02X ", y[i]);
        printf("\r\n");
    }
}

Example 4: Detect PUF Key Code and Compute Expected Size

#include "vss_keystore.h"
#include "crypto/puf04891_pic32cmsg/drv_puf.h"

/* Check if a slot contains a PUF key code and return its actual size */
uint16_t get_keycode_actual_size(const uint8_t *pData, uint16_t rawLen)
{
    /* PUF key code magic: 0x37 0x10 */
    if (rawLen < 4) return rawLen;
    if (pData[0] != 0x37 || pData[1] != 0x10) return rawLen;

    /* keyLenBits at bytes 2-3 (little-endian) */
    uint16_t keyLenBits = (uint16_t)pData[2] | ((uint16_t)pData[3] << 8);
    if (keyLenBits == 0) return rawLen;

    uint16_t kcSize = (uint16_t)DRV_PUF_KEY_CODE_SIZE(keyLenBits);
    return (kcSize <= rawLen) ? kcSize : rawLen;
}

Notes and Limitations

  1. Read-only module: writing to VSS must be done via the provisioning tool (hsmsfmdgen.exe) at programming time. The firmware never modifies VSS Flash at runtime.
  2. Active page selection: the Boot ROM always uses the page with the higher sequence number. After a provisioning update, the unused page becomes the backup.
  3. Last slot padding: the last slot in the offset table may report a larger size than its actual content (includes 0xFF trailing padding up to the hash boundary). Use VSS_TrimTrailingFF() or parse the slot's internal structure to get the true data length.
  4. Encrypted segments: the PIC32CMSG00 (HSM-Lite) only supports unencrypted segments. Flash-key and tamper-key encrypted segments are not available on this device family (they require a full HSM, as found on PIC32CZCA90 or PIC32CKSG). The flashKeyEncSeg and tamperKeyEncSeg descriptors in the page header are present in the structure for compatibility but are unused (offset = 0, size = 0).