183 lines
6.4 KiB
Rust
183 lines
6.4 KiB
Rust
pub mod app;
|
|
pub mod general;
|
|
pub mod types;
|
|
pub mod vm;
|
|
|
|
use crate::constants::ADMIN_ACCOUNTS;
|
|
use detee_shared::app_proto::*;
|
|
use detee_shared::common_proto::{Empty, Pubkey};
|
|
use detee_shared::general_proto::{
|
|
AirdropReq, BanUserReq, KickReq, RegOperatorReq, ReportNodeReq, SlashReq,
|
|
};
|
|
use detee_shared::vm_proto::*;
|
|
use tonic::{Request, Status};
|
|
|
|
pub trait PubkeyGetter {
|
|
fn get_pubkey(&self) -> Option<String>;
|
|
}
|
|
|
|
macro_rules! impl_pubkey_getter {
|
|
($t:ty, $field:ident) => {
|
|
impl PubkeyGetter for $t {
|
|
fn get_pubkey(&self) -> Option<String> {
|
|
Some(self.$field.clone())
|
|
}
|
|
}
|
|
};
|
|
($t:ty) => {
|
|
impl PubkeyGetter for $t {
|
|
fn get_pubkey(&self) -> Option<String> {
|
|
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<T: std::fmt::Debug + PubkeyGetter>(req: Request<T>) -> Result<T, Status> {
|
|
log::trace!("Checking signature from request: {:?}", req);
|
|
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> {
|
|
log::trace!(
|
|
"Checking signature from parts: pubkey: {pubkey}, time: {time}, msg: {msg}, sig: {sig}"
|
|
);
|
|
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<T>(req: &Request<T>) -> 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"))?
|
|
.to_owned();
|
|
|
|
if !ADMIN_ACCOUNTS.contains(&pubkey) {
|
|
return Err(Status::unauthenticated("This operation is reserved to admin accounts"));
|
|
}
|
|
|
|
Ok(())
|
|
}
|