Skip to content

Commit 068ea4b

Browse files
authored
feat: add hashing/sha256 implementation (TheAlgorithms#447)
* feat: new hashing package * feat: sha256 hashing function * test: sha256 hashing test cases and benchmarks * doc: improve sha256 docs
1 parent 385fe84 commit 068ea4b

File tree

4 files changed

+165
-0
lines changed

4 files changed

+165
-0
lines changed

hashing/doc.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// Package hashing containing different implementation of certain hashing
2+
package hashing

hashing/hashing_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// Empty test file to keep track of all the tests for the algorithms.
2+
3+
package hashing

hashing/sha256/sha256.go

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
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+
}

hashing/sha256/sha256_test.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package sha256
2+
3+
import (
4+
"encoding/hex"
5+
"testing"
6+
)
7+
8+
func TestHash(t *testing.T) {
9+
testCases := []struct {
10+
in string
11+
expected string
12+
}{
13+
{"hello world", "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"},
14+
{"", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"},
15+
{"a", "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb"},
16+
{"The quick brown fox jumps over the lazy dog", "d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592"},
17+
{"The quick brown fox jumps over the lazy dog.", "ef537f25c895bfa782526529a9b63d97aa631564d5d789c2b765448c8635fb6c"},
18+
{"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", "2d8c2f6d978ca21712b5f6de36c9d31fa8e96a4fa5d8ff8b0188dfb9e7c171bb"},
19+
}
20+
for _, tc := range testCases {
21+
res := Hash([]byte(tc.in))
22+
result := hex.EncodeToString(res[:])
23+
if result != tc.expected {
24+
t.Fatalf("Hash(%s) = %s, expected %s", tc.in, result, tc.expected)
25+
}
26+
}
27+
}
28+
29+
func BenchmarkHash(b *testing.B) {
30+
testCases := []struct {
31+
name string
32+
in string
33+
}{
34+
{"hello world", "hello world"},
35+
{"empty", ""},
36+
{"a", "a"},
37+
{"The quick brown fox jumps over the lazy dog", "The quick brown fox jumps over the lazy dog"},
38+
{"The quick brown fox jumps over the lazy dog.", "The quick brown fox jumps over the lazy dog."},
39+
{"Lorem ipsum", "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."},
40+
}
41+
for _, testCase := range testCases {
42+
b.Run(testCase.name, func(b *testing.B) {
43+
for i := 0; i < b.N; i++ {
44+
Hash([]byte(testCase.in))
45+
}
46+
})
47+
}
48+
}

0 commit comments

Comments
 (0)