Skip to main content

twine_codec/dataset/
security_policy.rs

1// Copyright (c) 2025 Jake Swensen
2// SPDX-License-Identifier: MPL-2.0
3//
4// This Source Code Form is subject to the terms of the Mozilla Public
5// License, v. 2.0. If a copy of the MPL was not distributed with this
6// file, You can obtain one at http://mozilla.org/MPL/2.0/.
7
8use crate::error::TwineCodecError;
9
10use twine_rs_macros::Tlv;
11
12pub enum VersionThreshold {
13    /// Protocol Version 1 (Thread v1.0) or 2 (Thread v1.1.x)
14    ProtocolVersion2,
15
16    /// Protocol Version 3 (Thread v1.2.x)
17    ProtocolVersion3,
18
19    /// Protocol Version 4, (Thread v1.3.x)
20    ProtocolVersion4,
21
22    /// Protocol Version 5, (Thread v1.4.x)
23    ProtocolVersion5,
24}
25
26bitfield::bitfield! {
27    #[derive(Clone, Copy, Eq, PartialEq, Tlv)]
28    #[tlv(tlv_type = 0x0C, tlv_length = 4, derive_inner)]
29    pub struct SecurityPolicy(u32);
30    impl Debug;
31
32    u16, get_rotation_time, set_rotation_time: 31, 16;
33
34    /// Obtain Network Key bit
35    ///
36    /// Obtaining the Network Key for out-of-band commissioning is enabled when this bit is set (1).
37    ///
38    /// Default: 1 (enabled) in non-CCM networks and 0 (disabled) in CCM networks
39    get_obtain_network_key_enabled, set_obtain_network_key_enabled: 15;
40
41    /// Native Commissioner bit
42    ///
43    /// Native Commissioning using PSKc is allowed when this bit is set (1).
44    ///
45    /// Default: 1 (enabled)
46    get_native_commissioning_enabled, set_native_commissioning_enabled: 14;
47
48    /// Legacy Routers bit
49    ///
50    /// Thread 1.0 and 1.1.x Routers are allowed to join and operate in the network when this bit
51    /// is set (1).
52    ///
53    /// Default: 1 (enabled) in non-CCM networks and 0 (disabled) in CCM networks
54    get_legacy_routers_enabled, set_legacy_routers_enabled: 13;
55
56    /// External Commissioner bit
57    ///
58    /// Indicates that External Commissioner authentication is allowed using
59    /// a PSKc.
60    ///
61    /// Default: 1 (enabled)
62    get_external_commissioner_enabled, set_external_commissioning_enabled: 12;
63
64    /// Reserved
65    ///
66    /// A Thread Device must ignore this bit.
67    ///
68    /// Default: 0
69    get_b_bit, set_b_bit: 11;
70
71    /// Commercial Commissioning Mode bit
72    ///
73    /// When enabled (0), the network is indicating support for Commercial Commissioning Mode (CCM).
74    ///
75    /// Default: 1 (disabled)
76    get_commercial_commissioning_mode_disabled, set_commercial_commissioning_mode_disabled: 10;
77
78    /// Autonomous Enrollment bit
79    ///
80    /// This bit indicates whether AE is enabled (0) or disabled (1).
81    ///
82    /// Default: 1 (disabled)
83    get_autonomous_enrollment_disabled, set_autonomous_enrollment_disabled: 9;
84
85    /// Network Key Provisioning bit
86    ///
87    /// This bit indicates whether Network Key Provisioning is enabled (0) or disabled (1).
88    ///
89    /// Default: 1 (disabled)
90    get_network_key_provisioning_disabled, set_network_key_provisioning_disabled: 8;
91
92    /// To BLE Link bit
93    ///
94    /// Default: 1
95    get_to_ble_link_disabled, set_to_ble_link_disabled: 7;
96
97    /// Non-CCM Routers bit
98    ///
99    /// Indicates whether, in a CCM Network, any non-CCM commissioned Thread Devices
100    /// that are present in the network are allowed to operate as Routers (0) or not (1).
101    ///
102    /// Default: 1
103    get_non_ccm_routers_disabled, set_non_ccm_routers_disabled: 6;
104    u8, get_reserved_bits, set_reserved_bits: 5, 3;
105    u8, get_version_threshold, set_version_threshold: 2, 0;
106}
107
108impl SecurityPolicy {
109    const TYPE_NAME: &str = "SecurityPolicy";
110
111    pub fn rotation_time_hours(&self) -> u16 {
112        self.get_rotation_time()
113    }
114
115    pub fn obtain_network_key_enabled(&self) -> bool {
116        self.get_obtain_network_key_enabled()
117    }
118
119    pub fn native_commissioning_enabled(&self) -> bool {
120        self.get_native_commissioning_enabled()
121    }
122
123    pub fn legacy_routers_enabled(&self) -> bool {
124        self.get_legacy_routers_enabled()
125    }
126
127    pub fn external_commissioner_enabled(&self) -> bool {
128        self.get_external_commissioner_enabled()
129    }
130
131    pub fn commercial_commissioning_mode_enabled(&self) -> bool {
132        !self.get_commercial_commissioning_mode_disabled()
133    }
134
135    pub fn autonomous_enrollment_enabled(&self) -> bool {
136        !self.get_autonomous_enrollment_disabled()
137    }
138
139    pub fn network_key_provisioning_enabled(&self) -> bool {
140        !self.get_network_key_provisioning_disabled()
141    }
142
143    pub fn to_ble_link_enabled(&self) -> bool {
144        !self.get_to_ble_link_disabled()
145    }
146
147    pub fn non_ccm_routers_enabled(&self) -> bool {
148        !self.get_non_ccm_routers_disabled()
149    }
150
151    /// Fetch the Thread Protocol Version threshold.
152    ///
153    /// If the protocol version is unknown, returns the value as an error.
154    pub fn version_threshold(&self) -> Result<VersionThreshold, u8> {
155        let threshold = self.get_version_threshold();
156        let r_bit = self.get_legacy_routers_enabled();
157        match (threshold, r_bit) {
158            (_, true) => Ok(VersionThreshold::ProtocolVersion2),
159            (0, false) => Ok(VersionThreshold::ProtocolVersion3),
160            (1, false) => Ok(VersionThreshold::ProtocolVersion4),
161            (2, false) => Ok(VersionThreshold::ProtocolVersion5),
162            _ => Err(threshold),
163        }
164    }
165}
166
167impl Default for SecurityPolicy {
168    fn default() -> Self {
169        let mut policy = SecurityPolicy(0);
170        policy.set_rotation_time(672);
171        policy.set_obtain_network_key_enabled(true);
172        policy.set_native_commissioning_enabled(true);
173        policy.set_legacy_routers_enabled(true);
174        policy.set_external_commissioning_enabled(true);
175        policy.set_commercial_commissioning_mode_disabled(true);
176        policy.set_autonomous_enrollment_disabled(true);
177        policy.set_network_key_provisioning_disabled(true);
178        policy.set_to_ble_link_disabled(true);
179        policy.set_non_ccm_routers_disabled(true);
180        policy.set_reserved_bits(0x07);
181        policy.set_version_threshold(VersionThreshold::ProtocolVersion2 as u8);
182
183        policy
184    }
185}
186
187impl core::fmt::Display for SecurityPolicy {
188    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
189        let rotation_time = self.rotation_time_hours();
190
191        // Rotation time
192        write!(f, "{} ", rotation_time)?;
193
194        // Policy bits
195        if self.obtain_network_key_enabled() {
196            write!(f, "o")?;
197        }
198
199        if self.native_commissioning_enabled() {
200            write!(f, "n")?;
201        }
202
203        if self.legacy_routers_enabled() {
204            write!(f, "r")?;
205        }
206
207        if self.external_commissioner_enabled() {
208            write!(f, "c")?;
209        }
210
211        if self.commercial_commissioning_mode_enabled() {
212            write!(f, "C")?;
213        }
214
215        if self.autonomous_enrollment_enabled() {
216            write!(f, "e")?;
217        }
218
219        if self.network_key_provisioning_enabled() {
220            write!(f, "p")?;
221        }
222
223        if self.to_ble_link_enabled() {
224            write!(f, "L")?;
225        }
226
227        if self.non_ccm_routers_enabled() {
228            write!(f, "R")?;
229        }
230
231        // Raw version-threshold value
232        let ver = self.get_version_threshold();
233        write!(f, " {}", ver)
234    }
235}
236
237#[derive(Default)]
238pub struct SecurityPolicyBuilder {
239    obtain_network_key_enabled: Option<bool>,
240    native_commissioning_enabled: Option<bool>,
241    legacy_routers_enabled: Option<bool>,
242    external_commissioner_enabled: Option<bool>,
243    commercial_commissioning_mode_disabled: Option<bool>,
244    autonomous_enrollment_disabled: Option<bool>,
245    network_key_provisioning_disabled: Option<bool>,
246    to_ble_link_disabled: Option<bool>,
247    non_ccm_routers_disabled: Option<bool>,
248    version_threshold: Option<VersionThreshold>,
249    rotation_time_hours: Option<u16>,
250}
251
252impl SecurityPolicyBuilder {
253    #[cfg(test)]
254    pub fn with_disabled_policy() -> Self {
255        SecurityPolicyBuilder {
256            obtain_network_key_enabled: Some(false),
257            native_commissioning_enabled: Some(false),
258            legacy_routers_enabled: Some(false),
259            external_commissioner_enabled: Some(false),
260            commercial_commissioning_mode_disabled: Some(true),
261            autonomous_enrollment_disabled: Some(true),
262            network_key_provisioning_disabled: Some(true),
263            to_ble_link_disabled: Some(true),
264            non_ccm_routers_disabled: Some(true),
265            version_threshold: Some(VersionThreshold::ProtocolVersion3),
266            rotation_time_hours: Some(672),
267        }
268    }
269
270    pub fn with_default_policy() -> Self {
271        SecurityPolicyBuilder {
272            obtain_network_key_enabled: Some(true),
273            native_commissioning_enabled: Some(true),
274            legacy_routers_enabled: Some(true),
275            external_commissioner_enabled: Some(true),
276            commercial_commissioning_mode_disabled: Some(true),
277            autonomous_enrollment_disabled: Some(true),
278            network_key_provisioning_disabled: Some(true),
279            to_ble_link_disabled: Some(true),
280            non_ccm_routers_disabled: Some(true),
281            version_threshold: Some(VersionThreshold::ProtocolVersion2),
282            rotation_time_hours: Some(672),
283        }
284    }
285
286    pub fn enable_obtain_network_key(mut self) -> Self {
287        self.obtain_network_key_enabled = Some(true);
288        self
289    }
290
291    pub fn disable_obtain_network_key(mut self) -> Self {
292        self.obtain_network_key_enabled = Some(false);
293        self
294    }
295
296    pub fn enable_native_commissioning(mut self) -> Self {
297        self.native_commissioning_enabled = Some(true);
298        self
299    }
300
301    pub fn disable_native_commissioning(mut self) -> Self {
302        self.native_commissioning_enabled = Some(false);
303        self
304    }
305
306    pub fn enable_legacy_routers(mut self) -> Self {
307        self.legacy_routers_enabled = Some(true);
308        self
309    }
310
311    pub fn disable_legacy_routers(mut self) -> Self {
312        self.legacy_routers_enabled = Some(false);
313        self
314    }
315
316    pub fn enable_external_commissioner(mut self) -> Self {
317        self.external_commissioner_enabled = Some(true);
318        self
319    }
320
321    pub fn disable_external_commissioner(mut self) -> Self {
322        self.external_commissioner_enabled = Some(false);
323        self
324    }
325
326    pub fn enable_commercial_commissioning(mut self) -> Self {
327        self.commercial_commissioning_mode_disabled = Some(false);
328        self
329    }
330
331    pub fn disable_commercial_commissioning(mut self) -> Self {
332        self.commercial_commissioning_mode_disabled = Some(true);
333        self
334    }
335
336    pub fn enable_autonomous_enrollment(mut self) -> Self {
337        self.autonomous_enrollment_disabled = Some(false);
338        self
339    }
340
341    pub fn disable_autonomous_enrollment(mut self) -> Self {
342        self.autonomous_enrollment_disabled = Some(true);
343        self
344    }
345
346    pub fn enable_network_key_provisioning(mut self) -> Self {
347        self.network_key_provisioning_disabled = Some(false);
348        self
349    }
350
351    pub fn disable_network_key_provisioning(mut self) -> Self {
352        self.network_key_provisioning_disabled = Some(true);
353        self
354    }
355
356    pub fn enable_to_ble_link(mut self) -> Self {
357        self.to_ble_link_disabled = Some(false);
358        self
359    }
360
361    pub fn disable_to_ble_link(mut self) -> Self {
362        self.to_ble_link_disabled = Some(true);
363        self
364    }
365
366    pub fn enable_non_ccm_routers(mut self) -> Self {
367        self.non_ccm_routers_disabled = Some(false);
368        self
369    }
370
371    pub fn disable_non_ccm_routers(mut self) -> Self {
372        self.non_ccm_routers_disabled = Some(true);
373        self
374    }
375
376    pub fn version_threshold(mut self, threshold: VersionThreshold) -> Self {
377        self.version_threshold = Some(threshold);
378        self
379    }
380
381    pub fn rotation_time_hours(mut self, hours: u16) -> Self {
382        self.rotation_time_hours = Some(hours);
383        self
384    }
385
386    pub fn build(self) -> Result<SecurityPolicy, TwineCodecError> {
387        if self.obtain_network_key_enabled.is_none()
388            || self.native_commissioning_enabled.is_none()
389            || self.legacy_routers_enabled.is_none()
390            || self.external_commissioner_enabled.is_none()
391            || self.commercial_commissioning_mode_disabled.is_none()
392            || self.autonomous_enrollment_disabled.is_none()
393            || self.network_key_provisioning_disabled.is_none()
394            || self.to_ble_link_disabled.is_none()
395            || self.non_ccm_routers_disabled.is_none()
396            || self.version_threshold.is_none()
397            || self.rotation_time_hours.is_none()
398        {
399            return Err(TwineCodecError::TypeBuildError(SecurityPolicy::TYPE_NAME));
400        }
401
402        let mut policy = SecurityPolicy::default();
403
404        if let Some(rotation_time) = self.rotation_time_hours {
405            policy.set_rotation_time(rotation_time);
406        }
407
408        if let Some(o_bit) = self.obtain_network_key_enabled {
409            policy.set_obtain_network_key_enabled(o_bit);
410        }
411
412        if let Some(n_bit) = self.native_commissioning_enabled {
413            policy.set_native_commissioning_enabled(n_bit);
414        }
415
416        if let Some(r_bit) = self.legacy_routers_enabled {
417            policy.set_legacy_routers_enabled(r_bit);
418        }
419
420        if let Some(c_bit) = self.external_commissioner_enabled {
421            policy.set_external_commissioning_enabled(c_bit);
422        }
423
424        if let Some(ccm_bit) = self.commercial_commissioning_mode_disabled {
425            policy.set_commercial_commissioning_mode_disabled(ccm_bit);
426        }
427
428        if let Some(ae_bit) = self.autonomous_enrollment_disabled {
429            policy.set_autonomous_enrollment_disabled(ae_bit);
430        }
431
432        if let Some(np_bit) = self.network_key_provisioning_disabled {
433            policy.set_network_key_provisioning_disabled(np_bit);
434        }
435
436        if let Some(ble_bit) = self.to_ble_link_disabled {
437            policy.set_to_ble_link_disabled(ble_bit);
438        }
439
440        if let Some(non_ccm_bit) = self.non_ccm_routers_disabled {
441            policy.set_non_ccm_routers_disabled(non_ccm_bit);
442        }
443
444        policy.set_reserved_bits(0x07);
445
446        if let Some(version_threshold) = self.version_threshold {
447            match version_threshold {
448                VersionThreshold::ProtocolVersion2 => {
449                    policy.set_legacy_routers_enabled(true);
450                    policy.set_version_threshold(0);
451                }
452                VersionThreshold::ProtocolVersion3 => {
453                    policy.set_legacy_routers_enabled(false);
454                    policy.set_version_threshold(0);
455                }
456                VersionThreshold::ProtocolVersion4 => {
457                    policy.set_legacy_routers_enabled(false);
458                    policy.set_version_threshold(1);
459                }
460                VersionThreshold::ProtocolVersion5 => {
461                    policy.set_legacy_routers_enabled(false);
462                    policy.set_version_threshold(2);
463                }
464            }
465        }
466
467        Ok(policy)
468    }
469}
470
471#[cfg(test)]
472mod tests {
473    use super::*;
474
475    #[test]
476    fn default_policy() {
477        let test = SecurityPolicyBuilder::with_default_policy()
478            .build()
479            .expect("Failed to build default policy");
480        let expected = SecurityPolicy(0x02A0_F7F8);
481        assert_eq!(expected, test);
482        assert_eq!(std::format!("{}", test), "672 onrc 0");
483
484        let test = SecurityPolicy::default();
485        assert_eq!(expected, test);
486        assert_eq!(std::format!("{}", test), "672 onrc 0");
487    }
488
489    #[test]
490    fn disabled_policy() {
491        let _ = env_logger::builder().is_test(true).try_init();
492        let test = SecurityPolicyBuilder::with_disabled_policy()
493            .build()
494            .expect("Failed to build disabled policy");
495        log::debug!("Disabled Policy: {:04x?}", test.0);
496
497        let expected = SecurityPolicy(0x02A0_07F8);
498        assert_eq!(expected, test);
499    }
500
501    #[test]
502    fn o_bit() {
503        let policy = SecurityPolicyBuilder::with_disabled_policy()
504            .enable_obtain_network_key()
505            .build()
506            .expect("Failed to build policy with O bit enabled");
507        assert!(policy.obtain_network_key_enabled());
508        assert_eq!(std::format!("{}", policy), "672 o 0");
509
510        let inner = policy.0;
511        assert_eq!(inner & 0x0000_8000, 0x0000_8000);
512
513        let policy = SecurityPolicyBuilder::with_disabled_policy()
514            .disable_obtain_network_key()
515            .build()
516            .expect("Failed to build policy with o bit enabled");
517        assert!(!policy.obtain_network_key_enabled());
518
519        let inner = policy.0;
520        assert_eq!(inner & 0x0000_8000, 0);
521    }
522
523    #[test]
524    fn n_bit() {
525        let n_bit_mask = 0x0000_4000;
526        let policy = SecurityPolicyBuilder::with_disabled_policy()
527            .enable_native_commissioning()
528            .build()
529            .expect("Failed to build policy with n bit enabled");
530        assert!(policy.native_commissioning_enabled());
531        assert_eq!(std::format!("{}", policy), "672 n 0");
532
533        let inner = policy.0;
534        assert_eq!(inner & n_bit_mask, n_bit_mask);
535
536        let policy = SecurityPolicyBuilder::with_disabled_policy()
537            .disable_native_commissioning()
538            .build()
539            .expect("Failed to build policy with n bit disabled");
540        assert!(!policy.native_commissioning_enabled());
541
542        let inner = policy.0;
543        assert_eq!(inner & n_bit_mask, 0);
544    }
545
546    #[test]
547    fn r_bit() {
548        let r_bit_mask = 0x0000_2000;
549        let policy = SecurityPolicyBuilder::with_disabled_policy()
550            .version_threshold(VersionThreshold::ProtocolVersion2)
551            .build()
552            .expect("Failed to build policy with r bit enabled");
553        assert!(policy.legacy_routers_enabled());
554        assert_eq!(std::format!("{}", policy), "672 r 0");
555
556        let inner = policy.0;
557        assert_eq!(inner & r_bit_mask, r_bit_mask);
558
559        let policy = SecurityPolicyBuilder::with_disabled_policy()
560            .disable_legacy_routers()
561            .build()
562            .expect("Failed to build policy with r bit disabled");
563        assert!(!policy.legacy_routers_enabled());
564
565        let inner = policy.0;
566        assert_eq!(inner & r_bit_mask, 0);
567    }
568
569    #[test]
570    fn c_bit() {
571        let c_bit_mask = 0x0000_1000;
572        let policy = SecurityPolicyBuilder::with_disabled_policy()
573            .enable_external_commissioner()
574            .build()
575            .expect("Failed to build policy with c bit enabled");
576        assert!(policy.external_commissioner_enabled());
577        assert_eq!(std::format!("{}", policy), "672 c 0");
578
579        let inner = policy.0;
580        assert_eq!(inner & c_bit_mask, c_bit_mask);
581
582        let policy = SecurityPolicyBuilder::with_disabled_policy()
583            .disable_external_commissioner()
584            .build()
585            .expect("Failed to build policy with c bit disabled");
586        assert!(!policy.external_commissioner_enabled());
587
588        let inner = policy.0;
589        assert_eq!(inner & c_bit_mask, 0);
590    }
591
592    #[test]
593    fn ccm_bit() {
594        let ccm_bit_mask = 0x0000_0400;
595        let policy = SecurityPolicyBuilder::with_disabled_policy()
596            .enable_commercial_commissioning()
597            .build()
598            .expect("Failed to build policy with ccm bit enabled");
599        assert!(policy.commercial_commissioning_mode_enabled());
600        assert_eq!(std::format!("{}", policy), "672 C 0");
601
602        let inner = policy.0;
603        assert_eq!(inner & ccm_bit_mask, 0);
604
605        let policy = SecurityPolicyBuilder::with_disabled_policy()
606            .disable_commercial_commissioning()
607            .build()
608            .expect("Failed to build policy with ccm bit disabled");
609        assert!(!policy.commercial_commissioning_mode_enabled());
610
611        let inner = policy.0;
612        assert_eq!(inner & ccm_bit_mask, ccm_bit_mask);
613    }
614
615    #[test]
616    fn ae_bit() {
617        let ae_bit_mask = 0x0000_0200;
618        let policy = SecurityPolicyBuilder::with_disabled_policy()
619            .enable_autonomous_enrollment()
620            .build()
621            .expect("Failed to build policy with ae bit enabled");
622        assert!(policy.autonomous_enrollment_enabled());
623        assert_eq!(std::format!("{}", policy), "672 e 0");
624
625        let inner = policy.0;
626        assert_eq!(inner & ae_bit_mask, 0);
627
628        let policy = SecurityPolicyBuilder::with_disabled_policy()
629            .disable_autonomous_enrollment()
630            .build()
631            .expect("Failed to build policy with ae bit disabled");
632        assert!(!policy.autonomous_enrollment_enabled());
633
634        let inner = policy.0;
635        assert_eq!(inner & ae_bit_mask, ae_bit_mask);
636    }
637
638    #[test]
639    fn np_bit() {
640        let np_bit_mask = 0x0000_0100;
641        let policy = SecurityPolicyBuilder::with_disabled_policy()
642            .enable_network_key_provisioning()
643            .build()
644            .expect("Failed to build policy with np bit enabled");
645        assert!(policy.network_key_provisioning_enabled());
646        assert_eq!(std::format!("{}", policy), "672 p 0");
647
648        let inner = policy.0;
649        assert_eq!(inner & np_bit_mask, 0);
650
651        let policy = SecurityPolicyBuilder::with_disabled_policy()
652            .disable_network_key_provisioning()
653            .build()
654            .expect("Failed to build policy with np bit disabled");
655        assert!(!policy.network_key_provisioning_enabled());
656
657        let inner = policy.0;
658        assert_eq!(inner & np_bit_mask, np_bit_mask);
659    }
660
661    #[test]
662    fn ncr_bit() {
663        let ncr_bit_mask = 0x0000_0040;
664        let policy = SecurityPolicyBuilder::with_disabled_policy()
665            .enable_non_ccm_routers()
666            .build()
667            .expect("Failed to build policy with ncr bit enabled");
668        assert!(policy.non_ccm_routers_enabled());
669        assert_eq!(std::format!("{}", policy), "672 R 0");
670
671        let inner = policy.0;
672        assert_eq!(inner & ncr_bit_mask, 0);
673
674        let policy = SecurityPolicyBuilder::with_disabled_policy()
675            .disable_non_ccm_routers()
676            .build()
677            .expect("Failed to build policy with ncr bit disabled");
678        assert!(!policy.non_ccm_routers_enabled());
679
680        let inner = policy.0;
681        assert_eq!(inner & ncr_bit_mask, ncr_bit_mask);
682    }
683
684    #[test]
685    fn version_threshold() {
686        let policy = SecurityPolicyBuilder::with_disabled_policy()
687            .version_threshold(VersionThreshold::ProtocolVersion4)
688            .build()
689            .expect("Failed to build policy with Protocol Version 4 threshold");
690        assert!(!policy.legacy_routers_enabled());
691
692        assert!(matches!(
693            policy.version_threshold(),
694            Ok(VersionThreshold::ProtocolVersion4)
695        ));
696
697        assert_eq!(std::format!("{}", policy), "672  1");
698        assert_eq!(policy.0 & 0x0000_0003, 1);
699    }
700
701    #[test]
702    fn display_security_policy() {
703        let _ = env_logger::builder().is_test(true).try_init();
704
705        let policy = SecurityPolicy::default();
706        assert_eq!(std::format!("{}", policy), "672 onrc 0");
707
708        let policy = SecurityPolicyBuilder::default()
709            .disable_obtain_network_key()
710            .disable_native_commissioning()
711            .disable_legacy_routers()
712            .disable_external_commissioner()
713            .enable_commercial_commissioning()
714            .disable_autonomous_enrollment()
715            .disable_network_key_provisioning()
716            .disable_to_ble_link()
717            .disable_non_ccm_routers()
718            .version_threshold(VersionThreshold::ProtocolVersion4)
719            .rotation_time_hours(672)
720            .build()
721            .inspect_err(|e| log::trace!("Failed to build security policy: {e:?}"));
722
723        let policy = policy.expect("Failed to build security policy");
724
725        log::debug!("Policy: {policy:?}");
726        assert!(!policy.obtain_network_key_enabled());
727        assert_eq!(std::format!("{}", policy), "672 C 1");
728    }
729}