use base64::{Engine as _, engine::general_purpose::URL_SAFE_NO_PAD};
use ring::{hmac, rand::SecureRandom};
use std::sync::{
Arc, Mutex, Weak,
atomic::{AtomicBool, Ordering},
};
use tokio::sync::watch;
pub struct ControlDocumentBootstrap {
session_id: String,
secret: String,
}
struct ControlDocumentAuthorityMaterial {
session_id: String,
secret: String,
execution_gate: RequiredV3ExecutionGate,
}
#[doc(hidden)]
#[derive(Clone)]
pub struct RequiredV3ExecutionGate(Arc<Mutex<RequiredV3ExecutionGateState>>);
struct RequiredV3ExecutionGateState {
revoked: bool,
permits: Vec<Weak<RequiredV3ExecutionPermitState>>,
}
struct RequiredV3ExecutionPermitState {
active: AtomicBool,
effect_committed: AtomicBool,
cancelled_tx: watch::Sender<bool>,
}
#[doc(hidden)]
#[derive(Clone)]
pub struct RequiredV3ExecutionPermit(Arc<RequiredV3ExecutionPermitState>);
#[doc(hidden)]
#[derive(Clone)]
pub struct ControlDocumentAuthority(Arc<ControlDocumentAuthorityMaterial>);
#[derive(Debug, thiserror::Error, Clone, Copy, PartialEq, Eq)]
pub enum ControlDocumentBootstrapError {
#[error("control document bootstrap requires the live native host authority")]
NativeAuthorityRequired,
#[error("secure random generation for control document bootstrap failed")]
EntropyUnavailable,
}
pub fn issue_control_document_bootstrap(
native_authority: &crate::NativeControlPlaneAuthority,
rng: &dyn SecureRandom,
) -> Result<(ControlDocumentBootstrap, ControlDocumentAuthority), ControlDocumentBootstrapError> {
if !native_authority.validate() {
return Err(ControlDocumentBootstrapError::NativeAuthorityRequired);
}
let mut public_session_id = [0_u8; 16];
let mut secret = [0_u8; 32];
rng.fill(&mut public_session_id)
.map_err(|_| ControlDocumentBootstrapError::EntropyUnavailable)?;
rng.fill(&mut secret)
.map_err(|_| ControlDocumentBootstrapError::EntropyUnavailable)?;
let session_id = URL_SAFE_NO_PAD.encode(public_session_id);
let secret = URL_SAFE_NO_PAD.encode(secret);
Ok((
ControlDocumentBootstrap {
session_id: session_id.clone(),
secret: secret.clone(),
},
ControlDocumentAuthority(Arc::new(ControlDocumentAuthorityMaterial {
session_id,
secret,
execution_gate: RequiredV3ExecutionGate(Arc::new(Mutex::new(
RequiredV3ExecutionGateState {
revoked: false,
permits: Vec::new(),
},
))),
})),
))
}
impl ControlDocumentBootstrap {
pub(crate) fn take_binding(self) -> (String, String) {
(self.session_id, self.secret)
}
}
impl ControlDocumentAuthority {
pub fn matches(&self, session_id: &str, secret: &str) -> bool {
let key = hmac::Key::new(hmac::HMAC_SHA256, b"lingxia-control-document-binding-v3");
let session_tag = hmac::sign(&key, self.0.session_id.as_bytes());
let secret_tag = hmac::sign(&key, self.0.secret.as_bytes());
let session_matches =
hmac::verify(&key, session_id.as_bytes(), session_tag.as_ref()).is_ok();
let secret_matches = hmac::verify(&key, secret.as_bytes(), secret_tag.as_ref()).is_ok();
session_matches & secret_matches
}
#[doc(hidden)]
pub fn execution_gate(&self) -> RequiredV3ExecutionGate {
self.0.execution_gate.clone()
}
#[doc(hidden)]
pub fn revoke_execution(&self) {
self.0.execution_gate.revoke();
}
#[allow(dead_code)] pub(crate) fn v3_inbound_binding(&self) -> crate::bridge::V3InboundBinding {
crate::bridge::V3InboundBinding::new(self.0.session_id.clone(), self.0.secret.clone())
.expect("native-generated control document binding must be valid")
}
}
impl RequiredV3ExecutionGate {
#[doc(hidden)]
pub fn try_begin(&self) -> Option<RequiredV3ExecutionPermit> {
let Ok(mut state) = self.0.lock() else {
return None;
};
if state.revoked {
return None;
}
state.permits.retain(|permit| permit.strong_count() != 0);
let permit = Arc::new(RequiredV3ExecutionPermitState {
active: AtomicBool::new(true),
effect_committed: AtomicBool::new(false),
cancelled_tx: watch::channel(false).0,
});
state.permits.push(Arc::downgrade(&permit));
Some(RequiredV3ExecutionPermit(permit))
}
fn revoke(&self) {
let Ok(mut state) = self.0.lock() else {
return;
};
state.revoked = true;
for permit in state
.permits
.drain(..)
.filter_map(|permit| permit.upgrade())
{
permit.active.store(false, Ordering::Release);
permit.cancelled_tx.send_replace(true);
}
}
}
impl RequiredV3ExecutionPermit {
#[doc(hidden)]
pub fn is_active(&self) -> bool {
self.0.active.load(Ordering::Acquire)
}
#[doc(hidden)]
pub fn try_commit_effect(&self) -> bool {
self.is_active()
&& self
.0
.effect_committed
.compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
.is_ok()
&& self.is_active()
}
#[doc(hidden)]
pub fn cancellation_receiver(&self) -> watch::Receiver<bool> {
self.0.cancelled_tx.subscribe()
}
}
#[cfg(test)]
mod tests {
use super::*;
use ring::rand::SystemRandom;
#[test]
fn issued_material_is_fresh_and_authenticates_only_its_tuple() {
let (first_bootstrap, first) = issue_control_document_bootstrap(
&crate::NativeControlPlaneAuthority::for_test(),
&SystemRandom::new(),
)
.unwrap();
let (_second_bootstrap, second) = issue_control_document_bootstrap(
&crate::NativeControlPlaneAuthority::for_test(),
&SystemRandom::new(),
)
.unwrap();
let (session_id, secret) = first_bootstrap.take_binding();
assert!(first.matches(&session_id, &secret));
assert!(!second.matches(&session_id, &secret));
assert!(!first.matches(&session_id, "wrong"));
assert!(!first.matches("wrong", &secret));
let gate = first.execution_gate();
let permit = gate.try_begin().expect("fresh gate should issue permit");
assert!(permit.is_active());
assert!(permit.try_commit_effect());
assert!(!permit.try_commit_effect());
first.revoke_execution();
assert!(!permit.is_active());
assert!(gate.try_begin().is_none());
}
}