cat / goq
clone

turns a linux box into a private steam streaming appliance: plug in, tap your key, play from another computer

1-n-sigls
goq / crates / sigil-host / src / bin / sigil-relay-spike.rs
sigil-relay-spike.rs 10.3K
1use std::collections::VecDeque;2use std::sync::Arc;3use std::time::Instant;45use anyhow::{Context, Result, ensure};6use clap::Parser;7use iroh::SecretKey;8use sigil_protocol::{9 AuthenticatedMediaObject, FrameFlags, MediaFrame, MediaFrameHeader, MediaGenerationSigningKey,10 MediaObjectCoordinates, MediaTrack, SignedSubscriptionCapability, SubscriptionClaims,11 SubscriptionTracks, encode_media_frame_object,12};1314const VIDEO_FPS: u64 = 60;15const AUDIO_OBJECTS_PER_SECOND: u64 = 50;16const VIDEO_PAYLOAD_BYTES: usize = 64 * 1024;17const AUDIO_PAYLOAD_BYTES: usize = 200;18const RELAY_QUEUE_CAPACITY: usize = 4;19const AUTHORIZATION_REVISION: u64 = 1;2021#[derive(Debug, Parser)]22#[command(23 name = "sigil-relay-spike",24 about = "Bounded relay-ready media authentication spike"25)]26struct Args {27 #[arg(long, default_value = "1280x800@60")]28 video: String,29 #[arg(long, default_value = "opus-48k-stereo")]30 audio: String,31 #[arg(long, default_value_t = 300, value_parser = clap::value_parser!(u64).range(1..=300))]32 duration_seconds: u64,33}3435#[derive(Default)]36struct BenchmarkEvidence {37 host_bytes: u64,38 downstream_bytes: u64,39 sign_nanos: u128,40 verification_micros: Vec<u64>,41 verification_nanos: u128,42 maximum_relay_queue: usize,43 first_object: Option<(Vec<u8>, MediaObjectCoordinates)>,44}4546fn main() -> Result<()> {47 let args = Args::parse();48 ensure!(49 args.video == "1280x800@60",50 "only the fixed 1280x800@60 target is supported"51 );52 ensure!(53 args.audio == "opus-48k-stereo",54 "only Opus 48 kHz stereo is supported"55 );5657 let host = SecretKey::from_bytes(&[0x31; 32]);58 let portal_a = SecretKey::from_bytes(&[0x41; 32]);59 let portal_b = SecretKey::from_bytes(&[0x42; 32]);60 let generation_id = 7_u64;61 let issued_at = 1_700_000_000_u64;62 let generation = MediaGenerationSigningKey::from_bytes(&[0x51; 32]);63 let certificate = generation.certify(64 *host.public().as_bytes(),65 &host.to_bytes(),66 generation_id,67 issued_at,68 issued_at + 600,69 )?;70 certificate.verify_binding(*host.public().as_bytes(), generation_id, issued_at + 1)?;71 let subscription = SignedSubscriptionCapability::issue(72 SubscriptionClaims::new(73 *host.public().as_bytes(),74 generation_id,75 *portal_b.public().as_bytes(),76 SubscriptionTracks::VIDEO_H264.union(SubscriptionTracks::AUDIO_OPUS),77 AUTHORIZATION_REVISION,78 issued_at,79 issued_at + 300,80 [0x61; 32],81 1,82 )?,83 &host.to_bytes(),84 )?;85 subscription.verify_binding(86 *host.public().as_bytes(),87 generation_id,88 *portal_b.public().as_bytes(),89 SubscriptionTracks::VIDEO_H264.union(SubscriptionTracks::AUDIO_OPUS),90 AUTHORIZATION_REVISION,91 issued_at + 1,92 )?;9394 let mut evidence = BenchmarkEvidence {95 verification_micros: Vec::with_capacity(96 usize::try_from(args.duration_seconds * (VIDEO_FPS + AUDIO_OBJECTS_PER_SECOND))97 .context("benchmark sample capacity overflow")?,98 ),99 ..BenchmarkEvidence::default()100 };101 let video_payload: Arc<[u8]> = Arc::from(vec![0x65; VIDEO_PAYLOAD_BYTES]);102 let audio_payload = vec![0x7a; AUDIO_PAYLOAD_BYTES];103 let mut relay_queue = VecDeque::with_capacity(RELAY_QUEUE_CAPACITY);104105 for sequence in 0..args.duration_seconds * VIDEO_FPS {106 let group_id = sequence / VIDEO_FPS;107 let object_id = u32::try_from(sequence % VIDEO_FPS).expect("video object id is bounded");108 let flags = if object_id == 0 {109 FrameFlags::KEYFRAME.union(FrameFlags::CODEC_CONFIG)110 } else {111 FrameFlags::NONE112 };113 let frame = MediaFrame::new(114 MediaFrameHeader::h264(115 1280,116 800,117 video_payload.len(),118 sequence,119 sequence * 1_000_000 / VIDEO_FPS,120 i64::try_from(sequence * 1_000_000 / VIDEO_FPS).expect("bounded timestamp"),121 flags,122 )?,123 Arc::clone(&video_payload),124 )?;125 let payload = encode_media_frame_object(&frame)?;126 authenticate_relay_verify(127 &generation,128 &certificate,129 MediaObjectCoordinates {130 generation_id,131 track: MediaTrack::VideoH264,132 group_id,133 object_id,134 flags: u16::from(flags.bits()),135 },136 &payload,137 &mut relay_queue,138 &mut evidence,139 )?;140 }141142 for sequence in 0..args.duration_seconds * AUDIO_OBJECTS_PER_SECOND {143 authenticate_relay_verify(144 &generation,145 &certificate,146 MediaObjectCoordinates {147 generation_id,148 track: MediaTrack::AudioOpus,149 group_id: sequence / AUDIO_OBJECTS_PER_SECOND,150 object_id: u32::try_from(sequence % AUDIO_OBJECTS_PER_SECOND)151 .expect("audio object id is bounded"),152 flags: 0,153 },154 &audio_payload,155 &mut relay_queue,156 &mut evidence,157 )?;158 }159160 ensure!(relay_queue.is_empty(), "relay queue did not drain");161 let (first_object, first_coordinates) = evidence162 .first_object163 .as_ref()164 .context("benchmark did not produce an authenticated object")?;165 let mut tampered = first_object.clone();166 *tampered167 .last_mut()168 .context("authenticated object was empty")? ^= 1;169 ensure!(170 AuthenticatedMediaObject::verify(&tampered, &certificate, *first_coordinates).is_err(),171 "tampered relayed media was trusted"172 );173 ensure!(174 subscription175 .verify_binding(176 *host.public().as_bytes(),177 generation_id,178 *portal_a.public().as_bytes(),179 SubscriptionTracks::VIDEO_H264,180 AUTHORIZATION_REVISION,181 issued_at + 1,182 )183 .is_err(),184 "wrong subscriber was authorized"185 );186 ensure!(187 subscription188 .verify_binding(189 *host.public().as_bytes(),190 generation_id,191 *portal_b.public().as_bytes(),192 SubscriptionTracks::VIDEO_H264,193 AUTHORIZATION_REVISION,194 issued_at + 301,195 )196 .is_err(),197 "expired subscription was authorized"198 );199 AuthenticatedMediaObject::verify(first_object, &certificate, *first_coordinates)200 .context("direct-host fallback could not verify the relayed object")?;201202 evidence.verification_micros.sort_unstable();203 let p95_index = evidence204 .verification_micros205 .len()206 .saturating_mul(95)207 .div_ceil(100)208 - 1;209 let verify_p95_us = evidence.verification_micros[p95_index];210 let host_direct_bytes = evidence.host_bytes.saturating_mul(2);211 let host_share_percent = evidence212 .host_bytes213 .saturating_mul(100)214 .checked_div(host_direct_bytes)215 .unwrap_or(0);216 let savings_percent = 100_u64.saturating_sub(host_share_percent);217 let sign_cpu_percent =218 evidence.sign_nanos as f64 / (args.duration_seconds as f64 * 1_000_000_000_f64) * 100.0;219 let verify_cpu_percent = evidence.verification_nanos as f64220 / (args.duration_seconds as f64 * 1_000_000_000_f64)221 * 100.0;222223 println!("relay_spike=ok");224 println!("authentication_mode=ed25519-v1");225 println!("decision=defer-production-mesh");226 println!("virtual_duration_seconds={}", args.duration_seconds);227 println!("video_objects={}", args.duration_seconds * VIDEO_FPS);228 println!(229 "audio_objects={}",230 args.duration_seconds * AUDIO_OBJECTS_PER_SECOND231 );232 println!("host_upload_bytes={}", evidence.host_bytes);233 println!("direct_two_viewer_host_upload_bytes={host_direct_bytes}");234 println!("host_upload_savings_percent={savings_percent}");235 println!("relay_downstream_bytes={}", evidence.downstream_bytes);236 println!("sign_cpu_percent_of_virtual_realtime={sign_cpu_percent:.4}");237 println!("verify_cpu_percent_of_virtual_realtime={verify_cpu_percent:.4}");238 println!("verify_p95_us={verify_p95_us}");239 println!("added_local_latency_p95_us={verify_p95_us}");240 println!("maximum_relay_queue={}", evidence.maximum_relay_queue);241 println!("tamper_rejected=ok");242 println!("wrong_subscriber_rejected=ok");243 println!("expired_subscription_rejected=ok");244 println!("relay_loss_direct_fallback=ok");245 println!("withheld_media_trusted=0");246 Ok(())247}248249fn authenticate_relay_verify(250 generation: &MediaGenerationSigningKey,251 certificate: &sigil_protocol::SignedMediaGenerationCertificate,252 coordinates: MediaObjectCoordinates,253 payload: &[u8],254 relay_queue: &mut VecDeque<Vec<u8>>,255 evidence: &mut BenchmarkEvidence,256) -> Result<()> {257 ensure!(258 relay_queue.len() < RELAY_QUEUE_CAPACITY,259 "relay queue exceeded its bound"260 );261 let sign_started = Instant::now();262 let object = generation.authenticate(coordinates, payload)?.into_bytes();263 evidence.sign_nanos = evidence264 .sign_nanos265 .saturating_add(sign_started.elapsed().as_nanos());266 evidence.host_bytes = evidence267 .host_bytes268 .saturating_add(u64::try_from(object.len()).unwrap_or(u64::MAX));269 if evidence.first_object.is_none() {270 evidence.first_object = Some((object.clone(), coordinates));271 }272 relay_queue.push_back(object);273 evidence.maximum_relay_queue = evidence.maximum_relay_queue.max(relay_queue.len());274275 let relayed = relay_queue276 .pop_front()277 .context("relay queue lost an object")?;278 evidence.downstream_bytes = evidence279 .downstream_bytes280 .saturating_add(u64::try_from(relayed.len()).unwrap_or(u64::MAX));281 let verify_started = Instant::now();282 let verified = AuthenticatedMediaObject::verify(&relayed, certificate, coordinates)?;283 let verify_elapsed = verify_started.elapsed();284 evidence.verification_nanos = evidence285 .verification_nanos286 .saturating_add(verify_elapsed.as_nanos());287 evidence288 .verification_micros289 .push(u64::try_from(verify_elapsed.as_micros()).unwrap_or(u64::MAX));290 ensure!(verified == payload, "verified relay payload changed bytes");291 Ok(())292}