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 prototypesvss_keystore.c: Function implementations
Design principles:
- All functions are read-only (no Flash writes)
- All inputs are validated - invalid arguments return
NULLor 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
| Macro | Value | Description |
|---|---|---|
VSS_PAGE0_BASE_ADDR | 0x0A004000 | VSS Page 0 base address |
VSS_PAGE1_BASE_ADDR | 0x0A005000 | VSS Page 1 base address |
VSS_PAGE_SIZE | 4096 | Size of one VSS page (bytes) |
VSS_FLASH_KEY_SIZE | 64 | Flash key field size (bytes) |
VSS_PAGE_HEADER_SIZE | 96 | Page header size (bytes) |
VSS_FLASH_KEY_OFFSET | 0x000 | Flash key offset from page start |
VSS_PAGE_HEADER_OFFSET | 0x040 | Page header offset from page start |
VSS_SHA256_HASH_SIZE | 32 | SHA-256 digest size (bytes) |
VSS_MAX_VARIABLE_SLOTS | 255 | Maximum slots per segment |
VSS_SLOT_META_ENTRY_SIZE | 16 | One slot metadata entry (bytes) |
VSS_AES256_KEY_LEN | 32 | AES-256 key length (bytes) |
Enumerations
| Value | Name | Description |
|---|---|---|
0xFF | VSS_LIFECYCLE_ERASED | Flash erased / Open mode |
0xAA | VSS_LIFECYCLE_OPEN | Open (any firmware accepted) |
0x01 | VSS_LIFECYCLE_SECURE | Secure (signed firmware only) |
| Value | Name | Description |
|---|---|---|
0xAA | VSS_SECBOOT_DISABLED | Secure boot disabled |
0x02 | VSS_SECBOOT_SEQUENTIAL | Sequential boot verification |
0xFF | VSS_SECBOOT_ERASED | Erased state |
| Value | Name | Description |
|---|---|---|
| 0 | VSS_SLOT_RAW | Raw data / PUF key codes |
| 1 | VSS_SLOT_SYMMETRICAL_KEY | Symmetric key (AES, ChaCha, DES) |
| 2 | VSS_SLOT_ASYMMETRICAL_KEY | Asymmetric key (ECC, RSA, DH) |
| 3 | VSS_SLOT_HASH_VALUE | Hash value (SHA, HMAC) |
| 4 | VSS_SLOT_HASH_IV | Hash IV / ongoing context |
| 5 | VSS_SLOT_VER_INFO | Version rollback protection |
| Value | Name | Description |
|---|---|---|
| 0 | VSS_STORAGE_VM | Volatile memory only |
| 1 | VSS_STORAGE_NVM_UNENCRYPTED | NVM unencrypted |
| 2 | VSS_STORAGE_NVM_FLASHKEY | NVM encrypted with Flash Key |
| 3 | VSS_STORAGE_NVM_TAMPERKEY | NVM encrypted with Tamper Key |
| Value | Name | Description |
|---|---|---|
| 0 | VSS_APL_NONE | No special permissions |
| 1 | VSS_APL_LEVEL1 | Create/modify NV slots |
| 2 | VSS_APL_LEVEL2 | Authenticate debug access |
| 3 | VSS_APL_LEVEL3 | Full admin access |
| Value | Name | Key Size |
|---|---|---|
| 0 | VSS_SK_AES_128 | 16 bytes |
| 1 | VSS_SK_AES_192 | 24 bytes |
| 2 | VSS_SK_AES_256 | 32 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.
| Parameter | Type | Description |
|---|---|---|
pageIndex | uint8_t | 0 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.
| Parameter | Type | Description |
|---|---|---|
pageIndex | uint8_t | 0 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.
| Parameter | Type | Description |
|---|---|---|
pageIndex | uint8_t | 0 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.
| Parameter | Type | Description |
|---|---|---|
pageIndex | uint8_t | 0 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.
| Parameter | Type | Description |
|---|---|---|
pageIndex | uint8_t | 0 or 1 |
slotIndex | uint8_t | Iteration
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.
| Parameter | Type | Description |
|---|---|---|
pageIndex | uint8_t | 0 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.
| Parameter | Type | Description |
|---|---|---|
pageIndex | uint8_t | 0 or 1 |
slotIndex | uint8_t | Iteration
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.
| Parameter | Type | Description |
|---|---|---|
pageIndex | uint8_t | 0 or 1 |
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
- 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. - 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.
- 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. - 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
flashKeyEncSegandtamperKeyEncSegdescriptors in the page header are present in the structure for compatibility but are unused (offset = 0, size = 0).
