Development over

This commit is contained in:
2026-01-15 22:40:51 +01:00
parent 2f6b16d946
commit 1e7f296635
63 changed files with 12945 additions and 331 deletions

View File

@@ -1,11 +1,88 @@
//! Configuration for Linux Hello
//! Configuration Module for Linux Hello
//!
//! This module provides configuration structures for all Linux Hello components.
//! Configuration is stored in TOML format and supports sensible defaults.
//!
//! # Configuration File Location
//!
//! The default configuration file is located at `/etc/linux-hello/config.toml`.
//!
//! # Configuration Sections
//!
//! - **general** - Logging and timeout settings
//! - **camera** - Camera device selection and resolution
//! - **detection** - Face detection model and thresholds
//! - **embedding** - Face embedding extraction settings
//! - **anti_spoofing** - Liveness detection configuration
//! - **tpm** - TPM2 hardware security settings
//!
//! # Example Configuration
//!
//! ```toml
//! [general]
//! log_level = "info"
//! timeout_seconds = 5
//!
//! [camera]
//! device = "auto"
//! ir_emitter = "auto"
//! resolution = [640, 480]
//! fps = 30
//!
//! [detection]
//! model = "blazeface"
//! min_face_size = 80
//! confidence_threshold = 0.9
//!
//! [embedding]
//! model = "mobilefacenet"
//! distance_threshold = 0.6
//!
//! [anti_spoofing]
//! enabled = true
//! depth_check = true
//! liveness_model = true
//! temporal_check = true
//! min_score = 0.7
//!
//! [tpm]
//! enabled = true
//! pcr_binding = false
//! ```
//!
//! # Example Usage
//!
//! ```rust,no_run
//! use linux_hello_common::Config;
//!
//! // Load from default location with fallback to defaults
//! let config = Config::load_or_default();
//!
//! // Check settings
//! if config.anti_spoofing.enabled {
//! println!("Anti-spoofing is enabled");
//! }
//! ```
use serde::{Deserialize, Serialize};
use std::path::Path;
use crate::error::{Error, Result};
/// Main configuration structure
/// Main configuration structure for Linux Hello.
///
/// This structure contains all configuration sections for the facial
/// authentication system. All fields have sensible defaults.
///
/// # Example
///
/// ```rust
/// use linux_hello_common::Config;
///
/// let config = Config::default();
/// assert_eq!(config.general.timeout_seconds, 5);
/// assert!(config.anti_spoofing.enabled);
/// ```
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Config {
#[serde(default)]
@@ -22,62 +99,140 @@ pub struct Config {
pub tpm: TpmConfig,
}
/// General system configuration.
///
/// Controls logging verbosity and authentication timeout.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GeneralConfig {
/// Log level: "error", "warn", "info", "debug", or "trace".
/// Default: "info"
#[serde(default = "default_log_level")]
pub log_level: String,
/// Authentication timeout in seconds. If face detection or matching
/// takes longer than this, authentication fails.
/// Default: 5
#[serde(default = "default_timeout")]
pub timeout_seconds: u32,
}
/// Camera hardware configuration.
///
/// Specifies which camera device to use and capture parameters.
/// Set `device` and `ir_emitter` to "auto" for automatic detection.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CameraConfig {
/// Camera device path (e.g., "/dev/video0") or "auto" for automatic detection.
/// Auto-detection prefers IR cameras over regular webcams.
/// Default: "auto"
#[serde(default = "default_auto")]
pub device: String,
/// IR emitter control path or "auto" for automatic detection.
/// Controls the infrared LED for illumination during capture.
/// Default: "auto"
#[serde(default = "default_auto")]
pub ir_emitter: String,
/// Capture resolution as [width, height] in pixels.
/// Default: [640, 480]
#[serde(default = "default_resolution")]
pub resolution: [u32; 2],
/// Target frame rate in frames per second.
/// Default: 30
#[serde(default = "default_fps")]
pub fps: u32,
}
/// Face detection configuration.
///
/// Controls the face detection model and sensitivity thresholds.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DetectionConfig {
/// Face detection model to use: "blazeface" or "mtcnn".
/// Default: "blazeface"
#[serde(default = "default_model")]
pub model: String,
/// Minimum face size in pixels for detection.
/// Faces smaller than this are ignored.
/// Default: 80
#[serde(default = "default_min_face_size")]
pub min_face_size: u32,
/// Minimum confidence score (0.0-1.0) for accepting a face detection.
/// Higher values reduce false positives but may miss valid faces.
/// Default: 0.9
#[serde(default = "default_confidence_threshold")]
pub confidence_threshold: f32,
}
/// Face embedding extraction configuration.
///
/// Controls the neural network model used to generate face embeddings
/// and the distance threshold for matching.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EmbeddingConfig {
/// Embedding extraction model: "mobilefacenet" or "arcface".
/// Default: "mobilefacenet"
#[serde(default = "default_embedding_model")]
pub model: String,
/// Maximum cosine distance (0.0-2.0) for a successful match.
/// Lower values are more strict. A value of 0.6 means embeddings
/// must be at least 70% similar.
/// Default: 0.6
#[serde(default = "default_distance_threshold")]
pub distance_threshold: f32,
}
/// Anti-spoofing and liveness detection configuration.
///
/// Controls multiple detection methods to prevent authentication attacks
/// using photos, videos, or masks. Each method can be individually enabled.
///
/// # Security Considerations
///
/// For maximum security, enable all detection methods. However, this may
/// increase authentication time. Adjust based on your security requirements.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AntiSpoofingConfig {
/// Master switch to enable/disable all anti-spoofing checks.
/// Default: true
#[serde(default = "default_true")]
pub enabled: bool,
/// Enable depth estimation to detect flat images (photos/screens).
/// Default: true
#[serde(default = "default_true")]
pub depth_check: bool,
/// Enable ML-based liveness detection model.
/// Default: true
#[serde(default = "default_true")]
pub liveness_model: bool,
/// Enable temporal analysis (micro-movements, blink detection).
/// Requires multiple frames and increases authentication time.
/// Default: true
#[serde(default = "default_true")]
pub temporal_check: bool,
/// Minimum combined liveness score (0.0-1.0) to pass anti-spoofing.
/// Default: 0.7
#[serde(default = "default_min_score")]
pub min_score: f32,
}
/// TPM2 (Trusted Platform Module) configuration.
///
/// When enabled, face templates are encrypted using keys bound to the
/// TPM hardware, making them inaccessible on other machines.
///
/// # Hardware Requirements
///
/// Requires a TPM 2.0 chip. Most modern laptops include one.
/// Falls back to software encryption if TPM is unavailable.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TpmConfig {
/// Enable TPM-based encryption for face templates.
/// Default: true
#[serde(default = "default_true")]
pub enabled: bool,
/// Bind encryption keys to PCR (Platform Configuration Register) values.
/// If enabled, templates become inaccessible if the boot configuration changes.
/// Provides additional security but may require re-enrollment after BIOS updates.
/// Default: false
#[serde(default)]
pub pcr_binding: bool,
}

