Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • redox-os/pkgar
  • Rustywolf/pkgar
  • freewilll/pkgar
  • rw_van/pkgar
  • josh/pkgar
5 results
Show changes
Commits on Source (10)
Showing with 2311 additions and 417 deletions
/target /target
**/*.rs.bk **/*.rs.bk
Cargo.lock
This diff is collapsed.
...@@ -5,7 +5,3 @@ members = [ ...@@ -5,7 +5,3 @@ members = [
"pkgar-keys", "pkgar-keys",
"pkgar-repo", "pkgar-repo",
] ]
[patch.crates-io]
ring = { git = "https://gitlab.redox-os.org/redox-os/ring.git", branch = "redox-0.16.20" }
tokio = { git = "https://gitlab.redox-os.org/redox-os/tokio.git", branch = "redox-1.17.0" }
[package] [package]
name = "pkgar-core" name = "pkgar-core"
version = "0.1.11" version = "0.1.15"
description = "Core Data Types for the Redox Package Archive" description = "Core Data Types for the Redox Package Archive"
license = "MIT" license = "MIT"
authors = ["Jeremy Soller <jackpot51@gmail.com>", "Wesley Hershberger <mggmugginsmc@gmail.com>"] authors = ["Jeremy Soller <jackpot51@gmail.com>", "Wesley Hershberger <mggmugginsmc@gmail.com>"]
...@@ -9,6 +9,6 @@ edition = "2018" ...@@ -9,6 +9,6 @@ edition = "2018"
[dependencies] [dependencies]
bitflags = "1.2.1" bitflags = "1.2.1"
blake3 = { version = "0.3.6", default_features = false, features = ["rayon"] } blake3 = { version = "0.3.6", default-features = false, features = ["rayon"] }
dryoc = "0.6"
plain = "0.2.3" plain = "0.2.3"
sodiumoxide = { version = "0.2.7", default_features = false }
...@@ -23,20 +23,19 @@ impl Entry { ...@@ -23,20 +23,19 @@ impl Entry {
pub fn blake3(&self) -> Hash { pub fn blake3(&self) -> Hash {
Hash::from(self.blake3) Hash::from(self.blake3)
} }
pub fn offset(&self) -> u64 { pub fn offset(&self) -> u64 {
self.offset self.offset
} }
pub fn size(&self) -> u64 { pub fn size(&self) -> u64 {
self.size self.size
} }
pub fn mode(&self) -> Result<Mode, Error> { pub fn mode(&self) -> Result<Mode, Error> {
Mode::from_bits(self.mode) Mode::from_bits(self.mode).ok_or(Error::InvalidMode(self.mode))
.ok_or(Error::InvalidMode(self.mode))
} }
/// Retrieve the path, ending at the first NUL /// Retrieve the path, ending at the first NUL
pub fn path_bytes(&self) -> &[u8] { pub fn path_bytes(&self) -> &[u8] {
let mut i = 0; let mut i = 0;
...@@ -51,4 +50,3 @@ impl Entry { ...@@ -51,4 +50,3 @@ impl Entry {
} }
unsafe impl Plain for Entry {} unsafe impl Plain for Entry {}
...@@ -4,11 +4,11 @@ use core::fmt::{Display, Formatter, Result}; ...@@ -4,11 +4,11 @@ use core::fmt::{Display, Formatter, Result};
#[derive(Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {
Dryoc(dryoc::Error),
InvalidBlake3, InvalidBlake3,
InvalidData, InvalidData,
InvalidKey, InvalidKey,
InvalidMode(u32), InvalidMode(u32),
InvalidSignature,
Plain(plain::Error), Plain(plain::Error),
Overflow, Overflow,
TryFromInt(core::num::TryFromIntError), TryFromInt(core::num::TryFromIntError),
...@@ -17,13 +17,13 @@ pub enum Error { ...@@ -17,13 +17,13 @@ pub enum Error {
impl Display for Error { impl Display for Error {
fn fmt(&self, f: &mut Formatter) -> Result { fn fmt(&self, f: &mut Formatter) -> Result {
use Error::*; use Error::*;
let msg = match self { let msg = match self {
Dryoc(err) => format!("Dryoc: {:?}", err),
InvalidBlake3 => "Invalid Blake3".to_string(), InvalidBlake3 => "Invalid Blake3".to_string(),
InvalidData => "Data Invalid".to_string(), InvalidData => "Data Invalid".to_string(),
InvalidKey => "Key Invalid".to_string(), InvalidKey => "Key Invalid".to_string(),
InvalidMode(mode) => format!("Invalid Mode: {:o}", mode), InvalidMode(mode) => format!("Invalid Mode: {:o}", mode),
InvalidSignature => "Invalid Signature".to_string(),
Plain(err) => format!("Plain: {:?}", err), Plain(err) => format!("Plain: {:?}", err),
Overflow => "Overflow".to_string(), Overflow => "Overflow".to_string(),
TryFromInt(err) => format!("TryFromInt: {}", err), TryFromInt(err) => format!("TryFromInt: {}", err),
...@@ -32,6 +32,12 @@ impl Display for Error { ...@@ -32,6 +32,12 @@ impl Display for Error {
} }
} }
impl From<dryoc::Error> for Error {
fn from(err: dryoc::Error) -> Error {
Error::Dryoc(err)
}
}
impl From<plain::Error> for Error { impl From<plain::Error> for Error {
fn from(err: plain::Error) -> Error { fn from(err: plain::Error) -> Error {
Error::Plain(err) Error::Plain(err)
...@@ -43,4 +49,3 @@ impl From<core::num::TryFromIntError> for Error { ...@@ -43,4 +49,3 @@ impl From<core::num::TryFromIntError> for Error {
Error::TryFromInt(err) Error::TryFromInt(err)
} }
} }
//! The packed structs represent the on-disk format of pkgar //! The packed structs represent the on-disk format of pkgar
use alloc::vec;
use core::convert::TryFrom; use core::convert::TryFrom;
use core::mem; use core::mem;
use plain::Plain; use plain::Plain;
use sodiumoxide::crypto::sign::{self, PublicKey};
use crate::{Entry, Error}; use crate::{dryoc::classic::crypto_sign::crypto_sign_open, Entry, Error, PublicKey};
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
#[repr(packed)] #[repr(packed)]
...@@ -26,11 +26,13 @@ impl Header { ...@@ -26,11 +26,13 @@ impl Header {
/// Parse header from raw header data and verify using public key /// Parse header from raw header data and verify using public key
pub fn new<'a>(data: &'a [u8], public_key: &PublicKey) -> Result<&'a Header, Error> { pub fn new<'a>(data: &'a [u8], public_key: &PublicKey) -> Result<&'a Header, Error> {
// Retrieve signed header data // Retrieve signed header data
let signed = data.get(..mem::size_of::<Header>()) let signed = data
.get(..mem::size_of::<Header>())
.ok_or(Error::Plain(plain::Error::TooShort))?; .ok_or(Error::Plain(plain::Error::TooShort))?;
let verified = sign::verify(signed, public_key) // Verify signature
.map_err(|_err| Error::InvalidSignature)?; let mut verified = vec![0; signed.len() - 64];
crypto_sign_open(&mut verified, signed, public_key)?;
// Check that verified data matches signed data after skipping the signature // Check that verified data matches signed data after skipping the signature
if verified.as_slice() != &signed[64..] { if verified.as_slice() != &signed[64..] {
...@@ -58,9 +60,7 @@ impl Header { ...@@ -58,9 +60,7 @@ impl Header {
/// Retrieve the size of the entries /// Retrieve the size of the entries
pub fn entries_size(&self) -> Result<u64, Error> { pub fn entries_size(&self) -> Result<u64, Error> {
let entry_size = u64::try_from(mem::size_of::<Entry>())?; let entry_size = u64::try_from(mem::size_of::<Entry>())?;
self.count self.count.checked_mul(entry_size).ok_or(Error::Overflow)
.checked_mul(entry_size)
.ok_or(Error::Overflow)
} }
/// Retrieve the size of the Header and its entries /// Retrieve the size of the Header and its entries
...@@ -75,7 +75,8 @@ impl Header { ...@@ -75,7 +75,8 @@ impl Header {
pub fn entries<'a>(&self, data: &'a [u8]) -> Result<&'a [Entry], Error> { pub fn entries<'a>(&self, data: &'a [u8]) -> Result<&'a [Entry], Error> {
let entries_size = usize::try_from(self.entries_size()?)?; let entries_size = usize::try_from(self.entries_size()?)?;
let entries_data = data.get(..entries_size) let entries_data = data
.get(..entries_size)
.ok_or(Error::Plain(plain::Error::TooShort))?; .ok_or(Error::Plain(plain::Error::TooShort))?;
let hash = { let hash = {
...@@ -84,7 +85,6 @@ impl Header { ...@@ -84,7 +85,6 @@ impl Header {
hasher.finalize() hasher.finalize()
}; };
if &self.blake3 != hash.as_bytes() { if &self.blake3 != hash.as_bytes() {
return Err(Error::InvalidBlake3); return Err(Error::InvalidBlake3);
} }
...@@ -108,4 +108,3 @@ impl fmt::Debug for Header { ...@@ -108,4 +108,3 @@ impl fmt::Debug for Header {
) )
} }
}*/ }*/
...@@ -5,6 +5,11 @@ use core::mem; ...@@ -5,6 +5,11 @@ use core::mem;
use bitflags::bitflags; use bitflags::bitflags;
pub use dryoc::{
self,
classic::crypto_sign_ed25519::{PublicKey, SecretKey, Signature},
};
pub use crate::entry::Entry; pub use crate::entry::Entry;
pub use crate::error::Error; pub use crate::error::Error;
pub use crate::header::Header; pub use crate::header::Header;
...@@ -44,7 +49,7 @@ impl Mode { ...@@ -44,7 +49,7 @@ impl Mode {
mod tests { mod tests {
use core::mem; use core::mem;
use crate::{Entry, ENTRY_SIZE, Header, HEADER_SIZE}; use crate::{Entry, Header, ENTRY_SIZE, HEADER_SIZE};
#[test] #[test]
fn header_size() { fn header_size() {
......
...@@ -2,9 +2,9 @@ use alloc::vec; ...@@ -2,9 +2,9 @@ use alloc::vec;
use alloc::vec::Vec; use alloc::vec::Vec;
use core::convert::TryFrom; use core::convert::TryFrom;
use sodiumoxide::crypto::sign::PublicKey; use dryoc::classic::crypto_sign_ed25519::PublicKey;
use crate::{Entry, Error, HEADER_SIZE, Header}; use crate::{Entry, Error, Header, HEADER_SIZE};
pub trait PackageSrc { pub trait PackageSrc {
type Err: From<Error>; type Err: From<Error>;
...@@ -26,10 +26,9 @@ pub trait PackageSrc { ...@@ -26,10 +26,9 @@ pub trait PackageSrc {
fn read_entries(&mut self) -> Result<Vec<Entry>, Self::Err> { fn read_entries(&mut self) -> Result<Vec<Entry>, Self::Err> {
let header = self.header(); let header = self.header();
let entries_size = header.entries_size() let entries_size = header
.and_then(|rslt| usize::try_from(rslt) .entries_size()
.map_err(Error::TryFromInt) .and_then(|rslt| usize::try_from(rslt).map_err(Error::TryFromInt))?;
)?;
let mut entries_data = vec![0; entries_size]; let mut entries_data = vec![0; entries_size];
self.read_at(HEADER_SIZE as u64, &mut entries_data)?; self.read_at(HEADER_SIZE as u64, &mut entries_data)?;
let entries = header.entries(&entries_data)?; let entries = header.entries(&entries_data)?;
...@@ -37,22 +36,24 @@ pub trait PackageSrc { ...@@ -37,22 +36,24 @@ pub trait PackageSrc {
} }
/// Read from this src at a given entry's data with a given offset within that entry /// Read from this src at a given entry's data with a given offset within that entry
fn read_entry(&mut self, entry: Entry, offset: usize, buf: &mut [u8]) -> Result<usize, Self::Err> { fn read_entry(
&mut self,
entry: Entry,
offset: usize,
buf: &mut [u8],
) -> Result<usize, Self::Err> {
if offset as u64 > entry.size { if offset as u64 > entry.size {
return Ok(0); return Ok(0);
} }
let mut end = usize::try_from(entry.size - offset as u64) let mut end = usize::try_from(entry.size - offset as u64).map_err(Error::TryFromInt)?;
.map_err(Error::TryFromInt)?;
if end > buf.len() { if end > buf.len() {
end = buf.len(); end = buf.len();
} }
let offset = let offset =
HEADER_SIZE as u64 + HEADER_SIZE as u64 + self.header().entries_size()? + entry.offset + offset as u64;
self.header().entries_size()? +
entry.offset + offset as u64;
self.read_at(offset as u64, &mut buf[..end]) self.read_at(offset as u64, &mut buf[..end])
} }
...@@ -84,14 +85,12 @@ impl PackageSrc for PackageBuf<'_> { ...@@ -84,14 +85,12 @@ impl PackageSrc for PackageBuf<'_> {
} }
fn read_at(&mut self, offset: u64, buf: &mut [u8]) -> Result<usize, Error> { fn read_at(&mut self, offset: u64, buf: &mut [u8]) -> Result<usize, Error> {
let start = usize::try_from(offset) let start = usize::try_from(offset).map_err(Error::TryFromInt)?;
.map_err(Error::TryFromInt)?;
let len = self.src.len(); let len = self.src.len();
if start >= len { if start >= len {
return Ok(0); return Ok(0);
} }
let mut end = start.checked_add(buf.len()) let mut end = start.checked_add(buf.len()).ok_or(Error::Overflow)?;
.ok_or(Error::Overflow)?;
if end > len { if end > len {
end = len; end = len;
} }
......
[package] [package]
name = "pkgar-keys" name = "pkgar-keys"
version = "0.1.11" version = "0.1.15"
description = "Key management tool/library for pkgar" description = "Key management tool/library for pkgar"
license = "MIT" license = "MIT"
authors = ["Wesley Hershberger <mggmugginsmc@gmail.com>"] authors = ["Wesley Hershberger <mggmugginsmc@gmail.com>"]
...@@ -14,10 +14,10 @@ dirs = "3.0.1" ...@@ -14,10 +14,10 @@ dirs = "3.0.1"
error-chain = "0.12" error-chain = "0.12"
hex = { version = "0.4.2", features = ["serde"] } hex = { version = "0.4.2", features = ["serde"] }
lazy_static = "1.4.0" lazy_static = "1.4.0"
pkgar-core = { version = "0.1.15", path = "../pkgar-core" }
seckey = "0.11.2" seckey = "0.11.2"
serde = { version = "1.0.115", default_features = false, features = ["derive"] } serde = { version = "=1.0.197", default-features = false, features = ["derive"] }
sodiumoxide = { version = "0.2.7", default_features = false } termion = "4"
termion = "1.5.5"
#thiserror = "1.0.20" #thiserror = "1.0.20"
toml = "0.5.6" toml = "0.8"
user-error = "1.2.8" user-error = "1.2.8"
...@@ -9,39 +9,43 @@ error_chain! { ...@@ -9,39 +9,43 @@ error_chain! {
types { types {
Error, ErrorKind, ResultExt; Error, ErrorKind, ResultExt;
} }
foreign_links { foreign_links {
Io(io::Error); Io(io::Error);
Ser(toml::ser::Error); Ser(toml::ser::Error);
Deser(toml::de::Error); Deser(toml::de::Error);
} }
errors { errors {
Core(src: pkgar_core::Error) {
display("{}", src),
}
KeyInvalid { KeyInvalid {
description("Key length invalid"), description("Key length invalid"),
} }
KeyMismatch { KeyMismatch {
description("Public and secret keys do not match"), description("Public and secret keys do not match"),
} }
NonceInvalid { NonceInvalid {
description("Invalid nonce length"), description("Invalid nonce length"),
} }
PassphraseIncorrect { PassphraseIncorrect {
description("Incorrect passphrase"), description("Incorrect passphrase"),
} }
PassphraseMismatch { PassphraseMismatch {
description("Passphrases did not match"), description("Passphrases did not match"),
} }
Path(path: PathBuf) { Path(path: PathBuf) {
display("{}: ", path.display()), display("{}: ", path.display()),
} }
} }
skip_msg_variant skip_msg_variant
} }
...@@ -60,3 +64,10 @@ impl From<&PathBuf> for ErrorKind { ...@@ -60,3 +64,10 @@ impl From<&PathBuf> for ErrorKind {
} }
} }
// Unfortunately error_chain does not handle types that don't implement
// std::error::Error very well.
impl From<pkgar_core::Error> for Error {
fn from(err: pkgar_core::Error) -> Error {
Error::from_kind(ErrorKind::Core(err))
}
}
...@@ -9,23 +9,32 @@ use std::path::{Path, PathBuf}; ...@@ -9,23 +9,32 @@ use std::path::{Path, PathBuf};
use error_chain::bail; use error_chain::bail;
use hex::FromHex; use hex::FromHex;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use pkgar_core::{
dryoc::{
classic::{
crypto_pwhash::{crypto_pwhash, PasswordHashAlgorithm},
crypto_secretbox::{crypto_secretbox_easy, crypto_secretbox_open_easy, Key, Nonce},
crypto_sign::{crypto_sign_keypair, crypto_sign_seed_keypair},
},
constants::{CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE, CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE},
types::NewByteArray,
},
PublicKey, SecretKey,
};
use seckey::SecBytes; use seckey::SecBytes;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sodiumoxide::crypto::{
pwhash,
secretbox,
sign,
};
use termion::input::TermRead; use termion::input::TermRead;
pub use crate::error::{ErrorKind, Error, ResultExt}; type Salt = [u8; 32];
pub use crate::error::{Error, ErrorKind, ResultExt};
lazy_static! { lazy_static! {
static ref HOMEDIR: PathBuf = { static ref HOMEDIR: PathBuf = {
dirs::home_dir() dirs::home_dir()
.unwrap_or("./".into()) .unwrap_or("./".into())
}; };
/// The default location for pkgar to look for the user's public key. /// The default location for pkgar to look for the user's public key.
/// ///
/// Defaults to `$HOME/.pkgar/keys/id_ed25519.pub.toml`. If `$HOME` is /// Defaults to `$HOME/.pkgar/keys/id_ed25519.pub.toml`. If `$HOME` is
...@@ -33,7 +42,7 @@ lazy_static! { ...@@ -33,7 +42,7 @@ lazy_static! {
pub static ref DEFAULT_PUBKEY: PathBuf = { pub static ref DEFAULT_PUBKEY: PathBuf = {
Path::join(&HOMEDIR, ".pkgar/keys/id_ed25519.pub.toml") Path::join(&HOMEDIR, ".pkgar/keys/id_ed25519.pub.toml")
}; };
/// The default location for pkgar to look for the user's secret key. /// The default location for pkgar to look for the user's secret key.
/// ///
/// Defaults to `$HOME/.pkgar/keys/id_ed25519.toml`. If `$HOME` is unset, /// Defaults to `$HOME/.pkgar/keys/id_ed25519.toml`. If `$HOME` is unset,
...@@ -45,32 +54,26 @@ lazy_static! { ...@@ -45,32 +54,26 @@ lazy_static! {
mod ser { mod ser {
use hex::FromHex; use hex::FromHex;
use serde::{Deserialize, Deserializer};
use serde::de::Error; use serde::de::Error;
use sodiumoxide::crypto::{pwhash, secretbox, sign}; use serde::{Deserialize, Deserializer};
use crate::{Nonce, PublicKey, Salt};
//TODO: Macro? //TODO: Macro?
pub(crate) fn to_salt<'d, D: Deserializer<'d>>(deser: D) -> Result<pwhash::Salt, D::Error> { pub(crate) fn to_salt<'d, D: Deserializer<'d>>(deser: D) -> Result<Salt, D::Error> {
String::deserialize(deser) String::deserialize(deser)
.and_then(|s| <[u8; 32]>::from_hex(s) .and_then(|s| <[u8; 32]>::from_hex(s).map_err(|err| Error::custom(err.to_string())))
.map(|val| pwhash::Salt(val) )
.map_err(|err| Error::custom(err.to_string()) ) )
} }
pub(crate) fn to_nonce<'d, D: Deserializer<'d>>(deser: D) -> Result<secretbox::Nonce, D::Error> { pub(crate) fn to_nonce<'d, D: Deserializer<'d>>(deser: D) -> Result<Nonce, D::Error> {
String::deserialize(deser) String::deserialize(deser)
.and_then(|s| <[u8; 24]>::from_hex(s) .and_then(|s| <[u8; 24]>::from_hex(s).map_err(|err| Error::custom(err.to_string())))
.map(|val| secretbox::Nonce(val) )
.map_err(|err| Error::custom(err.to_string()) ) )
} }
pub(crate) fn to_pubkey<'d, D: Deserializer<'d>>(deser: D) -> Result<sign::PublicKey, D::Error> { pub(crate) fn to_pubkey<'d, D: Deserializer<'d>>(deser: D) -> Result<PublicKey, D::Error> {
String::deserialize(deser) String::deserialize(deser)
.and_then(|s| <[u8; 32]>::from_hex(s) .and_then(|s| <[u8; 32]>::from_hex(s).map_err(|err| Error::custom(err.to_string())))
.map(|val| sign::PublicKey(val) )
.map_err(|err| Error::custom(err.to_string()) ) )
} }
} }
/// Standard pkgar public key format definition. Use serde to serialize/deserialize /// Standard pkgar public key format definition. Use serde to serialize/deserialize
...@@ -78,73 +81,65 @@ mod ser { ...@@ -78,73 +81,65 @@ mod ser {
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize)]
pub struct PublicKeyFile { pub struct PublicKeyFile {
#[serde(serialize_with = "hex::serialize", deserialize_with = "ser::to_pubkey")] #[serde(serialize_with = "hex::serialize", deserialize_with = "ser::to_pubkey")]
pub pkey: sign::PublicKey, pub pkey: PublicKey,
} }
impl PublicKeyFile { impl PublicKeyFile {
/// Parse a `PublicKeyFile` from `file` (in toml format). /// Parse a `PublicKeyFile` from `file` (in toml format).
pub fn open(file: impl AsRef<Path>) -> Result<PublicKeyFile, Error> { pub fn open(file: impl AsRef<Path>) -> Result<PublicKeyFile, Error> {
let content = fs::read_to_string(&file) let content = fs::read_to_string(&file).chain_err(|| file.as_ref())?;
.chain_err(|| file.as_ref() )?;
toml::from_str(&content).chain_err(|| file.as_ref())
toml::from_str(&content)
.chain_err(|| file.as_ref() )
} }
/// Write `self` serialized as toml to `w`. /// Write `self` serialized as toml to `w`.
pub fn write(&self, mut w: impl Write) -> Result<(), Error> { pub fn write(&self, mut w: impl Write) -> Result<(), Error> {
w.write_all(toml::to_string(self)?.as_bytes())?; w.write_all(toml::to_string(self)?.as_bytes())?;
Ok(()) Ok(())
} }
/// Shortcut to write the public key to `file` /// Shortcut to write the public key to `file`
pub fn save(&self, file: impl AsRef<Path>) -> Result<(), Error> { pub fn save(&self, file: impl AsRef<Path>) -> Result<(), Error> {
self.write( self.write(File::create(&file).chain_err(|| file.as_ref())?)
File::create(&file) .chain_err(|| file.as_ref())
.chain_err(|| file.as_ref() )?
).chain_err(|| file.as_ref() )
} }
} }
enum SKey { enum SKey {
Cipher([u8; 80]), Cipher([u8; 80]),
Plain(sign::SecretKey), Plain(SecretKey),
} }
impl SKey { impl SKey {
fn encrypt(&mut self, passwd: Passwd, salt: pwhash::Salt, nonce: secretbox::Nonce) { fn encrypt(&mut self, passwd: Passwd, salt: Salt, nonce: Nonce) -> Result<(), Error> {
if let SKey::Plain(skey) = self { if let SKey::Plain(skey) = self {
if let Some(passwd_key) = passwd.gen_key(salt) { if let Some(passwd_key) = passwd.gen_key(salt) {
let mut buf = [0; 80]; let mut buf = [0; 80];
buf.copy_from_slice(&secretbox::seal(skey.as_ref(), &nonce, &passwd_key)); crypto_secretbox_easy(&mut buf, skey.as_ref(), &nonce, &passwd_key)
.map_err(pkgar_core::Error::Dryoc)?;
*self = SKey::Cipher(buf); *self = SKey::Cipher(buf);
} }
} }
Ok(())
} }
fn decrypt(&mut self, passwd: Passwd, salt: pwhash::Salt, nonce: secretbox::Nonce) -> Result<(), Error> { fn decrypt(&mut self, passwd: Passwd, salt: Salt, nonce: Nonce) -> Result<(), Error> {
if let SKey::Cipher(ciphertext) = self { if let SKey::Cipher(ciphertext) = self {
let mut buf = [0; 64];
if let Some(passwd_key) = passwd.gen_key(salt) { if let Some(passwd_key) = passwd.gen_key(salt) {
let skey_plain = secretbox::open(ciphertext.as_ref(), &nonce, &passwd_key) crypto_secretbox_open_easy(&mut buf, ciphertext.as_ref(), &nonce, &passwd_key)
.map_err(|_| ErrorKind::PassphraseIncorrect )?; .map_err(pkgar_core::Error::Dryoc)?;
*self = SKey::Plain(sign::SecretKey::from_slice(&skey_plain)
.ok_or(ErrorKind::KeyInvalid)?);
} else { } else {
*self = SKey::Plain(sign::SecretKey::from_slice(&ciphertext[..64]) let skey_plain = &ciphertext[..64];
.ok_or(ErrorKind::KeyInvalid)?); if skey_plain.len() != buf.len() {
return Err(ErrorKind::KeyInvalid.into());
}
buf.copy_from_slice(&skey_plain);
} }
*self = SKey::Plain(buf);
} }
Ok(()) Ok(())
} }
/// Returns `None` if encrypted
fn skey(&self) -> Option<sign::SecretKey> {
match &self {
SKey::Plain(skey) => Some(skey.clone()),
SKey::Cipher(_) => None,
}
}
} }
impl AsRef<[u8]> for SKey { impl AsRef<[u8]> for SKey {
...@@ -158,14 +153,15 @@ impl AsRef<[u8]> for SKey { ...@@ -158,14 +153,15 @@ impl AsRef<[u8]> for SKey {
impl FromHex for SKey { impl FromHex for SKey {
type Error = hex::FromHexError; type Error = hex::FromHexError;
fn from_hex<T: AsRef<[u8]>>(buf: T) -> Result<SKey, hex::FromHexError> { fn from_hex<T: AsRef<[u8]>>(buf: T) -> Result<SKey, hex::FromHexError> {
let bytes = hex::decode(buf)?; let bytes = hex::decode(buf)?;
// Public key is only 64 bytes... // Public key is only 64 bytes...
if bytes.len() == 64 { if bytes.len() == 64 {
Ok(SKey::Plain(sign::SecretKey::from_slice(&bytes) let mut buf = [0; 64];
.expect("Somehow not the right number of bytes"))) buf.copy_from_slice(&bytes);
Ok(SKey::Plain(buf))
} else { } else {
let mut buf = [0; 80]; let mut buf = [0; 80];
buf.copy_from_slice(&bytes); buf.copy_from_slice(&bytes);
...@@ -180,9 +176,9 @@ impl FromHex for SKey { ...@@ -180,9 +176,9 @@ impl FromHex for SKey {
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize)]
pub struct SecretKeyFile { pub struct SecretKeyFile {
#[serde(serialize_with = "hex::serialize", deserialize_with = "ser::to_salt")] #[serde(serialize_with = "hex::serialize", deserialize_with = "ser::to_salt")]
salt: pwhash::Salt, salt: Salt,
#[serde(serialize_with = "hex::serialize", deserialize_with = "ser::to_nonce")] #[serde(serialize_with = "hex::serialize", deserialize_with = "ser::to_nonce")]
nonce: secretbox::Nonce, nonce: Nonce,
#[serde(with = "hex")] #[serde(with = "hex")]
skey: SKey, skey: SKey,
} }
...@@ -191,33 +187,31 @@ impl SecretKeyFile { ...@@ -191,33 +187,31 @@ impl SecretKeyFile {
/// Generate a keypair with all the nessesary info to save both keys. You /// Generate a keypair with all the nessesary info to save both keys. You
/// must call `save()` on each object to persist them to disk. /// must call `save()` on each object to persist them to disk.
pub fn new() -> (PublicKeyFile, SecretKeyFile) { pub fn new() -> (PublicKeyFile, SecretKeyFile) {
let (pkey, skey) = sign::gen_keypair(); let (pkey, skey) = crypto_sign_keypair();
let pkey_file = PublicKeyFile { pkey }; let pkey_file = PublicKeyFile { pkey };
let skey_file = SecretKeyFile { let skey_file = SecretKeyFile {
salt: pwhash::gen_salt(), salt: Salt::gen(),
nonce: secretbox::gen_nonce(), nonce: Nonce::gen(),
skey: SKey::Plain(skey), skey: SKey::Plain(skey),
}; };
(pkey_file, skey_file) (pkey_file, skey_file)
} }
/// Parse a `SecretKeyFile` from `file` (in toml format). /// Parse a `SecretKeyFile` from `file` (in toml format).
pub fn open(file: impl AsRef<Path>) -> Result<SecretKeyFile, Error> { pub fn open(file: impl AsRef<Path>) -> Result<SecretKeyFile, Error> {
let content = fs::read_to_string(&file) let content = fs::read_to_string(&file).chain_err(|| file.as_ref())?;
.chain_err(|| file.as_ref() )?;
toml::from_str(&content).chain_err(|| file.as_ref())
toml::from_str(&content)
.chain_err(|| file.as_ref() )
} }
/// Write `self` serialized as toml to `w`. /// Write `self` serialized as toml to `w`.
pub fn write(&self, mut w: impl Write) -> Result<(), Error> { pub fn write(&self, mut w: impl Write) -> Result<(), Error> {
w.write_all(toml::to_string(&self)?.as_bytes())?; w.write_all(toml::to_string(&self)?.as_bytes())?;
Ok(()) Ok(())
} }
/// Shortcut to write the secret key to `file`. /// Shortcut to write the secret key to `file`.
/// ///
/// Make sure to call `encrypt()` in order to encrypt /// Make sure to call `encrypt()` in order to encrypt
...@@ -229,22 +223,23 @@ impl SecretKeyFile { ...@@ -229,22 +223,23 @@ impl SecretKeyFile {
.create(true) .create(true)
.mode(0o600) .mode(0o600)
.open(&file) .open(&file)
.chain_err(|| file.as_ref() )? .chain_err(|| file.as_ref())?,
).chain_err(|| file.as_ref() ) )
.chain_err(|| file.as_ref())
} }
/// Ensure that the internal state of this struct is encrypted. /// Ensure that the internal state of this struct is encrypted.
/// Note that if passwd is empty, this function is a no-op. /// Note that if passwd is empty, this function is a no-op.
pub fn encrypt(&mut self, passwd: Passwd) { pub fn encrypt(&mut self, passwd: Passwd) -> Result<(), Error> {
self.skey.encrypt(passwd, self.salt, self.nonce) self.skey.encrypt(passwd, self.salt, self.nonce)
} }
/// Ensure that the internal state of this struct is decrypted. /// Ensure that the internal state of this struct is decrypted.
/// If the internal state is already decrypted, this function is a no-op. /// If the internal state is already decrypted, this function is a no-op.
pub fn decrypt(&mut self, passwd: Passwd) -> Result<(), Error> { pub fn decrypt(&mut self, passwd: Passwd) -> Result<(), Error> {
self.skey.decrypt(passwd, self.salt, self.nonce) self.skey.decrypt(passwd, self.salt, self.nonce)
} }
/// Status of the internal state. /// Status of the internal state.
pub fn is_encrypted(&self) -> bool { pub fn is_encrypted(&self) -> bool {
match self.skey { match self.skey {
...@@ -252,19 +247,29 @@ impl SecretKeyFile { ...@@ -252,19 +247,29 @@ impl SecretKeyFile {
SKey::Plain(_) => false, SKey::Plain(_) => false,
} }
} }
/// Returns `None` if the secret key is encrypted. /// Returns `None` if the secret key is encrypted.
pub fn key(&mut self) -> Option<sign::SecretKey> { pub fn secret_key(&self) -> Option<SecretKey> {
match &self.skey { match &self.skey {
SKey::Plain(skey) => Some(skey.clone()), SKey::Plain(skey) => Some(skey.clone()),
SKey::Cipher(_) => None, SKey::Cipher(_) => None,
} }
} }
/// Returns `None` if the secret key is encrypted.
pub fn public_key(&self) -> Option<PublicKey> {
let skey = self.secret_key()?;
let mut seed = [0; 32];
seed.copy_from_slice(&skey[..32]);
let (pkey, new_skey) = crypto_sign_seed_keypair(&seed);
assert_eq!(skey, new_skey);
Some(pkey)
}
/// Returns `None` if the secret key is encrypted. /// Returns `None` if the secret key is encrypted.
pub fn public_key_file(&self) -> Option<PublicKeyFile> { pub fn public_key_file(&self) -> Option<PublicKeyFile> {
Some(PublicKeyFile { Some(PublicKeyFile {
pkey: self.skey.skey()?.public_key(), pkey: self.public_key()?,
}) })
} }
} }
...@@ -278,66 +283,62 @@ impl Passwd { ...@@ -278,66 +283,62 @@ impl Passwd {
/// Create a new `Passwd` and zero the old string. /// Create a new `Passwd` and zero the old string.
pub fn new(passwd: &mut String) -> Passwd { pub fn new(passwd: &mut String) -> Passwd {
let pwd = Passwd { let pwd = Passwd {
bytes :SecBytes::with( bytes: SecBytes::with(passwd.len(), |buf| buf.copy_from_slice(passwd.as_bytes())),
passwd.len(),
|buf| buf.copy_from_slice(passwd.as_bytes())
),
}; };
unsafe { unsafe {
seckey::zero(passwd.as_bytes_mut()); seckey::zero(passwd.as_bytes_mut());
} }
pwd pwd
} }
/// Prompt the user for a `Passwd` on stdin. /// Prompt the user for a `Passwd` on stdin.
pub fn prompt(prompt: impl AsRef<str>) -> Result<Passwd, Error> { pub fn prompt(prompt: impl AsRef<str>) -> Result<Passwd, Error> {
let stdout = stdout(); let stdout = stdout();
let mut stdout = stdout.lock(); let mut stdout = stdout.lock();
let stdin = stdin(); let stdin = stdin();
let mut stdin = stdin.lock(); let mut stdin = stdin.lock();
stdout.write_all(prompt.as_ref().as_bytes())?; stdout.write_all(prompt.as_ref().as_bytes())?;
stdout.flush()?; stdout.flush()?;
let mut passwd = stdin.read_passwd(&mut stdout)? let mut passwd = stdin
.ok_or(ErrorKind::Io( .read_passwd(&mut stdout)?
io::Error::new( .ok_or(ErrorKind::Io(io::Error::new(
io::ErrorKind::UnexpectedEof, io::ErrorKind::UnexpectedEof,
"Invalid Password Input", "Invalid Password Input",
) )))?;
))?;
println!(); println!();
Ok(Passwd::new(&mut passwd)) Ok(Passwd::new(&mut passwd))
} }
/// Prompt for a password on stdin and confirm it. For configurable /// Prompt for a password on stdin and confirm it. For configurable
/// prompts, use [`Passwd::prompt`](struct.Passwd.html#method.prompt). /// prompts, use [`Passwd::prompt`](struct.Passwd.html#method.prompt).
pub fn prompt_new() -> Result<Passwd, Error> { pub fn prompt_new() -> Result<Passwd, Error> {
let passwd = Passwd::prompt( let passwd = Passwd::prompt(
"Please enter a new passphrase (leave empty to store the key in plaintext): " "Please enter a new passphrase (leave empty to store the key in plaintext): ",
)?; )?;
let confirm = Passwd::prompt("Please re-enter the passphrase: ")?; let confirm = Passwd::prompt("Please re-enter the passphrase: ")?;
if passwd != confirm { if passwd != confirm {
bail!(ErrorKind::PassphraseMismatch); bail!(ErrorKind::PassphraseMismatch);
} }
Ok(passwd) Ok(passwd)
} }
/// Get a key for symmetric key encryption from a password. /// Get a key for symmetric key encryption from a password.
fn gen_key(&self, salt: pwhash::Salt) -> Option<secretbox::Key> { fn gen_key(&self, salt: Salt) -> Option<Key> {
if self.bytes.read().len() > 0 { if self.bytes.read().len() > 0 {
let mut key = secretbox::Key([0; secretbox::KEYBYTES]); let mut key = [0; 32];
let secretbox::Key(ref mut binary_key) = key; crypto_pwhash(
&mut key,
pwhash::derive_key(
binary_key,
&self.bytes.read(), &self.bytes.read(),
&salt, &salt,
pwhash::OPSLIMIT_INTERACTIVE, CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE,
pwhash::MEMLIMIT_INTERACTIVE, CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE,
).expect("Failed to get key from password"); PasswordHashAlgorithm::Argon2id13,
)
.expect("Failed to get key from password");
Some(key) Some(key)
} else { } else {
None None
...@@ -356,34 +357,38 @@ impl Eq for Passwd {} ...@@ -356,34 +357,38 @@ impl Eq for Passwd {}
/// will be prompted on stdin for a password, empty passwords will cause the /// will be prompted on stdin for a password, empty passwords will cause the
/// secret key to be stored in plain text. Note that parent /// secret key to be stored in plain text. Note that parent
/// directories will not be created. /// directories will not be created.
pub fn gen_keypair(pkey_path: &Path, skey_path: &Path) -> Result<(PublicKeyFile, SecretKeyFile), Error> { pub fn gen_keypair(
let passwd = Passwd::prompt_new() pkey_path: &Path,
.chain_err(|| skey_path )?; skey_path: &Path,
) -> Result<(PublicKeyFile, SecretKeyFile), Error> {
let passwd = Passwd::prompt_new().chain_err(|| skey_path)?;
let (pkey_file, mut skey_file) = SecretKeyFile::new(); let (pkey_file, mut skey_file) = SecretKeyFile::new();
skey_file.encrypt(passwd); skey_file.encrypt(passwd)?;
skey_file.save(skey_path)?; skey_file.save(skey_path)?;
pkey_file.save(pkey_path)?; pkey_file.save(pkey_path)?;
println!("Generated {} and {}", pkey_path.display(), skey_path.display()); println!(
"Generated {} and {}",
pkey_path.display(),
skey_path.display()
);
Ok((pkey_file, skey_file)) Ok((pkey_file, skey_file))
} }
fn prompt_skey(skey_path: &Path, prompt: impl AsRef<str>) -> Result<SecretKeyFile, Error> { fn prompt_skey(skey_path: &Path, prompt: impl AsRef<str>) -> Result<SecretKeyFile, Error> {
let mut key_file = SecretKeyFile::open(skey_path)?; let mut key_file = SecretKeyFile::open(skey_path)?;
if key_file.is_encrypted() { if key_file.is_encrypted() {
let passwd = Passwd::prompt(&format!("{} {}: ", prompt.as_ref(), skey_path.display())) let passwd = Passwd::prompt(&format!("{} {}: ", prompt.as_ref(), skey_path.display()))
.chain_err(|| skey_path )?; .chain_err(|| skey_path)?;
key_file.decrypt(passwd) key_file.decrypt(passwd).chain_err(|| skey_path)?;
.chain_err(|| skey_path )?;
} }
Ok(key_file) Ok(key_file)
} }
/// Get a SecretKeyFile from a path. If the file is encrypted, prompt for a password on stdin. /// Get a SecretKeyFile from a path. If the file is encrypted, prompt for a password on stdin.
pub fn get_skey(skey_path: &Path) -> Result<SecretKeyFile, Error> { pub fn get_skey(skey_path: &Path) -> Result<SecretKeyFile, Error> {
prompt_skey(skey_path, "Passphrase for") prompt_skey(skey_path, "Passphrase for")
...@@ -393,11 +398,9 @@ pub fn get_skey(skey_path: &Path) -> Result<SecretKeyFile, Error> { ...@@ -393,11 +398,9 @@ pub fn get_skey(skey_path: &Path) -> Result<SecretKeyFile, Error> {
/// secret key at `skey_path`. /// secret key at `skey_path`.
pub fn re_encrypt(skey_path: &Path) -> Result<(), Error> { pub fn re_encrypt(skey_path: &Path) -> Result<(), Error> {
let mut skey_file = prompt_skey(skey_path, "Old passphrase for")?; let mut skey_file = prompt_skey(skey_path, "Old passphrase for")?;
let passwd = Passwd::prompt_new() let passwd = Passwd::prompt_new().chain_err(|| skey_path)?;
.chain_err(|| skey_path )?; skey_file.encrypt(passwd)?;
skey_file.encrypt(passwd);
skey_file.save(skey_path) skey_file.save(skey_path)
} }
use std::io;
use std::fs; use std::fs;
use std::io;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::process; use std::process;
...@@ -7,15 +7,8 @@ use clap::clap_app; ...@@ -7,15 +7,8 @@ use clap::clap_app;
use user_error::UFE; use user_error::UFE;
use pkgar_keys::{ use pkgar_keys::{
DEFAULT_PUBKEY, gen_keypair, get_skey, re_encrypt, Error, ErrorKind, ResultExt, SecretKeyFile, DEFAULT_PUBKEY,
DEFAULT_SECKEY, DEFAULT_SECKEY,
Error,
ErrorKind,
gen_keypair,
get_skey,
ResultExt,
SecretKeyFile,
re_encrypt
}; };
fn cli() -> Result<i32, Error> { fn cli() -> Result<i32, Error> {
...@@ -41,62 +34,63 @@ fn cli() -> Result<i32, Error> { ...@@ -41,62 +34,63 @@ fn cli() -> Result<i32, Error> {
(@arg file: -f --file [FILE] "Output to a file instead of stdout") (@arg file: -f --file [FILE] "Output to a file instead of stdout")
) )
).get_matches(); ).get_matches();
let skey_path = matches.value_of("skey") let skey_path = matches
.map(|file| PathBuf::from(file) ) .value_of("skey")
.map(|file| PathBuf::from(file))
.unwrap_or(DEFAULT_SECKEY.clone()); .unwrap_or(DEFAULT_SECKEY.clone());
let (subcommand, submatches) = matches.subcommand(); let (subcommand, submatches) = matches.subcommand();
let submatches = submatches let submatches = submatches.expect("A subcommand should have been provided");
.expect("A subcommand should have been provided");
match subcommand { match subcommand {
"gen" => { "gen" => {
if let Some(keydir) = skey_path.parent() { if let Some(keydir) = skey_path.parent() {
fs::create_dir_all(&keydir) fs::create_dir_all(&keydir).chain_err(|| keydir)?;
.chain_err(|| keydir )?;
} }
if ! submatches.is_present("force") { if !submatches.is_present("force") {
if skey_path.exists() { if skey_path.exists() {
return Err(Error::from_kind(ErrorKind::Io( return Err(Error::from_kind(ErrorKind::Io(io::Error::from(
io::Error::from(io::ErrorKind::AlreadyExists) io::ErrorKind::AlreadyExists,
))) ))))
.chain_err(|| &skey_path ); .chain_err(|| &skey_path);
} }
} }
let pkey_path = submatches.value_of("pkey") let pkey_path = submatches
.map(|file| PathBuf::from(file) ) .value_of("pkey")
.map(|file| PathBuf::from(file))
.unwrap_or(DEFAULT_PUBKEY.clone()); .unwrap_or(DEFAULT_PUBKEY.clone());
if ! submatches.is_present("plaintext") { if !submatches.is_present("plaintext") {
gen_keypair(&pkey_path, &skey_path)?; gen_keypair(&pkey_path, &skey_path)?;
} else { } else {
let (pkey, skey) = SecretKeyFile::new(); let (pkey, skey) = SecretKeyFile::new();
pkey.save(&pkey_path)?; pkey.save(&pkey_path)?;
skey.save(&skey_path)?; skey.save(&skey_path)?;
} }
}, }
"export" => { "export" => {
let skey = get_skey(&skey_path)?; let skey = get_skey(&skey_path)?;
let pkey = skey.public_key_file() let pkey = skey
.public_key_file()
.expect("Secret key was encrypted after being decrypted"); .expect("Secret key was encrypted after being decrypted");
if let Some(file) = submatches.value_of("file") { if let Some(file) = submatches.value_of("file") {
pkey.save(file)?; pkey.save(file)?;
} else { } else {
pkey.write(io::stdout().lock()) pkey.write(io::stdout().lock())
.chain_err(|| Path::new("stdout") )?; .chain_err(|| Path::new("stdout"))?;
} }
}, }
"rencrypt" => { "rencrypt" => {
re_encrypt(&skey_path)?; re_encrypt(&skey_path)?;
println!("Successfully re-encrypted {}", skey_path.display()); println!("Successfully re-encrypted {}", skey_path.display());
}, }
_ => unreachable!(), _ => unreachable!(),
} }
Ok(0) Ok(0)
} }
...@@ -107,4 +101,3 @@ fn main() { ...@@ -107,4 +101,3 @@ fn main() {
}); });
process::exit(code); process::exit(code);
} }
[package] [package]
name = "pkgar-repo" name = "pkgar-repo"
version = "0.1.11" version = "0.1.15"
description = "Redox Package Repository" description = "Redox Package Repository"
license = "MIT" license = "MIT"
authors = ["Jeremy Soller <jackpot51@gmail.com>"] authors = ["Jeremy Soller <jackpot51@gmail.com>"]
...@@ -8,7 +8,6 @@ repository = "https://gitlab.redox-os.org/redox-os/pkgar" ...@@ -8,7 +8,6 @@ repository = "https://gitlab.redox-os.org/redox-os/pkgar"
edition = "2018" edition = "2018"
[dependencies] [dependencies]
pkgar = { version = "0.1.11", path = "../pkgar" } pkgar = { version = "0.1.15", path = "../pkgar" }
pkgar-core = { version = "0.1.11", path = "../pkgar-core" } pkgar-core = { version = "0.1.15", path = "../pkgar-core" }
reqwest = { version = "0.11.10", default_features = false, features = ["blocking", "rustls-tls"] } reqwest = { version = "0.11.10", default-features = false, features = ["blocking", "rustls-tls"] }
sodiumoxide = { version = "0.2.7", default_features = false }
use pkgar_core::{HEADER_SIZE, Header, PackageSrc}; use pkgar_core::{Header, PackageSrc, PublicKey, HEADER_SIZE};
use sodiumoxide::crypto::sign::PublicKey; use std::{convert::TryFrom, io::Read};
use std::{
convert::TryFrom,
io::Read
};
use crate::Error; use crate::Error;
...@@ -17,16 +13,14 @@ impl<'a> PackageUrl<'a> { ...@@ -17,16 +13,14 @@ impl<'a> PackageUrl<'a> {
pub fn new( pub fn new(
client: &'a reqwest::blocking::Client, client: &'a reqwest::blocking::Client,
url: String, url: String,
public_key: &PublicKey public_key: &PublicKey,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
let mut new = Self { let mut new = Self {
client, client,
url, url,
// Need a blank header to construct the PackageFile, since we need to // Need a blank header to construct the PackageFile, since we need to
// use a method of PackageSrc in order to get the actual header... // use a method of PackageSrc in order to get the actual header...
header: unsafe { header: unsafe { *Header::new_unchecked(&[0; HEADER_SIZE])? },
*Header::new_unchecked(&[0; HEADER_SIZE])?
},
}; };
new.header = new.read_header(public_key)?; new.header = new.read_header(public_key)?;
Ok(new) Ok(new)
...@@ -44,16 +38,22 @@ impl<'a> PackageSrc for PackageUrl<'a> { ...@@ -44,16 +38,22 @@ impl<'a> PackageSrc for PackageUrl<'a> {
if buf.is_empty() { if buf.is_empty() {
return Ok(0); return Ok(0);
} }
let end_offset = offset.checked_add( let end_offset = offset
u64::try_from( .checked_add(
buf.len().checked_sub(1) u64::try_from(
.ok_or(pkgar_core::Error::Overflow)? buf.len()
).map_err(pkgar_core::Error::TryFromInt)? .checked_sub(1)
).ok_or(pkgar_core::Error::Overflow)?; .ok_or(pkgar_core::Error::Overflow)?,
)
.map_err(pkgar_core::Error::TryFromInt)?,
)
.ok_or(pkgar_core::Error::Overflow)?;
let range = format!("bytes={}-{}", offset, end_offset); let range = format!("bytes={}-{}", offset, end_offset);
eprint!("Request {} from {}", range, self.url); eprint!("Request {} from {}", range, self.url);
let mut response = self.client.get(&self.url) let mut response = self
.client
.get(&self.url)
.header(reqwest::header::RANGE, range) .header(reqwest::header::RANGE, range)
.send()? .send()?
.error_for_status()?; .error_for_status()?;
......
[package] [package]
name = "pkgar" name = "pkgar"
version = "0.1.11" version = "0.1.15"
description = "Redox Package Archive" description = "Redox Package Archive"
license = "MIT" license = "MIT"
authors = ["Jeremy Soller <jackpot51@gmail.com>", "Wesley Hershberger <mggmugginsmc@gmail.com>"] authors = ["Jeremy Soller <jackpot51@gmail.com>", "Wesley Hershberger <mggmugginsmc@gmail.com>"]
...@@ -10,9 +10,8 @@ edition = "2018" ...@@ -10,9 +10,8 @@ edition = "2018"
[dependencies] [dependencies]
error-chain = "0.12" error-chain = "0.12"
plain = "0.2.3" plain = "0.2.3"
pkgar-core = { version = "0.1.11", path = "../pkgar-core" } pkgar-core = { version = "0.1.15", path = "../pkgar-core" }
pkgar-keys = { version = "0.1.11", path = "../pkgar-keys" } pkgar-keys = { version = "0.1.15", path = "../pkgar-keys" }
sodiumoxide = { version = "0.2.7", default_features = false }
#thiserror = "1.0.20" #thiserror = "1.0.20"
user-error = "1.2.8" user-error = "1.2.8"
......
...@@ -4,17 +4,20 @@ use std::os::unix::ffi::OsStrExt; ...@@ -4,17 +4,20 @@ use std::os::unix::ffi::OsStrExt;
use std::os::unix::fs::PermissionsExt; use std::os::unix::fs::PermissionsExt;
use std::path::Path; use std::path::Path;
use pkgar_core::{Entry, Header, Mode, PackageSrc}; use pkgar_core::{
dryoc::classic::crypto_sign::crypto_sign_detached, Entry, Header, Mode, PackageSrc,
};
use pkgar_keys::PublicKeyFile; use pkgar_keys::PublicKeyFile;
use sodiumoxide::crypto::sign;
use crate::{Error, ErrorKind, READ_WRITE_HASH_BUF_SIZE, ResultExt};
use crate::ext::{copy_and_hash, EntryExt}; use crate::ext::{copy_and_hash, EntryExt};
use crate::package::PackageFile; use crate::package::PackageFile;
use crate::transaction::Transaction; use crate::transaction::Transaction;
use crate::{Error, ErrorKind, ResultExt, READ_WRITE_HASH_BUF_SIZE};
fn folder_entries<P, Q>(base: P, path: Q, entries: &mut Vec<Entry>) -> io::Result<()> fn folder_entries<P, Q>(base: P, path: Q, entries: &mut Vec<Entry>) -> io::Result<()>
where P: AsRef<Path>, Q: AsRef<Path> where
P: AsRef<Path>,
Q: AsRef<Path>,
{ {
let base = base.as_ref(); let base = base.as_ref();
let path = path.as_ref(); let path = path.as_ref();
...@@ -32,19 +35,20 @@ fn folder_entries<P, Q>(base: P, path: Q, entries: &mut Vec<Entry>) -> io::Resul ...@@ -32,19 +35,20 @@ fn folder_entries<P, Q>(base: P, path: Q, entries: &mut Vec<Entry>) -> io::Resul
if metadata.is_dir() { if metadata.is_dir() {
folder_entries(base, entry_path, entries)?; folder_entries(base, entry_path, entries)?;
} else { } else {
let relative = entry_path.strip_prefix(base).map_err(|err| { let relative = entry_path
io::Error::new( .strip_prefix(base)
io::ErrorKind::Other, .map_err(|err| io::Error::new(io::ErrorKind::Other, err))?;
err
)
})?;
let mut path_bytes = [0; 256]; let mut path_bytes = [0; 256];
let relative_bytes = relative.as_os_str().as_bytes(); let relative_bytes = relative.as_os_str().as_bytes();
if relative_bytes.len() >= path_bytes.len() { if relative_bytes.len() >= path_bytes.len() {
return Err(io::Error::new( return Err(io::Error::new(
io::ErrorKind::Other, io::ErrorKind::Other,
format!("relative path longer than supported: {} > {}", relative_bytes.len(), path_bytes.len()) format!(
"relative path longer than supported: {} > {}",
relative_bytes.len(),
path_bytes.len()
),
)); ));
} }
path_bytes[..relative_bytes.len()].copy_from_slice(relative_bytes); path_bytes[..relative_bytes.len()].copy_from_slice(relative_bytes);
...@@ -83,9 +87,15 @@ pub fn create( ...@@ -83,9 +87,15 @@ pub fn create(
archive_path: impl AsRef<Path>, archive_path: impl AsRef<Path>,
folder: impl AsRef<Path>, folder: impl AsRef<Path>,
) -> Result<(), Error> { ) -> Result<(), Error> {
let secret_key = pkgar_keys::get_skey(&secret_path.as_ref())? let keyfile = pkgar_keys::get_skey(&secret_path.as_ref())?;
.key() let secret_key = keyfile.secret_key().expect(&format!(
.expect(&format!("{} was encrypted?", secret_path.as_ref().display())); "{} was encrypted?",
secret_path.as_ref().display()
));
let public_key = keyfile.public_key().expect(&format!(
"{} was encrypted?",
secret_path.as_ref().display()
));
//TODO: move functions to library //TODO: move functions to library
...@@ -94,38 +104,38 @@ pub fn create( ...@@ -94,38 +104,38 @@ pub fn create(
.create(true) .create(true)
.truncate(true) .truncate(true)
.open(&archive_path) .open(&archive_path)
.chain_err(|| archive_path.as_ref() )?; .chain_err(|| archive_path.as_ref())?;
// Create a list of entries // Create a list of entries
let mut entries = Vec::new(); let mut entries = Vec::new();
folder_entries(&folder, &folder, &mut entries) folder_entries(&folder, &folder, &mut entries)
.chain_err(|| folder.as_ref() ) .chain_err(|| folder.as_ref())
.chain_err(|| "Recursing buildroot" )?; .chain_err(|| "Recursing buildroot")?;
// Create initial header // Create initial header
let mut header = Header { let mut header = Header {
signature: [0; 64], signature: [0; 64],
public_key: [0; 32], public_key,
blake3: [0; 32], blake3: [0; 32],
count: entries.len() as u64 count: entries.len() as u64,
}; };
header.public_key.copy_from_slice(secret_key.public_key().as_ref());
// Assign offsets to each entry // Assign offsets to each entry
let mut data_size: u64 = 0; let mut data_size: u64 = 0;
for entry in &mut entries { for entry in &mut entries {
entry.offset = data_size; entry.offset = data_size;
data_size = data_size.checked_add(entry.size) data_size = data_size
.checked_add(entry.size)
.ok_or(pkgar_core::Error::Overflow) .ok_or(pkgar_core::Error::Overflow)
.map_err(Error::from) .map_err(Error::from)
.chain_err(|| ErrorKind::Entry(*entry) )?; .chain_err(|| ErrorKind::Entry(*entry))?;
} }
let data_offset = header.total_size()?; let data_offset = header.total_size()?;
archive_file.seek(SeekFrom::Start(data_offset as u64)) archive_file
.chain_err(|| archive_path.as_ref() ) .seek(SeekFrom::Start(data_offset as u64))
.chain_err(|| format!("Seek to {} (data offset)", data_offset) )?; .chain_err(|| archive_path.as_ref())
.chain_err(|| format!("Seek to {} (data offset)", data_offset))?;
//TODO: fallocate data_offset + data_size //TODO: fallocate data_offset + data_size
...@@ -136,68 +146,78 @@ pub fn create( ...@@ -136,68 +146,78 @@ pub fn create(
let relative = entry.check_path()?; let relative = entry.check_path()?;
let path = folder.as_ref().join(relative); let path = folder.as_ref().join(relative);
let mode = entry.mode() let mode = entry
.mode()
.map_err(Error::from) .map_err(Error::from)
.chain_err(|| ErrorKind::Entry(*entry) )?; .chain_err(|| ErrorKind::Entry(*entry))?;
let (total, hash) = match mode.kind() { let (total, hash) = match mode.kind() {
Mode::FILE => { Mode::FILE => {
let mut entry_file = fs::OpenOptions::new() let mut entry_file = fs::OpenOptions::new()
.read(true) .read(true)
.open(&path) .open(&path)
.chain_err(|| &path )?; .chain_err(|| &path)?;
copy_and_hash(&mut entry_file, &mut archive_file, &mut buf) copy_and_hash(&mut entry_file, &mut archive_file, &mut buf)
.chain_err(|| &path ) .chain_err(|| &path)
.chain_err(|| format!("Writing entry to archive: '{}'", relative.display()) )? .chain_err(|| format!("Writing entry to archive: '{}'", relative.display()))?
}, }
Mode::SYMLINK => { Mode::SYMLINK => {
let destination = fs::read_link(&path) let destination = fs::read_link(&path).chain_err(|| &path)?;
.chain_err(|| &path )?;
let mut data = destination.as_os_str().as_bytes(); let mut data = destination.as_os_str().as_bytes();
copy_and_hash(&mut data, &mut archive_file, &mut buf) copy_and_hash(&mut data, &mut archive_file, &mut buf)
.chain_err(|| &path ) .chain_err(|| &path)
.chain_err(|| format!("Writing entry to archive: '{}'", relative.display()) )? .chain_err(|| format!("Writing entry to archive: '{}'", relative.display()))?
}, }
_ => return Err(Error::from( _ => {
pkgar_core::Error::InvalidMode(mode.bits()) return Err(Error::from(pkgar_core::Error::InvalidMode(mode.bits())))
)) .chain_err(|| ErrorKind::Entry(*entry))
.chain_err(|| ErrorKind::Entry(*entry) ), }
}; };
if total != entry.size() { if total != entry.size() {
return Err(Error::from_kind(ErrorKind::LengthMismatch(total, entry.size()))) return Err(Error::from_kind(ErrorKind::LengthMismatch(
.chain_err(|| ErrorKind::Entry(*entry) ); total,
entry.size(),
)))
.chain_err(|| ErrorKind::Entry(*entry));
} }
entry.blake3.copy_from_slice(hash.as_bytes()); entry.blake3.copy_from_slice(hash.as_bytes());
header_hasher.update_with_join::<blake3::join::RayonJoin>(unsafe { header_hasher
plain::as_bytes(entry) .update_with_join::<blake3::join::RayonJoin>(unsafe { plain::as_bytes(entry) });
});
} }
header.blake3.copy_from_slice(header_hasher.finalize().as_bytes()); header
.blake3
.copy_from_slice(header_hasher.finalize().as_bytes());
//TODO: ensure file size matches //TODO: ensure file size matches
header.signature = sign::sign_detached(unsafe { &plain::as_bytes(&header)[64..] }, &secret_key).to_bytes(); let mut signature = [0; 64];
crypto_sign_detached(
&mut signature,
unsafe { &plain::as_bytes(&header)[64..] },
&secret_key,
)
.map_err(pkgar_core::Error::Dryoc)?;
header.signature.copy_from_slice(&signature);
// Write archive header // Write archive header
archive_file.seek(SeekFrom::Start(0)) archive_file
.chain_err(|| archive_path.as_ref() )?; .seek(SeekFrom::Start(0))
.chain_err(|| archive_path.as_ref())?;
archive_file.write_all(unsafe { archive_file
plain::as_bytes(&header) .write_all(unsafe { plain::as_bytes(&header) })
}) .chain_err(|| archive_path.as_ref())?;
.chain_err(|| archive_path.as_ref() )?;
// Write each entry header // Write each entry header
for entry in &entries { for entry in &entries {
let checked_path = entry.check_path()?; let checked_path = entry.check_path()?;
archive_file.write_all(unsafe { archive_file
plain::as_bytes(entry) .write_all(unsafe { plain::as_bytes(entry) })
}) .chain_err(|| archive_path.as_ref())
.chain_err(|| archive_path.as_ref() ) .chain_err(|| format!("Write entry {}", checked_path.display()))?;
.chain_err(|| format!("Write entry {}", checked_path.display()) )?;
} }
Ok(()) Ok(())
...@@ -212,8 +232,7 @@ pub fn extract( ...@@ -212,8 +232,7 @@ pub fn extract(
let mut package = PackageFile::new(archive_path, &pkey)?; let mut package = PackageFile::new(archive_path, &pkey)?;
Transaction::install(&mut package, base_dir)? Transaction::install(&mut package, base_dir)?.commit()?;
.commit()?;
Ok(()) Ok(())
} }
...@@ -227,16 +246,12 @@ pub fn remove( ...@@ -227,16 +246,12 @@ pub fn remove(
let mut package = PackageFile::new(archive_path, &pkey)?; let mut package = PackageFile::new(archive_path, &pkey)?;
Transaction::remove(&mut package, base_dir)? Transaction::remove(&mut package, base_dir)?.commit()?;
.commit()?;
Ok(()) Ok(())
} }
pub fn list( pub fn list(pkey_path: impl AsRef<Path>, archive_path: impl AsRef<Path>) -> Result<(), Error> {
pkey_path: impl AsRef<Path>,
archive_path: impl AsRef<Path>,
) -> Result<(), Error> {
let pkey = PublicKeyFile::open(&pkey_path.as_ref())?.pkey; let pkey = PublicKeyFile::open(&pkey_path.as_ref())?.pkey;
let mut package = PackageFile::new(archive_path, &pkey)?; let mut package = PackageFile::new(archive_path, &pkey)?;
...@@ -266,7 +281,7 @@ pub fn split( ...@@ -266,7 +281,7 @@ pub fn split(
.create(true) .create(true)
.truncate(true) .truncate(true)
.open(&data_path) .open(&data_path)
.chain_err(|| data_path.as_ref() )?; .chain_err(|| data_path.as_ref())?;
src.seek(SeekFrom::Start(data_offset)) src.seek(SeekFrom::Start(data_offset))
.chain_err(|| archive_path.as_ref())?; .chain_err(|| archive_path.as_ref())?;
...@@ -281,7 +296,7 @@ pub fn split( ...@@ -281,7 +296,7 @@ pub fn split(
.create(true) .create(true)
.truncate(true) .truncate(true)
.open(&head_path) .open(&head_path)
.chain_err(|| head_path.as_ref() )?; .chain_err(|| head_path.as_ref())?;
src.seek(SeekFrom::Start(0)) src.seek(SeekFrom::Start(0))
.chain_err(|| archive_path.as_ref())?; .chain_err(|| archive_path.as_ref())?;
...@@ -304,11 +319,9 @@ pub fn verify( ...@@ -304,11 +319,9 @@ pub fn verify(
let mut buf = vec![0; READ_WRITE_HASH_BUF_SIZE]; let mut buf = vec![0; READ_WRITE_HASH_BUF_SIZE];
for entry in package.read_entries()? { for entry in package.read_entries()? {
let expected_path = base_dir.as_ref() let expected_path = base_dir.as_ref().join(entry.check_path()?);
.join(entry.check_path()?);
let expected = File::open(&expected_path) let expected = File::open(&expected_path).chain_err(|| &expected_path)?;
.chain_err(|| &expected_path )?;
let (count, hash) = copy_and_hash(expected, io::sink(), &mut buf)?; let (count, hash) = copy_and_hash(expected, io::sink(), &mut buf)?;
......
//! Extention traits for base types defined in `pkgar-core`. //! Extention traits for base types defined in `pkgar-core`.
use std::io::{self, Read, Write};
use std::ffi::OsStr; use std::ffi::OsStr;
use std::io::{self, Read, Write};
use std::os::unix::ffi::OsStrExt; use std::os::unix::ffi::OsStrExt;
use std::path::{Component, Path}; use std::path::{Component, Path};
...@@ -12,7 +12,7 @@ use crate::{Error, ErrorKind, ResultExt}; ...@@ -12,7 +12,7 @@ use crate::{Error, ErrorKind, ResultExt};
/// Handy associated functions for `pkgar_core::Entry` that depend on std /// Handy associated functions for `pkgar_core::Entry` that depend on std
pub trait EntryExt { pub trait EntryExt {
fn check_path(&self) -> Result<&Path, Error>; fn check_path(&self) -> Result<&Path, Error>;
fn verify(&self, blake3: Hash, size: u64) -> Result<(), Error>; fn verify(&self, blake3: Hash, size: u64) -> Result<(), Error>;
} }
...@@ -23,23 +23,26 @@ impl EntryExt for Entry { ...@@ -23,23 +23,26 @@ impl EntryExt for Entry {
let path = Path::new(OsStr::from_bytes(self.path_bytes())); let path = Path::new(OsStr::from_bytes(self.path_bytes()));
for component in path.components() { for component in path.components() {
match component { match component {
Component::Normal(_) => {}, Component::Normal(_) => {}
invalid => { invalid => {
let bad_component: &Path = invalid.as_ref(); let bad_component: &Path = invalid.as_ref();
return Err(Error::from_kind( return Err(Error::from_kind(ErrorKind::InvalidPathComponent(
ErrorKind::InvalidPathComponent(bad_component.to_path_buf()) bad_component.to_path_buf(),
)) )))
.chain_err(|| ErrorKind::Entry(*self) ); .chain_err(|| ErrorKind::Entry(*self));
}, }
} }
} }
Ok(&path) Ok(&path)
} }
fn verify(&self, blake3: Hash, size: u64) -> Result<(), Error> { fn verify(&self, blake3: Hash, size: u64) -> Result<(), Error> {
if size != self.size() { if size != self.size() {
Err(Error::from_kind(ErrorKind::LengthMismatch(size, self.size()))) Err(Error::from_kind(ErrorKind::LengthMismatch(
.chain_err(|| ErrorKind::Entry(*self) ) size,
self.size(),
)))
.chain_err(|| ErrorKind::Entry(*self))
} else if blake3 != self.blake3() { } else if blake3 != self.blake3() {
Err(pkgar_core::Error::InvalidBlake3.into()) Err(pkgar_core::Error::InvalidBlake3.into())
} else { } else {
...@@ -49,12 +52,13 @@ impl EntryExt for Entry { ...@@ -49,12 +52,13 @@ impl EntryExt for Entry {
} }
pub trait PackageSrcExt pub trait PackageSrcExt
where Self: PackageSrc + Sized, where
Self: PackageSrc + Sized,
{ {
/// Get the path corresponding to this `PackageSrc`. This will likely be /// Get the path corresponding to this `PackageSrc`. This will likely be
/// refactored to use something more generic than `Path` in future. /// refactored to use something more generic than `Path` in future.
fn path(&self) -> &Path; fn path(&self) -> &Path;
/// Build a reader for a given entry on this source. /// Build a reader for a given entry on this source.
fn entry_reader(&mut self, entry: Entry) -> EntryReader<'_, Self> { fn entry_reader(&mut self, entry: Entry) -> EntryReader<'_, Self> {
EntryReader { EntryReader {
...@@ -68,7 +72,8 @@ pub trait PackageSrcExt ...@@ -68,7 +72,8 @@ pub trait PackageSrcExt
/// A reader that provides acess to one entry's data within a `PackageSrc`. /// A reader that provides acess to one entry's data within a `PackageSrc`.
/// Use `PackageSrcExt::entry_reader` for construction /// Use `PackageSrcExt::entry_reader` for construction
pub struct EntryReader<'a, Src> pub struct EntryReader<'a, Src>
where Src: PackageSrc where
Src: PackageSrc,
{ {
src: &'a mut Src, src: &'a mut Src,
entry: Entry, entry: Entry,
...@@ -76,18 +81,18 @@ pub struct EntryReader<'a, Src> ...@@ -76,18 +81,18 @@ pub struct EntryReader<'a, Src>
} }
impl<Src, E> Read for EntryReader<'_, Src> impl<Src, E> Read for EntryReader<'_, Src>
where where
Src: PackageSrc<Err = E>, Src: PackageSrc<Err = E>,
E: From<pkgar_core::Error> + std::error::Error, E: From<pkgar_core::Error> + std::error::Error,
{ {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let count = self.src.read_entry(self.entry, self.pos, buf) let count = self
.src
.read_entry(self.entry, self.pos, buf)
// This is a little painful, since e is pkgar::Error... // This is a little painful, since e is pkgar::Error...
// However, this is likely to be a very rarely triggered error // However, this is likely to be a very rarely triggered error
// condition. // condition.
.map_err(|err| .map_err(|err| io::Error::new(io::ErrorKind::Other, err.to_string()))?;
io::Error::new(io::ErrorKind::Other, err.to_string())
)?;
self.pos += count; self.pos += count;
Ok(count) Ok(count)
} }
...@@ -99,7 +104,7 @@ impl<Src, E> Read for EntryReader<'_, Src> ...@@ -99,7 +104,7 @@ impl<Src, E> Read for EntryReader<'_, Src>
pub(crate) fn copy_and_hash<R: Read, W: Write>( pub(crate) fn copy_and_hash<R: Read, W: Write>(
mut read: R, mut read: R,
mut write: W, mut write: W,
buf: &mut [u8] buf: &mut [u8],
) -> Result<(u64, Hash), io::Error> { ) -> Result<(u64, Hash), io::Error> {
let mut hasher = Hasher::new(); let mut hasher = Hasher::new();
let mut written = 0; let mut written = 0;
...@@ -110,9 +115,8 @@ pub(crate) fn copy_and_hash<R: Read, W: Write>( ...@@ -110,9 +115,8 @@ pub(crate) fn copy_and_hash<R: Read, W: Write>(
} }
written += count as u64; written += count as u64;
hasher.update_with_join::<blake3::join::RayonJoin>(&buf[..count]); hasher.update_with_join::<blake3::join::RayonJoin>(&buf[..count]);
write.write_all(&buf[..count])?; write.write_all(&buf[..count])?;
} }
Ok((written, hasher.finalize())) Ok((written, hasher.finalize()))
} }
...@@ -21,20 +21,20 @@ error_chain! { ...@@ -21,20 +21,20 @@ error_chain! {
types { types {
Error, ErrorKind, ResultExt; Error, ErrorKind, ResultExt;
} }
links { links {
Keys(pkgar_keys::Error, pkgar_keys::ErrorKind); Keys(pkgar_keys::Error, pkgar_keys::ErrorKind);
} }
foreign_links { foreign_links {
Io(io::Error); Io(io::Error);
} }
errors { errors {
Core(src: pkgar_core::Error) { Core(src: pkgar_core::Error) {
display("{}", src), display("{}", src),
} }
FailedCommit(changed: usize, remaining: usize) { FailedCommit(changed: usize, remaining: usize) {
display( display(
"Failed to commit transaction. {} files changed, {} files remaining", "Failed to commit transaction. {} files changed, {} files remaining",
...@@ -42,23 +42,23 @@ error_chain! { ...@@ -42,23 +42,23 @@ error_chain! {
remaining, remaining,
), ),
} }
InvalidPathComponent(path: PathBuf) { InvalidPathComponent(path: PathBuf) {
display("Invalid path component: {}", path.display()), display("Invalid path component: {}", path.display()),
} }
LengthMismatch(actual: u64, expected: u64) { LengthMismatch(actual: u64, expected: u64) {
display("Entry size mismatch: expected {}, got {}", expected, actual), display("Entry size mismatch: expected {}, got {}", expected, actual),
} }
InvalidModeKind(mode: Mode) { InvalidModeKind(mode: Mode) {
display("Invalid Mode Kind: {:#o}", mode), display("Invalid Mode Kind: {:#o}", mode),
} }
Path(path: PathBuf) { Path(path: PathBuf) {
display("Path: {}", path.display()), display("Path: {}", path.display()),
} }
Entry(entry: Entry) { Entry(entry: Entry) {
display("Entry: {:?}", entry), display("Entry: {:?}", entry),
} }
...@@ -101,4 +101,3 @@ impl<T> ResultExt<T> for Result<T, pkgar_core::Error> { ...@@ -101,4 +101,3 @@ impl<T> ResultExt<T> for Result<T, pkgar_core::Error> {
) )
} }
}*/ }*/
use std::process; use std::process;
use clap::{App, AppSettings, Arg, crate_authors, crate_description, crate_name, crate_version, SubCommand}; use clap::{
use pkgar::{ crate_authors, crate_description, crate_name, crate_version, App, AppSettings, Arg, SubCommand,
create,
extract,
remove,
list,
split,
verify,
}; };
use pkgar::{create, extract, list, remove, split, verify};
use pkgar_keys::{DEFAULT_PUBKEY, DEFAULT_SECKEY}; use pkgar_keys::{DEFAULT_PUBKEY, DEFAULT_SECKEY};
use user_error::UFE; use user_error::UFE;
...@@ -58,50 +53,52 @@ fn main() { ...@@ -58,50 +53,52 @@ fn main() {
.about(crate_description!()) .about(crate_description!())
.version(crate_version!()) .version(crate_version!())
.setting(AppSettings::SubcommandRequiredElseHelp) .setting(AppSettings::SubcommandRequiredElseHelp)
.subcommand(SubCommand::with_name("create") .subcommand(
.about("Create archive") SubCommand::with_name("create")
.arg(&arg_skey) .about("Create archive")
.arg(&arg_archive) .arg(&arg_skey)
.arg(&arg_basedir) .arg(&arg_archive)
.arg(&arg_basedir),
) )
.subcommand(SubCommand::with_name("extract") .subcommand(
.about("Extract archive") SubCommand::with_name("extract")
.arg(&arg_pkey) .about("Extract archive")
.arg(&arg_archive) .arg(&arg_pkey)
.arg(&arg_basedir) .arg(&arg_archive)
.arg(&arg_basedir),
) )
.subcommand(SubCommand::with_name("list") .subcommand(
.about("List archive") SubCommand::with_name("list")
.arg(&arg_pkey) .about("List archive")
.arg(&arg_archive) .arg(&arg_pkey)
.arg(&arg_archive),
) )
.subcommand(SubCommand::with_name("remove") .subcommand(
.about("Unextract archive") SubCommand::with_name("remove")
.arg(&arg_pkey) .about("Unextract archive")
.arg(&arg_archive) .arg(&arg_pkey)
.arg(&arg_basedir) .arg(&arg_archive)
.arg(&arg_basedir),
) )
.subcommand(SubCommand::with_name("split") .subcommand(
.about("Split archive into head and data files") SubCommand::with_name("split")
.arg(&arg_pkey) .about("Split archive into head and data files")
.arg(&arg_archive) .arg(&arg_pkey)
.arg( .arg(&arg_archive)
Arg::with_name("head") .arg(
.help("Header file") Arg::with_name("head")
.required(true) .help("Header file")
.value_name("head") .required(true)
) .value_name("head"),
.arg( )
Arg::with_name("data") .arg(Arg::with_name("data").help("Data file").value_name("data")),
.help("Data file")
.value_name("data")
)
) )
.subcommand(SubCommand::with_name("verify") .subcommand(
.about("Verify archive") SubCommand::with_name("verify")
.arg(&arg_pkey) .about("Verify archive")
.arg(&arg_archive) .arg(&arg_pkey)
.arg(&arg_basedir) .arg(&arg_archive)
.arg(&arg_basedir),
) )
.get_matches(); .get_matches();
...@@ -109,13 +106,13 @@ fn main() { ...@@ -109,13 +106,13 @@ fn main() {
create( create(
matches.value_of("skey").unwrap(), matches.value_of("skey").unwrap(),
matches.value_of("archive").unwrap(), matches.value_of("archive").unwrap(),
matches.value_of("basedir").unwrap() matches.value_of("basedir").unwrap(),
) )
} else if let Some(matches) = matches.subcommand_matches("extract") { } else if let Some(matches) = matches.subcommand_matches("extract") {
extract( extract(
matches.value_of("pkey").unwrap(), matches.value_of("pkey").unwrap(),
matches.value_of("archive").unwrap(), matches.value_of("archive").unwrap(),
matches.value_of("basedir").unwrap() matches.value_of("basedir").unwrap(),
) )
} else if let Some(matches) = matches.subcommand_matches("remove") { } else if let Some(matches) = matches.subcommand_matches("remove") {
remove( remove(
...@@ -126,20 +123,20 @@ fn main() { ...@@ -126,20 +123,20 @@ fn main() {
} else if let Some(matches) = matches.subcommand_matches("list") { } else if let Some(matches) = matches.subcommand_matches("list") {
list( list(
matches.value_of("pkey").unwrap(), matches.value_of("pkey").unwrap(),
matches.value_of("archive").unwrap() matches.value_of("archive").unwrap(),
) )
} else if let Some(matches) = matches.subcommand_matches("split") { } else if let Some(matches) = matches.subcommand_matches("split") {
split( split(
matches.value_of("pkey").unwrap(), matches.value_of("pkey").unwrap(),
matches.value_of("archive").unwrap(), matches.value_of("archive").unwrap(),
matches.value_of("head").unwrap(), matches.value_of("head").unwrap(),
matches.value_of("data") matches.value_of("data"),
) )
} else if let Some(matches) = matches.subcommand_matches("verify") { } else if let Some(matches) = matches.subcommand_matches("verify") {
verify( verify(
matches.value_of("pkey").unwrap(), matches.value_of("pkey").unwrap(),
matches.value_of("archive").unwrap(), matches.value_of("archive").unwrap(),
matches.value_of("basedir").unwrap() matches.value_of("basedir").unwrap(),
) )
} else { } else {
Ok(()) Ok(())
......