signstar_config/nethsm/
backend.rs

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