/* ex:ts=4:sw=4:sts=4:et */
|
|
/* -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
|
/**
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This program 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 General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* AUTHORS
|
|
* Maciek Borzecki <maciek.borzecki (at] gmail.com>
|
|
*/
|
|
#include <openssl/rsa.h>
|
|
#include <openssl/bio.h>
|
|
#include <openssl/pem.h>
|
|
#include "mconn-crypt.h"
|
|
|
|
/* encrypted data padding */
|
|
#define M_CONN_CRYPT_RSA_PADDING RSA_PKCS1_PADDING
|
|
|
|
typedef struct _MConnCryptPrivate MConnCryptPrivate;
|
|
|
|
/**
|
|
* MConnCrypt:
|
|
*
|
|
* A simple wrapper for cypto operations.
|
|
**/
|
|
|
|
struct _MConnCrypt
|
|
{
|
|
GObject parent;
|
|
MConnCryptPrivate *priv;
|
|
};
|
|
|
|
struct _MConnCryptPrivate
|
|
{
|
|
RSA *key; /* RSA key wrapper */
|
|
};
|
|
|
|
static void m_conn_crypt_dispose (GObject *object);
|
|
static void m_conn_crypt_finalize (GObject *object);
|
|
static gchar *__m_conn_get_public_key_as_pem(MConnCryptPrivate *priv);
|
|
static gboolean __m_conn_load_key(MConnCryptPrivate *priv, const char *path);
|
|
static gboolean __m_conn_generate_key_at_path(const char *path);
|
|
|
|
G_DEFINE_TYPE_WITH_PRIVATE (MConnCrypt, m_conn_crypt, G_TYPE_OBJECT);
|
|
|
|
static void
|
|
m_conn_crypt_class_init (MConnCryptClass *klass)
|
|
{
|
|
GObjectClass *gobject_class = (GObjectClass *)klass;
|
|
|
|
gobject_class->dispose = m_conn_crypt_dispose;
|
|
gobject_class->finalize = m_conn_crypt_finalize;
|
|
}
|
|
|
|
static void
|
|
m_conn_crypt_init (MConnCrypt *self)
|
|
{
|
|
g_debug("mconn-crypt: new instance");
|
|
self->priv = m_conn_crypt_get_instance_private(self);
|
|
}
|
|
|
|
static void
|
|
m_conn_crypt_dispose (GObject *object)
|
|
{
|
|
MConnCrypt *self = (MConnCrypt *)object;
|
|
|
|
if (self->priv->key != NULL)
|
|
{
|
|
RSA_free(self->priv->key);
|
|
self->priv->key = NULL;
|
|
}
|
|
|
|
G_OBJECT_CLASS (m_conn_crypt_parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
m_conn_crypt_finalize (GObject *object)
|
|
{
|
|
MConnCrypt *self = (MConnCrypt *)object;
|
|
|
|
g_signal_handlers_destroy (object);
|
|
G_OBJECT_CLASS (m_conn_crypt_parent_class)->finalize (object);
|
|
}
|
|
|
|
MConnCrypt *m_conn_crypt_new_for_key_path(const char *path)
|
|
{
|
|
g_debug("mconn-crypt: new crypt for key %s", path);
|
|
|
|
MConnCrypt *self = g_object_new(M_CONN_TYPE_CRYPT, NULL);
|
|
|
|
if (g_file_test(path, G_FILE_TEST_EXISTS) == FALSE)
|
|
__m_conn_generate_key_at_path(path);
|
|
|
|
if (__m_conn_load_key(self->priv, path) == FALSE)
|
|
{
|
|
m_conn_crypt_unref(self);
|
|
return NULL;
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
MConnCrypt * m_conn_crypt_ref(MConnCrypt *self)
|
|
{
|
|
g_assert(IS_M_CONN_CRYPT(self));
|
|
return M_CONN_CRYPT(g_object_ref(self));
|
|
}
|
|
|
|
void m_conn_crypt_unref(MConnCrypt *self)
|
|
{
|
|
if (self != NULL)
|
|
{
|
|
g_assert(IS_M_CONN_CRYPT(self));
|
|
g_object_unref(self);
|
|
}
|
|
}
|
|
|
|
GByteArray * m_conn_crypt_decrypt(MConnCrypt *self, GBytes *data, GError **err)
|
|
{
|
|
g_assert(IS_M_CONN_CRYPT(self));
|
|
g_assert(self->priv->key);
|
|
|
|
g_debug("decrypt: %zu bytes of data", g_bytes_get_size(data));
|
|
|
|
g_assert_cmpint(g_bytes_get_size(data), ==, RSA_size(self->priv->key));
|
|
|
|
/* decrypted data is less than RSA_size() long */
|
|
gsize out_buf_size = RSA_size(self->priv->key);
|
|
GByteArray *out_data = g_byte_array_sized_new(out_buf_size);
|
|
|
|
int dec_size;
|
|
dec_size = RSA_private_decrypt(g_bytes_get_size(data),
|
|
g_bytes_get_data(data, NULL),
|
|
(unsigned char *)out_data->data,
|
|
self->priv->key,
|
|
M_CONN_CRYPT_RSA_PADDING);
|
|
g_debug("decrypted size: %d", dec_size);
|
|
g_assert(dec_size != -1);
|
|
|
|
g_byte_array_set_size(out_data, dec_size);
|
|
|
|
return out_data;
|
|
}
|
|
|
|
gchar *m_conn_crypt_get_public_key_pem(MConnCrypt *self)
|
|
{
|
|
g_assert(IS_M_CONN_CRYPT(self));
|
|
g_assert(self->priv);
|
|
g_assert(self->priv->key);
|
|
return __m_conn_get_public_key_as_pem(self->priv);
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
static gchar *__m_conn_get_public_key_as_pem(MConnCryptPrivate *priv)
|
|
{
|
|
gchar *pubkey = NULL;
|
|
|
|
/* memory IO */
|
|
BIO *bm = BIO_new(BIO_s_mem());
|
|
|
|
/* generate PEM */
|
|
/* PEM_write_bio_RSAPublicKey(bm, priv->key); */
|
|
PEM_write_bio_RSA_PUBKEY(bm, priv->key);
|
|
|
|
/* get PEM as text */
|
|
char *oss_pubkey = NULL;
|
|
long data = BIO_get_mem_data(bm, &oss_pubkey);
|
|
g_debug("mconn-crypt: public key length: %l", data);
|
|
g_assert(data != 0);
|
|
g_assert(oss_pubkey != NULL);
|
|
|
|
/* dup the key as buffer goes away with BIO */
|
|
pubkey = g_strndup(oss_pubkey, data);
|
|
|
|
BIO_set_close(bm, BIO_CLOSE);
|
|
BIO_free(bm);
|
|
|
|
return pubkey;
|
|
}
|
|
|
|
static gboolean __m_conn_load_key(MConnCryptPrivate *priv, const char *path)
|
|
{
|
|
if (g_file_test(path, G_FILE_TEST_EXISTS) == FALSE)
|
|
{
|
|
g_critical("mconn-crypt: key file %s does not exist", path);
|
|
return FALSE;
|
|
}
|
|
|
|
g_debug("mconn-crypt: loading key from %s", path);
|
|
|
|
BIO *bf = BIO_new_file(path, "r");
|
|
|
|
if (bf == NULL)
|
|
{
|
|
g_critical("mconn-crypt: failed to open file %s", path);
|
|
return FALSE;
|
|
}
|
|
|
|
RSA *rsa = NULL;
|
|
|
|
rsa = PEM_read_bio_RSAPrivateKey(bf, NULL, NULL, NULL);
|
|
|
|
BIO_free(bf);
|
|
|
|
if (rsa == NULL)
|
|
{
|
|
g_critical("mconn-crypt: failed to read private key");
|
|
return FALSE;
|
|
}
|
|
|
|
priv->key = rsa;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean __m_conn_generate_key_at_path(const char *path)
|
|
{
|
|
gboolean ret = TRUE;
|
|
RSA *rsa = NULL;
|
|
|
|
BIO *bf = BIO_new_file(path, "w");
|
|
if (bf == NULL)
|
|
{
|
|
g_error("mconn-crypt: failed to open file");
|
|
return FALSE;
|
|
}
|
|
|
|
rsa = RSA_generate_key(2048, RSA_3, NULL, NULL);
|
|
|
|
if (PEM_write_bio_RSAPrivateKey(bf, rsa, NULL, NULL, 0, NULL, NULL) == 0)
|
|
{
|
|
g_critical("mconn-crypt: failed to private write key to file");
|
|
ret = FALSE;
|
|
}
|
|
|
|
RSA_free(rsa);
|
|
|
|
BIO_free(bf);
|
|
|
|
return ret;
|
|
}
|