openaliro
Aliro reader: UWB/CCC core and ESP32-S3/C5/C6 port
Loading...
Searching...
No Matches
ccc_kdf.c File Reference

— UWB key schedule + SP0 Pre-POLL frame codec. More...

#include "ccc_kdf.h"
#include <errno.h>
#include <string.h>
Include dependency graph for ccc_kdf.c:

Macros

#define AES_BLOCK_LEN   16u
 AES block size, bytes.
 
#define CMAC_RB   0x87u
 CMAC/CCM* subkey constant Rb for the 128-bit block.
 
#define KDF_INPUT_MAX   48u
 Largest KDF fixed-input we assemble: ctr(4)+"URSK_KT"(7)+0x00+ctx(32)+L(4).
 
#define L_UPSK   0x00000180u
 [L]_32 = 0x180 (384): the 3-block mUPSK material (mUPSK1 || mUPSK2).
 
#define L_256BIT   0x00000100u
 [L]_32 = 0x100 (256): the 2-block mURSK / URSK_KT material.
 
#define L_128BIT   0x00000080u
 [L]_32 = 0x80 (128): the 1-block Salt / dURSK / dUDSK / UAD material.
 
#define SP0_SEC_LEVEL   0x06u
 SP0 nonce Security Level field (6 = ENC-MIC-64).
 
#define CCM_L   2u
 CCM length-field size L (2 → 16-bit frame lengths).
 
#define CCM_SCRATCH_MAX   128u
 Largest AAD/payload we buffer for the SP0 CBC-MAC.
 

Functions

static void put_be32 (uint8_t *out, uint32_t v)
 Store v big-endian into out (4 bytes).
 
static uint32_t get_be32 (const uint8_t *in)
 Load a big-endian uint32 from in (4 bytes).
 
static void xor_bytes (uint8_t *out, const uint8_t *a, const uint8_t *b, size_t n)
 XOR n bytes: out = a ^ b (out may alias a).
 
static uint8_t block_lshift1 (const uint8_t in[AES_BLOCK_LEN], uint8_t out[AES_BLOCK_LEN])
 One-bit left shift of a 128-bit big-endian block.
 
static void cmac_subkey (const uint8_t l[AES_BLOCK_LEN], uint8_t out[AES_BLOCK_LEN])
 CMAC subkey derivation: K = (L << 1), XOR Rb if the top bit was set.
 
int ccc_aes_cmac (const uint8_t *key, size_t key_bits, const uint8_t *msg, size_t msg_len, uint8_t tag[CCC_CMAC_TAG_LEN])
 AES-CMAC authentication tag over message.
 
static int kdf108_block (const uint8_t *kdk, size_t kdk_bits, uint32_t counter, const uint8_t *label, size_t label_len, const uint8_t *context, size_t ctx_len, uint32_t l_bits, uint8_t out[AES_BLOCK_LEN])
 One counter-mode CMAC block: CMAC(kdk, counter || label || 0x00 || context || l_bits).
 
int ccc_derive_mupsk1 (const uint8_t ursk[CCC_URSK_LEN], uint8_t out[CCC_MUPSK1_LEN])
 Derive mUPSK1, the SP0 Pre-POLL AES-CCM* key.
 
int ccc_derive_mupsk2 (const uint8_t ursk[CCC_URSK_LEN], uint8_t out[CCC_MUPSK2_LEN])
 Derive mUPSK2, the seed for the UWB-address KDF.
 
int ccc_derive_mursk (const uint8_t ursk[CCC_URSK_LEN], uint8_t out[CCC_MURSK_LEN])
 Derive mURSK, the ranging-key seed feeding URSK_KT.
 
int ccc_derive_salted_hash (const uint8_t ursk[CCC_URSK_LEN], const uint8_t *ranging_config, size_t rc_len, uint8_t out[CCC_SALTED_HASH_LEN])
 Derive SaltedHash from the serialized ranging configuration.
 
