signstar_config/nethsm/
backend.rs

1//! Backend handling for [`NetHsm`].
2//!
3//! Based on a [`NetHsm`], [`AdminCredentials`] and a [`HermeticParallelConfig`] this module offers
4//! the ability to populate a [`NetHsm`] backend with the help of the [`NetHsmBackend`] struct.
5//!
6//! Using [`NetHsmBackend::sync`] all users and keys configured in [`HermeticParallelConfig`]
7//! are created and adapted to changes upon re-run.
8//! With the help of [`NetHsmBackend::state`] the current [`State`] of a [`NetHsm`] backend can
9//! be created and compared with e.g. the [`State`] representation of a [`HermeticParallelConfig`].
10//!
11//! # Note
12//!
13//! This module only works with data for the same iteration (i.e. the iteration of the
14//! [`AdminCredentials`] and those of the [`NetHsm`] backend must match).
15
16use std::{collections::HashSet, str::FromStr};
17
18use log::{debug, trace, warn};
19use nethsm::{
20    CryptographicKeyContext,
21    FullCredentials,
22    KeyId,
23    KeyMechanism,
24    KeyType,
25    NamespaceId,
26    NetHsm,
27    OpenPgpKeyUsageFlags,
28    Passphrase,
29    SystemState,
30    UserId,
31    UserRole,
32    Utc,
33};
34use nethsm_config::{FilterUserKeys, HermeticParallelConfig, UserMapping};
35use pgp::composed::{Deserializable, SignedPublicKey};
36
37use super::{Error, state::StateType};
38use crate::{AdminCredentials, KeyState, State, UserState, nethsm::state::KeyCertificateState};
39
40/// Creates all _R-Administrators_ on a [`NetHsm`].
41///
42/// If users exist already, only their passphrase is set.
43///
44/// # Note
45///
46/// Uses the `nethsm` with the [default
47/// _R-Administrator_][`AdminCredentials::get_default_administrator`].
48///
49/// # Errors
50///
51/// Returns an error if
52///
53/// - the default [`Administrator`][`UserRole::Administrator`] can not be retrieved from
54///   `admin_credentials`,
55/// - the default [`Administrator`][`UserRole::Administrator`] credentials cannot be used with the
56///   `nethsm`,
57/// - available users of the `nethsm` cannot be retrieved,
58/// - or one of the admin credentials cannot be added, or updated.
59fn add_system_wide_admins(
60    nethsm: &NetHsm,
61    admin_credentials: &AdminCredentials,
62) -> Result<(), crate::Error> {
63    debug!(
64        "Setup system-wide administrators (R-Administrators) on NetHSM backend at {}",
65        nethsm.get_url()
66    );
67
68    let default_admin = &admin_credentials.get_default_administrator()?.name;
69    nethsm.use_credentials(default_admin)?;
70    let available_users = nethsm.get_users()?;
71    trace!(
72        "Available users on NetHSM: {}",
73        available_users
74            .iter()
75            .map(|user| user.to_string())
76            .collect::<Vec<_>>()
77            .join(", ")
78    );
79
80    for user in admin_credentials.get_administrators().iter() {
81        // Only add if user doesn't exist yet, else set passphrase
82        if !available_users.contains(&user.name) {
83            nethsm.add_user(
84                format!("System-wide Admin {}", user.name),
85                UserRole::Administrator,
86                user.passphrase.clone(),
87                Some(user.name.clone()),
88            )?;
89        } else {
90            nethsm.set_user_passphrase(user.name.clone(), user.passphrase.clone())?;
91        }
92    }
93    Ok(())
94}
95
96/// Retrieves the first available user in the [`Administrator`][`UserRole::Administrator`]
97/// (*N-Administrator*) role in a namespace.
98///
99/// Derives a list of users in the [`Administrator`][`UserRole::Administrator`] role in `namespace`
100/// from `available_users`.
101/// Ensures that at least one of the users is available on the `nethsm`.
102///
103/// # Errors
104///
105/// Returns an error if
106/// - user information of an *N-Administrator* cannot be retrieved,
107/// - or no *N-Administrator* is available in the `namespace`.
108fn get_first_available_namespace_admin(
109    nethsm: &NetHsm,
110    admin_credentials: &AdminCredentials,
111    available_users: &[UserId],
112    namespace: &NamespaceId,
113) -> Result<UserId, crate::Error> {
114    debug!("Get the first available N-Administrator in namespace \"{namespace}\"");
115
116    // Retrieve the list of users that are both in the namespace and match an entry in the list of
117    // N-Administrators in the administrative credentials.
118    let namespace_admins = available_users
119        .iter()
120        .filter(|user| {
121            user.namespace() == Some(namespace)
122                && admin_credentials
123                    .get_namespace_administrators()
124                    .iter()
125                    .any(|creds| &creds.name == *user)
126        })
127        .cloned()
128        .collect::<Vec<UserId>>();
129
130    let mut checked_namespace_admins = Vec::new();
131    for namespace_admin in namespace_admins {
132        if Into::<UserRole>::into(nethsm.get_user(&namespace_admin)?.role)
133            == UserRole::Administrator
134        {
135            checked_namespace_admins.push(namespace_admin);
136        }
137    }
138
139    debug!(
140        "All N-Administrators in namespace \"{namespace}\": {}",
141        checked_namespace_admins
142            .iter()
143            .map(|user| user.to_string())
144            .collect::<Vec<String>>()
145            .join(", ")
146    );
147
148    if checked_namespace_admins.is_empty() {
149        return Err(Error::NamespaceHasNoAdmin {
150            namespace: namespace.clone(),
151            url: nethsm.get_url(),
152        }
153        .into());
154    }
155
156    // Select the first N-Administrator in the namespace.
157    let Some(admin) = checked_namespace_admins.first() else {
158        return Err(Error::NamespaceHasNoAdmin {
159            namespace: namespace.clone(),
160            url: nethsm.get_url(),
161        }
162        .into());
163    };
164
165    Ok(admin.clone())
166}
167
168/// Sets up all _N-Administrators_ and their respective namespaces.
169///
170/// # Note
171///
172/// This function uses the `nethsm` with the [default
173/// _R-Administrator_][`AdminCredentials::get_default_administrator`], but may switch to a
174/// namespace-specific _N-Administrator_ for individual operations.
175/// If this function succeeds, the `nethsm` is guaranteed to use the [default
176/// _R-Administrator_][`AdminCredentials::get_default_administrator`] again.
177/// If this function fails, the `nethsm` may still use a namespace-specific _N-Administrator_.
178///
179/// # Errors
180///
181/// Returns an error if
182///
183/// - user information cannot be retrieved from the `nethsm`,
184/// - the available namespaces cannot be retrieved from the `nethsm`,
185/// - one of the N-Administrators in the `admin_credentials` is not in a namespace,
186/// - a namespace exists already, but no known N-Administrator is available for it,
187/// - an N-Administrator and its namespace exist already, but that user's passphrase cannot be set,
188/// - an N-Administrator does not yet exist and cannot be added,
189/// - a namespace does not yet exist and cannot be added,
190/// - or switching back to the default R-Administrator credentials fails.
191fn add_namespace_admins(
192    nethsm: &NetHsm,
193    admin_credentials: &AdminCredentials,
194) -> Result<(), crate::Error> {
195    debug!(
196        "Setup namespace administrators (N-Administrators) on NetHSM backend at {}",
197        nethsm.get_url()
198    );
199
200    // Use the default R-Administrator for authentication to the backend by default.
201    let default_admin = &admin_credentials.get_default_administrator()?.name;
202    nethsm.use_credentials(default_admin)?;
203
204    let available_users = nethsm.get_users()?;
205    trace!(
206        "The available users on the NetHSM backend at {} are: {}",
207        nethsm.get_url(),
208        available_users
209            .iter()
210            .map(|user| user.to_string())
211            .collect::<Vec<String>>()
212            .join(", ")
213    );
214    let available_namespaces = nethsm.get_namespaces()?;
215    trace!(
216        "The available namespaces on the NetHSM backend at {} are: {}",
217        nethsm.get_url(),
218        available_namespaces
219            .iter()
220            .map(|namespace| namespace.to_string())
221            .collect::<Vec<String>>()
222            .join(", ")
223    );
224
225    // Extract the namespace from each namespace administrator found in the administrative
226    // credentials.
227    for user in admin_credentials.get_namespace_administrators() {
228        let Some(namespace) = user.name.namespace() else {
229            return Err(Error::NamespaceAdminHasNoNamespace {
230                user: user.name.clone(),
231            }
232            .into());
233        };
234
235        let namespace_exists = available_namespaces.contains(namespace);
236        if namespace_exists {
237            // Select the first available N-Administrator credentials for interacting with the
238            // NetHSM backend.
239            // This might be the targeted user itself!
240            nethsm.use_credentials(&get_first_available_namespace_admin(
241                nethsm,
242                admin_credentials,
243                &available_users,
244                namespace,
245            )?)?;
246        }
247
248        // If the list of available users on the NetHSM does not include the given N-Administrator,
249        // we create the user.
250        if available_users.contains(&user.name) {
251            // Set the passphrase of the user.
252            nethsm.set_user_passphrase(user.name.clone(), user.passphrase.clone())?;
253        } else {
254            nethsm.add_user(
255                format!("Namespace Admin {}", user.name),
256                UserRole::Administrator,
257                user.passphrase.clone(),
258                Some(user.name.clone()),
259            )?;
260
261            // If the namespace does not yet exist add the namespace (authenticated as the default
262            // R-Administrator).
263            if !namespace_exists {
264                nethsm.add_namespace(namespace)?;
265            }
266        }
267        // Always use the default R-Administrator again.
268        nethsm.use_credentials(default_admin)?;
269    }
270
271    Ok(())
272}
273
274/// Sets up all system-wide, non-administrative users based on provided credentials.
275///
276/// # Note
277///
278/// It is assumed that the [default
279/// _R-Administrator_][`AdminCredentials::get_default_administrator`] and system-wide keys are
280/// already set up, before calling this function (see `add_system_wide_admins` and
281/// `add_system_wide_keys`, respectively).
282///
283/// This function uses the `nethsm` with the [default
284/// _R-Administrator_][`AdminCredentials::get_default_administrator`] and is guaranteed to do so
285/// when it finishes.
286///
287/// # Errors
288///
289/// Returns an error if
290///
291/// - there are no matching credentials in `user_credentials` for a user in the list of all
292///   available system-wide, non-administrative users,
293/// - a user exists already, but its passphrase cannot be set,
294/// - a user does not yet exist and it cannot be added,
295/// - a user has a tag and deleting it fails,
296/// - or adding a tag to a user fails.
297fn add_non_administrative_users(
298    nethsm: &NetHsm,
299    admin_credentials: &AdminCredentials,
300    users: &[UserMapping],
301    user_credentials: &[FullCredentials],
302) -> Result<(), crate::Error> {
303    debug!(
304        "Setup non-administrative, system-wide users on NetHSM backend at {}",
305        nethsm.get_url()
306    );
307
308    let default_admin = &admin_credentials.get_default_administrator()?.name;
309    nethsm.use_credentials(default_admin)?;
310    let available_users = nethsm.get_users()?;
311    debug!("Available users: {available_users:?}");
312
313    for mapping in users.iter() {
314        // Add all users of the mapping.
315        for (user, role, tags) in mapping
316            .get_nethsm_user_role_and_tags()
317            .iter()
318            // Only use system-wide, non-administrative users.
319            .filter(|(user, role, _tags)| !user.is_namespaced() && role != &UserRole::Administrator)
320        {
321            let Some(creds) = user_credentials.iter().find(|creds| &creds.name == user) else {
322                return Err(Error::UserMissingPassphrase { user: user.clone() }.into());
323            };
324
325            if available_users.contains(user) {
326                nethsm.set_user_passphrase(user.clone(), creds.passphrase.clone())?;
327            } else {
328                nethsm.add_user(
329                    format!("{role} user {user}"),
330                    *role,
331                    creds.passphrase.clone(),
332                    Some(user.clone()),
333                )?;
334            }
335
336            // Add tags to users.
337            for tag in tags {
338                for available_tag in nethsm.get_user_tags(user)? {
339                    nethsm.delete_user_tag(user, available_tag.as_str())?;
340                }
341                nethsm.add_user_tag(user, tag.as_str())?;
342            }
343        }
344    }
345
346    Ok(())
347}
348
349/// Sets up all namespaced non-administrative users.
350///
351/// # Note
352///
353/// It is assumed that _N-Administrators_ and namespaced keys are already set up, before calling
354/// this function (see `add_namespace_admins` and `add_namespaced_keys`, respectively).
355///
356/// This function uses the `nethsm` with the [default
357/// _R-Administrator_][`AdminCredentials::get_default_administrator`], but may switch to a
358/// namespace-specific _N-Administrator_ for individual operations.
359/// If this function succeeds, the `nethsm` is guaranteed to use the [default
360/// _R-Administrator_][`AdminCredentials::get_default_administrator`] again.
361/// If this function fails, the `nethsm` may still use a namespace-specific _N-Administrator_.
362///
363/// # Errors
364///
365/// Returns an error if
366///
367/// - a namespaced user is not in a namespace,
368/// - the namespace of a user does not exist,
369/// - the namespace of a user exists, but no usable *N-Administrator* for it is known,
370/// - there are no matching credentials in `user_credentials` for a user in the list of all,
371/// - a user exists already, but its passphrase cannot be set,
372/// - a user does not yet exist and cannot be created,
373/// - a tag cannot be removed from a user,
374/// - or a tag cannot be added to a user.
375fn add_namespaced_non_administrative_users(
376    nethsm: &NetHsm,
377    admin_credentials: &AdminCredentials,
378    users: &[UserMapping],
379    user_credentials: &[FullCredentials],
380) -> Result<(), crate::Error> {
381    debug!(
382        "Setup non-administrative, namespaced users on NetHSM backend at {}",
383        nethsm.get_url()
384    );
385
386    // Use the default R-Administrator for authentication to the backend by default.
387    let default_admin = &admin_credentials.get_default_administrator()?.name;
388    nethsm.use_credentials(default_admin)?;
389
390    let available_users = nethsm.get_users()?;
391    let available_namespaces = nethsm.get_namespaces()?;
392
393    for mapping in users.iter() {
394        for (user, role, tags) in mapping
395            .get_nethsm_user_role_and_tags()
396            .iter()
397            // Only use non-administrative, namespaced users on the NetHSM backend.
398            .filter(|(user, role, _tags)| user.is_namespaced() && role != &UserRole::Administrator)
399        {
400            // Extract the namespace of the user and ensure that the namespace exists already.
401            let Some(namespace) = user.namespace() else {
402                return Err(Error::NamespaceUserNoNamespace { user: user.clone() }.into());
403            };
404            if !available_namespaces.contains(namespace) {
405                return Err(Error::NamespaceMissing {
406                    namespace: namespace.clone(),
407                }
408                .into());
409            }
410
411            // Select the first available N-Administrator credentials for interacting with the
412            // NetHSM backend.
413            nethsm.use_credentials(&get_first_available_namespace_admin(
414                nethsm,
415                admin_credentials,
416                &available_users,
417                namespace,
418            )?)?;
419
420            // Retrieve credentials for the specific user.
421            let Some(creds) = user_credentials.iter().find(|creds| &creds.name == user) else {
422                return Err(Error::UserMissingPassphrase { user: user.clone() }.into());
423            };
424
425            // If the user exists already, only set its passphrase, otherwise create it.
426            if available_users.contains(user) {
427                nethsm.set_user_passphrase(user.clone(), creds.passphrase.clone())?;
428            } else {
429                nethsm.add_user(
430                    format!("{role} user {user}"),
431                    *role,
432                    creds.passphrase.clone(),
433                    Some(user.clone()),
434                )?;
435            }
436
437            // Delete all tags of the user.
438            let available_tags = nethsm.get_user_tags(user)?;
439            for available_tag in available_tags {
440                nethsm.delete_user_tag(user, available_tag.as_str())?;
441            }
442            // Setup tags for the user.
443            for tag in tags.iter() {
444                nethsm.add_user_tag(user, tag)?;
445            }
446        }
447    }
448    // Always use the default R-Administrator again.
449    nethsm.use_credentials(default_admin)?;
450
451    Ok(())
452}
453
454/// Comparable components of a key setup between a [`NetHsm`] backend and a Signstar config.
455struct KeySetupComparison {
456    /// The type of state, that the data originates from.
457    pub state_type: StateType,
458    /// The key type of the setup.
459    pub key_type: KeyType,
460    /// The key mechanisms of the setup.
461    pub key_mechanisms: HashSet<KeyMechanism>,
462}
463
464/// Compares the key setups of a key from a Signstar config and that of a NetHSM backend.
465///
466/// Compares the [`KeyType`] and [`KeyMechanism`]s of `key_setup_a` and `key_setup_b`, which both
467/// have to be identical.
468///
469/// Emits a warning if the [`KeyType`] or list of [`KeyMechanism`]s of `key_setup_a` and
470/// `key_setup_b` do not match.
471fn compare_key_setups(
472    key_id: &KeyId,
473    namespace: Option<&NamespaceId>,
474    key_setup_a: KeySetupComparison,
475    key_setup_b: KeySetupComparison,
476) {
477    let namespace = if let Some(namespace) = namespace {
478        format!(" in namespace \"{namespace}\"")
479    } else {
480        "".to_string()
481    };
482    debug!(
483        "Compare key setup of key \"{key_id}\"{namespace} in {} (A) and {} (B)",
484        key_setup_a.state_type, key_setup_b.state_type
485    );
486
487    // Compare key type and warn about mismatches.
488    if key_setup_b.key_type != key_setup_a.key_type {
489        warn!(
490            "Key type mismatch of key \"{key_id}\"{namespace}:\n{} (A): {}\n{} (B) backend: {}!",
491            key_setup_a.state_type,
492            key_setup_a.key_type,
493            key_setup_b.state_type,
494            key_setup_b.key_type
495        );
496    }
497
498    // Compare key mechanisms and warn about mismatches.
499    if key_setup_b.key_mechanisms != key_setup_a.key_mechanisms {
500        warn!(
501            "Key mechanisms mismatch for key \"{key_id}\"{namespace}:\n{} (A): {}\n{} (B): {}!",
502            key_setup_a.state_type,
503            key_setup_a
504                .key_mechanisms
505                .iter()
506                .map(|mechanism| mechanism.to_string())
507                .collect::<Vec<String>>()
508                .join(", "),
509            key_setup_b.state_type,
510            key_setup_b
511                .key_mechanisms
512                .iter()
513                .map(|mechanism| mechanism.to_string())
514                .collect::<Vec<String>>()
515                .join(", "),
516        );
517    }
518}
519
520/// Sets up all system-wide keys.
521///
522/// Creates any missing keys and adds the configured tags for all of them.
523/// If keys exist already, deletes all tags and adds the configured ones for them.
524///
525/// # Note
526///
527/// It is assumed that all required _R-Administrators_ have already been set up (see
528/// `add_system_wide_admins`) before calling this function.
529///
530/// This function uses the `nethsm` with the [default
531/// _R-Administrator_][`AdminCredentials::get_default_administrator`].
532///
533/// This function does not fail on mismatching keys, as it is assumed that keys are added
534/// intentionally and should not be deleted or altered.
535/// However, warnings are emitted if an existing key has a mismatching [`KeyType`] or
536/// [`KeyMechanisms`][`KeyMechanism`] from what is configured in the Signstar configuration file.
537///
538/// # Errors
539///
540/// Returns an error if
541///
542/// - the default system-wide *R-Administrator* cannot be retrieved or used for authentication,
543/// - the list of available keys on the NetHSM backend cannot be retrieved,
544/// - information about a single key cannot be retrieved from the NetHSM backend,
545/// - if a tag cannot be removed from an existing key,
546/// - if a tag cannot be added to an existing key,
547/// - or if a missing key cannot be created.
548fn add_system_wide_keys(
549    nethsm: &NetHsm,
550    admin_credentials: &AdminCredentials,
551    users: &[UserMapping],
552) -> Result<(), crate::Error> {
553    debug!(
554        "Setup system-wide cryptographic keys on NetHSM backend at {}",
555        nethsm.get_url()
556    );
557
558    // Use the default R-Administrator for authentication to the backend by default.
559    let default_admin = &admin_credentials.get_default_administrator()?.name;
560    nethsm.use_credentials(default_admin)?;
561
562    let available_keys = nethsm.get_keys(None)?;
563
564    for mapping in users {
565        for (_user, key_setup, tag) in
566            mapping.get_nethsm_user_key_and_tag(FilterUserKeys::SystemWide)
567        {
568            let key_id = key_setup.get_key_id();
569            if available_keys.contains(&key_setup.get_key_id()) {
570                // Retrieve information about the key.
571                let info = nethsm.get_key(&key_id)?;
572
573                // Compare the key setups.
574                compare_key_setups(
575                    &key_id,
576                    None,
577                    KeySetupComparison {
578                        state_type: StateType::SignstarConfig,
579                        key_type: key_setup.get_key_type(),
580                        key_mechanisms: HashSet::from_iter(key_setup.get_key_mechanisms()),
581                    },
582                    KeySetupComparison {
583                        state_type: StateType::NetHsm,
584                        key_type: info.r#type.into(),
585                        key_mechanisms: info.mechanisms.iter().map(Into::into).collect(),
586                    },
587                );
588
589                // Remove all existing tags.
590                if let Some(available_tags) = info.restrictions.tags {
591                    for available_tag in available_tags {
592                        nethsm.delete_key_tag(&key_id, available_tag.as_str())?;
593                    }
594                }
595                // Add the required tag to the key.
596                nethsm.add_key_tag(&key_id, tag.as_str())?;
597            } else {
598                // Add the key, including the required tag.
599                nethsm.generate_key(
600                    key_setup.get_key_type(),
601                    key_setup.get_key_mechanisms(),
602                    key_setup.get_key_length(),
603                    Some(key_id.clone()),
604                    Some(vec![tag]),
605                )?;
606            }
607        }
608    }
609
610    Ok(())
611}
612
613/// Sets up all namespaced keys and tags them.
614///
615/// Creates any missing keys and adds the configured tags for all of them.
616/// If keys exist already, deletes all tags and adds the configured ones for them.
617///
618/// # Note
619///
620/// It is assumed that _N-Administrators_ have already been set up, before calling
621/// this function (see `add_namespace_admins`).
622///
623/// This function uses the `nethsm` with the [default
624/// _R-Administrator_][`AdminCredentials::get_default_administrator`], but may switch to a
625/// namespace-specific _N-Administrator_ for individual operations.
626/// If this function succeeds, the `nethsm` is guaranteed to use the [default
627/// _R-Administrator_][`AdminCredentials::get_default_administrator`] again.
628/// If this function fails, the `nethsm` may still use a namespace-specific _N-Administrator_.
629///
630/// This function does not fail on mismatching keys, as it is assumed that keys are added
631/// intentionally and should not be deleted/altered.
632/// However, warnings are emitted if an existing key has a mismatching key type or key mechanisms
633/// from what is configured in the Signstar configuration file.
634///
635/// Opposite to the behavior of `add_system_wide_keys`, this function does not delete any tags from
636/// keys.
637/// This is due to [a bug in the NetHSM firmware], which leads to a crash when adding a tag to a
638/// key, trying to remove and then re-adding it again.
639///
640/// # Errors
641///
642/// Returns an error if
643///
644/// - the default system-wide *R-Administrator* cannot be retrieved or used for authentication,
645/// - retrieving the list of available users from the NetHSM backend fails,
646/// - a namespaced user mapped to a key is not in a namespace,
647/// - no usable *N-Administrator* for a namespace is known,
648/// - the available keys in the namespace cannot be retrieved,
649/// - information about a specific key in the namespace cannot be retrieved,
650/// - a tag cannot be added to an already existing key,
651/// - a new key cannot be generated,
652/// - or using the default system-wide administrator again fails.
653///
654/// [a bug in the NetHSM firmware]: https://github.com/Nitrokey/nethsm/issues/13
655fn add_namespaced_keys(
656    nethsm: &NetHsm,
657    admin_credentials: &AdminCredentials,
658    users: &[UserMapping],
659) -> Result<(), crate::Error> {
660    debug!(
661        "Setup namespaced cryptographic keys on NetHSM backend at {}",
662        nethsm.get_url()
663    );
664
665    // Use the default R-Administrator for authentication to the backend by default.
666    let default_admin = &admin_credentials.get_default_administrator()?.name;
667    nethsm.use_credentials(default_admin)?;
668
669    let available_users = nethsm.get_users()?;
670
671    for mapping in users {
672        for (user, key_setup, tag) in
673            mapping.get_nethsm_user_key_and_tag(FilterUserKeys::Namespaced)
674        {
675            let key_id = key_setup.get_key_id();
676            debug!("Set up key \"{key_id}\" with tag {tag} for user {user}");
677
678            // Extract the namespace from the user or return an error.
679            let Some(namespace) = user.namespace() else {
680                // Note: Returning this error is not really possible, as we are explicitly
681                // requesting tuples of namespaced user, key setup and tag.
682                return Err(Error::NamespaceUserNoNamespace { user }.into());
683            };
684
685            // Select the first available N-Administrator credentials for interacting with the
686            // NetHSM backend.
687            nethsm.use_credentials(&get_first_available_namespace_admin(
688                nethsm,
689                admin_credentials,
690                &available_users,
691                namespace,
692            )?)?;
693
694            let available_keys = nethsm.get_keys(None)?;
695
696            if available_keys.contains(&key_id) {
697                let key_info = nethsm.get_key(&key_id)?;
698
699                // Compare the key setups.
700                compare_key_setups(
701                    &key_id,
702                    Some(namespace),
703                    KeySetupComparison {
704                        state_type: StateType::SignstarConfig,
705                        key_type: key_setup.get_key_type(),
706                        key_mechanisms: HashSet::from_iter(key_setup.get_key_mechanisms()),
707                    },
708                    KeySetupComparison {
709                        state_type: StateType::NetHsm,
710                        key_type: key_info.r#type.into(),
711                        key_mechanisms: key_info.mechanisms.iter().map(Into::into).collect(),
712                    },
713                );
714
715                // If there are tags already, check if the tag we are looking for is already set and
716                // if so, skip to the next key.
717                if let Some(available_tags) = key_info.restrictions.tags {
718                    debug!(
719                        "Available tags for key \"{key_id}\" in namespace {namespace}: {}",
720                        available_tags.join(", ")
721                    );
722                    // NOTE: If the required tag is already set, continue to the next key.
723                    //       Without this we otherwise trigger a bug in the NetHSM firmware which
724                    //       breaks the connection after re-adding the tag for the key further down.
725                    //       (i.e. "Bad Status: HTTP version did not start with HTTP/")
726                    //       See https://github.com/Nitrokey/nethsm/issues/13 for details.
727                    if available_tags.len() == 1 && available_tags.contains(&tag) {
728                        continue;
729                    }
730                }
731
732                // Add the tag to the key.
733                nethsm.add_key_tag(&key_id, tag.as_str())?;
734            } else {
735                // Add the key, including the required tag.
736                nethsm.generate_key(
737                    key_setup.get_key_type(),
738                    key_setup.get_key_mechanisms(),
739                    key_setup.get_key_length(),
740                    Some(key_id.clone()),
741                    Some(vec![tag]),
742                )?;
743            }
744        }
745    }
746    // Always use the default R-Administrator again.
747    nethsm.use_credentials(default_admin)?;
748
749    Ok(())
750}
751
752/// Adds OpenPGP certificates for system-wide keys that are used for OpenPGP signing.
753///
754/// # Note
755///
756/// It is assumed that the [default
757/// _R-Administrator_][`AdminCredentials::get_default_administrator`], all system-wide keys and all
758/// system-wide non-administrative users are already set up, before calling this function
759/// (see `add_system_wide_admins`, `add_system_wide_keys` and `add_non_administrative_users`,
760/// respectively).
761///
762/// This function uses the `nethsm` with the [default
763/// _R-Administrator_][`AdminCredentials::get_default_administrator`], but may switch to a
764/// system-wide, non-administrative user for individual operations.
765/// If this function succeeds, the `nethsm` is guaranteed to use the [default
766/// _R-Administrator_][`AdminCredentials::get_default_administrator`] again.
767/// If this function fails, the `nethsm` may still use a system-wide, non-administrative user.
768///
769/// This function does not overwrite or alter existing OpenPGP certificates, as this would introduce
770/// inconsistencies between signatures created with a previous version of a certificate and those
771/// created with a new version of the certificate, which is hard to debug.
772///
773/// # Errors
774///
775/// Returns an error if
776///
777/// - using the default *R-Administrator* fails,
778/// - retrieving the names of all system-wide users fails,
779/// - retrieving the names of all system-wide keys fails,
780/// - a user used for OpenPGP signing does not exist,
781/// - the tags assigned to a user cannot be retrieved from the `nethsm`,
782/// - a user used for OpenPGP signing does not have a required tag,
783/// - a key used for OpenPGP signing does not exist,
784/// - the tags assigned to a key cannot be retrieved from the `nethsm`,
785/// - a key used for OpenPGP signing does not have a required tag,
786/// - the key setup for a key used for OpenPGP signing does not have at least one User ID,
787/// - the user assigned the same tag as the key that is used for OpenPGP signing cannot be used to
788///   create an OpenPGP certificate for the key,
789/// - or the default *R-Administrator* cannot be used to import the generated OpenPGP certificate
790///   for the key.
791fn add_system_wide_openpgp_certificates(
792    nethsm: &NetHsm,
793    admin_credentials: &AdminCredentials,
794    users: &[UserMapping],
795) -> Result<(), crate::Error> {
796    debug!(
797        "Setup OpenPGP certificates for system-wide cryptographic keys on NetHSM backend at {}",
798        nethsm.get_url()
799    );
800
801    // Use the default R-Administrator for authentication to the backend by default.
802    let default_admin = &admin_credentials.get_default_administrator()?.name;
803    nethsm.use_credentials(default_admin)?;
804
805    let available_users = nethsm.get_users()?;
806
807    for mapping in users {
808        // Continue to the next mapping if it is not used for signing.
809        if !matches!(mapping, UserMapping::SystemNetHsmOperatorSigning { .. }) {
810            continue;
811        }
812
813        for (user, key_setup, tag) in
814            mapping.get_nethsm_user_key_and_tag(FilterUserKeys::SystemWide)
815        {
816            // Get OpenPGP User IDs and version or continue to the next user/key setup if the
817            // mapping is not used for OpenPGP signing.
818            let CryptographicKeyContext::OpenPgp { user_ids, version } =
819                key_setup.get_key_context()
820            else {
821                debug!(
822                    "Skip creating an OpenPGP certificate for the key \"{}\" used by user \"{user}\" as it is not used in an OpenPGP context.",
823                    key_setup.get_key_id()
824                );
825                continue;
826            };
827
828            // Ensure the targeted user exists.
829            if !available_users.contains(&user) {
830                return Err(Error::UserMissing { user_id: user }.into());
831            }
832            // Ensure the required tag is assigned to the targeted user.
833            if !nethsm.get_user_tags(&user)?.contains(&tag) {
834                return Err(Error::UserMissingTag { user_id: user, tag }.into());
835            }
836
837            let available_keys = nethsm.get_keys(None)?;
838            let key_id = key_setup.get_key_id();
839
840            // Ensure the targeted key exists.
841            if !available_keys.contains(&key_id) {
842                return Err(Error::KeyMissing { key_id }.into());
843            }
844            // Ensure the required tag is assigned to the targeted key.
845            if !nethsm
846                .get_key(&key_id)?
847                .restrictions
848                .tags
849                .is_some_and(|tags| tags.contains(&tag))
850            {
851                return Err(Error::KeyIsMissingTag { key_id, tag }.into());
852            }
853
854            // Create the OpenPGP certificate if it does not exist yet.
855            if nethsm.get_key_certificate(&key_id)?.is_none() {
856                // Ensure the first OpenPGP User ID exists.
857                let Some(user_id) = user_ids.first() else {
858                    return Err(Error::OpenPgpUserIdMissing { key_id }.into());
859                };
860
861                // Switch to the dedicated user with access to the key to create an OpenPGP
862                // certificate for the key.
863                nethsm.use_credentials(&user)?;
864                let data = nethsm.create_openpgp_cert(
865                    &key_id,
866                    OpenPgpKeyUsageFlags::default(),
867                    user_id.clone(),
868                    Utc::now(),
869                    version,
870                )?;
871
872                // Switch back to the default R-Administrator for the import of the OpenPGP
873                // certificate.
874                nethsm.use_credentials(default_admin)?;
875                nethsm.import_key_certificate(&key_id, data)?;
876            }
877        }
878    }
879    // Always use the default R-Administrator again.
880    nethsm.use_credentials(default_admin)?;
881
882    Ok(())
883}
884
885/// Adds OpenPGP certificates for namespaced keys that are used for OpenPGP signing.
886///
887/// # Note
888///
889/// It is assumed that the [default
890/// _R-Administrator_][`AdminCredentials::get_default_administrator`], all namespaced keys, all
891/// _N-Administrators_ and all namespaced non-administrative users are already set up, before
892/// calling this function (see `add_system_wide_admins`, `add_namespaced_keys`,
893/// `add_namespace_admins` and `add_namespaced_non_administrative_users`, respectively).
894///
895/// This function uses the `nethsm` with the [default
896/// _R-Administrator_][`AdminCredentials::get_default_administrator`], but may switch to a
897/// namespace-specific _N-Administrator_ or non-administrative user for individual operations.
898/// If this function succeeds, the `nethsm` is guaranteed to use the [default
899/// _R-Administrator_][`AdminCredentials::get_default_administrator`] again.
900/// If this function fails, the `nethsm` may still use a namespace-specific _N-Administrator_ or
901/// non-administrative user.
902///
903/// This function does not overwrite or alter existing OpenPGP certificates, as this would introduce
904/// inconsistencies between signatures created with a previous version of a certificate and those
905/// created with a new version of the certificate, which is hard to debug.
906///
907/// # Errors
908///
909/// Returns an error if
910///
911/// - using the default *R-Administrator* fails,
912/// - retrieving the names of all users fails,
913/// - a namespaced user is not in a namespace,
914/// - no usable *N-Administrator* for a namespace is known,
915/// - a user used for OpenPGP signing does not exist,
916/// - the tags assigned to a user cannot be retrieved from the `nethsm`,
917/// - a user used for OpenPGP signing does not have a required tag,
918/// - retrieving the names of all keys in a namespace fails,
919/// - a key used for OpenPGP signing does not exist,
920/// - the tags assigned to a key cannot be retrieved from the `nethsm`,
921/// - a key used for OpenPGP signing does not have a required tag,
922/// - the key setup for a key used for OpenPGP signing does not have at least one User ID,
923/// - the user assigned the same tag as the key that is used for OpenPGP signing cannot be used to
924///   create an OpenPGP certificate for the key,
925/// - or the *N-Administrator* cannot be used to import the generated OpenPGP certificate for the
926///   key.
927fn add_namespaced_openpgp_certificates(
928    nethsm: &NetHsm,
929    admin_credentials: &AdminCredentials,
930    users: &[UserMapping],
931) -> Result<(), crate::Error> {
932    debug!(
933        "Setup OpenPGP certificates for namespaced cryptographic keys on NetHSM backend at {}",
934        nethsm.get_url()
935    );
936
937    // Use the default R-Administrator for authentication to the backend by default.
938    let default_admin = &admin_credentials.get_default_administrator()?.name;
939    nethsm.use_credentials(default_admin)?;
940
941    let available_users = nethsm.get_users()?;
942
943    for mapping in users {
944        // Continue to the next mapping if it is not used for signing.
945        if !matches!(mapping, UserMapping::SystemNetHsmOperatorSigning { .. }) {
946            continue;
947        }
948
949        for (user, key_setup, tag) in
950            mapping.get_nethsm_user_key_and_tag(FilterUserKeys::Namespaced)
951        {
952            // Get OpenPGP User IDs and version or continue to the next user/key setup if the
953            // mapping is not used for OpenPGP signing.
954            let CryptographicKeyContext::OpenPgp { user_ids, version } =
955                key_setup.get_key_context()
956            else {
957                continue;
958            };
959
960            // Extract the namespace from the user.
961            let Some(namespace) = user.namespace() else {
962                // Note: Returning this error is not really possible, as we are explicitly
963                // requesting tuples of namespaced user, key setup and tag.
964                return Err(Error::NamespaceUserNoNamespace { user }.into());
965            };
966
967            // Select the first available N-Administrator credentials for interacting with the
968            // NetHSM backend.
969            let admin = get_first_available_namespace_admin(
970                nethsm,
971                admin_credentials,
972                &available_users,
973                namespace,
974            )?;
975            nethsm.use_credentials(&admin)?;
976
977            // Ensure the targeted user exists.
978            if !available_users.contains(&user) {
979                return Err(Error::NamespaceUserMissing {
980                    user: user.clone(),
981                    namespace: namespace.clone(),
982                }
983                .into());
984            }
985            // Ensure the required tag is assigned to the targeted user.
986            let user_tags = nethsm.get_user_tags(&user)?;
987            if !user_tags.contains(&tag) {
988                return Err(Error::NamespaceUserMissingTag {
989                    user: user.clone(),
990                    namespace: namespace.clone(),
991                    tag,
992                }
993                .into());
994            }
995
996            let available_keys = nethsm.get_keys(None)?;
997            let key_id = key_setup.get_key_id();
998
999            // Ensure the targeted key exists.
1000            if !available_keys.contains(&key_id) {
1001                return Err(Error::NamespaceKeyMissing {
1002                    key_id,
1003                    namespace: namespace.clone(),
1004                }
1005                .into());
1006            }
1007            // Ensure the required tag is assigned to the targeted key.
1008            let pubkey = nethsm.get_key(&key_id)?;
1009            if !pubkey
1010                .restrictions
1011                .tags
1012                .is_some_and(|tags| tags.contains(&tag))
1013            {
1014                return Err(Error::NamespaceKeyMissesTag {
1015                    key_id,
1016                    namespace: namespace.clone(),
1017                    tag,
1018                }
1019                .into());
1020            }
1021
1022            // Create the OpenPGP certificate if it does not exist yet.
1023            if nethsm.get_key_certificate(&key_id)?.is_none() {
1024                // Ensure the first OpenPGP User ID exists.
1025                let Some(user_id) = user_ids.first() else {
1026                    return Err(Error::NamespaceOpenPgpUserIdMissing {
1027                        key_id,
1028                        namespace: namespace.clone(),
1029                    }
1030                    .into());
1031                };
1032
1033                // Switch to the dedicated user with access to the key to create an OpenPGP
1034                // certificate for the key.
1035                nethsm.use_credentials(&user)?;
1036                let data = nethsm.create_openpgp_cert(
1037                    &key_id,
1038                    OpenPgpKeyUsageFlags::default(),
1039                    user_id.clone(),
1040                    Utc::now(),
1041                    version,
1042                )?;
1043
1044                // Switch back to the N-Administrator for the import of the OpenPGP certificate.
1045                nethsm.use_credentials(&admin)?;
1046                nethsm.import_key_certificate(&key_id, data)?;
1047            }
1048        }
1049    }
1050    // Always use the default R-Administrator again.
1051    nethsm.use_credentials(default_admin)?;
1052
1053    Ok(())
1054}
1055
1056/// Retrieves the state for all users on a [`NetHsm`] backend.
1057///
1058/// # Note
1059///
1060/// Uses the `nethsm` with the [default
1061/// _R-Administrator_][`AdminCredentials::get_default_administrator`].
1062///
1063/// # Errors
1064///
1065/// Returns an error if
1066///
1067/// - using the credentials of the default *R-Administrator* fails,
1068/// - retrieving all user names of the NetHSM backend fails,
1069/// - retrieving information about a specific NetHSM user fails,
1070/// - or retrieving the tags of an *Operator* user fails.
1071pub(crate) fn get_user_states(
1072    nethsm: &NetHsm,
1073    admin_credentials: &AdminCredentials,
1074) -> Result<Vec<UserState>, crate::Error> {
1075    // Use the default R-Administrator.
1076    nethsm.use_credentials(&admin_credentials.get_default_administrator()?.name)?;
1077
1078    let mut users = Vec::new();
1079    for user_id in nethsm.get_users()? {
1080        let user_data = nethsm.get_user(&user_id)?;
1081        let tags = if user_data.role == UserRole::Operator.into() {
1082            nethsm.get_user_tags(&user_id)?
1083        } else {
1084            Vec::new()
1085        };
1086        users.push(UserState {
1087            name: user_id,
1088            role: user_data.role.into(),
1089            tags,
1090        });
1091    }
1092
1093    Ok(users)
1094}
1095
1096/// Retrieve the state of a key certificate.
1097///
1098/// Key certificates may be retrieved for system-wide keys or namespaced keys.
1099/// Returns a [`KeyCertificateState`], which may also encode reasons for why state cannot be
1100/// retrieved.
1101///
1102/// # Note
1103///
1104/// It is assumed that the current credentials for the `nethsm` provide access to the key
1105/// certificate of key `key_id`.
1106fn get_key_certificate_state(
1107    nethsm: &NetHsm,
1108    key_id: &KeyId,
1109    namespace: Option<&NamespaceId>,
1110) -> KeyCertificateState {
1111    // Provide a dedicated string for log messages in case a namespace is used.
1112    let namespace = if let Some(namespace) = namespace {
1113        format!(" in namespace \"{namespace}\"")
1114    } else {
1115        "".to_string()
1116    };
1117
1118    match nethsm.get_key_certificate(key_id) {
1119        Ok(Some(key_cert)) => {
1120            let public_key = match SignedPublicKey::from_reader_single(key_cert.as_slice()) {
1121                Ok((public_key, _armor_header)) => public_key,
1122                Err(error) => {
1123                    let message = format!(
1124                        "Unable to create OpenPGP certificate from key certificate of key \"{key_id}\"{namespace}:\n{error}"
1125                    );
1126                    debug!("{message}");
1127                    return KeyCertificateState::NotAnOpenPgpCertificate { message };
1128                }
1129            };
1130
1131            match TryInto::<CryptographicKeyContext>::try_into(public_key) {
1132                Ok(key_context) => KeyCertificateState::KeyContext(key_context),
1133                Err(error) => {
1134                    let message = format!(
1135                        "Unable to convert OpenPGP certificate of key \"{key_id}\"{namespace} to key context:\n{error}"
1136                    );
1137                    debug!("{message}");
1138                    KeyCertificateState::NotACryptographicKeyContext { message }
1139                }
1140            }
1141        }
1142        Ok(None) => KeyCertificateState::Empty,
1143        Err(error) => {
1144            let message = error.to_string();
1145            debug!("{message}");
1146            KeyCertificateState::Error { message }
1147        }
1148    }
1149}
1150
1151/// Retrieves the state for all keys on a [`NetHsm`] backend.
1152///
1153/// Collects each key, their [`KeyType`] and list of [`KeyMechanisms`][`KeyMechanism`].
1154/// Also attempts to derive a [`CryptographicKeyContext`] from the key certificate.
1155///
1156/// # Note
1157///
1158/// This function uses the `nethsm` with the [default
1159/// _R-Administrator_][`AdminCredentials::get_default_administrator`], but may switch to a
1160/// namespace-specific _N-Administrator_ for individual operations.
1161/// If this function succeeds, the `nethsm` is guaranteed to use the [default
1162/// _R-Administrator_][`AdminCredentials::get_default_administrator`] again.
1163/// If this function fails, the `nethsm` may still use a namespace-specific _N-Administrator_.
1164///
1165///
1166/// # Errors
1167///
1168/// Returns an error if
1169///
1170/// - using the default *R-Administrator* for authentication against the backend fails,
1171/// - retrieving the names of all system-wide keys on the backend fails,
1172/// - retrieving information on a specific system-wide key on the backend fails,
1173/// - an *N-Administrator* in `admin_credentials` is not actually in a namespace,
1174/// - using the credentials of an *N-Administrator* fails,
1175/// - retrieving the names of all namespaced keys on the backend fails,
1176/// - or retrieving information on a specific namespaced key on the backend fails.
1177fn get_key_states(
1178    nethsm: &NetHsm,
1179    admin_credentials: &AdminCredentials,
1180) -> Result<Vec<KeyState>, crate::Error> {
1181    // Use the default administrator
1182    let default_admin = &admin_credentials.get_default_administrator()?.name;
1183    nethsm.use_credentials(default_admin)?;
1184
1185    let mut keys = Vec::new();
1186    // Get the state of system-wide keys.
1187    for key_id in nethsm.get_keys(None)? {
1188        let key = nethsm.get_key(&key_id)?;
1189        let key_context = get_key_certificate_state(nethsm, &key_id, None);
1190
1191        keys.push(KeyState {
1192            name: key_id,
1193            namespace: None,
1194            tags: key.restrictions.tags.unwrap_or_default(),
1195            key_type: key.r#type.into(),
1196            mechanisms: key.mechanisms.iter().map(KeyMechanism::from).collect(),
1197            key_cert_state: key_context,
1198        });
1199    }
1200
1201    let mut seen_namespaces = HashSet::new();
1202    // Get the state of namespaced keys.
1203    for user_id in admin_credentials
1204        .get_namespace_administrators()
1205        .iter()
1206        .map(|creds| creds.name.clone())
1207    {
1208        // Extract the namespace of the user and ensure that the namespace exists already.
1209        let Some(namespace) = user_id.namespace() else {
1210            return Err(Error::NamespaceUserNoNamespace {
1211                user: user_id.clone(),
1212            }
1213            .into());
1214        };
1215
1216        // Only extract key information for the namespace if we have not already looked at it.
1217        if seen_namespaces.contains(namespace) {
1218            continue;
1219        }
1220        seen_namespaces.insert(namespace.clone());
1221
1222        nethsm.use_credentials(&user_id)?;
1223        for key_id in nethsm.get_keys(None)? {
1224            let key = nethsm.get_key(&key_id)?;
1225            let key_context = get_key_certificate_state(nethsm, &key_id, Some(namespace));
1226
1227            keys.push(KeyState {
1228                name: key_id,
1229                namespace: Some(namespace.clone()),
1230                tags: key.restrictions.tags.unwrap_or_default(),
1231                key_type: key.r#type.into(),
1232                mechanisms: key.mechanisms.iter().map(KeyMechanism::from).collect(),
1233                key_cert_state: key_context,
1234            });
1235        }
1236    }
1237
1238    // Always use the default *R-Administrator* again.
1239    nethsm.use_credentials(default_admin)?;
1240
1241    Ok(keys)
1242}
1243
1244/// A NetHSM backend that provides full control over its data.
1245///
1246/// This backend allows full control over the data in a [`NetHsm`], to the extend that is configured
1247/// by the tracked [`AdminCredentials`] and [`HermeticParallelConfig`].
1248#[derive(Debug)]
1249pub struct NetHsmBackend<'a, 'b> {
1250    nethsm: NetHsm,
1251    admin_credentials: &'a AdminCredentials,
1252    signstar_config: &'b HermeticParallelConfig,
1253}
1254
1255impl<'a, 'b> NetHsmBackend<'a, 'b> {
1256    /// Creates a new [`NetHsmBackend`].
1257    ///
1258    /// # Errors
1259    ///
1260    /// Returns an error if
1261    ///
1262    /// - the iteration of the `admin_credentials` does not match that of the `signstar_config`,
1263    /// - or retrieving the default administrator from the `admin_credentials` fails.
1264    ///
1265    /// # Examples
1266    ///
1267    /// ```
1268    /// use std::collections::HashSet;
1269    ///
1270    /// use nethsm::{FullCredentials, Connection, ConnectionSecurity, NetHsm};
1271    /// use nethsm_config::{
1272    ///     AdministrativeSecretHandling,
1273    ///     AuthorizedKeyEntryList,
1274    ///     ConfigInteractivity,
1275    ///     ConfigSettings,
1276    ///     HermeticParallelConfig,
1277    ///     NonAdministrativeSecretHandling,
1278    ///     UserMapping,
1279    /// };
1280    /// use signstar_config::{AdminCredentials, NetHsmBackend};
1281    ///
1282    /// # fn main() -> testresult::TestResult {
1283    /// // The NetHSM connection.
1284    /// let nethsm = NetHsm::new(
1285    ///     Connection::new(
1286    ///         "https://example.org/api/v1".try_into()?,
1287    ///         ConnectionSecurity::Unsafe,
1288    ///     ),
1289    ///     None,
1290    ///     None,
1291    ///     None,
1292    /// )?;
1293    /// // The administrative credentials.
1294    /// let admin_credentials = AdminCredentials::new(
1295    ///     1,
1296    ///     "backup-passphrase".parse()?,
1297    ///     "unlock-passphrase".parse()?,
1298    ///     vec![FullCredentials::new(
1299    ///         "admin".parse()?,
1300    ///         "admin-passphrase".parse()?,
1301    ///     )],
1302    ///     vec![FullCredentials::new(
1303    ///         "ns1~admin".parse()?,
1304    ///         "ns1-admin-passphrase".parse()?,
1305    ///     )],
1306    /// )?;
1307    /// // The Signstar config.
1308    /// let signstar_config = HermeticParallelConfig::new(
1309    ///     ConfigSettings::new(
1310    ///         "my_app".to_string(),
1311    ///         ConfigInteractivity::NonInteractive,
1312    ///         None,
1313    ///     ),
1314    ///     1,
1315    ///     AdministrativeSecretHandling::ShamirsSecretSharing,
1316    ///     NonAdministrativeSecretHandling::SystemdCreds,
1317    ///     HashSet::from([Connection::new(
1318    ///         "https://localhost:8443/api/v1/".parse()?,
1319    ///         "Unsafe".parse()?,
1320    ///     )]),
1321    ///     HashSet::from([
1322    ///         UserMapping::NetHsmOnlyAdmin("admin".parse()?),
1323    ///         UserMapping::SystemOnlyShareDownload {
1324    ///             system_user: "ssh-share-down".parse()?,
1325    ///             ssh_authorized_keys: AuthorizedKeyEntryList::new(vec!["ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?])?,
1326    ///         },
1327    ///         UserMapping::SystemOnlyShareUpload {
1328    ///             system_user: "ssh-share-up".parse()?,
1329    ///             ssh_authorized_keys: AuthorizedKeyEntryList::new(vec!["ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?])?,
1330    ///         }]),
1331    /// )?;
1332    ///
1333    /// let nethsm_backend = NetHsmBackend::new(nethsm, &admin_credentials, &signstar_config)?;
1334    /// # Ok(())
1335    /// # }
1336    /// ```
1337    pub fn new(
1338        nethsm: NetHsm,
1339        admin_credentials: &'a AdminCredentials,
1340        signstar_config: &'b HermeticParallelConfig,
1341    ) -> Result<Self, crate::Error> {
1342        debug!(
1343            "Create a new NetHSM backend for Signstar config at {}",
1344            nethsm.get_url()
1345        );
1346
1347        // Ensure that the iterations of administrative credentials and signstar config match.
1348        if admin_credentials.get_iteration() != signstar_config.get_iteration() {
1349            return Err(Error::IterationMismatch {
1350                admin_creds: admin_credentials.get_iteration(),
1351                signstar_config: signstar_config.get_iteration(),
1352            }
1353            .into());
1354        }
1355
1356        // Add all system-wide Administrators for the connection
1357        for user in admin_credentials.get_administrators() {
1358            nethsm.add_credentials(user.into());
1359        }
1360        // Add all namespace Administrators for the connection
1361        for user in admin_credentials.get_namespace_administrators() {
1362            nethsm.add_credentials(user.into());
1363        }
1364        // Use the default administrator
1365        nethsm.use_credentials(&admin_credentials.get_default_administrator()?.name)?;
1366
1367        Ok(Self {
1368            nethsm,
1369            admin_credentials,
1370            signstar_config,
1371        })
1372    }
1373
1374    /// Creates a new [`State`] for the [`NetHsm`] backend.
1375    ///
1376    /// # Note
1377    ///
1378    /// This function uses the `nethsm` with the [default
1379    /// _R-Administrator_][`AdminCredentials::get_default_administrator`], but may switch to a
1380    /// namespace-specific _N-Administrator_ for individual operations.
1381    /// If this function succeeds, the `nethsm` is guaranteed to use the [default
1382    /// _R-Administrator_][`AdminCredentials::get_default_administrator`] again.
1383    /// If this function fails, the `nethsm` may still use a namespace-specific _N-Administrator_.
1384    ///
1385    /// # Errors
1386    ///
1387    /// Returns an error if
1388    ///
1389    /// - retrieving the system state of the tracked [`NetHsm`] fails,
1390    /// - unlocking a locked [`NetHsm`] backend fails,
1391    /// - or retrieving the state of users or keys on the tracked [`NetHsm`] backend fails.
1392    pub fn state(&self) -> Result<State, crate::Error> {
1393        debug!(
1394            "Retrieve state of the NetHSM backend at {}",
1395            self.nethsm.get_url()
1396        );
1397
1398        let (users, keys) = match self.nethsm.state()? {
1399            SystemState::Unprovisioned => {
1400                debug!(
1401                    "Unprovisioned NetHSM backend detected at {}.\nSync should be run!",
1402                    self.nethsm.get_url()
1403                );
1404
1405                (Vec::new(), Vec::new())
1406            }
1407            SystemState::Locked => {
1408                debug!(
1409                    "Locked NetHSM backend detected at {}",
1410                    self.nethsm.get_url()
1411                );
1412
1413                self.nethsm.unlock(Passphrase::new(
1414                    self.admin_credentials.get_unlock_passphrase().into(),
1415                ))?;
1416
1417                let users = get_user_states(&self.nethsm, self.admin_credentials)?;
1418                let keys = get_key_states(&self.nethsm, self.admin_credentials)?;
1419                (users, keys)
1420            }
1421            SystemState::Operational => {
1422                debug!(
1423                    "Operational NetHSM backend detected at {}",
1424                    self.nethsm.get_url()
1425                );
1426
1427                let users = get_user_states(&self.nethsm, self.admin_credentials)?;
1428                let keys = get_key_states(&self.nethsm, self.admin_credentials)?;
1429                (users, keys)
1430            }
1431        };
1432
1433        Ok(State {
1434            state_type: StateType::NetHsm,
1435            users,
1436            keys,
1437        })
1438    }
1439
1440    /// Syncs the state of a Signstar configuration with the backend using credentials for users in
1441    /// non-administrative roles.
1442    ///
1443    /// Provisions unprovisioned NetHSM backends and unlocks locked ones.
1444    /// Then works down the following list to
1445    ///
1446    /// - create _R-Administrators_,
1447    ///     - or set their passphrase if they exist already,
1448    /// - create system-wide keys and add tags to them,
1449    ///     - or remove all tags from existing keys and only add the configured tags,
1450    /// - create users in the system-wide, non-administrative roles (i.e.
1451    ///   [`Backup`][`UserRole::Backup`], [`Metrics`][`UserRole::Metrics`] and
1452    ///   [`Operator`][`UserRole::Operator`]),
1453    ///     - or set their passphrase if they exist already,
1454    /// - create OpenPGP certificates for system-wide keys,
1455    ///     - or do nothing if they exist already,
1456    /// - create _N-Administrators_ and their respective namespaces,
1457    ///     - or set their passphrase if they exist already,
1458    /// - create namespaced keys and add tags to them,
1459    ///     - or remove all tags from existing keys and only add the configured tags,
1460    /// - create users in the namespaced, non-administrative roles (i.e.
1461    ///   [`Operator`][`UserRole::Operator`]),
1462    ///     - or set their passphrase if they exist already,
1463    /// - and create OpenPGP certificates for namespaced keys,
1464    ///     - or do nothing if they exist already.
1465    ///
1466    /// # Note
1467    ///
1468    /// This function uses the `nethsm` with the [default
1469    /// _R-Administrator_][`AdminCredentials::get_default_administrator`], but may switch to a
1470    /// namespace-specific _N-Administrator_ or non-administrative user for individual operations.
1471    /// If this function succeeds, the `nethsm` is guaranteed to use the [default
1472    /// _R-Administrator_][`AdminCredentials::get_default_administrator`] again.
1473    /// If this function fails, the `nethsm` may still use a namespace-specific _N-Administrator_ or
1474    /// non-administrative user.
1475    ///
1476    /// # Errors
1477    ///
1478    /// Returns an error if
1479    ///
1480    /// - retrieving the state of the [`NetHsm`] backend fails,
1481    /// - provisioning an unprovisioned [`NetHsm`] fails,
1482    /// - unlocking a locked [`NetHsm`] backend fails,
1483    /// - adding users in the system-wide [`Administrator`][`UserRole::Administrator`] role fails,
1484    /// - adding system-wide keys fails,
1485    /// - adding system-wide users in the [`Backup`][`UserRole::Backup`],
1486    ///   [`Metrics`][`UserRole::Metrics`] or [`Operator`][`UserRole::Operator`] role fails,
1487    /// - adding OpenPGP certificates for system-wide keys fails,
1488    /// - adding namespaced users in the [`Administrator`][`UserRole::Administrator`] role or adding
1489    ///   their respective namespace fails,
1490    /// - adding namespaced keys fails,
1491    /// - adding namespaced users in the [`Operator`][`UserRole::Operator`] role fails,
1492    /// - or adding OpenPGP certificates for namespaced keys fails.
1493    pub fn sync(&self, user_credentials: &[FullCredentials]) -> Result<(), crate::Error> {
1494        debug!(
1495            "Synchronize state of users and keys for the NetHSM backend at {} with the Signstar config.",
1496            self.nethsm.get_url()
1497        );
1498
1499        // Extract the user mappings.
1500        let users = self
1501            .signstar_config
1502            .iter_user_mappings()
1503            .cloned()
1504            .collect::<Vec<UserMapping>>();
1505
1506        match self.nethsm.state()? {
1507            SystemState::Unprovisioned => {
1508                debug!(
1509                    "Unprovisioned NetHSM backend detected at {}",
1510                    self.nethsm.get_url()
1511                );
1512
1513                self.nethsm.provision(
1514                    Passphrase::from_str(self.admin_credentials.get_unlock_passphrase())
1515                        .map_err(Error::NetHsmUser)?,
1516                    self.admin_credentials
1517                        .get_default_administrator()?
1518                        .passphrase
1519                        .clone(),
1520                    nethsm::Utc::now(),
1521                )?;
1522            }
1523            SystemState::Locked => {
1524                debug!(
1525                    "Locked NetHSM backend detected at {}",
1526                    self.nethsm.get_url()
1527                );
1528
1529                self.nethsm.unlock(Passphrase::new(
1530                    self.admin_credentials.get_unlock_passphrase().into(),
1531                ))?;
1532            }
1533            SystemState::Operational => {
1534                debug!(
1535                    "Operational NetHSM backend detected at {}",
1536                    self.nethsm.get_url()
1537                );
1538            }
1539        }
1540
1541        // Add any missing users and keys.
1542        add_system_wide_admins(&self.nethsm, self.admin_credentials)?;
1543        add_system_wide_keys(&self.nethsm, self.admin_credentials, &users)?;
1544        add_non_administrative_users(
1545            &self.nethsm,
1546            self.admin_credentials,
1547            &users,
1548            user_credentials,
1549        )?;
1550        add_system_wide_openpgp_certificates(&self.nethsm, self.admin_credentials, &users)?;
1551        add_namespace_admins(&self.nethsm, self.admin_credentials)?;
1552        add_namespaced_keys(&self.nethsm, self.admin_credentials, &users)?;
1553        add_namespaced_non_administrative_users(
1554            &self.nethsm,
1555            self.admin_credentials,
1556            &users,
1557            user_credentials,
1558        )?;
1559        add_namespaced_openpgp_certificates(&self.nethsm, self.admin_credentials, &users)?;
1560
1561        Ok(())
1562    }
1563}
1564
1565#[cfg(test)]
1566mod tests {
1567    use std::collections::HashSet;
1568
1569    use log::{LevelFilter, debug};
1570    use nethsm::{Connection, ConnectionSecurity, FullCredentials, NetHsm};
1571    use nethsm_config::{
1572        AdministrativeSecretHandling,
1573        AuthorizedKeyEntryList,
1574        ConfigInteractivity,
1575        ConfigSettings,
1576        HermeticParallelConfig,
1577        NonAdministrativeSecretHandling,
1578        UserMapping,
1579    };
1580    use simplelog::{ColorChoice, Config, TermLogger, TerminalMode};
1581    use testresult::TestResult;
1582
1583    use super::*;
1584
1585    /// Initializes a global [`TermLogger`].
1586    fn init_logger() {
1587        if TermLogger::init(
1588            LevelFilter::Trace,
1589            Config::default(),
1590            TerminalMode::Stderr,
1591            ColorChoice::Auto,
1592        )
1593        .is_err()
1594        {
1595            debug!("Not initializing another logger, as one is initialized already.");
1596        }
1597    }
1598
1599    /// Ensures that the [`NetHsmBackend::new`] fails on mismatching iterations in
1600    /// [`AdminCredentials`] and [`HermeticParallelConfig`].
1601    #[test]
1602    fn nethsm_backend_new_fails_on_iteration_mismatch() -> TestResult {
1603        init_logger();
1604
1605        let nethsm = NetHsm::new(
1606            Connection::new(
1607                "https://example.org/api/v1".try_into()?,
1608                ConnectionSecurity::Unsafe,
1609            ),
1610            None,
1611            None,
1612            None,
1613        )?;
1614        // The administrative credentials.
1615        let admin_credentials = AdminCredentials::new(
1616            // this is different from the one in the Signstar config.
1617            2,
1618            "backup-passphrase".parse()?,
1619            "unlock-passphrase".parse()?,
1620            vec![FullCredentials::new(
1621                "admin".parse()?,
1622                "admin-passphrase".parse()?,
1623            )],
1624            vec![FullCredentials::new(
1625                "ns1~admin".parse()?,
1626                "ns1-admin-passphrase".parse()?,
1627            )],
1628        )?;
1629        // The Signstar config.
1630        let signstar_config = HermeticParallelConfig::new(
1631         ConfigSettings::new(
1632             "my_app".to_string(),
1633             ConfigInteractivity::NonInteractive,
1634             None,
1635         ),
1636         1,
1637         AdministrativeSecretHandling::ShamirsSecretSharing,
1638         NonAdministrativeSecretHandling::SystemdCreds,
1639         HashSet::from([Connection::new(
1640             "https://localhost:8443/api/v1/".parse()?,
1641             "Unsafe".parse()?,
1642         )]),
1643         HashSet::from([
1644             UserMapping::NetHsmOnlyAdmin("admin".parse()?),
1645             UserMapping::SystemOnlyShareDownload {
1646                 system_user: "ssh-share-down".parse()?,
1647                 ssh_authorized_keys: AuthorizedKeyEntryList::new(vec!["ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?])?,
1648             },
1649             UserMapping::SystemOnlyShareUpload {
1650                 system_user: "ssh-share-up".parse()?,
1651                 ssh_authorized_keys: AuthorizedKeyEntryList::new(vec!["ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?])?,
1652             }]),
1653     )?;
1654        let nethsm_backend_result =
1655            NetHsmBackend::new(nethsm, &admin_credentials, &signstar_config);
1656
1657        assert!(
1658            nethsm_backend_result.is_err(),
1659            "Test should have failed, but succeeded"
1660        );
1661        assert!(
1662            matches!(
1663                nethsm_backend_result,
1664                Err(crate::Error::NetHsmBackend(Error::IterationMismatch {
1665                    admin_creds: _,
1666                    signstar_config: _
1667                }))
1668            ),
1669            "Expected an `Error::IterationMismatch` but got {nethsm_backend_result:?}"
1670        );
1671
1672        Ok(())
1673    }
1674}