1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
|
/******************************************************************************
__ ____ __
/ / ___ ____ _/ __/_____________ _/ /__
/ / / _ \/ __ `/ /_/ ___/ ___/ __ `/ / _ \
/ /___/ __/ /_/ / __(__ ) /__/ /_/ / / __/
/_____/\___/\__,_/_/ /____/\___/\__,_/_/\___/
(C)opyright 2025, Leafscale, LLC - https://www.leafscale.com
Project: Zygaena
Filename: checksum.reef
Authors: Chris Tusa <chris.tusa@leafscale.com>
License: <see LICENSE file included with this source code>
Description: SHA256 checksum verification utilities
******************************************************************************/
module util.checksum
import io.file
import core.str
import sys.process
import crypto.sha256 as sha
import core.result as res
export
// Compute SHA256 hash of a file, returns hex string (lowercase)
fn sha256_file(path: string): string
// Compute SHA256 hash of a string, returns hex string (lowercase)
fn sha256_string(data: string): string
// Verify a file matches expected checksum
fn verify_checksum(path: string, expected: string): bool
// Parse checksum format "algo:hash" and verify
fn verify_checksum_with_algo(path: string, checksum_str: string): bool
end export
// Compute SHA256 of a file
// Uses Reef's native streaming sha256_file() (Reef 0.1.14+)
// Falls back to sha256sum command if native fails
fn sha256_file(path: string): string
if not file.fileExists(path)
return ""
end if
// Use native streaming API (memory efficient, works on all file sizes)
// Reef 0.1.14 fixed the large file hang/segfault issue
let hash = sha.sha256_file(path)
if str.length(hash) == 64
return str.to_lower(hash)
end if
// Fallback to sha256sum command if native fails
return sha256_file_cmd(path)
end sha256_file
// Compute SHA256 using sha256sum command (for large files)
fn sha256_file_cmd(path: string): string
let output_file = "/tmp/coral_checksum_" + extract_basename(path)
let cmd = "sha256sum \"" + path + "\" | cut -d' ' -f1 > \"" + output_file + "\""
let pid = process.process_spawn_shell(cmd)
if pid < 0
return ""
end if
process.process_wait(pid)
// Read the hash from output file
if not file.fileExists(output_file)
return ""
end if
let hash = str.trim_ws(res.unwrap_or(file.readFile(output_file), ""))
// Clean up
let rm_pid = process.process_spawn_shell("rm -f \"" + output_file + "\"")
if rm_pid > 0
process.process_wait(rm_pid)
end if
return hash
end sha256_file_cmd
// Extract basename from path for temp file naming
fn extract_basename(path: string): string
let last_slash = str.last_index_of_char(path, '/')
if last_slash < 0
return path
end if
return str.substring(path, last_slash + 1, str.length(path) - last_slash - 1)
end extract_basename
// Compute SHA256 of a string using native crypto
fn sha256_string(data: string): string
let hash = sha.sha256(data)
return str.to_lower(hash)
end sha256_string
// Verify that a file's SHA256 matches the expected value
fn verify_checksum(path: string, expected: string): bool
let computed = sha256_file(path)
if str.length(computed) == 0
return false
end if
// Compare case-insensitively (normalize to lowercase)
let computed_lower = str.to_lower(computed)
let expected_lower = str.to_lower(expected)
return computed_lower == expected_lower
end verify_checksum
// Parse and verify checksum in format "algo:hash" or just "hash"
// Supported algorithms: sha256, sha256sum
// If no algorithm prefix, assumes sha256
fn verify_checksum_with_algo(path: string, checksum_str: string): bool
// Check for algorithm prefix
let colon_idx = str.index_of_char(checksum_str, ':')
if colon_idx < 0
// No prefix, assume SHA256
return verify_checksum(path, checksum_str)
end if
// Extract algorithm and hash
let algo = str.to_lower(str.substring(checksum_str, 0, colon_idx))
let hash = str.substring(checksum_str, colon_idx + 1, str.length(checksum_str) - colon_idx - 1)
// Verify based on algorithm
if algo == "sha256" or algo == "sha256sum"
return verify_checksum(path, hash)
end if
// Unsupported algorithm
return false
end verify_checksum_with_algo
end module
|