콘텐츠로 이동

v0.0

오프라인 서명 앱 아키텍처 설계서

1. Executive Summary

본 문서는 D'CENT 엔터프라이즈 콜드월렛 커스터디 솔루션의 오프라인 서명 앱 아키텍처를 설계한다. 오프라인 앱은 3-Zone 보안 아키텍처에서 Zone 2(Offline App Zone)에 해당하며, Zone 1(대시보드)과 Zone 3(콜드월렛 SE) 사이의 에어갭 브릿지 역할을 수행한다.

핵심 역할: - 에어갭 브릿지: 대시보드 ↔ 콜드월렛 간 데이터 중계 - 2차 정책 검증: 캐시된 정책 데이터로 핵심 규칙 재확인 - M-of-N 서명 수집: 여러 서명자의 부분 서명을 로컬에서 수집/관리 - WYSIWYS 보조: TX 내용 표시로 사용자 확인 유도


2. 플랫폼 선택 및 근거

2.1. 후보 플랫폼 비교

항목 태블릿 앱 (iOS/Android) 데스크톱 앱 (Electron/Tauri) 전용 Linux 디바이스
카메라 내장 (QR 스캔 즉시 가능) 외장 웹캠 필요 외장 또는 내장
USB-C 내장 (USB-C 포트) 내장 (USB-C 포트) 내장 (USB-C 포트)
에어갭 강제 비행기 모드 + MDM 네트워크 비활성화 (SW) NIC 물리적 제거 가능
이동성 높음 낮음 중간
공급망 신뢰 Apple/Google 앱스토어 자체 배포 자체 빌드
보안 수준 중간-높음 중간 최고
배포 용이성 높음 (앱스토어) 중간 낮음 (하드웨어 포함)

2.2. 추천: 태블릿 앱 (iOS + Android)

선택 근거: 1. 카메라 내장 + USB-C 포트: QR 스캔에 별도 장비 불필요, 콜드월렛 USB-C 직결 2. 에어갭 강제 가능: MDM(Mobile Device Management)으로 네트워크 완전 차단 3. 이동성: 서명자가 콜드월렛과 함께 휴대 가능 4. 앱스토어 배포: 코드 서명 검증 자동화 5. D'CENT 기존 역량: 기존 D'CENT 모바일 앱 개발 경험 활용

2.3. 에어갭 강제 방안

방안 설명 보안 수준
MDM 프로필 기업 MDM으로 WiFi/셀룰러/BT 영구 차단 높음
비행기 모드 + 앱 검증 앱 시작 시 네트워크 상태 감지 → 활성 시 경고/차단 중간
Supervised Mode (iOS) iOS 감독 모드에서 네트워크 완전 비활성화 높음
Work Profile (Android) Android Enterprise 정책으로 네트워크 차단 높음

2.4. 대안: 전용 Linux 디바이스 (최고 보안 환경)

네트워크 하드웨어를 물리적으로 제거한 전용 디바이스: - WiFi/BT 모듈 물리적 제거 - 셀룰러 모뎀 없음 - USB-C 포트는 D'CENT X 연결 전용으로 제한 (MDM 정책) - 카메라 + USB-C 포트만 장착 - 적합: 최고 보안이 필요한 금융기관, 국방 관련 기관


3. 앱 내부 모듈 구조

