ERC-7913 주소 없는 키의 서명 검증기 (Signature Verifiers)

Final · 서명·인증 · 2025-03-21 작성

이더리움 주소가 없는 키(모바일 기기의 P256 키, RSA 키 등)가 만든 서명을 공용 검증기(verifier) 컨트랙트로 확인할 수 있게 하는 표준입니다. 키마다 컨트랙트를 배포하지 않아도 됩니다.

왜 필요한가

지금까지 온체인 서명 검증은 서명자가 이더리움 주소를 가진 경우만 다뤘습니다. 일반 계정(EOA)은 개인키로 서명하고 ERC-1271은 멀티시그 같은 스마트 계정이 서명을 검증하는 방법을 정하는데, 두 경우 모두 서명자의 정체는 이더리움 주소입니다. 그런데 계정 추상화(account abstraction)가 발전하면서 이더리움이 기본 지원하는 secp256k1 외의 키가 널리 쓰이고 있습니다. 많은 모바일 기기가 지원하는 secp256r1 곡선, 전통 기관이 발급하는 RSA 키, 이메일이나 대형 웹 서비스의 JWT로 서명하는 영지식(ZK) 방식 등입니다. 이런 키들은 자신을 나타낼 이더리움 주소가 없습니다. 키마다 ERC-1271 호환 컨트랙트를 하나씩 배포할 수도 있지만 번거롭고 비쌉니다. 또 키에 고정 온체인 주소를 주면 실수로 그 주소에 자산을 보내는 문제도 생길 수 있어, 자산을 보관하는 계정 주소와 계정을 제어하는 키를 분리하려는 계정 추상화의 방향과도 맞지 않습니다. 이 표준은 키 종류별로 소수의 검증기 컨트랙트를 두고 계정이 (검증기, 키) 쌍으로 서명자를 식별하게 하여 이 문제를 풉니다.

구현해야 하는 것

심층 설명

키와 검증기의 정의

키는 길이 제한이 없는 bytes 객체로 표현합니다. 예를 들어 P256이나 RSA 공개키입니다. 키는 비어 있으면 안 됩니다(MUST NOT). 검증기는 특정 종류의 키에 대한 서명 검증을 맡는 스마트 컨트랙트이며 이더리움 주소를 갖습니다. 즉 주소가 없는 것은 키이고 검증 책임은 주소를 가진 검증기 컨트랙트가 집니다.

verify 함수의 반환 규칙

검증기는 IERC7913SignatureVerifier 인터페이스를 구현해야 합니다(MUST). verify(bytes key, bytes32 hash, bytes signature)는 서명이 유효하면 bytes4 매직값 0x024ad318(IERC7913SignatureVerifier.verify.selector)을 반환해야 하고(MUST) 서명이 유효하지 않거나 키가 비어 있으면 0xffffffff를 반환하거나 revert해야 합니다(SHOULD). 또한 검증기는 상태를 갖지 않아야 합니다(SHOULD, stateless).

EOA·ERC-1271과의 하위 호환 검증 절차

서명자는 verifier 주소와 선택적 key를 이어 붙인 bytes 객체(verifier || key)로, 최소 20바이트입니다. 검증 절차는 이렇습니다. signer.length < 20이면 실패합니다. 앞 20바이트를 verifier, 나머지를 key로 나눕니다. key가 비어 있으면 verifier를 서명자 본인으로 간주해, 해당 주소에 코드가 있으면 ERC-1271의 isValidSignature로, 없으면 ecrecover로 검증합니다. key가 비어 있지 않으면 verify를 호출해 반환값이 매직값 0x024ad318일 때만 성공으로 봅니다. 이렇게 기존 EOA와 ERC-1271 컨트랙트는 '검증기 주소 + 빈 키' 형태로 그대로 수용됩니다.

빈 키를 금지한 이유

키마다 ERC-1271 '정체성 컨트랙트'를 배포하면 비용이 많이 들지만 이 모델에서는 검증기만 있으면 새 키를 배포 비용 없이 쓸 수 있습니다. 키가 필요 없는 경우는 이미 ERC-1271이 다루므로, 모호함을 피하기 위해 검증기는 빈 키를 지원하지 않아야 합니다. 서명자가 20바이트(빈 키)이면 ERC-1271로 처리해야 합니다.

왜 메시지가 bytes32 해시인가

기존 체계인 ecrecover와 ERC-1271과의 일관성을 위해 메시지는 bytes32 해시여야 합니다. 이 해시는 보통 ERC-191이나 ERC-712를 따라 만들어집니다. 다른 해싱 방법을 쓰는 암호 체계는 이 해시를 하나의 메시지로 보고 필요하면 해당 표준에 따라 다시 해싱해야 합니다(SHOULD).

참조 구현

참조 구현은 OpenZeppelin의 SignatureChecker 라이브러리를 확장한 SignatureCheckerExtended 라이브러리입니다. isValidSignatureNow(bytes signer, bytes32 hash, bytes signature)는 signer 길이가 20 미만이면 false, 정확히 20이면 기존 SignatureChecker.isValidSignatureNow로 처리하고 20을 넘으면 앞 20바이트를 검증기 주소로 삼아 verify를 try/catch로 호출한 뒤 반환값이 verify.selector와 같은지 확인합니다. 호출이 revert하면 false를 반환합니다.

쓸 때 주의할 점

ERC 원문 · ← ERCs