LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
sha256.h
Go to the documentation of this file.
1/*
2 * Copyright (c) 2012-2026, QORE Lab, Imperial College London
3 * All rights reserved.
4 */
5#ifndef LINE_UTIL_SHA256_H
6#define LINE_UTIL_SHA256_H
7
8/**
9 * @file
10 * @ingroup line_util
11 * SHA-256 (FIPS 180-4), for verifying a file the port did not build.
12 *
13 * The port needs this for the one thing it downloads, `JMT.jar`: the transfer
14 * is delegated to curl or wget, which authenticate the SERVER, and a digest
15 * over the received bytes is what authenticates the ARTEFACT. The two answer
16 * different questions and the second is the one a jar about to be handed to a
17 * JVM raises.
18 *
19 * Rather than link OpenSSL for one hash -- a link-time obligation on every
20 * binary in the tree, for a dependency nothing else here wants -- this computes
21 * it directly, as `util/websocket.h` already does for the SHA-1 of the
22 * handshake. It is NOT a general cryptographic toolkit and must not become one:
23 * the file digest below is its whole purpose.
24 *
25 * `sha256_file_hex` streams the file in blocks and never holds it in memory,
26 * since the jar it was written for is 31 MiB.
27 */
28
29#include <cstddef>
30#include <cstdint>
31#include <cstdio>
32#include <string>
33
34namespace line {
35namespace util {
36
37namespace detail {
38
39/** The 64 round constants: the cube roots of the first 64 primes, FIPS 180-4. */
40static const std::uint32_t SHA256_K[64] = {
41 0x428a2f98u, 0x71374491u, 0xb5c0fbcfu, 0xe9b5dba5u, 0x3956c25bu, 0x59f111f1u, 0x923f82a4u,
42 0xab1c5ed5u, 0xd807aa98u, 0x12835b01u, 0x243185beu, 0x550c7dc3u, 0x72be5d74u, 0x80deb1feu,
43 0x9bdc06a7u, 0xc19bf174u, 0xe49b69c1u, 0xefbe4786u, 0x0fc19dc6u, 0x240ca1ccu, 0x2de92c6fu,
44 0x4a7484aau, 0x5cb0a9dcu, 0x76f988dau, 0x983e5152u, 0xa831c66du, 0xb00327c8u, 0xbf597fc7u,
45 0xc6e00bf3u, 0xd5a79147u, 0x06ca6351u, 0x14292967u, 0x27b70a85u, 0x2e1b2138u, 0x4d2c6dfcu,
46 0x53380d13u, 0x650a7354u, 0x766a0abbu, 0x81c2c92eu, 0x92722c85u, 0xa2bfe8a1u, 0xa81a664bu,
47 0xc24b8b70u, 0xc76c51a3u, 0xd192e819u, 0xd6990624u, 0xf40e3585u, 0x106aa070u, 0x19a4c116u,
48 0x1e376c08u, 0x2748774cu, 0x34b0bcb5u, 0x391c0cb3u, 0x4ed8aa4au, 0x5b9cca4fu, 0x682e6ff3u,
49 0x748f82eeu, 0x78a5636fu, 0x84c87814u, 0x8cc70208u, 0x90befffau, 0xa4506cebu, 0xbef9a3f7u,
50 0xc67178f2u};
51
52inline std::uint32_t sha256_ror(std::uint32_t x, int n) { return (x >> n) | (x << (32 - n)); }
53
54} // namespace detail
55
56/**
57 * Incremental SHA-256. Feed it with update(), read the digest once with hex().
58 *
59 * The object is SPENT by hex(): the padding is appended to the running state
60 * rather than to a copy, so a second call would digest the padding again. The
61 * two free functions below are the intended interface; this class is public
62 * only because a caller hashing something that is not a file or a string needs
63 * somewhere to put the loop.
64 */
65class Sha256 {
66public:
67 Sha256() { reset(); }
68
69 void reset() {
70 h_[0] = 0x6a09e667u;
71 h_[1] = 0xbb67ae85u;
72 h_[2] = 0x3c6ef372u;
73 h_[3] = 0xa54ff53au;
74 h_[4] = 0x510e527fu;
75 h_[5] = 0x9b05688cu;
76 h_[6] = 0x1f83d9abu;
77 h_[7] = 0x5be0cd19u;
78 len_ = 0;
79 total_ = 0;
80 }
81
82 void update(const void* data, std::size_t n) {
83 const unsigned char* p = static_cast<const unsigned char*>(data);
84 total_ += static_cast<std::uint64_t>(n);
85 while (n > 0) {
86 const std::size_t room = 64 - len_;
87 const std::size_t take = n < room ? n : room;
88 for (std::size_t i = 0; i < take; ++i) buf_[len_ + i] = p[i];
89 len_ += take;
90 p += take;
91 n -= take;
92 if (len_ == 64) {
93 compress(buf_);
94 len_ = 0;
95 }
96 }
97 }
98
99 void update(const std::string& s) { update(s.data(), s.size()); }
100
101 /** Finalizes and returns the digest as 64 lowercase hex characters. */
102 std::string hex() {
103 const std::uint64_t bitlen = total_ * 8ull;
104 unsigned char pad = 0x80;
105 update(&pad, 1);
106 pad = 0x00;
107 while (len_ != 56) update(&pad, 1);
108 unsigned char tail[8];
109 for (int i = 0; i < 8; ++i)
110 tail[i] = static_cast<unsigned char>((bitlen >> ((7 - i) * 8)) & 0xFFu);
111 update(tail, 8);
112
113 static const char* const digits = "0123456789abcdef";
114 std::string out;
115 out.reserve(64);
116 for (int i = 0; i < 8; ++i)
117 for (int b = 3; b >= 0; --b) {
118 const unsigned char byte = static_cast<unsigned char>((h_[i] >> (b * 8)) & 0xFFu);
119 out.push_back(digits[byte >> 4]);
120 out.push_back(digits[byte & 0x0F]);
121 }
122 return out;
123 }
124
125private:
126 void compress(const unsigned char block[64]) {
127 std::uint32_t w[64];
128 for (int i = 0; i < 16; ++i)
129 w[i] = (std::uint32_t(block[i * 4]) << 24) | (std::uint32_t(block[i * 4 + 1]) << 16) |
130 (std::uint32_t(block[i * 4 + 2]) << 8) | std::uint32_t(block[i * 4 + 3]);
131 for (int i = 16; i < 64; ++i) {
132 const std::uint32_t s0 = detail::sha256_ror(w[i - 15], 7) ^
133 detail::sha256_ror(w[i - 15], 18) ^ (w[i - 15] >> 3);
134 const std::uint32_t s1 = detail::sha256_ror(w[i - 2], 17) ^
135 detail::sha256_ror(w[i - 2], 19) ^ (w[i - 2] >> 10);
136 w[i] = w[i - 16] + s0 + w[i - 7] + s1;
137 }
138 std::uint32_t a = h_[0], b = h_[1], c = h_[2], d = h_[3];
139 std::uint32_t e = h_[4], f = h_[5], g = h_[6], hh = h_[7];
140 for (int i = 0; i < 64; ++i) {
141 const std::uint32_t S1 =
142 detail::sha256_ror(e, 6) ^ detail::sha256_ror(e, 11) ^ detail::sha256_ror(e, 25);
143 const std::uint32_t ch = (e & f) ^ ((~e) & g);
144 const std::uint32_t t1 = hh + S1 + ch + detail::SHA256_K[i] + w[i];
145 const std::uint32_t S0 =
146 detail::sha256_ror(a, 2) ^ detail::sha256_ror(a, 13) ^ detail::sha256_ror(a, 22);
147 const std::uint32_t maj = (a & b) ^ (a & c) ^ (b & c);
148 const std::uint32_t t2 = S0 + maj;
149 hh = g;
150 g = f;
151 f = e;
152 e = d + t1;
153 d = c;
154 c = b;
155 b = a;
156 a = t1 + t2;
157 }
158 h_[0] += a;
159 h_[1] += b;
160 h_[2] += c;
161 h_[3] += d;
162 h_[4] += e;
163 h_[5] += f;
164 h_[6] += g;
165 h_[7] += hh;
166 }
167
168 std::uint32_t h_[8];
169 unsigned char buf_[64];
170 std::size_t len_;
171 std::uint64_t total_;
172};
173
174/** The digest of a byte string, as 64 lowercase hex characters. */
175inline std::string sha256_hex(const std::string& data) {
176 Sha256 s;
177 s.update(data);
178 return s.hex();
179}
180
181/**
182 * The digest of a file's contents, streamed.
183 *
184 * @return the 64 hex characters, or an EMPTY string when the file cannot be
185 * read -- which the caller must not confuse with a mismatch: a digest
186 * that could not be computed has verified nothing.
187 */
188inline std::string sha256_file_hex(const std::string& path) {
189 std::FILE* f = std::fopen(path.c_str(), "rb");
190 if (f == nullptr) return std::string();
191 Sha256 s;
192 unsigned char buf[65536];
193 while (true) {
194 const std::size_t n = std::fread(buf, 1, sizeof(buf), f);
195 if (n > 0) s.update(buf, n);
196 if (n < sizeof(buf)) break;
197 }
198 const bool bad = std::ferror(f) != 0;
199 std::fclose(f);
200 return bad ? std::string() : s.hex();
201}
202
203} // namespace util
204} // namespace line
205
206#endif // LINE_UTIL_SHA256_H
Incremental SHA-256.
Definition sha256.h:65
std::string hex()
Finalizes and returns the digest as 64 lowercase hex characters.
Definition sha256.h:102
void update(const std::string &s)
Definition sha256.h:99
void update(const void *data, std::size_t n)
Definition sha256.h:82
std::string sha256_hex(const std::string &data)
The digest of a byte string, as 64 lowercase hex characters.
Definition sha256.h:175
std::string sha256_file_hex(const std::string &path)
The digest of a file's contents, streamed.
Definition sha256.h:188
Conservation laws of a layered queueing network, enumerated from its structure.
Definition aoi_dist2ph.h:52