Workspace IndexAlgorithms › Day 84

Random Number Generation and CSPRNG Quality (TAOCP Vol. 2) — Statistical Testing and Seed Management, Upstream of Nonce-Reuse Incidents TODO

Algorithms · Day 84 / 100 · F. Cryptography & ZK (Day 82-96)

Concept

Random number generators split into PRNGs, which deterministically generate a sequence from a seed, and CSPRNGs, which are designed to guarantee unpredictability. Statistical test suites check whether output shows abnormal structure in uniformity or independence, but passing those tests doesn't imply cryptographic security. Generators that look statistically fine, like linear congruential generators or the Mersenne Twister, can have their internal state reconstructed from just a small amount of observed output, letting an attacker predict every value from then on. A CSPRNG must ensure that knowing previous outputs gives no meaningful edge in predicting the next bit, that exposing the state can't roll back to recover past outputs, and that seeding comes from an OS entropy source. Failures usually come from seed and state management, not the algorithm — classic cases are seeding right after boot when entropy is scarce, and state duplication from fork or VM snapshot cloning.

Signature nonces, session tokens, and key generation all depend on this, and a single reproducible random value leads straight to a leaked private key. ECDSA in particular: reuse the same nonce across two different signatures and the private key can be recovered algebraically.

Code & Formula

# 난수 생성과 CSPRNG 품질 — 통계적 검정(빈도·런) 통과가 예측불가능성을 뜻하지 않음을 보인다.
# 선형합동생성기(LCG)는 검정을 통과해도 상태 복원이 쉽고, secrets 는 OS 엔트로피 기반이라 다르다.

import secrets

class WeakLCG:
    """교육용 취약 PRNG — 통계 검정은 통과하지만 관측값으로 상태를 복원해 다음 값을 예측 가능."""
    def __init__(self, seed):
        self.state = seed
        self.a, self.c, self.m = 1103515245, 12345, 2**31

    def next(self):
        self.state = (self.a * self.state + self.c) % self.m
        return self.state

def monobit_test(bits):
    """간단 빈도 검정: 0/1 비율이 균형에 가까운지만 본다 (진짜 무작위성 증명은 아님)."""
    ones = sum(bits)
    return abs(ones - len(bits) / 2) < len(bits) * 0.05

lcg = WeakLCG(seed=42)
lcg_bits = [lcg.next() & 1 for _ in range(1000)]
print("LCG passes naive monobit test:", monobit_test(lcg_bits))

# 취약점: 연속된 출력 두 개만 관측하면 다음 값을 그대로 예측할 수 있다 (선형 재귀이므로)
attacker_lcg = WeakLCG(seed=1)
observed = [attacker_lcg.next() for _ in range(2)]
recovered = WeakLCG(seed=42)
recovered.state = lcg.state  # 공격자가 내부 상태를 역산했다고 가정
predicted_next = recovered.next()
actual_next = lcg.next()
print("LCG next value predictable once state is known:", predicted_next == actual_next)

# CSPRNG: secrets 모듈은 OS 엔트로피(os.urandom)를 쓰고, 이전 출력으로 다음을 예측할 수 없다
csprng_bits = [secrets.randbits(1) for _ in range(1000)]
print("CSPRNG passes naive monobit test too:", monobit_test(csprng_bits))
print("=> passing a statistical test proves nothing about predictability;")
print("   seed source and state-recovery resistance are what make a generator crypto-safe")

Exercise

Compare seed/output reproducibility between a language's general-purpose random function and its cryptographic random function (e.g., Node.js's Math.random vs. crypto.randomBytes), and check whether two children of a forked process draw the same values.

Practical Connection

Every signing path Verex touches — order signatures, oracle submissions, relayer keys — has its security riding on the quality of the nonce and key-generation randomness, so the rule is to use a vetted library with a deterministic nonce spec rather than rolling your own.

Where it lands in Jayverse

Key expressions

Words and phrases from this page worth keeping, with the Korean meaning and the sentence they come from.

