Safe Communication Framework 0.1.0 build feea4f9
ERTMS/ETCS safe communication (Subset-037 EuroRadio, Subset-098 RBC/RBC), CENELEC EN 50128 SIL 4 oriented
Loading...
Searching...
No Matches
mac.c
Go to the documentation of this file.
1
8#include "safecomm/mac/mac.h"
9
10#include <string.h>
11
12/* ========================================================================= */
13/* DES / 3DES Engine (FIPS 46-3 / NIST SP 800-20) */
14/* ========================================================================= */
15
16static const uint8_t IP_TABLE[64] = {
17 58U, 50U, 42U, 34U, 26U, 18U, 10U, 2U,
18 60U, 52U, 44U, 36U, 28U, 20U, 12U, 4U,
19 62U, 54U, 46U, 38U, 30U, 22U, 14U, 6U,
20 64U, 56U, 48U, 40U, 32U, 24U, 16U, 8U,
21 57U, 49U, 41U, 33U, 25U, 17U, 9U, 1U,
22 59U, 51U, 43U, 35U, 27U, 19U, 11U, 3U,
23 61U, 53U, 45U, 37U, 29U, 21U, 13U, 5U,
24 63U, 55U, 47U, 39U, 31U, 23U, 15U, 7U
25};
26
27static const uint8_t FP_TABLE[64] = {
28 40U, 8U, 48U, 16U, 56U, 24U, 64U, 32U,
29 39U, 7U, 47U, 15U, 55U, 23U, 63U, 31U,
30 38U, 6U, 46U, 14U, 54U, 22U, 62U, 30U,
31 37U, 5U, 45U, 13U, 53U, 21U, 61U, 29U,
32 36U, 4U, 44U, 12U, 52U, 20U, 60U, 28U,
33 35U, 3U, 43U, 11U, 51U, 19U, 59U, 27U,
34 34U, 2U, 42U, 10U, 50U, 18U, 58U, 26U,
35 33U, 1U, 41U, 9U, 49U, 17U, 57U, 25U
36};
37
38static const uint8_t E_TABLE[48] = {
39 32U, 1U, 2U, 3U, 4U, 5U,
40 4U, 5U, 6U, 7U, 8U, 9U,
41 8U, 9U, 10U, 11U, 12U, 13U,
42 12U, 13U, 14U, 15U, 16U, 17U,
43 16U, 17U, 18U, 19U, 20U, 21U,
44 20U, 21U, 22U, 23U, 24U, 25U,
45 24U, 25U, 26U, 27U, 28U, 29U,
46 28U, 29U, 30U, 31U, 32U, 1U
47};
48
49static const uint8_t P_TABLE[32] = {
50 16U, 7U, 20U, 21U, 29U, 12U, 28U, 17U,
51 1U, 15U, 23U, 26U, 5U, 18U, 31U, 10U,
52 2U, 8U, 24U, 14U, 32U, 27U, 3U, 9U,
53 19U, 13U, 30U, 6U, 22U, 11U, 4U, 25U
54};
55
56static const uint8_t SBOX[8][4][16] = {
57 { /* S1 */
58 { 14U, 4U, 13U, 1U, 2U, 15U, 11U, 8U, 3U, 10U, 6U, 12U, 5U, 9U, 0U, 7U },
59 { 0U, 15U, 7U, 4U, 14U, 2U, 13U, 1U, 10U, 6U, 12U, 11U, 9U, 5U, 3U, 8U },
60 { 4U, 1U, 14U, 8U, 13U, 6U, 2U, 11U, 15U, 12U, 9U, 7U, 3U, 10U, 5U, 0U },
61 { 15U, 12U, 8U, 2U, 4U, 9U, 1U, 7U, 5U, 11U, 3U, 14U, 10U, 0U, 6U, 13U }
62 },
63 { /* S2 */
64 { 15U, 1U, 8U, 14U, 6U, 11U, 3U, 4U, 9U, 7U, 2U, 13U, 12U, 0U, 5U, 10U },
65 { 3U, 13U, 4U, 7U, 15U, 2U, 8U, 14U, 12U, 0U, 1U, 10U, 6U, 9U, 11U, 5U },
66 { 0U, 14U, 7U, 11U, 10U, 4U, 13U, 1U, 5U, 8U, 12U, 6U, 9U, 3U, 2U, 15U },
67 { 13U, 8U, 10U, 1U, 3U, 15U, 4U, 2U, 11U, 6U, 7U, 12U, 0U, 5U, 14U, 9U }
68 },
69 { /* S3 */
70 { 10U, 0U, 9U, 14U, 6U, 3U, 15U, 5U, 1U, 13U, 12U, 7U, 11U, 4U, 2U, 8U },
71 { 13U, 7U, 0U, 9U, 3U, 4U, 6U, 10U, 2U, 8U, 5U, 14U, 12U, 11U, 15U, 1U },
72 { 13U, 6U, 4U, 9U, 8U, 15U, 3U, 0U, 11U, 1U, 2U, 12U, 5U, 10U, 14U, 7U },
73 { 1U, 10U, 13U, 0U, 6U, 9U, 8U, 7U, 4U, 15U, 14U, 3U, 11U, 5U, 2U, 12U }
74 },
75 { /* S4 */
76 { 7U, 13U, 14U, 3U, 0U, 6U, 9U, 10U, 1U, 2U, 8U, 5U, 11U, 12U, 4U, 15U },
77 { 13U, 8U, 11U, 5U, 6U, 15U, 0U, 3U, 4U, 7U, 2U, 12U, 1U, 10U, 14U, 9U },
78 { 10U, 6U, 9U, 0U, 12U, 11U, 7U, 13U, 15U, 1U, 3U, 14U, 5U, 2U, 8U, 4U },
79 { 3U, 15U, 0U, 6U, 10U, 1U, 13U, 8U, 9U, 4U, 5U, 11U, 12U, 7U, 2U, 14U }
80 },
81 { /* S5 */
82 { 2U, 12U, 4U, 1U, 7U, 10U, 11U, 6U, 8U, 5U, 3U, 15U, 13U, 0U, 14U, 9U },
83 { 14U, 11U, 2U, 12U, 4U, 7U, 13U, 1U, 5U, 0U, 15U, 10U, 3U, 9U, 8U, 6U },
84 { 4U, 2U, 1U, 11U, 10U, 13U, 7U, 8U, 15U, 9U, 12U, 5U, 6U, 3U, 0U, 14U },
85 { 11U, 8U, 12U, 7U, 1U, 14U, 2U, 13U, 6U, 15U, 0U, 9U, 10U, 4U, 5U, 3U }
86 },
87 { /* S6 */
88 { 12U, 1U, 10U, 15U, 9U, 2U, 6U, 8U, 0U, 13U, 3U, 4U, 14U, 7U, 5U, 11U },
89 { 10U, 15U, 4U, 2U, 7U, 12U, 9U, 5U, 6U, 1U, 13U, 14U, 0U, 11U, 3U, 8U },
90 { 9U, 14U, 15U, 5U, 2U, 8U, 12U, 3U, 7U, 0U, 4U, 10U, 1U, 13U, 11U, 6U },
91 { 4U, 3U, 2U, 12U, 9U, 5U, 15U, 10U, 11U, 14U, 1U, 7U, 6U, 0U, 8U, 13U }
92 },
93 { /* S7 */
94 { 4U, 11U, 2U, 14U, 15U, 0U, 8U, 13U, 3U, 12U, 9U, 7U, 5U, 10U, 6U, 1U },
95 { 13U, 0U, 11U, 7U, 4U, 9U, 1U, 10U, 14U, 3U, 5U, 12U, 2U, 15U, 8U, 6U },
96 { 1U, 4U, 11U, 13U, 12U, 3U, 7U, 14U, 10U, 15U, 6U, 8U, 0U, 5U, 9U, 2U },
97 { 6U, 11U, 13U, 8U, 1U, 4U, 10U, 7U, 9U, 5U, 0U, 15U, 14U, 2U, 3U, 12U }
98 },
99 { /* S8 */
100 { 13U, 2U, 8U, 4U, 6U, 15U, 11U, 1U, 10U, 9U, 3U, 14U, 5U, 0U, 12U, 7U },
101 { 1U, 15U, 13U, 8U, 10U, 3U, 7U, 4U, 12U, 5U, 6U, 11U, 0U, 14U, 9U, 2U },
102 { 7U, 11U, 4U, 1U, 9U, 12U, 14U, 2U, 0U, 6U, 10U, 13U, 15U, 3U, 5U, 8U },
103 { 2U, 1U, 14U, 7U, 4U, 10U, 8U, 13U, 15U, 12U, 9U, 0U, 3U, 5U, 6U, 11U }
104 }
105};
106
107static const uint8_t PC1_TABLE[56] = {
108 57U, 49U, 41U, 33U, 25U, 17U, 9U,
109 1U, 58U, 50U, 42U, 34U, 26U, 18U,
110 10U, 2U, 59U, 51U, 43U, 35U, 27U,
111 19U, 11U, 3U, 60U, 52U, 44U, 36U,
112 63U, 55U, 47U, 39U, 31U, 23U, 15U,
113 7U, 62U, 54U, 46U, 38U, 30U, 22U,
114 14U, 6U, 61U, 53U, 45U, 37U, 29U,
115 21U, 13U, 5U, 28U, 20U, 12U, 4U
116};
117
118static const uint8_t PC2_TABLE[48] = {
119 14U, 17U, 11U, 24U, 1U, 5U,
120 3U, 28U, 15U, 6U, 21U, 10U,
121 23U, 19U, 12U, 4U, 26U, 8U,
122 16U, 7U, 27U, 20U, 13U, 2U,
123 41U, 52U, 31U, 37U, 47U, 55U,
124 30U, 40U, 51U, 45U, 33U, 48U,
125 44U, 49U, 39U, 56U, 34U, 53U,
126 46U, 42U, 50U, 36U, 29U, 32U
127};
128
129static const uint8_t ROTATE_TABLE[16] = {
130 1U, 1U, 2U, 2U, 2U, 2U, 2U, 2U, 1U, 2U, 2U, 2U, 2U, 2U, 2U, 1U
131};
132
133typedef struct
134{
135 uint64_t subkeys[16];
137
138static uint64_t des_permute(uint64_t in, const uint8_t *table, uint8_t n, uint8_t in_bits)
139{
140 uint64_t out = 0ULL;
141 for (uint8_t i = 0U; i < n; i++)
142 {
143 uint8_t bit_pos = (uint8_t)(in_bits - table[i]);
144 if (((in >> bit_pos) & 1ULL) != 0ULL)
145 {
146 out |= (1ULL << (n - 1U - i));
147 }
148 }
149 return out;
150}
151
152static void des_schedule(const uint8_t key[8], des_key_schedule_t *ks)
153{
154 uint64_t k = 0ULL;
155 for (size_t i = 0U; i < 8U; i++)
156 {
157 k = (k << 8U) | (uint64_t)key[i];
158 }
159 uint64_t pc1 = des_permute(k, PC1_TABLE, 56U, 64U);
160 uint32_t c = (uint32_t)((pc1 >> 28U) & 0x0FFFFFFFU);
161 uint32_t d = (uint32_t)(pc1 & 0x0FFFFFFFU);
162
163 for (size_t r = 0U; r < 16U; r++)
164 {
165 uint8_t shift = ROTATE_TABLE[r];
166 c = (((c << shift) | (c >> (28U - shift)))) & 0x0FFFFFFFU;
167 d = (((d << shift) | (d >> (28U - shift)))) & 0x0FFFFFFFU;
168 uint64_t cd = ((uint64_t)c << 28U) | (uint64_t)d;
169 ks->subkeys[r] = des_permute(cd, PC2_TABLE, 48U, 56U);
170 }
171}
172
173static uint64_t des_cipher_core(uint64_t in_block, const des_key_schedule_t *ks, int decrypt)
174{
175 uint64_t ip = des_permute(in_block, IP_TABLE, 64U, 64U);
176 uint32_t l = (uint32_t)(ip >> 32U);
177 uint32_t r = (uint32_t)(ip & 0xFFFFFFFFU);
178
179 for (size_t round = 0U; round < 16U; round++)
180 {
181 size_t r_idx = (decrypt != 0) ? (15U - round) : round;
182 uint64_t er = des_permute((uint64_t)r, E_TABLE, 48U, 32U);
183 uint64_t b = er ^ ks->subkeys[r_idx];
184
185 uint32_t s_out = 0U;
186 for (size_t s = 0U; s < 8U; s++)
187 {
188 uint8_t chunk = (uint8_t)((b >> (42U - s * 6U)) & 0x3FU);
189 uint8_t row = (uint8_t)(((chunk & 0x20U) >> 4U) | (chunk & 0x01U));
190 uint8_t col = (uint8_t)((chunk >> 1U) & 0x0FU);
191 uint8_t val = SBOX[s][row][col];
192 s_out = (s_out << 4U) | (uint32_t)val;
193 }
194
195 uint32_t f_res = (uint32_t)des_permute((uint64_t)s_out, P_TABLE, 32U, 32U);
196 uint32_t new_r = l ^ f_res;
197 l = r;
198 r = new_r;
199 }
200
201 uint64_t pre_fp = ((uint64_t)r << 32U) | (uint64_t)l;
202 return des_permute(pre_fp, FP_TABLE, 64U, 64U);
203}
204
205static uint64_t des_bytes_to_u64(const uint8_t b[8])
206{
207 uint64_t v = 0ULL;
208 for (size_t i = 0U; i < 8U; i++)
209 {
210 v = (v << 8U) | (uint64_t)b[i];
211 }
212 return v;
213}
214
215static void des_u64_to_bytes(uint64_t v, uint8_t b[8])
216{
217 for (size_t i = 8U; i > 0U; i--)
218 {
219 b[i - 1U] = (uint8_t)(v & 0xFFU);
220 v >>= 8U;
221 }
222}
223
227static void des_ecb(const uint8_t key[8], const uint8_t in[8], int decrypt, uint8_t out[8])
228{
230 des_schedule(key, &ks);
231 uint64_t b = des_cipher_core(des_bytes_to_u64(in), &ks, decrypt);
232 des_u64_to_bytes(b, out);
233}
234
240static void des3_algo3(const uint8_t key24[24], const uint8_t in[8], uint8_t out[8])
241{
242 uint8_t t1[8];
243 uint8_t t2[8];
244 des_ecb(&key24[0], in, 0, t1); /* Encrypt K1 */
245 des_ecb(&key24[8], t1, 1, t2); /* Decrypt K2 */
246 des_ecb(&key24[16], t2, 0, out); /* Encrypt K3 */
247}
248
253static void des3_algo3_reversed(const uint8_t key24[24], const uint8_t in[8], uint8_t out[8])
254{
255 uint8_t t1[8];
256 uint8_t t2[8];
257 des_ecb(&key24[16], in, 0, t1); /* Encrypt K3 */
258 des_ecb(&key24[8], t1, 1, t2); /* Decrypt K2 */
259 des_ecb(&key24[0], t2, 0, out); /* Encrypt K1 */
260}
261
262/* ========================================================================= */
263/* Public API */
264/* ========================================================================= */
265
266/* REQ-SC-MAC-003: Derive KSMAC from KMAC and RA/RB (Subset-037-2 §6.2.3.2.3.13-16) */
268 const uint8_t ra[SAFECOMM_MAC_RAND_LEN],
269 const uint8_t rb[SAFECOMM_MAC_RAND_LEN],
270 safecomm_mac_ctx_t *out_ctx)
271{
272 if ((kmac == NULL) || (ra == NULL) || (rb == NULL) || (out_ctx == NULL))
273 {
275 }
276
277 (void)memset(out_ctx, 0, sizeof(*out_ctx));
279
280 /*
281 * RA = RA_L | RA_R, RB = RB_L | RB_R (32-bit halves each).
282 * KS1 = E(K3,D(K2,E(K1, RA_L|RB_L))) KS2 = E(K3,D(K2,E(K1, RA_R|RB_R)))
283 * KS3 = E(K1,D(K2,E(K3, RA_L|RB_L))) (reversed key order - "K'AB")
284 */
285 uint8_t block_l[8];
286 uint8_t block_r[8];
287 (void)memcpy(&block_l[0], &ra[0], 4U);
288 (void)memcpy(&block_l[4], &rb[0], 4U);
289 (void)memcpy(&block_r[0], &ra[4], 4U);
290 (void)memcpy(&block_r[4], &rb[4], 4U);
291
292 des3_algo3(kmac, block_l, &out_ctx->ksmac[0]);
293 des3_algo3(kmac, block_r, &out_ctx->ksmac[8]);
294 des3_algo3_reversed(kmac, block_l, &out_ctx->ksmac[16]);
295
296 return SAFECOMM_STATUS_OK;
297}
298
299/* REQ-SC-MAC-005: Compute the CBC-MAC (Subset-037-2 §6.2.2) */
301 const uint8_t *msg, size_t msg_len,
302 uint8_t out_mac[SAFECOMM_MAC_LEN])
303{
304 if ((ctx == NULL) || (out_mac == NULL) || ((msg == NULL) && (msg_len > 0U)))
305 {
307 }
308
310 {
312 }
313
314 /*
315 * ISO/IEC 9797-1 Padding Method 1: as few zero bits as needed (possibly none) to
316 * reach a multiple of 64 bits. Unlike PKCS#7, a message already a multiple of 8
317 * bytes gets NO extra block. An empty message still needs one (all-zero) block
318 * for the algorithm to produce an output.
319 */
320 size_t padded_len = ((msg_len % 8U) == 0U) ? msg_len : (msg_len + (8U - (msg_len % 8U)));
321 if (padded_len == 0U)
322 {
323 padded_len = 8U;
324 }
325 size_t nblocks = padded_len / 8U;
326
327 uint8_t state[8];
328 (void)memset(state, 0, sizeof(state)); /* H0 = 0, not enciphered before first use */
329
330 for (size_t blk = 0U; blk < nblocks; blk++)
331 {
332 uint8_t xi[8];
333 for (size_t i = 0U; i < 8U; i++)
334 {
335 size_t idx = (blk * 8U) + i;
336 xi[i] = (idx < msg_len) ? msg[idx] : 0U; /* zero padding past msg_len */
337 state[i] ^= xi[i];
338 }
339 if (blk < (nblocks - 1U))
340 {
341 uint8_t enc[8];
342 des_ecb(&ctx->ksmac[0], state, 0, enc); /* Hi = E(K1, Hi-1 xor Xi) */
343 (void)memcpy(state, enc, 8U);
344 }
345 else
346 {
347 des3_algo3(ctx->ksmac, state, out_mac); /* Hq = E(K3,D(K2,E(K1, Hq-1 xor Xq))) */
348 }
349 }
350
351 return SAFECOMM_STATUS_OK;
352}
353
354/* REQ-SC-MAC-006: Constant-time MAC verify */
356 const uint8_t *msg, size_t msg_len,
357 const uint8_t rx_mac[SAFECOMM_MAC_LEN])
358{
359 if ((ctx == NULL) || (rx_mac == NULL) || ((msg == NULL) && (msg_len > 0U)))
360 {
362 }
363
364 uint8_t expected_mac[SAFECOMM_MAC_LEN];
365 safecomm_status_t st = safecomm_mac_compute(ctx, msg, msg_len, expected_mac);
366 if (st != SAFECOMM_STATUS_OK)
367 {
368 return st;
369 }
370
371 /* Constant-time comparison: never short-circuit */
372 uint8_t diff = 0U;
373 for (size_t i = 0U; i < SAFECOMM_MAC_LEN; i++)
374 {
375 diff |= (uint8_t)(expected_mac[i] ^ rx_mac[i]);
376 }
377
378 return (diff == 0U) ? SAFECOMM_STATUS_OK : SAFECOMM_STATUS_MAC_MISMATCH;
379}
static void des3_algo3_reversed(const uint8_t key24[24], const uint8_t in[8], uint8_t out[8])
The same construction with K1 and K3 swapped: E(K1, D(K2, E(K3, block))). Used only for KS3 of the KS...
Definition mac.c:253
static void des_ecb(const uint8_t key[8], const uint8_t in[8], int decrypt, uint8_t out[8])
Single DES encrypt/decrypt of one 8-byte block under one 8-byte key.
Definition mac.c:227
static void des3_algo3(const uint8_t key24[24], const uint8_t in[8], uint8_t out[8])
Subset-037-2 §6.2.2.1 "single DES with modified MAC algorithm 3": E(K3, D(K2, E(K1,...
Definition mac.c:240
Message Authentication Code and session-key derivation (ERTMS/ETCS Subset-037-2 §6....
#define SAFECOMM_MAC_LEN
Definition mac.h:36
#define SAFECOMM_MAC_RAND_LEN
Definition mac.h:38
#define SAFECOMM_MAC_KEY_LEN
Definition mac.h:34
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.
Definition mac.c:267
@ SAFECOMM_MAC_ALG_3DES_SS037
Definition mac.h:43
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.
Definition mac.c:300
safecomm_status_t safecomm_mac_verify(const safecomm_mac_ctx_t *ctx, const uint8_t *msg, size_t msg_len, const uint8_t rx_mac[SAFECOMM_MAC_LEN])
Constant-time verify of a received MAC against a freshly computed one.
Definition mac.c:355
safecomm_status_t
Result of a Safe Communication Framework operation.
Definition status.h:17
@ SAFECOMM_STATUS_INVALID_PARAM
Definition status.h:19
@ SAFECOMM_STATUS_MAC_MISMATCH
Definition status.h:25
@ SAFECOMM_STATUS_NOT_INITIALISED
Definition status.h:20
@ SAFECOMM_STATUS_OK
Definition status.h:18
Immutable session-key material for one SaF connection.
Definition mac.h:48
uint8_t ksmac[SAFECOMM_MAC_KEY_LEN]
Definition mac.h:49
safecomm_mac_alg_t alg
Definition mac.h:50