|
| static void | pack_id24 (uint32_t id, uint8_t buf[3]) |
| static uint32_t | unpack_id24 (const uint8_t buf[3]) |
| static uint8_t | pack_header (uint8_t ety, uint8_t mti, uint8_t df) |
| static uint8_t | header_mti (uint8_t header) |
| static uint8_t | header_df (uint8_t header) |
| static void | pack_u32_be (uint32_t val, uint8_t buf[4]) |
| static uint32_t | unpack_u32_be (const uint8_t buf[4]) |
| static void | generate_random_bytes (uint8_t *out, size_t len, uint32_t seed_hint) |
| static safecomm_status_t | mac_over_da_body (const safecomm_mac_ctx_t *mac, uint32_t da_id, const uint8_t *body, size_t body_len, uint8_t out_mac[SAFECOMM_MAC_LEN]) |
| | Build [length(2, BE) | DA(3) | body] and MAC it (Subset-037-2 §6.2.3.2.1.1 "Procedure 1"). safecomm_mac_compute() applies the ISO/IEC 9797-1 Method 1 zero-padding itself, so the caller need only supply the unpadded bytes.
|
| safecomm_status_t | safecomm_saf_init (safecomm_saf_conn_t *conn, const safecomm_saf_config_t *cfg, const safecomm_transport_t *transport) |
| | Initialise a SaF connection object (no I/O yet).
|
| static bool | connect_expired (const safecomm_saf_conn_t *conn, uint32_t start_ms) |
| static safecomm_status_t | responder_process_au1 (safecomm_saf_conn_t *conn, const uint8_t *au1, size_t au1_len, const uint8_t rb[SAFECOMM_MAC_RAND_LEN], uint8_t *out_au2) |
| static safecomm_status_t | responder_process_au3 (safecomm_saf_conn_t *conn, const uint8_t *au3, size_t au3_len, uint8_t *out_ar) |
| safecomm_status_t | safecomm_saf_connect (safecomm_saf_conn_t *conn) |
| | Run the AU1/AU2/AU3 handshake to completion (blocking).
|
| safecomm_saf_frame_kind_t | safecomm_saf_frame_kind (const uint8_t *frame, size_t frame_len) |
| | Classify a received frame by its header MTI (no MAC check).
|
| safecomm_status_t | safecomm_saf_au1_fields (const uint8_t *au1, size_t au1_len, uint32_t *out_peer_id, uint8_t out_ra[SAFECOMM_MAC_RAND_LEN]) |
| | Read the caller's ETCS identity and initiator random RA out of an AU1 frame (no MAC exists on AU1). A responder that serves many peers uses the identity to pick the peer's KMAC and configure the connection, and RA to pair a shared RB with the handshake it belongs to.
|
| safecomm_status_t | safecomm_saf_responder_on_au1 (safecomm_saf_conn_t *conn, const uint8_t *au1, size_t au1_len, const uint8_t rb[SAFECOMM_MAC_RAND_LEN], uint8_t *out_au2, size_t out_cap, size_t *out_len) |
| | Responder: accept AU1, derive the session key from RA and the caller's rb, and build AU2.
|
| safecomm_status_t | safecomm_saf_responder_on_au3 (safecomm_saf_conn_t *conn, const uint8_t *au3, size_t au3_len, uint8_t *out_ar, size_t out_cap, size_t *out_len) |
| | Responder: verify AU3 and build the AR confirmation; the connection enters DATA.
|
| safecomm_status_t | safecomm_saf_frame_sent (safecomm_saf_conn_t *conn) |
| | Record that the caller transmitted the frame from safecomm_saf_prepare_send() itself: advances the send sequence exactly as safecomm_saf_commit_send() does after a successful transport send.
|
| safecomm_status_t | safecomm_saf_disconnect_frame (safecomm_saf_role_t role, uint8_t *out, size_t out_cap, size_t *out_len) |
| | Build a Sa-DISCONNECT (reason 0, sub-reason 0) for a side of the given role, without any connection state - e.g. a responder releasing a Sa-DATA frame that belongs to no connection it holds (the peer's session is stale, typically after this node restarted), so the peer reconnects at once instead of waiting out its supervision time. Unauthenticated, as every Sa-DISCONNECT (Subset-037-2 §6.2.3.3.3.2).
|
| safecomm_status_t | safecomm_saf_prepare_send (safecomm_saf_conn_t *conn, const uint8_t *pdu, size_t pdu_len, uint8_t *out_frame, size_t out_frame_cap, size_t *out_frame_len) |
| | Prepare and MAC-protect one Sa-DATA frame without transmitting it (for 2oo2 cross-checking).
|
| safecomm_status_t | safecomm_saf_commit_send (safecomm_saf_conn_t *conn, const uint8_t *frame, size_t frame_len) |
| | Transmit an already prepared and cross-compared Sa-DATA frame.
|
| safecomm_status_t | safecomm_saf_send (safecomm_saf_conn_t *conn, const uint8_t *pdu, size_t pdu_len) |
| | Send one user PDU as a MAC-protected, sequence-numbered Sa-DATA.
|
| safecomm_status_t | safecomm_saf_recv (safecomm_saf_conn_t *conn, uint8_t *out, size_t out_cap, size_t *out_len) |
| | Receive and verify one Sa-DATA PDU (blocking).
|
| safecomm_status_t | safecomm_saf_accept_frame (safecomm_saf_conn_t *conn, const uint8_t *rx_buf, size_t rx_len, uint8_t *out, size_t out_cap, size_t *out_len) |
| | Verify one received Sa-DATA or Sa-DISCONNECT frame (the checks of safecomm_saf_recv(), without the transport).
|
| void | safecomm_saf_set_time (safecomm_saf_conn_t *conn, uint32_t now_ms) |
| | Orderly Sa-DISCONNECT, then return the object to IDLE.
|
| safecomm_status_t | safecomm_saf_supervise (safecomm_saf_conn_t *conn) |
| | Idle-link supervision (EN 50159 delay and loss): in DATA, if no valid frame arrived for cfg.t_supervision_ms the connection goes to IDLE and SAFECOMM_STATUS_TIMEOUT is returned. Call it cyclically. The first call arms the timer. t_supervision_ms == 0 disables it.
|
| safecomm_status_t | safecomm_saf_disconnect (safecomm_saf_conn_t *conn) |
| safecomm_saf_state_t | safecomm_saf_state (const safecomm_saf_conn_t *conn) |
| | Current state (for logging / tests). Returns IDLE for NULL.
|
| safecomm_status_t | safecomm_saf_export_session_state (const safecomm_saf_conn_t *conn, safecomm_session_vital_state_t *out_state) |
| | Export vital session state for warm/hot standby synchronization.
|
| safecomm_status_t | safecomm_saf_import_session_state (safecomm_saf_conn_t *conn, const safecomm_session_vital_state_t *in_state) |
| | Import vital session state upon warm-standby takeover or channel resynchronization.
|
Safety Functional (SaF) sub-layer state machine.
PDU layouts and the MAC input construction follow Subset-037-2 ("EuroRadio
FIS - Safety Layer") v4.0.0 §6.2.5 (frame structure) and §6.2.3.2.1/6.2.3.2.2 (message-origin-authentication / peer-entity-authentication MAC inputs), cross-checked against the worked CBC-MAC example in that document's Annex B.
One deliberate addition beyond the official SL: the official Sa-DATA (DT) PDU carries no sequence number (Subset-037-2 Table 12) — replay/deletion/ re-ordering defence there is the job of the Subset-098 SAI sub-layer, which this library does not implement. In its place this module puts a 4-byte sequence number inside the MAC-covered SaUD area (immediately before the user PDU) so a frame still carries some ordering defence end to end. This is a project-specific extension, not part of Subset-037-2's own PDU, and is not itself conformance-tested; see docs/safety/SUBSET_MAPPING.md.
Implements:
- AU1/AU2/AU3/AR mutual authentication handshake
- Sa-DATA framing: MAC-protected, with the sequence extension above
- 2oo2 split-send (prepare_send / commit_send) for hot redundancy
- Vital session state checkpoint export/import for warm/hot failover
- Sa-DISCONNECT teardown (unauthenticated, per Subset-037-2 §6.2.3.3.3.2: "the authentication of the connection release phase is not required")
Definition in file saf.c.