1extern crate alloc;
44use alloc::vec::Vec;
45
46use crate::error::WireError;
47
48pub mod symmetric_bit {
54 pub const AES128: u32 = 1 << 0;
56 pub const AES256: u32 = 1 << 1;
58}
59
60pub mod digital_signature_bit {
62 pub const RSASSA_PSS_2048_SHA256: u32 = 1 << 0;
64 pub const RSASSA_PKCS1_V15_2048_SHA256: u32 = 1 << 1;
66 pub const ECDSA_P256_SHA256: u32 = 1 << 2;
68 pub const ECDSA_P384_SHA384: u32 = 1 << 3;
70}
71
72pub mod key_establishment_bit {
74 pub const DHE_MODP_2048_256: u32 = 1 << 0;
76 pub const ECDHE_CEUM_P256: u32 = 1 << 1;
78 pub const ECDHE_CEUM_P384: u32 = 1 << 2;
80}
81
82#[derive(Debug, Clone, Copy, PartialEq, Eq)]
89pub struct AlgorithmRequirements {
90 pub supported: u32,
92 pub required: u32,
96}
97
98impl AlgorithmRequirements {
99 pub const WIRE_SIZE: usize = 8;
101
102 #[must_use]
104 pub fn to_bytes(&self, little_endian: bool) -> [u8; 8] {
105 let mut out = [0u8; 8];
106 if little_endian {
107 out[0..4].copy_from_slice(&self.supported.to_le_bytes());
108 out[4..8].copy_from_slice(&self.required.to_le_bytes());
109 } else {
110 out[0..4].copy_from_slice(&self.supported.to_be_bytes());
111 out[4..8].copy_from_slice(&self.required.to_be_bytes());
112 }
113 out
114 }
115
116 pub fn from_bytes(bytes: &[u8], little_endian: bool) -> Result<Self, WireError> {
121 if bytes.len() < 8 {
122 return Err(WireError::ValueOutOfRange {
123 message: "AlgorithmRequirements: < 8 bytes",
124 });
125 }
126 let mut s = [0u8; 4];
127 let mut r = [0u8; 4];
128 s.copy_from_slice(&bytes[0..4]);
129 r.copy_from_slice(&bytes[4..8]);
130 Ok(if little_endian {
131 Self {
132 supported: u32::from_le_bytes(s),
133 required: u32::from_le_bytes(r),
134 }
135 } else {
136 Self {
137 supported: u32::from_be_bytes(s),
138 required: u32::from_be_bytes(r),
139 }
140 })
141 }
142
143 #[must_use]
146 pub fn is_compatible_with(&self, remote_supported: u32) -> bool {
147 (remote_supported & self.required) == self.required
148 }
149}
150
151#[derive(Debug, Clone, Copy, PartialEq, Eq)]
157pub struct ParticipantSecurityDigitalSignatureAlgorithmInfo {
158 pub trust_chain: AlgorithmRequirements,
160 pub message_auth: AlgorithmRequirements,
162}
163
164impl ParticipantSecurityDigitalSignatureAlgorithmInfo {
165 pub const WIRE_SIZE: usize = 16;
167
168 #[must_use]
170 pub fn spec_default() -> Self {
171 let mask = digital_signature_bit::RSASSA_PSS_2048_SHA256
172 | digital_signature_bit::ECDSA_P256_SHA256;
173 Self {
174 trust_chain: AlgorithmRequirements {
175 supported: mask,
176 required: mask,
177 },
178 message_auth: AlgorithmRequirements {
179 supported: mask,
180 required: mask,
181 },
182 }
183 }
184
185 #[must_use]
187 pub fn to_bytes(&self, little_endian: bool) -> [u8; 16] {
188 let mut out = [0u8; 16];
189 out[0..8].copy_from_slice(&self.trust_chain.to_bytes(little_endian));
190 out[8..16].copy_from_slice(&self.message_auth.to_bytes(little_endian));
191 out
192 }
193
194 pub fn from_bytes(bytes: &[u8], little_endian: bool) -> Result<Self, WireError> {
199 if bytes.len() < 16 {
200 return Err(WireError::ValueOutOfRange {
201 message: "DigitalSignatureAlgorithmInfo: < 16 bytes",
202 });
203 }
204 Ok(Self {
205 trust_chain: AlgorithmRequirements::from_bytes(&bytes[0..8], little_endian)?,
206 message_auth: AlgorithmRequirements::from_bytes(&bytes[8..16], little_endian)?,
207 })
208 }
209}
210
211#[derive(Debug, Clone, Copy, PartialEq, Eq)]
217pub struct ParticipantSecurityKeyEstablishmentAlgorithmInfo {
218 pub shared_secret: AlgorithmRequirements,
220}
221
222impl ParticipantSecurityKeyEstablishmentAlgorithmInfo {
223 pub const WIRE_SIZE: usize = 8;
225
226 #[must_use]
228 pub fn spec_default() -> Self {
229 let mask =
230 key_establishment_bit::DHE_MODP_2048_256 | key_establishment_bit::ECDHE_CEUM_P256;
231 Self {
232 shared_secret: AlgorithmRequirements {
233 supported: mask,
234 required: mask,
235 },
236 }
237 }
238
239 #[must_use]
241 pub fn to_bytes(&self, little_endian: bool) -> [u8; 8] {
242 self.shared_secret.to_bytes(little_endian)
243 }
244
245 pub fn from_bytes(bytes: &[u8], little_endian: bool) -> Result<Self, WireError> {
250 Ok(Self {
251 shared_secret: AlgorithmRequirements::from_bytes(bytes, little_endian)?,
252 })
253 }
254}
255
256#[derive(Debug, Clone, Copy, PartialEq, Eq)]
262pub struct ParticipantSecuritySymmetricCipherAlgorithmInfo {
263 pub supported_mask: u32,
266 pub builtin_endpoints_required_mask: u32,
269 pub builtin_kx_endpoints_required_mask: u32,
272 pub user_endpoints_default_required_mask: u32,
275}
276
277impl ParticipantSecuritySymmetricCipherAlgorithmInfo {
278 pub const WIRE_SIZE: usize = 16;
280
281 #[must_use]
283 pub fn spec_default() -> Self {
284 Self {
285 supported_mask: symmetric_bit::AES128 | symmetric_bit::AES256,
286 builtin_endpoints_required_mask: symmetric_bit::AES128,
287 builtin_kx_endpoints_required_mask: symmetric_bit::AES128,
288 user_endpoints_default_required_mask: symmetric_bit::AES128,
289 }
290 }
291
292 #[must_use]
294 pub fn to_bytes(&self, little_endian: bool) -> [u8; 16] {
295 let mut out = [0u8; 16];
296 let fields = [
297 self.supported_mask,
298 self.builtin_endpoints_required_mask,
299 self.builtin_kx_endpoints_required_mask,
300 self.user_endpoints_default_required_mask,
301 ];
302 for (i, v) in fields.iter().enumerate() {
303 let bytes = if little_endian {
304 v.to_le_bytes()
305 } else {
306 v.to_be_bytes()
307 };
308 out[i * 4..i * 4 + 4].copy_from_slice(&bytes);
309 }
310 out
311 }
312
313 pub fn from_bytes(bytes: &[u8], little_endian: bool) -> Result<Self, WireError> {
318 if bytes.len() < 16 {
319 return Err(WireError::ValueOutOfRange {
320 message: "SymmetricCipherAlgorithmInfo: < 16 bytes",
321 });
322 }
323 let read = |off: usize| -> u32 {
324 let mut a = [0u8; 4];
325 a.copy_from_slice(&bytes[off..off + 4]);
326 if little_endian {
327 u32::from_le_bytes(a)
328 } else {
329 u32::from_be_bytes(a)
330 }
331 };
332 Ok(Self {
333 supported_mask: read(0),
334 builtin_endpoints_required_mask: read(4),
335 builtin_kx_endpoints_required_mask: read(8),
336 user_endpoints_default_required_mask: read(12),
337 })
338 }
339}
340
341#[derive(Debug, Clone, Copy, PartialEq, Eq)]
349pub struct EndpointSecuritySymmetricCipherAlgorithmInfo {
350 pub required_mask: u32,
352}
353
354impl EndpointSecuritySymmetricCipherAlgorithmInfo {
355 pub const WIRE_SIZE: usize = 4;
357
358 #[must_use]
360 pub fn to_bytes(&self, little_endian: bool) -> [u8; 4] {
361 if little_endian {
362 self.required_mask.to_le_bytes()
363 } else {
364 self.required_mask.to_be_bytes()
365 }
366 }
367
368 pub fn from_bytes(bytes: &[u8], little_endian: bool) -> Result<Self, WireError> {
373 if bytes.len() < 4 {
374 return Err(WireError::ValueOutOfRange {
375 message: "EndpointSymmetricCipherAlgorithmInfo: < 4 bytes",
376 });
377 }
378 let mut a = [0u8; 4];
379 a.copy_from_slice(&bytes[0..4]);
380 Ok(Self {
381 required_mask: if little_endian {
382 u32::from_le_bytes(a)
383 } else {
384 u32::from_be_bytes(a)
385 },
386 })
387 }
388}
389
390#[allow(dead_code)]
392fn _vec_keepalive(v: Vec<u8>) -> Vec<u8> {
393 v
394}
395
396#[cfg(test)]
397#[allow(clippy::expect_used, clippy::unwrap_used)]
398mod tests {
399 use super::*;
400
401 #[test]
402 fn algorithm_requirements_roundtrip_le() {
403 let a = AlgorithmRequirements {
404 supported: 0xCAFE_BABE,
405 required: 0xDEAD_BEEF,
406 };
407 let bytes = a.to_bytes(true);
408 let back = AlgorithmRequirements::from_bytes(&bytes, true).unwrap();
409 assert_eq!(a, back);
410 }
411
412 #[test]
413 fn algorithm_requirements_roundtrip_be() {
414 let a = AlgorithmRequirements {
415 supported: 0x0102_0304,
416 required: 0x0506_0708,
417 };
418 let bytes = a.to_bytes(false);
419 let back = AlgorithmRequirements::from_bytes(&bytes, false).unwrap();
420 assert_eq!(a, back);
421 }
422
423 #[test]
424 fn algorithm_requirements_layout_be() {
425 let a = AlgorithmRequirements {
427 supported: 0x01,
428 required: 0x02,
429 };
430 assert_eq!(a.to_bytes(false), [0, 0, 0, 0x01, 0, 0, 0, 0x02]);
431 }
432
433 #[test]
434 fn compatibility_check_strict() {
435 let local = AlgorithmRequirements {
436 supported: 0,
437 required: 0b101,
438 };
439 assert!(local.is_compatible_with(0b111));
441 assert!(local.is_compatible_with(0b101));
443 assert!(!local.is_compatible_with(0b001));
445 assert!(!local.is_compatible_with(0));
447 }
448
449 #[test]
450 fn sig_info_spec_default() {
451 let d = ParticipantSecurityDigitalSignatureAlgorithmInfo::spec_default();
452 let expected = digital_signature_bit::RSASSA_PSS_2048_SHA256
453 | digital_signature_bit::ECDSA_P256_SHA256;
454 assert_eq!(d.trust_chain.supported, expected);
455 assert_eq!(d.trust_chain.required, expected);
456 assert_eq!(d.message_auth.supported, expected);
457 assert_eq!(d.message_auth.required, expected);
458 }
459
460 #[test]
461 fn sig_info_roundtrip() {
462 let d = ParticipantSecurityDigitalSignatureAlgorithmInfo::spec_default();
463 let bytes = d.to_bytes(true);
464 let back =
465 ParticipantSecurityDigitalSignatureAlgorithmInfo::from_bytes(&bytes, true).unwrap();
466 assert_eq!(d, back);
467 }
468
469 #[test]
470 fn kx_info_spec_default() {
471 let k = ParticipantSecurityKeyEstablishmentAlgorithmInfo::spec_default();
472 let expected =
473 key_establishment_bit::DHE_MODP_2048_256 | key_establishment_bit::ECDHE_CEUM_P256;
474 assert_eq!(k.shared_secret.supported, expected);
475 assert_eq!(k.shared_secret.required, expected);
476 }
477
478 #[test]
479 fn kx_info_roundtrip() {
480 let k = ParticipantSecurityKeyEstablishmentAlgorithmInfo::spec_default();
481 let bytes = k.to_bytes(true);
482 let back =
483 ParticipantSecurityKeyEstablishmentAlgorithmInfo::from_bytes(&bytes, true).unwrap();
484 assert_eq!(k, back);
485 }
486
487 #[test]
488 fn sym_info_spec_default() {
489 let s = ParticipantSecuritySymmetricCipherAlgorithmInfo::spec_default();
490 assert_eq!(
491 s.supported_mask,
492 symmetric_bit::AES128 | symmetric_bit::AES256
493 );
494 assert_eq!(s.builtin_endpoints_required_mask, symmetric_bit::AES128);
495 assert_eq!(s.builtin_kx_endpoints_required_mask, symmetric_bit::AES128);
496 assert_eq!(
497 s.user_endpoints_default_required_mask,
498 symmetric_bit::AES128
499 );
500 }
501
502 #[test]
503 fn sym_info_roundtrip() {
504 let s = ParticipantSecuritySymmetricCipherAlgorithmInfo::spec_default();
505 let bytes = s.to_bytes(true);
506 let back =
507 ParticipantSecuritySymmetricCipherAlgorithmInfo::from_bytes(&bytes, true).unwrap();
508 assert_eq!(s, back);
509 }
510
511 #[test]
512 fn endpoint_sym_info_roundtrip() {
513 let e = EndpointSecuritySymmetricCipherAlgorithmInfo {
514 required_mask: symmetric_bit::AES256,
515 };
516 let bytes = e.to_bytes(true);
517 let back = EndpointSecuritySymmetricCipherAlgorithmInfo::from_bytes(&bytes, true).unwrap();
518 assert_eq!(e, back);
519 }
520
521 #[test]
522 fn truncated_buffer_rejected() {
523 assert!(AlgorithmRequirements::from_bytes(&[1, 2, 3], true).is_err());
524 assert!(
525 ParticipantSecurityDigitalSignatureAlgorithmInfo::from_bytes(&[0u8; 15], true).is_err()
526 );
527 assert!(
528 ParticipantSecuritySymmetricCipherAlgorithmInfo::from_bytes(&[0u8; 15], true).is_err()
529 );
530 assert!(EndpointSecuritySymmetricCipherAlgorithmInfo::from_bytes(&[0u8; 3], true).is_err());
531 }
532
533 #[test]
534 fn spec_bit_constants_match() {
535 assert_eq!(symmetric_bit::AES128, 0x01);
538 assert_eq!(symmetric_bit::AES256, 0x02);
539 assert_eq!(digital_signature_bit::RSASSA_PSS_2048_SHA256, 0x01);
540 assert_eq!(digital_signature_bit::RSASSA_PKCS1_V15_2048_SHA256, 0x02);
541 assert_eq!(digital_signature_bit::ECDSA_P256_SHA256, 0x04);
542 assert_eq!(digital_signature_bit::ECDSA_P384_SHA384, 0x08);
543 assert_eq!(key_establishment_bit::DHE_MODP_2048_256, 0x01);
544 assert_eq!(key_establishment_bit::ECDHE_CEUM_P256, 0x02);
545 assert_eq!(key_establishment_bit::ECDHE_CEUM_P384, 0x04);
546 }
547}