breakmancer 0.4.0

Drop a breakpoint into any shell.
use std::{
    fs::File,
    io::{prelude::Write, BufReader},
};

use alkali::{asymmetric::kx::x25519blake2b as X25519, mem::FullAccess};
use serde::{Deserialize, Serialize};

use crate::protocol::{SetupSecret, SETUP_SECRET_LENGTH};

pub struct DebugKeys {
    pub controller_keypair: X25519::Keypair,
    pub breakpoint_keypair: X25519::Keypair,
    pub setup_secret: SetupSecret,
}

#[derive(Debug, Serialize, Deserialize)]
struct DebugKeysFile {
    controller_private: X25519::PrivateKey<FullAccess>,
    controller_public: X25519::PublicKey,
    breakpoint_private: X25519::PrivateKey<FullAccess>,
    breakpoint_public: X25519::PublicKey,
    setup_secret: [u8; SETUP_SECRET_LENGTH],
}

fn read_debug_keys(path: &str) -> Option<DebugKeys> {
    let fd = File::open(path).ok()?;
    let reader = BufReader::new(fd);
    let raw: DebugKeysFile = serde_cbor::from_reader(reader).ok()?;
    Some(DebugKeys {
        controller_keypair: X25519::Keypair {
            public_key: raw.controller_public,
            private_key: raw.controller_private,
        },
        breakpoint_keypair: X25519::Keypair {
            public_key: raw.breakpoint_public,
            private_key: raw.breakpoint_private,
        },
        setup_secret: SetupSecret::from_bytes(&raw.setup_secret),
    })
}

fn create_debug_keys(path: &str) -> Result<DebugKeys, ()> {
    let controller_keypair = X25519::Keypair::generate().map_err(|_| ())?;
    let breakpoint_keypair = X25519::Keypair::generate().map_err(|_| ())?;
    let setup_secret = SetupSecret::new_random();

    let for_file = serde_cbor::to_vec(&DebugKeysFile {
        controller_private: controller_keypair.private_key.try_clone().map_err(|_| ())?,
        controller_public: controller_keypair.public_key,
        breakpoint_private: breakpoint_keypair.private_key.try_clone().map_err(|_| ())?,
        breakpoint_public: breakpoint_keypair.public_key,
        setup_secret: setup_secret.to_owned(),
    })
    .map_err(|_| ())?;
    let mut fd = File::create(path).map_err(|_| ())?;
    fd.write_all(&for_file).map_err(|_| ())?;

    Ok(DebugKeys {
        controller_keypair,
        breakpoint_keypair,
        setup_secret,
    })
}

// This is intended only for manual testing.
//
// To use this feature, set environment variable `BM_USE_DEBUG_KEYS`
// to a place where breakmancer can write a set of debugging keys to
// file. If controller and breakpoint are on the same machine, they can
// share keys without requiring the developer to copy public keys and
// shared secrets around.
pub(crate) fn get_debug_keys() -> Option<DebugKeys> {
    let path = std::env::var("BM_USE_DEBUG_KEYS").ok()?;
    match read_debug_keys(&path) {
        Some(keys) => {
            eprintln!("[WARNING] Using debug keys from disk.");
            Some(keys)
        }
        None => match create_debug_keys(&path) {
            Ok(keys) => {
                eprintln!("[WARNING] Using new debug keys (and saving to disk.)");
                Some(keys)
            }
            Err(err) => {
                eprintln!("[WARNING] Could not read or create new debug keys: {err:?}");
                None
            }
        },
    }
}