NAH 3.0.0
Native Application Host - Library API Reference
Loading...
Searching...
No Matches
nah_digest.h
Go to the documentation of this file.
1/* SHA-256 artifact digests. SPDX-License-Identifier: MIT */
2
3#ifndef NAH_DIGEST_H
4#define NAH_DIGEST_H
5
6#include <algorithm>
7#include <array>
8#include <cstdint>
9#include <filesystem>
10#include <fstream>
11#include <iomanip>
12#include <sstream>
13#include <string>
14
15namespace nah::digest {
16
18 std::string algorithm;
19 std::string value;
20
21 std::string canonical() const { return algorithm + ":" + value; }
22 bool operator==(const ArtifactDigest& other) const {
23 return algorithm == other.algorithm && value == other.value;
24 }
25};
26
27struct FileDigest {
28 std::filesystem::path path;
30 std::uintmax_t size = 0;
31};
32
33struct Result {
34 bool ok = false;
36 std::string error;
37};
38
39namespace detail {
40
41class Sha256 {
42public:
43 void update(const unsigned char* data, std::size_t size) {
44 total_size_ += size;
45 while (size != 0) {
46 const auto count = (std::min)(size, block_.size() - block_size_);
47 std::copy(data, data + count, block_.begin() + static_cast<std::ptrdiff_t>(block_size_));
48 block_size_ += count;
49 data += count;
50 size -= count;
51 if (block_size_ == block_.size()) {
52 transform(block_.data());
53 block_size_ = 0;
54 }
55 }
56 }
57
58 std::array<unsigned char, 32> finish() {
59 const auto bit_size = static_cast<std::uint64_t>(total_size_) * 8U;
60 block_[block_size_++] = 0x80U;
61 if (block_size_ > 56) {
62 std::fill(block_.begin() + static_cast<std::ptrdiff_t>(block_size_), block_.end(), 0U);
63 transform(block_.data());
64 block_size_ = 0;
65 }
66 std::fill(block_.begin() + static_cast<std::ptrdiff_t>(block_size_), block_.begin() + 56, 0U);
67 for (std::size_t i = 0; i < 8; ++i) {
68 block_[63 - i] = static_cast<unsigned char>((bit_size >> (i * 8U)) & 0xffU);
69 }
70 transform(block_.data());
71
72 std::array<unsigned char, 32> output{};
73 for (std::size_t i = 0; i < state_.size(); ++i) {
74 output[i * 4] = static_cast<unsigned char>(state_[i] >> 24U);
75 output[i * 4 + 1] = static_cast<unsigned char>(state_[i] >> 16U);
76 output[i * 4 + 2] = static_cast<unsigned char>(state_[i] >> 8U);
77 output[i * 4 + 3] = static_cast<unsigned char>(state_[i]);
78 }
79 return output;
80 }
81
82private:
83 static std::uint32_t rotate(std::uint32_t value, std::uint32_t count) {
84 return (value >> count) | (value << (32U - count));
85 }
86
87 void transform(const unsigned char* input) {
88 static constexpr std::array<std::uint32_t, 64> constants{{
89 0x428a2f98U, 0x71374491U, 0xb5c0fbcfU, 0xe9b5dba5U, 0x3956c25bU, 0x59f111f1U, 0x923f82a4U, 0xab1c5ed5U,
90 0xd807aa98U, 0x12835b01U, 0x243185beU, 0x550c7dc3U, 0x72be5d74U, 0x80deb1feU, 0x9bdc06a7U, 0xc19bf174U,
91 0xe49b69c1U, 0xefbe4786U, 0x0fc19dc6U, 0x240ca1ccU, 0x2de92c6fU, 0x4a7484aaU, 0x5cb0a9dcU, 0x76f988daU,
92 0x983e5152U, 0xa831c66dU, 0xb00327c8U, 0xbf597fc7U, 0xc6e00bf3U, 0xd5a79147U, 0x06ca6351U, 0x14292967U,
93 0x27b70a85U, 0x2e1b2138U, 0x4d2c6dfcU, 0x53380d13U, 0x650a7354U, 0x766a0abbU, 0x81c2c92eU, 0x92722c85U,
94 0xa2bfe8a1U, 0xa81a664bU, 0xc24b8b70U, 0xc76c51a3U, 0xd192e819U, 0xd6990624U, 0xf40e3585U, 0x106aa070U,
95 0x19a4c116U, 0x1e376c08U, 0x2748774cU, 0x34b0bcb5U, 0x391c0cb3U, 0x4ed8aa4aU, 0x5b9cca4fU, 0x682e6ff3U,
96 0x748f82eeU, 0x78a5636fU, 0x84c87814U, 0x8cc70208U, 0x90befffaU, 0xa4506cebU, 0xbef9a3f7U, 0xc67178f2U
97 }};
98 std::array<std::uint32_t, 64> words{};
99 for (std::size_t i = 0; i < 16; ++i) {
100 words[i] = (static_cast<std::uint32_t>(input[i * 4]) << 24U) |
101 (static_cast<std::uint32_t>(input[i * 4 + 1]) << 16U) |
102 (static_cast<std::uint32_t>(input[i * 4 + 2]) << 8U) |
103 static_cast<std::uint32_t>(input[i * 4 + 3]);
104 }
105 for (std::size_t i = 16; i < words.size(); ++i) {
106 const auto s0 = rotate(words[i - 15], 7U) ^ rotate(words[i - 15], 18U) ^ (words[i - 15] >> 3U);
107 const auto s1 = rotate(words[i - 2], 17U) ^ rotate(words[i - 2], 19U) ^ (words[i - 2] >> 10U);
108 words[i] = words[i - 16] + s0 + words[i - 7] + s1;
109 }
110 auto a = state_[0]; auto b = state_[1]; auto c = state_[2]; auto d = state_[3];
111 auto e = state_[4]; auto f = state_[5]; auto g = state_[6]; auto h = state_[7];
112 for (std::size_t i = 0; i < words.size(); ++i) {
113 const auto s1 = rotate(e, 6U) ^ rotate(e, 11U) ^ rotate(e, 25U);
114 const auto choice = (e & f) ^ ((~e) & g);
115 const auto temp1 = h + s1 + choice + constants[i] + words[i];
116 const auto s0 = rotate(a, 2U) ^ rotate(a, 13U) ^ rotate(a, 22U);
117 const auto majority = (a & b) ^ (a & c) ^ (b & c);
118 const auto temp2 = s0 + majority;
119 h = g; g = f; f = e; e = d + temp1;
120 d = c; c = b; b = a; a = temp1 + temp2;
121 }
122 state_[0] += a; state_[1] += b; state_[2] += c; state_[3] += d;
123 state_[4] += e; state_[5] += f; state_[6] += g; state_[7] += h;
124 }
125
126 std::array<std::uint32_t, 8> state_{{
127 0x6a09e667U, 0xbb67ae85U, 0x3c6ef372U, 0xa54ff53aU,
128 0x510e527fU, 0x9b05688cU, 0x1f83d9abU, 0x5be0cd19U
129 }};
130 std::array<unsigned char, 64> block_{};
131 std::size_t block_size_ = 0;
132 std::uintmax_t total_size_ = 0;
133};
134
135inline std::string hex(const std::array<unsigned char, 32>& bytes) {
136 std::ostringstream output;
137 output << std::hex << std::setfill('0');
138 for (const auto byte : bytes) output << std::setw(2) << static_cast<unsigned int>(byte);
139 return output.str();
140}
141
142} // namespace detail
143
144inline Result sha256_file(const std::filesystem::path& path) {
145 Result result;
146 std::error_code ec;
147 const auto status = std::filesystem::symlink_status(path, ec);
148 if (ec || !std::filesystem::is_regular_file(status)) {
149 result.error = "artifact must be a regular file";
150 return result;
151 }
152 std::ifstream input(path, std::ios::binary);
153 if (!input) {
154 result.error = "cannot open artifact";
155 return result;
156 }
157 detail::Sha256 hash;
158 std::array<unsigned char, 64 * 1024> buffer{};
159 std::uintmax_t size = 0;
160 while (input) {
161 input.read(reinterpret_cast<char*>(buffer.data()), static_cast<std::streamsize>(buffer.size()));
162 const auto count = input.gcount();
163 if (count > 0) {
164 hash.update(buffer.data(), static_cast<std::size_t>(count));
165 size += static_cast<std::uintmax_t>(count);
166 }
167 }
168 if (!input.eof()) {
169 result.error = "cannot read artifact";
170 return result;
171 }
172 result.ok = true;
173 result.file = {std::filesystem::absolute(path, ec).lexically_normal(),
174 {"sha256", detail::hex(hash.finish())}, size};
175 if (ec) {
176 result.ok = false;
177 result.error = "cannot resolve artifact path";
178 }
179 return result;
180}
181
182} // namespace nah::digest
183
184#endif // NAH_DIGEST_H
void update(const unsigned char *data, std::size_t size)
Definition nah_digest.h:43
std::array< unsigned char, 32 > finish()
Definition nah_digest.h:58
std::string hex(const std::array< unsigned char, 32 > &bytes)
Definition nah_digest.h:135
Result sha256_file(const std::filesystem::path &path)
Definition nah_digest.h:144
std::string canonical() const
Definition nah_digest.h:21
bool operator==(const ArtifactDigest &other) const
Definition nah_digest.h:22
ArtifactDigest value
Definition nah_digest.h:29
std::uintmax_t size
Definition nah_digest.h:30
std::filesystem::path path
Definition nah_digest.h:28
std::string error
Definition nah_digest.h:36