Browse Source

crypt: dump openssl cryptography helpers in favor of GnuTLS based wrapper

Replace OpenSSL helpers with ones based on GnuTLS. GnuTLS is already an indirect
dependency since we require GLib's TLS support. As support for older protocol
versions was removed, we will not need any extra library for handling explicit
packet decryption.

This change will also resolve potential license incompatibility issues.
bboozzoo/tls-support
Maciek Borzecki 7 years ago
parent
commit
d632aa5c74
7 changed files with 231 additions and 452 deletions
  1. +15
    -43
      Makefile.am
  2. +1
    -0
      configure.ac
  3. +150
    -0
      src/crypt/certificate.vala
  4. +0
    -270
      src/crypt/mconn-crypt.c
  5. +0
    -99
      src/crypt/mconn-crypt.h
  6. +0
    -13
      src/crypt/mconn-crypt.vapi
  7. +65
    -27
      test/mconn-crypt-vala-test.vala

+ 15
- 43
Makefile.am View File

@ -31,11 +31,9 @@ bin_PROGRAMS = \
mconnectctl
noinst_PROGRAMS = \
test-mconn-crypt \
test-mconn-crypt-vala
test-mconn-crypt
noinst_LTLIBRARIES = \
libmconn-crypt.la
noinst_LTLIBRARIES =
VALAFLAGS = \
--no-color \
@ -49,6 +47,7 @@ VALAFLAGS = \
--pkg=posix \
--pkg=gdk-3.0 \
--pkg=atspi-2 \
--pkg=gnutls \
--vapidir=src/crypt
#-------------------------------------------------------------
@ -78,64 +77,37 @@ mconnect_SOURCES = \
src/mconnect/config.vala \
src/mconnect/application.vala \
src/mconnect/utils.vala \
src/mconnect/property-proxy.vala
src/mconnect/property-proxy.vala \
src/crypt/certificate.vala
mconnect_LDADD = \
libmconn-crypt.la \
$(MCONNECT_LIBS)
mconnect_CFLAGS = \
$(MCONNECT_CFLAGS) \
-I$(top_srcdir)/src/crypt
mconnect_VALAFLAGS = \
--pkg=mconn-crypt
#-------------------------------------------------------------
libmconn_crypt_la_SOURCES = \
src/crypt/mconn-crypt.c \
src/crypt/mconn-crypt.h
libmconn_crypt_la_CFLAGS = \
$(MCONNECT_CFLAGS)
libmconn_crypt_la_LIBADD = \
$(MCONNECT_LIBS)
#-------------------------------------------------------------
test_mconn_crypt_SOURCES = \
test/mconn-crypt-test.c
src/crypt/certificate.vala \
test/mconn-crypt-vala-test.vala
test_mconn_crypt_LDADD = \
$(MCONNECT_LIBS) \
libmconn-crypt.la
$(MCONNECT_LIBS)
test_mconn_crypt_CFLAGS = \
$(MCONNECT_CFLAGS) \
-I$(top_srcdir)/src/crypt
#-------------------------------------------------------------
test_mconn_crypt_vala_SOURCES = \
test/mconn-crypt-vala-test.vala
test_mconn_crypt_vala_LDADD = \
$(MCONNECT_LIBS) \
libmconn-crypt.la
test_mconn_crypt_vala_CFLAGS = \
$(MCONNECT_CFLAGS) \
-I$(top_srcdir)/src/crypt
$(MCONNECT_CFLAGS)
test_mconn_crypt_vala_VALAFLAGS = \
--pkg=mconn-crypt
test_mconn_crypt_VALAFLAGS = \
--pkg=gio-2.0 \
--pkg=posix \
--pkg=gnutls
#-------------------------------------------------------------
mconnectctl_SOURCES = \
src/mconnectctl/main.vala
src/mconnectctl/main.vala
mconnectctl_LDADD = \
$(MCONNECT_LIBS)
@ -174,7 +146,7 @@ gdb-script: gdb-script.in
#-------------------------------------------------------------
GEN_FROM_VALA = $(filter %.vala,$(mconnect_SOURCES))
GEN_FROM_VALA = $(filter %.vala,$(mconnect_SOURCES) $(test_mconn_crypt_SOURCES))
BUILT_SOURCES = \
mconnect.desktop \
$(GEN_FROM_VALA:.vala=.c)


