v0.0
엔터프라이즈 멀티체인 서명 펌웨어 요구사항 명세서¶
1. Executive Summary¶
본 문서는 D'CENT X 엔터프라이즈 콜드월렛의 Secure Element(SE) 내부 멀티체인 서명 기능에 대한 펌웨어 요구사항을 정의한다. BTC(PSBT + Schnorr + MuSig2), EVM(RLP + ECDSA + EIP-712), ERC-20/스테이블코인 서명을 SE 리소스 제약 내에서 구현하기 위한 상세 요구사항을 FW-SIGN- ID 체계로 관리한다.
지원 범위: - BTC: PSBT(BIP-174/370) 파싱, Schnorr 서명(BIP-340), MuSig2 2라운드 프로토콜(BIP-327), BIP-373 PSBT 확장 - EVM: RLP 디코딩(EIP-1559 Type 2), ECDSA 서명(secp256k1), EIP-155 Chain ID, EIP-712 Structured Data - ERC-20: ABI 디코딩(transfer, approve, transferFrom), SE 토큰 DB, 함수 셀렉터 DB - 확장: Chain Parser Interface, 향후 체인(Solana, Cosmos, Tron) 파서 추가 구조
설계 기반: - Phase 5 firmware-signing-interface.md — SE 앱릿 구조 및 Signing Applet 인터페이스 - Phase 5 airgap-communication-protocol.md — CBOR 스키마, NFC APDU 커맨드셋 - Phase 5 chain-abstraction-layer.md — 체인 추상화 인터페이스 - Phase 6 hardware-capability-assessment.md — SE 리소스 버짓
2. BTC 서명 펌웨어 요구사항¶
2.1. PSBT 파싱 및 서명¶
SE 내부에서 PSBT(Partially Signed Bitcoin Transaction) 바이너리를 직접 파싱하여 서명에 필요한 데이터를 추출한다.
PSBT 디코딩 요구사항:
| 항목 | 요구사항 | SE 리소스 영향 |
|---|---|---|
| PSBTv0 (BIP-174) 디코딩 | 글로벌 맵 + 입력 맵 + 출력 맵 파싱 | RAM 8KB 내 스트리밍 파싱 |
| PSBTv2 (BIP-370) 디코딩 | Creator/Constructor/Signer 역할 분리 지원 | PSBTv0과 동일 버퍼 공유 |
| 입력(Input) 파싱 | UTXO 참조(txid+vout), scriptPubKey, witness 추출 | 입력당 ~100B |
| 출력(Output) 파싱 | scriptPubKey → 주소 변환, value(satoshis) 추출 | 출력당 ~60B |
| Change Output 감지 | BIP-44/84/86 경로 매칭으로 잔금 출력 식별 | 키 파생 경로 비교 |
| 수수료 계산 | 입력 합계 - 출력 합계 | uint64 연산 |
| Sighash 계산 | BIP-341 Taproot sighash (SHA-256 기반) | SHA-256 HW 가속 |
스트리밍 파싱 전략:
대용량 PSBT(>8KB) 처리를 위한 single-pass 스트리밍 파싱:
[1] PSBT 헤더 파싱 (magic bytes + 버전)
[2] 글로벌 맵 스트리밍 파싱 → 필요 필드만 추출 + 즉시 해시 업데이트
[3] 입력 맵 순차 파싱 (N개):
- 각 입력의 sighash 기여분 계산
- UTXO 참조 + 금액 추출
- 처리 완료 후 입력 버퍼 해제
[4] 출력 맵 순차 파싱 (M개):
- 수신 주소 + 금액 추출
- Change 출력 식별
- WYSIWYS 표시 데이터 생성
[5] 최종 sighash 완성 → Schnorr 서명 입력
2.2. Schnorr 서명 (BIP-340)¶
| 항목 | 요구사항 | 비고 |
|---|---|---|
| 커브 | secp256k1 | ECDSA와 동일 커브, HW 가속 공유 |
| 서명 크기 | 64 bytes (r, s) | ECDSA(65B)보다 1바이트 작음 |
| Nonce 생성 | RFC 6979 확장 (결정적 + 보조 랜덤) | TRNG 시드 혼합 |
| 서명 검증 | batch verification 미지원 (SE 리소스) | 단일 검증만 |
| X-only 공개키 | 32 bytes (y 좌표 제거) | BIP-340 표준 |
| Tagged Hash | SHA-256("BIP0340/challenge" || ...) | BIP-340 표준 해시 |
2.3. MuSig2 (BIP-327) 2라운드 프로토콜¶
D'CENT X SE 내부에서 MuSig2 서명의 한 참여자(signer) 역할을 수행한다.
Round 1: Nonce 생성
입력: derivation_path, message_hash(선택)
처리:
1. 결정적 nonce 시드 생성:
nonce_seed = HMAC-SHA256(private_key, session_id || counter || random)
- session_id: 요청 UUID
- counter: SE 서명 카운터
- random: TRNG 8 bytes (추가 엔트로피)
2. 두 개의 public nonce 생성:
R1 = nonce_seed_1 * G
R2 = nonce_seed_2 * G
3. 상태 저장:
MuSig2Session {
session_id: bytes(16) // UUID
nonce_secret_1: bytes(32) // 비밀 nonce 1
nonce_secret_2: bytes(32) // 비밀 nonce 2
created_counter: uint32 // 생성 시점 카운터
timeout_counter: uint32 // 만료 카운터 (현재 + 100)
}
4. public_nonce = R1 || R2 (66 bytes) 반환
보안 제약:
- 최대 2개 동시 MuSig2 세션 (SE 저장 공간 제약)
- 세션 타임아웃: 서명 카운터 100 경과 시 자동 만료
- 동일 nonce 재사용 절대 금지 (private key 노출 위험)
Round 2: Partial Signature 생성
입력: session_id, aggregated_nonce(66B), sighash
처리:
1. 세션 조회: session_id로 저장된 MuSig2Session 검색
- 미발견 → MUSIG2_SESSION_NOT_FOUND (0x35) 에러
- 타임아웃 → MUSIG2_SESSION_EXPIRED (0x36) 에러
2. Aggregated nonce 검증:
R_agg = NonceAgg([R1_all, R2_all]) // 모든 참여자 nonce 집계
3. 부분 서명 생성:
partial_sig = Sign_partial(nonce_secret, private_key, R_agg, sighash)
4. 세션 정리: nonce_secret 즉시 삭제 (1회용)
5. partial_sig (32 bytes) 반환
보안 제약:
- Round 2는 동일 세션에 대해 1회만 실행 가능
- 실행 후 nonce 비밀값 즉시 폐기
- 동일 sighash에 대한 다중 partial_sig 생성 금지
SE 내부 MuSig2 상태 관리:
| 항목 | 크기 | 설명 |
|---|---|---|
| 세션 저장 | 66B/세션 x 2 = 132B | EEPROM Secure Storage |
| 임시 연산 | ~256B | RAM (Round 2 시) |
| 성능 | Round 1: ~110ms, Round 2: ~120ms | SE HW 가속 기준 |
2.4. BIP-373 PSBT 확장 필드¶
MuSig2 관련 PSBT 확장 필드의 SE 내부 처리:
| PSBT 필드 | 키 타입 | 처리 |
|---|---|---|
| MUSIG2_PARTICIPANT_PUBKEYS | 입력 맵 | 참여자 공개키 목록 추출 → Signer 인덱스 확인 |
| MUSIG2_PUB_NONCE | 입력 맵 | 타 참여자 public nonce 추출 → Round 2 입력 |
| MUSIG2_PARTIAL_SIG | 출력 | SE에서 생성한 partial signature 기록 |
2.5. 키 파생¶
| 경로 | 용도 | BIP | 비고 |
|---|---|---|---|
| m/44'/0'/N'/0/M | BTC Legacy (P2PKH) | BIP-44 | 하위 호환 |
| m/84'/0'/N'/0/M | BTC Native SegWit (P2WPKH) | BIP-84 | 권장 |
| m/86'/0'/N'/0/M | BTC Taproot (P2TR) | BIP-86 | MuSig2 필수 |
SE 내부 키 파생 구현: - BIP-32 HMAC-SHA512 체인 키 유도 - 하드닝 키 파생(m/44'h) 지원 필수 - 최대 파생 깊이: 5단계 (m/purpose'/coin'/account'/change/index) - 파생 시간: ~20ms/단계, 5단계 = ~100ms
2.6. 성능 요구¶
| 시나리오 | 자동 처리 시간 목표 | 항목 |
|---|---|---|
| BTC 단순 전송 (1 입력, 2 출력) | < 2초 | PSBT 파싱 + sighash + Schnorr 서명 |
| BTC 다중 입력 (5 입력, 2 출력) | < 4초 | 스트리밍 PSBT 파싱 |
| MuSig2 Round 1 | < 1초 | Nonce 생성 + 저장 |
| MuSig2 Round 2 | < 1.5초 | 세션 조회 + partial sig + 정리 |
| 전체 BTC 서명 파이프라인 | < 5초 | WYSIWYS + 정책 + 서명 (사용자 대기 제외) |
3. EVM 서명 펌웨어 요구사항¶
3.1. RLP 디코딩¶
SE 내부에서 EVM 트랜잭션의 RLP(Recursive Length Prefix) 인코딩을 디코딩한다.
지원 트랜잭션 타입:
| 타입 | 식별 | 디코딩 | 우선순위 |
|---|---|---|---|
| Legacy (Type 0) | 첫 바이트 >= 0xC0 | 직접 RLP | 필수 |
| EIP-2930 (Type 1) | 첫 바이트 = 0x01 | 0x01 || RLP | 높음 |
| EIP-1559 (Type 2) | 첫 바이트 = 0x02 | 0x02 || RLP | 필수 (기본) |
| EIP-4844 (Type 3) | 첫 바이트 = 0x03 | 향후 확장 | 중간 |
RLP 디코딩 구현:
RLP 디코딩 규칙:
- 단일 바이트 [0x00, 0x7F]: 값 자체
- 짧은 문자열 [0x80, 0xB7]: 길이 = 첫 바이트 - 0x80
- 긴 문자열 [0xB8, 0xBF]: 길이의 바이트 수 = 첫 바이트 - 0xB7
- 짧은 리스트 [0xC0, 0xF7]: 길이 = 첫 바이트 - 0xC0
- 긴 리스트 [0xF8, 0xFF]: 길이의 바이트 수 = 첫 바이트 - 0xF7
EIP-1559 필드 추출:
[chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas,
gas_limit, to, value, data, access_list, v, r, s]
SE 리소스 영향: - RLP 디코딩 버퍼: ~2KB (최대 트랜잭션 크기 제한) - calldata 파싱: 함수 셀렉터(4B) + 파라미터 → 별도 ABI 디코딩
3.2. ECDSA 서명 (secp256k1)¶
| 항목 | 요구사항 |
|---|---|
| 해시 알고리즘 | keccak256 (SHA-3 변형) |
| 서명 출력 | v (1B) + r (32B) + s (32B) = 65 bytes |
| v 값 | EIP-155: chain_id * 2 + 35/36 |
| Nonce 생성 | RFC 6979 (결정적) |
| 서명 정규화 | Low-S normalization (s < n/2) |
| keccak256 구현 | SE 소프트웨어 구현 필요 (SHA-256과 다름) [HW-CONFIRM] |
keccak256 관련 참고: 대부분의 SE는 SHA-256 하드웨어 가속을 제공하나, keccak256은 별도 구현이 필요할 수 있다. EVM 서명 성능은 keccak256 구현 방식(HW/SW)에 크게 의존한다.
3.3. EIP-155 Chain ID¶
서명에 Chain ID를 포함하여 크로스체인 리플레이 공격을 방지한다.
| Chain ID | 네트워크 | SE 표시명 |
|---|---|---|
| 1 | Ethereum Mainnet | "Ethereum" |
| 137 | Polygon | "Polygon" |
| 56 | BNB Smart Chain | "BNB Chain" |
| 42161 | Arbitrum One | "Arbitrum" |
| 10 | Optimism | "Optimism" |
| 43114 | Avalanche C-Chain | "Avalanche" |
SE 내부에 상위 10개 Chain ID → 네트워크명 매핑 테이블을 저장한다. 미등록 Chain ID는 숫자 표시 ("Chain #[ID]").
3.4. EIP-712 Structured Data¶
EIP-712 구조화 데이터 서명 지원 (Safe multisig 등에서 사용):
EIP-712 서명 프로세스:
1. domain_separator (32B) 수신
2. message_hash (32B) 수신
3. 최종 해시: keccak256("\x19\x01" || domain_separator || message_hash)
4. ECDSA 서명 생성
WYSIWYS 표시:
- domain: 앱 이름, 체인 ID, 컨트랙트 주소
- message: typed data 주요 필드 (파라미터 표시)
- 파싱 불가 시: Raw hash 표시 + CRITICAL 경고
3.5. 키 파생¶
| 경로 | 용도 | 비고 |
|---|---|---|
| m/44'/60'/0'/0/N | Ethereum 표준 | MetaMask 호환 |
| m/44'/60'/N'/0/0 | Ledger 방식 | 계정 인덱스 위치 차이 |
| m/44'/966'/0'/0/N | Polygon (SLIP-0044) | 독립 경로 사용 시 |
기본 채택: m/44'/60'/0'/0/N (Ethereum 표준, MetaMask 호환)
3.6. 성능 요구¶
| 시나리오 | 자동 처리 시간 목표 |
|---|---|
| EVM 단순 전송 (ETH transfer) | < 1초 |
| ERC-20 transfer | < 1.5초 |
| EIP-712 Safe execTransaction | < 2초 |
| 전체 EVM 서명 파이프라인 | < 2초 (사용자 대기 제외) |
4. ERC-20/스테이블코인 서명 지원¶
4.1. ABI 디코딩¶
SE 내부에서 EVM calldata의 ABI(Application Binary Interface)를 디코딩한다.
지원 함수:
| 함수 | Selector | 파라미터 | 디코딩 결과 |
|---|---|---|---|
transfer(address,uint256) |
0xa9059cbb | to(20B), amount(32B) | 수신자 + 금액 |
approve(address,uint256) |
0x095ea7b3 | spender(20B), amount(32B) | 승인 대상 + 한도 |
transferFrom(address,address,uint256) |
0x23b872dd | from, to, amount | 출금원 + 수신자 + 금액 |
ABI 디코딩 로직:
ABI 디코딩:
1. calldata 첫 4바이트 → 함수 셀렉터 추출
2. SE 함수 셀렉터 DB에서 매칭 조회
3. 매칭 시: 파라미터 ABI 디코딩 (32B 단위 파싱)
4. 미매칭 시: CAUTION 경고 + Raw Hex 표시
파라미터 디코딩 규칙:
- address: 32B 중 하위 20B 추출
- uint256: 32B big-endian → 금액 변환
- bytes: offset + length 기반 동적 디코딩
- 중첩 구조체: 재귀적 디코딩 (최대 depth 3)
4.2. SE 내부 토큰 데이터베이스¶
초기 등록 토큰 (5종):
| 토큰 | 체인 | 컨트랙트 주소 | Decimals | 크기 |
|---|---|---|---|---|
| USDT | Ethereum (1) | 0xdAC17F958D2ee523a2206206994597C13D831ec7 | 6 | 40B |
| USDC | Ethereum (1) | 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 | 6 | 40B |
| DAI | Ethereum (1) | 0x6B175474E89094C44Da98b954EedeAC495271d0F | 18 | 40B |
| WBTC | Ethereum (1) | 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599 | 8 | 40B |
| USDT | Tron (195) | TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t | 6 | 40B |
토큰 DB 구조:
TokenEntry {
chain_id: uint32 // 4B
contract_address: bytes(20) // 20B
decimals: uint8 // 1B
symbol: bytes(8) // 8B (UTF-8, null-padded)
flags: uint8 // 1B (검증 상태 등)
reserved: bytes(6) // 6B (향후 확장)
}
// 항목당 40 bytes, 최대 50개 = 2,000B (2KB)
토큰 DB 업데이트: - 업데이트 방식: 관리자 쿼럼 승인 + 에어갭 펌웨어 업데이트 경유 - 무결성 검증: 전체 DB의 SHA-256 해시를 SE 내부에 별도 저장 - 미등록 토큰: decimals 0 처리 + "소수점 미확인" 경고 WYSIWYS 표시
4.3. SE 내부 함수 셀렉터 데이터베이스¶
초기 등록 셀렉터 (10종):
| # | 셀렉터 | 함수 | 카테고리 | 위험도 |
|---|---|---|---|---|
| 1 | 0xa9059cbb | ERC-20 transfer | 토큰 | 낮음 |
| 2 | 0x095ea7b3 | ERC-20 approve | 토큰 | 높음 (무제한 승인 가능) |
| 3 | 0x23b872dd | ERC-20 transferFrom | 토큰 | 중간 |
| 4 | 0x42842e0e | ERC-721 safeTransferFrom | NFT | 중간 |
| 5 | 0x6a761202 | Safe execTransaction | 멀티시그 | 높음 |
| 6 | 0x0d582f13 | Safe addOwnerWithThreshold | 멀티시그 | 최고 |
| 7 | 0x694e80c3 | Safe changeThreshold | 멀티시그 | 최고 |
| 8 | 0xa22cb465 | ERC-721 setApprovalForAll | NFT | 높음 |
| 9 | 0x2e1a7d4d | WETH withdraw | DeFi | 중간 |
| 10 | 0xd0e30db0 | WETH deposit | DeFi | 낮음 |
셀렉터 DB 구조:
SelectorEntry {
selector: bytes(4) // 4B — 함수 셀렉터
category: uint8 // 1B — 카테고리 (0=토큰, 1=NFT, 2=멀티시그, 3=DeFi, 4=기타)
risk_level: uint8 // 1B — 위험도 (0=낮음, 1=중간, 2=높음, 3=최고)
param_count: uint8 // 1B — 파라미터 수
param_types: bytes(4) // 4B — 파라미터 타입 (address=0x01, uint256=0x02, bytes=0x03, ...)
display_name_offset: uint8 // 1B — 표시명 오프셋 (별도 문자열 테이블)
flags: bytes(4) // 4B — 플래그 (approve 무제한 감지 등)
}
// 항목당 16 bytes, 최대 256개 = 4,096B (4KB)
5. 향후 체인 확장 요구사항¶
5.1. Chain Parser Interface¶
SE 내부에서 체인별 파서를 분리 관리하는 표준 인터페이스:
ChainParserInterface {
// 체인 식별
getChainId() → uint32 // SLIP-0044 coin type
getSupportedTxTypes() → [uint8] // 지원 트랜잭션 타입
// 트랜잭션 파싱
parse(raw_tx: bytes) → ParsedTransaction // 바이너리 → 구조체
calculateSighash(parsed: ParsedTransaction, input_index: uint8) → bytes(32)
// WYSIWYS 지원
formatDisplay(parsed: ParsedTransaction) → [DisplayPage]
getWarningLevel(parsed: ParsedTransaction) → WarningLevel
// 서명 생성
getSignAlgorithm() → SignAlgorithm // ECDSA, Schnorr, Ed25519
getDerivationPath(account: uint32) → string
}
ParsedTransaction {
chain_id: uint32
tx_type: uint8
recipients: [{address: bytes, amount: bytes(32), asset_type: uint8}]
fee: {amount: bytes(32), unit: string}
nonce: uint64
metadata: bytes // 체인별 추가 데이터
}
SignAlgorithm: ECDSA_SECP256K1 | SCHNORR_SECP256K1 | EDDSA_ED25519
5.2. 확장 대상 체인¶
| 체인 | TX 포맷 | 서명 알고리즘 | SE 추가 리소스 | 도입 시기 |
|---|---|---|---|---|
| Tron | Protobuf | ECDSA (secp256k1) | 파서 ~2KB ROM | Phase 1 출시 (USDT) |
| Solana | Borsh | Ed25519 | Ed25519 HW 지원 필요, 파서 ~4KB | Phase 2 |
| Cosmos | Protobuf (amino/direct) | ECDSA (secp256k1) | 파서 ~2KB ROM | Phase 2 |
| Polkadot | SCALE | Sr25519 | 신규 커브 필요, 파서 ~4KB | Phase 3 |
5.3. 플러그인 방식 파서 로딩¶
펌웨어 업데이트를 통한 체인 파서 추가 구조:
파서 추가 워크플로우:
1. 신규 체인 파서 코드 개발 (ChainParserInterface 구현)
2. 펌웨어 빌드에 파서 포함
3. 코드 서명 + Admin 쿼럼 승인
4. 에어갭 펌웨어 업데이트로 설치
5. ChainRegistry에 자동 등록
ChainRegistry:
- 설치된 파서 목록 관리
- chain_id 기반 라우팅
- GetDeviceInfo 응답에 supported_chains 반영
6. Signing Applet 상세 요구사항 목록¶
firmware-signing-interface.md 섹션 3.4의 [1]~[6] 단계별 구현 요구사항:
| ID | 요구사항명 | 설명 | 우선순위 | SE 리소스 영향 | 연계 문서 |
|---|---|---|---|---|---|
| FW-SIGN-01 | PSBT v0/v2 바이너리 디코딩 | BIP-174/370 PSBT 바이너리를 SE 내부에서 파싱, 입력/출력/글로벌 맵 추출 | 필수 | RAM 8KB (스트리밍) | firmware-signing-interface.md [1] |
| FW-SIGN-02 | PSBT 스트리밍 파싱 엔진 | 8KB RAM 제약 내 single-pass 파싱, 청크 단위 해시 업데이트 | 필수 | RAM 8KB 최적화 | hardware-capability-assessment.md |
| FW-SIGN-03 | Schnorr 서명 생성 (BIP-340) | secp256k1 Schnorr 서명, Tagged Hash, X-only 공개키 | 필수 | RAM 512B, ~100ms | firmware-signing-interface.md [5] |
| FW-SIGN-04 | MuSig2 Round 1 — Nonce 생성 | 결정적 nonce 생성, public nonce 반환, 세션 상태 저장 | 필수 | 저장 66B/세션, ~110ms | BIP-327 |
| FW-SIGN-05 | MuSig2 Round 2 — Partial Sig | Aggregated nonce 수신, partial signature 생성, 세션 정리 | 필수 | RAM 256B, ~120ms | BIP-327 |
| FW-SIGN-06 | MuSig2 세션 관리 | 최대 2세션, 타임아웃(카운터 100), nonce 1회 사용 강제 | 필수 | 저장 132B | 섹션 2.3 |
| FW-SIGN-07 | BIP-373 PSBT 확장 필드 처리 | MuSig2 participant pubkeys, nonces, partial sigs 파싱 | 필수 | PSBT 파싱 확장 | BIP-373 |
| FW-SIGN-08 | EVM RLP 디코딩 | EIP-1559 Type 2 우선, Legacy/Type 1 호환, calldata 추출 | 필수 | RAM 2KB | 섹션 3.1 |
| FW-SIGN-09 | ECDSA 서명 (secp256k1) | keccak256 해시 + ECDSA 서명, v/r/s 반환, EIP-155 Chain ID | 필수 | RAM 256B, ~100ms | firmware-signing-interface.md [5] |
| FW-SIGN-10 | EIP-712 Structured Data 서명 | domain separator + message hash 처리, typed data 서명 | 필수 | RAM 128B | 섹션 3.4 |
| FW-SIGN-11 | ABI 디코딩 엔진 | 함수 셀렉터 매칭 + 파라미터 디코딩 (address, uint256, bytes) | 필수 | RAM 512B | 섹션 4.1 |
| FW-SIGN-12 | SE 토큰 DB 관리 | 토큰 등록/조회/업데이트, 초기 5종, 최대 50개, 무결성 해시 | 필수 | 저장 2KB | 섹션 4.2 |
| FW-SIGN-13 | SE 함수 셀렉터 DB 관리 | 셀렉터 등록/조회/업데이트, 초기 10종, 최대 256개 | 필수 | 저장 4KB | 섹션 4.3 |
| FW-SIGN-14 | BIP-32 HD 키 파생 | BIP-44/84/86 경로 지원, 최대 5단계, 하드닝 파생 | 필수 | RAM 128B, ~100ms | 섹션 2.5 |
| FW-SIGN-15 | Chain Parser Interface | chain_id 라우팅, parse/sighash/formatDisplay 인터페이스 | 높음 | 인터페이스 정의만 | 섹션 5.1 |
| FW-SIGN-16 | EIP-155 Chain ID 관리 | 상위 10개 Chain ID → 네트워크명 매핑, 미등록 ID 숫자 표시 | 높음 | 저장 ~200B | 섹션 3.3 |
| FW-SIGN-17 | Sighash 계산 (BTC) | BIP-341 Taproot sighash, SegWit sighash | 필수 | SHA-256 HW | 섹션 2.1 |
| FW-SIGN-18 | keccak256 구현 | EVM 서명용 keccak256 해시, HW 가속 또는 SW 구현 | 필수 | ROM ~2KB (SW 시) | 섹션 3.2 |
| FW-SIGN-19 | Nonce 생성 (RFC 6979) | 결정적 nonce (ECDSA/Schnorr 공용), TRNG 보조 엔트로피 | 필수 | RAM 64B | 섹션 2.2 |
| FW-SIGN-20 | 서명 카운터 관리 | 전역 카운터 단조 증가, EEPROM NV 저장, 서명 성공 시만 증가 | 필수 | 저장 4B | firmware-signing-interface.md [5] |
7. 테스트 벡터 및 검증 기준¶
7.1. 정상 케이스¶
TC-SIGN-01: BTC 단순 전송
입력:
- PSBT: 1 입력 (P2TR UTXO, 0.5 BTC) → 2 출력 (0.3 BTC 전송, 0.1999 BTC 잔금)
- 경로: m/86'/0'/0'/0/0
- 서명 방식: Schnorr (BIP-340)
기대 출력:
- SE_PARSE_HASH: SHA-256(recipient || 30000000 sat || 100000 sat fee || "BTC")
- 서명: 64 bytes Schnorr signature
- 카운터: 이전 + 1
- 응답 코드: 0x00 (SUCCESS)
TC-SIGN-02: MuSig2 2-of-2
Round 1:
입력: session_id, derivation_path m/86'/0'/0'/0/0
기대 출력: public_nonce (66 bytes), 세션 저장 확인
Round 2:
입력: session_id, aggregated_nonce, sighash
기대 출력: partial_sig (32 bytes), 세션 삭제 확인
검증: 2개 partial_sig 집계 → 유효한 Schnorr 서명
TC-SIGN-03: EVM 단순 전송
입력:
- RLP: EIP-1559 Type 2, chain_id=1, to=0x742d...bD14, value=1 ETH, data=0x
- 경로: m/44'/60'/0'/0/0
기대 출력:
- SE_PARSE_HASH: SHA-256(recipient || 1 ETH || gas_fee || chain_id=1)
- 서명: v (1B) + r (32B) + s (32B) = 65 bytes ECDSA
- 응답 코드: 0x00 (SUCCESS)
TC-SIGN-04: ERC-20 transfer
입력:
- RLP: to=USDT 컨트랙트, value=0, data=0xa9059cbb + recipient + 1000 USDT
- 토큰 DB: USDT (decimals=6)
기대 출력:
- WYSIWYS 표시: "토큰 전송: 1,000.00 USDT → 0x742d...bD14"
- 서명: ECDSA 65 bytes
- 응답 코드: 0x00 (SUCCESS)
7.2. 에러 케이스¶
| TC ID | 시나리오 | 기대 에러 코드 | 기대 동작 |
|---|---|---|---|
| TC-ERR-01 | 미지원 chain_type (0x03) | 0x40 (UNSUPPORTED_CHAIN) | 파싱 거부, 에러 반환 |
| TC-ERR-02 | 잘못된 PSBT (magic bytes 불일치) | 0x41 (PARSE_ERROR) | 파싱 실패, 에러 반환 |
| TC-ERR-03 | 미지원 EIP 타입 (Type 4) | 0x42 (UNSUPPORTED_TX_TYPE) | 에러 반환 |
| TC-ERR-04 | MuSig2 세션 미존재 | 0x35 (MUSIG2_SESSION_NOT_FOUND) | 에러 반환 |
| TC-ERR-05 | MuSig2 세션 타임아웃 | 0x36 (MUSIG2_SESSION_EXPIRED) | 세션 삭제 + 에러 |
| TC-ERR-06 | PSBT 크기 초과 (>100KB) | 0x43 (PAYLOAD_TOO_LARGE) | 수신 거부 |
| TC-ERR-07 | 정책 버전 불일치 | 0x24 (POLICY_VERSION_MISMATCH) | 서명 거부 |
| TC-ERR-08 | 미등록 함수 셀렉터 | (에러 아님) | CAUTION 경고 + 사용자 판단 |
본 문서는 Phase 6 Firmware Requirements의 두 번째 산출물(1/2)로, D'CENT X SE 멀티체인 서명 기능의 펌웨어 요구사항을 정의한다. Phase 5 firmware-signing-interface.md의 Signing Applet 구현을 상세화하며, FW-SIGN-01~20의 요구사항 ID 체계로 추적 가능하다. Phase 4 wysiwys-design.md의 체인별 파싱 범위와 Phase 5 chain-abstraction-layer.md의 체인 추상화 인터페이스를 펌웨어 구현 수준으로 변환하였다.
관련 문서¶
- D'CENT X 엔터프라이즈 펌웨어 팀 핸드오프 종합 요약서 -- 펌웨어 요구사항
- 펌웨어 업데이트 보안 메커니즘 설계서 -- 펌웨어 요구사항
- D'CENT X 하드웨어 역량 검증 및 제약사항 문서 -- 펌웨어 요구사항
- WYSIWYS 디스플레이 펌웨어 요구사항 명세서 -- 펌웨어 요구사항