use std::{
mem,
sync::{
atomic::{AtomicBool, Ordering},
Arc,
},
};
use alkali::{
asymmetric::kx::x25519blake2b as X25519,
hash::generic::blake2b,
mem::{hardened_buffer, FullAccess},
symmetric::cipher_stream::{self, DecryptionStream, EncryptionStream},
};
use rand::Fill;
use serde::{Deserialize, Serialize};
use tokio::{
io::{AsyncReadExt, AsyncWriteExt},
net::{
tcp::{OwnedReadHalf, OwnedWriteHalf},
TcpListener, TcpStream,
},
sync::mpsc,
};
pub const SETUP_SECRET_LENGTH: usize = 16;
hardened_buffer! {
pub SetupSecretBuffer(SETUP_SECRET_LENGTH)
}
pub type SetupSecret = SetupSecretBuffer<FullAccess>;
impl SetupSecret {
pub fn new_random() -> SetupSecret {
let mut setup_secret = SetupSecret::new_empty().unwrap();
setup_secret.try_fill(&mut rand::thread_rng()).unwrap();
setup_secret
}
pub fn from_bytes(bytes: &[u8; SETUP_SECRET_LENGTH]) -> SetupSecret {
let mut secret = SetupSecret::new_empty().unwrap();
secret.copy_from_slice(bytes); secret
}
}
const VERSION: &str = "dev.2";
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct BreakpointHello {
pub protocol: String,
#[serde(with = "serde_bytes")]
pub breakpoint_pub_key: X25519::PublicKey,
#[serde(with = "serde_bytes")]
pub cipher_header: [u8; cipher_stream::HEADER_LENGTH],
#[serde(with = "serde_bytes")]
pub encrypted_intro: Vec<u8>,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct ListenerHello {
#[serde(with = "serde_bytes")]
pub cipher_header: [u8; cipher_stream::HEADER_LENGTH],
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct EncryptedMsg {
#[serde(with = "serde_bytes")]
pub encrypted: Vec<u8>,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
enum InsecureMsg {
BreakpointHello(BreakpointHello),
ListenerHello(ListenerHello),
EncryptedMsg(EncryptedMsg),
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct BreakpointIntro {
pub which: Option<String>,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct Command {
pub command: String,
pub seq: u32,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct OutputLine {
#[serde(with = "serde_bytes")]
pub bytes: Vec<u8>,
pub gender: OutputType,
pub seq: u32,
}
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
pub enum OutputType {
Stdout,
Stderr,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct Finished {
pub exit_code: Option<i32>,
pub seq: u32,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct TerminateCmd {
pub seq: u32,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct ExitBreak {}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum SecureMsg {
BreakpointIntro(BreakpointIntro),
Command(Command),
OutputLine(OutputLine),
Finished(Finished),
TerminateCmd(TerminateCmd),
ExitBreak(ExitBreak),
}
enum Party {
Listener,
Breakpoint,
}
const KDF_INPUT_LEN_BYTES: usize = 8;
fn length_prefixed_kdf_input(inputs: &Vec<&[u8]>) -> Vec<u8> {
let mut out = Vec::new();
for &input in inputs {
let length = input.len();
assert!(
u64::try_from(length).is_ok(),
"Length of KDF input was greater than allowed ({length} > 2^64 - 1)",
);
let len_prefix = &length.to_le_bytes()[..KDF_INPUT_LEN_BYTES];
out.extend(len_prefix);
out.extend(input);
}
out
}
fn make_stream_key(
stream_prekey: &[u8; X25519::SESSION_KEY_LENGTH],
pubkey_server: &X25519::PublicKey,
pubkey_client: &X25519::PublicKey,
setup_secret: &SetupSecret,
) -> cipher_stream::xchacha20poly1305::Key<FullAccess> {
let kdf_in_raw = length_prefixed_kdf_input(&vec![
"breakmancer.streamkey".as_bytes(),
VERSION.as_bytes(),
pubkey_server,
pubkey_client,
stream_prekey,
]);
let key_bytes = blake2b::hash_custom_to_vec(
&kdf_in_raw,
Some(setup_secret.as_slice()),
cipher_stream::xchacha20poly1305::KEY_LENGTH,
)
.unwrap();
cipher_stream::xchacha20poly1305::Key::<FullAccess>::try_from(key_bytes.as_slice()).unwrap()
}
fn derive_stream_keys(
my_role: &Party,
my_keypair: &X25519::Keypair,
other_public: &X25519::PublicKey,
setup_secret: &SetupSecret,
) -> (
cipher_stream::xchacha20poly1305::Key<FullAccess>,
cipher_stream::xchacha20poly1305::Key<FullAccess>,
) {
let prekeys = match my_role {
Party::Breakpoint => my_keypair.client_keys(other_public),
Party::Listener => my_keypair.server_keys(other_public),
};
let (tx_prekey, rx_prekey) = prekeys.unwrap();
let (pubkey_server, pubkey_client) = match my_role {
Party::Breakpoint => (other_public, &my_keypair.public_key),
Party::Listener => (&my_keypair.public_key, other_public),
};
let tx = make_stream_key(&tx_prekey, pubkey_server, pubkey_client, setup_secret);
let rx = make_stream_key(&rx_prekey, pubkey_server, pubkey_client, setup_secret);
(tx, rx)
}
fn serialize<D>(data: &D) -> Vec<u8>
where
D: Serialize,
{
serde_cbor::to_vec(&data).unwrap()
}
fn deserialize<'a, T>(bytes: &'a [u8]) -> Result<T, String>
where
T: Deserialize<'a>,
{
serde_cbor::from_slice(bytes).map_err(|err| format!("{err}"))
}
fn encrypt_message(msg: &SecureMsg, cipher_tx: &mut EncryptionStream) -> EncryptedMsg {
let msg_bytes = serialize(msg);
let msg_slice = msg_bytes.as_slice();
let mut ciphered = vec![0u8; msg_slice.len() + cipher_stream::OVERHEAD_LENGTH];
cipher_tx.encrypt(msg_slice, None, &mut ciphered).unwrap();
EncryptedMsg {
encrypted: ciphered,
}
}
fn decrypt_message(
msg: &EncryptedMsg,
cipher_rx: &mut DecryptionStream,
) -> Result<SecureMsg, String> {
let ciphered = msg.encrypted.as_slice();
let mut data = vec![0u8; ciphered.len() - cipher_stream::OVERHEAD_LENGTH];
cipher_rx
.decrypt(ciphered, None, &mut data)
.map_err(|err| format!("Unable to decrypt message: {err}"))?;
deserialize(&data).map_err(|err| format!("Could not parse message data: {err}"))
}
const FRAME_HEADER_SIZE: u32 = 4;
const FRAME_DATA_MAX_BYTES: usize = 2usize.pow(FRAME_HEADER_SIZE * 8);
async fn send_clear_frame<W>(stream: &mut W, clear_msg: &InsecureMsg) -> Result<(), String>
where
W: AsyncWriteExt + Unpin,
{
let data = serialize(clear_msg);
let data_len = data.len();
if data_len > FRAME_DATA_MAX_BYTES {
return Err(String::from(
"Cannot send frame of more than {data_len} bytes",
));
}
let len_bytes = &data_len.to_le_bytes()[..FRAME_HEADER_SIZE as usize];
let mut frame = Vec::<u8>::from(len_bytes);
frame.extend_from_slice(&data);
stream
.write_all(&frame)
.await
.map_err(|err| format!("Failed to send frame: {err}"))?;
Ok(())
}
async fn receive_clear_frame<R>(stream: &mut R) -> Result<InsecureMsg, String>
where
R: AsyncReadExt + Unpin,
{
let mut len_header = [0u8; mem::size_of::<usize>()];
stream
.read_exact(&mut len_header[..FRAME_HEADER_SIZE as usize])
.await
.map_err(|err| format!("Failed to receive frame length: {err}"))?;
let mut data = vec![0u8; usize::from_le_bytes(len_header)];
stream
.read_exact(&mut data)
.await
.map_err(|err| format!("Failed to receive frame body: {err}"))?;
deserialize(&data).map_err(|err| format!("Could not parse frame body: {err}"))
}
struct EncryptionStreamWrapper(EncryptionStream);
unsafe impl Send for EncryptionStreamWrapper {}
struct DecryptionStreamWrapper(DecryptionStream);
unsafe impl Send for DecryptionStreamWrapper {}
struct ConnectionTx {
tcp_write: OwnedWriteHalf,
cipher_tx: EncryptionStreamWrapper,
shutting_down: Arc<AtomicBool>,
}
impl ConnectionTx {
pub fn from(
tcp_write: OwnedWriteHalf,
cipher_tx: EncryptionStream,
shutting_down: Arc<AtomicBool>,
) -> ConnectionTx {
ConnectionTx {
tcp_write,
cipher_tx: EncryptionStreamWrapper(cipher_tx),
shutting_down,
}
}
fn log_error(&self, msg: &str) {
if !self.shutting_down.load(Ordering::SeqCst) {
eprintln!("[ERROR] {msg}");
}
}
async fn send_insecure(&mut self, msg: &InsecureMsg) -> Result<(), String> {
send_clear_frame(&mut self.tcp_write, msg).await
}
fn encrypt_message(&mut self, msg: &SecureMsg) -> EncryptedMsg {
encrypt_message(msg, &mut self.cipher_tx.0)
}
async fn send_secure(&mut self, msg: &SecureMsg) -> Result<(), String> {
let msg = self.encrypt_message(msg);
self.send_insecure(&InsecureMsg::EncryptedMsg(msg)).await
}
pub fn spawn_tx(mut self) -> mpsc::Sender<SecureMsg> {
let (send_producer, mut send_consumer) = mpsc::channel::<SecureMsg>(200);
tokio::spawn(async move {
loop {
let Some(msg) = send_consumer.recv().await else {
self.log_error(
"Internal error getting new message to send, shutting down connection.",
);
break;
};
if let Err(err) = self.send_secure(&msg).await {
self.log_error(&format!(
"Failed to send message to network, shutting down connection: {err}"
));
break;
}
if let SecureMsg::ExitBreak(_) = msg {
self.shutting_down.store(true, Ordering::SeqCst);
};
}
if let Err(err) = self.tcp_write.shutdown().await {
self.log_error(&format!("Couldn't do clean hangup: {err}"));
}
});
send_producer
}
}
struct ConnectionRx {
tcp_read: OwnedReadHalf,
cipher_rx: DecryptionStreamWrapper,
shutting_down: Arc<AtomicBool>,
}
impl ConnectionRx {
pub fn from(
tcp_read: OwnedReadHalf,
cipher_rx: DecryptionStream,
shutting_down: Arc<AtomicBool>,
) -> ConnectionRx {
ConnectionRx {
tcp_read,
cipher_rx: DecryptionStreamWrapper(cipher_rx),
shutting_down,
}
}
fn log_error(&self, msg: &str) {
if !self.shutting_down.load(Ordering::SeqCst) {
eprintln!("[ERROR] {msg}");
}
}
async fn receive_insecure(&mut self) -> Result<InsecureMsg, String> {
receive_clear_frame(&mut self.tcp_read).await
}
fn decrypt_message(&mut self, msg: &EncryptedMsg) -> Result<SecureMsg, String> {
decrypt_message(msg, &mut self.cipher_rx.0)
}
async fn receive_secure(&mut self) -> Result<SecureMsg, String> {
match self.receive_insecure().await? {
InsecureMsg::EncryptedMsg(enc) => self.decrypt_message(&enc),
clear_msg => Err(format!("Unexpected insecure message type: {clear_msg:?}")),
}
}
pub fn spawn_rx(mut self) -> mpsc::Receiver<SecureMsg> {
let (recv_producer, recv_consumer) = mpsc::channel::<SecureMsg>(200);
tokio::spawn(async move {
loop {
let msg = match self.receive_secure().await {
Ok(msg) => msg,
Err(err) => {
self.log_error(&format!(
"Couldn't receive message from network, shutting down connection: {err}"
));
break;
}
};
if let SecureMsg::ExitBreak(_) = msg {
self.shutting_down.store(true, Ordering::SeqCst);
};
if let Err(err) = recv_producer.send(msg).await {
self.log_error(&format!(
"Internal error passing a received message around; shutting down: {err}"
));
break;
}
}
});
recv_consumer
}
}
pub struct Connection {
pub rx: mpsc::Receiver<SecureMsg>,
pub tx: mpsc::Sender<SecureMsg>,
shutting_down: Arc<AtomicBool>,
}
impl Connection {
pub async fn new_listener(
server_socket: &TcpListener,
listener_keypair: &X25519::Keypair,
setup_secret: &SetupSecret,
) -> Result<(Connection, BreakpointIntro), String> {
let (stream, client_addr) = server_socket
.accept()
.await
.map_err(|err| format!("Couldn't get new connection: {err}"))?;
println!("Connection from {client_addr:?}");
let (mut tcp_read, mut tcp_write) = stream.into_split();
let breakpoint_hello = match receive_clear_frame(&mut tcp_read).await {
Ok(InsecureMsg::BreakpointHello(hello)) => Ok(hello),
Ok(_) => Err("Expected a breakpoint hello.".to_owned()),
Err(err) => Err(format!(
"Couldn't receive data when waiting for a breakpoint hello: {err}"
)
.to_owned()),
}?;
let (tx, rx) = derive_stream_keys(
&Party::Listener,
listener_keypair,
&breakpoint_hello.breakpoint_pub_key,
setup_secret,
);
let mut rx_stream = DecryptionStream::new(&rx, &breakpoint_hello.cipher_header).unwrap();
let maybe_intro = decrypt_message(
&EncryptedMsg {
encrypted: breakpoint_hello.encrypted_intro,
},
&mut rx_stream,
)
.map_err(|err| format!("Could not decrypt breakpoint introduction: {err}"))?;
let breakpoint_intro = match maybe_intro {
SecureMsg::BreakpointIntro(intro) => intro,
_ => Err("Unexpected message type for breakpoint intro.")?,
};
let tx_stream = EncryptionStream::new(&tx).unwrap();
let tx_header = tx_stream.get_header();
send_clear_frame(
&mut tcp_write,
&InsecureMsg::ListenerHello(ListenerHello {
cipher_header: tx_header,
}),
)
.await
.map_err(|err| format!("Couldn't send encrypted stream header: {err}"))?;
let shutting_down = Arc::new(AtomicBool::from(false));
Ok((
Connection {
rx: ConnectionRx::from(tcp_read, rx_stream, shutting_down.clone()).spawn_rx(),
tx: ConnectionTx::from(tcp_write, tx_stream, shutting_down.clone()).spawn_tx(),
shutting_down,
},
breakpoint_intro,
))
}
pub async fn new_breakpoint(
breakpoint_keypair: &X25519::Keypair,
listener_addr: &String,
listener_public: &X25519::PublicKey,
setup_secret: &SetupSecret,
which: &Option<String>,
) -> Result<Connection, String> {
let stream = TcpStream::connect(listener_addr)
.await
.map_err(|err| format!("Unable to connect to {listener_addr}: {err}"))?;
match stream.peer_addr() {
Ok(connected) => eprintln!("Connecting to listener at {connected}"),
Err(err) => eprintln!("Unable to get address of listener connection: {err}"),
};
let (mut tcp_read, mut tcp_write) = stream.into_split();
let (tx, rx) = derive_stream_keys(
&Party::Breakpoint,
breakpoint_keypair,
listener_public,
setup_secret,
);
let mut tx_stream = EncryptionStream::new(&tx).unwrap();
let tx_header = tx_stream.get_header();
let intro = SecureMsg::BreakpointIntro(BreakpointIntro {
which: which.clone(),
});
let hello = BreakpointHello {
protocol: VERSION.to_owned(),
breakpoint_pub_key: breakpoint_keypair.public_key,
cipher_header: tx_header,
encrypted_intro: encrypt_message(&intro, &mut tx_stream).encrypted,
};
send_clear_frame(&mut tcp_write, &InsecureMsg::BreakpointHello(hello))
.await
.map_err(|err| format!("Unable to send hello: {err}"))?;
let listener_hello = match receive_clear_frame(&mut tcp_read).await {
Ok(InsecureMsg::ListenerHello(hello)) => Ok(hello),
Ok(_) => Err("Expected a listener cipher header.".to_owned()),
Err(err) => Err(format!(
"Couldn't receive data when waiting for a listener cipher header: {err}"
)
.to_owned()),
}?;
let rx_stream = DecryptionStream::new(&rx, &listener_hello.cipher_header)
.map_err(|err| format!("Unable to start decryption stream: {err}"))?;
let shutting_down = Arc::new(AtomicBool::from(false));
Ok(Connection {
rx: ConnectionRx::from(tcp_read, rx_stream, shutting_down.clone()).spawn_rx(),
tx: ConnectionTx::from(tcp_write, tx_stream, shutting_down.clone()).spawn_tx(),
shutting_down,
})
}
}
impl Drop for Connection {
fn drop(&mut self) {
self.shutting_down.store(true, Ordering::SeqCst);
}
}
#[cfg(test)]
mod tests {
use crate::test_utils::{all_variant_mapping, partial_hexlify};
use super::*;
#[test]
fn kdf_inputs() {
assert_eq!(KDF_INPUT_LEN_BYTES * 8, 64);
}
#[tokio::test]
async fn network_frame() {
let mut written = Vec::<u8>::new();
send_clear_frame(
&mut written,
&InsecureMsg::BreakpointHello(BreakpointHello {
protocol: String::from("test"),
breakpoint_pub_key: [5u8; 32],
cipher_header: [1u8; 24],
encrypted_intro: [3u8; 6].to_vec(),
}),
)
.await
.unwrap();
let expected = [
b"\x94\x00\x00\x00" as &[u8],
b"\xA1", b"o", b"BreakpointHello",
b"\xA4", b"h", b"protocol",
b"d", b"test",
b"r", b"breakpoint_pub_key",
b"X", b"\x20", b"\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05",
b"m", b"cipher_header",
b"X\x18", b"\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01",
b"o", b"encrypted_intro",
b"F", b"\x03\x03\x03\x03\x03\x03",
].concat();
assert_eq!(
expected,
written,
"expected {} but got {}",
partial_hexlify(&expected),
partial_hexlify(&written)
);
}
#[tokio::test]
async fn insecure_message_format_pinning() {
let examples = all_variant_mapping!(
InsecureMsg => &[u8]:
InsecureMsg::BreakpointHello =
(
BreakpointHello {
protocol: String::from("someproto"),
breakpoint_pub_key: [6u8; 32],
cipher_header: [7u8; 24],
encrypted_intro: [3u8; 6].to_vec(),
}
)
=> b"\xA1oBreakpointHello\xA4hprotocolisomeprotorbreakpoint_pub_keyX\x20\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06mcipher_headerX\x18\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07oencrypted_intro\x46\x03\x03\x03\x03\x03\x03",
InsecureMsg::ListenerHello =
(
ListenerHello {
cipher_header: [8u8; 24],
}
)
=> b"\xA1mListenerHello\xA1mcipher_headerX\x18\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08",
InsecureMsg::EncryptedMsg =
(
EncryptedMsg {
encrypted: [9u8; 3].to_vec(),
}
)
=> b"\xA1lEncryptedMsg\xA1iencrypted\x43\x09\x09\x09",
);
for (variant_value, serialized) in examples {
assert_eq!(
serialized,
serialize(&variant_value),
"Serialized bytes did not match expected value for input {variant_value:?}"
);
let deserialized = deserialize(serialized)
.map_err(|err| format!("Could not deserialize: {err}; expected {variant_value:?}"))
.unwrap();
assert_eq!(
variant_value, deserialized,
"Deserialized value did not match expected value {variant_value:?}"
);
}
}
#[tokio::test]
async fn secure_message_format_pinning() {
let examples = all_variant_mapping!(
SecureMsg => &[u8]:
SecureMsg::BreakpointIntro =
(
BreakpointIntro {
which: Some("loop".to_owned()),
}
)
=> b"\xA1oBreakpointIntro\xA1ewhichdloop",
SecureMsg::Command =
(
Command {
command: "pwd".to_owned(),
seq: 5,
}
)
=> b"\xA1gCommand\xA2gcommandcpwdcseq\x05",
SecureMsg::OutputLine =
(
OutputLine {
bytes: b"hello world!".to_vec(),
gender: OutputType::Stdout,
seq: 7,
}
)
=> b"\xA1jOutputLine\xA3ebytes\x4Chello world!fgenderfStdoutcseq\x07",
SecureMsg::Finished =
(
Finished {
exit_code: Some(1),
seq: 9,
}
)
=> b"\xA1hFinished\xA2iexit_code\x01cseq\x09",
SecureMsg::TerminateCmd =
(
TerminateCmd { seq: 11 }
)
=> b"\xA1lTerminateCmd\xA1cseq\x0b",
SecureMsg::ExitBreak =
(ExitBreak {})
=> b"\xA1iExitBreak\xA0",
);
for (variant_value, serialized) in examples {
assert_eq!(
serialized,
serialize(&variant_value),
"Serialized bytes did not match expected value for input {variant_value:?}"
);
let deserialized = deserialize(serialized)
.map_err(|err| format!("Could not deserialize: {err}; expected {variant_value:?}"))
.unwrap();
assert_eq!(
variant_value, deserialized,
"Deserialized value did not match expected value {variant_value:?}"
);
}
}
}