View File

@@ -1,51 +1,130 @@
//! Error types for Linux Hello
//! Error Types for Linux Hello
//!
//! This module defines all error types used throughout the Linux Hello system.
//! Errors are designed to be informative while not leaking sensitive information.
//!
//! # Error Categories
//!
//! - **Camera errors** - Hardware access, IR emitter control, device not found
//! - **Detection errors** - Face detection failures, multiple faces, no face
//! - **Authentication errors** - Template matching failures, user not enrolled
//! - **Storage errors** - Configuration, serialization, I/O failures
//! - **Security errors** - TPM failures, encryption errors
//!
//! # Example
//!
//! ```rust
//! use linux_hello_common::{Error, Result};
//!
//! fn authenticate_user(user: &str) -> Result<bool> {
//! // Simulate checking if user is enrolled
//! if user.is_empty() {
//! return Err(Error::UserNotEnrolled("unknown".to_string()));
//! }
//! Ok(true)
//! }
//!
//! match authenticate_user("") {
//! Ok(true) => println!("Authenticated!"),
//! Ok(false) => println!("Authentication failed"),
//! Err(Error::UserNotEnrolled(user)) => println!("User {} not enrolled", user),
//! Err(e) => println!("Error: {}", e),
//! }
//! ```
use thiserror::Error;
/// Main error type for Linux Hello
/// Main error type for Linux Hello.
///
/// All operations in Linux Hello return this error type or wrap it
/// in a [`Result`]. Error messages are designed to be user-friendly
/// without exposing internal implementation details.
///
/// # Security Note
///
/// Error messages intentionally avoid including sensitive information
/// like internal paths or cryptographic details to prevent information disclosure.
#[derive(Error, Debug)]
pub enum Error {
/// Camera hardware access error.
/// Includes the underlying system error message.
#[error("Camera error: {0}")]
Camera(String),
/// No suitable IR camera was detected on the system.
/// Ensure an IR camera is connected and accessible.
#[error("No IR camera found")]
NoCameraFound,
/// Failed to control the IR emitter (LED).
/// The emitter may be in use by another process.
#[error("IR emitter control failed: {0}")]
IrEmitter(String),
/// Face detection failed during processing.
/// May indicate a model loading issue or corrupted frame.
#[error("Face detection error: {0}")]
Detection(String),
/// No face was detected in the captured frame.
/// Ensure face is visible and properly lit.
#[error("No face detected in frame")]
NoFaceDetected,
/// Multiple faces were detected when only one is expected.
/// For security, authentication requires exactly one face.
#[error("Multiple faces detected")]
MultipleFacesDetected,
/// Failed to load an ML model (detection or embedding).
#[error("Model loading error: {0}")]
ModelLoad(String),
/// Configuration file parsing or validation error.
#[error("Configuration error: {0}")]
Config(String),
/// TPM (Trusted Platform Module) operation failed.
/// May indicate TPM is unavailable or key access was denied.
#[error("TPM error: {0}")]
Tpm(String),
/// Face did not match any enrolled template.
/// Generic error to prevent information disclosure.
#[error("Authentication failed")]
AuthenticationFailed,
/// The specified user has no enrolled face templates.
#[error("User not enrolled: {0}")]
UserNotEnrolled(String),
/// File system or network I/O error.
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
/// JSON/TOML serialization or deserialization error.
#[error("Serialization error: {0}")]
Serialization(String),
/// D-Bus communication error with system services.
#[error("D-Bus error: {0}")]
Dbus(String),
}
/// Result type alias for Linux Hello
/// Result type alias for Linux Hello operations.
///
/// This is a convenience alias for `std::result::Result<T, Error>`.
///
/// # Example
///
/// ```rust
/// use linux_hello_common::Result;
///
/// fn do_something() -> Result<()> {
/// // ... operation that might fail
/// Ok(())
/// }
/// ```
pub type Result<T> = std::result::Result<T, Error>;
#[cfg(test)]

