Kea 3.1.1
botan_hmac.cc
Go to the documentation of this file.
1// Copyright (C) 2011-2025 Internet Systems Consortium, Inc. ("ISC")
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7#include <config.h>
8
9#include <cryptolink.h>
11
12#include <botan/mac.h>
13#include <botan/mem_ops.h>
14#include <botan/exceptn.h>
15
17
18namespace isc {
19namespace cryptolink {
20
21const std::string
23 switch (algorithm) {
25 return ("HMAC(MD5)");
27 return ("HMAC(SHA-1)");
29 return ("HMAC(SHA-256)");
31 return ("HMAC(SHA-224)");
33 return ("HMAC(SHA-384)");
35 return ("HMAC(SHA-512)");
37 return ("HMAC(Unknown)");
38 }
39 // compiler should have prevented us to reach this, since we have
40 // no default. But we need a return value anyway
41 return ("Unknown");
42}
43
46class HMACImpl {
47public:
55 explicit HMACImpl(const void* secret, size_t secret_len,
56 const HashAlgorithm hash_algorithm)
57 : hash_algorithm_(hash_algorithm), hmac_() {
58 try {
59 const std::string& name =
60 btn::getHmacAlgorithmName(hash_algorithm);
61 hmac_ = Botan::MessageAuthenticationCode::create_or_throw(name);
62 } catch (const Botan::Lookup_Error&) {
64 "Unknown hash algorithm: " <<
65 static_cast<int>(hash_algorithm));
66 } catch (const Botan::Exception& exc) {
67 isc_throw(LibraryError, "Botan error: " << exc.what());
68 }
69
70 try {
71 // Botan 1.8 considers len 0 a bad key. 1.9 does not,
72 // but we won't accept it anyway, and fail early
73 if (secret_len == 0) {
74 isc_throw(BadKey, "Bad HMAC secret length: 0");
75 }
76 hmac_->set_key(static_cast<const Botan::byte*>(secret),
77 secret_len);
78 } catch (const Botan::Invalid_Key_Length& ikl) {
79 isc_throw(BadKey, ikl.what());
80 } catch (const Botan::Exception& exc) {
81 isc_throw(LibraryError, "Botan error: " << exc.what());
82 }
83 }
84
86 ~HMACImpl() = default;
87
90 return (hash_algorithm_);
91 }
92
96 size_t getOutputLength() const {
97 return (hmac_->output_length());
98 }
99
103 void update(const void* data, const size_t len) {
104 try {
105 hmac_->update(static_cast<const Botan::byte*>(data), len);
106 } catch (const Botan::Exception& exc) {
107 isc_throw(LibraryError, "Botan error: " << exc.what());
108 }
109 }
110
114 void sign(isc::util::OutputBuffer& result, size_t len) {
115 try {
116 Botan::secure_vector<Botan::byte> b_result(hmac_->final());
117
118 if (len > b_result.size()) {
119 len = b_result.size();
120 }
121 result.writeData(&b_result[0], len);
122 } catch (const Botan::Exception& exc) {
123 isc_throw(LibraryError, "Botan error: " << exc.what());
124 }
125 }
126
130 void sign(void* result, size_t len) {
131 try {
132 Botan::secure_vector<Botan::byte> b_result(hmac_->final());
133 size_t output_size = getOutputLength();
134 if (output_size > len) {
135 output_size = len;
136 }
137 std::memcpy(result, &b_result[0], output_size);
138 } catch (const Botan::Exception& exc) {
139 isc_throw(LibraryError, "Botan error: " << exc.what());
140 }
141 }
142
146 std::vector<uint8_t> sign(size_t len) {
147 try {
148 Botan::secure_vector<Botan::byte> b_result(hmac_->final());
149 if (len > b_result.size()) {
150 len = b_result.size();
151 }
152 // Return vector with content. Construct &b_result[len] attempts
153 // to get an address of one element beyond the b_result. Replaced
154 // with the address of first element + len
155 return (std::vector<uint8_t>(&b_result[0], &b_result[0]+len));
156 } catch (const Botan::Exception& exc) {
157 isc_throw(LibraryError, "Botan error: " << exc.what());
158 }
159 }
160
161
165 bool verify(const void* sig, size_t len) {
166 // Botan's verify_mac checks if len matches the output_length,
167 // which causes it to fail for truncated signatures, so we do
168 // the check ourselves
169 try {
170 size_t size = getOutputLength();
171 if (len < 10 || len < size / 2) {
172 return (false);
173 }
174 if (len > size) {
175 len = size;
176 }
177 if (digest_.size() == 0) {
178 digest_ = hmac_->final();
179 }
180 const uint8_t* sig8 = static_cast<const uint8_t*>(sig);
181 return (Botan::constant_time_compare(&digest_[0], sig8, len));
182 } catch (const Botan::Exception& exc) {
183 isc_throw(LibraryError, "Botan error: " << exc.what());
184 }
185 }
186
187private:
189 HashAlgorithm hash_algorithm_;
190
192 std::unique_ptr<Botan::MessageAuthenticationCode> hmac_;
193
195 Botan::secure_vector<Botan::byte> digest_;
196};
197
198HMAC::HMAC(const void* secret, size_t secret_length,
199 const HashAlgorithm hash_algorithm)
200{
201 impl_ = new HMACImpl(secret, secret_length, hash_algorithm);
202}
203
205 delete impl_;
206}
207
210 return (impl_->getHashAlgorithm());
211}
212
213size_t
215 return (impl_->getOutputLength());
216}
217
218void
219HMAC::update(const void* data, const size_t len) {
220 impl_->update(data, len);
221}
222
223void
225 impl_->sign(result, len);
226}
227
228void
229HMAC::sign(void* result, size_t len) {
230 impl_->sign(result, len);
231}
232
233std::vector<uint8_t>
234HMAC::sign(size_t len) {
235 return impl_->sign(len);
236}
237
238bool
239HMAC::verify(const void* sig, const size_t len) {
240 return (impl_->verify(sig, len));
241}
242
243} // namespace cryptolink
244} // namespace isc
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
The OutputBuffer class is a buffer abstraction for manipulating mutable data.
Definition buffer.h:346
void writeData(const void *data, size_t len)
Copy an arbitrary length of data into the buffer.
Definition buffer.h:559
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
Defines the logger used by the top-level component of kea-lfc.