Certificates from successful pair are now saved to disk

This commit is contained in:
2025-07-01 21:54:30 -06:00
parent 9b34307103
commit d9aa724b90
3 changed files with 140 additions and 15 deletions
+1
View File
@@ -6,6 +6,7 @@ edition = "2024"
[dependencies]
anyhow = "1.0.98"
axum = "0.8.4"
directories = "6.0.0"
getrandom = { version = "0.3.3", features = ["std"] }
hex = "0.4.3"
libc = "0.2.174"
+90 -15
View File
@@ -1,3 +1,7 @@
use std::fs;
use std::io::Write;
use axum::Json;
use axum::extract::Path;
use axum::http::StatusCode;
use axum::response::{IntoResponse, Response};
@@ -8,10 +12,9 @@ use serde::{Deserialize, Serialize};
use openssl::pkey::{PKey, Private};
use openssl::rsa::Rsa;
use openssl::x509::{self, X509};
use openssl::x509::X509;
use anyhow::Result;
use url_constructor::UrlConstructor;
#[derive(Debug, Deserialize)]
struct ServerCertResponse {
@@ -30,6 +33,11 @@ pub struct ServerChallengeResponseResponse {
pairingsecret: String,
}
#[derive(Debug, Deserialize)]
pub struct ClientPairingSecretResponse {
paired: i32,
}
#[derive(Debug)]
struct ServerCert {
cert: Vec<u8>,
@@ -41,6 +49,11 @@ pub struct ServerPairingSecret {
signature: Vec<u8>,
}
#[derive(Debug, Serialize)]
struct PairResult {
paired: bool,
}
fn get_cert_and_private_key() -> Result<(X509, PKey<Private>)> {
let rsa = Rsa::generate(2048)?;
let key_pair = PKey::from_rsa(rsa)?;
@@ -74,6 +87,49 @@ fn get_cert_and_private_key() -> Result<(X509, PKey<Private>)> {
Ok((cert, key_pair))
}
fn save_cert_and_key_to_disk(
cert: X509,
key: PKey<Private>,
host: &String,
port: u16,
) -> Result<()> {
let project_dirs =
directories::ProjectDirs::from("xyz", "ohea", "gamestream-webtransport-proxy")
.ok_or(anyhow::anyhow!("Could not get project dirs"))?;
let data_dir = project_dirs.data_dir();
let cert_dir = data_dir.join("certs");
fs::create_dir_all(&cert_dir)?;
let cert_filepath = cert_dir.join(format!("{host}_{port}_cert"));
let key_filepath = cert_dir.join(format!("{host}_{port}_key"));
let mut cert_file_builder = std::fs::OpenOptions::new();
cert_file_builder.create(true);
cert_file_builder.truncate(true);
cert_file_builder.write(true);
let mut key_file_builder = std::fs::OpenOptions::new();
key_file_builder.create(true);
key_file_builder.truncate(true);
key_file_builder.write(true);
#[cfg(target_family = "unix")]
{
use std::os::unix::fs::OpenOptionsExt;
key_file_builder.mode(0o600);
cert_file_builder.mode(0o600);
}
let mut cert_file = cert_file_builder.open(&cert_filepath)?;
let mut key_file = key_file_builder.open(&key_filepath)?;
cert_file.write_all(&cert.to_pem()?)?;
key_file.write_all(&key.private_key_to_pem_pkcs8()?)?;
Ok(())
}
async fn get_url(base_url: &mut url_constructor::UrlConstructor) -> Result<String> {
let mut uuidv2 = [0u8; 16];
openssl::rand::rand_bytes(&mut uuidv2)?;
@@ -260,14 +316,14 @@ async fn do_challenge(
}
pub async fn get_base_url(
host: String,
host: &String,
port: u16,
unique_id: String,
) -> url_constructor::UrlConstructor {
let mut base_url = url_constructor::UrlConstructor::new();
base_url
.scheme("http")
.host(&host)
.host(host)
.port(port)
.subdir("pair")
.param("uniqueid", unique_id)
@@ -284,10 +340,10 @@ pub async fn generate_pin() -> [u8; 4] {
// TODO: reenable real RNG
let mut rng = rand::rng();
for i in 0..pin.len() {
pin[i] = rng.random_range(48..58); // Generate ascii number 0-9
// Generate ascii number 0-9
pin[i] = rng.random_range(48..58);
print!("{}", pin[i] as char);
}
// Print as a four-digit, zero-padded integer
println!("");
}
pin
@@ -328,7 +384,7 @@ pub async fn send_client_pairing_secret(
mut base_url: url_constructor::UrlConstructor,
client_secret_data: &[u8; 16],
private_key: &PKey<Private>,
) -> Result<()> {
) -> Result<ClientPairingSecretResponse> {
let signature = create_signature(client_secret_data, private_key).await?;
let mut client_secret = Vec::with_capacity(client_secret_data.len() + signature.len());
@@ -340,9 +396,8 @@ pub async fn send_client_pairing_secret(
let url = base_url.param("clientpairingsecret", client_secret_hex);
let text = get_url(url).await?;
println!("{text:?}");
Ok(())
Ok(serde_xml_rs::from_str(&text)?)
}
async fn get_unique_id() -> Result<String> {
@@ -358,7 +413,7 @@ pub async fn get_pair(Path((host, port)): Path<(String, u16)>) -> Response {
let unique_id = get_unique_id().await.unwrap();
let base_url = get_base_url(host, port, unique_id).await;
let base_url = get_base_url(&host, port, unique_id).await;
let pin = generate_pin().await;
@@ -422,12 +477,32 @@ pub async fn get_pair(Path((host, port)): Path<(String, u16)>) -> Response {
return StatusCode::INTERNAL_SERVER_ERROR.into_response();
}
if let Err(e) =
send_client_pairing_secret(base_url.clone(), &client_secret_data, &private_key).await
{
println!("Could not send client pairing secret: {e}");
let client_pairing_secret_response =
match send_client_pairing_secret(base_url.clone(), &client_secret_data, &private_key).await
{
Ok(p) => p,
Err(e) => {
println!("Could not send client pairing secret: {e}");
return StatusCode::INTERNAL_SERVER_ERROR.into_response();
}
};
let pairing_result = PairResult {
paired: client_pairing_secret_response.paired == 1,
};
if pairing_result.paired {
println!("Successfully paired to server {host}:{port}");
} else {
println!("Failed to pair with server {host}:{port}");
return Json(pairing_result).into_response();
}
// Save certificate to disk so it can be used for subsequent connections
if let Err(e) = save_cert_and_key_to_disk(cert, private_key, &host, port) {
println!("Could not save cert and key to disk: {e}");
return StatusCode::INTERNAL_SERVER_ERROR.into_response();
};
"test".into_response()
Json(pairing_result).into_response()
}