View File

@@ -1,7 +1,52 @@
//! Linux Hello Common Library
//!
//! Shared types, configuration, and error handling for the Linux Hello
//! facial authentication system.
//! This crate provides shared types, configuration, and error handling for the
//! Linux Hello facial authentication system. It serves as the foundation for
//! both the daemon and CLI components.
//!
//! # Overview
//!
//! Linux Hello is a facial authentication system for Linux, similar to Windows Hello.
//! It uses IR camera technology combined with anti-spoofing measures to provide
//! secure biometric authentication.
//!
//! # Modules
//!
//! - [`config`] - Configuration structures for all system components
//! - [`error`] - Error types and result aliases
//! - [`template`] - Face template storage and management
//!
//! # Security Model
//!
//! The system implements a layered security approach:
//!
//! 1. **IR Camera Verification** - Uses infrared cameras to prevent photo attacks
//! 2. **Anti-Spoofing** - Multiple liveness detection methods (depth, texture, blink)
//! 3. **Encrypted Storage** - Templates are encrypted at rest using AES-256-GCM
//! 4. **TPM Integration** - Hardware-bound encryption when TPM2 is available
//! 5. **Memory Protection** - Sensitive data is zeroized on drop
//!
//! # Example
//!
//! ```rust,no_run
//! use linux_hello_common::{Config, TemplateStore, Result};
//!
//! fn main() -> Result<()> {
//! // Load configuration
//! let config = Config::load_or_default();
//!
//! // Initialize template storage
//! let store = TemplateStore::new(TemplateStore::default_path());
//! store.initialize()?;
//!
//! // Check if user is enrolled
//! if store.is_enrolled("john") {
//! println!("User john is enrolled");
//! }
//!
//! Ok(())
//! }
//! ```
pub mod config;
pub mod error;
@@ -9,4 +54,4 @@ pub mod template;
pub use config::Config;
pub use error::{Error, Result};
pub use template::{FaceTemplate, TemplateStore};
pub use template::{FaceTemplate, TemplateStore};

View File

