본문으로 건너뛰기

서명과 개인키 처리

SCOPE Connect 서버는 개인키를 수집하거나 저장하지 않습니다. WalletKit의 세션 API도 개인키를 생성·보관하지 않고, 서명이 필요할 때 지갑 앱이 제공한 Signer를 호출합니다. 개인키 생성·보관·사용자 인증·백업·복구 정책은 지갑 제공사가 책임집니다.

다만 WalletKit이 제공하는 InMemorySignerSecp256k1Signer를 사용하면 개인키 바이트가 지갑 앱 프로세스 메모리에서 처리됩니다. InMemorySigner는 테스트·데모에만 사용하고, 실제 지갑은 자체 보안 구조에 맞게 Signer를 구현합니다.

승인된 서명 요청이 이 지점까지 오는 과정은 세션 연결승인 단계에서 다룹니다.

Signer 인터페이스

connect()에 넘긴 Signer가 서명 계정과 실제 서명 연산을 제공합니다.

public interface Signer {
/** 서명자 EVM 주소. 0x 접두사가 있는 소문자, 체크섬 없음 */
public val address: String

/** 32바이트 EIP-712 서명 해시를 복구 가능한 서명으로 반환 */
public suspend fun signRecoverable(digest: ByteArray): EthSignature
}
멤버설명
address세션 연결 단계에서 dApp에 전송되는 서명자 EVM 주소
signRecoverable라이브러리가 계산한 32바이트 EIP-712 서명 해시에 서명

라이브러리는 요청의 EIP-712 구조화 데이터로부터 32바이트 서명 해시(digest)를 계산한 뒤 signRecoverable(digest)를 호출합니다. 지갑 앱은 이 해시에 서명해 65바이트 복구 가능한 서명을 반환하면 되며, EIP-712 인코딩을 직접 구현할 필요가 없습니다. signRecoverable은 Kotlin 비동기 함수인 suspend 함수이므로 생체 인증처럼 사용자 상호작용이 필요한 서명도 기다릴 수 있습니다.

실제 지갑 앱의 개인키 처리

EVM의 secp256k1 개인키를 처리하는 방법은 지갑 앱의 보안 구조와 플랫폼 기능에 따라 달라집니다. WalletKit은 특정 저장 방식을 강제하지 않습니다. 개인키 바이트를 직접 받는 서명 함수를 사용하는 구현은 키가 앱 메모리에 머무는 시간을 최소화해야 합니다.

  • 플랫폼 보안 기능과 지갑 자체 암호화 정책을 조합해 개인키 접근을 제한합니다.
  • signRecoverable 호출 직전에 사용자 인증을 거쳐 필요한 개인키 바이트만 메모리에 불러옵니다.
  • 서명이 끝나면 임시 바이트 배열을 즉시 덮어쓰고 참조를 해제합니다.
  • 키 생성·백업·복구 정책은 지갑 제공사가 소유하며 WalletKit은 관여하지 않습니다.
// 지갑의 보호된 키 저장 방식과 연동하는 Signer의 형태 (개념 예시)
class WalletSigner(
override val address: String,
private val loadPrivateKey: suspend () -> ByteArray,
) : Signer {
override suspend fun signRecoverable(digest: ByteArray): EthSignature {
val privateKey = loadPrivateKey()
try {
return Secp256k1Signer.signRecoverable(digest, privateKey)
} finally {
privateKey.fill(0) // 사용 후 즉시 덮어쓰기
}
}
}

Secp256k1Signer, EthSignature 등 서명 함수와 타입의 정확한 정의와 제약은 WalletKit 암호화에서 확인합니다.

서명 대상 — EIP-3009

현재 세션 요청이 지원하는 서명은 EIP-3009 가스리스 전송 계열입니다. eth_signTypedData_v4의 구조화 데이터는 TransferWithAuthorization(가스리스 전송)과 ReceiveWithAuthorization(수신형 결제) 두 가지 primaryType을 다룹니다. 라이브러리가 도메인 분리자와 구조체 해시를 계산해 최종 서명 해시를 만들고, 지갑 앱의 Signer는 그 해시에만 서명합니다.

서명 이후의 가스 대납·온체인 제출은 SCOPE Connect가 제공하는 기능이 아니며, dApp 측 외부 서버가 수행합니다. WalletKit의 역할은 서명 응답을 릴레이로 반환하는 것까지입니다.

서명 요청의 표시 필드(컨트랙트·체인·수취인·금액)는 승인 단계에서 사용자에게 확인시킨 뒤 서명합니다.

키를 샘플 코드에 심어 두지 마세요

소스 트리의 샘플·테스트에 등장하는 개인키 상수는 로컬 연산 검증용입니다. 실제 지갑에서는 이 값을 복사하지 말고 자체 보안 구조와 개인키 생성·보관·백업·복구 정책을 적용하세요.

다음 단계