pub mod app; pub mod general; pub mod types; pub mod vm; use crate::constants::ADMIN_ACCOUNTS; use detee_shared::common_proto::{Empty, Pubkey}; use detee_shared::general_proto::{ AirdropReq, BanUserReq, KickReq, RegOperatorReq, ReportNodeReq, SlashReq, }; use detee_shared::vm_proto::{ListVmContractsReq, *}; use tonic::{Request, Status}; pub trait PubkeyGetter { fn get_pubkey(&self) -> Option; } macro_rules! impl_pubkey_getter { ($t:ty, $field:ident) => { impl PubkeyGetter for $t { fn get_pubkey(&self) -> Option { Some(self.$field.clone()) } } }; ($t:ty) => { impl PubkeyGetter for $t { fn get_pubkey(&self) -> Option { None } } }; } impl_pubkey_getter!(Pubkey, pubkey); impl_pubkey_getter!(NewVmReq, admin_pubkey); impl_pubkey_getter!(DeleteVmReq, admin_pubkey); impl_pubkey_getter!(UpdateVmReq, admin_pubkey); impl_pubkey_getter!(ExtendVmReq, admin_pubkey); impl_pubkey_getter!(ReportNodeReq, admin_pubkey); impl_pubkey_getter!(ListVmContractsReq, wallet); impl_pubkey_getter!(RegisterVmNodeReq, node_pubkey); impl_pubkey_getter!(RegOperatorReq, pubkey); impl_pubkey_getter!(KickReq, operator_wallet); impl_pubkey_getter!(BanUserReq, operator_wallet); impl_pubkey_getter!(VmNodeFilters); impl_pubkey_getter!(Empty); impl_pubkey_getter!(AirdropReq); impl_pubkey_getter!(SlashReq); // impl_pubkey_getter!(NewAppReq, admin_pubkey); // impl_pubkey_getter!(DelAppReq, admin_pubkey); // impl_pubkey_getter!(ListAppContractsReq, admin_pubkey); // // impl_pubkey_getter!(RegisterAppNodeReq); // impl_pubkey_getter!(AppNodeFilters); pub fn check_sig_from_req(req: Request) -> Result { let time = match req.metadata().get("timestamp") { Some(t) => t.clone(), None => return Err(Status::unauthenticated("Timestamp not found in metadata.")), }; let time = time .to_str() .map_err(|_| Status::unauthenticated("Timestamp in metadata is not a string"))?; let now = chrono::Utc::now(); let parsed_time = chrono::DateTime::parse_from_rfc3339(time) .map_err(|_| Status::unauthenticated("Coult not parse timestamp"))?; let seconds_elapsed = now.signed_duration_since(parsed_time).num_seconds(); if !(-4..=4).contains(&seconds_elapsed) { return Err(Status::unauthenticated(format!( "Date is not within 4 sec of the time of the server: CLI {} vs Server {}", parsed_time, now ))); } let signature = match req.metadata().get("request-signature") { Some(t) => t, None => return Err(Status::unauthenticated("signature not found in metadata.")), }; let signature = bs58::decode(signature) .into_vec() .map_err(|_| Status::unauthenticated("signature is not a bs58 string"))?; let signature = ed25519_dalek::Signature::from_bytes( signature .as_slice() .try_into() .map_err(|_| Status::unauthenticated("could not parse ed25519 signature"))?, ); let pubkey_value = match req.metadata().get("pubkey") { Some(p) => p.clone(), None => return Err(Status::unauthenticated("pubkey not found in metadata.")), }; let pubkey = ed25519_dalek::VerifyingKey::from_bytes( &bs58::decode(&pubkey_value) .into_vec() .map_err(|_| Status::unauthenticated("pubkey is not a bs58 string"))? .try_into() .map_err(|_| Status::unauthenticated("pubkey does not have the correct size."))?, ) .map_err(|_| Status::unauthenticated("could not parse ed25519 pubkey"))?; let req = req.into_inner(); let message = format!("{time}{req:?}"); use ed25519_dalek::Verifier; pubkey .verify(message.as_bytes(), &signature) .map_err(|_| Status::unauthenticated("the signature is not valid"))?; if let Some(req_pubkey) = req.get_pubkey() { if *pubkey_value.to_str().unwrap() != req_pubkey { return Err(Status::unauthenticated( "pubkey of signature does not match pubkey of request", )); } } Ok(req) } pub fn check_sig_from_parts(pubkey: &str, time: &str, msg: &str, sig: &str) -> Result<(), Status> { let now = chrono::Utc::now(); let parsed_time = chrono::DateTime::parse_from_rfc3339(time) .map_err(|_| Status::unauthenticated("Coult not parse timestamp"))?; let seconds_elapsed = now.signed_duration_since(parsed_time).num_seconds(); if !(-4..=4).contains(&seconds_elapsed) { return Err(Status::unauthenticated(format!( "Date is not within 4 sec of the time of the server: CLI {} vs Server {}", parsed_time, now ))); } let signature = bs58::decode(sig) .into_vec() .map_err(|_| Status::unauthenticated("signature is not a bs58 string"))?; let signature = ed25519_dalek::Signature::from_bytes( signature .as_slice() .try_into() .map_err(|_| Status::unauthenticated("could not parse ed25519 signature"))?, ); let pubkey = ed25519_dalek::VerifyingKey::from_bytes( &bs58::decode(&pubkey) .into_vec() .map_err(|_| Status::unauthenticated("pubkey is not a bs58 string"))? .try_into() .map_err(|_| Status::unauthenticated("pubkey does not have the correct size."))?, ) .map_err(|_| Status::unauthenticated("could not parse ed25519 pubkey"))?; let msg = time.to_string() + msg; use ed25519_dalek::Verifier; pubkey .verify(msg.as_bytes(), &signature) .map_err(|_| Status::unauthenticated("the signature is not valid"))?; Ok(()) } pub fn check_admin_key(req: &Request) -> Result<(), Status> { let pubkey = match req.metadata().get("pubkey") { Some(p) => p.clone(), None => return Err(Status::unauthenticated("pubkey not found in metadata.")), }; let pubkey = pubkey .to_str() .map_err(|_| Status::unauthenticated("could not parse pubkey metadata to str"))?; if !ADMIN_ACCOUNTS.contains(&pubkey) { return Err(Status::unauthenticated("This operation is reserved to admin accounts")); } Ok(()) }