EIP-8355 ML-DSA 서명 검증 프리컴파일 (Precompiles for ML-DSA Verification)

Hegota 포함 제외 (DFI) · 보안·프라이버시 · 원문 상태 Draft · 2026-07-30 작성

양자내성 서명 ML-DSA(FIPS 204)를 검증하는 프리컴파일 세 개를 0x12, 0x13, 0x14 주소에 추가합니다. 공개키, 서명, 메시지를 길이 표시 없이 이어 붙여 넣으면 32바이트로 유효 여부를 돌려줍니다.

왜 필요한가

이더리움에는 컨트랙트가 호출할 수 있는 양자내성(post-quantum) 서명 검증기가 아직 없습니다. ML-DSA는 격자(lattice) 수학에 바탕을 둔 서명 방식으로, 미국 표준기술연구소(NIST)가 최종 표준(FIPS 204)으로 발표한 첫 격자 서명입니다. 클라우드 키 관리 서비스와 하드웨어 보안 모듈이 이미 세 가지 보안 수준의 ML-DSA 키를 만들고 서명하며 인증서와 전송 프로토콜 규격도 같은 FIPS 204 인코딩을 씁니다. 그 인코딩을 그대로 받는 프리컴파일이 있으면 어느 장비가 만든 서명이든 검증할 수 있고 키 소유자가 키를 밖으로 꺼내지 않고도 계정을 그 키로 지킬 수 있습니다. 같은 목적의 EIP-8051도 있지만 보안 수준 II(ML-DSA-44) 하나만 다루고 메시지를 32바이트 해시로 고정합니다. 계정 인증 키는 몇 년 동안 자금을 지키므로 128비트보다 여유가 필요하다는 것이 이 제안의 판단입니다. 그래서 더 높은 보안 수준 III과 V를 함께 넣고 메시지 길이를 FIPS 204 그대로 자유롭게 둡니다.

무엇이 바뀌나

지금

EIP-8355 이후

누구에게 영향이 있나

심층 설명

주소 세 개와 매개변수 집합별 크기

프리컴파일 세 개를 연속 주소에 둡니다. VERIFY_MLDSA44는 0x12, VERIFY_MLDSA65는 0x13, VERIFY_MLDSA87은 0x14이며 각각 FIPS 204의 ML-DSA-44, ML-DSA-65, ML-DSA-87에 대응합니다. 공개키는 FIPS 204 표준 압축 인코딩 pk = (ρ, t1), 서명은 표준 서명 인코딩을 씁니다. 크기는 매개변수 집합마다 고정입니다. ML-DSA-44는 PK_LEN 1312, SIG_LEN 2420으로 최소 입력이 3732바이트, ML-DSA-65는 1952와 3309로 5261바이트, ML-DSA-87은 2592와 4627로 7219바이트입니다. 선택자 바이트로 세 수준을 가르는 단일 프리컴파일 대신 주소를 나눈 이유는 세 가지입니다. 주소마다 입력 모양과 가스가 고정되어 분기 바이트를 파싱할 필요가 없고 선택자와 공개키 첫 바이트를 혼동할 여지가 없으며 체인이 수준별로 따로 켜고 가스를 매길 수 있습니다.

입력 형식: 공개키, 서명, 메시지 순서로 길이 표시 없이

입력은 헤더와 길이 필드가 없는 빅엔디언 바이트열 하나입니다. 오프셋 0부터 PK_LEN바이트가 pubkey, 그다음 SIG_LEN바이트가 signature, 나머지 len(input) - PK_LEN - SIG_LEN바이트가 message입니다. L = len(input)일 때 L < PK_LEN + SIG_LEN이면 revert 없이 실패(32바이트 0)를 돌려줘야 합니다(MUST). L == PK_LEN + SIG_LEN은 빈 메시지로 유효한 입력입니다. FIPS 204가 빈 메시지를 허용하기 때문입니다. 메시지를 맨 뒤에 두고 길이 필드를 뺀 것은 입력 조립을 단순하게 하려는 선택입니다. 고정 길이 두 필드를 먼저 쓰고 메시지를 이어 쓴 뒤 그 크기로 호출하면 되므로 길이 워드를 계산하거나 잘못 놓을 일이 없습니다. 증인이 도착하는 방식과도 맞습니다. EIP-8141에서 ML-DSA 증인은 pubkey ++ signature를 담은 ARBITRARY 서명 항목으로 올 수 있고 VERIFY 프레임이 SIGPARAM 복사 한 번으로 메모리에 옮깁니다. 그 뒤에 서명이 승인하는 메시지를 이어 쓰면 재포장이나 증인 바이트 재탐색 없이 프리컴파일 입력이 됩니다.

빈 컨텍스트 문자열과 도메인 분리 책임