+ 1
- 0
configure.ac View File

@ -49,6 +49,7 @@ PKG_CHECK_MODULES(MCONNECT, [glib-2.0,
libnotify
gdk-3.0
atspi-2
gnutls
])
AC_SUBST(MCONNECT_CFLAGS)
AC_SUBST(MCONNECT_LIBS)


+ 150
- 0
src/crypt/certificate.vala View File

@ -0,0 +1,150 @@
/* 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>
*/
namespace Mconn {
namespace Crypt {
private GnuTLS.X509.PrivateKey generate_private_key() {
var key = GnuTLS.X509.PrivateKey.create();
key.generate(GnuTLS.PKAlgorithm.RSA, 2048);
// size_t sz = 4096;
// var buf = GnuTLS.malloc(sz);
// key.export_pkcs8(GnuTLS.X509.CertificateFormat.PEM, "",
// GnuTLS.X509.PKCSEncryptFlags.PLAIN,
// buf, ref sz);
// stdout.printf("private key:\n");
// stdout.printf("%s", (string)buf);
// GnuTLS.free(buf);
return key;
}
private struct dn_setting {
string oid;
string name;
}
GnuTLS.X509.Certificate generate_self_signed_cert(GnuTLS.X509.PrivateKey key, string common_name) {
var cert = GnuTLS.X509.Certificate.create();
var start_time = new DateTime.now_local();
var end_time = start_time.add_years(10);
cert.set_key(key);
cert.set_version(1);
cert.set_activation_time((time_t)start_time.to_unix());
cert.set_expiration_time((time_t)end_time.to_unix());
uint32 serial = Posix.htonl(10);
cert.set_serial(&serial, sizeof(uint32));
dn_setting[] dn = {
dn_setting() { oid=GnuTLS.OID.X520_ORGANIZATION_NAME,
name="mconnect"},
dn_setting() { oid=GnuTLS.OID.X520_ORGANIZATIONAL_UNIT_NAME,
name="mconnect"},
dn_setting() { oid=GnuTLS.OID.X520_COMMON_NAME,
name=common_name},
};
foreach (var dn_val in dn) {
var err = cert.set_dn_by_oid(dn_val.oid, 0,
dn_val.name.data, dn_val.name.length);
if (err != GnuTLS.ErrorCode.SUCCESS ) {
warning("set dn failed for OID %s - %s, err: %d\n",
dn_val.oid, dn_val.name, err);
}
}
var err = cert.sign(cert, key);
GLib.assert(err == GnuTLS.ErrorCode.SUCCESS);
// size_t sz = 8192;
// var buf = GnuTLS.malloc(sz);
// err = cert.export(GnuTLS.X509.CertificateFormat.PEM, buf, ref sz);
// if (err != GnuTLS.ErrorCode.SUCCESS) {
// if (err == GnuTLS.ErrorCode.SHORT_MEMORY_BUFFER) {
// stdout.printf("too short\n");
// } else {
// stdout.printf("other error: %d\n", err);
// }
// } else {
// stdout.printf("certificate:\n");
// stdout.printf("size: %zu\n", sz);
// stdout.printf("%s", (string)buf);
// }
// GnuTLS.free(buf);
return cert;
}
private uint8[] export_certificate(GnuTLS.X509.Certificate cert) {
var buf = new uint8[8192];
size_t sz = buf.length;
var err = cert.export(GnuTLS.X509.CertificateFormat.PEM, buf, ref sz);
assert(err == GnuTLS.ErrorCode.SUCCESS);
debug("actual certificate PEM size: %zu", sz);
debug("certificate PEM:\n%s", (string)buf);
// TODO: figure out if this is valid at all
buf.length = (int) sz;
return buf;
}
private uint8[] export_private_key(GnuTLS.X509.PrivateKey key) {
var buf = new uint8[8192];
size_t sz = buf.length;
var err = key.export_pkcs8(GnuTLS.X509.CertificateFormat.PEM, "",
GnuTLS.X509.PKCSEncryptFlags.PLAIN,
buf, ref sz);
assert(err == GnuTLS.ErrorCode.SUCCESS);
debug("actual private key PEM size: %zu", sz);
debug("private key PEM:\n%s", (string)buf);
// TODO: figure out if this is valid at all
buf.length = (int) sz;
return buf;
}
private void export_to_file(string path, uint8[] data) throws Error {
var f = File.new_for_path(path);
f.replace_contents(data, "", false,
FileCreateFlags.PRIVATE | FileCreateFlags.REPLACE_DESTINATION,
null);
}
public void generate_key_cert(string key_path, string cert_path, string name) throws Error {
var key = generate_private_key();
var cert = generate_self_signed_cert(key, name);
export_to_file(cert_path, export_certificate(cert));
export_to_file(key_path, export_private_key(key));
}
}
}

