From a669d3147712eecc129acaf101f469c472382ded Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Sun, 18 Jan 2026 19:43:39 -0800 Subject: [PATCH 1/7] Use bech32m for package fingerprints --- src/bech32m.rs | 1 + src/fingerprint.rs | 11 ++++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/bech32m.rs b/src/bech32m.rs index 6ca5c816..4c8ce97f 100644 --- a/src/bech32m.rs +++ b/src/bech32m.rs @@ -75,6 +75,7 @@ mod tests { assert_eq!(T::TYPE, ty); } + case::<{ Fingerprint::LEN }, Fingerprint>("package", "package fingerprint"); case::<{ PrivateKey::LEN }, PrivateKey>("private", "private key"); case::<{ PublicKey::LEN }, PublicKey>("public", "public key"); case::<{ Signature::LEN }, Signature>("signature", "signature"); diff --git a/src/fingerprint.rs b/src/fingerprint.rs index 727fc155..d50e67fc 100644 --- a/src/fingerprint.rs +++ b/src/fingerprint.rs @@ -12,17 +12,22 @@ impl Fingerprint { } } +impl Bech32m<{ Fingerprint::LEN }> for Fingerprint { + const HRP: Hrp = Hrp::parse_unchecked("package"); + const TYPE: &'static str = "package fingerprint"; +} + impl Display for Fingerprint { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - self.0.fmt(f) + Self::encode_bech32m(f, *self.as_bytes()) } } impl FromStr for Fingerprint { - type Err = HashError; + type Err = Bech32mError; fn from_str(s: &str) -> Result { - Ok(Self(s.parse()?)) + Ok(Self(Self::decode_bech32m(s)?.into())) } } From ee764a12e57d80dcbe4429e04c5e7d2523ac50f7 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Sun, 18 Jan 2026 19:50:42 -0800 Subject: [PATCH 2/7] Update tests --- src/fingerprint.rs | 13 ------------- src/signature.rs | 2 +- src/test.rs | 5 ++++- tests/fingerprint.rs | 2 +- tests/verify.rs | 8 ++++---- 5 files changed, 10 insertions(+), 20 deletions(-) diff --git a/src/fingerprint.rs b/src/fingerprint.rs index d50e67fc..cd690dd1 100644 --- a/src/fingerprint.rs +++ b/src/fingerprint.rs @@ -30,16 +30,3 @@ impl FromStr for Fingerprint { Ok(Self(Self::decode_bech32m(s)?.into())) } } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn uppercase_is_forbidden() { - test::HASH - .to_uppercase() - .parse::() - .unwrap_err(); - } -} diff --git a/src/signature.rs b/src/signature.rs index b5a49638..69c73984 100644 --- a/src/signature.rs +++ b/src/signature.rs @@ -51,7 +51,7 @@ mod tests { #[test] fn parse() { - let signature = PrivateKey::generate().sign(Digest(test::HASH.parse().unwrap())); + let signature = PrivateKey::generate().sign(Digest(test::FINGERPRINT.parse().unwrap())); assert_eq!( signature.to_string().parse::().unwrap(), signature diff --git a/src/test.rs b/src/test.rs index 8a487117..25c770e2 100644 --- a/src/test.rs +++ b/src/test.rs @@ -2,6 +2,9 @@ use super::*; pub(crate) const HASH: &str = "af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262"; +pub(crate) const FINGERPRINT: &str = + "package14uf5nw04lxs6dgzqfh4rdhxffxdukfwf4hq39d7vn2fu4eqlxf3q33muym"; + pub(crate) const PRIVATE_KEY: &str = "private124p4zsr2nh04f4pkgtxfzv5yle473x4jue7s6lkwg9tdkk73q59qz34d70"; @@ -49,6 +52,6 @@ fn signature_is_valid() { #[test] fn signature_matches() { let private_key = PRIVATE_KEY.parse::().unwrap(); - let signature = private_key.sign(Digest(HASH.parse().unwrap())); + let signature = private_key.sign(Digest(FINGERPRINT.parse().unwrap())); assert_eq!(signature.to_string(), SIGNATURE); } diff --git a/tests/fingerprint.rs b/tests/fingerprint.rs index ccf73394..a404097b 100644 --- a/tests/fingerprint.rs +++ b/tests/fingerprint.rs @@ -19,7 +19,7 @@ fn fingerprint() { ) .success(); - let fingerprint = "864e5111ebe431702448d7d7c3f9b962d5659f761fb4287049d52d6376a4c20e"; + let fingerprint = "package1se89zy0tuschqfzg6ltu87devt2kt8mkr76zsuzf65kkxa4ycg8qpej6n9"; let path = test.path(); diff --git a/tests/verify.rs b/tests/verify.rs index ce45e8f8..746f9dcc 100644 --- a/tests/verify.rs +++ b/tests/verify.rs @@ -609,20 +609,20 @@ fn verify_fingerprint() { .args([ "verify", "--fingerprint", - "864e5111ebe431702448d7d7c3f9b962d5659f761fb4287049d52d6376a4c20e", + "package1se89zy0tuschqfzg6ltu87devt2kt8mkr76zsuzf65kkxa4ycg8qpej6n9", ]) .stderr("successfully verified 1 file totaling 0 bytes\n") .success() .args([ "verify", "--fingerprint", - "0000000000000000000000000000000000000000000000000000000000000000", + "package14uf5nw04lxs6dgzqfh4rdhxffxdukfwf4hq39d7vn2fu4eqlxf3q33muym", ]) .stderr_regex( "\ fingerprint mismatch: `.*filepack\\.json` - expected: 0000000000000000000000000000000000000000000000000000000000000000 - actual: 864e5111ebe431702448d7d7c3f9b962d5659f761fb4287049d52d6376a4c20e + expected: package14uf5nw04lxs6dgzqfh4rdhxffxdukfwf4hq39d7vn2fu4eqlxf3q33muym + actual: package1se89zy0tuschqfzg6ltu87devt2kt8mkr76zsuzf65kkxa4ycg8qpej6n9 error: fingerprint mismatch\n", ) .failure(); From c87573a21c178e7c7cbd4de9c5891dc2775bc8e3 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Sun, 18 Jan 2026 19:52:01 -0800 Subject: [PATCH 3/7] Update design. --- DESIGN.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/DESIGN.md b/DESIGN.md index 9f214cbd..3faaddc7 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -12,13 +12,15 @@ Open Questions since deriving a key with an explicit context seems like good practice, but `ed25519_dalek` doesn't support it. -- *Should Bech32m be used for fingerprints, public keys, and private keys?* - This would make them distinct and easy to identify, and give private keys - names like `private1…` which suggests that they shouldn't be exposed. - Closed Questions ---------------- +- *Should Bech32m be used for fingerprints, public keys, private keys, and + signatures?* This would make them distinct and easy to identify, and give + private keys names like `private1…` which suggests that they shouldn't be + exposed. **Conclusion: Use bech32m everything. Having easy-to-distinguish + keys, fingerprints, and signatures is a huge benefit.** + - *Should signatures be included in the manifest or in a subdirectory?* Currently, signatures are stored in the manifest in an object under the `signatures` key. This has pros and cons. The major pro is one only needs to From bbc615e0e7244178c14fe78abad96a52ead7dd73 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Sun, 18 Jan 2026 19:56:38 -0800 Subject: [PATCH 4/7] Adapt --- README.md | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index f4810278..1bbabc6b 100644 --- a/README.md +++ b/README.md @@ -174,9 +174,7 @@ such as `C:`. The value of the mandatory `notes` key is an array of signed notes. Notes are objects containing a single mandatory key `signatures`, an object mapping -public keys to signatures. Public keys and signatures are -[bech32m]([bech32m](https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki))-encoded -strings with `public1…` and `signature1…` prefixes, respectively. +public keys to signatures. Notes may optionally contain a `time` field whose value is a timestamp given as the number of nanoseconds after the UNIX epoch. @@ -218,6 +216,16 @@ signed by the public key The signature is elided for brevity. +Keys, Signatures, Fingerprints, and Hashes +------------------------------------------ + +Public keys, private keys, signatures, and package fingerprints are all +[bech32m](https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki)-encoded +strings beginning with `public1…`, `private1…`, `signature1…`, and `package1…` +respectively. + +BLAKE3 file hashes are 64-character lowercase hexidecimal. + Metadata -------- @@ -383,10 +391,6 @@ filepack keygen Which creates `master.public` and `master.private` files in the filepack `keychain` directory. -Public keys, private keys, and signatures are -[bech32m](https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki)-encoded -strings beginning with `public1…`, `private1…`, and `signature1…` respectively. - The `keychain` directory is located in the filepack data directory whose location is platform-dependent: From 084ada0454a60b138d00a0784ca7cbc2eaea75a5 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Sun, 18 Jan 2026 19:58:16 -0800 Subject: [PATCH 5/7] Adapt --- src/test.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test.rs b/src/test.rs index 25c770e2..be54c0bf 100644 --- a/src/test.rs +++ b/src/test.rs @@ -1,10 +1,10 @@ use super::*; -pub(crate) const HASH: &str = "af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262"; - pub(crate) const FINGERPRINT: &str = "package14uf5nw04lxs6dgzqfh4rdhxffxdukfwf4hq39d7vn2fu4eqlxf3q33muym"; +pub(crate) const HASH: &str = "af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262"; + pub(crate) const PRIVATE_KEY: &str = "private124p4zsr2nh04f4pkgtxfzv5yle473x4jue7s6lkwg9tdkk73q59qz34d70"; From 4c775b9eef156a543af0b12b85a93776d492b895 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Sun, 18 Jan 2026 20:00:08 -0800 Subject: [PATCH 6/7] Enhance --- DESIGN.md | 3 ++- README.md | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/DESIGN.md b/DESIGN.md index 3faaddc7..c98fec58 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -19,7 +19,8 @@ Closed Questions signatures?* This would make them distinct and easy to identify, and give private keys names like `private1…` which suggests that they shouldn't be exposed. **Conclusion: Use bech32m everything. Having easy-to-distinguish - keys, fingerprints, and signatures is a huge benefit.** + keys, fingerprints, and signatures is a huge benefit. BLAKE3 file hashes are + standard hexidecimal.** - *Should signatures be included in the manifest or in a subdirectory?* Currently, signatures are stored in the manifest in an object under the diff --git a/README.md b/README.md index 1bbabc6b..a7032a50 100644 --- a/README.md +++ b/README.md @@ -224,7 +224,7 @@ Public keys, private keys, signatures, and package fingerprints are all strings beginning with `public1…`, `private1…`, `signature1…`, and `package1…` respectively. -BLAKE3 file hashes are 64-character lowercase hexidecimal. +BLAKE3 file hashes are 64-character lowercase hexadecimal. Metadata -------- From 98eae75ac8e52b37fbc42cc2fb36da36c644c6a7 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Sun, 18 Jan 2026 20:00:20 -0800 Subject: [PATCH 7/7] Adjust --- DESIGN.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DESIGN.md b/DESIGN.md index c98fec58..54d11472 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -20,7 +20,7 @@ Closed Questions private keys names like `private1…` which suggests that they shouldn't be exposed. **Conclusion: Use bech32m everything. Having easy-to-distinguish keys, fingerprints, and signatures is a huge benefit. BLAKE3 file hashes are - standard hexidecimal.** + standard hexadecimal.** - *Should signatures be included in the manifest or in a subdirectory?* Currently, signatures are stored in the manifest in an object under the