2025/09/21

Rust SHA-1 実装

SHA-1 (Secure Hash Algorithm 1)は,暗号学的ハッシュ関数の1つである.いまでは非推奨とされている.

主な特徴

  • Merkle–Damgård構造
  • 固定長160ビットのハッシュ値を生成する.
  • 出力は,16進数として40桁になる.
  • 入力長に制限はない(正しくは2^64 - 1が限度).
  • 同じ入力からは,必ず同じ値が得られる.
  • 入力が1ビットでも変われば,出力結果は全く異なる.
  • (共通:暗号学的ハッシュ関数としての最低条件については割合).

安全性について

特徴では’入力が1ビットでも変われば,出力結果は全く異なる.’と書いたが,今現在では衝突が確認されている.そのため,暗号学的ハッシュ関数としての使用は非推奨とされている.

適当に処理の流れ

  1. 初期値(決まっている).
  2. 入力を得る.
  3. 入力を512ビット倍数になるようパディング処理をする.
  4. 512ビットブロックとし,80単位の32ビットに拡張する.
  5. メイン.80ラウンド(80単位としたため)の反復処理をする.
  6. 結果の出力.

パディングについて

  1. 入力が512ビット倍数になるよう,0x80の1ビットをアペンドしていく.
  2. 448 mod 512となるまでゼロ(0)パディングを行う.
  3. 元を64ビット表現としアペンドする.

実装

/*
MIT License

Copyright (c) 2025 flucium

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

/*
    Reference
    https://datatracker.ietf.org/doc/html/rfc3174#page-11
*/
#[inline]
const fn intermediate_hash_value() -> [u32; 5] {
    [0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0]
}

#[inline]
fn u32_into(u: u32, dst: &mut [u8]) {
    dst[0] = (u >> 24) as u8;
    dst[1] = (u >> 16) as u8;
    dst[2] = (u >> 8) as u8;
    dst[3] = u as u8;
}

#[inline]
fn u64_into(u: u64, dst: &mut [u8]) {
    dst[0] = (u >> 56) as u8;
    dst[1] = (u >> 48) as u8;
    dst[2] = (u >> 40) as u8;
    dst[3] = (u >> 32) as u8;
    dst[4] = (u >> 24) as u8;
    dst[5] = (u >> 16) as u8;
    dst[6] = (u >> 8) as u8;
    dst[7] = u as u8;
}

/*
    Reference
    https://datatracker.ietf.org/doc/html/rfc3174#page-4
*/
#[inline]
fn rol32(a: u32, b: u32) -> u32 {
    let l: u32 = a << b;
    let r: u32 = a >> (32 - b);

    l | r
}

/*
    Reference
    https://datatracker.ietf.org/doc/html/rfc3174#page-15
*/
#[inline]
fn block_process_wrapping_add(
    w: [u32; 80],
    h0: &mut u32,
    h1: &mut u32,
    h2: &mut u32,
    h3: &mut u32,
    h4: &mut u32,
) {
    const LOOP_I_1: u32 = 0x5A827999u32;
    const LOOP_I_2: u32 = 0x6ED9EBA1;
    const LOOP_I_3: u32 = 0x8F1BBCDC;
    const LOOP_I_4: u32 = 0xCA62C1D6;

    let mut a: u32 = *h0;
    let mut b: u32 = *h1;
    let mut c: u32 = *h2;
    let mut d: u32 = *h3;
    let mut e: u32 = *h4;

    for i in 0..80 {
        let (f, k) = if i < 20 {
            ((b & c) | ((!b) & d), LOOP_I_1)
        } else if i < 40 {
            (b ^ c ^ d, LOOP_I_2)
        } else if i < 60 {
            (((b & c) | (b & d) | (c & d)), LOOP_I_3)
        } else {
            (b ^ c ^ d, LOOP_I_4)
        };

        let tmp = rol32(a, 5)
            .wrapping_add(f)
            .wrapping_add(e)
            .wrapping_add(k)
            .wrapping_add(w[i]);

        e = d;
        d = c;
        c = rol32(b, 30);
        b = a;
        a = tmp;
    }

    *h0 = h0.wrapping_add(a);
    *h1 = h1.wrapping_add(b);
    *h2 = h2.wrapping_add(c);
    *h3 = h3.wrapping_add(d);
    *h4 = h4.wrapping_add(e);
}

/*
    Reference
    https://datatracker.ietf.org/doc/html/rfc3174#page-15
*/
fn block_process(
    block: &[u8],
    h0: &mut u32,
    h1: &mut u32,
    h2: &mut u32,
    h3: &mut u32,
    h4: &mut u32,
) {
    let mut w: [u32; 80] = [0u32; 80];

    for i in 0..16 {
        let j = i * 4;

        let tmp: [u8; 4] = [block[j], block[j + 1], block[j + 2], block[j + 3]];

        w[i] = u32::from_be_bytes(tmp);
    }

    for i in 16..80 {
        let a: u32 = w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16];

        let b: u32 = 1;

        let tmp: u32 = rol32(a, b);

        w[i] = tmp;
    }

    block_process_wrapping_add(w, h0, h1, h2, h3, h4);
}

/*
    Reference
    https://datatracker.ietf.org/doc/html/rfc3174#page-13
    https://datatracker.ietf.org/doc/html/rfc3174#page-17
*/
pub fn sha1(input: &[u8]) -> [u8; 20] {
    let (mut h0, mut h1, mut h2, mut h3, mut h4): (u32, u32, u32, u32, u32) =
        intermediate_hash_value().into();

    let mut chunks_exact = input.chunks_exact(64);

    for block in chunks_exact.by_ref() {
        block_process(block, &mut h0, &mut h1, &mut h2, &mut h3, &mut h4);
    }

    let chunks: &[u8] = chunks_exact.remainder();

    let chunks_len: usize = chunks.len();

    let mut buf: [u8; 128] = [0u8; 128];

    buf[..chunks_len].copy_from_slice(chunks);

    buf[chunks_len] = 0x80;

    let mut counter: usize = chunks_len + 1;

    while (counter % 64) != 56 {
        buf[counter] = 0;
        counter += 1;
    }

    let dst: &mut [u8] = &mut [0u8; 8];

    u64_into((input.len() as u64) * 8, dst);

    buf[counter..counter + 8].copy_from_slice(&dst);

    counter += 8;

    let pad: usize = counter;

    let mut pad_off_counter: usize = 0;

    while pad_off_counter < pad {
        let block: &[u8] = &buf[pad_off_counter..pad_off_counter + 64];

        block_process(block, &mut h0, &mut h1, &mut h2, &mut h3, &mut h4);

        pad_off_counter += 64;
    }

    let mut buf = [0u8; 20];

    u32_into(h0, &mut buf[0..4]);

    u32_into(h1, &mut buf[4..8]);

    u32_into(h2, &mut buf[8..12]);

    u32_into(h3, &mut buf[12..16]);

    u32_into(h4, &mut buf[16..20]);

    buf
}

実行

/*
MIT License

Copyright (c) 2025 flucium

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/


fn main() {
    println!("{:?}", sha1(b""));
}

終わり

終わり 終わり 終わり.