Skip to main content

signstar_config/nethsm/
backend.rs

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