@@ -1,7 +1,57 @@
//! Template Storage Module
//! Face Template Storage Module
//!
//! Handles storage and retrieval of face templates (embeddings) for enrolled users.
//! Currently uses unencrypted file-based storage. TPM encryption will be added in Phase 3.
//! This module handles storage and retrieval of face templates (embeddings) for
//! enrolled users. Templates are stored as JSON files organized by user.
//!
//! # Storage Layout
//!
//! Templates are stored in a hierarchical structure:
//!
//! ```text
//! /var/lib/linux-hello/templates/
//! john/
//! default.json
//! backup.json
//! alice/
//! default.json
//! ```
//!
//! # Security Considerations
//!
//! - This module provides unencrypted storage for templates
//! - For production use, combine with `SecureTemplateStore` from `linux-hello-daemon` for encryption
//! - Templates contain biometric data and should be protected
//! - Directory permissions should be restricted (0700)
//!
//! # Example
//!
//! ```rust,no_run
//! use linux_hello_common::{FaceTemplate, TemplateStore};
//!
//! // Create and initialize store
//! let store = TemplateStore::new("/var/lib/linux-hello/templates");
//! store.initialize().expect("Failed to create template directory");
//!
//! // Create a template
//! let template = FaceTemplate {
//! user: "john".to_string(),
//! label: "default".to_string(),
//! embedding: vec![0.1, 0.2, 0.3], // Actual embeddings are 128-512 dimensions
//! enrolled_at: 1234567890,
//! frame_count: 5,
//! };
//!
//! // Store the template
//! store.store(&template).expect("Failed to store template");
//!
//! // Check enrollment status
//! if store.is_enrolled("john") {
//! println!("User john is enrolled");
//! }
//!
//! // Load template for matching
//! let loaded = store.load("john", "default").expect("Template not found");
//! ```
use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
@@ -9,57 +59,131 @@ use std::fs;
use crate::error::{Error, Result};
/// A face template (embedding vector) for a user
/// A face template containing the embedding vector for a user.
///
/// Templates are created during enrollment by capturing multiple frames,
/// extracting face embeddings, and averaging them for robustness.
///
/// # Fields
///
/// - `user` - System username this template belongs to
/// - `label` - Identifier for multiple enrollments (e.g., "default", "glasses")
/// - `embedding` - Normalized face embedding vector (typically 128 or 512 dimensions)
/// - `enrolled_at` - Unix timestamp when the template was created
/// - `frame_count` - Number of frames used to generate the averaged embedding
///
/// # Example
///
/// ```rust
/// use linux_hello_common::FaceTemplate;
///
/// let template = FaceTemplate {
/// user: "alice".to_string(),
/// label: "default".to_string(),
/// embedding: vec![0.1, 0.2, 0.3, 0.4], // Simplified example
/// enrolled_at: std::time::SystemTime::now()
/// .duration_since(std::time::UNIX_EPOCH)
/// .unwrap()
/// .as_secs(),
/// frame_count: 5,
/// };
/// ```
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FaceTemplate {
/// User identifier
/// System username this template belongs to.
pub user: String,
/// Label for this enrollment (e.g., "default", "backup")
/// Label for this enrollment (e.g., "default", "glasses", "backup").
/// Users can have multiple templates with different labels.
pub label: String,
/// Face embedding vector (normalized)
/// Normalized face embedding vector.
/// Typically 128 dimensions (MobileFaceNet) or 512 dimensions (ArcFace).
pub embedding: Vec<f32>,
/// Timestamp when enrolled
/// Unix timestamp when this template was enrolled.
pub enrolled_at: u64,
/// Number of frames used to generate this template
/// Number of frames averaged to create this template.
/// More frames generally produce more robust templates.
pub frame_count: u32,
}
/// Template storage manager
/// File-based template storage manager.
///
/// Manages face templates on the filesystem with operations for storing,
/// loading, listing, and removing templates.
///
/// # Thread Safety
///
/// This struct is not thread-safe. For concurrent access, use external
/// synchronization or wrap in a `Mutex`.
pub struct TemplateStore {
/// Base directory for template storage
base_path: PathBuf,
}
impl TemplateStore {
/// Create a new template store
/// Create a new template store at the specified path.
///
/// # Arguments
///
/// * `base_path` - Directory where templates will be stored
///
/// # Example
///
/// ```rust
/// use linux_hello_common::TemplateStore;
///
/// let store = TemplateStore::new("/var/lib/linux-hello/templates");
/// ```
pub fn new<P: AsRef<Path>>(base_path: P) -> Self {
Self {
base_path: base_path.as_ref().to_path_buf(),
}
}
/// Get the default template storage path
/// Get the default template storage path.
///
/// Returns `/var/lib/linux-hello/templates`.
pub fn default_path() -> PathBuf {
PathBuf::from("/var/lib/linux-hello/templates")
}
/// Initialize the template store (create directories if needed)
/// Initialize the template store by creating the storage directory.
///
/// This must be called before storing templates. Creates the directory
/// with all parent directories if they don't exist.
///
/// # Errors
///
/// Returns an error if the directory cannot be created (permission denied, etc.).
pub fn initialize(&self) -> Result<()> {
fs::create_dir_all(&self.base_path)?;
Ok(())
}
/// Get the path for a user's template directory
/// Get the path for a user's template directory.
fn user_path(&self, user: &str) -> PathBuf {
self.base_path.join(user)
}
/// Get the path for a specific template file
/// Get the path for a specific template file.
fn template_path(&self, user: &str, label: &str) -> PathBuf {
self.user_path(user).join(format!("{}.json", label))
}
/// Store a template for a user
/// Store a face template for a user.
///
/// Creates the user's directory if it doesn't exist and writes the
/// template as a JSON file.
///
/// # Arguments
///
/// * `template` - The face template to store
///
/// # Errors
///
/// Returns an error if:
/// - The directory cannot be created
/// - The file cannot be written
/// - JSON serialization fails
pub fn store(&self, template: &FaceTemplate) -> Result<()> {
let user_dir = self.user_path(&template.user);
fs::create_dir_all(&user_dir)?;
@@ -208,7 +332,6 @@ impl TemplateStore {
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
use tempfile::TempDir;
#[test]