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>
10 #include <cryptolink/crypto_hmac.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 
22 namespace isc {
23 namespace cryptolink {
24 
27 class HMACImpl {
28 public:
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 
194 private:
196  HashAlgorithm hash_algorithm_;
197 
199  boost::scoped_ptr<Botan::HMAC> hmac_;
200 
202  Botan::SecureVector<Botan::byte> digest_;
203 };
204 
205 HMAC::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 
220 size_t
222  return (impl_->getOutputLength());
223 }
224 
225 void
226 HMAC::update(const void* data, const size_t len) {
227  impl_->update(data, len);
228 }
229 
230 void
231 HMAC::sign(isc::util::OutputBuffer& result, size_t len) {
232  impl_->sign(result, len);
233 }
234 
235 void
236 HMAC::sign(void* result, size_t len) {
237  impl_->sign(result, len);
238 }
239 
240 std::vector<uint8_t>
241 HMAC::sign(size_t len) {
242  return impl_->sign(len);
243 }
244 
245 bool
246 HMAC::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.
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.
The OutputBuffer class is a buffer abstraction for manipulating mutable data.
Definition: buffer.h:294
Defines the logger used by the top-level component of kea-dhcp-ddns.