Arduino_CAN
로딩중...
검색중...
일치하는것 없음
minimac.cpp 파일 참조

Mini-MAC 프로토콜 구현 (EEPROM 상태 관리, HMAC-MD5 기반 MAC 생성/검증) 더 자세히 ...

#include "minimac.h"
minimac.cpp에 대한 include 의존 그래프

이 파일의 소스 코드 페이지로 가기

함수

static void debug_print_hex (const uint8_t *buf, uint16_t len)
 디버깅용: 바이트 배열을 16진수로 출력
 
static void print_u64 (uint64_t v)
 디버깅용: 64비트 부호 없는 정수를 10진수 문자열로 변환해 출력
 
static void compute_digest (const uint8_t *data, uint8_t len, unsigned char digest[16])
 Mini-MAC용 HMAC-MD5 다이제스트 계산
 
static bool load_state (void)
 EEPROM에서 Mini-MAC 상태 불러오기
 
static void save_state (void)
 Mini-MAC 상태를 EEPROM에 저장
 
void minimac_init (uint16_t can_id, const uint8_t *key)
 Mini-MAC 초기화 및 EEPROM 동기화
 
uint8_t minimac_sign (uint8_t *data, uint8_t payload_len)
 송신할 메시지에 Mini-MAC 태그 생성 및 내부 상태 갱신
 
bool minimac_verify (const uint8_t *data, uint8_t payload_len, const uint8_t *tag)
 수신된 메시지의 Mini-MAC 태그 검증 및 상태 동기화
 

변수

static const int SIG_ADDR = 0
 EEPROM 레이아웃: 시그니처 및 데이터 저장 시작 주소
 
static const uint32_t SIGVAL = 0xAA55AA55
 
static const int DATA_ADDR = SIG_ADDR + sizeof(SIGVAL)
 
static uint16_t mm_id
 보호할 CAN ID, 그룹 키, 카운터, 메시지 히스토리
 
static uint8_t mm_key [16]
 공유 그룹 키
 
static uint64_t mm_counter
 64비트 메시지 카운터
 
static MiniMacHist mm_hist [5]
 최근 λ개 메시지 히스토리
 
static uint8_t mm_hist_cnt
 히스토리 항목 수 (≤ λ)
 

상세한 설명

Mini-MAC 프로토콜 구현 (EEPROM 상태 관리, HMAC-MD5 기반 MAC 생성/검증)

minimac.cpp 파일에서 정의되었습니다.

함수 문서화

◆ compute_digest()

static void compute_digest ( const uint8_t * data,
uint8_t len,
unsigned char digest[16] )
static

Mini-MAC용 HMAC-MD5 다이제스트 계산

매개변수
data서명할 페이로드 데이터 버퍼
len페이로드 길이(Byte)
digest결과 다이제스트 저장 버퍼(16바이트)

메시지 카운터(mm_counter), CAN ID(mm_id), 최근 메시지 히스토리(mm_hist), 그리고 현재 페이로드(data)를 하나의 연속 버퍼에 결합한 후, HMAC-MD5를 수행하여 16바이트 다이제스트를 생성한다. 각 단계별 내부 상태는 Serial 디버그 출력으로 확인 가능하다.

minimac.cpp 파일의 65 번째 라인에서 정의되었습니다.

66 {
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}
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 uint16_t mm_id
보호할 CAN ID, 그룹 키, 카운터, 메시지 히스토리
Definition minimac.cpp:14
static uint8_t mm_key[16]
공유 그룹 키
Definition minimac.cpp:15
#define MINIMAC_KEY_LEN
Mini-MAC HMAC 키 길이 (16바이트, 128비트)
Definition minimac.h:20
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
이 함수 내부에서 호출하는 함수들에 대한 그래프입니다.:
이 함수를 호출하는 함수들에 대한 그래프입니다.:

◆ debug_print_hex()

static void debug_print_hex ( const uint8_t * buf,
uint16_t len )
static

디버깅용: 바이트 배열을 16진수로 출력

매개변수
buf출력할 바이트 배열
len배열 길이(Byte)

minimac.cpp 파일의 25 번째 라인에서 정의되었습니다.

25 {
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}

◆ load_state()

static bool load_state ( void )
static

EEPROM에서 Mini-MAC 상태 불러오기

EEPROM에 저장된 시그니처(SIGVAL)를 확인한 뒤, 유효하면 mm_counter, mm_hist_cnt 및 메시지 히스토리 배열을 복원한다.

반환값
true EEPROM에 유효한 상태가 있어 복원 성공
false 시그니처 불일치로 초기화가 필요함

minimac.cpp 파일의 159 번째 라인에서 정의되었습니다.

159 {
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}
static const int DATA_ADDR
Definition minimac.cpp:11
static const uint32_t SIGVAL
Definition minimac.cpp:10
static const int SIG_ADDR
EEPROM 레이아웃: 시그니처 및 데이터 저장 시작 주소
Definition minimac.cpp:9
#define MINIMAC_MAX_DATA
CAN 데이터 필드 최대 길이 (8바이트)
Definition minimac.h:35
이 함수 내부에서 호출하는 함수들에 대한 그래프입니다.:

◆ minimac_init()

void minimac_init ( uint16_t can_id,
const uint8_t * key )

Mini-MAC 초기화 및 EEPROM 동기화

Mini-MAC 프로토콜 초기화

매개변수
can_id보호할 CAN 메시지 식별자 (16비트)
keyMini-MAC HMAC 키 (128비트, 16바이트)

