# SHA256

為單向加密HASH，不可逆

## 範例:

```javascript
var crypto = require('crypto');
let msg = 'test';
var hash = crypto.createHash('sha256').update(msg).digest('base64');
console.log(hash);
```

## 原理:

### #第一步:

```
把msg轉為二進位的數字msgBit，之後後面先放一個數字1然後pad補k個0 直到msgBit.length + 1 + k % 512 = 448
```

然後最後面再加上64bits的數字，內容為msg的長度(以二進位表示) ex:abc之ASCII二進位長度為24，

所以轉為64bit二進位顯示為0....11000

(以ABC為例子)

![](https://1775491709-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M0u4DAKHJCUBld9DYCi%2F-M0u4Ft9n4JCnniIVQ-O%2F-M0u4_-VHlLEPVmy29SF%2F52542.png?generation=1582596282233684\&alt=media)

### #第二步:

寫出八個initial hash value(此為固定值)

![](https://1775491709-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M0u4DAKHJCUBld9DYCi%2F-M0u4Ft9n4JCnniIVQ-O%2F-M0u4_-XMI_hpX2vzEI7%2F755.png?generation=1582596282120225\&alt=media)

```
原理: 
python:  hex(int(math.modf(math.sqrt(2))[0]*(1<<32)))     
js: 先取math.sqrt(2)的小數，然後乘上2**32，之後轉為16進位，然後取小數前面部分  ex:(0.4142135623730951*(2**32)).toString(16)




H(0)
1 = 6a09e667            


H(0)
2 = bb67ae85

先進入到python命令列，然後輸入import math
hex(int(math.modf(math.sqrt(3))[0]*(1<<32)))

H(0)
3 = 3c6ef372

hex(int(math.modf(math.sqrt(5))[0]*(1<<32)))

H(0)
4 = a54ff53a

hex(int(math.modf(math.sqrt(7))[0]*(1<<32)))

H(0)
5 = 510e527f

hex(int(math.modf(math.sqrt(11))[0]*(1<<32)))

H(0)
6 = 9b05688c

hex(int(math.modf(math.sqrt(13))[0]*(1<<32)))



H(0)
7 = 1f83d9ab

hex(int(math.modf(math.sqrt(17))[0]*(1<<32)))



H(0)
8 = 5be0cd19

hex(int(math.modf(math.sqrt(19))[0]*(1<<32)))


之後可以發現上面八個initial number中math.sqrt()都是取首八個質數
```

> 附註:1<<32在python和js行為會不同，可參考<https://stackoverflow.com/questions/45024682/different-behavior-for-1-32-compare-javascript-to-python>

## #第三步

64個固定k值

![](https://1775491709-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M0u4DAKHJCUBld9DYCi%2F-M0u4Ft9n4JCnniIVQ-O%2F-M0u4_-ZTCnfgbQtFtVd%2F4587.png?generation=1582596281649016\&alt=media)

```
原理: 用math cube root的方法找首64個質數

以下用Python為例:

(先定義cube方法，意思即為開三次方根，js可直接用Math.cbrt())
def cube(x):
    if 0<=x: return x**(1./3.)
    return -(-x)**(1./3.)


-------------------------------------------------------------    

 hex(int(math.modf(cube(2))[0]*(1<<32)))       //0x428a2f98

 hex(int(math.modf(cube(3))[0]*(1<<32)))       //0x71374491

 ...
```

## #第四步:

使用sha256定義的六個hash function

![](https://1775491709-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M0u4DAKHJCUBld9DYCi%2F-M0u4Ft9n4JCnniIVQ-O%2F-M0u4_-aKrsUBGJKeVkQ%2F74.png?generation=1582596282099204\&alt=media)

其中符號定義如下

![](https://1775491709-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M0u4DAKHJCUBld9DYCi%2F-M0u4Ft9n4JCnniIVQ-O%2F-M0u4_-cj5XrNL8uhADU%2F7857.png?generation=1582596282073839\&alt=media)

寫成程式為:

```javascript
function ch (x, y, z) {
  return z ^ (x & (y ^ z))
}

function maj (x, y, z) {
  return (x & y) | (z & (x | y))
}

function sigma0 (x) {
  return (x >>> 2 | x << 30) ^ (x >>> 13 | x << 19) ^ (x >>> 22 | x << 10)
}

function sigma1 (x) {
  return (x >>> 6 | x << 26) ^ (x >>> 11 | x << 21) ^ (x >>> 25 | x << 7)
}

function gamma0 (x) {
  return (x >>> 7 | x << 25) ^ (x >>> 18 | x << 14) ^ (x >>> 3)
}

function gamma1 (x) {
  return (x >>> 17 | x << 15) ^ (x >>> 19 | x << 13) ^ (x >>> 10)
}
```

## #完整範例

```javascript
const util = require('util');
var bigInt = require("big-integer");


function Hash (blockSize, finalSize) {
  this._block = new Buffer(blockSize)
  this._finalSize = finalSize
  this._blockSize = blockSize
  this._len = 0
}

Hash.prototype.digest = function (data, enc) {

data = (data).toString(); 
//////////////////
let s = ""
let s1 = ""

//將字串轉為二進位ASCII
function ascii (a) { 
  let dd = "";
  for(let i = 0 ; i< a.length; i++) {
    let charAscII = a.charAt(i).charCodeAt(0).toString(2)
    while(charAscII.length < 8) { //最前面的0都會被省略，所以手動加上
      charAscII = 0 + charAscII
    }
    dd += charAscII; 
  }
  return dd
}

let msgLengthToBinary = ((ascii(data).length).toString(2).length); //計算訊息有幾個字元再轉為二進位數字的長度
let BinarymsgLength = (ascii(data).length); //訊息的二進位的長度

for(i = 0; i < 448 - 1 - BinarymsgLength; i++) {s += 0} //前面補448bit 0
for(i = 0; i < 64 - msgLengthToBinary ; i++) {s1 += 0} //後面補64 bit 0

let msgLength = ascii(data).length;
let frontPad = ascii(data) + 1 + s + s1 + (ascii(data).length).toString(2);


let c0 = bigInt(bin2dec(frontPad)).toString(16);  //涉及大數轉進位(MAX_SAFE_INTEGER)


let initPadedValue = c0.substring(0, c0.length - 2) + msgLength.toString(16); //因為太大的數parseInt並轉二進位後面的數字會被省略掉，所以把它加上


let initPadedValueBuffer = Buffer.from(initPadedValue, 'hex');

  this._update(initPadedValueBuffer) //傳入一開始pad好之buffer進行main loop hash function
  var hash = this._hash() //拼接main loop好的a-h八個區塊

  return enc ? hash.toString(enc) : hash  //轉為16進位
}


function bin2dec(str){ //大數二進位在用parseInt轉十進位時後面常被省略，所以用此方法
    var dec = str.toString().split(''), sum = [], hex = [], i, s
    while(dec.length){
        s = 1 * dec.shift()
        for(i = 0; s || i < sum.length; i++){
            s += (sum[i] || 0) * 2
            sum[i] = s % 10
            s = (s - sum[i]) / 10
        }
    }
    while(sum.length){
        hex.push(sum.pop().toString(10))
    }
    return hex.join('')
}


var 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
]

var W = new Array(64)

function Sha256 () {
  this.init()

  this._w = W // new Array(64)

  Hash.call(this, 64, 56)
}

util.inherits(Sha256, Hash)

Sha256.prototype.init = function () {
  this._a = 0x6a09e667
  this._b = 0xbb67ae85
  this._c = 0x3c6ef372
  this._d = 0xa54ff53a
  this._e = 0x510e527f
  this._f = 0x9b05688c
  this._g = 0x1f83d9ab
  this._h = 0x5be0cd19

  return this
}

function ch (x, y, z) {
  return z ^ (x & (y ^ z))
}

function maj (x, y, z) {
  return (x & y) | (z & (x | y))
}

function sigma0 (x) {
  return (x >>> 2 | x << 30) ^ (x >>> 13 | x << 19) ^ (x >>> 22 | x << 10)
}

function sigma1 (x) {
  return (x >>> 6 | x << 26) ^ (x >>> 11 | x << 21) ^ (x >>> 25 | x << 7)
}

function gamma0 (x) {
  return (x >>> 7 | x << 25) ^ (x >>> 18 | x << 14) ^ (x >>> 3)
}

function gamma1 (x) {
  return (x >>> 17 | x << 15) ^ (x >>> 19 | x << 13) ^ (x >>> 10)
}

Sha256.prototype._update = function (M) {
  var W = this._w

  var a = this._a | 0
  var b = this._b | 0
  var c = this._c | 0
  var d = this._d | 0
  var e = this._e | 0
  var f = this._f | 0
  var g = this._g | 0
  var h = this._h | 0

  for (var i = 0; i < 16; ++i) W[i] = M.readInt32BE(i * 4)
  for (; i < 64; ++i) W[i] = (gamma1(W[i - 2]) + W[i - 7] + gamma0(W[i - 15]) + W[i - 16]) | 0

  for (var j = 0; j < 64; ++j) {
    var T1 = (h + sigma1(e) + ch(e, f, g) + K[j] + W[j]) | 0
    var T2 = (sigma0(a) + maj(a, b, c)) | 0

    h = g
    g = f
    f = e
    e = (d + T1) | 0
    d = c
    c = b
    b = a
    a = (T1 + T2) | 0
  }

  this._a = (a + this._a) | 0
  this._b = (b + this._b) | 0
  this._c = (c + this._c) | 0
  this._d = (d + this._d) | 0
  this._e = (e + this._e) | 0
  this._f = (f + this._f) | 0
  this._g = (g + this._g) | 0
  this._h = (h + this._h) | 0
}

Sha256.prototype._hash = function () {
  var H = new Buffer(32)

  H.writeInt32BE(this._a, 0)
  H.writeInt32BE(this._b, 4)
  H.writeInt32BE(this._c, 8)
  H.writeInt32BE(this._d, 12)
  H.writeInt32BE(this._e, 16)
  H.writeInt32BE(this._f, 20)
  H.writeInt32BE(this._g, 24)
  H.writeInt32BE(this._h, 28)

  return H
}

//範例:

let hashString = new Sha256().digest('1234567','hex');

console.log(hashString)
```

也可用Node.js的crypto模組

```
var crypto = require('crypto');
let a = crypto.createHash('sha256').update('1234567').digest()
console.log(a.toString('hex'))
```

Reference:

> <https://github.com/peterolson/BigInteger.js>(大數轉換無法用在parseInt，toString())
>
> <http://www.danvk.org/hex2dec.html>
>
> <https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER>
>
> <https://stackoverflow.com/questions/18626844/convert-a-large-integer-to-a-hex-string-in-javascript> (其解答dec2hex在數字更大時還是無法解決問題，但可做為參考)
>
> Python內建BigInteger所以如用Python則不會有問題


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://easonwang.gitbook.io/crypto/sha256.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
