diff --git a/rust/Cargo.lock b/rust/Cargo.lock index fc2995e..7140f9c 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -1,6 +1,79 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "anstream" +version = "0.6.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" + +[[package]] +name = "anstyle-parse" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "arraydeque" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "bitflags" @@ -29,6 +102,52 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "clap" +version = "4.5.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" + +[[package]] +name = "colorchoice" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" + [[package]] name = "cpufeatures" version = "0.2.12" @@ -85,6 +204,31 @@ dependencies = [ "subtle", ] +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "errno" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + [[package]] name = "fiat-crypto" version = "0.2.8" @@ -131,13 +275,48 @@ dependencies = [ name = "glome" version = "0.1.0" dependencies = [ + "base64", + "clap", + "hex", "hex-literal", "hmac", "openssl", "sha2", + "tempfile", "x25519-dalek", + "yaml-rust2", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashlink" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" +dependencies = [ + "hashbrown", ] +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "hex-literal" version = "0.3.4" @@ -153,11 +332,23 @@ dependencies = [ "digest", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "libc" -version = "0.2.154" +version = "0.2.168" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "once_cell" @@ -245,6 +436,19 @@ dependencies = [ "semver", ] +[[package]] +name = "rustix" +version = "0.38.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + [[package]] name = "semver" version = "1.0.22" @@ -282,6 +486,12 @@ dependencies = [ "digest", ] +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "subtle" version = "2.5.0" @@ -299,6 +509,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tempfile" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys", +] + [[package]] name = "typenum" version = "1.17.0" @@ -311,6 +534,12 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "vcpkg" version = "0.2.15" @@ -329,6 +558,79 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + [[package]] name = "x25519-dalek" version = "2.0.1" @@ -341,6 +643,37 @@ dependencies = [ "zeroize", ] +[[package]] +name = "yaml-rust2" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a1a1c0bc9823338a3bdf8c61f994f23ac004c6fa32c08cd152984499b445e8d" +dependencies = [ + "arraydeque", + "encoding_rs", + "hashlink", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "zeroize" version = "1.7.0" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 11988aa..d0ad61f 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -7,17 +7,34 @@ edition = "2021" default = [ "dalek" ] dalek = [ "dep:x25519-dalek" ] openssl = [ "dep:openssl" ] +cli = [ "dep:base64", "dep:clap" ] [dependencies] +# lib sha2 = "0.10" hmac = "0.12" x25519-dalek = { version = "2.0", features = ["getrandom", "static_secrets"], optional = true } openssl = { version = "0.10", optional = true } +# cli +base64 = { version = "0.21", optional = true } +clap = { version = "4", features = ["derive"], optional = true} + [dev-dependencies] # test +hex = "0.4" hex-literal = "0.3" +tempfile = "3.14.0" +yaml-rust2 = "0.9" [lib] name = "glome" path = "src/lib.rs" + +[[bin]] +name = "glome" +path = "src/cli/bin.rs" + +# The binary has more dependencies than the library. We allow skipping the binary and its +# dependencies by hiding it behind a feature. +required-features = ["cli"] diff --git a/rust/src/cli/bin.rs b/rust/src/cli/bin.rs new file mode 100644 index 0000000..c486134 --- /dev/null +++ b/rust/src/cli/bin.rs @@ -0,0 +1,317 @@ +use base64::{engine::general_purpose, Engine as _}; +use clap::{Args, Parser, Subcommand}; +use glome::PrivateKey; +use std::convert::TryInto; +use std::error::Error; +use std::fs; +use std::io; +use std::path::PathBuf; +use x25519_dalek::{PublicKey, StaticSecret}; + +#[derive(Parser)] +#[command(author, version, about, long_about = None)] +struct Cli { + #[command(subcommand)] + command: Glome, +} + +#[derive(Args)] +struct TagArgs { + /// Path to secret key + #[arg(short, long, value_name = "FILE")] + key: PathBuf, + /// Path to peer's public key + #[arg(short, long, value_name = "FILE")] + peer: PathBuf, + /// Message counter index + #[arg(short, long, value_name = "n")] + counter: Option, +} + +#[derive(Args)] +struct LoginArgs { + /// Path to secret key + #[arg(short, long, value_name = "FILE")] + key: PathBuf, + /// Challenge to generate a tag for + challenge: String, +} + +#[derive(Subcommand)] +enum Glome { + /// Generate a new secret key and print it to stdout + Genkey, + /// Read a private key from stdin and write its public key to stdout + Pubkey, + /// Tag a message read from stdin + Tag(TagArgs), + /// Generate a tag for a GLOME-Login challenge + Login(LoginArgs), +} + +type CommandResult = Result<(), Box>; + +fn genkey(stdout: &mut dyn io::Write) -> CommandResult { + Ok(stdout.write_all(StaticSecret::random().as_bytes())?) +} + +fn pubkey(stdin: &mut dyn io::Read, stdout: &mut dyn io::Write) -> CommandResult { + let mut buf: [u8; 32] = [0; 32]; + stdin.read_exact(&mut buf)?; + let sk: StaticSecret = buf.into(); + let pk: PublicKey = (&sk).into(); + + Ok(writeln!( + stdout, + "glome-v1 {}", + general_purpose::URL_SAFE.encode(pk.as_bytes()) + )?) +} + +fn read_key(path: &PathBuf) -> Result<[u8; 32], Box> { + let b: Box<[u8; 32]> = fs::read(path) + .map_err(|e| format!("reading file {:?}: {}", path, e))? + .into_boxed_slice() + .try_into() + .map_err(|_| "private key must have exactly 32 bytes")?; + Ok(*b) +} + +fn read_pub(path: &PathBuf) -> Result<[u8; 32], Box> { + let pubkey = fs::read_to_string(path).map_err(|e| format!("reading file {:?}: {}", path, e))?; + let b64 = match pubkey.strip_prefix("glome-v1 ") { + Some(tail) => tail.trim_end(), + None => return Err("unsupported public key version, expected 'glome-v1'".into()), + }; + let raw: Box<[u8; 32]> = general_purpose::URL_SAFE + .decode(b64) + .map_err(|e| format!("decoding public key: {}", e))? + .into_boxed_slice() + .try_into() + .map_err(|_| "public key must have exactly 32 bytes")?; + Ok(*raw) +} + +fn gentag(args: &TagArgs, stdin: &mut dyn io::Read, stdout: &mut dyn io::Write) -> CommandResult { + 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(); + stdin.read_to_end(&mut msg)?; + + let t = glome::tag(&ours, &theirs, ctr, &msg); + + let encoded = general_purpose::URL_SAFE.encode(t); + + Ok(stdout.write_all(encoded.as_bytes())?) +} + +fn login(args: &LoginArgs, stdout: &mut dyn io::Write) -> CommandResult { + let ours: StaticSecret = read_key(&args.key)?.into(); + + let challenge_start = match args.challenge.find("v2/") { + Some(n) => n, + None => return Err("challenge should have a v2/ prefix".into()), + }; + let (_, challenge) = args.challenge.split_at(challenge_start + 3); + let parts: Vec<_> = challenge.split("/").collect(); + if parts.len() != 4 || !parts[3].is_empty() { + return Err("unexpected format".into()); + } + let mut handshake = general_purpose::URL_SAFE.decode(parts[0])?; + if handshake.len() < 33 { + return Err("handshake too short".into()); + } + let message_tag_prefix = handshake.split_off(33); + let raw_public_key: [u8; 32] = handshake + .split_off(1) + .try_into() + .expect("there should be exactly 33 bytes in the argument"); + let theirs: PublicKey = raw_public_key.into(); + + // Check public key prefix, if present. + let prefix = handshake[0]; + if prefix & 1 << 7 == 0 { + let pubkey = ours.public_key().to_bytes(); + if pubkey[31] != prefix { + return Err(format!("challenge was generated for a different key: our key has MSB {}, challenge requests {}", pubkey[31], prefix).into()); + } + } + + let msg = [parts[1], parts[2]].join("/"); + + // Check message tag in challenge, if present. + let message_tag_prefix_len = message_tag_prefix.len(); + if message_tag_prefix_len > 0 + && !glome::verify(&ours, &theirs, 0, msg.as_bytes(), &message_tag_prefix) + { + return Err("unexpected message tag prefix".into()); + } + + let t = glome::tag(&ours, &theirs, 0, msg.as_bytes()); + + let encoded = general_purpose::URL_SAFE.encode(t); + + Ok(stdout.write_all(encoded.as_bytes())?) +} + +fn main() -> CommandResult { + match &Cli::parse().command { + 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 io::Write; + use std::{fmt::Debug, path::Path}; + use tempfile::NamedTempFile; + use yaml_rust2::{Yaml, YamlLoader}; + + use super::*; + + #[derive(Debug)] + struct Person { + private: [u8; 32], + public_cli: String, + } + + impl From<&Yaml> for Person { + fn from(case: &Yaml) -> Self { + let private: [u8; 32] = hex::decode(case["private-key"]["hex"].as_str().unwrap()) + .unwrap() + .try_into() + .unwrap(); + let public_cli = case["public-key"]["glome-cli"] + .as_str() + .unwrap() + .to_string(); + Person { + private, + public_cli, + } + } + } + + #[derive(Debug)] + struct TestVector { + name: String, + alice: Person, + bob: Person, + message: String, + tag: String, + host_id_type: String, + host_id: String, + action: String, + } + + impl From<&Yaml> for TestVector { + fn from(case: &Yaml) -> Self { + TestVector { + name: format!("vector-{:02}", case["vector"].as_i64().unwrap()), + alice: (&case["alice"]).into(), + bob: (&case["bob"]).into(), + message: case["message"].as_str().unwrap().to_string(), + tag: case["tag"].as_str().unwrap().to_string(), + host_id_type: case["host-id-type"].as_str().unwrap().to_string(), + host_id: case["host-id"].as_str().unwrap().to_string(), + action: case["action"].as_str().unwrap().to_string(), + } + } + } + + fn test_vectors() -> Vec { + let rust_dir = + std::env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR should be set"); + let vectors_file = Path::new(&rust_dir).join("../docs/login-v2-test-vectors.yaml"); + let content = fs::read_to_string(vectors_file).expect("test vectors should be readable"); + let cases = &YamlLoader::load_from_str(&content).expect("test vectors should be yaml")[0]; + + let mut vectors: Vec = Vec::new(); + for case in cases.as_vec().expect("top level should be a list") { + vectors.push(case.into()); + } + vectors + } + + #[test] + fn test_genkey() { + let mut stdout = io::Cursor::new(Vec::new()); + genkey(&mut stdout).expect("genkey should work"); + assert_eq!(32, stdout.get_ref().len()) + } + + fn cursor_to_string(cursor: &io::Cursor>) -> String { + std::str::from_utf8(cursor.get_ref().as_slice()) + .expect("all test vectors should be UTF-8") + .to_string() + } + + #[test] + fn test_pubkey() { + for tc in test_vectors() { + for person in [tc.alice, tc.bob] { + let mut stdin = io::Cursor::new(person.private); + let mut stdout = io::Cursor::new(Vec::new()); + pubkey(&mut stdin, &mut stdout).expect("pubkey should work"); + let expected = format!("{}\n", person.public_cli); + let actual = cursor_to_string(&stdout); + assert_eq!(expected, actual, "vector {}", tc.name) + } + } + } + + fn temp_file(content: &[u8]) -> NamedTempFile { + let mut temp_file = NamedTempFile::new().expect("temp file should be creatable"); + temp_file + .write_all(content) + .expect("temp file should be writable"); + temp_file + } + + #[test] + fn test_tag() { + for tc in test_vectors() { + let host = if tc.host_id_type.is_empty() { + tc.host_id + } else { + format!("{}:{}", tc.host_id_type, tc.host_id) + }; + // Some test messages contain slashes, but we don't want to add a dependency for URL + // escaping, so we just replace the one character that occurs in the test vectors. + let message = format!("{}/{}", host, tc.action.replace("/", "%2F")); + let mut stdin = io::Cursor::new(message.into_bytes()); + let mut stdout = io::Cursor::new(Vec::new()); + let key_file = temp_file(&tc.bob.private); + let peer_file = temp_file(tc.alice.public_cli.as_bytes()); + let args = TagArgs { + key: key_file.path().to_path_buf(), + peer: peer_file.path().to_path_buf(), + counter: None, + }; + gentag(&args, &mut stdin, &mut stdout).expect("gentag should work"); + + let actual = cursor_to_string(&stdout); + assert_eq!(tc.tag, actual, "vector {}", tc.name) + } + } + + #[test] + fn test_login() { + for tc in test_vectors() { + let mut stdout = io::Cursor::new(Vec::new()); + let key_file = temp_file(&tc.bob.private); + let args = LoginArgs { + key: key_file.path().to_path_buf(), + challenge: tc.message, + }; + login(&args, &mut stdout).expect("login should work"); + + let actual = cursor_to_string(&stdout); + assert_eq!(tc.tag, actual, "vector {}", tc.name) + } + } +} diff --git a/rust/src/lib.rs b/rust/src/lib.rs index bb1b982..747f116 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -110,6 +110,7 @@ pub mod tests { use crate::PrivateKey; use hex_literal::hex; + #[doc(hidden)] pub fn run_vector_1(load_keypair: &F) where T: PrivateKey, @@ -147,6 +148,7 @@ pub mod tests { )); } + #[doc(hidden)] pub fn run_vector_2(load_keypair: &F) where T: PrivateKey,