1#![cfg(feature = "yubihsm2")]
3
4use std::collections::{BTreeSet, HashSet};
5
6use garde::Validate;
7use serde::{Deserialize, Serialize};
8use signstar_crypto::{key::SigningKeySetup, passphrase::Passphrase, traits::UserWithPassphrase};
9use signstar_yubihsm2::{
10 Credentials,
11 object::{Domain, Id},
12};
13
14use crate::{
15 AuthorizedKeyEntry,
16 SystemUserId,
17 config::{
18 BackendDomainFilter,
19 BackendKeyIdFilter,
20 BackendUserIdFilter,
21 BackendUserIdKind,
22 ConfigAuthorizedKeyEntries,
23 ConfigSystemUserIds,
24 MappingAuthorizedKeyEntry,
25 MappingBackendDomain,
26 MappingBackendKeyId,
27 MappingBackendUserIds,
28 MappingBackendUserSecrets,
29 MappingSystemUserId,
30 duplicate_authorized_keys,
31 duplicate_backend_user_ids,
32 duplicate_domains,
33 duplicate_key_ids,
34 duplicate_system_user_ids,
35 },
36 yubihsm2::backend::YubiHsmConnection,
37};
38
39#[derive(Debug, thiserror::Error)]
41pub enum Error {
42 #[error("Expected the YubiHSM2 authentication key ID {expected}, but found {actual} instead")]
44 AuthenticationKeyIdMismatch {
45 expected: String,
47
48 actual: String,
50 },
51
52 #[error("Error while constructing a YubiHSM2 key domain from {key_domain}, because {reason}")]
54 InvalidDomain {
55 reason: String,
60
61 key_domain: String,
63 },
64}
65
66#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
68#[serde(rename_all = "snake_case")]
69pub enum YubiHsm2UserMapping {
70 Admin {
120 authentication_key_id: Id,
122 },
123
124 AuditLog {
143 authentication_key_id: Id,
145
146 ssh_authorized_key: AuthorizedKeyEntry,
148
149 system_user: SystemUserId,
151 },
152
153 Backup {
178 authentication_key_id: Id,
184
185 wrapping_key_id: Id,
196
197 ssh_authorized_key: AuthorizedKeyEntry,
199
200 system_user: SystemUserId,
202 },
203
204 HermeticAuditLog {
223 authentication_key_id: Id,
225
226 system_user: SystemUserId,
228 },
229
230 Signing {
261 authentication_key_id: Id,
263
264 key_setup: SigningKeySetup,
266
267 domain: Domain,
271
272 signing_key_id: Id,
274
275 ssh_authorized_key: AuthorizedKeyEntry,
277
278 system_user: SystemUserId,
280 },
281}
282
283impl YubiHsm2UserMapping {
284 pub fn domain(&self) -> Option<&Domain> {
286 match self {
287 Self::Admin { .. }
288 | Self::Backup { .. }
289 | Self::AuditLog { .. }
290 | Self::HermeticAuditLog { .. } => None,
291 Self::Signing {
292 domain: key_domain, ..
293 } => Some(key_domain),
294 }
295 }
296
297 pub fn backend_user_id(&self) -> Id {
299 match self {
300 Self::Admin {
301 authentication_key_id,
302 }
303 | Self::AuditLog {
304 authentication_key_id,
305 ..
306 }
307 | Self::Backup {
308 authentication_key_id,
309 ..
310 }
311 | Self::HermeticAuditLog {
312 authentication_key_id,
313 ..
314 }
315 | Self::Signing {
316 authentication_key_id,
317 ..
318 } => *authentication_key_id,
319 }
320 }
321}
322
323impl MappingSystemUserId for YubiHsm2UserMapping {
324 fn system_user_id(&self) -> Option<&SystemUserId> {
325 match self {
326 Self::Admin { .. } => None,
327 Self::AuditLog { system_user, .. }
328 | Self::Backup { system_user, .. }
329 | Self::HermeticAuditLog { system_user, .. }
330 | Self::Signing { system_user, .. } => Some(system_user),
331 }
332 }
333}
334
335impl MappingBackendUserIds for YubiHsm2UserMapping {
336 fn backend_user_ids(&self, filter: BackendUserIdFilter) -> Vec<String> {
337 match self {
338 Self::Admin {
339 authentication_key_id,
340 } => {
341 if [BackendUserIdKind::Admin, BackendUserIdKind::Any]
342 .contains(&filter.backend_user_id_kind)
343 {
344 Some(vec![authentication_key_id.to_string()])
345 } else {
346 None
347 }
348 }
349 Self::AuditLog {
350 authentication_key_id,
351 ..
352 } => {
353 if [
354 BackendUserIdKind::Any,
355 BackendUserIdKind::Metrics,
356 BackendUserIdKind::NonAdmin,
357 ]
358 .contains(&filter.backend_user_id_kind)
359 {
360 Some(vec![authentication_key_id.to_string()])
361 } else {
362 None
363 }
364 }
365 Self::Backup {
366 authentication_key_id,
367 ..
368 } => {
369 if [
370 BackendUserIdKind::Any,
371 BackendUserIdKind::Backup,
372 BackendUserIdKind::NonAdmin,
373 ]
374 .contains(&filter.backend_user_id_kind)
375 {
376 Some(vec![authentication_key_id.to_string()])
377 } else {
378 None
379 }
380 }
381 Self::HermeticAuditLog {
382 authentication_key_id,
383 ..
384 } => {
385 if [
386 BackendUserIdKind::Any,
387 BackendUserIdKind::Metrics,
388 BackendUserIdKind::NonAdmin,
389 ]
390 .contains(&filter.backend_user_id_kind)
391 {
392 Some(vec![authentication_key_id.to_string()])
393 } else {
394 None
395 }
396 }
397 Self::Signing {
398 authentication_key_id,
399 ..
400 } => {
401 if [
402 BackendUserIdKind::Any,
403 BackendUserIdKind::NonAdmin,
404 BackendUserIdKind::Signing,
405 ]
406 .contains(&filter.backend_user_id_kind)
407 {
408 Some(vec![authentication_key_id.to_string()])
409 } else {
410 None
411 }
412 }
413 }
414 .unwrap_or_default()
415 }
416
417 fn backend_user_with_passphrase(
418 &self,
419 name: &str,
420 passphrase: Passphrase,
421 ) -> Result<Box<dyn UserWithPassphrase>, crate::Error> {
422 let backend_user_id = self.backend_user_id();
423 if backend_user_id.to_string() != name {
424 return Err(Error::AuthenticationKeyIdMismatch {
425 expected: name.to_string(),
426 actual: backend_user_id.to_string(),
427 }
428 .into());
429 }
430
431 Ok(Box::new(Credentials::new(backend_user_id, passphrase)))
432 }
433
434 fn backend_users_with_new_passphrase(
435 &self,
436 filter: BackendUserIdFilter,
437 ) -> Vec<Box<dyn UserWithPassphrase>> {
438 if let Some(authentication_key_id) = match self {
439 Self::Admin {
440 authentication_key_id,
441 } => {
442 if [BackendUserIdKind::Admin, BackendUserIdKind::Any]
443 .contains(&filter.backend_user_id_kind)
444 {
445 Some(authentication_key_id)
446 } else {
447 None
448 }
449 }
450 Self::AuditLog {
451 authentication_key_id,
452 ..
453 } => {
454 if [
455 BackendUserIdKind::Any,
456 BackendUserIdKind::Metrics,
457 BackendUserIdKind::NonAdmin,
458 ]
459 .contains(&filter.backend_user_id_kind)
460 {
461 Some(authentication_key_id)
462 } else {
463 None
464 }
465 }
466 Self::Backup {
467 authentication_key_id,
468 ..
469 } => {
470 if [
471 BackendUserIdKind::Any,
472 BackendUserIdKind::Backup,
473 BackendUserIdKind::NonAdmin,
474 ]
475 .contains(&filter.backend_user_id_kind)
476 {
477 Some(authentication_key_id)
478 } else {
479 None
480 }
481 }
482 Self::HermeticAuditLog {
483 authentication_key_id,
484 ..
485 } => {
486 if [
487 BackendUserIdKind::Any,
488 BackendUserIdKind::Metrics,
489 BackendUserIdKind::NonAdmin,
490 ]
491 .contains(&filter.backend_user_id_kind)
492 {
493 Some(authentication_key_id)
494 } else {
495 None
496 }
497 }
498 Self::Signing {
499 authentication_key_id,
500 ..
501 } => {
502 if [
503 BackendUserIdKind::Any,
504 BackendUserIdKind::NonAdmin,
505 BackendUserIdKind::Signing,
506 ]
507 .contains(&filter.backend_user_id_kind)
508 {
509 Some(authentication_key_id)
510 } else {
511 None
512 }
513 }
514 } {
515 vec![Box::new(Credentials::new(
516 *authentication_key_id,
517 Passphrase::generate(None),
518 ))]
519 } else {
520 Vec::new()
521 }
522 }
523}
524
525impl MappingAuthorizedKeyEntry for YubiHsm2UserMapping {
526 fn authorized_key_entry(&self) -> Option<&AuthorizedKeyEntry> {
527 match self {
528 Self::Admin { .. } | Self::HermeticAuditLog { .. } => None,
529 Self::AuditLog {
530 ssh_authorized_key, ..
531 }
532 | Self::Backup {
533 ssh_authorized_key, ..
534 }
535 | Self::Signing {
536 ssh_authorized_key, ..
537 } => Some(ssh_authorized_key),
538 }
539 }
540}
541
542#[derive(Clone, Copy, Debug)]
544pub struct YubiHsm2DomainFilter {}
545
546impl BackendDomainFilter for YubiHsm2DomainFilter {}
547
548impl MappingBackendDomain<YubiHsm2DomainFilter> for YubiHsm2UserMapping {
549 fn backend_domain(&self, _filter: Option<&YubiHsm2DomainFilter>) -> Option<String> {
550 self.domain().map(|domain| domain.to_string())
551 }
552}
553
554#[derive(Clone, Copy, Debug, Eq, PartialEq)]
562pub enum KeyObjectType {
563 Signing,
567
568 Wrapping,
572}
573
574#[derive(Clone, Debug)]
576pub struct YubiHsm2BackendKeyIdFilter {
577 pub key_type: KeyObjectType,
581
582 pub key_domain: Option<Domain>,
586}
587
588impl BackendKeyIdFilter for YubiHsm2BackendKeyIdFilter {}
589
590impl MappingBackendKeyId<YubiHsm2BackendKeyIdFilter> for YubiHsm2UserMapping {
591 fn backend_key_id(&self, filter: &YubiHsm2BackendKeyIdFilter) -> Option<String> {
592 match self {
593 Self::Admin { .. } | Self::AuditLog { .. } | Self::HermeticAuditLog { .. } => None,
594 Self::Backup {
595 wrapping_key_id, ..
596 } => {
597 if filter.key_type == KeyObjectType::Wrapping {
598 Some(wrapping_key_id.to_string())
600 } else {
601 None
602 }
603 }
604 Self::Signing {
605 signing_key_id,
606 domain: key_domain,
607 ..
608 } => {
609 if filter.key_type == KeyObjectType::Signing {
610 if let Some(filter_key_domain) = filter.key_domain {
611 if &filter_key_domain == key_domain {
612 Some(signing_key_id.to_string())
613 } else {
614 None
615 }
616 } else {
617 Some(signing_key_id.to_string())
618 }
619 } else {
620 None
621 }
622 }
623 }
624 }
625}
626
627impl MappingBackendUserSecrets for YubiHsm2UserMapping {}
628
629fn validate_yubihsm2_config_connections(
637 value: &BTreeSet<YubiHsmConnection>,
638 _context: &(),
639) -> garde::Result {
640 if value.is_empty() {
641 return Err(garde::Error::new("contains no connections".to_string()));
642 }
643
644 Ok(())
645}
646
647fn validate_yubihsm2_config_mappings(
674 value: &BTreeSet<YubiHsm2UserMapping>,
675 _context: &(),
676) -> garde::Result {
677 if value.is_empty() {
678 return Err(garde::Error::new("contains no user mappings".to_string()));
679 }
680
681 let duplicate_system_user_ids = duplicate_system_user_ids(value);
683
684 let duplicate_authorized_keys = duplicate_authorized_keys(value);
686
687 let missing_admin = {
689 let num_system_admins = value
690 .iter()
691 .filter_map(|mapping| {
692 if let YubiHsm2UserMapping::Admin {
693 authentication_key_id,
694 } = mapping
695 {
696 Some(authentication_key_id)
697 } else {
698 None
699 }
700 })
701 .count();
702
703 if num_system_admins == 0 {
704 Some("no administrator user".to_string())
705 } else {
706 None
707 }
708 };
709
710 let duplicate_backend_user_ids = duplicate_backend_user_ids(value);
712
713 let duplicate_signing_key_ids = duplicate_key_ids(
715 value,
716 &YubiHsm2BackendKeyIdFilter {
717 key_type: KeyObjectType::Signing,
718 key_domain: None,
719 },
720 Some(" signing".to_string()),
721 );
722
723 let duplicate_wrapping_key_ids = duplicate_key_ids(
725 value,
726 &YubiHsm2BackendKeyIdFilter {
727 key_type: KeyObjectType::Wrapping,
728 key_domain: None,
729 },
730 Some(" wrapping".to_string()),
731 );
732
733 let duplicate_domains = duplicate_domains(value, None, None, None);
735
736 let messages = [
737 duplicate_system_user_ids,
738 duplicate_authorized_keys,
739 missing_admin,
740 duplicate_backend_user_ids,
741 duplicate_signing_key_ids,
742 duplicate_wrapping_key_ids,
743 duplicate_domains,
744 ];
745 let error_messages = {
746 let mut error_messages = Vec::new();
747
748 for message in messages.iter().flatten() {
749 error_messages.push(message.as_str());
750 }
751
752 error_messages
753 };
754
755 match error_messages.len() {
756 0 => Ok(()),
757 1 => Err(garde::Error::new(format!(
758 "contains {}",
759 error_messages.join("\n")
760 ))),
761 _ => Err(garde::Error::new(format!(
762 "contains multiple issues:\n⤷ {}",
763 error_messages.join("\n⤷ ")
764 ))),
765 }
766}
767
768#[derive(Clone, Debug, Default, Deserialize, Serialize, Validate)]
773#[serde(rename_all = "snake_case")]
774pub struct YubiHsm2Config {
775 #[garde(custom(validate_yubihsm2_config_connections))]
777 connections: BTreeSet<YubiHsmConnection>,
778
779 #[garde(custom(validate_yubihsm2_config_mappings))]
781 mappings: BTreeSet<YubiHsm2UserMapping>,
782}
783
784impl YubiHsm2Config {
785 pub fn new(
788 connections: BTreeSet<YubiHsmConnection>,
789 mappings: BTreeSet<YubiHsm2UserMapping>,
790 ) -> Result<Self, crate::Error> {
791 let config = Self {
792 connections,
793 mappings,
794 };
795 config
796 .validate()
797 .map_err(|source| crate::Error::Validation {
798 context: "validating a YubiHSM2 specific configuration item".to_string(),
799 source,
800 })?;
801
802 Ok(config)
803 }
804
805 pub fn connections(&self) -> &BTreeSet<YubiHsmConnection> {
807 &self.connections
808 }
809
810 pub fn mappings(&self) -> &BTreeSet<YubiHsm2UserMapping> {
812 &self.mappings
813 }
814}
815
816impl ConfigAuthorizedKeyEntries for YubiHsm2Config {
817 fn authorized_key_entries(&self) -> HashSet<&AuthorizedKeyEntry> {
818 self.mappings
819 .iter()
820 .filter_map(|mapping| mapping.authorized_key_entry())
821 .collect()
822 }
823}
824
825impl ConfigSystemUserIds for YubiHsm2Config {
826 fn system_user_ids(&self) -> HashSet<&SystemUserId> {
827 self.mappings
828 .iter()
829 .filter_map(|mapping| mapping.system_user_id())
830 .collect()
831 }
832}
833
834#[cfg(test)]
835mod tests {
836 use std::thread::current;
837
838 use insta::{assert_snapshot, with_settings};
839 use rstest::{fixture, rstest};
840 use signstar_crypto::{
841 key::{CryptographicKeyContext, KeyMechanism, KeyType, SignatureType, SigningKeySetup},
842 openpgp::OpenPgpUserIdList,
843 };
844 use testresult::TestResult;
845
846 use super::*;
847
848 const SNAPSHOT_PATH: &str = "fixtures/yubihsm2_config/";
849
850 #[rstest]
851 #[case::admin(YubiHsm2UserMapping::Admin{ authentication_key_id: "1".parse()? })]
852 #[case::audit_log(
853 YubiHsm2UserMapping::AuditLog {
854 authentication_key_id: "1".parse()?,
855 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host".parse()?,
856 system_user: "metrics-user".parse()?,
857 },
858 )]
859 #[case::backup(
860 YubiHsm2UserMapping::Backup{
861 authentication_key_id: "1".parse()?,
862 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
863 system_user: "backup-user".parse()?,
864 wrapping_key_id: "1".parse()?,
865 },
866 )]
867 #[case::hermetic_audit_log(
868 YubiHsm2UserMapping::HermeticAuditLog {
869 authentication_key_id: "1".parse()?,
870 system_user: "metrics-user".parse()?,
871 },
872 )]
873 #[case::signing(
874 YubiHsm2UserMapping::Signing {
875 authentication_key_id: "1".parse()?,
876 signing_key_id: "1".parse()?,
877 key_setup: SigningKeySetup::new(
878 KeyType::Curve25519,
879 vec![KeyMechanism::EdDsaSignature],
880 None,
881 SignatureType::EdDsa,
882 CryptographicKeyContext::OpenPgp {
883 user_ids: OpenPgpUserIdList::new(vec![
884 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
885 ])?,
886 version: "v4".parse()?,
887 },
888 )?,
889 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
890 system_user: "signing-user".parse()?,
891 domain: Domain::One,
892 }
893 )]
894 fn yubihsm2_user_mapping_backend_user_id(#[case] mapping: YubiHsm2UserMapping) -> TestResult {
895 let id: Id = "1".parse()?;
896 assert_eq!(mapping.backend_user_id(), id);
897
898 Ok(())
899 }
900
901 #[rstest]
902 #[case::admin_filter_admin(
903 YubiHsm2UserMapping::Admin{ authentication_key_id: "1".parse()? },
904 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Admin },
905 )]
906 #[case::admin_filter_any(
907 YubiHsm2UserMapping::Admin{ authentication_key_id: "1".parse()? },
908 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Any },
909 )]
910 #[case::audit_log_filter_metrics(
911 YubiHsm2UserMapping::AuditLog {
912 authentication_key_id: "1".parse()?,
913 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host".parse()?,
914 system_user: "metrics-user".parse()?,
915 },
916 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Metrics },
917 )]
918 #[case::audit_log_filter_any(
919 YubiHsm2UserMapping::AuditLog {
920 authentication_key_id: "1".parse()?,
921 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host".parse()?,
922 system_user: "metrics-user".parse()?,
923 },
924 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Any },
925 )]
926 #[case::audit_log_filter_non_admin(
927 YubiHsm2UserMapping::AuditLog {
928 authentication_key_id: "1".parse()?,
929 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host".parse()?,
930 system_user: "metrics-user".parse()?,
931 },
932 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::NonAdmin },
933 )]
934 #[case::backup_filter_backup(
935 YubiHsm2UserMapping::Backup{
936 authentication_key_id: "1".parse()?,
937 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
938 system_user: "backup-user".parse()?,
939 wrapping_key_id: "1".parse()?,
940 },
941 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Backup },
942 )]
943 #[case::backup_filter_any(
944 YubiHsm2UserMapping::Backup{
945 authentication_key_id: "1".parse()?,
946 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
947 system_user: "backup-user".parse()?,
948 wrapping_key_id: "1".parse()?,
949 },
950 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Any },
951 )]
952 #[case::backup_filter_non_admin(
953 YubiHsm2UserMapping::Backup{
954 authentication_key_id: "1".parse()?,
955 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
956 system_user: "backup-user".parse()?,
957 wrapping_key_id: "1".parse()?,
958 },
959 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::NonAdmin },
960 )]
961 #[case::hermetic_audit_log_filter_metrics(
962 YubiHsm2UserMapping::HermeticAuditLog {
963 authentication_key_id: "1".parse()?,
964 system_user: "metrics-user".parse()?,
965 },
966 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Metrics },
967 )]
968 #[case::hermetic_audit_log_filter_any(
969 YubiHsm2UserMapping::HermeticAuditLog {
970 authentication_key_id: "1".parse()?,
971 system_user: "metrics-user".parse()?,
972 },
973 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Any },
974 )]
975 #[case::hermetic_audit_log_filter_non_admin(
976 YubiHsm2UserMapping::HermeticAuditLog {
977 authentication_key_id: "1".parse()?,
978 system_user: "metrics-user".parse()?,
979 },
980 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::NonAdmin },
981 )]
982 #[case::signing_filter_signing(
983 YubiHsm2UserMapping::Signing {
984 authentication_key_id: "1".parse()?,
985 signing_key_id: "1".parse()?,
986 key_setup: SigningKeySetup::new(
987 KeyType::Curve25519,
988 vec![KeyMechanism::EdDsaSignature],
989 None,
990 SignatureType::EdDsa,
991 CryptographicKeyContext::OpenPgp {
992 user_ids: OpenPgpUserIdList::new(vec![
993 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
994 ])?,
995 version: "v4".parse()?,
996 },
997 )?,
998 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
999 system_user: "signing-user".parse()?,
1000 domain: Domain::One,
1001 },
1002 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Signing },
1003 )]
1004 #[case::signing_filter_any(
1005 YubiHsm2UserMapping::Signing {
1006 authentication_key_id: "1".parse()?,
1007 signing_key_id: "1".parse()?,
1008 key_setup: SigningKeySetup::new(
1009 KeyType::Curve25519,
1010 vec![KeyMechanism::EdDsaSignature],
1011 None,
1012 SignatureType::EdDsa,
1013 CryptographicKeyContext::OpenPgp {
1014 user_ids: OpenPgpUserIdList::new(vec![
1015 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
1016 ])?,
1017 version: "v4".parse()?,
1018 },
1019 )?,
1020 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
1021 system_user: "signing-user".parse()?,
1022 domain: Domain::One,
1023 },
1024 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Any },
1025 )]
1026 #[case::signing_filter_non_admin(
1027 YubiHsm2UserMapping::Signing {
1028 authentication_key_id: "1".parse()?,
1029 signing_key_id: "1".parse()?,
1030 key_setup: SigningKeySetup::new(
1031 KeyType::Curve25519,
1032 vec![KeyMechanism::EdDsaSignature],
1033 None,
1034 SignatureType::EdDsa,
1035 CryptographicKeyContext::OpenPgp {
1036 user_ids: OpenPgpUserIdList::new(vec![
1037 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
1038 ])?,
1039 version: "v4".parse()?,
1040 },
1041 )?,
1042 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
1043 system_user: "signing-user".parse()?,
1044 domain: Domain::One,
1045 },
1046 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::NonAdmin },
1047 )]
1048 fn yubihsm2_user_mapping_backend_user_ids_filter_matches(
1049 #[case] mapping: YubiHsm2UserMapping,
1050 #[case] filter: BackendUserIdFilter,
1051 ) -> TestResult {
1052 assert_eq!(mapping.backend_user_ids(filter), vec!["1".to_string()]);
1053
1054 Ok(())
1055 }
1056
1057 #[rstest]
1058 #[case::admin_filter_non_admin(
1059 YubiHsm2UserMapping::Admin{ authentication_key_id: "1".parse()? },
1060 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::NonAdmin },
1061 )]
1062 #[case::admin_filter_backup(
1063 YubiHsm2UserMapping::Admin{ authentication_key_id: "1".parse()? },
1064 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Backup },
1065 )]
1066 #[case::admin_filter_metrics(
1067 YubiHsm2UserMapping::Admin{ authentication_key_id: "1".parse()? },
1068 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Metrics },
1069 )]
1070 #[case::admin_filter_observer(
1071 YubiHsm2UserMapping::Admin{ authentication_key_id: "1".parse()? },
1072 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Observer },
1073 )]
1074 #[case::admin_filter_signing(
1075 YubiHsm2UserMapping::Admin{ authentication_key_id: "1".parse()? },
1076 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Signing },
1077 )]
1078 #[case::audit_log_filter_admin(
1079 YubiHsm2UserMapping::AuditLog {
1080 authentication_key_id: "1".parse()?,
1081 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host".parse()?,
1082 system_user: "metrics-user".parse()?,
1083 },
1084 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Admin },
1085 )]
1086 #[case::audit_log_filter_backup(
1087 YubiHsm2UserMapping::AuditLog {
1088 authentication_key_id: "1".parse()?,
1089 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host".parse()?,
1090 system_user: "metrics-user".parse()?,
1091 },
1092 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Backup },
1093 )]
1094 #[case::audit_log_filter_observer(
1095 YubiHsm2UserMapping::AuditLog {
1096 authentication_key_id: "1".parse()?,
1097 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host".parse()?,
1098 system_user: "metrics-user".parse()?,
1099 },
1100 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Observer },
1101 )]
1102 #[case::audit_log_filter_signing(
1103 YubiHsm2UserMapping::AuditLog {
1104 authentication_key_id: "1".parse()?,
1105 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host".parse()?,
1106 system_user: "metrics-user".parse()?,
1107 },
1108 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Signing },
1109 )]
1110 #[case::backup_filter_admin(
1111 YubiHsm2UserMapping::Backup{
1112 authentication_key_id: "1".parse()?,
1113 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
1114 system_user: "backup-user".parse()?,
1115 wrapping_key_id: "1".parse()?,
1116 },
1117 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Admin },
1118 )]
1119 #[case::backup_filter_metrics(
1120 YubiHsm2UserMapping::Backup{
1121 authentication_key_id: "1".parse()?,
1122 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
1123 system_user: "backup-user".parse()?,
1124 wrapping_key_id: "1".parse()?,
1125 },
1126 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Metrics },
1127 )]
1128 #[case::backup_filter_observer(
1129 YubiHsm2UserMapping::Backup{
1130 authentication_key_id: "1".parse()?,
1131 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
1132 system_user: "backup-user".parse()?,
1133 wrapping_key_id: "1".parse()?,
1134 },
1135 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Observer },
1136 )]
1137 #[case::backup_filter_signing(
1138 YubiHsm2UserMapping::Backup{
1139 authentication_key_id: "1".parse()?,
1140 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
1141 system_user: "backup-user".parse()?,
1142 wrapping_key_id: "1".parse()?,
1143 },
1144 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Signing },
1145 )]
1146 #[case::hermetic_audit_log_filter_admin(
1147 YubiHsm2UserMapping::HermeticAuditLog {
1148 authentication_key_id: "1".parse()?,
1149 system_user: "metrics-user".parse()?,
1150 },
1151 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Admin },
1152 )]
1153 #[case::hermetic_audit_log_filter_backup(
1154 YubiHsm2UserMapping::HermeticAuditLog {
1155 authentication_key_id: "1".parse()?,
1156 system_user: "metrics-user".parse()?,
1157 },
1158 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Backup },
1159 )]
1160 #[case::hermetic_audit_log_filter_observer(
1161 YubiHsm2UserMapping::HermeticAuditLog {
1162 authentication_key_id: "1".parse()?,
1163 system_user: "metrics-user".parse()?,
1164 },
1165 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Observer },
1166 )]
1167 #[case::hermetic_audit_log_filter_signing(
1168 YubiHsm2UserMapping::HermeticAuditLog {
1169 authentication_key_id: "1".parse()?,
1170 system_user: "metrics-user".parse()?,
1171 },
1172 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Signing },
1173 )]
1174 #[case::signing_filter_admin(
1175 YubiHsm2UserMapping::Signing {
1176 authentication_key_id: "1".parse()?,
1177 signing_key_id: "1".parse()?,
1178 key_setup: SigningKeySetup::new(
1179 KeyType::Curve25519,
1180 vec![KeyMechanism::EdDsaSignature],
1181 None,
1182 SignatureType::EdDsa,
1183 CryptographicKeyContext::OpenPgp {
1184 user_ids: OpenPgpUserIdList::new(vec![
1185 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
1186 ])?,
1187 version: "v4".parse()?,
1188 },
1189 )?,
1190 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
1191 system_user: "signing-user".parse()?,
1192 domain: Domain::One,
1193 },
1194 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Admin },
1195 )]
1196 #[case::signing_filter_backup(
1197 YubiHsm2UserMapping::Signing {
1198 authentication_key_id: "1".parse()?,
1199 signing_key_id: "1".parse()?,
1200 key_setup: SigningKeySetup::new(
1201 KeyType::Curve25519,
1202 vec![KeyMechanism::EdDsaSignature],
1203 None,
1204 SignatureType::EdDsa,
1205 CryptographicKeyContext::OpenPgp {
1206 user_ids: OpenPgpUserIdList::new(vec![
1207 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
1208 ])?,
1209 version: "v4".parse()?,
1210 },
1211 )?,
1212 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
1213 system_user: "signing-user".parse()?,
1214 domain: Domain::One,
1215 },
1216 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Backup },
1217 )]
1218 #[case::signing_filter_metrics(
1219 YubiHsm2UserMapping::Signing {
1220 authentication_key_id: "1".parse()?,
1221 signing_key_id: "1".parse()?,
1222 key_setup: SigningKeySetup::new(
1223 KeyType::Curve25519,
1224 vec![KeyMechanism::EdDsaSignature],
1225 None,
1226 SignatureType::EdDsa,
1227 CryptographicKeyContext::OpenPgp {
1228 user_ids: OpenPgpUserIdList::new(vec![
1229 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
1230 ])?,
1231 version: "v4".parse()?,
1232 },
1233 )?,
1234 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
1235 system_user: "signing-user".parse()?,
1236 domain: Domain::One,
1237 },
1238 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Metrics },
1239 )]
1240 #[case::signing_filter_observer(
1241 YubiHsm2UserMapping::Signing {
1242 authentication_key_id: "1".parse()?,
1243 signing_key_id: "1".parse()?,
1244 key_setup: SigningKeySetup::new(
1245 KeyType::Curve25519,
1246 vec![KeyMechanism::EdDsaSignature],
1247 None,
1248 SignatureType::EdDsa,
1249 CryptographicKeyContext::OpenPgp {
1250 user_ids: OpenPgpUserIdList::new(vec![
1251 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
1252 ])?,
1253 version: "v4".parse()?,
1254 },
1255 )?,
1256 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
1257 system_user: "signing-user".parse()?,
1258 domain: Domain::One,
1259 },
1260 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Observer },
1261 )]
1262 fn yubihsm2_user_mapping_backend_user_ids_filter_mismatches(
1263 #[case] mapping: YubiHsm2UserMapping,
1264 #[case] filter: BackendUserIdFilter,
1265 ) -> TestResult {
1266 assert!(mapping.backend_user_ids(filter).is_empty());
1267
1268 Ok(())
1269 }
1270
1271 #[test]
1272 fn yubihsm2_user_mapping_backend_user_with_passphrase_succeeds() -> TestResult {
1273 let mapping = YubiHsm2UserMapping::Admin {
1274 authentication_key_id: "1".parse()?,
1275 };
1276 let passphrase = Passphrase::generate(None);
1277 let creds = mapping.backend_user_with_passphrase("1", passphrase.clone())?;
1278
1279 assert_eq!(creds.user(), "1");
1280 assert_eq!(
1281 creds.passphrase().expose_borrowed(),
1282 passphrase.expose_borrowed()
1283 );
1284
1285 Ok(())
1286 }
1287
1288 #[test]
1289 fn yubihsm2_user_mapping_backend_user_with_passphrase_fails() -> TestResult {
1290 let mapping = YubiHsm2UserMapping::Admin {
1291 authentication_key_id: "1".parse()?,
1292 };
1293 assert!(
1294 mapping
1295 .backend_user_with_passphrase("2", Passphrase::generate(None))
1296 .is_err()
1297 );
1298
1299 Ok(())
1300 }
1301
1302 #[rstest]
1303 #[case::admin_filter_admin(
1304 YubiHsm2UserMapping::Admin{ authentication_key_id: "1".parse()? },
1305 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Admin },
1306 )]
1307 #[case::admin_filter_any(
1308 YubiHsm2UserMapping::Admin{ authentication_key_id: "1".parse()? },
1309 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Any },
1310 )]
1311 #[case::audit_log_filter_metrics(
1312 YubiHsm2UserMapping::AuditLog {
1313 authentication_key_id: "1".parse()?,
1314 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host".parse()?,
1315 system_user: "metrics-user".parse()?,
1316 },
1317 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Metrics },
1318 )]
1319 #[case::audit_log_filter_any(
1320 YubiHsm2UserMapping::AuditLog {
1321 authentication_key_id: "1".parse()?,
1322 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host".parse()?,
1323 system_user: "metrics-user".parse()?,
1324 },
1325 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Any },
1326 )]
1327 #[case::audit_log_filter_non_admin(
1328 YubiHsm2UserMapping::AuditLog {
1329 authentication_key_id: "1".parse()?,
1330 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host".parse()?,
1331 system_user: "metrics-user".parse()?,
1332 },
1333 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::NonAdmin },
1334 )]
1335 #[case::backup_filter_backup(
1336 YubiHsm2UserMapping::Backup{
1337 authentication_key_id: "1".parse()?,
1338 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
1339 system_user: "backup-user".parse()?,
1340 wrapping_key_id: "1".parse()?,
1341 },
1342 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Backup },
1343 )]
1344 #[case::backup_filter_any(
1345 YubiHsm2UserMapping::Backup{
1346 authentication_key_id: "1".parse()?,
1347 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
1348 system_user: "backup-user".parse()?,
1349 wrapping_key_id: "1".parse()?,
1350 },
1351 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Any },
1352 )]
1353 #[case::backup_filter_non_admin(
1354 YubiHsm2UserMapping::Backup{
1355 authentication_key_id: "1".parse()?,
1356 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
1357 system_user: "backup-user".parse()?,
1358 wrapping_key_id: "1".parse()?,
1359 },
1360 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::NonAdmin },
1361 )]
1362 #[case::hermetic_audit_log_filter_metrics(
1363 YubiHsm2UserMapping::HermeticAuditLog {
1364 authentication_key_id: "1".parse()?,
1365 system_user: "metrics-user".parse()?,
1366 },
1367 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Metrics },
1368 )]
1369 #[case::hermetic_audit_log_filter_any(
1370 YubiHsm2UserMapping::HermeticAuditLog {
1371 authentication_key_id: "1".parse()?,
1372 system_user: "metrics-user".parse()?,
1373 },
1374 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Any },
1375 )]
1376 #[case::hermetic_audit_log_filter_non_admin(
1377 YubiHsm2UserMapping::HermeticAuditLog {
1378 authentication_key_id: "1".parse()?,
1379 system_user: "metrics-user".parse()?,
1380 },
1381 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::NonAdmin },
1382 )]
1383 #[case::signing_filter_signing(
1384 YubiHsm2UserMapping::Signing {
1385 authentication_key_id: "1".parse()?,
1386 signing_key_id: "1".parse()?,
1387 key_setup: SigningKeySetup::new(
1388 KeyType::Curve25519,
1389 vec![KeyMechanism::EdDsaSignature],
1390 None,
1391 SignatureType::EdDsa,
1392 CryptographicKeyContext::OpenPgp {
1393 user_ids: OpenPgpUserIdList::new(vec![
1394 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
1395 ])?,
1396 version: "v4".parse()?,
1397 },
1398 )?,
1399 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
1400 system_user: "signing-user".parse()?,
1401 domain: Domain::One,
1402 },
1403 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Signing },
1404 )]
1405 #[case::signing_filter_any(
1406 YubiHsm2UserMapping::Signing {
1407 authentication_key_id: "1".parse()?,
1408 signing_key_id: "1".parse()?,
1409 key_setup: SigningKeySetup::new(
1410 KeyType::Curve25519,
1411 vec![KeyMechanism::EdDsaSignature],
1412 None,
1413 SignatureType::EdDsa,
1414 CryptographicKeyContext::OpenPgp {
1415 user_ids: OpenPgpUserIdList::new(vec![
1416 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
1417 ])?,
1418 version: "v4".parse()?,
1419 },
1420 )?,
1421 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
1422 system_user: "signing-user".parse()?,
1423 domain: Domain::One,
1424 },
1425 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Any },
1426 )]
1427 #[case::signing_filter_non_admin(
1428 YubiHsm2UserMapping::Signing {
1429 authentication_key_id: "1".parse()?,
1430 signing_key_id: "1".parse()?,
1431 key_setup: SigningKeySetup::new(
1432 KeyType::Curve25519,
1433 vec![KeyMechanism::EdDsaSignature],
1434 None,
1435 SignatureType::EdDsa,
1436 CryptographicKeyContext::OpenPgp {
1437 user_ids: OpenPgpUserIdList::new(vec![
1438 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
1439 ])?,
1440 version: "v4".parse()?,
1441 },
1442 )?,
1443 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
1444 system_user: "signing-user".parse()?,
1445 domain: Domain::One,
1446 },
1447 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::NonAdmin },
1448 )]
1449 fn yubihsm2_user_mapping_backend_users_with_new_passphrase_filter_matches(
1450 #[case] mapping: YubiHsm2UserMapping,
1451 #[case] filter: BackendUserIdFilter,
1452 ) -> TestResult {
1453 let creds = mapping.backend_users_with_new_passphrase(filter);
1454 assert!(creds.first().is_some_and(|creds| creds.user() == "1"));
1455
1456 Ok(())
1457 }
1458
1459 #[rstest]
1460 #[case::admin_filter_non_admin(
1461 YubiHsm2UserMapping::Admin{ authentication_key_id: "1".parse()? },
1462 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::NonAdmin },
1463 )]
1464 #[case::admin_filter_backup(
1465 YubiHsm2UserMapping::Admin{ authentication_key_id: "1".parse()? },
1466 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Backup },
1467 )]
1468 #[case::admin_filter_metrics(
1469 YubiHsm2UserMapping::Admin{ authentication_key_id: "1".parse()? },
1470 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Metrics },
1471 )]
1472 #[case::admin_filter_observer(
1473 YubiHsm2UserMapping::Admin{ authentication_key_id: "1".parse()? },
1474 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Observer },
1475 )]
1476 #[case::admin_filter_signing(
1477 YubiHsm2UserMapping::Admin{ authentication_key_id: "1".parse()? },
1478 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Signing },
1479 )]
1480 #[case::audit_log_filter_admin(
1481 YubiHsm2UserMapping::AuditLog {
1482 authentication_key_id: "1".parse()?,
1483 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host".parse()?,
1484 system_user: "metrics-user".parse()?,
1485 },
1486 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Admin },
1487 )]
1488 #[case::audit_log_filter_backup(
1489 YubiHsm2UserMapping::AuditLog {
1490 authentication_key_id: "1".parse()?,
1491 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host".parse()?,
1492 system_user: "metrics-user".parse()?,
1493 },
1494 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Backup },
1495 )]
1496 #[case::audit_log_filter_observer(
1497 YubiHsm2UserMapping::AuditLog {
1498 authentication_key_id: "1".parse()?,
1499 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host".parse()?,
1500 system_user: "metrics-user".parse()?,
1501 },
1502 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Observer },
1503 )]
1504 #[case::audit_log_filter_signing(
1505 YubiHsm2UserMapping::AuditLog {
1506 authentication_key_id: "1".parse()?,
1507 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host".parse()?,
1508 system_user: "metrics-user".parse()?,
1509 },
1510 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Signing },
1511 )]
1512 #[case::backup_filter_admin(
1513 YubiHsm2UserMapping::Backup{
1514 authentication_key_id: "1".parse()?,
1515 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
1516 system_user: "backup-user".parse()?,
1517 wrapping_key_id: "1".parse()?,
1518 },
1519 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Admin },
1520 )]
1521 #[case::backup_filter_metrics(
1522 YubiHsm2UserMapping::Backup{
1523 authentication_key_id: "1".parse()?,
1524 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
1525 system_user: "backup-user".parse()?,
1526 wrapping_key_id: "1".parse()?,
1527 },
1528 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Metrics },
1529 )]
1530 #[case::backup_filter_observer(
1531 YubiHsm2UserMapping::Backup{
1532 authentication_key_id: "1".parse()?,
1533 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
1534 system_user: "backup-user".parse()?,
1535 wrapping_key_id: "1".parse()?,
1536 },
1537 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Observer },
1538 )]
1539 #[case::backup_filter_signing(
1540 YubiHsm2UserMapping::Backup{
1541 authentication_key_id: "1".parse()?,
1542 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
1543 system_user: "backup-user".parse()?,
1544 wrapping_key_id: "1".parse()?,
1545 },
1546 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Signing },
1547 )]
1548 #[case::hermetic_audit_log_filter_admin(
1549 YubiHsm2UserMapping::HermeticAuditLog {
1550 authentication_key_id: "1".parse()?,
1551 system_user: "metrics-user".parse()?,
1552 },
1553 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Admin },
1554 )]
1555 #[case::hermetic_audit_log_filter_backup(
1556 YubiHsm2UserMapping::HermeticAuditLog {
1557 authentication_key_id: "1".parse()?,
1558 system_user: "metrics-user".parse()?,
1559 },
1560 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Backup },
1561 )]
1562 #[case::hermetic_audit_log_filter_observer(
1563 YubiHsm2UserMapping::HermeticAuditLog {
1564 authentication_key_id: "1".parse()?,
1565 system_user: "metrics-user".parse()?,
1566 },
1567 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Observer },
1568 )]
1569 #[case::hermetic_audit_log_filter_signing(
1570 YubiHsm2UserMapping::HermeticAuditLog {
1571 authentication_key_id: "1".parse()?,
1572 system_user: "metrics-user".parse()?,
1573 },
1574 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Signing },
1575 )]
1576 #[case::signing_filter_admin(
1577 YubiHsm2UserMapping::Signing {
1578 authentication_key_id: "1".parse()?,
1579 signing_key_id: "1".parse()?,
1580 key_setup: SigningKeySetup::new(
1581 KeyType::Curve25519,
1582 vec![KeyMechanism::EdDsaSignature],
1583 None,
1584 SignatureType::EdDsa,
1585 CryptographicKeyContext::OpenPgp {
1586 user_ids: OpenPgpUserIdList::new(vec![
1587 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
1588 ])?,
1589 version: "v4".parse()?,
1590 },
1591 )?,
1592 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
1593 system_user: "signing-user".parse()?,
1594 domain: Domain::One,
1595 },
1596 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Admin },
1597 )]
1598 #[case::signing_filter_backup(
1599 YubiHsm2UserMapping::Signing {
1600 authentication_key_id: "1".parse()?,
1601 signing_key_id: "1".parse()?,
1602 key_setup: SigningKeySetup::new(
1603 KeyType::Curve25519,
1604 vec![KeyMechanism::EdDsaSignature],
1605 None,
1606 SignatureType::EdDsa,
1607 CryptographicKeyContext::OpenPgp {
1608 user_ids: OpenPgpUserIdList::new(vec![
1609 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
1610 ])?,
1611 version: "v4".parse()?,
1612 },
1613 )?,
1614 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
1615 system_user: "signing-user".parse()?,
1616 domain: Domain::One,
1617 },
1618 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Backup },
1619 )]
1620 #[case::signing_filter_metrics(
1621 YubiHsm2UserMapping::Signing {
1622 authentication_key_id: "1".parse()?,
1623 signing_key_id: "1".parse()?,
1624 key_setup: SigningKeySetup::new(
1625 KeyType::Curve25519,
1626 vec![KeyMechanism::EdDsaSignature],
1627 None,
1628 SignatureType::EdDsa,
1629 CryptographicKeyContext::OpenPgp {
1630 user_ids: OpenPgpUserIdList::new(vec![
1631 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
1632 ])?,
1633 version: "v4".parse()?,
1634 },
1635 )?,
1636 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
1637 system_user: "signing-user".parse()?,
1638 domain: Domain::One,
1639 },
1640 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Metrics },
1641 )]
1642 #[case::signing_filter_observer(
1643 YubiHsm2UserMapping::Signing {
1644 authentication_key_id: "1".parse()?,
1645 signing_key_id: "1".parse()?,
1646 key_setup: SigningKeySetup::new(
1647 KeyType::Curve25519,
1648 vec![KeyMechanism::EdDsaSignature],
1649 None,
1650 SignatureType::EdDsa,
1651 CryptographicKeyContext::OpenPgp {
1652 user_ids: OpenPgpUserIdList::new(vec![
1653 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
1654 ])?,
1655 version: "v4".parse()?,
1656 },
1657 )?,
1658 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
1659 system_user: "signing-user".parse()?,
1660 domain: Domain::One,
1661 },
1662 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Observer },
1663 )]
1664 fn yubihsm2_user_mapping_backend_users_with_new_passphrase_filter_mismatches(
1665 #[case] mapping: YubiHsm2UserMapping,
1666 #[case] filter: BackendUserIdFilter,
1667 ) -> TestResult {
1668 assert!(mapping.backend_users_with_new_passphrase(filter).is_empty());
1669
1670 Ok(())
1671 }
1672
1673 #[rstest]
1674 #[case::backup_filter_wrapping_no_domain(
1675 YubiHsm2UserMapping::Backup{
1676 authentication_key_id: "1".parse()?,
1677 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
1678 system_user: "backup-user".parse()?,
1679 wrapping_key_id: "1".parse()?,
1680 },
1681 YubiHsm2BackendKeyIdFilter{ key_type: KeyObjectType::Wrapping, key_domain: None },
1682 )]
1683 #[case::backup_filter_wrapping_some_domain(
1684 YubiHsm2UserMapping::Backup{
1685 authentication_key_id: "1".parse()?,
1686 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
1687 system_user: "backup-user".parse()?,
1688 wrapping_key_id: "1".parse()?,
1689 },
1690 YubiHsm2BackendKeyIdFilter{ key_type: KeyObjectType::Wrapping, key_domain: Some(Domain::One) },
1691 )]
1692 #[case::signing_filter_signing_matching_domain(
1693 YubiHsm2UserMapping::Signing {
1694 authentication_key_id: "1".parse()?,
1695 signing_key_id: "1".parse()?,
1696 key_setup: SigningKeySetup::new(
1697 KeyType::Curve25519,
1698 vec![KeyMechanism::EdDsaSignature],
1699 None,
1700 SignatureType::EdDsa,
1701 CryptographicKeyContext::OpenPgp {
1702 user_ids: OpenPgpUserIdList::new(vec![
1703 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
1704 ])?,
1705 version: "v4".parse()?,
1706 },
1707 )?,
1708 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
1709 system_user: "signing-user".parse()?,
1710 domain: Domain::One,
1711 },
1712 YubiHsm2BackendKeyIdFilter{ key_type: KeyObjectType::Signing, key_domain: Some(Domain::One) },
1713 )]
1714 fn yubihsm2_user_mapping_backend_key_id_filter_matches(
1715 #[case] mapping: YubiHsm2UserMapping,
1716 #[case] filter: YubiHsm2BackendKeyIdFilter,
1717 ) -> TestResult {
1718 assert!(mapping.backend_key_id(&filter).is_some_and(|id| id == "1"));
1719
1720 Ok(())
1721 }
1722
1723 #[rstest]
1724 #[case::backup_filter_signing_no_domain(
1725 YubiHsm2UserMapping::Backup{
1726 authentication_key_id: "1".parse()?,
1727 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
1728 system_user: "backup-user".parse()?,
1729 wrapping_key_id: "1".parse()?,
1730 },
1731 YubiHsm2BackendKeyIdFilter{ key_type: KeyObjectType::Signing, key_domain: None },
1732 )]
1733 #[case::backup_filter_signing_some_domain(
1734 YubiHsm2UserMapping::Backup{
1735 authentication_key_id: "1".parse()?,
1736 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
1737 system_user: "backup-user".parse()?,
1738 wrapping_key_id: "1".parse()?,
1739 },
1740 YubiHsm2BackendKeyIdFilter{ key_type: KeyObjectType::Signing, key_domain: Some(Domain::One) },
1741 )]
1742 #[case::signing_filter_signing_wrong_domain(
1743 YubiHsm2UserMapping::Signing {
1744 authentication_key_id: "1".parse()?,
1745 signing_key_id: "1".parse()?,
1746 key_setup: SigningKeySetup::new(
1747 KeyType::Curve25519,
1748 vec![KeyMechanism::EdDsaSignature],
1749 None,
1750 SignatureType::EdDsa,
1751 CryptographicKeyContext::OpenPgp {
1752 user_ids: OpenPgpUserIdList::new(vec![
1753 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
1754 ])?,
1755 version: "v4".parse()?,
1756 },
1757 )?,
1758 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
1759 system_user: "signing-user".parse()?,
1760 domain: Domain::One,
1761 },
1762 YubiHsm2BackendKeyIdFilter{ key_type: KeyObjectType::Signing, key_domain: Some(Domain::Two) },
1763 )]
1764 #[case::signing_filter_wrapping_same_domain(
1765 YubiHsm2UserMapping::Signing {
1766 authentication_key_id: "1".parse()?,
1767 signing_key_id: "1".parse()?,
1768 key_setup: SigningKeySetup::new(
1769 KeyType::Curve25519,
1770 vec![KeyMechanism::EdDsaSignature],
1771 None,
1772 SignatureType::EdDsa,
1773 CryptographicKeyContext::OpenPgp {
1774 user_ids: OpenPgpUserIdList::new(vec![
1775 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
1776 ])?,
1777 version: "v4".parse()?,
1778 },
1779 )?,
1780 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
1781 system_user: "signing-user".parse()?,
1782 domain: Domain::One,
1783 },
1784 YubiHsm2BackendKeyIdFilter{ key_type: KeyObjectType::Wrapping, key_domain: Some(Domain::One) },
1785 )]
1786 #[case::signing_filter_wrapping_wrong_domain(
1787 YubiHsm2UserMapping::Signing {
1788 authentication_key_id: "1".parse()?,
1789 signing_key_id: "1".parse()?,
1790 key_setup: SigningKeySetup::new(
1791 KeyType::Curve25519,
1792 vec![KeyMechanism::EdDsaSignature],
1793 None,
1794 SignatureType::EdDsa,
1795 CryptographicKeyContext::OpenPgp {
1796 user_ids: OpenPgpUserIdList::new(vec![
1797 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
1798 ])?,
1799 version: "v4".parse()?,
1800 },
1801 )?,
1802 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
1803 system_user: "signing-user".parse()?,
1804 domain: Domain::One,
1805 },
1806 YubiHsm2BackendKeyIdFilter{ key_type: KeyObjectType::Wrapping, key_domain: Some(Domain::Two) },
1807 )]
1808 #[case::signing_filter_wrapping_no_domain(
1809 YubiHsm2UserMapping::Signing {
1810 authentication_key_id: "1".parse()?,
1811 signing_key_id: "1".parse()?,
1812 key_setup: SigningKeySetup::new(
1813 KeyType::Curve25519,
1814 vec![KeyMechanism::EdDsaSignature],
1815 None,
1816 SignatureType::EdDsa,
1817 CryptographicKeyContext::OpenPgp {
1818 user_ids: OpenPgpUserIdList::new(vec![
1819 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
1820 ])?,
1821 version: "v4".parse()?,
1822 },
1823 )?,
1824 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
1825 system_user: "signing-user".parse()?,
1826 domain: Domain::One,
1827 },
1828 YubiHsm2BackendKeyIdFilter{ key_type: KeyObjectType::Wrapping, key_domain: None },
1829 )]
1830 fn yubihsm2_user_mapping_backend_key_id_filter_mismatches(
1831 #[case] mapping: YubiHsm2UserMapping,
1832 #[case] filter: YubiHsm2BackendKeyIdFilter,
1833 ) -> TestResult {
1834 assert!(mapping.backend_key_id(&filter).is_none());
1835
1836 Ok(())
1837 }
1838
1839 #[fixture]
1840 fn yubihsm2_yubihsm_connections() -> TestResult<[YubiHsmConnection; 2]> {
1841 Ok([
1842 YubiHsmConnection::Usb {
1843 serial_number: "0012345678".parse()?,
1844 },
1845 YubiHsmConnection::Usb {
1846 serial_number: "0087654321".parse()?,
1847 },
1848 ])
1849 }
1850
1851 #[fixture]
1852 fn yubihsm2_mappings() -> TestResult<[YubiHsm2UserMapping; 5]> {
1853 Ok([
1854 YubiHsm2UserMapping::Admin { authentication_key_id: "1".parse()? },
1855 YubiHsm2UserMapping::Backup{
1856 authentication_key_id: "2".parse()?,
1857 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
1858 system_user: "backup-user".parse()?,
1859 wrapping_key_id: "1".parse()?,
1860 },
1861 YubiHsm2UserMapping::AuditLog {
1862 authentication_key_id: "3".parse()?,
1863 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host".parse()?,
1864 system_user: "metrics-user".parse()?,
1865 },
1866 YubiHsm2UserMapping::HermeticAuditLog {
1867 authentication_key_id: "4".parse()?,
1868 system_user: "hermetic-metrics".parse()?,
1869 },
1870 YubiHsm2UserMapping::Signing {
1871 authentication_key_id: "5".parse()?,
1872 signing_key_id: "1".parse()?,
1873 key_setup: SigningKeySetup::new(
1874 KeyType::Curve25519,
1875 vec![KeyMechanism::EdDsaSignature],
1876 None,
1877 SignatureType::EdDsa,
1878 CryptographicKeyContext::OpenPgp {
1879 user_ids: OpenPgpUserIdList::new(vec![
1880 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
1881 ])?,
1882 version: "v4".parse()?,
1883 },
1884 )?,
1885 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
1886 system_user: "signing-user".parse()?,
1887 domain: Domain::One,
1888 }
1889 ])
1890 }
1891
1892 #[fixture]
1893 fn yubihsm2_config(
1894 yubihsm2_yubihsm_connections: TestResult<[YubiHsmConnection; 2]>,
1895 yubihsm2_mappings: TestResult<[YubiHsm2UserMapping; 5]>,
1896 ) -> TestResult<YubiHsm2Config> {
1897 let yubihsm2_yubihsm_connections = yubihsm2_yubihsm_connections?;
1898 let yubihsm2_mappings = yubihsm2_mappings?;
1899 let config = YubiHsm2Config::new(
1900 BTreeSet::from_iter(yubihsm2_yubihsm_connections),
1901 BTreeSet::from_iter(yubihsm2_mappings),
1902 )?;
1903
1904 Ok(config)
1905 }
1906
1907 #[rstest]
1908 fn yubihsm2_config_connections(
1909 yubihsm2_yubihsm_connections: TestResult<[YubiHsmConnection; 2]>,
1910 yubihsm2_config: TestResult<YubiHsm2Config>,
1911 ) -> TestResult {
1912 let yubihsm2_config = yubihsm2_config?;
1913 let yubihsm2_yubihsm_connections = yubihsm2_yubihsm_connections?;
1914 let connections = yubihsm2_config.connections();
1915
1916 assert_eq!(connections.len(), 2);
1917 assert!(
1918 connections
1919 .first()
1920 .is_some_and(|connection| connection == &yubihsm2_yubihsm_connections[0]),
1921 );
1922 assert!(
1923 connections
1924 .last()
1925 .is_some_and(|connection| connection == &yubihsm2_yubihsm_connections[1]),
1926 );
1927
1928 Ok(())
1929 }
1930
1931 #[rstest]
1932 fn yubihsm2_config_mappings(
1933 yubihsm2_mappings: TestResult<[YubiHsm2UserMapping; 5]>,
1934 yubihsm2_config: TestResult<YubiHsm2Config>,
1935 ) -> TestResult {
1936 let yubihsm2_config = yubihsm2_config?;
1937 let yubihsm2_mappings = yubihsm2_mappings?;
1938 let mappings = yubihsm2_config.mappings();
1939
1940 assert_eq!(mappings.len(), 5);
1941 for mapping in yubihsm2_mappings.iter() {
1942 assert!(mappings.contains(mapping));
1943 }
1944
1945 Ok(())
1946 }
1947
1948 #[rstest]
1949 fn yubihsm2_config_authorized_key_entries(
1950 yubihsm2_mappings: TestResult<[YubiHsm2UserMapping; 5]>,
1951 yubihsm2_config: TestResult<YubiHsm2Config>,
1952 ) -> TestResult {
1953 let yubihsm2_config = yubihsm2_config?;
1954 let authorized_key_entries = yubihsm2_config.authorized_key_entries();
1955
1956 let yubihsm2_mappings = yubihsm2_mappings?;
1957 let initial_entries = yubihsm2_mappings
1958 .iter()
1959 .filter_map(|mapping| mapping.authorized_key_entry())
1960 .collect::<HashSet<_>>();
1961
1962 assert_eq!(initial_entries, authorized_key_entries);
1963
1964 Ok(())
1965 }
1966
1967 #[rstest]
1968 fn yubihsm2_config_system_user_ids(
1969 yubihsm2_mappings: TestResult<[YubiHsm2UserMapping; 5]>,
1970 yubihsm2_config: TestResult<YubiHsm2Config>,
1971 ) -> TestResult {
1972 let yubihsm2_config = yubihsm2_config?;
1973 let system_user_ids = yubihsm2_config.system_user_ids();
1974
1975 let yubihsm2_mappings = yubihsm2_mappings?;
1976 let initial_entries = yubihsm2_mappings
1977 .iter()
1978 .filter_map(|mapping| mapping.system_user_id())
1979 .collect::<HashSet<_>>();
1980
1981 assert_eq!(initial_entries, system_user_ids);
1982
1983 Ok(())
1984 }
1985
1986 #[rstest]
1987 #[case::no_connection(
1988 "Error message for YubiHsm2Config::new with no connection",
1989 BTreeSet::new(),
1990 BTreeSet::from_iter([
1991 YubiHsm2UserMapping::Admin { authentication_key_id: "1".parse()? },
1992 YubiHsm2UserMapping::Backup{
1993 authentication_key_id: "2".parse()?,
1994 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
1995 system_user: "backup-user".parse()?,
1996 wrapping_key_id: "1".parse()?,
1997 },
1998 YubiHsm2UserMapping::AuditLog {
1999 authentication_key_id: "3".parse()?,
2000 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host".parse()?,
2001 system_user: "metrics-user".parse()?,
2002 },
2003 YubiHsm2UserMapping::Signing {
2004 authentication_key_id: "4".parse()?,
2005 signing_key_id: "1".parse()?,
2006 key_setup: SigningKeySetup::new(
2007 KeyType::Curve25519,
2008 vec![KeyMechanism::EdDsaSignature],
2009 None,
2010 SignatureType::EdDsa,
2011 CryptographicKeyContext::OpenPgp {
2012 user_ids: OpenPgpUserIdList::new(vec![
2013 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
2014 ])?,
2015 version: "v4".parse()?,
2016 },
2017 )?,
2018 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
2019 system_user: "signing-user".parse()?,
2020 domain: Domain::One,
2021 }
2022 ]),
2023 )]
2024 #[case::no_mappings(
2025 "Error message for YubiHsm2Config::new with no user mappings",
2026 BTreeSet::from_iter([
2027 YubiHsmConnection::Usb {serial_number: "0012345678".parse()? },
2028 YubiHsmConnection::Usb {serial_number: "0087654321".parse()? },
2029 ]),
2030 BTreeSet::new(),
2031 )]
2032 #[case::duplicate_system_user_ids(
2033 "Error message for YubiHsm2Config::new with two duplicate system user IDs",
2034 BTreeSet::from_iter([
2035 YubiHsmConnection::Usb {serial_number: "0012345678".parse()? },
2036 YubiHsmConnection::Usb {serial_number: "0087654321".parse()? },
2037 ]),
2038 BTreeSet::from_iter([
2039 YubiHsm2UserMapping::Admin { authentication_key_id: "1".parse()? },
2040 YubiHsm2UserMapping::Backup{
2041 authentication_key_id: "2".parse()?,
2042 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
2043 system_user: "backup-user".parse()?,
2044 wrapping_key_id: "1".parse()?,
2045 },
2046 YubiHsm2UserMapping::AuditLog {
2047 authentication_key_id: "3".parse()?,
2048 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host".parse()?,
2049 system_user: "backup-user".parse()?,
2050 },
2051 YubiHsm2UserMapping::Signing {
2052 authentication_key_id: "4".parse()?,
2053 signing_key_id: "1".parse()?,
2054 key_setup: SigningKeySetup::new(
2055 KeyType::Curve25519,
2056 vec![KeyMechanism::EdDsaSignature],
2057 None,
2058 SignatureType::EdDsa,
2059 CryptographicKeyContext::OpenPgp {
2060 user_ids: OpenPgpUserIdList::new(vec![
2061 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
2062 ])?,
2063 version: "v4".parse()?,
2064 },
2065 )?,
2066 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
2067 system_user: "signing-user".parse()?,
2068 domain: Domain::One,
2069 }
2070 ]),
2071 )]
2072 #[case::duplicate_ssh_public_keys(
2073 "Error message for YubiHsm2Config::new with two duplicate SSH public keys as authorized keys",
2074 BTreeSet::from_iter([
2075 YubiHsmConnection::Usb {serial_number: "0012345678".parse()? },
2076 YubiHsmConnection::Usb {serial_number: "0087654321".parse()? },
2077 ]),
2078 BTreeSet::from_iter([
2079 YubiHsm2UserMapping::Admin { authentication_key_id: "1".parse()? },
2080 YubiHsm2UserMapping::Backup{
2081 authentication_key_id: "2".parse()?,
2082 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
2083 system_user: "backup-user".parse()?,
2084 wrapping_key_id: "1".parse()?,
2085 },
2086 YubiHsm2UserMapping::AuditLog {
2087 authentication_key_id: "3".parse()?,
2088 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
2089 system_user: "metrics-user".parse()?,
2090 },
2091 YubiHsm2UserMapping::Signing {
2092 authentication_key_id: "4".parse()?,
2093 signing_key_id: "1".parse()?,
2094 key_setup: SigningKeySetup::new(
2095 KeyType::Curve25519,
2096 vec![KeyMechanism::EdDsaSignature],
2097 None,
2098 SignatureType::EdDsa,
2099 CryptographicKeyContext::OpenPgp {
2100 user_ids: OpenPgpUserIdList::new(vec![
2101 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
2102 ])?,
2103 version: "v4".parse()?,
2104 },
2105 )?,
2106 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
2107 system_user: "signing-user".parse()?,
2108 domain: Domain::One,
2109 }
2110 ]),
2111 )]
2112 #[case::no_administrator(
2113 "Error message for YubiHsm2Config::new with no administrator",
2114 BTreeSet::from_iter([
2115 YubiHsmConnection::Usb {serial_number: "0012345678".parse()? },
2116 YubiHsmConnection::Usb {serial_number: "0087654321".parse()? },
2117 ]),
2118 BTreeSet::from_iter([
2119 YubiHsm2UserMapping::Backup{
2120 authentication_key_id: "2".parse()?,
2121 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
2122 system_user: "backup-user".parse()?,
2123 wrapping_key_id: "1".parse()?,
2124 },
2125 YubiHsm2UserMapping::AuditLog {
2126 authentication_key_id: "3".parse()?,
2127 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host".parse()?,
2128 system_user: "metrics-user".parse()?,
2129 },
2130 YubiHsm2UserMapping::Signing {
2131 authentication_key_id: "4".parse()?,
2132 signing_key_id: "1".parse()?,
2133 key_setup: SigningKeySetup::new(
2134 KeyType::Curve25519,
2135 vec![KeyMechanism::EdDsaSignature],
2136 None,
2137 SignatureType::EdDsa,
2138 CryptographicKeyContext::OpenPgp {
2139 user_ids: OpenPgpUserIdList::new(vec![
2140 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
2141 ])?,
2142 version: "v4".parse()?,
2143 },
2144 )?,
2145 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
2146 system_user: "signing-user".parse()?,
2147 domain: Domain::One,
2148 }
2149 ]),
2150 )]
2151 #[case::duplicate_backend_user_ids(
2152 "Error message for YubiHsm2Config::new with two duplicate backend user IDs",
2153 BTreeSet::from_iter([
2154 YubiHsmConnection::Usb {serial_number: "0012345678".parse()? },
2155 YubiHsmConnection::Usb {serial_number: "0087654321".parse()? },
2156 ]),
2157 BTreeSet::from_iter([
2158 YubiHsm2UserMapping::Admin { authentication_key_id: "1".parse()? },
2159 YubiHsm2UserMapping::Backup{
2160 authentication_key_id: "2".parse()?,
2161 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
2162 system_user: "backup-user".parse()?,
2163 wrapping_key_id: "1".parse()?,
2164 },
2165 YubiHsm2UserMapping::AuditLog {
2166 authentication_key_id: "3".parse()?,
2167 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host".parse()?,
2168 system_user: "metrics-user".parse()?,
2169 },
2170 YubiHsm2UserMapping::Signing {
2171 authentication_key_id: "3".parse()?,
2172 signing_key_id: "1".parse()?,
2173 key_setup: SigningKeySetup::new(
2174 KeyType::Curve25519,
2175 vec![KeyMechanism::EdDsaSignature],
2176 None,
2177 SignatureType::EdDsa,
2178 CryptographicKeyContext::OpenPgp {
2179 user_ids: OpenPgpUserIdList::new(vec![
2180 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
2181 ])?,
2182 version: "v4".parse()?,
2183 },
2184 )?,
2185 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
2186 system_user: "signing-user".parse()?,
2187 domain: Domain::One,
2188 }
2189 ]),
2190 )]
2191 #[case::duplicate_signing_key_ids(
2192 "Error message for YubiHsm2Config::new with two duplicate signing key IDs",
2193 BTreeSet::from_iter([
2194 YubiHsmConnection::Usb {serial_number: "0012345678".parse()? },
2195 YubiHsmConnection::Usb {serial_number: "0087654321".parse()? },
2196 ]),
2197 BTreeSet::from_iter([
2198 YubiHsm2UserMapping::Admin { authentication_key_id: "1".parse()? },
2199 YubiHsm2UserMapping::Backup{
2200 authentication_key_id: "2".parse()?,
2201 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
2202 system_user: "backup-user".parse()?,
2203 wrapping_key_id: "1".parse()?,
2204 },
2205 YubiHsm2UserMapping::AuditLog {
2206 authentication_key_id: "3".parse()?,
2207 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host".parse()?,
2208 system_user: "metrics-user".parse()?,
2209 },
2210 YubiHsm2UserMapping::Signing {
2211 authentication_key_id: "4".parse()?,
2212 signing_key_id: "1".parse()?,
2213 key_setup: SigningKeySetup::new(
2214 KeyType::Curve25519,
2215 vec![KeyMechanism::EdDsaSignature],
2216 None,
2217 SignatureType::EdDsa,
2218 CryptographicKeyContext::OpenPgp {
2219 user_ids: OpenPgpUserIdList::new(vec![
2220 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
2221 ])?,
2222 version: "v4".parse()?,
2223 },
2224 )?,
2225 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
2226 system_user: "signing-user".parse()?,
2227 domain: Domain::One,
2228 },
2229 YubiHsm2UserMapping::Signing {
2230 authentication_key_id: "5".parse()?,
2231 signing_key_id: "1".parse()?,
2232 key_setup: SigningKeySetup::new(
2233 KeyType::Curve25519,
2234 vec![KeyMechanism::EdDsaSignature],
2235 None,
2236 SignatureType::EdDsa,
2237 CryptographicKeyContext::OpenPgp {
2238 user_ids: OpenPgpUserIdList::new(vec![
2239 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
2240 ])?,
2241 version: "v4".parse()?,
2242 },
2243 )?,
2244 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPDgwGfIRBAsOUuDEZw/uJQZSwOYr4sg2DAZpcc7MfOj user@host".parse()?,
2245 system_user: "signing-user2".parse()?,
2246 domain: Domain::Two,
2247 },
2248 ]),
2249 )]
2250 #[case::duplicate_wrapping_key_ids(
2251 "Error message for YubiHsm2Config::new with two duplicate wrapping key IDs",
2252 BTreeSet::from_iter([
2253 YubiHsmConnection::Usb {serial_number: "0012345678".parse()? },
2254 YubiHsmConnection::Usb {serial_number: "0087654321".parse()? },
2255 ]),
2256 BTreeSet::from_iter([
2257 YubiHsm2UserMapping::Admin { authentication_key_id: "1".parse()? },
2258 YubiHsm2UserMapping::Backup{
2259 authentication_key_id: "2".parse()?,
2260 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
2261 system_user: "backup-user".parse()?,
2262 wrapping_key_id: "1".parse()?,
2263 },
2264 YubiHsm2UserMapping::Backup{
2265 authentication_key_id: "3".parse()?,
2266 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPDgwGfIRBAsOUuDEZw/uJQZSwOYr4sg2DAZpcc7MfOj user@host".parse()?,
2267 system_user: "backup-user2".parse()?,
2268 wrapping_key_id: "1".parse()?,
2269 },
2270 YubiHsm2UserMapping::AuditLog {
2271 authentication_key_id: "4".parse()?,
2272 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host".parse()?,
2273 system_user: "metrics-user".parse()?,
2274 },
2275 YubiHsm2UserMapping::Signing {
2276 authentication_key_id: "5".parse()?,
2277 signing_key_id: "1".parse()?,
2278 key_setup: SigningKeySetup::new(
2279 KeyType::Curve25519,
2280 vec![KeyMechanism::EdDsaSignature],
2281 None,
2282 SignatureType::EdDsa,
2283 CryptographicKeyContext::OpenPgp {
2284 user_ids: OpenPgpUserIdList::new(vec![
2285 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
2286 ])?,
2287 version: "v4".parse()?,
2288 },
2289 )?,
2290 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
2291 system_user: "signing-user".parse()?,
2292 domain: Domain::One,
2293 },
2294 ]),
2295 )]
2296 #[case::duplicate_domains(
2297 "Error message for YubiHsm2Config::new with two duplicate domains",
2298 BTreeSet::from_iter([
2299 YubiHsmConnection::Usb {serial_number: "0012345678".parse()? },
2300 YubiHsmConnection::Usb {serial_number: "0087654321".parse()? },
2301 ]),
2302 BTreeSet::from_iter([
2303 YubiHsm2UserMapping::Admin { authentication_key_id: "1".parse()? },
2304 YubiHsm2UserMapping::Backup{
2305 authentication_key_id: "2".parse()?,
2306 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
2307 system_user: "backup-user".parse()?,
2308 wrapping_key_id: "1".parse()?,
2309 },
2310 YubiHsm2UserMapping::AuditLog {
2311 authentication_key_id: "3".parse()?,
2312 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host".parse()?,
2313 system_user: "metrics-user".parse()?,
2314 },
2315 YubiHsm2UserMapping::Signing {
2316 authentication_key_id: "4".parse()?,
2317 signing_key_id: "1".parse()?,
2318 key_setup: SigningKeySetup::new(
2319 KeyType::Curve25519,
2320 vec![KeyMechanism::EdDsaSignature],
2321 None,
2322 SignatureType::EdDsa,
2323 CryptographicKeyContext::OpenPgp {
2324 user_ids: OpenPgpUserIdList::new(vec![
2325 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
2326 ])?,
2327 version: "v4".parse()?,
2328 },
2329 )?,
2330 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
2331 system_user: "signing-user".parse()?,
2332 domain: Domain::One,
2333 },
2334 YubiHsm2UserMapping::Signing {
2335 authentication_key_id: "5".parse()?,
2336 signing_key_id: "2".parse()?,
2337 key_setup: SigningKeySetup::new(
2338 KeyType::Curve25519,
2339 vec![KeyMechanism::EdDsaSignature],
2340 None,
2341 SignatureType::EdDsa,
2342 CryptographicKeyContext::OpenPgp {
2343 user_ids: OpenPgpUserIdList::new(vec![
2344 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
2345 ])?,
2346 version: "v4".parse()?,
2347 },
2348 )?,
2349 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPDgwGfIRBAsOUuDEZw/uJQZSwOYr4sg2DAZpcc7MfOj user@host".parse()?,
2350 system_user: "signing-user2".parse()?,
2351 domain: Domain::One,
2352 },
2353 ]),
2354 )]
2355 #[case::all_the_issues(
2356 "Error message for YubiHsm2Config::new with multiple validation issues (connections and mappings)",
2357 BTreeSet::new(),
2358 BTreeSet::from_iter([
2359 YubiHsm2UserMapping::Backup{
2360 authentication_key_id: "2".parse()?,
2361 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
2362 system_user: "backup-user".parse()?,
2363 wrapping_key_id: "1".parse()?,
2364 },
2365 YubiHsm2UserMapping::Backup{
2366 authentication_key_id: "3".parse()?,
2367 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPDgwGfIRBAsOUuDEZw/uJQZSwOYr4sg2DAZpcc7MfOj user@host".parse()?,
2368 system_user: "backup-user".parse()?,
2369 wrapping_key_id: "1".parse()?,
2370 },
2371 YubiHsm2UserMapping::AuditLog {
2372 authentication_key_id: "3".parse()?,
2373 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host".parse()?,
2374 system_user: "metrics-backupuser".parse()?,
2375 },
2376 YubiHsm2UserMapping::Signing {
2377 authentication_key_id: "5".parse()?,
2378 signing_key_id: "1".parse()?,
2379 key_setup: SigningKeySetup::new(
2380 KeyType::Curve25519,
2381 vec![KeyMechanism::EdDsaSignature],
2382 None,
2383 SignatureType::EdDsa,
2384 CryptographicKeyContext::OpenPgp {
2385 user_ids: OpenPgpUserIdList::new(vec![
2386 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
2387 ])?,
2388 version: "v4".parse()?,
2389 },
2390 )?,
2391 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
2392 system_user: "signing-user".parse()?,
2393 domain: Domain::One,
2394 },
2395 YubiHsm2UserMapping::Signing {
2396 authentication_key_id: "5".parse()?,
2397 signing_key_id: "1".parse()?,
2398 key_setup: SigningKeySetup::new(
2399 KeyType::Curve25519,
2400 vec![KeyMechanism::EdDsaSignature],
2401 None,
2402 SignatureType::EdDsa,
2403 CryptographicKeyContext::OpenPgp {
2404 user_ids: OpenPgpUserIdList::new(vec![
2405 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
2406 ])?,
2407 version: "v4".parse()?,
2408 },
2409 )?,
2410 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
2411 system_user: "signing-user2".parse()?,
2412 domain: Domain::One,
2413 },
2414 ]),
2415 )]
2416 fn yubihsm2_config_new_fails_validation(
2417 #[case] description: &str,
2418 #[case] connections: BTreeSet<YubiHsmConnection>,
2419 #[case] mappings: BTreeSet<YubiHsm2UserMapping>,
2420 ) -> TestResult {
2421 let error_msg = match YubiHsm2Config::new(connections, mappings) {
2422 Err(crate::Error::Validation { source, .. }) => source.to_string(),
2423 Ok(config) => {
2424 panic!("Expected to fail with Error::Validation, but succeeded instead: {config:?}")
2425 }
2426 Err(error) => panic!(
2427 "Expected to fail with Error::Validation, but failed with a different error instead: {error}"
2428 ),
2429 };
2430
2431 with_settings!({
2432 description => description,
2433 snapshot_path => SNAPSHOT_PATH,
2434 prepend_module_to_snapshot => false,
2435 }, {
2436 assert_snapshot!(current().name().expect("current thread should have a name").to_string().replace("::", "__"), error_msg);
2437 });
2438 Ok(())
2439 }
2440}