Hash Function Design Principles (Sponge, Merkle-Damgård) TODO
Concept
A cryptographic hash function maps an arbitrary-length input to a fixed-length output while aiming for preimage resistance, second-preimage resistance, and collision resistance. Because of the birthday problem, collision resistance for an n-bit output is capped at roughly 2^(n/2), so the choice of output length is what sets the security level. The Merkle-Damgård construction splits the input into blocks and repeatedly applies a compression function, appending length-encoding padding at the end; it's provable that if the compression function is collision resistant, so is the whole construction — but because the internal state is exactly the output, it's vulnerable to length-extension attacks. The sponge construction instead splits internal state into a rate portion that's exposed and a capacity portion that's never exposed; it absorbs input and then squeezes out as much output as needed, which supports arbitrary-length output and is not vulnerable to length-extension attacks. The security level is governed by the size of the capacity.
Using a hash for authentication without knowing whether it's vulnerable to length extension breaks the MAC construction, and choosing too short an output length opens the door to collision-based attacks.
Code & Formula
# 해시함수 설계 원리(스펀지·머클-담고르) — 고정 크기 압축함수를 체이닝해 임의 길이 입력을
# 고정 길이로 접는 머클-담고르 구성을 hashlib 없이 토이 버전으로 직접 만들어 본다.
def compress(state: int, block: int, mod: int = 2**32) -> int:
# 진짜 해시가 아니라 예시용 압축함수 — 비선형 섞기 흉내만 낸다.
return ((state ^ block) * 2654435761 + 0x9E3779B9) % mod
def merkle_damgard(message: bytes, block_size: int = 4) -> int:
padded = message + b"\x80" + b"\x00" * ((-len(message) - 1) % block_size)
state = 0
for i in range(0, len(padded), block_size):
block = int.from_bytes(padded[i:i + block_size], "big")
state = compress(state, block)
return state
h1 = merkle_damgard(b"hello world")
h2 = merkle_damgard(b"hello world!") # 한 글자만 달라짐
h3 = merkle_damgard(b"hello world") # 같은 입력 → 같은 해시
print(f"H('hello world') = {h1:#010x}")
print(f"H('hello world!') = {h2:#010x} (한 글자만 달라도 완전히 다른 출력 — 눈사태 효과)")
print(f"determinism check: H('hello world') 재계산 == 원래 값? {h1 == h3}")
Exercise
Build a naive secret-prefix MAC using a Merkle-Damgård-family hash and actually succeed at a length-extension attack against it, then try the same attack against a sponge-family hash and against HMAC and confirm they're not vulnerable.
Practical Connection
Ethereum uses Keccak-family sponge hashes, and Merkle trees, address generation, and storage keys all depend on it — so how you construct hash input encoding for Verex's condition ID or position ID is directly a question of collision safety.
Where it lands in Jayverse
- Verex: document which hash construction backs condition ID and position ID generation. Confirm it's Keccak-family sponge (inherited from Solidity), and confirm the input encoding can't let two different conditions collide on the same ID — an encoding ambiguity, not the hash itself, is the usual source of that bug.
- Auditor: add a length-extension test to the checklist for any hash-based authentication scheme. If Verex or another service ever signs API tokens or session data on top of a raw hash, confirm it's HMAC or a sponge construction rather than naive secret-prefix Merkle-Damgård before trusting it as a MAC.
Key expressions
| Expression | 뜻 · 쓰이는 자리 |
|---|---|
| preimage resistance | 역상 저항성 · 출력값으로부터 원래 입력을 찾기 어려운 성질을 가리키는 암호학 용어. "aiming for preimage resistance, second-preimage resistance, and collision resistance" |
| length-extension attack | 길이 확장 공격 · 해시 뒤에 데이터를 이어 붙여 위조할 수 있는 취약점. "it's vulnerable to length-extension attacks" |
| squeeze out | (결과를) 짜내다, 뽑아내다 · 스펀지 구조가 필요한 만큼 출력을 만들어낼 때 쓰는 동사. "squeezes out as much output as needed" |
| capped at | ~로 상한이 정해지다 · 값이 특정 수준을 넘지 못하도록 제한될 때. "is capped at roughly 2^(n/2)" |
| opens the door to | ~의 여지를 열다, ~을 가능하게 만들다 · 부주의한 선택이 공격 가능성을 만들 때. "opens the door to collision-based attacks" |
| governed by | ~에 의해 결정되다 · 보안 수준이 어떤 요소로 정해지는지 말할 때. "The security level is governed by the size of the capacity" |
| provable that | ~라는 것이 증명 가능하다 · 수학적으로 엄밀히 보장된 성질을 말할 때. "it's provable that if the compression function is collision resistant" |
| HMAC | 해시 기반 메시지 인증 코드(Hash-based Message Authentication Code) · 스펀지·머클-담고르 계열과 함께 길이확장 공격 내성을 검증하는 대상. "and against HMAC and confirm they're not vulnerable" |
| MAC | 메시지 인증 코드(Message Authentication Code) · 해시로 인증을 구현하는 구조, 길이확장 공격에 취약할 수 있음. "breaks the MAC construction" |
| Merkle-Damgård | 머클-담고르 구성(해시 함수 설계 방식) · 압축 함수를 반복 적용해 임의 길이 입력을 처리하는 전통적 해시 구조. "The Merkle-Damgård construction splits the input into blocks" |
| sponge construction | 스펀지 구성(해시 함수 설계 방식) · 내부 상태를 rate/capacity로 나누어 길이확장 공격에 안전한 최신 구조. "The sponge construction instead splits internal state into a rate portion" |
| Keccak | 케첵(이더리움이 쓰는 스펀지 계열 해시 함수군) · 이더리움의 주소 생성, 머클트리, 스토리지 키가 이 해시에 의존. "Ethereum uses Keccak-family sponge hashes" |
If you study this on a given day, add a note link and a ✅ to this line in the source curriculum (docs/knowledge/math-50-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/.