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 EntropyInputTooShort,
514 NonceTooShort,
521}
522
523impl fmt::Display for Error {
524 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
525 match self {
526 Self::NotOperational { current } => {
527 write!(f, "FIPS module not operational (state = {current})")
528 }
529 Self::SelfTestFailed { test } => {
530 write!(f, "FIPS power-up self-test failed: {test}")
531 }
532 Self::ConditionalTestFailed { reason } => {
533 write!(f, "FIPS conditional self-test failed: {reason}")
534 }
535 Self::IntegrityNotAttested => f.write_str(
536 "this module did not check itself at startup, so it cannot tell whether this \
537 copy is the one that was built and signed. That does not mean the module is \
538 damaged: the program that started it skipped the check.",
539 ),
540 Self::AlreadyInitialized => f.write_str("FIPS module already initialized"),
541 Self::AlreadyFailed => f.write_str(
542 "FIPS module initialization already failed; restart the process to retry",
543 ),
544 Self::InvalidInput => f.write_str("invalid input to FIPS service"),
545 Self::AlgorithmRestricted { service } => {
546 write!(
547 f,
548 "algorithm {service} is not permitted under the {} profile",
549 active_profile()
550 )
551 }
552 Self::NotImplemented => f.write_str("algorithm not yet implemented"),
553 Self::EntropyInputTooShort => f.write_str(
554 "DRBG entropy input is shorter than the minimum for the instantiated \
555 security strength (SP 800-90A Rev. 1 Tables 2 and 3)",
556 ),
557 Self::NonceTooShort => f.write_str(
558 "DRBG nonce is shorter than half the instantiated security strength, \
559 the module's nonce floor (SP 800-90A Rev. 1 §9.1 step 8)",
560 ),
561 }
562 }
563}
564
565#[derive(Debug, Clone, Copy, PartialEq, Eq)]
574pub struct SelfTestFailure;
575
576impl fmt::Display for SelfTestFailure {
577 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
578 f.write_str("self-test failure")
579 }
580}
581
582pub trait SelfTest {
594 const NAME: &'static str;
597
598 fn run() -> Result<(), SelfTestFailure>;
602}
603
604#[derive(Debug, Clone, Copy)]
617pub struct KatEntry {
618 pub name: &'static str,
622 pub run: fn() -> Result<(), SelfTestFailure>,
624}
625
626pub fn initialize_with_tests(integrity: &[KatEntry], tests: &[KatEntry]) -> Result<(), Error> {
654 initialize(integrity, tests, AlgorithmProfile::Unrestricted)
655}
656
657fn initialize(
671 integrity: &[KatEntry],
672 tests: &[KatEntry],
673 profile: AlgorithmProfile,
674) -> Result<(), Error> {
675 initialize_as(caller_owner_id(), integrity, tests, profile)
676}
677
678fn initialize_as(
681 owner: Option<u64>,
682 integrity: &[KatEntry],
683 tests: &[KatEntry],
684 profile: AlgorithmProfile,
685) -> Result<(), Error> {
686 let claim = owner.map_or(ERROR, self_test_word);
690 if let Err(observed) =
691 STATE.compare_exchange(POWER_OFF, claim, Ordering::AcqRel, Ordering::Acquire)
692 {
693 return Err(already_left_power_off(observed, claim));
694 }
695 if owner.is_none() {
696 return Err(Error::SelfTestFailed {
697 test: THREAD_ID_CHECK,
698 });
699 }
700 let _unwind = UnwindToError { claim };
701
702 if integrity.is_empty() {
717 settle(claim, INTEGRITY_UNVERIFIED);
718 return Err(Error::IntegrityNotAttested);
719 }
720
721 for entry in integrity.iter().chain(tests) {
722 if (entry.run)().is_err() {
723 settle(claim, ERROR);
727 return Err(Error::SelfTestFailed { test: entry.name });
728 }
729 }
730
731 PROFILE.store(profile as u8, Ordering::Release);
745 if STATE
746 .compare_exchange(claim, OPERATIONAL, Ordering::AcqRel, Ordering::Acquire)
747 .is_err()
748 {
749 PROFILE.store(AlgorithmProfile::Unrestricted as u8, Ordering::Release);
750 return Err(Error::NotOperational { current: state() });
751 }
752 Ok(())
753}
754
755fn settle(claim: u64, to: u64) {
760 let _ = STATE.compare_exchange(claim, to, Ordering::AcqRel, Ordering::Acquire);
763}
764
765fn already_left_power_off(mut observed: u64, claim: u64) -> Error {
773 #[cfg(test)]
774 if decode(observed) == State::SelfTest && observed != claim {
775 WAITING_LOSERS.fetch_add(1, Ordering::AcqRel);
776 }
777 while decode(observed) == State::SelfTest && observed != claim {
778 core::hint::spin_loop();
779 observed = STATE.load(Ordering::Acquire);
780 }
781 match decode(observed) {
782 State::Operational => Error::AlreadyInitialized,
783 State::SelfTest => Error::NotOperational {
784 current: State::SelfTest,
785 },
786 State::PowerOff | State::Error | State::IntegrityUnverified => Error::AlreadyFailed,
787 }
788}
789
790#[cfg(test)]
793static WAITING_LOSERS: core::sync::atomic::AtomicUsize = core::sync::atomic::AtomicUsize::new(0);
794
795struct UnwindToError {
802 claim: u64,
803}
804
805impl Drop for UnwindToError {
806 fn drop(&mut self) {
807 settle(self.claim, ERROR);
808 }
809}
810
811pub fn enter_error_state(_reason: &'static str) {
823 STATE.store(ERROR, Ordering::Release);
824}
825
826#[derive(Debug, Clone, Copy, PartialEq, Eq)]
841#[repr(u8)]
842pub enum AlgorithmProfile {
843 Unrestricted = 0,
847 Cnsa2 = 1,
855 Cnsa1 = 2,
862 Migration = 3,
872}
873
874impl AlgorithmProfile {
875 const fn from_u8(raw: u8) -> Self {
876 match raw {
877 0 => Self::Unrestricted,
878 2 => Self::Cnsa1,
879 3 => Self::Migration,
880 _ => Self::Cnsa2,
883 }
884 }
885}
886
887impl fmt::Display for AlgorithmProfile {
888 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
889 let s = match self {
890 Self::Unrestricted => "Unrestricted",
891 Self::Cnsa2 => "CNSA 2.0",
892 Self::Cnsa1 => "CNSA 1.0",
893 Self::Migration => "Migration (CNSA 1.0 + CNSA 2.0)",
894 };
895 f.write_str(s)
896 }
897}
898
899static PROFILE: AtomicU8 = AtomicU8::new(AlgorithmProfile::Unrestricted as u8);
902
903pub fn active_profile() -> AlgorithmProfile {
905 AlgorithmProfile::from_u8(PROFILE.load(Ordering::Acquire))
906}
907
908pub fn initialize_with_profile(
939 integrity: &[KatEntry],
940 tests: &[KatEntry],
941 profile: AlgorithmProfile,
942) -> Result<(), Error> {
943 initialize(integrity, tests, profile)
944}
945
946#[derive(Debug, Clone, Copy, PartialEq, Eq)]
957#[repr(u16)]
958#[allow(missing_docs)]
959pub enum Service {
960 Sha1 = 0,
962 Sha224 = 1,
963 Sha256 = 2,
964 Sha384 = 3,
965 Sha512 = 4,
966 Sha512_224 = 5,
967 Sha512_256 = 6,
968
969 Sha3_224 = 10,
970 Sha3_256 = 11,
971 Sha3_384 = 12,
972 Sha3_512 = 13,
973
974 Shake128 = 20,
976 Shake256 = 21,
977 CShake128 = 22,
978 CShake256 = 23,
979 Kmac128 = 24,
980 Kmac256 = 25,
981 KmacXof128 = 26,
982 KmacXof256 = 27,
983 TupleHash128 = 28,
984 TupleHash256 = 29,
985 TupleHashXof128 = 30,
986 TupleHashXof256 = 31,
987 ParallelHash128 = 32,
988 ParallelHash256 = 33,
989 ParallelHashXof128 = 34,
990 ParallelHashXof256 = 35,
991
992 HmacSha1 = 40,
994 HmacSha224 = 41,
995 HmacSha256 = 42,
996 HmacSha384 = 43,
997 HmacSha512 = 44,
998 HmacSha512_224 = 45,
999 HmacSha512_256 = 46,
1000 HmacSha3_224 = 47,
1001 HmacSha3_256 = 48,
1002 HmacSha3_384 = 49,
1003 HmacSha3_512 = 50,
1004
1005 CmacAes128 = 60,
1007 CmacAes192 = 61,
1008 CmacAes256 = 62,
1009
1010 Aes128Ecb = 70,
1012 Aes128Cbc = 71,
1013 Aes128Ctr = 72,
1014 Aes128Gcm = 73,
1015 Aes128Ccm = 74,
1016 Aes128Kw = 75,
1017 Aes128Kwp = 76,
1018 Aes192Ecb = 80,
1019 Aes192Cbc = 81,
1020 Aes192Ctr = 82,
1021 Aes192Gcm = 83,
1022 Aes192Ccm = 84,
1023 Aes192Kw = 85,
1024 Aes192Kwp = 86,
1025 Aes256Ecb = 90,
1026 Aes256Cbc = 91,
1027 Aes256Ctr = 92,
1028 Aes256Gcm = 93,
1029 Aes256Ccm = 94,
1030 Aes256Kw = 95,
1031 Aes256Kwp = 96,
1032
1033 Aes128 = 97,
1040 Aes192 = 98,
1042 Aes256 = 99,
1044
1045 CtrDrbgAes128 = 100,
1047 CtrDrbgAes192 = 101,
1048 CtrDrbgAes256 = 102,
1049 HashDrbgSha256 = 103,
1050 HashDrbgSha384 = 104,
1051 HashDrbgSha512 = 105,
1052 HmacDrbgSha256 = 106,
1053 HmacDrbgSha384 = 107,
1054 HmacDrbgSha512 = 108,
1055
1056 HkdfSha1 = 120,
1058 HkdfSha256 = 121,
1059 HkdfSha384 = 122,
1060 HkdfSha512 = 123,
1061 KbkdfHmacSha256 = 130,
1062 KbkdfHmacSha384 = 131,
1063 KbkdfHmacSha512 = 132,
1064 KbkdfCmacAes128 = 133,
1065 KbkdfCmacAes192 = 134,
1066 KbkdfCmacAes256 = 135,
1067 Pbkdf2HmacSha1 = 140,
1068 Pbkdf2HmacSha224 = 141,
1069 Pbkdf2HmacSha256 = 142,
1070 Pbkdf2HmacSha384 = 143,
1071 Pbkdf2HmacSha512 = 144,
1072
1073 RsaKeygen2048 = 150,
1075 RsaPkcs1v15Sign2048 = 151,
1076 RsaPssSign2048 = 152,
1077 RsaOaep2048 = 153,
1078 RsaPkcs1v15Verify2048 = 154,
1079 RsaPssVerify2048 = 155,
1080 RsaKeygen3072 = 160,
1081 RsaPkcs1v15Sign3072 = 161,
1082 RsaPssSign3072 = 162,
1083 RsaOaep3072 = 163,
1084 RsaPkcs1v15Verify3072 = 164,
1085 RsaPssVerify3072 = 165,
1086 RsaKeygen4096 = 170,
1087 RsaPkcs1v15Sign4096 = 171,
1088 RsaPssSign4096 = 172,
1089 RsaOaep4096 = 173,
1090 RsaPkcs1v15Verify4096 = 174,
1091 RsaPssVerify4096 = 175,
1092
1093 EcdsaP256Sign = 200,
1095 EcdsaP256Verify = 201,
1096 EcdsaP256Keygen = 202,
1097 EcdsaP384Sign = 210,
1098 EcdsaP384Verify = 211,
1099 EcdsaP384Keygen = 212,
1100
1101 EcdhP256 = 220,
1103 EcdhP384 = 221,
1104
1105 Ed25519Sign = 230,
1107 Ed25519Verify = 231,
1108 Ed25519Keygen = 232,
1109
1110 Tls12Kdf = 240,
1112 Tls13Kdf = 241,
1113
1114 MlKem1024Encaps = 300,
1116 MlKem1024Decaps = 301,
1117 MlKem1024Keygen = 302,
1118 MlKem512Encaps = 303,
1119 MlKem512Decaps = 304,
1120 MlKem512Keygen = 305,
1121 MlKem768Encaps = 306,
1122 MlKem768Decaps = 307,
1123 MlKem768Keygen = 308,
1124
1125 MlDsa87Sign = 310,
1127 MlDsa87Verify = 311,
1128 MlDsa87Keygen = 312,
1129 MlDsa44Sign = 313,
1130 MlDsa44Verify = 314,
1131 MlDsa44Keygen = 315,
1132 MlDsa65Sign = 316,
1133 MlDsa65Verify = 317,
1134 MlDsa65Keygen = 318,
1135
1136 SlhDsaSha2256sKeygen = 320,
1141 SlhDsaSha2256sSign = 321,
1142 SlhDsaSha2256sVerify = 322,
1143 SlhDsaSha2128sKeygen = 323,
1144 SlhDsaSha2128sSign = 324,
1145 SlhDsaSha2128sVerify = 325,
1146 SlhDsaSha2128fKeygen = 326,
1147 SlhDsaSha2128fSign = 327,
1148 SlhDsaSha2128fVerify = 328,
1149 SlhDsaSha2192sKeygen = 329,
1150 SlhDsaSha2192sSign = 330,
1151 SlhDsaSha2192sVerify = 331,
1152 SlhDsaSha2192fKeygen = 332,
1153 SlhDsaSha2192fSign = 333,
1154 SlhDsaSha2192fVerify = 334,
1155 SlhDsaSha2256fKeygen = 335,
1156 SlhDsaSha2256fSign = 336,
1157 SlhDsaSha2256fVerify = 337,
1158 SlhDsaShake128sKeygen = 338,
1159 SlhDsaShake128sSign = 339,
1160 SlhDsaShake128sVerify = 340,
1161 SlhDsaShake128fKeygen = 341,
1162 SlhDsaShake128fSign = 342,
1163 SlhDsaShake128fVerify = 343,
1164 SlhDsaShake192sKeygen = 344,
1165 SlhDsaShake192sSign = 345,
1166 SlhDsaShake192sVerify = 346,
1167 SlhDsaShake192fKeygen = 347,
1168 SlhDsaShake192fSign = 348,
1169 SlhDsaShake192fVerify = 349,
1170 SlhDsaShake256sKeygen = 350,
1171 SlhDsaShake256sSign = 351,
1172 SlhDsaShake256sVerify = 352,
1173 SlhDsaShake256fKeygen = 353,
1174 SlhDsaShake256fSign = 354,
1175 SlhDsaShake256fVerify = 355,
1176
1177 LmsSha256M32H5W1Verify = 501,
1192 LmsSha256M32H5W2Verify = 503,
1193 LmsSha256M32H5W4Verify = 505,
1194 LmsSha256M32H5W8Verify = 507,
1195 LmsSha256M32H10W1Verify = 509,
1196 LmsSha256M32H10W2Verify = 511,
1197 LmsSha256M32H10W4Verify = 513,
1198 LmsSha256M32H10W8Verify = 515,
1199 LmsSha256M32H15W1Verify = 517,
1200 LmsSha256M32H15W2Verify = 519,
1201 LmsSha256M32H15W4Verify = 521,
1202 LmsSha256M32H15W8Verify = 523,
1203 LmsSha256M32H20W1Verify = 525,
1204 LmsSha256M32H20W2Verify = 527,
1205 LmsSha256M32H20W4Verify = 529,
1206 LmsSha256M32H20W8Verify = 531,
1207 LmsSha256M32H25W1Verify = 533,
1208 LmsSha256M32H25W2Verify = 535,
1209 LmsSha256M32H25W4Verify = 537,
1210 LmsSha256M32H25W8Verify = 539,
1211
1212 LmsSha256M24H5W1Verify = 541,
1214 LmsSha256M24H5W2Verify = 543,
1215 LmsSha256M24H5W4Verify = 545,
1216 LmsSha256M24H5W8Verify = 547,
1217 LmsSha256M24H10W1Verify = 549,
1218 LmsSha256M24H10W2Verify = 551,
1219 LmsSha256M24H10W4Verify = 553,
1220 LmsSha256M24H10W8Verify = 555,
1221 LmsSha256M24H15W1Verify = 557,
1222 LmsSha256M24H15W2Verify = 559,
1223 LmsSha256M24H15W4Verify = 561,
1224 LmsSha256M24H15W8Verify = 563,
1225 LmsSha256M24H20W1Verify = 565,
1226 LmsSha256M24H20W2Verify = 567,
1227 LmsSha256M24H20W4Verify = 569,
1228 LmsSha256M24H20W8Verify = 571,
1229 LmsSha256M24H25W1Verify = 573,
1230 LmsSha256M24H25W2Verify = 575,
1231 LmsSha256M24H25W4Verify = 577,
1232 LmsSha256M24H25W8Verify = 579,
1233
1234 LmsShakeM32H5W1Verify = 581,
1236 LmsShakeM32H5W2Verify = 583,
1237 LmsShakeM32H5W4Verify = 585,
1238 LmsShakeM32H5W8Verify = 587,
1239 LmsShakeM32H10W1Verify = 589,
1240 LmsShakeM32H10W2Verify = 591,
1241 LmsShakeM32H10W4Verify = 593,
1242 LmsShakeM32H10W8Verify = 595,
1243 LmsShakeM32H15W1Verify = 597,
1244 LmsShakeM32H15W2Verify = 599,
1245 LmsShakeM32H15W4Verify = 601,
1246 LmsShakeM32H15W8Verify = 603,
1247 LmsShakeM32H20W1Verify = 605,
1248 LmsShakeM32H20W2Verify = 607,
1249 LmsShakeM32H20W4Verify = 609,
1250 LmsShakeM32H20W8Verify = 611,
1251 LmsShakeM32H25W1Verify = 613,
1252 LmsShakeM32H25W2Verify = 615,
1253 LmsShakeM32H25W4Verify = 617,
1254 LmsShakeM32H25W8Verify = 619,
1255
1256 LmsShakeM24H5W1Verify = 621,
1258 LmsShakeM24H5W2Verify = 623,
1259 LmsShakeM24H5W4Verify = 625,
1260 LmsShakeM24H5W8Verify = 627,
1261 LmsShakeM24H10W1Verify = 629,
1262 LmsShakeM24H10W2Verify = 631,
1263 LmsShakeM24H10W4Verify = 633,
1264 LmsShakeM24H10W8Verify = 635,
1265 LmsShakeM24H15W1Verify = 637,
1266 LmsShakeM24H15W2Verify = 639,
1267 LmsShakeM24H15W4Verify = 641,
1268 LmsShakeM24H15W8Verify = 643,
1269 LmsShakeM24H20W1Verify = 645,
1270 LmsShakeM24H20W2Verify = 647,
1271 LmsShakeM24H20W4Verify = 649,
1272 LmsShakeM24H20W8Verify = 651,
1273 LmsShakeM24H25W1Verify = 653,
1274 LmsShakeM24H25W2Verify = 655,
1275 LmsShakeM24H25W4Verify = 657,
1276 LmsShakeM24H25W8Verify = 659,
1277
1278 XmssVerify = 371,
1281
1282 Dh3072 = 380,
1285}
1286
1287impl fmt::Display for Service {
1288 #[allow(clippy::too_many_lines)]
1292 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1293 let name = match self {
1294 Self::Sha1 => "SHA-1",
1295 Self::Sha224 => "SHA-224",
1296 Self::Sha256 => "SHA-256",
1297 Self::Sha384 => "SHA-384",
1298 Self::Sha512 => "SHA-512",
1299 Self::Sha512_224 => "SHA-512/224",
1300 Self::Sha512_256 => "SHA-512/256",
1301 Self::Sha3_224 => "SHA3-224",
1302 Self::Sha3_256 => "SHA3-256",
1303 Self::Sha3_384 => "SHA3-384",
1304 Self::Sha3_512 => "SHA3-512",
1305 Self::Shake128 => "SHAKE128",
1306 Self::Shake256 => "SHAKE256",
1307 Self::CShake128 => "cSHAKE128",
1308 Self::CShake256 => "cSHAKE256",
1309 Self::Kmac128 => "KMAC128",
1310 Self::Kmac256 => "KMAC256",
1311 Self::KmacXof128 => "KMACXOF128",
1312 Self::KmacXof256 => "KMACXOF256",
1313 Self::TupleHash128 => "TupleHash128",
1314 Self::TupleHash256 => "TupleHash256",
1315 Self::TupleHashXof128 => "TupleHashXOF128",
1316 Self::TupleHashXof256 => "TupleHashXOF256",
1317 Self::ParallelHash128 => "ParallelHash128",
1318 Self::ParallelHash256 => "ParallelHash256",
1319 Self::ParallelHashXof128 => "ParallelHashXOF128",
1320 Self::ParallelHashXof256 => "ParallelHashXOF256",
1321 Self::HmacSha1 => "HMAC-SHA-1",
1322 Self::HmacSha224 => "HMAC-SHA-224",
1323 Self::HmacSha256 => "HMAC-SHA-256",
1324 Self::HmacSha384 => "HMAC-SHA-384",
1325 Self::HmacSha512 => "HMAC-SHA-512",
1326 Self::HmacSha512_224 => "HMAC-SHA-512/224",
1327 Self::HmacSha512_256 => "HMAC-SHA-512/256",
1328 Self::HmacSha3_224 => "HMAC-SHA3-224",
1329 Self::HmacSha3_256 => "HMAC-SHA3-256",
1330 Self::HmacSha3_384 => "HMAC-SHA3-384",
1331 Self::HmacSha3_512 => "HMAC-SHA3-512",
1332 Self::CmacAes128 => "CMAC-AES-128",
1333 Self::CmacAes192 => "CMAC-AES-192",
1334 Self::CmacAes256 => "CMAC-AES-256",
1335 Self::Aes128Ecb => "AES-128-ECB",
1336 Self::Aes128Cbc => "AES-128-CBC",
1337 Self::Aes128Ctr => "AES-128-CTR",
1338 Self::Aes128Gcm => "AES-128-GCM",
1339 Self::Aes128Ccm => "AES-128-CCM",
1340 Self::Aes128Kw => "AES-128-KW",
1341 Self::Aes128Kwp => "AES-128-KWP",
1342 Self::Aes192Ecb => "AES-192-ECB",
1343 Self::Aes192Cbc => "AES-192-CBC",
1344 Self::Aes192Ctr => "AES-192-CTR",
1345 Self::Aes192Gcm => "AES-192-GCM",
1346 Self::Aes192Ccm => "AES-192-CCM",
1347 Self::Aes192Kw => "AES-192-KW",
1348 Self::Aes192Kwp => "AES-192-KWP",
1349 Self::Aes256Ecb => "AES-256-ECB",
1350 Self::Aes256Cbc => "AES-256-CBC",
1351 Self::Aes256Ctr => "AES-256-CTR",
1352 Self::Aes256Gcm => "AES-256-GCM",
1353 Self::Aes256Ccm => "AES-256-CCM",
1354 Self::Aes256Kw => "AES-256-KW",
1355 Self::Aes256Kwp => "AES-256-KWP",
1356 Self::Aes128 => "AES-128",
1357 Self::Aes192 => "AES-192",
1358 Self::Aes256 => "AES-256",
1359 Self::CtrDrbgAes128 => "CTR_DRBG-AES-128",
1360 Self::CtrDrbgAes192 => "CTR_DRBG-AES-192",
1361 Self::CtrDrbgAes256 => "CTR_DRBG-AES-256",
1362 Self::HashDrbgSha256 => "Hash_DRBG-SHA-256",
1363 Self::HashDrbgSha384 => "Hash_DRBG-SHA-384",
1364 Self::HashDrbgSha512 => "Hash_DRBG-SHA-512",
1365 Self::HmacDrbgSha256 => "HMAC_DRBG-SHA-256",
1366 Self::HmacDrbgSha384 => "HMAC_DRBG-SHA-384",
1367 Self::HmacDrbgSha512 => "HMAC_DRBG-SHA-512",
1368 Self::HkdfSha1 => "HKDF-SHA-1",
1369 Self::HkdfSha256 => "HKDF-SHA-256",
1370 Self::HkdfSha384 => "HKDF-SHA-384",
1371 Self::HkdfSha512 => "HKDF-SHA-512",
1372 Self::KbkdfHmacSha256 => "KBKDF-HMAC-SHA-256",
1373 Self::KbkdfHmacSha384 => "KBKDF-HMAC-SHA-384",
1374 Self::KbkdfHmacSha512 => "KBKDF-HMAC-SHA-512",
1375 Self::KbkdfCmacAes128 => "KBKDF-CMAC-AES-128",
1376 Self::KbkdfCmacAes192 => "KBKDF-CMAC-AES-192",
1377 Self::KbkdfCmacAes256 => "KBKDF-CMAC-AES-256",
1378 Self::Pbkdf2HmacSha1 => "PBKDF2-HMAC-SHA-1",
1379 Self::Pbkdf2HmacSha224 => "PBKDF2-HMAC-SHA-224",
1380 Self::Pbkdf2HmacSha256 => "PBKDF2-HMAC-SHA-256",
1381 Self::Pbkdf2HmacSha384 => "PBKDF2-HMAC-SHA-384",
1382 Self::Pbkdf2HmacSha512 => "PBKDF2-HMAC-SHA-512",
1383 Self::RsaKeygen2048 => "RSA-2048 keygen",
1384 Self::RsaPkcs1v15Sign2048 => "RSA-PKCS1v1.5-Sign-2048",
1385 Self::RsaPssSign2048 => "RSA-PSS-Sign-2048",
1386 Self::RsaOaep2048 => "RSA-OAEP-2048",
1387 Self::RsaPkcs1v15Verify2048 => "RSA-PKCS1v1.5-Verify-2048",
1388 Self::RsaPssVerify2048 => "RSA-PSS-Verify-2048",
1389 Self::RsaKeygen3072 => "RSA-3072 keygen",
1390 Self::RsaPkcs1v15Sign3072 => "RSA-PKCS1v1.5-Sign-3072",
1391 Self::RsaPssSign3072 => "RSA-PSS-Sign-3072",
1392 Self::RsaOaep3072 => "RSA-OAEP-3072",
1393 Self::RsaPkcs1v15Verify3072 => "RSA-PKCS1v1.5-Verify-3072",
1394 Self::RsaPssVerify3072 => "RSA-PSS-Verify-3072",
1395 Self::RsaKeygen4096 => "RSA-4096 keygen",
1396 Self::RsaPkcs1v15Sign4096 => "RSA-PKCS1v1.5-Sign-4096",
1397 Self::RsaPssSign4096 => "RSA-PSS-Sign-4096",
1398 Self::RsaOaep4096 => "RSA-OAEP-4096",
1399 Self::RsaPkcs1v15Verify4096 => "RSA-PKCS1v1.5-Verify-4096",
1400 Self::RsaPssVerify4096 => "RSA-PSS-Verify-4096",
1401 Self::EcdsaP256Sign => "ECDSA-P256 sign",
1402 Self::EcdsaP256Verify => "ECDSA-P256 verify",
1403 Self::EcdsaP256Keygen => "ECDSA-P256 keygen",
1404 Self::EcdsaP384Sign => "ECDSA-P384 sign",
1405 Self::EcdsaP384Verify => "ECDSA-P384 verify",
1406 Self::EcdsaP384Keygen => "ECDSA-P384 keygen",
1407 Self::EcdhP256 => "ECDH-P256",
1408 Self::EcdhP384 => "ECDH-P384",
1409 Self::Ed25519Sign => "Ed25519 sign",
1410 Self::Ed25519Verify => "Ed25519 verify",
1411 Self::Ed25519Keygen => "Ed25519 keygen",
1412 Self::Tls12Kdf => "TLS 1.2 KDF",
1413 Self::Tls13Kdf => "TLS 1.3 KDF",
1414 Self::MlKem1024Encaps => "ML-KEM-1024 encaps",
1415 Self::MlKem1024Decaps => "ML-KEM-1024 decaps",
1416 Self::MlKem1024Keygen => "ML-KEM-1024 keygen",
1417 Self::MlKem512Encaps => "ML-KEM-512 encaps",
1418 Self::MlKem512Decaps => "ML-KEM-512 decaps",
1419 Self::MlKem512Keygen => "ML-KEM-512 keygen",
1420 Self::MlKem768Encaps => "ML-KEM-768 encaps",
1421 Self::MlKem768Decaps => "ML-KEM-768 decaps",
1422 Self::MlKem768Keygen => "ML-KEM-768 keygen",
1423 Self::MlDsa87Sign => "ML-DSA-87 sign",
1424 Self::MlDsa87Verify => "ML-DSA-87 verify",
1425 Self::MlDsa87Keygen => "ML-DSA-87 keygen",
1426 Self::MlDsa44Sign => "ML-DSA-44 sign",
1427 Self::MlDsa44Verify => "ML-DSA-44 verify",
1428 Self::MlDsa44Keygen => "ML-DSA-44 keygen",
1429 Self::MlDsa65Sign => "ML-DSA-65 sign",
1430 Self::MlDsa65Verify => "ML-DSA-65 verify",
1431 Self::MlDsa65Keygen => "ML-DSA-65 keygen",
1432 Self::SlhDsaSha2256sKeygen => "SLH-DSA-SHA2-256s keygen",
1433 Self::SlhDsaSha2256sSign => "SLH-DSA-SHA2-256s sign",
1434 Self::SlhDsaSha2256sVerify => "SLH-DSA-SHA2-256s verify",
1435 Self::SlhDsaSha2128sKeygen => "SLH-DSA-SHA2-128s keygen",
1436 Self::SlhDsaSha2128sSign => "SLH-DSA-SHA2-128s sign",
1437 Self::SlhDsaSha2128sVerify => "SLH-DSA-SHA2-128s verify",
1438 Self::SlhDsaSha2128fKeygen => "SLH-DSA-SHA2-128f keygen",
1439 Self::SlhDsaSha2128fSign => "SLH-DSA-SHA2-128f sign",
1440 Self::SlhDsaSha2128fVerify => "SLH-DSA-SHA2-128f verify",
1441 Self::SlhDsaSha2192sKeygen => "SLH-DSA-SHA2-192s keygen",
1442 Self::SlhDsaSha2192sSign => "SLH-DSA-SHA2-192s sign",
1443 Self::SlhDsaSha2192sVerify => "SLH-DSA-SHA2-192s verify",
1444 Self::SlhDsaSha2192fKeygen => "SLH-DSA-SHA2-192f keygen",
1445 Self::SlhDsaSha2192fSign => "SLH-DSA-SHA2-192f sign",
1446 Self::SlhDsaSha2192fVerify => "SLH-DSA-SHA2-192f verify",
1447 Self::SlhDsaSha2256fKeygen => "SLH-DSA-SHA2-256f keygen",
1448 Self::SlhDsaSha2256fSign => "SLH-DSA-SHA2-256f sign",
1449 Self::SlhDsaSha2256fVerify => "SLH-DSA-SHA2-256f verify",
1450 Self::SlhDsaShake128sKeygen => "SLH-DSA-SHAKE-128s keygen",
1451 Self::SlhDsaShake128sSign => "SLH-DSA-SHAKE-128s sign",
1452 Self::SlhDsaShake128sVerify => "SLH-DSA-SHAKE-128s verify",
1453 Self::SlhDsaShake128fKeygen => "SLH-DSA-SHAKE-128f keygen",
1454 Self::SlhDsaShake128fSign => "SLH-DSA-SHAKE-128f sign",
1455 Self::SlhDsaShake128fVerify => "SLH-DSA-SHAKE-128f verify",
1456 Self::SlhDsaShake192sKeygen => "SLH-DSA-SHAKE-192s keygen",
1457 Self::SlhDsaShake192sSign => "SLH-DSA-SHAKE-192s sign",
1458 Self::SlhDsaShake192sVerify => "SLH-DSA-SHAKE-192s verify",
1459 Self::SlhDsaShake192fKeygen => "SLH-DSA-SHAKE-192f keygen",
1460 Self::SlhDsaShake192fSign => "SLH-DSA-SHAKE-192f sign",
1461 Self::SlhDsaShake192fVerify => "SLH-DSA-SHAKE-192f verify",
1462 Self::SlhDsaShake256sKeygen => "SLH-DSA-SHAKE-256s keygen",
1463 Self::SlhDsaShake256sSign => "SLH-DSA-SHAKE-256s sign",
1464 Self::SlhDsaShake256sVerify => "SLH-DSA-SHAKE-256s verify",
1465 Self::SlhDsaShake256fKeygen => "SLH-DSA-SHAKE-256f keygen",
1466 Self::SlhDsaShake256fSign => "SLH-DSA-SHAKE-256f sign",
1467 Self::SlhDsaShake256fVerify => "SLH-DSA-SHAKE-256f verify",
1468 Self::LmsSha256M32H5W1Verify => "LMS SHA-256 M=32 H=5 W=1 verify",
1469 Self::LmsSha256M32H5W2Verify => "LMS SHA-256 M=32 H=5 W=2 verify",
1470 Self::LmsSha256M32H5W4Verify => "LMS SHA-256 M=32 H=5 W=4 verify",
1471 Self::LmsSha256M32H5W8Verify => "LMS SHA-256 M=32 H=5 W=8 verify",
1472 Self::LmsSha256M32H10W1Verify => "LMS SHA-256 M=32 H=10 W=1 verify",
1473 Self::LmsSha256M32H10W2Verify => "LMS SHA-256 M=32 H=10 W=2 verify",
1474 Self::LmsSha256M32H10W4Verify => "LMS SHA-256 M=32 H=10 W=4 verify",
1475 Self::LmsSha256M32H10W8Verify => "LMS SHA-256 M=32 H=10 W=8 verify",
1476 Self::LmsSha256M32H15W1Verify => "LMS SHA-256 M=32 H=15 W=1 verify",
1477 Self::LmsSha256M32H15W2Verify => "LMS SHA-256 M=32 H=15 W=2 verify",
1478 Self::LmsSha256M32H15W4Verify => "LMS SHA-256 M=32 H=15 W=4 verify",
1479 Self::LmsSha256M32H15W8Verify => "LMS SHA-256 M=32 H=15 W=8 verify",
1480 Self::LmsSha256M32H20W1Verify => "LMS SHA-256 M=32 H=20 W=1 verify",
1481 Self::LmsSha256M32H20W2Verify => "LMS SHA-256 M=32 H=20 W=2 verify",
1482 Self::LmsSha256M32H20W4Verify => "LMS SHA-256 M=32 H=20 W=4 verify",
1483 Self::LmsSha256M32H20W8Verify => "LMS SHA-256 M=32 H=20 W=8 verify",
1484 Self::LmsSha256M32H25W1Verify => "LMS SHA-256 M=32 H=25 W=1 verify",
1485 Self::LmsSha256M32H25W2Verify => "LMS SHA-256 M=32 H=25 W=2 verify",
1486 Self::LmsSha256M32H25W4Verify => "LMS SHA-256 M=32 H=25 W=4 verify",
1487 Self::LmsSha256M32H25W8Verify => "LMS SHA-256 M=32 H=25 W=8 verify",
1488 Self::LmsSha256M24H5W1Verify => "LMS SHA-256 M=24 H=5 W=1 verify",
1489 Self::LmsSha256M24H5W2Verify => "LMS SHA-256 M=24 H=5 W=2 verify",
1490 Self::LmsSha256M24H5W4Verify => "LMS SHA-256 M=24 H=5 W=4 verify",
1491 Self::LmsSha256M24H5W8Verify => "LMS SHA-256 M=24 H=5 W=8 verify",
1492 Self::LmsSha256M24H10W1Verify => "LMS SHA-256 M=24 H=10 W=1 verify",
1493 Self::LmsSha256M24H10W2Verify => "LMS SHA-256 M=24 H=10 W=2 verify",
1494 Self::LmsSha256M24H10W4Verify => "LMS SHA-256 M=24 H=10 W=4 verify",
1495 Self::LmsSha256M24H10W8Verify => "LMS SHA-256 M=24 H=10 W=8 verify",
1496 Self::LmsSha256M24H15W1Verify => "LMS SHA-256 M=24 H=15 W=1 verify",
1497 Self::LmsSha256M24H15W2Verify => "LMS SHA-256 M=24 H=15 W=2 verify",
1498 Self::LmsSha256M24H15W4Verify => "LMS SHA-256 M=24 H=15 W=4 verify",
1499 Self::LmsSha256M24H15W8Verify => "LMS SHA-256 M=24 H=15 W=8 verify",
1500 Self::LmsSha256M24H20W1Verify => "LMS SHA-256 M=24 H=20 W=1 verify",
1501 Self::LmsSha256M24H20W2Verify => "LMS SHA-256 M=24 H=20 W=2 verify",
1502 Self::LmsSha256M24H20W4Verify => "LMS SHA-256 M=24 H=20 W=4 verify",
1503 Self::LmsSha256M24H20W8Verify => "LMS SHA-256 M=24 H=20 W=8 verify",
1504 Self::LmsSha256M24H25W1Verify => "LMS SHA-256 M=24 H=25 W=1 verify",
1505 Self::LmsSha256M24H25W2Verify => "LMS SHA-256 M=24 H=25 W=2 verify",
1506 Self::LmsSha256M24H25W4Verify => "LMS SHA-256 M=24 H=25 W=4 verify",
1507 Self::LmsSha256M24H25W8Verify => "LMS SHA-256 M=24 H=25 W=8 verify",
1508 Self::LmsShakeM32H5W1Verify => "LMS SHAKE M=32 H=5 W=1 verify",
1509 Self::LmsShakeM32H5W2Verify => "LMS SHAKE M=32 H=5 W=2 verify",
1510 Self::LmsShakeM32H5W4Verify => "LMS SHAKE M=32 H=5 W=4 verify",
1511 Self::LmsShakeM32H5W8Verify => "LMS SHAKE M=32 H=5 W=8 verify",
1512 Self::LmsShakeM32H10W1Verify => "LMS SHAKE M=32 H=10 W=1 verify",
1513 Self::LmsShakeM32H10W2Verify => "LMS SHAKE M=32 H=10 W=2 verify",
1514 Self::LmsShakeM32H10W4Verify => "LMS SHAKE M=32 H=10 W=4 verify",
1515 Self::LmsShakeM32H10W8Verify => "LMS SHAKE M=32 H=10 W=8 verify",
1516 Self::LmsShakeM32H15W1Verify => "LMS SHAKE M=32 H=15 W=1 verify",
1517 Self::LmsShakeM32H15W2Verify => "LMS SHAKE M=32 H=15 W=2 verify",
1518 Self::LmsShakeM32H15W4Verify => "LMS SHAKE M=32 H=15 W=4 verify",
1519 Self::LmsShakeM32H15W8Verify => "LMS SHAKE M=32 H=15 W=8 verify",
1520 Self::LmsShakeM32H20W1Verify => "LMS SHAKE M=32 H=20 W=1 verify",
1521 Self::LmsShakeM32H20W2Verify => "LMS SHAKE M=32 H=20 W=2 verify",
1522 Self::LmsShakeM32H20W4Verify => "LMS SHAKE M=32 H=20 W=4 verify",
1523 Self::LmsShakeM32H20W8Verify => "LMS SHAKE M=32 H=20 W=8 verify",
1524 Self::LmsShakeM32H25W1Verify => "LMS SHAKE M=32 H=25 W=1 verify",
1525 Self::LmsShakeM32H25W2Verify => "LMS SHAKE M=32 H=25 W=2 verify",
1526 Self::LmsShakeM32H25W4Verify => "LMS SHAKE M=32 H=25 W=4 verify",
1527 Self::LmsShakeM32H25W8Verify => "LMS SHAKE M=32 H=25 W=8 verify",
1528 Self::LmsShakeM24H5W1Verify => "LMS SHAKE M=24 H=5 W=1 verify",
1529 Self::LmsShakeM24H5W2Verify => "LMS SHAKE M=24 H=5 W=2 verify",
1530 Self::LmsShakeM24H5W4Verify => "LMS SHAKE M=24 H=5 W=4 verify",
1531 Self::LmsShakeM24H5W8Verify => "LMS SHAKE M=24 H=5 W=8 verify",
1532 Self::LmsShakeM24H10W1Verify => "LMS SHAKE M=24 H=10 W=1 verify",
1533 Self::LmsShakeM24H10W2Verify => "LMS SHAKE M=24 H=10 W=2 verify",
1534 Self::LmsShakeM24H10W4Verify => "LMS SHAKE M=24 H=10 W=4 verify",
1535 Self::LmsShakeM24H10W8Verify => "LMS SHAKE M=24 H=10 W=8 verify",
1536 Self::LmsShakeM24H15W1Verify => "LMS SHAKE M=24 H=15 W=1 verify",
1537 Self::LmsShakeM24H15W2Verify => "LMS SHAKE M=24 H=15 W=2 verify",
1538 Self::LmsShakeM24H15W4Verify => "LMS SHAKE M=24 H=15 W=4 verify",
1539 Self::LmsShakeM24H15W8Verify => "LMS SHAKE M=24 H=15 W=8 verify",
1540 Self::LmsShakeM24H20W1Verify => "LMS SHAKE M=24 H=20 W=1 verify",
1541 Self::LmsShakeM24H20W2Verify => "LMS SHAKE M=24 H=20 W=2 verify",
1542 Self::LmsShakeM24H20W4Verify => "LMS SHAKE M=24 H=20 W=4 verify",
1543 Self::LmsShakeM24H20W8Verify => "LMS SHAKE M=24 H=20 W=8 verify",
1544 Self::LmsShakeM24H25W1Verify => "LMS SHAKE M=24 H=25 W=1 verify",
1545 Self::LmsShakeM24H25W2Verify => "LMS SHAKE M=24 H=25 W=2 verify",
1546 Self::LmsShakeM24H25W4Verify => "LMS SHAKE M=24 H=25 W=4 verify",
1547 Self::LmsShakeM24H25W8Verify => "LMS SHAKE M=24 H=25 W=8 verify",
1548 Self::XmssVerify => "XMSS verify",
1549 Self::Dh3072 => "DH-3072",
1550 };
1551 f.write_str(name)
1552 }
1553}
1554
1555pub fn require_allowed(service: Service) -> Result<(), Error> {
1571 let profile = active_profile();
1572 if is_allowed(profile, service) {
1573 Ok(())
1574 } else {
1575 Err(Error::AlgorithmRestricted { service })
1576 }
1577}
1578
1579const fn is_allowed(profile: AlgorithmProfile, service: Service) -> bool {
1582 match profile {
1583 AlgorithmProfile::Unrestricted => true,
1584 AlgorithmProfile::Cnsa2 => is_cnsa2_allowed(service),
1585 AlgorithmProfile::Cnsa1 => is_cnsa1_allowed(service),
1586 AlgorithmProfile::Migration => is_cnsa1_allowed(service) || is_cnsa2_allowed(service),
1592 }
1593}
1594
1595const fn is_lms_service(service: Service) -> bool {
1605 lms_block_contains(service as u16)
1606}
1607
1608const fn lms_block_contains(discriminant: u16) -> bool {
1619 discriminant >= Service::LmsSha256M32H5W1Verify as u16
1620 && discriminant <= Service::LmsShakeM24H25W8Verify as u16
1621 && discriminant & 1 == 1
1622}
1623
1624const LMS_SERVICES: [Service; 80] = [
1629 Service::LmsSha256M32H5W1Verify,
1631 Service::LmsSha256M32H5W2Verify,
1632 Service::LmsSha256M32H5W4Verify,
1633 Service::LmsSha256M32H5W8Verify,
1634 Service::LmsSha256M32H10W1Verify,
1635 Service::LmsSha256M32H10W2Verify,
1636 Service::LmsSha256M32H10W4Verify,
1637 Service::LmsSha256M32H10W8Verify,
1638 Service::LmsSha256M32H15W1Verify,
1639 Service::LmsSha256M32H15W2Verify,
1640 Service::LmsSha256M32H15W4Verify,
1641 Service::LmsSha256M32H15W8Verify,
1642 Service::LmsSha256M32H20W1Verify,
1643 Service::LmsSha256M32H20W2Verify,
1644 Service::LmsSha256M32H20W4Verify,
1645 Service::LmsSha256M32H20W8Verify,
1646 Service::LmsSha256M32H25W1Verify,
1647 Service::LmsSha256M32H25W2Verify,
1648 Service::LmsSha256M32H25W4Verify,
1649 Service::LmsSha256M32H25W8Verify,
1650 Service::LmsSha256M24H5W1Verify,
1652 Service::LmsSha256M24H5W2Verify,
1653 Service::LmsSha256M24H5W4Verify,
1654 Service::LmsSha256M24H5W8Verify,
1655 Service::LmsSha256M24H10W1Verify,
1656 Service::LmsSha256M24H10W2Verify,
1657 Service::LmsSha256M24H10W4Verify,
1658 Service::LmsSha256M24H10W8Verify,
1659 Service::LmsSha256M24H15W1Verify,
1660 Service::LmsSha256M24H15W2Verify,
1661 Service::LmsSha256M24H15W4Verify,
1662 Service::LmsSha256M24H15W8Verify,
1663 Service::LmsSha256M24H20W1Verify,
1664 Service::LmsSha256M24H20W2Verify,
1665 Service::LmsSha256M24H20W4Verify,
1666 Service::LmsSha256M24H20W8Verify,
1667 Service::LmsSha256M24H25W1Verify,
1668 Service::LmsSha256M24H25W2Verify,
1669 Service::LmsSha256M24H25W4Verify,
1670 Service::LmsSha256M24H25W8Verify,
1671 Service::LmsShakeM32H5W1Verify,
1673 Service::LmsShakeM32H5W2Verify,
1674 Service::LmsShakeM32H5W4Verify,
1675 Service::LmsShakeM32H5W8Verify,
1676 Service::LmsShakeM32H10W1Verify,
1677 Service::LmsShakeM32H10W2Verify,
1678 Service::LmsShakeM32H10W4Verify,
1679 Service::LmsShakeM32H10W8Verify,
1680 Service::LmsShakeM32H15W1Verify,
1681 Service::LmsShakeM32H15W2Verify,
1682 Service::LmsShakeM32H15W4Verify,
1683 Service::LmsShakeM32H15W8Verify,
1684 Service::LmsShakeM32H20W1Verify,
1685 Service::LmsShakeM32H20W2Verify,
1686 Service::LmsShakeM32H20W4Verify,
1687 Service::LmsShakeM32H20W8Verify,
1688 Service::LmsShakeM32H25W1Verify,
1689 Service::LmsShakeM32H25W2Verify,
1690 Service::LmsShakeM32H25W4Verify,
1691 Service::LmsShakeM32H25W8Verify,
1692 Service::LmsShakeM24H5W1Verify,
1694 Service::LmsShakeM24H5W2Verify,
1695 Service::LmsShakeM24H5W4Verify,
1696 Service::LmsShakeM24H5W8Verify,
1697 Service::LmsShakeM24H10W1Verify,
1698 Service::LmsShakeM24H10W2Verify,
1699 Service::LmsShakeM24H10W4Verify,
1700 Service::LmsShakeM24H10W8Verify,
1701 Service::LmsShakeM24H15W1Verify,
1702 Service::LmsShakeM24H15W2Verify,
1703 Service::LmsShakeM24H15W4Verify,
1704 Service::LmsShakeM24H15W8Verify,
1705 Service::LmsShakeM24H20W1Verify,
1706 Service::LmsShakeM24H20W2Verify,
1707 Service::LmsShakeM24H20W4Verify,
1708 Service::LmsShakeM24H20W8Verify,
1709 Service::LmsShakeM24H25W1Verify,
1710 Service::LmsShakeM24H25W2Verify,
1711 Service::LmsShakeM24H25W4Verify,
1712 Service::LmsShakeM24H25W8Verify,
1713];
1714
1715#[allow(clippy::indexing_slicing, clippy::arithmetic_side_effects)]
1721const _: () = {
1722 let mut i = 0;
1723 let mut slot: u16 = 501;
1724 while i < LMS_SERVICES.len() {
1725 let d = LMS_SERVICES[i] as u16;
1726 assert!(d == slot, "LMS service out of its slot");
1727 assert!(lms_block_contains(d), "LMS service outside the LMS block");
1728 slot += 2;
1729 i += 1;
1730 }
1731 assert!(
1732 LMS_SERVICES[79] as u16 == 659,
1733 "LMS block does not end at 659"
1734 );
1735};
1736
1737const fn is_cnsa2_allowed(service: Service) -> bool {
1749 matches!(
1750 service,
1751 Service::Aes256Ecb
1753 | Service::Aes256Cbc
1754 | Service::Aes256Ctr
1755 | Service::Aes256Gcm
1756 | Service::Aes256Ccm
1757 | Service::Aes256Kw
1758 | Service::Aes256Kwp
1759 | Service::Aes256
1760 | Service::Sha384
1762 | Service::Sha512
1763 | Service::HmacSha384
1769 | Service::HmacSha512
1770 | Service::CmacAes256
1772 | Service::CtrDrbgAes256
1774 | Service::HashDrbgSha384
1775 | Service::HashDrbgSha512
1776 | Service::HmacDrbgSha384
1777 | Service::HmacDrbgSha512
1778 | Service::HkdfSha384
1780 | Service::HkdfSha512
1781 | Service::KbkdfHmacSha384
1782 | Service::KbkdfHmacSha512
1783 | Service::KbkdfCmacAes256
1784 | Service::MlKem1024Encaps
1789 | Service::MlKem1024Decaps
1790 | Service::MlKem1024Keygen
1791 | Service::MlDsa87Sign
1792 | Service::MlDsa87Verify
1793 | Service::MlDsa87Keygen
1794 | Service::XmssVerify
1797 )
1798 || is_lms_service(service)
1805}
1806
1807const fn is_cnsa1_allowed(service: Service) -> bool {
1834 matches!(
1835 service,
1836 Service::Aes256
1838 | Service::Aes256Ecb
1839 | Service::Aes256Cbc
1840 | Service::Aes256Ctr
1841 | Service::Aes256Gcm
1842 | Service::Aes256Ccm
1843 | Service::Aes256Kw
1844 | Service::Aes256Kwp
1845 | Service::EcdhP384
1847 | Service::EcdsaP384Keygen
1849 | Service::EcdsaP384Sign
1850 | Service::EcdsaP384Verify
1851 | Service::Sha384
1853 | Service::Dh3072
1855 | Service::RsaKeygen3072
1857 | Service::RsaKeygen4096
1858 | Service::RsaOaep3072
1859 | Service::RsaOaep4096
1860 | Service::RsaPkcs1v15Sign3072
1862 | Service::RsaPkcs1v15Sign4096
1863 | Service::RsaPkcs1v15Verify3072
1864 | Service::RsaPkcs1v15Verify4096
1865 | Service::RsaPssSign3072
1866 | Service::RsaPssSign4096
1867 | Service::RsaPssVerify3072
1868 | Service::RsaPssVerify4096
1869 | Service::CtrDrbgAes256
1873 | Service::HashDrbgSha384
1874 | Service::HmacDrbgSha384
1875 | Service::HmacSha384
1876 | Service::CmacAes256
1877 | Service::HkdfSha384
1878 | Service::KbkdfHmacSha384
1879 | Service::KbkdfCmacAes256
1880 )
1881}
1882
1883#[cfg(test)]
1912pub(crate) mod state_lock {
1913 use super::{AlgorithmProfile, Ordering, PROFILE, STATE, State};
1914
1915 static STATE_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
1916
1917 #[must_use]
1920 pub(crate) struct StateGuard(
1921 #[allow(dead_code)]
1924 std::sync::MutexGuard<'static, ()>,
1925 );
1926
1927 impl StateGuard {
1928 #[allow(clippy::unused_self)]
1932 pub(crate) fn reset(&self) {
1933 STATE.store(State::PowerOff as u64, Ordering::Release);
1934 PROFILE.store(AlgorithmProfile::Unrestricted as u8, Ordering::Release);
1935 }
1936 }
1937
1938 pub(crate) fn reset_for_test() -> StateGuard {
1955 let guard = StateGuard(
1956 STATE_LOCK
1957 .lock()
1958 .unwrap_or_else(std::sync::PoisonError::into_inner),
1959 );
1960 guard.reset();
1961 guard
1962 }
1963}
1964
1965#[cfg(test)]
1966extern crate alloc;
1967
1968#[cfg(test)]
1969extern crate std;
1970
1971#[cfg(test)]
1972#[allow(clippy::unwrap_used, clippy::panic)]
1973mod tests {
1974 use super::state_lock::reset_for_test;
1975 use super::{
1976 AlgorithmProfile, Error, KatEntry, LMS_SERVICES, SelfTest, SelfTestFailure, Service, State,
1977 active_profile, enter_error_state, initialize_with_profile, initialize_with_tests,
1978 is_allowed, is_lms_service, is_operational, lms_block_contains, require_allowed,
1979 require_operational, require_self_test_started, state,
1980 };
1981 use alloc::string::ToString;
1982 use core::sync::atomic::Ordering;
1983
1984 const UNSIGNED_TEST_BINARY: &[KatEntry] = &[KatEntry {
1992 name: "integrity not verifiable in an unsigned test binary",
1993 run: || Ok(()),
1994 }];
1995
1996 #[allow(clippy::unnecessary_wraps)]
2000 fn always_pass() -> Result<(), SelfTestFailure> {
2001 Ok(())
2002 }
2003 fn always_fail() -> Result<(), SelfTestFailure> {
2004 Err(SelfTestFailure)
2005 }
2006
2007 static UNREACHED_RUNS: core::sync::atomic::AtomicUsize =
2009 core::sync::atomic::AtomicUsize::new(0);
2010
2011 #[allow(clippy::unnecessary_wraps)]
2013 fn count_unreached() -> Result<(), SelfTestFailure> {
2014 UNREACHED_RUNS.fetch_add(1, Ordering::AcqRel);
2015 Ok(())
2016 }
2017
2018 #[test]
2023 fn initial_state_is_power_off() {
2024 let _guard = reset_for_test();
2025 assert_eq!(state(), State::PowerOff);
2026 assert!(!is_operational());
2027 }
2028
2029 #[test]
2030 fn initialize_transitions_to_operational() {
2031 let _guard = reset_for_test();
2032 initialize_with_tests(UNSIGNED_TEST_BINARY, &[]).unwrap();
2033 assert_eq!(state(), State::Operational);
2034 assert!(is_operational());
2035 require_operational().unwrap();
2036 }
2037
2038 #[test]
2039 fn second_initialize_reports_already_initialized() {
2040 let _guard = reset_for_test();
2041 initialize_with_tests(UNSIGNED_TEST_BINARY, &[]).unwrap();
2042 match initialize_with_tests(UNSIGNED_TEST_BINARY, &[]) {
2043 Err(Error::AlreadyInitialized) => {}
2044 other => panic!("expected AlreadyInitialized, got {other:?}"),
2045 }
2046 }
2047
2048 #[test]
2049 fn require_operational_rejects_power_off() {
2050 let _guard = reset_for_test();
2051 match require_operational() {
2052 Err(Error::NotOperational {
2053 current: State::PowerOff,
2054 }) => {}
2055 other => panic!("expected NotOperational{{PowerOff}}, got {other:?}"),
2056 }
2057 }
2058
2059 #[test]
2060 fn enter_error_state_is_terminal_for_require_operational() {
2061 let _guard = reset_for_test();
2062 initialize_with_tests(UNSIGNED_TEST_BINARY, &[]).unwrap();
2063 assert!(is_operational());
2064 enter_error_state("unit-test forced failure");
2065 assert_eq!(state(), State::Error);
2066 match require_operational() {
2067 Err(Error::NotOperational {
2068 current: State::Error,
2069 }) => {}
2070 other => panic!("expected NotOperational{{Error}}, got {other:?}"),
2071 }
2072 }
2073
2074 fn guard_admits_self_test() -> Result<(), SelfTestFailure> {
2077 if state() != State::SelfTest {
2078 return Err(SelfTestFailure);
2079 }
2080 require_self_test_started().map_err(|_| SelfTestFailure)
2081 }
2082
2083 #[test]
2084 fn require_self_test_started_rejects_power_off() {
2085 let _guard = reset_for_test();
2086 assert_eq!(
2087 require_self_test_started(),
2088 Err(Error::NotOperational {
2089 current: State::PowerOff
2090 })
2091 );
2092 }
2093
2094 #[test]
2095 fn require_self_test_started_rejects_error_after_failed_init() {
2096 let _guard = reset_for_test();
2097 let tests = [KatEntry {
2098 name: "dummy-fail",
2099 run: always_fail,
2100 }];
2101 assert!(initialize_with_tests(UNSIGNED_TEST_BINARY, &tests).is_err());
2102 assert_eq!(state(), State::Error);
2103 assert_eq!(
2104 require_self_test_started(),
2105 Err(Error::NotOperational {
2106 current: State::Error
2107 })
2108 );
2109 }
2110
2111 #[test]
2112 fn require_self_test_started_rejects_integrity_unverified() {
2113 let _guard = reset_for_test();
2114 assert_eq!(
2115 initialize_with_tests(&[], &[]),
2116 Err(Error::IntegrityNotAttested)
2117 );
2118 assert_eq!(state(), State::IntegrityUnverified);
2119 assert_eq!(
2120 require_self_test_started(),
2121 Err(Error::NotOperational {
2122 current: State::IntegrityUnverified
2123 })
2124 );
2125 }
2126
2127 #[test]
2130 fn initialize_after_integrity_unverified_is_already_failed() {
2131 let _guard = reset_for_test();
2132 assert_eq!(
2133 initialize_with_tests(&[], &[]),
2134 Err(Error::IntegrityNotAttested)
2135 );
2136 assert_eq!(state(), State::IntegrityUnverified);
2137 let counting = [KatEntry {
2138 name: "counts its runs",
2139 run: count_unreached,
2140 }];
2141 UNREACHED_RUNS.store(0, Ordering::Release);
2142 assert_eq!(
2143 initialize_with_tests(UNSIGNED_TEST_BINARY, &counting),
2144 Err(Error::AlreadyFailed)
2145 );
2146 assert_eq!(UNREACHED_RUNS.load(Ordering::Acquire), 0);
2147 assert_eq!(state(), State::IntegrityUnverified);
2148 }
2149
2150 #[test]
2151 fn require_self_test_started_admits_self_test_and_operational() {
2152 let _guard = reset_for_test();
2153 let tests = [KatEntry {
2154 name: "guard admits SelfTest",
2155 run: guard_admits_self_test,
2156 }];
2157 initialize_with_tests(UNSIGNED_TEST_BINARY, &tests).unwrap();
2158 assert_eq!(state(), State::Operational);
2159 assert_eq!(require_self_test_started(), Ok(()));
2160 }
2161
2162 #[test]
2165 fn a_malformed_state_word_reads_as_error() {
2166 let guard = reset_for_test();
2167 let owner = 0x1234_u64 << super::STATE_BITS;
2168 let malformed = [
2169 State::SelfTest as u64,
2171 State::Operational as u64 | owner,
2173 State::PowerOff as u64 | owner,
2174 State::IntegrityUnverified as u64 | owner,
2175 5,
2177 0xff,
2178 0xff | owner,
2179 ];
2180 assert_eq!(malformed.len(), 7);
2181 for word in malformed {
2182 guard.reset();
2183 super::STATE.store(word, Ordering::Release);
2184 assert_eq!(state(), State::Error, "word {word:#x}");
2185 assert!(!is_operational(), "word {word:#x}");
2186 assert_eq!(
2187 require_operational(),
2188 Err(Error::NotOperational {
2189 current: State::Error
2190 }),
2191 "word {word:#x}"
2192 );
2193 assert_eq!(
2194 require_self_test_started(),
2195 Err(Error::NotOperational {
2196 current: State::Error
2197 }),
2198 "word {word:#x}"
2199 );
2200 }
2201 guard.reset();
2203 assert_eq!(state(), State::PowerOff);
2204 super::STATE.store(State::Operational as u64, Ordering::Release);
2205 assert_eq!(state(), State::Operational);
2206 }
2207
2208 static RELEASE_FAILING_WINNER: core::sync::atomic::AtomicBool =
2211 core::sync::atomic::AtomicBool::new(false);
2212 static FAILING_WINNER_PAUSED: core::sync::atomic::AtomicBool =
2214 core::sync::atomic::AtomicBool::new(false);
2215
2216 fn wait_until(what: &str, done: impl Fn() -> bool) {
2218 let start = std::time::Instant::now();
2219 while !done() {
2220 assert!(
2221 start.elapsed() < std::time::Duration::from_secs(30),
2222 "timed out waiting for {what}"
2223 );
2224 std::thread::yield_now();
2225 }
2226 }
2227
2228 fn paused_then_fails() -> Result<(), SelfTestFailure> {
2229 FAILING_WINNER_PAUSED.store(true, Ordering::Release);
2230 wait_until("the release of the failing winner", || {
2231 RELEASE_FAILING_WINNER.load(Ordering::Acquire)
2232 });
2233 Err(SelfTestFailure)
2234 }
2235
2236 #[test]
2239 fn loser_waits_then_gets_already_failed() {
2240 let _guard = reset_for_test();
2241 FAILING_WINNER_PAUSED.store(false, Ordering::Release);
2242 RELEASE_FAILING_WINNER.store(false, Ordering::Release);
2243 let waiting_before = super::WAITING_LOSERS.load(Ordering::Acquire);
2244 let winner = std::thread::spawn(|| {
2245 let tests = [KatEntry {
2246 name: "pauses, then fails",
2247 run: paused_then_fails,
2248 }];
2249 initialize_with_tests(UNSIGNED_TEST_BINARY, &tests)
2250 });
2251 wait_until("the winner to pause", || {
2252 FAILING_WINNER_PAUSED.load(Ordering::Acquire)
2253 });
2254 let loser = std::thread::spawn(|| initialize_with_tests(UNSIGNED_TEST_BINARY, &[]));
2255 wait_until("the loser to enter its wait", || {
2256 super::WAITING_LOSERS.load(Ordering::Acquire) > waiting_before
2257 });
2258 assert!(!loser.is_finished());
2259 RELEASE_FAILING_WINNER.store(true, Ordering::Release);
2260 assert_eq!(
2261 winner.join().unwrap(),
2262 Err(Error::SelfTestFailed {
2263 test: "pauses, then fails"
2264 })
2265 );
2266 assert_eq!(loser.join().unwrap(), Err(Error::AlreadyFailed));
2267 assert_eq!(state(), State::Error);
2268 }
2269
2270 #[test]
2272 fn the_owner_id_filter_admits_only_recordable_ids() {
2273 assert_eq!(super::owner_id(None), None);
2274 assert_eq!(super::owner_id(Some(0)), None);
2275 assert_eq!(super::owner_id(Some(1)), Some(1));
2276 assert_eq!(super::owner_id(Some((1 << 56) - 1)), Some((1 << 56) - 1));
2277 assert_eq!(super::owner_id(Some(1 << 56)), None);
2278 assert_eq!(super::owner_id(Some(u64::MAX)), None);
2279 }
2280
2281 #[test]
2284 fn no_recordable_thread_id_latches_error_with_its_cause() {
2285 let guard = reset_for_test();
2286 let tests = [KatEntry {
2287 name: "must not run",
2288 run: always_fail,
2289 }];
2290 for raw in [None, Some(0), Some(1 << 56)] {
2291 guard.reset();
2292 assert_eq!(
2293 super::initialize_as(
2294 super::owner_id(raw),
2295 UNSIGNED_TEST_BINARY,
2296 &tests,
2297 AlgorithmProfile::Unrestricted
2298 ),
2299 Err(Error::SelfTestFailed {
2300 test: super::THREAD_ID_CHECK
2301 }),
2302 "raw id {raw:?}"
2303 );
2304 assert_eq!(state(), State::Error);
2305 assert_eq!(
2306 initialize_with_tests(UNSIGNED_TEST_BINARY, &[]),
2307 Err(Error::AlreadyFailed)
2308 );
2309 }
2310 }
2311
2312 #[test]
2313 fn state_display_is_stable() {
2314 assert_eq!(State::PowerOff.to_string(), "PowerOff");
2317 assert_eq!(State::SelfTest.to_string(), "SelfTest");
2318 assert_eq!(State::Operational.to_string(), "Operational");
2319 assert_eq!(State::Error.to_string(), "Error");
2320 }
2321
2322 #[test]
2323 fn registry_runs_passing_tests_and_reaches_operational() {
2324 let _guard = reset_for_test();
2325 let tests = [
2326 KatEntry {
2327 name: "dummy-pass-a",
2328 run: always_pass,
2329 },
2330 KatEntry {
2331 name: "dummy-pass-b",
2332 run: always_pass,
2333 },
2334 ];
2335 initialize_with_tests(UNSIGNED_TEST_BINARY, &tests).unwrap();
2336 assert_eq!(state(), State::Operational);
2337 }
2338
2339 #[test]
2340 fn registry_failing_test_latches_error_and_returns_name() {
2341 let _guard = reset_for_test();
2342 let tests = [
2343 KatEntry {
2344 name: "dummy-pass",
2345 run: always_pass,
2346 },
2347 KatEntry {
2348 name: "dummy-fail",
2349 run: always_fail,
2350 },
2351 KatEntry {
2353 name: "dummy-unreached",
2354 run: count_unreached,
2355 },
2356 ];
2357 UNREACHED_RUNS.store(0, Ordering::Release);
2358 match initialize_with_tests(UNSIGNED_TEST_BINARY, &tests) {
2359 Err(Error::SelfTestFailed { test: "dummy-fail" }) => {}
2360 other => panic!("expected SelfTestFailed{{dummy-fail}}, got {other:?}"),
2361 }
2362 assert_eq!(UNREACHED_RUNS.load(Ordering::Acquire), 0);
2363 assert_eq!(state(), State::Error);
2364 assert!(matches!(
2366 require_operational(),
2367 Err(Error::NotOperational {
2368 current: State::Error
2369 })
2370 ));
2371 }
2372
2373 struct DummyTest;
2376 impl SelfTest for DummyTest {
2377 const NAME: &'static str = "dummy-trait-vector";
2378 fn run() -> Result<(), SelfTestFailure> {
2379 Ok(())
2380 }
2381 }
2382
2383 #[test]
2384 fn dummy_self_test_trait_is_usable() {
2385 assert_eq!(DummyTest::NAME, "dummy-trait-vector");
2386 DummyTest::run().unwrap();
2387 }
2388
2389 #[test]
2394 fn default_profile_is_unrestricted() {
2395 let _guard = reset_for_test();
2396 assert_eq!(active_profile(), AlgorithmProfile::Unrestricted);
2397 }
2398
2399 #[test]
2400 fn initialize_with_profile_sets_cnsa2() {
2401 let _guard = reset_for_test();
2402 initialize_with_profile(UNSIGNED_TEST_BINARY, &[], AlgorithmProfile::Cnsa2).unwrap();
2403 assert_eq!(active_profile(), AlgorithmProfile::Cnsa2);
2404 assert!(is_operational());
2405 }
2406
2407 #[test]
2410 fn a_second_call_does_not_change_the_profile() {
2411 let _guard = reset_for_test();
2412 initialize_with_profile(UNSIGNED_TEST_BINARY, &[], AlgorithmProfile::Cnsa2).unwrap();
2413 match initialize_with_profile(UNSIGNED_TEST_BINARY, &[], AlgorithmProfile::Unrestricted) {
2414 Err(Error::AlreadyInitialized) => {}
2415 other => panic!("expected AlreadyInitialized, got {other:?}"),
2416 }
2417 assert_eq!(active_profile(), AlgorithmProfile::Cnsa2);
2418 match initialize_with_tests(UNSIGNED_TEST_BINARY, &[]) {
2419 Err(Error::AlreadyInitialized) => {}
2420 other => panic!("expected AlreadyInitialized, got {other:?}"),
2421 }
2422 assert_eq!(active_profile(), AlgorithmProfile::Cnsa2);
2423 }
2424
2425 static RACER_CALLED: core::sync::atomic::AtomicBool =
2428 core::sync::atomic::AtomicBool::new(false);
2429
2430 #[allow(clippy::unnecessary_wraps)]
2431 fn wait_for_racer() -> Result<(), SelfTestFailure> {
2432 while !RACER_CALLED.load(Ordering::Acquire) {
2433 std::thread::yield_now();
2434 }
2435 std::thread::sleep(std::time::Duration::from_millis(50));
2438 Ok(())
2439 }
2440
2441 #[test]
2444 fn a_racing_call_does_not_change_the_profile() {
2445 let _guard = reset_for_test();
2446 RACER_CALLED.store(false, Ordering::Release);
2447 let tests = [KatEntry {
2448 name: "waits for the racing call",
2449 run: wait_for_racer,
2450 }];
2451 let winner = std::thread::spawn(move || {
2452 initialize_with_profile(UNSIGNED_TEST_BINARY, &tests, AlgorithmProfile::Cnsa2)
2453 });
2454 while state() != State::SelfTest {
2455 std::thread::yield_now();
2456 }
2457 RACER_CALLED.store(true, Ordering::Release);
2458 let racer =
2459 initialize_with_profile(UNSIGNED_TEST_BINARY, &[], AlgorithmProfile::Unrestricted);
2460 let winner = winner.join().unwrap();
2461 assert!(winner.is_ok(), "the first call failed: {winner:?}");
2462 match racer {
2463 Err(Error::AlreadyInitialized) => {}
2464 other => panic!("expected AlreadyInitialized, got {other:?}"),
2465 }
2466 assert_eq!(active_profile(), AlgorithmProfile::Cnsa2);
2467 }
2468
2469 #[test]
2472 fn a_failed_initialisation_leaves_the_default_profile() {
2473 let guard = reset_for_test();
2474 let failing = [KatEntry {
2475 name: "always fails",
2476 run: always_fail,
2477 }];
2478 match initialize_with_profile(UNSIGNED_TEST_BINARY, &failing, AlgorithmProfile::Cnsa2) {
2479 Err(Error::SelfTestFailed { .. }) => {}
2480 other => panic!("expected SelfTestFailed, got {other:?}"),
2481 }
2482 assert_eq!(state(), State::Error);
2483 assert_eq!(active_profile(), AlgorithmProfile::Unrestricted);
2484
2485 guard.reset();
2486 match initialize_with_profile(&[], &[], AlgorithmProfile::Cnsa1) {
2487 Err(Error::IntegrityNotAttested) => {}
2488 other => panic!("expected IntegrityNotAttested, got {other:?}"),
2489 }
2490 assert_eq!(active_profile(), AlgorithmProfile::Unrestricted);
2491 }
2492
2493 #[test]
2494 fn initialize_with_profile_sets_cnsa1() {
2495 let _guard = reset_for_test();
2496 initialize_with_profile(UNSIGNED_TEST_BINARY, &[], AlgorithmProfile::Cnsa1).unwrap();
2497 assert_eq!(active_profile(), AlgorithmProfile::Cnsa1);
2498 assert!(is_operational());
2499 }
2500
2501 #[test]
2502 fn unrestricted_allows_all_services() {
2503 let spot = [
2505 Service::Sha1,
2506 Service::Sha256,
2507 Service::Aes128Ecb,
2508 Service::Aes256Gcm,
2509 Service::CtrDrbgAes128,
2510 Service::EcdsaP256Sign,
2511 Service::Ed25519Sign,
2512 Service::RsaPssSign2048,
2513 Service::MlKem1024Encaps,
2514 Service::LmsSha256M32H10W4Verify,
2515 Service::Dh3072,
2516 Service::SlhDsaSha2256sSign,
2517 ];
2518 for svc in spot {
2519 assert!(
2520 is_allowed(AlgorithmProfile::Unrestricted, svc),
2521 "{svc} should be allowed in Unrestricted"
2522 );
2523 }
2524 }
2525
2526 #[test]
2527 fn cnsa2_allows_aes256_and_pq() {
2528 let allowed = [
2529 Service::Aes256Gcm,
2530 Service::Aes256Cbc,
2531 Service::Sha384,
2532 Service::Sha512,
2533 Service::HmacSha384,
2534 Service::HmacSha512,
2535 Service::CtrDrbgAes256,
2536 Service::HashDrbgSha384,
2537 Service::HmacDrbgSha512,
2538 Service::MlKem1024Encaps,
2539 Service::MlKem1024Decaps,
2540 Service::MlKem1024Keygen,
2541 Service::MlDsa87Sign,
2542 Service::MlDsa87Verify,
2543 Service::MlDsa87Keygen,
2544 Service::LmsSha256M32H10W4Verify,
2545 Service::XmssVerify,
2546 ];
2547 for svc in allowed {
2548 assert!(
2549 is_allowed(AlgorithmProfile::Cnsa2, svc),
2550 "{svc} should be allowed in CNSA 2.0"
2551 );
2552 }
2553 }
2554
2555 #[test]
2563 fn cnsa2_refuses_the_keccak_family() {
2564 let refused = [
2565 Service::Shake256,
2566 Service::CShake256,
2567 Service::Kmac256,
2568 Service::KmacXof256,
2569 Service::TupleHash256,
2570 Service::TupleHashXof256,
2571 Service::ParallelHash256,
2572 Service::ParallelHashXof256,
2573 Service::Sha3_384,
2574 Service::Sha3_512,
2575 Service::HmacSha3_384,
2576 Service::HmacSha3_512,
2577 ];
2578 for svc in refused {
2579 assert!(
2580 !is_allowed(AlgorithmProfile::Cnsa2, svc),
2581 "{svc} is not in CNSA 2.0 and must be refused"
2582 );
2583 }
2584 assert!(is_allowed(AlgorithmProfile::Cnsa2, Service::Sha384));
2587 assert!(is_allowed(
2589 AlgorithmProfile::Cnsa2,
2590 Service::MlKem1024Encaps
2591 ));
2592 }
2593
2594 #[test]
2595 fn cnsa2_blocks_classical_and_small_keys() {
2596 let blocked = [
2597 Service::Sha1,
2598 Service::Sha224,
2599 Service::Sha256,
2600 Service::Sha512_224,
2601 Service::Sha512_256,
2602 Service::Sha3_224,
2603 Service::Sha3_256,
2604 Service::Shake128,
2605 Service::Kmac128,
2606 Service::Aes128Ecb,
2607 Service::Aes128Gcm,
2608 Service::Aes192Cbc,
2609 Service::CtrDrbgAes128,
2610 Service::CtrDrbgAes192,
2611 Service::HashDrbgSha256,
2612 Service::HmacDrbgSha256,
2613 Service::HmacSha1,
2614 Service::HmacSha256,
2615 Service::CmacAes128,
2616 Service::CmacAes192,
2617 Service::EcdsaP256Sign,
2618 Service::EcdsaP256Verify,
2619 Service::EcdhP256,
2620 Service::Ed25519Sign,
2621 Service::Ed25519Verify,
2622 Service::RsaPssSign2048,
2623 Service::RsaOaep2048,
2624 Service::SlhDsaSha2128sSign,
2626 Service::MlDsa44Sign,
2627 Service::MlDsa44Verify,
2628 Service::MlDsa44Keygen,
2629 Service::MlDsa65Sign,
2630 Service::MlDsa65Verify,
2631 Service::MlDsa65Keygen,
2632 ];
2633 for svc in blocked {
2634 assert!(
2635 !is_allowed(AlgorithmProfile::Cnsa2, svc),
2636 "{svc} should be BLOCKED in CNSA 2.0"
2637 );
2638 }
2639 }
2640
2641 const ALL_SERVICES: [Service; 256] = [
2656 Service::Sha1,
2657 Service::Sha224,
2658 Service::Sha256,
2659 Service::Sha384,
2660 Service::Sha512,
2661 Service::Sha512_224,
2662 Service::Sha512_256,
2663 Service::Sha3_224,
2664 Service::Sha3_256,
2665 Service::Sha3_384,
2666 Service::Sha3_512,
2667 Service::Shake128,
2668 Service::Shake256,
2669 Service::CShake128,
2670 Service::CShake256,
2671 Service::Kmac128,
2672 Service::Kmac256,
2673 Service::KmacXof128,
2674 Service::KmacXof256,
2675 Service::TupleHash128,
2676 Service::TupleHash256,
2677 Service::TupleHashXof128,
2678 Service::TupleHashXof256,
2679 Service::ParallelHash128,
2680 Service::ParallelHash256,
2681 Service::ParallelHashXof128,
2682 Service::ParallelHashXof256,
2683 Service::HmacSha1,
2684 Service::HmacSha224,
2685 Service::HmacSha256,
2686 Service::HmacSha384,
2687 Service::HmacSha512,
2688 Service::HmacSha512_224,
2689 Service::HmacSha512_256,
2690 Service::HmacSha3_224,
2691 Service::HmacSha3_256,
2692 Service::HmacSha3_384,
2693 Service::HmacSha3_512,
2694 Service::CmacAes128,
2695 Service::CmacAes192,
2696 Service::CmacAes256,
2697 Service::Aes128Ecb,
2698 Service::Aes128Cbc,
2699 Service::Aes128Ctr,
2700 Service::Aes128Gcm,
2701 Service::Aes128Ccm,
2702 Service::Aes128Kw,
2703 Service::Aes128Kwp,
2704 Service::Aes192Ecb,
2705 Service::Aes192Cbc,
2706 Service::Aes192Ctr,
2707 Service::Aes192Gcm,
2708 Service::Aes192Ccm,
2709 Service::Aes192Kw,
2710 Service::Aes192Kwp,
2711 Service::Aes256Ecb,
2712 Service::Aes256Cbc,
2713 Service::Aes256Ctr,
2714 Service::Aes256Gcm,
2715 Service::Aes256Ccm,
2716 Service::Aes256Kw,
2717 Service::Aes256Kwp,
2718 Service::Aes128,
2719 Service::Aes192,
2720 Service::Aes256,
2721 Service::CtrDrbgAes128,
2722 Service::CtrDrbgAes192,
2723 Service::CtrDrbgAes256,
2724 Service::HashDrbgSha256,
2725 Service::HashDrbgSha384,
2726 Service::HashDrbgSha512,
2727 Service::HmacDrbgSha256,
2728 Service::HmacDrbgSha384,
2729 Service::HmacDrbgSha512,
2730 Service::HkdfSha1,
2731 Service::HkdfSha256,
2732 Service::HkdfSha384,
2733 Service::HkdfSha512,
2734 Service::KbkdfHmacSha256,
2735 Service::KbkdfHmacSha384,
2736 Service::KbkdfHmacSha512,
2737 Service::KbkdfCmacAes128,
2738 Service::KbkdfCmacAes192,
2739 Service::KbkdfCmacAes256,
2740 Service::Pbkdf2HmacSha1,
2741 Service::Pbkdf2HmacSha224,
2742 Service::Pbkdf2HmacSha256,
2743 Service::Pbkdf2HmacSha384,
2744 Service::Pbkdf2HmacSha512,
2745 Service::RsaKeygen2048,
2746 Service::RsaPkcs1v15Sign2048,
2747 Service::RsaPssSign2048,
2748 Service::RsaOaep2048,
2749 Service::RsaPkcs1v15Verify2048,
2750 Service::RsaPssVerify2048,
2751 Service::RsaKeygen3072,
2752 Service::RsaPkcs1v15Sign3072,
2753 Service::RsaPssSign3072,
2754 Service::RsaOaep3072,
2755 Service::RsaPkcs1v15Verify3072,
2756 Service::RsaPssVerify3072,
2757 Service::RsaKeygen4096,
2758 Service::RsaPkcs1v15Sign4096,
2759 Service::RsaPssSign4096,
2760 Service::RsaOaep4096,
2761 Service::RsaPkcs1v15Verify4096,
2762 Service::RsaPssVerify4096,
2763 Service::EcdsaP256Sign,
2764 Service::EcdsaP256Verify,
2765 Service::EcdsaP256Keygen,
2766 Service::EcdsaP384Sign,
2767 Service::EcdsaP384Verify,
2768 Service::EcdsaP384Keygen,
2769 Service::EcdhP256,
2770 Service::EcdhP384,
2771 Service::Ed25519Sign,
2772 Service::Ed25519Verify,
2773 Service::Ed25519Keygen,
2774 Service::Tls12Kdf,
2775 Service::Tls13Kdf,
2776 Service::MlKem1024Encaps,
2777 Service::MlKem1024Decaps,
2778 Service::MlKem1024Keygen,
2779 Service::MlKem512Encaps,
2780 Service::MlKem512Decaps,
2781 Service::MlKem512Keygen,
2782 Service::MlKem768Encaps,
2783 Service::MlKem768Decaps,
2784 Service::MlKem768Keygen,
2785 Service::MlDsa87Sign,
2786 Service::MlDsa87Verify,
2787 Service::MlDsa87Keygen,
2788 Service::MlDsa44Sign,
2789 Service::MlDsa44Verify,
2790 Service::MlDsa44Keygen,
2791 Service::MlDsa65Sign,
2792 Service::MlDsa65Verify,
2793 Service::MlDsa65Keygen,
2794 Service::SlhDsaSha2256sKeygen,
2795 Service::SlhDsaSha2256sSign,
2796 Service::SlhDsaSha2256sVerify,
2797 Service::SlhDsaSha2128sKeygen,
2798 Service::SlhDsaSha2128sSign,
2799 Service::SlhDsaSha2128sVerify,
2800 Service::SlhDsaSha2128fKeygen,
2801 Service::SlhDsaSha2128fSign,
2802 Service::SlhDsaSha2128fVerify,
2803 Service::SlhDsaSha2192sKeygen,
2804 Service::SlhDsaSha2192sSign,
2805 Service::SlhDsaSha2192sVerify,
2806 Service::SlhDsaSha2192fKeygen,
2807 Service::SlhDsaSha2192fSign,
2808 Service::SlhDsaSha2192fVerify,
2809 Service::SlhDsaSha2256fKeygen,
2810 Service::SlhDsaSha2256fSign,
2811 Service::SlhDsaSha2256fVerify,
2812 Service::SlhDsaShake128sKeygen,
2813 Service::SlhDsaShake128sSign,
2814 Service::SlhDsaShake128sVerify,
2815 Service::SlhDsaShake128fKeygen,
2816 Service::SlhDsaShake128fSign,
2817 Service::SlhDsaShake128fVerify,
2818 Service::SlhDsaShake192sKeygen,
2819 Service::SlhDsaShake192sSign,
2820 Service::SlhDsaShake192sVerify,
2821 Service::SlhDsaShake192fKeygen,
2822 Service::SlhDsaShake192fSign,
2823 Service::SlhDsaShake192fVerify,
2824 Service::SlhDsaShake256sKeygen,
2825 Service::SlhDsaShake256sSign,
2826 Service::SlhDsaShake256sVerify,
2827 Service::SlhDsaShake256fKeygen,
2828 Service::SlhDsaShake256fSign,
2829 Service::SlhDsaShake256fVerify,
2830 Service::LmsSha256M32H5W1Verify,
2831 Service::LmsSha256M32H5W2Verify,
2832 Service::LmsSha256M32H5W4Verify,
2833 Service::LmsSha256M32H5W8Verify,
2834 Service::LmsSha256M32H10W1Verify,
2835 Service::LmsSha256M32H10W2Verify,
2836 Service::LmsSha256M32H10W4Verify,
2837 Service::LmsSha256M32H10W8Verify,
2838 Service::LmsSha256M32H15W1Verify,
2839 Service::LmsSha256M32H15W2Verify,
2840 Service::LmsSha256M32H15W4Verify,
2841 Service::LmsSha256M32H15W8Verify,
2842 Service::LmsSha256M32H20W1Verify,
2843 Service::LmsSha256M32H20W2Verify,
2844 Service::LmsSha256M32H20W4Verify,
2845 Service::LmsSha256M32H20W8Verify,
2846 Service::LmsSha256M32H25W1Verify,
2847 Service::LmsSha256M32H25W2Verify,
2848 Service::LmsSha256M32H25W4Verify,
2849 Service::LmsSha256M32H25W8Verify,
2850 Service::LmsSha256M24H5W1Verify,
2851 Service::LmsSha256M24H5W2Verify,
2852 Service::LmsSha256M24H5W4Verify,
2853 Service::LmsSha256M24H5W8Verify,
2854 Service::LmsSha256M24H10W1Verify,
2855 Service::LmsSha256M24H10W2Verify,
2856 Service::LmsSha256M24H10W4Verify,
2857 Service::LmsSha256M24H10W8Verify,
2858 Service::LmsSha256M24H15W1Verify,
2859 Service::LmsSha256M24H15W2Verify,
2860 Service::LmsSha256M24H15W4Verify,
2861 Service::LmsSha256M24H15W8Verify,
2862 Service::LmsSha256M24H20W1Verify,
2863 Service::LmsSha256M24H20W2Verify,
2864 Service::LmsSha256M24H20W4Verify,
2865 Service::LmsSha256M24H20W8Verify,
2866 Service::LmsSha256M24H25W1Verify,
2867 Service::LmsSha256M24H25W2Verify,
2868 Service::LmsSha256M24H25W4Verify,
2869 Service::LmsSha256M24H25W8Verify,
2870 Service::LmsShakeM32H5W1Verify,
2871 Service::LmsShakeM32H5W2Verify,
2872 Service::LmsShakeM32H5W4Verify,
2873 Service::LmsShakeM32H5W8Verify,
2874 Service::LmsShakeM32H10W1Verify,
2875 Service::LmsShakeM32H10W2Verify,
2876 Service::LmsShakeM32H10W4Verify,
2877 Service::LmsShakeM32H10W8Verify,
2878 Service::LmsShakeM32H15W1Verify,
2879 Service::LmsShakeM32H15W2Verify,
2880 Service::LmsShakeM32H15W4Verify,
2881 Service::LmsShakeM32H15W8Verify,
2882 Service::LmsShakeM32H20W1Verify,
2883 Service::LmsShakeM32H20W2Verify,
2884 Service::LmsShakeM32H20W4Verify,
2885 Service::LmsShakeM32H20W8Verify,
2886 Service::LmsShakeM32H25W1Verify,
2887 Service::LmsShakeM32H25W2Verify,
2888 Service::LmsShakeM32H25W4Verify,
2889 Service::LmsShakeM32H25W8Verify,
2890 Service::LmsShakeM24H5W1Verify,
2891 Service::LmsShakeM24H5W2Verify,
2892 Service::LmsShakeM24H5W4Verify,
2893 Service::LmsShakeM24H5W8Verify,
2894 Service::LmsShakeM24H10W1Verify,
2895 Service::LmsShakeM24H10W2Verify,
2896 Service::LmsShakeM24H10W4Verify,
2897 Service::LmsShakeM24H10W8Verify,
2898 Service::LmsShakeM24H15W1Verify,
2899 Service::LmsShakeM24H15W2Verify,
2900 Service::LmsShakeM24H15W4Verify,
2901 Service::LmsShakeM24H15W8Verify,
2902 Service::LmsShakeM24H20W1Verify,
2903 Service::LmsShakeM24H20W2Verify,
2904 Service::LmsShakeM24H20W4Verify,
2905 Service::LmsShakeM24H20W8Verify,
2906 Service::LmsShakeM24H25W1Verify,
2907 Service::LmsShakeM24H25W2Verify,
2908 Service::LmsShakeM24H25W4Verify,
2909 Service::LmsShakeM24H25W8Verify,
2910 Service::XmssVerify,
2911 Service::Dh3072,
2912 ];
2913
2914 const EXPECTED_CNSA1: [Service; 34] = [
2917 Service::Sha384,
2918 Service::HmacSha384,
2919 Service::CmacAes256,
2920 Service::Aes256Ecb,
2921 Service::Aes256Cbc,
2922 Service::Aes256Ctr,
2923 Service::Aes256Gcm,
2924 Service::Aes256Ccm,
2925 Service::Aes256Kw,
2926 Service::Aes256Kwp,
2927 Service::Aes256,
2928 Service::CtrDrbgAes256,
2929 Service::HashDrbgSha384,
2930 Service::HmacDrbgSha384,
2931 Service::HkdfSha384,
2932 Service::KbkdfHmacSha384,
2933 Service::KbkdfCmacAes256,
2934 Service::RsaKeygen3072,
2935 Service::RsaPkcs1v15Sign3072,
2936 Service::RsaPssSign3072,
2937 Service::RsaOaep3072,
2938 Service::RsaPkcs1v15Verify3072,
2939 Service::RsaPssVerify3072,
2940 Service::RsaKeygen4096,
2941 Service::RsaPkcs1v15Sign4096,
2942 Service::RsaPssSign4096,
2943 Service::RsaOaep4096,
2944 Service::RsaPkcs1v15Verify4096,
2945 Service::RsaPssVerify4096,
2946 Service::EcdsaP384Sign,
2947 Service::EcdsaP384Verify,
2948 Service::EcdsaP384Keygen,
2949 Service::EcdhP384,
2950 Service::Dh3072,
2951 ];
2952
2953 const EXPECTED_CNSA2_NON_LMS: [Service; 30] = [
2956 Service::Sha384,
2957 Service::Sha512,
2958 Service::HmacSha384,
2959 Service::HmacSha512,
2960 Service::CmacAes256,
2961 Service::Aes256Ecb,
2962 Service::Aes256Cbc,
2963 Service::Aes256Ctr,
2964 Service::Aes256Gcm,
2965 Service::Aes256Ccm,
2966 Service::Aes256Kw,
2967 Service::Aes256Kwp,
2968 Service::Aes256,
2969 Service::CtrDrbgAes256,
2970 Service::HashDrbgSha384,
2971 Service::HashDrbgSha512,
2972 Service::HmacDrbgSha384,
2973 Service::HmacDrbgSha512,
2974 Service::HkdfSha384,
2975 Service::HkdfSha512,
2976 Service::KbkdfHmacSha384,
2977 Service::KbkdfHmacSha512,
2978 Service::KbkdfCmacAes256,
2979 Service::MlKem1024Encaps,
2980 Service::MlKem1024Decaps,
2981 Service::MlKem1024Keygen,
2982 Service::MlDsa87Sign,
2983 Service::MlDsa87Verify,
2984 Service::MlDsa87Keygen,
2985 Service::XmssVerify,
2986 ];
2987
2988 fn assert_exact_membership(profile: AlgorithmProfile, expected: &[Service], allow_lms: bool) {
2991 for svc in ALL_SERVICES {
2992 let want = expected.iter().any(|e| *e as u16 == svc as u16)
2993 || (allow_lms && is_lms_service(svc));
2994 assert_eq!(
2995 is_allowed(profile, svc),
2996 want,
2997 "{svc} under {profile}: gate says {}, suite says {want}",
2998 is_allowed(profile, svc)
2999 );
3000 }
3001 }
3002
3003 #[test]
3013 fn every_profile_round_trips_through_the_active_profile() {
3014 for profile in [
3015 AlgorithmProfile::Unrestricted,
3016 AlgorithmProfile::Cnsa2,
3017 AlgorithmProfile::Cnsa1,
3018 AlgorithmProfile::Migration,
3019 ] {
3020 assert_eq!(
3021 AlgorithmProfile::from_u8(profile as u8),
3022 profile,
3023 "{profile} did not survive the round trip through its discriminant"
3024 );
3025 }
3026
3027 for raw in [4u8, 9, 200, 255] {
3031 assert_ne!(
3032 AlgorithmProfile::from_u8(raw),
3033 AlgorithmProfile::Unrestricted,
3034 "unknown discriminant {raw} must not open the module"
3035 );
3036 }
3037 }
3038
3039 #[test]
3045 fn migration_gates_as_itself_through_require_allowed() {
3046 let guard = reset_for_test();
3047 guard.reset();
3048 initialize_with_profile(UNSIGNED_TEST_BINARY, &[], AlgorithmProfile::Migration).unwrap();
3049 assert_eq!(active_profile(), AlgorithmProfile::Migration);
3050
3051 assert!(
3053 require_allowed(Service::EcdsaP384Sign).is_ok(),
3054 "ECDSA P-384 is in CNSA 1.0 and must pass under Migration"
3055 );
3056 assert!(
3057 require_allowed(Service::Dh3072).is_ok(),
3058 "DH-3072 is in CNSA 1.0 and must pass under Migration"
3059 );
3060 assert!(
3062 require_allowed(Service::MlKem1024Encaps).is_ok(),
3063 "ML-KEM-1024 is in CNSA 2.0 and must pass under Migration"
3064 );
3065 assert!(require_allowed(Service::Sha256).is_err());
3067 }
3068
3069 #[test]
3072 fn profile_membership_is_exact() {
3073 assert_exact_membership(AlgorithmProfile::Cnsa1, &EXPECTED_CNSA1, false);
3074 assert_exact_membership(AlgorithmProfile::Cnsa2, &EXPECTED_CNSA2_NON_LMS, true);
3075
3076 for svc in ALL_SERVICES {
3079 let want = is_allowed(AlgorithmProfile::Cnsa1, svc)
3080 || is_allowed(AlgorithmProfile::Cnsa2, svc);
3081 assert_eq!(
3082 is_allowed(AlgorithmProfile::Migration, svc),
3083 want,
3084 "{svc}: Migration must permit exactly the union"
3085 );
3086 }
3087
3088 for svc in ALL_SERVICES {
3091 assert!(is_allowed(AlgorithmProfile::Unrestricted, svc));
3092 }
3093 }
3094
3095 #[test]
3102 fn migration_is_the_union_of_both_suites() {
3103 for svc in [
3105 Service::EcdsaP384Sign,
3106 Service::EcdhP384,
3107 Service::RsaPssSign3072,
3108 Service::Dh3072,
3109 ] {
3110 assert!(
3111 is_allowed(AlgorithmProfile::Migration, svc),
3112 "{svc} (CNSA 1.0) must pass"
3113 );
3114 assert!(
3115 !is_allowed(AlgorithmProfile::Cnsa2, svc),
3116 "{svc} is not CNSA 2.0"
3117 );
3118 }
3119
3120 for svc in [
3122 Service::MlKem1024Encaps,
3123 Service::MlDsa87Sign,
3124 Service::XmssVerify,
3125 Service::Sha512,
3126 Service::LmsSha256M32H10W4Verify,
3127 ] {
3128 assert!(
3129 is_allowed(AlgorithmProfile::Migration, svc),
3130 "{svc} (CNSA 2.0) must pass"
3131 );
3132 assert!(
3133 !is_allowed(AlgorithmProfile::Cnsa1, svc),
3134 "{svc} is not CNSA 1.0"
3135 );
3136 }
3137
3138 for svc in [Service::Aes256Gcm, Service::Sha384, Service::HmacSha384] {
3141 assert!(is_allowed(AlgorithmProfile::Cnsa1, svc));
3142 assert!(is_allowed(AlgorithmProfile::Cnsa2, svc));
3143 assert!(is_allowed(AlgorithmProfile::Migration, svc));
3144 }
3145
3146 for svc in [
3149 Service::Sha1,
3150 Service::Sha256,
3151 Service::Aes128Gcm,
3152 Service::RsaPssSign2048,
3153 Service::EcdsaP256Sign,
3154 Service::Ed25519Sign,
3155 Service::Sha3_384,
3157 Service::Shake256,
3158 Service::Kmac256,
3159 ] {
3160 assert!(
3161 !is_allowed(AlgorithmProfile::Migration, svc),
3162 "{svc} is in neither suite and must be refused"
3163 );
3164 assert!(is_allowed(AlgorithmProfile::Unrestricted, svc));
3167 }
3168 }
3169
3170 #[test]
3171 fn cnsa1_allows_the_2016_suite() {
3172 let allowed = [
3173 Service::Aes256Gcm,
3174 Service::Sha384,
3175 Service::EcdsaP384Sign,
3176 Service::EcdsaP384Verify,
3177 Service::EcdsaP384Keygen,
3178 Service::EcdhP384,
3179 Service::RsaPssSign3072,
3180 Service::RsaOaep3072,
3181 Service::RsaPssSign4096,
3182 Service::Dh3072,
3183 Service::HmacSha384,
3185 Service::CtrDrbgAes256,
3186 Service::HashDrbgSha384,
3187 Service::KbkdfHmacSha384,
3188 ];
3189 for svc in allowed {
3190 assert!(
3191 is_allowed(AlgorithmProfile::Cnsa1, svc),
3192 "{svc} should be allowed in CNSA 1.0"
3193 );
3194 }
3195 }
3196
3197 #[test]
3198 fn cnsa1_blocks_small_keys_and_ed25519() {
3199 let blocked = [
3200 Service::Sha1,
3201 Service::Sha224,
3202 Service::Aes128Ecb,
3203 Service::Aes192Gcm,
3204 Service::CtrDrbgAes128,
3205 Service::EcdsaP256Sign,
3206 Service::EcdhP256,
3207 Service::Ed25519Sign,
3208 Service::Ed25519Verify,
3209 Service::RsaPssSign2048,
3210 Service::RsaOaep2048,
3211 Service::Sha256,
3217 Service::HmacSha256,
3218 Service::HashDrbgSha256,
3219 Service::HkdfSha256,
3220 Service::Sha512,
3222 Service::HmacSha512,
3223 Service::Sha3_256,
3225 Service::Shake256,
3226 Service::MlKem1024Encaps,
3228 Service::MlDsa87Sign,
3229 Service::XmssVerify,
3230 Service::LmsSha256M32H10W4Verify,
3231 ];
3232 for svc in blocked {
3233 assert!(
3234 !is_allowed(AlgorithmProfile::Cnsa1, svc),
3235 "{svc} should be BLOCKED in CNSA 1.0"
3236 );
3237 }
3238 }
3239
3240 #[test]
3241 fn lms_gating_is_exhaustive_across_all_80_variants() {
3242 let all_80 = LMS_SERVICES;
3248 assert_eq!(all_80.len(), 80, "LMS enumeration drift");
3249
3250 for svc in all_80 {
3251 assert!(
3253 is_allowed(AlgorithmProfile::Unrestricted, svc),
3254 "{svc} must be allowed in Unrestricted"
3255 );
3256
3257 assert!(
3260 is_allowed(AlgorithmProfile::Cnsa2, svc),
3261 "{svc} must be allowed in CNSA 2.0"
3262 );
3263
3264 assert!(
3267 !is_allowed(AlgorithmProfile::Cnsa1, svc),
3268 "{svc} is not in CNSA 1.0 and must be refused"
3269 );
3270
3271 assert!(
3273 is_allowed(AlgorithmProfile::Migration, svc),
3274 "{svc} must be allowed in Migration"
3275 );
3276 }
3277 }
3278
3279 #[test]
3280 fn lms_discriminant_slots_are_complete_and_pure() {
3281 let mut discriminants = [0u16; 80];
3287 for (slot, svc) in discriminants.iter_mut().zip(LMS_SERVICES) {
3288 *slot = svc as u16;
3289 }
3290 discriminants.sort_unstable();
3291
3292 let mut compared = 0usize;
3297 for pair in discriminants.windows(2) {
3298 let [lo, hi] = pair else {
3299 unreachable!("windows(2) yields two-element slices")
3300 };
3301 assert_eq!(*hi, lo + 2, "LMS slot after {lo} is missing or duplicated");
3302 compared += 1;
3303 }
3304 assert_eq!(compared, 79, "slot sweep did not run in full");
3305
3306 let (Some(&first), Some(&last)) = (discriminants.first(), discriminants.last()) else {
3307 unreachable!("the array holds 80 elements")
3308 };
3309 assert_eq!(first, 501, "LMS block does not start at 501");
3310 assert_eq!(last, 659, "LMS block does not end at 659");
3311
3312 for svc in LMS_SERVICES {
3313 assert!(is_lms_service(svc), "{svc} must be inside the LMS block");
3314 }
3315
3316 let lms_count = ALL_SERVICES
3320 .iter()
3321 .filter(|svc| is_lms_service(**svc))
3322 .count();
3323 assert_eq!(lms_count, 80, "a non-LMS variant sits on an LMS slot");
3324
3325 for svc in [
3328 Service::SlhDsaShake256fVerify,
3329 Service::XmssVerify,
3330 Service::Dh3072,
3331 ] {
3332 assert!(!is_lms_service(svc), "{svc} must be outside the LMS block");
3333 }
3334 }
3335
3336 #[test]
3337 fn slh_dsa_gating_is_exhaustive_across_all_36_variants() {
3338 let all_36 = [
3346 Service::SlhDsaSha2256sKeygen,
3348 Service::SlhDsaSha2256sSign,
3349 Service::SlhDsaSha2256sVerify,
3350 Service::SlhDsaSha2128sKeygen,
3351 Service::SlhDsaSha2128sSign,
3352 Service::SlhDsaSha2128sVerify,
3353 Service::SlhDsaSha2128fKeygen,
3354 Service::SlhDsaSha2128fSign,
3355 Service::SlhDsaSha2128fVerify,
3356 Service::SlhDsaSha2192sKeygen,
3357 Service::SlhDsaSha2192sSign,
3358 Service::SlhDsaSha2192sVerify,
3359 Service::SlhDsaSha2192fKeygen,
3360 Service::SlhDsaSha2192fSign,
3361 Service::SlhDsaSha2192fVerify,
3362 Service::SlhDsaSha2256fKeygen,
3363 Service::SlhDsaSha2256fSign,
3364 Service::SlhDsaSha2256fVerify,
3365 Service::SlhDsaShake128sKeygen,
3367 Service::SlhDsaShake128sSign,
3368 Service::SlhDsaShake128sVerify,
3369 Service::SlhDsaShake128fKeygen,
3370 Service::SlhDsaShake128fSign,
3371 Service::SlhDsaShake128fVerify,
3372 Service::SlhDsaShake192sKeygen,
3373 Service::SlhDsaShake192sSign,
3374 Service::SlhDsaShake192sVerify,
3375 Service::SlhDsaShake192fKeygen,
3376 Service::SlhDsaShake192fSign,
3377 Service::SlhDsaShake192fVerify,
3378 Service::SlhDsaShake256sKeygen,
3379 Service::SlhDsaShake256sSign,
3380 Service::SlhDsaShake256sVerify,
3381 Service::SlhDsaShake256fKeygen,
3382 Service::SlhDsaShake256fSign,
3383 Service::SlhDsaShake256fVerify,
3384 ];
3385 assert_eq!(all_36.len(), 36, "SLH-DSA enumeration drift");
3386
3387 for svc in all_36 {
3388 assert!(
3390 is_allowed(AlgorithmProfile::Unrestricted, svc),
3391 "{svc} must be allowed in Unrestricted"
3392 );
3393
3394 assert!(
3396 !is_allowed(AlgorithmProfile::Cnsa1, svc),
3397 "{svc} must be BLOCKED in CNSA 1.0"
3398 );
3399
3400 assert!(
3404 !is_allowed(AlgorithmProfile::Cnsa2, svc),
3405 "{svc} must be BLOCKED in CNSA 2.0"
3406 );
3407 }
3408 }
3409
3410 #[test]
3411 fn require_allowed_returns_restricted_error_under_cnsa2() {
3412 let _guard = reset_for_test();
3413 initialize_with_profile(UNSIGNED_TEST_BINARY, &[], AlgorithmProfile::Cnsa2).unwrap();
3414 match require_allowed(Service::Aes128Ecb) {
3415 Err(Error::AlgorithmRestricted {
3416 service: Service::Aes128Ecb,
3417 }) => {}
3418 other => panic!("expected AlgorithmRestricted(Aes128Ecb), got {other:?}"),
3419 }
3420 }
3421
3422 #[test]
3423 fn require_allowed_passes_permitted_services() {
3424 let _guard = reset_for_test();
3425 initialize_with_profile(UNSIGNED_TEST_BINARY, &[], AlgorithmProfile::Cnsa2).unwrap();
3426 require_allowed(Service::Aes256Gcm).unwrap();
3427 require_allowed(Service::Sha384).unwrap();
3428 require_allowed(Service::MlKem1024Encaps).unwrap();
3429 }
3430
3431 #[test]
3432 fn require_allowed_passes_everything_in_unrestricted() {
3433 let _guard = reset_for_test();
3434 initialize_with_tests(UNSIGNED_TEST_BINARY, &[]).unwrap();
3435 require_allowed(Service::Sha1).unwrap();
3437 require_allowed(Service::Aes128Ecb).unwrap();
3438 require_allowed(Service::Ed25519Sign).unwrap();
3439 require_allowed(Service::MlKem1024Encaps).unwrap();
3440 }
3441
3442 #[test]
3448 fn require_allowed_refuses_every_slh_dsa_service_under_cnsa2() {
3449 let guard = reset_for_test();
3450 for profile in [AlgorithmProfile::Cnsa2, AlgorithmProfile::Cnsa1] {
3451 guard.reset();
3452 initialize_with_profile(UNSIGNED_TEST_BINARY, &[], profile).unwrap();
3453 for svc in [
3454 Service::SlhDsaSha2256sKeygen,
3455 Service::SlhDsaSha2256sSign,
3456 Service::SlhDsaSha2256sVerify,
3457 Service::SlhDsaShake128fVerify,
3458 ] {
3459 match require_allowed(svc) {
3460 Err(Error::AlgorithmRestricted { service }) => assert_eq!(service, svc),
3461 other => {
3462 panic!("expected AlgorithmRestricted({svc}) under {profile}, got {other:?}")
3463 }
3464 }
3465 }
3466 }
3467 }
3468
3469 #[test]
3477 fn require_allowed_admits_lms_m24_under_cnsa2_and_migration() {
3478 let guard = reset_for_test();
3479 for profile in [AlgorithmProfile::Cnsa2, AlgorithmProfile::Migration] {
3480 guard.reset();
3481 initialize_with_profile(UNSIGNED_TEST_BINARY, &[], profile).unwrap();
3482 for svc in [
3483 Service::LmsSha256M24H5W1Verify,
3484 Service::LmsSha256M24H25W8Verify,
3485 Service::LmsShakeM24H10W4Verify,
3486 Service::LmsShakeM24H25W8Verify,
3487 ] {
3488 require_allowed(svc)
3489 .unwrap_or_else(|e| panic!("{svc} must be allowed under {profile}: {e:?}"));
3490 }
3491 }
3492 }
3493
3494 #[test]
3501 fn lms_block_boundaries_are_exact() {
3502 assert!(!lms_block_contains(499), "499 is below the LMS block");
3503 assert!(!lms_block_contains(500), "500 is even");
3504 assert!(lms_block_contains(501), "501 is the first LMS discriminant");
3505 assert!(!lms_block_contains(502), "502 is even");
3506 assert!(!lms_block_contains(658), "658 is even");
3507 assert!(lms_block_contains(659), "659 is the last LMS discriminant");
3508 assert!(!lms_block_contains(660), "660 is above the LMS block");
3509 assert!(!lms_block_contains(661), "661 is above the LMS block");
3510
3511 assert_eq!(Service::LmsSha256M32H5W1Verify as u16, 501);
3513 assert_eq!(Service::LmsShakeM24H25W8Verify as u16, 659);
3514 }
3515
3516 #[test]
3521 fn require_allowed_distinguishes_cnsa1_from_cnsa2() {
3522 let guard = reset_for_test();
3523
3524 initialize_with_profile(UNSIGNED_TEST_BINARY, &[], AlgorithmProfile::Cnsa1).unwrap();
3525 require_allowed(Service::EcdsaP384Sign)
3526 .unwrap_or_else(|e| panic!("P-384 ECDSA must be permitted under CNSA 1.0: {e:?}"));
3527
3528 guard.reset();
3529 initialize_with_profile(UNSIGNED_TEST_BINARY, &[], AlgorithmProfile::Cnsa2).unwrap();
3530 match require_allowed(Service::EcdsaP384Sign) {
3531 Err(Error::AlgorithmRestricted {
3532 service: Service::EcdsaP384Sign,
3533 }) => {}
3534 other => panic!("expected P-384 ECDSA refused under CNSA 2.0, got {other:?}"),
3535 }
3536 }
3537
3538 #[test]
3539 fn drbg_minimum_length_errors_display_their_cause() {
3540 let entropy = Error::EntropyInputTooShort.to_string();
3541 let nonce = Error::NonceTooShort.to_string();
3542 assert!(
3543 entropy.contains("entropy input is shorter than the minimum"),
3544 "{entropy}"
3545 );
3546 assert!(nonce.contains("nonce is shorter than half"), "{nonce}");
3547 assert_ne!(entropy, Error::InvalidInput.to_string());
3548 }
3549
3550 #[test]
3551 fn algorithm_profile_display() {
3552 assert_eq!(AlgorithmProfile::Unrestricted.to_string(), "Unrestricted");
3553 assert_eq!(AlgorithmProfile::Cnsa2.to_string(), "CNSA 2.0");
3554 assert_eq!(AlgorithmProfile::Cnsa1.to_string(), "CNSA 1.0");
3555 }
3556
3557 #[test]
3558 fn algorithm_restricted_error_display() {
3559 let _guard = reset_for_test();
3560 initialize_with_profile(UNSIGNED_TEST_BINARY, &[], AlgorithmProfile::Cnsa2).unwrap();
3561 let err = Error::AlgorithmRestricted {
3562 service: Service::Aes128Ecb,
3563 };
3564 let msg = err.to_string();
3565 assert!(msg.contains("AES-128-ECB"), "got: {msg}");
3566 assert!(msg.contains("CNSA 2.0"), "got: {msg}");
3567 }
3568
3569 #[test]
3570 fn not_implemented_error_display() {
3571 let err = Error::NotImplemented;
3572 assert_eq!(err.to_string(), "algorithm not yet implemented");
3573 }
3574
3575 #[test]
3576 fn service_display_is_stable() {
3577 assert_eq!(Service::Sha256.to_string(), "SHA-256");
3579 assert_eq!(Service::Aes256Gcm.to_string(), "AES-256-GCM");
3580 assert_eq!(Service::MlKem1024Encaps.to_string(), "ML-KEM-1024 encaps");
3581 assert_eq!(Service::Ed25519Sign.to_string(), "Ed25519 sign");
3582 }
3583}