Skip to content

Commit

Permalink
Remove unneeded encryption of exit lists
Browse files Browse the repository at this point in the history
And cleanup for exit server list handling. We no longer need to have
ExitList and ExitListV2 with the new ExitServerList which simply
verifies received SignedExitServerList from an exit.
  • Loading branch information
ch-iara authored and jkilpatr committed Oct 18, 2024
1 parent 52ffca4 commit 15d5381
Show file tree
Hide file tree
Showing 12 changed files with 111 additions and 219 deletions.
55 changes: 0 additions & 55 deletions althea_types/src/exits/encryption.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
//! this file contains utility functions for the exit communcaiton which requires encrypting/decrypting requests
//! to secure them as the pass over the babel network
use crate::SignedExitServerList;
use crate::WgKey;
use crypto_box::aead::Aead;
use crypto_box::aead::AeadCore;
Expand All @@ -13,7 +12,6 @@ use std::fmt::Display;
use std::fmt::Formatter;

use super::EncryptedExitClientIdentity;
use super::EncryptedExitServerList;
use super::EncryptedExitState;
use super::ExitClientIdentity;
use super::ExitState;
Expand Down Expand Up @@ -154,59 +152,6 @@ pub fn encrypt_setup_return(
}
}

impl EncryptedExitServerList {
pub fn decrypt(
&self,
our_secretkey: &SecretKey,
) -> Result<SignedExitServerList, ExitEncryptionError> {
let ciphertext = self.encrypted_exit_server_list.clone();
let nonce = self.nonce;

let b = SalsaBox::new(&self.pubkey.into(), our_secretkey);

let ret: SignedExitServerList = match b.decrypt(nonce.as_ref().into(), ciphertext.as_ref())
{
Ok(decrypted_bytes) => match String::from_utf8(decrypted_bytes) {
Ok(json_string) => match serde_json::from_str(&json_string) {
Ok(exit_list) => exit_list,
Err(e) => {
return Err(ExitEncryptionError::SerdeError { e: e.to_string() });
}
},
Err(e) => {
return Err(ExitEncryptionError::Utf8Error { e: e.to_string() });
}
},
Err(_) => {
return Err(ExitEncryptionError::ExitListDecryptionError {
e: "Could not decrypt exit list".to_string(),
});
}
};
Ok(ret)
}
}

impl SignedExitServerList {
pub fn encrypt(
&self,
our_secretkey: &SecretKey,
their_pubkey: &PublicKey,
) -> EncryptedExitServerList {
let plaintext = serde_json::to_string(&self)
.expect("Failed to serialize ExitServerList!")
.into_bytes();
let nonce = SalsaBox::generate_nonce(&mut OsRng);
let b = SalsaBox::new(their_pubkey, our_secretkey);
let ciphertext = b.encrypt(&nonce, plaintext.as_ref()).unwrap();
EncryptedExitServerList {
pubkey: WgKey::from(*their_pubkey.as_bytes()),
nonce: nonce.into(),
encrypted_exit_server_list: ciphertext,
}
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
8 changes: 0 additions & 8 deletions althea_types/src/exits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,11 +137,3 @@ pub struct ExitClientDetails {
pub client_internal_ip: IpAddr,
pub internet_ipv6_subnet: Option<IpNetwork>,
}

/// Wrapper for secure box containing a Signed Exit Server List
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Hash, Clone)]
pub struct EncryptedExitServerList {
pub pubkey: WgKey,
pub nonce: [u8; 24],
pub encrypted_exit_server_list: Vec<u8>,
}
10 changes: 10 additions & 0 deletions althea_types/src/exits/server_list_signatures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@ pub struct ExitServerList {
pub created: SystemTime,
}

impl Default for ExitServerList {
fn default() -> Self {
ExitServerList {
contract: Address::default(),
exit_list: Vec::new(),
created: SystemTime::now(),
}
}
}

