hexz_cli/cmd/sys/verify.rs
1//! Verify Ed25519 signatures on Hexz archives.
2//!
3//! This module implements the `verify` command, which validates the cryptographic
4//! signature on a signed Hexz archive to ensure authenticity and integrity.
5//!
6//! # Verification Process
7//!
8//! The verification operation follows these steps:
9//!
10//! 1. **Load Header**: Read archive header and extract signature metadata
11//! 2. **Check Signature Exists**: Verify the archive has been signed
12//! 3. **Read Signature**: Load the 64-byte Ed25519 signature from the archive
13//! 4. **Read Master Index**: Read the index structure that was signed
14//! 5. **Compute Digest**: Calculate SHA-256 hash of the index
15//! 6. **Verify Signature**: Validate Ed25519 signature using public key
16//!
17//! # What Gets Verified
18//!
19//! The signature verification checks:
20//! - The **Master Index** has not been modified since signing
21//! - The signature was created by the holder of the corresponding private key
22//! - The signature is mathematically valid (correct Ed25519 signature)
23//!
24//! # Security Guarantees
25//!
26//! A valid signature proves:
27//! - **Authenticity**: Archive was signed by holder of the private key
28//! - **Integrity**: Index structure has not been tampered with
29//! - **Trust**: If you trust the public key, you can trust the archive
30//!
31//! # Limitations
32//!
33//! Signature verification does NOT protect against:
34//! - **Replay attacks**: Old valid archives can be replayed
35//! - **Data block modification**: Individual blocks could be swapped if hashes collide
36//! - **Header manipulation**: Some header fields are mutable (e.g., signature metadata)
37//!
38//! # Usage
39//!
40//! ```bash
41//! # Verify an archive signature
42//! hexz sys verify --key ~/.hexz/keys/public.key archive.st
43//!
44//! # On success
45//! # => Signature Verified! The image index is authentic.
46//!
47//! # On failure
48//! # => Error: Signature verification failed
49//! ```
50//!
51//! # Exit Codes
52//!
53//! - **0**: Signature is valid
54//! - **Non-zero**: Verification failed (invalid signature or archive not signed)
55
56use anyhow::Result;
57use colored::Colorize;
58use hexz_ops::sign::verify_archive;
59use std::path::Path;
60
61/// Verify the Ed25519 signature on a signed Hexz archive.
62///
63/// This function validates that the archive's Master Index has not been modified
64/// since it was signed, and that the signature was created by the holder of the
65/// corresponding private key.
66///
67/// # Arguments
68///
69/// * `key_path` - Path to the Ed25519 public key file (32 bytes)
70/// * `image_path` - Path to the signed Hexz archive file
71///
72/// # Process
73///
74/// 1. Opens the archive and reads the header
75/// 2. Checks that signature metadata exists in header
76/// 3. Reads the 64-byte signature from the file
77/// 4. Reads the Master Index (from `header.index_offset` to signature offset)
78/// 5. Computes SHA-256 digest of the index
79/// 6. Verifies the Ed25519 signature against the digest
80///
81/// # Returns
82///
83/// Returns `Ok(())` if signature is valid, or an error if:
84/// - Archive is not signed (missing signature metadata)
85/// - Public key file cannot be read
86/// - Archive file is malformed
87/// - Signature length is invalid (not 64 bytes)
88/// - Signature verification fails (tampered index or wrong key)
89///
90/// # Example
91///
92/// ```no_run
93/// # use std::path::PathBuf;
94/// # use hexz_cli::cmd::sys::verify;
95/// let key = PathBuf::from("~/.hexz/keys/public.key");
96/// let archive = PathBuf::from("archive.hxz");
97///
98/// match verify::run(&key, &archive) {
99/// Ok(()) => println!("✓ Signature valid"),
100/// Err(e) => eprintln!("✗ Verification failed: {}", e),
101/// }
102/// # Ok::<(), anyhow::Error>(())
103/// ```
104pub fn run(key_path: &Path, image_path: &Path) -> Result<()> {
105 println!("{} Verifying archive", "╭".dimmed());
106 println!("{} Image {}", "│".dimmed(), image_path.display().to_string().cyan());
107 println!("{} Key {}", "╰".dimmed(), key_path.display().to_string().bright_black());
108
109 verify_archive(image_path, key_path)?;
110 println!("\n {} Signature verified. The index is authentic.", "✓".green());
111 Ok(())
112}