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
    define_list_builder_helper, impl_not_auto_value, impl_standard_builder, BoolOrAuto,
11
    ExplicitOrAuto,
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
6508
#[derive(Debug, Clone, Builder, Eq, PartialEq, Serialize, Deserialize, Getters)]
35
#[builder(derive(Serialize, Deserialize, Debug))]
36
6
#[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
6592
#[derive(Debug, Clone, Builder, Eq, PartialEq, Serialize, Deserialize, Getters)]
65
#[builder(derive(Serialize, Deserialize, Debug))]
66
2
#[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
6504
#[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
40
#[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
    fn try_from(value: CTorServiceKeystoreConfigBuilderMap) -> Result<Self, Self::Error> {
145
        let mut list_builder = CTorServiceKeystoreConfigMapBuilder::default();
146
        for (nickname, mut cfg) in value {
147
            match &cfg.nickname {
148
                Some(n) if n == &nickname => (),
149
                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
            cfg.nickname = Some(nickname);
158
            list_builder.access().push(cfg);
159
        }
160
        Ok(list_builder)
161
    }
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
3168
fn build_ctor_service_list(
195
3168
    ctor_stores: Vec<CTorServiceKeystoreConfig>,
196
3168
) -> Result<CTorServiceKeystoreConfigMap, ConfigBuildError> {
197
    use itertools::Itertools as _;
198

            
199
3170
    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
3168
    }
205
3168

            
206
3168
    let mut map = BTreeMap::new();
207
3170
    for service in ctor_stores {
208
4
        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
2
        };
217
    }
218

            
219
3166
    Ok(map)
220
3168
}
221

            
222
/// C Tor [`ArtiNativeKeystore`](crate::ArtiNativeKeystore) configuration
223
32
#[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
3166
fn build_ctor_client_store_config(
264
3166
    ctor_stores: Vec<CTorClientKeystoreConfig>,
265
3166
) -> Result<CTorClientKeystoreConfigList, ConfigBuildError> {
266
    use itertools::Itertools as _;
267

            
268
3168
    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
3166
    }
274
3166

            
275
3166
    Ok(ctor_stores)
276
3166
}
277

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

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

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

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

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

            
301
480
        Some(kind)
302
480
    }
303

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

            
309
    /// The ctor client keystore configs
310
480
    pub fn ctor_client_stores(&self) -> impl Iterator<Item = &CTorClientKeystoreConfig> {
311
480
        self.ctor.clients.iter()
312
480
    }
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
3172
    fn validate(&self) -> Result<(), ConfigBuildError> {
349
3172
        Ok(())
350
3172
    }
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
3172
    fn validate(&self) -> Result<(), ConfigBuildError> {
395
        use itertools::chain;
396
        use itertools::Itertools as _;
397

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

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

            
416
3168
        Ok(())
417
3172
    }
418

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

            
427
8
        self
428
8
    }
429
}
430

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

            
447
    use super::*;
448

            
449
    use std::path::PathBuf;
450
    use std::str::FromStr as _;
451
    use tor_config::assert_config_error;
452

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

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

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

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

            
492
        assert_config_error!(
493
            err,
494
            Inconsistent,
495
            "the C Tor keystores do not have unique IDs"
496
        );
497

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

            
505
        assert_config_error!(
506
            err,
507
            Inconsistent,
508
            "the C Tor keystores do not have unique IDs"
509
        );
510

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

            
518
        assert_config_error!(
519
            err,
520
            Inconsistent,
521
            "Multiple C Tor service keystores for service with nickname pungent"
522
        );
523
    }
524

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

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

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

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

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

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

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

            
582
        let res = builder.build();
583
        assert!(res.is_ok(), "{:?}", res);
584
    }
585

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

            
595
        let res = builder.build();
596
        assert!(res.is_ok(), "{:?}", res);
597
    }
598
}