Blob Blame History Raw
/* ghmac.h - data hashing functions
 *
 * Copyright (C) 2011  Collabora Ltd.
 * Copyright (C) 2019  Red Hat, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library; if not, see <http://www.gnu.org/licenses/>.
 */

#include "config.h"

#include <string.h>
#include <gnutls/crypto.h>

#include "ghmac.h"

#include "glib/galloca.h"
#include "gatomic.h"
#include "gslice.h"
#include "gmem.h"
#include "gstrfuncs.h"
#include "gchecksumprivate.h"
#include "gtestutils.h"
#include "gtypes.h"
#include "glibintl.h"

struct _GHmac
{
  int ref_count;
  GChecksumType digest_type;
  gnutls_hmac_hd_t hmac;
  gchar *digest_str;
};

GHmac *
g_hmac_new (GChecksumType  digest_type,
            const guchar  *key,
            gsize          key_len)
{
  gnutls_mac_algorithm_t algo;
  GHmac *hmac = g_slice_new0 (GHmac);
  hmac->ref_count = 1;
  hmac->digest_type = digest_type;  

  switch (digest_type)
    {
    case G_CHECKSUM_MD5:
      algo = GNUTLS_MAC_MD5;
      break;
    case G_CHECKSUM_SHA1:
      algo = GNUTLS_MAC_SHA1;
      break;
    case G_CHECKSUM_SHA256:
      algo = GNUTLS_MAC_SHA256;
      break;
    case G_CHECKSUM_SHA384:
      algo = GNUTLS_MAC_SHA384;
      break;
    case G_CHECKSUM_SHA512:
      algo = GNUTLS_MAC_SHA512;
      break;
    default:
      g_return_val_if_reached (NULL);
    }

  gnutls_hmac_init (&hmac->hmac, algo, key, key_len);

  return hmac;
}

GHmac *
g_hmac_copy (const GHmac *hmac)
{
  GHmac *copy;

  g_return_val_if_fail (hmac != NULL, NULL);

  copy = g_slice_new0 (GHmac);
  copy->ref_count = 1;
  copy->digest_type = hmac->digest_type;
  copy->hmac = gnutls_hmac_copy (hmac->hmac);

  return copy;
}

GHmac *
g_hmac_ref (GHmac *hmac)
{
  g_return_val_if_fail (hmac != NULL, NULL);

  g_atomic_int_inc (&hmac->ref_count);

  return hmac;
}

void
g_hmac_unref (GHmac *hmac)
{
  g_return_if_fail (hmac != NULL);

  if (g_atomic_int_dec_and_test (&hmac->ref_count))
    {
      gnutls_hmac_deinit (hmac->hmac, NULL);
      g_free (hmac->digest_str);
      g_slice_free (GHmac, hmac);
    }
}


void
g_hmac_update (GHmac        *hmac,
               const guchar *data,
               gssize        length)
{
  g_return_if_fail (hmac != NULL);
  g_return_if_fail (length == 0 || data != NULL);

  gnutls_hmac (hmac->hmac, data, length);
}

const gchar *
g_hmac_get_string (GHmac *hmac)
{
  guint8 *buffer;
  gsize digest_len;

  g_return_val_if_fail (hmac != NULL, NULL);

  if (hmac->digest_str)
    return hmac->digest_str;

  digest_len = g_checksum_type_get_length (hmac->digest_type);
  buffer = g_alloca (digest_len);

  gnutls_hmac_output (hmac->hmac, buffer);
  hmac->digest_str = gchecksum_digest_to_string (buffer, digest_len);
  return hmac->digest_str;
}


void
g_hmac_get_digest (GHmac  *hmac,
                   guint8 *buffer,
                   gsize  *digest_len)
{
  g_return_if_fail (hmac != NULL);

  gnutls_hmac_output (hmac->hmac, buffer);
  *digest_len = g_checksum_type_get_length (hmac->digest_type);
}