From 7678dc62fb50d3ccd7085962b1f9d872679aa26a Mon Sep 17 00:00:00 2001 From: Markus Rudy Date: Sat, 2 Nov 2024 23:43:56 +0100 Subject: [PATCH] fixup! wip: rust cli testable subcommands --- rust/src/cli/bin.rs | 55 ++++++++++++++++++++++++++++++++------------- 1 file changed, 39 insertions(+), 16 deletions(-) diff --git a/rust/src/cli/bin.rs b/rust/src/cli/bin.rs index 39c143e..0dcbafc 100644 --- a/rust/src/cli/bin.rs +++ b/rust/src/cli/bin.rs @@ -2,7 +2,7 @@ use base64::{engine::general_purpose, Engine as _}; use clap::{Args, Parser, Subcommand}; use std::convert::TryInto; use std::fs; -use std::io::{self, Read, Write}; +use std::io; use std::path::PathBuf; use x25519_dalek::{PublicKey, StaticSecret}; @@ -47,17 +47,17 @@ enum Glome { Login(LoginArgs), } -fn genkey() -> io::Result<()> { - io::stdout().write_all(StaticSecret::random().as_bytes()) +fn genkey(stdout: &mut dyn io::Write) -> io::Result<()> { + stdout.write_all(StaticSecret::random().as_bytes()) } -fn pubkey() -> io::Result<()> { +fn pubkey(stdin: &mut dyn io::Read, stdout: &mut dyn io::Write) -> io::Result<()> { let mut buf: [u8; 32] = [0; 32]; - io::stdin().read_exact(&mut buf)?; + stdin.read_exact(&mut buf)?; let sk: StaticSecret = buf.into(); let pk: PublicKey = (&sk).into(); - write!(io::stdout(), "glome-v1 {}\n", general_purpose::URL_SAFE.encode(pk.as_bytes())) + write!(stdout, "glome-v1 {}\n", general_purpose::URL_SAFE.encode(pk.as_bytes())) } fn read_key(path: &PathBuf) -> [u8; 32] { @@ -79,21 +79,21 @@ fn read_pub(path: &PathBuf) -> [u8; 32] { *raw } -fn gentag(args: &TagArgs) -> io::Result<()> { +fn gentag(args: &TagArgs, stdin: &mut dyn io::Read, stdout: &mut dyn io::Write) -> io::Result<()> { let ours: StaticSecret = read_key(&args.key).into(); let theirs: PublicKey = read_pub(&args.peer).into(); let ctr = args.counter.unwrap_or_default(); let mut msg = Vec::new(); - io::stdin().read_to_end(&mut msg)?; + stdin.read_to_end(&mut msg)?; let t = glome::tag(&ours, &theirs, ctr, &msg); let encoded = general_purpose::URL_SAFE.encode(&t); - io::stdout().write_all(encoded.as_bytes()) + stdout.write_all(encoded.as_bytes()) } -fn login(args: &LoginArgs) -> io::Result<()> { +fn login(args: &LoginArgs, stdout: &mut dyn io::Write) -> io::Result<()> { let ours: StaticSecret = read_key(&args.key).into(); let challenge_start = args @@ -122,15 +122,38 @@ fn login(args: &LoginArgs) -> io::Result<()> { let encoded = general_purpose::URL_SAFE.encode(&t); - io::stdout().write_all(encoded.as_bytes()) + stdout.write_all(encoded.as_bytes()) } fn main() -> io::Result<()> { - // TODO(burgerdev): pass an output buffer to make implementations testable. match &Cli::parse().command { - Glome::Genkey => genkey(), - Glome::Pubkey => pubkey(), - Glome::Tag(tag_args) => gentag(tag_args), - Glome::Login(login_args) => login(login_args), + Glome::Genkey => genkey(&mut io::stdout()), + Glome::Pubkey => pubkey(&mut io::stdin(), &mut io::stdout()), + Glome::Tag(tag_args) => gentag(tag_args, &mut io::stdin(), &mut io::stdout()), + Glome::Login(login_args) => login(login_args, &mut io::stdout()), } } + +#[cfg(test)] +mod tests { + use hex_literal::hex; + + use super::*; + + #[test] + fn test_genkey() { + let mut stdout = io::Cursor::new(Vec::new()); + genkey(&mut stdout).unwrap(); + assert_eq!(32, stdout.get_ref().len()) + } + + #[test] + fn test_pubkey() { + // Login test vector 1 - Alice's key. + let mut stdin = io::Cursor::new(hex!("77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a")); + let mut stdout = io::Cursor::new(Vec::new()); + pubkey(&mut stdin, &mut stdout).unwrap(); + let expected = "glome-v1 hSDwCYkwp1R0i33ctD73Wg2_Og0mOBr066SpjqqbTmo=\n"; + assert_eq!(expected, std::str::from_utf8(stdout.get_ref().as_slice()).unwrap()) + } +} \ No newline at end of file