Arduino_CAN
로딩중...
검색중...
일치하는것 없음
minimac.cpp
이 파일의 문서화 페이지로 가기
1
5
6#include "minimac.h"
7
9static const int SIG_ADDR = 0;
10static const uint32_t SIGVAL = 0xAA55AA55;
11static const int DATA_ADDR = SIG_ADDR + sizeof(SIGVAL);
12
14static uint16_t mm_id;
15static uint8_t mm_key[MINIMAC_KEY_LEN];
16static uint64_t mm_counter;
18static uint8_t mm_hist_cnt;
19
25static void debug_print_hex(const uint8_t *buf, uint16_t len) {
26 for (uint16_t i = 0; i < len; i++) {
27 if (buf[i] < 0x10)
28 Serial.print('0');
29 Serial.print(buf[i], HEX);
30 Serial.print(' ');
31 }
32 Serial.println();
33}
34
39static void print_u64(uint64_t v) {
40 if (v == 0) {
41 Serial.print('0');
42 return;
43 }
44 char buf[21]; // 최대 20자리 숫자 + 널 종료
45 buf[20] = '\0';
46 int pos = 19;
47 while (v > 0 && pos >= 0) {
48 buf[pos--] = '0' + (v % 10);
49 v /= 10;
50 }
51 Serial.print(&buf[pos + 1]);
52}
53
65static void compute_digest(const uint8_t *data, uint8_t len,
66 unsigned char digest[16]) {
67 /* (1) 버퍼 크기 계산:
68 * - 메시지 카운터(mm_counter, 8바이트)
69 * - CAN ID(mm_id, 2바이트)
70 * - 과거 메시지 히스토리(mm_hist_cnt개, 각 항목 len 바이트)
71 * - 현재 페이로드(data, len 바이트)
72 * 위 항목의 총합(buf_len)을 계산한 뒤, 이를 저장할 버퍼(buf)를 동적 할당.
73 * off 변수는 buf 내 현재 쓰기 위치를 나타냄.
74 */
75 uint16_t buf_len = 8 + 2;
76 for (uint8_t i = 0; i < mm_hist_cnt; i++)
77 buf_len += mm_hist[i].len;
78 buf_len += len;
79
80 uint8_t *buf = (uint8_t *)malloc(buf_len);
81 uint16_t off = 0;
82
83 /* (2) 카운터 삽입 (big-endian):
84 * - 64비트 카운터를 빅엔디안 순서로 buf[off..off+7]에 저장
85 * - Serial.print로 현재 카운터 값을 10진수 문자열로 출력
86 */
87 Serial.print("[DBG] counter = ");
89 Serial.println();
90
91 uint64_t tmp = mm_counter;
92 for (int i = 7; i >= 0; i--) {
93 buf[off + i] = tmp & 0xFF;
94 tmp >>= 8;
95 }
96 off += 8;
97
98 /* (3) CAN ID 삽입:
99 * - mm_id 상위 바이트(buf[off])와 하위 바이트(buf[off+1])를 저장
100 * - Serial.print로 16진수 형태의 CAN ID 출력
101 */
102 buf[off++] = mm_id >> 8;
103 buf[off++] = mm_id & 0xFF;
104 Serial.print("[DBG] CAN ID = 0x");
105 Serial.println(mm_id, HEX);
106
107 /* (4) 메시지 히스토리 삽입:
108 * - 저장된 히스토리 개수(mm_hist_cnt)만큼 반복
109 * - 각 항목(mm_hist[i].data, length mm_hist[i].len)을 buf에 복사
110 * - debug_print_hex로 각 히스토리 데이터 덤프
111 */
112 Serial.print("[DBG] history_count = ");
113 Serial.println(mm_hist_cnt);
114
115 for (uint8_t i = 0; i < mm_hist_cnt; i++) {
116 Serial.print("[DBG] hist[");
117 Serial.print(i);
118 Serial.print("] = ");
119 debug_print_hex(mm_hist[i].data, mm_hist[i].len);
120
121 memcpy(buf + off, mm_hist[i].data, mm_hist[i].len);
122 off += mm_hist[i].len;
123 }
124
125 /* (5) 현재 페이로드 삽입:
126 * - data[0..len-1]를 buf에 연속 복사
127 * - debug_print_hex로 페이로드 덤프
128 */
129 Serial.print("[DBG] current_data = ");
130 debug_print_hex(data, len);
131
132 memcpy(buf + off, data, len);
133 off += len;
134
135 /* (6) HMAC-MD5 계산:
136 * - MD5.hmac_md5(buf, off, mm_key, MINIMAC_KEY_LEN, digest)를 호출하여
137 * 전체 입력(buf, 길이 off)에 대한 HMAC-MD5 다이제스트 생성
138 * - debug_print_hex로 16바이트 raw MD5 덤프
139 * - 동적 할당된 buf 메모리 해제
140 */
141 MD5 hasher;
142 hasher.hmac_md5(buf, off, (void *)mm_key, MINIMAC_KEY_LEN, digest);
143
144 Serial.print("[DBG] raw MD5 = ");
145 debug_print_hex(digest, 16);
146
147 free(buf);
148}
149
159static bool load_state(void) {
160 uint32_t sig;
161
162 /* (1) 시그니처 확인 */
163 EEPROM.get(SIG_ADDR, sig);
164 if (sig != SIGVAL)
165 return false;
166
167 /* (2) 카운터 및 히스토리 개수 복원 */
168 EEPROM.get(DATA_ADDR, mm_counter);
169 EEPROM.get(DATA_ADDR + sizeof(mm_counter), mm_hist_cnt);
170
171 /* (3) 히스토리 항목 복원 */
172 int addr = DATA_ADDR + sizeof(mm_counter) + sizeof(mm_hist_cnt);
173 for (uint8_t i = 0; i < mm_hist_cnt; i++) {
174 /* (3a) 각 히스토리 길이 로드 */
175 EEPROM.get(addr, mm_hist[i].len);
176 addr += sizeof(mm_hist[i].len);
177
178 /* (3b) 고정 크기 버퍼에 과거 페이로드 데이터 로드 */
179 EEPROM.get(addr, mm_hist[i].data);
180 addr += MINIMAC_MAX_DATA;
181 }
182
183 /* (4) 디버그 출력으로 복원된 상태 확인 */
184 Serial.println("[DBG] load_state: loaded from EEPROM");
185 Serial.print(" counter = ");
187 Serial.println();
188 Serial.print(" history_count = ");
189 Serial.println(mm_hist_cnt);
190
191 return true;
192}
193
200static void save_state(void) {
201 /* (1) 시그니처 기록 */
202 EEPROM.put(SIG_ADDR, SIGVAL);
203
204 /* (2) 카운터 및 히스토리 개수 기록 */
205 EEPROM.put(DATA_ADDR, mm_counter);
206 EEPROM.put(DATA_ADDR + sizeof(mm_counter), mm_hist_cnt);
207
208 /* (3) 히스토리 항목 기록 */
209 int addr = DATA_ADDR + sizeof(mm_counter) + sizeof(mm_hist_cnt);
210 for (uint8_t i = 0; i < mm_hist_cnt; i++) {
211 /* (3a) 각 히스토리 길이 저장 */
212 EEPROM.put(addr, mm_hist[i].len);
213 addr += sizeof(mm_hist[i].len);
214
215 /* (3b) 고정 크기 버퍼에 과거 페이로드 데이터 저장 */
216 EEPROM.put(addr, mm_hist[i].data);
217 addr += MINIMAC_MAX_DATA;
218 }
219
220 /* (4) 디버그 출력으로 저장된 상태 확인 */
221 Serial.println("[DBG] save_state: saved to EEPROM");
222 Serial.print(" counter = ");
224 Serial.println();
225 Serial.print(" history_count = ");
226 Serial.println(mm_hist_cnt);
227}
228
241void minimac_init(uint16_t can_id, const uint8_t *key) {
242 /* Serial 초기화: 디버그 출력용 */
243 Serial.begin(115200);
244 while (!Serial)
245 /* 시리얼 포트가 준비될 때까지 대기 */;
246 Serial.println("[DBG] minimac_init()");
247
248 /* (1) CAN ID 설정: 보호할 그룹 식별자 */
249 mm_id = can_id;
250
251 /* (2) 그룹 키 복사: 16바이트 비밀키 */
252 memcpy(mm_key, key, MINIMAC_KEY_LEN);
253
254 /* (3) EEPROM에서 이전 상태 불러오기 */
255 if (!load_state()) {
256 /* EEPROM에 유효한 시그니처 없음: fresh 초기화 */
257 Serial.println("[DBG] minimac_init: no EEPROM state, initialize fresh");
258
259 /* (3a) 카운터 초기화 */
260 mm_counter = 0;
261
262 /* (3b) 히스토리 개수 초기화 */
263 mm_hist_cnt = 0;
264
265 /* (3c) 초기 상태 EEPROM에 저장 */
266 save_state();
267 }
268}
269
282uint8_t minimac_sign(uint8_t *data, uint8_t payload_len) {
283 /* 디버그: 함수 진입 */
284 Serial.println("[DBG] minimac_sign()");
285
286 /* (1) HMAC 입력 구성 및 다이제스트 계산 */
287 unsigned char digest[16];
288 compute_digest(data, payload_len, digest);
289
290 /* (2) 디버그: 생성된 다이제스트의 태그 부분 출력 */
291 Serial.print("[DBG] sign: tag = ");
293
294 /* (3) 태그(4바이트) 붙이기 */
295 memcpy(data + payload_len, digest, MINIMAC_TAG_LEN);
296 uint8_t total = payload_len + MINIMAC_TAG_LEN;
297
298 /* (4) 메시지 히스토리 순환 버퍼 관리 */
300 Serial.println("[DBG] sign: history full, dropping oldest");
301 /* 가장 오래된 히스토리 항목 삭제 */
302 for (uint8_t i = 1; i < mm_hist_cnt; i++)
303 mm_hist[i - 1] = mm_hist[i];
304 mm_hist_cnt--;
305 }
306 /* 새로운 페이로드를 히스토리에 추가 */
307 mm_hist[mm_hist_cnt].len = payload_len;
308 memcpy(mm_hist[mm_hist_cnt].data, data, payload_len);
309 mm_hist_cnt++;
310 Serial.print("[DBG] sign: new history_count = ");
311 Serial.println(mm_hist_cnt);
312
313 /* (5) 카운터 증가 및 디버그 출력 */
314 mm_counter++;
315 Serial.print("[DBG] sign: new counter = ");
317 Serial.println();
318
319 /* (6) EEPROM에 상태 저장 */
320 save_state();
321
322 return total;
323}
324
338bool minimac_verify(const uint8_t *data, uint8_t payload_len,
339 const uint8_t *tag) {
340 /* 디버그: 함수 진입 */
341 Serial.println("[DBG] minimac_verify()");
342
343 /* (1) HMAC 입력 구성 및 다이제스트 재계산 */
344 unsigned char digest[16];
345 compute_digest(data, payload_len, digest);
346
347 /* (2) 디버그: 기대 태그(expected) 및 수신 태그(received) 출력 */
348 Serial.print("[DBG] verify: expected tag = ");
350 Serial.print("[DBG] verify: recv tag = ");
352
353 /* (3) 태그 비교: 불일치 시 실패 처리 */
354 if (memcmp(digest, tag, MINIMAC_TAG_LEN) != 0) {
355 Serial.println("[DBG] verify: FAILED");
356 return false;
357 }
358
359 /* (4) 히스토리 순환 버퍼 관리 (가득 찼다면 가장 오래된 항목 삭제) */
361 Serial.println("[DBG] verify: history full, dropping oldest");
362 for (uint8_t i = 1; i < mm_hist_cnt; i++)
363 mm_hist[i - 1] = mm_hist[i];
364 mm_hist_cnt--;
365 }
366
367 /* (5) 성공 페이로드를 히스토리에 추가 */
368 mm_hist[mm_hist_cnt].len = payload_len;
369 memcpy(mm_hist[mm_hist_cnt].data, data, payload_len);
370 mm_hist_cnt++;
371 Serial.print("[DBG] verify: new history_count = ");
372 Serial.println(mm_hist_cnt);
373
374 /* (6) 카운터 증가 및 디버그 출력 */
375 mm_counter++;
376 Serial.print("[DBG] verify: new counter = ");
378 Serial.println();
379
380 /* (7) EEPROM에 상태 저장 */
381 save_state();
382
383 Serial.println("[DBG] verify: SUCCESS");
384 return true;
385}
uint8_t minimac_sign(uint8_t *data, uint8_t payload_len)
송신할 메시지에 Mini-MAC 태그 생성 및 내부 상태 갱신
Definition minimac.cpp:282
static void save_state(void)
Mini-MAC 상태를 EEPROM에 저장
Definition minimac.cpp:200
static uint64_t mm_counter
64비트 메시지 카운터
Definition minimac.cpp:16
static bool load_state(void)
EEPROM에서 Mini-MAC 상태 불러오기
Definition minimac.cpp:159
static MiniMacHist mm_hist[5]
최근 λ개 메시지 히스토리
Definition minimac.cpp:17
bool minimac_verify(const uint8_t *data, uint8_t payload_len, const uint8_t *tag)
수신된 메시지의 Mini-MAC 태그 검증 및 상태 동기화
Definition minimac.cpp:338
static uint8_t mm_hist_cnt
히스토리 항목 수 (≤ λ)
Definition minimac.cpp:18
static const int DATA_ADDR
Definition minimac.cpp:11
static uint16_t mm_id
보호할 CAN ID, 그룹 키, 카운터, 메시지 히스토리
Definition minimac.cpp:14
static const uint32_t SIGVAL
Definition minimac.cpp:10
static const int SIG_ADDR
EEPROM 레이아웃: 시그니처 및 데이터 저장 시작 주소
Definition minimac.cpp:9
void minimac_init(uint16_t can_id, const uint8_t *key)
Mini-MAC 초기화 및 EEPROM 동기화
Definition minimac.cpp:241
static uint8_t mm_key[16]
공유 그룹 키
Definition minimac.cpp:15
static void compute_digest(const uint8_t *data, uint8_t len, unsigned char digest[16])
Mini-MAC용 HMAC-MD5 다이제스트 계산
Definition minimac.cpp:65
Mini-MAC 프로토콜 구현용 헤더 파일
#define MINIMAC_HIST_LEN
메시지 히스토리 최대 개수 (λ = 5)
Definition minimac.h:30
#define MINIMAC_TAG_LEN
Mini-MAC 다이제스트에서 사용할 태그 길이 (4바이트, 32비트)
Definition minimac.h:25
#define MINIMAC_MAX_DATA
CAN 데이터 필드 최대 길이 (8바이트)
Definition minimac.h:35
#define MINIMAC_KEY_LEN
Mini-MAC HMAC 키 길이 (16바이트, 128비트)
Definition minimac.h:20
static bool load_state(void)
EEPROM에서 Mini-MAC 상태 불러오기
Definition minimac.cpp:160
static void debug_print_hex(const uint8_t *buf, uint16_t len)
디버깅용: 바이트 배열을 16진수로 출력
Definition minimac.cpp:25
static void print_u64(uint64_t v)
디버깅용: 64비트 부호 없는 정수를 10진수 문자열로 변환해 출력
Definition minimac.cpp:39
과거 페이로드를 저장하기 위한 구조체
Definition minimac.h:44