text: /*
   * 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>
  #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"))
      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 ((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 */
  
      _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);
  }
  