Skip to main content

sbom_tools/model/
crypto.rs

1//! Cryptographic Bill of Materials (CBOM) data structures.
2//!
3//! Format-agnostic representation of cryptographic assets as defined by
4//! CycloneDX 1.6+ `cryptoProperties`. Supports four asset types:
5//! algorithms, certificates, key material, and protocols.
6
7use chrono::{DateTime, Utc};
8use serde::{Deserialize, Serialize};
9
10// ── Top-level CryptoProperties ──────────────────────────────────────────
11
12/// Cryptographic properties for a component of type `cryptographic-asset`.
13///
14/// Mirrors the CycloneDX 1.6+ `cryptoProperties` object. Exactly one of
15/// the four property sub-structs should be populated, matching `asset_type`.
16#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
17#[non_exhaustive]
18pub struct CryptoProperties {
19    /// The type of cryptographic asset.
20    pub asset_type: CryptoAssetType,
21    /// Object Identifier (OID) for unambiguous algorithm identification.
22    pub oid: Option<String>,
23    /// Properties specific to algorithm assets.
24    pub algorithm_properties: Option<AlgorithmProperties>,
25    /// Properties specific to certificate assets.
26    pub certificate_properties: Option<CertificateProperties>,
27    /// Properties specific to key material assets.
28    pub related_crypto_material_properties: Option<RelatedCryptoMaterialProperties>,
29    /// Properties specific to protocol assets.
30    pub protocol_properties: Option<ProtocolProperties>,
31}
32
33impl CryptoProperties {
34    /// Create new crypto properties with the given asset type.
35    #[must_use]
36    pub fn new(asset_type: CryptoAssetType) -> Self {
37        Self {
38            asset_type,
39            oid: None,
40            algorithm_properties: None,
41            certificate_properties: None,
42            related_crypto_material_properties: None,
43            protocol_properties: None,
44        }
45    }
46
47    #[must_use]
48    pub fn with_oid(mut self, oid: String) -> Self {
49        self.oid = Some(oid);
50        self
51    }
52
53    #[must_use]
54    pub fn with_algorithm_properties(mut self, props: AlgorithmProperties) -> Self {
55        self.algorithm_properties = Some(props);
56        self
57    }
58
59    #[must_use]
60    pub fn with_certificate_properties(mut self, props: CertificateProperties) -> Self {
61        self.certificate_properties = Some(props);
62        self
63    }
64
65    #[must_use]
66    pub fn with_related_crypto_material_properties(
67        mut self,
68        props: RelatedCryptoMaterialProperties,
69    ) -> Self {
70        self.related_crypto_material_properties = Some(props);
71        self
72    }
73
74    #[must_use]
75    pub fn with_protocol_properties(mut self, props: ProtocolProperties) -> Self {
76        self.protocol_properties = Some(props);
77        self
78    }
79}
80
81// ── Asset Type ──────────────────────────────────────────────────────────
82
83/// Type of cryptographic asset.
84#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
85#[non_exhaustive]
86pub enum CryptoAssetType {
87    Algorithm,
88    Certificate,
89    RelatedCryptoMaterial,
90    Protocol,
91    Other(String),
92}
93
94impl std::fmt::Display for CryptoAssetType {
95    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
96        match self {
97            Self::Algorithm => write!(f, "algorithm"),
98            Self::Certificate => write!(f, "certificate"),
99            Self::RelatedCryptoMaterial => write!(f, "related-crypto-material"),
100            Self::Protocol => write!(f, "protocol"),
101            Self::Other(s) => write!(f, "{s}"),
102        }
103    }
104}
105
106// ── Algorithm Properties ────────────────────────────────────────────────
107
108/// Properties of a cryptographic algorithm asset.
109#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
110#[non_exhaustive]
111pub struct AlgorithmProperties {
112    /// Cryptographic primitive category.
113    pub primitive: CryptoPrimitive,
114    /// Algorithm family name (e.g., "AES", "ML-KEM", "SHA-2"). CycloneDX 1.7+.
115    pub algorithm_family: Option<String>,
116    /// Parameter set identifier (e.g., "256", "1024", "P-384").
117    pub parameter_set_identifier: Option<String>,
118    /// Block cipher mode of operation.
119    pub mode: Option<CryptoMode>,
120    /// Padding scheme.
121    pub padding: Option<CryptoPadding>,
122    /// Cryptographic functions this algorithm supports.
123    pub crypto_functions: Vec<CryptoFunction>,
124    /// Execution environment.
125    pub execution_environment: Option<ExecutionEnvironment>,
126    /// Implementation platform.
127    pub implementation_platform: Option<ImplementationPlatform>,
128    /// Certification levels achieved.
129    pub certification_level: Vec<CertificationLevel>,
130    /// Classical security level in bits.
131    pub classical_security_level: Option<u32>,
132    /// NIST post-quantum security category (0 = vulnerable, 1-5 = increasing resistance).
133    pub nist_quantum_security_level: Option<u8>,
134    /// Elliptic curve identifier (CycloneDX 1.7+, e.g., "secg/secp521r1").
135    pub elliptic_curve: Option<String>,
136}
137
138impl AlgorithmProperties {
139    /// Create new algorithm properties with the given primitive.
140    #[must_use]
141    pub fn new(primitive: CryptoPrimitive) -> Self {
142        Self {
143            primitive,
144            algorithm_family: None,
145            parameter_set_identifier: None,
146            mode: None,
147            padding: None,
148            crypto_functions: Vec::new(),
149            execution_environment: None,
150            implementation_platform: None,
151            certification_level: Vec::new(),
152            classical_security_level: None,
153            nist_quantum_security_level: None,
154            elliptic_curve: None,
155        }
156    }
157
158    /// Returns `true` if this algorithm has post-quantum security
159    /// (`nistQuantumSecurityLevel > 0`).
160    #[must_use]
161    pub fn is_quantum_safe(&self) -> bool {
162        self.nist_quantum_security_level.is_some_and(|l| l > 0)
163    }
164
165    /// Returns `true` if this is a hybrid PQC scheme (combiner primitive).
166    #[must_use]
167    pub fn is_hybrid_pqc(&self) -> bool {
168        self.primitive == CryptoPrimitive::Combiner
169    }
170
171    /// Returns `true` if the algorithm is considered broken or weak.
172    /// Checks `algorithm_family` first, then falls back to matching
173    /// common weak names in the `parameter_set_identifier`.
174    #[must_use]
175    pub fn is_weak(&self) -> bool {
176        /// Unconditionally broken/weak algorithm families.
177        const WEAK_FAMILIES: &[&str] = &[
178            "MD5", "MD4", "MD2", "SHA-1", "DES", "3DES", "TDEA", "RC2", "RC4", "BLOWFISH", "IDEA",
179            "CAST5",
180        ];
181
182        if let Some(family) = &self.algorithm_family {
183            let upper = family.to_uppercase();
184            if WEAK_FAMILIES.iter().any(|w| upper == *w) {
185                return true;
186            }
187        }
188        false
189    }
190
191    /// Returns `true` if the algorithm is considered broken or weak,
192    /// using the component name as a fallback when `algorithm_family` is absent.
193    #[must_use]
194    pub fn is_weak_by_name(&self, component_name: &str) -> bool {
195        if self.is_weak() {
196            return true;
197        }
198        // Fallback: check component name for weak algorithm patterns
199        let upper = component_name.to_uppercase();
200        upper.starts_with("MD5")
201            || upper.starts_with("MD4")
202            || upper.starts_with("SHA-1")
203            || upper.starts_with("DES")
204            || upper.starts_with("3DES")
205            || upper.starts_with("RC4")
206            || upper.starts_with("RC2")
207            || upper.starts_with("BLOWFISH")
208    }
209
210    /// Returns the classical security level in bits, if known.
211    #[must_use]
212    pub fn effective_security_bits(&self) -> Option<u32> {
213        self.classical_security_level
214    }
215
216    #[must_use]
217    pub fn with_algorithm_family(mut self, family: String) -> Self {
218        self.algorithm_family = Some(family);
219        self
220    }
221
222    #[must_use]
223    pub fn with_parameter_set_identifier(mut self, id: String) -> Self {
224        self.parameter_set_identifier = Some(id);
225        self
226    }
227
228    #[must_use]
229    pub fn with_mode(mut self, mode: CryptoMode) -> Self {
230        self.mode = Some(mode);
231        self
232    }
233
234    #[must_use]
235    pub fn with_padding(mut self, padding: CryptoPadding) -> Self {
236        self.padding = Some(padding);
237        self
238    }
239
240    #[must_use]
241    pub fn with_crypto_functions(mut self, funcs: Vec<CryptoFunction>) -> Self {
242        self.crypto_functions = funcs;
243        self
244    }
245
246    #[must_use]
247    pub fn with_execution_environment(mut self, env: ExecutionEnvironment) -> Self {
248        self.execution_environment = Some(env);
249        self
250    }
251
252    #[must_use]
253    pub fn with_implementation_platform(mut self, platform: ImplementationPlatform) -> Self {
254        self.implementation_platform = Some(platform);
255        self
256    }
257
258    #[must_use]
259    pub fn with_certification_level(mut self, levels: Vec<CertificationLevel>) -> Self {
260        self.certification_level = levels;
261        self
262    }
263
264    #[must_use]
265    pub fn with_classical_security_level(mut self, bits: u32) -> Self {
266        self.classical_security_level = Some(bits);
267        self
268    }
269
270    #[must_use]
271    pub fn with_nist_quantum_security_level(mut self, level: u8) -> Self {
272        self.nist_quantum_security_level = Some(level);
273        self
274    }
275
276    #[must_use]
277    pub fn with_elliptic_curve(mut self, curve: String) -> Self {
278        self.elliptic_curve = Some(curve);
279        self
280    }
281}
282
283// ── Certificate Properties ──────────────────────────────────────────────
284
285/// Properties of a digital certificate asset.
286#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
287#[non_exhaustive]
288pub struct CertificateProperties {
289    /// Certificate subject distinguished name.
290    pub subject_name: Option<String>,
291    /// Certificate issuer distinguished name.
292    pub issuer_name: Option<String>,
293    /// Start of validity period.
294    pub not_valid_before: Option<DateTime<Utc>>,
295    /// End of validity period.
296    pub not_valid_after: Option<DateTime<Utc>>,
297    /// Bom-ref of the signature algorithm component.
298    pub signature_algorithm_ref: Option<String>,
299    /// Bom-ref of the subject public key component.
300    pub subject_public_key_ref: Option<String>,
301    /// Certificate format (e.g., "X.509").
302    pub certificate_format: Option<String>,
303    /// Certificate file extension (e.g., "pem", "crt", "der").
304    pub certificate_extension: Option<String>,
305}
306
307impl CertificateProperties {
308    #[must_use]
309    pub fn new() -> Self {
310        Self {
311            subject_name: None,
312            issuer_name: None,
313            not_valid_before: None,
314            not_valid_after: None,
315            signature_algorithm_ref: None,
316            subject_public_key_ref: None,
317            certificate_format: None,
318            certificate_extension: None,
319        }
320    }
321
322    /// Returns `true` if the certificate has expired.
323    #[must_use]
324    pub fn is_expired(&self) -> bool {
325        self.not_valid_after
326            .is_some_and(|expiry| expiry < Utc::now())
327    }
328
329    /// Returns `true` if the certificate expires within the given number of days.
330    #[must_use]
331    pub fn is_expiring_soon(&self, days: u32) -> bool {
332        self.not_valid_after.is_some_and(|expiry| {
333            let threshold = Utc::now() + chrono::Duration::days(i64::from(days));
334            expiry <= threshold && expiry > Utc::now()
335        })
336    }
337
338    /// Returns remaining days until expiry, or `None` if no expiry date is set.
339    /// Returns negative values for already-expired certificates.
340    #[must_use]
341    pub fn validity_days(&self) -> Option<i64> {
342        self.not_valid_after
343            .map(|expiry| (expiry - Utc::now()).num_days())
344    }
345
346    #[must_use]
347    pub fn with_subject_name(mut self, name: String) -> Self {
348        self.subject_name = Some(name);
349        self
350    }
351
352    #[must_use]
353    pub fn with_issuer_name(mut self, name: String) -> Self {
354        self.issuer_name = Some(name);
355        self
356    }
357
358    #[must_use]
359    pub fn with_not_valid_before(mut self, dt: DateTime<Utc>) -> Self {
360        self.not_valid_before = Some(dt);
361        self
362    }
363
364    #[must_use]
365    pub fn with_not_valid_after(mut self, dt: DateTime<Utc>) -> Self {
366        self.not_valid_after = Some(dt);
367        self
368    }
369
370    #[must_use]
371    pub fn with_signature_algorithm_ref(mut self, r: String) -> Self {
372        self.signature_algorithm_ref = Some(r);
373        self
374    }
375
376    #[must_use]
377    pub fn with_subject_public_key_ref(mut self, r: String) -> Self {
378        self.subject_public_key_ref = Some(r);
379        self
380    }
381
382    #[must_use]
383    pub fn with_certificate_format(mut self, fmt: String) -> Self {
384        self.certificate_format = Some(fmt);
385        self
386    }
387
388    #[must_use]
389    pub fn with_certificate_extension(mut self, ext: String) -> Self {
390        self.certificate_extension = Some(ext);
391        self
392    }
393}
394
395impl Default for CertificateProperties {
396    fn default() -> Self {
397        Self::new()
398    }
399}
400
401// ── Related Crypto Material Properties ──────────────────────────────────
402
403/// Properties of a cryptographic key or related material asset.
404#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
405#[non_exhaustive]
406pub struct RelatedCryptoMaterialProperties {
407    /// Type of key material.
408    pub material_type: CryptoMaterialType,
409    /// Unique identifier for the material.
410    pub id: Option<String>,
411    /// Lifecycle state of the material.
412    pub state: Option<CryptoMaterialState>,
413    /// Key size in bits.
414    pub size: Option<u32>,
415    /// Bom-ref of the associated algorithm component.
416    pub algorithm_ref: Option<String>,
417    /// How this material is protected.
418    pub secured_by: Option<SecuredBy>,
419    /// Key encoding format (e.g., "PEM", "DER").
420    pub format: Option<String>,
421    /// When the material was created.
422    pub creation_date: Option<DateTime<Utc>>,
423    /// When the material was activated.
424    pub activation_date: Option<DateTime<Utc>>,
425    /// When the material was last updated.
426    pub update_date: Option<DateTime<Utc>>,
427    /// When the material expires.
428    pub expiration_date: Option<DateTime<Utc>>,
429}
430
431impl RelatedCryptoMaterialProperties {
432    #[must_use]
433    pub fn new(material_type: CryptoMaterialType) -> Self {
434        Self {
435            material_type,
436            id: None,
437            state: None,
438            size: None,
439            algorithm_ref: None,
440            secured_by: None,
441            format: None,
442            creation_date: None,
443            activation_date: None,
444            update_date: None,
445            expiration_date: None,
446        }
447    }
448
449    #[must_use]
450    pub fn with_id(mut self, id: String) -> Self {
451        self.id = Some(id);
452        self
453    }
454
455    #[must_use]
456    pub fn with_state(mut self, state: CryptoMaterialState) -> Self {
457        self.state = Some(state);
458        self
459    }
460
461    #[must_use]
462    pub fn with_size(mut self, bits: u32) -> Self {
463        self.size = Some(bits);
464        self
465    }
466
467    #[must_use]
468    pub fn with_algorithm_ref(mut self, r: String) -> Self {
469        self.algorithm_ref = Some(r);
470        self
471    }
472
473    #[must_use]
474    pub fn with_secured_by(mut self, secured: SecuredBy) -> Self {
475        self.secured_by = Some(secured);
476        self
477    }
478
479    #[must_use]
480    pub fn with_format(mut self, fmt: String) -> Self {
481        self.format = Some(fmt);
482        self
483    }
484
485    #[must_use]
486    pub fn with_creation_date(mut self, dt: DateTime<Utc>) -> Self {
487        self.creation_date = Some(dt);
488        self
489    }
490
491    #[must_use]
492    pub fn with_activation_date(mut self, dt: DateTime<Utc>) -> Self {
493        self.activation_date = Some(dt);
494        self
495    }
496
497    #[must_use]
498    pub fn with_expiration_date(mut self, dt: DateTime<Utc>) -> Self {
499        self.expiration_date = Some(dt);
500        self
501    }
502}
503
504// ── Protocol Properties ─────────────────────────────────────────────────
505
506/// Properties of a cryptographic protocol asset.
507#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
508#[non_exhaustive]
509pub struct ProtocolProperties {
510    /// Protocol type.
511    pub protocol_type: ProtocolType,
512    /// Protocol version (e.g., "1.3" for TLS).
513    pub version: Option<String>,
514    /// Cipher suites supported by this protocol.
515    pub cipher_suites: Vec<CipherSuite>,
516    /// IKEv2 transform types (for IPsec protocols).
517    pub ikev2_transform_types: Option<Ikev2TransformTypes>,
518    /// Bom-refs of related crypto assets used by this protocol.
519    pub crypto_ref_array: Vec<String>,
520}
521
522impl ProtocolProperties {
523    #[must_use]
524    pub fn new(protocol_type: ProtocolType) -> Self {
525        Self {
526            protocol_type,
527            version: None,
528            cipher_suites: Vec::new(),
529            ikev2_transform_types: None,
530            crypto_ref_array: Vec::new(),
531        }
532    }
533
534    #[must_use]
535    pub fn with_version(mut self, version: String) -> Self {
536        self.version = Some(version);
537        self
538    }
539
540    #[must_use]
541    pub fn with_cipher_suites(mut self, suites: Vec<CipherSuite>) -> Self {
542        self.cipher_suites = suites;
543        self
544    }
545
546    #[must_use]
547    pub fn with_ikev2_transform_types(mut self, types: Ikev2TransformTypes) -> Self {
548        self.ikev2_transform_types = Some(types);
549        self
550    }
551
552    #[must_use]
553    pub fn with_crypto_ref_array(mut self, refs: Vec<String>) -> Self {
554        self.crypto_ref_array = refs;
555        self
556    }
557}
558
559// ── Supporting Structs ──────────────────────────────────────────────────
560
561/// A cipher suite within a protocol.
562#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
563pub struct CipherSuite {
564    /// Cipher suite name (e.g., `"TLS_AES_256_GCM_SHA384"`).
565    pub name: Option<String>,
566    /// Bom-refs of the constituent algorithm components.
567    pub algorithms: Vec<String>,
568    /// IANA cipher suite identifiers (e.g., `["0x13", "0x02"]`).
569    pub identifiers: Vec<String>,
570}
571
572/// IKEv2 transform types for IPsec protocols (RFC 9370).
573#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
574pub struct Ikev2TransformTypes {
575    /// Encryption algorithm bom-refs.
576    pub encr: Vec<String>,
577    /// Pseudorandom function bom-refs.
578    pub prf: Vec<String>,
579    /// Integrity algorithm bom-refs.
580    pub integ: Vec<String>,
581    /// Key exchange method bom-refs.
582    pub ke: Vec<String>,
583}
584
585/// How a cryptographic material is secured/protected.
586#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
587pub struct SecuredBy {
588    /// Protection mechanism (e.g., "Software", "HSM").
589    pub mechanism: String,
590    /// Bom-ref of the protection algorithm, if applicable.
591    pub algorithm_ref: Option<String>,
592}
593
594// ── Enums ───────────────────────────────────────────────────────────────
595
596/// Cryptographic primitive type.
597#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
598#[non_exhaustive]
599pub enum CryptoPrimitive {
600    /// Authenticated encryption (e.g., AES-GCM).
601    Ae,
602    /// Block cipher (e.g., AES-CBC).
603    BlockCipher,
604    /// Stream cipher (e.g., ChaCha20).
605    StreamCipher,
606    /// Hash function (e.g., SHA-256).
607    Hash,
608    /// Message authentication code (e.g., HMAC).
609    Mac,
610    /// Digital signature (e.g., ECDSA, ML-DSA).
611    Signature,
612    /// Public-key encryption (e.g., RSA).
613    Pke,
614    /// Key encapsulation mechanism (e.g., ML-KEM).
615    Kem,
616    /// Key derivation function (e.g., HKDF).
617    Kdf,
618    /// Key agreement (e.g., ECDH, X25519).
619    KeyAgree,
620    /// Extendable output function (e.g., SHAKE).
621    Xof,
622    /// Deterministic random bit generator.
623    Drbg,
624    /// Hybrid combiner (classical + PQC).
625    Combiner,
626    Other(String),
627    Unknown,
628}
629
630impl std::fmt::Display for CryptoPrimitive {
631    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
632        match self {
633            Self::Ae => write!(f, "ae"),
634            Self::BlockCipher => write!(f, "block-cipher"),
635            Self::StreamCipher => write!(f, "stream-cipher"),
636            Self::Hash => write!(f, "hash"),
637            Self::Mac => write!(f, "mac"),
638            Self::Signature => write!(f, "signature"),
639            Self::Pke => write!(f, "pke"),
640            Self::Kem => write!(f, "kem"),
641            Self::Kdf => write!(f, "kdf"),
642            Self::KeyAgree => write!(f, "key-agree"),
643            Self::Xof => write!(f, "xof"),
644            Self::Drbg => write!(f, "drbg"),
645            Self::Combiner => write!(f, "combiner"),
646            Self::Other(s) => write!(f, "{s}"),
647            Self::Unknown => write!(f, "unknown"),
648        }
649    }
650}
651
652/// Block cipher mode of operation.
653#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
654#[non_exhaustive]
655pub enum CryptoMode {
656    Ecb,
657    Cbc,
658    Ofb,
659    Cfb,
660    Ctr,
661    Gcm,
662    Ccm,
663    Xts,
664    Other(String),
665}
666
667impl std::fmt::Display for CryptoMode {
668    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
669        match self {
670            Self::Ecb => write!(f, "ecb"),
671            Self::Cbc => write!(f, "cbc"),
672            Self::Ofb => write!(f, "ofb"),
673            Self::Cfb => write!(f, "cfb"),
674            Self::Ctr => write!(f, "ctr"),
675            Self::Gcm => write!(f, "gcm"),
676            Self::Ccm => write!(f, "ccm"),
677            Self::Xts => write!(f, "xts"),
678            Self::Other(s) => write!(f, "{s}"),
679        }
680    }
681}
682
683/// Padding scheme.
684#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
685#[non_exhaustive]
686pub enum CryptoPadding {
687    Pkcs5,
688    Oaep,
689    Pss,
690    Other(String),
691}
692
693impl std::fmt::Display for CryptoPadding {
694    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
695        match self {
696            Self::Pkcs5 => write!(f, "pkcs5"),
697            Self::Oaep => write!(f, "oaep"),
698            Self::Pss => write!(f, "pss"),
699            Self::Other(s) => write!(f, "{s}"),
700        }
701    }
702}
703
704/// Cryptographic function capability.
705#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
706#[non_exhaustive]
707pub enum CryptoFunction {
708    Keygen,
709    Encrypt,
710    Decrypt,
711    Sign,
712    Verify,
713    Digest,
714    Tag,
715    KeyDerive,
716    Encapsulate,
717    Decapsulate,
718    Wrap,
719    Unwrap,
720    Other(String),
721}
722
723impl std::fmt::Display for CryptoFunction {
724    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
725        match self {
726            Self::Keygen => write!(f, "keygen"),
727            Self::Encrypt => write!(f, "encrypt"),
728            Self::Decrypt => write!(f, "decrypt"),
729            Self::Sign => write!(f, "sign"),
730            Self::Verify => write!(f, "verify"),
731            Self::Digest => write!(f, "digest"),
732            Self::Tag => write!(f, "tag"),
733            Self::KeyDerive => write!(f, "keyderive"),
734            Self::Encapsulate => write!(f, "encapsulate"),
735            Self::Decapsulate => write!(f, "decapsulate"),
736            Self::Wrap => write!(f, "wrap"),
737            Self::Unwrap => write!(f, "unwrap"),
738            Self::Other(s) => write!(f, "{s}"),
739        }
740    }
741}
742
743/// Execution environment for the cryptographic implementation.
744#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
745#[non_exhaustive]
746pub enum ExecutionEnvironment {
747    SoftwarePlainRam,
748    SoftwareEncryptedRam,
749    SoftwareTee,
750    Hardware,
751    Other(String),
752}
753
754impl std::fmt::Display for ExecutionEnvironment {
755    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
756        match self {
757            Self::SoftwarePlainRam => write!(f, "software-plain-ram"),
758            Self::SoftwareEncryptedRam => write!(f, "software-encrypted-ram"),
759            Self::SoftwareTee => write!(f, "software-tee"),
760            Self::Hardware => write!(f, "hardware"),
761            Self::Other(s) => write!(f, "{s}"),
762        }
763    }
764}
765
766/// Hardware/software platform of the implementation.
767#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
768#[non_exhaustive]
769pub enum ImplementationPlatform {
770    X86_32,
771    X86_64,
772    Armv7A,
773    Armv7M,
774    Armv8A,
775    S390x,
776    Generic,
777    Other(String),
778}
779
780impl std::fmt::Display for ImplementationPlatform {
781    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
782        match self {
783            Self::X86_32 => write!(f, "x86_32"),
784            Self::X86_64 => write!(f, "x86_64"),
785            Self::Armv7A => write!(f, "armv7-a"),
786            Self::Armv7M => write!(f, "armv7-m"),
787            Self::Armv8A => write!(f, "armv8-a"),
788            Self::S390x => write!(f, "s390x"),
789            Self::Generic => write!(f, "generic"),
790            Self::Other(s) => write!(f, "{s}"),
791        }
792    }
793}
794
795/// Certification or validation level achieved.
796#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
797#[non_exhaustive]
798pub enum CertificationLevel {
799    None,
800    Fips140_1L1,
801    Fips140_1L2,
802    Fips140_1L3,
803    Fips140_1L4,
804    Fips140_2L1,
805    Fips140_2L2,
806    Fips140_2L3,
807    Fips140_2L4,
808    Fips140_3L1,
809    Fips140_3L2,
810    Fips140_3L3,
811    Fips140_3L4,
812    CcEal1,
813    CcEal2,
814    CcEal3,
815    CcEal4,
816    CcEal5,
817    CcEal6,
818    CcEal7,
819    Other(String),
820}
821
822impl std::fmt::Display for CertificationLevel {
823    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
824        match self {
825            Self::None => write!(f, "none"),
826            Self::Fips140_1L1 => write!(f, "fips140-1-l1"),
827            Self::Fips140_1L2 => write!(f, "fips140-1-l2"),
828            Self::Fips140_1L3 => write!(f, "fips140-1-l3"),
829            Self::Fips140_1L4 => write!(f, "fips140-1-l4"),
830            Self::Fips140_2L1 => write!(f, "fips140-2-l1"),
831            Self::Fips140_2L2 => write!(f, "fips140-2-l2"),
832            Self::Fips140_2L3 => write!(f, "fips140-2-l3"),
833            Self::Fips140_2L4 => write!(f, "fips140-2-l4"),
834            Self::Fips140_3L1 => write!(f, "fips140-3-l1"),
835            Self::Fips140_3L2 => write!(f, "fips140-3-l2"),
836            Self::Fips140_3L3 => write!(f, "fips140-3-l3"),
837            Self::Fips140_3L4 => write!(f, "fips140-3-l4"),
838            Self::CcEal1 => write!(f, "cc-eal1"),
839            Self::CcEal2 => write!(f, "cc-eal2"),
840            Self::CcEal3 => write!(f, "cc-eal3"),
841            Self::CcEal4 => write!(f, "cc-eal4"),
842            Self::CcEal5 => write!(f, "cc-eal5"),
843            Self::CcEal6 => write!(f, "cc-eal6"),
844            Self::CcEal7 => write!(f, "cc-eal7"),
845            Self::Other(s) => write!(f, "{s}"),
846        }
847    }
848}
849
850/// Type of cryptographic key material.
851#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
852#[non_exhaustive]
853pub enum CryptoMaterialType {
854    PublicKey,
855    PrivateKey,
856    SymmetricKey,
857    SecretKey,
858    KeyPair,
859    Ciphertext,
860    Signature,
861    Digest,
862    Iv,
863    Nonce,
864    Seed,
865    Salt,
866    SharedSecret,
867    Tag,
868    Password,
869    Credential,
870    Token,
871    Other(String),
872    Unknown,
873}
874
875impl std::fmt::Display for CryptoMaterialType {
876    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
877        match self {
878            Self::PublicKey => write!(f, "public-key"),
879            Self::PrivateKey => write!(f, "private-key"),
880            Self::SymmetricKey => write!(f, "symmetric-key"),
881            Self::SecretKey => write!(f, "secret-key"),
882            Self::KeyPair => write!(f, "key-pair"),
883            Self::Ciphertext => write!(f, "ciphertext"),
884            Self::Signature => write!(f, "signature"),
885            Self::Digest => write!(f, "digest"),
886            Self::Iv => write!(f, "initialization-vector"),
887            Self::Nonce => write!(f, "nonce"),
888            Self::Seed => write!(f, "seed"),
889            Self::Salt => write!(f, "salt"),
890            Self::SharedSecret => write!(f, "shared-secret"),
891            Self::Tag => write!(f, "tag"),
892            Self::Password => write!(f, "password"),
893            Self::Credential => write!(f, "credential"),
894            Self::Token => write!(f, "token"),
895            Self::Other(s) => write!(f, "{s}"),
896            Self::Unknown => write!(f, "unknown"),
897        }
898    }
899}
900
901/// Lifecycle state of cryptographic material.
902#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
903#[non_exhaustive]
904pub enum CryptoMaterialState {
905    PreActivation,
906    Active,
907    Suspended,
908    Deactivated,
909    Compromised,
910    Destroyed,
911}
912
913impl std::fmt::Display for CryptoMaterialState {
914    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
915        match self {
916            Self::PreActivation => write!(f, "pre-activation"),
917            Self::Active => write!(f, "active"),
918            Self::Suspended => write!(f, "suspended"),
919            Self::Deactivated => write!(f, "deactivated"),
920            Self::Compromised => write!(f, "compromised"),
921            Self::Destroyed => write!(f, "destroyed"),
922        }
923    }
924}
925
926/// Cryptographic protocol type.
927#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
928#[non_exhaustive]
929pub enum ProtocolType {
930    Tls,
931    Dtls,
932    Ipsec,
933    Ssh,
934    Srtp,
935    Wireguard,
936    Ikev1,
937    Ikev2,
938    Zrtp,
939    Mikey,
940    Other(String),
941    Unknown,
942}
943
944impl std::fmt::Display for ProtocolType {
945    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
946        match self {
947            Self::Tls => write!(f, "tls"),
948            Self::Dtls => write!(f, "dtls"),
949            Self::Ipsec => write!(f, "ipsec"),
950            Self::Ssh => write!(f, "ssh"),
951            Self::Srtp => write!(f, "srtp"),
952            Self::Wireguard => write!(f, "wireguard"),
953            Self::Ikev1 => write!(f, "ikev1"),
954            Self::Ikev2 => write!(f, "ikev2"),
955            Self::Zrtp => write!(f, "zrtp"),
956            Self::Mikey => write!(f, "mikey"),
957            Self::Other(s) => write!(f, "{s}"),
958            Self::Unknown => write!(f, "unknown"),
959        }
960    }
961}
962
963// ── Tests ───────────────────────────────────────────────────────────────
964
965#[cfg(test)]
966mod tests {
967    use super::*;
968
969    #[test]
970    fn algorithm_is_quantum_safe() {
971        let algo =
972            AlgorithmProperties::new(CryptoPrimitive::Kem).with_nist_quantum_security_level(5);
973        assert!(algo.is_quantum_safe());
974
975        let classical =
976            AlgorithmProperties::new(CryptoPrimitive::Pke).with_nist_quantum_security_level(0);
977        assert!(!classical.is_quantum_safe());
978
979        let unknown = AlgorithmProperties::new(CryptoPrimitive::Pke);
980        assert!(!unknown.is_quantum_safe());
981    }
982
983    #[test]
984    fn algorithm_is_hybrid_pqc() {
985        let hybrid = AlgorithmProperties::new(CryptoPrimitive::Combiner);
986        assert!(hybrid.is_hybrid_pqc());
987
988        let normal = AlgorithmProperties::new(CryptoPrimitive::Kem);
989        assert!(!normal.is_hybrid_pqc());
990    }
991
992    #[test]
993    fn algorithm_is_weak() {
994        let md5 = AlgorithmProperties::new(CryptoPrimitive::Hash)
995            .with_algorithm_family("MD5".to_string());
996        assert!(md5.is_weak());
997
998        let sha1 = AlgorithmProperties::new(CryptoPrimitive::Hash)
999            .with_algorithm_family("SHA-1".to_string());
1000        assert!(sha1.is_weak());
1001
1002        let des = AlgorithmProperties::new(CryptoPrimitive::BlockCipher)
1003            .with_algorithm_family("DES".to_string());
1004        assert!(des.is_weak());
1005
1006        let rc4 = AlgorithmProperties::new(CryptoPrimitive::StreamCipher)
1007            .with_algorithm_family("RC4".to_string());
1008        assert!(rc4.is_weak());
1009
1010        let aes =
1011            AlgorithmProperties::new(CryptoPrimitive::Ae).with_algorithm_family("AES".to_string());
1012        assert!(!aes.is_weak());
1013
1014        let ml_kem = AlgorithmProperties::new(CryptoPrimitive::Kem)
1015            .with_algorithm_family("ML-KEM".to_string());
1016        assert!(!ml_kem.is_weak());
1017    }
1018
1019    #[test]
1020    fn certificate_expiry() {
1021        let expired = CertificateProperties::new()
1022            .with_not_valid_after(Utc::now() - chrono::Duration::days(1));
1023        assert!(expired.is_expired());
1024        assert!(!expired.is_expiring_soon(90));
1025
1026        let valid = CertificateProperties::new()
1027            .with_not_valid_after(Utc::now() + chrono::Duration::days(365));
1028        assert!(!valid.is_expired());
1029        assert!(!valid.is_expiring_soon(90));
1030
1031        let expiring = CertificateProperties::new()
1032            .with_not_valid_after(Utc::now() + chrono::Duration::days(30));
1033        assert!(!expiring.is_expired());
1034        assert!(expiring.is_expiring_soon(90));
1035    }
1036
1037    #[test]
1038    fn certificate_validity_days() {
1039        let no_expiry = CertificateProperties::new();
1040        assert!(no_expiry.validity_days().is_none());
1041
1042        let expired = CertificateProperties::new()
1043            .with_not_valid_after(Utc::now() - chrono::Duration::days(10));
1044        assert!(expired.validity_days().unwrap() < 0);
1045
1046        let future = CertificateProperties::new()
1047            .with_not_valid_after(Utc::now() + chrono::Duration::days(100));
1048        let days = future.validity_days().unwrap();
1049        assert!(days >= 99 && days <= 100);
1050    }
1051
1052    #[test]
1053    fn crypto_properties_builder() {
1054        let props = CryptoProperties::new(CryptoAssetType::Algorithm)
1055            .with_oid("2.16.840.1.101.3.4.1.46".to_string())
1056            .with_algorithm_properties(
1057                AlgorithmProperties::new(CryptoPrimitive::Ae)
1058                    .with_algorithm_family("AES".to_string())
1059                    .with_mode(CryptoMode::Gcm)
1060                    .with_classical_security_level(256)
1061                    .with_nist_quantum_security_level(1),
1062            );
1063
1064        assert_eq!(props.asset_type, CryptoAssetType::Algorithm);
1065        assert_eq!(props.oid.as_deref(), Some("2.16.840.1.101.3.4.1.46"));
1066        let algo = props.algorithm_properties.unwrap();
1067        assert_eq!(algo.primitive, CryptoPrimitive::Ae);
1068        assert_eq!(algo.algorithm_family.as_deref(), Some("AES"));
1069        assert_eq!(algo.mode, Some(CryptoMode::Gcm));
1070        assert_eq!(algo.classical_security_level, Some(256));
1071        assert!(algo.is_quantum_safe());
1072        assert!(!algo.is_weak());
1073    }
1074
1075    #[test]
1076    fn display_impls() {
1077        assert_eq!(CryptoAssetType::Algorithm.to_string(), "algorithm");
1078        assert_eq!(
1079            CryptoAssetType::RelatedCryptoMaterial.to_string(),
1080            "related-crypto-material"
1081        );
1082        assert_eq!(CryptoPrimitive::Kem.to_string(), "kem");
1083        assert_eq!(CryptoPrimitive::Combiner.to_string(), "combiner");
1084        assert_eq!(CryptoMode::Gcm.to_string(), "gcm");
1085        assert_eq!(CryptoFunction::Encapsulate.to_string(), "encapsulate");
1086        assert_eq!(CryptoMaterialType::PublicKey.to_string(), "public-key");
1087        assert_eq!(CryptoMaterialState::Compromised.to_string(), "compromised");
1088        assert_eq!(ProtocolType::Tls.to_string(), "tls");
1089        assert_eq!(CertificationLevel::Fips140_3L1.to_string(), "fips140-3-l1");
1090        assert_eq!(ExecutionEnvironment::Hardware.to_string(), "hardware");
1091        assert_eq!(ImplementationPlatform::X86_64.to_string(), "x86_64");
1092    }
1093
1094    #[test]
1095    fn protocol_builder() {
1096        let proto = ProtocolProperties::new(ProtocolType::Tls)
1097            .with_version("1.3".to_string())
1098            .with_cipher_suites(vec![CipherSuite {
1099                name: Some("TLS_AES_256_GCM_SHA384".to_string()),
1100                algorithms: vec!["algo/aes-256-gcm".to_string()],
1101                identifiers: vec!["0x13".to_string(), "0x02".to_string()],
1102            }]);
1103
1104        assert_eq!(proto.protocol_type, ProtocolType::Tls);
1105        assert_eq!(proto.version.as_deref(), Some("1.3"));
1106        assert_eq!(proto.cipher_suites.len(), 1);
1107    }
1108
1109    #[test]
1110    fn related_material_builder() {
1111        let key = RelatedCryptoMaterialProperties::new(CryptoMaterialType::PublicKey)
1112            .with_id("test-id".to_string())
1113            .with_state(CryptoMaterialState::Active)
1114            .with_size(2048)
1115            .with_algorithm_ref("algo/rsa-2048".to_string())
1116            .with_secured_by(SecuredBy {
1117                mechanism: "HSM".to_string(),
1118                algorithm_ref: Some("algo/aes-256".to_string()),
1119            });
1120
1121        assert_eq!(key.material_type, CryptoMaterialType::PublicKey);
1122        assert_eq!(key.state, Some(CryptoMaterialState::Active));
1123        assert_eq!(key.size, Some(2048));
1124        assert!(key.secured_by.is_some());
1125    }
1126}