/* * Hashing function for CUPS. * * Copyright 2015-2017 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 #elif defined(HAVE_GNUTLS) # include #else # include "md5-private.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")) alg = GNUTLS_DIG_MD5; 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 (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 */ _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. */ 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); }