0
0
mirror of https://github.com/OpenVPN/openvpn3.git synced 2024-09-20 04:02:15 +02:00

Added CryptoContextBase abstraction to allow use of new

data channel modes and non-CBC ciphers such as AES-GCM.
This commit is contained in:
James Yonan 2014-10-08 13:43:40 -06:00
parent 1e8d9d55ee
commit ab7dfe6f05
6 changed files with 282 additions and 72 deletions

View File

@ -36,6 +36,7 @@
#include <openvpn/common/options.hpp>
#include <openvpn/frame/frame_init.hpp>
#include <openvpn/pki/epkibase.hpp>
#include <openvpn/crypto/crypto_chm.hpp>
#include <openvpn/transport/socket_protect.hpp>
#include <openvpn/transport/reconnect_notify.hpp>
@ -171,6 +172,7 @@ namespace openvpn {
cp->load(opt, *proto_context_options, config.default_key_direction);
cp->set_xmit_creds(!autologin || pcc.hasEmbeddedPassword());
cp->ssl_ctx.reset(new SSLLib::SSLAPI(cc));
cp->cc_factory.reset(new CryptoContextCHMFactory<SSLLib::RandomAPI, SSLLib::CryptoAPI>());
cp->gui_version = config.gui_version;
cp->frame = frame;
cp->now = &now_;

View File

@ -1,41 +0,0 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2013-2014 OpenVPN Technologies, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
// A general purpose container for OpenVPN protocol encrypt and decrypt objects.
#ifndef OPENVPN_CRYPTO_CRYPTO_H
#define OPENVPN_CRYPTO_CRYPTO_H
#include <openvpn/crypto/encrypt.hpp>
#include <openvpn/crypto/decrypt.hpp>
namespace openvpn {
template <typename RAND_API, typename CRYPTO_API>
struct CryptoContext
{
Encrypt<RAND_API, CRYPTO_API> encrypt;
Decrypt<CRYPTO_API> decrypt;
};
} // namespace openvpn
#endif // OPENVPN_CRYPTO_CRYPTO_H

View File

@ -0,0 +1,118 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2013-2014 OpenVPN Technologies, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
// OpenVPN CBC/HMAC data channel
#ifndef OPENVPN_CRYPTO_CRYPTO_CHM_H
#define OPENVPN_CRYPTO_CRYPTO_CHM_H
#include <openvpn/crypto/encrypt.hpp>
#include <openvpn/crypto/decrypt.hpp>
#include <openvpn/crypto/cryptobase.hpp>
namespace openvpn {
template <typename RAND_API, typename CRYPTO_API>
class CryptoContextCHM : public CryptoContextBase<RAND_API, CRYPTO_API>
{
typedef CryptoContextBase<RAND_API, CRYPTO_API> Base;
virtual void init_frame(const Frame::Ptr& frame)
{
encrypt_.frame = frame;
decrypt_.frame = frame;
}
virtual void init_prng(const typename PRNG<RAND_API, CRYPTO_API>::Ptr& prng)
{
encrypt_.prng = prng;
}
virtual void init_encrypt_cipher(const typename CRYPTO_API::Cipher& cipher,
const StaticKey& key, const int mode)
{
encrypt_.cipher.init(cipher, key, mode);
}
virtual void init_encrypt_hmac(const typename CRYPTO_API::Digest& digest,
const StaticKey& key)
{
encrypt_.hmac.init(digest, key);
}
virtual void init_encrypt_pid_send(const int form)
{
encrypt_.pid_send.init(form);
}
virtual void init_decrypt_cipher(const typename CRYPTO_API::Cipher& cipher,
const StaticKey& key, const int mode)
{
decrypt_.cipher.init(cipher, key, mode);
}
virtual void init_decrypt_hmac(const typename CRYPTO_API::Digest& digest,
const StaticKey& key)
{
decrypt_.hmac.init(digest, key);
}
virtual void init_decrypt_pid_recv(const int mode, const int form,
const int seq_backtrack, const int time_backtrack,
const char *name, const int unit,
const SessionStats::Ptr& stats_arg)
{
decrypt_.pid_recv.init(mode, form, seq_backtrack, time_backtrack, name, unit, stats_arg);
}
/* returns true if packet ID is close to wrapping */
virtual bool encrypt(BufferAllocated& buf, const PacketID::time_t now)
{
encrypt_.encrypt(buf, now);
return encrypt_.pid_send.wrap_warning();
}
virtual Error::Type decrypt(BufferAllocated& buf, const PacketID::time_t now)
{
return decrypt_.decrypt(buf, now);
}
virtual void rekey(const typename Base::RekeyType type)
{
}
private:
Encrypt<RAND_API, CRYPTO_API> encrypt_;
Decrypt<CRYPTO_API> decrypt_;
};
template <typename RAND_API, typename CRYPTO_API>
class CryptoContextCHMFactory : public CryptoContextFactory<RAND_API, CRYPTO_API>
{
virtual typename CryptoContextBase<RAND_API, CRYPTO_API>::Ptr new_obj(const unsigned int key_id)
{
return new CryptoContextCHM<RAND_API, CRYPTO_API>();
}
};
}
#endif

View File

@ -0,0 +1,98 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2013-2014 OpenVPN Technologies, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
// Base class for OpenVPN data channel encryption/decryption
#ifndef OPENVPN_CRYPTO_CRYPTOBASE_H
#define OPENVPN_CRYPTO_CRYPTOBASE_H
#include <openvpn/buffer/buffer.hpp>
#include <openvpn/error/error.hpp>
#include <openvpn/common/rc.hpp>
#include <openvpn/frame/frame.hpp>
#include <openvpn/random/prng.hpp>
#include <openvpn/crypto/static_key.hpp>
#include <openvpn/crypto/packet_id.hpp>
namespace openvpn {
template <typename RAND_API, typename CRYPTO_API>
class CryptoContextBase : public RC<thread_unsafe_refcount>
{
public:
typedef boost::intrusive_ptr<CryptoContextBase> Ptr;
// Encrypt/Decrypt
// returns true if packet ID is close to wrapping
virtual bool encrypt(BufferAllocated& buf, const PacketID::time_t now) = 0;
virtual Error::Type decrypt(BufferAllocated& buf, const PacketID::time_t now) = 0;
// Rekeying
enum RekeyType {
ACTIVATE_PRIMARY,
DEACTIVATE_SECONDARY,
PROMOTE_SECONDARY_TO_PRIMARY,
DEACTIVATE_ALL,
};
virtual void rekey(const RekeyType type) = 0;
// Initialization
virtual void init_frame(const Frame::Ptr& frame) = 0;
virtual void init_prng(const typename PRNG<RAND_API, CRYPTO_API>::Ptr& prng) = 0;
virtual void init_encrypt_cipher(const typename CRYPTO_API::Cipher& cipher,
const StaticKey& key, const int mode) = 0;
virtual void init_encrypt_hmac(const typename CRYPTO_API::Digest& digest,
const StaticKey& key) = 0;
virtual void init_encrypt_pid_send(const int form) = 0;
virtual void init_decrypt_cipher(const typename CRYPTO_API::Cipher& cipher,
const StaticKey& key, const int mode) = 0;
virtual void init_decrypt_hmac(const typename CRYPTO_API::Digest& digest,
const StaticKey& key) = 0;
virtual void init_decrypt_pid_recv(const int mode, const int form,
const int seq_backtrack, const int time_backtrack,
const char *name, const int unit,
const SessionStats::Ptr& stats_arg) = 0;
};
// Factory for CryptoContextBase objects
template <typename RAND_API, typename CRYPTO_API>
class CryptoContextFactory : public RC<thread_unsafe_refcount>
{
public:
typedef boost::intrusive_ptr<CryptoContextFactory> Ptr;
virtual typename CryptoContextBase<RAND_API, CRYPTO_API>::Ptr new_obj(const unsigned int key_id) = 0;
};
}
#endif

View File

@ -46,7 +46,9 @@
#include <openvpn/time/time.hpp>
#include <openvpn/frame/frame.hpp>
#include <openvpn/random/prng.hpp>
#include <openvpn/crypto/crypto.hpp>
#include <openvpn/crypto/cryptobase.hpp>
#include <openvpn/crypto/cipher.hpp>
#include <openvpn/crypto/hmac.hpp>
#include <openvpn/crypto/packet_id.hpp>
#include <openvpn/crypto/static_key.hpp>
#include <openvpn/log/sessionstats.hpp>
@ -201,6 +203,9 @@ namespace openvpn {
public:
typedef SSL_API SSLContext;
typedef CryptoContextBase<RAND_API, CRYPTO_API> CC;
typedef CryptoContextFactory<RAND_API, CRYPTO_API> CCFactory;
OPENVPN_SIMPLE_EXCEPTION(peer_psid_undef);
OPENVPN_SIMPLE_EXCEPTION(bad_auth_prefix);
OPENVPN_EXCEPTION(process_server_push_error);
@ -231,6 +236,9 @@ namespace openvpn {
// master SSL context
typename SSLContext::Ptr ssl_ctx;
// master crypto context factory
typename CCFactory::Ptr cc_factory;
// master Frame object
Frame::Ptr frame;
@ -945,12 +953,16 @@ namespace openvpn {
// get key_id from parent
key_id_ = proto.next_key_id();
// build crypto context for data channel encryption/decryption
crypto = proto.config->cc_factory->new_obj(key_id_);
// remember when we were constructed
construct_time = *now;
// set must-negotiate-by time
set_event(KEV_NONE, KEV_NEGOTIATE, construct_time + p.config->handshake_window);
// build compressor object
construct_compressor();
}
@ -1033,13 +1045,13 @@ namespace openvpn {
compress->compress(buf, true);
// encrypt packet
crypto.encrypt.encrypt(buf, now->seconds_since_epoch());
const bool pid_wrap = crypto->encrypt(buf, now->seconds_since_epoch());
// prepend op
buf.push_front(op_compose(DATA_V1, key_id_));
// check for rare situation where packet ID is near overflow
test_pid_wrap();
test_pid_wrap(pid_wrap);
}
else
buf.reset_size(); // no crypto context available
@ -1055,7 +1067,7 @@ namespace openvpn {
buf.advance(1);
// decrypt packet
const Error::Type err = crypto.decrypt.decrypt(buf, now->seconds_since_epoch());
const Error::Type err = crypto->decrypt(buf, now->seconds_since_epoch());
if (err)
{
proto.stats->error(err);
@ -1113,6 +1125,13 @@ namespace openvpn {
bool is_dirty() const { return dirty; }
// notification from parent of rekey operation
void rekey(const typename CC::RekeyType type)
{
if (crypto)
crypto->rekey(type);
}
// time that our state transitioned to ACTIVE
Time reached_active() const { return reached_active_time_; }
@ -1145,7 +1164,7 @@ namespace openvpn {
// process packet for transmission
compress->compress(*pkt.buf, false); // set compress hint to "no"
crypto.encrypt.encrypt(*pkt.buf, now->seconds_since_epoch());
crypto->encrypt(*pkt.buf, now->seconds_since_epoch());
pkt.buf->push_front(op_compose(DATA_V1, key_id_));
// send it
@ -1334,9 +1353,9 @@ namespace openvpn {
// is getting close to wrapping around. If it wraps back to 0 without
// a renegotiation, it would cause the relay protection logic to wrongly
// think that all further packets are replays.
void test_pid_wrap()
void test_pid_wrap(const bool pid_wrap)
{
if (!handled_pid_wrap && crypto.encrypt.pid_send.wrap_warning())
if (pid_wrap && !handled_pid_wrap)
{
trigger_renegotiation();
handled_pid_wrap = true;
@ -1551,32 +1570,33 @@ namespace openvpn {
const Config& c = *proto.config;
const unsigned int key_dir = proto.is_server() ? OpenVPNStaticKey::INVERSE : OpenVPNStaticKey::NORMAL;
// initialize CryptoContext
crypto->init_frame(c.frame);
crypto->init_prng(c.prng);
// initialize CryptoContext encrypt
crypto.encrypt.frame = c.frame;
if (c.cipher.defined())
crypto.encrypt.cipher.init(c.cipher,
key.slice(OpenVPNStaticKey::CIPHER | OpenVPNStaticKey::ENCRYPT | key_dir),
CRYPTO_API::CipherContext::ENCRYPT);
crypto->init_encrypt_cipher(c.cipher,
key.slice(OpenVPNStaticKey::CIPHER | OpenVPNStaticKey::ENCRYPT | key_dir),
CRYPTO_API::CipherContext::ENCRYPT);
if (c.digest.defined())
crypto.encrypt.hmac.init(c.digest,
key.slice(OpenVPNStaticKey::HMAC | OpenVPNStaticKey::ENCRYPT | key_dir));
crypto.encrypt.pid_send.init(PacketID::SHORT_FORM);
crypto.encrypt.prng = c.prng;
crypto->init_encrypt_hmac(c.digest,
key.slice(OpenVPNStaticKey::HMAC | OpenVPNStaticKey::ENCRYPT | key_dir));
crypto->init_encrypt_pid_send(PacketID::SHORT_FORM);
// initialize CryptoContext decrypt
crypto.decrypt.frame = c.frame;
if (c.cipher.defined())
crypto.decrypt.cipher.init(c.cipher,
key.slice(OpenVPNStaticKey::CIPHER | OpenVPNStaticKey::DECRYPT | key_dir),
CRYPTO_API::CipherContext::DECRYPT);
crypto->init_decrypt_cipher(c.cipher,
key.slice(OpenVPNStaticKey::CIPHER | OpenVPNStaticKey::DECRYPT | key_dir),
CRYPTO_API::CipherContext::DECRYPT);
if (c.digest.defined())
crypto.decrypt.hmac.init(c.digest,
key.slice(OpenVPNStaticKey::HMAC | OpenVPNStaticKey::DECRYPT | key_dir));
crypto.decrypt.pid_recv.init(c.pid_mode,
PacketID::SHORT_FORM,
c.pid_seq_backtrack, c.pid_time_backtrack,
"DATA", int(key_id_),
proto.stats);
crypto->init_decrypt_hmac(c.digest,
key.slice(OpenVPNStaticKey::HMAC | OpenVPNStaticKey::DECRYPT | key_dir));
crypto->init_decrypt_pid_recv(c.pid_mode,
PacketID::SHORT_FORM,
c.pid_seq_backtrack, c.pid_time_backtrack,
"DATA", int(key_id_),
proto.stats);
}
// generate message head
@ -1844,7 +1864,7 @@ namespace openvpn {
EventType next_event;
Compress::Ptr compress;
std::deque<BufferPtr> app_pre_write_queue;
CryptoContext<RAND_API, CRYPTO_API> crypto;
typename CC::Ptr crypto;
TLSPRF<CRYPTO_API> tlsprf_self;
TLSPRF<CRYPTO_API> tlsprf_peer;
};
@ -1934,8 +1954,7 @@ namespace openvpn {
fast_transition = false;
// clear key contexts
primary.reset();
secondary.reset();
reset_all();
// start with key ID 0
upcoming_key_id = 0;
@ -1983,8 +2002,7 @@ namespace openvpn {
// object destruction is not immediately scheduled.
void pre_destroy()
{
primary.reset();
secondary.reset();
reset_all();
}
virtual ~ProtoContext() {}
@ -2234,6 +2252,14 @@ namespace openvpn {
SessionStats& stat() const { return *stats; }
private:
void reset_all()
{
if (primary)
primary->rekey(CC::DEACTIVATE_ALL);
primary.reset();
secondary.reset();
}
virtual void control_net_send(const Buffer& net_buf) = 0;
virtual void control_recv(BufferPtr& app_bp) = 0;
@ -2379,6 +2405,7 @@ namespace openvpn {
void promote_secondary_to_primary()
{
primary.swap(secondary);
primary->rekey(CC::PROMOTE_SECONDARY_TO_PRIMARY);
secondary->prepare_expire();
}
@ -2392,6 +2419,7 @@ namespace openvpn {
{
case KeyContext::KEV_ACTIVE:
OPENVPN_LOG_PROTO_VERBOSE("*** SESSION_ACTIVE");
primary->rekey(CC::ACTIVATE_PRIMARY);
active();
break;
case KeyContext::KEV_RENEGOTIATE:
@ -2432,6 +2460,7 @@ namespace openvpn {
promote_secondary_to_primary();
break;
case KeyContext::KEV_EXPIRE:
secondary->rekey(CC::DEACTIVATE_SECONDARY);
secondary.reset();
break;
case KeyContext::KEV_NEGOTIATE_FAILED:

View File

@ -96,6 +96,8 @@
#include <openvpn/ssl/proto.hpp>
#include <openvpn/init/initprocess.hpp>
#include <openvpn/crypto/crypto_chm.hpp>
#if !(defined(USE_OPENSSL) || defined(USE_POLARSSL) || defined(USE_APPLE_SSL))
#error Must define one or more of USE_OPENSSL, USE_POLARSSL, USE_APPLE_SSL.
#endif
@ -725,6 +727,7 @@ int test(const int thread_num)
typedef ProtoContext<ClientRandomAPI, ClientCryptoAPI, ClientSSLAPI> ClientProtoContext;
ClientProtoContext::Config::Ptr cp(new ClientProtoContext::Config);
cp->ssl_ctx.reset(new ClientSSLAPI(cc));
cp->cc_factory.reset(new CryptoContextCHMFactory<ClientRandomAPI, ClientCryptoAPI>());
cp->frame = frame;
cp->now = &time;
cp->rng = rng_cli;
@ -787,6 +790,7 @@ int test(const int thread_num)
typedef ProtoContext<ServerRandomAPI, ServerCryptoAPI, ServerSSLAPI> ServerProtoContext;
ServerProtoContext::Config::Ptr sp(new ServerProtoContext::Config);
sp->ssl_ctx.reset(new ServerSSLAPI(sc));
sp->cc_factory.reset(new CryptoContextCHMFactory<ServerRandomAPI, ServerCryptoAPI>());
sp->frame = frame;
sp->now = &time;
sp->rng = rng_serv;