v2 / vlib / crypto / sha256 / sha256.v
251 lines · 230 sloc · 5.85 KB · 4f70d9700a125bc055cdaf694a65f26f7e869ee3
Raw
1// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4// Package sha256 implements the SHA224 and SHA256 hash algorithms as defined
5// in FIPS 180-4.
6// Based off: https://github.com/golang/go/tree/master/src/crypto/sha256
7// Last commit: https://github.com/golang/go/commit/3ce865d7a0b88714cc433454ae2370a105210c01
8module sha256
9
10import encoding.binary
11
12// The size of a SHA256 checksum in bytes.
13pub const size = 32
14// The size of a SHA224 checksum in bytes.
15pub const size224 = 28
16// The blocksize of SHA256 and SHA224 in bytes.
17pub const block_size = 64
18
19const chunk = 64
20const init0 = u32(0x6A09E667)
21const init1 = u32(0xBB67AE85)
22const init2 = u32(0x3C6EF372)
23const init3 = u32(0xA54FF53A)
24const init4 = u32(0x510E527F)
25const init5 = u32(0x9B05688C)
26const init6 = u32(0x1F83D9AB)
27const init7 = u32(0x5BE0CD19)
28const init0_224 = u32(0xC1059ED8)
29const init1_224 = u32(0x367CD507)
30const init2_224 = u32(0x3070DD17)
31const init3_224 = u32(0xF70E5939)
32const init4_224 = u32(0xFFC00B31)
33const init5_224 = u32(0x68581511)
34const init6_224 = u32(0x64F98FA7)
35const init7_224 = u32(0xBEFA4FA4)
36
37// digest represents the partial evaluation of a checksum.
38struct Digest {
39mut:
40 h []u32
41 x []u8
42 nx int
43 len u64
44 is224 bool // mark if this digest is SHA-224
45}
46
47// free the resources taken by the Digest `d`
48@[unsafe]
49pub fn (mut d Digest) free() {
50 $if prealloc {
51 return
52 }
53 unsafe {
54 d.x.free()
55 d.h.free()
56 }
57}
58
59fn (mut d Digest) init() {
60 d.h = []u32{len: (8)}
61 d.x = []u8{len: chunk}
62 d.reset()
63}
64
65// reset the state of the Digest `d`
66pub fn (mut d Digest) reset() {
67 if !d.is224 {
68 d.h[0] = u32(init0)
69 d.h[1] = u32(init1)
70 d.h[2] = u32(init2)
71 d.h[3] = u32(init3)
72 d.h[4] = u32(init4)
73 d.h[5] = u32(init5)
74 d.h[6] = u32(init6)
75 d.h[7] = u32(init7)
76 } else {
77 d.h[0] = u32(init0_224)
78 d.h[1] = u32(init1_224)
79 d.h[2] = u32(init2_224)
80 d.h[3] = u32(init3_224)
81 d.h[4] = u32(init4_224)
82 d.h[5] = u32(init5_224)
83 d.h[6] = u32(init6_224)
84 d.h[7] = u32(init7_224)
85 }
86 d.nx = 0
87 d.len = 0
88}
89
90fn (d &Digest) clone() &Digest {
91 return &Digest{
92 ...d
93 h: d.h.clone()
94 x: d.x.clone()
95 }
96}
97
98// new returns a new Digest (implementing hash.Hash) computing the SHA256 checksum.
99pub fn new() &Digest {
100 mut d := &Digest{}
101 d.init()
102 return d
103}
104
105// new224 returns a new Digest (implementing hash.Hash) computing the SHA224 checksum.
106pub fn new224() &Digest {
107 mut d := &Digest{}
108 d.is224 = true
109 d.init()
110 return d
111}
112
113// write writes the contents of `p_` to the internal hash representation.
114pub fn (mut d Digest) write(p_ []u8) !int {
115 unsafe {
116 mut p := p_
117 nn := p.len
118 d.len += u64(nn)
119 if d.nx > 0 {
120 n := copy(mut d.x[d.nx..], p)
121 d.nx += n
122 if d.nx == chunk {
123 block(mut d, d.x)
124 d.nx = 0
125 }
126 if n >= p.len {
127 p = []
128 } else {
129 p = p[n..]
130 }
131 }
132 if p.len >= chunk {
133 n := p.len & ~(chunk - 1)
134 block(mut d, p[..n])
135 if n >= p.len {
136 p = []
137 } else {
138 p = p[n..]
139 }
140 }
141 if p.len > 0 {
142 d.nx = copy(mut d.x, p)
143 }
144 return nn
145 }
146}
147
148// sum returns the SHA256 or SHA224 checksum of digest with the data.
149pub fn (d &Digest) sum(b_in []u8) []u8 {
150 // Make a copy of d so that caller can keep writing and summing.
151 mut d0 := d.clone()
152 hash := d0.checksum()
153 mut b_out := b_in.clone()
154 if d0.is224 {
155 for b in hash[..size224] {
156 b_out << b
157 }
158 } else {
159 for b in hash {
160 b_out << b
161 }
162 }
163 return b_out
164}
165
166// checksum returns the current byte checksum of the Digest,
167// it is an internal method and is not recommended because its results are not idempotent.
168@[direct_array_access]
169fn (mut d Digest) checksum() []u8 {
170 mut len := d.len
171 // Padding. Add a 1 bit and 0 bits until 56 bytes mod 64.
172 mut tmp := []u8{len: (64)}
173 tmp[0] = 0x80
174 if int(len) % 64 < 56 {
175 d.write(tmp[..56 - int(len) % 64]) or { panic(err) }
176 } else {
177 d.write(tmp[..64 + 56 - int(len) % 64]) or { panic(err) }
178 }
179 // Length in bits.
180 len <<= u64(3)
181 binary.big_endian_put_u64(mut tmp, len)
182 d.write(tmp[..8]) or { panic(err) }
183 if d.nx != 0 {
184 panic('d.nx != 0')
185 }
186 mut digest := []u8{len: size}
187 binary.big_endian_put_u32(mut digest, d.h[0])
188 binary.big_endian_put_u32(mut digest[4..], d.h[1])
189 binary.big_endian_put_u32(mut digest[8..], d.h[2])
190 binary.big_endian_put_u32(mut digest[12..], d.h[3])
191 binary.big_endian_put_u32(mut digest[16..], d.h[4])
192 binary.big_endian_put_u32(mut digest[20..], d.h[5])
193 binary.big_endian_put_u32(mut digest[24..], d.h[6])
194 if !d.is224 {
195 binary.big_endian_put_u32(mut digest[28..], d.h[7])
196 }
197 return digest
198}
199
200// sum returns the SHA256 checksum of the bytes in `data`.
201// Example: assert sha256.sum('V'.bytes()).len > 0 == true
202pub fn sum(data []u8) []u8 {
203 return sum256(data)
204}
205
206// sum256 returns the SHA256 checksum of the data.
207pub fn sum256(data []u8) []u8 {
208 mut d := new()
209 d.write(data) or { panic(err) }
210 return d.checksum()
211}
212
213// sum224 returns the SHA224 checksum of the data.
214pub fn sum224(data []u8) []u8 {
215 mut d := new224()
216 d.write(data) or { panic(err) }
217 sum := d.checksum()
218 mut sum224 := []u8{len: size224}
219 copy(mut sum224, sum[..size224])
220 return sum224
221}
222
223fn block(mut dig Digest, p []u8) {
224 // For now just use block_generic until we have specific
225 // architecture optimized versions
226 block_generic(mut dig, p)
227}
228
229// size returns the size of the checksum in bytes.
230pub fn (d &Digest) size() int {
231 if !d.is224 {
232 return size
233 }
234 return size224
235}
236
237// block_size returns the block size of the checksum in bytes.
238pub fn (d &Digest) block_size() int {
239 return block_size
240}
241
242// hexhash returns a hexadecimal SHA256 hash sum `string` of `s`.
243// Example: assert sha256.hexhash('V') == 'de5a6f78116eca62d7fc5ce159d23ae6b889b365a1739ad2cf36f925a140d0cc'
244pub fn hexhash(s string) string {
245 return sum256(s.bytes()).hex()
246}
247
248// hexhash_224 returns a hexadecimal SHA224 hash sum `string` of `s`.
249pub fn hexhash_224(s string) string {
250 return sum224(s.bytes()).hex()
251}
252