Encryption and authentication of the meta connection is spread out over meta.c and protocol_auth.c. The new protocol was added there as well, leading to spaghetti code. To improve things, the new protocol will now be implemented in sptps.[ch]. The goal is to have a very simplified version of TLS. There is a record layer, and there are only two record types: application data and handshake messages. The handshake message contains a random nonce, an ephemeral ECDH public key, and an ECDSA signature over the former. After the ECDH public keys are exchanged, a shared secret is calculated, and a TLS style PRF is used to generate the key material for the cipher and HMAC algorithm, and further communication is encrypted and authenticated. A lot of the simplicity comes from the fact that both sides must have each other's public keys in advance, and there are no options to choose. There will be one fixed cipher suite, and both peers always authenticate each other. (Inspiration taken from Ian Grigg's hypotheses[0].) There might be some compromise in the future, to enable or disable encryption, authentication and compression, but there will be no choice of algorithms. This will allow SPTPS to be built with a few embedded crypto algorithms instead of linking with huge crypto libraries. The API is also kept simple. There is a start and a stop function. All data necessary to make the connection work is passed in the start function. Instead having both send- and receive-record functions, there is a send-record function and a receive-data function. The latter will pass protocol data received from the peer to the SPTPS implementation, which will in turn call a receive-record callback function when necessary. This hides all the handshaking from the application, and is completely independent from any event loop or socket characteristics. [0] http://iang.org/ssl/hn_hypotheses_in_secure_protocol_design.html
146 lines
4.4 KiB
C
146 lines
4.4 KiB
C
/*
|
|
cipher.c -- Symmetric block cipher handling
|
|
Copyright (C) 2007 Guus Sliepen <guus@tinc-vpn.org>
|
|
|
|
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.,
|
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
|
|
#include "system.h"
|
|
|
|
#include <openssl/rand.h>
|
|
#include <openssl/err.h>
|
|
|
|
#include "cipher.h"
|
|
#include "logger.h"
|
|
#include "xalloc.h"
|
|
|
|
static bool cipher_open(cipher_t *cipher) {
|
|
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_ERR, "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_ERR, "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);
|
|
}
|
|
|
|
size_t cipher_keylength(const cipher_t *cipher) {
|
|
return cipher->cipher->key_len + cipher->cipher->iv_len;
|
|
}
|
|
|
|
bool cipher_set_key(cipher_t *cipher, void *key, bool encrypt) {
|
|
bool result;
|
|
|
|
if(encrypt)
|
|
result = EVP_EncryptInit_ex(&cipher->ctx, cipher->cipher, NULL, (unsigned char *)key, (unsigned char *)key + cipher->cipher->key_len);
|
|
else
|
|
result = EVP_DecryptInit_ex(&cipher->ctx, cipher->cipher, NULL, (unsigned char *)key, (unsigned char *)key + cipher->cipher->key_len);
|
|
|
|
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) {
|
|
bool result;
|
|
|
|
if(encrypt)
|
|
result = EVP_EncryptInit_ex(&cipher->ctx, cipher->cipher, NULL, (unsigned char *)key + len - cipher->cipher->key_len, (unsigned char *)key + len - cipher->cipher->iv_len - cipher->cipher->key_len);
|
|
else
|
|
result = EVP_DecryptInit_ex(&cipher->ctx, cipher->cipher, NULL, (unsigned char *)key + len - cipher->cipher->key_len, (unsigned char *)key + len - cipher->cipher->iv_len - cipher->cipher->key_len);
|
|
|
|
if(result)
|
|
return true;
|
|
|
|
logger(LOG_ERR, "Error while setting key: %s", ERR_error_string(ERR_get_error(), NULL));
|
|
return false;
|
|
}
|
|
|
|
bool cipher_encrypt(cipher_t *cipher, const void *indata, size_t inlen, void *outdata, size_t *outlen, bool oneshot) {
|
|
if(oneshot) {
|
|
int len, pad;
|
|
if(EVP_EncryptInit_ex(&cipher->ctx, NULL, NULL, NULL, NULL)
|
|
&& EVP_EncryptUpdate(&cipher->ctx, (unsigned char *)outdata, &len, indata, inlen)
|
|
&& EVP_EncryptFinal(&cipher->ctx, (unsigned char *)outdata + len, &pad)) {
|
|
if(outlen) *outlen = len + pad;
|
|
return true;
|
|
}
|
|
} else {
|
|
int len;
|
|
if(EVP_EncryptUpdate(&cipher->ctx, outdata, &len, indata, inlen)) {
|
|
if(outlen) *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, const void *indata, size_t inlen, void *outdata, size_t *outlen, bool oneshot) {
|
|
if(oneshot) {
|
|
int len, pad;
|
|
if(EVP_DecryptInit_ex(&cipher->ctx, NULL, NULL, NULL, NULL)
|
|
&& EVP_DecryptUpdate(&cipher->ctx, (unsigned char *)outdata, &len, indata, inlen)
|
|
&& EVP_DecryptFinal(&cipher->ctx, (unsigned char *)outdata + len, &pad)) {
|
|
if(outlen) *outlen = len + pad;
|
|
return true;
|
|
}
|
|
} else {
|
|
int len;
|
|
if(EVP_EncryptUpdate(&cipher->ctx, outdata, &len, indata, inlen)) {
|
|
if(outlen) *outlen = len;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
logger(LOG_ERR, "Error while decrypting: %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;
|
|
}
|