Coverage Report

Created: 2025-07-11 06:21

/src/libcups/cups/hash.c
Line
Count
Source (jump to first uncovered line)
1
//
2
// Hashing functions for CUPS.
3
//
4
// Copyright © 2022-2023 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
#else // HAVE_GNUTLS
16
#  include <gnutls/crypto.h>
17
#endif // HAVE_OPENSSL
18
19
20
//
21
// Note: While both GNU TLS and OpenSSL offer HMAC functions, they also exclude
22
// certain hashes depending on the version of library and whatever patches are
23
// applied by the OS vendor/Linux distribution.  Since printers sometimes rely
24
// on otherwise deprecated/obsolete hash functions for things like PIN printing
25
// ("job-password"), and since such uses already have poor security regardless
26
// of the hash function used, it is more important to provide guaranteed
27
// implementations over some imaginary notion of "guaranteed security"...
28
//
29
30
//
31
// Local functions...
32
//
33
34
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);
35
36
37
//
38
// 'cupsHashData()' - Perform a hash function on the given data.
39
//
40
// This function performs a hash function on the given data. The "algorithm"
41
// argument can be any of the registered, non-deprecated IPP hash algorithms for
42
// the "job-password-encryption" attribute, including "sha" for SHA-1,
43
// "sha2-256" for SHA2-256, etc.
44
//
45
// The "hash" argument points to a buffer of "hashsize" bytes and should be at
46
// least 64 bytes in length for all of the supported algorithms.
47
//
48
// The returned hash is binary data.
49
//
50
51
ssize_t         // O - Size of hash or -1 on error
52
cupsHashData(const char    *algorithm,  // I - Algorithm name
53
             const void    *data, // I - Data to hash
54
             size_t        datalen, // I - Length of data to hash
55
             unsigned char *hash, // I - Hash buffer
56
             size_t        hashsize)  // I - Size of hash buffer
57
0
{
58
0
  if (!algorithm || !data || datalen == 0 || !hash || hashsize == 0)
59
0
  {
60
0
    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad arguments to function"), 1);
61
0
    return (-1);
62
0
  }
63
64
0
  return (hash_data(algorithm, hash, hashsize, data, datalen, NULL, 0));
65
0
}
66
67
68
//
69
// 'cupsHashString()' - Format a hash value as a hexadecimal string.
70
//
71
// The passed buffer must be at least 2 * hashsize + 1 characters in length.
72
//
73
74
const char *        // O - Formatted string
75
cupsHashString(
76
    const unsigned char *hash,    // I - Hash
77
    size_t              hashsize, // I - Size of hash
78
    char                *buffer,  // I - String buffer
79
    size_t    bufsize)  // I - Size of string buffer
80
0
{
81
0
  char    *bufptr = buffer; // Pointer into buffer
82
0
  static const char *hex = "0123456789abcdef";
83
          // Hex characters (lowercase!)
84
85
86
  // Range check input...
87
0
  if (!hash || hashsize < 1 || !buffer || bufsize < (2 * hashsize + 1))
88
0
  {
89
0
    if (buffer)
90
0
      *buffer = '\0';
91
0
    return (NULL);
92
0
  }
93
94
  // Loop until we've converted the whole hash...
95
0
  while (hashsize > 0)
96
0
  {
97
0
    *bufptr++ = hex[*hash >> 4];
98
0
    *bufptr++ = hex[*hash & 15];
99
100
0
    hash ++;
101
0
    hashsize --;
102
0
  }
103
104
0
  *bufptr = '\0';
105
106
0
  return (buffer);
107
0
}
108
109
110
//
111
// 'cupsHMACData()' - Perform a HMAC function on the given data.
112
//
113
// This function performs a HMAC function on the given data with the given key.
114
// The "algorithm" argument can be any of the registered, non-deprecated IPP
115
// hash algorithms for the "job-password-encryption" attribute, including
116
// "sha" for SHA-1, "sha2-256" for SHA2-256, etc.
117
//
118
// The "hmac" argument points to a buffer of "hmacsize" bytes and should be at
119
// least 64 bytes in length for all of the supported algorithms.
120
//
121
// The returned HMAC is binary data.
122
//
123
124
ssize_t         // O - The length of the HMAC or `-1` on error
125
cupsHMACData(
126
    const char          *algorithm, // I - Hash algorithm
127
    const unsigned char *key,   // I - Key
128
    size_t              keylen,   // I - Length of key
129
    const void          *data,    // I - Data to hash
130
    size_t              datalen,  // I - Length of data to hash
131
    unsigned char       *hmac,    // I - HMAC buffer
132
    size_t              hmacsize) // I - Size of HMAC buffer
