Skip to content

Commit 879f997

Browse files
authored
Merge branch 'main' into restore-key
2 parents 7b238bd + 02499fb commit 879f997

4 files changed

Lines changed: 88 additions & 36 deletions

File tree

.github/workflows/ci.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ jobs:
8787
toolchain: 1.81.0
8888
override: true
8989
- name: Cache dependencies
90-
uses: actions/cache@v2
90+
uses: actions/cache@v4
9191
env:
9292
cache-name: cache-dependencies
9393
with:

bpb/src/config.rs

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -61,26 +61,23 @@ struct PublicKey {
6161
timestamp: u64,
6262
}
6363

64-
pub fn keys_file() -> std::path::PathBuf {
65-
// for archaic reasons we first check the config path
66-
// however this is an error, we shouldn’t store this as config seeing as it is
67-
// tied to the private key which is likely a host setting and should not thus be
68-
// synced between machines
64+
fn keys_file() -> std::path::PathBuf {
65+
// for archaic reasons we first check the config path
66+
// however this is an error, we shouldn’t store this as config seeing as it is
67+
// tied to the private key which is likely a host setting and should not thus be
68+
// synced between machines
6969

70-
let config_path = if let Ok(config_home) = std::env::var("XDG_CONFIG_HOME") {
71-
std::path::PathBuf::from(config_home).join("pkgx/bpb.toml")
72-
} else {
73-
std::path::PathBuf::from(std::env::var("HOME").unwrap()).join(".config/pkgx/bpb.toml")
74-
};
70+
let config_path = if let Ok(config_home) = std::env::var("XDG_CONFIG_HOME") {
71+
std::path::PathBuf::from(config_home).join("pkgx/bpb.toml")
72+
} else {
73+
std::path::PathBuf::from(std::env::var("HOME").unwrap()).join(".config/pkgx/bpb.toml")
74+
};
7575

76-
if config_path.exists() {
77-
config_path
78-
} else {
79-
let data_path = if let Ok(data_home) = std::env::var("XDG_DATA_HOME") {
80-
std::path::PathBuf::from(data_home).join("pkgx/bpb.toml")
81-
} else {
82-
std::path::PathBuf::from(std::env::var("HOME").unwrap()).join(".local/share/pkgx/bpb.toml")
83-
};
84-
data_path
85-
}
76+
if config_path.exists() {
77+
config_path
78+
} else if let Ok(data_home) = std::env::var("XDG_DATA_HOME") {
79+
std::path::PathBuf::from(data_home).join("pkgx/bpb.toml")
80+
} else {
81+
std::path::PathBuf::from(std::env::var("HOME").unwrap()).join(".local/share/pkgx/bpb.toml")
82+
}
8683
}

bpb/src/keychain.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,7 @@ pub fn add_keychain_item(service: &str, account: &str, secret: &str) -> Result<(
8181
Ok(())
8282
} else {
8383
Err(failure::err_msg(format!(
84-
"SecItemAdd failed with status: {}",
85-
status
84+
"SecItemAdd failed with status: {status}"
8685
)))
8786
}
8887
}
@@ -155,8 +154,7 @@ pub fn get_keychain_item(service: &str, account: &str) -> Result<String, Error>
155154
Ok(secret)
156155
} else {
157156
Err(failure::err_msg(format!(
158-
"SecItemCopyMatching failed with status: {}",
159-
status
157+
"SecItemCopyMatching failed with status: {status}"
160158
)))
161159
}
162160
}

bpb/src/main.rs

