diff --git a/Cargo.toml b/Cargo.toml index dca082388575f5dab98a4e6957e51838c57faf75..70ffa02a52e84e1d374791c60756c8dda92e813f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ required-features = ["clap", "rand", "std"] [dependencies] plain = "0.2.3" +pkgar-keys = { path = "../pkgar-keys" } sodiumoxide = { version = "0.2.5", default_features = false } [dependencies.clap] diff --git a/README.md b/README.md index 1148a43d1e496e70dfb2baef05dd01476f1aeeab..3d2131a70e297f6624f50804f1902af0a08cae66 100644 --- a/README.md +++ b/README.md @@ -66,3 +66,10 @@ interest. If desired, they can check if a locally cached file matches the referenced blake3. If this is not the case, they may access the data portion and verify that the data at the offset and length in the header entry matches the blake3. In that case, the data may be retrieved. + +## Development +To run the integration tests, you'll need to have pkgar-keys in your $PATH (or the +$PATH of the test script). Clone the repo from +[https://gitlab.redox-os.org/MggMuggins/pkgar-keys]() and run `cargo install --path .`. +Use `test.sh` to run the integration tests. + diff --git a/src/bin.rs b/src/bin.rs index cdfb582cf1c83eb95d2455d611e6df6689c83f11..fe08d70fb91b9579f8e9d70783ac6b4b153e6c86 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -6,9 +6,10 @@ use std::os::unix::fs::{symlink, OpenOptionsExt, PermissionsExt}; use std::path::{Component, Path}; use blake3::Hash; -use sodiumoxide::crypto::sign::{self, PublicKey, SecretKey}; +use sodiumoxide::crypto::sign; use crate::{Entry, Error, Header, Package, PackageSrc}; +use crate::keys::{self, PublicKeyFile}; // This ensures that all platforms use the same mode defines const MODE_PERM: u32 = 0o7777; @@ -20,15 +21,13 @@ fn copy_hash<R: Read, W: Write>(mut read: R, mut write: W, buf: &mut [u8]) -> Re let mut hasher = blake3::Hasher::new(); let mut total = 0; loop { - let count = read.read(buf) - .map_err(Error::Io)?; + let count = read.read(buf)?; if count == 0 { break; } total += count as u64; //TODO: Progress - write.write_all(&buf[..count]) - .map_err(Error::Io)?; + write.write_all(&buf[..count])?; hasher.update_with_join::<blake3::join::RayonJoin>(&buf[..count]); } Ok((total, hasher.finalize())) @@ -96,17 +95,9 @@ fn folder_entries<P, Q>(base: P, path: Q, entries: &mut Vec<Entry>) -> io::Resul } pub fn create(secret_path: &str, archive_path: &str, folder: &str) -> Result<(), Error> { - let secret_key = { - let mut data = [0; 64]; - fs::OpenOptions::new() - .read(true) - .open(secret_path) - .map_err(Error::Io)? - .read_exact(&mut data) - .map_err(Error::Io)?; - SecretKey::from_slice(&data) - .ok_or(Error::InvalidKey)? - }; + let secret_key = keys::get_skey(&secret_path.as_ref())? + .key() + .expect(&format!("{} was encrypted?", secret_path)); //TODO: move functions to library @@ -114,13 +105,11 @@ pub fn create(secret_path: &str, archive_path: &str, folder: &str) -> Result<(), .write(true) .create(true) .truncate(true) - .open(archive_path) - .map_err(Error::Io)?; + .open(archive_path)?; // Create a list of entries let mut entries = Vec::new(); - folder_entries(folder, folder, &mut entries) - .map_err(Error::Io)?; + folder_entries(folder, folder, &mut entries)?; // Create initial header let mut header = Header { @@ -142,8 +131,7 @@ pub fn create(secret_path: &str, archive_path: &str, folder: &str) -> Result<(), // Seek to data offset let data_offset = header.total_size()?; - archive_file.seek(SeekFrom::Start(data_offset as u64)) - .map_err(Error::Io)?; + archive_file.seek(SeekFrom::Start(data_offset as u64))?; //TODO: fallocate data_offset + data_size // Stream each file, writing data and calculating b3sums @@ -158,13 +146,11 @@ pub fn create(secret_path: &str, archive_path: &str, folder: &str) -> Result<(), MODE_FILE => { let mut entry_file = fs::OpenOptions::new() .read(true) - .open(path) - .map_err(Error::Io)?; + .open(path)?; copy_hash(&mut entry_file, &mut archive_file, &mut buf)? }, MODE_SYMLINK => { - let destination = fs::read_link(path) - .map_err(Error::Io)?; + let destination = fs::read_link(path)?; let mut data = destination.as_os_str().as_bytes(); copy_hash(&mut data, &mut archive_file, &mut buf)? }, @@ -194,39 +180,27 @@ pub fn create(secret_path: &str, archive_path: &str, folder: &str) -> Result<(), header.signature = sign::sign_detached(unsafe { &plain::as_bytes(&header)[64..] }, &secret_key).0; // Write archive header - archive_file.seek(SeekFrom::Start(0)) - .map_err(Error::Io)?; + archive_file.seek(SeekFrom::Start(0))?; archive_file.write_all(unsafe { plain::as_bytes(&header) - }).map_err(Error::Io)?; + })?; // Write each entry header for entry in &entries { archive_file.write_all(unsafe { plain::as_bytes(entry) - }).map_err(Error::Io)?; + })?; } Ok(()) } pub fn extract(public_path: &str, archive_path: &str, folder: &str) -> Result<(), Error> { - let public_key = { - let mut data = [0; 32]; - fs::OpenOptions::new() - .read(true) - .open(public_path) - .map_err(Error::Io)? - .read_exact(&mut data) - .map_err(Error::Io)?; - PublicKey::from_slice(&data) - .ok_or(Error::InvalidKey)? - }; + let public_key = PublicKeyFile::open(&public_path.as_ref())?.pkey; let mut archive_file = fs::OpenOptions::new() .read(true) - .open(archive_path) - .map_err(Error::Io)?; + .open(archive_path)?; let mut package = Package::new( PackageSrc::File(&mut archive_file), @@ -269,8 +243,7 @@ pub fn extract(public_path: &str, archive_path: &str, folder: &str) -> Result<() format!(".pkgar.{}", entry_hash.to_hex()) }; let temp_path = if let Some(parent) = entry_path.parent() { - fs::create_dir_all(parent) - .map_err(Error::Io)?; + fs::create_dir_all(parent)?; parent.join(temp_name) } else { return Err(Error::Io(io::Error::new( @@ -290,16 +263,14 @@ pub fn extract(public_path: &str, archive_path: &str, folder: &str) -> Result<() .create(true) .truncate(true) .mode(mode_perm) - .open(&temp_path) - .map_err(Error::Io)?; + .open(&temp_path)?; entry.copy_hash(&mut package, &mut temp_file, &mut buf)? }, MODE_SYMLINK => { let mut data = Vec::new(); let (total, hash) = entry.copy_hash(&mut package, &mut data, &mut buf)?; let os_str: &OsStr = OsStrExt::from_bytes(data.as_slice()); - symlink(os_str, &temp_path) - .map_err(Error::Io)?; + symlink(os_str, &temp_path)?; (total, hash) }, _ => { @@ -324,57 +295,18 @@ pub fn extract(public_path: &str, archive_path: &str, folder: &str) -> Result<() } for (temp_path, entry_path) in renames { - fs::rename(&temp_path, &entry_path) - .map_err(Error::Io)?; + fs::rename(&temp_path, &entry_path)?; } Ok(()) } -#[cfg(feature = "rand")] -pub fn keygen(secret_path: &str, public_path: &str) -> Result<(), Error> { - let (public_key, secret_key) = sign::gen_keypair(); - - fs::OpenOptions::new() - .write(true) - .create(true) - .truncate(true) - .mode(0o400) - .open(secret_path) - .map_err(Error::Io)? - .write_all(secret_key.as_ref()) - .map_err(Error::Io)?; - - fs::OpenOptions::new() - .write(true) - .create(true) - .truncate(true) - .mode(0o400) - .open(public_path) - .map_err(Error::Io)? - .write_all(public_key.as_ref()) - .map_err(Error::Io)?; - - Ok(()) -} - pub fn list(public_path: &str, archive_path: &str) -> Result<(), Error> { - let public_key = { - let mut data = [0; 32]; - fs::OpenOptions::new() - .read(true) - .open(public_path) - .map_err(Error::Io)? - .read_exact(&mut data) - .map_err(Error::Io)?; - PublicKey::from_slice(&data) - .ok_or(Error::InvalidKey)? - }; + let public_key = PublicKeyFile::open(&public_path.as_ref())?.pkey; let mut archive_file = fs::OpenOptions::new() .read(true) - .open(archive_path) - .map_err(Error::Io)?; + .open(archive_path)?; // Read header first let mut package = Package::new( @@ -389,3 +321,4 @@ pub fn list(public_path: &str, archive_path: &str) -> Result<(), Error> { Ok(()) } + diff --git a/src/bin/pkgar.rs b/src/bin/pkgar.rs index 0ea4ab23e65792a0b149fa954681b1350a8f2b34..48e3a8dceb8f80c2bd8c3954791c279bf4c58981 100644 --- a/src/bin/pkgar.rs +++ b/src/bin/pkgar.rs @@ -2,7 +2,6 @@ use clap::{App, AppSettings, Arg, SubCommand}; use pkgar::bin::{ create, extract, - keygen, list, }; use std::process; @@ -54,23 +53,6 @@ fn main() { .default_value(".") ) ) - .subcommand(SubCommand::with_name("keygen") - .about("Generate keys") - .arg(Arg::with_name("secret") - .help("Secret key") - .short("s") - .long("secret") - .required(true) - .takes_value(true) - ) - .arg(Arg::with_name("public") - .help("Public key") - .short("p") - .long("public") - .required(true) - .takes_value(true) - ) - ) .subcommand(SubCommand::with_name("list") .about("List archive") .arg(Arg::with_name("public") @@ -102,11 +84,6 @@ fn main() { matches.value_of("file").unwrap(), matches.value_of("folder").unwrap() ) - } else if let Some(matches) = matches.subcommand_matches("keygen") { - keygen( - matches.value_of("secret").unwrap(), - matches.value_of("public").unwrap(), - ) } else if let Some(matches) = matches.subcommand_matches("list") { list( matches.value_of("public").unwrap(), diff --git a/src/error.rs b/src/error.rs index a2d78d2d1b3629fdb9cac8bbf20591727f586397..60dc8e0adb9f15d4dad118530a7ca9626e6c9b85 100644 --- a/src/error.rs +++ b/src/error.rs @@ -6,9 +6,38 @@ pub enum Error { InvalidSignature, #[cfg(feature = "std")] Io(std::io::Error), + #[cfg(feature = "std")] + Keys(pkgar_keys::Error), Plain(plain::Error), Overflow, TryFromInt(core::num::TryFromIntError), - #[cfg(feature = "rand")] - Rand(rand_core::Error), + //#[cfg(feature = "rand")] + //Rand(rand_core::Error), +} + +#[cfg(feature = "std")] +impl From<std::io::Error> for Error { + fn from(err: std::io::Error) -> Error { + Error::Io(err) + } +} + +#[cfg(feature = "std")] +impl From<pkgar_keys::Error> for Error { + fn from(err: pkgar_keys::Error) -> Error { + Error::Keys(err) + } +} + +impl From<plain::Error> for Error { + fn from(err: plain::Error) -> Error { + Error::Plain(err) + } +} + +impl From<core::num::TryFromIntError> for Error { + fn from(err: core::num::TryFromIntError) -> Error { + Error::TryFromInt(err) + } } + diff --git a/src/lib.rs b/src/lib.rs index 8b5160f72241a4475c1c1dca70807fc1f61f0de3..23c6fdaf6ee1bb6871682dbf77e02778829c81e9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,6 +5,8 @@ pub use crate::error::Error; pub use crate::header::Header; pub use crate::package::{Package, PackageSrc}; +pub(crate) use pkgar_keys as keys; + mod entry; mod error; mod header; diff --git a/test.sh b/test.sh index b4f41dc11206db926fd58eb738999d11d8559431..b18c721a2c8f69decb5ce67106edec4e260d79c0 100755 --- a/test.sh +++ b/test.sh @@ -16,20 +16,19 @@ else cargo build --release fi -time target/$build/pkgar \ - keygen \ - --secret target/test/secret.key \ - --public target/test/public.key +time pkgar-keys gen \ + --keyfile target/test/secret.toml \ + --pubkeyfile target/test/public.toml time target/$build/pkgar \ create \ - --secret target/test/secret.key \ + --secret target/test/secret.toml \ --file target/test/src.pkg \ src time target/$build/pkgar \ extract \ - --public target/test/public.key \ + --public target/test/public.toml \ --file target/test/src.pkg \ target/test/src