Rust
SHA-256
sha-2
SHA
SHA-512

Rust で SHA-2 を実装してみた


はじめに

ふと思い立って SHA-2 を実装したので、仕様のメモ書きと共に公開してみる。

成果物は GitHub-Gist の『 Rust で SHA-2 を実装してみた』 においてある。

なお、 Rust のバージョンは 1.12.1 stable である。


SHA-2 とは

SHA-2 は、デジタルデータが改竄されてないかチェックするのに適したハッシュ関数(暗号学的ハッシュ関数)の標準規格の一つ。 NSA (アメリカ国家安全保障局)が考案し、 2001 年に NIST (アメリカ国標準技術局)によって連邦情報処理標準の一つ( FIPS 180-4 )として標準化された。 SHA-224 、 SHA-256 、 SHA-384 、 SHA-512 、 SHA-512/224 、 SHA-512/256 の 6 つのバリエーションからなり、それぞれの末尾の数字がハッシュ長(単位は bit )を表している(つまり、ハッシュ長は 224 、 256 、 384 、 512 bit の 4 つ)。 SHA は " Secure Hash Algorithm " の略である。


アルゴリズム

SHA-2 は SHA-256 を切り詰めたもの(以降、 SHA-256 系)と SHA-512 を切り詰めたもの(以降、 SHA-512 系)の 2 つに大別できる。以下はそれぞれがどちらに部類出来るかを示したものである。


部類
バリエーション名


SHA-256 系
SHA-224

SHA-256

SHA-512 系
SHA-384

SHA-512
SHA-512/224
SHA-512/256

SHA-256 系と SHA-512 系のワード長とラウンド数1の違いは次の表に示す通り。

アルゴリズム
ワード長 (bit)
ラウンド数

SHA-256 系
32
64

SHA-512 系
64
80

そのほかにもシフト量や、追加定数なども違うが、本質的な構造はいずれも同じである。


数式における記号とプログラム実装の対応関係


$x + y$

x.wrapping_add(y)

$x \oplus y$

x ^ y

$x \wedge y$

x & y

$\lnot x$

!x

${\it ROTR}^{\,y}(x)$

x.rotate_right(y)

${\it SHR}^{\,y}(x)$

x >> y


SHA-2 の構造

メッセージ(ハッシュを作りたいデータ)を $M_{\rm in}$ 、メッセージ長を $l$ としたとき、 SHA-2 のすべてのバリエーションは、以下の構造を持つ。

このうち、すべてのバリエーションで異なるのは初期値 $H^{(0)}$ である。

