Login | Register For Free | Help
Search for: (Advanced)

Mailing List Archive: vpnc: devel

[PATCH] modularize certificate code + gnutls support

 

 

vpnc devel RSS feed   Index | Next | Previous | View Threaded


dcbw at redhat

May 26, 2009, 12:37 PM

Post #1 of 6 (1311 views)
Permalink
[PATCH] modularize certificate code + gnutls support

vpnc currently requires openssl to provide the hybrid auth mode. That's
not ideal, since vpnc doesn't not include the OpenSSL exception in its
license, meaning many distributions cannot ship a hybrid-auth-enabled
vpnc.

This patch splits out the certificate handing functions into a more
modular framework, and adds a gnutls implementation of certificate
handling bits, including testcases to ensure things work as expected.
Hybrid-auth with gnutls is the default mode now, since vpnc has no
licensing problem with gnutls. That means everyone gets hybrid FTW.

I haven't been able to test against a real hybrid-auth enabled VPN
concentrator, but the testcases are built so that I have a reasonable
confidence that it will work. I have verified that this patch does not
regress connections to both Cisco PIX and Juniper Netscreen boxes in the
Group Auth IKE mode (ie, non-hybrid mode).

Patch against current trunk, and if people want, I have a version
against 0.5.3 as well.

Dan


diff --git a/Makefile b/Makefile
index 920bd18..633ad7c 100644
--- a/Makefile
+++ b/Makefile
@@ -27,16 +27,8 @@ SBINDIR=$(PREFIX)/sbin
MANDIR=$(PREFIX)/share/man
DOCDIR=$(PREFIX)/share/doc/vpnc

-SRCS = sysdep.c vpnc-debug.c isakmp-pkt.c tunip.c config.c dh.c math_group.c supp.c decrypt-utils.c
-BINS = vpnc cisco-decrypt
-OBJS = $(addsuffix .o,$(basename $(SRCS)))
-BINOBJS = $(addsuffix .o,$(BINS))
-BINSRCS = $(addsuffix .c,$(BINS))
-VERSION := $(shell sh mk-version)
-RELEASE_VERSION := $(shell cat VERSION)
-
# The license of vpnc (Gpl >= 2) is quite likely incompatible with the
-# openssl license. Openssl is currently used to provide certificate
+# openssl license. Openssl is one possible library used to provide certificate
# support for vpnc (hybrid only).
# While it is OK for users to build their own binaries linking in openssl
# with vpnc and even providing dynamically linked binaries it is probably
@@ -47,16 +39,34 @@ RELEASE_VERSION := $(shell cat VERSION)

# Comment this in to obtain a binary with certificate support which is
# GPL incompliant though.
-#OPENSSL_GPL_VIOLATION = -DOPENSSL_GPL_VIOLATION
-#OPENSSLLIBS = -lcrypto
+#OPENSSL_GPL_VIOLATION=yes
+
+CRYPTO_LDADD = $(shell libgnutls-config --libs)
+CRYPTO_CFLAGS = $(shell libgnutls-config --cflags) -DCRYPTO_GNUTLS
+CRYPTO_SRCS = crypto-gnutls.c
+
+ifeq ($(OPENSSL_GPL_VIOLATION), yes)
+CRYPTO_LDADD = -lcrypto
+CRYPTO_CFLAGS = -DOPENSSL_GPL_VIOLATION -DCRYPTO_OPENSSL
+CRYPTO_SRCS = crypto-openssl.c
+endif
+
+SRCS = sysdep.c vpnc-debug.c isakmp-pkt.c tunip.c config.c dh.c math_group.c supp.c decrypt-utils.c crypto.c $(CRYPTO_SRCS)
+BINS = vpnc cisco-decrypt test-crypto
+OBJS = $(addsuffix .o,$(basename $(SRCS)))
+CRYPTO_OBJS = $(addsuffix .o,$(basename $(CRYPTO_SRCS)))
+BINOBJS = $(addsuffix .o,$(BINS))
+BINSRCS = $(addsuffix .c,$(BINS))
+VERSION := $(shell sh mk-version)
+RELEASE_VERSION := $(shell cat VERSION)

CC=gcc
CFLAGS ?= -O3 -g
CFLAGS += -W -Wall -Wmissing-declarations -Wwrite-strings
-CFLAGS += $(shell libgcrypt-config --cflags)
-CPPFLAGS += -DVERSION=\"$(VERSION)\" $(OPENSSL_GPL_VIOLATION)
+CFLAGS += $(shell libgcrypt-config --cflags) $(CRYPTO_CFLAGS)
+CPPFLAGS += -DVERSION=\"$(VERSION)\"
LDFLAGS ?= -g
-LDFLAGS += $(shell libgcrypt-config --libs) $(OPENSSLLIBS)
+LDFLAGS += $(shell libgcrypt-config --libs) $(CRYPTO_LDADD)

ifeq ($(shell uname -s), SunOS)
LDFLAGS += -lnsl -lresolv -lsocket
@@ -80,6 +90,9 @@ vpnc-script : vpnc-script.in
cisco-decrypt : cisco-decrypt.o decrypt-utils.o
$(CC) -o $@ $^ $(LDFLAGS)

+test-crypto : test-crypto.o crypto.o $(CRYPTO_OBJS)
+ $(CC) -o $@ $^ $(LDFLAGS)
+
.depend: $(SRCS) $(BINSRCS)
$(CC) -MM $(SRCS) $(BINSRCS) $(CFLAGS) $(CPPFLAGS) > $@

@@ -103,6 +116,10 @@ vpnc-%.tar.gz :
tar -czf ../$@ vpnc-$*
rm -rf vpnc-$*

+test : all
+ ./test-crypto /etc/pki/tls/cert.pem test/cert0.pem test/cert1.pem test/cert2.pem test/root.pem
+ ./test-crypto /etc/pki/tls/cert.pem test/cert0.crt test/cert1.crt test/cert2.crt test/root.crt
+
dist : VERSION vpnc.8 vpnc-$(RELEASE_VERSION).tar.gz

clean :
diff --git a/config.c b/config.c
index 2e7caa0..6bab051 100644
--- a/config.c
+++ b/config.c
@@ -424,7 +424,7 @@ static const struct config_names_s {
"Authentication mode:\n"
" * psk: pre-shared key (default)\n"
" * cert: server + client certificate (not implemented yet)\n"
- " * hybrid: server certificate + xauth (if built with openssl support)\n",
+ " * hybrid: server certificate + xauth\n",
config_def_auth_mode
}, {
CONFIG_CA_FILE, 1, 1,
@@ -581,10 +581,10 @@ static void print_version(void)
"Public License. For more information about these matters, see the files\n"
"named COPYING.\n");
#ifdef OPENSSL_GPL_VIOLATION
- printf("Built with openssl (certificate) support. Be aware of the\n"
+ printf("Built with openssl certificate support. Be aware of the\n"
"license implications.\n");
#else /* OPENSSL_GPL_VIOLATION */
- printf("Built without openssl (certificate) support.\n");
+ printf("Built with certificate support.\n");
#endif /* OPENSSL_GPL_VIOLATION */
printf("\n");

@@ -699,13 +699,6 @@ void do_config(int argc, char **argv)
printf("%s: unknown authentication mode %s\nknown modes: psk cert hybrid\n", argv[0], config[CONFIG_AUTH_MODE]);
exit(1);
}
-#ifndef OPENSSL_GPL_VIOLATION
- if (opt_auth_mode == AUTH_MODE_HYBRID ||
- opt_auth_mode == AUTH_MODE_CERT) {
- printf("%s was built without openssl: Can't do hybrid or cert mode.\n", argv[0]);
- exit(1);
- }
-#endif
opt_no_encryption = (config[CONFIG_ENABLE_NO_ENCRYPTION]) ? 1 : 0;
opt_udpencapport=atoi(config[CONFIG_UDP_ENCAP_PORT]);

