/* libquvi * Copyright (C) 2013 Toni Gundogdu * * This file is part of libquvi . * * This library is free software: you can redistribute it and/or * modify it under the terms of the GNU Affero General Public * License as published by the Free Software Foundation, either * version 3 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General * Public License along with this library. If not, see * . */ #include "config.h" #include #include #include #include "gcrypt/crypto.h" static void _fail_set(crypto_t c, gchar *errmsg) { c->errmsg = errmsg; c->rc = EXIT_FAILURE; } static crypto_t _fail(crypto_t c, gchar *errmsg) { _fail_set(c, errmsg); return (c); } static gint _failn(crypto_t c, gchar *errmsg) { _fail_set(c, errmsg); return (c->rc); } static crypto_t _setkey(crypto_t c, gchar *key) { gcry_error_t e; gsize keylen; #ifdef _1 g_message("[%s] key=%s", __func__, key); #endif c->cipher.key = (gchar*) crypto_hex2bytes(key, &keylen); if (c->cipher.key == NULL || keylen ==0) { return (_fail(c, g_strdup("crypto_hex2bytes failed: invalid " "hexadecimal string length"))); } #ifdef _1 crypto_dump("c->cipher.key", (guchar*) c->cipher.key, keylen); #endif c->cipher.keylen = gcry_cipher_get_algo_keylen(c->algo); if (c->cipher.keylen ==0) { return (_fail(c, g_strdup_printf("gcry_cipher_get_algo_keylen failed " "c->cipher.keylen=%" G_GSIZE_FORMAT", " "keylen=%" G_GSIZE_FORMAT, c->cipher.keylen, keylen))); } #ifdef _1 if (keylen > c->cipher.keylen) { g_warning("key length exceeds %lu, ignoring the exceeding bytes",keylen); keylen = c->cipher.keylen; } #endif e = gcry_cipher_setkey(c->cipher.h, c->cipher.key, keylen); if (e != 0) { return (_fail(c, g_strdup_printf("gcry_cipher_setkey failed: %s", gpg_strerror(e)))); } c->rc = EXIT_SUCCESS; return (c); } static crypto_t _cipher_new(crypto_t c, gchar *key, const gchar *algoname, const gint cipher_mode, const guint cipher_flags) { gcry_error_t e; c->algo = gcry_cipher_map_name(algoname); if (c->algo ==0) { return (_fail(c, g_strdup_printf("algorithm `%s' was not available", algoname))); } c->cipher.flags = cipher_flags; c->cipher.mode = cipher_mode; c->cipher.should_pad = (cipher_mode != GCRY_CIPHER_MODE_STREAM && cipher_mode != GCRY_CIPHER_MODE_CFB && cipher_mode != GCRY_CIPHER_MODE_OFB) ? TRUE : FALSE; c->cipher.blklen = gcry_cipher_get_algo_blklen(c->algo); if (c->cipher.blklen ==0) return (_fail(c, g_strdup("gcry_cipher_get_algo_blklen failed"))); e = gcry_cipher_open(&c->cipher.h, c->algo, cipher_mode, cipher_flags); if (e != 0) { return (_fail(c, g_strdup_printf("gcry_cipher_open failed: %s", gpg_strerror(e)))); } return (_setkey(c, key)); } static crypto_t _hash_new(crypto_t c, const gchar *algoname) { c->algo = gcry_md_map_name(algoname); if (c->algo ==0) { return (_fail(c, g_strdup_printf("algorithm `%s' was not available", algoname))); } return (c); } crypto_t crypto_new(const gchar *algoname, const CryptoMode crypto_mode, gchar *key, const gint cipher_mode, const guint cipher_flags) { crypto_t c = g_new0(struct crypto_s, 1); c->mode = crypto_mode; if (crypto_mode != CRYPTO_MODE_HASH) _cipher_new(c, key, algoname, cipher_mode, cipher_flags); else _hash_new(c, algoname); return (c); } void crypto_free(crypto_t c) { if (c == NULL) return; if (c->cipher.h != NULL) gcry_cipher_close(c->cipher.h); c->cipher.h = NULL; g_free(c->cipher.key); c->cipher.key = NULL; g_free(c->out.data); c->out.data = NULL; g_free(c->errmsg); c->errmsg = NULL; g_free(c); c = NULL; } gboolean crypto_ok(crypto_t c) { return ((c->rc == EXIT_SUCCESS) ? TRUE:FALSE); } static gint _encrypt_blk(crypto_t c, const guchar *data, const gsize dlen, guchar *out /* size: c->blklen */) { gcry_error_t e; gpointer p; memcpy(out, data, dlen); /* Pad to block length. */ if (c->cipher.should_pad == TRUE && dlen < c->cipher.blklen) { gsize i = dlen; out[i++] = 0x80; while (i < c->cipher.blklen) out[i++] = 0x00; } /* in-place */ e = gcry_cipher_encrypt(c->cipher.h, out, c->cipher.blklen, NULL, 0); if (e != 0) { return (_failn(c, g_strdup_printf("gcry_cipher_encrypt failed: %s", gpg_strerror(e)))); } /* Append. */ p = g_realloc(c->out.data, c->out.dlen + c->cipher.blklen); if (p != NULL) { c->out.data = p; memcpy(&(c->out.data[c->out.dlen]), out, c->cipher.blklen); c->out.dlen += c->cipher.blklen; } return (EXIT_SUCCESS); } static gint _decrypt_blk(crypto_t c, const guchar *data, const gsize dlen, guchar *out /* size: c->blk_len */) { gcry_error_t e; gpointer p; gsize i, n; memcpy(out, data, dlen); /* in-place */ e = gcry_cipher_decrypt(c->cipher.h, out, c->cipher.blklen, NULL, 0); if (e != 0) { return (_failn(c, g_strdup_printf("gcry_cipher_decrypt failed: %s", gpg_strerror(e)))); } n = c->cipher.blklen; if (c->cipher.should_pad == TRUE) { for (i=0; i0 || i ==n || (iout.data, c->out.dlen+n); /* Append. */ if (p != NULL) { c->out.data = p; memcpy(&(c->out.data[c->out.dlen]), out, n); c->out.dlen += n; } return (EXIT_SUCCESS); } static gint _setiv(crypto_t c) { const gcry_error_t e = gcry_cipher_setiv(c->cipher.h, NULL, 0); if (e != 0) { return (_failn(c, g_strdup_printf("gcry_cipher_setiv failed: %s", gpg_strerror(e)))); } return (EXIT_SUCCESS); } typedef gint (*blk_cb)(crypto_t, const guchar*, const gsize, guchar*); static gint _cipher_exec(crypto_t c, const guchar *data, const gsize size) { guchar *out; gsize n, m; blk_cb cb; gint r; if (_setiv(c) != EXIT_SUCCESS) return (EXIT_FAILURE); cb = (c->mode == CRYPTO_MODE_ENCRYPT) ? _encrypt_blk : _decrypt_blk; g_assert(c->out.data == NULL); g_assert(c->out.dlen == 0); m = size - (size % c->cipher.blklen); out = g_malloc0(c->cipher.blklen); r = EXIT_SUCCESS; n = 0; while ( (n < m) && (r == EXIT_SUCCESS) ) { r = cb(c, (guchar*) data+n, c->cipher.blklen, out); n += c->cipher.blklen; } if ( (size % c->cipher.blklen) && (r == EXIT_SUCCESS) ) r = cb(c, (guchar*) data+n, (size % c->cipher.blklen), out); g_free(out); c->rc = r; return (r); } gint _hash_exec(crypto_t c, const guchar *data, const gsize size) { guint dlen; gsize n; dlen = gcry_md_get_algo_dlen(c->algo); n = dlen * sizeof(guchar); c->out.data = g_malloc0(n); c->out.dlen = n; gcry_md_hash_buffer(c->algo, c->out.data, data, size); #ifdef _1 g_message("[%s] algo=%s", __func__, gcry_md_algo_name(c->algo)); g_message("[%s] dlen=%u, c->out.dlen=%lu", __func__, dlen, c->out.dlen); #endif return (EXIT_SUCCESS); } gint crypto_exec(crypto_t c, const guchar *data, const gsize size) { g_assert(data != NULL); g_assert(size >0); g_assert(c->out.data == NULL); g_assert(c->out.dlen == 0); if (c->mode != CRYPTO_MODE_HASH) return (_cipher_exec(c, data, size)); return (_hash_exec(c, data, size)); } void crypto_dump(const gchar *w, const guchar *p, const gsize n) { gsize i = 0; g_print("%s=", w); while (i0); #ifdef _1 crypto_dump("data", data, n); #endif s = g_string_new(NULL); i = 0; while (istr; g_string_free(s, FALSE); return (r); } /* vim: set ts=2 sw=2 tw=72 expandtab: */