また、 SHA-256 系 と SHA-512 系 で異なるのは、ワード長 $w_{\rm len}$ 、 ラウンド数 $n_{\rm rnd}$ 、追加定数 $K_t$ および $\Sigma_0(x)$, $\Sigma_1(x)$, $\sigma_0(x)$, $\sigma_1(x)$ の 4 つの関数である。なお、 $t$ は整数。



  1. $M_{\rm in}$ にパディングを追加する。これを $M$ とする。詳細は個別に記載( SHA-256 系の場合と、 SHA-512 系の場合)するが大まかには次の通り。

    M=\underbrace{\overbrace{\underbrace{01100001}_{\rm ``{\bf a}”}\quad\underbrace{01100010}_{\rm ``{\bf b}”}\quad\dots\quad\underbrace{01100011}_{\rm ``{\bf c}”}}^{l\,{\rm bits}}}_{{\rm Massage\,(}M_{\rm in}{\rm )}}\quad\underbrace{1\quad\overbrace{00\dots0}^{k\,{\rm zero\,bits}}\quad\overbrace{00\dots0\underbrace{110\dots00}_{l}}^{2w_{\rm len}\,{\rm bits}}}_{\rm Padding}
    


    1. $M_{\rm in}$ の末尾に "1" を追加。

    2. $l+1+k \equiv 14 w_{\rm len} \bmod 16 w_{\rm len}$ によって求められる $k$ ビットの "0" を追加。

    3. $2 w_{\rm len}$ ビットの 0 埋めした $l$ を追加。



  2. パディングを追加したメッセージをワード長の 16 倍で分割したベクトルにする。この分割した単位をブロックと呼ぶ。分割して得られた $N$ 個のブロックを $M^{(1)},\,M^{(2)},\,\dots,\,M^{(N)}$ とする。



  3. ブロック $M^{(i)}\,(1 \leq i \leq N)$ を 1 から $N$ の順に圧縮関数へ代入する。圧縮関数はブロック $M^{(i)}$ と内部状態 $H^{(i-1)}$ の 2 つを引数とし、 $H^{(i)}$ を戻り値とする関数である。また $H^{(0)}$ は初期値である。


    1. $M^{(i)}$ を 16 分割したベクトルにする。これを $M_{0}^{(i)}, \, M_{1}^{(i)},\dots,\,M_{15}^{(i)}$ と表す。なお $M_{x}^{(i)}$ は $w_{\rm len}$ の長さを持つビット列である。

    2. 同様に $H^{(i)}$ を 16 分割したベクトルにする。これを $H_{0}^{(i)}, \, H_{1}^{(i)},\dots,\,H_{15}^{(i)}$ と表す。同じく $H_{x}^{(i)}$ は $w_{\rm len}$ の長さを持つビット列である。


    3. メッセージスケジュール $W_t$ を準備する。ラウンド数を $n_{\rm rnd}$ とする。

      {W_t = \left\{
      
      \begin{array}{ll}
      M_t^{(i)} & (0 \leq t \leq 15)\\
      \sigma_1(W_{t-2})+W_{t-7}+\sigma_0(W_{t-15})+W_{t-16} & (16 \leq t \leq (n_{\rm rnd} - 1))
      \end{array}
      \right.
      }

      $\sigma_0(x)$ と $\sigma_1(x)$ は、 SHA-256 系と SHA-512 系で異なる。




    4. $H_{0}^{(i)}, \, H_{1}^{(i)},\dots,\,H_{15}^{(i)}$ を $a, \, b,\dots,\,h$ に代入する。

      \begin{align}
      
      a&=H_0^{(i-1)}\\
      b&=H_1^{(i-1)}\\
      c&=H_2^{(i-1)}\\
      d&=H_3^{(i-1)}\\
      e&=H_4^{(i-1)}\\
      f&=H_5^{(i-1)}\\
      g&=H_6^{(i-1)}\\
      h&=H_7^{(i-1)}
      \end{align}



    5. 以下を、 $t$ が 0 から $n_{\rm rnd} - 1$ になるまで繰り返し実行する。$K_t$ は追加定数。

      \begin{align}
      
      T_1&=h+\Sigma_1(e)+{\it Ch}(e,f,g)+K_t+W_t\\
      T_2&=\Sigma_0(a)+{\it Maj}(a,b,c)\\
      h&=g\\
      g&=f\\
      f&=e\\
      e&=d+T_1\\
      d&=c\\
      c&=b\\
      b&=a\\
      a&=T_1+T_2
      \end{align}

      $K_t$, $\Sigma_1(x)$, $\Sigma_0(x)$ は、 SHA-256 系と SHA-512 系で異なる。




    6. 圧縮関数の戻り値。

      \begin{align}
      
      H_0^{(i)}&=a+H_0^{(i-1)}\\
      H_1^{(i)}&=b+H_1^{(i-1)}\\
      H_2^{(i)}&=c+H_2^{(i-1)}\\
      H_3^{(i)}&=d+H_3^{(i-1)}\\
      H_4^{(i)}&=e+H_4^{(i-1)}\\
      H_5^{(i)}&=f+H_5^{(i-1)}\\
      H_6^{(i)}&=g+H_6^{(i-1)}\\
      H_7^{(i)}&=h+H_7^{(i-1)}\\
      \end{align}




  4. $H_x^{(i)}$ をハッシュ長の長さになるまで結合する( $H^{(i)}$ をハッシュ長の長さに切り詰める)。



SHA-256 と SHA-512 共通で使う関数

\begin{align}

{\it Ch}(x,y,z) &= (x\wedge y)\oplus(\lnot x\wedge z)\tag{1}\\
{\it Maj}(x,y,z) &= (x\wedge y)\oplus(x\wedge z)\oplus(y\wedge z)\tag{2}
\end{align}

下記は実装。 SHA-256 と SHA-512 ではラウンド数が異なるので、マクロを使用している。

macro_rules! Ch {

($x:expr, $y:expr, $z:expr) => { ($x & $y) ^ (!$x & $z) };
}
macro_rules! Maj {
($x:expr, $y:expr, $z:expr) => { ($x & $y) ^ ($x & $z) ^ ($y & $z) };
}


SHA-224 および SHA-256

SHA-256 系について説明する。


定数

追加定数 $K^{\{256\}}$

428a2f98 71374491 b5c0fbcf e9b5dba5 3956c25b 59f111f1 923f82a4 ab1c5ed5

d807aa98 12835b01 243185be 550c7dc3 72be5d74 80deb1fe 9bdc06a7 c19bf174
e49b69c1 efbe4786 0fc19dc6 240ca1cc 2de92c6f 4a7484aa 5cb0a9dc 76f988da
983e5152 a831c66d b00327c8 bf597fc7 c6e00bf3 d5a79147 06ca6351 14292967
27b70a85 2e1b2138 4d2c6dfc 53380d13 650a7354 766a0abb 81c2c92e 92722c85
a2bfe8a1 a81a664b c24b8b70 c76c51a3 d192e819 d6990624 f40e3585 106aa070
19a4c116 1e376c08 2748774c 34b0bcb5 391c0cb3 4ed8aa4a 5b9cca4f 682e6ff3
748f82ee 78a5636f 84c87814 8cc70208 90befffa a4506ceb bef9a3f7 c67178f2

const K256: [u32; 64] = [

0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
];

・SHA-224 の初期値 $H^{(0)}$ (値は 16 進表記)

\begin{align}

H^{(0)}_0&={\tt c1059ed8}\\
H^{(0)}_1&={\tt 367cd507}\\
H^{(0)}_2&={\tt 3070dd17}\\
H^{(0)}_3&={\tt f70e5939}\\
H^{(0)}_4&={\tt ffc00b31}\\
H^{(0)}_5&={\tt 68581511}\\
H^{(0)}_6&={\tt 64f98fa7}\\
H^{(0)}_7&={\tt befa4fa4}\\
\end{align}

・SHA-256 の初期値 $H^{(0)}$ (値は 16 進表記)

\begin{align}

H^{(0)}_0&={\tt 6a09e667}\\
H^{(0)}_1&={\tt bb67ae85}\\
H^{(0)}_2&={\tt 3c6ef372}\\
H^{(0)}_3&={\tt a54ff53a}\\
H^{(0)}_4&={\tt 510e527f}\\
H^{(0)}_5&={\tt 9b05688c}\\
H^{(0)}_6&={\tt 1f83d9ab}\\
H^{(0)}_7&={\tt 5be0cd19}\\
\end{align}


パディング

$M_{\rm in}$ が ASCII コードの "abc" である場合を考える。 1 文字が 8 bit の長さなので、メッセージの長さ $l$ は 24 である。

M_{\rm in} = \overbrace{\underbrace{01100001}_{\rm ``{\bf a}”}\quad\underbrace{01100010}_{\rm ``{\bf b}”}\quad\underbrace{01100011}_{\rm ``{\bf c}”}}^{8 + 8 + 8 = 24 = l\,{\rm bits}}

パディングとして追加する 0 の数 $k$ は、次の式によって求められる。

l+1+k \equiv 448 \bmod 512

より

\begin{align}

k&=(512-64)-(l+1)\\
&=448-(24+1)\\
&=405
\end{align}

以上から $M$ は次のような構成になる。

\underbrace{\overbrace{\underbrace{01100001}_{\rm ``{\bf a}”}\quad\underbrace{01100010}_{\rm ``{\bf b}”}\quad\underbrace{01100011}_{\rm ``{\bf c}”}}^{l=24\,{\rm bits}}}_{\rm Massage\,"abc"}\quad\underbrace{1\quad\overbrace{00\dots00}^{k=405\,{\rm bits}}\quad\overbrace{00\dots0\underbrace{11000}_{l=24}}^{64\,{\rm bits}}}_{\rm Padding}

以下は $M_{\rm in}$ にパディングを追加し、 $M$ を出力する関数である。第一引数に $M_{\rm in}$ を代入し、第二引数にメッセージ長 $l$ を代入する。

fn add_padding(message: &Vec<u8>, length: u64) -> Vec<u8> {

let data_len = ((length / 512) * 64 + if (length % 512) <= 447 { 64 } else { 128 }) as usize;
let mut data: Vec<u8> = Vec::with_capacity(data_len);

// copy message
for byte in message.iter() {
data.push(*byte);
}

// add padding
// add 1
if length % 8 == 0 {
data.push(0x80);
} else {
let len = data.len();
data[len - 1] |= 1 << (7 - length % 8) as u32;
}
// add zero
for _ in 0..(data_len - data.len() - 8) {
data.push(0x00);
}
// add l (64bits)
for i in 0..8 {
data.push(((length >> (56 - i * 8) as u32) & 0xff) as u8);
}

data
}


圧縮関数

$\Sigma_0(x),\,\Sigma_1(x),\,\sigma_0(x),\,\sigma_1(x)$ の SHA-256 系で使うものは以下の通り。それぞれ $\Sigma_0^{\{256\}}(x),\, \Sigma_1^{\{256\}}(x),\, \sigma_0^{\{256\}}(x),\, \sigma_1^{\{256\}}(x)$ として定義する。

\begin{align}

\Sigma_0^{\{256\}}(x)&={\it ROTR}^{\,2}(x) \oplus {\it ROTR}^{\,13}(x)\oplus {\it ROTR}^{\,22}(x)\\
\Sigma_1^{\{256\}}(x)&={\it ROTR}^{\,6}(x) \oplus {\it ROTR}^{\,11}(x)\oplus {\it ROTR}^{\,25}(x)\\
\sigma_0^{\{256\}}(x)&={\it ROTR}^{\,7}(x) \oplus {\it ROTR}^{\,18}(x)\oplus {\it SHR}^{\,3}(x)\\
\sigma_1^{\{256\}}(x)&={\it ROTR}^{\,17}(x) \oplus {\it ROTR}^{\,19}(x)\oplus {\it SHR}^{\,10}(x)
\end{align}

実装は下記。

fn large_sigma0_256(x: u32) -> u32 {

x.rotate_right( 2) ^ x.rotate_right(13) ^ x.rotate_right(22)
}
fn large_sigma1_256(x: u32) -> u32 {
x.rotate_right( 6) ^ x.rotate_right(11) ^ x.rotate_right(25)
}
fn sigma0_256(x: u32) -> u32 {
x.rotate_right( 7) ^ x.rotate_right(18) ^ (x >> 3)
}
fn sigma1_256(x: u32) -> u32 {
x.rotate_right(17) ^ x.rotate_right(19) ^ (x >> 10)
}

これを適用すると、メッセージスケジュールは次のようになる。

{W_t = \left\{

\begin{array}{ll}
M_t^{(i)} & (0 \leq t \leq 15)\\
\sigma^{\{256\}}_1(W_{t-2})+W_{t-7}+\sigma^{\{256\}}_0(W_{t-15})+W_{t-16} & (16 \leq t \leq 63)
\end{array}
\right.
}

実装は下記。

let w = {

let mut w = [0; 64];
for t in 0..16 {
w[t] = self.buffer[t];
}
for t in 16..64 {
w[t] = sigma1_256(w[t-2]).wrapping_add(w[t-7])
.wrapping_add(sigma0_256(w[t-15]))
.wrapping_add(w[t-16]);
}
w
};

圧縮関数の手順 5 は次のようになる。

\begin{align}

T_1&=h+\Sigma^{\{256\}}_1(e)+{\it Ch}(e,f,g)+K_t^{\{256\}}+W_t\\
T_2&=\Sigma^{\{256\}}_0(a)+{\it Maj}(a,b,c)\\
h&=g\\
g&=f\\
f&=e\\
e&=d+T_1\\
d&=c\\
c&=b\\
b&=a\\
a&=T_1+T_2
\end{align}

実装は以下。

for t in 0..64 {

let t1 = h.wrapping_add(large_sigma1_256(e))
.wrapping_add(Ch!(e, f, g))
.wrapping_add(K256[t])
.wrapping_add(w[t]);
let t2 = large_sigma0_256(a).wrapping_add(Maj!(a, b, c));
h = g;
g = f;
f = e;
e = d.wrapping_add(t1);
d = c;
c = b;
b = a;
a = t1.wrapping_add(t2);
}

圧縮関数の手順 6 は次のようになる。

\begin{align}

H_0^{(i)}&=a+H_0^{(i-1)}\\
H_1^{(i)}&=b+H_1^{(i-1)}\\
H_2^{(i)}&=c+H_2^{(i-1)}\\
H_3^{(i)}&=d+H_3^{(i-1)}\\
H_4^{(i)}&=e+H_4^{(i-1)}\\
H_5^{(i)}&=f+H_5^{(i-1)}\\
H_6^{(i)}&=g+H_6^{(i-1)}\\
H_7^{(i)}&=h+H_7^{(i-1)}\\
\end{align}

self.state = [

a.wrapping_add(self.state[0]),
b.wrapping_add(self.state[1]),
c.wrapping_add(self.state[2]),
d.wrapping_add(self.state[3]),
e.wrapping_add(self.state[4]),
f.wrapping_add(self.state[5]),
g.wrapping_add(self.state[6]),
h.wrapping_add(self.state[7])
];

以上を持って圧縮関数を実装すると次のようになる。

macro_rules! Ch {

($x:expr, $y:expr, $z:expr) => { ($x & $y) ^ (!$x & $z) };
}
macro_rules! Maj {
($x:expr, $y:expr, $z:expr) => { ($x & $y) ^ ($x & $z) ^ ($y & $z) };
}

const K256: [u32; 64] = [
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
];

fn large_sigma0_256(x: u32) -> u32 {
x.rotate_right( 2) ^ x.rotate_right(13) ^ x.rotate_right(22)
}
fn large_sigma1_256(x: u32) -> u32 {
x.rotate_right( 6) ^ x.rotate_right(11) ^ x.rotate_right(25)
}
fn sigma0_256(x: u32) -> u32 {
x.rotate_right( 7) ^ x.rotate_right(18) ^ (x >> 3)
}
fn sigma1_256(x: u32) -> u32 {
x.rotate_right(17) ^ x.rotate_right(19) ^ (x >> 10)
}

fn round(state: [u32; 8], block: [u32; 16]) -> [u32; 8] {
let w = {
let mut w = [0; 64];
for t in 0..16 {
w[t] = block[t];
}
for t in 16..64 {
w[t] = sigma1_256(w[t-2]).wrapping_add(w[t-7])
.wrapping_add(sigma0_256(w[t-15]))
.wrapping_add(w[t-16]);
}
w
};
let (mut a, mut b, mut c, mut d, mut e, mut f, mut g, mut h) = (
state[0],
state[1],
state[2],
state[3],
state[4],
state[5],
state[6],
state[7]
);
for t in 0..64 {
let t1 = h.wrapping_add(large_sigma1_256(e))
.wrapping_add(Ch!(e, f, g))
.wrapping_add(K256[t])
.wrapping_add(w[t]);
let t2 = large_sigma0_256(a).wrapping_add(Maj!(a, b, c));
h = g;
g = f;
f = e;
e = d.wrapping_add(t1);
d = c;
c = b;
b = a;
a = t1.wrapping_add(t2);
}
[
a.wrapping_add(state[0]),
b.wrapping_add(state[1]),
c.wrapping_add(state[2]),
d.wrapping_add(state[3]),
e.wrapping_add(state[4]),
f.wrapping_add(state[5]),
g.wrapping_add(state[6]),
h.wrapping_add(state[7])
]
}

enum Sha256BitLength {
Len224 = 224,
Len256 = 256,
}


ハッシュ作成

ハッシュは次のように結合される。 $\parallel$ は結合を表す。例えば、 $A=010,\,B=001$ としたとき、 $A\parallel B=010001$ となる。

SHA-256

H_0^{(N)}\parallel H_1^{(N)}\parallel H_2^{(N)}\parallel H_3^{(N)}\parallel H_4^{(N)}\parallel H_5^{(N)}\parallel H_6^{(N)}\parallel H_7^{(N)}

SHA-224

H_0^{(N)}\parallel H_1^{(N)}\parallel H_2^{(N)}\parallel H_3^{(N)}\parallel H_4^{(N)}\parallel H_5^{(N)}\parallel H_6^{(N)}

以上を見てわかる通り、 SHA-224 は SHA-256 の先頭 224 bit を切り取ったものである。ただし、初期値が異なるため、同一メッセージであっても値は異なる。

$H^{(N)}$ を state: [u32; 8] 、ハッシュ長を hash_length: usize として、 Vec<u8> 型でハッシュを表すとすると、コードは次のようになる。

fn (state: [u32; 8], hash_length: usize) -> Vec<u8> {

let mut hash: Vec<u8> = Vec::with_capacity(hash_length);
let mut length = 0;
'outer: for h in state.iter() {
use std::mem::transmute;

let from = if cfg!(target_endian = "big") {
*h
} else {
(*h).swap_bytes()
};
for byte in unsafe { transmute::<u32, [u8; 4]>(from) }.iter() {
hash.push(*byte);
length += 8;
if length == hash_length as usize {
break 'outer;
}
}
}

hash
}


ソースコード


sha256.rs

macro_rules! Ch {

($x:expr, $y:expr, $z:expr) => { ($x & $y) ^ (!$x & $z) };
}
macro_rules! Maj {
($x:expr, $y:expr, $z:expr) => { ($x & $y) ^ ($x & $z) ^ ($y & $z) };
}

/// 追加定数
const K256: [u32; 64] = [
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
];

fn large_sigma0_256(x: u32) -> u32 {
x.rotate_right( 2) ^ x.rotate_right(13) ^ x.rotate_right(22)
}
fn large_sigma1_256(x: u32) -> u32 {
x.rotate_right( 6) ^ x.rotate_right(11) ^ x.rotate_right(25)
}
fn sigma0_256(x: u32) -> u32 {
x.rotate_right( 7) ^ x.rotate_right(18) ^ (x >> 3)
}
fn sigma1_256(x: u32) -> u32 {
x.rotate_right(17) ^ x.rotate_right(19) ^ (x >> 10)
}

/// 圧縮関数
fn round(state: [u32; 8], block: [u32; 16]) -> [u32; 8] {
let w = {
let mut w = [0; 64];
for t in 0..16 {
w[t] = block[t];
}
for t in 16..64 {
w[t] = sigma1_256(w[t-2]).wrapping_add(w[t-7])
.wrapping_add(sigma0_256(w[t-15]))
.wrapping_add(w[t-16]);
}
w
};
let (mut a, mut b, mut c, mut d, mut e, mut f, mut g, mut h) = (
state[0],
state[1],
state[2],
state[3],
state[4],
state[5],
state[6],
state[7]
);
for t in 0..64 {
let t1 = h.wrapping_add(large_sigma1_256(e))
.wrapping_add(Ch!(e, f, g))
.wrapping_add(K256[t])
.wrapping_add(w[t]);
let t2 = large_sigma0_256(a).wrapping_add(Maj!(a, b, c));
h = g;
g = f;
f = e;
e = d.wrapping_add(t1);
d = c;
c = b;
b = a;
a = t1.wrapping_add(t2);
}
[
a.wrapping_add(state[0]),
b.wrapping_add(state[1]),
c.wrapping_add(state[2]),
d.wrapping_add(state[3]),
e.wrapping_add(state[4]),
f.wrapping_add(state[5]),
g.wrapping_add(state[6]),
h.wrapping_add(state[7])
]
}

/// パディングを加えたデータをブロックに分割する。
fn separate_data(data: Vec<u8>) -> Vec<[u32; 16]> {
let mut blocks = Vec::with_capacity(data.len() / 512);
let mut idx = 0;
let mut shift = 32;
let mut block = [0; 16];
for byte in data.iter() {
shift -= 8;
block[idx] |= (*byte as u32) << shift;
if shift == 0 {
idx += 1;
shift = 32;
}
if idx == 16 {
blocks.push(block);
block = [0; 16];
idx = 0;
}
}
blocks
}

/// メッセージにパディングを追加する。
fn add_padding(message: &Vec<u8>, length: u64) -> Vec<u8> {
let data_len = ((length / 512) * 64 + if (length % 512) <= 447 { 64 } else { 128 }) as usize;
let mut data: Vec<u8> = Vec::with_capacity(data_len);

// copy message
for byte in message.iter() {
data.push(*byte);
}

// add padding
if length % 8 == 0 {
data.push(0x80);
} else {
let len = data.len();
data[len - 1] |= 1 << (7 - length % 8) as u32;
}
for _ in 0..(data_len - data.len() - 8) {
data.push(0x00);
}
for i in 0..8 {
data.push(((length >> (56 - i * 8) as u32) & 0xff) as u8);
}

data
}

/// ハッシュ長
pub enum Sha256DigestSize {
Len224 = 224,
Len256 = 256,
}

/// ハッシュの取得。message にメッセージを代入し、 message_length にメッセージ長を代入する。
pub fn create_hash(message: &Vec<u8>, message_length: u64, digest_size: Sha256DigestSize) -> Box<[u8]> {
// パディングの追加
let data = add_padding(message, message_length);
// データをブロックに分割
let blocks = separate_data(data);
// 初期値
let mut state = match digest_size {
Sha256DigestSize::Len224 => [
0xc1059ed8,
0x367cd507,
0x3070dd17,
0xf70e5939,
0xffc00b31,
0x68581511,
0x64f98fa7,
0xbefa4fa4
],
Sha256DigestSize::Len256 => [
0x6a09e667,
0xbb67ae85,
0x3c6ef372,
0xa54ff53a,
0x510e527f,
0x9b05688c,
0x1f83d9ab,
0x5be0cd19
],
};
// ブロックを圧縮関数に代入
for block in blocks.iter() {
state = round(state, *block);
}
// ハッシュの作成
let hash_length = digest_size as usize;
let mut hash: Vec<u8> = Vec::with_capacity(hash_length);
let mut length = 0;
'outer: for h in state.iter() {
use std::mem::transmute;

let from = if cfg!(target_endian = "big") {
*h
} else {
(*h).swap_bytes()
};
for byte in unsafe { transmute::<u32, [u8; 4]>(from) }.iter() {
hash.push(*byte);
length += 8;
if length == hash_length as usize {
break 'outer;
}
}
}

// 戻り値
hash.into_boxed_slice()
}


