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
11 changes: 7 additions & 4 deletions DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,16 @@ 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. BLAKE3 file hashes are
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
`signatures` key. This has pros and cons. The major pro is one only needs to
Expand Down
18 changes: 11 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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 hexadecimal.

Metadata
--------

Expand Down Expand Up @@ -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:

Expand Down
1 change: 1 addition & 0 deletions src/bech32m.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
24 changes: 8 additions & 16 deletions src/fingerprint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,29 +12,21 @@ 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<Self, Self::Err> {
Ok(Self(s.parse()?))
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn uppercase_is_forbidden() {
test::HASH
.to_uppercase()
.parse::<Fingerprint>()
.unwrap_err();
Ok(Self(Self::decode_bech32m(s)?.into()))
}
}
2 changes: 1 addition & 1 deletion src/signature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<Signature>().unwrap(),
signature
Expand Down
5 changes: 4 additions & 1 deletion src/test.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use super::*;

pub(crate) const FINGERPRINT: &str =
"package14uf5nw04lxs6dgzqfh4rdhxffxdukfwf4hq39d7vn2fu4eqlxf3q33muym";

pub(crate) const HASH: &str = "af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262";

pub(crate) const PRIVATE_KEY: &str =
Expand Down Expand Up @@ -49,6 +52,6 @@ fn signature_is_valid() {
#[test]
fn signature_matches() {
let private_key = PRIVATE_KEY.parse::<PrivateKey>().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);
}
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 = "864e5111ebe431702448d7d7c3f9b962d5659f761fb4287049d52d6376a4c20e";
let fingerprint = "package1se89zy0tuschqfzg6ltu87devt2kt8mkr76zsuzf65kkxa4ycg8qpej6n9";

let path = test.path();

Expand Down
8 changes: 4 additions & 4 deletions tests/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down