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 파일의 66 번째 라인에서 정의되었습니다.

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}
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 번째 라인에서 정의되었습니다.

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

◆ load_state()

static bool load_state ( void )
static

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

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

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

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

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}
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 파일의 246 번째 라인에서 정의되었습니다.

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}
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 파일의 286 번째 라인에서 정의되었습니다.

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}
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 파일의 343 번째 라인에서 정의되었습니다.

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

◆ print_u64()

static void print_u64 ( uint64_t v)
static

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

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

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

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

◆ save_state()

static void save_state ( void )
static

Mini-MAC 상태를 EEPROM에 저장

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

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

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

변수 문서화

◆ 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 번째 라인에서 정의되었습니다.