このプログラムは、次の手順によってハッシュ ( Box<[u8]> 型 ) を取得する。



  1. Message::new の第1引数にメッセージ ( &[u8] 型 ) 、第2引数にメッセージ長 ( u64 型 ) を代入


  2. create_hash 関数の第1引数に手順 1 で作成した Message 構造体、第3引数にハッシュ長 ( Sha256DigestSize 型 ) を代入


  3. create_hash 関数の戻り値がハッシュ

次のコードは、空のメッセージから SHA-256 を取得する例である。

let msg = Message::new(&vec![], 0);

let hash = create_hash(msg, Sha256DigestSize::Len256);


テスト


メッセージが空 "" ( 0 ビットデータ ) の場合

アルゴリズム
ハッシュ

SHA-224
d14a028c 2a3a2bc9 476102bb 288234c4 15a2b01f 828ea62a c5b3e42f

SHA-256
e3b0c442 98fc1c14 9afbf4c8 996fb924 27ae41e4 649b934c a495991b 7852b855


sha256_empty_message_tests

/// 空のメッセージ "" に対し SHA-224 を取る

#[test]
fn sha2_224_hash_by_empty_msg() {
let msg = Message::new(&vec![], 0);
let hash = create_hash(msg, Sha256DigestSize::Len224);
assert_eq!(hash, vec![
0xd1, 0x4a, 0x02, 0x8c, 0x2a, 0x3a, 0x2b, 0xc9,
0x47, 0x61, 0x02, 0xbb, 0x28, 0x82, 0x34, 0xc4,
0x15, 0xa2, 0xb0, 0x1f, 0x82, 0x8e, 0xa6, 0x2a,
0xc5, 0xb3, 0xe4, 0x2f
].into_boxed_slice());
}
/// 空のメッセージ "" に対し SHA-256 を取る
#[test]
fn sha2_256_hash_by_empty_msg() {
let msg = Message::new(&vec![], 0);
let hash = create_hash(msg, Sha256DigestSize::Len256);
assert_eq!(hash, vec![
0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14,
0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24,
0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c,
0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55
].into_boxed_slice());
}


