sha256.mx raw
1 // Package sha256 implements SHA-256 in pure Go.
2 // No stdlib crypto dependency — compiles cleanly through tinyjs.
3 package sha256
4
5 const (
6 Size = 32
7 BlockSize = 64
8 )
9
10 var k = [64]uint32{
11 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
12 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
13 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
14 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
15 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
16 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
17 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
18 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
19 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
20 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
21 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
22 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
23 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
24 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
25 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
26 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
27 }
28
29 var init0 = [8]uint32{
30 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
31 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
32 }
33
34 // Sum returns the SHA-256 checksum of data.
35 func Sum(data []byte) [Size]byte {
36 var d digest
37 d.Reset()
38 d.Write(data)
39 return d.Sum()
40 }
41
42 // SumHex returns the SHA-256 checksum as a lowercase hex string.
43 func SumHex(data []byte) string {
44 h := Sum(data)
45 return bytesToHex(h[:])
46 }
47
48 type digest struct {
49 h [8]uint32
50 buf [BlockSize]byte
51 len int
52 tot uint64
53 }
54
55 func (d *digest) Reset() {
56 d.h = init0
57 d.len = 0
58 d.tot = 0
59 }
60
61 func (d *digest) Write(p []byte) {
62 d.tot += uint64(len(p))
63 // Fill buffer.
64 if d.len > 0 {
65 n := copy(d.buf[d.len:], p)
66 d.len += n
67 p = p[n:]
68 if d.len == BlockSize {
69 block(&d.h, d.buf[:])
70 d.len = 0
71 }
72 }
73 // Process full blocks.
74 for len(p) >= BlockSize {
75 block(&d.h, p[:BlockSize])
76 p = p[BlockSize:]
77 }
78 // Save remainder.
79 if len(p) > 0 {
80 d.len = copy(d.buf[:], p)
81 }
82 }
83
84 func (d *digest) Sum() [Size]byte {
85 // Padding.
86 var tmp [BlockSize]byte
87 tmp[0] = 0x80
88 bits := d.tot * 8
89
90 if d.len < 56 {
91 d.Write(tmp[:56-d.len])
92 } else {
93 d.Write(tmp[:64+56-d.len])
94 }
95
96 // Length in bits, big-endian.
97 var lb [8]byte
98 lb[0] = byte(bits >> 56)
99 lb[1] = byte(bits >> 48)
100 lb[2] = byte(bits >> 40)
101 lb[3] = byte(bits >> 32)
102 lb[4] = byte(bits >> 24)
103 lb[5] = byte(bits >> 16)
104 lb[6] = byte(bits >> 8)
105 lb[7] = byte(bits)
106 d.Write(lb[:])
107
108 var out [Size]byte
109 for i := 0; i < 8; i++ {
110 out[i*4] = byte(d.h[i] >> 24)
111 out[i*4+1] = byte(d.h[i] >> 16)
112 out[i*4+2] = byte(d.h[i] >> 8)
113 out[i*4+3] = byte(d.h[i])
114 }
115 return out
116 }
117
118 func block(h *[8]uint32, data []byte) {
119 var w [64]uint32
120
121 for i := 0; i < 16; i++ {
122 j := i * 4
123 w[i] = uint32(data[j])<<24 | uint32(data[j+1])<<16 |
124 uint32(data[j+2])<<8 | uint32(data[j+3])
125 }
126
127 for i := 16; i < 64; i++ {
128 s0 := rotr(w[i-15], 7) ^ rotr(w[i-15], 18) ^ (w[i-15] >> 3)
129 s1 := rotr(w[i-2], 17) ^ rotr(w[i-2], 19) ^ (w[i-2] >> 10)
130 w[i] = w[i-16] + s0 + w[i-7] + s1
131 }
132
133 a, b, c, d, e, f, g, hh := h[0], h[1], h[2], h[3], h[4], h[5], h[6], h[7]
134
135 for i := 0; i < 64; i++ {
136 S1 := rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25)
137 ch := (e & f) ^ (^e & g)
138 temp1 := hh + S1 + ch + k[i] + w[i]
139 S0 := rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22)
140 maj := (a & b) ^ (a & c) ^ (b & c)
141 temp2 := S0 + maj
142
143 hh = g
144 g = f
145 f = e
146 e = d + temp1
147 d = c
148 c = b
149 b = a
150 a = temp1 + temp2
151 }
152
153 h[0] += a
154 h[1] += b
155 h[2] += c
156 h[3] += d
157 h[4] += e
158 h[5] += f
159 h[6] += g
160 h[7] += hh
161 }
162
163 func rotr(x uint32, n uint) uint32 {
164 return (x >> n) | (x << (32 - n))
165 }
166
167 const hex = "0123456789abcdef"
168
169 func bytesToHex(b []byte) string {
170 out := []byte{:len(b)*2}
171 for i, v := range b {
172 out[i*2] = hex[v>>4]
173 out[i*2+1] = hex[v&0x0f]
174 }
175 return string(out)
176 }
177