diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 0b90ab1..c1c28e8 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -87,7 +87,7 @@ jobs: toolchain: 1.81.0 override: true - name: Cache dependencies - uses: actions/cache@v2 + uses: actions/cache@v4 env: cache-name: cache-dependencies with: diff --git a/bpb/src/config.rs b/bpb/src/config.rs index 046c849..b2c8912 100644 --- a/bpb/src/config.rs +++ b/bpb/src/config.rs @@ -62,25 +62,22 @@ struct PublicKey { } fn keys_file() -> std::path::PathBuf { - // for archaic reasons we first check the config path - // however this is an error, we shouldn’t store this as config seeing as it is - // tied to the private key which is likely a host setting and should not thus be - // synced between machines + // for archaic reasons we first check the config path + // however this is an error, we shouldn’t store this as config seeing as it is + // tied to the private key which is likely a host setting and should not thus be + // synced between machines - let config_path = if let Ok(config_home) = std::env::var("XDG_CONFIG_HOME") { - std::path::PathBuf::from(config_home).join("pkgx/bpb.toml") - } else { - std::path::PathBuf::from(std::env::var("HOME").unwrap()).join(".config/pkgx/bpb.toml") - }; + let config_path = if let Ok(config_home) = std::env::var("XDG_CONFIG_HOME") { + std::path::PathBuf::from(config_home).join("pkgx/bpb.toml") + } else { + std::path::PathBuf::from(std::env::var("HOME").unwrap()).join(".config/pkgx/bpb.toml") + }; - if config_path.exists() { - config_path - } else { - let data_path = if let Ok(data_home) = std::env::var("XDG_DATA_HOME") { - std::path::PathBuf::from(data_home).join("pkgx/bpb.toml") - } else { - std::path::PathBuf::from(std::env::var("HOME").unwrap()).join(".local/share/pkgx/bpb.toml") - }; - data_path - } + if config_path.exists() { + config_path + } else if let Ok(data_home) = std::env::var("XDG_DATA_HOME") { + std::path::PathBuf::from(data_home).join("pkgx/bpb.toml") + } else { + std::path::PathBuf::from(std::env::var("HOME").unwrap()).join(".local/share/pkgx/bpb.toml") + } } diff --git a/bpb/src/keychain.rs b/bpb/src/keychain.rs index 31727c2..9a778a9 100644 --- a/bpb/src/keychain.rs +++ b/bpb/src/keychain.rs @@ -81,8 +81,7 @@ pub fn add_keychain_item(service: &str, account: &str, secret: &str) -> Result<( Ok(()) } else { Err(failure::err_msg(format!( - "SecItemAdd failed with status: {}", - status + "SecItemAdd failed with status: {status}" ))) } } @@ -155,8 +154,7 @@ pub fn get_keychain_item(service: &str, account: &str) -> Result Ok(secret) } else { Err(failure::err_msg(format!( - "SecItemCopyMatching failed with status: {}", - status + "SecItemCopyMatching failed with status: {status}" ))) } } diff --git a/bpb/src/main.rs b/bpb/src/main.rs index 2b26f86..b89b17d 100644 --- a/bpb/src/main.rs +++ b/bpb/src/main.rs @@ -33,6 +33,8 @@ fn main() -> Result<(), Error> { Some("import") => import(), Some("upgrade") => upgrade(), Some("print") => print_public_key(), + Some("fingerprint") => print_fingerprint(), + Some("key-id") => print_key_id(), Some("--help") => print_help_message(), Some(arg) if gpg_sign_arg(arg) => verify_commit(), _ => { @@ -54,7 +56,10 @@ fn print_help_message() -> Result<(), Error> { println!("A program for signing git commits.\n"); println!("Arguments:"); println!(" init : Generate a keypair and store in the keychain."); - println!(" print: Print public key in OpenPGP format.\n"); + println!(" import : Import a key from the command line."); + println!(" print: Print public key in OpenPGP format."); + println!(" fingerprint: Print the fingerprint of the public key."); + println!(" key-id: Print the key ID of the public key.\n"); println!("See https://github.com/pkgxdev/bpb for more information."); Ok(()) } @@ -104,6 +109,29 @@ fn print_public_key() -> Result<(), Error> { Ok(()) } +fn get_fingerprint() -> Result { + let config = Config::load()?; + let service = config.service(); + let account = config.user_id(); + let secret_str = get_keychain_item(service, account)?; + let secret = to_32_bytes(&secret_str)?; + + let keypair = KeyData::load(&config, secret)?; + Ok(keypair.fingerprint()) +} + +// Prints the fingerprint (sha256 hash of the public key -- 20 bytes) +fn print_fingerprint() -> Result<(), Error> { + println!("{}", pretty_print_hex_string(&get_fingerprint()?)); + Ok(()) +} + +// Prints the long key ID (the last 8 bytes of the fingerprint) +fn print_key_id() -> Result<(), Error> { + println!("{}", pretty_print_hex_string(&get_fingerprint()?[12..])); + Ok(()) +} + fn verify_commit() -> Result<(), Error> { use std::io::Read; @@ -123,7 +151,7 @@ fn verify_commit() -> Result<(), Error> { let sig = keypair.sign(commit.as_bytes())?; eprintln!("\n[GNUPG:] SIG_CREATED "); - println!("{}", sig); + println!("{sig}"); Ok(()) } @@ -167,3 +195,11 @@ fn to_32_bytes(slice: &String) -> Result<[u8; 32], Error> { array[..len].copy_from_slice(&vector[..len]); Ok(array) } + +// iterates over a hex array and prints space-separated groups of four characters +fn pretty_print_hex_string(hex: &[u8]) -> String { + hex.chunks(2) + .map(hex::encode_upper) + .collect::>() + .join(" ") +}