メッセージが "abc" である場合

アルゴリズム
ハッシュ

SHA-224
23097d22 3405d822 8642a477 bda255b3 2aadbce4 bda0b3f7 e36c9da7

SHA-256
ba7816bf 8f01cfea 414140de 5dae2223 b00361a3 96177a9c b410ff61 f20015ad


sha256_abc_message_tests

/// メッセージ "abc" に対し SHA-224 を取る

#[test]
fn sha2_224_hash_by_abc_msg() {
let msg = Message::new(b"abc".as_ref(), 24);
let hash = create_hash(msg, Sha256DigestSize::Len224);
assert_eq!(hash, vec![
0x23, 0x09, 0x7d, 0x22, 0x34, 0x05, 0xd8, 0x22,
0x86, 0x42, 0xa4, 0x77, 0xbd, 0xa2, 0x55, 0xb3,
0x2a, 0xad, 0xbc, 0xe4, 0xbd, 0xa0, 0xb3, 0xf7,
0xe3, 0x6c, 0x9d, 0xa7
].into_boxed_slice());
}
/// メッセージ "abc" に対し SHA-256 を取る
#[test]
fn sha2_256_hash_by_abc_msg() {
let msg = Message::new(b"abc".as_ref(), 24);
let hash = create_hash(msg, Sha256DigestSize::Len256);
assert_eq!(hash, vec![
0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea,
0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23,
0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c,
0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad
].into_boxed_slice());
}


