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