use assert_cmd::Command;
use auths_crypto::testing::gen_keypair;
use auths_verifier::AttestationBuilder;
use auths_verifier::core::{
Attestation, Ed25519PublicKey, Ed25519Signature, canonicalize_attestation_data,
};
use auths_verifier::types::CanonicalDid;
use chrono::{Duration, Utc};
use ring::signature::KeyPair;
use std::io::Write;
use tempfile::NamedTempFile;
fn create_signed_attestation(
issuer_kp: &ring::signature::Ed25519KeyPair,
device_kp: &ring::signature::Ed25519KeyPair,
) -> Attestation {
let device_pk: [u8; 32] = device_kp.public_key().as_ref().try_into().unwrap();
let issuer_pk: [u8; 32] = issuer_kp.public_key().as_ref().try_into().unwrap();
let device_did =
CanonicalDid::from_public_key_did_key(&device_pk, auths_crypto::CurveType::Ed25519);
let issuer_did =
CanonicalDid::from_public_key_did_key(&issuer_pk, auths_crypto::CurveType::Ed25519);
let mut att = AttestationBuilder::default()
.rid("test-rid")
.issuer(issuer_did.as_str())
.subject(device_did.as_ref())
.device_public_key(Ed25519PublicKey::from_bytes(device_pk))
.expires_at(Some(Utc::now() + Duration::days(365)))
.timestamp(Some(Utc::now()))
.build();
let canonical_bytes = canonicalize_attestation_data(&att.canonical_data()).unwrap();
att.identity_signature =
Ed25519Signature::try_from_slice(issuer_kp.sign(&canonical_bytes).as_ref()).unwrap();
att.device_signature =
Ed25519Signature::try_from_slice(device_kp.sign(&canonical_bytes).as_ref()).unwrap();
att
}
fn write_attestation_to_file(att: &Attestation) -> NamedTempFile {
let mut file = tempfile::Builder::new().suffix(".json").tempfile().unwrap();
let json = serde_json::to_string(att).unwrap();
file.write_all(json.as_bytes()).unwrap();
file.flush().unwrap();
file
}
#[test]
fn test_verify_valid_attestation_returns_exit_code_0() {
let issuer_kp = gen_keypair();
let device_kp = gen_keypair();
let att = create_signed_attestation(&issuer_kp, &device_kp);
let file = write_attestation_to_file(&att);
let pk_hex = hex::encode(issuer_kp.public_key().as_ref());
let mut cmd = Command::cargo_bin("auths").unwrap();
cmd.arg("verify")
.arg(file.path())
.arg("--signer-key")
.arg(&pk_hex);
cmd.assert().success();
}
#[test]
fn test_verify_invalid_attestation_returns_exit_code_1() {
let issuer_kp = gen_keypair();
let wrong_kp = gen_keypair();
let device_kp = gen_keypair();
let att = create_signed_attestation(&issuer_kp, &device_kp);
let file = write_attestation_to_file(&att);
let wrong_pk_hex = hex::encode(wrong_kp.public_key().as_ref());
let mut cmd = Command::cargo_bin("auths").unwrap();
cmd.arg("verify")
.arg(file.path())
.arg("--signer-key")
.arg(&wrong_pk_hex);
cmd.assert().code(1);
}
#[test]
fn test_verify_invalid_json_returns_exit_code_2() {
let mut file = NamedTempFile::new().unwrap();
file.write_all(b"not valid json").unwrap();
file.flush().unwrap();
let mut cmd = Command::cargo_bin("auths").unwrap();
cmd.arg("verify")
.arg(file.path())
.arg("--signer-key")
.arg("a".repeat(64));
cmd.assert().code(2);
}
#[test]
fn test_verify_json_output_valid() {
let issuer_kp = gen_keypair();
let device_kp = gen_keypair();
let att = create_signed_attestation(&issuer_kp, &device_kp);
let file = write_attestation_to_file(&att);
let pk_hex = hex::encode(issuer_kp.public_key().as_ref());
let mut cmd = Command::cargo_bin("auths").unwrap();
cmd.arg("verify")
.arg(file.path())
.arg("--signer-key")
.arg(&pk_hex)
.arg("--json");
let output = cmd.output().unwrap();
assert!(output.status.success());
let stdout = String::from_utf8(output.stdout).unwrap();
let result: serde_json::Value = serde_json::from_str(&stdout).unwrap();
assert_eq!(result["valid"], true);
assert!(result["issuer"].is_string());
assert!(result["subject"].is_string());
}
#[test]
fn test_verify_json_output_invalid() {
let issuer_kp = gen_keypair();
let wrong_kp = gen_keypair();
let device_kp = gen_keypair();
let att = create_signed_attestation(&issuer_kp, &device_kp);
let file = write_attestation_to_file(&att);
let wrong_pk_hex = hex::encode(wrong_kp.public_key().as_ref());
let mut cmd = Command::cargo_bin("auths").unwrap();
cmd.arg("verify")
.arg(file.path())
.arg("--signer-key")
.arg(&wrong_pk_hex)
.arg("--json");
let output = cmd.output().unwrap();
assert_eq!(output.status.code(), Some(1));
let stdout = String::from_utf8(output.stdout).unwrap();
let result: serde_json::Value = serde_json::from_str(&stdout).unwrap();
assert_eq!(result["valid"], false);
assert!(result["error"].is_string());
}
#[test]
fn test_verify_stdin_input() {
let issuer_kp = gen_keypair();
let device_kp = gen_keypair();
let att = create_signed_attestation(&issuer_kp, &device_kp);
let pk_hex = hex::encode(issuer_kp.public_key().as_ref());
let json = serde_json::to_string(&att).unwrap();
let mut cmd = Command::cargo_bin("auths").unwrap();
cmd.arg("verify")
.arg("-")
.arg("--signer-key")
.arg(&pk_hex)
.write_stdin(json);
cmd.assert().success();
}
#[test]
fn test_verify_help_shows_usage() {
let mut cmd = Command::cargo_bin("auths").unwrap();
cmd.arg("verify").arg("--help");
cmd.assert()
.success()
.stdout(predicates::str::contains("attestation"))
.stdout(predicates::str::contains("signer-key"));
}
#[test]
fn test_verify_with_roots_json_explicit_policy() {
let issuer_kp = gen_keypair();
let device_kp = gen_keypair();
let att = create_signed_attestation(&issuer_kp, &device_kp);
let att_file = write_attestation_to_file(&att);
let pk_hex = hex::encode(issuer_kp.public_key().as_ref());
let roots_dir = tempfile::tempdir().unwrap();
let roots_path = roots_dir.path().join("roots.json");
let roots_content = format!(
r#"{{
"version": 2,
"roots": [
{{
"did": "{}",
"public_key_hex": "{}",
"curve": "ed25519",
"note": "Test issuer"
}}
]
}}"#,
att.issuer, pk_hex
);
std::fs::write(&roots_path, roots_content).unwrap();
let mut cmd = Command::cargo_bin("auths").unwrap();
cmd.arg("device")
.arg("verify")
.arg("--attestation")
.arg(att_file.path())
.arg("--roots-file")
.arg(&roots_path)
.arg("--trust")
.arg("explicit");
cmd.assert().success();
}
#[test]
fn test_verify_explicit_rejects_unknown_identity() {
let issuer_kp = gen_keypair();
let device_kp = gen_keypair();
let att = create_signed_attestation(&issuer_kp, &device_kp);
let att_file = write_attestation_to_file(&att);
let roots_dir = tempfile::tempdir().unwrap();
let roots_path = roots_dir.path().join("roots.json");
std::fs::write(&roots_path, r#"{"version": 2, "roots": []}"#).unwrap();
let mut cmd = Command::cargo_bin("auths").unwrap();
cmd.arg("device")
.arg("verify")
.arg("--attestation")
.arg(att_file.path())
.arg("--roots-file")
.arg(&roots_path)
.arg("--trust")
.arg("explicit");
cmd.assert().code(2);
}
#[test]
fn test_verify_issuer_did_with_pinned_store() {
let issuer_kp = gen_keypair();
let device_kp = gen_keypair();
let att = create_signed_attestation(&issuer_kp, &device_kp);
let att_file = write_attestation_to_file(&att);
let pk_hex = hex::encode(issuer_kp.public_key().as_ref());
let store_dir = tempfile::tempdir().unwrap();
let store_path = store_dir.path().join("known_identities.json");
let pin_content = format!(
r#"[
{{
"did": "{}",
"public_key_hex": "{}",
"first_seen": "2024-01-01T00:00:00Z",
"origin": "test",
"trust_level": "manual"
}}
]"#,
att.issuer, pk_hex
);
std::fs::write(&store_path, pin_content).unwrap();
let mut cmd = Command::cargo_bin("auths").unwrap();
cmd.arg("verify")
.arg(att_file.path())
.arg("--signer-key")
.arg(&pk_hex);
cmd.assert().success();
}
#[test]
fn test_verify_help_shows_unified_options() {
let mut cmd = Command::cargo_bin("auths").unwrap();
cmd.arg("verify").arg("--help");
cmd.assert()
.success()
.stdout(predicates::str::contains("--signer"))
.stdout(predicates::str::contains("--signer-key"));
}