メッセージが x ビットの連続した 0 である場合 ( SHA-256 のみ )

ビット数
ハッシュ

447
43fdd2ee d4df6d2c 38e971da 88411505 1951aa68 d892720f 79689d49 62c9efae

448
d4817aa5 497628e7 c77e6b60 6107042b bba31308 88c5f47a 375e6179 be789fbb


sha256_x_zero_bits_tests

/// 447 bits の 0 で構成されたメッセージに対し SHA-256 を取る

#[test]
fn sha2_256_hash_by_447_zero_bits() {
let msg = Message::new(&vec![
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
], 447);
let hash = create_hash(msg, Sha256DigestSize::Len256);
assert_eq!(hash, vec![
0x43, 0xfd, 0xd2, 0xee, 0xd4, 0xdf, 0x6d, 0x2c,
0x38, 0xe9, 0x71, 0xda, 0x88, 0x41, 0x15, 0x05,
0x19, 0x51, 0xaa, 0x68, 0xd8, 0x92, 0x72, 0x0f,
0x79, 0x68, 0x9d, 0x49, 0x62, 0xc9, 0xef, 0xae
].into_boxed_slice());
}
/// 448 bits の 0 で構成されたメッセージに対し SHA-256 を取る
#[test]
fn sha2_256_hash_by_448_zero_bits() {
let msg = Message::new(&vec![
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
], 448);
let hash = create_hash(msg, Sha256DigestSize::Len256);
assert_eq!(hash, vec![
0xd4, 0x81, 0x7a, 0xa5, 0x49, 0x76, 0x28, 0xe7,
0xc7, 0x7e, 0x6b, 0x60, 0x61, 0x07, 0x04, 0x2b,
0xbb, 0xa3, 0x13, 0x08, 0x88, 0xc5, 0xf4, 0x7a,
0x37, 0x5e, 0x61, 0x79, 0xbe, 0x78, 0x9f, 0xbb
].into_boxed_slice());
}


SHA-384 および SHA-512,SHA-512/224,SHA-512/256

SHA-512 系は SHA-256 系と全体の構造が同じなので、相違点のみを述べる。


定数

・追加定数 $K^{\{512\}}$

428a2f98d728ae22 7137449123ef65cd b5c0fbcfec4d3b2f e9b5dba58189dbbc

3956c25bf348b538 59f111f1b605d019 923f82a4af194f9b ab1c5ed5da6d8118
d807aa98a3030242 12835b0145706fbe 243185be4ee4b28c 550c7dc3d5ffb4e2
72be5d74f27b896f 80deb1fe3b1696b1 9bdc06a725c71235 c19bf174cf692694
e49b69c19ef14ad2 efbe4786384f25e3 0fc19dc68b8cd5b5 240ca1cc77ac9c65
2de92c6f592b0275 4a7484aa6ea6e483 5cb0a9dcbd41fbd4 76f988da831153b5
983e5152ee66dfab a831c66d2db43210 b00327c898fb213f bf597fc7beef0ee4
c6e00bf33da88fc2 d5a79147930aa725 06ca6351e003826f 142929670a0e6e70
27b70a8546d22ffc 2e1b21385c26c926 4d2c6dfc5ac42aed 53380d139d95b3df
650a73548baf63de 766a0abb3c77b2a8 81c2c92e47edaee6 92722c851482353b
a2bfe8a14cf10364 a81a664bbc423001 c24b8b70d0f89791 c76c51a30654be30
d192e819d6ef5218 d69906245565a910 f40e35855771202a 106aa07032bbd1b8
19a4c116b8d2d0c8 1e376c085141ab53 2748774cdf8eeb99 34b0bcb5e19b48a8
391c0cb3c5c95a63 4ed8aa4ae3418acb 5b9cca4f7763e373 682e6ff3d6b2b8a3
748f82ee5defb2fc 78a5636f43172f60 84c87814a1f0ab72 8cc702081a6439ec
90befffa23631e28 a4506cebde82bde9 bef9a3f7b2c67915 c67178f2e372532b
ca273eceea26619c d186b8c721c0c207 eada7dd6cde0eb1e f57d4f7fee6ed178
06f067aa72176fba 0a637dc5a2c898a6 113f9804bef90dae 1b710b35131c471b
28db77f523047d84 32caab7b40c72493 3c9ebe0a15c9bebc 431d67c49c100d4c
4cc5d4becb3e42b6 597f299cfc657e2a 5fcb6fab3ad6faec 6c44198c4a475817

\begin{align}

\Sigma^{\{512\}}_0(x)&={\it ROTR}^{\,28}(x)\oplus {\it ROTR}^{\,34}(x)\oplus {\it ROTR}^{\,39}(x)\\
\Sigma^{\{512\}}_1(x)&={\it ROTR}^{\,14}(x)\oplus {\it ROTR}^{\,18}(x)\oplus {\it ROTR}^{\,41}(x)\\
\sigma^{\{512\}}_0(x)&={\it ROTR}^{\,1}(x)\oplus {\it ROTR}^{\,8}(x)\oplus {\it SHR}^{\,7}(x)\\
\sigma^{\{512\}}_1(x)&={\it ROTR}^{\,19}(x)\oplus {\it ROTR}^{\,61}(x)\oplus {\it SHR}^{\,6}(x)
\end{align}

・SHA-384 の初期値 $H^{(0)}$

\begin{align}

H^{(0)}_0&={\tt cbbb9d5dc1059ed8}\\
H^{(0)}_1&={\tt 629a292a367cd507}\\
H^{(0)}_2&={\tt 9159015a3070dd17}\\
H^{(0)}_3&={\tt 152fecd8f70e5939}\\
H^{(0)}_4&={\tt 67332667ffc00b31}\\
H^{(0)}_5&={\tt 8eb44a8768581511}\\
H^{(0)}_6&={\tt db0c2e0d64f98fa7}\\
H^{(0)}_7&={\tt 47b5481dbefa4fa4}\\
\end{align}

・SHA-512 の初期値 $H^{(0)}$

\begin{align}

H^{(0)}_0&={\tt 6a09e667f3bcc908}\\
H^{(0)}_1&={\tt bb67ae8584caa73b}\\
H^{(0)}_2&={\tt 3c6ef372fe94f82b}\\
H^{(0)}_3&={\tt a54ff53a5f1d36f1}\\
H^{(0)}_4&={\tt 510e527fade682d1}\\
H^{(0)}_5&={\tt 9b05688c2b3e6c1f}\\
H^{(0)}_6&={\tt 1f83d9abfb41bd6b}\\
H^{(0)}_7&={\tt 5be0cd19137e2179}\\
\end{align}

・SHA-512/224 の初期値 $H^{(0)}$

\begin{align}