+ 0
- 270
src/crypt/mconn-crypt.c View File

@ -1,270 +0,0 @@
/**
* 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 MCONN_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 mconn_crypt_dispose (GObject *object);
static void mconn_crypt_finalize (GObject *object);
static gchar *__mconn_get_public_key_as_pem(MconnCryptPrivate *priv);
static gboolean __mconn_load_key(MconnCryptPrivate *priv, const char *path);
static gboolean __mconn_generate_key_at_path(const char *path);
G_DEFINE_TYPE_WITH_PRIVATE (MconnCrypt, mconn_crypt, G_TYPE_OBJECT);
static void
mconn_crypt_class_init (MconnCryptClass *klass)
{
GObjectClass *gobject_class = (GObjectClass *)klass;
gobject_class->dispose = mconn_crypt_dispose;
gobject_class->finalize = mconn_crypt_finalize;
}
static void
mconn_crypt_init (MconnCrypt *self)
{
g_debug("mconn-crypt: new instance");
self->priv = mconn_crypt_get_instance_private(self);
}
static void
mconn_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 (mconn_crypt_parent_class)->dispose (object);
}
static void
mconn_crypt_finalize (GObject *object)
{
MconnCrypt *self = (MconnCrypt *)object;
g_signal_handlers_destroy (object);
G_OBJECT_CLASS (mconn_crypt_parent_class)->finalize (object);
}
MconnCrypt *mconn_crypt_new_for_key_path(const char *path)
{
g_debug("mconn-crypt: new crypt for key %s", path);
MconnCrypt *self = g_object_new(MCONN_TYPE_CRYPT, NULL);
if (g_file_test(path, G_FILE_TEST_EXISTS) == FALSE)
__mconn_generate_key_at_path(path);
if (__mconn_load_key(self->priv, path) == FALSE)
{
mconn_crypt_unref(self);
return NULL;
}
return self;
}
MconnCrypt * mconn_crypt_ref(MconnCrypt *self)
{
g_assert(IS_MCONN_CRYPT(self));
return MCONN_CRYPT(g_object_ref(self));
}
void mconn_crypt_unref(MconnCrypt *self)
{
if (self != NULL)
{
g_assert(IS_MCONN_CRYPT(self));
g_object_unref(self);
}
}
GByteArray * mconn_crypt_decrypt(MconnCrypt *self, GBytes *data, GError **err)
{
g_assert(IS_MCONN_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,
MCONN_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 *mconn_crypt_get_public_key_pem(MconnCrypt *self)
{
g_assert(IS_MCONN_CRYPT(self));
g_assert(self->priv);
g_assert(self->priv->key);
return __mconn_get_public_key_as_pem(self->priv);
}
/**
*
*/
static gchar *__mconn_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: %ld", 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 __mconn_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 __mconn_generate_key_at_path(const char *path)
{
gboolean ret = FALSE;
RSA *rsa = NULL;
BIO *bf = NULL;
BIGNUM *e = NULL;
int res = 0;
rsa = RSA_new();
g_return_val_if_fail(rsa != NULL, FALSE);
e = BN_new();
if (e == NULL)
{
goto cleanup;
}
BN_set_word(e, RSA_3);
if (RSA_generate_key_ex(rsa, 2048, e, NULL) != 1) {
g_critical("mconn-crypt: failed to generate RSA key");
goto cleanup;
}
bf = BIO_new_file(path, "w");
if (bf == NULL)
{
g_error("mconn-crypt: failed to open file");
goto cleanup;
}
if (PEM_write_bio_RSAPrivateKey(bf, rsa, NULL, NULL, 0, NULL, NULL) == 0)
{
g_critical("mconn-crypt: failed to private write key to file");
goto cleanup;
}
ret = TRUE;
cleanup:
BN_free(e);
RSA_free(rsa);
BIO_free(bf);
return ret;
}

