Skip to content

Commit

Permalink
Implemented PSK support #34
Browse files Browse the repository at this point in the history
  • Loading branch information
ereOn committed Mar 15, 2015
1 parent 2bed4d1 commit 538eb12
Show file tree
Hide file tree
Showing 18 changed files with 539 additions and 154 deletions.
45 changes: 43 additions & 2 deletions apps/freelan/config/freelan.cfg
Expand Up @@ -660,16 +660,57 @@ ipv6_address_prefix_length=2aa1::1/8

[security]

# The passphrase used to generate a pre-shared key to use for encryption.
#
# The PSK is derived using PBKDF2.
#
# Using a PSK is less secure than using a certificate and should never be a
# first choice. It is useful in cases where generating certificates, private
# keys is not feasible.
#
# You can specify a PSK even if you have a certificate, which allows to connect
# with certificate-less nodes.
#
# The passphrase MUST remain secret.
#
# Default: <none>
#passphrase=

# The salt to use when deriving the PSK from the passphrase.
#
# It is recommended that you change this value for your own freelan
# installation when using PSKs. It doesn't have to be secret but it should
# ideally be unique.
#
# Default: freelan
#passphrase_salt=freelan

# The number of iterations to use when deriving the PSK from the passphrase.
#
# You can increase (or decrease, but please, don't) this number to increase the
# time it takes to derive the key from the passphrase and reduces the
# likelyhood of brute-force attacks.
#
# Default: 2000
#passphrase_iterations_count=2000

# The salt to use when deriving the PSK from the passphrase.
#
# Default: freelan
#passphrase_salt=freelan

# The X509 certificate file to use for signing.
#
# Unless client.enabled is set to "yes", this parameter is mandatory.
# Unless client.enabled is set to "yes" or a PSK is specified, this parameter
# is mandatory.
#
# Default: <none>
#signature_certificate_file=

# The private key file to use for signing.
#
# Unless client.enabled is set to "yes", this parameter is mandatory.
# Unless client.enabled is set to "yes" or PSK is specified, this parameter is
# mandatory.
#
# This private key must match with the specified signing certificate file.
#
Expand Down
21 changes: 19 additions & 2 deletions apps/freelan/src/configuration_helper.cpp
Expand Up @@ -53,6 +53,9 @@

#include "configuration_types.hpp"

#include <cryptoplus/hash/pbkdf2.hpp>
#include <cryptoplus/hash/message_digest_algorithm.hpp>

// This file is generated locally.
#include <defines.hpp>

Expand Down Expand Up @@ -266,6 +269,9 @@ po::options_description get_security_options()
po::options_description result("Security options");

result.add_options()
("security.passphrase", po::value<std::string>()->default_value(""), "A passphrase to generate the pre - shared key from.")
("security.passphrase_salt", po::value<std::string>()->default_value("freelan"), "The salt to use during the pre-shared key derivation.")
("security.passphrase_iterations_count", po::value<unsigned int>()->default_value(2000, "2000"), "The number of iterations to use during the pre-shared key derivation.")
("security.signature_certificate_file", po::value<fs::path>(), "The certificate file to use for signing.")
("security.signature_private_key_file", po::value<fs::path>(), "The private key file to use for signing.")
("security.certificate_validation_method", po::value<fl::security_configuration::certificate_validation_method_type>()->default_value(fl::security_configuration::CVM_DEFAULT), "The certificate validation method.")
Expand Down Expand Up @@ -382,15 +388,26 @@ void setup_configuration(fl::configuration& configuration, const boost::filesyst
configuration.fscp.elliptic_curve_capabilities = vm["fscp.elliptic_curve_capability"].as<std::vector<fscp::elliptic_curve_type>>();

// Security options
const std::string passphrase = vm["security.passphrase"].as<std::string>();
const std::string passphrase_salt = vm["security.passphrase_salt"].as<std::string>();
const unsigned int passphrase_iterations_count = vm["security.passphrase_iterations_count"].as<unsigned int>();
cryptoplus::buffer pre_shared_key;

if (!passphrase.empty())
{
const auto mdalg = cryptoplus::hash::message_digest_algorithm(NID_sha256);
pre_shared_key = cryptoplus::hash::pbkdf2(&passphrase[0], passphrase.size(), &passphrase_salt[0], passphrase_salt.size(), mdalg, passphrase_iterations_count);
}

cert_type signature_certificate;
pkey signature_private_key;

load_certificate(signature_certificate, "security.signature_certificate_file", vm, root);
load_private_key(signature_private_key, "security.signature_private_key_file", vm, root);

if (!!signature_certificate && !!signature_private_key)
if ((!!signature_certificate && !!signature_private_key) || !!pre_shared_key)
{
configuration.security.identity = fscp::identity_store(signature_certificate, signature_private_key);
configuration.security.identity = fscp::identity_store(signature_certificate, signature_private_key, pre_shared_key);
}

configuration.security.certificate_validation_method = vm["security.certificate_validation_method"].as<fl::security_configuration::certificate_validation_method_type>();
Expand Down
36 changes: 34 additions & 2 deletions libs/cryptoplus/include/cryptoplus/buffer.hpp
Expand Up @@ -48,6 +48,7 @@
#include <vector>
#include <stdint.h>
#include <iostream>
#include <algorithm>

namespace cryptoplus
{
Expand Down Expand Up @@ -135,9 +136,23 @@ namespace cryptoplus
return std::string(m_data.begin(), m_data.end());
}

/**
* \brief Check if the buffer is empty.
* \return true if the buffer is empty, false otherwise.
*/
bool empty() const
{
return m_data.empty();
}

private:

std::vector<uint8_t> m_data;

friend bool operator!(const buffer& buf)
{
return buf.empty();
}
};

