Branch data Line data Source code
1 : : // Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
2 : : //
3 : : // Permission to use, copy, modify, and/or distribute this software for any
4 : : // purpose with or without fee is hereby granted, provided that the above
5 : : // copyright notice and this permission notice appear in all copies.
6 : : //
7 : : // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
8 : : // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
9 : : // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
10 : : // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
11 : : // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
12 : : // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
13 : : // PERFORMANCE OF THIS SOFTWARE.
14 : :
15 : : #include <cryptolink.h>
16 : : #include <cryptolink/crypto_hmac.h>
17 : :
18 : : #include <boost/scoped_ptr.hpp>
19 : :
20 : : #include <botan/version.h>
21 : : #include <botan/botan.h>
22 : : #include <botan/hmac.h>
23 : : #include <botan/hash.h>
24 : : #include <botan/types.h>
25 : :
26 : : #include <cstring>
27 : :
28 : : namespace {
29 : : const char*
30 : : getBotanHashAlgorithmName(isc::cryptolink::HashAlgorithm algorithm) {
31 [ + + + + : 785 : switch (algorithm) {
+ + - + ]
32 : : case isc::cryptolink::MD5:
33 : : return ("MD5");
34 : : break;
35 : : case isc::cryptolink::SHA1:
36 : : return ("SHA-1");
37 : : break;
38 : : case isc::cryptolink::SHA256:
39 : : return ("SHA-256");
40 : : break;
41 : : case isc::cryptolink::SHA224:
42 : : return ("SHA-224");
43 : : break;
44 : : case isc::cryptolink::SHA384:
45 : : return ("SHA-384");
46 : : break;
47 : : case isc::cryptolink::SHA512:
48 : : return ("SHA-512");
49 : : break;
50 : : case isc::cryptolink::UNKNOWN_HASH:
51 : : return ("Unknown");
52 : : break;
53 : : }
54 : : // compiler should have prevented us to reach this, since we have
55 : : // no default. But we need a return value anyway
56 : : return ("Unknown");
57 : : }
58 : :
59 : : } // local namespace
60 : :
61 : :
62 : : namespace isc {
63 : : namespace cryptolink {
64 : :
65 : : class HMACImpl {
66 : : public:
67 : 785 : explicit HMACImpl(const void* secret, size_t secret_len,
68 : 793 : const HashAlgorithm hash_algorithm) {
69 : : Botan::HashFunction* hash;
70 : : try {
71 : : hash = Botan::get_hash(
72 [ + - ][ + + ]: 785 : getBotanHashAlgorithmName(hash_algorithm));
73 : 6 : } catch (const Botan::Algorithm_Not_Found&) {
74 [ - + ][ - + ]: 6 : isc_throw(isc::cryptolink::UnsupportedAlgorithm,
[ - + ]
75 : : "Unknown hash algorithm: " <<
76 : : static_cast<int>(hash_algorithm));
77 [ - + - ]: 3 : } catch (const Botan::Exception& exc) {
78 [ # # ][ # # ]: 0 : isc_throw(isc::cryptolink::LibraryError, exc.what());
79 : : }
80 : :
81 [ + - ][ + - ]: 782 : hmac_.reset(new Botan::HMAC(hash));
82 : :
83 : : // If the key length is larger than the block size, we hash the
84 : : // key itself first.
85 : : try {
86 : : // use a temp var so we don't have blocks spanning
87 : : // preprocessor directives
88 : : #if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,9,0)
89 : : size_t block_length = hash->hash_block_size();
90 : : #elif BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,8,0)
91 : 782 : size_t block_length = hash->HASH_BLOCK_SIZE;
92 : : #else
93 : : #error "Unsupported Botan version (need 1.8 or higher)"
94 : : // added to suppress irrelevant compiler errors
95 : : size_t block_length = 0;
96 : : #endif
97 [ + + ]: 782 : if (secret_len > block_length) {
98 : : Botan::SecureVector<Botan::byte> hashed_key =
99 : : hash->process(static_cast<const Botan::byte*>(secret),
100 : 112 : secret_len);
101 [ + - ]: 112 : hmac_->set_key(hashed_key.begin(), hashed_key.size());
102 : : } else {
103 : : // Botan 1.8 considers len 0 a bad key. 1.9 does not,
104 : : // but we won't accept it anyway, and fail early
105 [ + + ]: 670 : if (secret_len == 0) {
106 [ + - ][ + - ]: 10 : isc_throw(BadKey, "Bad HMAC secret length: 0");
107 : : }
108 : 665 : hmac_->set_key(static_cast<const Botan::byte*>(secret),
109 [ + - ]: 665 : secret_len);
110 : : }
111 : 0 : } catch (const Botan::Invalid_Key_Length& ikl) {
112 [ # # ][ # # ]: 0 : isc_throw(BadKey, ikl.what());
113 [ + - - ]: 5 : } catch (const Botan::Exception& exc) {
114 [ # # ][ # # ]: 0 : isc_throw(isc::cryptolink::LibraryError, exc.what());
115 : : }
116 : 777 : }
117 : :
118 : 777 : ~HMACImpl() { }
119 : :
120 : : size_t getOutputLength() const {
121 : : #if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,9,0)
122 : : return (hmac_->output_length());
123 : : #elif BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,8,0)
124 : 1011 : return (hmac_->OUTPUT_LENGTH);
125 : : #else
126 : : #error "Unsupported Botan version (need 1.8 or higher)"
127 : : // added to suppress irrelevant compiler errors
128 : : return 0;
129 : : #endif
130 : : }
131 : :
132 : 1061 : void update(const void* data, const size_t len) {
133 : : try {
134 : 1061 : hmac_->update(static_cast<const Botan::byte*>(data), len);
135 [ # # ]: 0 : } catch (const Botan::Exception& exc) {
136 [ # # ][ # # ]: 0 : isc_throw(isc::cryptolink::LibraryError, exc.what());
137 : : }
138 : 1061 : }
139 : :
140 : 97 : void sign(isc::util::OutputBuffer& result, size_t len) {
141 : : try {
142 [ + - ]: 97 : Botan::SecureVector<Botan::byte> b_result(hmac_->final());
143 : :
144 [ + + ][ + + ]: 97 : if (len == 0 || len > b_result.size()) {
[ + + ]
145 : 15 : len = b_result.size();
146 : : }
147 : 97 : result.writeData(b_result.begin(), len);
148 [ # # ]: 0 : } catch (const Botan::Exception& exc) {
149 [ # # ][ # # ]: 0 : isc_throw(isc::cryptolink::LibraryError, exc.what());
150 : : }
151 : 97 : }
152 : :
153 : 36 : void sign(void* result, size_t len) {
154 : : try {
155 [ + - ]: 36 : Botan::SecureVector<Botan::byte> b_result(hmac_->final());
156 : 36 : size_t output_size = getOutputLength();
157 [ - + ]: 36 : if (output_size > len) {
158 : 0 : output_size = len;
159 : : }
160 : 36 : std::memcpy(result, b_result.begin(), output_size);
161 [ # # ]: 0 : } catch (const Botan::Exception& exc) {
162 [ # # ][ # # ]: 0 : isc_throw(isc::cryptolink::LibraryError, exc.what());
163 : : }
164 : 36 : }
165 : :
166 : 235 : std::vector<uint8_t> sign(size_t len) {
167 : : try {
168 [ + - ]: 235 : Botan::SecureVector<Botan::byte> b_result(hmac_->final());
169 [ + + ][ + + ]: 235 : if (len == 0 || len > b_result.size()) {
[ + + ]
170 [ + - ]: 197 : return (std::vector<uint8_t>(b_result.begin(), b_result.end()));
171 : : } else {
172 [ + - ]: 38 : return (std::vector<uint8_t>(b_result.begin(), &b_result[len]));
173 : : }
174 [ # # ]: 0 : } catch (const Botan::Exception& exc) {
175 [ # # ][ # # ]: 0 : isc_throw(isc::cryptolink::LibraryError, exc.what());
176 : : }
177 : : }
178 : :
179 : :
180 : 346 : bool verify(const void* sig, size_t len) {
181 : : // Botan's verify_mac checks if len matches the output_length,
182 : : // which causes it to fail for truncated signatures, so we do
183 : : // the check ourselves
184 : : // SEE BELOW FOR TEMPORARY CHANGE
185 : : try {
186 [ + - ]: 346 : Botan::SecureVector<Botan::byte> our_mac = hmac_->final();
187 [ + + ]: 346 : if (len < getOutputLength()) {
188 : : // Currently we don't support truncated signature in TSIG (see
189 : : // #920). To avoid validating too short signature accidently,
190 : : // we enforce the standard signature size for the moment.
191 : : // Once we support truncation correctly, this if-clause should
192 : : // (and the capitalized comment above) be removed.
193 : : return (false);
194 : : }
195 [ + - + - ]: 688 : if (len == 0 || len > getOutputLength()) {
[ - + ]
196 : 0 : len = getOutputLength();
197 : : }
198 : 344 : return (Botan::same_mem(&our_mac[0],
199 : : static_cast<const unsigned char*>(sig),
200 : 344 : len));
201 [ # # ]: 0 : } catch (const Botan::Exception& exc) {
202 [ # # ][ # # ]: 0 : isc_throw(isc::cryptolink::LibraryError, exc.what());
203 : : }
204 : : }
205 : :
206 : : private:
207 : : boost::scoped_ptr<Botan::HMAC> hmac_;
208 : : };
209 : :
210 : 785 : HMAC::HMAC(const void* secret, size_t secret_length,
211 : : const HashAlgorithm hash_algorithm)
212 : : {
213 [ + + ]: 785 : impl_ = new HMACImpl(secret, secret_length, hash_algorithm);
214 : 777 : }
215 : :
216 : 777 : HMAC::~HMAC() {
217 [ + - ]: 1554 : delete impl_;
218 : 777 : }
219 : :
220 : : size_t
221 : 285 : HMAC::getOutputLength() const {
222 : 285 : return (impl_->getOutputLength());
223 : : }
224 : :
225 : : void
226 : 1061 : HMAC::update(const void* data, const size_t len) {
227 : 1061 : impl_->update(data, len);
228 : 1061 : }
229 : :
230 : : void
231 : 97 : HMAC::sign(isc::util::OutputBuffer& result, size_t len) {
232 : 97 : impl_->sign(result, len);
233 : 97 : }
234 : :
235 : : void
236 : 36 : HMAC::sign(void* result, size_t len) {
237 : 36 : impl_->sign(result, len);
238 : 36 : }
239 : :
240 : : std::vector<uint8_t>
241 : 235 : HMAC::sign(size_t len) {
242 : 235 : return impl_->sign(len);
243 : : }
244 : :
245 : : bool
246 : 346 : HMAC::verify(const void* sig, const size_t len) {
247 : 346 : return (impl_->verify(sig, len));
248 : : }
249 : :
250 : : void
251 : 38 : signHMAC(const void* data, const size_t data_len, const void* secret,
252 : : size_t secret_len, const HashAlgorithm hash_algorithm,
253 : : isc::util::OutputBuffer& result, size_t len)
254 : : {
255 : : boost::scoped_ptr<HMAC> hmac(
256 : 38 : CryptoLink::getCryptoLink().createHMAC(secret,
257 : : secret_len,
258 : 74 : hash_algorithm));
259 [ + - ]: 36 : hmac->update(data, data_len);
260 [ + - ]: 36 : hmac->sign(result, len);
261 : 36 : }
262 : :
263 : :
264 : : bool
265 : 74 : verifyHMAC(const void* data, const size_t data_len, const void* secret,
266 : : size_t secret_len, const HashAlgorithm hash_algorithm,
267 : : const void* sig, const size_t sig_len)
268 : : {
269 : : boost::scoped_ptr<HMAC> hmac(
270 : 74 : CryptoLink::getCryptoLink().createHMAC(secret,
271 : : secret_len,
272 : 146 : hash_algorithm));
273 [ + - ]: 72 : hmac->update(data, data_len);
274 [ + - ]: 144 : return (hmac->verify(sig, sig_len));
275 : : }
276 : :
277 : : void
278 : 669 : deleteHMAC(HMAC* hmac) {
279 [ + - ]: 669 : delete hmac;
280 : 669 : }
281 : :
282 : : } // namespace cryptolink
283 : 4375 : } // namespace isc
|