@@ -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+
146185fn 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