int ccc_derive_ursk_kt (const uint8_t mursk[CCC_MURSK_LEN], uint32_t sts_index, uint8_t out[CCC_URSK_KT_LEN])
 Derive URSK_KT, generated once per ranging cycle and keyed by the STS index.
 
static int derive_dkey (const uint8_t ursk_kt[CCC_URSK_KT_LEN], const uint8_t label[4], const uint8_t salted_hash[CCC_SALTED_HASH_LEN], uint8_t out[CCC_DURSK_LEN])
 Shared dURSK/dUDSK derivation body: CMAC(URSK_KT, ctr1 || label || 0x00 || SaltedHash || 0x000000 || 0x80).
 
int ccc_derive_dursk (const uint8_t ursk_kt[CCC_URSK_KT_LEN], const uint8_t salted_hash[CCC_SALTED_HASH_LEN], uint8_t out[CCC_DURSK_LEN])
 Derive dURSK, per-cycle STS key material.
 
int ccc_derive_dudsk (const uint8_t ursk_kt[CCC_URSK_KT_LEN], const uint8_t salted_hash[CCC_SALTED_HASH_LEN], uint8_t out[CCC_DUDSK_LEN])
 Derive dUDSK, per-cycle SP0 timestamp-frame key.
 
int ccc_derive_sts_v (const uint8_t salted_hash[CCC_SALTED_HASH_LEN], uint32_t sts_index, uint8_t out[CCC_STS_V_LEN])
 Derive STS-V (phyHrpUwbStsV), the per-PPDU STS IV for the DW3000.
 
int ccc_derive_uad (const uint8_t mupsk2[CCC_MUPSK2_LEN], uint32_t sts_index0, uint8_t out[CCC_UAD_LEN])
 Derive UAD, the raw UWB-address derivation output.
 
static void remap_if_reserved (uint8_t *addr, size_t len)
 If addr is a reserved all-ones value (0xFFFF/0xFFFE for 2 bytes, 0xFF..FF for other lengths), clear its top bit.
 
int ccc_uad_addresses (const uint8_t uad[CCC_UAD_LEN], uint8_t keysource[CCC_KEYSOURCE_LEN], uint8_t dest_short_addr[CCC_DEST_SHORT_ADDR_LEN], uint8_t src_long_addr[CCC_SRC_LONG_ADDR_LEN])
 Split UAD into the UWB addresses (KeySource, destination short address, source long address).
 
static void sp0_nonce (uint8_t nonce[CCC_SP0_NONCE_LEN], const uint8_t src_long_addr[CCC_SRC_LONG_ADDR_LEN], uint32_t frame_counter)
 Build the SP0 CCM* nonce: SrcLongAddr || FrameCounter(BE) || SecLevel.
 
static int sp0_cbc_mac (const uint8_t key[CCC_MUPSK1_LEN], const uint8_t nonce[CCC_SP0_NONCE_LEN], const uint8_t *mhr, size_t mhr_len, const uint8_t *payload, size_t payload_len, uint8_t tag[AES_BLOCK_LEN])
 Compute the CCM* CBC-MAC over B0 || l(a)||MHR || payload, zero-padded per block.
 
static int sp0_ctr (const uint8_t key[CCC_MUPSK1_LEN], const uint8_t nonce[CCC_SP0_NONCE_LEN], const uint8_t *in, size_t len, uint8_t *out, uint8_t s0[AES_BLOCK_LEN])
 Apply CCM* CTR mode: emit the S0 keystream block, then XOR keystream S1.
 
static int sp0_ct_diff (const uint8_t *a, const uint8_t *b, size_t n)
 Constant-time inequality: 0 iff the n bytes are equal.
 
int ccc_sp0_encrypt (const uint8_t key[CCC_MUPSK1_LEN], const uint8_t src_long_addr[CCC_SRC_LONG_ADDR_LEN], uint32_t frame_counter, const uint8_t *mhr, size_t mhr_len, const uint8_t *payload, size_t payload_len, uint8_t *ciphertext_out, uint8_t mic_out[CCC_SP0_MIC_LEN])
 Encrypt + authenticate an SP0 data frame (AES-CCM*, ENC-MIC-64).
 
