Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ authors = ["Casey Rodarmor <[email protected]>"]
autotests = false
categories = ["authentication", "command-line-utilities", "cryptography"]
description = "file verification utility"
edition = "2021"
edition = "2024"
homepage = "https://github.com/casey/filepack"
keywords = ["checksum", "verification"]
license = "CC0-1.0"
Expand Down Expand Up @@ -42,6 +42,8 @@ all = { level = "deny", priority = -1 }
arbitrary-source-item-ordering = "deny"
float-cmp = "allow"
large_enum_variant = "allow"
missing-errors-doc = "allow"
missing-panics-doc = "allow"
needless-pass-by-value = "allow"
pedantic = { level = "deny", priority = -1 }
result-large-err = "allow"
Expand Down
2 changes: 1 addition & 1 deletion rustfmt.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
edition = "2021"
edition = "2024"
max_width = 100
newline_style = "Unix"
tab_spaces = 2
Expand Down
2 changes: 1 addition & 1 deletion src/arguments.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use {
super::*,
clap::builder::{
styling::{AnsiColor, Effects},
Styles,
styling::{AnsiColor, Effects},
},
};

Expand Down
2 changes: 1 addition & 1 deletion src/display_path.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use super::*;

#[derive(Debug)]
pub(crate) struct DisplayPath(PathBuf);
pub struct DisplayPath(PathBuf);

