Repository navigation
Expand file tree
/
Copy pathdigest_response.hpp
More file actions
147 lines (120 loc) · 5.38 KB
/
Copy pathdigest_response.hpp
File metadata and controls
147 lines (120 loc) · 5.38 KB
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
140
141
142
143
144
145
146
147
/*
This file is part of libhttpserver
Copyright (C) 2011-2026 Sebastiano Merlino
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see the file LICENSE in
the distribution; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
// The RFC 7616 section 3.4 response computation (TASK-115, plan
// section 5). ONE algorithm-parameterized path -- a hash_descriptor
// carries the wire token, the digest width, and H() as a lowercase-hex
// function -- so MD5 and SHA-256 share every line of chain assembly
// and a third algorithm would be one more descriptor, not a parallel
// block (the duplication gate's requirement). The non-session
// formulas only (v2 parity: MD5-sess/SHA-256-sess were never
// exposed):
//
// HA1 = H(username ":" realm ":" password)
// HA2 = H(method ":" uri) [qop=auth]
// response = H(HA1 ":" nonce ":" nc ":" cnonce ":" qop ":" HA2)
// legacy = H(HA1 ":" nonce ":" HA2) [no qop, RFC 2617]
//
// Every input arrives as a view over already-decoded parser output
// (digest_params.hpp) or policy state; nothing here parses, locks, or
// allocates beyond the joined chain and its hex digest.
#if !defined(HTTPSERVER_COMPILATION)
#error "digest_response.hpp is internal; only reachable when compiling libhttpserver."
#endif
#ifndef SRC_HTTPSERVER_DETAIL_DIGEST_RESPONSE_HPP_
#define SRC_HTTPSERVER_DETAIL_DIGEST_RESPONSE_HPP_
#include <cstddef>
#include <initializer_list>
#include <span>
#include <string>
#include <string_view>
#include <httpserver/detail/digest_hex.hpp>
#include <httpserver/detail/md5.hpp>
#include <httpserver/detail/sha256.hpp>
namespace httpserver {
namespace detail {
namespace digest {
namespace response_detail {
inline std::span<const std::byte> raw_bytes(std::string_view text) noexcept {
const auto* raw = reinterpret_cast<const std::byte*>(text.data());
return std::span<const std::byte>(raw, text.size());
}
// "a:b:c" over any number of parts.
inline std::string colon_joined(
std::initializer_list<std::string_view> parts) {
std::string out;
for (const std::string_view part : parts) {
if (!out.empty()) out.push_back(':');
out.append(part);
}
return out;
}
} // namespace response_detail
// The one algorithm axis of the Digest computation. token is the
// RFC 7616 wire spelling the policy matches client values against;
// digest_size is the byte width (2x the hex length of HA1/HA2 and
// the response); hex_of is H() as canonical lowercase hex.
struct hash_descriptor {
std::string_view token;
std::size_t digest_size;
std::string (*hex_of)(std::string_view);
};
inline std::string md5_hex(std::string_view text) {
return encode_hex(md5(response_detail::raw_bytes(text)));
}
inline std::string sha256_hex(std::string_view text) {
return encode_hex(sha256(response_detail::raw_bytes(text)));
}
inline constexpr hash_descriptor md5_descriptor{"MD5", 16, &md5_hex};
inline constexpr hash_descriptor sha256_descriptor{"SHA-256", 32, &sha256_hex};
// HA1 = H(username:realm:password); the fixed-credential factory
// computes this once and scrubs the cleartext, the HA1-source form
// receives it from the application.
inline std::string compute_ha1(const hash_descriptor& descriptor,
std::string_view username,
std::string_view realm,
std::string_view password) {
return descriptor.hex_of(response_detail::colon_joined(
{username, realm, password}));
}
// HA2 = H(method:uri) for qop=auth (auth-int is rejected at the
// policy, never computed here).
inline std::string compute_ha2(const hash_descriptor& descriptor,
std::string_view method,
std::string_view uri) {
return descriptor.hex_of(
response_detail::colon_joined({method, uri}));
}
// The qop-present chain (RFC 7616 section 3.4.1).
inline std::string compute_response_qop(
const hash_descriptor& descriptor, std::string_view ha1_hex,
std::string_view nonce, std::string_view nc, std::string_view cnonce,
std::string_view qop, std::string_view ha2_hex) {
return descriptor.hex_of(response_detail::colon_joined(
{ha1_hex, nonce, nc, cnonce, qop, ha2_hex}));
}
// The legacy chain for a request that carried no qop (RFC 2617
// section 3.2.2.1 compatibility): response = H(HA1:nonce:HA2).
inline std::string compute_response_legacy(
const hash_descriptor& descriptor, std::string_view ha1_hex,
std::string_view nonce, std::string_view ha2_hex) {
return descriptor.hex_of(
response_detail::colon_joined({ha1_hex, nonce, ha2_hex}));
}
} // namespace digest
} // namespace detail
} // namespace httpserver
#endif // SRC_HTTPSERVER_DETAIL_DIGEST_RESPONSE_HPP_