Blame src/gcrypt/crypto.c

Packit 3ff1e7
/* libquvi
Packit 3ff1e7
 * Copyright (C) 2013  Toni Gundogdu <legatvs@gmail.com>
Packit 3ff1e7
 *
Packit 3ff1e7
 * This file is part of libquvi <http://quvi.sourceforge.net/>.
Packit 3ff1e7
 *
Packit 3ff1e7
 * This library is free software: you can redistribute it and/or
Packit 3ff1e7
 * modify it under the terms of the GNU Affero General Public
Packit 3ff1e7
 * License as published by the Free Software Foundation, either
Packit 3ff1e7
 * version 3 of the License, or (at your option) any later version.
Packit 3ff1e7
 *
Packit 3ff1e7
 * This library is distributed in the hope that it will be useful,
Packit 3ff1e7
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 3ff1e7
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 3ff1e7
 * GNU Affero General Public License for more details.
Packit 3ff1e7
 *
Packit 3ff1e7
 * You should have received a copy of the GNU Affero General
Packit 3ff1e7
 * Public License along with this library.  If not, see
Packit 3ff1e7
 * <http://www.gnu.org/licenses/>.
Packit 3ff1e7
 */
Packit 3ff1e7
Packit 3ff1e7
#include "config.h"
Packit 3ff1e7
Packit 3ff1e7
#include <stdlib.h>
Packit 3ff1e7
#include <gcrypt.h>
Packit 3ff1e7
#include <glib/gstdio.h>
Packit 3ff1e7
Packit 3ff1e7
#include "gcrypt/crypto.h"
Packit 3ff1e7
Packit 3ff1e7
static void _fail_set(crypto_t c, gchar *errmsg)
Packit 3ff1e7
{
Packit 3ff1e7
  c->errmsg = errmsg;
Packit 3ff1e7
  c->rc = EXIT_FAILURE;
Packit 3ff1e7
}
Packit 3ff1e7
Packit 3ff1e7
static crypto_t _fail(crypto_t c, gchar *errmsg)
Packit 3ff1e7
{
Packit 3ff1e7
  _fail_set(c, errmsg);
Packit 3ff1e7
  return (c);
Packit 3ff1e7
}
Packit 3ff1e7
Packit 3ff1e7
static gint _failn(crypto_t c, gchar *errmsg)
Packit 3ff1e7
{
Packit 3ff1e7
  _fail_set(c, errmsg);
Packit 3ff1e7
  return (c->rc);
Packit 3ff1e7
}
Packit 3ff1e7
Packit 3ff1e7
static crypto_t _setkey(crypto_t c, gchar *key)
Packit 3ff1e7
{
Packit 3ff1e7
  gcry_error_t e;
Packit 3ff1e7
  gsize keylen;
Packit 3ff1e7
#ifdef _1
Packit 3ff1e7
  g_message("[%s] key=%s", __func__, key);
Packit 3ff1e7
#endif
Packit 3ff1e7
  c->cipher.key = (gchar*) crypto_hex2bytes(key, &keylen);
Packit 3ff1e7
  if (c->cipher.key == NULL || keylen ==0)
Packit 3ff1e7
    {
Packit 3ff1e7
      return (_fail(c,
Packit 3ff1e7
                    g_strdup("crypto_hex2bytes failed: invalid "
Packit 3ff1e7
                             "hexadecimal string length")));
Packit 3ff1e7
    }
Packit 3ff1e7
#ifdef _1
Packit 3ff1e7
  crypto_dump("c->cipher.key", (guchar*) c->cipher.key, keylen);
Packit 3ff1e7
#endif
Packit 3ff1e7
  c->cipher.keylen = gcry_cipher_get_algo_keylen(c->algo);
Packit 3ff1e7
  if (c->cipher.keylen ==0)
Packit 3ff1e7
    {
Packit 3ff1e7
      return (_fail(c, g_strdup_printf("gcry_cipher_get_algo_keylen failed "
Packit 3ff1e7
                                       "c->cipher.keylen=%" G_GSIZE_FORMAT", "
Packit 3ff1e7
                                       "keylen=%" G_GSIZE_FORMAT,
Packit 3ff1e7
                                       c->cipher.keylen, keylen)));
Packit 3ff1e7
    }
Packit 3ff1e7
Packit 3ff1e7
#ifdef _1
Packit 3ff1e7
  if (keylen > c->cipher.keylen)
Packit 3ff1e7
    {
Packit 3ff1e7
      g_warning("key length exceeds %lu, ignoring the exceeding bytes",keylen);
Packit 3ff1e7
      keylen = c->cipher.keylen;
Packit 3ff1e7
    }
Packit 3ff1e7
#endif
Packit 3ff1e7
Packit 3ff1e7
  e = gcry_cipher_setkey(c->cipher.h, c->cipher.key, keylen);
Packit 3ff1e7
  if (e != 0)
Packit 3ff1e7
    {
Packit 3ff1e7
      return (_fail(c,
Packit 3ff1e7
                    g_strdup_printf("gcry_cipher_setkey failed: %s",
Packit 3ff1e7
                                    gpg_strerror(e))));
Packit 3ff1e7
    }
Packit 3ff1e7
  c->rc = EXIT_SUCCESS;
Packit 3ff1e7
  return (c);
Packit 3ff1e7
}
Packit 3ff1e7
Packit 3ff1e7
static crypto_t _cipher_new(crypto_t c, gchar *key,
Packit 3ff1e7
                            const gchar *algoname,
Packit 3ff1e7
                            const gint cipher_mode,
Packit 3ff1e7
                            const guint cipher_flags)
