|
| 1 | +// sha256.go |
| 2 | +// description: The sha256 cryptographic hash function as defined in the RFC6234 standard. |
| 3 | +// author: [Paul Leydier] (https://github.com/paul-leydier) |
| 4 | +// ref: https://datatracker.ietf.org/doc/html/rfc6234 |
| 5 | +// ref: https://en.wikipedia.org/wiki/SHA-2 |
| 6 | +// see sha256_test.go |
| 7 | + |
| 8 | +package sha256 |
| 9 | + |
| 10 | +import ( |
| 11 | + "encoding/binary" // Used for interacting with uint at the byte level |
| 12 | + "math/bits" // Used for bits rotation operations |
| 13 | +) |
| 14 | + |
| 15 | +var K = [64]uint32{ |
| 16 | + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, |
| 17 | + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, |
| 18 | + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, |
| 19 | + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, |
| 20 | + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, |
| 21 | + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, |
| 22 | + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, |
| 23 | + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, |
| 24 | +} |
| 25 | + |
| 26 | +const chunkSize = 64 |
| 27 | + |
| 28 | +// pad returns a padded version of the input message, such as the padded message's length is a multiple |
| 29 | +// of 512 bits. |
| 30 | +// The padding methodology is as follows: |
| 31 | +// A "1" bit is appended at the end of the input message, followed by m "0" bits such as the length is |
| 32 | +// 64 bits short of a 512 bits multiple. The remaining 64 bits are filled with the initial length of the |
| 33 | +// message, represented as a 64-bits unsigned integer. |
| 34 | +// For more details, see: https://datatracker.ietf.org/doc/html/rfc6234#section-4.1 |
| 35 | +func pad(message []byte) []byte { |
| 36 | + L := make([]byte, 8) |
| 37 | + binary.BigEndian.PutUint64(L, uint64(len(message)*8)) |
| 38 | + message = append(message, 0x80) // "1" bit followed by 7 "0" bits |
| 39 | + for (len(message)+8)%64 != 0 { |
| 40 | + message = append(message, 0x00) // 8 "0" bits |
| 41 | + } |
| 42 | + message = append(message, L...) |
| 43 | + |
| 44 | + return message |
| 45 | +} |
| 46 | + |
| 47 | +// Hash hashes the input message using the sha256 hashing function, and return a 32 byte array. |
| 48 | +// The implementation follows the RGC6234 standard, which is documented |
| 49 | +// at https://datatracker.ietf.org/doc/html/rfc6234 |
| 50 | +func Hash(message []byte) [32]byte { |
| 51 | + message = pad(message) |
| 52 | + |
| 53 | + // Initialize round constants |
| 54 | + h0, h1, h2, h3, h4, h5, h6, h7 := uint32(0x6a09e667), uint32(0xbb67ae85), uint32(0x3c6ef372), uint32(0xa54ff53a), |
| 55 | + uint32(0x510e527f), uint32(0x9b05688c), uint32(0x1f83d9ab), uint32(0x5be0cd19) |
| 56 | + |
| 57 | + // Iterate through 512-bit chunks |
| 58 | + for chunkStart := 0; chunkStart < len(message); chunkStart += chunkSize { |
| 59 | + // Message schedule |
| 60 | + var w [64]uint32 |
| 61 | + for i := 0; i*4 < chunkSize; i++ { |
| 62 | + w[i] = binary.BigEndian.Uint32(message[chunkStart+i*4 : chunkStart+(i+1)*4]) |
| 63 | + } |
| 64 | + |
| 65 | + // Extend the 16 bytes chunk to the whole 64 bytes message schedule |
| 66 | + for i := 16; i < 64; i++ { |
| 67 | + s0 := bits.RotateLeft32(w[i-15], -7) ^ bits.RotateLeft32(w[i-15], -18) ^ (w[i-15] >> 3) |
| 68 | + s1 := bits.RotateLeft32(w[i-2], -17) ^ bits.RotateLeft32(w[i-2], -19) ^ (w[i-2] >> 10) |
| 69 | + w[i] = w[i-16] + s0 + w[i-7] + s1 |
| 70 | + } |
| 71 | + |
| 72 | + // Actual hashing loop |
| 73 | + a, b, c, d, e, f, g, h := h0, h1, h2, h3, h4, h5, h6, h7 |
| 74 | + for i := 0; i < 64; i++ { |
| 75 | + S1 := bits.RotateLeft32(e, -6) ^ bits.RotateLeft32(e, -11) ^ bits.RotateLeft32(e, -25) |
| 76 | + ch := (e & f) ^ ((^e) & g) |
| 77 | + tmp1 := h + S1 + ch + K[i] + w[i] |
| 78 | + S0 := bits.RotateLeft32(a, -2) ^ bits.RotateLeft32(a, -13) ^ bits.RotateLeft32(a, -22) |
| 79 | + maj := (a & b) ^ (a & c) ^ (b & c) |
| 80 | + tmp2 := S0 + maj |
| 81 | + h = g |
| 82 | + g = f |
| 83 | + f = e |
| 84 | + e = d + tmp1 |
| 85 | + d = c |
| 86 | + c = b |
| 87 | + b = a |
| 88 | + a = tmp1 + tmp2 |
| 89 | + } |
| 90 | + h0 += a |
| 91 | + h1 += b |
| 92 | + h2 += c |
| 93 | + h3 += d |
| 94 | + h4 += e |
| 95 | + h5 += f |
| 96 | + h6 += g |
| 97 | + h7 += h |
| 98 | + } |
| 99 | + |
| 100 | + // Export digest |
| 101 | + digest := [32]byte{} |
| 102 | + binary.BigEndian.PutUint32(digest[:4], h0) |
| 103 | + binary.BigEndian.PutUint32(digest[4:8], h1) |
| 104 | + binary.BigEndian.PutUint32(digest[8:12], h2) |
| 105 | + binary.BigEndian.PutUint32(digest[12:16], h3) |
| 106 | + binary.BigEndian.PutUint32(digest[16:20], h4) |
| 107 | + binary.BigEndian.PutUint32(digest[20:24], h5) |
| 108 | + binary.BigEndian.PutUint32(digest[24:28], h6) |
| 109 | + binary.BigEndian.PutUint32(digest[28:], h7) |
| 110 | + |
| 111 | + return digest |
| 112 | +} |
0 commit comments