int ccc_sp0_decrypt (const uint8_t key[CCC_MUPSK1_LEN], const uint8_t src_long_addr[CCC_SRC_LONG_ADDR_LEN], uint32_t frame_counter, const uint8_t *mhr, size_t mhr_len, const uint8_t *ciphertext, size_t ciphertext_len, const uint8_t mic[CCC_SP0_MIC_LEN], uint8_t *payload_out)
 Decrypt + verify an SP0 data frame; zeroes plaintext and returns -EBADMSG on MIC failure.
 

Variables

static const uint8_t LBL_UPSK [4] = {0x55, 0x50, 0x53, 0x4B}
 Label "UPSK" (0x5550534B).
 
static const uint8_t LBL_URSK [4] = {0x55, 0x52, 0x53, 0x4B}
 Label "URSK" (0x5552534B).
 
static const uint8_t LBL_UDSK [4] = {0x55, 0x44, 0x53, 0x4B}
 Label "UDSK" (0x5544534B).
 
static const uint8_t LBL_SALT [4] = {0x53, 0x41, 0x4C, 0x54}
 Label "SALT" (0x53414C54).
 
static const uint8_t LBL_URSK_KT [7] = {0x55, 0x52, 0x53, 0x4B, 0x5F, 0x4B, 0x54}
 Label "URSK_KT" (0x5552534B5F4B54).
 
static const uint8_t LBL_UAD [3] = {0x55, 0x41, 0x44}
 Label "UAD" (0x554144).
 
static const uint8_t CTX_ZERO3 [3] = {0x00, 0x00, 0x00}
 3-byte zero context tail carried by mUPSK/Salt/mURSK/dURSK/dUDSK.
 

Detailed Description

— UWB key schedule + SP0 Pre-POLL frame codec.

Function Documentation

◆ block_lshift1()

static uint8_t block_lshift1 ( const uint8_t  in[AES_BLOCK_LEN],
uint8_t  out[AES_BLOCK_LEN] 
)
static

One-bit left shift of a 128-bit big-endian block.

Parameters
inInput 128-bit block.
outOutput buffer receiving the shifted block.
Returns
The bit shifted out of the most significant bit position.

◆ ccc_aes_cmac()

int ccc_aes_cmac ( const uint8_t *  key,
size_t  key_bits,
const uint8_t *  msg,
size_t  msg_len,
uint8_t  tag[CCC_CMAC_TAG_LEN] 
)

AES-CMAC authentication tag over message.

AES-CMAC over msg (msg may be NULL iff msg_len is 0).

◆ ccc_derive_dudsk()

int ccc_derive_dudsk ( const uint8_t  ursk_kt[CCC_URSK_KT_LEN],
const uint8_t  salted_hash[CCC_SALTED_HASH_LEN],
uint8_t  out[CCC_DUDSK_LEN] 
)

Derive dUDSK, per-cycle SP0 timestamp-frame key.

Derive dUDSK — per-cycle SP0 timestamp-frame key.

◆ ccc_derive_dursk()

int ccc_derive_dursk ( const uint8_t  ursk_kt[CCC_URSK_KT_LEN],
const uint8_t  salted_hash[CCC_SALTED_HASH_LEN],
uint8_t  out[CCC_DURSK_LEN] 
)

Derive dURSK, per-cycle STS key material.

Derive dURSK — per-cycle STS key material.

◆ ccc_derive_mupsk1()

int ccc_derive_mupsk1 ( const uint8_t  ursk[CCC_URSK_LEN],
uint8_t  out[CCC_MUPSK1_LEN] 
)

Derive mUPSK1, the SP0 Pre-POLL AES-CCM* key.

Derive mUPSK1 — the SP0 Pre-POLL AES-CCM* key.

◆ ccc_derive_mupsk2()