Packit 3ff1e7
{
Packit 3ff1e7
  gcry_error_t e;
Packit 3ff1e7
Packit 3ff1e7
  c->algo = gcry_cipher_map_name(algoname);
Packit 3ff1e7
  if (c->algo ==0)
Packit 3ff1e7
    {
Packit 3ff1e7
      return (_fail(c, g_strdup_printf("algorithm `%s' was not available",
Packit 3ff1e7
                                       algoname)));
Packit 3ff1e7
    }
Packit 3ff1e7
Packit 3ff1e7
  c->cipher.flags = cipher_flags;
Packit 3ff1e7
  c->cipher.mode = cipher_mode;
Packit 3ff1e7
Packit 3ff1e7
  c->cipher.should_pad = (cipher_mode != GCRY_CIPHER_MODE_STREAM
Packit 3ff1e7
                          && cipher_mode != GCRY_CIPHER_MODE_CFB
Packit 3ff1e7
                          && cipher_mode != GCRY_CIPHER_MODE_OFB)
Packit 3ff1e7
                         ? TRUE
Packit 3ff1e7
                         : FALSE;
Packit 3ff1e7
Packit 3ff1e7
  c->cipher.blklen = gcry_cipher_get_algo_blklen(c->algo);
Packit 3ff1e7
  if (c->cipher.blklen ==0)
Packit 3ff1e7
    return (_fail(c, g_strdup("gcry_cipher_get_algo_blklen failed")));
Packit 3ff1e7
Packit 3ff1e7
  e = gcry_cipher_open(&c->cipher.h, c->algo, cipher_mode, cipher_flags);
Packit 3ff1e7
  if (e != 0)
Packit 3ff1e7
    {
Packit 3ff1e7
      return (_fail(c, g_strdup_printf("gcry_cipher_open failed: %s",
Packit 3ff1e7
                                       gpg_strerror(e))));
Packit 3ff1e7
    }
Packit 3ff1e7
  return (_setkey(c, key));
Packit 3ff1e7
}
Packit 3ff1e7
Packit 3ff1e7
static crypto_t _hash_new(crypto_t c, const gchar *algoname)
Packit 3ff1e7
{
Packit 3ff1e7
  c->algo = gcry_md_map_name(algoname);
Packit 3ff1e7
  if (c->algo ==0)
Packit 3ff1e7
    {
Packit 3ff1e7
      return (_fail(c, g_strdup_printf("algorithm `%s' was not available",
Packit 3ff1e7
                                       algoname)));
Packit 3ff1e7
    }
Packit 3ff1e7
  return (c);
Packit 3ff1e7
}
Packit 3ff1e7
Packit 3ff1e7
crypto_t crypto_new(const gchar *algoname, const CryptoMode crypto_mode,
Packit 3ff1e7
                    gchar *key, const gint cipher_mode,
Packit 3ff1e7
                    const guint cipher_flags)
