組內技術分享的內容,目前網上相關資料不少,但讀起來都不太合本身的習慣,因而本身整理並編寫一篇簡潔並便於(本身)理解和分享的文章。
由於以前對密碼學沒有專門研究,本身的體會或理解會特別標註爲「我的理解」,請注意甄別,若有必要能夠自行查證。
閱讀前須要樹立一種觀點:大部分場景都是基於機率的大小而言的,好比SHA256安全性、區塊鏈不可更改性等。html
區塊鏈的基礎算法之一,在其中用於區塊hash計算方法。算法
是SHA-2下的一個算法標準,而SHA-2全稱安全散列算法2,即Secure Hash Algorithm 2,屬於SHA算法之一,是SHA-1的後繼者,一種密碼散列函數算法標準,由美國國家安全局研發,由美國國家標準與技術研究院(NIST)在2001年發佈。其下一共分六個不一樣的算法標準:SHA-22四、SHA-25六、SHA-38四、SHA-5十二、SHA-512/22四、SHA-512/256。數據庫
散列(hash)函數須要知足如下性質纔是安全的:
1. 不能從散列結果獲得原始信息,也就是密碼學中的原像問題。
2. 不能出現兩個不一樣的原始信息,可是hash結果同樣,即碰撞問題。
3. 最好由原信息的微小變更能讓hash結果面目全非,即雪崩效應。安全
SHA-256的壓縮函數知足Merkle-Damgard結構,此時能夠保證壓縮函數在知足散列函數安全性的前提下,在分組後作壓縮時仍能保證。關於Merkle-Damgard結構,能夠參考《密碼學原理與實踐》第四章及文末參考資料[2]。
// 全部變量均爲無符號32位整型,計算時以2^32爲模 // 1. 變量初始化 // 1.1 初始化變量第一部分:前8個質數(2, 3, 5 ... 19)的平方根的前32位 h0 := 0x6a09e667 h1 := 0xbb67ae85 h2 := 0x3c6ef372 h3 := 0xa54ff53a h4 := 0x510e527f h5 := 0x9b05688c h6 := 0x1f83d9ab h7 := 0x5be0cd19 // 1.2 初始化變量第二部分:前64個質數(2, 3, ..., 311)的立方根的前32位 k[0..63] := 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 // 2. 預處理 // 2.1 給原始消息m末尾增長(接續)一個二進制1 m0 = add1bit(m) // 2.2 給m0末尾增長連續k個0,直到其長度對 512取模後餘數爲448 // k >= 0 m1 = add0bitsWhenMod512Equals448(m0) // 2.3 給m1末尾增長64bit表示的m1的長度, 以大端表示 m2 = addLengthBigEndian(m1) // 3. 將m1拆分紅l個以512bit爲長度的塊 sub_ms[l] = splitBy512bit(m1) // 3.1 子塊處理 for(sub_m : sub_ms ) { // 對每一個512bit的子塊,進一步拆成16個32bit的子塊 w[0...15] = splitBy32bit(sub_m) for(i from 16 to 63) { // 計算w[16]到w[63] //rightrotate 循環右移x位, rightshift右移x位高位補0 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 } // 局部變量初始化 a := h0 b := h1 c := h2 d := h3 e := h4 f := h5 g := h6 h := h7 // 子塊內主循環 for i from 0 to 63 { s0 := (a rightrotate 2) xor (a rightrotate 13) xor(a rightrotate 22) maj := (a and b) xor (a and c) xor(b and c) t2 := s0 + maj s1 := (e rightrotate 6) xor (e rightrotate 11) xor(e rightrotate 25) ch := (e and f) xor ((not e) and g) t1 := h + s1 + ch + k[i] + w[i] h := g g := f f := e e := d + t1 d := c c := b b := a a := t1 + t2 } // 子塊結果更新到全局變量 h0 := h0 + a h1 := h1 + b h2 := h2 + c h3 := h3 + d h4 := h4 + e h5 := h5 + f h6 := h6 + g h7 := h7 + h } // 3.2 最終的摘要結果計算, append即位拼接 digest = hash = h0 append h1 append h2 append h3 append h4 append h5 append h6 append h7
去中心的分佈式數據庫。數據結構
僞碼以下app
// 區塊與前個區塊相連。 /** * 區塊 */ class Block { /** 區塊頭 */ BlockHead head; /** 區塊體 */ BlockBody body; } /** * 區塊頭 */ class BlockHead { /** 生成時間 */ Date gmtGenerate; /** 本區塊體的hash值 */ String bodyHashCode; /** 上個區塊的hash值 */ String prevHashCode; /** 難度係數 */ int difficulty; /** 隨機數, 32位*/ int nonce; } /** * 區塊體 */ class BlockBody { /** 數據 */ byte[] data; }
區塊的hash=SHA256(區塊頭),區塊頭包含上一個區塊hash及本區塊體的hash。所以,當前區塊(不管區塊頭和區塊體)或上一個區塊發生變化,都會致使區塊的hash變化。這個關係能夠用公式表達爲:分佈式
blockHash = SHA256(block.header) = SHA256(prevBlockHash + block.body + other)
所以,修改一個區塊會致使斷鏈,必須連鎖地修改後續全部區塊。因爲後面會提到計算hash很慢的緣由,幾乎不可能,除非擁有全網51%計算能力——區塊鏈所以得名。函數
*__我的理解__爲何是全網51%計算的能力?即在全網中全部計算能力都在挖礦時,修改的速度超過建立的速度。假設當前全網只有1%的計算能力在挖礦,那麼你只要這1%的51%——全網的0.51%足矣。區塊鏈
全部節點都會建立新的區塊。爲了保證節點同步,區塊添加的速度不能太快。同步的速度由發明者中本聰設置爲平均10分鐘1次,即1小時6個。控制速度的方式是,建立區塊須要大量的計算才能得到當前塊的hash,此後才能添加新的塊。這個海量計算過程相似於從大量的沙子中獲取有用的一粒金子,所以被比喻爲「挖礦」。可是「金子」並不徹底是指這粒「合適的hash」,下文會提到。3d
只有符合要求的hash才被區塊連接受,這個要求是:hash < 常量targetMax / 當前區塊difficulty。
大部分計算的hash是不符合條件的。爲了使hash發生變化,須要改動區塊頭。爲了讓區塊頭產生變化,中本聰在區塊頭增長了隨機值Nonce。從0開始計算到2^32位找到Nonce具體值的過程,就是挖礦的過程,所以Nonce和對應的hash合起來纔是金子,而計算得到的hash只是須要作進一步確認金子的原石。
存在全部值都不是符合要求的Nonce的狀況,此時協議容許礦工改變區塊體,從新計算。
固然也存在符合條件的hash值有多個的狀況,爲了下降這種狀況的機率,能夠調節difficulty值。
根據當前的建立速度,能夠動態地更改difficulty的大小來調節,確保近似平均10分鐘1次。隨着運算能力提高,difficulty會愈來愈大,致使挖礦難度愈來愈高。
若是同時提交兩個區塊,二者鏈接了同一個區塊,那麼區塊鏈採用哪個?
新節點老是採用最長的區塊鏈;哪一個分支先達到6個新區塊(「6次確認」),區塊鏈就會採用那條鏈。6次確認只須要1個小時就會完成。可見,取決於哪條分支擁有更多的計算速度。
被丟棄的短鏈,相關計算是徹底沒有價值的。在比特幣中是這樣,以太坊則會受到必定的補償。
8年的持續運行證明了其可行性。可是代價是:10分鐘同步一次;大量無心義計算。所以使用場景有限:
目前的應用:比特幣;電子證書等。