int ccc_derive_mupsk2 ( const uint8_t  ursk[CCC_URSK_LEN],
uint8_t  out[CCC_MUPSK2_LEN] 
)

Derive mUPSK2, the seed for the UWB-address KDF.

Derive mUPSK2 — the seed for the UWB-address KDF.

◆ ccc_derive_mursk()

int ccc_derive_mursk ( const uint8_t  ursk[CCC_URSK_LEN],
uint8_t  out[CCC_MURSK_LEN] 
)

Derive mURSK, the ranging-key seed feeding URSK_KT.

Derive mURSK — the ranging-key seed feeding URSK_KT.

◆ ccc_derive_sts_v()

int ccc_derive_sts_v ( const uint8_t  salted_hash[CCC_SALTED_HASH_LEN],
uint32_t  sts_index,
uint8_t  out[CCC_STS_V_LEN] 
)

Derive STS-V (phyHrpUwbStsV), the per-PPDU STS IV for the DW3000.

Derive STS-V (phyHrpUwbStsV) — the per-PPDU STS IV for the DW3000 (out may alias salted_hash).

◆ ccc_derive_uad()

int ccc_derive_uad ( const uint8_t  mupsk2[CCC_MUPSK2_LEN],
uint32_t  sts_index0,
uint8_t  out[CCC_UAD_LEN] 
)

Derive UAD, the raw UWB-address derivation output.

Derive UAD — the raw UWB-address derivation output.

◆ ccc_derive_ursk_kt()

int ccc_derive_ursk_kt ( const uint8_t  mursk[CCC_MURSK_LEN],
uint32_t  sts_index,
uint8_t  out[CCC_URSK_KT_LEN] 
)

Derive URSK_KT, generated once per ranging cycle and keyed by the STS index.

Derive URSK_KT — once per ranging cycle, keyed by the STS index.

◆ ccc_uad_addresses()

int ccc_uad_addresses ( const uint8_t  uad[CCC_UAD_LEN],
uint8_t  keysource[CCC_KEYSOURCE_LEN],
uint8_t  dest_short_addr[CCC_DEST_SHORT_ADDR_LEN],
uint8_t  src_long_addr[CCC_SRC_LONG_ADDR_LEN] 
)

Split UAD into the UWB addresses (KeySource, destination short address, source long address).

Split UAD into the UWB addresses (KeySource, dest short, src long).

◆ cmac_subkey()

static void cmac_subkey ( const uint8_t  l[AES_BLOCK_LEN],
uint8_t  out[AES_BLOCK_LEN] 
)
static

CMAC subkey derivation: K = (L << 1), XOR Rb if the top bit was set.

Parameters
lInput 128-bit value (L, or a previously derived subkey).
outOutput buffer receiving the derived subkey.

◆ derive_dkey()

static int derive_dkey ( const uint8_t  ursk_kt[CCC_URSK_KT_LEN],
const uint8_t  label[4],
const uint8_t  salted_hash[CCC_SALTED_HASH_LEN],
uint8_t  out[CCC_DURSK_LEN] 
)
static

Shared dURSK/dUDSK derivation body: CMAC(URSK_KT, ctr1 || label || 0x00 || SaltedHash || 0x000000 || 0x80).

Parameters
ursk_ktURSK_KT input key for the current ranging cycle.
label4-byte KDF label distinguishing dURSK from dUDSK.
salted_hashSaltedHash of the ranging configuration.
outOutput buffer receiving the derived key.
Returns
Result of the underlying KDF block derivation.

◆ get_be32()

static uint32_t get_be32 ( const uint8_t *  in)
static

Load a big-endian uint32 from in (4 bytes).

Parameters
inSource buffer holding 4 big-endian bytes.
Returns
The decoded uint32 value.

◆ kdf108_block()

static int kdf108_block ( const uint8_t *  kdk,
size_t  kdk_bits,
uint32_t  counter,
const uint8_t *  label,
size_t  label_len,
const uint8_t *  context,
size_t  ctx_len,
uint32_t  l_bits,
uint8_t  out[AES_BLOCK_LEN] 
)
static

