|
|
/*
|
|
|
* Hashing function for CUPS.
|
|
|
*
|
|
|
* Copyright © 2015-2019 by Apple Inc.
|
|
|
*
|
|
|
* Licensed under Apache License v2.0. See the file "LICENSE" for more
|
|
|
* information.
|
|
|
*/
|
|
|
|
|
|
/*
|
|
|
* Include necessary headers...
|
|
|
*/
|
|
|
|
|
|
#include "cups-private.h"
|
|
|
#ifdef __APPLE__
|
|
|
# include <CommonCrypto/CommonDigest.h>
|
|
|
#elif defined(HAVE_GNUTLS)
|
|
|
# include <gnutls/crypto.h>
|
|
|
# include "md5-internal.h"
|
|
|
#else
|
|
|
# include "md5-internal.h"
|
|
|
#endif /* __APPLE__ */
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'cupsHashData()' - Perform a hash function on the given data.
|
|
|
*
|
|
|
* The "algorithm" argument can be any of the registered, non-deprecated IPP
|
|
|
* hash algorithms for the "job-password-encryption" attribute, including
|
|
|
* "sha" for SHA-1, "sha-256" for SHA2-256, etc.
|
|
|
*
|
|
|
* The "hash" argument points to a buffer of "hashsize" bytes and should be at
|
|
|
* least 64 bytes in length for all of the supported algorithms.
|
|
|
*
|
|
|
* The returned hash is binary data.
|
|
|
*
|
|
|
* @since CUPS 2.2/macOS 10.12@
|
|
|
*/
|
|
|
|
|
|
ssize_t /* O - Size of hash or -1 on error */
|
|
|
cupsHashData(const char *algorithm, /* I - Algorithm name */
|
|
|
const void *data, /* I - Data to hash */
|
|
|
size_t datalen, /* I - Length of data to hash */
|
|
|
unsigned char *hash, /* I - Hash buffer */
|
|
|
size_t hashsize) /* I - Size of hash buffer */
|
|
|
{
|
|
|
if (!algorithm || !data || datalen == 0 || !hash || hashsize == 0)
|
|
|
{
|
|
|
_cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad arguments to function"), 1);
|
|
|
return (-1);
|
|
|
}
|
|
|
|
|
|
#ifdef __APPLE__
|
|
|
if (!strcmp(algorithm, "md5"))
|
|
|
{
|
|
|
/*
|
|
|
* MD5 (deprecated but widely used...)
|
|
|
*/
|
|
|
|
|
|
CC_MD5_CTX ctx; /* MD5 context */
|
|
|
|
|
|
if (hashsize < CC_MD5_DIGEST_LENGTH)
|
|
|
goto too_small;
|
|
|
|
|
|
CC_MD5_Init(&ctx);
|
|
|
CC_MD5_Update(&ctx, data, (CC_LONG)datalen);
|
|
|
CC_MD5_Final(hash, &ctx);
|
|
|
|
|
|
return (CC_MD5_DIGEST_LENGTH);
|
|
|
}
|
|
|
else if (!strcmp(algorithm, "sha"))
|
|
|
{
|
|
|
/*
|
|
|
* SHA-1...
|
|
|
*/
|
|
|
|
|
|
CC_SHA1_CTX ctx; /* SHA-1 context */
|
|
|
|
|
|
if (hashsize < CC_SHA1_DIGEST_LENGTH)
|
|
|
goto too_small;
|
|
|
|
|
|
CC_SHA1_Init(&ctx);
|
|
|
CC_SHA1_Update(&ctx, data, (CC_LONG)datalen);
|
|
|
CC_SHA1_Final(hash, &ctx);
|
|
|
|
|
|
return (CC_SHA1_DIGEST_LENGTH);
|
|
|
}
|
|
|
else if (!strcmp(algorithm, "sha2-224"))
|
|
|
{
|
|
|
CC_SHA256_CTX ctx; /* SHA-224 context */
|
|
|
|
|
|
if (hashsize < CC_SHA224_DIGEST_LENGTH)
|
|
|
goto too_small;
|
|
|
|
|
|
CC_SHA224_Init(&ctx);
|
|
|
CC_SHA224_Update(&ctx, data, (CC_LONG)datalen);
|
|
|
CC_SHA224_Final(hash, &ctx);
|
|
|
|
|
|
return (CC_SHA224_DIGEST_LENGTH);
|
|
|
}
|
|
|
else if (!strcmp(algorithm, "sha2-256"))
|
|
|
{
|
|
|
CC_SHA256_CTX ctx; /* SHA-256 context */
|
|
|
|
|
|
if (hashsize < CC_SHA256_DIGEST_LENGTH)
|
|
|
goto too_small;
|
|
|
|
|
|
CC_SHA256_Init(&ctx);
|
|
|
CC_SHA256_Update(&ctx, data, (CC_LONG)datalen);
|
|
|
CC_SHA256_Final(hash, &ctx);
|
|
|
|
|
|
return (CC_SHA256_DIGEST_LENGTH);
|
|
|
}
|
|
|
else if (!strcmp(algorithm, "sha2-384"))
|
|
|
{
|
|
|
CC_SHA512_CTX ctx; /* SHA-384 context */
|
|
|
|
|
|
if (hashsize < CC_SHA384_DIGEST_LENGTH)
|
|
|
goto too_small;
|
|
|
|
|
|
CC_SHA384_Init(&ctx);
|
|
|
CC_SHA384_Update(&ctx, data, (CC_LONG)datalen);
|
|
|
CC_SHA384_Final(hash, &ctx);
|
|
|
|
|
|
return (CC_SHA384_DIGEST_LENGTH);
|
|
|
}
|
|
|
else if (!strcmp(algorithm, "sha2-512"))
|
|
|
{
|
|
|
CC_SHA512_CTX ctx; /* SHA-512 context */
|
|
|
|
|
|
if (hashsize < CC_SHA512_DIGEST_LENGTH)
|
|
|
goto too_small;
|
|
|
|
|
|
CC_SHA512_Init(&ctx);
|
|
|
CC_SHA512_Update(&ctx, data, (CC_LONG)datalen);
|
|
|
CC_SHA512_Final(hash, &ctx);
|
|
|
|
|
|
return (CC_SHA512_DIGEST_LENGTH);
|
|
|
}
|
|
|
else if (!strcmp(algorithm, "sha2-512_224"))
|
|
|
{
|
|
|
CC_SHA512_CTX ctx; /* SHA-512 context */
|
|
|
unsigned char temp[CC_SHA512_DIGEST_LENGTH];
|
|
|
/* SHA-512 hash */
|
|
|
|
|
|
/*
|
|
|
* SHA2-512 truncated to 224 bits (28 bytes)...
|
|
|
*/
|
|
|
|
|
|
if (hashsize < CC_SHA224_DIGEST_LENGTH)
|
|
|
goto too_small;
|
|
|
|
|
|
CC_SHA512_Init(&ctx);
|
|
|
CC_SHA512_Update(&ctx, data, (CC_LONG)datalen);
|
|
|
CC_SHA512_Final(temp, &ctx);
|
|
|
|
|
|
memcpy(hash, temp, CC_SHA224_DIGEST_LENGTH);
|
|
|
|
|
|
return (CC_SHA224_DIGEST_LENGTH);
|
|
|
}
|
|
|
else if (!strcmp(algorithm, "sha2-512_256"))
|
|
|
{
|
|
|
CC_SHA512_CTX ctx; /* SHA-512 context */
|
|
|
unsigned char temp[CC_SHA512_DIGEST_LENGTH];
|
|
|
/* SHA-512 hash */
|
|
|
|
|
|
/*
|
|
|
* SHA2-512 truncated to 256 bits (32 bytes)...
|
|
|
*/
|
|
|
|
|
|
if (hashsize < CC_SHA256_DIGEST_LENGTH)
|
|
|
goto too_small;
|
|
|
|
|
|
CC_SHA512_Init(&ctx);
|
|
|
CC_SHA512_Update(&ctx, data, (CC_LONG)datalen);
|
|
|
CC_SHA512_Final(temp, &ctx);
|
|
|
|
|
|
memcpy(hash, temp, CC_SHA256_DIGEST_LENGTH);
|
|
|
|
|
|
return (CC_SHA256_DIGEST_LENGTH);
|
|
|
}
|
|
|
|
|
|
#elif defined(HAVE_GNUTLS)
|
|
|
gnutls_digest_algorithm_t alg = GNUTLS_DIG_UNKNOWN;
|
|
|
/* Algorithm */
|
|
|
unsigned char temp[64]; /* Temporary hash buffer */
|
|
|
size_t tempsize = 0; /* Truncate to this size? */
|
|
|
|
|
|
|
|
|
if (!strcmp(algorithm, "md5"))
|
|
|
{
|
|
|
/*
|
|
|
* Some versions of GNU TLS disable MD5 without warning...
|
|
|
*/
|
|
|
|
|
|
_cups_md5_state_t state; /* MD5 state info */
|
|
|
|
|
|
if (hashsize < 16)
|
|
|
goto too_small;
|
|
|
|
|
|
_cupsMD5Init(&state);
|
|
|
_cupsMD5Append(&state, data, datalen);
|
|
|
_cupsMD5Finish(&state, hash);
|
|
|
|
|
|
return (16);
|
|
|
}
|
|
|
else if (!strcmp(algorithm, "sha"))
|
|
|
alg = GNUTLS_DIG_SHA1;
|
|
|
else if (!strcmp(algorithm, "sha2-224"))
|
|
|
alg = GNUTLS_DIG_SHA224;
|
|
|
else if (!strcmp(algorithm, "sha2-256"))
|
|
|
alg = GNUTLS_DIG_SHA256;
|
|
|
else if (!strcmp(algorithm, "sha2-384"))
|
|
|
alg = GNUTLS_DIG_SHA384;
|
|
|
else if (!strcmp(algorithm, "sha2-512"))
|
|
|
alg = GNUTLS_DIG_SHA512;
|
|
|
else if (!strcmp(algorithm, "sha2-512_224"))
|
|
|
{
|
|
|
alg = GNUTLS_DIG_SHA512;
|
|
|
tempsize = 28;
|
|
|
}
|
|
|
else if (!strcmp(algorithm, "sha2-512_256"))
|
|
|
{
|
|
|
alg = GNUTLS_DIG_SHA512;
|
|
|
tempsize = 32;
|
|
|
}
|
|
|
|
|
|
if (alg != GNUTLS_DIG_UNKNOWN)
|
|
|
{
|
|
|
if (tempsize > 0)
|
|
|
{
|
|
|
/*
|
|
|
* Truncate result to tempsize bytes...
|
|
|
*/
|
|
|
|
|
|
if (hashsize < tempsize)
|
|
|
goto too_small;
|
|
|
|
|
|
gnutls_hash_fast(alg, data, datalen, temp);
|
|
|
memcpy(hash, temp, tempsize);
|
|
|
|
|
|
return ((ssize_t)tempsize);
|
|
|
}
|
|
|
|
|
|
if (hashsize < gnutls_hash_get_len(alg))
|
|
|
goto too_small;
|
|
|
|
|
|
gnutls_hash_fast(alg, data, datalen, hash);
|
|
|
|
|
|
return ((ssize_t)gnutls_hash_get_len(alg));
|
|
|
}
|
|
|
|
|
|
#else
|
|
|
/*
|
|
|
* No hash support beyond MD5 without CommonCrypto or GNU TLS...
|
|
|
*/
|
|
|
|
|
|
if (!strcmp(algorithm, "md5"))
|
|
|
{
|
|
|
_cups_md5_state_t state; /* MD5 state info */
|
|
|
|
|
|
if (hashsize < 16)
|
|
|
goto too_small;
|
|
|
|
|
|
_cupsMD5Init(&state);
|
|
|
_cupsMD5Append(&state, data, datalen);
|
|
|
_cupsMD5Finish(&state, hash);
|
|
|
|
|
|
return (16);
|
|
|
}
|
|
|
else if (hashsize < 64)
|
|
|
goto too_small;
|
|
|
#endif /* __APPLE__ */
|
|
|
|
|
|
/*
|
|
|
* Unknown hash algorithm...
|
|
|
*/
|
|
|
|
|
|
_cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unknown hash algorithm."), 1);
|
|
|
|
|
|
return (-1);
|
|
|
|
|
|
/*
|
|
|
* We get here if the buffer is too small.
|
|
|
*/
|
|
|
|
|
|
too_small:
|
|
|
|
|
|
_cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Hash buffer too small."), 1);
|
|
|
return (-1);
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'cupsHashString()' - Format a hash value as a hexadecimal string.
|
|
|
*
|
|
|
* The passed buffer must be at least 2 * hashsize + 1 characters in length.
|
|
|
*
|
|
|
* @since CUPS 2.2.7@
|
|
|
*/
|
|
|
|
|
|
const char * /* O - Formatted string */
|
|
|
cupsHashString(
|
|
|
const unsigned char *hash, /* I - Hash */
|
|
|
size_t hashsize, /* I - Size of hash */
|
|
|
char *buffer, /* I - String buffer */
|
|
|
size_t bufsize) /* I - Size of string buffer */
|
|
|
{
|
|
|
char *bufptr = buffer; /* Pointer into buffer */
|
|
|
static const char *hex = "0123456789abcdef";
|
|
|
/* Hex characters (lowercase!) */
|
|
|
|
|
|
|
|
|
/*
|
|
|
* Range check input...
|
|
|
*/
|
|
|
|
|
|
if (!hash || hashsize < 1 || !buffer || bufsize < (2 * hashsize + 1))
|
|
|
{
|
|
|
if (buffer)
|
|
|
*buffer = '\0';
|
|
|
return (NULL);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* Loop until we've converted the whole hash...
|
|
|
*/
|
|
|
|
|
|
while (hashsize > 0)
|
|
|
{
|
|
|
*bufptr++ = hex[*hash >> 4];
|
|
|
*bufptr++ = hex[*hash & 15];
|
|
|
|
|
|
hash ++;
|
|
|
hashsize --;
|
|
|
}
|
|
|
|
|
|
*bufptr = '\0';
|
|
|
|
|
|
return (buffer);
|
|
|
}
|