1 /*
2 * Hashing function for CUPS.
3 *
4 * Copyright © 2015-2019 by Apple Inc.
5 *
6 * Licensed under Apache License v2.0. See the file "LICENSE" for more
7 * information.
8 */
9
10 /*
11 * Include necessary headers...
12 */
13
14 #include "cups-private.h"
15 #ifdef __APPLE__
16 # include <CommonCrypto/CommonDigest.h>
17 #elif defined(HAVE_GNUTLS)
18 # include <gnutls/crypto.h>
19 # include "md5-internal.h"
20 #else
21 # include "md5-internal.h"
22 #endif /* __APPLE__ */
23
24
25 /*
26 * 'cupsHashData()' - Perform a hash function on the given data.
27 *
28 * The "algorithm" argument can be any of the registered, non-deprecated IPP
29 * hash algorithms for the "job-password-encryption" attribute, including
30 * "sha" for SHA-1, "sha-256" for SHA2-256, etc.
31 *
32 * The "hash" argument points to a buffer of "hashsize" bytes and should be at
33 * least 64 bytes in length for all of the supported algorithms.
34 *
35 * The returned hash is binary data.
36 *
37 * @since CUPS 2.2/macOS 10.12@
38 */
39
40 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)41 cupsHashData(const char *algorithm, /* I - Algorithm name */
42 const void *data, /* I - Data to hash */
43 size_t datalen, /* I - Length of data to hash */
44 unsigned char *hash, /* I - Hash buffer */
45 size_t hashsize) /* I - Size of hash buffer */
46 {
47 if (!algorithm || !data || datalen == 0 || !hash || hashsize == 0)
48 {
49 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad arguments to function"), 1);
50 return (-1);
51 }
52
53 #ifdef __APPLE__
54 if (!strcmp(algorithm, "md5"))
55 {
56 /*
57 * MD5 (deprecated but widely used...)
58 */
59
60 CC_MD5_CTX ctx; /* MD5 context */
61
62 if (hashsize < CC_MD5_DIGEST_LENGTH)
63 goto too_small;
64
65 CC_MD5_Init(&ctx);
66 CC_MD5_Update(&ctx, data, (CC_LONG)datalen);
67 CC_MD5_Final(hash, &ctx);
68
69 return (CC_MD5_DIGEST_LENGTH);
70 }
71 else if (!strcmp(algorithm, "sha"))
72 {
73 /*
74 * SHA-1...
75 */
76
77 CC_SHA1_CTX ctx; /* SHA-1 context */
78
79 if (hashsize < CC_SHA1_DIGEST_LENGTH)
80 goto too_small;
81
82 CC_SHA1_Init(&ctx);
83 CC_SHA1_Update(&ctx, data, (CC_LONG)datalen);
84 CC_SHA1_Final(hash, &ctx);
85
86 return (CC_SHA1_DIGEST_LENGTH);
87 }
88 else if (!strcmp(algorithm, "sha2-224"))
89 {
90 CC_SHA256_CTX ctx; /* SHA-224 context */
91
92 if (hashsize < CC_SHA224_DIGEST_LENGTH)
93 goto too_small;
94
95 CC_SHA224_Init(&ctx);
96 CC_SHA224_Update(&ctx, data, (CC_LONG)datalen);
97 CC_SHA224_Final(hash, &ctx);
98
99 return (CC_SHA224_DIGEST_LENGTH);
100 }
101 else if (!strcmp(algorithm, "sha2-256"))
102 {
103 CC_SHA256_CTX ctx; /* SHA-256 context */
104
105 if (hashsize < CC_SHA256_DIGEST_LENGTH)
106 goto too_small;
107
108 CC_SHA256_Init(&ctx);
109 CC_SHA256_Update(&ctx, data, (CC_LONG)datalen);
110 CC_SHA256_Final(hash, &ctx);
111
112 return (CC_SHA256_DIGEST_LENGTH);
113 }
114 else if (!strcmp(algorithm, "sha2-384"))
115 {
116 CC_SHA512_CTX ctx; /* SHA-384 context */
117
118 if (hashsize < CC_SHA384_DIGEST_LENGTH)
119 goto too_small;
120
121 CC_SHA384_Init(&ctx);
122 CC_SHA384_Update(&ctx, data, (CC_LONG)datalen);
123 CC_SHA384_Final(hash, &ctx);
124
125 return (CC_SHA384_DIGEST_LENGTH);
126 }
127 else if (!strcmp(algorithm, "sha2-512"))
128 {
129 CC_SHA512_CTX ctx; /* SHA-512 context */
130
131 if (hashsize < CC_SHA512_DIGEST_LENGTH)
132 goto too_small;
133
134 CC_SHA512_Init(&ctx);
135 CC_SHA512_Update(&ctx, data, (CC_LONG)datalen);
136 CC_SHA512_Final(hash, &ctx);
137
138 return (CC_SHA512_DIGEST_LENGTH);
139 }
140 else if (!strcmp(algorithm, "sha2-512_224"))
141 {
142 CC_SHA512_CTX ctx; /* SHA-512 context */
143 unsigned char temp[CC_SHA512_DIGEST_LENGTH];
144 /* SHA-512 hash */
145
146 /*
147 * SHA2-512 truncated to 224 bits (28 bytes)...
148 */
149
150 if (hashsize < CC_SHA224_DIGEST_LENGTH)
151 goto too_small;
152
153 CC_SHA512_Init(&ctx);
154 CC_SHA512_Update(&ctx, data, (CC_LONG)datalen);
155 CC_SHA512_Final(temp, &ctx);
156
157 memcpy(hash, temp, CC_SHA224_DIGEST_LENGTH);
158
159 return (CC_SHA224_DIGEST_LENGTH);
160 }
161 else if (!strcmp(algorithm, "sha2-512_256"))
162 {
163 CC_SHA512_CTX ctx; /* SHA-512 context */
164 unsigned char temp[CC_SHA512_DIGEST_LENGTH];
165 /* SHA-512 hash */
166
167 /*
168 * SHA2-512 truncated to 256 bits (32 bytes)...
169 */
170
171 if (hashsize < CC_SHA256_DIGEST_LENGTH)
172 goto too_small;
173
174 CC_SHA512_Init(&ctx);
175 CC_SHA512_Update(&ctx, data, (CC_LONG)datalen);
176 CC_SHA512_Final(temp, &ctx);
177
178 memcpy(hash, temp, CC_SHA256_DIGEST_LENGTH);
179
180 return (CC_SHA256_DIGEST_LENGTH);
181 }
182
183 #elif defined(HAVE_GNUTLS)
184 gnutls_digest_algorithm_t alg = GNUTLS_DIG_UNKNOWN;
185 /* Algorithm */
186 unsigned char temp[64]; /* Temporary hash buffer */
187 size_t tempsize = 0; /* Truncate to this size? */
188
189
190 if (!strcmp(algorithm, "md5"))
191 {
192 /*
193 * Some versions of GNU TLS disable MD5 without warning...
194 */
195
196 _cups_md5_state_t state; /* MD5 state info */
197
198 if (hashsize < 16)
199 goto too_small;
200
201 _cupsMD5Init(&state);
202 _cupsMD5Append(&state, data, datalen);
203 _cupsMD5Finish(&state, hash);
204
205 return (16);
206 }
207 else if (!strcmp(algorithm, "sha"))
208 alg = GNUTLS_DIG_SHA1;
209 else if (!strcmp(algorithm, "sha2-224"))
210 alg = GNUTLS_DIG_SHA224;
211 else if (!strcmp(algorithm, "sha2-256"))
212 alg = GNUTLS_DIG_SHA256;
213 else if (!strcmp(algorithm, "sha2-384"))
214 alg = GNUTLS_DIG_SHA384;
215 else if (!strcmp(algorithm, "sha2-512"))
216 alg = GNUTLS_DIG_SHA512;
217 else if (!strcmp(algorithm, "sha2-512_224"))
218 {
219 alg = GNUTLS_DIG_SHA512;
220 tempsize = 28;
221 }
222 else if (!strcmp(algorithm, "sha2-512_256"))
223 {
224 alg = GNUTLS_DIG_SHA512;
225 tempsize = 32;
226 }
227
228 if (alg != GNUTLS_DIG_UNKNOWN)
229 {
230 if (tempsize > 0)
231 {
232 /*
233 * Truncate result to tempsize bytes...
234 */
235
236 if (hashsize < tempsize)
237 goto too_small;
238
239 gnutls_hash_fast(alg, data, datalen, temp);
240 memcpy(hash, temp, tempsize);
241
242 return ((ssize_t)tempsize);
243 }
244
245 if (hashsize < gnutls_hash_get_len(alg))
246 goto too_small;
247
248 gnutls_hash_fast(alg, data, datalen, hash);
249
250 return ((ssize_t)gnutls_hash_get_len(alg));
251 }
252
253 #else
254 /*
255 * No hash support beyond MD5 without CommonCrypto or GNU TLS...
256 */
257
258 if (!strcmp(algorithm, "md5"))
259 {
260 _cups_md5_state_t state; /* MD5 state info */
261
262 if (hashsize < 16)
263 goto too_small;
264
265 _cupsMD5Init(&state);
266 _cupsMD5Append(&state, data, datalen);
267 _cupsMD5Finish(&state, hash);
268
269 return (16);
270 }
271 else if (hashsize < 64)
272 goto too_small;
273 #endif /* __APPLE__ */
274
275 /*
276 * Unknown hash algorithm...
277 */
278
279 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unknown hash algorithm."), 1);
280
281 return (-1);
282
283 /*
284 * We get here if the buffer is too small.
285 */
286
287 too_small:
288
289 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Hash buffer too small."), 1);
290 return (-1);
291 }
292
293
294 /*
295 * 'cupsHashString()' - Format a hash value as a hexadecimal string.
296 *
297 * The passed buffer must be at least 2 * hashsize + 1 characters in length.
298 *
299 * @since CUPS 2.2.7@
300 */
301
302 const char * /* O - Formatted string */
cupsHashString(const unsigned char * hash,size_t hashsize,char * buffer,size_t bufsize)303 cupsHashString(
304 const unsigned char *hash, /* I - Hash */
305 size_t hashsize, /* I - Size of hash */
306 char *buffer, /* I - String buffer */
307 size_t bufsize) /* I - Size of string buffer */
308 {
309 char *bufptr = buffer; /* Pointer into buffer */
310 static const char *hex = "0123456789abcdef";
311 /* Hex characters (lowercase!) */
312
313
314 /*
315 * Range check input...
316 */
317
318 if (!hash || hashsize < 1 || !buffer || bufsize < (2 * hashsize + 1))
319 {
320 if (buffer)
321 *buffer = '\0';
322 return (NULL);
323 }
324
325 /*
326 * Loop until we've converted the whole hash...
327 */
328
329 while (hashsize > 0)
330 {
331 *bufptr++ = hex[*hash >> 4];
332 *bufptr++ = hex[*hash & 15];
333
334 hash ++;
335 hashsize --;
336 }
337
338 *bufptr = '\0';
339
340 return (buffer);
341 }
342