impl Display for DisplayPath {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Expand Down
2 changes: 1 addition & 1 deletion src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use super::*;

#[derive(Debug, Snafu)]
#[snafu(context(suffix(false)), visibility(pub(crate)))]
pub(crate) enum Error {
pub enum Error {
#[snafu(display("failed to get current directory"))]
CurrentDir { source: io::Error },
#[snafu(display("failed to get local data directory"))]
Expand Down
29 changes: 15 additions & 14 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
use {
self::{
arguments::Arguments, display_path::DisplayPath, display_secret::DisplaySecret, error::Error,
lint::Lint, lint_group::LintGroup, list::List, manifest::Manifest, metadata::Metadata,
options::Options, owo_colorize_ext::OwoColorizeExt, private_key::PrivateKey,
public_key::PublicKey, relative_path::RelativePath, signature::Signature,
signature_error::SignatureError, style::Style, subcommand::Subcommand, template::Template,
utf8_path_ext::Utf8PathExt,
arguments::Arguments, display_path::DisplayPath, display_secret::DisplaySecret, lint::Lint,
lint_group::LintGroup, list::List, metadata::Metadata, options::Options,
owo_colorize_ext::OwoColorizeExt, private_key::PrivateKey, signature_error::SignatureError,
style::Style, subcommand::Subcommand, template::Template, utf8_path_ext::Utf8PathExt,
},
blake3::Hasher,
camino::{Utf8Component, Utf8Path, Utf8PathBuf},
Expand All @@ -15,7 +13,7 @@ use {
owo_colors::Styled,
serde::{Deserialize, Deserializer, Serialize, Serializer},
serde_with::{DeserializeFromStr, SerializeDisplay},
snafu::{ensure, ErrorCompat, OptionExt, ResultExt, Snafu},
snafu::{ErrorCompat, OptionExt, ResultExt, Snafu, ensure},
std::{
array::TryFromSliceError,
backtrace::{Backtrace, BacktraceStatus},
Expand All @@ -32,7 +30,10 @@ use {
walkdir::WalkDir,
};

pub use self::{entry::Entry, hash::Hash};
pub use self::{
entry::Entry, error::Error, hash::Hash, manifest::Manifest, public_key::PublicKey,
relative_path::RelativePath, signature::Signature,
};

#[cfg(test)]
use assert_fs::TempDir;
Expand Down Expand Up @@ -91,12 +92,12 @@ pub fn run() {
eprintln!(" {}─ {err}", if i < causes - 1 { '├' } else { '└' });
}

if let Some(backtrace) = err.backtrace() {
if backtrace.status() == BacktraceStatus::Captured {
eprintln!();
eprintln!("backtrace:");
eprintln!("{backtrace}");
}
if let Some(backtrace) = err.backtrace()
&& backtrace.status() == BacktraceStatus::Captured
{
eprintln!();
eprintln!("backtrace:");
eprintln!("{backtrace}");
}

process::exit(1);
Expand Down
10 changes: 5 additions & 5 deletions src/manifest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ use super::*;

#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
pub(crate) struct Manifest {
pub struct Manifest {
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub(crate) files: BTreeMap<RelativePath, Entry>,
pub files: BTreeMap<RelativePath, Entry>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub(crate) signatures: BTreeMap<PublicKey, Signature>,
pub signatures: BTreeMap<PublicKey, Signature>,
}

impl Manifest {
Expand All @@ -25,7 +25,7 @@ impl Manifest {
hasher.finalize().into()
}

pub(crate) fn load(path: Option<&Utf8Path>) -> Result<(Utf8PathBuf, Self)> {
pub fn load(path: Option<&Utf8Path>) -> Result<(Utf8PathBuf, Self)> {
let path = if let Some(path) = path {
if filesystem::metadata(path)?.is_dir() {
path.join(Manifest::FILENAME)
Expand All @@ -45,7 +45,7 @@ impl Manifest {
Ok((path, manifest))
}

pub(crate) fn save(&self, path: &Utf8Path) -> Result<()> {
pub fn save(&self, path: &Utf8Path) -> Result<()> {
filesystem::write(path, format!("{}\n", serde_json::to_string(self).unwrap()))
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/private_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use super::*;

#[derive(Debug, Snafu)]
#[snafu(context(suffix(Error)))]
pub(crate) enum Error {
pub enum Error {
#[snafu(display("invalid private key hex"))]
Hex { source: hex::FromHexError },
#[snafu(display("invalid private key byte length {length}"))]
Expand Down
6 changes: 3 additions & 3 deletions src/public_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use super::*;

#[derive(Debug, Snafu)]
#[snafu(context(suffix(Error)))]
pub(crate) enum Error {
pub enum Error {
#[snafu(display("invalid public key hex: `{key}`"))]
Hex {
key: String,
Expand All @@ -21,7 +21,7 @@ pub(crate) enum Error {
}

#[derive(Clone, Debug, DeserializeFromStr, Eq, PartialEq, SerializeDisplay)]
pub(crate) struct PublicKey(ed25519_dalek::VerifyingKey);
pub struct PublicKey(ed25519_dalek::VerifyingKey);

impl PublicKey {
const LEN: usize = ed25519_dalek::PUBLIC_KEY_LENGTH;
Expand All @@ -38,7 +38,7 @@ impl PublicKey {
Ok(public_key)
}

pub(crate) fn verify(&self, message: &[u8], signature: &Signature) -> Result<()> {
pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<()> {
self
.0
.verify_strict(message, signature.as_ref())
Expand Down
10 changes: 5 additions & 5 deletions src/relative_path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ pub(crate) use self::error::Error;
mod error;

#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
pub(crate) struct RelativePath(String);
pub struct RelativePath(String);

impl RelativePath {
const JUNK_NAMES: [&'static str; 2] = [".DS_Store", ".localized"];
Expand Down Expand Up @@ -140,10 +140,10 @@ impl FromStr for RelativePath {
let mut chars = s.chars();
let first = chars.next();
let second = chars.next();
if let Some((first, second)) = first.zip(second) {
if second == ':' {
return Err(Error::WindowsDiskPrefix { letter: first });
}
if let Some((first, second)) = first.zip(second)
&& second == ':'
{
return Err(Error::WindowsDiskPrefix { letter: first });
}

let mut path = String::new();
Expand Down
2 changes: 1 addition & 1 deletion src/relative_path/error.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use super::*;

#[derive(Debug, PartialEq)]
pub(crate) enum Error {
pub enum Error {
Component { component: String },
DoubleSlash,
Empty,
Expand Down
4 changes: 2 additions & 2 deletions src/signature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use super::*;

#[derive(Debug, Snafu)]
#[snafu(context(suffix(Error)))]
pub(crate) enum Error {
pub enum Error {
#[snafu(display("invalid signature hex: `{signature}`"))]
Hex {
signature: String,
Expand All @@ -17,7 +17,7 @@ pub(crate) enum Error {
}

#[derive(Clone, DeserializeFromStr, PartialEq, SerializeDisplay)]
pub(crate) struct Signature(ed25519_dalek::Signature);
pub struct Signature(ed25519_dalek::Signature);

impl Signature {
const LEN: usize = ed25519_dalek::Signature::BYTE_SIZE;
Expand Down
2 changes: 1 addition & 1 deletion src/signature_error.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::*;

pub(crate) struct SignatureError(pub(crate) ed25519_dalek::SignatureError);
pub struct SignatureError(pub(crate) ed25519_dalek::SignatureError);

impl Display for SignatureError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Expand Down
2 changes: 1 addition & 1 deletion src/subcommand.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use {
super::*,
clap::builder::{
styling::{AnsiColor, Effects},
Styles,
styling::{AnsiColor, Effects},
},
};

Expand Down
20 changes: 10 additions & 10 deletions src/subcommand/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,19 +47,19 @@ impl Verify {

let fingerprint = manifest.fingerprint();

if let Some(expected) = self.fingerprint {
if fingerprint != expected {
let style = Style::stderr();
eprintln!(
"\
if let Some(expected) = self.fingerprint
&& fingerprint != expected
{
let style = Style::stderr();
eprintln!(
"\
fingerprint mismatch: `{source}`
expected: {}
actual: {}",
expected.style(style.good()),
fingerprint.style(style.bad()),
);
return Err(error::FingerprintMismatch.build());
}
expected.style(style.good()),
fingerprint.style(style.bad()),
);
return Err(error::FingerprintMismatch.build());
}

let bar = progress_bar::new(
Expand Down
16 changes: 6 additions & 10 deletions tests/create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -589,24 +589,20 @@ fn sign_creates_valid_signature() {
.assert()
.success();

let manifest = Manifest::load(&dir.child("foo/filepack.json"));
let (_path, manifest) = Manifest::load(Some(dir.child("foo/filepack.json").utf8_path())).unwrap();

let public_key = load_key(&dir.child("keys/master.public"));
let public_key = load_key(&dir.child("keys/master.public"))
.parse::<PublicKey>()
.unwrap();

assert_eq!(manifest.signatures.len(), 1);

let signature = manifest.signatures[&public_key]
.parse::<ed25519_dalek::Signature>()
.unwrap();

let public_key =
ed25519_dalek::VerifyingKey::from_bytes(&hex::decode(public_key).unwrap().try_into().unwrap())
.unwrap();
let signature = manifest.signatures[&public_key].clone();

let fingerprint = blake3::hash(r#"{"files":{"bar":{"hash":"af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262","size":0}}}"#.as_bytes());

public_key
.verify_strict(fingerprint.as_bytes(), &signature)
.verify(fingerprint.as_bytes(), &signature)
.unwrap();

Command::cargo_bin("filepack")
Expand Down
38 changes: 15 additions & 23 deletions tests/lib.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,26 @@
use {
assert_cmd::Command,
assert_fs::{
assert::PathAssert,
fixture::{FileTouch, FileWriteBin, FileWriteStr, PathChild, PathCreateDir},
TempDir,
assert::PathAssert,
fixture::{ChildPath, FileTouch, FileWriteBin, FileWriteStr, PathChild, PathCreateDir},
},
filepack::Entry,
camino::Utf8Path,
filepack::{Manifest, PublicKey, Signature},
predicates::str::RegexPredicate,
serde::{Deserialize, Serialize},
std::{collections::BTreeMap, fs, path::Path, str},
std::{fs, path::Path, str},
};

trait ChildPathExt {
fn utf8_path(&self) -> &Utf8Path;
}

impl ChildPathExt for ChildPath {
fn utf8_path(&self) -> &Utf8Path {
self.path().try_into().unwrap()
}
}

fn path(message: &str) -> String {
message.replace('/', std::path::MAIN_SEPARATOR_STR)
}
Expand All @@ -26,24 +36,6 @@ fn load_key(path: &Path) -> String {
fs::read_to_string(path).unwrap().trim().into()
}

#[derive(Deserialize, Serialize)]
struct Manifest {
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
files: BTreeMap<String, Entry>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
signatures: BTreeMap<String, String>,
}

impl Manifest {
fn load(path: &Path) -> Self {
serde_json::from_str(&fs::read_to_string(path).unwrap()).unwrap()
}

fn save(&self, path: &Path) {
fs::write(path, serde_json::to_string(self).unwrap()).unwrap();
}
}

mod create;
mod fingerprint;
mod hash;
Expand Down
13 changes: 9 additions & 4 deletions tests/sign.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,14 +72,19 @@ fn existing_signatures_must_be_valid() {
.assert()
.success();

let mut manifest = Manifest::load(&dir.child("foo/filepack.json"));
let (_path, mut manifest) =
Manifest::load(Some(dir.child("foo/filepack.json").utf8_path())).unwrap();

manifest.signatures.insert(
"7f1420cdc898f9370fd196b9e8e5606a7992fab5144fc1873d91b8c65ef5db6b".into(),
"0".repeat(128),
"7f1420cdc898f9370fd196b9e8e5606a7992fab5144fc1873d91b8c65ef5db6b"
.parse::<PublicKey>()
.unwrap(),
"0".repeat(128).parse::<Signature>().unwrap(),
);

manifest.save(&dir.child("foo/filepack.json"));
manifest
.save(dir.child("foo/filepack.json").utf8_path())
.unwrap();

Command::cargo_bin("filepack")
.unwrap()
Expand Down
Loading