Skip to content

Commit

Permalink
Split public/private key storage
Browse files Browse the repository at this point in the history
# Conflicts:
#	bpb-pkgx-cli/src/legacy_config.rs
  • Loading branch information
mxcl committed Nov 26, 2024
1 parent 3be5874 commit 34b3c47
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 90 deletions.
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ do.

### TODO

- [ ] Move keychain identifiers out to build variables in `config.rs`
- [ ] Move keychain identifier out to a build variable in `keychain.rs`
- [ ] Move keychain identifiers out to build variables in `main.rs`

## How to Install

Expand Down
68 changes: 16 additions & 52 deletions bpb-pkgx-cli/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,53 +1,37 @@
use std::io::Read;
use std::io::{Read, Write};

use failure::Error;

use crate::key_data::KeyData;

use crate::keychain::{add_keychain_item, get_keychain_item};

const KEYCHAIN_SERVICE: &str = "xyz.tea.BASE.bpb";
const KEYCHAIN_ACCOUNT: &str = "example_account";

#[derive(Serialize, Deserialize)]
pub struct Config {
public: PublicKey,
secret: SecretKey,
}

impl Config {
pub fn create(key_data: &KeyData) -> Result<Config, Error> {
let keypair = key_data.keypair();
let userid = key_data.user_id().to_owned();
let timestamp = key_data.timestamp();
pub fn create(public_key: String, user_id: String, timestamp: u64) -> Result<Config, Error> {
let userid = user_id.to_owned();
let key = public_key;
Ok(Config {
public: PublicKey {
key: hex::encode(keypair.verifying_key().as_bytes()),
key,
userid,
timestamp,
},
secret: SecretKey {
key: Some(hex::encode(keypair.as_bytes())),
program: None,
},
})
}

pub fn legacy_load(file: &mut impl Read) -> Result<Config, Error> {
pub fn load() -> Result<Config, Error> {
let mut file = std::fs::File::open(keys_file())?;
let mut buf = vec![];
file.read_to_end(&mut buf)?;
Ok(toml::from_slice(&buf)?)
}

pub fn load() -> Result<Config, Error> {
let str = get_keychain_item(KEYCHAIN_SERVICE, KEYCHAIN_ACCOUNT)?;
Ok(toml::from_str::<Config>(&str)?)
}

pub fn write(&self) -> Result<(), Error> {
let secret = toml::to_string(self)?;
// let account = self.user_id();
add_keychain_item(KEYCHAIN_SERVICE, KEYCHAIN_ACCOUNT, &secret)
let path = keys_file();
std::fs::create_dir_all(path.parent().unwrap())?;
let mut file = std::fs::File::create(path)?;
Ok(file.write_all(&toml::to_vec(self)?)?)
}

pub fn timestamp(&self) -> u64 {
Expand All @@ -57,10 +41,6 @@ impl Config {
pub fn user_id(&self) -> &str {
&self.public.userid
}

pub fn secret(&self) -> Result<[u8; 32], Error> {
self.secret.secret()
}
}

#[derive(Serialize, Deserialize)]
Expand All @@ -70,26 +50,10 @@ struct PublicKey {
timestamp: u64,
}

#[derive(Serialize, Deserialize)]
struct SecretKey {
key: Option<String>,
program: Option<String>,
}

impl SecretKey {
fn secret(&self) -> Result<[u8; 32], Error> {
if let Some(key) = &self.key {
to_32_bytes(key)
} else {
bail!("No secret key or program specified")
}
fn keys_file() -> std::path::PathBuf {
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")
}
}

fn to_32_bytes(slice: &String) -> Result<[u8; 32], Error> {
let vector = hex::decode(slice)?;
let mut array = [0u8; 32];
let len = std::cmp::min(vector.len(), 32);
array[..len].copy_from_slice(&vector[..len]);
Ok(array)
}
18 changes: 3 additions & 15 deletions bpb-pkgx-cli/src/key_data.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::time::SystemTime;

use ed25519_dalek as ed25519;
use ed25519_dalek::{self as ed25519};
use failure::Error;

use crate::config::Config;
Expand All @@ -20,8 +20,8 @@ impl KeyData {
}
}

pub fn load(config: &Config) -> Result<KeyData, Error> {
let keypair = ed25519::SigningKey::from_bytes(&config.secret()?);
pub fn load(config: &Config, secret: [u8; 32]) -> Result<KeyData, Error> {
let keypair = ed25519::SigningKey::from_bytes(&secret);
Ok(KeyData::create(
keypair,
config.user_id().to_owned(),
Expand All @@ -42,18 +42,6 @@ impl KeyData {
))
}

pub fn keypair(&self) -> &ed25519::SigningKey {
&self.keypair
}

pub fn timestamp(&self) -> u64 {
self.timestamp
}

pub fn user_id(&self) -> &str {
&self.user_id
}

pub fn fingerprint(&self) -> pbp_pkgx::Fingerprint {
self.public().fingerprint()
}
Expand Down
59 changes: 59 additions & 0 deletions bpb-pkgx-cli/src/legacy_config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
use failure::Error;
use std::io::Read;

use crate::config::Config;

#[derive(Serialize, Deserialize)]
pub struct LegacyConfig {
public: PublicKey,
secret: SecretKey,
}

impl LegacyConfig {
pub fn convert(file: &mut impl Read) -> Result<(Config, [u8; 32]), Error> {
let mut buf = vec![];
file.read_to_end(&mut buf)?;
let config: LegacyConfig = toml::from_slice(&buf)?;
Ok((
Config::create(
config.public.key,
config.public.userid,
config.public.timestamp,
)?,
config.secret.secret()?,
))
}
}

#[derive(Serialize, Deserialize)]
struct PublicKey {
pub key: String,
pub userid: String,
pub timestamp: u64,
}

#[derive(Serialize, Deserialize)]
struct SecretKey {
key: Option<String>,
program: Option<String>,
}

impl SecretKey {
pub fn secret(&self) -> Result<[u8; 32], Error> {
if let Some(key) = &self.key {
to_32_bytes(key)
} else if let Some(_program) = &self.program {
bail!("unsupported program configuration")
} else {
bail!("no secret key found")
}
}
}

fn to_32_bytes(slice: &String) -> Result<[u8; 32], Error> {
let vector = hex::decode(slice)?;
let mut array = [0u8; 32];
let len = std::cmp::min(vector.len(), 32);
array[..len].copy_from_slice(&vector[..len]);
Ok(array)
}
70 changes: 49 additions & 21 deletions bpb-pkgx-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,19 @@ extern crate serde_derive;
mod config;
mod key_data;
mod keychain;
mod legacy_config;
mod tests;

use std::fs;
use std::time::SystemTime;

use ed25519_dalek as ed25519;
use failure::Error;
use keychain::{add_keychain_item, get_keychain_item};
use rand::RngCore;

use crate::config::Config;
use crate::key_data::KeyData;
use crate::legacy_config::LegacyConfig;

fn main() -> Result<(), Error> {
let mut args = std::env::args().skip(1);
Expand All @@ -43,46 +45,55 @@ fn main() -> Result<(), Error> {
}

fn gpg_sign_arg(arg: &str) -> bool {
arg == "--sign" || (arg.starts_with("-") && !arg.starts_with("--") && arg.contains("s"))
arg == "--sign" || (arg.starts_with('-') && !arg.starts_with("--") && arg.contains('s'))
}

fn print_help_message() -> Result<(), Error> {
println!("bpb: boats's personal barricade\n");
println!("This is a program for signing your git commits.\n");
println!("A program for signing git commits.\n");
println!("Arguments:");
println!(" init <userid>: (Re)initialize bpb, generate a new keypair.");
println!(" print: Print the current bpb public key, in OpenPGP format.\n");
println!("See https://github.com/withoutboats/bpb for more information.");
println!(" init <userid>: Generate a keypair and store in the keychain.");
println!(" print: Print public key in OpenPGP format.\n");
println!("See https://github.com/pkgxdev/bpb for more information.");
Ok(())
}

fn generate_keypair(userid: String) -> Result<(), Error> {
let keys_file = keys_file();
if std::fs::metadata(&keys_file).is_ok() {
if let Ok(_config) = Config::load() {
eprintln!(
"A bpb_keys.toml already exists. If you want to reinitialize your state\n\
delete the file at `{}` first",
keys_file
"A keypair already exists. If you (really) want to reinitialize your state\n\
run `security delete-generic-password -s xyz.tea.BASE.bpb` first."
);
return Ok(());
}

let timestamp = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)?
.as_secs();

let mut rng = [0u8; 32];
rand::rngs::OsRng.fill_bytes(&mut rng[0..32]);
let keypair = ed25519::SigningKey::from_bytes(&rng);
let key_data = KeyData::create(keypair, userid, timestamp);
let config = Config::create(&key_data)?;

let public_key = hex::encode(keypair.verifying_key().as_bytes());
let config = Config::create(public_key, userid, timestamp)?;
config.write()?;
println!("{}", key_data.public());

let service = "xyz.tea.BASE.bpb";
let account = config.user_id();
let hex = hex::encode(keypair.to_bytes());
add_keychain_item(service, account, &hex)?;

let keydata = KeyData::load(&config, keypair.to_bytes())?;
println!("{}", keydata.public());

Ok(())
}

fn print_public_key() -> Result<(), Error> {
let config = Config::load()?;
let keypair = KeyData::load(&config)?;
let secret = [0u8; 32];
let keypair = KeyData::load(&config, secret)?;
println!("{}", keypair.public());
Ok(())
}
Expand All @@ -95,7 +106,13 @@ fn verify_commit() -> Result<(), Error> {
stdin.read_to_string(&mut commit)?;

let config = Config::load()?;
let keypair = KeyData::load(&config)?;
let service = "xyz.tea.BASE.bpb";
let account = config.user_id();
let secret_str = get_keychain_item(service, account)?;
let secret = to_32_bytes(&secret_str)?;

let config = Config::load()?;
let keypair = KeyData::load(&config, secret)?;

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

Expand All @@ -114,13 +131,24 @@ fn delegate() -> ! {
}

fn upgrade() -> Result<(), Error> {
let mut file = fs::File::open(keys_file())?;
let config = Config::legacy_load(&mut file)?;
config.write()?;
fs::remove_file(keys_file()).map_err(|e| failure::err_msg(e.to_string()))
let mut file = std::fs::File::open(legacy_keys_file())?;
let (config, secret) = LegacyConfig::convert(&mut file)?;
let service = "xyz.tea.BASE.bpb";
let account = config.user_id();
let hex = hex::encode(secret);
add_keychain_item(service, account, &hex)?;
config.write()
}

fn keys_file() -> String {
fn legacy_keys_file() -> String {
std::env::var("BPB_KEYS")
.unwrap_or_else(|_| format!("{}/.bpb_keys.toml", std::env::var("HOME").unwrap()))
}

fn to_32_bytes(slice: &String) -> Result<[u8; 32], Error> {
let vector = hex::decode(slice)?;
let mut array = [0u8; 32];
let len = std::cmp::min(vector.len(), 32);
array[..len].copy_from_slice(&vector[..len]);
Ok(array)
}

0 comments on commit 34b3c47

Please sign in to comment.