From: Guus Sliepen Date: Wed, 23 May 2007 13:45:49 +0000 (+0000) Subject: Finish crypto wrapping. Also provide wrappers for OpenSSL. X-Git-Tag: import-tinc-1.1~631 X-Git-Url: http://git.meshlink.io/?p=meshlink;a=commitdiff_plain;h=1b8f8918360b40a2749d40355266ed7dedbe41b5 Finish crypto wrapping. Also provide wrappers for OpenSSL. Disable libgcrypt by default. Since it doesn't support the OFB cipher mode, we can't use it in a backwards compatible way. --- diff --git a/configure.in b/configure.in index cbf6bf23..f6a163b7 100644 --- a/configure.in +++ b/configure.in @@ -145,11 +145,21 @@ AC_CACHE_SAVE dnl These are defined in files in m4/ -AM_PATH_LIBGCRYPT([], [], [AC_MSG_ERROR([Libgcrypt not found.]); break]) +AC_ARG_WITH(libgcrypt, AC_HELP_STRING([--with-libgcrypt], [enable use of libgcrypt instead of OpenSSL])], []) + +AM_PATH_LIBGCRYPT([], [], []) tinc_OPENSSL tinc_ZLIB tinc_LZO +if test "$with_libgcrypt" = yes; then + AC_MSG_ERROR([Libgcrypt support not fully implemented yet.]); + break; +else + ln -sf openssl/crypto.c openssl/crypto.h openssl/cipher.c openssl/cipher.h openssl/digest.c openssl/digest.h openssl/rsa.c openssl/rsa.h src/ +fi + + dnl Check if support for jumbograms is requested AC_ARG_ENABLE(jumbograms, AS_HELP_STRING([--enable-jumbograms], [enable support for jumbograms (packets up to 9000 bytes)]), diff --git a/src/Makefile.am b/src/Makefile.am index 5259ff7a..a318dcd5 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -5,7 +5,7 @@ sbin_PROGRAMS = tincd tincctl EXTRA_DIST = linux/device.c bsd/device.c solaris/device.c cygwin/device.c mingw/device.c mingw/common.h raw_socket/device.c uml_socket/device.c -tincd_SOURCES = conf.c connection.c control.c edge.c graph.c logger.c meta.c net.c net_packet.c net_setup.c \ +tincd_SOURCES = cipher.c conf.c connection.c control.c crypto.c digest.c edge.c graph.c logger.c meta.c net.c net_packet.c net_setup.c \ net_socket.c netutl.c node.c process.c protocol.c protocol_auth.c protocol_edge.c protocol_misc.c \ protocol_key.c protocol_subnet.c route.c rsa.c subnet.c tincd.c @@ -17,14 +17,13 @@ DEFAULT_INCLUDES = INCLUDES = @INCLUDES@ -I$(top_builddir) -I$(top_srcdir)/lib -noinst_HEADERS = conf.h connection.h control.h device.h edge.h graph.h logger.h meta.h net.h netutl.h node.h process.h \ +noinst_HEADERS = cipher.h conf.h connection.h control.h crypto.h device.h digest.h edge.h graph.h logger.h meta.h net.h netutl.h node.h process.h \ protocol.h route.h rsa.h subnet.h LIBS = @LIBS@ @LIBINTL@ tincd_LDADD = \ - $(top_builddir)/lib/libvpn.a \ - $(LIBGCRYPT_LIBS) + $(top_builddir)/lib/libvpn.a tincctl_LDADD = \ $(top_builddir)/lib/libvpn.a @@ -32,7 +31,6 @@ tincctl_LDADD = \ localedir = $(datadir)/locale AM_CFLAGS = @CFLAGS@ -DCONFDIR=\"$(sysconfdir)\" -DLOCALEDIR=\"$(localedir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -tinc_CPPFLAGS = $(LIBGCRYPT_CFLAGS) dist-hook: rm -f `find . -type l` diff --git a/src/cipher.c b/src/cipher.c deleted file mode 100644 index 61179078..00000000 --- a/src/cipher.c +++ /dev/null @@ -1,253 +0,0 @@ -/* - cipher.c -- Symmetric block cipher handling - Copyright (C) 2007 Guus Sliepen - - 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., 675 Mass Ave, Cambridge, MA 02139, USA. - - $Id$ -*/ - -#include "system.h" - -#include "cipher.h" -#include "logger.h" -#include "xalloc.h" - -static struct { - const char *name; - int algo; - int mode; - int nid; -} ciphertable[] = { - {"none", GCRY_CIPHER_NONE, GCRY_CIPHER_MODE_NONE, 0}, - - {NULL, GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_ECB, 92}, - {"blowfish", GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_CBC, 91}, - {NULL, GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_CFB, 93}, - {NULL, GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_OFB, 94}, - - {NULL, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_ECB, 418}, - {"aes", GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CBC, 419}, - {NULL, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CFB, 421}, - {NULL, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_OFB, 420}, - - {NULL, GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_ECB, 422}, - {"aes192", GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_CBC, 423}, - {NULL, GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_CFB, 425}, - {NULL, GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_OFB, 424}, - - {NULL, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_ECB, 426}, - {"aes256", GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 427}, - {NULL, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CFB, 429}, - {NULL, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_OFB, 428}, -}; - -static bool nametocipher(const char *name, int *algo, int *mode) { - int i; - - for(i = 0; i < sizeof ciphertable / sizeof *ciphertable; i++) { - if(ciphertable[i].name && !strcasecmp(name, ciphertable[i].name)) { - *algo = ciphertable[i].algo; - *mode = ciphertable[i].mode; - return true; - } - } - - return false; -} - -static bool nidtocipher(int nid, int *algo, int *mode) { - int i; - - for(i = 0; i < sizeof ciphertable / sizeof *ciphertable; i++) { - if(nid == ciphertable[i].nid) { - *algo = ciphertable[i].algo; - *mode = ciphertable[i].mode; - return true; - } - } - - return false; -} - -static bool ciphertonid(int algo, int mode, int *nid) { - int i; - - for(i = 0; i < sizeof ciphertable / sizeof *ciphertable; i++) { - if(algo == ciphertable[i].algo && mode == ciphertable[i].mode) { - *nid = ciphertable[i].nid; - return true; - } - } - - return false; -} - -static bool cipher_open(cipher_t *cipher, int algo, int mode) { - gcry_error_t err; - - if(!ciphertonid(algo, mode, &cipher->nid)) { - logger(LOG_DEBUG, _("Cipher %d mode %d has no corresponding nid!"), algo, mode); - return false; - } - - if((err = gcry_cipher_open(&cipher->handle, algo, mode, 0))) { - logger(LOG_DEBUG, _("Unable to intialise cipher %d mode %d!"), algo, mode); - return false; - } - - cipher->keylen = gcry_cipher_get_algo_keylen(algo); - if(mode == GCRY_CIPHER_MODE_ECB || mode == GCRY_CIPHER_MODE_CBC) - cipher->blklen = gcry_cipher_get_algo_blklen(algo); - else - cipher->blklen = 0; - cipher->key = xmalloc(cipher->keylen + cipher->blklen); - - return true; -} - -bool cipher_open_by_name(cipher_t *cipher, const char *name) { - int algo, mode; - - if(!nametocipher(name, &algo, &mode)) { - logger(LOG_DEBUG, _("Unknown cipher name '%s'!"), name); - return false; - } - - return cipher_open(cipher, algo, mode); -} - -bool cipher_open_by_nid(cipher_t *cipher, int nid) { - int algo, mode; - - if(!nidtocipher(nid, &algo, &mode)) { - logger(LOG_DEBUG, _("Unknown cipher ID %d!"), nid); - return false; - } - - return cipher_open(cipher, algo, mode); -} - -bool cipher_open_blowfish_ofb(cipher_t *cipher) { - return cipher_open(cipher, GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_OFB); -} - -void cipher_close(cipher_t *cipher) { - if(cipher->handle) { - gcry_cipher_close(cipher->handle); - cipher->handle = NULL; - } - - if(cipher->key) { - free(cipher->key); - cipher->key = NULL; - } -} - -size_t cipher_keylength(const cipher_t *cipher) { - return cipher->keylen + cipher->blklen; -} - -void cipher_get_key(const cipher_t *cipher, void *key) { - memcpy(key, cipher->key, cipher->keylen + cipher->blklen); -} - -bool cipher_set_key(cipher_t *cipher, void *key) { - memcpy(cipher->key, key, cipher->keylen + cipher->blklen); - - gcry_cipher_setkey(cipher->handle, cipher->key, cipher->keylen); - gcry_cipher_setiv(cipher->handle, cipher->key + cipher->keylen, cipher->blklen); - - return true; -} - -bool cipher_regenerate_key(cipher_t *cipher) { - gcry_create_nonce(cipher->key, cipher->keylen + cipher->blklen); - - gcry_cipher_setkey(cipher->handle, cipher->key, cipher->keylen); - gcry_cipher_setiv(cipher->handle, cipher->key + cipher->keylen, cipher->blklen); - - return true; -} - -bool cipher_add_padding(cipher_t *cipher, void *indata, size_t inlen, size_t *outlen) { - size_t reqlen; - - if(cipher->blklen == 1) { - *outlen = inlen; - return true; - } - - reqlen = ((inlen + 1) / cipher->blklen) * cipher->blklen; - if(reqlen > *outlen) - return false; - - // add padding - - *outlen = reqlen; - return true; -} - -bool cipher_remove_padding(cipher_t *cipher, void *indata, size_t inlen, size_t *outlen) { - size_t origlen; - - if(cipher->blklen == 1) { - *outlen = inlen; - return true; - } - - if(inlen % cipher->blklen) - return false; - - // check and remove padding - - *outlen = origlen; - return true; -} - -bool cipher_encrypt(cipher_t *cipher, void *indata, size_t inlen, void *outdata, size_t *outlen) { - gcry_error_t err; - - if((err = gcry_cipher_encrypt(cipher->handle, outdata, inlen, indata, inlen))) { - logger(LOG_ERR, _("Error while encrypting")); - return false; - } - - return true; -} - -bool cipher_decrypt(cipher_t *cipher, void *indata, size_t inlen, void *outdata, size_t *outlen) { - gcry_error_t err; - - if((err = gcry_cipher_decrypt(cipher->handle, outdata, inlen, indata, inlen))) { - logger(LOG_ERR, _("Error while encrypting")); - return false; - } - - return true; -} - -void cipher_reset(cipher_t *cipher) { - gcry_cipher_reset(cipher->handle); - gcry_cipher_setiv(cipher->handle, cipher->key + cipher->keylen, cipher->blklen); -} - -int cipher_get_nid(const cipher_t *cipher) { - return cipher->nid; -} - -bool cipher_active(const cipher_t *cipher) { - return cipher->nid != 0; -} diff --git a/src/cipher.h b/src/cipher.h deleted file mode 100644 index 45a4fc5a..00000000 --- a/src/cipher.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - cipher.h -- header file cipher.c - Copyright (C) 2007 Guus Sliepen - - 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., 675 Mass Ave, Cambridge, MA 02139, USA. - - $Id$ -*/ - -#ifndef __TINC_CIPHER_H__ -#define __TINC_CIPHER_H__ - -#include - -typedef struct cipher { - gcry_cipher_hd_t handle; - char *key; - int nid; - uint16_t keylen; - uint16_t blklen; -} cipher_t; - -extern bool cipher_open_by_name(struct cipher *, const char *); -extern bool cipher_open_by_nid(struct cipher *, int); -extern bool cipher_open_blowfish_ofb(struct cipher *); -extern void cipher_close(struct cipher *); -extern size_t cipher_keylength(const struct cipher *); -extern void cipher_get_key(const struct cipher *, void *); -extern bool cipher_set_key(struct cipher *, void *); -extern bool cipher_regenerate_key(struct cipher *); -extern void cipher_reset(struct cipher *); -extern bool cipher_encrypt(struct cipher *, void *indata, size_t inlen, void *outdata, size_t *outlen); -extern bool cipher_decrypt(struct cipher *, void *indata, size_t inlen, void *outdata, size_t *outlen); -extern int cipher_get_nid(const struct cipher *); -extern bool cipher_active(const struct cipher *); - -#endif diff --git a/src/connection.c b/src/connection.c index 21cb6aa9..a369cb83 100644 --- a/src/connection.c +++ b/src/connection.c @@ -23,6 +23,7 @@ #include "system.h" #include "splay_tree.h" +#include "cipher.h" #include "conf.h" #include "list.h" #include "logger.h" @@ -73,14 +74,8 @@ void free_connection(connection_t *c) { if(c->hostname) free(c->hostname); - if(c->inkey) - free(c->inkey); - - if(c->outkey) - free(c->outkey); - - if(c->mychallenge) - free(c->mychallenge); + cipher_close(&c->incipher); + cipher_close(&c->outcipher); if(c->hischallenge) free(c->hischallenge); diff --git a/src/connection.h b/src/connection.h index e5cd6895..54caa261 100644 --- a/src/connection.h +++ b/src/connection.h @@ -23,11 +23,10 @@ #ifndef __TINC_CONNECTION_H__ #define __TINC_CONNECTION_H__ -//#include -#include - #include +#include "cipher.h" +#include "digest.h" #include "rsa.h" #include "splay_tree.h" @@ -73,24 +72,18 @@ typedef struct connection_t { struct node_t *node; /* node associated with the other end */ struct edge_t *edge; /* edge associated with this connection */ - //RSA *rsa_key; /* his public/private key */ - struct rsa_key_t rsa_key; /* his public/private key */ - const EVP_CIPHER *incipher; /* Cipher he will use to send data to us */ - const EVP_CIPHER *outcipher; /* Cipher we will use to send data to him */ - EVP_CIPHER_CTX *inctx; /* Context of encrypted meta data that will come from him to us */ - EVP_CIPHER_CTX *outctx; /* Context of encrypted meta data that will be sent from us to him */ - char *inkey; /* His symmetric meta key + iv */ - char *outkey; /* Our symmetric meta key + iv */ - int inkeylength; /* Length of his key + iv */ - int outkeylength; /* Length of our key + iv */ - const EVP_MD *indigest; - const EVP_MD *outdigest; + rsa_t rsa; /* his public/private key */ + cipher_t incipher; /* Cipher he will use to send data to us */ + cipher_t outcipher; /* Cipher we will use to send data to him */ + digest_t indigest; + digest_t outdigest; + int inmaclength; int outmaclength; int incompression; int outcompression; - char *mychallenge; /* challenge we received from him */ - char *hischallenge; /* challenge we sent to him */ + + char *hischallenge; /* The challenge we sent to him */ struct bufferevent *buffer; /* buffer events on this metadata connection */ struct event inevent; /* input event on this metadata connection */ diff --git a/src/digest.c b/src/digest.c deleted file mode 100644 index 50b0f238..00000000 --- a/src/digest.c +++ /dev/null @@ -1,140 +0,0 @@ -/* - digest.c -- Digest handling - Copyright (C) 2007 Guus Sliepen - - 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., 675 Mass Ave, Cambridge, MA 02139, USA. - - $Id$ -*/ - -#include "system.h" - -#include "digest.h" -#include "logger.h" - -static struct { - const char *name; - int algo; - int nid; -} digesttable[] = { - {"none", GCRY_MD_NONE, 0}, - {"sha1", GCRY_MD_SHA1, 64}, - {"sha256", GCRY_MD_SHA256, 672}, - {"sha384", GCRY_MD_SHA384, 673}, - {"sha512", GCRY_MD_SHA512, 674}, -}; - -static bool nametodigest(const char *name, int *algo) { - int i; - - for(i = 0; i < sizeof digesttable / sizeof *digesttable; i++) { - if(digesttable[i].name && !strcasecmp(name, digesttable[i].name)) { - *algo = digesttable[i].algo; - return true; - } - } - - return false; -} - -static bool nidtodigest(int nid, int *algo) { - int i; - - for(i = 0; i < sizeof digesttable / sizeof *digesttable; i++) { - if(nid == digesttable[i].nid) { - *algo = digesttable[i].algo; - return true; - } - } - - return false; -} - -static bool digesttonid(int algo, int *nid) { - int i; - - for(i = 0; i < sizeof digesttable / sizeof *digesttable; i++) { - if(algo == digesttable[i].algo) { - *nid = digesttable[i].nid; - return true; - } - } - - return false; -} - -static bool digest_open(digest_t *digest, int algo) { - if(!digesttonid(algo, &digest->nid)) { - logger(LOG_DEBUG, _("Digest %d has no corresponding nid!"), algo); - return false; - } - - digest->len = gcry_md_get_algo_dlen(algo); - - return true; -} - -bool digest_open_by_name(digest_t *digest, const char *name) { - int algo; - - if(!nametodigest(name, &algo)) { - logger(LOG_DEBUG, _("Unknown digest name '%s'!"), name); - return false; - } - - return digest_open(digest, algo); -} - -bool digest_open_by_nid(digest_t *digest, int nid) { - int algo; - - if(!nidtodigest(nid, &algo)) { - logger(LOG_DEBUG, _("Unknown digest ID %d!"), nid); - return false; - } - - return digest_open(digest, algo); -} - -bool digest_open_sha1(digest_t *digest) { - return digest_open(digest, GCRY_MD_SHA1); -} - -void digest_close(digest_t *digest) { -} - -bool digest_create(digest_t *digest, void *indata, size_t inlen, void *outdata) { - gcry_md_hash_buffer(digest->algo, outdata, indata, inlen); - return true; -} - -bool digest_verify(digest_t *digest, void *indata, size_t inlen, void *cmpdata) { - char outdata[digest->len]; - - gcry_md_hash_buffer(digest->algo, outdata, indata, inlen); - return !memcmp(indata, outdata, digest->len); -} - -int digest_get_nid(const digest_t *digest) { - return digest->nid; -} - -size_t digest_length(const digest_t *digest) { - return digest->len; -} - -bool digest_active(const digest_t *digest) { - return digest->algo != GCRY_MD_NONE; -} diff --git a/src/digest.h b/src/digest.h deleted file mode 100644 index dd9029e7..00000000 --- a/src/digest.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - digest.h -- header file digest.c - Copyright (C) 2007 Guus Sliepen - - 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., 675 Mass Ave, Cambridge, MA 02139, USA. - - $Id$ -*/ - -#ifndef __TINC_DIGEST_H__ -#define __TINC_DIGEST_H__ - -#include - -typedef struct digest { - int algo; - int nid; - uint16_t len; -} digest_t; - -bool digest_open_by_name(struct digest *, const char *); -bool digest_open_by_nid(struct digest *, int); -bool digest_open_sha1(struct digest *); -void digest_close(struct digest *); -bool digest_create(struct digest *, void *indata, size_t inlen, void *outdata); -bool digest_verify(struct digest *, void *indata, size_t inlen, void *digestdata); -int digest_get_nid(const struct digest *); -size_t digest_length(const struct digest *); -bool digest_active(const struct digest *); - -#endif diff --git a/src/gcrypt/cipher.c b/src/gcrypt/cipher.c new file mode 100644 index 00000000..e1f1e057 --- /dev/null +++ b/src/gcrypt/cipher.c @@ -0,0 +1,262 @@ +/* + cipher.c -- Symmetric block cipher handling + Copyright (C) 2007 Guus Sliepen + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id$ +*/ + +#include "system.h" + +#include "cipher.h" +#include "logger.h" +#include "xalloc.h" + +static struct { + const char *name; + int algo; + int mode; + int nid; +} ciphertable[] = { + {"none", GCRY_CIPHER_NONE, GCRY_CIPHER_MODE_NONE, 0}, + + {NULL, GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_ECB, 92}, + {"blowfish", GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_CBC, 91}, + {NULL, GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_CFB, 93}, + {NULL, GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_OFB, 94}, + + {NULL, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_ECB, 418}, + {"aes", GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CBC, 419}, + {NULL, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CFB, 421}, + {NULL, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_OFB, 420}, + + {NULL, GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_ECB, 422}, + {"aes192", GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_CBC, 423}, + {NULL, GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_CFB, 425}, + {NULL, GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_OFB, 424}, + + {NULL, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_ECB, 426}, + {"aes256", GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 427}, + {NULL, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CFB, 429}, + {NULL, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_OFB, 428}, +}; + +static bool nametocipher(const char *name, int *algo, int *mode) { + int i; + + for(i = 0; i < sizeof ciphertable / sizeof *ciphertable; i++) { + if(ciphertable[i].name && !strcasecmp(name, ciphertable[i].name)) { + *algo = ciphertable[i].algo; + *mode = ciphertable[i].mode; + return true; + } + } + + return false; +} + +static bool nidtocipher(int nid, int *algo, int *mode) { + int i; + + for(i = 0; i < sizeof ciphertable / sizeof *ciphertable; i++) { + if(nid == ciphertable[i].nid) { + *algo = ciphertable[i].algo; + *mode = ciphertable[i].mode; + return true; + } + } + + return false; +} + +static bool ciphertonid(int algo, int mode, int *nid) { + int i; + + for(i = 0; i < sizeof ciphertable / sizeof *ciphertable; i++) { + if(algo == ciphertable[i].algo && mode == ciphertable[i].mode) { + *nid = ciphertable[i].nid; + return true; + } + } + + return false; +} + +static bool cipher_open(cipher_t *cipher, int algo, int mode) { + gcry_error_t err; + + if(!ciphertonid(algo, mode, &cipher->nid)) { + logger(LOG_DEBUG, _("Cipher %d mode %d has no corresponding nid!"), algo, mode); + return false; + } + + if((err = gcry_cipher_open(&cipher->handle, algo, mode, 0))) { + logger(LOG_DEBUG, _("Unable to intialise cipher %d mode %d: %s"), algo, mode, gcry_strerror(err)); + return false; + } + + cipher->keylen = gcry_cipher_get_algo_keylen(algo); + if(mode == GCRY_CIPHER_MODE_ECB || mode == GCRY_CIPHER_MODE_CBC) + cipher->blklen = gcry_cipher_get_algo_blklen(algo); + else + cipher->blklen = 0; + cipher->key = xmalloc(cipher->keylen + cipher->blklen); + + return true; +} + +bool cipher_open_by_name(cipher_t *cipher, const char *name) { + int algo, mode; + + if(!nametocipher(name, &algo, &mode)) { + logger(LOG_DEBUG, _("Unknown cipher name '%s'!"), name); + return false; + } + + return cipher_open(cipher, algo, mode); +} + +bool cipher_open_by_nid(cipher_t *cipher, int nid) { + int algo, mode; + + if(!nidtocipher(nid, &algo, &mode)) { + logger(LOG_DEBUG, _("Unknown cipher ID %d!"), nid); + return false; + } + + return cipher_open(cipher, algo, mode); +} + +bool cipher_open_blowfish_ofb(cipher_t *cipher) { + return cipher_open(cipher, GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_OFB); +} + +void cipher_close(cipher_t *cipher) { + if(cipher->handle) { + gcry_cipher_close(cipher->handle); + cipher->handle = NULL; + } + + if(cipher->key) { + free(cipher->key); + cipher->key = NULL; + } +} + +size_t cipher_keylength(const cipher_t *cipher) { + return cipher->keylen + cipher->blklen; +} + +void cipher_get_key(const cipher_t *cipher, void *key) { + memcpy(key, cipher->key, cipher->keylen + cipher->blklen); +} + +bool cipher_set_key(cipher_t *cipher, void *key, bool encrypt) { + memcpy(cipher->key, key, cipher->keylen + cipher->blklen); + + gcry_cipher_setkey(cipher->handle, cipher->key, cipher->keylen); + gcry_cipher_setiv(cipher->handle, cipher->key + cipher->keylen, cipher->blklen); + + return true; +} + +bool cipher_set_key(cipher_t *cipher, void *key, size_t len, bool encrypt) { + memcpy(cipher->key, key + len - cipher->keylen, cipher->keylen + cipher->blklen); + memcpy(cipher->key + cipher->keylen, key + len - cipher->keylen - cipher->blklen, cipher->blklen); + + gcry_cipher_setkey(cipher->handle, cipher->key, cipher->keylen); + gcry_cipher_setiv(cipher->handle, cipher->key + cipher->keylen, cipher->blklen); + + return true; +} + +bool cipher_regenerate_key(cipher_t *cipher, bool encrypt) { + gcry_create_nonce(cipher->key, cipher->keylen + cipher->blklen); + + gcry_cipher_setkey(cipher->handle, cipher->key, cipher->keylen); + gcry_cipher_setiv(cipher->handle, cipher->key + cipher->keylen, cipher->blklen); + + return true; +} + +static bool cipher_add_padding(cipher_t *cipher, void *indata, size_t inlen, size_t *outlen) { + size_t reqlen; + + if(cipher->blklen == 1) { + *outlen = inlen; + return true; + } + + reqlen = ((inlen + 1) / cipher->blklen) * cipher->blklen; + if(reqlen > *outlen) + return false; + + // add padding + + *outlen = reqlen; + return true; +} + +static bool cipher_remove_padding(cipher_t *cipher, void *indata, size_t inlen, size_t *outlen) { + size_t origlen; + + if(cipher->blklen == 1) { + *outlen = inlen; + return true; + } + + if(inlen % cipher->blklen) + return false; + + // check and remove padding + + *outlen = origlen; + return true; +} + +bool cipher_encrypt(cipher_t *cipher, void *indata, size_t inlen, void *outdata, size_t *outlen, bool oneshot) { + gcry_error_t err; + + // To be fixed + + if((err = gcry_cipher_encrypt(cipher->handle, outdata, inlen, indata, inlen))) { + logger(LOG_ERR, _("Error while encrypting: %s"), gcry_strerror(err)); + return false; + } + + return true; +} + +bool cipher_decrypt(cipher_t *cipher, void *indata, size_t inlen, void *outdata, size_t *outlen, bool oneshot) { + gcry_error_t err; + + // To be fixed + + if((err = gcry_cipher_decrypt(cipher->handle, outdata, inlen, indata, inlen))) { + logger(LOG_ERR, _("Error while decrypting: %s"), gcry_strerror(err)); + return false; + } + + return true; +} + +int cipher_get_nid(const cipher_t *cipher) { + return cipher->nid; +} + +bool cipher_active(const cipher_t *cipher) { + return cipher->nid != 0; +} diff --git a/src/gcrypt/cipher.h b/src/gcrypt/cipher.h new file mode 100644 index 00000000..759a5239 --- /dev/null +++ b/src/gcrypt/cipher.h @@ -0,0 +1,49 @@ +/* + cipher.h -- header file cipher.c + Copyright (C) 2007 Guus Sliepen + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id$ +*/ + +#ifndef __TINC_CIPHER_H__ +#define __TINC_CIPHER_H__ + +#include + +typedef struct cipher { + gcry_cipher_hd_t handle; + char *key; + int nid; + uint16_t keylen; + uint16_t blklen; +} cipher_t; + +extern bool cipher_open_by_name(struct cipher *, const char *); +extern bool cipher_open_by_nid(struct cipher *, int); +extern bool cipher_open_blowfish_ofb(struct cipher *); +extern void cipher_close(struct cipher *); +extern size_t cipher_keylength(const struct cipher *); +extern void cipher_get_key(const struct cipher *, void *, bool); +extern bool cipher_set_key(struct cipher *, void *, bool); +extern bool cipher_set_key_from_rsa(struct cipher *, void *, size_t, bool); +extern bool cipher_regenerate_key(struct cipher *); +extern bool cipher_encrypt(struct cipher *, void *indata, size_t inlen, void *outdata, size_t *outlen, bool oneshot); +extern bool cipher_decrypt(struct cipher *, void *indata, size_t inlen, void *outdata, size_t *outlen, bool oneshot); +extern int cipher_get_nid(const struct cipher *); +extern bool cipher_active(const struct cipher *); + +#endif diff --git a/src/gcrypt/crypto.c b/src/gcrypt/crypto.c new file mode 100644 index 00000000..94656061 --- /dev/null +++ b/src/gcrypt/crypto.c @@ -0,0 +1,36 @@ +/* + crypto.c -- Cryptographic miscellaneous functions and initialisation + Copyright (C) 2007 Guus Sliepen + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id$ +*/ + +#include "system.h" + +#include + +#include "crypto.h" + +void crypto_init() { +} + +void crypto_exit() { +} + +void randomize(void *out, size_t outlen) { + gcry_create_nonce(out, outlen); +} diff --git a/src/gcrypt/crypto.h b/src/gcrypt/crypto.h new file mode 100644 index 00000000..39992517 --- /dev/null +++ b/src/gcrypt/crypto.h @@ -0,0 +1,29 @@ +/* + crypto.h -- header for crypto.c + Copyright (C) 2007 Guus Sliepen + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id$ +*/ + +#ifndef __TINC_CRYPTO_H__ +#define __TINC_CRYPTO_H__ + +extern void crypto_init(); +extern void crypto_exit(); +extern void randomize(void *, size_t); + +#endif diff --git a/src/gcrypt/digest.c b/src/gcrypt/digest.c new file mode 100644 index 00000000..639fa677 --- /dev/null +++ b/src/gcrypt/digest.c @@ -0,0 +1,140 @@ +/* + digest.c -- Digest handling + Copyright (C) 2007 Guus Sliepen + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id$ +*/ + +#include "system.h" + +#include "digest.h" +#include "logger.h" + +static struct { + const char *name; + int algo; + int nid; +} digesttable[] = { + {"none", GCRY_MD_NONE, 0}, + {"sha1", GCRY_MD_SHA1, 64}, + {"sha256", GCRY_MD_SHA256, 672}, + {"sha384", GCRY_MD_SHA384, 673}, + {"sha512", GCRY_MD_SHA512, 674}, +}; + +static bool nametodigest(const char *name, int *algo) { + int i; + + for(i = 0; i < sizeof digesttable / sizeof *digesttable; i++) { + if(digesttable[i].name && !strcasecmp(name, digesttable[i].name)) { + *algo = digesttable[i].algo; + return true; + } + } + + return false; +} + +static bool nidtodigest(int nid, int *algo) { + int i; + + for(i = 0; i < sizeof digesttable / sizeof *digesttable; i++) { + if(nid == digesttable[i].nid) { + *algo = digesttable[i].algo; + return true; + } + } + + return false; +} + +static bool digesttonid(int algo, int *nid) { + int i; + + for(i = 0; i < sizeof digesttable / sizeof *digesttable; i++) { + if(algo == digesttable[i].algo) { + *nid = digesttable[i].nid; + return true; + } + } + + return false; +} + +static bool digest_open(digest_t *digest, int algo) { + if(!digesttonid(algo, &digest->nid)) { + logger(LOG_DEBUG, _("Digest %d has no corresponding nid!"), algo); + return false; + } + + digest->len = gcry_md_get_algo_dlen(algo); + + return true; +} + +bool digest_open_by_name(digest_t *digest, const char *name) { + int algo; + + if(!nametodigest(name, &algo)) { + logger(LOG_DEBUG, _("Unknown digest name '%s'!"), name); + return false; + } + + return digest_open(digest, algo); +} + +bool digest_open_by_nid(digest_t *digest, int nid) { + int algo; + + if(!nidtodigest(nid, &algo)) { + logger(LOG_DEBUG, _("Unknown digest ID %d!"), nid); + return false; + } + + return digest_open(digest, algo); +} + +bool digest_open_sha1(digest_t *digest) { + return digest_open(digest, GCRY_MD_SHA1); +} + +void digest_close(digest_t *digest) { +} + +bool digest_create(digest_t *digest, const void *indata, size_t inlen, void *outdata) { + gcry_md_hash_buffer(digest->algo, outdata, indata, inlen); + return true; +} + +bool digest_verify(digest_t *digest, const void *indata, size_t inlen, const void *cmpdata) { + char outdata[digest->len]; + + gcry_md_hash_buffer(digest->algo, outdata, indata, inlen); + return !memcmp(cmpdata, outdata, digest->len); +} + +int digest_get_nid(const digest_t *digest) { + return digest->nid; +} + +size_t digest_length(const digest_t *digest) { + return digest->len; +} + +bool digest_active(const digest_t *digest) { + return digest->algo != GCRY_MD_NONE; +} diff --git a/src/gcrypt/digest.h b/src/gcrypt/digest.h new file mode 100644 index 00000000..6a644c7a --- /dev/null +++ b/src/gcrypt/digest.h @@ -0,0 +1,43 @@ +/* + digest.h -- header file digest.c + Copyright (C) 2007 Guus Sliepen + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id$ +*/ + +#ifndef __TINC_DIGEST_H__ +#define __TINC_DIGEST_H__ + +#include + +typedef struct digest { + int algo; + int nid; + uint16_t len; +} digest_t; + +static bool digest_open_by_name(struct digest *, const char *); +static bool digest_open_by_nid(struct digest *, int); +static bool digest_open_sha1(struct digest *); +static void digest_close(struct digest *); +static bool digest_create(struct digest *, const void *indata, size_t inlen, void *outdata); +static bool digest_verify(struct digest *, const void *indata, size_t inlen, const void *digestdata); +static int digest_get_nid(const struct digest *); +static size_t digest_length(const struct digest *); +static bool digest_active(const struct digest *); + +#endif diff --git a/src/gcrypt/rsa.c b/src/gcrypt/rsa.c new file mode 100644 index 00000000..99ee11b3 --- /dev/null +++ b/src/gcrypt/rsa.c @@ -0,0 +1,294 @@ +/* + rsa.c -- RSA key handling + Copyright (C) 2007 Guus Sliepen + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id$ +*/ + +#include "system.h" + +#include + +#include "logger.h" +#include "rsa.h" + +// Base64 encoding/decoding tables + +static const uint8_t b64d[128] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f, + 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x3a, 0x3b, 0x3c, 0x3d, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, + 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, + 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, + 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, + 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, + 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, + 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, + 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, + 0xff, 0xff +}; + +static const char b64e[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +// PEM encoding/decoding functions + +static bool pem_decode(FILE *fp, const char *header, uint8_t *buf, size_t size, size_t *outsize) { + bool decode = false; + char line[1024]; + uint16_t word = 0; + int shift = 10; + size_t i, j = 0; + + while(!feof(fp)) { + if(!fgets(line, sizeof line, fp)) + return false; + + if(!decode && !strncmp(line, "-----BEGIN ", 11)) { + if(!strncmp(line + 11, header, strlen(header))) + decode = true; + continue; + } + + if(decode && !strncmp(line, "-----END", 8)) { + break; + } + + if(!decode) + continue; + + for(i = 0; line[i] >= ' '; i++) { + if(line[i] >= 128 || line[i] < 0 || b64d[(int)line[i]] == 0xff) + break; + word |= b64d[(int)line[i]] << shift; + shift -= 6; + if(shift <= 2) { + if(j > size) { + errno = ENOMEM; + return false; + } + + buf[j++] = word >> 8; + word <<= 8; + shift += 8; + } + } + } + + if(outsize) + *outsize = j; + return true; +} + + +// BER decoding functions + +static int ber_read_id(unsigned char **p, size_t *buflen) { + if(*buflen <= 0) + return -1; + + if((**p & 0x1f) == 0x1f) { + int id = 0; + bool more; + while(*buflen > 0) { + id <<= 7; + id |= **p & 0x7f; + more = *(*p)++ & 0x80; + (*buflen)--; + if(!more) + break; + } + return id; + } else { + (*buflen)--; + return *(*p)++ & 0x1f; + } +} + +static size_t ber_read_len(unsigned char **p, size_t *buflen) { + if(*buflen <= 0) + return -1; + + if(**p & 0x80) { + size_t result = 0; + int len = *(*p)++ & 0x7f; + (*buflen)--; + if(len > *buflen) + return 0; + + while(len--) { + result <<= 8; + result |= *(*p)++; + (*buflen)--; + } + + return result; + } else { + (*buflen)--; + return *(*p)++; + } +} + + +static bool ber_read_sequence(unsigned char **p, size_t *buflen, size_t *result) { + int tag = ber_read_id(p, buflen); + size_t len = ber_read_len(p, buflen); + + if(tag == 0x10) { + if(result) + *result = len; + return true; + } else { + return false; + } +} + +static bool ber_read_mpi(unsigned char **p, size_t *buflen, gcry_mpi_t *mpi) { + int tag = ber_read_id(p, buflen); + size_t len = ber_read_len(p, buflen); + gcry_error_t err = 0; + + if(tag != 0x02 || len > *buflen) + return false; + + if(mpi) + err = gcry_mpi_scan(mpi, GCRYMPI_FMT_USG, *p, len, NULL); + + *p += len; + *buflen -= len; + + return mpi ? !err : true; +} + +bool rsa_set_hex_public_key(rsa_t *rsa, char *n, char *e) { + gcry_error_t err = 0; + + err = gcry_mpi_scan(&rsa->n, GCRY_FMT_HEX, n, 0, NULL) + ?: gcry_mpi_scan(&rsa->e, GCRY_FMT_HEX, n, 0, NULL); + + if(err) { + logger(LOG_ERR, _("Error while reading RSA public key: %s"), gcry_strerror(errno)); + return false; + } +} + +bool rsa_set_hex_private_key(rsa_t *rsa, char *n, char *e, char *d) { + gcry_error_t err = 0; + + err = gcry_mpi_scan(&rsa->n, GCRY_FMT_HEX, n, 0, NULL) + ?: gcry_mpi_scan(&rsa->e, GCRY_FMT_HEX, n, 0, NULL) + ?: gcry_mpi_scan(&rsa->d, GCRY_FMT_HEX, n, 0, NULL); + + if(err) { + logger(LOG_ERR, _("Error while reading RSA public key: %s"), gcry_strerror(errno)); + return false; + } +} + +// Read PEM RSA keys + +bool read_pem_rsa_public_key(rsa_t *rsa, FILE *fp) { + uint8_t derbuf[8096], *derp = derbuf; + size_t derlen; + + if(!pem_decode(fp, "RSA PUBLIC KEY", derbuf, sizeof derbuf, &derlen)) { + logger(LOG_ERR, _("Unable to read RSA public key: %s"), strerror(errno)); + return NULL; + } + + if(!ber_read_sequence(&derp, &derlen, NULL) + || !ber_read_mpi(&derp, &derlen, &rsa->n) + || !ber_read_mpi(&derp, &derlen, &rsa->e) + || derlen) { + logger(LOG_ERR, _("Error while decoding RSA public key")); + return NULL; + } + + return true; +} + +bool read_pem_rsa_private_key(rsa_t *rsa, FILE *fp) { + uint8_t derbuf[8096], *derp = derbuf; + size_t derlen; + + if(!pem_decode(fp, "RSA PRIVATE KEY", derbuf, sizeof derbuf, &derlen)) { + logger(LOG_ERR, _("Unable to read RSA private key: %s"), strerror(errno)); + return NULL; + } + + if(!ber_read_sequence(&derp, &derlen, NULL) + || !ber_read_mpi(&derp, &derlen, NULL) + || !ber_read_mpi(&derp, &derlen, &rsa->n) + || !ber_read_mpi(&derp, &derlen, &rsa->e) + || !ber_read_mpi(&derp, &derlen, &rsa->d) + || !ber_read_mpi(&derp, &derlen, NULL) // p + || !ber_read_mpi(&derp, &derlen, NULL) // q + || !ber_read_mpi(&derp, &derlen, NULL) + || !ber_read_mpi(&derp, &derlen, NULL) + || !ber_read_mpi(&derp, &derlen, NULL) // u + || derlen) { + logger(LOG_ERR, _("Error while decoding RSA private key")); + return NULL; + } + + return true; +} + +size_t rsa_size(rsa_t *rsa) { + return (gcry_mpi_get_nbits(rsa->n) + 7) / 8; +} + +/* Well, libgcrypt has functions to handle RSA keys, but they suck. + * So we just use libgcrypt's mpi functions, and do the math ourselves. + */ + +// TODO: get rid of this macro, properly clean up gcry_ structures after use +#define check(foo) { gcry_error_t err = (foo); if(err) {logger(LOG_ERR, "gcrypt error %s/%s at %s:%d\n", gcry_strsource(err), gcry_strerror(err), __FILE__, __LINE__); return false; }} + +bool rsa_public_encrypt(rsa_t *rsa, void *in, size_t len, void *out) { + gcry_mpi_t inmpi; + check(gcry_mpi_scan(&inmpi, GCRYMPI_FMT_USG, in, len, NULL)); + + gcry_mpi_t outmpi = gcry_mpi_new(len * 8); + gcry_mpi_powm(outmpi, inmpi, rsa->e, rsa->n); + + check(gcry_mpi_print(GCRYMPI_FMT_USG, out,len, NULL, outmpi)); + + return true; +} + +bool rsa_public_decrypt(rsa_t *rsa, void *in, size_t len, void *out) { + gcry_mpi_t inmpi; + check(gcry_mpi_scan(&inmpi, GCRYMPI_FMT_USG, in, len, NULL)); + + gcry_mpi_t outmpi = gcry_mpi_new(len * 8); + gcry_mpi_powm(outmpi, inmpi, rsa->d, rsa->n); + + check(gcry_mpi_print(GCRYMPI_FMT_USG, out,len, NULL, outmpi)); + + return true; +} diff --git a/src/gcrypt/rsa.h b/src/gcrypt/rsa.h new file mode 100644 index 00000000..0b3937a4 --- /dev/null +++ b/src/gcrypt/rsa.h @@ -0,0 +1,41 @@ +/* + rsa.h -- RSA key handling + Copyright (C) 2007 Guus Sliepen + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id$ +*/ + +#ifndef __TINC_RSA_H__ +#define __TINC_RSA_H__ + +#include + +typedef struct rsa { + gcry_mpi_t n; + gcry_mpi_t e; + gcry_mpi_t d; +} rsa_t; + +extern bool rsa_set_hex_public_key(rsa_t *rsa, char *n, char *e); +extern bool rsa_set_hex_private_key(rsa_t *rsa, char *n, char *e, char *d); +extern bool rsa_read_pem_public_key(rsa_t *rsa, FILE *fp); +extern bool rsa_read_pem_private_key(rsa_t *rsa, FILE *fp); +extern size_t rsa_size(rsa_t *rsa); +extern bool rsa_public_encrypt(rsa_t *rsa, void *in, size_t len, void *out); +extern bool rsa_private_decrypt(rsa_t *rsa, void *in, size_t len, void *out); + +#endif diff --git a/src/meta.c b/src/meta.c index 79514112..2791ea19 100644 --- a/src/meta.c +++ b/src/meta.c @@ -22,10 +22,8 @@ #include "system.h" -#include -#include - #include "splay_tree.h" +#include "cipher.h" #include "connection.h" #include "logger.h" #include "meta.h" @@ -35,31 +33,23 @@ #include "xalloc.h" bool send_meta(connection_t *c, const char *buffer, int length) { - int outlen; - int result; cp(); - ifdebug(META) logger(LOG_DEBUG, _("Sending %d bytes of metadata to %s (%s)"), length, - c->name, c->hostname); + ifdebug(META) logger(LOG_DEBUG, _("Sending %d bytes of metadata to %s (%s)"), length, c->name, c->hostname); /* Add our data to buffer */ if(c->status.encryptout) { char outbuf[length]; + size_t outlen = length; - result = EVP_EncryptUpdate(c->outctx, (unsigned char *)outbuf, &outlen, (unsigned char *)buffer, length); - if(!result || outlen != length) { - logger(LOG_ERR, _("Error while encrypting metadata to %s (%s): %s"), - c->name, c->hostname, ERR_error_string(ERR_get_error(), NULL)); + if(!cipher_encrypt(&c->outcipher, buffer, length, outbuf, &outlen, false) || outlen != length) { + logger(LOG_ERR, _("Error while encrypting metadata to %s (%s)"), c->name, c->hostname); return false; } - logger(LOG_DEBUG, _("Encrypted write %p %p %p %d"), c, c->buffer, outbuf, length); bufferevent_write(c->buffer, (void *)outbuf, length); - logger(LOG_DEBUG, _("Done.")); } else { - logger(LOG_DEBUG, _("Unencrypted write %p %p %p %d"), c, c->buffer, buffer, length); bufferevent_write(c->buffer, (void *)buffer, length); - logger(LOG_DEBUG, _("Done.")); } return true; @@ -80,7 +70,7 @@ void broadcast_meta(connection_t *from, const char *buffer, int length) { } bool receive_meta(connection_t *c) { - int result, inlen, outlen; + size_t inlen; char inbuf[MAXBUFSIZE]; char *bufp = inbuf, *endp; @@ -110,23 +100,20 @@ bool receive_meta(connection_t *c) { else endp = bufp + inlen; - logger(LOG_DEBUG, _("Received unencrypted %ld of %d bytes"), endp - bufp, inlen); - evbuffer_add(c->buffer->input, bufp, endp - bufp); inlen -= endp - bufp; bufp = endp; } else { - logger(LOG_DEBUG, _("Received encrypted %d bytes"), inlen); + size_t outlen = inlen; evbuffer_expand(c->buffer->input, inlen); - result = EVP_DecryptUpdate(c->inctx, (unsigned char *)c->buffer->input->buffer, &outlen, (unsigned char *)bufp, inlen); - if(!result || outlen != inlen) { - logger(LOG_ERR, _("Error while decrypting metadata from %s (%s): %s"), - c->name, c->hostname, ERR_error_string(ERR_get_error(), NULL)); + + if(!cipher_decrypt(&c->incipher, bufp, inlen, c->buffer->input->buffer, &outlen, false) || inlen != outlen) { + logger(LOG_ERR, _("Error while decrypting metadata from %s (%s)"), c->name, c->hostname); return false; } - c->buffer->input->off += inlen; + c->buffer->input->off += inlen; inlen = 0; } @@ -148,8 +135,10 @@ bool receive_meta(connection_t *c) { char *request = evbuffer_readline(c->buffer->input); if(request) { - receive_request(c, request); + bool result = receive_request(c, request); free(request); + if(!result) + return false; continue; } else { break; diff --git a/src/net_packet.c b/src/net_packet.c index 2a86d657..eee3972e 100644 --- a/src/net_packet.c +++ b/src/net_packet.c @@ -22,18 +22,15 @@ #include "system.h" -#include -#include -#include -#include -#include - #include #include LZO1X_H #include "splay_tree.h" +#include "cipher.h" #include "conf.h" #include "connection.h" +#include "crypto.h" +#include "digest.h" #include "device.h" #include "ethernet.h" #include "graph.h" @@ -52,7 +49,6 @@ #endif int keylifetime = 0; -EVP_CIPHER_CTX packet_ctx; static char lzo_wrkmem[LZO1X_999_MEM_COMPRESS > LZO1X_1_MEM_COMPRESS ? LZO1X_999_MEM_COMPRESS : LZO1X_1_MEM_COMPRESS]; static void send_udppacket(node_t *, vpn_packet_t *); @@ -85,7 +81,7 @@ static void send_mtu_probe_handler(int fd, short events, void *data) { len = 64; memset(packet.data, 0, 14); - RAND_pseudo_bytes(packet.data + 14, len - 14); + randomize(packet.data + 14, len - 14); packet.len = len; ifdebug(TRAFFIC) logger(LOG_INFO, _("Sending MTU probe length %d to %s (%s)"), len, n->name, n->hostname); @@ -168,15 +164,14 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) { vpn_packet_t *pkt[] = { &pkt1, &pkt2, &pkt1, &pkt2 }; int nextpkt = 0; vpn_packet_t *outpkt = pkt[0]; - int outlen, outpad; - unsigned char hmac[EVP_MAX_MD_SIZE]; + size_t outlen; int i; cp(); /* Check packet length */ - if(inpkt->len < sizeof(inpkt->seqno) + myself->maclength) { + if(inpkt->len < sizeof(inpkt->seqno) + digest_length(&myself->digest)) { ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Got too short packet from %s (%s)"), n->name, n->hostname); return; @@ -184,33 +179,23 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) { /* Check the message authentication code */ - if(myself->digest && myself->maclength) { - inpkt->len -= myself->maclength; - HMAC(myself->digest, myself->key, myself->keylength, - (unsigned char *) &inpkt->seqno, inpkt->len, (unsigned char *)hmac, NULL); - - if(memcmp(hmac, (char *) &inpkt->seqno + inpkt->len, myself->maclength)) { - ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Got unauthenticated packet from %s (%s)"), - n->name, n->hostname); - return; - } + if(digest_active(&myself->digest) && !digest_verify(&myself->digest, &inpkt->seqno, inpkt->len, &inpkt->seqno + inpkt->len)) { + ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Got unauthenticated packet from %s (%s)"), n->name, n->hostname); + return; } /* Decrypt the packet */ - if(myself->cipher) { + if(cipher_active(&myself->cipher)) { outpkt = pkt[nextpkt++]; + outlen = MAXSIZE; - if(!EVP_DecryptInit_ex(&packet_ctx, NULL, NULL, NULL, NULL) - || !EVP_DecryptUpdate(&packet_ctx, (unsigned char *) &outpkt->seqno, &outlen, - (unsigned char *) &inpkt->seqno, inpkt->len) - || !EVP_DecryptFinal_ex(&packet_ctx, (unsigned char *) &outpkt->seqno + outlen, &outpad)) { - ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Error decrypting packet from %s (%s): %s"), - n->name, n->hostname, ERR_error_string(ERR_get_error(), NULL)); + if(!cipher_decrypt(&myself->cipher, &inpkt->seqno, inpkt->len, &outpkt->seqno, &outlen, true)) { + ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Error decrypting packet from %s (%s)"), n->name, n->hostname); return; } - outpkt->len = outlen + outpad; + outpkt->len = outlen; inpkt = outpkt; } @@ -283,7 +268,7 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) { int nextpkt = 0; vpn_packet_t *outpkt; int origlen; - int outlen, outpad; + size_t outlen; vpn_packet_t *copy; static int priority = 0; int origpriority; @@ -339,28 +324,24 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) { /* Encrypt the packet */ - if(n->cipher) { + if(cipher_active(&n->cipher)) { outpkt = pkt[nextpkt++]; + outlen = MAXSIZE; - if(!EVP_EncryptInit_ex(&n->packet_ctx, NULL, NULL, NULL, NULL) - || !EVP_EncryptUpdate(&n->packet_ctx, (unsigned char *) &outpkt->seqno, &outlen, - (unsigned char *) &inpkt->seqno, inpkt->len) - || !EVP_EncryptFinal_ex(&n->packet_ctx, (unsigned char *) &outpkt->seqno + outlen, &outpad)) { - ifdebug(TRAFFIC) logger(LOG_ERR, _("Error while encrypting packet to %s (%s): %s"), - n->name, n->hostname, ERR_error_string(ERR_get_error(), NULL)); + if(!cipher_encrypt(&n->cipher, &inpkt->seqno, inpkt->len, &outpkt->seqno, &outlen, true)) { + ifdebug(TRAFFIC) logger(LOG_ERR, _("Error while encrypting packet to %s (%s)"), n->name, n->hostname); goto end; } - outpkt->len = outlen + outpad; + outpkt->len = outlen; inpkt = outpkt; } /* Add the message authentication code */ - if(n->digest && n->maclength) { - HMAC(n->digest, n->key, n->keylength, (unsigned char *) &inpkt->seqno, - inpkt->len, (unsigned char *) &inpkt->seqno + inpkt->len, NULL); - inpkt->len += n->maclength; + if(digest_active(&n->digest)) { + digest_create(&n->digest, &inpkt->seqno, inpkt->len, &inpkt->seqno + inpkt->len); + inpkt->len += digest_length(&n->digest); } /* Determine which socket we have to use */ diff --git a/src/net_setup.c b/src/net_setup.c index 94e39872..033bf376 100644 --- a/src/net_setup.c +++ b/src/net_setup.c @@ -22,19 +22,13 @@ #include "system.h" -#include - -#include -#include -#include -#include -#include - #include "splay_tree.h" +#include "cipher.h" #include "conf.h" #include "connection.h" #include "control.h" #include "device.h" +#include "digest.h" #include "graph.h" #include "logger.h" #include "net.h" @@ -53,10 +47,21 @@ static struct event device_ev; bool read_rsa_public_key(connection_t *c) { FILE *fp; char *fname; + char *n; bool result; cp(); + /* First, check for simple PublicKey statement */ + + if(get_config_string(lookup_config(c->config_tree, "PublicKey"), &n)) { + result = rsa_set_hex_public_key(&c->rsa, n, "FFFF"); + free(n); + return result; + } + + /* Else, check for PublicKeyFile statement and read it */ + if(!get_config_string(lookup_config(c->config_tree, "PublicKeyFile"), &fname)) asprintf(&fname, "%s/hosts/%s", confbase, c->name); @@ -69,7 +74,7 @@ bool read_rsa_public_key(connection_t *c) { return false; } - result = read_pem_rsa_public_key(fp, &c->rsa_key); + result = rsa_read_pem_public_key(&c->rsa, fp); fclose(fp); if(!result) @@ -81,10 +86,27 @@ bool read_rsa_public_key(connection_t *c) { bool read_rsa_private_key() { FILE *fp; char *fname; + char *n, *d; bool result; cp(); + /* First, check for simple PrivateKey statement */ + + if(get_config_string(lookup_config(config_tree, "PrivateKey"), &d)) { + if(!get_config_string(lookup_config(myself->connection->config_tree, "PublicKey"), &n)) { + logger(LOG_ERR, _("PrivateKey used but no PublicKey found!")); + free(d); + return false; + } + result = rsa_set_hex_private_key(&myself->connection->rsa, n, "FFFF", d); + free(n); + free(d); + return true; + } + + /* Else, check for PrivateKeyFile statement and read it */ + if(!get_config_string(lookup_config(config_tree, "PrivateKeyFile"), &fname)) asprintf(&fname, "%s/rsa_key.priv", confbase); @@ -110,7 +132,7 @@ bool read_rsa_private_key() { logger(LOG_WARNING, _("Warning: insecure file permissions for RSA private key file `%s'!"), fname); #endif - result = read_pem_rsa_private_key(fp, &myself->connection->rsa_key); + result = rsa_read_pem_private_key(&myself->connection->rsa, fp); fclose(fp); if(!result) @@ -126,10 +148,14 @@ static void keyexpire_handler(int fd, short events, void *data) { } void regenerate_key() { - RAND_pseudo_bytes((unsigned char *)myself->key, myself->keylength); + ifdebug(STATUS) logger(LOG_INFO, _("Regenerating symmetric key")); + + if(!cipher_regenerate_key(&myself->cipher, true)) { + logger(LOG_ERR, _("Error regenerating key!")); + abort(); + } if(timeout_initialized(&keyexpire_event)) { - ifdebug(STATUS) logger(LOG_INFO, _("Regenerating symmetric key")); event_del(&keyexpire_event); send_key_changed(broadcast, myself); } else { @@ -137,16 +163,6 @@ void regenerate_key() { } event_add(&keyexpire_event, &(struct timeval){keylifetime, 0}); - - if(myself->cipher) { - EVP_CIPHER_CTX_init(&packet_ctx); - if(!EVP_DecryptInit_ex(&packet_ctx, myself->cipher, NULL, (unsigned char *)myself->key, (unsigned char *)myself->key + myself->cipher->key_len)) { - logger(LOG_ERR, _("Error during initialisation of cipher for %s (%s): %s"), - myself->name, myself->hostname, ERR_error_string(ERR_get_error(), NULL)); - abort(); - } - - } } /* @@ -285,73 +301,44 @@ bool setup_myself(void) { /* Generate packet encryption key */ - if(get_config_string - (lookup_config(myself->connection->config_tree, "Cipher"), &cipher)) { - if(!strcasecmp(cipher, "none")) { - myself->cipher = NULL; - } else { - myself->cipher = EVP_get_cipherbyname(cipher); - - if(!myself->cipher) { - logger(LOG_ERR, _("Unrecognized cipher type!")); - return false; - } - } - } else - myself->cipher = EVP_bf_cbc(); - - if(myself->cipher) - myself->keylength = myself->cipher->key_len + myself->cipher->iv_len; - else - myself->keylength = 1; - - myself->connection->outcipher = EVP_bf_ofb(); + if(!get_config_string(lookup_config(myself->connection->config_tree, "Cipher"), &cipher)) + cipher = xstrdup("blowfish"); - myself->key = xmalloc(myself->keylength); + if(!cipher_open_by_name(&myself->cipher, cipher)) { + logger(LOG_ERR, _("Unrecognized cipher type!")); + return false; + } if(!get_config_int(lookup_config(config_tree, "KeyExpire"), &keylifetime)) keylifetime = 3600; regenerate_key(); + /* Check if we want to use message authentication codes... */ - if(get_config_string - (lookup_config(myself->connection->config_tree, "Digest"), &digest)) { - if(!strcasecmp(digest, "none")) { - myself->digest = NULL; - } else { - myself->digest = EVP_get_digestbyname(digest); - - if(!myself->digest) { - logger(LOG_ERR, _("Unrecognized digest type!")); - return false; - } - } - } else - myself->digest = EVP_sha1(); - - myself->connection->outdigest = EVP_sha1(); - - if(get_config_int(lookup_config(myself->connection->config_tree, "MACLength"), - &myself->maclength)) { - if(myself->digest) { - if(myself->maclength > myself->digest->md_size) { - logger(LOG_ERR, _("MAC length exceeds size of digest!")); - return false; - } else if(myself->maclength < 0) { - logger(LOG_ERR, _("Bogus MAC length!")); - return false; - } - } - } else - myself->maclength = 4; + if(!get_config_string(lookup_config(myself->connection->config_tree, "Digest"), &digest)) + digest = xstrdup("sha1"); - myself->connection->outmaclength = 0; + if(!digest_open_by_name(&myself->digest, digest)) { + logger(LOG_ERR, _("Unrecognized digest type!")); + return false; + } + + if(!get_config_int(lookup_config(myself->connection->config_tree, "MACLength"), &myself->maclength)) + + if(digest_active(&myself->digest)) { + if(myself->maclength > digest_length(&myself->digest)) { + logger(LOG_ERR, _("MAC length exceeds size of digest!")); + return false; + } else if(myself->maclength < 0) { + logger(LOG_ERR, _("Bogus MAC length!")); + return false; + } + } /* Compression */ - if(get_config_int(lookup_config(myself->connection->config_tree, "Compression"), - &myself->compression)) { + if(get_config_int(lookup_config(myself->connection->config_tree, "Compression"), &myself->compression)) { if(myself->compression < 0 || myself->compression > 11) { logger(LOG_ERR, _("Bogus compression level!")); return false; @@ -375,8 +362,8 @@ bool setup_myself(void) { if(!setup_device()) return false; - event_set(&device_ev, device_fd, EV_READ|EV_PERSIST, - handle_device_data, NULL); + event_set(&device_ev, device_fd, EV_READ|EV_PERSIST, handle_device_data, NULL); + if (event_add(&device_ev, NULL) < 0) { logger(LOG_ERR, _("event_add failed: %s"), strerror(errno)); close_device(); diff --git a/src/node.c b/src/node.c index cbafe712..280a70cb 100644 --- a/src/node.c +++ b/src/node.c @@ -74,7 +74,6 @@ node_t *new_node(void) { n->subnet_tree = new_subnet_tree(); n->edge_tree = new_edge_tree(); n->queue = list_alloc((list_action_t) free); - EVP_CIPHER_CTX_init(&n->packet_ctx); n->mtu = MTU; n->maxmtu = MTU; @@ -87,9 +86,6 @@ void free_node(node_t *n) { if(n->queue) list_delete_list(n->queue); - if(n->key) - free(n->key); - if(n->subnet_tree) free_subnet_tree(n->subnet_tree); @@ -98,7 +94,8 @@ void free_node(node_t *n) { sockaddrfree(&n->address); - EVP_CIPHER_CTX_cleanup(&n->packet_ctx); + cipher_close(&n->cipher); + digest_close(&n->digest); event_del(&n->mtuevent); @@ -171,8 +168,8 @@ void dump_nodes(void) { for(node = node_tree->head; node; node = node->next) { n = node->data; logger(LOG_DEBUG, _(" %s at %s cipher %d digest %d maclength %d compression %d options %lx status %04x nexthop %s via %s pmtu %d (min %d max %d)"), - n->name, n->hostname, n->cipher ? n->cipher->nid : 0, - n->digest ? n->digest->type : 0, n->maclength, n->compression, + n->name, n->hostname, cipher_get_nid(&n->cipher), + digest_get_nid(&n->digest), n->maclength, n->compression, n->options, *(uint32_t *)&n->status, n->nexthop ? n->nexthop->name : "-", n->via ? n->via->name : "-", n->mtu, n->minmtu, n->maxmtu); } diff --git a/src/node.h b/src/node.h index 83af5e3c..4186d061 100644 --- a/src/node.h +++ b/src/node.h @@ -24,7 +24,9 @@ #define __TINC_NODE_H__ #include "splay_tree.h" +#include "cipher.h" #include "connection.h" +#include "digest.h" #include "list.h" #include "subnet.h" @@ -50,13 +52,9 @@ typedef struct node_t { node_status_t status; - const EVP_CIPHER *cipher; /* Cipher type for UDP packets */ - char *key; /* Cipher key and iv */ - int keylength; /* Cipher key and iv length */ - EVP_CIPHER_CTX packet_ctx; /* Cipher context */ - - const EVP_MD *digest; /* Digest type for MAC */ - int maclength; /* Length of MAC */ + cipher_t cipher; /* Cipher for UDP packets */ + digest_t digest; /* Digest for UDP packets */ + int maclength; /* Portion of digest to use */ int compression; /* Compressionlevel, 0 = no compression */ diff --git a/src/openssl/cipher.c b/src/openssl/cipher.c new file mode 100644 index 00000000..bf999a21 --- /dev/null +++ b/src/openssl/cipher.c @@ -0,0 +1,182 @@ +/* + cipher.c -- Symmetric block cipher handling + Copyright (C) 2007 Guus Sliepen + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id$ +*/ + +#include "system.h" + +#include +#include + +#include "cipher.h" +#include "logger.h" +#include "xalloc.h" + +static bool cipher_open(cipher_t *cipher) { + cipher->keylen = cipher->cipher->key_len; + cipher->blklen = cipher->cipher->iv_len; + + cipher->key = xmalloc(cipher->keylen + cipher->blklen); + + EVP_CIPHER_CTX_init(&cipher->ctx); + + return true; +} + +bool cipher_open_by_name(cipher_t *cipher, const char *name) { + cipher->cipher = EVP_get_cipherbyname(name); + + if(cipher->cipher) + return cipher_open(cipher); + + logger(LOG_DEBUG, _("Unknown cipher name '%s'!"), name); + return false; +} + +bool cipher_open_by_nid(cipher_t *cipher, int nid) { + cipher->cipher = EVP_get_cipherbynid(nid); + + if(cipher->cipher) + return cipher_open(cipher); + + logger(LOG_DEBUG, _("Unknown cipher nid %d!"), nid); + return false; +} + +bool cipher_open_blowfish_ofb(cipher_t *cipher) { + cipher->cipher = EVP_bf_ofb(); + return cipher_open(cipher); +} + +void cipher_close(cipher_t *cipher) { + EVP_CIPHER_CTX_cleanup(&cipher->ctx); + + if(cipher->key) { + free(cipher->key); + cipher->key = NULL; + } +} + +size_t cipher_keylength(const cipher_t *cipher) { + return cipher->keylen + cipher->blklen; +} + +void cipher_get_key(const cipher_t *cipher, void *key) { + memcpy(key, cipher->key, cipher->keylen + cipher->blklen); +} + +bool cipher_set_key(cipher_t *cipher, void *key, bool encrypt) { + memcpy(cipher->key, key, cipher->keylen + cipher->blklen); + bool result; + + if(encrypt) + result = EVP_EncryptInit_ex(&cipher->ctx, cipher->cipher, NULL, (unsigned char *)cipher->key, (unsigned char *)cipher->key + cipher->keylen); + else + result = EVP_DecryptInit_ex(&cipher->ctx, cipher->cipher, NULL, (unsigned char *)cipher->key, (unsigned char *)cipher->key + cipher->keylen); + + if(result) + return true; + + logger(LOG_ERR, _("Error while setting key: %s"), ERR_error_string(ERR_get_error(), NULL)); + return false; +} + +bool cipher_set_key_from_rsa(cipher_t *cipher, void *key, size_t len, bool encrypt) { + memcpy(cipher->key, key + len - (size_t)cipher->keylen, cipher->keylen); + memcpy(cipher->key + cipher->keylen, key + len - (size_t)cipher->keylen - (size_t)cipher->blklen, cipher->blklen); + bool result; + + if(encrypt) + result = EVP_EncryptInit_ex(&cipher->ctx, cipher->cipher, NULL, (unsigned char *)cipher->key, (unsigned char *)cipher->key + cipher->keylen); + else + result = EVP_DecryptInit_ex(&cipher->ctx, cipher->cipher, NULL, (unsigned char *)cipher->key, (unsigned char *)cipher->key + cipher->keylen); + + if(result) + return true; + + logger(LOG_ERR, _("Error while setting key: %s"), ERR_error_string(ERR_get_error(), NULL)); + return false; +} + +bool cipher_regenerate_key(cipher_t *cipher, bool encrypt) { + bool result; + + RAND_pseudo_bytes((unsigned char *)cipher->key, cipher->keylen + cipher->blklen); + + if(encrypt) + result = EVP_EncryptInit_ex(&cipher->ctx, cipher->cipher, NULL, (unsigned char *)cipher->key, (unsigned char *)cipher->key + cipher->keylen); + else + result = EVP_DecryptInit_ex(&cipher->ctx, cipher->cipher, NULL, (unsigned char *)cipher->key, (unsigned char *)cipher->key + cipher->keylen); + + if(result) + return true; + + logger(LOG_ERR, _("Error while regenerating key: %s"), ERR_error_string(ERR_get_error(), NULL)); + return false; +} + +bool cipher_encrypt(cipher_t *cipher, void *indata, size_t inlen, void *outdata, size_t *outlen, bool oneshot) { + if(oneshot) { + int len = *outlen, pad; + if(EVP_EncryptInit_ex(&cipher->ctx, NULL, NULL, NULL, NULL) + &&EVP_EncryptUpdate(&cipher->ctx, outdata, &len, indata, inlen) + && EVP_EncryptFinal(&cipher->ctx, outdata + len, &pad)) { + *outlen = len + pad; + return true; + } + } else { + int len = *outlen; + if(EVP_EncryptUpdate(&cipher->ctx, outdata, &len, indata, inlen)) { + *outlen = len; + return true; + } + } + + logger(LOG_ERR, _("Error while encrypting: %s"), ERR_error_string(ERR_get_error(), NULL)); + return false; +} + +bool cipher_decrypt(cipher_t *cipher, void *indata, size_t inlen, void *outdata, size_t *outlen, bool oneshot) { + if(oneshot) { + int len = *outlen, pad; + if(EVP_DecryptInit_ex(&cipher->ctx, NULL, NULL, NULL, NULL) + && EVP_DecryptUpdate(&cipher->ctx, outdata, &len, indata, inlen) + && EVP_DecryptFinal(&cipher->ctx, outdata + len, &pad)) { + *outlen = len + pad; + return true; + } + } else { + int len = *outlen; + if(EVP_EncryptUpdate(&cipher->ctx, outdata, &len, indata, inlen)) { + *outlen = len; + return true; + } + } + + logger(LOG_ERR, _("Error while encrypting: %s"), ERR_error_string(ERR_get_error(), NULL)); + return false; +} + +int cipher_get_nid(const cipher_t *cipher) { + return cipher->cipher ? cipher->cipher->nid : 0; +} + +bool cipher_active(const cipher_t *cipher) { + return cipher->cipher && cipher->cipher->nid != 0; +} diff --git a/src/openssl/cipher.h b/src/openssl/cipher.h new file mode 100644 index 00000000..68acb410 --- /dev/null +++ b/src/openssl/cipher.h @@ -0,0 +1,49 @@ +/* + cipher.h -- header file cipher.c + Copyright (C) 2007 Guus Sliepen + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id$ +*/ + +#ifndef __TINC_CIPHER_H__ +#define __TINC_CIPHER_H__ + +#include + +typedef struct cipher { + EVP_CIPHER_CTX ctx; + const EVP_CIPHER *cipher; + char *key; + uint16_t keylen; + uint16_t blklen; +} cipher_t; + +extern bool cipher_open_by_name(cipher_t *, const char *); +extern bool cipher_open_by_nid(cipher_t *, int); +extern bool cipher_open_blowfish_ofb(cipher_t *); +extern void cipher_close(cipher_t *); +extern size_t cipher_keylength(const cipher_t *); +extern void cipher_get_key(const cipher_t *, void *); +extern bool cipher_set_key(cipher_t *, void *, bool); +extern bool cipher_set_key_from_rsa(cipher_t *, void *, size_t, bool); +extern bool cipher_regenerate_key(cipher_t *, bool); +extern bool cipher_encrypt(cipher_t *, void *indata, size_t inlen, void *outdata, size_t *outlen, bool); +extern bool cipher_decrypt(cipher_t *, void *indata, size_t inlen, void *outdata, size_t *outlen, bool); +extern int cipher_get_nid(const cipher_t *); +extern bool cipher_active(const cipher_t *); + +#endif diff --git a/src/openssl/crypto.c b/src/openssl/crypto.c new file mode 100644 index 00000000..7e6e9f62 --- /dev/null +++ b/src/openssl/crypto.c @@ -0,0 +1,45 @@ +/* + crypto.c -- Cryptographic miscellaneous functions and initialisation + Copyright (C) 2007 Guus Sliepen + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id$ +*/ + +#include "system.h" + +#include +#include +#include + +#include "crypto.h" + +void crypto_init() { + RAND_load_file("/dev/urandom", 1024); + + ENGINE_load_builtin_engines(); + ENGINE_register_all_complete(); + + OpenSSL_add_all_algorithms(); +} + +void crypto_exit() { + EVP_cleanup(); +} + +void randomize(void *out, size_t outlen) { + RAND_pseudo_bytes(out, outlen); +} diff --git a/src/openssl/crypto.h b/src/openssl/crypto.h new file mode 100644 index 00000000..39992517 --- /dev/null +++ b/src/openssl/crypto.h @@ -0,0 +1,29 @@ +/* + crypto.h -- header for crypto.c + Copyright (C) 2007 Guus Sliepen + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id$ +*/ + +#ifndef __TINC_CRYPTO_H__ +#define __TINC_CRYPTO_H__ + +extern void crypto_init(); +extern void crypto_exit(); +extern void randomize(void *, size_t); + +#endif diff --git a/src/openssl/digest.c b/src/openssl/digest.c new file mode 100644 index 00000000..7c4dfc23 --- /dev/null +++ b/src/openssl/digest.c @@ -0,0 +1,84 @@ +/* + digest.c -- Digest handling + Copyright (C) 2007 Guus Sliepen + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id$ +*/ + +#include "system.h" + +#include + +#include "digest.h" +#include "logger.h" + +bool digest_open_by_name(digest_t *digest, const char *name) { + digest->digest = EVP_get_digestbyname(name); + if(digest->digest) + return true; + + logger(LOG_DEBUG, _("Unknown digest name '%s'!"), name); + return false; +} + +bool digest_open_by_nid(digest_t *digest, int nid) { + digest->digest = EVP_get_digestbynid(nid); + if(digest->digest) + return true; + + logger(LOG_DEBUG, _("Unknown digest nid %d!"), nid); + return false; +} + +bool digest_open_sha1(digest_t *digest) { + digest->digest = EVP_sha1(); + return true; +} + +void digest_close(digest_t *digest) { +} + +bool digest_create(digest_t *digest, void *indata, size_t inlen, void *outdata) { + EVP_MD_CTX ctx; + + if(EVP_DigestInit(&ctx, digest->digest) + && EVP_DigestUpdate(&ctx, indata, inlen) + && EVP_DigestFinal(&ctx, outdata, NULL)) + return true; + + logger(LOG_DEBUG, _("Error creating digest: %s"), ERR_error_string(ERR_get_error(), NULL)); + return false; +} + +bool digest_verify(digest_t *digest, void *indata, size_t inlen, void *cmpdata) { + size_t len = EVP_MD_size(digest->digest); + char outdata[len]; + + return digest_create(digest, indata, inlen, outdata) && !memcmp(cmpdata, outdata, len); +} + +int digest_get_nid(const digest_t *digest) { + return digest->digest ? digest->digest->type : 0; +} + +size_t digest_length(const digest_t *digest) { + return EVP_MD_size(digest->digest); +} + +bool digest_active(const digest_t *digest) { + return digest->digest && digest->digest->type != 0; +} diff --git a/src/openssl/digest.h b/src/openssl/digest.h new file mode 100644 index 00000000..28ee9d0d --- /dev/null +++ b/src/openssl/digest.h @@ -0,0 +1,41 @@ +/* + digest.h -- header file digest.c + Copyright (C) 2007 Guus Sliepen + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id$ +*/ + +#ifndef __TINC_DIGEST_H__ +#define __TINC_DIGEST_H__ + +#include + +typedef struct digest { + const EVP_MD *digest; +} digest_t; + +extern bool digest_open_by_name(struct digest *, const char *); +extern bool digest_open_by_nid(struct digest *, int); +extern bool digest_open_sha1(struct digest *); +extern void digest_close(struct digest *); +extern bool digest_create(struct digest *, void *indata, size_t inlen, void *outdata); +extern bool digest_verify(struct digest *, void *indata, size_t inlen, void *digestdata); +extern int digest_get_nid(const struct digest *); +extern size_t digest_length(const struct digest *); +extern bool digest_active(const struct digest *); + +#endif diff --git a/src/openssl/rsa.c b/src/openssl/rsa.c new file mode 100644 index 00000000..5e923f16 --- /dev/null +++ b/src/openssl/rsa.c @@ -0,0 +1,92 @@ +/* + rsa.c -- RSA key handling + Copyright (C) 2007 Guus Sliepen + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id$ +*/ + +#include "system.h" + +#include +#include + +#include "logger.h" +#include "rsa.h" + +// Set RSA keys + +bool rsa_set_hex_public_key(rsa_t *rsa, char *n, char *e) { + *rsa = RSA_new(); + BN_hex2bn(&(*rsa)->n, n); + BN_hex2bn(&(*rsa)->e, e); + return true; +} + +bool rsa_set_hex_private_key(rsa_t *rsa, char *n, char *e, char *d) { + *rsa = RSA_new(); + BN_hex2bn(&(*rsa)->n, n); + BN_hex2bn(&(*rsa)->e, e); + BN_hex2bn(&(*rsa)->d, d); + return true; +} + +// Read PEM RSA keys + +bool rsa_read_pem_public_key(rsa_t *rsa, FILE *fp) { + *rsa = PEM_read_RSAPublicKey(fp, rsa, NULL, NULL); + + if(*rsa) + return true; + + *rsa = PEM_read_RSA_PUBKEY(fp, rsa, NULL, NULL); + + if(*rsa) + return true; + + logger(LOG_ERR, _("Unable to read RSA public key: %s"), ERR_error_string(ERR_get_error(), NULL)); + return false; +} + +bool rsa_read_pem_private_key(rsa_t *rsa, FILE *fp) { + *rsa = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL); + + if(*rsa) + return true; + + logger(LOG_ERR, _("Unable to read RSA private key: %s"), ERR_error_string(ERR_get_error(), NULL)); + return false; +} + +size_t rsa_size(rsa_t *rsa) { + return RSA_size(*rsa); +} + +bool rsa_public_encrypt(rsa_t *rsa, void *in, size_t len, void *out) { + if(RSA_public_encrypt(len, in, out, *rsa, RSA_NO_PADDING) == len) + return true; + + logger(LOG_ERR, _("Unable to perform RSA encryption: %s"), ERR_error_string(ERR_get_error(), NULL)); + return false; +} + +bool rsa_private_decrypt(rsa_t *rsa, void *in, size_t len, void *out) { + if(RSA_private_decrypt(len, in, out, *rsa, RSA_NO_PADDING) == len) + return true; + + logger(LOG_ERR, _("Unable to perform RSA decryption: %s"), ERR_error_string(ERR_get_error(), NULL)); + return false; +} diff --git a/src/openssl/rsa.h b/src/openssl/rsa.h new file mode 100644 index 00000000..dade1f3b --- /dev/null +++ b/src/openssl/rsa.h @@ -0,0 +1,37 @@ +/* + rsa.h -- RSA key handling + Copyright (C) 2007 Guus Sliepen + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id$ +*/ + +#ifndef __TINC_RSA_H__ +#define __TINC_RSA_H__ + +#include + +typedef RSA *rsa_t; + +extern bool rsa_set_hex_public_key(rsa_t *rsa, char *n, char *e); +extern bool rsa_set_hex_private_key(rsa_t *rsa, char *n, char *e, char *d); +extern bool rsa_read_pem_public_key(rsa_t *rsa, FILE *fp); +extern bool rsa_read_pem_private_key(rsa_t *rsa, FILE *fp); +extern size_t rsa_size(rsa_t *rsa); +extern bool rsa_public_encrypt(rsa_t *rsa, void *in, size_t inlen, void *out); +extern bool rsa_private_decrypt(rsa_t *rsa, void *in, size_t inlen, void *out); + +#endif diff --git a/src/protocol_auth.c b/src/protocol_auth.c index 499e6869..291c4b01 100644 --- a/src/protocol_auth.c +++ b/src/protocol_auth.c @@ -22,14 +22,10 @@ #include "system.h" -#include -#include -#include -#include - #include "splay_tree.h" #include "conf.h" #include "connection.h" +#include "crypto.h" #include "edge.h" #include "graph.h" #include "logger.h" @@ -116,27 +112,22 @@ bool id_h(connection_t *c, char *request) { } bool send_metakey(connection_t *c) { - char *buffer; - unsigned int len; - bool x; + size_t len = rsa_size(&c->rsa); + char key[len]; + char enckey[len]; + char hexkey[2 * len + 1]; cp(); - len = get_rsa_size(&c->rsa_key); - - /* Allocate buffers for the meta key */ - - buffer = alloca(2 * len + 1); + if(!cipher_open_blowfish_ofb(&c->outcipher)) + return false; - if(!c->outkey) - c->outkey = xmalloc(len); + if(!digest_open_sha1(&c->outdigest)) + return false; - if(!c->outctx) - c->outctx = xmalloc_and_zero(sizeof(*c->outctx)); - cp(); - /* Copy random data to the buffer */ + /* Create a random key */ - RAND_pseudo_bytes((unsigned char *)c->outkey, len); + randomize(key, len); /* The message we send must be smaller than the modulus of the RSA key. By definition, for a key of k bits, the following formula holds: @@ -148,13 +139,14 @@ bool send_metakey(connection_t *c) { This can be done by setting the most significant bit to zero. */ - c->outkey[0] &= 0x7F; + key[0] &= 0x7F; + + cipher_set_key_from_rsa(&c->outcipher, key, len, true); ifdebug(SCARY_THINGS) { - bin2hex(c->outkey, buffer, len); - buffer[len * 2] = '\0'; - logger(LOG_DEBUG, _("Generated random meta key (unencrypted): %s"), - buffer); + bin2hex(key, hexkey, len); + hexkey[len * 2] = '\0'; + logger(LOG_DEBUG, _("Generated random meta key (unencrypted): %s"), hexkey); } /* Encrypt the random data @@ -164,134 +156,78 @@ bool send_metakey(connection_t *c) { with a length equal to that of the modulus of the RSA key. */ - if(!rsa_public_encrypt(len, (unsigned char *)c->outkey, (unsigned char *)buffer, &c->rsa_key)) { - logger(LOG_ERR, _("Error during encryption of meta key for %s (%s)"), - c->name, c->hostname); + if(!rsa_public_encrypt(&c->rsa, key, len, enckey)) { + logger(LOG_ERR, _("Error during encryption of meta key for %s (%s)"), c->name, c->hostname); return false; } /* Convert the encrypted random data to a hexadecimal formatted string */ - bin2hex(buffer, buffer, len); - buffer[len * 2] = '\0'; + bin2hex(enckey, hexkey, len); + hexkey[len * 2] = '\0'; /* Send the meta key */ - x = send_request(c, "%d %d %d %d %d %s", METAKEY, - c->outcipher ? c->outcipher->nid : 0, - c->outdigest ? c->outdigest->type : 0, c->outmaclength, - c->outcompression, buffer); - - /* Further outgoing requests are encrypted with the key we just generated */ - - if(c->outcipher) { - if(!EVP_EncryptInit(c->outctx, c->outcipher, - (unsigned char *)c->outkey + len - c->outcipher->key_len, - (unsigned char *)c->outkey + len - c->outcipher->key_len - - c->outcipher->iv_len)) { - logger(LOG_ERR, _("Error during initialisation of cipher for %s (%s): %s"), - c->name, c->hostname, ERR_error_string(ERR_get_error(), NULL)); - return false; - } - - c->status.encryptout = true; - } - - return x; + bool result = send_request(c, "%d %d %d %d %d %s", METAKEY, + cipher_get_nid(&c->outcipher), + digest_get_nid(&c->outdigest), c->outmaclength, + c->outcompression, hexkey); + + c->status.encryptout = true; + return result; } bool metakey_h(connection_t *c, char *request) { - char buffer[MAX_STRING_SIZE]; + char hexkey[MAX_STRING_SIZE]; int cipher, digest, maclength, compression; - int len; + size_t len = rsa_size(&myself->connection->rsa); + char enckey[len]; + char key[len]; cp(); - if(sscanf(request, "%*d %d %d %d %d " MAX_STRING, &cipher, &digest, &maclength, &compression, buffer) != 5) { - logger(LOG_ERR, _("Got bad %s from %s (%s)"), "METAKEY", c->name, - c->hostname); + if(sscanf(request, "%*d %d %d %d %d " MAX_STRING, &cipher, &digest, &maclength, &compression, hexkey) != 5) { + logger(LOG_ERR, _("Got bad %s from %s (%s)"), "METAKEY", c->name, c->hostname); return false; } - len = get_rsa_size(&myself->connection->rsa_key); - /* Check if the length of the meta key is all right */ - if(strlen(buffer) != len * 2) { + if(strlen(hexkey) != len * 2) { logger(LOG_ERR, _("Possible intruder %s (%s): %s"), c->name, c->hostname, "wrong keylength"); return false; } - /* Allocate buffers for the meta key */ - - if(!c->inkey) - c->inkey = xmalloc(len); - - if(!c->inctx) - c->inctx = xmalloc_and_zero(sizeof(*c->inctx)); - /* Convert the challenge from hexadecimal back to binary */ - hex2bin(buffer, buffer, len); + hex2bin(hexkey, enckey, len); /* Decrypt the meta key */ - if(!rsa_private_decrypt(len, (unsigned char *)buffer, (unsigned char *)c->inkey, &myself->connection->rsa_key)) { + if(!rsa_private_decrypt(&myself->connection->rsa, enckey, len, key)) { logger(LOG_ERR, _("Error during encryption of meta key for %s (%s)"), c->name, c->hostname); return false; } ifdebug(SCARY_THINGS) { - bin2hex(c->inkey, buffer, len); - buffer[len * 2] = '\0'; - logger(LOG_DEBUG, _("Received random meta key (unencrypted): %s"), buffer); + bin2hex(key, hexkey, len); + hexkey[len * 2] = '\0'; + logger(LOG_DEBUG, _("Received random meta key (unencrypted): %s"), hexkey); } - /* All incoming requests will now be encrypted. */ - /* Check and lookup cipher and digest algorithms */ - if(cipher) { - c->incipher = EVP_get_cipherbynid(cipher); - - if(!c->incipher) { - logger(LOG_ERR, _("%s (%s) uses unknown cipher!"), c->name, c->hostname); - return false; - } - - if(!EVP_DecryptInit(c->inctx, c->incipher, - (unsigned char *)c->inkey + len - c->incipher->key_len, - (unsigned char *)c->inkey + len - c->incipher->key_len - - c->incipher->iv_len)) { - logger(LOG_ERR, _("Error during initialisation of cipher from %s (%s): %s"), - c->name, c->hostname, ERR_error_string(ERR_get_error(), NULL)); - return false; - } - - c->status.decryptin = true; - } else { - c->incipher = NULL; + if(!cipher_open_by_nid(&c->incipher, cipher) || !cipher_set_key_from_rsa(&c->incipher, key, len, false)) { + logger(LOG_ERR, _("Error during initialisation of cipher from %s (%s)"), c->name, c->hostname); + return false; } - c->inmaclength = maclength; - - if(digest) { - c->indigest = EVP_get_digestbynid(digest); - - if(!c->indigest) { - logger(LOG_ERR, _("Node %s (%s) uses unknown digest!"), c->name, c->hostname); - return false; - } - - if(c->inmaclength > c->indigest->md_size || c->inmaclength < 0) { - logger(LOG_ERR, _("%s (%s) uses bogus MAC length!"), c->name, c->hostname); - return false; - } - } else { - c->indigest = NULL; + if(!digest_open_by_nid(&c->indigest, digest)) { + logger(LOG_ERR, _("Error during initialisation of digest from %s (%s)"), c->name, c->hostname); + return false; } - c->incompression = compression; + c->status.decryptin = true; c->allow_request = CHALLENGE; @@ -299,25 +235,17 @@ bool metakey_h(connection_t *c, char *request) { } bool send_challenge(connection_t *c) { - char *buffer; - int len; + size_t len = rsa_size(&c->rsa); + char buffer[len * 2 + 1]; cp(); - /* CHECKME: what is most reasonable value for len? */ - - len = get_rsa_size(&c->rsa_key); - - /* Allocate buffers for the challenge */ - - buffer = alloca(2 * len + 1); - if(!c->hischallenge) c->hischallenge = xmalloc(len); /* Copy random data to the buffer */ - RAND_pseudo_bytes((unsigned char *)c->hischallenge, len); + randomize(c->hischallenge, len); /* Convert to hex */ @@ -331,72 +259,48 @@ bool send_challenge(connection_t *c) { bool challenge_h(connection_t *c, char *request) { char buffer[MAX_STRING_SIZE]; - int len; + size_t len = rsa_size(&myself->connection->rsa); + size_t digestlen = digest_length(&c->outdigest); + char digest[digestlen]; cp(); if(sscanf(request, "%*d " MAX_STRING, buffer) != 1) { - logger(LOG_ERR, _("Got bad %s from %s (%s)"), "CHALLENGE", c->name, - c->hostname); + logger(LOG_ERR, _("Got bad %s from %s (%s)"), "CHALLENGE", c->name, c->hostname); return false; } - len = get_rsa_size(&myself->connection->rsa_key); - /* Check if the length of the challenge is all right */ if(strlen(buffer) != len * 2) { - logger(LOG_ERR, _("Possible intruder %s (%s): %s"), c->name, - c->hostname, "wrong challenge length"); + logger(LOG_ERR, _("Possible intruder %s (%s): %s"), c->name, c->hostname, "wrong challenge length"); return false; } - /* Allocate buffers for the challenge */ - - if(!c->mychallenge) - c->mychallenge = xmalloc(len); - /* Convert the challenge from hexadecimal back to binary */ - hex2bin(buffer, c->mychallenge, len); + hex2bin(buffer, buffer, len); c->allow_request = CHAL_REPLY; - /* Rest is done by send_chal_reply() */ - - return send_chal_reply(c); -} - -bool send_chal_reply(connection_t *c) { - char hash[EVP_MAX_MD_SIZE * 2 + 1]; - EVP_MD_CTX ctx; - cp(); /* Calculate the hash from the challenge we received */ - if(!EVP_DigestInit(&ctx, c->indigest) - || !EVP_DigestUpdate(&ctx, c->mychallenge, get_rsa_size(&myself->connection->rsa_key)) - || !EVP_DigestFinal(&ctx, (unsigned char *)hash, NULL)) { - logger(LOG_ERR, _("Error during calculation of response for %s (%s): %s"), - c->name, c->hostname, ERR_error_string(ERR_get_error(), NULL)); - return false; - } + digest_create(&c->indigest, buffer, len, digest); /* Convert the hash to a hexadecimal formatted string */ - bin2hex(hash, hash, c->indigest->md_size); - hash[c->indigest->md_size * 2] = '\0'; + bin2hex(digest, buffer, digestlen); + buffer[digestlen * 2] = '\0'; /* Send the reply */ - return send_request(c, "%d %s", CHAL_REPLY, hash); + return send_request(c, "%d %s", CHAL_REPLY, buffer); } bool chal_reply_h(connection_t *c, char *request) { char hishash[MAX_STRING_SIZE]; - char myhash[EVP_MAX_MD_SIZE]; - EVP_MD_CTX ctx; cp(); @@ -408,38 +312,19 @@ bool chal_reply_h(connection_t *c, char *request) { /* Check if the length of the hash is all right */ - if(strlen(hishash) != c->outdigest->md_size * 2) { - logger(LOG_ERR, _("Possible intruder %s (%s): %s"), c->name, - c->hostname, _("wrong challenge reply length")); + if(strlen(hishash) != digest_length(&c->outdigest) * 2) { + logger(LOG_ERR, _("Possible intruder %s (%s): %s"), c->name, c->hostname, _("wrong challenge reply length")); return false; } /* Convert the hash to binary format */ - hex2bin(hishash, hishash, c->outdigest->md_size); - - /* Calculate the hash from the challenge we sent */ - - if(!EVP_DigestInit(&ctx, c->outdigest) - || !EVP_DigestUpdate(&ctx, c->hischallenge, get_rsa_size(&c->rsa_key)) - || !EVP_DigestFinal(&ctx, (unsigned char *)myhash, NULL)) { - logger(LOG_ERR, _("Error during calculation of response from %s (%s): %s"), - c->name, c->hostname, ERR_error_string(ERR_get_error(), NULL)); - return false; - } - - /* Verify the incoming hash with the calculated hash */ + hex2bin(hishash, hishash, digest_length(&c->outdigest)); - if(memcmp(hishash, myhash, c->outdigest->md_size)) { - logger(LOG_ERR, _("Possible intruder %s (%s): %s"), c->name, - c->hostname, _("wrong challenge reply")); - - ifdebug(SCARY_THINGS) { - bin2hex(myhash, hishash, SHA_DIGEST_LENGTH); - hishash[SHA_DIGEST_LENGTH * 2] = '\0'; - logger(LOG_DEBUG, _("Expected challenge reply: %s"), hishash); - } + /* Verify the hash */ + if(!digest_verify(&c->outdigest, c->hischallenge, rsa_size(&c->rsa), hishash)) { + logger(LOG_ERR, _("Possible intruder %s (%s): %s"), c->name, c->hostname, _("wrong challenge reply")); return false; } @@ -447,6 +332,7 @@ bool chal_reply_h(connection_t *c, char *request) { Send an acknowledgement with the rest of the information needed. */ + free(c->hischallenge); c->allow_request = ACK; return send_ack(c); diff --git a/src/protocol_key.c b/src/protocol_key.c index 05bc97ba..66ffb115 100644 --- a/src/protocol_key.c +++ b/src/protocol_key.c @@ -22,10 +22,8 @@ #include "system.h" -#include -#include - #include "splay_tree.h" +#include "cipher.h" #include "connection.h" #include "logger.h" #include "net.h" @@ -35,7 +33,7 @@ #include "utils.h" #include "xalloc.h" -bool mykeyused = false; +static bool mykeyused = false; bool send_key_changed(connection_t *c, const node_t *n) { cp(); @@ -137,18 +135,19 @@ bool req_key_h(connection_t *c, char *request) { } bool send_ans_key(connection_t *c, const node_t *from, const node_t *to) { - char *key; + size_t keylen = cipher_keylength(&from->cipher); + char key[keylen]; cp(); - key = alloca(2 * from->keylength + 1); - bin2hex(from->key, key, from->keylength); - key[from->keylength * 2] = '\0'; + cipher_get_key(&from->cipher, key); + bin2hex(key, key, keylen); + key[keylen * 2] = '\0'; return send_request(c, "%d %s %s %s %d %d %d %d", ANS_KEY, from->name, to->name, key, - from->cipher ? from->cipher->nid : 0, - from->digest ? from->digest->type : 0, from->maclength, + cipher_get_nid(&from->cipher), + digest_get_nid(&from->digest), from->maclength, from->compression); } @@ -194,58 +193,28 @@ bool ans_key_h(connection_t *c, char *request) { return send_request(to->nexthop->connection, "%s", request); } - /* Update our copy of the origin's packet key */ - - if(from->key) - free(from->key); - - from->key = xstrdup(key); - from->keylength = strlen(key) / 2; - hex2bin(from->key, from->key, from->keylength); - from->key[from->keylength] = '\0'; - - from->status.validkey = true; - from->status.waitingforkey = false; - from->sent_seqno = 0; - /* Check and lookup cipher and digest algorithms */ - if(cipher) { - from->cipher = EVP_get_cipherbynid(cipher); - - if(!from->cipher) { - logger(LOG_ERR, _("Node %s (%s) uses unknown cipher!"), from->name, - from->hostname); - return false; - } + if(!cipher_open_by_nid(&from->cipher, cipher)) { + logger(LOG_ERR, _("Node %s (%s) uses unknown cipher!"), from->name, from->hostname); + return false; + } - if(from->keylength != from->cipher->key_len + from->cipher->iv_len) { - logger(LOG_ERR, _("Node %s (%s) uses wrong keylength!"), from->name, - from->hostname); - return false; - } - } else { - from->cipher = NULL; + if(strlen(key) / 2 != cipher_keylength(&from->cipher)) { + logger(LOG_ERR, _("Node %s (%s) uses wrong keylength!"), from->name, from->hostname); + return false; } from->maclength = maclength; - if(digest) { - from->digest = EVP_get_digestbynid(digest); - - if(!from->digest) { - logger(LOG_ERR, _("Node %s (%s) uses unknown digest!"), from->name, - from->hostname); - return false; - } + if(!digest_open_by_nid(&from->digest, digest)) { + logger(LOG_ERR, _("Node %s (%s) uses unknown digest!"), from->name, from->hostname); + return false; + } - if(from->maclength > from->digest->md_size || from->maclength < 0) { - logger(LOG_ERR, _("Node %s (%s) uses bogus MAC length!"), - from->name, from->hostname); - return false; - } - } else { - from->digest = NULL; + if(from->maclength > digest_length(&from->digest) || from->maclength < 0) { + logger(LOG_ERR, _("Node %s (%s) uses bogus MAC length!"), from->name, from->hostname); + return false; } if(compression < 0 || compression > 11) { @@ -255,12 +224,14 @@ bool ans_key_h(connection_t *c, char *request) { from->compression = compression; - if(from->cipher) - if(!EVP_EncryptInit_ex(&from->packet_ctx, from->cipher, NULL, (unsigned char *)from->key, (unsigned char *)from->key + from->cipher->key_len)) { - logger(LOG_ERR, _("Error during initialisation of key from %s (%s): %s"), - from->name, from->hostname, ERR_error_string(ERR_get_error(), NULL)); - return false; - } + /* Update our copy of the origin's packet key */ + + hex2bin(key, key, cipher_keylength(&from->cipher)); + cipher_set_key(&from->cipher, key, false); + + from->status.validkey = true; + from->status.waitingforkey = false; + from->sent_seqno = 0; if(from->options & OPTION_PMTU_DISCOVERY && !from->mtuprobes) send_mtu_probe(from); diff --git a/src/rsa.c b/src/rsa.c deleted file mode 100644 index 486f354c..00000000 --- a/src/rsa.c +++ /dev/null @@ -1,269 +0,0 @@ -/* - rsa.c -- RSA key handling - Copyright (C) 2007 Guus Sliepen - - 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., 675 Mass Ave, Cambridge, MA 02139, USA. - - $Id$ -*/ - -#include "system.h" - -#include - -#include "logger.h" -#include "rsa.h" - -// Base64 encoding/decoding tables - -static const uint8_t b64d[128] = { - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f, - 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, - 0x3a, 0x3b, 0x3c, 0x3d, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, - 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, - 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, - 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, - 0x19, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, - 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, - 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, - 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, - 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, - 0xff, 0xff -}; - -static const char b64e[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - -// PEM encoding/decoding functions - -static bool pem_decode(FILE *fp, const char *header, uint8_t *buf, size_t size, size_t *outsize) { - bool decode = false; - char line[1024]; - uint16_t word = 0; - int shift = 10; - size_t i, j = 0; - - while(!feof(fp)) { - if(!fgets(line, sizeof line, fp)) - return false; - - if(!decode && !strncmp(line, "-----BEGIN ", 11)) { - if(!strncmp(line + 11, header, strlen(header))) - decode = true; - continue; - } - - if(decode && !strncmp(line, "-----END", 8)) { - break; - } - - if(!decode) - continue; - - for(i = 0; line[i] >= ' '; i++) { - if(line[i] >= 128 || line[i] < 0 || b64d[(int)line[i]] == 0xff) - break; - word |= b64d[(int)line[i]] << shift; - shift -= 6; - if(shift <= 2) { - if(j > size) { - errno = ENOMEM; - return false; - } - - buf[j++] = word >> 8; - word <<= 8; - shift += 8; - } - } - } - - if(outsize) - *outsize = j; - return true; -} - - -// BER decoding functions - -static int ber_read_id(unsigned char **p, size_t *buflen) { - if(*buflen <= 0) - return -1; - - if((**p & 0x1f) == 0x1f) { - int id = 0; - bool more; - while(*buflen > 0) { - id <<= 7; - id |= **p & 0x7f; - more = *(*p)++ & 0x80; - (*buflen)--; - if(!more) - break; - } - return id; - } else { - (*buflen)--; - return *(*p)++ & 0x1f; - } -} - -static size_t ber_read_len(unsigned char **p, size_t *buflen) { - if(*buflen <= 0) - return -1; - - if(**p & 0x80) { - size_t result = 0; - int len = *(*p)++ & 0x7f; - (*buflen)--; - if(len > *buflen) - return 0; - - while(len--) { - result <<= 8; - result |= *(*p)++; - (*buflen)--; - } - - return result; - } else { - (*buflen)--; - return *(*p)++; - } -} - - -static bool ber_read_sequence(unsigned char **p, size_t *buflen, size_t *result) { - int tag = ber_read_id(p, buflen); - size_t len = ber_read_len(p, buflen); - - if(tag == 0x10) { - if(result) - *result = len; - return true; - } else { - return false; - } -} - -static bool ber_read_mpi(unsigned char **p, size_t *buflen, gcry_mpi_t *mpi) { - int tag = ber_read_id(p, buflen); - size_t len = ber_read_len(p, buflen); - gcry_error_t err = 0; - - if(tag != 0x02 || len > *buflen) - return false; - - if(mpi) - err = gcry_mpi_scan(mpi, GCRYMPI_FMT_USG, *p, len, NULL); - - *p += len; - *buflen -= len; - - return mpi ? !err : true; -} - -// Read PEM RSA keys - -bool read_pem_rsa_public_key(FILE *fp, rsa_key_t *key) { - uint8_t derbuf[8096], *derp = derbuf; - size_t derlen; - - if(!pem_decode(fp, "RSA PUBLIC KEY", derbuf, sizeof derbuf, &derlen)) { - logger(LOG_ERR, _("Unable to read RSA public key: %s"), strerror(errno)); - return NULL; - } - - if(!ber_read_sequence(&derp, &derlen, NULL) - || !ber_read_mpi(&derp, &derlen, &key->n) - || !ber_read_mpi(&derp, &derlen, &key->e) - || derlen) { - logger(LOG_ERR, _("Error while decoding RSA public key")); - return NULL; - } - - return true; -} - -bool read_pem_rsa_private_key(FILE *fp, rsa_key_t *key) { - uint8_t derbuf[8096], *derp = derbuf; - size_t derlen; - - if(!pem_decode(fp, "RSA PRIVATE KEY", derbuf, sizeof derbuf, &derlen)) { - logger(LOG_ERR, _("Unable to read RSA private key: %s"), strerror(errno)); - return NULL; - } - - if(!ber_read_sequence(&derp, &derlen, NULL) - || !ber_read_mpi(&derp, &derlen, NULL) - || !ber_read_mpi(&derp, &derlen, &key->n) - || !ber_read_mpi(&derp, &derlen, &key->e) - || !ber_read_mpi(&derp, &derlen, &key->d) - || !ber_read_mpi(&derp, &derlen, NULL) // p - || !ber_read_mpi(&derp, &derlen, NULL) // q - || !ber_read_mpi(&derp, &derlen, NULL) - || !ber_read_mpi(&derp, &derlen, NULL) - || !ber_read_mpi(&derp, &derlen, NULL) // u - || derlen) { - logger(LOG_ERR, _("Error while decoding RSA private key")); - return NULL; - } - - return true; -} - -unsigned int get_rsa_size(rsa_key_t *key) { - return (gcry_mpi_get_nbits(key->n) + 7) / 8; -} - -/* Well, libgcrypt has functions to handle RSA keys, but they suck. - * So we just use libgcrypt's mpi functions, and do the math ourselves. - */ - -// TODO: get rid of this macro, properly clean up gcry_ structures after use -#define check(foo) { gcry_error_t err = (foo); if(err) {logger(LOG_ERR, "gcrypt error %s/%s at %s:%d\n", gcry_strsource(err), gcry_strerror(err), __FILE__, __LINE__); return false; }} - -bool rsa_public_encrypt(size_t len, void *in, void *out, rsa_key_t *key) { - gcry_mpi_t inmpi; - check(gcry_mpi_scan(&inmpi, GCRYMPI_FMT_USG, in, len, NULL)); - - gcry_mpi_t outmpi = gcry_mpi_new(len * 8); - gcry_mpi_powm(outmpi, inmpi, key->e, key->n); - - check(gcry_mpi_print(GCRYMPI_FMT_USG, out,len, NULL, outmpi)); - - return true; -} - -bool rsa_private_decrypt(size_t len, void *in, void *out, rsa_key_t *key) { - gcry_mpi_t inmpi; - check(gcry_mpi_scan(&inmpi, GCRYMPI_FMT_USG, in, len, NULL)); - - gcry_mpi_t outmpi = gcry_mpi_new(len * 8); - gcry_mpi_powm(outmpi, inmpi, key->d, key->n); - - check(gcry_mpi_print(GCRYMPI_FMT_USG, out,len, NULL, outmpi)); - - return true; -} diff --git a/src/rsa.h b/src/rsa.h deleted file mode 100644 index bcb795e0..00000000 --- a/src/rsa.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - rsa.h -- RSA key handling - Copyright (C) 2007 Guus Sliepen - - 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., 675 Mass Ave, Cambridge, MA 02139, USA. - - $Id$ -*/ - -#ifndef __TINC_RSA_H__ -#define __TINC_RSA_H__ - -#include - -typedef struct rsa_key_t { - gcry_mpi_t n; - gcry_mpi_t e; - gcry_mpi_t d; -} rsa_key_t; - -extern bool read_pem_rsa_public_key(FILE *fp, struct rsa_key_t *key); -extern bool read_pem_rsa_private_key(FILE *fp, struct rsa_key_t *key); -extern unsigned int get_rsa_size(struct rsa_key_t *key); -extern bool rsa_public_encrypt(size_t len, void *in, void *out, struct rsa_key_t *key); -extern bool rsa_private_decrypt(size_t len, void *in, void *out, struct rsa_key_t *key); - - -#endif diff --git a/src/tincd.c b/src/tincd.c index 95c45d3b..d5942ce0 100644 --- a/src/tincd.c +++ b/src/tincd.c @@ -31,18 +31,13 @@ #include #endif -#include -#include -#include -#include -#include - #include LZO1X_H #include #include "conf.h" #include "control.h" +#include "crypto.h" #include "device.h" #include "logger.h" #include "net.h" @@ -294,12 +289,7 @@ int main(int argc, char **argv) /* Slllluuuuuuurrrrp! */ srand(time(NULL)); - RAND_load_file("/dev/urandom", 1024); - - ENGINE_load_builtin_engines(); - ENGINE_register_all_complete(); - - OpenSSL_add_all_algorithms(); + crypto_init(); if(!read_server_config()) return 1; @@ -352,7 +342,7 @@ end: exit_control(); #endif - EVP_cleanup(); - + crypto_exit(); + return status; }