Bài này chúng ta sẽ tìm hiểu về thuật toán hash, mà cụ thể là thuật toán Hash SHA-256.
Hash function (hàm băm) là gì?
Hash function (hàm băm) hiểu đơn giản là một hàm có khả năng băm nát data nếu chúng ta đưa data vào cho nó :)).
Đặc điểm của hash function đó là với cùng 1 đầu vào, thì nó sẽ băm ra đúng 1 kết quả duy nhất.
Ví dụ : Đầu vào của chúng ta là "cuonglv" thì hàm băm sẽ băm ra thành kết quả: "431ebbdd7efdcedc28b3ace1ec7b19302a834c9085f943d2af3036361b155e7d"
Hash function có một đặc điểm đó nữa là từ output sau khi bị băm xong, thì ko có cách nào truy ngược lại input được hết.
Ví dụ : từ chuỗi "431ebbdd7efdcedc28b3ace1ec7b19302a834c9085f943d2af3036361b155e7d" không có cách nào bạn truy ngược được về input ban đầu là "cuonglv" được cả.
Theo mình thì chủ yếu người đọc blog của mình đều là dân lập trình cả, nên giới thiệu hàm băm tới đây mình nghĩ có lẽ vậy là đã đủ rồi =)) Vì chắc chắn anh em lập trình ít nhiều cũng xài hash 1 lần nào đó rồi.
Vậy nên phần tiếp theo của bài này sẽ nói sâu về 1 hàm băm có tên là SHA-256.
SHA-256 là gì?
Đơn giản SHA-256 cũng là 1 hash function, vậy nên nó cũng chứa trong nó 2 đặc tính của hash fuction ở trên. SHA-256 này nó được cục an ninh quốc gia Mỹ tạo ra, nên độ đẳng cấp của nó còn cao hơn, đó là nó có thêm 1 đặc tính nữa: việc tìm ra chuỗi input thứ 2 để cho ra cùng 1 output là điều bất khả thi.
Ví dụ: "cuonglv" khi cho vào SHA-256 sẽ cho ra kết quả : "431ebbdd7efdcedc28b3ace1ec7b19302a834c9085f943d2af3036361b155e7d". Và để tìm được 1 cái input nào đó khác "cuonglv" mà cũng cho ra được "431ebbdd7efdcedc28b3ace1ec7b19302a834c9085f943d2af3036361b155e7d" là điều bất khả thi.
Tất nhiên trên lý thuyết là được, nhưng thực tế chưa ai làm được thôi. =)) Tại sao lý thuyết lại có việc 2 input ra cùng 1 output được. Thì đơn giản là vầy, cái hàm SHA-256 này, dù bạn có đưa input ngắn tới đâu (1 kí tự "a") hay bạn có đưa input dài thòng lòng như nội dung của 1 cuốn sách, thì hàm băm này cũng cho ra 1 kết quả dài 64 kí tự. Suy ra là chắc chắn sẽ có trường hợp 2 input cho ra cùng 1 kết quả giống nhau rồi. Nhưng mà để tìm được 2 chuỗi này gần như là bất khả thi.
Ok, luyên thuyên đủ rồi. Bây giờ chúng ta sẽ đi vào tìm hiểu thuật giải của mã băm này, xem đội ngũ của cục an ninh quốc gia Mỹ nghĩ ra được 1 thuật toán như nào, có quá xịn sò hay ko.
Thuật toán SHA-256
Constants
K constant : bao gồm 64 giá trị được tạo nên bằng cách sau.
function isPrime(num) { for(var i = 2; i < num; i++) if(num % i === 0) return false; return num > 1; } var k = []; var i = 0; for(var j=2; i < 64; j++) { if(isPrime(j)) { k[i] = Math.floor(Math.cbrt(j)%1*(Math.pow(2,32))); i++; } } k
Để dễ nhìn thì mình thấy người ta chủ yếu chuyển nó về hex, và do đó mình cũng học theo người ta đưa nó về dạng hex =))[1116352408, 1899447441, 3049323471, 3921009573, 961987163, 1508970993, 2453635748, 2870763221, 3624381080, 310598401, 607225278, 1426881987, 1925078388, 2162078206, 2614888103, 3248222580, 3835390401, 4022224774, 264347078, 604807628, 770255983, 1249150122, 1555081692, 1996064986, 2554220882, 2821834349, 2952996808, 3210313671, 3336571891, 3584528711, 113926993, 338241895, 666307205, 773529912, 1294757372, 1396182291, 1695183700, 1986661051, 2177026350, 2456956037, 2730485921, 2820302411, 3259730800, 3345764771, 3516065817, 3600352804, 4094571909, 275423344, 430227734, 506948616, 659060556, 883997877, 958139571, 1322822218, 1537002063, 1747873779, 1955562222, 2024104815, 2227730452, 2361852424, 2428436474, 2756734187, 3204031479, 3329325298]
['0x428a2f98', '0x71374491', '0xb5c0fbcf', '0xe9b5dba5', '0x3956c25b', '0x59f111f1', '0x923f82a4', '0xab1c5ed5', '0xd807aa98', '0x12835b01', '0x243185be', '0x550c7dc3', '0x72be5d74', '0x80deb1fe', '0x9bdc06a7', '0xc19bf174', '0xe49b69c1', '0xefbe4786', '0xfc19dc6', '0x240ca1cc', '0x2de92c6f', '0x4a7484aa', '0x5cb0a9dc', '0x76f988da', '0x983e5152', '0xa831c66d', '0xb00327c8', '0xbf597fc7', '0xc6e00bf3', '0xd5a79147', '0x6ca6351', '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']
Initial Hash : bao gồm 8 giá trị được khởi tạo như sau
function isPrime(num) { for(var i = 2; i < num; i++) if(num % i === 0) return false; return num > 1; } var k = []; var i = 0; for(var j=2; i < 8; j++) { if(isPrime(j)) { k[i] = Math.floor(Math.sqrt(j)%1*(Math.pow(2,32))); i++; } } k
Mình sẽ chuyển nó về dạng hex là:[1779033703, 3144134277, 1013904242, 2773480762, 1359893119, 2600822924, 528734635, 1541459225]
['0x6a09e667', '0xbb67ae85', '0x3c6ef372', '0xa54ff53a', '0x510e527f', '0x9b05688c', '0x1f83d9ab', '0x5be0cd19']
Operations:
Phép dịch phải (Shift right)
Phép đảo phải (Rotate right)
function rightRotate(value, amount) { return (value>>>amount) | (value<<(32 - amount)); }
Phép XOR:
Functions:
Hàm Gamma 0:
Hàm Gamma 1:
Hàm Sigma 0:
Hàm Sigma 1:
Hàm choice
function Choice(x, y, z) { return ((x & y) ^ ((~x) & z)); }
Hàm Majority
function Majority(x, y, z) { return ((x & y) ^ (x & z) ^ (y & z)); }
Quỳ trình băm
Khởi tạo chuỗi binary
Xử lý block
Sau khi tạo được chuổi binary, chúng ta sẽ chia nó ra thành các block, mỗi block này dài 512 bit. Trong trường hợp này, vì độ dài chuỗi của chúng ta chỉ có 512 bit, nên chúng ta chỉ có 1 block duy nhất.W1 : 01100011011101010110111101101110 W2 : 01100111011011000111011010000000 W3 : 00000000000000000000000000000000 W4 : 00000000000000000000000000000000 W5 : 00000000000000000000000000000000 W6 : 00000000000000000000000000000000 W7 : 00000000000000000000000000000000 W8 : 00000000000000000000000000000000 W9 : 00000000000000000000000000000000 W10 : 00000000000000000000000000000000 W11 : 00000000000000000000000000000000 W12 : 00000000000000000000000000000000 W13 : 00000000000000000000000000000000 W14 : 00000000000000000000000000000000 W15 : 00000000000000000000000000000000 W16 : 00000000000000000000000000111000
W[n] = W[n-16] + gamma0(W[n-15]) + W[n-7] + gamma1(W[n-2])
Compression
h1: 01101010000010011110011001100111 h2: 10111011011001111010111010000101 h3: 00111100011011101111001101110010 h4: 10100101010011111111010100111010 h5: 01010001000011100101001001111111 h6: 10011011000001010110100010001100 h7: 00011111100000111101100110101011 h8: 01011011111000001100110100011001
T1 = Sigma1(H5) + Choice(H5 + H6 + H7) + H8 + K1 + W1
T2 = Sigma0(H1) + Majority(H1, H2, H3);
h1: h2: 01101010000010011110011001100111 h3: 10111011011001111010111010000101 h4: 00111100011011101111001101110010 h5: 10100101010011111111010100111010 h6: 01010001000011100101001001111111 h7: 10011011000001010110100010001100 h8: 00011111100000111101100110101011
h1: 01011111011111011111011110111011 h2: 01101010000010011110011001100111 h3: 10111011011001111010111010000101 h4: 00111100011011101111001101110010 h5: 11111100001111010101001000010000 h6: 01010001000011100101001001111111 h7: 10011011000001010110100010001100 h8: 00011111100000111101100110101011
h1: 01101010000010011110011001100111 h2: 10111011011001111010111010000101 h3: 00111100011011101111001101110010 h4: 10100101010011111111010100111010 h5: 01010001000011100101001001111111 h6: 10011011000001010110100010001100 h7: 00011111100000111101100110101011 h8: 01011011111000001100110100011001
h1: 01000011000111101011101111011101 h2: 01111110111111011100111011011100 h3: 00101000101100111010110011100001 h4: 11101100011110110001100100110000 h5: 00101010100000110100110010010000 h6: 10000101111110010100001111010010 h7: 10101111001100000011011000110110 h8: 00011011000101010101111001111101
h1 : 431ebbdd h2 : 7efdcedc h3 : 28b3ace1 h4 : ec7b1930 h5 : 2a834c90 h6 : 85f943d2 h7 : af303636 h8 : 1b155e7d