Search
Duplicate

2.블록체인의 정의

블록체인: 데이터가 저장된 ‘블록’이 ‘체인’으로 연결
[구조의 이해] ‘동일한 거래내역’을 기존 구조와 블록체인 구조를 비교한다면?
1.
기존의 데이터 구조
INDEX
판매자
구매자
개수
시간
0
파랑불
김민수
3개
1990년 1월 1일 00시 00분 00초
1
김민수
김영수
3개
1990년 1월 2일 01시 02분 03초
2
김영수
박영수
3개
1990년 1월 3일 02시 03분 04초
3
박영수
이미래
3개
1990년 1월 4일 02시 03분 04초
4
이미래
손영호
3개
1990년 1월 5일 02시 03분 04초
5
손영호
수영호
3개
1990년 1월 6일 02시 03분 04초
2.
블록체인의 데이터 구조
┌───────────────┐ │ 블록1 │ │--------------- │ │ 'INDEX' : 0 │ │ '판매자' : 파이썬 │ │ '구매자' : 김민수 │ │ '개수' : 3개 │ │ '시간' : 1990-01-01 00:00:00 │ └───────────────┘ │ ▼ ┌───────────────┐ │ 블록2 │ │--------------- │ │ 'INDEX' : 1 │ │ '판매자' : 김민수 │ │ '구매자' : 김영수 │ │ '개수' : 3개 │ │ '시간' : 1990-01-01 00:00:00 │ └───────────────┘ │ ▼ ┌───────────────┐ │ 블록3 │ │--------------- │ │ 'INDEX' : 3 │ │ '판매자' : 김영수 │ │ '구매자' : 박명수 │ │ '개수' : 3개 │ │ '시간' : 1990-01-01 00:00:00 │ └───────────────┘

1.파이썬으로 만드는 블록체인

