v0.6
SEAuditDigest CBOR 스키마 및 SE 역할 경계 정의¶
1. 설계 원칙¶
1.1. "SE에서 처리"의 명확한 경계¶
D'CENT Enterprise의 3-Zone 아키텍처에서 SE(Secure Element, Zone 3)는 감사 무결성 체인의 최하위 신뢰 앵커(trust anchor)로 기능한다. SE의 역할은 엄격히 제한되며, 이 경계를 명확히 정의하는 것이 본 문서의 핵심 목적이다.
SE가 수행하는 역할:
| 역할 | 설명 | 업데이트 주기 |
|------|------|-------------|
| 해시 앵커 저장 | latest_log_hash와 latest_sign_hash에 최신 해시 저장 | 동기화 시 / 서명 시 |
| 서명 카운터 유지 | sign_counter 단조 증가, 롤백 불가 | 서명 수행 시마다 |
| 다이제스트 서명 | digest_sig로 SEAuditDigest 전체를 SE 개인키로 서명 | 모든 필드 변경 시 |
| 정책 해시 저장 | policy_hash에 현재 적용 정책의 해시 저장 | 정책 변경 시 |
SE가 수행하지 않는 역할: | 제외 항목 | 이유 | 대안 (담당 Zone) | |----------|------|----------------| | 상세 감사 로그 저장 | NVM 용량 제한 (16KB 전체), 구조화 데이터 부적합 | Zone 2 (오프라인 앱 로컬 스토리지) | | 로그 검색/필터링 | SE 프로세서의 연산 성능 부족 | Zone 1 (대시보드 DB 쿼리) | | 이벤트 분류/분석 | 복잡한 로직 실행 불가 | Zone 1 (대시보드 분석 엔진) | | 보고서 생성 | 출력 포맷 변환, 템플릿 처리 불가 | Zone 1 (대시보드 보고서 엔진) | | 시간 관리 | SE 내부 RTC(Real-Time Clock) 없음 | Zone 2 (오프라인 앱이 시간 제공) |
1.2. 설계 근거¶
EAL5+ SE 자원 제약: - NVM 저장소: 총 16KB 중 ~3.7KB 사용(v0.5 기준), SEAuditDigest에 128B 추가 - APDU 전송: 단일 명령/응답 최대 256B, 128B 다이제스트는 1회 전송으로 처리 가능 - 연산 속도: ECDSA P-256 서명 ~150ms, SHA-256 해시 ~5ms
128 bytes 크기 결정 근거: 1. SE NVM 예산 대비 최소 오버헤드 (전체 대비 0.8%) 2. APDU 1회 전송 가능 (256B 제한 내) 3. 해시 앵커 + 카운터 + 정책 추적에 필요한 최소 정보 포함 4. 3 bytes 예약 공간으로 향후 확장 가능 (Phase 31 규제 태그 등)
규제 근거: | 규제 | 요건 | SEAuditDigest 대응 | |------|------|-------------------| | REG-KR-14 | 변조 불가 감사 로그 | SE NVM 저장 + digest_sig 서명 = 하드웨어 레벨 변조 방지 | | CORE-04 | 3계층 무결성 체인 | SEAuditDigest가 Layer 1(최하위 앵커) 역할 | | CORE-09 | 정책 변경 이력 관리 | policy_hash로 정책 상태 추적 |
2. SEAuditDigest CBOR 스키마 (128 bytes 고정)¶
2.1. CDDL 정의¶
; ====================================================================
; D'CENT Enterprise SEAuditDigest Schema
; CBOR (RFC 8949) encoded, 128 bytes fixed size
; SE NVM에 저장되는 감사 무결성 앵커
; Phase: 30-audit-architecture
; ====================================================================
SEAuditDigest = {
version: uint .size 1, ; 스키마 버전 (1 byte)
sign_counter: uint .size 4, ; 단조 증가 서명 카운터 (4 bytes)
latest_log_hash: bstr .size 32, ; Zone 2 마지막 동기화 로그 배치의 SHA-256 (32 bytes)
latest_sign_hash: bstr .size 32, ; 마지막 서명 트랜잭션 해시 (32 bytes)
policy_hash: bstr .size 32, ; 현재 적용 정책의 SHA-256 해시 (32 bytes)
timestamp: uint .size 4, ; 마지막 업데이트 epoch seconds (4 bytes)
digest_sig: bstr .size 20, ; SE audit_key로 서명한 다이제스트 (20 bytes)
reserved: bstr .size 3, ; 향후 확장 예약 (3 bytes)
}
; Total: 1 + 4 + 32 + 32 + 32 + 4 + 20 + 3 = 128 bytes
2.2. 바이트 레이아웃¶
Offset Size Field Description
------ ---- ----------------- ------------------------------------------
0x00 1 version 스키마 버전 (초기값: 0x01)
0x01 4 sign_counter 서명 카운터 (big-endian uint32)
0x05 32 latest_log_hash 마지막 동기화 로그 배치 해시
0x25 32 latest_sign_hash 마지막 서명 트랜잭션 해시
0x45 32 policy_hash 현재 정책 해시
0x65 4 timestamp 마지막 업데이트 시간 (epoch seconds)
0x69 20 digest_sig ECDSA P-256 서명의 truncated hash
0x7D 3 reserved 예약 (0x00 * 3)
------ ---- ----------------- ------------------------------------------
Total: 128 bytes
2.3. 초기 상태 (Factory Default)¶
SEAuditDigest (factory) = {
version: 1,
sign_counter: 0,
latest_log_hash: 0x00 * 32, ; 동기화 이력 없음
latest_sign_hash: 0x00 * 32, ; 서명 이력 없음
policy_hash: 0x00 * 32, ; 정책 미설정
timestamp: 0, ; 미갱신
digest_sig: 0x00 * 20, ; 미서명
reserved: 0x00 * 3,
}
3. 각 필드 상세 설명¶
3.1. version (1 byte)¶
- 목적: 향후 SEAuditDigest 스키마 마이그레이션 지원
- 초기값:
0x01(Version 1) - 갱신 조건: 펌웨어 업데이트로 스키마 구조 변경 시
- 호환성 규칙:
- 오프라인 앱은 version 필드를 읽어 파싱 방법 결정
- 알 수 없는 version은 거부하고 펌웨어 업데이트 권고
- version 변경 시 reserved 영역을 재할당할 수 있음
3.2. sign_counter (4 bytes)¶
- 목적: 서명 수행 횟수의 변조 불가 추적
- 범위: 0 ~ 4,294,967,295 (uint32, ~43억 서명)
- 갱신 조건: 매 서명(TX_SIGN) 수행 시
+1 - 보안 속성:
- 단조 증가(monotonically increasing): 감소 불가, 롤백 불가
- SE NVM에 직접 기록되므로 소프트웨어 수준 변조 불가
- 오프라인 앱이 이전 값과 비교하여 연속성 검증
- 활용:
- 오프라인 앱:
현재 sign_counter - 이전 sign_counter = 기간 내 서명 횟수검증 - 대시보드: 동기화된 TX_SIGN 레코드 수와 카운터 증분 일치 확인
- Proof of Cold Storage: 콜드 환경 서명 건수 증명 (Plan 02)
- 오버플로우 대응: uint32 최대값 도달 시 SE가 서명 거부, 새 키 생성 필요 (실무상 도달 불가)
3.3. latest_log_hash (32 bytes)¶
- 목적: Zone 2 해시 체인의 앵커 포인트
- 내용: 오프라인 앱이 AuditSyncPayload 생성 시 제출한 배치 로그의 SHA-256 해시
- 갱신 조건: 오프라인 앱이
UPDATE_AUDIT_DIGESTAPDU로 갱신 요청 시 - 검증 흐름:
- 오프라인 앱이 AuditSyncPayload 조립 완료
- 배치 전체의 SHA-256 해시 계산
- SE에
UPDATE_AUDIT_DIGEST(latest_log_hash = batch_hash)전송 - SE가 해시 저장 후
digest_sig재계산 - 대시보드가 AuditSyncPayload 수신 시
se_digest.latest_log_hash와 수신 배치 해시 비교 - 공격 벡터 차단:
- 오프라인 앱이 로그를 변조하면 → 배치 해시가 변경됨 → SE의
latest_log_hash와 불일치 → 대시보드에서 감지 - SE NVM에 저장된 해시를 변조하면 →
digest_sig검증 실패 → 변조 감지
3.4. latest_sign_hash (32 bytes)¶
- 목적: 마지막 서명 트랜잭션의 암호학적 증거
- 내용: SE가 서명한 트랜잭션 데이터의 SHA-256 해시
- 갱신 조건: 매 TX_SIGN 수행 시 자동 갱신 (SE 펌웨어 내부)
- 활용:
- 오프라인 앱이 마지막 서명 트랜잭션과 대조 검증
- 감사인이 특정 시점의 마지막 서명 상태 확인
- Proof of Cold Storage에서 콜드 서명 증명의 기초 데이터
3.5. policy_hash (32 bytes)¶
- 목적: 현재 SE에 적용된 정책의 상태 추적
- 내용: SE에 로드된 정책 번들의 SHA-256 해시
- 갱신 조건: 정책 업데이트(PolicyUpdateBundle, Phase 31) 적용 시
- 활용:
- 오프라인 앱이 정책 버전 일치 확인
- 대시보드가 "SE에 적용된 정책"과 "대시보드의 최신 정책" 일치 여부 검증
- 임의 시점의 정책 상태를 감사 증적으로 증명
- Phase 31 연결: PolicyUpdateBundle의 해시가 이 필드에 저장됨
3.6. timestamp (4 bytes)¶
- 목적: SEAuditDigest의 마지막 갱신 시점 기록
- 범위: epoch seconds (uint32, ~2106년까지)
- 중요 제약: SE 내부에 RTC(Real-Time Clock)가 없음
- 오프라인 앱이
UPDATE_AUDIT_DIGEST시 현재 시간을 함께 전달 - SE는 전달받은 시간이 기존 timestamp 이상인 경우에만 수용 (시간 역행 방지)
- 따라서 timestamp의 정밀도는 "오프라인 앱이 제공한 시간"에 의존
- 시간 정합성 검증:
- 대시보드:
se_digest.timestamp와 AuditSyncPayload 수신 시간 간 합리적 차이 확인 - 허용 범위:
|se_timestamp - sync_receive_time| < 24시간(에어갭 지연 허용)
3.7. digest_sig (20 bytes)¶
- 목적: SEAuditDigest 전체의 무결성 서명
- 알고리즘: ECDSA P-256 서명의 truncated hash
- SE의
audit_key(전용 P-256 키 쌍)로 서명 - 전체 ECDSA 서명(64B)에서 SHA-256 해시를 계산한 후 앞 20 bytes 추출
- 20 bytes = 160-bit 보안 수준, SEAuditDigest 무결성 검증에 충분
- Truncation 근거:
- 128B 고정 크기 제약 내에서 64B 서명은 공간 과다
- 160-bit 해시는 충돌 저항 2^80, 목적(무결성 검증)에 충분
- 전체 서명이 필요한 경우
READ_AUDIT_DIGEST_FULL확장 APDU로 대응 가능 - 서명 대상: version || sign_counter || latest_log_hash || latest_sign_hash || policy_hash || timestamp (105 bytes)
- 갱신 조건: 모든 필드 변경 시 자동 재계산 (SE 내부)
3.8. reserved (3 bytes)¶
- 목적: 향후 확장 예약 공간
- 초기값:
0x00 * 3 - 예상 활용 (Phase 31+):
- 규제 태그 비트맵 (어떤 규제 관할 정책이 활성화되어 있는지)
- 긴급 플래그 (보안 이벤트 감지 시)
- 다이제스트 체인 시퀀스 번호
- 제약: 128B 고정 크기를 초과할 수 없으므로, 확장 시 다른 필드 최적화와 병행
4. SEAuditDigest 업데이트 흐름¶
4.1. 서명 시 (TX_SIGN)¶
1. 오프라인 앱 -> SE: SIGN_TRANSACTION(tx_data)
2. SE 내부 처리:
a. 트랜잭션 서명 수행 (기존 흐름)
b. sign_counter++
c. latest_sign_hash = SHA-256(tx_data)
d. timestamp = 오프라인 앱 제공 시간
e. digest_sig = Truncate20(ECDSA_P256(audit_key, digest_content))
3. SE -> 오프라인 앱: 서명 결과 + 갱신된 sign_counter
4.2. 동기화 시 (AuditSyncPayload 생성 전)¶
1. 오프라인 앱: AuditSyncPayload 조립
a. 마지막 동기화 이후 축적된 AuditRecord 수집
b. 배치 전체의 SHA-256 해시 계산 (batch_hash)
2. 오프라인 앱 -> SE: UPDATE_AUDIT_DIGEST(latest_log_hash = batch_hash, timestamp = now)
3. SE 내부 처리:
a. latest_log_hash = batch_hash
b. timestamp = max(기존 timestamp, 제공 timestamp) ; 시간 역행 방지
c. digest_sig = Truncate20(ECDSA_P256(audit_key, digest_content))
4. SE -> 오프라인 앱: ACK + 갱신된 SEAuditDigest
5. 오프라인 앱: AuditSyncPayload.se_digest에 갱신된 다이제스트 포함
4.3. 정책 변경 시 (PolicyUpdateBundle 적용)¶
1. 오프라인 앱 -> SE: APPLY_POLICY(policy_bundle) ; Phase 31에서 상세 설계
2. SE 내부 처리:
a. policy_bundle 검증 (쿼럼 서명 확인)
b. 정책 적용
c. policy_hash = SHA-256(policy_bundle)
d. timestamp = 오프라인 앱 제공 시간
e. digest_sig = Truncate20(ECDSA_P256(audit_key, digest_content))
3. SE -> 오프라인 앱: 정책 적용 결과 + 갱신된 SEAuditDigest
5. 무결성 검증 방법¶
5.1. 오프라인 앱 검증 (실시간)¶
1. SE에서 READ_AUDIT_DIGEST 수행 -> 128B 수신
2. digest_sig 검증:
a. SE의 audit_key 공개키로 서명 검증
b. 서명 대상 = version || sign_counter || latest_log_hash ||
latest_sign_hash || policy_hash || timestamp (105B)
c. 검증 실패 시: SECURITY_EVENT(sec-tampering-detected) 생성
3. sign_counter 연속성 확인:
a. 이전에 읽은 sign_counter 값과 비교
b. 차이 = 그 사이 수행된 서명 횟수
c. 기대값과 불일치 시: SECURITY_EVENT(sec-counter-mismatch) 생성
4. latest_log_hash 일치 확인:
a. 마지막으로 SE에 제출한 batch_hash와 비교
b. 불일치 시: 다른 앱이 SE를 업데이트했거나, SE 변조 의심
5.2. 대시보드 검증 (동기화 수신 시)¶
1. AuditSyncPayload 수신
2. 포함된 se_digest 검증:
a. digest_sig 검증 (SE audit_key 공개키 사용)
b. se_digest.latest_log_hash == SHA-256(수신 records 배치) 확인
c. 불일치 시: 오프라인 앱 변조 또는 전송 중 변조 의심
3. sign_counter 누적 확인:
a. 이전 동기화의 sign_counter와 비교
b. 증분 = 기간 내 서명 횟수
c. 수신된 TX_SIGN 레코드 수와 일치 확인
4. timestamp 정합성:
a. se_digest.timestamp와 AuditSyncPayload 수신 시간 비교
b. 합리적 범위(24시간) 초과 시 경고
5.3. 감사인 검증 (임의 시점 스냅샷)¶
1. 감사인이 감사 대상 시점 결정
2. SE에서 READ_AUDIT_DIGEST -> 현재 SEAuditDigest 스냅샷 획득
3. 대시보드에서 해당 시점의 마지막 AuditSyncPayload 조회
4. 교차 검증:
a. SE latest_log_hash == 대시보드 기록의 마지막 동기화 배치 해시
b. SE sign_counter >= 대시보드 기록의 누적 TX_SIGN 수
c. SE policy_hash == 대시보드 기록의 정책 해시
5. 해시 체인 구간 검증:
a. 감사 대상 기간의 AuditRecord 해시 체인 순회
b. 각 레코드의 prev_hash 일치 확인
c. 마지막 레코드의 해시 == SE latest_log_hash (또는 해당 시점의 스냅샷)
6. 검증 결과: 통과 -> "해당 기간 감사 증적 무결성 확인"
실패 -> 변조 구간 특정 및 보고
6. SE 메모리 레이아웃 업데이트¶
6.1. 메모리 예산 현황¶
SE NVM 메모리 레이아웃 (v0.6 기준)
총 NVM: 16,384 bytes (16 KB)
기존 사용 (v0.5):
키 저장소 (마스터 키, 유도 키 등): ~2,048 bytes
정책 규칙 테이블: ~512 bytes
RBAC 역할 매핑: ~256 bytes
MuSig2 세션 데이터: ~384 bytes
인증서/메타데이터: ~512 bytes
─────────────────────────────────────────────
v0.5 합계: ~3,712 bytes (~3.7 KB)
v0.6 추가:
SEAuditDigest: 128 bytes
sign_counter NVM 미러링: 4 bytes (*)
audit_key 슬롯 인덱스: 4 bytes
─────────────────────────────────────────────
v0.6 추가 합계: 136 bytes
v0.6 총 사용: ~3,848 bytes (~3.84 KB)
여유 공간: ~12,536 bytes (~12.16 KB)
(*) sign_counter는 SEAuditDigest 내에 포함되어 있으나,
전원 차단 시 데이터 손실 방지를 위해 NVM 별도 영역에
미러링(redundant copy) 권장. Wear leveling 적용.
6.2. Phase 31+ 확장 여지¶
| 예상 확장 | 크기 | Phase | 출처 |
|---|---|---|---|
| 규제 태그 비트맵 | ~32 bytes | Phase 31 | SE 정책 엔진 규제 확장 |
| PolicyUpdateBundle 캐시 | ~256 bytes | Phase 31 | 정책 버전 관리 |
| 추가 키 슬롯 | ~128 bytes | 후속 | 감사 키, 인증 키 추가 |
| 확장 후 예상 사용 | ~4.3 KB | - | 16 KB 대비 26% |
12 KB 이상의 여유 공간이 확보되어 있으므로 향후 확장에 충분하다.
7. APDU 커맨드 명세¶
7.1. UPDATE_AUDIT_DIGEST¶
오프라인 앱이 SE에 latest_log_hash 또는 policy_hash를 갱신하는 커맨드.
Command APDU:
CLA: 0x80
INS: 0xA0 ; AUDIT 클래스
P1: 0x01 ; UPDATE 서브커맨드
P2: <update_flags> ; 어떤 필드를 갱신할지 비트 플래그
0x01: latest_log_hash 갱신
0x02: policy_hash 갱신
0x03: 둘 다 갱신
Lc: <data length>
Data: <갱신할 해시 값(32B)> + <timestamp(4B)>
Le: 0x80 (128 bytes 응답 요청)
Response APDU:
Data: 갱신된 SEAuditDigest 전체 (128 bytes)
SW1-SW2:
0x9000: 성공
0x6982: 인증 필요 (PIN/서명 세션 미인증)
0x6A80: 잘못된 데이터 (해시 크기 불일치 등)
0x6985: 시간 역행 거부 (제공 timestamp < 기존 timestamp)
접근 제어: 기존 PIN/서명 세션 인증 완료 후에만 실행 가능. 비인증 상태에서 호출 시 0x6982 반환.
7.2. READ_AUDIT_DIGEST¶
SE에서 현재 SEAuditDigest를 읽는 커맨드.
Command APDU:
CLA: 0x80
INS: 0xA0 ; AUDIT 클래스
P1: 0x02 ; READ 서브커맨드
P2: 0x00 ; 전체 다이제스트 읽기
Lc: 0x00
Le: 0x80 (128 bytes)
Response APDU:
Data: SEAuditDigest 전체 (128 bytes)
SW1-SW2:
0x9000: 성공
0x6982: 인증 필요
접근 제어: PIN 인증 후 접근 가능. 서명 세션 중에도 읽기 가능.
7.3. APDU 보안 고려사항¶
| 항목 | 설계 | 근거 |
|---|---|---|
| 인증 요구 | 모든 AUDIT APDU는 PIN 인증 필수 | 비인가 접근 방지 |
| timestamp 검증 | 제공 시간이 기존 timestamp 이상이어야 수용 | 시간 역행 공격 방지 |
| sign_counter 직접 변경 불가 | UPDATE_AUDIT_DIGEST로 sign_counter 변경 불가 | 서명 카운터 조작 방지 |
| digest_sig 자동 재계산 | 모든 필드 변경 시 SE가 자동으로 재서명 | 수동 서명 주입 방지 |
| NVM 원자성 | 필드 갱신은 원자적(atomic) — 중간 상태 없음 | 전원 차단 시 일관성 보장 |
8. Plan 02 연결점¶
본 문서에서 정의한 SEAuditDigest는 Plan 02의 다음 설계에 직접 연결된다:
8.1. 3계층 무결성 체인의 Layer 1¶
SEAuditDigest는 3계층 무결성 체인의 최하위 앵커(Layer 1)로 기능한다:
- latest_log_hash: Layer 2(오프라인 앱) 해시 체인의 앵커
- digest_sig: Layer 1 자체의 무결성 증명
- Layer 2 -> Layer 3(대시보드)로의 교차 검증 시 SEAuditDigest 스냅샷이 포함됨
8.2. AuditSyncPayload 포함¶
AuditSyncPayload(Plan 02)의 se_digest 필드에 SEAuditDigest 128B 전체가 포함된다:
- 대시보드가 수신 시 se_digest.latest_log_hash와 수신 레코드 배치 해시를 비교
- sign_counter 연속성 확인으로 누락된 서명 감지
8.3. Proof of Cold Storage 입력¶
sign_counter: 콜드 환경(에어갭 SE)에서 수행된 서명 총 횟수latest_sign_hash: 마지막 콜드 서명의 암호학적 증거- 콜드 비율 계산: SE 관리 자산 / 전체 자산 = 100% (에어갭 구조)
Phase: 30-audit-architecture Version: 1.0 SE 메모리 기준: v0.6 (~3.84 KB / 16 KB)
관련 문서¶
- 감사 로그 레코드 CDDL 스키마 설계 -- 규제 준수
- AuditSyncPayload 동기화 프로토콜 및 3계층 무결성 체인 설계 -- 규제 준수
- Proof of Cold Storage 보고서 구조 및 SE 서명 자동 증명 설계 -- 규제 준수