Expression뜻 · 쓰이는 자리
upstream of~의 원인 쪽(상류)에 있는 · 문제의 근본 원인이 더 앞단에 있음을 가리킬 때. "Upstream of Nonce-Reuse Incidents"
roll back(상태를) 되돌리다 · 과거 값을 역산해 복구하지 못하게 막는 성질을 말할 때. "can't roll back to recover past outputs"
roll your own직접 만들어 쓰다(비표준 방식으로) · 검증된 라이브러리 대신 스스로 구현하는 것을 경계할 때. "rather than rolling your own"
meaningful edge의미 있는 우위, 예측에 도움이 되는 정보 · 공격자가 다음 값을 맞힐 단서를 얻는지 말할 때. "gives no meaningful edge in predicting the next bit"
state duplication내부 상태 중복 발생 · 프로세스 포크나 VM 스냅샷 복제로 난수 상태가 복사될 때. "state duplication from fork or VM snapshot cloning"
vetted library검증된 라이브러리 · 신뢰할 수 있다고 확인된 코드베이스를 가리킬 때. "use a vetted library with a deterministic nonce spec"
recovered algebraically대수적으로(수식 계산으로) 복구되다 · 개인키가 수학적 계산만으로 역산될 때. "the private key can be recovered algebraically"
TAOCP컴퓨터 프로그래밍의 예술(The Art of Computer Programming) · 도널드 커누스의 저서, 이 카드가 다루는 난수 이론의 원전으로 표제에 인용됨. "Random Number Generation and CSPRNG Quality (TAOCP Vol. 2)"
PRNG의사난수생성기(Pseudo-Random Number Generator) · 시드로부터 결정론적으로 수열을 만드는 생성기, 암호적으로 안전하지 않을 수 있음. "PRNGs, which deterministically generate a sequence from a seed"
CSPRNG암호학적으로 안전한 의사난수생성기(Cryptographically Secure PRNG) · 예측 불가능성을 보장하도록 설계된 난수 생성기. "CSPRNGs, which are designed to guarantee unpredictability"
ECDSA타원곡선 전자서명 알고리즘(Elliptic Curve Digital Signature Algorithm) · 논스를 재사용하면 개인키가 복구되는 대표 사례로 언급. "ECDSA in particular: reuse the same nonce across two different signatures"
Mersenne Twister통계적으로는 양호해 보이지만 예측 가능한 대표적 PRNG 알고리즘 · 내부 상태가 적은 출력만으로 복원될 수 있는 예로 언급. "linear congruential generators or the Mersenne Twister"

If you study this on a given day, add a note link and a ✅ to this line in the source curriculum (docs/knowledge/dev-100-curriculum.md) and this spot will lead straight to the note body. You can also write directly on this page — but regenerating overwrites it, so it's safer to keep anything you want to save as markdown under docs/algorithms/.


한국어

난수 생성과 CSPRNG 품질 (TAOCP 2권) TODO

Algorithms · Day 84 / 100 · F. 암호학·ZK (Day 82–96)

통계적 검정·시드 관리, nonce 재사용 사고의 상류

개념

난수 생성기는 시드에서 결정적으로 수열을 만드는 PRNG와, 예측 불가능성을 보장하도록 설계된 CSPRNG로 나뉜다. 통계적 검정 묶음은 출력이 균등성·독립성 면에서 이상한 구조를 보이는지 검사하지만, 검정을 통과했다는 사실이 암호학적 안전성을 뜻하지는 않는다. 선형 합동법이나 메르센 트위스터처럼 통계적으로 무난한 생성기도 출력을 조금만 관측하면 내부 상태를 복원해 이후 값을 전부 예측할 수 있다. CSPRNG는 이전 출력을 알아도 다음 비트를 유의미하게 예측할 수 없어야 하고, 상태가 노출돼도 과거 출력을 되돌릴 수 없어야 하며, 시드는 OS 엔트로피 소스에서 받아야 한다. 실패는 대개 알고리즘이 아니라 시드·상태 관리에서 나는데, 엔트로피가 부족한 부팅 직후 시딩, fork나 VM 스냅샷 복제로 인한 상태 중복이 대표적이다.

서명 nonce, 세션 토큰, 키 생성이 모두 여기에 의존하며, 재현되는 난수 하나가 곧바로 개인키 노출로 이어진다. 특히 ECDSA는 서로 다른 두 서명에서 같은 nonce를 쓰면 대수적으로 개인키가 복원된다.

코드 · 수식

# 난수 생성과 CSPRNG 품질 — 통계적 검정(빈도·런) 통과가 예측불가능성을 뜻하지 않음을 보인다.
# 선형합동생성기(LCG)는 검정을 통과해도 상태 복원이 쉽고, secrets 는 OS 엔트로피 기반이라 다르다.

import secrets

class WeakLCG:
    """교육용 취약 PRNG — 통계 검정은 통과하지만 관측값으로 상태를 복원해 다음 값을 예측 가능."""
    def __init__(self, seed):
        self.state = seed
        self.a, self.c, self.m = 1103515245, 12345, 2**31

    def next(self):
        self.state = (self.a * self.state + self.c) % self.m
        return self.state

def monobit_test(bits):
    """간단 빈도 검정: 0/1 비율이 균형에 가까운지만 본다 (진짜 무작위성 증명은 아님)."""
    ones = sum(bits)
    return abs(ones - len(bits) / 2) < len(bits) * 0.05

lcg = WeakLCG(seed=42)
lcg_bits = [lcg.next() & 1 for _ in range(1000)]
print("LCG passes naive monobit test:", monobit_test(lcg_bits))

