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