⚠️ VeridianOS Kernel Documentation - This is low-level kernel code. All functions are unsafe unless explicitly marked otherwise. no_std

veridian_kernel/pkg/
mod.rs

1//! Package Management System
2//!
3//! VeridianOS package manager for installing, updating, and managing software
4//! packages. Provides signature verification using real Ed25519 (RFC 8032)
5//! from `crate::crypto::asymmetric`, policy-based enforcement, and hash
6//! integrity checking.
7
8#![allow(clippy::unwrap_or_default)]
9
10pub mod async_types;
11#[cfg(feature = "alloc")]
12pub mod build_package;
13#[cfg(feature = "alloc")]
14pub mod build_system;
15pub mod compliance;
16pub mod database;
17pub mod delta;
18pub mod ecosystem;
19pub mod format;
20pub mod manifest;
21pub mod plugin;
22pub mod ports;
23#[cfg(feature = "alloc")]
24pub mod repo_server;
25pub mod repository;
26pub mod reproducible;
27pub mod resolver;
28pub mod sdk;
29pub mod statistics;
30pub mod testing;
31pub mod toml_parser;
32
33use alloc::{collections::BTreeMap, string::String, vec, vec::Vec};
34
35use spin::Mutex;
36
37use crate::error::KernelError;
38
39/// Package identifier
40pub type PackageId = String;
41
42/// Package version using semantic versioning
43#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
44pub struct Version {
45    pub major: u32,
46    pub minor: u32,
47    pub patch: u32,
48}
49
50impl Version {
51    pub fn new(major: u32, minor: u32, patch: u32) -> Self {
52        Self {
53            major,
54            minor,
55            patch,
56        }
57    }
58}
59
60/// Package metadata
61#[derive(Debug, Clone)]
62pub struct PackageMetadata {
63    pub name: String,
64    pub version: Version,
65    pub author: String,
66    pub description: String,
67    pub license: String,
68    pub dependencies: Vec<Dependency>,
69    /// Packages that conflict with this package
70    pub conflicts: Vec<String>,
71}
72
73/// Package dependency
74#[derive(Debug, Clone)]
75pub struct Dependency {
76    pub name: String,
77    pub version_req: String,
78}
79
80/// Package installation result
81pub type PkgResult<T> = Result<T, KernelError>;
82
83/// Options controlling how `install()` behaves.
84#[derive(Debug, Clone, Default)]
85pub struct InstallOptions {
86    /// When true, allow installing packages that fail signature verification.
87    /// Intended for development/testing only. A warning is always logged.
88    pub force_unsigned: bool,
89    /// When set, the expected SHA-256 hash of the entire package file
90    /// (from the repository index). The package is rejected if the hash does
91    /// not match, regardless of `force_unsigned`.
92    pub expected_hash: Option<[u8; 32]>,
93}
94
95/// Transaction state for atomic install/remove operations
96#[derive(Debug, Clone)]
97pub struct PackageTransaction {
98    /// Operations pending in this transaction
99    operations: Vec<TransactionOp>,
100    /// Snapshot of installed packages at transaction start (for rollback)
101    snapshot: BTreeMap<PackageId, PackageMetadata>,
102}
103
104/// A single operation inside a transaction
105#[derive(Debug, Clone)]
106pub enum TransactionOp {
107    /// Install a package at a specific version
108    Install(PackageId, Version),
109    /// Remove a package
110    Remove(PackageId),
111}
112
113impl PackageTransaction {
114    fn new(current_installed: &BTreeMap<PackageId, PackageMetadata>) -> Self {
115        Self {
116            operations: Vec::new(),
117            snapshot: current_installed.clone(),
118        }
119    }
120}
121
122/// Package manager
123pub struct PackageManager {
124    /// Installed packages
125    installed: BTreeMap<PackageId, PackageMetadata>,
126    /// Dependency resolver
127    resolver: resolver::DependencyResolver,
128    /// Available repositories
129    repositories: Vec<repository::Repository>,
130    /// Signature verification policy
131    signature_policy: format::SignaturePolicy,
132    /// Trusted signing key ring
133    trusted_keys: format::TrustedKeyRing,
134    /// Persistent package database
135    database: database::PackageDatabase,
136    /// File manifest tracking
137    file_manifest: manifest::FileManifest,
138    /// Active transaction (if any)
139    transaction: Option<PackageTransaction>,
140}
141
142impl PackageManager {
143    pub fn new() -> Self {
144        Self {
145            installed: BTreeMap::new(),
146            resolver: resolver::DependencyResolver::new(),
147            repositories: Vec::new(),
148            signature_policy: format::SignaturePolicy::default(),
149            trusted_keys: format::TrustedKeyRing::default(),
150            database: database::PackageDatabase::default(),
151            file_manifest: manifest::FileManifest::default(),
152            transaction: None,
153        }
154    }
155
156    /// Get a reference to the current signature policy.
157    pub fn signature_policy(&self) -> &format::SignaturePolicy {
158        &self.signature_policy
159    }
160
161    /// Replace the signature policy.
162    pub fn set_signature_policy(&mut self, policy: format::SignaturePolicy) {
163        self.signature_policy = policy;
164    }
165
166    /// Get a mutable reference to the trusted key ring.
167    pub fn trusted_keys_mut(&mut self) -> &mut format::TrustedKeyRing {
168        &mut self.trusted_keys
169    }
170
171    /// Add a repository
172    pub fn add_repository(&mut self, repo: repository::Repository) {
173        self.repositories.push(repo);
174    }
175
176    /// Install a package by name and version.
177    ///
178    /// Uses the default `SignaturePolicy` -- signatures are required and
179    /// `force_unsigned` is false.
180    pub fn install(&mut self, name: String, version_req: String) -> PkgResult<()> {
181        self.install_with_options(name, version_req, InstallOptions::default())
182    }
183
184    /// Install a package with explicit install options.
185    ///
186    /// When `options.force_unsigned` is true, a failed signature verification
187    /// produces a warning instead of a hard error.  A hash mismatch (if
188    /// `expected_hash` is provided) always fails regardless.
189    pub fn install_with_options(
190        &mut self,
191        name: String,
192        version_req: String,
193        options: InstallOptions,
194    ) -> PkgResult<()> {
195        // Create dependency for the package
196        let dep = Dependency {
197            name: name.clone(),
198            version_req: version_req.clone(),
199        };
200
201        // Resolve dependencies
202        let packages = self
203            .resolver
204            .resolve(&[dep])
205            .map_err(|_e| KernelError::InvalidState {
206                expected: "resolvable dependencies",
207                actual: "dependency resolution failed",
208            })?;
209
210        // Install each package in order
211        for (pkg_id, version) in packages {
212            if self.installed.contains_key(&pkg_id) {
213                // Already installed - skip or upgrade
214                continue;
215            }
216
217            // Download package
218            let package_data = self.download_package(&pkg_id, &version)?;
219
220            // Verify package hash against repository index, if provided.
221            if let Some(expected) = options.expected_hash {
222                let actual = crate::crypto::hash::sha256(&package_data);
223                if actual.as_bytes() != &expected {
224                    crate::println!(
225                        "[PKG] REJECT {}: package hash does not match repository index",
226                        pkg_id
227                    );
228                    return Err(KernelError::PermissionDenied {
229                        operation: "verify package hash",
230                    });
231                }
232            }
233
234            // Verify package signature
235            match self.verify_package(&package_data) {
236                Ok(()) => {}
237                Err(e) if options.force_unsigned => {
238                    crate::println!(
239                        "[PKG] WARNING: --force-unsigned: skipping signature failure for {}: {:?}",
240                        pkg_id,
241                        e
242                    );
243                }
244                Err(e) => return Err(e),
245            }
246
247            // Extract and install
248            self.install_package(pkg_id.clone(), version.clone(), package_data)?;
249
250            // Record in transaction if active
251            if let Some(txn) = &mut self.transaction {
252                txn.operations
253                    .push(TransactionOp::Install(pkg_id.clone(), version.clone()));
254            }
255
256            crate::println!(
257                "[PKG] Installed {} {}.{}.{}",
258                pkg_id,
259                version.major,
260                version.minor,
261                version.patch
262            );
263        }
264
265        Ok(())
266    }
267
268    /// Remove an installed package
269    pub fn remove(&mut self, package_id: &PackageId) -> PkgResult<()> {
270        if !self.installed.contains_key(package_id) {
271            return Err(KernelError::NotFound {
272                resource: "package",
273                id: 0,
274            });
275        }
276
277        // Check reverse dependencies
278        let dependents = self.find_dependents(package_id);
279        if !dependents.is_empty() {
280            return Err(KernelError::InvalidState {
281                expected: "no reverse dependencies",
282                actual: "package is required by other packages",
283            });
284        }
285
286        // Record in transaction if active
287        if let Some(txn) = &mut self.transaction {
288            txn.operations
289                .push(TransactionOp::Remove(package_id.clone()));
290        }
291
292        // Remove package
293        self.installed.remove(package_id);
294
295        crate::println!("[PKG] Removed {}", package_id);
296
297        Ok(())
298    }
299
300    /// Remove a package, preserving user-modified config files.
301    ///
302    /// Config files that the user has edited are saved with a `.bak` suffix
303    /// instead of being deleted.
304    pub fn remove_preserving_configs(&mut self, package_id: &PackageId) -> PkgResult<()> {
305        if !self.installed.contains_key(package_id) {
306            return Err(KernelError::NotFound {
307                resource: "package",
308                id: 0,
309            });
310        }
311
312        // Check reverse dependencies
313        let dependents = self.find_dependents(package_id);
314        if !dependents.is_empty() {
315            return Err(KernelError::InvalidState {
316                expected: "no reverse dependencies",
317                actual: "package is required by other packages",
318            });
319        }
320
321        // Log which config files are being preserved
322        let config_files = self.database.list_config_files(package_id);
323        for config in config_files {
324            if config.is_user_modified {
325                crate::println!(
326                    "[PKG] Preserving modified config: {} -> {}.bak",
327                    config.path,
328                    config.path
329                );
330            }
331        }
332
333        // Record in transaction if active
334        if let Some(txn) = &mut self.transaction {
335            txn.operations
336                .push(TransactionOp::Remove(package_id.clone()));
337        }
338
339        // Remove package
340        self.installed.remove(package_id);
341        crate::println!("[PKG] Removed {} (configs preserved)", package_id);
342
343        Ok(())
344    }
345
346    /// Find and remove orphan packages (packages with no reverse dependencies).
347    ///
348    /// Returns the list of removed package names.
349    pub fn remove_orphans(&mut self) -> PkgResult<Vec<PackageId>> {
350        let orphans = self.database.find_orphans();
351        let mut removed = Vec::new();
352
353        for orphan in &orphans {
354            // Skip packages that are not in the installed set
355            if !self.installed.contains_key(orphan) {
356                continue;
357            }
358            if self.remove(orphan).is_ok() {
359                removed.push(orphan.clone());
360            }
361        }
362
363        if !removed.is_empty() {
364            crate::println!("[PKG] Removed {} orphan package(s)", removed.len());
365        }
366
367        Ok(removed)
368    }
369
370    /// List installed packages
371    pub fn list_installed(&self) -> Vec<(PackageId, Version)> {
372        self.installed
373            .iter()
374            .map(|(id, meta)| (id.clone(), meta.version.clone()))
375            .collect()
376    }
377
378    /// Check if package is installed
379    pub fn is_installed(&self, package_id: &PackageId) -> bool {
380        self.installed.contains_key(package_id)
381    }
382
383    /// Get installed package metadata
384    pub fn get_metadata(&self, package_id: &PackageId) -> Option<&PackageMetadata> {
385        self.installed.get(package_id)
386    }
387
388    /// Update package lists from repositories
389    pub fn update(&mut self) -> PkgResult<()> {
390        for repo in &self.repositories {
391            let packages = repo.fetch_package_list();
392
393            for pkg in packages {
394                self.resolver.register_package(
395                    pkg.name.clone(),
396                    pkg.version.clone(),
397                    pkg.dependencies.clone(),
398                    pkg.conflicts.clone(),
399                );
400            }
401        }
402
403        Ok(())
404    }
405
406    /// Download package from repositories
407    fn download_package(&self, package_id: &PackageId, _version: &Version) -> PkgResult<Vec<u8>> {
408        for repo in &self.repositories {
409            if let Some(data) = repo.download_package(package_id) {
410                return Ok(data);
411            }
412        }
413
414        Err(KernelError::NotFound {
415            resource: "package in repositories",
416            id: 0,
417        })
418    }
419
420    /// Verify package signature (dual Ed25519 + Dilithium).
421    ///
422    /// Uses the real Ed25519 implementation from `crate::crypto::asymmetric`
423    /// (RFC 8032) for Ed25519 verification, and the Dilithium structural
424    /// verifier for the post-quantum layer.
425    fn verify_package(&self, package_data: &[u8]) -> PkgResult<()> {
426        use format::{PackageHeader, PackageSignatures, TrustLevel, VPKG_MAGIC};
427
428        use crate::crypto::asymmetric;
429
430        // Check policy: if signatures are not required, skip verification.
431        if !self.signature_policy.require_signatures {
432            crate::println!("[PKG] Signature verification skipped (policy: not required)");
433            return Ok(());
434        }
435
436        // Step 1: Parse package header
437        if package_data.len() < 64 {
438            return Err(KernelError::InvalidArgument {
439                name: "package_data",
440                value: "too_short",
441            });
442        }
443
444        // Extract header
445        // SAFETY: package_data.as_ptr() points to at least 64 bytes (checked above).
446        // read_unaligned is used because the byte buffer may not satisfy
447        // PackageHeader's alignment requirements. The resulting header is validated
448        // immediately via magic number and version checks before any fields are
449        // trusted.
450        let header =
451            unsafe { core::ptr::read_unaligned(package_data.as_ptr() as *const PackageHeader) };
452
453        // Validate magic number
454        if header.magic != VPKG_MAGIC {
455            return Err(KernelError::InvalidArgument {
456                name: "package_magic",
457                value: "invalid",
458            });
459        }
460
461        // Validate version
462        if !header.validate() {
463            return Err(KernelError::InvalidArgument {
464                name: "package_version",
465                value: "unsupported",
466            });
467        }
468
469        // Step 2: Extract signatures
470        let sig_offset = header.signature_offset as usize;
471        let sig_size = header.signature_size as usize;
472
473        if sig_offset + sig_size > package_data.len() {
474            return Err(KernelError::InvalidArgument {
475                name: "signature_offset",
476                value: "out_of_bounds",
477            });
478        }
479
480        let sig_data = &package_data[sig_offset..sig_offset + sig_size];
481        let signatures =
482            PackageSignatures::from_bytes(sig_data).map_err(|_| KernelError::InvalidArgument {
483                name: "signatures",
484                value: "parse_failed",
485            })?;
486
487        // Step 3: Determine content to verify (metadata + content sections)
488        let content_start = header.metadata_offset as usize;
489        let content_end = header.signature_offset as usize;
490        if content_start > content_end || content_end > package_data.len() {
491            return Err(KernelError::InvalidArgument {
492                name: "content_range",
493                value: "out_of_bounds",
494            });
495        }
496        let content_to_verify = &package_data[content_start..content_end];
497
498        // Step 4: Verify Ed25519 signature against ALL trusted keys using
499        //         real Ed25519 verification (RFC 8032) from crypto::asymmetric.
500        let ed25519_sig =
501            asymmetric::Signature::from_bytes(&signatures.ed25519_sig).map_err(|_| {
502                KernelError::InvalidArgument {
503                    name: "ed25519_signature",
504                    value: "malformed",
505                }
506            })?;
507
508        let mut ed25519_valid = false;
509        let mut matched_trust_level = TrustLevel::Untrusted;
510
511        for trusted in self.trusted_keys.keys() {
512            let vk = match asymmetric::VerifyingKey::from_bytes(&trusted.public_key) {
513                Ok(vk) => vk,
514                Err(_) => continue,
515            };
516
517            match vk.verify(content_to_verify, &ed25519_sig) {
518                Ok(true) => {
519                    ed25519_valid = true;
520                    matched_trust_level = trusted.trust_level;
521                    break;
522                }
523                _ => continue,
524            }
525        }
526
527        if !ed25519_valid {
528            crate::println!("[PKG] REJECT: Ed25519 signature verification failed");
529            return Err(KernelError::PermissionDenied {
530                operation: "verify Ed25519 signature",
531            });
532        }
533
534        // Step 5: Check trust level meets policy minimum.
535        if matched_trust_level < self.signature_policy.minimum_trust_level {
536            crate::println!("[PKG] REJECT: signing key trust level insufficient");
537            return Err(KernelError::PermissionDenied {
538                operation: "verify signing key trust level",
539            });
540        }
541
542        // Step 6: Verify Dilithium (post-quantum) signature if policy requires it.
543        if self.signature_policy.require_post_quantum {
544            let dilithium_valid = verify_dilithium_signature(
545                content_to_verify,
546                &signatures.dilithium_sig,
547                &get_trusted_dilithium_pubkey(),
548            );
549
550            if !dilithium_valid {
551                crate::println!("[PKG] REJECT: Dilithium signature verification failed");
552                return Err(KernelError::PermissionDenied {
553                    operation: "verify Dilithium signature",
554                });
555            }
556        }
557
558        crate::println!(
559            "[PKG] Package signature verification passed (Ed25519{})",
560            if self.signature_policy.require_post_quantum {
561                " + Dilithium"
562            } else {
563                ""
564            }
565        );
566
567        Ok(())
568    }
569
570    /// Install package from data
571    fn install_package(
572        &mut self,
573        package_id: PackageId,
574        _version: Version,
575        package_data: Vec<u8>,
576    ) -> PkgResult<()> {
577        use format::{Compression, PackageHeader};
578
579        // Step 1: Parse package header
580        if package_data.len() < 64 {
581            return Err(KernelError::InvalidArgument {
582                name: "package_data",
583                value: "too_short",
584            });
585        }
586
587        // SAFETY: package_data.as_ptr() points to at least 64 bytes (checked
588        // above). read_unaligned handles potentially misaligned byte buffers.
589        // The resulting header fields (offsets, sizes) are bounds-checked
590        // against package_data.len() before use.
591        let header =
592            unsafe { core::ptr::read_unaligned(package_data.as_ptr() as *const PackageHeader) };
593
594        // Step 2: Extract and parse metadata (JSON)
595        let meta_offset = header.metadata_offset as usize;
596        let meta_size = header.metadata_size as usize;
597
598        if meta_offset + meta_size > package_data.len() {
599            return Err(KernelError::InvalidArgument {
600                name: "metadata_offset",
601                value: "out_of_bounds",
602            });
603        }
604
605        let meta_bytes = &package_data[meta_offset..meta_offset + meta_size];
606        let metadata = parse_package_metadata(meta_bytes)?;
607
608        // Step 3: Extract content section
609        let content_offset = header.content_offset as usize;
610        let content_size = header.content_size as usize;
611
612        if content_offset + content_size > package_data.len() {
613            return Err(KernelError::InvalidArgument {
614                name: "content_offset",
615                value: "out_of_bounds",
616            });
617        }
618
619        let content_bytes = &package_data[content_offset..content_offset + content_size];
620
621        // Step 4: Decompress content
622        let compression = header.get_compression().unwrap_or(Compression::None);
623        let decompressed = format::decompress(content_bytes, compression).map_err(|_| {
624            KernelError::InvalidArgument {
625                name: "compression",
626                value: "decompression_failed",
627            }
628        })?;
629
630        // Step 5: Extract files from decompressed content
631        extract_package_files(&package_id, &decompressed)?;
632
633        // Step 6: Update installed registry
634        self.installed.insert(package_id.clone(), metadata);
635
636        crate::println!(
637            "[PKG] Package {} successfully extracted and installed",
638            package_id
639        );
640
641        Ok(())
642    }
643
644    /// Find packages that depend on the given package
645    fn find_dependents(&self, package_id: &PackageId) -> Vec<PackageId> {
646        self.installed
647            .iter()
648            .filter(|(_, meta)| meta.dependencies.iter().any(|dep| &dep.name == package_id))
649            .map(|(id, _)| id.clone())
650            .collect()
651    }
652
653    // ========================================================================
654    // Transaction System
655    // ========================================================================
656
657    /// Begin an atomic transaction.
658    ///
659    /// All install/remove operations after this call are staged and only
660    /// applied when `commit_transaction()` is called. If any operation
661    /// fails or `rollback_transaction()` is called, the package state
662    /// reverts to the snapshot taken here.
663    pub fn begin_transaction(&mut self) -> PkgResult<()> {
664        if self.transaction.is_some() {
665            return Err(KernelError::InvalidState {
666                expected: "no active transaction",
667                actual: "transaction already in progress",
668            });
669        }
670        self.transaction = Some(PackageTransaction::new(&self.installed));
671        crate::println!("[PKG] Transaction started");
672        Ok(())
673    }
674
675    /// Commit the current transaction, persisting state to the database.
676    pub fn commit_transaction(&mut self) -> PkgResult<()> {
677        let txn = self.transaction.take().ok_or(KernelError::InvalidState {
678            expected: "active transaction",
679            actual: "no transaction in progress",
680        })?;
681
682        let _op_count = txn.operations.len();
683        crate::println!("[PKG] Transaction committed ({} operations)", _op_count);
684
685        // Persist to on-disk database
686        if let Err(_e) = self.database.save() {
687            crate::println!("[PKG] Warning: failed to persist database: {:?}", _e);
688        }
689        Ok(())
690    }
691
692    /// Roll back the current transaction, restoring the pre-transaction state.
693    pub fn rollback_transaction(&mut self) -> PkgResult<()> {
694        let txn = self.transaction.take().ok_or(KernelError::InvalidState {
695            expected: "active transaction",
696            actual: "no transaction in progress",
697        })?;
698
699        self.installed = txn.snapshot;
700        crate::println!("[PKG] Transaction rolled back");
701        Ok(())
702    }
703
704    // ========================================================================
705    // Upgrade Operations
706    // ========================================================================
707
708    /// Upgrade a single installed package to the latest available version.
709    pub fn upgrade(&mut self, package_id: &str) -> PkgResult<()> {
710        if !self.installed.contains_key(package_id) {
711            return Err(KernelError::NotFound {
712                resource: "installed package",
713                id: 0,
714            });
715        }
716
717        let current_version = self
718            .installed
719            .get(package_id)
720            .map(|m| m.version.clone())
721            .ok_or(KernelError::NotFound {
722                resource: "package",
723                id: 0,
724            })?;
725
726        // Ask the resolver for the latest available version
727        let latest = self.resolver.latest_version(package_id);
728        match latest {
729            Some(v) if v > current_version => {
730                crate::println!(
731                    "[PKG] Upgrading {} from {}.{}.{} to {}.{}.{}",
732                    package_id,
733                    current_version.major,
734                    current_version.minor,
735                    current_version.patch,
736                    v.major,
737                    v.minor,
738                    v.patch
739                );
740                let version_req = alloc::format!("{}.{}.{}", v.major, v.minor, v.patch);
741                // Remove old, install new
742                self.installed.remove(package_id);
743                self.install(String::from(package_id), version_req)?;
744            }
745            _ => {
746                crate::println!("[PKG] {} is already at the latest version", package_id);
747            }
748        }
749
750        Ok(())
751    }
752
753    /// Upgrade all installed packages to their latest available versions.
754    pub fn upgrade_all(&mut self) -> PkgResult<usize> {
755        let installed_names: Vec<PackageId> = self.installed.keys().cloned().collect();
756        let mut upgraded = 0;
757
758        for name in &installed_names {
759            let current = self.installed.get(name).map(|m| m.version.clone());
760            let latest = self.resolver.latest_version(name);
761
762            if let (Some(cur), Some(lat)) = (current, latest) {
763                if lat > cur {
764                    self.upgrade(name)?;
765                    upgraded += 1;
766                }
767            }
768        }
769
770        crate::println!("[PKG] Upgraded {} package(s)", upgraded);
771        Ok(upgraded)
772    }
773
774    // ========================================================================
775    // Search and Query
776    // ========================================================================
777
778    /// Search available packages by name substring.
779    pub fn search(&self, query: &str) -> Vec<(PackageId, Version)> {
780        self.resolver.search(query)
781    }
782
783    /// Get detailed information about a package (installed or available).
784    pub fn get_package_info(&self, package_id: &str) -> Option<PackageMetadata> {
785        // Check installed first
786        if let Some(meta) = self.installed.get(package_id) {
787            return Some(meta.clone());
788        }
789        // Check resolver's known packages
790        self.resolver.get_package_metadata(package_id)
791    }
792
793    /// Get a reference to the file manifest.
794    pub fn file_manifest(&self) -> &manifest::FileManifest {
795        &self.file_manifest
796    }
797
798    /// Get a mutable reference to the file manifest.
799    pub fn file_manifest_mut(&mut self) -> &mut manifest::FileManifest {
800        &mut self.file_manifest
801    }
802
803    /// Get a reference to the persistent database.
804    pub fn database(&self) -> &database::PackageDatabase {
805        &self.database
806    }
807
808    /// Get a mutable reference to the persistent database.
809    pub fn database_mut(&mut self) -> &mut database::PackageDatabase {
810        &mut self.database
811    }
812}
813
814impl Default for PackageManager {
815    fn default() -> Self {
816        Self::new()
817    }
818}
819
820/// Global package manager instance protected by Mutex
821static PACKAGE_MANAGER: Mutex<Option<PackageManager>> = Mutex::new(None);
822
823/// Initialize package management system
824pub fn init() {
825    *PACKAGE_MANAGER.lock() = Some(PackageManager::new());
826    crate::println!("[PKG] Package management system initialized");
827}
828
829/// Execute a closure with the package manager (mutable access)
830pub fn with_package_manager<R, F: FnOnce(&mut PackageManager) -> R>(f: F) -> Option<R> {
831    PACKAGE_MANAGER.lock().as_mut().map(f)
832}
833
834// ============================================================================
835// Signature Verification Helpers
836// ============================================================================
837
838/// Trusted Dilithium public key (post-quantum ML-DSA-65)
839///
840/// This returns the embedded ML-DSA-65 (Dilithium3) public key for package
841/// signature verification. In production, this key would be:
842/// - Embedded at kernel build time from a secure key ceremony
843/// - Stored in TPM/secure enclave for hardware-backed verification
844/// - Part of the kernel's trusted computing base
845///
846/// Key size: 1952 bytes per NIST FIPS 204 specification
847fn get_trusted_dilithium_pubkey() -> Vec<u8> {
848    use crate::crypto::pq_params::dilithium::level3::PUBLIC_KEY_SIZE;
849
850    // ML-DSA-65 public key embedded at build time
851    // This is a deterministically generated test key for development
852    // Production builds MUST replace this with a real key ceremony output
853    let mut key = vec![0u8; PUBLIC_KEY_SIZE];
854
855    // Seed the key with deterministic bytes for reproducible testing
856    // Real key would be loaded from secure storage or compiled-in
857    let seed: [u8; 32] = [
858        0x56, 0x65, 0x72, 0x69, 0x64, 0x69, 0x61, 0x6e, // "Veridian"
859        0x4f, 0x53, 0x2d, 0x50, 0x4b, 0x47, 0x2d, 0x4b, // "OS-PKG-K"
860        0x45, 0x59, 0x2d, 0x53, 0x45, 0x45, 0x44, 0x00, // "EY-SEED\0"
861        0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // Version 1
862    ];
863
864    // Expand seed into full public key using deterministic derivation
865    // This is for testing only - real keys use proper key generation
866    for (i, key_byte) in key.iter_mut().enumerate() {
867        // Simple expansion: hash(seed || counter)
868        let counter = i as u8;
869        let idx = i % 32;
870        *key_byte = seed[idx].wrapping_add(counter).wrapping_mul(0x6D);
871    }
872
873    // Set magic bytes to identify key type
874    key[0] = 0x4D; // 'M' for ML-DSA
875    key[1] = 0x44; // 'D'
876    key[2] = 0x36; // '6' for level 6 (65)
877    key[3] = 0x35; // '5'
878
879    key
880}
881
882/// Verify Dilithium (ML-DSA) signature.
883///
884/// Structural verification of a Dilithium/ML-DSA signature. A full algebraic
885/// verification (NTT, matrix operations) is not yet implemented; this checks
886/// that the signature components have valid structure and reasonable entropy.
887fn verify_dilithium_signature(message: &[u8], signature: &[u8], public_key: &[u8]) -> bool {
888    // Delegate to the dedicated Dilithium/ML-DSA module which performs
889    // FIPS 204 structural verification with hash-based binding.
890    match crate::security::dilithium::verify(public_key, message, signature) {
891        Ok(valid) => valid,
892        Err(_e) => {
893            crate::println!("[PKG] Dilithium verification error: {:?}", _e);
894            false
895        }
896    }
897}
898
899// ============================================================================
900// Package Metadata Parsing
901// ============================================================================
902
903/// Parse package metadata from JSON bytes
904fn parse_package_metadata(data: &[u8]) -> PkgResult<PackageMetadata> {
905    // Simple JSON parser for package metadata
906    // Format: {"name":"...", "version":"...", "author":"...", ...}
907
908    let json_str = core::str::from_utf8(data).map_err(|_| KernelError::InvalidArgument {
909        name: "metadata",
910        value: "not_utf8",
911    })?;
912
913    // Extract fields using simple pattern matching
914    let name = extract_json_string(json_str, "name").unwrap_or_else(|| String::from("unknown"));
915    let version_str =
916        extract_json_string(json_str, "version").unwrap_or_else(|| String::from("0.0.0"));
917    let author = extract_json_string(json_str, "author").unwrap_or_else(|| String::from("unknown"));
918    let description =
919        extract_json_string(json_str, "description").unwrap_or_else(|| String::from(""));
920    let license = extract_json_string(json_str, "license").unwrap_or_else(|| String::from(""));
921
922    // Parse version
923    let version = parse_version(&version_str);
924
925    // Parse dependencies array
926    let dependencies = extract_json_array(json_str, "dependencies")
927        .map(|deps| {
928            deps.into_iter()
929                .filter_map(|dep_str| {
930                    let parts: Vec<&str> = dep_str.split('@').collect();
931                    if !parts.is_empty() {
932                        Some(Dependency {
933                            name: String::from(parts[0]),
934                            version_req: if parts.len() > 1 {
935                                String::from(parts[1])
936                            } else {
937                                String::from("*")
938                            },
939                        })
940                    } else {
941                        None
942                    }
943                })
944                .collect()
945        })
946        .unwrap_or_else(Vec::new);
947
948    Ok(PackageMetadata {
949        name,
950        version,
951        author,
952        description,
953        license,
954        dependencies,
955        conflicts: Vec::new(),
956    })
957}
958
959/// Extract string value from JSON
960fn extract_json_string(json: &str, key: &str) -> Option<String> {
961    let pattern = alloc::format!("\"{}\":\"", key);
962    if let Some(start) = json.find(&pattern) {
963        let value_start = start + pattern.len();
964        if let Some(end) = json[value_start..].find('"') {
965            return Some(String::from(&json[value_start..value_start + end]));
966        }
967    }
968    None
969}
970
971/// Extract array from JSON (simplified)
972fn extract_json_array(json: &str, key: &str) -> Option<Vec<String>> {
973    let pattern = alloc::format!("\"{}\":[", key);
974    if let Some(start) = json.find(&pattern) {
975        let array_start = start + pattern.len();
976        if let Some(end) = json[array_start..].find(']') {
977            let array_str = &json[array_start..array_start + end];
978            let items: Vec<String> = array_str
979                .split(',')
980                .map(|s: &str| String::from(s.trim().trim_matches('"')))
981                .filter(|s: &String| !s.is_empty())
982                .collect();
983            return Some(items);
984        }
985    }
986    None
987}
988
989/// Parse semantic version string
990fn parse_version(version_str: &str) -> Version {
991    let parts: Vec<&str> = version_str.split('.').collect();
992
993    let major = parts.first().and_then(|s| s.parse().ok()).unwrap_or(0);
994    let minor = parts.get(1).and_then(|s| s.parse().ok()).unwrap_or(0);
995    let patch = parts.get(2).and_then(|s| s.parse().ok()).unwrap_or(0);
996
997    Version {
998        major,
999        minor,
1000        patch,
1001    }
1002}
1003
1004// ============================================================================
1005// Package File Extraction
1006// ============================================================================
1007
1008/// File entry in package archive
1009///
1010/// Phase 4 (package ecosystem) -- struct fields used during extraction
1011/// but not directly accessed outside `extract_package_files`.
1012#[allow(dead_code)] // Package format documentation struct -- fields describe archive layout
1013struct PackageFileEntry {
1014    path: String,
1015    size: u64,
1016    mode: u32,
1017    data_offset: usize,
1018}
1019
1020/// Extract files from decompressed package content
1021fn extract_package_files(package_id: &str, data: &[u8]) -> PkgResult<()> {
1022    // Package content format:
1023    // [4 bytes] Number of files
1024    // For each file:
1025    //   [2 bytes] Path length
1026    //   [N bytes] Path (UTF-8)
1027    //   [8 bytes] File size
1028    //   [4 bytes] File mode/permissions
1029    //   [N bytes] File data
1030
1031    if data.len() < 4 {
1032        return Err(KernelError::InvalidArgument {
1033            name: "package_content",
1034            value: "too_short",
1035        });
1036    }
1037
1038    let num_files = u32::from_le_bytes([data[0], data[1], data[2], data[3]]) as usize;
1039    let mut pos = 4;
1040
1041    let install_base = alloc::format!("/usr/local/packages/{}", package_id);
1042
1043    #[cfg_attr(not(target_arch = "x86_64"), allow(unused_variables))]
1044    for file_idx in 0..num_files {
1045        // Read path length
1046        if pos + 2 > data.len() {
1047            return Err(KernelError::InvalidArgument {
1048                name: "file_entry",
1049                value: "truncated_path_len",
1050            });
1051        }
1052        let path_len = u16::from_le_bytes([data[pos], data[pos + 1]]) as usize;
1053        pos += 2;
1054
1055        // Read path
1056        if pos + path_len > data.len() {
1057            return Err(KernelError::InvalidArgument {
1058                name: "file_entry",
1059                value: "truncated_path",
1060            });
1061        }
1062        let path = core::str::from_utf8(&data[pos..pos + path_len]).map_err(|_| {
1063            KernelError::InvalidArgument {
1064                name: "file_path",
1065                value: "not_utf8",
1066            }
1067        })?;
1068        pos += path_len;
1069
1070        // Read file size
1071        if pos + 8 > data.len() {
1072            return Err(KernelError::InvalidArgument {
1073                name: "file_entry",
1074                value: "truncated_size",
1075            });
1076        }
1077        let file_size = u64::from_le_bytes([
1078            data[pos],
1079            data[pos + 1],
1080            data[pos + 2],
1081            data[pos + 3],
1082            data[pos + 4],
1083            data[pos + 5],
1084            data[pos + 6],
1085            data[pos + 7],
1086        ]) as usize;
1087        pos += 8;
1088
1089        // Read file mode
1090        if pos + 4 > data.len() {
1091            return Err(KernelError::InvalidArgument {
1092                name: "file_entry",
1093                value: "truncated_mode",
1094            });
1095        }
1096        let file_mode =
1097            u32::from_le_bytes([data[pos], data[pos + 1], data[pos + 2], data[pos + 3]]);
1098        pos += 4;
1099
1100        // Read file data
1101        if pos + file_size > data.len() {
1102            return Err(KernelError::InvalidArgument {
1103                name: "file_entry",
1104                value: "truncated_data",
1105            });
1106        }
1107        let file_data = &data[pos..pos + file_size];
1108        pos += file_size;
1109
1110        // Install file to filesystem
1111        let full_path = alloc::format!("{}/{}", install_base, path);
1112        install_file(&full_path, file_data, file_mode)?;
1113
1114        crate::println!(
1115            "[PKG] Extracted file {}/{}: {} ({} bytes, mode {:o})",
1116            file_idx + 1,
1117            num_files,
1118            path,
1119            file_size,
1120            file_mode
1121        );
1122    }
1123
1124    Ok(())
1125}
1126
1127/// Install a single file to the filesystem
1128fn install_file(path: &str, data: &[u8], mode: u32) -> PkgResult<()> {
1129    // Create parent directories if needed
1130    if let Some(parent_end) = path.rfind('/') {
1131        let parent_path = &path[..parent_end];
1132        create_directories(parent_path)?;
1133    }
1134
1135    // Write file to VFS
1136    // In a full implementation, this would use the VFS layer
1137    #[cfg(feature = "alloc")]
1138    {
1139        use crate::fs::{get_vfs, OpenFlags, Permissions};
1140
1141        // Create parent directories if needed
1142        if let Some(parent_end) = path.rfind('/') {
1143            if parent_end > 0 {
1144                let parent = &path[..parent_end];
1145                let perms = Permissions::from_mode(0o755);
1146                // Ignore errors for existing directories
1147                let _ = get_vfs().write().mkdir(parent, perms);
1148            }
1149        }
1150
1151        // Open/create file via VFS and write data
1152        // For now, use the ramfs node write capability
1153        let flags = OpenFlags::read_write();
1154        match get_vfs().read().open(path, flags) {
1155            Ok(node) => {
1156                // Write data to the node
1157                if let Err(_e) = node.write(0, data) {
1158                    crate::println!("[PKG] Warning: Could not write to file {}", path);
1159                }
1160            }
1161            Err(_e) => {
1162                // File creation may not be fully supported yet
1163                crate::println!(
1164                    "[PKG] Warning: Could not create file {} (VFS limitation)",
1165                    path
1166                );
1167            }
1168        }
1169        let _ = mode; // Mode will be set when VFS supports it
1170    }
1171
1172    #[cfg(not(feature = "alloc"))]
1173    {
1174        // Without alloc, just log
1175        let _ = (path, data, mode);
1176    }
1177
1178    Ok(())
1179}
1180
1181#[cfg(test)]
1182mod tests {
1183    #[allow(unused_imports)]
1184    use alloc::vec;
1185
1186    use super::*;
1187
1188    // ---- Version ----
1189
1190    #[test]
1191    fn test_version_new() {
1192        let v = Version::new(1, 2, 3);
1193        assert_eq!(v.major, 1);
1194        assert_eq!(v.minor, 2);
1195        assert_eq!(v.patch, 3);
1196    }
1197
1198    #[test]
1199    fn test_version_ordering() {
1200        let v1 = Version::new(1, 0, 0);
1201        let v2 = Version::new(1, 0, 1);
1202        let v3 = Version::new(1, 1, 0);
1203        let v4 = Version::new(2, 0, 0);
1204        assert!(v1 < v2);
1205        assert!(v2 < v3);
1206        assert!(v3 < v4);
1207        assert_eq!(v1, Version::new(1, 0, 0));
1208    }
1209
1210    #[test]
1211    fn test_version_clone_and_eq() {
1212        let v1 = Version::new(5, 10, 15);
1213        let v2 = v1.clone();
1214        assert_eq!(v1, v2);
1215    }
1216
1217    // ---- parse_version ----
1218
1219    #[test]
1220    fn test_parse_version_full() {
1221        let v = parse_version("1.2.3");
1222        assert_eq!(v, Version::new(1, 2, 3));
1223    }
1224
1225    #[test]
1226    fn test_parse_version_partial() {
1227        let v = parse_version("5.10");
1228        assert_eq!(v, Version::new(5, 10, 0));
1229    }
1230
1231    #[test]
1232    fn test_parse_version_major_only() {
1233        let v = parse_version("7");
1234        assert_eq!(v, Version::new(7, 0, 0));
1235    }
1236
1237    #[test]
1238    fn test_parse_version_invalid() {
1239        let v = parse_version("abc");
1240        assert_eq!(v, Version::new(0, 0, 0));
1241    }
1242
1243    #[test]
1244    fn test_parse_version_empty() {
1245        let v = parse_version("");
1246        assert_eq!(v, Version::new(0, 0, 0));
1247    }
1248
1249    // ---- extract_json_string ----
1250
1251    #[test]
1252    fn test_extract_json_string_found() {
1253        let json = r#"{"name":"hello","version":"1.0.0"}"#;
1254        assert_eq!(
1255            extract_json_string(json, "name"),
1256            Some(String::from("hello"))
1257        );
1258        assert_eq!(
1259            extract_json_string(json, "version"),
1260            Some(String::from("1.0.0"))
1261        );
1262    }
1263
1264    #[test]
1265    fn test_extract_json_string_not_found() {
1266        let json = r#"{"name":"hello"}"#;
1267        assert_eq!(extract_json_string(json, "missing"), None);
1268    }
1269
1270    // ---- extract_json_array ----
1271
1272    #[test]
1273    fn test_extract_json_array_found() {
1274        let json = r#"{"items":["a","b","c"]}"#;
1275        let arr = extract_json_array(json, "items").unwrap();
1276        assert_eq!(arr.len(), 3);
1277        assert_eq!(arr[0], "a");
1278        assert_eq!(arr[1], "b");
1279        assert_eq!(arr[2], "c");
1280    }
1281
1282    #[test]
1283    fn test_extract_json_array_not_found() {
1284        let json = r#"{"name":"test"}"#;
1285        assert!(extract_json_array(json, "items").is_none());
1286    }
1287
1288    #[test]
1289    fn test_extract_json_array_empty() {
1290        let json = r#"{"items":[]}"#;
1291        let arr = extract_json_array(json, "items").unwrap();
1292        assert!(arr.is_empty());
1293    }
1294
1295    // ---- parse_package_metadata ----
1296
1297    #[test]
1298    fn test_parse_package_metadata_valid() {
1299        let json = br#"{"name":"test-pkg","version":"1.2.3","author":"dev","description":"a package","license":"MIT"}"#;
1300        let meta = parse_package_metadata(json).unwrap();
1301        assert_eq!(meta.name, "test-pkg");
1302        assert_eq!(meta.version, Version::new(1, 2, 3));
1303        assert_eq!(meta.author, "dev");
1304        assert_eq!(meta.description, "a package");
1305        assert_eq!(meta.license, "MIT");
1306    }
1307
1308    #[test]
1309    fn test_parse_package_metadata_minimal() {
1310        let json = br#"{}"#;
1311        let meta = parse_package_metadata(json).unwrap();
1312        assert_eq!(meta.name, "unknown");
1313        assert_eq!(meta.version, Version::new(0, 0, 0));
1314    }
1315
1316    #[test]
1317    fn test_parse_package_metadata_invalid_utf8() {
1318        let data: &[u8] = &[0xFF, 0xFE];
1319        assert!(parse_package_metadata(data).is_err());
1320    }
1321
1322    #[test]
1323    fn test_parse_package_metadata_with_deps() {
1324        let json = br#"{"name":"app","version":"1.0.0","dependencies":["lib@>=1.0.0","core"]}"#;
1325        let meta = parse_package_metadata(json).unwrap();
1326        assert_eq!(meta.dependencies.len(), 2);
1327        assert_eq!(meta.dependencies[0].name, "lib");
1328        assert_eq!(meta.dependencies[0].version_req, ">=1.0.0");
1329        assert_eq!(meta.dependencies[1].name, "core");
1330        assert_eq!(meta.dependencies[1].version_req, "*");
1331    }
1332
1333    // ---- InstallOptions ----
1334
1335    #[test]
1336    fn test_install_options_default() {
1337        let opts = InstallOptions::default();
1338        assert!(!opts.force_unsigned);
1339        assert!(opts.expected_hash.is_none());
1340    }
1341
1342    // ---- PackageManager basic operations ----
1343
1344    #[test]
1345    fn test_package_manager_new() {
1346        let pm = PackageManager::new();
1347        assert!(pm.list_installed().is_empty());
1348        assert!(!pm.is_installed(&String::from("test")));
1349    }
1350
1351    #[test]
1352    fn test_package_manager_default() {
1353        let pm = PackageManager::default();
1354        assert!(pm.list_installed().is_empty());
1355    }
1356
1357    #[test]
1358    fn test_package_manager_remove_not_installed() {
1359        let mut pm = PackageManager::new();
1360        let result = pm.remove(&String::from("nonexistent"));
1361        assert!(result.is_err());
1362    }
1363
1364    #[test]
1365    fn test_package_manager_get_metadata_none() {
1366        let pm = PackageManager::new();
1367        assert!(pm.get_metadata(&String::from("missing")).is_none());
1368    }
1369
1370    // ---- Transaction system ----
1371
1372    #[test]
1373    fn test_transaction_begin_commit() {
1374        let mut pm = PackageManager::new();
1375        assert!(pm.begin_transaction().is_ok());
1376        assert!(pm.commit_transaction().is_ok());
1377    }
1378
1379    #[test]
1380    fn test_transaction_double_begin() {
1381        let mut pm = PackageManager::new();
1382        assert!(pm.begin_transaction().is_ok());
1383        assert!(pm.begin_transaction().is_err());
1384    }
1385
1386    #[test]
1387    fn test_transaction_commit_without_begin() {
1388        let mut pm = PackageManager::new();
1389        assert!(pm.commit_transaction().is_err());
1390    }
1391
1392    #[test]
1393    fn test_transaction_rollback_without_begin() {
1394        let mut pm = PackageManager::new();
1395        assert!(pm.rollback_transaction().is_err());
1396    }
1397
1398    #[test]
1399    fn test_transaction_rollback() {
1400        let mut pm = PackageManager::new();
1401        // Insert a package manually for testing
1402        pm.installed.insert(
1403            String::from("test-pkg"),
1404            PackageMetadata {
1405                name: String::from("test-pkg"),
1406                version: Version::new(1, 0, 0),
1407                author: String::new(),
1408                description: String::new(),
1409                license: String::new(),
1410                dependencies: vec![],
1411                conflicts: vec![],
1412            },
1413        );
1414
1415        assert!(pm.begin_transaction().is_ok());
1416        // Remove the package
1417        assert!(pm.remove(&String::from("test-pkg")).is_ok());
1418        assert!(!pm.is_installed(&String::from("test-pkg")));
1419        // Rollback
1420        assert!(pm.rollback_transaction().is_ok());
1421        assert!(pm.is_installed(&String::from("test-pkg")));
1422    }
1423
1424    // ---- find_dependents ----
1425
1426    #[test]
1427    fn test_find_dependents() {
1428        let mut pm = PackageManager::new();
1429        pm.installed.insert(
1430            String::from("lib"),
1431            PackageMetadata {
1432                name: String::from("lib"),
1433                version: Version::new(1, 0, 0),
1434                author: String::new(),
1435                description: String::new(),
1436                license: String::new(),
1437                dependencies: vec![],
1438                conflicts: vec![],
1439            },
1440        );
1441        pm.installed.insert(
1442            String::from("app"),
1443            PackageMetadata {
1444                name: String::from("app"),
1445                version: Version::new(1, 0, 0),
1446                author: String::new(),
1447                description: String::new(),
1448                license: String::new(),
1449                dependencies: vec![Dependency {
1450                    name: String::from("lib"),
1451                    version_req: String::from("*"),
1452                }],
1453                conflicts: vec![],
1454            },
1455        );
1456        let deps = pm.find_dependents(&String::from("lib"));
1457        assert_eq!(deps.len(), 1);
1458        assert_eq!(deps[0], "app");
1459    }
1460
1461    #[test]
1462    fn test_remove_with_dependents_fails() {
1463        let mut pm = PackageManager::new();
1464        pm.installed.insert(
1465            String::from("lib"),
1466            PackageMetadata {
1467                name: String::from("lib"),
1468                version: Version::new(1, 0, 0),
1469                author: String::new(),
1470                description: String::new(),
1471                license: String::new(),
1472                dependencies: vec![],
1473                conflicts: vec![],
1474            },
1475        );
1476        pm.installed.insert(
1477            String::from("app"),
1478            PackageMetadata {
1479                name: String::from("app"),
1480                version: Version::new(1, 0, 0),
1481                author: String::new(),
1482                description: String::new(),
1483                license: String::new(),
1484                dependencies: vec![Dependency {
1485                    name: String::from("lib"),
1486                    version_req: String::from("*"),
1487                }],
1488                conflicts: vec![],
1489            },
1490        );
1491        // Cannot remove lib because app depends on it
1492        assert!(pm.remove(&String::from("lib")).is_err());
1493    }
1494
1495    // ---- extract_package_files ----
1496
1497    #[test]
1498    fn test_extract_package_files_too_short() {
1499        let data: &[u8] = &[0, 1, 2];
1500        assert!(extract_package_files("test", data).is_err());
1501    }
1502
1503    #[test]
1504    fn test_extract_package_files_zero_files() {
1505        let data: &[u8] = &[0, 0, 0, 0]; // num_files = 0
1506        assert!(extract_package_files("test", data).is_ok());
1507    }
1508}
1509
1510/// Create directory hierarchy
1511fn create_directories(path: &str) -> PkgResult<()> {
1512    // Split path and create each component
1513    let components: Vec<&str> = path.split('/').filter(|s| !s.is_empty()).collect();
1514
1515    let mut current_path = String::new();
1516    for component in components {
1517        current_path.push('/');
1518        current_path.push_str(component);
1519
1520        // Try to create directory (ignore if exists)
1521        #[cfg(feature = "alloc")]
1522        {
1523            use crate::fs::{get_vfs, Permissions};
1524            let perms = Permissions::from_mode(0o755);
1525            // Ignore errors for existing directories
1526            let _ = get_vfs().write().mkdir(&current_path, perms);
1527        }
1528    }
1529
1530    Ok(())
1531}