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
10 changes: 7 additions & 3 deletions src/directory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,13 @@ impl Directory {
pub(crate) fn fingerprint(&self) -> Hash {
let mut hasher = FingerprintHasher::new(FingerprintPrefix::Directory);

for (component, entry) in &self.entries {
hasher.field(0, entry.fingerprint(component).as_bytes());
}
let entries = self
.entries
.iter()
.flat_map(|(component, entry)| *entry.fingerprint(component).as_bytes())
.collect::<Vec<u8>>();

hasher.field(0, &entries);

hasher.finalize()
}
Expand Down
71 changes: 60 additions & 11 deletions src/fingerprint_hasher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ use super::*;

pub(crate) struct FingerprintHasher {
hasher: Hasher,
tag: u64,
next: u64,
}

impl FingerprintHasher {
pub(crate) fn field(&mut self, tag: u64, field: &[u8]) {
assert!(tag >= self.tag, "unexpected tag {tag}");
self.tag = tag;
self.hasher.update(&tag.to_le_bytes());
self.hasher.update(&field.len().into_u64().to_le_bytes());
assert!(tag == self.next, "unexpected tag {tag}");
self.next += 1;
self.varint(tag);
self.varint(field.len().into_u64());
self.hasher.update(field);
}

Expand All @@ -19,11 +19,26 @@ impl FingerprintHasher {
}

pub(crate) fn new(context: FingerprintPrefix) -> Self {
let mut hasher = Hasher::new();
let mut hasher = Self {
hasher: Hasher::new(),
next: 0,
};
let prefix = context.prefix();
hasher.update(&prefix.len().into_u64().to_le_bytes());
hasher.update(prefix.as_bytes());
Self { hasher, tag: 0 }
hasher.varint(prefix.len().into_u64());
hasher.hasher.update(prefix.as_bytes());
hasher
}

fn varint(&mut self, mut n: u64) {
loop {
let byte = (n & 0b0111_1111).try_into().unwrap();
n >>= 7;
if n == 0 {
self.hasher.update(&[byte]);
break;
}
self.hasher.update(&[byte | 0b1000_0000]);
}
}
}

Expand All @@ -50,10 +65,44 @@ mod tests {
}

#[test]
#[should_panic(expected = "unexpected tag 0")]
#[should_panic(expected = "unexpected tag 2")]
fn tag_order() {
let mut hasher = FingerprintHasher::new(FingerprintPrefix::File);
hasher.field(1, &[]);
hasher.field(0, &[]);
hasher.field(2, &[]);
}

#[test]
fn varint_encoding() {
#[track_caller]
fn case(len: usize, varint: &[u8]) {
let field = iter::repeat_n(0, len).collect::<Vec<u8>>();

let actual = {
let mut hasher = FingerprintHasher::new(FingerprintPrefix::File);
hasher.field(0, &field);
hasher.finalize()
};

let expected = {
let mut hasher = Hasher::new();
hasher.update(&[13]);
hasher.update("filepack:file".as_bytes());
hasher.update(&[0]);
hasher.update(varint);
hasher.update(&field);
hasher.finalize().into()
};

assert_eq!(actual, expected, "unexpected hash for length {len} field");
}

case(0, &[0]);
case(1, &[1]);
case(127, &[0x7F]);
case(128, &[0x80, 0x01]);
case(129, &[0x81, 0x01]);
case(16383, &[0xFF, 0x7F]);
case(16384, &[0x80, 0x80, 0x01]);
}
}
4 changes: 2 additions & 2 deletions src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ pub(crate) const PUBLIC_KEY: &str =

pub(crate) const SIGNATURE: &str = concat!(
"signature1a67dndhhmae7p6fsfnj0z37zf78cde6mwqgtms0y87h8ldlvvflyq4uf5nw04lxs6dgzqf",
"h4rdhxffxdukfwf4hq39d7vn2fu4eqlxf3q5j9kf2jslrnmfptk2rsj85tnp4ttqwagu46kkw64uf3dg",
"ffz3juhjnh9us86m2xzugrgxhn87kcn6azkernfruce7qh4mhzfefycuqq7t9j5l"
"h4rdhxffxdukfwf4hq39d7vn2fu4eqlxf3qsvczv268s6lkxtsc0eufqc5xz3g99640gtpwadk349d8f",
"qkjgl3tkp2m95ujz9arxzwt74ggzd3f9vnc6skcns9kn6xnxuqz6v26yrgw3lv6u",
);

pub(crate) const WEAK_PUBLIC_KEY: &str =
Expand Down
2 changes: 1 addition & 1 deletion tests/fingerprint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ fn fingerprint() {
)
.success();

let fingerprint = "package1ase89zy0tuschqfzg6ltu87devt2kt8mkr76zsuzf65kkxa4ycg8q0kds50";
let fingerprint = "package1a6mpecnnzja3uzmdxruf87074wy778qra3yn25xuudzgjx49v3tsq9qx6vs";

let path = test.path();

Expand Down
4 changes: 2 additions & 2 deletions tests/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -663,7 +663,7 @@ fn verify_fingerprint() {
.args([
"verify",
"--fingerprint",
"package1ase89zy0tuschqfzg6ltu87devt2kt8mkr76zsuzf65kkxa4ycg8q0kds50",
"package1a6mpecnnzja3uzmdxruf87074wy778qra3yn25xuudzgjx49v3tsq9qx6vs",
])
.stderr("successfully verified 1 file totaling 0 bytes\n")
.success()
Expand All @@ -676,7 +676,7 @@ fn verify_fingerprint() {
"\
fingerprint mismatch: `.*filepack\\.json`
expected: package1a4uf5nw04lxs6dgzqfh4rdhxffxdukfwf4hq39d7vn2fu4eqlxf3ql7ykr3
actual: package1ase89zy0tuschqfzg6ltu87devt2kt8mkr76zsuzf65kkxa4ycg8q0kds50
actual: package1a6mpecnnzja3uzmdxruf87074wy778qra3yn25xuudzgjx49v3tsq9qx6vs
error: fingerprint mismatch\n",
)
.failure();
Expand Down