16 KiB
Linux Hello API Documentation
This document provides a high-level overview of the Linux Hello API for developers who want to integrate with, extend, or understand the facial authentication system.
Table of Contents
- Architecture Overview
- Security Model
- Authentication Flow
- Crate Structure
- Key APIs
- Extension Points
- Configuration
- IPC Protocol
- D-Bus API
- Error Handling
Architecture Overview
Linux Hello uses a pipeline architecture for facial authentication:
┌─────────────────────────────────────────────────────────────────────────────────┐
│ Authentication Pipeline │
├─────────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┐ ┌───────────┐ ┌─────────────┐ ┌───────────┐ ┌──────────┐ │
│ │ Camera │──▶│ Face │──▶│ Anti- │──▶│ Embedding │──▶│ Template │ │
│ │ Capture │ │ Detection │ │ Spoofing │ │Extraction │ │ Matching │ │
│ └──────────┘ └───────────┘ └─────────────┘ └───────────┘ └──────────┘ │
│ │ │ │ │ │ │
│ ▼ ▼ ▼ ▼ ▼ │
│ camera/ detection/ anti_spoofing/ embedding/ matching/ │
│ │
└─────────────────────────────────────────────────────────────────────────────────┘
Components
| Component | Purpose | Module |
|---|---|---|
| Camera Capture | Acquire IR frames from webcam | camera |
| Face Detection | Locate faces in frames | detection |
| Anti-Spoofing | Verify liveness (prevent photos/videos) | anti_spoofing |
| Embedding Extraction | Generate face feature vector | embedding |
| Template Matching | Compare against enrolled templates | matching |
Security Model
Linux Hello implements defense-in-depth security:
Layer 1: Hardware Security
- IR Camera Requirement: Only infrared cameras are accepted
- TPM Integration: Templates encrypted with hardware-bound keys
- PCR Binding: Optional boot configuration verification
Layer 2: Biometric Security
- Anti-Spoofing: Multiple liveness detection methods
- IR reflection analysis
- Depth estimation
- Texture analysis (LBP)
- Blink detection
- Micro-movement tracking
Layer 3: Data Security
- Encrypted Storage: AES-256-GCM for templates at rest
- Secure Memory: Sensitive data zeroized on drop
- Memory Locking: Prevents swapping to disk
Layer 4: Access Control
- IPC Authorization: Peer credential verification
- Rate Limiting: Prevents brute-force attacks
- Permission Checks: Users can only manage their own templates
Authentication Flow
┌──────────────────────────────────────────────────────────────────────────────┐
│ Authentication Sequence │
└──────────────────────────────────────────────────────────────────────────────┘
PAM Module Daemon Storage
│ │ │
│ 1. Authenticate(user) │ │
│───────────────────────▶│ │
│ │ │
│ │ 2. Load templates │
│ │─────────────────────────▶│
│ │ │
│ │ 3. Capture frame │
│ │ ◄──── IR Camera │
│ │ │
│ │ 4. Detect face │
│ │ 5. Anti-spoofing check │
│ │ 6. Extract embedding │
│ │ 7. Match templates │
│ │ │
│ 8. Auth result │ │
│◄───────────────────────│ │
│ │ │
Crate Structure
linux-hello-common
Shared types and utilities used by all components.
// Key exports
use linux_hello_common::{
Config, // System configuration
Error, Result, // Error handling
FaceTemplate, // Template data structure
TemplateStore, // File-based storage
};
linux-hello-daemon
Core authentication functionality and services.
// Camera access
use linux_hello_daemon::{
enumerate_cameras, // List available cameras
Camera, // Camera control
CameraInfo, // Camera metadata
Frame, PixelFormat, // Frame data
};
// Face processing
use linux_hello_daemon::{
FaceDetection, FaceDetect, // Detection types
EmbeddingExtractor, // Embedding trait
cosine_similarity, // Distance metrics
match_template, MatchResult, // Matching
};
// Security
use linux_hello_daemon::{
AntiSpoofingDetector, LivenessResult, // Anti-spoofing
SecureEmbedding, SecureBytes, // Secure memory
TpmStorage, SoftwareTpmFallback, // Encryption
};
// IPC
use linux_hello_daemon::{
IpcServer, IpcClient, // Server/client
IpcRequest, IpcResponse, // Messages
};
Key APIs
Camera API
use linux_hello_daemon::{enumerate_cameras, Camera, Frame};
// Find IR camera
let cameras = enumerate_cameras()?;
let ir_camera = cameras.iter()
.find(|c| c.is_ir)
.ok_or("No IR camera found")?;
// Capture frames
let mut camera = Camera::open(&ir_camera.device_path)?;
camera.start()?;
let frame: Frame = camera.capture_frame()?;
Face Detection API
use linux_hello_daemon::{FaceDetect, SimpleFaceDetector, FaceDetection};
// Create detector
let detector = SimpleFaceDetector::new(0.5); // confidence threshold
// Detect faces
let detections: Vec<FaceDetection> = detector.detect(
&frame.data,
frame.width,
frame.height
)?;
// Convert to pixel coordinates
if let Some(face) = detections.first() {
let (x, y, w, h) = face.to_pixels(frame.width, frame.height);
}
Embedding API
use linux_hello_daemon::{
EmbeddingExtractor, PlaceholderEmbeddingExtractor,
cosine_similarity, euclidean_distance, similarity_to_distance,
};
use image::GrayImage;
// Extract embedding
let extractor = PlaceholderEmbeddingExtractor::new(128);
let face_image = GrayImage::new(112, 112); // cropped face
let embedding: Vec<f32> = extractor.extract(&face_image)?;
// Compare embeddings
let similarity = cosine_similarity(&embedding1, &embedding2);
let distance = similarity_to_distance(similarity);
Template Matching API
use linux_hello_daemon::{match_template, MatchResult, average_embeddings};
use linux_hello_common::FaceTemplate;
// Match against stored templates
let result: MatchResult = match_template(
&probe_embedding,
&stored_templates,
0.6 // distance threshold
);
if result.matched {
println!("Match found: {:?}", result.matched_label);
}
// Create averaged template for enrollment
let avg_embedding = average_embeddings(&multiple_embeddings)?;
Anti-Spoofing API
use linux_hello_daemon::anti_spoofing::{
AntiSpoofingDetector, AntiSpoofingConfig, AntiSpoofingFrame, LivenessResult
};
let config = AntiSpoofingConfig::default();
let mut detector = AntiSpoofingDetector::new(config);
let frame = AntiSpoofingFrame {
pixels: frame_data,
width: 640,
height: 480,
is_ir: true,
face_bbox: Some((x, y, w, h)),
timestamp_ms: 0,
};
let result: LivenessResult = detector.check_frame(&frame)?;
if result.is_live {
// Proceed with authentication
}
Secure Memory API
use linux_hello_daemon::{SecureEmbedding, SecureBytes};
// Automatically zeroized on drop
let secure_emb = SecureEmbedding::new(embedding);
// Constant-time comparison
let bytes1 = SecureBytes::new(data1);
let bytes2 = SecureBytes::new(data2);
let equal = bytes1.constant_time_eq(&bytes2);
IPC API
use linux_hello_daemon::ipc::{IpcClient, IpcServer, IpcRequest, IpcResponse};
// Client usage
let client = IpcClient::default();
let response = client.authenticate("username").await?;
if response.success {
println!("Authenticated!");
}
// Server setup
let mut server = IpcServer::new("/run/linux-hello/auth.sock");
server.set_auth_handler(|user| async move {
// Perform authentication
Ok(true)
});
server.start().await?;
Extension Points
Custom Face Detector
Implement the FaceDetect trait:
use linux_hello_daemon::{FaceDetect, FaceDetection};
use linux_hello_common::Result;
struct MyDetector { /* ... */ }
impl FaceDetect for MyDetector {
fn detect(&self, image_data: &[u8], width: u32, height: u32)
-> Result<Vec<FaceDetection>>
{
// Custom detection logic
Ok(vec![])
}
}
Custom Embedding Extractor
Implement the EmbeddingExtractor trait:
use linux_hello_daemon::EmbeddingExtractor;
use linux_hello_common::Result;
use image::GrayImage;
struct MyExtractor { /* ... */ }
impl EmbeddingExtractor for MyExtractor {
fn extract(&self, face_image: &GrayImage) -> Result<Vec<f32>> {
// Custom embedding extraction
Ok(vec![])
}
}
Custom TPM Storage
Implement the TpmStorage trait:
use linux_hello_daemon::tpm::{TpmStorage, EncryptedTemplate};
use linux_hello_common::Result;
struct MyStorage { /* ... */ }
impl TpmStorage for MyStorage {
fn is_available(&self) -> bool { true }
fn initialize(&mut self) -> Result<()> { Ok(()) }
fn encrypt(&self, user: &str, plaintext: &[u8]) -> Result<EncryptedTemplate> { /* ... */ }
fn decrypt(&self, user: &str, encrypted: &EncryptedTemplate) -> Result<Vec<u8>> { /* ... */ }
fn create_user_key(&mut self, user: &str) -> Result<()> { Ok(()) }
fn remove_user_key(&mut self, user: &str) -> Result<()> { Ok(()) }
}
Configuration
Configuration is stored in /etc/linux-hello/config.toml:
[general]
log_level = "info"
timeout_seconds = 5
[camera]
device = "auto" # or "/dev/video0"
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
IPC Protocol
The daemon communicates via Unix socket using JSON messages.
Socket Location
/run/linux-hello/auth.sock
Request Format
{"action": "authenticate", "user": "username"}
{"action": "enroll", "user": "username", "label": "default", "frame_count": 5}
{"action": "list", "user": "username"}
{"action": "remove", "user": "username", "label": "default"}
{"action": "ping"}
Response Format
{
"success": true,
"message": "Authentication successful",
"confidence": 0.95,
"templates": ["default", "backup"]
}
D-Bus API
The daemon exposes a D-Bus interface for desktop integration.
Service Information
| Property | Value |
|---|---|
| Bus | System bus |
| Service Name | org.linuxhello.Daemon |
| Object Path | /org/linuxhello/Manager |
| Interface | org.linuxhello.Manager |
Methods
<!-- Authenticate a user -->
<method name="Authenticate">
<arg name="user" type="s" direction="in"/>
<arg name="success" type="b" direction="out"/>
<arg name="confidence" type="d" direction="out"/>
<arg name="message" type="s" direction="out"/>
</method>
<!-- Start enrollment -->
<method name="EnrollStart">
<arg name="user" type="s" direction="in"/>
<arg name="label" type="s" direction="in"/>
<arg name="frame_count" type="u" direction="in"/>
<arg name="success" type="b" direction="out"/>
</method>
<!-- Cancel enrollment -->
<method name="EnrollCancel">
<arg name="success" type="b" direction="out"/>
</method>
<!-- List templates for user -->
<method name="ListTemplates">
<arg name="user" type="s" direction="in"/>
<arg name="templates" type="as" direction="out"/>
</method>
<!-- Remove a template -->
<method name="RemoveTemplate">
<arg name="user" type="s" direction="in"/>
<arg name="label" type="s" direction="in"/>
<arg name="success" type="b" direction="out"/>
</method>
<!-- Get system status -->
<method name="GetSystemStatus">
<arg name="camera_available" type="b" direction="out"/>
<arg name="tpm_available" type="b" direction="out"/>
<arg name="anti_spoofing_enabled" type="b" direction="out"/>
<arg name="enrolled_count" type="u" direction="out"/>
</method>
Properties
<property name="Version" type="s" access="read"/>
<property name="CameraAvailable" type="b" access="read"/>
<property name="TpmAvailable" type="b" access="read"/>
<property name="AntiSpoofingEnabled" type="b" access="read"/>
Signals
<!-- Enrollment progress -->
<signal name="EnrollmentProgress">
<arg name="frames_captured" type="u"/>
<arg name="frames_total" type="u"/>
<arg name="status" type="s"/>
</signal>
<!-- Enrollment complete -->
<signal name="EnrollmentComplete">
<arg name="success" type="b"/>
<arg name="message" type="s"/>
</signal>
D-Bus Client Example (Rust with zbus)
use zbus::{Connection, proxy};
#[proxy(
interface = "org.linuxhello.Manager",
default_service = "org.linuxhello.Daemon",
default_path = "/org/linuxhello/Manager"
)]
trait LinuxHelloManager {
async fn authenticate(&self, user: &str) -> zbus::Result<(bool, f64, String)>;
async fn list_templates(&self, user: &str) -> zbus::Result<Vec<String>>;
}
async fn authenticate_user() -> zbus::Result<()> {
let connection = Connection::system().await?;
let proxy = LinuxHelloManagerProxy::new(&connection).await?;
let (success, confidence, message) = proxy.authenticate("alice").await?;
println!("Auth: {} ({}): {}", success, confidence, message);
Ok(())
}
Error Handling
All operations return Result<T, Error> where Error is defined in linux_hello_common::Error:
use linux_hello_common::{Error, Result};
match operation() {
Ok(result) => { /* success */ }
Err(Error::NoCameraFound) => { /* no IR camera */ }
Err(Error::NoFaceDetected) => { /* face not visible */ }
Err(Error::AuthenticationFailed) => { /* no match */ }
Err(Error::UserNotEnrolled(user)) => { /* not enrolled */ }
Err(e) => { /* other error */ }
}
Building Documentation
Generate HTML documentation:
cargo doc --workspace --no-deps --open
Documentation is generated at target/doc/linux_hello_daemon/index.html.
See Also
- README.md - Project overview and quick start
- BENCHMARKS.md - Performance benchmarks
- Source code documentation:
cargo doc --open