|
| 1 | +# SHA-256實作原理 |
| 2 | + |
| 3 | +### 第一步: |
| 4 | + |
| 5 | +``` |
| 6 | +1.把明文轉為二進位的數字bits |
| 7 | +2.在最後面先放一個數字1 |
| 8 | +3.在最後面補 k 個 0 直到明文bits的長度 + 1 + k % 512 = 448 |
| 9 | +``` |
| 10 | + |
| 11 | +做完上述三點後,在最後面再加上 64 bits的數字,內容為明文的長度\(以二進位表示\) ,例如:abc之ASCII二進位長度為24 bits |
| 12 | + |
| 13 | +而 24 之二進位為11000,因為不足 64 bit 於是在前面補0使其成為:`00....11000`,補足64 bits。 |
| 14 | + |
| 15 | +> 以下為 8-bit 之 ASCII 字母:abc 之例子。對照下圖的第一個 8 bits: `01100001` 對應到 `a`,可參照ASCII Table。 |
| 16 | +
|
| 17 | + |
| 18 | + |
| 19 | +### 第二步: |
| 20 | + |
| 21 | +寫出八個 initial hash value(此為被預先定義之固定值) |
| 22 | + |
| 23 | +####  |
| 24 | + |
| 25 | +##### 原理: |
| 26 | + |
| 27 | +```js |
| 28 | +先取Math.sqrt(n)的小數,然後乘上2 ** 32,之後轉為16進位,然後取小數前面部分 |
| 29 | +其中math.sqrt(n),n為從0往上找到的最小八個質數。 |
| 30 | + |
| 31 | +H(0) |
| 32 | +1 = 6a09e667 |
| 33 | +(Math.sqrt(2) % 1 * (2**32)).toString(16).substring(0,8) |
| 34 | + |
| 35 | +H(0) |
| 36 | +2 = bb67ae85 |
| 37 | +(Math.sqrt(3) % 1 * (2**32)).toString(16).substring(0,8) |
| 38 | + |
| 39 | +H(0) |
| 40 | +3 = 3c6ef372 |
| 41 | +(Math.sqrt(5) % 1 * (2**32)).toString(16).substring(0,8) |
| 42 | + |
| 43 | +H(0) |
| 44 | +4 = a54ff53a |
| 45 | +(Math.sqrt(7) % 1 * (2**32)).toString(16).substring(0,8) |
| 46 | + |
| 47 | +H(0) |
| 48 | +5 = 510e527f |
| 49 | +(Math.sqrt(11) % 1 * (2**32)).toString(16).substring(0,8) |
| 50 | + |
| 51 | +H(0) |
| 52 | +6 = 9b05688c |
| 53 | +(Math.sqrt(13) % 1 * (2**32)).toString(16).substring(0,8) |
| 54 | + |
| 55 | +H(0) |
| 56 | +7 = 1f83d9ab |
| 57 | +(Math.sqrt(17) % 1 * (2**32)).toString(16).substring(0,8) |
| 58 | + |
| 59 | +H(0) |
| 60 | +8 = 5be0cd19 |
| 61 | +(Math.sqrt(19) % 1 * (2**32)).toString(16).substring(0,8) |
| 62 | +``` |
| 63 | + |
| 64 | + |
| 65 | + |
| 66 | +### 第三步 |
| 67 | + |
| 68 | +寫出 64 個固定 k 值 |
| 69 | + |
| 70 | +**原理: ** |
| 71 | + |
| 72 | +```js |
| 73 | +用Math cube root的方法找首64個質數 |
| 74 | + |
| 75 | +(Math.cbrt(2) * (2**32)).toString(16).substring(1,9) |
| 76 | +"428a2f98" |
| 77 | + |
| 78 | +(Math.cbrt(3) * (2**32)).toString(16).substring(1,9) |
| 79 | +"71374491" |
| 80 | + |
| 81 | +(Math.cbrt(5) * (2**32)).toString(16).substring(1,9) |
| 82 | +"b5c0fbcf" |
| 83 | +``` |
| 84 | + |
| 85 | + |
| 86 | + |
| 87 | +### 第四步: |
| 88 | + |
| 89 | +使用SHA-256定義的六個邏輯 Function |
| 90 | + |
| 91 | + |
| 92 | + |
| 93 | +其中符號定義如下 |
| 94 | + |
| 95 | + |
| 96 | + |
| 97 | +寫成程式為: |
| 98 | + |
| 99 | +```js |
| 100 | +function ch (x, y, z) { |
| 101 | + return z ^ (x & (y ^ z)) |
| 102 | +} |
| 103 | + |
| 104 | +function maj (x, y, z) { |
| 105 | + return (x & y) | (z & (x | y)) |
| 106 | +} |
| 107 | + |
| 108 | +function sigma0 (x) { |
| 109 | + return (x >>> 2 | x << 30) ^ (x >>> 13 | x << 19) ^ (x >>> 22 | x << 10) |
| 110 | +} |
| 111 | + |
| 112 | +function sigma1 (x) { |
| 113 | + return (x >>> 6 | x << 26) ^ (x >>> 11 | x << 21) ^ (x >>> 25 | x << 7) |
| 114 | +} |
| 115 | + |
| 116 | +function gamma0 (x) { |
| 117 | + return (x >>> 7 | x << 25) ^ (x >>> 18 | x << 14) ^ (x >>> 3) |
| 118 | +} |
| 119 | + |
| 120 | +function gamma1 (x) { |
| 121 | + return (x >>> 17 | x << 15) ^ (x >>> 19 | x << 13) ^ (x >>> 10) |
| 122 | +} |
| 123 | +``` |
| 124 | + |
| 125 | +# 完整範例 |
| 126 | + |
| 127 | +> 需要先安裝big-integer模組 |
| 128 | +
|
| 129 | +``` |
| 130 | +npm install big-integer |
| 131 | +``` |
| 132 | + |
| 133 | +```js |
| 134 | +const util = require('util'); |
| 135 | +var bigInt = require("big-integer"); |
| 136 | + |
| 137 | + |
| 138 | +function Hash (blockSize, finalSize) { |
| 139 | + this._block = new Buffer(blockSize) |
| 140 | + this._finalSize = finalSize |
| 141 | + this._blockSize = blockSize |
| 142 | + this._len = 0 |
| 143 | +} |
| 144 | + |
| 145 | +Hash.prototype.digest = function (data, enc) { |
| 146 | + |
| 147 | +data = (data).toString(); |
| 148 | +////////////////// |
| 149 | +let s = "" |
| 150 | +let s1 = "" |
| 151 | + |
| 152 | +// 將字串轉為二進位ASCII |
| 153 | +function ascii (a) { |
| 154 | + let dd = ""; |
| 155 | + for(let i = 0 ; i< a.length; i++) { |
| 156 | + let charAscII = a.charAt(i).charCodeAt(0).toString(2) |
| 157 | + while(charAscII.length < 8) { // 最前面的0都會被省略,所以手動加上 |
| 158 | + charAscII = 0 + charAscII |
| 159 | + } |
| 160 | + dd += charAscII; |
| 161 | + } |
| 162 | + return dd |
| 163 | +} |
| 164 | + |
| 165 | +let msgLengthToBinary = ((ascii(data).length).toString(2).length); // 計算訊息有幾個字元再轉為二進位數字的長度 |
| 166 | +let BinarymsgLength = (ascii(data).length); // 訊息的二進位的長度 |
| 167 | + |
| 168 | +for(i = 0; i < 448 - 1 - BinarymsgLength; i++) {s += 0} // 前面補448 bit 的 0 |
| 169 | +for(i = 0; i < 64 - msgLengthToBinary ; i++) {s1 += 0} // 後面補64 bit 的 0 |
| 170 | + |
| 171 | +let msgLength = ascii(data).length; |
| 172 | +let frontPad = ascii(data) + 1 + s + s1 + (ascii(data).length).toString(2); |
| 173 | + |
| 174 | + |
| 175 | +let c0 = bigInt(bin2dec(frontPad)).toString(16); //涉及大數轉進位(MAX_SAFE_INTEGER) |
| 176 | + |
| 177 | + |
| 178 | +let initPadedValue = c0.substring(0, c0.length - 2) + msgLength.toString(16); //因為太大的數parseInt並轉二進位後面的數字會被省略掉,所以把它加上 |
| 179 | + |
| 180 | + |
| 181 | +let initPadedValueBuffer = Buffer.from(initPadedValue, 'hex'); |
| 182 | + |
| 183 | + this._update(initPadedValueBuffer) //傳入一開始pad好之buffer進行main loop hash function |
| 184 | + var hash = this._hash() //拼接main loop好的a-h八個區塊 |
| 185 | + |
| 186 | + return enc ? hash.toString(enc) : hash //轉為16進位 |
| 187 | +} |
| 188 | + |
| 189 | + |
| 190 | +function bin2dec(str){ //大數二進位在用parseInt轉十進位時後面常被省略,所以用此方法 |
| 191 | + var dec = str.toString().split(''), sum = [], hex = [], i, s |
| 192 | + while(dec.length){ |
| 193 | + s = 1 * dec.shift() |
| 194 | + for(i = 0; s || i < sum.length; i++){ |
| 195 | + s += (sum[i] || 0) * 2 |
| 196 | + sum[i] = s % 10 |
| 197 | + s = (s - sum[i]) / 10 |
| 198 | + } |
| 199 | + } |
| 200 | + while(sum.length){ |
| 201 | + hex.push(sum.pop().toString(10)) |
| 202 | + } |
| 203 | + return hex.join('') |
| 204 | +} |
| 205 | + |
| 206 | + |
| 207 | +var K = [ |
| 208 | + 0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, |
| 209 | + 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5, |
| 210 | + 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, |
| 211 | + 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, |
| 212 | + 0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC, |
| 213 | + 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA, |
| 214 | + 0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, |
| 215 | + 0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967, |
| 216 | + 0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13, |
| 217 | + 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85, |
| 218 | + 0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3, |
| 219 | + 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070, |
| 220 | + 0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5, |
| 221 | + 0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3, |
| 222 | + 0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208, |
| 223 | + 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2 |
| 224 | +] |
| 225 | + |
| 226 | +var W = new Array(64) |
| 227 | + |
| 228 | +function Sha256 () { |
| 229 | + this.init() |
| 230 | + |
| 231 | + this._w = W // new Array(64) |
| 232 | + |
| 233 | + Hash.call(this, 64, 56) |
| 234 | +} |
| 235 | + |
| 236 | +util.inherits(Sha256, Hash) |
| 237 | + |
| 238 | +Sha256.prototype.init = function () { |
| 239 | + this._a = 0x6a09e667 |
| 240 | + this._b = 0xbb67ae85 |
| 241 | + this._c = 0x3c6ef372 |
| 242 | + this._d = 0xa54ff53a |
| 243 | + this._e = 0x510e527f |
| 244 | + this._f = 0x9b05688c |
| 245 | + this._g = 0x1f83d9ab |
| 246 | + this._h = 0x5be0cd19 |
| 247 | + |
| 248 | + return this |
| 249 | +} |
| 250 | + |
| 251 | +function ch (x, y, z) { |
| 252 | + return z ^ (x & (y ^ z)) |
| 253 | +} |
| 254 | + |
| 255 | +function maj (x, y, z) { |
| 256 | + return (x & y) | (z & (x | y)) |
| 257 | +} |
| 258 | + |
| 259 | +function sigma0 (x) { |
| 260 | + return (x >>> 2 | x << 30) ^ (x >>> 13 | x << 19) ^ (x >>> 22 | x << 10) |
| 261 | +} |
| 262 | + |
| 263 | +function sigma1 (x) { |
| 264 | + return (x >>> 6 | x << 26) ^ (x >>> 11 | x << 21) ^ (x >>> 25 | x << 7) |
| 265 | +} |
| 266 | + |
| 267 | +function gamma0 (x) { |
| 268 | + return (x >>> 7 | x << 25) ^ (x >>> 18 | x << 14) ^ (x >>> 3) |
| 269 | +} |
| 270 | + |
| 271 | +function gamma1 (x) { |
| 272 | + return (x >>> 17 | x << 15) ^ (x >>> 19 | x << 13) ^ (x >>> 10) |
| 273 | +} |
| 274 | + |
| 275 | +Sha256.prototype._update = function (M) { |
| 276 | + var W = this._w |
| 277 | + |
| 278 | + var a = this._a | 0 |
| 279 | + var b = this._b | 0 |
| 280 | + var c = this._c | 0 |
| 281 | + var d = this._d | 0 |
| 282 | + var e = this._e | 0 |
| 283 | + var f = this._f | 0 |
| 284 | + var g = this._g | 0 |
| 285 | + var h = this._h | 0 |
| 286 | + |
| 287 | + for (var i = 0; i < 16; ++i) W[i] = M.readInt32BE(i * 4) |
| 288 | + for (; i < 64; ++i) W[i] = (gamma1(W[i - 2]) + W[i - 7] + gamma0(W[i - 15]) + W[i - 16]) | 0 |
| 289 | + |
| 290 | + for (var j = 0; j < 64; ++j) { |
| 291 | + var T1 = (h + sigma1(e) + ch(e, f, g) + K[j] + W[j]) | 0 |
| 292 | + var T2 = (sigma0(a) + maj(a, b, c)) | 0 |
| 293 | + |
| 294 | + h = g |
| 295 | + g = f |
| 296 | + f = e |
| 297 | + e = (d + T1) | 0 |
| 298 | + d = c |
| 299 | + c = b |
| 300 | + b = a |
| 301 | + a = (T1 + T2) | 0 |
| 302 | + } |
| 303 | + |
| 304 | + this._a = (a + this._a) | 0 |
| 305 | + this._b = (b + this._b) | 0 |
| 306 | + this._c = (c + this._c) | 0 |
| 307 | + this._d = (d + this._d) | 0 |
| 308 | + this._e = (e + this._e) | 0 |
| 309 | + this._f = (f + this._f) | 0 |
| 310 | + this._g = (g + this._g) | 0 |
| 311 | + this._h = (h + this._h) | 0 |
| 312 | +} |
| 313 | + |
| 314 | +Sha256.prototype._hash = function () { |
| 315 | + var H = new Buffer(32) |
| 316 | + |
| 317 | + H.writeInt32BE(this._a, 0) |
| 318 | + H.writeInt32BE(this._b, 4) |
| 319 | + H.writeInt32BE(this._c, 8) |
| 320 | + H.writeInt32BE(this._d, 12) |
| 321 | + H.writeInt32BE(this._e, 16) |
| 322 | + H.writeInt32BE(this._f, 20) |
| 323 | + H.writeInt32BE(this._g, 24) |
| 324 | + H.writeInt32BE(this._h, 28) |
| 325 | + |
| 326 | + return H |
| 327 | +} |
| 328 | + |
| 329 | +// 執行: |
| 330 | + |
| 331 | +let hashString = new Sha256().digest('test','hex'); |
| 332 | + |
| 333 | +console.log(hashString) |
| 334 | +``` |
| 335 | + |
| 336 | +使用 Node.js 的 crypto 模組進行驗證,可以得到相同的雜湊值。 |
| 337 | + |
| 338 | +```js |
| 339 | +const crypto = require('crypto'); |
| 340 | +let result = crypto.createHash('sha256').update('test').digest('hex') |
| 341 | +console.log(result) |
| 342 | +``` |
| 343 | + |
| 344 | + |
| 345 | + |
| 346 | +參考資料: |
| 347 | + |
| 348 | +> [http://www.iwar.org.uk/comsec/resources/cipher/sha256-384-512.pdf](http://www.iwar.org.uk/comsec/resources/cipher/sha256-384-512.pdf) |
| 349 | +
|
| 350 | +# |
| 351 | + |
| 352 | + |
| 353 | + |
0 commit comments