diff --git a/cyclonedx/model/crypto.py b/cyclonedx/model/crypto.py index e1eda84e..f692ab19 100644 --- a/cyclonedx/model/crypto.py +++ b/cyclonedx/model/crypto.py @@ -322,7 +322,7 @@ def primitive(self) -> Optional[CryptoPrimitive]: return self._primitive @primitive.setter - def primitive(self, primitive: CryptoPrimitive) -> None: + def primitive(self, primitive: Optional[CryptoPrimitive]) -> None: self._primitive = primitive @property @@ -339,7 +339,7 @@ def parameter_set_identifier(self) -> Optional[str]: return self._parameter_set_identifier @parameter_set_identifier.setter - def parameter_set_identifier(self, parameter_set_identifier: str) -> None: + def parameter_set_identifier(self, parameter_set_identifier: Optional[str]) -> None: self._parameter_set_identifier = parameter_set_identifier @property @@ -357,7 +357,7 @@ def curve(self) -> Optional[str]: return self._curve @curve.setter - def curve(self, curve: str) -> None: + def curve(self, curve: Optional[str]) -> None: self._curve = curve @property @@ -372,7 +372,7 @@ def execution_environment(self) -> Optional[CryptoExecutionEnvironment]: return self._execution_environment @execution_environment.setter - def execution_environment(self, execution_environment: CryptoExecutionEnvironment) -> None: + def execution_environment(self, execution_environment: Optional[CryptoExecutionEnvironment]) -> None: self._execution_environment = execution_environment @property @@ -388,7 +388,7 @@ def implementation_platform(self) -> Optional[CryptoImplementationPlatform]: return self._implementation_platform @implementation_platform.setter - def implementation_platform(self, implementation_platform: CryptoImplementationPlatform) -> None: + def implementation_platform(self, implementation_platform: Optional[CryptoImplementationPlatform]) -> None: self._implementation_platform = implementation_platform @property @@ -422,7 +422,7 @@ def mode(self) -> Optional[CryptoMode]: return self._mode @mode.setter - def mode(self, mode: CryptoMode) -> None: + def mode(self, mode: Optional[CryptoMode]) -> None: self._mode = mode @property @@ -437,7 +437,7 @@ def padding(self) -> Optional[CryptoPadding]: return self._padding @padding.setter - def padding(self, padding: CryptoPadding) -> None: + def padding(self, padding: Optional[CryptoPadding]) -> None: self._padding = padding @property @@ -468,7 +468,7 @@ def classical_security_level(self) -> Optional[int]: return self._classical_security_level @classical_security_level.setter - def classical_security_level(self, classical_security_level: int) -> None: + def classical_security_level(self, classical_security_level: Optional[int]) -> None: self._classical_security_level = classical_security_level @property @@ -485,12 +485,14 @@ def nist_quantum_security_level(self) -> Optional[int]: return self._nist_quantum_security_level @nist_quantum_security_level.setter - def nist_quantum_security_level(self, nist_quantum_security_level: int) -> None: - if nist_quantum_security_level < 0 or nist_quantum_security_level > 6: + def nist_quantum_security_level(self, nist_quantum_security_level: Optional[int]) -> None: + if nist_quantum_security_level is not None and ( + nist_quantum_security_level < 0 + or nist_quantum_security_level > 6 + ): raise InvalidNistQuantumSecurityLevelException( 'NIST Quantum Security Level must be (0 <= value <= 6)' ) - self._nist_quantum_security_level = nist_quantum_security_level def __eq__(self, other: object) -> bool: @@ -553,7 +555,7 @@ def subject_name(self) -> Optional[str]: return self._subject_name @subject_name.setter - def subject_name(self, subject_name: str) -> None: + def subject_name(self, subject_name: Optional[str]) -> None: self._subject_name = subject_name @property @@ -584,7 +586,7 @@ def not_valid_before(self) -> Optional[datetime]: return self._not_valid_before @not_valid_before.setter - def not_valid_before(self, not_valid_before: datetime) -> None: + def not_valid_before(self, not_valid_before: Optional[datetime]) -> None: self._not_valid_before = not_valid_before @property @@ -600,7 +602,7 @@ def not_valid_after(self) -> Optional[datetime]: return self._not_valid_after @not_valid_after.setter - def not_valid_after(self, not_valid_after: datetime) -> None: + def not_valid_after(self, not_valid_after: Optional[datetime]) -> None: self._not_valid_after = not_valid_after @property @@ -616,7 +618,7 @@ def signature_algorithm_ref(self) -> Optional[BomRef]: return self._signature_algorithm_ref @signature_algorithm_ref.setter - def signature_algorithm_ref(self, signature_algorithm_ref: BomRef) -> None: + def signature_algorithm_ref(self, signature_algorithm_ref: Optional[BomRef]) -> None: self._signature_algorithm_ref = signature_algorithm_ref @property @@ -632,7 +634,7 @@ def subject_public_key_ref(self) -> Optional[BomRef]: return self._subject_public_key_ref @subject_public_key_ref.setter - def subject_public_key_ref(self, subject_public_key_ref: BomRef) -> None: + def subject_public_key_ref(self, subject_public_key_ref: Optional[BomRef]) -> None: self._subject_public_key_ref = subject_public_key_ref @property @@ -647,7 +649,7 @@ def certificate_format(self) -> Optional[str]: return self._certificate_format @certificate_format.setter - def certificate_format(self, certificate_format: str) -> None: + def certificate_format(self, certificate_format: Optional[str]) -> None: self._certificate_format = certificate_format @property @@ -662,7 +664,7 @@ def certificate_extension(self) -> Optional[str]: return self._certificate_extension @certificate_extension.setter - def certificate_extension(self, certificate_extension: str) -> None: + def certificate_extension(self, certificate_extension: Optional[str]) -> None: self._certificate_extension = certificate_extension def __eq__(self, other: object) -> bool: @@ -898,7 +900,7 @@ def algorithm_ref(self) -> Optional[BomRef]: return self._algorithm_ref @algorithm_ref.setter - def algorithm_ref(self, algorithm_ref: BomRef) -> None: + def algorithm_ref(self, algorithm_ref: Optional[BomRef]) -> None: self._algorithm_ref = algorithm_ref @property @@ -1476,9 +1478,10 @@ def related_crypto_material_properties(self) -> Optional[RelatedCryptoMaterialPr return self._related_crypto_material_properties @related_crypto_material_properties.setter - def related_crypto_material_properties(self, - related_crypto_material_properties: Optional[RelatedCryptoMaterialProperties] - ) -> None: + def related_crypto_material_properties( + self, + related_crypto_material_properties: Optional[RelatedCryptoMaterialProperties] + ) -> None: self._related_crypto_material_properties = related_crypto_material_properties @property @@ -1516,6 +1519,25 @@ def __eq__(self, other: object) -> bool: return hash(other) == hash(self) return False + def __lt__(self, other: Any) -> bool: + if isinstance(other, CryptoProperties): + return _ComparableTuple(( + self.asset_type, + self.algorithm_properties, + self.certificate_properties, + self.related_crypto_material_properties, + self.protocol_properties, + self.oid, + )) < _ComparableTuple(( + other.asset_type, + other.algorithm_properties, + other.certificate_properties, + other.related_crypto_material_properties, + other.protocol_properties, + other.oid, + )) + return NotImplemented + def __hash__(self) -> int: return hash((self.asset_type, self.algorithm_properties, self.certificate_properties, self.related_crypto_material_properties, self.protocol_properties, self.oid)) diff --git a/tests/_data/own/json/1.6/issue690.json b/tests/_data/own/json/1.6/issue690.json new file mode 100644 index 00000000..71023861 --- /dev/null +++ b/tests/_data/own/json/1.6/issue690.json @@ -0,0 +1,97 @@ +{ + "bomFormat": "CycloneDX", + "specVersion": "1.6", + "serialNumber": "urn:uuid:e8c355aa-2142-4084-a8c7-6d42c8610ba2", + "version": 1, + "metadata": { + "timestamp": "2024-01-09T12:00:00Z", + "component": { + "type": "application", + "name": "my application", + "version": "1.0" + } + }, + "components": [ + { + "name": "google.com", + "type": "cryptographic-asset", + "bom-ref": "crypto/certificate/google.com@sha256:1e15e0fbd3ce95bde5945633ae96add551341b11e5bae7bba12e98ad84a5beb4", + "cryptoProperties": { + "assetType": "certificate", + "certificateProperties": { + "subjectName": "CN = www.google.com", + "issuerName": "C = US, O = Google Trust Services LLC, CN = GTS CA 1C3", + "notValidBefore": "2016-11-21T08:00:00Z", + "notValidAfter": "2017-11-22T07:59:59Z", + "signatureAlgorithmRef": "crypto/algorithm/sha-512-rsa@1.2.840.113549.1.1.13", + "subjectPublicKeyRef": "crypto/key/rsa-2048@1.2.840.113549.1.1.1", + "certificateFormat": "X.509", + "certificateExtension": "crt" + } + } + }, + { + "name": "SHA512withRSA", + "type": "cryptographic-asset", + "bom-ref": "crypto/algorithm/sha-512-rsa@1.2.840.113549.1.1.13", + "cryptoProperties": { + "assetType": "algorithm", + "algorithmProperties": { + "parameterSetIdentifier": "512", + "executionEnvironment": "software-plain-ram", + "implementationPlatform": "x86_64", + "certificationLevel": [ + "none" + ], + "cryptoFunctions": [ + "digest" + ], + "nistQuantumSecurityLevel": 0 + }, + "oid": "1.2.840.113549.1.1.13" + } + }, + { + "name": "RSA-2048", + "type": "cryptographic-asset", + "bom-ref": "crypto/key/rsa-2048@1.2.840.113549.1.1.1", + "cryptoProperties": { + "assetType": "related-crypto-material", + "relatedCryptoMaterialProperties": { + "type": "public-key", + "id": "2e9ef09e-dfac-4526-96b4-d02f31af1b22", + "state": "active", + "size": 2048, + "algorithmRef": "crypto/algorithm/rsa-2048@1.2.840.113549.1.1.1", + "securedBy": { + "mechanism": "None" + }, + "creationDate": "2016-11-21T08:00:00Z", + "activationDate": "2016-11-21T08:20:00Z" + }, + "oid": "1.2.840.113549.1.1.1" + } + }, + { + "name": "RSA-2048", + "type": "cryptographic-asset", + "bom-ref": "crypto/algorithm/rsa-2048@1.2.840.113549.1.1.1", + "cryptoProperties": { + "assetType": "algorithm", + "algorithmProperties": { + "parameterSetIdentifier": "2048", + "executionEnvironment": "software-plain-ram", + "implementationPlatform": "x86_64", + "certificationLevel": [ + "none" + ], + "cryptoFunctions": [ + "encapsulate", + "decapsulate" + ] + }, + "oid": "1.2.840.113549.1.1.1" + } + } + ] +} diff --git a/tests/test_deserialize_json.py b/tests/test_deserialize_json.py index de4e3940..380ba5b6 100644 --- a/tests/test_deserialize_json.py +++ b/tests/test_deserialize_json.py @@ -82,3 +82,16 @@ def test(ls: LicenseRepository) -> None: test(bom.metadata.component.licenses) test(list(bom.components)[0].licenses) test(list(bom.services)[0].licenses) + + def test_regression_issue690(self) -> None: + """ + regressio test for issue#690. + see https://github.com/CycloneDX/cyclonedx-python-lib/issues/690 + """ + json_file = join(OWN_DATA_DIRECTORY, 'json', + SchemaVersion.V1_6.to_version(), + 'issue690.json') + with open(json_file) as f: + json = json_loads(f.read()) + bom: Bom = Bom.from_json(json) # <<< is expected to not crash + self.assertIsNotNone(bom)