diff --git a/crypto-gnutls.c b/crypto-gnutls.c
new file mode 100644
index 0000000..35885ff
--- /dev/null
+++ b/crypto-gnutls.c
@@ -0,0 +1,477 @@
+/* IPSec VPN client compatible with Cisco equipment.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <gcrypt.h>
+
+#include "config.h"
+#include "sysdep.h"
+#include "crypto.h"
+
+static int gnutls_initialized = 0;
+
+#define CERT_STACK_DEPTH 20
+
+crypto_ctx *crypto_ctx_new(crypto_error **error)
+{
+ crypto_ctx *ctx;
+
+ if (!gnutls_initialized) {
+ if (gnutls_global_init() != 0) {
+ crypto_error_set(error, 1, 0, "error initializing gnutls globals");
+ return NULL;
+ }
+ gnutls_initialized = 1;
+ }
+
+ ctx = gnutls_calloc(1, sizeof(crypto_ctx));
+ if (!ctx) {
+ crypto_error_set(error, 1, ENOMEM, "not enough memory for crypto context");
+ return NULL;
+ }
+
+ ctx->stack = gnutls_calloc(CERT_STACK_DEPTH, sizeof(gnutls_x509_crt_t));
+ if (!ctx->stack) {
+ crypto_ctx_free(ctx);
+ crypto_error_set(error, 1, ENOMEM,
+ "not enough memory for crypto certificate stack");
+ ctx = NULL;
+ }
+
+ return ctx;
+}
+
+void crypto_ctx_free(crypto_ctx *ctx)
+{
+ if (ctx) {
+ int i;
+
+ for (i = 0; i < ctx->num; i++)
+ gnutls_x509_crt_deinit(ctx->stack[i]);
+ gnutls_free(ctx->stack);
+ memset(ctx, 0, sizeof(crypto_ctx));
+ gnutls_free(ctx);
+ }
+}
+
+unsigned char *crypto_read_cert(const char *path,
+ size_t *out_len,
+ crypto_error **error)
+{
+ gnutls_x509_crt_t cert;
+ unsigned char *data = NULL;
+ gnutls_datum dt;
+ size_t fsize = 0;
+ int err;
+
+ dt.data = crypto_read_file(path, &fsize, error);
+ if (!dt.data)
+ return NULL;
+
+ dt.size = (unsigned int) fsize;
+ if (gnutls_x509_crt_init(&cert) != GNUTLS_E_SUCCESS) {
+ crypto_error_set(error, 1, ENOMEM, "not enough memory for certificate");
+ goto out;
+ }
+
+ err = gnutls_x509_crt_import(cert, &dt, GNUTLS_X509_FMT_PEM);
+ if (err != GNUTLS_E_SUCCESS)
+ err = gnutls_x509_crt_import(cert, &dt, GNUTLS_X509_FMT_DER);
+ if (err != GNUTLS_E_SUCCESS) {
+ crypto_error_set(error, 1, 0, "certificate (%s) format unknown", path);
+ goto out;
+ }
+
+ *out_len = 10000;
+ data = malloc(*out_len);
+ err = gnutls_x509_crt_export(cert, GNUTLS_X509_FMT_DER, data, out_len);
+ if (err != GNUTLS_E_SUCCESS) {
+ free(data);
+ *out_len = 0;
+ crypto_error_set(error, 1, 0, "certificate could not be exported");
+ }
+
+out:
+ if (dt.data)
+ gnutls_free(dt.data);
+ gnutls_x509_crt_deinit(cert);
+ return data;
+}
+
+int crypto_push_cert(crypto_ctx *ctx,
+ const unsigned char *data,
+ size_t len,
+ crypto_error **error)
+{
+ gnutls_x509_crt_t cert;
+ gnutls_datum dt;
+ int err;
+
+ if (!ctx || !data || (len <= 0)) {
+ crypto_error_set(error, 1, 0, "invalid crypto context or data");
+ return 1;
+ }
+
+ if (ctx->num >= CERT_STACK_DEPTH) {
+ crypto_error_set(error, 1, 0, "too many certificates in the chain.");
+ return 1;
+ }
+
+ gnutls_x509_crt_init (&cert);
+
+ dt.data = (unsigned char *) data;
+ dt.size = len;
+ err = gnutls_x509_crt_import (cert, &dt, GNUTLS_X509_FMT_DER);
+ if (err != GNUTLS_E_SUCCESS) {
+ gnutls_x509_crt_deinit (cert);
+ crypto_error_set(error, 1, 0, "failed to decode certificate");
+ return 1;
+ }
+
+ ctx->stack[ctx->num] = cert;
+ ctx->num++;
+ return 0;
+}
+
+static int verify_issuer(gnutls_x509_crt_t crt,
+ gnutls_x509_crt_t issuer,
+ crypto_error **error)
+{
+ unsigned int output;
+ time_t now = time (0);
+
+ if (gnutls_x509_crt_verify(crt, &issuer, 1, 0, &output) < 0) {
+ crypto_error_set(error, 1, 0, "failed to verify against issuer");
+ return 1;
+ }
+
+ if (output & GNUTLS_CERT_INVALID) {
+ if (output & GNUTLS_CERT_SIGNER_NOT_FOUND) {
+ crypto_error_set(error, 1, 0, "certificate signer not found");
+ return 1;
+ }
+ if (output & GNUTLS_CERT_SIGNER_NOT_CA) {
+ crypto_error_set(error, 1, 0, "certificate signer not a CA");
+ return 1;
+ }
+ }
+
+ if (gnutls_x509_crt_get_activation_time(crt) > now) {
+ crypto_error_set(error, 1, 0, "certificate activation in the future");
+ return 1;
+ }
+
+ if (gnutls_x509_crt_get_expiration_time(crt) < now) {
+ crypto_error_set(error, 1, 0, "certificate expired");
+ return 1;
+ }
+
+ return 0;
+}
+
+static int verify_last(gnutls_x509_crt_t crt,
+ gnutls_x509_crt_t *ca_list,
+ size_t ca_list_size,
+ crypto_error **error)
+{
+ unsigned int output;
+ time_t now = time (0);
+
+ if (gnutls_x509_crt_verify (crt, ca_list, ca_list_size,
+ GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT,
+ &output) < 0) {
+ crypto_error_set(error, 1, 0, "failed to verify against CA list");
+ return 1;
+ }
+
+ if (output & GNUTLS_CERT_INVALID) {
+ if (output & GNUTLS_CERT_SIGNER_NOT_CA) {
+ crypto_error_set(error, 1, 0, "certificate signer not a CA");
+ return 1;
+ }
+ }
+
+ if (gnutls_x509_crt_get_activation_time(crt) > now) {
+ crypto_error_set(error, 1, 0, "certificate activation in the future");
+ return 1;
+ }
+
+ if (gnutls_x509_crt_get_expiration_time(crt) < now) {
+ crypto_error_set(error, 1, 0, "certificate expired");
+ return 1;
+ }
+
+ return 0;
+}
+
+static gnutls_x509_crt_t *load_one_ca_file(const char *path, crypto_error **error)
+{
+ gnutls_x509_crt_t *list = NULL;
+ gnutls_x509_crt_t cert;
+ gnutls_datum dt;
+ size_t fsize = 0;
+ int err;
+
+ dt.data = crypto_read_file(path, &fsize, error);
+ if (!dt.data)
+ return NULL;
+
+ dt.size = (unsigned int) fsize;
+ if (gnutls_x509_crt_init (&cert) != GNUTLS_E_SUCCESS) {
+ gnutls_free(dt.data);
+ crypto_error_set(error, 1, ENOMEM, "not enough memory for certificate");
+ goto out;
+ }
+
+ err = gnutls_x509_crt_import (cert, &dt, GNUTLS_X509_FMT_PEM);
+ if (err != GNUTLS_E_SUCCESS)
+ err = gnutls_x509_crt_import (cert, &dt, GNUTLS_X509_FMT_DER);
+ gnutls_free(dt.data);
+ if (err != GNUTLS_E_SUCCESS) {
+ crypto_error_set(error, 1, 0, "certificate (%s) format unknown", path);
+ goto out;
+ }
+
+ list = gnutls_malloc(sizeof(gnutls_x509_crt_t));
+ if (!list) {
+ crypto_error_set(error, 1, ENOMEM, "not enough memory for certificate list");
+ goto out;
+ } else
+ list[0] = cert;
+
+out:
+ gnutls_x509_crt_deinit (cert);
+ return list;
+}
+
+static gnutls_x509_crt_t *load_ca_list_file(const char *path,
+ size_t *out_list_size,
+ crypto_error **error)
+{
+ gnutls_x509_crt_t *list = NULL, *old;
+ gnutls_datum dt = { NULL, 0 };
+ size_t fsize = 0;
+ int err;
+ unsigned int num = 200;
+
+ dt.data = crypto_read_file(path, &fsize, error);
+ if (!dt.data)
+ return NULL;
+
+ dt.size = (unsigned int) fsize;
+ old = list = gnutls_malloc(sizeof(gnutls_x509_crt_t) * num);
+ if (!list) {
+ crypto_error_set(error, 1, ENOMEM, "not enough memory for CA list");
+ goto out;
+ }
+
+ err = gnutls_x509_crt_list_import(list, &num, &dt, GNUTLS_X509_FMT_PEM, 0);
+ if (err <= 0) {
+ /* DER then maybe */
+ gnutls_free(list);
+ list = load_one_ca_file(path, error);
+ if (!list)
+ goto out;
+ num = 1;
+ } else
+ num = err; /* gnutls_x509_crt_list_import() returns # read */
+
+ if (err < 0) {
+ crypto_error_set(error, 1, 0, "importing CA list (%d)", err);
+ gnutls_free(list);
+ list = NULL;
+ } else
+ *out_list_size = num;
+
+out:
+ gnutls_free(dt.data);
+ return list;
+}
+
+int crypto_verify_chain(crypto_ctx *ctx,
+ const char *ca_file,
+ const char *ca_dir,
+ crypto_error **error)
+{
+ int err, i, ret = 1, start = 0;
+ gnutls_x509_crt_t *ca_list = NULL;
+ size_t ca_list_size = 0;
+
+ if (!ctx)
+ return 1;
+
+ if (ctx->num == 0)
+ return 0;
+
+ if (ca_file) {
+ ca_list = load_ca_list_file(ca_file, &ca_list_size, error);
+ if (!ca_list)
+ return 1;
+ } else if (ca_dir) {
+ /* FIXME: Try to load all files in the directory I guess... */
+ crypto_error_set(error, 1, 0, "ca_dir not yet supported");
+ return 1;
+ }
+
+ /* If the server cert is self-signed, ignore it in the issuers check */
+ err = gnutls_x509_crt_check_issuer(ctx->stack[0], ctx->stack[0]);
+ if (err > 0)
+ start++;
+
+ /* Check each certificate against its issuer */
+ for (i = start; i < ctx->num - 1; i++) {
+ if (verify_issuer(ctx->stack[i], ctx->stack[i + 1], error))
+ goto out;
+ }
+
+ /* Verify the last certificate */
+ if (verify_last(ctx->stack[ctx->num - 1], ca_list, ca_list_size, error))
+ goto out;
+
+ ret = 0;
+
+out:
+ if (ca_list) {
+ for (i = 0; i < (int) ca_list_size; i++)
+ gnutls_x509_crt_deinit(ca_list[i]);
+ gnutls_free(ca_list);
+ }
+ return ret;
+}
+
+unsigned char *crypto_decrypt_signature(crypto_ctx *ctx,
+ const unsigned char *sig_data,
+ size_t sig_len,
+ size_t *out_len,
+ unsigned int padding,
+ crypto_error **error)
+{
+ unsigned char *rec_hash = NULL;
+ gnutls_datum_t n = { NULL, 0 }, e = { NULL, 0 };
+ int err, algo;
+ gcry_sexp_t key = NULL, sig = NULL, decrypted = NULL, child = NULL;
+ gcry_mpi_t n_mpi = NULL, e_mpi = NULL, dec_mpi = NULL;
+ size_t hash_len = 0;
+
+ if (!ctx) {
+ crypto_error_set(error, 1, 0, "invalid crypto context");
+ return NULL;
+ }
+
+ if (!ctx->num) {
+ crypto_error_set(error, 1, 0, "no certificates in the stack");
+ return NULL;
+ }
+
+ algo = gnutls_x509_crt_get_pk_algorithm(ctx->stack[ctx->num - 1], NULL);
+ if (algo != GNUTLS_PK_RSA) {
+ crypto_error_set(error, 1, 0, "certificate public key algorithm not RSA");
+ return NULL;
+ }
+
+ err = gnutls_x509_crt_get_pk_rsa_raw(ctx->stack[ctx->num - 1], &n, &e);
+ if (err != GNUTLS_E_SUCCESS) {
+ crypto_error_set(error, 1, 0, "error getting certificate public key");
+ return NULL;
+ }
+
+ err = gcry_mpi_scan(&n_mpi, GCRYMPI_FMT_USG, n.data, n.size, NULL);
+ if (err) {
+ crypto_error_set(error, 1, 0, "invalid RSA key 'n' format");
+ goto out;
+ }
+
+ err = gcry_mpi_scan(&e_mpi, GCRYMPI_FMT_USG, e.data, e.size, NULL);
+ if (err) {
+ crypto_error_set(error, 1, 0, "invalid RSA key 'e' format");
+ goto out;
+ }
+
+ err = gcry_sexp_build(&key, NULL, "(public-key (rsa (n %m) (e %m)))", n_mpi, e_mpi);
+ if (err) {
+ crypto_error_set(error, 1, 0, "could not create public-key expression");
+ goto out;
+ }
+
+ switch (padding) {
+ case CRYPTO_PAD_NONE:
+ err = gcry_sexp_build(&sig, NULL, "(data (flags raw) (value %b))", sig_len, sig_data);
+ break;
+ case CRYPTO_PAD_PKCS1:
+ err = gcry_sexp_build(&sig, NULL, "(data (flags pkcs1) (value %b))", sig_len, sig_data);
+ break;
+ default:
+ crypto_error_set(error, 1, 0, "unknown padding mechanism %d", padding);
+ goto out;
+ }
+
+ if (err) {
+ crypto_error_set(error, 1, 0, "could not create signature expression");
+ goto out;
+ }
+
+ /* encrypt is equivalent to public key decryption for RSA keys */
+ err = gcry_pk_encrypt(&decrypted, sig, key);
+ if (err) {
+ crypto_error_set(error, 1, 0, "could not decrypt signature");
+ goto out;
+ }
+
+ child = gcry_sexp_find_token(decrypted, "a", 1);
+ if (!child) {
+ crypto_error_set(error, 1, 0, "could not get decrypted signature result");
+ goto out;
+ }
+
+ dec_mpi = gcry_sexp_nth_mpi(child, 1, GCRYMPI_FMT_USG);
+ gcry_sexp_release(child);
+
+ if (!dec_mpi) {
+ crypto_error_set(error, 1, 0, "could not get decrypted signature result");
+ goto out;
+ }
+
+ gcry_mpi_aprint(GCRYMPI_FMT_USG, &rec_hash, &hash_len, dec_mpi);
+ if (!rec_hash)
+ crypto_error_set(error, 1, 0, "could not get extract decrypted signature");
+ else
+ *out_len = (int) hash_len;
+
+out:
+ if (dec_mpi)
+ gcry_mpi_release(dec_mpi);
+ if (decrypted)
+ gcry_sexp_release(decrypted);
+ if (key)
+ gcry_sexp_release(key);
+ if (sig)
+ gcry_sexp_release(sig);
+ if (n_mpi)
+ gcry_mpi_release(n_mpi);
+ if (e_mpi)
+ gcry_mpi_release(e_mpi);
+ if (n.data)
+ gcry_free(n.data);
+ if (e.data)
+ gcry_free(e.data);
+
+ return rec_hash;
+}
+
diff --git a/crypto-gnutls.h b/crypto-gnutls.h
new file mode 100644
index 0000000..31443fc
--- /dev/null
+++ b/crypto-gnutls.h
@@ -0,0 +1,30 @@
+/* IPSec VPN client compatible with Cisco equipment.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#ifndef __CRYPTO_GNUTLS_H__
+#define __CRTPTO_GNUTLS_H__
+
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+
+typedef struct {
+ int num;
+ gnutls_x509_crt_t *stack;
+} crypto_ctx;
+
+#endif /* __CRYPTO_GNUTLS_H__ */
+
diff --git a/crypto-openssl.c b/crypto-openssl.c
new file mode 100644
index 0000000..d3c057c
--- /dev/null
+++ b/crypto-openssl.c
@@ -0,0 +1,323 @@
+/* IPSec VPN client compatible with Cisco equipment.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <openssl/pem.h>
+#include "config.h"
+#include "sysdep.h"
+#include "crypto.h"
+
+crypto_ctx *crypto_ctx_new(crypto_error **error)
+{
+ crypto_ctx *ctx;
+
+ ctx = malloc(sizeof(crypto_ctx));
+ if (!ctx) {
+ crypto_error_set(error, 1, ENOMEM,
+ "not enough memory for crypto context");
+ return NULL;
+ }
+
+ OpenSSL_add_all_ciphers();
+ OpenSSL_add_all_digests();
+ OpenSSL_add_all_algorithms();
+ ERR_load_crypto_strings();
+
+ memset(ctx, 0, sizeof(crypto_ctx));
+ ctx->stack = sk_X509_new_null();
+ if (!ctx->stack) {
+ crypto_ctx_free(ctx);
+ crypto_error_set(error, 1, ENOMEM,
+ "not enough memory for crypto certificate stack");
+ ctx = NULL;
+ }
+
+ return ctx;
+}
+
+void crypto_ctx_free(crypto_ctx *ctx)
+{
+ if (ctx) {
+ if (ctx->stack)
+ sk_X509_free(ctx->stack);
+
+ memset(ctx, 0, sizeof(crypto_ctx));
+ free(ctx);
+ }
+}
+
+static int password_cb(char *buf, int size, int rwflag, void *userdata)
+{
+ /* Dummy callback to ensure openssl doesn't prompt for a password */
+ return 0;
+}
+
+unsigned char *crypto_read_cert(const char *path,
+ size_t *out_len,
+ crypto_error **error)
+{
+ FILE *fp;
+ X509 *cert = NULL;
+ unsigned char *data = NULL, *p;
+
+ fp = fopen(path, "r");
+ if (!fp) {
+ crypto_error_set(error, 1, 0, "certificate (%s) could not be opened", path);
+ return NULL;
+ }
+
+ cert = PEM_read_X509(fp, NULL, password_cb, NULL);
+ fclose (fp);
+
+ if (!cert) {
+ /* Try DER then */
+ p = data = crypto_read_file(path, out_len, error);
+ if (!data || !*out_len) {
+ crypto_error_set(error, 1, 0, "could not read certificate %s", path);
+ return NULL;
+ }
+
+ cert = d2i_X509(NULL, (const unsigned char **) &p, (int) (*out_len));
+ if (!cert) {
+ free(data);
+ crypto_error_set(error, 1, 0, "could not allocate memory for certificate");
+ return NULL;
+ }
+
+ return data;
+ }
+
+ /* Get length of DER data */
+ *out_len = i2d_X509(cert, NULL);
+ if (!*out_len) {
+ crypto_error_set(error, 1, 0, "invalid certificate length");
+ goto out;
+ }
+
+ p = data = malloc(*out_len);
+ if (!data) {
+ crypto_error_set(error, 1, 0, "could not allocate memory for certificate");
+ goto out;
+ }
+
+ /* Encode the certificate to DER */
+ *out_len = i2d_X509(cert, &p);
+ if (!*out_len) {
+ crypto_error_set(error, 1, 0, "could not export certificate data");
+ if (data) {
+ free(data);
+ data = NULL;
+ }
+ goto out;
+ }
+
+out:
+ if (cert)
+ X509_free(cert);
+ return data;
+}
+
+int crypto_push_cert(crypto_ctx *ctx,
+ const unsigned char *data,
+ size_t len,
+ crypto_error **error)
+{
+ X509 *cert = NULL;
+
+ if (!ctx || !data || (len <= 0)) {
+ crypto_error_set(error, 1, 0, "invalid crypto context or data");
+ return 1;
+ }
+
+ /* convert the certificate to an openssl-X509 structure and push it onto the chain stack */
+ cert = d2i_X509(NULL, &data, (int) len);
+ if (!cert) {
+ ERR_print_errors_fp(stderr);
+ crypto_error_set(error, 1, 0, "failed to decode certificate");
+ return 1;
+ }
+ sk_X509_push(ctx->stack, cert);
+ return 0;
+}
+
+int crypto_verify_chain(crypto_ctx *ctx,
+ const char *ca_file,
+ const char *ca_dir,
+ crypto_error **error)
+{
+ X509 *x509;
+ X509_STORE *store = NULL;
+ X509_LOOKUP *lookup = NULL;
+ X509_STORE_CTX *verify_ctx = NULL;
+ int ret = 1;
+
+ if (!ctx) {
+ crypto_error_set(error, 1, 0, "invalid crypto context");
+ return 1;
+ }
+
+ x509 = sk_X509_value(ctx->stack, sk_X509_num(ctx->stack) - 1);
+ if (x509 == NULL) {
+ ERR_print_errors_fp (stderr);
+ crypto_error_set(error, 1, 0, "no certificates in the stack");
+ return 1;
+ }
+
+ /* BEGIN - verify certificate chain */
+ /* create the cert store */
+ if (!(store = X509_STORE_new())) {
+ crypto_error_set(error, 1, 0, "error creating X509_STORE object");
+ return 1;
+ }
+ /* load the CA certificates */
+ if (X509_STORE_load_locations (store, ca_file, ca_dir) != 1) {
+ crypto_error_set(error, 1, 0, "error loading the CA file (%s) "
+ "or directory (%s)", ca_file, ca_dir);
+ goto out;
+ }
+ if (X509_STORE_set_default_paths (store) != 1) {
+ crypto_error_set(error, 1, 0, "error loading the system-wide CA"
+ " certificates");
+ goto out;
+ }
+
+#if 0
+ /* check CRLs */
+ /* add the corresponding CRL for each CA in the chain to the lookup */
+#define CRL_FILE "root-ca-crl.crl.pem"
+
+ if (!(lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()))) {
+ crypto_error_set(error, 1, 0, "error creating X509 lookup object.");
+ goto out;
+ }
+ if (X509_load_crl_file(lookup, CRL_FILE, X509_FILETYPE_PEM) != 1) {
+ ERR_print_errors_fp(stderr);
+ crypto_error_set(error, 1, 0, "error reading CRL file");
+ goto out;
+ }
+ X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
+#endif /* 0 */
+
+ /* create a verification context and initialize it */
+ if (!(verify_ctx = X509_STORE_CTX_new ())) {
+ crypto_error_set(error, 1, 0, "error creating X509_STORE_CTX object");
+ goto out;
+ }
+ /* X509_STORE_CTX_init did not return an error condition in prior versions */
+ if (X509_STORE_CTX_init (verify_ctx, store, x509, ctx->stack) != 1) {
+ crypto_error_set(error, 1, 0, "error intializing verification context");
+ goto out;
+ }
+
+ /* verify the certificate */
+ if (X509_verify_cert(verify_ctx) != 1) {
+ ERR_print_errors_fp(stderr);
+ crypto_error_set(error, 2, 0, "error verifying the certificate "
+ "chain");
+ goto out;
+ }
+
+ ret = 0;
+
+out:
+ if (lookup)
+ X509_LOOKUP_free(lookup);
+ if (store)
+ X509_STORE_free(store);
+ if (verify_ctx)
+ X509_STORE_CTX_free(verify_ctx);
+ return ret;
+}
+
+unsigned char *crypto_decrypt_signature(crypto_ctx *ctx,
+ const unsigned char *sig_data,
+ size_t sig_len,
+ size_t *out_len,
+ unsigned int padding,
+ crypto_error **error)
+{
+ X509 *x509;
+ EVP_PKEY *pkey = NULL;
+ RSA *rsa;
+ unsigned char *hash = NULL;
+ int tmp_len = -1, ossl_pad;
+
+ *out_len = 0;
+
+ if (!ctx) {
+ crypto_error_set(error, 1, 0, "invalid crypto context");
+ return NULL;
+ }
+
+ x509 = sk_X509_value(ctx->stack, sk_X509_num(ctx->stack) - 1);
+ if (x509 == NULL) {
+ ERR_print_errors_fp (stderr);
+ crypto_error_set(error, 1, 0, "no certificates in the stack");
+ return NULL;
+ }
+
+ pkey = X509_get_pubkey(x509);
+ if (pkey == NULL) {
+ ERR_print_errors_fp (stderr);
+ crypto_error_set(error, 1, 0, "error getting certificate public key");
+ return NULL;
+ }
+
+ rsa = EVP_PKEY_get1_RSA(pkey);
+ if (rsa == NULL) {
+ ERR_print_errors_fp (stderr);
+ crypto_error_set(error, 1, 0, "error getting public key RSA");
+ goto out;
+ }
+
+ hash = calloc(1, RSA_size(rsa));
+ if (!hash) {
+ crypto_error_set(error, 1, 0, "not enough memory to decrypt signature");
+ goto out;
+ }
+
+ switch (padding) {
+ case CRYPTO_PAD_NONE:
+ ossl_pad = RSA_NO_PADDING;
+ break;
+ case CRYPTO_PAD_PKCS1:
+ ossl_pad = RSA_PKCS1_PADDING;
+ break;
+ default:
+ crypto_error_set(error, 1, 0, "unknown padding mechanism %d", padding);
+ goto out;
+ }
+
+ tmp_len = RSA_public_decrypt(sig_len, sig_data, hash, rsa, ossl_pad);
+ if (tmp_len > 0) {
+ *out_len = (size_t) tmp_len;
+ } else {
+ ERR_print_errors_fp (stderr);
+ crypto_error_set(error, 1, 0, "could not decrypt signature");
+ free(hash);
+ hash = NULL;
+ }
+
+out:
+ if (pkey)
+ EVP_PKEY_free(pkey);
+ return hash;
+}
+
diff --git a/crypto-openssl.h b/crypto-openssl.h
new file mode 100644
index 0000000..57ac883
--- /dev/null
+++ b/crypto-openssl.h
@@ -0,0 +1,33 @@
+/* IPSec VPN client compatible with Cisco equipment.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#ifndef OPENSSL_GPL_VIOLATION
+#error "openssl support cannot be built without defining OPENSSL_GPL_VIOLATION"
+#endif
+
+#ifndef __CRYPTO_OPENSSL_H__
+#define __CRYPTO_OPENSSL_H__
+
+#include <openssl/x509.h>
+#include <openssl/err.h>
+
+typedef struct {
+ STACK_OF(X509) *stack;
+} crypto_ctx;
+
+#endif /* __CRYPTO_OPENSSL_H__ */
+
diff --git a/crypto.c b/crypto.c
new file mode 100644
index 0000000..62fb371
--- /dev/null
+++ b/crypto.c
@@ -0,0 +1,143 @@
+/* IPSec VPN client compatible with Cisco equipment.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <error.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "crypto.h"
+
+
+#define MSG_SIZE 200
+void crypto_error_set(crypto_error **error,
+ int code,
+ int in_errno,
+ const char *fmt, ...)
+{
+ va_list args;
+
+ if (!error)
+ return;
+ if (*error) {
+ fprintf(stderr, "%s: called with non-NULL *error\n", __func__);
+ return;
+ }
+
+ *error = calloc(1, sizeof(crypto_error));
+ if (!*error)
+ return;
+
+ (*error)->code = code;
+ (*error)->err = in_errno;
+
+ (*error)->msg = malloc(MSG_SIZE);
+ if (!(*error)->msg) {
+ fprintf(stderr, "%s: not enough memory for error message\n", __func__);
+ crypto_error_clear(error);
+ return;
+ }
+
+ va_start(args, fmt);
+ if (vsnprintf((*error)->msg, MSG_SIZE, fmt, args) == -1)
+ (*error)->msg[0] = '\0';
+ va_end(args);
+}
+
+void crypto_error_free(crypto_error *error)
+{
+ if (error) {
+ if (error->msg)
+ free(error->msg);
+ memset(error, 0, sizeof(crypto_error));
+ free(error);
+ }
+}
+
+void crypto_error_clear(crypto_error **error)
+{
+ if (error && *error) {
+ crypto_error_free(*error);
+ *error = NULL;
+ }
+}
+
+void crypto_call_error(crypto_error *err)
+{
+ if (err)
+ error(err->code, err->err, "%s\n", err->msg);
+ else
+ error(1, 0, "unknown error");
+}
+
+unsigned char *
+crypto_read_file(const char *path, size_t *out_len, crypto_error **error)
+{
+ struct stat st;
+ int fd;
+ ssize_t bytes_read;
+ size_t file_size;
+ unsigned char *data = NULL;
+
+ *out_len = 0;
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ crypto_error_set(error, 1, errno, "failed to open file '%s'", path);
+ return NULL;
+ }
+
+ if (fstat(fd, &st) < 0) {
+ crypto_error_set(error, 1, errno, "failed to stat file '%s'", path);
+ goto out;
+ }
+
+ if (st.st_size <= 0 || st.st_size > INT_MAX) {
+ crypto_error_set(error, 1, errno, "invalid file '%s' length %ld", path, st.st_size);
+ goto out;
+ }
+
+ file_size = st.st_size;
+ data = malloc(file_size);
+ if (!data) {
+ crypto_error_set(error, 1, ENOMEM, "not enough memory to read file '%s'", path);
+ goto out;
+ }
+
+ do {
+ bytes_read = read(fd, &(data[*out_len]), (st.st_size - *out_len));
+ if (bytes_read < 0) {
+ free(data);
+ data = NULL;
+ *out_len = 0;
+ crypto_error_set(error, 1, errno, "failed to read file '%s'", path);
+ goto out;
+ }
+ *out_len += bytes_read;
+ } while ((bytes_read > 0) && (*out_len <= file_size));
+
+out:
+ close(fd);
+ return data;
+}
+
diff --git a/crypto.h b/crypto.h
new file mode 100644
index 0000000..88fa5bb
--- /dev/null
+++ b/crypto.h
@@ -0,0 +1,139 @@
+/* IPSec VPN client compatible with Cisco equipment.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#ifndef __CRYPTO_H__
+#define __CRYPTO_H__
+
+#include <stdarg.h>
+
+typedef struct {
+ int code;
+ int err;
+ char *msg;
+} crypto_error;
+
+void crypto_error_set(crypto_error **error, int code, int in_errno, const char *fmt, ...);
+
+void crypto_error_free(crypto_error *error);
+
+void crypto_error_clear(crypto_error **error);
+
+void crypto_call_error(crypto_error *err);
+
+unsigned char *crypto_read_file(const char *path, size_t *out_len, crypto_error **error);
+
+#if CRYPTO_GNUTLS
+#include "crypto-gnutls.h"
+#elif CRYPTO_OPENSSL
+#include "crypto-openssl.h"
+#else
+#error "no crypto library defined"
+#endif
+
+#define CRYPTO_PAD_NONE 0
+#define CRYPTO_PAD_PKCS1 1
+
+/**
+ * crypto_push_cert:
+ *
+ * Allocates a crypto context with the resources necessary for the specific
+ * crypto library being used.
+ *
+ * Returns: a valid crypto context, or #NULL on error
+ **/
+crypto_ctx *crypto_ctx_new(crypto_error **error);
+
+/**
+ * crypto_ctx_free:
+ * @ctx: a valid crypto context created with crypto_ctx_new()
+ *
+ * Frees resources allocated by crypo_ctx_new().
+ **/
+void crypto_ctx_free(crypto_ctx *ctx);
+
+/**
+ * crypto_read_cert:
+ * @path: path to certificate file in either PEM or DER format
+ * @out_len: length of raw certificate data
+ * @error: return location for an error
+ *
+ * Loads a certificate and returns the binary ASN certificate data;
+ *
+ * Returns: certificate data on success, NULL on error
+ **/
+unsigned char *crypto_read_cert(const char *path,
+ size_t *out_len,
+ crypto_error **error);
+
+/**
+ * crypto_push_cert:
+ * @ctx: a valid crypto context created with crypto_ctx_new()
+ * @data: buffer containing raw certificate data
+ * @len: length of raw certificate data
+ * @error: return location for an error
+ *
+ * Pushes the given certificate onto the context's certificate stack.
+ *
+ * Returns: 0 on success, 1 on error
+ **/
+int crypto_push_cert(crypto_ctx *ctx,
+ const unsigned char *data,
+ size_t len,
+ crypto_error **error);
+
+/**
+ * crypto_verify_chain:
+ * @ctx: a valid crypto context created with crypto_ctx_new()
+ * @ca_file: path of a CA certificate file to use for verification of the
+ * certificate stack. File may be a PEM-encoded file containing
+ * multiple CA certificates. @ca_file is preferred over @ca_dir
+ * @ca_dir: directory containing CA certificates to use for verification of the
+ * certificate stack
+ * @error: return location for an error
+ *
+ * Verifies the certificate stack previously built with crypto_push_cert() using
+ * the supplied CA certificates or certificate locations.
+ *
+ * Returns: 0 on success, 1 on error
+ **/
+int crypto_verify_chain(crypto_ctx *ctx,
+ const char *ca_file,
+ const char *ca_dir,
+ crypto_error **error);
+
+/**
+ * crypto_decrypt_signature:
+ * @ctx: a valid crypto context created with crypto_ctx_new()
+ * @sig_data: encrypted signature data
+ * @sig_len: length of encrypted signature data
+ * @out_len: size of decrypted signature data
+ * @error: return location for an error
+ *
+ * Recovers the message digest stored in @sig_data using the public key of the
+ * last certificate on the certificate stack
+ *
+ * Returns: decrypted message digest, or #NULL on error
+ **/
+unsigned char *crypto_decrypt_signature(crypto_ctx *ctx,
+ const unsigned char *sig_data,
+ size_t sig_len,
+ size_t *out_hash_len,
+ unsigned int padding,
+ crypto_error **error);
+
+#endif /* __CRYPTO_H__ */
+
diff --git a/test-crypto.c b/test-crypto.c
new file mode 100644
index 0000000..05c1322
--- /dev/null
+++ b/test-crypto.c
@@ -0,0 +1,133 @@
+/* IPSec VPN client compatible with Cisco equipment.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <stdio.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <limits.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include "crypto.h"
+
+int main(int argc, char *argv[])
+{
+ crypto_ctx *cctx;
+ crypto_error *error = NULL;
+ int i;
+ unsigned char *data;
+ size_t size = 0;
+ const unsigned char sig_data[] = {
+ 0x30, 0x82, 0x04, 0xb5, 0x30, 0x82, 0x03, 0x9d, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x01, 0x02,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x04, 0x05, 0x00, 0x30,
+ 0x81, 0x9f, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
+ 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x09, 0x42, 0x65, 0x72, 0x6b, 0x73, 0x68,
+
+ 0x69, 0x72, 0x65, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x07, 0x4e, 0x65,
+ 0x77, 0x62, 0x75, 0x72, 0x79, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e,
+ 0x4d, 0x79, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x31, 0x11,
+ 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x08, 0x54, 0x68, 0x65, 0x20, 0x55, 0x6e, 0x69,
+
+ 0x74, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x74, 0x65, 0x73, 0x74,
+ 0x2e, 0x73, 0x6f, 0x6d, 0x65, 0x77, 0x68, 0x65, 0x72, 0x65, 0x2e, 0x6f, 0x72, 0x67, 0x31, 0x21,
+ 0x30, 0x1f, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x12, 0x74,
+ 0x65, 0x73, 0x74, 0x40, 0x73, 0x6f, 0x6d, 0x65, 0x77, 0x68, 0x65, 0x72, 0x65, 0x2e, 0x6f, 0x72,
+
+ 0x67, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x39, 0x30, 0x34, 0x32, 0x38, 0x30, 0x32, 0x35, 0x30, 0x35,
+ 0x32, 0x5a, 0x17, 0x0d, 0x31, 0x39, 0x30, 0x34, 0x32, 0x36, 0x30, 0x32, 0x35, 0x30, 0x35, 0x32,
+ 0x5a, 0x30, 0x81, 0x8f, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x09, 0x42, 0x65, 0x72, 0x6b,
+ };
+ const unsigned char dec_data[] = {
+ 0x7c, 0x2a, 0xe4, 0x60, 0x10, 0x9f, 0xab, 0xd6, 0x76, 0x7b, 0x9d, 0x16, 0xbb, 0xd3, 0x16, 0xa3,
+ 0x61, 0x50, 0x56, 0x13, 0xe4, 0x61, 0x0e, 0x90, 0x71, 0x5c, 0x47, 0xae, 0x4a, 0xc2, 0x89, 0xf8,
+ 0x47, 0x61, 0x4c, 0x3f, 0xd6, 0x11, 0x97, 0xb7, 0x0d, 0x84, 0x86, 0xdd, 0xe9, 0x6d, 0x3e, 0x89,
+ 0xe0, 0x4f, 0x7a, 0x95, 0x3f, 0x6e, 0xe4, 0xcd, 0xb2, 0x80, 0x3e, 0x19, 0x3e, 0x97, 0x7c, 0xdf,
+ 0xd5, 0xff, 0xcb, 0x90, 0xfb, 0x71, 0x9c, 0xef, 0xa1, 0xf6, 0x8c, 0x36, 0xb3, 0x1f, 0x63, 0x7f,
+ 0x32, 0xf5, 0x00, 0x12, 0x5e, 0x13, 0x84, 0x88, 0xe3, 0x13, 0x1c, 0x11, 0x2d, 0x9a, 0xd7, 0xec,
+ 0x51, 0x94, 0x20, 0x6e, 0x8f, 0x69, 0xdf, 0x07, 0xe9, 0x46, 0x3b, 0xd9, 0x1c, 0x0a, 0xc0, 0x60,
+ 0x90, 0x3a, 0x9a, 0x18, 0xa8, 0x19, 0xc6, 0x78, 0xc9, 0xf3, 0x1a, 0xbb, 0xca, 0xa8, 0xb5, 0x05,
+ 0x6b, 0xa8, 0xfb, 0xeb, 0xdd, 0x19, 0x56, 0xc4, 0xfe, 0x7c, 0x84, 0xb1, 0xfd, 0x92, 0xbd, 0xe2,
+ 0xb2, 0x94, 0x57, 0x3d, 0x03, 0x0a, 0xf1, 0xee, 0xca, 0xec, 0x8a, 0x0f, 0xb6, 0x23, 0x0b, 0x44,
+ 0x14, 0x0d, 0xe0, 0xb1, 0x68, 0x38, 0x56, 0x7c, 0x66, 0x60, 0x8f, 0x54, 0x8b, 0x5c, 0x80, 0x37,
+ 0x94, 0x27, 0x89, 0x47, 0x2c, 0x24, 0x45, 0x6b, 0x76, 0xdd, 0xfb, 0xf1, 0x31, 0xef, 0x7f, 0xa4,
+ 0xba, 0x95, 0x4b, 0x91, 0x9c, 0x86, 0xa6, 0x48, 0xa2, 0x5a, 0x41, 0x64, 0x31, 0x14, 0x80, 0x6b,
+ 0xb3, 0x0d, 0x46, 0x14, 0xb2, 0x61, 0x49, 0x81, 0xf5, 0x14, 0x2e, 0x1c, 0x3b, 0x7b, 0xc2, 0x23,
+ 0x9d, 0x31, 0x66, 0x49, 0x56, 0x50, 0x69, 0x69, 0x5a, 0x5c, 0x82, 0x68, 0x96, 0x04, 0xc1, 0x76,
+ 0x18, 0x19, 0x13, 0x95, 0xad, 0xbd, 0x5f, 0x96, 0x6d, 0xfe, 0xde, 0x65, 0x6a, 0x78, 0x47, 0x63,
+ };
+
+ if (argc < 4) {
+ fprintf(stderr, "Need at least 3 arguments: <ca> <cert1> <server>\n");
+ return 1;
+ }
+
+ cctx = crypto_ctx_new(&error);
+ if (!cctx) {
+ fprintf(stderr, "Error initializing crypto: %s\n", error->msg);
+ return error->code;
+ }
+
+ /* Load certificates */
+ for (i = 2; i < argc; i++) {
+ data = crypto_read_cert(argv[i], &size, &error);
+ if (!data) {
+ fprintf(stderr, "Error reading cert %d: %s\n", i + 1, error->msg);
+ return error->code;
+ }
+ if (crypto_push_cert(cctx, data, size, &error)) {
+ free(data);
+ fprintf(stderr, "Error pushing cert %d: %s\n", i + 1, error->msg);
+ return error->code;
+ }
+ free(data);
+ }
+
+ /* Verify the cert chain */
+ if (crypto_verify_chain(cctx, argv[1], NULL, &error) != 0) {
+ fprintf(stderr, "Error verifying chain: %s\n", error && error->msg ? error->msg : "(none)");
+ return error->code;
+ }
+
+ /* Decrypt something using the public key of the server certificate */
+ size = 0;
+ data = crypto_decrypt_signature(cctx, &sig_data[0], sizeof(sig_data), &size, CRYPTO_PAD_NONE, &error);
+ if (!data || !size) {
+ fprintf(stderr, "Error decrypting signature: %s\n", error && error->msg ? error->msg : "(none)");
+ return error->code;
+ }
+
+ if (size != sizeof(dec_data)) {
+ fprintf(stderr, "Error decrypting signature: unexpected "
+ "decrypted size %lu (expected %lu)\n", size, sizeof(dec_data));
+ return 1;
+ }
+
+ if (memcmp(data, dec_data, sizeof(dec_data))) {
+ fprintf(stderr, "Error decrypting signature: decrypted data did"
+ " not match expected decrypted data\n");
+ return 1;
+ }
+ free(data);
+
+ fprintf(stdout, "Success\n");
+
+ crypto_ctx_free(cctx);
+ return 0;
+}
+
diff --git a/test/cert0.crt b/test/cert0.crt
new file mode 100644
index 0000000..1d9d811
Binary files /dev/null and b/test/cert0.crt differ
diff --git a/test/cert0.pem b/test/cert0.pem
new file mode 100644
index 0000000..51b0bf5
--- /dev/null
+++ b/test/cert0.pem
@@ -0,0 +1,36 @@
+ 0 s:/1.3.6.1.4.1.311.60.2.1.3=CH/1.3.6.1.4.1.311.60.2.1.2=Bern/2.5.4.15=V1.0, Clause 5(b)/serialNumber=CH-035.7.001.278-9/C=CH/ST=Zuerich/L=Zuerich/O=SWITCH/CN=www.switch.ch
+ i:/C=BM/O=QuoVadis Limited/OU=www.quovadisglobal.com/CN=QuoVadis Global SSL ICA
+-----BEGIN CERTIFICATE-----
+MIIFpjCCBI6gAwIBAgICD4YwDQYJKoZIhvcNAQEFBQAwazELMAkGA1UEBhMCQk0x
+GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHzAdBgNVBAsTFnd3dy5xdW92YWRp
+c2dsb2JhbC5jb20xIDAeBgNVBAMTF1F1b1ZhZGlzIEdsb2JhbCBTU0wgSUNBMB4X
+DTA5MDExNTA5MjEzM1oXDTExMDExNTA5MjEzM1owgb8xEzARBgsrBgEEAYI3PAIB
+AxMCQ0gxFTATBgsrBgEEAYI3PAIBAhMEQmVybjEaMBgGA1UEDxMRVjEuMCwgQ2xh
+dXNlIDUoYikxGzAZBgNVBAUTEkNILTAzNS43LjAwMS4yNzgtOTELMAkGA1UEBhMC
+Q0gxEDAOBgNVBAgTB1p1ZXJpY2gxEDAOBgNVBAcTB1p1ZXJpY2gxDzANBgNVBAoT
+BlNXSVRDSDEWMBQGA1UEAxMNd3d3LnN3aXRjaC5jaDCCASIwDQYJKoZIhvcNAQEB
+BQADggEPADCCAQoCggEBAKqwpnO5zcYxC829nQpHkFeZp9Hp4gzlyvHj0BHaLx9F
+pQxaFw7bsgbrMR+M+OjI+NXbWhPbc6ftY5VjqYwaVQAWmA3vvo5ELsy11lzyQusi
+ZT2wjx0Rx1SV7ocP20rDS0gkFqrej0ymdQKO/mcyht53a076goaUuacOElhNttlM
+baXiGwSMFURVUA/9dcOC8HhYPokzWnQD7BkFl3pg3BsmHz5mQ+rh79e+rKJylsXS
+qfSI1zD0QQTLd01JBzX4iOM37IlHBAJb/EWAuNJPjA9SHZlfILhphaAiEtKUlcyL
+4atAUUgbM2SI9yFfwALHliyBgoBcsZSd7ZlzhaFVA6UCAwEAAaOCAf0wggH5MHQG
+CCsGAQUFBwEBBGgwZjAqBggrBgEFBQcwAYYeaHR0cDovL29jc3AucXVvdmFkaXNn
+bG9iYWwuY29tMDgGCCsGAQUFBzAChixodHRwOi8vdHJ1c3QucXVvdmFkaXNnbG9i
+YWwuY29tL3F2c3NsaWNhLmNydDBRBgNVHSAESjBIMEYGDCsGAQQBvlgAAmQBAjA2
+MDQGCCsGAQUFBwIBFihodHRwOi8vd3d3LnF1b3ZhZGlzZ2xvYmFsLmNvbS9yZXBv
+c2l0b3J5MIGEBgNVHREEfTB7gg13d3cuc3dpdGNoLmNogglzd2l0Y2guY2iCEXd3
+dy1kYXYuc3dpdGNoLmNoggxjbXMuc21zY2cuY2iCEWNtcy53d3cuc3dpdGNoLmNo
+ggllZHVodWIuY2iCDXd3dy5lZHVodWIuY2iCEWNtcy53d3cuZWR1aHViLmNoMAsG
+A1UdDwQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwHwYDVR0j
+BBgwFoAUMk2hT+rwrpm27psHLIQIEVCL4n4wOwYDVR0fBDQwMjAwoC6gLIYqaHR0
+cDovL2NybC5xdW92YWRpc2dsb2JhbC5jb20vcXZzc2xpY2EuY3JsMB0GA1UdDgQW
+BBRj4EuKFVw3hT+IAecPzu1V+KBfRDANBgkqhkiG9w0BAQUFAAOCAQEAOGUv6vmY
+Bz1d8aewypeEpfGG6HEM59xXEnawhywiT7642y0ZCrAIYQASpKhI4sLPKOJpmQRg
+IzApWKaYvLhUsqvnaEvGS+zj+WGvPps7Ky23mwNmLr4qlMdlW6HuXacZvePAUp9v
+qCzQzcxD2QRncZ1vmG1uz/2gR34b/pgb2HnUS4tT6HbUQxTbQAEEbRubTMjFAD5w
+MXIFvNdOl+fhsehC9xxRnXy0dprXE2Wtk29fqnnXmpTSaOOuzc5BhXamdjebCeY/
+ACI+6A2o7ZbwRLN/J/lnBItJuWam78u0ypLOpWpDImt7eWMP+3JjJcegxVwp80dU
+2TumER72gt2EOA==
+-----END CERTIFICATE-----
+
diff --git a/test/cert1.crt b/test/cert1.crt
new file mode 100644
index 0000000..2e471d4
Binary files /dev/null and b/test/cert1.crt differ
diff --git a/test/cert1.pem b/test/cert1.pem
new file mode 100644
index 0000000..d734bd7
--- /dev/null
+++ b/test/cert1.pem
@@ -0,0 +1,34 @@
+ 1 s:/C=BM/O=QuoVadis Limited/OU=www.quovadisglobal.com/CN=QuoVadis Global SSL ICA
+ i:/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2
+-----BEGIN CERTIFICATE-----
+MIIFTjCCAzagAwIBAgICBXowDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x
+GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv
+b3QgQ0EgMjAeFw0wNzAxMTIxNjEzMzNaFw0xNzAxMTIxNjEzMTFaMGsxCzAJBgNV
+BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR8wHQYDVQQLExZ3d3cu
+cXVvdmFkaXNnbG9iYWwuY29tMSAwHgYDVQQDExdRdW9WYWRpcyBHbG9iYWwgU1NM
+IElDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKk1mD/CiG1+aGcM
+xI7LJL0x4qQpmljkCt1BFL1oaoyuFW4l0GKVTNPFsJ6w4a7pLejG1uQJgeRmKy8n
+xm12NXgIshfqBvTqVFAcuGViwCreo5S+oZWlLxTIYRVJZB3OujED5IyXVibMLR7g
+xWwcXS2BCSNDUnCAN2x+sGHSR9o4sGTbiYFMZPWZfOc0rIbWtms/cUSVfqneyRGN
+WgoIvKPdT2vGvf70RpszxqjEEBLT2A1F2QwM/BxgxylzyelGCN6qVDJrE2rP1KRq
+AN+qiV7kK9MphZ9RYRkjtHE3qNkIxTi4KLy/FBWCy9abwK7t8+AGP6y+N8Oxf7Ed
+9AU37VcCAwEAAaOCASAwggEcMA8GA1UdEwEB/wQFMAMBAf8wOgYIKwYBBQUHAQEE
+LjAsMCoGCCsGAQUFBzABhh5odHRwOi8vb2NzcC5xdW92YWRpc2dsb2JhbC5jb20w
+QgYDVR0gBDswOTA3BgRVHSAAMC8wLQYIKwYBBQUHAgEWIWh0dHA6Ly93d3cucXVv
+dmFkaXNnbG9iYWwuY29tL2NwczAOBgNVHQ8BAf8EBAMCAQYwHwYDVR0jBBgwFoAU
+GoRivEhMMyUE1O7Q9gPEGUbRlGswOQYDVR0fBDIwMDAuoCygKoYoaHR0cDovL2Ny
+bC5xdW92YWRpc2dsb2JhbC5jb20vcXZyY2EyLmNybDAdBgNVHQ4EFgQUMk2hT+rw
+rpm27psHLIQIEVCL4n4wDQYJKoZIhvcNAQEFBQADggIBAI5zWxH+LIAvrc/dYIWZ
+8zHozDuc1kbd7IaiSgjJCZwNo1vMSLbNfgPg7XIoTDJ903URzDUWh4l8/XncwRil
+rRafR23N/iFkM+NF+LoABd9qpF/oAmOGuJ6GwPUf/yhioc8nQ/WXuMVF4/OTdvGF
+0QRsk7rivttpGx2aQhGBwO39ft4cySvXToNsBjH4VWcduEooZDg6plIec8S2zrFA
+dXvxSgz/sV41QHwyUokTxEY1UoXF9aA5VeGLKIkC1NasTyy26bzuOYOKxgqRUXIu
+n6M+CdWiKKJWVi3rBpbnFQWSrsotp4jeQn9zBuovTR0OOijTBWHj9ThxrIG5pb4g
+Nmd03/NZDe5l3ja59+UtBUpfCbdqPCCZSUy7t6PLAoDo5JwQKCEOrmNpwD/207GP
+2WMo77wh5/mvJRJMFfEZ+CwQXk5LPXXU7EJr+7PYpJB67hryxts1I6FJI0AF3ET9
+3YZ4sgEK009h6bdeZbIOvcT4e0v33EAJggFtxU/5xRdtk/PmwxBjSxeg+jBK2xeH
+3TScxc6nNvtcw22Lds5GucMsoxmpblYV1adrowg3twQvSXQZ96jzyT3qfmk09M+e
+bBTqd3GFwZcJNaQigOw8EQHQtjJm9Zco7FtJ+SxEqcQYFJ+M7QZz+0wWCPwlflMo
+7aGlYILpWH4iR3ZhuH/3xMkx
+-----END CERTIFICATE-----
+
diff --git a/test/cert2.crt b/test/cert2.crt
new file mode 100644
index 0000000..6ae0a55
Binary files /dev/null and b/test/cert2.crt differ
diff --git a/test/cert2.pem b/test/cert2.pem
new file mode 100644
index 0000000..c1f00ad
--- /dev/null
+++ b/test/cert2.pem
@@ -0,0 +1,34 @@
+ 2 s:/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2
+ i:/C=BM/O=QuoVadis Limited/OU=Root Certification Authority/CN=QuoVadis Root Certification Authority
+-----BEGIN CERTIFICATE-----
+MIIFQjCCBCqgAwIBAgIEQh/RwTANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJC
+TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0
+aWZpY2F0aW9uIEF1dGhvcml0eTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0
+aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNzA3MTAxNDMyMjFaFw0xNzA3MTAxNDMx
+MDRaMEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRsw
+GQYDVQQDExJRdW9WYWRpcyBSb290IENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4IC
+DwAwggIKAoICAQCaGMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4GtMh6QRr+j
+hiYaHv5+HBg6XJxgFyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp
+3MJGF/hd/aTa/55JWpzmM+Yklvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02
+kfN/+NsRE8Scd3bBrrcCaoF6qUWD4gXmuVbBlDePSHFjIuwXZQeVikvfj8ZaCuWw
+419eaxGrDPmF60Tp+ARz8un+XJiM9XOva7R+zdRcAitMOeGylZUtQofX1bOQQ7ds
+E/He3fbE+Ik/0XX1ksOR1YqI0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt66/3
+FsvbzSUr5R/7mp/iUcw6UwxI5g69ybR2BlLmEROFcmMDBOAENisgGQLodKcftslW
+ZvB1JdxnwQ5hYIizPtGo/KPaHbDRsSNU30R2be1B2MGyIrZTHN81Hdyhdyox5C31
+5eXbyOD/5YDXC2Og/zOhD7osFRXql7PSorW+8oyWHhqPHWykYTe5hnMz15eWniN9
+gqRMgeKh0bpnX5UHoycR7hYQe7xFSkyyBNKr79X9DFHOUGoIMfmR2gyPZFwDwzqL
+ID9ujWc9Otb+fVuIyV77zGHcizN300QyNQliBJIWENieJ0f7OyHj+OsdWwIDAQAB
+o4H/MIH8MA8GA1UdEwEB/wQFMAMBAf8wQgYDVR0gBDswOTA3BgRVHSAAMC8wLQYI
+KwYBBQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2NwczA6Bggr
+BgEFBQcBAQQuMCwwKgYIKwYBBQUHMAGGHmh0dHA6Ly9vY3NwLnF1b3ZhZGlzZ2xv
+YmFsLmNvbTAOBgNVHQ8BAf8EBAMCAQYwHwYDVR0jBBgwFoAUi0tt7dMpuQYZ7Dk5
+qfCXhGrL798wOAYDVR0fBDEwLzAtoCugKYYnaHR0cDovL2NybC5xdW92YWRpc2ds
+b2JhbC5jb20vcXZyY2EuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQBMcgnQQhxa0o7B
+SMNVmyE8suH3nmg3+FC6sAWUsYEKZUZU+PdUrOYGTybAjdPSghSzyhWf/h+l1zwb
+/vxiaZET3ikOni1G1L9rmNPNd0o8Omr/sxTtNyCIEugoJtiBV324XD9wjYr4TjzH
+fn5pq33j+5iqCS0oaynouNevRB/Kcn36esUBg5eEL84cu7JxoOgyPxIccskf5Zp+
+4pqUlQod9cedCi2NaSJ6ZyNExTTtsWXRZM2DYfwMNilHBwPhgj472vQqxN3wb7f6
+ndMU7j2DXbO6G9V891AT1OM6J0JC1DYaA4bMr4m31lJs2sIn99IgrondrOsPSWuu
+TYzbyDZK
+-----END CERTIFICATE-----
+
diff --git a/test/root.crt b/test/root.crt
new file mode 100644
index 0000000..77e783a
Binary files /dev/null and b/test/root.crt differ
diff --git a/test/root.pem b/test/root.pem
new file mode 100644
index 0000000..0050532
--- /dev/null
+++ b/test/root.pem
@@ -0,0 +1,35 @@
+-----BEGIN CERTIFICATE-----
+MIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJC
+TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0
+aWZpY2F0aW9uIEF1dGhvcml0eTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0
+aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMTAzMTkxODMzMzNaFw0yMTAzMTcxODMz
+MzNaMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUw
+IwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQDEyVR
+dW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG
+9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv2G1lVO6V/z68mcLOhrfEYBklbTRvM16z/Yp
+li4kVEAkOPcahdxYTMukJ0KX0J+DisPkBgNbAKVRHnAEdOLB1Dqr1607BxgFjv2D
+rOpm2RgbaIr1VxqYuvXtdj182d6UajtLF8HVj71lODqV0D1VNk7feVcxKh7YWWVJ
+WCCYfqtffp/p1k3sg3Spx2zY7ilKhSoGFPlU5tPaZQeLYzcS19Dsw3sgQUSj7cug
+F+FxZc4dZjH3dgEZyH0DWLaVSR2mEiboxgx24ONmy+pdpibu5cxfvWenAScOospU
+xbF6lR1xHkopigPcakXBpBlebzbNw6Kwt/5cOOJSvPhEQ+aQuwIDAQABo4ICUjCC
+Ak4wPQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwczovL29jc3AucXVv
+dmFkaXNvZmZzaG9yZS5jb20wDwYDVR0TAQH/BAUwAwEB/zCCARoGA1UdIASCAREw
+ggENMIIBCQYJKwYBBAG+WAABMIH7MIHUBggrBgEFBQcCAjCBxxqBxFJlbGlhbmNl
+IG9uIHRoZSBRdW9WYWRpcyBSb290IENlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBh
+c3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFy
+ZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRpb24gcHJh
+Y3RpY2VzLCBhbmQgdGhlIFF1b1ZhZGlzIENlcnRpZmljYXRlIFBvbGljeS4wIgYI
+KwYBBQUHAgEWFmh0dHA6Ly93d3cucXVvdmFkaXMuYm0wHQYDVR0OBBYEFItLbe3T
+KbkGGew5Oanwl4Rqy+/fMIGuBgNVHSMEgaYwgaOAFItLbe3TKbkGGew5Oanwl4Rq
+y+/foYGEpIGBMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1p
+dGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYD
+VQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ6tlCL
+MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAitQUtf70mpKnGdSk
+fnIYj9lofFIk3WdvOXrEql494liwTXCYhGHoG+NpGA7O+0dQoE7/8CQfvbLO9Sf8
+7C9TqnN7Az10buYWnuulLsS/VidQK2K6vkscPFVcQR0kvoIgR13VRH56FmjffU1R
+cHhXHTMe/QKZnAzNCgVPx7uOpHX6Sm2xgI4JVrmcGmD+XcHXetwReNDWXcG31a0y
+mQM6isxUJTkxgXsTIlG6Rmyhu576BGxJJnSP0nPrzDCi5upZIof4l/UO/erMkqQW
+xFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi5nrQNiOK
+SnQ2+Q==
+-----END CERTIFICATE-----
+
diff --git a/vpnc.c b/vpnc.c
index 44cbe65..a1f6bce 100644
--- a/vpnc.c
+++ b/vpnc.c
@@ -40,12 +40,7 @@

