日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術文章
文章詳情頁

PHP實現sha-256哈希算法實例代碼

瀏覽:338日期:2022-06-06 18:33:13
目錄
  • 前言
  • 準備一:代碼主體
  • 準備二:助手函數
    • 步驟一:字符串轉二進制
    • 步驟二:追加數字 1
    • 步驟三:填充至 512 的倍數
    • 步驟四:追加原始長度信息
    • 步驟五:切分區塊并填充至 2048 位
    • 步驟六:區塊數據修改
    • 步驟七:壓縮
  • 總結

    前言

    哈希 又稱作 “散列”,它接收任何一組任意長度的輸入信息,通過 哈希 算法變換成固定長度的數據指紋,該指紋就是 哈希值。總體而言,哈希 可理解為一種消息摘要。

    在 PHP 中有這個函數 hash(),可以計算字符串的哈希值,出于好奇我 Google 了一下哈希計算的具體步驟,并使用 PHP 編寫了一套計算 sha-256 哈希值的代碼。當然除了 sha-256 以外還有一些別的哈希算法,只是目前 sha-256 用的多一些。下面是目前 美國國家標準與技術研究院 發布哈希算法:

    哈希算法輸入大小(bits)分塊大小(bits)行大小(bits)生成二進制長度(bits)生成十六進制長度(chars)sha1< 2^645123216040sha-224< 2^645123222456sha-256< 2^645123225664sha-384< 2^12810246438496sha-512< 2^128102464512128sha-512/224< 2^12810246422456sha-512/256< 2^12810246425664

    在編寫過程中我主要參考了以下文檔和站點:

    Lane Wagner - How SHA-256 Works Step-By-Step:https://blog.boot.dev/cryptography/how-sha-2-works-step-by-step-sha-256/

    Secure Hash Standard (SHS) - FIPS 180-4(官方文檔):https://csrc.nist.gov/publications/detail/fips/180/4/final

    ASCII Table:https://www.asciitable.com/

    本文內容較多,主要分為下面這幾個部分,讀者閱讀時可以先跳過 準備二:助手方法 直接進入 步驟 部分,在閱讀 步驟 部分需要用到指定方法時再回過頭來查閱 準備二:助手方法 中的函數。

    準備一:代碼主體

    準備二:助手方法(閱讀時可先跳過)

    步驟一:字符串轉二進制

    步驟二:追加數字 1

    步驟三:填充至 512 的倍數

    步驟四:追加原始長度信息

    步驟五:切分區塊并填充至 2048 位

    步驟六:區塊數據修改

    步驟七:壓縮

    準備一:代碼主體

    我們創建一個類 Algorithm 來存放我們計算哈希所需要用到的方法和屬性。這個類中只有一個 public 的方法 sha256(),此方法傳入一個字符串參數,輸出此字符串的 sha-256 哈希值。要完成我們的哈希計算,總共需要經過七個步驟,我們先把這七個步驟的調用寫到 sha256() 的函數體中。

    <?php 
    declare(strict_types=1);
    class Algorithm
    {
        public function sha256(string $str): string
        {
    // 步驟一:將字符串轉化為二進制
     $this->step1_convert_str_to_bits($str);
     
    // 步驟二:在最后面追加一個1
     $this->step2_append_1();
     
    // 步驟三:在數據末尾添加0,確保二進制的個數是512的倍數,最后預留64位用于存儲原始長度信息
     $this->step3_extend_to_multiple_of_512();
     
    // 步驟四:把原始字符串位長度,填充到預留在最后的64位(8個字節的長整型)中
     $this->step4_append_origin_length();
     
    // 步驟五:每一個512位切分區塊,在區塊末尾填充0,使得每個區塊位數為2048位,需要增加48行(32位一行)
     $this->step5_split_blocks_and_append_48_lines();
     
    // 步驟六:針對每一個2048位區塊處理:以32位為一行,總共有64行,修改【16-63】行的數據
     $this->step6_modify_blocks_appended_48_lines();
     
    // 步驟七:壓縮數據,生成最終的哈希值
    return $this->step7_compress_to_final_hash();
        }
    }

    除了 sha256() 這個函數外, 我們要需要幾個成員屬性來保存計算過程中產生的數據。

    $originLen 屬性用于記錄字符串被轉化為二進制之后的原始長度,這個長度值后續會追加到數據中去。

    /** @var int 原始數據的二進制長度  */
    private int $originLen = 0;

    $bits 屬性用于儲存字符串轉化后得到的二進制數據。

    /** @var array 存儲二進制數組 */
    private array $bits;

    $blocks 存放分塊后的二進制數據。

    /** @var array 二進制區塊 */
    private array $blocks;

    H 哈希計所需的常量,hash-256 的 8 個哈希常量是質數 2、3、5、7、11、13、17、19 各自平方根取二進制小數部分前 32 位所得。

    /** @var array 質數平方根常量 */
    private const H = [
        0x6a09e667, // 質數2的平方根取二進制小數部分前32位
        0xbb67ae85, // 質數3的平方根取二進制小數部分前32位
        0x3c6ef372, // 質數5的平方根取二進制小數部分前32位
        0xa54ff53a, // 質數7的平方根取二進制小數部分前32位
        0x510e527f, // 質數11的平方根取二進制小數部分前32位
        0x9b05688c, // 質數13的平方根取二進制小數部分前32位
        0x1f83d9ab, // 質數17的平方根取二進制小數部分前32位
        0x5be0cd19, // 質數19的平方根取二進制小數部分前32位
    ];

    對于上面這幾個常量,感興趣的同學也可以自己計算得到,我這里只提供一個簡單的計算示例,以質數 2 為例,我們先通過計算器得到它的平方根:1.4142135623730950488016887242097 然后只取小數部分:0.4142135623730950488016887242097,接著將這個十進制的小數轉為二進制,轉為流程如下:

    小數轉二進制
        0.
    0.4142135623730950488016887242097 x 2 => 0
    0.8284271247461900976033774484194 x 2 => 1
    0.6568542494923801952067548968388 x 2 => 1
    0.3137084989847603904135097936776 x 2 => 0
    0.6274169979695207808270195873552 x 2 => 1
    0.2548339959390415616540391747104 x 2 => 0
    0.~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ x 2 => 1
    0.~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ x 2 => 0
    0.~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ x 2 => 0
    0.~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ x 2 => 0
    0.~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ x 2 => 0
    0.~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ x 2 => 0
    0.~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ x 2 => 1
    0.~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ x 2 => 0
    0.~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ x 2 => 0
    0.~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ x 2 => 1
    0.~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ x 2 => 1
    0.~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ x 2 => 1
    0.~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ x 2 => 1
    0.~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ x 2 => 0
    0.~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ x 2 => 0
    . . .

    上面計算得到的小數部分二進制,取前 32 位:01101010 00001001 11100110 01100111,轉為十六進制表示:0x6a09e667,其他幾個質數的計算也是類似。當然由于是常量,值是固定不變的,所以我們只要知道其計算原理即可。

    和上面的平方根常量類似,hash-256 的另外 64 個常量是質數 2、3、5、…、311 各自立方根取二進制小數部分前 32 位。

    /** @var array 質數立方根常量 */
    private const K = [
        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,
    ];

    準備二:助手函數

    你可以直接跳過此部分內容,從下面的 步驟一 開始著手去計算哈希值,當需要使用到某一個助手函數的時候再來這里查找即可。

    在計算哈希的過程中,我們是把二進制數據存儲到數組中的,數組中的每一個元素對應了二進制的一個比特位,所以如果要對這些二進制數組進行 與 非 異或 相加 等操作,我們就需要實現自己的操作函數。

    十進制整數轉化為二進制數組。

    /**
     * 十進制整數轉化為二進制數組
     * @param int $num 十進制整數
     * @param int $fillTo 填充到多少位,不夠的用0來補齊
     */
     
    public function int2bits(int $num, int $fillTo = 0): array
    {
        $bits = str_split(decbin($num));
        array_walk($bits, function (&$val) {
    $val = intval($val);
        });
     
        for ($len = count($bits); $len < $fillTo; $len++) {
    array_unshift($bits, 0);
        }
        return $bits;
    }

    二進制數組向右移動指定位數。

    /**
     * 二進制數組向右移動
     * @param array $bits 二進制數組
     */
     
    public function rightShift(array $bits, int $move): array
     
    {
        $len = count($bits);
        $move = $move % $len;
        if ($move <= 0) return $bits;
        return array_merge(array_fill(0, $move, 0), array_slice($bits, 0, $len-$move));
    }

    二進制數組向右旋轉,與右移類似,不過移出去的數要插回到頭部。

    /**
     * 二進制數組向右旋轉
     * @param array $bits 二進制數組
     */
     
    public function rightRotate(array $bits, int $move): array
    {
        $len = count($bits);
        $move = $move % $len;
        if ($move <= 0) return $bits;
        return array_merge(array_slice($bits, $len-$move, $move), array_slice($bits, 0, $len-$move));
    }

    二進制數組求 非。

    /**
     * 二進制數組求非
     * @param array $bits 二進制數組
     */
     
    public function not(array $bits): array
    {
        for ($i = count($bits)-1; $i >= 0; $i--) {
    $bits[$i] = ($bits[$i] == 0) ? 1 : 0;
        }
        return $bits;
    }

    多個二進制數組相 與。

    /**
     * 二進制數組求與
     * @param array $args 二進制數組
     */
     
    public function and(array ...$args): array
    {
     
        $argc = count($args);
        if ($argc == 0) return [];
        for ($i = 1; $i < $argc; $i++) {
    $j = count($args[0]) - 1;
    $k = count($args[$i]) - 1;
    while ($j >= 0 || $k >= 0) {
        $j < 0 and array_unshift($args[0], 0) and $j = 0; // 如果是$args[0]不夠長就頭插補齊
        ($args[$i][$k] ?? 0) == 0 and $args[0][$j] = 0;
        $j--;
        $k--;
    }
        }
        return $args[0];
    }

    多個二進制數組求 異或。

    /**
     * 二進制數組求異或
     * @param array $args 二進制數組
     */
     
    public function xor(array ...$args): array
    {
        $argc = count($args);
        if ($argc == 0) return [];
        for ($i = 1; $i < $argc; $i++) {
    $j = count($args[0]) - 1;
    $k = count($args[$i]) - 1;
    while ($j >= 0 || $k >= 0) {
        $j < 0 and array_unshift($args[0], 0) and $j = 0; // 如果是$args[0]不夠長就頭插補齊
        $args[0][$j] = intval($args[0][$j] != ($args[$i][$k] ?? 0));
        $j--;
        $k--;
    }
        }
        return $args[0];
    }

    多個二進制數組 相加。

    /**
     * 二進制數組相加
     * @param array $args 二進制數組
     */
     
    public function add(array ...$args): array
    {
     
        $argc = count($args);
        if ($argc == 0) return [];
        for ($i = 1; $i < $argc; $i++) {
    $carry = 0;
    $j = count($args[0]) - 1;
    $k = count($args[$i]) - 1;
    while ($j >= 0 || $k >= 0) {
        $j < 0 and array_unshift($args[0], 0) and $j = 0; // 如果是$args[0]不夠長就頭插補齊
        $carry += $args[0][$j] + ($args[$i][$k] ?? 0);
        switch ($carry) {
     case 1: $carry = 0; $args[0][$j] = 1; break;
    case 2: $carry = 1; $args[0][$j] = 0; break;
    case 3: $carry = 1; $args[0][$j] = 1; break;
        }
        $j--;
        $k--;
    }
    $carry == 1 and array_unshift($args[0], $carry); // 計算完后還有進位則加長存放
         }
        return array_slice($args[0], -32); // 計算結果只保留32位
    }

    打印二進制數組,用于調試用途,每 8 位會補一個空格,每 32 位補兩個空格,每 64 位換一行,每 512 位空一行,讓打印的數據更容易查看。

    /**
     * 打印二進制數組
     * @param array $bits 二進制數組
     */
     
    public function printBits(array $bits): void
    {
        $len = 0;
        foreach ($bits as $bit) {
    if ($len > 0) {
        if ($len % 512 == 0) echo PHP_EOL;
        if ($len % 64 == 0) {
    echo PHP_EOL;  
        } else {
    if ($len % 32 == 0) echo " ";
    if ($len % 8 == 0) echo " ";
        }
    }
    echo $bit;
    $len++;
        }
        echo PHP_EOL;
    }

    二進制數組轉化為十六進制,用于最后一步將二進制轉換為哈希值字符串。

    /**
     * 二進制數組轉化為十六進制
     * @param array $bits 二進制數組
     */
     
    public function bits2hex(array $bits): string
    {
        $str = "";
        for ($i = count($bits)-1; $i >= 0; $i -= 4) {
    $dec = $bits[$i] + ($bits[$i-1] ?? 0)*2 + ($bits[$i-2] ?? 0)*4 + ($bits[$i-3] ?? 0)*8;
    switch ($dec) {
        case 0:  $str = "0" . $str; break;
        case 1:  $str = "1" . $str; break;
        case 2:  $str = "2" . $str; break;
        case 3:  $str = "3" . $str; break;
        case 4:  $str = "4" . $str; break;
        case 5:  $str = "5" . $str; break;
        case 6:  $str = "6" . $str; break;
        case 7:  $str = "7" . $str; break;
        case 8:  $str = "8" . $str; break;
        case 9:  $str = "9" . $str; break;
        case 10: $str = "a" . $str; break;
        case 11: $str = "b" . $str; break;
        case 12: $str = "c" . $str; break;
        case 13: $str = "d" . $str; break;
        case 14: $str = "e" . $str; break;
        case 15: $str = "f" . $str; break;
    }
        }
        return $str;
    }

    步驟一:字符串轉二進制

    這里我們使用 "hello world" 字符串來演示整個哈希計算過程。我們可以先用 PHP 內置的哈希函數將結果算出來, "hello world" 的哈希值是 "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9",到最后我們計算出來的哈希值如果等于這個值則說明我們的計算邏輯是正確的。

    首先我們把 "hello world" 拆成一個個的字符,每個字符都有對應一個 ASCII 碼值,這些 ASCII 碼值都是 0-256 的整數。使用 PHP 的 ord() 函數可以把這些字符轉為整數,再將這些整數轉為對應的二進制并存儲到屬性 $bits 中。并將此時 $bits 的長度值保存到 $originLen 屬性里。

    "hello world" 轉為二進制后的數據是:

    “hello world”

    01101000 01100101 01101100 01101100  01101111 00100000 01110111 01101111

    01110010 01101100 01100100

    /**
     * 步驟一:將字符串轉化為二進制
     * @param string $str 原始字符串
     */
     
    public function step1_convert_str_to_bits(string $str): void
    {
     
        $this->bits = [];
        $chars = str_split($str);
        foreach ($chars as $char) {
    $this->bits = array_merge($this->bits, $this->int2bits(ord($char), 8));
        }
        $this->originLen = count($this->bits);
    }

    步驟二:追加數字 1

    接著在二進制數組的末尾添加一個 1。

    $bits

    01101000 01100101 01101100 01101100  01101111 00100000 01110111 01101111

    01110010 01101100 01100100 1

    /**
     * 步驟二:在最后面追加一個1
     */
     
    public function step2_append_1(): void
    {
        $this->bits[] = 1;
    }

    步驟三:填充至 512 的倍數

    在二進制數組的末尾添加 0 以使得整個二進制數組的個數剛好是 512 的倍數。需要注意的是,二進制數組的最末尾要預留 64 位用于存放原始二進制的長度。也就是一開始將字符串轉換成二進制時的長度,我們在 步驟一 中將這個長度值保存到了 $originLen 屬性里。

    $bits

    01101000 01100101 01101100 01101100  01101111 00100000 01110111 01101111

    01110010 01101100 01100100 10000000  00000000 00000000 00000000 00000000

    00000000 00000000 00000000 00000000  00000000 00000000 00000000 00000000

    00000000 00000000 00000000 00000000  00000000 00000000 00000000 00000000

    00000000 00000000 00000000 00000000  00000000 00000000 00000000 00000000

    00000000 00000000 00000000 00000000  00000000 00000000 00000000 00000000

    00000000 00000000 00000000 00000000  00000000 00000000 00000000 00000000

    [    預留 64 位用于存儲原始字符串的長度    ]

    /**
     * 步驟三:在數據末尾添加0,確保二進制的個數是512的倍數,最后預留64位用于存儲原始長度信息
     */
     
    public function step3_extend_to_multiple_of_512(): void
    {
        $rem = (count($this->bits) + 64) % 512;
        if ($rem > 0) {
    while ($rem < 512) {
        $this->bits[] = 0;
        $rem++;
    }
        }
    }

    步驟四:追加原始長度信息

    把之前記錄的原始數據長度 $originLen 轉換為 64 位的二進制追加到 $bits 末尾。

    $bits

    01101000 01100101 01101100 01101100  01101111 00100000 01110111 01101111

    01110010 01101100 01100100 10000000  00000000 00000000 00000000 00000000

    00000000 00000000 00000000 00000000  00000000 00000000 00000000 00000000

    00000000 00000000 00000000 00000000  00000000 00000000 00000000 00000000

    00000000 00000000 00000000 00000000  00000000 00000000 00000000 00000000

    00000000 00000000 00000000 00000000  00000000 00000000 00000000 00000000

    00000000 00000000 00000000 00000000  00000000 00000000 00000000 00000000

    00000000 00000000 00000000 00000000  00000000 00000000 00000000 01011000

    /**
     * 步驟四:把原始字符串位長度,填充到預留在最后的64位(8個字節的長整型)中
     */
     
    public function step4_append_origin_length(): voi
     
    {
        $this->bits = array_merge($this->bits, $this->int2bits($this->originLen, 64));
    }

    步驟五:切分區塊并填充至 2048 位

    經過 步驟四 之后,$bits 二進制數組的個數已經是 512 的倍數,現在以每 512 位分為一個區塊,然后在每個區塊末尾填充 0,讓每個區塊的大小變成 2048 位。每個區塊的 2048 位數據以 32 位作為一行,那么就有 64 行。由于 "hello world" 數據比較短,我們就只有一個區塊。

    -$blocks[0]$blocks[0]-0
    2
    4
    6
    8
    10
    12
    14

    16
    18
    20
    22
    24
    26
    28
    30

    32
    34
    36
    38
    40
    42
    44
    46

    48
    50
    52
    54
    56
    58
    60
    6201101000 01100101 01101100 01101100
    01110010 01101100 01100100 10000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000

    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000

    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000

    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 0000000001101111 00100000 01110111 01101111
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 01011000

    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000

    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000

    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 000000001
    3
    5
    7
    9
    11
    13
    15

    17
    19
    21
    23
    25
    27
    29
    31

    33
    35
    37
    39
    41
    43
    45
    47

    49
    51
    53
    55
    57
    59
    61
    63
    /**
     * 步驟五:每一個512位切分區塊,在區塊末尾填充0,使得每個區塊位數為2048位,經計算
     * 每個區塊還需要添加48x32個0
     */
     
    public function step5_split_blocks_and_append_48_lines(): void
    {
        $this->blocks = [];
        $append = $this->int2bits(0, 48 * 32);
        $len = count($this->bits);
        for ($i = 0; $i < $len; $i += 512) {
    $this->blocks[] = array_merge(array_slice($this->bits, $i, 512), $append);
        }
    }

    步驟六:區塊數據修改

    上一步中我們給每一個區塊末尾添加了很多 0,在這一步中,通過一些位操作將這些數據進一步調整。按 32 位為一行,我們需要修改新增加的 16-63 行的數據。修改的邏輯如下:

    算法邏輯

    For i from w[16…63]:
    ????s0 = (w[i-15] rightrotate 7) xor (w[i-15] rightrotate 18) xor (w[i-15] rightshift 3)
    ????s1 = (w[i-2] rightrotate 17) xor (w[i- 2] rightrotate 19) xor (w[i- 2] rightshift 10)
     ????w[i] = w[i-16] + s0 + w[i-7] + s1

    其中 w 是每個區塊的行數組,w[i] 就是第 i 行。

    rightshift 是右移,rightrotate 是旋轉右移, xor 是異或。

    這里以第 16 行的處理為例:

    算法詳解

    i = 16
    (w[1] rightrotate 7) = 01101111001000000111011101101111 -> 11011110110111100100000011101110
    (w[1] rightrotate 18) = 01101111001000000111011101101111 -> 00011101110110111101101111001000
    (w[1] rightshift 3) = 01101111001000000111011101101111 -> 00001101111001000000111011101101
    s0 = (w[1] rightrotate 7) xor (w[1] rightrotate 18) xor (w[1] rightshift 3)
    ?= 11001110111000011001010111001011
    (w[14] rightrotate 17) = 00000000000000000000000000000000 -> 00000000000000000000000000000000
    (w[14] rightrotate 19) = 00000000000000000000000000000000 -> 00000000000000000000000000000000
    (w[14] rightshift 10) = 00000000000000000000000000000000 -> 00000000000000000000000000000000
    s1 = (w[14] rightrotate 17) xor (w[14] rightrotate 19) xor (w[14] rightshift 10)
    = 00000000000000000000000000000000
    w[i] = w[0] + s0 + w[9] + s1
    = 00110111010001110000001000110111(相加得到的值如果超過 32 位,則抹去高位)
    /**
     * 步驟六:針對每一個2048位區塊處理:以32位為一行,總共有64行,修改【16-63】行的數據,
     * 這【16-63】行就是上一步新增的48x32個0
     */
     
    public function step6_modify_blocks_appended_48_lines(): void
    {
        foreach ($this->blocks as &$block) {
    for ($i = 16; $i < 64; $i++) {
        $w0 = array_slice($block, ($i-16)*32, 32);
        $w1 = array_slice($block, ($i-15)*32, 32);
        $w9 = array_slice($block, ($i-7)*32, 32);
        $w14 = array_slice($block, ($i-2)*32, 32);
        $s0 = $this->xor(   
    $this->rightRotate($w1, 7),
    $this->rightRotate($w1, 18),
    $this->rightShift($w1, 3)
        );
     
        $s1 = $this->xor(
    $this->rightRotate($w14, 17),
    $this->rightRotate($w14, 19),
    $this->rightShift($w14, 10)
        );
     
        $wi = $this->add($w0, $s0, $w9, $s1);
        // 如果$wi的長度超過了32位,則只取32位,舍棄高位
        $k = count($wi) - 1;
        for ($j = $i * 32 + 31; $j >= $i * 32; $j--) {
    $block[$j] = $wi[$k] ?? 0;
    $k--;
        }
    }
        }
    }

    步驟七:壓縮

    新建變量 $a、$b、$c、$d、$e、$f、$g、$h 值依次分別等于哈希常量 H[0-7],接著循環每一個區塊的每一行,通過 與 非 異或 等操作將信息壓縮到 $a、$b、$c、$d、$e、$f、$g、$h 中,最后將 $a、$b、$c、$d、$e、$f、$g、$h 的值與原始常量 H[0-7] 相加,拼接相加后的二進制結果 h0~h7 并轉化為十六進制字符串得到最終的哈希值。

    具體的壓縮算法如下:

    算法邏輯

    For i from 0 to 63
    ????s1 = (e rightrotate 6) xor (e rightrotate 11) xor (e rightrotate 25)
    ????ch = (e and f) xor ((not e) and g)
    ????temp1 = h + s1 + ch + k[i] + w[i]
    ????s0 = (a rightrotate 2) xor (a rightrotate 13) xor (a rightrotate 22)
    ????maj = (a and b) xor (a and c) xor (b and c)
    ????temp2 := s0 + maj
    ????h = g
    ????g = f
    ????f = e
    ????e = d + temp1
    ????d = c
    ????c = b
    ????b = a
    ????a = temp1 + temp2

    這里以第 0 行的處理為例,列出了變量計算結果方便大家對照調試:

    計算結果

    i = 0

    s1 = 00110101100001110010011100101011

    ch = 00011111100001011100100110001100

    temp1 = 01011011110111010101100111010100

    s0 = 11001110001000001011010001111110

    maj = 00111010011011111110011001100111

    temp2 = 00001000100100001001101011100101

    h = 00011111100000111101100110101011

    g = 10011011000001010110100010001100

    f = 01010001000011100101001001111111

    e = 00000001001011010100111100001110

    d = 00111100011011101111001101110010

    c = 10111011011001111010111010000101

    b = 01101010000010011110011001100111

    a = 01100100011011011111010010111001

    /**
     * 步驟七:壓縮數據
     */
     
    public function step7_compress_to_final_hash(): string
    {
     
        $a = $h0 = $this->int2bits(static::H[0], 32);
        $b = $h1 = $this->int2bits(static::H[1], 32);
        $c = $h2 = $this->int2bits(static::H[2], 32);
        $d = $h3 = $this->int2bits(static::H[3], 32);
        $e = $h4 = $this->int2bits(static::H[4], 32);
        $f = $h5 = $this->int2bits(static::H[5], 32);
        $g = $h6 = $this->int2bits(static::H[6], 32);
        $h = $h7 = $this->int2bits(static::H[7], 32);
        foreach ($this->blocks as $block) {
    for ($i = 0; $i < 64; $i++) {
        $s1 = $this->xor(
    $this->rightRotate($e, 6),
    $this->rightRotate($e, 11),
    $this->rightRotate($e, 25)
        );
     
        $ch = $this->xor(
    $this->and($e, $f),
    $this->and($this->not($e), $g)
        );
     
        $ki = $this->int2bits(static::K[$i], 32);
        $wi = array_slice($block, $i*32, 32);
        $temp1 = $this->add($h, $s1, $ch, $ki, $wi);
        $s0 = $this->xor(
    $this->rightRotate($a, 2),
    $this->rightRotate($a, 13),
    $this->rightRotate($a, 22),
        );
     
        $maj = $this->xor(
    $this->and($a, $b),
    $this->and($a, $c),
    $this->and($b, $c)
        );
     
        $temp2 = $this->add($s0, $maj);
        $h = $g;
        $g = $f;
        $f = $e;
        $e = $this->add($d, $temp1);
        $d = $c;
        $c = $b;
        $b = $a;
        $a = $this->add($temp1, $temp2);
    }
        }
     
        $h0 = $this->add($h0, $a);
        $h1 = $this->add($h1, $b);
        $h2 = $this->add($h2, $c);
        $h3 = $this->add($h3, $d);
        $h4 = $this->add($h4, $e);
        $h5 = $this->add($h5, $f);
        $h6 = $this->add($h6, $g);
        $h7 = $this->add($h7, $h);
        return $this->bits2hex(array_merge($h0, $h1, $h2, $h3, $h4, $h5, $h6, $h7));
    }

    至此整個哈希 sha-256 計算流程就完成了, 計算得到的哈希值也與 PHP 自帶的 hash() 函數計算結果一致。

    總結

    到此這篇關于PHP實現sha-256哈希算法的文章就介紹到這了,更多相關PHP實現sha-256哈希算法內容請搜索以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持!

    標簽: PHP
    相關文章:
    日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
    日韩在线观看一区二区| 91综合网人人| 欧美日韩精品一本二本三本| 久久精品国产亚洲夜色av网站| 国产一区福利| 中文字幕色婷婷在线视频| 人人草在线视频| 91综合视频| 在线成人动漫av| 亚洲资源av| 日韩美女精品| 久久精品国产亚洲aⅴ| 欧美好骚综合网| 久久精品不卡| 免费日本视频一区| 奇米亚洲欧美| 国产69精品久久| 国产高清一区| 四虎成人精品一区二区免费网站| 亚洲精品字幕| 欧美日韩一区二区三区不卡视频| 老司机免费视频一区二区| 成人三级高清视频在线看| 欧美成人精品| 亚洲日产av中文字幕| 国产精品嫩模av在线| 国产66精品| 国产综合欧美| 亚洲精选久久| 精品国产午夜| 亚洲一区免费| 国产精品欧美三级在线观看| 国产麻豆久久| 亚洲不卡视频| 久久av国产紧身裤| 99精品电影| 亚洲精品第一| 91一区二区三区四区| 老鸭窝毛片一区二区三区| 国产精品日本一区二区不卡视频| 成人精品国产亚洲| 午夜av一区| 免费在线成人| 久久国产精品毛片| 精品少妇av| 午夜在线播放视频欧美| 久久99偷拍| 欧美日韩四区| 久久不见久久见中文字幕免费| 日韩伦理一区| 午夜精品福利影院| 精品三级久久| 日本午夜精品视频在线观看| 久久久天天操| 久久激五月天综合精品| 999国产精品视频| 久久国产麻豆精品| 亚洲高清成人| 国产精品videossex久久发布 | 日韩激情视频网站| 吉吉日韩欧美| 欧美亚洲三区| 国产精品人人爽人人做我的可爱| 麻豆国产欧美日韩综合精品二区| 久久中文字幕二区| 国产精品久久久久av蜜臀 | 久久xxxx精品视频| 国产夫妻在线| 欧美日韩精品一区二区三区视频 | 91精品国产自产观看在线 | 国产资源在线观看入口av| 亚洲精品视频一二三区| 久久av网站| 国产一区91| 中文字幕在线免费观看视频| 日韩欧美激情电影| 国产主播一区| 福利视频一区| 国产日产精品一区二区三区四区的观看方式| 99久久夜色精品国产亚洲1000部| 国产精品亚洲综合色区韩国 | 国产韩日影视精品| 精品视频国内| 国产日韩欧美一区二区三区在线观看| 日韩一区二区久久| 亚洲成人av观看| 国产精品久久久久久模特| 人人爽香蕉精品| 成人精品亚洲| 免费在线观看一区| 奇米狠狠一区二区三区| 午夜亚洲精品| 亚洲午夜黄色| 亚洲伦乱视频| 日韩一区欧美| 国产中文字幕一区二区三区| 国产精品成人3p一区二区三区| 日韩精品亚洲aⅴ在线影院| 免播放器亚洲| 午夜精品亚洲| 91九色精品| 亚洲精品.com| 国产精品久久观看| 精品欧美视频| 黄色精品视频| 狠狠久久伊人| 日本一二区不卡| 精品久久久中文字幕| 久久xxx视频| 国产精品白丝一区二区三区| 国产一卡不卡| 国产视频一区二区在线播放| 日本在线不卡视频| 天堂久久av| 日本一区二区三区中文字幕| 中文字幕av一区二区三区四区| 亚洲成人免费| 亚洲激情不卡| 亚洲一区观看| 久久国产88| 亚洲精品乱码| 日本不卡一二三区黄网| 日本不卡中文字幕| 国产日产精品_国产精品毛片 | 久久精品亚洲人成影院| 日韩精品水蜜桃| 亚洲午夜精品久久久久久app| 欧美性感美女一区二区| 99国产精品免费视频观看| 99精品视频在线观看免费播放| 久久久影院免费| 国产精品毛片一区二区三区| 蜜臀国产一区二区三区在线播放| 日本欧洲一区二区| 欧美一级二级视频| 麻豆视频久久| 日韩大片在线观看| 91精品国产自产在线观看永久∴ | 精品成人18| 欧洲亚洲一区二区三区| 婷婷国产精品| 日韩在线播放一区二区| 欧美亚洲免费| 国产一区二区三区成人欧美日韩在线观看| 91综合视频| 亚洲精品123区| 日韩国产一区二| 精品国产美女a久久9999| 午夜av成人| 免费成人性网站| 国产精品视频一区二区三区四蜜臂 | 久久久久免费av| 91精品99| 综合视频一区| 国产精品久久国产愉拍| 日韩在线高清| 玖玖玖国产精品| 国产精品巨作av| 久久精品亚洲欧美日韩精品中文字幕| 国产精品日韩久久久| 欧美日韩亚洲一区二区三区在线 | 美女精品视频在线| 电影亚洲精品噜噜在线观看| 欧美专区在线| 国产精品啊啊啊| 欧美1级日本1级| 国产免费久久| 欧美日一区二区| 国产日韩欧美在线播放不卡| 久久久夜夜夜| 91亚洲精品在看在线观看高清| 国产一区二区三区探花| 亚洲欧美不卡| 久久精品资源| 亚洲一卡久久| 精品国产精品国产偷麻豆 | 9久re热视频在线精品| 国产日产一区| 激情五月综合网| 国产精品黄色| 99在线|亚洲一区二区| 麻豆精品视频在线观看| 中国女人久久久| 国际精品欧美精品| 免费观看久久久4p| 国产精品亚洲一区二区三区在线观看| 亚洲1区在线| 香蕉成人av| 欧美一级二级视频| 欧美福利专区| 免费看久久久| 一区二区三区四区日韩| 精品丝袜在线| 国产精品magnet| 国产视频一区免费看| 欧美韩日一区| 欧美一级网站| 鲁大师成人一区二区三区| 亚洲深夜视频| 国产欧美精品久久|