One counter-mode CMAC block: CMAC(kdk, counter || label || 0x00 || context || l_bits).

Parameters
kdkKey-derivation key.
kdk_bitskdk size in bits.
counterBlock counter value.
labelKDF label bytes.
label_lenLength of label in bytes.
contextKDF context bytes (may be omitted if ctx_len is 0).
ctx_lenLength of context in bytes.
l_bitsRequested output length in bits, encoded into the KDF input.
outOutput buffer receiving the CMAC block.
Returns
0 on success; -E2BIG if the assembled KDF input exceeds the internal buffer; propagated CMAC error otherwise.

◆ put_be32()

static void put_be32 ( uint8_t *  out,
uint32_t  v 
)
static

Store v big-endian into out (4 bytes).

Parameters
outDestination buffer for the 4 big-endian bytes.
vValue to store.

◆ remap_if_reserved()

static void remap_if_reserved ( uint8_t *  addr,
size_t  len 
)
static

If addr is a reserved all-ones value (0xFFFF/0xFFFE for 2 bytes, 0xFF..FF for other lengths), clear its top bit.

Parameters
addrAddress buffer checked and modified in place.
lenLength of addr in bytes.

◆ sp0_cbc_mac()

static int sp0_cbc_mac ( const uint8_t  key[CCC_MUPSK1_LEN],
const uint8_t  nonce[CCC_SP0_NONCE_LEN],
const uint8_t *  mhr,
size_t  mhr_len,
const uint8_t *  payload,
size_t  payload_len,
uint8_t  tag[AES_BLOCK_LEN] 
)
static

Compute the CCM* CBC-MAC over B0 || l(a)||MHR || payload, zero-padded per block.

Parameters
keyAES-128 key used for the CBC-MAC.
nonceSP0 CCM* nonce for this frame.
mhrMAC header bytes forming the additional authenticated data (may be omitted if mhr_len is 0).
mhr_lenLength of mhr in bytes.
payloadPayload bytes to authenticate.
payload_lenLength of payload in bytes.
tagOutput buffer receiving the computed CBC-MAC tag.
Returns
0 on success; propagated error from AES encryption otherwise.

◆ sp0_ctr()

static int sp0_ctr ( const uint8_t  key[CCC_MUPSK1_LEN],
const uint8_t  nonce[CCC_SP0_NONCE_LEN],
const uint8_t *  in,
size_t  len,
uint8_t *  out,
uint8_t  s0[AES_BLOCK_LEN] 
)
static

Apply CCM* CTR mode: emit the S0 keystream block, then XOR keystream S1.

. over in to out (symmetric encrypt/decrypt).

Parameters
keyAES-128 key used for the CTR keystream.
nonceSP0 CCM* nonce for this frame.
inInput bytes to transform.
lenLength of in/out in bytes.
outOutput buffer receiving the transformed bytes.
s0Output buffer receiving the S0 keystream block (used for MIC encryption).
Returns
0 on success; propagated error from AES encryption otherwise.

◆ sp0_nonce()

static void sp0_nonce ( uint8_t  nonce[CCC_SP0_NONCE_LEN],
const uint8_t  src_long_addr[CCC_SRC_LONG_ADDR_LEN],
uint32_t  frame_counter 
)
static

Build the SP0 CCM* nonce: SrcLongAddr || FrameCounter(BE) || SecLevel.

Parameters
nonceOutput buffer receiving the assembled nonce.
src_long_addrSource long address included in the nonce.
frame_counterFrame counter encoded big-endian into the nonce.

◆ xor_bytes()

static void xor_bytes ( uint8_t *  out,
const uint8_t *  a,
const uint8_t *  b,
size_t  n 
)
static

XOR n bytes: out = a ^ b (out may alias a).

Parameters
outDestination buffer for the XOR result.
aFirst input buffer.
bSecond input buffer.
nNumber of bytes to XOR.