impl ExitServerList {
/// Returns the ExitServerList as an Ethereum ABI token
pub fn encode_to_eth_abi_token(&self) -> AbiToken {
Expand Down
86 changes: 26 additions & 60 deletions exit_trust_root/src/bin.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
use actix_web::rt::System;
use actix_web::{get, web, App, HttpResponse, HttpServer, Responder};
use althea_types::{EncryptedExitServerList, ExitServerList, SignedExitServerList};
use althea_types::{ExitServerList, SignedExitServerList};
use clarity::Address;
use config::{load_config, CONFIG};
use crypto_box::aead::{Aead, AeadCore, OsRng};
use crypto_box::{PublicKey, SalsaBox, SecretKey};
use env_logger::Env;
use log::info;
use rita_client_registration::client_db::get_exits_list;
Expand All @@ -20,8 +18,6 @@ use web30::jsonrpc::error::Web3Error;
pub mod config;
pub mod tls;

const RPC_SERVER: &str = "https://althea.gravitychain.io";

pub const DEVELOPMENT: bool = cfg!(feature = "development");
const SSL: bool = !DEVELOPMENT;
pub const DOMAIN: &str = if cfg!(test) || DEVELOPMENT {
Expand All @@ -37,37 +33,35 @@ const SERVER_PORT: u16 = 9000;
#[get("/{exit_contract}")]
async fn return_exit_contract_data(
exit_contract: web::Path<Address>,
pubkey: web::Data<[u8; 32]>,
cache: web::Data<Arc<RwLock<HashMap<Address, ExitContractSignatureCacheValue>>>>,
cache: web::Data<Arc<RwLock<HashMap<Address, SignedExitServerList>>>>,
) -> impl Responder {
let contract = exit_contract.into_inner();
match cache.read().unwrap().get(&contract) {
Some(cache) => {
// return an encrypted exit server list based on the given key
HttpResponse::Ok().json(cache.to_encrypted_exit_server_list((*pubkey.get_ref()).into()))
let cached_list = {
let cache_read = cache.read().unwrap();
cache_read.get(&contract).cloned()
};

match cached_list {
Some(list) => {
// return a signed exit server list based on the given key
HttpResponse::Ok().json(list)
}
None => {
match retrieve_exit_server_list(contract, cache.get_ref().clone()).await {
Ok(cache_value) => {
// encrypt and return
return HttpResponse::Ok().json(
cache_value.to_encrypted_exit_server_list((*pubkey.get_ref()).into()),
);
}
Err(e) => {
info!("Failed to get exit list from contract {:?}", e);
HttpResponse::InternalServerError()
.json("Failed to get exit list from contract")
}
None => match retrieve_exit_server_list(contract, cache.get_ref().clone()).await {
Ok(list) => {
HttpResponse::Ok().json(list)
}
}
Err(e) => {
info!("Failed to get exit list from contract {:?}", e);
HttpResponse::InternalServerError().json("Failed to get exit list from contract")
}
},
}
}

async fn retrieve_exit_server_list(
exit_contract: Address,
cache: Arc<RwLock<HashMap<Address, ExitContractSignatureCacheValue>>>,
) -> Result<ExitContractSignatureCacheValue, Web3Error> {
cache: Arc<RwLock<HashMap<Address, SignedExitServerList>>>,
) -> Result<SignedExitServerList, Web3Error> {
const WEB3_TIMEOUT: Duration = Duration::from_secs(10);
let exits = get_exits_list(
&Web3::new("https://dai.althea.net", WEB3_TIMEOUT),
Expand All @@ -83,12 +77,9 @@ async fn retrieve_exit_server_list(
exit_list: exits,
created: std::time::SystemTime::now(),
};
let nonce = SalsaBox::generate_nonce(&mut OsRng);
let cache_value = ExitContractSignatureCacheValue {
exit_list: exit_list.sign(CONFIG.clarity_private_key),
nonce: nonce.into(),
};
// add this new exit to the cache
let cache_value = exit_list.sign(CONFIG.clarity_private_key);

// add this new exit list to the cache
cache
.write()
.unwrap()
Expand All @@ -102,38 +93,13 @@ async fn retrieve_exit_server_list(
}
}

/// Cache struct for the exit contract signature data
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
struct ExitContractSignatureCacheValue {
exit_list: SignedExitServerList,
nonce: [u8; 24],
}

impl ExitContractSignatureCacheValue {
fn to_encrypted_exit_server_list(&self, their_pubkey: PublicKey) -> EncryptedExitServerList {
// we already have a signed list- now to encrypt it given the nonce & our... keys...
let plaintext = serde_json::to_string(&self.exit_list.data)
.expect("Failed to serialize ExitServerList")
.into_bytes();
// using the clarity private key as the Crypto_box SecretKey
let our_secretkey = SecretKey::from(CONFIG.clarity_private_key.to_bytes());
let b = SalsaBox::new(&their_pubkey, &our_secretkey);
let ciphertext = b.encrypt((&self.nonce).into(), plaintext.as_ref()).unwrap();
EncryptedExitServerList {
pubkey: CONFIG.wg_private_key,
nonce: self.nonce,
encrypted_exit_server_list: ciphertext,
}
}
}

// five minutes
const SIGNATURE_UPDATE_SLEEP: Duration = Duration::from_secs(300);

/// In order to improve scalability this loop grabs and signs an updated list of exits from each exit contract
/// that has previously been requested from this server every 5 minutes. This allows the server to return instantly
/// on the next request from the client without having to perform rpc query 1-1 with requests.
fn signature_update_loop(cache: Arc<RwLock<HashMap<Address, ExitContractSignatureCacheValue>>>) {
fn signature_update_loop(cache: Arc<RwLock<HashMap<Address, SignedExitServerList>>>) {
thread::spawn(move || loop {
let runner = System::new();
runner.block_on(async {
Expand Down Expand Up @@ -165,7 +131,7 @@ async fn main() -> std::io::Result<()> {
// lazy static variable after this
load_config();

let exit_contract_data_cache: Arc<RwLock<HashMap<Address, ExitContractSignatureCacheValue>>> =
let exit_contract_data_cache: Arc<RwLock<HashMap<Address, SignedExitServerList>>> =
Arc::new(RwLock::new(HashMap::new()));
signature_update_loop(exit_contract_data_cache.clone());
let web_data = web::Data::new(exit_contract_data_cache.clone());
Expand Down
10 changes: 4 additions & 6 deletions rita_client/src/exit_manager/exit_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ use actix_async::System as AsyncSystem;
use althea_kernel_interface::ip_addr::setup_ipv6_slaac as setup_ipv6_slaac_ki;
use althea_kernel_interface::ip_route::get_default_route;
use althea_kernel_interface::run_command;
use althea_types::{ExitDetails, ExitListV2};
use althea_types::ExitDetails;
use althea_types::ExitServerList;
use althea_types::{ExitIdentity, ExitState};
use rita_common::blockchain_oracle::low_balance;
use std::net::IpAddr;
Expand Down Expand Up @@ -138,14 +139,11 @@ async fn handle_exit_switching(
let exit_list = match get_exit_list(current_exit_id).await {
Ok(a) => {
info!("Received an exit list: {:?}", a);
a
a.data
}
Err(e) => {
error!("Exit_Switcher: Unable to get exit list: {:?}", e);

ExitListV2 {
exit_list: Vec::new(),
}
ExitServerList::default()
}
};

Expand Down
19 changes: 12 additions & 7 deletions rita_client/src/exit_manager/exit_selector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
use super::exit_loop::EXIT_LOOP_SPEED;
use super::utils::get_babel_routes;
use crate::RitaClientError;
use althea_types::{ExitIdentity, ExitListV2};
use althea_types::{ExitIdentity, ExitServerList};
use std::cmp::{max, min};
use std::time::Instant;
use std::{collections::HashMap, net::IpAddr, time::Duration};
Expand All @@ -15,7 +15,7 @@ use std::{collections::HashMap, net::IpAddr, time::Duration};
/// quality and sets it as the currently selected exit in the exit manager
pub fn select_best_exit(
switcher_state: &mut ExitSwitcherState,
exit_list: ExitListV2,
exit_list: ExitServerList,
babel_port: u16,
) {
let current_exit = switcher_state.currently_selected.clone();
Expand Down Expand Up @@ -205,7 +205,7 @@ pub const MAX_QUALITY_SAMPLE_AGE: Duration = Duration::from_secs(900);
/// we can't reach that exit / it is down
pub fn get_and_merge_routes_for_exit_list(
quality_history: &mut HashMap<ExitIdentity, Vec<ExitQualitySample>>,
exit_list: ExitListV2,
exit_list: ExitServerList,
babel_port: u16,
) -> Result<(), RitaClientError> {
let routes = get_babel_routes(babel_port)?;
Expand Down Expand Up @@ -272,9 +272,10 @@ fn exit_is_valid_for_us(exit: ExitIdentity) -> bool {
#[cfg(test)]
mod tests {
use super::*;
use althea_types::{ExitIdentity, ExitListV2};
use althea_types::{ExitIdentity, ExitServerList};
use clarity::Address;
use std::collections::HashSet;
use std::time::{Duration, Instant};
use std::time::{Duration, Instant, SystemTime};

/// generates a random identity, never use in production, your money will be stolen
pub fn random_exit_identity() -> ExitIdentity {
Expand Down Expand Up @@ -311,8 +312,10 @@ mod tests {
quality_history: HashMap::new(),
};

let exit_list = ExitListV2 {
let exit_list = ExitServerList {
exit_list: vec![random_exit_identity(), random_exit_identity()],
contract: Address::default(),
created: SystemTime::now(),
};

// Simulate route qualities
Expand Down Expand Up @@ -408,8 +411,10 @@ mod tests {
quality_history: HashMap::new(),
};

let exit_list = ExitListV2 {
let exit_list = ExitServerList {
exit_list: vec![random_exit_identity(), random_exit_identity()],
contract: Address::default(),
created: SystemTime::now(),
};

// Simulate route qualities
Expand Down
6 changes: 3 additions & 3 deletions rita_client/src/exit_manager/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ pub mod time_sync;
pub mod utils;

use althea_types::ExitIdentity;
use althea_types::ExitListV2;
use althea_types::ExitServerList;
use althea_types::ExitState;
use exit_selector::ExitSwitcherState;
use std::collections::HashMap;
Expand All @@ -46,7 +46,7 @@ pub struct LastExitStates {
pub struct ExitManager {
pub nat_setup: bool,
/// Every tick we query an exit endpoint to get a list of exits in that cluster. We use this list for exit switching
pub exit_list: ExitListV2,
pub exit_list: ExitServerList,
/// Store last exit here, when we see an exit change, we reset wg tunnels
pub last_exit_state: Option<LastExitStates>,
pub last_status_request: Option<Instant>,
Expand All @@ -57,7 +57,7 @@ impl ExitManager {
pub fn new(currently_selected: ExitIdentity) -> ExitManager {
ExitManager {
nat_setup: false,
exit_list: ExitListV2::default(),
exit_list: ExitServerList::default(),
last_exit_state: None,
last_status_request: None,
exit_switcher_state: ExitSwitcherState {
Expand Down
Loading

0 comments on commit 15d5381

Please sign in to comment.