mirror of
https://github.com/OpenVPN/openvpn3.git
synced 2024-09-20 12:12:15 +02:00
44ee74f374
Implemented LZO_STUB compressor. Added methods to generate options and peer info strings.
262 lines
5.9 KiB
C++
262 lines
5.9 KiB
C++
#ifndef OPENVPN_CRYPTO_CIPHER
|
|
#define OPENVPN_CRYPTO_CIPHER
|
|
|
|
#include <boost/noncopyable.hpp>
|
|
|
|
#include <openvpn/gencrypto/evpcipher.hpp>
|
|
|
|
#include <openvpn/common/types.hpp>
|
|
#include <openvpn/common/exception.hpp>
|
|
#include <openvpn/crypto/static_key.hpp>
|
|
|
|
namespace openvpn {
|
|
class Cipher
|
|
{
|
|
friend class CipherContext;
|
|
|
|
public:
|
|
OPENVPN_SIMPLE_EXCEPTION(cipher_not_found);
|
|
OPENVPN_SIMPLE_EXCEPTION(cipher_undefined);
|
|
|
|
Cipher() : cipher_(NULL) {}
|
|
|
|
Cipher(const char *name)
|
|
{
|
|
cipher_ = EVP_get_cipherbyname(name);
|
|
if (!cipher_)
|
|
throw cipher_not_found();
|
|
}
|
|
|
|
const char *name() const
|
|
{
|
|
if (!cipher_)
|
|
throw cipher_undefined();
|
|
return EVP_CIPHER_name (cipher_);
|
|
}
|
|
|
|
size_t key_length() const
|
|
{
|
|
if (!cipher_)
|
|
throw cipher_undefined();
|
|
return EVP_CIPHER_key_length (cipher_);
|
|
}
|
|
|
|
size_t key_length_in_bits() const
|
|
{
|
|
return key_length() * 8;
|
|
}
|
|
|
|
size_t iv_length() const
|
|
{
|
|
if (!cipher_)
|
|
throw cipher_undefined();
|
|
return EVP_CIPHER_iv_length (cipher_);
|
|
}
|
|
|
|
size_t block_size() const
|
|
{
|
|
if (!cipher_)
|
|
throw cipher_undefined();
|
|
return EVP_CIPHER_block_size (cipher_);
|
|
}
|
|
|
|
bool defined() const { return cipher_ != NULL; }
|
|
|
|
private:
|
|
const EVP_CIPHER *get() const { return cipher_; }
|
|
|
|
const EVP_CIPHER *cipher_;
|
|
};
|
|
|
|
class CipherContext
|
|
{
|
|
public:
|
|
// mode parameter for constructor
|
|
enum {
|
|
ENCRYPT = 1,
|
|
DECRYPT = 0
|
|
};
|
|
|
|
// OpenSSL cipher constants
|
|
enum {
|
|
MAX_IV_SIZE = EVP_MAX_IV_LENGTH,
|
|
CIPH_CBC_MODE = EVP_CIPH_CBC_MODE
|
|
};
|
|
|
|
OPENVPN_SIMPLE_EXCEPTION(cipher_init);
|
|
OPENVPN_SIMPLE_EXCEPTION(cipher_mode_error);
|
|
OPENVPN_SIMPLE_EXCEPTION(cipher_uninitialized);
|
|
OPENVPN_SIMPLE_EXCEPTION(cipher_init_insufficient_key_material);
|
|
OPENVPN_SIMPLE_EXCEPTION(cipher_internal_error);
|
|
OPENVPN_SIMPLE_EXCEPTION(cipher_output_buffer);
|
|
|
|
class EVP_CIPHER_CTX_wrapper : boost::noncopyable
|
|
{
|
|
public:
|
|
EVP_CIPHER_CTX_wrapper()
|
|
: initialized(false)
|
|
{
|
|
}
|
|
|
|
~EVP_CIPHER_CTX_wrapper() { erase() ; }
|
|
|
|
void init()
|
|
{
|
|
erase();
|
|
EVP_CIPHER_CTX_init (&ctx);
|
|
initialized = true;
|
|
}
|
|
|
|
void erase()
|
|
{
|
|
if (initialized)
|
|
{
|
|
EVP_CIPHER_CTX_cleanup(&ctx);
|
|
initialized = false;
|
|
}
|
|
}
|
|
|
|
EVP_CIPHER_CTX* operator()() {
|
|
if (!initialized)
|
|
throw cipher_uninitialized();
|
|
return &ctx;
|
|
}
|
|
|
|
const EVP_CIPHER_CTX* operator()() const {
|
|
if (!initialized)
|
|
throw cipher_uninitialized();
|
|
return &ctx;
|
|
}
|
|
|
|
bool is_initialized() const { return initialized; }
|
|
|
|
private:
|
|
EVP_CIPHER_CTX ctx;
|
|
bool initialized;
|
|
};
|
|
|
|
public:
|
|
CipherContext() : mode_(-1) {}
|
|
|
|
CipherContext(const Cipher& cipher, const StaticKey& key, const int mode)
|
|
{
|
|
init(cipher, key, mode);
|
|
}
|
|
|
|
CipherContext(const CipherContext& ref)
|
|
{
|
|
init(ref.cipher_, ref.key_, ref.mode_);
|
|
}
|
|
|
|
bool defined() const { return ctx.is_initialized(); }
|
|
|
|
const Cipher& cipher() const { return cipher_; }
|
|
|
|
void operator=(const CipherContext& ref)
|
|
{
|
|
if (this != &ref)
|
|
init(ref.cipher_, ref.key_, ref.mode_);
|
|
}
|
|
|
|
// size of iv buffer to pass to encrypt_decrypt
|
|
size_t iv_size() const
|
|
{
|
|
return EVP_CIPHER_CTX_iv_length (ctx());
|
|
}
|
|
|
|
// cipher mode (such as CBC, etc.)
|
|
int cipher_mode() const
|
|
{
|
|
return EVP_CIPHER_CTX_mode (ctx());
|
|
}
|
|
|
|
// size of out buffer to pass to encrypt_decrypt
|
|
size_t output_size(const size_t in_size) const
|
|
{
|
|
return in_size + EVP_CIPHER_CTX_block_size(ctx());
|
|
}
|
|
|
|
void init(const Cipher& cipher, const StaticKey& key, const int mode)
|
|
{
|
|
cipher_ = cipher;
|
|
key_ = key;
|
|
mode_ = mode;
|
|
ctx.erase();
|
|
|
|
if (cipher.defined())
|
|
{
|
|
// check that key is large enough
|
|
if (key_.size() < cipher_.key_length())
|
|
throw cipher_init_insufficient_key_material();
|
|
|
|
// check that mode is valid
|
|
if (!(mode_ == ENCRYPT || mode_ == DECRYPT))
|
|
throw cipher_mode_error();
|
|
|
|
// initialize cipher context with cipher type, key, and encrypt/decrypt mode
|
|
ctx.init();
|
|
try
|
|
{
|
|
if (!EVP_CipherInit_ex (ctx(), cipher_.get(), NULL, key_.data(), NULL, mode_))
|
|
throw cipher_init();
|
|
|
|
// This could occur if we were built with a different version of
|
|
// OpenSSL headers than the underlying library.
|
|
if (iv_size() > MAX_IV_SIZE)
|
|
throw cipher_internal_error();
|
|
}
|
|
catch (...)
|
|
{
|
|
ctx.erase();
|
|
throw;
|
|
}
|
|
}
|
|
}
|
|
|
|
size_t encrypt(const unsigned char *iv,
|
|
unsigned char *out, const size_t out_size,
|
|
const unsigned char *in, const size_t in_size)
|
|
{
|
|
if (mode_ != ENCRYPT)
|
|
throw cipher_mode_error();
|
|
return encrypt_decrypt(iv, out, out_size, in, in_size);
|
|
}
|
|
|
|
size_t decrypt(const unsigned char *iv,
|
|
unsigned char *out, const size_t out_size,
|
|
const unsigned char *in, const size_t in_size)
|
|
{
|
|
if (mode_ != DECRYPT)
|
|
throw cipher_mode_error();
|
|
return encrypt_decrypt(iv, out, out_size, in, in_size);
|
|
}
|
|
|
|
size_t encrypt_decrypt(const unsigned char *iv,
|
|
unsigned char *out, const size_t out_size,
|
|
const unsigned char *in, const size_t in_size)
|
|
{
|
|
EVP_CIPHER_CTX *c = ctx();
|
|
if (out_size < output_size(in_size))
|
|
throw cipher_output_buffer();
|
|
if (!EVP_CipherInit_ex (c, NULL, NULL, NULL, iv, -1))
|
|
throw cipher_init();
|
|
int outlen = out_size; // NOTE: minor change to OpenSSL semantics, pass size of output buffer
|
|
if (!EVP_CipherUpdate (c, out, &outlen, in, int(in_size)))
|
|
return 0;
|
|
int tmplen = out_size - outlen; // NOTE: minor change to OpenSSL semantics, pass size of output buffer
|
|
if (!EVP_CipherFinal_ex (c, out + outlen, &tmplen))
|
|
return 0;
|
|
return outlen + tmplen;
|
|
}
|
|
|
|
private:
|
|
Cipher cipher_;
|
|
StaticKey key_;
|
|
int mode_;
|
|
EVP_CIPHER_CTX_wrapper ctx;
|
|
};
|
|
|
|
} // namespace openvpn
|
|
|
|
#endif // OPENVPN_CRYPTO_CIPHER
|