first commit

This commit is contained in:
2026-01-02 19:08:56 +01:00
commit fdf37aa7b4
22 changed files with 4000 additions and 0 deletions

View File

@@ -0,0 +1,12 @@
[package]
name = "linux-hello-common"
version.workspace = true
edition.workspace = true
license.workspace = true
description = "Shared types and utilities for Linux Hello"
[dependencies]
thiserror.workspace = true
serde.workspace = true
toml.workspace = true
tracing.workspace = true

View File

@@ -0,0 +1,237 @@
//! Configuration for Linux Hello
use serde::{Deserialize, Serialize};
use std::path::Path;
use crate::error::{Error, Result};
/// Main configuration structure
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Config {
#[serde(default)]
pub general: GeneralConfig,
#[serde(default)]
pub camera: CameraConfig,
#[serde(default)]
pub detection: DetectionConfig,
#[serde(default)]
pub embedding: EmbeddingConfig,
#[serde(default)]
pub anti_spoofing: AntiSpoofingConfig,
#[serde(default)]
pub tpm: TpmConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GeneralConfig {
#[serde(default = "default_log_level")]
pub log_level: String,
#[serde(default = "default_timeout")]
pub timeout_seconds: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CameraConfig {
#[serde(default = "default_auto")]
pub device: String,
#[serde(default = "default_auto")]
pub ir_emitter: String,
#[serde(default = "default_resolution")]
pub resolution: [u32; 2],
#[serde(default = "default_fps")]
pub fps: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DetectionConfig {
#[serde(default = "default_model")]
pub model: String,
#[serde(default = "default_min_face_size")]
pub min_face_size: u32,
#[serde(default = "default_confidence_threshold")]
pub confidence_threshold: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EmbeddingConfig {
#[serde(default = "default_embedding_model")]
pub model: String,
#[serde(default = "default_distance_threshold")]
pub distance_threshold: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AntiSpoofingConfig {
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default = "default_true")]
pub depth_check: bool,
#[serde(default = "default_true")]
pub liveness_model: bool,
#[serde(default = "default_true")]
pub temporal_check: bool,
#[serde(default = "default_min_score")]
pub min_score: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TpmConfig {
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default)]
pub pcr_binding: bool,
}
// Default value functions
fn default_log_level() -> String {
"info".to_string()
}
fn default_timeout() -> u32 {
5
}
fn default_auto() -> String {
"auto".to_string()
}
fn default_resolution() -> [u32; 2] {
[640, 480]
}
fn default_fps() -> u32 {
30
}
fn default_model() -> String {
"blazeface".to_string()
}
fn default_min_face_size() -> u32 {
80
}
fn default_confidence_threshold() -> f32 {
0.9
}
fn default_embedding_model() -> String {
"mobilefacenet".to_string()
}
fn default_distance_threshold() -> f32 {
0.6
}
fn default_true() -> bool {
true
}
fn default_min_score() -> f32 {
0.7
}
impl Default for GeneralConfig {
fn default() -> Self {
Self {
log_level: default_log_level(),
timeout_seconds: default_timeout(),
}
}
}
impl Default for CameraConfig {
fn default() -> Self {
Self {
device: default_auto(),
ir_emitter: default_auto(),
resolution: default_resolution(),
fps: default_fps(),
}
}
}
impl Default for DetectionConfig {
fn default() -> Self {
Self {
model: default_model(),
min_face_size: default_min_face_size(),
confidence_threshold: default_confidence_threshold(),
}
}
}
impl Default for EmbeddingConfig {
fn default() -> Self {
Self {
model: default_embedding_model(),
distance_threshold: default_distance_threshold(),
}
}
}
impl Default for AntiSpoofingConfig {
fn default() -> Self {
Self {
enabled: default_true(),
depth_check: default_true(),
liveness_model: default_true(),
temporal_check: default_true(),
min_score: default_min_score(),
}
}
}
impl Default for TpmConfig {
fn default() -> Self {
Self {
enabled: default_true(),
pcr_binding: false,
}
}
}
impl Default for Config {
fn default() -> Self {
Self {
general: GeneralConfig::default(),
camera: CameraConfig::default(),
detection: DetectionConfig::default(),
embedding: EmbeddingConfig::default(),
anti_spoofing: AntiSpoofingConfig::default(),
tpm: TpmConfig::default(),
}
}
}
impl Config {
/// Load configuration from a TOML file
pub fn load<P: AsRef<Path>>(path: P) -> Result<Self> {
let content = std::fs::read_to_string(path)?;
toml::from_str(&content).map_err(|e| Error::Config(e.to_string()))
}
/// Load configuration from the default path or return defaults
pub fn load_or_default() -> Self {
Self::load("/etc/linux-hello/config.toml").unwrap_or_default()
}
/// Save configuration to a TOML file
pub fn save<P: AsRef<Path>>(&self, path: P) -> Result<()> {
let content = toml::to_string_pretty(self).map_err(|e| Error::Serialization(e.to_string()))?;
std::fs::write(path, content)?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config() {
let config = Config::default();
assert_eq!(config.general.log_level, "info");
assert_eq!(config.general.timeout_seconds, 5);
assert_eq!(config.camera.device, "auto");
assert_eq!(config.camera.resolution, [640, 480]);
assert!(config.anti_spoofing.enabled);
}
#[test]
fn test_config_serialization() {
let config = Config::default();
let toml_str = toml::to_string(&config).unwrap();
let parsed: Config = toml::from_str(&toml_str).unwrap();
assert_eq!(parsed.general.log_level, config.general.log_level);
}
}

View File

@@ -0,0 +1,63 @@
//! Error types for Linux Hello
use thiserror::Error;
/// Main error type for Linux Hello
#[derive(Error, Debug)]
pub enum Error {
#[error("Camera error: {0}")]
Camera(String),
#[error("No IR camera found")]
NoCameraFound,
#[error("IR emitter control failed: {0}")]
IrEmitter(String),
#[error("Face detection error: {0}")]
Detection(String),
#[error("No face detected in frame")]
NoFaceDetected,
#[error("Multiple faces detected")]
MultipleFacesDetected,
#[error("Model loading error: {0}")]
ModelLoad(String),
#[error("Configuration error: {0}")]
Config(String),
#[error("TPM error: {0}")]
Tpm(String),
#[error("Authentication failed")]
AuthenticationFailed,
#[error("User not enrolled: {0}")]
UserNotEnrolled(String),
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("Serialization error: {0}")]
Serialization(String),
}
/// Result type alias for Linux Hello
pub type Result<T> = std::result::Result<T, Error>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_display() {
let err = Error::NoCameraFound;
assert_eq!(err.to_string(), "No IR camera found");
let err = Error::Camera("device busy".to_string());
assert_eq!(err.to_string(), "Camera error: device busy");
}
}

View File

@@ -0,0 +1,10 @@
//! Linux Hello Common Library
//!
//! Shared types, configuration, and error handling for the Linux Hello
//! facial authentication system.
pub mod config;
pub mod error;
pub use config::Config;
pub use error::{Error, Result};