use anyhow::Context; use base64::Engine; use hmac::{Hmac, Mac}; use sha1::Sha1; use std::time::{Duration, SystemTime, UNIX_EPOCH}; fn now_unix_secs() -> u64 { SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap_or_else(|_| Duration::from_secs(0)) .as_secs() } fn turn_rest_password_base64(secret: &[u8], username: &str) -> String { type HmacSha1 = Hmac; let mut mac = HmacSha1::new_from_slice(secret).expect("rest secret to build hmac"); mac.update(username.as_bytes()); let bytes = mac.finalize().into_bytes(); base64::engine::general_purpose::STANDARD.encode(bytes) } /// Minimal offline TURN REST credential generator. /// /// Usage: /// - `cargo run --bin turn_rest_cred -- --secret "..." --user alice --ttl 600` /// /// Output: /// - `TURN_USERNAME=...` /// - `TURN_PASSWORD=...` fn main() -> anyhow::Result<()> { let mut secret: Option = None; let mut user: Option = None; let mut ttl: u64 = 600; let mut json = false; let mut args = std::env::args().skip(1); while let Some(arg) = args.next() { match arg.as_str() { "--secret" => secret = Some(args.next().context("--secret requires a value")?), "--user" => user = Some(args.next().context("--user requires a value")?), "--ttl" => { let v = args.next().context("--ttl requires a value")?; ttl = v.parse::().context("--ttl must be an integer")?; } "--json" => json = true, "-h" | "--help" => { println!( "turn_rest_cred\n\nUSAGE:\n turn_rest_cred --secret [--user ] [--ttl ] [--json]\n\nNOTES:\n Username format is [:]. Password is base64(HMAC-SHA1(secret, username)).\n" ); return Ok(()); } other => return Err(anyhow::anyhow!("unknown arg: {}", other)), } } let secret = secret.context("missing --secret")?; let expiry = now_unix_secs().saturating_add(ttl.max(60)); let username = match user { Some(u) if !u.is_empty() => format!("{}:{}", expiry, u), _ => expiry.to_string(), }; let password = turn_rest_password_base64(secret.as_bytes(), &username); if json { println!( "{{\n \"username\": \"{}\",\n \"credential\": \"{}\",\n \"ttl\": {}\n}}", username, password, ttl ); } else { println!("TURN_USERNAME={}", username); println!("TURN_PASSWORD={}", password); println!("TURN_TTL={}", ttl); } Ok(()) }