Skip to content

Commit

Permalink
Merge commit from fork
Browse files Browse the repository at this point in the history
Co-authored-by: ReenigneArcher <[email protected]>
Co-authored-by: Cameron Gutman <[email protected]>
  • Loading branch information
3 people authored Jan 18, 2025
1 parent 80fa04c commit 89f097a
Show file tree
Hide file tree
Showing 6 changed files with 484 additions and 81 deletions.
186 changes: 105 additions & 81 deletions src/nvhttp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

// lib includes
#include <Simple-Web-Server/server_http.hpp>
#include <Simple-Web-Server/server_https.hpp>
#include <boost/asio/ssl/context.hpp>
#include <boost/asio/ssl/context_base.hpp>
#include <boost/property_tree/json_parser.hpp>
Expand All @@ -21,7 +20,6 @@

// local includes
#include "config.h"
#include "crypto.h"
#include "display_device.h"
#include "file_handler.h"
#include "globals.h"
Expand All @@ -45,18 +43,6 @@ namespace nvhttp {

crypto::cert_chain_t cert_chain;

class SunshineHTTPS: public SimpleWeb::HTTPS {
public:
SunshineHTTPS(boost::asio::io_context &io_context, boost::asio::ssl::context &ctx):
SimpleWeb::HTTPS(io_context, ctx) {}

virtual ~SunshineHTTPS() {
// Gracefully shutdown the TLS connection
SimpleWeb::error_code ec;
shutdown(ec);
}
};

class SunshineHTTPSServer: public SimpleWeb::ServerBase<SunshineHTTPS> {
public:
SunshineHTTPSServer(const std::string &certification_file, const std::string &private_key_file):
Expand Down Expand Up @@ -146,28 +132,6 @@ namespace nvhttp {
std::vector<named_cert_t> named_devices;
};

struct pair_session_t {
struct {
std::string uniqueID;
std::string cert;
std::string name;
} client;

std::unique_ptr<crypto::aes_t> cipher_key;
std::vector<uint8_t> clienthash;

std::string serversecret;
std::string serverchallenge;

struct {
util::Either<
std::shared_ptr<typename SimpleWeb::ServerBase<SimpleWeb::HTTP>::Response>,
std::shared_ptr<typename SimpleWeb::ServerBase<SunshineHTTPS>::Response>>
response;
std::string salt;
} async_insert_pin;
};

// uniqueID, session
std::unordered_map<std::string, pair_session_t> map_id_sess;
client_t client_root;
Expand Down Expand Up @@ -367,12 +331,29 @@ namespace nvhttp {
return launch_session;
}

void
remove_session(const pair_session_t &sess) {
map_id_sess.erase(sess.client.uniqueID);
}

void
fail_pair(pair_session_t &sess, pt::ptree &tree, const std::string status_msg) {
tree.put("root.paired", 0);
tree.put("root.<xmlattr>.status_code", 400);
tree.put("root.<xmlattr>.status_message", status_msg);
remove_session(sess); // Security measure, delete the session when something went wrong and force a re-pair
}

void
getservercert(pair_session_t &sess, pt::ptree &tree, const std::string &pin) {
if (sess.last_phase != PAIR_PHASE::NONE) {
fail_pair(sess, tree, "Out of order call to getservercert");
return;
}
sess.last_phase = PAIR_PHASE::GETSERVERCERT;

if (sess.async_insert_pin.salt.size() < 32) {
tree.put("root.paired", 0);
tree.put("root.<xmlattr>.status_code", 400);
tree.put("root.<xmlattr>.status_message", "Salt too short");
fail_pair(sess, tree, "Salt too short");
return;
}

Expand All @@ -389,30 +370,17 @@ namespace nvhttp {
}

void
serverchallengeresp(pair_session_t &sess, pt::ptree &tree, const args_t &args) {
auto encrypted_response = util::from_hex_vec(get_arg(args, "serverchallengeresp"), true);

std::vector<uint8_t> decrypted;
crypto::cipher::ecb_t cipher(*sess.cipher_key, false);

cipher.decrypt(encrypted_response, decrypted);

sess.clienthash = std::move(decrypted);

auto serversecret = sess.serversecret;
auto sign = crypto::sign256(crypto::pkey(conf_intern.pkey), serversecret);

serversecret.insert(std::end(serversecret), std::begin(sign), std::end(sign));

tree.put("root.pairingsecret", util::hex_vec(serversecret, true));
tree.put("root.paired", 1);
tree.put("root.<xmlattr>.status_code", 200);
}

void
clientchallenge(pair_session_t &sess, pt::ptree &tree, const args_t &args) {
auto challenge = util::from_hex_vec(get_arg(args, "clientchallenge"), true);
clientchallenge(pair_session_t &sess, pt::ptree &tree, const std::string &challenge) {
if (sess.last_phase != PAIR_PHASE::GETSERVERCERT) {
fail_pair(sess, tree, "Out of order call to clientchallenge");
return;
}
sess.last_phase = PAIR_PHASE::CLIENTCHALLENGE;

if (!sess.cipher_key) {
fail_pair(sess, tree, "Cipher key not set");
return;
}
crypto::cipher::ecb_t cipher(*sess.cipher_key, false);

std::vector<uint8_t> decrypted;
Expand Down Expand Up @@ -446,21 +414,58 @@ namespace nvhttp {
}

void
clientpairingsecret(std::shared_ptr<safe::queue_t<crypto::x509_t>> &add_cert, pair_session_t &sess, pt::ptree &tree, const args_t &args) {
serverchallengeresp(pair_session_t &sess, pt::ptree &tree, const std::string &encrypted_response) {
if (sess.last_phase != PAIR_PHASE::CLIENTCHALLENGE) {
fail_pair(sess, tree, "Out of order call to serverchallengeresp");
return;
}
sess.last_phase = PAIR_PHASE::SERVERCHALLENGERESP;

if (!sess.cipher_key || sess.serversecret.empty()) {
fail_pair(sess, tree, "Cipher key or serversecret not set");
return;
}

std::vector<uint8_t> decrypted;
crypto::cipher::ecb_t cipher(*sess.cipher_key, false);

cipher.decrypt(encrypted_response, decrypted);

sess.clienthash = std::move(decrypted);

auto serversecret = sess.serversecret;
auto sign = crypto::sign256(crypto::pkey(conf_intern.pkey), serversecret);

serversecret.insert(std::end(serversecret), std::begin(sign), std::end(sign));

tree.put("root.pairingsecret", util::hex_vec(serversecret, true));
tree.put("root.paired", 1);
tree.put("root.<xmlattr>.status_code", 200);
}

void
clientpairingsecret(pair_session_t &sess, std::shared_ptr<safe::queue_t<crypto::x509_t>> &add_cert, pt::ptree &tree, const std::string &client_pairing_secret) {
if (sess.last_phase != PAIR_PHASE::SERVERCHALLENGERESP) {
fail_pair(sess, tree, "Out of order call to clientpairingsecret");
return;
}
sess.last_phase = PAIR_PHASE::CLIENTPAIRINGSECRET;

auto &client = sess.client;

auto pairingsecret = util::from_hex_vec(get_arg(args, "clientpairingsecret"), true);
if (pairingsecret.size() <= 16) {
tree.put("root.paired", 0);
tree.put("root.<xmlattr>.status_code", 400);
tree.put("root.<xmlattr>.status_message", "Clientpairingsecret too short");
if (client_pairing_secret.size() <= 16) {
fail_pair(sess, tree, "Client pairing secret too short");
return;
}

std::string_view secret { pairingsecret.data(), 16 };
std::string_view sign { pairingsecret.data() + secret.size(), pairingsecret.size() - secret.size() };
std::string_view secret { client_pairing_secret.data(), 16 };
std::string_view sign { client_pairing_secret.data() + secret.size(), client_pairing_secret.size() - secret.size() };

auto x509 = crypto::x509(client.cert);
if (!x509) {
fail_pair(sess, tree, "Invalid client certificate");
return;
}
auto x509_sign = crypto::signature(x509);

std::string data;
Expand All @@ -473,20 +478,20 @@ namespace nvhttp {
auto hash = crypto::hash(data);

// if hash not correct, probably MITM
if (!std::memcmp(hash.data(), sess.clienthash.data(), hash.size()) && crypto::verify256(crypto::x509(client.cert), secret, sign)) {
bool same_hash = hash.size() == sess.clienthash.size() && std::equal(hash.begin(), hash.end(), sess.clienthash.begin());
auto verify = crypto::verify256(crypto::x509(client.cert), secret, sign);
if (same_hash && verify) {
tree.put("root.paired", 1);
add_cert->raise(crypto::x509(client.cert));

// The client is now successfully paired and will be authorized to connect
auto it = map_id_sess.find(client.uniqueID);
add_authorized_client(client.name, std::move(client.cert));
map_id_sess.erase(it);
}
else {
map_id_sess.erase(client.uniqueID);
tree.put("root.paired", 0);
}

remove_session(sess);
tree.put("root.<xmlattr>.status_code", 200);
}

Expand Down Expand Up @@ -568,7 +573,6 @@ namespace nvhttp {
}

auto uniqID { get_arg(args, "uniqueid") };
auto sess_it = map_id_sess.find(uniqID);

args_t::const_iterator it;
if (it = args.find("phrase"); it != std::end(args)) {
Expand Down Expand Up @@ -603,16 +607,29 @@ namespace nvhttp {
else if (it->second == "pairchallenge"sv) {
tree.put("root.paired", 1);
tree.put("root.<xmlattr>.status_code", 200);
return;
}
}
else if (it = args.find("clientchallenge"); it != std::end(args)) {
clientchallenge(sess_it->second, tree, args);

auto sess_it = map_id_sess.find(uniqID);
if (sess_it == std::end(map_id_sess)) {
tree.put("root.<xmlattr>.status_code", 400);
tree.put("root.<xmlattr>.status_message", "Invalid uniqueid");

return;
}

if (it = args.find("clientchallenge"); it != std::end(args)) {
auto challenge = util::from_hex_vec(it->second, true);
clientchallenge(sess_it->second, tree, challenge);
}
else if (it = args.find("serverchallengeresp"); it != std::end(args)) {
serverchallengeresp(sess_it->second, tree, args);
auto encrypted_response = util::from_hex_vec(it->second, true);
serverchallengeresp(sess_it->second, tree, encrypted_response);
}
else if (it = args.find("clientpairingsecret"); it != std::end(args)) {
clientpairingsecret(add_cert, sess_it->second, tree, args);
auto pairingsecret = util::from_hex_vec(it->second, true);
clientpairingsecret(sess_it->second, add_cert, tree, pairingsecret);
}
else {
tree.put("root.<xmlattr>.status_code", 404);
Expand Down Expand Up @@ -1030,6 +1047,12 @@ namespace nvhttp {
response->close_connection_after_response = true;
}

void
setup(const std::string &pkey, const std::string &cert) {
conf_intern.pkey = pkey;
conf_intern.servercert = cert;
}

void
start() {
auto shutdown_event = mail::man->event<bool>(mail::shutdown);
Expand All @@ -1044,8 +1067,9 @@ namespace nvhttp {
load_state();
}

conf_intern.pkey = file_handler::read_file(config::nvhttp.pkey.c_str());
conf_intern.servercert = file_handler::read_file(config::nvhttp.cert.c_str());
auto pkey = file_handler::read_file(config::nvhttp.pkey.c_str());
auto cert = file_handler::read_file(config::nvhttp.cert.c_str());
setup(pkey, cert);

auto add_cert = std::make_shared<safe::queue_t<crypto::x509_t>>(30);

Expand Down
Loading

0 comments on commit 89f097a

Please sign in to comment.