Packit 3ff1e7
{
Packit 3ff1e7
  crypto_t c = g_new0(struct crypto_s, 1);
Packit 3ff1e7
  c->mode = crypto_mode;
Packit 3ff1e7
  if (crypto_mode != CRYPTO_MODE_HASH)
Packit 3ff1e7
    _cipher_new(c, key, algoname, cipher_mode, cipher_flags);
Packit 3ff1e7
  else
Packit 3ff1e7
    _hash_new(c, algoname);
Packit 3ff1e7
  return (c);
Packit 3ff1e7
}
Packit 3ff1e7
Packit 3ff1e7
void crypto_free(crypto_t c)
Packit 3ff1e7
{
Packit 3ff1e7
  if (c == NULL)
Packit 3ff1e7
    return;
Packit 3ff1e7
Packit 3ff1e7
  if (c->cipher.h != NULL)
Packit 3ff1e7
    gcry_cipher_close(c->cipher.h);
Packit 3ff1e7
  c->cipher.h = NULL;
Packit 3ff1e7
Packit 3ff1e7
  g_free(c->cipher.key);
Packit 3ff1e7
  c->cipher.key = NULL;
Packit 3ff1e7
Packit 3ff1e7
  g_free(c->out.data);
Packit 3ff1e7
  c->out.data = NULL;
Packit 3ff1e7
Packit 3ff1e7
  g_free(c->errmsg);
Packit 3ff1e7
  c->errmsg = NULL;
Packit 3ff1e7
Packit 3ff1e7
  g_free(c);
Packit 3ff1e7
  c = NULL;
Packit 3ff1e7
}
Packit 3ff1e7
Packit 3ff1e7
gboolean crypto_ok(crypto_t c)
Packit 3ff1e7
{
Packit 3ff1e7
  return ((c->rc == EXIT_SUCCESS) ? TRUE:FALSE);
Packit 3ff1e7
}
Packit 3ff1e7
Packit 3ff1e7
static gint _encrypt_blk(crypto_t c, const guchar *data, const gsize dlen,
Packit 3ff1e7
                         guchar *out /* size: c->blklen */)
Packit 3ff1e7
{
Packit 3ff1e7
  gcry_error_t e;
Packit 3ff1e7
  gpointer p;
Packit 3ff1e7
Packit 3ff1e7
  memcpy(out, data, dlen);
Packit 3ff1e7
Packit 3ff1e7
  /* Pad to block length. */
Packit 3ff1e7
  if (c->cipher.should_pad == TRUE && dlen < c->cipher.blklen)
Packit 3ff1e7
    {
Packit 3ff1e7
      gsize i = dlen;
Packit 3ff1e7
      out[i++] = 0x80;
Packit 3ff1e7
      while (i < c->cipher.blklen)
Packit 3ff1e7
        out[i++] = 0x00;
Packit 3ff1e7
    }
Packit 3ff1e7
Packit 3ff1e7
  /* in-place */
Packit 3ff1e7
  e = gcry_cipher_encrypt(c->cipher.h, out, c->cipher.blklen, NULL, 0);
Packit 3ff1e7
  if (e != 0)
Packit 3ff1e7
    {
Packit 3ff1e7
      return (_failn(c,
Packit 3ff1e7
                     g_strdup_printf("gcry_cipher_encrypt failed: %s",
Packit 3ff1e7
                                     gpg_strerror(e))));
Packit 3ff1e7
    }
Packit 3ff1e7
Packit 3ff1e7
  /* Append. */
Packit 3ff1e7
  p = g_realloc(c->out.data, c->out.dlen + c->cipher.blklen);
Packit 3ff1e7
  if (p != NULL)
Packit 3ff1e7
    {
Packit 3ff1e7
      c->out.data = p;
Packit 3ff1e7
      memcpy(&(c->out.data[c->out.dlen]), out, c->cipher.blklen);
Packit 3ff1e7
      c->out.dlen += c->cipher.blklen;
Packit 3ff1e7
    }
Packit 3ff1e7
  return (EXIT_SUCCESS);
Packit 3ff1e7
}
Packit 3ff1e7
Packit 3ff1e7
static gint _decrypt_blk(crypto_t c, const guchar *data, const gsize dlen,
Packit 3ff1e7
                         guchar *out /* size: c->blk_len */)