H^{(0)}_0&={\tt 8C3D37C819544DA2}\\
H^{(0)}_1&={\tt 73E1996689DCD4D6}\\
H^{(0)}_2&={\tt 1DFAB7AE32FF9C82}\\
H^{(0)}_3&={\tt 679DD514582F9FCF}\\
H^{(0)}_4&={\tt 0F6D2B697BD44DA8}\\
H^{(0)}_5&={\tt 77E36F7304C48942}\\
H^{(0)}_6&={\tt 3F9D85A86A1D36C8}\\
H^{(0)}_7&={\tt 1112E6AD91D692A1}\\
\end{align}

・SHA-512/256 の初期値 $H^{(0)}$

\begin{align}

H^{(0)}_0&={\tt 22312194FC2BF72C}\\
H^{(0)}_1&={\tt 9F555FA3C84C64C2}\\
H^{(0)}_2&={\tt 2393B86B6F53B151}\\
H^{(0)}_3&={\tt 963877195940EABD}\\
H^{(0)}_4&={\tt 96283EE2A88EFFE3}\\
H^{(0)}_5&={\tt BE5E1E2553863992}\\
H^{(0)}_6&={\tt 2B0199FC2C85B8AA}\\
H^{(0)}_7&={\tt 0EB72DDC81C52CA2}\\
\end{align}


パディング

SHA-256 系と同様に、 $M_{\rm in}={\rm ``abc"}=01100001\,01100010\,01100011$ としたとき、パディングとして追加する 0 の数 $k$ は、次の式によって求められる。

l+1+k \equiv 896 \bmod 1024

より

\begin{align}

k&=(1024-128)-(l+1)\\
&=896-(24+1)\\
&=871
\end{align}

したがって、 $M$ の構成は以下のようになる。

\underbrace{\underbrace{01100001}_{\rm ``{\bf a}”}\quad\underbrace{01100010}_{\rm ``{\bf b}”}\quad\underbrace{01100011}_{\rm ``{\bf c}”}}_{\rm Massage}\quad\underbrace{1\quad\overbrace{00\dots00}^{k=871}\quad\overbrace{00\dots0\underbrace{11000}_{l=24}}^{128}}_{\rm Padding}


ハッシュ作成

SHA-512

H_0^{(N)}\parallel H_1^{(N)}\parallel H_2^{(N)}\parallel H_3^{(N)}\parallel H_4^{(N)}\parallel H_5^{(N)}\parallel H_6^{(N)}\parallel H_7^{(N)}

SHA-384

H_0^{(N)}\parallel H_1^{(N)}\parallel H_2^{(N)}\parallel H_3^{(N)}\parallel H_4^{(N)}\parallel H_5^{(N)}

以上を見てわかる通り、 SHA-256 系と同じく、 SHA-384 は SHA-512 の先頭 384 bit を切り取ったものであるが、初期値が異なるため、同一メッセージであっても値は異なる。 SHA-512/224 および SHA-512/256 も同様である。


ソースコード


sha512.rs

macro_rules! Ch {

($x:expr, $y:expr, $z:expr) => { ($x & $y) ^ (!$x & $z) };
}
macro_rules! Maj {
($x:expr, $y:expr, $z:expr) => { ($x & $y) ^ ($x & $z) ^ ($y & $z) };
}

/// 追加定数
const K512: [u64; 80] = [
0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc,
0x3956c25bf348b538, 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118,
0xd807aa98a3030242, 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2,
0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235, 0xc19bf174cf692694,
0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65,
0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5,
0x983e5152ee66dfab, 0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4,
0xc6e00bf33da88fc2, 0xd5a79147930aa725, 0x06ca6351e003826f, 0x142929670a0e6e70,
0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df,
0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b,
0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30,
0xd192e819d6ef5218, 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8,
0x19a4c116b8d2d0c8, 0x1e376c085141ab53, 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8,
0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3,
0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec,
0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b,
0xca273eceea26619c, 0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178,
0x06f067aa72176fba, 0x0a637dc5a2c898a6, 0x113f9804bef90dae, 0x1b710b35131c471b,
0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc, 0x431d67c49c100d4c,
0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817
];

fn large_sigma0_512(x: u64) -> u64 {
x.rotate_right(28) ^ x.rotate_right(34) ^ x.rotate_right(39)
}
fn large_sigma1_512(x: u64) -> u64 {
x.rotate_right(14) ^ x.rotate_right(18) ^ x.rotate_right(41)
}
fn sigma0_512(x: u64) -> u64 {
x.rotate_right( 1) ^ x.rotate_right( 8) ^ (x >> 7)
}
fn sigma1_512(x: u64) -> u64 {
x.rotate_right(19) ^ x.rotate_right(61) ^ (x >> 6)
}

/// 圧縮関数
fn round(state: [u64; 8], block: [u64; 16]) -> [u64; 8] {
let w = {
let mut w = [0; 80];
for t in 0..16 {
w[t] = block[t];
}
for t in 16..80 {
w[t] = sigma1_512(w[t-2]).wrapping_add(w[t-7])
.wrapping_add(sigma0_512(w[t-15]))
.wrapping_add(w[t-16]);
}
w
};
let (mut a, mut b, mut c, mut d, mut e, mut f, mut g, mut h) = (
state[0],
state[1],
state[2],
state[3],
state[4],
state[5],
state[6],
state[7]
);
for t in 0..80 {
let t1 = h.wrapping_add(large_sigma1_512(e))
.wrapping_add(Ch!(e, f, g))
.wrapping_add(K512[t])
.wrapping_add(w[t]);
let t2 = large_sigma0_512(a).wrapping_add(Maj!(a, b, c));
h = g;
g = f;
f = e;
e = d.wrapping_add(t1);
d = c;
c = b;
b = a;
a = t1.wrapping_add(t2);
}
[
a.wrapping_add(state[0]),
b.wrapping_add(state[1]),
c.wrapping_add(state[2]),
d.wrapping_add(state[3]),
e.wrapping_add(state[4]),
f.wrapping_add(state[5]),
g.wrapping_add(state[6]),
h.wrapping_add(state[7])
]
}

/// パディングを加えたデータをブロックに分割する。
fn separate_data(data: Vec<u8>) -> Vec<[u64; 16]> {
let mut blocks = Vec::with_capacity(data.len() / 128);
let mut idx = 0;
let mut shift = 64;
let mut block = [0; 16];
for byte in data.iter() {
shift -= 8;
block[idx] |= (*byte as u64) << shift;
if shift == 0 {
idx += 1;
shift = 64;
}
if idx == 16 {
blocks.push(block);
block = [0; 16];
idx = 0;
}
}
blocks
}

pub struct Length {
uppper: u64,
lower: u64
}
impl Length {
pub fn new(val: u64) -> Length {
Length {
uppper: 0,
lower: val
}
}
fn get_data_length(&self) -> usize {
((self.lower / 1024) * 128 + if (self.lower % 1024) <= 895 { 128 } else { 256 }) as usize
}
fn seek_remainder_octet(&self) -> usize {
// self % 8
(self.lower & 0b111) as usize
}
fn get_octet_vec(&self) -> Vec<u8> {
let mut vec: Vec<u8> = Vec::with_capacity(16);
for j in 0..8 {
vec.push((self.uppper >> (56 - j * 8) as u32) as u8);
}
for j in 0..8 {
vec.push((self.lower >> (56 - j * 8) as u32) as u8);
}
vec
}
}

/// メッセージにパディングを追加する。
fn add_padding(message: &Vec<u8>, length: Length) -> Vec<u8> {
let data_len = length.get_data_length();
let mut data: Vec<u8> = Vec::with_capacity(data_len);

// copy message
for byte in message.iter() {
data.push(*byte);
}

// add padding
if length.seek_remainder_octet() == 0 {
data.push(0x80);
} else {
let len = data.len();
data[len - 1] |= 1 << (7 - length.seek_remainder_octet()) as u32;
}
for _ in 0..(data_len - data.len() - 16) {
data.push(0x00);
}
for byte in length.get_octet_vec().iter() {
data.push(*byte);
}

data
}

/// ハッシュ長
pub enum Sha521DigestSize {
Len224 = 224,
Len256 = 256,
Len384 = 384,
Len512 = 512,
}

/// ハッシュの取得。message にメッセージを代入し、 message_length にメッセージ長を代入する。
pub fn create_hash(message: &Vec<u8>, message_length: Length, digest_size: Sha521DigestSize) -> Box<[u8]> {
// パディングの追加
let data = add_padding(message, message_length);
// データをブロックに分割
let blocks = separate_data(data);
// 初期値
let mut state = match digest_size {
Sha521DigestSize::Len224 => [
0x8c3d37c819544da2,
0x73e1996689dcd4d6,
0x1dfab7ae32ff9c82,
0x679dd514582f9fcf,
0x0f6d2b697bd44da8,
0x77e36f7304c48942,
0x3f9d85a86a1d36c8,
0x1112e6ad91d692a1
],
Sha521DigestSize::Len256 => [
0x22312194fc2bf72c,
0x9f555fa3c84c64c2,
0x2393b86b6f53b151,
0x963877195940eabd,
0x96283ee2a88effe3,
0xbe5e1e2553863992,
0x2b0199fc2c85b8aa,
0x0eb72ddc81c52ca2
],
Sha521DigestSize::Len384 => [
0xcbbb9d5dc1059ed8,
0x629a292a367cd507,
0x9159015a3070dd17,
0x152fecd8f70e5939,
0x67332667ffc00b31,
0x8eb44a8768581511,
0xdb0c2e0d64f98fa7,
0x47b5481dbefa4fa4
],
Sha521DigestSize::Len512 => [
0x6a09e667f3bcc908,
0xbb67ae8584caa73b,
0x3c6ef372fe94f82b,
0xa54ff53a5f1d36f1,
0x510e527fade682d1,
0x9b05688c2b3e6c1f,
0x1f83d9abfb41bd6b,
0x5be0cd19137e2179
],
};
// ブロックを圧縮関数に代入
for block in blocks.iter() {
state = round(state, *block);
}
// ハッシュの作成
let hash_length = digest_size as usize;
let mut hash: Vec<u8> = Vec::with_capacity(hash_length);
let mut length = 0;
'outer: for h in state.iter() {
use std::mem::transmute;

let from = if cfg!(target_endian = "big") {
*h
} else {
(*h).swap_bytes()
};
for byte in unsafe { transmute::<u64, [u8; 8]>(from) }.iter() {
hash.push(*byte);
length += 8;
if length == hash_length as usize {
break 'outer;
}
}
}

// 戻り値
hash.into_boxed_slice()
}


