use std::{
net::SocketAddr,
sync::atomic::{AtomicU64, Ordering},
time::Duration,
};
use sha2::{Digest, Sha256};
static NEXT_RENDEZVOUS_GENERATION: AtomicU64 = AtomicU64::new(1);
#[doc(hidden)]
pub struct BootstrapRendezvousIssuer {
generation: u64,
}
#[doc(hidden)]
pub struct GeneratedApplicationFreezeSource {
application: &'static str,
descriptor: &'static [u8],
route_sources: &'static [&'static str],
}
#[doc(hidden)]
pub struct ListenerStartupFreezeSource<'a> {
application: &'a str,
listener: SocketAddr,
management: SocketAddr,
request_timeout: Duration,
}
#[doc(hidden)]
pub struct BootstrapApplicationHalf {
generation: u64,
application_identity: [u8; 32],
route_cost_identity: [u8; 32],
}
#[doc(hidden)]
pub struct BootstrapListenerIssuer {
generation: u64,
application_name_identity: [u8; 32],
application_identity: [u8; 32],
route_cost_identity: [u8; 32],
}
#[doc(hidden)]
pub struct BootstrapListenerHalf {
generation: u64,
application_identity: [u8; 32],
route_cost_identity: [u8; 32],
listener_startup_identity: [u8; 32],
}
#[doc(hidden)]
pub struct BootstrapRendezvousWhole {
binding_id: BootstrapBindingId,
_application_identity: [u8; 32],
_route_cost_identity: [u8; 32],
_listener_startup_identity: [u8; 32],
}
#[derive(Eq, PartialEq)]
struct BootstrapBindingId([u8; 32]);
#[doc(hidden)]
pub struct BootstrapBindingReceipt {
binding_id: BootstrapBindingId,
}
#[doc(hidden)]
pub struct BootstrapRuntimeBindingReceipt {
binding_id: BootstrapBindingId,
}
#[doc(hidden)]
pub struct BootstrapRuntimeBindingRemainder {
whole: BootstrapRendezvousWhole,
}
#[doc(hidden)]
pub struct VerifiedBootstrapRuntimeBinding {
whole: BootstrapRendezvousWhole,
receipt: BootstrapRuntimeBindingReceipt,
}
impl core::fmt::Debug for VerifiedBootstrapRuntimeBinding {
fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
formatter.write_str("VerifiedBootstrapRuntimeBinding(..)")
}
}
impl PartialEq for VerifiedBootstrapRuntimeBinding {
fn eq(&self, other: &Self) -> bool {
self.whole.binding_id == other.whole.binding_id
&& self.receipt.binding_id == other.receipt.binding_id
}
}
impl Eq for VerifiedBootstrapRuntimeBinding {}
#[doc(hidden)]
pub struct BoundBootstrapRendezvous {
_whole: BootstrapRendezvousWhole,
runtime_receipt: BootstrapRuntimeBindingReceipt,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[doc(hidden)]
pub enum BootstrapRendezvousError {
InvalidSource,
ForeignSource,
}
impl BootstrapRendezvousIssuer {
#[doc(hidden)]
pub fn issue() -> Self {
Self {
generation: NEXT_RENDEZVOUS_GENERATION.fetch_add(1, Ordering::Relaxed),
}
}
#[doc(hidden)]
pub fn freeze_application(
self,
source: GeneratedApplicationFreezeSource,
) -> Result<(BootstrapApplicationHalf, BootstrapListenerIssuer), BootstrapRendezvousError> {
if self.generation == 0
|| source.application.is_empty()
|| source.descriptor.is_empty()
|| source.route_sources.is_empty()
|| source.route_sources.iter().any(|route| route.is_empty())
{
return Err(BootstrapRendezvousError::InvalidSource);
}
let application_identity = identity(&[
b"saddle-bootstrap-application-v1",
source.application.as_bytes(),
source.descriptor,
]);
let application_name_identity = identity(&[
b"saddle-bootstrap-application-name-v1",
source.application.as_bytes(),
]);
let mut route_hasher = Sha256::new();
route_hasher.update(b"saddle-bootstrap-route-cost-source-v1");
for route in source.route_sources {
route_hasher.update((route.len() as u64).to_be_bytes());
route_hasher.update(route.as_bytes());
}
let route_cost_identity = route_hasher.finalize().into();
Ok((
BootstrapApplicationHalf {
generation: self.generation,
application_identity,
route_cost_identity,
},
BootstrapListenerIssuer {
generation: self.generation,
application_name_identity,
application_identity,
route_cost_identity,
},
))
}
}
impl GeneratedApplicationFreezeSource {
#[doc(hidden)]
pub const fn new(
application: &'static str,
descriptor: &'static [u8],
route_sources: &'static [&'static str],
) -> Self {
Self {
application,
descriptor,
route_sources,
}
}
}
impl<'a> ListenerStartupFreezeSource<'a> {
#[doc(hidden)]
pub const fn new(
application: &'a str,
listener: SocketAddr,
management: SocketAddr,
request_timeout: Duration,
) -> Self {
Self {
application,
listener,
management,
request_timeout,
}
}
}
impl BootstrapListenerIssuer {
#[doc(hidden)]
pub fn freeze_listener(
self,
source: ListenerStartupFreezeSource<'_>,
) -> Result<BootstrapListenerHalf, (BootstrapRendezvousError, Self)> {
if source.application.is_empty() || source.request_timeout.is_zero() {
return Err((BootstrapRendezvousError::InvalidSource, self));
}
let application_name_identity = identity(&[
b"saddle-bootstrap-application-name-v1",
source.application.as_bytes(),
]);
if application_name_identity != self.application_name_identity {
return Err((BootstrapRendezvousError::ForeignSource, self));
}
let listener = source.listener.to_string();
let management = source.management.to_string();
let timeout = source.request_timeout.as_millis().to_be_bytes();
let listener_startup_identity = identity(&[
b"saddle-bootstrap-listener-startup-v1",
listener.as_bytes(),
management.as_bytes(),
&timeout,
]);
Ok(BootstrapListenerHalf {
generation: self.generation,
application_identity: self.application_identity,
route_cost_identity: self.route_cost_identity,
listener_startup_identity,
})
}
}
#[doc(hidden)]
pub fn pair_bootstrap_rendezvous(
application: BootstrapApplicationHalf,
listener: BootstrapListenerHalf,
) -> Result<
(BootstrapRendezvousWhole, BootstrapBindingReceipt),
(
BootstrapRendezvousError,
BootstrapApplicationHalf,
BootstrapListenerHalf,
),
> {
if application.generation != listener.generation
|| application.application_identity != listener.application_identity
|| application.route_cost_identity != listener.route_cost_identity
{
return Err((
BootstrapRendezvousError::ForeignSource,
application,
listener,
));
}
let binding = identity(&[
b"saddle-bootstrap-binding-v1",
&application.generation.to_be_bytes(),
&application.application_identity,
&application.route_cost_identity,
&listener.listener_startup_identity,
]);
Ok((
BootstrapRendezvousWhole {
binding_id: BootstrapBindingId(binding),
_application_identity: application.application_identity,
_route_cost_identity: application.route_cost_identity,
_listener_startup_identity: listener.listener_startup_identity,
},
BootstrapBindingReceipt {
binding_id: BootstrapBindingId(binding),
},
))
}
#[doc(hidden)]
pub fn bind_bootstrap_receipt(
whole: BootstrapRendezvousWhole,
receipt: BootstrapBindingReceipt,
) -> Result<BoundBootstrapRendezvous, (BootstrapRendezvousWhole, BootstrapBindingReceipt)> {
if whole.binding_id != receipt.binding_id {
return Err((whole, receipt));
}
let runtime_receipt = BootstrapRuntimeBindingReceipt {
binding_id: BootstrapBindingId(receipt.binding_id.0),
};
Ok(BoundBootstrapRendezvous {
_whole: whole,
runtime_receipt,
})
}
#[doc(hidden)]
pub fn take_runtime_bootstrap_receipt(
bound: BoundBootstrapRendezvous,
) -> (
BootstrapRuntimeBindingRemainder,
BootstrapRuntimeBindingReceipt,
) {
(
BootstrapRuntimeBindingRemainder {
whole: bound._whole,
},
bound.runtime_receipt,
)
}
#[doc(hidden)]
pub fn verify_runtime_bootstrap_binding(
remainder: BootstrapRuntimeBindingRemainder,
receipt: BootstrapRuntimeBindingReceipt,
) -> Result<
VerifiedBootstrapRuntimeBinding,
(
BootstrapRuntimeBindingRemainder,
BootstrapRuntimeBindingReceipt,
),
> {
if remainder.whole.binding_id != receipt.binding_id {
return Err((remainder, receipt));
}
Ok(VerifiedBootstrapRuntimeBinding {
whole: remainder.whole,
receipt,
})
}
#[doc(hidden)]
pub fn restore_bound_bootstrap_rendezvous(
verified: VerifiedBootstrapRuntimeBinding,
) -> BoundBootstrapRendezvous {
BoundBootstrapRendezvous {
_whole: verified.whole,
runtime_receipt: verified.receipt,
}
}
#[doc(hidden)]
pub fn recover_verified_runtime_bootstrap_binding(
verified: VerifiedBootstrapRuntimeBinding,
) -> (
BootstrapRuntimeBindingRemainder,
BootstrapRuntimeBindingReceipt,
) {
(
BootstrapRuntimeBindingRemainder {
whole: verified.whole,
},
verified.receipt,
)
}
fn identity(parts: &[&[u8]]) -> [u8; 32] {
let mut hasher = Sha256::new();
for part in parts {
hasher.update((part.len() as u64).to_be_bytes());
hasher.update(part);
}
hasher.finalize().into()
}
#[cfg(test)]
mod tests {
use super::*;
fn halves(
application: &'static str,
listener: SocketAddr,
) -> (BootstrapApplicationHalf, BootstrapListenerHalf) {
let (application_half, listener_issuer) = BootstrapRendezvousIssuer::issue()
.freeze_application(GeneratedApplicationFreezeSource::new(
application,
b"descriptor",
&["lookup|Input|Output|handler"],
))
.unwrap();
let listener_half = match listener_issuer.freeze_listener(ListenerStartupFreezeSource::new(
application,
listener,
"127.0.0.1:9001".parse().unwrap(),
Duration::from_millis(5_000),
)) {
Ok(listener) => listener,
Err(_) => panic!("valid listener source rejected"),
};
(application_half, listener_half)
}
#[test]
fn same_transaction_pairs_and_foreign_halves_are_returned_whole() {
let (application_a, listener_a) = halves("app-a", "127.0.0.1:8000".parse().unwrap());
assert!(pair_bootstrap_rendezvous(application_a, listener_a).is_ok());
let (application_a, listener_a) = halves("app-a", "127.0.0.1:8000".parse().unwrap());
let (application_b, listener_b) = halves("app-b", "127.0.0.1:8002".parse().unwrap());
let (error, application_a, listener_b) =
match pair_bootstrap_rendezvous(application_a, listener_b) {
Ok(_) => panic!("foreign halves paired"),
Err(failure) => failure,
};
assert_eq!(error, BootstrapRendezvousError::ForeignSource);
assert!(pair_bootstrap_rendezvous(application_a, listener_a).is_ok());
assert!(pair_bootstrap_rendezvous(application_b, listener_b).is_ok());
}
#[test]
fn binding_receipt_rejects_crossed_wholes_and_returns_both_for_retry() {
let (application_a, listener_a) = halves("app-a", "127.0.0.1:8000".parse().unwrap());
let (application_b, listener_b) = halves("app-b", "127.0.0.1:8002".parse().unwrap());
let (whole_a, receipt_a) = match pair_bootstrap_rendezvous(application_a, listener_a) {
Ok(value) => value,
Err(_) => panic!("same-source A rejected"),
};
let (whole_b, receipt_b) = match pair_bootstrap_rendezvous(application_b, listener_b) {
Ok(value) => value,
Err(_) => panic!("same-source B rejected"),
};
let (whole_a, receipt_b) = match bind_bootstrap_receipt(whole_a, receipt_b) {
Ok(_) => panic!("crossed binding accepted"),
Err(inputs) => inputs,
};
let (whole_b, receipt_a) = match bind_bootstrap_receipt(whole_b, receipt_a) {
Ok(_) => panic!("crossed binding accepted"),
Err(inputs) => inputs,
};
assert!(bind_bootstrap_receipt(whole_a, receipt_a).is_ok());
assert!(bind_bootstrap_receipt(whole_b, receipt_b).is_ok());
}
#[test]
fn runtime_halves_reject_cross_source_and_original_pairs_retry() {
let (application_a, listener_a) = halves("app-a", "127.0.0.1:8000".parse().unwrap());
let (application_b, listener_b) = halves("app-b", "127.0.0.1:8002".parse().unwrap());
let (whole_a, receipt_a) = match pair_bootstrap_rendezvous(application_a, listener_a) {
Ok(value) => value,
Err(_) => panic!("same-source A rejected"),
};
let (whole_b, receipt_b) = match pair_bootstrap_rendezvous(application_b, listener_b) {
Ok(value) => value,
Err(_) => panic!("same-source B rejected"),
};
let bound_a = match bind_bootstrap_receipt(whole_a, receipt_a) {
Ok(value) => value,
Err(_) => panic!("same-source A receipt rejected"),
};
let bound_b = match bind_bootstrap_receipt(whole_b, receipt_b) {
Ok(value) => value,
Err(_) => panic!("same-source B receipt rejected"),
};
let (remainder_a, runtime_a) = take_runtime_bootstrap_receipt(bound_a);
let (remainder_b, runtime_b) = take_runtime_bootstrap_receipt(bound_b);
let (remainder_a, runtime_b) =
verify_runtime_bootstrap_binding(remainder_a, runtime_b).unwrap_err();
let (remainder_b, runtime_a) =
verify_runtime_bootstrap_binding(remainder_b, runtime_a).unwrap_err();
let verified_a = match verify_runtime_bootstrap_binding(remainder_a, runtime_a) {
Ok(value) => value,
Err(_) => panic!("original A pair rejected"),
};
let verified_b = match verify_runtime_bootstrap_binding(remainder_b, runtime_b) {
Ok(value) => value,
Err(_) => panic!("original B pair rejected"),
};
let (remainder_a, runtime_a) = recover_verified_runtime_bootstrap_binding(verified_a);
let (remainder_b, runtime_b) = recover_verified_runtime_bootstrap_binding(verified_b);
assert!(verify_runtime_bootstrap_binding(remainder_a, runtime_a).is_ok());
assert!(verify_runtime_bootstrap_binding(remainder_b, runtime_b).is_ok());
}
#[test]
fn listener_application_drift_returns_the_issuer() {
let (_application, listener) = BootstrapRendezvousIssuer::issue()
.freeze_application(GeneratedApplicationFreezeSource::new(
"app-a",
b"descriptor",
&["route"],
))
.unwrap();
let (error, _listener) = match listener.freeze_listener(ListenerStartupFreezeSource::new(
"app-b",
"127.0.0.1:8000".parse().unwrap(),
"127.0.0.1:9000".parse().unwrap(),
Duration::from_millis(5_000),
)) {
Ok(_) => panic!("foreign listener application accepted"),
Err(failure) => failure,
};
assert_eq!(error, BootstrapRendezvousError::ForeignSource);
}
}