use std::time::Duration;
use crate::jose::{Advertisment, Jwk, JwkSet, KeyMeta, ProvisionedData};
use crate::{EncryptionKey, Result};
const DEFAULT_TIMEOUT: Duration = Duration::from_secs(120);
#[must_use]
#[derive(Clone, Debug)]
pub struct TangClient {
url: String,
timeout: Duration,
}
impl TangClient {
pub fn new(url: &str, timeout: Option<Duration>) -> Self {
let url = if url.starts_with("http") {
url.to_owned()
} else {
format!("http://{url}")
};
Self {
url,
timeout: timeout.unwrap_or(DEFAULT_TIMEOUT),
}
}
pub fn url(&self) -> &str {
&self.url
}
pub fn create_secure_key<const KEYBYTES: usize>(&self) -> Result<ProvisionedData<KEYBYTES>> {
let (keys, signing_thp) = self.fetch_keys(None)?;
keys.make_tang_enc_key(&self.url, signing_thp)
}
pub fn create_secure_key_trusted_key<const KEYBYTES: usize>(
&self,
thumbprint: &str,
) -> Result<ProvisionedData<KEYBYTES>> {
let (keys, signing_thp) = self.fetch_keys(Some(thumbprint))?;
keys.make_tang_enc_key(&self.url, signing_thp)
}
pub fn recover_secure_key<const KEYBYTES: usize>(
&self,
meta: &KeyMeta,
) -> Result<EncryptionKey<KEYBYTES>> {
meta.recover_key(|kid, x_pub_jwk| self.fetch_recovery_key(kid, x_pub_jwk))
}
fn fetch_keys(&self, thumbprint: Option<&str>) -> Result<(JwkSet, Box<str>)> {
let url = format!("{}/adv/{}", &self.url, thumbprint.unwrap_or(""));
log::debug!("fetching advertisment from '{url}'");
let adv: Advertisment = ureq::get(&url).timeout(self.timeout).call()?.into_json()?;
adv.validate_into_keys(thumbprint)
}
fn fetch_recovery_key(&self, kid: &str, x_pub_jwk: &Jwk) -> Result<Jwk> {
let url = format!("{}/rec/{kid}", &self.url);
log::debug!("requesting recovery key from '{url}'");
ureq::post(&url)
.timeout(self.timeout)
.set("Content-Type", "application/jwk+json")
.send_json(x_pub_jwk)?
.into_json()
.map_err(Into::into)
}
}