/**
Expand Down Expand Up @@ -212,12 +227,29 @@ namespace cryptoplus

inline bool operator==(const buffer& lhs, const buffer& rhs)
{
return lhs.data() == rhs.data();
// Optimization-free implementation to prevent timing attacks.
bool result = true;
const auto len = std::min(lhs.data().size(), rhs.data().size());

for (size_t i = 0; i < len; ++i)
{
if (lhs.data()[i] != rhs.data()[i])
{
result = false;
}
}

if (lhs.data().size() != rhs.data().size())
{
result = false;
}

return result;
}

inline bool operator!=(const buffer& lhs, const buffer& rhs)
{
return lhs.data() != rhs.data();
return !(lhs == rhs);
}

inline bool operator<(const buffer& lhs, const buffer& rhs)
Expand Down
40 changes: 33 additions & 7 deletions libs/freelan/src/core.cpp
Expand Up @@ -479,7 +479,7 @@ namespace freelan
{
if (!m_configuration.security.identity)
{
m_logger(fscp::log_level::warning) << "No user certificate or private key set. Generating temporary ones...";
m_logger(fscp::log_level::warning) << "No user certificate/private key or pre-shared key set. Generating a temporary certificate/private key pair...";

const auto private_key = generate_private_key();
const auto certificate = generate_self_signed_certificate(private_key);
Expand All @@ -488,6 +488,18 @@ namespace freelan

m_logger(fscp::log_level::warning) << "Using a generated temporary certificate (" << certificate.subject() << ") prevents reliable authentication ! Generate and specify a static certificate/key pair for use in production.";
}
else
{
if (!!m_configuration.security.identity->signature_certificate())
{
m_logger(fscp::log_level::information) << "Enabling certificate-based authentication.";
}

if (!m_configuration.security.identity->pre_shared_key().empty())
{
m_logger(fscp::log_level::information) << "Enabling pre-shared key authentication.";
}
}

m_logger(fscp::log_level::information) << "Starting FSCP server...";

Expand Down Expand Up @@ -980,7 +992,14 @@ namespace freelan
{
if (m_logger.level() <= fscp::log_level::debug)
{
m_logger(fscp::log_level::debug) << "Received PRESENTATION from " << sender << ": " << sig_cert.subject() << ".";
if (!!sig_cert)
{
m_logger(fscp::log_level::debug) << "Received PRESENTATION from " << sender << ": " << sig_cert.subject() << ".";
}
else
{
m_logger(fscp::log_level::debug) << "Received PRESENTATION from " << sender << " using pre-shared key authentication.";
}
}

if (is_banned(sender.address()))
Expand All @@ -997,14 +1016,21 @@ namespace freelan
return false;
}

if (!certificate_is_valid(sig_cert))
if (!!sig_cert)
{
m_logger(fscp::log_level::warning) << "Ignoring PRESENTATION from " << sender << " as the signature certificate is invalid.";
if (!certificate_is_valid(sig_cert))
{
m_logger(fscp::log_level::warning) << "Ignoring PRESENTATION from " << sender << " as the signature certificate is invalid.";

return false;
}
return false;
}

m_logger(fscp::log_level::information) << "Accepting PRESENTATION from " << sender << " (" << sig_cert.subject() << "): " << status << ".";
m_logger(fscp::log_level::information) << "Accepting PRESENTATION from " << sender << " (" << sig_cert.subject() << "): " << status << ".";
}
else
{
m_logger(fscp::log_level::information) << "Accepting PRESENTATION from " << sender << " for pre-shared key authentication: " << status << ".";
}

async_request_session(sender);

Expand Down
2 changes: 1 addition & 1 deletion libs/freelan/src/server.cpp
Expand Up @@ -206,7 +206,7 @@ namespace freelan

const cryptoplus::x509::certificate cert = cryptoplus::x509::certificate::from_der(req.content(), req.content_size());

cinfop->presentation = fscp::presentation_store(cert);
cinfop->presentation = fscp::presentation_store(cert, cryptoplus::buffer());
cinfop->expires_from_now(configuration.registration_validity_duration);

typedef boost::date_time::c_local_adjustor<boost::posix_time::ptime> local_adjustor;
Expand Down
13 changes: 10 additions & 3 deletions libs/fscp/fscp.txt
Expand Up @@ -104,8 +104,13 @@ Abstract
associated RSA public and private keys for signature will respectively be
referred as PKV and PKS.

sig_cert can be empty (and thus sig_cert_len is 0) if the host is using a
pre-shared key instead of RSA certificates. In this case, such a
PRESENTATION message conveys an intent to establish a session.

A host MIGHT ignore any PRESENTATION message whose sig_cert field does not
satisfy the key usage requirements.
satisfy the key usage requirements or if it doesn't support pre-shared key
encryption.

The minimum RSA key size is 1024 bits. The RECOMMENDED RSA key size is 2048
bits or higher, with an exponent of 2^16 + 1. A strict implementation MAY
Expand Down Expand Up @@ -181,7 +186,8 @@ Abstract
The hr_sig_len field indicates the length of the hr_sig field.

The hr_sig field is the header signature, generated using the private
signature key (PKS) of the sender host.
signature key (PKS) of the sender host or HMAC-SHA-256 with the pre-shared
key if a pre-shared key is used.

A host who receives a SESSION_REQUEST message MUST first check if the
hr_sig signature matches the sending host public verification key (PKV).
Expand Down Expand Up @@ -270,7 +276,8 @@ Abstract
The hr_sig_len field indicates the length of the hr_sig field.

The hr_sig field is the header signature, generated using the private
signature key (PKS) of the sender host.
signature key (PKS) of the sender host or HMAC-SHA-256 with the pre-shared
key if a pre-shared key is used.

A host who receives a SESSION message MUST first check if the hr_sig
signature matches the sending host public verification key (PKV). If the
Expand Down
37 changes: 22 additions & 15 deletions libs/fscp/include/fscp/identity_store.hpp
Expand Up @@ -69,38 +69,45 @@ namespace fscp

/**
* \brief Create a new identity store.
* \param sig_cert The signature certificate. Cannot be null.
* \param sig_key The signature key. Cannot be null.
* \param sig_cert The signature certificate.
* \param sig_key The signature key.
* \param pre_shared_key The pre-shared key.
*/
identity_store(cert_type sig_cert, key_type sig_key);
identity_store(cert_type sig_cert, key_type sig_key, const cryptoplus::buffer& pre_shared_key = cryptoplus::buffer());