Serial 포트를 115200bps로 초기화하고, mm_id와 mm_key 전역 변수에 인자를 설정한다. EEPROM에서 이전에 저장된 mm_counter와 메시지 히스토리를 불러오되(load_state()), 저장된 시그니처가 없으면 fresh 상태로 간주하여 mm_counter와 mm_hist_cnt를 0으로 초기화한 뒤(save_state()), EEPROM에 초기 상태를 기록한다. 디버그용으로 Serial.print를 통해 초기화 과정을 출력한다.

minimac.cpp 파일의 241 번째 라인에서 정의되었습니다.

241 {
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}
static void save_state(void)
Mini-MAC 상태를 EEPROM에 저장
Definition minimac.cpp:200
static bool load_state(void)
EEPROM에서 Mini-MAC 상태 불러오기
Definition minimac.cpp:160
이 함수 내부에서 호출하는 함수들에 대한 그래프입니다.:
이 함수를 호출하는 함수들에 대한 그래프입니다.:

◆ minimac_sign()

uint8_t minimac_sign ( uint8_t * data,
uint8_t payload_len )

송신할 메시지에 Mini-MAC 태그 생성 및 내부 상태 갱신

송신 전 페이로드에 Mini-MAC 태그 생성 및 붙이기

매개변수
data서명할 페이로드 버퍼, 호출 후 buf[payload_len..] 위치에 태그가 덧붙여짐
payload_len페이로드 길이(Byte)
반환값
전체 전송 길이 (payload_len + MINIMAC_TAG_LEN)

전달받은 페이로드(data, payload_len)를 바탕으로 HMAC-MD5 다이제스트를 계산하여 상위 4바이트(tag)를 data 뒤에 덧붙인다. 이후 메시지 히스토리(mm_hist)와 메시지 카운터(mm_counter)를 갱신하고 EEPROM에 저장(save_state)한다.

minimac.cpp 파일의 282 번째 라인에서 정의되었습니다.

282 {
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}
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
이 함수 내부에서 호출하는 함수들에 대한 그래프입니다.:
이 함수를 호출하는 함수들에 대한 그래프입니다.:

◆ minimac_verify()

bool minimac_verify ( const uint8_t * data,
uint8_t payload_len,
const uint8_t * tag )

수신된 메시지의 Mini-MAC 태그 검증 및 상태 동기화

수신 후 Mini-MAC 태그 검증 및 내부 상태 갱신

매개변수
data검증할 페이로드 버퍼
payload_len페이로드 길이(Byte)
tag수신된 태그 버퍼 (MINIMAC_TAG_LEN 바이트)
반환값
true 검증 성공 및 내부 상태 갱신
false 검증 실패 (TAG 불일치)

data와 tag를 기반으로 HMAC-MD5 다이제스트를 재계산하여 수신된 tag와 비교한다. 검증 성공 시 메시지 히스토리(mm_hist)와 카운터(mm_counter)를 갱신하고 EEPROM에 저장(save_state)한 뒤 true를 반환한다. 실패 시 false 반환하며 상태는 갱신되지 않음.

minimac.cpp 파일의 338 번째 라인에서 정의되었습니다.

339 {
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}
이 함수 내부에서 호출하는 함수들에 대한 그래프입니다.:
이 함수를 호출하는 함수들에 대한 그래프입니다.:

◆ print_u64()

static void print_u64 ( uint64_t v)
static

디버깅용: 64비트 부호 없는 정수를 10진수 문자열로 변환해 출력

매개변수
v변환할 64비트 값

minimac.cpp 파일의 39 번째 라인에서 정의되었습니다.

39 {
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}

◆ save_state()

static void save_state ( void )
static

Mini-MAC 상태를 EEPROM에 저장

현재 mm_counter, mm_hist_cnt 및 메시지 히스토리 배열을 EEPROM에 시그니처와 함께 순차 기록하여 재부팅 시에도 상태 유지.

minimac.cpp 파일의 200 번째 라인에서 정의되었습니다.

200 {
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}
이 함수 내부에서 호출하는 함수들에 대한 그래프입니다.:
이 함수를 호출하는 함수들에 대한 그래프입니다.:

변수 문서화

◆ DATA_ADDR

const int DATA_ADDR = SIG_ADDR + sizeof(SIGVAL)
static

minimac.cpp 파일의 11 번째 라인에서 정의되었습니다.

◆ mm_counter

uint64_t mm_counter
static

64비트 메시지 카운터

minimac.cpp 파일의 16 번째 라인에서 정의되었습니다.

◆ mm_hist

MiniMacHist mm_hist[5]
static

최근 λ개 메시지 히스토리

minimac.cpp 파일의 17 번째 라인에서 정의되었습니다.

◆ mm_hist_cnt

uint8_t mm_hist_cnt
static

히스토리 항목 수 (≤ λ)

minimac.cpp 파일의 18 번째 라인에서 정의되었습니다.

◆ mm_id

uint16_t mm_id
static

보호할 CAN ID, 그룹 키, 카운터, 메시지 히스토리

CAN ID (그룹 식별자)

minimac.cpp 파일의 14 번째 라인에서 정의되었습니다.

◆ mm_key

uint8_t mm_key[16]
static

공유 그룹 키

minimac.cpp 파일의 15 번째 라인에서 정의되었습니다.

◆ SIG_ADDR

const int SIG_ADDR = 0
static

EEPROM 레이아웃: 시그니처 및 데이터 저장 시작 주소

minimac.cpp 파일의 9 번째 라인에서 정의되었습니다.

◆ SIGVAL

const uint32_t SIGVAL = 0xAA55AA55
static

minimac.cpp 파일의 10 번째 라인에서 정의되었습니다.