#include <gcrypt.h>

-#ifdef OPENSSL_GPL_VIOLATION
-/* OpenSSL */
-#include <openssl/x509.h>
-#include <openssl/err.h>
-#endif /* OPENSSL_GPL_VIOLATION */
-
+#include "crypto.h"
#include "sysdep.h"
#include "config.h"
#include "isakmp-pkt.h"
@@ -1303,13 +1298,12 @@ static void do_phase1_am_packet2(struct sa_block *
DEBUGTOP(2, printf("S4.4 AM_packet2\n"));
/* Decode the recieved packet. */
{
- int reject;
+ int reject, ret;
struct isakmp_packet *r;
struct isakmp_payload *rp;
struct isakmp_payload *nonce = NULL;
struct isakmp_payload *ke = NULL;
struct isakmp_payload *hash = NULL;
- struct isakmp_payload *last_cert = NULL;
struct isakmp_payload *sig = NULL;
struct isakmp_payload *idp = NULL;
int seen_sa = 0, seen_xauth_vid = 0;
@@ -1319,12 +1313,12 @@ static void do_phase1_am_packet2(struct sa_block *
uint8_t *dh_shared_secret;
int seen_natt_vid = 0, seen_natd = 0, seen_natd_them = 0, seen_natd_us = 0;
int natt_draft = -1;
+ crypto_ctx *cctx;
+ crypto_error *crerr = NULL;

-#ifdef OPENSSL_GPL_VIOLATION
- X509 *current_cert;
- /* structure to store the certificate chain */
- STACK_OF(X509) *cert_stack = sk_X509_new_null();
-#endif /* OPENSSL_GPL_VIOLATION */
+ cctx = crypto_ctx_new (&crerr);
+ if (crerr)
+ crypto_call_error(crerr);

reject = 0;
r = parse_isakmp_packet(r_packet, r_length, &reject);
@@ -1482,14 +1476,15 @@ static void do_phase1_am_packet2(struct sa_block *
hash = rp;
break;
case ISAKMP_PAYLOAD_CERT:
- last_cert = rp;
- if (last_cert->u.cert.encoding == ISAKMP_CERT_X509_SIG) {
-#ifdef OPENSSL_GPL_VIOLATION
- /* convert the certificate to an openssl-X509 structure and push it onto the chain stack */
- current_cert = d2i_X509(NULL, (const unsigned char **)&last_cert->u.cert.data, last_cert->u.cert.length);
- sk_X509_push(cert_stack, current_cert);
- last_cert->u.cert.data -= last_cert->u.cert.length; /* 'rewind' the pointer */
-#endif /* OPENSSL_GPL_VIOLATION */
+ if (rp->u.cert.encoding == ISAKMP_CERT_X509_SIG) {
+ hex_dump("cert", rp->u.cert.data, rp->u.cert.length, NULL);
+
+ ret = crypto_push_cert(cctx,
+ (const unsigned char *) rp->u.cert.data,
+ rp->u.cert.length,
+ &crerr);
+ if (ret)
+ crypto_call_error(crerr);
}
break;
case ISAKMP_PAYLOAD_SIG:
@@ -1677,9 +1672,10 @@ static void do_phase1_am_packet2(struct sa_block *
/* Verify the hash. */
{
gcry_md_hd_t hm;
- unsigned char *expected_hash;
+ unsigned char *expected_hash, *rec_hash;
uint8_t *idp_f;
size_t idp_size;
+ size_t decr_size = 0;

flatten_isakmp_payload(idp, &idp_f, &idp_size);

@@ -1703,104 +1699,34 @@ static void do_phase1_am_packet2(struct sa_block *
hex_dump("received hash", hash->u.hash.data, hash->u.hash.length, NULL);
} else if (opt_auth_mode == AUTH_MODE_CERT ||
opt_auth_mode == AUTH_MODE_HYBRID) {
-#ifdef OPENSSL_GPL_VIOLATION
-
- /* BEGIN - check the signature using OpenSSL */
-
- X509 *x509;
- EVP_PKEY *pkey;
- RSA *rsa;
- X509_STORE *store;
- /* X509_LOOKUP *lookup; */
- X509_STORE_CTX *verify_ctx;
- unsigned char *rec_hash;
- int decr_size;
-
hex_dump("received signature", sig->u.sig.data, sig->u.sig.length, NULL);
- OpenSSL_add_all_ciphers();
- OpenSSL_add_all_digests();
- OpenSSL_add_all_algorithms();

- ERR_load_crypto_strings();
+ ret = crypto_verify_chain(cctx,
+ config[CONFIG_CA_FILE],
+ config[CONFIG_CA_DIR],
+ &crerr);
+ if (ret)
+ crypto_call_error(crerr);

- hex_dump("last cert", last_cert->u.cert.data, last_cert->u.cert.length, NULL);
- x509 = d2i_X509(NULL, (const unsigned char **)&last_cert->u.cert.data, last_cert->u.cert.length);
- if (x509 == NULL) {
- ERR_print_errors_fp (stderr);
- error(1, 0, "x509 error\n");
- }
- DEBUG(3, printf("Subject name hash: %08lx\n",X509_subject_name_hash(x509)));
+ /* Verify signature */
+ rec_hash = crypto_decrypt_signature (cctx,
+ sig->u.sig.data,
+ sig->u.sig.length,
+ &decr_size,
+ CRYPTO_PAD_PKCS1,
+ &crerr);
+ if (!rec_hash)
+ crypto_call_error(crerr);

- /* BEGIN - verify certificate chain */
- /* create the cert store */
- if (!(store = X509_STORE_new())) {
- error(1, 0, "Error creating X509_STORE object\n");
- }
- /* load the CA certificates */
- if (X509_STORE_load_locations (store, config[CONFIG_CA_FILE], config[CONFIG_CA_DIR]) != 1) {
- error(1, 0, "Error loading the CA file or directory\n");
- }
- if (X509_STORE_set_default_paths (store) != 1) {
- error(1, 0, "Error loading the system-wide CA certificates\n");
- }
-
-#if 0
- /* check CRLs */
- /* add the corresponding CRL for each CA in the chain to the lookup */
-#define CRL_FILE "root-ca-crl.crl.pem"
-
- if (!(lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()))) {
- error(1, 0, "Error creating X509 lookup object.\n");
- }
- if (X509_load_crl_file(lookup, CRL_FILE, X509_FILETYPE_PEM) != 1) {
- ERR_print_errors_fp(stderr);
- error(1, 0, "Error reading CRL file\n");
- }
- X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
-#endif /* 0 */
- /* create a verification context and initialize it */
- if (!(verify_ctx = X509_STORE_CTX_new ())) {
- error(1, 0, "Error creating X509_STORE_CTX object\n");
- }
- /* X509_STORE_CTX_init did not return an error condition
- in prior versions */
- if (X509_STORE_CTX_init (verify_ctx, store, x509, cert_stack) != 1)
- printf("Error intializing verification context\n");
-
- /* verify the certificate */
- if (X509_verify_cert(verify_ctx) != 1) {
- ERR_print_errors_fp(stderr);
- error(2, 0, "Error verifying the certificate-chain\n");
- } else
- DEBUG(3, printf("Certificate-chain verified correctly!\n"));
-
- /* END - verify certificate chain */
-
-
- /* BEGIN - Signature Verification */
- pkey = X509_get_pubkey(x509);
- if (pkey == NULL) {
- ERR_print_errors_fp (stderr);
- exit (1);
- }
-
- rsa = EVP_PKEY_get1_RSA(pkey);
- if (rsa == NULL) {
- ERR_print_errors_fp (stderr);
- exit (1);
- }
- rec_hash = xallocc(s->ike.md_len);
- decr_size = RSA_public_decrypt(sig->u.sig.length, sig->u.sig.data, rec_hash, rsa, RSA_PKCS1_PADDING);
-
- if (decr_size != (int) s->ike.md_len) {
- printf("Decrypted-Size: %d\n",decr_size);
+ if (decr_size != s->ike.md_len) {
+ printf("Decrypted-Size: %lu\n",decr_size);
hex_dump(" decr_hash", rec_hash, decr_size, NULL);
hex_dump("expected hash", expected_hash, s->ike.md_len, NULL);

error(2, 0, "The hash-value, which was decrypted from the received signature, and the expected hash-value differ in size.\n");
} else {
if (memcmp(rec_hash, expected_hash, decr_size) != 0) {
- printf("Decrypted-Size: %d\n",decr_size);
+ printf("Decrypted-Size: %lu\n",decr_size);
hex_dump(" decr_hash", rec_hash, decr_size, NULL);
hex_dump("expected hash", expected_hash, s->ike.md_len, NULL);

@@ -1811,11 +1737,7 @@ static void do_phase1_am_packet2(struct sa_block *
}
/* END - Signature Verification */

- EVP_PKEY_free(pkey);
free(rec_hash);
-
- /* END - check the signature using OpenSSL */
-#endif /* OPENSSL_GPL_VIOLATION */
}

gcry_md_close(hm);
@@ -1953,6 +1875,7 @@ static void do_phase1_am_packet2(struct sa_block *
}

gcry_md_close(skeyid_ctx);
+ crypto_ctx_free(cctx);
free(dh_shared_secret);

/* Determine presence of NAT */
@@ -2030,18 +1953,7 @@ static void do_phase1_am_packet3(struct sa_block *
uint8_t *p2kt;
size_t p2kt_len;
struct isakmp_payload *pl;
-#if 0 /* cert support */
-#ifdef OPENSSL_GPL_VIOLATION
- struct isakmp_payload *last_cert = NULL;
- struct isakmp_payload *sig = NULL;

-
- X509 *current_cert;
- /* structure to store the certificate chain */
- STACK_OF(X509) *cert_stack = sk_X509_new_null();
-#endif /* OPENSSL_GPL_VIOLATION */
-#endif /* 0 */
-
p2 = new_isakmp_packet();
memcpy(p2->i_cookie, s->ike.i_cookie, ISAKMP_COOKIE_LENGTH);
memcpy(p2->r_cookie, s->ike.r_cookie, ISAKMP_COOKIE_LENGTH);




_______________________________________________
vpnc-devel mailing list
vpnc-devel [at] unix-ag
https://lists.unix-ag.uni-kl.de/mailman/listinfo/vpnc-devel
http://www.unix-ag.uni-kl.de/~massar/vpnc/


martin.dummer at gmx

May 27, 2009, 10:48 PM

Post #2 of 6 (1244 views)
Permalink
Re: [PATCH] modularize certificate code + gnutls support [In reply to]

Hi Dan,


On Tuesday 26 May 2009 21:37:13 Dan Williams wrote:
> I haven't been able to test against a real hybrid-auth enabled VPN
> concentrator, but the testcases are built so that I have a reasonable
> confidence that it will work. I have verified that this patch does not
> regress connections to both Cisco PIX and Juniper Netscreen boxes in the
> Group Auth IKE mode (ie, non-hybrid mode).
>

I appreciate your work and will test against a cisco concentrator *WITH* Group Auth IKE mode.

> Patch against current trunk, and if people want, I have a version
> against 0.5.3 as well.

Could you please post the 0.5.3 patch also? I would prefer testing it with the stable version.


Greets
Martin


dcbw at redhat

May 28, 2009, 11:03 AM

Post #3 of 6 (1235 views)
Permalink
Re: [PATCH] modularize certificate code + gnutls support [In reply to]

On Thu, 2009-05-28 at 07:48 +0200, Martin Dummer wrote:
> Hi Dan,
>
>
>
>
> On Tuesday 26 May 2009 21:37:13 Dan Williams wrote:
> > I haven't been able to test against a real hybrid-auth enabled VPN
> > concentrator, but the testcases are built so that I have a
> reasonable
> > confidence that it will work. I have verified that this patch does
> not
> > regress connections to both Cisco PIX and Juniper Netscreen boxes in
> the
> > Group Auth IKE mode (ie, non-hybrid mode).
> >
>
>
>
> I appreciate your work and will test against a cisco concentrator
> *WITH* Group Auth IKE mode.
>
>
>
> > Patch against current trunk, and if people want, I have a version
> > against 0.5.3 as well.
>
>
>
> Could you please post the 0.5.3 patch also? I would prefer testing it
> with the stable version.

Attached.

Dan
Attachments: vpnc-gnutls-2.patch (60.5 KB)


stellarspace69 at yahoo

Jun 3, 2009, 10:27 PM

Post #4 of 6 (1230 views)
Permalink
Re: [PATCH] modularize certificate code + gnutls support [In reply to]

Is it possible to port this patch to work under cygwin?

--- On Thu, 5/28/09, Dan Williams <dcbw [at] redhat> wrote:

From: Dan Williams <dcbw [at] redhat>
Subject: Re: [vpnc-devel] [PATCH] modularize certificate code + gnutls support
To: "Martin Dummer" <martin.dummer [at] gmx>
Cc: vpnc-devel [at] unix-ag
Date: Thursday, May 28, 2009, 11:03 AM

On Thu, 2009-05-28 at 07:48 +0200, Martin Dummer wrote:
> Hi Dan,
>
>
>
>
> On Tuesday 26 May 2009 21:37:13 Dan Williams wrote:
> > I haven't been able to test against a real hybrid-auth enabled VPN
> > concentrator, but the testcases are built so that I have a
> reasonable
> > confidence that it will work.  I have verified that this patch does
> not
> > regress connections to both Cisco PIX and Juniper Netscreen boxes in
> the
> > Group Auth IKE mode (ie, non-hybrid mode).
> >
>
>
>
> I appreciate your work and will test against a cisco concentrator
> *WITH* Group Auth IKE mode.
>
>
>
> > Patch against current trunk, and if people want, I have a version
> > against 0.5.3 as well.
>
>
>
> Could you please post the 0.5.3 patch also? I would prefer testing it
> with the stable version.

Attached.

Dan


-----Inline Attachment Follows-----

_______________________________________________
vpnc-devel mailing list
vpnc-devel [at] unix-ag
https://lists.unix-ag.uni-kl.de/mailman/listinfo/vpnc-devel
http://www.unix-ag.uni-kl.de/~massar/vpnc/


dcbw at redhat

Jun 4, 2009, 1:05 PM

Post #5 of 6 (1218 views)
Permalink
Re: [PATCH] modularize certificate code + gnutls support [In reply to]

I can't think of any reason why it wouldn't work; it shouldn't be doing
anything Linux specific. The gnutls code doesn't actually work quite
yet, but I expect that'll be pretty easy to fix.

Dan

On Wed, 2009-06-03 at 22:27 -0700, Joshua Bahnsen wrote:
> Is it possible to port this patch to work under cygwin?
>
> --- On Thu, 5/28/09, Dan Williams <dcbw [at] redhat> wrote:
>
> From: Dan Williams <dcbw [at] redhat>
> Subject: Re: [vpnc-devel] [PATCH] modularize certificate code
> + gnutls support
> To: "Martin Dummer" <martin.dummer [at] gmx>
> Cc: vpnc-devel [at] unix-ag
> Date: Thursday, May 28, 2009, 11:03 AM
>
> On Thu, 2009-05-28 at 07:48 +0200, Martin Dummer wrote:
> > Hi Dan,
> >
> >
> >
> >
> > On Tuesday 26 May 2009 21:37:13 Dan Williams wrote:
> > > I haven't been able to test against a real hybrid-auth
> enabled VPN
> > > concentrator, but the testcases are built so that I have a
> > reasonable
> > > confidence that it will work. I have verified that this
> patch does
> > not
> > > regress connections to both Cisco PIX and Juniper
> Netscreen boxes in
> > the
> > > Group Auth IKE mode (ie, non-hybrid mode).
> > >
> >
> >
> >
> > I appreciate your work and will test against a cisco
> concentrator
> > *WITH* Group Auth IKE mode.
> >
> >
> >
> > > Patch against current trunk, and if people want, I have a
> version
> > > against 0.5.3 as well.
> >
> >
> >
> > Could you please post the 0.5.3 patch also? I would prefer
> testing it
> > with the stable version.
>
> Attached.
>
> Dan
>
>
>
> -----Inline Attachment Follows-----
>
> _______________________________________________
> vpnc-devel mailing list
> vpnc-devel [at] unix-ag
> https://lists.unix-ag.uni-kl.de/mailman/listinfo/vpnc-devel
> http://www.unix-ag.uni-kl.de/~massar/vpnc/
>
> _______________________________________________
> vpnc-devel mailing list
> vpnc-devel [at] unix-ag
> https://lists.unix-ag.uni-kl.de/mailman/listinfo/vpnc-devel
> http://www.unix-ag.uni-kl.de/~massar/vpnc/

_______________________________________________
vpnc-devel mailing list
vpnc-devel [at] unix-ag
https://lists.unix-ag.uni-kl.de/mailman/listinfo/vpnc-devel
http://www.unix-ag.uni-kl.de/~massar/vpnc/


stellarspace69 at yahoo

Jun 4, 2009, 3:40 PM

Post #6 of 6 (1211 views)
Permalink
Re: [PATCH] modularize certificate code + gnutls support [In reply to]

Well crypto.c includes error.h, this file is not provided in a default cygwin install. Is there another package that would provide this?

Everything I have read mentions gnulib for portability reasons on cygwin but that is usually applied at a project source level and isn't available as an individual release. I'm all for not using OpenSSL, in fact I don't have the capability of testing it either, but I'm willing to help where I can.

--- On Thu, 6/4/09, Dan Williams <dcbw [at] redhat> wrote:

From: Dan Williams <dcbw [at] redhat>
Subject: Re: [vpnc-devel] [PATCH] modularize certificate code + gnutls support
To: "vpnc list to send bug reports and discussions with developers" <vpnc-devel [at] unix-ag>
Date: Thursday, June 4, 2009, 1:05 PM

I can't think of any reason why it wouldn't work; it shouldn't be doing
anything Linux specific.  The gnutls code doesn't actually work quite
yet, but I expect that'll be pretty easy to fix.

Dan

On Wed, 2009-06-03 at 22:27 -0700, Joshua Bahnsen wrote:
> Is it possible to port this patch to work under cygwin?
>
> --- On Thu, 5/28/09, Dan Williams <dcbw [at] redhat> wrote:
>         
>         From: Dan Williams <dcbw [at] redhat>
>         Subject: Re: [vpnc-devel] [PATCH] modularize certificate code
>         + gnutls support
>         To: "Martin Dummer" <martin.dummer [at] gmx>
>         Cc: vpnc-devel [at] unix-ag
>         Date: Thursday, May 28, 2009, 11:03 AM
>         
>         On Thu, 2009-05-28 at 07:48 +0200, Martin Dummer wrote:
>         > Hi Dan,
>         >
>         >
>         >
>         >
>         > On Tuesday 26 May 2009 21:37:13 Dan Williams wrote:
>         > > I haven't been able to test against a real hybrid-auth
>         enabled VPN
>         > > concentrator, but the testcases are built so that I have a
>         > reasonable
>         > > confidence that it will work.  I have verified that this
>         patch does
>         > not
>         > > regress connections to both Cisco PIX and Juniper
>         Netscreen boxes in
>         > the
>         > > Group Auth IKE mode (ie, non-hybrid mode).
>         > >
>         >
>         >
>         >
>         > I appreciate your work and will test against a cisco
>         concentrator
>         > *WITH* Group Auth IKE mode.
>         >
>         >
>         >
>         > > Patch against current trunk, and if people want, I have a
>         version
>         > > against 0.5.3 as well.
>         >
>         >
>         >
>         > Could you please post the 0.5.3 patch also? I would prefer
>         testing it
>         > with the stable version.
>         
>         Attached.
>         
>         Dan
>         
>         
>         
>         -----Inline Attachment Follows-----
>         
>         _______________________________________________
>         vpnc-devel mailing list
>         vpnc-devel [at] unix-ag
>         https://lists.unix-ag.uni-kl.de/mailman/listinfo/vpnc-devel
>         http://www.unix-ag.uni-kl.de/~massar/vpnc/
>         
> _______________________________________________
> vpnc-devel mailing list
> vpnc-devel [at] unix-ag
> https://lists.unix-ag.uni-kl.de/mailman/listinfo/vpnc-devel
> http://www.unix-ag.uni-kl.de/~massar/vpnc/

_______________________________________________
vpnc-devel mailing list
vpnc-devel [at] unix-ag
https://lists.unix-ag.uni-kl.de/mailman/listinfo/vpnc-devel
http://www.unix-ag.uni-kl.de/~massar/vpnc/

vpnc devel RSS feed   Index | Next | Previous | View Threaded
 
 


Interested in having your list archived? Contact Gossamer Threads
 
  Web Applications & Managed Hosting Powered by Gossamer Threads Inc.