#![allow(deprecated)]
use assert_cmd::Command;
use auths_crypto::testing::gen_keypair;
use auths_verifier::IdentityDID;
use auths_verifier::core::{
Attestation, CanonicalAttestationData, Ed25519PublicKey, Ed25519Signature, ResourceId,
canonicalize_attestation_data,
};
use auths_verifier::types::DeviceDID;
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 mut att = Attestation {
version: 1,
rid: ResourceId::new("test-rid"),
issuer: IdentityDID::new(format!(
"did:key:{}",
hex::encode(issuer_kp.public_key().as_ref())
)),
subject: DeviceDID::new(format!(
"did:key:{}",
hex::encode(device_kp.public_key().as_ref())
)),
device_public_key: Ed25519PublicKey::from_bytes(device_pk),
identity_signature: Ed25519Signature::empty(),
device_signature: Ed25519Signature::empty(),
revoked_at: None,
expires_at: Some(Utc::now() + Duration::days(365)),
timestamp: Some(Utc::now()),
note: None,
payload: None,
role: None,
capabilities: vec![],
delegated_by: None,
signer_type: None,
};
let data = CanonicalAttestationData {
version: att.version,
rid: &att.rid,
issuer: &att.issuer,
subject: &att.subject,
device_public_key: att.device_public_key.as_bytes(),
payload: &att.payload,
timestamp: &att.timestamp,
expires_at: &att.expires_at,
revoked_at: &att.revoked_at,
note: &att.note,
role: att.role.as_ref().map(|r| r.as_str()),
capabilities: if att.capabilities.is_empty() {
None
} else {
Some(&att.capabilities)
},
delegated_by: att.delegated_by.as_ref(),
signer_type: att.signer_type.as_ref(),
};
let canonical_bytes = canonicalize_attestation_data(&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 = NamedTempFile::new().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("--issuer-pk")
.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("--issuer-pk")
.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("--issuer-pk")
.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("--issuer-pk")
.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("--issuer-pk")
.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("--issuer-pk")
.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("issuer-pk"));
}
#[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": 1,
"roots": [
{{
"did": "{}",
"public_key_hex": "{}",
"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": 1, "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("--issuer-pk")
.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("--issuer-did"))
.stdout(predicates::str::contains("--issuer-pk"))
.stdout(predicates::str::contains("--allowed-signers"));
}