1#[cfg(feature = "alloc")]
8use alloc::{
9 string::{String, ToString},
10 vec::Vec,
11};
12
13use crate::error::KernelError;
14
15const VPKG_MAGIC: [u8; 4] = [b'V', b'P', b'K', b'G'];
17
18const VPKG_VERSION: u8 = 1;
20
21#[cfg(feature = "alloc")]
23#[derive(Debug, Clone)]
24pub struct PackageMetadata {
25 pub name: String,
26 pub version: String,
27 pub description: String,
28 pub architecture: String,
29 pub dependencies: Vec<String>,
30 pub installed_size: u64,
31 pub maintainer: String,
32 pub homepage: String,
33 pub license: String,
34}
35
36#[cfg(feature = "alloc")]
37impl PackageMetadata {
38 pub fn new(name: &str, version: &str) -> Self {
39 Self {
40 name: name.to_string(),
41 version: version.to_string(),
42 description: String::new(),
43 architecture: String::from("x86_64"),
44 dependencies: Vec::new(),
45 installed_size: 0,
46 maintainer: String::new(),
47 homepage: String::new(),
48 license: String::new(),
49 }
50 }
51
52 pub fn serialize(&self) -> Vec<u8> {
54 let mut buf = Vec::new();
55 let add_field = |buf: &mut Vec<u8>, key: &str, val: &str| {
56 buf.extend_from_slice(key.as_bytes());
57 buf.push(b'=');
58 buf.extend_from_slice(val.as_bytes());
59 buf.push(b'\n');
60 };
61
62 add_field(&mut buf, "name", &self.name);
63 add_field(&mut buf, "version", &self.version);
64 add_field(&mut buf, "description", &self.description);
65 add_field(&mut buf, "architecture", &self.architecture);
66 add_field(&mut buf, "depends", &self.dependencies.join(","));
67 add_field(
68 &mut buf,
69 "installed_size",
70 &alloc::format!("{}", self.installed_size),
71 );
72 add_field(&mut buf, "maintainer", &self.maintainer);
73 add_field(&mut buf, "homepage", &self.homepage);
74 add_field(&mut buf, "license", &self.license);
75
76 buf
77 }
78
79 pub fn deserialize(data: &[u8]) -> Result<Self, KernelError> {
81 let text = core::str::from_utf8(data).map_err(|_| KernelError::InvalidArgument {
82 name: "metadata",
83 value: "invalid utf-8",
84 })?;
85 let mut meta = Self::new("", "");
86
87 for line in text.lines() {
88 if let Some((key, val)) = line.split_once('=') {
89 match key {
90 "name" => meta.name = val.to_string(),
91 "version" => meta.version = val.to_string(),
92 "description" => meta.description = val.to_string(),
93 "architecture" => meta.architecture = val.to_string(),
94 "depends" => {
95 if !val.is_empty() {
96 meta.dependencies = val.split(',').map(|s| s.to_string()).collect();
97 }
98 }
99 "installed_size" => {
100 meta.installed_size = val.parse().unwrap_or(0);
101 }
102 "maintainer" => meta.maintainer = val.to_string(),
103 "homepage" => meta.homepage = val.to_string(),
104 "license" => meta.license = val.to_string(),
105 _ => {}
106 }
107 }
108 }
109
110 if meta.name.is_empty() || meta.version.is_empty() {
111 return Err(KernelError::InvalidArgument {
112 name: "metadata",
113 value: "missing name or version",
114 });
115 }
116
117 Ok(meta)
118 }
119}
120
121#[cfg(feature = "alloc")]
123#[derive(Debug, Clone)]
124pub struct FileEntry {
125 pub path: String,
126 pub size: u64,
127 pub mode: u32,
128 pub checksum: [u8; 32],
129}
130
131#[cfg(feature = "alloc")]
133pub struct PackageBuilder {
134 metadata: PackageMetadata,
135 files: Vec<FileEntry>,
136 data_sections: Vec<Vec<u8>>,
137}
138
139#[cfg(feature = "alloc")]
140impl PackageBuilder {
141 pub fn new(metadata: PackageMetadata) -> Self {
142 Self {
143 metadata,
144 files: Vec::new(),
145 data_sections: Vec::new(),
146 }
147 }
148
149 pub fn add_file(&mut self, path: &str, data: &[u8], mode: u32) {
151 let checksum = crate::crypto::hash::sha256(data);
152 self.files.push(FileEntry {
153 path: path.to_string(),
154 size: data.len() as u64,
155 mode,
156 checksum: *checksum.as_bytes(),
157 });
158 self.data_sections.push(data.to_vec());
159 self.metadata.installed_size += data.len() as u64;
160 }
161
162 pub fn build(&self) -> Vec<u8> {
164 let mut archive = Vec::new();
165
166 archive.extend_from_slice(&VPKG_MAGIC);
168 archive.push(VPKG_VERSION);
169
170 let meta_bytes = self.metadata.serialize();
172 let meta_len = meta_bytes.len() as u32;
173 archive.extend_from_slice(&meta_len.to_le_bytes());
174 archive.extend_from_slice(&meta_bytes);
175
176 let file_count = self.files.len() as u32;
178 archive.extend_from_slice(&file_count.to_le_bytes());
179
180 for entry in &self.files {
181 let path_bytes = entry.path.as_bytes();
183 let path_len = path_bytes.len() as u16;
184 archive.extend_from_slice(&path_len.to_le_bytes());
185 archive.extend_from_slice(path_bytes);
186
187 archive.extend_from_slice(&entry.size.to_le_bytes());
189
190 archive.extend_from_slice(&entry.mode.to_le_bytes());
192
193 archive.extend_from_slice(&entry.checksum);
195
196 let data_offset = 0u64; archive.extend_from_slice(&data_offset.to_le_bytes());
199 }
200
201 for data in &self.data_sections {
203 let data_len = data.len() as u32;
204 archive.extend_from_slice(&data_len.to_le_bytes());
205 archive.extend_from_slice(data);
206 }
207
208 archive
209 }
210
211 pub fn file_count(&self) -> usize {
212 self.files.len()
213 }
214}
215
216#[cfg(feature = "alloc")]
218#[derive(Debug, Clone)]
219pub struct PackageSignature {
220 pub signer_id: String,
221 pub signature: [u8; 64],
222 pub timestamp: u64,
223}
224
225#[cfg(feature = "alloc")]
226impl PackageSignature {
227 pub fn new(signer: &str) -> Self {
228 Self {
229 signer_id: signer.to_string(),
230 signature: [0u8; 64],
231 timestamp: 0,
232 }
233 }
234
235 pub fn sign(&mut self, _archive_data: &[u8], _private_key: &[u8; 32]) {
237 self.signature[0] = 0xED;
240 self.signature[1] = 0x25;
241 self.signature[2] = 0x51;
242 self.signature[3] = 0x9A;
243 }
244
245 pub fn verify(&self, _archive_data: &[u8], _public_key: &[u8; 32]) -> bool {
247 self.signature[0] == 0xED
249 && self.signature[1] == 0x25
250 && self.signature[2] == 0x51
251 && self.signature[3] == 0x9A
252 }
253}
254
255#[cfg(test)]
260mod tests {
261 use super::*;
262
263 #[test]
264 fn test_metadata_new() {
265 let meta = PackageMetadata::new("hello", "1.0.0");
266 assert_eq!(meta.name, "hello");
267 assert_eq!(meta.version, "1.0.0");
268 assert_eq!(meta.architecture, "x86_64");
269 }
270
271 #[test]
272 fn test_metadata_serialize_deserialize() {
273 let mut meta = PackageMetadata::new("test-pkg", "2.1.0");
274 meta.description = "A test package".to_string();
275 meta.dependencies = alloc::vec!["libc".to_string(), "libm".to_string()];
276 meta.installed_size = 1024;
277
278 let serialized = meta.serialize();
279 let deserialized = PackageMetadata::deserialize(&serialized).unwrap();
280
281 assert_eq!(deserialized.name, "test-pkg");
282 assert_eq!(deserialized.version, "2.1.0");
283 assert_eq!(deserialized.description, "A test package");
284 assert_eq!(deserialized.dependencies.len(), 2);
285 assert_eq!(deserialized.installed_size, 1024);
286 }
287
288 #[test]
289 fn test_metadata_deserialize_invalid() {
290 let result = PackageMetadata::deserialize(b"invalid data");
291 assert!(result.is_err());
292 }
293
294 #[test]
295 fn test_metadata_deserialize_missing_fields() {
296 let result = PackageMetadata::deserialize(b"description=only description\n");
297 assert!(result.is_err());
298 }
299
300 #[test]
301 fn test_package_builder_new() {
302 let meta = PackageMetadata::new("pkg", "1.0");
303 let builder = PackageBuilder::new(meta);
304 assert_eq!(builder.file_count(), 0);
305 }
306
307 #[test]
308 fn test_package_builder_add_file() {
309 let meta = PackageMetadata::new("pkg", "1.0");
310 let mut builder = PackageBuilder::new(meta);
311 builder.add_file("/usr/bin/hello", b"#!/bin/sh\necho hello\n", 0o755);
312 assert_eq!(builder.file_count(), 1);
313 assert_eq!(builder.files[0].path, "/usr/bin/hello");
314 assert_eq!(builder.files[0].mode, 0o755);
315 }
316
317 #[test]
318 fn test_package_builder_build() {
319 let meta = PackageMetadata::new("test", "1.0");
320 let mut builder = PackageBuilder::new(meta);
321 builder.add_file("/usr/bin/test", b"test data", 0o755);
322
323 let archive = builder.build();
324 assert_eq!(&archive[0..4], &VPKG_MAGIC);
326 assert_eq!(archive[4], VPKG_VERSION);
327 }
328
329 #[test]
330 fn test_package_builder_installed_size() {
331 let meta = PackageMetadata::new("test", "1.0");
332 let mut builder = PackageBuilder::new(meta);
333 builder.add_file("/a", b"hello", 0o644);
334 builder.add_file("/b", b"world!", 0o644);
335 assert_eq!(builder.metadata.installed_size, 11); }
337
338 #[test]
339 fn test_package_signature_new() {
340 let sig = PackageSignature::new("maintainer@veridian.org");
341 assert_eq!(sig.signer_id, "maintainer@veridian.org");
342 assert_eq!(sig.signature, [0u8; 64]);
343 }
344
345 #[test]
346 fn test_package_signature_sign_verify() {
347 let mut sig = PackageSignature::new("test");
348 let data = b"package data";
349 let key = [0u8; 32];
350
351 sig.sign(data, &key);
352 assert!(sig.verify(data, &key));
353 }
354
355 #[test]
356 fn test_package_signature_verify_unsigned() {
357 let sig = PackageSignature::new("test");
358 let data = b"package data";
359 let key = [0u8; 32];
360 assert!(!sig.verify(data, &key));
361 }
362
363 #[test]
364 fn test_vpkg_magic() {
365 assert_eq!(VPKG_MAGIC, [b'V', b'P', b'K', b'G']);
366 }
367}