このプログラムは、 create_hash 関数の第1引数にメッセージ ( Vec<u8> 型 ) 、第2引数にメッセージ長 ( Length 型 ) 、第3引数にハッシュ長 ( Sha521DigestSize 型 ) を代入してでハッシュ ( Box<[u8]> 型 ) を取得する。

次のコードは、空のメッセージから SHA-512 を取得する例である。

let message: Vec<u8> = vec![];

let hash = create_hash(&message, Length::new(0), Sha521DigestSize::Len512);


テスト


メッセージが空 "" ( 0 ビットデータ ) である場合

アルゴリズム
ハッシュ

SHA-384
38b060a751ac9638 4cd9327eb1b1e36a 21fdb71114be0743 4c0cc7bf63f6e1da 274edebfe76f65fb d51ad2f14898b95b

SHA-512
cf83e1357eefb8bd f1542850d66d8007 d620e4050b5715dc 83f4a921d36ce9ce 47d0d13c5d85f2b0 ff8318d2877eec2f 63b931bd47417a81 a538327af927da3e

SHA-512/224
6b4e03423667dbb7 3b6e15454f0eb1ab d4597f9a1b078e3f 5b5a6bc7

SHA-512/256
a7ffc6f8bf1ed766 51c14756a061d662 f580ff4de43b49fa 82d80a4b80f8434a


sha512_empty_message_tests

/// 空のメッセージ "" に対し SHA384 を取る

#[test]
fn sha2_384_hash_by_empty_msg() {
let hash = create_hash(&vec![], Length::new(0), Sha521DigestSize::Len384);
assert_eq!(hash, vec![
0x38, 0xb0, 0x60, 0xa7, 0x51, 0xac, 0x96, 0x38,
0x4c, 0xd9, 0x32, 0x7e, 0xb1, 0xb1, 0xe3, 0x6a,
0x21, 0xfd, 0xb7, 0x11, 0x14, 0xbe, 0x07, 0x43,
0x4c, 0x0c, 0xc7, 0xbf, 0x63, 0xf6, 0xe1, 0xda,
0x27, 0x4e, 0xde, 0xbf, 0xe7, 0x6f, 0x65, 0xfb,
0xd5, 0x1a, 0xd2, 0xf1, 0x48, 0x98, 0xb9, 0x5b
].into_boxed_slice());
}
/// 空のメッセージ "" に対し SHA512 を取る
#[test]
fn sha2_512_hash_by_empty_msg() {
let hash = create_hash(&vec![], Length::new(0), Sha521DigestSize::Len512);
assert_eq!(hash, vec![
0xcf, 0x83, 0xe1, 0x35, 0x7e, 0xef, 0xb8, 0xbd,
0xf1, 0x54, 0x28, 0x50, 0xd6, 0x6d, 0x80, 0x07,
0xd6, 0x20, 0xe4, 0x05, 0x0b, 0x57, 0x15, 0xdc,
0x83, 0xf4, 0xa9, 0x21, 0xd3, 0x6c, 0xe9, 0xce,
0x47, 0xd0, 0xd1, 0x3c, 0x5d, 0x85, 0xf2, 0xb0,
0xff, 0x83, 0x18, 0xd2, 0x87, 0x7e, 0xec, 0x2f,
0x63, 0xb9, 0x31, 0xbd, 0x47, 0x41, 0x7a, 0x81,
0xa5, 0x38, 0x32, 0x7a, 0xf9, 0x27, 0xda, 0x3e
].into_boxed_slice());
}
/// 空のメッセージ "" に対し SHA521/224 を取る
#[test]
fn sha2_224_by_512_hash_by_empty_msg() {
let hash = create_hash(&vec![], Length::new(0), Sha521DigestSize::Len224);
assert_eq!(hash, vec![
0x6e, 0xd0, 0xdd, 0x02, 0x80, 0x6f, 0xa8, 0x9e,
0x25, 0xde, 0x06, 0x0c, 0x19, 0xd3, 0xac, 0x86,
0xca, 0xbb, 0x87, 0xd6, 0xa0, 0xdd, 0xd0, 0x5c,
0x33, 0x3b, 0x84, 0xf4
].into_boxed_slice());
}
/// 空のメッセージ "" に対し SHA521/256 を取る
#[test]
fn sha2_256_by_512_hash_by_empty_msg() {
let hash = create_hash(&vec![], Length::new(0), Sha521DigestSize::Len256);
assert_eq!(hash, vec![
0xc6, 0x72, 0xb8, 0xd1, 0xef, 0x56, 0xed, 0x28,
0xab, 0x87, 0xc3, 0x62, 0x2c, 0x51, 0x14, 0x06,
0x9b, 0xdd, 0x3a, 0xd7, 0xb8, 0xf9, 0x73, 0x74,
0x98, 0xd0, 0xc0, 0x1e, 0xce, 0xf0, 0x96, 0x7a
].into_boxed_slice());
}


