zerodds_rtps/
endpoint_security_info.rs1use crate::error::WireError;
21
22pub mod attrs {
28 pub const IS_VALID: u32 = 0x8000_0000;
31 pub const IS_READ_PROTECTED: u32 = 0x0000_0001;
33 pub const IS_WRITE_PROTECTED: u32 = 0x0000_0002;
35 pub const IS_DISCOVERY_PROTECTED: u32 = 0x0000_0004;
37 pub const IS_SUBMESSAGE_PROTECTED: u32 = 0x0000_0008;
39 pub const IS_PAYLOAD_PROTECTED: u32 = 0x0000_0010;
41 pub const IS_KEY_PROTECTED: u32 = 0x0000_0020;
43 pub const IS_LIVELINESS_PROTECTED: u32 = 0x0000_0040;
45}
46
47pub mod plugin_attrs {
57 pub const IS_VALID: u32 = 0x8000_0000;
59 pub const IS_SUBMESSAGE_ENCRYPTED: u32 = 0x0000_0001;
61 pub const IS_SUBMESSAGE_ORIGIN_AUTHENTICATED: u32 = 0x0000_0002;
63 pub const IS_PAYLOAD_ENCRYPTED: u32 = 0x0000_0004;
65}
66
67#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
77pub struct EndpointSecurityInfo {
78 pub endpoint_security_attributes: u32,
80 pub plugin_endpoint_security_attributes: u32,
83}
84
85impl EndpointSecurityInfo {
86 pub const WIRE_SIZE: usize = 8;
88
89 #[must_use]
93 pub const fn is_valid(&self) -> bool {
94 (self.endpoint_security_attributes & attrs::IS_VALID) != 0
95 && (self.plugin_endpoint_security_attributes & plugin_attrs::IS_VALID) != 0
96 }
97
98 #[must_use]
102 pub const fn plain() -> Self {
103 Self {
104 endpoint_security_attributes: attrs::IS_VALID,
105 plugin_endpoint_security_attributes: plugin_attrs::IS_VALID,
106 }
107 }
108
109 #[must_use]
111 pub const fn is_submessage_protected(&self) -> bool {
112 (self.endpoint_security_attributes & attrs::IS_SUBMESSAGE_PROTECTED) != 0
113 }
114
115 #[must_use]
117 pub const fn is_payload_protected(&self) -> bool {
118 (self.endpoint_security_attributes & attrs::IS_PAYLOAD_PROTECTED) != 0
119 }
120
121 #[must_use]
123 pub const fn is_submessage_encrypted(&self) -> bool {
124 (self.plugin_endpoint_security_attributes & plugin_attrs::IS_SUBMESSAGE_ENCRYPTED) != 0
125 }
126
127 #[must_use]
130 pub const fn is_submessage_origin_authenticated(&self) -> bool {
131 (self.plugin_endpoint_security_attributes
132 & plugin_attrs::IS_SUBMESSAGE_ORIGIN_AUTHENTICATED)
133 != 0
134 }
135
136 #[must_use]
138 pub const fn is_payload_encrypted(&self) -> bool {
139 (self.plugin_endpoint_security_attributes & plugin_attrs::IS_PAYLOAD_ENCRYPTED) != 0
140 }
141
142 #[must_use]
144 pub fn to_bytes(&self, little_endian: bool) -> [u8; Self::WIRE_SIZE] {
145 let mut out = [0u8; Self::WIRE_SIZE];
146 let (a, p) = if little_endian {
147 (
148 self.endpoint_security_attributes.to_le_bytes(),
149 self.plugin_endpoint_security_attributes.to_le_bytes(),
150 )
151 } else {
152 (
153 self.endpoint_security_attributes.to_be_bytes(),
154 self.plugin_endpoint_security_attributes.to_be_bytes(),
155 )
156 };
157 out[..4].copy_from_slice(&a);
158 out[4..].copy_from_slice(&p);
159 out
160 }
161
162 pub fn from_bytes(bytes: &[u8], little_endian: bool) -> Result<Self, WireError> {
168 if bytes.len() < Self::WIRE_SIZE {
169 return Err(WireError::UnexpectedEof {
170 needed: Self::WIRE_SIZE,
171 offset: 0,
172 });
173 }
174 let mut a = [0u8; 4];
175 a.copy_from_slice(&bytes[..4]);
176 let mut p = [0u8; 4];
177 p.copy_from_slice(&bytes[4..8]);
178 let (attrs_raw, plugin_raw) = if little_endian {
179 (u32::from_le_bytes(a), u32::from_le_bytes(p))
180 } else {
181 (u32::from_be_bytes(a), u32::from_be_bytes(p))
182 };
183 Ok(Self {
184 endpoint_security_attributes: attrs_raw,
185 plugin_endpoint_security_attributes: plugin_raw,
186 })
187 }
188}
189
190#[cfg(test)]
195mod tests {
196 #![allow(clippy::expect_used, clippy::unwrap_used)]
197 use super::*;
198
199 #[test]
200 fn plain_is_valid_with_no_protection_bits() {
201 let p = EndpointSecurityInfo::plain();
202 assert!(p.is_valid());
203 assert!(!p.is_submessage_protected());
204 assert!(!p.is_payload_protected());
205 assert!(!p.is_submessage_encrypted());
206 assert!(!p.is_payload_encrypted());
207 assert!(!p.is_submessage_origin_authenticated());
208 }
209
210 #[test]
211 fn default_is_not_valid() {
212 let d = EndpointSecurityInfo::default();
213 assert!(
214 !d.is_valid(),
215 "default == alle null == is_valid muss false sein"
216 );
217 }
218
219 #[test]
220 fn roundtrip_le() {
221 let info = EndpointSecurityInfo {
222 endpoint_security_attributes: attrs::IS_VALID
223 | attrs::IS_SUBMESSAGE_PROTECTED
224 | attrs::IS_PAYLOAD_PROTECTED,
225 plugin_endpoint_security_attributes: plugin_attrs::IS_VALID
226 | plugin_attrs::IS_SUBMESSAGE_ENCRYPTED
227 | plugin_attrs::IS_PAYLOAD_ENCRYPTED,
228 };
229 let bytes = info.to_bytes(true);
230 let decoded = EndpointSecurityInfo::from_bytes(&bytes, true).unwrap();
231 assert_eq!(decoded, info);
232 }
233
234 #[test]
235 fn roundtrip_be() {
236 let info = EndpointSecurityInfo {
237 endpoint_security_attributes: attrs::IS_VALID | attrs::IS_SUBMESSAGE_PROTECTED,
238 plugin_endpoint_security_attributes: plugin_attrs::IS_VALID
239 | plugin_attrs::IS_SUBMESSAGE_ENCRYPTED,
240 };
241 let bytes = info.to_bytes(false);
242 let decoded = EndpointSecurityInfo::from_bytes(&bytes, false).unwrap();
243 assert_eq!(decoded, info);
244 }
245
246 #[test]
247 fn wire_size_is_eight_bytes() {
248 let info = EndpointSecurityInfo::plain();
249 assert_eq!(info.to_bytes(true).len(), 8);
250 }
251
252 #[test]
253 fn decode_rejects_short_input() {
254 let err = EndpointSecurityInfo::from_bytes(&[0u8; 7], true).unwrap_err();
255 assert!(matches!(err, WireError::UnexpectedEof { .. }));
256 }
257
258 #[test]
259 fn encoded_bytes_le_match_spec_layout() {
260 let info = EndpointSecurityInfo {
261 endpoint_security_attributes: 0x8000_0008,
262 plugin_endpoint_security_attributes: 0x8000_0001,
263 };
264 let bytes = info.to_bytes(true);
265 assert_eq!(bytes, [0x08, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80]);
267 }
268
269 #[test]
270 fn protection_bit_accessors_read_correctly() {
271 let info = EndpointSecurityInfo {
272 endpoint_security_attributes: attrs::IS_VALID | attrs::IS_SUBMESSAGE_PROTECTED,
273 plugin_endpoint_security_attributes: plugin_attrs::IS_VALID
274 | plugin_attrs::IS_SUBMESSAGE_ENCRYPTED
275 | plugin_attrs::IS_SUBMESSAGE_ORIGIN_AUTHENTICATED,
276 };
277 assert!(info.is_submessage_protected());
278 assert!(!info.is_payload_protected());
279 assert!(info.is_submessage_encrypted());
280 assert!(info.is_submessage_origin_authenticated());
281 assert!(!info.is_payload_encrypted());
282 }
283
284 #[test]
285 fn is_valid_requires_both_masks() {
286 let only_attrs = EndpointSecurityInfo {
287 endpoint_security_attributes: attrs::IS_VALID,
288 plugin_endpoint_security_attributes: 0,
289 };
290 assert!(!only_attrs.is_valid());
291
292 let only_plugin = EndpointSecurityInfo {
293 endpoint_security_attributes: 0,
294 plugin_endpoint_security_attributes: plugin_attrs::IS_VALID,
295 };
296 assert!(!only_plugin.is_valid());
297
298 let both = EndpointSecurityInfo::plain();
299 assert!(both.is_valid());
300 }
301}