+ 0
- 99
src/crypt/mconn-crypt.h View File

@ -1,99 +0,0 @@
/*
* 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.
*
* Author: Maciek Borzecki <maciek.borzecki (at] gmail.com>
*/
#ifndef __M_CONN_CRYPT_H__
#define __M_CONN_CRYPT_H__
#include <glib-object.h>
#include <glib.h>
#include <glib/gbytes.h>
G_BEGIN_DECLS
#define MCONN_TYPE_CRYPT \
(mconn_crypt_get_type())
#define MCONN_CRYPT(obj) \
(G_TYPE_CHECK_INSTANCE_CAST ((obj), \
MCONN_TYPE_CRYPT, \
MconnCrypt))
#define MCONN_CRYPT_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST ((klass), \
MCONN_TYPE_CRYPT, \
MconnCryptClass))
#define IS_MCONN_CRYPT(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
MCONN_TYPE_CRYPT))
#define IS_MCONN_CRYPT_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE ((klass), \
MCONN_TYPE_CRYPT))
#define MCONN_CRYPT_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS ((obj), \
MCONN_TYPE_CRYPT, \
MconnCryptClass))
typedef struct _MconnCrypt MconnCrypt;
typedef struct _MconnCryptClass MconnCryptClass;
struct _MconnCryptClass
{
GObjectClass parent_class;
};
GType mconn_crypt_get_type (void) G_GNUC_CONST;
/**
* mconn_crypt_new_for_key_path: (constructor)
* @path: key path
*
* Returns: (transfer full): new object
*/
MconnCrypt *mconn_crypt_new_for_key_path(const char *path);
/**
* mconn_crypt_unref:
* @crypt: crypt object
*/
void mconn_crypt_unref(MconnCrypt *crypt);
/**
* mconn_crypt_ref:
* @crypt: crypt object
*
* Take reference to crypt object
* Returns: (transfer none): reffed object
*/
MconnCrypt *mconn_crypt_ref(MconnCrypt *crypt);
/**
* mconn_crypt_decrypt:
* @crypt: crypt object
* @data: (type GBytes): data
* @error: return location for a GError or NULL
*
* Returns: (transfer full): a new #GByteArray with decoded data
*/
GByteArray * mconn_crypt_decrypt(MconnCrypt *crypt, GBytes *data, GError **error);
/**
* mconn_crypt_get_public_key_pem:
* @crypt: crypt object
*
* Returns: (transfer full): allocated string with public key in PEM format
*/
gchar * mconn_crypt_get_public_key_pem(MconnCrypt *crypt);
G_END_DECLS
#endif /* __MCONN_CRYPT_H__ */

+ 0
- 13
src/crypt/mconn-crypt.vapi View File

