ECDSA (Elliptic Curve Digital Signature Algorithm)
왜 디지털 서명이 필요한가
블록체인에서 트랜잭션을 보낼 때, 네트워크는 한 가지 핵심적인 질문에 답해야 한다. "이 트랜잭션이 정말 해당 지갑의 소유자가 보낸 것인가?"
전통적인 금융 시스템에서는 은행이 신원을 확인하고, 비밀번호나 OTP로 본인 인증을 수행한다. 하지만 블록체인은 중앙 기관이 없다. 그렇다면 어떻게 신원을 증명할 수 있을까?
디지털 서명은 이 딜레마를 해결한다. 개인키를 공개하지 않으면서도, 개인키를 알고 있다는 사실을 증명할 수 있게 해준다. 이것이 바로 ECDSA(Elliptic Curve Digital Signature Algorithm)가 하는 일이다.
사전 지식: 타원곡선 암호학
ECDSA에 필요한 핵심 개념
1. 타원곡선과 점 덧셈
타원곡선은 y² = x³ + ax + b 형태의 방정식을 만족하는 점들의 집합이다. 이전 글에서 보았듯이, 곡선 위의 두 점을 더해서 새로운 점을 만드는 특별한 연산이 있다. 이것이 ECDSA의 수학적 기반이다.
2. 스칼라 곱셈과 일방향성
점 P를 k번 더하는 연산을 스칼라 곱셈이라고 부르고, k × P로 표기한다. 이 연산의 핵심은 일방향성이다:
- 순방향 (쉬움): k와 P를 알면
k × P = Q를 빠르게 계산할 수 있다 - 역방향 (어려움): Q와 P를 알더라도 k를 찾는 것은 사실상 불가능하다
이것이 ECDSA 보안의 수학적 기반이다. 공개키 Q를 공개해도 개인키 k를 역산할 수 없기 때문이다.
3. 생성점 G와 위수 n
- 생성점 G: 모든 사람이 알고 있는 표준 점. 공개키는
Q = d × G로 계산된다 - 위수 n: 곡선 위의 점 개수. 개인키는 1부터 n-1 사이의 값이다
secp256k1에서 n은 약 2²⁵⁶이므로, 개인키는 256비트 범위 내에서 선택한다.
4. secp256k1 곡선
비트코인과 이더리움이 사용하는 곡선으로, y² = x³ + 7 (mod p) 형태다. a = 0이어서 계산이 단순화되고, 투명하고 검증된 보안을 제공한다.
왜 이 개념들이 ECDSA에 중요한가?
ECDSA는 이 일방향성을 활용한다. 개인키를 직접 공개하지 않고도, 개인키를 알고 있다는 사실을 수학적으로 증명할 수 있게 해준다. 서명 생성 과정에서 스칼라 곱셈을 사용하고, 검증 과정에서 이 일방향성을 이용해 서명의 유효성을 확인한다.
ECDSA 동작 원리
이전 글 타원곡선 암호학에서 배운 개념들을 활용하여, 이제 실제로 디지털 서명을 생성하고 검증하는 방법을 배워보자.
ECDSA는 크게 세 단계로 구성된다.
1. 키 생성: 개인키 d → 공개키 Q = d × G
2. 서명 생성: 메시지 m + 개인키 d → 서명 (r, s)
3. 서명 검증: 메시지 m + 서명 (r, s) + 공개키 Q → 유효/무효 판단
각 단계에서 타원곡선의 수학적 성질이 어떻게 활용되는지 살펴보자.
1단계: 키 생성
키 생성은 타원곡선 암호학의 핵심 개념을 그대로 사용한다.
개인키(d): 1부터 n-1 사이의 랜덤한 정수를 선택한다.
- n은 곡선의 위수(order)로, 곡선 위의 점 개수다.
- secp256k1에서 n ≈ 2²⁵⁶이므로, 개인키는 256비트(32바이트) 크기다.
공개키(Q): 개인키와 생성점 G를 곱한다.
Q = d × G- G는 생성점(generator point)으로, secp256k1 표준에서 미리 정해진 점이다.
- 스칼라 곱셈
d × G는 이전 글에서 배운 Double-and-Add 알고리즘으로 빠르게 계산된다. - Q는 타원곡선 위의 점이므로 (x, y) 좌표로 표현된다.
개인키: d (256비트 랜덤 정수, 비밀)
공개키: Q = d × G (타원곡선 위의 점, 공개)
개인키에서 공개키를 계산하는 것은 쉽지만(순방향), 공개키에서 개인키를 역산하는 것은 타원곡선 이산 로그 문제(ECDLP)로 인해 사실상 불가능하다.
2단계: 서명 생성
메시지 m에 대한 서명을 생성하는 과정을 단계별로 살펴보자.
1. 메시지 해시: z = hash(m)
메시지를 해시 함수(SHA-256 등)로 처리하여 고정 길이 정수로 변환한다.
- 해시를 하는 이유: 메시지 길이가 가변적이므로, 항상 같은 크기의 정수로 변환해야 한다.
- 해시 함수의 일방향성 덕분에, 해시값만으로는 원본 메시지를 복원할 수 없다.
2. 랜덤 nonce 선택: k
1부터 n-1 사이의 랜덤 정수 k를 선택한다. 이 k를 nonce(number used once)라고 부른다.
- nonce가 필요한 이유: 같은 메시지에 대해 매번 다른 서명을 생성하기 위해서다.
- 같은 서명을 재사용하면 보안 문제가 발생할 수 있기 때문에 nonce는 매번 달라져야 한다.
3. 점 계산: R = k × G
nonce k와 생성점 G를 곱하여 곡선 위의 점 R을 계산한다.
- 이전 글에서 배운 스칼라 곱셈을 사용한다.
- R은 곡선 위의 점이므로 (x, y) 좌표를 가진다.
4. r 값 계산: r = R의 x좌표 mod n
점 R의 x좌표를 곡선의 위수 n으로 나눈 나머지를 r로 사용한다.
- x 좌표만 사용하는 이유: R은 곡선 위의 점이지만, 서명에선 그중 x좌표만 r로 사용한다. y좌표는 x축 대칭 때문에 두 후보가 있어 모호하므로, 필요할 때는 v(recovery id)로 어느 쪽인지 구분한다.
- mod n을 하는 이유: ECDSA의 모든 계산은 생성점의 위수 n 위에서 이루어진다. r과 s가 0~n-1 범위에 있도록 맞추고, 검증 수식과 일관되게 만들기 위해 mod n을 적용한다.
5. s 값 계산: s = k⁻¹ × (z + r × d) mod n
이것이 ECDSA의 핵심 공식이다. 각 요소의 의미를 하나씩 풀어보자.
- k⁻¹: k의 모듈러 역원 (k × k⁻¹ ≡ 1 mod n)
- z: 메시지 해시값
- r: 위에서 계산한 값
- d: 개인키
이 공식의 목적은 개인키 d를 직접 노출하지 않으면서도, d를 알고 있다는 사실을 증명하는 것이다.
z + r × d: 메시지 해시값(z)과 개인키(d)를 결합한다.
- 이 값만으로는 d를 역산할 수 없다 (r도 모르고, z는 해시값이므로).
- 하지만 d를 알고 있어야 이 값을 계산할 수 있다.
k⁻¹ × (...): nonce k의 역원을 곱한다.
- 이것은 검증 과정에서 k를 복원하기 위한 장치다.
- k⁻¹을 곱하면, 검증 시 s × k를 계산하여 원래의 (z + r × d)를 복원할 수 있다.
최종 결과 s:
- s를 계산하려면 개인키 d가 필요하다.
- 하지만 s만으로는 d를 역산할 수 없다 (이산 로그 문제).
- 검증자는 나중에 이 s 값을 이용해 서명의 유효성을 확인할 수 있다.
최종 서명은 (r, s) 쌍이다.
서명 = (r, s)
- r: 랜덤 점 R의 x좌표 (32바이트)
- s: 개인키를 사용한 증명값 (32바이트)
3단계: 서명 검증
서명 (r, s)와 공개키 Q, 메시지 m이 주어졌을 때, 개인키를 모르는 검증자가 서명의 유효성을 확인하는 과정이다. 핵심은 “서명할 때 만들어진 점 R을 간접적으로 다시 만들어서, r과 맞는지 확인”하는 것이다.
서명 생성 시: s = k⁻¹ × (z + r × d) mod n
여기서 k와 d는 숨겨져 있지만, s와 r, 공개키 Q는 공개되어 있다.
메시지 해시: z = hash(m).
- 서명 생성 때와 같은 해시 함수를 사용한다.
s의 역원 계산: s⁻¹ mod n.
- s에 곱하면 원래 값으로 되돌리는 값이다.
중간값 계산: u₁ = z × s⁻¹ mod n, u₂ = r × s⁻¹ mod n.
- u₁은 메시지 해시 쪽의 비율, u₂는 공개키 쪽의 비율이다.
- 이 비율로 두 항을 섞으면, 서명 때와 같은 점이 다시 나온다.
점 복원: R' = u₁ × G + u₂ × Q.
- G는 기준점, Q는 공개키다.
- 두 점을 가중치(u₁, u₂)로 섞어 R'을 만든다.
검증: R'의 x좌표가 r과 같으면 유효한 서명이다.
검증 성공 조건: R'.x ≡ r (mod n)- r은 k로 만든 R의 x좌표이지만, s에는 d가 섞여 있다.
- 검증 과정은 s와 Q를 이용해 “서명자가 만든 R”을 다시 만들고, 그 x좌표가 r과 같은지 확인한다.
검증자는 개인키 d를 몰라도 공개키 Q만으로 r과 맞는지 확인할 수 있다. 이때 s 안에는 d가 섞여 있고, Q 역시 d로 만든 값이므로 둘이 서로 맞아야만 R'이 제대로 복원된다. 즉, r은 k에서 나왔지만, s와 Q가 같은 d를 공유하지 않으면 r과 일치하는 R'이 나오지 않는다. 따라서 서명이 유효하려면, 서명 생성 시 사용한 개인키 d로 만든 공개키 Q여야 한다.
비트코인 vs 이더리움
비트코인과 이더리움은 모두 secp256k1 곡선과 ECDSA를 사용한다. 하지만 세부 구현에서 중요한 차이가 있다.
공통점
| 항목 | 비트코인 | 이더리움 |
|---|---|---|
| 타원곡선 | secp256k1 | secp256k1 |
| 서명 알고리즘 | ECDSA | ECDSA |
| 개인키 크기 | 256비트 | 256비트 |
| 공개키 크기 | 512비트 (비압축) | 512비트 (비압축) |
동일한 개인키로 비트코인과 이더리움 주소를 모두 생성할 수 있다. 실제로 많은 하드웨어 지갑이 이 방식을 사용한다.
주소 생성 방식
비트코인 주소 생성
공개키 → SHA256 → RIPEMD160 → Base58Check 인코딩
- 공개키를 SHA256으로 해시
- 결과를 RIPEMD160으로 다시 해시 (160비트 = 20바이트)
- 버전 바이트 추가 (0x00 for mainnet)
- 체크섬 추가 (더블 SHA256의 첫 4바이트)
- Base58Check로 인코딩
결과: 1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2
이더리움 주소 생성:
공개키 → Keccak256 → 마지막 20바이트 → Hex 인코딩
- 비압축 공개키(64바이트, 접두사 0x04 제외)를 Keccak256으로 해시
- 해시 결과의 마지막 20바이트를 주소로 사용
- 0x 접두사와 함께 16진수로 표현
결과: 0x71C7656EC7ab88b098defB751B7401B5f6d8976F
서명 형식
비트코인: DER 인코딩
비트코인은 서명을 DER(Distinguished Encoding Rules) 형식으로 인코딩한다.
30 <총 길이>
02 <r 길이> <r 값>
02 <s 길이> <s 값>
DER 인코딩의 특징
- 가변 길이 (70~72 바이트)
- r과 s 앞에 0x00 패딩이 필요할 수 있음 (음수로 해석되지 않도록)
- 구조화된 형식으로 파싱이 명확함
이더리움: r, s, v 분리
이더리움은 서명을 세 개의 값으로 분리해 저장한다.
r: 32바이트 (빅엔디안)
s: 32바이트 (빅엔디안)
v: 1바이트 (recovery id + chain id)
이더리움 서명의 특징
- 고정 길이 (65바이트)
- v 값으로 공개키 복구 가능 (ecrecover)
- EIP-155 이후 chain id가 v에 포함되어 리플레이 공격 방지
v 값의 의미
이더리움의 v 값은 recovery id에 추가 정보가 더해진 값이다.
기본 recovery id (0 또는 1)
ECDSA 서명에서 r 값은 점 R의 x좌표다. 하지만 r 값만으로는 원래 점 R을 유일하게 결정할 수 없다. 이전 글 타원곡선 암호학에서 보았듯이, 타원곡선은 x축 대칭이다. 방정식이 y² 형태이기 때문에, 같은 x좌표에 대해 두 개의 y좌표(+y, -y)가 존재할 수 있다.
- r = 5라면, 점 R은 (5, y₁) 또는 (5, -y₁) 중 하나일 수 있다.
- 두 점 모두 곡선 위에 있고, x좌표는 같지만 y좌표는 다르다.
recovery id의 역할
recovery id는 이 두 후보 중 어떤 점인지 구분하는 역할을 한다.
- recovery id = 0: y좌표가 짝수인 점
- recovery id = 1: y좌표가 홀수인 점
이 정보가 있으면 r 값과 함께 원래 점 R을 유일하게 복원할 수 있다. 이것이 이더리움의 ecrecover 함수가 공개키를 복구할 수 있는 이유다.
EIP-155 이전 (v = 27 또는 28)
초기 이더리움은 recovery id에 27을 더해서 v 값으로 사용했다.
v = recovery_id + 27
EIP-155 이후 (chain id 포함)
리플레이 공격을 방지하기 위해 chain id가 v에 포함되었다.
v = recovery_id + 35 + (chain_id × 2)
예시 (이더리움 메인넷, chain_id = 1):
v = 0 + 35 + 2 = 37 또는
v = 1 + 35 + 2 = 38
이를 통해 이더리움 메인넷에서 생성된 서명이 다른 체인(BSC, Polygon 등)에서 유효하지 않게 된다.
서명 알고리즘 비교: ECDSA vs Schnorr vs EdDSA
블록체인에서 사용되는 디지털 서명 알고리즘은 ECDSA만 있는 것이 아니다. 각 알고리즘의 특성을 비교해보자.
ECDSA
현재 비트코인과 이더리움의 표준 서명 알고리즘이다.
서명 공식
s = k⁻¹ × (z + r × d) mod n
- 1992년에 제안된 성숙한 알고리즘
- 광범위한 구현과 검증 이력
- 서명 생성에 모듈러 역원(k⁻¹) 계산 필요
- 비선형 구조로 인해 서명 집계가 불가능
비선형 구조란?
ECDSA 서명 공식 s = k⁻¹ × (z + r × d)를 보면, k⁻¹(역원)과 곱셈이 포함되어 있다.
- 두 서명 (r₁, s₁)과 (r₂, s₂)를 단순히 더하거나 곱해서 새로운 유효한 서명을 만들 수 없다.
- 각 서명은 독립적으로 생성되어야 한다.
- MPC(Multi-Party Computation) 적용 시 복잡한 프로토콜이 필요하다.
Schnorr 서명
비트코인 Taproot 업그레이드(2021)로 도입된 서명 알고리즘이다.
서명 공식
s = k + e × d (mod n)
여기서 e = hash(R || P || m)
- ECDSA보다 단순한 수학적 구조
- 선형성(Linearity)으로 서명 집계 가능
- MuSig 프로토콜로 다중 서명 효율화
- 배치 검증으로 여러 서명을 한 번에 빠르게 검증
선형성의 의미
선형성은 수학에서 매우 중요한 성질이다. 함수 f가 선형이면 f(a + b) = f(a) + f(b)를 만족한다.
Schnorr 서명 공식 s = k + e × d는 완전히 선형이다.
서명1: s₁ = k₁ + e × d₁
서명2: s₂ = k₂ + e × d₂
합산: s = s₁ + s₂ = (k₁ + k₂) + e × (d₁ + d₂)
이렇게 합산된 서명은 합산된 공개키(P₁ + P₂)에 대해 유효한 단일 서명이 된다.
왜 선형성이 중요한가?
- 서명 집계: 여러 서명자의 서명을 하나로 합칠 수 있다.
- 배치 검증: 여러 서명을 한 번에 빠르게 검증할 수 있다.
- MPC 친화적: 각 참여자가 부분 서명을 생성하고 단순히 합산하면 되어, ECDSA에 비해 MPC 적용이 훨씬 간단하다.
EdDSA (Ed25519)
Curve25519 곡선을 사용하는 서명 알고리즘으로, Solana, Cardano, Polkadot 등이 채택했다.
- 결정론적 nonce: 랜덤 생성 대신 메시지와 개인키로 nonce를 유도하여 nonce 재사용 취약점을 원천 차단
- 빠른 속도: 최적화된 구현으로 ECDSA보다 빠름
- 작은 서명 크기: 64바이트
- 배치 검증 지원: 여러 서명을 동시에 빠르게 검증
코드로 보는 ECDSA
ethers.js를 사용해 서명 생성, 검증, r/s/v 값 테스트를 해보자.
전체 테스트 코드는 crypto-examples/cryptography-examples/03-ecdsa에서 확인할 수 있다.
import { ethers } from "ethers";
const privateKey =
"0x4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318";
const wallet = new ethers.Wallet(privateKey);
const message = "Hello, ECDSA!";
async function run() {
console.log("=== Key Info ===");
console.log("Private Key:", wallet.privateKey);
console.log("Public Key:", wallet.signingKey.publicKey);
console.log("Address:", wallet.address);
console.log("\n=== Sign ===");
console.log("Message:", message);
const signature = await wallet.signMessage(message);
console.log("Signature:", signature);
console.log("\n=== Verify ===");
const recoveredAddress = ethers.verifyMessage(message, signature);
console.log("Recovered Address:", recoveredAddress);
console.log("Original Address:", wallet.address);
console.log("Match:", recoveredAddress === wallet.address);
console.log("\n=== r, s, v Analysis ===");
console.log("Full Signature (65 bytes):", signature);
console.log("Signature Length:", ethers.getBytes(signature).length, "bytes");
const sig = ethers.Signature.from(signature);
console.log("r (32 bytes):", sig.r);
console.log("s (32 bytes):", sig.s);
console.log("v (recovery id):", sig.v);
console.log("\n=== v Value Interpretation ===");
if (sig.v === 27 || sig.v === 28) {
console.log("Pre-EIP-155 format (legacy)");
console.log("Recovery ID:", sig.v - 27);
} else {
const recoveryId = (sig.v - 35) % 2;
const chainId = Math.floor((sig.v - 35) / 2);
console.log("EIP-155 format");
console.log("Recovery ID:", recoveryId);
console.log("Chain ID:", chainId);
}
console.log("\n=== Public Key Recovery ===");
const messageHash = ethers.hashMessage(message);
const recoveredPublicKey = ethers.SigningKey.recoverPublicKey(
messageHash,
signature
);
console.log("Recovered Public Key:", recoveredPublicKey);
console.log("Original Public Key:", wallet.signingKey.publicKey);
console.log("Match:", recoveredPublicKey === wallet.signingKey.publicKey);
}
run();
위 코드를 실행하면 다음과 같은 결과를 확인할 수 있다.
=== Key Info ===
Private Key: 0x4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318
Public Key: 0x044e3b81af9c2234cad09d679ce6035ed1392347ce64ce405f5dcd36228a25de6e47fd35c4215d1edf53e6f83de344615ce719bdb0fd878f6ed76f06dd277956de
Address: 0x2c7536E3605D9C16a7a3D7b1898e529396a65c23
=== Sign ===
Message: Hello, ECDSA!
Signature: 0xfdaf07b43e6119f3a15c6edd90b11ae7991a083d36ba26e430d698ed0e72c06865405f56d3a9452ad020500150c4bf1bef71f683cbcc20501d65b39fad5e9c4a1c
=== Verify ===
Recovered Address: 0x2c7536E3605D9C16a7a3D7b1898e529396a65c23
Original Address: 0x2c7536E3605D9C16a7a3D7b1898e529396a65c23
Match: true
=== r, s, v Analysis ===
Full Signature (65 bytes): 0xfdaf07b43e6119f3a15c6edd90b11ae7991a083d36ba26e430d698ed0e72c06865405f56d3a9452ad020500150c4bf1bef71f683cbcc20501d65b39fad5e9c4a1c
Signature Length: 65 bytes
r (32 bytes): 0xfdaf07b43e6119f3a15c6edd90b11ae7991a083d36ba26e430d698ed0e72c068
s (32 bytes): 0x65405f56d3a9452ad020500150c4bf1bef71f683cbcc20501d65b39fad5e9c4a
v (recovery id): 28
=== v Value Interpretation ===
Pre-EIP-155 format (legacy)
Recovery ID: 1
=== Public Key Recovery ===
Recovered Public Key: 0x044e3b81af9c2234cad09d679ce6035ed1392347ce64ce405f5dcd36228a25de6e47fd35c4215d1edf53e6f83de344615ce719bdb0fd878f6ed76f06dd277956de
Original Public Key: 0x044e3b81af9c2234cad09d679ce6035ed1392347ce64ce405f5dcd36228a25de6e47fd35c4215d1edf53e6f83de344615ce719bdb0fd878f6ed76f06dd277956de
Match: true
같은 메시지라도 서명을 다시 생성하면 값이 달라질 수 있다. 이는 nonce(k)가 매번 달라지기 때문이다.
보안 고려사항
nonce 재사용
ECDSA에서 가장 치명적인 취약점은 nonce(k) 재사용이다. 같은 nonce로 두 개의 다른 메시지에 서명하면 개인키가 노출된다.
nonce 재사용이 위험한 이유
s = k⁻¹ × (z + r × d) mod n
여기서 k는 랜덤 nonce이고, d는 개인키다. 만약 같은 k를 두 번 사용하면:
첫 번째 메시지 m₁에 대한 서명
- z₁ = hash(m₁)
- r₁ = (k × G).x mod n
- s₁ = k⁻¹ × (z₁ + r₁ × d) mod n
두 번째 메시지 m₂에 대한 서명 (같은 k 사용)
- z₂ = hash(m₂)
- r₂ = (k × G).x mod n = r₁ (같은 k이므로 같은 점 R)
- s₂ = k⁻¹ × (z₂ + r₂ × d) mod n
개인키 추출 과정
공격자는 공개된 서명 (r₁, s₁)과 (r₂, s₂)를 관찰할 수 있다. r₁ = r₂ = r임을 알 수 있다.
s₁ = k⁻¹(z₁ + rd) mod n
s₂ = k⁻¹(z₂ + rd) mod n
s₁ - s₂ = k⁻¹(z₁ - z₂) mod n
k = (z₁ - z₂)(s₁ - s₂)⁻¹ mod n
nonce k를 알아내면, 이제 개인키 d를 계산할 수 있다.
s₁ = k⁻¹(z₁ + rd) mod n
s₁ × k = z₁ + rd mod n
rd = s₁ × k - z₁ mod n
d = r⁻¹(s₁ × k - z₁) mod n
사례: Sony PlayStation 3 해킹 (2010)
Sony는 PS3의 소프트웨어 서명에 ECDSA를 사용했는데, 모든 서명에 동일한 nonce를 사용하는 실수를 저질렀다. 해커들은 이를 통해 Sony의 개인키를 추출하여 임의의 소프트웨어에 서명할 수 있게 되었다.
불충분한 랜덤성
nonce가 완전히 랜덤하지 않아도 취약할 수 있다.
- 예측 가능한 난수 생성기 사용
- 타임스탬프나 순차적 값을 nonce로 사용
- 엔트로피가 부족한 환경에서 생성
RFC 6979: 결정론적 nonce
이러한 문제를 해결하기 위해 RFC 6979는 결정론적 nonce 생성 방법을 제안한다.
k = HMAC(private_key, hash(message))
- 같은 메시지에는 같은 nonce가 생성되어, 재서명해도 동일한 서명 생성
- 개인키와 메시지가 달라지면 nonce도 달라짐
- 랜덤 소스 없이도 안전한 nonce 생성
대부분의 현대 라이브러리(ethers.js 포함)는 RFC 6979를 기본으로 사용한다.
정리
요약
- ECDSA는 타원곡선의 이산 로그 문제에 기반한 디지털 서명 알고리즘이다.
- 서명 과정: 개인키와 랜덤 nonce로 (r, s) 값을 생성
- 검증 과정: 공개키만으로 서명의 유효성 확인 가능
- 비트코인 vs 이더리움: 같은 secp256k1 곡선을 사용하지만 주소 생성, 서명 형식, 해시 방식이 다름
ECDSA의 한계
- 비선형 구조: 서명 집계나 MPC 적용이 Schnorr에 비해 복잡함
- 양자 컴퓨팅 위협: Shor 알고리즘으로 이산 로그 문제 해결 가능 (이론적)
- 대안의 등장: Schnorr (Bitcoin Taproot), EdDSA (Solana, Cardano)
블록체인 생태계는 점진적으로 더 효율적이고 안전한 서명 알고리즘으로 전환하고 있다. 이더리움은 Account Abstraction(ERC-4337)을 통해 계정 수준에서 서명 알고리즘을 추상화하는 방향으로 발전하고 있다.