use crate::app_config::AppConfig; use crate::crypto::pki; use crate::devices::device::{Device, DeviceId}; use crate::energy::energy_actor; use crate::server::WebEnergyActor; use jsonwebtoken::{Algorithm, DecodingKey, Validation}; use openssl::x509::X509; use serde::de::DeserializeOwned; use std::collections::HashSet; #[derive(thiserror::Error, Debug)] pub enum JWTError { #[error("Failed to decode JWT header")] FailedDecodeJWT, #[error("Missing KID in JWT!")] MissingKidInJWT, #[error("Sent a JWT for a device which does not exists!")] DeviceDoesNotExists, #[error("Sent a JWT for a device which is not validated!")] DeviceNotValidated, #[error("Sent a JWT using a revoked certificate!")] RevokedCertificate, #[error("Failed to validate JWT!")] FailedValidateJWT, } #[derive(serde::Deserialize)] pub struct JWTRequest { pub payload: String, } impl JWTRequest { pub async fn parse_jwt( &self, actor: WebEnergyActor, ) -> anyhow::Result<(Device, E)> { // First, we need to extract device kid from query let Ok(jwt_header) = jsonwebtoken::decode_header(&self.payload) else { log::error!("Failed to decode JWT header!"); return Err(JWTError::FailedDecodeJWT.into()); }; let Some(kid) = jwt_header.kid else { log::error!("Missing KID in JWT!"); return Err(JWTError::MissingKidInJWT.into()); }; // Fetch device information let Some(device) = actor .send(energy_actor::GetSingleDevice(DeviceId(kid))) .await? else { log::error!("Sent a JWT for a device which does not exists!"); return Err(JWTError::DeviceDoesNotExists.into()); }; if !device.validated { log::error!("Sent a JWT for a device which is not validated!"); return Err(JWTError::DeviceNotValidated.into()); } // Check certificate revocation status let cert_bytes = std::fs::read(AppConfig::get().device_cert_path(&device.id))?; let certificate = X509::from_pem(&cert_bytes)?; if pki::CertData::load_devices_ca()?.is_revoked(&certificate)? { log::error!("Sent a JWT using a revoked certificate!"); return Err(JWTError::RevokedCertificate.into()); } let (key, alg) = match DecodingKey::from_ec_pem(&cert_bytes) { Ok(key) => (key, Algorithm::ES256), Err(e) => { log::warn!("Failed to decode certificate as EC certificate {e}, trying RSA..."); ( DecodingKey::from_rsa_pem(&cert_bytes) .expect("Failed to decode RSA certificate"), Algorithm::RS256, ) } }; let mut validation = Validation::new(alg); validation.validate_exp = false; validation.required_spec_claims = HashSet::default(); let c = match jsonwebtoken::decode::(&self.payload, &key, &validation) { Ok(c) => c, Err(e) => { log::error!("Failed to validate JWT! {e}"); return Err(JWTError::FailedValidateJWT.into()); } }; Ok((device, c.claims)) } }