133
0
{
134
0
  size_t  i,      // Looping var
135
0
    b;      // Block size
136
0
  unsigned char buffer[128],    // Intermediate buffer
137
0
    hash[128],    // Hash buffer
138
0
    hkey[128];    // Hashed key buffer
139
0
  ssize_t hashlen;    // Length of hash
140
141
142
  // Range check input...
143
0
  if (!algorithm || !key || keylen == 0 || !data || datalen == 0 || !hmac || hmacsize < 32)
144
0
    return (-1);
145
146
  // Determine the block size...
147
0
  if (!strcmp(algorithm, "sha2-384") || !strncmp(algorithm, "sha2-512", 8))
148
0
    b = 128;
149
0
  else
150
0
    b = 64;
151
152
  // If the key length is larger than the block size, hash it and use that
153
  // instead...
154
0
  if (keylen > b)
155
0
  {
156
0
    if ((hashlen = hash_data(algorithm, hkey, sizeof(hkey), key, keylen, NULL, 0)) < 0)
157
0
      return (-1);
158
159
0
    key    = hkey;
160
0
    keylen = (size_t)hashlen;
161
0
  }
162
163
  // HMAC = H(K' ^ opad, H(K' ^ ipad, data))
164
  // K'   = Klen > b ? H(K) : K, padded with 0's
165
  // opad = 0x5c, ipad = 0x36
166
0
  for (i = 0; i < b && i < keylen; i ++)
167
0
    buffer[i] = key[i] ^ 0x36;
168
0
  for (; i < b; i ++)
169
0
    buffer[i] = 0x36;
170
171
0
  if ((hashlen = hash_data(algorithm, hash, sizeof(hash), buffer, b, data, datalen)) < 0)
172
0
    return (-1);
173
174
0
  for (i = 0; i < b && i < keylen; i ++)
175
0
    buffer[i] = key[i] ^ 0x5c;
176
0
  for (; i < b; i ++)
177
0
    buffer[i] = 0x5c;
178
179
0
  return (hash_data(algorithm, hmac, hmacsize, buffer, b, hash, (size_t)hashlen));
180
0
}
181
182
183
//
184
// 'hash_data()' - Hash up to two blocks of data.
185
//
186
187
static ssize_t        // O - Size of hash or `-1` on error
188
hash_data(const char    *algorithm, // I - Algorithm
189
          unsigned char *hash,    // I - Hash buffer
190
          size_t        hashsize, // I - Size of hash buffer
191
          const void    *a,   // I - First block
192
          size_t        alen,   // I - Length of first block
193
          const void    *b,   // I - Second block or `NULL` for none
194
          size_t        blen)   // I - Length of second block or `0` for none