/**
* \brief Get the signature certificate.
* \return The signature certificate.
*/
cert_type signature_certificate() const;
cert_type signature_certificate() const
{
return m_sig_cert;
}

/**
* \brief Get the signature key.
* \return The signature key.
*/
key_type signature_key() const;
key_type signature_key() const
{
return m_sig_key;
}

/**
* \brief Get the pre-shared key.
* \return The pre-shared key.
*/
const cryptoplus::buffer& pre_shared_key() const
{
return m_pre_shared_key;
}

private:

cert_type m_sig_cert;
key_type m_sig_key;
cryptoplus::buffer m_pre_shared_key;
};

inline identity_store::cert_type identity_store::signature_certificate() const
{
return m_sig_cert;
}

inline identity_store::key_type identity_store::signature_key() const
{
return m_sig_key;
}
}

#endif /* FSCP_IDENTITY_STORE_HPP */
18 changes: 14 additions & 4 deletions libs/fscp/include/fscp/presentation_store.hpp
Expand Up @@ -73,18 +73,18 @@ namespace fscp

/**
* \brief Create a new presentation store.
* \param sig_cert The signature certificate. Cannot be null.
* \param enc_cert The encryption certificate. If enc_cert is null, sig_cert is taken instead.
* \param sig_cert The signature certificate.
* \param pre_shared_key The pre-shared key.
*/
explicit presentation_store(cert_type sig_cert);
explicit presentation_store(cert_type sig_cert, const cryptoplus::buffer& pre_shared_key);

/**
* \brief Check if the presentation store is empty.
* \return true if the presentation store is empty.
*/
bool empty() const
{
return !m_sig_cert;
return !m_sig_cert && !m_pre_shared_key;
}

/**
Expand All @@ -105,10 +105,20 @@ namespace fscp
return m_sig_hash;
}

/**
* \brief Get the pre-shared key.
* \return The pre-shared key.
*/
const cryptoplus::buffer& pre_shared_key() const
{
return m_pre_shared_key;
}

private:

cert_type m_sig_cert;
hash_type m_sig_hash;
cryptoplus::buffer m_pre_shared_key;
};
}

Expand Down

0 comments on commit 538eb12

Please sign in to comment.