Lines changed: 68 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,15 @@ fn main() -> Result<(), Error> {
7070
bail!("Must specify a 64-character private key and a user ID, e.g.: `bpb restore [-f] YOUR_PRIVATE_KEY \"Name <email@example.com>\" [TIMESTAMP]`")
7171
}
7272
},
73+
Some("fingerprint") => print_fingerprint(),
74+
Some("key-id") => print_key_id(),
75+
Some("sign-hex") => {
76+
if let Some(hex) = args.next() {
77+
sign_from_hex(hex)
78+
} else {
79+
bail!("Must specify a hex string to sign, e.g.: `bpb sign-hex 1234abcd`")
80+
}
81+
}
7382
Some("--help") => print_help_message(),
7483
Some(arg) if gpg_sign_arg(arg) => verify_commit(),
7584
_ => {
@@ -91,7 +100,11 @@ fn print_help_message() -> Result<(), Error> {
91100
println!("A program for signing git commits.\n");
92101
println!("Arguments:");
93102
println!(" init <userid>: Generate a keypair and store in the keychain.");
103+
println!(" import <key>: Import a key from the command line.");
94104
println!(" print: Print public key in OpenPGP format.");
105+
println!(" fingerprint: Print the fingerprint of the public key.");
106+
println!(" key-id: Print the key ID of the public key.");
107+
println!(" sign-hex <hex>: Sign a hex string and print the signature and public key.\n");
95108
println!(" timestamp: Print the timestamp of the current key.");
96109
println!(" restore [-f] <key> <userid> [timestamp]: Restore a key from a 64-character private key.\n The -f flag will override any existing key.\n The timestamp is optional and will be used to generate the same public key format.");
97110
println!("See https://github.com/pkgxdev/bpb for more information.");
@@ -131,38 +144,74 @@ fn generate_keypair(userid: String) -> Result<(), Error> {
131144
Ok(())
132145
}
133146

134-
fn print_public_key() -> Result<(), Error> {
147+
// Does most of the initial setup
148+
// used for quite a few of the subcommands
149+
//
150+
// - Loads the config
151+
// - Gets the keypair from the keychain
152+
fn get_keypair() -> Result<KeyData, Error> {
135153
let config = Config::load()?;
136154
let service = config.service();
137155
let account = config.user_id();
138156
let secret_str = get_keychain_item(service, account)?;
139157
let secret = to_32_bytes(&secret_str)?;
140158

141-
let keypair = KeyData::load(&config, secret)?;
159+
KeyData::load(&config, secret)
160+
}
161+
162+
fn print_public_key() -> Result<(), Error> {
163+
let keypair = get_keypair()?;
142164
println!("{}", keypair.public());
143165
Ok(())
144166
}
145167

168+
fn get_fingerprint() -> Result<pbp::Fingerprint, Error> {
169+
let keypair = get_keypair()?;
170+
Ok(keypair.fingerprint())
171+
}
172+
173+
// Prints the fingerprint (sha256 hash of the public key -- 20 bytes)
174+
fn print_fingerprint() -> Result<(), Error> {
175+
println!("{}", pretty_print_hex_string(&get_fingerprint()?));
176+
Ok(())
177+
}
178+
179+
// Prints the long key ID (the last 8 bytes of the fingerprint)
180+
fn print_key_id() -> Result<(), Error> {
181+
println!("{}", pretty_print_hex_string(&get_fingerprint()?[12..]));
182+
Ok(())
183+
}
184+
146185
fn verify_commit() -> Result<(), Error> {
147186
use std::io::Read;
148187

149188
let mut commit = String::new();
150189
let mut stdin = std::io::stdin();
151190
stdin.read_to_string(&mut commit)?;
152191

153-
let config = Config::load()?;
154-
let service = config.service();
155-
let account = config.user_id();
156-
let secret_str = get_keychain_item(service, account)?;
157-
let secret = to_32_bytes(&secret_str)?;
158-
159-
let config = Config::load()?;
160-
let keypair = KeyData::load(&config, secret)?;
192+
let keypair = get_keypair()?;
161193

162194
let sig = keypair.sign(commit.as_bytes())?;
163195

164196
eprintln!("\n[GNUPG:] SIG_CREATED ");
165-
println!("{}", sig);
197+
println!("{sig}");
198+
Ok(())
199+
}
200+
201+
// Signs a hex string and prints the signature
202+
fn sign_from_hex(hex: String) -> Result<(), Error> {
203+
let keypair = get_keypair()?;
204+
// remove any leading 0x prefix
205+
let hex = hex.trim().to_lowercase();
206+
let hex = hex.trim_start_matches("0x");
207+
let data = hex::decode(hex)?;
208+
209+
let signed = keypair.sign(&data)?;
210+
let signature = hex::encode(signed.as_bytes());
211+
212+
let public_key = hex::encode_upper(keypair.public().as_bytes());
213+
println!("signature:\n\n{signature}\n");
214+
println!("public key:\n\n{public_key}\n");
166215
Ok(())
167216
}
168217

@@ -320,3 +369,11 @@ fn print_timestamp() -> Result<(), Error> {
320369

321370
Ok(())
322371
}
372+
373+
// iterates over a hex array and prints space-separated groups of four characters
374+
fn pretty_print_hex_string(hex: &[u8]) -> String {
375+
hex.chunks(2)
376+
.map(hex::encode_upper)
377+
.collect::<Vec<String>>()
378+
.join(" ")
379+
}

0 commit comments

Comments
 (0)