v0.0
에어갭 통신 프로토콜 설계서¶
1. Executive Summary¶
본 문서는 D'CENT 엔터프라이즈 콜드월렛 커스터디 솔루션의 에어갭 통신 프로토콜을 바이트 수준으로 명세한다. Blockchain Commons UR(Uniform Resources) v2 표준을 기반으로 CBOR 스키마, Animated QR 프로토콜 파라미터, USB-C 통신 프로토콜을 정의하여 펌웨어/앱/백엔드 개발팀이 프로토콜 호환성을 보장하면서 독립 구현할 수 있게 한다.
설계 기반: - Phase 3 airgap-signing-flow.md 섹션 7 "Phase 5 설계 시 참조사항"의 상세화 범위 충족 - Blockchain Commons UR v2 표준 준수 - D'CENT X 기존 USB-C 통신 프로토콜과의 호환성 고려
통신 채널 구성: - 대시보드 ↔ 오프라인 앱: QR (Animated QR / Fountain Code) — 에어갭 경계 - 오프라인 앱 ↔ D'CENT X 콜드월렛: USB-C — 유선 직결 (에어갭이 아닌 SE 보안 경계) - D'CENT X ↔ 리커버리카드: NFC (ISO 14443) — 리커버리카드 전용, 본 문서 범위 외
2. UR (Uniform Resources) 프로토콜 스택¶
2.1. 계층 구조¶
┌─────────────────────────────────────┐
│ Layer 4: Transport │
│ QR (Animated QR / Fountain Code) │
│ USB-C (HID / Bulk Transfer) │
├─────────────────────────────────────┤
│ Layer 3: UR Encoding │
│ UR v2 (Uniform Resources) │
│ ur:<type>/<cbor-encoded-data> │
├─────────────────────────────────────┤
│ Layer 2: Serialization │
│ CBOR (RFC 8949) │
│ CDDL Schema Definition │
├─────────────────────────────────────┤
│ Layer 1: Application Data │
│ PSBT, EIP-712, PolicyUpdate, etc. │
└─────────────────────────────────────┘
2.2. UR 버전¶
- UR v2 (Blockchain Commons): 현재 표준
- URI 형식:
ur:<type>/<cbor-bytewords-encoded> - Bytewords 인코딩: CBOR 바이너리를 인간 판독 가능한 단어 시퀀스로 변환
- Animated QR 분할 시 Fountain 코드 (LT 코드 기반)
2.3. UR Type Registry¶
| UR Type | CBOR 태그 | 용도 | 표준 |
|---|---|---|---|
crypto-psbt |
310 | BTC PSBT 전달 | BC 표준 |
crypto-account |
311 | HD 계정 정보 | BC 표준 |
crypto-hdkey |
303 | HD 키 정보 | BC 표준 |
crypto-eth-sign-request |
401 | EVM 서명 요청 | BC 확장 |
crypto-eth-signature |
402 | EVM 서명 응답 | BC 확장 |
dcent-sign-request |
45001 | D'CENT 확장 서명 요청 | D'CENT 커스텀 |
dcent-sign-response |
45002 | D'CENT 확장 서명 응답 | D'CENT 커스텀 |
dcent-policy-update |
45010 | 정책 업데이트 | D'CENT 커스텀 |
dcent-policy-response |
45011 | 정책 응답 | D'CENT 커스텀 |
dcent-whitelist-verify |
45020 | 화이트리스트 검증 | D'CENT 커스텀 |
dcent-device-status |
45031 | 디바이스 상태 | D'CENT 커스텀 |
D'CENT 커스텀 태그 범위: 45000-45999 (IANA 미등록 Private Use 범위)
3. CBOR 스키마 정의 (CDDL)¶
3.1. BTC 서명 요청 (crypto-psbt 확장)¶
; D'CENT BTC 서명 요청
; UR Type: dcent-sign-request (chain_type = 0)
; CBOR Tag: 45001
dcent-btc-sign-request = {
1: uint, ; version (프로토콜 버전)
2: uint, ; message_type (0x01)
3: bytes .size 16, ; request_id (UUID)
4: uint, ; timestamp_ref
5: bytes .size 32, ; payload_hash (SHA-256)
6: uint, ; chain_type (0x00 = BTC)
7: bytes .size 16, ; wallet_id
8: bytes, ; psbt (PSBT 바이너리)
9: [+ derivation-entry], ; derivation_paths
? 10: musig2-context, ; musig2_context (MuSig2 시)
11: uint, ; policy_version
12: uint, ; whitelist_version
13: bytes .size 32, ; app_hash (WYSIWYS)
? 14: whitelist-proof ; whitelist_proof
}
derivation-entry = {
1: text, ; path (e.g., "m/86'/0'/0'/0/0")
2: uint ; signer_index
}
musig2-context = {
1: uint, ; round (1 또는 2)
? 2: bytes .size 66, ; aggregated_nonce (Round 2)
3: [+ bytes .size 33] ; participant_pubkeys
}
whitelist-proof = {
1: bytes, ; recipient_address
2: [+ bytes .size 32] ; merkle_proof (최대 20개)
}
3.2. EVM 서명 요청¶
; D'CENT EVM 서명 요청
; UR Type: dcent-sign-request (chain_type = 1)
; CBOR Tag: 45001
dcent-evm-sign-request = {
1: uint, ; version
2: uint, ; message_type (0x02)
3: bytes .size 16, ; request_id
4: uint, ; timestamp_ref
5: bytes .size 32, ; payload_hash
6: uint, ; chain_type (0x01 = EVM)
7: bytes .size 16, ; wallet_id
8: uint, ; chain_id (EIP-155)
9: evm-transaction, ; transaction
? 10: eip712-data, ; eip712_data (Safe 등)
11: text, ; derivation_path
12: uint, ; policy_version
13: uint, ; whitelist_version
14: bytes .size 32, ; app_hash
? 15: token-info, ; token_info (ERC-20)
? 16: whitelist-proof ; whitelist_proof
}
evm-transaction = {
1: bytes .size 20, ; to
2: bytes .size 32, ; value (big-endian wei)
3: bytes, ; data (calldata)
4: uint, ; gas_limit
5: uint, ; max_fee_per_gas
6: uint, ; max_priority_fee_per_gas
7: uint, ; nonce
? 8: [+ access-list-entry] ; access_list
}
access-list-entry = {
1: bytes .size 20, ; address
2: [+ bytes .size 32] ; storage_keys
}
eip712-data = {
1: bytes .size 32, ; domain_separator
2: bytes .size 32, ; message_hash
3: text ; typed_data_json
}
token-info = {
1: text, ; symbol
2: uint, ; decimals
3: bytes .size 20 ; contract_address
}
3.3. 서명 응답¶
; D'CENT 서명 응답
; UR Type: dcent-sign-response
; CBOR Tag: 45002
dcent-sign-response = {
1: uint, ; version
2: uint, ; message_type (0x03)
3: bytes .size 16, ; request_id
4: uint, ; timestamp_ref
5: bytes .size 32, ; payload_hash
6: uint, ; status_code
? 7: signature-data, ; signature (성공 시)
8: bytes .size 32, ; se_parse_hash
9: uint, ; sign_counter
? 10: musig2-nonce-data, ; musig2_nonce (Round 1 응답)
? 11: error-detail ; error (실패 시)
}
signature-data = {
1: uint, ; chain_type
2: bytes, ; sig_bytes (64B Schnorr / 65B ECDSA)
3: bytes .size 33, ; pubkey
4: bytes ; derivation_path (바이너리)
}
musig2-nonce-data = {
1: bytes .size 66 ; public_nonce
}
error-detail = {
1: uint, ; error_code
? 2: text, ; error_message
? 3: policy-violation-info ; policy_info
}
policy-violation-info = {
1: uint, ; violated_policy (0x01-0x04)
2: bytes .size 32, ; current_value
3: bytes .size 32 ; requested_value
}
3.4. 정책 업데이트¶
; D'CENT 정책 업데이트
; UR Type: dcent-policy-update
; CBOR Tag: 45010
dcent-policy-update = {
1: uint, ; version
2: uint, ; message_type (0x10)
3: bytes .size 16, ; request_id
4: uint, ; timestamp_ref
5: bytes .size 32, ; payload_hash
6: policy-data, ; policy_data
7: [+ admin-signature], ; admin_signatures
8: uint ; quorum_threshold
}
policy-data = {
1: uint, ; version (단조 증가)
2: uint, ; target_policy (0x01-0x04)
3: bytes .size 32, ; previous_value
4: bytes .size 32, ; new_value
5: uint ; cooldown_seconds
}
admin-signature = {
1: bytes .size 33, ; pubkey
2: bytes .size 64 ; signature (ECDSA)
}
4. Animated QR 프로토콜 상세¶
4.1. Fountain 코드 기반 멀티파트 분할¶
Animated QR은 Blockchain Commons의 UR v2 Fountain 코드를 사용하여 대용량 데이터를 여러 QR 프레임으로 분할한다.
Fountain 코드 (LT Code) 특성: - Rateless: 수신측이 충분한 프레임을 수신하면 원본 복원 가능 - 순서 무관: 어떤 프레임부터 수신해도 복원 가능 - 오류 복구: 일부 프레임 손실 시에도 추가 프레임으로 복구
4.2. QR 프레임 구조¶
┌──────────────────────────────────────┐
│ QR Frame │
│ │
│ UR Part Header: │
│ sequence_number: uint32 │
│ sequence_count: uint32 │
│ message_length: uint32 │
│ checksum: uint32 (CRC32) │
│ │
│ Fragment Data: │
│ fragment_bytes: bytes │
│ (XOR-mixed for Fountain coding) │
│ │
│ UR Encoding: │
│ ur:<type>/[seq-num]-[seq-count]/ │
│ <bytewords-encoded-fragment> │
└──────────────────────────────────────┘
4.3. QR 코드 파라미터¶
| 파라미터 | 권장 값 | 범위 | 근거 |
|---|---|---|---|
| QR 버전 | 12-15 | 1-40 | 카메라 해상도와 인식률 최적 밸런스 |
| 에러 보정 | Level L (7%) | L/M/Q/H | 에어갭 환경에서 육안 확인 가능, L로 최대 데이터 용량 확보 |
| 모듈 크기 | 8px 이상 | 4-16px | 카메라 초점 거리 30-50cm 기준 |
| 프레임당 데이터 | ~500 bytes | 100-1000B | 인식률 99%+ 유지 범위 |
| 프레임 전환 속도 | 100ms | 50-500ms | 기본 100ms, 사용자 조절 가능 |
| 프레임 표시 시간 | 무제한 반복 | - | Fountain 코드이므로 무한 반복 표시 |
4.4. 페이로드 크기별 QR 파라미터¶
| 페이로드 크기 | 예상 프레임 수 | 전송 시간 (100ms) | 적합 사용 |
|---|---|---|---|
| < 500B | 1 (정적 QR) | 즉시 | EVM 단순 전송 응답 |
| 500B - 2KB | 4-8 프레임 | 0.4-0.8초 | EVM 서명 요청, BTC 단순 PSBT |
| 2KB - 10KB | 8-40 프레임 | 0.8-4초 | BTC 다중 입력 PSBT |
| 10KB - 50KB | 40-200 프레임 | 4-20초 | MuSig2 멀티 서명자 PSBT |
| > 50KB | 200+ 프레임 | 20초+ | 배치 트랜잭션 (QR 필수) |
4.5. Animated QR 구현 사양¶
송신측 (QR 생성): 1. 원본 데이터를 CBOR 인코딩 2. UR 래핑 (type + CBOR) 3. Fountain 코드로 프래그먼트 생성 (원본 프래그먼트 수의 2-3배 여분) 4. 각 프래그먼트를 Bytewords로 인코딩 5. QR 코드로 렌더링 → 순차 표시 (100ms 간격) 6. 전체 프래그먼트를 무한 반복
수신측 (QR 스캔): 1. 카메라로 QR 프레임 연속 캡처 2. Bytewords 디코딩 → Fountain 프래그먼트 추출 3. 충분한 프래그먼트 수집 시 LT 디코딩으로 원본 복원 4. UR 파싱 → CBOR 디코딩 → 원본 데이터 추출 5. payload_hash로 무결성 검증
5. USB-C 통신 프로토콜 명세¶
D'CENT X 콜드월렛은 오프라인 서명 앱(태블릿/PC)과 USB-C로 통신한다. D'CENT 기존 지문인증형 월렛의 USB 통신 프로토콜을 기반으로 엔터프라이즈 확장 커맨드를 추가한다.
참고: D'CENT X의 NFC 인터페이스는 리커버리카드 전용이며, 서명 채널로 사용하지 않는다.
5.1. USB HID 프로토콜 구조¶
D'CENT X는 USB HID(Human Interface Device) 클래스로 동작하여 별도 드라이버 없이 연결된다.
USB HID Report 구조:
┌──────────┬──────────┬──────┬────────┐
│ Report ID│ Cmd Type │ Len │ Data │
│ 1B │ 1B │ 2B │ 0-60B │
└──────────┴──────────┴──────┴────────┘
HID Report 크기: 64 bytes (USB Full-Speed HID 표준)
대용량 페이로드 전송: 64B 패킷 단위로 분할 전송. 기존 D'CENT USB 프로토콜의 청킹 메커니즘을 재사용한다.
[패킷 1] Report=01 Cmd=SIGN_REQ Len=총길이 Data=[60B] → ACK
[패킷 2] Report=01 Cmd=CONTINUE Len=0 Data=[60B] → ACK
[패킷 N] Report=01 Cmd=END Len=0 Data=[나머지] → Processing
[응답] Report=01 Cmd=RESPONSE Len=응답길이 Data=[...] → Complete
5.2. D'CENT Enterprise 커맨드 정의¶
| Cmd Type | 값 | 설명 | 페이로드 |
|---|---|---|---|
| SIGN_REQUEST | 0x10 | 서명 요청 전달 | CBOR 인코딩 SignRequest |
| GET_SIGNATURE | 0x20 | 서명 결과 수신 | - |
| POLICY_UPDATE | 0x30 | 정책 업데이트 | CBOR 인코딩 PolicyUpdate |
| GET_STATUS | 0x40 | 디바이스 상태 조회 | - |
| VERIFY_WHITELIST | 0x50 | 화이트리스트 검증 | CBOR 인코딩 WhitelistVerifyRequest |
5.3. 응답 상태 코드¶
| 코드 | 의미 | 처리 |
|---|---|---|
0x00 |
성공 | 정상 처리 |
0x01 |
데이터 수신 중 | 추가 패킷 대기 |
0x10 |
사용자 거부 | 물리 버튼 거부 |
0x20 |
정책 위반 | 정책 위반 상세 포함 |
0x30 |
WYSIWYS 불일치 | 서명 거부됨 |
0x31 |
WYSIWYS 잠금 (24h) | 잠금 해제 대기 |
0x40 |
파싱 에러 | 데이터 포맷 확인 |
0x82 |
보안 조건 미충족 | PIN 입력 필요 |
0xF0 |
내부 오류 | SE 상태 확인 |
5.4. USB-C vs 기존 NFC APDU 비교¶
| 항목 | USB-C (현재) | NFC APDU (미사용) |
|---|---|---|
| 패킷 크기 | 64B HID Report | 255B APDU |
| 대용량 전송 | 분할 전송 (사실상 무제한) | APDU 체이닝 (~4KB 한계) |
| 연결 안정성 | 높음 (유선) | 낮음 (접촉 유지 필요) |
| 세션 보안 | 물리적 격리 (USB 케이블) | ECDH + AES-128-CCM 필요 |
| 드라이버 | 불필요 (HID 클래스) | NFC 리더 필요 |
| 속도 | 12 Mbps (Full-Speed) | ~424 kbps |
5.5. USB-C 보안 고려사항¶
USB-C는 물리적 유선 연결이므로 NFC의 도청/릴레이 위협이 존재하지 않는다. 다만:
- 악성 USB 디바이스: MDM으로 오프라인 앱 태블릿에서 허용 USB 디바이스를 D'CENT X로 제한
- USB 데이터 유출: 오프라인 앱이 USB 통신 대상을 D'CENT X Vendor ID/Product ID로 검증
- 세션 암호화: 물리적 격리 환경에서는 선택적. 추가 보안이 필요한 경우 ECDH 세션 키 교환 적용 가능
6. 채널 구성 및 데이터 경로¶
6.1. 구간별 채널 정의¶
대시보드 ◄──── QR (UR Animated) ────► 오프라인 앱 ◄──── USB-C ────► D'CENT X
에어갭 경계 유선 직결
| 구간 | 채널 | 역할 |
|---|---|---|
| 대시보드 ↔ 오프라인 앱 | QR (Animated QR / Fountain Code) | 에어갭 경계. 네트워크 격리 유지. |
| 오프라인 앱 ↔ D'CENT X | USB-C (HID) | 유선 직결. SE 보안 경계 (키는 SE 외부 미노출). |
6.2. 데이터 경로별 채널¶
| 방향 | 경로 | 채널 |
|---|---|---|
| → | 대시보드 → 오프라인 앱 | 앱 카메라로 대시보드 QR 촬영 |
| → | 오프라인 앱 → D'CENT X | USB-C 커맨드 전송 |
| ← | D'CENT X → 오프라인 앱 | USB-C 응답 수신 |
| ← | 오프라인 앱 → 대시보드 | 앱 화면에 QR 표시 → 대시보드 스캐너 촬영 |
6.3. 보안 모델¶
에어갭 경계는 대시보드 ↔ 오프라인 서명 유닛(앱+디바이스) 사이의 QR 채널 하나이다. 오프라인 앱과 D'CENT X는 USB-C로 직결된 하나의 오프라인 서명 유닛을 구성한다.
핵심 보안 보장: - 네트워크 격리: QR 에어갭으로 대시보드 침해가 서명 유닛에 전파 불가 - 키 격리: USB-C 연결에서도 프라이빗 키는 SE 외부로 나가지 않음 - WYSIWYS: SE가 TX를 독립 파싱하여 앱 변조를 감지
7. 에어갭 데이터 암호화 및 무결성¶
7.1. 무결성 보장¶
모든 에어갭 메시지는 payload_hash 필드를 통해 무결성을 검증한다:
payload_hash = SHA-256(header.version || header.message_type ||
header.request_id || payload_body)
수신측 검증: 1. 메시지 수신 → payload_body에서 SHA-256 해시 계산 2. 계산 결과와 header.payload_hash 대조 3. 불일치 시 메시지 폐기 + 에러 반환
7.2. 리플레이 방지¶
세션 관리:
request_id (UUID v4) — 각 요청에 고유 ID
SE 서명 카운터 — 단조 증가, 이전 카운터의 요청 거부
리플레이 공격 방어:
1. request_id 중복 확인 (오프라인 앱 레벨)
2. sign_counter 검증 (SE 레벨)
3. 세션 타임아웃 (4시간 기본)
7.3. 채널 암호화¶
| 채널 | 암호화 | 근거 |
|---|---|---|
| QR (대시보드↔앱) | 선택적 | 시각적 채널이므로 숄더 서핑 외 중간자 공격 어려움. PSBT는 공개 데이터 위주. WYSIWYS 해시 비교가 무결성 보장. |
| USB-C (앱↔디바이스) | 선택적 | 물리적 유선 연결이므로 도청/릴레이 위협 없음. 오프라인 환경에서는 암호화 불필요. 추가 보안 시 ECDH 세션 키 교환 적용 가능. |
8. 프로토콜 테스트 벡터¶
8.1. BTC 단순 전송 서명 요청¶
시나리오: 1 BTC를 bc1q...addr로 전송하는 단일 입력 PSBT
Request (CBOR Hex):
A8 ; map(8)
01 ; key: version
01 ; value: 1
02 ; key: message_type
01 ; value: 0x01 (BTC_SIGN_REQUEST)
03 ; key: request_id
50 ; bytes(16)
550E8400E29B41D4A716446655440000
06 ; key: chain_type
00 ; value: 0x00 (BTC)
08 ; key: psbt
59 01F4 ; bytes(500) — PSBT 바이너리
[PSBT binary data...]
0D ; key: app_hash
58 20 ; bytes(32)
[SHA-256 hash 32 bytes]
0B ; key: policy_version
19 0005 ; value: 5
0C ; key: whitelist_version
19 000A ; value: 10
UR Encoding:
ur:dcent-sign-request/[bytewords...]
Response (성공, CBOR Hex):
A5 ; map(5)
06 ; key: status_code
00 ; value: 0x00 (SUCCESS)
07 ; key: signature
A4 ; map(4) — signature-data
01 00 ; chain_type: BTC
02 58 40 ; sig_bytes: 64 bytes (Schnorr)
[64 bytes signature]
03 58 21 ; pubkey: 33 bytes
[33 bytes compressed pubkey]
04 46 ; derivation_path: 6 bytes
8680000080000000...
08 58 20 ; se_parse_hash: 32 bytes
[32 bytes hash]
09 19 012C ; sign_counter: 300
8.2. EVM ERC-20 전송 서명 요청¶
시나리오: 1000 USDT를 0x742d...로 전송
Request (핵심 필드):
message_type: 0x02 (EVM_SIGN_REQUEST)
chain_type: 0x01 (EVM)
chain_id: 1 (Ethereum)
transaction:
to: 0xdAC17F958D2ee523a2206206994597C13D831ec7 (USDT)
value: 0x00 (토큰 전송이므로 ETH value = 0)
data: 0xa9059cbb (transfer selector)
000000000000000000000000742d35Cc6634... (recipient, padded)
00000000000000000000000000000000003B9ACA00 (amount: 1000 * 10^6)
gas_limit: 80000
max_fee_per_gas: 30000000000 (30 Gwei)
nonce: 42
token_info:
symbol: "USDT"
decimals: 6
contract_address: 0xdAC17F958D2ee523a2206206994597C13D831ec7
app_hash: [SHA-256 of parsed fields]
8.3. 정책 업데이트 메시지¶
시나리오: 건당 한도를 10 BTC에서 20 BTC로 변경 (Admin 3-of-5 서명)
PolicyUpdate:
policy_data:
version: 6 (현재 5에서 증가)
target_policy: 0x01 (AMOUNT_LIMIT)
previous_value: 0x00000000 3B9ACA00 (10 BTC = 10^9 sat, 32B padded)
new_value: 0x00000000 77359400 (20 BTC = 2*10^9 sat, 32B padded)
cooldown_seconds: 86400 (24시간)
admin_signatures: [
{pubkey: [33B], signature: [64B]}, ; Admin 1
{pubkey: [33B], signature: [64B]}, ; Admin 2
{pubkey: [33B], signature: [64B]} ; Admin 3
]
quorum_threshold: 3
SE 처리:
1. Admin 서명 3개 검증 (공개키 매칭)
2. version == current + 1 확인 (6 == 5+1)
3. previous_value == 현재 SE 저장값 확인
4. 물리 버튼 2회 확인
5. 쿨다운 시작 (24시간 후 적용)
본 문서는 Phase 5 System Architecture Design의 일부로, Phase 3 에어갭 서명 플로우의 프로토콜 수준 상세화이다. component-data-flow.md의 인터페이스 메시지 구조를 UR/CBOR/APDU 바이트 수준으로 구체화한다. D'CENT X 기존 USB-C 통신 프로토콜을 기반으로 엔터프라이즈 확장 커맨드를 추가한다. NFC는 리커버리카드 전용으로 사용한다.
관련 문서¶
- 체인 추상화 레이어 인터페이스 정의서 -- 시스템 아키텍처
- 컴포넌트 간 데이터 흐름 및 인터페이스 명세 -- 시스템 아키텍처
- 온라인 대시보드 백엔드 아키텍처 설계서 -- 시스템 아키텍처
- D'CENT X 펌웨어 서명 인터페이스 설계서 -- 시스템 아키텍처
- 오프라인 서명 앱 아키텍처 설계서 -- 시스템 아키텍처
- STRIDE 기반 보안 위협 모델 -- 시스템 아키텍처
- 3-Zone 보안 아키텍처 설계서 -- 시스템 아키텍처