#![allow(dead_code)]
use std::{
fmt::Write,
io::{self, BufRead, Write as _},
};
mod receiver;
mod sender;
use clap::{Parser, ValueEnum};
use receiver::{Receiver, ReceiverOptions};
use sender::{Sender, SenderOptions};
use sframe::{
CipherSuite,
header::SframeHeader,
ratchet::{RatchetingBaseKey, RatchetingKeyId},
};
fn main() {
let Args {
cipher_suite,
key_id,
log_level,
max_counter,
secret,
n_ratchet_bits,
} = Args::parse();
println!("- Using cipher suite {cipher_suite:?}, key id {key_id}, secret {secret}");
if let Some(log_level) = log_level {
println!("- Using log level {log_level}");
simple_logger::init_with_level(log_level).unwrap();
}
let cipher_suite = cipher_suite.into();
let (mut base_key, key_id) = if let Some(n_ratchet_bits) = n_ratchet_bits {
println!("- Using {n_ratchet_bits} bits for the ratcheting step");
let r = RatchetingKeyId::new(key_id, n_ratchet_bits);
let base_key =
RatchetingBaseKey::ratchet_forward(r, secret.as_bytes(), cipher_suite).unwrap();
(Some(base_key), r.into())
} else {
(None, key_id)
};
let sender_options = SenderOptions {
key_id,
cipher_suite,
max_counter,
};
let mut sender = Sender::from(sender_options);
sender.set_encryption_key(&secret).unwrap();
let receiver_options = ReceiverOptions {
cipher_suite,
frame_validation: None,
n_ratchet_bits,
};
let mut receiver = Receiver::from(receiver_options);
receiver.set_encryption_key(key_id, &secret).unwrap();
let print_before_input = || {
println!("--------------------------------------------------------------------------");
println!("- Enter a phrase to be encrypted, confirm with [ENTER], abort with [CTRL+C]");
print!("- To be encrypted: ");
std::io::stdout().flush().unwrap();
};
print_before_input();
let stdin = io::stdin();
let lines = stdin
.lock()
.lines()
.take_while(Result::is_ok)
.map(Result::unwrap);
lines.for_each(|line| {
if n_ratchet_bits.is_some() {
let base_key = base_key.as_mut().unwrap();
let (new_key_id, key_material) = base_key.next_base_key().unwrap();
println!(
"- Ratcheting sender key, ratcheting step: {}",
new_key_id.ratchet_step()
);
sender
.ratchet_encryption_key(new_key_id, &key_material)
.unwrap();
}
println!("- Encrypting {}", bin2string(line.as_bytes()));
let encrypted = sender.encrypt(line, 0).unwrap();
display_encrypted(encrypted);
let decrypted = receiver.decrypt(encrypted, 0).unwrap();
println!("- Decrypted {}", bin2string(decrypted));
print_before_input();
});
}
fn display_encrypted(encrypted: &[u8]) {
let header = SframeHeader::deserialize(encrypted).unwrap();
println!("- Sframe Header: {header}");
let header_len = header.len();
let payload = bin2string(&encrypted[header_len..]);
println!("- Encrypted Payload: {payload}")
}
fn bin2string(bin: &[u8]) -> String {
bin.iter().fold(String::new(), |mut output, x| {
let _ = write!(output, "{x:08b} ");
output
})
}
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {
#[arg(value_enum, short, long, default_value_t = ArgCipherSuiteVariant::AesGcm128Sha256)]
cipher_suite: ArgCipherSuiteVariant,
#[arg(short, long, default_value_t = 3)]
key_id: u64,
#[arg(short, long)]
log_level: Option<log::Level>,
#[arg(short, long, default_value_t = u64::MAX)]
max_counter: u64,
#[arg(short, long, default_value = "SUPER_SECRET")]
secret: String,
#[arg(short, long)]
n_ratchet_bits: Option<u8>,
}
#[derive(ValueEnum, Clone, Copy, Debug)]
pub enum ArgCipherSuiteVariant {
#[cfg(any(feature = "openssl", feature = "rust-crypto"))]
AesCtr128HmacSha256_80,
#[cfg(any(feature = "openssl", feature = "rust-crypto"))]
AesCtr128HmacSha256_64,
#[cfg(any(feature = "openssl", feature = "rust-crypto"))]
AesCtr128HmacSha256_32,
AesGcm128Sha256,
AesGcm256Sha512,
}
impl From<ArgCipherSuiteVariant> for CipherSuite {
fn from(val: ArgCipherSuiteVariant) -> Self {
match val {
#[cfg(any(feature = "openssl", feature = "rust-crypto"))]
ArgCipherSuiteVariant::AesCtr128HmacSha256_80 => CipherSuite::AesCtr128HmacSha256_80,
#[cfg(any(feature = "openssl", feature = "rust-crypto"))]
ArgCipherSuiteVariant::AesCtr128HmacSha256_64 => CipherSuite::AesCtr128HmacSha256_64,
#[cfg(any(feature = "openssl", feature = "rust-crypto"))]
ArgCipherSuiteVariant::AesCtr128HmacSha256_32 => CipherSuite::AesCtr128HmacSha256_32,
ArgCipherSuiteVariant::AesGcm128Sha256 => CipherSuite::AesGcm128Sha256,
ArgCipherSuiteVariant::AesGcm256Sha512 => CipherSuite::AesGcm256Sha512,
}
}
}