session.cpp
Overview
@file session.cpp Aliro reader BLE session state machine and cryptographic session context. Manages NFC APDU limits, response timeouts, connection setup, fast-path and standard key derivation, message encryption and decryption, and reader-status notifications. Processes events from the BLE transport and application layer.
depends on access_document.h ble_message.h ble_timeout.h nfc_auth.h nfc_select.h nfc_step_up.h
flowchart TD AllocateSession --> NextResponseTimerGeneration AllocateSession --> SessionIndex
API
Cstruct SessionContext
Session context holding all state needed to manage an Aliro session: connection handle, protocol version, cryptographic keys, buffers, counters, and access document details.
Fvoid ApplyNfcApduLimits(SessionContext &session, const struct woz_aliro_select_response &selected)
Sets the session's maximum command and response data lengths from the NFC select response, constrained by the session's buffer sizes and NFC protocol limits, and records whether extended-length APDUs are supported.
ProcessSessionDataCstruct ResponseTimerContext
Atomic generation counter used to invalidate stale response timeout expirations.
Cstruct SessionDataEvent
Internal event structure for session data notifications; carries the connection handle and a copy of the received data.
FSessionDataEvent(ConnectionHandle handle, Data data)
Initializes a session data event by copying the connection handle and data payload.
Cstruct ResponseTimeoutEvent
Internal event structure for response timeout notifications; carries session index and generation number to detect stale timeouts.
FResponseTimeoutEvent(size_t sessionIndex, uint32_t generation)
Initializes a response timeout event with the given session index and generation number.
Cstruct EventHeader
Internal event structure reserved by the FIFO queue mechanism; carries a magic number for validation.
Cclass StackLock
RAII lock guard that acquires the stack mutex on construction and releases it on destruction.
F~StackLock()
Releases the stack mutex.
FStackLock &operator=(const StackLock &) = delete
Deleted copy-assignment operator to prevent copying of the lock guard.
FSessionContext *FindSession(ConnectionHandle handle)
Returns a pointer to the session matching the given connection handle, or nullptr if no session is found.
CreateSession, DestroySession, ProcessSessionData, SendBleMessageFsize_t SessionIndex(const SessionContext &session)
Returns the index of the given session in the global session array.
AllocateSession, NextResponseTimerGenerationFuint32_t NextResponseTimerGeneration(SessionContext &session)
Atomically increments the generation counter for the session's response timer, updates the session's stored generation, and returns the new value.
AllocateSession, ObserveResponseTimeoutMessage, ProcessResponseTimeout, ResetSession · calls SessionIndexFvoid ResponseTimerExpired(void *context)
Response timeout callback invoked by the timer; validates the context pointer, constructs a ResponseTimeoutEvent, and queues it for deferred processing; logs errors if event allocation or queueing fails.
FSessionContext *AllocateSession(ConnectionHandle handle)
Allocates a free session slot, initializes it with the connection handle, and acquires a response timer for BLE sessions; returns nullptr if no slots are available or timer acquisition fails.
CreateSession · calls NextResponseTimerGeneration, SessionIndexFvoid DestroyKey(CryptoTypes::KeyId &keyId)
Destroys a transient cryptographic key if it is non-zero, logs a warning on failure, and zeros the key ID.
CompleteBleAccess, HandleAuth0Response, ResetSession, TryFastKeyFvoid ResetSession(SessionContext &session)
Releases the response timer, destroys all session keys, zeros the URSK, and clears the session context.
CreateSession, DestroySession · calls DestroyKey, NextResponseTimerGenerationFbool ObserveResponseTimeoutMessage(SessionContext &session, enum woz_aliro_ble_timeout_direction direction, const uint8_t *data, size_t length)
Observes an incoming or outgoing BLE message to update response timeout state (arm, stop, or terminate the timer); returns true if the timeout action indicates termination.
EncryptBleMessage, ProcessSessionData, SendApCommand · calls NextResponseTimerGenerationFbool Append(uint8_t *buffer, size_t capacity, size_t &offset, const uint8_t *data, size_t length)
Appends data to a buffer, advancing the offset by the data length, and returns true if the append succeeded without overflow; returns false if data is null, offset exceeds capacity, or the data length exceeds remaining capacity.
AppendCommonSalt, DerivePersistentKey, TryFastKeyFbool AppendCommonSalt(SessionContext &session, uint8_t *salt, size_t capacity, size_t &offset, const char label[12])
Appends reader public key, label, reader identifier, interface byte, version TLV, reader ephemeral public key, transaction identifier, flags, and proprietary information to the salt buffer; returns false if any append overflows the buffer.
DerivePersistentKey, DeriveVolatileKeys, TryFastKey · calls AppendFAliroError DeriveVolatileKeys(SessionContext &session)
Performs key agreement on the ephemeral keys, derives the Kdh key via X9.63 KDF, derives 160 bytes of keying material from Kdh, imports the expedited reader and device keys, imports the StepUpSK and BleSK roots, and returns early on error.
HandleAuth0Response · calls AppendCommonSaltFAliroError DerivePersistentKey(SessionContext &session, const CryptoTypes::PublicKey &credentialPublicKey)
Derives the persistent key by computing a salt from the credential public key and deriving a shared key using the Kdh key; returns an error if the salt overflows or key derivation fails.
HandleAuth1Response · calls Append, AppendCommonSaltFbool IsValidCryptogramPlaintext(const uint8_t *plaintext, size_t length)
Returns true if the plaintext is exactly 48 bytes and contains the fixed-width cryptogram structure (signaling bitmap followed by two signed timestamps) as specified in Table 8-6; returns false otherwise.
TryFastKeyFAliroError TryFastKey(SessionContext &session, CryptoTypes::KeyId kpersistentKeyId, const uint8_t *cryptogram, size_t cryptogramLength, bool &matched, bool &requiresStandard)
Attempts fast-path access by deriving keys from a stored persistent key, decrypting the cryptogram, validating its plaintext, checking whether a new access document is required, and importing the expedited and BLE keys on success; returns the validation status or ALIRO_NO_ERROR on a successful fast match.
HandleAuth0Response · calls Append, AppendCommonSalt, DestroyKey, IsValidCryptogramPlaintextFAliroError SendApCommand(SessionContext &session, const uint8_t *command, size_t commandLength)
For NFC, sends the command directly; for BLE, frames the command in a BLE message, sends it, and observes the timeout message.
HandleAuth0Response, HandleAuth1Response, SendAuth0, SendEncryptedExchange, SendGetResponse, SendNextEnvelope · calls ObserveResponseTimeoutMessageFAliroError SendAuth0(SessionContext &session)
Acquires the reader identity, public key, and ephemeral key pair, generates a random transaction identifier, optionally loads persistent credential key IDs for fast-path attempts, builds and sends the Auth0 command, and transitions to AwaitingAuth0.
ProcessSessionData · calls SendApCommandFAliroError HandleAuth0Response(SessionContext &session, Data data)
Decrypt and validate Auth0 response; attempt fast-key authentication if enabled, otherwise derive volatile keys and build Auth1 command. Fails on parse, fast-key, key derivation, signature generation, or command build errors. Attempts each persistent fast key in order if fast-path enabled; succeeds early if matched and does not require standard phase. Destroys expedited and volatile keys if fast-path active. Sets state to AwaitingAuth1. On trace enabled, logs each fast-key trial and final derivation status.
ProcessSessionData · calls DeriveVolatileKeys, DestroyKey, ProcessAccess, SendApCommand, SendNfcCompletionExchange, SendUrskExchange, TryFastKeyFCryptoTypes::Nonce MakeNonce(bool device, uint32_t counter)
Constructs a 12-byte nonce with a device/reader flag in byte 7 and a big-endian counter in bytes 8–11.
DecryptBleMessage, EncryptBleMessage, FinishStepUpResponse, HandleAuth1Response, HandleExchangeResponse, SendEncryptedExchange, StartStepUpExchangeFAliroError DeriveBleSessionKeys(SessionContext &session)
Derives the directional BLE session keys (BleSKReader and BleSKDevice) from the BLE key using protocol versions and standard info labels; returns an error if key derivation fails.
CompleteBleAccessFAliroError EncryptBleMessage(SessionContext &session, const uint8_t *plaintext, size_t plaintextLength)
Encrypts a BLE message by parsing the plaintext header, encrypting the payload with the reader counter and reader key, constructing the protected frame, sending it, and observing the timeout message; returns an error if the key is absent, counter overflows, or encryption fails.
CompleteBleAccess, SendBleMessage, SendReaderStatusChangedMessage · calls MakeNonce, ObserveResponseTimeoutMessageFAliroError DecryptBleMessage(SessionContext &session, const struct woz_aliro_ble_message &message, uint8_t *plaintext, size_t plaintextCapacity, size_t &plaintextLength)
Decrypts a BLE message using the device key and current device counter as a nonce, validates the authentication tag, increments the counter, and reconstructs the plaintext with the BLE header; returns an error if the key is absent, counter overflows, or decryption fails.
ProcessSessionData · calls MakeNonceFAliroError SendEncryptedExchange(SessionContext &session, const uint8_t *plaintext, size_t plaintextLength, CryptoTypes::KeyId readerKeyId, bool useStepUpKeys)
Encrypt and send an exchange command (0x80c9) with the given plaintext, reader key, and counter state. Fails if plaintext is null, exceeds 254 bytes (to fit tag), or command length overflows the TX buffer. Increments reader counter and sets session state to AwaitingExchangeResponse. Caller must ensure key ID and counter state are correct for the current protocol phase (expedited or step-up).
SendNfcCompletionExchange, SendUrskExchange · calls MakeNonce, SendApCommandFAliroError SendUrskExchange(SessionContext &session)
Send a URSK exchange command (0x98 0x00) using the expedited reader key without requesting step-up. Wrapper around SendEncryptedExchange. Used to signal unlock completion when no Access Document is needed.
HandleAuth0Response, HandleAuth1Response · calls SendEncryptedExchangeFAliroError SendNfcCompletionExchange(SessionContext &session, bool useStepUpKeys)
Send a final NFC completion exchange (0x97 0x02 0x01 0x00) in the expedited or step-up phase, matching the reference reader's successful secure state. Wrapper around SendEncryptedExchange. Caller specifies which reader key (expedited or step-up) and phase to use via useStepUpKeys.
CollectStepUpResponse, HandleAuth0Response, HandleAuth1Response · calls SendEncryptedExchangeFAliroError ProcessAccess(SessionContext &session)
Invokes the appropriate access processing method (fast or standard) based on the session state, marks access as processed on success, and returns the operation status.
CompleteBleAccess, HandleAuth0Response, HandleAuth1ResponseFAliroError CompleteBleAccess(SessionContext &session)
Processes access, derives BLE session keys, extracts the ranging session ID from the transaction identifier, starts the ranging session, encrypts and sends an access-completed message, destroys Access Protocol keys, and transitions to UwbRanging on success.
CollectStepUpResponse, HandleExchangeResponse · calls DeriveBleSessionKeys, DestroyKey, EncryptBleMessage, ProcessAccessFAliroError HandleExchangeResponse(SessionContext &session, Data data)
Decrypt and validate an encrypted exchange response (expedited or step-up phase); optionally request Access Document via step-up or complete the access. Fails on APDU status, decryption, or plaintext validation errors. Expects plaintext 0x00 0x02 0x00 0x00 (success marker). If NFC, sets state to AccessComplete. If BLE with step-up requested, calls StartStepUpExchange; otherwise completes access. Caller must have set the correct device key ID and counter state before calling.
ProcessSessionData · calls CompleteBleAccess, MakeNonce, StartStepUpExchangeFAliroError SendNextEnvelope(SessionContext &session)
Build and send the next envelope command in a step-up exchange, segmenting mExchangeBuffer across one or more APDUs. Fails if segmentation fails or buffer is too small. Updates mExchangeOffset and mLastEnvelope; sets state to SendingStepUpEnvelope. Caller must have populated mExchangeBuffer, mMaxCommandData, and mMaxResponseData before calling.
ProcessSessionData, StartStepUpExchange · calls SendApCommandFAliroError SendGetResponse(SessionContext &session, size_t expectedLength)
Request the next chunk of a step-up response from the reader via GET RESPONSE command. Fails if expectedLength cannot be encoded or buffer is too small. Sets session state to AwaitingStepUpResponse. Used when a single APDU response is insufficient to deliver the full step-up plaintext.
CollectStepUpResponse · calls SendApCommandFAliroError StartStepUpExchange(SessionContext &session)
Derive directional step-up keys, build and encrypt a device request, wrap it in session data and DO53, then send the first envelope segment. Fails if key derivation, request building, encryption, wrapping, or segmentation fails. Resets reader and device counters to initial value. Sets state to SendingStepUpEnvelope. Caller must have populated mRequestedElement, mRequestedElementLength, and mIntentToStore before calling. On trace enabled, performs AEAD round-trip verification.
HandleAuth1Response, HandleExchangeResponse, ProcessSessionData · calls MakeNonce, SendNextEnvelopeFsize_t EncodeBstrHead(size_t length, uint8_t *output)
Encodes the CBOR byte-string header for a given length into output, returning the number of bytes written (1, 2, or 3 depending on the length value).
ValidateAndProcessAccessDocumentFAliroError ValidateAndProcessAccessDocument(SessionContext &session, const uint8_t *deviceResponse, size_t deviceResponseLength)
Parses and validates an access document: verifies the issuer-signed item digest, validates the issuer certificate or key ID, checks the document's validity period, ensures the device public key matches, constructs a COSE Sig_structure, verifies the signature, and invokes the access processing interface.
FinishStepUpResponse · calls EncodeBstrHeadFAliroError FinishStepUpResponse(SessionContext &session)
Unwraps the step-up response DO53 and session-data containers, decrypts the access document using the device counter and StepUpDeviceKey, validates and processes the access document, and marks access as processed on success.
CollectStepUpResponse · calls MakeNonce, ValidateAndProcessAccessDocumentFAliroError CollectStepUpResponse(SessionContext &session, Data data)
Collect one or more envelope responses from the reader and assemble the complete step-up plaintext. Fails if response collection fails or APDU status is non-zero. If more data needed, sends GET RESPONSE. Otherwise decrypts and validates step-up response, then completes the access (BLE or NFC). Sets state to AwaitingStepUpResponse or AccessComplete. Caller must have initialized mExchangeBuffer before calling.
ProcessSessionData · calls CompleteBleAccess, FinishStepUpResponse, SendGetResponse, SendNfcCompletionExchangeFAliroError HandleAuth1Response(SessionContext &session, Data data)
Decrypt, validate, and process Auth1 response; derive persistent key; optionally request Access Document or start step-up exchange. Fails on APDU status, decryption, parse, signature verification, or persistent key derivation errors. Extracts credential public key, verifies signature, and checks signaling bitmap for Access Document and step-up AID selection requirements. Sets state to SelectingStepUp, AwaitingAuth1 (BLE with document), or initiates step-up/completion. Caller must have set mExpeditedDeviceKeyId and mDeviceCounter before calling.
ProcessSessionData · calls DerivePersistentKey, MakeNonce, ProcessAccess, SendApCommand, SendNfcCompletionExchange, SendUrskExchange, StartStepUpExchangeFAliroError AliroStack::CreateSession(ConnectionHandle connectionHandle)
Creates a new session for the given connection handle: for NFC, sends a Select command and transitions to SelectingExpedited; for BLE, validates the protocol version and transitions to BleConnected; returns an error if the session already exists, allocation fails, or the protocol version is unsupported.
AllocateSession, FindSession, ResetSessionFvoid AliroStack::DestroySession(ConnectionHandle connectionHandle)
Finds the session for the given connection handle and destroys it if found; invokes the session termination callback on success.
HandleSessionData, ProcessResponseTimeout, ProcessSessionData, SendBleMessage · calls FindSession, ResetSessionFvoid ProcessSessionData(ConnectionHandle handle, Data data)
Route incoming session data (NFC APDU or reassembled BLE frame) to the appropriate protocol handler based on session state, forwarding UWB control to the UWB stack if needed. On BLE: reassembles fragmented messages, validates frames, decrypts, and routes to auth/exchange/ranging handlers or timeout control. On NFC: dispatches to Select/Auth0/Auth1/Exchange response handlers. Destroys session on frame error, parse error, or explicit termination. Caller must ensure handle and data are valid; data may be null (triggers session destruction).
HandleSessionData, ProcessEvent · calls ApplyNfcApduLimits, CollectStepUpResponse, DecryptBleMessage, DestroySession, FindSession, HandleAuth0Response, HandleAuth1Response, HandleExchangeResponseFvoid ProcessResponseTimeout(size_t sessionIndex, uint32_t generation)
Validates the response timeout expiration (session exists, is BLE, timer handle is valid, generation matches, and timeout is not idle), destroys the session on confirmed timeout, and logs a warning.
ProcessEvent · calls DestroySession, NextResponseTimerGenerationFvoid AliroStack::HandleSessionData(ConnectionHandle handle, Data data)
Accept incoming session data from BLE or NFC and defer or process it synchronously. On NFC, processes immediately. On BLE, enqueues as SessionDataEvent for deferred processing to avoid deadlock. Destroys session on invalid length, allocation failure, or queueing error. Caller must ensure handle is valid; data may be null.
DestroySession, ProcessSessionDataFvoid AliroStack::SendBleMessage(ConnectionHandle connectionHandle, const uint8_t *data, size_t length) const
Finds the session in UwbRanging state, encrypts the message using the BLE reader key, logs a warning and destroys the session on failure.
DestroySession, EncryptBleMessage, FindSessionFAliroError AliroStack::SendReaderStatusChangedMessage(OperationSource operationSource, ReaderStateByte readerState, const CryptoTypes::PublicKey *accessCredentialPublicKey) const
Encrypt and broadcast a Reader Status Changed message to all BLE sessions in UWB ranging state, optionally filtering by credential public key. Returns the first error encountered, or ALIRO_NO_ERROR if all sessions received the message (or were not BLE/not ranging). Used to signal reader state changes (e.g., unlocked, secured) during active UWB sessions.
EncryptBleMessageFvoid AliroStack::ProcessEvent(void *event)
Dequeue and process a pending Aliro event (ResponseTimeoutEvent or SessionDataEvent) or log and ignore unknown events. Deletes the event after processing. Called by the OS event loop when an Aliro-owned event is ready. Caller must pass a non-null event pointer; null triggers a warning log.
ProcessResponseTimeout, ProcessSessionData