Packit 3ff1e7
{
Packit 3ff1e7
  gcry_error_t e;
Packit 3ff1e7
  gpointer p;
Packit 3ff1e7
  gsize i, n;
Packit 3ff1e7
Packit 3ff1e7
  memcpy(out, data, dlen);
Packit 3ff1e7
Packit 3ff1e7
  /* in-place */
Packit 3ff1e7
  e = gcry_cipher_decrypt(c->cipher.h, out, c->cipher.blklen, NULL, 0);
Packit 3ff1e7
  if (e != 0)
Packit 3ff1e7
    {
Packit 3ff1e7
      return (_failn(c,
Packit 3ff1e7
                     g_strdup_printf("gcry_cipher_decrypt failed: %s",
Packit 3ff1e7
                                     gpg_strerror(e))));
Packit 3ff1e7
    }
Packit 3ff1e7
Packit 3ff1e7
  n = c->cipher.blklen;
Packit 3ff1e7
Packit 3ff1e7
  if (c->cipher.should_pad == TRUE)
Packit 3ff1e7
    {
Packit 3ff1e7
      for (i=0; i
Packit 3ff1e7
        {
Packit 3ff1e7
          if (out[i] == 0x80
Packit 3ff1e7
              && (i >0 || i ==n || (i
Packit 3ff1e7
            {
Packit 3ff1e7
              n = i;
Packit 3ff1e7
              break;
Packit 3ff1e7
            }
Packit 3ff1e7
        }
Packit 3ff1e7
    }
Packit 3ff1e7
Packit 3ff1e7
  p = g_realloc(c->out.data, c->out.dlen+n); /* Append. */
Packit 3ff1e7
  if (p != NULL)
Packit 3ff1e7
    {
Packit 3ff1e7
      c->out.data = p;
Packit 3ff1e7
      memcpy(&(c->out.data[c->out.dlen]), out, n);
Packit 3ff1e7
      c->out.dlen += n;
Packit 3ff1e7
    }
Packit 3ff1e7
  return (EXIT_SUCCESS);
Packit 3ff1e7
}
Packit 3ff1e7
Packit 3ff1e7
static gint _setiv(crypto_t c)
Packit 3ff1e7
{
Packit 3ff1e7
  const gcry_error_t e = gcry_cipher_setiv(c->cipher.h, NULL, 0);
Packit 3ff1e7
  if (e != 0)
Packit 3ff1e7
    {
Packit 3ff1e7
      return (_failn(c, g_strdup_printf("gcry_cipher_setiv failed: %s",
Packit 3ff1e7
                                        gpg_strerror(e))));
Packit 3ff1e7
    }
Packit 3ff1e7
  return (EXIT_SUCCESS);
Packit 3ff1e7
}
Packit 3ff1e7
Packit 3ff1e7
typedef gint (*blk_cb)(crypto_t, const guchar*, const gsize, guchar*);
Packit 3ff1e7
Packit 3ff1e7
static gint _cipher_exec(crypto_t c, const guchar *data, const gsize size)
Packit 3ff1e7
{
Packit 3ff1e7
  guchar *out;
Packit 3ff1e7
  gsize n, m;
Packit 3ff1e7
  blk_cb cb;
Packit 3ff1e7
  gint r;
Packit 3ff1e7
Packit 3ff1e7
  if (_setiv(c) != EXIT_SUCCESS)
Packit 3ff1e7
    return (EXIT_FAILURE);
Packit 3ff1e7
Packit 3ff1e7
  cb = (c->mode == CRYPTO_MODE_ENCRYPT)
Packit 3ff1e7
       ? _encrypt_blk
Packit 3ff1e7
       : _decrypt_blk;
Packit 3ff1e7
Packit 3ff1e7
  g_assert(c->out.data == NULL);
Packit 3ff1e7
  g_assert(c->out.dlen == 0);
Packit 3ff1e7
Packit 3ff1e7
  m = size - (size % c->cipher.blklen);
Packit 3ff1e7
  out = g_malloc0(c->cipher.blklen);
Packit 3ff1e7
  r = EXIT_SUCCESS;
Packit 3ff1e7
Packit 3ff1e7
  n = 0;
Packit 3ff1e7
  while ( (n < m) && (r == EXIT_SUCCESS) )
Packit 3ff1e7
    {
Packit 3ff1e7
      r = cb(c, (guchar*) data+n, c->cipher.blklen, out);
Packit 3ff1e7
      n += c->cipher.blklen;
Packit 3ff1e7
    }
Packit 3ff1e7
Packit 3ff1e7
  if ( (size % c->cipher.blklen) && (r == EXIT_SUCCESS) )
Packit 3ff1e7
    r = cb(c, (guchar*) data+n, (size % c->cipher.blklen), out);
Packit 3ff1e7
Packit 3ff1e7
  g_free(out);
Packit 3ff1e7
  c->rc = r;
Packit 3ff1e7
Packit 3ff1e7
  return (r);
Packit 3ff1e7
}
Packit 3ff1e7
Packit 3ff1e7
gint _hash_exec(crypto_t c, const guchar *data, const gsize size)
Packit 3ff1e7
{
Packit 3ff1e7
  guint dlen;
Packit 3ff1e7
  gsize n;
Packit 3ff1e7
Packit 3ff1e7
  dlen = gcry_md_get_algo_dlen(c->algo);
Packit 3ff1e7
  n = dlen * sizeof(guchar);
Packit 3ff1e7
Packit 3ff1e7
  c->out.data = g_malloc0(n);
Packit 3ff1e7
  c->out.dlen = n;
Packit 3ff1e7
Packit 3ff1e7
  gcry_md_hash_buffer(c->algo, c->out.data, data, size);
Packit 3ff1e7
#ifdef _1
Packit 3ff1e7
  g_message("[%s] algo=%s", __func__, gcry_md_algo_name(c->algo));
Packit 3ff1e7
  g_message("[%s] dlen=%u, c->out.dlen=%lu", __func__, dlen, c->out.dlen);
Packit 3ff1e7
#endif
Packit 3ff1e7
  return (EXIT_SUCCESS);
Packit 3ff1e7
}
Packit 3ff1e7
Packit 3ff1e7
gint crypto_exec(crypto_t c, const guchar *data, const gsize size)
Packit 3ff1e7
{
Packit 3ff1e7
  g_assert(data != NULL);
Packit 3ff1e7
  g_assert(size >0);
Packit 3ff1e7
Packit 3ff1e7
  g_assert(c->out.data == NULL);
Packit 3ff1e7
  g_assert(c->out.dlen == 0);
Packit 3ff1e7
Packit 3ff1e7
  if (c->mode != CRYPTO_MODE_HASH)
Packit 3ff1e7
    return (_cipher_exec(c, data, size));
Packit 3ff1e7
Packit 3ff1e7
  return (_hash_exec(c, data, size));
Packit 3ff1e7
}
Packit 3ff1e7
Packit 3ff1e7
void crypto_dump(const gchar *w, const guchar *p, const gsize n)
Packit 3ff1e7
{
Packit 3ff1e7
  gsize i = 0;
Packit 3ff1e7
  g_print("%s=", w);
Packit 3ff1e7
  while (i
Packit 3ff1e7
    g_print("%02x", p[i++] & 0xff);
Packit 3ff1e7
  g_print(" =(%" G_GSIZE_FORMAT ")\n", i);
Packit 3ff1e7
}
Packit 3ff1e7
Packit 3ff1e7
guchar *crypto_hex2bytes(const gchar *hexstr, gsize *size)
Packit 3ff1e7
{
Packit 3ff1e7
  const gchar *p;
Packit 3ff1e7
  guint n, b;
Packit 3ff1e7
  guchar *r;
Packit 3ff1e7
Packit 3ff1e7
  *size = 0;
Packit 3ff1e7
Packit 3ff1e7
  n = strlen(hexstr);
Packit 3ff1e7
  if ( (n % 2) ==1)
Packit 3ff1e7
    return (NULL); /* Invalid hexadecimal string length. */
Packit 3ff1e7
Packit 3ff1e7
  r = g_malloc0_n(n/2, sizeof(guchar));
Packit 3ff1e7
  p = hexstr;
Packit 3ff1e7
Packit 3ff1e7
  while ( *p && (n = sscanf((gchar*) p, "%02x", &b)) == 1)
Packit 3ff1e7
    {
Packit 3ff1e7
      r[(*size)++] = b;
Packit 3ff1e7
      p += 2;
Packit 3ff1e7
    }
Packit 3ff1e7
  return (r);
Packit 3ff1e7
}
Packit 3ff1e7
Packit 3ff1e7
gchar *crypto_bytes2hex(const guchar *data, const gsize n)
Packit 3ff1e7
{
Packit 3ff1e7
  GString *s;
Packit 3ff1e7
  gchar *r;
Packit 3ff1e7
  gsize i;
Packit 3ff1e7
Packit 3ff1e7
  g_assert(data != NULL);
Packit 3ff1e7
  g_assert(n>0);
Packit 3ff1e7
#ifdef _1
Packit 3ff1e7
  crypto_dump("data", data, n);
Packit 3ff1e7
#endif
Packit 3ff1e7
  s = g_string_new(NULL);
Packit 3ff1e7
  i = 0;
Packit 3ff1e7
Packit 3ff1e7
  while (i
Packit 3ff1e7
    g_string_append_printf(s, "%02x", data[i++] & 0xff);
Packit 3ff1e7
Packit 3ff1e7
  r = s->str;
Packit 3ff1e7
  g_string_free(s, FALSE);
Packit 3ff1e7
Packit 3ff1e7
  return (r);
Packit 3ff1e7
}
Packit 3ff1e7
Packit 3ff1e7
/* vim: set ts=2 sw=2 tw=72 expandtab: */