Documentation
Hash Verification at Each Compilation Stage
This document explains the hash verification callback system implemented across all compilers to enforce integrity checks at each stage of the compilation pipeline.
Overview
The hash verification system provides cryptographic proof that files are not tampered with at any stage of compilation. Client code can subscribe to hash events to:
- Log all hash computations for audit trails
- Enforce custom verification policies (strict vs. permissive)
- Detect tampering in real-time
- Track file integrity across the pipeline
- Prevent MITM attacks on downloaded sources
Architecture
Compilation Stages with Hash Verification
┌─────────────────────────────────────────────────────────────┐
│ 1. Configuration File Loading │
│ ├─ Hash computed: config_file │
│ └─ Event fired: HashComputed │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 2. Source Files Loading (Local & Remote) │
│ ├─ Hash computed: input_file / downloaded_source │
│ ├─ Event fired: HashComputed │
│ └─ Optional verification against expected hash │
│ ├─ Match → Event: HashVerified │
│ └─ Mismatch → Event: HashMismatch (can abort) │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 3. Compilation (via hostlist-compiler) │
│ └─ (No hash events - external tool) │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 4. Output File Writing │
│ ├─ Hash computed: output_file │
│ └─ Event fired: HashComputed │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 5. Rules File Copying (if requested) │
│ ├─ Hash computed: copied_rules_file │
│ ├─ Event fired: HashComputed │
│ └─ Can verify against output hash │
│ ├─ Match → Event: HashVerified │
│ └─ Mismatch → Event: HashMismatch │
└─────────────────────────────────────────────────────────────┘
Event Types
1. Hash Computed Event
Fired whenever a hash is computed for any file.
Data:
itemIdentifier: File path or identifieritemType: Type (e.g., "config_file", "output_file", "input_file")hash: SHA-384 hash (96 hex characters)sizeBytes: File sizeisVerification: Whether this is for verification purposes
2. Hash Verified Event
Fired when a hash successfully matches the expected value.
Data:
itemIdentifier: File path or identifieritemType: Type of fileexpectedHash: Expected SHA-384 hashactualHash: Computed SHA-384 hash (should match expected)sizeBytes: File sizecomputationDurationMs: Time taken to compute hash
3. Hash Mismatch Event
Fired when a hash does NOT match the expected value.
Data:
itemIdentifier: File path or identifieritemType: Type of fileexpectedHash: Expected SHA-384 hashactualHash: Computed SHA-384 hash (different from expected)sizeBytes: File sizeabort: Whether to abort compilation (default: true)abortReason: Reason for abortingallowContinuation: Handler can set this to continue despite mismatch
Handler Control:
- Set
allowContinuation = trueto continue despite mismatch - Set
abort = falseto prevent compilation failure
Implementation by Language
Rust
Event Handler Trait:
use rules_compiler::events::{
CompilationEventHandler,
HashComputedEventArgs,
HashVerifiedEventArgs,
HashMismatchEventArgs,
};
struct MyHandler;
impl CompilationEventHandler for MyHandler {
fn on_hash_computed(&self, args: &HashComputedEventArgs) {
println!("Hash for {}: {}", args.item_type, &args.hash[..16]);
}
fn on_hash_verified(&self, args: &HashVerifiedEventArgs) {
println!("Hash verified for {}", args.item_identifier);
}
fn on_hash_mismatch(&self, args: &mut HashMismatchEventArgs) {
eprintln!("Hash mismatch for {}", args.item_identifier);
// Optionally allow continuation:
// args.allow_continuation = true;
// args.abort = false;
}
}
Usage:
use rules_compiler::{compile_rules_with_events, EventDispatcher};
let mut dispatcher = EventDispatcher::new();
dispatcher.add_handler(Box::new(MyHandler));
let result = compile_rules_with_events("config.yaml", &options, &dispatcher)?;
TypeScript
Callback Interface:
import type { HashVerificationCallbacks } from './types.ts';
const callbacks: HashVerificationCallbacks = {
onHashComputed: (event) => {
console.log(`Hash for ${event.itemType}: ${event.hash.slice(0, 16)}...`);
},
onHashVerified: (event) => {
console.log(`Hash verified for ${event.itemIdentifier}`);
},
onHashMismatch: (event) => {
console.error(`Hash mismatch for ${event.itemIdentifier}`);
// Optionally allow continuation:
// event.allowContinuation = true;
},
};
Usage:
import { runCompiler } from './compiler.ts';
const result = await runCompiler({
configPath: 'config.yaml',
hashCallbacks: callbacks,
});
.NET
Event Handler:
using RulesCompiler.Abstractions;
public class MyHashHandler : CompilationEventHandlerBase
{
public override Task OnHashComputedAsync(
HashComputedEventArgs args,
CancellationToken cancellationToken = default)
{
Console.WriteLine($"Hash for {args.ItemType}: {args.Hash.Substring(0, 16)}...");
return Task.CompletedTask;
}
public override Task OnHashVerifiedAsync(
HashVerifiedEventArgs args,
CancellationToken cancellationToken = default)
{
Console.WriteLine($"Hash verified for {args.ItemIdentifier}");
return Task.CompletedTask;
}
public override Task OnHashMismatchAsync(
HashMismatchEventArgs args,
CancellationToken cancellationToken = default)
{
Console.Error.WriteLine($"Hash mismatch for {args.ItemIdentifier}");
// Optionally allow continuation:
// args.AllowContinuation = true;
// args.Abort = false;
return Task.CompletedTask;
}
}
Usage:
// To be implemented in compilation pipeline
Python
To be implemented (similar pattern to Rust/TypeScript)
Use Cases
1. Audit Trail Logging
impl CompilationEventHandler for AuditLogger {
fn on_hash_computed(&self, args: &HashComputedEventArgs) {
self.log(format!(
"AUDIT: {} hash={} size={} timestamp={}",
args.item_type,
args.hash,
args.size_bytes,
chrono::Utc::now()
));
}
}
2. Strict Zero-Trust Verification
impl CompilationEventHandler for StrictVerifier {
fn on_hash_mismatch(&self, args: &mut HashMismatchEventArgs) {
// Never allow continuation on mismatch
args.abort = true;
args.allow_continuation = false;
self.alert_security_team(args);
}
}
3. Permissive Development Mode
impl CompilationEventHandler for DevModeHandler {
fn on_hash_mismatch(&self, args: &mut HashMismatchEventArgs) {
// Log but don't fail in development
eprintln!("WARN: Hash mismatch but allowing continuation in dev mode");
args.allow_continuation = true;
args.abort = false;
}
}
4. Database Tracking
impl CompilationEventHandler for DatabaseTracker {
fn on_hash_computed(&self, args: &HashComputedEventArgs) {
self.db.insert_hash_record(
args.item_identifier.clone(),
args.hash.clone(),
chrono::Utc::now(),
);
}
}
Security Considerations
- SHA-384 Algorithm: All hashes use SHA-384 (96 hex characters) for cryptographic strength
- At-Rest Verification: Local files are hashed to detect tampering
- In-Flight Verification: Downloaded sources can be verified against expected hashes
- MITM Prevention: Hash verification on downloads prevents man-in-the-middle attacks
- Immutable Audit Trail: Hash events create an immutable log of all file states
Testing
Example tests are included in examples/hash_audit_handler.rs demonstrating:
- Strict verification (fails on mismatch)
- Permissive verification (logs but continues)
- Custom policy implementation
Example Handler
See examples/hash_audit_handler.rs for a complete implementation of:
- Logging all hash events
- Customizable strictness (strict vs. permissive)
- Comprehensive test coverage
Future Enhancements
Potential additions:
- Hash database persistence across compilations
- Historical hash tracking and drift detection
- Integration with external validation services
- Support for multiple hash algorithms (SHA-256, BLAKE3)
- Signature verification (GPG, minisign)