LSH (해시 함수)

LSH는 대한민국이 PC, 스마트 디바이스 등 범용 CPU에서 무결성을 제공하기 위해 2014년에 개발한 해시 함수이다. LSH는 대한민국 암호모듈 검증제도 검증대상 암호 알고리즘이며, 대한민국 국가 표준(KS X 3262)이다. 해시 함수 LSH의 전체 구조는 아래와 같다. LSH는 입력 메시지에 대해 다음과 같은 세 단계를 거쳐 해시값을 출력한다. 초기화(Initialization): 입력 메시지를 메시지 블록 비트 길이의 배수가 되도록 패딩을 한 후, 이를 메시지 블록 단위로 분할한다. 그리고 연결 변수를 IV {\displaystyle {\textsf {IV}}} 로 초기화한다. 압축(Compression): 32워드 배열 메시지 블록을 압축 함수의 입력으로 하여 얻은 출력값으로 연결 변수를 갱신하며, 이를 마지막 메시지 블록을 처리할 때까지 반복하여 메시지를 압축한다. 완료(Finalization): 압축 과정을 통해 연결 변수에 최종 저장된 값으로부터 n비트 길이의 해시 함수 출력값을 생성한다. 해시 함수 LSH의 규격은 다음 표와 같다. 해시 함수의 입력 메시지를 m {\displaystyle m} 이라 하자. 먼저 m {\displaystyle m} 은 덧붙이기(padding) 과정을 거친다. 덧붙이기 과정은 m {\displaystyle m} 의 끝에 비트 ‘1’을 덧붙인 후, 전체 길이가 32 w t {\displaystyle 32wt} 비트가 될 때까지 비트 ‘0’을 덧붙인다. 여기에서 t = ⌈ ( | m | + 1 ) / 32 w ⌉ {\displaystyle t=\lceil (|m|+1)/32w\rceil } 이다. 덧붙이기를 마친 메시지를 m p = m 0 ‖ m 1 ‖ … ‖ m ( 32 w t − 1 ) {\displaystyle m_{p}=m_{0}\|m_{1}\|\ldots \|m_{(32wt-1)}} 이라고 하자. 이때 m p {\displaystyle m_{p}} 는 4 w t {\displaystyle 4wt} 바이트 배열 m a = ( m [ 0 ] , … , m [ 4 w t − 1 ] ) {\displaystyle m_{a}=(m[0],\ldots ,m[4wt-1])} 로 볼 수 있다. 여기에서 m [ k ] = m 8 k ‖ m ( 8 k + 1 ) ‖ … ‖ m ( 8 k + 7 ) {\displaystyle m[k]=m_{8k}\|m_{(8k+1)}\|\ldots \|m_{(8k+7)}} ( 0 ≤ k ≤ ( 4 w t − 1 ) ) {\displaystyle (0\leq k\leq (4wt-1))} 이다. 바이트 배열 m a {\displaystyle m_{a}} 는 다음과 같이 32 t {\displaystyle 32t} 워드 배열 M = ( M [ 0 ] , … , M [ 32 t − 1 ] ) {\displaystyle {\textsf {M}}=(M[0],\ldots ,M[32t-1])} 으로 변환할 수 있다. M [ s ] ← m [ w s / 8 + ( w / 8 − 1 ) ] ‖ … ‖ m [ w s / 8 + 1 ] ‖ m [ w s / 8 ] {\displaystyle M[s]\leftarrow m[ws/8+(w/8-1)]\|\ldots \|m[ws/8+1]\|m[ws/8]} ( 0 ≤ s ≤ ( 32 t − 1 ) ) {\displaystyle (0\leq s\leq (32t-1))} 이어서 워드 배열 M {\displaystyle {\textsf {M}}} 으로부터 다음과 같은 규칙에 따라 t {\displaystyle t} 개 메시지 블록 M ( 0 ) {\displaystyle {\textsf {M}}^{(0)}} , M ( 1 ) {\displaystyle {\textsf {M}}^{(1)}} , \ldots , M ( t − 1 ) {\displaystyle {\textsf {M}}^{(t-1)}} 을 구성할 수 있다. M ( i ) ← ( M [ 32 i ] , M [ 32 i + 1 ] , … , M [ 32 i + 31 ] ) {\displaystyle {\textsf {M}}^{(i)}\leftarrow (M[32i],M[32i+1],\ldots ,M[32i+31])} ( 0 ≤ i ≤ ( t − 1 ) ) {\displaystyle (0\leq i\leq (t-1))} 연결 변수 CV ( 0 ) {\displaystyle {\textsf {CV}}^{(0)}} ( ∈ W 16 ) {\displaystyle (\in {\mathcal {W}}^{16})} 는 초기값을 이용하여 다음과 같이 배열 색인별로 값을 할당하는 방식으로 초기화한다. 이때, W t {\displaystyle {\mathcal {W}}^{t}} 는 t ≥ 1 {\displaystyle t\geq 1} 일 때 t {\displaystyle t} 개 워드 배열의 전체 집합을 나타낸다. CV ( 0 ) [ l ] ← IV [ l ] {\displaystyle {\textsf {CV}}^{(0)}[l]\leftarrow {\textsf {IV}}[l]} ( 0 ≤ l ≤ 15 ) {\displaystyle (0\leq l\leq 15)} 초기값은 다음과 같다. 모든 값은 16진수로 표현되어 있다. 초기화 단계에서 생성된 t {\displaystyle t} 개의 메시지 블록은 압축 단계에서 순차적으로 압축 함수(compression function) CF : W 16 × W 32 → W 16 {\displaystyle {\textrm {CF}}:{\mathcal {W}}^{16}\times {\mathcal {W}}^{32}\rightarrow {\mathcal {W}}^{16}} 의 입력값으로 사용된다. 압축 함수 CF {\displaystyle {\textrm {CF}}} 는 연결 변수 CV ( i ) ( ∈ W 16 ) {\displaystyle {\textsf {CV}}^{(i)}(\in {\mathcal {W}}^{16})} 와 메시지 블록 M ( i ) ( ∈ W 32 ) {\displaystyle {\textsf {M}}^{(i)}(\in {\mathcal {W}}^{32})} 를 입력으로 받아 연결 변수 CV ( i + 1 ) ( ∈ W 16 ) {\displaystyle {\textsf {CV}}^{(i+1)}(\in {\mathcal {W}}^{16})} 을 반환한다. 압축 함수는 다음 네 가지 함수로 구성된다. 메시지 확장 함수 MsgExp : W 32 → W 16 ( N s + 1 ) {\displaystyle {\textrm {MsgExp}}:{\mathcal {W}}^{32}\rightarrow {\mathcal {W}}^{16(Ns+1)}} 메시지 덧셈 함수 MsgAdd : W 16 × W 16 → W 16 {\displaystyle {\textrm {MsgAdd}}:{\mathcal {W}}^{16}\times {\mathcal {W}}^{16}\rightarrow {\mathcal {W}}^{16}} 섞음 함수 Mix j : W 16 → W 16 {\displaystyle {\textrm {Mix}}_{j}:{\mathcal {W}}^{16}\rightarrow {\mathcal {W}}^{16}} 워드 단위 치환 WordPerm : W 16 → W 16 {\displaystyle {\textrm {WordPerm}}:{\mathcal {W}}^{16}\rightarrow {\mathcal {W}}^{16}} 압축 함수의 전체 구조를 도시하면 다음 그림과 같다. 압축 함수 입력값 중 메시지 블록 M ( i ) {\displaystyle {\textsf {M}}^{(i)}} 는 메시지 확장 함수 MsgExp를 거쳐 ( N s + 1 ) {\displaystyle (N_{s}+1)} 개의 16워드 크기 데이터 M j ( i ) {\displaystyle {\textsf {M}}_{j}^{(i)}} ( 0 ≤ j ≤ N s ) {\displaystyle (0\leq j\leq N_{s})} 로 확장된다. 이어서 16워드 크기의 임시 변수 T = ( T [ 0 ] , … , T [ 15 ] ) {\displaystyle {\textsf {T}}=(T[0],\ldots ,T[15])} 에 CV ( i ) {\displaystyle {\textsf {CV}}^{(i)}} 를 할당한 후, 순차적으로 단계 함수(step function) Step j {\displaystyle {\textrm {Step}}_{j}} 를 통해 데이터 M j ( i ) {\displaystyle {\textsf {M}}_{j}^{(i)}} 를 처리하면서 T {\displaystyle {\textsf {T}}} 를 갱신한다. 단계 함수를 거쳐 T {\displaystyle {\textsf {T}}} 에 저장된 값은 MsgAdd 함수를 통해 처리된 후 ( i + 1 ) {\displaystyle (i+1)} 번 째 연결 변수 CV ( i + 1 ) {\displaystyle {\textsf {CV}}^{(i+1)}} 에 입력된다. 이러한 압축 함수 처리 절차는 다음과 같다. 압축 함수에서 순차적으로 M j ( i ) {\displaystyle {\textsf {M}}_{j}^{(i)}} 를 처리하는 단계 함수 Step j : W 16 × W 16 → W 16 {\displaystyle {\textrm {Step}}_{j}:{\mathcal {W}}^{16}\times {\mathcal {W}}^{16}\rightarrow {\mathcal {W}}^{16}} 는 다음과 같다. Step j := WordPerm ∘ Mix j ∘ MsgAdd {\displaystyle {\textrm {Step}}_{j}:={\textrm {WordPerm}}\circ {\textrm {Mix}}_{j}\circ {\textrm {MsgAdd}}} ( 0 ≤ j ≤ ( N s − 1 ) ) {\displaystyle (0\leq j\leq (N_{s}-1))} 단계 함수의 전체 구조는 다음 그림과 같다. 압축 함수에 입력된 메시지 블록 M ( i ) = ( M ( i ) [ 0 ] , … , M ( i ) [ 31 ] ) {\displaystyle {\textsf {M}}^{(i)}=(M^{(i)}[0],\ldots ,M^{(i)}[31])} 에 대해, 메시지 확장 함수 MsgExp는 ( N s + 1 ) {\displaystyle (N_{s}+1)} 개의 16워드 길이의 데이터 M j ( i ) {\displaystyle {\textsf {M}}_{j}^{(i)}} ( 0 ≤ j ≤ N s ) {\displaystyle (0\leq j\leq N_{s})} 를 생성한다. 생성 방법은 다음과 같다. M 0 ( i ) ← ( M ( i ) [ 0 ] , M ( i ) [ 1 ] , … , M ( i ) [ 15 ] ) {\displaystyle {\textsf {M}}_{0}^{(i)}\leftarrow (M^{(i)}[0],M^{(i)}[1],\ldots ,M^{(i)}[15])} M 1 ( i ) ← ( M ( i ) [ 16 ] , M ( i ) [ 17 ] , … , M ( i ) [ 31 ] ) {\displaystyle {\textsf {M}}_{1}^{(i)}\leftarrow (M^{(i)}[16],M^{(i)}[17],\ldots ,M^{(i)}[31])} M j ( i ) [ l ] ← M j − 1 ( i ) [ l ] ⊞ M j − 2 ( i ) [ τ ( l ) ] {\displaystyle {\textsf {M}}_{j}^{(i)}[l]\leftarrow {\textsf {M}}_{j-1}^{(i)}[l]\boxplus {\textsf {M}}_{j-2}^{(i)}[\tau (l)]} ( 0 ≤ l ≤ 15 ,   2 ≤ j ≤ N s ) {\displaystyle (0\leq l\leq 15,\ 2\leq j\leq N_{s})} 함수 τ {\displaystyle \tau } 는 다음과 같이 정의된 Z 16 {\displaystyle \mathbb {Z} _{16}} 상의 치환이다. 두 개의 16워드 길이의 변수 X = ( X [ 0 ] , … , X [ 15 ] ) {\displaystyle {\textsf {X}}=(X[0],\ldots ,X[15])} 와 Y = ( Y [ 0 ] , … , Y [ 15 ] ) {\displaystyle {\textsf {Y}}=(Y[0],\ldots ,Y[15])} 를 입력으로 받아 16워드 길이의 결과값을 출력하는 메시지 덧셈 함수 MsgAdd : W 16 × W 16 → W 16 {\displaystyle {\textrm {MsgAdd}}:{\mathcal {W}}^{16}\times {\mathcal {W}}^{16}\rightarrow {\mathcal {W}}^{16}} 는 다음과 같이 정의한다. MsgAdd ( X , Y ) := ( X [ 0 ] ⊕ Y [ 0 ] , … , X [ 15 ] ⊕ Y [ 15 ] ) {\displaystyle {\textrm {MsgAdd}}({\textsf {X}},{\textsf {Y}}):=(X[0]\oplus Y[0],\ldots ,X[15]\oplus Y[15])} 섞음 함수 Mix j : W 16 → W 16 {\displaystyle {\textrm {Mix}}_{j}:{\mathcal {W}}^{16}\rightarrow {\mathcal {W}}^{16}} 는 16워드 길이의 변수 T = ( T [ 0 ] , … , T [ 15 ] ) {\displaystyle {\textsf {T}}=(T[0],\ldots ,T[15])} 를 입력으로 받아 두 개의 워드 T [ l ] {\displaystyle T[l]} , T [ l + 8 ] {\displaystyle T[l+8]} ( 0 ≤ l ≤ 7 ) {\displaystyle (0\leq l\leq 7)} 을 쌍으로 구성한 후 아래와 같이 각각을 섞어 T {\displaystyle {\textsf {T}}} 를 갱신한다. ( T [ l ] , T [ l + 8 ] ) ← Mix j , l ( T [ l ] , T [ l + 8 ] ) {\displaystyle (T[l],T[l+8])\leftarrow {\textrm {Mix}}_{j,l}(T[l],T[l+8])} ( 0 ≤ l ≤ 7 ) {\displaystyle (0\leq l\leq 7)} 여기에서 Mix j , l : W 2 → W 2 {\displaystyle {\textrm {Mix}}_{j,l}:{\mathcal {W}}^{2}\rightarrow {\mathcal {W}}^{2}} 은 두 개 워드 X {\displaystyle X} , Y {\displaystyle Y} 를 처리하는 부분 섞음 함수로 다음과 같다. 부분 섞음 함수 Mix j , l {\displaystyle {\textrm {Mix}}_{j,l}} 를 도식화하면 다음 그림과 같다. 섞음 함수 Mix j , l {\displaystyle {\textrm {Mix}}_{j,l}} 에 사용되는 비트 순환량 α j {\displaystyle \alpha _{j}} , β j {\displaystyle \beta _{j}} , γ l {\displaystyle \gamma _{l}} 은 다음 표와 같다. 비트 순환량 α j {\displaystyle \alpha _{j}} , β j {\displaystyle \beta _{j}} 는 짝수 단계와 홀수 단계에서 다른 값이 적용된다. 그리고 8워드 길이의 단계 상수 SC j = ( S C j [ 0 ] , … , S C j [ 7 ] ) {\displaystyle {\textsf {SC}}_{j}=(SC_{j}[0],\ldots ,SC_{j}[7])} 는 먼저 SC 0 {\displaystyle {\textsf {SC}}_{0}} 를 아래 표와 같이 정의한 후, 나머지 ( N s − 1 ) {\displaystyle (N_{s}-1)} 개의 상수 SC j {\displaystyle {\textsf {SC}}_{j}} 를 S C j [ l ] ← S C j − 1 [ l ] ⊞ S C j − 1 [ l ] ⋘ 8 {\displaystyle SC_{j}[l]\leftarrow SC_{j-1}[l]\boxplus SC_{j-1}[l]^{\lll 8}} ( 0 ≤ l ≤ 7 , 1 ≤ j ≤ ( N s − 1 ) ) {\displaystyle (0\leq l\leq 7,1\leq j\leq (N_{s}-1))} 와 같이 유도한다. 워드 단위 치환 WordPerm : W 16 → W 16 {\displaystyle {\textrm {WordPerm}}:{\mathcal {W}}^{16}\rightarrow {\mathcal {W}}^{16}} 은 16워드 길이의 변수 X = ( X [ 0 ] , … , X [ 15 ] ) {\displaystyle {\textsf {X}}=(X[0],\ldots ,X[15])} 를 입력으로 받아 16워드 길이의 결과값을 출력한다. WordPerm ( X ) := ( X [ σ ( 0 ) ] , … , X [ σ ( 15 ) ] ) {\displaystyle {\textrm {WordPerm}}({\textsf {X}}):=(X[\sigma (0)],\ldots ,X[\sigma (15)])} 이때, 사용되는 함수 σ {\displaystyle \sigma } 는 다음 표와 같이 정의된 Z 16 {\displaystyle \mathbb {Z} _{16}} 상의 치환이다. 완료 함수 FIN n : W 16 → { 0 , 1 } n {\displaystyle {\textrm {FIN}}_{n}:{\mathcal {W}}^{16}\rightarrow \{0,1\}^{n}} 은 압축 과정의 결과로 생성된 마지막 연결 변수 CV ( t ) = ( C V ( t ) [ 0 ] , … , C V ( t ) [ 15 ] ) {\displaystyle {\textsf {CV}}^{(t)}=(CV^{(t)}[0],\ldots ,CV^{(t)}[15])} 에 적용되어 n {\displaystyle n} 비트 길이의 해시값 h {\displaystyle h} 를 생성한다. H = ( H [ 0 ] , … , H [ 7 ] ) {\displaystyle {\textsf {H}}=(H[0],\ldots ,H[7])} 를 8워드 길이의 변수, h b = ( h b [ 0 ] , … , h b [ w − 1 ] ) {\displaystyle {\textsf {h}}_{\textsf {b}}=(h_{b}[0],\ldots ,h_{b}[w-1])} 는 w {\displaystyle w} 바이트 길이의 변수라고 하면, 출력 함수 FIN n {\displaystyle {\textrm {FIN}}_{n}} 은 다음 절차를 수행한다. H [ l ] ← C V ( t ) [ l ] ⊕ C V ( t ) [ l + 8 ] {\displaystyle H[l]\leftarrow CV^{(t)}[l]\oplus CV^{(t)}[l+8]} ( 0 ≤ l ≤ 7 ) {\displaystyle (0\leq l\leq 7)} h b [ s ] ← H [ ⌊ 8 s / w ⌋ ] [ 7 : 0 ] ⋙ ( 8 s mod w ) {\displaystyle h_{b}[s]\leftarrow H[\lfloor 8s/w\rfloor ]_{[7:0]}^{\ggg (8s\mod w)}} ( 0 ≤ s ≤ ( w − 1 ) ) {\displaystyle (0\leq s\leq (w-1))} h ← ( h b [ 0 ] ‖ … ‖ h b [ w − 1 ] ) [ 0 : n − 1 ] {\displaystyle h\leftarrow (h_{b}[0]\|\ldots \|h_{b}[w-1])_{[0:n-1]}} 이때, 임의의 워드 X {\displaystyle X} 에 대해 r ≥ s {\displaystyle r\geq s} 일 때, X [ r : s ] {\displaystyle X_{[r:s]}} 는 X {\displaystyle X} 의 비트열 표현에서 부분 비트열 x r ‖ x r − 1 ‖ … ‖ x s {\displaystyle x_{r}\|x_{r-1}\|\ldots \|x_{s}} 를 나타낸다. 그리고 임의의 비트열 x {\displaystyle x} 에 대해 r ≤ s {\displaystyle r\leq s} 일 때, x [ r : s ] {\displaystyle x_{[r:s]}} 는 x {\displaystyle x} 의 부분 비트열 x r ‖ x r + 1 ‖ … ‖ x s {\displaystyle x_{r}\|x_{r+1}\|\ldots \|x_{s}} 를 나타낸다. LSH는 현재까지 알려진 모든 해시 함수 공격에 대해 안전하다. LSH는 ideal cipher model 하에서 쿼리의 수가 q < 2 n / 2 {\displaystyle q<2^{n/2}} 일 때 충돌 저항성(collision-resistance)을 가지며, 쿼리의 수가 q < 2 n {\displaystyle q<2^{n}} 일 때 역상 저항성(preimage-resistance) 및 제2-역상 저항성(second-preimage-resistance)을 가지는 것이 증명되었다. 또한, LSH-256의 경우 13스텝, LSH-512의 경우 14스텝이 알려진 모든 공격에 대해 안전함이 알려져 있어 50% 이상의 안전성 마진을 제공한다. LSH는 다양한 소프트웨어 플랫폼에서 SHA-2/3 대비 빠른 속도를 보유하고 있다. 아래 표는 다양한 플랫폼 상에서 LSH의 1MB 메시지에 대한 해시값 계산 속도를 보여준다. Intel Core i7-4770K @ 3.5GHz (Haswell), Ubuntu 12.04 64-bit, GCC 4.8.1 with “-m64 -mavx2 -O3” Intel Core i7-2600K @ 3.40GHz (Sandy Bridge), Ubuntu 12.04 64-bit, GCC 4.8.1 with “-m64 -msse4 -O3” Intel Core 2 Quad Q9550 @ 2.83GHz (Yorkfield), Windows 7 32-bit, Visual studio 2012 AMD FX-8350 @ 4GHz (Piledriver), Ubuntu 12.04 64-bit, GCC 4.8.1 with “-m64 -mxop -O3” Samsung Exynos 5250 ARM Cortex-A15 @ 1.7GHz dual core (Huins ACHRO 5250), Android 4.1.1 Qualcomm Snapdragon 800 Krait 400 @ 2.26GHz quad core (LG G2), Android 4.4.2 Qualcomm Snapdragon 800 Krait 400 @ 2.3GHz quad core (Samsung Galaxy S4), Android 4.2.2 Qualcomm Snapdragon 400 Krait 300 @ 1.7GHz dual core (Samsung Galaxy S4 mini), Android 4.2.2 아래 표는 Haswell 기반 플랫폼에서 LSH와 몇 종류의 해시 함수 성능을 비교한 것이다. LSH는 Intel Core i7-4770k @ 3.5 GHz quad core 플랫폼에서, 다른 해시 함수는 Intel Core i5-4570S @ 2.9 GHz quad core 플랫폼에서 성능을 측정한 결과이다. 아래 표는 Samsung Exynos 5250 ARM Cortex-A15 @ 1.7 GHz dual core 플랫폼에서 LSH와 몇 종류의 해시 함수 성능을 비교한 것이다. 해시 함수 LSH의 테스트 벡터는 다음과 같다. 모든 값은 16진수로 표현되어 있다. LSH-256-224("abc") = F7 C5 3B A4 03 4E 70 8E 74 FB A4 2E 55 99 7C A5 12 6B B7 62 36 88 F8 53 42 F7 37 32 LSH-256-256("abc") = 5F BF 36 5D AE A5 44 6A 70 53 C5 2B 57 40 4D 77 A0 7A 5F 48 A1 F7 C1 96 3A 08 98 BA 1B 71 47 41 LSH-512-224("abc") = D1 68 32 34 51 3E C5 69 83 94 57 1E AD 12 8A 8C D5 37 3E 97 66 1B A2 0D CF 89 E4 89 LSH-512-256("abc") = CD 89 23 10 53 26 02 33 2B 61 3F 1E C1 1A 69 62 FC A6 1E A0 9E CF FC D4 BC F7 58 58 D8 02 ED EC LSH-512-384("abc") = 5F 34 4E FA A0 E4 3C CD 2E 5E 19 4D 60 39 79 4B 4F B4 31 F1 0F B4 B6 5F D4 5E 9D A4 EC DE 0F 27 B6 6E 8D BD FA 47 25 2E 0D 0B 74 1B FD 91 F9 FE LSH-512-512("abc") = A3 D9 3C FE 60 DC 1A AC DD 3B D4 BE F0 A6 98 53 81 A3 96 C7 D4 9D 9F D1 77 79 56 97 C3 53 52 08 B5 C5 72 24 BE F2 10 84 D4 20 83 E9 5A 4B D8 EB 33 E8 69 81 2B 65 03 1C 42 88 19 A1 E7 CE 59 6D C, Java, Python으로 구현된 LSH의 배포용 소스코드는 KISA 암호이용활성화 웹페이지에서 다운받을 수 있다. LSH는 2019년 대한민국 암호모듈 검증제도 검증대상 암호알고리즘 목록에 포함되었다. LSH는 아래 표준으로 제정되어 있다. KS X 3262, 해시 함수 LSH Kim, Dong-Chan; Hong, Deukjo; Lee, Jung-Keun; Kim, Woo-Hwan; Kwon, Daesung (2014). 〈LSH: A New Fast Secure Hash Function Family〉. 《International Conference on Information Security and Cryptology - ICISC 2014》. Lecture Notes in Computer Science 8949. Springer/Heidelberg. 286–313쪽.  “LSH 소스코드”.  “암호모듈검증”.  “e-나라 표준인증”.