use std::fs;
use std::path::PathBuf;
use serde::Deserialize;
use crate::client::proof::{base64url_encode, build_proof_input, sha256};
use crate::client::{verify_proof, SessionAuth};
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct AuthProofFixture {
name: String,
seed: String,
session_key: String,
oauth_init: DomainSigFixture,
flow_bind: DomainSigFixture,
nats_connect: NatsConnectFixture,
rpc_proof: RpcProofFixture,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct DomainSigFixture {
redirect_to: Option<String>,
flow_id: Option<String>,
sig: String,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct NatsConnectFixture {
contract_digest: String,
iat: u64,
iat_sig: String,
runtime_token: String,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct RpcProofFixture {
subject: String,
payload: String,
iat: i64,
request_id: String,
payload_hash_base64url: String,
proof_input_hex: String,
proof_digest_base64url: String,
proof: String,
}
fn bytes_to_hex(bytes: &[u8]) -> String {
let mut out = String::with_capacity(bytes.len() * 2);
for byte in bytes {
use std::fmt::Write as _;
write!(&mut out, "{:02x}", byte).unwrap();
}
out
}
#[test]
fn auth_proof_matches_shared_conformance_vectors() {
let fixture_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("../../../conformance/auth-proof/vectors.json");
let fixtures: Vec<AuthProofFixture> =
serde_json::from_str(&fs::read_to_string(fixture_path).unwrap()).unwrap();
assert!(fixtures.len() >= 2);
for fixture in fixtures {
assert!(fixture.name.starts_with("proof-layout-current"));
let auth = SessionAuth::from_seed_base64url(&fixture.seed).unwrap();
assert_eq!(auth.session_key, fixture.session_key);
assert_eq!(
auth.sign_sha256_domain(
"oauth-init",
&format!(
"{}:null",
fixture.oauth_init.redirect_to.as_deref().unwrap()
)
),
fixture.oauth_init.sig
);
assert_eq!(
auth.sign_sha256_domain("bind-flow", fixture.flow_bind.flow_id.as_deref().unwrap()),
fixture.flow_bind.sig
);
assert_eq!(
auth.sign_sha256_domain(
"nats-connect",
&format!(
"{}:{}",
fixture.nats_connect.iat, fixture.nats_connect.contract_digest
)
),
fixture.nats_connect.iat_sig
);
assert_eq!(
auth.nats_connect_user_token(
fixture.nats_connect.iat,
&fixture.nats_connect.contract_digest,
),
fixture.nats_connect.runtime_token
);
let payload_hash = sha256(fixture.rpc_proof.payload.as_bytes());
assert_eq!(
base64url_encode(&payload_hash),
fixture.rpc_proof.payload_hash_base64url
);
let proof_input = build_proof_input(
&fixture.session_key,
&fixture.rpc_proof.subject,
&payload_hash,
fixture.rpc_proof.iat,
&fixture.rpc_proof.request_id,
);
assert_eq!(
bytes_to_hex(&proof_input),
fixture.rpc_proof.proof_input_hex
);
let proof_digest = sha256(&proof_input);
assert_eq!(
base64url_encode(&proof_digest),
fixture.rpc_proof.proof_digest_base64url
);
assert_eq!(
auth.create_proof(
&fixture.rpc_proof.subject,
fixture.rpc_proof.payload.as_bytes(),
fixture.rpc_proof.iat,
&fixture.rpc_proof.request_id,
),
fixture.rpc_proof.proof
);
assert!(verify_proof(
&fixture.session_key,
&fixture.rpc_proof.subject,
fixture.rpc_proof.payload.as_bytes(),
fixture.rpc_proof.iat,
&fixture.rpc_proof.request_id,
&fixture.rpc_proof.proof,
)
.unwrap());
}
}