1 /**
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License. You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied. See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  */
19 
20 /*
21  * XSEC
22  *
23  * WinCAPICryptoHashHMAC := Windows CAPI Implementation of Message digests
24  *
25  * Author(s): Berin Lautenbach
26  *
27  * $Id: WinCAPICryptoHashHMAC.cpp 1833341 2018-06-11 16:25:41Z scantor $
28  *
29  */
30 
31 #include <xsec/enc/XSECCryptoException.hpp>
32 #include <xsec/enc/WinCAPI/WinCAPICryptoHashHMAC.hpp>
33 #include <xsec/enc/WinCAPI/WinCAPICryptoProvider.hpp>
34 #include <xsec/enc/WinCAPI/WinCAPICryptoKeyHMAC.hpp>
35 
36 #if defined (XSEC_HAVE_WINCAPI)
37 
38 #include "../../utils/XSECDOMUtils.hpp"
39 
40 #include <memory.h>
41 
42 // --------------------------------------------------------------------------------
43 //           IPAD/OPAD definitions
44 // --------------------------------------------------------------------------------
45 
46 static unsigned char ipad[] = {
47 
48 	0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
49 	0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
50 	0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
51 	0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
52 	0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
53 	0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
54 	0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
55 	0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
56 };
57 
58 static unsigned char opad[] = {
59 
60 	0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
61 	0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
62 	0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
63 	0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
64 	0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
65 	0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
66 	0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
67 	0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
68 };
69 
70 // --------------------------------------------------------------------------------
71 //           Constructors/Destructors
72 // --------------------------------------------------------------------------------
73 
WinCAPICryptoHashHMAC(HCRYPTPROV prov,HashType alg)74 WinCAPICryptoHashHMAC::WinCAPICryptoHashHMAC(HCRYPTPROV prov, HashType alg) {
75 
76 	m_p = prov;
77 	m_h = 0;
78 	m_blockSize = 64;		// We only know SHA-1 and MD5 at this time - both are 64 bytes
79 
80 	switch (alg) {
81 
82 	case (XSECCryptoHash::HASH_SHA1) :
83 
84 		m_algId = CALG_SHA;
85 		break;
86 
87 	case (XSECCryptoHash::HASH_MD5) :
88 
89 		m_algId = CALG_MD5;
90 		break;
91 
92 	default :
93 
94 		m_algId = 0;
95 
96 	}
97 
98 	if(m_algId == 0) {
99 
100 		throw XSECCryptoException(XSECCryptoException::MDError,
101 			"WinCAPI:Hash - Unknown algorithm");
102 	}
103 
104 	m_hashType = alg;
105 
106 
107 }
108 
reset()109 void WinCAPICryptoHashHMAC::reset() {
110 
111 	if (m_h != 0)
112 		CryptDestroyHash(m_h);
113 
114 }
115 
~WinCAPICryptoHashHMAC()116 WinCAPICryptoHashHMAC::~WinCAPICryptoHashHMAC() {
117 
118 	if (m_h != 0)
119 		CryptDestroyHash(m_h);
120 
121 }
122 
123 // --------------------------------------------------------------------------------
124 //           Key manipulation
125 // --------------------------------------------------------------------------------
126 
eraseKeys(void)127 void WinCAPICryptoHashHMAC::eraseKeys(void) {
128 
129 	// Overwrite the ipad/opad calculated key values
130 	unsigned char * i = m_ipadKeyed;
131 	unsigned char * j = m_opadKeyed;
132 
133 	for (unsigned int k = 0; k < XSEC_MAX_HASH_BLOCK_SIZE; ++k) {
134 		*i++ = 0;
135 		*j++ = 0;
136 	}
137 
138 }
139 
setKey(const XSECCryptoKey * key)140 void WinCAPICryptoHashHMAC::setKey(const XSECCryptoKey *key) {
141 
142 
143 	BOOL fResult;
144 
145 	// Use this to initialise the ipadKeyed/opadKeyed values
146 
147 	if (key->getKeyType() != XSECCryptoKey::KEY_HMAC) {
148 
149 		throw XSECCryptoException(XSECCryptoException::MDError,
150 			"WinCAPI:HashHMAC - Non HMAC Key passed to HashHMAC");
151 
152 	}
153 
154 	if (m_blockSize > XSEC_MAX_HASH_BLOCK_SIZE) {
155 
156 		throw XSECCryptoException(XSECCryptoException::MDError,
157 			"WinCAPI:HashHMAC - Internal error - have got a blocksize bigger than I can handle");
158 
159 	}
160 
161 	// Check to see if this is an internal Windows Key
162 	if (strEquals(key->getProviderName(), DSIGConstants::s_unicodeStrPROVWinCAPI) &&
163 		((WinCAPICryptoKeyHMAC *) key)->getWinKey() != 0) {
164 
165 		// Over-ride the local provider for this
166 
167 		HCRYPTPROV p = ((WinCAPICryptoKeyHMAC *) key)->getWinKeyProv();
168 		HCRYPTKEY k = ((WinCAPICryptoKeyHMAC *) key)->getWinKey();
169 
170 		fResult = CryptCreateHash(
171 			p,
172 			CALG_HMAC,
173 			k,
174 			0,
175 			&m_h);
176 
177 		if (fResult == 0 || m_h == 0) {
178 			DWORD error = GetLastError();
179 			throw XSECCryptoException(XSECCryptoException::MDError,
180 				"WinCAPI:Hash::setKey - Error creating internally keyed hash object");
181 		}
182 
183 		// Set the HMAC algorithm
184 		HMAC_INFO hi;
185 
186 		hi.HashAlgid = m_algId;
187 		hi.pbInnerString = NULL;		// Use default inner and outer strings
188 		hi.cbInnerString = 0;
189 		hi.pbOuterString = NULL;
190 		hi.cbOuterString = 0;
191 
192 		fResult = CryptSetHashParam(
193 			m_h,
194 			HP_HMAC_INFO,
195 			(BYTE *) &hi,
196 			0);
197 
198 		if (fResult == 0 || m_h == 0) {
199 			DWORD error = GetLastError();
200 			throw XSECCryptoException(XSECCryptoException::MDError,
201 				"WinCAPI:Hash::setKey - Error setting HASH_INFO object");
202 		}
203 
204 
205 
206 		return;
207 
208 	}
209 
210 	// Need to load from raw bit string
211 
212 	safeBuffer keyBuf;
213 	unsigned int keyLen = ((XSECCryptoKeyHMAC *) key)->getKey(keyBuf);
214 
215 	if (keyLen > m_blockSize) {
216 
217 		HCRYPTHASH h;
218 
219 		fResult = CryptCreateHash(
220 			m_p,
221 			m_algId,
222 			0,
223 			0,
224 			&h);
225 
226 		if (fResult == 0 || h == 0) {
227 			throw XSECCryptoException(XSECCryptoException::MDError,
228 				"WinCAPI:Hash::setKey - Error creating hash object");
229 		}
230 
231 		fResult = CryptHashData(
232 			h,
233 			keyBuf.rawBuffer(),
234 			keyLen,
235 			0);
236 
237 		if (fResult == 0 || h == 0) {
238 			if (h)
239 				CryptDestroyHash(h);
240 			throw XSECCryptoException(XSECCryptoException::MDError,
241 				"WinCAPI:Hash::setKey - Error hashing key data");
242 		}
243 
244 		BYTE outData[XSEC_MAX_HASH_SIZE];
245 		DWORD outDataLen = XSEC_MAX_HASH_SIZE;
246 
247 		CryptGetHashParam(
248 			h,
249 			HP_HASHVAL,
250 			outData,
251 			&outDataLen,
252 			0);
253 
254 		if (fResult == 0 || h == 0) {
255 			if (h)
256 				CryptDestroyHash(h);
257 			throw XSECCryptoException(XSECCryptoException::MDError,
258 				"WinCAPI:Hash::setKey - Error getting hash result");
259 		}
260 
261 		keyBuf.sbMemcpyIn(outData, outDataLen);
262 		keyLen = outDataLen;
263 
264 		if (h)
265 			CryptDestroyHash(h);
266 
267 
268 	}
269 
270 	// Now create the ipad and opad keyed values
271 	memcpy(m_ipadKeyed, ipad, m_blockSize);
272 	memcpy(m_opadKeyed, opad, m_blockSize);
273 
274 	// XOR with the key
275 	for (unsigned int i = 0; i < keyLen; ++i) {
276 		m_ipadKeyed[i] = keyBuf[i] ^ m_ipadKeyed[i];
277 		m_opadKeyed[i] = keyBuf[i] ^ m_opadKeyed[i];
278 	}
279 
280 
281 	// Now create the hash object, and start with the ipad operation
282 	fResult = CryptCreateHash(
283 		m_p,
284 		m_algId,
285 		0,
286 		0,
287 		&m_h);
288 
289 	if (fResult == 0 || m_h == 0) {
290 		throw XSECCryptoException(XSECCryptoException::MDError,
291 			"WinCAPI:HashHMAC - Error creating hash object");
292 	}
293 
294 	fResult = CryptHashData(
295 		m_h,
296 		m_ipadKeyed,
297 		m_blockSize,
298 		0);
299 
300 	if (fResult == 0 || m_h == 0) {
301 		throw XSECCryptoException(XSECCryptoException::MDError,
302 			"WinCAPI:HashHMAC - Error performing initial ipad digest");
303 	}
304 
305 }
306 
307 // --------------------------------------------------------------------------------
308 //           Hash operations
309 // --------------------------------------------------------------------------------
310 
hash(unsigned char * data,unsigned int length)311 void WinCAPICryptoHashHMAC::hash(unsigned char * data,
312 								 unsigned int length) {
313 
314 	if (m_h == 0) {
315 		throw XSECCryptoException(XSECCryptoException::MDError,
316 			"WinCAPI:HashHMAC::hash() - Called prior to setting key");
317 	}
318 
319 	BOOL fResult = CryptHashData(
320 		m_h,
321 		data,
322 		length,
323 		0);
324 
325 	if (fResult == 0) {
326 		throw XSECCryptoException(XSECCryptoException::MDError,
327 			"WinCAPI:Hash - Error Hashing Data");
328 	}
329 
330 }
331 
finish(unsigned char * hash,unsigned int maxLength)332 unsigned int WinCAPICryptoHashHMAC::finish(unsigned char * hash,
333 									   unsigned int maxLength) {
334 
335 	DWORD retLen;
336 	BOOL fResult;
337 
338 	retLen = XSEC_MAX_HASH_SIZE;
339 
340 	fResult = CryptGetHashParam(
341 		m_h,
342 		HP_HASHVAL,
343 		m_mdValue,
344 		&retLen,
345 		0);
346 
347 	if (fResult == 0) {
348 		throw XSECCryptoException(XSECCryptoException::MDError,
349 			"WinCAPI:Hash - Error getting hash value");
350 	}
351 
352 	// Perform the opad operation
353 	HCRYPTHASH h;
354 	fResult = CryptCreateHash(
355 		m_p,
356 		m_algId,
357 		0,
358 		0,
359 		&h);
360 
361 	if (fResult == 0 || h == 0) {
362 		throw XSECCryptoException(XSECCryptoException::MDError,
363 			"WinCAPI:Hash::finish - Error creating hash object for opad operation");
364 	}
365 
366 	fResult = CryptHashData(
367 		h,
368 		m_opadKeyed,
369 		m_blockSize,
370 		0);
371 
372 	if (fResult == 0 || h == 0) {
373 		if (h)
374 			CryptDestroyHash(h);
375 		throw XSECCryptoException(XSECCryptoException::MDError,
376 			"WinCAPI:Hash::finish - Error hashing opad data");
377 	}
378 
379 	fResult = CryptHashData(
380 		h,
381 		m_mdValue,
382 		retLen,
383 		0);
384 
385 	if (fResult == 0 || h == 0) {
386 		if (h)
387 			CryptDestroyHash(h);
388 		throw XSECCryptoException(XSECCryptoException::MDError,
389 			"WinCAPI:Hash::finish - Error hashing ipad hash to opad");
390 	}
391 
392 	// Read out the final hash
393 	retLen = XSEC_MAX_HASH_SIZE;
394 
395 	fResult = CryptGetHashParam(
396 		h,
397 		HP_HASHVAL,
398 		m_mdValue,
399 		&retLen,
400 		0);
401 
402 	CryptDestroyHash(h);
403 
404 	m_mdLen = retLen;
405 	retLen = (maxLength > m_mdLen ? m_mdLen : maxLength);
406 	memcpy(hash, m_mdValue, retLen);
407 
408 	return (unsigned int) retLen;
409 
410 }
411 
412 // Get information
413 
getHashType(void) const414 XSECCryptoHash::HashType WinCAPICryptoHashHMAC::getHashType(void) const {
415 
416 	return m_hashType;			// This could be any kind of hash
417 
418 }
419 
420 #endif /* XSEC_HAVE_WINCAPI */
421