1extern crate alloc;
43
44use alloc::string::{String, ToString};
45use alloc::vec::Vec;
46
47use crate::error::{SecurityError, SecurityErrorKind, SecurityResult};
48
49#[derive(Debug, Clone, PartialEq, Eq, Default)]
53pub struct WireProperty {
54 pub name: String,
56 pub value: String,
58}
59
60impl WireProperty {
61 #[must_use]
63 pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
64 Self {
65 name: name.into(),
66 value: value.into(),
67 }
68 }
69}
70
71#[derive(Debug, Clone, PartialEq, Eq, Default)]
74pub struct BinaryProperty {
75 pub name: String,
77 pub value: Vec<u8>,
79}
80
81impl BinaryProperty {
82 #[must_use]
84 pub fn new(name: impl Into<String>, value: impl Into<Vec<u8>>) -> Self {
85 Self {
86 name: name.into(),
87 value: value.into(),
88 }
89 }
90}
91
92#[derive(Debug, Clone, PartialEq, Eq, Default)]
96pub struct DataHolder {
97 pub class_id: String,
99 pub properties: Vec<WireProperty>,
101 pub binary_properties: Vec<BinaryProperty>,
103}
104
105impl DataHolder {
106 #[must_use]
108 pub fn new(class_id: impl Into<String>) -> Self {
109 Self {
110 class_id: class_id.into(),
111 properties: Vec::new(),
112 binary_properties: Vec::new(),
113 }
114 }
115
116 #[must_use]
118 pub fn with_property(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
119 self.properties.push(WireProperty::new(name, value));
120 self
121 }
122
123 #[must_use]
125 pub fn with_binary_property(
126 mut self,
127 name: impl Into<String>,
128 value: impl Into<Vec<u8>>,
129 ) -> Self {
130 self.binary_properties
131 .push(BinaryProperty::new(name, value));
132 self
133 }
134
135 pub fn set_property(&mut self, name: impl Into<String>, value: impl Into<String>) {
140 let n = name.into();
141 if let Some(existing) = self.properties.iter_mut().find(|p| p.name == n) {
142 existing.value = value.into();
143 } else {
144 self.properties.push(WireProperty {
145 name: n,
146 value: value.into(),
147 });
148 }
149 }
150
151 pub fn set_binary_property(&mut self, name: impl Into<String>, value: impl Into<Vec<u8>>) {
153 let n = name.into();
154 if let Some(existing) = self.binary_properties.iter_mut().find(|p| p.name == n) {
155 existing.value = value.into();
156 } else {
157 self.binary_properties.push(BinaryProperty {
158 name: n,
159 value: value.into(),
160 });
161 }
162 }
163
164 #[must_use]
166 pub fn property(&self, name: &str) -> Option<&str> {
167 self.properties
168 .iter()
169 .find(|p| p.name == name)
170 .map(|p| p.value.as_str())
171 }
172
173 #[must_use]
175 pub fn binary_property(&self, name: &str) -> Option<&[u8]> {
176 self.binary_properties
177 .iter()
178 .find(|p| p.name == name)
179 .map(|p| p.value.as_slice())
180 }
181
182 #[must_use]
186 pub fn to_cdr_le(&self) -> Vec<u8> {
187 encode(self, true)
188 }
189
190 #[must_use]
192 pub fn to_cdr_be(&self) -> Vec<u8> {
193 encode(self, false)
194 }
195
196 pub fn from_cdr_le(bytes: &[u8]) -> SecurityResult<Self> {
202 decode(bytes, true)
203 }
204
205 pub fn from_cdr_be(bytes: &[u8]) -> SecurityResult<Self> {
210 decode(bytes, false)
211 }
212}
213
214pub type IdentityToken = DataHolder;
221
222pub type PermissionsToken = DataHolder;
225
226pub type IdentityStatusToken = DataHolder;
229
230pub type PermissionsCredentialToken = DataHolder;
233
234pub type AuthRequestMessageToken = DataHolder;
236
237pub type HandshakeMessageToken = DataHolder;
239
240pub type CryptoToken = DataHolder;
242
243pub mod class_id {
249 pub const AUTH_PKI_DH_V12: &str = "DDS:Auth:PKI-DH:1.2";
251 pub const ACCESS_PERMISSIONS_V12: &str = "DDS:Access:Permissions:1.2";
253 pub const CRYPTO_AES_GCM_GMAC_V12: &str = "DDS:Crypto:AES-GCM-GMAC:1.2";
255 pub const ACCESS_PERMISSIONS_CREDENTIAL: &str = "DDS:Access:PermissionsCredential";
257}
258
259pub mod prop {
261 pub const CERT_SN: &str = "dds.cert.sn";
263 pub const CERT_ALGO: &str = "dds.cert.algo";
265 pub const CA_SN: &str = "dds.ca.sn";
267 pub const CA_ALGO: &str = "dds.ca.algo";
269 pub const PERM_CA_SN: &str = "dds.perm_ca.sn";
272 pub const PERM_CA_ALGO: &str = "dds.perm_ca.algo";
274}
275
276impl IdentityToken {
277 #[must_use]
281 pub fn pki_dh_v12(
282 cert_sn: impl Into<String>,
283 cert_algo: impl Into<String>,
284 ca_sn: impl Into<String>,
285 ca_algo: impl Into<String>,
286 ) -> Self {
287 DataHolder::new(class_id::AUTH_PKI_DH_V12)
288 .with_property(prop::CERT_SN, cert_sn)
289 .with_property(prop::CERT_ALGO, cert_algo)
290 .with_property(prop::CA_SN, ca_sn)
291 .with_property(prop::CA_ALGO, ca_algo)
292 }
293
294 #[must_use]
298 pub fn permissions_v12(perm_ca_sn: impl Into<String>, perm_ca_algo: impl Into<String>) -> Self {
299 DataHolder::new(class_id::ACCESS_PERMISSIONS_V12)
300 .with_property(prop::PERM_CA_SN, perm_ca_sn)
301 .with_property(prop::PERM_CA_ALGO, perm_ca_algo)
302 }
303}
304
305const MAX_TOKEN_BYTES: usize = 64 * 1024;
325const MAX_PROPS: u32 = 256;
326const MAX_BIN_PROPS: u32 = 256;
327const MAX_STRING_LEN: u32 = 8 * 1024;
328const MAX_BINARY_LEN: u32 = 8 * 1024;
329
330fn encode(d: &DataHolder, le: bool) -> Vec<u8> {
331 let mut out = Vec::with_capacity(64);
332 encode_string(&mut out, &d.class_id, le);
333 encode_u32(&mut out, d.properties.len() as u32, le);
334 for p in &d.properties {
335 encode_string(&mut out, &p.name, le);
336 encode_string(&mut out, &p.value, le);
337 }
338 encode_u32(&mut out, d.binary_properties.len() as u32, le);
339 for p in &d.binary_properties {
340 encode_string(&mut out, &p.name, le);
341 encode_octet_seq(&mut out, &p.value, le);
342 }
343 out
344}
345
346fn decode(bytes: &[u8], le: bool) -> SecurityResult<DataHolder> {
347 if bytes.len() > MAX_TOKEN_BYTES {
348 return Err(SecurityError::new(
349 SecurityErrorKind::BadArgument,
350 "token: payload exceeds DoS cap",
351 ));
352 }
353 let mut cur = Cursor::new(bytes);
354 let class_id = cur.read_string(le)?;
355 let prop_count = cur.read_u32(le)?;
356 if prop_count > MAX_PROPS {
357 return Err(SecurityError::new(
358 SecurityErrorKind::BadArgument,
359 "token: property count exceeds cap",
360 ));
361 }
362 let mut properties = Vec::with_capacity(prop_count as usize);
363 for _ in 0..prop_count {
364 let name = cur.read_string(le)?;
365 let value = cur.read_string(le)?;
366 properties.push(WireProperty { name, value });
367 }
368 let bin_count = cur.read_u32(le)?;
369 if bin_count > MAX_BIN_PROPS {
370 return Err(SecurityError::new(
371 SecurityErrorKind::BadArgument,
372 "token: binary_property count exceeds cap",
373 ));
374 }
375 let mut binary_properties = Vec::with_capacity(bin_count as usize);
376 for _ in 0..bin_count {
377 let name = cur.read_string(le)?;
378 let value = cur.read_octet_seq(le)?;
379 binary_properties.push(BinaryProperty { name, value });
380 }
381 Ok(DataHolder {
382 class_id,
383 properties,
384 binary_properties,
385 })
386}
387
388fn align_to(out: &mut Vec<u8>, n: usize) {
389 let pad = (n - out.len() % n) % n;
390 for _ in 0..pad {
391 out.push(0);
392 }
393}
394
395fn encode_u32(out: &mut Vec<u8>, v: u32, le: bool) {
396 align_to(out, 4);
397 if le {
398 out.extend_from_slice(&v.to_le_bytes());
399 } else {
400 out.extend_from_slice(&v.to_be_bytes());
401 }
402}
403
404fn encode_string(out: &mut Vec<u8>, s: &str, le: bool) {
405 let bytes = s.as_bytes();
406 let len = (bytes.len() + 1) as u32;
407 encode_u32(out, len, le);
408 out.extend_from_slice(bytes);
409 out.push(0);
410}
411
412fn encode_octet_seq(out: &mut Vec<u8>, v: &[u8], le: bool) {
413 encode_u32(out, v.len() as u32, le);
414 out.extend_from_slice(v);
415}
416
417struct Cursor<'a> {
418 buf: &'a [u8],
419 pos: usize,
420}
421
422impl<'a> Cursor<'a> {
423 fn new(buf: &'a [u8]) -> Self {
424 Self { buf, pos: 0 }
425 }
426
427 fn align_to(&mut self, n: usize) {
428 let pad = (n - self.pos % n) % n;
429 self.pos = self.pos.saturating_add(pad);
430 }
431
432 fn read_u32(&mut self, le: bool) -> SecurityResult<u32> {
433 self.align_to(4);
434 if self.pos + 4 > self.buf.len() {
435 return Err(SecurityError::new(
436 SecurityErrorKind::BadArgument,
437 "token: truncated u32",
438 ));
439 }
440 let raw = [
441 self.buf[self.pos],
442 self.buf[self.pos + 1],
443 self.buf[self.pos + 2],
444 self.buf[self.pos + 3],
445 ];
446 self.pos += 4;
447 Ok(if le {
448 u32::from_le_bytes(raw)
449 } else {
450 u32::from_be_bytes(raw)
451 })
452 }
453
454 fn read_string(&mut self, le: bool) -> SecurityResult<String> {
455 let len = self.read_u32(le)?;
456 if len > MAX_STRING_LEN {
457 return Err(SecurityError::new(
458 SecurityErrorKind::BadArgument,
459 "token: string exceeds cap",
460 ));
461 }
462 if len == 0 {
463 return Ok(String::new());
464 }
465 if self.pos + len as usize > self.buf.len() {
466 return Err(SecurityError::new(
467 SecurityErrorKind::BadArgument,
468 "token: truncated string",
469 ));
470 }
471 let body = &self.buf[self.pos..self.pos + len as usize];
472 self.pos += len as usize;
473 if let Some((_, rest)) = body.split_last() {
475 if body.last() != Some(&0) {
476 return Err(SecurityError::new(
477 SecurityErrorKind::BadArgument,
478 "token: string missing trailing NUL",
479 ));
480 }
481 let s = core::str::from_utf8(rest).map_err(|_| {
482 SecurityError::new(SecurityErrorKind::BadArgument, "token: string not UTF-8")
483 })?;
484 Ok(s.to_string())
485 } else {
486 Err(SecurityError::new(
487 SecurityErrorKind::BadArgument,
488 "token: zero-length string body",
489 ))
490 }
491 }
492
493 fn read_octet_seq(&mut self, le: bool) -> SecurityResult<Vec<u8>> {
494 let len = self.read_u32(le)?;
495 if len > MAX_BINARY_LEN {
496 return Err(SecurityError::new(
497 SecurityErrorKind::BadArgument,
498 "token: binary value exceeds cap",
499 ));
500 }
501 if self.pos + len as usize > self.buf.len() {
502 return Err(SecurityError::new(
503 SecurityErrorKind::BadArgument,
504 "token: truncated binary",
505 ));
506 }
507 let v = self.buf[self.pos..self.pos + len as usize].to_vec();
508 self.pos += len as usize;
509 Ok(v)
510 }
511}
512
513#[cfg(test)]
514#[allow(clippy::expect_used, clippy::unwrap_used)]
515mod tests {
516 use super::*;
517
518 #[test]
519 fn empty_data_holder_roundtrip_le() {
520 let dh = DataHolder::new("DDS:Auth:PKI-DH:1.2");
521 let bytes = dh.to_cdr_le();
522 let back = DataHolder::from_cdr_le(&bytes).unwrap();
523 assert_eq!(dh, back);
524 }
525
526 #[test]
527 fn empty_data_holder_roundtrip_be() {
528 let dh = DataHolder::new("DDS:Auth:PKI-DH:1.2");
529 let bytes = dh.to_cdr_be();
530 let back = DataHolder::from_cdr_be(&bytes).unwrap();
531 assert_eq!(dh, back);
532 }
533
534 #[test]
535 fn pki_dh_identity_token_roundtrip() {
536 let tok =
537 IdentityToken::pki_dh_v12("01:23:45:67", "ECDSA-SHA256", "FA:CE:0B:01", "RSA-SHA256");
538 let bytes = tok.to_cdr_le();
539 let back = DataHolder::from_cdr_le(&bytes).unwrap();
540 assert_eq!(tok, back);
541 assert_eq!(back.class_id, "DDS:Auth:PKI-DH:1.2");
542 assert_eq!(back.property("dds.cert.sn"), Some("01:23:45:67"));
543 assert_eq!(back.property("dds.cert.algo"), Some("ECDSA-SHA256"));
544 assert_eq!(back.property("dds.ca.sn"), Some("FA:CE:0B:01"));
545 assert_eq!(back.property("dds.ca.algo"), Some("RSA-SHA256"));
546 assert!(back.binary_properties.is_empty());
547 }
548
549 #[test]
550 fn permissions_token_roundtrip() {
551 let tok = IdentityToken::permissions_v12("DE:AD:BE:EF", "ECDSA-SHA256");
552 let le = tok.to_cdr_le();
553 let be = tok.to_cdr_be();
554 assert_eq!(tok, DataHolder::from_cdr_le(&le).unwrap());
555 assert_eq!(tok, DataHolder::from_cdr_be(&be).unwrap());
556 assert_ne!(le, be, "BE/LE Streams unterscheiden sich");
557 }
558
559 #[test]
560 fn token_with_binary_property_roundtrip() {
561 let tok = DataHolder::new("DDS:Auth:PKI-DH:1.2")
562 .with_property("dds.cert.sn", "01:23")
563 .with_binary_property("dds.cert.bytes", vec![0xCA, 0xFE, 0xBA, 0xBE, 0xDE]);
564 let bytes = tok.to_cdr_le();
565 let back = DataHolder::from_cdr_le(&bytes).unwrap();
566 assert_eq!(tok, back);
567 assert_eq!(
568 back.binary_property("dds.cert.bytes"),
569 Some(&[0xCA, 0xFE, 0xBA, 0xBE, 0xDE][..])
570 );
571 }
572
573 #[test]
574 fn cdr_le_layout_class_id_only() {
575 let dh = DataHolder::new("A");
582 let bytes = dh.to_cdr_le();
583 assert_eq!(
584 bytes,
585 vec![
586 0x02, 0x00, 0x00, 0x00, b'A', 0x00, 0x00, 0x00, 0, 0, 0, 0, 0, 0, 0, 0
587 ]
588 );
589 }
590
591 #[test]
592 fn truncated_buffer_is_error() {
593 let err = DataHolder::from_cdr_le(&[0x10, 0x00, 0x00]).unwrap_err();
594 assert_eq!(err.kind, SecurityErrorKind::BadArgument);
595 }
596
597 #[test]
598 fn property_count_cap_rejects_huge() {
599 let mut bytes = Vec::new();
601 encode_string(&mut bytes, "X", true);
602 encode_u32(&mut bytes, 1_000_000, true); let err = DataHolder::from_cdr_le(&bytes).unwrap_err();
604 assert_eq!(err.kind, SecurityErrorKind::BadArgument);
605 }
606
607 #[test]
608 fn missing_trailing_nul_rejected() {
609 let bytes = vec![0x01, 0x00, 0x00, 0x00, b'A'];
611 let err = DataHolder::from_cdr_le(&bytes).unwrap_err();
612 assert_eq!(err.kind, SecurityErrorKind::BadArgument);
613 }
614
615 #[test]
616 fn dos_cap_overall_payload() {
617 let big = vec![0u8; MAX_TOKEN_BYTES + 1];
618 let err = DataHolder::from_cdr_le(&big).unwrap_err();
619 assert_eq!(err.kind, SecurityErrorKind::BadArgument);
620 }
621}