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 | | |