┌──────────────────────────────────────────────────────┐
│                 Offline Signing App                    │
│                                                        │
│  ┌──────────────────────────────────────────────────┐ │
│  │                  UI Layer                         │ │
│  │  ┌────────────┐  ┌────────────┐  ┌────────────┐ │ │
│  │  │ TX Viewer  │  │ Signature  │  │ Settings   │ │ │
│  │  │ Screen     │  │ Status     │  │ Screen     │ │ │
│  │  └────────────┘  └────────────┘  └────────────┘ │ │
│  └──────────────────────────────────────────────────┘ │
│                                                        │
│  ┌──────────────────────────────────────────────────┐ │
│  │                Service Layer                      │ │
│  │  ┌──────────────┐  ┌──────────────────────────┐ │ │
│  │  │ QR Scanner   │  │ Signature Collector      │ │ │
│  │  │ Module       │  │                          │ │ │
│  │  │              │  │ ● 부분 서명 수집          │ │ │
│  │  │ ● UR Parser  │  │ ● M-of-N 상태 추적       │ │ │
│  │  │ ● Fountain   │  │ ● 세션 관리              │ │ │
│  │  │   Decoder    │  │ ● 릴레이/중앙 수집       │ │ │
│  │  └──────────────┘  └──────────────────────────┘ │ │
│  │  ┌──────────────┐  ┌──────────────────────────┐ │ │
│  │  │ USB Bridge   │  │ Policy Checker           │ │ │
│  │  │ Module       │  │                          │ │ │
│  │  │              │  │ ● 캐시 정책 검증          │ │ │
│  │  │ ● USB HID 관리│ │ ● 버전 대조              │ │ │
│  │  │ ● 디바이스 인증│ │ ● 경고 표시              │ │ │
│  │  │ ● 패킷 분할  │  │                          │ │ │
│  │  └──────────────┘  └──────────────────────────┘ │ │
│  │  ┌──────────────┐  ┌──────────────────────────┐ │ │
│  │  │ Transaction  │  │ APP_HASH                 │ │ │
│  │  │ Viewer       │  │ Generator                │ │ │
│  │  │              │  │                          │ │ │
│  │  │ ● TX 파싱    │  │ ● WYSIWYS 해시 생성      │ │ │
│  │  │ ● 표시       │  │ ● 필드 추출              │ │ │
│  │  └──────────────┘  └──────────────────────────┘ │ │
│  └──────────────────────────────────────────────────┘ │
│                                                        │
│  ┌──────────────────────────────────────────────────┐ │
│  │                Data Layer                         │ │
│  │  ┌──────────────────────────────────────────────┐│ │
│  │  │ Local Storage (AES-256 암호화)                ││ │
│  │  │                                               ││ │
│  │  │ ● 진행 중 서명 세션                            ││ │
│  │  │ ● 정책 캐시                                    ││ │
│  │  │ ● 화이트리스트 캐시                             ││ │
│  │  │ ● 앱 설정                                      ││ │
│  │  │ ● 감사 로그 (로컬)                              ││ │
│  │  └──────────────────────────────────────────────┘│ │
│  └──────────────────────────────────────────────────┘ │
│                                                        │
│  ┌──────────────────────────────────────────────────┐ │
│  │                Security Layer                     │ │
│  │  ● 앱 무결성 검증 (코드 서명 해시)                  │ │
│  │  ● PIN/생체인증                                    │ │
│  │  ● 화면 캡처 방지                                  │ │
│  │  ● 네트워크 상태 감지 (활성 시 경고)                │ │
│  │  ● 데이터 자동 만료 (24시간)                       │ │
│  └──────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────┘

4. QR/USB-C 브릿지 구현 방향

4.1. 데이터 흐름별 채널

방향 경로 앱 동작 채널
대시보드 → 앱 앱 카메라로 대시보드 화면의 QR 촬영 QR 스캔
앱 → 콜드월렛 앱이 USB-C로 콜드월렛에 전송 USB-C 전송
콜드월렛 → 앱 앱이 USB-C로 콜드월렛에서 수신 USB-C 수신
앱 → 대시보드 앱 화면에 QR 표시 → 대시보드 스캐너 촬영 QR 표시

4.2. QR Scanner Module

QRScannerModule {
    // Animated QR 디코딩
    startScan(onFrame: callback): ScanSession
    stopScan(session: ScanSession): void

    // UR 파서
    parseURFrame(qrData: string): URFragment
    assembleFountain(fragments: [URFragment]): bytes?

    // 진행률 표시
    getProgress(): {received: uint, total: uint, percentage: float}

    // 파라미터 조정
    adjustScanSpeed(fps: uint): void  // 카메라 FPS
}

4.3. USB Bridge Module

USBBridgeModule {
    // USB 디바이스 관리
    connectDevice(): USBDevice
    disconnectDevice(device: USBDevice): void

    // USB HID 통신
    initEnterprise(deviceId: bytes): USBResponse
    sendUSBCommand(command: bytes): USBResponse
    sendChunkedUSB(data: bytes, commandType: uint8): USBResponse

    // 에러 핸들링
    onConnectionLost(callback): void
    retry(maxAttempts: uint): void
}

5. M-of-N 서명 수집 흐름

5.1. 서명 수집 모드

중앙 수집 (권장):

서명 요청 수신 (대시보드 → 앱)
    │
    ▼
세션 생성 (session_id, M, N, 타임아웃)
    │
    ├──▶ 서명자 1에게 USB-C 전달 → 콜드월렛1 서명 → 부분 서명1 수신
    │    └─ 세션에 부분 서명1 저장
    │
    ├──▶ 서명자 2에게 USB-C 전달 → 콜드월렛2 서명 → 부분 서명2 수신
    │    └─ 세션에 부분 서명2 저장
    │
    └──▶ ... (M명까지)
         │
         ▼
    M개 서명 도달 → 서명 결과 QR 생성 → 대시보드 반환

릴레이 수집 (세레모니 환경):

서명 요청 + 빈 서명 목록으로 시작
    │
    ▼
서명자 1 콜드월렛 → 부분 서명1 추가 → 앱에 저장
    │
    ▼
