Files
Sunshine/src/nvhttp.h
ReenigneArcher 257a102127
Some checks failed
CI / GitHub Env Debug (push) Waiting to run
CI / Setup Release (push) Waiting to run
CI / Setup Flatpak Matrix (push) Waiting to run
CI / Linux Flatpak (push) Blocked by required conditions
CI / Linux ${{ matrix.type }} (--appimage-build, 22.04, AppImage) (push) Blocked by required conditions
CI / Homebrew (${{ matrix.os_name }}-${{ matrix.os_version }}${{ matrix.release == true && ' (Release)' || '' }}) (macos, 13) (push) Blocked by required conditions
CI / Homebrew (${{ matrix.os_name }}-${{ matrix.os_version }}${{ matrix.release == true && ' (Release)' || '' }}) (macos, 14) (push) Blocked by required conditions
CI / Homebrew (${{ matrix.os_name }}-${{ matrix.os_version }}${{ matrix.release == true && ' (Release)' || '' }}) (ubuntu, latest) (push) Blocked by required conditions
CI / Homebrew (${{ matrix.os_name }}-${{ matrix.os_version }}${{ matrix.release == true && ' (Release)' || '' }}) (ubuntu, latest, true) (push) Blocked by required conditions
CI / Windows (push) Blocked by required conditions
CI Docker / Check Dockerfiles (push) Waiting to run
CI Docker / Setup Release (push) Blocked by required conditions
CI Docker / Docker${{ matrix.tag }} (push) Blocked by required conditions
CodeQL / Get language matrix (push) Waiting to run
CodeQL / Analyze (${{ matrix.name }}) (push) Blocked by required conditions
Build GH-Pages / prep (push) Waiting to run
Build GH-Pages / call-jekyll-build (push) Blocked by required conditions
localize / Update Localization (push) Has been cancelled
fix(api): return proper json objects (#3544)
2025-01-31 23:38:22 -05:00

204 lines
6.0 KiB
C++

/**
* @file src/nvhttp.h
* @brief Declarations for the nvhttp (GameStream) server.
*/
// macros
#pragma once
// standard includes
#include <string>
// lib includes
#include <boost/property_tree/ptree.hpp>
#include <nlohmann/json.hpp>
#include <Simple-Web-Server/server_https.hpp>
// local includes
#include "crypto.h"
#include "thread_safe.h"
/**
* @brief Contains all the functions and variables related to the nvhttp (GameStream) server.
*/
namespace nvhttp {
/**
* @brief The protocol version.
* @details The version of the GameStream protocol we are mocking.
* @note The negative 4th number indicates to Moonlight that this is Sunshine.
*/
constexpr auto VERSION = "7.1.431.-1";
/**
* @brief The GFE version we are replicating.
*/
constexpr auto GFE_VERSION = "3.23.0.74";
/**
* @brief The HTTP port, as a difference from the config port.
*/
constexpr auto PORT_HTTP = 0;
/**
* @brief The HTTPS port, as a difference from the config port.
*/
constexpr auto PORT_HTTPS = -5;
/**
* @brief Start the nvhttp server.
* @examples
* nvhttp::start();
* @examples_end
*/
void start();
/**
* @brief Setup the nvhttp server.
* @param pkey
* @param cert
*/
void setup(const std::string &pkey, const std::string &cert);
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);
}
};
enum class PAIR_PHASE {
NONE, ///< Sunshine is not in a pairing phase
GETSERVERCERT, ///< Sunshine is in the get server certificate phase
CLIENTCHALLENGE, ///< Sunshine is in the client challenge phase
SERVERCHALLENGERESP, ///< Sunshine is in the server challenge response phase
CLIENTPAIRINGSECRET ///< Sunshine is in the client pairing secret phase
};
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;
/**
* @brief used as a security measure to prevent out of order calls
*/
PAIR_PHASE last_phase = PAIR_PHASE::NONE;
};
/**
* @brief removes the temporary pairing session
* @param sess
*/
void remove_session(const pair_session_t &sess);
/**
* @brief Pair, phase 1
*
* Moonlight will send a salt and client certificate, we'll also need the user provided pin.
*
* PIN and SALT will be used to derive a shared AES key that needs to be stored
* in order to be used to decrypt_symmetric in the next phases.
*
* At this stage we only have to send back our public certificate.
*/
void getservercert(pair_session_t &sess, boost::property_tree::ptree &tree, const std::string &pin);
/**
* @brief Pair, phase 2
*
* Using the AES key that we generated in phase 1 we have to decrypt the client challenge,
*
* We generate a SHA256 hash with the following:
* - Decrypted challenge
* - Server certificate signature
* - Server secret: a randomly generated secret
*
* The hash + server_challenge will then be AES encrypted and sent as the `challengeresponse` in the returned XML
*/
void clientchallenge(pair_session_t &sess, boost::property_tree::ptree &tree, const std::string &challenge);
/**
* @brief Pair, phase 3
*
* Moonlight will send back a `serverchallengeresp`: an AES encrypted client hash,
* we have to send back the `pairingsecret`:
* using our private key we have to sign the certificate_signature + server_secret (generated in phase 2)
*/
void serverchallengeresp(pair_session_t &sess, boost::property_tree::ptree &tree, const std::string &encrypted_response);
/**
* @brief Pair, phase 4 (final)
*
* We now have to use everything we exchanged before in order to verify and finally pair the clients
*
* We'll check the client_hash obtained at phase 3, it should contain the following:
* - The original server_challenge
* - The signature of the X509 client_cert
* - The unencrypted client_pairing_secret
* We'll check that SHA256(server_challenge + client_public_cert_signature + client_secret) == client_hash
*
* Then using the client certificate public key we should be able to verify that
* the client secret has been signed by Moonlight
*/
void clientpairingsecret(pair_session_t &sess, std::shared_ptr<safe::queue_t<crypto::x509_t>> &add_cert, boost::property_tree::ptree &tree, const std::string &client_pairing_secret);
/**
* @brief Compare the user supplied pin to the Moonlight pin.
* @param pin The user supplied pin.
* @param name The user supplied name.
* @return `true` if the pin is correct, `false` otherwise.
* @examples
* bool pin_status = nvhttp::pin("1234", "laptop");
* @examples_end
*/
bool pin(std::string pin, std::string name);
/**
* @brief Remove single client.
* @param uuid The UUID of the client to remove.
* @examples
* nvhttp::unpair_client("4D7BB2DD-5704-A405-B41C-891A022932E1");
* @examples_end
*/
bool unpair_client(std::string_view uuid);
/**
* @brief Get all paired clients.
* @return The list of all paired clients.
* @examples
* nlohmann::json clients = nvhttp::get_all_clients();
* @examples_end
*/
nlohmann::json get_all_clients();
/**
* @brief Remove all paired clients.
* @examples
* nvhttp::erase_all_clients();
* @examples_end
*/
void erase_all_clients();
} // namespace nvhttp