Re-Add length bytes to keys endpoint

This commit is contained in:
Bastian Gruber 2025-11-23 16:27:24 -04:00
parent 8a68df9e54
commit 367ff8b10e
Signed by: gruberb
GPG key ID: 426AF1CBA0530691
5 changed files with 89 additions and 12 deletions

View file

@ -0,0 +1,63 @@
use ohttp_gateway::key_manager::{KeyManager, KeyManagerConfig};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create a key manager with default configuration
let config = KeyManagerConfig::default();
let manager = KeyManager::new(config).await?;
// Get the encoded configuration
let encoded_config = manager.get_encoded_config().await?;
println!("Key configuration format verification:");
println!("=====================================");
// Check that we have at least 2 bytes for the length prefix
if encoded_config.len() < 2 {
println!("❌ ERROR: Configuration too short (< 2 bytes)");
return Ok(());
}
// Extract the length prefix (first 2 bytes in network byte order)
let length_prefix = u16::from_be_bytes([encoded_config[0], encoded_config[1]]);
let actual_config_length = encoded_config.len() - 2; // Total length minus 2-byte prefix
println!(
"Total encoded config length: {} bytes",
encoded_config.len()
);
println!("Length prefix value: {} bytes", length_prefix);
println!("Actual config data length: {} bytes", actual_config_length);
// Verify the length prefix matches the actual config data length
if length_prefix as usize == actual_config_length {
println!("✅ SUCCESS: Length prefix matches actual config data length");
println!("✅ SUCCESS: Key configuration format is RFC 9458 compliant");
} else {
println!(
"❌ ERROR: Length prefix ({}) doesn't match actual config data length ({})",
length_prefix, actual_config_length
);
return Ok(());
}
// Display the first few bytes of the configuration for inspection
println!("\nFirst 16 bytes of encoded configuration (hex):");
let display_bytes = std::cmp::min(16, encoded_config.len());
for (i, byte) in encoded_config[..display_bytes].iter().enumerate() {
if i == 2 {
print!(" | "); // Separator between length prefix and config data
} else if i > 2 && (i - 2) % 4 == 0 {
print!(" ");
}
print!("{:02x}", byte);
}
println!();
println!(" ^^ ^^");
println!(" Length prefix");
println!(" (2 bytes, big-endian)");
println!("\nFormat verification complete!");
Ok(())
}

View file

@ -206,7 +206,7 @@ impl KeyManager {
keys.get(&key_id).map(|info| info.server.clone()) keys.get(&key_id).map(|info| info.server.clone())
} }
/// Get encoded config for backward compatibility /// Get encoded config with length prefix per RFC 9458 Section 3.2
pub async fn get_encoded_config(&self) -> Result<Vec<u8>, Box<dyn std::error::Error>> { pub async fn get_encoded_config(&self) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
let keys = self.keys.read().await; let keys = self.keys.read().await;
let active_id = self.active_key_id.read().await; let active_id = self.active_key_id.read().await;
@ -216,7 +216,9 @@ impl KeyManager {
.config .config
.encode()?; .encode()?;
let mut out = Vec::with_capacity(cfg_bytes.len()); let mut out = Vec::with_capacity(2 + cfg_bytes.len());
// Add 2-byte length prefix in network byte order per RFC 9458
out.extend_from_slice(&(cfg_bytes.len() as u16).to_be_bytes());
out.extend_from_slice(&cfg_bytes); out.extend_from_slice(&cfg_bytes);
Ok(out) Ok(out)
} }

View file

@ -1,4 +1,4 @@
use hyper::StatusCode; use axum::http::StatusCode;
use rand::Rng; use rand::Rng;
use ohttp_gateway::{key_manager::KeyManager, key_manager::KeyManagerConfig}; use ohttp_gateway::{key_manager::KeyManager, key_manager::KeyManagerConfig};
@ -90,9 +90,13 @@ async fn test_config_handler() {
let cache_control = response.get_header("Cache-Control").unwrap(); let cache_control = response.get_header("Cache-Control").unwrap();
validate_cache_control_header(cache_control).unwrap(); validate_cache_control_header(cache_control).unwrap();
// Check body is not empty and has expected structure // Check body is not empty and has expected structure with 2-byte length prefix
assert!(!response.body.is_empty()); assert!(!response.body.is_empty());
assert!(response.body.len() >= 4); // At least length prefix + some config data assert!(response.body.len() >= 4); // At least 2-byte length prefix + some config data
// Verify the length prefix is correct per RFC 9458
let length_prefix = u16::from_be_bytes([response.body[0], response.body[1]]);
assert_eq!(length_prefix as usize, response.body.len() - 2);
} }
#[tokio::test] #[tokio::test]

View file

@ -162,15 +162,19 @@ async fn test_config_serialization_format() {
let encoded_config = manager.get_encoded_config().await.unwrap(); let encoded_config = manager.get_encoded_config().await.unwrap();
// Verify we have config data (no length prefix anymore) // Verify we have config data with 2-byte length prefix per RFC 9458
assert!(!encoded_config.is_empty()); assert!(!encoded_config.is_empty());
assert!(encoded_config.len() > 10); assert!(encoded_config.len() > 12); // At least 2 bytes for length + some config data
// Verify the length prefix is correct
let length_prefix = u16::from_be_bytes([encoded_config[0], encoded_config[1]]);
assert_eq!(length_prefix as usize, encoded_config.len() - 2);
// Verify it contains reasonable OHTTP key configuration data // Verify it contains reasonable OHTTP key configuration data
assert!(encoded_config.len() < 1000); // Reasonable upper bound assert!(encoded_config.len() < 1000); // Reasonable upper bound
// The config should be the raw encoded configuration // The config should be the length-prefixed encoded configuration
let config_data = &encoded_config; let config_data = &encoded_config[2..]; // Skip the length prefix
assert!(!config_data.is_empty()); assert!(!config_data.is_empty());
} }

View file

@ -112,11 +112,15 @@ async fn test_get_encoded_config() {
let encoded_config = manager.get_encoded_config().await.unwrap(); let encoded_config = manager.get_encoded_config().await.unwrap();
// Should have some config data (no length prefix anymore) // Should have config data with 2-byte length prefix per RFC 9458
assert!(!encoded_config.is_empty()); assert!(!encoded_config.is_empty());
assert!(encoded_config.len() > 0); assert!(encoded_config.len() > 2);
// The encoded config should be the raw config bytes without length prefix // Verify the length prefix is correct
let length_prefix = u16::from_be_bytes([encoded_config[0], encoded_config[1]]);
assert_eq!(length_prefix as usize, encoded_config.len() - 2);
// The encoded config should be the length-prefixed config bytes
// We can't easily verify the exact content without duplicating the encoding logic, // We can't easily verify the exact content without duplicating the encoding logic,
// but we can at least verify it's reasonable in size // but we can at least verify it's reasonable in size
assert!(encoded_config.len() < 1000); // Reasonable upper bound assert!(encoded_config.len() < 1000); // Reasonable upper bound