입력에 컨텍스트 문자열이 없고 프리컴파일은 항상 FIPS 204 § 5.3의 기본값인 빈 컨텍스트 ctx = ""를 씁니다. FIPS 204는 최대 255바이트의 컨텍스트를 메시지 표현에 묶을 수 있게 하지만 ML-DSA를 쓰는 X.509 인증서와 TLS 규격은 모두 기본값을 두고 주변 프로토콜에 도메인 분리를 맡깁니다. 이 프리컴파일도 같은 처지입니다. 검증하는 메시지가 이미 프로토콜별 커밋이고 더 분리하고 싶은 호출자는 메시지 앞에 붙이면 됩니다. 컨텍스트 필드를 두면 길이 태그가 필요해 이 레이아웃이 피하려는 헤더가 되살아나고 널리 쓰이는 암호 라이브러리 여럿이 빈 컨텍스트 검증만 노출해 클라이언트가 직접 검증기를 짜야 하는 문제도 생깁니다. 보안 고려사항에서도 같은 점을 짚습니다. 프리컴파일 자체는 도메인 분리를 해 주지 않으므로 ECRECOVER나 secp256r1 검증과 마찬가지로 체인, 컨트랙트, 논스, 의도에 대한 결속은 애플리케이션이 메시지에 넣어야 합니다. 한 키를 여러 서명 방식에 쓴다면 메시지 안에서 분리해야 합니다. EIP-8141 위에 만든 인증 컨트랙트는 승인 대상 프레임을 커밋하는 프레임 해시에 서명해 이 조건을 만족합니다.

검증 의미와 32바이트 고정 출력

