1
//! Configuration options for types implementing [`Keystore`](crate::Keystore)
2

            
3
pub use tor_config::{ConfigBuildError, ConfigurationSource, Reconfigure};
4
pub use tor_config_path::{CfgPath, CfgPathError};
5

            
6
use amplify::Getters;
7
use derive_builder::Builder;
8
use serde::{Deserialize, Serialize};
9
use tor_config::{
10
    BoolOrAuto, ExplicitOrAuto, define_list_builder_helper, impl_not_auto_value,
11
    impl_standard_builder,
12
};
13
use tor_persist::hsnickname::HsNickname;
14

            
15
use std::collections::BTreeMap;
16
use std::path::PathBuf;
17

            
18
use crate::KeystoreId;
19

            
20
/// The kind of keystore to use
21
#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
22
#[serde(rename_all = "lowercase")]
23
#[non_exhaustive]
24
pub enum ArtiKeystoreKind {
25
    /// Use the [`ArtiNativeKeystore`](crate::ArtiNativeKeystore).
26
    Native,
27
    /// Use the [`ArtiEphemeralKeystore`](crate::ArtiEphemeralKeystore).
28
    #[cfg(feature = "ephemeral-keystore")]
29
    Ephemeral,
30
}
31
impl_not_auto_value! {ArtiKeystoreKind}
32

            
33
/// [`ArtiNativeKeystore`](crate::ArtiNativeKeystore) configuration
34
#[derive(Debug, Clone, Builder, Eq, PartialEq, Serialize, Deserialize, Getters)]
35
#[builder(derive(Serialize, Deserialize, Debug))]
36
#[builder(build_fn(validate = "Self::validate", error = "ConfigBuildError"))]
37
#[non_exhaustive]
38
#[builder_struct_attr(non_exhaustive)]
39
pub struct ArtiKeystoreConfig {
40
    /// Whether keystore use is enabled.
41
    #[builder_field_attr(serde(default))]
42
    #[builder(default)]
43
    enabled: BoolOrAuto,
44

            
45
    /// The primary keystore.
46
    #[builder(sub_builder)]
47
    #[builder_field_attr(serde(default))]
48
    primary: PrimaryKeystoreConfig,
49

            
50
    /// Optionally configure C Tor keystores for arti to use.
51
    ///
52
    /// Note: The keystores listed here are read-only (keys are only
53
    /// ever written to the primary keystore, configured in
54
    /// `storage.keystore.primary`).
55
    ///
56
    /// Each C Tor keystore **must** have a unique identifier.
57
    /// It is an error to configure multiple keystores with the same [`KeystoreId`].
58
    #[builder(sub_builder)]
59
    #[builder_field_attr(serde(default))]
60
    ctor: CTorKeystoreConfig,
61
}
62

            
63
/// [`ArtiNativeKeystore`](crate::ArtiNativeKeystore) configuration
64
#[derive(Debug, Clone, Builder, Eq, PartialEq, Serialize, Deserialize, Getters)]
65
#[builder(derive(Serialize, Deserialize, Debug))]
66
#[builder(build_fn(validate = "Self::validate", error = "ConfigBuildError"))]
67
#[non_exhaustive]
68
#[builder_struct_attr(non_exhaustive)]
69
pub struct CTorKeystoreConfig {
70
    /// C Tor hidden service keystores.
71
    #[builder(default, sub_builder(fn_name = "build"), setter(custom))]
72
    #[builder_field_attr(serde(default))]
73
    services: CTorServiceKeystoreConfigMap,
74

            
75
    /// C Tor hidden service client keystores.
76
    #[builder(default, sub_builder(fn_name = "build"))]
77
    #[builder_field_attr(serde(default))]
78
    clients: CTorClientKeystoreConfigList,
79
}
80

            
81
/// Primary [`ArtiNativeKeystore`](crate::ArtiNativeKeystore) configuration
82
#[derive(Debug, Clone, Builder, Eq, PartialEq, Serialize, Deserialize)]
83
#[builder(derive(Serialize, Deserialize, Debug))]
84
#[builder(build_fn(error = "ConfigBuildError"))]
85
#[non_exhaustive]
86
#[builder_struct_attr(non_exhaustive)]
87
pub struct PrimaryKeystoreConfig {
88
    /// The type of keystore to use, or none at all.
89
    #[builder_field_attr(serde(default))]
90
    #[builder(default)]
91
    kind: ExplicitOrAuto<ArtiKeystoreKind>,
92
}
93

            
94
/// C Tor [`ArtiNativeKeystore`](crate::ArtiNativeKeystore) configuration
95
#[derive(Debug, Clone, Builder, Eq, PartialEq, Serialize, Deserialize, Getters)]
96
#[builder(derive(Serialize, Deserialize, Debug))]
97
#[builder(build_fn(error = "ConfigBuildError"))]
98
#[non_exhaustive]
99
#[builder_struct_attr(non_exhaustive)]
100
pub struct CTorServiceKeystoreConfig {
101
    /// The identifier of this keystore.
102
    ///
103
    /// Each C Tor keystore **must**:
104
    ///
105
    ///   * have a unique identifier. It is an error to configure multiple keystores
106
    ///     with the same [`KeystoreId`].
107
    ///   * have a corresponding arti hidden service configured in the
108
    ///     `[onion_services]` section with the same nickname
109
    id: KeystoreId,
110

            
111
    /// The root directory of this keystore.
112
    ///
113
    /// This should be set to the `HiddenServiceDirectory` of your hidden service.
114
    /// Arti will read `HiddenServiceDirectory/hostname` and `HiddenServiceDirectory/private_key`.
115
    /// (Note: if your service is running in restricted discovery mode, you must also set the
116
    /// `[[onion_services."<the nickname of your svc>".restricted_discovery.key_dirs]]`
117
    /// to `HiddenServiceDirectory/client_keys`).
118
    path: PathBuf,
119

            
120
    /// The nickname of the service this keystore is to be used with.
121
    nickname: HsNickname,
122
}
123

            
124
/// Alias for a `BTreeMap` of `CTorServiceKeystoreConfig`; used to make derive_builder
125
/// happy.
126
pub(crate) type CTorServiceKeystoreConfigMap = BTreeMap<HsNickname, CTorServiceKeystoreConfig>;
127

            
128
/// The serialized format of an CTorServiceKeystoreConfigListBuilder:
129
/// a map from nickname to `CTorServiceKeystoreConfigBuilder`
130
type CTorServiceKeystoreConfigBuilderMap = BTreeMap<HsNickname, CTorServiceKeystoreConfigBuilder>;
131

            
132
define_list_builder_helper! {
133
    pub struct CTorServiceKeystoreConfigMapBuilder {
134
        stores: [CTorServiceKeystoreConfigBuilder],
135
    }
136
    built: CTorServiceKeystoreConfigMap = build_ctor_service_list(stores)?;
137
    default = vec![];
138
    #[serde(try_from="CTorServiceKeystoreConfigBuilderMap", into="CTorServiceKeystoreConfigBuilderMap")]
139
}
140

            
141
impl TryFrom<CTorServiceKeystoreConfigBuilderMap> for CTorServiceKeystoreConfigMapBuilder {
142
    type Error = ConfigBuildError;
143

            
144
720
    fn try_from(value: CTorServiceKeystoreConfigBuilderMap) -> Result<Self, Self::Error> {
145
720
        let mut list_builder = CTorServiceKeystoreConfigMapBuilder::default();
146
1440
        for (nickname, mut cfg) in value {
147
            match &cfg.nickname {
148
                Some(n) if n == &nickname => (),
149
720
                None => (),
150
                Some(other) => {
151
                    return Err(ConfigBuildError::Inconsistent {
152
                        fields: vec![nickname.to_string(), format!("{nickname}.{other}")],
153
                        problem: "mismatched nicknames on onion service.".into(),
154
                    });
155
                }
156
            }
157
720
            cfg.nickname = Some(nickname);
158
720
            list_builder.access().push(cfg);
159
        }
160
720
        Ok(list_builder)
161
720
    }
162
}
163

            
164
impl From<CTorServiceKeystoreConfigMapBuilder> for CTorServiceKeystoreConfigBuilderMap {
165
    // Note: this is *similar* to the OnionServiceProxyConfigMap implementation (it duplicates much
166
    // of that logic, so perhaps at some point it's worth abstracting all of it away behind a
167
    // general-purpose map builder API).
168
    //
169
    /// Convert our Builder representation of a set of C Tor service configs into the
170
    /// format that serde will serialize.
171
    ///
172
    /// Note: This is a potentially lossy conversion, since the serialized format
173
    /// can't represent partially-built configs without a nickname, or
174
    /// a collection of configs with duplicate nicknames.
175
4
    fn from(value: CTorServiceKeystoreConfigMapBuilder) -> CTorServiceKeystoreConfigBuilderMap {
176
4
        let mut map = BTreeMap::new();
177
4
        for cfg in value.stores.into_iter().flatten() {
178
            let nickname = cfg.nickname.clone().unwrap_or_else(|| {
179
                "Unnamed"
180
                    .to_string()
181
                    .try_into()
182
                    .expect("'Unnamed' was not a valid nickname")
183
            });
184
            map.insert(nickname, cfg);
185
        }
186
4
        map
187
4
    }
188
}
189

            
190
/// Construct a CTorServiceKeystoreConfigList from a vec of CTorServiceKeystoreConfig;
191
/// enforce that nicknames are unique.
192
///
193
/// Returns an error if the [`KeystoreId`] of the `CTorServiceKeystoreConfig`s are not unique.
194
5723
fn build_ctor_service_list(
195
5723
    ctor_stores: Vec<CTorServiceKeystoreConfig>,
196
5723
) -> Result<CTorServiceKeystoreConfigMap, ConfigBuildError> {
197
    use itertools::Itertools as _;
198

            
199
5723
    if !ctor_stores.iter().map(|s| &s.id).all_unique() {
200
        return Err(ConfigBuildError::Inconsistent {
201
            fields: ["id"].map(Into::into).into_iter().collect(),
202
            problem: "the C Tor keystores do not have unique IDs".into(),
203
        });
204
5723
    }
205

            
206
5723
    let mut map = BTreeMap::new();
207
6445
    for service in ctor_stores {
208
724
        if let Some(previous_value) = map.insert(service.nickname.clone(), service) {
209
2
            return Err(ConfigBuildError::Inconsistent {
210
2
                fields: vec!["nickname".into()],
211
2
                problem: format!(
212
2
                    "Multiple C Tor service keystores for service with nickname {}",
213
2
                    previous_value.nickname
214
2
                ),
215
2
            });
216
722
        };
217
    }
218

            
219
5721
    Ok(map)
220
5723
}
221

            
222
/// C Tor [`ArtiNativeKeystore`](crate::ArtiNativeKeystore) configuration
223
#[derive(Debug, Clone, Builder, Eq, PartialEq, Serialize, Deserialize, Getters)]
224
#[builder(derive(Serialize, Deserialize, Debug))]
225
#[builder(build_fn(error = "ConfigBuildError"))]
226
#[non_exhaustive]
227
#[builder_struct_attr(non_exhaustive)]
228
pub struct CTorClientKeystoreConfig {
229
    /// The identifier of this keystore.
230
    ///
231
    /// Each keystore **must** have a unique identifier.
232
    /// It is an error to configure multiple keystores with the same [`KeystoreId`].
233
    id: KeystoreId,
234

            
235
    /// The root directory of this keystore.
236
    ///
237
    /// This should be set to the `ClientOnionAuthDir` of your client.
238
    /// If Arti is configured to run as a client (i.e. if it runs in SOCKS proxy mode),
239
    /// it will read the client restricted discovery keys from this path.
240
    ///
241
    /// The key files are expected to have the `.auth_private` extension,
242
    /// and their content **must** be of the form:
243
    /// `<56-char-onion-addr-without-.onion-part>:descriptor:x25519:<x25519 private key in base32>`.
244
    ///
245
    /// Malformed files, and files that don't have the `.auth_private` extension, will be ignored.
246
    path: PathBuf,
247
}
248

            
249
/// The serialized format of a [`CTorClientKeystoreConfigListBuilder`]:
250
pub type CTorClientKeystoreConfigList = Vec<CTorClientKeystoreConfig>;
251

            
252
define_list_builder_helper! {
253
    pub struct CTorClientKeystoreConfigListBuilder {
254
        stores: [CTorClientKeystoreConfigBuilder],
255
    }
256
    built: CTorClientKeystoreConfigList = build_ctor_client_store_config(stores)?;
257
    default = vec![];
258
}
259

            
260
/// Helper for building and validating a [`CTorClientKeystoreConfigList`].
261
///
262
/// Returns an error if the [`KeystoreId`]s of the `CTorClientKeystoreConfig`s are not unique.
263
5721
fn build_ctor_client_store_config(
264
5721
    ctor_stores: Vec<CTorClientKeystoreConfig>,
265
5721
) -> Result<CTorClientKeystoreConfigList, ConfigBuildError> {
266
    use itertools::Itertools as _;
267

            
268
5721
    if !ctor_stores.iter().map(|s| &s.id).all_unique() {
269
        return Err(ConfigBuildError::Inconsistent {
270
            fields: ["id"].map(Into::into).into_iter().collect(),
271
            problem: "the C Tor keystores do not have unique IDs".into(),
272
        });
273
5721
    }
274

            
275
5721
    Ok(ctor_stores)
276
5721
}
277

            
278
impl ArtiKeystoreConfig {
279
    /// Whether the keystore is enabled.
280
1665
    pub fn is_enabled(&self) -> bool {
281
1665
        let default = cfg!(feature = "keymgr");
282

            
283
1665
        self.enabled.as_bool().unwrap_or(default)
284
1665
    }
285

            
286
    /// The type of keystore to use
287
    ///
288
    /// Returns `None` if keystore use is disabled.
289
1665
    pub fn primary_kind(&self) -> Option<ArtiKeystoreKind> {
290
        use ExplicitOrAuto as EoA;
291

            
292
1665
        if !self.is_enabled() {
293
            return None;
294
1665
        }
295

            
296
1665
        let kind = match self.primary.kind {
297
            EoA::Explicit(kind) => kind,
298
1665
            EoA::Auto => ArtiKeystoreKind::Native,
299
        };
300

            
301
1665
        Some(kind)
302
1665
    }
303

            
304
    /// The ctor keystore configs
305
1665
    pub fn ctor_svc_stores(&self) -> impl Iterator<Item = &CTorServiceKeystoreConfig> {
306
1665
        self.ctor.services.values()
307
1665
    }
308

            
309
    /// The ctor client keystore configs
310
1665
    pub fn ctor_client_stores(&self) -> impl Iterator<Item = &CTorClientKeystoreConfig> {
311
1665
        self.ctor.clients.iter()
312
1665
    }
313
}
314

            
315
impl_standard_builder! { ArtiKeystoreConfig }
316

            
317
impl ArtiKeystoreConfigBuilder {
318
    /// Check that the keystore configuration is valid
319
    #[cfg(not(feature = "keymgr"))]
320
    #[allow(clippy::unnecessary_wraps)]
321
    fn validate(&self) -> Result<(), ConfigBuildError> {
322
        use BoolOrAuto as BoA;
323
        use ExplicitOrAuto as EoA;
324

            
325
        // Keystore support is disabled unless the `keymgr` feature is enabled.
326
        if self.enabled == Some(BoA::Explicit(true)) {
327
            return Err(ConfigBuildError::Inconsistent {
328
                fields: ["enabled"].map(Into::into).into_iter().collect(),
329
                problem: "keystore enabled=true, but keymgr feature not enabled".into(),
330
            });
331
        }
332

            
333
        let () = match self.primary.kind {
334
            // only enabled OR kind may be set, and when keymgr is not enabled they must be false|disabled
335
            None | Some(EoA::Auto) => Ok(()),
336
            _ => Err(ConfigBuildError::Inconsistent {
337
                fields: ["enabled", "kind"].map(Into::into).into_iter().collect(),
338
                problem: "kind!=auto, but keymgr feature not enabled".into(),
339
            }),
340
        }?;
341

            
342
        Ok(())
343
    }
344

            
345
    /// Check that the keystore configuration is valid
346
    #[cfg(feature = "keymgr")]
347
    #[allow(clippy::unnecessary_wraps)]
348
5727
    fn validate(&self) -> Result<(), ConfigBuildError> {
349
5727
        Ok(())
350
5727
    }
351

            
352
    /// Add a `CTorServiceKeystoreConfigBuilder` to this builder.
353
8
    pub fn ctor_service(&mut self, builder: CTorServiceKeystoreConfigBuilder) -> &mut Self {
354
8
        self.ctor.ctor_service(builder);
355
8
        self
356
8
    }
357
}
358

            
359
impl CTorKeystoreConfigBuilder {
360
    /// Ensure no C Tor keystores are configured.
361
    /// (C Tor keystores are only supported if the `ctor-keystore` is enabled).
362
    #[cfg(not(feature = "ctor-keystore"))]
363
    fn validate(&self) -> Result<(), ConfigBuildError> {
364
        let no_compile_time_support = |field: &str| ConfigBuildError::NoCompileTimeSupport {
365
            field: field.into(),
366
            problem: format!("{field} configured but ctor-keystore feature not enabled"),
367
        };
368

            
369
        if self
370
            .services
371
            .stores
372
            .as_ref()
373
            .map(|s| !s.is_empty())
374
            .unwrap_or_default()
375
        {
376
            return Err(no_compile_time_support("C Tor service keystores"));
377
        }
378

            
379
        if self
380
            .clients
381
            .stores
382
            .as_ref()
383
            .map(|s| !s.is_empty())
384
            .unwrap_or_default()
385
        {
386
            return Err(no_compile_time_support("C Tor client keystores"));
387
        }
388

            
389
        Ok(())
390
    }
391

            
392
    /// Validate the configured C Tor keystores.
393
    #[cfg(feature = "ctor-keystore")]
394
5727
    fn validate(&self) -> Result<(), ConfigBuildError> {
395
        use itertools::Itertools as _;
396
        use itertools::chain;
397

            
398
5727
        let Self { services, clients } = self;
399
5727
        let mut ctor_store_ids = chain![
400
5727
            services.stores.iter().flatten().map(|s| &s.id),
401
5727
            clients.stores.iter().flatten().map(|s| &s.id)
402
        ];
403

            
404
        // This is also validated by the KeyMgrBuilder (but it's a good idea to catch this sort of
405
        // mistake at configuration-time regardless).
406
5727
        if !ctor_store_ids.all_unique() {
407
4
            return Err(ConfigBuildError::Inconsistent {
408
4
                fields: ["id"].map(Into::into).into_iter().collect(),
409
4
                problem: "the C Tor keystores do not have unique IDs".into(),
410
4
            });
411
5723
        }
412

            
413
5723
        Ok(())
414
5727
    }
415

            
416
    /// Add a `CTorServiceKeystoreConfigBuilder` to this builder.
417
8
    pub fn ctor_service(&mut self, builder: CTorServiceKeystoreConfigBuilder) -> &mut Self {
418
8
        if let Some(ref mut stores) = self.services.stores {
419
4
            stores.push(builder);
420
4
        } else {
421
4
            self.services.stores = Some(vec![builder]);
422
4
        }
423

            
424
8
        self
425
8
    }
426
}
427

            
428
#[cfg(test)]
429
mod test {
430
    // @@ begin test lint list maintained by maint/add_warning @@
431
    #![allow(clippy::bool_assert_comparison)]
432
    #![allow(clippy::clone_on_copy)]
433
    #![allow(clippy::dbg_macro)]
434
    #![allow(clippy::mixed_attributes_style)]
435
    #![allow(clippy::print_stderr)]
436
    #![allow(clippy::print_stdout)]
437
    #![allow(clippy::single_char_pattern)]
438
    #![allow(clippy::unwrap_used)]
439
    #![allow(clippy::unchecked_duration_subtraction)]
440
    #![allow(clippy::useless_vec)]
441
    #![allow(clippy::needless_pass_by_value)]
442
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
443

            
444
    use super::*;
445

            
446
    use std::path::PathBuf;
447
    use std::str::FromStr as _;
448
    use tor_config::assert_config_error;
449

            
450
    /// Helper for creating [`CTorServiceKeystoreConfigBuilders`].
451
    fn svc_config_builder(
452
        id: &str,
453
        path: &str,
454
        nickname: &str,
455
    ) -> CTorServiceKeystoreConfigBuilder {
456
        let mut b = CTorServiceKeystoreConfigBuilder::default();
457
        b.id(KeystoreId::from_str(id).unwrap());
458
        b.path(PathBuf::from(path));
459
        b.nickname(HsNickname::from_str(nickname).unwrap());
460
        b
461
    }
462

            
463
    /// Helper for creating [`CTorClientKeystoreConfigBuilders`].
464
    fn client_config_builder(id: &str, path: &str) -> CTorClientKeystoreConfigBuilder {
465
        let mut b = CTorClientKeystoreConfigBuilder::default();
466
        b.id(KeystoreId::from_str(id).unwrap());
467
        b.path(PathBuf::from(path));
468
        b
469
    }
470

            
471
    #[test]
472
    #[cfg(all(feature = "ctor-keystore", feature = "keymgr"))]
473
    fn invalid_config() {
474
        let mut builder = ArtiKeystoreConfigBuilder::default();
475
        // Push two clients with the same (default) ID:
476
        builder
477
            .ctor()
478
            .clients()
479
            .access()
480
            .push(client_config_builder("foo", "/var/lib/foo"));
481

            
482
        builder
483
            .ctor()
484
            .clients()
485
            .access()
486
            .push(client_config_builder("foo", "/var/lib/bar"));
487
        let err = builder.build().unwrap_err();
488

            
489
        assert_config_error!(
490
            err,
491
            Inconsistent,
492
            "the C Tor keystores do not have unique IDs"
493
        );
494

            
495
        let mut builder = ArtiKeystoreConfigBuilder::default();
496
        // Push two services with the same ID:
497
        builder
498
            .ctor_service(svc_config_builder("foo", "/var/lib/foo", "pungent"))
499
            .ctor_service(svc_config_builder("foo", "/var/lib/foo", "pungent"));
500
        let err = builder.build().unwrap_err();
501

            
502
        assert_config_error!(
503
            err,
504
            Inconsistent,
505
            "the C Tor keystores do not have unique IDs"
506
        );
507

            
508
        let mut builder = ArtiKeystoreConfigBuilder::default();
509
        // Push two services with different IDs but same nicknames:
510
        builder
511
            .ctor_service(svc_config_builder("foo", "/var/lib/foo", "pungent"))
512
            .ctor_service(svc_config_builder("bar", "/var/lib/bar", "pungent"));
513
        let err = builder.build().unwrap_err();
514

            
515
        assert_config_error!(
516
            err,
517
            Inconsistent,
518
            "Multiple C Tor service keystores for service with nickname pungent"
519
        );
520
    }
521

            
522
    #[test]
523
    #[cfg(all(not(feature = "ctor-keystore"), feature = "keymgr"))]
524
    fn invalid_config() {
525
        let mut builder = ArtiKeystoreConfigBuilder::default();
526
        builder
527
            .ctor()
528
            .clients()
529
            .access()
530
            .push(client_config_builder("foo", "/var/lib/foo"));
531
        let err = builder.build().unwrap_err();
532

            
533
        assert_config_error!(
534
            err,
535
            NoCompileTimeSupport,
536
            "C Tor client keystores configured but ctor-keystore feature not enabled"
537
        );
538

            
539
        let mut builder = ArtiKeystoreConfigBuilder::default();
540
        builder.ctor_service(svc_config_builder("foo", "/var/lib/foo", "pungent"));
541
        let err = builder.build().unwrap_err();
542

            
543
        assert_config_error!(
544
            err,
545
            NoCompileTimeSupport,
546
            "C Tor service keystores configured but ctor-keystore feature not enabled"
547
        );
548
    }
549

            
550
    #[test]
551
    #[cfg(not(feature = "keymgr"))]
552
    fn invalid_config() {
553
        let mut builder = ArtiKeystoreConfigBuilder::default();
554
        builder.enabled(BoolOrAuto::Explicit(true));
555

            
556
        let err = builder.build().unwrap_err();
557
        assert_config_error!(
558
            err,
559
            Inconsistent,
560
            "keystore enabled=true, but keymgr feature not enabled"
561
        );
562
    }
563

            
564
    #[test]
565
    #[cfg(feature = "ctor-keystore")]
566
    fn valid_config() {
567
        let mut builder = ArtiKeystoreConfigBuilder::default();
568
        builder
569
            .ctor()
570
            .clients()
571
            .access()
572
            .push(client_config_builder("foo", "/var/lib/foo"));
573
        builder
574
            .ctor()
575
            .clients()
576
            .access()
577
            .push(client_config_builder("bar", "/var/lib/bar"));
578

            
579
        let res = builder.build();
580
        assert!(res.is_ok(), "{:?}", res);
581
    }
582

            
583
    #[test]
584
    #[cfg(all(not(feature = "ctor-keystore"), feature = "keymgr"))]
585
    fn valid_config() {
586
        let mut builder = ArtiKeystoreConfigBuilder::default();
587
        builder
588
            .enabled(BoolOrAuto::Explicit(true))
589
            .primary()
590
            .kind(ExplicitOrAuto::Explicit(ArtiKeystoreKind::Native));
591

            
592
        let res = builder.build();
593
        assert!(res.is_ok(), "{:?}", res);
594
    }
595
}