Zero-Knowledge Schnorr Proof 구현체 — Rust로 작성된 가장 간단한 형태의 타원곡선 기반 영지식 증명
*** 학습 및 세미나 공유 목적이니 절대 프로덕션에 사용하지 마세요.
Schnorr Proof는 비밀 키를 공개하지 않고도 해당 키를 알고 있음을 증명할 수 있는 영지식 증명(Zero-Knowledge Proof) 입니다.
이 프로젝트는 암호학적 기초 원리를 직접 구현하여 학습하고, 실제 Bitcoin/Ethereum에서 사용되는 서명 알고리즘의 동작 원리를 이해하기 위해 개발되었습니다. (PDAO 세미나 공유 용도)
Setup:
G = Generator Point (타원곡선 상의 기준점)
x = Private Key (비밀 키)
P = x·G (Public Key, 공개 키)
Signature Generation:
1. k ← random() # 랜덤 nonce 생성
2. R = k·G # Commitment
3. e = H(m) # Challenge (메시지 해시)
4. s = k + e·x # Response
σ = (R, s) # 서명
Verification:
s·G ?= R + e·P # 검증 방정식
⚠️ Note: 이 구현에서는 학습 목적으로 단순화된e = H(m)방식을 사용합니다.
실제 BIP-340에서는e = H(R || P || m)형태의 Fiat-Shamir 변환을 사용합니다.
- Completeness: 정직한 증명자는 항상 검증을 통과
- Soundness: 비밀 키 없이는 유효한 증명 생성 불가능
- Zero-Knowledge: 검증 과정에서 비밀 키
x에 대한 정보가 노출되지 않음
schnorr_proof/
├── src/
│ ├── lib.rs # 라이브러리 진입점 및 테스트
│ ├── zkp.rs # Schnorr 증명 핵심 구현
│ └── compat.rs # 타원곡선 추상화 레이어
└── Cargo.toml
| 모듈 | 설명 |
|---|---|
ECCurve |
다양한 타원곡선을 지원하기 위한 추상화 트레이트 |
SchnorrProof<C> |
제네릭 Schnorr 증명 구조체 |
k256_impl |
secp256k1 곡선 구현체 |
use schnorr_proof::{SchnorrProof, ECCurve};
use k256::{Scalar, Secp256k1, ProjectivePoint};
// 1. 키 쌍 생성
let private_key = Scalar::generate_biased(&mut rand::thread_rng());
let public_key = (ProjectivePoint::GENERATOR * private_key).to_affine();
// 2. 챌린지 생성 (메시지 해시)
let message = b"Hello, PDAO!";
let challenge = scalar_hash(message);
// 3. 서명 생성
let proof = SchnorrProof::<Secp256k1>::create_signature(
private_key,
challenge,
&mut rand::thread_rng(),
);
// 4. 검증
assert!(proof.verify(public_key, challenge));use k256::schnorr::{SigningKey, VerifyingKey};
// BIP-340은 짝수 y 좌표를 요구
let affine = public_key.to_affine();
if affine.y_is_odd().into() {
private_key = -private_key; // y 좌표 정규화
}
let signing_key = SigningKey::from(SecretKey::from_bytes(&private_key.to_bytes())?);
let signature = signing_key.sign_raw(message, &aux_rand)?;
// 검증
let verifying_key = VerifyingKey::try_from(public_key)?;
verifying_key.verify_raw(message, &signature)?;pub trait ECCurve: PrimeCurve + CurveArithmetic {
const NAME: &'static [u8];
const BITS: usize;
fn generator() -> Self::ProjectivePoint;
fn sample_scalar_constant_time<R: CryptoRngCore>(r: &mut R) -> Self::Scalar;
}이 추상화를 통해 secp256k1 외에도 다른 곡선(secp256r1 등)으로 쉽게 확장 가능합니다.
fn sample_scalar_constant_time<R: CryptoRngCore>(r: &mut R) -> Self::Scalar {
let mut data = [0u8; 64];
r.fill_bytes(&mut data);
<Self::Scalar as Reduce<U512>>::reduce_bytes(&data.into())
}사이드 채널 공격 방지를 위해 상수 시간(constant-time) 연산을 사용합니다.
- Language: Rust 2021 Edition
- Elliptic Curve:
elliptic-curve0.13.8 - secp256k1:
k2560.13.4 - Hashing: SHA-256 (
sha2) - Serialization: Serde
# 모든 테스트 실행
cargo test
# 특정 테스트 실행 (상세 출력)
cargo test test_schnorr_signature -- --nocapture- Schnorr's Identification Protocol
- BIP-340: Schnorr Signatures for secp256k1
- Zero-Knowledge Proofs: An Illustrated Primer
MIT License © 2024