서명 전 서명 기기의 확인 항목
온라인 기기에서 온 QR 코드는 신뢰할 수 없는 입력입니다. BitTool Signer는 모든 요청을 잠재적으로 악의적인 것으로 간주하고, 모든 체인에 같은 파이프라인을 적용합니다.
- 1QR 프레임을 조립하고 요청(CBOR, PSBT, 트랜잭션 바이트)을 엄격하게 디코딩합니다.
- 2서명할 정확한 바이트를 파싱해 검토 화면(금액, 수신자, 수수료, 컨트랙트 호출, 경고)을 만듭니다.
- 3공개 키만으로 요청을 로컬 지갑과 매칭하고, 요청 해시에 묶인 서명 계획을 확정합니다.
- 사용자가 검토하고 위험을 확인한 뒤 비밀번호를 입력 ▶
- 4시드에서 키를 다시 파생하고, 사용하기 전에 계획과 대조합니다.
- 5서명하고, 새 서명을 검증한 뒤, 요청과 같은 형식으로 응답을 인코딩합니다.
엄격한 디코딩
체인별 로직이 실행되기 전에 전송 계층부터 검증합니다.
- CBOR은 확정 길이를 사용해야 하며, 중복 맵 키와 뒤따르는 잉여 바이트가 없어야 하고, 최대 24단계 깊이와 20,000개 항목까지만 허용됩니다. 알 수 없는 요청 필드는 거부됩니다.
- 애니메이션 QR 코드는 하나의 세션에서 나온 것이어야 합니다. 두 요청의 프레임이 섞이면 병합하지 않고 오류로 처리합니다.
- 크기 제한: 프레임 2,048개, 프레임당 4,300자, 스캔한 텍스트 1 MiB, 디코딩된 페이로드 128 KiB.
원본 CBOR(또는 PSBT) 바이트는 디코딩된 요청에 첨부된 채로 유지됩니다. 서명 직전에 엔진은 이를 다시 디코딩하고, 검토한 내용과 더 이상 일치하지 않으면 서명을 거부합니다.
검토 화면은 실제 바이트를 보여 줍니다
엔진은 트랜잭션이나 메시지를 직접 파싱하며, 온라인 지갑이 제공한 요약은 절대 신뢰하지 않습니다. 예를 들면 다음과 같습니다.
- Bitcoin: 모든 입력과 출력의 금액과 주소, 수수료, 그리고 어떤 출력이 잔돈인지. 출력은 서명 기기가 자신의 공개 키로 스크립트를 다시 파생할 수 있을 때만 잔돈으로 간주됩니다. PSBT 레이블만으로는 출력이 "잔돈"이 되지 않습니다.
- EVM: 네트워크, nonce, 수수료, 수신자, 네이티브 금액, 그리고 해석된 호출(ERC-20/721/1155 전송과 승인, Permit, Permit2, Safe 트랜잭션 등). spender와 operator 주소는 내장된 공개 컨트랙트 목록과 비교하며, 알 수 없는 spender는 위험 수준을 높입니다.
- TRON: 컨트랙트 유형, 금액, TRC-20/721 호출, 수수료 한도, 권한 ID.
- Solana: 알고 있는 모든 명령어(System, SPL Token, Token-2022, ATA, Compute Budget, Memo, Stake, NFT 프로그램), 수수료 지불자와 서명자.
각 요청에는 서로 독립적인 레이블 두 개가 붙습니다.
| 해석 | 위험 |
|---|---|
| 완전 해석(Fully decoded) · 부분 해석(Partially decoded) · 해석 불가(Opaque) | 보통(Normal) · 경고(Warning) · 높음(High) |
유효하지만 완전히 해석되지 않은 요청(예: 알 수 없는 컨트랙트 호출)도 서명할 수 있지만, 사용자가 해당 요청에 대해 위험을 명시적으로 확인한 경우에만 가능합니다. 계속 켜져 있는 "블라인드 서명" 설정 같은 것은 없습니다. 형식이 잘못된 바이트, 지원하지 않는 트랜잭션 유형, 서로 충돌하는 네트워크 데이터는 즉시 거부되며 무시하고 진행할 수 없습니다.
별도로 표시하지 않은 한, 두 앱이 공유하는 엔진의 문제입니다. 보안 검토를 참고하세요.
- 위험 확인(iPhone은 BTS-OP5-01, Android는 BTS-12, 낮음). 무제한 승인이나
setApprovalForAll처럼 완전히 디코딩된 높은 위험 요청도 명시적 확인이 필요하지만, 이 규칙은 앱 인터페이스가 강제합니다(서명 버튼이 비활성 상태로 유지됨). 엔진은 부분적으로 디코딩된 요청과 불투명한 요청에만 이를 강제합니다. QR 코드로 우회할 수는 없습니다. - Solana 요약(BTS-07, 중간). 아주 작은 송금과 함께 보낸 권한 변경, 위임 또는 불투명한 명령이 그 송금으로 요약됩니다. iPhone 1.0.0에서는 소각도 같은 방식으로 가려질 수 있습니다. 경고와 위험 확인은 그대로 남습니다.
- Bitcoin 잔돈(BTS-08, 중간). 지갑 자체의 단일 서명 구성이라면 어떤 인덱스의 출력이든 잔돈으로 간주됩니다. 보기 전용 지갑이 절대 스캔하지 않는 인덱스도 포함됩니다.
- EVM 및 TRON 요약(BTS-09, 낮음~중간). 알 수 없는 컨트랙트 호출과 함께 보내는 네이티브 금액이 저장된 요약에서 빠집니다. 검토 카드에는 표시됩니다.
올바른 지갑 매칭
사용자는 어떤 지갑으로 서명할지 직접 고르지 않습니다. 엔진은 요청을 모든 로컬 지갑의 공개 데이터와 비교하며, 정확히 하나가 일치할 때만 받아들입니다.
| 체인 | 사용하는 근거 |
|---|---|
| Bitcoin | bip32Derivation의 지문과 경로를 입력의 실제 스크립트와 대조합니다. 파생 데이터가 없으면 각 단일 서명 계정의 /0/0만 시도합니다. |
| EVM | 파생 경로(+ 마스터 지문) 및/또는 주소. 경로가 없으면 인덱스 0만 비교합니다. |
| TRON | 파생 경로(모든 인덱스), 또는 인덱스 0–99와 비교하는 주소. |
| Solana | 세 프리셋 키 중 하나이며, 이 키는 메시지의 필수 서명자 슬롯에도 있어야 합니다. |
일치 없음, 서명자 누락, 모호한 일치(두 지갑이 해당), 불완전한 지갑 데이터는 각각 별개의 오류입니다. 서명 기기는 모든 지갑의 시드를 하나씩 시도하는 일을 하지 않습니다.
매칭 결과는 정규 요청 해시, 원본 바이트, 정확한 서명 대상 바이트, 대상 키와 입력을 묶는 변경 불가능한 계획(plan)입니다. 비밀번호가 입력되면 엔진은 키를 다시 파생하고, 공개 데이터를 다시 만들고, 매칭을 다시 실행해 전체 계획을 비교한 뒤에야 개인 키를 사용합니다.
파생 데이터는 항상 넣으세요. PSBT에는 bip32Derivation을, eth-, tron-, sol-sign-request에는 경로와 지문을 넣습니다. 서명자 데이터가 완전한 요청은 즉시 매칭됩니다. 그렇지 않은 요청은 인덱스 0으로 대체되며, 다른 계정이라면 실패할 수 있습니다.
서명을 요청에 묶기
- 엔진은 정규화된 요청 전체를 해시하고, 서명하고, 자신의 서명을 검증한 뒤, 원래 요청 해시와 함께 결과를 반환합니다. 서명 중에 사용자가 앱을 떠나거나 새 요청이 기존 요청을 대체하면, 늦게 도착한 결과는 폐기됩니다.
- EVM, TRON, Solana 응답은 요청 ID를 그대로 돌려주며, 서명은 검토한 바로 그 바이트를 커버합니다. Bitcoin 응답은 서명이 추가된 같은 PSBT를 돌려주며, 그 밖의 모든 필드는 바뀌지 않아야 합니다.
- PSBT 하나에 여러 로컬 지갑이 필요할 수 있습니다(같은 iPhone에 키 두 개가 있는 멀티시그). 지갑마다 별도의 작업으로 처리되어 각자의 비밀번호를 요구하며, 부분 서명은 작업 사이에 유지됩니다.
버전 1.0.0에 대한 두 보안 검토 모두 이 연결 과정을 점검했습니다. Opus 5 검토는 변조된 계획이 거부된다는 점과, Release 빌드의 유일한 서명 진입점이 이 모든 검사를 거친다는 점을 실행으로 확인했습니다.
오프라인에서 확인할 수 없는 것
검토 화면은 알 수 없는 것이 있으면 그렇다고 명시합니다. 토큰 컨트랙트의 동작, 현재 잔액과 허용량(allowance), TRON 멀티시그 구성원이 여전히 권한을 가지고 있는지, Solana blockhash가 아직 유효한지, Blur Merkle 루트가 실제로 무엇을 커밋하는지 등입니다. 온라인 상태인 지갑이 QR 코드를 만들기 전에 이런 맥락을 보여 주기에 더 좋은 위치에 있습니다.