39#define SAF_SEQ_WINDOW 8U
43static void pack_id24(uint32_t
id, uint8_t buf[3])
45 buf[0] = (uint8_t)((
id >> 16U) & 0xFFU);
46 buf[1] = (uint8_t)((
id >> 8U) & 0xFFU);
47 buf[2] = (uint8_t)(
id & 0xFFU);
50static uint32_t unpack_id24(
const uint8_t buf[3])
52 return ((uint32_t)buf[0] << 16U) | ((uint32_t)buf[1] << 8U) | (uint32_t)buf[2];
56static uint8_t pack_header(uint8_t ety, uint8_t mti, uint8_t df)
58 return (uint8_t)(((ety & 0x07U) << 5U) | ((mti & 0x0FU) << 1U) | (df & 0x01U));
61static uint8_t header_mti(uint8_t header)
63 return (uint8_t)((header >> 1U) & 0x0FU);
66static uint8_t header_df(uint8_t header)
68 return (uint8_t)(header & 0x01U);
71static void pack_u32_be(uint32_t val, uint8_t buf[4])
73 buf[0] = (uint8_t)((val >> 24U) & 0xFFU);
74 buf[1] = (uint8_t)((val >> 16U) & 0xFFU);
75 buf[2] = (uint8_t)((val >> 8U) & 0xFFU);
76 buf[3] = (uint8_t)(val & 0xFFU);
79static uint32_t unpack_u32_be(
const uint8_t buf[4])
81 return ((uint32_t)buf[0] << 24U) | ((uint32_t)buf[1] << 16U) | ((uint32_t)buf[2] << 8U) | (uint32_t)buf[3];
84static void generate_random_bytes(uint8_t *out,
size_t len, uint32_t seed_hint)
86 static uint32_t lcg_state = 0x12345678U;
89 lcg_state ^= seed_hint;
91 for (
size_t i = 0U; i < len; i++)
93 lcg_state = (lcg_state * 1664525U) + 1013904223U;
94 out[i] = (uint8_t)((lcg_state >> 16U) & 0xFFU);
97 out[i] = (uint8_t)(i + 1U);
108 const uint8_t *body,
size_t body_len,
113 size_t l = 3U + body_len;
114 if ((body_len + 5U) >
sizeof(buf))
118 buf[0] = (uint8_t)((l >> 8U) & 0xFFU);
119 buf[1] = (uint8_t)(l & 0xFFU);
120 pack_id24(da_id, &buf[2]);
121 (void)memcpy(&buf[5], body, body_len);
129 if ((conn == NULL) || (cfg == NULL) || (transport == NULL)
130 || (transport->
send == NULL) || (transport->
recv == NULL))
134 (void)memset(conn, 0,
sizeof(*conn));
136 conn->transport = *transport;
137 conn->state = SAFECOMM_SAF_STATE_IDLE;
154 conn->state = SAFECOMM_SAF_STATE_IDLE;
155 conn->supervision_armed =
false;
156 if ((au1_len !=
SAFECOMM_SAF_AU1_LEN) || (header_mti(au1[0]) != MTI_AU1) || (header_df(au1[0]) != 0U))
173 out_au2[0] = pack_header(conn->cfg.
ety, MTI_AU2, 1U);
179 size_t body_len = 5U;
180 size_t l = 3U + body_len;
181 input[0] = (uint8_t)((l >> 8U) & 0xFFU);
182 input[1] = (uint8_t)(l & 0xFFU);
184 (void)memcpy(&input[5], &out_au2[0], body_len);
194 conn->state = SAFECOMM_SAF_STATE_WAIT_AU3;
209 if ((au3_len != SAFECOMM_SAF_AU3_LEN) || (header_mti(au3[0]) != MTI_AU3) || (header_df(au3[0]) != 0U))
211 conn->state = SAFECOMM_SAF_STATE_IDLE;
214 rmi[0] = (uint8_t)((rl >> 8U) & 0xFFU);
215 rmi[1] = (uint8_t)(rl & 0xFFU);
223 conn->state = SAFECOMM_SAF_STATE_IDLE;
228 out_ar[0] = pack_header(0U, MTI_AR, 1U);
232 conn->state = SAFECOMM_SAF_STATE_IDLE;
237 conn->state = SAFECOMM_SAF_STATE_DATA;
247 if (conn->state != SAFECOMM_SAF_STATE_IDLE)
251 const uint32_t start_ms = conn->
now_ms;
259 conn->state = SAFECOMM_SAF_STATE_WAIT_AU2;
260 conn->supervision_armed =
false;
263 au1[0] = pack_header(conn->cfg.
ety, MTI_AU1, 0U);
272 conn->state = SAFECOMM_SAF_STATE_IDLE;
278 st = conn->transport.
recv(conn->transport.
user, au2,
sizeof(au2), &au2_len);
281 conn->state = SAFECOMM_SAF_STATE_IDLE;
284 if (connect_expired(conn, start_ms))
286 conn->state = SAFECOMM_SAF_STATE_IDLE;
289 if ((au2_len !=
sizeof(au2)) || (header_mti(au2[0]) != MTI_AU2) || (header_df(au2[0]) != 1U))
291 conn->state = SAFECOMM_SAF_STATE_IDLE;
294 uint32_t peer_id = unpack_id24(&au2[1]);
297 conn->state = SAFECOMM_SAF_STATE_IDLE;
305 conn->state = SAFECOMM_SAF_STATE_IDLE;
313 size_t body_len = 5U;
314 size_t l = 3U + body_len;
315 input[0] = (uint8_t)((l >> 8U) & 0xFFU);
316 input[1] = (uint8_t)(l & 0xFFU);
318 (void)memcpy(&input[5], &au2[0], body_len);
327 conn->state = SAFECOMM_SAF_STATE_IDLE;
333 au3[0] = pack_header(0U, MTI_AU3, 0U);
338 rmi[0] = (uint8_t)((rl >> 8U) & 0xFFU);
339 rmi[1] = (uint8_t)(rl & 0xFFU);
348 conn->state = SAFECOMM_SAF_STATE_IDLE;
352 st = conn->transport.
send(conn->transport.
user, au3,
sizeof(au3));
355 conn->state = SAFECOMM_SAF_STATE_IDLE;
363 st = conn->transport.
recv(conn->transport.
user, ar,
sizeof(ar), &ar_len);
366 conn->state = SAFECOMM_SAF_STATE_IDLE;
369 if (connect_expired(conn, start_ms))
371 conn->state = SAFECOMM_SAF_STATE_IDLE;
374 if ((ar_len !=
sizeof(ar)) || (header_mti(ar[0]) != MTI_AR) || (header_df(ar[0]) != 1U))
376 conn->state = SAFECOMM_SAF_STATE_IDLE;
383 conn->state = SAFECOMM_SAF_STATE_IDLE;
389 conn->state = SAFECOMM_SAF_STATE_DATA;
398 uint8_t au2[SAFECOMM_SAF_AU2_LEN];
399 uint8_t au3[SAFECOMM_SAF_AU3_LEN];
400 uint8_t ar[SAFECOMM_SAF_AR_LEN];
407 conn->state = SAFECOMM_SAF_STATE_IDLE;
410 if (connect_expired(conn, start_ms))
412 conn->state = SAFECOMM_SAF_STATE_IDLE;
416 st = responder_process_au1(conn, au1, au1_len, rb, au2);
422 st = conn->transport.
send(conn->transport.
user, au2,
sizeof(au2));
425 conn->state = SAFECOMM_SAF_STATE_IDLE;
428 st = conn->transport.
recv(conn->transport.
user, au3,
sizeof(au3), &au3_len);
431 conn->state = SAFECOMM_SAF_STATE_IDLE;
434 if (connect_expired(conn, start_ms))
436 conn->state = SAFECOMM_SAF_STATE_IDLE;
439 st = responder_process_au3(conn, au3, au3_len, ar);
444 st = conn->transport.
send(conn->transport.
user, ar,
sizeof(ar));
447 conn->state = SAFECOMM_SAF_STATE_IDLE;
458 if ((frame != NULL) && (frame_len > 0U))
460 switch (header_mti(frame[0]))
462 case MTI_AU1: kind = SAFECOMM_SAF_FRAME_AU1;
break;
463 case MTI_AU2: kind = SAFECOMM_SAF_FRAME_AU2;
break;
464 case MTI_AU3: kind = SAFECOMM_SAF_FRAME_AU3;
break;
465 case MTI_DT: kind = SAFECOMM_SAF_FRAME_DATA;
break;
466 case MTI_DI: kind = SAFECOMM_SAF_FRAME_DISCONNECT;
break;
467 case MTI_AR: kind = SAFECOMM_SAF_FRAME_AR;
break;
468 default: kind = SAFECOMM_SAF_FRAME_UNKNOWN;
break;
477 if ((au1 == NULL) || (out_peer_id == NULL) || (out_ra == NULL))
481 if ((au1_len !=
SAFECOMM_SAF_AU1_LEN) || (header_mti(au1[0]) != MTI_AU1) || (header_df(au1[0]) != 0U))
485 *out_peer_id = unpack_id24(&au1[1]);
492 uint8_t *out_au2,
size_t out_cap,
size_t *out_len)
496 if ((conn == NULL) || (au1 == NULL) || (rb == NULL) || (out_au2 == NULL) || (out_len == NULL))
504 if (out_cap < SAFECOMM_SAF_AU2_LEN)
510 st = responder_process_au1(conn, au1, au1_len, rb, out_au2);
514 *out_len = SAFECOMM_SAF_AU2_LEN;
520 uint8_t *out_ar,
size_t out_cap,
size_t *out_len)
524 if ((conn == NULL) || (au3 == NULL) || (out_ar == NULL) || (out_len == NULL))
532 if (out_cap < SAFECOMM_SAF_AR_LEN)
538 conn->state = SAFECOMM_SAF_STATE_IDLE;
541 st = responder_process_au3(conn, au3, au3_len, out_ar);
544 *out_len = SAFECOMM_SAF_AR_LEN;
555 if (conn->state != SAFECOMM_SAF_STATE_DATA)
566 if ((out == NULL) || (out_len == NULL))
582 const uint8_t *pdu,
size_t pdu_len,
583 uint8_t *out_frame,
size_t out_frame_cap,
584 size_t *out_frame_len)
586 if ((conn == NULL) || (out_frame == NULL) || (out_frame_len == NULL) ||
591 if (conn->state != SAFECOMM_SAF_STATE_DATA)
597 size_t body_len = 1U + 4U + pdu_len;
599 if (out_frame_cap < total_len)
605 out_frame[0] = pack_header(0U, MTI_DT, df);
606 pack_u32_be(conn->seq_tx, &out_frame[1]);
609 (void)memcpy(&out_frame[5], pdu, pdu_len);
613 out_frame, body_len, &out_frame[body_len]);
619 *out_frame_len = total_len;
624 const uint8_t *frame,
size_t frame_len)
626 if ((conn == NULL) || (frame == NULL) || (frame_len == 0U))
630 if (conn->state != SAFECOMM_SAF_STATE_DATA)
644 const uint8_t *pdu,
size_t pdu_len)
647 size_t frame_len = 0U;
658 uint8_t *out,
size_t out_cap,
size_t *out_len)
660 if ((conn == NULL) || (out == NULL) || (out_len == NULL))
664 if (conn->state != SAFECOMM_SAF_STATE_DATA)
681 uint8_t *out,
size_t out_cap,
size_t *out_len)
685 if ((conn == NULL) || (rx_buf == NULL) || (out == NULL) || (out_len == NULL))
689 if (conn->state != SAFECOMM_SAF_STATE_DATA)
696 conn->state = SAFECOMM_SAF_STATE_IDLE;
703 if (header_mti(rx_buf[0]) == MTI_DI)
709 conn->state = SAFECOMM_SAF_STATE_IDLE;
714 if ((header_mti(rx_buf[0]) != MTI_DT) || (rx_len < (1U + 4U +
SAFECOMM_MAC_LEN)))
716 conn->state = SAFECOMM_SAF_STATE_IDLE;
719 if (header_df(rx_buf[0]) != expected_df)
721 conn->state = SAFECOMM_SAF_STATE_IDLE;
730 conn->state = SAFECOMM_SAF_STATE_IDLE;
734 uint32_t rx_seq = unpack_u32_be(&rx_buf[1]);
737 if ((uint32_t)(rx_seq - conn->seq_rx) > (uint32_t)SAF_SEQ_WINDOW)
739 conn->state = SAFECOMM_SAF_STATE_IDLE;
743 conn->seq_rx = rx_seq + 1U;
745 conn->supervision_armed =
true;
747 size_t payload_len = body_len - 5U;
748 if (payload_len > out_cap)
752 if (payload_len > 0U)
754 (void)memcpy(out, &rx_buf[5], payload_len);
756 *out_len = payload_len;
775 if (conn->state != SAFECOMM_SAF_STATE_DATA)
777 conn->supervision_armed =
false;
784 if (!conn->supervision_armed)
787 conn->supervision_armed =
true;
792 conn->state = SAFECOMM_SAF_STATE_IDLE;
793 conn->supervision_armed =
false;
806 if (conn->state != SAFECOMM_SAF_STATE_IDLE)
809 size_t disconn_len = 0U;
812 (void)conn->transport.
send(conn->transport.
user, disconn, disconn_len);
815 conn->state = SAFECOMM_SAF_STATE_IDLE;
821 return (conn != NULL) ? conn->state : SAFECOMM_SAF_STATE_IDLE;
827 if ((conn == NULL) || (out_state == NULL))
832 (void)memset(out_state, 0,
sizeof(*out_state));
833 out_state->state = conn->state;
834 out_state->seq_tx = conn->seq_tx;
835 out_state->seq_rx = conn->seq_rx;
836 out_state->seq_rx_last_accepted = (conn->seq_rx > 0U) ? (conn->seq_rx - 1U) : 0U;
849 if ((conn == NULL) || (in_state == NULL))
854 conn->state = in_state->state;
855 conn->seq_tx = in_state->seq_tx;
856 conn->seq_rx = in_state->seq_rx;
#define SAFECOMM_MAC_RAND_LEN
#define SAFECOMM_MAC_KEY_LEN
safecomm_status_t safecomm_mac_derive_ksmac(const uint8_t kmac[SAFECOMM_MAC_KEY_LEN], const uint8_t ra[SAFECOMM_MAC_RAND_LEN], const uint8_t rb[SAFECOMM_MAC_RAND_LEN], safecomm_mac_ctx_t *out_ctx)
Derive the session key KSMAC from KMAC and the AU random values.
@ SAFECOMM_MAC_ALG_3DES_SS037
safecomm_status_t safecomm_mac_compute(const safecomm_mac_ctx_t *ctx, const uint8_t *msg, size_t msg_len, uint8_t out_mac[SAFECOMM_MAC_LEN])
Compute the 8-byte EuroRadio MAC over msg.
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")....
Safety Functional (SaF) sub-layer — shared by Subset-037 and Subset-098 (ERTMS/ETCS Subset-037 §7).
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.
void safecomm_saf_set_time(safecomm_saf_conn_t *conn, uint32_t now_ms)
Orderly Sa-DISCONNECT, then return the object to IDLE.
safecomm_saf_frame_kind_t
Frame kind by its header MTI, for a caller that routes received frames itself.
safecomm_status_t safecomm_saf_connect(safecomm_saf_conn_t *conn)
Run the AU1/AU2/AU3 handshake to completion (blocking).
#define SAFECOMM_SAF_AU1_LEN
Authentication-phase frame lengths (Subset-037-2 §6.2.5), bytes.
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_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_saf_state_t safecomm_saf_state(const safecomm_saf_conn_t *conn)
Current state (for logging / tests). Returns IDLE for NULL.
safecomm_saf_state_t
SaF state-machine state (observable, for logging / tests).
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 *frame, size_t frame_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(),...
#define SAFECOMM_SAF_DI_LEN
Sa-DISCONNECT frame length (header + reason + sub-reason).
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....
#define SAFECOMM_SAF_MAX_PDU
Largest SaF user-data PDU this build accepts (bytes).
#define SAFECOMM_SAF_DATA_OVERHEAD
Sa-DATA framing overhead around the user PDU: header(1) + sequence(4) + MAC(8).
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 s...
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_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)....
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_frame_sent(safecomm_saf_conn_t *conn)
Record that the caller transmitted the frame from safecomm_saf_prepare_send() itself: advances the se...
safecomm_saf_role_t
SaF connection role.
@ SAFECOMM_SAF_ROLE_RESPONDER
@ SAFECOMM_SAF_ROLE_INITIATOR
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.
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_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).
safecomm_status_t
Result of a Safe Communication Framework operation.
@ SAFECOMM_STATUS_AUTH_FAILED
@ SAFECOMM_STATUS_INVALID_PARAM
@ SAFECOMM_STATUS_MAC_MISMATCH
@ SAFECOMM_STATUS_TIMEOUT
@ SAFECOMM_STATUS_SEQ_ERROR
@ SAFECOMM_STATUS_BUFFER_TOO_SMALL
@ SAFECOMM_STATUS_PEER_DISCONNECT
@ SAFECOMM_STATUS_TRANSPORT
Immutable session-key material for one SaF connection.
uint8_t ksmac[SAFECOMM_MAC_KEY_LEN]
Static configuration for one SaF connection.
uint8_t kmac[SAFECOMM_MAC_KEY_LEN]
uint32_t t_supervision_ms
SaF connection object. Caller-allocated, zero-initialised, opaque in practice — fields are not part o...
uint32_t connect_start_ms
Vital session state for 2oo2 hot redundancy and active/warm-standby failover.
A consumer-supplied bearer the SaF layer runs over.
safecomm_transport_send_fn send
safecomm_transport_recv_fn recv