1#![no_std]
165#![forbid(unsafe_code)]
166
167#[cfg(not(target_has_atomic = "64"))]
171compile_error!(
172 "oxicrypt-module needs 64-bit atomics: the module state and the thread running the \
173 power-up self-tests are published as one AtomicU64"
174);
175
176use core::fmt;
177use core::sync::atomic::{AtomicU8, AtomicU64, Ordering};
178
179#[derive(Debug, Clone, Copy, PartialEq, Eq)]
181#[repr(u8)]
182pub enum State {
183 PowerOff = 0,
185 SelfTest = 1,
188 Operational = 2,
191 Error = 3,
197 IntegrityUnverified = 4,
211}
212
213impl State {
214 const fn from_u8(raw: u8) -> Self {
215 match raw {
216 0 => Self::PowerOff,
217 1 => Self::SelfTest,
218 2 => Self::Operational,
219 4 => Self::IntegrityUnverified,
220 _ => Self::Error,
226 }
227 }
228}
229
230impl fmt::Display for State {
231 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
232 let s = match self {
233 Self::PowerOff => "PowerOff",
234 Self::SelfTest => "SelfTest",
235 Self::Operational => "Operational",
236 Self::Error => "Error",
237 Self::IntegrityUnverified => "IntegrityUnverified",
238 };
239 f.write_str(s)
240 }
241}
242
243static STATE: AtomicU64 = AtomicU64::new(POWER_OFF);
252
253const STATE_BITS: u32 = 8;
255const POWER_OFF: u64 = State::PowerOff as u64;
256const OPERATIONAL: u64 = State::Operational as u64;
259const ERROR: u64 = State::Error as u64;
260const INTEGRITY_UNVERIFIED: u64 = State::IntegrityUnverified as u64;
261
262const fn self_test_word(owner: u64) -> u64 {
267 owner.wrapping_shl(STATE_BITS) | State::SelfTest as u64
268}
269
270const fn decode(word: u64) -> State {
277 let [discriminant, ..] = word.to_le_bytes();
278 let owned = word.wrapping_shr(STATE_BITS) != 0;
279 match (State::from_u8(discriminant), owned) {
280 (State::SelfTest, true) => State::SelfTest,
281 (State::SelfTest, false) | (_, true) => State::Error,
282 (state, false) => state,
283 }
284}
285
286fn caller_owner_id() -> Option<u64> {
288 owner_id(os_thread_id())
289}
290
291fn owner_id(raw: Option<u64>) -> Option<u64> {
296 raw.filter(|&id| id != 0 && id.leading_zeros() >= STATE_BITS)
297}
298
299pub const THREAD_ID_CHECK: &str = "initialising thread id: none the module can record";
303
304#[cfg(any(
307 target_os = "linux",
308 target_os = "android",
309 target_os = "macos",
310 target_os = "ios",
311 windows
312))]
313fn os_thread_id() -> Option<u64> {
314 oxicrypt_imageread::current_thread_id().ok()
315}
316
317#[cfg(not(any(
320 target_os = "linux",
321 target_os = "android",
322 target_os = "macos",
323 target_os = "ios",
324 windows
325)))]
326fn os_thread_id() -> Option<u64> {
327 None
328}
329
330pub fn state() -> State {
332 decode(STATE.load(Ordering::Acquire))
333}
334
335pub fn is_operational() -> bool {
338 STATE.load(Ordering::Acquire) == OPERATIONAL
339}
340
341pub fn require_operational() -> Result<(), Error> {
349 let word = STATE.load(Ordering::Acquire);
350 if word == OPERATIONAL {
351 Ok(())
352 } else {
353 Err(Error::NotOperational {
354 current: decode(word),
355 })
356 }
357}
358
359pub fn require_self_test_started() -> Result<(), Error> {
378 let word = STATE.load(Ordering::Acquire);
379 if word == OPERATIONAL {
380 return Ok(());
381 }
382 let current = decode(word);
383 if current == State::SelfTest && caller_owner_id().map(self_test_word) == Some(word) {
384 return Ok(());
385 }
386 Err(Error::NotOperational { current })
387}
388
389#[derive(Debug)]
406pub struct SelfTestStarted {
407 _private: (),
408}
409
410const _: () = {
412 const fn assert_sync<T: Sync>() {}
413 assert_sync::<SelfTestStarted>();
414};
415
416pub fn require_self_test_started_token() -> Result<SelfTestStarted, Error> {
425 require_self_test_started()?;
426 Ok(SelfTestStarted { _private: () })
427}
428
429#[derive(Debug, Clone, Copy, PartialEq, Eq)]
436#[non_exhaustive]
437pub enum Error {
438 NotOperational {
441 current: State,
443 },
444 SelfTestFailed {
451 test: &'static str,
454 },
455 ConditionalTestFailed {
458 reason: &'static str,
460 },
461 IntegrityNotAttested,
469 AlreadyInitialized,
474 AlreadyFailed,
482 InvalidInput,
490 AlgorithmRestricted {
496 service: Service,
498 },
499 NotImplemented,
506}
507
508impl fmt::Display for Error {
509 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
510 match self {
511 Self::NotOperational { current } => {
512 write!(f, "FIPS module not operational (state = {current})")
513 }
514 Self::SelfTestFailed { test } => {
515 write!(f, "FIPS power-up self-test failed: {test}")
516 }
517 Self::ConditionalTestFailed { reason } => {
518 write!(f, "FIPS conditional self-test failed: {reason}")
519 }
520 Self::IntegrityNotAttested => f.write_str(
521 "this module did not check itself at startup, so it cannot tell whether this \
522 copy is the one that was built and signed. That does not mean the module is \
523 damaged: the program that started it skipped the check.",
524 ),
525 Self::AlreadyInitialized => f.write_str("FIPS module already initialized"),
526 Self::AlreadyFailed => f.write_str(
527 "FIPS module initialization already failed; restart the process to retry",
528 ),
529 Self::InvalidInput => f.write_str("invalid input to FIPS service"),
530 Self::AlgorithmRestricted { service } => {
531 write!(
532 f,
533 "algorithm {service} is not permitted under the {} profile",
534 active_profile()
535 )
536 }
537 Self::NotImplemented => f.write_str("algorithm not yet implemented"),
538 }
539 }
540}
541
542#[derive(Debug, Clone, Copy, PartialEq, Eq)]
551pub struct SelfTestFailure;
552
553impl fmt::Display for SelfTestFailure {
554 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
555 f.write_str("self-test failure")
556 }
557}
558
559pub trait SelfTest {
571 const NAME: &'static str;
574
575 fn run() -> Result<(), SelfTestFailure>;
579}
580
581#[derive(Debug, Clone, Copy)]
594pub struct KatEntry {
595 pub name: &'static str,
599 pub run: fn() -> Result<(), SelfTestFailure>,
601}
602
603pub fn initialize_with_tests(integrity: &[KatEntry], tests: &[KatEntry]) -> Result<(), Error> {
631 initialize(integrity, tests, AlgorithmProfile::Unrestricted)
632}
633
634fn initialize(
648 integrity: &[KatEntry],
649 tests: &[KatEntry],
650 profile: AlgorithmProfile,
651) -> Result<(), Error> {
652 initialize_as(caller_owner_id(), integrity, tests, profile)
653}
654
655fn initialize_as(
658 owner: Option<u64>,
659 integrity: &[KatEntry],
660 tests: &[KatEntry],
661 profile: AlgorithmProfile,
662) -> Result<(), Error> {
663 let claim = owner.map_or(ERROR, self_test_word);
667 if let Err(observed) =
668 STATE.compare_exchange(POWER_OFF, claim, Ordering::AcqRel, Ordering::Acquire)
669 {
670 return Err(already_left_power_off(observed, claim));
671 }
672 if owner.is_none() {
673 return Err(Error::SelfTestFailed {
674 test: THREAD_ID_CHECK,
675 });
676 }
677 let _unwind = UnwindToError { claim };
678
679 if integrity.is_empty() {
694 settle(claim, INTEGRITY_UNVERIFIED);
695 return Err(Error::IntegrityNotAttested);
696 }
697
698 for entry in integrity.iter().chain(tests) {
699 if (entry.run)().is_err() {
700 settle(claim, ERROR);
704 return Err(Error::SelfTestFailed { test: entry.name });
705 }
706 }
707
708 PROFILE.store(profile as u8, Ordering::Release);
722 if STATE
723 .compare_exchange(claim, OPERATIONAL, Ordering::AcqRel, Ordering::Acquire)
724 .is_err()
725 {
726 PROFILE.store(AlgorithmProfile::Unrestricted as u8, Ordering::Release);
727 return Err(Error::NotOperational { current: state() });
728 }
729 Ok(())
730}
731
732fn settle(claim: u64, to: u64) {
737 let _ = STATE.compare_exchange(claim, to, Ordering::AcqRel, Ordering::Acquire);
740}
741
742fn already_left_power_off(mut observed: u64, claim: u64) -> Error {
750 #[cfg(test)]
751 if decode(observed) == State::SelfTest && observed != claim {
752 WAITING_LOSERS.fetch_add(1, Ordering::AcqRel);
753 }
754 while decode(observed) == State::SelfTest && observed != claim {
755 core::hint::spin_loop();
756 observed = STATE.load(Ordering::Acquire);
757 }
758 match decode(observed) {
759 State::Operational => Error::AlreadyInitialized,
760 State::SelfTest => Error::NotOperational {
761 current: State::SelfTest,
762 },
763 State::PowerOff | State::Error | State::IntegrityUnverified => Error::AlreadyFailed,
764 }
765}
766
767#[cfg(test)]
770static WAITING_LOSERS: core::sync::atomic::AtomicUsize = core::sync::atomic::AtomicUsize::new(0);
771
772struct UnwindToError {
779 claim: u64,
780}
781
782impl Drop for UnwindToError {
783 fn drop(&mut self) {
784 settle(self.claim, ERROR);
785 }
786}
787
788pub fn enter_error_state(_reason: &'static str) {
800 STATE.store(ERROR, Ordering::Release);
801}
802
803#[derive(Debug, Clone, Copy, PartialEq, Eq)]
818#[repr(u8)]
819pub enum AlgorithmProfile {
820 Unrestricted = 0,
824 Cnsa2 = 1,
832 Cnsa1 = 2,
839 Migration = 3,
849}
850
851impl AlgorithmProfile {
852 const fn from_u8(raw: u8) -> Self {
853 match raw {
854 0 => Self::Unrestricted,
855 2 => Self::Cnsa1,
856 3 => Self::Migration,
857 _ => Self::Cnsa2,
860 }
861 }
862}
863
864impl fmt::Display for AlgorithmProfile {
865 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
866 let s = match self {
867 Self::Unrestricted => "Unrestricted",
868 Self::Cnsa2 => "CNSA 2.0",
869 Self::Cnsa1 => "CNSA 1.0",
870 Self::Migration => "Migration (CNSA 1.0 + CNSA 2.0)",
871 };
872 f.write_str(s)
873 }
874}
875
876static PROFILE: AtomicU8 = AtomicU8::new(AlgorithmProfile::Unrestricted as u8);
879
880pub fn active_profile() -> AlgorithmProfile {
882 AlgorithmProfile::from_u8(PROFILE.load(Ordering::Acquire))
883}
884
885pub fn initialize_with_profile(
916 integrity: &[KatEntry],
917 tests: &[KatEntry],
918 profile: AlgorithmProfile,
919) -> Result<(), Error> {
920 initialize(integrity, tests, profile)
921}
922
923#[derive(Debug, Clone, Copy, PartialEq, Eq)]
934#[repr(u16)]
935#[allow(missing_docs)]
936pub enum Service {
937 Sha1 = 0,
939 Sha224 = 1,
940 Sha256 = 2,
941 Sha384 = 3,
942 Sha512 = 4,
943 Sha512_224 = 5,
944 Sha512_256 = 6,
945
946 Sha3_224 = 10,
947 Sha3_256 = 11,
948 Sha3_384 = 12,
949 Sha3_512 = 13,
950
951 Shake128 = 20,
953 Shake256 = 21,
954 CShake128 = 22,
955 CShake256 = 23,
956 Kmac128 = 24,
957 Kmac256 = 25,
958 KmacXof128 = 26,
959 KmacXof256 = 27,
960 TupleHash128 = 28,
961 TupleHash256 = 29,
962 TupleHashXof128 = 30,
963 TupleHashXof256 = 31,
964 ParallelHash128 = 32,
965 ParallelHash256 = 33,
966 ParallelHashXof128 = 34,
967 ParallelHashXof256 = 35,
968
969 HmacSha1 = 40,
971 HmacSha224 = 41,
972 HmacSha256 = 42,
973 HmacSha384 = 43,
974 HmacSha512 = 44,
975 HmacSha512_224 = 45,
976 HmacSha512_256 = 46,
977 HmacSha3_224 = 47,
978 HmacSha3_256 = 48,
979 HmacSha3_384 = 49,
980 HmacSha3_512 = 50,
981
982 CmacAes128 = 60,
984 CmacAes192 = 61,
985 CmacAes256 = 62,
986
987 Aes128Ecb = 70,
989 Aes128Cbc = 71,
990 Aes128Ctr = 72,
991 Aes128Gcm = 73,
992 Aes128Ccm = 74,
993 Aes128Kw = 75,
994 Aes128Kwp = 76,
995 Aes192Ecb = 80,
996 Aes192Cbc = 81,
997 Aes192Ctr = 82,
998 Aes192Gcm = 83,
999 Aes192Ccm = 84,
1000 Aes192Kw = 85,
1001 Aes192Kwp = 86,
1002 Aes256Ecb = 90,
1003 Aes256Cbc = 91,
1004 Aes256Ctr = 92,
1005 Aes256Gcm = 93,
1006 Aes256Ccm = 94,
1007 Aes256Kw = 95,
1008 Aes256Kwp = 96,
1009
1010 Aes128 = 97,
1017 Aes192 = 98,
1019 Aes256 = 99,
1021
1022 CtrDrbgAes128 = 100,
1024 CtrDrbgAes192 = 101,
1025 CtrDrbgAes256 = 102,
1026 HashDrbgSha256 = 103,
1027 HashDrbgSha384 = 104,
1028 HashDrbgSha512 = 105,
1029 HmacDrbgSha256 = 106,
1030 HmacDrbgSha384 = 107,
1031 HmacDrbgSha512 = 108,
1032
1033 HkdfSha1 = 120,
1035 HkdfSha256 = 121,
1036 HkdfSha384 = 122,
1037 HkdfSha512 = 123,
1038 KbkdfHmacSha256 = 130,
1039 KbkdfHmacSha384 = 131,
1040 KbkdfHmacSha512 = 132,
1041 KbkdfCmacAes128 = 133,
1042 KbkdfCmacAes192 = 134,
1043 KbkdfCmacAes256 = 135,
1044 Pbkdf2HmacSha1 = 140,
1045 Pbkdf2HmacSha224 = 141,
1046 Pbkdf2HmacSha256 = 142,
1047 Pbkdf2HmacSha384 = 143,
1048 Pbkdf2HmacSha512 = 144,
1049
1050 RsaKeygen2048 = 150,
1052 RsaPkcs1v15Sign2048 = 151,
1053 RsaPssSign2048 = 152,
1054 RsaOaep2048 = 153,
1055 RsaPkcs1v15Verify2048 = 154,
1056 RsaPssVerify2048 = 155,
1057 RsaKeygen3072 = 160,
1058 RsaPkcs1v15Sign3072 = 161,
1059 RsaPssSign3072 = 162,
1060 RsaOaep3072 = 163,
1061 RsaPkcs1v15Verify3072 = 164,
1062 RsaPssVerify3072 = 165,
1063 RsaKeygen4096 = 170,
1064 RsaPkcs1v15Sign4096 = 171,
1065 RsaPssSign4096 = 172,
1066 RsaOaep4096 = 173,
1067 RsaPkcs1v15Verify4096 = 174,
1068 RsaPssVerify4096 = 175,
1069
1070 EcdsaP256Sign = 200,
1072 EcdsaP256Verify = 201,
1073 EcdsaP256Keygen = 202,
1074 EcdsaP384Sign = 210,
1075 EcdsaP384Verify = 211,
1076 EcdsaP384Keygen = 212,
1077
1078 EcdhP256 = 220,
1080 EcdhP384 = 221,
1081
1082 Ed25519Sign = 230,
1084 Ed25519Verify = 231,
1085 Ed25519Keygen = 232,
1086
1087 Tls12Kdf = 240,
1089 Tls13Kdf = 241,
1090
1091 MlKem1024Encaps = 300,
1093 MlKem1024Decaps = 301,
1094 MlKem1024Keygen = 302,
1095 MlKem512Encaps = 303,
1096 MlKem512Decaps = 304,
1097 MlKem512Keygen = 305,
1098 MlKem768Encaps = 306,
1099 MlKem768Decaps = 307,
1100 MlKem768Keygen = 308,
1101
1102 MlDsa87Sign = 310,
1104 MlDsa87Verify = 311,
1105 MlDsa87Keygen = 312,
1106 MlDsa44Sign = 313,
1107 MlDsa44Verify = 314,
1108 MlDsa44Keygen = 315,
1109 MlDsa65Sign = 316,
1110 MlDsa65Verify = 317,
1111 MlDsa65Keygen = 318,
1112
1113 SlhDsaSha2256sKeygen = 320,
1118 SlhDsaSha2256sSign = 321,
1119 SlhDsaSha2256sVerify = 322,
1120 SlhDsaSha2128sKeygen = 323,
1121 SlhDsaSha2128sSign = 324,
1122 SlhDsaSha2128sVerify = 325,
1123 SlhDsaSha2128fKeygen = 326,
1124 SlhDsaSha2128fSign = 327,
1125 SlhDsaSha2128fVerify = 328,
1126 SlhDsaSha2192sKeygen = 329,
1127 SlhDsaSha2192sSign = 330,
1128 SlhDsaSha2192sVerify = 331,
1129 SlhDsaSha2192fKeygen = 332,
1130 SlhDsaSha2192fSign = 333,
1131 SlhDsaSha2192fVerify = 334,
1132 SlhDsaSha2256fKeygen = 335,
1133 SlhDsaSha2256fSign = 336,
1134 SlhDsaSha2256fVerify = 337,
1135 SlhDsaShake128sKeygen = 338,
1136 SlhDsaShake128sSign = 339,
1137 SlhDsaShake128sVerify = 340,
1138 SlhDsaShake128fKeygen = 341,
1139 SlhDsaShake128fSign = 342,
1140 SlhDsaShake128fVerify = 343,
1141 SlhDsaShake192sKeygen = 344,
1142 SlhDsaShake192sSign = 345,
1143 SlhDsaShake192sVerify = 346,
1144 SlhDsaShake192fKeygen = 347,
1145 SlhDsaShake192fSign = 348,
1146 SlhDsaShake192fVerify = 349,
1147 SlhDsaShake256sKeygen = 350,
1148 SlhDsaShake256sSign = 351,
1149 SlhDsaShake256sVerify = 352,
1150 SlhDsaShake256fKeygen = 353,
1151 SlhDsaShake256fSign = 354,
1152 SlhDsaShake256fVerify = 355,
1153
1154 LmsSha256M32H5W1Verify = 501,
1169 LmsSha256M32H5W2Verify = 503,
1170 LmsSha256M32H5W4Verify = 505,
1171 LmsSha256M32H5W8Verify = 507,
1172 LmsSha256M32H10W1Verify = 509,
1173 LmsSha256M32H10W2Verify = 511,
1174 LmsSha256M32H10W4Verify = 513,
1175 LmsSha256M32H10W8Verify = 515,
1176 LmsSha256M32H15W1Verify = 517,
1177 LmsSha256M32H15W2Verify = 519,
1178 LmsSha256M32H15W4Verify = 521,
1179 LmsSha256M32H15W8Verify = 523,
1180 LmsSha256M32H20W1Verify = 525,
1181 LmsSha256M32H20W2Verify = 527,
1182 LmsSha256M32H20W4Verify = 529,
1183 LmsSha256M32H20W8Verify = 531,
1184 LmsSha256M32H25W1Verify = 533,
1185 LmsSha256M32H25W2Verify = 535,
1186 LmsSha256M32H25W4Verify = 537,
1187 LmsSha256M32H25W8Verify = 539,
1188
1189 LmsSha256M24H5W1Verify = 541,
1191 LmsSha256M24H5W2Verify = 543,
1192 LmsSha256M24H5W4Verify = 545,
1193 LmsSha256M24H5W8Verify = 547,
1194 LmsSha256M24H10W1Verify = 549,
1195 LmsSha256M24H10W2Verify = 551,
1196 LmsSha256M24H10W4Verify = 553,
1197 LmsSha256M24H10W8Verify = 555,
1198 LmsSha256M24H15W1Verify = 557,
1199 LmsSha256M24H15W2Verify = 559,
1200 LmsSha256M24H15W4Verify = 561,
1201 LmsSha256M24H15W8Verify = 563,
1202 LmsSha256M24H20W1Verify = 565,
1203 LmsSha256M24H20W2Verify = 567,
1204 LmsSha256M24H20W4Verify = 569,
1205 LmsSha256M24H20W8Verify = 571,
1206 LmsSha256M24H25W1Verify = 573,
1207 LmsSha256M24H25W2Verify = 575,
1208 LmsSha256M24H25W4Verify = 577,
1209 LmsSha256M24H25W8Verify = 579,
1210
1211 LmsShakeM32H5W1Verify = 581,
1213 LmsShakeM32H5W2Verify = 583,
1214 LmsShakeM32H5W4Verify = 585,
1215 LmsShakeM32H5W8Verify = 587,
1216 LmsShakeM32H10W1Verify = 589,
1217 LmsShakeM32H10W2Verify = 591,
1218 LmsShakeM32H10W4Verify = 593,
1219 LmsShakeM32H10W8Verify = 595,
1220 LmsShakeM32H15W1Verify = 597,
1221 LmsShakeM32H15W2Verify = 599,
1222 LmsShakeM32H15W4Verify = 601,
1223 LmsShakeM32H15W8Verify = 603,
1224 LmsShakeM32H20W1Verify = 605,
1225 LmsShakeM32H20W2Verify = 607,
1226 LmsShakeM32H20W4Verify = 609,
1227 LmsShakeM32H20W8Verify = 611,
1228 LmsShakeM32H25W1Verify = 613,
1229 LmsShakeM32H25W2Verify = 615,
1230 LmsShakeM32H25W4Verify = 617,
1231 LmsShakeM32H25W8Verify = 619,
1232
1233 LmsShakeM24H5W1Verify = 621,
1235 LmsShakeM24H5W2Verify = 623,
1236 LmsShakeM24H5W4Verify = 625,
1237 LmsShakeM24H5W8Verify = 627,
1238 LmsShakeM24H10W1Verify = 629,
1239 LmsShakeM24H10W2Verify = 631,
1240 LmsShakeM24H10W4Verify = 633,
1241 LmsShakeM24H10W8Verify = 635,
1242 LmsShakeM24H15W1Verify = 637,
1243 LmsShakeM24H15W2Verify = 639,
1244 LmsShakeM24H15W4Verify = 641,
1245 LmsShakeM24H15W8Verify = 643,
1246 LmsShakeM24H20W1Verify = 645,
1247 LmsShakeM24H20W2Verify = 647,
1248 LmsShakeM24H20W4Verify = 649,
1249 LmsShakeM24H20W8Verify = 651,
1250 LmsShakeM24H25W1Verify = 653,
1251 LmsShakeM24H25W2Verify = 655,
1252 LmsShakeM24H25W4Verify = 657,
1253 LmsShakeM24H25W8Verify = 659,
1254
1255 XmssVerify = 371,
1258
1259 Dh3072 = 380,
1262}
1263
1264impl fmt::Display for Service {
1265 #[allow(clippy::too_many_lines)]
1269 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1270 let name = match self {
1271 Self::Sha1 => "SHA-1",
1272 Self::Sha224 => "SHA-224",
1273 Self::Sha256 => "SHA-256",
1274 Self::Sha384 => "SHA-384",
1275 Self::Sha512 => "SHA-512",
1276 Self::Sha512_224 => "SHA-512/224",
1277 Self::Sha512_256 => "SHA-512/256",
1278 Self::Sha3_224 => "SHA3-224",
1279 Self::Sha3_256 => "SHA3-256",
1280 Self::Sha3_384 => "SHA3-384",
1281 Self::Sha3_512 => "SHA3-512",
1282 Self::Shake128 => "SHAKE128",
1283 Self::Shake256 => "SHAKE256",
1284 Self::CShake128 => "cSHAKE128",
1285 Self::CShake256 => "cSHAKE256",
1286 Self::Kmac128 => "KMAC128",
1287 Self::Kmac256 => "KMAC256",
1288 Self::KmacXof128 => "KMACXOF128",
1289 Self::KmacXof256 => "KMACXOF256",
1290 Self::TupleHash128 => "TupleHash128",
1291 Self::TupleHash256 => "TupleHash256",
1292 Self::TupleHashXof128 => "TupleHashXOF128",
1293 Self::TupleHashXof256 => "TupleHashXOF256",
1294 Self::ParallelHash128 => "ParallelHash128",
1295 Self::ParallelHash256 => "ParallelHash256",
1296 Self::ParallelHashXof128 => "ParallelHashXOF128",
1297 Self::ParallelHashXof256 => "ParallelHashXOF256",
1298 Self::HmacSha1 => "HMAC-SHA-1",
1299 Self::HmacSha224 => "HMAC-SHA-224",
1300 Self::HmacSha256 => "HMAC-SHA-256",
1301 Self::HmacSha384 => "HMAC-SHA-384",
1302 Self::HmacSha512 => "HMAC-SHA-512",
1303 Self::HmacSha512_224 => "HMAC-SHA-512/224",
1304 Self::HmacSha512_256 => "HMAC-SHA-512/256",
1305 Self::HmacSha3_224 => "HMAC-SHA3-224",
1306 Self::HmacSha3_256 => "HMAC-SHA3-256",
1307 Self::HmacSha3_384 => "HMAC-SHA3-384",
1308 Self::HmacSha3_512 => "HMAC-SHA3-512",
1309 Self::CmacAes128 => "CMAC-AES-128",
1310 Self::CmacAes192 => "CMAC-AES-192",
1311 Self::CmacAes256 => "CMAC-AES-256",
1312 Self::Aes128Ecb => "AES-128-ECB",
1313 Self::Aes128Cbc => "AES-128-CBC",
1314 Self::Aes128Ctr => "AES-128-CTR",
1315 Self::Aes128Gcm => "AES-128-GCM",
1316 Self::Aes128Ccm => "AES-128-CCM",
1317 Self::Aes128Kw => "AES-128-KW",
1318 Self::Aes128Kwp => "AES-128-KWP",
1319 Self::Aes192Ecb => "AES-192-ECB",
1320 Self::Aes192Cbc => "AES-192-CBC",
1321 Self::Aes192Ctr => "AES-192-CTR",
1322 Self::Aes192Gcm => "AES-192-GCM",
1323 Self::Aes192Ccm => "AES-192-CCM",
1324 Self::Aes192Kw => "AES-192-KW",
1325 Self::Aes192Kwp => "AES-192-KWP",
1326 Self::Aes256Ecb => "AES-256-ECB",
1327 Self::Aes256Cbc => "AES-256-CBC",
1328 Self::Aes256Ctr => "AES-256-CTR",
1329 Self::Aes256Gcm => "AES-256-GCM",
1330 Self::Aes256Ccm => "AES-256-CCM",
1331 Self::Aes256Kw => "AES-256-KW",
1332 Self::Aes256Kwp => "AES-256-KWP",
1333 Self::Aes128 => "AES-128",
1334 Self::Aes192 => "AES-192",
1335 Self::Aes256 => "AES-256",
1336 Self::CtrDrbgAes128 => "CTR_DRBG-AES-128",
1337 Self::CtrDrbgAes192 => "CTR_DRBG-AES-192",
1338 Self::CtrDrbgAes256 => "CTR_DRBG-AES-256",
1339 Self::HashDrbgSha256 => "Hash_DRBG-SHA-256",
1340 Self::HashDrbgSha384 => "Hash_DRBG-SHA-384",
1341 Self::HashDrbgSha512 => "Hash_DRBG-SHA-512",
1342 Self::HmacDrbgSha256 => "HMAC_DRBG-SHA-256",
1343 Self::HmacDrbgSha384 => "HMAC_DRBG-SHA-384",
1344 Self::HmacDrbgSha512 => "HMAC_DRBG-SHA-512",
1345 Self::HkdfSha1 => "HKDF-SHA-1",
1346 Self::HkdfSha256 => "HKDF-SHA-256",
1347 Self::HkdfSha384 => "HKDF-SHA-384",
1348 Self::HkdfSha512 => "HKDF-SHA-512",
1349 Self::KbkdfHmacSha256 => "KBKDF-HMAC-SHA-256",
1350 Self::KbkdfHmacSha384 => "KBKDF-HMAC-SHA-384",
1351 Self::KbkdfHmacSha512 => "KBKDF-HMAC-SHA-512",
1352 Self::KbkdfCmacAes128 => "KBKDF-CMAC-AES-128",
1353 Self::KbkdfCmacAes192 => "KBKDF-CMAC-AES-192",
1354 Self::KbkdfCmacAes256 => "KBKDF-CMAC-AES-256",
1355 Self::Pbkdf2HmacSha1 => "PBKDF2-HMAC-SHA-1",
1356 Self::Pbkdf2HmacSha224 => "PBKDF2-HMAC-SHA-224",
1357 Self::Pbkdf2HmacSha256 => "PBKDF2-HMAC-SHA-256",
1358 Self::Pbkdf2HmacSha384 => "PBKDF2-HMAC-SHA-384",
1359 Self::Pbkdf2HmacSha512 => "PBKDF2-HMAC-SHA-512",
1360 Self::RsaKeygen2048 => "RSA-2048 keygen",
1361 Self::RsaPkcs1v15Sign2048 => "RSA-PKCS1v1.5-Sign-2048",
1362 Self::RsaPssSign2048 => "RSA-PSS-Sign-2048",
1363 Self::RsaOaep2048 => "RSA-OAEP-2048",
1364 Self::RsaPkcs1v15Verify2048 => "RSA-PKCS1v1.5-Verify-2048",
1365 Self::RsaPssVerify2048 => "RSA-PSS-Verify-2048",
1366 Self::RsaKeygen3072 => "RSA-3072 keygen",
1367 Self::RsaPkcs1v15Sign3072 => "RSA-PKCS1v1.5-Sign-3072",
1368 Self::RsaPssSign3072 => "RSA-PSS-Sign-3072",
1369 Self::RsaOaep3072 => "RSA-OAEP-3072",
1370 Self::RsaPkcs1v15Verify3072 => "RSA-PKCS1v1.5-Verify-3072",
1371 Self::RsaPssVerify3072 => "RSA-PSS-Verify-3072",
1372 Self::RsaKeygen4096 => "RSA-4096 keygen",
1373 Self::RsaPkcs1v15Sign4096 => "RSA-PKCS1v1.5-Sign-4096",
1374 Self::RsaPssSign4096 => "RSA-PSS-Sign-4096",
1375 Self::RsaOaep4096 => "RSA-OAEP-4096",
1376 Self::RsaPkcs1v15Verify4096 => "RSA-PKCS1v1.5-Verify-4096",
1377 Self::RsaPssVerify4096 => "RSA-PSS-Verify-4096",
1378 Self::EcdsaP256Sign => "ECDSA-P256 sign",
1379 Self::EcdsaP256Verify => "ECDSA-P256 verify",
1380 Self::EcdsaP256Keygen => "ECDSA-P256 keygen",
1381 Self::EcdsaP384Sign => "ECDSA-P384 sign",
1382 Self::EcdsaP384Verify => "ECDSA-P384 verify",
1383 Self::EcdsaP384Keygen => "ECDSA-P384 keygen",
1384 Self::EcdhP256 => "ECDH-P256",
1385 Self::EcdhP384 => "ECDH-P384",
1386 Self::Ed25519Sign => "Ed25519 sign",
1387 Self::Ed25519Verify => "Ed25519 verify",
1388 Self::Ed25519Keygen => "Ed25519 keygen",
1389 Self::Tls12Kdf => "TLS 1.2 KDF",
1390 Self::Tls13Kdf => "TLS 1.3 KDF",
1391 Self::MlKem1024Encaps => "ML-KEM-1024 encaps",
1392 Self::MlKem1024Decaps => "ML-KEM-1024 decaps",
1393 Self::MlKem1024Keygen => "ML-KEM-1024 keygen",
1394 Self::MlKem512Encaps => "ML-KEM-512 encaps",
1395 Self::MlKem512Decaps => "ML-KEM-512 decaps",
1396 Self::MlKem512Keygen => "ML-KEM-512 keygen",
1397 Self::MlKem768Encaps => "ML-KEM-768 encaps",
1398 Self::MlKem768Decaps => "ML-KEM-768 decaps",
1399 Self::MlKem768Keygen => "ML-KEM-768 keygen",
1400 Self::MlDsa87Sign => "ML-DSA-87 sign",
1401 Self::MlDsa87Verify => "ML-DSA-87 verify",
1402 Self::MlDsa87Keygen => "ML-DSA-87 keygen",
1403 Self::MlDsa44Sign => "ML-DSA-44 sign",
1404 Self::MlDsa44Verify => "ML-DSA-44 verify",
1405 Self::MlDsa44Keygen => "ML-DSA-44 keygen",
1406 Self::MlDsa65Sign => "ML-DSA-65 sign",
1407 Self::MlDsa65Verify => "ML-DSA-65 verify",
1408 Self::MlDsa65Keygen => "ML-DSA-65 keygen",
1409 Self::SlhDsaSha2256sKeygen => "SLH-DSA-SHA2-256s keygen",
1410 Self::SlhDsaSha2256sSign => "SLH-DSA-SHA2-256s sign",
1411 Self::SlhDsaSha2256sVerify => "SLH-DSA-SHA2-256s verify",
1412 Self::SlhDsaSha2128sKeygen => "SLH-DSA-SHA2-128s keygen",
1413 Self::SlhDsaSha2128sSign => "SLH-DSA-SHA2-128s sign",
1414 Self::SlhDsaSha2128sVerify => "SLH-DSA-SHA2-128s verify",
1415 Self::SlhDsaSha2128fKeygen => "SLH-DSA-SHA2-128f keygen",
1416 Self::SlhDsaSha2128fSign => "SLH-DSA-SHA2-128f sign",
1417 Self::SlhDsaSha2128fVerify => "SLH-DSA-SHA2-128f verify",
1418 Self::SlhDsaSha2192sKeygen => "SLH-DSA-SHA2-192s keygen",
1419 Self::SlhDsaSha2192sSign => "SLH-DSA-SHA2-192s sign",
1420 Self::SlhDsaSha2192sVerify => "SLH-DSA-SHA2-192s verify",
1421 Self::SlhDsaSha2192fKeygen => "SLH-DSA-SHA2-192f keygen",
1422 Self::SlhDsaSha2192fSign => "SLH-DSA-SHA2-192f sign",
1423 Self::SlhDsaSha2192fVerify => "SLH-DSA-SHA2-192f verify",
1424 Self::SlhDsaSha2256fKeygen => "SLH-DSA-SHA2-256f keygen",
1425 Self::SlhDsaSha2256fSign => "SLH-DSA-SHA2-256f sign",
1426 Self::SlhDsaSha2256fVerify => "SLH-DSA-SHA2-256f verify",
1427 Self::SlhDsaShake128sKeygen => "SLH-DSA-SHAKE-128s keygen",
1428 Self::SlhDsaShake128sSign => "SLH-DSA-SHAKE-128s sign",
1429 Self::SlhDsaShake128sVerify => "SLH-DSA-SHAKE-128s verify",
1430 Self::SlhDsaShake128fKeygen => "SLH-DSA-SHAKE-128f keygen",
1431 Self::SlhDsaShake128fSign => "SLH-DSA-SHAKE-128f sign",
1432 Self::SlhDsaShake128fVerify => "SLH-DSA-SHAKE-128f verify",
1433 Self::SlhDsaShake192sKeygen => "SLH-DSA-SHAKE-192s keygen",
1434 Self::SlhDsaShake192sSign => "SLH-DSA-SHAKE-192s sign",
1435 Self::SlhDsaShake192sVerify => "SLH-DSA-SHAKE-192s verify",
1436 Self::SlhDsaShake192fKeygen => "SLH-DSA-SHAKE-192f keygen",
1437 Self::SlhDsaShake192fSign => "SLH-DSA-SHAKE-192f sign",
1438 Self::SlhDsaShake192fVerify => "SLH-DSA-SHAKE-192f verify",
1439 Self::SlhDsaShake256sKeygen => "SLH-DSA-SHAKE-256s keygen",
1440 Self::SlhDsaShake256sSign => "SLH-DSA-SHAKE-256s sign",
1441 Self::SlhDsaShake256sVerify => "SLH-DSA-SHAKE-256s verify",
1442 Self::SlhDsaShake256fKeygen => "SLH-DSA-SHAKE-256f keygen",
1443 Self::SlhDsaShake256fSign => "SLH-DSA-SHAKE-256f sign",
1444 Self::SlhDsaShake256fVerify => "SLH-DSA-SHAKE-256f verify",
1445 Self::LmsSha256M32H5W1Verify => "LMS SHA-256 M=32 H=5 W=1 verify",
1446 Self::LmsSha256M32H5W2Verify => "LMS SHA-256 M=32 H=5 W=2 verify",
1447 Self::LmsSha256M32H5W4Verify => "LMS SHA-256 M=32 H=5 W=4 verify",
1448 Self::LmsSha256M32H5W8Verify => "LMS SHA-256 M=32 H=5 W=8 verify",
1449 Self::LmsSha256M32H10W1Verify => "LMS SHA-256 M=32 H=10 W=1 verify",
1450 Self::LmsSha256M32H10W2Verify => "LMS SHA-256 M=32 H=10 W=2 verify",
1451 Self::LmsSha256M32H10W4Verify => "LMS SHA-256 M=32 H=10 W=4 verify",
1452 Self::LmsSha256M32H10W8Verify => "LMS SHA-256 M=32 H=10 W=8 verify",
1453 Self::LmsSha256M32H15W1Verify => "LMS SHA-256 M=32 H=15 W=1 verify",
1454 Self::LmsSha256M32H15W2Verify => "LMS SHA-256 M=32 H=15 W=2 verify",
1455 Self::LmsSha256M32H15W4Verify => "LMS SHA-256 M=32 H=15 W=4 verify",
1456 Self::LmsSha256M32H15W8Verify => "LMS SHA-256 M=32 H=15 W=8 verify",
1457 Self::LmsSha256M32H20W1Verify => "LMS SHA-256 M=32 H=20 W=1 verify",
1458 Self::LmsSha256M32H20W2Verify => "LMS SHA-256 M=32 H=20 W=2 verify",
1459 Self::LmsSha256M32H20W4Verify => "LMS SHA-256 M=32 H=20 W=4 verify",
1460 Self::LmsSha256M32H20W8Verify => "LMS SHA-256 M=32 H=20 W=8 verify",
1461 Self::LmsSha256M32H25W1Verify => "LMS SHA-256 M=32 H=25 W=1 verify",
1462 Self::LmsSha256M32H25W2Verify => "LMS SHA-256 M=32 H=25 W=2 verify",
1463 Self::LmsSha256M32H25W4Verify => "LMS SHA-256 M=32 H=25 W=4 verify",
1464 Self::LmsSha256M32H25W8Verify => "LMS SHA-256 M=32 H=25 W=8 verify",
1465 Self::LmsSha256M24H5W1Verify => "LMS SHA-256 M=24 H=5 W=1 verify",
1466 Self::LmsSha256M24H5W2Verify => "LMS SHA-256 M=24 H=5 W=2 verify",
1467 Self::LmsSha256M24H5W4Verify => "LMS SHA-256 M=24 H=5 W=4 verify",
1468 Self::LmsSha256M24H5W8Verify => "LMS SHA-256 M=24 H=5 W=8 verify",
1469 Self::LmsSha256M24H10W1Verify => "LMS SHA-256 M=24 H=10 W=1 verify",
1470 Self::LmsSha256M24H10W2Verify => "LMS SHA-256 M=24 H=10 W=2 verify",
1471 Self::LmsSha256M24H10W4Verify => "LMS SHA-256 M=24 H=10 W=4 verify",
1472 Self::LmsSha256M24H10W8Verify => "LMS SHA-256 M=24 H=10 W=8 verify",
1473 Self::LmsSha256M24H15W1Verify => "LMS SHA-256 M=24 H=15 W=1 verify",
1474 Self::LmsSha256M24H15W2Verify => "LMS SHA-256 M=24 H=15 W=2 verify",
1475 Self::LmsSha256M24H15W4Verify => "LMS SHA-256 M=24 H=15 W=4 verify",
1476 Self::LmsSha256M24H15W8Verify => "LMS SHA-256 M=24 H=15 W=8 verify",
1477 Self::LmsSha256M24H20W1Verify => "LMS SHA-256 M=24 H=20 W=1 verify",
1478 Self::LmsSha256M24H20W2Verify => "LMS SHA-256 M=24 H=20 W=2 verify",
1479 Self::LmsSha256M24H20W4Verify => "LMS SHA-256 M=24 H=20 W=4 verify",
1480 Self::LmsSha256M24H20W8Verify => "LMS SHA-256 M=24 H=20 W=8 verify",
1481 Self::LmsSha256M24H25W1Verify => "LMS SHA-256 M=24 H=25 W=1 verify",
1482 Self::LmsSha256M24H25W2Verify => "LMS SHA-256 M=24 H=25 W=2 verify",
1483 Self::LmsSha256M24H25W4Verify => "LMS SHA-256 M=24 H=25 W=4 verify",
1484 Self::LmsSha256M24H25W8Verify => "LMS SHA-256 M=24 H=25 W=8 verify",
1485 Self::LmsShakeM32H5W1Verify => "LMS SHAKE M=32 H=5 W=1 verify",
1486 Self::LmsShakeM32H5W2Verify => "LMS SHAKE M=32 H=5 W=2 verify",
1487 Self::LmsShakeM32H5W4Verify => "LMS SHAKE M=32 H=5 W=4 verify",
1488 Self::LmsShakeM32H5W8Verify => "LMS SHAKE M=32 H=5 W=8 verify",
1489 Self::LmsShakeM32H10W1Verify => "LMS SHAKE M=32 H=10 W=1 verify",
1490 Self::LmsShakeM32H10W2Verify => "LMS SHAKE M=32 H=10 W=2 verify",
1491 Self::LmsShakeM32H10W4Verify => "LMS SHAKE M=32 H=10 W=4 verify",
1492 Self::LmsShakeM32H10W8Verify => "LMS SHAKE M=32 H=10 W=8 verify",
1493 Self::LmsShakeM32H15W1Verify => "LMS SHAKE M=32 H=15 W=1 verify",
1494 Self::LmsShakeM32H15W2Verify => "LMS SHAKE M=32 H=15 W=2 verify",
1495 Self::LmsShakeM32H15W4Verify => "LMS SHAKE M=32 H=15 W=4 verify",
1496 Self::LmsShakeM32H15W8Verify => "LMS SHAKE M=32 H=15 W=8 verify",
1497 Self::LmsShakeM32H20W1Verify => "LMS SHAKE M=32 H=20 W=1 verify",
1498 Self::LmsShakeM32H20W2Verify => "LMS SHAKE M=32 H=20 W=2 verify",
1499 Self::LmsShakeM32H20W4Verify => "LMS SHAKE M=32 H=20 W=4 verify",
1500 Self::LmsShakeM32H20W8Verify => "LMS SHAKE M=32 H=20 W=8 verify",
1501 Self::LmsShakeM32H25W1Verify => "LMS SHAKE M=32 H=25 W=1 verify",
1502 Self::LmsShakeM32H25W2Verify => "LMS SHAKE M=32 H=25 W=2 verify",
1503 Self::LmsShakeM32H25W4Verify => "LMS SHAKE M=32 H=25 W=4 verify",
1504 Self::LmsShakeM32H25W8Verify => "LMS SHAKE M=32 H=25 W=8 verify",
1505 Self::LmsShakeM24H5W1Verify => "LMS SHAKE M=24 H=5 W=1 verify",
1506 Self::LmsShakeM24H5W2Verify => "LMS SHAKE M=24 H=5 W=2 verify",
1507 Self::LmsShakeM24H5W4Verify => "LMS SHAKE M=24 H=5 W=4 verify",
1508 Self::LmsShakeM24H5W8Verify => "LMS SHAKE M=24 H=5 W=8 verify",
1509 Self::LmsShakeM24H10W1Verify => "LMS SHAKE M=24 H=10 W=1 verify",
1510 Self::LmsShakeM24H10W2Verify => "LMS SHAKE M=24 H=10 W=2 verify",
1511 Self::LmsShakeM24H10W4Verify => "LMS SHAKE M=24 H=10 W=4 verify",
1512 Self::LmsShakeM24H10W8Verify => "LMS SHAKE M=24 H=10 W=8 verify",
1513 Self::LmsShakeM24H15W1Verify => "LMS SHAKE M=24 H=15 W=1 verify",
1514 Self::LmsShakeM24H15W2Verify => "LMS SHAKE M=24 H=15 W=2 verify",
1515 Self::LmsShakeM24H15W4Verify => "LMS SHAKE M=24 H=15 W=4 verify",
1516 Self::LmsShakeM24H15W8Verify => "LMS SHAKE M=24 H=15 W=8 verify",
1517 Self::LmsShakeM24H20W1Verify => "LMS SHAKE M=24 H=20 W=1 verify",
1518 Self::LmsShakeM24H20W2Verify => "LMS SHAKE M=24 H=20 W=2 verify",
1519 Self::LmsShakeM24H20W4Verify => "LMS SHAKE M=24 H=20 W=4 verify",
1520 Self::LmsShakeM24H20W8Verify => "LMS SHAKE M=24 H=20 W=8 verify",
1521 Self::LmsShakeM24H25W1Verify => "LMS SHAKE M=24 H=25 W=1 verify",
1522 Self::LmsShakeM24H25W2Verify => "LMS SHAKE M=24 H=25 W=2 verify",
1523 Self::LmsShakeM24H25W4Verify => "LMS SHAKE M=24 H=25 W=4 verify",
1524 Self::LmsShakeM24H25W8Verify => "LMS SHAKE M=24 H=25 W=8 verify",
1525 Self::XmssVerify => "XMSS verify",
1526 Self::Dh3072 => "DH-3072",
1527 };
1528 f.write_str(name)
1529 }
1530}
1531
1532pub fn require_allowed(service: Service) -> Result<(), Error> {
1548 let profile = active_profile();
1549 if is_allowed(profile, service) {
1550 Ok(())
1551 } else {
1552 Err(Error::AlgorithmRestricted { service })
1553 }
1554}
1555
1556const fn is_allowed(profile: AlgorithmProfile, service: Service) -> bool {
1559 match profile {
1560 AlgorithmProfile::Unrestricted => true,
1561 AlgorithmProfile::Cnsa2 => is_cnsa2_allowed(service),
1562 AlgorithmProfile::Cnsa1 => is_cnsa1_allowed(service),
1563 AlgorithmProfile::Migration => is_cnsa1_allowed(service) || is_cnsa2_allowed(service),
1569 }
1570}
1571
1572const fn is_lms_service(service: Service) -> bool {
1582 lms_block_contains(service as u16)
1583}
1584
1585const fn lms_block_contains(discriminant: u16) -> bool {
1596 discriminant >= Service::LmsSha256M32H5W1Verify as u16
1597 && discriminant <= Service::LmsShakeM24H25W8Verify as u16
1598 && discriminant & 1 == 1
1599}
1600
1601const LMS_SERVICES: [Service; 80] = [
1606 Service::LmsSha256M32H5W1Verify,
1608 Service::LmsSha256M32H5W2Verify,
1609 Service::LmsSha256M32H5W4Verify,
1610 Service::LmsSha256M32H5W8Verify,
1611 Service::LmsSha256M32H10W1Verify,
1612 Service::LmsSha256M32H10W2Verify,
1613 Service::LmsSha256M32H10W4Verify,
1614 Service::LmsSha256M32H10W8Verify,
1615 Service::LmsSha256M32H15W1Verify,
1616 Service::LmsSha256M32H15W2Verify,
1617 Service::LmsSha256M32H15W4Verify,
1618 Service::LmsSha256M32H15W8Verify,
1619 Service::LmsSha256M32H20W1Verify,
1620 Service::LmsSha256M32H20W2Verify,
1621 Service::LmsSha256M32H20W4Verify,
1622 Service::LmsSha256M32H20W8Verify,
1623 Service::LmsSha256M32H25W1Verify,
1624 Service::LmsSha256M32H25W2Verify,
1625 Service::LmsSha256M32H25W4Verify,
1626 Service::LmsSha256M32H25W8Verify,
1627 Service::LmsSha256M24H5W1Verify,
1629 Service::LmsSha256M24H5W2Verify,
1630 Service::LmsSha256M24H5W4Verify,
1631 Service::LmsSha256M24H5W8Verify,
1632 Service::LmsSha256M24H10W1Verify,
1633 Service::LmsSha256M24H10W2Verify,
1634 Service::LmsSha256M24H10W4Verify,
1635 Service::LmsSha256M24H10W8Verify,
1636 Service::LmsSha256M24H15W1Verify,
1637 Service::LmsSha256M24H15W2Verify,
1638 Service::LmsSha256M24H15W4Verify,
1639 Service::LmsSha256M24H15W8Verify,
1640 Service::LmsSha256M24H20W1Verify,
1641 Service::LmsSha256M24H20W2Verify,
1642 Service::LmsSha256M24H20W4Verify,
1643 Service::LmsSha256M24H20W8Verify,
1644 Service::LmsSha256M24H25W1Verify,
1645 Service::LmsSha256M24H25W2Verify,
1646 Service::LmsSha256M24H25W4Verify,
1647 Service::LmsSha256M24H25W8Verify,
1648 Service::LmsShakeM32H5W1Verify,
1650 Service::LmsShakeM32H5W2Verify,
1651 Service::LmsShakeM32H5W4Verify,
1652 Service::LmsShakeM32H5W8Verify,
1653 Service::LmsShakeM32H10W1Verify,
1654 Service::LmsShakeM32H10W2Verify,
1655 Service::LmsShakeM32H10W4Verify,
1656 Service::LmsShakeM32H10W8Verify,
1657 Service::LmsShakeM32H15W1Verify,
1658 Service::LmsShakeM32H15W2Verify,
1659 Service::LmsShakeM32H15W4Verify,
1660 Service::LmsShakeM32H15W8Verify,
1661 Service::LmsShakeM32H20W1Verify,
1662 Service::LmsShakeM32H20W2Verify,
1663 Service::LmsShakeM32H20W4Verify,
1664 Service::LmsShakeM32H20W8Verify,
1665 Service::LmsShakeM32H25W1Verify,
1666 Service::LmsShakeM32H25W2Verify,
1667 Service::LmsShakeM32H25W4Verify,
1668 Service::LmsShakeM32H25W8Verify,
1669 Service::LmsShakeM24H5W1Verify,
1671 Service::LmsShakeM24H5W2Verify,
1672 Service::LmsShakeM24H5W4Verify,
1673 Service::LmsShakeM24H5W8Verify,
1674 Service::LmsShakeM24H10W1Verify,
1675 Service::LmsShakeM24H10W2Verify,
1676 Service::LmsShakeM24H10W4Verify,
1677 Service::LmsShakeM24H10W8Verify,
1678 Service::LmsShakeM24H15W1Verify,
1679 Service::LmsShakeM24H15W2Verify,
1680 Service::LmsShakeM24H15W4Verify,
1681 Service::LmsShakeM24H15W8Verify,
1682 Service::LmsShakeM24H20W1Verify,
1683 Service::LmsShakeM24H20W2Verify,
1684 Service::LmsShakeM24H20W4Verify,
1685 Service::LmsShakeM24H20W8Verify,
1686 Service::LmsShakeM24H25W1Verify,
1687 Service::LmsShakeM24H25W2Verify,
1688 Service::LmsShakeM24H25W4Verify,
1689 Service::LmsShakeM24H25W8Verify,
1690];
1691
1692#[allow(clippy::indexing_slicing, clippy::arithmetic_side_effects)]
1698const _: () = {
1699 let mut i = 0;
1700 let mut slot: u16 = 501;
1701 while i < LMS_SERVICES.len() {
1702 let d = LMS_SERVICES[i] as u16;
1703 assert!(d == slot, "LMS service out of its slot");
1704 assert!(lms_block_contains(d), "LMS service outside the LMS block");
1705 slot += 2;
1706 i += 1;
1707 }
1708 assert!(
1709 LMS_SERVICES[79] as u16 == 659,
1710 "LMS block does not end at 659"
1711 );
1712};
1713
1714const fn is_cnsa2_allowed(service: Service) -> bool {
1726 matches!(
1727 service,
1728 Service::Aes256Ecb
1730 | Service::Aes256Cbc
1731 | Service::Aes256Ctr
1732 | Service::Aes256Gcm
1733 | Service::Aes256Ccm
1734 | Service::Aes256Kw
1735 | Service::Aes256Kwp
1736 | Service::Aes256
1737 | Service::Sha384
1739 | Service::Sha512
1740 | Service::HmacSha384
1746 | Service::HmacSha512
1747 | Service::CmacAes256
1749 | Service::CtrDrbgAes256
1751 | Service::HashDrbgSha384
1752 | Service::HashDrbgSha512
1753 | Service::HmacDrbgSha384
1754 | Service::HmacDrbgSha512
1755 | Service::HkdfSha384
1757 | Service::HkdfSha512
1758 | Service::KbkdfHmacSha384
1759 | Service::KbkdfHmacSha512
1760 | Service::KbkdfCmacAes256
1761 | Service::MlKem1024Encaps
1766 | Service::MlKem1024Decaps
1767 | Service::MlKem1024Keygen
1768 | Service::MlDsa87Sign
1769 | Service::MlDsa87Verify
1770 | Service::MlDsa87Keygen
1771 | Service::XmssVerify
1774 )
1775 || is_lms_service(service)
1782}
1783
1784const fn is_cnsa1_allowed(service: Service) -> bool {
1811 matches!(
1812 service,
1813 Service::Aes256
1815 | Service::Aes256Ecb
1816 | Service::Aes256Cbc
1817 | Service::Aes256Ctr
1818 | Service::Aes256Gcm
1819 | Service::Aes256Ccm
1820 | Service::Aes256Kw
1821 | Service::Aes256Kwp
1822 | Service::EcdhP384
1824 | Service::EcdsaP384Keygen
1826 | Service::EcdsaP384Sign
1827 | Service::EcdsaP384Verify
1828 | Service::Sha384
1830 | Service::Dh3072
1832 | Service::RsaKeygen3072
1834 | Service::RsaKeygen4096
1835 | Service::RsaOaep3072
1836 | Service::RsaOaep4096
1837 | Service::RsaPkcs1v15Sign3072
1839 | Service::RsaPkcs1v15Sign4096
1840 | Service::RsaPkcs1v15Verify3072
1841 | Service::RsaPkcs1v15Verify4096
1842 | Service::RsaPssSign3072
1843 | Service::RsaPssSign4096
1844 | Service::RsaPssVerify3072
1845 | Service::RsaPssVerify4096
1846 | Service::CtrDrbgAes256
1850 | Service::HashDrbgSha384
1851 | Service::HmacDrbgSha384
1852 | Service::HmacSha384
1853 | Service::CmacAes256
1854 | Service::HkdfSha384
1855 | Service::KbkdfHmacSha384
1856 | Service::KbkdfCmacAes256
1857 )
1858}
1859
1860#[cfg(test)]
1889pub(crate) mod state_lock {
1890 use super::{AlgorithmProfile, Ordering, PROFILE, STATE, State};
1891
1892 static STATE_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
1893
1894 #[must_use]
1897 pub(crate) struct StateGuard(
1898 #[allow(dead_code)]
1901 std::sync::MutexGuard<'static, ()>,
1902 );
1903
1904 impl StateGuard {
1905 #[allow(clippy::unused_self)]
1909 pub(crate) fn reset(&self) {
1910 STATE.store(State::PowerOff as u64, Ordering::Release);
1911 PROFILE.store(AlgorithmProfile::Unrestricted as u8, Ordering::Release);
1912 }
1913 }
1914
1915 pub(crate) fn reset_for_test() -> StateGuard {
1932 let guard = StateGuard(
1933 STATE_LOCK
1934 .lock()
1935 .unwrap_or_else(std::sync::PoisonError::into_inner),
1936 );
1937 guard.reset();
1938 guard
1939 }
1940}
1941
1942#[cfg(test)]
1943extern crate alloc;
1944
1945#[cfg(test)]
1946extern crate std;
1947
1948#[cfg(test)]
1949#[allow(clippy::unwrap_used, clippy::panic)]
1950mod tests {
1951 use super::state_lock::reset_for_test;
1952 use super::{
1953 AlgorithmProfile, Error, KatEntry, LMS_SERVICES, SelfTest, SelfTestFailure, Service, State,
1954 active_profile, enter_error_state, initialize_with_profile, initialize_with_tests,
1955 is_allowed, is_lms_service, is_operational, lms_block_contains, require_allowed,
1956 require_operational, require_self_test_started, state,
1957 };
1958 use alloc::string::ToString;
1959 use core::sync::atomic::Ordering;
1960
1961 const UNSIGNED_TEST_BINARY: &[KatEntry] = &[KatEntry {
1969 name: "integrity not verifiable in an unsigned test binary",
1970 run: || Ok(()),
1971 }];
1972
1973 #[allow(clippy::unnecessary_wraps)]
1977 fn always_pass() -> Result<(), SelfTestFailure> {
1978 Ok(())
1979 }
1980 fn always_fail() -> Result<(), SelfTestFailure> {
1981 Err(SelfTestFailure)
1982 }
1983
1984 static UNREACHED_RUNS: core::sync::atomic::AtomicUsize =
1986 core::sync::atomic::AtomicUsize::new(0);
1987
1988 #[allow(clippy::unnecessary_wraps)]
1990 fn count_unreached() -> Result<(), SelfTestFailure> {
1991 UNREACHED_RUNS.fetch_add(1, Ordering::AcqRel);
1992 Ok(())
1993 }
1994
1995 #[test]
2000 fn initial_state_is_power_off() {
2001 let _guard = reset_for_test();
2002 assert_eq!(state(), State::PowerOff);
2003 assert!(!is_operational());
2004 }
2005
2006 #[test]
2007 fn initialize_transitions_to_operational() {
2008 let _guard = reset_for_test();
2009 initialize_with_tests(UNSIGNED_TEST_BINARY, &[]).unwrap();
2010 assert_eq!(state(), State::Operational);
2011 assert!(is_operational());
2012 require_operational().unwrap();
2013 }
2014
2015 #[test]
2016 fn second_initialize_reports_already_initialized() {
2017 let _guard = reset_for_test();
2018 initialize_with_tests(UNSIGNED_TEST_BINARY, &[]).unwrap();
2019 match initialize_with_tests(UNSIGNED_TEST_BINARY, &[]) {
2020 Err(Error::AlreadyInitialized) => {}
2021 other => panic!("expected AlreadyInitialized, got {other:?}"),
2022 }
2023 }
2024
2025 #[test]
2026 fn require_operational_rejects_power_off() {
2027 let _guard = reset_for_test();
2028 match require_operational() {
2029 Err(Error::NotOperational {
2030 current: State::PowerOff,
2031 }) => {}
2032 other => panic!("expected NotOperational{{PowerOff}}, got {other:?}"),
2033 }
2034 }
2035
2036 #[test]
2037 fn enter_error_state_is_terminal_for_require_operational() {
2038 let _guard = reset_for_test();
2039 initialize_with_tests(UNSIGNED_TEST_BINARY, &[]).unwrap();
2040 assert!(is_operational());
2041 enter_error_state("unit-test forced failure");
2042 assert_eq!(state(), State::Error);
2043 match require_operational() {
2044 Err(Error::NotOperational {
2045 current: State::Error,
2046 }) => {}
2047 other => panic!("expected NotOperational{{Error}}, got {other:?}"),
2048 }
2049 }
2050
2051 fn guard_admits_self_test() -> Result<(), SelfTestFailure> {
2054 if state() != State::SelfTest {
2055 return Err(SelfTestFailure);
2056 }
2057 require_self_test_started().map_err(|_| SelfTestFailure)
2058 }
2059
2060 #[test]
2061 fn require_self_test_started_rejects_power_off() {
2062 let _guard = reset_for_test();
2063 assert_eq!(
2064 require_self_test_started(),
2065 Err(Error::NotOperational {
2066 current: State::PowerOff
2067 })
2068 );
2069 }
2070
2071 #[test]
2072 fn require_self_test_started_rejects_error_after_failed_init() {
2073 let _guard = reset_for_test();
2074 let tests = [KatEntry {
2075 name: "dummy-fail",
2076 run: always_fail,
2077 }];
2078 assert!(initialize_with_tests(UNSIGNED_TEST_BINARY, &tests).is_err());
2079 assert_eq!(state(), State::Error);
2080 assert_eq!(
2081 require_self_test_started(),
2082 Err(Error::NotOperational {
2083 current: State::Error
2084 })
2085 );
2086 }
2087
2088 #[test]
2089 fn require_self_test_started_rejects_integrity_unverified() {
2090 let _guard = reset_for_test();
2091 assert_eq!(
2092 initialize_with_tests(&[], &[]),
2093 Err(Error::IntegrityNotAttested)
2094 );
2095 assert_eq!(state(), State::IntegrityUnverified);
2096 assert_eq!(
2097 require_self_test_started(),
2098 Err(Error::NotOperational {
2099 current: State::IntegrityUnverified
2100 })
2101 );
2102 }
2103
2104 #[test]
2107 fn initialize_after_integrity_unverified_is_already_failed() {
2108 let _guard = reset_for_test();
2109 assert_eq!(
2110 initialize_with_tests(&[], &[]),
2111 Err(Error::IntegrityNotAttested)
2112 );
2113 assert_eq!(state(), State::IntegrityUnverified);
2114 let counting = [KatEntry {
2115 name: "counts its runs",
2116 run: count_unreached,
2117 }];
2118 UNREACHED_RUNS.store(0, Ordering::Release);
2119 assert_eq!(
2120 initialize_with_tests(UNSIGNED_TEST_BINARY, &counting),
2121 Err(Error::AlreadyFailed)
2122 );
2123 assert_eq!(UNREACHED_RUNS.load(Ordering::Acquire), 0);
2124 assert_eq!(state(), State::IntegrityUnverified);
2125 }
2126
2127 #[test]
2128 fn require_self_test_started_admits_self_test_and_operational() {
2129 let _guard = reset_for_test();
2130 let tests = [KatEntry {
2131 name: "guard admits SelfTest",
2132 run: guard_admits_self_test,
2133 }];
2134 initialize_with_tests(UNSIGNED_TEST_BINARY, &tests).unwrap();
2135 assert_eq!(state(), State::Operational);
2136 assert_eq!(require_self_test_started(), Ok(()));
2137 }
2138
2139 #[test]
2142 fn a_malformed_state_word_reads_as_error() {
2143 let guard = reset_for_test();
2144 let owner = 0x1234_u64 << super::STATE_BITS;
2145 let malformed = [
2146 State::SelfTest as u64,
2148 State::Operational as u64 | owner,
2150 State::PowerOff as u64 | owner,
2151 State::IntegrityUnverified as u64 | owner,
2152 5,
2154 0xff,
2155 0xff | owner,
2156 ];
2157 assert_eq!(malformed.len(), 7);
2158 for word in malformed {
2159 guard.reset();
2160 super::STATE.store(word, Ordering::Release);
2161 assert_eq!(state(), State::Error, "word {word:#x}");
2162 assert!(!is_operational(), "word {word:#x}");
2163 assert_eq!(
2164 require_operational(),
2165 Err(Error::NotOperational {
2166 current: State::Error
2167 }),
2168 "word {word:#x}"
2169 );
2170 assert_eq!(
2171 require_self_test_started(),
2172 Err(Error::NotOperational {
2173 current: State::Error
2174 }),
2175 "word {word:#x}"
2176 );
2177 }
2178 guard.reset();
2180 assert_eq!(state(), State::PowerOff);
2181 super::STATE.store(State::Operational as u64, Ordering::Release);
2182 assert_eq!(state(), State::Operational);
2183 }
2184
2185 static RELEASE_FAILING_WINNER: core::sync::atomic::AtomicBool =
2188 core::sync::atomic::AtomicBool::new(false);
2189 static FAILING_WINNER_PAUSED: core::sync::atomic::AtomicBool =
2191 core::sync::atomic::AtomicBool::new(false);
2192
2193 fn wait_until(what: &str, done: impl Fn() -> bool) {
2195 let start = std::time::Instant::now();
2196 while !done() {
2197 assert!(
2198 start.elapsed() < std::time::Duration::from_secs(30),
2199 "timed out waiting for {what}"
2200 );
2201 std::thread::yield_now();
2202 }
2203 }
2204
2205 fn paused_then_fails() -> Result<(), SelfTestFailure> {
2206 FAILING_WINNER_PAUSED.store(true, Ordering::Release);
2207 wait_until("the release of the failing winner", || {
2208 RELEASE_FAILING_WINNER.load(Ordering::Acquire)
2209 });
2210 Err(SelfTestFailure)
2211 }
2212
2213 #[test]
2216 fn loser_waits_then_gets_already_failed() {
2217 let _guard = reset_for_test();
2218 FAILING_WINNER_PAUSED.store(false, Ordering::Release);
2219 RELEASE_FAILING_WINNER.store(false, Ordering::Release);
2220 let waiting_before = super::WAITING_LOSERS.load(Ordering::Acquire);
2221 let winner = std::thread::spawn(|| {
2222 let tests = [KatEntry {
2223 name: "pauses, then fails",
2224 run: paused_then_fails,
2225 }];
2226 initialize_with_tests(UNSIGNED_TEST_BINARY, &tests)
2227 });
2228 wait_until("the winner to pause", || {
2229 FAILING_WINNER_PAUSED.load(Ordering::Acquire)
2230 });
2231 let loser = std::thread::spawn(|| initialize_with_tests(UNSIGNED_TEST_BINARY, &[]));
2232 wait_until("the loser to enter its wait", || {
2233 super::WAITING_LOSERS.load(Ordering::Acquire) > waiting_before
2234 });
2235 assert!(!loser.is_finished());
2236 RELEASE_FAILING_WINNER.store(true, Ordering::Release);
2237 assert_eq!(
2238 winner.join().unwrap(),
2239 Err(Error::SelfTestFailed {
2240 test: "pauses, then fails"
2241 })
2242 );
2243 assert_eq!(loser.join().unwrap(), Err(Error::AlreadyFailed));
2244 assert_eq!(state(), State::Error);
2245 }
2246
2247 #[test]
2249 fn the_owner_id_filter_admits_only_recordable_ids() {
2250 assert_eq!(super::owner_id(None), None);
2251 assert_eq!(super::owner_id(Some(0)), None);
2252 assert_eq!(super::owner_id(Some(1)), Some(1));
2253 assert_eq!(super::owner_id(Some((1 << 56) - 1)), Some((1 << 56) - 1));
2254 assert_eq!(super::owner_id(Some(1 << 56)), None);
2255 assert_eq!(super::owner_id(Some(u64::MAX)), None);
2256 }
2257
2258 #[test]
2261 fn no_recordable_thread_id_latches_error_with_its_cause() {
2262 let guard = reset_for_test();
2263 let tests = [KatEntry {
2264 name: "must not run",
2265 run: always_fail,
2266 }];
2267 for raw in [None, Some(0), Some(1 << 56)] {
2268 guard.reset();
2269 assert_eq!(
2270 super::initialize_as(
2271 super::owner_id(raw),
2272 UNSIGNED_TEST_BINARY,
2273 &tests,
2274 AlgorithmProfile::Unrestricted
2275 ),
2276 Err(Error::SelfTestFailed {
2277 test: super::THREAD_ID_CHECK
2278 }),
2279 "raw id {raw:?}"
2280 );
2281 assert_eq!(state(), State::Error);
2282 assert_eq!(
2283 initialize_with_tests(UNSIGNED_TEST_BINARY, &[]),
2284 Err(Error::AlreadyFailed)
2285 );
2286 }
2287 }
2288
2289 #[test]
2290 fn state_display_is_stable() {
2291 assert_eq!(State::PowerOff.to_string(), "PowerOff");
2294 assert_eq!(State::SelfTest.to_string(), "SelfTest");
2295 assert_eq!(State::Operational.to_string(), "Operational");
2296 assert_eq!(State::Error.to_string(), "Error");
2297 }
2298
2299 #[test]
2300 fn registry_runs_passing_tests_and_reaches_operational() {
2301 let _guard = reset_for_test();
2302 let tests = [
2303 KatEntry {
2304 name: "dummy-pass-a",
2305 run: always_pass,
2306 },
2307 KatEntry {
2308 name: "dummy-pass-b",
2309 run: always_pass,
2310 },
2311 ];
2312 initialize_with_tests(UNSIGNED_TEST_BINARY, &tests).unwrap();
2313 assert_eq!(state(), State::Operational);
2314 }
2315
2316 #[test]
2317 fn registry_failing_test_latches_error_and_returns_name() {
2318 let _guard = reset_for_test();
2319 let tests = [
2320 KatEntry {
2321 name: "dummy-pass",
2322 run: always_pass,
2323 },
2324 KatEntry {
2325 name: "dummy-fail",
2326 run: always_fail,
2327 },
2328 KatEntry {
2330 name: "dummy-unreached",
2331 run: count_unreached,
2332 },
2333 ];
2334 UNREACHED_RUNS.store(0, Ordering::Release);
2335 match initialize_with_tests(UNSIGNED_TEST_BINARY, &tests) {
2336 Err(Error::SelfTestFailed { test: "dummy-fail" }) => {}
2337 other => panic!("expected SelfTestFailed{{dummy-fail}}, got {other:?}"),
2338 }
2339 assert_eq!(UNREACHED_RUNS.load(Ordering::Acquire), 0);
2340 assert_eq!(state(), State::Error);
2341 assert!(matches!(
2343 require_operational(),
2344 Err(Error::NotOperational {
2345 current: State::Error
2346 })
2347 ));
2348 }
2349
2350 struct DummyTest;
2353 impl SelfTest for DummyTest {
2354 const NAME: &'static str = "dummy-trait-vector";
2355 fn run() -> Result<(), SelfTestFailure> {
2356 Ok(())
2357 }
2358 }
2359
2360 #[test]
2361 fn dummy_self_test_trait_is_usable() {
2362 assert_eq!(DummyTest::NAME, "dummy-trait-vector");
2363 DummyTest::run().unwrap();
2364 }
2365
2366 #[test]
2371 fn default_profile_is_unrestricted() {
2372 let _guard = reset_for_test();
2373 assert_eq!(active_profile(), AlgorithmProfile::Unrestricted);
2374 }
2375
2376 #[test]
2377 fn initialize_with_profile_sets_cnsa2() {
2378 let _guard = reset_for_test();
2379 initialize_with_profile(UNSIGNED_TEST_BINARY, &[], AlgorithmProfile::Cnsa2).unwrap();
2380 assert_eq!(active_profile(), AlgorithmProfile::Cnsa2);
2381 assert!(is_operational());
2382 }
2383
2384 #[test]
2387 fn a_second_call_does_not_change_the_profile() {
2388 let _guard = reset_for_test();
2389 initialize_with_profile(UNSIGNED_TEST_BINARY, &[], AlgorithmProfile::Cnsa2).unwrap();
2390 match initialize_with_profile(UNSIGNED_TEST_BINARY, &[], AlgorithmProfile::Unrestricted) {
2391 Err(Error::AlreadyInitialized) => {}
2392 other => panic!("expected AlreadyInitialized, got {other:?}"),
2393 }
2394 assert_eq!(active_profile(), AlgorithmProfile::Cnsa2);
2395 match initialize_with_tests(UNSIGNED_TEST_BINARY, &[]) {
2396 Err(Error::AlreadyInitialized) => {}
2397 other => panic!("expected AlreadyInitialized, got {other:?}"),
2398 }
2399 assert_eq!(active_profile(), AlgorithmProfile::Cnsa2);
2400 }
2401
2402 static RACER_CALLED: core::sync::atomic::AtomicBool =
2405 core::sync::atomic::AtomicBool::new(false);
2406
2407 #[allow(clippy::unnecessary_wraps)]
2408 fn wait_for_racer() -> Result<(), SelfTestFailure> {
2409 while !RACER_CALLED.load(Ordering::Acquire) {
2410 std::thread::yield_now();
2411 }
2412 std::thread::sleep(std::time::Duration::from_millis(50));
2415 Ok(())
2416 }
2417
2418 #[test]
2421 fn a_racing_call_does_not_change_the_profile() {
2422 let _guard = reset_for_test();
2423 RACER_CALLED.store(false, Ordering::Release);
2424 let tests = [KatEntry {
2425 name: "waits for the racing call",
2426 run: wait_for_racer,
2427 }];
2428 let winner = std::thread::spawn(move || {
2429 initialize_with_profile(UNSIGNED_TEST_BINARY, &tests, AlgorithmProfile::Cnsa2)
2430 });
2431 while state() != State::SelfTest {
2432 std::thread::yield_now();
2433 }
2434 RACER_CALLED.store(true, Ordering::Release);
2435 let racer =
2436 initialize_with_profile(UNSIGNED_TEST_BINARY, &[], AlgorithmProfile::Unrestricted);
2437 let winner = winner.join().unwrap();
2438 assert!(winner.is_ok(), "the first call failed: {winner:?}");
2439 match racer {
2440 Err(Error::AlreadyInitialized) => {}
2441 other => panic!("expected AlreadyInitialized, got {other:?}"),
2442 }
2443 assert_eq!(active_profile(), AlgorithmProfile::Cnsa2);
2444 }
2445
2446 #[test]
2449 fn a_failed_initialisation_leaves_the_default_profile() {
2450 let guard = reset_for_test();
2451 let failing = [KatEntry {
2452 name: "always fails",
2453 run: always_fail,
2454 }];
2455 match initialize_with_profile(UNSIGNED_TEST_BINARY, &failing, AlgorithmProfile::Cnsa2) {
2456 Err(Error::SelfTestFailed { .. }) => {}
2457 other => panic!("expected SelfTestFailed, got {other:?}"),
2458 }
2459 assert_eq!(state(), State::Error);
2460 assert_eq!(active_profile(), AlgorithmProfile::Unrestricted);
2461
2462 guard.reset();
2463 match initialize_with_profile(&[], &[], AlgorithmProfile::Cnsa1) {
2464 Err(Error::IntegrityNotAttested) => {}
2465 other => panic!("expected IntegrityNotAttested, got {other:?}"),
2466 }
2467 assert_eq!(active_profile(), AlgorithmProfile::Unrestricted);
2468 }
2469
2470 #[test]
2471 fn initialize_with_profile_sets_cnsa1() {
2472 let _guard = reset_for_test();
2473 initialize_with_profile(UNSIGNED_TEST_BINARY, &[], AlgorithmProfile::Cnsa1).unwrap();
2474 assert_eq!(active_profile(), AlgorithmProfile::Cnsa1);
2475 assert!(is_operational());
2476 }
2477
2478 #[test]
2479 fn unrestricted_allows_all_services() {
2480 let spot = [
2482 Service::Sha1,
2483 Service::Sha256,
2484 Service::Aes128Ecb,
2485 Service::Aes256Gcm,
2486 Service::CtrDrbgAes128,
2487 Service::EcdsaP256Sign,
2488 Service::Ed25519Sign,
2489 Service::RsaPssSign2048,
2490 Service::MlKem1024Encaps,
2491 Service::LmsSha256M32H10W4Verify,
2492 Service::Dh3072,
2493 Service::SlhDsaSha2256sSign,
2494 ];
2495 for svc in spot {
2496 assert!(
2497 is_allowed(AlgorithmProfile::Unrestricted, svc),
2498 "{svc} should be allowed in Unrestricted"
2499 );
2500 }
2501 }
2502
2503 #[test]
2504 fn cnsa2_allows_aes256_and_pq() {
2505 let allowed = [
2506 Service::Aes256Gcm,
2507 Service::Aes256Cbc,
2508 Service::Sha384,
2509 Service::Sha512,
2510 Service::HmacSha384,
2511 Service::HmacSha512,
2512 Service::CtrDrbgAes256,
2513 Service::HashDrbgSha384,
2514 Service::HmacDrbgSha512,
2515 Service::MlKem1024Encaps,
2516 Service::MlKem1024Decaps,
2517 Service::MlKem1024Keygen,
2518 Service::MlDsa87Sign,
2519 Service::MlDsa87Verify,
2520 Service::MlDsa87Keygen,
2521 Service::LmsSha256M32H10W4Verify,
2522 Service::XmssVerify,
2523 ];
2524 for svc in allowed {
2525 assert!(
2526 is_allowed(AlgorithmProfile::Cnsa2, svc),
2527 "{svc} should be allowed in CNSA 2.0"
2528 );
2529 }
2530 }
2531
2532 #[test]
2540 fn cnsa2_refuses_the_keccak_family() {
2541 let refused = [
2542 Service::Shake256,
2543 Service::CShake256,
2544 Service::Kmac256,
2545 Service::KmacXof256,
2546 Service::TupleHash256,
2547 Service::TupleHashXof256,
2548 Service::ParallelHash256,
2549 Service::ParallelHashXof256,
2550 Service::Sha3_384,
2551 Service::Sha3_512,
2552 Service::HmacSha3_384,
2553 Service::HmacSha3_512,
2554 ];
2555 for svc in refused {
2556 assert!(
2557 !is_allowed(AlgorithmProfile::Cnsa2, svc),
2558 "{svc} is not in CNSA 2.0 and must be refused"
2559 );
2560 }
2561 assert!(is_allowed(AlgorithmProfile::Cnsa2, Service::Sha384));
2564 assert!(is_allowed(
2566 AlgorithmProfile::Cnsa2,
2567 Service::MlKem1024Encaps
2568 ));
2569 }
2570
2571 #[test]
2572 fn cnsa2_blocks_classical_and_small_keys() {
2573 let blocked = [
2574 Service::Sha1,
2575 Service::Sha224,
2576 Service::Sha256,
2577 Service::Sha512_224,
2578 Service::Sha512_256,
2579 Service::Sha3_224,
2580 Service::Sha3_256,
2581 Service::Shake128,
2582 Service::Kmac128,
2583 Service::Aes128Ecb,
2584 Service::Aes128Gcm,
2585 Service::Aes192Cbc,
2586 Service::CtrDrbgAes128,
2587 Service::CtrDrbgAes192,
2588 Service::HashDrbgSha256,
2589 Service::HmacDrbgSha256,
2590 Service::HmacSha1,
2591 Service::HmacSha256,
2592 Service::CmacAes128,
2593 Service::CmacAes192,
2594 Service::EcdsaP256Sign,
2595 Service::EcdsaP256Verify,
2596 Service::EcdhP256,
2597 Service::Ed25519Sign,
2598 Service::Ed25519Verify,
2599 Service::RsaPssSign2048,
2600 Service::RsaOaep2048,
2601 Service::SlhDsaSha2128sSign,
2603 Service::MlDsa44Sign,
2604 Service::MlDsa44Verify,
2605 Service::MlDsa44Keygen,
2606 Service::MlDsa65Sign,
2607 Service::MlDsa65Verify,
2608 Service::MlDsa65Keygen,
2609 ];
2610 for svc in blocked {
2611 assert!(
2612 !is_allowed(AlgorithmProfile::Cnsa2, svc),
2613 "{svc} should be BLOCKED in CNSA 2.0"
2614 );
2615 }
2616 }
2617
2618 const ALL_SERVICES: [Service; 256] = [
2633 Service::Sha1,
2634 Service::Sha224,
2635 Service::Sha256,
2636 Service::Sha384,
2637 Service::Sha512,
2638 Service::Sha512_224,
2639 Service::Sha512_256,
2640 Service::Sha3_224,
2641 Service::Sha3_256,
2642 Service::Sha3_384,
2643 Service::Sha3_512,
2644 Service::Shake128,
2645 Service::Shake256,
2646 Service::CShake128,
2647 Service::CShake256,
2648 Service::Kmac128,
2649 Service::Kmac256,
2650 Service::KmacXof128,
2651 Service::KmacXof256,
2652 Service::TupleHash128,
2653 Service::TupleHash256,
2654 Service::TupleHashXof128,
2655 Service::TupleHashXof256,
2656 Service::ParallelHash128,
2657 Service::ParallelHash256,
2658 Service::ParallelHashXof128,
2659 Service::ParallelHashXof256,
2660 Service::HmacSha1,
2661 Service::HmacSha224,
2662 Service::HmacSha256,
2663 Service::HmacSha384,
2664 Service::HmacSha512,
2665 Service::HmacSha512_224,
2666 Service::HmacSha512_256,
2667 Service::HmacSha3_224,
2668 Service::HmacSha3_256,
2669 Service::HmacSha3_384,
2670 Service::HmacSha3_512,
2671 Service::CmacAes128,
2672 Service::CmacAes192,
2673 Service::CmacAes256,
2674 Service::Aes128Ecb,
2675 Service::Aes128Cbc,
2676 Service::Aes128Ctr,
2677 Service::Aes128Gcm,
2678 Service::Aes128Ccm,
2679 Service::Aes128Kw,
2680 Service::Aes128Kwp,
2681 Service::Aes192Ecb,
2682 Service::Aes192Cbc,
2683 Service::Aes192Ctr,
2684 Service::Aes192Gcm,
2685 Service::Aes192Ccm,
2686 Service::Aes192Kw,
2687 Service::Aes192Kwp,
2688 Service::Aes256Ecb,
2689 Service::Aes256Cbc,
2690 Service::Aes256Ctr,
2691 Service::Aes256Gcm,
2692 Service::Aes256Ccm,
2693 Service::Aes256Kw,
2694 Service::Aes256Kwp,
2695 Service::Aes128,
2696 Service::Aes192,
2697 Service::Aes256,
2698 Service::CtrDrbgAes128,
2699 Service::CtrDrbgAes192,
2700 Service::CtrDrbgAes256,
2701 Service::HashDrbgSha256,
2702 Service::HashDrbgSha384,
2703 Service::HashDrbgSha512,
2704 Service::HmacDrbgSha256,
2705 Service::HmacDrbgSha384,
2706 Service::HmacDrbgSha512,
2707 Service::HkdfSha1,
2708 Service::HkdfSha256,
2709 Service::HkdfSha384,
2710 Service::HkdfSha512,
2711 Service::KbkdfHmacSha256,
2712 Service::KbkdfHmacSha384,
2713 Service::KbkdfHmacSha512,
2714 Service::KbkdfCmacAes128,
2715 Service::KbkdfCmacAes192,
2716 Service::KbkdfCmacAes256,
2717 Service::Pbkdf2HmacSha1,
2718 Service::Pbkdf2HmacSha224,
2719 Service::Pbkdf2HmacSha256,
2720 Service::Pbkdf2HmacSha384,
2721 Service::Pbkdf2HmacSha512,
2722 Service::RsaKeygen2048,
2723 Service::RsaPkcs1v15Sign2048,
2724 Service::RsaPssSign2048,
2725 Service::RsaOaep2048,
2726 Service::RsaPkcs1v15Verify2048,
2727 Service::RsaPssVerify2048,
2728 Service::RsaKeygen3072,
2729 Service::RsaPkcs1v15Sign3072,
2730 Service::RsaPssSign3072,
2731 Service::RsaOaep3072,
2732 Service::RsaPkcs1v15Verify3072,
2733 Service::RsaPssVerify3072,
2734 Service::RsaKeygen4096,
2735 Service::RsaPkcs1v15Sign4096,
2736 Service::RsaPssSign4096,
2737 Service::RsaOaep4096,
2738 Service::RsaPkcs1v15Verify4096,
2739 Service::RsaPssVerify4096,
2740 Service::EcdsaP256Sign,
2741 Service::EcdsaP256Verify,
2742 Service::EcdsaP256Keygen,
2743 Service::EcdsaP384Sign,
2744 Service::EcdsaP384Verify,
2745 Service::EcdsaP384Keygen,
2746 Service::EcdhP256,
2747 Service::EcdhP384,
2748 Service::Ed25519Sign,
2749 Service::Ed25519Verify,
2750 Service::Ed25519Keygen,
2751 Service::Tls12Kdf,
2752 Service::Tls13Kdf,
2753 Service::MlKem1024Encaps,
2754 Service::MlKem1024Decaps,
2755 Service::MlKem1024Keygen,
2756 Service::MlKem512Encaps,
2757 Service::MlKem512Decaps,
2758 Service::MlKem512Keygen,
2759 Service::MlKem768Encaps,
2760 Service::MlKem768Decaps,
2761 Service::MlKem768Keygen,
2762 Service::MlDsa87Sign,
2763 Service::MlDsa87Verify,
2764 Service::MlDsa87Keygen,
2765 Service::MlDsa44Sign,
2766 Service::MlDsa44Verify,
2767 Service::MlDsa44Keygen,
2768 Service::MlDsa65Sign,
2769 Service::MlDsa65Verify,
2770 Service::MlDsa65Keygen,
2771 Service::SlhDsaSha2256sKeygen,
2772 Service::SlhDsaSha2256sSign,
2773 Service::SlhDsaSha2256sVerify,
2774 Service::SlhDsaSha2128sKeygen,
2775 Service::SlhDsaSha2128sSign,
2776 Service::SlhDsaSha2128sVerify,
2777 Service::SlhDsaSha2128fKeygen,
2778 Service::SlhDsaSha2128fSign,
2779 Service::SlhDsaSha2128fVerify,
2780 Service::SlhDsaSha2192sKeygen,
2781 Service::SlhDsaSha2192sSign,
2782 Service::SlhDsaSha2192sVerify,
2783 Service::SlhDsaSha2192fKeygen,
2784 Service::SlhDsaSha2192fSign,
2785 Service::SlhDsaSha2192fVerify,
2786 Service::SlhDsaSha2256fKeygen,
2787 Service::SlhDsaSha2256fSign,
2788 Service::SlhDsaSha2256fVerify,
2789 Service::SlhDsaShake128sKeygen,
2790 Service::SlhDsaShake128sSign,
2791 Service::SlhDsaShake128sVerify,
2792 Service::SlhDsaShake128fKeygen,
2793 Service::SlhDsaShake128fSign,
2794 Service::SlhDsaShake128fVerify,
2795 Service::SlhDsaShake192sKeygen,
2796 Service::SlhDsaShake192sSign,
2797 Service::SlhDsaShake192sVerify,
2798 Service::SlhDsaShake192fKeygen,
2799 Service::SlhDsaShake192fSign,
2800 Service::SlhDsaShake192fVerify,
2801 Service::SlhDsaShake256sKeygen,
2802 Service::SlhDsaShake256sSign,
2803 Service::SlhDsaShake256sVerify,
2804 Service::SlhDsaShake256fKeygen,
2805 Service::SlhDsaShake256fSign,
2806 Service::SlhDsaShake256fVerify,
2807 Service::LmsSha256M32H5W1Verify,
2808 Service::LmsSha256M32H5W2Verify,
2809 Service::LmsSha256M32H5W4Verify,
2810 Service::LmsSha256M32H5W8Verify,
2811 Service::LmsSha256M32H10W1Verify,
2812 Service::LmsSha256M32H10W2Verify,
2813 Service::LmsSha256M32H10W4Verify,
2814 Service::LmsSha256M32H10W8Verify,
2815 Service::LmsSha256M32H15W1Verify,
2816 Service::LmsSha256M32H15W2Verify,
2817 Service::LmsSha256M32H15W4Verify,
2818 Service::LmsSha256M32H15W8Verify,
2819 Service::LmsSha256M32H20W1Verify,
2820 Service::LmsSha256M32H20W2Verify,
2821 Service::LmsSha256M32H20W4Verify,
2822 Service::LmsSha256M32H20W8Verify,
2823 Service::LmsSha256M32H25W1Verify,
2824 Service::LmsSha256M32H25W2Verify,
2825 Service::LmsSha256M32H25W4Verify,
2826 Service::LmsSha256M32H25W8Verify,
2827 Service::LmsSha256M24H5W1Verify,
2828 Service::LmsSha256M24H5W2Verify,
2829 Service::LmsSha256M24H5W4Verify,
2830 Service::LmsSha256M24H5W8Verify,
2831 Service::LmsSha256M24H10W1Verify,
2832 Service::LmsSha256M24H10W2Verify,
2833 Service::LmsSha256M24H10W4Verify,
2834 Service::LmsSha256M24H10W8Verify,
2835 Service::LmsSha256M24H15W1Verify,
2836 Service::LmsSha256M24H15W2Verify,
2837 Service::LmsSha256M24H15W4Verify,
2838 Service::LmsSha256M24H15W8Verify,
2839 Service::LmsSha256M24H20W1Verify,
2840 Service::LmsSha256M24H20W2Verify,
2841 Service::LmsSha256M24H20W4Verify,
2842 Service::LmsSha256M24H20W8Verify,
2843 Service::LmsSha256M24H25W1Verify,
2844 Service::LmsSha256M24H25W2Verify,
2845 Service::LmsSha256M24H25W4Verify,
2846 Service::LmsSha256M24H25W8Verify,
2847 Service::LmsShakeM32H5W1Verify,
2848 Service::LmsShakeM32H5W2Verify,
2849 Service::LmsShakeM32H5W4Verify,
2850 Service::LmsShakeM32H5W8Verify,
2851 Service::LmsShakeM32H10W1Verify,
2852 Service::LmsShakeM32H10W2Verify,
2853 Service::LmsShakeM32H10W4Verify,
2854 Service::LmsShakeM32H10W8Verify,
2855 Service::LmsShakeM32H15W1Verify,
2856 Service::LmsShakeM32H15W2Verify,
2857 Service::LmsShakeM32H15W4Verify,
2858 Service::LmsShakeM32H15W8Verify,
2859 Service::LmsShakeM32H20W1Verify,
2860 Service::LmsShakeM32H20W2Verify,
2861 Service::LmsShakeM32H20W4Verify,
2862 Service::LmsShakeM32H20W8Verify,
2863 Service::LmsShakeM32H25W1Verify,
2864 Service::LmsShakeM32H25W2Verify,
2865 Service::LmsShakeM32H25W4Verify,
2866 Service::LmsShakeM32H25W8Verify,
2867 Service::LmsShakeM24H5W1Verify,
2868 Service::LmsShakeM24H5W2Verify,
2869 Service::LmsShakeM24H5W4Verify,
2870 Service::LmsShakeM24H5W8Verify,
2871 Service::LmsShakeM24H10W1Verify,
2872 Service::LmsShakeM24H10W2Verify,
2873 Service::LmsShakeM24H10W4Verify,
2874 Service::LmsShakeM24H10W8Verify,
2875 Service::LmsShakeM24H15W1Verify,
2876 Service::LmsShakeM24H15W2Verify,
2877 Service::LmsShakeM24H15W4Verify,
2878 Service::LmsShakeM24H15W8Verify,
2879 Service::LmsShakeM24H20W1Verify,
2880 Service::LmsShakeM24H20W2Verify,
2881 Service::LmsShakeM24H20W4Verify,
2882 Service::LmsShakeM24H20W8Verify,
2883 Service::LmsShakeM24H25W1Verify,
2884 Service::LmsShakeM24H25W2Verify,
2885 Service::LmsShakeM24H25W4Verify,
2886 Service::LmsShakeM24H25W8Verify,
2887 Service::XmssVerify,
2888 Service::Dh3072,
2889 ];
2890
2891 const EXPECTED_CNSA1: [Service; 34] = [
2894 Service::Sha384,
2895 Service::HmacSha384,
2896 Service::CmacAes256,
2897 Service::Aes256Ecb,
2898 Service::Aes256Cbc,
2899 Service::Aes256Ctr,
2900 Service::Aes256Gcm,
2901 Service::Aes256Ccm,
2902 Service::Aes256Kw,
2903 Service::Aes256Kwp,
2904 Service::Aes256,
2905 Service::CtrDrbgAes256,
2906 Service::HashDrbgSha384,
2907 Service::HmacDrbgSha384,
2908 Service::HkdfSha384,
2909 Service::KbkdfHmacSha384,
2910 Service::KbkdfCmacAes256,
2911 Service::RsaKeygen3072,
2912 Service::RsaPkcs1v15Sign3072,
2913 Service::RsaPssSign3072,
2914 Service::RsaOaep3072,
2915 Service::RsaPkcs1v15Verify3072,
2916 Service::RsaPssVerify3072,
2917 Service::RsaKeygen4096,
2918 Service::RsaPkcs1v15Sign4096,
2919 Service::RsaPssSign4096,
2920 Service::RsaOaep4096,
2921 Service::RsaPkcs1v15Verify4096,
2922 Service::RsaPssVerify4096,
2923 Service::EcdsaP384Sign,
2924 Service::EcdsaP384Verify,
2925 Service::EcdsaP384Keygen,
2926 Service::EcdhP384,
2927 Service::Dh3072,
2928 ];
2929
2930 const EXPECTED_CNSA2_NON_LMS: [Service; 30] = [
2933 Service::Sha384,
2934 Service::Sha512,
2935 Service::HmacSha384,
2936 Service::HmacSha512,
2937 Service::CmacAes256,
2938 Service::Aes256Ecb,
2939 Service::Aes256Cbc,
2940 Service::Aes256Ctr,
2941 Service::Aes256Gcm,
2942 Service::Aes256Ccm,
2943 Service::Aes256Kw,
2944 Service::Aes256Kwp,
2945 Service::Aes256,
2946 Service::CtrDrbgAes256,
2947 Service::HashDrbgSha384,
2948 Service::HashDrbgSha512,
2949 Service::HmacDrbgSha384,
2950 Service::HmacDrbgSha512,
2951 Service::HkdfSha384,
2952 Service::HkdfSha512,
2953 Service::KbkdfHmacSha384,
2954 Service::KbkdfHmacSha512,
2955 Service::KbkdfCmacAes256,
2956 Service::MlKem1024Encaps,
2957 Service::MlKem1024Decaps,
2958 Service::MlKem1024Keygen,
2959 Service::MlDsa87Sign,
2960 Service::MlDsa87Verify,
2961 Service::MlDsa87Keygen,
2962 Service::XmssVerify,
2963 ];
2964
2965 fn assert_exact_membership(profile: AlgorithmProfile, expected: &[Service], allow_lms: bool) {
2968 for svc in ALL_SERVICES {
2969 let want = expected.iter().any(|e| *e as u16 == svc as u16)
2970 || (allow_lms && is_lms_service(svc));
2971 assert_eq!(
2972 is_allowed(profile, svc),
2973 want,
2974 "{svc} under {profile}: gate says {}, suite says {want}",
2975 is_allowed(profile, svc)
2976 );
2977 }
2978 }
2979
2980 #[test]
2990 fn every_profile_round_trips_through_the_active_profile() {
2991 for profile in [
2992 AlgorithmProfile::Unrestricted,
2993 AlgorithmProfile::Cnsa2,
2994 AlgorithmProfile::Cnsa1,
2995 AlgorithmProfile::Migration,
2996 ] {
2997 assert_eq!(
2998 AlgorithmProfile::from_u8(profile as u8),
2999 profile,
3000 "{profile} did not survive the round trip through its discriminant"
3001 );
3002 }
3003
3004 for raw in [4u8, 9, 200, 255] {
3008 assert_ne!(
3009 AlgorithmProfile::from_u8(raw),
3010 AlgorithmProfile::Unrestricted,
3011 "unknown discriminant {raw} must not open the module"
3012 );
3013 }
3014 }
3015
3016 #[test]
3022 fn migration_gates_as_itself_through_require_allowed() {
3023 let guard = reset_for_test();
3024 guard.reset();
3025 initialize_with_profile(UNSIGNED_TEST_BINARY, &[], AlgorithmProfile::Migration).unwrap();
3026 assert_eq!(active_profile(), AlgorithmProfile::Migration);
3027
3028 assert!(
3030 require_allowed(Service::EcdsaP384Sign).is_ok(),
3031 "ECDSA P-384 is in CNSA 1.0 and must pass under Migration"
3032 );
3033 assert!(
3034 require_allowed(Service::Dh3072).is_ok(),
3035 "DH-3072 is in CNSA 1.0 and must pass under Migration"
3036 );
3037 assert!(
3039 require_allowed(Service::MlKem1024Encaps).is_ok(),
3040 "ML-KEM-1024 is in CNSA 2.0 and must pass under Migration"
3041 );
3042 assert!(require_allowed(Service::Sha256).is_err());
3044 }
3045
3046 #[test]
3049 fn profile_membership_is_exact() {
3050 assert_exact_membership(AlgorithmProfile::Cnsa1, &EXPECTED_CNSA1, false);
3051 assert_exact_membership(AlgorithmProfile::Cnsa2, &EXPECTED_CNSA2_NON_LMS, true);
3052
3053 for svc in ALL_SERVICES {
3056 let want = is_allowed(AlgorithmProfile::Cnsa1, svc)
3057 || is_allowed(AlgorithmProfile::Cnsa2, svc);
3058 assert_eq!(
3059 is_allowed(AlgorithmProfile::Migration, svc),
3060 want,
3061 "{svc}: Migration must permit exactly the union"
3062 );
3063 }
3064
3065 for svc in ALL_SERVICES {
3068 assert!(is_allowed(AlgorithmProfile::Unrestricted, svc));
3069 }
3070 }
3071
3072 #[test]
3079 fn migration_is_the_union_of_both_suites() {
3080 for svc in [
3082 Service::EcdsaP384Sign,
3083 Service::EcdhP384,
3084 Service::RsaPssSign3072,
3085 Service::Dh3072,
3086 ] {
3087 assert!(
3088 is_allowed(AlgorithmProfile::Migration, svc),
3089 "{svc} (CNSA 1.0) must pass"
3090 );
3091 assert!(
3092 !is_allowed(AlgorithmProfile::Cnsa2, svc),
3093 "{svc} is not CNSA 2.0"
3094 );
3095 }
3096
3097 for svc in [
3099 Service::MlKem1024Encaps,
3100 Service::MlDsa87Sign,
3101 Service::XmssVerify,
3102 Service::Sha512,
3103 Service::LmsSha256M32H10W4Verify,
3104 ] {
3105 assert!(
3106 is_allowed(AlgorithmProfile::Migration, svc),
3107 "{svc} (CNSA 2.0) must pass"
3108 );
3109 assert!(
3110 !is_allowed(AlgorithmProfile::Cnsa1, svc),
3111 "{svc} is not CNSA 1.0"
3112 );
3113 }
3114
3115 for svc in [Service::Aes256Gcm, Service::Sha384, Service::HmacSha384] {
3118 assert!(is_allowed(AlgorithmProfile::Cnsa1, svc));
3119 assert!(is_allowed(AlgorithmProfile::Cnsa2, svc));
3120 assert!(is_allowed(AlgorithmProfile::Migration, svc));
3121 }
3122
3123 for svc in [
3126 Service::Sha1,
3127 Service::Sha256,
3128 Service::Aes128Gcm,
3129 Service::RsaPssSign2048,
3130 Service::EcdsaP256Sign,
3131 Service::Ed25519Sign,
3132 Service::Sha3_384,
3134 Service::Shake256,
3135 Service::Kmac256,
3136 ] {
3137 assert!(
3138 !is_allowed(AlgorithmProfile::Migration, svc),
3139 "{svc} is in neither suite and must be refused"
3140 );
3141 assert!(is_allowed(AlgorithmProfile::Unrestricted, svc));
3144 }
3145 }
3146
3147 #[test]
3148 fn cnsa1_allows_the_2016_suite() {
3149 let allowed = [
3150 Service::Aes256Gcm,
3151 Service::Sha384,
3152 Service::EcdsaP384Sign,
3153 Service::EcdsaP384Verify,
3154 Service::EcdsaP384Keygen,
3155 Service::EcdhP384,
3156 Service::RsaPssSign3072,
3157 Service::RsaOaep3072,
3158 Service::RsaPssSign4096,
3159 Service::Dh3072,
3160 Service::HmacSha384,
3162 Service::CtrDrbgAes256,
3163 Service::HashDrbgSha384,
3164 Service::KbkdfHmacSha384,
3165 ];
3166 for svc in allowed {
3167 assert!(
3168 is_allowed(AlgorithmProfile::Cnsa1, svc),
3169 "{svc} should be allowed in CNSA 1.0"
3170 );
3171 }
3172 }
3173
3174 #[test]
3175 fn cnsa1_blocks_small_keys_and_ed25519() {
3176 let blocked = [
3177 Service::Sha1,
3178 Service::Sha224,
3179 Service::Aes128Ecb,
3180 Service::Aes192Gcm,
3181 Service::CtrDrbgAes128,
3182 Service::EcdsaP256Sign,
3183 Service::EcdhP256,
3184 Service::Ed25519Sign,
3185 Service::Ed25519Verify,
3186 Service::RsaPssSign2048,
3187 Service::RsaOaep2048,
3188 Service::Sha256,
3194 Service::HmacSha256,
3195 Service::HashDrbgSha256,
3196 Service::HkdfSha256,
3197 Service::Sha512,
3199 Service::HmacSha512,
3200 Service::Sha3_256,
3202 Service::Shake256,
3203 Service::MlKem1024Encaps,
3205 Service::MlDsa87Sign,
3206 Service::XmssVerify,
3207 Service::LmsSha256M32H10W4Verify,
3208 ];
3209 for svc in blocked {
3210 assert!(
3211 !is_allowed(AlgorithmProfile::Cnsa1, svc),
3212 "{svc} should be BLOCKED in CNSA 1.0"
3213 );
3214 }
3215 }
3216
3217 #[test]
3218 fn lms_gating_is_exhaustive_across_all_80_variants() {
3219 let all_80 = LMS_SERVICES;
3225 assert_eq!(all_80.len(), 80, "LMS enumeration drift");
3226
3227 for svc in all_80 {
3228 assert!(
3230 is_allowed(AlgorithmProfile::Unrestricted, svc),
3231 "{svc} must be allowed in Unrestricted"
3232 );
3233
3234 assert!(
3237 is_allowed(AlgorithmProfile::Cnsa2, svc),
3238 "{svc} must be allowed in CNSA 2.0"
3239 );
3240
3241 assert!(
3244 !is_allowed(AlgorithmProfile::Cnsa1, svc),
3245 "{svc} is not in CNSA 1.0 and must be refused"
3246 );
3247
3248 assert!(
3250 is_allowed(AlgorithmProfile::Migration, svc),
3251 "{svc} must be allowed in Migration"
3252 );
3253 }
3254 }
3255
3256 #[test]
3257 fn lms_discriminant_slots_are_complete_and_pure() {
3258 let mut discriminants = [0u16; 80];
3264 for (slot, svc) in discriminants.iter_mut().zip(LMS_SERVICES) {
3265 *slot = svc as u16;
3266 }
3267 discriminants.sort_unstable();
3268
3269 let mut compared = 0usize;
3274 for pair in discriminants.windows(2) {
3275 let [lo, hi] = pair else {
3276 unreachable!("windows(2) yields two-element slices")
3277 };
3278 assert_eq!(*hi, lo + 2, "LMS slot after {lo} is missing or duplicated");
3279 compared += 1;
3280 }
3281 assert_eq!(compared, 79, "slot sweep did not run in full");
3282
3283 let (Some(&first), Some(&last)) = (discriminants.first(), discriminants.last()) else {
3284 unreachable!("the array holds 80 elements")
3285 };
3286 assert_eq!(first, 501, "LMS block does not start at 501");
3287 assert_eq!(last, 659, "LMS block does not end at 659");
3288
3289 for svc in LMS_SERVICES {
3290 assert!(is_lms_service(svc), "{svc} must be inside the LMS block");
3291 }
3292
3293 let lms_count = ALL_SERVICES
3297 .iter()
3298 .filter(|svc| is_lms_service(**svc))
3299 .count();
3300 assert_eq!(lms_count, 80, "a non-LMS variant sits on an LMS slot");
3301
3302 for svc in [
3305 Service::SlhDsaShake256fVerify,
3306 Service::XmssVerify,
3307 Service::Dh3072,
3308 ] {
3309 assert!(!is_lms_service(svc), "{svc} must be outside the LMS block");
3310 }
3311 }
3312
3313 #[test]
3314 fn slh_dsa_gating_is_exhaustive_across_all_36_variants() {
3315 let all_36 = [
3323 Service::SlhDsaSha2256sKeygen,
3325 Service::SlhDsaSha2256sSign,
3326 Service::SlhDsaSha2256sVerify,
3327 Service::SlhDsaSha2128sKeygen,
3328 Service::SlhDsaSha2128sSign,
3329 Service::SlhDsaSha2128sVerify,
3330 Service::SlhDsaSha2128fKeygen,
3331 Service::SlhDsaSha2128fSign,
3332 Service::SlhDsaSha2128fVerify,
3333 Service::SlhDsaSha2192sKeygen,
3334 Service::SlhDsaSha2192sSign,
3335 Service::SlhDsaSha2192sVerify,
3336 Service::SlhDsaSha2192fKeygen,
3337 Service::SlhDsaSha2192fSign,
3338 Service::SlhDsaSha2192fVerify,
3339 Service::SlhDsaSha2256fKeygen,
3340 Service::SlhDsaSha2256fSign,
3341 Service::SlhDsaSha2256fVerify,
3342 Service::SlhDsaShake128sKeygen,
3344 Service::SlhDsaShake128sSign,
3345 Service::SlhDsaShake128sVerify,
3346 Service::SlhDsaShake128fKeygen,
3347 Service::SlhDsaShake128fSign,
3348 Service::SlhDsaShake128fVerify,
3349 Service::SlhDsaShake192sKeygen,
3350 Service::SlhDsaShake192sSign,
3351 Service::SlhDsaShake192sVerify,
3352 Service::SlhDsaShake192fKeygen,
3353 Service::SlhDsaShake192fSign,
3354 Service::SlhDsaShake192fVerify,
3355 Service::SlhDsaShake256sKeygen,
3356 Service::SlhDsaShake256sSign,
3357 Service::SlhDsaShake256sVerify,
3358 Service::SlhDsaShake256fKeygen,
3359 Service::SlhDsaShake256fSign,
3360 Service::SlhDsaShake256fVerify,
3361 ];
3362 assert_eq!(all_36.len(), 36, "SLH-DSA enumeration drift");
3363
3364 for svc in all_36 {
3365 assert!(
3367 is_allowed(AlgorithmProfile::Unrestricted, svc),
3368 "{svc} must be allowed in Unrestricted"
3369 );
3370
3371 assert!(
3373 !is_allowed(AlgorithmProfile::Cnsa1, svc),
3374 "{svc} must be BLOCKED in CNSA 1.0"
3375 );
3376
3377 assert!(
3381 !is_allowed(AlgorithmProfile::Cnsa2, svc),
3382 "{svc} must be BLOCKED in CNSA 2.0"
3383 );
3384 }
3385 }
3386
3387 #[test]
3388 fn require_allowed_returns_restricted_error_under_cnsa2() {
3389 let _guard = reset_for_test();
3390 initialize_with_profile(UNSIGNED_TEST_BINARY, &[], AlgorithmProfile::Cnsa2).unwrap();
3391 match require_allowed(Service::Aes128Ecb) {
3392 Err(Error::AlgorithmRestricted {
3393 service: Service::Aes128Ecb,
3394 }) => {}
3395 other => panic!("expected AlgorithmRestricted(Aes128Ecb), got {other:?}"),
3396 }
3397 }
3398
3399 #[test]
3400 fn require_allowed_passes_permitted_services() {
3401 let _guard = reset_for_test();
3402 initialize_with_profile(UNSIGNED_TEST_BINARY, &[], AlgorithmProfile::Cnsa2).unwrap();
3403 require_allowed(Service::Aes256Gcm).unwrap();
3404 require_allowed(Service::Sha384).unwrap();
3405 require_allowed(Service::MlKem1024Encaps).unwrap();
3406 }
3407
3408 #[test]
3409 fn require_allowed_passes_everything_in_unrestricted() {
3410 let _guard = reset_for_test();
3411 initialize_with_tests(UNSIGNED_TEST_BINARY, &[]).unwrap();
3412 require_allowed(Service::Sha1).unwrap();
3414 require_allowed(Service::Aes128Ecb).unwrap();
3415 require_allowed(Service::Ed25519Sign).unwrap();
3416 require_allowed(Service::MlKem1024Encaps).unwrap();
3417 }
3418
3419 #[test]
3425 fn require_allowed_refuses_every_slh_dsa_service_under_cnsa2() {
3426 let guard = reset_for_test();
3427 for profile in [AlgorithmProfile::Cnsa2, AlgorithmProfile::Cnsa1] {
3428 guard.reset();
3429 initialize_with_profile(UNSIGNED_TEST_BINARY, &[], profile).unwrap();
3430 for svc in [
3431 Service::SlhDsaSha2256sKeygen,
3432 Service::SlhDsaSha2256sSign,
3433 Service::SlhDsaSha2256sVerify,
3434 Service::SlhDsaShake128fVerify,
3435 ] {
3436 match require_allowed(svc) {
3437 Err(Error::AlgorithmRestricted { service }) => assert_eq!(service, svc),
3438 other => {
3439 panic!("expected AlgorithmRestricted({svc}) under {profile}, got {other:?}")
3440 }
3441 }
3442 }
3443 }
3444 }
3445
3446 #[test]
3454 fn require_allowed_admits_lms_m24_under_cnsa2_and_migration() {
3455 let guard = reset_for_test();
3456 for profile in [AlgorithmProfile::Cnsa2, AlgorithmProfile::Migration] {
3457 guard.reset();
3458 initialize_with_profile(UNSIGNED_TEST_BINARY, &[], profile).unwrap();
3459 for svc in [
3460 Service::LmsSha256M24H5W1Verify,
3461 Service::LmsSha256M24H25W8Verify,
3462 Service::LmsShakeM24H10W4Verify,
3463 Service::LmsShakeM24H25W8Verify,
3464 ] {
3465 require_allowed(svc)
3466 .unwrap_or_else(|e| panic!("{svc} must be allowed under {profile}: {e:?}"));
3467 }
3468 }
3469 }
3470
3471 #[test]
3478 fn lms_block_boundaries_are_exact() {
3479 assert!(!lms_block_contains(499), "499 is below the LMS block");
3480 assert!(!lms_block_contains(500), "500 is even");
3481 assert!(lms_block_contains(501), "501 is the first LMS discriminant");
3482 assert!(!lms_block_contains(502), "502 is even");
3483 assert!(!lms_block_contains(658), "658 is even");
3484 assert!(lms_block_contains(659), "659 is the last LMS discriminant");
3485 assert!(!lms_block_contains(660), "660 is above the LMS block");
3486 assert!(!lms_block_contains(661), "661 is above the LMS block");
3487
3488 assert_eq!(Service::LmsSha256M32H5W1Verify as u16, 501);
3490 assert_eq!(Service::LmsShakeM24H25W8Verify as u16, 659);
3491 }
3492
3493 #[test]
3498 fn require_allowed_distinguishes_cnsa1_from_cnsa2() {
3499 let guard = reset_for_test();
3500
3501 initialize_with_profile(UNSIGNED_TEST_BINARY, &[], AlgorithmProfile::Cnsa1).unwrap();
3502 require_allowed(Service::EcdsaP384Sign)
3503 .unwrap_or_else(|e| panic!("P-384 ECDSA must be permitted under CNSA 1.0: {e:?}"));
3504
3505 guard.reset();
3506 initialize_with_profile(UNSIGNED_TEST_BINARY, &[], AlgorithmProfile::Cnsa2).unwrap();
3507 match require_allowed(Service::EcdsaP384Sign) {
3508 Err(Error::AlgorithmRestricted {
3509 service: Service::EcdsaP384Sign,
3510 }) => {}
3511 other => panic!("expected P-384 ECDSA refused under CNSA 2.0, got {other:?}"),
3512 }
3513 }
3514
3515 #[test]
3516 fn algorithm_profile_display() {
3517 assert_eq!(AlgorithmProfile::Unrestricted.to_string(), "Unrestricted");
3518 assert_eq!(AlgorithmProfile::Cnsa2.to_string(), "CNSA 2.0");
3519 assert_eq!(AlgorithmProfile::Cnsa1.to_string(), "CNSA 1.0");
3520 }
3521
3522 #[test]
3523 fn algorithm_restricted_error_display() {
3524 let _guard = reset_for_test();
3525 initialize_with_profile(UNSIGNED_TEST_BINARY, &[], AlgorithmProfile::Cnsa2).unwrap();
3526 let err = Error::AlgorithmRestricted {
3527 service: Service::Aes128Ecb,
3528 };
3529 let msg = err.to_string();
3530 assert!(msg.contains("AES-128-ECB"), "got: {msg}");
3531 assert!(msg.contains("CNSA 2.0"), "got: {msg}");
3532 }
3533
3534 #[test]
3535 fn not_implemented_error_display() {
3536 let err = Error::NotImplemented;
3537 assert_eq!(err.to_string(), "algorithm not yet implemented");
3538 }
3539
3540 #[test]
3541 fn service_display_is_stable() {
3542 assert_eq!(Service::Sha256.to_string(), "SHA-256");
3544 assert_eq!(Service::Aes256Gcm.to_string(), "AES-256-GCM");
3545 assert_eq!(Service::MlKem1024Encaps.to_string(), "ML-KEM-1024 encaps");
3546 assert_eq!(Service::Ed25519Sign.to_string(), "Ed25519 sign");
3547 }
3548}