@ -1,13 +0,0 @@
namespace Mconn {
[CCode (cheader_filename = "mconn-crypt.h", type_id = "mconn_crypt_get_type ()")]
public class Crypt : GLib.Object {
[CCode (has_construct_function = false)]
protected Crypt ();
public GLib.ByteArray decrypt (GLib.Bytes data) throws GLib.Error;
[CCode (has_construct_function = false)]
public Crypt.for_key_path (string path);
public string get_public_key_pem ();
public unowned Mconn.Crypt @ref ();
public void unref ();
}
}

+ 65
- 27
test/mconn-crypt-vala-test.vala View File

@ -1,41 +1,79 @@
using Mconn;
void test_simple() {
string file_path = "/tmp/test-key-vala.pem";
FileUtils.remove(file_path);
string pubkey1;
string pubkey2;
{
assert(FileUtils.test(file_path, FileTest.EXISTS) == false);
var c = new Crypt.for_key_path(file_path);
assert(FileUtils.test(file_path, FileTest.EXISTS) == true);
pubkey1 = c.get_public_key_pem();
assert(pubkey1 != null);
void test_generate() {
string key_path = "/tmp/test-key-vala.pem";
string cert_path = "/tmp/test-cert-vala.pem";
FileUtils.remove(key_path);
FileUtils.remove(cert_path);
assert(FileUtils.test(key_path, FileTest.EXISTS) == false);
Crypt.generate_key_cert(key_path, cert_path, "foo");
assert(FileUtils.test(key_path, FileTest.EXISTS) == true);
assert(FileUtils.test(cert_path, FileTest.EXISTS) == true);
}
void test_generate_load() {
string key_path = "/tmp/test-key-vala.pem";
string cert_path = "/tmp/test-cert-vala.pem";
FileUtils.remove(key_path);
FileUtils.remove(cert_path);
Crypt.generate_key_cert(key_path, cert_path, "bar");
try {
var cert = new TlsCertificate.from_files(cert_path,
key_path);
} catch (Error e) {
Test.fail();
}
}
// file should still exist
assert(FileUtils.test(file_path, FileTest.EXISTS) == true);
void test_custom_cn() {
string key_path = "/tmp/test-key-vala.pem";
string cert_path = "/tmp/test-cert-vala.pem";
FileUtils.remove(key_path);
FileUtils.remove(cert_path);
{
assert(FileUtils.test(file_path, FileTest.EXISTS) == true);
var c = new Crypt.for_key_path(file_path);
assert(FileUtils.test(file_path, FileTest.EXISTS) == true);
pubkey2 = c.get_public_key_pem();
assert(pubkey2 != null);
Crypt.generate_key_cert(key_path, cert_path, "custom-cn");
uint8[] data;
try {
File.new_for_path(cert_path).load_contents(null, out data, null);
} catch (Error e) {
Test.fail();
}
debug("public key1:\n%s", pubkey1);
debug("public key2:\n%s", pubkey2);
assert(pubkey1 == pubkey2);
var datum = GnuTLS.Datum() { data=data, size=data.length };
var cert = GnuTLS.X509.Certificate.create();
var res = cert.import(ref datum, GnuTLS.X509.CertificateFormat.PEM);
assert(res == GnuTLS.ErrorCode.SUCCESS);
// verify DN
var dn = new uint8[1024];
size_t sz = dn.length;
cert.get_dn(dn, ref sz);
debug("dn: %s\n", (string)dn);
var issuer_dn = new uint8[1024];
sz = issuer_dn.length;
cert.get_issuer_dn(issuer_dn, ref sz);
debug("dn: %s\n", (string)issuer_dn);
var subject = (string)dn;
var issuer = (string)issuer_dn;
// verify that the certificate is self signed
assert(subject == issuer);
//
assert("CN=custom-cn" in subject);
}
public static void main(string[] args) {
Test.init(ref args);
Test.add_func("/mconn-crypt-vala/simple", () => {
test_simple();
});
Test.add_func("/mconn-crypt-vala/generated", test_generate);
Test.add_func("/mconn-crypt-vala/load", test_generate_load);
Test.add_func("/mconn-crypt-vala/verify-cn", test_custom_cn);
Test.run();
}

Loading…
Cancel
Save