P-256 precompile은 왜 지갑 UX 기능이 아니라 신뢰 경계일까: EIP-7951로 보는 passkey 검증
지갑이 passkey를 지원한다는 말은 로그인 편의일까, 아니면 온체인 서명 검증의 신뢰 경계가 하나 더 생겼다는 뜻일까?
P-256 precompile은 왜 지갑 UX 기능이 아니라 신뢰 경계일까: EIP-7951로 보는 passkey 검증
- 카테고리: blockchain_dev
- 예상 읽기 시간: 10분
- 오늘의 질문: 지갑이 passkey를 지원한다는 말은 “로그인이 쉬워졌다”는 뜻일까, 아니면 온체인 서명 검증의 신뢰 경계가 하나 더 생겼다는 뜻일까?
- 핵심 출처:
- EIP-7951: Precompile for secp256r1 Curve Support - 생성일 2025-05-27, 확인일 2026-07-09
- EIP-7607: Hardfork Meta - Fusaka - 생성일 2024-02, 확인일 2026-07-09
- ERC-7913: Signature Verifiers - 생성일 2025-03-21, 확인일 2026-07-09
- ethereum/EIPs PR #9978: update gas costs for R1 verification - 병합일 2025-07-04, 확인일 2026-07-09
- execution-specs P256VERIFY mainnet checklist test - 확인일 2026-07-09
1. 왜 지금 봐야 하나
Ethereum Fusaka 메타 EIP인 EIP-7607은 core EIP 목록에 EIP-7951, 즉 secp256r1 곡선 지원 precompile을 포함한다. secp256r1은 P-256 또는 prime256v1이라고도 불리는 NIST 표준 곡선이고, Apple Secure Enclave, Android Keystore, FIDO2/WebAuthn 장치, HSM 같은 현실의 인증 인프라에서 널리 쓰인다.
블록체인 개발자에게 이 변화는 “드디어 passkey 지갑 UX가 좋아진다” 정도로만 읽히기 쉽다. 하지만 더 중요한 관점은 따로 있다.
P-256 지원은 로그인 기능이 아니라, Ethereum 상태 전이 안에 새로운 서명 검증 경계를 추가하는 일이다.
지금까지 Ethereum 기본 서명 검증은 secp256k1과 ecrecover 중심이었다. 스마트컨트랙트 지갑은 ERC-1271처럼 “이 컨트랙트가 이 서명을 유효하다고 판단하는가”를 묻는 방식으로 확장했다. EIP-7951은 여기에 기기 보안 모듈과 WebAuthn 생태계에서 나온 P-256 서명을 L1에서 효율적으로 검증할 수 있는 P256VERIFY precompile을 추가한다.
그러면 개발자가 봐야 할 질문은 UX가 아니라 이것이다.
- 어떤 hash를 검증하는가?
- 어떤 public key가 계정 권한과 연결되는가?
- 실패한 검증은 어떻게 fail-closed 되는가?
- L1과 L2의 같은 주소
0x100precompile이 같은 비용·같은 의미라고 가정해도 되는가?
2. 핵심 개념
EIP-7951은 P256VERIFY라는 precompile을 주소 0x100에 둔다. 기능은 P-256 ECDSA 서명 검증이다. 입력은 정확히 160바이트이며, 다섯 개의 32바이트 값을 이어 붙인다.
h || r || s || qx || qy
각 필드의 의미는 다음과 같다.
h: 검증할 32바이트 message hashr,s: ECDSA signature componentqx,qy: P-256 public key의 x, y 좌표
성공하면 32바이트 성공 값을 반환하고, 실패하면 빈 출력(empty output)을 반환한다. 중요한 점은 실패가 revert가 아니라는 것이다. 따라서 호출하는 컨트랙트는 “call이 성공했는가”와 “반환값이 성공 값인가”를 분리해서 봐야 한다.
EIP-7951이 명시한 검증 조건도 실무적으로 중요하다. precompile은 입력 길이가 정확히 160바이트인지, r과 s가 올바른 범위인지, public key 좌표가 field 범위 안에 있는지, (qx, qy)가 실제 P-256 curve equation을 만족하는지, point at infinity가 아닌지 확인해야 한다. 즉 “P-256 서명 검증”은 단순히 수학 연산 하나가 아니라 입력 정규화와 public key 유효성 검사를 포함한 프로토콜 API다.
gas cost도 설계 신호다. EIP-7951의 현재 명세는 P256VERIFY 비용을 6900 gas로 둔다. EIP PR #9978은 R1 검증 gas cost 업데이트가 병합된 기록을 남기고, 명세는 이 비용이 기존 ECRECOVER 3000 gas와의 benchmark 비교를 바탕으로 정해졌다고 설명한다. L2에서 먼저 쓰인 RIP-7212 계열 구현은 같은 주소·같은 입력 형식을 목표로 하지만, EIP-7951 명세는 L2 구현의 3450 gas와 달리 6900 gas를 사용한다고 밝힌다.
여기서 배울 점은 간단하다.
precompile은 “싸고 표준화된 내장 함수”가 아니라, 클라이언트 구현 비용·보안 검증·기존 생태계 호환성을 동시에 반영한 합의 레벨 API다.
3. 최신 이슈와 연결
EIP-7607은 Fusaka에 포함된 core EIP 중 하나로 EIP-7951을 나열한다. 같은 Fusaka 묶음 안에는 PeerDAS, MODEXP bound/repricing, transaction gas limit cap, RLP block size limit, CLZ opcode 같은 변화도 들어 있다. 이 조합은 Ethereum이 단순히 기능을 추가하는 것이 아니라 “더 많은 데이터를 처리하고, 실행 계층의 최악 케이스를 줄이고, 개발자에게 필요한 저수준 primitive를 추가하는 방향”으로 움직이고 있음을 보여준다.
P-256은 특히 account abstraction과 연결된다. 사용자는 이미 휴대폰의 passkey나 hardware-backed key로 로그인한다. 하지만 그 key가 곧바로 Ethereum address가 되는 것은 아니다. ERC-7913은 이 문제를 더 일반화해 설명한다. account abstraction에서는 자산을 들고 있는 account와 그 account를 제어하는 key를 분리하고 싶다. P-256, RSA, Web2 identity, ZK email/JWT 같은 키는 자체 Ethereum address가 없다. ERC-7913은 이런 signer를 (verifier, key) 쌍으로 표현하고, verifier contract가 signature를 검증하는 모델을 제안한다.
EIP-7951과 ERC-7913을 함께 보면 구조가 보인다.
- EIP-7951: L1이 P-256 서명 검증 primitive를 제공한다.
- ERC-7913: smart account가 address-less key를 표준 signer로 표현하는 방법을 제안한다.
- ERC-1271: account contract 자체가 서명 유효성을 판단하는 기존 경로를 제공한다.
즉 passkey 지갑은 “지갑 앱이 WebAuthn을 붙였다”가 아니라, 기기 key → verifier/precompile → smart account 권한 → 상태 전이로 이어지는 권한 그래프다.
4. 개발자 관점 해석
첫째, hash domain을 분리해야 한다. EIP-7951의 입력은 32바이트 hash다. WebAuthn이나 FIDO2 세계에서 사용자가 승인한 payload, challenge, origin, rpId, clientDataJSON, authenticatorData가 어떤 방식으로 hash되어 온체인 h가 되는지 명확해야 한다. 온체인에서는 이미 hash만 보일 수 있기 때문에, off-chain 인코딩 실수는 on-chain precompile이 고쳐주지 못한다.
둘째, public key 등록과 회전을 별도 상태로 설계해야 한다. P-256 public key 좌표 qx, qy가 올바른 curve point인지 검증되는 것과, 그 key가 “이 계정의 유효한 signer인가”는 다른 문제다. 전자는 precompile의 일이고, 후자는 지갑·account contract·recovery module의 access control 문제다.
셋째, 실패 처리를 fail-closed로 만들어야 한다. EIP-7951은 실패 시 empty output을 반환한다. 낮은 수준의 staticcall wrapper를 작성할 때는 다음 조건을 모두 만족해야 valid로 처리하는 편이 안전하다.
(bool ok, bytes memory out) = P256VERIFY.staticcall(input);
valid = ok && out.length == 32 && bytes32(out) == SUCCESS_VALUE;
최근 ERC-1271 계열 사고에서 봤듯이, “magic value처럼 보이는 값”과 “정상 호출에서 나온 성공 값”은 다르다. P-256 wrapper도 같은 실수를 반복하지 말아야 한다.
넷째, L1/L2 호환성을 비용까지 포함해 테스트해야 한다. EIP-7951은 RIP-7212와 주소 0x100, 160바이트 입력, 32바이트 출력 같은 interface compatibility를 유지하려고 한다. 하지만 gas cost는 3450과 6900처럼 다를 수 있다. 같은 smart account 코드가 L2에서는 통과하지만 L1에서는 bundler simulation, paymaster budget, signature aggregation cost에서 다른 결과를 낼 수 있다.
다섯째, “기기 보안”과 “프로토콜 보안”을 혼동하면 안 된다. Secure Enclave나 Android Keystore가 private key 보호를 도와도, 온체인 contract가 어떤 key를 신뢰하는지, recovery가 어떻게 작동하는지, malicious dapp이 어떤 message를 서명시키는지는 별도 문제다.
5. 내 프로젝트에 적용할 체크포인트
- precompile wrapper:
0x100호출 결과를ok,out.length, 성공 값 세 조건으로 검증하는가? - 입력 인코딩:
h || r || s || qx || qy160바이트를 정확히 만들고, endian/좌표 순서를 테스트하는가? - hash domain: WebAuthn challenge와 온체인 digest 사이의 domain separator를 문서화했는가?
- key registry: P-256 public key 등록, 회전, 폐기, recovery 권한이 account state에 명확히 분리되어 있는가?
- multi-chain cost: RIP-7212 L2와 EIP-7951 L1의 gas 차이를 bundler/paymaster budget에 반영했는가?
- negative test: k1 서명처럼 생겼지만 P-256으로는 유효하지 않은 케이스, 길이가 틀린 입력, curve 밖 point를 모두 실패로 테스트하는가?
- UX 문구: 사용자에게 “passkey로 로그인”이 아니라 “이 기기 key가 어떤 계정 권한을 갖는지”를 확인시키는가?
6. 오늘 10분 액션
- 코드베이스에서
P256,RIP-7212,0x100,WebAuthn,passkey를 검색한다. - P-256 verify wrapper가 있다면 실패 조건을 세 가지로 나눠 본다: call 실패, empty output, 잘못된 success value.
- L1과 L2별 gas assumption을 표로 만든다. 특히 paymaster나 session key 모듈이 fixed gas budget을 쓰는지 확인한다.
- WebAuthn challenge를 만드는 서버 코드와 온체인 digest를 검증하는 contract 코드 사이에 같은 domain separator가 있는지 확인한다.
- test vector를 하나 추가한다. execution-specs의 mainnet checklist test처럼 “유효한 P-256 서명”과 “다른 곡선에서는 유효해 보일 수 있지만 P-256으로는 실패해야 하는 서명”을 나란히 둔다.
7. 더 볼 자료
- EIP-7951: Precompile for secp256r1 Curve Support
- EIP-7607: Hardfork Meta - Fusaka
- ERC-7913: Signature Verifiers
- ethereum/EIPs PR #9978
- execution-specs P256VERIFY tests
중복 회피 메모
최근 blockchain_dev 글은 ERC-1271 fail-closed 검증, EIP-7883 MODEXP precompile repricing, EIP-7702 wallet delegation, bridge parsing/source authentication, Arbitrum BoLD finality, EIP-8184 encrypted mempool, PeerDAS와 blob fee를 다뤘다. 이번 글은 지갑 UX 자체나 ERC-1271 사고 재해석이 아니라, EIP-7951 P256VERIFY가 P-256/passkey 서명을 합의 레벨 precompile로 들여오면서 생기는 hash domain, public key registry, fail-closed wrapper, L1/L2 gas compatibility 경계에 집중해 중복을 피했다.
핵심 출처
로그인하면 이 글을 북마크하고, 나만 보는 한 줄 메모를 남길 수 있어요.
댓글 0
최신순 ▾혹시 이 글을 읽는 동료 개발자가 있다면, GitHub으로 로그인하고 한 줄 흔적을 남겨줘요. (스팸 방지용 로그인이에요)