cbradio 0.1.0

System orchestration based on Redis
Documentation
// SPDX-FileCopyrightText: 2021 Jakub Pastuszek <jpastuszek@protonmail.com>
//
// SPDX-License-Identifier: GPL-3.0-or-later
use cotton::prelude::*;

use cbradio::crypto::{
    generate_encryption_key, generate_signing_key,
    encode,
    default_base_staion_public_key_file, default_base_staion_secret_key_file,
    default_network_public_key_file, default_network_secret_key_file,
    SignPublicKey,
};
use cbradio::with_create_dir;

#[derive(Debug, StructOpt)]
enum KeyPair {
    /// Generate base station key pair
    BaseStation,
    /// Generate network key pair
    Network,
}

/// Generate key pairs for cbradio communication
#[derive(Debug, StructOpt)]
struct Cli {
    #[structopt(flatten)]
    logging: LoggingOpt,

    /// File path where to store the public key
    #[structopt(long, short = "k")]
    public_key: Option<PathBuf>,

    /// File path where to store the secret key
    #[structopt(long, short = "K")]
    secret_key: Option<PathBuf>,

    #[structopt(subcommand)]
    key_pair: KeyPair,
}

fn write_key_pair(public_key_file: &Path, secret_key_file: &Path, generate_key_pair: impl Fn() -> ([u8; 32], [u8; 32])) -> PResult<()> {
    if public_key_file.exists() {
        problem!("Public key file already exists")?;
    }
    if secret_key_file.exists() {
        problem!("Secret key file already exists")?;
    }

    let (public_key, secret_key)  = generate_key_pair();

    write(public_key_file, encode(&public_key))?;
    println!("Written public key: {}", public_key_file.display());

    with_umask(0o077, || write(secret_key_file, encode(&secret_key)))?;
    println!("Written public key: {}", secret_key_file.display());

    Ok(())
}

fn main() -> FinalResult {
    let args = Cli::from_args();
    init_logger(&args.logging, vec![module_path!()]);

    match args.key_pair {
        KeyPair::BaseStation => {
            let public_key_file = args.public_key.map(Ok).unwrap_or_else(|| with_create_dir(default_base_staion_public_key_file()))?;
            let secret_key_file = args.secret_key.map(Ok).unwrap_or_else(|| with_create_dir(default_base_staion_secret_key_file()))?;

            write_key_pair(&public_key_file, &secret_key_file, || {
                let key = generate_signing_key();
                let pub_key: SignPublicKey = (&key).into();
                (pub_key.to_bytes(), key.to_bytes())
            })?;
        },
        KeyPair::Network => {
            let public_key_file = args.public_key.map(Ok).unwrap_or_else(|| with_create_dir(default_network_public_key_file()))?;
            let secret_key_file = args.secret_key.map(Ok).unwrap_or_else(|| with_create_dir(default_network_secret_key_file()))?;

            write_key_pair(&public_key_file, &secret_key_file, || {
                let key = generate_encryption_key();
                let pub_key = key.public_key();
                (*pub_key.as_bytes(), key.to_bytes())
            })?;
        },
    };

    Ok(())
}