メッセージが "abc" である場合

アルゴリズム
ハッシュ

SHA-384
cb00753f45a35e8b b5a03d699ac65007 272c32ab0eded163 1a8b605a43ff5bed 8086072ba1e7cc23 58baeca134c825a7

SHA-512
ddaf35a193617aba cc417349ae204131 12e6fa4e89a97ea2 0a9eeee64b55d39a 2192992a274fc1a8 36ba3c23a3feebbd 454d4423643ce80e 2a9ac94fa54ca49f


sha512_abc_message_tests

/// メッセージ "abc" に対し SHA384 を取る

#[test]
fn sha2_384_hash_by_abc_msg() {
let hash = create_hash(&vec![b'a', b'b', b'c'], Length::new(24), Sha521DigestSize::Len384);
assert_eq!(hash, vec![
0xcb, 0x00, 0x75, 0x3f, 0x45, 0xa3, 0x5e, 0x8b,
0xb5, 0xa0, 0x3d, 0x69, 0x9a, 0xc6, 0x50, 0x07,
0x27, 0x2c, 0x32, 0xab, 0x0e, 0xde, 0xd1, 0x63,
0x1a, 0x8b, 0x60, 0x5a, 0x43, 0xff, 0x5b, 0xed,
0x80, 0x86, 0x07, 0x2b, 0xa1, 0xe7, 0xcc, 0x23,
0x58, 0xba, 0xec, 0xa1, 0x34, 0xc8, 0x25, 0xa7
].into_boxed_slice());
}
/// メッセージ "abc" に対し SHA512 を取る
#[test]
fn sha2_512_hash_by_abc_msg() {
let hash = create_hash(&vec![b'a', b'b', b'c'], Length::new(24), Sha521DigestSize::Len512);
assert_eq!(hash, vec![
0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba,
0xcc, 0x41, 0x73, 0x49, 0xae, 0x20, 0x41, 0x31,
0x12, 0xe6, 0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2,
0x0a, 0x9e, 0xee, 0xe6, 0x4b, 0x55, 0xd3, 0x9a,
0x21, 0x92, 0x99, 0x2a, 0x27, 0x4f, 0xc1, 0xa8,
0x36, 0xba, 0x3c, 0x23, 0xa3, 0xfe, 0xeb, 0xbd,
0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, 0xe8, 0x0e,
0x2a, 0x9a, 0xc9, 0x4f, 0xa5, 0x4c, 0xa4, 0x9f
].into_boxed_slice());
}


メッセージが x ビットの連続した 0 である場合 ( SHA-512 のみ )

ビット数
ハッシュ

895
12DD83C5B6547758 452DC7020EE32F53 F5A0EB65D33C4D3F EEBCE17D7113DB14 0393C8FBE49FC071 E40B585DF969C7AA 3A8196CE2B94E83E 7941EC05E2018751

896
2BE2E788C8A8ADEA A9C89A7F78904CAC EA6E39297D75E057 3A73C756234534D6 627AB4156B48A665 7B29AB8BEB733340 40AD39EAD81446BB 09C70704EC707952


sha512_x_zero_bits_tests

/// 895 bit の 0 で構成されたメッセージに対し SHA512 を取る(境界テスト)

#[test]
fn sha2_512_hash_by_895_zero_bits() {
let hash = create_hash(&vec![
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
], Length::new(895), Sha521DigestSize::Len512);
assert_eq!(hash, vec![
0x12, 0xDD, 0x83, 0xC5, 0xB6, 0x54, 0x77, 0x58,
0x45, 0x2D, 0xC7, 0x02, 0x0E, 0xE3, 0x2F, 0x53,
0xF5, 0xA0, 0xEB, 0x65, 0xD3, 0x3C, 0x4D, 0x3F,
0xEE, 0xBC, 0xE1, 0x7D, 0x71, 0x13, 0xDB, 0x14,
0x03, 0x93, 0xC8, 0xFB, 0xE4, 0x9F, 0xC0, 0x71,
0xE4, 0x0B, 0x58, 0x5D, 0xF9, 0x69, 0xC7, 0xAA,
0x3A, 0x81, 0x96, 0xCE, 0x2B, 0x94, 0xE8, 0x3E,
0x79, 0x41, 0xEC, 0x05, 0xE2, 0x01, 0x87, 0x51
].into_boxed_slice());
}
/// 896 bit の 0 で構成されたメッセージに対し SHA512 を取る(境界テスト)
#[test]
fn sha2_512_hash_by_896_zero_bits() {
let hash = create_hash(&vec![
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
], Length::new(896), Sha521DigestSize::Len512);
assert_eq!(hash, vec![
0x2B, 0xE2, 0xE7, 0x88, 0xC8, 0xA8, 0xAD, 0xEA,
0xA9, 0xC8, 0x9A, 0x7F, 0x78, 0x90, 0x4C, 0xAC,
0xEA, 0x6E, 0x39, 0x29, 0x7D, 0x75, 0xE0, 0x57,
0x3A, 0x73, 0xC7, 0x56, 0x23, 0x45, 0x34, 0xD6,
0x62, 0x7A, 0xB4, 0x15, 0x6B, 0x48, 0xA6, 0x65,
0x7B, 0x29, 0xAB, 0x8B, 0xEB, 0x73, 0x33, 0x40,
0x40, 0xAD, 0x39, 0xEA, 0xD8, 0x14, 0x46, 0xBB,
0x09, 0xC7, 0x07, 0x04, 0xEC, 0x70, 0x79, 0x52
].into_boxed_slice());
}


逐次実行

鋭い方はすでにお気づきかもしれないが、実は sha256.rs と sha512.rs は完全ではない。メッセージを代入する型はいずれも Vec<u8> 型であるが、これでは仕様が定める最大メッセージ長のメッセージを代入できない。

Vec<u32>Vec<u64> に拡張するという手もあるが、メモリを多く占有するし、最終的に一気に処理をしなければならないので不便である。

したがって、メッセージを書き込んだら、一時的にバッファに溜め、ブロックサイズまで溜まるごとに処理を行うようにする。メッセージをすべて書き込んだらパディング追加処理を行えばいい。


sha2.rs

use std::boxed::Box;

macro_rules! Ch {
($x:expr, $y:expr, $z:expr) => { ($x & $y) ^ (!$x & $z) };
}
macro_rules! Maj {
($x:expr, $y:expr, $z:expr) => { ($x & $y) ^ ($x & $z) ^ ($y & $z) };
}

/// SHA256追加定数
const K256: [u32; 64] = [
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
];
/// SHA512追加定数
const K512: [u64; 80] = [
0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc,
0x3956c25bf348b538, 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118,
0xd807aa98a3030242, 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2,
0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235, 0xc19bf174cf692694,
0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65,
0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5,
0x983e5152ee66dfab, 0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4,
0xc6e00bf33da88fc2, 0xd5a79147930aa725, 0x06ca6351e003826f, 0x142929670a0e6e70,
0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df,
0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b,
0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30,
0xd192e819d6ef5218, 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8,
0x19a4c116b8d2d0c8