morph 0.1.0
Asynchronous bridge between GUI thread and business models
Loading...
Searching...
No Matches
sha1.hpp
1// SPDX-License-Identifier: Apache-2.0
2
3#pragma once
4#include <array>
5#include <cstddef>
6#include <cstdint>
7#include <string_view>
8#include <vector>
9
10namespace morph::net::detail {
11
12namespace sha1_impl {
13inline std::uint32_t rotl(std::uint32_t value, int bits) { return (value << bits) | (value >> (32 - bits)); }
14} // namespace sha1_impl
15
16/// @brief Computes the raw 20-byte SHA-1 digest of @p message.
17///
18/// Used only to compute the WebSocket handshake's `Sec-WebSocket-Accept`
19/// value (RFC 6455 §1.3) — not a general-purpose or security-audited hash
20/// implementation.
21/// @param message Input bytes, treated as an opaque byte sequence (not text).
22/// @return The 20-byte digest, big-endian per FIPS 180-4.
23inline std::array<std::uint8_t, 20> sha1Digest(std::string_view message) {
24 std::uint32_t h0 = 0x67452301;
25 std::uint32_t h1 = 0xEFCDAB89;
26 std::uint32_t h2 = 0x98BADCFE;
27 std::uint32_t h3 = 0x10325476;
28 std::uint32_t h4 = 0xC3D2E1F0;
29
30 // message.size() is already std::size_t (== std::uint64_t on every
31 // platform morph::net builds for), so an explicit cast trips GCC's
32 // -Wuseless-cast under this project's warnings-as-errors policy.
33 std::uint64_t const bitLen = std::uint64_t{message.size()} * 8U;
34 std::vector<std::uint8_t> data(message.begin(), message.end());
35 data.push_back(std::uint8_t{0x80});
36 while (data.size() % 64 != 56) {
37 data.push_back(std::uint8_t{0x00});
38 }
39 for (int shift = 56; shift >= 0; shift -= 8) {
40 data.push_back(static_cast<std::uint8_t>((bitLen >> shift) & 0xFFu));
41 }
42
43 for (std::size_t chunkStart = 0; chunkStart < data.size(); chunkStart += 64) {
44 std::array<std::uint32_t, 80> w{};
45 for (int i = 0; i < 16; ++i) {
46 std::size_t const base = chunkStart + static_cast<std::size_t>(i) * 4;
47 w[static_cast<std::size_t>(i)] =
48 (static_cast<std::uint32_t>(data[base]) << 24) | (static_cast<std::uint32_t>(data[base + 1]) << 16) |
49 (static_cast<std::uint32_t>(data[base + 2]) << 8) | static_cast<std::uint32_t>(data[base + 3]);
50 }
51 for (int i = 16; i < 80; ++i) {
52 w[static_cast<std::size_t>(i)] =
53 sha1_impl::rotl(w[static_cast<std::size_t>(i - 3)] ^ w[static_cast<std::size_t>(i - 8)] ^
54 w[static_cast<std::size_t>(i - 14)] ^ w[static_cast<std::size_t>(i - 16)],
55 1);
56 }
57
58 std::uint32_t a = h0;
59 std::uint32_t b = h1;
60 std::uint32_t c = h2;
61 std::uint32_t d = h3;
62 std::uint32_t e = h4;
63 for (int i = 0; i < 80; ++i) {
64 std::uint32_t f = 0;
65 std::uint32_t k = 0;
66 if (i < 20) {
67 f = (b & c) | ((~b) & d);
68 k = 0x5A827999;
69 } else if (i < 40) {
70 f = b ^ c ^ d;
71 k = 0x6ED9EBA1;
72 } else if (i < 60) {
73 f = (b & c) | (b & d) | (c & d);
74 k = 0x8F1BBCDC;
75 } else {
76 f = b ^ c ^ d;
77 k = 0xCA62C1D6;
78 }
79 std::uint32_t const temp = sha1_impl::rotl(a, 5) + f + e + k + w[static_cast<std::size_t>(i)];
80 e = d;
81 d = c;
82 c = sha1_impl::rotl(b, 30);
83 b = a;
84 a = temp;
85 }
86 h0 += a;
87 h1 += b;
88 h2 += c;
89 h3 += d;
90 h4 += e;
91 }
92
93 std::array<std::uint8_t, 20> digest{};
94 std::array<std::uint32_t, 5> const hs{h0, h1, h2, h3, h4};
95 for (std::size_t i = 0; i < 5; ++i) {
96 digest[(i * 4) + 0] = static_cast<std::uint8_t>((hs[i] >> 24) & 0xFFu);
97 digest[(i * 4) + 1] = static_cast<std::uint8_t>((hs[i] >> 16) & 0xFFu);
98 digest[(i * 4) + 2] = static_cast<std::uint8_t>((hs[i] >> 8) & 0xFFu);
99 digest[(i * 4) + 3] = static_cast<std::uint8_t>(hs[i] & 0xFFu);
100 }
101 return digest;
102}
103
104} // namespace morph::net::detail