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