서명자 2 콜드월렛 → 부분 서명2 추가 → 앱에 저장
    │
    ▼
M개 도달 → 대시보드 반환

5.2. 서명 세션 관리

SigningSession {
    session_id: bytes(16)        // UUID
    request_id: bytes(16)        // 원본 서명 요청 ID
    chain_type: uint8
    unsigned_tx: bytes           // 미서명 TX
    required_signatures: uint8   // M (필요 서명 수)
    total_signers: uint8         // N (전체 서명자 수)
    collected_signatures: [{
        signer_index: uint8
        signer_pubkey: bytes(33)
        sig_bytes: bytes
        timestamp: uint32
    }]
    status: "collecting" | "complete" | "expired" | "failed"
    created_at: uint32
    expires_at: uint32           // 기본 4시간
    musig2_state: {              // MuSig2 시 상태 관리
        round: uint8
        nonces: [{signer_index, public_nonce}]
        partial_sigs: [{signer_index, partial_sig}]
    }?
}

5.3. 다중 오프라인 앱 환경

서명자가 각자 자신의 오프라인 앱을 사용하는 분산 비동기 환경:

대시보드: 서명 요청 QR 표시 (request_id 포함)
    │
    ├──▶ 서명자 1 (앱A): QR 스캔 → 콜드월렛1 서명 → 부분 서명 QR → 대시보드
    │
    ├──▶ 서명자 2 (앱B): QR 스캔 → 콜드월렛2 서명 → 부분 서명 QR → 대시보드
    │
    └──▶ 서명자 3 (앱C): QR 스캔 → 콜드월렛3 서명 → 부분 서명 QR → 대시보드

대시보드: request_id로 부분 서명 매칭 → M개 도달 시 취합

이 경우 서명 세션 관리는 대시보드에서 수행하며, 각 오프라인 앱은 단일 서명 세션만 처리한다.


6. 보안 요구사항

항목 구현 방향
앱 무결성 검증 앱 시작 시 코드 서명 해시 확인. 변조 감지 시 실행 차단.
로컬 데이터 암호화 AES-256-GCM. 키는 디바이스 Keychain/Keystore에서 파생.
PIN/생체인증 앱 접근 시 PIN(최소 6자리) 또는 생체인증(Face ID/지문) 필수.
화면 캡처 방지 iOS: UIScreen.captured 감지. Android: FLAG_SECURE.
데이터 자동 만료 서명 세션 데이터 24시간 후 자동 삭제. 앱 종료 시 메모리 클리어.
네트워크 감지 앱 실행 중 네트워크 활성 감지 시 경고 배너 + 서명 기능 비활성화.
Jailbreak/Root 감지 탈옥/루팅 디바이스 감지 시 앱 실행 차단 (보안 무결성).
디버그 모드 차단 프로덕션 빌드에서 디버거 연결 감지 시 즉시 종료.

7. 오프라인 정책 캐시

7.1. 캐시 구조

PolicyCache {
    policy_version: uint32      // 대시보드 정책 버전
    last_synced: uint32         // 마지막 동기화 타임스탬프
    rules: [{
        rule_type: uint8        // 정책 규칙 유형
        value: bytes(32)        // 정책 값
    }]
    whitelist: [{
        address: string
        chain_type: uint8
        label: string
    }]
    whitelist_merkle_root: bytes(32)
    whitelist_version: uint32
}

7.2. 동기화 방식

정책 캐시는 에어갭을 통해 동기화된다:

대시보드: 정책 동기화 QR 생성 (현재 정책 + 화이트리스트 스냅샷)
    │
    ▼
앱: QR 스캔 → 캐시 업데이트
    │
    ▼
앱: policy_version 저장 → 이후 서명 요청 시 버전 대조

7.3. 캐시 미스 시 동작

상황 동작
캐시 없음 (첫 사용) 2차 정책 검증 스킵 + "정책 미동기화" 경고 표시. SE(Zone 3)가 최종 검증.
캐시 버전 불일치 경고 표시 + 서명 진행 허용. SE가 최종 검증 수행.
캐시 만료 (30일 초과) 강한 경고 + 동기화 권고. 서명 진행은 허용 (SE 의존).

설계 철학: 오프라인 앱의 정책 검증은 보조적 역할이다. 최종 정책 강제는 항상 SE(Zone 3)에서 수행되므로, 캐시 부재가 보안 취약점이 되지 않는다.


본 문서는 Phase 5 System Architecture Design의 일부로, Phase 3 에어갭 서명 플로우(airgap-signing-flow.md)의 오프라인 앱 역할을 아키텍처 수준으로 설계한다. three-zone-security-architecture.md의 Zone 2 컴포넌트 책임을 구현 수준으로 구체화한다.


관련 문서