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