97 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			97 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| 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<E: DeserializeOwned>(
 | |
|         &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::<E>(&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))
 | |
|     }
 | |
| }
 |