Skip to content

Commit 61fc1b2

Browse files
refactor: move auth types to core keylime library
Move authentication types (AuthConfig, SessionToken, TpmOperations) from keylime-push-model-agent to the core keylime library to enable sharing between different components. This refactoring creates a clean foundation for the authentication middleware integration. Signed-off-by: Sergio Correia <[email protected]>
1 parent 06d277f commit 61fc1b2

File tree

3 files changed

+60
-135
lines changed

3 files changed

+60
-135
lines changed

keylime-push-model-agent/src/main.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ use clap::Parser;
55
use keylime::config::PushModelConfigTrait;
66
use log::{debug, error, info};
77
mod attestation;
8-
mod auth;
98
mod context_info_handler;
109
mod registration;
1110
mod response_handler;

keylime-push-model-agent/src/auth.rs renamed to keylime/src/auth.rs

Lines changed: 59 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,25 @@
11
// SPDX-License-Identifier: Apache-2.0
22
// Copyright 2025 Keylime Authors
33

4-
//! Challenge-Response Authentication Module
4+
//! Authentication types and utilities
55
//!
6-
//! This module implements the challenge-response authentication protocol
7-
//! as described in Keylime Enhancement 103. It provides a standalone
8-
//! authentication client that can be used independently or integrated
9-
//! with existing HTTP clients.
6+
//! This module provides common types and utilities for authentication
7+
//! that are shared between different components of the Keylime system.
108
11-
use anyhow::{anyhow, Result};
12-
use chrono::{DateTime, Duration, Utc};
13-
use keylime::structures::{
9+
use crate::structures::{
1410
ProofOfPossession, SessionIdResponse, SessionRequest,
1511
SessionRequestAttributes, SessionRequestData, SessionResponse,
1612
SupportedAuthMethod,
1713
};
14+
use anyhow::{anyhow, Result};
15+
use chrono::{DateTime, Duration, Utc};
1816
use log::{debug, info, warn};
19-
use reqwest::{Client, Method, StatusCode};
17+
use reqwest::Client;
2018
use std::sync::Arc;
2119
use tokio::sync::Mutex;
2220

23-
/// Configuration for the authentication client
21+
/// Configuration for authentication
2422
#[derive(Debug, Clone)]
25-
#[allow(dead_code)] // TODO: Remove when auth client is integrated
2623
pub struct AuthConfig {
2724
/// Base URL of the verifier (e.g., "https://verifier.example.com")
2825
pub verifier_base_url: String,
@@ -53,34 +50,37 @@ impl Default for AuthConfig {
5350

5451
/// Session token with expiration information
5552
#[derive(Debug, Clone)]
56-
#[allow(dead_code)] // TODO: Remove when auth client is integrated
57-
struct SessionToken {
58-
token: String,
59-
expires_at: DateTime<Utc>,
60-
session_id: u64,
53+
pub struct SessionToken {
54+
pub token: String,
55+
pub expires_at: DateTime<Utc>,
56+
pub session_id: u64,
6157
}
6258

6359
impl SessionToken {
64-
#[allow(dead_code)] // TODO: Remove when auth client is integrated
65-
fn is_valid(&self, buffer_minutes: i64) -> bool {
60+
pub fn is_valid(&self, buffer_minutes: i64) -> bool {
6661
let buffer = Duration::minutes(buffer_minutes);
6762
Utc::now() + buffer < self.expires_at
6863
}
6964
}
7065

7166
/// Mock TPM operations for testing
72-
#[allow(dead_code)] // TODO: Remove when auth client is integrated
7367
pub trait TpmOperations: Send + Sync {
74-
fn generate_proof(&self, challenge: &str) -> Result<ProofOfPossession>;
68+
fn generate_proof(
69+
&self,
70+
challenge: &str,
71+
) -> Result<crate::structures::ProofOfPossession>;
7572
}
7673

7774
/// Default mock TPM implementation
7875
#[derive(Debug, Clone)]
79-
#[allow(dead_code)] // TODO: Remove when auth client is integrated
8076
pub struct MockTpmOperations;
8177

8278
impl TpmOperations for MockTpmOperations {
83-
fn generate_proof(&self, challenge: &str) -> Result<ProofOfPossession> {
79+
fn generate_proof(
80+
&self,
81+
challenge: &str,
82+
) -> Result<crate::structures::ProofOfPossession> {
83+
use log::debug;
8484
debug!("Generating mock TPM proof for challenge: {challenge}");
8585

8686
// Create a deterministic but unique proof based on the challenge
@@ -89,23 +89,32 @@ impl TpmOperations for MockTpmOperations {
8989

9090
use base64::{engine::general_purpose, Engine as _};
9191

92-
Ok(ProofOfPossession {
92+
Ok(crate::structures::ProofOfPossession {
9393
message: general_purpose::STANDARD.encode(message),
9494
signature: general_purpose::STANDARD.encode(signature),
9595
})
9696
}
9797
}
9898

9999
/// Standalone authentication client implementing the challenge-response protocol
100-
#[allow(dead_code)] // TODO: Remove when auth client is integrated
101100
pub struct AuthenticationClient {
102101
config: AuthConfig,
103102
http_client: Client,
104103
session_token: Arc<Mutex<Option<SessionToken>>>,
105104
tpm_ops: Box<dyn TpmOperations>,
106105
}
107106

108-
#[allow(dead_code)] // TODO: Remove when auth client is integrated
107+
impl std::fmt::Debug for AuthenticationClient {
108+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
109+
f.debug_struct("AuthenticationClient")
110+
.field("config", &self.config)
111+
.field("http_client", &"<Client>")
112+
.field("session_token", &"<Arc<Mutex<SessionToken>>>")
113+
.field("tpm_ops", &"<Box<dyn TpmOperations>>")
114+
.finish()
115+
}
116+
}
117+
109118
impl AuthenticationClient {
110119
/// Create a new authentication client with the given configuration
111120
pub fn new(config: AuthConfig) -> Result<Self> {
@@ -178,45 +187,9 @@ impl AuthenticationClient {
178187
})
179188
}
180189

181-
/// Get a valid authentication token, performing authentication if necessary
182-
pub async fn get_auth_token(&self) -> Result<String> {
183-
let token_guard = self.session_token.lock().await;
184-
185-
// Check if we have a valid token
186-
if let Some(ref token) = *token_guard {
187-
if token.is_valid(self.config.token_refresh_buffer_minutes) {
188-
debug!("Using existing valid token");
189-
return Ok(token.token.clone());
190-
} else {
191-
debug!(
192-
"Token expired or expiring soon, need to re-authenticate"
193-
);
194-
}
195-
} else {
196-
debug!("No token available, need to authenticate");
197-
}
198-
199-
drop(token_guard); // Release lock before authentication
200-
201-
// Perform authentication
202-
self.authenticate().await
203-
}
204-
205-
/// Check if we currently have a valid token
206-
pub async fn has_valid_token(&self) -> bool {
207-
let token_guard = self.session_token.lock().await;
208-
if let Some(ref token) = *token_guard {
209-
token.is_valid(self.config.token_refresh_buffer_minutes)
210-
} else {
211-
false
212-
}
213-
}
214-
215-
/// Clear the current token (e.g., after receiving 401)
216-
pub async fn clear_token(&self) {
217-
let mut token_guard = self.session_token.lock().await;
218-
*token_guard = None;
219-
debug!("Authentication token cleared");
190+
/// Get the authentication configuration
191+
pub fn config(&self) -> &AuthConfig {
192+
&self.config
220193
}
221194

222195
/// Get a valid authentication token with metadata (token, expiration, session_id)
@@ -494,41 +467,12 @@ impl AuthenticationClient {
494467

495468
Ok(token.clone())
496469
}
497-
498-
/// Make an authenticated HTTP request (convenience method for testing)
499-
pub async fn make_authenticated_request(
500-
&self,
501-
method: Method,
502-
url: &str,
503-
body: Option<String>,
504-
) -> Result<reqwest::Response> {
505-
let token = self.get_auth_token().await?;
506-
507-
let mut request = self.http_client.request(method, url);
508-
request = request.header("Authorization", format!("Bearer {token}"));
509-
510-
if let Some(body) = body {
511-
request = request
512-
.header("Content-Type", "application/vnd.api+json")
513-
.body(body);
514-
}
515-
516-
let response = request.send().await?;
517-
518-
// Handle 401 responses by clearing token
519-
if response.status() == StatusCode::UNAUTHORIZED {
520-
warn!("Received 401, clearing token");
521-
self.clear_token().await;
522-
return Err(anyhow!("Authentication token was rejected (401)"));
523-
}
524-
525-
Ok(response)
526-
}
527470
}
528471

529472
#[cfg(test)]
530473
mod tests {
531474
use super::*;
475+
use serde_json;
532476
use wiremock::matchers::{header, method, path};
533477
use wiremock::{Mock, MockServer, ResponseTemplate};
534478

@@ -544,7 +488,7 @@ mod tests {
544488
max_auth_retries: 2,
545489
};
546490

547-
AuthenticationClient::new(config).unwrap()
491+
AuthenticationClient::new(config).unwrap() //#[allow_ci]
548492
}
549493

550494
#[tokio::test]
@@ -615,16 +559,16 @@ mod tests {
615559

616560
let client = create_test_client(&mock_server.uri()).await;
617561

618-
// Test authentication
619-
let token = client.get_auth_token().await.unwrap();
562+
// Test authentication - get token with metadata since that's our main method
563+
let (token, _expires_at, session_id) =
564+
client.get_auth_token_with_metadata().await.unwrap(); //#[allow_ci]
620565
assert_eq!(token, "test-token-456");
566+
assert_eq!(session_id, 1);
621567

622-
// Test that token is cached
623-
assert!(client.has_valid_token().await);
624-
625-
// Test that subsequent calls use cached token
626-
let token2 = client.get_auth_token().await.unwrap();
627-
assert_eq!(token2, "test-token-456");
568+
// Verify token is valid
569+
let token_guard = client.session_token.lock().await;
570+
let session_token = token_guard.as_ref().unwrap(); //#[allow_ci]
571+
assert!(session_token.is_valid(5));
628572
}
629573

630574
#[tokio::test]
@@ -691,10 +635,10 @@ mod tests {
691635

692636
let client = create_test_client(&mock_server.uri()).await;
693637

694-
let result = client.get_auth_token().await;
638+
let result = client.get_auth_token_with_metadata().await;
695639
assert!(result.is_err());
696640
assert!(result
697-
.unwrap_err()
641+
.unwrap_err() //#[allow_ci]
698642
.to_string()
699643
.contains("Authentication failed"));
700644
}
@@ -726,7 +670,7 @@ mod tests {
726670
}
727671
}),
728672
))
729-
.expect(1..) // May be called multiple times
673+
.expect(1..) //#[allow_ci] // May be called multiple times
730674
.mount(&mock_server)
731675
.await;
732676

@@ -762,7 +706,7 @@ mod tests {
762706
}
763707
}),
764708
))
765-
.expect(1..) // May be called multiple times
709+
.expect(1..) //#[allow_ci] // May be called multiple times
766710
.mount(&mock_server)
767711
.await;
768712

