Skip to content

Commit acb9405

Browse files
author
Easonwang01
committed
Add SHA
1 parent 5664b55 commit acb9405

File tree

2 files changed

+354
-1
lines changed

2 files changed

+354
-1
lines changed

1.2 密碼雜湊.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ hash的過程會加入一個隨機的salt,然後salt跟password一起hash。
1212

1313
以下為在2GHz core上的耗費時間表:
1414

15-
![](blob:https://legacy.gitbook.com/78af164f-81c6-4ae3-b0f8-48c70df5cb18)
15+
![](https://easonwang01.gitbooks.io/blockchain-and-cryptography/content/assets/螢幕快照 2018-01-28 下午12.06.21.png)
1616

1717
再來你可能會想既然每次產生的Hash不同,那要怎麼進行訊息驗證呢?
1818

1.3 SHA原理.md

Lines changed: 353 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,353 @@
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+
![](https://easonwang01.gitbooks.io/blockchain-and-cryptography/content/assets/螢幕快照 2018-02-01 上午9.01.01.png)
18+
19+
### 第二步:
20+
21+
寫出八個 initial hash value(此為被預先定義之固定值)
22+
23+
#### ![](https://easonwang01.gitbooks.io/blockchain-and-cryptography/content/assets/螢幕快照 2018-02-01 上午9.05.43.png)
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+
![](https://easonwang01.gitbooks.io/blockchain-and-cryptography/content/assets/螢幕快照 2018-02-01 上午9.07.07.png)**原理: **
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+
![](https://easonwang01.gitbooks.io/blockchain-and-cryptography/content/assets/螢幕快照 2018-02-01 上午9.08.21.png)
92+
93+
其中符號定義如下
94+
95+
![](https://easonwang01.gitbooks.io/blockchain-and-cryptography/content/assets/螢幕快照 2018-02-01 上午9.09.22.png)
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

Comments
 (0)