mirror of
https://github.com/deneraraujo/OpenVPNAdapter.git
synced 2026-04-24 00:00:05 +08:00
Merge commit '86cc97e55fe346502462284d2e636a2b3708163e' as 'Sources/OpenVPN3'
This commit is contained in:
@@ -0,0 +1,395 @@
|
||||
// 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) 2012-2017 OpenVPN 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/>.
|
||||
|
||||
// Implement the TLS-PRF function, used by ProtoContext.
|
||||
|
||||
#ifndef OPENVPN_SSL_TLSPRF_H
|
||||
#define OPENVPN_SSL_TLSPRF_H
|
||||
|
||||
#include <cstring> // for std::strlen and others
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/common/hexstr.hpp>
|
||||
#include <openvpn/buffer/buffer.hpp>
|
||||
#include <openvpn/buffer/bufcomplete.hpp>
|
||||
#include <openvpn/crypto/static_key.hpp>
|
||||
#include <openvpn/crypto/cryptoalgs.hpp>
|
||||
#include <openvpn/ssl/psid.hpp>
|
||||
#include <openvpn/random/randapi.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
template <typename CRYPTO_API>
|
||||
class TLSPRF
|
||||
{
|
||||
public:
|
||||
OPENVPN_SIMPLE_EXCEPTION(tlsprf_uninitialized);
|
||||
OPENVPN_SIMPLE_EXCEPTION(tlsprf_client_server_mismatch);
|
||||
|
||||
TLSPRF(const bool server)
|
||||
: initialized_(false), server_(server) {}
|
||||
|
||||
void randomize(RandomAPI& rng)
|
||||
{
|
||||
rng.assert_crypto();
|
||||
if (!server_)
|
||||
rng.rand_bytes(pre_master, sizeof(pre_master));
|
||||
rng.rand_bytes(random1, sizeof(random1));
|
||||
rng.rand_bytes(random2, sizeof(random2));
|
||||
initialized_ = true;
|
||||
}
|
||||
|
||||
void read(Buffer& buf)
|
||||
{
|
||||
if (!server_)
|
||||
buf.read(pre_master, sizeof(pre_master));
|
||||
buf.read(random1, sizeof(random1));
|
||||
buf.read(random2, sizeof(random2));
|
||||
initialized_ = true;
|
||||
}
|
||||
|
||||
bool read_complete(BufferComplete& bc)
|
||||
{
|
||||
size_t need = sizeof(random1) + sizeof(random2);
|
||||
if (!server_)
|
||||
need += sizeof(pre_master);
|
||||
if (!bc.advance(need))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void write(Buffer& buf)
|
||||
{
|
||||
verify_initialized();
|
||||
if (!server_)
|
||||
buf.write(pre_master, sizeof(pre_master));
|
||||
buf.write(random1, sizeof(random1));
|
||||
buf.write(random2, sizeof(random2));
|
||||
}
|
||||
|
||||
void generate_key_expansion(OpenVPNStaticKey& dest, const TLSPRF& peer,
|
||||
const ProtoSessionID& psid_self, const ProtoSessionID& psid_peer) const
|
||||
{
|
||||
if (server_ == peer.server_)
|
||||
throw tlsprf_client_server_mismatch();
|
||||
if (server_)
|
||||
gen_exp(dest, peer, psid_peer, *this, psid_self);
|
||||
else
|
||||
gen_exp(dest, *this, psid_self, peer, psid_peer);
|
||||
}
|
||||
|
||||
void erase()
|
||||
{
|
||||
if (initialized_)
|
||||
{
|
||||
if (!server_)
|
||||
std::memset(pre_master, 0, sizeof(pre_master));
|
||||
std::memset(random1, 0, sizeof(random1));
|
||||
std::memset(random2, 0, sizeof(random2));
|
||||
initialized_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
std::string dump(const char *title)
|
||||
{
|
||||
std::ostringstream out;
|
||||
out << "*** TLSPRF " << title << " pre_master: " << render_hex(pre_master, sizeof(pre_master)) << std::endl;
|
||||
out << "*** TLSPRF " << title << " random1: " << render_hex(random1, sizeof(random1)) << std::endl;
|
||||
out << "*** TLSPRF " << title << " random2: " << render_hex(random2, sizeof(random2)) << std::endl;
|
||||
return out.str();
|
||||
}
|
||||
|
||||
~TLSPRF()
|
||||
{
|
||||
erase();
|
||||
}
|
||||
|
||||
static void openvpn_PRF (const unsigned char *secret,
|
||||
const size_t secret_len,
|
||||
const char *label,
|
||||
const unsigned char *client_seed,
|
||||
const size_t client_seed_len,
|
||||
const unsigned char *server_seed,
|
||||
const size_t server_seed_len,
|
||||
const ProtoSessionID* client_sid,
|
||||
const ProtoSessionID* server_sid,
|
||||
unsigned char *output,
|
||||
const size_t output_len)
|
||||
{
|
||||
const size_t label_len = std::strlen(label);
|
||||
BufferAllocated seed(label_len
|
||||
+ client_seed_len
|
||||
+ server_seed_len
|
||||
+ ProtoSessionID::SIZE * 2,
|
||||
BufferAllocated::DESTRUCT_ZERO);
|
||||
seed.write((unsigned char *)label, label_len);
|
||||
seed.write(client_seed, client_seed_len);
|
||||
seed.write(server_seed, server_seed_len);
|
||||
if (client_sid)
|
||||
client_sid->write(seed);
|
||||
if (server_sid)
|
||||
server_sid->write(seed);
|
||||
|
||||
// compute PRF
|
||||
PRF(seed.data(), seed.size(), secret, secret_len, output, output_len);
|
||||
}
|
||||
|
||||
private:
|
||||
/*
|
||||
* Use the TLS PRF function for generating data channel keys.
|
||||
* This code is adapted from the OpenSSL library.
|
||||
*
|
||||
* TLS generates keys as such:
|
||||
*
|
||||
* master_secret[48] = PRF(pre_master_secret[48], "master secret",
|
||||
* ClientHello.random[32] + ServerHello.random[32])
|
||||
*
|
||||
* key_block[] = PRF(SecurityParameters.master_secret[48],
|
||||
* "key expansion",
|
||||
* SecurityParameters.server_random[32] +
|
||||
* SecurityParameters.client_random[32]);
|
||||
*
|
||||
* Notes:
|
||||
*
|
||||
* (1) key_block contains a full set of 4 keys.
|
||||
* (2) The pre-master secret is generated by the client.
|
||||
*/
|
||||
|
||||
static void hash (const CryptoAlgs::Type md,
|
||||
const unsigned char *sec,
|
||||
const size_t sec_len,
|
||||
const unsigned char *seed,
|
||||
const size_t seed_len,
|
||||
unsigned char *out,
|
||||
size_t olen)
|
||||
{
|
||||
size_t j;
|
||||
unsigned char A1[CRYPTO_API::HMACContext::MAX_HMAC_SIZE];
|
||||
size_t A1_len;
|
||||
typename CRYPTO_API::HMACContext ctx;
|
||||
typename CRYPTO_API::HMACContext ctx_tmp;
|
||||
const size_t chunk = CryptoAlgs::size(md);
|
||||
|
||||
ctx.init(md, sec, sec_len);
|
||||
ctx_tmp.init(md, sec, sec_len);
|
||||
ctx.update(seed, seed_len);
|
||||
A1_len = ctx.final(A1);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
ctx.reset();
|
||||
ctx_tmp.reset();
|
||||
ctx.update(A1, A1_len);
|
||||
ctx_tmp.update(A1, A1_len);
|
||||
ctx.update(seed, seed_len);
|
||||
|
||||
if (olen > chunk)
|
||||
{
|
||||
j = ctx.final(out);
|
||||
out+=j;
|
||||
olen-=j;
|
||||
A1_len = ctx_tmp.final(A1); /* calc the next A1 value */
|
||||
}
|
||||
else /* last one */
|
||||
{
|
||||
A1_len = ctx.final(A1);
|
||||
memcpy(out,A1,olen);
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::memset(A1, 0, sizeof(A1));
|
||||
}
|
||||
|
||||
static void PRF (unsigned char *label,
|
||||
const size_t label_len,
|
||||
const unsigned char *sec,
|
||||
const size_t slen,
|
||||
unsigned char *out1,
|
||||
const size_t olen)
|
||||
{
|
||||
size_t len, i;
|
||||
const unsigned char *S1, *S2;
|
||||
unsigned char *out2;
|
||||
|
||||
out2 = new unsigned char[olen];
|
||||
|
||||
len = slen / 2;
|
||||
S1 = sec;
|
||||
S2 = &(sec[len]);
|
||||
len += (slen & 1); /* add for odd, make longer */
|
||||
|
||||
hash(CryptoAlgs::MD5,S1,len,label,label_len,out1,olen);
|
||||
hash(CryptoAlgs::SHA1,S2,len,label,label_len,out2,olen);
|
||||
|
||||
for (i=0; i<olen; i++)
|
||||
out1[i]^=out2[i];
|
||||
|
||||
std::memset(out2, 0, olen);
|
||||
delete [] out2;
|
||||
}
|
||||
|
||||
static void gen_exp(OpenVPNStaticKey& dest, const TLSPRF& client, const ProtoSessionID& psid_client,
|
||||
const TLSPRF& server, const ProtoSessionID& psid_server)
|
||||
{
|
||||
static const char master_secret_id[] = "OpenVPN master secret";
|
||||
static const char key_expansion_id[] = "OpenVPN key expansion";
|
||||
|
||||
unsigned char master[48];
|
||||
|
||||
client.verify_initialized();
|
||||
server.verify_initialized();
|
||||
|
||||
// compute master secret
|
||||
openvpn_PRF (client.pre_master,
|
||||
sizeof(client.pre_master),
|
||||
master_secret_id,
|
||||
client.random1,
|
||||
sizeof(client.random1),
|
||||
server.random1,
|
||||
sizeof(server.random1),
|
||||
nullptr,
|
||||
nullptr,
|
||||
master,
|
||||
sizeof(master));
|
||||
|
||||
// compute key expansion */
|
||||
openvpn_PRF (master,
|
||||
sizeof(master),
|
||||
key_expansion_id,
|
||||
client.random2,
|
||||
sizeof(client.random2),
|
||||
server.random2,
|
||||
sizeof(server.random2),
|
||||
&psid_client,
|
||||
&psid_server,
|
||||
dest.raw_alloc(),
|
||||
OpenVPNStaticKey::KEY_SIZE);
|
||||
|
||||
std::memset(master, 0, sizeof(master));
|
||||
}
|
||||
|
||||
void verify_initialized() const
|
||||
{
|
||||
if (!initialized_)
|
||||
throw tlsprf_uninitialized();
|
||||
}
|
||||
|
||||
bool initialized_;
|
||||
bool server_;
|
||||
unsigned char pre_master[48]; // client generated
|
||||
unsigned char random1[32]; // generated by both client and server
|
||||
unsigned char random2[32]; // generated by both client and server
|
||||
};
|
||||
|
||||
// TLSPRF wrapper API using dynamic polymorphism
|
||||
|
||||
class TLSPRFInstance : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<TLSPRFInstance> Ptr;
|
||||
|
||||
virtual void self_randomize(RandomAPI& rng) = 0;
|
||||
virtual void self_write(Buffer& buf) = 0;
|
||||
virtual void peer_read(Buffer& buf) = 0;
|
||||
virtual bool peer_read_complete(BufferComplete& bc) = 0;
|
||||
virtual void erase() = 0;
|
||||
|
||||
virtual void generate_key_expansion(OpenVPNStaticKey& dest,
|
||||
const ProtoSessionID& psid_self,
|
||||
const ProtoSessionID& psid_peer) const = 0;
|
||||
};
|
||||
|
||||
class TLSPRFFactory : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<TLSPRFFactory> Ptr;
|
||||
|
||||
virtual TLSPRFInstance::Ptr new_obj(const bool self_is_server) = 0;
|
||||
};
|
||||
|
||||
// TLSPRF wrapper implementation using dynamic polymorphism
|
||||
|
||||
template <typename CRYPTO_API>
|
||||
class CryptoTLSPRFInstance : public TLSPRFInstance
|
||||
{
|
||||
public:
|
||||
CryptoTLSPRFInstance(const bool self_is_server)
|
||||
: self(self_is_server),
|
||||
peer(!self_is_server)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void self_randomize(RandomAPI& rng)
|
||||
{
|
||||
self.randomize(rng);
|
||||
}
|
||||
|
||||
virtual void self_write(Buffer& buf)
|
||||
{
|
||||
self.write(buf);
|
||||
}
|
||||
|
||||
virtual void peer_read(Buffer& buf)
|
||||
{
|
||||
peer.read(buf);
|
||||
}
|
||||
|
||||
virtual bool peer_read_complete(BufferComplete& bc)
|
||||
{
|
||||
return peer.read_complete(bc);
|
||||
}
|
||||
|
||||
virtual void erase()
|
||||
{
|
||||
self.erase();
|
||||
peer.erase();
|
||||
}
|
||||
|
||||
virtual void generate_key_expansion(OpenVPNStaticKey& dest,
|
||||
const ProtoSessionID& psid_self,
|
||||
const ProtoSessionID& psid_peer) const
|
||||
{
|
||||
self.generate_key_expansion(dest, peer, psid_self, psid_peer);
|
||||
}
|
||||
|
||||
private:
|
||||
TLSPRF<CRYPTO_API> self;
|
||||
TLSPRF<CRYPTO_API> peer;
|
||||
};
|
||||
|
||||
template <typename CRYPTO_API>
|
||||
class CryptoTLSPRFFactory : public TLSPRFFactory
|
||||
{
|
||||
public:
|
||||
virtual TLSPRFInstance::Ptr new_obj(const bool self_is_server)
|
||||
{
|
||||
return new CryptoTLSPRFInstance<CRYPTO_API>(self_is_server);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_SSL_TLSPRF_H
|
||||
Reference in New Issue
Block a user