Skip to main content

pkgar_core/
header.rs

1//! The packed structs represent the on-disk format of pkgar
2
3use alloc::vec;
4use bytemuck::{Pod, PodCastError, Zeroable};
5use core::mem;
6use dryoc::classic::crypto_sign::crypto_sign_open;
7
8use crate::{Entry, Error, HeaderFlags, PublicKey, ENTRY_SIZE, HEADER_SIZE};
9
10#[derive(Clone, Copy, Debug, Pod, Zeroable)]
11#[repr(packed, C)]
12pub struct Header {
13    /// NaCl signature of header data
14    pub signature: [u8; 64],
15    /// NaCl public key used to generate signature
16    pub public_key: [u8; 32],
17    /// Blake3 sum of entry data
18    pub blake3: [u8; 32],
19    /// Count of Entry structs, which starts immediately after header struct
20    pub count: u32,
21    /// Generic flags contain data and entry struct properties
22    pub flags: HeaderFlags,
23}
24
25impl Header {
26    /// Parse header from raw header data and verify using public key
27    pub fn new<'a>(data: &'a [u8], public_key: &PublicKey) -> Result<&'a Header, Error> {
28        // Retrieve signed header data
29        let signed = data
30            .get(..mem::size_of::<Header>())
31            .ok_or(Error::Cast(PodCastError::SizeMismatch))?;
32
33        // Verify signature
34        let mut verified = vec![0; signed.len() - 64];
35        crypto_sign_open(&mut verified, signed, public_key)?;
36
37        // Check that verified data matches signed data after skipping the signature
38        if verified.as_slice() != &signed[64..] {
39            return Err(Error::InvalidData);
40        }
41
42        // Create header from signed data and check that public key matches
43        let header: &Header = unsafe { Header::new_unchecked(signed)? };
44        if header.public_key != public_key.as_ref()[..] {
45            return Err(Error::InvalidKey);
46        }
47
48        Ok(header)
49    }
50
51    /// Parse header from raw header data without verification
52    pub unsafe fn new_unchecked(data: &[u8]) -> Result<&Header, Error> {
53        Ok(bytemuck::try_from_bytes(data)?)
54    }
55
56    pub fn count(&self) -> u32 {
57        self.count
58    }
59
60    /// Retrieve the size of the entries
61    pub fn entries_size(&self) -> Result<usize, Error> {
62        (self.count as usize)
63            .checked_mul(ENTRY_SIZE)
64            .ok_or(Error::Overflow)
65    }
66
67    /// Retrieve the size of the Header and its entries
68    pub fn total_size(&self) -> Result<usize, Error> {
69        self.entries_size()?
70            .checked_add(HEADER_SIZE)
71            .ok_or(Error::Overflow)
72    }
73
74    /// Parse entries from raw entries data and verify using blake3
75    pub fn entries<'a>(&self, data: &'a [u8]) -> Result<&'a [Entry], Error> {
76        let entries_size = self.entries_size()?;
77
78        let entries_data = data
79            .get(..entries_size)
80            .ok_or(Error::Cast(PodCastError::SizeMismatch))?;
81
82        let hash = {
83            let mut hasher = blake3::Hasher::new();
84            hasher.update_rayon(entries_data);
85            hasher.finalize()
86        };
87
88        if &self.blake3 != hash.as_bytes() {
89            return Err(Error::InvalidBlake3);
90        }
91
92        unsafe { Self::entries_unchecked(entries_data) }
93    }
94
95    /// Parse entries from raw entries data without verification
96    pub unsafe fn entries_unchecked(data: &[u8]) -> Result<&[Entry], Error> {
97        Ok(bytemuck::try_cast_slice(data)?)
98    }
99}
100/*
101impl fmt::Debug for Header {
102    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
103        write!(f, "Header {{\n\tsignature: {:?},\n\tpublic_key: {:?},\n\tblake3: {:?},count: {:?},\n}}",
104            &self.signature[..],
105            self.public_key,
106            self.blake3,
107            self.count(),
108        )
109    }
110}*/