#![cfg(fuzzing)]
#![allow(dead_code, unused_imports)]
mod admin;
mod admission;
mod aliases;
mod availability;
mod backends;
mod budget;
mod config;
mod convergence;
mod credentials;
mod desired_state;
mod error;
mod key_material;
mod mint;
mod ops;
mod policy;
mod principals;
mod rate_limit;
mod redis_support;
mod reload;
mod revocation;
mod routes;
mod shutdown;
mod state;
mod status;
mod streaming;
#[allow(unused_imports)]
mod telemetry;
mod usage;
use std::collections::HashMap;
use std::sync::OnceLock;
use std::time::Duration;
use crate::config::Config;
use crate::mint::{MintAlgorithm, MintRequest};
use crate::principals::{
Presented, PrincipalStore, PrincipalStoreError, TokenVerificationError, TokenVerifier,
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Rejection {
Load(String),
Invalid(String),
BadRequest(String),
Unauthenticated(&'static str),
Unauthorized(&'static str),
Unavailable,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ConfigShape {
pub stateful: bool,
pub namespaces: usize,
pub providers: usize,
pub models: usize,
pub credentials: usize,
pub gateway_keys: usize,
pub verifiers: usize,
}
pub fn config_from_toml_str(input: &str) -> Result<ConfigShape, Rejection> {
match Config::from_toml_str(input) {
Ok(config) => Ok(ConfigShape {
stateful: config.mode == config::Mode::Stateful,
namespaces: config.namespace.len(),
providers: config.provider.len(),
models: config.model.len(),
credentials: config.credential.len(),
gateway_keys: config.gateway_key.len(),
verifiers: config.gateway_verifier.len(),
}),
Err(config::ConfigError::Load(message)) => Err(Rejection::Load(message)),
Err(config::ConfigError::Invalid(message)) => Err(Rejection::Invalid(message)),
}
}
pub fn credentials_query_namespaces(raw_query: Option<&str>) -> Result<Option<String>, Rejection> {
routes::fuzz_parse_credential_query(raw_query)
.map_err(|error| Rejection::BadRequest(error.to_string()))
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct VerifiedToken {
pub namespace: String,
pub subject: String,
pub capabilities: usize,
pub scoped_aliases: bool,
pub max_request_microdollars: Option<u64>,
}
pub fn verify_token(credential: &str) -> Result<Option<VerifiedToken>, Rejection> {
let presented = Presented { credential };
let resolved = futures::executor::block_on(verifier().resolve(&presented));
match resolved {
Ok(None) => Ok(None),
Ok(Some(key)) => Ok(Some(VerifiedToken {
namespace: key.namespace,
subject: key.subject,
capabilities: key.scope.map_or(0, |scope| scope.len()),
scoped_aliases: key.alias_scope.is_some(),
max_request_microdollars: key.max_request_microdollars,
})),
Err(PrincipalStoreError::Unauthorized(error)) => {
Err(Rejection::Unauthenticated(code(&error)))
}
Err(PrincipalStoreError::Forbidden(error)) => Err(Rejection::Unauthorized(code(&error))),
Err(PrincipalStoreError::Unavailable) => Err(Rejection::Unavailable),
}
}
pub fn mint_hs256_token(
namespace: &str,
subject: &str,
audience: &str,
ttl_seconds: u64,
issued_at: Option<u64>,
scope: Option<Vec<String>>,
aliases: Option<Vec<String>>,
) -> Option<String> {
mint::fuzz_mint_token_with_raw_scope(
MintRequest {
kid: HS256_KID,
algorithm: MintAlgorithm::Hs256,
key_material: HS256_MATERIAL,
namespace,
subject,
audience,
ttl: Duration::from_secs(ttl_seconds),
aliases,
max_request_microdollars: None,
scope: None,
},
issued_at,
scope,
)
.ok()
.map(|minted| minted.token)
}
pub fn resign_seed_onto_this_run(token: &str) -> Option<String> {
use base64::{Engine as _, engine::general_purpose::URL_SAFE_NO_PAD};
let mut segments = token.strip_prefix("axt1.")?.split('.');
let header: jsonwebtoken::Header =
serde_json::from_slice(&URL_SAFE_NO_PAD.decode(segments.next()?).ok()?).ok()?;
let mut claims: serde_json::Map<String, serde_json::Value> =
serde_json::from_slice(&URL_SAFE_NO_PAD.decode(segments.next()?).ok()?).ok()?;
let iat = claims.get("iat")?.as_u64()?;
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.ok()?
.as_secs();
let offset = i128::from(now) - i128::from(iat);
let shift = |value: &mut serde_json::Value| {
if let Some(seconds) = value.as_u64() {
let shifted = (i128::from(seconds) + offset).clamp(0, i128::from(u64::MAX));
*value = serde_json::Value::from(u64::try_from(shifted).unwrap_or(0));
}
};
for claim in ["iat", "exp", "nbf"] {
if let Some(value) = claims.get_mut(claim) {
shift(value);
}
}
let kid = header.kid.clone().unwrap_or_else(|| HS256_KID.to_owned());
mint::fuzz_sign_claims(
&header,
&serde_json::Value::Object(claims),
MintAlgorithm::Hs256,
HS256_MATERIAL,
&kid,
)
.ok()
}
pub const AUDIENCE: &str = "fuzz.axond.invalid";
pub const NAMESPACES: [&str; 2] = ["fuzz", "denied"];
pub const CAPABILITY_COUNT: usize = principals::Capability::ALL.len();
pub const MAX_TTL_SECONDS: u64 = 900;
pub fn epoch_min_iat() -> u64 {
static MIN_IAT: OnceLock<u64> = OnceLock::new();
*MIN_IAT.get_or_init(|| {
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map_or(0, |since| since.as_secs())
.saturating_sub(EPOCH_LOOKBACK_SECONDS)
})
}
const EPOCH_LOOKBACK_SECONDS: u64 = 300;
pub const HS256_KID: &str = "fuzz-hs256";
pub const EDDSA_KID: &str = "fuzz-eddsa";
const HS256_MATERIAL: &str = "axond-fuzz-hs256-material-not-a-secret";
const EDDSA_PUBLIC_BASE64: &str = "ZnV6ei1heG9uZC1lZDI1NTE5LXB1YmxpYy1rZXktMzI=";
const CONFIG: &str = r#"
[[namespace]]
id = "fuzz"
default = true
[[namespace]]
id = "denied"
[[gateway_key]]
env = "AXOND_FUZZ_STATIC_KEY"
namespace = "fuzz"
[gateway_token]
audience = "fuzz.axond.invalid"
[[gateway_verifier]]
kid = "fuzz-hs256"
alg = "HS256"
env = "AXOND_FUZZ_HS256"
namespaces = ["fuzz"]
max_ttl = "15m"
[[gateway_verifier]]
kid = "fuzz-eddsa"
alg = "EdDSA"
env = "AXOND_FUZZ_EDDSA"
namespaces = ["fuzz", "denied"]
max_ttl = "15m"
[[gateway_token_epoch]]
namespace = "fuzz"
min_iat = {MIN_IAT}
"#;
fn verifier() -> &'static TokenVerifier {
static VERIFIER: OnceLock<TokenVerifier> = OnceLock::new();
VERIFIER.get_or_init(|| {
let text = CONFIG.replace("{MIN_IAT}", &epoch_min_iat().to_string());
let config = Config::from_toml_str(&text).expect("the seam's own config is valid");
let env = HashMap::from([
(
"AXOND_FUZZ_STATIC_KEY".to_owned(),
"axond-fuzz-static-key-not-a-secret".to_owned(),
),
("AXOND_FUZZ_HS256".to_owned(), HS256_MATERIAL.to_owned()),
(
"AXOND_FUZZ_EDDSA".to_owned(),
EDDSA_PUBLIC_BASE64.to_owned(),
),
]);
TokenVerifier::build(&config, &env)
.expect("the seam's own verifiers build")
.expect("the seam configures verifiers")
})
}
fn code(error: &TokenVerificationError) -> &'static str {
error.code()
}