195
0
{
196
0
  unsigned  hashlen;    // Length of hash
197
0
  unsigned char hashtemp[64];   // Temporary hash buffer
198
0
#ifdef HAVE_OPENSSL
199
0
  const EVP_MD  *md = NULL;   // Message digest implementation
200
0
  EVP_MD_CTX  *ctx;     // Context
201
#else // HAVE_GNUTLS
202
  gnutls_digest_algorithm_t alg = GNUTLS_DIG_UNKNOWN;
203
          // Algorithm
204
  gnutls_hash_hd_t ctx;     // Context
205
#endif // HAVE_OPENSSL
206
207
208
0
  if (!strcmp(algorithm, "md5"))
209
0
  {
210
    // Some versions of GNU TLS and OpenSSL disable MD5 without warning...
211
0
    _cups_md5_state_t state;    // MD5 state info
212
213
0
    if (hashsize < 16)
214
0
      goto too_small;
215
216
0
    _cupsMD5Init(&state);
217
0
    _cupsMD5Append(&state, a, (int)alen);
218
0
    if (b && blen)
219
0
      _cupsMD5Append(&state, b, (int)blen);
220
0
    _cupsMD5Finish(&state, hash);
221
222
0
    return (16);
223
0
  }
224
225
0
#ifdef HAVE_OPENSSL
226
0
  if (!strcmp(algorithm, "sha"))
227
0
  {
228
    // SHA-1
229
0
    md = EVP_sha1();
230
0
  }
231
0
  else if (!strcmp(algorithm, "sha2-224"))
232
0
  {
233
0
    md = EVP_sha224();
234
0
  }
235
0
  else if (!strcmp(algorithm, "sha2-256"))
236
0
  {
237
0
    md = EVP_sha256();
238
0
  }
239
0
  else if (!strcmp(algorithm, "sha2-384"))
240
0
  {
241
0
    md = EVP_sha384();
242
0
  }
243
0
  else if (!strcmp(algorithm, "sha2-512"))
244
0
  {
245
0
    md = EVP_sha512();
246
0
  }
247
248
0
  if (md)
249
0
  {
250
0
    ctx = EVP_MD_CTX_new();
251
0
    EVP_DigestInit(ctx, md);
252
0
    EVP_DigestUpdate(ctx, a, alen);
253
0
    if (b && blen)
254
0
      EVP_DigestUpdate(ctx, b, blen);
255
0
    EVP_DigestFinal(ctx, hashtemp, &hashlen);
256
0
    EVP_MD_CTX_free(ctx);
257
258
0
    if (hashlen > hashsize)
259
0
      goto too_small;
260
261
0
    memcpy(hash, hashtemp, hashlen);
262
263
0
    return ((ssize_t)hashlen);
264
0
  }
265
266
#else // HAVE_GNUTLS
267
  if (!strcmp(algorithm, "sha"))
268
  {
269
    // SHA-1
270
    alg = GNUTLS_DIG_SHA1;
271
  }
272
  else if (!strcmp(algorithm, "sha2-224"))
273
  {
274
    alg = GNUTLS_DIG_SHA224;
275
  }
276
  else if (!strcmp(algorithm, "sha2-256"))
277
  {
278
    alg = GNUTLS_DIG_SHA256;
279
  }
280
  else if (!strcmp(algorithm, "sha2-384"))
281
  {
282
    alg = GNUTLS_DIG_SHA384;
283
  }
284
  else if (!strcmp(algorithm, "sha2-512"))
285
  {
286
    alg = GNUTLS_DIG_SHA512;
287
  }
288
289
  if (alg != GNUTLS_DIG_UNKNOWN)
290
  {
291
    hashlen = gnutls_hash_get_len(alg);
292
293
    if (hashlen > hashsize)
294
      goto too_small;
295
296
    gnutls_hash_init(&ctx, alg);
297
    gnutls_hash(ctx, a, alen);
298
    if (b && blen)
299
      gnutls_hash(ctx, b, blen);
300
    gnutls_hash_deinit(ctx, hashtemp);
301
302
    memcpy(hash, hashtemp, hashlen);
303
304
    return ((ssize_t)hashlen);
305
  }
306
#endif // HAVE_OPENSSL
307
308
  // Unknown hash algorithm...
309
0
  _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unknown hash algorithm."), 1);
310
311
0
  return (-1);
312
313
  // We get here if the buffer is too small.
314
0
  too_small:
315
316
0
  _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Hash buffer too small."), 1);
317
0
  return (-1);
318
0
}
319