Kea 1.5.0
botan1_hmac.cc
Go to the documentation of this file.
1// Copyright (C) 2011-2018 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 <boost/scoped_ptr.hpp>
13
14#include <botan/version.h>
15#include <botan/botan.h>
16#include <botan/hmac.h>
17#include <botan/hash.h>
18#include <botan/types.h>
19
21
22namespace isc {
23namespace cryptolink {
24
27class HMACImpl {
28public:
36 explicit HMACImpl(const void* secret, size_t secret_len,
37 const HashAlgorithm hash_algorithm)
38 : hash_algorithm_(hash_algorithm), hmac_() {
39 Botan::HashFunction* hash;
40 try {
41 const std::string& name =
42 btn::getHashAlgorithmName(hash_algorithm);
43 hash = Botan::get_hash(name);
44 } catch (const Botan::Algorithm_Not_Found&) {
46 "Unknown hash algorithm: " <<
47 static_cast<int>(hash_algorithm));
48 } catch (const Botan::Exception& exc) {
49 isc_throw(LibraryError, "Botan error: " << exc.what());
50 }
51
52 hmac_.reset(new Botan::HMAC(hash));
53
54 // If the key length is larger than the block size, we hash the
55 // key itself first.
56 try {
57 // use a temp var so we don't have blocks spanning
58 // preprocessor directives
59#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,9,0)
60 size_t block_length = hash->hash_block_size();
61#else
62#error "Unsupported Botan version (need 1.9 or higher)"
63 // added to suppress irrelevant compiler errors
64 size_t block_length = 0;
65#endif
66 if (secret_len > block_length) {
67 Botan::SecureVector<Botan::byte> hashed_key =
68 hash->process(static_cast<const Botan::byte*>(secret),
69 secret_len);
70 hmac_->set_key(&hashed_key[0], hashed_key.size());
71 } else {
72 // Botan 1.8 considers len 0 a bad key. 1.9 does not,
73 // but we won't accept it anyway, and fail early
74 if (secret_len == 0) {
75 isc_throw(BadKey, "Bad HMAC secret length: 0");
76 }
77 hmac_->set_key(static_cast<const Botan::byte*>(secret),
78 secret_len);
79 }
80 } catch (const Botan::Invalid_Key_Length& ikl) {
81 isc_throw(BadKey, ikl.what());
82 } catch (const Botan::Exception& exc) {
83 isc_throw(LibraryError, "Botan error: " << exc.what());
84 }
85 }
86
89 }
90
93 return (hash_algorithm_);
94 }
95
99 size_t getOutputLength() const {
100#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,9,0)
101 return (hmac_->output_length());
102#else
103#error "Unsupported Botan version (need 1.9 or higher)"
104 // added to suppress irrelevant compiler errors
105 return 0;
106#endif
107 }
108
112 void update(const void* data, const size_t len) {
113 try {
114 hmac_->update(static_cast<const Botan::byte*>(data), len);
115 } catch (const Botan::Exception& exc) {
116 isc_throw(LibraryError, "Botan error: " << exc.what());
117 }
118 }
119
123 void sign(isc::util::OutputBuffer& result, size_t len) {
124 try {
125 Botan::SecureVector<Botan::byte> b_result(hmac_->final());
126
127 if (len > b_result.size()) {
128 len = b_result.size();
129 }
130 result.writeData(&b_result[0], len);
131 } catch (const Botan::Exception& exc) {
132 isc_throw(LibraryError, "Botan error: " << exc.what());
133 }
134 }
135
139 void sign(void* result, size_t len) {
140 try {
141 Botan::SecureVector<Botan::byte> b_result(hmac_->final());
142 size_t output_size = getOutputLength();
143 if (output_size > len) {
144 output_size = len;
145 }
146 std::memcpy(result, &b_result[0], output_size);
147 } catch (const Botan::Exception& exc) {
148 isc_throw(LibraryError, "Botan error: " << exc.what());
149 }
150 }
151
155 std::vector<uint8_t> sign(size_t len) {
156 try {
157 Botan::SecureVector<Botan::byte> b_result(hmac_->final());
158 if (len > b_result.size()) {
159 len = b_result.size();
160 }
161 return (std::vector<uint8_t>(&b_result[0], &b_result[len]));
162 } catch (const Botan::Exception& exc) {
163 isc_throw(LibraryError, "Botan error: " << exc.what());
164 }
165 }
166
167
171 bool verify(const void* sig, size_t len) {
172 // Botan's verify_mac checks if len matches the output_length,
173 // which causes it to fail for truncated signatures, so we do
174 // the check ourselves
175 try {
176 size_t size = getOutputLength();
177 if (len < 10 || len < size / 2) {
178 return (false);
179 }
180 if (len > size) {
181 len = size;
182 }
183 if (digest_.size() == 0) {
184 digest_ = hmac_->final();
185 }
186 return (Botan::same_mem(&digest_[0],
187 static_cast<const unsigned char*>(sig),
188 len));
189 } catch (const Botan::Exception& exc) {
190 isc_throw(LibraryError, "Botan error: " << exc.what());
191 }
192 }
193
194private:
196 HashAlgorithm hash_algorithm_;
197
199 boost::scoped_ptr<Botan::HMAC> hmac_;
200
202 Botan::SecureVector<Botan::byte> digest_;
203};
204
205HMAC::HMAC(const void* secret, size_t secret_length,
206 const HashAlgorithm hash_algorithm)
207{
208 impl_ = new HMACImpl(secret, secret_length, hash_algorithm);
209}
210
212 delete impl_;
213}
214
217 return (impl_->getHashAlgorithm());
218}
219
220size_t
222 return (impl_->getOutputLength());
223}
224
225void
226HMAC::update(const void* data, const size_t len) {
227 impl_->update(data, len);
228}
229
230void
232 impl_->sign(result, len);
233}
234
235void
236HMAC::sign(void* result, size_t len) {
237 impl_->sign(result, len);
238}
239
240std::vector<uint8_t>
241HMAC::sign(size_t len) {
242 return impl_->sign(len);
243}
244
245bool
246HMAC::verify(const void* sig, const size_t len) {
247 return (impl_->verify(sig, len));
248}
249
250} // namespace cryptolink
251} // 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:294
void writeData(const void *data, size_t len)
Copy an arbitrary length of data into the buffer.
Definition: buffer.h:547
#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-dhcp-ddns.