中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

怎么用PHP實現自己的sha-256哈希算法

發布時間:2022-05-23 14:00:37 來源:億速云 閱讀:214 作者:iii 欄目:編程語言

今天小編給大家分享一下怎么用PHP實現自己的sha-256哈希算法的相關知識點,內容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。

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

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

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

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

Lane Wagner - How SHA-256 Works Step-By-Step

Secure Hash Standard (SHS) - FIPS 180-4(官方文檔)

ASCII Table

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

準備一:代碼主體

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

步驟一:字符串轉二進制

步驟二:追加數字 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(): void
{
    $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
62
01101000 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 00000000
01101111 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 00000000
1
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 = $h2 = $this->int2bits(static::H[1], 32);
    $c = $h3 = $this->int2bits(static::H[2], 32);
    $d = $h4 = $this->int2bits(static::H[3], 32);
    $e = $h5 = $this->int2bits(static::H[4], 32);
    $f = $h6 = $this->int2bits(static::H[5], 32);
    $g = $h7 = $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);
    $h2 = $this->add($h2, $b);
    $h3 = $this->add($h3, $c);
    $h4 = $this->add($h4, $d);
    $h5 = $this->add($h5, $e);
    $h6 = $this->add($h6, $f);
    $h7 = $this->add($h7, $g);
    $h7 = $this->add($h7, $h);
    return $this->bits2hex(array_merge($h0, $h2, $h3, $h4, $h5, $h6, $h7, $h7));
}

以上就是“怎么用PHP實現自己的sha-256哈希算法”這篇文章的所有內容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

php
AI

旬阳县| 宝兴县| 额尔古纳市| 安西县| 南和县| 台北县| 余姚市| 鄂伦春自治旗| 遂平县| 肇东市| 苍溪县| 岳阳县| 闵行区| 武功县| 东海县| 五莲县| 遂宁市| 双峰县| 定结县| 新竹市| 兰坪| 南宫市| 托克逊县| 阿巴嘎旗| 达州市| 大新县| 浦县| 上栗县| 秦皇岛市| 通辽市| 普兰店市| 垣曲县| 嵩明县| 民县| 江源县| 尚义县| 芦山县| 图们市| 安泽县| 大冶市| 云南省|