secured_cam_sender_receiver/
secured_cam_sender_receiver.rs1use std::env;
36use std::f64::consts::PI;
37use std::fs;
38use std::path::Path;
39use std::sync::{mpsc, Arc, Mutex};
40use std::thread;
41use std::time::Duration;
42
43use rustflexstack::btp::router::Router as BTPRouter;
44use rustflexstack::facilities::ca_basic_service::{CooperativeAwarenessBasicService, VehicleData};
45use rustflexstack::facilities::local_dynamic_map::{
46 ldm_constants::ITS_AID_CAM,
47 ldm_storage::ItsDataObject,
48 ldm_types::{RegisterDataConsumerReq, RegisterDataProviderReq, RequestDataObjectsReq},
49 LdmFacility,
50};
51use rustflexstack::facilities::location_service::{GpsFix, LocationService};
52use rustflexstack::geonet::basic_header::{BasicHeader, BasicNH};
53use rustflexstack::geonet::gn_address::{GNAddress, M, MID, ST};
54use rustflexstack::geonet::mib::Mib;
55use rustflexstack::geonet::position_vector::LongPositionVector;
56use rustflexstack::geonet::router::Router as GNRouter;
57use rustflexstack::link_layer::raw_link_layer::RawLinkLayer;
58
59use rustflexstack::security::certificate::{Certificate, OwnCertificate};
60use rustflexstack::security::certificate_library::CertificateLibrary;
61use rustflexstack::security::ecdsa_backend::EcdsaBackend;
62use rustflexstack::security::sign_service::SignService;
63use rustflexstack::security::sn_sap::{ReportVerify, SNSignRequest, SNVerifyRequest};
64use rustflexstack::security::verify_service::{verify_message, VerifyEvent};
65
66const ITS_AID_CAM_VAL: u64 = 36;
67
68fn build_security_stack(at_index: usize) -> SignService {
71 let cert_dir = Path::new("certs");
72
73 let root_bytes = fs::read(cert_dir.join("root_ca.cert"))
75 .expect("root_ca.cert not found — run generate_certificate_chain first");
76 let aa_bytes = fs::read(cert_dir.join("aa.cert"))
77 .expect("aa.cert not found — run generate_certificate_chain first");
78
79 let root_ca = Certificate::from_bytes(&root_bytes, None);
80 let aa = Certificate::from_bytes(&aa_bytes, Some(root_ca.clone()));
81
82 let at1_cert_bytes = fs::read(cert_dir.join("at1.cert")).expect("at1.cert not found");
84 let at2_cert_bytes = fs::read(cert_dir.join("at2.cert")).expect("at2.cert not found");
85
86 let at1 = Certificate::from_bytes(&at1_cert_bytes, Some(aa.clone()));
87 let at2 = Certificate::from_bytes(&at2_cert_bytes, Some(aa.clone()));
88
89 let own_key_file = if at_index == 1 { "at1.key" } else { "at2.key" };
91 let key_bytes = fs::read(cert_dir.join(own_key_file))
92 .unwrap_or_else(|_| panic!("{} not found", own_key_file));
93
94 let mut backend = EcdsaBackend::new();
96 let key_id = backend.import_signing_key(&key_bytes);
97
98 let own_cert = if at_index == 1 {
99 at1.clone()
100 } else {
101 at2.clone()
102 };
103 let peer_cert = if at_index == 1 {
104 at2.clone()
105 } else {
106 at1.clone()
107 };
108
109 let cert_library = CertificateLibrary::new(
111 &backend,
112 vec![root_ca],
113 vec![aa],
114 vec![own_cert.clone(), peer_cert],
115 );
116
117 let mut sign_service = SignService::new(backend, cert_library);
119 let own = OwnCertificate::new(own_cert, key_id);
120 sign_service.add_own_certificate(own);
121
122 sign_service
123}
124
125fn main() {
126 let args: Vec<String> = env::args().collect();
128 let mut at_index: usize = 1;
129 let mut iface = "lo".to_string();
130
131 let mut i = 1;
132 while i < args.len() {
133 match args[i].as_str() {
134 "--at" => {
135 i += 1;
136 at_index = args[i].parse::<usize>().expect("--at must be 1 or 2");
137 assert!(at_index == 1 || at_index == 2, "--at must be 1 or 2");
138 }
139 "--iface" | "-i" => {
140 i += 1;
141 iface = args[i].clone();
142 }
143 other => {
144 iface = other.to_string();
146 }
147 }
148 i += 1;
149 }
150
151 println!("=== Secured CAM Sender/Receiver ===");
152 println!("AT index: {}", at_index);
153 println!("Interface: {}", iface);
154
155 let sign_service = build_security_stack(at_index);
157 println!(
158 "Security stack loaded. Own AT HashedId8: {:02x?}",
159 sign_service
160 .cert_library
161 .own_certificates
162 .keys()
163 .next()
164 .unwrap()
165 );
166
167 let sign_service = Arc::new(Mutex::new(sign_service));
169
170 let mac = {
172 use std::time::{SystemTime, UNIX_EPOCH};
173 let seed = SystemTime::now()
174 .duration_since(UNIX_EPOCH)
175 .unwrap()
176 .subsec_nanos()
177 ^ (at_index as u32 * 0x1234_5678); [
179 0x02u8,
180 (seed >> 24) as u8,
181 (seed >> 16) as u8,
182 (seed >> 8) as u8,
183 seed as u8,
184 at_index as u8,
185 ]
186 };
187 println!(
188 "MAC: {:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}",
189 mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]
190 );
191
192 let mut mib: Mib = Mib::new();
194 mib.itsGnLocalGnAddr = GNAddress::new(M::GnMulticast, ST::PassengerCar, MID::new(mac));
195 mib.itsGnBeaconServiceRetransmitTimer = 0;
196
197 let mut loc_svc = LocationService::new();
199 let gn_gps_rx = loc_svc.subscribe();
200 let ca_gps_rx = loc_svc.subscribe();
201
202 let (gn_handle, gn_to_ll_rx, gn_to_btp_rx) = GNRouter::spawn(mib, None, None, None);
204 let (btp_handle, btp_to_gn_rx) = BTPRouter::spawn(mib);
205
206 let (ll_to_gn_tx, ll_to_gn_rx) = mpsc::channel::<Vec<u8>>();
208
209 let (secured_ll_tx, secured_ll_rx) = mpsc::channel::<Vec<u8>>();
215 let sign_svc_tx = Arc::clone(&sign_service);
216 thread::spawn(move || {
217 while let Ok(packet) = gn_to_ll_rx.recv() {
218 if packet.len() < 4 {
219 let _ = secured_ll_tx.send(packet);
220 continue;
221 }
222 let bh_bytes: [u8; 4] = packet[0..4].try_into().unwrap();
224 let bh = BasicHeader::decode(bh_bytes);
225
226 match bh.nh {
227 BasicNH::CommonHeader if packet.len() > 4 => {
228 let inner_payload = &packet[4..];
230
231 let request = SNSignRequest {
232 tbs_message: inner_payload.to_vec(),
233 its_aid: ITS_AID_CAM_VAL,
234 permissions: vec![],
235 generation_location: None,
236 };
237
238 let sec_message = {
239 let mut svc = sign_svc_tx.lock().unwrap();
240 svc.sign_request(&request).sec_message
241 };
242
243 let mut new_bh = bh;
245 new_bh.nh = BasicNH::SecuredPacket;
246 let secured_packet: Vec<u8> = new_bh
247 .encode()
248 .iter()
249 .copied()
250 .chain(sec_message.iter().copied())
251 .collect();
252 let _ = secured_ll_tx.send(secured_packet);
253 }
254 _ => {
255 let _ = secured_ll_tx.send(packet);
257 }
258 }
259 }
260 });
261
262 let raw_ll = RawLinkLayer::new(ll_to_gn_tx, secured_ll_rx, &iface, mac);
264 raw_ll.start();
265
266 let gn_h_rx = gn_handle.clone();
272 let sign_svc_rx = Arc::clone(&sign_service);
273 thread::spawn(move || {
274 while let Ok(packet) = ll_to_gn_rx.recv() {
275 if packet.len() < 4 {
276 gn_h_rx.send_incoming_packet(packet);
277 continue;
278 }
279 let bh_bytes: [u8; 4] = packet[0..4].try_into().unwrap();
280 let bh = BasicHeader::decode(bh_bytes);
281
282 match bh.nh {
283 BasicNH::SecuredPacket if packet.len() > 4 => {
284 let sec_message = &packet[4..];
285 let request = SNVerifyRequest {
286 message: sec_message.to_vec(),
287 };
288
289 let (confirm, _events) = {
290 let mut svc = sign_svc_rx.lock().unwrap();
291 let svc = &mut *svc;
292 let result = verify_message(&request, &svc.backend, &mut svc.cert_library);
293 for event in &result.1 {
295 match event {
296 VerifyEvent::UnknownAt(h8) => {
297 svc.notify_unknown_at(h8);
298 }
299 VerifyEvent::InlineP2pcdRequest(h3s) => {
300 svc.notify_inline_p2pcd_request(h3s);
301 }
302 VerifyEvent::ReceivedCaCertificate(cert) => {
303 svc.notify_received_ca_certificate(cert.as_ref().clone());
304 }
305 }
306 }
307 result
308 };
309
310 if confirm.report == ReportVerify::Success {
311 println!(
312 "[SEC RX] Verified OK — ITS-AID={}, cert={:02x?}",
313 confirm.its_aid,
314 &confirm.certificate_id[..],
315 );
316 let mut new_bh = bh;
318 new_bh.nh = BasicNH::CommonHeader;
319 let plain_packet: Vec<u8> = new_bh
320 .encode()
321 .iter()
322 .copied()
323 .chain(confirm.plain_message.iter().copied())
324 .collect();
325 gn_h_rx.send_incoming_packet(plain_packet);
326 } else {
327 eprintln!("[SEC RX] Verification failed: {:?}", confirm.report);
328 }
329 }
330 _ => {
331 gn_h_rx.send_incoming_packet(packet);
333 }
334 }
335 }
336 });
337
338 let btp_h1 = btp_handle.clone();
340 thread::spawn(move || {
341 while let Ok(ind) = gn_to_btp_rx.recv() {
342 btp_h1.send_gn_data_indication(ind);
343 }
344 });
345
346 let gn_h2 = gn_handle.clone();
348 thread::spawn(move || {
349 while let Ok(req) = btp_to_gn_rx.recv() {
350 gn_h2.send_gn_data_request(req);
351 }
352 });
353
354 let gn_h3 = gn_handle.clone();
356 thread::spawn(move || {
357 while let Ok(fix) = gn_gps_rx.recv() {
358 let mut epv = LongPositionVector::decode([0u8; 24]);
359 epv.update_from_gps(
360 fix.latitude,
361 fix.longitude,
362 fix.speed_mps,
363 fix.heading_deg,
364 fix.pai,
365 );
366 gn_h3.update_position_vector(epv);
367 }
368 });
369
370 let ldm = LdmFacility::new(415_520_000, 21_340_000, 5_000.0);
372 ldm.if_ldm_3
373 .register_data_provider(RegisterDataProviderReq {
374 application_id: ITS_AID_CAM,
375 });
376 ldm.if_ldm_4
377 .register_data_consumer(RegisterDataConsumerReq {
378 application_id: ITS_AID_CAM,
379 });
380
381 let station_id = u32::from_be_bytes([mac[2], mac[3], mac[4], mac[5]]);
383 let vehicle_data = VehicleData {
384 station_id,
385 ..VehicleData::default()
386 };
387 let (ca_svc, _cam_rx) =
388 CooperativeAwarenessBasicService::new(btp_handle.clone(), vehicle_data, Some(ldm.clone()));
389 ca_svc.start(ca_gps_rx);
390
391 let ldm_reader = ldm.clone();
393 thread::spawn(move || loop {
394 thread::sleep(Duration::from_secs(1));
395 let resp = ldm_reader
396 .if_ldm_4
397 .request_data_objects(RequestDataObjectsReq {
398 application_id: ITS_AID_CAM,
399 data_object_types: vec![ITS_AID_CAM],
400 filter: None,
401 order: None,
402 max_results: None,
403 });
404 if resp.data_objects.is_empty() {
405 println!("[LDM] No CAM records in store");
406 } else {
407 println!("[LDM] {} CAM record(s):", resp.data_objects.len());
408 for entry in &resp.data_objects {
409 if let ItsDataObject::Cam(cam) = &entry.data_object {
410 let lat = cam
411 .cam
412 .cam_parameters
413 .basic_container
414 .reference_position
415 .latitude
416 .0 as f64
417 / 1e7;
418 let lon = cam
419 .cam
420 .cam_parameters
421 .basic_container
422 .reference_position
423 .longitude
424 .0 as f64
425 / 1e7;
426 println!(
427 " [LDM CAM] record={:>5} station={:>10} lat={:.5} lon={:.5}",
428 entry.record_id, cam.header.station_id.0, lat, lon,
429 );
430 }
431 }
432 }
433 });
434
435 thread::sleep(Duration::from_millis(100));
442 println!("Publishing GPS fixes @ 12.5 Hz (80 ms) with random trajectory — Ctrl+C to stop\n");
443
444 use rand::Rng;
445 let mut rng = rand::thread_rng();
446
447 const PERIOD_S: f64 = 0.08; const EARTH_R: f64 = 6_371_000.0;
449
450 let mut lat: f64 = 41.386931;
451 let mut lon: f64 = 2.112104;
452 let mut heading: f64 = rng.gen_range(0.0..360.0);
453 let mut speed: f64 = 10.0; loop {
456 thread::sleep(Duration::from_millis(80));
457
458 let delta: f64 = rng.gen_range(5.0..15.0) * if rng.gen_bool(0.5) { 1.0 } else { -1.0 };
460 heading = (heading + delta).rem_euclid(360.0);
461
462 speed = (speed + rng.gen_range(-0.5..0.5)).clamp(5.0, 20.0);
464
465 let d = speed * PERIOD_S;
467 let heading_r = heading * PI / 180.0;
468 let lat_r = lat * PI / 180.0;
469 lat += (d * heading_r.cos() / EARTH_R) * (180.0 / PI);
470 lon += (d * heading_r.sin() / (EARTH_R * lat_r.cos())) * (180.0 / PI);
471
472 loc_svc.publish(GpsFix {
473 latitude: lat,
474 longitude: lon,
475 altitude_m: 120.0,
476 speed_mps: speed,
477 heading_deg: heading,
478 pai: true,
479 });
480 }
481}