여기서 핵심은 ‘previous_block’
previous_block이 chain역할을 함
block1 = { 'INDEX': 0, '판매자': '파이썬', '구매자': '김민수', '개수': '3개', '시간': '2022년 5월 11일 16시 25분 16초', 'previous_block': None } block2 = { 'INDEX': 1, '판매자': '김민수', '구매자': '김영수', '개수': '3개', '시간': '2022년 5월 12일 13시 25분 16초', 'previous_block': block1 } block3 = { 'INDEX': 2, # 수정: 3 → 2 (순서대로) '판매자': '김영수', '구매자': '박명수', '개수': '3개', '시간': '2022년 5월 13일 16시 25분 16초', 'previous_block': block2 } block4 = { 'INDEX': 3, # 수정: 4 → 3 (순서대로) '판매자': '박명수', '구매자': '이미래', '개수': '3개', '시간': '2022년 5월 14일 16시 25분 16초', 'previous_block': block3 } block5 = { 'INDEX': 4, # 수정: 5 → 4 (순서대로) '판매자': '이미래', '구매자': '최용수', '개수': '3개', '시간': '2022년 5월 15일 16시 25분 16초', 'previous_block': block4 } block6 = { 'INDEX': 5, # 수정: 6 → 5 (순서대로) '판매자': '최용수', '구매자': '강영희', '개수': '3개', '시간': '2022년 5월 16일 16시 25분 16초', # 수정: 공백 추가 'previous_block': block5 }
Python
복사
위와 같은 형태로 블록이 체인으로 연결되며 거래가 계속 진행되면 어떻게 될까?
참고로 비트코인은 75만개의 블록이 생성되어 있으며, 실시간으로 약 10분당 1개의 블록이 생성되고 있음
또 이더리움은 150만개 이상의 블록이 생성되어 있으며, 실시간으로 약 12초에 1개씩 블록이 생성되고 있음
이런 방식으로 블록이 이어진다면 최후 블록의 길이가 너무 길어져서 아무도 블록체인을 쓰지 않을듯..
그래서 필요한 것이 바로 ‘암호해시’

2.블록체인의 핵심: 암호’해시’가 필요한 이유

[해시 함수의 특징]
마법의 ‘믹서기’
: 어떤 재료를 넣어도 항상 똑같은 크기의 스무디가 나오는데, 한 번 갈아버리면 원래 재료가 무엇인지 절대 알 수 없고, 재료가 조금만 달라져도 완전히 다른 맛의 스무디가 나오는 신기한 믹서기

2-1. 해시함수의 가지 핵심 특징

결정적 (Deterministic)

같은 입력 → 항상 같은 출력
특징:
동일한 데이터를 해시하면 언제나 동일한 해시값 생성
컴퓨터가 달라도, 시간이 달라도 결과는 항상 같음

일방향성 (One-way Function)

해시값으로 원본 데이터를 추측하는 것은 현실적으로 불가능
특징:
해시값에서 원본을 역산하는 것은 계산적으로 불가능
"계란을 스크램블로 만들면 다시 온전한 계란으로 되돌릴 수 없다"와 같은 개념

눈사태 효과 (Avalanche Effect)

입력이 1비트만 바뀌어도 출력이 완전히 달라짐
특징:
아주 작은 변화도 완전히 다른 해시값 생성
50% 이상의 비트가 변경되는 것이 이상적
모두 똑같은 양식의 결과값을 리턴
입력값이 텍스트이든, 숫자이든, 텍스트의 길이가 길든 짧든 간에 모두 64개의 문자가 조합된 동일 양식의 리턴값을 확인

2-2. 해시함수의 공굴리기 효과 실습

[조작이 불가능한 이유]
import hashlib # 변경전 genesis_block = {'INDEX':0, '판매자' : '파공블', '구매자' : '김민수', '개수' : '3개', '시간' : '2022년 5월 11일 16시 25분 16초', 'previous_block' : None } hash_data = hashlib.sha256(str(genesis_block).encode()).hexdigest() print(hash_data)
Python
복사
import hashlib # 변경후 genesis_block = {'INDEX':0, '판매자' : '파공블', '구매자' : '김민수', '개수' : '3개', '시간' : '2022년 5월 11일 16시 23분 16초', 'previous_block' : None } hash_data = hashlib.sha256(str(genesis_block).encode()).hexdigest() print(hash_data)
Python
복사
시간 일부만 변경해도 해시 암호의 값이 완전히 바뀌어 버림
따라서 블록체인 내의 과거 이력을 수정하려면, 이후 모든 블록의 암호 해시값이 바꿔야 하는 노가다 of 노가다, 심지어 동시에 바꿔주어야하므로, 과거의 거래 내겨을 수정하는 것은 불가능!!

2-3. 해시함수의 역사

SHA: Secure Hash Algorithm
1.
1993년 미국의 국가안보구NSA이 제작
2.
이 후 위험성을 줄이고, 안정성을 높여가며 SHA-2, SHA-3으로 발전해오고 있음

2-4. 해시함수의 구성(2단계)

단계
설명
1. 전처리(Preprocessing)
입력 데이터를 고정된 규격에 맞추기 위해 인코딩(UTF-8), 패딩(padding), 블록 분할 등의 과정을 수행함
2. 해싱(Hashing)
전처리된 데이터를 해시 함수에 입력하여 고정 길이의 해시값(hash value)을 생성함

1단계: 전처리 (Preprocessing)

패딩 (Padding)

목적: 입력 데이터를 알고리즘이 처리할 수 있는 고정 크기 블록으로 만들기

패딩 규칙 (SHA-256 기준)

1.
메시지 끝에 '1' 비트 추가
2.
0 비트들을 적절히 추가
3.
원본 메시지 길이를 64비트로 표현하여 맨 끝에 추가
4.
최종 크기: 512비트의 배수
원본: "Hello" (40비트) ↓ Step 1: "Hello" + 1 (41비트) ↓ Step 2: "Hello" + 1 + 0...0 (448비트까지 0 채움) ↓ Step 3: 앞의 448비트 + 원본길이(4064비트로) = 512비트
Python
복사
왜 원본 길이를 추가할까?
서로 다른 메시지가 같은 패딩 결과를 갖는 것을 방지
예: "a" + 패딩과 "aa" + 패딩이 같아지는 것을 방지
보안상 의미:
길이 확장 공격 방지
충돌 공격 저항성 강화

2단계: 해싱 (Hashing)

해싱은 다시 2단계로 구분 가능
1. 1단계: H, K, W 값 준비
2.
2단계: H+K+W 조합으로 64라운드 압축
내부조건에 따라 H,K,W라는 값을 구한뒤 이 값들을 조합으로 최종암호화 된 값을 산출하는 과정

SHA-256의 3가지 핵심 값

H값: 해시 상태 (Hash State)

8개 워킹 레지스터 - 계속 변화하는 "상태값"
# 초기값 (소수의 제곱근에서 유도) H₀ = 0x6a09e667 # √2 H₁ = 0xbb67ae85 # √3 H₂ = 0x3c6ef372 # √5 H₃ = 0xa54ff53a # √7 H₄ = 0x510e527f # √11 H₅ = 0x9b05688c # √13 H₆ = 0x1f83d9ab # √17 H₇ = 0x5be0cd19 # √19 # 라운드마다 a,b,c,d,e,f,g,h 레지스터에 복사되어 변화
Python
복사

K값: 라운드 상수 (Round Constants)

64개 고정값 - 각 라운드마다 다른 "양념"
# 처음 64개 소수의 세제곱근에서 유도 K[0] = 0x428a2f98 # ∛2 K[1] = 0x71374491 # ∛3 K[2] = 0xb5c0fbcf # ∛5 K[3] = 0xe9b5dba5 # ∛7 # ... K[63] = 0xc67178f2 # ∛311 (64번째 소수) # 목적: 각 라운드를 고유하게 만듦
Python
복사

W값: 메시지 스케줄 (Message Schedule)

64개 확장 워드 - 원본 메시지에서 생성된 "재료"
이 복잡한걸 하는 이유? 원본 메시지는 16개 워드밖에 없는데, 64라운드를 돌려야 하기 때문
결론적으로 16개 워드를 64개로 "늘려서" 각 라운드마다 각기 다른 W값을 사용하기 위함
# 1. 원본 16개 워드 (512비트 블록을 32비트씩 분할) W[0] ~ W[15] = 원본 메시지 워드 # 2. 48개 워드 확장 생성 for i in range(16, 64): W[i] = σ₁(W[i-2]) + W[i-7] + σ₀(W[i-15]) + W[i-16] # σ₀, σ₁: 비선형 함수 (비트 회전 + XOR 조합)
Python
복사

2단계

H+K+W 조합으로 64라운드 압축
for i in range(64): # 각 라운드마다 # 3가지 값을 조합하여 계산 T₁ = h + Σ₁(e) + Ch(e,f,g) + K[i] + W[i] # ↑ ↑ ↑ ↑ ↑ # H값 H값기반 H값기반 K값 W값 T₂ = Σ₀(a) + Maj(a,b,c) # ↑ ↑ # H값기반 H값기반 # H값(상태) 업데이트 h, g, f, e, d, c, b, a = g, f, e, d+T₁, c, b, a, T₁+T₂ # 결과: H, K, W가 모두 섞여서 새로운 H값 생성
Python
복사

3.코드로 이해하기

기본 해싱 확인

import hashlib def basic_hash_demo(): """기본적인 SHA-256 해싱 확인""" messages = ["Hello", "hello", "Hello!", ""] print("🔐 SHA-256 해싱 결과") print("=" * 60) for msg in messages: hash_result = hashlib.sha256(msg.encode()).hexdigest() print(f"'{msg:8}' → {hash_result}") print("\n✅ 특징:") print("- 출력 길이: 항상 64자 (256비트)") print("- 작은 변화도 완전히 다른 결과") print("- 빈 문자열도 고정된 해시값") basic_hash_demo()
Python
복사

H, K, W 값 확인하기

def hkw_values_demo(): """SHA-256의 H, K, W 값들을 실제로 확인""" message = "Hello" print("🔍 H, K, W 값 확인하기") print("=" * 50) # H값 (초기 해시 상태) print("1️⃣ H값 - 초기 해시 상태 (8개 워드)") H_initial = [ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 ] for i, h in enumerate(H_initial): print(f" H{i} = 0x{h:08x}") # K값 (라운드 상수) - 일부만 표시 print(f"\n2️⃣ K값 - 라운드 상수 (64개 중 처음 8개)") K_constants = [ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5 ] for i, k in enumerate(K_constants): print(f" K[{i}] = 0x{k:08x}") print(" ... (총 64개)") # W값 시뮬레이션 (실제로는 복잡한 계산) print(f"\n3️⃣ W값 - 메시지 스케줄") message_bytes = message.encode('utf-8').hex() print(f" 원본: '{message}' → {message_bytes}") print(f" W[0] = 0x48656c6c ('Hell')") print(f" W[1] = 0x6f800000 ('o' + 패딩)") print(f" W[2~15] = 패딩으로 채움") print(f" W[16~63] = 수학적 확장 (σ₀, σ₁ 함수 사용)") # 최종 해시 final_hash = hashlib.sha256(message.encode()).hexdigest() print(f"\n4️⃣ 최종 결과 - H+K+W 조합") print(f" 64라운드 후: {final_hash}") hkw_values_demo()
Python
복사

조합 과정 시뮬레이션

def combination_simulation(): """H, K, W가 어떻게 조합되는지 시뮬레이션""" print("⚙️ H+K+W 조합 과정 시뮬레이션") print("=" * 40) # 가상의 라운드 0 계산 print("📊 라운드 0 예시:") # H값 (현재 상태) h = 0x5be0cd19 e = 0x510e527f print(f" H값 사용: h=0x{h:08x}, e=0x{e:08x}") # K값 (라운드 상수) K0 = 0x428a2f98 print(f" K값 사용: K[0]=0x{K0:08x}") # W값 (메시지 스케줄) W0 = 0x48656c6c # "Hell" print(f" W값 사용: W[0]=0x{W0:08x}") # 조합 계산 (단순화) print(f"\n 조합 공식:") print(f" T₁ = h + Σ₁(e) + Ch(e,f,g) + K[0] + W[0]") print(f" = 0x{h:08x} + [복잡한 함수들] + 0x{K0:08x} + 0x{W0:08x}") print(f" = [새로운 32비트 값]") print(f"\n 결과: H, K, W가 모두 섞여서 새로운 해시 상태 생성") print(f" 이 과정을 64번 반복 → 최종 해시값") combination_simulation()
Python
복사

초기값 검증

def verify_initial_values(): """SHA-256 초기값이 정말 소수의 제곱근인지 확인""" import math print("🔬 초기값 검증: 소수의 제곱근") print("=" * 40) primes = [2, 3, 5, 7, 11, 13, 17, 19] sha256_initials = [ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 ] for i, prime in enumerate(primes): # 제곱근 계산 sqrt_val = math.sqrt(prime) fractional_part = sqrt_val - int(sqrt_val) # 32비트로 변환 hex_val = int(fractional_part * (2**32)) print(f"√{prime:2d} = {sqrt_val:.8f}") print(f" 소수부분 × 2³² = 0x{hex_val:08x}") print(f" SHA-256 H{i} = 0x{sha256_initials[i]:08x}") print(f" 일치여부: {'✅' if hex_val == sha256_initials[i] else '❌'}") print() verify_initial_values()
Python
복사

H, K, W 값 역할 확인

def hkw_role_experiment(): """H, K, W 각각의 역할을 실험으로 확인""" print("🧪 H, K, W 역할 실험") print("=" * 40) # 같은 메시지, 다른 초기 H값이라면? (가정) print("실험 1: H값의 역할") print("- H값이 바뀌면 → 완전히 다른 해시") print("- 초기 상태가 최종 결과를 좌우") messages = ["A", "B", "A"] # 같은 입력도 다시 해싱 for i, msg in enumerate(messages): hash_val = hashlib.sha256(msg.encode()).hexdigest() print(f" '{msg}' → {hash_val[:16]}...") print(f"\n실험 2: K값의 역할") print("- 라운드마다 다른 K값 → 각 라운드가 고유함") print("- 만약 모든 K값이 0이라면 → 보안 취약") print(f"\n실험 3: W값의 역할") print("- 입력 메시지가 W값으로 변환됨") print("- 메시지가 바뀌면 → W값 변화 → 해시 변화") # 메시지 변화 확인 test_msgs = ["Hello", "Hello!", "Hell", "Hellp"] print(" 메시지 변화에 따른 해시 변화:") for msg in test_msgs: hash_val = hashlib.sha256(msg.encode()).hexdigest() print(f" '{msg:6}' → {hash_val[:16]}...") hkw_role_experiment()
Python
복사