해시 함수
-
해시 함수는 데이터를 입력으로 받고 고유한 바이트 문자열을 생성한다. 출력을 다이제스트, 혹은 해시라고 한다.
-
해시의 특징 3가지가 있다.
- 역상 저항성(Preimage resistance): 역상(
Preimage == y
) 추측에 저항하는(Resistance) 성질- 최초, 해시값(y)이 확인된 상태
- 입력값(x)을 찾는 것은 계산적으로 불가능
- 제2 역상 저항성(Second preimage resistance): 역상 추측에 저항하는 성질
- 최초, 입력값(x)이 확인된 상태
- 동일한 해시값(y)이 나오는 다른 입력값(x’)을 찾는 것은 계산적으로 불가능
- 충돌 저항성(Collision resistance): 서로 다른 입력값 추측에 저항하는 성질.
- 서로 다른 입력값은 동일한 해시값을 생성(Collision)
- 역상 저항성(Preimage resistance): 역상(
-
아래와 같은 openssl 명령으로 파일의 해시를 얻을 수 있다.
Terminal window $ openssl dgst -sha256 tmpSHA2-256(tmp)= e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -
해시 다이제스트의 크기 (생일 문제)
- 한 방에 여러 사람이 있을 때, 최소 50%의 확률로 두 사람이 같은 생일을 가지려면, 단 23명만 무작위로 선출하면 된다.
- 이처럼, 2^n가지 가능성이 있는 공간에서 문자열을 무작위로 생성할 때, 약 2^n/2의 문자열만 생성해보면 50% 확률로 충돌이 발생할 수 있다. (동일한 다이제스트가 나올 수 있다)
- 그렇기에 해시에서도 이처럼 128비트의 보안으로 충돌을 피하려면 대략 256비트의 다이제스트를 생성해야한다.
-
SHA-2
- 여러 버전이 있다 (SHA-224, SHA-256, SHA-384, SHA-512, SHA-512/224, SHA-512/256)
- 오늘날 SHA-256가 주로 사용된다.
- SHA-2는 압축 함수를 반복적으로 호출하여 메시지를 해시하는 머클-담고르 구조이다.
- 압축함수: 몇 개의 데이터를 받고 더 적은 데이터를 반환하는 함수
- 동작 과정:
- 해시하려는 입력에 패딩을 적용한 다음 입력을 압축 함수에 맞는 블록으로 자른다.
- 압축 함수의 이전 출력을 압축 함수에 대한 두 번째 인수로 사용하여 압축 함수를 메시지 블록에 반복적으로 적용한다. 최종 출력이 다이제스트가 된다.
- 길이 확장 공격(length extension attack)에 취약하다.
- 공격자가 해시값 H(m)과 메시지 m의 길이를 알 때, m의 내용을 모르더라도 H(m || padding || m’)을 계산할 수 있다.
-
SHA-3
- 여러 버전이 있다. (SHA-3-224, SHA-3-256, SHA-3-384, SHA-3-512)
- SHA-3는 스펀지 구조(sponge construction)로 제작되었다.
- 입력을 받아 동일한 크기의 출력을 반환하는 Keccak-f라는 순열을 기반으로 한다.
- 동작 과정:
- 입력과 출력을 r(rate)부분과 c(capacity) 부분으로 임의로 나눈다.
- (버전마다 다른 파라미터를 사용한다. c는 비밀로 취급해야하며 c가 클수록 스펀지 구조가 더 안전하다고 한다.)
- 순열 입력의 r과 입력을 XOR한다.
- 마지막 상태의 r을 사용한다.
- 입력과 출력을 r(rate)부분과 c(capacity) 부분으로 임의로 나눈다.
- 길이 확장 공격에 면역이다.
-
SHA-2, SHA-3은 고정 길이만 출력할 수 있는데 반해 임의의 길이의 출력을 생성할 수 있는 함수를 SHA-3에서 XOF로 정의하고 조프로 발음한다.
-
SHAKE, cSHAKE 두개의 표준화된 XOF가 있다.
-
SHAKE
- SHAKE128과 SHAKE256 두 가지 버전이 있다.
- 기본적으로 SHA-3와 구조가 동일하지만 속도가 더 빠르며 짜내기 단계에서 원하는 만큼 순열 연산을 할 수 있다.
-
cSHAKE (customizable SHAKE)
-
SHAKE의 확장 버전으로 사용자 정의가 가능하다.
-
함수 이름과 custom 문자열을 입력으로 받아 도메인 분리를 제공한다.
-
TupleHash: cSHAKE를 기반으로 하고 cSHAKE와 동일한 표준(NIST SP 800-185)으로 지정된 함수이다.
- 값 목록을 해시할 때, 단순하게 나열한 string대로 해싱하면
hash("abc" || "d")
와hash("ab" || "cd")
처럼 서로 다른 값이 같아질 수 있다 - 따라서
TupleHash(["abc", "d"]) = cSHAKE([3, "abc", 1, "d"])
처럼 각 함목의 글자 수를 함께 해싱하는 방식이며, 상황에 따라 유요하게 쓸 수 있다.
- 값 목록을 해시할 때, 단순하게 나열한 string대로 해싱하면
-
MAC(message authentication code)
-
동일한 키를 공유하는 하나 이상의 당사자가 메시지의 무결성과 신뢰성을 확인할 수 있도록 하는 대칭 암호학 알고리즘이다. 해시함수 비밀키 있는 버전이다.
- 메시지 및 관련 인증 태그의 신뢰성을 확인하기 위해 메시지의 인증 태그와 비밀 키를 다시 계산한 다음 두 인증 태그를 비교한다. 두 인증 태그가 서로 다르면 메시지가 변조된 것이다.
- 수신된 인증 태그와 계산된 인증 태그를 항상 상수 시간에 비교해야 한다.
-
대부분의 실용적 MAC은 PRF(Pseudorandom Function) 구조를 가지고 있다
- PRF는 키를 입력으로 받아 무작위처럼 보이는 출력을 생성하는 함수이다. 실제로는 결정론적(deterministic)이지만 출력이 진짜 무작위 함수와 구분할 수 없어야 한다.
- PRF는 키 유도 함수(KDF), 스트림 암호, 블록 암호 등 다양한 암호학적 응용에 사용된다.
-
주의해야할 문제
-
리플레이 공격의 위험 있음
- 카운터를 추가해 대응 가능
- 카운터가 증가하지 않은, 똑같은 요청 그대로 보내면 유효하지 않은 것으로 판단
- 카운터는 고정 길이로 사용해야함. 가변 길이라면 카운터 1인 메세지를 바탕으로 11을 유추할 수 있기 때문
-
주기적 인증 태그 검증
- 수신한 인증 태그와 계산한 인증 태그의 비교는 상수 시간에 이뤄져야함.
- 답변이 늦으면 ‘앞쪽까지는 일치하는군’하며 유추할 수 있기 때문
-
-
HMAC (Hash-based Message Authentication Code)
- HMAC은 해시 함수를 기반으로 한 MAC 알고리즘으로, 가장 널리 사용되는 MAC 구조이다.
- RFC 2104에 정의되어 있으며 모든 암호학적 해시 함수와 함께 사용할 수 있다. 하지만 대부분 SHA-2와 함께 사용된다.
- 구조:
- 두 번의 해시 연산을 수행한다. 따라서 길이 확장 공격에 면역이다.
HMAC(K, m) = H((K ⊕ opad) || H((K ⊕ ipad) || m))
- K: 비밀 키
- m: 메시지
- H: 해시 함수 (SHA-256, SHA-512 등)
- ipad: 내부 패딩 (0x36을 반복)
- opad: 외부 패딩 (0x5c를 반복)
# OpenSSL로 HMAC 생성$ echo -n "message" | openssl dgst -sha256 -hmac "secret-key"HMAC-SHA256(stdin)= 8b5f48702995c1598c573db1e21866a9b825d4a794d169d7060a03605796360b -
KMAC (Keccak Message Authentication Code)
-
KMAC은 SHA-3의 스펀지 구조를 기반으로 한 MAC 알고리즘이다. NIST SP 800-185에 정의되어 있으며, cSHAKE를 기반으로 구현되었다.
-
가변 길이 출력을 지원한다. (XOF 기능)
-
커스텀 문자열(customization string)을 지원하여 도메인 분리가 가능하다.
-
HMAC보다 더 간단한 구조로 구현되어 있다.
# Python cryptography 라이브러리 예시from hashlib import shake_256def kmac256(key, data, length, custom=b''):# KMAC256의 간소화된 개념적 구현# 실제로는 cSHAKE256 기반으로 구현됨pass
-
HKDF (HMAC-based Extract-and-Expand Key Derivation Function)
-
HKDF는 HMAC을 기반으로 한 키 유도 함수(KDF)로, 하나의 키 자료로부터 여러 암호학적 키를 안전하게 유도한다.
-
RFC 5869에 정의되어 있으며, TLS 1.3 등에서 표준으로 사용된다.
-
약한 엔트로피 소스로부터 강력한 키를 유도할 수 있다.
-
Info 파라미터를 통한 도메인 분리로 키 재사용 공격을 방지한다.
-
파라미터
- IKM (Input Keying Material): 초기 키 자료 (예: Diffie-Hellman 공유 비밀)
- Salt: 선택적 랜덤 값으로 키 자료의 엔트로피를 강화한다.
- Info: 응용 프로그램별 컨텍스트 정보로 도메인 분리를 제공한다.
- Length: 출력 키 자료의 길이
-
동작 흐름
- Extract: 입력 키 자료(IKM)에서 고정 길이의 의사 난수 키(PRK)를 추출한다.
PRK = HMAC(salt, IKM)
- Expand: PRK로부터 원하는 길이의 출력 키 자료(OKM)를 확장한다.
OKM = HMAC(PRK, info || counter)
from cryptography.hazmat.primitives import hashesfrom cryptography.hazmat.primitives.kdf.hkdf import HKDF# 마스터 키로부터 여러 키 유도hkdf = HKDF(algorithm=hashes.SHA256(),length=32,salt=b'optional-salt',info=b'application-specific-context',)derived_key = hkdf.derive(b'master-key-material') - Extract: 입력 키 자료(IKM)에서 고정 길이의 의사 난수 키(PRK)를 추출한다.
참고