프리컴파일은 FIPS 204에 따라 원시 메시지에 대해 ML-DSA.Verify(pubkey, message, signature)를 계산합니다. 행렬 확장 Â = ExpandA(ρ)와 내부 해시 μ = H(BytesToBits(tr) ‖ M')도 직접 수행하며 검증이 통과할 때만 성공을 돌려줍니다. 계수 범위 초과, 비정규(non-canonical) 힌트 h, ‖z‖ 경계 위반 같은 깨진 인코딩에서도 revert가 아니라 실패를 돌려줘야 합니다(MUST). 호출자가 '서명 무효'를 불리언으로 다루게 하기 위해서입니다. 출력은 항상 정확히 32바이트입니다. 0x00..01은 유효한 서명, 0x00..00은 무효하거나 깨진 서명 또는 PK_LEN + SIG_LEN보다 짧은 입력입니다. 가스 소진 외에는 revert 하지 않으므로 호출 성공 플래그는 서명 정보를 담지 않으며 호출 코드는 성공 여부와 함께 반환 워드를 ISZERO로 확인해야 합니다. 고정 길이 반환은 ECRECOVER와 EIP-7951 secp256r1 검증이 실패 시 빈 바이트열을 돌려주는 것과 일부러 다르게 한 선택입니다. 코드 없는 주소 호출도 성공하며 빈 값을 돌려주므로, 빈 값 방식에서는 '서명 무효'와 '포크 미활성화 또는 주소 미배정 체인'이 바이트 단위로 같아집니다. P256VERIFY에 대한 반복된 불만이 이것입니다. 이 제안에서는 반환 길이가 32가 아니면 프리컴파일 부재, 32바이트 0이면 서명 실패로 나뉩니다. 대가는 메모리 한 워드와 Solidity가 빈 반환값을 디코딩할 때 얻던 우연한 fail-closed 동작뿐이라고 봅니다.

가스 일정과 검증 전 선차감

가스는 BASE + 6 * ceil(max(0, L - PK_LEN - SIG_LEN) / 32)입니다. BASE는 VERIFY_MLDSA44가 6500, VERIFY_MLDSA65가 9000, VERIFY_MLDSA87이 13500이고 메시지 워드당 6가스입니다. max(0, ...) 클램프 덕분에 PK_LEN + SIG_LEN보다 짧은 입력은 특별 규칙 없이 정확히 BASE만 내고 거부됩니다. 기본 비용이 대부분을 차지하며 행렬 확장과 수론 변환(NTT) 검증 작업을 덮습니다. 작은 선형 항은 메시지에 대한 SHAKE256 해시 비용으로, 격자 연산에 비해 쌉니다. 워드당 6가스는 KECCAK256 명령어가 이미 매기는 요율과 같습니다. SHAKE256과 Keccak-256은 같은 Keccak-f[1600] 순열을 같은 136바이트 속도로 흡수하므로 바이트당 비용이 같습니다. 기본 비용이 EIP-8051의 4500보다 높은 것은 행렬을 미리 확장해 받지 않고 ρ에서 직접 확장하기 때문입니다. 클라이언트는 검증 작업 전에 전체 가스를 먼저 차감해야 합니다(MUST). 비용이 입력 길이에만 의존하므로 미리 계산할 수 있습니다. 검증 뒤에 차감하면 가스 부족으로 끝나는 트랜잭션에서 공격자가 작업을 공짜로 시킬 수 있는 서비스 거부 표면이 생깁니다. 원문은 이 수치가 참조 구현 벤치마크 전의 값이며 비슷한 작업량의 페어링 검사보다 조금 높게 잡은 것이라고 밝힙니다.

EIP-8051과 다른 두 선택: 압축 공개키와 가변 길이 메시지

EIP-8051은 행렬을 실체화하고 t1을 NTT 영역에 둔 20512바이트 공개키를 받아 확장을 건너뜁니다. 이 제안은 그 교환을 일부러 뒤집습니다. 공개키는 서명과 함께 증인에 실리므로 그 크기는 검증할 때마다 트랜잭션 대역폭과 데이터 비용으로 지불되지만 확장은 가스로 가격을 매길 수 있는 연산으로 한 번만 지불됩니다. 20512바이트는 1312바이트 ML-DSA-44 압축 키의 열다섯 배가 넘고 ML-DSA-87 증인 전체보다 큽니다. 클라이언트가 마이크로초에 끝낼 일을 아끼려고 모든 트랜잭션에 그 비용을 물리는 것은 거꾸로라는 판단이며 반대 교환을 원하는 체인은 사전 확장 키를 받는 자매 프리컴파일을 추가할 수 있습니다. 메시지 길이도 다릅니다. EIP-8051은 32바이트 사전 해시로 고정하지만 ML-DSA는 고정 길이 메시지 방식이 아니고 FIPS 204 § 5.4는 사전 해시를 별도 구성 HashML-DSA로 정의해 다이제스트를 만든 해시 함수 식별자를 함께 묶습니다. 맨 32바이트 메시지에는 그런 결속이 없고 32바이트 전용 프리컴파일은 식별자와 도메인 분리자 때문에 길어진 HashML-DSA 서명도 검증하지 못합니다. 순수 ML-DSA도 HashML-DSA도 아닌, 호출자가 안전성 논증을 직접 대야 하는 세 번째 구성만 남습니다. 다이제스트만 가진 호출자는 여전히 다이제스트를 넘기고 그 책임을 질 수 있지만 메시지에 직접 서명할 수 있는 호출자에게까지 강제하지는 않습니다.

구현 성숙도와 테스트 케이스

ML-DSA는 실행 계층 클라이언트가 쓰는 모든 언어에 검증된 구현이 있어 클라이언트가 격자 코드를 새로 짜지 않고 라이브러리에 연결하면 됩니다. Java는 JDK 24부터 SUN 제공자가 세 매개변수 집합을 지원하고 Rust에는 no-std, no-unsafe, 상수 시간 크레이트와 AVX2 백엔드를 갖춘 형식 검증 크레이트가 있습니다. Go는 1.27부터 표준 라이브러리 crypto/mldsa가 crypto/x509와 TLS에 연결되어 있고 C#과 .NET은 .NET 10부터 네이티브 ML-DSA 타입을 노출합니다. C에는 참조 구현과 liboqs 외에 이식 가능한 C90 구현이 있고 Node.js는 Web Crypto로 세 집합을 검증합니다. 테스트 케이스는 VERIFY_MLDSA44로 호출자가 볼 수 있는 세 결과를 보여 줍니다. 케이스 1과 2는 Wycheproof mldsa_44_verify_test.json 그룹 1의 tcId 1과 8로, 같은 공개키와 메시지 "Hello world"를 쓰고 서명의 c~ 첫 바이트 한 비트만 다릅니다. 케이스 1은 3743바이트 입력에 0x00..01을 돌려주고 6506가스, 케이스 2는 같은 길이와 가스에 0x00..00, 케이스 3은 21바이트 "This is invalid input"에 검증 없이 0x00..00을 돌려주고 6500가스입니다. 세 케이스 모두 호출 자체는 성공하므로 호출 성공 여부만으로는 구분할 수 없습니다. Last Call 전에 NIST ACVP와 Wycheproof 세 집합 전체 세트를 이 레이아웃으로 재인코딩해 넣고 빈 메시지와 경계 길이 같은 EVM 고유 케이스를 더하는 작업이 남아 있습니다.

하위 호환성, Draft 상태, 헤고타 포크 논의

프리컴파일은 미배정 주소를 쓰므로 기존 동작은 바뀌지 않습니다. 활성화 포크 전에는 이 주소 호출이 빈 출력을 돌려주고 포크 뒤에는 32바이트 워드를 돌려주며 위 가스 일정을 매깁니다. 이 반환 길이 차이가 컨트랙트가 프리컴파일 유무를 알아내는 수단이며 이전 동작에 의존하는 배포 컨트랙트는 알려진 바 없습니다. 이 EIP의 상태는 Draft입니다. 가스 수치는 참조 구현 벤치마크 전이고 테스트 벡터도 Last Call 전에 보강할 예정이어서 명세가 바뀔 수 있습니다. 코어 개발자 콜에서는 08.13 ACDE #243에서 헤고타(Hegota) 포크 후보(PFI)로 접수됐습니다. FIPS 204 양자내성 서명 검증을 클라이언트가 직접 구현하지 않고 표준 라이브러리로 쓰자는 취지였습니다. 그러나 10.08 ACDE #247에서 헤고타 후보에서 제외(DFI)됐습니다. 격자 기반 서명에 합의가 없고 zkEVM 비용 우려가 커 해시 기반 접근이 선호됐습니다.

EIP 원문 · ← EIPs