v2 / vlib / crypto / sha512 / sha512.v
350 lines · 321 sloc · 8.63 KB · 8e35f4d9848f7ad35d857a187dddbfd2eca5e19d
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 sha512 implements the SHA-384, SHA-512, SHA-512/224, and SHA-512/256
5// hash algorithms as defined in FIPS 180-4.
6// Based off: https://github.com/golang/go/tree/master/src/crypto/sha512
7// Last commit: https://github.com/golang/go/commit/3ce865d7a0b88714cc433454ae2370a105210c01
8module sha512
9
10import crypto
11import encoding.binary
12
13// size is the size, in bytes, of a SHA-512 checksum.
14pub const size = 64
15// size224 is the size, in bytes, of a SHA-512/224 checksum.
16pub const size224 = 28
17// size256 is the size, in bytes, of a SHA-512/256 checksum.
18pub const size256 = 32
19// size384 is the size, in bytes, of a SHA-384 checksum.
20pub const size384 = 48
21// block_size is the block size, in bytes, of the SHA-512/224,
22// SHA-512/256, SHA-384 and SHA-512 hash functions.
23pub const block_size = 128
24
25const chunk = 128
26const init0 = u64(0x6a09e667f3bcc908)
27const init1 = u64(0xbb67ae8584caa73b)
28const init2 = u64(0x3c6ef372fe94f82b)
29const init3 = u64(0xa54ff53a5f1d36f1)
30const init4 = u64(0x510e527fade682d1)
31const init5 = u64(0x9b05688c2b3e6c1f)
32const init6 = u64(0x1f83d9abfb41bd6b)
33const init7 = u64(0x5be0cd19137e2179)
34const init0_224 = u64(0x8c3d37c819544da2)
35const init1_224 = u64(0x73e1996689dcd4d6)
36const init2_224 = u64(0x1dfab7ae32ff9c82)
37const init3_224 = u64(0x679dd514582f9fcf)
38const init4_224 = u64(0x0f6d2b697bd44da8)
39const init5_224 = u64(0x77e36f7304c48942)
40const init6_224 = u64(0x3f9d85a86a1d36c8)
41const init7_224 = u64(0x1112e6ad91d692a1)
42const init0_256 = u64(0x22312194fc2bf72c)
43const init1_256 = u64(0x9f555fa3c84c64c2)
44const init2_256 = u64(0x2393b86b6f53b151)
45const init3_256 = u64(0x963877195940eabd)
46const init4_256 = u64(0x96283ee2a88effe3)
47const init5_256 = u64(0xbe5e1e2553863992)
48const init6_256 = u64(0x2b0199fc2c85b8aa)
49const init7_256 = u64(0x0eb72ddc81c52ca2)
50const init0_384 = u64(0xcbbb9d5dc1059ed8)
51const init1_384 = u64(0x629a292a367cd507)
52const init2_384 = u64(0x9159015a3070dd17)
53const init3_384 = u64(0x152fecd8f70e5939)
54const init4_384 = u64(0x67332667ffc00b31)
55const init5_384 = u64(0x8eb44a8768581511)
56const init6_384 = u64(0xdb0c2e0d64f98fa7)
57const init7_384 = u64(0x47b5481dbefa4fa4)
58
59// Digest represents the partial evaluation of a checksum.
60struct Digest {
61mut:
62 h []u64
63 x []u8
64 nx int
65 len u64
66 function crypto.Hash
67}
68
69// free the resources taken by the Digest `d`
70@[unsafe]
71pub fn (mut d Digest) free() {
72 $if prealloc {
73 return
74 }
75 unsafe {
76 d.x.free()
77 d.h.free()
78 }
79}
80
81fn (mut d Digest) init() {
82 d.h = []u64{len: (8)}
83 d.x = []u8{len: chunk}
84 d.reset()
85}
86
87// reset the state of the Digest `d`
88pub fn (mut d Digest) reset() {
89 match d.function {
90 .sha384 {
91 d.h[0] = init0_384
92 d.h[1] = init1_384
93 d.h[2] = init2_384
94 d.h[3] = init3_384
95 d.h[4] = init4_384
96 d.h[5] = init5_384
97 d.h[6] = init6_384
98 d.h[7] = init7_384
99 }
100 .sha512_224 {
101 d.h[0] = init0_224
102 d.h[1] = init1_224
103 d.h[2] = init2_224
104 d.h[3] = init3_224
105 d.h[4] = init4_224
106 d.h[5] = init5_224
107 d.h[6] = init6_224
108 d.h[7] = init7_224
109 }
110 .sha512_256 {
111 d.h[0] = init0_256
112 d.h[1] = init1_256
113 d.h[2] = init2_256
114 d.h[3] = init3_256
115 d.h[4] = init4_256
116 d.h[5] = init5_256
117 d.h[6] = init6_256
118 d.h[7] = init7_256
119 }
120 else {
121 d.h[0] = init0
122 d.h[1] = init1
123 d.h[2] = init2
124 d.h[3] = init3
125 d.h[4] = init4
126 d.h[5] = init5
127 d.h[6] = init6
128 d.h[7] = init7
129 }
130 }
131
132 d.nx = 0
133 d.len = 0
134}
135
136fn (d &Digest) clone() &Digest {
137 return &Digest{
138 ...d
139 h: d.h.clone()
140 x: d.x.clone()
141 }
142}
143
144// internal
145fn new_digest(hash crypto.Hash) &Digest {
146 mut d := &Digest{
147 function: hash
148 }
149 d.init()
150 return d
151}
152
153// new returns a new Digest (implementing hash.Hash) computing the SHA-512 checksum.
154pub fn new() &Digest {
155 return new_digest(.sha512)
156}
157
158// new512_224 returns a new Digest (implementing hash.Hash) computing the SHA-512/224 checksum.
159pub fn new512_224() &Digest {
160 return new_digest(.sha512_224)
161}
162
163// new512_256 returns a new Digest (implementing hash.Hash) computing the SHA-512/256 checksum.
164pub fn new512_256() &Digest {
165 return new_digest(.sha512_256)
166}
167
168// new384 returns a new Digest (implementing hash.Hash) computing the SHA-384 checksum.
169pub fn new384() &Digest {
170 return new_digest(.sha384)
171}
172
173// write writes the contents of `p_` to the internal hash representation.
174pub fn (mut d Digest) write(p_ []u8) !int {
175 unsafe {
176 mut p := p_
177 nn := p.len
178 d.len += u64(nn)
179 if d.nx > 0 {
180 n := copy(mut d.x[d.nx..], p)
181 d.nx += n
182 if d.nx == chunk {
183 block(mut d, d.x)
184 d.nx = 0
185 }
186 if n >= p.len {
187 p = []
188 } else {
189 p = p[n..]
190 }
191 }
192 if p.len >= chunk {
193 n := p.len & ~(chunk - 1)
194 block(mut d, p[..n])
195 if n >= p.len {
196 p = []
197 } else {
198 p = p[n..]
199 }
200 }
201 if p.len > 0 {
202 d.nx = copy(mut d.x, p)
203 }
204 return nn
205 }
206}
207
208// sum returns the SHA512 or SHA384 checksum of digest with the data bytes in `b_in`
209pub fn (d &Digest) sum(b_in []u8) []u8 {
210 // Make a copy of d so that caller can keep writing and summing.
211 mut d0 := d.clone()
212 hash := d0.checksum()
213 mut b_out := b_in.clone()
214 match d0.function {
215 .sha384 {
216 for b in hash[..size384] {
217 b_out << b
218 }
219 }
220 .sha512_224 {
221 for b in hash[..size224] {
222 b_out << b
223 }
224 }
225 .sha512_256 {
226 for b in hash[..size256] {
227 b_out << b
228 }
229 }
230 else {
231 for b in hash {
232 b_out << b
233 }
234 }
235 }
236
237 return b_out
238}
239
240// checksum returns the current byte checksum of the Digest,
241// it is an internal method and is not recommended because its results are not idempotent.
242fn (mut d Digest) checksum() []u8 {
243 // Padding. Add a 1 bit and 0 bits until 112 bytes mod 128.
244 mut len := d.len
245 mut tmp := []u8{len: (128)}
246 tmp[0] = 0x80
247 if int(len) % 128 < 112 {
248 d.write(tmp[..112 - int(len) % 128]) or { panic(err) }
249 } else {
250 d.write(tmp[..128 + 112 - int(len) % 128]) or { panic(err) }
251 }
252 // Length in bits.
253 len <<= u64(3)
254 binary.big_endian_put_u64(mut tmp, u64(0)) // upper 64 bits are always zero, because len variable has type u64
255 binary.big_endian_put_u64(mut tmp[8..], len)
256 d.write(tmp[..16]) or { panic(err) }
257 if d.nx != 0 {
258 panic('d.nx != 0')
259 }
260 mut digest := []u8{len: size}
261 binary.big_endian_put_u64(mut digest, d.h[0])
262 binary.big_endian_put_u64(mut digest[8..], d.h[1])
263 binary.big_endian_put_u64(mut digest[16..], d.h[2])
264 binary.big_endian_put_u64(mut digest[24..], d.h[3])
265 binary.big_endian_put_u64(mut digest[32..], d.h[4])
266 binary.big_endian_put_u64(mut digest[40..], d.h[5])
267 if d.function != .sha384 {
268 binary.big_endian_put_u64(mut digest[48..], d.h[6])
269 binary.big_endian_put_u64(mut digest[56..], d.h[7])
270 }
271 return digest
272}
273
274// sum512 returns the SHA512 checksum of the data.
275pub fn sum512(data []u8) []u8 {
276 mut d := new_digest(.sha512)
277 d.write(data) or { panic(err) }
278 return d.checksum()
279}
280
281// sum384 returns the SHA384 checksum of the data.
282pub fn sum384(data []u8) []u8 {
283 mut d := new_digest(.sha384)
284 d.write(data) or { panic(err) }
285 sum := d.checksum()
286 mut sum384 := []u8{len: size384}
287 copy(mut sum384, sum[..size384])
288 return sum384
289}
290
291// sum512_224 returns the Sum512/224 checksum of the data.
292pub fn sum512_224(data []u8) []u8 {
293 mut d := new_digest(.sha512_224)
294 d.write(data) or { panic(err) }
295 sum := d.checksum()
296 mut sum224 := []u8{len: size224}
297 copy(mut sum224, sum[..size224])
298 return sum224
299}
300
301// sum512_256 returns the Sum512/256 checksum of the data.
302pub fn sum512_256(data []u8) []u8 {
303 mut d := new_digest(.sha512_256)
304 d.write(data) or { panic(err) }
305 sum := d.checksum()
306 mut sum256 := []u8{len: size256}
307 copy(mut sum256, sum[..size256])
308 return sum256
309}
310
311fn block(mut dig Digest, p []u8) {
312 // For now just use block_generic until we have specific
313 // architecture optimized versions
314 block_generic(mut dig, p)
315}
316
317// size returns the size of the checksum in bytes.
318pub fn (d &Digest) size() int {
319 match d.function {
320 .sha512_224 { return size224 }
321 .sha512_256 { return size256 }
322 .sha384 { return size384 }
323 else { return size }
324 }
325}
326
327// block_size returns the block size of the checksum in bytes.
328pub fn (d &Digest) block_size() int {
329 return block_size
330}
331
332// hexhash returns a hexadecimal SHA512 hash sum `string` of `s`.
333pub fn hexhash(s string) string {
334 return sum512(s.bytes()).hex()
335}
336
337// hexhash_384 returns a hexadecimal SHA384 hash sum `string` of `s`.
338pub fn hexhash_384(s string) string {
339 return sum384(s.bytes()).hex()
340}
341
342// hexhash_512_224 returns a hexadecimal SHA512/224 hash sum `string` of `s`.
343pub fn hexhash_512_224(s string) string {
344 return sum512_224(s.bytes()).hex()
345}
346
347// hexhash_512_256 returns a hexadecimal 512/256 hash sum `string` of `s`.
348pub fn hexhash_512_256(s string) string {
349 return sum512_256(s.bytes()).hex()
350}
351