1use std::collections::{BTreeSet, HashSet};
3
4use garde::Validate;
5use serde::{Deserialize, Serialize};
6use signstar_crypto::{key::SigningKeySetup, passphrase::Passphrase, traits::UserWithPassphrase};
7use signstar_yubihsm2::{
8 Connection,
9 Credentials,
10 automation::OpaqueData,
11 backup::Label,
12 object::{Capabilities, Capability, Domain, Domains, KeyInfo},
13 yubihsm::{Code, Id},
14};
15
16use crate::config::{
17 AuthorizedKeyEntry,
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 SystemUserData,
31 SystemUserId,
32 duplicate_authorized_keys,
33 duplicate_backend_user_ids,
34 duplicate_domains,
35 duplicate_key_ids,
36 duplicate_system_user_ids,
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 {
86 authentication_key_id: Id,
88 },
89
90 AuditLog {
113 authentication_key_id: Id,
115
116 ssh_authorized_key: AuthorizedKeyEntry,
118
119 system_user: SystemUserId,
121 },
122
123 Backup {
150 authentication_key_id: Id,
156
157 wrapping_key_id: Id,
168
169 ssh_authorized_key: AuthorizedKeyEntry,
171
172 system_user: SystemUserId,
174 },
175
176 HermeticAuditLog {
199 authentication_key_id: Id,
201
202 system_user: SystemUserId,
204 },
205
206 Signing {
237 authentication_key_id: Id,
239
240 key_setup: SigningKeySetup,
242
243 domain: Domain,
247
248 signing_key_id: Id,
250
251 ssh_authorized_key: AuthorizedKeyEntry,
253
254 system_user: SystemUserId,
256 },
257}
258
259impl YubiHsm2UserMapping {
260 pub const CAP_ADMIN: &[Capability] = &[
294 Capability::ChangeAuthenticationKey,
295 Capability::DeleteAsymmetricKey,
296 Capability::DeleteAuthenticationKey,
297 Capability::DeleteHmacKey,
298 Capability::DeleteOpaque,
299 Capability::DeleteTemplate,
300 Capability::DeleteWrapKey,
301 Capability::ExportableUnderWrap,
302 Capability::ExportWrapped,
303 Capability::GenerateAsymmetricKey,
304 Capability::GenerateHmacKey,
305 Capability::GenerateWrapKey,
306 Capability::GetLogEntries,
307 Capability::GetOpaque,
308 Capability::GetOption,
309 Capability::GetTemplate,
310 Capability::ImportWrapped,
311 Capability::PutAsymmetricKey,
312 Capability::PutAuthenticationKey,
313 Capability::PutHmacKey,
314 Capability::PutOpaque,
315 Capability::SetOption,
316 Capability::PutTemplate,
317 Capability::PutWrapKey,
318 Capability::ResetDevice,
319 Capability::SignHmac,
320 Capability::SignEddsa,
321 Capability::UnwrapData,
322 Capability::VerifyHmac,
323 Capability::WrapData,
324 ];
325
326 pub const CAP_AUDIT_LOG: &[Capability] = &[Capability::GetLogEntries];
334
335 pub const CAP_BACKUP: &[Capability] = &[Capability::ExportWrapped];
343
344 pub const CAP_HERMETIC_AUDIT_LOG: &[Capability] = &[Capability::GetLogEntries];
352
353 pub const CAP_SIGNING: &[Capability] = &[Capability::SignEddsa];
361
362 pub fn domains(&self) -> Domains {
364 match self {
365 Self::Admin { .. }
366 | Self::Backup { .. }
367 | Self::AuditLog { .. }
368 | Self::HermeticAuditLog { .. } => Domains::all(),
369 Self::Signing {
370 domain: key_domain, ..
371 } => Domains::from(*key_domain),
372 }
373 }
374
375 pub fn backend_user_id(&self) -> Id {
377 match self {
378 Self::Admin {
379 authentication_key_id,
380 }
381 | Self::AuditLog {
382 authentication_key_id,
383 ..
384 }
385 | Self::Backup {
386 authentication_key_id,
387 ..
388 }
389 | Self::HermeticAuditLog {
390 authentication_key_id,
391 ..
392 }
393 | Self::Signing {
394 authentication_key_id,
395 ..
396 } => *authentication_key_id,
397 }
398 }
399
400 pub fn capabilities(&self) -> Capabilities {
407 Capabilities::from(match self {
408 Self::Admin { .. } => Self::CAP_ADMIN,
409 Self::AuditLog { .. } => Self::CAP_AUDIT_LOG,
410 Self::Backup { .. } => Self::CAP_BACKUP,
411 Self::HermeticAuditLog { .. } => Self::CAP_HERMETIC_AUDIT_LOG,
412 Self::Signing { .. } => Self::CAP_SIGNING,
413 })
414 }
415
416 pub fn label(&self) -> Label {
418 Label::from_truncated_str(match self {
419 Self::Admin { .. } => "admin",
420 Self::AuditLog { .. } => "audit log",
421 Self::Backup { .. } => "backup",
422 Self::HermeticAuditLog { .. } => "hermetic audit log",
423 Self::Signing { .. } => "signing",
424 })
425 }
426
427 pub fn authentication_key_info(&self) -> KeyInfo {
429 match self {
430 Self::Admin {
431 authentication_key_id,
432 }
433 | Self::AuditLog {
434 authentication_key_id,
435 ..
436 }
437 | Self::Backup {
438 authentication_key_id,
439 ..
440 }
441 | Self::HermeticAuditLog {
442 authentication_key_id,
443 ..
444 }
445 | Self::Signing {
446 authentication_key_id,
447 ..
448 } => KeyInfo {
449 key_id: *authentication_key_id,
450 domains: self.domains(),
451 caps: self.capabilities(),
452 label: self.label(),
453 },
454 }
455 }
456}
457
458impl MappingSystemUserId for YubiHsm2UserMapping {
459 fn system_user_id(&self) -> Option<&SystemUserId> {
460 match self {
461 Self::Admin { .. } => None,
462 Self::AuditLog { system_user, .. }
463 | Self::Backup { system_user, .. }
464 | Self::HermeticAuditLog { system_user, .. }
465 | Self::Signing { system_user, .. } => Some(system_user),
466 }
467 }
468}
469
470impl MappingBackendUserIds for YubiHsm2UserMapping {
471 fn backend_user_ids(&self, filter: BackendUserIdFilter) -> Vec<String> {
472 match self {
473 Self::Admin {
474 authentication_key_id,
475 } => {
476 if [BackendUserIdKind::Admin, BackendUserIdKind::Any]
477 .contains(&filter.backend_user_id_kind)
478 {
479 Some(vec![authentication_key_id.to_string()])
480 } else {
481 None
482 }
483 }
484 Self::AuditLog {
485 authentication_key_id,
486 ..
487 } => {
488 if [
489 BackendUserIdKind::Any,
490 BackendUserIdKind::Metrics,
491 BackendUserIdKind::NonAdmin,
492 ]
493 .contains(&filter.backend_user_id_kind)
494 {
495 Some(vec![authentication_key_id.to_string()])
496 } else {
497 None
498 }
499 }
500 Self::Backup {
501 authentication_key_id,
502 ..
503 } => {
504 if [
505 BackendUserIdKind::Any,
506 BackendUserIdKind::Backup,
507 BackendUserIdKind::NonAdmin,
508 ]
509 .contains(&filter.backend_user_id_kind)
510 {
511 Some(vec![authentication_key_id.to_string()])
512 } else {
513 None
514 }
515 }
516 Self::HermeticAuditLog {
517 authentication_key_id,
518 ..
519 } => {
520 if [
521 BackendUserIdKind::Any,
522 BackendUserIdKind::Metrics,
523 BackendUserIdKind::NonAdmin,
524 ]
525 .contains(&filter.backend_user_id_kind)
526 {
527 Some(vec![authentication_key_id.to_string()])
528 } else {
529 None
530 }
531 }
532 Self::Signing {
533 authentication_key_id,
534 ..
535 } => {
536 if [
537 BackendUserIdKind::Any,
538 BackendUserIdKind::NonAdmin,
539 BackendUserIdKind::Signing,
540 ]
541 .contains(&filter.backend_user_id_kind)
542 {
543 Some(vec![authentication_key_id.to_string()])
544 } else {
545 None
546 }
547 }
548 }
549 .unwrap_or_default()
550 }
551
552 fn backend_user_with_passphrase(
553 &self,
554 name: &str,
555 passphrase: Passphrase,
556 ) -> Result<Box<dyn UserWithPassphrase>, crate::Error> {
557 let backend_user_id = self.backend_user_id();
558 if backend_user_id.to_string() != name {
559 return Err(Error::AuthenticationKeyIdMismatch {
560 expected: name.to_string(),
561 actual: backend_user_id.to_string(),
562 }
563 .into());
564 }
565
566 Ok(Box::new(Credentials::new(backend_user_id, passphrase)))
567 }
568
569 fn backend_users_with_new_passphrase(
570 &self,
571 filter: BackendUserIdFilter,
572 ) -> Vec<Box<dyn UserWithPassphrase>> {
573 if let Some(authentication_key_id) = match self {
574 Self::Admin {
575 authentication_key_id,
576 } => {
577 if [BackendUserIdKind::Admin, BackendUserIdKind::Any]
578 .contains(&filter.backend_user_id_kind)
579 {
580 Some(authentication_key_id)
581 } else {
582 None
583 }
584 }
585 Self::AuditLog {
586 authentication_key_id,
587 ..
588 } => {
589 if [
590 BackendUserIdKind::Any,
591 BackendUserIdKind::Metrics,
592 BackendUserIdKind::NonAdmin,
593 ]
594 .contains(&filter.backend_user_id_kind)
595 {
596 Some(authentication_key_id)
597 } else {
598 None
599 }
600 }
601 Self::Backup {
602 authentication_key_id,
603 ..
604 } => {
605 if [
606 BackendUserIdKind::Any,
607 BackendUserIdKind::Backup,
608 BackendUserIdKind::NonAdmin,
609 ]
610 .contains(&filter.backend_user_id_kind)
611 {
612 Some(authentication_key_id)
613 } else {
614 None
615 }
616 }
617 Self::HermeticAuditLog {
618 authentication_key_id,
619 ..
620 } => {
621 if [
622 BackendUserIdKind::Any,
623 BackendUserIdKind::Metrics,
624 BackendUserIdKind::NonAdmin,
625 ]
626 .contains(&filter.backend_user_id_kind)
627 {
628 Some(authentication_key_id)
629 } else {
630 None
631 }
632 }
633 Self::Signing {
634 authentication_key_id,
635 ..
636 } => {
637 if [
638 BackendUserIdKind::Any,
639 BackendUserIdKind::NonAdmin,
640 BackendUserIdKind::Signing,
641 ]
642 .contains(&filter.backend_user_id_kind)
643 {
644 Some(authentication_key_id)
645 } else {
646 None
647 }
648 }
649 } {
650 vec![Box::new(Credentials::new(
651 *authentication_key_id,
652 Passphrase::generate(None),
653 ))]
654 } else {
655 Vec::new()
656 }
657 }
658}
659
660impl MappingAuthorizedKeyEntry for YubiHsm2UserMapping {
661 fn authorized_key_entry(&self) -> Option<&AuthorizedKeyEntry> {
662 match self {
663 Self::Admin { .. } | Self::HermeticAuditLog { .. } => None,
664 Self::AuditLog {
665 ssh_authorized_key, ..
666 }
667 | Self::Backup {
668 ssh_authorized_key, ..
669 }
670 | Self::Signing {
671 ssh_authorized_key, ..
672 } => Some(ssh_authorized_key),
673 }
674 }
675}
676
677impl<'a> From<&'a YubiHsm2UserMapping> for SystemUserData<'a> {
678 fn from(value: &'a YubiHsm2UserMapping) -> Self {
679 match value {
680 YubiHsm2UserMapping::Admin { .. } => Self::BackendAdmin {
681 system_user: SystemUserId::root(),
682 },
683 YubiHsm2UserMapping::AuditLog {
684 ssh_authorized_key,
685 system_user,
686 ..
687 } => Self::BackendMetrics {
688 system_user,
689 ssh_authorized_key,
690 },
691 YubiHsm2UserMapping::Backup {
692 ssh_authorized_key,
693 system_user,
694 ..
695 } => Self::BackendBackup {
696 system_user,
697 ssh_authorized_key,
698 },
699 YubiHsm2UserMapping::HermeticAuditLog { system_user, .. } => {
700 Self::BackendHermeticMetrics { system_user }
701 }
702 YubiHsm2UserMapping::Signing {
703 ssh_authorized_key,
704 system_user,
705 ..
706 } => Self::BackendSign {
707 system_user,
708 ssh_authorized_key,
709 },
710 }
711 }
712}
713
714#[derive(Clone, Copy, Debug)]
716pub struct YubiHsm2DomainFilter {}
717
718impl BackendDomainFilter for YubiHsm2DomainFilter {}
719
720impl MappingBackendDomain<YubiHsm2DomainFilter> for YubiHsm2UserMapping {
721 fn backend_domain(&self, _filter: Option<&YubiHsm2DomainFilter>) -> Option<String> {
722 Some(self.domains().bits().to_string())
723 }
724}
725
726#[derive(Clone, Copy, Debug, Eq, PartialEq)]
734pub enum KeyObjectType {
735 Signing,
739
740 Wrapping,
744}
745
746#[derive(Clone, Debug)]
748pub struct YubiHsm2BackendKeyIdFilter {
749 pub key_type: KeyObjectType,
753
754 pub key_domain: Option<Domain>,
758}
759
760impl BackendKeyIdFilter for YubiHsm2BackendKeyIdFilter {}
761
762impl MappingBackendKeyId<YubiHsm2BackendKeyIdFilter> for YubiHsm2UserMapping {
763 fn backend_key_id(&self, filter: &YubiHsm2BackendKeyIdFilter) -> Option<String> {
764 match self {
765 Self::Admin { .. } | Self::AuditLog { .. } | Self::HermeticAuditLog { .. } => None,
766 Self::Backup {
767 wrapping_key_id, ..
768 } => {
769 if filter.key_type == KeyObjectType::Wrapping {
770 Some(wrapping_key_id.to_string())
772 } else {
773 None
774 }
775 }
776 Self::Signing {
777 signing_key_id,
778 domain: key_domain,
779 ..
780 } => {
781 if filter.key_type == KeyObjectType::Signing {
782 if let Some(filter_key_domain) = filter.key_domain {
783 if &filter_key_domain == key_domain {
784 Some(signing_key_id.to_string())
785 } else {
786 None
787 }
788 } else {
789 Some(signing_key_id.to_string())
790 }
791 } else {
792 None
793 }
794 }
795 }
796 }
797}
798
799impl MappingBackendUserSecrets for YubiHsm2UserMapping {}
800
801fn validate_yubihsm2_config_connections(
809 value: &BTreeSet<Connection>,
810 _context: &(),
811) -> garde::Result {
812 if value.is_empty() {
813 return Err(garde::Error::new("contains no connections".to_string()));
814 }
815
816 Ok(())
817}
818
819fn validate_yubihsm2_config_mappings(
848 value: &BTreeSet<YubiHsm2UserMapping>,
849 _context: &(),
850) -> garde::Result {
851 if value.is_empty() {
852 return Err(garde::Error::new("contains no user mappings".to_string()));
853 }
854
855 let duplicate_system_user_ids = duplicate_system_user_ids(value);
857
858 let duplicate_authorized_keys = duplicate_authorized_keys(value);
860
861 let missing_admin = {
863 let num_system_admins = value
864 .iter()
865 .filter_map(|mapping| {
866 if let YubiHsm2UserMapping::Admin {
867 authentication_key_id,
868 } = mapping
869 {
870 Some(authentication_key_id)
871 } else {
872 None
873 }
874 })
875 .count();
876
877 if num_system_admins == 0 {
878 Some("no administrator user".to_string())
879 } else {
880 None
881 }
882 };
883
884 let size_estimations = value.iter().filter_map(|mapping| {
885 if let YubiHsm2UserMapping::Signing { key_setup, .. } = mapping {
886 match key_setup.key_context().openpgp_cert_size() {
887 Err(source) =>
888 Some(format!(
889 "dummy certificate creation for size estimation failed because of: {source:?}"
890 )),
891 Ok(Some(cert_size)) if cert_size > OpaqueData::MAX_DATA_SIZE => Some(format!("estimated certificate size {cert_size} exceeds the storage limit of {max_size}", max_size = OpaqueData::MAX_DATA_SIZE)),
892 _ => None
893 }
894 } else {
895 None
896 }
897 }).fold(None, |error, item| {
898 if let Some(error) = error {
899 Some(error + item.as_ref())
900 } else {
901 Some(item)
902 }
903 });
904
905 let duplicate_backend_user_ids = duplicate_backend_user_ids(value);
907
908 let duplicate_signing_key_ids = duplicate_key_ids(
910 value,
911 &YubiHsm2BackendKeyIdFilter {
912 key_type: KeyObjectType::Signing,
913 key_domain: None,
914 },
915 Some(" signing".to_string()),
916 );
917
918 let duplicate_wrapping_key_ids = duplicate_key_ids(
920 value,
921 &YubiHsm2BackendKeyIdFilter {
922 key_type: KeyObjectType::Wrapping,
923 key_domain: None,
924 },
925 Some(" wrapping".to_string()),
926 );
927
928 let duplicate_domains = duplicate_domains(
933 &value
934 .iter()
935 .filter(|mapping| matches!(mapping, YubiHsm2UserMapping::Signing { .. }))
936 .collect::<BTreeSet<_>>(),
937 None,
938 None,
939 None,
940 );
941
942 let messages = [
943 duplicate_system_user_ids,
944 duplicate_authorized_keys,
945 missing_admin,
946 duplicate_backend_user_ids,
947 duplicate_signing_key_ids,
948 duplicate_wrapping_key_ids,
949 duplicate_domains,
950 size_estimations,
951 ];
952 let error_messages = {
953 let mut error_messages = Vec::new();
954
955 for message in messages.iter().flatten() {
956 error_messages.push(message.as_str());
957 }
958
959 error_messages
960 };
961
962 match error_messages.len() {
963 0 => Ok(()),
964 1 => Err(garde::Error::new(format!(
965 "contains {}",
966 error_messages.join("\n")
967 ))),
968 _ => Err(garde::Error::new(format!(
969 "contains multiple issues:\n⤷ {}",
970 error_messages.join("\n⤷ ")
971 ))),
972 }
973}
974
975#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize, Validate)]
980#[serde(rename_all = "snake_case")]
981pub struct YubiHsm2Config {
982 #[garde(custom(validate_yubihsm2_config_connections))]
984 connections: BTreeSet<Connection>,
985
986 #[garde(custom(validate_yubihsm2_config_mappings))]
988 mappings: BTreeSet<YubiHsm2UserMapping>,
989}
990
991impl YubiHsm2Config {
992 pub const AUDIT_COMMANDS: &[Code] = &[
996 Code::AuthenticateSession,
997 Code::ChangeAuthenticationKey,
998 Code::CloseSession,
999 Code::CreateSession,
1000 Code::DeleteObject,
1001 Code::ExportWrapped,
1002 Code::GetObjectInfo,
1003 Code::GetLogEntries,
1004 Code::GetOpaqueObject,
1005 Code::GetOption,
1006 Code::GetPublicKey,
1007 Code::GetStorageInfo,
1008 Code::HsmInitialization,
1009 Code::ImportWrapped,
1010 Code::PutOpaqueObject,
1011 Code::PutWrapKey,
1012 Code::ResetDevice,
1013 Code::SetOption,
1014 Code::SignAttestationCertificate,
1015 Code::SignEddsa,
1016 ];
1017
1018 pub const WRAP_KEY_ID: Id = 1;
1022
1023 pub const WRAP_KEY_LABEL: &str = "wrap key";
1025
1026 pub const OPENPGP_CERTIFICATE_LABEL: &str = "OpenPGP certificate";
1028
1029 pub fn new(
1032 connections: BTreeSet<Connection>,
1033 mappings: BTreeSet<YubiHsm2UserMapping>,
1034 ) -> Result<Self, crate::Error> {
1035 let config = Self {
1036 connections,
1037 mappings,
1038 };
1039 config
1040 .validate()
1041 .map_err(|source| crate::Error::Validation {
1042 context: "validating a YubiHSM2 specific configuration item".to_string(),
1043 source,
1044 })?;
1045
1046 Ok(config)
1047 }
1048
1049 pub fn connections(&self) -> &BTreeSet<Connection> {
1051 &self.connections
1052 }
1053
1054 pub fn mappings(&self) -> &BTreeSet<YubiHsm2UserMapping> {
1056 &self.mappings
1057 }
1058
1059 pub fn wrap_key_label() -> Label {
1061 Label::from_truncated_str(Self::WRAP_KEY_LABEL)
1062 }
1063
1064 pub fn openpgp_certificate_label() -> Label {
1066 Label::from_truncated_str(Self::OPENPGP_CERTIFICATE_LABEL)
1067 }
1068}
1069
1070impl ConfigAuthorizedKeyEntries for YubiHsm2Config {
1071 fn authorized_key_entries(&self) -> HashSet<&AuthorizedKeyEntry> {
1072 self.mappings
1073 .iter()
1074 .filter_map(|mapping| mapping.authorized_key_entry())
1075 .collect()
1076 }
1077}
1078
1079impl ConfigSystemUserIds for YubiHsm2Config {
1080 fn system_user_ids(&self) -> HashSet<&SystemUserId> {
1081 self.mappings
1082 .iter()
1083 .filter_map(|mapping| mapping.system_user_id())
1084 .collect()
1085 }
1086}
1087
1088#[derive(Clone, Copy, Debug, strum::Display, Eq, Hash, Ord, PartialEq, PartialOrd)]
1090#[strum(serialize_all = "snake_case")]
1091pub enum AuthType {
1092 Admin,
1094
1095 AuditLog,
1097
1098 Backup,
1100
1101 HermeticAuditLog,
1103
1104 Signing,
1106}
1107
1108impl From<&YubiHsm2UserMapping> for AuthType {
1109 fn from(value: &YubiHsm2UserMapping) -> Self {
1110 match value {
1111 YubiHsm2UserMapping::Admin { .. } => Self::Admin,
1112 YubiHsm2UserMapping::AuditLog { .. } => Self::AuditLog,
1113 YubiHsm2UserMapping::Backup { .. } => Self::Backup,
1114 YubiHsm2UserMapping::HermeticAuditLog { .. } => Self::HermeticAuditLog,
1115 YubiHsm2UserMapping::Signing { .. } => Self::Signing,
1116 }
1117 }
1118}
1119
1120#[cfg(test)]
1121mod tests {
1122 use std::thread::current;
1123
1124 use insta::{assert_snapshot, with_settings};
1125 use rstest::{fixture, rstest};
1126 use signstar_crypto::{
1127 key::{CryptographicKeyContext, KeyMechanism, KeyType, SignatureType, SigningKeySetup},
1128 openpgp::OpenPgpUserIdList,
1129 };
1130 use testresult::TestResult;
1131
1132 use super::*;
1133
1134 const SNAPSHOT_PATH: &str = "fixtures/yubihsm2_config/";
1135
1136 #[rstest]
1137 #[case::admin(YubiHsm2UserMapping::Admin{ authentication_key_id: "1".parse()? })]
1138 #[case::audit_log(
1139 YubiHsm2UserMapping::AuditLog {
1140 authentication_key_id: "1".parse()?,
1141 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host".parse()?,
1142 system_user: "metrics-user".parse()?,
1143 },
1144 )]
1145 #[case::backup(
1146 YubiHsm2UserMapping::Backup{
1147 authentication_key_id: "1".parse()?,
1148 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
1149 system_user: "backup-user".parse()?,
1150 wrapping_key_id: "1".parse()?,
1151 },
1152 )]
1153 #[case::hermetic_audit_log(
1154 YubiHsm2UserMapping::HermeticAuditLog {
1155 authentication_key_id: "1".parse()?,
1156 system_user: "metrics-user".parse()?,
1157 },
1158 )]
1159 #[case::signing(
1160 YubiHsm2UserMapping::Signing {
1161 authentication_key_id: "1".parse()?,
1162 signing_key_id: "1".parse()?,
1163 key_setup: SigningKeySetup::new(
1164 KeyType::Curve25519,
1165 vec![KeyMechanism::EdDsaSignature],
1166 None,
1167 SignatureType::EdDsa,
1168 CryptographicKeyContext::OpenPgp {
1169 user_ids: OpenPgpUserIdList::new(vec![
1170 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
1171 ])?,
1172 version: "v4".parse()?,
1173 notations: Default::default(),
1174 },
1175 )?,
1176 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
1177 system_user: "signing-user".parse()?,
1178 domain: Domain::One,
1179 }
1180 )]
1181 fn yubihsm2_user_mapping_backend_user_id(#[case] mapping: YubiHsm2UserMapping) -> TestResult {
1182 let id: Id = "1".parse()?;
1183 assert_eq!(mapping.backend_user_id(), id);
1184
1185 Ok(())
1186 }
1187
1188 #[rstest]
1190 #[case::admin(
1191 YubiHsm2UserMapping::Admin{ authentication_key_id: "1".parse()? },
1192 YubiHsm2UserMapping::CAP_ADMIN,
1193 )]
1194 #[case::audit_log(
1195 YubiHsm2UserMapping::AuditLog {
1196 authentication_key_id: "1".parse()?,
1197 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host".parse()?,
1198 system_user: "metrics-user".parse()?,
1199 },
1200 YubiHsm2UserMapping::CAP_AUDIT_LOG,
1201 )]
1202 #[case::backup(
1203 YubiHsm2UserMapping::Backup{
1204 authentication_key_id: "1".parse()?,
1205 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
1206 system_user: "backup-user".parse()?,
1207 wrapping_key_id: "1".parse()?,
1208 },
1209 YubiHsm2UserMapping::CAP_BACKUP,
1210 )]
1211 #[case::hermetic_audit_log(
1212 YubiHsm2UserMapping::HermeticAuditLog {
1213 authentication_key_id: "1".parse()?,
1214 system_user: "metrics-user".parse()?,
1215 },
1216 YubiHsm2UserMapping::CAP_HERMETIC_AUDIT_LOG,
1217 )]
1218 #[case::signing(
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 notations: Default::default(),
1233 },
1234 )?,
1235 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
1236 system_user: "signing-user".parse()?,
1237 domain: Domain::One,
1238 },
1239 YubiHsm2UserMapping::CAP_SIGNING,
1240 )]
1241 fn yubihsm2_user_mapping_capability(
1242 #[case] mapping: YubiHsm2UserMapping,
1243 #[case] expected: &[Capability],
1244 ) -> TestResult {
1245 let expected = Capabilities::from(expected);
1246 assert_eq!(mapping.capabilities(), expected);
1247
1248 Ok(())
1249 }
1250
1251 #[rstest]
1252 #[case::admin_filter_admin(
1253 YubiHsm2UserMapping::Admin{ authentication_key_id: "1".parse()? },
1254 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Admin },
1255 )]
1256 #[case::admin_filter_any(
1257 YubiHsm2UserMapping::Admin{ authentication_key_id: "1".parse()? },
1258 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Any },
1259 )]
1260 #[case::audit_log_filter_metrics(
1261 YubiHsm2UserMapping::AuditLog {
1262 authentication_key_id: "1".parse()?,
1263 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host".parse()?,
1264 system_user: "metrics-user".parse()?,
1265 },
1266 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Metrics },
1267 )]
1268 #[case::audit_log_filter_any(
1269 YubiHsm2UserMapping::AuditLog {
1270 authentication_key_id: "1".parse()?,
1271 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host".parse()?,
1272 system_user: "metrics-user".parse()?,
1273 },
1274 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Any },
1275 )]
1276 #[case::audit_log_filter_non_admin(
1277 YubiHsm2UserMapping::AuditLog {
1278 authentication_key_id: "1".parse()?,
1279 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host".parse()?,
1280 system_user: "metrics-user".parse()?,
1281 },
1282 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::NonAdmin },
1283 )]
1284 #[case::backup_filter_backup(
1285 YubiHsm2UserMapping::Backup{
1286 authentication_key_id: "1".parse()?,
1287 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
1288 system_user: "backup-user".parse()?,
1289 wrapping_key_id: "1".parse()?,
1290 },
1291 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Backup },
1292 )]
1293 #[case::backup_filter_any(
1294 YubiHsm2UserMapping::Backup{
1295 authentication_key_id: "1".parse()?,
1296 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
1297 system_user: "backup-user".parse()?,
1298 wrapping_key_id: "1".parse()?,
1299 },
1300 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Any },
1301 )]
1302 #[case::backup_filter_non_admin(
1303 YubiHsm2UserMapping::Backup{
1304 authentication_key_id: "1".parse()?,
1305 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
1306 system_user: "backup-user".parse()?,
1307 wrapping_key_id: "1".parse()?,
1308 },
1309 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::NonAdmin },
1310 )]
1311 #[case::hermetic_audit_log_filter_metrics(
1312 YubiHsm2UserMapping::HermeticAuditLog {
1313 authentication_key_id: "1".parse()?,
1314 system_user: "metrics-user".parse()?,
1315 },
1316 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Metrics },
1317 )]
1318 #[case::hermetic_audit_log_filter_any(
1319 YubiHsm2UserMapping::HermeticAuditLog {
1320 authentication_key_id: "1".parse()?,
1321 system_user: "metrics-user".parse()?,
1322 },
1323 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Any },
1324 )]
1325 #[case::hermetic_audit_log_filter_non_admin(
1326 YubiHsm2UserMapping::HermeticAuditLog {
1327 authentication_key_id: "1".parse()?,
1328 system_user: "metrics-user".parse()?,
1329 },
1330 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::NonAdmin },
1331 )]
1332 #[case::signing_filter_signing(
1333 YubiHsm2UserMapping::Signing {
1334 authentication_key_id: "1".parse()?,
1335 signing_key_id: "1".parse()?,
1336 key_setup: SigningKeySetup::new(
1337 KeyType::Curve25519,
1338 vec![KeyMechanism::EdDsaSignature],
1339 None,
1340 SignatureType::EdDsa,
1341 CryptographicKeyContext::OpenPgp {
1342 user_ids: OpenPgpUserIdList::new(vec![
1343 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
1344 ])?,
1345 version: "v4".parse()?,
1346 notations: Default::default(),
1347 },
1348 )?,
1349 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
1350 system_user: "signing-user".parse()?,
1351 domain: Domain::One,
1352 },
1353 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Signing },
1354 )]
1355 #[case::signing_filter_any(
1356 YubiHsm2UserMapping::Signing {
1357 authentication_key_id: "1".parse()?,
1358 signing_key_id: "1".parse()?,
1359 key_setup: SigningKeySetup::new(
1360 KeyType::Curve25519,
1361 vec![KeyMechanism::EdDsaSignature],
1362 None,
1363 SignatureType::EdDsa,
1364 CryptographicKeyContext::OpenPgp {
1365 user_ids: OpenPgpUserIdList::new(vec![
1366 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
1367 ])?,
1368 version: "v4".parse()?,
1369 notations: Default::default(),
1370 },
1371 )?,
1372 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
1373 system_user: "signing-user".parse()?,
1374 domain: Domain::One,
1375 },
1376 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Any },
1377 )]
1378 #[case::signing_filter_non_admin(
1379 YubiHsm2UserMapping::Signing {
1380 authentication_key_id: "1".parse()?,
1381 signing_key_id: "1".parse()?,
1382 key_setup: SigningKeySetup::new(
1383 KeyType::Curve25519,
1384 vec![KeyMechanism::EdDsaSignature],
1385 None,
1386 SignatureType::EdDsa,
1387 CryptographicKeyContext::OpenPgp {
1388 user_ids: OpenPgpUserIdList::new(vec![
1389 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
1390 ])?,
1391 version: "v4".parse()?,
1392 notations: Default::default(),
1393 },
1394 )?,
1395 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
1396 system_user: "signing-user".parse()?,
1397 domain: Domain::One,
1398 },
1399 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::NonAdmin },
1400 )]
1401 fn yubihsm2_user_mapping_backend_user_ids_filter_matches(
1402 #[case] mapping: YubiHsm2UserMapping,
1403 #[case] filter: BackendUserIdFilter,
1404 ) -> TestResult {
1405 assert_eq!(mapping.backend_user_ids(filter), vec!["1".to_string()]);
1406
1407 Ok(())
1408 }
1409
1410 #[rstest]
1411 #[case::admin_filter_non_admin(
1412 YubiHsm2UserMapping::Admin{ authentication_key_id: "1".parse()? },
1413 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::NonAdmin },
1414 )]
1415 #[case::admin_filter_backup(
1416 YubiHsm2UserMapping::Admin{ authentication_key_id: "1".parse()? },
1417 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Backup },
1418 )]
1419 #[case::admin_filter_metrics(
1420 YubiHsm2UserMapping::Admin{ authentication_key_id: "1".parse()? },
1421 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Metrics },
1422 )]
1423 #[case::admin_filter_observer(
1424 YubiHsm2UserMapping::Admin{ authentication_key_id: "1".parse()? },
1425 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Observer },
1426 )]
1427 #[case::admin_filter_signing(
1428 YubiHsm2UserMapping::Admin{ authentication_key_id: "1".parse()? },
1429 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Signing },
1430 )]
1431 #[case::audit_log_filter_admin(
1432 YubiHsm2UserMapping::AuditLog {
1433 authentication_key_id: "1".parse()?,
1434 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host".parse()?,
1435 system_user: "metrics-user".parse()?,
1436 },
1437 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Admin },
1438 )]
1439 #[case::audit_log_filter_backup(
1440 YubiHsm2UserMapping::AuditLog {
1441 authentication_key_id: "1".parse()?,
1442 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host".parse()?,
1443 system_user: "metrics-user".parse()?,
1444 },
1445 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Backup },
1446 )]
1447 #[case::audit_log_filter_observer(
1448 YubiHsm2UserMapping::AuditLog {
1449 authentication_key_id: "1".parse()?,
1450 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host".parse()?,
1451 system_user: "metrics-user".parse()?,
1452 },
1453 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Observer },
1454 )]
1455 #[case::audit_log_filter_signing(
1456 YubiHsm2UserMapping::AuditLog {
1457 authentication_key_id: "1".parse()?,
1458 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host".parse()?,
1459 system_user: "metrics-user".parse()?,
1460 },
1461 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Signing },
1462 )]
1463 #[case::backup_filter_admin(
1464 YubiHsm2UserMapping::Backup{
1465 authentication_key_id: "1".parse()?,
1466 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
1467 system_user: "backup-user".parse()?,
1468 wrapping_key_id: "1".parse()?,
1469 },
1470 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Admin },
1471 )]
1472 #[case::backup_filter_metrics(
1473 YubiHsm2UserMapping::Backup{
1474 authentication_key_id: "1".parse()?,
1475 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
1476 system_user: "backup-user".parse()?,
1477 wrapping_key_id: "1".parse()?,
1478 },
1479 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Metrics },
1480 )]
1481 #[case::backup_filter_observer(
1482 YubiHsm2UserMapping::Backup{
1483 authentication_key_id: "1".parse()?,
1484 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
1485 system_user: "backup-user".parse()?,
1486 wrapping_key_id: "1".parse()?,
1487 },
1488 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Observer },
1489 )]
1490 #[case::backup_filter_signing(
1491 YubiHsm2UserMapping::Backup{
1492 authentication_key_id: "1".parse()?,
1493 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
1494 system_user: "backup-user".parse()?,
1495 wrapping_key_id: "1".parse()?,
1496 },
1497 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Signing },
1498 )]
1499 #[case::hermetic_audit_log_filter_admin(
1500 YubiHsm2UserMapping::HermeticAuditLog {
1501 authentication_key_id: "1".parse()?,
1502 system_user: "metrics-user".parse()?,
1503 },
1504 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Admin },
1505 )]
1506 #[case::hermetic_audit_log_filter_backup(
1507 YubiHsm2UserMapping::HermeticAuditLog {
1508 authentication_key_id: "1".parse()?,
1509 system_user: "metrics-user".parse()?,
1510 },
1511 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Backup },
1512 )]
1513 #[case::hermetic_audit_log_filter_observer(
1514 YubiHsm2UserMapping::HermeticAuditLog {
1515 authentication_key_id: "1".parse()?,
1516 system_user: "metrics-user".parse()?,
1517 },
1518 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Observer },
1519 )]
1520 #[case::hermetic_audit_log_filter_signing(
1521 YubiHsm2UserMapping::HermeticAuditLog {
1522 authentication_key_id: "1".parse()?,
1523 system_user: "metrics-user".parse()?,
1524 },
1525 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Signing },
1526 )]
1527 #[case::signing_filter_admin(
1528 YubiHsm2UserMapping::Signing {
1529 authentication_key_id: "1".parse()?,
1530 signing_key_id: "1".parse()?,
1531 key_setup: SigningKeySetup::new(
1532 KeyType::Curve25519,
1533 vec![KeyMechanism::EdDsaSignature],
1534 None,
1535 SignatureType::EdDsa,
1536 CryptographicKeyContext::OpenPgp {
1537 user_ids: OpenPgpUserIdList::new(vec![
1538 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
1539 ])?,
1540 version: "v4".parse()?,
1541 notations: Default::default(),
1542 },
1543 )?,
1544 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
1545 system_user: "signing-user".parse()?,
1546 domain: Domain::One,
1547 },
1548 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Admin },
1549 )]
1550 #[case::signing_filter_backup(
1551 YubiHsm2UserMapping::Signing {
1552 authentication_key_id: "1".parse()?,
1553 signing_key_id: "1".parse()?,
1554 key_setup: SigningKeySetup::new(
1555 KeyType::Curve25519,
1556 vec![KeyMechanism::EdDsaSignature],
1557 None,
1558 SignatureType::EdDsa,
1559 CryptographicKeyContext::OpenPgp {
1560 user_ids: OpenPgpUserIdList::new(vec![
1561 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
1562 ])?,
1563 version: "v4".parse()?,
1564 notations: Default::default(),
1565 },
1566 )?,
1567 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
1568 system_user: "signing-user".parse()?,
1569 domain: Domain::One,
1570 },
1571 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Backup },
1572 )]
1573 #[case::signing_filter_metrics(
1574 YubiHsm2UserMapping::Signing {
1575 authentication_key_id: "1".parse()?,
1576 signing_key_id: "1".parse()?,
1577 key_setup: SigningKeySetup::new(
1578 KeyType::Curve25519,
1579 vec![KeyMechanism::EdDsaSignature],
1580 None,
1581 SignatureType::EdDsa,
1582 CryptographicKeyContext::OpenPgp {
1583 user_ids: OpenPgpUserIdList::new(vec![
1584 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
1585 ])?,
1586 version: "v4".parse()?,
1587 notations: Default::default(),
1588 },
1589 )?,
1590 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
1591 system_user: "signing-user".parse()?,
1592 domain: Domain::One,
1593 },
1594 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Metrics },
1595 )]
1596 #[case::signing_filter_observer(
1597 YubiHsm2UserMapping::Signing {
1598 authentication_key_id: "1".parse()?,
1599 signing_key_id: "1".parse()?,
1600 key_setup: SigningKeySetup::new(
1601 KeyType::Curve25519,
1602 vec![KeyMechanism::EdDsaSignature],
1603 None,
1604 SignatureType::EdDsa,
1605 CryptographicKeyContext::OpenPgp {
1606 user_ids: OpenPgpUserIdList::new(vec![
1607 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
1608 ])?,
1609 version: "v4".parse()?,
1610 notations: Default::default(),
1611 },
1612 )?,
1613 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
1614 system_user: "signing-user".parse()?,
1615 domain: Domain::One,
1616 },
1617 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Observer },
1618 )]
1619 fn yubihsm2_user_mapping_backend_user_ids_filter_mismatches(
1620 #[case] mapping: YubiHsm2UserMapping,
1621 #[case] filter: BackendUserIdFilter,
1622 ) -> TestResult {
1623 assert!(mapping.backend_user_ids(filter).is_empty());
1624
1625 Ok(())
1626 }
1627
1628 #[test]
1629 fn yubihsm2_user_mapping_backend_user_with_passphrase_succeeds() -> TestResult {
1630 let mapping = YubiHsm2UserMapping::Admin {
1631 authentication_key_id: "1".parse()?,
1632 };
1633 let passphrase = Passphrase::generate(None);
1634 let creds = mapping.backend_user_with_passphrase("1", passphrase.clone())?;
1635
1636 assert_eq!(creds.user(), "1");
1637 assert_eq!(
1638 creds.passphrase().expose_borrowed(),
1639 passphrase.expose_borrowed()
1640 );
1641
1642 Ok(())
1643 }
1644
1645 #[test]
1646 fn yubihsm2_user_mapping_backend_user_with_passphrase_fails() -> TestResult {
1647 let mapping = YubiHsm2UserMapping::Admin {
1648 authentication_key_id: "1".parse()?,
1649 };
1650 assert!(
1651 mapping
1652 .backend_user_with_passphrase("2", Passphrase::generate(None))
1653 .is_err()
1654 );
1655
1656 Ok(())
1657 }
1658
1659 #[rstest]
1660 #[case::admin_filter_admin(
1661 YubiHsm2UserMapping::Admin{ authentication_key_id: "1".parse()? },
1662 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Admin },
1663 )]
1664 #[case::admin_filter_any(
1665 YubiHsm2UserMapping::Admin{ authentication_key_id: "1".parse()? },
1666 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Any },
1667 )]
1668 #[case::audit_log_filter_metrics(
1669 YubiHsm2UserMapping::AuditLog {
1670 authentication_key_id: "1".parse()?,
1671 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host".parse()?,
1672 system_user: "metrics-user".parse()?,
1673 },
1674 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Metrics },
1675 )]
1676 #[case::audit_log_filter_any(
1677 YubiHsm2UserMapping::AuditLog {
1678 authentication_key_id: "1".parse()?,
1679 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host".parse()?,
1680 system_user: "metrics-user".parse()?,
1681 },
1682 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Any },
1683 )]
1684 #[case::audit_log_filter_non_admin(
1685 YubiHsm2UserMapping::AuditLog {
1686 authentication_key_id: "1".parse()?,
1687 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host".parse()?,
1688 system_user: "metrics-user".parse()?,
1689 },
1690 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::NonAdmin },
1691 )]
1692 #[case::backup_filter_backup(
1693 YubiHsm2UserMapping::Backup{
1694 authentication_key_id: "1".parse()?,
1695 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
1696 system_user: "backup-user".parse()?,
1697 wrapping_key_id: "1".parse()?,
1698 },
1699 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Backup },
1700 )]
1701 #[case::backup_filter_any(
1702 YubiHsm2UserMapping::Backup{
1703 authentication_key_id: "1".parse()?,
1704 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
1705 system_user: "backup-user".parse()?,
1706 wrapping_key_id: "1".parse()?,
1707 },
1708 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Any },
1709 )]
1710 #[case::backup_filter_non_admin(
1711 YubiHsm2UserMapping::Backup{
1712 authentication_key_id: "1".parse()?,
1713 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
1714 system_user: "backup-user".parse()?,
1715 wrapping_key_id: "1".parse()?,
1716 },
1717 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::NonAdmin },
1718 )]
1719 #[case::hermetic_audit_log_filter_metrics(
1720 YubiHsm2UserMapping::HermeticAuditLog {
1721 authentication_key_id: "1".parse()?,
1722 system_user: "metrics-user".parse()?,
1723 },
1724 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Metrics },
1725 )]
1726 #[case::hermetic_audit_log_filter_any(
1727 YubiHsm2UserMapping::HermeticAuditLog {
1728 authentication_key_id: "1".parse()?,
1729 system_user: "metrics-user".parse()?,
1730 },
1731 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Any },
1732 )]
1733 #[case::hermetic_audit_log_filter_non_admin(
1734 YubiHsm2UserMapping::HermeticAuditLog {
1735 authentication_key_id: "1".parse()?,
1736 system_user: "metrics-user".parse()?,
1737 },
1738 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::NonAdmin },
1739 )]
1740 #[case::signing_filter_signing(
1741 YubiHsm2UserMapping::Signing {
1742 authentication_key_id: "1".parse()?,
1743 signing_key_id: "1".parse()?,
1744 key_setup: SigningKeySetup::new(
1745 KeyType::Curve25519,
1746 vec![KeyMechanism::EdDsaSignature],
1747 None,
1748 SignatureType::EdDsa,
1749 CryptographicKeyContext::OpenPgp {
1750 user_ids: OpenPgpUserIdList::new(vec![
1751 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
1752 ])?,
1753 version: "v4".parse()?,
1754 notations: Default::default(),
1755 },
1756 )?,
1757 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
1758 system_user: "signing-user".parse()?,
1759 domain: Domain::One,
1760 },
1761 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Signing },
1762 )]
1763 #[case::signing_filter_any(
1764 YubiHsm2UserMapping::Signing {
1765 authentication_key_id: "1".parse()?,
1766 signing_key_id: "1".parse()?,
1767 key_setup: SigningKeySetup::new(
1768 KeyType::Curve25519,
1769 vec![KeyMechanism::EdDsaSignature],
1770 None,
1771 SignatureType::EdDsa,
1772 CryptographicKeyContext::OpenPgp {
1773 user_ids: OpenPgpUserIdList::new(vec![
1774 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
1775 ])?,
1776 version: "v4".parse()?,
1777 notations: Default::default(),
1778 },
1779 )?,
1780 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
1781 system_user: "signing-user".parse()?,
1782 domain: Domain::One,
1783 },
1784 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Any },
1785 )]
1786 #[case::signing_filter_non_admin(
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 notations: Default::default(),
1801 },
1802 )?,
1803 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
1804 system_user: "signing-user".parse()?,
1805 domain: Domain::One,
1806 },
1807 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::NonAdmin },
1808 )]
1809 fn yubihsm2_user_mapping_backend_users_with_new_passphrase_filter_matches(
1810 #[case] mapping: YubiHsm2UserMapping,
1811 #[case] filter: BackendUserIdFilter,
1812 ) -> TestResult {
1813 let creds = mapping.backend_users_with_new_passphrase(filter);
1814 assert!(creds.first().is_some_and(|creds| creds.user() == "1"));
1815
1816 Ok(())
1817 }
1818
1819 #[rstest]
1820 #[case::admin_filter_non_admin(
1821 YubiHsm2UserMapping::Admin{ authentication_key_id: "1".parse()? },
1822 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::NonAdmin },
1823 )]
1824 #[case::admin_filter_backup(
1825 YubiHsm2UserMapping::Admin{ authentication_key_id: "1".parse()? },
1826 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Backup },
1827 )]
1828 #[case::admin_filter_metrics(
1829 YubiHsm2UserMapping::Admin{ authentication_key_id: "1".parse()? },
1830 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Metrics },
1831 )]
1832 #[case::admin_filter_observer(
1833 YubiHsm2UserMapping::Admin{ authentication_key_id: "1".parse()? },
1834 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Observer },
1835 )]
1836 #[case::admin_filter_signing(
1837 YubiHsm2UserMapping::Admin{ authentication_key_id: "1".parse()? },
1838 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Signing },
1839 )]
1840 #[case::audit_log_filter_admin(
1841 YubiHsm2UserMapping::AuditLog {
1842 authentication_key_id: "1".parse()?,
1843 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host".parse()?,
1844 system_user: "metrics-user".parse()?,
1845 },
1846 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Admin },
1847 )]
1848 #[case::audit_log_filter_backup(
1849 YubiHsm2UserMapping::AuditLog {
1850 authentication_key_id: "1".parse()?,
1851 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host".parse()?,
1852 system_user: "metrics-user".parse()?,
1853 },
1854 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Backup },
1855 )]
1856 #[case::audit_log_filter_observer(
1857 YubiHsm2UserMapping::AuditLog {
1858 authentication_key_id: "1".parse()?,
1859 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host".parse()?,
1860 system_user: "metrics-user".parse()?,
1861 },
1862 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Observer },
1863 )]
1864 #[case::audit_log_filter_signing(
1865 YubiHsm2UserMapping::AuditLog {
1866 authentication_key_id: "1".parse()?,
1867 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host".parse()?,
1868 system_user: "metrics-user".parse()?,
1869 },
1870 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Signing },
1871 )]
1872 #[case::backup_filter_admin(
1873 YubiHsm2UserMapping::Backup{
1874 authentication_key_id: "1".parse()?,
1875 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
1876 system_user: "backup-user".parse()?,
1877 wrapping_key_id: "1".parse()?,
1878 },
1879 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Admin },
1880 )]
1881 #[case::backup_filter_metrics(
1882 YubiHsm2UserMapping::Backup{
1883 authentication_key_id: "1".parse()?,
1884 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
1885 system_user: "backup-user".parse()?,
1886 wrapping_key_id: "1".parse()?,
1887 },
1888 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Metrics },
1889 )]
1890 #[case::backup_filter_observer(
1891 YubiHsm2UserMapping::Backup{
1892 authentication_key_id: "1".parse()?,
1893 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
1894 system_user: "backup-user".parse()?,
1895 wrapping_key_id: "1".parse()?,
1896 },
1897 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Observer },
1898 )]
1899 #[case::backup_filter_signing(
1900 YubiHsm2UserMapping::Backup{
1901 authentication_key_id: "1".parse()?,
1902 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
1903 system_user: "backup-user".parse()?,
1904 wrapping_key_id: "1".parse()?,
1905 },
1906 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Signing },
1907 )]
1908 #[case::hermetic_audit_log_filter_admin(
1909 YubiHsm2UserMapping::HermeticAuditLog {
1910 authentication_key_id: "1".parse()?,
1911 system_user: "metrics-user".parse()?,
1912 },
1913 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Admin },
1914 )]
1915 #[case::hermetic_audit_log_filter_backup(
1916 YubiHsm2UserMapping::HermeticAuditLog {
1917 authentication_key_id: "1".parse()?,
1918 system_user: "metrics-user".parse()?,
1919 },
1920 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Backup },
1921 )]
1922 #[case::hermetic_audit_log_filter_observer(
1923 YubiHsm2UserMapping::HermeticAuditLog {
1924 authentication_key_id: "1".parse()?,
1925 system_user: "metrics-user".parse()?,
1926 },
1927 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Observer },
1928 )]
1929 #[case::hermetic_audit_log_filter_signing(
1930 YubiHsm2UserMapping::HermeticAuditLog {
1931 authentication_key_id: "1".parse()?,
1932 system_user: "metrics-user".parse()?,
1933 },
1934 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Signing },
1935 )]
1936 #[case::signing_filter_admin(
1937 YubiHsm2UserMapping::Signing {
1938 authentication_key_id: "1".parse()?,
1939 signing_key_id: "1".parse()?,
1940 key_setup: SigningKeySetup::new(
1941 KeyType::Curve25519,
1942 vec![KeyMechanism::EdDsaSignature],
1943 None,
1944 SignatureType::EdDsa,
1945 CryptographicKeyContext::OpenPgp {
1946 user_ids: OpenPgpUserIdList::new(vec![
1947 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
1948 ])?,
1949 version: "v4".parse()?,
1950 notations: Default::default(),
1951 },
1952 )?,
1953 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
1954 system_user: "signing-user".parse()?,
1955 domain: Domain::One,
1956 },
1957 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Admin },
1958 )]
1959 #[case::signing_filter_backup(
1960 YubiHsm2UserMapping::Signing {
1961 authentication_key_id: "1".parse()?,
1962 signing_key_id: "1".parse()?,
1963 key_setup: SigningKeySetup::new(
1964 KeyType::Curve25519,
1965 vec![KeyMechanism::EdDsaSignature],
1966 None,
1967 SignatureType::EdDsa,
1968 CryptographicKeyContext::OpenPgp {
1969 user_ids: OpenPgpUserIdList::new(vec![
1970 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
1971 ])?,
1972 version: "v4".parse()?,
1973 notations: Default::default(),
1974 },
1975 )?,
1976 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
1977 system_user: "signing-user".parse()?,
1978 domain: Domain::One,
1979 },
1980 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Backup },
1981 )]
1982 #[case::signing_filter_metrics(
1983 YubiHsm2UserMapping::Signing {
1984 authentication_key_id: "1".parse()?,
1985 signing_key_id: "1".parse()?,
1986 key_setup: SigningKeySetup::new(
1987 KeyType::Curve25519,
1988 vec![KeyMechanism::EdDsaSignature],
1989 None,
1990 SignatureType::EdDsa,
1991 CryptographicKeyContext::OpenPgp {
1992 user_ids: OpenPgpUserIdList::new(vec![
1993 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
1994 ])?,
1995 version: "v4".parse()?,
1996 notations: Default::default(),
1997 },
1998 )?,
1999 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
2000 system_user: "signing-user".parse()?,
2001 domain: Domain::One,
2002 },
2003 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Metrics },
2004 )]
2005 #[case::signing_filter_observer(
2006 YubiHsm2UserMapping::Signing {
2007 authentication_key_id: "1".parse()?,
2008 signing_key_id: "1".parse()?,
2009 key_setup: SigningKeySetup::new(
2010 KeyType::Curve25519,
2011 vec![KeyMechanism::EdDsaSignature],
2012 None,
2013 SignatureType::EdDsa,
2014 CryptographicKeyContext::OpenPgp {
2015 user_ids: OpenPgpUserIdList::new(vec![
2016 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
2017 ])?,
2018 version: "v4".parse()?,
2019 notations: Default::default(),
2020 },
2021 )?,
2022 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
2023 system_user: "signing-user".parse()?,
2024 domain: Domain::One,
2025 },
2026 BackendUserIdFilter{ backend_user_id_kind: BackendUserIdKind::Observer },
2027 )]
2028 fn yubihsm2_user_mapping_backend_users_with_new_passphrase_filter_mismatches(
2029 #[case] mapping: YubiHsm2UserMapping,
2030 #[case] filter: BackendUserIdFilter,
2031 ) -> TestResult {
2032 assert!(mapping.backend_users_with_new_passphrase(filter).is_empty());
2033
2034 Ok(())
2035 }
2036
2037 #[rstest]
2038 #[case::backup_filter_wrapping_no_domain(
2039 YubiHsm2UserMapping::Backup{
2040 authentication_key_id: "1".parse()?,
2041 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
2042 system_user: "backup-user".parse()?,
2043 wrapping_key_id: "1".parse()?,
2044 },
2045 YubiHsm2BackendKeyIdFilter{ key_type: KeyObjectType::Wrapping, key_domain: None },
2046 )]
2047 #[case::backup_filter_wrapping_some_domain(
2048 YubiHsm2UserMapping::Backup{
2049 authentication_key_id: "1".parse()?,
2050 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
2051 system_user: "backup-user".parse()?,
2052 wrapping_key_id: "1".parse()?,
2053 },
2054 YubiHsm2BackendKeyIdFilter{ key_type: KeyObjectType::Wrapping, key_domain: Some(Domain::One) },
2055 )]
2056 #[case::signing_filter_signing_matching_domain(
2057 YubiHsm2UserMapping::Signing {
2058 authentication_key_id: "1".parse()?,
2059 signing_key_id: "1".parse()?,
2060 key_setup: SigningKeySetup::new(
2061 KeyType::Curve25519,
2062 vec![KeyMechanism::EdDsaSignature],
2063 None,
2064 SignatureType::EdDsa,
2065 CryptographicKeyContext::OpenPgp {
2066 user_ids: OpenPgpUserIdList::new(vec![
2067 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
2068 ])?,
2069 version: "v4".parse()?,
2070 notations: Default::default(),
2071 },
2072 )?,
2073 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
2074 system_user: "signing-user".parse()?,
2075 domain: Domain::One,
2076 },
2077 YubiHsm2BackendKeyIdFilter{ key_type: KeyObjectType::Signing, key_domain: Some(Domain::One) },
2078 )]
2079 fn yubihsm2_user_mapping_backend_key_id_filter_matches(
2080 #[case] mapping: YubiHsm2UserMapping,
2081 #[case] filter: YubiHsm2BackendKeyIdFilter,
2082 ) -> TestResult {
2083 assert!(mapping.backend_key_id(&filter).is_some_and(|id| id == "1"));
2084
2085 Ok(())
2086 }
2087
2088 #[rstest]
2089 #[case::backup_filter_signing_no_domain(
2090 YubiHsm2UserMapping::Backup{
2091 authentication_key_id: "1".parse()?,
2092 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
2093 system_user: "backup-user".parse()?,
2094 wrapping_key_id: "1".parse()?,
2095 },
2096 YubiHsm2BackendKeyIdFilter{ key_type: KeyObjectType::Signing, key_domain: None },
2097 )]
2098 #[case::backup_filter_signing_some_domain(
2099 YubiHsm2UserMapping::Backup{
2100 authentication_key_id: "1".parse()?,
2101 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
2102 system_user: "backup-user".parse()?,
2103 wrapping_key_id: "1".parse()?,
2104 },
2105 YubiHsm2BackendKeyIdFilter{ key_type: KeyObjectType::Signing, key_domain: Some(Domain::One) },
2106 )]
2107 #[case::signing_filter_signing_wrong_domain(
2108 YubiHsm2UserMapping::Signing {
2109 authentication_key_id: "1".parse()?,
2110 signing_key_id: "1".parse()?,
2111 key_setup: SigningKeySetup::new(
2112 KeyType::Curve25519,
2113 vec![KeyMechanism::EdDsaSignature],
2114 None,
2115 SignatureType::EdDsa,
2116 CryptographicKeyContext::OpenPgp {
2117 user_ids: OpenPgpUserIdList::new(vec![
2118 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
2119 ])?,
2120 version: "v4".parse()?,
2121 notations: Default::default(),
2122 },
2123 )?,
2124 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
2125 system_user: "signing-user".parse()?,
2126 domain: Domain::One,
2127 },
2128 YubiHsm2BackendKeyIdFilter{ key_type: KeyObjectType::Signing, key_domain: Some(Domain::Two) },
2129 )]
2130 #[case::signing_filter_wrapping_same_domain(
2131 YubiHsm2UserMapping::Signing {
2132 authentication_key_id: "1".parse()?,
2133 signing_key_id: "1".parse()?,
2134 key_setup: SigningKeySetup::new(
2135 KeyType::Curve25519,
2136 vec![KeyMechanism::EdDsaSignature],
2137 None,
2138 SignatureType::EdDsa,
2139 CryptographicKeyContext::OpenPgp {
2140 user_ids: OpenPgpUserIdList::new(vec![
2141 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
2142 ])?,
2143 version: "v4".parse()?,
2144 notations: Default::default(),
2145 },
2146 )?,
2147 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
2148 system_user: "signing-user".parse()?,
2149 domain: Domain::One,
2150 },
2151 YubiHsm2BackendKeyIdFilter{ key_type: KeyObjectType::Wrapping, key_domain: Some(Domain::One) },
2152 )]
2153 #[case::signing_filter_wrapping_wrong_domain(
2154 YubiHsm2UserMapping::Signing {
2155 authentication_key_id: "1".parse()?,
2156 signing_key_id: "1".parse()?,
2157 key_setup: SigningKeySetup::new(
2158 KeyType::Curve25519,
2159 vec![KeyMechanism::EdDsaSignature],
2160 None,
2161 SignatureType::EdDsa,
2162 CryptographicKeyContext::OpenPgp {
2163 user_ids: OpenPgpUserIdList::new(vec![
2164 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
2165 ])?,
2166 version: "v4".parse()?,
2167 notations: Default::default(),
2168 },
2169 )?,
2170 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
2171 system_user: "signing-user".parse()?,
2172 domain: Domain::One,
2173 },
2174 YubiHsm2BackendKeyIdFilter{ key_type: KeyObjectType::Wrapping, key_domain: Some(Domain::Two) },
2175 )]
2176 #[case::signing_filter_wrapping_no_domain(
2177 YubiHsm2UserMapping::Signing {
2178 authentication_key_id: "1".parse()?,
2179 signing_key_id: "1".parse()?,
2180 key_setup: SigningKeySetup::new(
2181 KeyType::Curve25519,
2182 vec![KeyMechanism::EdDsaSignature],
2183 None,
2184 SignatureType::EdDsa,
2185 CryptographicKeyContext::OpenPgp {
2186 user_ids: OpenPgpUserIdList::new(vec![
2187 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
2188 ])?,
2189 version: "v4".parse()?,
2190 notations: Default::default(),
2191 },
2192 )?,
2193 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
2194 system_user: "signing-user".parse()?,
2195 domain: Domain::One,
2196 },
2197 YubiHsm2BackendKeyIdFilter{ key_type: KeyObjectType::Wrapping, key_domain: None },
2198 )]
2199 fn yubihsm2_user_mapping_backend_key_id_filter_mismatches(
2200 #[case] mapping: YubiHsm2UserMapping,
2201 #[case] filter: YubiHsm2BackendKeyIdFilter,
2202 ) -> TestResult {
2203 assert!(mapping.backend_key_id(&filter).is_none());
2204
2205 Ok(())
2206 }
2207
2208 #[fixture]
2209 fn yubihsm2_yubihsm_connections() -> TestResult<[Connection; 2]> {
2210 Ok([
2211 Connection::Usb {
2212 serial_number: "0012345678".parse()?,
2213 },
2214 Connection::Usb {
2215 serial_number: "0087654321".parse()?,
2216 },
2217 ])
2218 }
2219
2220 #[fixture]
2221 fn yubihsm2_mappings() -> TestResult<[YubiHsm2UserMapping; 5]> {
2222 Ok([
2223 YubiHsm2UserMapping::Admin { authentication_key_id: "1".parse()? },
2224 YubiHsm2UserMapping::Backup{
2225 authentication_key_id: "2".parse()?,
2226 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
2227 system_user: "backup-user".parse()?,
2228 wrapping_key_id: "1".parse()?,
2229 },
2230 YubiHsm2UserMapping::AuditLog {
2231 authentication_key_id: "3".parse()?,
2232 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host".parse()?,
2233 system_user: "metrics-user".parse()?,
2234 },
2235 YubiHsm2UserMapping::HermeticAuditLog {
2236 authentication_key_id: "4".parse()?,
2237 system_user: "hermetic-metrics".parse()?,
2238 },
2239 YubiHsm2UserMapping::Signing {
2240 authentication_key_id: "5".parse()?,
2241 signing_key_id: "1".parse()?,
2242 key_setup: SigningKeySetup::new(
2243 KeyType::Curve25519,
2244 vec![KeyMechanism::EdDsaSignature],
2245 None,
2246 SignatureType::EdDsa,
2247 CryptographicKeyContext::OpenPgp {
2248 user_ids: OpenPgpUserIdList::new(vec![
2249 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
2250 ])?,
2251 version: "v4".parse()?,
2252 notations: Default::default(),
2253 },
2254 )?,
2255 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
2256 system_user: "signing-user".parse()?,
2257 domain: Domain::One,
2258 }
2259 ])
2260 }
2261
2262 #[fixture]
2263 fn yubihsm2_config(
2264 yubihsm2_yubihsm_connections: TestResult<[Connection; 2]>,
2265 yubihsm2_mappings: TestResult<[YubiHsm2UserMapping; 5]>,
2266 ) -> TestResult<YubiHsm2Config> {
2267 let yubihsm2_yubihsm_connections = yubihsm2_yubihsm_connections?;
2268 let yubihsm2_mappings = yubihsm2_mappings?;
2269 let config = YubiHsm2Config::new(
2270 BTreeSet::from_iter(yubihsm2_yubihsm_connections),
2271 BTreeSet::from_iter(yubihsm2_mappings),
2272 )?;
2273
2274 Ok(config)
2275 }
2276
2277 #[rstest]
2278 fn yubihsm2_config_connections(
2279 yubihsm2_yubihsm_connections: TestResult<[Connection; 2]>,
2280 yubihsm2_config: TestResult<YubiHsm2Config>,
2281 ) -> TestResult {
2282 let yubihsm2_config = yubihsm2_config?;
2283 let yubihsm2_yubihsm_connections = yubihsm2_yubihsm_connections?;
2284 let connections = yubihsm2_config.connections();
2285
2286 assert_eq!(connections.len(), 2);
2287 assert!(
2288 connections
2289 .first()
2290 .is_some_and(|connection| connection == &yubihsm2_yubihsm_connections[0]),
2291 );
2292 assert!(
2293 connections
2294 .last()
2295 .is_some_and(|connection| connection == &yubihsm2_yubihsm_connections[1]),
2296 );
2297
2298 Ok(())
2299 }
2300
2301 #[rstest]
2302 fn yubihsm2_config_mappings(
2303 yubihsm2_mappings: TestResult<[YubiHsm2UserMapping; 5]>,
2304 yubihsm2_config: TestResult<YubiHsm2Config>,
2305 ) -> TestResult {
2306 let yubihsm2_config = yubihsm2_config?;
2307 let yubihsm2_mappings = yubihsm2_mappings?;
2308 let mappings = yubihsm2_config.mappings();
2309
2310 assert_eq!(mappings.len(), 5);
2311 for mapping in yubihsm2_mappings.iter() {
2312 assert!(mappings.contains(mapping));
2313 }
2314
2315 Ok(())
2316 }
2317
2318 #[rstest]
2319 fn yubihsm2_config_authorized_key_entries(
2320 yubihsm2_mappings: TestResult<[YubiHsm2UserMapping; 5]>,
2321 yubihsm2_config: TestResult<YubiHsm2Config>,
2322 ) -> TestResult {
2323 let yubihsm2_config = yubihsm2_config?;
2324 let authorized_key_entries = yubihsm2_config.authorized_key_entries();
2325
2326 let yubihsm2_mappings = yubihsm2_mappings?;
2327 let initial_entries = yubihsm2_mappings
2328 .iter()
2329 .filter_map(|mapping| mapping.authorized_key_entry())
2330 .collect::<HashSet<_>>();
2331
2332 assert_eq!(initial_entries, authorized_key_entries);
2333
2334 Ok(())
2335 }
2336
2337 #[rstest]
2338 fn yubihsm2_config_system_user_ids(
2339 yubihsm2_mappings: TestResult<[YubiHsm2UserMapping; 5]>,
2340 yubihsm2_config: TestResult<YubiHsm2Config>,
2341 ) -> TestResult {
2342 let yubihsm2_config = yubihsm2_config?;
2343 let system_user_ids = yubihsm2_config.system_user_ids();
2344
2345 let yubihsm2_mappings = yubihsm2_mappings?;
2346 let initial_entries = yubihsm2_mappings
2347 .iter()
2348 .filter_map(|mapping| mapping.system_user_id())
2349 .collect::<HashSet<_>>();
2350
2351 assert_eq!(initial_entries, system_user_ids);
2352
2353 Ok(())
2354 }
2355
2356 #[rstest]
2357 #[case::no_connection(
2358 "Error message for YubiHsm2Config::new with no connection",
2359 BTreeSet::new(),
2360 BTreeSet::from_iter([
2361 YubiHsm2UserMapping::Admin { authentication_key_id: "1".parse()? },
2362 YubiHsm2UserMapping::Backup{
2363 authentication_key_id: "2".parse()?,
2364 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
2365 system_user: "backup-user".parse()?,
2366 wrapping_key_id: "1".parse()?,
2367 },
2368 YubiHsm2UserMapping::AuditLog {
2369 authentication_key_id: "3".parse()?,
2370 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host".parse()?,
2371 system_user: "metrics-user".parse()?,
2372 },
2373 YubiHsm2UserMapping::Signing {
2374 authentication_key_id: "4".parse()?,
2375 signing_key_id: "1".parse()?,
2376 key_setup: SigningKeySetup::new(
2377 KeyType::Curve25519,
2378 vec![KeyMechanism::EdDsaSignature],
2379 None,
2380 SignatureType::EdDsa,
2381 CryptographicKeyContext::OpenPgp {
2382 user_ids: OpenPgpUserIdList::new(vec![
2383 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
2384 ])?,
2385 version: "v4".parse()?,
2386 notations: Default::default(),
2387 },
2388 )?,
2389 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
2390 system_user: "signing-user".parse()?,
2391 domain: Domain::One,
2392 }
2393 ]),
2394 )]
2395 #[case::no_mappings(
2396 "Error message for YubiHsm2Config::new with no user mappings",
2397 BTreeSet::from_iter([
2398 Connection::Usb {serial_number: "0012345678".parse()? },
2399 Connection::Usb {serial_number: "0087654321".parse()? },
2400 ]),
2401 BTreeSet::new(),
2402 )]
2403 #[case::duplicate_system_user_ids(
2404 "Error message for YubiHsm2Config::new with two duplicate system user IDs",
2405 BTreeSet::from_iter([
2406 Connection::Usb {serial_number: "0012345678".parse()? },
2407 Connection::Usb {serial_number: "0087654321".parse()? },
2408 ]),
2409 BTreeSet::from_iter([
2410 YubiHsm2UserMapping::Admin { authentication_key_id: "1".parse()? },
2411 YubiHsm2UserMapping::Backup{
2412 authentication_key_id: "2".parse()?,
2413 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
2414 system_user: "backup-user".parse()?,
2415 wrapping_key_id: "1".parse()?,
2416 },
2417 YubiHsm2UserMapping::AuditLog {
2418 authentication_key_id: "3".parse()?,
2419 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host".parse()?,
2420 system_user: "backup-user".parse()?,
2421 },
2422 YubiHsm2UserMapping::Signing {
2423 authentication_key_id: "4".parse()?,
2424 signing_key_id: "1".parse()?,
2425 key_setup: SigningKeySetup::new(
2426 KeyType::Curve25519,
2427 vec![KeyMechanism::EdDsaSignature],
2428 None,
2429 SignatureType::EdDsa,
2430 CryptographicKeyContext::OpenPgp {
2431 user_ids: OpenPgpUserIdList::new(vec![
2432 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
2433 ])?,
2434 version: "v4".parse()?,
2435 notations: Default::default(),
2436 },
2437 )?,
2438 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
2439 system_user: "signing-user".parse()?,
2440 domain: Domain::One,
2441 }
2442 ]),
2443 )]
2444 #[case::duplicate_ssh_public_keys(
2445 "Error message for YubiHsm2Config::new with two duplicate SSH public keys as authorized keys",
2446 BTreeSet::from_iter([
2447 Connection::Usb {serial_number: "0012345678".parse()? },
2448 Connection::Usb {serial_number: "0087654321".parse()? },
2449 ]),
2450 BTreeSet::from_iter([
2451 YubiHsm2UserMapping::Admin { authentication_key_id: "1".parse()? },
2452 YubiHsm2UserMapping::Backup{
2453 authentication_key_id: "2".parse()?,
2454 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
2455 system_user: "backup-user".parse()?,
2456 wrapping_key_id: "1".parse()?,
2457 },
2458 YubiHsm2UserMapping::AuditLog {
2459 authentication_key_id: "3".parse()?,
2460 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
2461 system_user: "metrics-user".parse()?,
2462 },
2463 YubiHsm2UserMapping::Signing {
2464 authentication_key_id: "4".parse()?,
2465 signing_key_id: "1".parse()?,
2466 key_setup: SigningKeySetup::new(
2467 KeyType::Curve25519,
2468 vec![KeyMechanism::EdDsaSignature],
2469 None,
2470 SignatureType::EdDsa,
2471 CryptographicKeyContext::OpenPgp {
2472 user_ids: OpenPgpUserIdList::new(vec![
2473 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
2474 ])?,
2475 version: "v4".parse()?,
2476 notations: Default::default(),
2477 },
2478 )?,
2479 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
2480 system_user: "signing-user".parse()?,
2481 domain: Domain::One,
2482 }
2483 ]),
2484 )]
2485 #[case::no_administrator(
2486 "Error message for YubiHsm2Config::new with no administrator",
2487 BTreeSet::from_iter([
2488 Connection::Usb {serial_number: "0012345678".parse()? },
2489 Connection::Usb {serial_number: "0087654321".parse()? },
2490 ]),
2491 BTreeSet::from_iter([
2492 YubiHsm2UserMapping::Backup{
2493 authentication_key_id: "2".parse()?,
2494 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
2495 system_user: "backup-user".parse()?,
2496 wrapping_key_id: "1".parse()?,
2497 },
2498 YubiHsm2UserMapping::AuditLog {
2499 authentication_key_id: "3".parse()?,
2500 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host".parse()?,
2501 system_user: "metrics-user".parse()?,
2502 },
2503 YubiHsm2UserMapping::Signing {
2504 authentication_key_id: "4".parse()?,
2505 signing_key_id: "1".parse()?,
2506 key_setup: SigningKeySetup::new(
2507 KeyType::Curve25519,
2508 vec![KeyMechanism::EdDsaSignature],
2509 None,
2510 SignatureType::EdDsa,
2511 CryptographicKeyContext::OpenPgp {
2512 user_ids: OpenPgpUserIdList::new(vec![
2513 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
2514 ])?,
2515 version: "v4".parse()?,
2516 notations: Default::default(),
2517 },
2518 )?,
2519 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
2520 system_user: "signing-user".parse()?,
2521 domain: Domain::One,
2522 }
2523 ]),
2524 )]
2525 #[case::duplicate_backend_user_ids(
2526 "Error message for YubiHsm2Config::new with two duplicate backend user IDs",
2527 BTreeSet::from_iter([
2528 Connection::Usb {serial_number: "0012345678".parse()? },
2529 Connection::Usb {serial_number: "0087654321".parse()? },
2530 ]),
2531 BTreeSet::from_iter([
2532 YubiHsm2UserMapping::Admin { authentication_key_id: "1".parse()? },
2533 YubiHsm2UserMapping::Backup{
2534 authentication_key_id: "2".parse()?,
2535 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
2536 system_user: "backup-user".parse()?,
2537 wrapping_key_id: "1".parse()?,
2538 },
2539 YubiHsm2UserMapping::AuditLog {
2540 authentication_key_id: "3".parse()?,
2541 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host".parse()?,
2542 system_user: "metrics-user".parse()?,
2543 },
2544 YubiHsm2UserMapping::Signing {
2545 authentication_key_id: "3".parse()?,
2546 signing_key_id: "1".parse()?,
2547 key_setup: SigningKeySetup::new(
2548 KeyType::Curve25519,
2549 vec![KeyMechanism::EdDsaSignature],
2550 None,
2551 SignatureType::EdDsa,
2552 CryptographicKeyContext::OpenPgp {
2553 user_ids: OpenPgpUserIdList::new(vec![
2554 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
2555 ])?,
2556 version: "v4".parse()?,
2557 notations: Default::default(),
2558 },
2559 )?,
2560 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
2561 system_user: "signing-user".parse()?,
2562 domain: Domain::One,
2563 }
2564 ]),
2565 )]
2566 #[case::duplicate_signing_key_ids(
2567 "Error message for YubiHsm2Config::new with two duplicate signing key IDs",
2568 BTreeSet::from_iter([
2569 Connection::Usb {serial_number: "0012345678".parse()? },
2570 Connection::Usb {serial_number: "0087654321".parse()? },
2571 ]),
2572 BTreeSet::from_iter([
2573 YubiHsm2UserMapping::Admin { authentication_key_id: "1".parse()? },
2574 YubiHsm2UserMapping::Backup{
2575 authentication_key_id: "2".parse()?,
2576 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
2577 system_user: "backup-user".parse()?,
2578 wrapping_key_id: "1".parse()?,
2579 },
2580 YubiHsm2UserMapping::AuditLog {
2581 authentication_key_id: "3".parse()?,
2582 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host".parse()?,
2583 system_user: "metrics-user".parse()?,
2584 },
2585 YubiHsm2UserMapping::Signing {
2586 authentication_key_id: "4".parse()?,
2587 signing_key_id: "1".parse()?,
2588 key_setup: SigningKeySetup::new(
2589 KeyType::Curve25519,
2590 vec![KeyMechanism::EdDsaSignature],
2591 None,
2592 SignatureType::EdDsa,
2593 CryptographicKeyContext::OpenPgp {
2594 user_ids: OpenPgpUserIdList::new(vec![
2595 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
2596 ])?,
2597 version: "v4".parse()?,
2598 notations: Default::default(),
2599 },
2600 )?,
2601 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
2602 system_user: "signing-user".parse()?,
2603 domain: Domain::One,
2604 },
2605 YubiHsm2UserMapping::Signing {
2606 authentication_key_id: "5".parse()?,
2607 signing_key_id: "1".parse()?,
2608 key_setup: SigningKeySetup::new(
2609 KeyType::Curve25519,
2610 vec![KeyMechanism::EdDsaSignature],
2611 None,
2612 SignatureType::EdDsa,
2613 CryptographicKeyContext::OpenPgp {
2614 user_ids: OpenPgpUserIdList::new(vec![
2615 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
2616 ])?,
2617 version: "v4".parse()?,
2618 notations: Default::default(),
2619 },
2620 )?,
2621 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPDgwGfIRBAsOUuDEZw/uJQZSwOYr4sg2DAZpcc7MfOj user@host".parse()?,
2622 system_user: "signing-user2".parse()?,
2623 domain: Domain::Two,
2624 },
2625 ]),
2626 )]
2627 #[case::duplicate_wrapping_key_ids(
2628 "Error message for YubiHsm2Config::new with two duplicate wrapping key IDs",
2629 BTreeSet::from_iter([
2630 Connection::Usb {serial_number: "0012345678".parse()? },
2631 Connection::Usb {serial_number: "0087654321".parse()? },
2632 ]),
2633 BTreeSet::from_iter([
2634 YubiHsm2UserMapping::Admin { authentication_key_id: "1".parse()? },
2635 YubiHsm2UserMapping::Backup{
2636 authentication_key_id: "2".parse()?,
2637 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
2638 system_user: "backup-user".parse()?,
2639 wrapping_key_id: "1".parse()?,
2640 },
2641 YubiHsm2UserMapping::Backup{
2642 authentication_key_id: "3".parse()?,
2643 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPDgwGfIRBAsOUuDEZw/uJQZSwOYr4sg2DAZpcc7MfOj user@host".parse()?,
2644 system_user: "backup-user2".parse()?,
2645 wrapping_key_id: "1".parse()?,
2646 },
2647 YubiHsm2UserMapping::AuditLog {
2648 authentication_key_id: "4".parse()?,
2649 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host".parse()?,
2650 system_user: "metrics-user".parse()?,
2651 },
2652 YubiHsm2UserMapping::Signing {
2653 authentication_key_id: "5".parse()?,
2654 signing_key_id: "1".parse()?,
2655 key_setup: SigningKeySetup::new(
2656 KeyType::Curve25519,
2657 vec![KeyMechanism::EdDsaSignature],
2658 None,
2659 SignatureType::EdDsa,
2660 CryptographicKeyContext::OpenPgp {
2661 user_ids: OpenPgpUserIdList::new(vec![
2662 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
2663 ])?,
2664 version: "v4".parse()?,
2665 notations: Default::default(),
2666 },
2667 )?,
2668 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
2669 system_user: "signing-user".parse()?,
2670 domain: Domain::One,
2671 },
2672 ]),
2673 )]
2674 #[case::duplicate_domains(
2675 "Error message for YubiHsm2Config::new with two duplicate domains",
2676 BTreeSet::from_iter([
2677 Connection::Usb {serial_number: "0012345678".parse()? },
2678 Connection::Usb {serial_number: "0087654321".parse()? },
2679 ]),
2680 BTreeSet::from_iter([
2681 YubiHsm2UserMapping::Admin { authentication_key_id: "1".parse()? },
2682 YubiHsm2UserMapping::Backup{
2683 authentication_key_id: "2".parse()?,
2684 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
2685 system_user: "backup-user".parse()?,
2686 wrapping_key_id: "1".parse()?,
2687 },
2688 YubiHsm2UserMapping::AuditLog {
2689 authentication_key_id: "3".parse()?,
2690 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host".parse()?,
2691 system_user: "metrics-user".parse()?,
2692 },
2693 YubiHsm2UserMapping::Signing {
2694 authentication_key_id: "4".parse()?,
2695 signing_key_id: "1".parse()?,
2696 key_setup: SigningKeySetup::new(
2697 KeyType::Curve25519,
2698 vec![KeyMechanism::EdDsaSignature],
2699 None,
2700 SignatureType::EdDsa,
2701 CryptographicKeyContext::OpenPgp {
2702 user_ids: OpenPgpUserIdList::new(vec![
2703 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
2704 ])?,
2705 version: "v4".parse()?,
2706 notations: Default::default(),
2707 },
2708 )?,
2709 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
2710 system_user: "signing-user".parse()?,
2711 domain: Domain::One,
2712 },
2713 YubiHsm2UserMapping::Signing {
2714 authentication_key_id: "5".parse()?,
2715 signing_key_id: "2".parse()?,
2716 key_setup: SigningKeySetup::new(
2717 KeyType::Curve25519,
2718 vec![KeyMechanism::EdDsaSignature],
2719 None,
2720 SignatureType::EdDsa,
2721 CryptographicKeyContext::OpenPgp {
2722 user_ids: OpenPgpUserIdList::new(vec![
2723 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
2724 ])?,
2725 version: "v4".parse()?,
2726 notations: Default::default(),
2727 },
2728 )?,
2729 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPDgwGfIRBAsOUuDEZw/uJQZSwOYr4sg2DAZpcc7MfOj user@host".parse()?,
2730 system_user: "signing-user2".parse()?,
2731 domain: Domain::One,
2732 },
2733 ]),
2734 )]
2735 #[case::certificate_too_large_for_backend(
2736 "Error message for YubiHsm2Config::new with size estimation",
2737 BTreeSet::from_iter([
2738 Connection::Usb {serial_number: "0012345678".parse()? },
2739 ]),
2740 BTreeSet::from_iter([
2741 YubiHsm2UserMapping::Admin { authentication_key_id: "1".parse()? },
2742 YubiHsm2UserMapping::Signing {
2743 authentication_key_id: "4".parse()?,
2744 signing_key_id: "1".parse()?,
2745 key_setup: SigningKeySetup::new(
2746 KeyType::Curve25519,
2747 vec![KeyMechanism::EdDsaSignature],
2748 None,
2749 SignatureType::EdDsa,
2750 CryptographicKeyContext::OpenPgp {
2751 user_ids: OpenPgpUserIdList::new(vec![
2752 "Foobar McFooface 1 <foobar@example.org>".parse()?,
2753 "Foobar McFooface 2 <foobar@example.org>".parse()?,
2754 "Foobar McFooface 3 <foobar@example.org>".parse()?,
2755 "Foobar McFooface 4 <foobar@example.org>".parse()?,
2756 "Foobar McFooface 5 <foobar@example.org>".parse()?,
2757 "Foobar McFooface 6 <foobar@example.org>".parse()?,
2758 "Foobar McFooface 7 <foobar@example.org>".parse()?,
2759 "Foobar McFooface 8 <foobar@example.org>".parse()?,
2760 "Foobar McFooface 9 <foobar@example.org>".parse()?,
2761 "Foobar McFooface 10 <foobar@example.org>".parse()?,
2762 "Foobar McFooface 11 <foobar@example.org>".parse()?,
2763 "Foobar McFooface 12 <foobar@example.org>".parse()?,
2764 "Foobar McFooface 13 <foobar@example.org>".parse()?,
2765 "Foobar McFooface 14 <foobar@example.org>".parse()?,
2766 "Foobar McFooface 15 <foobar@example.org>".parse()?,
2767 "Foobar McFooface 16 <foobar@example.org>".parse()?,
2768 "Foobar McFooface 17 <foobar@example.org>".parse()?,
2769 "Foobar McFooface 18 <foobar@example.org>".parse()?,
2770 "Foobar McFooface 19 <foobar@example.org>".parse()?,
2771 "Foobar McFooface 20 <foobar@example.org>".parse()?,
2772 ])?,
2773 version: "v4".parse()?,
2774 notations: Default::default()
2775 },
2776 )?,
2777 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
2778 system_user: "signing-user".parse()?,
2779 domain: Domain::One,
2780 },
2781 ])
2782 )]
2783 #[case::all_the_issues(
2784 "Error message for YubiHsm2Config::new with multiple validation issues (connections and mappings)",
2785 BTreeSet::new(),
2786 BTreeSet::from_iter([
2787 YubiHsm2UserMapping::Backup{
2788 authentication_key_id: "2".parse()?,
2789 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
2790 system_user: "backup-user".parse()?,
2791 wrapping_key_id: "1".parse()?,
2792 },
2793 YubiHsm2UserMapping::Backup{
2794 authentication_key_id: "3".parse()?,
2795 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPDgwGfIRBAsOUuDEZw/uJQZSwOYr4sg2DAZpcc7MfOj user@host".parse()?,
2796 system_user: "backup-user".parse()?,
2797 wrapping_key_id: "1".parse()?,
2798 },
2799 YubiHsm2UserMapping::AuditLog {
2800 authentication_key_id: "3".parse()?,
2801 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host".parse()?,
2802 system_user: "metrics-backupuser".parse()?,
2803 },
2804 YubiHsm2UserMapping::Signing {
2805 authentication_key_id: "5".parse()?,
2806 signing_key_id: "1".parse()?,
2807 key_setup: SigningKeySetup::new(
2808 KeyType::Curve25519,
2809 vec![KeyMechanism::EdDsaSignature],
2810 None,
2811 SignatureType::EdDsa,
2812 CryptographicKeyContext::OpenPgp {
2813 user_ids: OpenPgpUserIdList::new(vec![
2814 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
2815 ])?,
2816 version: "v4".parse()?,
2817 notations: Default::default(),
2818 },
2819 )?,
2820 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
2821 system_user: "signing-user".parse()?,
2822 domain: Domain::One,
2823 },
2824 YubiHsm2UserMapping::Signing {
2825 authentication_key_id: "5".parse()?,
2826 signing_key_id: "1".parse()?,
2827 key_setup: SigningKeySetup::new(
2828 KeyType::Curve25519,
2829 vec![KeyMechanism::EdDsaSignature],
2830 None,
2831 SignatureType::EdDsa,
2832 CryptographicKeyContext::OpenPgp {
2833 user_ids: OpenPgpUserIdList::new(vec![
2834 "Foobar McFooface <foobar@mcfooface.org>".parse()?,
2835 ])?,
2836 version: "v4".parse()?,
2837 notations: Default::default(),
2838 },
2839 )?,
2840 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
2841 system_user: "signing-user2".parse()?,
2842 domain: Domain::One,
2843 },
2844 YubiHsm2UserMapping::Signing {
2845 authentication_key_id: "4".parse()?,
2846 signing_key_id: "1".parse()?,
2847 key_setup: SigningKeySetup::new(
2848 KeyType::Curve25519,
2849 vec![KeyMechanism::EdDsaSignature],
2850 None,
2851 SignatureType::EdDsa,
2852 CryptographicKeyContext::OpenPgp {
2853 notations: Default::default(),
2854 user_ids: OpenPgpUserIdList::new(vec![
2855 "Foobar McFooface 1 <foobar@example.org>".parse()?,
2856 "Foobar McFooface 2 <foobar@example.org>".parse()?,
2857 "Foobar McFooface 3 <foobar@example.org>".parse()?,
2858 "Foobar McFooface 4 <foobar@example.org>".parse()?,
2859 "Foobar McFooface 5 <foobar@example.org>".parse()?,
2860 "Foobar McFooface 6 <foobar@example.org>".parse()?,
2861 "Foobar McFooface 7 <foobar@example.org>".parse()?,
2862 "Foobar McFooface 8 <foobar@example.org>".parse()?,
2863 "Foobar McFooface 9 <foobar@example.org>".parse()?,
2864 "Foobar McFooface 10 <foobar@example.org>".parse()?,
2865 "Foobar McFooface 11 <foobar@example.org>".parse()?,
2866 "Foobar McFooface 12 <foobar@example.org>".parse()?,
2867 "Foobar McFooface 13 <foobar@example.org>".parse()?,
2868 "Foobar McFooface 14 <foobar@example.org>".parse()?,
2869 "Foobar McFooface 15 <foobar@example.org>".parse()?,
2870 "Foobar McFooface 16 <foobar@example.org>".parse()?,
2871 "Foobar McFooface 17 <foobar@example.org>".parse()?,
2872 "Foobar McFooface 18 <foobar@example.org>".parse()?,
2873 "Foobar McFooface 19 <foobar@example.org>".parse()?,
2874 "Foobar McFooface 20 <foobar@example.org>".parse()?,
2875 ])?,
2876 version: "v4".parse()?,
2877 },
2878 )?,
2879 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
2880 system_user: "signing-user".parse()?,
2881 domain: Domain::One,
2882 },
2883 ]),
2884 )]
2885 fn yubihsm2_config_new_fails_validation(
2886 #[case] description: &str,
2887 #[case] connections: BTreeSet<Connection>,
2888 #[case] mappings: BTreeSet<YubiHsm2UserMapping>,
2889 ) -> TestResult {
2890 let error_msg = match YubiHsm2Config::new(connections, mappings) {
2891 Err(crate::Error::Validation { source, .. }) => source.to_string(),
2892 Ok(config) => {
2893 panic!("Expected to fail with Error::Validation, but succeeded instead: {config:?}")
2894 }
2895 Err(error) => panic!(
2896 "Expected to fail with Error::Validation, but failed with a different error instead: {error}"
2897 ),
2898 };
2899
2900 with_settings!({
2901 description => description,
2902 snapshot_path => SNAPSHOT_PATH,
2903 prepend_module_to_snapshot => false,
2904 }, {
2905 assert_snapshot!(current().name().expect("current thread should have a name").to_string().replace("::", "__"), error_msg);
2906 });
2907 Ok(())
2908 }
2909}