451 lines
14 KiB
Markdown
451 lines
14 KiB
Markdown
# 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](#architecture-overview)
|
|
- [Security Model](#security-model)
|
|
- [Authentication Flow](#authentication-flow)
|
|
- [Crate Structure](#crate-structure)
|
|
- [Key APIs](#key-apis)
|
|
- [Extension Points](#extension-points)
|
|
- [Configuration](#configuration)
|
|
- [IPC Protocol](#ipc-protocol)
|
|
|
|
## 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.
|
|
|
|
```rust
|
|
// 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.
|
|
|
|
```rust
|
|
// 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
|
|
|
|
```rust
|
|
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
|
|
|
|
```rust
|
|
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
|
|
|
|
```rust
|
|
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
|
|
|
|
```rust
|
|
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
|
|
|
|
```rust
|
|
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
|
|
|
|
```rust
|
|
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
|
|
|
|
```rust
|
|
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:
|
|
|
|
```rust
|
|
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:
|
|
|
|
```rust
|
|
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:
|
|
|
|
```rust
|
|
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`:
|
|
|
|
```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
|
|
|
|
```json
|
|
{"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
|
|
|
|
```json
|
|
{
|
|
"success": true,
|
|
"message": "Authentication successful",
|
|
"confidence": 0.95,
|
|
"templates": ["default", "backup"]
|
|
}
|
|
```
|
|
|
|
## Error Handling
|
|
|
|
All operations return `Result<T, Error>` where `Error` is defined in `linux_hello_common::Error`:
|
|
|
|
```rust
|
|
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:
|
|
|
|
```bash
|
|
cargo doc --workspace --no-deps --open
|
|
```
|
|
|
|
Documentation is generated at `target/doc/linux_hello_daemon/index.html`.
|
|
|
|
## See Also
|
|
|
|
- [README.md](../README.md) - Project overview and quick start
|
|
- [BENCHMARKS.md](BENCHMARKS.md) - Performance benchmarks
|
|
- Source code documentation: `cargo doc --open`
|