# 취약점: 연속된 출력 두 개만 관측하면 다음 값을 그대로 예측할 수 있다 (선형 재귀이므로)
attacker_lcg = WeakLCG(seed=1)
observed = [attacker_lcg.next() for _ in range(2)]
recovered = WeakLCG(seed=42)
recovered.state = lcg.state  # 공격자가 내부 상태를 역산했다고 가정
predicted_next = recovered.next()
actual_next = lcg.next()
print("LCG next value predictable once state is known:", predicted_next == actual_next)

# CSPRNG: secrets 모듈은 OS 엔트로피(os.urandom)를 쓰고, 이전 출력으로 다음을 예측할 수 없다
csprng_bits = [secrets.randbits(1) for _ in range(1000)]
print("CSPRNG passes naive monobit test too:", monobit_test(csprng_bits))
print("=> passing a statistical test proves nothing about predictability;")
print("   seed source and state-recovery resistance are what make a generator crypto-safe")

연습

언어 표준 라이브러리의 일반 난수 함수와 암호용 난수 함수(예: Node.js의 Math.random과 crypto.randomBytes)로 각각 시드/출력 재현성을 실험하고, 프로세스 fork 후 두 자식이 같은 값을 뽑는지 확인하라.

실무 · Verex 연결

Verex가 다루는 서명 경로(주문 서명, 오라클 제출, 릴레이어 키)는 모두 nonce·키 생성 난수의 품질에 안전성이 걸려 있으므로, 결정적 nonce 규격을 쓰는 검증된 라이브러리를 쓰고 직접 구현하지 않는 것이 원칙이다.

Jayverse에서의 위치

핵심 표현

이 페이지의 영어 본문에서 배울 만한 단어와 표현, 뜻과 나온 자리.

Expression뜻 · 쓰이는 자리
upstream of~의 원인 쪽(상류)에 있는 · 문제의 근본 원인이 더 앞단에 있음을 가리킬 때. "Upstream of Nonce-Reuse Incidents"
roll back(상태를) 되돌리다 · 과거 값을 역산해 복구하지 못하게 막는 성질을 말할 때. "can't roll back to recover past outputs"
roll your own직접 만들어 쓰다(비표준 방식으로) · 검증된 라이브러리 대신 스스로 구현하는 것을 경계할 때. "rather than rolling your own"
meaningful edge의미 있는 우위, 예측에 도움이 되는 정보 · 공격자가 다음 값을 맞힐 단서를 얻는지 말할 때. "gives no meaningful edge in predicting the next bit"
state duplication내부 상태 중복 발생 · 프로세스 포크나 VM 스냅샷 복제로 난수 상태가 복사될 때. "state duplication from fork or VM snapshot cloning"
vetted library검증된 라이브러리 · 신뢰할 수 있다고 확인된 코드베이스를 가리킬 때. "use a vetted library with a deterministic nonce spec"
recovered algebraically대수적으로(수식 계산으로) 복구되다 · 개인키가 수학적 계산만으로 역산될 때. "the private key can be recovered algebraically"
TAOCP컴퓨터 프로그래밍의 예술(The Art of Computer Programming) · 도널드 커누스의 저서, 이 카드가 다루는 난수 이론의 원전으로 표제에 인용됨. "Random Number Generation and CSPRNG Quality (TAOCP Vol. 2)"
PRNG의사난수생성기(Pseudo-Random Number Generator) · 시드로부터 결정론적으로 수열을 만드는 생성기, 암호적으로 안전하지 않을 수 있음. "PRNGs, which deterministically generate a sequence from a seed"
CSPRNG암호학적으로 안전한 의사난수생성기(Cryptographically Secure PRNG) · 예측 불가능성을 보장하도록 설계된 난수 생성기. "CSPRNGs, which are designed to guarantee unpredictability"
ECDSA타원곡선 전자서명 알고리즘(Elliptic Curve Digital Signature Algorithm) · 논스를 재사용하면 개인키가 복구되는 대표 사례로 언급. "ECDSA in particular: reuse the same nonce across two different signatures"
Mersenne Twister통계적으로는 양호해 보이지만 예측 가능한 대표적 PRNG 알고리즘 · 내부 상태가 적은 출력만으로 복원될 수 있는 예로 언급. "linear congruential generators or the Mersenne Twister"

공부한 날 원본 커리큘럼(docs/knowledge/dev-100-curriculum.md)의 이 줄에 노트 링크와 ✅ 를 붙이면, 이 자리는 노트 본문으로 바로 이어집니다. 노트 없이 이 페이지에 바로 적어도 됩니다 — 다만 다시 생성하면 덮어쓰이므로, 남길 글은 docs/algorithms/ 의 마크다운으로 쓰는 편이 안전합니다.

← 1136. HMAC·AEAD와 nonce 오용 저항1138. 서명 스킴 비교 →