POSA_LEAKSMS/firmware/board_sht30/main.c
유창욱 90f121e14c chore: import codebase with security hardening
SHT30 온습도 모니터링 시스템 전체 소스(서버 PHP, STM32 펌웨어, SQL, 테스트).
전체 코드리뷰에서 도출된 보안 하드닝 10건 반영:
- 요청 서명 HMAC-SHA256 전환(펌웨어 sig.c/서버 config.php/호스트 패리티 동시)
- 재전송 방어 + 기본 API_KEY fail-closed + 디바이스 문자열 정제(api/sensor_data.php)
- 오프라인 SMS 중복 발송 경합 제거(cron_heartbeat.php, 원자적 선점)
- CSV 수식 주입 방지(monthly_report.php), 감사로그 회전 락(retention_cleanup.php)
- 브루트포스 카운터 원자화(login.php), 예시 TOTP 비밀키 무효화, 마이그레이션 멱등화

_backup/(하드코딩 실 비밀값 포함)·config.local.php·런타임 상태는 .gitignore 제외.
2026-06-20 09:37:40 +09:00

89 lines
3.8 KiB
C

/* =============================================================================
* main.c - SHT30 온습도 보드 펌웨어 진입점 (BOARD_SHT30)
*
* 부팅 순서 (RPi sht30_monitor.py 기동 대응):
* bsp_init() : HAL/클럭(HSE 8MHz -> 168MHz)/공통 GPIO/LED
* applog_init() : USART3 디버그 로그(PD8/PD9, 115200) — PA2 는 ETH_MDIO 전용
* watchdog_init() : IWDG 활성화 (이후 헬스 태스크가 refresh)
* sht30_init() : I2C 주변장치 + 센서 초기화
* net_init() : ETH MAC + LAN8720 PHY(RMII) + LwIP
* tls_init() : mbedTLS 엔트로피/DRBG 시드, 루트 CA 파싱
* app_sht30_start() : 측정 태스크 + 헬스 태스크 생성
* vTaskStartScheduler() : FreeRTOS 스케줄러 기동(반환하지 않음)
*
* 폐쇄망(air-gapped): 모든 의존성(HAL/CMSIS/FreeRTOS/lwip/mbedtls)은 third_party
* 에 벤더링되어 있으며 런타임에 외부 네트워크를 사용하지 않는다(SNTP/HTTPS는
* 사내 망 내부 서버 대상).
* ===========================================================================*/
#include <stdint.h>
#include "FreeRTOS.h"
#include "task.h"
#include "app_config.h" /* board_config.h + secrets.h 포함 */
#include "bsp.h"
#include "applog.h"
#include "watchdog.h"
#include "sht30.h"
#include "net.h"
#include "tls.h"
/* 이 펌웨어는 SHT30 보드 전용이다(CMake sht30_fw 타깃이 -DBOARD_SHT30 정의). */
#if !defined(BOARD_SHT30)
#error "board_sht30/main.c 는 -DBOARD_SHT30 로 빌드해야 합니다."
#endif
/* app_sht30.c 에서 정의 (전용 헤더 없이 모듈 내부 함수로 연결) */
extern void app_sht30_start(void);
int main(void)
{
/* 1) 클럭/HAL/공통 주변장치. 가장 먼저 수행해야 이후 초기화가 유효하다. */
bsp_init();
/* 2) 디버그 로그 UART. 이후 단계의 진단 메시지를 위해 일찍 켠다. */
applog_init();
LOGI("=== SHT30 board boot ===");
LOGI("device_id=%s sensor_id=%d ver=%s",
BOARD_DEVICE_ID, BOARD_SENSOR_ID, APP_VERSION);
LOGI("location=%s name=%s", BOARD_DEVICE_LOCATION, BOARD_SENSOR_NAME);
/* 3) 독립 워치독(IWDG). 활성화 즉시 카운트다운이 시작되므로, 스케줄러 기동
* 후 헬스 태스크가 첫 refresh 를 하기 전까지 타임아웃되지 않도록
* APP_WATCHDOG_TIMEOUT_MS 는 충분히 크게(20s) 설정되어 있다. */
watchdog_init(APP_WATCHDOG_TIMEOUT_MS);
/* 4) SHT30 센서(I2C). 실패해도 부팅은 계속한다: 측정 태스크가 매 주기
* 재시도하므로(센서 일시 미연결 대비) 여기서 멈추지 않는다. */
if (sht30_init() != 0) {
LOGE("sht30_init failed (will retry in measure loop)");
} else {
LOGI("sht30 init ok (i2c addr=0x%02X)", APP_SHT30_I2C_ADDR);
}
/* 5) 네트워크 스택 초기화 (LwIP tcpip 스레드 포함). 링크 업 대기는
* 측정 태스크에서 수행한다. */
net_init();
LOGI("net init ok");
/* 6) TLS 전역 초기화 (엔트로피/DRBG, 임베드 루트 CA 파싱). 한 번만 수행. */
if (tls_init() != 0) {
/* TLS 가 없으면 서버 보고가 불가능하므로 치명적 오류로 처리.
* (워치독이 결국 리셋하여 재기동을 시도) */
bsp_fatal("tls_init failed");
}
LOGI("tls init ok");
/* 7) 애플리케이션 태스크(측정 + 헬스) 생성. */
app_sht30_start();
LOGI("tasks created, starting scheduler");
/* 8) 스케줄러 기동. 정상적으로는 반환하지 않는다. */
vTaskStartScheduler();
/* 여기에 도달했다면 힙 부족으로 스케줄러가 기동하지 못한 것이다. */
bsp_fatal("scheduler returned (insufficient heap)");
for (;;) {
/* unreachable */
}
}