• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Hashing functions for CUPS.
3 //
4 // Copyright © 2020-2024 by OpenPrinting.
5 // Copyright © 2015-2019 by Apple Inc.
6 //
7 // Licensed under Apache License v2.0.  See the file "LICENSE" for more
8 // information.
9 //
10 
11 #include "cups-private.h"
12 #include "md5-internal.h"
13 #ifdef HAVE_OPENSSL
14 #  include <openssl/evp.h>
15 #elif defined(HAVE_GNUTLS)
16 #  include <gnutls/crypto.h>
17 #elif __APPLE__
18 #  include <CommonCrypto/CommonDigest.h>
19 #elif _WIN32
20 #  include <windows.h>
21 #  include <bcrypt.h>
22 #endif // HAVE_OPENSSL
23 
24 
25 //
26 // Note: While both GNU TLS and OpenSSL offer HMAC functions, they also exclude
27 // certain hashes depending on the version of library and whatever patches are
28 // applied by the OS vendor/Linux distribution.  Since printers sometimes rely
29 // on otherwise deprecated/obsolete hash functions for things like PIN printing
30 // ("job-password"), and since such uses already have poor security regardless
31 // of the hash function used, it is more important to provide guaranteed
32 // implementations over some imaginary notion of "guaranteed security"...
33 //
34 
35 //
36 // Local functions...
37 //
38 
39 static ssize_t	hash_data(const char *algorithm, unsigned char *hash, size_t hashsize, const void *a, size_t alen, const void *b, size_t blen);
40 
41 
42 //
43 // 'cupsHashData()' - Perform a hash function on the given data.
44 //
45 // This function performs a hash function on the given data. The "algorithm"
46 // argument can be any of the registered, non-deprecated IPP hash algorithms for
47 // the "job-password-encryption" attribute, including "sha" for SHA-1,
48 // "sha2-256" for SHA2-256, etc.
49 //
50 // The "hash" argument points to a buffer of "hashsize" bytes and should be at
51 // least 64 bytes in length for all of the supported algorithms.
52 //
53 // The returned hash is binary data.
54 //
55 
56 ssize_t					// O - Size of hash or -1 on error
cupsHashData(const char * algorithm,const void * data,size_t datalen,unsigned char * hash,size_t hashsize)57 cupsHashData(const char    *algorithm,	// I - Algorithm name
58              const void    *data,	// I - Data to hash
59              size_t        datalen,	// I - Length of data to hash
60              unsigned char *hash,	// I - Hash buffer
61              size_t        hashsize)	// I - Size of hash buffer
62 {
63   if (!algorithm || !data || datalen == 0 || !hash || hashsize == 0)
64   {
65     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad arguments to function"), 1);
66     return (-1);
67   }
68 
69   return (hash_data(algorithm, hash, hashsize, data, datalen, NULL, 0));
70 }
71 
72 
73 //
74 // 'cupsHashString()' - Format a hash value as a hexadecimal string.
75 //
76 // The passed buffer must be at least 2 * hashsize + 1 characters in length.
77 //
78 
79 const char *				// O - Formatted string
cupsHashString(const unsigned char * hash,size_t hashsize,char * buffer,size_t bufsize)80 cupsHashString(
81     const unsigned char *hash,		// I - Hash
82     size_t              hashsize,	// I - Size of hash
83     char                *buffer,	// I - String buffer
84     size_t		bufsize)	// I - Size of string buffer
85 {
86   char		*bufptr = buffer;	// Pointer into buffer
87   static const char *hex = "0123456789abcdef";
88 					// Hex characters (lowercase!)
89 
90 
91   // Range check input...
92   if (!hash || hashsize < 1 || !buffer || bufsize < (2 * hashsize + 1))
93   {
94     if (buffer)
95       *buffer = '\0';
96     return (NULL);
97   }
98 
99   // Loop until we've converted the whole hash...
100   while (hashsize > 0)
101   {
102     *bufptr++ = hex[*hash >> 4];
103     *bufptr++ = hex[*hash & 15];
104 
105     hash ++;
106     hashsize --;
107   }
108 
109   *bufptr = '\0';
110 
111   return (buffer);
112 }
113 
114 
115 //
116 // 'cupsHMACData()' - Perform a HMAC function on the given data.
117 //
118 // This function performs a HMAC function on the given data with the given key.
119 // The "algorithm" argument can be any of the registered, non-deprecated IPP
120 // hash algorithms for the "job-password-encryption" attribute, including
121 // "sha" for SHA-1, "sha2-256" for SHA2-256, etc.
122 //
123 // The "hmac" argument points to a buffer of "hmacsize" bytes and should be at
124 // least 64 bytes in length for all of the supported algorithms.
125 //
126 // The returned HMAC is binary data.
127 //
128 
129 ssize_t					// O - The length of the HMAC or `-1` on error
cupsHMACData(const char * algorithm,const unsigned char * key,size_t keylen,const void * data,size_t datalen,unsigned char * hmac,size_t hmacsize)130 cupsHMACData(
131     const char          *algorithm,	// I - Hash algorithm
132     const unsigned char *key,		// I - Key
133     size_t              keylen,		// I - Length of key
134     const void          *data,		// I - Data to hash
135     size_t              datalen,	// I - Length of data to hash
136     unsigned char       *hmac,		// I - HMAC buffer
137     size_t              hmacsize)	// I - Size of HMAC buffer
138 {
139   size_t	i,			// Looping var
140 		b;			// Block size
141   unsigned char	buffer[128],		// Intermediate buffer
142 		hash[128],		// Hash buffer
143 		hkey[128];		// Hashed key buffer
144   ssize_t	hashlen;		// Length of hash
145 
146 
147   // Range check input...
148   if (!algorithm || !key || keylen == 0 || !data || datalen == 0 || !hmac || hmacsize < 32)
149     return (-1);
150 
151   // Determine the block size...
152   if (!strcmp(algorithm, "sha2-384") || !strncmp(algorithm, "sha2-512", 8))
153     b = 128;
154   else
155     b = 64;
156 
157   // If the key length is larger than the block size, hash it and use that
158   // instead...
159   if (keylen > b)
160   {
161     if ((hashlen = hash_data(algorithm, hkey, sizeof(hkey), key, keylen, NULL, 0)) < 0)
162       return (-1);
163 
164     key    = hkey;
165     keylen = (size_t)hashlen;
166   }
167 
168   // HMAC = H(K' ^ opad, H(K' ^ ipad, data))
169   // K'   = Klen > b ? H(K) : K, padded with 0's
170   // opad = 0x5c, ipad = 0x36
171   for (i = 0; i < b && i < keylen; i ++)
172     buffer[i] = key[i] ^ 0x36;
173   for (; i < b; i ++)
174     buffer[i] = 0x36;
175 
176   if ((hashlen = hash_data(algorithm, hash, sizeof(hash), buffer, b, data, datalen)) < 0)
177     return (-1);
178 
179   for (i = 0; i < b && i < keylen; i ++)
180     buffer[i] = key[i] ^ 0x5c;
181   for (; i < b; i ++)
182     buffer[i] = 0x5c;
183 
184   return (hash_data(algorithm, hmac, hmacsize, buffer, b, hash, (size_t)hashlen));
185 }
186 
187 
188 //
189 // 'hash_data()' - Hash up to two blocks of data.
190 //
191 
192 static ssize_t				// O - Size of hash or `-1` on error
hash_data(const char * algorithm,unsigned char * hash,size_t hashsize,const void * a,size_t alen,const void * b,size_t blen)193 hash_data(const char    *algorithm,	// I - Algorithm
194           unsigned char *hash,		// I - Hash buffer
195           size_t        hashsize,	// I - Size of hash buffer
196           const void    *a,		// I - First block
197           size_t        alen,		// I - Length of first block
198           const void    *b,		// I - Second block or `NULL` for none
199           size_t        blen)		// I - Length of second block or `0` for none
200 {
201 #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS)
202   unsigned	hashlen;		// Length of hash
203   unsigned char	hashtemp[64];		// Temporary hash buffer
204 #else
205   if (strcmp(algorithm, "md5") && (b || blen != 0))
206   {
207     // Second block hashing is not supported without OpenSSL or GnuTLS
208     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unsupported without GnuTLS or OpenSSL/LibreSSL."), 1);
209 
210     return (-1);
211   }
212 #endif
213 
214   if (!strcmp(algorithm, "md5"))
215   {
216     // Some versions of GNU TLS and OpenSSL disable MD5 without warning...
217     _cups_md5_state_t	state;		// MD5 state info
218 
219     if (hashsize < 16)
220       goto too_small;
221 
222     _cupsMD5Init(&state);
223     _cupsMD5Append(&state, a, (int)alen);
224     if (b && blen)
225       _cupsMD5Append(&state, b, (int)blen);
226     _cupsMD5Finish(&state, hash);
227 
228     return (16);
229   }
230 
231 #ifdef HAVE_OPENSSL
232   const EVP_MD	*md = NULL;		// Message digest implementation
233   EVP_MD_CTX	*ctx;			// Context
234 
235 
236   if (!strcmp(algorithm, "sha"))
237   {
238     // SHA-1
239     md = EVP_sha1();
240   }
241   else if (!strcmp(algorithm, "sha2-224"))
242   {
243     md = EVP_sha224();
244   }
245   else if (!strcmp(algorithm, "sha2-256"))
246   {
247     md = EVP_sha256();
248   }
249   else if (!strcmp(algorithm, "sha2-384"))
250   {
251     md = EVP_sha384();
252   }
253   else if (!strcmp(algorithm, "sha2-512"))
254   {
255     md = EVP_sha512();
256   }
257 
258   if (md)
259   {
260     ctx = EVP_MD_CTX_new();
261     EVP_DigestInit(ctx, md);
262     EVP_DigestUpdate(ctx, a, alen);
263     if (b && blen)
264       EVP_DigestUpdate(ctx, b, blen);
265     EVP_DigestFinal(ctx, hashtemp, &hashlen);
266 
267     if (hashlen > hashsize)
268       goto too_small;
269 
270     memcpy(hash, hashtemp, hashlen);
271 
272     return ((ssize_t)hashlen);
273   }
274 
275 #elif defined(HAVE_GNUTLS)
276   gnutls_digest_algorithm_t	alg = GNUTLS_DIG_UNKNOWN;	// Algorithm
277   gnutls_hash_hd_t		ctx;				// Context
278   unsigned char		temp[64];			// Temporary hash buffer
279   size_t			tempsize = 0;			// Truncate to this size?
280 
281 
282   if (!strcmp(algorithm, "sha"))
283   {
284     // SHA-1
285     alg = GNUTLS_DIG_SHA1;
286   }
287   else if (!strcmp(algorithm, "sha2-224"))
288   {
289     alg = GNUTLS_DIG_SHA224;
290   }
291   else if (!strcmp(algorithm, "sha2-256"))
292   {
293     alg = GNUTLS_DIG_SHA256;
294   }
295   else if (!strcmp(algorithm, "sha2-384"))
296   {
297     alg = GNUTLS_DIG_SHA384;
298   }
299   else if (!strcmp(algorithm, "sha2-512"))
300   {
301     alg = GNUTLS_DIG_SHA512;
302   }
303   else if (!strcmp(algorithm, "sha2-512_224"))
304   {
305     alg      = GNUTLS_DIG_SHA512;
306     tempsize = 28;
307   }
308   else if (!strcmp(algorithm, "sha2-512_256"))
309   {
310     alg      = GNUTLS_DIG_SHA512;
311     tempsize = 32;
312   }
313 
314   if (alg != GNUTLS_DIG_UNKNOWN)
315   {
316     if (tempsize > 0)
317     {
318       // Truncate result to tempsize bytes...
319 
320       if (hashsize < tempsize)
321         goto too_small;
322 
323       gnutls_hash_fast(alg, a, alen, temp);
324       memcpy(hash, temp, tempsize);
325 
326       return ((ssize_t)tempsize);
327     }
328 
329     hashlen = gnutls_hash_get_len(alg);
330 
331     if (hashlen > hashsize)
332       goto too_small;
333 
334     gnutls_hash_init(&ctx, alg);
335     gnutls_hash(ctx, a, alen);
336     if (b && blen)
337       gnutls_hash(ctx, b, blen);
338     gnutls_hash_deinit(ctx, hashtemp);
339 
340     memcpy(hash, hashtemp, hashlen);
341 
342     return ((ssize_t)hashlen);
343   }
344 
345 #elif __APPLE__
346   if (!strcmp(algorithm, "sha"))
347   {
348     // SHA-1...
349 
350     CC_SHA1_CTX	ctx;			// SHA-1 context
351 
352     if (hashsize < CC_SHA1_DIGEST_LENGTH)
353       goto too_small;
354 
355     CC_SHA1_Init(&ctx);
356     CC_SHA1_Update(&ctx, a, (CC_LONG)alen);
357     CC_SHA1_Final(hash, &ctx);
358 
359     return (CC_SHA1_DIGEST_LENGTH);
360   }
361 #  ifdef CC_SHA224_DIGEST_LENGTH
362   else if (!strcmp(algorithm, "sha2-224"))
363   {
364     CC_SHA256_CTX	ctx;		// SHA-224 context
365 
366     if (hashsize < CC_SHA224_DIGEST_LENGTH)
367       goto too_small;
368 
369     CC_SHA224_Init(&ctx);
370     CC_SHA224_Update(&ctx, a, (CC_LONG)alen);
371     CC_SHA224_Final(hash, &ctx);
372 
373     return (CC_SHA224_DIGEST_LENGTH);
374   }
375 #  endif /* CC_SHA224_DIGEST_LENGTH */
376   else if (!strcmp(algorithm, "sha2-256"))
377   {
378     CC_SHA256_CTX	ctx;		// SHA-256 context
379 
380     if (hashsize < CC_SHA256_DIGEST_LENGTH)
381       goto too_small;
382 
383     CC_SHA256_Init(&ctx);
384     CC_SHA256_Update(&ctx, a, (CC_LONG)alen);
385     CC_SHA256_Final(hash, &ctx);
386 
387     return (CC_SHA256_DIGEST_LENGTH);
388   }
389   else if (!strcmp(algorithm, "sha2-384"))
390   {
391     CC_SHA512_CTX	ctx;		// SHA-384 context
392 
393     if (hashsize < CC_SHA384_DIGEST_LENGTH)
394       goto too_small;
395 
396     CC_SHA384_Init(&ctx);
397     CC_SHA384_Update(&ctx, a, (CC_LONG)alen);
398     CC_SHA384_Final(hash, &ctx);
399 
400     return (CC_SHA384_DIGEST_LENGTH);
401   }
402   else if (!strcmp(algorithm, "sha2-512"))
403   {
404     CC_SHA512_CTX	ctx;		// SHA-512 context
405 
406     if (hashsize < CC_SHA512_DIGEST_LENGTH)
407       goto too_small;
408 
409     CC_SHA512_Init(&ctx);
410     CC_SHA512_Update(&ctx, a, (CC_LONG)alen);
411     CC_SHA512_Final(hash, &ctx);
412 
413     return (CC_SHA512_DIGEST_LENGTH);
414   }
415 #  ifdef CC_SHA224_DIGEST_LENGTH
416   else if (!strcmp(algorithm, "sha2-512_224"))
417   {
418     CC_SHA512_CTX	ctx;		// SHA-512 context
419     unsigned char	temp[CC_SHA512_DIGEST_LENGTH];
420                                         // SHA-512 hash
421 
422     // SHA2-512 truncated to 224 bits (28 bytes)...
423 
424     if (hashsize < CC_SHA224_DIGEST_LENGTH)
425       goto too_small;
426 
427     CC_SHA512_Init(&ctx);
428     CC_SHA512_Update(&ctx, a, (CC_LONG)alen);
429     CC_SHA512_Final(temp, &ctx);
430 
431     memcpy(hash, temp, CC_SHA224_DIGEST_LENGTH);
432 
433     return (CC_SHA224_DIGEST_LENGTH);
434   }
435 #  endif // CC_SHA224_DIGEST_LENGTH
436   else if (!strcmp(algorithm, "sha2-512_256"))
437   {
438     CC_SHA512_CTX	ctx;		// SHA-512 context
439     unsigned char	temp[CC_SHA512_DIGEST_LENGTH];
440                                         // SHA-512 hash
441 
442     // SHA2-512 truncated to 256 bits (32 bytes)...
443 
444     if (hashsize < CC_SHA256_DIGEST_LENGTH)
445       goto too_small;
446 
447     CC_SHA512_Init(&ctx);
448     CC_SHA512_Update(&ctx, a, (CC_LONG)alen);
449     CC_SHA512_Final(temp, &ctx);
450 
451     memcpy(hash, temp, CC_SHA256_DIGEST_LENGTH);
452 
453     return (CC_SHA256_DIGEST_LENGTH);
454   }
455 
456 #elif _WIN32
457   // Use Windows CNG APIs to perform hashing...
458   BCRYPT_ALG_HANDLE	alg;		// Algorithm handle
459   LPCWSTR		algid = NULL;	// Algorithm ID
460   ssize_t		hashlen;	// Hash length
461   NTSTATUS		status;		// Status of hash
462   unsigned char		temp[64];	// Temporary hash buffer
463   size_t		tempsize = 0;	// Truncate to this size?
464 
465 
466   if (!strcmp(algorithm, "sha"))
467   {
468     algid   = BCRYPT_SHA1_ALGORITHM;
469     hashlen = 20;
470   }
471   else if (!strcmp(algorithm, "sha2-256"))
472   {
473     algid   = BCRYPT_SHA256_ALGORITHM;
474     hashlen = 32;
475   }
476   else if (!strcmp(algorithm, "sha2-384"))
477   {
478     algid   = BCRYPT_SHA384_ALGORITHM;
479     hashlen = 48;
480   }
481   else if (!strcmp(algorithm, "sha2-512"))
482   {
483     algid   = BCRYPT_SHA512_ALGORITHM;
484     hashlen = 64;
485   }
486   else if (!strcmp(algorithm, "sha2-512_224"))
487   {
488     algid   = BCRYPT_SHA512_ALGORITHM;
489     hashlen = tempsize = 28;
490   }
491   else if (!strcmp(algorithm, "sha2-512_256"))
492   {
493     algid   = BCRYPT_SHA512_ALGORITHM;
494     hashlen = tempsize = 32;
495   }
496 
497   if (algid)
498   {
499     if (hashsize < (size_t)hashlen)
500       goto too_small;
501 
502     if ((status = BCryptOpenAlgorithmProvider(&alg, algid, NULL, 0)) < 0)
503     {
504       DEBUG_printf(("2cupsHashData: BCryptOpenAlgorithmProvider returned %d.", status));
505 
506       if (status == STATUS_INVALID_PARAMETER)
507 	_cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad algorithm parameter."), 1);
508       else
509 	_cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to access cryptographic provider."), 1);
510 
511       return (-1);
512     }
513 
514     if (tempsize > 0)
515     {
516       // Do a truncated SHA2-512 hash...
517       status = BCryptHash(alg, NULL, 0, (PUCHAR)a, (ULONG)alen, temp, sizeof(temp));
518       memcpy(hash, temp, hashlen);
519     }
520     else
521     {
522       // Hash directly to buffer...
523       status = BCryptHash(alg, NULL, 0, (PUCHAR)a, (ULONG)alen, hash, (ULONG)hashlen);
524     }
525 
526     BCryptCloseAlgorithmProvider(alg, 0);
527 
528     if (status < 0)
529     {
530       DEBUG_printf(("2cupsHashData: BCryptHash returned %d.", status));
531 
532       if (status == STATUS_INVALID_PARAMETER)
533 	_cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad hashing parameter."), 1);
534       else
535 	_cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Hashing failed."), 1);
536 
537       return (-1);
538     }
539 
540     return (hashlen);
541   }
542 
543 #else
544   if (hashsize < 64)
545     goto too_small;
546 #endif // __APPLE__
547 
548   // Unknown hash algorithm...
549   _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unknown hash algorithm."), 1);
550 
551   return (-1);
552 
553   // We get here if the buffer is too small.
554   too_small:
555 
556   _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Hash buffer too small."), 1);
557   return (-1);
558 }
559 
560