@@ -775,37 +719,18 @@ mod tests {
775719
max_auth_retries: 2,
776720
};
777721

778-
let client = AuthenticationClient::new(config).unwrap();
722+
let client = AuthenticationClient::new(config).unwrap(); //#[allow_ci]
779723

780724
// Since token expires in 1 minute but we have 5 minute buffer,
781725
// it should be considered invalid and trigger re-authentication
782-
let token = client.get_auth_token().await.unwrap();
726+
let (token, _, _) =
727+
client.get_auth_token_with_metadata().await.unwrap(); //#[allow_ci]
783728
assert_eq!(token, "short-lived-token");
784729

785730
// Check that token is considered invalid due to buffer
786-
assert!(!client.has_valid_token().await);
787-
}
788-
789-
#[tokio::test]
790-
async fn test_clear_token() {
791-
let mock_server = MockServer::start().await;
792-
let client = create_test_client(&mock_server.uri()).await;
793-
794-
// Manually insert a token
795-
{
796-
let mut token_guard = client.session_token.lock().await;
797-
*token_guard = Some(SessionToken {
798-
token: "test-token".to_string(),
799-
expires_at: Utc::now() + Duration::hours(1),
800-
session_id: 1,
801-
});
802-
}
803-
804-
assert!(client.has_valid_token().await);
805-
806-
client.clear_token().await;
807-
808-
assert!(!client.has_valid_token().await);
731+
let token_guard = client.session_token.lock().await;
732+
let session_token = token_guard.as_ref().unwrap(); //#[allow_ci]
733+
assert!(!session_token.is_valid(5));
809734
}
810735

811736
#[tokio::test]
@@ -819,7 +744,7 @@ mod tests {
819744
max_auth_retries: 2,
820745
};
821746

822-
let raw_client = AuthenticationClient::new_raw(config).unwrap();
747+
let raw_client = AuthenticationClient::new_raw(config).unwrap(); //#[allow_ci]
823748

824749
// Verify the client was created successfully
825750
assert_eq!(raw_client.config.agent_id, "test-agent-raw");
@@ -843,7 +768,7 @@ mod tests {
843768
config,
844769
custom_tpm_ops,
845770
)
846-
.unwrap();
771+
.unwrap(); //#[allow_ci]
847772

848773
// Verify the client was created successfully
849774
assert_eq!(raw_client.config.agent_id, "test-agent-raw-tpm");

keylime/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ pub mod agent_data;
22
pub mod agent_identity;
33
pub mod agent_registration;
44
pub mod algorithms;
5+
pub mod auth;
56
pub mod boot_time;
67
pub mod cert;
78
pub mod config;

0 commit comments

Comments
 (0)