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